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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
/*
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"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
extern bool SDL_DINPUT_JoystickInit(void);
extern void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext);
extern bool SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version);
extern bool SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice);
extern bool SDL_DINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
extern void SDL_DINPUT_JoystickUpdate(SDL_Joystick *joystick);
extern void SDL_DINPUT_JoystickClose(SDL_Joystick *joystick);
extern void SDL_DINPUT_JoystickQuit(void);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
/*
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 "../../core/windows/SDL_windows.h"
// Return true if the RawInput driver is enabled
extern bool RAWINPUT_IsEnabled(void);
// Registers for input events
extern int RAWINPUT_RegisterNotifications(HWND hWnd);
extern int RAWINPUT_UnregisterNotifications(void);
// Returns 0 if message was handled
extern LRESULT CALLBACK RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,693 @@
/*
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_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
/* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de
* A. Formiga's WINMM driver.
*
* Hats and sliders are completely untested; the app I'm writing this for mostly
* doesn't use them and I don't own any joysticks with them.
*
* We don't bother to use event notification here. It doesn't seem to work
* with polled devices, and it's fine to call IDirectInputDevice8_GetDeviceData and
* let it return 0 events. */
#include "../SDL_sysjoystick.h"
#include "../../thread/SDL_systhread.h"
#include "../../core/windows/SDL_windows.h"
#include "../../core/windows/SDL_hid.h"
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#include <dbt.h>
#endif
#define INITGUID // Only set here, if set twice will cause mingw32 to break.
#include "SDL_windowsjoystick_c.h"
#include "SDL_dinputjoystick_c.h"
#include "SDL_xinputjoystick_c.h"
#include "SDL_rawinputjoystick_c.h"
#include "../../haptic/windows/SDL_dinputhaptic_c.h" // For haptic hot plugging
#ifndef DEVICE_NOTIFY_WINDOW_HANDLE
#define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
#endif
// local variables
static bool s_bJoystickThread = false;
static SDL_Condition *s_condJoystickThread = NULL;
static SDL_Mutex *s_mutexJoyStickEnum = NULL;
static SDL_Thread *s_joystickThread = NULL;
static bool s_bJoystickThreadQuit = false;
static Uint64 s_lastDeviceChange = 0;
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
JoyStick_DeviceData *SYS_Joystick; // array to hold joystick ID values
static bool WindowsDeviceChanged(void)
{
return (s_lastDeviceChange != WIN_GetLastDeviceNotification());
}
static void SetWindowsDeviceChanged(void)
{
s_lastDeviceChange = 0;
}
void WINDOWS_RAWINPUTEnabledChanged(void)
{
SetWindowsDeviceChanged();
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
typedef struct
{
HRESULT coinitialized;
WNDCLASSEX wincl;
HWND messageWindow;
HDEVNOTIFY hNotify;
} SDL_DeviceNotificationData;
#define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200
#define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201
// windowproc for our joystick detect thread message only window, to detect any USB device addition/removal
static LRESULT CALLBACK SDL_PrivateJoystickDetectProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_DEVICECHANGE:
switch (wParam) {
case DBT_DEVICEARRIVAL:
case DBT_DEVICEREMOVECOMPLETE:
if (((DEV_BROADCAST_HDR *)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
// notify 300ms and 2 seconds later to ensure all APIs have updated status
SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL);
SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL);
}
break;
}
return true;
case WM_TIMER:
if (wParam == IDT_SDL_DEVICE_CHANGE_TIMER_1 ||
wParam == IDT_SDL_DEVICE_CHANGE_TIMER_2) {
KillTimer(hwnd, wParam);
SetWindowsDeviceChanged();
return true;
}
break;
}
#ifdef SDL_JOYSTICK_RAWINPUT
return CallWindowProc(RAWINPUT_WindowProc, hwnd, msg, wParam, lParam);
#else
return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
#endif
}
static void SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
{
#ifdef SDL_JOYSTICK_RAWINPUT
RAWINPUT_UnregisterNotifications();
#endif
if (data->hNotify) {
UnregisterDeviceNotification(data->hNotify);
}
if (data->messageWindow) {
DestroyWindow(data->messageWindow);
}
UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance);
if (data->coinitialized == S_OK) {
WIN_CoUninitialize();
}
}
static bool SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
{
DEV_BROADCAST_DEVICEINTERFACE dbh;
SDL_zerop(data);
data->coinitialized = WIN_CoInitialize();
data->wincl.hInstance = GetModuleHandle(NULL);
data->wincl.lpszClassName = TEXT("Message");
data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; // This function is called by windows
data->wincl.cbSize = sizeof(WNDCLASSEX);
if (!RegisterClassEx(&data->wincl)) {
WIN_SetError("Failed to create register class for joystick autodetect");
SDL_CleanupDeviceNotification(data);
return false;
}
data->messageWindow = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
if (!data->messageWindow) {
WIN_SetError("Failed to create message window for joystick autodetect");
SDL_CleanupDeviceNotification(data);
return false;
}
SDL_zero(dbh);
dbh.dbcc_size = sizeof(dbh);
dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
if (!data->hNotify) {
WIN_SetError("Failed to create notify device for joystick autodetect");
SDL_CleanupDeviceNotification(data);
return false;
}
#ifdef SDL_JOYSTICK_RAWINPUT
RAWINPUT_RegisterNotifications(data->messageWindow);
#endif
return true;
}
static bool SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_Mutex *mutex)
{
MSG msg;
int lastret = 1;
if (!data->messageWindow) {
return false; // device notifications require a window
}
SDL_UnlockMutex(mutex);
while (lastret > 0 && !WindowsDeviceChanged()) {
lastret = GetMessage(&msg, NULL, 0, 0); // WM_QUIT causes return value of 0
if (lastret > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
SDL_LockMutex(mutex);
return (lastret != -1);
}
#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
static SDL_DeviceNotificationData s_notification_data;
#endif
// Function/thread to scan the system for joysticks.
static int SDLCALL SDL_JoystickThread(void *_data)
{
#ifdef SDL_JOYSTICK_XINPUT
bool bOpenedXInputDevices[XUSER_MAX_COUNT];
SDL_zeroa(bOpenedXInputDevices);
#endif
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
if (!SDL_CreateDeviceNotification(&s_notification_data)) {
return 0;
}
#endif
SDL_LockMutex(s_mutexJoyStickEnum);
while (s_bJoystickThreadQuit == false) {
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
if (SDL_WaitForDeviceNotification(&s_notification_data, s_mutexJoyStickEnum) == false) {
#else
{
#endif
#ifdef SDL_JOYSTICK_XINPUT
// WM_DEVICECHANGE not working, poll for new XINPUT controllers
SDL_WaitConditionTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000);
if (SDL_XINPUT_Enabled()) {
// scan for any change in XInput devices
Uint8 userId;
for (userId = 0; userId < XUSER_MAX_COUNT; userId++) {
XINPUT_CAPABILITIES capabilities;
const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities);
const bool available = (result == ERROR_SUCCESS);
if (bOpenedXInputDevices[userId] != available) {
SetWindowsDeviceChanged();
bOpenedXInputDevices[userId] = available;
}
}
}
#else
// WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive
break;
#endif // SDL_JOYSTICK_XINPUT
}
}
SDL_UnlockMutex(s_mutexJoyStickEnum);
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
SDL_CleanupDeviceNotification(&s_notification_data);
#endif
return 1;
}
// spin up the thread to detect hotplug of devices
static bool SDL_StartJoystickThread(void)
{
s_mutexJoyStickEnum = SDL_CreateMutex();
if (!s_mutexJoyStickEnum) {
return false;
}
s_condJoystickThread = SDL_CreateCondition();
if (!s_condJoystickThread) {
return false;
}
s_bJoystickThreadQuit = false;
s_joystickThread = SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL);
if (!s_joystickThread) {
return false;
}
return true;
}
static void SDL_StopJoystickThread(void)
{
if (!s_joystickThread) {
return;
}
SDL_LockMutex(s_mutexJoyStickEnum);
s_bJoystickThreadQuit = true;
SDL_BroadcastCondition(s_condJoystickThread); // signal the joystick thread to quit
SDL_UnlockMutex(s_mutexJoyStickEnum);
PostThreadMessage((DWORD)SDL_GetThreadID(s_joystickThread), WM_QUIT, 0, 0);
// Unlock joysticks while the joystick thread finishes processing messages
SDL_AssertJoysticksLocked();
SDL_UnlockJoysticks();
SDL_WaitThread(s_joystickThread, NULL); // wait for it to bugger off
SDL_LockJoysticks();
SDL_DestroyCondition(s_condJoystickThread);
s_condJoystickThread = NULL;
SDL_DestroyMutex(s_mutexJoyStickEnum);
s_mutexJoyStickEnum = NULL;
s_joystickThread = NULL;
}
void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device)
{
device->send_add_event = true;
device->nInstanceID = SDL_GetNextObjectID();
device->pNext = SYS_Joystick;
SYS_Joystick = device;
}
void WINDOWS_JoystickDetect(void);
void WINDOWS_JoystickQuit(void);
static bool WINDOWS_JoystickInit(void)
{
if (!SDL_XINPUT_JoystickInit()) {
WINDOWS_JoystickQuit();
return false;
}
if (!SDL_DINPUT_JoystickInit()) {
WINDOWS_JoystickQuit();
return false;
}
WIN_InitDeviceNotification();
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
s_bJoystickThread = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_THREAD, true);
if (s_bJoystickThread) {
if (!SDL_StartJoystickThread()) {
return false;
}
} else {
if (!SDL_CreateDeviceNotification(&s_notification_data)) {
return false;
}
}
#endif
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
// On Xbox, force create the joystick thread for device detection (since other methods don't work
s_bJoystickThread = true;
if (!SDL_StartJoystickThread()) {
return false;
}
#endif
SetWindowsDeviceChanged(); // force a scan of the system for joysticks this first time
WINDOWS_JoystickDetect();
return true;
}
// return the number of joysticks that are connected right now
static int WINDOWS_JoystickGetCount(void)
{
int nJoysticks = 0;
JoyStick_DeviceData *device = SYS_Joystick;
while (device) {
nJoysticks++;
device = device->pNext;
}
return nJoysticks;
}
// detect any new joysticks being inserted into the system
void WINDOWS_JoystickDetect(void)
{
JoyStick_DeviceData *pCurList = NULL;
// only enum the devices if the joystick thread told us something changed
if (!WindowsDeviceChanged()) {
return; // thread hasn't signaled, nothing to do right now.
}
if (s_mutexJoyStickEnum) {
SDL_LockMutex(s_mutexJoyStickEnum);
}
s_lastDeviceChange = WIN_GetLastDeviceNotification();
pCurList = SYS_Joystick;
SYS_Joystick = NULL;
// Look for DirectInput joysticks, wheels, head trackers, gamepads, etc..
SDL_DINPUT_JoystickDetect(&pCurList);
// Look for XInput devices. Do this last, so they're first in the final list.
SDL_XINPUT_JoystickDetect(&pCurList);
if (s_mutexJoyStickEnum) {
SDL_UnlockMutex(s_mutexJoyStickEnum);
}
while (pCurList) {
JoyStick_DeviceData *pListNext = NULL;
if (!pCurList->bXInputDevice) {
#ifdef SDL_HAPTIC_DINPUT
SDL_DINPUT_HapticMaybeRemoveDevice(&pCurList->dxdevice);
#endif
}
SDL_PrivateJoystickRemoved(pCurList->nInstanceID);
pListNext = pCurList->pNext;
SDL_free(pCurList->joystickname);
SDL_free(pCurList);
pCurList = pListNext;
}
for (pCurList = SYS_Joystick; pCurList; pCurList = pCurList->pNext) {
if (pCurList->send_add_event) {
if (!pCurList->bXInputDevice) {
#ifdef SDL_HAPTIC_DINPUT
SDL_DINPUT_HapticMaybeAddDevice(&pCurList->dxdevice);
#endif
}
SDL_PrivateJoystickAdded(pCurList->nInstanceID);
pCurList->send_add_event = false;
}
}
}
static bool WINDOWS_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
{
if (SDL_DINPUT_JoystickPresent(vendor_id, product_id, version)) {
return true;
}
if (SDL_XINPUT_JoystickPresent(vendor_id, product_id, version)) {
return true;
}
return false;
}
static const char *WINDOWS_JoystickGetDeviceName(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
for (index = device_index; index > 0; index--) {
device = device->pNext;
}
return device->joystickname;
}
static const char *WINDOWS_JoystickGetDevicePath(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
for (index = device_index; index > 0; index--) {
device = device->pNext;
}
return device->path;
}
static int WINDOWS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
for (index = device_index; index > 0; index--) {
device = device->pNext;
}
if (device->bXInputDevice) {
// The slot for XInput devices can change as controllers are seated
return SDL_XINPUT_GetSteamVirtualGamepadSlot(device->XInputUserId);
} else {
return device->steam_virtual_gamepad_slot;
}
}
static int WINDOWS_JoystickGetDevicePlayerIndex(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
for (index = device_index; index > 0; index--) {
device = device->pNext;
}
return device->bXInputDevice ? (int)device->XInputUserId : -1;
}
static void WINDOWS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
{
}
// return the stable device guid for this device index
static SDL_GUID WINDOWS_JoystickGetDeviceGUID(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
for (index = device_index; index > 0; index--) {
device = device->pNext;
}
return device->guid;
}
// Function to perform the mapping between current device instance and this joysticks instance id
static SDL_JoystickID WINDOWS_JoystickGetDeviceInstanceID(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
for (index = device_index; index > 0; index--) {
device = device->pNext;
}
return device->nInstanceID;
}
/* Function to open a joystick for use.
The joystick to open is specified by the device index.
This should fill the nbuttons and naxes fields of the joystick structure.
It returns 0, or -1 if there is an error.
*/
static bool WINDOWS_JoystickOpen(SDL_Joystick *joystick, int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
for (index = device_index; index > 0; index--) {
device = device->pNext;
}
// allocate memory for system specific hardware data
joystick->hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(struct joystick_hwdata));
if (!joystick->hwdata) {
return false;
}
joystick->hwdata->guid = device->guid;
if (device->bXInputDevice) {
return SDL_XINPUT_JoystickOpen(joystick, device);
} else {
return SDL_DINPUT_JoystickOpen(joystick, device);
}
}
static bool WINDOWS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
if (joystick->hwdata->bXInputDevice) {
return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble);
} else {
return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble);
}
}
static bool WINDOWS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
{
return SDL_Unsupported();
}
static bool WINDOWS_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
{
return SDL_Unsupported();
}
static bool WINDOWS_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
{
return SDL_Unsupported();
}
static bool WINDOWS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)
{
return SDL_Unsupported();
}
static void WINDOWS_JoystickUpdate(SDL_Joystick *joystick)
{
if (!joystick->hwdata) {
return;
}
if (joystick->hwdata->bXInputDevice) {
SDL_XINPUT_JoystickUpdate(joystick);
} else {
SDL_DINPUT_JoystickUpdate(joystick);
}
}
// Function to close a joystick after use
static void WINDOWS_JoystickClose(SDL_Joystick *joystick)
{
if (joystick->hwdata->bXInputDevice) {
SDL_XINPUT_JoystickClose(joystick);
} else {
SDL_DINPUT_JoystickClose(joystick);
}
SDL_free(joystick->hwdata);
}
// Function to perform any system-specific joystick related cleanup
void WINDOWS_JoystickQuit(void)
{
JoyStick_DeviceData *device = SYS_Joystick;
while (device) {
JoyStick_DeviceData *device_next = device->pNext;
SDL_free(device->joystickname);
SDL_free(device);
device = device_next;
}
SYS_Joystick = NULL;
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
if (s_bJoystickThread) {
SDL_StopJoystickThread();
} else {
SDL_CleanupDeviceNotification(&s_notification_data);
}
#endif
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
if (s_bJoystickThread) {
SDL_StopJoystickThread();
}
#endif
SDL_DINPUT_JoystickQuit();
SDL_XINPUT_JoystickQuit();
WIN_QuitDeviceNotification();
}
static bool WINDOWS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
{
return false;
}
SDL_JoystickDriver SDL_WINDOWS_JoystickDriver = {
WINDOWS_JoystickInit,
WINDOWS_JoystickGetCount,
WINDOWS_JoystickDetect,
WINDOWS_JoystickIsDevicePresent,
WINDOWS_JoystickGetDeviceName,
WINDOWS_JoystickGetDevicePath,
WINDOWS_JoystickGetDeviceSteamVirtualGamepadSlot,
WINDOWS_JoystickGetDevicePlayerIndex,
WINDOWS_JoystickSetDevicePlayerIndex,
WINDOWS_JoystickGetDeviceGUID,
WINDOWS_JoystickGetDeviceInstanceID,
WINDOWS_JoystickOpen,
WINDOWS_JoystickRumble,
WINDOWS_JoystickRumbleTriggers,
WINDOWS_JoystickSetLED,
WINDOWS_JoystickSendEffect,
WINDOWS_JoystickSetSensorsEnabled,
WINDOWS_JoystickUpdate,
WINDOWS_JoystickClose,
WINDOWS_JoystickQuit,
WINDOWS_JoystickGetGamepadMapping
};
#else
#ifdef SDL_JOYSTICK_RAWINPUT
// The RAWINPUT driver needs the device notification setup above
#error SDL_JOYSTICK_RAWINPUT requires SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT
#endif
#endif // SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT

View File

@@ -0,0 +1,103 @@
/*
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_sysjoystick.h"
#include "../../core/windows/SDL_windows.h"
#include "../../core/windows/SDL_directx.h"
#define MAX_INPUTS 256 // each joystick can have up to 256 inputs
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
typedef struct JoyStick_DeviceData
{
SDL_GUID guid;
char *joystickname;
Uint8 send_add_event;
SDL_JoystickID nInstanceID;
bool bXInputDevice;
BYTE SubType;
Uint8 XInputUserId;
DIDEVICEINSTANCE dxdevice;
char path[MAX_PATH];
int steam_virtual_gamepad_slot;
struct JoyStick_DeviceData *pNext;
} JoyStick_DeviceData;
extern JoyStick_DeviceData *SYS_Joystick; // array to hold joystick ID values
typedef enum Type
{
BUTTON,
AXIS,
HAT
} Type;
typedef struct input_t
{
// DirectInput offset for this input type:
DWORD ofs;
// Button, axis or hat:
Type type;
// SDL input offset:
Uint8 num;
} input_t;
// The private structure used to keep track of a joystick
struct joystick_hwdata
{
SDL_GUID guid;
#ifdef SDL_JOYSTICK_DINPUT
LPDIRECTINPUTDEVICE8 InputDevice;
DIDEVCAPS Capabilities;
bool buffered;
bool first_update;
input_t Inputs[MAX_INPUTS];
int NumInputs;
int NumSliders;
bool ff_initialized;
DIEFFECT *ffeffect;
LPDIRECTINPUTEFFECT ffeffect_ref;
#endif
bool bXInputDevice; // true if this device supports using the xinput API rather than DirectInput
bool bXInputHaptic; // Supports force feedback via XInput.
Uint8 userid; // XInput userid index for this joystick
DWORD dwPacketNumber;
};
#ifdef SDL_JOYSTICK_DINPUT
extern const DIDATAFORMAT SDL_c_dfDIJoystick2;
#endif
extern void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,473 @@
/*
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_sysjoystick.h"
#ifdef SDL_JOYSTICK_XINPUT
#include "SDL_windowsjoystick_c.h"
#include "SDL_xinputjoystick_c.h"
#include "SDL_rawinputjoystick_c.h"
#include "../hidapi/SDL_hidapijoystick_c.h"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
/*
* Internal stuff.
*/
static bool s_bXInputEnabled = false;
bool SDL_XINPUT_Enabled(void)
{
return s_bXInputEnabled;
}
bool SDL_XINPUT_JoystickInit(void)
{
bool enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, true);
if (enabled && !WIN_LoadXInputDLL()) {
enabled = false; // oh well.
}
s_bXInputEnabled = enabled;
return true;
}
static const char *GetXInputName(const Uint8 userid, BYTE SubType)
{
static char name[32];
switch (SubType) {
case XINPUT_DEVSUBTYPE_GAMEPAD:
(void)SDL_snprintf(name, sizeof(name), "XInput Controller #%d", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_WHEEL:
(void)SDL_snprintf(name, sizeof(name), "XInput Wheel #%d", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_ARCADE_STICK:
(void)SDL_snprintf(name, sizeof(name), "XInput ArcadeStick #%d", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
(void)SDL_snprintf(name, sizeof(name), "XInput FlightStick #%d", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_DANCE_PAD:
(void)SDL_snprintf(name, sizeof(name), "XInput DancePad #%d", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_GUITAR:
case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
case XINPUT_DEVSUBTYPE_GUITAR_BASS:
(void)SDL_snprintf(name, sizeof(name), "XInput Guitar #%d", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_DRUM_KIT:
(void)SDL_snprintf(name, sizeof(name), "XInput DrumKit #%d", 1 + userid);
break;
case XINPUT_DEVSUBTYPE_ARCADE_PAD:
(void)SDL_snprintf(name, sizeof(name), "XInput ArcadePad #%d", 1 + userid);
break;
default:
(void)SDL_snprintf(name, sizeof(name), "XInput Device #%d", 1 + userid);
break;
}
return name;
}
static bool GetXInputDeviceInfo(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Uint16 *pVersion)
{
SDL_XINPUT_CAPABILITIES_EX capabilities;
if (!XINPUTGETCAPABILITIESEX || XINPUTGETCAPABILITIESEX(1, userid, 0, &capabilities) != ERROR_SUCCESS) {
// Use a generic VID/PID representing an XInput controller
if (pVID) {
*pVID = USB_VENDOR_MICROSOFT;
}
if (pPID) {
*pPID = USB_PRODUCT_XBOX360_XUSB_CONTROLLER;
}
return false;
}
// Fixup for Wireless Xbox 360 Controller
if (capabilities.ProductId == 0 && capabilities.Capabilities.Flags & XINPUT_CAPS_WIRELESS) {
capabilities.VendorId = USB_VENDOR_MICROSOFT;
capabilities.ProductId = USB_PRODUCT_XBOX360_XUSB_CONTROLLER;
}
if (pVID) {
*pVID = capabilities.VendorId;
}
if (pPID) {
*pPID = capabilities.ProductId;
}
if (pVersion) {
*pVersion = capabilities.ProductVersion;
}
return true;
}
int SDL_XINPUT_GetSteamVirtualGamepadSlot(Uint8 userid)
{
SDL_XINPUT_CAPABILITIES_EX capabilities;
if (XINPUTGETCAPABILITIESEX &&
XINPUTGETCAPABILITIESEX(1, userid, 0, &capabilities) == ERROR_SUCCESS &&
capabilities.VendorId == USB_VENDOR_VALVE &&
capabilities.ProductId == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {
return (int)capabilities.unk2;
}
return -1;
}
static void AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
{
const char *name = NULL;
Uint16 vendor = 0;
Uint16 product = 0;
Uint16 version = 0;
JoyStick_DeviceData *pPrevJoystick = NULL;
JoyStick_DeviceData *pNewJoystick = *pContext;
#ifdef SDL_JOYSTICK_RAWINPUT
if (RAWINPUT_IsEnabled()) {
// The raw input driver handles more than 4 controllers, so prefer that when available
/* We do this check here rather than at the top of SDL_XINPUT_JoystickDetect() because
we need to check XInput state before RAWINPUT gets a hold of the device, otherwise
when a controller is connected via the wireless adapter, it will shut down at the
first subsequent XInput call. This seems like a driver stack bug?
Reference: https://github.com/libsdl-org/SDL/issues/3468
*/
return;
}
#endif
if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN) {
return;
}
while (pNewJoystick) {
if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) {
// if we are replacing the front of the list then update it
if (pNewJoystick == *pContext) {
*pContext = pNewJoystick->pNext;
} else if (pPrevJoystick) {
pPrevJoystick->pNext = pNewJoystick->pNext;
}
pNewJoystick->pNext = SYS_Joystick;
SYS_Joystick = pNewJoystick;
return; // already in the list.
}
pPrevJoystick = pNewJoystick;
pNewJoystick = pNewJoystick->pNext;
}
name = GetXInputName(userid, SubType);
GetXInputDeviceInfo(userid, &vendor, &product, &version);
if (SDL_ShouldIgnoreJoystick(vendor, product, version, name) ||
SDL_JoystickHandledByAnotherDriver(&SDL_WINDOWS_JoystickDriver, vendor, product, version, name)) {
return;
}
pNewJoystick = (JoyStick_DeviceData *)SDL_calloc(1, sizeof(JoyStick_DeviceData));
if (!pNewJoystick) {
return; // better luck next time?
}
pNewJoystick->bXInputDevice = true;
pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, NULL, name);
if (!pNewJoystick->joystickname) {
SDL_free(pNewJoystick);
return; // better luck next time?
}
(void)SDL_snprintf(pNewJoystick->path, sizeof(pNewJoystick->path), "XInput#%u", userid);
pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, vendor, product, version, NULL, name, 'x', SubType);
pNewJoystick->SubType = SubType;
pNewJoystick->XInputUserId = userid;
WINDOWS_AddJoystickDevice(pNewJoystick);
}
void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
{
int iuserid;
if (!s_bXInputEnabled) {
return;
}
// iterate in reverse, so these are in the final list in ascending numeric order.
for (iuserid = XUSER_MAX_COUNT - 1; iuserid >= 0; iuserid--) {
const Uint8 userid = (Uint8)iuserid;
XINPUT_CAPABILITIES capabilities;
if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
AddXInputDevice(userid, capabilities.SubType, pContext);
}
}
}
bool SDL_XINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)
{
int iuserid;
if (!s_bXInputEnabled) {
return false;
}
// iterate in reverse, so these are in the final list in ascending numeric order.
for (iuserid = 0; iuserid < XUSER_MAX_COUNT; ++iuserid) {
const Uint8 userid = (Uint8)iuserid;
Uint16 slot_vendor;
Uint16 slot_product;
Uint16 slot_version;
if (GetXInputDeviceInfo(userid, &slot_vendor, &slot_product, &slot_version)) {
if (vendor == slot_vendor && product == slot_product && version == slot_version) {
return true;
}
}
}
return false;
}
bool SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
{
const Uint8 userId = joystickdevice->XInputUserId;
XINPUT_CAPABILITIES capabilities;
XINPUT_VIBRATION state;
SDL_assert(s_bXInputEnabled);
SDL_assert(XINPUTGETCAPABILITIES);
SDL_assert(XINPUTSETSTATE);
SDL_assert(userId < XUSER_MAX_COUNT);
joystick->hwdata->bXInputDevice = true;
if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
SDL_free(joystick->hwdata);
joystick->hwdata = NULL;
return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
}
SDL_zero(state);
joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS);
joystick->hwdata->userid = userId;
// The XInput API has a hard coded button/axis mapping, so we just match it
joystick->naxes = 6;
joystick->nbuttons = 11;
joystick->nhats = 1;
SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);
return true;
}
static void UpdateXInputJoystickBatteryInformation(SDL_Joystick *joystick, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
{
SDL_PowerState state;
int percent;
switch (pBatteryInformation->BatteryType) {
case BATTERY_TYPE_WIRED:
state = SDL_POWERSTATE_CHARGING;
break;
case BATTERY_TYPE_UNKNOWN:
case BATTERY_TYPE_DISCONNECTED:
state = SDL_POWERSTATE_UNKNOWN;
break;
default:
state = SDL_POWERSTATE_ON_BATTERY;
break;
}
switch (pBatteryInformation->BatteryLevel) {
case BATTERY_LEVEL_EMPTY:
percent = 10;
break;
case BATTERY_LEVEL_LOW:
percent = 40;
break;
case BATTERY_LEVEL_MEDIUM:
percent = 70;
break;
default:
case BATTERY_LEVEL_FULL:
percent = 100;
break;
}
SDL_SendJoystickPowerInfo(joystick, state, percent);
}
static void UpdateXInputJoystickState(SDL_Joystick *joystick, XINPUT_STATE *pXInputState, XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation)
{
static WORD s_XInputButtons[] = {
XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START,
XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB,
XINPUT_GAMEPAD_GUIDE
};
WORD wButtons = pXInputState->Gamepad.wButtons;
Uint8 button;
Uint8 hat = SDL_HAT_CENTERED;
Uint64 timestamp = SDL_GetTicksNS();
SDL_SendJoystickAxis(timestamp, joystick, 0, pXInputState->Gamepad.sThumbLX);
SDL_SendJoystickAxis(timestamp, joystick, 1, ~pXInputState->Gamepad.sThumbLY);
SDL_SendJoystickAxis(timestamp, joystick, 2, ((int)pXInputState->Gamepad.bLeftTrigger * 257) - 32768);
SDL_SendJoystickAxis(timestamp, joystick, 3, pXInputState->Gamepad.sThumbRX);
SDL_SendJoystickAxis(timestamp, joystick, 4, ~pXInputState->Gamepad.sThumbRY);
SDL_SendJoystickAxis(timestamp, joystick, 5, ((int)pXInputState->Gamepad.bRightTrigger * 257) - 32768);
for (button = 0; button < (Uint8)SDL_arraysize(s_XInputButtons); ++button) {
bool down = ((wButtons & s_XInputButtons[button]) != 0);
SDL_SendJoystickButton(timestamp, joystick, button, down);
}
if (wButtons & XINPUT_GAMEPAD_DPAD_UP) {
hat |= SDL_HAT_UP;
}
if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
hat |= SDL_HAT_DOWN;
}
if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
hat |= SDL_HAT_LEFT;
}
if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
hat |= SDL_HAT_RIGHT;
}
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation);
}
bool SDL_XINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
XINPUT_VIBRATION XVibration;
if (!XINPUTSETSTATE) {
return SDL_Unsupported();
}
XVibration.wLeftMotorSpeed = low_frequency_rumble;
XVibration.wRightMotorSpeed = high_frequency_rumble;
if (XINPUTSETSTATE(joystick->hwdata->userid, &XVibration) != ERROR_SUCCESS) {
return SDL_SetError("XInputSetState() failed");
}
return true;
}
void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick)
{
DWORD result;
XINPUT_STATE XInputState;
XINPUT_BATTERY_INFORMATION_EX XBatteryInformation;
if (!XINPUTGETSTATE) {
return;
}
result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
if (result == ERROR_DEVICE_NOT_CONNECTED) {
return;
}
SDL_zero(XBatteryInformation);
if (XINPUTGETBATTERYINFORMATION) {
result = XINPUTGETBATTERYINFORMATION(joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation);
}
#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
// XInputOnGameInput doesn't ever change dwPacketNumber, so have to just update every frame
UpdateXInputJoystickState(joystick, &XInputState, &XBatteryInformation);
#else
// only fire events if the data changed from last time
if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
UpdateXInputJoystickState(joystick, &XInputState, &XBatteryInformation);
joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
}
#endif
}
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
{
}
void SDL_XINPUT_JoystickQuit(void)
{
if (s_bXInputEnabled) {
s_bXInputEnabled = false;
WIN_UnloadXInputDLL();
}
}
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
#else // !SDL_JOYSTICK_XINPUT
typedef struct JoyStick_DeviceData JoyStick_DeviceData;
bool SDL_XINPUT_Enabled(void)
{
return false;
}
bool SDL_XINPUT_JoystickInit(void)
{
return true;
}
void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
{
}
bool SDL_XINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)
{
return false;
}
bool SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)
{
return SDL_Unsupported();
}
bool SDL_XINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
return SDL_Unsupported();
}
void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick)
{
}
void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick)
{
}
void SDL_XINPUT_JoystickQuit(void)
{
}
#endif // SDL_JOYSTICK_XINPUT

View File

@@ -0,0 +1,44 @@
/*
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 "../../core/windows/SDL_xinput.h"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
extern bool SDL_XINPUT_Enabled(void);
extern bool SDL_XINPUT_JoystickInit(void);
extern void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext);
extern bool SDL_XINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version);
extern bool SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice);
extern bool SDL_XINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
extern void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick);
extern void SDL_XINPUT_JoystickClose(SDL_Joystick *joystick);
extern void SDL_XINPUT_JoystickQuit(void);
extern int SDL_XINPUT_GetSteamVirtualGamepadSlot(Uint8 userid);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif