1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Fixed alt-tabbing in Rayman 2

See issue #286.
This commit is contained in:
narzoul 2024-04-01 23:35:30 +02:00
parent d5a89cad94
commit ec9cf32e91
8 changed files with 174 additions and 62 deletions

View File

@ -22,10 +22,17 @@
#include <DDraw/Surfaces/PrimarySurface.h>
#include <DDraw/Surfaces/TagSurface.h>
#include <DDraw/Visitors/DirectDrawVtblVisitor.h>
#include <Gdi/WinProc.h>
#include <Win32/DisplayMode.h>
#include <Win32/Thread.h>
namespace
{
WNDPROC g_origDDrawWindowProc = nullptr;
LRESULT handleActivateApp(HWND hwnd, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc);
LRESULT handleSize(HWND hwnd, WPARAM wParam, LPARAM lParam);
template <typename TDirectDraw, typename TSurfaceDesc, typename TSurface>
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<DDRAWI_DDRAWSURFACE_LCL*> 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 <typename Vtable>
constexpr void setCompatVtable(Vtable& vtable)
{
@ -213,56 +313,19 @@ namespace DDraw
return pf;
}
LRESULT handleActivateApp(bool isActivated, std::function<LRESULT()> 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<DDRAWI_DDRAWSURFACE_LCL*> 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<void*&>(g_origDDrawWindowProc), ddrawWindowProc, "ddrawWindowProc");
Compat::closeDbgEng();
}
void onCreate(GUID* guid, CompatRef<IDirectDraw7> dd)

View File

@ -13,7 +13,7 @@ namespace DDraw
namespace DirectDraw
{
DDPIXELFORMAT getRgbPixelFormat(DWORD bpp);
LRESULT handleActivateApp(bool isActivated, std::function<LRESULT()> callOrigWndProc);
void hookDDrawWindowProc(WNDPROC ddrawWndProc);
void onCreate(GUID* guid, CompatRef<IDirectDraw7> dd);
void suppressEmulatedDirectDraw(GUID*& guid);

View File

@ -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())
{

View File

@ -19,6 +19,7 @@ namespace DDraw
static TagSurface* findFullscreenWindow(HWND hwnd = nullptr);
CompatPtr<IDirectDraw7> 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<IDirectDraw> dd);
DDRAWI_DIRECTDRAW_INT m_ddInt;
DWORD m_exclusiveOwnerThreadId;
HWND m_fullscreenWindow;
LONG m_fullscreenWindowStyle;
LONG m_fullscreenWindowExStyle;

View File

@ -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<void*>(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<void*>(dwNewLong)))
{
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.find(hWnd);
if (it != g_windowProc.end())
{
it->second.ddrawOrigWndProc = reinterpret_cast<WNDPROC>(origWndProc);
}
DDraw::DirectDraw::hookDDrawWindowProc(reinterpret_cast<WNDPROC>(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);

View File

@ -7,6 +7,7 @@ namespace Gdi
namespace WinProc
{
void dllThreadDetach();
WNDPROC getDDrawOrigWndProc(HWND hwnd);
void installHooks();
void onCreateWindow(HWND hwnd);
void startFrame();

View File

@ -7,6 +7,7 @@
#include <Common/Hook.h>
#include <Common/Log.h>
#include <Common/ScopedCriticalSection.h>
#include <Common/Time.h>
#include <Config/Settings/CpuAffinity.h>
#include <Config/Settings/CpuAffinityRotation.h>
@ -20,9 +21,33 @@ namespace
bool g_cpuAffinityRotationEnabled = false;
std::map<BYTE, BYTE> 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<BYTE, std::vector<DWORD>> getProcessorSets()
{
std::map<BYTE, std::vector<DWORD>> 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;
}
}
}

View File

@ -7,5 +7,6 @@ namespace Win32
void applyConfig();
void installHooks();
void rotateCpuAffinity();
void skipWaitingForExclusiveModeMutex(bool skip);
}
}