FreeRDP
Loading...
Searching...
No Matches
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
41static 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
48static 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
72static 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
94static 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
178static 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
223static 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
241static 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
332static 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
367static 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
420static 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
452static 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
473static 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
488static 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
505static 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
515static 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
526static 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
542FREERDP_API const char* ShadowSubsystemName(void)
543{
544 return "Win";
545}
546
547FREERDP_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}