/** * FreeRDP: A Remote Desktop Protocol Implementation * * Copyright 2011-2014 Marc-Andre Moreau * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "win_shadow.h" #define TAG SERVER_TAG("shadow.win") /* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event * does not mention this flag is only supported if building for _WIN32_WINNT >= 0x0600 */ #ifndef MOUSEEVENTF_HWHEEL #define MOUSEEVENTF_HWHEEL 0x1000 #endif static BOOL win_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client, UINT32 flags) { WLog_WARN(TAG, "TODO: Implement!"); return TRUE; } static BOOL win_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client, UINT16 flags, UINT8 code) { UINT rc; INPUT event; event.type = INPUT_KEYBOARD; event.ki.wVk = 0; event.ki.wScan = code; event.ki.dwFlags = KEYEVENTF_SCANCODE; event.ki.dwExtraInfo = 0; event.ki.time = 0; if (flags & KBD_FLAGS_RELEASE) event.ki.dwFlags |= KEYEVENTF_KEYUP; if (flags & KBD_FLAGS_EXTENDED) event.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; rc = SendInput(1, &event, sizeof(INPUT)); if (rc == 0) return FALSE; return TRUE; } static BOOL win_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client, UINT16 flags, UINT16 code) { UINT rc; INPUT event; event.type = INPUT_KEYBOARD; event.ki.wVk = 0; event.ki.wScan = code; event.ki.dwFlags = KEYEVENTF_UNICODE; event.ki.dwExtraInfo = 0; event.ki.time = 0; if (flags & KBD_FLAGS_RELEASE) event.ki.dwFlags |= KEYEVENTF_KEYUP; rc = SendInput(1, &event, sizeof(INPUT)); if (rc == 0) return FALSE; return TRUE; } static BOOL win_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client, UINT16 flags, UINT16 x, UINT16 y) { UINT rc = 1; INPUT event = { 0 }; float width; float height; event.type = INPUT_MOUSE; if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL)) { if (flags & PTR_FLAGS_WHEEL) event.mi.dwFlags = MOUSEEVENTF_WHEEL; else event.mi.dwFlags = MOUSEEVENTF_HWHEEL; event.mi.mouseData = flags & WheelRotationMask; if (flags & PTR_FLAGS_WHEEL_NEGATIVE) event.mi.mouseData *= -1; rc = SendInput(1, &event, sizeof(INPUT)); /* The build target is a system that did not support MOUSEEVENTF_HWHEEL * but it may run on newer systems supporting it. * Ignore the return value in these cases. */ #if (_WIN32_WINNT < 0x0600) if (flags & PTR_FLAGS_HWHEEL) rc = 1; #endif } else { width = (float)GetSystemMetrics(SM_CXSCREEN); height = (float)GetSystemMetrics(SM_CYSCREEN); event.mi.dx = (LONG)((float)x * (65535.0f / width)); event.mi.dy = (LONG)((float)y * (65535.0f / height)); event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE; if (flags & PTR_FLAGS_MOVE) { event.mi.dwFlags |= MOUSEEVENTF_MOVE; rc = SendInput(1, &event, sizeof(INPUT)); if (rc == 0) return FALSE; } event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE; if (flags & PTR_FLAGS_BUTTON1) { if (flags & PTR_FLAGS_DOWN) event.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN; else event.mi.dwFlags |= MOUSEEVENTF_LEFTUP; rc = SendInput(1, &event, sizeof(INPUT)); } else if (flags & PTR_FLAGS_BUTTON2) { if (flags & PTR_FLAGS_DOWN) event.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN; else event.mi.dwFlags |= MOUSEEVENTF_RIGHTUP; rc = SendInput(1, &event, sizeof(INPUT)); } else if (flags & PTR_FLAGS_BUTTON3) { if (flags & PTR_FLAGS_DOWN) event.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN; else event.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP; rc = SendInput(1, &event, sizeof(INPUT)); } } if (rc == 0) return FALSE; return TRUE; } static BOOL win_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client, UINT16 flags, UINT16 x, UINT16 y) { UINT rc = 1; INPUT event = { 0 }; float width; float height; if ((flags & PTR_XFLAGS_BUTTON1) || (flags & PTR_XFLAGS_BUTTON2)) { event.type = INPUT_MOUSE; if (flags & PTR_FLAGS_MOVE) { width = (float)GetSystemMetrics(SM_CXSCREEN); height = (float)GetSystemMetrics(SM_CYSCREEN); event.mi.dx = (LONG)((float)x * (65535.0f / width)); event.mi.dy = (LONG)((float)y * (65535.0f / height)); event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; rc = SendInput(1, &event, sizeof(INPUT)); if (rc == 0) return FALSE; } event.mi.dx = event.mi.dy = event.mi.dwFlags = 0; if (flags & PTR_XFLAGS_DOWN) event.mi.dwFlags |= MOUSEEVENTF_XDOWN; else event.mi.dwFlags |= MOUSEEVENTF_XUP; if (flags & PTR_XFLAGS_BUTTON1) event.mi.mouseData = XBUTTON1; else if (flags & PTR_XFLAGS_BUTTON2) event.mi.mouseData = XBUTTON2; rc = SendInput(1, &event, sizeof(INPUT)); } if (rc == 0) return FALSE; return TRUE; } static int win_shadow_invalidate_region(winShadowSubsystem* subsystem, int x, int y, int width, int height) { rdpShadowServer* server; rdpShadowSurface* surface; RECTANGLE_16 invalidRect; server = subsystem->base.server; surface = server->surface; invalidRect.left = x; invalidRect.top = y; invalidRect.right = x + width; invalidRect.bottom = y + height; EnterCriticalSection(&(surface->lock)); region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); LeaveCriticalSection(&(surface->lock)); return 1; } static int win_shadow_surface_copy(winShadowSubsystem* subsystem) { int x, y; int width; int height; int count; int status = 1; int nDstStep = 0; DWORD DstFormat; BYTE* pDstData = NULL; rdpShadowServer* server; rdpShadowSurface* surface; RECTANGLE_16 surfaceRect; RECTANGLE_16 invalidRect; const RECTANGLE_16* extents; server = subsystem->base.server; surface = server->surface; if (ArrayList_Count(server->clients) < 1) return 1; surfaceRect.left = surface->x; surfaceRect.top = surface->y; surfaceRect.right = surface->x + surface->width; surfaceRect.bottom = surface->y + surface->height; region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect); if (region16_is_empty(&(surface->invalidRegion))) return 1; extents = region16_extents(&(surface->invalidRegion)); CopyMemory(&invalidRect, extents, sizeof(RECTANGLE_16)); shadow_capture_align_clip_rect(&invalidRect, &surfaceRect); x = invalidRect.left; y = invalidRect.top; width = invalidRect.right - invalidRect.left; height = invalidRect.bottom - invalidRect.top; if (0) { x = 0; y = 0; width = surface->width; height = surface->height; } WLog_INFO(TAG, "SurfaceCopy x: %d y: %d width: %d height: %d right: %d bottom: %d", x, y, width, height, x + width, y + height); #if defined(WITH_WDS_API) { rdpGdi* gdi; shwContext* shw; rdpContext* context; WINPR_ASSERT(subsystem); shw = subsystem->shw; WINPR_ASSERT(shw); context = &shw->common.context; WINPR_ASSERT(context); gdi = context->gdi; WINPR_ASSERT(gdi); pDstData = gdi->primary_buffer; nDstStep = gdi->width * 4; DstFormat = gdi->dstFormat; } #elif defined(WITH_DXGI_1_2) DstFormat = PIXEL_FORMAT_BGRX32; status = win_shadow_dxgi_fetch_frame_data(subsystem, &pDstData, &nDstStep, x, y, width, height); #endif if (status <= 0) return status; if (!freerdp_image_copy(surface->data, surface->format, surface->scanline, x, y, width, height, pDstData, DstFormat, nDstStep, x, y, NULL, FREERDP_FLIP_NONE)) return ERROR_INTERNAL_ERROR; ArrayList_Lock(server->clients); count = ArrayList_Count(server->clients); shadow_subsystem_frame_update(&subsystem->base); ArrayList_Unlock(server->clients); region16_clear(&(surface->invalidRegion)); return 1; } #if defined(WITH_WDS_API) static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg) { winShadowSubsystem* subsystem = (winShadowSubsystem*)arg; DWORD status; DWORD nCount; HANDLE events[32]; HANDLE StopEvent; StopEvent = subsystem->base.server->StopEvent; nCount = 0; events[nCount++] = StopEvent; events[nCount++] = subsystem->RdpUpdateEnterEvent; while (1) { status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0) { break; } if (WaitForSingleObject(subsystem->RdpUpdateEnterEvent, 0) == WAIT_OBJECT_0) { win_shadow_surface_copy(subsystem); ResetEvent(subsystem->RdpUpdateEnterEvent); SetEvent(subsystem->RdpUpdateLeaveEvent); } } ExitThread(0); return 0; } #elif defined(WITH_DXGI_1_2) static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg) { winShadowSubsystem* subsystem = (winShadowSubsystem*)arg; int fps; DWORD status; DWORD nCount; UINT64 cTime; DWORD dwTimeout; DWORD dwInterval; UINT64 frameTime; HANDLE events[32]; HANDLE StopEvent; StopEvent = subsystem->server->StopEvent; nCount = 0; events[nCount++] = StopEvent; fps = 16; dwInterval = 1000 / fps; frameTime = GetTickCount64() + dwInterval; while (1) { dwTimeout = INFINITE; cTime = GetTickCount64(); dwTimeout = (DWORD)((cTime > frameTime) ? 0 : frameTime - cTime); status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout); if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0) { break; } if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime)) { int dxgi_status; dxgi_status = win_shadow_dxgi_get_next_frame(subsystem); if (dxgi_status > 0) dxgi_status = win_shadow_dxgi_get_invalid_region(subsystem); if (dxgi_status > 0) win_shadow_surface_copy(subsystem); dwInterval = 1000 / fps; frameTime += dwInterval; } } ExitThread(0); return 0; } #endif static UINT32 win_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors) { HDC hdc; int index; int desktopWidth; int desktopHeight; DWORD iDevNum = 0; int numMonitors = 0; MONITOR_DEF* monitor; DISPLAY_DEVICE displayDevice = { 0 }; displayDevice.cb = sizeof(DISPLAY_DEVICE); if (EnumDisplayDevices(NULL, iDevNum, &displayDevice, 0)) { hdc = CreateDC(displayDevice.DeviceName, NULL, NULL, NULL); desktopWidth = GetDeviceCaps(hdc, HORZRES); desktopHeight = GetDeviceCaps(hdc, VERTRES); index = 0; numMonitors = 1; monitor = &monitors[index]; monitor->left = 0; monitor->top = 0; monitor->right = desktopWidth; monitor->bottom = desktopHeight; monitor->flags = 1; DeleteDC(hdc); } return numMonitors; } static int win_shadow_subsystem_init(rdpShadowSubsystem* arg) { winShadowSubsystem* subsystem = (winShadowSubsystem*)arg; int status; MONITOR_DEF* virtualScreen; subsystem->base.numMonitors = win_shadow_enum_monitors(subsystem->base.monitors, 16); #if defined(WITH_WDS_API) status = win_shadow_wds_init(subsystem); #elif defined(WITH_DXGI_1_2) status = win_shadow_dxgi_init(subsystem); #endif virtualScreen = &(subsystem->base.virtualScreen); virtualScreen->left = 0; virtualScreen->top = 0; virtualScreen->right = subsystem->width; virtualScreen->bottom = subsystem->height; virtualScreen->flags = 1; WLog_INFO(TAG, "width: %d height: %d", subsystem->width, subsystem->height); return 1; } static int win_shadow_subsystem_uninit(rdpShadowSubsystem* arg) { winShadowSubsystem* subsystem = (winShadowSubsystem*)arg; if (!subsystem) return -1; #if defined(WITH_WDS_API) win_shadow_wds_uninit(subsystem); #elif defined(WITH_DXGI_1_2) win_shadow_dxgi_uninit(subsystem); #endif return 1; } static int win_shadow_subsystem_start(rdpShadowSubsystem* arg) { winShadowSubsystem* subsystem = (winShadowSubsystem*)arg; HANDLE thread; if (!subsystem) return -1; if (!(thread = CreateThread(NULL, 0, win_shadow_subsystem_thread, (void*)subsystem, 0, NULL))) { WLog_ERR(TAG, "Failed to create thread"); return -1; } return 1; } static int win_shadow_subsystem_stop(rdpShadowSubsystem* arg) { winShadowSubsystem* subsystem = (winShadowSubsystem*)arg; if (!subsystem) return -1; return 1; } static void win_shadow_subsystem_free(rdpShadowSubsystem* arg) { winShadowSubsystem* subsystem = (winShadowSubsystem*)arg; if (!subsystem) return; win_shadow_subsystem_uninit(arg); free(subsystem); } static rdpShadowSubsystem* win_shadow_subsystem_new(void) { winShadowSubsystem* subsystem; subsystem = (winShadowSubsystem*)calloc(1, sizeof(winShadowSubsystem)); if (!subsystem) return NULL; subsystem->base.SynchronizeEvent = win_shadow_input_synchronize_event; subsystem->base.KeyboardEvent = win_shadow_input_keyboard_event; subsystem->base.UnicodeKeyboardEvent = win_shadow_input_unicode_keyboard_event; subsystem->base.MouseEvent = win_shadow_input_mouse_event; subsystem->base.ExtendedMouseEvent = win_shadow_input_extended_mouse_event; return &subsystem->base; } FREERDP_API const char* ShadowSubsystemName(void) { return "Win"; } FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints) { const char name[] = "windows shadow subsystem"; const char* arg[] = { name }; freerdp_server_warn_unmaintained(ARRAYSIZE(arg), arg); pEntryPoints->New = win_shadow_subsystem_new; pEntryPoints->Free = win_shadow_subsystem_free; pEntryPoints->Init = win_shadow_subsystem_init; pEntryPoints->Uninit = win_shadow_subsystem_uninit; pEntryPoints->Start = win_shadow_subsystem_start; pEntryPoints->Stop = win_shadow_subsystem_stop; pEntryPoints->EnumMonitors = win_shadow_enum_monitors; return 1; }