diff options
Diffstat (limited to 'SDL-3.2.8/src/core/windows/SDL_windows.c')
| -rw-r--r-- | SDL-3.2.8/src/core/windows/SDL_windows.c | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/SDL-3.2.8/src/core/windows/SDL_windows.c b/SDL-3.2.8/src/core/windows/SDL_windows.c new file mode 100644 index 0000000..286e8e6 --- /dev/null +++ b/SDL-3.2.8/src/core/windows/SDL_windows.c | |||
| @@ -0,0 +1,375 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
| 4 | |||
| 5 | This software is provided 'as-is', without any express or implied | ||
| 6 | warranty. In no event will the authors be held liable for any damages | ||
| 7 | arising from the use of this software. | ||
| 8 | |||
| 9 | Permission is granted to anyone to use this software for any purpose, | ||
| 10 | including commercial applications, and to alter it and redistribute it | ||
| 11 | freely, subject to the following restrictions: | ||
| 12 | |||
| 13 | 1. The origin of this software must not be misrepresented; you must not | ||
| 14 | claim that you wrote the original software. If you use this software | ||
| 15 | in a product, an acknowledgment in the product documentation would be | ||
| 16 | appreciated but is not required. | ||
| 17 | 2. Altered source versions must be plainly marked as such, and must not be | ||
| 18 | misrepresented as being the original software. | ||
| 19 | 3. This notice may not be removed or altered from any source distribution. | ||
| 20 | */ | ||
| 21 | #include "SDL_internal.h" | ||
| 22 | |||
| 23 | #if defined(SDL_PLATFORM_WINDOWS) | ||
| 24 | |||
| 25 | #include "SDL_windows.h" | ||
| 26 | |||
| 27 | #include <objbase.h> // for CoInitialize/CoUninitialize (Win32 only) | ||
| 28 | #ifdef HAVE_ROAPI_H | ||
| 29 | #include <roapi.h> // For RoInitialize/RoUninitialize (Win32 only) | ||
| 30 | #else | ||
| 31 | typedef enum RO_INIT_TYPE | ||
| 32 | { | ||
| 33 | RO_INIT_SINGLETHREADED = 0, | ||
| 34 | RO_INIT_MULTITHREADED = 1 | ||
| 35 | } RO_INIT_TYPE; | ||
| 36 | #endif | ||
| 37 | |||
| 38 | #ifndef _WIN32_WINNT_VISTA | ||
| 39 | #define _WIN32_WINNT_VISTA 0x0600 | ||
| 40 | #endif | ||
| 41 | #ifndef _WIN32_WINNT_WIN7 | ||
| 42 | #define _WIN32_WINNT_WIN7 0x0601 | ||
| 43 | #endif | ||
| 44 | #ifndef _WIN32_WINNT_WIN8 | ||
| 45 | #define _WIN32_WINNT_WIN8 0x0602 | ||
| 46 | #endif | ||
| 47 | |||
| 48 | #ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 | ||
| 49 | #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 | ||
| 50 | #endif | ||
| 51 | |||
| 52 | #ifndef WC_ERR_INVALID_CHARS | ||
| 53 | #define WC_ERR_INVALID_CHARS 0x00000080 | ||
| 54 | #endif | ||
| 55 | |||
| 56 | // Sets an error message based on an HRESULT | ||
| 57 | bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr) | ||
| 58 | { | ||
| 59 | TCHAR buffer[1024]; | ||
| 60 | char *message; | ||
| 61 | TCHAR *p = buffer; | ||
| 62 | DWORD c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, | ||
| 63 | buffer, SDL_arraysize(buffer), NULL); | ||
| 64 | buffer[c] = 0; | ||
| 65 | // kill CR/LF that FormatMessage() sticks at the end | ||
| 66 | while (*p) { | ||
| 67 | if (*p == '\r') { | ||
| 68 | *p = 0; | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | ++p; | ||
| 72 | } | ||
| 73 | message = WIN_StringToUTF8(buffer); | ||
| 74 | SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ": " : "", message); | ||
| 75 | SDL_free(message); | ||
| 76 | return false; | ||
| 77 | } | ||
| 78 | |||
| 79 | // Sets an error message based on GetLastError() | ||
| 80 | bool WIN_SetError(const char *prefix) | ||
| 81 | { | ||
| 82 | return WIN_SetErrorFromHRESULT(prefix, GetLastError()); | ||
| 83 | } | ||
| 84 | |||
| 85 | HRESULT | ||
| 86 | WIN_CoInitialize(void) | ||
| 87 | { | ||
| 88 | /* SDL handles any threading model, so initialize with the default, which | ||
| 89 | is compatible with OLE and if that doesn't work, try multi-threaded mode. | ||
| 90 | |||
| 91 | If you need multi-threaded mode, call CoInitializeEx() before SDL_Init() | ||
| 92 | */ | ||
| 93 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 94 | // On Xbox, there's no need to call CoInitializeEx (and it's not implemented) | ||
| 95 | return S_OK; | ||
| 96 | #else | ||
| 97 | HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); | ||
| 98 | if (hr == RPC_E_CHANGED_MODE) { | ||
| 99 | hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 100 | } | ||
| 101 | |||
| 102 | // S_FALSE means success, but someone else already initialized. | ||
| 103 | // You still need to call CoUninitialize in this case! | ||
| 104 | if (hr == S_FALSE) { | ||
| 105 | return S_OK; | ||
| 106 | } | ||
| 107 | |||
| 108 | return hr; | ||
| 109 | #endif | ||
| 110 | } | ||
| 111 | |||
| 112 | void WIN_CoUninitialize(void) | ||
| 113 | { | ||
| 114 | CoUninitialize(); | ||
| 115 | } | ||
| 116 | |||
| 117 | FARPROC WIN_LoadComBaseFunction(const char *name) | ||
| 118 | { | ||
| 119 | static bool s_bLoaded; | ||
| 120 | static HMODULE s_hComBase; | ||
| 121 | |||
| 122 | if (!s_bLoaded) { | ||
| 123 | s_hComBase = LoadLibraryEx(TEXT("combase.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | ||
| 124 | s_bLoaded = true; | ||
| 125 | } | ||
| 126 | if (s_hComBase) { | ||
| 127 | return GetProcAddress(s_hComBase, name); | ||
| 128 | } else { | ||
| 129 | return NULL; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | HRESULT | ||
| 134 | WIN_RoInitialize(void) | ||
| 135 | { | ||
| 136 | typedef HRESULT(WINAPI * RoInitialize_t)(RO_INIT_TYPE initType); | ||
| 137 | RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize"); | ||
| 138 | if (RoInitializeFunc) { | ||
| 139 | // RO_INIT_SINGLETHREADED is equivalent to COINIT_APARTMENTTHREADED | ||
| 140 | HRESULT hr = RoInitializeFunc(RO_INIT_SINGLETHREADED); | ||
| 141 | if (hr == RPC_E_CHANGED_MODE) { | ||
| 142 | hr = RoInitializeFunc(RO_INIT_MULTITHREADED); | ||
| 143 | } | ||
| 144 | |||
| 145 | // S_FALSE means success, but someone else already initialized. | ||
| 146 | // You still need to call RoUninitialize in this case! | ||
| 147 | if (hr == S_FALSE) { | ||
| 148 | return S_OK; | ||
| 149 | } | ||
| 150 | |||
| 151 | return hr; | ||
| 152 | } else { | ||
| 153 | return E_NOINTERFACE; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | void WIN_RoUninitialize(void) | ||
| 158 | { | ||
| 159 | typedef void(WINAPI * RoUninitialize_t)(void); | ||
| 160 | RoUninitialize_t RoUninitializeFunc = (RoUninitialize_t)WIN_LoadComBaseFunction("RoUninitialize"); | ||
| 161 | if (RoUninitializeFunc) { | ||
| 162 | RoUninitializeFunc(); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 167 | static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) | ||
| 168 | { | ||
| 169 | OSVERSIONINFOEXW osvi; | ||
| 170 | DWORDLONG const dwlConditionMask = VerSetConditionMask( | ||
| 171 | VerSetConditionMask( | ||
| 172 | VerSetConditionMask( | ||
| 173 | 0, VER_MAJORVERSION, VER_GREATER_EQUAL), | ||
| 174 | VER_MINORVERSION, VER_GREATER_EQUAL), | ||
| 175 | VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); | ||
| 176 | |||
| 177 | SDL_zero(osvi); | ||
| 178 | osvi.dwOSVersionInfoSize = sizeof(osvi); | ||
| 179 | osvi.dwMajorVersion = wMajorVersion; | ||
| 180 | osvi.dwMinorVersion = wMinorVersion; | ||
| 181 | osvi.wServicePackMajor = wServicePackMajor; | ||
| 182 | |||
| 183 | return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; | ||
| 184 | } | ||
| 185 | #endif | ||
| 186 | |||
| 187 | // apply some static variables so we only call into the Win32 API once per process for each check. | ||
| 188 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 189 | #define CHECKWINVER(notdesktop_platform_result, test) return (notdesktop_platform_result); | ||
| 190 | #else | ||
| 191 | #define CHECKWINVER(notdesktop_platform_result, test) \ | ||
| 192 | static bool checked = false; \ | ||
| 193 | static BOOL result = FALSE; \ | ||
| 194 | if (!checked) { \ | ||
| 195 | result = (test); \ | ||
| 196 | checked = true; \ | ||
| 197 | } \ | ||
| 198 | return result; | ||
| 199 | #endif | ||
| 200 | |||
| 201 | // this is the oldest thing we run on (and we may lose support for this in SDL3 at any time!), | ||
| 202 | // so there's no "OrGreater" as that would always be TRUE. The other functions are here to | ||
| 203 | // ask "can we support a specific feature?" but this function is here to ask "do we need to do | ||
| 204 | // something different for an OS version we probably should abandon?" :) | ||
| 205 | BOOL WIN_IsWindowsXP(void) | ||
| 206 | { | ||
| 207 | CHECKWINVER(FALSE, !WIN_IsWindowsVistaOrGreater() && IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0)); | ||
| 208 | } | ||
| 209 | |||
| 210 | BOOL WIN_IsWindowsVistaOrGreater(void) | ||
| 211 | { | ||
| 212 | CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0)); | ||
| 213 | } | ||
| 214 | |||
| 215 | BOOL WIN_IsWindows7OrGreater(void) | ||
| 216 | { | ||
| 217 | CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0)); | ||
| 218 | } | ||
| 219 | |||
| 220 | BOOL WIN_IsWindows8OrGreater(void) | ||
| 221 | { | ||
| 222 | CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0)); | ||
| 223 | } | ||
| 224 | |||
| 225 | #undef CHECKWINVER | ||
| 226 | |||
| 227 | |||
| 228 | /* | ||
| 229 | WAVExxxCAPS gives you 31 bytes for the device name, and just truncates if it's | ||
| 230 | longer. However, since WinXP, you can use the WAVExxxCAPS2 structure, which | ||
| 231 | will give you a name GUID. The full name is in the Windows Registry under | ||
| 232 | that GUID, located here: HKLM\System\CurrentControlSet\Control\MediaCategories | ||
| 233 | |||
| 234 | Note that drivers can report GUID_NULL for the name GUID, in which case, | ||
| 235 | Windows makes a best effort to fill in those 31 bytes in the usual place. | ||
| 236 | This info summarized from MSDN: | ||
| 237 | |||
| 238 | http://web.archive.org/web/20131027093034/http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382(v=vs.85).aspx | ||
| 239 | |||
| 240 | Always look this up in the registry if possible, because the strings are | ||
| 241 | different! At least on Win10, I see "Yeti Stereo Microphone" in the | ||
| 242 | Registry, and a unhelpful "Microphone(Yeti Stereo Microph" in winmm. Sigh. | ||
| 243 | |||
| 244 | (Also, DirectSound shouldn't be limited to 32 chars, but its device enum | ||
| 245 | has the same problem.) | ||
| 246 | |||
| 247 | WASAPI doesn't need this. This is just for DirectSound/WinMM. | ||
| 248 | */ | ||
| 249 | char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid) | ||
| 250 | { | ||
| 251 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 252 | return WIN_StringToUTF8W(name); // No registry access on Xbox, go with what we've got. | ||
| 253 | #else | ||
| 254 | static const GUID nullguid = { 0 }; | ||
| 255 | const unsigned char *ptr; | ||
| 256 | char keystr[128]; | ||
| 257 | WCHAR *strw = NULL; | ||
| 258 | bool rc; | ||
| 259 | HKEY hkey; | ||
| 260 | DWORD len = 0; | ||
| 261 | char *result = NULL; | ||
| 262 | |||
| 263 | if (WIN_IsEqualGUID(guid, &nullguid)) { | ||
| 264 | return WIN_StringToUTF8(name); // No GUID, go with what we've got. | ||
| 265 | } | ||
| 266 | |||
| 267 | ptr = (const unsigned char *)guid; | ||
| 268 | (void)SDL_snprintf(keystr, sizeof(keystr), | ||
| 269 | "System\\CurrentControlSet\\Control\\MediaCategories\\{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", | ||
| 270 | ptr[3], ptr[2], ptr[1], ptr[0], ptr[5], ptr[4], ptr[7], ptr[6], | ||
| 271 | ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]); | ||
| 272 | |||
| 273 | strw = WIN_UTF8ToString(keystr); | ||
| 274 | rc = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, strw, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS); | ||
| 275 | SDL_free(strw); | ||
| 276 | if (!rc) { | ||
| 277 | return WIN_StringToUTF8(name); // oh well. | ||
| 278 | } | ||
| 279 | |||
| 280 | rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, NULL, &len) == ERROR_SUCCESS); | ||
| 281 | if (!rc) { | ||
| 282 | RegCloseKey(hkey); | ||
| 283 | return WIN_StringToUTF8(name); // oh well. | ||
| 284 | } | ||
| 285 | |||
| 286 | strw = (WCHAR *)SDL_malloc(len + sizeof(WCHAR)); | ||
| 287 | if (!strw) { | ||
| 288 | RegCloseKey(hkey); | ||
| 289 | return WIN_StringToUTF8(name); // oh well. | ||
| 290 | } | ||
| 291 | |||
| 292 | rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, (LPBYTE)strw, &len) == ERROR_SUCCESS); | ||
| 293 | RegCloseKey(hkey); | ||
| 294 | if (!rc) { | ||
| 295 | SDL_free(strw); | ||
| 296 | return WIN_StringToUTF8(name); // oh well. | ||
| 297 | } | ||
| 298 | |||
| 299 | strw[len / 2] = 0; // make sure it's null-terminated. | ||
| 300 | |||
| 301 | result = WIN_StringToUTF8(strw); | ||
| 302 | SDL_free(strw); | ||
| 303 | return result ? result : WIN_StringToUTF8(name); | ||
| 304 | #endif | ||
| 305 | } | ||
| 306 | |||
| 307 | BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b) | ||
| 308 | { | ||
| 309 | return (SDL_memcmp(a, b, sizeof(*a)) == 0); | ||
| 310 | } | ||
| 311 | |||
| 312 | BOOL WIN_IsEqualIID(REFIID a, REFIID b) | ||
| 313 | { | ||
| 314 | return (SDL_memcmp(a, b, sizeof(*a)) == 0); | ||
| 315 | } | ||
| 316 | |||
| 317 | void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect) | ||
| 318 | { | ||
| 319 | sdlrect->x = winrect->left; | ||
| 320 | sdlrect->w = (winrect->right - winrect->left) + 1; | ||
| 321 | sdlrect->y = winrect->top; | ||
| 322 | sdlrect->h = (winrect->bottom - winrect->top) + 1; | ||
| 323 | } | ||
| 324 | |||
| 325 | void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect) | ||
| 326 | { | ||
| 327 | winrect->left = sdlrect->x; | ||
| 328 | winrect->right = sdlrect->x + sdlrect->w - 1; | ||
| 329 | winrect->top = sdlrect->y; | ||
| 330 | winrect->bottom = sdlrect->y + sdlrect->h - 1; | ||
| 331 | } | ||
| 332 | |||
| 333 | BOOL WIN_IsRectEmpty(const RECT *rect) | ||
| 334 | { | ||
| 335 | // Calculating this manually because Xbox does not support Win32 IsRectEmpty. | ||
| 336 | return (rect->right <= rect->left) || (rect->bottom <= rect->top); | ||
| 337 | } | ||
| 338 | |||
| 339 | // Some GUIDs we need to know without linking to libraries that aren't available before Vista. | ||
| 340 | /* *INDENT-OFF* */ // clang-format off | ||
| 341 | static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; | ||
| 342 | static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; | ||
| 343 | /* *INDENT-ON* */ // clang-format on | ||
| 344 | |||
| 345 | SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat) | ||
| 346 | { | ||
| 347 | if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) { | ||
| 348 | return SDL_AUDIO_F32; | ||
| 349 | } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) { | ||
| 350 | return SDL_AUDIO_S16; | ||
| 351 | } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) { | ||
| 352 | return SDL_AUDIO_S32; | ||
| 353 | } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { | ||
| 354 | const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat; | ||
| 355 | if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { | ||
| 356 | return SDL_AUDIO_F32; | ||
| 357 | } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) { | ||
| 358 | return SDL_AUDIO_S16; | ||
| 359 | } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { | ||
| 360 | return SDL_AUDIO_S32; | ||
| 361 | } | ||
| 362 | } | ||
| 363 | return SDL_AUDIO_UNKNOWN; | ||
| 364 | } | ||
| 365 | |||
| 366 | |||
| 367 | int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar) | ||
| 368 | { | ||
| 369 | if (WIN_IsWindowsXP()) { | ||
| 370 | dwFlags &= ~WC_ERR_INVALID_CHARS; // not supported before Vista. Without this flag, it will just replace bogus chars with U+FFFD. You're on your own, WinXP. | ||
| 371 | } | ||
| 372 | return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar); | ||
| 373 | } | ||
| 374 | |||
| 375 | #endif // defined(SDL_PLATFORM_WINDOWS) | ||
