mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
Disabled visual styles and added WM_NCPAINT handling (as a simple BitBlt copy from the original DC) to reduce glitches in rendering of common controls and windows, such as the GetSaveFileName dialog window in StarCraft. Fixed a deadlock in the caret emulation code caused by locking the GDI critical section prior to calling beginGdiRendering, which is locking both the DirectDraw and GDI critical sections. Another thread that only calls beginGdiRendering without entering the GDI critical section first could thus run into a deadlock as both threads were waiting on each other's critical sections. Now the global caret data has it its own critical section instead of sharing the GDI critical section.
166 lines
4.5 KiB
C++
166 lines
4.5 KiB
C++
#include <algorithm>
|
|
#include <unordered_map>
|
|
|
|
#include "CompatGdiDc.h"
|
|
#include "CompatGdiDcCache.h"
|
|
#include "DDrawLog.h"
|
|
|
|
namespace
|
|
{
|
|
using CompatGdiDcCache::CachedDc;
|
|
|
|
struct CompatDc : CachedDc
|
|
{
|
|
CompatDc(const CachedDc& cachedDc = {}) : CachedDc(cachedDc) {}
|
|
DWORD refCount;
|
|
HDC origDc;
|
|
HGDIOBJ origFont;
|
|
HGDIOBJ origBrush;
|
|
HGDIOBJ origPen;
|
|
};
|
|
|
|
typedef std::unordered_map<HDC, CompatDc> CompatDcMap;
|
|
CompatDcMap g_origDcToCompatDc;
|
|
|
|
struct ExcludeClipRectsData
|
|
{
|
|
HDC compatDc;
|
|
POINT origin;
|
|
HWND rootWnd;
|
|
};
|
|
|
|
void copyDcAttributes(CompatDc& compatDc, HDC origDc, POINT& origin)
|
|
{
|
|
compatDc.origFont = SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_FONT));
|
|
compatDc.origBrush = SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_BRUSH));
|
|
compatDc.origPen = SelectObject(compatDc.dc, GetCurrentObject(origDc, OBJ_PEN));
|
|
|
|
SetArcDirection(compatDc.dc, GetArcDirection(origDc));
|
|
SetBkColor(compatDc.dc, GetBkColor(origDc));
|
|
SetBkMode(compatDc.dc, GetBkMode(origDc));
|
|
SetDCBrushColor(compatDc.dc, GetDCBrushColor(origDc));
|
|
SetDCPenColor(compatDc.dc, GetDCPenColor(origDc));
|
|
SetPolyFillMode(compatDc.dc, GetPolyFillMode(origDc));
|
|
SetROP2(compatDc.dc, GetROP2(origDc));
|
|
SetStretchBltMode(compatDc.dc, GetStretchBltMode(origDc));
|
|
SetTextAlign(compatDc.dc, GetTextAlign(origDc));
|
|
SetTextCharacterExtra(compatDc.dc, GetTextCharacterExtra(origDc));
|
|
SetTextColor(compatDc.dc, GetTextColor(origDc));
|
|
SetWindowOrgEx(compatDc.dc, -origin.x, -origin.y, nullptr);
|
|
|
|
POINT brushOrg = {};
|
|
GetBrushOrgEx(origDc, &brushOrg);
|
|
SetBrushOrgEx(compatDc.dc, brushOrg.x, brushOrg.y, nullptr);
|
|
|
|
POINT currentPos = {};
|
|
MoveToEx(origDc, 0, 0, ¤tPos);
|
|
MoveToEx(origDc, currentPos.x, currentPos.y, nullptr);
|
|
MoveToEx(compatDc.dc, currentPos.x, currentPos.y, nullptr);
|
|
}
|
|
|
|
BOOL CALLBACK excludeClipRectsForOverlappingWindows(HWND hwnd, LPARAM lParam)
|
|
{
|
|
auto excludeClipRectsData = reinterpret_cast<ExcludeClipRectsData*>(lParam);
|
|
if (hwnd == excludeClipRectsData->rootWnd)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!IsWindowVisible(hwnd))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
RECT rect = {};
|
|
GetWindowRect(hwnd, &rect);
|
|
OffsetRect(&rect, -excludeClipRectsData->origin.x, -excludeClipRectsData->origin.y);
|
|
ExcludeClipRect(excludeClipRectsData->compatDc, rect.left, rect.top, rect.right, rect.bottom);
|
|
return TRUE;
|
|
}
|
|
|
|
void setClippingRegion(HDC compatDc, HDC origDc, POINT& origin)
|
|
{
|
|
HRGN clipRgn = CreateRectRgn(0, 0, 0, 0);
|
|
const bool isEmptyClipRgn = 1 != GetRandomRgn(origDc, clipRgn, SYSRGN);
|
|
SelectClipRgn(compatDc, isEmptyClipRgn ? nullptr : clipRgn);
|
|
DeleteObject(clipRgn);
|
|
|
|
HRGN origClipRgn = CreateRectRgn(0, 0, 0, 0);
|
|
if (1 == GetClipRgn(origDc, origClipRgn))
|
|
{
|
|
OffsetRgn(origClipRgn, origin.x, origin.y);
|
|
ExtSelectClipRgn(compatDc, origClipRgn, RGN_AND);
|
|
}
|
|
DeleteObject(origClipRgn);
|
|
|
|
if (!isEmptyClipRgn)
|
|
{
|
|
HWND hwnd = WindowFromDC(origDc);
|
|
if (hwnd)
|
|
{
|
|
ExcludeClipRectsData excludeClipRectsData = { compatDc, origin, GetAncestor(hwnd, GA_ROOT) };
|
|
EnumWindows(&excludeClipRectsForOverlappingWindows,
|
|
reinterpret_cast<LPARAM>(&excludeClipRectsData));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace CompatGdiDc
|
|
{
|
|
HDC getDc(HDC origDc)
|
|
{
|
|
if (!origDc || OBJ_DC != GetObjectType(origDc) || DT_RASDISPLAY != GetDeviceCaps(origDc, TECHNOLOGY) ||
|
|
g_origDcToCompatDc.end() != std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(),
|
|
[=](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == origDc; }))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
auto it = g_origDcToCompatDc.find(origDc);
|
|
if (it != g_origDcToCompatDc.end())
|
|
{
|
|
++it->second.refCount;
|
|
return it->second.dc;
|
|
}
|
|
|
|
CompatDc compatDc(CompatGdiDcCache::getDc());
|
|
if (!compatDc.dc)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
POINT origin = {};
|
|
GetDCOrgEx(origDc, &origin);
|
|
|
|
copyDcAttributes(compatDc, origDc, origin);
|
|
setClippingRegion(compatDc.dc, origDc, origin);
|
|
|
|
compatDc.refCount = 1;
|
|
compatDc.origDc = origDc;
|
|
g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc));
|
|
|
|
return compatDc.dc;
|
|
}
|
|
|
|
void releaseDc(HDC origDc)
|
|
{
|
|
auto it = g_origDcToCompatDc.find(origDc);
|
|
if (it == g_origDcToCompatDc.end())
|
|
{
|
|
return;
|
|
}
|
|
|
|
CompatDc& compatDc = it->second;
|
|
--compatDc.refCount;
|
|
if (0 == compatDc.refCount)
|
|
{
|
|
SelectObject(compatDc.dc, compatDc.origFont);
|
|
SelectObject(compatDc.dc, compatDc.origBrush);
|
|
SelectObject(compatDc.dc, compatDc.origPen);
|
|
CompatGdiDcCache::releaseDc(compatDc);
|
|
g_origDcToCompatDc.erase(origDc);
|
|
}
|
|
}
|
|
}
|