From c38416724471204bbb542a66167ce321e274c4a5 Mon Sep 17 00:00:00 2001 From: narzoul Date: Sat, 17 Mar 2018 21:40:00 +0100 Subject: [PATCH] Removed layered windows from DirectDraw primary surface --- DDrawCompat/Common/Log.cpp | 21 +++++- DDrawCompat/Common/Log.h | 1 + DDrawCompat/DDraw/RealPrimarySurface.cpp | 59 ++++++++++++++++- DDrawCompat/Gdi/Caret.cpp | 4 +- DDrawCompat/Gdi/DcFunctions.cpp | 84 +++++++++++------------- DDrawCompat/Gdi/DcFunctions.h | 5 ++ DDrawCompat/Gdi/Gdi.cpp | 18 +++++ DDrawCompat/Gdi/Gdi.h | 3 + DDrawCompat/Gdi/WinProc.cpp | 13 ++-- 9 files changed, 150 insertions(+), 58 deletions(-) diff --git a/DDrawCompat/Common/Log.cpp b/DDrawCompat/Common/Log.cpp index e113b80..1439c19 100644 --- a/DDrawCompat/Common/Log.cpp +++ b/DDrawCompat/Common/Log.cpp @@ -1,5 +1,7 @@ #define WIN32_LEAN_AND_MEAN +#include + #include #include @@ -26,7 +28,7 @@ std::ostream& operator<<(std::ostream& os, const char* str) return os << "null"; } - if (!Compat::Log::isPointerDereferencingAllowed()) + if (!Compat::Log::isPointerDereferencingAllowed() || reinterpret_cast(str) <= 0xFFFF) { return os << static_cast(str); } @@ -46,7 +48,7 @@ std::ostream& operator<<(std::ostream& os, const WCHAR* wstr) return os << "null"; } - if (!Compat::Log::isPointerDereferencingAllowed()) + if (!Compat::Log::isPointerDereferencingAllowed() || reinterpret_cast(wstr) <= 0xFFFF) { return os << static_cast(wstr); } @@ -79,6 +81,21 @@ std::ostream& operator<<(std::ostream& os, HDC__& dc) return os << "DC(" << static_cast(&dc) << ',' << WindowFromDC(&dc) << ')'; } +std::ostream& operator<<(std::ostream& os, HRGN__& rgn) +{ + DWORD size = GetRegionData(&rgn, 0, nullptr); + if (0 == size) + { + return os << "RGN[]"; + } + + std::vector rgnDataBuf(size); + auto& rgnData = *reinterpret_cast(rgnDataBuf.data()); + GetRegionData(&rgn, size, &rgnData); + + return os << "RGN" << Compat::array(reinterpret_cast(rgnData.Buffer), rgnData.rdh.nCount); +} + std::ostream& operator<<(std::ostream& os, HWND__& hwnd) { char name[256] = {}; diff --git a/DDrawCompat/Common/Log.h b/DDrawCompat/Common/Log.h index 259831a..ac3260d 100644 --- a/DDrawCompat/Common/Log.h +++ b/DDrawCompat/Common/Log.h @@ -22,6 +22,7 @@ std::ostream& operator<<(std::ostream& os, const DEVMODEA& dm); std::ostream& operator<<(std::ostream& os, const DEVMODEW& dm); std::ostream& operator<<(std::ostream& os, const RECT& rect); std::ostream& operator<<(std::ostream& os, HDC__& dc); +std::ostream& operator<<(std::ostream& os, HRGN__& rgn); std::ostream& operator<<(std::ostream& os, HWND__& hwnd); std::ostream& operator<<(std::ostream& os, const DDSCAPS& caps); std::ostream& operator<<(std::ostream& os, const DDSCAPS2& caps); diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index ded4686..f76cd95 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -1,4 +1,5 @@ #include +#include #include "Common/CompatPtr.h" #include "Common/Hook.h" @@ -20,7 +21,6 @@ namespace void onRelease(); DWORD WINAPI updateThreadProc(LPVOID lpParameter); - DWORD g_primaryThreadId = 0; CompatWeakPtr g_frontBuffer; CompatWeakPtr g_backBuffer; CompatWeakPtr g_paletteConverter; @@ -40,14 +40,63 @@ namespace std::atomic g_isFullScreen(false); + BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam) + { + if (IsWindowVisible(hwnd) && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) + { + auto& visibleLayeredWindows = *reinterpret_cast*>(lParam); + visibleLayeredWindows.push_back(hwnd); + } + return TRUE; + } + BOOL CALLBACK bltToWindow(HWND hwnd, LPARAM lParam) { + if (!IsWindowVisible(hwnd) || (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) + { + return TRUE; + } + g_clipper->SetHWnd(g_clipper, 0, hwnd); auto src = reinterpret_cast(lParam); g_frontBuffer->Blt(g_frontBuffer, nullptr, src, nullptr, DDBLT_WAIT, nullptr); return TRUE; } + void bltVisibleLayeredWindowsToBackBuffer() + { + std::vector visibleLayeredWindows; + EnumThreadWindows(Gdi::getGdiThreadId(), addVisibleLayeredWindowToVector, + reinterpret_cast(&visibleLayeredWindows)); + + if (visibleLayeredWindows.empty()) + { + return; + } + + HDC backBufferDc = nullptr; + g_backBuffer->GetDC(g_backBuffer, &backBufferDc); + + for (auto it = visibleLayeredWindows.rbegin(); it != visibleLayeredWindows.rend(); ++it) + { + HDC windowDc = GetWindowDC(*it); + HRGN rgn = Gdi::getVisibleWindowRgn(*it); + RECT wr = {}; + GetWindowRect(*it, &wr); + + SelectClipRgn(backBufferDc, rgn); + CALL_ORIG_FUNC(BitBlt)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, + windowDc, 0, 0, SRCCOPY); + SelectClipRgn(backBufferDc, nullptr); + + DeleteObject(rgn); + ReleaseDC(*it, windowDc); + } + + g_backBuffer->ReleaseDC(g_backBuffer, backBufferDc); + DDraw::RealPrimarySurface::update(); + } + HRESULT bltToPrimaryChain(CompatRef src) { if (g_isFullScreen) @@ -55,7 +104,7 @@ namespace return g_backBuffer->Blt(g_backBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr); } - EnumThreadWindows(g_primaryThreadId, bltToWindow, reinterpret_cast(&src)); + EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindow, reinterpret_cast(&src)); return DD_OK; } @@ -92,6 +141,11 @@ namespace result = SUCCEEDED(bltToPrimaryChain(*primary)); } + if (result && g_isFullScreen && primary == DDraw::PrimarySurface::getGdiSurface()) + { + bltVisibleLayeredWindowsToBackBuffer(); + } + Compat::LogLeave("RealPrimarySurface::compatBlt") << result; return result; } @@ -180,7 +234,6 @@ namespace g_backBuffer = backBuffer; g_surfaceDesc = desc; g_isFullScreen = isFlippable; - g_primaryThreadId = GetCurrentThreadId(); return DD_OK; } diff --git a/DDrawCompat/Gdi/Caret.cpp b/DDrawCompat/Gdi/Caret.cpp index 82f61d9..8d05094 100644 --- a/DDrawCompat/Gdi/Caret.cpp +++ b/DDrawCompat/Gdi/Caret.cpp @@ -75,7 +75,7 @@ namespace { GUITHREADINFO gti = {}; gti.cbSize = sizeof(gti); - GetGUIThreadInfo(GetCurrentThreadId(), >i); + GetGUIThreadInfo(Gdi::getGdiThreadId(), >i); CaretData caretData = {}; caretData.hwnd = gti.hwndCaret; @@ -132,7 +132,7 @@ namespace Gdi HOOK_GDI_CARET_FUNCTION(user32, SetCaretPos); HOOK_GDI_CARET_FUNCTION(user32, ShowCaret); - const DWORD threadId = GetCurrentThreadId(); + const DWORD threadId = Gdi::getGdiThreadId(); g_compatGdiCaretGeneralEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_HIDE, nullptr, &compatGdiCaretEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); g_compatGdiCaretLocationChangeEventHook = SetWinEventHook( diff --git a/DDrawCompat/Gdi/DcFunctions.cpp b/DDrawCompat/Gdi/DcFunctions.cpp index e4cb20a..58ffe50 100644 --- a/DDrawCompat/Gdi/DcFunctions.cpp +++ b/DDrawCompat/Gdi/DcFunctions.cpp @@ -9,7 +9,7 @@ namespace { - struct ExcludeRectContext + struct ExcludeRgnForOverlappingWindowArgs { HRGN rgn; HWND rootWnd; @@ -20,6 +20,8 @@ namespace template DWORD getDdLockFlags(Params... params); + HRGN getWindowRegion(HWND hwnd); + BOOL WINAPI GdiDrawStream(HDC, DWORD, DWORD) { return FALSE; } BOOL WINAPI PolyPatBlt(HDC, DWORD, DWORD, DWORD, DWORD) { return FALSE; } @@ -28,7 +30,8 @@ namespace bool hasDisplayDcArg(HDC dc) { - return dc && OBJ_DC == GetObjectType(dc) && DT_RASDISPLAY == GetDeviceCaps(dc, TECHNOLOGY); + return dc && OBJ_DC == GetObjectType(dc) && DT_RASDISPLAY == GetDeviceCaps(dc, TECHNOLOGY) && + !(GetWindowLongPtr(CALL_ORIG_FUNC(WindowFromDC)(dc), GWL_EXSTYLE) & WS_EX_LAYERED); } template @@ -112,48 +115,22 @@ namespace return result; } - void enumTopLevelThreadWindows(WNDENUMPROC enumFunc, LPARAM lParam) + BOOL CALLBACK excludeRgnForOverlappingWindow(HWND hwnd, LPARAM lParam) { - const DWORD currentThreadId = GetCurrentThreadId(); - const char* MENU_ATOM = reinterpret_cast(0x8000); - HWND menuWindow = FindWindow(MENU_ATOM, nullptr); - BOOL cont = TRUE; - while (menuWindow && cont) - { - if (currentThreadId == GetWindowThreadProcessId(menuWindow, nullptr)) - { - cont = enumFunc(menuWindow, lParam); - } - if (cont) - { - menuWindow = FindWindowEx(nullptr, menuWindow, MENU_ATOM, nullptr); - } - } - - if (cont) - { - EnumThreadWindows(currentThreadId, enumFunc, lParam); - } - } - - BOOL CALLBACK excludeRectForOverlappingWindow(HWND hwnd, LPARAM lParam) - { - auto excludeRectContext = reinterpret_cast(lParam); - if (hwnd == excludeRectContext->rootWnd) + auto& args = *reinterpret_cast(lParam); + if (hwnd == args.rootWnd) { return FALSE; } - if (!IsWindowVisible(hwnd) || (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) + if (!IsWindowVisible(hwnd) || + (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & (WS_EX_LAYERED | WS_EX_TRANSPARENT))) { return TRUE; } - RECT windowRect = {}; - GetWindowRect(hwnd, &windowRect); - - HRGN windowRgn = CreateRectRgnIndirect(&windowRect); - CombineRgn(excludeRectContext->rgn, excludeRectContext->rgn, windowRgn, RGN_DIFF); + HRGN windowRgn = getWindowRegion(hwnd); + CombineRgn(args.rgn, args.rgn, windowRgn, RGN_DIFF); DeleteObject(windowRgn); return TRUE; @@ -245,6 +222,18 @@ namespace return getDdLockFlagsBlt(hdcDest, hdcSrc); } + 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 void hookGdiDcFunction(const char* moduleName, const char* funcName) { @@ -265,22 +254,14 @@ namespace } HWND hwnd = WindowFromDC(hdc); - if (!hwnd) + if (!hwnd || (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) { return 1; } - if ((GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) && - !GetLayeredWindowAttributes(hwnd, nullptr, nullptr, nullptr)) - { - RECT rect = {}; - GetWindowRect(hwnd, &rect); - SetRectRgn(hrgn, rect.left, rect.top, rect.right, rect.bottom); - } - - ExcludeRectContext excludeRectContext = { hrgn, GetAncestor(hwnd, GA_ROOT) }; - enumTopLevelThreadWindows(excludeRectForOverlappingWindow, - reinterpret_cast(&excludeRectContext)); + ExcludeRgnForOverlappingWindowArgs args = { hrgn, GetAncestor(hwnd, GA_ROOT) }; + EnumThreadWindows(Gdi::getGdiThreadId(), excludeRgnForOverlappingWindow, + reinterpret_cast(&args)); return 1; } @@ -302,6 +283,15 @@ namespace Gdi { namespace DcFunctions { + HRGN getVisibleWindowRgn(HWND hwnd) + { + HRGN rgn = getWindowRegion(hwnd); + ExcludeRgnForOverlappingWindowArgs args = { rgn, hwnd }; + EnumThreadWindows(Gdi::getGdiThreadId(), excludeRgnForOverlappingWindow, + reinterpret_cast(&args)); + return rgn; + } + void installHooks() { // Bitmap functions diff --git a/DDrawCompat/Gdi/DcFunctions.h b/DDrawCompat/Gdi/DcFunctions.h index 3ae80af..a4efb18 100644 --- a/DDrawCompat/Gdi/DcFunctions.h +++ b/DDrawCompat/Gdi/DcFunctions.h @@ -1,9 +1,14 @@ #pragma once +#define WIN32_LEAN_AND_MEAN + +#include + namespace Gdi { namespace DcFunctions { + HRGN getVisibleWindowRgn(HWND hwnd); void installHooks(); } } diff --git a/DDrawCompat/Gdi/Gdi.cpp b/DDrawCompat/Gdi/Gdi.cpp index d079a1a..2a9cf4a 100644 --- a/DDrawCompat/Gdi/Gdi.cpp +++ b/DDrawCompat/Gdi/Gdi.cpp @@ -15,6 +15,7 @@ namespace { std::atomic g_disableEmulationCount = 0; + DWORD g_gdiThreadId = 0; DWORD g_renderingRefCount = 0; DWORD g_ddLockFlags = 0; DWORD g_ddLockThreadRenderingRefCount = 0; @@ -159,6 +160,16 @@ namespace Gdi --g_disableEmulationCount; } + DWORD getGdiThreadId() + { + return g_gdiThreadId; + } + + HRGN getVisibleWindowRgn(HWND hwnd) + { + return DcFunctions::getVisibleWindowRgn(hwnd); + } + void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc) { HWND hwnd = CreateWindow(className, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, 0); @@ -169,6 +180,7 @@ namespace Gdi void installHooks() { + g_gdiThreadId = GetCurrentThreadId(); InitializeCriticalSection(&g_gdiCriticalSection); if (Gdi::DcCache::init()) { @@ -188,6 +200,12 @@ namespace Gdi } } + bool isTopLevelWindow(HWND hwnd) + { + return !(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || + GetParent(hwnd) == GetDesktopWindow(); + } + void redraw(HRGN rgn) { if (isEmulationEnabled()) diff --git a/DDrawCompat/Gdi/Gdi.h b/DDrawCompat/Gdi/Gdi.h index a042d20..d9e3211 100644 --- a/DDrawCompat/Gdi/Gdi.h +++ b/DDrawCompat/Gdi/Gdi.h @@ -14,9 +14,12 @@ namespace Gdi void disableEmulation(); void enableEmulation(); + DWORD getGdiThreadId(); + HRGN getVisibleWindowRgn(HWND hwnd); void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc); void installHooks(); bool isEmulationEnabled(); + bool isTopLevelWindow(HWND hwnd); void redraw(HRGN rgn); void redrawWindow(HWND hwnd, HRGN rgn); void unhookWndProc(LPCSTR className, WNDPROC oldWndProc); diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index e4a83b1..0aa0179 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -94,11 +94,10 @@ namespace WindowData getWindowData(HWND hwnd) { WindowData data; - if (IsWindowVisible(hwnd)) + if (IsWindowVisible(hwnd) && !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) { GetWindowRect(hwnd, &data.wndRect); data.sysClipRgn.reset(CreateRectRgnIndirect(&data.wndRect), DeleteObject); - HDC dc = GetWindowDC(hwnd); GetRandomRgn(dc, data.sysClipRgn.get(), SYSRGN); ReleaseDC(hwnd, dc); @@ -186,7 +185,13 @@ namespace void onWindowPosChanged(HWND hwnd) { - if (GetAncestor(hwnd, GA_ROOT) != hwnd) + const ATOM menuAtom = 0x8000; + if (menuAtom == GetClassLongPtr(hwnd, GCW_ATOM)) + { + SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED); + } + + if (!Gdi::isTopLevelWindow(hwnd)) { return; } @@ -272,7 +277,7 @@ namespace Gdi { void installHooks() { - const DWORD threadId = GetCurrentThreadId(); + const DWORD threadId = Gdi::getGdiThreadId(); g_callWndRetProcHook = SetWindowsHookEx(WH_CALLWNDPROCRET, callWndRetProc, nullptr, threadId); g_objectStateChangeEventHook = SetWinEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE, nullptr, &objectStateChangeEvent, 0, threadId, WINEVENT_OUTOFCONTEXT);