33#ifdef __WINDOWS__
44
55#include < Tempest/Event>
6+ #include < Tempest/Pixmap>
67#include < Tempest/Except>
78#include < Tempest/TextCodec>
89#include < Tempest/Window>
910
1011#include < atomic>
1112#include < unordered_set>
1213#include < thread>
14+ #include < vector>
1315
1416#include < windows.h>
1517#include < shellscalingapi.h>
@@ -328,7 +330,13 @@ void WindowsApi::implSetCursorPosition(SystemApi::Window *w, int x, int y) {
328330 }
329331 }
330332
333+ static HCURSOR customCursor = nullptr ;
334+
331335static void SetCursor (CursorShape show) {
336+ if (show==CursorShape::Custom && customCursor!=nullptr ) {
337+ SetCursor (customCursor);
338+ return ;
339+ }
332340 LPSTR idc = IDC_ARROW ;
333341 switch (show) {
334342 case Tempest::CursorShape::Arrow:
@@ -355,6 +363,9 @@ static void SetCursor(CursorShape show) {
355363 case Tempest::CursorShape::SizeAll:
356364 idc = IDC_SIZEALL ;
357365 break ;
366+ case Tempest::CursorShape::Custom:
367+ idc = IDC_ARROW ;
368+ break ;
358369 }
359370 HCURSOR hcur = idc==nullptr ? nullptr : LoadCursorA (nullptr , idc);
360371 SetCursor (hcur);
@@ -364,6 +375,69 @@ void WindowsApi::implShowCursor(SystemApi::Window*, CursorShape show) {
364375 SetCursor (show);
365376 }
366377
378+ static HCURSOR makeCursor (const Pixmap& image, int hotX, int hotY) {
379+ if (image.isEmpty () || image.format ()!=TextureFormat::RGBA8 )
380+ return nullptr ;
381+
382+ const int w = int (image.w ());
383+ const int h = int (image.h ());
384+
385+ BITMAPV5HEADER bi = {};
386+ bi.bV5Size = sizeof (bi);
387+ bi.bV5Width = w;
388+ bi.bV5Height = -h; // top-down rows, as in Pixmap
389+ bi.bV5Planes = 1 ;
390+ bi.bV5BitCount = 32 ;
391+ bi.bV5Compression = BI_BITFIELDS ;
392+ bi.bV5RedMask = 0x00FF0000 ;
393+ bi.bV5GreenMask = 0x0000FF00 ;
394+ bi.bV5BlueMask = 0x000000FF ;
395+ bi.bV5AlphaMask = 0xFF000000 ;
396+
397+ void * bits = nullptr ;
398+ HDC dc = GetDC (nullptr );
399+ HBITMAP color = CreateDIBSection (dc, reinterpret_cast <BITMAPINFO *>(&bi), DIB_RGB_COLORS , &bits, nullptr , 0 );
400+ ReleaseDC (nullptr , dc);
401+ if (color==nullptr )
402+ return nullptr ;
403+
404+ auto src = reinterpret_cast <const uint8_t *>(image.data ());
405+ auto dst = reinterpret_cast <uint8_t *>(bits);
406+ for (size_t i=0 ; i<size_t (w)*size_t (h); ++i) {
407+ dst[i*4 +0 ] = src[i*4 +2 ];
408+ dst[i*4 +1 ] = src[i*4 +1 ];
409+ dst[i*4 +2 ] = src[i*4 +0 ];
410+ dst[i*4 +3 ] = src[i*4 +3 ];
411+ }
412+
413+ // The alpha channel of a 32-bit color bitmap takes precedence; an empty AND mask keeps every pixel.
414+ std::vector<uint8_t > maskBits (size_t ((w+15 )/16 *2 )*size_t (h), 0 );
415+ HBITMAP mask = CreateBitmap (w, h, 1 , 1 , maskBits.data ());
416+
417+ ICONINFO info = {};
418+ info.fIcon = FALSE ;
419+ info.xHotspot = DWORD (hotX);
420+ info.yHotspot = DWORD (hotY);
421+ info.hbmMask = mask;
422+ info.hbmColor = color;
423+ HCURSOR ret = CreateIconIndirect (&info);
424+
425+ DeleteObject (color);
426+ DeleteObject (mask);
427+ return ret;
428+ }
429+
430+ void WindowsApi::implSetCustomCursor (const Pixmap& image, int hotX, int hotY) {
431+ HCURSOR prev = customCursor;
432+ customCursor = makeCursor (image, hotX, hotY);
433+ if (prev==nullptr )
434+ return ;
435+ // Swap an image that is on screen, so a destroyed cursor is never shown.
436+ if (GetCursor ()==prev)
437+ SetCursor (customCursor!=nullptr ? customCursor : LoadCursorA (nullptr , IDC_ARROW ));
438+ DestroyCursor (prev);
439+ }
440+
367441long long WindowsApi::windowProc (void *_hWnd, uint32_t msg, const unsigned long long wParam, const long long lParam) {
368442 HWND hWnd = HWND (_hWnd);
369443
0 commit comments