1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Handle child window position changes

Fixes Loyalty window getting clipped while dragging it in Star Wars Rebellion.
This commit is contained in:
narzoul 2020-04-11 17:50:23 +02:00
parent 4d46189aa8
commit 4eb28cc596
4 changed files with 113 additions and 25 deletions

View File

@ -1,6 +1,7 @@
#include <utility> #include <utility>
#include "Gdi/Region.h" #include <Common/Hook.h>
#include <Gdi/Region.h>
namespace 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() Region::~Region()
{ {
if (m_region) if (m_region)

View File

@ -9,6 +9,7 @@ namespace Gdi
public: public:
Region(HRGN rgn); Region(HRGN rgn);
Region(const RECT& rect = RECT{ 0, 0, 0, 0 }); Region(const RECT& rect = RECT{ 0, 0, 0, 0 });
Region(HWND hwnd);
~Region(); ~Region();
Region(const Region& other); Region(const Region& other);
Region(Region&& other); Region(Region&& other);

View File

@ -3,23 +3,33 @@
#include <Windows.h> #include <Windows.h>
#include "Common/Hook.h" #include <Common/Hook.h>
#include "Common/Log.h" #include <Common/Log.h>
#include "Common/ScopedCriticalSection.h" #include <Common/ScopedCriticalSection.h>
#include "Gdi/AccessGuard.h" #include <Gdi/AccessGuard.h>
#include "Gdi/Dc.h" #include <Gdi/Dc.h>
#include "Win32/DisplayMode.h" #include <Win32/DisplayMode.h>
#include "Gdi/PaintHandlers.h" #include <Gdi/PaintHandlers.h>
#include "Gdi/ScrollBar.h" #include <Gdi/ScrollBar.h>
#include "Gdi/ScrollFunctions.h" #include <Gdi/ScrollFunctions.h>
#include "Gdi/TitleBar.h" #include <Gdi/TitleBar.h>
#include "Gdi/Window.h" #include <Gdi/Window.h>
#include "Gdi/WinProc.h" #include <Gdi/WinProc.h>
extern "C" IMAGE_DOS_HEADER __ImageBase; extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace namespace
{ {
const char* PROP_DDRAWCOMPAT = "DDrawCompat";
struct ChildWindowInfo
{
RECT rect;
Gdi::Region visibleRegion;
ChildWindowInfo() : rect{} {}
};
std::multimap<DWORD, HHOOK> g_threadIdToHook; std::multimap<DWORD, HHOOK> g_threadIdToHook;
Compat::CriticalSection g_threadIdToHookCs; Compat::CriticalSection g_threadIdToHookCs;
HWINEVENTHOOK g_objectCreateEventHook = nullptr; HWINEVENTHOOK g_objectCreateEventHook = nullptr;
@ -30,6 +40,7 @@ namespace
void onCreateWindow(HWND hwnd); void onCreateWindow(HWND hwnd);
void onDestroyWindow(HWND hwnd); void onDestroyWindow(HWND hwnd);
void onWindowPosChanged(HWND hwnd); void onWindowPosChanged(HWND hwnd);
void onWindowPosChanging(HWND hwnd, const WINDOWPOS& wp);
LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)
{ {
@ -68,6 +79,10 @@ namespace
case WM_WINDOWPOSCHANGED: case WM_WINDOWPOSCHANGED:
onWindowPosChanged(ret->hwnd); onWindowPosChanged(ret->hwnd);
break; break;
case WM_WINDOWPOSCHANGING:
onWindowPosChanging(ret->hwnd, *reinterpret_cast<WINDOWPOS*>(ret->lParam));
break;
} }
} }
@ -190,6 +205,7 @@ namespace
void onDestroyWindow(HWND hwnd) void onDestroyWindow(HWND hwnd)
{ {
Gdi::Window::remove(hwnd); Gdi::Window::remove(hwnd);
delete reinterpret_cast<ChildWindowInfo*>(RemoveProp(hwnd, PROP_DDRAWCOMPAT));
} }
void onWindowPosChanged(HWND hwnd) void onWindowPosChanged(HWND hwnd)
@ -209,10 +225,71 @@ namespace
notifyFunc(); notifyFunc();
} }
if (Gdi::Window::get(hwnd) || Gdi::Window::add(hwnd)) if (Gdi::Window::isTopLevelWindow(hwnd))
{ {
Gdi::Window::add(hwnd);
Gdi::Window::updateAll(); Gdi::Window::updateAll();
} }
else
{
std::unique_ptr<ChildWindowInfo> cwi(reinterpret_cast<ChildWindowInfo*>(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<ChildWindowInfo> cwi(reinterpret_cast<ChildWindowInfo*>(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<ChildWindowInfo*>(RemoveProp(hWnd, PROP_DDRAWCOMPAT));
return LOG_RESULT(result);
} }
} }
@ -236,6 +313,8 @@ namespace Gdi
void installHooks() void installHooks()
{ {
HOOK_FUNCTION(user32, SetWindowPos, setWindowPos);
g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE,
reinterpret_cast<HMODULE>(&__ImageBase), &objectCreateEvent, reinterpret_cast<HMODULE>(&__ImageBase), &objectCreateEvent,
GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);

View File

@ -1,11 +1,11 @@
#include <dwmapi.h> #include <dwmapi.h>
#include "Common/Hook.h" #include <Common/Hook.h>
#include "Common/Log.h" #include <Common/Log.h>
#include "D3dDdi/ScopedCriticalSection.h" #include <D3dDdi/ScopedCriticalSection.h>
#include "DDraw/RealPrimarySurface.h" #include <DDraw/RealPrimarySurface.h>
#include "Gdi/Gdi.h" #include <Gdi/Gdi.h>
#include "Gdi/Window.h" #include <Gdi/Window.h>
extern "C" IMAGE_DOS_HEADER __ImageBase; 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); HRGN rgn = CreateRectRgn(0, 0, 0, 0);
if (ERROR != GetWindowRgn(owner, rgn)) if (ERROR != GetWindowRgn(owner, rgn))
@ -271,7 +272,7 @@ namespace Gdi
oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top, oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top,
screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY); screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY);
SelectClipRgn(screenDc, nullptr); SelectClipRgn(screenDc, nullptr);
ReleaseDC(nullptr, screenDc); CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc);
} }
m_invalidatedRegion -= preservedRegion; m_invalidatedRegion -= preservedRegion;
} }
@ -423,9 +424,7 @@ namespace Gdi
GetWindowRect(m_hwnd, &newWindowRect); GetWindowRect(m_hwnd, &newWindowRect);
if (!IsRectEmpty(&newWindowRect) && !m_isLayered) if (!IsRectEmpty(&newWindowRect) && !m_isLayered)
{ {
HDC windowDc = GetWindowDC(m_hwnd); newVisibleRegion = m_hwnd;
GetRandomRgn(windowDc, newVisibleRegion, SYSRGN);
CALL_ORIG_FUNC(ReleaseDC)(m_hwnd, windowDc);
} }
} }