From eed82e29604ff999f0e68642d0397baa902b24ff Mon Sep 17 00:00:00 2001 From: elasota Date: Sun, 22 Dec 2019 00:35:30 -0500 Subject: [PATCH] Add mouse cursor handling --- ConvertColorCursors/ConvertColorCursors.cpp | 252 ++++++++++++++++++ .../ConvertColorCursors.vcxproj | 145 ++++++++++ .../ConvertColorCursors.vcxproj.filters | 25 ++ ConvertResources.bat | 3 + GlidePort.sln | 10 + GpCommon/EGpStandardCursor.h | 12 + GpCommon/GpColorCursor_Win32.cpp | 58 ++++ GpCommon/GpColorCursor_Win32.h | 24 ++ GpCommon/GpDisplayDriverProperties.h | 2 + GpCommon/GpWindows.h | 34 +-- GpCommon/IGpColorCursor.h | 6 + GpCommon/IGpDisplayDriver.h | 6 + GpD3D/GpD3D.vcxproj | 4 + GpD3D/GpD3D.vcxproj.filters | 12 + GpD3D/GpDisplayDriverD3D11.cpp | 130 ++++++++- GpD3D/GpDisplayDriverD3D11.h | 33 ++- GpD3D/GpFileSystem_Win32.cpp | 6 + GpD3D/GpFileSystem_Win32.h | 58 ++-- GpD3D/GpGlobalConfig.h | 3 +- GpD3D/GpMain.cpp | 1 + GpD3D/GpMain_Win32.cpp | 4 +- GpD3D/GpPLGlueDisplayDriver.cpp | 16 +- GpD3D/GpPLGlueDisplayDriver.h | 4 +- PictChecker/PictChecker.vcxproj | 6 +- PictChecker/PictChecker.vcxproj.filters | 2 +- PortabilityLayer/HostDisplayDriver.h | 7 +- PortabilityLayer/PLCore.cpp | 30 ++- PortabilityLayer/PLCore.h | 3 + PortabilityLayer/VirtualDirectory.h | 1 + stb.props | 10 + {PictChecker => stb}/stb_image_write.c | 0 {PictChecker => stb}/stb_image_write.h | 0 32 files changed, 835 insertions(+), 72 deletions(-) create mode 100644 ConvertColorCursors/ConvertColorCursors.cpp create mode 100644 ConvertColorCursors/ConvertColorCursors.vcxproj create mode 100644 ConvertColorCursors/ConvertColorCursors.vcxproj.filters create mode 100644 GpCommon/EGpStandardCursor.h create mode 100644 GpCommon/GpColorCursor_Win32.cpp create mode 100644 GpCommon/GpColorCursor_Win32.h create mode 100644 GpCommon/IGpColorCursor.h create mode 100644 stb.props rename {PictChecker => stb}/stb_image_write.c (100%) rename {PictChecker => stb}/stb_image_write.h (100%) diff --git a/ConvertColorCursors/ConvertColorCursors.cpp b/ConvertColorCursors/ConvertColorCursors.cpp new file mode 100644 index 0000000..12debe3 --- /dev/null +++ b/ConvertColorCursors/ConvertColorCursors.cpp @@ -0,0 +1,252 @@ +#include "CFileStream.h" +#include "MMHandleBlock.h" +#include "ResourceCompiledTypeList.h" +#include "ResourceFile.h" +#include "SharedTypes.h" +#include "PLBigEndian.h" +#include + +#include + +#include "stb_image_write.h" + +// This extracts all of the color cursors from a resource file and converts them to +// Windows cursor format. +// +// Windows only supports loading color cursors by loading them from an executable or DLL resource, +// or by loading them from a file, so we can't just do the conversion/construction at runtime. +// +// Also, classic Mac cursor features are not fully supported by newer operating systems. +// The classic Mac cursor function is basically (existing ^ color) where the mask bit isn't set, +// so it supports inversion masking and colors with the same cursor. +// +// Fortunately, none of the Glider PRO cursors use this combination. +// +// The color cursor format is not fully documented. Appears to be: +// Header +// CursorPixMapPrefix (at pixMapOffset) +// BEPixMap +// Pixel data +// BEColorTableHeader +// BEColorTableItem[...] + +struct CursorPixMapPrefix +{ + BEUInt32_t m_unknown; // Seems to always be zero + BEUInt16_t m_subFormat; // 0x8002 = 2 colors, 0x8004 = 4 colors, 0x8008 = 16 colors, 0x8010 = 256 colors +}; + +struct CursorHeader +{ + BEUInt16_t m_cursorType; + BEUInt32_t m_pixMapOffset; + BEUInt32_t m_pixDataOffset; + BEUInt32_t m_expandedData; + BEUInt16_t m_expandedDataDepth; + BEUInt32_t m_unused; + uint8_t m_bwCursor[32]; + uint8_t m_mask[32]; + BEUInt16_t m_hotSpotY; + BEUInt16_t m_hotSpotX; + BEUInt32_t m_colorTableResourceID; + BEUInt32_t m_cursorResourceID; +}; + +struct IconDir +{ + uint16_t m_reserved; + uint16_t m_type; + uint16_t m_numImages; +}; + +struct IconDirEntry +{ + uint8_t m_width; + uint8_t m_height; + uint8_t m_numColors; + uint8_t m_reserved; + uint16_t m_numPlanes_HotSpotX; + uint16_t m_bpp_HotSpotY; + uint32_t m_imageDataSize; + uint32_t m_imageDataOffset; +}; + +uint8_t CompactChannel(const uint8_t *doublet) +{ + unsigned int doubletValue = (doublet[0] << 8) + doublet[1]; + + return (doubletValue * 2 + 0x101) / 0x202; +} + +void WriteToFileCallback(void *context, void *data, int size) +{ + fwrite(data, 1, size, static_cast(context)); +} + +int main(int argc, const char **argv) +{ + FILE *f = nullptr; + errno_t err = fopen_s(&f, "Packaged\\ApplicationResources.gpr", "rb"); + if (err) + return err; + + PortabilityLayer::CFileStream stream(f); + + PortabilityLayer::ResourceFile *resFile = new PortabilityLayer::ResourceFile(); + if (!resFile->Load(&stream)) + return -1; + + stream.Close(); + + const PortabilityLayer::ResourceCompiledTypeList *typeList = resFile->GetResourceTypeList('crsr'); + if (!typeList) + return -1; + + const size_t numRefs = typeList->m_numRefs; + for (size_t i = 0; i < numRefs; i++) + { + const int resID = typeList->m_firstRef[i].m_resID; + const PortabilityLayer::MMHandleBlock *hBlock = resFile->GetResource('crsr', resID, true); + const void *cursorDataBase = hBlock->m_contents; + const void *cursorData = cursorDataBase; + + const CursorHeader *cursorHeader = static_cast(cursorData); + cursorData = cursorHeader + 1; + cursorData = static_cast(reinterpret_cast(cursorDataBase) + cursorHeader->m_pixMapOffset); + + const CursorPixMapPrefix *cursorPixMapPrefix = static_cast(cursorData); + + cursorData = cursorPixMapPrefix + 1; + + const BEPixMap *pixMap = reinterpret_cast(reinterpret_cast(cursorData)); + cursorData = pixMap + 1; + + cursorData = static_cast(reinterpret_cast(cursorDataBase) + cursorHeader->m_pixDataOffset); + const uint8_t *pixMapDataBytes = static_cast(cursorData); + + const Rect rect = pixMap->m_bounds.ToRect(); + const int width = rect.right - rect.left; + const int height = rect.bottom - rect.top; + const int componentSize = static_cast(pixMap->m_componentSize); + + switch (componentSize) + { + case 1: + case 2: + case 4: + case 8: + break; + default: + assert(false); + break; + } + + const int bitsRequired = width * height * componentSize; + const int bytesRequired = (bitsRequired + 7) / 8; + + uint8_t *colorValues = new uint8_t[width * height]; + + const int numPixels = width * height; + + switch (componentSize) + { + case 1: + for (int i = 0; i < numPixels; i++) + colorValues[i] = (pixMapDataBytes[i / 8] >> (7 - (i & 7) * 1)) & 0x1; + break; + case 2: + for (int i = 0; i < numPixels; i++) + colorValues[i] = (pixMapDataBytes[i / 4] >> (6 - (i & 3) * 2)) & 0x3; + break; + case 4: + for (int i = 0; i < numPixels; i++) + colorValues[i] = (pixMapDataBytes[i / 2] >> (4 - (i & 1) * 4)) & 0xf; + break; + case 8: + for (int i = 0; i < numPixels; i++) + colorValues[i] = pixMapDataBytes[i]; + break; + default: + assert(false); + break; + } + + cursorData = static_cast(pixMapDataBytes + bytesRequired); + const BEColorTableHeader *colorTableHeader = static_cast(cursorData); + + cursorData = colorTableHeader + 1; + + const BEColorTableItem *ctabItems = static_cast(cursorData); + + uint32_t decodedCTabItems[256]; + const int numCTabItems = colorTableHeader->m_numItemsMinusOne + 1; + + for (int i = 0; i < numCTabItems; i++) + { + const BEColorTableItem &item = ctabItems[i]; + unsigned char rgba[4]; + rgba[0] = CompactChannel(item.m_red); + rgba[1] = CompactChannel(item.m_green); + rgba[2] = CompactChannel(item.m_blue); + rgba[3] = 255; + + int index = item.m_index; + assert(index >= 0 && index < 256); + + memcpy(decodedCTabItems + index, rgba, 4); + } + + uint32_t *pixelDataRGBA = new uint32_t[width * height * 4]; + + for (int i = 0; i < numPixels; i++) + { + const uint8_t ctabIndex = colorValues[i]; + pixelDataRGBA[i] = decodedCTabItems[ctabIndex]; + + if (((cursorHeader->m_mask[i / 8] >> (7 - (i & 7))) & 1) == 0) + reinterpret_cast(pixelDataRGBA + i)[3] = 0; + } + + char outPath[64]; + sprintf_s(outPath, "Packaged\\WinCursors\\%i.cur", resID); + + FILE *outF = nullptr; + errno_t outErr = fopen_s(&outF, outPath, "wb"); + + if (!outErr) + { + IconDir iconDir; + iconDir.m_reserved = 0; + iconDir.m_type = 2; + iconDir.m_numImages = 1; + + IconDirEntry iconDirEntry; + iconDirEntry.m_width = width; + iconDirEntry.m_height = height; + iconDirEntry.m_numColors = 0; + iconDirEntry.m_reserved = 0; + iconDirEntry.m_numPlanes_HotSpotX = cursorHeader->m_hotSpotX; + iconDirEntry.m_bpp_HotSpotY = cursorHeader->m_hotSpotY; + iconDirEntry.m_imageDataSize = 0; + iconDirEntry.m_imageDataOffset = sizeof(IconDir) + sizeof(IconDirEntry); + + fwrite(&iconDir, 1, sizeof(IconDir), outF); + fwrite(&iconDirEntry, 1, sizeof(IconDirEntry), outF); + + long imageDataStart = ftell(outF); + stbi_write_png_to_func(WriteToFileCallback, outF, width, height, 4, pixelDataRGBA, width * 4); + long imageDataEnd = ftell(outF); + + fseek(outF, sizeof(IconDir), SEEK_SET); + + iconDirEntry.m_imageDataSize = static_cast(imageDataEnd - imageDataStart); + fwrite(&iconDirEntry, 1, sizeof(IconDirEntry), outF); + fclose(outF); + } + + delete[] colorValues; + delete[] pixelDataRGBA; + } + + return 0; +} diff --git a/ConvertColorCursors/ConvertColorCursors.vcxproj b/ConvertColorCursors/ConvertColorCursors.vcxproj new file mode 100644 index 0000000..f16fbf3 --- /dev/null +++ b/ConvertColorCursors/ConvertColorCursors.vcxproj @@ -0,0 +1,145 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {B852D549-4020-4477-8BFB-E199FF78B047} + ConvertColorCursors + 10.0.17763.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + {6ec62b0f-9353-40a4-a510-3788f1368b33} + + + + + + + + + + \ No newline at end of file diff --git a/ConvertColorCursors/ConvertColorCursors.vcxproj.filters b/ConvertColorCursors/ConvertColorCursors.vcxproj.filters new file mode 100644 index 0000000..310100b --- /dev/null +++ b/ConvertColorCursors/ConvertColorCursors.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/ConvertResources.bat b/ConvertResources.bat index 85ecee4..fd4f1a6 100644 --- a/ConvertResources.bat +++ b/ConvertResources.bat @@ -1,4 +1,7 @@ x64\Release\MiniRez.exe "GpApp2\Glider PRO.r" Packaged\ApplicationResources.gpr + +x64\Release\ConvertColorCursors.exe + x64\Release\hqx2gp.exe "GpApp2\Houses\Art Museum.binhex" "Packaged\Houses\Art Museum" x64\Release\hqx2gp.exe "GpApp2\Houses\California or Bust!.binhex" "Packaged\Houses\California or Bust!" x64\Release\hqx2gp.exe "GpApp2\Houses\Castle o' the Air.binhex" "Packaged\Houses\Castle o' the Air" diff --git a/GlidePort.sln b/GlidePort.sln index a393e17..2befae8 100644 --- a/GlidePort.sln +++ b/GlidePort.sln @@ -25,6 +25,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FreeType", "FreeType\FreeTy EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CompileShadersD3D11", "CompileShadersD3D11\CompileShadersD3D11.vcxproj", "{ED2F91E1-673A-4590-82B2-EB157927D3E3}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConvertColorCursors", "ConvertColorCursors\ConvertColorCursors.vcxproj", "{B852D549-4020-4477-8BFB-E199FF78B047}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -121,6 +123,14 @@ Global {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Release|x64.Build.0 = Release|x64 {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Release|x86.ActiveCfg = Release|Win32 {ED2F91E1-673A-4590-82B2-EB157927D3E3}.Release|x86.Build.0 = Release|Win32 + {B852D549-4020-4477-8BFB-E199FF78B047}.Debug|x64.ActiveCfg = Debug|x64 + {B852D549-4020-4477-8BFB-E199FF78B047}.Debug|x64.Build.0 = Debug|x64 + {B852D549-4020-4477-8BFB-E199FF78B047}.Debug|x86.ActiveCfg = Debug|Win32 + {B852D549-4020-4477-8BFB-E199FF78B047}.Debug|x86.Build.0 = Debug|Win32 + {B852D549-4020-4477-8BFB-E199FF78B047}.Release|x64.ActiveCfg = Release|x64 + {B852D549-4020-4477-8BFB-E199FF78B047}.Release|x64.Build.0 = Release|x64 + {B852D549-4020-4477-8BFB-E199FF78B047}.Release|x86.ActiveCfg = Release|Win32 + {B852D549-4020-4477-8BFB-E199FF78B047}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GpCommon/EGpStandardCursor.h b/GpCommon/EGpStandardCursor.h new file mode 100644 index 0000000..cde59a7 --- /dev/null +++ b/GpCommon/EGpStandardCursor.h @@ -0,0 +1,12 @@ +#pragma once + +namespace EGpStandardCursors +{ + enum EGpStandardCursor + { + kArrow, + kHidden, + }; +} + +typedef EGpStandardCursors::EGpStandardCursor EGpStandardCursor_t; diff --git a/GpCommon/GpColorCursor_Win32.cpp b/GpCommon/GpColorCursor_Win32.cpp new file mode 100644 index 0000000..935a89c --- /dev/null +++ b/GpCommon/GpColorCursor_Win32.cpp @@ -0,0 +1,58 @@ +#include "GpColorCursor_Win32.h" + +#include +#include + +void GpColorCursor_Win32::Destroy() +{ + this->DecRef(); +} + +GpColorCursor_Win32 *GpColorCursor_Win32::Load(const wchar_t *path) +{ + HANDLE imageH = LoadImageW(nullptr, path, IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE); + + if (imageH == nullptr) + return nullptr; + + HCURSOR cursor = reinterpret_cast(imageH); + void *storage = malloc(sizeof(GpColorCursor_Win32)); + if (!storage) + { + DestroyCursor(cursor); + return nullptr; + } + + return new (storage) GpColorCursor_Win32(reinterpret_cast(cursor)); +} + +GpColorCursor_Win32::GpColorCursor_Win32(HCURSOR cursor) + : m_cursor(cursor) + , m_refCount(1) +{ +} + +GpColorCursor_Win32::~GpColorCursor_Win32() +{ + DestroyCursor(m_cursor); +} + +const HCURSOR &GpColorCursor_Win32::GetHCursor() const +{ + return m_cursor; +} + +void GpColorCursor_Win32::IncRef() +{ + m_refCount++; +} + +void GpColorCursor_Win32::DecRef() +{ + m_refCount--; + if (m_refCount == 0) + { + this->~GpColorCursor_Win32(); + free(this); + } +} diff --git a/GpCommon/GpColorCursor_Win32.h b/GpCommon/GpColorCursor_Win32.h new file mode 100644 index 0000000..40d58a1 --- /dev/null +++ b/GpCommon/GpColorCursor_Win32.h @@ -0,0 +1,24 @@ +#pragma once + +#include "IGpColorCursor.h" +#include "GpWindows.h" + +class GpColorCursor_Win32 final : public IGpColorCursor +{ +public: + void Destroy() override; + + const HCURSOR &GetHCursor() const; + + void IncRef(); + void DecRef(); + + static GpColorCursor_Win32 *Load(const wchar_t *path); + +private: + GpColorCursor_Win32(HCURSOR cursor); + ~GpColorCursor_Win32(); + + HCURSOR m_cursor; + int m_refCount; +}; diff --git a/GpCommon/GpDisplayDriverProperties.h b/GpCommon/GpDisplayDriverProperties.h index 057bc38..588265b 100644 --- a/GpCommon/GpDisplayDriverProperties.h +++ b/GpCommon/GpDisplayDriverProperties.h @@ -21,6 +21,8 @@ struct GpDisplayDriverProperties unsigned int m_frameTimeLockMaxNumerator; unsigned int m_frameTimeLockMaxDenominator; + void *m_osGlobals; + // Tick function and context to call when a frame needs to be served. TickFunc_t m_tickFunc; void *m_tickFuncContext; diff --git a/GpCommon/GpWindows.h b/GpCommon/GpWindows.h index 85f5c31..12dbe23 100644 --- a/GpCommon/GpWindows.h +++ b/GpCommon/GpWindows.h @@ -1,18 +1,20 @@ -#pragma once - -#define NOMINMAX -#define WIN32_LEAN_AND_MEAN - -#include - -struct GPWindowsGlobals -{ - HINSTANCE m_hInstance; - HINSTANCE m_hPrevInstance; - LPSTR m_cmdLine; - int m_nCmdShow; -}; - -extern GPWindowsGlobals g_gpWindowsGlobals; +#pragma once + +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#define OEMRESOURCE + +#include + +struct GpWindowsGlobals +{ + HINSTANCE m_hInstance; + HINSTANCE m_hPrevInstance; + LPCSTR m_cmdLine; + LPCWSTR m_baseDir; + int m_nCmdShow; +}; + +extern GpWindowsGlobals g_gpWindowsGlobals; #undef CreateMutex diff --git a/GpCommon/IGpColorCursor.h b/GpCommon/IGpColorCursor.h new file mode 100644 index 0000000..30d54c7 --- /dev/null +++ b/GpCommon/IGpColorCursor.h @@ -0,0 +1,6 @@ +#pragma once + +struct IGpColorCursor +{ + virtual void Destroy() = 0; +}; diff --git a/GpCommon/IGpDisplayDriver.h b/GpCommon/IGpDisplayDriver.h index ab93f05..55152bf 100644 --- a/GpCommon/IGpDisplayDriver.h +++ b/GpCommon/IGpDisplayDriver.h @@ -1,8 +1,10 @@ #pragma once #include "PixelFormat.h" +#include "EGpStandardCursor.h" struct IGpDisplayDriverSurface; +struct IGpColorCursor; // Display drivers are responsible for timing and calling the game tick function. struct IGpDisplayDriver @@ -16,5 +18,9 @@ public: virtual IGpDisplayDriverSurface *CreateSurface(size_t width, size_t height, PortabilityLayer::PixelFormat pixelFormat) = 0; virtual void DrawSurface(IGpDisplayDriverSurface *surface, size_t x, size_t y, size_t width, size_t height) = 0; + virtual IGpColorCursor *LoadColorCursor(int cursorID) = 0; + virtual void SetColorCursor(IGpColorCursor *colorCursor) = 0; + virtual void SetStandardCursor(EGpStandardCursor_t standardCursor) = 0; + virtual void UpdatePalette(const void *paletteData) = 0; }; diff --git a/GpD3D/GpD3D.vcxproj b/GpD3D/GpD3D.vcxproj index c44602a..1908701 100644 --- a/GpD3D/GpD3D.vcxproj +++ b/GpD3D/GpD3D.vcxproj @@ -143,6 +143,7 @@ + @@ -172,7 +173,10 @@ + + + diff --git a/GpD3D/GpD3D.vcxproj.filters b/GpD3D/GpD3D.vcxproj.filters index 2efa6d8..bd557b8 100644 --- a/GpD3D/GpD3D.vcxproj.filters +++ b/GpD3D/GpD3D.vcxproj.filters @@ -99,6 +99,9 @@ Source Files\CompiledShaders + + Source Files + @@ -212,5 +215,14 @@ Header Files + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/GpD3D/GpDisplayDriverD3D11.cpp b/GpD3D/GpDisplayDriverD3D11.cpp index 8abb354..6a66e05 100644 --- a/GpD3D/GpDisplayDriverD3D11.cpp +++ b/GpD3D/GpDisplayDriverD3D11.cpp @@ -1,6 +1,8 @@ +#define _CRT_SECURE_NO_WARNINGS #include "GpDisplayDriverD3D11.h" #include "GpDisplayDriverSurfaceD3D11.h" #include "GpWindows.h" +#include "GpColorCursor_Win32.h" #include "GpFiber_Win32.h" #include @@ -308,6 +310,8 @@ bool GpDisplayDriverD3D11::InitResources() bool GpDisplayDriverD3D11::PresentFrameAndSync() { + SynchronizeCursors(); + float clearColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; m_deviceContext->ClearRenderTargetView(m_backBufferRTV, clearColor); @@ -443,9 +447,74 @@ bool GpDisplayDriverD3D11::PresentFrameAndSync() return true; } +void GpDisplayDriverD3D11::SynchronizeCursors() +{ + HCURSOR replacementCursor = nullptr; + + if (m_activeCursor) + { + if (m_pendingCursor != m_activeCursor) + { + if (m_pendingCursor == nullptr) + { + m_currentStandardCursor = m_pendingStandardCursor; + ChangeToStandardCursor(m_currentStandardCursor); + + m_activeCursor->DecRef(); + m_activeCursor = nullptr; + } + else + { + ChangeToCursor(m_pendingCursor->GetHCursor()); + + m_pendingCursor->IncRef(); + m_activeCursor->DecRef(); + m_activeCursor = m_pendingCursor; + } + } + } + else + { + if (m_pendingCursor) + { + m_pendingCursor->IncRef(); + m_activeCursor = m_pendingCursor; + + ChangeToCursor(m_activeCursor->GetHCursor()); + } + else + { + if (m_pendingStandardCursor != m_currentStandardCursor) + { + ChangeToStandardCursor(m_pendingStandardCursor); + m_currentStandardCursor = m_pendingStandardCursor; + } + } + } +} + + +void GpDisplayDriverD3D11::ChangeToCursor(HCURSOR cursor) +{ + if (m_mouseIsInClientArea) + SetCursor(cursor); + + SetClassLongPtrW(m_hwnd, GCLP_HCURSOR, reinterpret_cast(cursor)); +} + +void GpDisplayDriverD3D11::ChangeToStandardCursor(EGpStandardCursor_t cursor) +{ + switch (cursor) + { + case EGpStandardCursors::kArrow: + default: + ChangeToCursor(m_arrowCursor); + break; + } +} + void GpDisplayDriverD3D11::Run() { - HWND hWnd; WNDCLASSEX wc; LPVOID fiber = ConvertThreadToFiberEx(this, 0); @@ -460,7 +529,7 @@ void GpDisplayDriverD3D11::Run() wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WinProc; wc.hInstance = g_gpWindowsGlobals.m_hInstance; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hCursor = m_arrowCursor; wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszClassName = "GPD3D11WindowClass"; @@ -473,11 +542,11 @@ void GpDisplayDriverD3D11::Run() RECT wr = { 0, 0, m_windowWidth, m_windowHeight }; AdjustWindowRect(&wr, windowStyle, menus != NULL); - hWnd = CreateWindowExW(NULL, L"GPD3D11WindowClass", L"GlidePort (Direct3D 11)", WS_OVERLAPPEDWINDOW, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, menus, g_gpWindowsGlobals.m_hInstance, NULL); + m_hwnd = CreateWindowExW(NULL, L"GPD3D11WindowClass", L"GlidePort (Direct3D 11)", WS_OVERLAPPEDWINDOW, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, menus, g_gpWindowsGlobals.m_hInstance, NULL); - ShowWindow(hWnd, g_gpWindowsGlobals.m_nCmdShow); + ShowWindow(m_hwnd, g_gpWindowsGlobals.m_nCmdShow); - StartD3DForWindow(hWnd, m_swapChain, m_device, m_deviceContext); + StartD3DForWindow(m_hwnd, m_swapChain, m_device, m_deviceContext); InitResources(); @@ -495,6 +564,10 @@ void GpDisplayDriverD3D11::Run() if (msg.message == WM_QUIT) break; + else if (msg.message == WM_MOUSEMOVE) + m_mouseIsInClientArea = true; + else if (msg.message == WM_MOUSELEAVE) + m_mouseIsInClientArea = false; } else { @@ -612,6 +685,44 @@ void GpDisplayDriverD3D11::DrawSurface(IGpDisplayDriverSurface *surface, size_t m_deviceContext->DrawIndexed(6, 0, 0); } +IGpColorCursor *GpDisplayDriverD3D11::LoadColorCursor(int cursorID) +{ + const size_t bufSize = MAX_PATH; + wchar_t path[bufSize]; + + int sz = _snwprintf(path, bufSize, L"%sPackaged\\WinCursors\\%i.cur", m_osGlobals->m_baseDir, cursorID); + if (sz < 0 || static_cast(sz) >= bufSize) + return nullptr; + + return GpColorCursor_Win32::Load(path); +} + +// We can't just set the cursor because we want to post WM_SETCURSOR to keep it limited +// to the game window area, but depending on the fiber implementation, this may not be +// the window thread. +void GpDisplayDriverD3D11::SetColorCursor(IGpColorCursor *colorCursor) +{ + GpColorCursor_Win32 *winCursor = static_cast(colorCursor); + + winCursor->IncRef(); + + if (m_pendingCursor) + m_pendingCursor->DecRef(); + + m_pendingCursor = winCursor; +} + +void GpDisplayDriverD3D11::SetStandardCursor(EGpStandardCursor_t standardCursor) +{ + if (m_pendingCursor) + { + m_pendingCursor->DecRef(); + m_pendingCursor = nullptr; + } + + m_pendingStandardCursor = standardCursor; +} + void GpDisplayDriverD3D11::UpdatePalette(const void *paletteData) { const size_t dataSize = 256 * 4; @@ -643,14 +754,23 @@ GpDisplayDriverD3D11::GpDisplayDriverD3D11(const GpDisplayDriverProperties &prop , m_windowWidth(640) , m_windowHeight(480) , m_vosFiber(nullptr) + , m_osGlobals(static_cast(properties.m_osGlobals)) + , m_pendingCursor(nullptr) + , m_activeCursor(nullptr) + , m_currentStandardCursor(EGpStandardCursors::kArrow) + , m_pendingStandardCursor(EGpStandardCursors::kArrow) + , m_mouseIsInClientArea(false) { memset(&m_syncTimeBase, 0, sizeof(m_syncTimeBase)); QueryPerformanceFrequency(&m_QPFrequency); m_frameTimeSliceSize = m_QPFrequency.QuadPart * static_cast(properties.m_frameTimeLockNumerator) / static_cast(properties.m_frameTimeLockDenominator); + + m_arrowCursor = reinterpret_cast(LoadImageW(nullptr, MAKEINTRESOURCEW(OCR_NORMAL), IMAGE_CURSOR, 0, 0, LR_SHARED)); } GpDisplayDriverD3D11::~GpDisplayDriverD3D11() { + // GP TODO: Sloppy cleanup... Close the window!! } diff --git a/GpD3D/GpDisplayDriverD3D11.h b/GpD3D/GpDisplayDriverD3D11.h index ee5276a..1eaceb1 100644 --- a/GpD3D/GpDisplayDriverD3D11.h +++ b/GpD3D/GpDisplayDriverD3D11.h @@ -10,6 +10,9 @@ #include "PixelFormat.h" +struct GpWindowsGlobals; +class GpColorCursor_Win32; + struct IDXGISwapChain1; struct ID3D11Buffer; struct ID3D11DepthStencilState; @@ -35,6 +38,10 @@ public: IGpDisplayDriverSurface *CreateSurface(size_t width, size_t height, PortabilityLayer::PixelFormat pixelFormat) override; void DrawSurface(IGpDisplayDriverSurface *surface, size_t x, size_t y, size_t width, size_t height) override; + IGpColorCursor *LoadColorCursor(int cursorID) override; + void SetColorCursor(IGpColorCursor *colorCursor) override; + void SetStandardCursor(EGpStandardCursor_t standardCursor) override; + void UpdatePalette(const void *paletteData) override; static GpDisplayDriverD3D11 *Create(const GpDisplayDriverProperties &properties); @@ -52,12 +59,22 @@ private: float m_unused[2]; }; + struct CompactedPresentHistoryItem + { + LARGE_INTEGER m_timestamp; + unsigned int m_numFrames; + }; + GpDisplayDriverD3D11(const GpDisplayDriverProperties &properties); ~GpDisplayDriverD3D11(); bool InitResources(); bool PresentFrameAndSync(); + void SynchronizeCursors(); + void ChangeToCursor(HCURSOR cursor); + void ChangeToStandardCursor(EGpStandardCursor_t cursor); + GpComPtr m_swapChain; GpComPtr m_device; GpComPtr m_deviceContext; @@ -77,12 +94,6 @@ private: GpComPtr m_backBufferTexture; GpComPtr m_backBufferRTV; - struct CompactedPresentHistoryItem - { - LARGE_INTEGER m_timestamp; - unsigned int m_numFrames; - }; - GpRingBuffer m_presentHistory; GpDisplayDriverProperties m_properties; @@ -97,5 +108,15 @@ private: DWORD m_windowWidth; DWORD m_windowHeight; + GpColorCursor_Win32 *m_activeCursor; + GpColorCursor_Win32 *m_pendingCursor; + EGpStandardCursor_t m_currentStandardCursor; + EGpStandardCursor_t m_pendingStandardCursor; + bool m_mouseIsInClientArea; + GpFiber *m_vosFiber; + GpWindowsGlobals *m_osGlobals; + + HCURSOR m_arrowCursor; + HWND m_hwnd; }; diff --git a/GpD3D/GpFileSystem_Win32.cpp b/GpD3D/GpFileSystem_Win32.cpp index 66b20b0..ac7f064 100644 --- a/GpD3D/GpFileSystem_Win32.cpp +++ b/GpD3D/GpFileSystem_Win32.cpp @@ -95,6 +95,7 @@ GpDirectoryCursor_Win32::~GpDirectoryCursor_Win32() GpFileSystem_Win32::GpFileSystem_Win32() { + // GP TODO: This shouldn't be static init since it allocates memory m_executablePath[0] = 0; PWSTR docsPath; @@ -201,6 +202,11 @@ PortabilityLayer::HostDirectoryCursor *GpFileSystem_Win32::ScanDirectory(Portabi return GpDirectoryCursor_Win32::Create(ff, findData); } +const wchar_t *GpFileSystem_Win32::GetBasePath() const +{ + return m_executablePath; +} + GpFileSystem_Win32 *GpFileSystem_Win32::GetInstance() { return &ms_instance; diff --git a/GpD3D/GpFileSystem_Win32.h b/GpD3D/GpFileSystem_Win32.h index 1dc27ea..e52718a 100644 --- a/GpD3D/GpFileSystem_Win32.h +++ b/GpD3D/GpFileSystem_Win32.h @@ -1,31 +1,33 @@ -#pragma once - -#include "HostFileSystem.h" - +#pragma once + +#include "HostFileSystem.h" + #include "GpCoreDefs.h" -#include "GpWindows.h" - -#include - -class GpFileSystem_Win32 final : public PortabilityLayer::HostFileSystem -{ -public: - GpFileSystem_Win32(); - - bool FileExists(PortabilityLayer::EVirtualDirectory virtualDirectory, const char *path) override; - PortabilityLayer::IOStream *OpenFile(PortabilityLayer::EVirtualDirectory virtualDirectory, const char *path, bool writeAccess, bool create) override; - PortabilityLayer::HostDirectoryCursor *ScanDirectory(PortabilityLayer::EVirtualDirectory virtualDirectory) override; - - static GpFileSystem_Win32 *GetInstance(); - -private: - bool ResolvePath(PortabilityLayer::EVirtualDirectory virtualDirectory, const char *path, wchar_t *outPath); - - std::wstring m_prefsDir; - std::wstring m_packagedDir; +#include "GpWindows.h" + +#include + +class GpFileSystem_Win32 final : public PortabilityLayer::HostFileSystem +{ +public: + GpFileSystem_Win32(); + + bool FileExists(PortabilityLayer::EVirtualDirectory virtualDirectory, const char *path) override; + PortabilityLayer::IOStream *OpenFile(PortabilityLayer::EVirtualDirectory virtualDirectory, const char *path, bool writeAccess, bool create) override; + PortabilityLayer::HostDirectoryCursor *ScanDirectory(PortabilityLayer::EVirtualDirectory virtualDirectory) override; + + const wchar_t *GetBasePath() const; + + static GpFileSystem_Win32 *GetInstance(); + +private: + bool ResolvePath(PortabilityLayer::EVirtualDirectory virtualDirectory, const char *path, wchar_t *outPath); + + std::wstring m_prefsDir; + std::wstring m_packagedDir; std::wstring m_housesDir; std::wstring m_resourcesDir; - wchar_t m_executablePath[MAX_PATH]; - - static GpFileSystem_Win32 ms_instance; -}; + wchar_t m_executablePath[MAX_PATH]; + + static GpFileSystem_Win32 ms_instance; +}; diff --git a/GpD3D/GpGlobalConfig.h b/GpD3D/GpGlobalConfig.h index 54e8bcb..bc9a990 100644 --- a/GpD3D/GpGlobalConfig.h +++ b/GpD3D/GpGlobalConfig.h @@ -4,7 +4,8 @@ struct GpGlobalConfig { - EGpDisplayDriverType m_displayDriverType; + EGpDisplayDriverType m_displayDriverType; + void *m_osGlobals; }; extern GpGlobalConfig g_gpGlobalConfig; diff --git a/GpD3D/GpMain.cpp b/GpD3D/GpMain.cpp index 0a00821..737270a 100644 --- a/GpD3D/GpMain.cpp +++ b/GpD3D/GpMain.cpp @@ -49,6 +49,7 @@ int GpMain::Run() ddProps.m_renderFuncContext = appEnvironment; ddProps.m_type = g_gpGlobalConfig.m_displayDriverType; + ddProps.m_osGlobals = g_gpGlobalConfig.m_osGlobals; GpAudioDriverProperties adProps; memset(&adProps, 0, sizeof(adProps)); diff --git a/GpD3D/GpMain_Win32.cpp b/GpD3D/GpMain_Win32.cpp index 37eaa90..257c608 100644 --- a/GpD3D/GpMain_Win32.cpp +++ b/GpD3D/GpMain_Win32.cpp @@ -13,7 +13,7 @@ #include -GPWindowsGlobals g_gpWindowsGlobals; +GpWindowsGlobals g_gpWindowsGlobals; extern "C" __declspec(dllimport) IGpAudioDriver *GpDriver_CreateAudioDriver_XAudio2(const GpAudioDriverProperties &properties); @@ -26,8 +26,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine g_gpWindowsGlobals.m_hPrevInstance = hPrevInstance; g_gpWindowsGlobals.m_cmdLine = lpCmdLine; g_gpWindowsGlobals.m_nCmdShow = nCmdShow; + g_gpWindowsGlobals.m_baseDir = GpFileSystem_Win32::GetInstance()->GetBasePath(); g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_D3D11; + g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals; GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_D3D11, GpDisplayDriverFactoryD3D11::Create); GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_XAudio2, GpDriver_CreateAudioDriver_XAudio2); diff --git a/GpD3D/GpPLGlueDisplayDriver.cpp b/GpD3D/GpPLGlueDisplayDriver.cpp index 3ac92a4..df5d218 100644 --- a/GpD3D/GpPLGlueDisplayDriver.cpp +++ b/GpD3D/GpPLGlueDisplayDriver.cpp @@ -1,4 +1,5 @@ -#include "GpPLGlueDisplayDriver.h" +#include "GpPLGlueDisplayDriver.h" +#include "VirtualDirectory.h" #include "IGpDisplayDriver.h" GpPLGlueDisplayDriver::GpPLGlueDisplayDriver() @@ -9,10 +10,21 @@ GpPLGlueDisplayDriver::GpPLGlueDisplayDriver() void GpPLGlueDisplayDriver::GetDisplayResolution(unsigned int *width, unsigned int *height, PortabilityLayer::PixelFormat *bpp) { m_displayDriver->GetDisplayResolution(width, height, bpp); +} + +IGpColorCursor *GpPLGlueDisplayDriver::LoadColorCursor(int cursorID) +{ + return m_displayDriver->LoadColorCursor(cursorID); } -void GpPLGlueDisplayDriver::HideCursor() +void GpPLGlueDisplayDriver::SetColorCursor(IGpColorCursor *colorCursor) { + m_displayDriver->SetColorCursor(colorCursor); +} + +void GpPLGlueDisplayDriver::SetStandardCursor(EGpStandardCursor_t standardCursor) +{ + m_displayDriver->SetStandardCursor(standardCursor); } GpPLGlueDisplayDriver *GpPLGlueDisplayDriver::GetInstance() diff --git a/GpD3D/GpPLGlueDisplayDriver.h b/GpD3D/GpPLGlueDisplayDriver.h index f30c375..5653f14 100644 --- a/GpD3D/GpPLGlueDisplayDriver.h +++ b/GpD3D/GpPLGlueDisplayDriver.h @@ -10,7 +10,9 @@ public: GpPLGlueDisplayDriver(); void GetDisplayResolution(unsigned int *width, unsigned int *height, PortabilityLayer::PixelFormat *bpp) override; - void HideCursor() override; + IGpColorCursor *LoadColorCursor(int id) override; + void SetColorCursor(IGpColorCursor *colorCursor) override; + void SetStandardCursor(EGpStandardCursor_t standardCursor) override; void SetGpDisplayDriver(IGpDisplayDriver *displayDriver); diff --git a/PictChecker/PictChecker.vcxproj b/PictChecker/PictChecker.vcxproj index 0c36939..d3e89db 100644 --- a/PictChecker/PictChecker.vcxproj +++ b/PictChecker/PictChecker.vcxproj @@ -60,21 +60,25 @@ + + + + @@ -123,8 +127,8 @@ + - diff --git a/PictChecker/PictChecker.vcxproj.filters b/PictChecker/PictChecker.vcxproj.filters index 89cc874..36bee73 100644 --- a/PictChecker/PictChecker.vcxproj.filters +++ b/PictChecker/PictChecker.vcxproj.filters @@ -18,7 +18,7 @@ Source Files - + Source Files diff --git a/PortabilityLayer/HostDisplayDriver.h b/PortabilityLayer/HostDisplayDriver.h index 5df4060..f2dad90 100644 --- a/PortabilityLayer/HostDisplayDriver.h +++ b/PortabilityLayer/HostDisplayDriver.h @@ -3,6 +3,9 @@ #define __PL_HOST_DISPLAY_DRIVER_H__ #include "PixelFormat.h" +#include "EGpStandardCursor.h" + +struct IGpColorCursor; namespace PortabilityLayer { @@ -10,7 +13,9 @@ namespace PortabilityLayer { public: virtual void GetDisplayResolution(unsigned int *width, unsigned int *height, PixelFormat *pixelFormat) = 0; - virtual void HideCursor() = 0; + virtual IGpColorCursor *LoadColorCursor(int id) = 0; + virtual void SetColorCursor(IGpColorCursor *colorCursor) = 0; + virtual void SetStandardCursor(EGpStandardCursor_t standardCursor) = 0; static void SetInstance(HostDisplayDriver *instance); static HostDisplayDriver *GetInstance(); diff --git a/PortabilityLayer/PLCore.cpp b/PortabilityLayer/PLCore.cpp index af84684..9b32e59 100644 --- a/PortabilityLayer/PLCore.cpp +++ b/PortabilityLayer/PLCore.cpp @@ -14,6 +14,7 @@ #include "HostDisplayDriver.h" #include "HostSystemServices.h" #include "HostVOSEventQueue.h" +#include "IGpColorCursor.h" #include "InputManager.h" #include "ResourceManager.h" #include "MacFileInfo.h" @@ -64,6 +65,7 @@ static void ImportVOSEvents() void InitCursor() { + PortabilityLayer::HostDisplayDriver::GetInstance()->SetStandardCursor(EGpStandardCursors::kArrow); } Rect BERect::ToRect() const @@ -90,26 +92,34 @@ CursHandle GetCursor(int cursorID) CCrsrHandle GetCCursor(int cursorID) { - PortabilityLayer::ResourceManager *resManager = PortabilityLayer::ResourceManager::GetInstance(); - PortabilityLayer::MMHandleBlock *ccRes = resManager->GetResource('crsr', cursorID); + PortabilityLayer::HostDisplayDriver *driver = PortabilityLayer::HostDisplayDriver::GetInstance(); + IGpColorCursor *hwCursor = driver->LoadColorCursor(cursorID); - if (!ccRes) + if (!hwCursor) return nullptr; - PortabilityLayer::MMHandleBlock *copy = PortabilityLayer::MemoryManager::GetInstance()->AllocHandle(ccRes->m_size); - memcpy(copy->m_contents, ccRes->m_contents, ccRes->m_size); + CCrsrHandle hdl = PortabilityLayer::MemoryManager::GetInstance()->NewHandle(); + if (!hdl) + { + hwCursor->Destroy(); + return nullptr; + } - return reinterpret_cast(copy); + CCursor *ccursor = *hdl; + ccursor->hwCursor = hwCursor; + + return hdl; } void SetCCursor(CCrsrHandle handle) { - PL_NotYetImplemented_Minor(); + assert(handle); + PortabilityLayer::HostDisplayDriver::GetInstance()->SetColorCursor((*handle)->hwCursor); } void HideCursor() { - PortabilityLayer::HostDisplayDriver::GetInstance()->HideCursor(); + PortabilityLayer::HostDisplayDriver::GetInstance()->SetStandardCursor(EGpStandardCursors::kHidden); } void SetCursor(CursPtr cursor) @@ -124,7 +134,9 @@ void SetBuiltinCursor(int builtinCursor) void DisposeCCursor(CCrsrHandle handle) { - PL_NotYetImplemented(); + (*handle)->hwCursor->Destroy(); + + PortabilityLayer::MemoryManager::GetInstance()->ReleaseHandle(reinterpret_cast(handle)); } void Delay(int ticks, UInt32 *endTickCount) diff --git a/PortabilityLayer/PLCore.h b/PortabilityLayer/PLCore.h index e52b4bc..8f028a4 100644 --- a/PortabilityLayer/PLCore.h +++ b/PortabilityLayer/PLCore.h @@ -12,6 +12,8 @@ #pragma warning(error:4311) // Pointer truncation to int #endif +struct IGpColorCursor; + namespace PortabilityLayer { struct MMHandleBlock; @@ -92,6 +94,7 @@ struct Cursor struct CCursor { + IGpColorCursor *hwCursor; }; struct Window diff --git a/PortabilityLayer/VirtualDirectory.h b/PortabilityLayer/VirtualDirectory.h index fcbc646..6d2b59f 100644 --- a/PortabilityLayer/VirtualDirectory.h +++ b/PortabilityLayer/VirtualDirectory.h @@ -11,5 +11,6 @@ namespace PortabilityLayer EVirtualDirectory_UserData, EVirtualDirectory_Prefs, EVirtualDirectory_Fonts, + EVirtualDirectory_Cursors, }; } diff --git a/stb.props b/stb.props new file mode 100644 index 0000000..e7275df --- /dev/null +++ b/stb.props @@ -0,0 +1,10 @@ + + + + + + $(SolutionDir)stb;$(IncludePath) + + + + \ No newline at end of file diff --git a/PictChecker/stb_image_write.c b/stb/stb_image_write.c similarity index 100% rename from PictChecker/stb_image_write.c rename to stb/stb_image_write.c diff --git a/PictChecker/stb_image_write.h b/stb/stb_image_write.h similarity index 100% rename from PictChecker/stb_image_write.h rename to stb/stb_image_write.h