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#if defined(CHANNEL_CLIPRDR_SERVER)
35#include "win_shadow_cliprdr.h"
38#define TAG SERVER_TAG("shadow.win")
43#ifndef MOUSEEVENTF_HWHEEL
44#define MOUSEEVENTF_HWHEEL 0x1000
48static WINPR_TLS HDESK win_shadow_thread_desktop =
nullptr;
57static BOOL win_shadow_follow_input_desktop(
void)
59 WCHAR inputName[64] = WINPR_C_ARRAY_INIT;
60 WCHAR threadName[64] = WINPR_C_ARRAY_INIT;
61 HDESK input = OpenInputDesktop(0, FALSE, GENERIC_ALL);
66 if (!GetUserObjectInformationW(input, UOI_NAME, inputName,
sizeof(inputName),
nullptr))
67 WLog_WARN(TAG,
"GetUserObjectInformationW(input desktop, UOI_NAME) failed: 0x%08lX",
69 if (!GetUserObjectInformationW(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME, threadName,
70 sizeof(threadName),
nullptr))
71 WLog_WARN(TAG,
"GetUserObjectInformationW(thread desktop, UOI_NAME) failed: 0x%08lX",
74 if (_wcsicmp(inputName, threadName) == 0)
76 (void)CloseDesktop(input);
80 if (!SetThreadDesktop(input))
82 WLog_WARN(TAG,
"SetThreadDesktop(%ls) failed: 0x%08lX", inputName, GetLastError());
83 (void)CloseDesktop(input);
88 if (win_shadow_thread_desktop)
89 (void)CloseDesktop(win_shadow_thread_desktop);
90 win_shadow_thread_desktop = input;
91 WLog_INFO(TAG,
"thread %lu follows the input desktop from %ls to %ls", GetCurrentThreadId(),
92 threadName, inputName);
101static void win_shadow_send_input(
const char* what, INPUT* event)
106 if (SendInput(1, event,
sizeof(INPUT)) == 0)
107 WLog_WARN(TAG,
"%s: SendInput refused the event (0x%08lX), it is dropped", what,
120static void win_shadow_check_input_integrity(
void)
122 HANDLE token =
nullptr;
125 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
127 WLog_WARN(TAG,
"OpenProcessToken failed: cannot read the process integrity level (0x%08lX)",
134 if (!GetTokenInformation(token, TokenIntegrityLevel,
nullptr, 0, &length) &&
135 (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
140 WLog_WARN(TAG,
"out of memory: cannot read the process integrity level");
143 if (GetTokenInformation(token, TokenIntegrityLevel, label, length, &length) &&
146 const PSID sid = label->Label.Sid;
147 const DWORD rid = *GetSidSubAuthority(sid, *GetSidSubAuthorityCount(sid) - 1);
149 if (rid < SECURITY_MANDATORY_HIGH_RID)
151 "process is NOT elevated (integrity level 0x%04lX, Medium or lower): "
152 "injected input to elevated windows will be silently dropped by UIPI "
153 "- run the shadow elevated (e.g. scheduled task RunLevel Highest) or "
154 "expect \"keyboard and mouse dead while picture is fine\" reports",
158 "process integrity level 0x%04lX: injected input reaches elevated "
165 "GetTokenInformation(TokenIntegrityLevel) failed: cannot read the process "
166 "integrity level (0x%08lX)",
173 (void)CloseHandle(token);
178static void win_shadow_send_sas(
void)
180 HANDLE
event = OpenEventW(EVENT_MODIFY_STATE, FALSE, WIN_SHADOW_SAS_EVENT);
184 WLog_WARN(TAG,
"Ctrl+Alt+Del: no freerdp-shadow-service to send it (0x%08lX)",
191 "Ctrl+Alt+Del: asked freerdp-shadow-service for the secure attention sequence");
193 WLog_WARN(TAG,
"Ctrl+Alt+Del: SetEvent failed (0x%08lX)", GetLastError());
194 (void)CloseHandle(event);
197static BOOL win_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
198 rdpShadowClient* client, UINT32 flags)
200 WLog_WARN(TAG,
"TODO: Implement!");
204static BOOL win_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
205 UINT16 flags, UINT8 code)
207 winShadowSubsystem* win = (winShadowSubsystem*)subsystem;
208 const BOOL down = (flags & KBD_FLAGS_RELEASE) == 0;
211 (void)win_shadow_follow_input_desktop();
216 win->ctrlDown = down;
217 else if (code == 0x38)
219 else if ((code == 0x53) && win->ctrlDown && win->altDown)
222 win_shadow_send_sas();
225 INPUT
event = WINPR_C_ARRAY_INIT;
226 event.type = INPUT_KEYBOARD;
228#if defined(NONAMELESSUNION)
236 ki->dwFlags = KEYEVENTF_SCANCODE;
240 if (flags & KBD_FLAGS_RELEASE)
241 ki->dwFlags |= KEYEVENTF_KEYUP;
243 if (flags & KBD_FLAGS_EXTENDED)
244 ki->dwFlags |= KEYEVENTF_EXTENDEDKEY;
246 win_shadow_send_input(
"keyboard", &event);
250static BOOL win_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
251 rdpShadowClient* client, UINT16 flags,
254 (void)win_shadow_follow_input_desktop();
255 INPUT
event = WINPR_C_ARRAY_INIT;
256 event.type = INPUT_KEYBOARD;
258#if defined(NONAMELESSUNION)
265 ki->dwFlags = KEYEVENTF_UNICODE;
269 if (flags & KBD_FLAGS_RELEASE)
270 ki->dwFlags |= KEYEVENTF_KEYUP;
272 win_shadow_send_input(
"unicode keyboard", &event);
276static BOOL win_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
277 UINT16 flags, UINT16 x, UINT16 y)
279 (void)win_shadow_follow_input_desktop();
280 INPUT
event = WINPR_C_ARRAY_INIT;
284#if defined(NONAMELESSUNION)
290 event.type = INPUT_MOUSE;
292 if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
294 if (flags & PTR_FLAGS_WHEEL)
295 mi->dwFlags = MOUSEEVENTF_WHEEL;
297 mi->dwFlags = MOUSEEVENTF_HWHEEL;
298 mi->mouseData = flags & WheelRotationMask;
300 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
303 win_shadow_send_input(
"mouse", &event);
307 width = (float)GetSystemMetrics(SM_CXSCREEN);
308 height = (float)GetSystemMetrics(SM_CYSCREEN);
309 mi->dx = (LONG)((
float)x * (65535.0f / width));
310 mi->dy = (LONG)((
float)y * (65535.0f / height));
311 mi->dwFlags = MOUSEEVENTF_ABSOLUTE;
313 if (flags & PTR_FLAGS_MOVE)
315 mi->dwFlags |= MOUSEEVENTF_MOVE;
316 win_shadow_send_input(
"mouse", &event);
319 mi->dwFlags = MOUSEEVENTF_ABSOLUTE;
321 if (flags & PTR_FLAGS_BUTTON1)
323 if (flags & PTR_FLAGS_DOWN)
324 mi->dwFlags |= MOUSEEVENTF_LEFTDOWN;
326 mi->dwFlags |= MOUSEEVENTF_LEFTUP;
328 win_shadow_send_input(
"mouse", &event);
330 else if (flags & PTR_FLAGS_BUTTON2)
332 if (flags & PTR_FLAGS_DOWN)
333 mi->dwFlags |= MOUSEEVENTF_RIGHTDOWN;
335 mi->dwFlags |= MOUSEEVENTF_RIGHTUP;
337 win_shadow_send_input(
"mouse", &event);
339 else if (flags & PTR_FLAGS_BUTTON3)
341 if (flags & PTR_FLAGS_DOWN)
342 mi->dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
344 mi->dwFlags |= MOUSEEVENTF_MIDDLEUP;
346 win_shadow_send_input(
"mouse", &event);
353static BOOL win_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
354 rdpShadowClient* client, UINT16 flags, UINT16 x,
357 (void)win_shadow_follow_input_desktop();
358 INPUT
event = WINPR_C_ARRAY_INIT;
362#if defined(NONAMELESSUNION)
368 if ((flags & PTR_XFLAGS_BUTTON1) || (flags & PTR_XFLAGS_BUTTON2))
370 event.type = INPUT_MOUSE;
372 if (flags & PTR_FLAGS_MOVE)
374 width = (float)GetSystemMetrics(SM_CXSCREEN);
375 height = (float)GetSystemMetrics(SM_CYSCREEN);
376 mi->dx = (LONG)((
float)x * (65535.0f / width));
377 mi->dy = (LONG)((
float)y * (65535.0f / height));
378 mi->dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
379 win_shadow_send_input(
"extended mouse", &event);
382 mi->dx = mi->dy = mi->dwFlags = 0;
384 if (flags & PTR_XFLAGS_DOWN)
385 mi->dwFlags |= MOUSEEVENTF_XDOWN;
387 mi->dwFlags |= MOUSEEVENTF_XUP;
389 if (flags & PTR_XFLAGS_BUTTON1)
390 mi->mouseData = XBUTTON1;
391 else if (flags & PTR_XFLAGS_BUTTON2)
392 mi->mouseData = XBUTTON2;
394 win_shadow_send_input(
"extended mouse", &event);
400static int win_shadow_invalidate_region(winShadowSubsystem* subsystem,
int x,
int y,
int width,
403 rdpShadowServer* server = subsystem->base.server;
404 rdpShadowSurface* surface = server->surface;
405 RECTANGLE_16 invalidRect = { .left = x, .top = y, .right = x + width, .bottom = y + height };
406 EnterCriticalSection(&(surface->lock));
407 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
408 LeaveCriticalSection(&(surface->lock));
412static int win_shadow_surface_copy(winShadowSubsystem* subsystem)
420 BYTE* pDstData =
nullptr;
421 rdpShadowServer* server;
422 rdpShadowSurface* surface;
426 server = subsystem->base.server;
427 surface = server->surface;
429 if (ArrayList_Count(server->clients) < 1)
432 surfaceRect.left = surface->x;
433 surfaceRect.top = surface->y;
434 surfaceRect.right = surface->x + surface->width;
435 surfaceRect.bottom = surface->y + surface->height;
436 region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
438 if (region16_is_empty(&(surface->invalidRegion)))
441 extents = region16_extents(&(surface->invalidRegion));
442 CopyMemory(&invalidRect, extents,
sizeof(
RECTANGLE_16));
443 shadow_capture_align_clip_rect(&invalidRect, &surfaceRect);
444 x = invalidRect.left;
446 width = invalidRect.right - invalidRect.left;
447 height = invalidRect.bottom - invalidRect.top;
451 if (x + width > subsystem->width)
452 width = subsystem->width - x;
453 if (y + height > subsystem->height)
454 height = subsystem->height - y;
455 if ((width <= 0) || (height <= 0))
457 region16_clear(&(surface->invalidRegion));
465 width = surface->width;
466 height = surface->height;
469 WLog_INFO(TAG,
"SurfaceCopy x: %d y: %d width: %d height: %d right: %d bottom: %d", x, y, width,
470 height, x + width, y + height);
471#if defined(WITH_WDS_API)
477 WINPR_ASSERT(subsystem);
478 shw = subsystem->shw;
481 context = &shw->common.context;
482 WINPR_ASSERT(context);
487 pDstData = gdi->primary_buffer;
488 nDstStep = gdi->width * 4;
489 DstFormat = gdi->dstFormat;
491#elif defined(WITH_DXGI_1_2)
492 DstFormat = PIXEL_FORMAT_BGRX32;
493 status = win_shadow_dxgi_fetch_frame_data(subsystem, &pDstData, &nDstStep, x, y, width, height);
499#if defined(WITH_WDS_API)
503#elif defined(WITH_DXGI_1_2)
513 if (!freerdp_image_copy_no_overlap(surface->data, surface->format, surface->scanline, x, y,
514 width, height, pDstData, DstFormat, nDstStep, srcX, srcY,
515 nullptr, FREERDP_FLIP_NONE))
516 return ERROR_INTERNAL_ERROR;
527 shadow_subsystem_frame_update(&subsystem->base);
528 region16_clear(&(surface->invalidRegion));
532#if defined(WITH_WDS_API)
534static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg)
536 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
541 StopEvent = subsystem->base.server->StopEvent;
543 events[nCount++] = StopEvent;
544 events[nCount++] = subsystem->RdpUpdateEnterEvent;
548 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
550 if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
555 if (WaitForSingleObject(subsystem->RdpUpdateEnterEvent, 0) == WAIT_OBJECT_0)
557 win_shadow_surface_copy(subsystem);
558 (void)ResetEvent(subsystem->RdpUpdateEnterEvent);
559 (void)SetEvent(subsystem->RdpUpdateLeaveEvent);
567#elif defined(WITH_DXGI_1_2)
569static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg)
571 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
580 StopEvent = subsystem->base.server->StopEvent;
582 WINPR_ASSERT(MsgPipe);
584 events[nCount++] = StopEvent;
585 events[nCount++] = MessageQueue_Event(MsgPipe->In);
586 subsystem->base.captureFrameRate = 120;
587 dwInterval = 1000 / subsystem->base.captureFrameRate;
588 frameTime = GetTickCount64() + dwInterval;
589 BOOL refresh = FALSE;
593 dwTimeout = INFINITE;
594 cTime = GetTickCount64();
595 dwTimeout = (DWORD)((cTime > frameTime) ? 0 : frameTime - cTime);
596 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
598 if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
605 wMessage message = WINPR_C_ARRAY_INIT;
607 while (!quit && MessageQueue_Peek(MsgPipe->In, &message, TRUE))
609 quit = (message.id == WMQ_QUIT);
610 if (message.id == SHADOW_MSG_IN_REFRESH_REQUEST_ID)
613 message.Free(&message);
618 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
620 int dxgi_status = win_shadow_check_resize(subsystem);
625 if (dxgi_status >= 0)
638 const BOOL followed = win_shadow_follow_input_desktop();
639 const BOOL retry = !subsystem->dxgiOutputDuplication &&
640 (GetTickCount64() >= subsystem->dxgiRetryTime);
641 if (followed || refresh || retry)
644 if (win_shadow_dxgi_restart_duplication(subsystem) > 0)
645 (
void)win_shadow_invalidate_region(subsystem, 0, 0, subsystem->width,
651 dxgi_status = subsystem->dxgiOutputDuplication
652 ? win_shadow_dxgi_get_next_frame(subsystem)
660 const int changed = win_shadow_dxgi_get_invalid_region(subsystem);
661 rdpShadowSurface* surface = subsystem->base.server->surface;
662 WINPR_ASSERT(surface);
663 EnterCriticalSection(&(surface->lock));
664 const BOOL pending = !region16_is_empty(&(surface->invalidRegion));
665 LeaveCriticalSection(&(surface->lock));
666 dxgi_status = ((changed > 0) || ((changed == 0) && pending)) ? 1 : changed;
671 win_shadow_surface_copy(subsystem);
681 UINT32 captureFrameRate = UINT32_MAX;
682 ArrayList_Lock(subsystem->base.server->clients);
683 const UINT32 clientCount = ArrayList_Count(subsystem->base.server->clients);
684 for (UINT32 index = 0; index < clientCount; index++)
686 const rdpShadowClient* client =
687 (rdpShadowClient*)ArrayList_GetItem(subsystem->base.server->clients, index);
689 if (client && client->encoder)
691 const UINT32 suggestion = shadow_encoder_preferred_fps(client->encoder);
692 if (suggestion < captureFrameRate)
693 captureFrameRate = suggestion;
696 ArrayList_Unlock(subsystem->base.server->clients);
698 if ((captureFrameRate == UINT32_MAX) || (captureFrameRate < 1))
699 captureFrameRate = subsystem->base.captureFrameRate;
700 subsystem->base.captureFrameRate = captureFrameRate;
702 dwInterval = 1000 / subsystem->base.captureFrameRate;
703 frameTime += dwInterval;
713static UINT32 win_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors)
722 DISPLAY_DEVICE displayDevice = WINPR_C_ARRAY_INIT;
724 displayDevice.cb =
sizeof(DISPLAY_DEVICE);
732 BOOL attached = FALSE;
733 DISPLAY_DEVICE probe = WINPR_C_ARRAY_INIT;
734 probe.cb =
sizeof(DISPLAY_DEVICE);
735 while (EnumDisplayDevices(
nullptr, devNum, &probe, 0))
737 if (probe.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
743 probe.cb =
sizeof(DISPLAY_DEVICE);
745 if (attached && (devNum != 0))
747 displayDevice.cb =
sizeof(DISPLAY_DEVICE);
748 if (EnumDisplayDevices(
nullptr, devNum, &displayDevice, 0))
753 if (EnumDisplayDevices(
nullptr, iDevNum, &displayDevice, 0))
755 hdc = CreateDC(displayDevice.DeviceName,
nullptr,
nullptr,
nullptr);
756 desktopWidth = GetDeviceCaps(hdc, HORZRES);
757 desktopHeight = GetDeviceCaps(hdc, VERTRES);
760 monitor = &monitors[index];
764 monitor->right = desktopWidth - 1;
765 monitor->bottom = desktopHeight - 1;
773 if ((numMonitors == 0) || (monitors[0].right <= 0) || (monitors[0].bottom <= 0))
775 monitor = &monitors[0];
778 monitor->right = GetSystemMetrics(SM_CXSCREEN) - 1;
779 monitor->bottom = GetSystemMetrics(SM_CYSCREEN) - 1;
790int win_shadow_check_resize(winShadowSubsystem* subsystem)
792 WINPR_ASSERT(subsystem);
794 DEVMODEW dm = WINPR_C_ARRAY_INIT;
795 dm.dmSize = (WORD)
sizeof(dm);
797 if (!EnumDisplaySettingsW(
nullptr, ENUM_CURRENT_SETTINGS, &dm))
801 if (((UINT32)dm.dmPelsWidth == (UINT32)subsystem->width) &&
802 ((UINT32)dm.dmPelsHeight == (UINT32)subsystem->height))
805 MONITOR_DEF* virtualScreen = &(subsystem->base.virtualScreen);
809 subsystem->base.numMonitors = win_shadow_enum_monitors(subsystem->base.monitors, 16);
811 if (subsystem->base.numMonitors < 1)
815 const MONITOR_DEF* primary = &subsystem->base.monitors[0];
816 const int newWidth = primary->right - primary->left + 1;
817 const int newHeight = primary->bottom - primary->top + 1;
819 if ((newWidth == subsystem->width) && (newHeight == subsystem->height))
824 if (subsystem->base.server && subsystem->base.server->screen &&
825 !shadow_screen_resize(subsystem->base.server->screen))
828 subsystem->width = newWidth;
829 subsystem->height = newHeight;
832 virtualScreen->left = 0;
833 virtualScreen->top = 0;
834 virtualScreen->right = subsystem->width - 1;
835 virtualScreen->bottom = subsystem->height - 1;
836 virtualScreen->flags = 1;
838 WLog_INFO(TAG,
"desktop resized to %dx%d", subsystem->width, subsystem->height);
842static BOOL win_shadow_client_connect(rdpShadowSubsystem* subsystem, rdpShadowClient* client)
844 WINPR_ASSERT(subsystem);
845 WINPR_ASSERT(client);
847#if defined(CHANNEL_CLIPRDR_SERVER)
850 if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, CLIPRDR_SVC_CHANNEL_NAME))
852 if (win_shadow_cliprdr_init(client) < 0)
859static void win_shadow_client_disconnect(rdpShadowSubsystem* subsystem, rdpShadowClient* client)
861 WINPR_ASSERT(subsystem);
862 WINPR_ASSERT(client);
864#if defined(CHANNEL_CLIPRDR_SERVER)
865 win_shadow_cliprdr_uninit(client);
869static int win_shadow_subsystem_init(rdpShadowSubsystem* arg)
871 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
874 win_shadow_check_input_integrity();
875 subsystem->base.numMonitors = win_shadow_enum_monitors(subsystem->base.monitors, 16);
876 if (subsystem->base.numMonitors > 0)
878 const MONITOR_DEF* primary = &subsystem->base.monitors[0];
879 subsystem->width = primary->right - primary->left + 1;
880 subsystem->height = primary->bottom - primary->top + 1;
883 if ((subsystem->width <= 0) || (subsystem->height <= 0))
885 subsystem->width = GetSystemMetrics(SM_CXSCREEN);
886 subsystem->height = GetSystemMetrics(SM_CYSCREEN);
888#if defined(WITH_WDS_API)
896 "capture uses the Windows Desktop Sharing API: the low performance capture path, "
897 "throughput drops with multiple clients or high frame rates - build with "
898 "WITH_SHADOW_WIN_DXGI=ON for DXGI desktop duplication");
899 status = win_shadow_wds_init(subsystem);
900#elif defined(WITH_DXGI_1_2)
901 status = win_shadow_dxgi_init(subsystem);
903 WLog_WARN(TAG,
"DXGI init failed, the capture thread retries at a low rate");
905 virtualScreen = &(subsystem->base.virtualScreen);
906 virtualScreen->left = 0;
907 virtualScreen->top = 0;
908 virtualScreen->right = subsystem->width - 1;
909 virtualScreen->bottom = subsystem->height - 1;
910 virtualScreen->flags = 1;
911 WLog_INFO(TAG,
"width: %d height: %d", subsystem->width, subsystem->height);
913 if (!win_shadow_audio_init(subsystem))
914 WLog_WARN(TAG,
"audio: no formats to offer: no sound");
918static int win_shadow_subsystem_uninit(rdpShadowSubsystem* arg)
920 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
925 win_shadow_audio_uninit(subsystem);
926#if defined(WITH_WDS_API)
927 win_shadow_wds_uninit(subsystem);
928#elif defined(WITH_DXGI_1_2)
929 win_shadow_dxgi_uninit(subsystem);
934static int win_shadow_subsystem_start(rdpShadowSubsystem* arg)
936 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
943 CreateThread(
nullptr, 0, win_shadow_subsystem_thread, (
void*)subsystem, 0,
nullptr)))
945 WLog_ERR(TAG,
"Failed to create thread");
950 if (!win_shadow_audio_start(subsystem))
951 WLog_WARN(TAG,
"audio: no capture thread: no sound");
955static int win_shadow_subsystem_stop(rdpShadowSubsystem* arg)
957 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
962 win_shadow_audio_stop(subsystem);
966static void win_shadow_subsystem_free(rdpShadowSubsystem* arg)
968 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
973 win_shadow_subsystem_uninit(arg);
977static rdpShadowSubsystem* win_shadow_subsystem_new(
void)
979 winShadowSubsystem* subsystem;
980 subsystem = (winShadowSubsystem*)calloc(1,
sizeof(winShadowSubsystem));
985 subsystem->base.SynchronizeEvent = win_shadow_input_synchronize_event;
986 subsystem->base.KeyboardEvent = win_shadow_input_keyboard_event;
987 subsystem->base.UnicodeKeyboardEvent = win_shadow_input_unicode_keyboard_event;
988 subsystem->base.MouseEvent = win_shadow_input_mouse_event;
989 subsystem->base.ExtendedMouseEvent = win_shadow_input_extended_mouse_event;
990 subsystem->base.ClientConnect = win_shadow_client_connect;
991 subsystem->base.ClientDisconnect = win_shadow_client_disconnect;
992 return &subsystem->base;
995FREERDP_API
const char* ShadowSubsystemName(
void)
1000FREERDP_API
int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
1002 char name[] =
"windows shadow subsystem";
1003 char* arg[] = { name };
1005 freerdp_server_warn_unmaintained(ARRAYSIZE(arg), arg);
1006 pEntryPoints->New = win_shadow_subsystem_new;
1007 pEntryPoints->Free = win_shadow_subsystem_free;
1008 pEntryPoints->Init = win_shadow_subsystem_init;
1009 pEntryPoints->Uninit = win_shadow_subsystem_uninit;
1010 pEntryPoints->Start = win_shadow_subsystem_start;
1011 pEntryPoints->Stop = win_shadow_subsystem_stop;
1012 pEntryPoints->EnumMonitors = win_shadow_enum_monitors;