From f22af81e7dae3aa8f0ba2bcbc0ce0300e7c28e28 Mon Sep 17 00:00:00 2001 From: Ash Logan Date: Sun, 17 May 2020 16:32:51 +1000 Subject: [PATCH] (WiiU/wpad) Allow hotplugging Gamepad, support >1 channel --- input/drivers_joypad/wiiu/wpad_driver.c | 150 +++++++++++++++++++----- input/include/wiiu/input.h | 2 +- wiiu/include/wiiu/vpad.h | 5 + 3 files changed, 125 insertions(+), 32 deletions(-) diff --git a/input/drivers_joypad/wiiu/wpad_driver.c b/input/drivers_joypad/wiiu/wpad_driver.c index 1a755d92d9..1a6fb83d80 100644 --- a/input/drivers_joypad/wiiu/wpad_driver.c +++ b/input/drivers_joypad/wiiu/wpad_driver.c @@ -25,16 +25,80 @@ #define PANIC_BUTTON_MASK (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L) -static bool wpad_ready = false; -static uint64_t button_state = 0; -static int16_t analog_state[3][2]; +typedef struct _drc_state drc_state; +struct _drc_state +{ + uint64_t button_state; + int16_t analog_state[3][2]; +}; +static drc_state gamepads[WIIU_GAMEPAD_CHANNELS] = { 0 }; + +#define WPAD_INVALID_CHANNEL -1 +static int channel_slot_map[WIIU_GAMEPAD_CHANNELS] = { WPAD_INVALID_CHANNEL, WPAD_INVALID_CHANNEL }; + +static VPADChan to_gamepad_channel(unsigned pad) { + unsigned i; + + for (i = 0; i < WIIU_GAMEPAD_CHANNELS; i++) { + if (channel_slot_map[i] == pad) { + return i; + } + } + + return WPAD_INVALID_CHANNEL; +} + +static void wpad_deregister(unsigned channel) { + unsigned slot; + + if (channel >= WIIU_GAMEPAD_CHANNELS) + return; + + /* See if Gamepad is already disconnected */ + if (channel_slot_map[channel] == WPAD_INVALID_CHANNEL) + return; + + /* Sanity check, about to use as unsigned */ + if (channel_slot_map[channel] < 0) { + channel_slot_map[channel] = WPAD_INVALID_CHANNEL; + return; + } + + slot = (unsigned)channel_slot_map[channel]; + if (slot >= MAX_USERS) + return; + + input_autoconfigure_disconnect(slot, wpad_driver.ident); + hid_instance.pad_list[slot].connected = false; + channel_slot_map[channel] = WPAD_INVALID_CHANNEL; +} + +static void wpad_register(unsigned channel) { + int slot; + + if (channel >= WIIU_GAMEPAD_CHANNELS) + return; + + /* Check if gamepad is already handled + Other checks not needed here - about to overwrite channel_slot_map entry*/ + if (channel_slot_map[channel] != WPAD_INVALID_CHANNEL) + return; + + slot = pad_connection_find_vacant_pad(hid_instance.pad_list); + if(slot < 0) + return; + + hid_instance.pad_list[slot].connected = true; + input_pad_connect(slot, &wpad_driver); + channel_slot_map[channel] = slot; +} static void update_button_state(uint64_t *state, uint32_t held_buttons) { *state = held_buttons & VPAD_MASK_BUTTONS; } -static void update_analog_state(int16_t state[3][2], VPADStatus *vpad) +static void update_analog_state(int16_t state[3][2], VPADStatus *vpad) { state[RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_X] = WIIU_READ_STICK(vpad->leftStick.x); state[RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_Y] = WIIU_READ_STICK(vpad->leftStick.y); @@ -51,11 +115,12 @@ static int16_t scale_touchpad(int16_t from_min, int16_t from_max, return (((value - from_min) * to_range) / from_range) + to_min; } -static void get_calibrated_point(VPADTouchData *point, struct video_viewport *viewport, VPADStatus *vpad) +static void get_calibrated_point(VPADTouchData *point, + struct video_viewport *viewport, VPADStatus *vpad, unsigned channel) { VPADTouchData calibrated720p = {0}; - VPADGetTPCalibratedPoint(PAD_GAMEPAD, &calibrated720p, &(vpad->tpNormal)); + VPADGetTPCalibratedPoint(channel, &calibrated720p, &(vpad->tpNormal)); point->x = scale_touchpad(12, 1268, 0, viewport->full_width, calibrated720p.x); point->y = scale_touchpad(12, 708, 0, viewport->full_height, calibrated720p.y); } @@ -88,9 +153,9 @@ static void apply_clamping(VPADTouchData *point, struct video_viewport *viewport } static void get_touch_coordinates(VPADTouchData *point, VPADStatus *vpad, - struct video_viewport *viewport, bool *clamped) + VPADChan channel, struct video_viewport *viewport, bool *clamped) { - get_calibrated_point(point, viewport, vpad); + get_calibrated_point(point, viewport, vpad, channel); apply_clamping(point, viewport, clamped); } @@ -127,7 +192,7 @@ static void log_coords(int16_t x, int16_t y) } #endif -static void update_touch_state(int16_t state[3][2], uint64_t *buttons, VPADStatus *vpad) +static void update_touch_state(int16_t state[3][2], uint64_t *buttons, VPADStatus *vpad, VPADChan channel) { VPADTouchData point = {0}; struct video_viewport viewport = {0}; @@ -140,7 +205,7 @@ static void update_touch_state(int16_t state[3][2], uint64_t *buttons, VPADStatu } video_driver_get_viewport_info(&viewport); - get_touch_coordinates(&point, vpad, &viewport, &touch_clamped); + get_touch_coordinates(&point, vpad, channel, &viewport, &touch_clamped); state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_X] = scale_touchpad( viewport.x, viewport.x + viewport.width, -0x7fff, 0x7fff, point.x); @@ -168,67 +233,90 @@ static void wpad_poll(void) { VPADStatus vpad; VPADReadError error; + VPADChan channel; - VPADRead(PAD_GAMEPAD, &vpad, 1, &error); + for (channel = VPAD_CHAN_0; channel < WIIU_GAMEPAD_CHANNELS; channel++) { + VPADRead(channel, &vpad, 1, &error); - if (error) - return; + if (error == VPAD_READ_SUCCESS || error == VPAD_READ_NO_SAMPLES) { + /* Gamepad is connected! */ + wpad_register(channel); + } else if (error == VPAD_READ_INVALID_CONTROLLER) { + wpad_deregister(channel); + } - update_button_state(&button_state, vpad.hold); - update_analog_state(analog_state, &vpad); - update_touch_state(analog_state, &button_state, &vpad); - check_panic_button(vpad.hold); + if (error == VPAD_READ_SUCCESS) { + update_button_state(&gamepads[channel].button_state, vpad.hold); + update_analog_state(gamepads[channel].analog_state, &vpad); + update_touch_state(gamepads[channel].analog_state, &gamepads[channel].button_state, &vpad, channel); + check_panic_button(vpad.hold); + } + } } static bool wpad_init(void *data) { - int slot = pad_connection_find_vacant_pad(hid_instance.pad_list); - if(slot < 0) - return false; - - hid_instance.pad_list[slot].connected = true; - input_pad_connect(slot, &wpad_driver); wpad_poll(); - wpad_ready = true; return true; } static bool wpad_query_pad(unsigned pad) { - return wpad_ready && pad < MAX_USERS; + return pad < MAX_USERS && (to_gamepad_channel(pad) != WPAD_INVALID_CHANNEL); } static void wpad_destroy(void) { - wpad_ready = false; + } static bool wpad_button(unsigned pad, uint16_t button_bit) { + VPADChan channel; + if (!wpad_query_pad(pad)) return false; - return button_state & (UINT64_C(1) << button_bit); + channel = to_gamepad_channel(pad); + if (channel < 0) + return false; + + return gamepads[channel].button_state & (UINT64_C(1) << button_bit); } static void wpad_get_buttons(unsigned pad, input_bits_t *state) { - if (!wpad_query_pad(pad)) + VPADChan channel; + + if (!wpad_query_pad(pad)) { BIT256_CLEAR_ALL_PTR(state); - else - BITS_COPY32_PTR(state, button_state); + return; + } + + channel = to_gamepad_channel(pad); + if (channel < 0) { + BIT256_CLEAR_ALL_PTR(state); + return; + } + + BITS_COPY32_PTR(state, gamepads[channel].button_state); } static int16_t wpad_axis(unsigned pad, uint32_t axis) { axis_data data; + VPADChan channel; if (!wpad_query_pad(pad) || axis == AXIS_NONE) return 0; + channel = to_gamepad_channel(pad); + if (channel < 0) + return 0; + pad_functions.read_axis_data(axis, &data); - return pad_functions.get_axis_value(data.axis, analog_state, data.is_negative); + return pad_functions.get_axis_value(data.axis, gamepads[channel].analog_state, data.is_negative); } static const char *wpad_name(unsigned pad) diff --git a/input/include/wiiu/input.h b/input/include/wiiu/input.h index a2f450abd2..dbe2995e9f 100644 --- a/input/include/wiiu/input.h +++ b/input/include/wiiu/input.h @@ -51,7 +51,7 @@ #define WIIU_DEVICE_INDEX_TOUCHPAD 2 -#define PAD_GAMEPAD 0 +#define WIIU_GAMEPAD_CHANNELS 2 #define WIIU_WIIMOTE_CHANNELS 4 #define WIIU_ANALOG_FACTOR 0x7ff0 diff --git a/wiiu/include/wiiu/vpad.h b/wiiu/include/wiiu/vpad.h index 60d27deaa1..6c3da494cd 100644 --- a/wiiu/include/wiiu/vpad.h +++ b/wiiu/include/wiiu/vpad.h @@ -103,6 +103,11 @@ typedef enum VPADReadError VPAD_READ_INVALID_CONTROLLER = -2, } VPADReadError; +typedef enum VPADChan +{ + VPAD_CHAN_0 = 0, +} VPADChan; + typedef struct VPADVec2D { float x;