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