summaryrefslogtreecommitdiff
path: root/SDL-3.2.8/src/core/windows/SDL_windows.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2026-03-06 13:26:57 -0800
committer3gg <3gg@shellblade.net>2026-03-06 13:26:57 -0800
commitf5c89b3bd5d74849757fd5b4d1a300068522a3ca (patch)
treed6f6e4c81745b393d7594b334710f30c0b2df3bd /SDL-3.2.8/src/core/windows/SDL_windows.c
Initial commitHEADmain
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.c375
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
31typedef 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
57bool 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()
80bool WIN_SetError(const char *prefix)
81{
82 return WIN_SetErrorFromHRESULT(prefix, GetLastError());
83}
84
85HRESULT
86WIN_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
112void WIN_CoUninitialize(void)
113{
114 CoUninitialize();
115}
116
117FARPROC 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
133HRESULT
134WIN_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
157void 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)
167static 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?" :)
205BOOL WIN_IsWindowsXP(void)
206{
207 CHECKWINVER(FALSE, !WIN_IsWindowsVistaOrGreater() && IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0));
208}
209
210BOOL WIN_IsWindowsVistaOrGreater(void)
211{
212 CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0));
213}
214
215BOOL WIN_IsWindows7OrGreater(void)
216{
217 CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0));
218}
219
220BOOL 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/*
229WAVExxxCAPS gives you 31 bytes for the device name, and just truncates if it's
230longer. However, since WinXP, you can use the WAVExxxCAPS2 structure, which
231will give you a name GUID. The full name is in the Windows Registry under
232that GUID, located here: HKLM\System\CurrentControlSet\Control\MediaCategories
233
234Note that drivers can report GUID_NULL for the name GUID, in which case,
235Windows makes a best effort to fill in those 31 bytes in the usual place.
236This info summarized from MSDN:
237
238http://web.archive.org/web/20131027093034/http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382(v=vs.85).aspx
239
240Always look this up in the registry if possible, because the strings are
241different! At least on Win10, I see "Yeti Stereo Microphone" in the
242Registry, 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
245has the same problem.)
246
247WASAPI doesn't need this. This is just for DirectSound/WinMM.
248*/
249char *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
307BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b)
308{
309 return (SDL_memcmp(a, b, sizeof(*a)) == 0);
310}
311
312BOOL WIN_IsEqualIID(REFIID a, REFIID b)
313{
314 return (SDL_memcmp(a, b, sizeof(*a)) == 0);
315}
316
317void 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
325void 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
333BOOL 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
341static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
342static 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
345SDL_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
367int 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)