From bb4ec5250f550b81c12e3661169b3ab65ad699a6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 22:32:44 -0800 Subject: [PATCH] Added support for non-gamepad controllers to the GameInput driver --- src/joystick/gdk/SDL_gameinputjoystick.c | 156 +++++++++++++++-------- 1 file changed, 101 insertions(+), 55 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index 3f3942815c..c3ee5a2e77 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -63,6 +63,14 @@ static IGameInput *g_pGameInput = NULL; static GameInputCallbackToken g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; +static SDL_bool GAMEINPUT_InternalIsGamepad(const GameInputDeviceInfo *info) +{ + if (info->supportedInput & GameInputKindGamepad) { + return SDL_TRUE; + } + return SDL_FALSE; +} + static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) { GAMEINPUT_InternalDevice **devicelist = NULL; @@ -234,7 +242,7 @@ static int GAMEINPUT_JoystickInit(void) hR = IGameInput_RegisterDeviceCallback(g_pGameInput, NULL, - GameInputKindGamepad, + GameInputKindController, GameInputDeviceConnected, GameInputBlockingEnumeration, NULL, @@ -360,6 +368,7 @@ static void CALLBACK GAMEINPUT_InternalGuideButtonCallback(GameInputCallbackToke static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) { GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + const GameInputDeviceInfo *info = elem->info; GAMEINPUT_InternalJoystickHwdata *hwdata = NULL; if (!elem) { @@ -374,34 +383,40 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) hwdata->devref = elem; joystick->hwdata = hwdata; - joystick->naxes = 6; - joystick->nbuttons = 11; - joystick->nhats = 1; + if (GAMEINPUT_InternalIsGamepad(info)) { + joystick->naxes = 6; + joystick->nbuttons = 11; + joystick->nhats = 1; + } else { + joystick->naxes = info->controllerAxisCount; + joystick->nbuttons = info->controllerButtonCount; + joystick->nhats = info->controllerSwitchCount; + } - if (elem->info->supportedInput & GameInputKindGamepad) { + if (GAMEINPUT_InternalIsGamepad(info)) { #if 0 /* The actual signature for this function is GameInputClient::RegisterSystemButtonCallback(struct IGameInputDevice *,enum GameInputSystemButtons,void *,void (*)(unsigned __int64,void *,struct IGameInputDevice *,unsigned __int64,enum GameInputSystemButtons,enum GameInputSystemButtons),unsigned __int64 *) */ IGameInput_RegisterGuideButtonCallback(g_pGameInput, elem->device, joystick, GAMEINPUT_InternalGuideButtonCallback, &hwdata->guide_button_callback_token); #endif } - if (elem->info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { + if (info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_TRUE); } - if (elem->info->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { + if (info->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE); } - if (elem->info->supportedInput & GameInputKindTouch) { - SDL_PrivateJoystickAddTouchpad(joystick, elem->info->touchPointCount); + if (info->supportedInput & GameInputKindTouch) { + SDL_PrivateJoystickAddTouchpad(joystick, info->touchPointCount); } - if (elem->info->supportedInput & GameInputKindMotion) { + if (info->supportedInput & GameInputKindMotion) { /* FIXME: What's the sensor update rate? */ SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f); SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f); } - if (elem->info->capabilities & GameInputDeviceCapabilityWireless) { + if (info->capabilities & GameInputDeviceCapabilityWireless) { joystick->epowerlevel = GAMEINPUT_InternalGetPowerLevel(elem->device); } else { joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; @@ -449,20 +464,6 @@ static int GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) { - static WORD s_XInputButtons[] = { - GameInputGamepadA, /* SDL_GAMEPAD_BUTTON_SOUTH */ - GameInputGamepadB, /* SDL_GAMEPAD_BUTTON_EAST */ - GameInputGamepadX, /* SDL_GAMEPAD_BUTTON_WEST */ - GameInputGamepadY, /* SDL_GAMEPAD_BUTTON_NORTH */ - GameInputGamepadView, /* SDL_GAMEPAD_BUTTON_BACK */ - 0, /* The guide button is not available */ - GameInputGamepadMenu, /* SDL_GAMEPAD_BUTTON_START */ - GameInputGamepadLeftThumbstick, /* SDL_GAMEPAD_BUTTON_LEFT_STICK */ - GameInputGamepadRightThumbstick, /* SDL_GAMEPAD_BUTTON_RIGHT_STICK */ - GameInputGamepadLeftShoulder, /* SDL_GAMEPAD_BUTTON_LEFT_SHOULDER */ - GameInputGamepadRightShoulder, /* SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER */ - }; - Uint8 btnidx = 0, btnstate = 0, hat = 0; GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; IGameInputDevice *device = hwdata->devref->device; const GameInputDeviceInfo *info = hwdata->devref->info; @@ -471,8 +472,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) GameInputGamepadState state; HRESULT hR; - - hR = IGameInput_GetCurrentReading(g_pGameInput, GameInputKindGamepad, device, &reading); + hR = IGameInput_GetCurrentReading(g_pGameInput, info->supportedInput, device, &reading); if (FAILED(hR)) { /* don't SetError here since there can be a legitimate case when there's no reading avail */ return; @@ -480,40 +480,80 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) /* FIXME: See if we can get the delta between the reading timestamp and current time and apply the offset to timestamp */ - if (IGameInputReading_GetGamepadState(reading, &state)) { - for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) { - WORD button_mask = s_XInputButtons[btnidx]; - if (!button_mask) { - continue; - } - btnstate = (state.buttons & button_mask) ? SDL_PRESSED : SDL_RELEASED; - SDL_SendJoystickButton(timestamp, joystick, btnidx, btnstate); - } + if (GAMEINPUT_InternalIsGamepad(info)) { + static WORD s_XInputButtons[] = { + GameInputGamepadA, /* SDL_GAMEPAD_BUTTON_SOUTH */ + GameInputGamepadB, /* SDL_GAMEPAD_BUTTON_EAST */ + GameInputGamepadX, /* SDL_GAMEPAD_BUTTON_WEST */ + GameInputGamepadY, /* SDL_GAMEPAD_BUTTON_NORTH */ + GameInputGamepadView, /* SDL_GAMEPAD_BUTTON_BACK */ + 0, /* The guide button is not available */ + GameInputGamepadMenu, /* SDL_GAMEPAD_BUTTON_START */ + GameInputGamepadLeftThumbstick, /* SDL_GAMEPAD_BUTTON_LEFT_STICK */ + GameInputGamepadRightThumbstick, /* SDL_GAMEPAD_BUTTON_RIGHT_STICK */ + GameInputGamepadLeftShoulder, /* SDL_GAMEPAD_BUTTON_LEFT_SHOULDER */ + GameInputGamepadRightShoulder, /* SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER */ + }; + Uint8 btnidx = 0, btnstate = 0, hat = 0; - if (state.buttons & GameInputGamepadDPadUp) { - hat |= SDL_HAT_UP; - } - if (state.buttons & GameInputGamepadDPadDown) { - hat |= SDL_HAT_DOWN; - } - if (state.buttons & GameInputGamepadDPadLeft) { - hat |= SDL_HAT_LEFT; - } - if (state.buttons & GameInputGamepadDPadRight) { - hat |= SDL_HAT_RIGHT; - } - SDL_SendJoystickHat(timestamp, joystick, 0, hat); + if (IGameInputReading_GetGamepadState(reading, &state)) { + for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) { + WORD button_mask = s_XInputButtons[btnidx]; + if (!button_mask) { + continue; + } + btnstate = (state.buttons & button_mask) ? SDL_PRESSED : SDL_RELEASED; + SDL_SendJoystickButton(timestamp, joystick, btnidx, btnstate); + } + + if (state.buttons & GameInputGamepadDPadUp) { + hat |= SDL_HAT_UP; + } + if (state.buttons & GameInputGamepadDPadDown) { + hat |= SDL_HAT_DOWN; + } + if (state.buttons & GameInputGamepadDPadLeft) { + hat |= SDL_HAT_LEFT; + } + if (state.buttons & GameInputGamepadDPadRight) { + hat |= SDL_HAT_RIGHT; + } + SDL_SendJoystickHat(timestamp, joystick, 0, hat); #define CONVERT_AXIS(v) (Sint16)(((v) < 0.0f) ? ((v)*32768.0f) : ((v)*32767.0f)) - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, CONVERT_AXIS(state.leftThumbstickX)); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, CONVERT_AXIS(-state.leftThumbstickY)); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, CONVERT_AXIS(state.rightThumbstickX)); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, CONVERT_AXIS(-state.rightThumbstickY)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, CONVERT_AXIS(state.leftThumbstickX)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, CONVERT_AXIS(-state.leftThumbstickY)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, CONVERT_AXIS(state.rightThumbstickX)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, CONVERT_AXIS(-state.rightThumbstickY)); #undef CONVERT_AXIS #define CONVERT_TRIGGER(v) (Sint16)((v)*65535.0f - 32768.0f) - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, CONVERT_TRIGGER(state.leftTrigger)); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, CONVERT_TRIGGER(state.rightTrigger)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, CONVERT_TRIGGER(state.leftTrigger)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, CONVERT_TRIGGER(state.rightTrigger)); #undef CONVERT_TRIGGER + } + } else { + bool *button_state = SDL_stack_alloc(bool, info->controllerButtonCount); + float *axis_state = SDL_stack_alloc(float, info->controllerAxisCount); + + if (button_state) { + uint32_t i; + uint32_t button_count = IGameInputReading_GetControllerButtonState(reading, info->controllerButtonCount, button_state); + for (i = 0; i < button_count; ++i) { + SDL_SendJoystickButton(timestamp, joystick, i, button_state[i]); + } + SDL_stack_free(button_state); + } + +#define CONVERT_AXIS(v) (Sint16)((v)*65535.0f - 32768.0f) + if (axis_state) { + uint32_t i; + uint32_t axis_count = IGameInputReading_GetControllerAxisState(reading, info->controllerAxisCount, axis_state); + for (i = 0; i < axis_count; ++i) { + SDL_SendJoystickAxis(timestamp, joystick, i, CONVERT_AXIS(axis_state[i])); + } + SDL_stack_free(axis_state); + } +#undef CONVERT_AXIS } if (info->supportedInput & GameInputKindTouch) { @@ -582,6 +622,12 @@ static void GAMEINPUT_JoystickQuit(void) static SDL_bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) { + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + + if (!GAMEINPUT_InternalIsGamepad(elem->info)) { + return SDL_FALSE; + } + out->a.kind = EMappingKind_Button; out->a.target = SDL_GAMEPAD_BUTTON_SOUTH;