diff --git a/src/connection.c b/src/connection.c index abf42c4..fed199c 100644 --- a/src/connection.c +++ b/src/connection.c @@ -25,6 +25,7 @@ pthread_t main_thread_id = 0; bool connection_debug; +ConnListenerRumble rumble_handler = NULL; static void connection_terminated() { if (main_thread_id != 0) @@ -46,6 +47,11 @@ static void connection_log_message(const char* format, ...) { va_end(arglist); } +static void rumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) { + if (rumble_handler) + rumble_handler(controllerNumber, lowFreqMotor, highFreqMotor); +} + CONNECTION_LISTENER_CALLBACKS connection_callbacks = { .stageStarting = NULL, .stageComplete = NULL, @@ -55,4 +61,5 @@ CONNECTION_LISTENER_CALLBACKS connection_callbacks = { .displayMessage = connection_display_message, .displayTransientMessage = connection_display_transient_message, .logMessage = connection_log_message, + .rumble = rumble, }; diff --git a/src/connection.h b/src/connection.h index a8a9ea6..b203499 100644 --- a/src/connection.h +++ b/src/connection.h @@ -25,3 +25,4 @@ extern CONNECTION_LISTENER_CALLBACKS connection_callbacks; extern pthread_t main_thread_id; extern bool connection_debug; +extern ConnListenerRumble rumble_handler; diff --git a/src/input/sdl.c b/src/input/sdl.c index 0438d7a..3e18483 100644 --- a/src/input/sdl.c +++ b/src/input/sdl.c @@ -33,6 +33,8 @@ typedef struct _GAMEPAD_STATE { short rightStickX, rightStickY; int buttons; SDL_JoystickID sdl_id; + SDL_Haptic* haptic; + int haptic_effect_id; short id; bool initialized; } GAMEPAD_STATE, *PGAMEPAD_STATE; @@ -44,22 +46,6 @@ static int activeGamepadMask = 0; int sdl_gamepads = 0; -void sdlinput_init(char* mappings) { - memset(gamepads, 0, sizeof(gamepads)); - - SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); - SDL_GameControllerAddMappingsFromFile(mappings); - - for (int i = 0; i < SDL_NumJoysticks(); ++i) { - if (SDL_IsGameController(i)) { - sdl_gamepads++; - if (!SDL_GameControllerOpen(i)) { - fprintf(stderr, "Could not open gamecontroller %i: %s\n", i, SDL_GetError()); - } - } - } -} - static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id) { for (int i = 0;i<4;i++) { if (!gamepads[i].initialized) { @@ -74,6 +60,40 @@ static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id) { return &gamepads[0]; } +static void init_gamepad(int joystick_index) { + if (SDL_IsGameController(joystick_index)) { + sdl_gamepads++; + SDL_GameController* controller = SDL_GameControllerOpen(joystick_index); + if (!controller) { + fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError()); + return; + } + + SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); + SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick); + if (haptic && (SDL_HapticQuery(haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) { + SDL_HapticClose(haptic); + haptic = NULL; + } + + SDL_JoystickID sdl_id = SDL_JoystickInstanceID(joystick); + PGAMEPAD_STATE state = get_gamepad(joystick_index); + state->haptic = haptic; + state->haptic_effect_id = -1; + } +} + +void sdlinput_init(char* mappings) { + memset(gamepads, 0, sizeof(gamepads)); + + SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); + SDL_InitSubSystem(SDL_INIT_HAPTIC); + SDL_GameControllerAddMappingsFromFile(mappings); + + for (int i = 0; i < SDL_NumJoysticks(); ++i) + init_gamepad(i); +} + int sdlinput_handle_event(SDL_Event* event) { int button = 0; PGAMEPAD_STATE gamepad; @@ -250,3 +270,33 @@ int sdlinput_handle_event(SDL_Event* event) { } return SDL_NOTHING; } + +void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor) { + if (controller_id >= 4) + return; + + PGAMEPAD_STATE state = &gamepads[controller_id]; + + SDL_Haptic* haptic = state->haptic; + if (!haptic) + return; + + if (state->haptic_effect_id >= 0) + SDL_HapticDestroyEffect(haptic, state->haptic_effect_id); + + if (low_freq_motor == 0 && high_freq_motor == 0) + return; + + SDL_HapticEffect effect; + SDL_memset(&effect, 0, sizeof(effect)); + effect.type = SDL_HAPTIC_LEFTRIGHT; + effect.leftright.length = SDL_HAPTIC_INFINITY; + + // SDL haptics range from 0-32767 but XInput uses 0-65535, so divide by 2 to correct for SDL's scaling + effect.leftright.large_magnitude = low_freq_motor / 2; + effect.leftright.small_magnitude = high_freq_motor / 2; + + state->haptic_effect_id = SDL_HapticNewEffect(haptic, &effect); + if (state->haptic_effect_id >= 0) + SDL_HapticRunEffect(haptic, state->haptic_effect_id, 1); +} diff --git a/src/input/sdl.h b/src/input/sdl.h index 419e97b..5f803f3 100644 --- a/src/input/sdl.h +++ b/src/input/sdl.h @@ -101,3 +101,4 @@ static const short keyCodes5[] = { void sdlinput_init(char* mappings); int sdlinput_handle_event(SDL_Event* event); +void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor); diff --git a/src/main.c b/src/main.c index e4aa9c1..5dc9fd9 100644 --- a/src/main.c +++ b/src/main.c @@ -324,6 +324,7 @@ int main(int argc, char* argv[]) { sdl_init(config.stream.width, config.stream.height, config.fullscreen); sdlinput_init(config.mapping); + rumble_handler = sdlinput_rumble; } #endif