diff options
-rw-r--r-- | doc/ScintillaHistory.html | 4 | ||||
-rw-r--r-- | win32/PlatWin.cxx | 128 | ||||
-rw-r--r-- | win32/PlatWin.h | 2 | ||||
-rw-r--r-- | win32/ScintillaWin.cxx | 16 |
4 files changed, 104 insertions, 46 deletions
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index f60eb3933..503df317e 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -594,6 +594,10 @@ <a href="https://sourceforge.net/p/scintilla/feature-requests/1486/">Feature #1486</a>. </li> <li> + On Win32 fix reverse arrow cursor when scaled. + <a href="https://sourceforge.net/p/scintilla/bugs/2382/">Bug #2382</a>. + </li> + <li> On Win32 hide cursor when typing if that system preference has been chosen. <a href="https://sourceforge.net/p/scintilla/bugs/2333/">Bug #2333</a>. </li> diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 65e27b63a..40302102b 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -167,6 +167,9 @@ GetWindowDpiAwarenessContextSig fnGetWindowDpiAwarenessContext = nullptr; using GetScaleFactorForMonitorSig = HRESULT(WINAPI *)(HMONITOR, DEVICE_SCALE_FACTOR *); GetScaleFactorForMonitorSig fnGetScaleFactorForMonitor = nullptr; +using GetThreadDpiAwarenessContextSig = DPI_AWARENESS_CONTEXT(WINAPI *)(); +GetThreadDpiAwarenessContextSig fnGetThreadDpiAwarenessContext = nullptr; + using SetThreadDpiAwarenessContextSig = DPI_AWARENESS_CONTEXT(WINAPI *)(DPI_AWARENESS_CONTEXT); SetThreadDpiAwarenessContextSig fnSetThreadDpiAwarenessContext = nullptr; @@ -175,6 +178,7 @@ void LoadDpiForWindow() noexcept { fnGetDpiForWindow = DLLFunction<GetDpiForWindowSig>(user32, "GetDpiForWindow"); fnGetSystemMetricsForDpi = DLLFunction<GetSystemMetricsForDpiSig>(user32, "GetSystemMetricsForDpi"); fnAdjustWindowRectExForDpi = DLLFunction<AdjustWindowRectExForDpiSig>(user32, "AdjustWindowRectExForDpi"); + fnGetThreadDpiAwarenessContext = DLLFunction<GetThreadDpiAwarenessContextSig>(user32, "GetThreadDpiAwarenessContext"); fnSetThreadDpiAwarenessContext = DLLFunction<SetThreadDpiAwarenessContextSig>(user32, "SetThreadDpiAwarenessContext"); using GetDpiForSystemSig = UINT(WINAPI *)(void); @@ -393,7 +397,7 @@ HMONITOR MonitorFromWindowHandleScaling(HWND hWnd) noexcept { return monitor; } -int GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept { +float GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept { if (fnAreDpiAwarenessContextsEqual) { PLATFORM_ASSERT(fnGetWindowDpiAwarenessContext && fnGetScaleFactorForMonitor); if (fnAreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED, fnGetWindowDpiAwarenessContext(hWnd))) { @@ -401,10 +405,10 @@ int GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept { const HMONITOR hMonitor = MonitorFromWindowHandleScaling(hRootWnd); DEVICE_SCALE_FACTOR deviceScaleFactor; if (S_OK == fnGetScaleFactorForMonitor(hMonitor, &deviceScaleFactor)) - return (static_cast<int>(deviceScaleFactor) + 99) / 100; // increase to first integral multiple of 1 + return deviceScaleFactor / 100.f; } } - return 1; + return 1.f; } std::shared_ptr<Font> Font::Allocate(const FontParameters &fp) { @@ -2778,56 +2782,102 @@ void Window::InvalidateRectangle(PRectangle rc) { ::InvalidateRect(HwndFromWindowID(wid), &rcw, FALSE); } -namespace { - -void FlipBitmap(HBITMAP bitmap, int width, int height) noexcept { - HDC hdc = ::CreateCompatibleDC({}); - if (hdc) { - HBITMAP prevBmp = SelectBitmap(hdc, bitmap); - ::StretchBlt(hdc, width - 1, 0, -width, height, hdc, 0, 0, width, height, SRCCOPY); - SelectBitmap(hdc, prevBmp); - ::DeleteDC(hdc); - } -} - -} - HCURSOR LoadReverseArrowCursor(UINT dpi) noexcept { - HCURSOR reverseArrowCursor {}; + class CursorHelper { + public: + ICONINFO info{}; + BITMAP bmp{}; + bool HasBitmap() const noexcept { + return bmp.bmWidth > 0; + } - bool created = false; - HCURSOR cursor = ::LoadCursor({}, IDC_ARROW); + CursorHelper(const HCURSOR cursor) noexcept { + Init(cursor); + } + ~CursorHelper() { + CleanUp(); + } - if (dpi != uSystemDPI) { - const int width = SystemMetricsForDpi(SM_CXCURSOR, dpi); - const int height = SystemMetricsForDpi(SM_CYCURSOR, dpi); - HCURSOR copy = static_cast<HCURSOR>(::CopyImage(cursor, IMAGE_CURSOR, width, height, LR_COPYFROMRESOURCE | LR_COPYRETURNORG)); - if (copy) { - created = copy != cursor; - cursor = copy; + CursorHelper &operator=(const HCURSOR cursor) noexcept { + CleanUp(); + Init(cursor); + return *this; + } + + bool MatchesSize(const int width, const int height) noexcept { + return bmp.bmWidth == width && bmp.bmHeight == height; } - } - ICONINFO info; - if (::GetIconInfo(cursor, &info)) { - BITMAP bmp {}; - if (::GetObject(info.hbmMask, sizeof(bmp), &bmp)) { - FlipBitmap(info.hbmMask, bmp.bmWidth, bmp.bmHeight); + HCURSOR CreateFlippedCursor() noexcept { + if (info.hbmMask) + FlipBitmap(info.hbmMask, bmp.bmWidth, bmp.bmHeight); if (info.hbmColor) FlipBitmap(info.hbmColor, bmp.bmWidth, bmp.bmHeight); info.xHotspot = bmp.bmWidth - 1 - info.xHotspot; - reverseArrowCursor = ::CreateIconIndirect(&info); + return ::CreateIconIndirect(&info); + } + + private: + void Init(const HCURSOR &cursor) noexcept { + if (::GetIconInfo(cursor, &info)) { + ::GetObject(info.hbmMask, sizeof(bmp), &bmp); + PLATFORM_ASSERT(HasBitmap()); + } + } + + void CleanUp() noexcept { + if (info.hbmMask) + ::DeleteObject(info.hbmMask); + if (info.hbmColor) + ::DeleteObject(info.hbmColor); + info = {}; + bmp = {}; + } + + static void FlipBitmap(const HBITMAP bitmap, const int width, const int height) noexcept { + HDC hdc = ::CreateCompatibleDC({}); + if (hdc) { + HBITMAP prevBmp = SelectBitmap(hdc, bitmap); + ::StretchBlt(hdc, width - 1, 0, -width, height, hdc, 0, 0, width, height, SRCCOPY); + SelectBitmap(hdc, prevBmp); + ::DeleteDC(hdc); + } + } + }; + + HCURSOR reverseArrowCursor {}; + + const int width = SystemMetricsForDpi(SM_CXCURSOR, dpi); + const int height = SystemMetricsForDpi(SM_CYCURSOR, dpi); + + DPI_AWARENESS_CONTEXT oldContext = nullptr; + if (fnAreDpiAwarenessContextsEqual && fnAreDpiAwarenessContextsEqual(fnGetThreadDpiAwarenessContext(), DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) { + oldContext = fnSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + PLATFORM_ASSERT(oldContext != nullptr); + } + + const HCURSOR cursor = static_cast<HCURSOR>(::LoadImage({}, IDC_ARROW, IMAGE_CURSOR, width, height, LR_SHARED)); + if (cursor) { + CursorHelper cursorHelper(cursor); + + if (cursorHelper.HasBitmap() && !cursorHelper.MatchesSize(width, height)) { + const HCURSOR copy = static_cast<HCURSOR>(::CopyImage(cursor, IMAGE_CURSOR, width, height, LR_COPYFROMRESOURCE | LR_COPYRETURNORG)); + if (copy) { + cursorHelper = copy; + ::DestroyCursor(copy); + } } - ::DeleteObject(info.hbmMask); - if (info.hbmColor) - ::DeleteObject(info.hbmColor); + if (cursorHelper.HasBitmap()) { + reverseArrowCursor = cursorHelper.CreateFlippedCursor(); + } } - if (created) { - ::DestroyCursor(cursor); + if (oldContext) { + fnSetThreadDpiAwarenessContext(oldContext); } + return reverseArrowCursor; } diff --git a/win32/PlatWin.h b/win32/PlatWin.h index 8ce798cf1..f010923f2 100644 --- a/win32/PlatWin.h +++ b/win32/PlatWin.h @@ -46,7 +46,7 @@ void SetWindowPointer(HWND hWnd, void *ptr) noexcept; HMONITOR MonitorFromWindowHandleScaling(HWND hWnd) noexcept; UINT DpiForWindow(WindowID wid) noexcept; -int GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept; +float GetDeviceScaleFactorWhenGdiScalingActive(HWND hWnd) noexcept; int SystemMetricsForDpi(int nIndex, UINT dpi) noexcept; diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index 95bb79825..964b3ee3c 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -364,7 +364,10 @@ class ScintillaWin : static ATOM scintillaClassAtom; static ATOM callClassAtom; - int deviceScaleFactor = 1; + float deviceScaleFactor = 1.f; + int GetFirstIntegralMultipleDeviceScaleFactor() const noexcept { + return static_cast<int>(std::ceil(deviceScaleFactor)); + } #if defined(USE_D2D) ID2D1RenderTarget *pRenderTarget; @@ -723,14 +726,15 @@ void ScintillaWin::EnsureRenderTarget(HDC hdc) { } } else { - drtp.dpiX = 96.f * deviceScaleFactor; - drtp.dpiY = 96.f * deviceScaleFactor; + const int integralDeviceScaleFactor = GetFirstIntegralMultipleDeviceScaleFactor(); + drtp.dpiX = 96.f * integralDeviceScaleFactor; + drtp.dpiY = 96.f * integralDeviceScaleFactor; drtp.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_UNKNOWN); D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp {}; dhrtp.hwnd = hw; - dhrtp.pixelSize = ::GetSizeUFromRect(rc, deviceScaleFactor); + dhrtp.pixelSize = ::GetSizeUFromRect(rc, integralDeviceScaleFactor); dhrtp.presentOptions = (technology == Technology::DirectWriteRetain) ? D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS : D2D1_PRESENT_OPTIONS_NONE; @@ -775,7 +779,7 @@ void ScintillaWin::DisplayCursor(Window::Cursor c) { c = static_cast<Window::Cursor>(cursorMode); } if (c == Window::Cursor::reverseArrow) { - ::SetCursor(reverseArrowCursor.Load(dpi)); + ::SetCursor(reverseArrowCursor.Load(static_cast<UINT>(dpi * deviceScaleFactor))); } else { wMain.SetCursor(c); } @@ -3649,7 +3653,7 @@ LRESULT PASCAL ScintillaWin::CTWndProc( surfaceWindow->Init(ps.hdc, hWnd); } else { #if defined(USE_D2D) - const int scaleFactor = sciThis->deviceScaleFactor; + const int scaleFactor = sciThis->GetFirstIntegralMultipleDeviceScaleFactor(); // Create a Direct2D render target. D2D1_HWND_RENDER_TARGET_PROPERTIES dhrtp {}; |