diff options
Diffstat (limited to 'contrib/SDL-3.2.8/src/core/windows')
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_directx.h | 112 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.c | 98 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_gameinput.h | 36 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_hid.c | 254 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_hid.h | 215 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.c | 434 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_immdevice.h | 45 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_windows.c | 375 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_windows.h | 172 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_xinput.c | 140 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/SDL_xinput.h | 276 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/pch.c | 21 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/pch_cpp.cpp | 21 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/windows/version.rc | 38 |
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 | ||
| 106 | typedef 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} | ||
| 31 | DEFINE_GUID(SDL_IID_GameInput, 0x11BE2A7E, 0x4254, 0x445A, 0x9C, 0x09, 0xFF, 0xC4, 0x0F, 0x00, 0x69, 0x18); | ||
| 32 | #endif | ||
| 33 | |||
| 34 | static SDL_SharedObject *g_hGameInputDLL; | ||
| 35 | static IGameInput *g_pGameInput; | ||
| 36 | static int g_nGameInputRefCount; | ||
| 37 | |||
| 38 | bool 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 | |||
| 81 | void 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 | |||
| 31 | extern bool SDL_InitGameInput(IGameInput **ppGameInput); | ||
| 32 | extern 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 | |||
| 25 | HidD_GetAttributes_t SDL_HidD_GetAttributes; | ||
| 26 | HidD_GetString_t SDL_HidD_GetManufacturerString; | ||
| 27 | HidD_GetString_t SDL_HidD_GetProductString; | ||
| 28 | HidP_GetCaps_t SDL_HidP_GetCaps; | ||
| 29 | HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps; | ||
| 30 | HidP_GetValueCaps_t SDL_HidP_GetValueCaps; | ||
| 31 | HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength; | ||
| 32 | HidP_GetData_t SDL_HidP_GetData; | ||
| 33 | |||
| 34 | static HMODULE s_pHIDDLL = 0; | ||
| 35 | static int s_HIDDLLRefCount = 0; | ||
| 36 | |||
| 37 | |||
| 38 | bool 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 | |||
| 72 | void 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 | |||
| 91 | DECLARE_HANDLE(HCMNOTIFICATION); | ||
| 92 | typedef HCMNOTIFICATION *PHCMNOTIFICATION; | ||
| 93 | |||
| 94 | typedef 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 | |||
| 102 | typedef 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 | |||
| 125 | typedef 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 | |||
| 140 | typedef 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 | |||
| 165 | typedef DWORD (CALLBACK *PCM_NOTIFY_CALLBACK)(HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize); | ||
| 166 | |||
| 167 | typedef DWORD (WINAPI *CM_Register_NotificationFunc)(PCM_NOTIFY_FILTER pFilter, PVOID pContext, PCM_NOTIFY_CALLBACK pCallback, PHCMNOTIFICATION pNotifyContext); | ||
| 168 | typedef DWORD (WINAPI *CM_Unregister_NotificationFunc)(HCMNOTIFICATION NotifyContext); | ||
| 169 | |||
| 170 | static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; | ||
| 171 | |||
| 172 | static int s_DeviceNotificationsRequested; | ||
| 173 | static HMODULE cfgmgr32_lib_handle; | ||
| 174 | static CM_Register_NotificationFunc CM_Register_Notification; | ||
| 175 | static CM_Unregister_NotificationFunc CM_Unregister_Notification; | ||
| 176 | static HCMNOTIFICATION s_DeviceNotificationFuncHandle; | ||
| 177 | static Uint64 s_LastDeviceNotification = 1; | ||
| 178 | |||
| 179 | static 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 | |||
| 188 | void 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(¬ify_filter, NULL, SDL_DeviceNotificationFunc, &s_DeviceNotificationFuncHandle) == CR_SUCCESS) { | ||
| 207 | return; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | // FIXME: Should we log errors? | ||
| 213 | } | ||
| 214 | |||
| 215 | Uint64 WIN_GetLastDeviceNotification(void) | ||
| 216 | { | ||
| 217 | return s_LastDeviceNotification; | ||
| 218 | } | ||
| 219 | |||
| 220 | void 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 | |||
| 241 | void WIN_InitDeviceNotification(void) | ||
| 242 | { | ||
| 243 | } | ||
| 244 | |||
| 245 | Uint64 WIN_GetLastDeviceNotification( void ) | ||
| 246 | { | ||
| 247 | return 0; | ||
| 248 | } | ||
| 249 | |||
| 250 | void 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 | |||
| 28 | typedef LONG NTSTATUS; | ||
| 29 | typedef USHORT USAGE; | ||
| 30 | typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA; | ||
| 31 | |||
| 32 | typedef struct _HIDD_ATTRIBUTES | ||
| 33 | { | ||
| 34 | ULONG Size; | ||
| 35 | USHORT VendorID; | ||
| 36 | USHORT ProductID; | ||
| 37 | USHORT VersionNumber; | ||
| 38 | } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; | ||
| 39 | |||
| 40 | typedef enum | ||
| 41 | { | ||
| 42 | HidP_Input = 0, | ||
| 43 | HidP_Output = 1, | ||
| 44 | HidP_Feature = 2 | ||
| 45 | } HIDP_REPORT_TYPE; | ||
| 46 | |||
| 47 | typedef 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 | |||
| 88 | typedef 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 | |||
| 139 | typedef 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 | |||
| 159 | typedef 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 | |||
| 191 | extern bool WIN_LoadHIDDLL(void); | ||
| 192 | extern void WIN_UnloadHIDDLL(void); | ||
| 193 | |||
| 194 | typedef BOOLEAN (WINAPI *HidD_GetAttributes_t)(HANDLE HidDeviceObject, PHIDD_ATTRIBUTES Attributes); | ||
| 195 | typedef BOOLEAN (WINAPI *HidD_GetString_t)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength); | ||
| 196 | typedef NTSTATUS (WINAPI *HidP_GetCaps_t)(PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities); | ||
| 197 | typedef NTSTATUS (WINAPI *HidP_GetButtonCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData); | ||
| 198 | typedef NTSTATUS (WINAPI *HidP_GetValueCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData); | ||
| 199 | typedef ULONG (WINAPI *HidP_MaxDataListLength_t)(HIDP_REPORT_TYPE ReportType, PHIDP_PREPARSED_DATA PreparsedData); | ||
| 200 | typedef NTSTATUS (WINAPI *HidP_GetData_t)(HIDP_REPORT_TYPE ReportType, PHIDP_DATA DataList, PULONG DataLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength); | ||
| 201 | |||
| 202 | extern HidD_GetAttributes_t SDL_HidD_GetAttributes; | ||
| 203 | extern HidD_GetString_t SDL_HidD_GetManufacturerString; | ||
| 204 | extern HidD_GetString_t SDL_HidD_GetProductString; | ||
| 205 | extern HidP_GetCaps_t SDL_HidP_GetCaps; | ||
| 206 | extern HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps; | ||
| 207 | extern HidP_GetValueCaps_t SDL_HidP_GetValueCaps; | ||
| 208 | extern HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength; | ||
| 209 | extern HidP_GetData_t SDL_HidP_GetData; | ||
| 210 | |||
| 211 | void WIN_InitDeviceNotification(void); | ||
| 212 | Uint64 WIN_GetLastDeviceNotification(void); | ||
| 213 | void 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 | |||
| 30 | typedef struct SDL_IMMDevice_HandleData | ||
| 31 | { | ||
| 32 | LPWSTR immdevice_id; | ||
| 33 | GUID directsound_guid; | ||
| 34 | } SDL_IMMDevice_HandleData; | ||
| 35 | |||
| 36 | static 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. | ||
| 39 | static IMMDeviceEnumerator *enumerator = NULL; | ||
| 40 | static 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 | ||
| 50 | static const CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,{ 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } }; | ||
| 51 | static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,{ 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } }; | ||
| 52 | static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{ 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } }; | ||
| 53 | static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } }; | ||
| 54 | static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 }; | ||
| 55 | static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 }; | ||
| 56 | static 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 | |||
| 59 | static 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 | |||
| 71 | static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid) | ||
| 72 | { | ||
| 73 | return SDL_FindPhysicalAudioDeviceByCallback(FindByDevIDCallback, (void *) devid); | ||
| 74 | } | ||
| 75 | |||
| 76 | LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device) | ||
| 77 | { | ||
| 78 | return (device && device->handle) ? &(((SDL_IMMDevice_HandleData *) device->handle)->directsound_guid) : NULL; | ||
| 79 | } | ||
| 80 | |||
| 81 | LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device) | ||
| 82 | { | ||
| 83 | return (device && device->handle) ? ((const SDL_IMMDevice_HandleData *) device->handle)->immdevice_id : NULL; | ||
| 84 | } | ||
| 85 | |||
| 86 | static 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 | |||
| 113 | void 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 | |||
| 123 | static 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 | |||
| 182 | typedef struct SDLMMNotificationClient | ||
| 183 | { | ||
| 184 | const IMMNotificationClientVtbl *lpVtbl; | ||
| 185 | SDL_AtomicInt refcount; | ||
| 186 | } SDLMMNotificationClient; | ||
| 187 | |||
| 188 | static 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 | |||
| 200 | static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient) | ||
| 201 | { | ||
| 202 | SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient; | ||
| 203 | return (ULONG)(SDL_AtomicIncRef(&client->refcount) + 1); | ||
| 204 | } | ||
| 205 | |||
| 206 | static 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. | ||
| 219 | static 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 | |||
| 227 | static 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 | |||
| 237 | static 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 | |||
| 242 | static 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 | |||
| 273 | static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client, LPCWSTR pwstrDeviceId, const PROPERTYKEY key) | ||
| 274 | { | ||
| 275 | return S_OK; // we don't care about these. | ||
| 276 | } | ||
| 277 | |||
| 278 | static 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 | |||
| 289 | static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 } }; | ||
| 290 | |||
| 291 | bool 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 | |||
| 326 | void SDL_IMMDevice_Quit(void) | ||
| 327 | { | ||
| 328 | if (enumerator) { | ||
| 329 | IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client); | ||
| 330 | IMMDeviceEnumerator_Release(enumerator); | ||
| 331 | enumerator = NULL; | ||
| 332 | } | ||
| 333 | |||
| 334 | SDL_zero(immcallbacks); | ||
| 335 | |||
| 336 | WIN_CoUninitialize(); | ||
| 337 | } | ||
| 338 | |||
| 339 | bool 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 | |||
| 366 | static 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 | |||
| 425 | void 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 *)¬ification_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 | |||
| 29 | struct SDL_AudioDevice; // defined in src/audio/SDL_sysaudio.h | ||
| 30 | |||
| 31 | typedef 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 | |||
| 37 | bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks); | ||
| 38 | void SDL_IMMDevice_Quit(void); | ||
| 39 | bool SDL_IMMDevice_Get(struct SDL_AudioDevice *device, IMMDevice **immdevice, bool recording); | ||
| 40 | void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording); | ||
| 41 | LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device); | ||
| 42 | LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device); | ||
| 43 | void 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 | ||
| 31 | typedef enum RO_INIT_TYPE | ||
| 32 | { | ||
| 33 | RO_INIT_SINGLETHREADED = 0, | ||
| 34 | RO_INIT_MULTITHREADED = 1 | ||
| 35 | } RO_INIT_TYPE; | ||
| 36 | #endif | ||
| 37 | |||
| 38 | #ifndef _WIN32_WINNT_VISTA | ||
| 39 | #define _WIN32_WINNT_VISTA 0x0600 | ||
| 40 | #endif | ||
| 41 | #ifndef _WIN32_WINNT_WIN7 | ||
| 42 | #define _WIN32_WINNT_WIN7 0x0601 | ||
| 43 | #endif | ||
| 44 | #ifndef _WIN32_WINNT_WIN8 | ||
| 45 | #define _WIN32_WINNT_WIN8 0x0602 | ||
| 46 | #endif | ||
| 47 | |||
| 48 | #ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 | ||
| 49 | #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 | ||
| 50 | #endif | ||
| 51 | |||
| 52 | #ifndef WC_ERR_INVALID_CHARS | ||
| 53 | #define WC_ERR_INVALID_CHARS 0x00000080 | ||
| 54 | #endif | ||
| 55 | |||
| 56 | // Sets an error message based on an HRESULT | ||
| 57 | bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr) | ||
| 58 | { | ||
| 59 | TCHAR buffer[1024]; | ||
| 60 | char *message; | ||
| 61 | TCHAR *p = buffer; | ||
| 62 | DWORD c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, | ||
| 63 | buffer, SDL_arraysize(buffer), NULL); | ||
| 64 | buffer[c] = 0; | ||
| 65 | // kill CR/LF that FormatMessage() sticks at the end | ||
| 66 | while (*p) { | ||
| 67 | if (*p == '\r') { | ||
| 68 | *p = 0; | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | ++p; | ||
| 72 | } | ||
| 73 | message = WIN_StringToUTF8(buffer); | ||
| 74 | SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ": " : "", message); | ||
| 75 | SDL_free(message); | ||
| 76 | return false; | ||
| 77 | } | ||
| 78 | |||
| 79 | // Sets an error message based on GetLastError() | ||
| 80 | bool WIN_SetError(const char *prefix) | ||
| 81 | { | ||
| 82 | return WIN_SetErrorFromHRESULT(prefix, GetLastError()); | ||
| 83 | } | ||
| 84 | |||
| 85 | HRESULT | ||
| 86 | WIN_CoInitialize(void) | ||
| 87 | { | ||
| 88 | /* SDL handles any threading model, so initialize with the default, which | ||
| 89 | is compatible with OLE and if that doesn't work, try multi-threaded mode. | ||
| 90 | |||
| 91 | If you need multi-threaded mode, call CoInitializeEx() before SDL_Init() | ||
| 92 | */ | ||
| 93 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 94 | // On Xbox, there's no need to call CoInitializeEx (and it's not implemented) | ||
| 95 | return S_OK; | ||
| 96 | #else | ||
| 97 | HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); | ||
| 98 | if (hr == RPC_E_CHANGED_MODE) { | ||
| 99 | hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 100 | } | ||
| 101 | |||
| 102 | // S_FALSE means success, but someone else already initialized. | ||
| 103 | // You still need to call CoUninitialize in this case! | ||
| 104 | if (hr == S_FALSE) { | ||
| 105 | return S_OK; | ||
| 106 | } | ||
| 107 | |||
| 108 | return hr; | ||
| 109 | #endif | ||
| 110 | } | ||
| 111 | |||
| 112 | void WIN_CoUninitialize(void) | ||
| 113 | { | ||
| 114 | CoUninitialize(); | ||
| 115 | } | ||
| 116 | |||
| 117 | FARPROC WIN_LoadComBaseFunction(const char *name) | ||
| 118 | { | ||
| 119 | static bool s_bLoaded; | ||
| 120 | static HMODULE s_hComBase; | ||
| 121 | |||
| 122 | if (!s_bLoaded) { | ||
| 123 | s_hComBase = LoadLibraryEx(TEXT("combase.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); | ||
| 124 | s_bLoaded = true; | ||
| 125 | } | ||
| 126 | if (s_hComBase) { | ||
| 127 | return GetProcAddress(s_hComBase, name); | ||
| 128 | } else { | ||
| 129 | return NULL; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | HRESULT | ||
| 134 | WIN_RoInitialize(void) | ||
| 135 | { | ||
| 136 | typedef HRESULT(WINAPI * RoInitialize_t)(RO_INIT_TYPE initType); | ||
| 137 | RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize"); | ||
| 138 | if (RoInitializeFunc) { | ||
| 139 | // RO_INIT_SINGLETHREADED is equivalent to COINIT_APARTMENTTHREADED | ||
| 140 | HRESULT hr = RoInitializeFunc(RO_INIT_SINGLETHREADED); | ||
| 141 | if (hr == RPC_E_CHANGED_MODE) { | ||
| 142 | hr = RoInitializeFunc(RO_INIT_MULTITHREADED); | ||
| 143 | } | ||
| 144 | |||
| 145 | // S_FALSE means success, but someone else already initialized. | ||
| 146 | // You still need to call RoUninitialize in this case! | ||
| 147 | if (hr == S_FALSE) { | ||
| 148 | return S_OK; | ||
| 149 | } | ||
| 150 | |||
| 151 | return hr; | ||
| 152 | } else { | ||
| 153 | return E_NOINTERFACE; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | void WIN_RoUninitialize(void) | ||
| 158 | { | ||
| 159 | typedef void(WINAPI * RoUninitialize_t)(void); | ||
| 160 | RoUninitialize_t RoUninitializeFunc = (RoUninitialize_t)WIN_LoadComBaseFunction("RoUninitialize"); | ||
| 161 | if (RoUninitializeFunc) { | ||
| 162 | RoUninitializeFunc(); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 167 | static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) | ||
| 168 | { | ||
| 169 | OSVERSIONINFOEXW osvi; | ||
| 170 | DWORDLONG const dwlConditionMask = VerSetConditionMask( | ||
| 171 | VerSetConditionMask( | ||
| 172 | VerSetConditionMask( | ||
| 173 | 0, VER_MAJORVERSION, VER_GREATER_EQUAL), | ||
| 174 | VER_MINORVERSION, VER_GREATER_EQUAL), | ||
| 175 | VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); | ||
| 176 | |||
| 177 | SDL_zero(osvi); | ||
| 178 | osvi.dwOSVersionInfoSize = sizeof(osvi); | ||
| 179 | osvi.dwMajorVersion = wMajorVersion; | ||
| 180 | osvi.dwMinorVersion = wMinorVersion; | ||
| 181 | osvi.wServicePackMajor = wServicePackMajor; | ||
| 182 | |||
| 183 | return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; | ||
| 184 | } | ||
| 185 | #endif | ||
| 186 | |||
| 187 | // apply some static variables so we only call into the Win32 API once per process for each check. | ||
| 188 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 189 | #define CHECKWINVER(notdesktop_platform_result, test) return (notdesktop_platform_result); | ||
| 190 | #else | ||
| 191 | #define CHECKWINVER(notdesktop_platform_result, test) \ | ||
| 192 | static bool checked = false; \ | ||
| 193 | static BOOL result = FALSE; \ | ||
| 194 | if (!checked) { \ | ||
| 195 | result = (test); \ | ||
| 196 | checked = true; \ | ||
| 197 | } \ | ||
| 198 | return result; | ||
| 199 | #endif | ||
| 200 | |||
| 201 | // this is the oldest thing we run on (and we may lose support for this in SDL3 at any time!), | ||
| 202 | // so there's no "OrGreater" as that would always be TRUE. The other functions are here to | ||
| 203 | // ask "can we support a specific feature?" but this function is here to ask "do we need to do | ||
| 204 | // something different for an OS version we probably should abandon?" :) | ||
| 205 | BOOL WIN_IsWindowsXP(void) | ||
| 206 | { | ||
| 207 | CHECKWINVER(FALSE, !WIN_IsWindowsVistaOrGreater() && IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0)); | ||
| 208 | } | ||
| 209 | |||
| 210 | BOOL WIN_IsWindowsVistaOrGreater(void) | ||
| 211 | { | ||
| 212 | CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0)); | ||
| 213 | } | ||
| 214 | |||
| 215 | BOOL WIN_IsWindows7OrGreater(void) | ||
| 216 | { | ||
| 217 | CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0)); | ||
| 218 | } | ||
| 219 | |||
| 220 | BOOL WIN_IsWindows8OrGreater(void) | ||
| 221 | { | ||
| 222 | CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0)); | ||
| 223 | } | ||
| 224 | |||
| 225 | #undef CHECKWINVER | ||
| 226 | |||
| 227 | |||
| 228 | /* | ||
| 229 | WAVExxxCAPS gives you 31 bytes for the device name, and just truncates if it's | ||
| 230 | longer. However, since WinXP, you can use the WAVExxxCAPS2 structure, which | ||
| 231 | will give you a name GUID. The full name is in the Windows Registry under | ||
| 232 | that GUID, located here: HKLM\System\CurrentControlSet\Control\MediaCategories | ||
| 233 | |||
| 234 | Note that drivers can report GUID_NULL for the name GUID, in which case, | ||
| 235 | Windows makes a best effort to fill in those 31 bytes in the usual place. | ||
| 236 | This info summarized from MSDN: | ||
| 237 | |||
| 238 | http://web.archive.org/web/20131027093034/http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382(v=vs.85).aspx | ||
| 239 | |||
| 240 | Always look this up in the registry if possible, because the strings are | ||
| 241 | different! At least on Win10, I see "Yeti Stereo Microphone" in the | ||
| 242 | Registry, and a unhelpful "Microphone(Yeti Stereo Microph" in winmm. Sigh. | ||
| 243 | |||
| 244 | (Also, DirectSound shouldn't be limited to 32 chars, but its device enum | ||
| 245 | has the same problem.) | ||
| 246 | |||
| 247 | WASAPI doesn't need this. This is just for DirectSound/WinMM. | ||
| 248 | */ | ||
| 249 | char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid) | ||
| 250 | { | ||
| 251 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 252 | return WIN_StringToUTF8W(name); // No registry access on Xbox, go with what we've got. | ||
| 253 | #else | ||
| 254 | static const GUID nullguid = { 0 }; | ||
| 255 | const unsigned char *ptr; | ||
| 256 | char keystr[128]; | ||
| 257 | WCHAR *strw = NULL; | ||
| 258 | bool rc; | ||
| 259 | HKEY hkey; | ||
| 260 | DWORD len = 0; | ||
| 261 | char *result = NULL; | ||
| 262 | |||
| 263 | if (WIN_IsEqualGUID(guid, &nullguid)) { | ||
| 264 | return WIN_StringToUTF8(name); // No GUID, go with what we've got. | ||
| 265 | } | ||
| 266 | |||
| 267 | ptr = (const unsigned char *)guid; | ||
| 268 | (void)SDL_snprintf(keystr, sizeof(keystr), | ||
| 269 | "System\\CurrentControlSet\\Control\\MediaCategories\\{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", | ||
| 270 | ptr[3], ptr[2], ptr[1], ptr[0], ptr[5], ptr[4], ptr[7], ptr[6], | ||
| 271 | ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]); | ||
| 272 | |||
| 273 | strw = WIN_UTF8ToString(keystr); | ||
| 274 | rc = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, strw, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS); | ||
| 275 | SDL_free(strw); | ||
| 276 | if (!rc) { | ||
| 277 | return WIN_StringToUTF8(name); // oh well. | ||
| 278 | } | ||
| 279 | |||
| 280 | rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, NULL, &len) == ERROR_SUCCESS); | ||
| 281 | if (!rc) { | ||
| 282 | RegCloseKey(hkey); | ||
| 283 | return WIN_StringToUTF8(name); // oh well. | ||
| 284 | } | ||
| 285 | |||
| 286 | strw = (WCHAR *)SDL_malloc(len + sizeof(WCHAR)); | ||
| 287 | if (!strw) { | ||
| 288 | RegCloseKey(hkey); | ||
| 289 | return WIN_StringToUTF8(name); // oh well. | ||
| 290 | } | ||
| 291 | |||
| 292 | rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, (LPBYTE)strw, &len) == ERROR_SUCCESS); | ||
| 293 | RegCloseKey(hkey); | ||
| 294 | if (!rc) { | ||
| 295 | SDL_free(strw); | ||
| 296 | return WIN_StringToUTF8(name); // oh well. | ||
| 297 | } | ||
| 298 | |||
| 299 | strw[len / 2] = 0; // make sure it's null-terminated. | ||
| 300 | |||
| 301 | result = WIN_StringToUTF8(strw); | ||
| 302 | SDL_free(strw); | ||
| 303 | return result ? result : WIN_StringToUTF8(name); | ||
| 304 | #endif | ||
| 305 | } | ||
| 306 | |||
| 307 | BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b) | ||
| 308 | { | ||
| 309 | return (SDL_memcmp(a, b, sizeof(*a)) == 0); | ||
| 310 | } | ||
| 311 | |||
| 312 | BOOL WIN_IsEqualIID(REFIID a, REFIID b) | ||
| 313 | { | ||
| 314 | return (SDL_memcmp(a, b, sizeof(*a)) == 0); | ||
| 315 | } | ||
| 316 | |||
| 317 | void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect) | ||
| 318 | { | ||
| 319 | sdlrect->x = winrect->left; | ||
| 320 | sdlrect->w = (winrect->right - winrect->left) + 1; | ||
| 321 | sdlrect->y = winrect->top; | ||
| 322 | sdlrect->h = (winrect->bottom - winrect->top) + 1; | ||
| 323 | } | ||
| 324 | |||
| 325 | void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect) | ||
| 326 | { | ||
| 327 | winrect->left = sdlrect->x; | ||
| 328 | winrect->right = sdlrect->x + sdlrect->w - 1; | ||
| 329 | winrect->top = sdlrect->y; | ||
| 330 | winrect->bottom = sdlrect->y + sdlrect->h - 1; | ||
| 331 | } | ||
| 332 | |||
| 333 | BOOL WIN_IsRectEmpty(const RECT *rect) | ||
| 334 | { | ||
| 335 | // Calculating this manually because Xbox does not support Win32 IsRectEmpty. | ||
| 336 | return (rect->right <= rect->left) || (rect->bottom <= rect->top); | ||
| 337 | } | ||
| 338 | |||
| 339 | // Some GUIDs we need to know without linking to libraries that aren't available before Vista. | ||
| 340 | /* *INDENT-OFF* */ // clang-format off | ||
| 341 | static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; | ||
| 342 | static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; | ||
| 343 | /* *INDENT-ON* */ // clang-format on | ||
| 344 | |||
| 345 | SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat) | ||
| 346 | { | ||
| 347 | if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) { | ||
| 348 | return SDL_AUDIO_F32; | ||
| 349 | } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) { | ||
| 350 | return SDL_AUDIO_S16; | ||
| 351 | } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) { | ||
| 352 | return SDL_AUDIO_S32; | ||
| 353 | } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { | ||
| 354 | const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat; | ||
| 355 | if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { | ||
| 356 | return SDL_AUDIO_F32; | ||
| 357 | } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) { | ||
| 358 | return SDL_AUDIO_S16; | ||
| 359 | } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { | ||
| 360 | return SDL_AUDIO_S32; | ||
| 361 | } | ||
| 362 | } | ||
| 363 | return SDL_AUDIO_UNKNOWN; | ||
| 364 | } | ||
| 365 | |||
| 366 | |||
| 367 | int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar) | ||
| 368 | { | ||
| 369 | if (WIN_IsWindowsXP()) { | ||
| 370 | dwFlags &= ~WC_ERR_INVALID_CHARS; // not supported before Vista. Without this flag, it will just replace bogus chars with U+FFFD. You're on your own, WinXP. | ||
| 371 | } | ||
| 372 | return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar); | ||
| 373 | } | ||
| 374 | |||
| 375 | #endif // defined(SDL_PLATFORM_WINDOWS) | ||
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 | ||
| 116 | extern "C" { | ||
| 117 | #endif | ||
| 118 | |||
| 119 | // Sets an error message based on a given HRESULT | ||
| 120 | extern bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr); | ||
| 121 | |||
| 122 | // Sets an error message based on GetLastError(). Always returns false. | ||
| 123 | extern bool WIN_SetError(const char *prefix); | ||
| 124 | |||
| 125 | // Load a function from combase.dll | ||
| 126 | FARPROC WIN_LoadComBaseFunction(const char *name); | ||
| 127 | |||
| 128 | // Wrap up the oddities of CoInitialize() into a common function. | ||
| 129 | extern HRESULT WIN_CoInitialize(void); | ||
| 130 | extern void WIN_CoUninitialize(void); | ||
| 131 | |||
| 132 | // Wrap up the oddities of RoInitialize() into a common function. | ||
| 133 | extern HRESULT WIN_RoInitialize(void); | ||
| 134 | extern void WIN_RoUninitialize(void); | ||
| 135 | |||
| 136 | // Returns true if we're running on Windows XP (any service pack). DOES NOT CHECK XP "OR GREATER"! | ||
| 137 | extern BOOL WIN_IsWindowsXP(void); | ||
| 138 | |||
| 139 | // Returns true if we're running on Windows Vista and newer | ||
| 140 | extern BOOL WIN_IsWindowsVistaOrGreater(void); | ||
| 141 | |||
| 142 | // Returns true if we're running on Windows 7 and newer | ||
| 143 | extern BOOL WIN_IsWindows7OrGreater(void); | ||
| 144 | |||
| 145 | // Returns true if we're running on Windows 8 and newer | ||
| 146 | extern BOOL WIN_IsWindows8OrGreater(void); | ||
| 147 | |||
| 148 | // You need to SDL_free() the result of this call. | ||
| 149 | extern char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid); | ||
| 150 | |||
| 151 | // Checks to see if two GUID are the same. | ||
| 152 | extern BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b); | ||
| 153 | extern BOOL WIN_IsEqualIID(REFIID a, REFIID b); | ||
| 154 | |||
| 155 | // Convert between SDL_rect and RECT | ||
| 156 | extern void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect); | ||
| 157 | extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect); | ||
| 158 | |||
| 159 | // Returns true if the rect is empty | ||
| 160 | extern BOOL WIN_IsRectEmpty(const RECT *rect); | ||
| 161 | |||
| 162 | extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat); | ||
| 163 | |||
| 164 | // WideCharToMultiByte, but with some WinXP management. | ||
| 165 | extern 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 | ||
| 27 | extern "C" { | ||
| 28 | #endif | ||
| 29 | |||
| 30 | XInputGetState_t SDL_XInputGetState = NULL; | ||
| 31 | XInputSetState_t SDL_XInputSetState = NULL; | ||
| 32 | XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL; | ||
| 33 | XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx = NULL; | ||
| 34 | XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation = NULL; | ||
| 35 | DWORD SDL_XInputVersion = 0; | ||
| 36 | |||
| 37 | static HMODULE s_pXInputDLL = NULL; | ||
| 38 | static int s_XInputDLLRefCount = 0; | ||
| 39 | |||
| 40 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 41 | |||
| 42 | bool 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 | |||
| 63 | void WIN_UnloadXInputDLL(void) | ||
| 64 | { | ||
| 65 | } | ||
| 66 | |||
| 67 | #else // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)) | ||
| 68 | |||
| 69 | bool 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 | |||
| 122 | void 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> | ||
| 33 | using 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 | ||
| 165 | extern "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 | ||
| 172 | typedef struct | ||
| 173 | { | ||
| 174 | BYTE BatteryType; | ||
| 175 | BYTE BatteryLevel; | ||
| 176 | } XINPUT_BATTERY_INFORMATION_EX; | ||
| 177 | |||
| 178 | #ifndef HAVE_XINPUT_H | ||
| 179 | |||
| 180 | typedef 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 | |||
| 191 | typedef struct | ||
| 192 | { | ||
| 193 | DWORD dwPacketNumber; | ||
| 194 | XINPUT_GAMEPAD Gamepad; | ||
| 195 | } XINPUT_STATE; | ||
| 196 | |||
| 197 | typedef struct | ||
| 198 | { | ||
| 199 | WORD wLeftMotorSpeed; | ||
| 200 | WORD wRightMotorSpeed; | ||
| 201 | } XINPUT_VIBRATION; | ||
| 202 | |||
| 203 | typedef 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. | ||
| 215 | typedef 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 | ||
| 226 | typedef 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 | |||
| 231 | typedef 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 | |||
| 236 | typedef 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. | ||
| 243 | typedef 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 | |||
| 250 | typedef DWORD(WINAPI *XInputGetBatteryInformation_t)( | ||
| 251 | DWORD dwUserIndex, | ||
| 252 | BYTE devType, | ||
| 253 | XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation); | ||
| 254 | |||
| 255 | extern bool WIN_LoadXInputDLL(void); | ||
| 256 | extern void WIN_UnloadXInputDLL(void); | ||
| 257 | |||
| 258 | extern XInputGetState_t SDL_XInputGetState; | ||
| 259 | extern XInputSetState_t SDL_XInputSetState; | ||
| 260 | extern XInputGetCapabilities_t SDL_XInputGetCapabilities; | ||
| 261 | extern XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx; | ||
| 262 | extern XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation; | ||
| 263 | extern 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 | |||
| 4 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US | ||
| 5 | |||
| 6 | ///////////////////////////////////////////////////////////////////////////// | ||
| 7 | // | ||
| 8 | // Version | ||
| 9 | // | ||
| 10 | |||
| 11 | VS_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 | ||
| 19 | BEGIN | ||
| 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 | ||
| 38 | END | ||
