From 1999d1c56ef4af2b173764a0dffff0c270357628 Mon Sep 17 00:00:00 2001 From: narzoul Date: Sun, 10 Jan 2016 00:23:32 +0100 Subject: [PATCH] Revamping GDI interworking - Part 2 Extended GDI redirection to most of the GDI rendering methods. Readded handling of window messages and extended it to support scrolling. Readded manual drawing of the caret. Simplified the DC cache. --- DDrawCompat/CompatDirectDraw.cpp | 4 - DDrawCompat/CompatDirectDrawSurface.cpp | 18 +- DDrawCompat/CompatDirectDrawSurface.h | 1 - DDrawCompat/CompatGdi.cpp | 142 +++++++++++-- DDrawCompat/CompatGdi.h | 36 +++- DDrawCompat/CompatGdiCaret.cpp | 160 +++++++++++++++ DDrawCompat/CompatGdiDc.cpp | 151 +++++++++++--- DDrawCompat/CompatGdiDc.h | 8 +- DDrawCompat/CompatGdiDcCache.cpp | 119 +++-------- DDrawCompat/CompatGdiDcCache.h | 10 +- DDrawCompat/CompatGdiFunctions.cpp | 261 ++++++++++++------------ DDrawCompat/CompatGdiFunctions.h | 5 +- DDrawCompat/Config.h | 1 - DDrawCompat/DDrawCompat.vcxproj | 8 +- DDrawCompat/DDrawCompat.vcxproj.filters | 12 ++ DDrawCompat/RealPrimarySurface.cpp | 9 +- 16 files changed, 625 insertions(+), 320 deletions(-) create mode 100644 DDrawCompat/CompatGdiCaret.cpp diff --git a/DDrawCompat/CompatDirectDraw.cpp b/DDrawCompat/CompatDirectDraw.cpp index e5684b0..0047567 100644 --- a/DDrawCompat/CompatDirectDraw.cpp +++ b/DDrawCompat/CompatDirectDraw.cpp @@ -66,10 +66,6 @@ HRESULT STDMETHODCALLTYPE CompatDirectDraw::CreateSurface( if (SUCCEEDED(result)) { CompatDirectDrawSurface::fixSurfacePtrs(**lplpDDSurface); - if (isPrimary) - { - CompatDirectDrawSurface::updateSurfaceParams(); - } } return result; diff --git a/DDrawCompat/CompatDirectDrawSurface.cpp b/DDrawCompat/CompatDirectDrawSurface.cpp index c44a9a9..eef827d 100644 --- a/DDrawCompat/CompatDirectDrawSurface.cpp +++ b/DDrawCompat/CompatDirectDrawSurface.cpp @@ -477,7 +477,6 @@ HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::Flip( if (This == s_compatPrimarySurface && SUCCEEDED(result)) { result = RealPrimarySurface::flip(dwFlags); - updateSurfaceParams(); } return result; } @@ -603,8 +602,7 @@ HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::Restore(TSurface* T result = RealPrimarySurface::restore(); if (wasLost) { - CompatGdi::releaseSurfaceMemory(); - updateSurfaceParams(); + CompatGdi::invalidate(); } } } @@ -682,20 +680,6 @@ void CompatDirectDrawSurface::restorePrimaryCaps(TDdsCaps& caps) caps.dwCaps |= DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE; } -template -void CompatDirectDrawSurface::updateSurfaceParams() -{ - TSurfaceDesc desc = {}; - desc.dwSize = sizeof(desc); - g_lockingPrimary = true; - if (SUCCEEDED(s_origVtable.Lock(s_compatPrimarySurface, nullptr, &desc, DDLOCK_WAIT, nullptr))) - { - s_origVtable.Unlock(s_compatPrimarySurface, nullptr); - CompatGdi::setSurfaceMemory(desc.lpSurface, desc.lPitch); - } - g_lockingPrimary = false; -} - template TSurface* CompatDirectDrawSurface::s_compatPrimarySurface = nullptr; diff --git a/DDrawCompat/CompatDirectDrawSurface.h b/DDrawCompat/CompatDirectDrawSurface.h index de2c5c3..e3fe4ed 100644 --- a/DDrawCompat/CompatDirectDrawSurface.h +++ b/DDrawCompat/CompatDirectDrawSurface.h @@ -23,7 +23,6 @@ public: static void fixSurfacePtrs(TSurface& surface); static void initPrimarySurfacePtr(const GUID& guid, IUnknown& surface); static void resetPrimarySurfacePtr(); - static void updateSurfaceParams(); static HRESULT STDMETHODCALLTYPE Blt( TSurface* This, diff --git a/DDrawCompat/CompatGdi.cpp b/DDrawCompat/CompatGdi.cpp index b8874a2..fd4b374 100644 --- a/DDrawCompat/CompatGdi.cpp +++ b/DDrawCompat/CompatGdi.cpp @@ -1,14 +1,72 @@ +#include "CompatDirectDrawSurface.h" #include "CompatGdi.h" +#include "CompatGdiCaret.h" #include "CompatGdiDcCache.h" #include "CompatGdiFunctions.h" +#include "CompatGdiWinProc.h" +#include "CompatPrimarySurface.h" +#include "DDrawProcs.h" +#include "RealPrimarySurface.h" namespace { - CRITICAL_SECTION g_gdiCriticalSection; + DWORD g_renderingDepth = 0; + + 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; + } + + BOOL CALLBACK invalidateWindow(HWND hwnd, LPARAM /*lParam*/) + { + DWORD processId = 0; + GetWindowThreadProcessId(hwnd, &processId); + if (processId == GetCurrentProcessId()) + { + RedrawWindow(hwnd, nullptr, nullptr, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); + } + return TRUE; + } } namespace CompatGdi { + CRITICAL_SECTION g_gdiCriticalSection; + std::unordered_map g_funcNames; + GdiScopedThreadLock::GdiScopedThreadLock() { EnterCriticalSection(&g_gdiCriticalSection); @@ -19,29 +77,89 @@ namespace CompatGdi LeaveCriticalSection(&g_gdiCriticalSection); } + bool beginGdiRendering() + { + if (!RealPrimarySurface::isFullScreen()) + { + return false; + } + + Compat::origProcs.AcquireDDThreadLock(); + EnterCriticalSection(&g_gdiCriticalSection); + + if (0 == g_renderingDepth) + { + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + if (FAILED(CompatDirectDrawSurface::s_origVtable.Lock( + CompatPrimarySurface::surface, nullptr, &desc, DDLOCK_WAIT, nullptr))) + { + LeaveCriticalSection(&g_gdiCriticalSection); + Compat::origProcs.ReleaseDDThreadLock(); + return false; + } + CompatGdiDcCache::setSurfaceMemory(desc.lpSurface, desc.lPitch); + } + + ++g_renderingDepth; + return true; + } + + void endGdiRendering() + { + --g_renderingDepth; + if (0 == g_renderingDepth) + { + GdiFlush(); + CompatDirectDrawSurface::s_origVtable.Unlock( + CompatPrimarySurface::surface, nullptr); + RealPrimarySurface::update(); + } + + LeaveCriticalSection(&g_gdiCriticalSection); + Compat::origProcs.ReleaseDDThreadLock(); + } + + void hookGdiFunction(const char* moduleName, const char* funcName, void*& origFuncPtr, void* newFuncPtr) + { +#ifdef _DEBUG + g_funcNames[origFuncPtr] = funcName; +#endif + + FARPROC procAddr = getProcAddress(GetModuleHandle(moduleName), funcName); + if (!procAddr) + { + Compat::Log() << "Failed to load the address of a GDI function: " << funcName; + return; + } + + origFuncPtr = procAddr; + if (NO_ERROR != DetourAttach(&origFuncPtr, newFuncPtr)) + { + Compat::Log() << "Failed to hook a GDI function: " << funcName; + return; + } + } + void installHooks() { InitializeCriticalSection(&g_gdiCriticalSection); if (CompatGdiDcCache::init()) { - CompatGdiFunctions::hookGdiFunctions(); + CompatGdiFunctions::installHooks(); + CompatGdiWinProc::installHooks(); + CompatGdiCaret::installHooks(); } } - void releaseSurfaceMemory() + void invalidate() { - GdiScopedThreadLock gdiLock; - CompatGdiDcCache::release(); + EnumWindows(&invalidateWindow, 0); } - void setSurfaceMemory(void* surfaceMemory, int pitch) + void updatePalette() { GdiScopedThreadLock gdiLock; - const bool wasReleased = CompatGdiDcCache::isReleased(); - CompatGdiDcCache::setSurfaceMemory(surfaceMemory, pitch); - if (wasReleased) - { - InvalidateRect(nullptr, nullptr, TRUE); - } + CompatGdiDcCache::clear(); } } diff --git a/DDrawCompat/CompatGdi.h b/DDrawCompat/CompatGdi.h index dfb2829..df9e7fa 100644 --- a/DDrawCompat/CompatGdi.h +++ b/DDrawCompat/CompatGdi.h @@ -1,5 +1,15 @@ #pragma once +#define WIN32_LEAN_AND_MEAN + +#include + +#include + +#include "DDrawLog.h" + +#define CALL_ORIG_GDI(func) CompatGdi::getOrigFuncPtr() + namespace CompatGdi { class GdiScopedThreadLock @@ -9,7 +19,29 @@ namespace CompatGdi ~GdiScopedThreadLock(); }; + extern CRITICAL_SECTION g_gdiCriticalSection; + extern std::unordered_map g_funcNames; + + bool beginGdiRendering(); + void endGdiRendering(); + + template + OrigFuncPtr& getOrigFuncPtr() + { + static OrigFuncPtr origFuncPtr = origFunc; + return origFuncPtr; + } + + void hookGdiFunction(const char* moduleName, const char* funcName, void*& origFuncPtr, void* newFuncPtr); + + template + void hookGdiFunction(const char* moduleName, const char* funcName, OrigFuncPtr newFuncPtr) + { + hookGdiFunction(moduleName, funcName, + reinterpret_cast(getOrigFuncPtr()), newFuncPtr); + } + void installHooks(); - void releaseSurfaceMemory(); - void setSurfaceMemory(void* surfaceMemory, int pitch); + void invalidate(); + void updatePalette(); }; diff --git a/DDrawCompat/CompatGdiCaret.cpp b/DDrawCompat/CompatGdiCaret.cpp new file mode 100644 index 0000000..26db63e --- /dev/null +++ b/DDrawCompat/CompatGdiCaret.cpp @@ -0,0 +1,160 @@ +#define CINTERFACE +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +#include "CompatGdi.h" +#include "CompatGdiCaret.h" +#include "CompatGdiDc.h" + +namespace +{ + struct CaretData + { + HWND hwnd; + long left; + long top; + long width; + long height; + bool isDrawn; + }; + + CaretData g_caret = {}; + + void drawCaret() + { + if (CompatGdi::beginGdiRendering()) + { + HDC dc = GetDC(g_caret.hwnd); + HDC compatDc = CompatGdiDc::getDc(dc); + PatBlt(compatDc, g_caret.left, g_caret.top, g_caret.width, g_caret.height, PATINVERT); + CompatGdiDc::releaseDc(dc); + ReleaseDC(g_caret.hwnd, dc); + CompatGdi::endGdiRendering(); + } + } + + 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*/) + { + CompatGdi::GdiScopedThreadLock gdiLock; + 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)) + { + drawCaret(); + g_caret.isDrawn = false; + } + accessible->lpVtbl->Release(accessible); + } + } + + BOOL WINAPI createCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight) + { + BOOL result = CALL_ORIG_GDI(CreateCaret)(hWnd, hBitmap, nWidth, nHeight); + if (result) + { + CompatGdi::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) + { + if (!CALL_ORIG_GDI(ShowCaret)(hWnd)) + { + return FALSE; + } + + CompatGdi::GdiScopedThreadLock gdiLock; + if (!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 TRUE; + } + + BOOL WINAPI hideCaret(HWND hWnd) + { + BOOL result = CALL_ORIG_GDI(HideCaret)(hWnd); + if (result) + { + CompatGdi::GdiScopedThreadLock gdiLock; + if (g_caret.isDrawn) + { + drawCaret(); + g_caret.isDrawn = false; + } + } + return result; + } +} + +#define HOOK_GDI_FUNCTION(module, func, newFunc) \ + CompatGdi::hookGdiFunction(#module, #func, &newFunc); + +namespace CompatGdiCaret +{ + void installHooks() + { + DetourTransactionBegin(); + HOOK_GDI_FUNCTION(user32, CreateCaret, createCaret); + HOOK_GDI_FUNCTION(user32, ShowCaret, showCaret); + HOOK_GDI_FUNCTION(user32, HideCaret, hideCaret); + DetourTransactionCommit(); + + const DWORD threadId = GetCurrentThreadId(); + SetWinEventHook(EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY, + nullptr, &caretDestroyEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); + } +} diff --git a/DDrawCompat/CompatGdiDc.cpp b/DDrawCompat/CompatGdiDc.cpp index 59347b2..2ad6172 100644 --- a/DDrawCompat/CompatGdiDc.cpp +++ b/DDrawCompat/CompatGdiDc.cpp @@ -1,9 +1,27 @@ +#include +#include + #include "CompatGdiDc.h" #include "CompatGdiDcCache.h" -#include "RealPrimarySurface.h" +#include "DDrawLog.h" namespace { + using CompatGdiDcCache::CachedDc; + + struct CompatDc : CachedDc + { + CompatDc(const CachedDc& cachedDc = {}) : CachedDc(cachedDc) {} + DWORD refCount; + HDC origDc; + HGDIOBJ origFont; + HGDIOBJ origBrush; + HGDIOBJ origPen; + }; + + typedef std::unordered_map CompatDcMap; + CompatDcMap g_origDcToCompatDc; + struct ExcludeClipRectsData { HDC compatDc; @@ -11,6 +29,35 @@ namespace HWND rootWnd; }; + void copyDcAttributes(CompatDc& compatDc, HDC origDc, POINT& origin) + { + compatDc.origFont = SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_FONT)); + compatDc.origBrush = SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_BRUSH)); + compatDc.origPen = SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_PEN)); + + SetArcDirection(compatDc.dc, GetArcDirection(origDc)); + SetBkColor(compatDc.dc, GetBkColor(origDc)); + SetBkMode(compatDc.dc, GetBkMode(origDc)); + SetDCBrushColor(compatDc.dc, GetDCBrushColor(origDc)); + SetDCPenColor(compatDc.dc, GetDCPenColor(origDc)); + SetPolyFillMode(compatDc.dc, GetPolyFillMode(origDc)); + SetROP2(compatDc.dc, GetROP2(origDc)); + SetStretchBltMode(compatDc.dc, GetStretchBltMode(origDc)); + SetTextAlign(compatDc.dc, GetTextAlign(origDc)); + SetTextCharacterExtra(compatDc.dc, GetTextCharacterExtra(origDc)); + SetTextColor(compatDc.dc, GetTextColor(origDc)); + SetWindowOrgEx(compatDc.dc, -origin.x, -origin.y, nullptr); + + POINT brushOrg = {}; + GetBrushOrgEx(origDc, &brushOrg); + SetBrushOrgEx(compatDc.dc, brushOrg.x, brushOrg.y, nullptr); + + POINT currentPos = {}; + MoveToEx(origDc, 0, 0, ¤tPos); + MoveToEx(origDc, currentPos.x, currentPos.y, nullptr); + MoveToEx(compatDc.dc, currentPos.x, currentPos.y, nullptr); + } + BOOL CALLBACK excludeClipRectsForOverlappingWindows(HWND hwnd, LPARAM lParam) { auto excludeClipRectsData = reinterpret_cast(lParam); @@ -19,56 +66,102 @@ namespace return FALSE; } + if (!IsWindowVisible(hwnd)) + { + return TRUE; + } + 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; } + + void setClippingRegion(HDC compatDc, HDC origDc, POINT& origin) + { + HRGN clipRgn = CreateRectRgn(0, 0, 0, 0); + const bool isEmptyClipRgn = 1 != GetRandomRgn(origDc, clipRgn, SYSRGN); + SelectClipRgn(compatDc, isEmptyClipRgn ? nullptr : clipRgn); + DeleteObject(clipRgn); + + HRGN origClipRgn = CreateRectRgn(0, 0, 0, 0); + if (1 == GetClipRgn(origDc, origClipRgn)) + { + OffsetRgn(origClipRgn, origin.x, origin.y); + ExtSelectClipRgn(compatDc, origClipRgn, RGN_AND); + } + DeleteObject(origClipRgn); + + if (!isEmptyClipRgn) + { + HWND hwnd = WindowFromDC(origDc); + if (hwnd) + { + ExcludeClipRectsData excludeClipRectsData = { compatDc, origin, GetAncestor(hwnd, GA_ROOT) }; + EnumThreadWindows(GetCurrentThreadId(), &excludeClipRectsForOverlappingWindows, + reinterpret_cast(&excludeClipRectsData)); + RECT windowRect = {}; + GetWindowRect(hwnd, &windowRect); + } + } + } } namespace CompatGdiDc { - CachedDc getDc(HDC origDc) + HDC getDc(HDC origDc) { - CachedDc cachedDc = {}; - if (!origDc || !RealPrimarySurface::isFullScreen() || - OBJ_DC != GetObjectType(origDc) || - DT_RASDISPLAY != GetDeviceCaps(origDc, TECHNOLOGY)) + if (!origDc || OBJ_DC != GetObjectType(origDc) || DT_RASDISPLAY != GetDeviceCaps(origDc, TECHNOLOGY) || + g_origDcToCompatDc.end() != std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(), + [=](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == origDc; })) { - return cachedDc; + return nullptr; } - cachedDc = CompatGdiDcCache::getDc(); - if (!cachedDc.dc) + auto it = g_origDcToCompatDc.find(origDc); + if (it != g_origDcToCompatDc.end()) { - return cachedDc; + ++it->second.refCount; + return it->second.dc; } - HWND hwnd = WindowFromDC(origDc); - if (hwnd) + CompatDc compatDc(CompatGdiDcCache::getDc()); + if (!compatDc.dc) { - 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 nullptr; } - return cachedDc; + POINT origin = {}; + GetDCOrgEx(origDc, &origin); + + copyDcAttributes(compatDc, origDc, origin); + setClippingRegion(compatDc.dc, origDc, origin); + + compatDc.refCount = 1; + compatDc.origDc = origDc; + g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc)); + + return compatDc.dc; } - void releaseDc(const CachedDc& cachedDc) + void releaseDc(HDC origDc) { - CompatGdiDcCache::releaseDc(cachedDc); + auto it = g_origDcToCompatDc.find(origDc); + if (it == g_origDcToCompatDc.end()) + { + return; + } + + CompatDc& compatDc = it->second; + --compatDc.refCount; + if (0 == compatDc.refCount) + { + SelectObject(compatDc.dc, compatDc.origFont); + SelectObject(compatDc.dc, compatDc.origBrush); + SelectObject(compatDc.dc, compatDc.origPen); + CompatGdiDcCache::releaseDc(compatDc); + g_origDcToCompatDc.erase(origDc); + } } } diff --git a/DDrawCompat/CompatGdiDc.h b/DDrawCompat/CompatGdiDc.h index f5ff301..9def77d 100644 --- a/DDrawCompat/CompatGdiDc.h +++ b/DDrawCompat/CompatGdiDc.h @@ -4,12 +4,8 @@ #include -#include "CompatGdiDcCache.h" - namespace CompatGdiDc { - using CompatGdiDcCache::CachedDc; - - CachedDc getDc(HDC origDc); - void releaseDc(const CachedDc& cachedDc); + HDC getDc(HDC origDc); + void releaseDc(HDC origDc); } diff --git a/DDrawCompat/CompatGdiDcCache.cpp b/DDrawCompat/CompatGdiDcCache.cpp index d3f8bfd..550bc72 100644 --- a/DDrawCompat/CompatGdiDcCache.cpp +++ b/DDrawCompat/CompatGdiDcCache.cpp @@ -1,4 +1,3 @@ -#include #include #include "CompatDirectDraw.h" @@ -8,41 +7,18 @@ #include "Config.h" #include "DDrawLog.h" #include "DDrawProcs.h" -#include "DDrawScopedThreadLock.h" - -namespace CompatGdiDcCache -{ - bool operator<(const SurfaceMemoryDesc& desc1, const SurfaceMemoryDesc& desc2) - { - return desc1.surfaceMemory < desc2.surfaceMemory || - (desc1.surfaceMemory == desc2.surfaceMemory && desc1.pitch < desc2.pitch); - } -} namespace { - using CompatGdiDcCache::SurfaceMemoryDesc; using CompatGdiDcCache::CachedDc; - std::map> g_caches; - std::vector* g_currentCache = nullptr; + std::vector g_cache; IDirectDraw7* g_directDraw = nullptr; void* g_surfaceMemory = nullptr; LONG g_pitch = 0; IDirectDrawSurface7* createGdiSurface(); - void releaseCachedDc(CachedDc cachedDc); - void releaseCache(std::vector& cache); - - void clearAllCaches() - { - for (auto& cache : g_caches) - { - releaseCache(cache.second); - } - g_caches.clear(); - } IDirectDraw7* createDirectDraw() { @@ -86,17 +62,13 @@ namespace // Release DD critical section acquired by IDirectDrawSurface7::GetDC to avoid deadlocks Compat::origProcs.ReleaseDDThreadLock(); - cachedDc.surfaceMemoryDesc.surfaceMemory = g_surfaceMemory; - cachedDc.surfaceMemoryDesc.pitch = g_pitch; - cachedDc.dc = dc; cachedDc.surface = surface; + cachedDc.dc = dc; return cachedDc; } IDirectDrawSurface7* createGdiSurface() { - Compat::DDrawScopedThreadLock ddLock; - DDSURFACEDESC2 desc = {}; desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE; @@ -125,66 +97,49 @@ namespace return surface; } - void fillCurrentCache() - { - for (DWORD i = 0; i < Config::gdiDcCacheSize; ++i) - { - CachedDc cachedDc = createCachedDc(); - if (!cachedDc.dc) - { - return; - } - g_currentCache->push_back(cachedDc); - } - } - void releaseCachedDc(CachedDc cachedDc) { // Reacquire DD critical section that was temporarily released after IDirectDrawSurface7::GetDC Compat::origProcs.AcquireDDThreadLock(); - if (FAILED(CompatDirectDrawSurface::s_origVtable.ReleaseDC( - cachedDc.surface, cachedDc.dc))) + HRESULT result = CompatDirectDrawSurface::s_origVtable.ReleaseDC( + cachedDc.surface, cachedDc.dc); + if (FAILED(result)) { - LOG_ONCE("Failed to release a cached DC"); - Compat::origProcs.ReleaseDDThreadLock(); + LOG_ONCE("Failed to release a cached DC: " << result); } CompatDirectDrawSurface::s_origVtable.Release(cachedDc.surface); } - - void releaseCache(std::vector& cache) - { - for (auto& cachedDc : cache) - { - releaseCachedDc(cachedDc); - } - } } namespace CompatGdiDcCache { + void clear() + { + for (auto& cachedDc : g_cache) + { + releaseCachedDc(cachedDc); + } + g_cache.clear(); + } + CachedDc getDc() { CachedDc cachedDc = {}; - if (!g_currentCache) + if (!g_surfaceMemory) { return cachedDc; } - if (g_currentCache->empty()) + if (g_cache.empty()) { - LOG_ONCE("Warning: GDI DC cache size is insufficient"); cachedDc = createCachedDc(); - if (!cachedDc.dc) - { - return cachedDc; - } } else { - cachedDc = g_currentCache->back(); - g_currentCache->pop_back(); + cachedDc = g_cache.back(); + g_cache.pop_back(); } return cachedDc; @@ -196,46 +151,20 @@ namespace CompatGdiDcCache return nullptr != g_directDraw; } - bool isReleased() - { - return g_caches.empty(); - } - - void release() - { - if (g_currentCache) - { - g_currentCache = nullptr; - clearAllCaches(); - } - } - void releaseDc(const CachedDc& cachedDc) { - g_caches[cachedDc.surfaceMemoryDesc].push_back(cachedDc); + g_cache.push_back(cachedDc); } void setSurfaceMemory(void* surfaceMemory, LONG pitch) { - g_surfaceMemory = surfaceMemory; - g_pitch = pitch; - - if (!surfaceMemory) + if (g_surfaceMemory == surfaceMemory && g_pitch == pitch) { - g_currentCache = nullptr; return; } - SurfaceMemoryDesc surfaceMemoryDesc = { surfaceMemory, pitch }; - auto it = g_caches.find(surfaceMemoryDesc); - if (it == g_caches.end()) - { - g_currentCache = &g_caches[surfaceMemoryDesc]; - fillCurrentCache(); - } - else - { - g_currentCache = &it->second; - } + g_surfaceMemory = surfaceMemory; + g_pitch = pitch; + clear(); } } diff --git a/DDrawCompat/CompatGdiDcCache.h b/DDrawCompat/CompatGdiDcCache.h index 701592d..366155e 100644 --- a/DDrawCompat/CompatGdiDcCache.h +++ b/DDrawCompat/CompatGdiDcCache.h @@ -8,23 +8,15 @@ namespace CompatGdiDcCache { - struct SurfaceMemoryDesc - { - void* surfaceMemory; - LONG pitch; - }; - struct CachedDc { - SurfaceMemoryDesc surfaceMemoryDesc; IDirectDrawSurface7* surface; HDC dc; }; + void clear(); CachedDc getDc(); bool init(); - bool isReleased(); - void release(); void releaseDc(const CachedDc& cachedDc); void setSurfaceMemory(void* surfaceMemory, LONG pitch); } diff --git a/DDrawCompat/CompatGdiFunctions.cpp b/DDrawCompat/CompatGdiFunctions.cpp index 4a76310..1374862 100644 --- a/DDrawCompat/CompatGdiFunctions.cpp +++ b/DDrawCompat/CompatGdiFunctions.cpp @@ -1,42 +1,30 @@ -#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() + bool hasDisplayDcArg(HDC dc) { - static OrigFuncPtr origFuncPtr = origFunc; - return origFuncPtr; + return dc && OBJ_DC == GetObjectType(dc) && DT_RASDISPLAY == GetDeviceCaps(dc, TECHNOLOGY); + } + + template + bool hasDisplayDcArg(T) + { + return false; + } + + template + bool hasDisplayDcArg(T t, Params... params) + { + return hasDisplayDcArg(t) || hasDisplayDcArg(params...); } template @@ -47,140 +35,147 @@ namespace 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; - } + HDC compatDc = CompatGdiDc::getDc(dc); + return compatDc ? compatDc : dc; + } - CompatDc compatDc = CompatGdiDc::getDc(dc); - if (!compatDc.dc) - { - return dc; - } + template + void releaseDc(T) {} - 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)); + void releaseDc(HDC dc) + { + CompatGdiDc::releaseDc(dc); + } - g_usedCompatDcs.push_back(compatDc); - ++*g_usedCompatDcCount; - return compatDc.dc; + template + void releaseDc(T t, Params... params) + { + releaseDc(params...); + releaseDc(t); } 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))) + if (!hasDisplayDcArg(params...) || !CompatGdi::beginGdiRendering()) { - return getOrigFuncPtr()(params...); + return CompatGdi::getOrigFuncPtr()(params...); } - Result result = getOrigFuncPtr()(replaceDc(params)...); - GdiFlush(); +#ifdef _DEBUG + Compat::LogEnter(CompatGdi::g_funcNames[origFunc], params...); +#endif - CompatDirectDrawSurface::s_origVtable.Unlock( - CompatPrimarySurface::surface, nullptr); + Result result = CompatGdi::getOrigFuncPtr()(replaceDc(params)...); + releaseDc(params...); + CompatGdi::endGdiRendering(); - 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(); - } +#ifdef _DEBUG + Compat::LogLeave(CompatGdi::g_funcNames[origFunc], params...) << result; +#endif return result; } template - OrigFuncPtr getCompatGdiFuncPtr(FuncPtr&) + 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_FUNCTION(module, func) \ + CompatGdi::hookGdiFunction( \ + #module, #func, getCompatGdiFuncPtr(&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(); +namespace CompatGdiFunctions +{ + void installHooks() + { + DetourTransactionBegin(); -#undef HOOK_GDI_TEXT_FUNCTION -#undef HOOK_GDI_FUNCTION + // Bitmap functions + HOOK_GDI_FUNCTION(msimg32, AlphaBlend); + HOOK_GDI_FUNCTION(gdi32, BitBlt); + HOOK_GDI_FUNCTION(gdi32, CreateCompatibleBitmap); + HOOK_GDI_FUNCTION(gdi32, CreateDIBitmap); + HOOK_GDI_FUNCTION(gdi32, CreateDIBSection); + HOOK_GDI_FUNCTION(gdi32, CreateDiscardableBitmap); + HOOK_GDI_FUNCTION(gdi32, ExtFloodFill); + HOOK_GDI_FUNCTION(gdi32, GetDIBits); + HOOK_GDI_FUNCTION(gdi32, GetPixel); + HOOK_GDI_FUNCTION(msimg32, GradientFill); + HOOK_GDI_FUNCTION(gdi32, MaskBlt); + HOOK_GDI_FUNCTION(gdi32, PlgBlt); + HOOK_GDI_FUNCTION(gdi32, SetDIBits); + HOOK_GDI_FUNCTION(gdi32, SetDIBitsToDevice); + HOOK_GDI_FUNCTION(gdi32, SetPixel); + HOOK_GDI_FUNCTION(gdi32, SetPixelV); + HOOK_GDI_FUNCTION(gdi32, StretchBlt); + HOOK_GDI_FUNCTION(gdi32, StretchDIBits); + HOOK_GDI_FUNCTION(msimg32, TransparentBlt); + + // Brush functions + HOOK_GDI_FUNCTION(gdi32, PatBlt); + + // Device context functions + HOOK_GDI_FUNCTION(gdi32, CreateCompatibleDC); + HOOK_GDI_FUNCTION(gdi32, DrawEscape); + + // Filled shape functions + HOOK_GDI_FUNCTION(gdi32, Chord); + HOOK_GDI_FUNCTION(gdi32, Ellipse); + HOOK_GDI_FUNCTION(user32, FillRect); + HOOK_GDI_FUNCTION(user32, FrameRect); + HOOK_GDI_FUNCTION(user32, InvertRect); + HOOK_GDI_FUNCTION(gdi32, Pie); + HOOK_GDI_FUNCTION(gdi32, Polygon); + HOOK_GDI_FUNCTION(gdi32, PolyPolygon); + HOOK_GDI_FUNCTION(gdi32, Rectangle); + HOOK_GDI_FUNCTION(gdi32, RoundRect); + + // Font and text functions + HOOK_GDI_TEXT_FUNCTION(user32, DrawText); + HOOK_GDI_TEXT_FUNCTION(user32, DrawTextEx); + HOOK_GDI_TEXT_FUNCTION(gdi32, ExtTextOut); + HOOK_GDI_TEXT_FUNCTION(gdi32, PolyTextOut); + HOOK_GDI_TEXT_FUNCTION(user32, TabbedTextOut); + HOOK_GDI_TEXT_FUNCTION(gdi32, TextOut); + + // Line and curve functions + HOOK_GDI_FUNCTION(gdi32, AngleArc); + HOOK_GDI_FUNCTION(gdi32, Arc); + HOOK_GDI_FUNCTION(gdi32, ArcTo); + HOOK_GDI_FUNCTION(gdi32, LineTo); + HOOK_GDI_FUNCTION(gdi32, PolyBezier); + HOOK_GDI_FUNCTION(gdi32, PolyBezierTo); + HOOK_GDI_FUNCTION(gdi32, PolyDraw); + HOOK_GDI_FUNCTION(gdi32, Polyline); + HOOK_GDI_FUNCTION(gdi32, PolylineTo); + HOOK_GDI_FUNCTION(gdi32, PolyPolyline); + + // Painting and drawing functions + HOOK_GDI_FUNCTION(user32, DrawCaption); + HOOK_GDI_FUNCTION(user32, DrawEdge); + HOOK_GDI_FUNCTION(user32, DrawFocusRect); + HOOK_GDI_FUNCTION(user32, DrawFrameControl); + HOOK_GDI_TEXT_FUNCTION(user32, DrawState); + HOOK_GDI_TEXT_FUNCTION(user32, GrayString); + HOOK_GDI_FUNCTION(user32, PaintDesktop); + + // Region functions + HOOK_GDI_FUNCTION(gdi32, FillRgn); + HOOK_GDI_FUNCTION(gdi32, FrameRgn); + HOOK_GDI_FUNCTION(gdi32, InvertRgn); + HOOK_GDI_FUNCTION(gdi32, PaintRgn); + + // Scroll bar functions + HOOK_GDI_FUNCTION(user32, ScrollDC); + + DetourTransactionCommit(); + } } diff --git a/DDrawCompat/CompatGdiFunctions.h b/DDrawCompat/CompatGdiFunctions.h index ce2a0d2..0575fad 100644 --- a/DDrawCompat/CompatGdiFunctions.h +++ b/DDrawCompat/CompatGdiFunctions.h @@ -1,7 +1,6 @@ #pragma once -class CompatGdiFunctions +namespace CompatGdiFunctions { -public: - static void hookGdiFunctions(); + void installHooks(); }; diff --git a/DDrawCompat/Config.h b/DDrawCompat/Config.h index 9e451e3..ffa2da1 100644 --- a/DDrawCompat/Config.h +++ b/DDrawCompat/Config.h @@ -4,7 +4,6 @@ typedef unsigned long DWORD; namespace Config { - const DWORD gdiDcCacheSize = 10; const DWORD minRefreshInterval = 1000 / 60; const DWORD minRefreshIntervalAfterFlip = 1000 / 10; const DWORD minPaletteUpdateInterval = 1000 / 60; diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index a2188aa..97e1604 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -94,7 +94,7 @@ DDrawCompat.def - dxguid.lib;detours.lib;oleacc.lib;%(AdditionalDependencies) + dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;%(AdditionalDependencies) @@ -121,7 +121,7 @@ DDrawCompat.def - dxguid.lib;detours.lib;oleacc.lib;%(AdditionalDependencies) + dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;%(AdditionalDependencies) No @@ -146,9 +146,11 @@ + + @@ -170,8 +172,10 @@ + + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index ad2883e..ed4729d 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -75,6 +75,12 @@ Header Files + + Header Files + + + Header Files + @@ -122,6 +128,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/DDrawCompat/RealPrimarySurface.cpp b/DDrawCompat/RealPrimarySurface.cpp index 9ac50b0..28b8cdf 100644 --- a/DDrawCompat/RealPrimarySurface.cpp +++ b/DDrawCompat/RealPrimarySurface.cpp @@ -302,13 +302,8 @@ bool RealPrimarySurface::isFullScreen() bool RealPrimarySurface::isLost() { - const bool isLost = g_frontBuffer && + return g_frontBuffer && DDERR_SURFACELOST == CompatDirectDrawSurface::s_origVtable.IsLost(g_frontBuffer); - if (isLost) - { - CompatGdi::releaseSurfaceMemory(); - } - return isLost; } void RealPrimarySurface::release() @@ -380,6 +375,8 @@ void RealPrimarySurface::update() void RealPrimarySurface::updatePalette() { + CompatGdi::updatePalette(); + if (isFullScreen()) { flip(DDFLIP_WAIT);