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 "Gdi/Region.h"
#include <Common/Hook.h>
#include <Gdi/Region.h>
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)

View File

@ -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);

View File

@ -3,23 +3,33 @@
#include <Windows.h>
#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 <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>
extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace
{
const char* PROP_DDRAWCOMPAT = "DDrawCompat";
struct ChildWindowInfo
{
RECT rect;
Gdi::Region visibleRegion;
ChildWindowInfo() : rect{} {}
};
std::multimap<DWORD, HHOOK> 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<WINDOWPOS*>(ret->lParam));
break;
}
}
@ -190,6 +205,7 @@ namespace
void onDestroyWindow(HWND hwnd)
{
Gdi::Window::remove(hwnd);
delete reinterpret_cast<ChildWindowInfo*>(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<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()
{
HOOK_FUNCTION(user32, SetWindowPos, setWindowPos);
g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE,
reinterpret_cast<HMODULE>(&__ImageBase), &objectCreateEvent,
GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);

View File

@ -1,11 +1,11 @@
#include <dwmapi.h>
#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 <Common/Hook.h>
#include <Common/Log.h>
#include <D3dDdi/ScopedCriticalSection.h>
#include <DDraw/RealPrimarySurface.h>
#include <Gdi/Gdi.h>
#include <Gdi/Window.h>
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;
}
}