SDL_windowsjoystick.c (16970B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 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_assert.h" 37#include "SDL_events.h" 38#include "SDL_thread.h" 39#include "SDL_timer.h" 40#include "SDL_mutex.h" 41#include "SDL_events.h" 42#include "SDL_hints.h" 43#include "SDL_joystick.h" 44#include "../SDL_sysjoystick.h" 45#if !SDL_EVENTS_DISABLED 46#include "../../events/SDL_events_c.h" 47#endif 48#include "../../core/windows/SDL_windows.h" 49#if !defined(__WINRT__) 50#include <dbt.h> 51#endif 52 53#define INITGUID /* Only set here, if set twice will cause mingw32 to break. */ 54#include "SDL_windowsjoystick_c.h" 55#include "SDL_dinputjoystick_c.h" 56#include "SDL_xinputjoystick_c.h" 57 58#include "../../haptic/windows/SDL_dinputhaptic_c.h" /* For haptic hot plugging */ 59#include "../../haptic/windows/SDL_xinputhaptic_c.h" /* For haptic hot plugging */ 60 61 62#ifndef DEVICE_NOTIFY_WINDOW_HANDLE 63#define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000 64#endif 65 66/* local variables */ 67static SDL_bool s_bDeviceAdded = SDL_FALSE; 68static SDL_bool s_bDeviceRemoved = SDL_FALSE; 69static SDL_JoystickID s_nInstanceID = -1; 70static SDL_cond *s_condJoystickThread = NULL; 71static SDL_mutex *s_mutexJoyStickEnum = NULL; 72static SDL_Thread *s_threadJoystick = NULL; 73static SDL_bool s_bJoystickThreadQuit = SDL_FALSE; 74 75JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */ 76 77static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE; 78 79#ifdef __WINRT__ 80 81typedef struct 82{ 83 int unused; 84} SDL_DeviceNotificationData; 85 86static void 87SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) 88{ 89} 90 91static int 92SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) 93{ 94 return 0; 95} 96 97static void 98SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data) 99{ 100} 101 102#else /* !__WINRT__ */ 103 104typedef struct 105{ 106 HRESULT coinitialized; 107 WNDCLASSEX wincl; 108 HWND messageWindow; 109 HDEVNOTIFY hNotify; 110} SDL_DeviceNotificationData; 111 112 113/* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */ 114static LRESULT CALLBACK 115SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 116{ 117 switch (message) { 118 case WM_DEVICECHANGE: 119 switch (wParam) { 120 case DBT_DEVICEARRIVAL: 121 if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { 122 s_bWindowsDeviceChanged = SDL_TRUE; 123 } 124 break; 125 case DBT_DEVICEREMOVECOMPLETE: 126 if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { 127 s_bWindowsDeviceChanged = SDL_TRUE; 128 } 129 break; 130 } 131 return 0; 132 } 133 134 return DefWindowProc (hwnd, message, wParam, lParam); 135} 136 137static void 138SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) 139{ 140 if (data->hNotify) 141 UnregisterDeviceNotification(data->hNotify); 142 143 if (data->messageWindow) 144 DestroyWindow(data->messageWindow); 145 146 UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance); 147 148 if (data->coinitialized == S_OK) { 149 WIN_CoUninitialize(); 150 } 151} 152 153static int 154SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) 155{ 156 DEV_BROADCAST_DEVICEINTERFACE dbh; 157 GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; 158 159 SDL_zerop(data); 160 161 data->coinitialized = WIN_CoInitialize(); 162 163 data->wincl.hInstance = GetModuleHandle(NULL); 164 data->wincl.lpszClassName = L"Message"; 165 data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; /* This function is called by windows */ 166 data->wincl.cbSize = sizeof (WNDCLASSEX); 167 168 if (!RegisterClassEx(&data->wincl)) { 169 WIN_SetError("Failed to create register class for joystick autodetect"); 170 SDL_CleanupDeviceNotification(data); 171 return -1; 172 } 173 174 data->messageWindow = (HWND)CreateWindowEx(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); 175 if (!data->messageWindow) { 176 WIN_SetError("Failed to create message window for joystick autodetect"); 177 SDL_CleanupDeviceNotification(data); 178 return -1; 179 } 180 181 SDL_zero(dbh); 182 dbh.dbcc_size = sizeof(dbh); 183 dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 184 dbh.dbcc_classguid = GUID_DEVINTERFACE_HID; 185 186 data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE); 187 if (!data->hNotify) { 188 WIN_SetError("Failed to create notify device for joystick autodetect"); 189 SDL_CleanupDeviceNotification(data); 190 return -1; 191 } 192 return 0; 193} 194 195static void 196SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data) 197{ 198 MSG msg; 199 200 if (!data->messageWindow) { 201 return; 202 } 203 204 while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) { 205 if (GetMessage(&msg, data->messageWindow, 0, 0) != 0) { 206 TranslateMessage(&msg); 207 DispatchMessage(&msg); 208 } 209 } 210} 211 212#endif /* __WINRT__ */ 213 214/* Function/thread to scan the system for joysticks. */ 215static int 216SDL_JoystickThread(void *_data) 217{ 218 SDL_DeviceNotificationData notification_data; 219 220#if SDL_JOYSTICK_XINPUT 221 SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT]; 222 SDL_zero(bOpenedXInputDevices); 223#endif 224 225 if (SDL_CreateDeviceNotification(¬ification_data) < 0) { 226 return -1; 227 } 228 229 SDL_LockMutex(s_mutexJoyStickEnum); 230 while (s_bJoystickThreadQuit == SDL_FALSE) { 231 SDL_bool bXInputChanged = SDL_FALSE; 232 233 SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 300); 234 235 SDL_CheckDeviceNotification(¬ification_data); 236 237#if SDL_JOYSTICK_XINPUT 238 if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) { 239 /* scan for any change in XInput devices */ 240 Uint8 userId; 241 for (userId = 0; userId < XUSER_MAX_COUNT; userId++) { 242 XINPUT_CAPABILITIES capabilities; 243 const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities); 244 const SDL_bool available = (result == ERROR_SUCCESS); 245 if (bOpenedXInputDevices[userId] != available) { 246 bXInputChanged = SDL_TRUE; 247 bOpenedXInputDevices[userId] = available; 248 } 249 } 250 } 251#endif /* SDL_JOYSTICK_XINPUT */ 252 253 if (s_bWindowsDeviceChanged || bXInputChanged) { 254 SDL_UnlockMutex(s_mutexJoyStickEnum); /* let main thread go while we SDL_Delay(). */ 255 SDL_Delay(300); /* wait for direct input to find out about this device */ 256 SDL_LockMutex(s_mutexJoyStickEnum); 257 258 s_bDeviceRemoved = SDL_TRUE; 259 s_bDeviceAdded = SDL_TRUE; 260 s_bWindowsDeviceChanged = SDL_FALSE; 261 } 262 } 263 SDL_UnlockMutex(s_mutexJoyStickEnum); 264 265 SDL_CleanupDeviceNotification(¬ification_data); 266 267 return 1; 268} 269 270void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device) 271{ 272 device->send_add_event = SDL_TRUE; 273 device->nInstanceID = ++s_nInstanceID; 274 device->pNext = SYS_Joystick; 275 SYS_Joystick = device; 276 277 s_bDeviceAdded = SDL_TRUE; 278} 279 280/* Function to scan the system for joysticks. 281 * This function should set SDL_numjoysticks to the number of available 282 * joysticks. Joystick 0 should be the system default joystick. 283 * It should return 0, or -1 on an unrecoverable fatal error. 284 */ 285int 286SDL_SYS_JoystickInit(void) 287{ 288 if (SDL_DINPUT_JoystickInit() < 0) { 289 SDL_SYS_JoystickQuit(); 290 return -1; 291 } 292 293 if (SDL_XINPUT_JoystickInit() < 0) { 294 SDL_SYS_JoystickQuit(); 295 return -1; 296 } 297 298 s_mutexJoyStickEnum = SDL_CreateMutex(); 299 s_condJoystickThread = SDL_CreateCond(); 300 s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */ 301 302 SDL_SYS_JoystickDetect(); 303 304 if (!s_threadJoystick) { 305 s_bJoystickThreadQuit = SDL_FALSE; 306 /* spin up the thread to detect hotplug of devices */ 307#if defined(__WIN32__) && !defined(HAVE_LIBC) 308#undef SDL_CreateThread 309#if SDL_DYNAMIC_API 310 s_threadJoystick= SDL_CreateThread_REAL(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL); 311#else 312 s_threadJoystick= SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL); 313#endif 314#else 315 s_threadJoystick = SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL); 316#endif 317 } 318 return SDL_SYS_NumJoysticks(); 319} 320 321/* return the number of joysticks that are connected right now */ 322int 323SDL_SYS_NumJoysticks() 324{ 325 int nJoysticks = 0; 326 JoyStick_DeviceData *device = SYS_Joystick; 327 while (device) { 328 nJoysticks++; 329 device = device->pNext; 330 } 331 332 return nJoysticks; 333} 334 335/* detect any new joysticks being inserted into the system */ 336void 337SDL_SYS_JoystickDetect() 338{ 339 JoyStick_DeviceData *pCurList = NULL; 340#if !SDL_EVENTS_DISABLED 341 SDL_Event event; 342#endif 343 344 /* only enum the devices if the joystick thread told us something changed */ 345 if (!s_bDeviceAdded && !s_bDeviceRemoved) { 346 return; /* thread hasn't signaled, nothing to do right now. */ 347 } 348 349 SDL_LockMutex(s_mutexJoyStickEnum); 350 351 s_bDeviceAdded = SDL_FALSE; 352 s_bDeviceRemoved = SDL_FALSE; 353 354 pCurList = SYS_Joystick; 355 SYS_Joystick = NULL; 356 357 /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */ 358 SDL_DINPUT_JoystickDetect(&pCurList); 359 360 /* Look for XInput devices. Do this last, so they're first in the final list. */ 361 SDL_XINPUT_JoystickDetect(&pCurList); 362 363 SDL_UnlockMutex(s_mutexJoyStickEnum); 364 365 while (pCurList) { 366 JoyStick_DeviceData *pListNext = NULL; 367 368 if (pCurList->bXInputDevice) { 369 SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId); 370 } else { 371 SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice); 372 } 373 374#if !SDL_EVENTS_DISABLED 375 SDL_zero(event); 376 event.type = SDL_JOYDEVICEREMOVED; 377 378 if (SDL_GetEventState(event.type) == SDL_ENABLE) { 379 event.jdevice.which = pCurList->nInstanceID; 380 if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) { 381 SDL_PushEvent(&event); 382 } 383 } 384#endif /* !SDL_EVENTS_DISABLED */ 385 386 pListNext = pCurList->pNext; 387 SDL_free(pCurList->joystickname); 388 SDL_free(pCurList); 389 pCurList = pListNext; 390 } 391 392 if (s_bDeviceAdded) { 393 JoyStick_DeviceData *pNewJoystick; 394 int device_index = 0; 395 s_bDeviceAdded = SDL_FALSE; 396 pNewJoystick = SYS_Joystick; 397 while (pNewJoystick) { 398 if (pNewJoystick->send_add_event) { 399 if (pNewJoystick->bXInputDevice) { 400 SDL_XINPUT_MaybeAddDevice(pNewJoystick->XInputUserId); 401 } else { 402 SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice); 403 } 404 405#if !SDL_EVENTS_DISABLED 406 SDL_zero(event); 407 event.type = SDL_JOYDEVICEADDED; 408 409 if (SDL_GetEventState(event.type) == SDL_ENABLE) { 410 event.jdevice.which = device_index; 411 if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) { 412 SDL_PushEvent(&event); 413 } 414 } 415#endif /* !SDL_EVENTS_DISABLED */ 416 pNewJoystick->send_add_event = SDL_FALSE; 417 } 418 device_index++; 419 pNewJoystick = pNewJoystick->pNext; 420 } 421 } 422} 423 424/* Function to get the device-dependent name of a joystick */ 425const char * 426SDL_SYS_JoystickNameForDeviceIndex(int device_index) 427{ 428 JoyStick_DeviceData *device = SYS_Joystick; 429 430 for (; device_index > 0; device_index--) 431 device = device->pNext; 432 433 return device->joystickname; 434} 435 436/* Function to perform the mapping between current device instance and this joysticks instance id */ 437SDL_JoystickID 438SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) 439{ 440 JoyStick_DeviceData *device = SYS_Joystick; 441 int index; 442 443 for (index = device_index; index > 0; index--) 444 device = device->pNext; 445 446 return device->nInstanceID; 447} 448 449/* Function to open a joystick for use. 450 The joystick to open is specified by the index field of the joystick. 451 This should fill the nbuttons and naxes fields of the joystick structure. 452 It returns 0, or -1 if there is an error. 453 */ 454int 455SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) 456{ 457 JoyStick_DeviceData *joystickdevice = SYS_Joystick; 458 459 for (; device_index > 0; device_index--) 460 joystickdevice = joystickdevice->pNext; 461 462 /* allocate memory for system specific hardware data */ 463 joystick->instance_id = joystickdevice->nInstanceID; 464 joystick->closed = SDL_FALSE; 465 joystick->hwdata = 466 (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata)); 467 if (joystick->hwdata == NULL) { 468 return SDL_OutOfMemory(); 469 } 470 SDL_zerop(joystick->hwdata); 471 joystick->hwdata->guid = joystickdevice->guid; 472 473 if (joystickdevice->bXInputDevice) { 474 return SDL_XINPUT_JoystickOpen(joystick, joystickdevice); 475 } else { 476 return SDL_DINPUT_JoystickOpen(joystick, joystickdevice); 477 } 478} 479 480/* return true if this joystick is plugged in right now */ 481SDL_bool 482SDL_SYS_JoystickAttached(SDL_Joystick * joystick) 483{ 484 return !joystick->closed && !joystick->hwdata->removed; 485} 486 487void 488SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) 489{ 490 if (joystick->closed || !joystick->hwdata) { 491 return; 492 } 493 494 if (joystick->hwdata->bXInputDevice) { 495 SDL_XINPUT_JoystickUpdate(joystick); 496 } else { 497 SDL_DINPUT_JoystickUpdate(joystick); 498 } 499 500 if (joystick->hwdata->removed) { 501 joystick->closed = SDL_TRUE; 502 joystick->uncentered = SDL_TRUE; 503 } 504} 505 506/* Function to close a joystick after use */ 507void 508SDL_SYS_JoystickClose(SDL_Joystick * joystick) 509{ 510 if (joystick->hwdata->bXInputDevice) { 511 SDL_XINPUT_JoystickClose(joystick); 512 } else { 513 SDL_DINPUT_JoystickClose(joystick); 514 } 515 516 /* free system specific hardware data */ 517 SDL_free(joystick->hwdata); 518 519 joystick->closed = SDL_TRUE; 520} 521 522/* Function to perform any system-specific joystick related cleanup */ 523void 524SDL_SYS_JoystickQuit(void) 525{ 526 JoyStick_DeviceData *device = SYS_Joystick; 527 528 while (device) { 529 JoyStick_DeviceData *device_next = device->pNext; 530 SDL_free(device->joystickname); 531 SDL_free(device); 532 device = device_next; 533 } 534 SYS_Joystick = NULL; 535 536 if (s_threadJoystick) { 537 SDL_LockMutex(s_mutexJoyStickEnum); 538 s_bJoystickThreadQuit = SDL_TRUE; 539 SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */ 540 SDL_UnlockMutex(s_mutexJoyStickEnum); 541 SDL_WaitThread(s_threadJoystick, NULL); /* wait for it to bugger off */ 542 543 SDL_DestroyMutex(s_mutexJoyStickEnum); 544 SDL_DestroyCond(s_condJoystickThread); 545 s_condJoystickThread= NULL; 546 s_mutexJoyStickEnum = NULL; 547 s_threadJoystick = NULL; 548 } 549 550 SDL_DINPUT_JoystickQuit(); 551 SDL_XINPUT_JoystickQuit(); 552} 553 554/* return the stable device guid for this device index */ 555SDL_JoystickGUID 556SDL_SYS_JoystickGetDeviceGUID(int device_index) 557{ 558 JoyStick_DeviceData *device = SYS_Joystick; 559 int index; 560 561 for (index = device_index; index > 0; index--) 562 device = device->pNext; 563 564 return device->guid; 565} 566 567SDL_JoystickGUID 568SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) 569{ 570 return joystick->hwdata->guid; 571} 572 573#endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */ 574 575/* vi: set ts=4 sw=4 expandtab: */