22#include <winpr/synch.h>
23#include <winpr/sysinfo.h>
25#include <freerdp/log.h>
26#include <freerdp/codec/color.h>
27#include <freerdp/codec/region.h>
28#include <freerdp/server/server-common.h>
30#include "win_shadow.h"
34#define TAG SERVER_TAG("shadow.win")
39#ifndef MOUSEEVENTF_HWHEEL
40#define MOUSEEVENTF_HWHEEL 0x1000
44static WINPR_TLS HDESK win_shadow_thread_desktop =
nullptr;
53static BOOL win_shadow_follow_input_desktop(
void)
55 WCHAR inputName[64] = WINPR_C_ARRAY_INIT;
56 WCHAR threadName[64] = WINPR_C_ARRAY_INIT;
57 HDESK input = OpenInputDesktop(0, FALSE, GENERIC_ALL);
62 if (!GetUserObjectInformationW(input, UOI_NAME, inputName,
sizeof(inputName),
nullptr))
63 WLog_WARN(TAG,
"GetUserObjectInformationW(input desktop, UOI_NAME) failed: 0x%08lX",
65 if (!GetUserObjectInformationW(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME, threadName,
66 sizeof(threadName),
nullptr))
67 WLog_WARN(TAG,
"GetUserObjectInformationW(thread desktop, UOI_NAME) failed: 0x%08lX",
70 if (_wcsicmp(inputName, threadName) == 0)
72 (void)CloseDesktop(input);
76 if (!SetThreadDesktop(input))
78 WLog_WARN(TAG,
"SetThreadDesktop(%ls) failed: 0x%08lX", inputName, GetLastError());
79 (void)CloseDesktop(input);
84 if (win_shadow_thread_desktop)
85 (void)CloseDesktop(win_shadow_thread_desktop);
86 win_shadow_thread_desktop = input;
87 WLog_INFO(TAG,
"thread %lu follows the input desktop from %ls to %ls", GetCurrentThreadId(),
88 threadName, inputName);
97static void win_shadow_send_input(
const char* what, INPUT* event)
102 if (SendInput(1, event,
sizeof(INPUT)) == 0)
103 WLog_WARN(TAG,
"%s: SendInput refused the event (0x%08lX), it is dropped", what,
109static void win_shadow_send_sas(
void)
111 HANDLE
event = OpenEventW(EVENT_MODIFY_STATE, FALSE, WIN_SHADOW_SAS_EVENT);
115 WLog_WARN(TAG,
"Ctrl+Alt+Del: no freerdp-shadow-service to send it (0x%08lX)",
122 "Ctrl+Alt+Del: asked freerdp-shadow-service for the secure attention sequence");
124 WLog_WARN(TAG,
"Ctrl+Alt+Del: SetEvent failed (0x%08lX)", GetLastError());
125 (void)CloseHandle(event);
128static BOOL win_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
129 rdpShadowClient* client, UINT32 flags)
131 WLog_WARN(TAG,
"TODO: Implement!");
135static BOOL win_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
136 UINT16 flags, UINT8 code)
138 winShadowSubsystem* win = (winShadowSubsystem*)subsystem;
139 const BOOL down = (flags & KBD_FLAGS_RELEASE) == 0;
142 (void)win_shadow_follow_input_desktop();
147 win->ctrlDown = down;
148 else if (code == 0x38)
150 else if ((code == 0x53) && win->ctrlDown && win->altDown)
153 win_shadow_send_sas();
156 INPUT
event = WINPR_C_ARRAY_INIT;
157 event.type = INPUT_KEYBOARD;
159#if defined(NONAMELESSUNION)
167 ki->dwFlags = KEYEVENTF_SCANCODE;
171 if (flags & KBD_FLAGS_RELEASE)
172 ki->dwFlags |= KEYEVENTF_KEYUP;
174 if (flags & KBD_FLAGS_EXTENDED)
175 ki->dwFlags |= KEYEVENTF_EXTENDEDKEY;
177 win_shadow_send_input(
"keyboard", &event);
181static BOOL win_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
182 rdpShadowClient* client, UINT16 flags,
185 (void)win_shadow_follow_input_desktop();
186 INPUT
event = WINPR_C_ARRAY_INIT;
187 event.type = INPUT_KEYBOARD;
189#if defined(NONAMELESSUNION)
196 ki->dwFlags = KEYEVENTF_UNICODE;
200 if (flags & KBD_FLAGS_RELEASE)
201 ki->dwFlags |= KEYEVENTF_KEYUP;
203 win_shadow_send_input(
"unicode keyboard", &event);
207static BOOL win_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
208 UINT16 flags, UINT16 x, UINT16 y)
210 (void)win_shadow_follow_input_desktop();
211 INPUT
event = WINPR_C_ARRAY_INIT;
215#if defined(NONAMELESSUNION)
221 event.type = INPUT_MOUSE;
223 if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
225 if (flags & PTR_FLAGS_WHEEL)
226 mi->dwFlags = MOUSEEVENTF_WHEEL;
228 mi->dwFlags = MOUSEEVENTF_HWHEEL;
229 mi->mouseData = flags & WheelRotationMask;
231 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
234 win_shadow_send_input(
"mouse", &event);
238 width = (float)GetSystemMetrics(SM_CXSCREEN);
239 height = (float)GetSystemMetrics(SM_CYSCREEN);
240 mi->dx = (LONG)((
float)x * (65535.0f / width));
241 mi->dy = (LONG)((
float)y * (65535.0f / height));
242 mi->dwFlags = MOUSEEVENTF_ABSOLUTE;
244 if (flags & PTR_FLAGS_MOVE)
246 mi->dwFlags |= MOUSEEVENTF_MOVE;
247 win_shadow_send_input(
"mouse", &event);
250 mi->dwFlags = MOUSEEVENTF_ABSOLUTE;
252 if (flags & PTR_FLAGS_BUTTON1)
254 if (flags & PTR_FLAGS_DOWN)
255 mi->dwFlags |= MOUSEEVENTF_LEFTDOWN;
257 mi->dwFlags |= MOUSEEVENTF_LEFTUP;
259 win_shadow_send_input(
"mouse", &event);
261 else if (flags & PTR_FLAGS_BUTTON2)
263 if (flags & PTR_FLAGS_DOWN)
264 mi->dwFlags |= MOUSEEVENTF_RIGHTDOWN;
266 mi->dwFlags |= MOUSEEVENTF_RIGHTUP;
268 win_shadow_send_input(
"mouse", &event);
270 else if (flags & PTR_FLAGS_BUTTON3)
272 if (flags & PTR_FLAGS_DOWN)
273 mi->dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
275 mi->dwFlags |= MOUSEEVENTF_MIDDLEUP;
277 win_shadow_send_input(
"mouse", &event);
284static BOOL win_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
285 rdpShadowClient* client, UINT16 flags, UINT16 x,
288 (void)win_shadow_follow_input_desktop();
289 INPUT
event = WINPR_C_ARRAY_INIT;
293#if defined(NONAMELESSUNION)
299 if ((flags & PTR_XFLAGS_BUTTON1) || (flags & PTR_XFLAGS_BUTTON2))
301 event.type = INPUT_MOUSE;
303 if (flags & PTR_FLAGS_MOVE)
305 width = (float)GetSystemMetrics(SM_CXSCREEN);
306 height = (float)GetSystemMetrics(SM_CYSCREEN);
307 mi->dx = (LONG)((
float)x * (65535.0f / width));
308 mi->dy = (LONG)((
float)y * (65535.0f / height));
309 mi->dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
310 win_shadow_send_input(
"extended mouse", &event);
313 mi->dx = mi->dy = mi->dwFlags = 0;
315 if (flags & PTR_XFLAGS_DOWN)
316 mi->dwFlags |= MOUSEEVENTF_XDOWN;
318 mi->dwFlags |= MOUSEEVENTF_XUP;
320 if (flags & PTR_XFLAGS_BUTTON1)
321 mi->mouseData = XBUTTON1;
322 else if (flags & PTR_XFLAGS_BUTTON2)
323 mi->mouseData = XBUTTON2;
325 win_shadow_send_input(
"extended mouse", &event);
331static int win_shadow_invalidate_region(winShadowSubsystem* subsystem,
int x,
int y,
int width,
334 rdpShadowServer* server = subsystem->base.server;
335 rdpShadowSurface* surface = server->surface;
336 RECTANGLE_16 invalidRect = { .left = x, .top = y, .right = x + width, .bottom = y + height };
337 EnterCriticalSection(&(surface->lock));
338 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
339 LeaveCriticalSection(&(surface->lock));
343static int win_shadow_surface_copy(winShadowSubsystem* subsystem)
351 BYTE* pDstData =
nullptr;
352 rdpShadowServer* server;
353 rdpShadowSurface* surface;
357 server = subsystem->base.server;
358 surface = server->surface;
360 if (ArrayList_Count(server->clients) < 1)
363 surfaceRect.left = surface->x;
364 surfaceRect.top = surface->y;
365 surfaceRect.right = surface->x + surface->width;
366 surfaceRect.bottom = surface->y + surface->height;
367 region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
369 if (region16_is_empty(&(surface->invalidRegion)))
372 extents = region16_extents(&(surface->invalidRegion));
373 CopyMemory(&invalidRect, extents,
sizeof(
RECTANGLE_16));
374 shadow_capture_align_clip_rect(&invalidRect, &surfaceRect);
375 x = invalidRect.left;
377 width = invalidRect.right - invalidRect.left;
378 height = invalidRect.bottom - invalidRect.top;
382 if (x + width > subsystem->width)
383 width = subsystem->width - x;
384 if (y + height > subsystem->height)
385 height = subsystem->height - y;
386 if ((width <= 0) || (height <= 0))
388 region16_clear(&(surface->invalidRegion));
396 width = surface->width;
397 height = surface->height;
400 WLog_INFO(TAG,
"SurfaceCopy x: %d y: %d width: %d height: %d right: %d bottom: %d", x, y, width,
401 height, x + width, y + height);
402#if defined(WITH_WDS_API)
408 WINPR_ASSERT(subsystem);
409 shw = subsystem->shw;
412 context = &shw->common.context;
413 WINPR_ASSERT(context);
418 pDstData = gdi->primary_buffer;
419 nDstStep = gdi->width * 4;
420 DstFormat = gdi->dstFormat;
422#elif defined(WITH_DXGI_1_2)
423 DstFormat = PIXEL_FORMAT_BGRX32;
424 status = win_shadow_dxgi_fetch_frame_data(subsystem, &pDstData, &nDstStep, x, y, width, height);
430#if defined(WITH_WDS_API)
434#elif defined(WITH_DXGI_1_2)
444 if (!freerdp_image_copy_no_overlap(surface->data, surface->format, surface->scanline, x, y,
445 width, height, pDstData, DstFormat, nDstStep, srcX, srcY,
446 nullptr, FREERDP_FLIP_NONE))
447 return ERROR_INTERNAL_ERROR;
458 shadow_subsystem_frame_update(&subsystem->base);
459 region16_clear(&(surface->invalidRegion));
463#if defined(WITH_WDS_API)
465static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg)
467 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
472 StopEvent = subsystem->base.server->StopEvent;
474 events[nCount++] = StopEvent;
475 events[nCount++] = subsystem->RdpUpdateEnterEvent;
479 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
481 if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
486 if (WaitForSingleObject(subsystem->RdpUpdateEnterEvent, 0) == WAIT_OBJECT_0)
488 win_shadow_surface_copy(subsystem);
489 (void)ResetEvent(subsystem->RdpUpdateEnterEvent);
490 (void)SetEvent(subsystem->RdpUpdateLeaveEvent);
498#elif defined(WITH_DXGI_1_2)
500static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg)
502 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
511 StopEvent = subsystem->base.server->StopEvent;
513 WINPR_ASSERT(MsgPipe);
515 events[nCount++] = StopEvent;
516 events[nCount++] = MessageQueue_Event(MsgPipe->In);
517 subsystem->base.captureFrameRate = 120;
518 dwInterval = 1000 / subsystem->base.captureFrameRate;
519 frameTime = GetTickCount64() + dwInterval;
520 BOOL refresh = FALSE;
524 dwTimeout = INFINITE;
525 cTime = GetTickCount64();
526 dwTimeout = (DWORD)((cTime > frameTime) ? 0 : frameTime - cTime);
527 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
529 if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
536 wMessage message = WINPR_C_ARRAY_INIT;
538 while (!quit && MessageQueue_Peek(MsgPipe->In, &message, TRUE))
540 quit = (message.id == WMQ_QUIT);
541 if (message.id == SHADOW_MSG_IN_REFRESH_REQUEST_ID)
544 message.Free(&message);
549 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
551 int dxgi_status = win_shadow_check_resize(subsystem);
556 if (dxgi_status >= 0)
564 if (win_shadow_follow_input_desktop() || refresh ||
565 !subsystem->dxgiOutputDuplication)
568 if (win_shadow_dxgi_restart_duplication(subsystem) > 0)
569 (void)win_shadow_invalidate_region(subsystem, 0, 0, subsystem->width,
573 if (subsystem->dxgiOutputDuplication)
574 dxgi_status = win_shadow_dxgi_get_next_frame(subsystem);
581 const int changed = win_shadow_dxgi_get_invalid_region(subsystem);
582 rdpShadowSurface* surface = subsystem->base.server->surface;
583 WINPR_ASSERT(surface);
584 EnterCriticalSection(&(surface->lock));
585 const BOOL pending = !region16_is_empty(&(surface->invalidRegion));
586 LeaveCriticalSection(&(surface->lock));
587 dxgi_status = ((changed > 0) || ((changed == 0) && pending)) ? 1 : changed;
592 win_shadow_surface_copy(subsystem);
602 UINT32 captureFrameRate = UINT32_MAX;
603 ArrayList_Lock(subsystem->base.server->clients);
604 const UINT32 clientCount = ArrayList_Count(subsystem->base.server->clients);
605 for (UINT32 index = 0; index < clientCount; index++)
607 const rdpShadowClient* client =
608 (rdpShadowClient*)ArrayList_GetItem(subsystem->base.server->clients, index);
610 if (client && client->encoder)
612 const UINT32 suggestion = shadow_encoder_preferred_fps(client->encoder);
613 if (suggestion < captureFrameRate)
614 captureFrameRate = suggestion;
617 ArrayList_Unlock(subsystem->base.server->clients);
619 if ((captureFrameRate == UINT32_MAX) || (captureFrameRate < 1))
620 captureFrameRate = subsystem->base.captureFrameRate;
621 subsystem->base.captureFrameRate = captureFrameRate;
623 dwInterval = 1000 / subsystem->base.captureFrameRate;
624 frameTime += dwInterval;
634static UINT32 win_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors)
643 DISPLAY_DEVICE displayDevice = WINPR_C_ARRAY_INIT;
645 displayDevice.cb =
sizeof(DISPLAY_DEVICE);
653 BOOL attached = FALSE;
654 DISPLAY_DEVICE probe = WINPR_C_ARRAY_INIT;
655 probe.cb =
sizeof(DISPLAY_DEVICE);
656 while (EnumDisplayDevices(
nullptr, devNum, &probe, 0))
658 if (probe.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
664 probe.cb =
sizeof(DISPLAY_DEVICE);
666 if (attached && (devNum != 0))
668 displayDevice.cb =
sizeof(DISPLAY_DEVICE);
669 if (EnumDisplayDevices(
nullptr, devNum, &displayDevice, 0))
674 if (EnumDisplayDevices(
nullptr, iDevNum, &displayDevice, 0))
676 hdc = CreateDC(displayDevice.DeviceName,
nullptr,
nullptr,
nullptr);
677 desktopWidth = GetDeviceCaps(hdc, HORZRES);
678 desktopHeight = GetDeviceCaps(hdc, VERTRES);
681 monitor = &monitors[index];
685 monitor->right = desktopWidth - 1;
686 monitor->bottom = desktopHeight - 1;
694 if ((numMonitors == 0) || (monitors[0].right <= 0) || (monitors[0].bottom <= 0))
696 monitor = &monitors[0];
699 monitor->right = GetSystemMetrics(SM_CXSCREEN) - 1;
700 monitor->bottom = GetSystemMetrics(SM_CYSCREEN) - 1;
711int win_shadow_check_resize(winShadowSubsystem* subsystem)
713 WINPR_ASSERT(subsystem);
715 DEVMODEW dm = WINPR_C_ARRAY_INIT;
716 dm.dmSize = (WORD)
sizeof(dm);
718 if (!EnumDisplaySettingsW(
nullptr, ENUM_CURRENT_SETTINGS, &dm))
722 if (((UINT32)dm.dmPelsWidth == (UINT32)subsystem->width) &&
723 ((UINT32)dm.dmPelsHeight == (UINT32)subsystem->height))
726 MONITOR_DEF* virtualScreen = &(subsystem->base.virtualScreen);
730 subsystem->base.numMonitors = win_shadow_enum_monitors(subsystem->base.monitors, 16);
732 if (subsystem->base.numMonitors < 1)
736 const MONITOR_DEF* primary = &subsystem->base.monitors[0];
737 const int newWidth = primary->right - primary->left + 1;
738 const int newHeight = primary->bottom - primary->top + 1;
740 if ((newWidth == subsystem->width) && (newHeight == subsystem->height))
745 if (subsystem->base.server && subsystem->base.server->screen &&
746 !shadow_screen_resize(subsystem->base.server->screen))
749 subsystem->width = newWidth;
750 subsystem->height = newHeight;
753 virtualScreen->left = 0;
754 virtualScreen->top = 0;
755 virtualScreen->right = subsystem->width - 1;
756 virtualScreen->bottom = subsystem->height - 1;
757 virtualScreen->flags = 1;
759 WLog_INFO(TAG,
"desktop resized to %dx%d", subsystem->width, subsystem->height);
763static int win_shadow_subsystem_init(rdpShadowSubsystem* arg)
765 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
768 subsystem->base.numMonitors = win_shadow_enum_monitors(subsystem->base.monitors, 16);
769 if (subsystem->base.numMonitors > 0)
771 const MONITOR_DEF* primary = &subsystem->base.monitors[0];
772 subsystem->width = primary->right - primary->left + 1;
773 subsystem->height = primary->bottom - primary->top + 1;
776 if ((subsystem->width <= 0) || (subsystem->height <= 0))
778 subsystem->width = GetSystemMetrics(SM_CXSCREEN);
779 subsystem->height = GetSystemMetrics(SM_CYSCREEN);
781#if defined(WITH_WDS_API)
782 status = win_shadow_wds_init(subsystem);
783#elif defined(WITH_DXGI_1_2)
784 status = win_shadow_dxgi_init(subsystem);
786 virtualScreen = &(subsystem->base.virtualScreen);
787 virtualScreen->left = 0;
788 virtualScreen->top = 0;
789 virtualScreen->right = subsystem->width - 1;
790 virtualScreen->bottom = subsystem->height - 1;
791 virtualScreen->flags = 1;
792 WLog_INFO(TAG,
"width: %d height: %d", subsystem->width, subsystem->height);
794 if (!win_shadow_audio_init(subsystem))
795 WLog_WARN(TAG,
"audio: no formats to offer: no sound");
799static int win_shadow_subsystem_uninit(rdpShadowSubsystem* arg)
801 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
806 win_shadow_audio_uninit(subsystem);
807#if defined(WITH_WDS_API)
808 win_shadow_wds_uninit(subsystem);
809#elif defined(WITH_DXGI_1_2)
810 win_shadow_dxgi_uninit(subsystem);
815static int win_shadow_subsystem_start(rdpShadowSubsystem* arg)
817 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
824 CreateThread(
nullptr, 0, win_shadow_subsystem_thread, (
void*)subsystem, 0,
nullptr)))
826 WLog_ERR(TAG,
"Failed to create thread");
831 if (!win_shadow_audio_start(subsystem))
832 WLog_WARN(TAG,
"audio: no capture thread: no sound");
836static int win_shadow_subsystem_stop(rdpShadowSubsystem* arg)
838 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
843 win_shadow_audio_stop(subsystem);
847static void win_shadow_subsystem_free(rdpShadowSubsystem* arg)
849 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
854 win_shadow_subsystem_uninit(arg);
858static rdpShadowSubsystem* win_shadow_subsystem_new(
void)
860 winShadowSubsystem* subsystem;
861 subsystem = (winShadowSubsystem*)calloc(1,
sizeof(winShadowSubsystem));
866 subsystem->base.SynchronizeEvent = win_shadow_input_synchronize_event;
867 subsystem->base.KeyboardEvent = win_shadow_input_keyboard_event;
868 subsystem->base.UnicodeKeyboardEvent = win_shadow_input_unicode_keyboard_event;
869 subsystem->base.MouseEvent = win_shadow_input_mouse_event;
870 subsystem->base.ExtendedMouseEvent = win_shadow_input_extended_mouse_event;
871 return &subsystem->base;
874FREERDP_API
const char* ShadowSubsystemName(
void)
879FREERDP_API
int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
881 char name[] =
"windows shadow subsystem";
882 char* arg[] = { name };
884 freerdp_server_warn_unmaintained(ARRAYSIZE(arg), arg);
885 pEntryPoints->New = win_shadow_subsystem_new;
886 pEntryPoints->Free = win_shadow_subsystem_free;
887 pEntryPoints->Init = win_shadow_subsystem_init;
888 pEntryPoints->Uninit = win_shadow_subsystem_uninit;
889 pEntryPoints->Start = win_shadow_subsystem_start;
890 pEntryPoints->Stop = win_shadow_subsystem_stop;
891 pEntryPoints->EnumMonitors = win_shadow_enum_monitors;