diff --git a/DDrawCompat/CompatDirectDrawSurface.cpp b/DDrawCompat/CompatDirectDrawSurface.cpp index b6dd4f0..c44a9a9 100644 --- a/DDrawCompat/CompatDirectDrawSurface.cpp +++ b/DDrawCompat/CompatDirectDrawSurface.cpp @@ -4,7 +4,7 @@ #include "CompatDirectDraw.h" #include "CompatDirectDrawSurface.h" -#include "CompatGdiSurface.h" +#include "CompatGdi.h" #include "CompatPrimarySurface.h" #include "DDrawProcs.h" #include "IReleaseNotifier.h" @@ -603,7 +603,7 @@ HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::Restore(TSurface* T result = RealPrimarySurface::restore(); if (wasLost) { - CompatGdiSurface::release(); + CompatGdi::releaseSurfaceMemory(); updateSurfaceParams(); } } @@ -691,7 +691,7 @@ void CompatDirectDrawSurface::updateSurfaceParams() if (SUCCEEDED(s_origVtable.Lock(s_compatPrimarySurface, nullptr, &desc, DDLOCK_WAIT, nullptr))) { s_origVtable.Unlock(s_compatPrimarySurface, nullptr); - CompatGdiSurface::setSurfaceMemory(desc.lpSurface, desc.lPitch); + CompatGdi::setSurfaceMemory(desc.lpSurface, desc.lPitch); } g_lockingPrimary = false; } diff --git a/DDrawCompat/CompatGdi.cpp b/DDrawCompat/CompatGdi.cpp new file mode 100644 index 0000000..b8874a2 --- /dev/null +++ b/DDrawCompat/CompatGdi.cpp @@ -0,0 +1,47 @@ +#include "CompatGdi.h" +#include "CompatGdiDcCache.h" +#include "CompatGdiFunctions.h" + +namespace +{ + CRITICAL_SECTION g_gdiCriticalSection; +} + +namespace CompatGdi +{ + GdiScopedThreadLock::GdiScopedThreadLock() + { + EnterCriticalSection(&g_gdiCriticalSection); + } + + GdiScopedThreadLock::~GdiScopedThreadLock() + { + LeaveCriticalSection(&g_gdiCriticalSection); + } + + void installHooks() + { + InitializeCriticalSection(&g_gdiCriticalSection); + if (CompatGdiDcCache::init()) + { + CompatGdiFunctions::hookGdiFunctions(); + } + } + + void releaseSurfaceMemory() + { + GdiScopedThreadLock gdiLock; + CompatGdiDcCache::release(); + } + + void setSurfaceMemory(void* surfaceMemory, int pitch) + { + GdiScopedThreadLock gdiLock; + const bool wasReleased = CompatGdiDcCache::isReleased(); + CompatGdiDcCache::setSurfaceMemory(surfaceMemory, pitch); + if (wasReleased) + { + InvalidateRect(nullptr, nullptr, TRUE); + } + } +} diff --git a/DDrawCompat/CompatGdi.h b/DDrawCompat/CompatGdi.h new file mode 100644 index 0000000..dfb2829 --- /dev/null +++ b/DDrawCompat/CompatGdi.h @@ -0,0 +1,15 @@ +#pragma once + +namespace CompatGdi +{ + class GdiScopedThreadLock + { + public: + GdiScopedThreadLock(); + ~GdiScopedThreadLock(); + }; + + void installHooks(); + void releaseSurfaceMemory(); + void setSurfaceMemory(void* surfaceMemory, int pitch); +}; diff --git a/DDrawCompat/CompatGdiDc.cpp b/DDrawCompat/CompatGdiDc.cpp new file mode 100644 index 0000000..59347b2 --- /dev/null +++ b/DDrawCompat/CompatGdiDc.cpp @@ -0,0 +1,74 @@ +#include "CompatGdiDc.h" +#include "CompatGdiDcCache.h" +#include "RealPrimarySurface.h" + +namespace +{ + struct ExcludeClipRectsData + { + HDC compatDc; + POINT origin; + HWND rootWnd; + }; + + BOOL CALLBACK excludeClipRectsForOverlappingWindows(HWND hwnd, LPARAM lParam) + { + auto excludeClipRectsData = reinterpret_cast(lParam); + if (hwnd == excludeClipRectsData->rootWnd) + { + return FALSE; + } + + RECT rect = {}; + GetWindowRect(hwnd, &rect); + OffsetRect(&rect, -excludeClipRectsData->origin.x, -excludeClipRectsData->origin.y); + ExcludeClipRect(excludeClipRectsData->compatDc, rect.left, rect.top, rect.right, rect.bottom); + return TRUE; + } +} + +namespace CompatGdiDc +{ + CachedDc getDc(HDC origDc) + { + CachedDc cachedDc = {}; + if (!origDc || !RealPrimarySurface::isFullScreen() || + OBJ_DC != GetObjectType(origDc) || + DT_RASDISPLAY != GetDeviceCaps(origDc, TECHNOLOGY)) + { + return cachedDc; + } + + cachedDc = CompatGdiDcCache::getDc(); + if (!cachedDc.dc) + { + return cachedDc; + } + + HWND hwnd = WindowFromDC(origDc); + if (hwnd) + { + POINT origin = {}; + GetDCOrgEx(origDc, &origin); + SetWindowOrgEx(cachedDc.dc, -origin.x, -origin.y, nullptr); + + HRGN clipRgn = CreateRectRgn(0, 0, 0, 0); + GetRandomRgn(origDc, clipRgn, SYSRGN); + SelectClipRgn(cachedDc.dc, clipRgn); + RECT r = {}; + GetRgnBox(clipRgn, &r); + DeleteObject(clipRgn); + + ExcludeClipRectsData excludeClipRectsData = { cachedDc.dc, origin, GetAncestor(hwnd, GA_ROOT) }; + EnumThreadWindows(GetCurrentThreadId(), &excludeClipRectsForOverlappingWindows, + reinterpret_cast(&excludeClipRectsData)); + } + + return cachedDc; + } + + void releaseDc(const CachedDc& cachedDc) + { + CompatGdiDcCache::releaseDc(cachedDc); + } +} diff --git a/DDrawCompat/CompatGdiDc.h b/DDrawCompat/CompatGdiDc.h new file mode 100644 index 0000000..f5ff301 --- /dev/null +++ b/DDrawCompat/CompatGdiDc.h @@ -0,0 +1,15 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include + +#include "CompatGdiDcCache.h" + +namespace CompatGdiDc +{ + using CompatGdiDcCache::CachedDc; + + CachedDc getDc(HDC origDc); + void releaseDc(const CachedDc& cachedDc); +} diff --git a/DDrawCompat/CompatGdiDcCache.cpp b/DDrawCompat/CompatGdiDcCache.cpp index fa8516d..d3f8bfd 100644 --- a/DDrawCompat/CompatGdiDcCache.cpp +++ b/DDrawCompat/CompatGdiDcCache.cpp @@ -22,27 +22,26 @@ namespace CompatGdiDcCache namespace { using CompatGdiDcCache::SurfaceMemoryDesc; - using CompatGdiDcCache::CompatDc; + using CompatGdiDcCache::CachedDc; - std::map> g_compatDcCaches; - std::vector* g_currentCompatDcCache = nullptr; + std::map> g_caches; + std::vector* g_currentCache = nullptr; - DWORD g_cacheId = 0; IDirectDraw7* g_directDraw = nullptr; void* g_surfaceMemory = nullptr; LONG g_pitch = 0; IDirectDrawSurface7* createGdiSurface(); - void releaseCompatDc(CompatDc compatDc); - void releaseCompatDcCache(std::vector& compatDcCache); + void releaseCachedDc(CachedDc cachedDc); + void releaseCache(std::vector& cache); void clearAllCaches() { - for (auto& compatDcCache : g_compatDcCaches) + for (auto& cache : g_caches) { - releaseCompatDcCache(compatDcCache.second); + releaseCache(cache.second); } - g_compatDcCaches.clear(); + g_caches.clear(); } IDirectDraw7* createDirectDraw() @@ -65,14 +64,14 @@ namespace return dd; } - CompatDc createCompatDc() + CachedDc createCachedDc() { - CompatDc compatDc = {}; + CachedDc cachedDc = {}; IDirectDrawSurface7* surface = createGdiSurface(); if (!surface) { - return compatDc; + return cachedDc; } HDC dc = nullptr; @@ -81,18 +80,17 @@ namespace { LOG_ONCE("Failed to create a GDI DC: " << result); surface->lpVtbl->Release(surface); - return compatDc; + return cachedDc; } // Release DD critical section acquired by IDirectDrawSurface7::GetDC to avoid deadlocks Compat::origProcs.ReleaseDDThreadLock(); - compatDc.cacheId = g_cacheId; - compatDc.surfaceMemoryDesc.surfaceMemory = g_surfaceMemory; - compatDc.surfaceMemoryDesc.pitch = g_pitch; - compatDc.dc = dc; - compatDc.surface = surface; - return compatDc; + cachedDc.surfaceMemoryDesc.surfaceMemory = g_surfaceMemory; + cachedDc.surfaceMemoryDesc.pitch = g_pitch; + cachedDc.dc = dc; + cachedDc.surface = surface; + return cachedDc; } IDirectDrawSurface7* createGdiSurface() @@ -131,66 +129,65 @@ namespace { for (DWORD i = 0; i < Config::gdiDcCacheSize; ++i) { - CompatDc compatDc = createCompatDc(); - if (!compatDc.dc) + CachedDc cachedDc = createCachedDc(); + if (!cachedDc.dc) { return; } - g_currentCompatDcCache->push_back(compatDc); + g_currentCache->push_back(cachedDc); } } - void releaseCompatDc(CompatDc compatDc) + void releaseCachedDc(CachedDc cachedDc) { // Reacquire DD critical section that was temporarily released after IDirectDrawSurface7::GetDC Compat::origProcs.AcquireDDThreadLock(); if (FAILED(CompatDirectDrawSurface::s_origVtable.ReleaseDC( - compatDc.surface, compatDc.dc))) + cachedDc.surface, cachedDc.dc))) { LOG_ONCE("Failed to release a cached DC"); Compat::origProcs.ReleaseDDThreadLock(); } - CompatDirectDrawSurface::s_origVtable.Release(compatDc.surface); + CompatDirectDrawSurface::s_origVtable.Release(cachedDc.surface); } - void releaseCompatDcCache(std::vector& compatDcCache) + void releaseCache(std::vector& cache) { - for (auto& compatDc : compatDcCache) + for (auto& cachedDc : cache) { - releaseCompatDc(compatDc); + releaseCachedDc(cachedDc); } } } namespace CompatGdiDcCache { - CompatDc getDc() + CachedDc getDc() { - CompatDc compatDc = {}; - if (!g_currentCompatDcCache) + CachedDc cachedDc = {}; + if (!g_currentCache) { - return compatDc; + return cachedDc; } - if (g_currentCompatDcCache->empty()) + if (g_currentCache->empty()) { LOG_ONCE("Warning: GDI DC cache size is insufficient"); - compatDc = createCompatDc(); - if (!compatDc.dc) + cachedDc = createCachedDc(); + if (!cachedDc.dc) { - return compatDc; + return cachedDc; } } else { - compatDc = g_currentCompatDcCache->back(); - g_currentCompatDcCache->pop_back(); + cachedDc = g_currentCache->back(); + g_currentCache->pop_back(); } - compatDc.dcState = SaveDC(compatDc.dc); - return compatDc; + return cachedDc; } bool init() @@ -201,31 +198,21 @@ namespace CompatGdiDcCache bool isReleased() { - return g_compatDcCaches.empty(); + return g_caches.empty(); } void release() { - if (g_currentCompatDcCache) + if (g_currentCache) { - g_currentCompatDcCache = nullptr; + g_currentCache = nullptr; clearAllCaches(); - ++g_cacheId; } } - void returnDc(const CompatDc& compatDc) + void releaseDc(const CachedDc& cachedDc) { - RestoreDC(compatDc.dc, compatDc.dcState); - - if (compatDc.cacheId != g_cacheId) - { - releaseCompatDc(compatDc); - } - else - { - g_compatDcCaches[compatDc.surfaceMemoryDesc].push_back(compatDc); - } + g_caches[cachedDc.surfaceMemoryDesc].push_back(cachedDc); } void setSurfaceMemory(void* surfaceMemory, LONG pitch) @@ -235,20 +222,20 @@ namespace CompatGdiDcCache if (!surfaceMemory) { - g_currentCompatDcCache = nullptr; + g_currentCache = nullptr; return; } SurfaceMemoryDesc surfaceMemoryDesc = { surfaceMemory, pitch }; - auto it = g_compatDcCaches.find(surfaceMemoryDesc); - if (it == g_compatDcCaches.end()) + auto it = g_caches.find(surfaceMemoryDesc); + if (it == g_caches.end()) { - g_currentCompatDcCache = &g_compatDcCaches[surfaceMemoryDesc]; + g_currentCache = &g_caches[surfaceMemoryDesc]; fillCurrentCache(); } else { - g_currentCompatDcCache = &it->second; + g_currentCache = &it->second; } } } diff --git a/DDrawCompat/CompatGdiDcCache.h b/DDrawCompat/CompatGdiDcCache.h index 12ca0a3..701592d 100644 --- a/DDrawCompat/CompatGdiDcCache.h +++ b/DDrawCompat/CompatGdiDcCache.h @@ -14,20 +14,17 @@ namespace CompatGdiDcCache LONG pitch; }; - struct CompatDc + struct CachedDc { - DWORD cacheId; SurfaceMemoryDesc surfaceMemoryDesc; IDirectDrawSurface7* surface; - HDC origDc; HDC dc; - int dcState; }; - CompatDc getDc(); + CachedDc getDc(); bool init(); bool isReleased(); void release(); - void returnDc(const CompatDc& compatDc); + void releaseDc(const CachedDc& cachedDc); void setSurfaceMemory(void* surfaceMemory, LONG pitch); } diff --git a/DDrawCompat/CompatGdiFunctions.cpp b/DDrawCompat/CompatGdiFunctions.cpp new file mode 100644 index 0000000..4a76310 --- /dev/null +++ b/DDrawCompat/CompatGdiFunctions.cpp @@ -0,0 +1,186 @@ +#include +#include +#include + +#include "CompatDirectDrawSurface.h" +#include "CompatGdi.h" +#include "CompatGdiDc.h" +#include "CompatGdiFunctions.h" +#include "CompatPrimarySurface.h" +#include "DDrawLog.h" +#include "DDrawScopedThreadLock.h" +#include "RealPrimarySurface.h" + +#include + +namespace +{ + using CompatGdiDc::CachedDc; + + struct CompatDc : CachedDc + { + CompatDc(const CachedDc& cachedDc) : CachedDc(cachedDc) {} + HGDIOBJ origFont; + HGDIOBJ origBrush; + HGDIOBJ origPen; + }; + + std::unordered_map g_funcNames; + std::vector g_usedCompatDcs; + DWORD* g_usedCompatDcCount = nullptr; + + template + using FuncPtr = Result(WINAPI *)(Params...); + + template + OrigFuncPtr& getOrigFuncPtr() + { + static OrigFuncPtr origFuncPtr = origFunc; + return origFuncPtr; + } + + template + T replaceDc(T t) + { + return t; + } + + HDC replaceDc(HDC dc) + { + auto it = std::find_if(g_usedCompatDcs.begin(), g_usedCompatDcs.end(), + [dc](const CompatDc& compatDc) { return compatDc.dc == dc; }); + if (it != g_usedCompatDcs.end()) + { + return it->dc; + } + + CompatDc compatDc = CompatGdiDc::getDc(dc); + if (!compatDc.dc) + { + return dc; + } + + compatDc.origFont = SelectObject(compatDc.dc, GetCurrentObject(dc, OBJ_FONT)); + compatDc.origBrush = SelectObject(compatDc.dc, GetCurrentObject(dc, OBJ_BRUSH)); + compatDc.origPen = SelectObject(compatDc.dc, GetCurrentObject(dc, OBJ_PEN)); + SetTextColor(compatDc.dc, GetTextColor(dc)); + SetBkColor(compatDc.dc, GetBkColor(dc)); + SetBkMode(compatDc.dc, GetBkMode(dc)); + + g_usedCompatDcs.push_back(compatDc); + ++*g_usedCompatDcCount; + return compatDc.dc; + } + + template + Result WINAPI compatGdiFunc(Params... params) + { + CompatGdi::GdiScopedThreadLock gdiLock; + Compat::DDrawScopedThreadLock ddLock; + + DWORD usedCompatDcCount = 0; + g_usedCompatDcCount = &usedCompatDcCount; + + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + if (FAILED(CompatDirectDrawSurface::s_origVtable.Lock( + CompatPrimarySurface::surface, nullptr, &desc, DDLOCK_WAIT, nullptr))) + { + return getOrigFuncPtr()(params...); + } + + Result result = getOrigFuncPtr()(replaceDc(params)...); + GdiFlush(); + + CompatDirectDrawSurface::s_origVtable.Unlock( + CompatPrimarySurface::surface, nullptr); + + if (0 != usedCompatDcCount) + { + RealPrimarySurface::update(); + } + + for (DWORD i = 0; i < usedCompatDcCount; ++i) + { + CompatDc& compatDc = g_usedCompatDcs.back(); + SelectObject(compatDc.dc, compatDc.origFont); + SelectObject(compatDc.dc, compatDc.origBrush); + SelectObject(compatDc.dc, compatDc.origPen); + + CompatGdiDc::releaseDc(compatDc); + g_usedCompatDcs.pop_back(); + } + + return result; + } + + template + OrigFuncPtr getCompatGdiFuncPtr(FuncPtr&) + { + return &compatGdiFunc; + } + + FARPROC getProcAddress(HMODULE module, const char* procName) + { + if (!module || !procName) + { + return nullptr; + } + + PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(module); + if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic) { + return nullptr; + } + char* moduleBase = reinterpret_cast(module); + + PIMAGE_NT_HEADERS ntHeader = reinterpret_cast( + reinterpret_cast(dosHeader) + dosHeader->e_lfanew); + if (IMAGE_NT_SIGNATURE != ntHeader->Signature) + { + return nullptr; + } + + PIMAGE_EXPORT_DIRECTORY exportDir = reinterpret_cast( + moduleBase + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + DWORD* rvaOfNames = reinterpret_cast(moduleBase + exportDir->AddressOfNames); + + for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) + { + if (0 == strcmp(procName, moduleBase + rvaOfNames[i])) + { + WORD* nameOrds = reinterpret_cast(moduleBase + exportDir->AddressOfNameOrdinals); + DWORD* rvaOfFunctions = reinterpret_cast(moduleBase + exportDir->AddressOfFunctions); + return reinterpret_cast(moduleBase + rvaOfFunctions[nameOrds[i]]); + } + } + + return nullptr; + } + + template + void hookGdiFunction(const char* moduleName, const char* funcName) + { + OrigFuncPtr& origFuncPtr = getOrigFuncPtr(); + origFuncPtr = reinterpret_cast(getProcAddress(GetModuleHandle(moduleName), funcName)); + OrigFuncPtr newFuncPtr = getCompatGdiFuncPtr(origFuncPtr); + DetourAttach(reinterpret_cast(&origFuncPtr), newFuncPtr); + g_funcNames[origFunc] = funcName; + } +} + +void CompatGdiFunctions::hookGdiFunctions() +{ +#define HOOK_GDI_FUNCTION(module, func) hookGdiFunction(#module, #func); + +#define HOOK_GDI_TEXT_FUNCTION(module, func) \ + HOOK_GDI_FUNCTION(module, func##A); \ + HOOK_GDI_FUNCTION(module, func##W) + + DetourTransactionBegin(); + HOOK_GDI_FUNCTION(gdi32, BitBlt); + DetourTransactionCommit(); + +#undef HOOK_GDI_TEXT_FUNCTION +#undef HOOK_GDI_FUNCTION +} diff --git a/DDrawCompat/CompatGdiFunctions.h b/DDrawCompat/CompatGdiFunctions.h new file mode 100644 index 0000000..ce2a0d2 --- /dev/null +++ b/DDrawCompat/CompatGdiFunctions.h @@ -0,0 +1,7 @@ +#pragma once + +class CompatGdiFunctions +{ +public: + static void hookGdiFunctions(); +}; diff --git a/DDrawCompat/CompatGdiSurface.cpp b/DDrawCompat/CompatGdiSurface.cpp deleted file mode 100644 index c43f4ea..0000000 --- a/DDrawCompat/CompatGdiSurface.cpp +++ /dev/null @@ -1,479 +0,0 @@ -#define CINTERFACE -#define WIN32_LEAN_AND_MEAN - -#include - -#include -#include -#include - -#include "CompatDirectDraw.h" -#include "CompatDirectDrawSurface.h" -#include "CompatGdiDcCache.h" -#include "CompatGdiSurface.h" -#include "CompatPrimarySurface.h" -#include "DDrawLog.h" -#include "DDrawProcs.h" -#include "DDrawScopedThreadLock.h" -#include "RealPrimarySurface.h" - -namespace -{ - using CompatGdiDcCache::CompatDc; - - struct CaretData - { - HWND hwnd; - long left; - long top; - long width; - long height; - bool isDrawn; - }; - - struct ExcludeClipRectsData - { - HDC compatDc; - POINT clientOrigin; - HWND rootWnd; - }; - - bool g_suppressGdiHooks = false; - - class HookRecursionGuard - { - public: - HookRecursionGuard() - { - g_suppressGdiHooks = true; - } - - ~HookRecursionGuard() - { - g_suppressGdiHooks = false; - } - }; - - auto g_origGetDc = &GetDC; - auto g_origGetDcEx = &GetDCEx; - auto g_origGetWindowDc = &GetWindowDC; - auto g_origReleaseDc = &ReleaseDC; - auto g_origBeginPaint = &BeginPaint; - auto g_origEndPaint = &EndPaint; - - auto g_origCreateCaret = &CreateCaret; - auto g_origShowCaret = &ShowCaret; - auto g_origHideCaret = &HideCaret; - - std::unordered_map g_dcToCompatDc; - - CaretData g_caret = {}; - - CRITICAL_SECTION g_gdiCriticalSection; - - class GdiScopedThreadLock - { - public: - GdiScopedThreadLock() - { - EnterCriticalSection(&g_gdiCriticalSection); - } - - ~GdiScopedThreadLock() - { - LeaveCriticalSection(&g_gdiCriticalSection); - } - }; - - POINT getClientOrigin(HWND hwnd); - HDC getCompatDc(HWND hwnd, HDC origDc, const POINT& origin); - HDC releaseCompatDc(HDC hdc); - - LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam) - { - if (HC_ACTION == nCode) - { - auto ret = reinterpret_cast(lParam); - if (WM_WINDOWPOSCHANGED == ret->message) - { - InvalidateRect(nullptr, nullptr, TRUE); - } - else if (WM_ERASEBKGND == ret->message && ret->lResult) - { - HDC origDc = reinterpret_cast(ret->wParam); - GdiScopedThreadLock gdiLock; - if (g_dcToCompatDc.find(origDc) == g_dcToCompatDc.end()) - { - HWND hwnd = WindowFromDC(origDc); - POINT origin = {}; - ClientToScreen(hwnd, &origin); - - HDC compatDc = getCompatDc(hwnd, origDc, origin); - if (compatDc != origDc) - { - SendMessage(hwnd, WM_ERASEBKGND, reinterpret_cast(compatDc), 0); - releaseCompatDc(compatDc); - } - } - } - } - - return CallNextHookEx(nullptr, nCode, wParam, lParam); - } - - BOOL CALLBACK excludeClipRectsForOverlappingWindows(HWND hwnd, LPARAM lParam) - { - auto excludeClipRectsData = reinterpret_cast(lParam); - if (hwnd == excludeClipRectsData->rootWnd) - { - return FALSE; - } - - RECT rect = {}; - GetWindowRect(hwnd, &rect); - OffsetRect(&rect, -excludeClipRectsData->clientOrigin.x, -excludeClipRectsData->clientOrigin.y); - ExcludeClipRect(excludeClipRectsData->compatDc, rect.left, rect.top, rect.right, rect.bottom); - return TRUE; - } - - POINT getClientOrigin(HWND hwnd) - { - POINT origin = {}; - if (hwnd) - { - ClientToScreen(hwnd, &origin); - } - return origin; - } - - HDC getCompatDc(HWND hwnd, HDC origDc, const POINT& origin) - { - GdiScopedThreadLock gdiLock; - if (!origDc || !RealPrimarySurface::isFullScreen() || g_suppressGdiHooks) - { - return origDc; - } - - HookRecursionGuard recursionGuard; - CompatDc compatDc = CompatGdiDcCache::getDc(); - if (!compatDc.dc) - { - return origDc; - } - - if (hwnd) - { - SetWindowOrgEx(compatDc.dc, -origin.x, -origin.y, nullptr); - - HRGN clipRgn = CreateRectRgn(0, 0, 0, 0); - GetRandomRgn(origDc, clipRgn, SYSRGN); - SelectClipRgn(compatDc.dc, clipRgn); - RECT r = {}; - GetRgnBox(clipRgn, &r); - DeleteObject(clipRgn); - - ExcludeClipRectsData excludeClipRectsData = { compatDc.dc, origin, GetAncestor(hwnd, GA_ROOT) }; - EnumThreadWindows(GetCurrentThreadId(), &excludeClipRectsForOverlappingWindows, - reinterpret_cast(&excludeClipRectsData)); - } - - compatDc.origDc = origDc; - g_dcToCompatDc[compatDc.dc] = compatDc; - return compatDc.dc; - } - - FARPROC getProcAddress(HMODULE module, const char* procName) - { - if (!module || !procName) - { - return nullptr; - } - - PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(module); - if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic) { - return nullptr; - } - char* moduleBase = reinterpret_cast(module); - - PIMAGE_NT_HEADERS ntHeader = reinterpret_cast( - reinterpret_cast(dosHeader) + dosHeader->e_lfanew); - if (IMAGE_NT_SIGNATURE != ntHeader->Signature) - { - return nullptr; - } - - PIMAGE_EXPORT_DIRECTORY exportDir = reinterpret_cast( - moduleBase + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - - DWORD* rvaOfNames = reinterpret_cast(moduleBase + exportDir->AddressOfNames); - - for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) - { - if (0 == strcmp(procName, moduleBase + rvaOfNames[i])) - { - WORD* nameOrds = reinterpret_cast(moduleBase + exportDir->AddressOfNameOrdinals); - DWORD* rvaOfFunctions = reinterpret_cast(moduleBase + exportDir->AddressOfFunctions); - return reinterpret_cast(moduleBase + rvaOfFunctions[nameOrds[i]]); - } - } - - return nullptr; - } - - POINT getWindowOrigin(HWND hwnd) - { - POINT origin = {}; - if (hwnd) - { - RECT windowRect = {}; - GetWindowRect(hwnd, &windowRect); - origin.x = windowRect.left; - origin.y = windowRect.top; - } - return origin; - } - - template - void hookGdiFunction(const char* funcName, FuncPtr& origFuncPtr, FuncPtr newFuncPtr) - { - origFuncPtr = reinterpret_cast(getProcAddress(GetModuleHandle("user32"), funcName)); - DetourAttach(reinterpret_cast(&origFuncPtr), newFuncPtr); - } - - HDC releaseCompatDc(HDC hdc) - { - GdiScopedThreadLock gdiLock; - if (g_suppressGdiHooks) - { - return hdc; - } - - HookRecursionGuard recursionGuard; - - auto it = g_dcToCompatDc.find(hdc); - - if (it != g_dcToCompatDc.end()) - { - HDC origDc = it->second.origDc; - CompatGdiDcCache::returnDc(it->second); - g_dcToCompatDc.erase(it); - - RealPrimarySurface::update(); - return origDc; - } - - return hdc; - } - - void drawCaret() - { - HDC dc = GetDC(g_caret.hwnd); - PatBlt(dc, g_caret.left, g_caret.top, g_caret.width, g_caret.height, PATINVERT); - ReleaseDC(g_caret.hwnd, dc); - } - - LONG getCaretState(IAccessible* accessible) - { - VARIANT varChild = {}; - varChild.vt = VT_I4; - varChild.lVal = CHILDID_SELF; - VARIANT varState = {}; - accessible->lpVtbl->get_accState(accessible, varChild, &varState); - return varState.lVal; - } - - void CALLBACK caretDestroyEvent( - HWINEVENTHOOK /*hWinEventHook*/, - DWORD /*event*/, - HWND hwnd, - LONG idObject, - LONG idChild, - DWORD /*dwEventThread*/, - DWORD /*dwmsEventTime*/) - { - if (OBJID_CARET != idObject || !g_caret.isDrawn || g_caret.hwnd != hwnd) - { - return; - } - - IAccessible* accessible = nullptr; - VARIANT varChild = {}; - AccessibleObjectFromEvent(hwnd, idObject, idChild, &accessible, &varChild); - if (accessible) - { - if (STATE_SYSTEM_INVISIBLE == getCaretState(accessible)) - { - GdiScopedThreadLock gdiLock; - drawCaret(); - g_caret.isDrawn = false; - } - accessible->lpVtbl->Release(accessible); - } - } - - HDC WINAPI getDc(HWND hWnd) - { - Compat::LogEnter("GetDC", hWnd); - HDC compatDc = getCompatDc(hWnd, g_origGetDc(hWnd), getClientOrigin(hWnd)); - Compat::LogLeave("GetDC", hWnd) << compatDc; - return compatDc; - } - - HDC WINAPI getDcEx(HWND hWnd, HRGN hrgnClip, DWORD flags) - { - Compat::LogEnter("GetDCEx", hWnd); - HDC compatDc = getCompatDc(hWnd, g_origGetDcEx(hWnd, hrgnClip, flags), - flags & (DCX_WINDOW | DCX_PARENTCLIP) ? getWindowOrigin(hWnd) : getClientOrigin(hWnd)); - Compat::LogLeave("GetDCEx", hWnd) << compatDc; - return compatDc; - } - - HDC WINAPI getWindowDc(HWND hWnd) - { - Compat::LogEnter("GetWindowDC", hWnd); - HDC compatDc = getCompatDc(hWnd, g_origGetWindowDc(hWnd), getWindowOrigin(hWnd)); - Compat::LogLeave("GetWindowDC", hWnd) << compatDc; - return compatDc; - } - - int WINAPI releaseDc(HWND hWnd, HDC hDC) - { - Compat::LogEnter("ReleaseDC", hWnd, hDC); - int result = g_origReleaseDc(hWnd, releaseCompatDc(hDC)); - Compat::LogLeave("ReleaseDC", hWnd, hDC) << result; - return result; - } - - HDC WINAPI beginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint) - { - Compat::LogEnter("BeginPaint", hWnd, lpPaint); - HDC compatDc = getCompatDc(hWnd, g_origBeginPaint(hWnd, lpPaint), getClientOrigin(hWnd)); - lpPaint->hdc = compatDc; - Compat::LogLeave("BeginPaint", hWnd, lpPaint) << compatDc; - return compatDc; - } - - BOOL WINAPI endPaint(HWND hWnd, const PAINTSTRUCT* lpPaint) - { - Compat::LogEnter("EndPaint", hWnd, lpPaint); - - BOOL result = FALSE; - if (lpPaint) - { - PAINTSTRUCT paint = *lpPaint; - paint.hdc = releaseCompatDc(lpPaint->hdc); - result = g_origEndPaint(hWnd, &paint); - } - else - { - result = g_origEndPaint(hWnd, lpPaint); - } - - Compat::LogLeave("EndPaint", hWnd, lpPaint) << result; - return result; - } - - BOOL WINAPI createCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight) - { - BOOL result = g_origCreateCaret(hWnd, hBitmap, nWidth, nHeight); - if (result) - { - GdiScopedThreadLock gdiLock; - if (g_caret.isDrawn) - { - drawCaret(); - g_caret.isDrawn = false; - } - g_caret.width = nWidth ? nWidth : GetSystemMetrics(SM_CXBORDER); - g_caret.height = nHeight ? nHeight : GetSystemMetrics(SM_CYBORDER); - } - return result; - } - - BOOL WINAPI showCaret(HWND hWnd) - { - BOOL result = g_origShowCaret(hWnd); - GdiScopedThreadLock gdiLock; - if (result && !g_caret.isDrawn) - { - IAccessible* accessible = nullptr; - AccessibleObjectFromWindow(hWnd, static_cast(OBJID_CARET), IID_IAccessible, - reinterpret_cast(&accessible)); - if (accessible) - { - if (0 == getCaretState(accessible)) - { - POINT caretPos = {}; - GetCaretPos(&caretPos); - g_caret.left = caretPos.x; - g_caret.top = caretPos.y; - g_caret.hwnd = hWnd; - drawCaret(); - g_caret.isDrawn = true; - } - accessible->lpVtbl->Release(accessible); - } - } - return result; - } - - BOOL WINAPI hideCaret(HWND hWnd) - { - BOOL result = g_origHideCaret(hWnd); - GdiScopedThreadLock gdiLock; - if (result && g_caret.isDrawn) - { - drawCaret(); - g_caret.isDrawn = false; - } - return result; - } -} - -void CompatGdiSurface::hookGdi() -{ - static bool alreadyHooked = false; - if (alreadyHooked) - { - return; - } - - InitializeCriticalSection(&g_gdiCriticalSection); - if (CompatGdiDcCache::init()) - { - DetourTransactionBegin(); - hookGdiFunction("GetDC", g_origGetDc, &getDc); - hookGdiFunction("GetDCEx", g_origGetDcEx, &getDcEx); - hookGdiFunction("GetWindowDC", g_origGetWindowDc, &getWindowDc); - hookGdiFunction("ReleaseDC", g_origReleaseDc, &releaseDc); - hookGdiFunction("BeginPaint", g_origBeginPaint, &beginPaint); - hookGdiFunction("EndPaint", g_origEndPaint, &endPaint); - hookGdiFunction("CreateCaret", g_origCreateCaret, &createCaret); - hookGdiFunction("ShowCaret", g_origShowCaret, &showCaret); - hookGdiFunction("HideCaret", g_origHideCaret, &hideCaret); - DetourTransactionCommit(); - - DWORD threadId = GetCurrentThreadId(); - SetWindowsHookEx(WH_CALLWNDPROCRET, callWndRetProc, nullptr, threadId); - SetWinEventHook(EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY, - nullptr, &caretDestroyEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); - } - alreadyHooked = true; -} - -void CompatGdiSurface::release() -{ - GdiScopedThreadLock gdiLock; - CompatGdiDcCache::release(); -} - -void CompatGdiSurface::setSurfaceMemory(void* surfaceMemory, LONG pitch) -{ - GdiScopedThreadLock gdiLock; - const bool wasReleased = CompatGdiDcCache::isReleased(); - CompatGdiDcCache::setSurfaceMemory(surfaceMemory, pitch); - if (wasReleased) - { - InvalidateRect(nullptr, nullptr, TRUE); - } -} diff --git a/DDrawCompat/CompatGdiSurface.h b/DDrawCompat/CompatGdiSurface.h deleted file mode 100644 index 712a242..0000000 --- a/DDrawCompat/CompatGdiSurface.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -class CompatGdiSurface -{ -public: - static void hookGdi(); - static void release(); - static void setSurfaceMemory(void* surfaceMemory, LONG pitch); -}; diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 0a1012b..a2188aa 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -145,8 +145,10 @@ + + - + @@ -167,8 +169,9 @@ + - + @@ -176,6 +179,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index b05c9e9..ad2883e 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -48,9 +48,6 @@ Header Files - - Header Files - Header Files @@ -69,6 +66,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -89,9 +95,6 @@ Source Files - - Source Files - Source Files @@ -110,6 +113,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/DDrawCompat/DllMain.cpp b/DDrawCompat/DllMain.cpp index 8dbd43d..65253c7 100644 --- a/DDrawCompat/DllMain.cpp +++ b/DDrawCompat/DllMain.cpp @@ -3,7 +3,7 @@ #include "CompatDirectDraw.h" #include "CompatDirectDrawSurface.h" #include "CompatDirectDrawPalette.h" -#include "CompatGdiSurface.h" +#include "CompatGdi.h" #include "CompatVtable.h" #include "DDrawProcs.h" @@ -95,7 +95,7 @@ namespace hookDirectDrawPalette(*dd); Compat::Log() << "Installing GDI hooks"; - CompatGdiSurface::hookGdi(); + CompatGdi::installHooks(); dd->lpVtbl->Release(dd); } diff --git a/DDrawCompat/RealPrimarySurface.cpp b/DDrawCompat/RealPrimarySurface.cpp index 5fae0cd..9ac50b0 100644 --- a/DDrawCompat/RealPrimarySurface.cpp +++ b/DDrawCompat/RealPrimarySurface.cpp @@ -2,7 +2,7 @@ #include "CompatDirectDraw.h" #include "CompatDirectDrawSurface.h" -#include "CompatGdiSurface.h" +#include "CompatGdi.h" #include "CompatPrimarySurface.h" #include "Config.h" #include "DDrawProcs.h" @@ -306,7 +306,7 @@ bool RealPrimarySurface::isLost() DDERR_SURFACELOST == CompatDirectDrawSurface::s_origVtable.IsLost(g_frontBuffer); if (isLost) { - CompatGdiSurface::release(); + CompatGdi::releaseSurfaceMemory(); } return isLost; }