SDL: api: Added SDL_GetNumProperties().

From 82141a2439acc111661102ba6f968c85e71cff40 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 10 Jul 2026 23:55:36 -0400
Subject: [PATCH] api: Added SDL_GetNumProperties().

Fixes #14761.
---
 include/SDL3/SDL_properties.h     | 15 +++++++++++++++
 src/SDL_hashtable.c               | 11 ++++++++---
 src/SDL_hashtable.h               | 18 ++++++++++++++++++
 src/SDL_properties.c              | 19 +++++++++++++++++++
 src/dynapi/SDL_dynapi.exports     |  1 +
 src/dynapi/SDL_dynapi.sym         |  1 +
 src/dynapi/SDL_dynapi_overrides.h |  1 +
 src/dynapi/SDL_dynapi_procs.h     |  1 +
 8 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/include/SDL3/SDL_properties.h b/include/SDL3/SDL_properties.h
index abd7b2aa5057c..e8c27c10c2f71 100644
--- a/include/SDL3/SDL_properties.h
+++ b/include/SDL3/SDL_properties.h
@@ -508,6 +508,21 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetBooleanProperty(SDL_PropertiesID props,
  */
 extern SDL_DECLSPEC bool SDLCALL SDL_ClearProperty(SDL_PropertiesID props, const char *name);
 
+/**
+ * Get the current number of items in a group of properties.
+ *
+ * For an invalid SDL_PropertiesID, this returns zero and does not set an
+ * error message.
+ *
+ * \param props the properties to query.
+ * \returns the number of property items available.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.6.0.
+ */
+extern SDL_DECLSPEC int SDLCALL SDL_GetNumProperties(SDL_PropertiesID props);
+
 /**
  * A callback used to enumerate all the properties in a group of properties.
  *
diff --git a/src/SDL_hashtable.c b/src/SDL_hashtable.c
index 8bc79f7e31eb1..91359231a07d7 100644
--- a/src/SDL_hashtable.c
+++ b/src/SDL_hashtable.c
@@ -409,18 +409,23 @@ bool SDL_IterateHashTable(const SDL_HashTable *table, SDL_HashTableIterateCallba
     return true;
 }
 
-bool SDL_HashTableEmpty(SDL_HashTable *table)
+int SDL_GetNumHashTableItems(SDL_HashTable *table)
 {
     CHECK_PARAM(!table) {
-        return SDL_InvalidParamError("table");
+        SDL_InvalidParamError("table");
+        return 0;
     }
 
     SDL_LockRWLockForReading(table->lock);
-    const bool retval = (table->num_occupied_slots == 0);
+    const int retval = (int) table->num_occupied_slots;
     SDL_UnlockRWLock(table->lock);
     return retval;
 }
 
+bool SDL_HashTableEmpty(SDL_HashTable *table)
+{
+    return SDL_GetNumHashTableItems(table) <= 0;
+}
 
 static void destroy_all(SDL_HashTable *table)
 {
diff --git a/src/SDL_hashtable.h b/src/SDL_hashtable.h
index e947efc22c8f2..a8a9f099e9bf9 100644
--- a/src/SDL_hashtable.h
+++ b/src/SDL_hashtable.h
@@ -393,12 +393,30 @@ extern bool SDL_RemoveFromHashTable(SDL_HashTable *table, const void *key);
  */
 extern void SDL_ClearHashTable(SDL_HashTable *table);
 
+/**
+ * Query the number of items currently in a hash table.
+ *
+ * Invalid tables report 0 items.
+ *
+ * \param table the hash table to check.
+ * \returns number of items in the table.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.6.0.
+ *
+ * \sa SDL_HashTableEmpty
+ */
+extern int SDL_GetNumHashTableItems(SDL_HashTable *table);
+
 /**
  * Check if any items are currently stored in a hash table.
  *
  * If there are no items stored (the table is completely empty), this will
  * return true.
  *
+ * Invalid tables report true.
+ *
  * \param table the hash table to check.
  * \returns true if the table is completely empty, false otherwise.
  *
diff --git a/src/SDL_properties.c b/src/SDL_properties.c
index f91db7de5a1c4..254d25c46a151 100644
--- a/src/SDL_properties.c
+++ b/src/SDL_properties.c
@@ -746,6 +746,25 @@ bool SDL_ClearProperty(SDL_PropertiesID props, const char *name)
     return SDL_PrivateSetProperty(props, name, NULL);
 }
 
+int SDL_GetNumProperties(SDL_PropertiesID props)
+{
+    if (!props) {
+        return 0;
+    }
+
+    SDL_Properties *properties = NULL;
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return 0;
+    }
+
+    SDL_LockMutex(properties->lock);
+    const int retval = SDL_GetNumHashTableItems(properties->props);
+    SDL_UnlockMutex(properties->lock);
+    return retval;
+}
+
+
 typedef struct EnumerateOnePropertyData
 {
     SDL_EnumeratePropertiesCallback callback;
diff --git a/src/dynapi/SDL_dynapi.exports b/src/dynapi/SDL_dynapi.exports
index 87e38965c9ea1..f0c685c4be7d5 100644
--- a/src/dynapi/SDL_dynapi.exports
+++ b/src/dynapi/SDL_dynapi.exports
@@ -1301,3 +1301,4 @@ _SDL_RemoveNotification
 _SDL_GetDeviceFormFactor
 _SDL_GetDeviceFormFactorName
 _SDL_IsUbuntuTouch
+_SDL_GetNumProperties
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 607222b2b7e6d..f30ea16eb5c00 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1302,6 +1302,7 @@ SDL3_0.0.0 {
     SDL_GetDeviceFormFactor;
     SDL_GetDeviceFormFactorName;
     SDL_IsUbuntuTouch;
+    SDL_GetNumProperties;
     # extra symbols go here (don't modify this line)
   local: *;
 };
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index b2d767829f2ab..c7ce948a96c86 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1328,3 +1328,4 @@
 #define SDL_GetDeviceFormFactor SDL_GetDeviceFormFactor_REAL
 #define SDL_GetDeviceFormFactorName SDL_GetDeviceFormFactorName_REAL
 #define SDL_IsUbuntuTouch SDL_IsUbuntuTouch_REAL
+#define SDL_GetNumProperties SDL_GetNumProperties_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 884cf2ae96b4f..299f77f2e89cd 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1336,3 +1336,4 @@ SDL_DYNAPI_PROC(bool,SDL_RemoveNotification,(SDL_NotificationID a),(a),return)
 SDL_DYNAPI_PROC(SDL_FormFactor,SDL_GetDeviceFormFactor,(void),(),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetDeviceFormFactorName,(SDL_FormFactor a),(a),return)
 SDL_DYNAPI_PROC(bool,SDL_IsUbuntuTouch,(void),(),return)
+SDL_DYNAPI_PROC(int,SDL_GetNumProperties,(SDL_PropertiesID a),(a),return)