Implement controller hotplugging for SDL

This commit is contained in:
Cameron Gutman 2022-01-09 19:41:35 -06:00
parent 634a0eee15
commit bbdd7e5b24

View File

@ -33,6 +33,7 @@ typedef struct _GAMEPAD_STATE {
short rightStickX, rightStickY; short rightStickX, rightStickY;
int buttons; int buttons;
SDL_JoystickID sdl_id; SDL_JoystickID sdl_id;
SDL_GameController* controller;
SDL_Haptic* haptic; SDL_Haptic* haptic;
int haptic_effect_id; int haptic_effect_id;
short id; short id;
@ -46,40 +47,84 @@ static int activeGamepadMask = 0;
int sdl_gamepads = 0; int sdl_gamepads = 0;
static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id) { static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id, bool add) {
// See if a gamepad already exists
for (int i = 0;i<4;i++) {
if (gamepads[i].initialized && gamepads[i].sdl_id == sdl_id)
return &gamepads[i];
}
if (!add)
return NULL;
for (int i = 0;i<4;i++) { for (int i = 0;i<4;i++) {
if (!gamepads[i].initialized) { if (!gamepads[i].initialized) {
gamepads[i].sdl_id = sdl_id; gamepads[i].sdl_id = sdl_id;
gamepads[i].id = i; gamepads[i].id = i;
gamepads[i].initialized = true; gamepads[i].initialized = true;
// This will cause connection of the virtual controller on the host PC
activeGamepadMask |= (1 << i); activeGamepadMask |= (1 << i);
LiSendMultiControllerEvent(i, activeGamepadMask, 0, 0, 0, 0, 0, 0, 0);
return &gamepads[i]; return &gamepads[i];
} else if (gamepads[i].sdl_id == sdl_id) }
return &gamepads[i];
} }
return &gamepads[0]; return &gamepads[0];
} }
static void init_gamepad(int joystick_index) { static void add_gamepad(int joystick_index) {
if (SDL_IsGameController(joystick_index)) { SDL_GameController* controller = SDL_GameControllerOpen(joystick_index);
sdl_gamepads++; if (!controller) {
SDL_GameController* controller = SDL_GameControllerOpen(joystick_index); fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError());
if (!controller) { return;
fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError()); }
return;
}
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick); SDL_JoystickID joystick_id = SDL_JoystickInstanceID(joystick);
if (haptic && (SDL_HapticQuery(haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
SDL_HapticClose(haptic);
haptic = NULL;
}
SDL_JoystickID sdl_id = SDL_JoystickInstanceID(joystick); if (get_gamepad(joystick_id, false)) {
PGAMEPAD_STATE state = get_gamepad(joystick_index); // Already added
state->haptic = haptic; SDL_GameControllerClose(controller);
state->haptic_effect_id = -1; return;
}
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
if (haptic && (SDL_HapticQuery(haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
SDL_HapticClose(haptic);
haptic = NULL;
}
PGAMEPAD_STATE state = get_gamepad(joystick_id, true);
state->controller = controller;
state->haptic = haptic;
state->haptic_effect_id = -1;
sdl_gamepads++;
}
static void remove_gamepad(SDL_JoystickID sdl_id) {
for (int i = 0;i<4;i++) {
if (gamepads[i].initialized && gamepads[i].sdl_id == sdl_id) {
if (gamepads[i].haptic_effect_id >= 0) {
SDL_HapticDestroyEffect(gamepads[i].haptic, gamepads[i].haptic_effect_id);
}
if (gamepads[i].haptic) {
SDL_HapticClose(gamepads[i].haptic);
}
SDL_GameControllerClose(gamepads[i].controller);
// This will cause disconnection of the virtual controller on the host PC
activeGamepadMask &= ~(1 << i);
LiSendMultiControllerEvent(i, activeGamepadMask, 0, 0, 0, 0, 0, 0, 0);
memset(&gamepads[i], 0, sizeof(*gamepads));
sdl_gamepads--;
break;
}
} }
} }
@ -90,8 +135,12 @@ void sdlinput_init(char* mappings) {
SDL_InitSubSystem(SDL_INIT_HAPTIC); SDL_InitSubSystem(SDL_INIT_HAPTIC);
SDL_GameControllerAddMappingsFromFile(mappings); SDL_GameControllerAddMappingsFromFile(mappings);
for (int i = 0; i < SDL_NumJoysticks(); ++i) // Add game controllers here to ensure an accurate count
init_gamepad(i); // goes to the host when starting a new session.
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
if (SDL_IsGameController(i))
add_gamepad(i);
}
} }
int sdlinput_handle_event(SDL_Event* event) { int sdlinput_handle_event(SDL_Event* event) {
@ -184,7 +233,9 @@ int sdlinput_handle_event(SDL_Event* event) {
LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, keyboard_modifiers); LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, keyboard_modifiers);
break; break;
case SDL_CONTROLLERAXISMOTION: case SDL_CONTROLLERAXISMOTION:
gamepad = get_gamepad(event->caxis.which); gamepad = get_gamepad(event->caxis.which, false);
if (!gamepad)
return SDL_NOTHING;
switch (event->caxis.axis) { switch (event->caxis.axis) {
case SDL_CONTROLLER_AXIS_LEFTX: case SDL_CONTROLLER_AXIS_LEFTX:
gamepad->leftStickX = event->caxis.value; gamepad->leftStickX = event->caxis.value;
@ -211,7 +262,9 @@ int sdlinput_handle_event(SDL_Event* event) {
break; break;
case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERBUTTONUP:
gamepad = get_gamepad(event->cbutton.which); gamepad = get_gamepad(event->cbutton.which, false);
if (!gamepad)
return SDL_NOTHING;
switch (event->cbutton.button) { switch (event->cbutton.button) {
case SDL_CONTROLLER_BUTTON_A: case SDL_CONTROLLER_BUTTON_A:
button = A_FLAG; button = A_FLAG;
@ -271,6 +324,12 @@ int sdlinput_handle_event(SDL_Event* event) {
LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY); LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY);
break; break;
case SDL_CONTROLLERDEVICEADDED:
add_gamepad(event->cdevice.which);
break;
case SDL_CONTROLLERDEVICEREMOVED:
remove_gamepad(event->cdevice.which);
break;
} }
return SDL_NOTHING; return SDL_NOTHING;
} }