Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/core/windows/SDL_windows.c
9905 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
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_WindowRectValid(const RECT *rect)
334
{
335
// A window can be resized to zero height, but not zero width
336
return (rect->right > 0);
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)
376
377