diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index 47b5b4a..0b3dd88 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -22,10 +22,17 @@ #include #include #include +#include #include +#include namespace { + WNDPROC g_origDDrawWindowProc = nullptr; + + LRESULT handleActivateApp(HWND hwnd, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc); + LRESULT handleSize(HWND hwnd, WPARAM wParam, LPARAM lParam); + template HRESULT STDMETHODCALLTYPE CreateSurface( TDirectDraw* This, TSurfaceDesc* lpDDSurfaceDesc, TSurface** lplpDDSurface, IUnknown* pUnkOuter) @@ -156,6 +163,99 @@ namespace return getOrigVtable(This).WaitForVerticalBlank(This, dwFlags, hEvent); } + LRESULT WINAPI ddrawWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LOG_FUNC("ddrawWindowProc", hwnd, uMsg, wParam, lParam); + switch (uMsg) + { + case WM_ACTIVATEAPP: + return LOG_RESULT(handleActivateApp(hwnd, wParam, lParam, Gdi::WinProc::getDDrawOrigWndProc(hwnd))); + case WM_SIZE: + return LOG_RESULT(handleSize(hwnd, wParam, lParam)); + } + return LOG_RESULT(g_origDDrawWindowProc(hwnd, uMsg, wParam, lParam)); + } + + LRESULT handleActivateApp(HWND hwnd, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) + { + LOG_FUNC("DirectDraw::handleActivateApp", hwnd, wParam, lParam, origWndProc); + + if (origWndProc) + { + auto tagSurface = DDraw::TagSurface::findFullscreenWindow(); + if (tagSurface && tagSurface->getExclusiveOwnerThreadId() != GetCurrentThreadId()) + { + if (!wParam) + { + ShowWindow(hwnd, SW_SHOWMINNOACTIVE); + } + return LOG_RESULT(CallWindowProcA(origWndProc, hwnd, WM_ACTIVATEAPP, wParam, lParam)); + } + } + + if (Config::Settings::AltTabFix::OFF == Config::altTabFix.get()) + { + return LOG_RESULT(g_origDDrawWindowProc(hwnd, WM_ACTIVATEAPP, wParam, lParam)); + } + + DDraw::ScopedThreadLock lock; + const bool keepPrimary = Config::Settings::AltTabFix::KEEPVIDMEM == Config::altTabFix.get(); + std::set surfacesToRestore; + DDraw::Surface::enumSurfaces([&](const DDraw::Surface& surface) + { + auto lcl = DDraw::DirectDrawSurface::getInt(*surface.getDDS()).lpLcl; + if (!(lcl->dwFlags & DDRAWISURF_INVALID) && + (keepPrimary || !(surface.getOrigCaps() & DDSCAPS_PRIMARYSURFACE))) + { + lcl->dwFlags |= DDRAWISURF_INVALID; + surfacesToRestore.insert(lcl); + } + }); + + LRESULT result = g_origDDrawWindowProc(hwnd, WM_ACTIVATEAPP, wParam, lParam); + + DDraw::Surface::enumSurfaces([&](const DDraw::Surface& surface) + { + auto lcl = DDraw::DirectDrawSurface::getInt(*surface.getDDS()).lpLcl; + auto it = surfacesToRestore.find(lcl); + if (it != surfacesToRestore.end()) + { + lcl->dwFlags &= ~DDRAWISURF_INVALID; + surfacesToRestore.erase(it); + } + }); + + if (wParam && keepPrimary) + { + auto realPrimary(DDraw::RealPrimarySurface::getSurface()); + if (realPrimary) + { + realPrimary->Restore(realPrimary); + auto gdiResource = DDraw::PrimarySurface::getGdiResource(); + if (gdiResource) + { + D3dDdi::Device::setGdiResourceHandle(gdiResource); + } + } + } + + return LOG_RESULT(result); + } + + LRESULT handleSize(HWND hwnd, WPARAM wParam, LPARAM lParam) + { + LOG_FUNC("DirectDraw::handleSize", hwnd, wParam, lParam); + LRESULT result = 0; + auto tagSurface = DDraw::TagSurface::findFullscreenWindow(); + if (tagSurface && tagSurface->getExclusiveOwnerThreadId() != GetCurrentThreadId()) + { + Win32::Thread::skipWaitingForExclusiveModeMutex(true); + } + result = g_origDDrawWindowProc(hwnd, WM_SIZE, wParam, lParam); + Win32::Thread::skipWaitingForExclusiveModeMutex(false); + return LOG_RESULT(result); + } + template constexpr void setCompatVtable(Vtable& vtable) { @@ -213,56 +313,19 @@ namespace DDraw return pf; } - LRESULT handleActivateApp(bool isActivated, std::function callOrigWndProc) + void hookDDrawWindowProc(WNDPROC ddrawWndProc) { - LOG_FUNC("handleActivateApp", isActivated, callOrigWndProc); - if (Config::Settings::AltTabFix::OFF == Config::altTabFix.get()) + LOG_FUNC("DirectDraw::hookDDrawWindowProc", ddrawWndProc); + static bool isHooked = false; + if (isHooked) { - return LOG_RESULT(callOrigWndProc()); + return; } + isHooked = true; - DDraw::ScopedThreadLock lock; - const bool keepPrimary = Config::Settings::AltTabFix::KEEPVIDMEM == Config::altTabFix.get(); - std::set surfacesToRestore; - DDraw::Surface::enumSurfaces([&](const Surface& surface) - { - auto lcl = DDraw::DirectDrawSurface::getInt(*surface.getDDS()).lpLcl; - if (!(lcl->dwFlags & DDRAWISURF_INVALID) && - (keepPrimary || !(surface.getOrigCaps() & DDSCAPS_PRIMARYSURFACE))) - { - lcl->dwFlags |= DDRAWISURF_INVALID; - surfacesToRestore.insert(lcl); - } - }); - - LRESULT result = callOrigWndProc(); - - DDraw::Surface::enumSurfaces([&](const Surface& surface) - { - auto lcl = DDraw::DirectDrawSurface::getInt(*surface.getDDS()).lpLcl; - auto it = surfacesToRestore.find(lcl); - if (it != surfacesToRestore.end()) - { - lcl->dwFlags &= ~DDRAWISURF_INVALID; - surfacesToRestore.erase(it); - } - }); - - if (isActivated && keepPrimary) - { - auto realPrimary(DDraw::RealPrimarySurface::getSurface()); - if (realPrimary) - { - realPrimary->Restore(realPrimary); - auto gdiResource = DDraw::PrimarySurface::getGdiResource(); - if (gdiResource) - { - D3dDdi::Device::setGdiResourceHandle(gdiResource); - } - } - } - - return LOG_RESULT(result); + g_origDDrawWindowProc = ddrawWndProc; + Compat::hookFunction(reinterpret_cast(g_origDDrawWindowProc), ddrawWindowProc, "ddrawWindowProc"); + Compat::closeDbgEng(); } void onCreate(GUID* guid, CompatRef dd) diff --git a/DDrawCompat/DDraw/DirectDraw.h b/DDrawCompat/DDraw/DirectDraw.h index 71d730b..9556ce1 100644 --- a/DDrawCompat/DDraw/DirectDraw.h +++ b/DDrawCompat/DDraw/DirectDraw.h @@ -13,7 +13,7 @@ namespace DDraw namespace DirectDraw { DDPIXELFORMAT getRgbPixelFormat(DWORD bpp); - LRESULT handleActivateApp(bool isActivated, std::function callOrigWndProc); + void hookDDrawWindowProc(WNDPROC ddrawWndProc); void onCreate(GUID* guid, CompatRef dd); void suppressEmulatedDirectDraw(GUID*& guid); diff --git a/DDrawCompat/DDraw/Surfaces/TagSurface.cpp b/DDrawCompat/DDraw/Surfaces/TagSurface.cpp index 955d360..99d2bb1 100644 --- a/DDrawCompat/DDraw/Surfaces/TagSurface.cpp +++ b/DDrawCompat/DDraw/Surfaces/TagSurface.cpp @@ -14,6 +14,7 @@ namespace DDraw TagSurface::TagSurface(DWORD origFlags, DWORD origCaps, DDRAWI_DIRECTDRAW_LCL* ddLcl) : Surface(origFlags, origCaps) , m_ddInt{} + , m_exclusiveOwnerThreadId(0) , m_fullscreenWindow(nullptr) , m_fullscreenWindowStyle(0) , m_fullscreenWindowExStyle(0) @@ -100,6 +101,7 @@ namespace DDraw } HWND prevFullscreenWindow = m_fullscreenWindow; m_fullscreenWindow = hwnd; + m_exclusiveOwnerThreadId = hwnd ? GetCurrentThreadId() : 0; if (Config::removeBorders.get()) { diff --git a/DDrawCompat/DDraw/Surfaces/TagSurface.h b/DDrawCompat/DDraw/Surfaces/TagSurface.h index f544bc3..814a394 100644 --- a/DDrawCompat/DDraw/Surfaces/TagSurface.h +++ b/DDrawCompat/DDraw/Surfaces/TagSurface.h @@ -19,6 +19,7 @@ namespace DDraw static TagSurface* findFullscreenWindow(HWND hwnd = nullptr); CompatPtr getDD(); + DWORD getExclusiveOwnerThreadId() { return m_exclusiveOwnerThreadId; } bool isFullscreen() const { return m_fullscreenWindow; } void setFullscreenWindow(HWND hwnd); LONG setWindowStyle(LONG style); @@ -28,6 +29,7 @@ namespace DDraw static HRESULT create(CompatRef dd); DDRAWI_DIRECTDRAW_INT m_ddInt; + DWORD m_exclusiveOwnerThreadId; HWND m_fullscreenWindow; LONG m_fullscreenWindowStyle; LONG m_fullscreenWindowExStyle; diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index 43c2333..e19fa26 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -49,6 +49,7 @@ namespace { WNDPROC wndProcA; WNDPROC wndProcW; + WNDPROC ddrawOrigWndProc; }; decltype(&DwmSetIconicThumbnail) g_dwmSetIconicThumbnail = nullptr; @@ -175,17 +176,7 @@ namespace break; } - LRESULT result = 0; - if (WM_ACTIVATEAPP == uMsg && Dll::g_origDDrawModule == Compat::getModuleHandleFromAddress( - reinterpret_cast(GetWindowLongA(hwnd, GWL_WNDPROC)))) - { - result = DDraw::DirectDraw::handleActivateApp(wParam, [=]() { - return callWindowProc(wndProc, hwnd, uMsg, wParam, lParam); }); - } - else - { - result = callWindowProc(wndProc, hwnd, uMsg, wParam, lParam); - } + LRESULT result = callWindowProc(wndProc, hwnd, uMsg, wParam, lParam); switch (uMsg) { @@ -282,7 +273,18 @@ namespace LOG_FUNC("ddrawSetWindowLongA", hWnd, nIndex, dwNewLong); if (GWL_WNDPROC == nIndex) { - return setWindowLongA(hWnd, GWL_WNDPROC, dwNewLong); + auto origWndProc = setWindowLongA(hWnd, GWL_WNDPROC, dwNewLong); + if (Dll::g_origDDrawModule == Compat::getModuleHandleFromAddress(reinterpret_cast(dwNewLong))) + { + Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); + auto it = g_windowProc.find(hWnd); + if (it != g_windowProc.end()) + { + it->second.ddrawOrigWndProc = reinterpret_cast(origWndProc); + } + DDraw::DirectDraw::hookDDrawWindowProc(reinterpret_cast(dwNewLong)); + } + return origWndProc; } if (GWL_STYLE == nIndex) @@ -381,7 +383,7 @@ namespace { if (GWL_WNDPROC == nIndex) { - Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); + Compat::ScopedSrwLockShared lock(g_windowProcSrwLock); auto it = g_windowProc.find(hWnd); if (it != g_windowProc.end()) { @@ -405,8 +407,9 @@ namespace WindowProc getWindowProc(HWND hwnd) { - Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); - return g_windowProc[hwnd]; + Compat::ScopedSrwLockShared lock(g_windowProcSrwLock); + auto it = g_windowProc.find(hwnd); + return it != g_windowProc.end() ? it->second : WindowProc{}; } std::wstring getWindowText(HWND hwnd, WNDPROC wndProc) @@ -883,6 +886,13 @@ namespace Gdi } } + WNDPROC getDDrawOrigWndProc(HWND hwnd) + { + Compat::ScopedSrwLockShared lock(g_windowProcSrwLock); + auto it = g_windowProc.find(hwnd); + return it != g_windowProc.end() ? it->second.ddrawOrigWndProc : nullptr; + } + void installHooks() { HOOK_FUNCTION(user32, AnimateWindow, animateWindow); diff --git a/DDrawCompat/Gdi/WinProc.h b/DDrawCompat/Gdi/WinProc.h index 6c5028e..935abb7 100644 --- a/DDrawCompat/Gdi/WinProc.h +++ b/DDrawCompat/Gdi/WinProc.h @@ -7,6 +7,7 @@ namespace Gdi namespace WinProc { void dllThreadDetach(); + WNDPROC getDDrawOrigWndProc(HWND hwnd); void installHooks(); void onCreateWindow(HWND hwnd); void startFrame(); diff --git a/DDrawCompat/Win32/Thread.cpp b/DDrawCompat/Win32/Thread.cpp index ec71f8f..8bbc3e5 100644 --- a/DDrawCompat/Win32/Thread.cpp +++ b/DDrawCompat/Win32/Thread.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -20,9 +21,33 @@ namespace bool g_cpuAffinityRotationEnabled = false; std::map g_nextProcessor; + HANDLE g_exclusiveModeMutex = nullptr; + thread_local bool g_skipWaitingForExclusiveModeMutex = false; + std::string maskToString(ULONG_PTR mask); void setNextProcessorSet(DWORD fromSet, DWORD toSet); + HANDLE WINAPI ddrawOpenMutexW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName) + { + LOG_FUNC("ddrawOpenMutexW", dwDesiredAccess, bInheritHandle, lpName); + auto result = CALL_ORIG_FUNC(OpenMutexW)(dwDesiredAccess, bInheritHandle, lpName); + if (SUCCEEDED(result) && lpName && 0 == lstrcmpW(lpName, L"Local\\__DDrawExclMode__")) + { + g_exclusiveModeMutex = result; + } + return LOG_RESULT(result); + } + + DWORD WINAPI ddrawWaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) + { + LOG_FUNC("ddrawWaitForSingleObject", hHandle, dwMilliseconds); + if (hHandle && hHandle == g_exclusiveModeMutex && g_skipWaitingForExclusiveModeMutex) + { + return WAIT_OBJECT_0; + } + return LOG_RESULT(CALL_ORIG_FUNC(WaitForSingleObject)(hHandle, dwMilliseconds)); + } + std::map> getProcessorSets() { std::map> result; @@ -280,6 +305,9 @@ namespace Win32 HOOK_FUNCTION(kernel32, SetProcessAffinityMask, setProcessAffinityMask); HOOK_FUNCTION(kernel32, SetProcessPriorityBoost, setProcessPriorityBoost); HOOK_FUNCTION(kernel32, SetThreadPriorityBoost, setThreadPriorityBoost); + + Compat::hookIatFunction(Dll::g_origDDrawModule, "OpenMutexW", ddrawOpenMutexW); + Compat::hookIatFunction(Dll::g_origDDrawModule, "WaitForSingleObject", ddrawWaitForSingleObject); } void rotateCpuAffinity() @@ -303,5 +331,10 @@ namespace Win32 LOG_ONCE("ERROR: Failed to set rotated CPU affinity: " << maskToString(g_cpuAffinity)); } } + + void skipWaitingForExclusiveModeMutex(bool skip) + { + g_skipWaitingForExclusiveModeMutex = skip; + } } } diff --git a/DDrawCompat/Win32/Thread.h b/DDrawCompat/Win32/Thread.h index 1e00bba..3bbd557 100644 --- a/DDrawCompat/Win32/Thread.h +++ b/DDrawCompat/Win32/Thread.h @@ -7,5 +7,6 @@ namespace Win32 void applyConfig(); void installHooks(); void rotateCpuAffinity(); + void skipWaitingForExclusiveModeMutex(bool skip); } }