From 785663700d0054d649708913cc5c7ec7cdf61b04 Mon Sep 17 00:00:00 2001 From: narzoul Date: Tue, 17 Jul 2018 20:46:38 +0200 Subject: [PATCH] Separate GDI and DirectDraw surfaces --- DDrawCompat/Common/Hook.cpp | 18 +- DDrawCompat/Config/Config.h | 1 - DDrawCompat/D3dDdi/Device.cpp | 82 ++++-- DDrawCompat/D3dDdi/Device.h | 3 + DDrawCompat/D3dDdi/KernelModeThunks.cpp | 21 ++ DDrawCompat/D3dDdi/KernelModeThunks.h | 9 + .../D3dDdi/Log/KernelModeThunksLog.cpp | 16 ++ DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h | 2 + DDrawCompat/DDraw/ActivateAppHandler.cpp | 3 + DDrawCompat/DDraw/DirectDraw.cpp | 74 +++--- DDrawCompat/DDraw/DirectDraw.h | 1 + DDrawCompat/DDraw/DirectDrawPalette.cpp | 3 +- DDrawCompat/DDraw/DirectDrawSurface.cpp | 1 + DDrawCompat/DDraw/Hooks.cpp | 2 + DDrawCompat/DDraw/RealPrimarySurface.cpp | 121 +++++++-- DDrawCompat/DDraw/RealPrimarySurface.h | 3 +- DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp | 63 ++++- DDrawCompat/DDraw/Surfaces/PrimarySurface.h | 5 +- .../DDraw/Surfaces/PrimarySurfaceImpl.cpp | 17 +- DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp | 17 ++ DDrawCompat/DDraw/Surfaces/SurfaceImpl.h | 1 + DDrawCompat/DDrawCompat.vcxproj | 4 + DDrawCompat/DDrawCompat.vcxproj.filters | 12 + DDrawCompat/Dll/DllMain.cpp | 6 +- DDrawCompat/Gdi/AccessGuard.cpp | 152 +++++++++++ DDrawCompat/Gdi/AccessGuard.h | 40 +++ DDrawCompat/Gdi/Caret.cpp | 5 +- DDrawCompat/Gdi/Dc.cpp | 61 +++-- DDrawCompat/Gdi/Dc.h | 1 + DDrawCompat/Gdi/DcCache.cpp | 208 +-------------- DDrawCompat/Gdi/DcCache.h | 21 +- DDrawCompat/Gdi/DcFunctions.cpp | 35 ++- DDrawCompat/Gdi/Gdi.cpp | 173 ++----------- DDrawCompat/Gdi/Gdi.h | 8 +- DDrawCompat/Gdi/PaintHandlers.cpp | 29 +-- DDrawCompat/Gdi/Region.cpp | 30 --- DDrawCompat/Gdi/Region.h | 2 - DDrawCompat/Gdi/VirtualScreen.cpp | 244 ++++++++++++++++++ DDrawCompat/Gdi/VirtualScreen.h | 27 ++ DDrawCompat/Gdi/WinProc.cpp | 33 ++- DDrawCompat/Gdi/Window.cpp | 70 ++++- DDrawCompat/Gdi/Window.h | 7 +- DDrawCompat/Win32/DisplayMode.cpp | 5 + DDrawCompat/Win32/DisplayMode.h | 1 + DDrawCompat/Win32/FontSmoothing.cpp | 3 +- 45 files changed, 1050 insertions(+), 590 deletions(-) create mode 100644 DDrawCompat/Gdi/AccessGuard.cpp create mode 100644 DDrawCompat/Gdi/AccessGuard.h create mode 100644 DDrawCompat/Gdi/VirtualScreen.cpp create mode 100644 DDrawCompat/Gdi/VirtualScreen.h diff --git a/DDrawCompat/Common/Hook.cpp b/DDrawCompat/Common/Hook.cpp index 65ff706..4e3e78a 100644 --- a/DDrawCompat/Common/Hook.cpp +++ b/DDrawCompat/Common/Hook.cpp @@ -1,11 +1,12 @@ #define WIN32_LEAN_AND_MEAN #include +#include #include #include #include -#include #include +#include #include #include @@ -19,7 +20,7 @@ namespace struct HookedFunctionInfo { HMODULE module; - void* trampoline; + void*& origFunction; void* newFunction; }; @@ -28,7 +29,7 @@ namespace std::map::iterator findOrigFunc(void* origFunc) { return std::find_if(g_hookedFunctions.begin(), g_hookedFunctions.end(), - [=](const auto& i) { return origFunc == i.first || origFunc == i.second.trampoline; }); + [=](const auto& i) { return origFunc == i.first || origFunc == i.second.origFunction; }); } std::vector getProcessModules(HANDLE process) @@ -111,7 +112,7 @@ namespace const auto it = findOrigFunc(origFuncPtr); if (it != g_hookedFunctions.end()) { - origFuncPtr = it->second.trampoline; + origFuncPtr = it->second.origFunction; return; } @@ -136,13 +137,14 @@ namespace HMODULE module = nullptr; GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(hookedFuncPtr), &module); - g_hookedFunctions[hookedFuncPtr] = { module, origFuncPtr, newFuncPtr }; + g_hookedFunctions.emplace( + std::make_pair(hookedFuncPtr, HookedFunctionInfo{ module, origFuncPtr, newFuncPtr })); } void unhookFunction(const std::map::iterator& hookedFunc) { DetourTransactionBegin(); - DetourDetach(&hookedFunc->second.trampoline, hookedFunc->second.newFunction); + DetourDetach(&hookedFunc->second.origFunction, hookedFunc->second.newFunction); DetourTransactionCommit(); if (hookedFunc->second.module) @@ -173,7 +175,9 @@ namespace Compat if (0 != _stricmp(moduleBaseName.c_str(), moduleName)) { Compat::Log() << "Disabling external hook to " << funcName << " in " << moduleBaseName; - hookFunction(hookFunc, newFunc); + static std::list origFuncs; + origFuncs.push_back(hookFunc); + hookFunction(origFuncs.back(), newFunc); } } } diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index 6b1bb08..17fa87b 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -6,6 +6,5 @@ namespace Config { const int maxPaletteUpdatesPerMs = 5; const int minExpectedFlipsPerSec = 5; - const DWORD preallocatedGdiDcCount = 4; const DWORD primarySurfaceExtraRows = 2; } diff --git a/DDrawCompat/D3dDdi/Device.cpp b/DDrawCompat/D3dDdi/Device.cpp index 9ca3d3e..117d146 100644 --- a/DDrawCompat/D3dDdi/Device.cpp +++ b/DDrawCompat/D3dDdi/Device.cpp @@ -2,12 +2,36 @@ #include "D3dDdi/Device.h" #include "D3dDdi/DeviceFuncs.h" #include "D3dDdi/KernelModeThunks.h" +#include "Gdi/AccessGuard.h" namespace { D3DDDI_RESOURCEFLAGS getResourceTypeFlags(); const UINT g_resourceTypeFlags = getResourceTypeFlags().Value; + HANDLE g_gdiResourceHandle = nullptr; + bool g_isReadOnlyGdiLockEnabled = false; + + class RenderGuard : public Gdi::DDrawAccessGuard + { + public: + RenderGuard(D3dDdi::Device& device, Gdi::Access access) + : Gdi::DDrawAccessGuard(access) + , m_device(device) + { + device.prepareForRendering(); + } + + RenderGuard(D3dDdi::Device& device, Gdi::Access access, HANDLE resource, UINT subResourceIndex = UINT_MAX) + : Gdi::DDrawAccessGuard(access, g_gdiResourceHandle == resource) + , m_device(device) + { + device.prepareForRendering(resource, subResourceIndex); + } + + private: + D3dDdi::Device & m_device; + }; D3DDDI_RESOURCEFLAGS getResourceTypeFlags() { @@ -81,8 +105,8 @@ namespace D3dDdi HRESULT Device::blt(const D3DDDIARG_BLT& data) { - prepareForRendering(data.hSrcResource, data.SrcSubResourceIndex); - prepareForRendering(data.hDstResource, data.DstSubResourceIndex); + RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource, data.SrcSubResourceIndex); + RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource, data.DstSubResourceIndex); auto it = m_oversizedResources.find(data.hSrcResource); if (it != m_oversizedResources.end()) @@ -101,13 +125,13 @@ namespace D3dDdi HRESULT Device::clear(const D3DDDIARG_CLEAR& data, UINT numRect, const RECT* rect) { - prepareForRendering(); + RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE); return m_origVtable->pfnClear(m_device, &data, numRect, rect); } HRESULT Device::colorFill(const D3DDDIARG_COLORFILL& data) { - prepareForRendering(data.hResource, data.SubResourceIndex); + RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE, data.hResource, data.SubResourceIndex); return m_origVtable->pfnColorFill(m_device, &data); } @@ -198,6 +222,10 @@ namespace D3dDdi { m_sharedPrimary = nullptr; } + if (resource == g_gdiResourceHandle) + { + g_gdiResourceHandle = nullptr; + } } return result; @@ -205,45 +233,49 @@ namespace D3dDdi HRESULT Device::drawIndexedPrimitive(const D3DDDIARG_DRAWINDEXEDPRIMITIVE& data) { - prepareForRendering(); + RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE); return m_origVtable->pfnDrawIndexedPrimitive(m_device, &data); } HRESULT Device::drawIndexedPrimitive2(const D3DDDIARG_DRAWINDEXEDPRIMITIVE2& data, UINT indicesSize, const void* indexBuffer, const UINT* flagBuffer) { - prepareForRendering(); + RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE); return m_origVtable->pfnDrawIndexedPrimitive2(m_device, &data, indicesSize, indexBuffer, flagBuffer); } HRESULT Device::drawPrimitive(const D3DDDIARG_DRAWPRIMITIVE& data, const UINT* flagBuffer) { - prepareForRendering(); + RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE); return m_origVtable->pfnDrawPrimitive(m_device, &data, flagBuffer); } HRESULT Device::drawPrimitive2(const D3DDDIARG_DRAWPRIMITIVE2& data) { - prepareForRendering(); + RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE); return m_origVtable->pfnDrawPrimitive2(m_device, &data); } HRESULT Device::drawRectPatch(const D3DDDIARG_DRAWRECTPATCH& data, const D3DDDIRECTPATCH_INFO* info, const FLOAT* patch) { - prepareForRendering(); + RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE); return m_origVtable->pfnDrawRectPatch(m_device, &data, info, patch); } HRESULT Device::drawTriPatch(const D3DDDIARG_DRAWTRIPATCH& data, const D3DDDITRIPATCH_INFO* info, const FLOAT* patch) { - prepareForRendering(); + RenderGuard renderGuard(*this, Gdi::ACCESS_WRITE); return m_origVtable->pfnDrawTriPatch(m_device, &data, info, patch); } HRESULT Device::lock(D3DDDIARG_LOCK& data) { + Gdi::DDrawAccessGuard accessGuard( + (data.Flags.ReadOnly || g_isReadOnlyGdiLockEnabled) ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE, + data.hResource == g_gdiResourceHandle); + auto it = m_renderTargetResources.find(data.hResource); if (it != m_renderTargetResources.end()) { @@ -269,35 +301,45 @@ namespace D3dDdi HRESULT Device::present(const D3DDDIARG_PRESENT& data) { - prepareForRendering(data.hSrcResource, data.SrcSubResourceIndex); + RenderGuard renderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource, data.SrcSubResourceIndex); return m_origVtable->pfnPresent(m_device, &data); } HRESULT Device::present1(D3DDDIARG_PRESENT1& data) { + bool isGdiResourceInvolved = false; + for (UINT i = 0; i < data.SrcResources && !isGdiResourceInvolved; ++i) + { + isGdiResourceInvolved = data.phSrcResources[i].hResource == g_gdiResourceHandle; + } + + Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, isGdiResourceInvolved); + for (UINT i = 0; i < data.SrcResources; ++i) { prepareForRendering(data.phSrcResources[i].hResource, data.phSrcResources[i].SubResourceIndex); } + return m_origVtable->pfnPresent1(m_device, &data); } HRESULT Device::texBlt(const D3DDDIARG_TEXBLT& data) { - prepareForRendering(data.hDstResource); - prepareForRendering(data.hSrcResource); + RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource); + RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource); return m_origVtable->pfnTexBlt(m_device, &data); } HRESULT Device::texBlt1(const D3DDDIARG_TEXBLT1& data) { - prepareForRendering(data.hDstResource); - prepareForRendering(data.hSrcResource); + RenderGuard dstRenderGuard(*this, Gdi::ACCESS_WRITE, data.hDstResource); + RenderGuard srcRenderGuard(*this, Gdi::ACCESS_READ, data.hSrcResource); return m_origVtable->pfnTexBlt1(m_device, &data); } HRESULT Device::unlock(const D3DDDIARG_UNLOCK& data) { + Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, data.hResource == g_gdiResourceHandle); auto it = m_renderTargetResources.find(data.hResource); if (it != m_renderTargetResources.end()) { @@ -344,4 +386,14 @@ namespace D3dDdi prepareForRendering((it++)->second); } } + + void Device::setGdiResourceHandle(HANDLE resource) + { + g_gdiResourceHandle = resource; + } + + void Device::setReadOnlyGdiLock(bool enable) + { + g_isReadOnlyGdiLockEnabled = enable; + } } diff --git a/DDrawCompat/D3dDdi/Device.h b/DDrawCompat/D3dDdi/Device.h index 50c19f8..b9ff08b 100644 --- a/DDrawCompat/D3dDdi/Device.h +++ b/DDrawCompat/D3dDdi/Device.h @@ -48,6 +48,9 @@ namespace D3dDdi void prepareForRendering(HANDLE resource, UINT subResourceIndex = UINT_MAX); void prepareForRendering(); + static void setGdiResourceHandle(HANDLE resource); + static void setReadOnlyGdiLock(bool enable); + private: template HRESULT createOversizedResource( diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.cpp b/DDrawCompat/D3dDdi/KernelModeThunks.cpp index 4430acd..ad8e265 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.cpp +++ b/DDrawCompat/D3dDdi/KernelModeThunks.cpp @@ -31,6 +31,7 @@ namespace std::map g_contexts; std::map g_devices; + HMONITOR g_lastOpenAdapterMonitor = nullptr; decltype(D3DKMTCreateContextVirtual)* g_origD3dKmtCreateContextVirtual = nullptr; decltype(D3DKMTSetVidPnSourceOwner1)* g_origD3dKmtSetVidPnSourceOwner1 = nullptr; @@ -123,6 +124,20 @@ namespace return result; } + NTSTATUS APIENTRY openAdapterFromHdc(D3DKMT_OPENADAPTERFROMHDC* pData) + { + Compat::LogEnter("D3DKMTOpenAdapterFromHdc", pData); + NTSTATUS result = CALL_ORIG_FUNC(D3DKMTOpenAdapterFromHdc)(pData); + if (pData) + { + POINT p = {}; + GetDCOrgEx(pData->hDc, &p); + g_lastOpenAdapterMonitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY); + } + Compat::LogLeave("D3DKMTOpenAdapterFromHdc", pData) << result; + return result; + } + bool isPresentReady(D3DKMT_HANDLE device, D3DDDI_VIDEO_PRESENT_SOURCE_ID vidPnSourceId) { D3DKMT_GETDEVICESTATE deviceState = {}; @@ -253,6 +268,11 @@ namespace D3dDdi { namespace KernelModeThunks { + HMONITOR getLastOpenAdapterMonitor() + { + return g_lastOpenAdapterMonitor; + } + bool isPresentReady() { for (auto it : g_devices) @@ -272,6 +292,7 @@ namespace D3dDdi HOOK_FUNCTION(gdi32, D3DKMTCreateDCFromMemory, createDcFromMemory); HOOK_FUNCTION(gdi32, D3DKMTDestroyContext, destroyContext); HOOK_FUNCTION(gdi32, D3DKMTDestroyDevice, destroyDevice); + HOOK_FUNCTION(gdi32, D3DKMTOpenAdapterFromHdc, openAdapterFromHdc); HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, queryAdapterInfo); HOOK_FUNCTION(gdi32, D3DKMTPresent, present); HOOK_FUNCTION(gdi32, D3DKMTSetQueuedLimit, setQueuedLimit); diff --git a/DDrawCompat/D3dDdi/KernelModeThunks.h b/DDrawCompat/D3dDdi/KernelModeThunks.h index b5be73d..4ca269d 100644 --- a/DDrawCompat/D3dDdi/KernelModeThunks.h +++ b/DDrawCompat/D3dDdi/KernelModeThunks.h @@ -1,5 +1,13 @@ #pragma once +#define CINTERFACE +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include <../km/d3dkmthk.h> +#include + #include "D3dDdi/Log/KernelModeThunksLog.h" static const auto D3DDDI_FLIPINTERVAL_NOOVERRIDE = static_cast(5); @@ -8,6 +16,7 @@ namespace D3dDdi { namespace KernelModeThunks { + HMONITOR getLastOpenAdapterMonitor(); void installHooks(); bool isPresentReady(); void overrideFlipInterval(D3DDDI_FLIPINTERVAL_TYPE flipInterval); diff --git a/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.cpp b/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.cpp index 3a51284..47a2eb9 100644 --- a/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.cpp +++ b/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.cpp @@ -1,6 +1,13 @@ #include "Common/Log.h" #include "D3dDdi/Log/KernelModeThunksLog.h" +std::ostream& operator<<(std::ostream& os, const LUID& luid) +{ + return Compat::LogStruct(os) + << Compat::hex(luid.LowPart) + << Compat::hex(luid.HighPart); +} + std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXT& data) { return Compat::LogStruct(os) @@ -73,6 +80,15 @@ std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYDEVICE& data) << Compat::hex(data.hDevice); } +std::ostream& operator<<(std::ostream& os, const D3DKMT_OPENADAPTERFROMHDC& data) +{ + return Compat::LogStruct(os) + << data.hDc + << Compat::hex(data.hAdapter) + << data.AdapterLuid + << data.VidPnSourceId; +} + std::ostream& operator<<(std::ostream& os, const D3DKMT_PRESENT& data) { return Compat::LogStruct(os) diff --git a/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h b/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h index b64583d..67a32ff 100644 --- a/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h +++ b/DDrawCompat/D3dDdi/Log/KernelModeThunksLog.h @@ -8,12 +8,14 @@ #include #include <../km/d3dkmthk.h> +std::ostream& operator<<(std::ostream& os, const LUID& luid); std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXT& data); std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATECONTEXTVIRTUAL& data); std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATEDCFROMMEMORY& data); std::ostream& operator<<(std::ostream& os, const D3DKMT_CREATEDEVICE& data); std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYCONTEXT& data); std::ostream& operator<<(std::ostream& os, const D3DKMT_DESTROYDEVICE& data); +std::ostream& operator<<(std::ostream& os, const D3DKMT_OPENADAPTERFROMHDC& data); std::ostream& operator<<(std::ostream& os, const D3DKMT_PRESENT& data); std::ostream& operator<<(std::ostream& os, const D3DKMT_SETQUEUEDLIMIT& data); std::ostream& operator<<(std::ostream& os, const D3DKMT_SETVIDPNSOURCEOWNER& data); diff --git a/DDrawCompat/DDraw/ActivateAppHandler.cpp b/DDrawCompat/DDraw/ActivateAppHandler.cpp index 70e96f8..7a1e9b4 100644 --- a/DDrawCompat/DDraw/ActivateAppHandler.cpp +++ b/DDrawCompat/DDraw/ActivateAppHandler.cpp @@ -4,6 +4,7 @@ #include "Common/Hook.h" #include "DDraw/ActivateAppHandler.h" +#include "DDraw/RealPrimarySurface.h" #include "Gdi/Gdi.h" #include "Win32/DisplayMode.h" #include "Win32/FontSmoothing.h" @@ -32,6 +33,7 @@ namespace { case WM_ACTIVATEAPP: { + DDraw::RealPrimarySurface::disableUpdates(); isDisplayChangeNotificationEnabled = false; if (TRUE == wParam) { @@ -43,6 +45,7 @@ namespace } LRESULT result = g_origDdWndProc(hwnd, uMsg, wParam, lParam); isDisplayChangeNotificationEnabled = true; + DDraw::RealPrimarySurface::enableUpdates(); return result; } diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index e138571..b44d428 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -8,43 +8,6 @@ namespace { - DDPIXELFORMAT getRgbPixelFormat(DWORD bpp) - { - DDPIXELFORMAT pf = {}; - pf.dwSize = sizeof(pf); - pf.dwFlags = DDPF_RGB; - pf.dwRGBBitCount = bpp; - - switch (bpp) - { - case 1: - pf.dwFlags |= DDPF_PALETTEINDEXED1; - break; - case 2: - pf.dwFlags |= DDPF_PALETTEINDEXED2; - break; - case 4: - pf.dwFlags |= DDPF_PALETTEINDEXED4; - break; - case 8: - pf.dwFlags |= DDPF_PALETTEINDEXED8; - break; - case 16: - pf.dwRBitMask = 0xF800; - pf.dwGBitMask = 0x07E0; - pf.dwBBitMask = 0x001F; - break; - case 24: - case 32: - pf.dwRBitMask = 0xFF0000; - pf.dwGBitMask = 0x00FF00; - pf.dwBBitMask = 0x0000FF; - break; - } - - return pf; - } - template HRESULT setDisplayMode(TDirectDraw* This, DWORD width, DWORD height, DWORD bpp) { @@ -100,6 +63,43 @@ namespace DDraw return dm; } + DDPIXELFORMAT getRgbPixelFormat(DWORD bpp) + { + DDPIXELFORMAT pf = {}; + pf.dwSize = sizeof(pf); + pf.dwFlags = DDPF_RGB; + pf.dwRGBBitCount = bpp; + + switch (bpp) + { + case 1: + pf.dwFlags |= DDPF_PALETTEINDEXED1; + break; + case 2: + pf.dwFlags |= DDPF_PALETTEINDEXED2; + break; + case 4: + pf.dwFlags |= DDPF_PALETTEINDEXED4; + break; + case 8: + pf.dwFlags |= DDPF_PALETTEINDEXED8; + break; + case 16: + pf.dwRBitMask = 0xF800; + pf.dwGBitMask = 0x07E0; + pf.dwBBitMask = 0x001F; + break; + case 24: + case 32: + pf.dwRBitMask = 0xFF0000; + pf.dwGBitMask = 0x00FF00; + pf.dwBBitMask = 0x0000FF; + break; + } + + return pf; + } + void suppressEmulatedDirectDraw(GUID*& guid) { if (reinterpret_cast(DDCREATE_EMULATIONONLY) == guid) diff --git a/DDrawCompat/DDraw/DirectDraw.h b/DDrawCompat/DDraw/DirectDraw.h index fbddf99..f1ad11b 100644 --- a/DDrawCompat/DDraw/DirectDraw.h +++ b/DDrawCompat/DDraw/DirectDraw.h @@ -18,6 +18,7 @@ namespace DDraw void* getDdObject(TDirectDraw& dd); DDSURFACEDESC2 getDisplayMode(CompatRef dd); + DDPIXELFORMAT getRgbPixelFormat(DWORD bpp); void suppressEmulatedDirectDraw(GUID*& guid); template diff --git a/DDrawCompat/DDraw/DirectDrawPalette.cpp b/DDrawCompat/DDraw/DirectDrawPalette.cpp index c950501..56e7c8f 100644 --- a/DDrawCompat/DDraw/DirectDrawPalette.cpp +++ b/DDrawCompat/DDraw/DirectDrawPalette.cpp @@ -6,6 +6,7 @@ #include "DDraw/DirectDrawPalette.h" #include "DDraw/RealPrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h" +#include "Gdi/AccessGuard.h" namespace DDraw { @@ -37,7 +38,7 @@ namespace DDraw { std::memcpy(&PrimarySurface::s_paletteEntries[dwStartingEntry], lpEntries, dwCount * sizeof(PALETTEENTRY)); - RealPrimarySurface::updatePalette(dwStartingEntry, dwCount); + RealPrimarySurface::updatePalette(); } return result; } diff --git a/DDrawCompat/DDraw/DirectDrawSurface.cpp b/DDrawCompat/DDraw/DirectDrawSurface.cpp index f116236..05472d7 100644 --- a/DDrawCompat/DDraw/DirectDrawSurface.cpp +++ b/DDrawCompat/DDraw/DirectDrawSurface.cpp @@ -33,6 +33,7 @@ namespace DDraw SET_COMPAT_METHOD(BltFast); SET_COMPAT_METHOD(Flip); SET_COMPAT_METHOD(GetCaps); + SET_COMPAT_METHOD(GetDC); SET_COMPAT_METHOD(GetSurfaceDesc); SET_COMPAT_METHOD(IsLost); SET_COMPAT_METHOD(Lock); diff --git a/DDrawCompat/DDraw/Hooks.cpp b/DDrawCompat/DDraw/Hooks.cpp index b21a36b..4c1f250 100644 --- a/DDrawCompat/DDraw/Hooks.cpp +++ b/DDrawCompat/DDraw/Hooks.cpp @@ -104,6 +104,8 @@ namespace DDraw { void installHooks() { + RealPrimarySurface::init(); + Win32::Registry::unsetValue( HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\DirectDraw", "EmulationOnly"); Win32::Registry::unsetValue( diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index f76cd95..f927602 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -1,10 +1,12 @@ #include +#include #include #include "Common/CompatPtr.h" #include "Common/Hook.h" #include "Common/Time.h" #include "Config/Config.h" +#include "D3dDdi/Device.h" #include "D3dDdi/KernelModeThunks.h" #include "DDraw/DirectDraw.h" #include "DDraw/DirectDrawSurface.h" @@ -13,11 +15,26 @@ #include "DDraw/ScopedThreadLock.h" #include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Types.h" +#include "Gdi/AccessGuard.h" #include "Gdi/Gdi.h" +#include "Gdi/VirtualScreen.h" +#include "Gdi/Window.h" #include "Win32/DisplayMode.h" namespace { + struct BltToWindowViaGdiArgs + { + std::unique_ptr virtualScreenDc; + Gdi::Region* primaryRegion; + + BltToWindowViaGdiArgs() + : virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc) + , primaryRegion(nullptr) + { + } + }; + void onRelease(); DWORD WINAPI updateThreadProc(LPVOID lpParameter); @@ -42,7 +59,8 @@ namespace BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam) { - if (IsWindowVisible(hwnd) && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) + if (IsWindowVisible(hwnd) && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) && + !Gdi::Window::isPresentationWindow(hwnd)) { auto& visibleLayeredWindows = *reinterpret_cast*>(lParam); visibleLayeredWindows.push_back(hwnd); @@ -52,7 +70,7 @@ namespace BOOL CALLBACK bltToWindow(HWND hwnd, LPARAM lParam) { - if (!IsWindowVisible(hwnd) || (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) + if (!IsWindowVisible(hwnd) || !Gdi::Window::isPresentationWindow(hwnd)) { return TRUE; } @@ -63,6 +81,54 @@ namespace return TRUE; } + BOOL CALLBACK bltToWindowViaGdi(HWND hwnd, LPARAM lParam) + { + if (!IsWindowVisible(hwnd) || !Gdi::Window::isPresentationWindow(hwnd)) + { + return TRUE; + } + + auto window = Gdi::Window::get(GetParent(hwnd)); + if (!window) + { + return TRUE; + } + + Gdi::Region visibleRegion = window->getVisibleRegion(); + if (visibleRegion.isEmpty()) + { + return TRUE; + } + + auto& args = *reinterpret_cast(lParam); + if (args.primaryRegion) + { + visibleRegion -= *args.primaryRegion; + if (visibleRegion.isEmpty()) + { + return TRUE; + } + } + + if (!args.virtualScreenDc) + { + args.virtualScreenDc.reset(Gdi::VirtualScreen::createDc()); + if (!args.virtualScreenDc) + { + return FALSE; + } + } + + Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_READ); + HDC presentationWindowDc = GetWindowDC(hwnd); + RECT rect = window->getWindowRect(); + CALL_ORIG_FUNC(BitBlt)(presentationWindowDc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, + args.virtualScreenDc.get(), rect.left, rect.top, SRCCOPY); + ReleaseDC(hwnd, presentationWindowDc); + + return TRUE; + } + void bltVisibleLayeredWindowsToBackBuffer() { std::vector visibleLayeredWindows; @@ -94,7 +160,6 @@ namespace } g_backBuffer->ReleaseDC(g_backBuffer, backBufferDc); - DDraw::RealPrimarySurface::update(); } HRESULT bltToPrimaryChain(CompatRef src) @@ -112,15 +177,31 @@ namespace { Compat::LogEnter("RealPrimarySurface::compatBlt"); - bool result = false; + BltToWindowViaGdiArgs bltToWindowViaGdiArgs; + if (!g_frontBuffer || (!g_isFullScreen && DDraw::RealPrimarySurface::isLost())) + { + EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi, + reinterpret_cast(&bltToWindowViaGdiArgs)); + Compat::LogLeave("RealPrimarySurface::compatBlt") << false; + return false; + } + Gdi::Region primaryRegion(DDraw::PrimarySurface::getMonitorRect()); + bltToWindowViaGdiArgs.primaryRegion = &primaryRegion; + EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi, + reinterpret_cast(&bltToWindowViaGdiArgs)); + + bool result = false; auto primary(DDraw::PrimarySurface::getPrimary()); + Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(primary.get())); if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8) { HDC paletteConverterDc = nullptr; g_paletteConverter->GetDC(g_paletteConverter, &paletteConverterDc); HDC primaryDc = nullptr; + D3dDdi::Device::setReadOnlyGdiLock(true); primary->GetDC(primary, &primaryDc); + D3dDdi::Device::setReadOnlyGdiLock(false); if (paletteConverterDc && primaryDc) { @@ -203,7 +284,6 @@ namespace surface->SetClipper(surface, g_clipper); } - g_qpcFlipModeTimeout = Time::g_qpcFrequency / Config::minExpectedFlipsPerSec; g_qpcLastFlip = Time::queryPerformanceCounter() - g_qpcFlipModeTimeout; g_qpcNextUpdate = Time::queryPerformanceCounter(); @@ -216,17 +296,6 @@ namespace } g_qpcUpdateInterval = Time::g_qpcFrequency / dm.dwRefreshRate; - if (!g_updateEvent) - { - g_updateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - } - - if (!g_updateThread) - { - g_updateThread = CreateThread(nullptr, 0, &updateThreadProc, nullptr, 0, nullptr); - SetThreadPriority(g_updateThread, THREAD_PRIORITY_TIME_CRITICAL); - } - surface->SetPrivateData(surface, IID_IReleaseNotifier, &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER); @@ -265,6 +334,7 @@ namespace g_clipper.release(); g_isFullScreen = false; g_paletteConverter.release(); + g_qpcUpdateInterval = Time::g_qpcFrequency / 60; ZeroMemory(&g_surfaceDesc, sizeof(g_surfaceDesc)); @@ -275,6 +345,7 @@ namespace { ResetEvent(g_updateEvent); + Gdi::VirtualScreen::update(); if (compatBlt() && g_isFullScreen) { D3dDdi::KernelModeThunks::overrideFlipInterval( @@ -349,7 +420,7 @@ namespace DDraw return result; } - return init(dd, surface); + return ::init(dd, surface); } template HRESULT RealPrimarySurface::create(CompatRef); @@ -408,6 +479,17 @@ namespace DDraw return g_frontBuffer; } + void RealPrimarySurface::init() + { + g_qpcNextUpdate = Time::queryPerformanceCounter(); + g_qpcUpdateInterval = Time::g_qpcFrequency / 60; + g_qpcFlipModeTimeout = Time::g_qpcFrequency / Config::minExpectedFlipsPerSec; + g_updateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + + g_updateThread = CreateThread(nullptr, 0, &updateThreadProc, nullptr, 0, nullptr); + SetThreadPriority(g_updateThread, THREAD_PRIORITY_TIME_CRITICAL); + } + bool RealPrimarySurface::isFullScreen() { return g_isFullScreen; @@ -465,7 +547,7 @@ namespace DDraw g_frontBuffer->SetPalette(g_frontBuffer, PrimarySurface::s_palette); } - updatePalette(0, 256); + updatePalette(); } void RealPrimarySurface::update() @@ -500,9 +582,8 @@ namespace DDraw } } - void RealPrimarySurface::updatePalette(DWORD startingEntry, DWORD count) + void RealPrimarySurface::updatePalette() { - Gdi::updatePalette(startingEntry, count); if (PrimarySurface::s_palette) { update(); diff --git a/DDrawCompat/DDraw/RealPrimarySurface.h b/DDrawCompat/DDraw/RealPrimarySurface.h index 3506512..798f0bf 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.h +++ b/DDrawCompat/DDraw/RealPrimarySurface.h @@ -20,6 +20,7 @@ namespace DDraw static HRESULT flip(DWORD flags); static HRESULT getGammaRamp(DDGAMMARAMP* rampData); static CompatWeakPtr getSurface(); + static void init(); static bool isFullScreen(); static bool isLost(); static void release(); @@ -28,6 +29,6 @@ namespace DDraw static HRESULT setGammaRamp(DDGAMMARAMP* rampData); static void setPalette(); static void update(); - static void updatePalette(DWORD startingEntry, DWORD count); + static void updatePalette(); }; } diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp index 8312892..26fa8dc 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.cpp @@ -1,5 +1,13 @@ +#define CINTERFACE +#define _NO_DDRAWINT_NO_COM + +#include + #include "Common/CompatPtr.h" +#include "Common/CompatRef.h" #include "Config/Config.h" +#include "D3dDdi/Device.h" +#include "D3dDdi/KernelModeThunks.h" #include "DDraw/DirectDraw.h" #include "DDraw/RealPrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h" @@ -8,11 +16,30 @@ namespace { DDSURFACEDESC2 g_primarySurfaceDesc = {}; - CompatWeakPtr g_primarySurface = nullptr; + CompatWeakPtr g_primarySurface; HANDLE g_gdiResourceHandle = nullptr; DWORD g_origCaps = 0; + RECT g_monitorRect = {}; - HANDLE getResourceHandle(IDirectDrawSurface7& surface) + RECT getDdMonitorRect(CompatRef dd) + { + DDDEVICEIDENTIFIER2 di = {}; + dd->GetDeviceIdentifier(&dd, &di, 0); // Calls D3DKMTOpenAdapterFromHdc, which updates last monitor below + + HMONITOR monitor = D3dDdi::KernelModeThunks::getLastOpenAdapterMonitor(); + if (!monitor) + { + monitor = MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY); + } + + MONITORINFO mi = {}; + mi.cbSize = sizeof(mi); + GetMonitorInfo(monitor, &mi); + return mi.rcMonitor; + } + + template + HANDLE getResourceHandle(TSurface& surface) { return reinterpret_cast(&surface)[1][2]; } @@ -32,6 +59,7 @@ namespace DDraw g_gdiResourceHandle = nullptr; g_primarySurface = nullptr; g_origCaps = 0; + g_monitorRect = {}; s_palette = nullptr; s_surfaceBuffers.clear(); ZeroMemory(&s_paletteEntries, sizeof(s_paletteEntries)); @@ -75,6 +103,7 @@ namespace DDraw g_primarySurface = surface7; g_origCaps = origCaps; + g_monitorRect = getDdMonitorRect(*CompatPtr::from(&dd)); ZeroMemory(&g_primarySurfaceDesc, sizeof(g_primarySurfaceDesc)); g_primarySurfaceDesc.dwSize = sizeof(g_primarySurfaceDesc); @@ -86,6 +115,8 @@ namespace DDraw } g_gdiResourceHandle = getResourceHandle(*surface7); + D3dDdi::Device::setGdiResourceHandle(*reinterpret_cast(g_gdiResourceHandle)); + return DD_OK; } @@ -135,7 +166,7 @@ namespace DDraw do { - if (getResourceHandle(*surface) == g_gdiResourceHandle) + if (isGdiSurface(surface.get())) { return CompatPtr::from(surface.get()); } @@ -150,6 +181,11 @@ namespace DDraw return nullptr; } + RECT PrimarySurface::getMonitorRect() + { + return g_monitorRect; + } + CompatWeakPtr PrimarySurface::getPrimary() { return g_primarySurface; @@ -160,6 +196,27 @@ namespace DDraw return g_origCaps; } + template + static bool PrimarySurface::isGdiSurface(TSurface* surface) + { + return surface && getResourceHandle(*surface) == g_gdiResourceHandle; + } + + template bool PrimarySurface::isGdiSurface(IDirectDrawSurface*); + template bool PrimarySurface::isGdiSurface(IDirectDrawSurface2*); + template bool PrimarySurface::isGdiSurface(IDirectDrawSurface3*); + template bool PrimarySurface::isGdiSurface(IDirectDrawSurface4*); + template bool PrimarySurface::isGdiSurface(IDirectDrawSurface7*); + + void PrimarySurface::onRestore() + { + CompatPtr ddUnk; + g_primarySurface.get()->lpVtbl->GetDDInterface( + g_primarySurface, reinterpret_cast(&ddUnk.getRef())); + CompatPtr dd7(ddUnk); + g_monitorRect = getDdMonitorRect(*dd7); + } + void PrimarySurface::resizeBuffers(CompatRef surface) { DDSCAPS2 flipCaps = {}; diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurface.h b/DDrawCompat/DDraw/Surfaces/PrimarySurface.h index 46912d6..b82f1b2 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurface.h +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurface.h @@ -19,10 +19,13 @@ namespace DDraw static HRESULT flipToGdiSurface(); static const DDSURFACEDESC2& getDesc(); static CompatPtr getGdiSurface(); + static RECT getMonitorRect(); static CompatWeakPtr getPrimary(); static DWORD getOrigCaps(); + static void onRestore(); - void updateGdiSurfacePtr(IDirectDrawSurface* flipTargetOverride); + template + static bool isGdiSurface(TSurface* surface); static CompatWeakPtr s_palette; static PALETTEENTRY s_paletteEntries[256]; diff --git a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp index 2046009..8dbe53b 100644 --- a/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/PrimarySurfaceImpl.cpp @@ -57,22 +57,6 @@ namespace DDraw HRESULT result = m_impl.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); if (SUCCEEDED(result)) { - const LONG x = dwX; - const LONG y = dwY; - RECT destRect = { x, y, x, y }; - if (lpSrcRect) - { - destRect.right += lpSrcRect->right - lpSrcRect->left; - destRect.bottom += lpSrcRect->bottom - lpSrcRect->top; - } - else - { - TSurfaceDesc desc = {}; - desc.dwSize = sizeof(desc); - CompatVtable>::s_origVtable.GetSurfaceDesc(lpDDSrcSurface, &desc); - destRect.right += desc.dwWidth; - destRect.bottom += desc.dwHeight; - } RealPrimarySurface::update(); } return result; @@ -195,6 +179,7 @@ namespace DDraw result = m_impl.Restore(This); if (SUCCEEDED(result)) { + PrimarySurface::onRestore(); Gdi::redraw(nullptr); } } diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp index 3aab85f..51873b2 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp @@ -2,8 +2,10 @@ #include "Common/CompatRef.h" #include "DDraw/Repository.h" +#include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/Surface.h" #include "DDraw/Surfaces/SurfaceImpl.h" +#include "Gdi/AccessGuard.h" namespace { @@ -159,6 +161,8 @@ namespace DDraw TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { + Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This)); + Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface)); HRESULT result = s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); if (DDERR_UNSUPPORTED == result || DDERR_GENERIC == result) { @@ -178,6 +182,8 @@ namespace DDraw HRESULT SurfaceImpl::BltFast( TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { + Gdi::DDrawAccessGuard dstAccessGuard(Gdi::ACCESS_WRITE, PrimarySurface::isGdiSurface(This)); + Gdi::DDrawAccessGuard srcAccessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(lpDDSrcSurface)); HRESULT result = s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); if (DDERR_UNSUPPORTED == result || DDERR_GENERIC == result) { @@ -221,6 +227,13 @@ namespace DDraw return s_origVtable.GetCaps(This, lpDDSCaps); } + template + HRESULT SurfaceImpl::GetDC(TSurface* This, HDC* lphDC) + { + Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_WRITE); + return s_origVtable.GetDC(This, lphDC); + } + template HRESULT SurfaceImpl2::GetDDInterface(TSurface* /*This*/, LPVOID* lplpDD) { @@ -250,6 +263,8 @@ namespace DDraw TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { + Gdi::DDrawAccessGuard accessGuard((dwFlags & DDLOCK_READONLY) ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE, + PrimarySurface::isGdiSurface(This)); HRESULT result = s_origVtable.Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); if (DDERR_SURFACELOST == result) { @@ -269,6 +284,7 @@ namespace DDraw template HRESULT SurfaceImpl::ReleaseDC(TSurface* This, HDC hDC) { + Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(This)); return s_origVtable.ReleaseDC(This, hDC); } @@ -287,6 +303,7 @@ namespace DDraw template HRESULT SurfaceImpl::Unlock(TSurface* This, TUnlockParam lpRect) { + Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, PrimarySurface::isGdiSurface(This)); return s_origVtable.Unlock(This, lpRect); } diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h index f288019..2b5b241 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h @@ -43,6 +43,7 @@ namespace DDraw TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans); virtual HRESULT Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags); virtual HRESULT GetCaps(TSurface* This, TDdsCaps* lpDDSCaps); + virtual HRESULT GetDC(TSurface* This, HDC* lphDC); virtual HRESULT GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc); virtual HRESULT IsLost(TSurface* This); virtual HRESULT Lock(TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc, diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 3f4957f..90c69cb 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -215,6 +215,7 @@ + @@ -225,6 +226,7 @@ + @@ -274,6 +276,7 @@ + @@ -284,6 +287,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 45fd920..27d1d81 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -321,6 +321,12 @@ Header Files\Gdi + + Header Files\Gdi + + + Header Files\Gdi + @@ -494,6 +500,12 @@ Source Files\Gdi + + Source Files\Gdi + + + Source Files\Gdi + diff --git a/DDrawCompat/Dll/DllMain.cpp b/DDrawCompat/Dll/DllMain.cpp index b0ec6d0..6a40bb0 100644 --- a/DDrawCompat/Dll/DllMain.cpp +++ b/DDrawCompat/Dll/DllMain.cpp @@ -15,6 +15,7 @@ #include "Direct3d/Hooks.h" #include "Dll/Procs.h" #include "Gdi/Gdi.h" +#include "Gdi/VirtualScreen.h" #include "Win32/DisplayMode.h" #include "Win32/FontSmoothing.h" #include "Win32/MsgHooks.h" @@ -39,14 +40,15 @@ namespace Win32::Registry::installHooks(); Compat::Log() << "Installing Direct3D driver hooks"; D3dDdi::installHooks(); + Compat::Log() << "Installing display mode hooks"; + Win32::DisplayMode::installHooks(g_origDDrawModule); + Gdi::VirtualScreen::init(); Compat::Log() << "Installing DirectDraw hooks"; DDraw::installHooks(); Compat::Log() << "Installing Direct3D hooks"; Direct3d::installHooks(); Compat::Log() << "Installing GDI hooks"; Gdi::installHooks(); - Compat::Log() << "Installing display mode hooks"; - Win32::DisplayMode::installHooks(g_origDDrawModule); Compat::Log() << "Finished installing hooks"; isAlreadyInstalled = true; } diff --git a/DDrawCompat/Gdi/AccessGuard.cpp b/DDrawCompat/Gdi/AccessGuard.cpp new file mode 100644 index 0000000..67070f5 --- /dev/null +++ b/DDrawCompat/Gdi/AccessGuard.cpp @@ -0,0 +1,152 @@ +#define WIN32_LEAN_AND_MEAN + +#include + +#include "DDraw/RealPrimarySurface.h" +#include "DDraw/ScopedThreadLock.h" +#include "DDraw/Surfaces/PrimarySurface.h" +#include "Gdi/AccessGuard.h" +#include "Gdi/VirtualScreen.h" + +namespace +{ + struct Accessor + { + DWORD readAccessDepth; + DWORD writeAccessDepth; + bool isSynced; + + Accessor(bool isSynced) : readAccessDepth(0), writeAccessDepth(0), isSynced(isSynced) {} + }; + + Accessor g_ddrawAccessor(false); + Accessor g_gdiAccessor(true); + bool g_isSyncing = false; + + bool synchronize(Gdi::User user); + + void beginAccess(Gdi::User user, Gdi::Access access) + { + Compat::LogEnter("beginAccess", user, access); + + Accessor& accessor = Gdi::USER_DDRAW == user ? g_ddrawAccessor : g_gdiAccessor; + DWORD& accessDepth = Gdi::ACCESS_READ == access ? accessor.readAccessDepth : accessor.writeAccessDepth; + ++accessDepth; + + accessor.isSynced = accessor.isSynced || synchronize(user); + if (accessor.isSynced && Gdi::ACCESS_WRITE == access) + { + Accessor& otherAccessor = Gdi::USER_DDRAW == user ? g_gdiAccessor : g_ddrawAccessor; + otherAccessor.isSynced = false; + } + + Compat::LogLeave("beginAccess", user, access) << accessor.isSynced; + } + + void endAccess(Gdi::User user, Gdi::Access access) + { + Compat::LogEnter("endAccess", user, access); + + Accessor& accessor = Gdi::USER_DDRAW == user ? g_ddrawAccessor : g_gdiAccessor; + DWORD& accessDepth = Gdi::ACCESS_READ == access ? accessor.readAccessDepth : accessor.writeAccessDepth; + --accessDepth; + + if (Gdi::USER_DDRAW == user && + 0 == g_ddrawAccessor.readAccessDepth && 0 == g_ddrawAccessor.writeAccessDepth && + (0 != g_gdiAccessor.readAccessDepth || 0 != g_gdiAccessor.writeAccessDepth)) + { + g_gdiAccessor.isSynced = g_gdiAccessor.isSynced || synchronize(Gdi::USER_GDI); + if (g_gdiAccessor.isSynced && 0 != g_gdiAccessor.writeAccessDepth) + { + g_ddrawAccessor.isSynced = false; + } + } + + if (0 == accessDepth && Gdi::ACCESS_WRITE == access && Gdi::USER_GDI == user && + 0 == g_ddrawAccessor.writeAccessDepth) + { + auto primary(DDraw::PrimarySurface::getPrimary()); + if (!primary || DDraw::PrimarySurface::isGdiSurface(primary.get())) + { + DDraw::RealPrimarySurface::update(); + } + } + + Compat::LogLeave("endAccess", user, access); + } + + bool synchronize(Gdi::User user) + { + auto ddrawSurface(DDraw::PrimarySurface::getGdiSurface()); + if (!ddrawSurface) + { + return false; + } + + auto gdiSurface(Gdi::VirtualScreen::createSurface(DDraw::PrimarySurface::getMonitorRect())); + if (!gdiSurface) + { + return false; + } + + bool result = true; + g_isSyncing = true; + if (Gdi::USER_DDRAW == user) + { + CompatPtr clipper; + ddrawSurface->GetClipper(ddrawSurface, &clipper.getRef()); + ddrawSurface->SetClipper(ddrawSurface, nullptr); + result = SUCCEEDED(ddrawSurface->BltFast( + ddrawSurface, 0, 0, gdiSurface, nullptr, DDBLTFAST_WAIT)); + ddrawSurface->SetClipper(ddrawSurface, clipper); + } + else + { + result = SUCCEEDED(gdiSurface->BltFast( + gdiSurface, 0, 0, ddrawSurface, nullptr, DDBLTFAST_WAIT)); + } + g_isSyncing = false; + + return result; + } +} + +namespace Gdi +{ + AccessGuard::AccessGuard(User user, Access access, bool condition) + : m_user(user) + , m_access(access) + , m_condition(condition) + { + if (m_condition) + { + DDraw::ScopedThreadLock lock; + if (g_isSyncing) + { + m_condition = false; + return; + } + + beginAccess(user, access); + } + } + + AccessGuard::~AccessGuard() + { + if (m_condition) + { + DDraw::ScopedThreadLock lock; + endAccess(m_user, m_access); + } + } + + DDrawAccessGuard::DDrawAccessGuard(Access access, bool condition) + : AccessGuard(USER_DDRAW, access, condition) + { + } + + GdiAccessGuard::GdiAccessGuard(Access access, bool condition) + : AccessGuard(USER_GDI, access, condition) + { + } +} diff --git a/DDrawCompat/Gdi/AccessGuard.h b/DDrawCompat/Gdi/AccessGuard.h new file mode 100644 index 0000000..ad46533 --- /dev/null +++ b/DDrawCompat/Gdi/AccessGuard.h @@ -0,0 +1,40 @@ +#pragma once + +namespace Gdi +{ + enum Access + { + ACCESS_READ, + ACCESS_WRITE + }; + + enum User + { + USER_DDRAW, + USER_GDI + }; + + class AccessGuard + { + protected: + AccessGuard(User user, Access access, bool condition = true); + ~AccessGuard(); + + private: + User m_user; + Access m_access; + bool m_condition; + }; + + class DDrawAccessGuard : public AccessGuard + { + public: + DDrawAccessGuard(Access access, bool condition = true); + }; + + class GdiAccessGuard : public AccessGuard + { + public: + GdiAccessGuard(Access access, bool condition = true); + }; +} diff --git a/DDrawCompat/Gdi/Caret.cpp b/DDrawCompat/Gdi/Caret.cpp index 8d05094..27b7fac 100644 --- a/DDrawCompat/Gdi/Caret.cpp +++ b/DDrawCompat/Gdi/Caret.cpp @@ -4,6 +4,7 @@ #include "Common/Hook.h" #include "Common/ScopedCriticalSection.h" +#include "Gdi/AccessGuard.h" #include "Gdi/Caret.h" #include "Gdi/Dc.h" #include "Gdi/Gdi.h" @@ -103,11 +104,11 @@ namespace return; } - if ((g_caret.isVisible || newCaret.isVisible) && Gdi::beginGdiRendering()) + if ((g_caret.isVisible || newCaret.isVisible)) { + Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE); drawCaret(g_caret); drawCaret(newCaret); - Gdi::endGdiRendering(); } g_caret = newCaret; diff --git a/DDrawCompat/Gdi/Dc.cpp b/DDrawCompat/Gdi/Dc.cpp index 459458c..0c08796 100644 --- a/DDrawCompat/Gdi/Dc.cpp +++ b/DDrawCompat/Gdi/Dc.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "Common/Hook.h" #include "Common/Log.h" @@ -7,24 +8,27 @@ #include "Gdi/Dc.h" #include "Gdi/DcCache.h" #include "Gdi/Gdi.h" +#include "Gdi/VirtualScreen.h" #include "Gdi/Window.h" namespace { - using Gdi::DcCache::CachedDc; - - struct CompatDc : CachedDc + struct CompatDc { - CompatDc(const CachedDc& cachedDc = {}) : CachedDc(cachedDc) {} + HDC dc; DWORD refCount; HDC origDc; int savedState; }; + typedef std::unique_ptr OrigDc; typedef std::unordered_map CompatDcMap; - CompatDcMap g_origDcToCompatDc; - void copyDcAttributes(CompatDc& compatDc, HDC origDc, POINT& origin) + CRITICAL_SECTION g_cs; + CompatDcMap g_origDcToCompatDc; + thread_local std::vector g_threadDcs; + + void copyDcAttributes(const CompatDc& compatDc, HDC origDc, const POINT& origin) { SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_FONT)); SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_BRUSH)); @@ -76,6 +80,15 @@ namespace MoveToEx(compatDc.dc, currentPos.x, currentPos.y, nullptr); } + void deleteDc(HDC origDc) + { + Compat::ScopedCriticalSection lock(g_cs); + auto it = g_origDcToCompatDc.find(origDc); + RestoreDC(it->second.dc, it->second.savedState); + Gdi::DcCache::deleteDc(it->second.dc); + g_origDcToCompatDc.erase(origDc); + } + void setClippingRegion(HDC compatDc, HDC origDc, HWND hwnd, const POINT& origin) { if (hwnd) @@ -100,12 +113,16 @@ namespace void updateWindow(HWND wnd) { + auto window = Gdi::Window::get(wnd); + if (!window) + { + return; + } + RECT windowRect = {}; GetWindowRect(wnd, &windowRect); - auto& window = Gdi::Window::get(wnd); - RECT cachedWindowRect = window.getWindowRect(); - + RECT cachedWindowRect = window->getWindowRect(); if (!EqualRect(&windowRect, &cachedWindowRect)) { Gdi::Window::updateAll(); @@ -124,8 +141,7 @@ namespace Gdi return nullptr; } - Compat::ScopedCriticalSection gdiLock(Gdi::g_gdiCriticalSection); - + Compat::ScopedCriticalSection lock(g_cs); auto it = g_origDcToCompatDc.find(origDc); if (it != g_origDcToCompatDc.end()) { @@ -140,7 +156,8 @@ namespace Gdi updateWindow(rootWnd); } - CompatDc compatDc(Gdi::DcCache::getDc()); + CompatDc compatDc; + compatDc.dc = Gdi::DcCache::getDc(); if (!compatDc.dc) { return nullptr; @@ -148,6 +165,9 @@ namespace Gdi POINT origin = {}; GetDCOrgEx(origDc, &origin); + RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds(); + origin.x -= virtualScreenBounds.left; + origin.y -= virtualScreenBounds.top; compatDc.savedState = SaveDC(compatDc.dc); copyDcAttributes(compatDc, origDc, origin); @@ -156,21 +176,27 @@ namespace Gdi compatDc.refCount = 1; compatDc.origDc = origDc; g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc)); + g_threadDcs.emplace_back(origDc, &deleteDc); return compatDc.dc; } HDC getOrigDc(HDC dc) { + Compat::ScopedCriticalSection lock(g_cs); const auto it = std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(), [dc](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == dc; }); return it != g_origDcToCompatDc.end() ? it->first : dc; } + void init() + { + InitializeCriticalSection(&g_cs); + } + void releaseDc(HDC origDc) { - Compat::ScopedCriticalSection gdiLock(Gdi::g_gdiCriticalSection); - + Compat::ScopedCriticalSection lock(g_cs); auto it = g_origDcToCompatDc.find(origDc); if (it == g_origDcToCompatDc.end()) { @@ -181,8 +207,13 @@ namespace Gdi --compatDc.refCount; if (0 == compatDc.refCount) { + auto threadDcIter = std::find_if(g_threadDcs.begin(), g_threadDcs.end(), + [origDc](const OrigDc& dc) { return dc.get() == origDc; }); + threadDcIter->release(); + g_threadDcs.erase(threadDcIter); + RestoreDC(compatDc.dc, compatDc.savedState); - Gdi::DcCache::releaseDc(compatDc); + Gdi::DcCache::releaseDc(compatDc.dc); g_origDcToCompatDc.erase(origDc); } } diff --git a/DDrawCompat/Gdi/Dc.h b/DDrawCompat/Gdi/Dc.h index a3a78e1..36e2971 100644 --- a/DDrawCompat/Gdi/Dc.h +++ b/DDrawCompat/Gdi/Dc.h @@ -10,6 +10,7 @@ namespace Gdi { HDC getDc(HDC origDc); HDC getOrigDc(HDC dc); + void init(); void releaseDc(HDC origDc); } } diff --git a/DDrawCompat/Gdi/DcCache.cpp b/DDrawCompat/Gdi/DcCache.cpp index 06c3635..902642c 100644 --- a/DDrawCompat/Gdi/DcCache.cpp +++ b/DDrawCompat/Gdi/DcCache.cpp @@ -1,227 +1,45 @@ -#include +#include #include -#include "Common/CompatPtr.h" -#include "Common/Log.h" -#include "Config/Config.h" -#include "DDraw/Repository.h" -#include "DDraw/Surfaces/PrimarySurface.h" -#include "Dll/Procs.h" #include "Gdi/DcCache.h" +#include "Gdi/VirtualScreen.h" namespace { - using Gdi::DcCache::CachedDc; - - std::vector g_cache; - DWORD g_cacheSize = 0; - DWORD g_cacheId = 0; - DWORD g_maxUsedCacheSize = 0; - DWORD g_ddLockThreadId = 0; + typedef std::unique_ptr CachedDc; - CompatWeakPtr g_palette; - PALETTEENTRY g_paletteEntries[256] = {}; - void* g_surfaceMemory = nullptr; - LONG g_pitch = 0; - - CompatPtr createGdiSurface(); - - CachedDc createCachedDc() - { - CachedDc cachedDc = {}; - - CompatPtr surface(createGdiSurface()); - if (!surface) - { - return cachedDc; - } - - HDC dc = nullptr; - HRESULT result = surface->GetDC(surface, &dc); - if (FAILED(result)) - { - LOG_ONCE("Failed to create a GDI DC: " << result); - return cachedDc; - } - - // Release DD critical section acquired by IDirectDrawSurface7::GetDC to avoid deadlocks - Dll::g_origProcs.ReleaseDDThreadLock(); - - cachedDc.surface = surface.detach(); - cachedDc.dc = dc; - cachedDc.cacheId = g_cacheId; - return cachedDc; - } - - CompatPtr createGdiSurface() - { - DDSURFACEDESC2 desc = DDraw::PrimarySurface::getDesc(); - desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE; - desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; - desc.lPitch = g_pitch; - desc.lpSurface = g_surfaceMemory; - - auto dd(DDraw::Repository::getDirectDraw()); - CompatPtr surface; - HRESULT result = dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr); - if (FAILED(result)) - { - LOG_ONCE("Failed to create a GDI surface: " << result); - return nullptr; - } - - if (desc.ddpfPixelFormat.dwRGBBitCount <= 8) - { - surface->SetPalette(surface, g_palette); - } - - return surface; - } - - void extendCache() - { - if (g_cacheSize >= Config::preallocatedGdiDcCount) - { - LOG_ONCE("Warning: Preallocated GDI DC count is insufficient. This may lead to graphical issues."); - } - - if (GetCurrentThreadId() != g_ddLockThreadId) - { - return; - } - - for (DWORD i = 0; i < Config::preallocatedGdiDcCount; ++i) - { - CachedDc cachedDc = createCachedDc(); - if (!cachedDc.dc) - { - return; - } - g_cache.push_back(cachedDc); - ++g_cacheSize; - } - } - - void releaseCachedDc(CachedDc cachedDc) - { - // Reacquire DD critical section that was temporarily released after IDirectDrawSurface7::GetDC - Dll::g_origProcs.AcquireDDThreadLock(); - - HRESULT result = cachedDc.surface->ReleaseDC(cachedDc.surface, cachedDc.dc); - if (FAILED(result)) - { - LOG_ONCE("Failed to release a cached DC: " << result); - } - - cachedDc.surface.release(); - } + thread_local std::vector g_cache; } namespace Gdi { namespace DcCache { - void clear() + void deleteDc(HDC cachedDc) { - for (auto& cachedDc : g_cache) - { - releaseCachedDc(cachedDc); - } - g_cache.clear(); - g_cacheSize = 0; - ++g_cacheId; + Gdi::VirtualScreen::deleteDc(cachedDc); } - CachedDc getDc() + HDC getDc() { - CachedDc cachedDc = {}; - if (!g_surfaceMemory) - { - return cachedDc; - } + HDC cachedDc = nullptr; if (g_cache.empty()) { - extendCache(); + cachedDc = Gdi::VirtualScreen::createDc(); } - - if (!g_cache.empty()) + else { - cachedDc = g_cache.back(); + cachedDc = g_cache.back().release(); g_cache.pop_back(); - - const DWORD usedCacheSize = g_cacheSize - g_cache.size(); - if (usedCacheSize > g_maxUsedCacheSize) - { - g_maxUsedCacheSize = usedCacheSize; - Compat::Log() << "GDI used DC cache size: " << g_maxUsedCacheSize; - } } return cachedDc; } - bool init() + void releaseDc(HDC cachedDc) { - auto dd(DDraw::Repository::getDirectDraw()); - dd->CreatePalette(dd, - DDPCAPS_8BIT | DDPCAPS_ALLOW256, g_paletteEntries, &g_palette.getRef(), nullptr); - return nullptr != g_palette; - } - - void releaseDc(const CachedDc& cachedDc) - { - if (cachedDc.cacheId == g_cacheId) - { - g_cache.push_back(cachedDc); - } - else - { - releaseCachedDc(cachedDc); - } - } - - void setDdLockThreadId(DWORD ddLockThreadId) - { - g_ddLockThreadId = ddLockThreadId; - } - - void setSurfaceMemory(void* surfaceMemory, LONG pitch) - { - if (g_surfaceMemory == surfaceMemory && g_pitch == pitch) - { - return; - } - - g_surfaceMemory = surfaceMemory; - g_pitch = pitch; - clear(); - } - - void updatePalette(DWORD startingEntry, DWORD count) - { - PALETTEENTRY entries[256] = {}; - std::memcpy(&entries[startingEntry], - &DDraw::PrimarySurface::s_paletteEntries[startingEntry], - count * sizeof(PALETTEENTRY)); - - for (DWORD i = startingEntry; i < startingEntry + count; ++i) - { - if (entries[i].peFlags & PC_RESERVED) - { - entries[i] = DDraw::PrimarySurface::s_paletteEntries[0]; - entries[i].peFlags = DDraw::PrimarySurface::s_paletteEntries[i].peFlags; - } - } - - if (0 != std::memcmp(&g_paletteEntries[startingEntry], &entries[startingEntry], - count * sizeof(PALETTEENTRY))) - { - std::memcpy(&g_paletteEntries[startingEntry], &entries[startingEntry], - count * sizeof(PALETTEENTRY)); - g_palette->SetEntries(g_palette, 0, startingEntry, count, g_paletteEntries); - clear(); - } + g_cache.emplace_back(CachedDc(cachedDc, &deleteDc)); } } } diff --git a/DDrawCompat/Gdi/DcCache.h b/DDrawCompat/Gdi/DcCache.h index 6a88a6e..f5f68fa 100644 --- a/DDrawCompat/Gdi/DcCache.h +++ b/DDrawCompat/Gdi/DcCache.h @@ -1,30 +1,15 @@ #pragma once -#define CINTERFACE #define WIN32_LEAN_AND_MEAN -#include #include -#include "Common/CompatWeakPtr.h" - namespace Gdi { namespace DcCache { - struct CachedDc - { - CompatWeakPtr surface; - HDC dc; - DWORD cacheId; - }; - - void clear(); - CachedDc getDc(); - bool init(); - void releaseDc(const CachedDc& cachedDc); - void setDdLockThreadId(DWORD ddLockThreadId); - void setSurfaceMemory(void* surfaceMemory, LONG pitch); - void updatePalette(DWORD startingEntry, DWORD count); + void deleteDc(HDC cachedDc); + HDC getDc(); + void releaseDc(HDC cachedDc); } } diff --git a/DDrawCompat/Gdi/DcFunctions.cpp b/DDrawCompat/Gdi/DcFunctions.cpp index 7828dc9..15659cf 100644 --- a/DDrawCompat/Gdi/DcFunctions.cpp +++ b/DDrawCompat/Gdi/DcFunctions.cpp @@ -2,9 +2,11 @@ #include "Common/Hook.h" #include "Common/Log.h" +#include "Gdi/AccessGuard.h" #include "Gdi/Dc.h" #include "Gdi/DcFunctions.h" #include "Gdi/Gdi.h" +#include "Gdi/VirtualScreen.h" #include "Win32/DisplayMode.h" namespace @@ -80,21 +82,18 @@ namespace Compat::LogEnter(g_funcNames[origFunc], params...); #endif - if (!hasDisplayDcArg(params...) || - !Gdi::beginGdiRendering(getDdLockFlags(params...))) + Result result = 0; + if (hasDisplayDcArg(params...)) { - Result result = Compat::getOrigFuncPtr()(params...); - -#ifdef _DEBUG - Compat::LogLeave(g_funcNames[origFunc], params...) << result; -#endif - - return result; + const bool isReadOnlyAccess = getDdLockFlags(params...) & DDLOCK_READONLY; + Gdi::GdiAccessGuard accessGuard(isReadOnlyAccess ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE); + result = Compat::getOrigFuncPtr()(replaceDc(params)...); + releaseDc(params...); + } + else + { + result = Compat::getOrigFuncPtr()(params...); } - - Result result = Compat::getOrigFuncPtr()(replaceDc(params)...); - releaseDc(params...); - Gdi::endGdiRendering(); #ifdef _DEBUG Compat::LogLeave(g_funcNames[origFunc], params...) << result; @@ -254,6 +253,13 @@ namespace return 1; } + UINT WINAPI realizePalette(HDC hdc) + { + UINT result = CALL_ORIG_FUNC(RealizePalette)(hdc); + Gdi::VirtualScreen::updatePalette(); + return result; + } + HWND WINAPI windowFromDc(HDC dc) { return CALL_ORIG_FUNC(WindowFromDC)(Gdi::Dc::getOrigDc(dc)); @@ -311,6 +317,9 @@ namespace Gdi // Clipping functions HOOK_FUNCTION(gdi32, GetRandomRgn, getRandomRgn); + // Color functions + HOOK_SHIM_FUNCTION(RealizePalette, realizePalette); + // Device context functions HOOK_GDI_DC_FUNCTION(gdi32, DrawEscape); HOOK_FUNCTION(user32, WindowFromDC, windowFromDc); diff --git a/DDrawCompat/Gdi/Gdi.cpp b/DDrawCompat/Gdi/Gdi.cpp index 32d7e9b..a2f891e 100644 --- a/DDrawCompat/Gdi/Gdi.cpp +++ b/DDrawCompat/Gdi/Gdi.cpp @@ -1,152 +1,37 @@ -#include "Common/ScopedCriticalSection.h" -#include "DDraw/RealPrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h" -#include "Dll/Procs.h" #include "Gdi/Caret.h" -#include "Gdi/DcCache.h" +#include "Gdi/Dc.h" #include "Gdi/DcFunctions.h" #include "Gdi/Gdi.h" #include "Gdi/PaintHandlers.h" #include "Gdi/ScrollFunctions.h" +#include "Gdi/Window.h" #include "Gdi/WinProc.h" namespace { DWORD g_gdiThreadId = 0; - DWORD g_renderingRefCount = 0; - DWORD g_ddLockFlags = 0; - DWORD g_ddLockThreadRenderingRefCount = 0; - DWORD g_ddLockThreadId = 0; - HANDLE g_ddUnlockBeginEvent = nullptr; - HANDLE g_ddUnlockEndEvent = nullptr; - bool g_isDelayedUnlockPending = false; - - bool lockGdiSurface(DWORD lockFlags) - { - DDSURFACEDESC2 desc = {}; - desc.dwSize = sizeof(desc); - auto gdiSurface(DDraw::PrimarySurface::getGdiSurface()); - if (!gdiSurface || FAILED(gdiSurface.get()->lpVtbl->Lock( - gdiSurface, nullptr, &desc, lockFlags | DDLOCK_WAIT, nullptr))) - { - return false; - } - - g_ddLockFlags = lockFlags; - if (0 != lockFlags) - { - EnterCriticalSection(&Gdi::g_gdiCriticalSection); - } - - g_ddLockThreadId = GetCurrentThreadId(); - Gdi::DcCache::setDdLockThreadId(g_ddLockThreadId); - Gdi::DcCache::setSurfaceMemory(desc.lpSurface, desc.lPitch); - return true; - } + HDC g_screenDc = nullptr; BOOL CALLBACK redrawWindowCallback(HWND hwnd, LPARAM lParam) { Gdi::redrawWindow(hwnd, reinterpret_cast(lParam)); return TRUE; } - - void unlockGdiSurface() - { - GdiFlush(); - auto gdiSurface(DDraw::PrimarySurface::getGdiSurface()); - if (gdiSurface) - { - gdiSurface.get()->lpVtbl->Unlock(gdiSurface, nullptr); - if (DDLOCK_READONLY != g_ddLockFlags) - { - DDraw::RealPrimarySurface::update(); - } - } - - if (0 != g_ddLockFlags) - { - LeaveCriticalSection(&Gdi::g_gdiCriticalSection); - } - g_ddLockFlags = 0; - - Dll::g_origProcs.ReleaseDDThreadLock(); - } } namespace Gdi { - CRITICAL_SECTION g_gdiCriticalSection; - - bool beginGdiRendering(DWORD lockFlags) - { - Compat::ScopedCriticalSection gdiLock(g_gdiCriticalSection); - - if (0 == g_renderingRefCount) - { - LeaveCriticalSection(&g_gdiCriticalSection); - Dll::g_origProcs.AcquireDDThreadLock(); - EnterCriticalSection(&g_gdiCriticalSection); - if (!lockGdiSurface(lockFlags)) - { - Dll::g_origProcs.ReleaseDDThreadLock(); - return false; - } - } - - if (GetCurrentThreadId() == g_ddLockThreadId) - { - ++g_ddLockThreadRenderingRefCount; - } - - ++g_renderingRefCount; - return true; - } - - void endGdiRendering() - { - Compat::ScopedCriticalSection gdiLock(g_gdiCriticalSection); - - if (GetCurrentThreadId() == g_ddLockThreadId) - { - if (1 == g_renderingRefCount) - { - unlockGdiSurface(); - g_ddLockThreadRenderingRefCount = 0; - g_renderingRefCount = 0; - } - else if (1 == g_ddLockThreadRenderingRefCount) - { - g_isDelayedUnlockPending = true; - gdiLock.unlock(); - WaitForSingleObject(g_ddUnlockBeginEvent, INFINITE); - unlockGdiSurface(); - g_ddLockThreadRenderingRefCount = 0; - g_renderingRefCount = 0; - SetEvent(g_ddUnlockEndEvent); - } - else - { - --g_ddLockThreadRenderingRefCount; - --g_renderingRefCount; - } - } - else - { - --g_renderingRefCount; - if (1 == g_renderingRefCount && g_isDelayedUnlockPending) - { - SetEvent(g_ddUnlockBeginEvent); - WaitForSingleObject(g_ddUnlockEndEvent, INFINITE); - g_isDelayedUnlockPending = false; - } - } - } - DWORD getGdiThreadId() { return g_gdiThreadId; } + HDC getScreenDc() + { + return g_screenDc; + } + HRGN getVisibleWindowRgn(HWND hwnd) { return DcFunctions::getVisibleWindowRgn(hwnd); @@ -163,39 +48,24 @@ namespace Gdi void installHooks() { g_gdiThreadId = GetCurrentThreadId(); - InitializeCriticalSection(&g_gdiCriticalSection); - if (Gdi::DcCache::init()) - { - g_ddUnlockBeginEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - g_ddUnlockEndEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (!g_ddUnlockBeginEvent || !g_ddUnlockEndEvent) - { - Compat::Log() << "Failed to create the unlock events for GDI"; - return; - } + g_screenDc = GetDC(nullptr); - Gdi::DcFunctions::installHooks(); - Gdi::PaintHandlers::installHooks(); - Gdi::ScrollFunctions::installHooks(); - Gdi::WinProc::installHooks(); - Gdi::Caret::installHooks(); - } - } - - bool isTopLevelWindow(HWND hwnd) - { - return !(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || - GetParent(hwnd) == GetDesktopWindow(); + Gdi::Dc::init(); + Gdi::DcFunctions::installHooks(); + Gdi::PaintHandlers::installHooks(); + Gdi::ScrollFunctions::installHooks(); + Gdi::WinProc::installHooks(); + Gdi::Caret::installHooks(); } void redraw(HRGN rgn) { - EnumThreadWindows(GetCurrentThreadId(), &redrawWindowCallback, reinterpret_cast(rgn)); + EnumThreadWindows(g_gdiThreadId, &redrawWindowCallback, reinterpret_cast(rgn)); } void redrawWindow(HWND hwnd, HRGN rgn) { - if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) + if (!IsWindowVisible(hwnd) || IsIconic(hwnd) || Window::isPresentationWindow(hwnd)) { return; } @@ -228,14 +98,7 @@ namespace Gdi Gdi::Caret::uninstallHooks(); Gdi::WinProc::uninstallHooks(); Gdi::PaintHandlers::uninstallHooks(); - } - - void updatePalette(DWORD startingEntry, DWORD count) - { - if (DDraw::PrimarySurface::s_palette) - { - Gdi::DcCache::updatePalette(startingEntry, count); - } + ReleaseDC(nullptr, g_screenDc); } void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc) diff --git a/DDrawCompat/Gdi/Gdi.h b/DDrawCompat/Gdi/Gdi.h index 4cae620..1052360 100644 --- a/DDrawCompat/Gdi/Gdi.h +++ b/DDrawCompat/Gdi/Gdi.h @@ -8,20 +8,14 @@ namespace Gdi { typedef void(*WindowPosChangeNotifyFunc)(); - bool beginGdiRendering(DWORD lockFlags = 0); - void endGdiRendering(); - DWORD getGdiThreadId(); + HDC getScreenDc(); HRGN getVisibleWindowRgn(HWND hwnd); void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc); void installHooks(); - bool isTopLevelWindow(HWND hwnd); void redraw(HRGN rgn); void redrawWindow(HWND hwnd, HRGN rgn); void unhookWndProc(LPCSTR className, WNDPROC oldWndProc); void uninstallHooks(); - void updatePalette(DWORD startingEntry, DWORD count); void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc); - - extern CRITICAL_SECTION g_gdiCriticalSection; }; diff --git a/DDrawCompat/Gdi/PaintHandlers.cpp b/DDrawCompat/Gdi/PaintHandlers.cpp index 8b529ef..4368d8a 100644 --- a/DDrawCompat/Gdi/PaintHandlers.cpp +++ b/DDrawCompat/Gdi/PaintHandlers.cpp @@ -1,6 +1,7 @@ #include "Common/Hook.h" #include "Common/Log.h" #include "DDraw/RealPrimarySurface.h" +#include "Gdi/AccessGuard.h" #include "Gdi/Dc.h" #include "Gdi/Gdi.h" #include "Gdi/PaintHandlers.h" @@ -185,19 +186,15 @@ namespace LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc) { - if (hwnd && Gdi::beginGdiRendering()) + if (hwnd) { LRESULT result = 0; HDC compatDc = Gdi::Dc::getDc(dc); if (compatDc) { + Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE); result = CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast(compatDc), 0); Gdi::Dc::releaseDc(dc); - } - - Gdi::endGdiRendering(); - if (compatDc) - { return result; } } @@ -207,7 +204,7 @@ namespace LRESULT onMenuPaint(HWND hwnd, WNDPROC origWndProc) { - if (!hwnd || !Gdi::beginGdiRendering()) + if (!hwnd) { return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0); } @@ -216,6 +213,7 @@ namespace HDC compatDc = Gdi::Dc::getDc(dc); if (compatDc) { + Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE); CallWindowProc(origWndProc, hwnd, WM_PRINT, reinterpret_cast(compatDc), PRF_NONCLIENT | PRF_ERASEBKGND | PRF_CLIENT); ValidateRect(hwnd, nullptr); @@ -227,13 +225,12 @@ namespace } ReleaseDC(hwnd, dc); - Gdi::endGdiRendering(); return 0; } LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc) { - if (!hwnd || !Gdi::beginGdiRendering()) + if (!hwnd) { return CallWindowProc(origWndProc, hwnd, WM_NCPAINT, wParam, 0); } @@ -243,6 +240,7 @@ namespace if (compatDc) { + Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE); Gdi::TitleBar titleBar(hwnd, compatDc); titleBar.drawAll(); titleBar.excludeFromClipRegion(); @@ -257,13 +255,12 @@ namespace } ReleaseDC(hwnd, windowDc); - Gdi::endGdiRendering(); return 0; } LRESULT onPaint(HWND hwnd, WNDPROC origWndProc) { - if (!hwnd || !Gdi::beginGdiRendering()) + if (!hwnd) { return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0); } @@ -274,6 +271,7 @@ namespace if (compatDc) { + Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE); CallWindowProc(origWndProc, hwnd, WM_PRINTCLIENT, reinterpret_cast(compatDc), PRF_CLIENT); Gdi::Dc::releaseDc(dc); @@ -285,21 +283,16 @@ namespace EndPaint(hwnd, &paint); - Gdi::endGdiRendering(); return 0; } LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc) { - if (!Gdi::beginGdiRendering()) - { - return CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast(dc), flags); - } - LRESULT result = 0; HDC compatDc = Gdi::Dc::getDc(dc); if (compatDc) { + Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE); result = CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast(compatDc), flags); Gdi::Dc::releaseDc(dc); } @@ -307,8 +300,6 @@ namespace { result = CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast(dc), flags); } - - Gdi::endGdiRendering(); return result; } diff --git a/DDrawCompat/Gdi/Region.cpp b/DDrawCompat/Gdi/Region.cpp index e355a1e..0279b51 100644 --- a/DDrawCompat/Gdi/Region.cpp +++ b/DDrawCompat/Gdi/Region.cpp @@ -1,26 +1,9 @@ #include #include "Gdi/Region.h" -#include "Win32/DisplayMode.h" namespace { - BOOL CALLBACK addMonitorRectToRegion( - HMONITOR /*hMonitor*/, HDC /*hdcMonitor*/, LPRECT lprcMonitor, LPARAM dwData) - { - Gdi::Region& virtualScreenRegion = *reinterpret_cast(dwData); - Gdi::Region monitorRegion(*lprcMonitor); - virtualScreenRegion |= monitorRegion; - return TRUE; - } - - Gdi::Region calculateVirtualScreenRegion() - { - Gdi::Region region; - EnumDisplayMonitors(nullptr, nullptr, addMonitorRectToRegion, reinterpret_cast(®ion)); - return region; - } - Gdi::Region combineRegions(const Gdi::Region& rgn1, const Gdi::Region& rgn2, int mode) { Gdi::Region region; @@ -132,17 +115,4 @@ namespace Gdi CombineRgn(m_region, m_region, other, mode); return *this; } - - const Region& getVirtualScreenRegion() - { - static Region virtualScreenRegion; - static ULONG displaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1; - const ULONG currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness(); - if (currentDisplaySettingsUniqueness != displaySettingsUniqueness) - { - virtualScreenRegion = calculateVirtualScreenRegion(); - displaySettingsUniqueness = currentDisplaySettingsUniqueness; - } - return virtualScreenRegion; - } } diff --git a/DDrawCompat/Gdi/Region.h b/DDrawCompat/Gdi/Region.h index 9fa4bea..eb5529a 100644 --- a/DDrawCompat/Gdi/Region.h +++ b/DDrawCompat/Gdi/Region.h @@ -35,6 +35,4 @@ namespace Gdi HRGN m_region; }; - - const Region& getVirtualScreenRegion(); } diff --git a/DDrawCompat/Gdi/VirtualScreen.cpp b/DDrawCompat/Gdi/VirtualScreen.cpp new file mode 100644 index 0000000..fe25eb5 --- /dev/null +++ b/DDrawCompat/Gdi/VirtualScreen.cpp @@ -0,0 +1,244 @@ +#include + +#include "Common/ScopedCriticalSection.h" +#include "DDraw/DirectDraw.h" +#include "DDraw/Repository.h" +#include "Gdi/Gdi.h" +#include "Gdi/Region.h" +#include "Gdi/VirtualScreen.h" +#include "Win32/DisplayMode.h" + +namespace +{ + CRITICAL_SECTION g_cs = {}; + Gdi::Region g_region; + RECT g_bounds = {}; + DWORD g_bpp = 0; + LONG g_width = 0; + LONG g_height = 0; + DWORD g_pitch = 0; + HANDLE g_surfaceFileMapping = nullptr; + void* g_surfaceView = nullptr; + + HGDIOBJ g_stockBitmap = nullptr; + RGBQUAD g_systemPalette[256] = {}; + std::set g_dcs; + + BOOL CALLBACK addMonitorRectToRegion( + HMONITOR /*hMonitor*/, HDC /*hdcMonitor*/, LPRECT lprcMonitor, LPARAM dwData) + { + Gdi::Region& virtualScreenRegion = *reinterpret_cast(dwData); + Gdi::Region monitorRegion(*lprcMonitor); + virtualScreenRegion |= monitorRegion; + return TRUE; + } +} + +namespace Gdi +{ + namespace VirtualScreen + { + HDC createDc() + { + Compat::ScopedCriticalSection lock(g_cs); + std::unique_ptr dib(createDib(), DeleteObject); + if (!dib) + { + return nullptr; + } + + std::unique_ptr dc(CreateCompatibleDC(nullptr), DeleteDC); + if (!dc) + { + return nullptr; + } + + HGDIOBJ stockBitmap = SelectObject(dc.get(), dib.get()); + if (!stockBitmap) + { + return nullptr; + } + + dib.release(); + + + g_stockBitmap = stockBitmap; + g_dcs.insert(dc.get()); + return dc.release(); + } + + HBITMAP createDib() + { + Compat::ScopedCriticalSection lock(g_cs); + if (!g_surfaceFileMapping) + { + return nullptr; + } + + struct BITMAPINFO256 : public BITMAPINFO + { + RGBQUAD bmiRemainingColors[255]; + }; + + BITMAPINFO256 bmi = {}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = g_width; + bmi.bmiHeader.biHeight = -g_height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = static_cast(g_bpp); + bmi.bmiHeader.biCompression = 8 == g_bpp ? BI_RGB : BI_BITFIELDS; + + if (8 == g_bpp) + { + memcpy(bmi.bmiColors, g_systemPalette, sizeof(g_systemPalette)); + } + else + { + const auto pf = DDraw::getRgbPixelFormat(g_bpp); + reinterpret_cast(bmi.bmiColors[0]) = pf.dwRBitMask; + reinterpret_cast(bmi.bmiColors[1]) = pf.dwGBitMask; + reinterpret_cast(bmi.bmiColors[2]) = pf.dwBBitMask; + } + + void* bits = nullptr; + return CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, g_surfaceFileMapping, 0); + } + + CompatPtr createSurface(const RECT& rect) + { + if (rect.left < g_bounds.left || rect.top < g_bounds.top || + rect.right > g_bounds.right || rect.bottom > g_bounds.bottom) + { + return nullptr; + } + + Compat::ScopedCriticalSection lock(g_cs); + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE; + desc.dwWidth = rect.right - rect.left; + desc.dwHeight = rect.bottom - rect.top; + desc.ddpfPixelFormat = DDraw::getRgbPixelFormat(g_bpp); + desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + desc.lPitch = g_pitch; + desc.lpSurface = static_cast(g_surfaceView) + + (rect.top - g_bounds.top) * g_pitch + + (rect.left - g_bounds.left) * g_bpp / 8; + + CompatPtr surface; + auto dd(DDraw::Repository::getDirectDraw()); + dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr); + return surface; + } + + void deleteDc(HDC dc) + { + if (!dc) + { + return; + } + + Compat::ScopedCriticalSection lock(g_cs); + DeleteObject(SelectObject(dc, g_stockBitmap)); + DeleteDC(dc); + g_dcs.erase(dc); + } + + RECT getBounds() + { + Compat::ScopedCriticalSection lock(g_cs); + return g_bounds; + } + + const Region& getRegion() + { + Compat::ScopedCriticalSection lock(g_cs); + return g_region; + } + + void init() + { + InitializeCriticalSection(&g_cs); + update(); + updatePalette(); + } + + bool update() + { + Compat::LogEnter("VirtualScreen::update"); + Compat::ScopedCriticalSection lock(g_cs); + + static auto prevDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1; + const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness(); + if (currentDisplaySettingsUniqueness == prevDisplaySettingsUniqueness) + { + Compat::LogLeave("VirtualScreen::update") << false; + return false; + } + + prevDisplaySettingsUniqueness = currentDisplaySettingsUniqueness; + + g_region = Region(); + EnumDisplayMonitors(nullptr, nullptr, addMonitorRectToRegion, reinterpret_cast(&g_region)); + GetRgnBox(g_region, &g_bounds); + + g_bpp = Win32::DisplayMode::getBpp(); + g_width = g_bounds.right - g_bounds.left; + g_height = g_bounds.bottom - g_bounds.top; + g_pitch = (g_width * g_bpp / 8 + 3) & ~3; + + if (g_surfaceFileMapping) + { + for (HDC dc : g_dcs) + { + DeleteObject(SelectObject(dc, g_stockBitmap)); + } + UnmapViewOfFile(g_surfaceView); + CloseHandle(g_surfaceFileMapping); + } + + g_surfaceFileMapping = CreateFileMapping( + INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, g_pitch * g_height, nullptr); + g_surfaceView = MapViewOfFile(g_surfaceFileMapping, FILE_MAP_WRITE, 0, 0, 0); + + for (HDC dc : g_dcs) + { + SelectObject(dc, createDib()); + } + + Gdi::redraw(nullptr); + + Compat::LogLeave("VirtualScreen::update") << true; + return true; + } + + void updatePalette() + { + Compat::ScopedCriticalSection lock(g_cs); + if (8 != g_bpp) + { + return; + } + + PALETTEENTRY pal[256] = {}; + GetSystemPaletteEntries(nullptr, 0, 256, pal); + + RGBQUAD systemPalette[256] = {}; + for (int i = 0; i < 256; ++i) + { + systemPalette[i].rgbRed = pal[i].peRed; + systemPalette[i].rgbGreen = pal[i].peGreen; + systemPalette[i].rgbBlue = pal[i].peBlue; + } + + if (0 != memcmp(g_systemPalette, systemPalette, sizeof(systemPalette))) + { + memcpy(g_systemPalette, systemPalette, sizeof(systemPalette)); + for (HDC dc : g_dcs) + { + SetDIBColorTable(dc, 0, 256, systemPalette); + } + } + } + } +} diff --git a/DDrawCompat/Gdi/VirtualScreen.h b/DDrawCompat/Gdi/VirtualScreen.h new file mode 100644 index 0000000..38b791f --- /dev/null +++ b/DDrawCompat/Gdi/VirtualScreen.h @@ -0,0 +1,27 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include + +#include "Common/CompatPtr.h" + +namespace Gdi +{ + class Region; + + namespace VirtualScreen + { + HDC createDc(); + HBITMAP createDib(); + CompatPtr createSurface(const RECT& rect); + void deleteDc(HDC dc); + + RECT getBounds(); + const Region& getRegion(); + + void init(); + bool update(); + void updatePalette(); + } +} diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index 7ed7762..960a8f6 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -9,6 +9,7 @@ #include "Common/Log.h" #include "Common/ScopedCriticalSection.h" +#include "Gdi/AccessGuard.h" #include "Gdi/Dc.h" #include "Gdi/ScrollBar.h" #include "Gdi/ScrollFunctions.h" @@ -94,6 +95,12 @@ namespace return TRUE; } + bool isTopLevelNonLayeredWindow(HWND hwnd) + { + return !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) && + (!(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow()); + } + void CALLBACK objectStateChangeEvent( HWINEVENTHOOK /*hWinEventHook*/, DWORD /*event*/, @@ -105,7 +112,7 @@ namespace { if (OBJID_TITLEBAR == idObject || OBJID_HSCROLL == idObject || OBJID_VSCROLL == idObject) { - if (!hwnd || !Gdi::beginGdiRendering()) + if (!hwnd) { return; } @@ -114,6 +121,7 @@ namespace HDC compatDc = Gdi::Dc::getDc(windowDc); if (compatDc) { + Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE); if (OBJID_TITLEBAR == idObject) { Gdi::TitleBar(hwnd, compatDc).drawButtons(); @@ -128,9 +136,7 @@ namespace } Gdi::Dc::releaseDc(windowDc); } - ReleaseDC(hwnd, windowDc); - Gdi::endGdiRendering(); } } @@ -154,22 +160,17 @@ namespace void onCreateWindow(HWND hwnd) { - if (Gdi::isTopLevelWindow(hwnd)) + if (isTopLevelNonLayeredWindow(hwnd)) { disableDwmAttributes(hwnd); removeDropShadow(hwnd); - Compat::ScopedCriticalSection lock(Gdi::g_gdiCriticalSection); Gdi::Window::add(hwnd); } } void onDestroyWindow(HWND hwnd) { - if (Gdi::isTopLevelWindow(hwnd)) - { - Compat::ScopedCriticalSection lock(Gdi::g_gdiCriticalSection); - Gdi::Window::remove(hwnd); - } + Gdi::Window::remove(hwnd); } void onMenuSelect() @@ -190,19 +191,15 @@ namespace SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED); } - if (!Gdi::isTopLevelWindow(hwnd)) - { - return; - } - - Compat::ScopedCriticalSection lock(Gdi::g_gdiCriticalSection); - for (auto notifyFunc : g_windowPosChangeNotifyFuncs) { notifyFunc(); } - Gdi::Window::updateAll(); + if (isTopLevelNonLayeredWindow(hwnd)) + { + Gdi::Window::updateAll(); + } } void removeDropShadow(HWND hwnd) diff --git a/DDrawCompat/Gdi/Window.cpp b/DDrawCompat/Gdi/Window.cpp index 020a45e..703bfcb 100644 --- a/DDrawCompat/Gdi/Window.cpp +++ b/DDrawCompat/Gdi/Window.cpp @@ -1,6 +1,34 @@ #include "Gdi/Gdi.h" +#include "Gdi/VirtualScreen.h" #include "Gdi/Window.h" +extern "C" IMAGE_DOS_HEADER __ImageBase; + +namespace +{ + ATOM registerPresentationWindowClass(); + + ATOM getPresentationWindowClassAtom() + { + static ATOM atom = registerPresentationWindowClass(); + return atom; + } + + LRESULT CALLBACK presentationWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return CALL_ORIG_FUNC(DefWindowProc)(hwnd, uMsg, wParam, lParam); + } + + ATOM registerPresentationWindowClass() + { + WNDCLASS wc = {}; + wc.lpfnWndProc = &presentationWindowProc; + wc.hInstance = reinterpret_cast(&__ImageBase); + wc.lpszClassName = "DDrawCompatPresentationWindow"; + return RegisterClass(&wc); + } +} + namespace Gdi { Window::Window(HWND hwnd) @@ -8,18 +36,34 @@ namespace Gdi , m_windowRect{ 0, 0, 0, 0 } , m_isUpdating(false) { + m_presentationWindow = CreateWindowEx( + WS_EX_LAYERED | WS_EX_TRANSPARENT, + reinterpret_cast(getPresentationWindowClassAtom()), + nullptr, + WS_DISABLED | WS_POPUP, + 0, 0, 1, 1, + m_hwnd, + nullptr, + nullptr, + nullptr); + SetLayeredWindowAttributes(m_presentationWindow, 0, 255, LWA_ALPHA); update(); } - Window& Window::add(HWND hwnd) + Window* Window::add(HWND hwnd) { auto it = s_windows.find(hwnd); if (it != s_windows.end()) { - return it->second; + return &it->second; } - return s_windows.emplace(hwnd, hwnd).first->second; + if (isPresentationWindow(hwnd)) + { + return nullptr; + } + + return &s_windows.emplace(hwnd, hwnd).first->second; } void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion) @@ -41,21 +85,22 @@ namespace Gdi if (!preservedRegion.isEmpty()) { - HDC screenDc = GetDC(nullptr); + HDC screenDc = Gdi::getScreenDc(); SelectClipRgn(screenDc, preservedRegion); BitBlt(screenDc, m_windowRect.left, m_windowRect.top, oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top, screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY); - ReleaseDC(nullptr, screenDc); + SelectClipRgn(screenDc, nullptr); m_invalidatedRegion -= preservedRegion; } } } - Window& Window::get(HWND hwnd) + Window* Window::get(HWND hwnd) { - return add(hwnd); + auto it = s_windows.find(hwnd); + return it != s_windows.end() ? &it->second : nullptr; } Region Window::getVisibleRegion() const @@ -68,6 +113,11 @@ namespace Gdi return m_windowRect; } + bool Window::isPresentationWindow(HWND hwnd) + { + return GetClassLong(hwnd, GCW_ATOM) == getPresentationWindowClassAtom(); + } + void Window::remove(HWND hwnd) { s_windows.erase(hwnd); @@ -92,8 +142,12 @@ namespace Gdi HDC windowDc = GetWindowDC(m_hwnd); GetRandomRgn(windowDc, newVisibleRegion, SYSRGN); ReleaseDC(m_hwnd, windowDc); - newVisibleRegion &= getVirtualScreenRegion(); + newVisibleRegion &= VirtualScreen::getRegion(); } + + SetWindowPos(m_presentationWindow, nullptr, newWindowRect.left, newWindowRect.top, + newWindowRect.right - newWindowRect.left, newWindowRect.bottom - newWindowRect.top, + SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW); } std::swap(m_windowRect, newWindowRect); diff --git a/DDrawCompat/Gdi/Window.h b/DDrawCompat/Gdi/Window.h index 180b673..dc138ba 100644 --- a/DDrawCompat/Gdi/Window.h +++ b/DDrawCompat/Gdi/Window.h @@ -20,9 +20,11 @@ namespace Gdi Region getVisibleRegion() const; RECT getWindowRect() const; - static Window& add(HWND hwnd); - static Window& get(HWND hwnd); + static Window* add(HWND hwnd); + static Window* get(HWND hwnd); static void remove(HWND hwnd); + + static bool isPresentationWindow(HWND hwnd); static void updateAll(); private: @@ -30,6 +32,7 @@ namespace Gdi void update(); HWND m_hwnd; + HWND m_presentationWindow; RECT m_windowRect; Region m_visibleRegion; Region m_invalidatedRegion; diff --git a/DDrawCompat/Win32/DisplayMode.cpp b/DDrawCompat/Win32/DisplayMode.cpp index 45694ff..b610a1d 100644 --- a/DDrawCompat/Win32/DisplayMode.cpp +++ b/DDrawCompat/Win32/DisplayMode.cpp @@ -372,6 +372,11 @@ namespace Win32 return result; } + DWORD getBpp() + { + return g_currentBpp; + } + ULONG queryDisplaySettingsUniqueness() { static auto ddQueryDisplaySettingsUniqueness = reinterpret_cast( diff --git a/DDrawCompat/Win32/DisplayMode.h b/DDrawCompat/Win32/DisplayMode.h index 6f4d39b..33d3cdf 100644 --- a/DDrawCompat/Win32/DisplayMode.h +++ b/DDrawCompat/Win32/DisplayMode.h @@ -13,6 +13,7 @@ namespace Win32 const void* lpbInit, const BITMAPINFO* lpbmi, UINT fuUsage); HBITMAP WINAPI createDiscardableBitmap(HDC hdc, int nWidth, int nHeight); + DWORD getBpp(); ULONG queryDisplaySettingsUniqueness(); void setDDrawBpp(DWORD bpp); diff --git a/DDrawCompat/Win32/FontSmoothing.cpp b/DDrawCompat/Win32/FontSmoothing.cpp index da0a9ba..6c489cb 100644 --- a/DDrawCompat/Win32/FontSmoothing.cpp +++ b/DDrawCompat/Win32/FontSmoothing.cpp @@ -52,8 +52,7 @@ namespace Win32 reinterpret_cast(settings.orientation), 0); const char* regKey = "FontSmoothing"; - SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETFONTSMOOTHING, - reinterpret_cast(regKey), SMTO_BLOCK, 100, nullptr); + PostMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETFONTSMOOTHING, reinterpret_cast(regKey)); RedrawWindow(nullptr, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); } }