FreeRDP
win_shadow.c
1 
19 #include <windows.h>
20 
21 #include <winpr/crt.h>
22 #include <winpr/synch.h>
23 #include <winpr/sysinfo.h>
24 
25 #include <freerdp/log.h>
26 #include <freerdp/codec/color.h>
27 #include <freerdp/codec/region.h>
28 #include <freerdp/server/server-common.h>
29 
30 #include "win_shadow.h"
31 
32 #define TAG SERVER_TAG("shadow.win")
33 
34 /* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
35  * does not mention this flag is only supported if building for _WIN32_WINNT >= 0x0600
36  */
37 #ifndef MOUSEEVENTF_HWHEEL
38 #define MOUSEEVENTF_HWHEEL 0x1000
39 #endif
40 
41 static BOOL win_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
42  rdpShadowClient* client, UINT32 flags)
43 {
44  WLog_WARN(TAG, "TODO: Implement!");
45  return TRUE;
46 }
47 
48 static BOOL win_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
49  UINT16 flags, UINT8 code)
50 {
51  UINT rc;
52  INPUT event;
53  event.type = INPUT_KEYBOARD;
54  event.ki.wVk = 0;
55  event.ki.wScan = code;
56  event.ki.dwFlags = KEYEVENTF_SCANCODE;
57  event.ki.dwExtraInfo = 0;
58  event.ki.time = 0;
59 
60  if (flags & KBD_FLAGS_RELEASE)
61  event.ki.dwFlags |= KEYEVENTF_KEYUP;
62 
63  if (flags & KBD_FLAGS_EXTENDED)
64  event.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
65 
66  rc = SendInput(1, &event, sizeof(INPUT));
67  if (rc == 0)
68  return FALSE;
69  return TRUE;
70 }
71 
72 static BOOL win_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
73  rdpShadowClient* client, UINT16 flags,
74  UINT16 code)
75 {
76  UINT rc;
77  INPUT event;
78  event.type = INPUT_KEYBOARD;
79  event.ki.wVk = 0;
80  event.ki.wScan = code;
81  event.ki.dwFlags = KEYEVENTF_UNICODE;
82  event.ki.dwExtraInfo = 0;
83  event.ki.time = 0;
84 
85  if (flags & KBD_FLAGS_RELEASE)
86  event.ki.dwFlags |= KEYEVENTF_KEYUP;
87 
88  rc = SendInput(1, &event, sizeof(INPUT));
89  if (rc == 0)
90  return FALSE;
91  return TRUE;
92 }
93 
94 static BOOL win_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
95  UINT16 flags, UINT16 x, UINT16 y)
96 {
97  UINT rc = 1;
98  INPUT event = { 0 };
99  float width;
100  float height;
101 
102  event.type = INPUT_MOUSE;
103 
104  if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
105  {
106  if (flags & PTR_FLAGS_WHEEL)
107  event.mi.dwFlags = MOUSEEVENTF_WHEEL;
108  else
109  event.mi.dwFlags = MOUSEEVENTF_HWHEEL;
110  event.mi.mouseData = flags & WheelRotationMask;
111 
112  if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
113  event.mi.mouseData *= -1;
114 
115  rc = SendInput(1, &event, sizeof(INPUT));
116 
117  /* The build target is a system that did not support MOUSEEVENTF_HWHEEL
118  * but it may run on newer systems supporting it.
119  * Ignore the return value in these cases.
120  */
121 #if (_WIN32_WINNT < 0x0600)
122  if (flags & PTR_FLAGS_HWHEEL)
123  rc = 1;
124 #endif
125  }
126  else
127  {
128  width = (float)GetSystemMetrics(SM_CXSCREEN);
129  height = (float)GetSystemMetrics(SM_CYSCREEN);
130  event.mi.dx = (LONG)((float)x * (65535.0f / width));
131  event.mi.dy = (LONG)((float)y * (65535.0f / height));
132  event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
133 
134  if (flags & PTR_FLAGS_MOVE)
135  {
136  event.mi.dwFlags |= MOUSEEVENTF_MOVE;
137  rc = SendInput(1, &event, sizeof(INPUT));
138  if (rc == 0)
139  return FALSE;
140  }
141 
142  event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
143 
144  if (flags & PTR_FLAGS_BUTTON1)
145  {
146  if (flags & PTR_FLAGS_DOWN)
147  event.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
148  else
149  event.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
150 
151  rc = SendInput(1, &event, sizeof(INPUT));
152  }
153  else if (flags & PTR_FLAGS_BUTTON2)
154  {
155  if (flags & PTR_FLAGS_DOWN)
156  event.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
157  else
158  event.mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
159 
160  rc = SendInput(1, &event, sizeof(INPUT));
161  }
162  else if (flags & PTR_FLAGS_BUTTON3)
163  {
164  if (flags & PTR_FLAGS_DOWN)
165  event.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
166  else
167  event.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
168 
169  rc = SendInput(1, &event, sizeof(INPUT));
170  }
171  }
172 
173  if (rc == 0)
174  return FALSE;
175  return TRUE;
176 }
177 
178 static BOOL win_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
179  rdpShadowClient* client, UINT16 flags, UINT16 x,
180  UINT16 y)
181 {
182  UINT rc = 1;
183  INPUT event = { 0 };
184  float width;
185  float height;
186 
187  if ((flags & PTR_XFLAGS_BUTTON1) || (flags & PTR_XFLAGS_BUTTON2))
188  {
189  event.type = INPUT_MOUSE;
190 
191  if (flags & PTR_FLAGS_MOVE)
192  {
193  width = (float)GetSystemMetrics(SM_CXSCREEN);
194  height = (float)GetSystemMetrics(SM_CYSCREEN);
195  event.mi.dx = (LONG)((float)x * (65535.0f / width));
196  event.mi.dy = (LONG)((float)y * (65535.0f / height));
197  event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
198  rc = SendInput(1, &event, sizeof(INPUT));
199  if (rc == 0)
200  return FALSE;
201  }
202 
203  event.mi.dx = event.mi.dy = event.mi.dwFlags = 0;
204 
205  if (flags & PTR_XFLAGS_DOWN)
206  event.mi.dwFlags |= MOUSEEVENTF_XDOWN;
207  else
208  event.mi.dwFlags |= MOUSEEVENTF_XUP;
209 
210  if (flags & PTR_XFLAGS_BUTTON1)
211  event.mi.mouseData = XBUTTON1;
212  else if (flags & PTR_XFLAGS_BUTTON2)
213  event.mi.mouseData = XBUTTON2;
214 
215  rc = SendInput(1, &event, sizeof(INPUT));
216  }
217 
218  if (rc == 0)
219  return FALSE;
220  return TRUE;
221 }
222 
223 static int win_shadow_invalidate_region(winShadowSubsystem* subsystem, int x, int y, int width,
224  int height)
225 {
226  rdpShadowServer* server;
227  rdpShadowSurface* surface;
228  RECTANGLE_16 invalidRect;
229  server = subsystem->base.server;
230  surface = server->surface;
231  invalidRect.left = x;
232  invalidRect.top = y;
233  invalidRect.right = x + width;
234  invalidRect.bottom = y + height;
235  EnterCriticalSection(&(surface->lock));
236  region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
237  LeaveCriticalSection(&(surface->lock));
238  return 1;
239 }
240 
241 static int win_shadow_surface_copy(winShadowSubsystem* subsystem)
242 {
243  int x, y;
244  int width;
245  int height;
246  int count;
247  int status = 1;
248  int nDstStep = 0;
249  DWORD DstFormat;
250  BYTE* pDstData = NULL;
251  rdpShadowServer* server;
252  rdpShadowSurface* surface;
253  RECTANGLE_16 surfaceRect;
254  RECTANGLE_16 invalidRect;
255  const RECTANGLE_16* extents;
256  server = subsystem->base.server;
257  surface = server->surface;
258 
259  if (ArrayList_Count(server->clients) < 1)
260  return 1;
261 
262  surfaceRect.left = surface->x;
263  surfaceRect.top = surface->y;
264  surfaceRect.right = surface->x + surface->width;
265  surfaceRect.bottom = surface->y + surface->height;
266  region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
267 
268  if (region16_is_empty(&(surface->invalidRegion)))
269  return 1;
270 
271  extents = region16_extents(&(surface->invalidRegion));
272  CopyMemory(&invalidRect, extents, sizeof(RECTANGLE_16));
273  shadow_capture_align_clip_rect(&invalidRect, &surfaceRect);
274  x = invalidRect.left;
275  y = invalidRect.top;
276  width = invalidRect.right - invalidRect.left;
277  height = invalidRect.bottom - invalidRect.top;
278 
279  if (0)
280  {
281  x = 0;
282  y = 0;
283  width = surface->width;
284  height = surface->height;
285  }
286 
287  WLog_INFO(TAG, "SurfaceCopy x: %d y: %d width: %d height: %d right: %d bottom: %d", x, y, width,
288  height, x + width, y + height);
289 #if defined(WITH_WDS_API)
290  {
291  rdpGdi* gdi;
292  shwContext* shw;
293  rdpContext* context;
294 
295  WINPR_ASSERT(subsystem);
296  shw = subsystem->shw;
297  WINPR_ASSERT(shw);
298 
299  context = &shw->common.context;
300  WINPR_ASSERT(context);
301 
302  gdi = context->gdi;
303  WINPR_ASSERT(gdi);
304 
305  pDstData = gdi->primary_buffer;
306  nDstStep = gdi->width * 4;
307  DstFormat = gdi->dstFormat;
308  }
309 #elif defined(WITH_DXGI_1_2)
310  DstFormat = PIXEL_FORMAT_BGRX32;
311  status = win_shadow_dxgi_fetch_frame_data(subsystem, &pDstData, &nDstStep, x, y, width, height);
312 #endif
313 
314  if (status <= 0)
315  return status;
316 
317  if (!freerdp_image_copy_no_overlap(surface->data, surface->format, surface->scanline, x, y,
318  width, height, pDstData, DstFormat, nDstStep, x, y, NULL,
319  FREERDP_FLIP_NONE))
320  return ERROR_INTERNAL_ERROR;
321 
322  ArrayList_Lock(server->clients);
323  count = ArrayList_Count(server->clients);
324  shadow_subsystem_frame_update(&subsystem->base);
325  ArrayList_Unlock(server->clients);
326  region16_clear(&(surface->invalidRegion));
327  return 1;
328 }
329 
330 #if defined(WITH_WDS_API)
331 
332 static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg)
333 {
334  winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
335  DWORD status;
336  DWORD nCount;
337  HANDLE events[32];
338  HANDLE StopEvent;
339  StopEvent = subsystem->base.server->StopEvent;
340  nCount = 0;
341  events[nCount++] = StopEvent;
342  events[nCount++] = subsystem->RdpUpdateEnterEvent;
343 
344  while (1)
345  {
346  status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
347 
348  if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
349  {
350  break;
351  }
352 
353  if (WaitForSingleObject(subsystem->RdpUpdateEnterEvent, 0) == WAIT_OBJECT_0)
354  {
355  win_shadow_surface_copy(subsystem);
356  (void)ResetEvent(subsystem->RdpUpdateEnterEvent);
357  (void)SetEvent(subsystem->RdpUpdateLeaveEvent);
358  }
359  }
360 
361  ExitThread(0);
362  return 0;
363 }
364 
365 #elif defined(WITH_DXGI_1_2)
366 
367 static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg)
368 {
369  winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
370  int fps;
371  DWORD status;
372  DWORD nCount;
373  UINT64 cTime;
374  DWORD dwTimeout;
375  DWORD dwInterval;
376  UINT64 frameTime;
377  HANDLE events[32];
378  HANDLE StopEvent;
379  StopEvent = subsystem->server->StopEvent;
380  nCount = 0;
381  events[nCount++] = StopEvent;
382  fps = 16;
383  dwInterval = 1000 / fps;
384  frameTime = GetTickCount64() + dwInterval;
385 
386  while (1)
387  {
388  dwTimeout = INFINITE;
389  cTime = GetTickCount64();
390  dwTimeout = (DWORD)((cTime > frameTime) ? 0 : frameTime - cTime);
391  status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
392 
393  if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
394  {
395  break;
396  }
397 
398  if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
399  {
400  int dxgi_status;
401  dxgi_status = win_shadow_dxgi_get_next_frame(subsystem);
402 
403  if (dxgi_status > 0)
404  dxgi_status = win_shadow_dxgi_get_invalid_region(subsystem);
405 
406  if (dxgi_status > 0)
407  win_shadow_surface_copy(subsystem);
408 
409  dwInterval = 1000 / fps;
410  frameTime += dwInterval;
411  }
412  }
413 
414  ExitThread(0);
415  return 0;
416 }
417 
418 #endif
419 
420 static UINT32 win_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
421 {
422  HDC hdc;
423  int index;
424  int desktopWidth;
425  int desktopHeight;
426  DWORD iDevNum = 0;
427  int numMonitors = 0;
428  MONITOR_DEF* monitor;
429  DISPLAY_DEVICE displayDevice = { 0 };
430 
431  displayDevice.cb = sizeof(DISPLAY_DEVICE);
432 
433  if (EnumDisplayDevices(NULL, iDevNum, &displayDevice, 0))
434  {
435  hdc = CreateDC(displayDevice.DeviceName, NULL, NULL, NULL);
436  desktopWidth = GetDeviceCaps(hdc, HORZRES);
437  desktopHeight = GetDeviceCaps(hdc, VERTRES);
438  index = 0;
439  numMonitors = 1;
440  monitor = &monitors[index];
441  monitor->left = 0;
442  monitor->top = 0;
443  monitor->right = desktopWidth;
444  monitor->bottom = desktopHeight;
445  monitor->flags = 1;
446  DeleteDC(hdc);
447  }
448 
449  return numMonitors;
450 }
451 
452 static int win_shadow_subsystem_init(rdpShadowSubsystem* arg)
453 {
454  winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
455  int status;
456  MONITOR_DEF* virtualScreen;
457  subsystem->base.numMonitors = win_shadow_enum_monitors(subsystem->base.monitors, 16);
458 #if defined(WITH_WDS_API)
459  status = win_shadow_wds_init(subsystem);
460 #elif defined(WITH_DXGI_1_2)
461  status = win_shadow_dxgi_init(subsystem);
462 #endif
463  virtualScreen = &(subsystem->base.virtualScreen);
464  virtualScreen->left = 0;
465  virtualScreen->top = 0;
466  virtualScreen->right = subsystem->width;
467  virtualScreen->bottom = subsystem->height;
468  virtualScreen->flags = 1;
469  WLog_INFO(TAG, "width: %d height: %d", subsystem->width, subsystem->height);
470  return 1;
471 }
472 
473 static int win_shadow_subsystem_uninit(rdpShadowSubsystem* arg)
474 {
475  winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
476 
477  if (!subsystem)
478  return -1;
479 
480 #if defined(WITH_WDS_API)
481  win_shadow_wds_uninit(subsystem);
482 #elif defined(WITH_DXGI_1_2)
483  win_shadow_dxgi_uninit(subsystem);
484 #endif
485  return 1;
486 }
487 
488 static int win_shadow_subsystem_start(rdpShadowSubsystem* arg)
489 {
490  winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
491  HANDLE thread;
492 
493  if (!subsystem)
494  return -1;
495 
496  if (!(thread = CreateThread(NULL, 0, win_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
497  {
498  WLog_ERR(TAG, "Failed to create thread");
499  return -1;
500  }
501 
502  return 1;
503 }
504 
505 static int win_shadow_subsystem_stop(rdpShadowSubsystem* arg)
506 {
507  winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
508 
509  if (!subsystem)
510  return -1;
511 
512  return 1;
513 }
514 
515 static void win_shadow_subsystem_free(rdpShadowSubsystem* arg)
516 {
517  winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
518 
519  if (!subsystem)
520  return;
521 
522  win_shadow_subsystem_uninit(arg);
523  free(subsystem);
524 }
525 
526 static rdpShadowSubsystem* win_shadow_subsystem_new(void)
527 {
528  winShadowSubsystem* subsystem;
529  subsystem = (winShadowSubsystem*)calloc(1, sizeof(winShadowSubsystem));
530 
531  if (!subsystem)
532  return NULL;
533 
534  subsystem->base.SynchronizeEvent = win_shadow_input_synchronize_event;
535  subsystem->base.KeyboardEvent = win_shadow_input_keyboard_event;
536  subsystem->base.UnicodeKeyboardEvent = win_shadow_input_unicode_keyboard_event;
537  subsystem->base.MouseEvent = win_shadow_input_mouse_event;
538  subsystem->base.ExtendedMouseEvent = win_shadow_input_extended_mouse_event;
539  return &subsystem->base;
540 }
541 
542 FREERDP_API const char* ShadowSubsystemName(void)
543 {
544  return "Win";
545 }
546 
547 FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
548 {
549  const char name[] = "windows shadow subsystem";
550  const char* arg[] = { name };
551 
552  freerdp_server_warn_unmaintained(ARRAYSIZE(arg), arg);
553  pEntryPoints->New = win_shadow_subsystem_new;
554  pEntryPoints->Free = win_shadow_subsystem_free;
555  pEntryPoints->Init = win_shadow_subsystem_init;
556  pEntryPoints->Uninit = win_shadow_subsystem_uninit;
557  pEntryPoints->Start = win_shadow_subsystem_start;
558  pEntryPoints->Stop = win_shadow_subsystem_stop;
559  pEntryPoints->EnumMonitors = win_shadow_enum_monitors;
560  return 1;
561 }