src/haptic/windows/SDL_xinputhaptic.c
author Ryan C. Gordon
Tue, 29 Dec 2015 02:29:56 -0500
changeset 9986 081fbd89a347
parent 9714 e501460cb4c4
child 9998 f67cf37e9cd4
permissions -rw-r--r--
NetBSD: improved joystick support (thanks, Thomas!).

This patch skips non-joystick HID devices and gives joysticks on NetBSD
a human readable name.

Fixes Bugzilla #3178.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 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 #include "SDL_error.h"
    24 #include "SDL_haptic.h"
    25 #include "../SDL_syshaptic.h"
    26 
    27 #if SDL_HAPTIC_XINPUT
    28 
    29 #include "SDL_assert.h"
    30 #include "SDL_hints.h"
    31 #include "SDL_timer.h"
    32 #include "SDL_windowshaptic_c.h"
    33 #include "SDL_xinputhaptic_c.h"
    34 #include "../../core/windows/SDL_xinput.h"
    35 #include "../../joystick/windows/SDL_windowsjoystick_c.h"
    36 
    37 /*
    38  * Internal stuff.
    39  */
    40 static SDL_bool loaded_xinput = SDL_FALSE;
    41 
    42 
    43 int
    44 SDL_XINPUT_HapticInit(void)
    45 {
    46     const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
    47     if (!env || SDL_atoi(env)) {
    48         loaded_xinput = (WIN_LoadXInputDLL() == 0);
    49     }
    50 
    51     if (loaded_xinput) {
    52         DWORD i;
    53         for (i = 0; i < XUSER_MAX_COUNT; i++) {
    54             SDL_XINPUT_MaybeAddDevice(i);
    55         }
    56     }
    57     return 0;
    58 }
    59 
    60 int
    61 SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
    62 {
    63     const Uint8 userid = (Uint8)dwUserid;
    64     SDL_hapticlist_item *item;
    65     XINPUT_VIBRATION state;
    66 
    67     if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
    68         return -1;
    69     }
    70 
    71     /* Make sure we don't already have it */
    72     for (item = SDL_hapticlist; item; item = item->next) {
    73         if (item->bXInputHaptic && item->userid == userid) {
    74             return -1;  /* Already added */
    75         }
    76     }
    77 
    78     SDL_zero(state);
    79     if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) {
    80         return -1;  /* no force feedback on this device. */
    81     }
    82 
    83     item = (SDL_hapticlist_item *)SDL_malloc(sizeof(SDL_hapticlist_item));
    84     if (item == NULL) {
    85         return SDL_OutOfMemory();
    86     }
    87 
    88     SDL_zerop(item);
    89 
    90     /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */
    91     {
    92         char buf[64];
    93         SDL_snprintf(buf, sizeof(buf), "XInput Controller #%u", (unsigned int)(userid + 1));
    94         item->name = SDL_strdup(buf);
    95     }
    96 
    97     if (!item->name) {
    98         SDL_free(item);
    99         return -1;
   100     }
   101 
   102     /* Copy the instance over, useful for creating devices. */
   103     item->bXInputHaptic = SDL_TRUE;
   104     item->userid = userid;
   105 
   106     return SDL_SYS_AddHapticDevice(item);
   107 }
   108 
   109 int
   110 SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
   111 {
   112     const Uint8 userid = (Uint8)dwUserid;
   113     SDL_hapticlist_item *item;
   114     SDL_hapticlist_item *prev = NULL;
   115 
   116     if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
   117         return -1;
   118     }
   119 
   120     for (item = SDL_hapticlist; item != NULL; item = item->next) {
   121         if (item->bXInputHaptic && item->userid == userid) {
   122             /* found it, remove it. */
   123             return SDL_SYS_RemoveHapticDevice(prev, item);
   124         }
   125         prev = item;
   126     }
   127     return -1;
   128 }
   129 
   130 /* !!! FIXME: this is a hack, remove this later. */
   131 /* Since XInput doesn't offer a way to vibrate for X time, we hook into
   132  *  SDL_PumpEvents() to check if it's time to stop vibrating with some
   133  *  frequency.
   134  * In practice, this works for 99% of use cases. But in an ideal world,
   135  *  we do this in a separate thread so that:
   136  *    - we aren't bound to when the app chooses to pump the event queue.
   137  *    - we aren't adding more polling to the event queue
   138  *    - we can emulate all the haptic effects correctly (start on a delay,
   139  *      mix multiple effects, etc).
   140  *
   141  * Mostly, this is here to get rumbling to work, and all the other features
   142  *  are absent in the XInput path for now.  :(
   143  */
   144 static int SDLCALL
   145 SDL_RunXInputHaptic(void *arg)
   146 {
   147     struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
   148 
   149     while (!hwdata->stopThread) {
   150         SDL_Delay(50);
   151         SDL_LockMutex(hwdata->mutex);
   152         /* If we're currently running and need to stop... */
   153         if (hwdata->stopTicks) {
   154             if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) {
   155                 XINPUT_VIBRATION vibration = { 0, 0 };
   156                 hwdata->stopTicks = 0;
   157                 XINPUTSETSTATE(hwdata->userid, &vibration);
   158             }
   159         }
   160         SDL_UnlockMutex(hwdata->mutex);
   161     }
   162 
   163     return 0;
   164 }
   165 
   166 static int
   167 SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid)
   168 {
   169     char threadName[32];
   170     XINPUT_VIBRATION vibration = { 0, 0 };  /* stop any current vibration */
   171     XINPUTSETSTATE(userid, &vibration);
   172 
   173     haptic->supported = SDL_HAPTIC_LEFTRIGHT;
   174 
   175     haptic->neffects = 1;
   176     haptic->nplaying = 1;
   177 
   178     /* Prepare effects memory. */
   179     haptic->effects = (struct haptic_effect *)
   180         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   181     if (haptic->effects == NULL) {
   182         return SDL_OutOfMemory();
   183     }
   184     /* Clear the memory */
   185     SDL_memset(haptic->effects, 0,
   186         sizeof(struct haptic_effect) * haptic->neffects);
   187 
   188     haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
   189     if (haptic->hwdata == NULL) {
   190         SDL_free(haptic->effects);
   191         haptic->effects = NULL;
   192         return SDL_OutOfMemory();
   193     }
   194     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   195 
   196     haptic->hwdata->bXInputHaptic = 1;
   197     haptic->hwdata->userid = userid;
   198 
   199     haptic->hwdata->mutex = SDL_CreateMutex();
   200     if (haptic->hwdata->mutex == NULL) {
   201         SDL_free(haptic->effects);
   202         SDL_free(haptic->hwdata);
   203         haptic->effects = NULL;
   204         return SDL_SetError("Couldn't create XInput haptic mutex");
   205     }
   206 
   207     SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid);
   208 
   209 #if defined(__WIN32__) && !defined(HAVE_LIBC)  /* !!! FIXME: this is nasty. */
   210 #undef SDL_CreateThread
   211 #if SDL_DYNAMIC_API
   212     haptic->hwdata->thread = SDL_CreateThread_REAL(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
   213 #else
   214     haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
   215 #endif
   216 #else
   217     haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata);
   218 #endif
   219     if (haptic->hwdata->thread == NULL) {
   220         SDL_DestroyMutex(haptic->hwdata->mutex);
   221         SDL_free(haptic->effects);
   222         SDL_free(haptic->hwdata);
   223         haptic->effects = NULL;
   224         return SDL_SetError("Couldn't create XInput haptic thread");
   225     }
   226 
   227     return 0;
   228 }
   229 
   230 int
   231 SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item)
   232 {
   233     return SDL_XINPUT_HapticOpenFromUserIndex(haptic, item->userid);
   234 }
   235 
   236 int
   237 SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   238 {
   239     return (haptic->hwdata->userid == joystick->hwdata->userid);
   240 }
   241 
   242 int
   243 SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
   244 {
   245     SDL_hapticlist_item *item;
   246     int index = 0;
   247 
   248     /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
   249     for (item = SDL_hapticlist; item != NULL; item = item->next) {
   250         if (item->bXInputHaptic && item->userid == joystick->hwdata->userid) {
   251             haptic->index = index;
   252             return SDL_XINPUT_HapticOpenFromUserIndex(haptic, joystick->hwdata->userid);
   253         }
   254         ++index;
   255     }
   256 
   257     SDL_SetError("Couldn't find joystick in haptic device list");
   258     return -1;
   259 }
   260 
   261 void
   262 SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
   263 {
   264     haptic->hwdata->stopThread = 1;
   265     SDL_WaitThread(haptic->hwdata->thread, NULL);
   266     SDL_DestroyMutex(haptic->hwdata->mutex);
   267 }
   268 
   269 void
   270 SDL_XINPUT_HapticQuit(void)
   271 {
   272     if (loaded_xinput) {
   273         WIN_UnloadXInputDLL();
   274         loaded_xinput = SDL_FALSE;
   275     }
   276 }
   277 
   278 int
   279 SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
   280 {
   281     SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
   282     return SDL_XINPUT_HapticUpdateEffect(haptic, effect, base);
   283 }
   284 
   285 int
   286 SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data)
   287 {
   288     XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
   289     SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT);
   290     vib->wLeftMotorSpeed = data->leftright.large_magnitude;
   291     vib->wRightMotorSpeed = data->leftright.small_magnitude;
   292     SDL_LockMutex(haptic->hwdata->mutex);
   293     if (haptic->hwdata->stopTicks) {  /* running right now? Update it. */
   294         XINPUTSETSTATE(haptic->hwdata->userid, vib);
   295     }
   296     SDL_UnlockMutex(haptic->hwdata->mutex);
   297     return 0;
   298 }
   299 
   300 int
   301 SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations)
   302 {
   303     XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
   304     SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT);  /* should catch this at higher level */
   305     SDL_LockMutex(haptic->hwdata->mutex);
   306     if (effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) {
   307         haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
   308     } else if ((!effect->effect.leftright.length) || (!iterations)) {
   309         /* do nothing. Effect runs for zero milliseconds. */
   310     } else {
   311         haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
   312         if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) {
   313             haptic->hwdata->stopTicks = 1;  /* fix edge cases. */
   314         }
   315     }
   316     SDL_UnlockMutex(haptic->hwdata->mutex);
   317     return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
   318 }
   319 
   320 int
   321 SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
   322 {
   323     XINPUT_VIBRATION vibration = { 0, 0 };
   324     SDL_LockMutex(haptic->hwdata->mutex);
   325     haptic->hwdata->stopTicks = 0;
   326     SDL_UnlockMutex(haptic->hwdata->mutex);
   327     return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
   328 }
   329 
   330 void
   331 SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
   332 {
   333     SDL_XINPUT_HapticStopEffect(haptic, effect);
   334 }
   335 
   336 int
   337 SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
   338 {
   339     return SDL_Unsupported();
   340 }
   341 
   342 int
   343 SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
   344 {
   345     return SDL_Unsupported();
   346 }
   347 
   348 int
   349 SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
   350 {
   351     return SDL_Unsupported();
   352 }
   353 
   354 int
   355 SDL_XINPUT_HapticPause(SDL_Haptic * haptic)
   356 {
   357     return SDL_Unsupported();
   358 }
   359 
   360 int
   361 SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic)
   362 {
   363     return SDL_Unsupported();
   364 }
   365 
   366 int
   367 SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic)
   368 {
   369     XINPUT_VIBRATION vibration = { 0, 0 };
   370     SDL_LockMutex(haptic->hwdata->mutex);
   371     haptic->hwdata->stopTicks = 0;
   372     SDL_UnlockMutex(haptic->hwdata->mutex);
   373     return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
   374 }
   375 
   376 #else /* !SDL_HAPTIC_XINPUT */
   377 
   378 #include "../../core/windows/SDL_windows.h"
   379 
   380 typedef struct SDL_hapticlist_item SDL_hapticlist_item;
   381 
   382 int
   383 SDL_XINPUT_HapticInit(void)
   384 {
   385     return 0;
   386 }
   387 
   388 int
   389 SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
   390 {
   391     return SDL_Unsupported();
   392 }
   393 
   394 int
   395 SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
   396 {
   397     return SDL_Unsupported();
   398 }
   399 
   400 int
   401 SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item)
   402 {
   403     return SDL_Unsupported();
   404 }
   405 
   406 int
   407 SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   408 {
   409     return SDL_Unsupported();
   410 }
   411 
   412 int
   413 SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
   414 {
   415     return SDL_Unsupported();
   416 }
   417 
   418 void
   419 SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
   420 {
   421 }
   422 
   423 void
   424 SDL_XINPUT_HapticQuit(void)
   425 {
   426 }
   427 
   428 int
   429 SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
   430 {
   431     return SDL_Unsupported();
   432 }
   433 
   434 int
   435 SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data)
   436 {
   437     return SDL_Unsupported();
   438 }
   439 
   440 int
   441 SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations)
   442 {
   443     return SDL_Unsupported();
   444 }
   445 
   446 int
   447 SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
   448 {
   449     return SDL_Unsupported();
   450 }
   451 
   452 void
   453 SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
   454 {
   455 }
   456 
   457 int
   458 SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
   459 {
   460     return SDL_Unsupported();
   461 }
   462 
   463 int
   464 SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
   465 {
   466     return SDL_Unsupported();
   467 }
   468 
   469 int
   470 SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
   471 {
   472     return SDL_Unsupported();
   473 }
   474 
   475 int
   476 SDL_XINPUT_HapticPause(SDL_Haptic * haptic)
   477 {
   478     return SDL_Unsupported();
   479 }
   480 
   481 int
   482 SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic)
   483 {
   484     return SDL_Unsupported();
   485 }
   486 
   487 int
   488 SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic)
   489 {
   490     return SDL_Unsupported();
   491 }
   492 
   493 #endif /* SDL_HAPTIC_XINPUT */
   494 
   495 /* vi: set ts=4 sw=4 expandtab: */