diff options
Diffstat (limited to 'contrib/SDL-3.2.8/src/joystick/windows/SDL_windowsjoystick.c')
| -rw-r--r-- | contrib/SDL-3.2.8/src/joystick/windows/SDL_windowsjoystick.c | 693 |
1 files changed, 693 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/joystick/windows/SDL_windowsjoystick.c b/contrib/SDL-3.2.8/src/joystick/windows/SDL_windowsjoystick.c new file mode 100644 index 0000000..e7fbfcb --- /dev/null +++ b/contrib/SDL-3.2.8/src/joystick/windows/SDL_windowsjoystick.c | |||
| @@ -0,0 +1,693 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
| 4 | |||
| 5 | This software is provided 'as-is', without any express or implied | ||
| 6 | warranty. In no event will the authors be held liable for any damages | ||
| 7 | arising from the use of this software. | ||
| 8 | |||
| 9 | Permission is granted to anyone to use this software for any purpose, | ||
| 10 | including commercial applications, and to alter it and redistribute it | ||
| 11 | freely, subject to the following restrictions: | ||
| 12 | |||
| 13 | 1. The origin of this software must not be misrepresented; you must not | ||
| 14 | claim that you wrote the original software. If you use this software | ||
| 15 | in a product, an acknowledgment in the product documentation would be | ||
| 16 | appreciated but is not required. | ||
| 17 | 2. Altered source versions must be plainly marked as such, and must not be | ||
| 18 | misrepresented as being the original software. | ||
| 19 | 3. This notice may not be removed or altered from any source distribution. | ||
| 20 | */ | ||
| 21 | #include "SDL_internal.h" | ||
| 22 | |||
| 23 | #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) | ||
| 24 | |||
| 25 | /* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de | ||
| 26 | * A. Formiga's WINMM driver. | ||
| 27 | * | ||
| 28 | * Hats and sliders are completely untested; the app I'm writing this for mostly | ||
| 29 | * doesn't use them and I don't own any joysticks with them. | ||
| 30 | * | ||
| 31 | * We don't bother to use event notification here. It doesn't seem to work | ||
| 32 | * with polled devices, and it's fine to call IDirectInputDevice8_GetDeviceData and | ||
| 33 | * let it return 0 events. */ | ||
| 34 | |||
| 35 | #include "../SDL_sysjoystick.h" | ||
| 36 | #include "../../thread/SDL_systhread.h" | ||
| 37 | #include "../../core/windows/SDL_windows.h" | ||
| 38 | #include "../../core/windows/SDL_hid.h" | ||
| 39 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 40 | #include <dbt.h> | ||
| 41 | #endif | ||
| 42 | |||
| 43 | #define INITGUID // Only set here, if set twice will cause mingw32 to break. | ||
| 44 | #include "SDL_windowsjoystick_c.h" | ||
| 45 | #include "SDL_dinputjoystick_c.h" | ||
| 46 | #include "SDL_xinputjoystick_c.h" | ||
| 47 | #include "SDL_rawinputjoystick_c.h" | ||
| 48 | |||
| 49 | #include "../../haptic/windows/SDL_dinputhaptic_c.h" // For haptic hot plugging | ||
| 50 | |||
| 51 | #ifndef DEVICE_NOTIFY_WINDOW_HANDLE | ||
| 52 | #define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000 | ||
| 53 | #endif | ||
| 54 | |||
| 55 | // local variables | ||
| 56 | static bool s_bJoystickThread = false; | ||
| 57 | static SDL_Condition *s_condJoystickThread = NULL; | ||
| 58 | static SDL_Mutex *s_mutexJoyStickEnum = NULL; | ||
| 59 | static SDL_Thread *s_joystickThread = NULL; | ||
| 60 | static bool s_bJoystickThreadQuit = false; | ||
| 61 | static Uint64 s_lastDeviceChange = 0; | ||
| 62 | static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; | ||
| 63 | |||
| 64 | JoyStick_DeviceData *SYS_Joystick; // array to hold joystick ID values | ||
| 65 | |||
| 66 | |||
| 67 | static bool WindowsDeviceChanged(void) | ||
| 68 | { | ||
| 69 | return (s_lastDeviceChange != WIN_GetLastDeviceNotification()); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void SetWindowsDeviceChanged(void) | ||
| 73 | { | ||
| 74 | s_lastDeviceChange = 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | void WINDOWS_RAWINPUTEnabledChanged(void) | ||
| 78 | { | ||
| 79 | SetWindowsDeviceChanged(); | ||
| 80 | } | ||
| 81 | |||
| 82 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 83 | |||
| 84 | typedef struct | ||
| 85 | { | ||
| 86 | HRESULT coinitialized; | ||
| 87 | WNDCLASSEX wincl; | ||
| 88 | HWND messageWindow; | ||
| 89 | HDEVNOTIFY hNotify; | ||
| 90 | } SDL_DeviceNotificationData; | ||
| 91 | |||
| 92 | #define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200 | ||
| 93 | #define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201 | ||
| 94 | |||
| 95 | // windowproc for our joystick detect thread message only window, to detect any USB device addition/removal | ||
| 96 | static LRESULT CALLBACK SDL_PrivateJoystickDetectProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | ||
| 97 | { | ||
| 98 | switch (msg) { | ||
| 99 | case WM_DEVICECHANGE: | ||
| 100 | switch (wParam) { | ||
| 101 | case DBT_DEVICEARRIVAL: | ||
| 102 | case DBT_DEVICEREMOVECOMPLETE: | ||
| 103 | if (((DEV_BROADCAST_HDR *)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { | ||
| 104 | // notify 300ms and 2 seconds later to ensure all APIs have updated status | ||
| 105 | SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL); | ||
| 106 | SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL); | ||
| 107 | } | ||
| 108 | break; | ||
| 109 | } | ||
| 110 | return true; | ||
| 111 | case WM_TIMER: | ||
| 112 | if (wParam == IDT_SDL_DEVICE_CHANGE_TIMER_1 || | ||
| 113 | wParam == IDT_SDL_DEVICE_CHANGE_TIMER_2) { | ||
| 114 | KillTimer(hwnd, wParam); | ||
| 115 | SetWindowsDeviceChanged(); | ||
| 116 | return true; | ||
| 117 | } | ||
| 118 | break; | ||
| 119 | } | ||
| 120 | |||
| 121 | #ifdef SDL_JOYSTICK_RAWINPUT | ||
| 122 | return CallWindowProc(RAWINPUT_WindowProc, hwnd, msg, wParam, lParam); | ||
| 123 | #else | ||
| 124 | return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); | ||
| 125 | #endif | ||
| 126 | } | ||
| 127 | |||
| 128 | static void SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) | ||
| 129 | { | ||
| 130 | #ifdef SDL_JOYSTICK_RAWINPUT | ||
| 131 | RAWINPUT_UnregisterNotifications(); | ||
| 132 | #endif | ||
| 133 | |||
| 134 | if (data->hNotify) { | ||
| 135 | UnregisterDeviceNotification(data->hNotify); | ||
| 136 | } | ||
| 137 | |||
| 138 | if (data->messageWindow) { | ||
| 139 | DestroyWindow(data->messageWindow); | ||
| 140 | } | ||
| 141 | |||
| 142 | UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance); | ||
| 143 | |||
| 144 | if (data->coinitialized == S_OK) { | ||
| 145 | WIN_CoUninitialize(); | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | static bool SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) | ||
| 150 | { | ||
| 151 | DEV_BROADCAST_DEVICEINTERFACE dbh; | ||
| 152 | |||
| 153 | SDL_zerop(data); | ||
| 154 | |||
| 155 | data->coinitialized = WIN_CoInitialize(); | ||
| 156 | |||
| 157 | data->wincl.hInstance = GetModuleHandle(NULL); | ||
| 158 | data->wincl.lpszClassName = TEXT("Message"); | ||
| 159 | data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; // This function is called by windows | ||
| 160 | data->wincl.cbSize = sizeof(WNDCLASSEX); | ||
| 161 | |||
| 162 | if (!RegisterClassEx(&data->wincl)) { | ||
| 163 | WIN_SetError("Failed to create register class for joystick autodetect"); | ||
| 164 | SDL_CleanupDeviceNotification(data); | ||
| 165 | return false; | ||
| 166 | } | ||
| 167 | |||
| 168 | data->messageWindow = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); | ||
| 169 | if (!data->messageWindow) { | ||
| 170 | WIN_SetError("Failed to create message window for joystick autodetect"); | ||
| 171 | SDL_CleanupDeviceNotification(data); | ||
| 172 | return false; | ||
| 173 | } | ||
| 174 | |||
| 175 | SDL_zero(dbh); | ||
| 176 | dbh.dbcc_size = sizeof(dbh); | ||
| 177 | dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; | ||
| 178 | dbh.dbcc_classguid = GUID_DEVINTERFACE_HID; | ||
| 179 | |||
| 180 | data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE); | ||
| 181 | if (!data->hNotify) { | ||
| 182 | WIN_SetError("Failed to create notify device for joystick autodetect"); | ||
| 183 | SDL_CleanupDeviceNotification(data); | ||
| 184 | return false; | ||
| 185 | } | ||
| 186 | |||
| 187 | #ifdef SDL_JOYSTICK_RAWINPUT | ||
| 188 | RAWINPUT_RegisterNotifications(data->messageWindow); | ||
| 189 | #endif | ||
| 190 | return true; | ||
| 191 | } | ||
| 192 | |||
| 193 | static bool SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_Mutex *mutex) | ||
| 194 | { | ||
| 195 | MSG msg; | ||
| 196 | int lastret = 1; | ||
| 197 | |||
| 198 | if (!data->messageWindow) { | ||
| 199 | return false; // device notifications require a window | ||
| 200 | } | ||
| 201 | |||
| 202 | SDL_UnlockMutex(mutex); | ||
| 203 | while (lastret > 0 && !WindowsDeviceChanged()) { | ||
| 204 | lastret = GetMessage(&msg, NULL, 0, 0); // WM_QUIT causes return value of 0 | ||
| 205 | if (lastret > 0) { | ||
| 206 | TranslateMessage(&msg); | ||
| 207 | DispatchMessage(&msg); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | SDL_LockMutex(mutex); | ||
| 211 | return (lastret != -1); | ||
| 212 | } | ||
| 213 | |||
| 214 | #endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 215 | |||
| 216 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 217 | static SDL_DeviceNotificationData s_notification_data; | ||
| 218 | #endif | ||
| 219 | |||
| 220 | // Function/thread to scan the system for joysticks. | ||
| 221 | static int SDLCALL SDL_JoystickThread(void *_data) | ||
| 222 | { | ||
| 223 | #ifdef SDL_JOYSTICK_XINPUT | ||
| 224 | bool bOpenedXInputDevices[XUSER_MAX_COUNT]; | ||
| 225 | SDL_zeroa(bOpenedXInputDevices); | ||
| 226 | #endif | ||
| 227 | |||
| 228 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 229 | if (!SDL_CreateDeviceNotification(&s_notification_data)) { | ||
| 230 | return 0; | ||
| 231 | } | ||
| 232 | #endif | ||
| 233 | |||
| 234 | SDL_LockMutex(s_mutexJoyStickEnum); | ||
| 235 | while (s_bJoystickThreadQuit == false) { | ||
| 236 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 237 | if (SDL_WaitForDeviceNotification(&s_notification_data, s_mutexJoyStickEnum) == false) { | ||
| 238 | #else | ||
| 239 | { | ||
| 240 | #endif | ||
| 241 | #ifdef SDL_JOYSTICK_XINPUT | ||
| 242 | // WM_DEVICECHANGE not working, poll for new XINPUT controllers | ||
| 243 | SDL_WaitConditionTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000); | ||
| 244 | if (SDL_XINPUT_Enabled()) { | ||
| 245 | // scan for any change in XInput devices | ||
| 246 | Uint8 userId; | ||
| 247 | for (userId = 0; userId < XUSER_MAX_COUNT; userId++) { | ||
| 248 | XINPUT_CAPABILITIES capabilities; | ||
| 249 | const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities); | ||
| 250 | const bool available = (result == ERROR_SUCCESS); | ||
| 251 | if (bOpenedXInputDevices[userId] != available) { | ||
| 252 | SetWindowsDeviceChanged(); | ||
| 253 | bOpenedXInputDevices[userId] = available; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | } | ||
| 257 | #else | ||
| 258 | // WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive | ||
| 259 | break; | ||
| 260 | #endif // SDL_JOYSTICK_XINPUT | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | SDL_UnlockMutex(s_mutexJoyStickEnum); | ||
| 265 | |||
| 266 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 267 | SDL_CleanupDeviceNotification(&s_notification_data); | ||
| 268 | #endif | ||
| 269 | |||
| 270 | return 1; | ||
| 271 | } | ||
| 272 | |||
| 273 | // spin up the thread to detect hotplug of devices | ||
| 274 | static bool SDL_StartJoystickThread(void) | ||
| 275 | { | ||
| 276 | s_mutexJoyStickEnum = SDL_CreateMutex(); | ||
| 277 | if (!s_mutexJoyStickEnum) { | ||
| 278 | return false; | ||
| 279 | } | ||
| 280 | |||
| 281 | s_condJoystickThread = SDL_CreateCondition(); | ||
| 282 | if (!s_condJoystickThread) { | ||
| 283 | return false; | ||
| 284 | } | ||
| 285 | |||
| 286 | s_bJoystickThreadQuit = false; | ||
| 287 | s_joystickThread = SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL); | ||
| 288 | if (!s_joystickThread) { | ||
| 289 | return false; | ||
| 290 | } | ||
| 291 | return true; | ||
| 292 | } | ||
| 293 | |||
| 294 | static void SDL_StopJoystickThread(void) | ||
| 295 | { | ||
| 296 | if (!s_joystickThread) { | ||
| 297 | return; | ||
| 298 | } | ||
| 299 | |||
| 300 | SDL_LockMutex(s_mutexJoyStickEnum); | ||
| 301 | s_bJoystickThreadQuit = true; | ||
| 302 | SDL_BroadcastCondition(s_condJoystickThread); // signal the joystick thread to quit | ||
| 303 | SDL_UnlockMutex(s_mutexJoyStickEnum); | ||
| 304 | PostThreadMessage((DWORD)SDL_GetThreadID(s_joystickThread), WM_QUIT, 0, 0); | ||
| 305 | |||
| 306 | // Unlock joysticks while the joystick thread finishes processing messages | ||
| 307 | SDL_AssertJoysticksLocked(); | ||
| 308 | SDL_UnlockJoysticks(); | ||
| 309 | SDL_WaitThread(s_joystickThread, NULL); // wait for it to bugger off | ||
| 310 | SDL_LockJoysticks(); | ||
| 311 | |||
| 312 | SDL_DestroyCondition(s_condJoystickThread); | ||
| 313 | s_condJoystickThread = NULL; | ||
| 314 | |||
| 315 | SDL_DestroyMutex(s_mutexJoyStickEnum); | ||
| 316 | s_mutexJoyStickEnum = NULL; | ||
| 317 | |||
| 318 | s_joystickThread = NULL; | ||
| 319 | } | ||
| 320 | |||
| 321 | void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device) | ||
| 322 | { | ||
| 323 | device->send_add_event = true; | ||
| 324 | device->nInstanceID = SDL_GetNextObjectID(); | ||
| 325 | device->pNext = SYS_Joystick; | ||
| 326 | SYS_Joystick = device; | ||
| 327 | } | ||
| 328 | |||
| 329 | void WINDOWS_JoystickDetect(void); | ||
| 330 | void WINDOWS_JoystickQuit(void); | ||
| 331 | |||
| 332 | static bool WINDOWS_JoystickInit(void) | ||
| 333 | { | ||
| 334 | if (!SDL_XINPUT_JoystickInit()) { | ||
| 335 | WINDOWS_JoystickQuit(); | ||
| 336 | return false; | ||
| 337 | } | ||
| 338 | |||
| 339 | if (!SDL_DINPUT_JoystickInit()) { | ||
| 340 | WINDOWS_JoystickQuit(); | ||
| 341 | return false; | ||
| 342 | } | ||
| 343 | |||
| 344 | WIN_InitDeviceNotification(); | ||
| 345 | |||
| 346 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 347 | s_bJoystickThread = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_THREAD, true); | ||
| 348 | if (s_bJoystickThread) { | ||
| 349 | if (!SDL_StartJoystickThread()) { | ||
| 350 | return false; | ||
| 351 | } | ||
| 352 | } else { | ||
| 353 | if (!SDL_CreateDeviceNotification(&s_notification_data)) { | ||
| 354 | return false; | ||
| 355 | } | ||
| 356 | } | ||
| 357 | #endif | ||
| 358 | |||
| 359 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 360 | // On Xbox, force create the joystick thread for device detection (since other methods don't work | ||
| 361 | s_bJoystickThread = true; | ||
| 362 | if (!SDL_StartJoystickThread()) { | ||
| 363 | return false; | ||
| 364 | } | ||
| 365 | #endif | ||
| 366 | |||
| 367 | SetWindowsDeviceChanged(); // force a scan of the system for joysticks this first time | ||
| 368 | |||
| 369 | WINDOWS_JoystickDetect(); | ||
| 370 | |||
| 371 | return true; | ||
| 372 | } | ||
| 373 | |||
| 374 | // return the number of joysticks that are connected right now | ||
| 375 | static int WINDOWS_JoystickGetCount(void) | ||
| 376 | { | ||
| 377 | int nJoysticks = 0; | ||
| 378 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 379 | while (device) { | ||
| 380 | nJoysticks++; | ||
| 381 | device = device->pNext; | ||
| 382 | } | ||
| 383 | |||
| 384 | return nJoysticks; | ||
| 385 | } | ||
| 386 | |||
| 387 | // detect any new joysticks being inserted into the system | ||
| 388 | void WINDOWS_JoystickDetect(void) | ||
| 389 | { | ||
| 390 | JoyStick_DeviceData *pCurList = NULL; | ||
| 391 | |||
| 392 | // only enum the devices if the joystick thread told us something changed | ||
| 393 | if (!WindowsDeviceChanged()) { | ||
| 394 | return; // thread hasn't signaled, nothing to do right now. | ||
| 395 | } | ||
| 396 | |||
| 397 | if (s_mutexJoyStickEnum) { | ||
| 398 | SDL_LockMutex(s_mutexJoyStickEnum); | ||
| 399 | } | ||
| 400 | |||
| 401 | s_lastDeviceChange = WIN_GetLastDeviceNotification(); | ||
| 402 | |||
| 403 | pCurList = SYS_Joystick; | ||
| 404 | SYS_Joystick = NULL; | ||
| 405 | |||
| 406 | // Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. | ||
| 407 | SDL_DINPUT_JoystickDetect(&pCurList); | ||
| 408 | |||
| 409 | // Look for XInput devices. Do this last, so they're first in the final list. | ||
| 410 | SDL_XINPUT_JoystickDetect(&pCurList); | ||
| 411 | |||
| 412 | if (s_mutexJoyStickEnum) { | ||
| 413 | SDL_UnlockMutex(s_mutexJoyStickEnum); | ||
| 414 | } | ||
| 415 | |||
| 416 | while (pCurList) { | ||
| 417 | JoyStick_DeviceData *pListNext = NULL; | ||
| 418 | |||
| 419 | if (!pCurList->bXInputDevice) { | ||
| 420 | #ifdef SDL_HAPTIC_DINPUT | ||
| 421 | SDL_DINPUT_HapticMaybeRemoveDevice(&pCurList->dxdevice); | ||
| 422 | #endif | ||
| 423 | } | ||
| 424 | |||
| 425 | SDL_PrivateJoystickRemoved(pCurList->nInstanceID); | ||
| 426 | |||
| 427 | pListNext = pCurList->pNext; | ||
| 428 | SDL_free(pCurList->joystickname); | ||
| 429 | SDL_free(pCurList); | ||
| 430 | pCurList = pListNext; | ||
| 431 | } | ||
| 432 | |||
| 433 | for (pCurList = SYS_Joystick; pCurList; pCurList = pCurList->pNext) { | ||
| 434 | if (pCurList->send_add_event) { | ||
| 435 | if (!pCurList->bXInputDevice) { | ||
| 436 | #ifdef SDL_HAPTIC_DINPUT | ||
| 437 | SDL_DINPUT_HapticMaybeAddDevice(&pCurList->dxdevice); | ||
| 438 | #endif | ||
| 439 | } | ||
| 440 | |||
| 441 | SDL_PrivateJoystickAdded(pCurList->nInstanceID); | ||
| 442 | |||
| 443 | pCurList->send_add_event = false; | ||
| 444 | } | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | static bool WINDOWS_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) | ||
| 449 | { | ||
| 450 | if (SDL_DINPUT_JoystickPresent(vendor_id, product_id, version)) { | ||
| 451 | return true; | ||
| 452 | } | ||
| 453 | if (SDL_XINPUT_JoystickPresent(vendor_id, product_id, version)) { | ||
| 454 | return true; | ||
| 455 | } | ||
| 456 | return false; | ||
| 457 | } | ||
| 458 | |||
| 459 | static const char *WINDOWS_JoystickGetDeviceName(int device_index) | ||
| 460 | { | ||
| 461 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 462 | int index; | ||
| 463 | |||
| 464 | for (index = device_index; index > 0; index--) { | ||
| 465 | device = device->pNext; | ||
| 466 | } | ||
| 467 | |||
| 468 | return device->joystickname; | ||
| 469 | } | ||
| 470 | |||
| 471 | static const char *WINDOWS_JoystickGetDevicePath(int device_index) | ||
| 472 | { | ||
| 473 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 474 | int index; | ||
| 475 | |||
| 476 | for (index = device_index; index > 0; index--) { | ||
| 477 | device = device->pNext; | ||
| 478 | } | ||
| 479 | |||
| 480 | return device->path; | ||
| 481 | } | ||
| 482 | |||
| 483 | static int WINDOWS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) | ||
| 484 | { | ||
| 485 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 486 | int index; | ||
| 487 | |||
| 488 | for (index = device_index; index > 0; index--) { | ||
| 489 | device = device->pNext; | ||
| 490 | } | ||
| 491 | |||
| 492 | if (device->bXInputDevice) { | ||
| 493 | // The slot for XInput devices can change as controllers are seated | ||
| 494 | return SDL_XINPUT_GetSteamVirtualGamepadSlot(device->XInputUserId); | ||
| 495 | } else { | ||
| 496 | return device->steam_virtual_gamepad_slot; | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | static int WINDOWS_JoystickGetDevicePlayerIndex(int device_index) | ||
| 501 | { | ||
| 502 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 503 | int index; | ||
| 504 | |||
| 505 | for (index = device_index; index > 0; index--) { | ||
| 506 | device = device->pNext; | ||
| 507 | } | ||
| 508 | |||
| 509 | return device->bXInputDevice ? (int)device->XInputUserId : -1; | ||
| 510 | } | ||
| 511 | |||
| 512 | static void WINDOWS_JoystickSetDevicePlayerIndex(int device_index, int player_index) | ||
| 513 | { | ||
| 514 | } | ||
| 515 | |||
| 516 | // return the stable device guid for this device index | ||
| 517 | static SDL_GUID WINDOWS_JoystickGetDeviceGUID(int device_index) | ||
| 518 | { | ||
| 519 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 520 | int index; | ||
| 521 | |||
| 522 | for (index = device_index; index > 0; index--) { | ||
| 523 | device = device->pNext; | ||
| 524 | } | ||
| 525 | |||
| 526 | return device->guid; | ||
| 527 | } | ||
| 528 | |||
| 529 | // Function to perform the mapping between current device instance and this joysticks instance id | ||
| 530 | static SDL_JoystickID WINDOWS_JoystickGetDeviceInstanceID(int device_index) | ||
| 531 | { | ||
| 532 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 533 | int index; | ||
| 534 | |||
| 535 | for (index = device_index; index > 0; index--) { | ||
| 536 | device = device->pNext; | ||
| 537 | } | ||
| 538 | |||
| 539 | return device->nInstanceID; | ||
| 540 | } | ||
| 541 | |||
| 542 | /* Function to open a joystick for use. | ||
| 543 | The joystick to open is specified by the device index. | ||
| 544 | This should fill the nbuttons and naxes fields of the joystick structure. | ||
| 545 | It returns 0, or -1 if there is an error. | ||
| 546 | */ | ||
| 547 | static bool WINDOWS_JoystickOpen(SDL_Joystick *joystick, int device_index) | ||
| 548 | { | ||
| 549 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 550 | int index; | ||
| 551 | |||
| 552 | for (index = device_index; index > 0; index--) { | ||
| 553 | device = device->pNext; | ||
| 554 | } | ||
| 555 | |||
| 556 | // allocate memory for system specific hardware data | ||
| 557 | joystick->hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(struct joystick_hwdata)); | ||
| 558 | if (!joystick->hwdata) { | ||
| 559 | return false; | ||
| 560 | } | ||
| 561 | joystick->hwdata->guid = device->guid; | ||
| 562 | |||
| 563 | if (device->bXInputDevice) { | ||
| 564 | return SDL_XINPUT_JoystickOpen(joystick, device); | ||
| 565 | } else { | ||
| 566 | return SDL_DINPUT_JoystickOpen(joystick, device); | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | static bool WINDOWS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) | ||
| 571 | { | ||
| 572 | if (joystick->hwdata->bXInputDevice) { | ||
| 573 | return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble); | ||
| 574 | } else { | ||
| 575 | return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | static bool WINDOWS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) | ||
| 580 | { | ||
| 581 | return SDL_Unsupported(); | ||
| 582 | } | ||
| 583 | |||
| 584 | static bool WINDOWS_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) | ||
| 585 | { | ||
| 586 | return SDL_Unsupported(); | ||
| 587 | } | ||
| 588 | |||
| 589 | static bool WINDOWS_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) | ||
| 590 | { | ||
| 591 | return SDL_Unsupported(); | ||
| 592 | } | ||
| 593 | |||
| 594 | static bool WINDOWS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled) | ||
| 595 | { | ||
| 596 | return SDL_Unsupported(); | ||
| 597 | } | ||
| 598 | |||
| 599 | static void WINDOWS_JoystickUpdate(SDL_Joystick *joystick) | ||
| 600 | { | ||
| 601 | if (!joystick->hwdata) { | ||
| 602 | return; | ||
| 603 | } | ||
| 604 | |||
| 605 | if (joystick->hwdata->bXInputDevice) { | ||
| 606 | SDL_XINPUT_JoystickUpdate(joystick); | ||
| 607 | } else { | ||
| 608 | SDL_DINPUT_JoystickUpdate(joystick); | ||
| 609 | } | ||
| 610 | } | ||
| 611 | |||
| 612 | // Function to close a joystick after use | ||
| 613 | static void WINDOWS_JoystickClose(SDL_Joystick *joystick) | ||
| 614 | { | ||
| 615 | if (joystick->hwdata->bXInputDevice) { | ||
| 616 | SDL_XINPUT_JoystickClose(joystick); | ||
| 617 | } else { | ||
| 618 | SDL_DINPUT_JoystickClose(joystick); | ||
| 619 | } | ||
| 620 | |||
| 621 | SDL_free(joystick->hwdata); | ||
| 622 | } | ||
| 623 | |||
| 624 | // Function to perform any system-specific joystick related cleanup | ||
| 625 | void WINDOWS_JoystickQuit(void) | ||
| 626 | { | ||
| 627 | JoyStick_DeviceData *device = SYS_Joystick; | ||
| 628 | |||
| 629 | while (device) { | ||
| 630 | JoyStick_DeviceData *device_next = device->pNext; | ||
| 631 | SDL_free(device->joystickname); | ||
| 632 | SDL_free(device); | ||
| 633 | device = device_next; | ||
| 634 | } | ||
| 635 | SYS_Joystick = NULL; | ||
| 636 | |||
| 637 | #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) | ||
| 638 | if (s_bJoystickThread) { | ||
| 639 | SDL_StopJoystickThread(); | ||
| 640 | } else { | ||
| 641 | SDL_CleanupDeviceNotification(&s_notification_data); | ||
| 642 | } | ||
| 643 | #endif | ||
| 644 | |||
| 645 | #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) | ||
| 646 | if (s_bJoystickThread) { | ||
| 647 | SDL_StopJoystickThread(); | ||
| 648 | } | ||
| 649 | #endif | ||
| 650 | |||
| 651 | SDL_DINPUT_JoystickQuit(); | ||
| 652 | SDL_XINPUT_JoystickQuit(); | ||
| 653 | |||
| 654 | WIN_QuitDeviceNotification(); | ||
| 655 | } | ||
| 656 | |||
| 657 | static bool WINDOWS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) | ||
| 658 | { | ||
| 659 | return false; | ||
| 660 | } | ||
| 661 | |||
| 662 | SDL_JoystickDriver SDL_WINDOWS_JoystickDriver = { | ||
| 663 | WINDOWS_JoystickInit, | ||
| 664 | WINDOWS_JoystickGetCount, | ||
| 665 | WINDOWS_JoystickDetect, | ||
| 666 | WINDOWS_JoystickIsDevicePresent, | ||
| 667 | WINDOWS_JoystickGetDeviceName, | ||
| 668 | WINDOWS_JoystickGetDevicePath, | ||
| 669 | WINDOWS_JoystickGetDeviceSteamVirtualGamepadSlot, | ||
| 670 | WINDOWS_JoystickGetDevicePlayerIndex, | ||
| 671 | WINDOWS_JoystickSetDevicePlayerIndex, | ||
| 672 | WINDOWS_JoystickGetDeviceGUID, | ||
| 673 | WINDOWS_JoystickGetDeviceInstanceID, | ||
| 674 | WINDOWS_JoystickOpen, | ||
| 675 | WINDOWS_JoystickRumble, | ||
| 676 | WINDOWS_JoystickRumbleTriggers, | ||
| 677 | WINDOWS_JoystickSetLED, | ||
| 678 | WINDOWS_JoystickSendEffect, | ||
| 679 | WINDOWS_JoystickSetSensorsEnabled, | ||
| 680 | WINDOWS_JoystickUpdate, | ||
| 681 | WINDOWS_JoystickClose, | ||
| 682 | WINDOWS_JoystickQuit, | ||
| 683 | WINDOWS_JoystickGetGamepadMapping | ||
| 684 | }; | ||
| 685 | |||
| 686 | #else | ||
| 687 | |||
| 688 | #ifdef SDL_JOYSTICK_RAWINPUT | ||
| 689 | // The RAWINPUT driver needs the device notification setup above | ||
| 690 | #error SDL_JOYSTICK_RAWINPUT requires SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT | ||
| 691 | #endif | ||
| 692 | |||
| 693 | #endif // SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT | ||
