summaryrefslogtreecommitdiffstats
path: root/server/shadow/Win/win_shadow.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/shadow/Win/win_shadow.c')
-rw-r--r--server/shadow/Win/win_shadow.c560
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;
+}