mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
Fixed deadlock when hooking NVIDIA user-mode display drivers
See issue #28
This commit is contained in:
parent
14104894b2
commit
8e8eeb083a
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "Common/Hook.h"
|
#include "Common/Hook.h"
|
||||||
#include "Common/Log.h"
|
#include "Common/Log.h"
|
||||||
|
#include "Common/ScopedCriticalSection.h"
|
||||||
#include "DDraw/ScopedThreadLock.h"
|
#include "DDraw/ScopedThreadLock.h"
|
||||||
#include "Gdi/Dc.h"
|
#include "Gdi/Dc.h"
|
||||||
#include "Gdi/DcCache.h"
|
#include "Gdi/DcCache.h"
|
||||||
@ -29,6 +30,7 @@ namespace
|
|||||||
|
|
||||||
typedef std::unordered_map<HDC, CompatDc> CompatDcMap;
|
typedef std::unordered_map<HDC, CompatDc> CompatDcMap;
|
||||||
|
|
||||||
|
Compat::CriticalSection g_cs;
|
||||||
CompatDcMap g_origDcToCompatDc;
|
CompatDcMap g_origDcToCompatDc;
|
||||||
|
|
||||||
void restoreDc(const CompatDc& compatDc);
|
void restoreDc(const CompatDc& compatDc);
|
||||||
@ -147,7 +149,7 @@ namespace Gdi
|
|||||||
{
|
{
|
||||||
void dllProcessDetach()
|
void dllProcessDetach()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
for (auto& origDcToCompatDc : g_origDcToCompatDc)
|
for (auto& origDcToCompatDc : g_origDcToCompatDc)
|
||||||
{
|
{
|
||||||
restoreDc(origDcToCompatDc.second);
|
restoreDc(origDcToCompatDc.second);
|
||||||
@ -158,7 +160,7 @@ namespace Gdi
|
|||||||
|
|
||||||
void dllThreadDetach()
|
void dllThreadDetach()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
const DWORD threadId = GetCurrentThreadId();
|
const DWORD threadId = GetCurrentThreadId();
|
||||||
auto it = g_origDcToCompatDc.begin();
|
auto it = g_origDcToCompatDc.begin();
|
||||||
while (it != g_origDcToCompatDc.end())
|
while (it != g_origDcToCompatDc.end())
|
||||||
@ -183,7 +185,8 @@ namespace Gdi
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
DDraw::ScopedThreadLock lock;
|
DDraw::ScopedThreadLock ddLock;
|
||||||
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
auto it = g_origDcToCompatDc.find(origDc);
|
auto it = g_origDcToCompatDc.find(origDc);
|
||||||
if (it != g_origDcToCompatDc.end())
|
if (it != g_origDcToCompatDc.end())
|
||||||
{
|
{
|
||||||
@ -228,7 +231,7 @@ namespace Gdi
|
|||||||
|
|
||||||
HDC getOrigDc(HDC dc)
|
HDC getOrigDc(HDC dc)
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
const auto it = std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(),
|
const auto it = std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(),
|
||||||
[dc](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == dc; });
|
[dc](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == dc; });
|
||||||
return it != g_origDcToCompatDc.end() ? it->first : dc;
|
return it != g_origDcToCompatDc.end() ? it->first : dc;
|
||||||
@ -236,7 +239,7 @@ namespace Gdi
|
|||||||
|
|
||||||
void releaseDc(HDC origDc)
|
void releaseDc(HDC origDc)
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
auto it = g_origDcToCompatDc.find(origDc);
|
auto it = g_origDcToCompatDc.find(origDc);
|
||||||
if (it == g_origDcToCompatDc.end())
|
if (it == g_origDcToCompatDc.end())
|
||||||
{
|
{
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "DDraw/ScopedThreadLock.h"
|
#include "Common/ScopedCriticalSection.h"
|
||||||
#include "Gdi/DcCache.h"
|
#include "Gdi/DcCache.h"
|
||||||
#include "Gdi/VirtualScreen.h"
|
#include "Gdi/VirtualScreen.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
Compat::CriticalSection g_cs;
|
||||||
std::map<DWORD, std::vector<HDC>> g_threadIdToDcCache;
|
std::map<DWORD, std::vector<HDC>> g_threadIdToDcCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ namespace Gdi
|
|||||||
{
|
{
|
||||||
void deleteDc(HDC cachedDc)
|
void deleteDc(HDC cachedDc)
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
for (auto& threadIdToDcCache : g_threadIdToDcCache)
|
for (auto& threadIdToDcCache : g_threadIdToDcCache)
|
||||||
{
|
{
|
||||||
auto& dcCache = threadIdToDcCache.second;
|
auto& dcCache = threadIdToDcCache.second;
|
||||||
@ -34,7 +35,7 @@ namespace Gdi
|
|||||||
|
|
||||||
void dllProcessDetach()
|
void dllProcessDetach()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
for (auto& threadIdToDcCache : g_threadIdToDcCache)
|
for (auto& threadIdToDcCache : g_threadIdToDcCache)
|
||||||
{
|
{
|
||||||
for (HDC dc : threadIdToDcCache.second)
|
for (HDC dc : threadIdToDcCache.second)
|
||||||
@ -47,7 +48,7 @@ namespace Gdi
|
|||||||
|
|
||||||
void dllThreadDetach()
|
void dllThreadDetach()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
auto it = g_threadIdToDcCache.find(GetCurrentThreadId());
|
auto it = g_threadIdToDcCache.find(GetCurrentThreadId());
|
||||||
if (it == g_threadIdToDcCache.end())
|
if (it == g_threadIdToDcCache.end())
|
||||||
{
|
{
|
||||||
@ -64,7 +65,7 @@ namespace Gdi
|
|||||||
|
|
||||||
HDC getDc()
|
HDC getDc()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
std::vector<HDC>& dcCache = g_threadIdToDcCache[GetCurrentThreadId()];
|
std::vector<HDC>& dcCache = g_threadIdToDcCache[GetCurrentThreadId()];
|
||||||
|
|
||||||
if (dcCache.empty())
|
if (dcCache.empty())
|
||||||
@ -79,7 +80,7 @@ namespace Gdi
|
|||||||
|
|
||||||
void releaseDc(HDC cachedDc)
|
void releaseDc(HDC cachedDc)
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
g_threadIdToDcCache[GetCurrentThreadId()].push_back(cachedDc);
|
g_threadIdToDcCache[GetCurrentThreadId()].push_back(cachedDc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "Common/Hook.h"
|
#include "Common/Hook.h"
|
||||||
#include "Common/Log.h"
|
#include "Common/Log.h"
|
||||||
#include "DDraw/ScopedThreadLock.h"
|
#include "Common/ScopedCriticalSection.h"
|
||||||
#include "Gdi/Gdi.h"
|
#include "Gdi/Gdi.h"
|
||||||
#include "Gdi/Palette.h"
|
#include "Gdi/Palette.h"
|
||||||
#include "VirtualScreen.h"
|
#include "VirtualScreen.h"
|
||||||
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
Compat::CriticalSection g_cs;
|
||||||
PALETTEENTRY g_systemPalette[256] = {};
|
PALETTEENTRY g_systemPalette[256] = {};
|
||||||
UINT g_systemPaletteUse = SYSPAL_STATIC;
|
UINT g_systemPaletteUse = SYSPAL_STATIC;
|
||||||
UINT g_systemPaletteFirstUnusedIndex = 10;
|
UINT g_systemPaletteFirstUnusedIndex = 10;
|
||||||
@ -75,7 +76,7 @@ namespace
|
|||||||
++g_systemPaletteFirstUnusedIndex;
|
++g_systemPaletteFirstUnusedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gdi::VirtualScreen::updatePalette();
|
Gdi::VirtualScreen::updatePalette(g_systemPalette);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ namespace
|
|||||||
nEntries = 256 - iStartIndex;
|
nEntries = 256 - iStartIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
std::memcpy(lppe, &g_systemPalette[iStartIndex], nEntries * sizeof(PALETTEENTRY));
|
std::memcpy(lppe, &g_systemPalette[iStartIndex], nEntries * sizeof(PALETTEENTRY));
|
||||||
|
|
||||||
return LOG_RESULT(nEntries);
|
return LOG_RESULT(nEntries);
|
||||||
@ -115,7 +116,7 @@ namespace
|
|||||||
{
|
{
|
||||||
return LOG_RESULT(SYSPAL_ERROR);
|
return LOG_RESULT(SYSPAL_ERROR);
|
||||||
}
|
}
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
return g_systemPaletteUse;
|
return g_systemPaletteUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +125,7 @@ namespace
|
|||||||
LOG_FUNC("RealizePalette", hdc);
|
LOG_FUNC("RealizePalette", hdc);
|
||||||
if (Gdi::isDisplayDc(hdc))
|
if (Gdi::isDisplayDc(hdc))
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
if (g_foregroundPaletteDcs.find(hdc) != g_foregroundPaletteDcs.end())
|
if (g_foregroundPaletteDcs.find(hdc) != g_foregroundPaletteDcs.end())
|
||||||
{
|
{
|
||||||
g_systemPaletteFirstUnusedIndex = g_systemPaletteFirstNonReservedIndex;
|
g_systemPaletteFirstUnusedIndex = g_systemPaletteFirstNonReservedIndex;
|
||||||
@ -137,7 +138,7 @@ namespace
|
|||||||
int WINAPI releaseDc(HWND hWnd, HDC hDC)
|
int WINAPI releaseDc(HWND hWnd, HDC hDC)
|
||||||
{
|
{
|
||||||
LOG_FUNC("ReleaseDC", hWnd, hDC);
|
LOG_FUNC("ReleaseDC", hWnd, hDC);
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
g_foregroundPaletteDcs.erase(hDC);
|
g_foregroundPaletteDcs.erase(hDC);
|
||||||
return LOG_RESULT(CALL_ORIG_FUNC(ReleaseDC)(hWnd, hDC));
|
return LOG_RESULT(CALL_ORIG_FUNC(ReleaseDC)(hWnd, hDC));
|
||||||
}
|
}
|
||||||
@ -151,7 +152,7 @@ namespace
|
|||||||
HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(hdc);
|
HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(hdc);
|
||||||
if (wnd && GetDesktopWindow() != wnd)
|
if (wnd && GetDesktopWindow() != wnd)
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
if (bForceBackground || GetStockObject(DEFAULT_PALETTE) == hpal)
|
if (bForceBackground || GetStockObject(DEFAULT_PALETTE) == hpal)
|
||||||
{
|
{
|
||||||
g_foregroundPaletteDcs.erase(hdc);
|
g_foregroundPaletteDcs.erase(hdc);
|
||||||
@ -173,7 +174,7 @@ namespace
|
|||||||
return LOG_RESULT(SYSPAL_ERROR);
|
return LOG_RESULT(SYSPAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
const UINT prevUsage = g_systemPaletteUse;
|
const UINT prevUsage = g_systemPaletteUse;
|
||||||
switch (uUsage)
|
switch (uUsage)
|
||||||
{
|
{
|
||||||
@ -210,7 +211,7 @@ namespace Gdi
|
|||||||
HPALETTE defaultPalette = reinterpret_cast<HPALETTE>(GetStockObject(DEFAULT_PALETTE));
|
HPALETTE defaultPalette = reinterpret_cast<HPALETTE>(GetStockObject(DEFAULT_PALETTE));
|
||||||
GetPaletteEntries(defaultPalette, 0, 10, g_systemPalette);
|
GetPaletteEntries(defaultPalette, 0, 10, g_systemPalette);
|
||||||
GetPaletteEntries(defaultPalette, 10, 10, &g_systemPalette[246]);
|
GetPaletteEntries(defaultPalette, 10, 10, &g_systemPalette[246]);
|
||||||
Gdi::VirtualScreen::updatePalette();
|
Gdi::VirtualScreen::updatePalette(g_systemPalette);
|
||||||
|
|
||||||
HOOK_FUNCTION(gdi32, GetSystemPaletteEntries, getSystemPaletteEntries);
|
HOOK_FUNCTION(gdi32, GetSystemPaletteEntries, getSystemPaletteEntries);
|
||||||
HOOK_FUNCTION(gdi32, GetSystemPaletteUse, getSystemPaletteUse);
|
HOOK_FUNCTION(gdi32, GetSystemPaletteUse, getSystemPaletteUse);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include "Common/ScopedCriticalSection.h"
|
||||||
#include "DDraw/DirectDraw.h"
|
#include "DDraw/DirectDraw.h"
|
||||||
#include "DDraw/ScopedThreadLock.h"
|
#include "DDraw/ScopedThreadLock.h"
|
||||||
#include "DDraw/Surfaces/PrimarySurface.h"
|
#include "DDraw/Surfaces/PrimarySurface.h"
|
||||||
@ -10,6 +11,7 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
Compat::CriticalSection g_cs;
|
||||||
Gdi::Region g_region;
|
Gdi::Region g_region;
|
||||||
RECT g_bounds = {};
|
RECT g_bounds = {};
|
||||||
DWORD g_bpp = 0;
|
DWORD g_bpp = 0;
|
||||||
@ -70,7 +72,7 @@ namespace Gdi
|
|||||||
{
|
{
|
||||||
HDC createDc()
|
HDC createDc()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
std::unique_ptr<void, decltype(&DeleteObject)> dib(createDib(), DeleteObject);
|
std::unique_ptr<void, decltype(&DeleteObject)> dib(createDib(), DeleteObject);
|
||||||
if (!dib)
|
if (!dib)
|
||||||
{
|
{
|
||||||
@ -91,7 +93,6 @@ namespace Gdi
|
|||||||
|
|
||||||
dib.release();
|
dib.release();
|
||||||
|
|
||||||
|
|
||||||
g_stockBitmap = stockBitmap;
|
g_stockBitmap = stockBitmap;
|
||||||
g_dcs.insert(dc.get());
|
g_dcs.insert(dc.get());
|
||||||
return dc.release();
|
return dc.release();
|
||||||
@ -99,7 +100,7 @@ namespace Gdi
|
|||||||
|
|
||||||
HBITMAP createDib()
|
HBITMAP createDib()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
if (!g_surfaceFileMapping)
|
if (!g_surfaceFileMapping)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -109,19 +110,21 @@ namespace Gdi
|
|||||||
|
|
||||||
HBITMAP createOffScreenDib(DWORD width, DWORD height)
|
HBITMAP createOffScreenDib(DWORD width, DWORD height)
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
return createDibSection(width, height, nullptr);
|
return createDibSection(width, height, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect)
|
CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect)
|
||||||
{
|
{
|
||||||
|
DDraw::ScopedThreadLock ddLock;
|
||||||
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
|
|
||||||
if (rect.left < g_bounds.left || rect.top < g_bounds.top ||
|
if (rect.left < g_bounds.left || rect.top < g_bounds.top ||
|
||||||
rect.right > g_bounds.right || rect.bottom > g_bounds.bottom)
|
rect.right > g_bounds.right || rect.bottom > g_bounds.bottom)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
DDraw::ScopedThreadLock lock;
|
|
||||||
DDSURFACEDESC2 desc = {};
|
DDSURFACEDESC2 desc = {};
|
||||||
desc.dwSize = sizeof(desc);
|
desc.dwSize = sizeof(desc);
|
||||||
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE;
|
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE;
|
||||||
@ -151,7 +154,7 @@ namespace Gdi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
DeleteObject(SelectObject(dc, g_stockBitmap));
|
DeleteObject(SelectObject(dc, g_stockBitmap));
|
||||||
DeleteDC(dc);
|
DeleteDC(dc);
|
||||||
g_dcs.erase(dc);
|
g_dcs.erase(dc);
|
||||||
@ -159,13 +162,13 @@ namespace Gdi
|
|||||||
|
|
||||||
RECT getBounds()
|
RECT getBounds()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
return g_bounds;
|
return g_bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Region& getRegion()
|
Region getRegion()
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
return g_region;
|
return g_region;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +180,7 @@ namespace Gdi
|
|||||||
bool update()
|
bool update()
|
||||||
{
|
{
|
||||||
LOG_FUNC("VirtualScreen::update");
|
LOG_FUNC("VirtualScreen::update");
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
|
|
||||||
static auto prevDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
|
static auto prevDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
|
||||||
const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
|
const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
|
||||||
@ -220,19 +223,16 @@ namespace Gdi
|
|||||||
return LOG_RESULT(true);
|
return LOG_RESULT(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePalette()
|
void updatePalette(PALETTEENTRY(&palette)[256])
|
||||||
{
|
{
|
||||||
DDraw::ScopedThreadLock lock;
|
Compat::ScopedCriticalSection lock(g_cs);
|
||||||
|
|
||||||
PALETTEENTRY pal[256] = {};
|
|
||||||
GetSystemPaletteEntries(Gdi::getScreenDc(), 0, 256, pal);
|
|
||||||
|
|
||||||
RGBQUAD systemPalette[256] = {};
|
RGBQUAD systemPalette[256] = {};
|
||||||
for (int i = 0; i < 256; ++i)
|
for (int i = 0; i < 256; ++i)
|
||||||
{
|
{
|
||||||
systemPalette[i].rgbRed = pal[i].peRed;
|
systemPalette[i].rgbRed = palette[i].peRed;
|
||||||
systemPalette[i].rgbGreen = pal[i].peGreen;
|
systemPalette[i].rgbGreen = palette[i].peGreen;
|
||||||
systemPalette[i].rgbBlue = pal[i].peBlue;
|
systemPalette[i].rgbBlue = palette[i].peBlue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 != memcmp(g_systemPalette, systemPalette, sizeof(systemPalette)))
|
if (0 != memcmp(g_systemPalette, systemPalette, sizeof(systemPalette)))
|
||||||
|
@ -19,10 +19,10 @@ namespace Gdi
|
|||||||
void deleteDc(HDC dc);
|
void deleteDc(HDC dc);
|
||||||
|
|
||||||
RECT getBounds();
|
RECT getBounds();
|
||||||
const Region& getRegion();
|
Region getRegion();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
bool update();
|
bool update();
|
||||||
void updatePalette();
|
void updatePalette(PALETTEENTRY(&palette)[256]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user