initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_directx_h_
#define SDL_directx_h_
// Include all of the DirectX 8.0 headers and adds any necessary tweaks
#include "SDL_windows.h"
#include <mmsystem.h>
#ifndef WIN32
#define WIN32
#endif
#undef WINNT
// Far pointers don't exist in 32-bit code
#ifndef FAR
#define FAR
#endif
// Error codes not yet included in Win32 API header files
#ifndef MAKE_HRESULT
#define MAKE_HRESULT(sev, fac, code) \
((HRESULT)(((unsigned long)(sev) << 31) | ((unsigned long)(fac) << 16) | ((unsigned long)(code))))
#endif
#ifndef S_OK
#define S_OK (HRESULT)0x00000000L
#endif
#ifndef SUCCEEDED
#define SUCCEEDED(x) ((HRESULT)(x) >= 0)
#endif
#ifndef FAILED
#define FAILED(x) ((HRESULT)(x) < 0)
#endif
#ifndef E_FAIL
#define E_FAIL (HRESULT)0x80000008L
#endif
#ifndef E_NOINTERFACE
#define E_NOINTERFACE (HRESULT)0x80004002L
#endif
#ifndef E_OUTOFMEMORY
#define E_OUTOFMEMORY (HRESULT)0x8007000EL
#endif
#ifndef E_INVALIDARG
#define E_INVALIDARG (HRESULT)0x80070057L
#endif
#ifndef E_NOTIMPL
#define E_NOTIMPL (HRESULT)0x80004001L
#endif
#ifndef REGDB_E_CLASSNOTREG
#define REGDB_E_CLASSNOTREG (HRESULT)0x80040154L
#endif
// Severity codes
#ifndef SEVERITY_ERROR
#define SEVERITY_ERROR 1
#endif
// Error facility codes
#ifndef FACILITY_WIN32
#define FACILITY_WIN32 7
#endif
#ifndef FIELD_OFFSET
#define FIELD_OFFSET(type, field) ((LONG) & (((type *)0)->field))
#endif
/* DirectX headers (if it isn't included, I haven't tested it yet)
*/
// We need these defines to mark what version of DirectX API we use
#define DIRECTDRAW_VERSION 0x0700
#define DIRECTSOUND_VERSION 0x0800
#define DIRECTINPUT_VERSION 0x0800 // Need version 7 for force feedback. Need version 8 so IDirectInput8_EnumDevices doesn't leak like a sieve...
#ifdef HAVE_DDRAW_H
#include <ddraw.h>
#endif
#ifdef HAVE_DSOUND_H
#include <dsound.h>
#endif
#ifdef HAVE_DINPUT_H
#include <dinput.h>
#else
typedef struct
{
int unused;
} DIDEVICEINSTANCE;
#endif
#endif // SDL_directx_h_

View File

@@ -0,0 +1,98 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef HAVE_GAMEINPUT_H
#include "SDL_windows.h"
#include "SDL_gameinput.h"
#ifdef SDL_PLATFORM_WIN32
#include <initguid.h>
// {11BE2A7E-4254-445A-9C09-FFC40F006918}
DEFINE_GUID(SDL_IID_GameInput, 0x11BE2A7E, 0x4254, 0x445A, 0x9C, 0x09, 0xFF, 0xC4, 0x0F, 0x00, 0x69, 0x18);
#endif
static SDL_SharedObject *g_hGameInputDLL;
static IGameInput *g_pGameInput;
static int g_nGameInputRefCount;
bool SDL_InitGameInput(IGameInput **ppGameInput)
{
if (g_nGameInputRefCount == 0) {
g_hGameInputDLL = SDL_LoadObject("gameinput.dll");
if (!g_hGameInputDLL) {
return false;
}
typedef HRESULT (WINAPI *GameInputCreate_t)(IGameInput * *gameInput);
GameInputCreate_t GameInputCreateFunc = (GameInputCreate_t)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate");
if (!GameInputCreateFunc) {
SDL_UnloadObject(g_hGameInputDLL);
return false;
}
IGameInput *pGameInput = NULL;
HRESULT hr = GameInputCreateFunc(&pGameInput);
if (FAILED(hr)) {
SDL_UnloadObject(g_hGameInputDLL);
return WIN_SetErrorFromHRESULT("GameInputCreate failed", hr);
}
#ifdef SDL_PLATFORM_WIN32
hr = IGameInput_QueryInterface(pGameInput, &SDL_IID_GameInput, (void **)&g_pGameInput);
IGameInput_Release(pGameInput);
if (FAILED(hr)) {
SDL_UnloadObject(g_hGameInputDLL);
return WIN_SetErrorFromHRESULT("GameInput QueryInterface failed", hr);
}
#else
// Assume that the version we get is compatible with the current SDK
// If that isn't the case, define the correct GUID for SDL_IID_GameInput above
g_pGameInput = pGameInput;
#endif
}
++g_nGameInputRefCount;
if (ppGameInput) {
*ppGameInput = g_pGameInput;
}
return true;
}
void SDL_QuitGameInput(void)
{
SDL_assert(g_nGameInputRefCount > 0);
--g_nGameInputRefCount;
if (g_nGameInputRefCount == 0) {
if (g_pGameInput) {
IGameInput_Release(g_pGameInput);
g_pGameInput = NULL;
}
if (g_hGameInputDLL) {
SDL_UnloadObject(g_hGameInputDLL);
g_hGameInputDLL = NULL;
}
}
}
#endif // HAVE_GAMEINPUT_H

View File

@@ -0,0 +1,36 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_gameinput_h_
#define SDL_gameinput_h_
#ifdef HAVE_GAMEINPUT_H
#define COBJMACROS
#include <gameinput.h>
extern bool SDL_InitGameInput(IGameInput **ppGameInput);
extern void SDL_QuitGameInput(void);
#endif // HAVE_GAMEINPUT_H
#endif // SDL_gameinput_h_

254
thirdparty/sdl/core/windows/SDL_hid.c vendored Normal file
View File

@@ -0,0 +1,254 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_hid.h"
HidD_GetAttributes_t SDL_HidD_GetAttributes;
HidD_GetString_t SDL_HidD_GetManufacturerString;
HidD_GetString_t SDL_HidD_GetProductString;
HidP_GetCaps_t SDL_HidP_GetCaps;
HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps;
HidP_GetValueCaps_t SDL_HidP_GetValueCaps;
HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength;
HidP_GetData_t SDL_HidP_GetData;
static HMODULE s_pHIDDLL = 0;
static int s_HIDDLLRefCount = 0;
bool WIN_LoadHIDDLL(void)
{
if (s_pHIDDLL) {
SDL_assert(s_HIDDLLRefCount > 0);
s_HIDDLLRefCount++;
return true; // already loaded
}
s_pHIDDLL = LoadLibrary(TEXT("hid.dll"));
if (!s_pHIDDLL) {
return false;
}
SDL_assert(s_HIDDLLRefCount == 0);
s_HIDDLLRefCount = 1;
SDL_HidD_GetAttributes = (HidD_GetAttributes_t)GetProcAddress(s_pHIDDLL, "HidD_GetAttributes");
SDL_HidD_GetManufacturerString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetManufacturerString");
SDL_HidD_GetProductString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetProductString");
SDL_HidP_GetCaps = (HidP_GetCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetCaps");
SDL_HidP_GetButtonCaps = (HidP_GetButtonCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetButtonCaps");
SDL_HidP_GetValueCaps = (HidP_GetValueCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetValueCaps");
SDL_HidP_MaxDataListLength = (HidP_MaxDataListLength_t)GetProcAddress(s_pHIDDLL, "HidP_MaxDataListLength");
SDL_HidP_GetData = (HidP_GetData_t)GetProcAddress(s_pHIDDLL, "HidP_GetData");
if (!SDL_HidD_GetManufacturerString || !SDL_HidD_GetProductString ||
!SDL_HidP_GetCaps || !SDL_HidP_GetButtonCaps ||
!SDL_HidP_GetValueCaps || !SDL_HidP_MaxDataListLength || !SDL_HidP_GetData) {
WIN_UnloadHIDDLL();
return false;
}
return true;
}
void WIN_UnloadHIDDLL(void)
{
if (s_pHIDDLL) {
SDL_assert(s_HIDDLLRefCount > 0);
if (--s_HIDDLLRefCount == 0) {
FreeLibrary(s_pHIDDLL);
s_pHIDDLL = NULL;
}
} else {
SDL_assert(s_HIDDLLRefCount == 0);
}
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
// CM_Register_Notification definitions
#define CR_SUCCESS 0
DECLARE_HANDLE(HCMNOTIFICATION);
typedef HCMNOTIFICATION *PHCMNOTIFICATION;
typedef enum _CM_NOTIFY_FILTER_TYPE
{
CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE = 0,
CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE,
CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE,
CM_NOTIFY_FILTER_TYPE_MAX
} CM_NOTIFY_FILTER_TYPE, *PCM_NOTIFY_FILTER_TYPE;
typedef struct _CM_NOTIFY_FILTER
{
DWORD cbSize;
DWORD Flags;
CM_NOTIFY_FILTER_TYPE FilterType;
DWORD Reserved;
union
{
struct
{
GUID ClassGuid;
} DeviceInterface;
struct
{
HANDLE hTarget;
} DeviceHandle;
struct
{
WCHAR InstanceId[200];
} DeviceInstance;
} u;
} CM_NOTIFY_FILTER, *PCM_NOTIFY_FILTER;
typedef enum _CM_NOTIFY_ACTION
{
CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL = 0,
CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL,
CM_NOTIFY_ACTION_DEVICEQUERYREMOVE,
CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED,
CM_NOTIFY_ACTION_DEVICEREMOVEPENDING,
CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE,
CM_NOTIFY_ACTION_DEVICECUSTOMEVENT,
CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED,
CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED,
CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED,
CM_NOTIFY_ACTION_MAX
} CM_NOTIFY_ACTION, *PCM_NOTIFY_ACTION;
typedef struct _CM_NOTIFY_EVENT_DATA
{
CM_NOTIFY_FILTER_TYPE FilterType;
DWORD Reserved;
union
{
struct
{
GUID ClassGuid;
WCHAR SymbolicLink[ANYSIZE_ARRAY];
} DeviceInterface;
struct
{
GUID EventGuid;
LONG NameOffset;
DWORD DataSize;
BYTE Data[ANYSIZE_ARRAY];
} DeviceHandle;
struct
{
WCHAR InstanceId[ANYSIZE_ARRAY];
} DeviceInstance;
} u;
} CM_NOTIFY_EVENT_DATA, *PCM_NOTIFY_EVENT_DATA;
typedef DWORD (CALLBACK *PCM_NOTIFY_CALLBACK)(HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize);
typedef DWORD (WINAPI *CM_Register_NotificationFunc)(PCM_NOTIFY_FILTER pFilter, PVOID pContext, PCM_NOTIFY_CALLBACK pCallback, PHCMNOTIFICATION pNotifyContext);
typedef DWORD (WINAPI *CM_Unregister_NotificationFunc)(HCMNOTIFICATION NotifyContext);
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
static int s_DeviceNotificationsRequested;
static HMODULE cfgmgr32_lib_handle;
static CM_Register_NotificationFunc CM_Register_Notification;
static CM_Unregister_NotificationFunc CM_Unregister_Notification;
static HCMNOTIFICATION s_DeviceNotificationFuncHandle;
static Uint64 s_LastDeviceNotification = 1;
static DWORD CALLBACK SDL_DeviceNotificationFunc(HCMNOTIFICATION hNotify, PVOID context, CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA eventData, DWORD event_data_size)
{
if (action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ||
action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL) {
s_LastDeviceNotification = SDL_GetTicksNS();
}
return ERROR_SUCCESS;
}
void WIN_InitDeviceNotification(void)
{
++s_DeviceNotificationsRequested;
if (s_DeviceNotificationsRequested > 1) {
return;
}
cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll");
if (cfgmgr32_lib_handle) {
CM_Register_Notification = (CM_Register_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Register_Notification");
CM_Unregister_Notification = (CM_Unregister_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Unregister_Notification");
if (CM_Register_Notification && CM_Unregister_Notification) {
CM_NOTIFY_FILTER notify_filter;
SDL_zero(notify_filter);
notify_filter.cbSize = sizeof(notify_filter);
notify_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
notify_filter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_HID;
if (CM_Register_Notification(&notify_filter, NULL, SDL_DeviceNotificationFunc, &s_DeviceNotificationFuncHandle) == CR_SUCCESS) {
return;
}
}
}
// FIXME: Should we log errors?
}
Uint64 WIN_GetLastDeviceNotification(void)
{
return s_LastDeviceNotification;
}
void WIN_QuitDeviceNotification(void)
{
if (--s_DeviceNotificationsRequested > 0) {
return;
}
// Make sure we have balanced calls to init/quit
SDL_assert(s_DeviceNotificationsRequested == 0);
if (cfgmgr32_lib_handle) {
if (s_DeviceNotificationFuncHandle && CM_Unregister_Notification) {
CM_Unregister_Notification(s_DeviceNotificationFuncHandle);
s_DeviceNotificationFuncHandle = NULL;
}
FreeLibrary(cfgmgr32_lib_handle);
cfgmgr32_lib_handle = NULL;
}
}
#else
void WIN_InitDeviceNotification(void)
{
}
Uint64 WIN_GetLastDeviceNotification( void )
{
return 0;
}
void WIN_QuitDeviceNotification(void)
{
}
#endif // !SDL_PLATFORM_XBOXONE && !SDL_PLATFORM_XBOXSERIES

215
thirdparty/sdl/core/windows/SDL_hid.h vendored Normal file
View File

@@ -0,0 +1,215 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_hid_h_
#define SDL_hid_h_
#include "SDL_windows.h"
typedef LONG NTSTATUS;
typedef USHORT USAGE;
typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA;
typedef struct _HIDD_ATTRIBUTES
{
ULONG Size;
USHORT VendorID;
USHORT ProductID;
USHORT VersionNumber;
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
typedef enum
{
HidP_Input = 0,
HidP_Output = 1,
HidP_Feature = 2
} HIDP_REPORT_TYPE;
typedef struct
{
USAGE UsagePage;
UCHAR ReportID;
BOOLEAN IsAlias;
USHORT BitField;
USHORT LinkCollection;
USAGE LinkUsage;
USAGE LinkUsagePage;
BOOLEAN IsRange;
BOOLEAN IsStringRange;
BOOLEAN IsDesignatorRange;
BOOLEAN IsAbsolute;
ULONG Reserved[10];
union
{
struct
{
USAGE UsageMin;
USAGE UsageMax;
USHORT StringMin;
USHORT StringMax;
USHORT DesignatorMin;
USHORT DesignatorMax;
USHORT DataIndexMin;
USHORT DataIndexMax;
} Range;
struct
{
USAGE Usage;
USAGE Reserved1;
USHORT StringIndex;
USHORT Reserved2;
USHORT DesignatorIndex;
USHORT Reserved3;
USHORT DataIndex;
USHORT Reserved4;
} NotRange;
};
} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS;
typedef struct
{
USAGE UsagePage;
UCHAR ReportID;
BOOLEAN IsAlias;
USHORT BitField;
USHORT LinkCollection;
USAGE LinkUsage;
USAGE LinkUsagePage;
BOOLEAN IsRange;
BOOLEAN IsStringRange;
BOOLEAN IsDesignatorRange;
BOOLEAN IsAbsolute;
BOOLEAN HasNull;
UCHAR Reserved;
USHORT BitSize;
USHORT ReportCount;
USHORT Reserved2[5];
ULONG UnitsExp;
ULONG Units;
LONG LogicalMin;
LONG LogicalMax;
LONG PhysicalMin;
LONG PhysicalMax;
union
{
struct
{
USAGE UsageMin;
USAGE UsageMax;
USHORT StringMin;
USHORT StringMax;
USHORT DesignatorMin;
USHORT DesignatorMax;
USHORT DataIndexMin;
USHORT DataIndexMax;
} Range;
struct
{
USAGE Usage;
USAGE Reserved1;
USHORT StringIndex;
USHORT Reserved2;
USHORT DesignatorIndex;
USHORT Reserved3;
USHORT DataIndex;
USHORT Reserved4;
} NotRange;
};
} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
typedef struct
{
USAGE Usage;
USAGE UsagePage;
USHORT InputReportByteLength;
USHORT OutputReportByteLength;
USHORT FeatureReportByteLength;
USHORT Reserved[17];
USHORT NumberLinkCollectionNodes;
USHORT NumberInputButtonCaps;
USHORT NumberInputValueCaps;
USHORT NumberInputDataIndices;
USHORT NumberOutputButtonCaps;
USHORT NumberOutputValueCaps;
USHORT NumberOutputDataIndices;
USHORT NumberFeatureButtonCaps;
USHORT NumberFeatureValueCaps;
USHORT NumberFeatureDataIndices;
} HIDP_CAPS, *PHIDP_CAPS;
typedef struct
{
USHORT DataIndex;
USHORT Reserved;
union
{
ULONG RawValue;
BOOLEAN On;
};
} HIDP_DATA, *PHIDP_DATA;
#define HIDP_ERROR_CODES(p1, p2) ((NTSTATUS)(((p1) << 28) | (0x11 << 16) | (p2)))
#define HIDP_STATUS_SUCCESS HIDP_ERROR_CODES(0x0, 0x0000)
#define HIDP_STATUS_NULL HIDP_ERROR_CODES(0x8, 0x0001)
#define HIDP_STATUS_INVALID_PREPARSED_DATA HIDP_ERROR_CODES(0xC, 0x0001)
#define HIDP_STATUS_INVALID_REPORT_TYPE HIDP_ERROR_CODES(0xC, 0x0002)
#define HIDP_STATUS_INVALID_REPORT_LENGTH HIDP_ERROR_CODES(0xC, 0x0003)
#define HIDP_STATUS_USAGE_NOT_FOUND HIDP_ERROR_CODES(0xC, 0x0004)
#define HIDP_STATUS_VALUE_OUT_OF_RANGE HIDP_ERROR_CODES(0xC, 0x0005)
#define HIDP_STATUS_BAD_LOG_PHY_VALUES HIDP_ERROR_CODES(0xC, 0x0006)
#define HIDP_STATUS_BUFFER_TOO_SMALL HIDP_ERROR_CODES(0xC, 0x0007)
#define HIDP_STATUS_INTERNAL_ERROR HIDP_ERROR_CODES(0xC, 0x0008)
#define HIDP_STATUS_I8042_TRANS_UNKNOWN HIDP_ERROR_CODES(0xC, 0x0009)
#define HIDP_STATUS_INCOMPATIBLE_REPORT_ID HIDP_ERROR_CODES(0xC, 0x000A)
#define HIDP_STATUS_NOT_VALUE_ARRAY HIDP_ERROR_CODES(0xC, 0x000B)
#define HIDP_STATUS_IS_VALUE_ARRAY HIDP_ERROR_CODES(0xC, 0x000C)
#define HIDP_STATUS_DATA_INDEX_NOT_FOUND HIDP_ERROR_CODES(0xC, 0x000D)
#define HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE HIDP_ERROR_CODES(0xC, 0x000E)
#define HIDP_STATUS_BUTTON_NOT_PRESSED HIDP_ERROR_CODES(0xC, 0x000F)
#define HIDP_STATUS_REPORT_DOES_NOT_EXIST HIDP_ERROR_CODES(0xC, 0x0010)
#define HIDP_STATUS_NOT_IMPLEMENTED HIDP_ERROR_CODES(0xC, 0x0020)
extern bool WIN_LoadHIDDLL(void);
extern void WIN_UnloadHIDDLL(void);
typedef BOOLEAN (WINAPI *HidD_GetAttributes_t)(HANDLE HidDeviceObject, PHIDD_ATTRIBUTES Attributes);
typedef BOOLEAN (WINAPI *HidD_GetString_t)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength);
typedef NTSTATUS (WINAPI *HidP_GetCaps_t)(PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities);
typedef NTSTATUS (WINAPI *HidP_GetButtonCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
typedef NTSTATUS (WINAPI *HidP_GetValueCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
typedef ULONG (WINAPI *HidP_MaxDataListLength_t)(HIDP_REPORT_TYPE ReportType, PHIDP_PREPARSED_DATA PreparsedData);
typedef NTSTATUS (WINAPI *HidP_GetData_t)(HIDP_REPORT_TYPE ReportType, PHIDP_DATA DataList, PULONG DataLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
extern HidD_GetAttributes_t SDL_HidD_GetAttributes;
extern HidD_GetString_t SDL_HidD_GetManufacturerString;
extern HidD_GetString_t SDL_HidD_GetProductString;
extern HidP_GetCaps_t SDL_HidP_GetCaps;
extern HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps;
extern HidP_GetValueCaps_t SDL_HidP_GetValueCaps;
extern HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength;
extern HidP_GetData_t SDL_HidP_GetData;
void WIN_InitDeviceNotification(void);
Uint64 WIN_GetLastDeviceNotification(void);
void WIN_QuitDeviceNotification(void);
#endif // SDL_hid_h_

View File

@@ -0,0 +1,434 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_PLATFORM_WINDOWS) && defined(HAVE_MMDEVICEAPI_H)
#include "SDL_windows.h"
#include "SDL_immdevice.h"
#include "../../audio/SDL_sysaudio.h"
#include <objbase.h> // For CLSIDFromString
typedef struct SDL_IMMDevice_HandleData
{
LPWSTR immdevice_id;
GUID directsound_guid;
} SDL_IMMDevice_HandleData;
static const ERole SDL_IMMDevice_role = eConsole; // !!! FIXME: should this be eMultimedia? Should be a hint?
// This is global to the WASAPI target, to handle hotplug and default device lookup.
static IMMDeviceEnumerator *enumerator = NULL;
static SDL_IMMDevice_callbacks immcallbacks;
// PropVariantInit() is an inline function/macro in PropIdl.h that calls the C runtime's memset() directly. Use ours instead, to avoid dependency.
#ifdef PropVariantInit
#undef PropVariantInit
#endif
#define PropVariantInit(p) SDL_zerop(p)
// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
/* *INDENT-OFF* */ // clang-format off
static const CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,{ 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } };
static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,{ 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } };
static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{ 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } };
static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 };
static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0x4edd,{ 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e, } }, 4 };
/* *INDENT-ON* */ // clang-format on
static bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata)
{
LPCWSTR devid = (LPCWSTR)userdata;
if (devid && device && device->handle) {
const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *)device->handle;
if (handle->immdevice_id && SDL_wcscmp(handle->immdevice_id, devid) == 0) {
return true;
}
}
return false;
}
static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid)
{
return SDL_FindPhysicalAudioDeviceByCallback(FindByDevIDCallback, (void *) devid);
}
LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device)
{
return (device && device->handle) ? &(((SDL_IMMDevice_HandleData *) device->handle)->directsound_guid) : NULL;
}
LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device)
{
return (device && device->handle) ? ((const SDL_IMMDevice_HandleData *) device->handle)->immdevice_id : NULL;
}
static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt, GUID *guid)
{
/* PKEY_Device_FriendlyName gives you "Speakers (SoundBlaster Pro)" which drives me nuts. I'd rather it be
"SoundBlaster Pro (Speakers)" but I guess that's developers vs users. Windows uses the FriendlyName in
its own UIs, like Volume Control, etc. */
IPropertyStore *props = NULL;
*utf8dev = NULL;
SDL_zerop(fmt);
if (SUCCEEDED(IMMDevice_OpenPropertyStore(device, STGM_READ, &props))) {
PROPVARIANT var;
PropVariantInit(&var);
if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_Device_FriendlyName, &var))) {
*utf8dev = WIN_StringToUTF8W(var.pwszVal);
}
PropVariantClear(&var);
if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEngine_DeviceFormat, &var))) {
SDL_memcpy(fmt, var.blob.pBlobData, SDL_min(var.blob.cbSize, sizeof(WAVEFORMATEXTENSIBLE)));
}
PropVariantClear(&var);
if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEndpoint_GUID, &var))) {
(void)CLSIDFromString(var.pwszVal, guid);
}
PropVariantClear(&var);
IPropertyStore_Release(props);
}
}
void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device)
{
if (device && device->handle) {
SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
SDL_free(handle->immdevice_id);
SDL_free(handle);
device->handle = NULL;
}
}
static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid)
{
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
available and switch automatically. (!!! FIXME...?) */
if (!devname) {
return NULL;
}
// see if we already have this one first.
SDL_AudioDevice *device = SDL_IMMDevice_FindByDevID(devid);
if (device) {
if (SDL_GetAtomicInt(&device->zombie)) {
// whoa, it came back! This can happen if you unplug and replug USB headphones while we're still keeping the SDL object alive.
// Kill this device's IMMDevice id; the device will go away when the app closes it, or maybe a new default device is chosen
// (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.
SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
SDL_free(handle->immdevice_id);
handle->immdevice_id = NULL;
device = NULL; // add a new device, below.
}
}
if (!device) {
// handle is freed by SDL_IMMDevice_FreeDeviceHandle!
SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *)SDL_malloc(sizeof(SDL_IMMDevice_HandleData));
if (!handle) {
return NULL;
}
handle->immdevice_id = SDL_wcsdup(devid);
if (!handle->immdevice_id) {
SDL_free(handle);
return NULL;
}
SDL_memcpy(&handle->directsound_guid, dsoundguid, sizeof(GUID));
SDL_AudioSpec spec;
SDL_zero(spec);
spec.channels = (Uint8)fmt->Format.nChannels;
spec.freq = fmt->Format.nSamplesPerSec;
spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
device = SDL_AddAudioDevice(recording, devname, &spec, handle);
if (!device) {
SDL_free(handle->immdevice_id);
SDL_free(handle);
}
}
return device;
}
/* We need a COM subclass of IMMNotificationClient for hotplug support, which is
easy in C++, but we have to tapdance more to make work in C.
Thanks to this page for coaching on how to make this work:
https://www.codeproject.com/Articles/13601/COM-in-plain-C */
typedef struct SDLMMNotificationClient
{
const IMMNotificationClientVtbl *lpVtbl;
SDL_AtomicInt refcount;
} SDLMMNotificationClient;
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
{
if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient))) {
*ppv = client;
client->lpVtbl->AddRef(client);
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient)
{
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
return (ULONG)(SDL_AtomicIncRef(&client->refcount) + 1);
}
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *iclient)
{
// client is a static object; we don't ever free it.
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
const ULONG rc = SDL_AtomicDecRef(&client->refcount);
if (rc == 0) {
SDL_SetAtomicInt(&client->refcount, 0); // uhh...
return 0;
}
return rc - 1;
}
// These are the entry points called when WASAPI device endpoints change.
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
{
if (role == SDL_IMMDevice_role) {
immcallbacks.default_audio_device_changed(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
}
return S_OK;
}
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
{
/* we ignore this; devices added here then progress to ACTIVE, if appropriate, in
OnDeviceStateChange, making that a better place to deal with device adds. More
importantly: the first time you plug in a USB audio device, this callback will
fire, but when you unplug it, it isn't removed (it's state changes to NOTPRESENT).
Plugging it back in won't fire this callback again. */
return S_OK;
}
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
{
return S_OK; // See notes in OnDeviceAdded handler about why we ignore this.
}
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId, DWORD dwNewState)
{
IMMDevice *device = NULL;
if (SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &device))) {
IMMEndpoint *endpoint = NULL;
if (SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (void **)&endpoint))) {
EDataFlow flow;
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
const bool recording = (flow == eCapture);
if (dwNewState == DEVICE_STATE_ACTIVE) {
char *utf8dev;
WAVEFORMATEXTENSIBLE fmt;
GUID dsoundguid;
GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
if (utf8dev) {
SDL_IMMDevice_Add(recording, utf8dev, &fmt, pwstrDeviceId, &dsoundguid);
SDL_free(utf8dev);
}
} else {
immcallbacks.audio_device_disconnected(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
}
}
IMMEndpoint_Release(endpoint);
}
IMMDevice_Release(device);
}
return S_OK;
}
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client, LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
{
return S_OK; // we don't care about these.
}
static const IMMNotificationClientVtbl notification_client_vtbl = {
SDLMMNotificationClient_QueryInterface,
SDLMMNotificationClient_AddRef,
SDLMMNotificationClient_Release,
SDLMMNotificationClient_OnDeviceStateChanged,
SDLMMNotificationClient_OnDeviceAdded,
SDLMMNotificationClient_OnDeviceRemoved,
SDLMMNotificationClient_OnDefaultDeviceChanged,
SDLMMNotificationClient_OnPropertyValueChanged
};
static SDLMMNotificationClient notification_client = { &notification_client_vtbl, { 1 } };
bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks)
{
HRESULT ret;
// just skip the discussion with COM here.
if (!WIN_IsWindowsVistaOrGreater()) {
return SDL_SetError("IMMDevice support requires Windows Vista or later");
}
if (FAILED(WIN_CoInitialize())) {
return SDL_SetError("IMMDevice: CoInitialize() failed");
}
ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *)&enumerator);
if (FAILED(ret)) {
WIN_CoUninitialize();
return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret);
}
if (callbacks) {
SDL_copyp(&immcallbacks, callbacks);
} else {
SDL_zero(immcallbacks);
}
if (!immcallbacks.audio_device_disconnected) {
immcallbacks.audio_device_disconnected = SDL_AudioDeviceDisconnected;
}
if (!immcallbacks.default_audio_device_changed) {
immcallbacks.default_audio_device_changed = SDL_DefaultAudioDeviceChanged;
}
return true;
}
void SDL_IMMDevice_Quit(void)
{
if (enumerator) {
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
IMMDeviceEnumerator_Release(enumerator);
enumerator = NULL;
}
SDL_zero(immcallbacks);
WIN_CoUninitialize();
}
bool SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, bool recording)
{
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.
SDL_assert(device != NULL);
SDL_assert(immdevice != NULL);
LPCWSTR devid = SDL_IMMDevice_GetDevID(device);
SDL_assert(devid != NULL);
HRESULT ret;
while ((ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, immdevice)) == E_NOTFOUND) {
const Uint64 now = SDL_GetTicks();
if (timeout > now) {
const Uint64 ticksleft = timeout - now;
SDL_Delay((Uint32)SDL_min(ticksleft, 300)); // wait awhile and try again.
continue;
}
break;
}
if (!SUCCEEDED(ret)) {
return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
}
return true;
}
static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **default_device)
{
/* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
IMMDeviceCollection *collection = NULL;
if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, recording ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
return;
}
UINT total = 0;
if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
IMMDeviceCollection_Release(collection);
return;
}
LPWSTR default_devid = NULL;
if (default_device) {
IMMDevice *default_immdevice = NULL;
const EDataFlow dataflow = recording ? eCapture : eRender;
if (SUCCEEDED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &default_immdevice))) {
LPWSTR devid = NULL;
if (SUCCEEDED(IMMDevice_GetId(default_immdevice, &devid))) {
default_devid = SDL_wcsdup(devid); // if this fails, oh well.
CoTaskMemFree(devid);
}
IMMDevice_Release(default_immdevice);
}
}
for (UINT i = 0; i < total; i++) {
IMMDevice *immdevice = NULL;
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &immdevice))) {
LPWSTR devid = NULL;
if (SUCCEEDED(IMMDevice_GetId(immdevice, &devid))) {
char *devname = NULL;
WAVEFORMATEXTENSIBLE fmt;
GUID dsoundguid;
SDL_zero(fmt);
SDL_zero(dsoundguid);
GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
if (devname) {
SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(recording, devname, &fmt, devid, &dsoundguid);
if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
*default_device = sdldevice;
}
SDL_free(devname);
}
CoTaskMemFree(devid);
}
IMMDevice_Release(immdevice);
}
}
SDL_free(default_devid);
IMMDeviceCollection_Release(collection);
}
void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording)
{
EnumerateEndpointsForFlow(false, default_playback);
EnumerateEndpointsForFlow(true, default_recording);
// if this fails, we just won't get hotplug events. Carry on anyhow.
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
}
#endif // defined(SDL_PLATFORM_WINDOWS) && defined(HAVE_MMDEVICEAPI_H)

View File

@@ -0,0 +1,45 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_IMMDEVICE_H
#define SDL_IMMDEVICE_H
#define COBJMACROS
#include <mmdeviceapi.h>
#include <mmreg.h>
struct SDL_AudioDevice; // defined in src/audio/SDL_sysaudio.h
typedef struct SDL_IMMDevice_callbacks
{
void (*audio_device_disconnected)(struct SDL_AudioDevice *device);
void (*default_audio_device_changed)(struct SDL_AudioDevice *new_default_device);
} SDL_IMMDevice_callbacks;
bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks);
void SDL_IMMDevice_Quit(void);
bool SDL_IMMDevice_Get(struct SDL_AudioDevice *device, IMMDevice **immdevice, bool recording);
void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording);
LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device);
LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device);
void SDL_IMMDevice_FreeDeviceHandle(struct SDL_AudioDevice *device);
#endif // SDL_IMMDEVICE_H

View File

@@ -0,0 +1,375 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_PLATFORM_WINDOWS)
#include "SDL_windows.h"
#include <objbase.h> // for CoInitialize/CoUninitialize (Win32 only)
#ifdef HAVE_ROAPI_H
#include <roapi.h> // For RoInitialize/RoUninitialize (Win32 only)
#else
typedef enum RO_INIT_TYPE
{
RO_INIT_SINGLETHREADED = 0,
RO_INIT_MULTITHREADED = 1
} RO_INIT_TYPE;
#endif
#ifndef _WIN32_WINNT_VISTA
#define _WIN32_WINNT_VISTA 0x0600
#endif
#ifndef _WIN32_WINNT_WIN7
#define _WIN32_WINNT_WIN7 0x0601
#endif
#ifndef _WIN32_WINNT_WIN8
#define _WIN32_WINNT_WIN8 0x0602
#endif
#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
#endif
#ifndef WC_ERR_INVALID_CHARS
#define WC_ERR_INVALID_CHARS 0x00000080
#endif
// Sets an error message based on an HRESULT
bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
{
TCHAR buffer[1024];
char *message;
TCHAR *p = buffer;
DWORD c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0,
buffer, SDL_arraysize(buffer), NULL);
buffer[c] = 0;
// kill CR/LF that FormatMessage() sticks at the end
while (*p) {
if (*p == '\r') {
*p = 0;
break;
}
++p;
}
message = WIN_StringToUTF8(buffer);
SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ": " : "", message);
SDL_free(message);
return false;
}
// Sets an error message based on GetLastError()
bool WIN_SetError(const char *prefix)
{
return WIN_SetErrorFromHRESULT(prefix, GetLastError());
}
HRESULT
WIN_CoInitialize(void)
{
/* SDL handles any threading model, so initialize with the default, which
is compatible with OLE and if that doesn't work, try multi-threaded mode.
If you need multi-threaded mode, call CoInitializeEx() before SDL_Init()
*/
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
// On Xbox, there's no need to call CoInitializeEx (and it's not implemented)
return S_OK;
#else
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (hr == RPC_E_CHANGED_MODE) {
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
}
// S_FALSE means success, but someone else already initialized.
// You still need to call CoUninitialize in this case!
if (hr == S_FALSE) {
return S_OK;
}
return hr;
#endif
}
void WIN_CoUninitialize(void)
{
CoUninitialize();
}
FARPROC WIN_LoadComBaseFunction(const char *name)
{
static bool s_bLoaded;
static HMODULE s_hComBase;
if (!s_bLoaded) {
s_hComBase = LoadLibraryEx(TEXT("combase.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
s_bLoaded = true;
}
if (s_hComBase) {
return GetProcAddress(s_hComBase, name);
} else {
return NULL;
}
}
HRESULT
WIN_RoInitialize(void)
{
typedef HRESULT(WINAPI * RoInitialize_t)(RO_INIT_TYPE initType);
RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize");
if (RoInitializeFunc) {
// RO_INIT_SINGLETHREADED is equivalent to COINIT_APARTMENTTHREADED
HRESULT hr = RoInitializeFunc(RO_INIT_SINGLETHREADED);
if (hr == RPC_E_CHANGED_MODE) {
hr = RoInitializeFunc(RO_INIT_MULTITHREADED);
}
// S_FALSE means success, but someone else already initialized.
// You still need to call RoUninitialize in this case!
if (hr == S_FALSE) {
return S_OK;
}
return hr;
} else {
return E_NOINTERFACE;
}
}
void WIN_RoUninitialize(void)
{
typedef void(WINAPI * RoUninitialize_t)(void);
RoUninitialize_t RoUninitializeFunc = (RoUninitialize_t)WIN_LoadComBaseFunction("RoUninitialize");
if (RoUninitializeFunc) {
RoUninitializeFunc();
}
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
{
OSVERSIONINFOEXW osvi;
DWORDLONG const dwlConditionMask = VerSetConditionMask(
VerSetConditionMask(
VerSetConditionMask(
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
VER_MINORVERSION, VER_GREATER_EQUAL),
VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
SDL_zero(osvi);
osvi.dwOSVersionInfoSize = sizeof(osvi);
osvi.dwMajorVersion = wMajorVersion;
osvi.dwMinorVersion = wMinorVersion;
osvi.wServicePackMajor = wServicePackMajor;
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
}
#endif
// apply some static variables so we only call into the Win32 API once per process for each check.
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
#define CHECKWINVER(notdesktop_platform_result, test) return (notdesktop_platform_result);
#else
#define CHECKWINVER(notdesktop_platform_result, test) \
static bool checked = false; \
static BOOL result = FALSE; \
if (!checked) { \
result = (test); \
checked = true; \
} \
return result;
#endif
// this is the oldest thing we run on (and we may lose support for this in SDL3 at any time!),
// so there's no "OrGreater" as that would always be TRUE. The other functions are here to
// ask "can we support a specific feature?" but this function is here to ask "do we need to do
// something different for an OS version we probably should abandon?" :)
BOOL WIN_IsWindowsXP(void)
{
CHECKWINVER(FALSE, !WIN_IsWindowsVistaOrGreater() && IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0));
}
BOOL WIN_IsWindowsVistaOrGreater(void)
{
CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0));
}
BOOL WIN_IsWindows7OrGreater(void)
{
CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0));
}
BOOL WIN_IsWindows8OrGreater(void)
{
CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0));
}
#undef CHECKWINVER
/*
WAVExxxCAPS gives you 31 bytes for the device name, and just truncates if it's
longer. However, since WinXP, you can use the WAVExxxCAPS2 structure, which
will give you a name GUID. The full name is in the Windows Registry under
that GUID, located here: HKLM\System\CurrentControlSet\Control\MediaCategories
Note that drivers can report GUID_NULL for the name GUID, in which case,
Windows makes a best effort to fill in those 31 bytes in the usual place.
This info summarized from MSDN:
http://web.archive.org/web/20131027093034/http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382(v=vs.85).aspx
Always look this up in the registry if possible, because the strings are
different! At least on Win10, I see "Yeti Stereo Microphone" in the
Registry, and a unhelpful "Microphone(Yeti Stereo Microph" in winmm. Sigh.
(Also, DirectSound shouldn't be limited to 32 chars, but its device enum
has the same problem.)
WASAPI doesn't need this. This is just for DirectSound/WinMM.
*/
char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid)
{
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
return WIN_StringToUTF8W(name); // No registry access on Xbox, go with what we've got.
#else
static const GUID nullguid = { 0 };
const unsigned char *ptr;
char keystr[128];
WCHAR *strw = NULL;
bool rc;
HKEY hkey;
DWORD len = 0;
char *result = NULL;
if (WIN_IsEqualGUID(guid, &nullguid)) {
return WIN_StringToUTF8(name); // No GUID, go with what we've got.
}
ptr = (const unsigned char *)guid;
(void)SDL_snprintf(keystr, sizeof(keystr),
"System\\CurrentControlSet\\Control\\MediaCategories\\{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
ptr[3], ptr[2], ptr[1], ptr[0], ptr[5], ptr[4], ptr[7], ptr[6],
ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]);
strw = WIN_UTF8ToString(keystr);
rc = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, strw, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS);
SDL_free(strw);
if (!rc) {
return WIN_StringToUTF8(name); // oh well.
}
rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, NULL, &len) == ERROR_SUCCESS);
if (!rc) {
RegCloseKey(hkey);
return WIN_StringToUTF8(name); // oh well.
}
strw = (WCHAR *)SDL_malloc(len + sizeof(WCHAR));
if (!strw) {
RegCloseKey(hkey);
return WIN_StringToUTF8(name); // oh well.
}
rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, (LPBYTE)strw, &len) == ERROR_SUCCESS);
RegCloseKey(hkey);
if (!rc) {
SDL_free(strw);
return WIN_StringToUTF8(name); // oh well.
}
strw[len / 2] = 0; // make sure it's null-terminated.
result = WIN_StringToUTF8(strw);
SDL_free(strw);
return result ? result : WIN_StringToUTF8(name);
#endif
}
BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b)
{
return (SDL_memcmp(a, b, sizeof(*a)) == 0);
}
BOOL WIN_IsEqualIID(REFIID a, REFIID b)
{
return (SDL_memcmp(a, b, sizeof(*a)) == 0);
}
void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect)
{
sdlrect->x = winrect->left;
sdlrect->w = (winrect->right - winrect->left) + 1;
sdlrect->y = winrect->top;
sdlrect->h = (winrect->bottom - winrect->top) + 1;
}
void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect)
{
winrect->left = sdlrect->x;
winrect->right = sdlrect->x + sdlrect->w - 1;
winrect->top = sdlrect->y;
winrect->bottom = sdlrect->y + sdlrect->h - 1;
}
bool WIN_WindowRectValid(const RECT *rect)
{
// A window can be resized to zero height, but not zero width
return (rect->right > 0);
}
// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
/* *INDENT-OFF* */ // clang-format off
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
/* *INDENT-ON* */ // clang-format on
SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat)
{
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_F32;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
return SDL_AUDIO_S16;
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_S32;
} else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat;
if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_F32;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
return SDL_AUDIO_S16;
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
return SDL_AUDIO_S32;
}
}
return SDL_AUDIO_UNKNOWN;
}
int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar)
{
if (WIN_IsWindowsXP()) {
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.
}
return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
}
#endif // defined(SDL_PLATFORM_WINDOWS)

View File

@@ -0,0 +1,172 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
// This is an include file for windows.h with the SDL build settings
#ifndef _INCLUDED_WINDOWS_H
#define _INCLUDED_WINDOWS_H
#ifdef SDL_PLATFORM_WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef STRICT
#define STRICT 1
#endif
#ifndef UNICODE
#define UNICODE 1
#endif
#undef WINVER
#undef _WIN32_WINNT
#if defined(SDL_VIDEO_RENDER_D3D12) || defined(HAVE_DXGI1_6_H)
#define _WIN32_WINNT 0xA00 // For D3D12, 0xA00 is required
#elif defined(HAVE_SHELLSCALINGAPI_H)
#define _WIN32_WINNT 0x603 // For DPI support
#else
#define _WIN32_WINNT 0x501 // Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input
#endif
#define WINVER _WIN32_WINNT
#elif defined(SDL_PLATFORM_WINGDK)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef STRICT
#define STRICT 1
#endif
#ifndef UNICODE
#define UNICODE 1
#endif
#undef WINVER
#undef _WIN32_WINNT
#define _WIN32_WINNT 0xA00
#define WINVER _WIN32_WINNT
#elif defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef STRICT
#define STRICT 1
#endif
#ifndef UNICODE
#define UNICODE 1
#endif
#undef WINVER
#undef _WIN32_WINNT
#define _WIN32_WINNT 0xA00
#define WINVER _WIN32_WINNT
#endif
// See https://github.com/libsdl-org/SDL/pull/7607
// force_align_arg_pointer attribute requires gcc >= 4.2.x.
#if defined(__clang__)
#define HAVE_FORCE_ALIGN_ARG_POINTER
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
#define HAVE_FORCE_ALIGN_ARG_POINTER
#endif
#if defined(__GNUC__) && defined(__i386__) && defined(HAVE_FORCE_ALIGN_ARG_POINTER)
#define MINGW32_FORCEALIGN __attribute__((force_align_arg_pointer))
#else
#define MINGW32_FORCEALIGN
#endif
#include <windows.h>
#include <basetyps.h> // for REFIID with broken mingw.org headers
#include <mmreg.h>
// Routines to convert from UTF8 to native Windows text
#define WIN_StringToUTF8W(S) SDL_iconv_string("UTF-8", "UTF-16LE", (const char *)(S), (SDL_wcslen(S) + 1) * sizeof(WCHAR))
#define WIN_UTF8ToStringW(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)(S), SDL_strlen(S) + 1)
// !!! FIXME: UTF8ToString() can just be a SDL_strdup() here.
#define WIN_StringToUTF8A(S) SDL_iconv_string("UTF-8", "ASCII", (const char *)(S), (SDL_strlen(S) + 1))
#define WIN_UTF8ToStringA(S) SDL_iconv_string("ASCII", "UTF-8", (const char *)(S), SDL_strlen(S) + 1)
#if UNICODE
#define WIN_StringToUTF8 WIN_StringToUTF8W
#define WIN_UTF8ToString WIN_UTF8ToStringW
#define SDL_tcslen SDL_wcslen
#define SDL_tcsstr SDL_wcsstr
#else
#define WIN_StringToUTF8 WIN_StringToUTF8A
#define WIN_UTF8ToString WIN_UTF8ToStringA
#define SDL_tcslen SDL_strlen
#define SDL_tcsstr SDL_strstr
#endif
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
// Sets an error message based on a given HRESULT
extern bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr);
// Sets an error message based on GetLastError(). Always returns false.
extern bool WIN_SetError(const char *prefix);
// Load a function from combase.dll
FARPROC WIN_LoadComBaseFunction(const char *name);
// Wrap up the oddities of CoInitialize() into a common function.
extern HRESULT WIN_CoInitialize(void);
extern void WIN_CoUninitialize(void);
// Wrap up the oddities of RoInitialize() into a common function.
extern HRESULT WIN_RoInitialize(void);
extern void WIN_RoUninitialize(void);
// Returns true if we're running on Windows XP (any service pack). DOES NOT CHECK XP "OR GREATER"!
extern BOOL WIN_IsWindowsXP(void);
// Returns true if we're running on Windows Vista and newer
extern BOOL WIN_IsWindowsVistaOrGreater(void);
// Returns true if we're running on Windows 7 and newer
extern BOOL WIN_IsWindows7OrGreater(void);
// Returns true if we're running on Windows 8 and newer
extern BOOL WIN_IsWindows8OrGreater(void);
// You need to SDL_free() the result of this call.
extern char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid);
// Checks to see if two GUID are the same.
extern BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b);
extern BOOL WIN_IsEqualIID(REFIID a, REFIID b);
// Convert between SDL_rect and RECT
extern void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect);
extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect);
// Returns false if a window client rect is not valid
bool WIN_WindowRectValid(const RECT *rect);
extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
// WideCharToMultiByte, but with some WinXP management.
extern int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
#endif // _INCLUDED_WINDOWS_H

140
thirdparty/sdl/core/windows/SDL_xinput.c vendored Normal file
View File

@@ -0,0 +1,140 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_xinput.h"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
XInputGetState_t SDL_XInputGetState = NULL;
XInputSetState_t SDL_XInputSetState = NULL;
XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx = NULL;
XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation = NULL;
DWORD SDL_XInputVersion = 0;
static HMODULE s_pXInputDLL = NULL;
static int s_XInputDLLRefCount = 0;
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
bool WIN_LoadXInputDLL(void)
{
/* Getting handles to system dlls (via LoadLibrary and its variants) is not
* supported on Xbox, thus, pointers to XInput's functions can't be
* retrieved via GetProcAddress.
*
* When on Xbox, assume that XInput is already loaded, and directly map
* its XInput.h-declared functions to the SDL_XInput* set of function
* pointers.
*/
SDL_XInputGetState = (XInputGetState_t)XInputGetState;
SDL_XInputSetState = (XInputSetState_t)XInputSetState;
SDL_XInputGetCapabilities = (XInputGetCapabilities_t)XInputGetCapabilities;
SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)XInputGetBatteryInformation;
// XInput 1.4 ships with Windows 8 and 8.1:
SDL_XInputVersion = (1 << 16) | 4;
return true;
}
void WIN_UnloadXInputDLL(void)
{
}
#else // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
bool WIN_LoadXInputDLL(void)
{
DWORD version = 0;
if (s_pXInputDLL) {
SDL_assert(s_XInputDLLRefCount > 0);
s_XInputDLLRefCount++;
return true; // already loaded
}
/* NOTE: Don't load XinputUap.dll
* This is XInput emulation over Windows.Gaming.Input, and has all the
* limitations of that API (no devices at startup, no background input, etc.)
*/
version = (1 << 16) | 4;
s_pXInputDLL = LoadLibrary(TEXT("XInput1_4.dll")); // 1.4 Ships with Windows 8.
if (!s_pXInputDLL) {
version = (1 << 16) | 3;
s_pXInputDLL = LoadLibrary(TEXT("XInput1_3.dll")); // 1.3 can be installed as a redistributable component.
}
if (!s_pXInputDLL) {
s_pXInputDLL = LoadLibrary(TEXT("bin\\XInput1_3.dll"));
}
if (!s_pXInputDLL) {
// "9.1.0" Ships with Vista and Win7, and is more limited than 1.3+ (e.g. XInputGetStateEx is not available.)
s_pXInputDLL = LoadLibrary(TEXT("XInput9_1_0.dll"));
}
if (!s_pXInputDLL) {
return false;
}
SDL_assert(s_XInputDLLRefCount == 0);
SDL_XInputVersion = version;
s_XInputDLLRefCount = 1;
// 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...
SDL_XInputGetState = (XInputGetState_t)GetProcAddress(s_pXInputDLL, (LPCSTR)100);
if (!SDL_XInputGetState) {
SDL_XInputGetState = (XInputGetState_t)GetProcAddress(s_pXInputDLL, "XInputGetState");
}
SDL_XInputSetState = (XInputSetState_t)GetProcAddress(s_pXInputDLL, "XInputSetState");
SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress(s_pXInputDLL, "XInputGetCapabilities");
// 108 is the ordinal for _XInputGetCapabilitiesEx, which additionally returns VID/PID of the controller.
SDL_XInputGetCapabilitiesEx = (XInputGetCapabilitiesEx_t)GetProcAddress(s_pXInputDLL, (LPCSTR)108);
SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)GetProcAddress(s_pXInputDLL, "XInputGetBatteryInformation");
if (!SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities) {
WIN_UnloadXInputDLL();
return false;
}
return true;
}
void WIN_UnloadXInputDLL(void)
{
if (s_pXInputDLL) {
SDL_assert(s_XInputDLLRefCount > 0);
if (--s_XInputDLLRefCount == 0) {
FreeLibrary(s_pXInputDLL);
s_pXInputDLL = NULL;
}
} else {
SDL_assert(s_XInputDLLRefCount == 0);
}
}
#endif
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif

276
thirdparty/sdl/core/windows/SDL_xinput.h vendored Normal file
View File

@@ -0,0 +1,276 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_xinput_h_
#define SDL_xinput_h_
#include "SDL_windows.h"
#ifdef HAVE_XINPUT_H
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
// Xbox supports an XInput wrapper which is a C++-only header...
#include <math.h> // Required to compile with recent MSVC...
#include <XInputOnGameInput.h>
using namespace XInputOnGameInput;
#else
#include <xinput.h>
#endif
#endif // HAVE_XINPUT_H
#ifndef XUSER_MAX_COUNT
#define XUSER_MAX_COUNT 4
#endif
#ifndef XUSER_INDEX_ANY
#define XUSER_INDEX_ANY 0x000000FF
#endif
#ifndef XINPUT_CAPS_FFB_SUPPORTED
#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
#endif
#ifndef XINPUT_CAPS_WIRELESS
#define XINPUT_CAPS_WIRELESS 0x0002
#endif
#ifndef XINPUT_DEVSUBTYPE_UNKNOWN
#define XINPUT_DEVSUBTYPE_UNKNOWN 0x00
#endif
#ifndef XINPUT_DEVSUBTYPE_GAMEPAD
#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01
#endif
#ifndef XINPUT_DEVSUBTYPE_WHEEL
#define XINPUT_DEVSUBTYPE_WHEEL 0x02
#endif
#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK
#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
#endif
#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK
#define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
#endif
#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD
#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
#endif
#ifndef XINPUT_DEVSUBTYPE_GUITAR
#define XINPUT_DEVSUBTYPE_GUITAR 0x06
#endif
#ifndef XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
#define XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE 0x07
#endif
#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT
#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
#endif
#ifndef XINPUT_DEVSUBTYPE_GUITAR_BASS
#define XINPUT_DEVSUBTYPE_GUITAR_BASS 0x0B
#endif
#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD
#define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13
#endif
#ifndef XINPUT_FLAG_GAMEPAD
#define XINPUT_FLAG_GAMEPAD 0x01
#endif
#ifndef XINPUT_GAMEPAD_DPAD_UP
#define XINPUT_GAMEPAD_DPAD_UP 0x0001
#endif
#ifndef XINPUT_GAMEPAD_DPAD_DOWN
#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
#endif
#ifndef XINPUT_GAMEPAD_DPAD_LEFT
#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
#endif
#ifndef XINPUT_GAMEPAD_DPAD_RIGHT
#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
#endif
#ifndef XINPUT_GAMEPAD_START
#define XINPUT_GAMEPAD_START 0x0010
#endif
#ifndef XINPUT_GAMEPAD_BACK
#define XINPUT_GAMEPAD_BACK 0x0020
#endif
#ifndef XINPUT_GAMEPAD_LEFT_THUMB
#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
#endif
#ifndef XINPUT_GAMEPAD_RIGHT_THUMB
#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
#endif
#ifndef XINPUT_GAMEPAD_LEFT_SHOULDER
#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
#endif
#ifndef XINPUT_GAMEPAD_RIGHT_SHOULDER
#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
#endif
#ifndef XINPUT_GAMEPAD_A
#define XINPUT_GAMEPAD_A 0x1000
#endif
#ifndef XINPUT_GAMEPAD_B
#define XINPUT_GAMEPAD_B 0x2000
#endif
#ifndef XINPUT_GAMEPAD_X
#define XINPUT_GAMEPAD_X 0x4000
#endif
#ifndef XINPUT_GAMEPAD_Y
#define XINPUT_GAMEPAD_Y 0x8000
#endif
#ifndef XINPUT_GAMEPAD_GUIDE
#define XINPUT_GAMEPAD_GUIDE 0x0400
#endif
#ifndef BATTERY_DEVTYPE_GAMEPAD
#define BATTERY_DEVTYPE_GAMEPAD 0x00
#endif
#ifndef BATTERY_TYPE_DISCONNECTED
#define BATTERY_TYPE_DISCONNECTED 0x00
#endif
#ifndef BATTERY_TYPE_WIRED
#define BATTERY_TYPE_WIRED 0x01
#endif
#ifndef BATTERY_TYPE_UNKNOWN
#define BATTERY_TYPE_UNKNOWN 0xFF
#endif
#ifndef BATTERY_LEVEL_EMPTY
#define BATTERY_LEVEL_EMPTY 0x00
#endif
#ifndef BATTERY_LEVEL_LOW
#define BATTERY_LEVEL_LOW 0x01
#endif
#ifndef BATTERY_LEVEL_MEDIUM
#define BATTERY_LEVEL_MEDIUM 0x02
#endif
#ifndef BATTERY_LEVEL_FULL
#define BATTERY_LEVEL_FULL 0x03
#endif
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
// typedef's for XInput structs we use
// This is the same as XINPUT_BATTERY_INFORMATION, but always defined instead of just if WIN32_WINNT >= _WIN32_WINNT_WIN8
typedef struct
{
BYTE BatteryType;
BYTE BatteryLevel;
} XINPUT_BATTERY_INFORMATION_EX;
#ifndef HAVE_XINPUT_H
typedef struct
{
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XINPUT_GAMEPAD;
typedef struct
{
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
} XINPUT_STATE;
typedef struct
{
WORD wLeftMotorSpeed;
WORD wRightMotorSpeed;
} XINPUT_VIBRATION;
typedef struct
{
BYTE Type;
BYTE SubType;
WORD Flags;
XINPUT_GAMEPAD Gamepad;
XINPUT_VIBRATION Vibration;
} XINPUT_CAPABILITIES;
#endif // HAVE_XINPUT_H
// This struct is not defined in XInput headers.
typedef struct
{
XINPUT_CAPABILITIES Capabilities;
WORD VendorId;
WORD ProductId;
WORD ProductVersion;
WORD unk1;
DWORD unk2;
} SDL_XINPUT_CAPABILITIES_EX;
// Forward decl's for XInput API's we load dynamically and use if available
typedef DWORD(WINAPI *XInputGetState_t)(
DWORD dwUserIndex, // [in] Index of the gamer associated with the device
XINPUT_STATE *pState // [out] Receives the current state
);
typedef DWORD(WINAPI *XInputSetState_t)(
DWORD dwUserIndex, // [in] Index of the gamer associated with the device
XINPUT_VIBRATION *pVibration // [in, out] The vibration information to send to the controller
);
typedef DWORD(WINAPI *XInputGetCapabilities_t)(
DWORD dwUserIndex, // [in] Index of the gamer associated with the device
DWORD dwFlags, // [in] Input flags that identify the device type
XINPUT_CAPABILITIES *pCapabilities // [out] Receives the capabilities
);
// Only available in XInput 1.4 that is shipped with Windows 8 and newer.
typedef DWORD(WINAPI *XInputGetCapabilitiesEx_t)(
DWORD dwReserved, // [in] Must be 1
DWORD dwUserIndex, // [in] Index of the gamer associated with the device
DWORD dwFlags, // [in] Input flags that identify the device type
SDL_XINPUT_CAPABILITIES_EX *pCapabilitiesEx // [out] Receives the capabilities
);
typedef DWORD(WINAPI *XInputGetBatteryInformation_t)(
DWORD dwUserIndex,
BYTE devType,
XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation);
extern bool WIN_LoadXInputDLL(void);
extern void WIN_UnloadXInputDLL(void);
extern XInputGetState_t SDL_XInputGetState;
extern XInputSetState_t SDL_XInputSetState;
extern XInputGetCapabilities_t SDL_XInputGetCapabilities;
extern XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx;
extern XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation;
extern DWORD SDL_XInputVersion; // ((major << 16) & 0xFF00) | (minor & 0xFF)
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
#define XINPUTGETSTATE SDL_XInputGetState
#define XINPUTSETSTATE SDL_XInputSetState
#define XINPUTGETCAPABILITIES SDL_XInputGetCapabilities
#define XINPUTGETCAPABILITIESEX SDL_XInputGetCapabilitiesEx
#define XINPUTGETBATTERYINFORMATION SDL_XInputGetBatteryInformation
#endif // SDL_xinput_h_

21
thirdparty/sdl/core/windows/pch.c vendored Normal file
View File

@@ -0,0 +1,21 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"

21
thirdparty/sdl/core/windows/pch_cpp.cpp vendored Normal file
View File

@@ -0,0 +1,21 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"