SDL_windowsjoystick.c (18761B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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 SDL_JOYSTICK_DINPUT || 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_error.h" 36 #include "SDL_events.h" 37 #include "SDL_hints.h" 38 #include "SDL_timer.h" 39 #include "SDL_mutex.h" 40 #include "SDL_joystick.h" 41 #include "../SDL_sysjoystick.h" 42 #include "../../thread/SDL_systhread.h" 43 #include "../../core/windows/SDL_windows.h" 44 #if !defined(__WINRT__) 45 #include <dbt.h> 46 #endif 47 48 #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */ 49 #include "SDL_windowsjoystick_c.h" 50 #include "SDL_dinputjoystick_c.h" 51 #include "SDL_xinputjoystick_c.h" 52 #include "SDL_rawinputjoystick_c.h" 53 54 #include "../../haptic/windows/SDL_dinputhaptic_c.h" /* For haptic hot plugging */ 55 #include "../../haptic/windows/SDL_xinputhaptic_c.h" /* For haptic hot plugging */ 56 57 58 #ifndef DEVICE_NOTIFY_WINDOW_HANDLE 59 #define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000 60 #endif 61 62 /* local variables */ 63 static SDL_bool s_bJoystickThread = SDL_FALSE; 64 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE; 65 static SDL_cond *s_condJoystickThread = NULL; 66 static SDL_mutex *s_mutexJoyStickEnum = NULL; 67 static SDL_Thread *s_joystickThread = NULL; 68 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE; 69 70 JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */ 71 72 #ifdef __WINRT__ 73 74 typedef struct 75 { 76 int unused; 77 } SDL_DeviceNotificationData; 78 79 static void 80 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) 81 { 82 } 83 84 static int 85 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) 86 { 87 return 0; 88 } 89 90 static SDL_bool 91 SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex) 92 { 93 return SDL_FALSE; 94 } 95 96 #else /* !__WINRT__ */ 97 98 typedef struct 99 { 100 HRESULT coinitialized; 101 WNDCLASSEX wincl; 102 HWND messageWindow; 103 HDEVNOTIFY hNotify; 104 } SDL_DeviceNotificationData; 105 106 #define IDT_SDL_DEVICE_CHANGE_TIMER_1 1200 107 #define IDT_SDL_DEVICE_CHANGE_TIMER_2 1201 108 109 /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */ 110 static LRESULT CALLBACK 111 SDL_PrivateJoystickDetectProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 112 { 113 switch (msg) { 114 case WM_DEVICECHANGE: 115 switch (wParam) { 116 case DBT_DEVICEARRIVAL: 117 case DBT_DEVICEREMOVECOMPLETE: 118 if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { 119 /* notify 300ms and 2 seconds later to ensure all APIs have updated status */ 120 SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_1, 300, NULL); 121 SetTimer(hwnd, IDT_SDL_DEVICE_CHANGE_TIMER_2, 2000, NULL); 122 } 123 break; 124 } 125 return 0; 126 case WM_TIMER: 127 if (wParam == IDT_SDL_DEVICE_CHANGE_TIMER_1 || 128 wParam == IDT_SDL_DEVICE_CHANGE_TIMER_2) { 129 KillTimer(hwnd, wParam); 130 s_bWindowsDeviceChanged = SDL_TRUE; 131 return 0; 132 } 133 break; 134 } 135 136 #if SDL_JOYSTICK_RAWINPUT 137 return CallWindowProc(RAWINPUT_WindowProc, hwnd, msg, wParam, lParam); 138 #else 139 return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); 140 #endif 141 } 142 143 static void 144 SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) 145 { 146 #if SDL_JOYSTICK_RAWINPUT 147 RAWINPUT_UnregisterNotifications(); 148 #endif 149 150 if (data->hNotify) 151 UnregisterDeviceNotification(data->hNotify); 152 153 if (data->messageWindow) 154 DestroyWindow(data->messageWindow); 155 156 UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance); 157 158 if (data->coinitialized == S_OK) { 159 WIN_CoUninitialize(); 160 } 161 } 162 163 static int 164 SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) 165 { 166 DEV_BROADCAST_DEVICEINTERFACE dbh; 167 GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; 168 169 SDL_zerop(data); 170 171 data->coinitialized = WIN_CoInitialize(); 172 173 data->wincl.hInstance = GetModuleHandle(NULL); 174 data->wincl.lpszClassName = L"Message"; 175 data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; /* This function is called by windows */ 176 data->wincl.cbSize = sizeof (WNDCLASSEX); 177 178 if (!RegisterClassEx(&data->wincl)) { 179 WIN_SetError("Failed to create register class for joystick autodetect"); 180 SDL_CleanupDeviceNotification(data); 181 return -1; 182 } 183 184 data->messageWindow = (HWND)CreateWindowEx(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); 185 if (!data->messageWindow) { 186 WIN_SetError("Failed to create message window for joystick autodetect"); 187 SDL_CleanupDeviceNotification(data); 188 return -1; 189 } 190 191 SDL_zero(dbh); 192 dbh.dbcc_size = sizeof(dbh); 193 dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 194 dbh.dbcc_classguid = GUID_DEVINTERFACE_HID; 195 196 data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE); 197 if (!data->hNotify) { 198 WIN_SetError("Failed to create notify device for joystick autodetect"); 199 SDL_CleanupDeviceNotification(data); 200 return -1; 201 } 202 203 #if SDL_JOYSTICK_RAWINPUT 204 RAWINPUT_RegisterNotifications(data->messageWindow); 205 #endif 206 return 0; 207 } 208 209 static SDL_bool 210 SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex) 211 { 212 MSG msg; 213 int lastret = 1; 214 215 if (!data->messageWindow) { 216 return SDL_FALSE; /* device notifications require a window */ 217 } 218 219 SDL_UnlockMutex(mutex); 220 while (lastret > 0 && s_bWindowsDeviceChanged == SDL_FALSE) { 221 lastret = GetMessage(&msg, NULL, 0, 0); /* WM_QUIT causes return value of 0 */ 222 if (lastret > 0) { 223 TranslateMessage(&msg); 224 DispatchMessage(&msg); 225 } 226 } 227 SDL_LockMutex(mutex); 228 return (lastret != -1) ? SDL_TRUE : SDL_FALSE; 229 } 230 231 #endif /* __WINRT__ */ 232 233 static SDL_DeviceNotificationData s_notification_data; 234 235 /* Function/thread to scan the system for joysticks. */ 236 static int 237 SDL_JoystickThread(void *_data) 238 { 239 #if SDL_JOYSTICK_XINPUT 240 SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT]; 241 SDL_zeroa(bOpenedXInputDevices); 242 #endif 243 244 if (SDL_CreateDeviceNotification(&s_notification_data) < 0) { 245 return -1; 246 } 247 248 SDL_LockMutex(s_mutexJoyStickEnum); 249 while (s_bJoystickThreadQuit == SDL_FALSE) { 250 if (SDL_WaitForDeviceNotification(&s_notification_data, s_mutexJoyStickEnum) == SDL_FALSE) { 251 #if SDL_JOYSTICK_XINPUT 252 /* WM_DEVICECHANGE not working, poll for new XINPUT controllers */ 253 SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 1000); 254 if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) { 255 /* scan for any change in XInput devices */ 256 Uint8 userId; 257 for (userId = 0; userId < XUSER_MAX_COUNT; userId++) { 258 XINPUT_CAPABILITIES capabilities; 259 const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities); 260 const SDL_bool available = (result == ERROR_SUCCESS); 261 if (bOpenedXInputDevices[userId] != available) { 262 s_bWindowsDeviceChanged = SDL_TRUE; 263 bOpenedXInputDevices[userId] = available; 264 } 265 } 266 } 267 #else 268 /* WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive */ 269 break; 270 #endif /* SDL_JOYSTICK_XINPUT */ 271 } 272 } 273 SDL_UnlockMutex(s_mutexJoyStickEnum); 274 275 SDL_CleanupDeviceNotification(&s_notification_data); 276 277 return 1; 278 } 279 280 /* spin up the thread to detect hotplug of devices */ 281 static int 282 SDL_StartJoystickThread(void) 283 { 284 s_mutexJoyStickEnum = SDL_CreateMutex(); 285 if (!s_mutexJoyStickEnum) { 286 return -1; 287 } 288 289 s_condJoystickThread = SDL_CreateCond(); 290 if (!s_condJoystickThread) { 291 return -1; 292 } 293 294 s_bJoystickThreadQuit = SDL_FALSE; 295 s_joystickThread = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL); 296 if (!s_joystickThread) { 297 return -1; 298 } 299 return 0; 300 } 301 302 static void 303 SDL_StopJoystickThread(void) 304 { 305 if (!s_joystickThread) { 306 return; 307 } 308 309 SDL_LockMutex(s_mutexJoyStickEnum); 310 s_bJoystickThreadQuit = SDL_TRUE; 311 SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */ 312 SDL_UnlockMutex(s_mutexJoyStickEnum); 313 #ifndef __WINRT__ 314 PostThreadMessage(SDL_GetThreadID(s_joystickThread), WM_QUIT, 0, 0); 315 #endif 316 SDL_WaitThread(s_joystickThread, NULL); /* wait for it to bugger off */ 317 318 SDL_DestroyCond(s_condJoystickThread); 319 s_condJoystickThread = NULL; 320 321 SDL_DestroyMutex(s_mutexJoyStickEnum); 322 s_mutexJoyStickEnum = NULL; 323 324 s_joystickThread = NULL; 325 } 326 327 void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device) 328 { 329 device->send_add_event = SDL_TRUE; 330 device->nInstanceID = SDL_GetNextJoystickInstanceID(); 331 device->pNext = SYS_Joystick; 332 SYS_Joystick = device; 333 } 334 335 static void WINDOWS_JoystickDetect(void); 336 static void WINDOWS_JoystickQuit(void); 337 338 /* Function to scan the system for joysticks. 339 * Joystick 0 should be the system default joystick. 340 * It should return 0, or -1 on an unrecoverable fatal error. 341 */ 342 static int 343 WINDOWS_JoystickInit(void) 344 { 345 if (SDL_DINPUT_JoystickInit() < 0) { 346 WINDOWS_JoystickQuit(); 347 return -1; 348 } 349 350 if (SDL_XINPUT_JoystickInit() < 0) { 351 WINDOWS_JoystickQuit(); 352 return -1; 353 } 354 355 s_bWindowsDeviceChanged = SDL_TRUE; /* force a scan of the system for joysticks this first time */ 356 357 WINDOWS_JoystickDetect(); 358 359 s_bJoystickThread = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_THREAD, SDL_FALSE); 360 if (s_bJoystickThread) { 361 if (SDL_StartJoystickThread() < 0) { 362 return -1; 363 } 364 } else { 365 if (SDL_CreateDeviceNotification(&s_notification_data) < 0) { 366 return -1; 367 } 368 } 369 return 0; 370 } 371 372 /* return the number of joysticks that are connected right now */ 373 static int 374 WINDOWS_JoystickGetCount(void) 375 { 376 int nJoysticks = 0; 377 JoyStick_DeviceData *device = SYS_Joystick; 378 while (device) { 379 nJoysticks++; 380 device = device->pNext; 381 } 382 383 return nJoysticks; 384 } 385 386 /* detect any new joysticks being inserted into the system */ 387 static void 388 WINDOWS_JoystickDetect(void) 389 { 390 int device_index = 0; 391 JoyStick_DeviceData *pCurList = NULL; 392 393 /* only enum the devices if the joystick thread told us something changed */ 394 if (!s_bWindowsDeviceChanged) { 395 return; /* thread hasn't signaled, nothing to do right now. */ 396 } 397 398 if (s_mutexJoyStickEnum) { 399 SDL_LockMutex(s_mutexJoyStickEnum); 400 } 401 402 s_bWindowsDeviceChanged = SDL_FALSE; 403 404 pCurList = SYS_Joystick; 405 SYS_Joystick = NULL; 406 407 /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */ 408 SDL_DINPUT_JoystickDetect(&pCurList); 409 410 /* Look for XInput devices. Do this last, so they're first in the final list. */ 411 SDL_XINPUT_JoystickDetect(&pCurList); 412 413 if (s_mutexJoyStickEnum) { 414 SDL_UnlockMutex(s_mutexJoyStickEnum); 415 } 416 417 while (pCurList) { 418 JoyStick_DeviceData *pListNext = NULL; 419 420 if (pCurList->bXInputDevice) { 421 #if SDL_HAPTIC_XINPUT 422 SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId); 423 #endif 424 } else { 425 #if SDL_HAPTIC_DINPUT 426 SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice); 427 #endif 428 } 429 430 SDL_PrivateJoystickRemoved(pCurList->nInstanceID); 431 432 pListNext = pCurList->pNext; 433 SDL_free(pCurList->joystickname); 434 SDL_free(pCurList); 435 pCurList = pListNext; 436 } 437 438 for (device_index = 0, pCurList = SYS_Joystick; pCurList; ++device_index, pCurList = pCurList->pNext) { 439 if (pCurList->send_add_event) { 440 if (pCurList->bXInputDevice) { 441 #if SDL_HAPTIC_XINPUT 442 SDL_XINPUT_MaybeAddDevice(pCurList->XInputUserId); 443 #endif 444 } else { 445 #if SDL_HAPTIC_DINPUT 446 SDL_DINPUT_MaybeAddDevice(&pCurList->dxdevice); 447 #endif 448 } 449 450 SDL_PrivateJoystickAdded(pCurList->nInstanceID); 451 452 pCurList->send_add_event = SDL_FALSE; 453 } 454 } 455 } 456 457 /* Function to get the device-dependent name of a joystick */ 458 static const char * 459 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 return device->joystickname; 468 } 469 470 static int 471 WINDOWS_JoystickGetDevicePlayerIndex(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 return device->bXInputDevice ? (int)device->XInputUserId : -1; 480 } 481 482 static void 483 WINDOWS_JoystickSetDevicePlayerIndex(int device_index, int player_index) 484 { 485 } 486 487 /* return the stable device guid for this device index */ 488 static SDL_JoystickGUID 489 WINDOWS_JoystickGetDeviceGUID(int device_index) 490 { 491 JoyStick_DeviceData *device = SYS_Joystick; 492 int index; 493 494 for (index = device_index; index > 0; index--) 495 device = device->pNext; 496 497 return device->guid; 498 } 499 500 /* Function to perform the mapping between current device instance and this joysticks instance id */ 501 static SDL_JoystickID 502 WINDOWS_JoystickGetDeviceInstanceID(int device_index) 503 { 504 JoyStick_DeviceData *device = SYS_Joystick; 505 int index; 506 507 for (index = device_index; index > 0; index--) 508 device = device->pNext; 509 510 return device->nInstanceID; 511 } 512 513 /* Function to open a joystick for use. 514 The joystick to open is specified by the device index. 515 This should fill the nbuttons and naxes fields of the joystick structure. 516 It returns 0, or -1 if there is an error. 517 */ 518 static int 519 WINDOWS_JoystickOpen(SDL_Joystick * joystick, int device_index) 520 { 521 JoyStick_DeviceData *device = SYS_Joystick; 522 int index; 523 524 for (index = device_index; index > 0; index--) 525 device = device->pNext; 526 527 /* allocate memory for system specific hardware data */ 528 joystick->instance_id = device->nInstanceID; 529 joystick->hwdata = 530 (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata)); 531 if (joystick->hwdata == NULL) { 532 return SDL_OutOfMemory(); 533 } 534 SDL_zerop(joystick->hwdata); 535 joystick->hwdata->guid = device->guid; 536 537 if (device->bXInputDevice) { 538 return SDL_XINPUT_JoystickOpen(joystick, device); 539 } else { 540 return SDL_DINPUT_JoystickOpen(joystick, device); 541 } 542 } 543 544 static int 545 WINDOWS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 546 { 547 if (joystick->hwdata->bXInputDevice) { 548 return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble); 549 } else { 550 return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble); 551 } 552 } 553 554 static int 555 WINDOWS_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) 556 { 557 return SDL_Unsupported(); 558 } 559 560 static SDL_bool 561 WINDOWS_JoystickHasLED(SDL_Joystick * joystick) 562 { 563 return SDL_FALSE; 564 } 565 566 static int 567 WINDOWS_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) 568 { 569 return SDL_Unsupported(); 570 } 571 572 static int 573 WINDOWS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) 574 { 575 return SDL_Unsupported(); 576 } 577 578 static void 579 WINDOWS_JoystickUpdate(SDL_Joystick * joystick) 580 { 581 if (!joystick->hwdata) { 582 return; 583 } 584 585 if (joystick->hwdata->bXInputDevice) { 586 SDL_XINPUT_JoystickUpdate(joystick); 587 } else { 588 SDL_DINPUT_JoystickUpdate(joystick); 589 } 590 } 591 592 /* Function to close a joystick after use */ 593 static void 594 WINDOWS_JoystickClose(SDL_Joystick * joystick) 595 { 596 if (joystick->hwdata->bXInputDevice) { 597 SDL_XINPUT_JoystickClose(joystick); 598 } else { 599 SDL_DINPUT_JoystickClose(joystick); 600 } 601 602 SDL_free(joystick->hwdata); 603 } 604 605 /* Function to perform any system-specific joystick related cleanup */ 606 static void 607 WINDOWS_JoystickQuit(void) 608 { 609 JoyStick_DeviceData *device = SYS_Joystick; 610 611 while (device) { 612 JoyStick_DeviceData *device_next = device->pNext; 613 SDL_free(device->joystickname); 614 SDL_free(device); 615 device = device_next; 616 } 617 SYS_Joystick = NULL; 618 619 if (s_bJoystickThread) { 620 SDL_StopJoystickThread(); 621 } else { 622 SDL_CleanupDeviceNotification(&s_notification_data); 623 } 624 625 SDL_DINPUT_JoystickQuit(); 626 SDL_XINPUT_JoystickQuit(); 627 628 s_bWindowsDeviceChanged = SDL_FALSE; 629 } 630 631 static SDL_bool 632 WINDOWS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) 633 { 634 return SDL_FALSE; 635 } 636 637 SDL_JoystickDriver SDL_WINDOWS_JoystickDriver = 638 { 639 WINDOWS_JoystickInit, 640 WINDOWS_JoystickGetCount, 641 WINDOWS_JoystickDetect, 642 WINDOWS_JoystickGetDeviceName, 643 WINDOWS_JoystickGetDevicePlayerIndex, 644 WINDOWS_JoystickSetDevicePlayerIndex, 645 WINDOWS_JoystickGetDeviceGUID, 646 WINDOWS_JoystickGetDeviceInstanceID, 647 WINDOWS_JoystickOpen, 648 WINDOWS_JoystickRumble, 649 WINDOWS_JoystickRumbleTriggers, 650 WINDOWS_JoystickHasLED, 651 WINDOWS_JoystickSetLED, 652 WINDOWS_JoystickSetSensorsEnabled, 653 WINDOWS_JoystickUpdate, 654 WINDOWS_JoystickClose, 655 WINDOWS_JoystickQuit, 656 WINDOWS_JoystickGetGamepadMapping 657 }; 658 659 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */ 660 661 /* vi: set ts=4 sw=4 expandtab: */