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

234 lines
5.5 KiB
C++

#include <atomic>
#include "CompatDirectDrawPalette.h"
#include "CompatDirectDrawSurface.h"
#include "CompatGdi.h"
#include "CompatGdiCaret.h"
#include "CompatGdiDcCache.h"
#include "CompatGdiDcFunctions.h"
#include "CompatGdiPaintHandlers.h"
#include "CompatGdiScrollFunctions.h"
#include "CompatGdiWinProc.h"
#include "CompatPaletteConverter.h"
#include "CompatPrimarySurface.h"
#include "DDrawProcs.h"
#include "RealPrimarySurface.h"
#include "ScopedCriticalSection.h"
namespace
{
std::atomic<int> g_disableEmulationCount = 0;
DWORD g_renderingRefCount = 0;
DWORD g_ddLockThreadRenderingRefCount = 0;
DWORD g_ddLockThreadId = 0;
HANDLE g_ddUnlockBeginEvent = nullptr;
HANDLE g_ddUnlockEndEvent = nullptr;
bool g_isDelayedUnlockPending = false;
BOOL CALLBACK invalidateWindow(HWND hwnd, LPARAM lParam)
{
if (!IsWindowVisible(hwnd))
{
return TRUE;
}
DWORD processId = 0;
GetWindowThreadProcessId(hwnd, &processId);
if (processId != GetCurrentProcessId())
{
return TRUE;
}
if (lParam)
{
POINT origin = {};
ClientToScreen(hwnd, &origin);
RECT rect = *reinterpret_cast<const RECT*>(lParam);
OffsetRect(&rect, -origin.x, -origin.y);
RedrawWindow(hwnd, &rect, nullptr, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
}
else
{
RedrawWindow(hwnd, nullptr, nullptr, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
}
return TRUE;
}
bool lockPrimarySurface()
{
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
if (FAILED(CompatDirectDrawSurface<IDirectDrawSurface7>::s_origVtable.Lock(
CompatPrimarySurface::surface, nullptr, &desc, DDLOCK_WAIT, nullptr)))
{
return false;
}
g_ddLockThreadId = GetCurrentThreadId();
CompatGdiDcCache::setDdLockThreadId(g_ddLockThreadId);
CompatGdiDcCache::setSurfaceMemory(desc.lpSurface, desc.lPitch);
return true;
}
void unlockPrimarySurface()
{
GdiFlush();
CompatDirectDrawSurface<IDirectDrawSurface7>::s_origVtable.Unlock(
CompatPrimarySurface::surface, nullptr);
RealPrimarySurface::invalidate(nullptr);
RealPrimarySurface::update();
Compat::origProcs.ReleaseDDThreadLock();
}
}
namespace CompatGdi
{
CRITICAL_SECTION g_gdiCriticalSection;
bool beginGdiRendering()
{
if (!isEmulationEnabled())
{
return false;
}
Compat::ScopedCriticalSection gdiLock(g_gdiCriticalSection);
if (0 == g_renderingRefCount)
{
LeaveCriticalSection(&g_gdiCriticalSection);
Compat::origProcs.AcquireDDThreadLock();
EnterCriticalSection(&g_gdiCriticalSection);
if (!lockPrimarySurface())
{
Compat::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)
{
unlockPrimarySurface();
g_ddLockThreadRenderingRefCount = 0;
g_renderingRefCount = 0;
}
else if (1 == g_ddLockThreadRenderingRefCount)
{
g_isDelayedUnlockPending = true;
gdiLock.unlock();
WaitForSingleObject(g_ddUnlockBeginEvent, INFINITE);
unlockPrimarySurface();
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;
}
}
}
void disableEmulation()
{
++g_disableEmulationCount;
}
void enableEmulation()
{
--g_disableEmulationCount;
}
void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc)
{
HWND hwnd = CreateWindow(className, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, 0);
oldWndProc = reinterpret_cast<WNDPROC>(
SetClassLongPtr(hwnd, GCLP_WNDPROC, reinterpret_cast<LONG>(newWndProc)));
DestroyWindow(hwnd);
}
void installHooks()
{
InitializeCriticalSection(&g_gdiCriticalSection);
if (CompatGdiDcCache::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;
}
CompatGdiDcFunctions::installHooks();
CompatGdiPaintHandlers::installHooks();
CompatGdiScrollFunctions::installHooks();
CompatGdiWinProc::installHooks();
CompatGdiCaret::installHooks();
}
}
void invalidate(const RECT* rect)
{
if (isEmulationEnabled())
{
EnumWindows(&invalidateWindow, reinterpret_cast<LPARAM>(rect));
}
}
bool isEmulationEnabled()
{
return g_disableEmulationCount <= 0 && RealPrimarySurface::isFullScreen();
}
void unhookWndProc(LPCSTR className, WNDPROC oldWndProc)
{
HWND hwnd = CreateWindow(className, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, 0);
SetClassLongPtr(hwnd, GCLP_WNDPROC, reinterpret_cast<LONG>(oldWndProc));
DestroyWindow(hwnd);
}
void uninstallHooks()
{
CompatGdiCaret::uninstallHooks();
CompatGdiWinProc::uninstallHooks();
CompatGdiPaintHandlers::uninstallHooks();
}
void updatePalette(DWORD startingEntry, DWORD count)
{
if (isEmulationEnabled() && CompatPrimarySurface::palette)
{
CompatGdiDcCache::updatePalette(startingEntry, count);
}
}
}