diff --git a/DDrawCompat/Config/Config.cpp b/DDrawCompat/Config/Config.cpp index 532c4ff..3126db3 100644 --- a/DDrawCompat/Config/Config.cpp +++ b/DDrawCompat/Config/Config.cpp @@ -3,6 +3,7 @@ namespace Config { Settings::AlternatePixelCenter alternatePixelCenter; + Settings::AltTabFix altTabFix; Settings::Antialiasing antialiasing; Settings::ConfigHotKey configHotKey; Settings::CpuAffinity cpuAffinity; diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index a56b4c6..68cb4a2 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -23,6 +24,7 @@ namespace Config { extern Settings::AlternatePixelCenter alternatePixelCenter; + extern Settings::AltTabFix altTabFix; extern Settings::Antialiasing antialiasing; extern Settings::ConfigHotKey configHotKey; extern Settings::CpuAffinity cpuAffinity; diff --git a/DDrawCompat/Config/Settings/AltTabFix.h b/DDrawCompat/Config/Settings/AltTabFix.h new file mode 100644 index 0000000..af444ef --- /dev/null +++ b/DDrawCompat/Config/Settings/AltTabFix.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace Config +{ + namespace Settings + { + class AltTabFix : public MappedSetting + { + public: + static const UINT OFF = 0; + static const UINT KEEPVIDMEM = 1; + + AltTabFix() + : MappedSetting("AltTabFix", "off", { + {"off", OFF}, + {"keepvidmem", KEEPVIDMEM} + }) + { + } + }; + } +} diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index 1d44506..038942d 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -1,19 +1,26 @@ #include +#include #include #include #include #include +#include #include +#include #include +#include #include +#include #include #include #include +#include #include namespace { + const DWORD SURFACE_LOST_FLAG = 0x10000000; template HRESULT STDMETHODCALLTYPE CreateSurface( @@ -70,6 +77,17 @@ namespace return getOrigVtable(This).RestoreAllSurfaces(This); } + template + HRESULT STDMETHODCALLTYPE SetCooperativeLevel(TDirectDraw* This, HWND hWnd, DWORD dwFlags) + { + HRESULT result = getOrigVtable(This).SetCooperativeLevel(This, hWnd, dwFlags); + if (SUCCEEDED(result)) + { + DDraw::TagSurface::create(*CompatPtr::from(This)); + } + return result; + } + template HRESULT STDMETHODCALLTYPE WaitForVerticalBlank(TDirectDraw* This, DWORD dwFlags, HANDLE hEvent) { @@ -78,12 +96,44 @@ namespace return getOrigVtable(This).WaitForVerticalBlank(This, dwFlags, hEvent); } + HRESULT WINAPI restoreSurfaceLostFlag( + LPDIRECTDRAWSURFACE7 lpDDSurface, LPDDSURFACEDESC2 /*lpDDSurfaceDesc*/, LPVOID lpContext) + { + auto& surfacesToRestore = *static_cast*>(lpContext); + auto it = surfacesToRestore.find(DDraw::DirectDrawSurface::getSurfaceObject(*lpDDSurface)); + if (it != surfacesToRestore.end()) + { + DWORD& flags = DDraw::DirectDrawSurface::getFlags(*lpDDSurface); + flags &= ~SURFACE_LOST_FLAG; + surfacesToRestore.erase(it); + } + return DDENUMRET_OK; + } + + HRESULT WINAPI setSurfaceLostFlag( + LPDIRECTDRAWSURFACE7 lpDDSurface, LPDDSURFACEDESC2 /*lpDDSurfaceDesc*/, LPVOID lpContext) + { + auto& surfacesToRestore = *static_cast*>(lpContext); + DWORD& flags = DDraw::DirectDrawSurface::getFlags(*lpDDSurface); + if (!(flags & SURFACE_LOST_FLAG)) + { + auto resource = D3dDdi::Device::findResource(DDraw::DirectDrawSurface::getDriverResourceHandle(*lpDDSurface)); + if (resource && !resource->getOrigDesc().Flags.MatchGdiPrimary) + { + flags |= SURFACE_LOST_FLAG; + surfacesToRestore.insert(DDraw::DirectDrawSurface::getSurfaceObject(*lpDDSurface)); + } + } + return DDENUMRET_OK; + } + template constexpr void setCompatVtable(Vtable& vtable) { vtable.CreateSurface = &CreateSurface; vtable.FlipToGDISurface = &FlipToGDISurface; vtable.GetGDISurface = &GetGDISurface; + vtable.SetCooperativeLevel = &SetCooperativeLevel; vtable.WaitForVerticalBlank = &WaitForVerticalBlank; if constexpr (std::is_same_v || std::is_same_v) @@ -142,6 +192,42 @@ namespace DDraw return pf; } + LRESULT handleActivateApp(bool isActivated, std::function callOrigWndProc) + { + LOG_FUNC("handleActivateApp", isActivated, callOrigWndProc); + if (Config::Settings::AltTabFix::KEEPVIDMEM != Config::altTabFix.get()) + { + return LOG_RESULT(callOrigWndProc()); + } + + DDraw::ScopedThreadLock lock; + std::set surfacesToRestore; + TagSurface::forEachDirectDraw([&](CompatRef dd) + { + dd->EnumSurfaces(&dd, DDENUMSURFACES_DOESEXIST | DDENUMSURFACES_ALL, nullptr, + &surfacesToRestore, &setSurfaceLostFlag); + }); + + LRESULT result = callOrigWndProc(); + + TagSurface::forEachDirectDraw([&](CompatRef dd) + { + dd->EnumSurfaces(&dd, DDENUMSURFACES_DOESEXIST | DDENUMSURFACES_ALL, nullptr, + &surfacesToRestore, &restoreSurfaceLostFlag); + }); + + if (isActivated) + { + auto realPrimary(DDraw::RealPrimarySurface::getSurface()); + if (realPrimary) + { + realPrimary->Restore(realPrimary); + } + } + + return LOG_RESULT(result); + } + void onCreate(GUID* guid, CompatRef dd) { static std::map> repositories; @@ -155,7 +241,7 @@ namespace DDraw { return; } - repo->SetCooperativeLevel(repo, nullptr, DDSCL_NORMAL); + repo.get()->lpVtbl->SetCooperativeLevel(repo, nullptr, DDSCL_NORMAL); it = repositories.insert({ adapterInfo.luid, repo }).first; repo.detach(); } diff --git a/DDrawCompat/DDraw/DirectDraw.h b/DDrawCompat/DDraw/DirectDraw.h index d924044..ebe8caf 100644 --- a/DDrawCompat/DDraw/DirectDraw.h +++ b/DDrawCompat/DDraw/DirectDraw.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -11,9 +13,16 @@ namespace DDraw { DDSURFACEDESC2 getDisplayMode(CompatRef dd); DDPIXELFORMAT getRgbPixelFormat(DWORD bpp); + LRESULT handleActivateApp(bool isActivated, std::function callOrigWndProc); void onCreate(GUID* guid, CompatRef dd); void suppressEmulatedDirectDraw(GUID*& guid); + template + void* getDdObject(TDirectDraw& dd) + { + return reinterpret_cast(&dd)[1]; + } + template HWND* getDeviceWindowPtr(TDirectDraw& dd) { diff --git a/DDrawCompat/DDraw/DirectDrawSurface.h b/DDrawCompat/DDraw/DirectDrawSurface.h index 614362c..7853934 100644 --- a/DDrawCompat/DDraw/DirectDrawSurface.h +++ b/DDrawCompat/DDraw/DirectDrawSurface.h @@ -13,6 +13,18 @@ namespace DDraw { std::vector> getAllAttachedSurfaces(CompatRef surface); + template + void* getSurfaceObject(TSurface& surface) + { + return reinterpret_cast(&surface)[1]; + } + + template + DWORD& getFlags(TSurface& surface) + { + return reinterpret_cast(&surface)[1][7]; + } + template HANDLE getRuntimeResourceHandle(TSurface& surface) { diff --git a/DDrawCompat/DDraw/Surfaces/TagSurface.cpp b/DDrawCompat/DDraw/Surfaces/TagSurface.cpp new file mode 100644 index 0000000..dc0ed95 --- /dev/null +++ b/DDrawCompat/DDraw/Surfaces/TagSurface.cpp @@ -0,0 +1,56 @@ +#include + +#include +#include + +namespace +{ + std::map g_ddObjects; +} + +namespace DDraw +{ + HRESULT TagSurface::create(CompatRef dd) + { + auto ddObject = DDraw::DirectDraw::getDdObject(dd.get()); + if (g_ddObjects.find(ddObject) != g_ddObjects.end()) + { + return DD_OK; + } + + DDSURFACEDESC desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + desc.dwWidth = 1; + desc.dwHeight = 1; + desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + + auto privateData(std::make_unique(desc.ddsCaps.dwCaps, ddObject)); + g_ddObjects[ddObject] = privateData.get(); + + IDirectDrawSurface* surface = nullptr; + return Surface::create(dd, desc, surface, std::move(privateData)); + } + + void TagSurface::forEachDirectDraw(std::function)> callback) + { + struct DirectDrawInterface + { + const void* vtable; + void* ddObject; + DirectDrawInterface* next; + DWORD refCount; + }; + + for (auto ddObj : g_ddObjects) + { + DirectDrawInterface intf = { &CompatVtable::s_origVtable, ddObj.first, nullptr, 1 }; + callback(CompatRef(reinterpret_cast(intf))); + } + } + + TagSurface::~TagSurface() + { + g_ddObjects.erase(m_ddObject); + } +} diff --git a/DDrawCompat/DDraw/Surfaces/TagSurface.h b/DDrawCompat/DDraw/Surfaces/TagSurface.h new file mode 100644 index 0000000..3778d6f --- /dev/null +++ b/DDrawCompat/DDraw/Surfaces/TagSurface.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include +#include + +namespace DDraw +{ + class TagSurface : public Surface + { + public: + TagSurface(DWORD origCaps, void* ddObject) : Surface(origCaps), m_ddObject(ddObject) {} + virtual ~TagSurface() override; + + static HRESULT create(CompatRef dd); + static void forEachDirectDraw(std::function)> callback); + + private: + void* m_ddObject; + }; +} diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index fcb9610..c100f96 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -218,6 +218,7 @@ + @@ -277,6 +278,7 @@ + @@ -399,6 +401,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 0c64677..576e100 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -546,6 +546,12 @@ Header Files\Config\Settings + + Header Files\DDraw\Surfaces + + + Header Files\Config\Settings + @@ -863,6 +869,9 @@ Source Files\Config\Settings + + Source Files\DDraw\Surfaces + diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index c1226da..946da25 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -105,7 +106,17 @@ namespace break; } - LRESULT result = callWindowProc(wndProc, hwnd, uMsg, wParam, lParam); + 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); + } switch (uMsg) {