summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/core/windows
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/core/windows
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/core/windows')
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_directx.h112
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.c98
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.h36
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_hid.c254
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_hid.h215
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.c434
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.h45
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_windows.c375
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_windows.h172
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_xinput.c140
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/SDL_xinput.h276
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/pch.c21
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/pch_cpp.cpp21
-rw-r--r--contrib/SDL-3.2.8/src/core/windows/version.rc38
14 files changed, 2237 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_directx.h b/contrib/SDL-3.2.8/src/core/windows/SDL_directx.h
new file mode 100644
index 0000000..6101e92
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_directx.h
@@ -0,0 +1,112 @@
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#ifndef SDL_directx_h_
24#define SDL_directx_h_
25
26// Include all of the DirectX 8.0 headers and adds any necessary tweaks
27
28#include "SDL_windows.h"
29#include <mmsystem.h>
30#ifndef WIN32
31#define WIN32
32#endif
33#undef WINNT
34
35// Far pointers don't exist in 32-bit code
36#ifndef FAR
37#define FAR
38#endif
39
40// Error codes not yet included in Win32 API header files
41#ifndef MAKE_HRESULT
42#define MAKE_HRESULT(sev, fac, code) \
43 ((HRESULT)(((unsigned long)(sev) << 31) | ((unsigned long)(fac) << 16) | ((unsigned long)(code))))
44#endif
45
46#ifndef S_OK
47#define S_OK (HRESULT)0x00000000L
48#endif
49
50#ifndef SUCCEEDED
51#define SUCCEEDED(x) ((HRESULT)(x) >= 0)
52#endif
53#ifndef FAILED
54#define FAILED(x) ((HRESULT)(x) < 0)
55#endif
56
57#ifndef E_FAIL
58#define E_FAIL (HRESULT)0x80000008L
59#endif
60#ifndef E_NOINTERFACE
61#define E_NOINTERFACE (HRESULT)0x80004002L
62#endif
63#ifndef E_OUTOFMEMORY
64#define E_OUTOFMEMORY (HRESULT)0x8007000EL
65#endif
66#ifndef E_INVALIDARG
67#define E_INVALIDARG (HRESULT)0x80070057L
68#endif
69#ifndef E_NOTIMPL
70#define E_NOTIMPL (HRESULT)0x80004001L
71#endif
72#ifndef REGDB_E_CLASSNOTREG
73#define REGDB_E_CLASSNOTREG (HRESULT)0x80040154L
74#endif
75
76// Severity codes
77#ifndef SEVERITY_ERROR
78#define SEVERITY_ERROR 1
79#endif
80
81// Error facility codes
82#ifndef FACILITY_WIN32
83#define FACILITY_WIN32 7
84#endif
85
86#ifndef FIELD_OFFSET
87#define FIELD_OFFSET(type, field) ((LONG) & (((type *)0)->field))
88#endif
89
90/* DirectX headers (if it isn't included, I haven't tested it yet)
91 */
92// We need these defines to mark what version of DirectX API we use
93#define DIRECTDRAW_VERSION 0x0700
94#define DIRECTSOUND_VERSION 0x0800
95#define DIRECTINPUT_VERSION 0x0800 // Need version 7 for force feedback. Need version 8 so IDirectInput8_EnumDevices doesn't leak like a sieve...
96
97#ifdef HAVE_DDRAW_H
98#include <ddraw.h>
99#endif
100#ifdef HAVE_DSOUND_H
101#include <dsound.h>
102#endif
103#ifdef HAVE_DINPUT_H
104#include <dinput.h>
105#else
106typedef struct
107{
108 int unused;
109} DIDEVICEINSTANCE;
110#endif
111
112#endif // SDL_directx_h_
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.c b/contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.c
new file mode 100644
index 0000000..9ac5912
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.c
@@ -0,0 +1,98 @@
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#ifdef HAVE_GAMEINPUT_H
24
25#include "SDL_windows.h"
26#include "SDL_gameinput.h"
27
28#ifdef SDL_PLATFORM_WIN32
29#include <initguid.h>
30// {11BE2A7E-4254-445A-9C09-FFC40F006918}
31DEFINE_GUID(SDL_IID_GameInput, 0x11BE2A7E, 0x4254, 0x445A, 0x9C, 0x09, 0xFF, 0xC4, 0x0F, 0x00, 0x69, 0x18);
32#endif
33
34static SDL_SharedObject *g_hGameInputDLL;
35static IGameInput *g_pGameInput;
36static int g_nGameInputRefCount;
37
38bool SDL_InitGameInput(IGameInput **ppGameInput)
39{
40 if (g_nGameInputRefCount == 0) {
41 g_hGameInputDLL = SDL_LoadObject("gameinput.dll");
42 if (!g_hGameInputDLL) {
43 return false;
44 }
45
46 typedef HRESULT (WINAPI *GameInputCreate_t)(IGameInput * *gameInput);
47 GameInputCreate_t GameInputCreateFunc = (GameInputCreate_t)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate");
48 if (!GameInputCreateFunc) {
49 SDL_UnloadObject(g_hGameInputDLL);
50 return false;
51 }
52
53 IGameInput *pGameInput = NULL;
54 HRESULT hr = GameInputCreateFunc(&pGameInput);
55 if (FAILED(hr)) {
56 SDL_UnloadObject(g_hGameInputDLL);
57 return WIN_SetErrorFromHRESULT("GameInputCreate failed", hr);
58 }
59
60#ifdef SDL_PLATFORM_WIN32
61 hr = IGameInput_QueryInterface(pGameInput, &SDL_IID_GameInput, (void **)&g_pGameInput);
62 IGameInput_Release(pGameInput);
63 if (FAILED(hr)) {
64 SDL_UnloadObject(g_hGameInputDLL);
65 return WIN_SetErrorFromHRESULT("GameInput QueryInterface failed", hr);
66 }
67#else
68 // Assume that the version we get is compatible with the current SDK
69 // If that isn't the case, define the correct GUID for SDL_IID_GameInput above
70 g_pGameInput = pGameInput;
71#endif
72 }
73 ++g_nGameInputRefCount;
74
75 if (ppGameInput) {
76 *ppGameInput = g_pGameInput;
77 }
78 return true;
79}
80
81void SDL_QuitGameInput(void)
82{
83 SDL_assert(g_nGameInputRefCount > 0);
84
85 --g_nGameInputRefCount;
86 if (g_nGameInputRefCount == 0) {
87 if (g_pGameInput) {
88 IGameInput_Release(g_pGameInput);
89 g_pGameInput = NULL;
90 }
91 if (g_hGameInputDLL) {
92 SDL_UnloadObject(g_hGameInputDLL);
93 g_hGameInputDLL = NULL;
94 }
95 }
96}
97
98#endif // HAVE_GAMEINPUT_H
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.h b/contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.h
new file mode 100644
index 0000000..0022c0b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.h
@@ -0,0 +1,36 @@
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#ifndef SDL_gameinput_h_
24#define SDL_gameinput_h_
25
26#ifdef HAVE_GAMEINPUT_H
27
28#define COBJMACROS
29#include <gameinput.h>
30
31extern bool SDL_InitGameInput(IGameInput **ppGameInput);
32extern void SDL_QuitGameInput(void);
33
34#endif // HAVE_GAMEINPUT_H
35
36#endif // SDL_gameinput_h_
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_hid.c b/contrib/SDL-3.2.8/src/core/windows/SDL_hid.c
new file mode 100644
index 0000000..87e8735
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_hid.c
@@ -0,0 +1,254 @@
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#include "SDL_hid.h"
24
25HidD_GetAttributes_t SDL_HidD_GetAttributes;
26HidD_GetString_t SDL_HidD_GetManufacturerString;
27HidD_GetString_t SDL_HidD_GetProductString;
28HidP_GetCaps_t SDL_HidP_GetCaps;
29HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps;
30HidP_GetValueCaps_t SDL_HidP_GetValueCaps;
31HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength;
32HidP_GetData_t SDL_HidP_GetData;
33
34static HMODULE s_pHIDDLL = 0;
35static int s_HIDDLLRefCount = 0;
36
37
38bool WIN_LoadHIDDLL(void)
39{
40 if (s_pHIDDLL) {
41 SDL_assert(s_HIDDLLRefCount > 0);
42 s_HIDDLLRefCount++;
43 return true; // already loaded
44 }
45
46 s_pHIDDLL = LoadLibrary(TEXT("hid.dll"));
47 if (!s_pHIDDLL) {
48 return false;
49 }
50
51 SDL_assert(s_HIDDLLRefCount == 0);
52 s_HIDDLLRefCount = 1;
53
54 SDL_HidD_GetAttributes = (HidD_GetAttributes_t)GetProcAddress(s_pHIDDLL, "HidD_GetAttributes");
55 SDL_HidD_GetManufacturerString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetManufacturerString");
56 SDL_HidD_GetProductString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetProductString");
57 SDL_HidP_GetCaps = (HidP_GetCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetCaps");
58 SDL_HidP_GetButtonCaps = (HidP_GetButtonCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetButtonCaps");
59 SDL_HidP_GetValueCaps = (HidP_GetValueCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetValueCaps");
60 SDL_HidP_MaxDataListLength = (HidP_MaxDataListLength_t)GetProcAddress(s_pHIDDLL, "HidP_MaxDataListLength");
61 SDL_HidP_GetData = (HidP_GetData_t)GetProcAddress(s_pHIDDLL, "HidP_GetData");
62 if (!SDL_HidD_GetManufacturerString || !SDL_HidD_GetProductString ||
63 !SDL_HidP_GetCaps || !SDL_HidP_GetButtonCaps ||
64 !SDL_HidP_GetValueCaps || !SDL_HidP_MaxDataListLength || !SDL_HidP_GetData) {
65 WIN_UnloadHIDDLL();
66 return false;
67 }
68
69 return true;
70}
71
72void WIN_UnloadHIDDLL(void)
73{
74 if (s_pHIDDLL) {
75 SDL_assert(s_HIDDLLRefCount > 0);
76 if (--s_HIDDLLRefCount == 0) {
77 FreeLibrary(s_pHIDDLL);
78 s_pHIDDLL = NULL;
79 }
80 } else {
81 SDL_assert(s_HIDDLLRefCount == 0);
82 }
83}
84
85#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
86
87// CM_Register_Notification definitions
88
89#define CR_SUCCESS 0
90
91DECLARE_HANDLE(HCMNOTIFICATION);
92typedef HCMNOTIFICATION *PHCMNOTIFICATION;
93
94typedef enum _CM_NOTIFY_FILTER_TYPE
95{
96 CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE = 0,
97 CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE,
98 CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE,
99 CM_NOTIFY_FILTER_TYPE_MAX
100} CM_NOTIFY_FILTER_TYPE, *PCM_NOTIFY_FILTER_TYPE;
101
102typedef struct _CM_NOTIFY_FILTER
103{
104 DWORD cbSize;
105 DWORD Flags;
106 CM_NOTIFY_FILTER_TYPE FilterType;
107 DWORD Reserved;
108 union
109 {
110 struct
111 {
112 GUID ClassGuid;
113 } DeviceInterface;
114 struct
115 {
116 HANDLE hTarget;
117 } DeviceHandle;
118 struct
119 {
120 WCHAR InstanceId[200];
121 } DeviceInstance;
122 } u;
123} CM_NOTIFY_FILTER, *PCM_NOTIFY_FILTER;
124
125typedef enum _CM_NOTIFY_ACTION
126{
127 CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL = 0,
128 CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL,
129 CM_NOTIFY_ACTION_DEVICEQUERYREMOVE,
130 CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED,
131 CM_NOTIFY_ACTION_DEVICEREMOVEPENDING,
132 CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE,
133 CM_NOTIFY_ACTION_DEVICECUSTOMEVENT,
134 CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED,
135 CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED,
136 CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED,
137 CM_NOTIFY_ACTION_MAX
138} CM_NOTIFY_ACTION, *PCM_NOTIFY_ACTION;
139
140typedef struct _CM_NOTIFY_EVENT_DATA
141{
142 CM_NOTIFY_FILTER_TYPE FilterType;
143 DWORD Reserved;
144 union
145 {
146 struct
147 {
148 GUID ClassGuid;
149 WCHAR SymbolicLink[ANYSIZE_ARRAY];
150 } DeviceInterface;
151 struct
152 {
153 GUID EventGuid;
154 LONG NameOffset;
155 DWORD DataSize;
156 BYTE Data[ANYSIZE_ARRAY];
157 } DeviceHandle;
158 struct
159 {
160 WCHAR InstanceId[ANYSIZE_ARRAY];
161 } DeviceInstance;
162 } u;
163} CM_NOTIFY_EVENT_DATA, *PCM_NOTIFY_EVENT_DATA;
164
165typedef DWORD (CALLBACK *PCM_NOTIFY_CALLBACK)(HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize);
166
167typedef DWORD (WINAPI *CM_Register_NotificationFunc)(PCM_NOTIFY_FILTER pFilter, PVOID pContext, PCM_NOTIFY_CALLBACK pCallback, PHCMNOTIFICATION pNotifyContext);
168typedef DWORD (WINAPI *CM_Unregister_NotificationFunc)(HCMNOTIFICATION NotifyContext);
169
170static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
171
172static int s_DeviceNotificationsRequested;
173static HMODULE cfgmgr32_lib_handle;
174static CM_Register_NotificationFunc CM_Register_Notification;
175static CM_Unregister_NotificationFunc CM_Unregister_Notification;
176static HCMNOTIFICATION s_DeviceNotificationFuncHandle;
177static Uint64 s_LastDeviceNotification = 1;
178
179static DWORD CALLBACK SDL_DeviceNotificationFunc(HCMNOTIFICATION hNotify, PVOID context, CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA eventData, DWORD event_data_size)
180{
181 if (action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ||
182 action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL) {
183 s_LastDeviceNotification = SDL_GetTicksNS();
184 }
185 return ERROR_SUCCESS;
186}
187
188void WIN_InitDeviceNotification(void)
189{
190 ++s_DeviceNotificationsRequested;
191 if (s_DeviceNotificationsRequested > 1) {
192 return;
193 }
194
195 cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll");
196 if (cfgmgr32_lib_handle) {
197 CM_Register_Notification = (CM_Register_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Register_Notification");
198 CM_Unregister_Notification = (CM_Unregister_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Unregister_Notification");
199 if (CM_Register_Notification && CM_Unregister_Notification) {
200 CM_NOTIFY_FILTER notify_filter;
201
202 SDL_zero(notify_filter);
203 notify_filter.cbSize = sizeof(notify_filter);
204 notify_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
205 notify_filter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_HID;
206 if (CM_Register_Notification(&notify_filter, NULL, SDL_DeviceNotificationFunc, &s_DeviceNotificationFuncHandle) == CR_SUCCESS) {
207 return;
208 }
209 }
210 }
211
212 // FIXME: Should we log errors?
213}
214
215Uint64 WIN_GetLastDeviceNotification(void)
216{
217 return s_LastDeviceNotification;
218}
219
220void WIN_QuitDeviceNotification(void)
221{
222 if (--s_DeviceNotificationsRequested > 0) {
223 return;
224 }
225 // Make sure we have balanced calls to init/quit
226 SDL_assert(s_DeviceNotificationsRequested == 0);
227
228 if (cfgmgr32_lib_handle) {
229 if (s_DeviceNotificationFuncHandle && CM_Unregister_Notification) {
230 CM_Unregister_Notification(s_DeviceNotificationFuncHandle);
231 s_DeviceNotificationFuncHandle = NULL;
232 }
233
234 FreeLibrary(cfgmgr32_lib_handle);
235 cfgmgr32_lib_handle = NULL;
236 }
237}
238
239#else
240
241void WIN_InitDeviceNotification(void)
242{
243}
244
245Uint64 WIN_GetLastDeviceNotification( void )
246{
247 return 0;
248}
249
250void WIN_QuitDeviceNotification(void)
251{
252}
253
254#endif // !SDL_PLATFORM_XBOXONE && !SDL_PLATFORM_XBOXSERIES
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_hid.h b/contrib/SDL-3.2.8/src/core/windows/SDL_hid.h
new file mode 100644
index 0000000..46c22f2
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_hid.h
@@ -0,0 +1,215 @@
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#ifndef SDL_hid_h_
24#define SDL_hid_h_
25
26#include "SDL_windows.h"
27
28typedef LONG NTSTATUS;
29typedef USHORT USAGE;
30typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA;
31
32typedef struct _HIDD_ATTRIBUTES
33{
34 ULONG Size;
35 USHORT VendorID;
36 USHORT ProductID;
37 USHORT VersionNumber;
38} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
39
40typedef enum
41{
42 HidP_Input = 0,
43 HidP_Output = 1,
44 HidP_Feature = 2
45} HIDP_REPORT_TYPE;
46
47typedef struct
48{
49 USAGE UsagePage;
50 UCHAR ReportID;
51 BOOLEAN IsAlias;
52 USHORT BitField;
53 USHORT LinkCollection;
54 USAGE LinkUsage;
55 USAGE LinkUsagePage;
56 BOOLEAN IsRange;
57 BOOLEAN IsStringRange;
58 BOOLEAN IsDesignatorRange;
59 BOOLEAN IsAbsolute;
60 ULONG Reserved[10];
61 union
62 {
63 struct
64 {
65 USAGE UsageMin;
66 USAGE UsageMax;
67 USHORT StringMin;
68 USHORT StringMax;
69 USHORT DesignatorMin;
70 USHORT DesignatorMax;
71 USHORT DataIndexMin;
72 USHORT DataIndexMax;
73 } Range;
74 struct
75 {
76 USAGE Usage;
77 USAGE Reserved1;
78 USHORT StringIndex;
79 USHORT Reserved2;
80 USHORT DesignatorIndex;
81 USHORT Reserved3;
82 USHORT DataIndex;
83 USHORT Reserved4;
84 } NotRange;
85 };
86} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS;
87
88typedef struct
89{
90 USAGE UsagePage;
91 UCHAR ReportID;
92 BOOLEAN IsAlias;
93 USHORT BitField;
94 USHORT LinkCollection;
95 USAGE LinkUsage;
96 USAGE LinkUsagePage;
97 BOOLEAN IsRange;
98 BOOLEAN IsStringRange;
99 BOOLEAN IsDesignatorRange;
100 BOOLEAN IsAbsolute;
101 BOOLEAN HasNull;
102 UCHAR Reserved;
103 USHORT BitSize;
104 USHORT ReportCount;
105 USHORT Reserved2[5];
106 ULONG UnitsExp;
107 ULONG Units;
108 LONG LogicalMin;
109 LONG LogicalMax;
110 LONG PhysicalMin;
111 LONG PhysicalMax;
112 union
113 {
114 struct
115 {
116 USAGE UsageMin;
117 USAGE UsageMax;
118 USHORT StringMin;
119 USHORT StringMax;
120 USHORT DesignatorMin;
121 USHORT DesignatorMax;
122 USHORT DataIndexMin;
123 USHORT DataIndexMax;
124 } Range;
125 struct
126 {
127 USAGE Usage;
128 USAGE Reserved1;
129 USHORT StringIndex;
130 USHORT Reserved2;
131 USHORT DesignatorIndex;
132 USHORT Reserved3;
133 USHORT DataIndex;
134 USHORT Reserved4;
135 } NotRange;
136 };
137} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
138
139typedef struct
140{
141 USAGE Usage;
142 USAGE UsagePage;
143 USHORT InputReportByteLength;
144 USHORT OutputReportByteLength;
145 USHORT FeatureReportByteLength;
146 USHORT Reserved[17];
147 USHORT NumberLinkCollectionNodes;
148 USHORT NumberInputButtonCaps;
149 USHORT NumberInputValueCaps;
150 USHORT NumberInputDataIndices;
151 USHORT NumberOutputButtonCaps;
152 USHORT NumberOutputValueCaps;
153 USHORT NumberOutputDataIndices;
154 USHORT NumberFeatureButtonCaps;
155 USHORT NumberFeatureValueCaps;
156 USHORT NumberFeatureDataIndices;
157} HIDP_CAPS, *PHIDP_CAPS;
158
159typedef struct
160{
161 USHORT DataIndex;
162 USHORT Reserved;
163 union
164 {
165 ULONG RawValue;
166 BOOLEAN On;
167 };
168} HIDP_DATA, *PHIDP_DATA;
169
170#define HIDP_ERROR_CODES(p1, p2) ((NTSTATUS)(((p1) << 28) | (0x11 << 16) | (p2)))
171#define HIDP_STATUS_SUCCESS HIDP_ERROR_CODES(0x0, 0x0000)
172#define HIDP_STATUS_NULL HIDP_ERROR_CODES(0x8, 0x0001)
173#define HIDP_STATUS_INVALID_PREPARSED_DATA HIDP_ERROR_CODES(0xC, 0x0001)
174#define HIDP_STATUS_INVALID_REPORT_TYPE HIDP_ERROR_CODES(0xC, 0x0002)
175#define HIDP_STATUS_INVALID_REPORT_LENGTH HIDP_ERROR_CODES(0xC, 0x0003)
176#define HIDP_STATUS_USAGE_NOT_FOUND HIDP_ERROR_CODES(0xC, 0x0004)
177#define HIDP_STATUS_VALUE_OUT_OF_RANGE HIDP_ERROR_CODES(0xC, 0x0005)
178#define HIDP_STATUS_BAD_LOG_PHY_VALUES HIDP_ERROR_CODES(0xC, 0x0006)
179#define HIDP_STATUS_BUFFER_TOO_SMALL HIDP_ERROR_CODES(0xC, 0x0007)
180#define HIDP_STATUS_INTERNAL_ERROR HIDP_ERROR_CODES(0xC, 0x0008)
181#define HIDP_STATUS_I8042_TRANS_UNKNOWN HIDP_ERROR_CODES(0xC, 0x0009)
182#define HIDP_STATUS_INCOMPATIBLE_REPORT_ID HIDP_ERROR_CODES(0xC, 0x000A)
183#define HIDP_STATUS_NOT_VALUE_ARRAY HIDP_ERROR_CODES(0xC, 0x000B)
184#define HIDP_STATUS_IS_VALUE_ARRAY HIDP_ERROR_CODES(0xC, 0x000C)
185#define HIDP_STATUS_DATA_INDEX_NOT_FOUND HIDP_ERROR_CODES(0xC, 0x000D)
186#define HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE HIDP_ERROR_CODES(0xC, 0x000E)
187#define HIDP_STATUS_BUTTON_NOT_PRESSED HIDP_ERROR_CODES(0xC, 0x000F)
188#define HIDP_STATUS_REPORT_DOES_NOT_EXIST HIDP_ERROR_CODES(0xC, 0x0010)
189#define HIDP_STATUS_NOT_IMPLEMENTED HIDP_ERROR_CODES(0xC, 0x0020)
190
191extern bool WIN_LoadHIDDLL(void);
192extern void WIN_UnloadHIDDLL(void);
193
194typedef BOOLEAN (WINAPI *HidD_GetAttributes_t)(HANDLE HidDeviceObject, PHIDD_ATTRIBUTES Attributes);
195typedef BOOLEAN (WINAPI *HidD_GetString_t)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength);
196typedef NTSTATUS (WINAPI *HidP_GetCaps_t)(PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities);
197typedef NTSTATUS (WINAPI *HidP_GetButtonCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
198typedef NTSTATUS (WINAPI *HidP_GetValueCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
199typedef ULONG (WINAPI *HidP_MaxDataListLength_t)(HIDP_REPORT_TYPE ReportType, PHIDP_PREPARSED_DATA PreparsedData);
200typedef NTSTATUS (WINAPI *HidP_GetData_t)(HIDP_REPORT_TYPE ReportType, PHIDP_DATA DataList, PULONG DataLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
201
202extern HidD_GetAttributes_t SDL_HidD_GetAttributes;
203extern HidD_GetString_t SDL_HidD_GetManufacturerString;
204extern HidD_GetString_t SDL_HidD_GetProductString;
205extern HidP_GetCaps_t SDL_HidP_GetCaps;
206extern HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps;
207extern HidP_GetValueCaps_t SDL_HidP_GetValueCaps;
208extern HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength;
209extern HidP_GetData_t SDL_HidP_GetData;
210
211void WIN_InitDeviceNotification(void);
212Uint64 WIN_GetLastDeviceNotification(void);
213void WIN_QuitDeviceNotification(void);
214
215#endif // SDL_hid_h_
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.c b/contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.c
new file mode 100644
index 0000000..802a412
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.c
@@ -0,0 +1,434 @@
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) && defined(HAVE_MMDEVICEAPI_H)
24
25#include "SDL_windows.h"
26#include "SDL_immdevice.h"
27#include "../../audio/SDL_sysaudio.h"
28#include <objbase.h> // For CLSIDFromString
29
30typedef struct SDL_IMMDevice_HandleData
31{
32 LPWSTR immdevice_id;
33 GUID directsound_guid;
34} SDL_IMMDevice_HandleData;
35
36static const ERole SDL_IMMDevice_role = eConsole; // !!! FIXME: should this be eMultimedia? Should be a hint?
37
38// This is global to the WASAPI target, to handle hotplug and default device lookup.
39static IMMDeviceEnumerator *enumerator = NULL;
40static SDL_IMMDevice_callbacks immcallbacks;
41
42// PropVariantInit() is an inline function/macro in PropIdl.h that calls the C runtime's memset() directly. Use ours instead, to avoid dependency.
43#ifdef PropVariantInit
44#undef PropVariantInit
45#endif
46#define PropVariantInit(p) SDL_zerop(p)
47
48// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
49/* *INDENT-OFF* */ // clang-format off
50static const CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,{ 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } };
51static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,{ 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } };
52static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{ 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } };
53static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
54static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
55static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 };
56static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0x4edd,{ 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e, } }, 4 };
57/* *INDENT-ON* */ // clang-format on
58
59static bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata)
60{
61 LPCWSTR devid = (LPCWSTR)userdata;
62 if (devid && device && device->handle) {
63 const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *)device->handle;
64 if (handle->immdevice_id && SDL_wcscmp(handle->immdevice_id, devid) == 0) {
65 return true;
66 }
67 }
68 return false;
69}
70
71static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid)
72{
73 return SDL_FindPhysicalAudioDeviceByCallback(FindByDevIDCallback, (void *) devid);
74}
75
76LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device)
77{
78 return (device && device->handle) ? &(((SDL_IMMDevice_HandleData *) device->handle)->directsound_guid) : NULL;
79}
80
81LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device)
82{
83 return (device && device->handle) ? ((const SDL_IMMDevice_HandleData *) device->handle)->immdevice_id : NULL;
84}
85
86static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt, GUID *guid)
87{
88 /* PKEY_Device_FriendlyName gives you "Speakers (SoundBlaster Pro)" which drives me nuts. I'd rather it be
89 "SoundBlaster Pro (Speakers)" but I guess that's developers vs users. Windows uses the FriendlyName in
90 its own UIs, like Volume Control, etc. */
91 IPropertyStore *props = NULL;
92 *utf8dev = NULL;
93 SDL_zerop(fmt);
94 if (SUCCEEDED(IMMDevice_OpenPropertyStore(device, STGM_READ, &props))) {
95 PROPVARIANT var;
96 PropVariantInit(&var);
97 if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_Device_FriendlyName, &var))) {
98 *utf8dev = WIN_StringToUTF8W(var.pwszVal);
99 }
100 PropVariantClear(&var);
101 if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEngine_DeviceFormat, &var))) {
102 SDL_memcpy(fmt, var.blob.pBlobData, SDL_min(var.blob.cbSize, sizeof(WAVEFORMATEXTENSIBLE)));
103 }
104 PropVariantClear(&var);
105 if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEndpoint_GUID, &var))) {
106 (void)CLSIDFromString(var.pwszVal, guid);
107 }
108 PropVariantClear(&var);
109 IPropertyStore_Release(props);
110 }
111}
112
113void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device)
114{
115 if (device && device->handle) {
116 SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
117 SDL_free(handle->immdevice_id);
118 SDL_free(handle);
119 device->handle = NULL;
120 }
121}
122
123static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid)
124{
125 /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
126 In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
127 phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
128 available and switch automatically. (!!! FIXME...?) */
129
130 if (!devname) {
131 return NULL;
132 }
133
134 // see if we already have this one first.
135 SDL_AudioDevice *device = SDL_IMMDevice_FindByDevID(devid);
136 if (device) {
137 if (SDL_GetAtomicInt(&device->zombie)) {
138 // whoa, it came back! This can happen if you unplug and replug USB headphones while we're still keeping the SDL object alive.
139 // Kill this device's IMMDevice id; the device will go away when the app closes it, or maybe a new default device is chosen
140 // (possibly this reconnected device), so we just want to make sure IMMDevice doesn't try to find the old device by the existing ID string.
141 SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
142 SDL_free(handle->immdevice_id);
143 handle->immdevice_id = NULL;
144 device = NULL; // add a new device, below.
145 }
146 }
147
148 if (!device) {
149 // handle is freed by SDL_IMMDevice_FreeDeviceHandle!
150 SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *)SDL_malloc(sizeof(SDL_IMMDevice_HandleData));
151 if (!handle) {
152 return NULL;
153 }
154 handle->immdevice_id = SDL_wcsdup(devid);
155 if (!handle->immdevice_id) {
156 SDL_free(handle);
157 return NULL;
158 }
159 SDL_memcpy(&handle->directsound_guid, dsoundguid, sizeof(GUID));
160
161 SDL_AudioSpec spec;
162 SDL_zero(spec);
163 spec.channels = (Uint8)fmt->Format.nChannels;
164 spec.freq = fmt->Format.nSamplesPerSec;
165 spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
166
167 device = SDL_AddAudioDevice(recording, devname, &spec, handle);
168 if (!device) {
169 SDL_free(handle->immdevice_id);
170 SDL_free(handle);
171 }
172 }
173
174 return device;
175}
176
177/* We need a COM subclass of IMMNotificationClient for hotplug support, which is
178 easy in C++, but we have to tapdance more to make work in C.
179 Thanks to this page for coaching on how to make this work:
180 https://www.codeproject.com/Articles/13601/COM-in-plain-C */
181
182typedef struct SDLMMNotificationClient
183{
184 const IMMNotificationClientVtbl *lpVtbl;
185 SDL_AtomicInt refcount;
186} SDLMMNotificationClient;
187
188static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
189{
190 if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient))) {
191 *ppv = client;
192 client->lpVtbl->AddRef(client);
193 return S_OK;
194 }
195
196 *ppv = NULL;
197 return E_NOINTERFACE;
198}
199
200static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient)
201{
202 SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
203 return (ULONG)(SDL_AtomicIncRef(&client->refcount) + 1);
204}
205
206static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *iclient)
207{
208 // client is a static object; we don't ever free it.
209 SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
210 const ULONG rc = SDL_AtomicDecRef(&client->refcount);
211 if (rc == 0) {
212 SDL_SetAtomicInt(&client->refcount, 0); // uhh...
213 return 0;
214 }
215 return rc - 1;
216}
217
218// These are the entry points called when WASAPI device endpoints change.
219static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
220{
221 if (role == SDL_IMMDevice_role) {
222 immcallbacks.default_audio_device_changed(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
223 }
224 return S_OK;
225}
226
227static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
228{
229 /* we ignore this; devices added here then progress to ACTIVE, if appropriate, in
230 OnDeviceStateChange, making that a better place to deal with device adds. More
231 importantly: the first time you plug in a USB audio device, this callback will
232 fire, but when you unplug it, it isn't removed (it's state changes to NOTPRESENT).
233 Plugging it back in won't fire this callback again. */
234 return S_OK;
235}
236
237static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
238{
239 return S_OK; // See notes in OnDeviceAdded handler about why we ignore this.
240}
241
242static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId, DWORD dwNewState)
243{
244 IMMDevice *device = NULL;
245
246 if (SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &device))) {
247 IMMEndpoint *endpoint = NULL;
248 if (SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (void **)&endpoint))) {
249 EDataFlow flow;
250 if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
251 const bool recording = (flow == eCapture);
252 if (dwNewState == DEVICE_STATE_ACTIVE) {
253 char *utf8dev;
254 WAVEFORMATEXTENSIBLE fmt;
255 GUID dsoundguid;
256 GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
257 if (utf8dev) {
258 SDL_IMMDevice_Add(recording, utf8dev, &fmt, pwstrDeviceId, &dsoundguid);
259 SDL_free(utf8dev);
260 }
261 } else {
262 immcallbacks.audio_device_disconnected(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
263 }
264 }
265 IMMEndpoint_Release(endpoint);
266 }
267 IMMDevice_Release(device);
268 }
269
270 return S_OK;
271}
272
273static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client, LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
274{
275 return S_OK; // we don't care about these.
276}
277
278static const IMMNotificationClientVtbl notification_client_vtbl = {
279 SDLMMNotificationClient_QueryInterface,
280 SDLMMNotificationClient_AddRef,
281 SDLMMNotificationClient_Release,
282 SDLMMNotificationClient_OnDeviceStateChanged,
283 SDLMMNotificationClient_OnDeviceAdded,
284 SDLMMNotificationClient_OnDeviceRemoved,
285 SDLMMNotificationClient_OnDefaultDeviceChanged,
286 SDLMMNotificationClient_OnPropertyValueChanged
287};
288
289static SDLMMNotificationClient notification_client = { &notification_client_vtbl, { 1 } };
290
291bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks)
292{
293 HRESULT ret;
294
295 // just skip the discussion with COM here.
296 if (!WIN_IsWindowsVistaOrGreater()) {
297 return SDL_SetError("IMMDevice support requires Windows Vista or later");
298 }
299
300 if (FAILED(WIN_CoInitialize())) {
301 return SDL_SetError("IMMDevice: CoInitialize() failed");
302 }
303
304 ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *)&enumerator);
305 if (FAILED(ret)) {
306 WIN_CoUninitialize();
307 return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret);
308 }
309
310 if (callbacks) {
311 SDL_copyp(&immcallbacks, callbacks);
312 } else {
313 SDL_zero(immcallbacks);
314 }
315
316 if (!immcallbacks.audio_device_disconnected) {
317 immcallbacks.audio_device_disconnected = SDL_AudioDeviceDisconnected;
318 }
319 if (!immcallbacks.default_audio_device_changed) {
320 immcallbacks.default_audio_device_changed = SDL_DefaultAudioDeviceChanged;
321 }
322
323 return true;
324}
325
326void SDL_IMMDevice_Quit(void)
327{
328 if (enumerator) {
329 IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
330 IMMDeviceEnumerator_Release(enumerator);
331 enumerator = NULL;
332 }
333
334 SDL_zero(immcallbacks);
335
336 WIN_CoUninitialize();
337}
338
339bool SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, bool recording)
340{
341 const Uint64 timeout = SDL_GetTicks() + 8000; // intel's audio drivers can fail for up to EIGHT SECONDS after a device is connected or we wake from sleep.
342
343 SDL_assert(device != NULL);
344 SDL_assert(immdevice != NULL);
345
346 LPCWSTR devid = SDL_IMMDevice_GetDevID(device);
347 SDL_assert(devid != NULL);
348
349 HRESULT ret;
350 while ((ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, immdevice)) == E_NOTFOUND) {
351 const Uint64 now = SDL_GetTicks();
352 if (timeout > now) {
353 const Uint64 ticksleft = timeout - now;
354 SDL_Delay((Uint32)SDL_min(ticksleft, 300)); // wait awhile and try again.
355 continue;
356 }
357 break;
358 }
359
360 if (!SUCCEEDED(ret)) {
361 return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
362 }
363 return true;
364}
365
366static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **default_device)
367{
368 /* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
369 ...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
370
371 IMMDeviceCollection *collection = NULL;
372 if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, recording ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
373 return;
374 }
375
376 UINT total = 0;
377 if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
378 IMMDeviceCollection_Release(collection);
379 return;
380 }
381
382 LPWSTR default_devid = NULL;
383 if (default_device) {
384 IMMDevice *default_immdevice = NULL;
385 const EDataFlow dataflow = recording ? eCapture : eRender;
386 if (SUCCEEDED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &default_immdevice))) {
387 LPWSTR devid = NULL;
388 if (SUCCEEDED(IMMDevice_GetId(default_immdevice, &devid))) {
389 default_devid = SDL_wcsdup(devid); // if this fails, oh well.
390 CoTaskMemFree(devid);
391 }
392 IMMDevice_Release(default_immdevice);
393 }
394 }
395
396 for (UINT i = 0; i < total; i++) {
397 IMMDevice *immdevice = NULL;
398 if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &immdevice))) {
399 LPWSTR devid = NULL;
400 if (SUCCEEDED(IMMDevice_GetId(immdevice, &devid))) {
401 char *devname = NULL;
402 WAVEFORMATEXTENSIBLE fmt;
403 GUID dsoundguid;
404 SDL_zero(fmt);
405 SDL_zero(dsoundguid);
406 GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
407 if (devname) {
408 SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(recording, devname, &fmt, devid, &dsoundguid);
409 if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
410 *default_device = sdldevice;
411 }
412 SDL_free(devname);
413 }
414 CoTaskMemFree(devid);
415 }
416 IMMDevice_Release(immdevice);
417 }
418 }
419
420 SDL_free(default_devid);
421
422 IMMDeviceCollection_Release(collection);
423}
424
425void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording)
426{
427 EnumerateEndpointsForFlow(false, default_playback);
428 EnumerateEndpointsForFlow(true, default_recording);
429
430 // if this fails, we just won't get hotplug events. Carry on anyhow.
431 IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
432}
433
434#endif // defined(SDL_PLATFORM_WINDOWS) && defined(HAVE_MMDEVICEAPI_H)
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.h b/contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.h
new file mode 100644
index 0000000..66fdf13
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.h
@@ -0,0 +1,45 @@
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
22#ifndef SDL_IMMDEVICE_H
23#define SDL_IMMDEVICE_H
24
25#define COBJMACROS
26#include <mmdeviceapi.h>
27#include <mmreg.h>
28
29struct SDL_AudioDevice; // defined in src/audio/SDL_sysaudio.h
30
31typedef struct SDL_IMMDevice_callbacks
32{
33 void (*audio_device_disconnected)(struct SDL_AudioDevice *device);
34 void (*default_audio_device_changed)(struct SDL_AudioDevice *new_default_device);
35} SDL_IMMDevice_callbacks;
36
37bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks);
38void SDL_IMMDevice_Quit(void);
39bool SDL_IMMDevice_Get(struct SDL_AudioDevice *device, IMMDevice **immdevice, bool recording);
40void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording);
41LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device);
42LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device);
43void SDL_IMMDevice_FreeDeviceHandle(struct SDL_AudioDevice *device);
44
45#endif // SDL_IMMDEVICE_H
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_windows.c b/contrib/SDL-3.2.8/src/core/windows/SDL_windows.c
new file mode 100644
index 0000000..286e8e6
--- /dev/null
+++ b/contrib/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)
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_windows.h b/contrib/SDL-3.2.8/src/core/windows/SDL_windows.h
new file mode 100644
index 0000000..b781b12
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_windows.h
@@ -0,0 +1,172 @@
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
22// This is an include file for windows.h with the SDL build settings
23
24#ifndef _INCLUDED_WINDOWS_H
25#define _INCLUDED_WINDOWS_H
26
27#ifdef SDL_PLATFORM_WIN32
28#ifndef WIN32_LEAN_AND_MEAN
29#define WIN32_LEAN_AND_MEAN 1
30#endif
31#ifndef STRICT
32#define STRICT 1
33#endif
34#ifndef UNICODE
35#define UNICODE 1
36#endif
37#undef WINVER
38#undef _WIN32_WINNT
39#if defined(SDL_VIDEO_RENDER_D3D12) || defined(HAVE_DXGI1_6_H)
40#define _WIN32_WINNT 0xA00 // For D3D12, 0xA00 is required
41#elif defined(HAVE_SHELLSCALINGAPI_H)
42#define _WIN32_WINNT 0x603 // For DPI support
43#else
44#define _WIN32_WINNT 0x501 // Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input
45#endif
46#define WINVER _WIN32_WINNT
47
48#elif defined(SDL_PLATFORM_WINGDK)
49#ifndef WIN32_LEAN_AND_MEAN
50#define WIN32_LEAN_AND_MEAN 1
51#endif
52#ifndef STRICT
53#define STRICT 1
54#endif
55#ifndef UNICODE
56#define UNICODE 1
57#endif
58#undef WINVER
59#undef _WIN32_WINNT
60#define _WIN32_WINNT 0xA00
61#define WINVER _WIN32_WINNT
62
63#elif defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
64#ifndef WIN32_LEAN_AND_MEAN
65#define WIN32_LEAN_AND_MEAN 1
66#endif
67#ifndef STRICT
68#define STRICT 1
69#endif
70#ifndef UNICODE
71#define UNICODE 1
72#endif
73#undef WINVER
74#undef _WIN32_WINNT
75#define _WIN32_WINNT 0xA00
76#define WINVER _WIN32_WINNT
77#endif
78
79// See https://github.com/libsdl-org/SDL/pull/7607
80// force_align_arg_pointer attribute requires gcc >= 4.2.x.
81#if defined(__clang__)
82#define HAVE_FORCE_ALIGN_ARG_POINTER
83#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
84#define HAVE_FORCE_ALIGN_ARG_POINTER
85#endif
86#if defined(__GNUC__) && defined(__i386__) && defined(HAVE_FORCE_ALIGN_ARG_POINTER)
87#define MINGW32_FORCEALIGN __attribute__((force_align_arg_pointer))
88#else
89#define MINGW32_FORCEALIGN
90#endif
91
92#include <windows.h>
93#include <basetyps.h> // for REFIID with broken mingw.org headers
94#include <mmreg.h>
95
96// Routines to convert from UTF8 to native Windows text
97#define WIN_StringToUTF8W(S) SDL_iconv_string("UTF-8", "UTF-16LE", (const char *)(S), (SDL_wcslen(S) + 1) * sizeof(WCHAR))
98#define WIN_UTF8ToStringW(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)(S), SDL_strlen(S) + 1)
99// !!! FIXME: UTF8ToString() can just be a SDL_strdup() here.
100#define WIN_StringToUTF8A(S) SDL_iconv_string("UTF-8", "ASCII", (const char *)(S), (SDL_strlen(S) + 1))
101#define WIN_UTF8ToStringA(S) SDL_iconv_string("ASCII", "UTF-8", (const char *)(S), SDL_strlen(S) + 1)
102#if UNICODE
103#define WIN_StringToUTF8 WIN_StringToUTF8W
104#define WIN_UTF8ToString WIN_UTF8ToStringW
105#define SDL_tcslen SDL_wcslen
106#define SDL_tcsstr SDL_wcsstr
107#else
108#define WIN_StringToUTF8 WIN_StringToUTF8A
109#define WIN_UTF8ToString WIN_UTF8ToStringA
110#define SDL_tcslen SDL_strlen
111#define SDL_tcsstr SDL_strstr
112#endif
113
114// Set up for C function definitions, even when using C++
115#ifdef __cplusplus
116extern "C" {
117#endif
118
119// Sets an error message based on a given HRESULT
120extern bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr);
121
122// Sets an error message based on GetLastError(). Always returns false.
123extern bool WIN_SetError(const char *prefix);
124
125// Load a function from combase.dll
126FARPROC WIN_LoadComBaseFunction(const char *name);
127
128// Wrap up the oddities of CoInitialize() into a common function.
129extern HRESULT WIN_CoInitialize(void);
130extern void WIN_CoUninitialize(void);
131
132// Wrap up the oddities of RoInitialize() into a common function.
133extern HRESULT WIN_RoInitialize(void);
134extern void WIN_RoUninitialize(void);
135
136// Returns true if we're running on Windows XP (any service pack). DOES NOT CHECK XP "OR GREATER"!
137extern BOOL WIN_IsWindowsXP(void);
138
139// Returns true if we're running on Windows Vista and newer
140extern BOOL WIN_IsWindowsVistaOrGreater(void);
141
142// Returns true if we're running on Windows 7 and newer
143extern BOOL WIN_IsWindows7OrGreater(void);
144
145// Returns true if we're running on Windows 8 and newer
146extern BOOL WIN_IsWindows8OrGreater(void);
147
148// You need to SDL_free() the result of this call.
149extern char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid);
150
151// Checks to see if two GUID are the same.
152extern BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b);
153extern BOOL WIN_IsEqualIID(REFIID a, REFIID b);
154
155// Convert between SDL_rect and RECT
156extern void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect);
157extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect);
158
159// Returns true if the rect is empty
160extern BOOL WIN_IsRectEmpty(const RECT *rect);
161
162extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
163
164// WideCharToMultiByte, but with some WinXP management.
165extern int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
166
167// Ends C function definitions when using C++
168#ifdef __cplusplus
169}
170#endif
171
172#endif // _INCLUDED_WINDOWS_H
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_xinput.c b/contrib/SDL-3.2.8/src/core/windows/SDL_xinput.c
new file mode 100644
index 0000000..ba5e4c1
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_xinput.c
@@ -0,0 +1,140 @@
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#include "SDL_xinput.h"
24
25// Set up for C function definitions, even when using C++
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30XInputGetState_t SDL_XInputGetState = NULL;
31XInputSetState_t SDL_XInputSetState = NULL;
32XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
33XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx = NULL;
34XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation = NULL;
35DWORD SDL_XInputVersion = 0;
36
37static HMODULE s_pXInputDLL = NULL;
38static int s_XInputDLLRefCount = 0;
39
40#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
41
42bool WIN_LoadXInputDLL(void)
43{
44 /* Getting handles to system dlls (via LoadLibrary and its variants) is not
45 * supported on Xbox, thus, pointers to XInput's functions can't be
46 * retrieved via GetProcAddress.
47 *
48 * When on Xbox, assume that XInput is already loaded, and directly map
49 * its XInput.h-declared functions to the SDL_XInput* set of function
50 * pointers.
51 */
52 SDL_XInputGetState = (XInputGetState_t)XInputGetState;
53 SDL_XInputSetState = (XInputSetState_t)XInputSetState;
54 SDL_XInputGetCapabilities = (XInputGetCapabilities_t)XInputGetCapabilities;
55 SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)XInputGetBatteryInformation;
56
57 // XInput 1.4 ships with Windows 8 and 8.1:
58 SDL_XInputVersion = (1 << 16) | 4;
59
60 return true;
61}
62
63void WIN_UnloadXInputDLL(void)
64{
65}
66
67#else // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
68
69bool WIN_LoadXInputDLL(void)
70{
71 DWORD version = 0;
72
73 if (s_pXInputDLL) {
74 SDL_assert(s_XInputDLLRefCount > 0);
75 s_XInputDLLRefCount++;
76 return true; // already loaded
77 }
78
79 /* NOTE: Don't load XinputUap.dll
80 * This is XInput emulation over Windows.Gaming.Input, and has all the
81 * limitations of that API (no devices at startup, no background input, etc.)
82 */
83 version = (1 << 16) | 4;
84 s_pXInputDLL = LoadLibrary(TEXT("XInput1_4.dll")); // 1.4 Ships with Windows 8.
85 if (!s_pXInputDLL) {
86 version = (1 << 16) | 3;
87 s_pXInputDLL = LoadLibrary(TEXT("XInput1_3.dll")); // 1.3 can be installed as a redistributable component.
88 }
89 if (!s_pXInputDLL) {
90 s_pXInputDLL = LoadLibrary(TEXT("bin\\XInput1_3.dll"));
91 }
92 if (!s_pXInputDLL) {
93 // "9.1.0" Ships with Vista and Win7, and is more limited than 1.3+ (e.g. XInputGetStateEx is not available.)
94 s_pXInputDLL = LoadLibrary(TEXT("XInput9_1_0.dll"));
95 }
96 if (!s_pXInputDLL) {
97 return false;
98 }
99
100 SDL_assert(s_XInputDLLRefCount == 0);
101 SDL_XInputVersion = version;
102 s_XInputDLLRefCount = 1;
103
104 // 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think...
105 SDL_XInputGetState = (XInputGetState_t)GetProcAddress(s_pXInputDLL, (LPCSTR)100);
106 if (!SDL_XInputGetState) {
107 SDL_XInputGetState = (XInputGetState_t)GetProcAddress(s_pXInputDLL, "XInputGetState");
108 }
109 SDL_XInputSetState = (XInputSetState_t)GetProcAddress(s_pXInputDLL, "XInputSetState");
110 SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress(s_pXInputDLL, "XInputGetCapabilities");
111 // 108 is the ordinal for _XInputGetCapabilitiesEx, which additionally returns VID/PID of the controller.
112 SDL_XInputGetCapabilitiesEx = (XInputGetCapabilitiesEx_t)GetProcAddress(s_pXInputDLL, (LPCSTR)108);
113 SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)GetProcAddress(s_pXInputDLL, "XInputGetBatteryInformation");
114 if (!SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities) {
115 WIN_UnloadXInputDLL();
116 return false;
117 }
118
119 return true;
120}
121
122void WIN_UnloadXInputDLL(void)
123{
124 if (s_pXInputDLL) {
125 SDL_assert(s_XInputDLLRefCount > 0);
126 if (--s_XInputDLLRefCount == 0) {
127 FreeLibrary(s_pXInputDLL);
128 s_pXInputDLL = NULL;
129 }
130 } else {
131 SDL_assert(s_XInputDLLRefCount == 0);
132 }
133}
134
135#endif
136
137// Ends C function definitions when using C++
138#ifdef __cplusplus
139}
140#endif
diff --git a/contrib/SDL-3.2.8/src/core/windows/SDL_xinput.h b/contrib/SDL-3.2.8/src/core/windows/SDL_xinput.h
new file mode 100644
index 0000000..d499cd5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/SDL_xinput.h
@@ -0,0 +1,276 @@
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#ifndef SDL_xinput_h_
24#define SDL_xinput_h_
25
26#include "SDL_windows.h"
27
28#ifdef HAVE_XINPUT_H
29#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
30// Xbox supports an XInput wrapper which is a C++-only header...
31#include <math.h> // Required to compile with recent MSVC...
32#include <XInputOnGameInput.h>
33using namespace XInputOnGameInput;
34#else
35#include <xinput.h>
36#endif
37#endif // HAVE_XINPUT_H
38
39#ifndef XUSER_MAX_COUNT
40#define XUSER_MAX_COUNT 4
41#endif
42#ifndef XUSER_INDEX_ANY
43#define XUSER_INDEX_ANY 0x000000FF
44#endif
45#ifndef XINPUT_CAPS_FFB_SUPPORTED
46#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
47#endif
48#ifndef XINPUT_CAPS_WIRELESS
49#define XINPUT_CAPS_WIRELESS 0x0002
50#endif
51
52#ifndef XINPUT_DEVSUBTYPE_UNKNOWN
53#define XINPUT_DEVSUBTYPE_UNKNOWN 0x00
54#endif
55#ifndef XINPUT_DEVSUBTYPE_GAMEPAD
56#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01
57#endif
58#ifndef XINPUT_DEVSUBTYPE_WHEEL
59#define XINPUT_DEVSUBTYPE_WHEEL 0x02
60#endif
61#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK
62#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
63#endif
64#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK
65#define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
66#endif
67#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD
68#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
69#endif
70#ifndef XINPUT_DEVSUBTYPE_GUITAR
71#define XINPUT_DEVSUBTYPE_GUITAR 0x06
72#endif
73#ifndef XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
74#define XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE 0x07
75#endif
76#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT
77#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
78#endif
79#ifndef XINPUT_DEVSUBTYPE_GUITAR_BASS
80#define XINPUT_DEVSUBTYPE_GUITAR_BASS 0x0B
81#endif
82#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD
83#define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13
84#endif
85
86#ifndef XINPUT_FLAG_GAMEPAD
87#define XINPUT_FLAG_GAMEPAD 0x01
88#endif
89
90#ifndef XINPUT_GAMEPAD_DPAD_UP
91#define XINPUT_GAMEPAD_DPAD_UP 0x0001
92#endif
93#ifndef XINPUT_GAMEPAD_DPAD_DOWN
94#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
95#endif
96#ifndef XINPUT_GAMEPAD_DPAD_LEFT
97#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
98#endif
99#ifndef XINPUT_GAMEPAD_DPAD_RIGHT
100#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
101#endif
102#ifndef XINPUT_GAMEPAD_START
103#define XINPUT_GAMEPAD_START 0x0010
104#endif
105#ifndef XINPUT_GAMEPAD_BACK
106#define XINPUT_GAMEPAD_BACK 0x0020
107#endif
108#ifndef XINPUT_GAMEPAD_LEFT_THUMB
109#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
110#endif
111#ifndef XINPUT_GAMEPAD_RIGHT_THUMB
112#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
113#endif
114#ifndef XINPUT_GAMEPAD_LEFT_SHOULDER
115#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
116#endif
117#ifndef XINPUT_GAMEPAD_RIGHT_SHOULDER
118#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
119#endif
120#ifndef XINPUT_GAMEPAD_A
121#define XINPUT_GAMEPAD_A 0x1000
122#endif
123#ifndef XINPUT_GAMEPAD_B
124#define XINPUT_GAMEPAD_B 0x2000
125#endif
126#ifndef XINPUT_GAMEPAD_X
127#define XINPUT_GAMEPAD_X 0x4000
128#endif
129#ifndef XINPUT_GAMEPAD_Y
130#define XINPUT_GAMEPAD_Y 0x8000
131#endif
132
133#ifndef XINPUT_GAMEPAD_GUIDE
134#define XINPUT_GAMEPAD_GUIDE 0x0400
135#endif
136
137#ifndef BATTERY_DEVTYPE_GAMEPAD
138#define BATTERY_DEVTYPE_GAMEPAD 0x00
139#endif
140
141#ifndef BATTERY_TYPE_DISCONNECTED
142#define BATTERY_TYPE_DISCONNECTED 0x00
143#endif
144#ifndef BATTERY_TYPE_WIRED
145#define BATTERY_TYPE_WIRED 0x01
146#endif
147#ifndef BATTERY_TYPE_UNKNOWN
148#define BATTERY_TYPE_UNKNOWN 0xFF
149#endif
150#ifndef BATTERY_LEVEL_EMPTY
151#define BATTERY_LEVEL_EMPTY 0x00
152#endif
153#ifndef BATTERY_LEVEL_LOW
154#define BATTERY_LEVEL_LOW 0x01
155#endif
156#ifndef BATTERY_LEVEL_MEDIUM
157#define BATTERY_LEVEL_MEDIUM 0x02
158#endif
159#ifndef BATTERY_LEVEL_FULL
160#define BATTERY_LEVEL_FULL 0x03
161#endif
162
163// Set up for C function definitions, even when using C++
164#ifdef __cplusplus
165extern "C" {
166#endif
167
168// typedef's for XInput structs we use
169
170
171// This is the same as XINPUT_BATTERY_INFORMATION, but always defined instead of just if WIN32_WINNT >= _WIN32_WINNT_WIN8
172typedef struct
173{
174 BYTE BatteryType;
175 BYTE BatteryLevel;
176} XINPUT_BATTERY_INFORMATION_EX;
177
178#ifndef HAVE_XINPUT_H
179
180typedef struct
181{
182 WORD wButtons;
183 BYTE bLeftTrigger;
184 BYTE bRightTrigger;
185 SHORT sThumbLX;
186 SHORT sThumbLY;
187 SHORT sThumbRX;
188 SHORT sThumbRY;
189} XINPUT_GAMEPAD;
190
191typedef struct
192{
193 DWORD dwPacketNumber;
194 XINPUT_GAMEPAD Gamepad;
195} XINPUT_STATE;
196
197typedef struct
198{
199 WORD wLeftMotorSpeed;
200 WORD wRightMotorSpeed;
201} XINPUT_VIBRATION;
202
203typedef struct
204{
205 BYTE Type;
206 BYTE SubType;
207 WORD Flags;
208 XINPUT_GAMEPAD Gamepad;
209 XINPUT_VIBRATION Vibration;
210} XINPUT_CAPABILITIES;
211
212#endif // HAVE_XINPUT_H
213
214// This struct is not defined in XInput headers.
215typedef struct
216{
217 XINPUT_CAPABILITIES Capabilities;
218 WORD VendorId;
219 WORD ProductId;
220 WORD ProductVersion;
221 WORD unk1;
222 DWORD unk2;
223} SDL_XINPUT_CAPABILITIES_EX;
224
225// Forward decl's for XInput API's we load dynamically and use if available
226typedef DWORD(WINAPI *XInputGetState_t)(
227 DWORD dwUserIndex, // [in] Index of the gamer associated with the device
228 XINPUT_STATE *pState // [out] Receives the current state
229);
230
231typedef DWORD(WINAPI *XInputSetState_t)(
232 DWORD dwUserIndex, // [in] Index of the gamer associated with the device
233 XINPUT_VIBRATION *pVibration // [in, out] The vibration information to send to the controller
234);
235
236typedef DWORD(WINAPI *XInputGetCapabilities_t)(
237 DWORD dwUserIndex, // [in] Index of the gamer associated with the device
238 DWORD dwFlags, // [in] Input flags that identify the device type
239 XINPUT_CAPABILITIES *pCapabilities // [out] Receives the capabilities
240);
241
242// Only available in XInput 1.4 that is shipped with Windows 8 and newer.
243typedef DWORD(WINAPI *XInputGetCapabilitiesEx_t)(
244 DWORD dwReserved, // [in] Must be 1
245 DWORD dwUserIndex, // [in] Index of the gamer associated with the device
246 DWORD dwFlags, // [in] Input flags that identify the device type
247 SDL_XINPUT_CAPABILITIES_EX *pCapabilitiesEx // [out] Receives the capabilities
248);
249
250typedef DWORD(WINAPI *XInputGetBatteryInformation_t)(
251 DWORD dwUserIndex,
252 BYTE devType,
253 XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation);
254
255extern bool WIN_LoadXInputDLL(void);
256extern void WIN_UnloadXInputDLL(void);
257
258extern XInputGetState_t SDL_XInputGetState;
259extern XInputSetState_t SDL_XInputSetState;
260extern XInputGetCapabilities_t SDL_XInputGetCapabilities;
261extern XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx;
262extern XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation;
263extern DWORD SDL_XInputVersion; // ((major << 16) & 0xFF00) | (minor & 0xFF)
264
265// Ends C function definitions when using C++
266#ifdef __cplusplus
267}
268#endif
269
270#define XINPUTGETSTATE SDL_XInputGetState
271#define XINPUTSETSTATE SDL_XInputSetState
272#define XINPUTGETCAPABILITIES SDL_XInputGetCapabilities
273#define XINPUTGETCAPABILITIESEX SDL_XInputGetCapabilitiesEx
274#define XINPUTGETBATTERYINFORMATION SDL_XInputGetBatteryInformation
275
276#endif // SDL_xinput_h_
diff --git a/contrib/SDL-3.2.8/src/core/windows/pch.c b/contrib/SDL-3.2.8/src/core/windows/pch.c
new file mode 100644
index 0000000..4b0c6f8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/pch.c
@@ -0,0 +1,21 @@
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"
diff --git a/contrib/SDL-3.2.8/src/core/windows/pch_cpp.cpp b/contrib/SDL-3.2.8/src/core/windows/pch_cpp.cpp
new file mode 100644
index 0000000..4b0c6f8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/pch_cpp.cpp
@@ -0,0 +1,21 @@
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"
diff --git a/contrib/SDL-3.2.8/src/core/windows/version.rc b/contrib/SDL-3.2.8/src/core/windows/version.rc
new file mode 100644
index 0000000..050f1b8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/windows/version.rc
@@ -0,0 +1,38 @@
1
2#include "winresrc.h"
3
4LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
5
6/////////////////////////////////////////////////////////////////////////////
7//
8// Version
9//
10
11VS_VERSION_INFO VERSIONINFO
12 FILEVERSION 3,2,8,0
13 PRODUCTVERSION 3,2,8,0
14 FILEFLAGSMASK 0x3fL
15 FILEFLAGS 0x0L
16 FILEOS 0x40004L
17 FILETYPE 0x2L
18 FILESUBTYPE 0x0L
19BEGIN
20 BLOCK "StringFileInfo"
21 BEGIN
22 BLOCK "040904b0"
23 BEGIN
24 VALUE "CompanyName", "\0"
25 VALUE "FileDescription", "SDL\0"
26 VALUE "FileVersion", "3, 2, 8, 0\0"
27 VALUE "InternalName", "SDL\0"
28 VALUE "LegalCopyright", "Copyright (C) 2025 Sam Lantinga\0"
29 VALUE "OriginalFilename", "SDL3.dll\0"
30 VALUE "ProductName", "Simple DirectMedia Layer\0"
31 VALUE "ProductVersion", "3, 2, 8, 0\0"
32 END
33 END
34 BLOCK "VarFileInfo"
35 BEGIN
36 VALUE "Translation", 0x409, 1200
37 END
38END