diff options
Diffstat (limited to 'server/shadow/Win/win_shadow.c')
-rw-r--r-- | server/shadow/Win/win_shadow.c | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/server/shadow/Win/win_shadow.c b/server/shadow/Win/win_shadow.c new file mode 100644 index 0000000..d3d5a17 --- /dev/null +++ b/server/shadow/Win/win_shadow.c @@ -0,0 +1,560 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * 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 <windows.h> + +#include <winpr/crt.h> +#include <winpr/synch.h> +#include <winpr/sysinfo.h> + +#include <freerdp/log.h> +#include <freerdp/codec/color.h> +#include <freerdp/codec/region.h> +#include <freerdp/server/server-common.h> + +#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; +} |