SDL: Added SDL_srand(), SDL_rand(), and SDL_rand_r() (thanks @JKaniarz!)

From d1d484ddbef2ad3a5bb243388ef9e47fac021f50 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 16 Jun 2024 07:20:11 -0700
Subject: [PATCH] Added SDL_srand(), SDL_rand(), and SDL_rand_r() (thanks
 @JKaniarz!)

These are simple random functions that should not be used for serious random number generation.

Fixes https://github.com/libsdl-org/SDL/issues/4968
---
 VisualC-GDK/SDL/SDL.vcxproj             |  1 +
 VisualC-GDK/SDL/SDL.vcxproj.filters     |  1 +
 VisualC-WinRT/SDL-UWP.vcxproj           |  1 +
 VisualC-WinRT/SDL-UWP.vcxproj.filters   |  3 +
 VisualC/SDL/SDL.vcxproj                 |  1 +
 VisualC/SDL/SDL.vcxproj.filters         |  3 +
 Xcode/SDL/SDL.xcodeproj/project.pbxproj | 12 ++++
 include/SDL3/SDL_stdinc.h               | 46 ++++++++++++++
 src/dynapi/SDL_dynapi.sym               |  3 +
 src/dynapi/SDL_dynapi_overrides.h       |  3 +
 src/dynapi/SDL_dynapi_procs.h           |  3 +
 src/stdlib/SDL_random.c                 | 80 +++++++++++++++++++++++++
 test/checkkeysthreads.c                 | 18 ++----
 test/testdraw.c                         | 25 ++++----
 test/testffmpeg.c                       | 12 ++--
 test/testgeometry.c                     |  7 +--
 test/testintersections.c                | 25 ++++----
 test/testnative.c                       | 14 ++---
 test/testoffscreen.c                    |  5 --
 test/testpopup.c                        |  1 -
 test/testrelative.c                     |  4 --
 test/testspriteminimal.c                | 12 ++--
 test/testwaylandcustom.c                | 12 ++--
 23 files changed, 203 insertions(+), 89 deletions(-)
 create mode 100644 src/stdlib/SDL_random.c

diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index b40ca40e86f82..057cb1c331ae9 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -785,6 +785,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
     </MASM>
     <ClCompile Include="..\..\src\stdlib\SDL_qsort.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_random.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_string.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_strtokr.c" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index d0dde99d3fc35..9ac9cd48fe5e4 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -160,6 +160,7 @@
     <ClCompile Include="..\..\src\stdlib\SDL_memset.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_mslibc.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_qsort.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_random.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_string.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_strtokr.c" />
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj
index 9be0738d4c7f0..ac172fb96456a 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj
+++ b/VisualC-WinRT/SDL-UWP.vcxproj
@@ -442,6 +442,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
     </MASM>
     <ClCompile Include="..\src\stdlib\SDL_qsort.c" />
+    <ClCompile Include="..\src\stdlib\SDL_random.c" />
     <ClCompile Include="..\src\stdlib\SDL_stdlib.c" />
     <ClCompile Include="..\src\stdlib\SDL_string.c" />
     <ClCompile Include="..\src\stdlib\SDL_strtokr.c" />
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters
index 7069a9566fdf9..8447d95db2bc3 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj.filters
+++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters
@@ -771,6 +771,9 @@
     <ClCompile Include="..\src\stdlib\SDL_qsort.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\stdlib\SDL_random.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\stdlib\SDL_stdlib.c">
       <Filter>Source Files</Filter>
     </ClCompile>
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 95f782b5184ec..0164338ee5ba3 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -640,6 +640,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
     </MASM>
     <ClCompile Include="..\..\src\stdlib\SDL_qsort.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_random.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_string.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_strtokr.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index c38962d176b84..17838de16a1dd 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -1393,6 +1393,9 @@
     <ClCompile Include="..\..\src\stdlib\SDL_qsort.c">
       <Filter>stdlib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\stdlib\SDL_random.c">
+      <Filter>stdlib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\stdlib\SDL_stdlib.c">
       <Filter>stdlib</Filter>
     </ClCompile>
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index 2c115111755cc..40e6effe10725 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -383,6 +383,9 @@
 		E4F7981C2AD8D85500669F54 /* SDL_dynapi_unsupported.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F7981B2AD8D85500669F54 /* SDL_dynapi_unsupported.h */; };
 		E4F7981E2AD8D86A00669F54 /* SDL_render_unsupported.c in Sources */ = {isa = PBXBuildFile; fileRef = E4F7981D2AD8D86A00669F54 /* SDL_render_unsupported.c */; };
 		E4F798202AD8D87F00669F54 /* SDL_video_unsupported.c in Sources */ = {isa = PBXBuildFile; fileRef = E4F7981F2AD8D87F00669F54 /* SDL_video_unsupported.c */; };
+		F310138D2C1F2CB700FBE946 /* SDL_getenv_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F310138A2C1F2CB700FBE946 /* SDL_getenv_c.h */; };
+		F310138E2C1F2CB700FBE946 /* SDL_random.c in Sources */ = {isa = PBXBuildFile; fileRef = F310138B2C1F2CB700FBE946 /* SDL_random.c */; };
+		F310138F2C1F2CB700FBE946 /* SDL_sysstdlib.h in Headers */ = {isa = PBXBuildFile; fileRef = F310138C2C1F2CB700FBE946 /* SDL_sysstdlib.h */; };
 		F316ABD82B5C3185002EF551 /* SDL_memset.c in Sources */ = {isa = PBXBuildFile; fileRef = F316ABD62B5C3185002EF551 /* SDL_memset.c */; };
 		F316ABD92B5C3185002EF551 /* SDL_memcpy.c in Sources */ = {isa = PBXBuildFile; fileRef = F316ABD72B5C3185002EF551 /* SDL_memcpy.c */; };
 		F316ABDB2B5CA721002EF551 /* SDL_memmove.c in Sources */ = {isa = PBXBuildFile; fileRef = F316ABDA2B5CA721002EF551 /* SDL_memmove.c */; };
@@ -909,6 +912,9 @@
 		E4F7981B2AD8D85500669F54 /* SDL_dynapi_unsupported.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dynapi_unsupported.h; sourceTree = "<group>"; };
 		E4F7981D2AD8D86A00669F54 /* SDL_render_unsupported.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_unsupported.c; sourceTree = "<group>"; };
 		E4F7981F2AD8D87F00669F54 /* SDL_video_unsupported.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_video_unsupported.c; sourceTree = "<group>"; };
+		F310138A2C1F2CB700FBE946 /* SDL_getenv_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_getenv_c.h; sourceTree = "<group>"; };
+		F310138B2C1F2CB700FBE946 /* SDL_random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_random.c; sourceTree = "<group>"; };
+		F310138C2C1F2CB700FBE946 /* SDL_sysstdlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysstdlib.h; sourceTree = "<group>"; };
 		F316ABD62B5C3185002EF551 /* SDL_memset.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_memset.c; sourceTree = "<group>"; };
 		F316ABD72B5C3185002EF551 /* SDL_memcpy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_memcpy.c; sourceTree = "<group>"; };
 		F316ABDA2B5CA721002EF551 /* SDL_memmove.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_memmove.c; sourceTree = "<group>"; };
@@ -2035,6 +2041,7 @@
 			children = (
 				F3973FA128A59BDD00B84553 /* SDL_crc16.c */,
 				F395BF6425633B2400942BFF /* SDL_crc32.c */,
+				F310138A2C1F2CB700FBE946 /* SDL_getenv_c.h */,
 				A7D8A8D423E2514000DCD162 /* SDL_getenv.c */,
 				A7D8A8D323E2514000DCD162 /* SDL_iconv.c */,
 				A7D8A8D923E2514000DCD162 /* SDL_malloc.c */,
@@ -2042,9 +2049,11 @@
 				F316ABDA2B5CA721002EF551 /* SDL_memmove.c */,
 				F316ABD62B5C3185002EF551 /* SDL_memset.c */,
 				A7D8A8D723E2514000DCD162 /* SDL_qsort.c */,
+				F310138B2C1F2CB700FBE946 /* SDL_random.c */,
 				A7D8A8D823E2514000DCD162 /* SDL_stdlib.c */,
 				A7D8A8D523E2514000DCD162 /* SDL_string.c */,
 				A7D8A8D623E2514000DCD162 /* SDL_strtokr.c */,
+				F310138C2C1F2CB700FBE946 /* SDL_sysstdlib.h */,
 				F3973FA028A59BDD00B84553 /* SDL_vacopy.h */,
 			);
 			path = stdlib;
@@ -2291,6 +2300,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				F3F7D9812933074E00816151 /* SDL.h in Headers */,
+				F310138D2C1F2CB700FBE946 /* SDL_getenv_c.h in Headers */,
 				A7D8B39E23E2514200DCD162 /* SDL_RLEaccel_c.h in Headers */,
 				F3F7D9C52933074E00816151 /* SDL_assert.h in Headers */,
 				A7D8B61723E2514300DCD162 /* SDL_assert_c.h in Headers */,
@@ -2439,6 +2449,7 @@
 				A7D8B98C23E2514400DCD162 /* SDL_shaders_metal_ios.h in Headers */,
 				A7D8B99B23E2514400DCD162 /* SDL_shaders_metal_macos.h in Headers */,
 				A7D8B9A123E2514400DCD162 /* SDL_shaders_metal_tvos.h in Headers */,
+				F310138F2C1F2CB700FBE946 /* SDL_sysstdlib.h in Headers */,
 				F3F7D8F52933074E00816151 /* SDL_stdinc.h in Headers */,
 				F362B91B2B3349E200D30B94 /* SDL_steam_virtual_gamepad.h in Headers */,
 				A7D8BBC723E2561500DCD162 /* SDL_steamcontroller.h in Headers */,
@@ -2869,6 +2880,7 @@
 				0000A4DA2F45A31DC4F00000 /* SDL_sysmain_callbacks.m in Sources */,
 				000028F8113A53F4333E0000 /* SDL_main_callbacks.c in Sources */,
 				000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */,
+				F310138E2C1F2CB700FBE946 /* SDL_random.c in Sources */,
 				00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */,
 				00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */,
 				000080903BC03006F24E0000 /* SDL_filesystem.c in Sources */,
diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h
index 3d509cbd7d335..d7522a931bd3e 100644
--- a/include/SDL3/SDL_stdinc.h
+++ b/include/SDL3/SDL_stdinc.h
@@ -1259,6 +1259,52 @@ extern SDL_DECLSPEC int SDLCALL SDL_vswprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *tex
 extern SDL_DECLSPEC int SDLCALL SDL_asprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(2);
 extern SDL_DECLSPEC int SDLCALL SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(2);
 
+/**
+ * Seed the pseudo-random number generator
+ *
+ * \param seed the value to use as a random number seed, or 0 to use SDL_GetPerformanceCounter()
+ *
+ * \threadsafety This should be called on the same thread that calls SDL_rand()
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_rand
+ */
+extern SDL_DECLSPEC void SDLCALL SDL_srand(Uint64 seed);
+
+/**
+ * Get a pseudo-random number.
+ *
+ * There are no guarantees as to the quality of the random sequence produced, and this should not be used for cryptography or anything that requires good random distribution. There are many random number libraries available with different characteristics and you should pick one of those to meet any serious needs.
+ *
+ * \returns a random value in the range of [0-SDL_MAX_UINT32]
+ *
+ * \threadsafety All calls should be from from a single thread, use SDL_rand_r() when using multiple threads.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_rand_r
+ * \sa SDL_srand
+ */
+extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand(void);
+
+/**
+ * Get a pseudo-random number.
+ *
+ * There are no guarantees as to the quality of the random sequence produced, and this should not be used for cryptography or anything that requires good random distribution. There are many random number libraries available with different characteristics and you should pick one of those to meet any serious needs.
+ *
+ * \param state a pointer to a 64-bit seed value that will be updated with each call to SDL_rand_r(). If the value of the seed is 0, it will be initialized with SDL_GetPerformanceCounter().
+ * \returns a random value in the range of [0-SDL_MAX_UINT32], or 0 if state is NULL.
+ *
+ * \threadsafety This can be called from any thread, however each thread should pass its own state pointer.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_rand
+ */
+extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_r(Uint64 *state);
+
+
 #ifndef SDL_PI_D
 #define SDL_PI_D   3.141592653589793238462643383279502884       /**< pi (double) */
 #endif
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 20f91ddbc30eb..56191fc18b9a8 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -955,6 +955,8 @@ SDL3_0.0.0 {
     SDL_powf;
     SDL_qsort;
     SDL_qsort_r;
+    SDL_rand;
+    SDL_rand_r;
     SDL_realloc;
     SDL_round;
     SDL_roundf;
@@ -966,6 +968,7 @@ SDL3_0.0.0 {
     SDL_snprintf;
     SDL_sqrt;
     SDL_sqrtf;
+    SDL_srand;
     SDL_sscanf;
     SDL_strcasecmp;
     SDL_strcasestr;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 80809bf7b0b39..d79f758f6187e 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -980,6 +980,8 @@
 #define SDL_powf SDL_powf_REAL
 #define SDL_qsort SDL_qsort_REAL
 #define SDL_qsort_r SDL_qsort_r_REAL
+#define SDL_rand SDL_rand_REAL
+#define SDL_rand_r SDL_rand_r_REAL
 #define SDL_realloc SDL_realloc_REAL
 #define SDL_round SDL_round_REAL
 #define SDL_roundf SDL_roundf_REAL
@@ -991,6 +993,7 @@
 #define SDL_snprintf    SDL_snprintf_REAL
 #define SDL_sqrt SDL_sqrt_REAL
 #define SDL_sqrtf SDL_sqrtf_REAL
+#define SDL_srand SDL_srand_REAL
 #define SDL_sscanf  SDL_sscanf_REAL
 #define SDL_strcasecmp SDL_strcasecmp_REAL
 #define SDL_strcasestr SDL_strcasestr_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index f4187911e3ba0..8bdecdb6c6ac5 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -989,6 +989,8 @@ SDL_DYNAPI_PROC(double,SDL_pow,(double a, double b),(a,b),return)
 SDL_DYNAPI_PROC(float,SDL_powf,(float a, float b),(a,b),return)
 SDL_DYNAPI_PROC(void,SDL_qsort,(void *a, size_t b, size_t c, SDL_CompareCallback d),(a,b,c,d),)
 SDL_DYNAPI_PROC(void,SDL_qsort_r,(void *a, size_t b, size_t c, SDL_CompareCallback_r d, void *e),(a,b,c,d,e),)
+SDL_DYNAPI_PROC(Uint32,SDL_rand,(void),(),return)
+SDL_DYNAPI_PROC(Uint32,SDL_rand_r,(Uint64 *a),(a),return)
 SDL_DYNAPI_PROC(void*,SDL_realloc,(void *a, size_t b),(a,b),return)
 SDL_DYNAPI_PROC(double,SDL_round,(double a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_roundf,(float a),(a),return)
@@ -999,6 +1001,7 @@ SDL_DYNAPI_PROC(double,SDL_sin,(double a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_sinf,(float a),(a),return)
 SDL_DYNAPI_PROC(double,SDL_sqrt,(double a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_sqrtf,(float a),(a),return)
+SDL_DYNAPI_PROC(void,SDL_srand,(Uint64 a),(a),)
 SDL_DYNAPI_PROC(int,SDL_strcasecmp,(const char *a, const char *b),(a,b),return)
 SDL_DYNAPI_PROC(char*,SDL_strcasestr,(const char *a, const char *b),(a,b),return)
 SDL_DYNAPI_PROC(char*,SDL_strchr,(const char *a, int b),(a,b),return)
diff --git a/src/stdlib/SDL_random.c b/src/stdlib/SDL_random.c
new file mode 100644
index 0000000000000..0102a7a088298
--- /dev/null
+++ b/src/stdlib/SDL_random.c
@@ -0,0 +1,80 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+/* This file contains portable random functions for SDL */
+
+static Uint64 SDL_rand_state;
+
+void SDL_srand(Uint64 seed)
+{
+    SDL_rand_state = seed;
+}
+
+Uint32 SDL_rand(void)
+{
+	return SDL_rand_r(&SDL_rand_state);
+}
+
+/*
+ * Return a number between [0, n)
+ * Fast but slightly biased. Don't run your casino with this.
+ */
+Sint32 SDL_rand_n(Sint32 n)
+{
+	// On 32-bit arch, the compiler will optimize to a single 32-bit multiply
+	Uint64 val = (Uint64)SDL_rand() * n;
+	return (Sint32)(val >> 32);
+}
+
+/*
+ * Random float in range [0,1)
+ */
+float SDL_rand_float(void)
+{
+	return (SDL_rand() >> (32-24)) * 0x1p-24f;
+}
+
+/* A fast psuedo-random number generator.
+ * Not suitable for cryptography or gambling
+ */
+Uint32 SDL_rand_r(Uint64 *state)
+{
+    if (!state) {
+        return 0;
+    }
+
+    if (!*state) {
+        *state = SDL_GetPerformanceCounter();
+    }
+
+	// Multiplier from Table 6 of
+	// Steele GL, Vigna S. Computationally easy, spectrally good multipliers
+	// for congruential pseudorandom number generators.
+	// Softw Pract Exper. 2022;52(2):443-458. doi: 10.1002/spe.3030
+
+	// 32-bit 'a' improves performance on 32-bit architectures
+	// 'c' can be any odd number, but < 256 generates smaller code on some arch
+	*state = *state * 0xf9b25d65ul + 0xFD;
+
+	// Only return top 32 bits because they have a longer period
+	return (Uint32)(*state >> 32);
+}
diff --git a/test/checkkeysthreads.c b/test/checkkeysthreads.c
index 6630f17c02468..e7f26238fb388 100644
--- a/test/checkkeysthreads.c
+++ b/test/checkkeysthreads.c
@@ -23,7 +23,6 @@
 #include <emscripten/emscripten.h>
 #endif
 
-#include <stdio.h>
 #include <stdlib.h>
 
 static int done;
@@ -172,8 +171,7 @@ static void loop(void)
     /* Check for events */
     /*SDL_WaitEvent(&event); emscripten does not like waiting*/
 
-    (void)fprintf(stderr, "starting loop\n");
-    (void)fflush(stderr);
+    SDL_Log("starting loop\n");
     while (!done && SDL_WaitEvent(&event)) {
         SDL_Log("Got event type: %" SDL_PRIu32 "\n", event.type);
         switch (event.type) {
@@ -189,8 +187,7 @@ static void loop(void)
             break;
         case SDL_EVENT_MOUSE_BUTTON_DOWN:
             /* Left button quits the app, other buttons toggles text input */
-            (void)fprintf(stderr, "mouse button down button: %d (LEFT=%d)\n", event.button.button, SDL_BUTTON_LEFT);
-            (void)fflush(stderr);
+            SDL_Log("mouse button down button: %d (LEFT=%d)\n", event.button.button, SDL_BUTTON_LEFT);
             if (event.button.button == SDL_BUTTON_LEFT) {
                 done = 1;
             } else {
@@ -209,11 +206,9 @@ static void loop(void)
         default:
             break;
         }
-        (void)fprintf(stderr, "waiting new event\n");
-        (void)fflush(stderr);
+       SDL_Log("waiting new event\n");
     }
-    (void)fprintf(stderr, "exiting event loop\n");
-    (void)fflush(stderr);
+    SDL_Log("exiting event loop\n");
 #ifdef SDL_PLATFORM_EMSCRIPTEN
     if (done) {
         emscripten_cancel_main_loop();
@@ -228,12 +223,11 @@ static int SDLCALL ping_thread(void *ptr)
     SDL_Event sdlevent;
     SDL_memset(&sdlevent, 0, sizeof(SDL_Event));
     for (cnt = 0; cnt < 10; ++cnt) {
-        (void)fprintf(stderr, "sending event (%d/%d) from thread.\n", cnt + 1, 10);
-        (void)fflush(stderr);
+        SDL_Log("sending event (%d/%d) from thread.\n", cnt + 1, 10);
         sdlevent.type = SDL_EVENT_KEY_DOWN;
         sdlevent.key.keysym.sym = SDLK_1;
         SDL_PushEvent(&sdlevent);
-        SDL_Delay(1000 + rand() % 1000);
+        SDL_Delay(1000 + SDL_rand() % 1000);
     }
     return cnt;
 }
diff --git a/test/testdraw.c b/test/testdraw.c
index 1458937584140..f0c750b77b21f 100644
--- a/test/testdraw.c
+++ b/test/testdraw.c
@@ -20,9 +20,6 @@
 #include <emscripten/emscripten.h>
 #endif
 
-#include <stdlib.h>
-#include <time.h>
-
 #define NUM_OBJECTS 100
 
 static SDLTest_CommonState *state;
@@ -75,8 +72,8 @@ static void DrawPoints(SDL_Renderer *renderer)
         SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color,
                                (Uint8)current_color, (Uint8)current_alpha);
 
-        x = (float)(rand() % viewport.w);
-        y = (float)(rand() % viewport.h);
+        x = (float)(SDL_rand() % viewport.w);
+        y = (float)(SDL_rand() % viewport.h);
         SDL_RenderPoint(renderer, x, y);
     }
 }
@@ -123,10 +120,10 @@ static void DrawLines(SDL_Renderer *renderer)
             SDL_RenderLine(renderer, 0.0f, (float)(viewport.h / 2), (float)(viewport.w - 1), (float)(viewport.h / 2));
             SDL_RenderLine(renderer, (float)(viewport.w / 2), 0.0f, (float)(viewport.w / 2), (float)(viewport.h - 1));
         } else {
-            x1 = (float)((rand() % (viewport.w * 2)) - viewport.w);
-            x2 = (float)((rand() % (viewport.w * 2)) - viewport.w);
-            y1 = (float)((rand() % (viewport.h * 2)) - viewport.h);
-            y2 = (float)((rand() % (viewport.h * 2)) - viewport.h);
+            x1 = (float)((SDL_rand() % (viewport.w * 2)) - viewport.w);
+            x2 = (float)((SDL_rand() % (viewport.w * 2)) - viewport.w);
+            y1 = (float)((SDL_rand() % (viewport.h * 2)) - viewport.h);
+            y2 = (float)((SDL_rand() % (viewport.h * 2)) - viewport.h);
             SDL_RenderLine(renderer, x1, y1, x2, y2);
         }
     }
@@ -168,10 +165,10 @@ static void DrawRects(SDL_Renderer *renderer)
         SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color,
                                (Uint8)current_color, (Uint8)current_alpha);
 
-        rect.w = (float)(rand() % (viewport.h / 2));
-        rect.h = (float)(rand() % (viewport.h / 2));
-        rect.x = (float)((rand() % (viewport.w * 2) - viewport.w) - (rect.w / 2));
-        rect.y = (float)((rand() % (viewport.h * 2) - viewport.h) - (rect.h / 2));
+        rect.w = (float)(SDL_rand() % (viewport.h / 2));
+        rect.h = (float)(SDL_rand() % (viewport.h / 2));
+        rect.x = (float)((SDL_rand() % (viewport.w * 2) - viewport.w) - (rect.w / 2));
+        rect.y = (float)((SDL_rand() % (viewport.h * 2) - viewport.h) - (rect.h / 2));
         SDL_RenderFillRect(renderer, &rect);
     }
 }
@@ -293,8 +290,6 @@ int main(int argc, char *argv[])
         SDL_RenderClear(renderer);
     }
 
-    srand((unsigned int)time(NULL));
-
     /* Main render loop */
     frames = 0;
     next_fps_check = SDL_GetTicks() + fps_check_delay;
diff --git a/test/testffmpeg.c b/test/testffmpeg.c
index fbfb117d120a7..fd5187f5f7d6f 100644
--- a/test/testffmpeg.c
+++ b/test/testffmpeg.c
@@ -14,9 +14,6 @@
  * For a more complete video example, see ffplay.c in the ffmpeg sources.
  */
 
-#include <stdlib.h>
-#include <time.h>
-
 #include <SDL3/SDL.h>
 #include <SDL3/SDL_main.h>
 #include <SDL3/SDL_test.h>
@@ -1486,17 +1483,16 @@ int main(int argc, char *argv[])
     /* Position sprites and set their velocities */
     SDL_Rect viewport;
     SDL_GetRenderViewport(renderer, &viewport);
-    srand((unsigned int)time(NULL));
     for (i = 0; i < num_sprites; ++i) {
-        positions[i].x = (float)(rand() % (viewport.w - sprite_w));
-        positions[i].y = (float)(rand() % (viewport.h - sprite_h));
+        positions[i].x = (float)(SDL_rand() % (viewport.w - sprite_w));
+        positions[i].y = (float)(SDL_rand() % (viewport.h - sprite_h));
         positions[i].w = (float)sprite_w;
         positions[i].h = (float)sprite_h;
         velocities[i].x = 0.0f;
         velocities[i].y = 0.0f;
         while (velocities[i].x == 0.f || velocities[i].y == 0.f) {
-            velocities[i].x = (float)((rand() % (2 + 1)) - 1);
-            velocities[i].y = (float)((rand() % (2 + 1)) - 1);
+            velocities[i].x = (float)((SDL_rand() % (2 + 1)) - 1);
+            velocities[i].y = (float)((SDL_rand() % (2 + 1)) - 1);
         }
     }
 
diff --git a/test/testgeometry.c b/test/testgeometry.c
index 1d9ad1822dee6..231f1e0045c73 100644
--- a/test/testgeometry.c
+++ b/test/testgeometry.c
@@ -12,6 +12,8 @@
 
 /* Simple program:  draw a RGB triangle, with texture  */
 
+#include <stdlib.h>
+
 #include "testutils.h"
 #include <SDL3/SDL.h>
 #include <SDL3/SDL_main.h>
@@ -21,9 +23,6 @@
 #include <emscripten/emscripten.h>
 #endif
 
-#include <stdlib.h>
-#include <time.h>
-
 static SDLTest_CommonState *state;
 static SDL_bool use_texture = SDL_FALSE;
 static SDL_Texture **sprites;
@@ -260,8 +259,6 @@ int main(int argc, char *argv[])
         }
     }
 
-    srand((unsigned int)time(NULL));
-
     /* Main render loop */
     frames = 0;
     then = SDL_GetTicks();
diff --git a/test/testintersections.c b/test/testintersections.c
index bb5e69d55a8bd..50ec081eeebf1 100644
--- a/test/testintersections.c
+++ b/test/testintersections.c
@@ -19,9 +19,6 @@
 #include <emscripten/emscripten.h>
 #endif
 
-#include <stdlib.h>
-#include <time.h>
-
 #define SWAP(typ, a, b) \
     do {                \
         typ t = a;      \
@@ -77,8 +74,8 @@ static void DrawPoints(SDL_Renderer *renderer)
         SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color,
                                (Uint8)current_color, (Uint8)current_alpha);
 
-        x = (float)(rand() % viewport.w);
-        y = (float)(rand() % viewport.h);
+        x = (float)(SDL_rand() % viewport.w);
+        y = (float)(SDL_rand() % viewport.h);
         SDL_RenderPoint(renderer, x, y);
     }
 }
@@ -234,10 +231,10 @@ static void loop(void *arg)
                     num_lines = 0;
                 } else {
                     add_line(
-                        (float)(rand() % 640),
-                        (float)(rand() % 480),
-                        (float)(rand() % 640),
-                        (float)(rand() % 480));
+                        (float)(SDL_rand() % 640),
+                        (float)(SDL_rand() % 480),
+                        (float)(SDL_rand() % 640),
+                        (float)(SDL_rand() % 480));
                 }
                 break;
             case 'r':
@@ -245,10 +242,10 @@ static void loop(void *arg)
                     num_rects = 0;
                 } else {
                     add_rect(
-                        (float)(rand() % 640),
-                        (float)(rand() % 480),
-                        (float)(rand() % 640),
-                        (float)(rand() % 480));
+                        (float)(SDL_rand() % 640),
+                        (float)(SDL_rand() % 480),
+                        (float)(SDL_rand() % 640),
+                        (float)(SDL_rand() % 480));
                 }
                 break;
             default:
@@ -363,8 +360,6 @@ int main(int argc, char *argv[])
         SDL_RenderClear(renderer);
     }
 
-    srand((unsigned int)time(NULL));
-
     /* Main render loop */
     frames = 0;
     then = SDL_GetTicks();
diff --git a/test/testnative.c b/test/testnative.c
index 9a40764b4d228..c85a1bf7726dd 100644
--- a/test/testnative.c
+++ b/test/testnative.c
@@ -11,6 +11,8 @@
 */
 /* Simple program:  Create a native window and attach an SDL renderer */
 
+#include <stdlib.h>
+
 #include <SDL3/SDL.h>
 #include <SDL3/SDL_main.h>
 #include <SDL3/SDL_test.h>
@@ -18,9 +20,6 @@
 #include "testnative.h"
 #include "testutils.h"
 
-#include <stdlib.h> /* for srand() */
-#include <time.h>   /* for time() */
-
 #define WINDOW_W    640
 #define WINDOW_H    480
 #define NUM_SPRITES 100
@@ -188,17 +187,16 @@ int main(int argc, char *argv[])
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!\n");
         quit(2);
     }
-    srand((unsigned int)time(NULL));
     for (i = 0; i < NUM_SPRITES; ++i) {
-        positions[i].x = (float)(rand() % (window_w - (int)sprite_w));
-        positions[i].y = (float)(rand() % (window_h - (int)sprite_h));
+        positions[i].x = (float)(SDL_rand() % (window_w - (int)sprite_w));
+        positions[i].y = (float)(SDL_rand() % (window_h - (int)sprite_h));
         positions[i].w = sprite_w;
         positions[i].h = sprite_h;
         velocities[i].x = 0.0f;
         velocities[i].y = 0.0f;
         while (velocities[i].x == 0.f && velocities[i].y == 0.f) {
-            velocities[i].x = (float)((rand() % (MAX_SPEED * 2 + 1)) - MAX_SPEED);
-            velocities[i].y = (float)((rand() % (MAX_SPEED * 2 + 1)) - MAX_SPEED);
+            velocities[i].x = (float)((SDL_rand() % (MAX_SPEED * 2 + 1)) - MAX_SPEED);
+            velocities[i].y = (float)((SDL_rand() % (MAX_SPEED * 2 + 1)) - MAX_SPEED);
         }
     }
 
diff --git a/test/testoffscreen.c b/test/testoffscreen.c
index f2975ea9acbad..613fa1fab9be1 100644
--- a/test/testoffscreen.c
+++ b/test/testoffscreen.c
@@ -21,9 +21,6 @@
 #include <emscripten/emscripten.h>
 #endif
 
-#include <stdlib.h>
-#include <time.h>
-
 static SDL_Renderer *renderer = NULL;
 static SDL_Window *window = NULL;
 static int done = SDL_FALSE;
@@ -137,8 +134,6 @@ int main(int argc, char *argv[])
 
     SDL_RenderClear(renderer);
 
-    srand((unsigned int)time(NULL));
-
 #ifndef SDL_PLATFORM_EMSCRIPTEN
     /* Main render loop */
     frames = 0;
diff --git a/test/testpopup.c b/test/testpopup.c
index 44e785b3336eb..ec136284faccc 100644
--- a/test/testpopup.c
+++ b/test/testpopup.c
@@ -20,7 +20,6 @@ freely.
 #endif
 
 #include <stdlib.h>
-#include <time.h>
 
 #define MENU_WIDTH  120
 #define MENU_HEIGHT 300
diff --git a/test/testrelative.c b/test/testrelative.c
index ec91e31102e67..349ba5b426574 100644
--- a/test/testrelative.c
+++ b/test/testrelative.c
@@ -19,9 +19,6 @@
 #include <emscripten/emscripten.h>
 #endif
 
-#include <stdlib.h>
-#include <time.h>
-
 static SDLTest_CommonState *state;
 static int i, done;
 static float mouseX, mouseY;
@@ -115,7 +112,6 @@ int main(int argc, char *argv[])
         SDL_RenderClear(renderer);
     }
 
-    srand((unsigned int)time(NULL));
     if (SDL_SetRelativeMouseMode(SDL_TRUE) < 0) {
         return 3;
     }
diff --git a/test/testspriteminimal.c b/test/testspriteminimal.c
index 67c5c052874a3..078d17657468e 100644
--- a/test/testspriteminimal.c
+++ b/test/testspriteminimal.c
@@ -18,9 +18,6 @@
 #include <emscripten/emscripten.h>
 #endif
 
-#include <stdlib.h>
-#include <time.h>
-
 #include "icon.h"
 
 #define WINDOW_WIDTH  640
@@ -136,17 +133,16 @@ int main(int argc, char *argv[])
     }
 
     /* Initialize the sprite positions */
-    srand((unsigned int)time(NULL));
     for (i = 0; i < NUM_SPRITES; ++i) {
-        positions[i].x = (float)(rand() % (WINDOW_WIDTH - sprite_w));
-        positions[i].y = (float)(rand() % (WINDOW_HEIGHT - sprite_h));
+        positions[i].x = (float)(SDL_rand() % (WINDOW_WIDTH - sprite_w));
+        positions[i].y = (float)(SDL_rand() % (WINDOW_HEIGHT - sprite_h));
         posit

(Patch may be truncated, please check the link at the top of this post.)