SDL: Property query functions don't set an error if they return the default value

From 8ce786d2b6c372dfca5374de20d15aae41b7ceb8 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 17 Feb 2024 07:59:04 -0800
Subject: [PATCH] Property query functions don't set an error if they return
 the default value

You can call SDL_HasProperty() if you want to check to see if a property exists.

Fixes https://github.com/libsdl-org/SDL/issues/9067
---
 include/SDL3/SDL_properties.h     | 24 ++++++++++++++++--
 src/SDL_properties.c              | 41 ++++---------------------------
 src/dynapi/SDL_dynapi.sym         |  1 +
 src/dynapi/SDL_dynapi_overrides.h |  1 +
 src/dynapi/SDL_dynapi_procs.h     |  1 +
 5 files changed, 30 insertions(+), 38 deletions(-)

diff --git a/include/SDL3/SDL_properties.h b/include/SDL3/SDL_properties.h
index 80a01cc123e0..8061972373c3 100644
--- a/include/SDL3/SDL_properties.h
+++ b/include/SDL3/SDL_properties.h
@@ -248,6 +248,21 @@ extern DECLSPEC int SDLCALL SDL_SetFloatProperty(SDL_PropertiesID props, const c
  */
 extern DECLSPEC int SDLCALL SDL_SetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bool value);
 
+/**
+ * Return whether a property exists in a set of properties.
+ *
+ * \param props the properties to query
+ * \param name the name of the property to query
+ * \returns SDL_TRUE if the property exists, or SDL_FALSE if it doesn't.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetPropertyType
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasProperty(SDL_PropertiesID props, const char *name);
+
 /**
  * Get the type of a property on a set of properties
  *
@@ -259,6 +274,8 @@ extern DECLSPEC int SDLCALL SDL_SetBooleanProperty(SDL_PropertiesID props, const
  * \threadsafety It is safe to call this function from any thread.
  *
  * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_HasProperty
  */
 extern DECLSPEC SDL_PropertyType SDLCALL SDL_GetPropertyType(SDL_PropertiesID props, const char *name);
 
@@ -285,6 +302,7 @@ extern DECLSPEC SDL_PropertyType SDLCALL SDL_GetPropertyType(SDL_PropertiesID pr
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_GetPropertyType
+ * \sa SDL_HasProperty
  * \sa SDL_SetProperty
  */
 extern DECLSPEC void *SDLCALL SDL_GetProperty(SDL_PropertiesID props, const char *name, void *default_value);
@@ -303,6 +321,7 @@ extern DECLSPEC void *SDLCALL SDL_GetProperty(SDL_PropertiesID props, const char
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_GetPropertyType
+ * \sa SDL_HasProperty
  * \sa SDL_SetStringProperty
  */
 extern DECLSPEC const char *SDLCALL SDL_GetStringProperty(SDL_PropertiesID props, const char *name, const char *default_value);
@@ -324,6 +343,7 @@ extern DECLSPEC const char *SDLCALL SDL_GetStringProperty(SDL_PropertiesID props
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_GetPropertyType
+ * \sa SDL_HasProperty
  * \sa SDL_SetNumberProperty
  */
 extern DECLSPEC Sint64 SDLCALL SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 default_value);
@@ -345,6 +365,7 @@ extern DECLSPEC Sint64 SDLCALL SDL_GetNumberProperty(SDL_PropertiesID props, con
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_GetPropertyType
+ * \sa SDL_HasProperty
  * \sa SDL_SetFloatProperty
  */
 extern DECLSPEC float SDLCALL SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float default_value);
@@ -366,6 +387,7 @@ extern DECLSPEC float SDLCALL SDL_GetFloatProperty(SDL_PropertiesID props, const
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_GetPropertyType
+ * \sa SDL_HasProperty
  * \sa SDL_SetBooleanProperty
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bool default_value);
@@ -381,8 +403,6 @@ extern DECLSPEC SDL_bool SDLCALL SDL_GetBooleanProperty(SDL_PropertiesID props,
  * \threadsafety It is safe to call this function from any thread.
  *
  * \since This function is available since SDL 3.0.0.
- *
- * \sa SDL_GetProperty
  */
 extern DECLSPEC int SDLCALL SDL_ClearProperty(SDL_PropertiesID props, const char *name);
 
diff --git a/src/SDL_properties.c b/src/SDL_properties.c
index e25d6f2d8dff..8ee6b00ed6a2 100644
--- a/src/SDL_properties.c
+++ b/src/SDL_properties.c
@@ -451,17 +451,20 @@ int SDL_SetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bool va
     return SDL_PrivateSetProperty(props, name, property);
 }
 
+SDL_bool SDL_HasProperty(SDL_PropertiesID props, const char *name)
+{
+    return (SDL_GetPropertyType(props, name) != SDL_PROPERTY_TYPE_INVALID);
+}
+
 SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name)
 {
     SDL_Properties *properties = NULL;
     SDL_PropertyType type = SDL_PROPERTY_TYPE_INVALID;
 
     if (!props) {
-        SDL_InvalidParamError("props");
         return SDL_PROPERTY_TYPE_INVALID;
     }
     if (!name || !*name) {
-        SDL_InvalidParamError("name");
         return SDL_PROPERTY_TYPE_INVALID;
     }
 
@@ -470,7 +473,6 @@ SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name)
     SDL_UnlockMutex(SDL_properties_lock);
 
     if (!properties) {
-        SDL_InvalidParamError("props");
         return SDL_PROPERTY_TYPE_INVALID;
     }
 
@@ -479,8 +481,6 @@ SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name)
         SDL_Property *property = NULL;
         if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
             type = property->type;
-        } else {
-            SDL_SetError("Couldn't find property named %s", name);
         }
     }
     SDL_UnlockMutex(properties->lock);
@@ -494,11 +494,9 @@ void *SDL_GetProperty(SDL_PropertiesID props, const char *name, void *default_va
     void *value = default_value;
 
     if (!props) {
-        SDL_InvalidParamError("props");
         return value;
     }
     if (!name || !*name) {
-        SDL_InvalidParamError("name");
         return value;
     }
 
@@ -507,7 +505,6 @@ void *SDL_GetProperty(SDL_PropertiesID props, const char *name, void *default_va
     SDL_UnlockMutex(SDL_properties_lock);
 
     if (!properties) {
-        SDL_InvalidParamError("props");
         return value;
     }
 
@@ -521,11 +518,7 @@ void *SDL_GetProperty(SDL_PropertiesID props, const char *name, void *default_va
         if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
             if (property->type == SDL_PROPERTY_TYPE_POINTER) {
                 value = property->value.pointer_value;
-            } else {
-                SDL_SetError("Property %s isn't a pointer value", name);
             }
-        } else {
-            SDL_SetError("Couldn't find property named %s", name);
         }
     }
     SDL_UnlockMutex(properties->lock);
@@ -539,11 +532,9 @@ const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, cons
     const char *value = default_value;
 
     if (!props) {
-        SDL_InvalidParamError("props");
         return value;
     }
     if (!name || !*name) {
-        SDL_InvalidParamError("name");
         return value;
     }
 
@@ -552,7 +543,6 @@ const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, cons
     SDL_UnlockMutex(SDL_properties_lock);
 
     if (!properties) {
-        SDL_InvalidParamError("props");
         return value;
     }
 
@@ -594,11 +584,8 @@ const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, cons
                 value = property->value.boolean_value ? "true" : "false";
                 break;
             default:
-                SDL_SetError("Property %s isn't a string value", name);
                 break;
             }
-        } else {
-            SDL_SetError("Couldn't find property named %s", name);
         }
     }
     SDL_UnlockMutex(properties->lock);
@@ -612,11 +599,9 @@ Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 de
     Sint64 value = default_value;
 
     if (!props) {
-        SDL_InvalidParamError("props");
         return value;
     }
     if (!name || !*name) {
-        SDL_InvalidParamError("name");
         return value;
     }
 
@@ -625,7 +610,6 @@ Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 de
     SDL_UnlockMutex(SDL_properties_lock);
 
     if (!properties) {
-        SDL_InvalidParamError("props");
         return value;
     }
 
@@ -647,11 +631,8 @@ Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 de
                 value = property->value.boolean_value;
                 break;
             default:
-                SDL_SetError("Property %s isn't a number value", name);
                 break;
             }
-        } else {
-            SDL_SetError("Couldn't find property named %s", name);
         }
     }
     SDL_UnlockMutex(properties->lock);
@@ -665,11 +646,9 @@ float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float defau
     float value = default_value;
 
     if (!props) {
-        SDL_InvalidParamError("props");
         return value;
     }
     if (!name || !*name) {
-        SDL_InvalidParamError("name");
         return value;
     }
 
@@ -678,7 +657,6 @@ float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float defau
     SDL_UnlockMutex(SDL_properties_lock);
 
     if (!properties) {
-        SDL_InvalidParamError("props");
         return value;
     }
 
@@ -700,11 +678,8 @@ float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float defau
                 value = (float)property->value.boolean_value;
                 break;
             default:
-                SDL_SetError("Property %s isn't a float value", name);
                 break;
             }
-        } else {
-            SDL_SetError("Couldn't find property named %s", name);
         }
     }
     SDL_UnlockMutex(properties->lock);
@@ -718,11 +693,9 @@ SDL_bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bo
     SDL_bool value = default_value;
 
     if (!props) {
-        SDL_InvalidParamError("props");
         return value;
     }
     if (!name || !*name) {
-        SDL_InvalidParamError("name");
         return value;
     }
 
@@ -731,7 +704,6 @@ SDL_bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bo
     SDL_UnlockMutex(SDL_properties_lock);
 
     if (!properties) {
-        SDL_InvalidParamError("props");
         return value;
     }
 
@@ -753,11 +725,8 @@ SDL_bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bo
                 value = property->value.boolean_value;
                 break;
             default:
-                SDL_SetError("Property %s isn't a boolean value", name);
                 break;
             }
-        } else {
-            SDL_SetError("Couldn't find property named %s", name);
         }
     }
     SDL_UnlockMutex(properties->lock);
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 2ba1f7be6a87..f545e9b3c909 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -972,6 +972,7 @@ SDL3_0.0.0 {
     SDL_RenderGeometryRawFloat;
     SDL_SetWindowShape;
     SDL_RenderViewportSet;
+    SDL_HasProperty;
     # 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 b297a3026ecf..b288bdcfa36e 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -997,3 +997,4 @@
 #define SDL_RenderGeometryRawFloat SDL_RenderGeometryRawFloat_REAL
 #define SDL_SetWindowShape SDL_SetWindowShape_REAL
 #define SDL_RenderViewportSet SDL_RenderViewportSet_REAL
+#define SDL_HasProperty SDL_HasProperty_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 3969fda9c95f..7ce6a43d8cb8 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1022,3 +1022,4 @@ SDL_DYNAPI_PROC(int,SDL_GetRenderColorScale,(SDL_Renderer *a, float *b),(a,b),re
 SDL_DYNAPI_PROC(int,SDL_RenderGeometryRawFloat,(SDL_Renderer *a, SDL_Texture *b, const float *c, int d, const SDL_FColor *e, int f, const float *g, int h, int i, const void *j, int k, int l),(a,b,c,d,e,f,g,h,i,j,k,l),return)
 SDL_DYNAPI_PROC(int,SDL_SetWindowShape,(SDL_Window *a, SDL_Surface *b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_RenderViewportSet,(SDL_Renderer *a),(a),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_HasProperty,(SDL_PropertiesID a, const char *b),(a,b),return)