SDL_image: Set the cursor hotspot properties when loading CUR images

From edd80cc9ea38bfb01f643cd1c5a9e4db1e455d02 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 10 Feb 2025 11:55:28 -0800
Subject: [PATCH] Set the cursor hotspot properties when loading CUR images

---
 CHANGES.txt    |  1 +
 CMakeLists.txt |  2 +-
 src/IMG_bmp.c  | 39 ++++++++++++++++++++++++++-------------
 3 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 81cb802f4..a99ff4473 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 3.2.2:
  * Fixed partial alpha in ICO and CUR images
+ * Set the cursor hotspot properties when loading CUR images
 
 3.0.0:
  * Added support for loading HDR AVIF images
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 94f0ae916..f50282954 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
 set(MAJOR_VERSION 3)
 set(MINOR_VERSION 2)
 set(MICRO_VERSION 1)
-set(SDL_REQUIRED_VERSION 3.2.0)
+set(SDL_REQUIRED_VERSION 3.2.5)
 
 project(SDL3_image
     LANGUAGES C
diff --git a/src/IMG_bmp.c b/src/IMG_bmp.c
index 82b431b0c..69bad1c9b 100644
--- a/src/IMG_bmp.c
+++ b/src/IMG_bmp.c
@@ -34,6 +34,9 @@
 
 #ifdef LOAD_BMP
 
+#define ICON_TYPE_ICO   1
+#define ICON_TYPE_CUR   2
+
 /* See if an image is contained in a data source */
 bool IMG_isBMP(SDL_IOStream *src)
 {
@@ -85,12 +88,12 @@ static bool IMG_isICOCUR(SDL_IOStream *src, int type)
 
 bool IMG_isICO(SDL_IOStream *src)
 {
-    return IMG_isICOCUR(src, 1);
+    return IMG_isICOCUR(src, ICON_TYPE_ICO);
 }
 
 bool IMG_isCUR(SDL_IOStream *src)
 {
-    return IMG_isICOCUR(src, 2);
+    return IMG_isICOCUR(src, ICON_TYPE_CUR);
 }
 
 #include <SDL3/SDL_error.h>
@@ -105,13 +108,12 @@ bool IMG_isCUR(SDL_IOStream *src)
 #define BI_BITFIELDS    3
 #endif
 
-static SDL_Surface *LoadBMP_IO (SDL_IOStream *src, bool closeio)
+static SDL_Surface *LoadBMP_IO(SDL_IOStream *src, bool closeio)
 {
     return SDL_LoadBMP_IO(src, closeio);
 }
 
-static SDL_Surface *
-LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
+static SDL_Surface *LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
 {
     bool was_error = true;
     Sint64 fp_offset = 0;
@@ -127,6 +129,8 @@ LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
     int ExpandBMP;
     Uint8 maxCol = 0;
     Uint32 icoOfs = 0;
+    int nHotX = 0;
+    int nHotY = 0;
     Uint32 palette[256];
 
     /* The Win32 ICO file header (14 bytes) */
@@ -171,22 +175,20 @@ LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
         Uint8 bWidth;       /* Uint8, but 0 = 256 ! */
         Uint8 bHeight;      /* Uint8, but 0 = 256 ! */
         Uint8 bColorCount;  /* Uint8, but 0 = 256 ! */
-        /*
         Uint8 bReserved;
         Uint16 wPlanes;
         Uint16 wBitCount;
         Uint32 dwBytesInRes;
-        */
         Uint32 dwImageOffset;
         int nWidth, nHeight, nColorCount;
 
         if (!SDL_ReadU8(src, &bWidth) ||
             !SDL_ReadU8(src, &bHeight) ||
             !SDL_ReadU8(src, &bColorCount) ||
-            !SDL_ReadU8(src, NULL /* bReserved */) ||
-            !SDL_ReadU16LE(src, NULL /* wPlanes */) ||
-            !SDL_ReadU16LE(src, NULL /* wBitCount */) ||
-            !SDL_ReadU32LE(src, NULL /* dwBytesInRes */) ||
+            !SDL_ReadU8(src, &bReserved) ||
+            !SDL_ReadU16LE(src, &wPlanes) ||
+            !SDL_ReadU16LE(src, &wBitCount) ||
+            !SDL_ReadU32LE(src, &dwBytesInRes) ||
             !SDL_ReadU32LE(src, &dwImageOffset)) {
             goto done;
         }
@@ -207,6 +209,11 @@ LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
             nColorCount = 256;
         }
 
+        if (type == ICON_TYPE_CUR) {
+            nHotX = wPlanes;
+            nHotY = wBitCount;
+        }
+
         //SDL_Log("%dx%d@%d - %08x\n", nWidth, nHeight, nColorCount, dwImageOffset);
         (void)nWidth;
         (void)nHeight;
@@ -417,6 +424,12 @@ LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
         }
     }
 
+    if (type == ICON_TYPE_CUR) {
+        SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
+        SDL_SetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, nHotX);
+        SDL_SetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER, nHotY);
+    }
+
     was_error = false;
 
 done:
@@ -444,13 +457,13 @@ SDL_Surface *IMG_LoadBMP_IO(SDL_IOStream *src)
 /* Load a ICO type image from an SDL datasource */
 SDL_Surface *IMG_LoadICO_IO(SDL_IOStream *src)
 {
-    return LoadICOCUR_IO(src, 1, false);
+    return LoadICOCUR_IO(src, ICON_TYPE_ICO, false);
 }
 
 /* Load a CUR type image from an SDL datasource */
 SDL_Surface *IMG_LoadCUR_IO(SDL_IOStream *src)
 {
-    return LoadICOCUR_IO(src, 2, false);
+    return LoadICOCUR_IO(src, ICON_TYPE_CUR, false);
 }
 
 #else