(WiiU/wpad) Allow hotplugging Gamepad, support >1 channel

This commit is contained in:
Ash Logan 2020-05-17 16:32:51 +10:00
parent f1158b988c
commit f22af81e7d
3 changed files with 125 additions and 32 deletions

View File

@ -25,9 +25,73 @@
#define PANIC_BUTTON_MASK (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L) #define PANIC_BUTTON_MASK (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L)
static bool wpad_ready = false; typedef struct _drc_state drc_state;
static uint64_t button_state = 0; struct _drc_state
static int16_t analog_state[3][2]; {
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) static void update_button_state(uint64_t *state, uint32_t held_buttons)
{ {
@ -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; 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}; 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->x = scale_touchpad(12, 1268, 0, viewport->full_width, calibrated720p.x);
point->y = scale_touchpad(12, 708, 0, viewport->full_height, calibrated720p.y); 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, 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); apply_clamping(point, viewport, clamped);
} }
@ -127,7 +192,7 @@ static void log_coords(int16_t x, int16_t y)
} }
#endif #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}; VPADTouchData point = {0};
struct video_viewport viewport = {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); 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( state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_X] = scale_touchpad(
viewport.x, viewport.x + viewport.width, -0x7fff, 0x7fff, point.x); viewport.x, viewport.x + viewport.width, -0x7fff, 0x7fff, point.x);
@ -168,67 +233,90 @@ static void wpad_poll(void)
{ {
VPADStatus vpad; VPADStatus vpad;
VPADReadError error; 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) if (error == VPAD_READ_SUCCESS || error == VPAD_READ_NO_SAMPLES) {
return; /* Gamepad is connected! */
wpad_register(channel);
} else if (error == VPAD_READ_INVALID_CONTROLLER) {
wpad_deregister(channel);
}
update_button_state(&button_state, vpad.hold); if (error == VPAD_READ_SUCCESS) {
update_analog_state(analog_state, &vpad); update_button_state(&gamepads[channel].button_state, vpad.hold);
update_touch_state(analog_state, &button_state, &vpad); 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); check_panic_button(vpad.hold);
}
}
} }
static bool wpad_init(void *data) 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_poll();
wpad_ready = true;
return true; return true;
} }
static bool wpad_query_pad(unsigned pad) 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) static void wpad_destroy(void)
{ {
wpad_ready = false;
} }
static bool wpad_button(unsigned pad, uint16_t button_bit) static bool wpad_button(unsigned pad, uint16_t button_bit)
{ {
VPADChan channel;
if (!wpad_query_pad(pad)) if (!wpad_query_pad(pad))
return false; 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) 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); BIT256_CLEAR_ALL_PTR(state);
else return;
BITS_COPY32_PTR(state, button_state); }
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) static int16_t wpad_axis(unsigned pad, uint32_t axis)
{ {
axis_data data; axis_data data;
VPADChan channel;
if (!wpad_query_pad(pad) || axis == AXIS_NONE) if (!wpad_query_pad(pad) || axis == AXIS_NONE)
return 0; return 0;
channel = to_gamepad_channel(pad);
if (channel < 0)
return 0;
pad_functions.read_axis_data(axis, &data); 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) static const char *wpad_name(unsigned pad)

View File

@ -51,7 +51,7 @@
#define WIIU_DEVICE_INDEX_TOUCHPAD 2 #define WIIU_DEVICE_INDEX_TOUCHPAD 2
#define PAD_GAMEPAD 0 #define WIIU_GAMEPAD_CHANNELS 2
#define WIIU_WIIMOTE_CHANNELS 4 #define WIIU_WIIMOTE_CHANNELS 4
#define WIIU_ANALOG_FACTOR 0x7ff0 #define WIIU_ANALOG_FACTOR 0x7ff0

View File

@ -103,6 +103,11 @@ typedef enum VPADReadError
VPAD_READ_INVALID_CONTROLLER = -2, VPAD_READ_INVALID_CONTROLLER = -2,
} VPADReadError; } VPADReadError;
typedef enum VPADChan
{
VPAD_CHAN_0 = 0,
} VPADChan;
typedef struct VPADVec2D typedef struct VPADVec2D
{ {
float x; float x;