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

Use window region to emulate pre-DWM system region

This commit is contained in:
narzoul 2020-12-27 17:08:44 +01:00
parent 679bd94629
commit 5911951d08
20 changed files with 791 additions and 708 deletions

View File

@ -16,15 +16,13 @@ namespace
}; };
std::map<IDirectDrawClipper*, ClipperData> g_clipperData; std::map<IDirectDrawClipper*, ClipperData> g_clipperData;
bool g_isInvalidated = false;
void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data); void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data);
void onWindowPosChange() void onWindowPosChange()
{ {
for (auto& clipperData : g_clipperData) g_isInvalidated = true;
{
updateWindowClipList(*clipperData.first, clipperData.second);
}
} }
void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data) void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data)
@ -146,4 +144,16 @@ namespace DDraw
vtable.SetClipList = &SetClipList; vtable.SetClipList = &SetClipList;
vtable.SetHWnd = &SetHWnd; vtable.SetHWnd = &SetHWnd;
} }
void DirectDrawClipper::update()
{
if (g_isInvalidated)
{
g_isInvalidated = false;
for (auto& clipperData : g_clipperData)
{
updateWindowClipList(*clipperData.first, clipperData.second);
}
}
}
} }

View File

@ -11,6 +11,7 @@ namespace DDraw
public: public:
static HRGN getClipRgn(CompatRef<IDirectDrawClipper> clipper); static HRGN getClipRgn(CompatRef<IDirectDrawClipper> clipper);
static HRESULT setClipRgn(CompatRef<IDirectDrawClipper> clipper, HRGN rgn); static HRESULT setClipRgn(CompatRef<IDirectDrawClipper> clipper, HRGN rgn);
static void update();
static void setCompatVtable(IDirectDrawClipperVtbl& vtable); static void setCompatVtable(IDirectDrawClipperVtbl& vtable);
}; };

View File

@ -47,137 +47,11 @@ namespace
CompatPtr<IDirectDrawSurface7> getBackBuffer(); CompatPtr<IDirectDrawSurface7> getBackBuffer();
CompatPtr<IDirectDrawSurface7> getLastSurface(); CompatPtr<IDirectDrawSurface7> getLastSurface();
void bltToWindow(CompatRef<IDirectDrawSurface7> src)
{
for (auto windowPair : Gdi::Window::getWindows())
{
if (!windowPair.second->isLayered() && !windowPair.second->getVisibleRegion().isEmpty())
{
g_clipper->SetHWnd(g_clipper, 0, windowPair.second->getPresentationWindow());
g_frontBuffer->Blt(g_frontBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
}
}
}
void bltToWindowViaGdi(Gdi::Region* primaryRegion)
{
D3dDdi::ScopedCriticalSection lock;
std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc);
RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
for (auto windowPair : Gdi::Window::getWindows())
{
HWND presentationWindow = windowPair.second->getPresentationWindow();
if (!presentationWindow)
{
continue;
}
Gdi::Region visibleRegion = windowPair.second->getVisibleRegion();
if (visibleRegion.isEmpty())
{
continue;
}
if (primaryRegion)
{
visibleRegion -= *primaryRegion;
if (visibleRegion.isEmpty())
{
continue;
}
}
if (!virtualScreenDc)
{
virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
if (!virtualScreenDc)
{
return;
}
}
Gdi::AccessGuard accessGuard(Gdi::ACCESS_READ, !primaryRegion);
HDC dc = GetWindowDC(presentationWindow);
RECT rect = windowPair.second->getWindowRect();
visibleRegion.offset(-rect.left, -rect.top);
SelectClipRgn(dc, visibleRegion);
CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, virtualScreenDc.get(),
rect.left - virtualScreenBounds.left, rect.top - virtualScreenBounds.top, SRCCOPY);
CALL_ORIG_FUNC(ReleaseDC)(presentationWindow, dc);
}
}
void bltVisibleLayeredWindowsToBackBuffer()
{
auto backBuffer(getBackBuffer());
if (!backBuffer)
{
return;
}
HDC backBufferDc = nullptr;
RECT ddrawMonitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
for (auto windowPair : Gdi::Window::getWindows())
{
if (!windowPair.second->isLayered())
{
continue;
}
if (!backBufferDc)
{
D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_X8R8G8B8);
backBuffer->GetDC(backBuffer, &backBufferDc);
D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_UNKNOWN);
if (!backBufferDc)
{
return;
}
}
HDC windowDc = GetWindowDC(windowPair.first);
Gdi::Region rgn(Gdi::getVisibleWindowRgn(windowPair.first));
RECT wr = windowPair.second->getWindowRect();
if (0 != ddrawMonitorRect.left || 0 != ddrawMonitorRect.top)
{
OffsetRect(&wr, -ddrawMonitorRect.left, -ddrawMonitorRect.top);
rgn.offset(-ddrawMonitorRect.left, -ddrawMonitorRect.top);
}
SelectClipRgn(backBufferDc, rgn);
auto colorKey = windowPair.second->getColorKey();
if (CLR_INVALID != colorKey)
{
CALL_ORIG_FUNC(TransparentBlt)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, colorKey);
}
else
{
BLENDFUNCTION blend = {};
blend.SourceConstantAlpha = windowPair.second->getAlpha();
CALL_ORIG_FUNC(AlphaBlend)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, blend);
}
CALL_ORIG_FUNC(ReleaseDC)(windowPair.first, windowDc);
}
if (backBufferDc)
{
SelectClipRgn(backBufferDc, nullptr);
backBuffer->ReleaseDC(backBuffer, backBufferDc);
}
}
void bltToPrimaryChain(CompatRef<IDirectDrawSurface7> src) void bltToPrimaryChain(CompatRef<IDirectDrawSurface7> src)
{ {
if (!g_isFullScreen) if (!g_isFullScreen)
{ {
bltToWindow(src); Gdi::Window::present(*g_frontBuffer, src, *g_clipper);
return; return;
} }
@ -324,12 +198,13 @@ namespace
if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost()) if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost())
{ {
bltToWindowViaGdi(nullptr); Gdi::Window::present(nullptr);
return; return;
} }
Gdi::Region primaryRegion(D3dDdi::KernelModeThunks::getMonitorRect()); RECT monitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
bltToWindowViaGdi(&primaryRegion); Gdi::Region excludeRegion(monitorRect);
Gdi::Window::present(excludeRegion);
if (Win32::DisplayMode::getBpp() <= 8) if (Win32::DisplayMode::getBpp() <= 8)
{ {
@ -360,7 +235,12 @@ namespace
if (g_isFullScreen && src == DDraw::PrimarySurface::getGdiSurface()) if (g_isFullScreen && src == DDraw::PrimarySurface::getGdiSurface())
{ {
bltVisibleLayeredWindowsToBackBuffer(); auto backBuffer(getBackBuffer());
if (backBuffer)
{
POINT offset = { -monitorRect.left, -monitorRect.top };
Gdi::Window::presentLayered(*backBuffer, offset);
}
} }
} }

View File

@ -1,10 +1,11 @@
#include <set> #include <set>
#include "DDraw/DirectDrawSurface.h" #include <DDraw/DirectDrawClipper.h>
#include "DDraw/RealPrimarySurface.h" #include <DDraw/DirectDrawSurface.h>
#include "DDraw/Surfaces/PrimarySurface.h" #include <DDraw/RealPrimarySurface.h>
#include "DDraw/Surfaces/Surface.h" #include <DDraw/Surfaces/PrimarySurface.h>
#include "DDraw/Surfaces/SurfaceImpl.h" #include <DDraw/Surfaces/Surface.h>
#include <DDraw/Surfaces/SurfaceImpl.h>
namespace DDraw namespace DDraw
{ {
@ -28,6 +29,7 @@ namespace DDraw
{ {
return DDERR_WASSTILLDRAWING; return DDERR_WASSTILLDRAWING;
} }
DirectDrawClipper::update();
return s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); return s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx);
} }

View File

@ -113,14 +113,12 @@ namespace
} }
} }
void setClippingRegion(const CompatDc& compatDc, std::shared_ptr<Gdi::Window> rootWindow, void setClippingRegion(const CompatDc& compatDc, HWND hwnd, const POINT& origin, const RECT& virtualScreenBounds)
const POINT& origin, const RECT& virtualScreenBounds)
{ {
if (rootWindow) if (hwnd)
{ {
Gdi::Region sysRgn; Gdi::Region sysRgn;
CALL_ORIG_FUNC(GetRandomRgn)(compatDc.origDc, sysRgn, SYSRGN); CALL_ORIG_FUNC(GetRandomRgn)(compatDc.origDc, sysRgn, SYSRGN);
sysRgn &= rootWindow->getVisibleRegion();
OffsetRgn(sysRgn, -virtualScreenBounds.left, -virtualScreenBounds.top); OffsetRgn(sysRgn, -virtualScreenBounds.left, -virtualScreenBounds.top);
SelectClipRgn(compatDc.dc, sysRgn); SelectClipRgn(compatDc.dc, sysRgn);
} }
@ -196,13 +194,6 @@ namespace Gdi
return it->second.dc; return it->second.dc;
} }
const HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(origDc);
auto rootWindow(wnd ? Gdi::Window::get(GetAncestor(wnd, GA_ROOT)) : nullptr);
if (rootWindow)
{
rootWindow->updateWindow();
}
CompatDc compatDc; CompatDc compatDc;
compatDc.dc = Gdi::DcCache::getDc(); compatDc.dc = Gdi::DcCache::getDc();
if (!compatDc.dc) if (!compatDc.dc)
@ -212,7 +203,8 @@ namespace Gdi
POINT origin = {}; POINT origin = {};
GetDCOrgEx(origDc, &origin); GetDCOrgEx(origDc, &origin);
if (wnd && GetDesktopWindow() != wnd) const HWND hwnd = CALL_ORIG_FUNC(WindowFromDC)(origDc);
if (hwnd && GetDesktopWindow() != hwnd)
{ {
origin.x -= virtualScreenBounds.left; origin.x -= virtualScreenBounds.left;
origin.y -= virtualScreenBounds.top; origin.y -= virtualScreenBounds.top;
@ -223,7 +215,7 @@ namespace Gdi
compatDc.threadId = GetCurrentThreadId(); compatDc.threadId = GetCurrentThreadId();
compatDc.savedState = useMetaRgn ? SaveDC(compatDc.dc) : 0; compatDc.savedState = useMetaRgn ? SaveDC(compatDc.dc) : 0;
copyDcAttributes(compatDc, origDc, origin); copyDcAttributes(compatDc, origDc, origin);
setClippingRegion(compatDc, rootWindow, origin, virtualScreenBounds); setClippingRegion(compatDc, hwnd, origin, virtualScreenBounds);
g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc)); g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc));

View File

@ -47,17 +47,9 @@ namespace
HDC m_compatDc; HDC m_compatDc;
}; };
struct ExcludeRgnForOverlappingWindowArgs
{
HRGN rgn;
HWND rootWnd;
};
std::unordered_map<void*, const char*> g_funcNames; std::unordered_map<void*, const char*> g_funcNames;
thread_local bool g_redirectToDib = true; thread_local bool g_redirectToDib = true;
HRGN getWindowRegion(HWND hwnd);
#define CREATE_DC_FUNC_ATTRIBUTE(attribute) \ #define CREATE_DC_FUNC_ATTRIBUTE(attribute) \
template <typename OrigFuncPtr, OrigFuncPtr origFunc> \ template <typename OrigFuncPtr, OrigFuncPtr origFunc> \
bool attribute() \ bool attribute() \
@ -266,30 +258,6 @@ namespace
return LOG_RESULT(CALL_ORIG_FUNC(DrawCaption)(hwnd, hdc, lprect, flags)); return LOG_RESULT(CALL_ORIG_FUNC(DrawCaption)(hwnd, hdc, lprect, flags));
} }
BOOL CALLBACK excludeRgnForOverlappingWindow(HWND hwnd, LPARAM lParam)
{
auto& args = *reinterpret_cast<ExcludeRgnForOverlappingWindowArgs*>(lParam);
if (hwnd == args.rootWnd)
{
return FALSE;
}
DWORD windowPid = 0;
GetWindowThreadProcessId(hwnd, &windowPid);
if (GetCurrentProcessId() != windowPid ||
!IsWindowVisible(hwnd) ||
(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE) & (WS_EX_LAYERED | WS_EX_TRANSPARENT)))
{
return TRUE;
}
HRGN windowRgn = getWindowRegion(hwnd);
CombineRgn(args.rgn, args.rgn, windowRgn, RGN_DIFF);
DeleteObject(windowRgn);
return TRUE;
}
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename Result, typename... Params> template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename Result, typename... Params>
OrigFuncPtr getCompatGdiDcFuncPtr(FuncPtr<Result, HDC, Params...>) OrigFuncPtr getCompatGdiDcFuncPtr(FuncPtr<Result, HDC, Params...>)
{ {
@ -302,18 +270,6 @@ namespace
return &compatGdiTextDcFunc<OrigFuncPtr, origFunc, Result, Params...>; return &compatGdiTextDcFunc<OrigFuncPtr, origFunc, Result, Params...>;
} }
HRGN getWindowRegion(HWND hwnd)
{
RECT wr = {};
GetWindowRect(hwnd, &wr);
HRGN windowRgn = CreateRectRgnIndirect(&wr);
if (ERROR != GetWindowRgn(hwnd, windowRgn))
{
OffsetRgn(windowRgn, wr.left, wr.top);
}
return windowRgn;
}
template <typename OrigFuncPtr, OrigFuncPtr origFunc> template <typename OrigFuncPtr, OrigFuncPtr origFunc>
void hookGdiDcFunction(const char* moduleName, const char* funcName) void hookGdiDcFunction(const char* moduleName, const char* funcName)
{ {
@ -336,26 +292,6 @@ namespace
moduleName, funcName, getCompatGdiTextDcFuncPtr<OrigFuncPtr, origFunc>(origFunc)); moduleName, funcName, getCompatGdiTextDcFuncPtr<OrigFuncPtr, origFunc>(origFunc));
} }
int WINAPI getRandomRgn(HDC hdc, HRGN hrgn, INT iNum)
{
int result = CALL_ORIG_FUNC(GetRandomRgn)(hdc, hrgn, iNum);
if (1 != result)
{
return result;
}
HWND hwnd = WindowFromDC(hdc);
if (!hwnd || hwnd == GetDesktopWindow() || (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED))
{
return 1;
}
ExcludeRgnForOverlappingWindowArgs args = { hrgn, GetAncestor(hwnd, GA_ROOT) };
EnumWindows(excludeRgnForOverlappingWindow, reinterpret_cast<LPARAM>(&args));
return 1;
}
template <typename WndClass, typename WndClassEx> template <typename WndClass, typename WndClassEx>
ATOM WINAPI registerClass(const WndClass* lpWndClass, ATOM(WINAPI* origRegisterClass)(const WndClass*), ATOM WINAPI registerClass(const WndClass* lpWndClass, ATOM(WINAPI* origRegisterClass)(const WndClass*),
ATOM(WINAPI* registerClassEx)(const WndClassEx*)) ATOM(WINAPI* registerClassEx)(const WndClassEx*))
@ -481,14 +417,6 @@ namespace Gdi
{ {
namespace DcFunctions namespace DcFunctions
{ {
HRGN getVisibleWindowRgn(HWND hwnd)
{
HRGN rgn = getWindowRegion(hwnd);
ExcludeRgnForOverlappingWindowArgs args = { rgn, hwnd };
EnumWindows(excludeRgnForOverlappingWindow, reinterpret_cast<LPARAM>(&args));
return rgn;
}
void installHooks() void installHooks()
{ {
// Bitmap functions // Bitmap functions
@ -517,9 +445,6 @@ namespace Gdi
// Brush functions // Brush functions
HOOK_GDI_DC_FUNCTION(gdi32, PatBlt); HOOK_GDI_DC_FUNCTION(gdi32, PatBlt);
// Clipping functions
HOOK_FUNCTION(gdi32, GetRandomRgn, getRandomRgn);
// Device context functions // Device context functions
HOOK_GDI_DC_FUNCTION(gdi32, DrawEscape); HOOK_GDI_DC_FUNCTION(gdi32, DrawEscape);
HOOK_FUNCTION(user32, WindowFromDC, windowFromDc); HOOK_FUNCTION(user32, WindowFromDC, windowFromDc);

View File

@ -1,12 +1,9 @@
#pragma once #pragma once
#include <Windows.h>
namespace Gdi namespace Gdi
{ {
namespace DcFunctions namespace DcFunctions
{ {
HRGN getVisibleWindowRgn(HWND hwnd);
void installHooks(); void installHooks();
} }
} }

View File

@ -34,11 +34,6 @@ namespace Gdi
DcCache::dllThreadDetach(); DcCache::dllThreadDetach();
} }
HRGN getVisibleWindowRgn(HWND hwnd)
{
return DcFunctions::getVisibleWindowRgn(hwnd);
}
void installHooks() void installHooks()
{ {
DcFunctions::installHooks(); DcFunctions::installHooks();

View File

@ -9,7 +9,6 @@ namespace Gdi
typedef void(*WindowPosChangeNotifyFunc)(); typedef void(*WindowPosChangeNotifyFunc)();
void dllThreadDetach(); void dllThreadDetach();
HRGN getVisibleWindowRgn(HWND hwnd);
void installHooks(); void installHooks();
bool isDisplayDc(HDC dc); bool isDisplayDc(HDC dc);
void redraw(HRGN rgn); void redraw(HRGN rgn);

View File

@ -7,6 +7,7 @@
#include <Gdi/ScrollFunctions.h> #include <Gdi/ScrollFunctions.h>
#include <Gdi/TitleBar.h> #include <Gdi/TitleBar.h>
#include <Gdi/VirtualScreen.h> #include <Gdi/VirtualScreen.h>
#include <Gdi/WinProc.h>
std::ostream& operator<<(std::ostream& os, const MENUITEMINFOW& val) std::ostream& operator<<(std::ostream& os, const MENUITEMINFOW& val)
{ {
@ -43,7 +44,8 @@ namespace
LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc, LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc,
const char* origWndProcName); const char* origWndProcName);
LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc); LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc);
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc); LRESULT onNcActivate(HWND hwnd, WPARAM wParam, LPARAM lParam);
LRESULT onNcPaint(HWND hwnd, WNDPROC origWndProc);
LRESULT onPaint(HWND hwnd, WNDPROC origWndProc); LRESULT onPaint(HWND hwnd, WNDPROC origWndProc);
LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc); LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc);
LRESULT onSetText(HWND hwnd, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc); LRESULT onSetText(HWND hwnd, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc);
@ -143,7 +145,7 @@ namespace
return onEraseBackground(hwnd, reinterpret_cast<HDC>(wParam), origWndProc); return onEraseBackground(hwnd, reinterpret_cast<HDC>(wParam), origWndProc);
case WM_NCPAINT: case WM_NCPAINT:
return onNcPaint(hwnd, wParam, origWndProc); return onNcPaint(hwnd, origWndProc);
case WM_PRINT: case WM_PRINT:
case WM_PRINTCLIENT: case WM_PRINTCLIENT:
@ -164,14 +166,38 @@ namespace
return LOG_RESULT(defPaintProc(hwnd, msg, wParam, lParam, origWndProc)); return LOG_RESULT(defPaintProc(hwnd, msg, wParam, lParam, origWndProc));
} }
LRESULT defWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origDefWindowProc)
{
switch (msg)
{
case WM_NCACTIVATE:
return onNcActivate(hwnd, wParam, lParam);
case WM_NCCREATE:
{
char className[64] = {};
GetClassName(hwnd, className, sizeof(className));
if (std::string(className) == "CompatWindowDesktopReplacement")
{
// Disable VirtualizeDesktopPainting shim
return 0;
}
}
}
return defPaintProc(hwnd, msg, wParam, lParam, origDefWindowProc);
}
LRESULT WINAPI defWindowProcA(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT WINAPI defWindowProcA(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
return defPaintProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcA), "defWindowProcA"); LOG_FUNC("DefWindowProcA", Compat::WindowMessageStruct(hwnd, msg, wParam, lParam));
return LOG_RESULT(defWindowProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcA)));
} }
LRESULT WINAPI defWindowProcW(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT WINAPI defWindowProcW(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
return defPaintProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcW), "defWindowProcW"); LOG_FUNC("DefWindowProcW", Compat::WindowMessageStruct(hwnd, msg, wParam, lParam));
return LOG_RESULT(defWindowProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcW)));
} }
LRESULT editWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) LRESULT editWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
@ -285,7 +311,7 @@ namespace
{ {
case WM_NCPAINT: case WM_NCPAINT:
CallWindowProc(origWndProc, hwnd, msg, wParam, lParam); CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
return onNcPaint(hwnd, wParam, origWndProc); return onNcPaint(hwnd, origWndProc);
case WM_PAINT: case WM_PAINT:
{ {
@ -310,6 +336,18 @@ namespace
return result; return result;
} }
case WM_WINDOWPOSCHANGED:
{
LRESULT result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
auto exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE);
if (exStyle & WS_EX_LAYERED)
{
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW);
}
return result;
}
case 0x1e5: case 0x1e5:
if (-1 == wParam) if (-1 == wParam)
{ {
@ -341,11 +379,11 @@ namespace
return CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0); return CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0);
} }
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc) LRESULT onNcActivate(HWND hwnd, WPARAM wParam, LPARAM lParam)
{ {
if (!hwnd) if (-1 == lParam)
{ {
return CallWindowProc(origWndProc, hwnd, WM_NCPAINT, wParam, 0); return TRUE;
} }
HDC windowDc = GetWindowDC(hwnd); HDC windowDc = GetWindowDC(hwnd);
@ -353,6 +391,26 @@ namespace
if (compatDc) if (compatDc)
{ {
D3dDdi::ScopedCriticalSection lock;
Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE);
Gdi::TitleBar titleBar(hwnd, compatDc);
titleBar.setActive(wParam);
titleBar.drawAll();
Gdi::Dc::releaseDc(windowDc);
}
CALL_ORIG_FUNC(ReleaseDC)(hwnd, windowDc);
return TRUE;
}
LRESULT onNcPaint(HWND hwnd, WNDPROC origWndProc)
{
HDC windowDc = GetWindowDC(hwnd);
HDC compatDc = Gdi::Dc::getDc(windowDc);
if (compatDc)
{
D3dDdi::ScopedCriticalSection lock;
Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE); Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE);
Gdi::TitleBar titleBar(hwnd, compatDc); Gdi::TitleBar titleBar(hwnd, compatDc);
titleBar.drawAll(); titleBar.drawAll();
@ -430,9 +488,10 @@ namespace
if (compatDc) if (compatDc)
{ {
D3dDdi::ScopedCriticalSection lock;
Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE); Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE);
Gdi::TitleBar titleBar(hwnd, compatDc); Gdi::TitleBar titleBar(hwnd, compatDc);
titleBar.drawCaption(); titleBar.drawAll();
Gdi::Dc::releaseDc(windowDc); Gdi::Dc::releaseDc(windowDc);
} }
@ -479,7 +538,12 @@ namespace
[[maybe_unused]] const std::string& procName, WndProcHook wndProcHook, WNDPROC oldWndProcTrampoline) [[maybe_unused]] const std::string& procName, WndProcHook wndProcHook, WNDPROC oldWndProcTrampoline)
{ {
LOG_FUNC(procName.c_str(), Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam)); LOG_FUNC(procName.c_str(), Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam));
return LOG_RESULT(wndProcHook(hwnd, uMsg, wParam, lParam, oldWndProcTrampoline)); LRESULT result = wndProcHook(hwnd, uMsg, wParam, lParam, oldWndProcTrampoline);
if (WM_CREATE == uMsg && -1 != result)
{
Gdi::WinProc::onCreateWindow(hwnd);
}
return LOG_RESULT(result);
} }
template <WndProcHook wndProcHook> template <WndProcHook wndProcHook>

View File

@ -15,6 +15,11 @@ namespace
namespace Gdi namespace Gdi
{ {
Region::Region(std::nullptr_t)
: m_region(nullptr)
{
}
Region::Region(HRGN rgn) Region::Region(HRGN rgn)
: m_region(rgn) : m_region(rgn)
{ {
@ -42,9 +47,13 @@ namespace Gdi
} }
Region::Region(const Region& other) Region::Region(const Region& other)
: Region() : Region(nullptr)
{ {
CombineRgn(m_region, other, nullptr, RGN_COPY); if (other.m_region)
{
m_region = CreateRectRgn(0, 0, 0, 0);
CombineRgn(m_region, other, nullptr, RGN_COPY);
}
} }
Region::Region(Region&& other) Region::Region(Region&& other)
@ -81,6 +90,23 @@ namespace Gdi
return m_region; return m_region;
} }
bool Region::operator==(const Region& other) const
{
if (m_region)
{
return other.m_region && EqualRgn(m_region, other.m_region);
}
else
{
return !other.m_region;
}
}
bool Region::operator!=(const Region& other) const
{
return !(*this == other);
}
Region Region::operator&(const Region& other) const Region Region::operator&(const Region& other) const
{ {
return combineRegions(*this, other, RGN_AND); return combineRegions(*this, other, RGN_AND);

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <cstddef>
#include <Windows.h> #include <Windows.h>
namespace Gdi namespace Gdi
@ -7,6 +9,7 @@ namespace Gdi
class Region class Region
{ {
public: public:
Region(std::nullptr_t);
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(HWND hwnd);
@ -21,6 +24,9 @@ namespace Gdi
operator HRGN() const; operator HRGN() const;
bool operator==(const Region& other) const;
bool operator!=(const Region& other) const;
Region operator&(const Region& other) const; Region operator&(const Region& other) const;
Region operator|(const Region& other) const; Region operator|(const Region& other) const;
Region operator-(const Region& other) const; Region operator-(const Region& other) const;

View File

@ -19,9 +19,16 @@ namespace
namespace Gdi namespace Gdi
{ {
TitleBar::TitleBar(HWND hwnd, HDC compatDc) : TitleBar::TitleBar(HWND hwnd, HDC compatDc)
m_hwnd(hwnd), m_compatDc(compatDc), m_buttonWidth(0), m_buttonHeight(0), m_tbi(), : m_hwnd(hwnd)
m_windowRect(), m_hasIcon(false), m_hasTitleBar(false) , m_compatDc(compatDc)
, m_buttonWidth(0)
, m_buttonHeight(0)
, m_tbi{}
, m_windowRect{}
, m_hasIcon(false)
, m_hasTitleBar(false)
, m_isActive(false)
{ {
m_hasTitleBar = 0 != (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_STYLE) & WS_CAPTION); m_hasTitleBar = 0 != (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_STYLE) & WS_CAPTION);
if (!m_hasTitleBar) if (!m_hasTitleBar)
@ -46,6 +53,7 @@ namespace Gdi
GetWindowRect(hwnd, &m_windowRect); GetWindowRect(hwnd, &m_windowRect);
OffsetRect(&m_tbi.rcTitleBar, -m_windowRect.left, -m_windowRect.top); OffsetRect(&m_tbi.rcTitleBar, -m_windowRect.left, -m_windowRect.top);
m_isActive = GetActiveWindow() == hwnd;
m_buttonWidth = GetSystemMetrics(SM_CXSIZE) - 2; m_buttonWidth = GetSystemMetrics(SM_CXSIZE) - 2;
m_buttonHeight = GetSystemMetrics(SM_CYSIZE) - 4; m_buttonHeight = GetSystemMetrics(SM_CYSIZE) - 4;
@ -94,7 +102,7 @@ namespace Gdi
} }
UINT flags = 0; UINT flags = 0;
if (GetActiveWindow() == m_hwnd) if (m_isActive)
{ {
flags |= DC_ACTIVE; flags |= DC_ACTIVE;
} }
@ -152,6 +160,11 @@ namespace Gdi
} }
} }
void TitleBar::setActive(bool isActive)
{
m_isActive = isActive;
}
bool TitleBar::isVisible(std::size_t tbiIndex) const bool TitleBar::isVisible(std::size_t tbiIndex) const
{ {
return !(m_tbi.rgstate[tbiIndex] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN)); return !(m_tbi.rgstate[tbiIndex] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN));

View File

@ -15,6 +15,7 @@ namespace Gdi
void drawButtons() const; void drawButtons() const;
void drawCaption() const; void drawCaption() const;
void excludeFromClipRegion() const; void excludeFromClipRegion() const;
void setActive(bool isActive);
private: private:
void adjustButtonSize(RECT& rect) const; void adjustButtonSize(RECT& rect) const;
@ -29,5 +30,6 @@ namespace Gdi
RECT m_windowRect; RECT m_windowRect;
bool m_hasIcon; bool m_hasIcon;
bool m_hasTitleBar; bool m_hasTitleBar;
bool m_isActive;
}; };
} }

View File

@ -5,7 +5,7 @@
#include <Common/Hook.h> #include <Common/Hook.h>
#include <Common/Log.h> #include <Common/Log.h>
#include <Common/ScopedCriticalSection.h> #include <Common/ScopedSrwLock.h>
#include <Dll/Dll.h> #include <Dll/Dll.h>
#include <Gdi/AccessGuard.h> #include <Gdi/AccessGuard.h>
#include <Gdi/Dc.h> #include <Gdi/Dc.h>
@ -38,15 +38,15 @@ namespace
HWINEVENTHOOK g_objectStateChangeEventHook = nullptr; HWINEVENTHOOK g_objectStateChangeEventHook = nullptr;
std::set<Gdi::WindowPosChangeNotifyFunc> g_windowPosChangeNotifyFuncs; std::set<Gdi::WindowPosChangeNotifyFunc> g_windowPosChangeNotifyFuncs;
Compat::CriticalSection g_windowProcCs; Compat::SrwLock g_windowProcSrwLock;
std::map<HWND, WindowProc> g_windowProc; std::map<HWND, WindowProc> g_windowProc;
WindowProc getWindowProc(HWND hwnd); WindowProc getWindowProc(HWND hwnd);
void onActivate(HWND hwnd); bool isTopLevelWindow(HWND hwnd);
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); void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp);
void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW); void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW);
LRESULT CALLBACK ddcWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT CALLBACK ddcWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
@ -56,6 +56,14 @@ namespace
switch (uMsg) switch (uMsg)
{ {
case WM_SYNCPAINT:
if (isTopLevelWindow(hwnd))
{
Gdi::Window::onSyncPaint(hwnd);
return 0;
}
break;
case WM_WINDOWPOSCHANGED: case WM_WINDOWPOSCHANGED:
onWindowPosChanged(hwnd); onWindowPosChanged(hwnd);
break; break;
@ -65,18 +73,14 @@ namespace
switch (uMsg) switch (uMsg)
{ {
case WM_ACTIVATE:
onActivate(hwnd);
break;
case WM_NCDESTROY: case WM_NCDESTROY:
onDestroyWindow(hwnd); onDestroyWindow(hwnd);
break; break;
case WM_STYLECHANGED: case WM_STYLECHANGED:
if (GWL_EXSTYLE == wParam) if (isTopLevelWindow(hwnd))
{ {
onWindowPosChanged(hwnd); Gdi::Window::onStyleChanged(hwnd, wParam);
} }
break; break;
@ -103,7 +107,7 @@ namespace
{ {
if (GWL_WNDPROC == nIndex) if (GWL_WNDPROC == nIndex)
{ {
Compat::ScopedCriticalSection lock(g_windowProcCs); Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.find(hWnd); auto it = g_windowProc.find(hWnd);
if (it != g_windowProc.end()) if (it != g_windowProc.end())
{ {
@ -127,7 +131,7 @@ namespace
WindowProc getWindowProc(HWND hwnd) WindowProc getWindowProc(HWND hwnd)
{ {
Compat::ScopedCriticalSection lock(g_windowProcCs); Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
return g_windowProc[hwnd]; return g_windowProc[hwnd];
} }
@ -153,6 +157,11 @@ namespace
return TRUE; return TRUE;
} }
bool isTopLevelWindow(HWND hwnd)
{
return GetDesktopWindow() == GetAncestor(hwnd, GA_PARENT);
}
void CALLBACK objectCreateEvent( void CALLBACK objectCreateEvent(
HWINEVENTHOOK /*hWinEventHook*/, HWINEVENTHOOK /*hWinEventHook*/,
DWORD /*event*/, DWORD /*event*/,
@ -207,26 +216,28 @@ namespace
} }
} }
void onActivate(HWND hwnd)
{
RECT windowRect = {};
GetWindowRect(hwnd, &windowRect);
RECT clientRect = {};
GetClientRect(hwnd, &clientRect);
POINT clientOrigin = {};
ClientToScreen(hwnd, &clientOrigin);
OffsetRect(&windowRect, -clientOrigin.x, -clientOrigin.y);
HRGN ncRgn = CreateRectRgnIndirect(&windowRect);
HRGN clientRgn = CreateRectRgnIndirect(&clientRect);
CombineRgn(ncRgn, ncRgn, clientRgn, RGN_DIFF);
RedrawWindow(hwnd, nullptr, ncRgn, RDW_FRAME | RDW_INVALIDATE);
DeleteObject(clientRgn);
DeleteObject(ncRgn);
}
void onCreateWindow(HWND hwnd) void onCreateWindow(HWND hwnd)
{ {
LOG_FUNC("onCreateWindow", hwnd);
{
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
if (g_windowProc.find(hwnd) != g_windowProc.end())
{
return;
}
auto wndProcA = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC));
auto wndProcW = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongW)(hwnd, GWL_WNDPROC));
g_windowProc[hwnd] = { wndProcA, wndProcW };
setWindowProc(hwnd, ddcWindowProcA, ddcWindowProcW);
}
if (!isTopLevelWindow(hwnd))
{
return;
}
char className[64] = {}; char className[64] = {};
GetClassName(hwnd, className, sizeof(className)); GetClassName(hwnd, className, sizeof(className));
if (std::string(className) == "CompatWindowDesktopReplacement") if (std::string(className) == "CompatWindowDesktopReplacement")
@ -236,27 +247,19 @@ namespace
return; return;
} }
if (!Gdi::Window::isPresentationWindow(hwnd)) Gdi::Window::updateAll();
{
Compat::ScopedCriticalSection lock(g_windowProcCs);
if (g_windowProc.find(hwnd) == g_windowProc.end())
{
auto wndProcA = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC));
auto wndProcW = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongW)(hwnd, GWL_WNDPROC));
g_windowProc[hwnd] = { wndProcA, wndProcW };
setWindowProc(hwnd, ddcWindowProcA, ddcWindowProcW);
}
}
Gdi::Window::add(hwnd);
} }
void onDestroyWindow(HWND hwnd) void onDestroyWindow(HWND hwnd)
{ {
Gdi::Window::remove(hwnd); if (isTopLevelWindow(hwnd))
delete reinterpret_cast<ChildWindowInfo*>(RemoveProp(hwnd, PROP_DDRAWCOMPAT)); {
Gdi::Window::updateAll();
return;
}
Compat::ScopedCriticalSection lock(g_windowProcCs); delete reinterpret_cast<ChildWindowInfo*>(RemoveProp(hwnd, PROP_DDRAWCOMPAT));
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.find(hwnd); auto it = g_windowProc.find(hwnd);
if (it != g_windowProc.end()) if (it != g_windowProc.end())
{ {
@ -267,24 +270,13 @@ namespace
void onWindowPosChanged(HWND hwnd) void onWindowPosChanged(HWND hwnd)
{ {
if (Gdi::MENU_ATOM == GetClassLongPtr(hwnd, GCW_ATOM))
{
auto exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE);
if (exStyle & WS_EX_LAYERED)
{
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
return;
}
}
for (auto notifyFunc : g_windowPosChangeNotifyFuncs) for (auto notifyFunc : g_windowPosChangeNotifyFuncs)
{ {
notifyFunc(); notifyFunc();
} }
if (Gdi::Window::isTopLevelWindow(hwnd)) if (isTopLevelWindow(hwnd))
{ {
Gdi::Window::add(hwnd);
Gdi::Window::updateAll(); Gdi::Window::updateAll();
} }
else else
@ -321,9 +313,13 @@ namespace
} }
} }
void onWindowPosChanging(HWND hwnd, const WINDOWPOS& wp) void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp)
{ {
if (!Gdi::Window::isTopLevelWindow(hwnd)) if (isTopLevelWindow(hwnd))
{
wp.flags |= SWP_NOREDRAW;
}
else
{ {
std::unique_ptr<ChildWindowInfo> cwi(reinterpret_cast<ChildWindowInfo*>(RemoveProp(hwnd, PROP_DDRAWCOMPAT))); std::unique_ptr<ChildWindowInfo> cwi(reinterpret_cast<ChildWindowInfo*>(RemoveProp(hwnd, PROP_DDRAWCOMPAT)));
if (!(wp.flags & SWP_NOMOVE) && IsWindowVisible(hwnd) && !IsIconic(GetAncestor(hwnd, GA_ROOT))) if (!(wp.flags & SWP_NOMOVE) && IsWindowVisible(hwnd) && !IsIconic(GetAncestor(hwnd, GA_ROOT)))
@ -339,12 +335,25 @@ namespace
} }
} }
BOOL WINAPI setLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags)
{
LOG_FUNC("SetLayeredWindowAttributes", hwnd, crKey, bAlpha, dwFlags);
BOOL result = CALL_ORIG_FUNC(SetLayeredWindowAttributes)(hwnd, crKey, bAlpha, dwFlags);
if (result)
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(dwFlags & LWA_COLORKEY) ? crKey : CLR_INVALID,
(dwFlags & LWA_ALPHA) ? bAlpha : 255);
}
return LOG_RESULT(result);
}
LONG setWindowLong(HWND hWnd, int nIndex, LONG dwNewLong, LONG setWindowLong(HWND hWnd, int nIndex, LONG dwNewLong,
decltype(&SetWindowLongA) origSetWindowLong, WNDPROC(WindowProc::* wndProc)) decltype(&SetWindowLongA) origSetWindowLong, WNDPROC(WindowProc::* wndProc))
{ {
if (GWL_WNDPROC == nIndex) if (GWL_WNDPROC == nIndex)
{ {
Compat::ScopedCriticalSection lock(g_windowProcCs); Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.find(hWnd); auto it = g_windowProc.find(hWnd);
if (it != g_windowProc.end() && 0 != origSetWindowLong(hWnd, nIndex, dwNewLong)) if (it != g_windowProc.end() && 0 != origSetWindowLong(hWnd, nIndex, dwNewLong))
{ {
@ -385,6 +394,7 @@ namespace
wp.cy = cy; wp.cy = cy;
wp.flags = uFlags; wp.flags = uFlags;
onWindowPosChanging(hWnd, wp); onWindowPosChanging(hWnd, wp);
uFlags = wp.flags;
} }
BOOL result = CALL_ORIG_FUNC(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); BOOL result = CALL_ORIG_FUNC(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
delete reinterpret_cast<ChildWindowInfo*>(RemoveProp(hWnd, PROP_DDRAWCOMPAT)); delete reinterpret_cast<ChildWindowInfo*>(RemoveProp(hWnd, PROP_DDRAWCOMPAT));
@ -402,6 +412,34 @@ namespace
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(wndProcA)); CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(wndProcA));
} }
} }
BOOL WINAPI updateLayeredWindow(HWND hWnd, HDC hdcDst, POINT* pptDst, SIZE* psize,
HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwFlags)
{
LOG_FUNC("UpdateLayeredWindow", hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindow)(
hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags);
if (result && hdcSrc)
{
Gdi::Window::updateLayeredWindowInfo(hWnd,
(dwFlags & ULW_COLORKEY) ? crKey : CLR_INVALID,
((dwFlags & LWA_ALPHA) && pblend) ? pblend->SourceConstantAlpha : 255);
}
return LOG_RESULT(result);
}
BOOL WINAPI updateLayeredWindowIndirect(HWND hwnd, const UPDATELAYEREDWINDOWINFO* pULWInfo)
{
LOG_FUNC("UpdateLayeredWindowIndirect", hwnd, pULWInfo);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindowIndirect)(hwnd, pULWInfo);
if (result && pULWInfo)
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(pULWInfo->dwFlags & ULW_COLORKEY) ? pULWInfo->crKey : CLR_INVALID,
((pULWInfo->dwFlags & LWA_ALPHA) && pULWInfo->pblend) ? pULWInfo->pblend->SourceConstantAlpha : 255);
}
return LOG_RESULT(result);
}
} }
namespace Gdi namespace Gdi
@ -411,7 +449,7 @@ namespace Gdi
void dllThreadDetach() void dllThreadDetach()
{ {
auto threadId = GetCurrentThreadId(); auto threadId = GetCurrentThreadId();
Compat::ScopedCriticalSection lock(g_windowProcCs); Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.begin(); auto it = g_windowProc.begin();
while (it != g_windowProc.end()) while (it != g_windowProc.end())
{ {
@ -430,9 +468,12 @@ namespace Gdi
{ {
HOOK_FUNCTION(user32, GetWindowLongA, getWindowLongA); HOOK_FUNCTION(user32, GetWindowLongA, getWindowLongA);
HOOK_FUNCTION(user32, GetWindowLongW, getWindowLongW); HOOK_FUNCTION(user32, GetWindowLongW, getWindowLongW);
HOOK_FUNCTION(user32, SetLayeredWindowAttributes, setLayeredWindowAttributes);
HOOK_FUNCTION(user32, SetWindowLongA, setWindowLongA); HOOK_FUNCTION(user32, SetWindowLongA, setWindowLongA);
HOOK_FUNCTION(user32, SetWindowLongW, setWindowLongW); HOOK_FUNCTION(user32, SetWindowLongW, setWindowLongW);
HOOK_FUNCTION(user32, SetWindowPos, setWindowPos); HOOK_FUNCTION(user32, SetWindowPos, setWindowPos);
HOOK_FUNCTION(user32, UpdateLayeredWindow, updateLayeredWindow);
HOOK_FUNCTION(user32, UpdateLayeredWindowIndirect, updateLayeredWindowIndirect);
g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE,
Dll::g_currentModule, &objectCreateEvent, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); Dll::g_currentModule, &objectCreateEvent, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
@ -443,6 +484,14 @@ namespace Gdi
Gdi::Window::updateAll(); Gdi::Window::updateAll();
} }
void onCreateWindow(HWND hwnd)
{
if (g_objectCreateEventHook)
{
::onCreateWindow(hwnd);
}
}
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc) void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc)
{ {
g_windowPosChangeNotifyFuncs.insert(notifyFunc); g_windowPosChangeNotifyFuncs.insert(notifyFunc);
@ -453,7 +502,7 @@ namespace Gdi
UnhookWinEvent(g_objectStateChangeEventHook); UnhookWinEvent(g_objectStateChangeEventHook);
UnhookWinEvent(g_objectCreateEventHook); UnhookWinEvent(g_objectCreateEventHook);
Compat::ScopedCriticalSection lock(g_windowProcCs); Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
for (const auto& windowProc : g_windowProc) for (const auto& windowProc : g_windowProc)
{ {
setWindowProc(windowProc.first, windowProc.second.wndProcA, windowProc.second.wndProcW); setWindowProc(windowProc.first, windowProc.second.wndProcA, windowProc.second.wndProcW);

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Gdi.h" #include <Gdi/Gdi.h>
namespace Gdi namespace Gdi
{ {
@ -8,6 +8,7 @@ namespace Gdi
{ {
void dllThreadDetach(); void dllThreadDetach();
void installHooks(); void installHooks();
void onCreateWindow(HWND hwnd);
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc); void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc);
void uninstallHooks(); void uninstallHooks();
} }

View File

@ -1,22 +1,90 @@
#include <algorithm>
#include <map>
#include <vector>
#include <dwmapi.h> #include <dwmapi.h>
#include <Common/Hook.h> #include <Common/Hook.h>
#include <Common/Log.h> #include <Common/Log.h>
#include <D3dDdi/KernelModeThunks.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/Region.h> #include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h> #include <Gdi/Window.h>
namespace namespace
{ {
struct UpdateWindowContext
{
Gdi::Region obscuredRegion;
Gdi::Region virtualScreenRegion;
DWORD processId;
};
struct Window
{
HWND hwnd;
HWND presentationWindow;
RECT windowRect;
Gdi::Region windowRegion;
Gdi::Region visibleRegion;
Gdi::Region invalidatedRegion;
COLORREF colorKey;
BYTE alpha;
bool isLayered;
bool isRgnChangePending;
Window(HWND hwnd)
: hwnd(hwnd)
, presentationWindow(nullptr)
, windowRect{}
, windowRegion(nullptr)
, colorKey(CLR_INVALID)
, alpha(255)
, isLayered(true)
, isRgnChangePending(false)
{
}
};
const UINT WM_CREATEPRESENTATIONWINDOW = WM_USER; const UINT WM_CREATEPRESENTATIONWINDOW = WM_USER;
const UINT WM_DESTROYPRESENTATIONWINDOW = WM_USER + 1; const UINT WM_SETPRESENTATIONWINDOWPOS = WM_USER + 1;
const UINT WM_SETPRESENTATIONWINDOWPOS = WM_USER + 2; const UINT WM_SETPRESENTATIONWINDOWRGN = WM_USER + 2;
const RECT REGION_OVERRIDE_MARKER_RECT = { 32000, 32000, 32001, 32001 };
HANDLE g_presentationWindowThread = nullptr; HANDLE g_presentationWindowThread = nullptr;
DWORD g_presentationWindowThreadId = 0; DWORD g_presentationWindowThreadId = 0;
HWND g_messageWindow = nullptr; HWND g_messageWindow = nullptr;
std::map<HWND, Window> g_windows;
std::vector<Window*> g_windowZOrder;
std::map<HWND, Window>::iterator addWindow(HWND hwnd)
{
DWMNCRENDERINGPOLICY ncRenderingPolicy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncRenderingPolicy, sizeof(ncRenderingPolicy));
BOOL disableTransitions = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disableTransitions, sizeof(disableTransitions));
const auto style = GetClassLongPtr(hwnd, GCL_STYLE);
if (style & CS_DROPSHADOW)
{
SetClassLongPtr(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW);
}
return g_windows.emplace(hwnd, Window(hwnd)).first;
}
Gdi::Region getWindowRegion(HWND hwnd)
{
Gdi::Region rgn;
if (ERROR == CALL_ORIG_FUNC(GetWindowRgn)(hwnd, rgn))
{
return nullptr;
}
return rgn;
}
LRESULT CALLBACK messageWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK messageWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ {
@ -25,25 +93,18 @@ namespace
{ {
case WM_CREATEPRESENTATIONWINDOW: case WM_CREATEPRESENTATIONWINDOW:
{ {
D3dDdi::ScopedCriticalSection lock;
HWND origWindow = reinterpret_cast<HWND>(lParam);
auto window = Gdi::Window::get(origWindow);
if (!window)
{
return 0;
}
// Workaround for ForceSimpleWindow shim // Workaround for ForceSimpleWindow shim
static auto origCreateWindowExA = reinterpret_cast<decltype(&CreateWindowExA)>( static auto origCreateWindowExA = reinterpret_cast<decltype(&CreateWindowExA)>(
Compat::getProcAddress(GetModuleHandle("user32"), "CreateWindowExA")); Compat::getProcAddress(GetModuleHandle("user32"), "CreateWindowExA"));
HWND owner = reinterpret_cast<HWND>(wParam);
HWND presentationWindow = origCreateWindowExA( HWND presentationWindow = origCreateWindowExA(
WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOPARENTNOTIFY | WS_EX_TOOLWINDOW, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOPARENTNOTIFY | WS_EX_TOOLWINDOW,
"DDrawCompatPresentationWindow", "DDrawCompatPresentationWindow",
nullptr, nullptr,
WS_DISABLED | WS_POPUP, WS_DISABLED | WS_POPUP,
0, 0, 1, 1, 0, 0, 1, 1,
origWindow, owner,
nullptr, nullptr,
nullptr, nullptr,
nullptr); nullptr);
@ -51,11 +112,9 @@ namespace
if (presentationWindow) if (presentationWindow)
{ {
CALL_ORIG_FUNC(SetLayeredWindowAttributes)(presentationWindow, 0, 255, LWA_ALPHA); CALL_ORIG_FUNC(SetLayeredWindowAttributes)(presentationWindow, 0, 255, LWA_ALPHA);
SendMessage(presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(origWindow));
window->setPresentationWindow(presentationWindow);
} }
return 0; return reinterpret_cast<LRESULT>(presentationWindow);
} }
case WM_DESTROY: case WM_DESTROY:
@ -73,62 +132,21 @@ namespace
switch (uMsg) switch (uMsg)
{ {
case WM_DESTROYPRESENTATIONWINDOW:
DestroyWindow(hwnd);
return 0;
case WM_SETPRESENTATIONWINDOWPOS: case WM_SETPRESENTATIONWINDOWPOS:
{ {
HWND owner = reinterpret_cast<HWND>(lParam); const auto& wp = *reinterpret_cast<WINDOWPOS*>(lParam);
if (IsWindowVisible(owner) && !IsIconic(owner)) return SetWindowPos(hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
}
case WM_SETPRESENTATIONWINDOWRGN:
{
HRGN rgn = nullptr;
if (wParam)
{ {
DWORD flags = SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW; rgn = CreateRectRgn(0, 0, 0, 0);
CombineRgn(rgn, reinterpret_cast<HRGN>(wParam), nullptr, RGN_COPY);
HWND insertAfter = HWND_TOP;
HWND prev = GetWindow(owner, GW_HWNDPREV);
if (prev)
{
if (hwnd == prev)
{
flags = flags | SWP_NOZORDER;
}
else
{
insertAfter = prev;
}
}
RECT wr = {};
GetWindowRect(owner, &wr);
RECT wrPres = {};
GetWindowRect(hwnd, &wrPres);
if (!(flags & SWP_NOZORDER) || !EqualRect(&wr, &wrPres) || !IsWindowVisible(hwnd))
{
CALL_ORIG_FUNC(SetWindowPos)(
hwnd, insertAfter, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, flags);
}
Gdi::Region rgn;
int rgnResult = GetWindowRgn(owner, rgn);
Gdi::Region rgnPres;
int rgnPresResult = GetWindowRgn(hwnd, rgnPres);
if (rgnResult != rgnPresResult || !EqualRgn(rgn, rgnPres))
{
if (ERROR != rgnResult)
{
SetWindowRgn(hwnd, rgn.release(), FALSE);
}
else
{
SetWindowRgn(hwnd, nullptr, FALSE);
}
}
} }
else if (IsWindowVisible(hwnd)) return SetWindowRgn(hwnd, rgn, FALSE);
{
ShowWindow(hwnd, SW_HIDE);
}
return 0;
} }
} }
@ -162,334 +180,469 @@ namespace
return 0; return 0;
} }
BOOL WINAPI setLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags) LRESULT sendMessageBlocking(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
LOG_FUNC("SetLayeredWindowAttributes", hwnd, crKey, bAlpha, dwFlags); DWORD_PTR result = 0;
BOOL result = CALL_ORIG_FUNC(SetLayeredWindowAttributes)(hwnd, crKey, bAlpha, dwFlags); SendMessageTimeout(hwnd, msg, wParam, lParam, SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG, 0, &result);
if (result) return result;
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(dwFlags & LWA_COLORKEY) ? crKey : CLR_INVALID,
(dwFlags & LWA_ALPHA) ? bAlpha : 255);
}
return LOG_RESULT(result);
} }
BOOL WINAPI updateLayeredWindow(HWND hWnd, HDC hdcDst, POINT* pptDst, SIZE* psize, void updatePosition(Window& window, const RECT& oldWindowRect, const Gdi::Region& oldVisibleRegion)
HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwFlags)
{ {
LOG_FUNC("UpdateLayeredWindow", hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags); Gdi::Region preservedRegion(oldVisibleRegion);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindow)( preservedRegion.offset(window.windowRect.left - oldWindowRect.left, window.windowRect.top - oldWindowRect.top);
hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags); preservedRegion &= window.visibleRegion;
if (result && hdcSrc)
POINT clientPos = {};
ClientToScreen(window.hwnd, &clientPos);
if (!preservedRegion.isEmpty())
{ {
Gdi::Window::updateLayeredWindowInfo(hWnd, Gdi::Region updateRegion;
(dwFlags & ULW_COLORKEY) ? crKey : CLR_INVALID, GetUpdateRgn(window.hwnd, updateRegion, FALSE);
((dwFlags & LWA_ALPHA) && pblend) ? pblend->SourceConstantAlpha : 255); OffsetRgn(updateRegion, clientPos.x, clientPos.y);
preservedRegion -= updateRegion;
if (!preservedRegion.isEmpty() &&
(window.windowRect.left != oldWindowRect.left || window.windowRect.top != oldWindowRect.top))
{
HDC screenDc = GetDC(nullptr);
SelectClipRgn(screenDc, preservedRegion);
BitBlt(screenDc, window.windowRect.left, window.windowRect.top,
oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top,
screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY);
SelectClipRgn(screenDc, nullptr);
CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc);
}
} }
return LOG_RESULT(result);
window.invalidatedRegion = window.visibleRegion - preservedRegion;
OffsetRgn(window.invalidatedRegion, -clientPos.x, -clientPos.y);
} }
BOOL WINAPI updateLayeredWindowIndirect(HWND hwnd, const UPDATELAYEREDWINDOWINFO* pULWInfo) BOOL CALLBACK updateWindow(HWND hwnd, LPARAM lParam)
{ {
LOG_FUNC("UpdateLayeredWindowIndirect", hwnd, pULWInfo); auto& context = *reinterpret_cast<UpdateWindowContext*>(lParam);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindowIndirect)(hwnd, pULWInfo);
if (result && pULWInfo) DWORD processId = 0;
GetWindowThreadProcessId(hwnd, &processId);
if (GetWindowThreadProcessId(hwnd, &processId) == g_presentationWindowThreadId ||
processId != context.processId)
{ {
Gdi::Window::updateLayeredWindowInfo(hwnd, return TRUE;
(pULWInfo->dwFlags & ULW_COLORKEY) ? pULWInfo->crKey : CLR_INVALID,
((pULWInfo->dwFlags & LWA_ALPHA) && pULWInfo->pblend) ? pULWInfo->pblend->SourceConstantAlpha : 255);
} }
return LOG_RESULT(result);
auto it = g_windows.find(hwnd);
if (it == g_windows.end())
{
it = addWindow(hwnd);
}
g_windowZOrder.push_back(&it->second);
const LONG exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE);
const bool isLayered = exStyle & WS_EX_LAYERED;
const bool isVisible = IsWindowVisible(hwnd) && !IsIconic(hwnd);
bool setPresentationWindowRgn = false;
if (isLayered != it->second.isLayered)
{
it->second.isLayered = isLayered;
it->second.isRgnChangePending = isVisible;
if (!isLayered)
{
it->second.presentationWindow = reinterpret_cast<HWND>(
sendMessageBlocking(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, reinterpret_cast<WPARAM>(hwnd), 0));
setPresentationWindowRgn = true;
}
else if (it->second.presentationWindow)
{
sendMessageBlocking(it->second.presentationWindow, WM_CLOSE, 0, 0);
it->second.presentationWindow = nullptr;
}
}
Gdi::Region windowRegion(getWindowRegion(hwnd));
if (windowRegion && !PtInRegion(windowRegion, REGION_OVERRIDE_MARKER_RECT.left, REGION_OVERRIDE_MARKER_RECT.top) ||
!windowRegion && it->second.windowRegion)
{
swap(it->second.windowRegion, windowRegion);
setPresentationWindowRgn = true;
}
RECT windowRect = {};
Gdi::Region visibleRegion;
if (isVisible)
{
GetWindowRect(hwnd, &windowRect);
if (!IsRectEmpty(&windowRect))
{
if (it->second.windowRegion)
{
visibleRegion = it->second.windowRegion;
OffsetRgn(visibleRegion, windowRect.left, windowRect.top);
}
else
{
visibleRegion = windowRect;
}
visibleRegion &= context.virtualScreenRegion;
visibleRegion -= context.obscuredRegion;
if (!isLayered && !(exStyle & WS_EX_TRANSPARENT))
{
context.obscuredRegion |= visibleRegion;
}
}
}
std::swap(it->second.windowRect, windowRect);
swap(it->second.visibleRegion, visibleRegion);
if (!isLayered)
{
if (!it->second.visibleRegion.isEmpty())
{
updatePosition(it->second, windowRect, visibleRegion);
}
if (isVisible && !it->second.isRgnChangePending)
{
OffsetRgn(visibleRegion, it->second.windowRect.left - windowRect.left, it->second.windowRect.top - windowRect.top);
if (it->second.visibleRegion != visibleRegion)
{
it->second.isRgnChangePending = true;
}
}
if (it->second.presentationWindow)
{
if (setPresentationWindowRgn)
{
HRGN rgn = it->second.windowRegion;
sendMessageBlocking(it->second.presentationWindow, WM_SETPRESENTATIONWINDOWRGN,
reinterpret_cast<WPARAM>(rgn), 0);
}
WINDOWPOS wp = {};
wp.flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOSENDCHANGING;
if (isVisible)
{
wp.hwndInsertAfter = GetWindow(hwnd, GW_HWNDPREV);
if (!wp.hwndInsertAfter)
{
wp.hwndInsertAfter = (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_TOP;
}
else if (wp.hwndInsertAfter == it->second.presentationWindow)
{
wp.flags |= SWP_NOZORDER;
}
wp.x = it->second.windowRect.left;
wp.y = it->second.windowRect.top;
wp.cx = it->second.windowRect.right - it->second.windowRect.left;
wp.cy = it->second.windowRect.bottom - it->second.windowRect.top;
wp.flags |= SWP_SHOWWINDOW;
}
else
{
wp.flags |= SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER;
}
sendMessageBlocking(it->second.presentationWindow, WM_SETPRESENTATIONWINDOWPOS,
0, reinterpret_cast<LPARAM>(&wp));
}
}
return TRUE;
} }
} }
namespace Gdi namespace Gdi
{ {
Window::Window(HWND hwnd) namespace Window
: m_hwnd(hwnd)
, m_presentationWindow(nullptr)
, m_windowRect{ 0, 0, 0, 0 }
, m_colorKey(CLR_INVALID)
, m_alpha(255)
, m_isLayered(CALL_ORIG_FUNC(GetWindowLongA)(m_hwnd, GWL_EXSTYLE)& WS_EX_LAYERED)
{ {
DWMNCRENDERINGPOLICY ncRenderingPolicy = DWMNCRP_DISABLED; void installHooks()
DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncRenderingPolicy, sizeof(ncRenderingPolicy));
BOOL disableTransitions = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disableTransitions, sizeof(disableTransitions));
const auto style = GetClassLongPtr(hwnd, GCL_STYLE);
if (style & CS_DROPSHADOW)
{ {
SetClassLongPtr(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW); WNDCLASS wc = {};
wc.lpfnWndProc = &presentationWindowProc;
wc.hInstance = Dll::g_currentModule;
wc.lpszClassName = "DDrawCompatPresentationWindow";
RegisterClass(&wc);
g_presentationWindowThread = CreateThread(
nullptr, 0, &presentationWindowThreadProc, nullptr, 0, &g_presentationWindowThreadId);
int i = 0;
while (!g_messageWindow && i < 1000)
{
Sleep(1);
++i;
}
if (!g_messageWindow)
{
Compat::Log() << "ERROR: Failed to create a message-only window";
}
} }
if (!m_isLayered) bool isPresentationWindow(HWND hwnd)
{ {
SendNotifyMessage(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, 0, reinterpret_cast<WPARAM>(hwnd)); return g_presentationWindowThreadId == GetWindowThreadProcessId(hwnd, nullptr);
} }
update(); void onStyleChanged(HWND hwnd, WPARAM wParam)
}
Window::~Window()
{
if (m_presentationWindow)
{ {
SendNotifyMessage(m_presentationWindow, WM_DESTROYPRESENTATIONWINDOW, 0, 0); if (GWL_EXSTYLE == wParam)
{
D3dDdi::ScopedCriticalSection lock;
auto it = g_windows.find(hwnd);
if (it != g_windows.end())
{
const bool isLayered = GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED;
if (isLayered != it->second.isLayered)
{
updateAll();
}
}
}
} }
}
bool Window::add(HWND hwnd) void onSyncPaint(HWND hwnd)
{ {
if (isTopLevelWindow(hwnd) && !get(hwnd)) LOG_FUNC("Window::onSyncPaint", hwnd);
bool isInvalidated = false;
{
D3dDdi::ScopedCriticalSection lock;
auto it = g_windows.find(hwnd);
if (it == g_windows.end())
{
return;
}
if (it->second.isRgnChangePending)
{
it->second.isRgnChangePending = false;
const LONG origWndProc = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC);
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(CALL_ORIG_FUNC(DefWindowProcA)));
if (it->second.isLayered)
{
SetWindowRgn(hwnd, Gdi::Region(it->second.windowRegion).release(), FALSE);
}
else
{
Gdi::Region rgn(it->second.visibleRegion);
OffsetRgn(rgn, -it->second.windowRect.left, -it->second.windowRect.top);
rgn |= REGION_OVERRIDE_MARKER_RECT;
SetWindowRgn(hwnd, rgn, FALSE);
}
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, origWndProc);
}
isInvalidated = !it->second.invalidatedRegion.isEmpty();
if (isInvalidated)
{
RedrawWindow(hwnd, nullptr, it->second.invalidatedRegion,
RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN);
it->second.invalidatedRegion = nullptr;
}
}
if (isInvalidated)
{
RECT emptyRect = {};
RedrawWindow(hwnd, &emptyRect, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ERASENOW);
}
}
void present(CompatRef<IDirectDrawSurface7> dst, CompatRef<IDirectDrawSurface7> src,
CompatRef<IDirectDrawClipper> clipper)
{ {
D3dDdi::ScopedCriticalSection lock; D3dDdi::ScopedCriticalSection lock;
s_windows.emplace(hwnd, std::make_shared<Window>(hwnd)); for (auto window : g_windowZOrder)
return true;
}
return false;
}
void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion)
{
if (IsRectEmpty(&m_windowRect) || m_visibleRegion.isEmpty())
{
m_invalidatedRegion = Region();
return;
}
m_invalidatedRegion = m_visibleRegion;
if (m_windowRect.right - m_windowRect.left == oldWindowRect.right - oldWindowRect.left &&
m_windowRect.bottom - m_windowRect.top == oldWindowRect.bottom - oldWindowRect.top)
{
Region preservedRegion(oldVisibleRegion);
preservedRegion.offset(m_windowRect.left - oldWindowRect.left, m_windowRect.top - oldWindowRect.top);
preservedRegion &= m_visibleRegion;
if (!preservedRegion.isEmpty())
{ {
if (m_windowRect.left != oldWindowRect.left || m_windowRect.top != oldWindowRect.top) if (window->presentationWindow && !window->visibleRegion.isEmpty())
{ {
HDC screenDc = GetDC(nullptr); clipper->SetHWnd(&clipper, 0, window->presentationWindow);
SelectClipRgn(screenDc, preservedRegion); dst->Blt(&dst, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
BitBlt(screenDc, m_windowRect.left, m_windowRect.top,
oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top,
screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY);
SelectClipRgn(screenDc, nullptr);
CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc);
} }
m_invalidatedRegion -= preservedRegion;
} }
} }
}
std::shared_ptr<Window> Window::get(HWND hwnd) void present(Gdi::Region excludeRegion)
{
D3dDdi::ScopedCriticalSection lock;
auto it = s_windows.find(hwnd);
return it != s_windows.end() ? it->second : nullptr;
}
BYTE Window::getAlpha() const
{
D3dDdi::ScopedCriticalSection lock;
return m_alpha;
}
COLORREF Window::getColorKey() const
{
D3dDdi::ScopedCriticalSection lock;
return m_colorKey;
}
HWND Window::getPresentationWindow() const
{
return m_presentationWindow ? m_presentationWindow : m_hwnd;
}
Region Window::getVisibleRegion() const
{
D3dDdi::ScopedCriticalSection lock;
return m_visibleRegion;
}
RECT Window::getWindowRect() const
{
D3dDdi::ScopedCriticalSection lock;
return m_windowRect;
}
std::map<HWND, std::shared_ptr<Window>> Window::getWindows()
{
D3dDdi::ScopedCriticalSection lock;
return s_windows;
}
void Window::installHooks()
{
HOOK_FUNCTION(user32, SetLayeredWindowAttributes, setLayeredWindowAttributes);
HOOK_FUNCTION(user32, UpdateLayeredWindow, updateLayeredWindow);
HOOK_FUNCTION(user32, UpdateLayeredWindowIndirect, updateLayeredWindowIndirect);
WNDCLASS wc = {};
wc.lpfnWndProc = &presentationWindowProc;
wc.hInstance = Dll::g_currentModule;
wc.lpszClassName = "DDrawCompatPresentationWindow";
RegisterClass(&wc);
g_presentationWindowThread = CreateThread(
nullptr, 0, &presentationWindowThreadProc, nullptr, 0, &g_presentationWindowThreadId);
int i = 0;
while (!g_messageWindow && i < 1000)
{ {
Sleep(1); D3dDdi::ScopedCriticalSection lock;
++i; std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc);
} RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
if (!g_messageWindow) for (auto window : g_windowZOrder)
{
Compat::Log() << "ERROR: Failed to create a message-only window";
}
}
bool Window::isLayered() const
{
return m_isLayered;
}
bool Window::isPresentationWindow(HWND hwnd)
{
return IsWindow(hwnd) && g_presentationWindowThreadId == GetWindowThreadProcessId(hwnd, nullptr);
}
bool Window::isTopLevelWindow(HWND hwnd)
{
return GetDesktopWindow() == GetAncestor(hwnd, GA_PARENT);
}
void Window::remove(HWND hwnd)
{
D3dDdi::ScopedCriticalSection lock;
s_windows.erase(hwnd);
}
void Window::uninstallHooks()
{
if (g_presentationWindowThread)
{
SendMessage(g_messageWindow, WM_CLOSE, 0, 0);
if (WAIT_OBJECT_0 != WaitForSingleObject(g_presentationWindowThread, 1000))
{ {
TerminateThread(g_presentationWindowThread, 0); if (!window->presentationWindow)
Compat::Log() << "The presentation window thread was terminated forcefully"; {
continue;
}
Gdi::Region visibleRegion(window->visibleRegion);
if (excludeRegion)
{
visibleRegion -= excludeRegion;
}
if (visibleRegion.isEmpty())
{
continue;
}
if (!virtualScreenDc)
{
virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
if (!virtualScreenDc)
{
return;
}
}
HDC dc = GetWindowDC(window->presentationWindow);
RECT rect = window->windowRect;
visibleRegion.offset(-rect.left, -rect.top);
SelectClipRgn(dc, visibleRegion);
CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, virtualScreenDc.get(),
rect.left - virtualScreenBounds.left, rect.top - virtualScreenBounds.top, SRCCOPY);
CALL_ORIG_FUNC(ReleaseDC)(window->presentationWindow, dc);
} }
} }
}
void Window::setPresentationWindow(HWND hwnd) void presentLayered(CompatRef<IDirectDrawSurface7> dst, POINT offset)
{
D3dDdi::ScopedCriticalSection lock;
if (m_isLayered)
{ {
SendNotifyMessage(hwnd, WM_DESTROYPRESENTATIONWINDOW, 0, 0); D3dDdi::ScopedCriticalSection lock;
}
else
{
m_presentationWindow = hwnd;
SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(m_hwnd));
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
void Window::update() HDC dstDc = nullptr;
{ for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it)
D3dDdi::ScopedCriticalSection lock;
const bool isLayered = CALL_ORIG_FUNC(GetWindowLongA)(m_hwnd, GWL_EXSTYLE) & WS_EX_LAYERED;
if (isLayered != m_isLayered)
{
if (!isLayered)
{ {
SendNotifyMessage(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, 0, reinterpret_cast<WPARAM>(m_hwnd)); auto& window = **it;
if (!window.isLayered)
{
continue;
}
if (!dstDc)
{
const UINT D3DDDIFMT_UNKNOWN = 0;
const UINT D3DDDIFMT_X8R8G8B8 = 22;
D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_X8R8G8B8);
dst->GetDC(&dst, &dstDc);
D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_UNKNOWN);
if (!dstDc)
{
return;
}
}
HDC windowDc = GetWindowDC(window.hwnd);
Gdi::Region rgn(window.visibleRegion);
RECT wr = window.windowRect;
if (0 != offset.x || 0 != offset.y)
{
OffsetRect(&wr, offset.x, offset.y);
rgn.offset(offset.x, offset.y);
}
SelectClipRgn(dstDc, rgn);
auto colorKey = window.colorKey;
if (CLR_INVALID != colorKey)
{
CALL_ORIG_FUNC(TransparentBlt)(dstDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, colorKey);
}
else
{
BLENDFUNCTION blend = {};
blend.SourceConstantAlpha = window.alpha;
CALL_ORIG_FUNC(AlphaBlend)(dstDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, blend);
}
CALL_ORIG_FUNC(ReleaseDC)(window.hwnd, windowDc);
} }
else if (m_presentationWindow)
if (dstDc)
{ {
SendNotifyMessage(m_presentationWindow, WM_DESTROYPRESENTATIONWINDOW, 0, 0); SelectClipRgn(dstDc, nullptr);
m_presentationWindow = nullptr; dst->ReleaseDC(&dst, dstDc);
} }
} }
m_isLayered = isLayered;
RECT newWindowRect = {}; void updateAll()
Region newVisibleRegion;
if (IsWindowVisible(m_hwnd) && !IsIconic(m_hwnd))
{ {
GetWindowRect(m_hwnd, &newWindowRect); LOG_FUNC("Window::updateAll");
if (!IsRectEmpty(&newWindowRect) && !m_isLayered) UpdateWindowContext context;
context.processId = GetCurrentProcessId();
context.virtualScreenRegion = VirtualScreen::getRegion();
std::vector<HWND> invalidatedWindows;
{ {
newVisibleRegion = m_hwnd; D3dDdi::ScopedCriticalSection lock;
g_windowZOrder.clear();
EnumWindows(updateWindow, reinterpret_cast<LPARAM>(&context));
for (auto it = g_windows.begin(); it != g_windows.end();)
{
if (g_windowZOrder.end() == std::find(g_windowZOrder.begin(), g_windowZOrder.end(), &it->second))
{
if (it->second.presentationWindow)
{
sendMessageBlocking(it->second.presentationWindow, WM_CLOSE, 0, 0);
}
it = g_windows.erase(it);
}
else
{
++it;
}
}
for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it)
{
auto& window = **it;
if (window.isRgnChangePending || !window.invalidatedRegion.isEmpty())
{
invalidatedWindows.push_back(window.hwnd);
}
}
} }
}
if (m_presentationWindow) for (auto hwnd : invalidatedWindows)
{
SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(m_hwnd));
}
std::swap(m_windowRect, newWindowRect);
swap(m_visibleRegion, newVisibleRegion);
calcInvalidatedRegion(newWindowRect, newVisibleRegion);
}
void Window::updateAll()
{
auto windows(getWindows());
for (auto& windowPair : windows)
{
windowPair.second->update();
}
for (auto& windowPair : windows)
{
if (!windowPair.second->m_invalidatedRegion.isEmpty())
{ {
POINT clientOrigin = {}; SendNotifyMessage(hwnd, WM_SYNCPAINT, 0, 0);
ClientToScreen(windowPair.first, &clientOrigin); }
windowPair.second->m_invalidatedRegion.offset(-clientOrigin.x, -clientOrigin.y); }
RedrawWindow(windowPair.first, nullptr, windowPair.second->m_invalidatedRegion,
RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASENOW); void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha)
{
D3dDdi::ScopedCriticalSection lock;
auto it = g_windows.find(hwnd);
if (it != g_windows.end())
{
it->second.colorKey = colorKey;
it->second.alpha = alpha;
if (!it->second.visibleRegion.isEmpty())
{
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
}
void uninstallHooks()
{
if (g_presentationWindowThread)
{
sendMessageBlocking(g_messageWindow, WM_CLOSE, 0, 0);
if (WAIT_OBJECT_0 != WaitForSingleObject(g_presentationWindowThread, 1000))
{
TerminateThread(g_presentationWindowThread, 0);
Compat::Log() << "The presentation window thread was terminated forcefully";
}
} }
} }
} }
void Window::updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha)
{
D3dDdi::ScopedCriticalSection lock;
auto window(get(hwnd));
if (window)
{
window->m_colorKey = colorKey;
window->m_alpha = alpha;
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
void Window::updateWindow()
{
RECT windowRect = {};
GetWindowRect(m_hwnd, &windowRect);
if (!EqualRect(&windowRect, &m_windowRect))
{
updateAll();
}
}
std::map<HWND, std::shared_ptr<Window>> Window::s_windows;
} }

View File

@ -1,57 +1,25 @@
#pragma once #pragma once
#include <map> #include <ddraw.h>
#include <memory>
#include <Windows.h> #include <Common/CompatRef.h>
#include <Gdi/Region.h>
#include "Gdi/Region.h"
namespace Gdi namespace Gdi
{ {
class Window namespace Window
{ {
public: bool isPresentationWindow(HWND hwnd);
Window(HWND hwnd); void onStyleChanged(HWND hwnd, WPARAM wParam);
Window(const Window&) = delete; void onSyncPaint(HWND hwnd);
Window& operator=(const Window&) = delete; void present(CompatRef<IDirectDrawSurface7> dst, CompatRef<IDirectDrawSurface7> src,
~Window(); CompatRef<IDirectDrawClipper> clipper);
void present(Gdi::Region excludeRegion);
void presentLayered(CompatRef<IDirectDrawSurface7> dst, POINT offset);
void updateAll();
void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha);
BYTE getAlpha() const; void installHooks();
COLORREF getColorKey() const; void uninstallHooks();
HWND getPresentationWindow() const; }
Region getVisibleRegion() const;
RECT getWindowRect() const;
bool isLayered() const;
void setPresentationWindow(HWND hwnd);
void updateWindow();
static bool add(HWND hwnd);
static std::shared_ptr<Window> get(HWND hwnd);
static void remove(HWND hwnd);
static std::map<HWND, std::shared_ptr<Window>> getWindows();
static bool isPresentationWindow(HWND hwnd);
static bool isTopLevelWindow(HWND hwnd);
static void updateAll();
static void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha);
static void installHooks();
static void uninstallHooks();
private:
void calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion);
void update();
HWND m_hwnd;
HWND m_presentationWindow;
RECT m_windowRect;
Region m_visibleRegion;
Region m_invalidatedRegion;
COLORREF m_colorKey;
BYTE m_alpha;
bool m_isLayered;
static std::map<HWND, std::shared_ptr<Window>> s_windows;
};
} }

View File

@ -339,7 +339,7 @@ std::ostream& operator<<(std::ostream& os, const NMHDR& nm)
<< Compat::hex(nm.code); << Compat::hex(nm.code);
} }
std::ostream& operator<<(std::ostream& os, const POINTS& p) std::ostream& operator<<(std::ostream& os, const POINT& p)
{ {
return Compat::LogStruct(os) return Compat::LogStruct(os)
<< p.x << p.x

View File

@ -32,7 +32,7 @@ std::ostream& operator<<(std::ostream& os, const MINMAXINFO& mmi);
std::ostream& operator<<(std::ostream& os, const MSG& msg); std::ostream& operator<<(std::ostream& os, const MSG& msg);
std::ostream& operator<<(std::ostream& os, const NCCALCSIZE_PARAMS& nccs); std::ostream& operator<<(std::ostream& os, const NCCALCSIZE_PARAMS& nccs);
std::ostream& operator<<(std::ostream& os, const NMHDR& nm); std::ostream& operator<<(std::ostream& os, const NMHDR& nm);
std::ostream& operator<<(std::ostream& os, const POINTS& p); std::ostream& operator<<(std::ostream& os, const POINT& p);
std::ostream& operator<<(std::ostream& os, const RECT& rect); std::ostream& operator<<(std::ostream& os, const RECT& rect);
std::ostream& operator<<(std::ostream& os, const SIZE& size); std::ostream& operator<<(std::ostream& os, const SIZE& size);
std::ostream& operator<<(std::ostream& os, const STYLESTRUCT& ss); std::ostream& operator<<(std::ostream& os, const STYLESTRUCT& ss);