SDL: windows: Add WIN_IsWindows11OrGreater, using a dwBuildNumber helper function

From 702a1adf4ac353920456ff1a7ba3c8e3b72911c2 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Wed, 12 Nov 2025 15:58:57 -0500
Subject: [PATCH] windows: Add WIN_IsWindows11OrGreater, using a dwBuildNumber
 helper function

---
 src/core/windows/SDL_windows.c | 50 ++++++++++++++++++++++------------
 src/core/windows/SDL_windows.h |  3 ++
 2 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c
index 7c433b252c788..4621531ca545f 100644
--- a/src/core/windows/SDL_windows.c
+++ b/src/core/windows/SDL_windows.c
@@ -284,6 +284,31 @@ static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WO
 
     return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
 }
+
+static DWORD WIN_BuildNumber = 0;
+static BOOL IsWindowsBuildVersionAtLeast(DWORD dwBuildNumber)
+{
+    if (WIN_BuildNumber != 0) {
+        return (WIN_BuildNumber >= dwBuildNumber);
+    }
+
+    HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll"));
+    if (!ntdll) {
+        return false;
+    }
+    // There is no function to get Windows build number, so let's get it here via RtlGetVersion
+    RtlGetVersion_t RtlGetVersionFunc = (RtlGetVersion_t)GetProcAddress(ntdll, "RtlGetVersion");
+    NT_OSVERSIONINFOW os_info;
+    os_info.dwOSVersionInfoSize = sizeof(NT_OSVERSIONINFOW);
+    os_info.dwBuildNumber = 0;
+    if (RtlGetVersionFunc) {
+        RtlGetVersionFunc(&os_info);
+    }
+    FreeLibrary(ntdll);
+
+    WIN_BuildNumber = (os_info.dwBuildNumber & ~0xF0000000);
+    return (WIN_BuildNumber >= dwBuildNumber);
+}
 #endif
 
 // apply some static variables so we only call into the Win32 API once per process for each check.
@@ -324,6 +349,11 @@ BOOL WIN_IsWindows8OrGreater(void)
     CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0));
 }
 
+BOOL WIN_IsWindows11OrGreater(void)
+{
+    return IsWindowsBuildVersionAtLeast(22000);
+}
+
 #undef CHECKWINVER
 
 
@@ -441,21 +471,7 @@ bool WIN_WindowRectValid(const RECT *rect)
 void WIN_UpdateDarkModeForHWND(HWND hwnd)
 {
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
-    HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll"));
-    if (!ntdll) {
-        return;
-    }
-    // There is no function to get Windows build number, so let's get it here via RtlGetVersion
-    RtlGetVersion_t RtlGetVersionFunc = (RtlGetVersion_t)GetProcAddress(ntdll, "RtlGetVersion");
-    NT_OSVERSIONINFOW os_info;
-    os_info.dwOSVersionInfoSize = sizeof(NT_OSVERSIONINFOW);
-    os_info.dwBuildNumber = 0;
-    if (RtlGetVersionFunc) {
-        RtlGetVersionFunc(&os_info);
-    }
-    FreeLibrary(ntdll);
-    os_info.dwBuildNumber &= ~0xF0000000;
-    if (os_info.dwBuildNumber < 17763) {
+    if (!IsWindowsBuildVersionAtLeast(17763)) {
         // Too old to support dark mode
         return;
     }
@@ -466,7 +482,7 @@ void WIN_UpdateDarkModeForHWND(HWND hwnd)
     RefreshImmersiveColorPolicyState_t RefreshImmersiveColorPolicyStateFunc = (RefreshImmersiveColorPolicyState_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(104));
     ShouldAppsUseDarkMode_t ShouldAppsUseDarkModeFunc = (ShouldAppsUseDarkMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(132));
     AllowDarkModeForWindow_t AllowDarkModeForWindowFunc = (AllowDarkModeForWindow_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(133));
-    if (os_info.dwBuildNumber < 18362) {
+    if (!IsWindowsBuildVersionAtLeast(18362)) {
         AllowDarkModeForApp_t AllowDarkModeForAppFunc = (AllowDarkModeForApp_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135));
         if (AllowDarkModeForAppFunc) {
             AllowDarkModeForAppFunc(true);
@@ -491,7 +507,7 @@ void WIN_UpdateDarkModeForHWND(HWND hwnd)
         value = (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK) ? TRUE : FALSE;
     }
     FreeLibrary(uxtheme);
-    if (os_info.dwBuildNumber < 18362) {
+    if (!IsWindowsBuildVersionAtLeast(18362)) {
         SetProp(hwnd, TEXT("UseImmersiveDarkModeColors"), SDL_reinterpret_cast(HANDLE, SDL_static_cast(INT_PTR, value)));
     } else {
         HMODULE user32 = GetModuleHandle(TEXT("user32.dll"));
diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h
index 80eb0997e5374..f26b653b92d61 100644
--- a/src/core/windows/SDL_windows.h
+++ b/src/core/windows/SDL_windows.h
@@ -180,6 +180,9 @@ extern BOOL WIN_IsWindows7OrGreater(void);
 // Returns true if we're running on Windows 8 and newer
 extern BOOL WIN_IsWindows8OrGreater(void);
 
+// Returns true if we're running on Windows 11 and newer
+extern BOOL WIN_IsWindows11OrGreater(void);
+
 // You need to SDL_free() the result of this call.
 extern char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid);