From 7e4d3234278ea2ea76a6f3cfc0c763b166cf35e5 Mon Sep 17 00:00:00 2001 From: Quantum Date: Mon, 4 Jan 2021 16:01:24 -0500 Subject: [PATCH] get display DPI info to scale mouse movement --- common/include/common/KVMFR.h | 17 +++++---- common/include/common/dpi.h | 25 +++++++++++++ common/src/platform/windows/CMakeLists.txt | 1 + common/src/platform/windows/dpi.c | 37 +++++++++++++++++++ host/include/interface/capture.h | 1 + host/platform/Linux/capture/XCB/src/xcb.c | 6 +++ host/platform/Windows/capture/DXGI/src/dxgi.c | 29 ++++++--------- .../Windows/capture/NVFBC/src/nvfbc.c | 15 ++++++-- host/platform/Windows/src/platform.c | 13 ++++++- host/src/app.c | 15 ++++---- 10 files changed, 123 insertions(+), 36 deletions(-) create mode 100644 common/include/common/dpi.h create mode 100644 common/src/platform/windows/dpi.c diff --git a/common/include/common/KVMFR.h b/common/include/common/KVMFR.h index a764edce..3bf093b7 100644 --- a/common/include/common/KVMFR.h +++ b/common/include/common/KVMFR.h @@ -57,7 +57,7 @@ typedef enum CursorType CursorType; #define KVMFR_MAGIC "KVMFR---" -#define KVMFR_VERSION 5 +#define KVMFR_VERSION 6 typedef struct KVMFR { @@ -80,12 +80,13 @@ KVMFRCursor; typedef struct KVMFRFrame { - uint32_t formatVer; // the frame format version number - FrameType type; // the frame data type - uint32_t width; // the width - uint32_t height; // the height - uint32_t stride; // the row stride (zero if compressed data) - uint32_t pitch; // the row pitch (stride in bytes or the compressed frame size) - uint32_t offset; // offset from the start of this header to the FrameBuffer header + uint32_t formatVer; // the frame format version number + FrameType type; // the frame data type + uint32_t width; // the width + uint32_t height; // the height + uint32_t stride; // the row stride (zero if compressed data) + uint32_t pitch; // the row pitch (stride in bytes or the compressed frame size) + uint32_t offset; // offset from the start of this header to the FrameBuffer header + uint32_t mouseScalePercent; // movement scale factor of the mouse (relates to DPI of display, 100 = no scale) } KVMFRFrame; diff --git a/common/include/common/dpi.h b/common/include/common/dpi.h new file mode 100644 index 00000000..10dc679b --- /dev/null +++ b/common/include/common/dpi.h @@ -0,0 +1,25 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017-2020 Geoffrey McRae +https://looking-glass.hostfission.com + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +// At 100% scaling, Windows reports 96 DPI. +#define DPI_100_PERCENT 96 + +UINT monitor_dpi(HMONITOR hMonitor); diff --git a/common/src/platform/windows/CMakeLists.txt b/common/src/platform/windows/CMakeLists.txt index 6954b5b8..26e5a3b8 100644 --- a/common/src/platform/windows/CMakeLists.txt +++ b/common/src/platform/windows/CMakeLists.txt @@ -7,6 +7,7 @@ include_directories( add_library(lg_common_platform_code STATIC crash.c + dpi.c sysinfo.c thread.c event.c diff --git a/common/src/platform/windows/dpi.c b/common/src/platform/windows/dpi.c new file mode 100644 index 00000000..5db5ad5a --- /dev/null +++ b/common/src/platform/windows/dpi.c @@ -0,0 +1,37 @@ +#include "common/dpi.h" +#include "common/windebug.h" + +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI, + MDT_ANGULAR_DPI, + MDT_RAW_DPI, + MDT_DEFAULT +} MONITOR_DPI_TYPE; +typedef HRESULT (WINAPI *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT * dpiX, UINT * dpiY); + +UINT monitor_dpi(HMONITOR hMonitor) +{ + HMODULE shcore = LoadLibraryA("shcore.dll"); + if (!shcore) + { + DEBUG_ERROR("Could not load shcore.dll"); + return DPI_100_PERCENT; + } + + GetDpiForMonitor_t GetDpiForMonitor = (GetDpiForMonitor_t) GetProcAddress(shcore, "GetDpiForMonitor"); + if (!GetDpiForMonitor) + { + DEBUG_ERROR("Could not find GetDpiForMonitor"); + return DPI_100_PERCENT; + } + + UINT dpiX, dpiY; + HRESULT status = GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + if (FAILED(status)) + { + DEBUG_WINERROR("GetDpiForMonitor failed", status); + return DPI_100_PERCENT; + } + + return dpiX; +} diff --git a/host/include/interface/capture.h b/host/include/interface/capture.h index 99849cdf..2f6d7162 100644 --- a/host/include/interface/capture.h +++ b/host/include/interface/capture.h @@ -93,6 +93,7 @@ typedef struct CaptureInterface bool (*deinit )(); void (*free )(); unsigned int (*getMaxFrameSize)(); + unsigned int (*getMouseScale )(); CaptureResult (*capture )(); CaptureResult (*waitFrame )(CaptureFrame * frame); diff --git a/host/platform/Linux/capture/XCB/src/xcb.c b/host/platform/Linux/capture/XCB/src/xcb.c index 3914de3d..92f6df87 100644 --- a/host/platform/Linux/capture/XCB/src/xcb.c +++ b/host/platform/Linux/capture/XCB/src/xcb.c @@ -169,6 +169,11 @@ static unsigned int xcb_getMaxFrameSize() return this->width * this->height * 4; } +static unsigned int xcb_getMouseScale() +{ + return 100; +} + static CaptureResult xcb_capture() { assert(this); @@ -241,6 +246,7 @@ struct CaptureInterface Capture_XCB = .deinit = xcb_deinit, .free = xcb_free, .getMaxFrameSize = xcb_getMaxFrameSize, + .getMouseScale = xcb_getMouseScale, .capture = xcb_capture, .waitFrame = xcb_waitFrame, .getFrame = xcb_getFrame, diff --git a/host/platform/Windows/capture/DXGI/src/dxgi.c b/host/platform/Windows/capture/DXGI/src/dxgi.c index 725e2205..a3d49aaa 100644 --- a/host/platform/Windows/capture/DXGI/src/dxgi.c +++ b/host/platform/Windows/capture/DXGI/src/dxgi.c @@ -24,6 +24,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "common/option.h" #include "common/locking.h" #include "common/event.h" +#include "common/dpi.h" #include #include @@ -98,12 +99,12 @@ struct iface unsigned int pitch; unsigned int stride; CaptureFormat format; + unsigned int dpi; int lastPointerX, lastPointerY; bool lastPointerVisible; }; -static bool dpiDone = false; static struct iface * this = NULL; // forwards @@ -210,22 +211,6 @@ static bool dxgi_init() DEBUG_INFO("looking-glass-host.exe InstallService"); } - // this is required for DXGI 1.5 support to function - if (!dpiDone) - { - DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); - #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4) - typedef BOOL (*User32_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value); - - HMODULE user32 = LoadLibraryA("user32.dll"); - User32_SetProcessDpiAwarenessContext fn; - fn = (User32_SetProcessDpiAwarenessContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext"); - if (fn) - fn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - FreeLibrary(user32); - dpiDone = true; - } - HRESULT status; DXGI_OUTPUT_DESC outputDesc; @@ -388,6 +373,7 @@ static bool dxgi_init() IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc); this->width = outputDesc.DesktopCoordinates.right - outputDesc.DesktopCoordinates.left; this->height = outputDesc.DesktopCoordinates.bottom - outputDesc.DesktopCoordinates.top; + this->dpi = monitor_dpi(outputDesc.Monitor); ++this->formatVer; DEBUG_INFO("Device Descripion: %ls" , adapterDesc.Description); @@ -688,6 +674,14 @@ static unsigned int dxgi_getMaxFrameSize() return this->height * this->pitch; } +static unsigned int dxgi_getMouseScale() +{ + assert(this); + assert(this->initialized); + + return this->dpi * 100 / DPI_100_PERCENT; +} + static CaptureResult dxgi_hResultToCaptureResult(const HRESULT status) { switch(status) @@ -1013,6 +1007,7 @@ struct CaptureInterface Capture_DXGI = .deinit = dxgi_deinit, .free = dxgi_free, .getMaxFrameSize = dxgi_getMaxFrameSize, + .getMouseScale = dxgi_getMouseScale, .capture = dxgi_capture, .waitFrame = dxgi_waitFrame, .getFrame = dxgi_getFrame diff --git a/host/platform/Windows/capture/NVFBC/src/nvfbc.c b/host/platform/Windows/capture/NVFBC/src/nvfbc.c index b40e968f..cc63c159 100644 --- a/host/platform/Windows/capture/NVFBC/src/nvfbc.c +++ b/host/platform/Windows/capture/NVFBC/src/nvfbc.c @@ -25,6 +25,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "common/framebuffer.h" #include "common/event.h" #include "common/thread.h" +#include "common/dpi.h" #include #include #include @@ -44,6 +45,7 @@ struct iface unsigned int maxWidth , maxHeight; unsigned int width , height; + unsigned int dpi; unsigned int formatVer; unsigned int grabWidth, grabHeight, grabStride; @@ -65,7 +67,7 @@ static struct iface * this = NULL; static void nvfbc_free(); static int pointerThread(void * unused); -static void getDesktopSize(unsigned int * width, unsigned int * height) +static void getDesktopSize(unsigned int * width, unsigned int * height, unsigned int * dpi) { HMONITOR monitor = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTOPRIMARY); MONITORINFO monitorInfo = { @@ -73,6 +75,7 @@ static void getDesktopSize(unsigned int * width, unsigned int * height) }; GetMonitorInfo(monitor, &monitorInfo); + *dpi = monitor_dpi(monitor); CloseHandle(monitor); *width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; @@ -163,7 +166,7 @@ static bool nvfbc_create( static bool nvfbc_init() { this->stop = false; - getDesktopSize(&this->width, &this->height); + getDesktopSize(&this->width, &this->height, &this->dpi); lgResetEvent(this->frameEvent); HANDLE event; @@ -245,9 +248,14 @@ static unsigned int nvfbc_getMaxFrameSize() return this->maxWidth * this->maxHeight * 4; } +static unsigned int nvfbc_getMouseScale() +{ + return this->dpi * 100 / DPI_100_PERCENT; +} + static CaptureResult nvfbc_capture() { - getDesktopSize(&this->width, &this->height); + getDesktopSize(&this->width, &this->height, &this->dpi); NvFBCFrameGrabInfo grabInfo; CaptureResult result = NvFBCToSysCapture( this->nvfbc, @@ -397,6 +405,7 @@ struct CaptureInterface Capture_NVFBC = .deinit = nvfbc_deinit, .free = nvfbc_free, .getMaxFrameSize = nvfbc_getMaxFrameSize, + .getMouseScale = nvfbc_getMouseScale, .capture = nvfbc_capture, .waitFrame = nvfbc_waitFrame, .getFrame = nvfbc_getFrame diff --git a/host/platform/Windows/src/platform.c b/host/platform/Windows/src/platform.c index 848d58e4..9c522395 100644 --- a/host/platform/Windows/src/platform.c +++ b/host/platform/Windows/src/platform.c @@ -226,6 +226,18 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine // setup a handler for ctrl+c SetConsoleCtrlHandler(CtrlHandler, TRUE); + // enable high DPI awareness + // this is required for DXGI 1.5 support to function and also capturing desktops with high DPI + DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); + #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4) + typedef BOOL (*User32_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value); + + HMODULE user32 = GetModuleHandle("user32.dll"); + User32_SetProcessDpiAwarenessContext fn; + fn = (User32_SetProcessDpiAwarenessContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext"); + if (fn) + fn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + // create a message window so that our message pump works WNDCLASSEX wx = {}; wx.cbSize = sizeof(WNDCLASSEX); @@ -249,7 +261,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine app.messageWnd = CreateWindowEx(0, MAKEINTATOM(class), NULL, 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); // this is needed so that unprivileged processes can send us this message - HMODULE user32 = GetModuleHandle("user32.dll"); _ChangeWindowMessageFilterEx = (PChangeWindowMessageFilterEx)GetProcAddress(user32, "ChangeWindowMessageFilterEx"); if (_ChangeWindowMessageFilterEx) _ChangeWindowMessageFilterEx(app.messageWnd, app.trayRestartMsg, MSGFLT_ALLOW, NULL); diff --git a/host/src/app.c b/host/src/app.c index 790f5991..335cddea 100644 --- a/host/src/app.c +++ b/host/src/app.c @@ -186,13 +186,14 @@ static int frameThread(void * opaque) continue; } - fi->formatVer = frame.formatVer; - fi->width = frame.width; - fi->height = frame.height; - fi->stride = frame.stride; - fi->pitch = frame.pitch; - fi->offset = pageSize - FrameBufferStructSize; - frameValid = true; + fi->formatVer = frame.formatVer; + fi->width = frame.width; + fi->height = frame.height; + fi->stride = frame.stride; + fi->pitch = frame.pitch; + fi->offset = pageSize - FrameBufferStructSize; + fi->mouseScalePercent = app.iface->getMouseScale(); + frameValid = true; // put the framebuffer on the border of the next page // this is to allow for aligned DMA transfers by the receiver