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

Emulate hardware cursor in scaled fullscreen mode

This commit is contained in:
narzoul 2021-06-15 23:14:33 +02:00
parent 647a4bfcff
commit ccc23c75b9
21 changed files with 771 additions and 169 deletions

View File

@ -11,6 +11,12 @@ std::enable_if_t<std::is_class_v<T> && std::is_trivial_v<T>, bool> operator==(co
return toTuple(left) == toTuple(right);
}
template <typename T>
std::enable_if_t<std::is_class_v<T>&& std::is_trivial_v<T>, bool> operator!=(const T& left, const T& right)
{
return toTuple(left) != toTuple(right);
}
template <typename T>
std::enable_if_t<std::is_class_v<T> && std::is_trivial_v<T>, bool> operator<(const T& left, const T& right)
{

View File

@ -82,7 +82,7 @@ namespace D3dDdi
Resource* Device::getResource(HANDLE resource)
{
auto it = m_resources.find(resource);
return it != m_resources.end() ? &it->second : nullptr;
return it != m_resources.end() ? it->second.get() : nullptr;
}
void Device::prepareForRendering(HANDLE resource, UINT subResourceIndex, bool isReadOnly)
@ -90,7 +90,7 @@ namespace D3dDdi
auto it = m_resources.find(resource);
if (it != m_resources.end())
{
it->second.prepareForRendering(subResourceIndex, isReadOnly);
it->second->prepareForRendering(subResourceIndex, isReadOnly);
}
}
@ -140,7 +140,7 @@ namespace D3dDdi
auto it = m_resources.find(data->hDstResource);
if (it != m_resources.end())
{
return it->second.blt(*data);
return it->second->blt(*data);
}
prepareForRendering(data->hSrcResource, data->SrcSubResourceIndex, true);
return m_origVtable.pfnBlt(m_device, data);
@ -162,7 +162,7 @@ namespace D3dDdi
auto it = m_resources.find(data->hResource);
if (it != m_resources.end())
{
return it->second.colorFill(*data);
return it->second->colorFill(*data);
}
return m_origVtable.pfnColorFill(m_device, data);
}
@ -180,8 +180,8 @@ namespace D3dDdi
{
try
{
Resource resource(*this, *data);
m_resources.emplace(resource, std::move(resource));
auto resource(std::make_unique<Resource>(*this, *data));
m_resources.emplace(*resource, std::move(resource));
if (data->Flags.VertexBuffer &&
D3DDDIPOOL_SYSTEMMEM == data->Pool &&
data->pSurfList[0].pSysMem)
@ -283,7 +283,7 @@ namespace D3dDdi
auto it = m_resources.find(data->hResource);
if (it != m_resources.end())
{
return it->second.lock(*data);
return it->second->lock(*data);
}
return m_origVtable.pfnLock(m_device, data);
}
@ -321,7 +321,7 @@ namespace D3dDdi
auto it = m_resources.find(data->hResource);
if (it != m_resources.end())
{
return it->second.unlock(*data);
return it->second->unlock(*data);
}
return m_origVtable.pfnUnlock(m_device, data);
}

View File

@ -1,7 +1,7 @@
#pragma once
#include <map>
#include <unordered_map>
#include <memory>
#include <d3d.h>
#include <d3dnthal.h>
@ -71,7 +71,7 @@ namespace D3dDdi
D3DDDI_DEVICEFUNCS m_origVtable;
Adapter& m_adapter;
HANDLE m_device;
std::unordered_map<HANDLE, Resource> m_resources;
std::map<HANDLE, std::unique_ptr<Resource>> m_resources;
Resource* m_renderTarget;
UINT m_renderTargetSubResourceIndex;
HANDLE m_sharedPrimary;

View File

@ -19,7 +19,7 @@
namespace
{
D3DDDIFORMAT g_dcFormatOverride = D3DDDIFMT_UNKNOWN;
bool g_dcPaletteOverride = false;
PALETTEENTRY* g_dcPaletteOverride = nullptr;
D3dDdi::KernelModeThunks::AdapterInfo g_gdiAdapterInfo = {};
D3dDdi::KernelModeThunks::AdapterInfo g_lastOpenAdapterInfo = {};
Compat::SrwLock g_lastOpenAdapterInfoSrwLock;
@ -59,8 +59,7 @@ namespace
{
if (g_dcPaletteOverride)
{
palette = Gdi::Palette::getHardwarePalette();
pData->pColorTable = palette.data();
pData->pColorTable = g_dcPaletteOverride;
}
else
{
@ -313,9 +312,9 @@ namespace D3dDdi
g_dcFormatOverride = static_cast<D3DDDIFORMAT>(format);
}
void setDcPaletteOverride(bool enable)
void setDcPaletteOverride(PALETTEENTRY* palette)
{
g_dcPaletteOverride = enable;
g_dcPaletteOverride = palette;
}
void waitForVsync()

View File

@ -22,7 +22,7 @@ namespace D3dDdi
UINT getVsyncCounter();
void installHooks();
void setDcFormatOverride(UINT format);
void setDcPaletteOverride(bool enable);
void setDcPaletteOverride(PALETTEENTRY* palette);
void waitForVsync();
bool waitForVsyncCounter(UINT counter);
}

View File

@ -13,6 +13,7 @@
#include <DDraw/Blitter.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/Cursor.h>
#include <Gdi/Palette.h>
#include <Gdi/VirtualScreen.h>
@ -113,6 +114,14 @@ namespace D3dDdi
if (m_origData.Flags.Primary)
{
g_presentationRect = calculatePresentationRect();
auto& si = m_origData.pSurfList[0];
RECT rect = { 0, 0, static_cast<LONG>(si.Width), static_cast<LONG>(si.Height) };
Gdi::Cursor::setMonitorClipRect(DDraw::PrimarySurface::getMonitorRect());
if (!EqualRect(&g_presentationRect, &rect))
{
Gdi::Cursor::setEmulated(true);
}
}
fixResourceData();
@ -141,6 +150,15 @@ namespace D3dDdi
data.hResource = m_fixedData.hResource;
}
Resource::~Resource()
{
if (m_origData.Flags.Primary)
{
Gdi::Cursor::setEmulated(false);
Gdi::Cursor::setMonitorClipRect({});
}
}
HRESULT Resource::blt(D3DDDIARG_BLT data)
{
if (!isValidRect(data.DstSubResourceIndex, data.DstRect))
@ -177,7 +195,7 @@ namespace D3dDdi
}
else if (m_fixedData.Flags.Primary)
{
return presentationBlt(data, *srcResource);
return presentationBlt(data, srcResource);
}
else
{
@ -511,36 +529,59 @@ namespace D3dDdi
}
}
HRESULT Resource::presentationBlt(D3DDDIARG_BLT data, Resource& srcResource)
HRESULT Resource::presentationBlt(D3DDDIARG_BLT data, Resource* srcResource)
{
if (srcResource.m_lockResource &&
srcResource.m_lockData[0].isSysMemUpToDate)
if (srcResource->m_lockResource &&
srcResource->m_lockData[0].isSysMemUpToDate)
{
srcResource.copyToVidMem(0);
srcResource->copyToVidMem(0);
}
if (D3DDDIFMT_P8 == srcResource.m_origData.Format)
const auto& si = srcResource->m_fixedData.pSurfList[0];
const bool isPalettized = D3DDDIFMT_P8 == srcResource->m_origData.Format;
const auto cursorInfo = Gdi::Cursor::getEmulatedCursorInfo();
const bool isCursorEmulated = cursorInfo.flags == CURSOR_SHOWING && cursorInfo.hCursor;
if (isPalettized || isCursorEmulated)
{
const auto& si = srcResource.m_fixedData.pSurfList[0];
auto palettizedBltRenderTarget(SurfaceRepository::get(m_device.getAdapter()).getPaletteBltRenderTarget(
si.Width, si.Height));
if (!palettizedBltRenderTarget)
auto dst(SurfaceRepository::get(m_device.getAdapter()).getRenderTarget(si.Width, si.Height));
if (!dst)
{
return E_OUTOFMEMORY;
}
auto entries(Gdi::Palette::getHardwarePalette());
RGBQUAD pal[256] = {};
for (UINT i = 0; i < 256; ++i)
if (isPalettized)
{
pal[i].rgbRed = entries[i].peRed;
pal[i].rgbGreen = entries[i].peGreen;
pal[i].rgbBlue = entries[i].peBlue;
auto entries(Gdi::Palette::getHardwarePalette());
RGBQUAD pal[256] = {};
for (UINT i = 0; i < 256; ++i)
{
pal[i].rgbRed = entries[i].peRed;
pal[i].rgbGreen = entries[i].peGreen;
pal[i].rgbBlue = entries[i].peBlue;
}
m_device.getShaderBlitter().palettizedBlt(*dst, 0, *srcResource, pal);
}
else
{
D3DDDIARG_BLT blt = {};
blt.hSrcResource = data.hSrcResource;
blt.SrcRect = data.SrcRect;
blt.hDstResource = *dst;
blt.DstRect = data.SrcRect;
blt.Flags.Point = 1;
m_device.getOrigVtable().pfnBlt(m_device, &blt);
}
m_device.getShaderBlitter().palettizedBlt(*palettizedBltRenderTarget, 0, srcResource, pal);
data.hSrcResource = *palettizedBltRenderTarget;
data.SrcSubResourceIndex = 0;
srcResource = dst;
data.hSrcResource = *dst;
}
if (isCursorEmulated)
{
RECT monitorRect = DDraw::PrimarySurface::getMonitorRect();
POINT pos = { cursorInfo.ptScreenPos.x - monitorRect.left, cursorInfo.ptScreenPos.y - monitorRect.top };
m_device.getShaderBlitter().cursorBlt(*srcResource, 0, cursorInfo.hCursor, pos);
}
data.DstRect = g_presentationRect;

View File

@ -18,10 +18,10 @@ namespace D3dDdi
Resource(Device& device, D3DDDIARG_CREATERESOURCE2& data);
Resource(const Resource&) = delete;
Resource(Resource&&) = delete;
Resource& operator=(const Resource&) = delete;
Resource(Resource&&) = default;
Resource& operator=(Resource&&) = default;
Resource& operator=(Resource&&) = delete;
~Resource();
operator HANDLE() const { return m_handle; }
const D3DDDIARG_CREATERESOURCE2& getOrigDesc() const { return m_origData; }
@ -43,10 +43,9 @@ namespace D3dDdi
Data(const D3DDDIARG_CREATERESOURCE2& data);
Data(const Data&) = delete;
Data(Data&&) = delete;
Data& operator=(const Data&) = delete;
Data(Data&&) = default;
Data& operator=(Data&&) = default;
Data& operator=(Data&&) = delete;
std::vector<D3DDDI_SURFACEINFO> surfaceData;
};
@ -83,7 +82,7 @@ namespace D3dDdi
void fixResourceData();
bool isOversized() const;
bool isValidRect(UINT subResourceIndex, const RECT& rect);
HRESULT presentationBlt(D3DDDIARG_BLT data, Resource& srcResource);
HRESULT presentationBlt(D3DDDIARG_BLT data, Resource* srcResource);
HRESULT splitBlt(D3DDDIARG_BLT& data, UINT& subResourceIndex, RECT& rect, RECT& otherRect);
template <typename Arg>

View File

@ -4,6 +4,7 @@
#include <D3dDdi/Resource.h>
#include <D3dDdi/ShaderBlitter.h>
#include <D3dDdi/SurfaceRepository.h>
#include <Shaders/DrawCursor.h>
#include <Shaders/PaletteLookup.h>
#define CONCAT_(a, b) a##b
@ -14,7 +15,8 @@ namespace D3dDdi
{
ShaderBlitter::ShaderBlitter(Device& device)
: m_device(device)
, m_psPaletteLookup(createPixelShader(g_psPaletteLookup, sizeof(g_psPaletteLookup)))
, m_psDrawCursor(createPixelShader(g_psDrawCursor))
, m_psPaletteLookup(createPixelShader(g_psPaletteLookup))
, m_vertexShaderDecl(createVertexShaderDecl())
{
}
@ -122,6 +124,46 @@ namespace D3dDdi
return data.ShaderHandle;
}
void ShaderBlitter::cursorBlt(const Resource& dstResource, UINT dstSubResourceIndex, HCURSOR cursor, POINT pt)
{
LOG_FUNC("ShaderBlitter::cursorBlt", static_cast<HANDLE>(dstResource), dstSubResourceIndex, cursor, pt);
auto& repo = SurfaceRepository::get(m_device.getAdapter());
auto cur = repo.getCursor(cursor);
auto xorTexture = repo.getLogicalXorTexture();
pt.x -= cur.hotspot.x;
pt.y -= cur.hotspot.y;
RECT dstRect = { pt.x, pt.y, pt.x + cur.size.cx, pt.y + cur.size.cy };
auto& dstDesc = dstResource.getFixedDesc().pSurfList[dstSubResourceIndex];
RECT clippedDstRect = {};
clippedDstRect.right = dstDesc.Width;
clippedDstRect.bottom = dstDesc.Height;
IntersectRect(&clippedDstRect, &clippedDstRect, &dstRect);
if (!cur.maskTexture || !cur.colorTexture || !cur.tempTexture || !xorTexture || IsRectEmpty(&clippedDstRect))
{
return;
}
RECT clippedSrcRect = clippedDstRect;
OffsetRect(&clippedSrcRect, -dstRect.left, -dstRect.top);
D3DDDIARG_BLT data = {};
data.hSrcResource = dstResource;
data.SrcSubResourceIndex = dstSubResourceIndex;
data.SrcRect = clippedDstRect;
data.hDstResource = *cur.tempTexture;
data.DstRect = clippedSrcRect;
m_device.getOrigVtable().pfnBlt(m_device, &data);
SCOPED_STATE(Texture, 1, *cur.maskTexture, D3DTEXF_POINT);
SCOPED_STATE(Texture, 2, *cur.colorTexture, D3DTEXF_POINT);
SCOPED_STATE(Texture, 3, *xorTexture, D3DTEXF_POINT);
blt(dstResource, dstSubResourceIndex, clippedDstRect, *cur.tempTexture, clippedSrcRect, m_psDrawCursor, D3DTEXF_POINT);
}
void ShaderBlitter::palettizedBlt(const Resource& dstResource, UINT dstSubResourceIndex,
const Resource& srcResource, RGBQUAD palette[256])
{

View File

@ -16,6 +16,7 @@ namespace D3dDdi
ShaderBlitter& operator=(const ShaderBlitter&) = delete;
ShaderBlitter& operator=(ShaderBlitter&&) = delete;
void cursorBlt(const Resource& dstResource, UINT dstSubResourceIndex, HCURSOR cursor, POINT pt);
void palettizedBlt(const Resource& dstResource, UINT dstSubResourceIndex,
const Resource& srcResource, RGBQUAD palette[256]);
@ -23,10 +24,17 @@ namespace D3dDdi
void blt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect,
const Resource& srcResource, const RECT& srcRect, HANDLE pixelShader, UINT filter);
template <int N>
HANDLE createPixelShader(const BYTE(&code)[N])
{
return createPixelShader(code, N);
}
HANDLE createPixelShader(const BYTE* code, UINT size);
HANDLE createVertexShaderDecl();
Device& m_device;
HANDLE m_psDrawCursor;
HANDLE m_psPaletteLookup;
HANDLE m_vertexShaderDecl;
};

View File

@ -1,8 +1,15 @@
#undef WIN32_LEAN_AND_MEAN
#include <map>
#include <Windows.h>
#include <d3dkmthk.h>
#include <Common/Comparison.h>
#include <D3dDdi/Adapter.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/KernelModeThunks.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/SurfaceRepository.h>
#include <DDraw/DirectDrawSurface.h>
@ -15,6 +22,9 @@ namespace D3dDdi
{
SurfaceRepository::SurfaceRepository(const Adapter& adapter)
: m_adapter(adapter)
, m_cursor(nullptr)
, m_cursorSize{}
, m_cursorHotspot{}
{
}
@ -57,10 +67,136 @@ namespace D3dDdi
return g_repositories.emplace(adapter.getLuid(), SurfaceRepository(adapter)).first->second;
}
Resource* SurfaceRepository::getPaletteBltRenderTarget(DWORD width, DWORD height)
Resource* SurfaceRepository::getBitmapResource(
Surface& surface, HBITMAP bitmap, const RECT& rect, const DDPIXELFORMAT& pf, DWORD caps)
{
return getResource(m_paletteBltRenderTarget, width, height, DDraw::DirectDraw::getRgbPixelFormat(32),
DDSCAPS_3DDEVICE | DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
DWORD width = rect.right - rect.left;
DWORD height = rect.bottom - rect.top;
auto resource = getResource(surface, width, height, pf, caps);
if (!resource)
{
return nullptr;
}
HDC srcDc = CreateCompatibleDC(nullptr);
HGDIOBJ prevBitmap = SelectObject(srcDc, bitmap);
HDC dstDc = nullptr;
PALETTEENTRY palette[256] = {};
palette[255] = { 0xFF, 0xFF, 0xFF };
KernelModeThunks::setDcPaletteOverride(palette);
surface.surface->GetDC(surface.surface, &dstDc);
KernelModeThunks::setDcPaletteOverride(nullptr);
CALL_ORIG_FUNC(BitBlt)(dstDc, 0, 0, width, height, srcDc, rect.left, rect.top, SRCCOPY);
surface.surface->ReleaseDC(surface.surface, dstDc);
SelectObject(srcDc, prevBitmap);
DeleteDC(srcDc);
return resource;
}
SurfaceRepository::Cursor SurfaceRepository::getCursor(HCURSOR cursor)
{
if (isLost(m_cursorMaskTexture) || isLost(m_cursorColorTexture))
{
m_cursor = nullptr;
release(m_cursorMaskTexture);
release(m_cursorColorTexture);
}
if (cursor != m_cursor)
{
m_cursor = cursor;
ICONINFO iconInfo = {};
if (!GetIconInfo(cursor, &iconInfo))
{
return {};
}
BITMAP bm = {};
GetObject(iconInfo.hbmMask, sizeof(bm), &bm);
RECT rect = {};
SetRect(&rect, 0, 0, bm.bmWidth, bm.bmHeight);
if (!iconInfo.hbmColor)
{
rect.bottom /= 2;
}
getBitmapResource(m_cursorMaskTexture, iconInfo.hbmMask, rect,
DDraw::DirectDraw::getRgbPixelFormat(8), DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY);
if (iconInfo.hbmColor)
{
getBitmapResource(m_cursorColorTexture, iconInfo.hbmColor, rect,
DDraw::DirectDraw::getRgbPixelFormat(32), DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
DeleteObject(iconInfo.hbmColor);
}
else
{
OffsetRect(&rect, 0, rect.bottom);
getBitmapResource(m_cursorColorTexture, iconInfo.hbmMask, rect,
DDraw::DirectDraw::getRgbPixelFormat(8), DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY);
}
DeleteObject(iconInfo.hbmMask);
m_cursorMaskTexture.resource->prepareForRendering(0, true);
m_cursorColorTexture.resource->prepareForRendering(0, true);
m_cursorSize.cx = rect.right - rect.left;
m_cursorSize.cy = rect.bottom - rect.top;
m_cursorHotspot.x = iconInfo.xHotspot;
m_cursorHotspot.y = iconInfo.yHotspot;
}
Cursor result = {};
result.cursor = m_cursor;
result.size = m_cursorSize;
result.hotspot = m_cursorHotspot;
result.maskTexture = m_cursorMaskTexture.resource;
result.colorTexture = m_cursorColorTexture.resource;
result.tempTexture = getResource(m_cursorTempTexture, m_cursorSize.cx, m_cursorSize.cy,
DDraw::DirectDraw::getRgbPixelFormat(32), DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
return result;
}
Resource* SurfaceRepository::getLogicalXorTexture()
{
return getInitializedResource(m_logicalXorTexture, 256, 256, DDraw::DirectDraw::getRgbPixelFormat(8),
DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY,
[](const DDSURFACEDESC2& desc) {
BYTE* p = static_cast<BYTE*>(desc.lpSurface);
for (UINT y = 0; y < 256; ++y)
{
for (UINT x = 0; x < 256; ++x)
{
p[x] = static_cast<BYTE>(x ^ y);
}
p += desc.lPitch;
}
});
}
Resource* SurfaceRepository::getInitializedResource(Surface& surface, DWORD width, DWORD height,
const DDPIXELFORMAT& pf, DWORD caps, std::function<void(const DDSURFACEDESC2&)> initFunc)
{
if (!isLost(surface) || !getResource(surface, width, height, pf, caps))
{
return surface.resource;
}
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
surface.surface->Lock(surface.surface, nullptr, &desc, DDLOCK_DISCARDCONTENTS | DDLOCK_WAIT, nullptr);
if (!desc.lpSurface)
{
return nullptr;
}
initFunc(desc);
surface.surface->Unlock(surface.surface, nullptr);
surface.resource->prepareForRendering(0, true);
return surface.resource;
}
Resource* SurfaceRepository::getPaletteTexture()
@ -69,18 +205,18 @@ namespace D3dDdi
DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
}
Resource* SurfaceRepository::getRenderTarget(DWORD width, DWORD height)
{
return getResource(m_renderTarget, width, height, DDraw::DirectDraw::getRgbPixelFormat(32),
DDSCAPS_3DDEVICE | DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
}
Resource* SurfaceRepository::getResource(Surface& surface, DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps)
{
if (surface.surface)
if (surface.surface && (surface.width != width || surface.height != height ||
0 != memcmp(&surface.pixelFormat, &pf, sizeof(pf)) || isLost(surface)))
{
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
surface.surface->GetSurfaceDesc(surface.surface, &desc);
if (desc.dwWidth != width || desc.dwHeight != height || FAILED(surface.surface->IsLost(surface.surface)))
{
surface.surface->Release(surface.surface);
surface = {};
}
release(surface);
}
if (!surface.surface)
@ -90,9 +226,26 @@ namespace D3dDdi
{
surface.resource = D3dDdi::Device::findResource(
DDraw::DirectDrawSurface::getDriverResourceHandle(*surface.surface));
surface.width = width;
surface.height = height;
surface.pixelFormat = pf;
}
}
return surface.resource;
}
bool SurfaceRepository::isLost(Surface& surface)
{
return !surface.surface || FAILED(surface.surface->IsLost(surface.surface));
}
void SurfaceRepository::release(Surface& surface)
{
if (surface.surface)
{
surface.surface->Release(surface.surface);
surface = {};
}
}
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <functional>
#include <ddraw.h>
#include <Common/CompatWeakPtr.h>
@ -12,8 +14,20 @@ namespace D3dDdi
class SurfaceRepository
{
public:
Resource* getPaletteBltRenderTarget(DWORD width, DWORD height);
struct Cursor
{
HCURSOR cursor;
SIZE size;
POINT hotspot;
Resource* maskTexture;
Resource* colorTexture;
Resource* tempTexture;
};
Cursor getCursor(HCURSOR cursor);
Resource* getLogicalXorTexture();
Resource* getPaletteTexture();
Resource* getRenderTarget(DWORD width, DWORD height);
static SurfaceRepository& get(const Adapter& adapter);
@ -22,15 +36,30 @@ namespace D3dDdi
{
CompatWeakPtr<IDirectDrawSurface7> surface;
Resource* resource;
DWORD width;
DWORD height;
DDPIXELFORMAT pixelFormat;
};
SurfaceRepository(const Adapter& adapter);
CompatWeakPtr<IDirectDrawSurface7> createSurface(DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps);
Resource* getBitmapResource(Surface& surface, HBITMAP bitmap, const RECT& rect, const DDPIXELFORMAT& pf, DWORD caps);
Resource* getInitializedResource(Surface& surface, DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps,
std::function<void(const DDSURFACEDESC2&)> initFunc);
Resource* getResource(Surface& surface, DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps);
bool isLost(Surface& surface);
void release(Surface& surface);
const Adapter& m_adapter;
Surface m_paletteBltRenderTarget;
HCURSOR m_cursor;
SIZE m_cursorSize;
POINT m_cursorHotspot;
Surface m_cursorMaskTexture;
Surface m_cursorColorTexture;
Surface m_cursorTempTexture;
Surface m_logicalXorTexture;
Surface m_paletteTexture;
Surface m_renderTarget;
};
}

View File

@ -2,6 +2,7 @@
#include <memory>
#include <vector>
#include <Common/Comparison.h>
#include <Common/CompatPtr.h>
#include <Common/Hook.h>
#include <Common/Time.h>
@ -16,7 +17,9 @@
#include <DDraw/Surfaces/PrimarySurface.h>
#include <DDraw/Types.h>
#include <Gdi/Caret.h>
#include <Gdi/Cursor.h>
#include <Gdi/Gdi.h>
#include <Gdi/Palette.h>
#include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h>
#include <Win32/DisplayMode.h>
@ -142,7 +145,7 @@ namespace
g_surfaceDesc = desc;
g_isFullScreen = isFlippable;
g_isUpdatePending = false;
g_isUpdatePending = true;
g_qpcLastUpdate = Time::queryPerformanceCounter() - Time::msToQpc(Config::delayedFlipModeTimeout);
if (isFlippable)
@ -169,9 +172,10 @@ namespace
Gdi::Region excludeRegion(DDraw::PrimarySurface::getMonitorRect());
Gdi::Window::present(excludeRegion);
D3dDdi::KernelModeThunks::setDcPaletteOverride(true);
auto palette(Gdi::Palette::getHardwarePalette());
D3dDdi::KernelModeThunks::setDcPaletteOverride(palette.data());
bltToPrimaryChain(*src);
D3dDdi::KernelModeThunks::setDcPaletteOverride(false);
D3dDdi::KernelModeThunks::setDcPaletteOverride(nullptr);
if (g_isFullScreen && src == DDraw::PrimarySurface::getGdiSurface())
{
@ -222,10 +226,15 @@ namespace
D3dDdi::KernelModeThunks::waitForVsync();
}
skipWaitForVsync = false;
Gdi::Caret::blink();
Sleep(1);
DDraw::ScopedThreadLock lock;
Gdi::Caret::blink();
if (Gdi::Cursor::update())
{
g_isUpdatePending = true;
}
if (g_isUpdatePending && !isPresentPending())
{
auto qpcNow = Time::queryPerformanceCounter();

View File

@ -282,6 +282,7 @@
<ClInclude Include="Direct3d\Visitors\Direct3dVtblVisitor.h" />
<ClInclude Include="Dll\Dll.h" />
<ClInclude Include="Gdi\CompatDc.h" />
<ClInclude Include="Gdi\Cursor.h" />
<ClInclude Include="Gdi\Font.h" />
<ClInclude Include="Gdi\Gdi.h" />
<ClInclude Include="Gdi\Caret.h" />
@ -366,6 +367,7 @@
<ClCompile Include="Dll\DllMain.cpp" />
<ClCompile Include="Dll\Dll.cpp" />
<ClCompile Include="Gdi\CompatDc.cpp" />
<ClCompile Include="Gdi\Cursor.cpp" />
<ClCompile Include="Gdi\Font.cpp" />
<ClCompile Include="Gdi\Gdi.cpp" />
<ClCompile Include="Gdi\Caret.cpp" />
@ -397,6 +399,7 @@
<None Include="genversion.ps1" />
</ItemGroup>
<ItemGroup>
<FxCompile Include="Shaders\DrawCursor.hlsl" />
<FxCompile Include="Shaders\PaletteLookup.hlsl" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -435,6 +435,9 @@
<ClInclude Include="D3dDdi\SurfaceRepository.h">
<Filter>Header Files\D3dDdi</Filter>
</ClInclude>
<ClInclude Include="Gdi\Cursor.h">
<Filter>Header Files\Gdi</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp">
@ -683,6 +686,9 @@
<ClCompile Include="D3dDdi\SurfaceRepository.cpp">
<Filter>Source Files\D3dDdi</Filter>
</ClCompile>
<ClCompile Include="Gdi\Cursor.cpp">
<Filter>Source Files\Gdi</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="DDrawCompat.rc">
@ -698,5 +704,8 @@
<FxCompile Include="Shaders\PaletteLookup.hlsl">
<Filter>Shaders</Filter>
</FxCompile>
<FxCompile Include="Shaders\DrawCursor.hlsl">
<Filter>Shaders</Filter>
</FxCompile>
</ItemGroup>
</Project>

248
DDrawCompat/Gdi/Cursor.cpp Normal file
View File

@ -0,0 +1,248 @@
#include <Common/ScopedCriticalSection.h>
#include <Common/Hook.h>
#include <Common/Log.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/Cursor.h>
#include <Win32/DisplayMode.h>
namespace
{
RECT g_clipRect = {};
HCURSOR g_cursor = nullptr;
bool g_isEmulated = false;
RECT g_monitorClipRect = {};
HCURSOR g_nullCursor = nullptr;
CURSORINFO g_prevCursorInfo = {};
Compat::CriticalSection g_cs;
RECT intersectRect(RECT rect1, RECT rect2);
void normalizeRect(RECT& rect);
BOOL WINAPI clipCursor(const RECT* lpRect)
{
LOG_FUNC("ClipCursor", lpRect);
Compat::ScopedCriticalSection lock(g_cs);
BOOL result = CALL_ORIG_FUNC(ClipCursor)(lpRect);
if (!result || IsRectEmpty(&g_monitorClipRect))
{
return LOG_RESULT(result);
}
CALL_ORIG_FUNC(GetClipCursor)(&g_clipRect);
RECT rect = intersectRect(g_clipRect, g_monitorClipRect);
CALL_ORIG_FUNC(ClipCursor)(&rect);
return LOG_RESULT(result);
}
BOOL WINAPI getClipCursor(LPRECT lpRect)
{
LOG_FUNC("GetClipCursor", lpRect);
Compat::ScopedCriticalSection lock(g_cs);
BOOL result = CALL_ORIG_FUNC(GetClipCursor)(lpRect);
if (result && !IsRectEmpty(&g_monitorClipRect))
{
*lpRect = g_clipRect;
}
return LOG_RESULT(result);
}
HCURSOR WINAPI getCursor()
{
LOG_FUNC("GetCursor");
Compat::ScopedCriticalSection lock(g_cs);
return LOG_RESULT(g_isEmulated ? g_cursor : CALL_ORIG_FUNC(GetCursor)());
}
BOOL WINAPI getCursorInfo(PCURSORINFO pci)
{
LOG_FUNC("GetCursorInfo", pci);
Compat::ScopedCriticalSection lock(g_cs);
BOOL result = CALL_ORIG_FUNC(GetCursorInfo)(pci);
if (result && pci->hCursor == g_nullCursor)
{
pci->hCursor = g_cursor;
}
return LOG_RESULT(result);
}
RECT intersectRect(RECT rect1, RECT rect2)
{
normalizeRect(rect1);
normalizeRect(rect2);
IntersectRect(&rect1, &rect1, &rect2);
return rect1;
}
void normalizeRect(RECT& rect)
{
if (rect.left == rect.right && rect.top == rect.bottom)
{
rect.right++;
rect.bottom++;
}
}
HCURSOR WINAPI setCursor(HCURSOR hCursor)
{
LOG_FUNC("SetCursor", hCursor);
return LOG_RESULT(Gdi::Cursor::setCursor(hCursor));
}
void updateClipRect()
{
auto hwnd = GetForegroundWindow();
if (!hwnd)
{
return;
}
DWORD pid = 0;
GetWindowThreadProcessId(hwnd, &pid);
if (pid != GetCurrentProcessId())
{
return;
}
RECT realClipRect = {};
CALL_ORIG_FUNC(GetClipCursor)(&realClipRect);
RECT clipRect = intersectRect(g_clipRect, g_monitorClipRect);
if (!EqualRect(&clipRect, &realClipRect))
{
CALL_ORIG_FUNC(ClipCursor)(&clipRect);
}
}
}
namespace Gdi
{
namespace Cursor
{
CURSORINFO getEmulatedCursorInfo()
{
CURSORINFO ci = {};
ci.cbSize = sizeof(ci);
Compat::ScopedCriticalSection lock(g_cs);
if (g_isEmulated)
{
CALL_ORIG_FUNC(GetCursorInfo)(&ci);
if (ci.hCursor == g_nullCursor)
{
ci.hCursor = g_cursor;
}
else
{
ci.hCursor = nullptr;
ci.flags = 0;
}
}
return ci;
}
void installHooks()
{
BYTE andPlane = 0xFF;
BYTE xorPlane = 0;
g_nullCursor = CreateCursor(nullptr, 0, 0, 1, 1, &andPlane, &xorPlane);
HOOK_FUNCTION(user32, ClipCursor, clipCursor);
HOOK_FUNCTION(user32, GetCursor, getCursor);
HOOK_FUNCTION(user32, GetCursorInfo, getCursorInfo);
HOOK_FUNCTION(user32, GetClipCursor, getClipCursor);
HOOK_FUNCTION(user32, SetCursor, ::setCursor);
}
bool isEmulated()
{
return g_isEmulated;
}
HCURSOR setCursor(HCURSOR cursor)
{
Compat::ScopedCriticalSection lock(g_cs);
if (!g_isEmulated)
{
return CALL_ORIG_FUNC(SetCursor)(cursor);
}
HCURSOR prevCursor = g_cursor;
if (cursor != g_nullCursor)
{
g_cursor = cursor;
CALL_ORIG_FUNC(SetCursor)(cursor ? g_nullCursor : nullptr);
}
return prevCursor;
}
void setEmulated(bool isEmulated)
{
Compat::ScopedCriticalSection lock(g_cs);
if (isEmulated == g_isEmulated)
{
return;
}
g_isEmulated = isEmulated;
g_prevCursorInfo = {};
if (isEmulated)
{
setCursor(CALL_ORIG_FUNC(GetCursor)());
}
else
{
CALL_ORIG_FUNC(SetCursor)(g_cursor);
g_cursor = nullptr;
}
}
void setMonitorClipRect(const RECT& rect)
{
Compat::ScopedCriticalSection lock(g_cs);
if (IsRectEmpty(&rect))
{
if (!IsRectEmpty(&g_monitorClipRect))
{
CALL_ORIG_FUNC(ClipCursor)(&g_clipRect);
g_clipRect = {};
g_monitorClipRect = {};
}
}
else
{
if (IsRectEmpty(&g_monitorClipRect))
{
CALL_ORIG_FUNC(GetClipCursor)(&g_clipRect);
}
g_monitorClipRect = rect;
updateClipRect();
}
}
bool update()
{
Compat::ScopedCriticalSection lock(g_cs);
if (!IsRectEmpty(&g_monitorClipRect))
{
updateClipRect();
}
if (g_isEmulated)
{
CURSORINFO cursorInfo = getEmulatedCursorInfo();
if ((CURSOR_SHOWING == cursorInfo.flags) != (CURSOR_SHOWING == g_prevCursorInfo.flags) ||
CURSOR_SHOWING == cursorInfo.flags &&
(cursorInfo.hCursor != g_prevCursorInfo.hCursor || cursorInfo.ptScreenPos != g_prevCursorInfo.ptScreenPos))
{
g_prevCursorInfo = cursorInfo;
return true;
}
}
return false;
}
}
}

17
DDrawCompat/Gdi/Cursor.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <Windows.h>
namespace Gdi
{
namespace Cursor
{
CURSORINFO getEmulatedCursorInfo();
void installHooks();
bool isEmulated();
HCURSOR setCursor(HCURSOR cursor);
void setMonitorClipRect(const RECT& rect);
void setEmulated(bool isEmulated);
bool update();
}
}

View File

@ -1,5 +1,6 @@
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/Caret.h>
#include <Gdi/Cursor.h>
#include <Gdi/Dc.h>
#include <Gdi/DcFunctions.h>
#include <Gdi/Font.h>
@ -46,6 +47,7 @@ namespace Gdi
ScrollFunctions::installHooks();
User32WndProcs::installHooks();
Caret::installHooks();
Cursor::installHooks();
Font::installHooks();
}

View File

@ -1,5 +1,7 @@
#include <D3dDdi/ScopedCriticalSection.h>
#include <DDraw/RealPrimarySurface.h>
#include <Gdi/CompatDc.h>
#include <Gdi/Cursor.h>
#include <Gdi/ScrollBar.h>
#include <Gdi/ScrollFunctions.h>
#include <Gdi/TitleBar.h>
@ -194,6 +196,57 @@ namespace
}
return origDefWindowProc(hwnd, msg, wParam, lParam);
}
case WM_SETCURSOR:
{
if (!Gdi::Cursor::isEmulated())
{
return origDefWindowProc(hwnd, msg, wParam, lParam);
}
switch (LOWORD(lParam))
{
case HTLEFT:
case HTRIGHT:
Gdi::Cursor::setCursor(LoadCursor(nullptr, IDC_SIZEWE));
return TRUE;
case HTTOP:
case HTBOTTOM:
Gdi::Cursor::setCursor(LoadCursor(nullptr, IDC_SIZENS));
return TRUE;
case HTTOPLEFT:
case HTBOTTOMRIGHT:
Gdi::Cursor::setCursor(LoadCursor(nullptr, IDC_SIZENWSE));
return TRUE;
case HTBOTTOMLEFT:
case HTTOPRIGHT:
Gdi::Cursor::setCursor(LoadCursor(nullptr, IDC_SIZENESW));
return TRUE;
}
HWND parent = GetAncestor(hwnd, GA_PARENT);
if (parent && SendMessage(parent, msg, wParam, lParam))
{
return TRUE;
}
if (HTCLIENT == LOWORD(lParam))
{
auto cursor = GetClassLong(hwnd, GCL_HCURSOR);
if (cursor)
{
Gdi::Cursor::setCursor(reinterpret_cast<HCURSOR>(cursor));
}
}
else
{
Gdi::Cursor::setCursor(LoadCursor(nullptr, IDC_ARROW));
}
return FALSE;
}
}
return defPaintProc(hwnd, msg, wParam, lParam, origDefWindowProc);

View File

@ -8,6 +8,7 @@
#include <Common/ScopedSrwLock.h>
#include <Dll/Dll.h>
#include <Gdi/CompatDc.h>
#include <Gdi/Cursor.h>
#include <Gdi/Dc.h>
#include <Gdi/PresentationWindow.h>
#include <Gdi/ScrollBar.h>
@ -172,65 +173,6 @@ namespace
IsWindowUnicode(hwnd) ? it->second.wndProcW : it->second.wndProcA);
}
void CALLBACK objectCreateEvent(
HWINEVENTHOOK /*hWinEventHook*/,
DWORD /*event*/,
HWND hwnd,
LONG idObject,
LONG /*idChild*/,
DWORD /*dwEventThread*/,
DWORD /*dwmsEventTime*/)
{
if (OBJID_WINDOW == idObject && !Gdi::PresentationWindow::isPresentationWindow(hwnd))
{
onCreateWindow(hwnd);
}
}
void CALLBACK objectStateChangeEvent(
HWINEVENTHOOK /*hWinEventHook*/,
DWORD /*event*/,
HWND hwnd,
LONG idObject,
LONG /*idChild*/,
DWORD /*dwEventThread*/,
DWORD /*dwmsEventTime*/)
{
switch (idObject)
{
case OBJID_TITLEBAR:
{
HDC dc = GetWindowDC(hwnd);
Gdi::TitleBar(hwnd).drawButtons(dc);
ReleaseDC(hwnd, dc);
break;
}
case OBJID_CLIENT:
if (!isUser32ScrollBar(hwnd))
{
break;
}
case OBJID_HSCROLL:
case OBJID_VSCROLL:
{
HDC dc = GetWindowDC(hwnd);
if (OBJID_CLIENT == idObject)
{
SendMessage(GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
reinterpret_cast<WPARAM>(dc), reinterpret_cast<LPARAM>(hwnd));
}
else
{
DefWindowProc(hwnd, WM_CTLCOLORSCROLLBAR,
reinterpret_cast<WPARAM>(dc), reinterpret_cast<LPARAM>(hwnd));
}
ReleaseDC(hwnd, dc);
break;
}
}
}
void onCreateWindow(HWND hwnd)
{
LOG_FUNC("onCreateWindow", hwnd);
@ -425,6 +367,72 @@ namespace
}
return LOG_RESULT(result);
}
void CALLBACK winEventProc(
HWINEVENTHOOK /*hWinEventHook*/,
DWORD event,
HWND hwnd,
LONG idObject,
LONG /*idChild*/,
DWORD /*dwEventThread*/,
DWORD /*dwmsEventTime*/)
{
LOG_FUNC("winEventProc", Compat::hex(event), hwnd, idObject);
switch (event)
{
case EVENT_OBJECT_CREATE:
if (OBJID_WINDOW == idObject && !Gdi::PresentationWindow::isPresentationWindow(hwnd))
{
onCreateWindow(hwnd);
}
break;
case EVENT_OBJECT_NAMECHANGE:
case EVENT_OBJECT_SHOW:
case EVENT_OBJECT_HIDE:
if (OBJID_CURSOR == idObject && Gdi::Cursor::isEmulated())
{
Gdi::Cursor::setCursor(GetCursor());
}
break;
case EVENT_OBJECT_STATECHANGE:
switch (idObject)
{
case OBJID_TITLEBAR:
{
HDC dc = GetWindowDC(hwnd);
Gdi::TitleBar(hwnd).drawButtons(dc);
ReleaseDC(hwnd, dc);
break;
}
case OBJID_CLIENT:
if (!isUser32ScrollBar(hwnd))
{
break;
}
case OBJID_HSCROLL:
case OBJID_VSCROLL:
{
HDC dc = GetWindowDC(hwnd);
if (OBJID_CLIENT == idObject)
{
SendMessage(GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
reinterpret_cast<WPARAM>(dc), reinterpret_cast<LPARAM>(hwnd));
}
else
{
DefWindowProc(hwnd, WM_CTLCOLORSCROLLBAR,
reinterpret_cast<WPARAM>(dc), reinterpret_cast<LPARAM>(hwnd));
}
ReleaseDC(hwnd, dc);
break;
}
}
}
}
}
namespace Gdi
@ -461,9 +469,13 @@ namespace Gdi
HOOK_FUNCTION(user32, UpdateLayeredWindowIndirect, updateLayeredWindowIndirect);
SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE,
Dll::g_currentModule, &objectCreateEvent, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
Dll::g_currentModule, &winEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE,
Dll::g_currentModule, &winEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE,
Dll::g_currentModule, &winEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
SetWinEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE,
Dll::g_currentModule, &objectStateChangeEvent, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
Dll::g_currentModule, &winEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
}
void onCreateWindow(HWND hwnd)

View File

@ -0,0 +1,19 @@
sampler2D s_dstTexture : register(s0);
sampler2D s_maskTexture : register(s1);
sampler2D s_colorTexture : register(s2);
sampler2D s_xorTexture : register(s3);
float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{
float4 dst = tex2D(s_dstTexture, texCoord);
float4 mask = tex2D(s_maskTexture, texCoord);
float4 color = tex2D(s_colorTexture, texCoord);
float4 maskedDst = dst * mask;
return float4(
tex2D(s_xorTexture, float2(maskedDst.r, color.r)).r,
tex2D(s_xorTexture, float2(maskedDst.g, color.g)).r,
tex2D(s_xorTexture, float2(maskedDst.b, color.b)).r,
1);
}

View File

@ -46,7 +46,6 @@ namespace
};
DWORD g_desktopBpp = 0;
RECT g_cursorRect = {};
ULONG g_displaySettingsUniquenessBias = 0;
EmulatedDisplayMode g_emulatedDisplayMode = {};
Compat::SrwLock g_srwLock;
@ -148,12 +147,9 @@ namespace
return result;
}
RECT clipRect = {};
{
Compat::ScopedSrwLockExclusive srwLock(g_srwLock);
++g_displaySettingsUniquenessBias;
clipRect = g_cursorRect;
if (lpDevMode)
{
g_emulatedDisplayMode.width = lpDevMode->dmPelsWidth;
@ -167,8 +163,6 @@ namespace
g_emulatedDisplayMode.rect.bottom = g_emulatedDisplayMode.rect.top + lpDevMode->dmPelsHeight;
g_emulatedDisplayMode.diff.cx = lpDevMode->dmPelsWidth - currDevMode.dmPelsWidth;
g_emulatedDisplayMode.diff.cy = lpDevMode->dmPelsHeight - currDevMode.dmPelsHeight;
IntersectRect(&clipRect, &clipRect, &g_emulatedDisplayMode.rect);
}
else
{
@ -177,8 +171,6 @@ namespace
}
}
CALL_ORIG_FUNC(ClipCursor)(&clipRect);
auto& dm = lpDevMode ? *lpDevMode : currDevMode;
LPARAM resolution = (dm.dmPelsHeight << 16) | dm.dmPelsWidth;
EnumWindows(sendDisplayChange, resolution);
@ -209,31 +201,6 @@ namespace
lpszDeviceName, lpDevMode, hwnd, dwflags, lParam));
}
BOOL WINAPI clipCursor(const RECT* lpRect)
{
LOG_FUNC("ClipCursor", lpRect);
BOOL result = CALL_ORIG_FUNC(ClipCursor)(lpRect);
if (!result)
{
return result;
}
RECT rect = {};
CALL_ORIG_FUNC(GetClipCursor)(&rect);
{
Compat::ScopedSrwLockExclusive srwLock(g_srwLock);
g_cursorRect = rect;
if (!g_emulatedDisplayMode.deviceName.empty())
{
IntersectRect(&rect, &rect, &g_emulatedDisplayMode.rect);
CALL_ORIG_FUNC(ClipCursor)(&rect);
}
}
return result;
}
void disableDwm8And16BitMitigation()
{
auto user32 = GetModuleHandle("user32");
@ -342,17 +309,6 @@ namespace
return CALL_ORIG_FUNC(GdiEntry13)() + g_displaySettingsUniquenessBias;
}
BOOL WINAPI getClipCursor(LPRECT lpRect)
{
LOG_FUNC("GetClipCursor", lpRect);
BOOL result = CALL_ORIG_FUNC(GetClipCursor)(lpRect);
if (result)
{
*lpRect = g_cursorRect;
}
return LOG_RESULT(result);
}
template <typename DevMode, typename EnumDisplaySettingsExFunc, typename Char>
SIZE getConfiguredResolution(EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName)
{
@ -671,16 +627,13 @@ namespace Win32
g_desktopBpp = dm.dmBitsPerPel;
g_emulatedDisplayMode.bpp = dm.dmBitsPerPel;
GetClipCursor(&g_cursorRect);
EnumDisplayMonitors(nullptr, nullptr, &initMonitor, 0);
HOOK_FUNCTION(user32, ChangeDisplaySettingsExA, changeDisplaySettingsExA);
HOOK_FUNCTION(user32, ChangeDisplaySettingsExW, changeDisplaySettingsExW);
HOOK_FUNCTION(user32, ClipCursor, clipCursor);
HOOK_FUNCTION(user32, EnumDisplaySettingsExA, enumDisplaySettingsExA);
HOOK_FUNCTION(user32, EnumDisplaySettingsExW, enumDisplaySettingsExW);
HOOK_FUNCTION(gdi32, GdiEntry13, gdiEntry13);
HOOK_FUNCTION(user32, GetClipCursor, getClipCursor);
HOOK_FUNCTION(gdi32, GetDeviceCaps, getDeviceCaps);
HOOK_FUNCTION(user32, GetMonitorInfoA, getMonitorInfoA);
HOOK_FUNCTION(user32, GetMonitorInfoW, getMonitorInfoW);