int 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 = SDL_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 */ if (SDL_XInputUseOldJoystickMapping()) { joystick->naxes = 6; joystick->nbuttons = 15; } else { joystick->naxes = 6; joystick->nbuttons = 11; joystick->nhats = 1; } return 0; }
static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid) { char threadName[32]; XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */ XINPUTSETSTATE(userid, &vibration); /* !!! FIXME: we can probably do more than SINE if we figure out how to set up the left and right motors properly. */ haptic->supported = SDL_HAPTIC_SINE; haptic->neffects = 1; haptic->nplaying = 1; /* Prepare effects memory. */ haptic->effects = (struct haptic_effect *) SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); if (haptic->effects == NULL) { return SDL_OutOfMemory(); } /* Clear the memory */ SDL_memset(haptic->effects, 0, sizeof(struct haptic_effect) * haptic->neffects); haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata)); if (haptic->hwdata == NULL) { SDL_free(haptic->effects); haptic->effects = NULL; return SDL_OutOfMemory(); } SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); haptic->hwdata->bXInputHaptic = 1; haptic->hwdata->userid = userid; haptic->hwdata->mutex = SDL_CreateMutex(); if (haptic->hwdata->mutex == NULL) { SDL_free(haptic->effects); SDL_free(haptic->hwdata); haptic->effects = NULL; return SDL_SetError("Couldn't create XInput haptic mutex"); } SDL_snprintf(threadName, sizeof (threadName), "SDLXInputDev%d", (int) userid); #if defined(__WIN32__) && !defined(HAVE_LIBC) /* !!! FIXME: this is nasty. */ #undef SDL_CreateThread haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); #else haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata); #endif if (haptic->hwdata->thread == NULL) { SDL_DestroyMutex(haptic->hwdata->mutex); SDL_free(haptic->effects); SDL_free(haptic->hwdata); haptic->effects = NULL; return SDL_SetError("Couldn't create XInput haptic thread"); } return 0; }
/* * Runs an effect. */ int SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) { HRESULT ret; DWORD iter; if (haptic->hwdata->bXInputHaptic) { XINPUT_VIBRATION *vib = &effect->hweffect->vibration; SDL_assert(effect->effect.type == SDL_HAPTIC_SINE); /* should catch this at higher level */ SDL_LockMutex(haptic->hwdata->mutex); haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.periodic.length * iterations); SDL_UnlockMutex(haptic->hwdata->mutex); return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1; } /* Check if it's infinite. */ if (iterations == SDL_HAPTIC_INFINITY) { iter = INFINITE; } else iter = iterations; /* Run the effect. */ ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0); if (FAILED(ret)) { return DI_SetError("Running the effect", ret); } return 0; }
int SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic) { XINPUT_VIBRATION vibration = { 0, 0 }; SDL_LockMutex(haptic->hwdata->mutex); haptic->hwdata->stopTicks = 0; SDL_UnlockMutex(haptic->hwdata->mutex); return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; }
static int SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid) { char threadName[32]; XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */ XINPUTSETSTATE(userid, &vibration); haptic->supported = SDL_HAPTIC_LEFTRIGHT; haptic->neffects = 1; haptic->nplaying = 1; /* Prepare effects memory. */ haptic->effects = (struct haptic_effect *) SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); if (haptic->effects == NULL) { return SDL_OutOfMemory(); } /* Clear the memory */ SDL_memset(haptic->effects, 0, sizeof(struct haptic_effect) * haptic->neffects); haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata)); if (haptic->hwdata == NULL) { SDL_free(haptic->effects); haptic->effects = NULL; return SDL_OutOfMemory(); } SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); haptic->hwdata->bXInputHaptic = 1; haptic->hwdata->userid = userid; haptic->hwdata->mutex = SDL_CreateMutex(); if (haptic->hwdata->mutex == NULL) { SDL_free(haptic->effects); SDL_free(haptic->hwdata); haptic->effects = NULL; return SDL_SetError("Couldn't create XInput haptic mutex"); } SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid); haptic->hwdata->thread = SDL_CreateThreadInternal(SDL_RunXInputHaptic, threadName, 64 * 1024, haptic->hwdata); if (haptic->hwdata->thread == NULL) { SDL_DestroyMutex(haptic->hwdata->mutex); SDL_free(haptic->effects); SDL_free(haptic->hwdata); haptic->effects = NULL; return SDL_SetError("Couldn't create XInput haptic thread"); } return 0; }
int SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) { XINPUT_VIBRATION *vib = &effect->hweffect->vibration; SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT); vib->wLeftMotorSpeed = data->leftright.large_magnitude; vib->wRightMotorSpeed = data->leftright.small_magnitude; SDL_LockMutex(haptic->hwdata->mutex); if (haptic->hwdata->stopTicks) { /* running right now? Update it. */ XINPUTSETSTATE(haptic->hwdata->userid, vib); } SDL_UnlockMutex(haptic->hwdata->mutex); return 0; }
int SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid) { const Uint8 userid = (Uint8)dwUserid; SDL_hapticlist_item *item; XINPUT_VIBRATION state; if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) { return -1; } /* Make sure we don't already have it */ for (item = SDL_hapticlist; item; item = item->next) { if (item->bXInputHaptic && item->userid == userid) { return -1; /* Already added */ } } SDL_zero(state); if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) { return -1; /* no force feedback on this device. */ } item = (SDL_hapticlist_item *)SDL_malloc(sizeof(SDL_hapticlist_item)); if (item == NULL) { return SDL_OutOfMemory(); } SDL_zerop(item); /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */ { char buf[64]; SDL_snprintf(buf, sizeof(buf), "XInput Controller #%u", (unsigned int)(userid + 1)); item->name = SDL_strdup(buf); } if (!item->name) { SDL_free(item); return -1; } /* Copy the instance over, useful for creating devices. */ item->bXInputHaptic = SDL_TRUE; item->userid = userid; return SDL_SYS_AddHapticDevice(item); }
int SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) { XINPUT_VIBRATION *vib = &effect->hweffect->vibration; SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ SDL_LockMutex(haptic->hwdata->mutex); if (effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) { haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY; } else if ((!effect->effect.leftright.length) || (!iterations)) { /* do nothing. Effect runs for zero milliseconds. */ } else { haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations); if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) { haptic->hwdata->stopTicks = 1; /* fix edge cases. */ } } SDL_UnlockMutex(haptic->hwdata->mutex); return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1; }
/* Since XInput doesn't offer a way to vibrate for X time, we hook into * SDL_PumpEvents() to check if it's time to stop vibrating with some * frequency. * In practice, this works for 99% of use cases. But in an ideal world, * we do this in a separate thread so that: * - we aren't bound to when the app chooses to pump the event queue. * - we aren't adding more polling to the event queue * - we can emulate all the haptic effects correctly (start on a delay, * mix multiple effects, etc). * * Mostly, this is here to get rumbling to work, and all the other features * are absent in the XInput path for now. :( */ static int SDLCALL SDL_RunXInputHaptic(void *arg) { struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg; while (!hwdata->stopThread) { SDL_Delay(50); SDL_LockMutex(hwdata->mutex); /* If we're currently running and need to stop... */ if ((hwdata->stopTicks) && (hwdata->stopTicks < SDL_GetTicks())) { XINPUT_VIBRATION vibration = { 0, 0 }; hwdata->stopTicks = 0; XINPUTSETSTATE(hwdata->userid, &vibration); } SDL_UnlockMutex(hwdata->mutex); } return 0; }
/* * Stops an effect. */ int SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) { HRESULT ret; if (haptic->hwdata->bXInputHaptic) { XINPUT_VIBRATION vibration = { 0, 0 }; SDL_LockMutex(haptic->hwdata->mutex); haptic->hwdata->stopTicks = 0; SDL_UnlockMutex(haptic->hwdata->mutex); return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; } ret = IDirectInputEffect_Stop(effect->hweffect->ref); if (FAILED(ret)) { return DI_SetError("Unable to stop effect", ret); } return 0; }
/* Since XInput doesn't offer a way to vibrate for X time, we hook into * SDL_PumpEvents() to check if it's time to stop vibrating with some * frequency. * In practice, this works for 99% of use cases. But in an ideal world, * we do this in a separate thread so that: * - we aren't bound to when the app chooses to pump the event queue. * - we aren't adding more polling to the event queue * - we can emulate all the haptic effects correctly (start on a delay, * mix multiple effects, etc). * * Mostly, this is here to get rumbling to work, and all the other features * are absent in the XInput path for now. :( */ static int SDLCALL SDL_RunXInputHaptic(void *arg) { struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg; while (!SDL_AtomicGet(&hwdata->stopThread)) { SDL_Delay(50); SDL_LockMutex(hwdata->mutex); /* If we're currently running and need to stop... */ if (hwdata->stopTicks) { if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) { XINPUT_VIBRATION vibration = { 0, 0 }; hwdata->stopTicks = 0; XINPUTSETSTATE(hwdata->userid, &vibration); } } SDL_UnlockMutex(hwdata->mutex); } return 0; }
int SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) { 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"); } if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) { joystick->hwdata->rumble_expiration = SDL_GetTicks() + duration_ms; } else { joystick->hwdata->rumble_expiration = 0; } return 0; }
/* * Stops all the playing effects on the device. */ int SDL_SYS_HapticStopAll(SDL_Haptic * haptic) { HRESULT ret; if (haptic->hwdata->bXInputHaptic) { XINPUT_VIBRATION vibration = { 0, 0 }; SDL_LockMutex(haptic->hwdata->mutex); haptic->hwdata->stopTicks = 0; SDL_UnlockMutex(haptic->hwdata->mutex); return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; } /* Try to stop the effects. */ ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, DISFFC_STOPALL); if (FAILED(ret)) { return DI_SetError("Stopping the device", ret); } return 0; }
static int SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid) { char threadName[32]; XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */ XINPUTSETSTATE(userid, &vibration); haptic->supported = SDL_HAPTIC_LEFTRIGHT; haptic->neffects = 1; haptic->nplaying = 1; /* Prepare effects memory. */ haptic->effects = (struct haptic_effect *) SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); if (haptic->effects == NULL) { return SDL_OutOfMemory(); } /* Clear the memory */ SDL_memset(haptic->effects, 0, sizeof(struct haptic_effect) * haptic->neffects); haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata)); if (haptic->hwdata == NULL) { SDL_free(haptic->effects); haptic->effects = NULL; return SDL_OutOfMemory(); } SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); haptic->hwdata->bXInputHaptic = 1; haptic->hwdata->userid = userid; haptic->hwdata->mutex = SDL_CreateMutex(); if (haptic->hwdata->mutex == NULL) { SDL_free(haptic->effects); SDL_free(haptic->hwdata); haptic->effects = NULL; return SDL_SetError("Couldn't create XInput haptic mutex"); } SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid); #if defined(__WIN32__) && !defined(HAVE_LIBC) /* !!! FIXME: this is nasty. */ #undef SDL_CreateThread #if SDL_DYNAMIC_API haptic->hwdata->thread = SDL_CreateThread_REAL(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); #else haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); #endif #else haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata); #endif if (haptic->hwdata->thread == NULL) { SDL_DestroyMutex(haptic->hwdata->mutex); SDL_free(haptic->effects); SDL_free(haptic->hwdata); haptic->effects = NULL; return SDL_SetError("Couldn't create XInput haptic thread"); } return 0; }