From 4eb28cc5964c66dfb2cf18f6d3b0b9f90317e81b Mon Sep 17 00:00:00 2001 From: narzoul Date: Sat, 11 Apr 2020 17:50:23 +0200 Subject: [PATCH] Handle child window position changes Fixes Loyalty window getting clipped while dragging it in Star Wars Rebellion. --- DDrawCompat/Gdi/Region.cpp | 11 +++- DDrawCompat/Gdi/Region.h | 1 + DDrawCompat/Gdi/WinProc.cpp | 105 +++++++++++++++++++++++++++++++----- DDrawCompat/Gdi/Window.cpp | 21 ++++---- 4 files changed, 113 insertions(+), 25 deletions(-) diff --git a/DDrawCompat/Gdi/Region.cpp b/DDrawCompat/Gdi/Region.cpp index cad3b65..2d84a13 100644 --- a/DDrawCompat/Gdi/Region.cpp +++ b/DDrawCompat/Gdi/Region.cpp @@ -1,6 +1,7 @@ #include -#include "Gdi/Region.h" +#include +#include namespace { @@ -24,6 +25,14 @@ namespace Gdi { } + Region::Region(HWND hwnd) + : m_region(CreateRectRgn(0, 0, 0, 0)) + { + HDC dc = GetWindowDC(hwnd); + GetRandomRgn(dc, m_region, SYSRGN); + CALL_ORIG_FUNC(ReleaseDC)(hwnd, dc); + } + Region::~Region() { if (m_region) diff --git a/DDrawCompat/Gdi/Region.h b/DDrawCompat/Gdi/Region.h index 8f15ad7..436e037 100644 --- a/DDrawCompat/Gdi/Region.h +++ b/DDrawCompat/Gdi/Region.h @@ -9,6 +9,7 @@ namespace Gdi public: Region(HRGN rgn); Region(const RECT& rect = RECT{ 0, 0, 0, 0 }); + Region(HWND hwnd); ~Region(); Region(const Region& other); Region(Region&& other); diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index 66b0bad..18afe75 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -3,23 +3,33 @@ #include -#include "Common/Hook.h" -#include "Common/Log.h" -#include "Common/ScopedCriticalSection.h" -#include "Gdi/AccessGuard.h" -#include "Gdi/Dc.h" -#include "Win32/DisplayMode.h" -#include "Gdi/PaintHandlers.h" -#include "Gdi/ScrollBar.h" -#include "Gdi/ScrollFunctions.h" -#include "Gdi/TitleBar.h" -#include "Gdi/Window.h" -#include "Gdi/WinProc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include extern "C" IMAGE_DOS_HEADER __ImageBase; namespace { + const char* PROP_DDRAWCOMPAT = "DDrawCompat"; + + struct ChildWindowInfo + { + RECT rect; + Gdi::Region visibleRegion; + + ChildWindowInfo() : rect{} {} + }; + std::multimap g_threadIdToHook; Compat::CriticalSection g_threadIdToHookCs; HWINEVENTHOOK g_objectCreateEventHook = nullptr; @@ -30,6 +40,7 @@ namespace void onCreateWindow(HWND hwnd); void onDestroyWindow(HWND hwnd); void onWindowPosChanged(HWND hwnd); + void onWindowPosChanging(HWND hwnd, const WINDOWPOS& wp); LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam) { @@ -68,6 +79,10 @@ namespace case WM_WINDOWPOSCHANGED: onWindowPosChanged(ret->hwnd); break; + + case WM_WINDOWPOSCHANGING: + onWindowPosChanging(ret->hwnd, *reinterpret_cast(ret->lParam)); + break; } } @@ -190,6 +205,7 @@ namespace void onDestroyWindow(HWND hwnd) { Gdi::Window::remove(hwnd); + delete reinterpret_cast(RemoveProp(hwnd, PROP_DDRAWCOMPAT)); } void onWindowPosChanged(HWND hwnd) @@ -209,10 +225,71 @@ namespace notifyFunc(); } - if (Gdi::Window::get(hwnd) || Gdi::Window::add(hwnd)) + if (Gdi::Window::isTopLevelWindow(hwnd)) { + Gdi::Window::add(hwnd); Gdi::Window::updateAll(); } + else + { + std::unique_ptr cwi(reinterpret_cast(RemoveProp(hwnd, PROP_DDRAWCOMPAT))); + if (cwi && IsWindowVisible(hwnd) && !IsIconic(GetAncestor(hwnd, GA_ROOT))) + { + RECT rect = {}; + GetWindowRect(hwnd, &rect); + if (rect.left != cwi->rect.left || rect.top != cwi->rect.top) + { + Gdi::Region clipRegion(hwnd); + cwi->visibleRegion.offset(rect.left - cwi->rect.left, rect.top - cwi->rect.top); + clipRegion &= cwi->visibleRegion; + + HDC screenDc = GetDC(nullptr); + SelectClipRgn(screenDc, clipRegion); + BitBlt(screenDc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + screenDc, cwi->rect.left, cwi->rect.top, SRCCOPY); + SelectClipRgn(screenDc, nullptr); + CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc); + } + } + } + } + + void onWindowPosChanging(HWND hwnd, const WINDOWPOS& wp) + { + if (!Gdi::Window::isTopLevelWindow(hwnd)) + { + std::unique_ptr cwi(reinterpret_cast(RemoveProp(hwnd, PROP_DDRAWCOMPAT))); + if (!(wp.flags & SWP_NOMOVE) && IsWindowVisible(hwnd) && !IsIconic(GetAncestor(hwnd, GA_ROOT))) + { + cwi.reset(new ChildWindowInfo()); + GetWindowRect(hwnd, &cwi->rect); + cwi->visibleRegion = hwnd; + if (!cwi->visibleRegion.isEmpty()) + { + SetProp(hwnd, PROP_DDRAWCOMPAT, cwi.release()); + } + } + } + } + + BOOL WINAPI setWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags) + { + LOG_FUNC("SetWindowPos", hWnd, hWndInsertAfter, X, Y, cx, cy, Compat::hex(uFlags)); + if (uFlags & SWP_NOSENDCHANGING) + { + WINDOWPOS wp = {}; + wp.hwnd = hWnd; + wp.hwndInsertAfter = hWndInsertAfter; + wp.x = X; + wp.y = Y; + wp.cx = cx; + wp.cy = cy; + wp.flags = uFlags; + onWindowPosChanging(hWnd, wp); + } + BOOL result = CALL_ORIG_FUNC(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); + delete reinterpret_cast(RemoveProp(hWnd, PROP_DDRAWCOMPAT)); + return LOG_RESULT(result); } } @@ -236,6 +313,8 @@ namespace Gdi void installHooks() { + HOOK_FUNCTION(user32, SetWindowPos, setWindowPos); + g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, reinterpret_cast(&__ImageBase), &objectCreateEvent, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); diff --git a/DDrawCompat/Gdi/Window.cpp b/DDrawCompat/Gdi/Window.cpp index 1195f13..56f4dd2 100644 --- a/DDrawCompat/Gdi/Window.cpp +++ b/DDrawCompat/Gdi/Window.cpp @@ -1,11 +1,11 @@ #include -#include "Common/Hook.h" -#include "Common/Log.h" -#include "D3dDdi/ScopedCriticalSection.h" -#include "DDraw/RealPrimarySurface.h" -#include "Gdi/Gdi.h" -#include "Gdi/Window.h" +#include +#include +#include +#include +#include +#include extern "C" IMAGE_DOS_HEADER __ImageBase; @@ -101,7 +101,8 @@ namespace } } - SetWindowPos(hwnd, insertAfter, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, flags); + CALL_ORIG_FUNC(SetWindowPos)( + hwnd, insertAfter, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, flags); HRGN rgn = CreateRectRgn(0, 0, 0, 0); if (ERROR != GetWindowRgn(owner, rgn)) @@ -271,7 +272,7 @@ namespace Gdi oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top, screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY); SelectClipRgn(screenDc, nullptr); - ReleaseDC(nullptr, screenDc); + CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc); } m_invalidatedRegion -= preservedRegion; } @@ -423,9 +424,7 @@ namespace Gdi GetWindowRect(m_hwnd, &newWindowRect); if (!IsRectEmpty(&newWindowRect) && !m_isLayered) { - HDC windowDc = GetWindowDC(m_hwnd); - GetRandomRgn(windowDc, newVisibleRegion, SYSRGN); - CALL_ORIG_FUNC(ReleaseDC)(m_hwnd, windowDc); + newVisibleRegion = m_hwnd; } }