From 52c754cfa83bbab1f2bdc2bea36c9ac0673fd3ce Mon Sep 17 00:00:00 2001 From: gblues Date: Wed, 27 Dec 2017 15:15:01 -0800 Subject: [PATCH] Split wiiu_gamepad into three sub-drivers == DETAILS Well, after a lot of code analysis, this seems like the best way to handle things on the Wii U without also completely re-architecting the I/O handling in RetroArch. How it works: - the top-level wiiu_joypad driver is now nothing more than a delegator. - the wiiu-specific drivers live in `wiiu/input/` - wpad_driver.c handles the WiiU gamepad - kpad_driver.c handles the wiimotes - hidpad_driver.c will handle HID devices like the GC adapter, DS3/DS4, etc. (I say "will" because this isn't implemented yet) == TESTING Haven't actually tried the build to see if it works, but it does compile. --- Makefile.wiiu | 4 + input/connect/joypad_connection.h | 2 + input/drivers_hid/wiiu_hid.c | 2 +- input/drivers_joypad/wiiu_joypad.c | 385 ++++++++--------------------- wiiu/include/wiiu/pad_driver.h | 102 ++++++++ wiiu/input/hidpad_driver.c | 99 ++++++++ wiiu/input/kpad_driver.c | 195 +++++++++++++++ wiiu/input/pad_functions.c | 98 ++++++++ wiiu/input/wpad_driver.c | 208 ++++++++++++++++ 9 files changed, 807 insertions(+), 288 deletions(-) create mode 100644 wiiu/include/wiiu/pad_driver.h create mode 100644 wiiu/input/hidpad_driver.c create mode 100644 wiiu/input/kpad_driver.c create mode 100644 wiiu/input/pad_functions.c create mode 100644 wiiu/input/wpad_driver.c diff --git a/Makefile.wiiu b/Makefile.wiiu index 4f8dfa3e9b..cdbdd7c6ef 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -15,6 +15,10 @@ PC_DEVELOPMENT_TCP_PORT ?=4405 OBJ := OBJ += input/drivers_hid/wiiu_hid.o +OBJ += wiiu/input/wpad_driver.o +OBJ += wiiu/input/kpad_driver.o +OBJ += wiiu/input/hidpad_driver.o +OBJ += wiiu/input/pad_functions.o OBJ += wiiu/system/memory.o OBJ += wiiu/system/exception_handler.o OBJ += wiiu/system/missing_libc_functions.o diff --git a/input/connect/joypad_connection.h b/input/connect/joypad_connection.h index 2bbf2ad438..ec78b3c4df 100644 --- a/input/connect/joypad_connection.h +++ b/input/connect/joypad_connection.h @@ -25,6 +25,7 @@ #include #include "../input_driver.h" +#define VID_NONE 0x0000 #define VID_NINTENDO swap_if_big16(0x057e) #define VID_SONY swap_if_big16(0x054c) #define VID_MICRONTEK swap_if_big16(0x0079) @@ -32,6 +33,7 @@ #define VID_PS3_CLONE swap_if_big16(0x0313) #define VID_SNES_CLONE swap_if_big16(0x081f) +#define PID_NONE 0x0000 #define PID_NINTENDO_PRO swap_if_big16(0x0330) #define PID_SONY_DS3 swap_if_big16(0x0268) #define PID_SONY_DS4 swap_if_big16(0x05c4) diff --git a/input/drivers_hid/wiiu_hid.c b/input/drivers_hid/wiiu_hid.c index 1545a7544e..a08937e628 100644 --- a/input/drivers_hid/wiiu_hid.c +++ b/input/drivers_hid/wiiu_hid.c @@ -398,7 +398,7 @@ static void log_buffer(uint8_t *data, uint32_t len) { static void wiiu_hid_do_read(wiiu_adapter_t *adapter, uint8_t *data, uint32_t length) { - log_buffer(data, length); + // log_buffer(data, length); // do_sampling() // do other stuff? } diff --git a/input/drivers_joypad/wiiu_joypad.c b/input/drivers_joypad/wiiu_joypad.c index 4fb667c34d..5418c215f2 100644 --- a/input/drivers_joypad/wiiu_joypad.c +++ b/input/drivers_joypad/wiiu_joypad.c @@ -14,320 +14,131 @@ * If not, see . */ -#ifdef HAVE_CONFIG_H -#include "../../config.h" -#endif - -#include -#include - -#include "../input_driver.h" - -#include "../../tasks/tasks_internal.h" -#include "../../retroarch.h" -#include "../../command.h" -#include "../../gfx/video_driver.h" -#include "string.h" +#include #include "wiiu_dbg.h" -#ifndef MAX_PADS -#define MAX_PADS 5 -#endif +static input_device_driver_t *pad_drivers[MAX_USERS]; +static bool ready = false; -#define WIIUINPUT_TYPE_WIIMOTE 0x00 -#define WIIUINPUT_TYPE_NUNCHUK 0x01 -#define WIIUINPUT_TYPE_CLASSIC_CONTROLLER 0x02 -#define WIIUINPUT_TYPE_PRO_CONTROLLER 0x1F -#define WIIUINPUT_TYPE_NONE 0xFD +static bool wiiu_joypad_init(void *data); +static bool wiiu_joypad_query_pad(unsigned pad); +static void wiiu_joypad_destroy(void); +static bool wiiu_joypad_button(unsigned pad, uint16_t button); +static void wiiu_joypad_get_buttons(unsigned pad, retro_bits_t *state); +static int16_t wiiu_joypad_axis(unsigned pad, uint32_t axis); +static void wiiu_joypad_poll(void); +static const char *wiiu_joypad_name(unsigned pad); -#define GAMEPAD_OFFSET 0 - -static uint64_t pad_state[MAX_PADS]; -static uint8_t pad_type[MAX_PADS-1] = {WIIUINPUT_TYPE_NONE, WIIUINPUT_TYPE_NONE, WIIUINPUT_TYPE_NONE, WIIUINPUT_TYPE_NONE}; - -/* 3 axis - one for touch/future IR support? */ -static int16_t analog_state[MAX_PADS][3][2]; -static bool wiiu_pad_inited = false; - -static const char* wiiu_joypad_name(unsigned pad) +/** + * Translates a pad to its appropriate driver. + * Note that this is a helper for build_pad_map and shouldn't be + * used directly. + */ +static input_device_driver_t *get_driver_for_pad(unsigned pad) { - if (pad > MAX_PADS) return "N/A"; + if(wpad_driver.query_pad(pad)) + return &wpad_driver; + if(kpad_driver.query_pad(pad)) + return &kpad_driver; - if (pad == GAMEPAD_OFFSET) - return "WIIU Gamepad"; - - if (pad < MAX_PADS) - { - switch (pad_type[pad-1]) - { - case WIIUINPUT_TYPE_PRO_CONTROLLER: - return "WIIU Pro Controller"; - - case WIIUINPUT_TYPE_WIIMOTE: - return "Wiimote Controller"; - - case WIIUINPUT_TYPE_NUNCHUK: - return "Nunchuk Controller"; - - case WIIUINPUT_TYPE_CLASSIC_CONTROLLER: - return "Classic Controller"; - - case WIIUINPUT_TYPE_NONE: - default: - return "N/A"; - } - } - - return "unknown"; + return &hidpad_driver; } -static void wiiu_joypad_autodetect_add(unsigned autoconf_pad) +/** + * Populates the pad_driver array. We do this once at init time so + * that lookups at runtime are constant time. + */ +static void build_pad_map(void) { - if (!input_autoconfigure_connect( - wiiu_joypad_name(autoconf_pad), - NULL, - wiiu_joypad.ident, - autoconf_pad, - 0, - 0 - )) - input_config_set_device_name(autoconf_pad, wiiu_joypad_name(autoconf_pad)); -} + unsigned i; -static bool wiiu_joypad_button(unsigned port_num, uint16_t key) -{ - if (port_num >= MAX_PADS) - return false; - - return (pad_state[port_num] & (UINT64_C(1) << key)); -} - -static void wiiu_joypad_get_buttons(unsigned port_num, retro_bits_t *state) -{ - if (port_num < MAX_PADS) - { - BITS_COPY16_PTR( state, pad_state[port_num] ); - } - else - BIT256_CLEAR_ALL_PTR(state); -} - -static int16_t wiiu_joypad_axis(unsigned port_num, uint32_t joyaxis) -{ - int val = 0; - int axis = -1; - bool is_neg = false; - bool is_pos = false; - - if (joyaxis == AXIS_NONE || port_num >= MAX_PADS) - return 0; - - if (AXIS_NEG_GET(joyaxis) < 6) - { - axis = AXIS_NEG_GET(joyaxis); - is_neg = true; - } - else if (AXIS_POS_GET(joyaxis) < 6) - { - axis = AXIS_POS_GET(joyaxis); - is_pos = true; - } - - switch (axis) - { - case 0: - val = analog_state[port_num][0][0]; - break; - - case 1: - val = analog_state[port_num][0][1]; - break; - - case 2: - val = analog_state[port_num][1][0]; - break; - - case 3: - val = analog_state[port_num][1][1]; - break; - - //For position data; just return the unmodified value - case 4: - return analog_state[port_num][2][0]; - - case 5: - return analog_state[port_num][2][1]; - } - - if (is_neg && val > 0) - val = 0; - else if (is_pos && val < 0) - val = 0; - - return val; -} - -static int16_t scaleTP(int16_t oldMin, int16_t oldMax, int16_t newMin, int16_t newMax, int16_t val) { - int32_t oldRange = oldMax - oldMin; - int32_t newRange = newMax - newMin; - return (((val - oldMin) * newRange) / oldRange) + newMin; -} - -static void wiiu_joypad_poll(void) -{ - int i, c; - VPADStatus vpad; - VPADReadError vpadError; - #if defined(ENABLE_CONTROLLER_PATCHER) - int result; - #endif - - VPADRead(0, &vpad, 1, &vpadError); - - if (!vpadError) - { - pad_state[0] = vpad.hold & VPAD_MASK_BUTTONS; /* buttons only */ - analog_state[0][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_X] = vpad.leftStick.x * 0x7FF0; - analog_state[0][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_Y] = vpad.leftStick.y * 0x7FF0; - analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_X] = vpad.rightStick.x * 0x7FF0; - analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_Y] = vpad.rightStick.y * 0x7FF0; - - /* You can only call VPADData once every loop, else the second one - won't get any data. Thus; I had to hack touch support into the existing - joystick API. Woo-hoo? Misplaced requests for touch axis are filtered - out in wiiu_input. - */ - if (vpad.tpNormal.touched && vpad.tpNormal.validity == VPAD_VALID) { - struct video_viewport vp = {0}; - video_driver_get_viewport_info(&vp); - VPADTouchData cal720p = {0}; - /* Calibrates data to a 720p screen, seems to clamp outer 12px */ - VPADGetTPCalibratedPoint(0, &cal720p, &(vpad.tpNormal)); - /* Recalibrate to match video driver's coordinate system */ - VPADTouchData calNative = {0}; - calNative.x = scaleTP(12, 1268, 0, vp.full_width, cal720p.x); - calNative.y = scaleTP(12, 708, 0, vp.full_height, cal720p.y); - /* Clamp to actual game image */ - VPADTouchData calClamped = calNative; - bool touchClamped = false; - if (calClamped.x < vp.x) { - calClamped.x = vp.x; - touchClamped = true; - } else if (calClamped.x > vp.x + vp.width) { - calClamped.x = vp.x + vp.width; - touchClamped = true; - } - if (calClamped.y < vp.y) { - calClamped.y = vp.y; - touchClamped = true; - } else if (calClamped.y > vp.y + vp.height) { - calClamped.y = vp.y + vp.height; - touchClamped = true; - } - /* Calibrate to libretro spec and save as axis 2 (idx 4,5) */ - analog_state[0][2][0] = scaleTP(vp.x, vp.x + vp.width, -0x7fff, 0x7fff, calClamped.x); - analog_state[0][2][1] = scaleTP(vp.y, vp.y + vp.height, -0x7fff, 0x7fff, calClamped.y); - - /* Emulating a button (#19) for touch; lets people assign it to menu - for that traditional RetroArch Wii U feel */ - if (!touchClamped) { - pad_state[0] |= VPAD_BUTTON_TOUCH; - } else { - pad_state[0] &= ~VPAD_BUTTON_TOUCH; - } - } else { - /* This is probably 0 anyway */ - pad_state[0] &= ~VPAD_BUTTON_TOUCH; - } - - /* panic button */ - if ((vpad.hold & (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L)) - == (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L)) - command_event(CMD_EVENT_QUIT, NULL); - } - - for (c = 0; c < 4; c++) - { - KPADData kpad; - - if (!KPADRead(c, &kpad, 1)) - continue; - - if (pad_type[c] != kpad.device_type) - { - pad_type[c] = kpad.device_type; - wiiu_joypad_autodetect_add(c + 1); - } - - switch (kpad.device_type) - { - case WIIUINPUT_TYPE_WIIMOTE: - pad_state[c + 1] = kpad.btns_h; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_X] = 0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_Y] = 0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_X] = 0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_Y] = 0; - break; - - case WIIUINPUT_TYPE_NUNCHUK: - pad_state[c + 1] = kpad.btns_h; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_X] = kpad.nunchuck.stick_x * 0x7FF0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_Y] = kpad.nunchuck.stick_y * 0x7FF0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_X] = 0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_Y] = 0; - break; - - case WIIUINPUT_TYPE_PRO_CONTROLLER: - pad_state[c + 1] = kpad.classic.btns_h & ~0x3FC0000; /* clear out emulated analog sticks */ - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_X] = kpad.classic.lstick_x * 0x7FF0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_Y] = kpad.classic.lstick_y * 0x7FF0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_X] = kpad.classic.rstick_x * 0x7FF0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_Y] = kpad.classic.rstick_y * 0x7FF0; - break; - - case WIIUINPUT_TYPE_CLASSIC_CONTROLLER: - pad_state[c + 1] = kpad.classic.btns_h & ~0xFF0000; /* clear out emulated analog sticks */ - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_X] = kpad.classic.lstick_x * 0x7FF0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_Y] = kpad.classic.lstick_y * 0x7FF0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_X] = kpad.classic.rstick_x * 0x7FF0; - analog_state[c + 1][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_Y] = kpad.classic.rstick_y * 0x7FF0; - break; - } - } + for(i = 0; i < MAX_USERS; i++) + { + pad_drivers[i] = get_driver_for_pad(i); + } } static bool wiiu_joypad_init(void* data) { - wiiu_joypad_autodetect_add(0); + // the sub-drivers have to init first, otherwise + // build_pad_map will fail (because all lookups will return false). + wpad_driver.init(data); + kpad_driver.init(data); + hidpad_driver.init(data); - wiiu_joypad_poll(); - wiiu_pad_inited = true; - (void)data; + build_pad_map(); - return true; + ready = true; + (void)data; + + return true; } static bool wiiu_joypad_query_pad(unsigned pad) { - return pad < MAX_PADS && wiiu_pad_inited; + return ready && pad < MAX_USERS; } static void wiiu_joypad_destroy(void) { - wiiu_pad_inited = false; + ready = false; + + wpad_driver.destroy(); + kpad_driver.destroy(); + hidpad_driver.destroy(); +} + +static bool wiiu_joypad_button(unsigned pad, uint16_t key) +{ + if(!wiiu_joypad_query_pad(pad)) + return false; + + return pad_drivers[pad]->button(pad, key); +} + +static void wiiu_joypad_get_buttons(unsigned pad, retro_bits_t *state) +{ + if(!wiiu_joypad_query_pad(pad)) + return; + + pad_drivers[pad]->get_buttons(pad, state); +} + +static int16_t wiiu_joypad_axis(unsigned pad, uint32_t joyaxis) +{ + if(!wiiu_joypad_query_pad(pad)) + return 0; + + return pad_drivers[pad]->axis(pad, joyaxis); +} + +static void wiiu_joypad_poll(void) +{ + wpad_driver.poll(); + kpad_driver.poll(); + hidpad_driver.poll(); +} + +static const char* wiiu_joypad_name(unsigned pad) +{ + if(!wiiu_joypad_query_pad(pad)) + return "N/A"; + + return pad_drivers[pad]->name(pad); } input_device_driver_t wiiu_joypad = { - wiiu_joypad_init, - wiiu_joypad_query_pad, - wiiu_joypad_destroy, - wiiu_joypad_button, - wiiu_joypad_get_buttons, - wiiu_joypad_axis, - wiiu_joypad_poll, - NULL, - wiiu_joypad_name, - "wiiu", + wiiu_joypad_init, + wiiu_joypad_query_pad, + wiiu_joypad_destroy, + wiiu_joypad_button, + wiiu_joypad_get_buttons, + wiiu_joypad_axis, + wiiu_joypad_poll, + NULL, + wiiu_joypad_name, + "wiiu", }; + diff --git a/wiiu/include/wiiu/pad_driver.h b/wiiu/include/wiiu/pad_driver.h new file mode 100644 index 0000000000..c91104c249 --- /dev/null +++ b/wiiu/include/wiiu/pad_driver.h @@ -0,0 +1,102 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Ali Bouhlel + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __PAD_DRIVER__H +#define __PAD_DRIVER__H + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif // HAVE_CONFIG_H + +#include +#include +#include + +#include "../../input/input_driver.h" +#include "../../input/connect/joypad_connection.h" +#include "../../tasks/tasks_internal.h" +#include "../../retroarch.h" +#include "../../verbosity.h" +#include "../../command.h" +#include "../../gfx/video_driver.h" + +/** + * Magic button sequence that triggers an exit. Useful for if the visuals are + * corrupted, but won't work in the case of a hard lock. + */ +#define PANIC_BUTTON_MASK (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L) + +/** + * Applies a standard transform to the Wii U gamepad's analog stick. + * No idea where 0x7ff0 comes from. + */ + +#define WIIU_ANALOG_FACTOR 0x7ff0 +#define WIIU_READ_STICK(stick) ((stick) * WIIU_ANALOG_FACTOR) + +/** + * the wiimote driver uses these to delimit which pads correspond to the + * wiimotes. + */ +#define PAD_GAMEPAD 0 +#define WIIU_WIIMOTE_CHANNELS 4 + +/** + * These are used by the wiimote driver to identify the wiimote configuration + * attached to the channel. + */ +// wiimote with Wii U Pro controller +#define WIIMOTE_TYPE_PRO 0x1f +// wiimote with Classic Controller +#define WIIMOTE_TYPE_CLASSIC 0x02 +// wiimote with nunchuk +#define WIIMOTE_TYPE_NUNCHUK 0x01 +// wiimote plus (no accessory attached) +#define WIIMOTE_TYPE_WIIPLUS 0x00 +// wiimote not attached on this channel +#define WIIMOTE_TYPE_NONE 0xFD + +/** + * The Wii U gamepad and wiimotes have 3 sets of x/y axes. The third + * is used by the gamepad for the touchpad driver; the wiimotes is + * currently unimplemented, but could be used for future IR pointer + * support. + */ +#define WIIU_DEVICE_INDEX_TOUCHPAD 2 + +typedef struct _axis_data axis_data; + +struct _axis_data { + int32_t axis; + bool is_negative; +}; + +typedef struct _wiiu_pad_functions wiiu_pad_functions_t; + +struct _wiiu_pad_functions { + int16_t (*get_axis_value)(int32_t axis, int16_t state[3][2], bool is_negative); + void (*set_axis_value)(int16_t state[3][2], int16_t left_x, int16_t left_y, + int16_t right_x, int16_t right_y, int16_t touch_x, int16_t touch_y); + void (*read_axis_data)(uint32_t axis, axis_data *data); + void (*connect)(unsigned pad, input_device_driver_t *driver); +}; + +extern wiiu_pad_functions_t pad_functions; +extern input_device_driver_t wpad_driver; +extern input_device_driver_t kpad_driver; +extern input_device_driver_t hidpad_driver; + +#endif // __PAD_DRIVER__H diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c new file mode 100644 index 0000000000..18682aa6ab --- /dev/null +++ b/wiiu/input/hidpad_driver.c @@ -0,0 +1,99 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Ali Bouhlel + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +static bool hidpad_init(void *data); +static bool hidpad_query_pad(unsigned pad); +static void hidpad_destroy(void); +static bool hidpad_button(unsigned pad, uint16_t button); +static void hidpad_get_buttons(unsigned pad, retro_bits_t *state); +static int16_t hidpad_axis(unsigned pad, uint32_t axis); +static void hidpad_poll(void); +static const char *hidpad_name(unsigned pad); + +static bool ready = false; + +// if the GameCube adapter is attached, this will be the offset +// of the first pad. +static unsigned gca_pad = 0; +static joypad_connection_t *hid_pads; + +static bool hidpad_init(void *data) +{ + (void *)data; + hid_pads = pad_connection_init(MAX_USERS-(WIIU_WIIMOTE_CHANNELS+1)); + + hidpad_poll(); + ready = true; + + return true; +} + +static bool hidpad_query_pad(unsigned pad) +{ + return (pad > WIIU_WIIMOTE_CHANNELS && pad < MAX_USERS); +} + +static void hidpad_destroy(void) +{ + ready = false; + if(hid_pads) { + pad_connection_destroy(hid_pads); + hid_pads = NULL; + } +} + +static bool hidpad_button(unsigned pad, uint16_t button) +{ + return false; +} + +static void hidpad_get_buttons(unsigned pad, retro_bits_t *state) +{ + BIT256_CLEAR_ALL_PTR(state); +} + +static int16_t hidpad_axis(unsigned pad, uint32_t axis) +{ + return 0; +} + +static void hidpad_poll(void) +{ +} + +static const char *hidpad_name(unsigned pad) +{ + if(!hidpad_query_pad(pad)) + return "n/a"; + + return "Unknown"; +} + +input_device_driver_t hidpad_driver = +{ + hidpad_init, + hidpad_query_pad, + hidpad_destroy, + hidpad_button, + hidpad_get_buttons, + hidpad_axis, + hidpad_poll, + NULL, + hidpad_name, + "hid" +}; diff --git a/wiiu/input/kpad_driver.c b/wiiu/input/kpad_driver.c new file mode 100644 index 0000000000..e2d9363c5d --- /dev/null +++ b/wiiu/input/kpad_driver.c @@ -0,0 +1,195 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Ali Bouhlel + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +/** + * This sub-driver handles the wiimotes. The Wii U has 4 channels available. + * This also handles wiimote attachments such as the nunchuk and classic/pro + * controllers. + */ + +#include + +static bool kpad_init(void *data); +static bool kpad_query_pad(unsigned pad); +static void kpad_destroy(void); +static bool kpad_button(unsigned pad, uint16_t button); +static void kpad_get_buttons(unsigned pad, retro_bits_t *state); +static int16_t kpad_axis(unsigned pad, uint32_t axis); +static void kpad_poll(void); +static const char *kpad_name(unsigned pad); + +typedef struct _wiimote_state wiimote_state; + +struct _wiimote_state { + uint64_t button_state; + int16_t analog_state[3][2]; + uint8_t type; +}; + +static bool ready = false; + +wiimote_state wiimotes[WIIU_WIIMOTE_CHANNELS]; + +static unsigned to_wiimote_channel(unsigned pad) { + if(pad == PAD_GAMEPAD || pad > WIIU_WIIMOTE_CHANNELS) + return 0xffffffff; + + return pad-1; +} + +static unsigned to_retro_pad(unsigned channel) { + return channel+1; +} + +static bool kpad_init(void *data) +{ + (void *)data; + + kpad_poll(); + ready = true; +} + +static bool kpad_query_pad(unsigned pad) +{ + return ready && pad <= WIIU_WIIMOTE_CHANNELS && pad > PAD_GAMEPAD; +} + +static void kpad_destroy(void) +{ + ready = false; +} + +static bool kpad_button(unsigned pad, uint16_t button_bit) +{ + if(!kpad_query_pad(pad)) + return false; + + return wiimotes[to_wiimote_channel(pad)].button_state & (UINT64_C(1) << button_bit); +} + +static void kpad_get_buttons(unsigned pad, retro_bits_t *state) +{ + if(!kpad_query_pad(pad)) + BIT256_CLEAR_ALL_PTR(state); + else + BITS_COPY16_PTR(state, wiimotes[to_wiimote_channel(pad)].button_state); +} + +static int16_t kpad_axis(unsigned pad, uint32_t axis) +{ + axis_data data; + if(!kpad_query_pad(pad) || axis == AXIS_NONE) + return 0; + + pad_functions.read_axis_data(axis, &data); + return pad_functions.get_axis_value(data.axis, wiimotes[to_wiimote_channel(pad)].analog_state, data.is_negative); +} + +static void kpad_register(unsigned channel, uint8_t device_type) +{ + if(wiimotes[channel].type != device_type) { + wiimotes[channel].type = device_type; + pad_functions.connect(to_retro_pad(channel), &kpad_driver); + } +} + +#define WIIU_PRO_BUTTON_MASK 0x3FC0000; +#define CLASSIC_BUTTON_MASK 0xFF0000; + +static void kpad_poll_one_channel(unsigned channel, KPADData *kpad) +{ + kpad_register(channel, kpad->device_type); + switch(kpad->device_type) + { + case WIIMOTE_TYPE_PRO: + wiimotes[channel].button_state = kpad->classic.btns_h & ~WIIU_PRO_BUTTON_MASK; + pad_functions.set_axis_value(wiimotes[channel].analog_state, + WIIU_READ_STICK(kpad->classic.lstick_x), + WIIU_READ_STICK(kpad->classic.lstick_y), + WIIU_READ_STICK(kpad->classic.rstick_x), + WIIU_READ_STICK(kpad->classic.rstick_y), 0, 0); + break; + case WIIMOTE_TYPE_CLASSIC: + wiimotes[channel].button_state = kpad->classic.btns_h & ~CLASSIC_BUTTON_MASK; + pad_functions.set_axis_value(wiimotes[channel].analog_state, + WIIU_READ_STICK(kpad->classic.lstick_x), + WIIU_READ_STICK(kpad->classic.lstick_y), + WIIU_READ_STICK(kpad->classic.rstick_x), + WIIU_READ_STICK(kpad->classic.rstick_y), 0, 0); + break; + case WIIMOTE_TYPE_NUNCHUK: + wiimotes[channel].button_state = kpad->btns_h; + pad_functions.set_axis_value(wiimotes[channel].analog_state, + WIIU_READ_STICK(kpad->nunchuck.stick_x), + WIIU_READ_STICK(kpad->nunchuck.stick_y), 0, 0, 0, 0); + break; + case WIIMOTE_TYPE_WIIPLUS: + wiimotes[channel].button_state = kpad->btns_h; + pad_functions.set_axis_value(wiimotes[channel].analog_state, 0, 0, 0, 0, 0, 0); + break; + } +} + +static void kpad_poll(void) +{ + unsigned channel; + + for(channel = 0; channel < WIIU_WIIMOTE_CHANNELS; channel++) + { + KPADData kpad; + + if(!KPADRead(channel, &kpad, 1)) + continue; + + kpad_poll_one_channel(channel, &kpad); + } +} + +static const char *kpad_name(unsigned pad) +{ + pad = to_wiimote_channel(pad); + if(pad >= WIIU_WIIMOTE_CHANNELS) + return "unknown"; + + switch(wiimotes[pad].type) + { + case WIIMOTE_TYPE_PRO: + return "WiiU Pro Controller"; + case WIIMOTE_TYPE_CLASSIC: + return "Classic Controller"; + case WIIMOTE_TYPE_NUNCHUK: + return "Wiimote+Nunchuk Controller"; + case WIIMOTE_TYPE_WIIPLUS: + return "Wiimote Controller"; + case WIIMOTE_TYPE_NONE: + default: + return "N/A"; + } +} + +input_device_driver_t kpad_driver = +{ + kpad_init, + kpad_query_pad, + kpad_destroy, + kpad_button, + kpad_get_buttons, + kpad_axis, + kpad_poll, + NULL, + kpad_name, + "wiimote", +}; diff --git a/wiiu/input/pad_functions.c b/wiiu/input/pad_functions.c new file mode 100644 index 0000000000..b0203d088b --- /dev/null +++ b/wiiu/input/pad_functions.c @@ -0,0 +1,98 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Ali Bouhlel + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +enum wiiu_pad_axes { + AXIS_LEFT_ANALOG_X, + AXIS_LEFT_ANALOG_Y, + AXIS_RIGHT_ANALOG_X, + AXIS_RIGHT_ANALOG_Y, + AXIS_TOUCH_X, + AXIS_TOUCH_Y, + AXIS_INVALID +}; + +static int16_t clamp_axis(int16_t value, bool is_negative) { + if(is_negative && value > 0) + return 0; + if(!is_negative && value < 0) + return 0; + + return value; +} + +static int16_t wiiu_pad_get_axis_value(int32_t axis, int16_t state[3][2], bool is_negative) { + int16_t value = 0; + + switch(axis) { + case AXIS_LEFT_ANALOG_X: + value = state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X]; + break; + case AXIS_LEFT_ANALOG_Y: + value = state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y]; + break; + case AXIS_RIGHT_ANALOG_X: + value = state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X]; + break; + case AXIS_RIGHT_ANALOG_Y: + value = state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y]; + break; + case AXIS_TOUCH_X: + return state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_X]; + case AXIS_TOUCH_Y: + return state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_Y]; + } + + return clamp_axis(value, is_negative); +} + +void wiiu_pad_set_axis_value(int16_t state[3][2], int16_t left_x, int16_t left_y, + int16_t right_x, int16_t right_y, int16_t touch_x, int16_t touch_y) +{ + state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X] = left_x; + state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y] = left_y; + state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X] = right_x; + state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y] = right_y; + state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_X] = touch_x; + state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_Y] = touch_y; +} + + +void wiiu_pad_read_axis_data(uint32_t axis, axis_data *data) { + data->axis = AXIS_POS_GET(axis); + data->is_negative = false; + + if(data->axis >= AXIS_INVALID) { + data->axis = AXIS_NEG_GET(axis); + data->is_negative = true; + } +} + +void wiiu_pad_connect(unsigned pad, input_device_driver_t *driver) { + if(!input_autoconfigure_connect(driver->name(pad), NULL, driver->ident, + pad, VID_NONE, PID_NONE)) + { + input_config_set_device_name(pad, driver->name(pad)); + } +} + +wiiu_pad_functions_t pad_functions = { + wiiu_pad_get_axis_value, + wiiu_pad_set_axis_value, + wiiu_pad_read_axis_data, + wiiu_pad_connect +}; diff --git a/wiiu/input/wpad_driver.c b/wiiu/input/wpad_driver.c new file mode 100644 index 0000000000..8ba7a5cb92 --- /dev/null +++ b/wiiu/input/wpad_driver.c @@ -0,0 +1,208 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Ali Bouhlel + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +/** + * This driver handles the Wii U Gamepad. + * + * - For wiimote and wiimote attachments, see kpad_driver.c + * - For HID controllers, see hid_driver.c + */ + +#include + +static bool wpad_init(void *data); +static bool wpad_query_pad(unsigned pad); +static void wpad_destroy(void); +static bool wpad_button(unsigned pad, uint16_t button); +static void wpad_get_buttons(unsigned pad, retro_bits_t *state); +static int16_t wpad_axis(unsigned pad, uint32_t axis); +static void wpad_poll(void); +static const char *wpad_name(unsigned pad); + +static void update_button_state(uint64_t *state, uint32_t held_buttons); +static void update_analog_state(int16_t state[3][2], VPADStatus *vpad); +static void update_touch_state(int16_t state[3][2], uint64_t *buttons, VPADStatus *vpad); +static void get_touch_coordinates(VPADTouchData *point, VPADStatus *vpad, bool *clamped); +static void get_calibrated_point(VPADTouchData *point, struct video_viewport *viewport, VPADStatus *vpad); +static void apply_clamping(VPADTouchData *point, struct video_viewport *viewport, bool *clamped); +static void check_panic_button(uint32_t held_buttons); + +static int16_t scale_touchpad(int16_t from_min, int16_t from_max, + int16_t to_min, int16_t to_max, int16_t value ); + +static bool ready = false; +static uint64_t button_state = 0; +static int16_t analog_state[3][2]; + +static bool wpad_init(void *data) { + pad_functions.connect(PAD_GAMEPAD, &wpad_driver); + wpad_poll(); + ready = true; + + return true; +} + +static bool wpad_query_pad(unsigned pad) { + return ready && pad == PAD_GAMEPAD; +} + +static void wpad_destroy(void) { + ready = false; +} + +static bool wpad_button(unsigned pad, uint16_t button_bit) { + if(!wpad_query_pad(pad)) + return false; + + return button_state & (UINT64_C(1) << button_bit); +} + +static void wpad_get_buttons(unsigned pad, retro_bits_t *state) { + if(!wpad_query_pad(pad)) + BIT256_CLEAR_ALL_PTR(state); + else + BITS_COPY16_PTR(state, button_state); +} + +static int16_t wpad_axis(unsigned pad, uint32_t axis) { + axis_data data; + + if(!wpad_query_pad(pad) || axis == AXIS_NONE) + return 0; + + pad_functions.read_axis_data(axis, &data); + return pad_functions.get_axis_value(data.axis, analog_state, data.is_negative); +} + +static void wpad_poll(void) { + VPADStatus vpad; + VPADReadError error; + + VPADRead(PAD_GAMEPAD, &vpad, 1, &error); + + if(!error) { + 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); + } +} + +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) +{ + 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); + state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X] = WIIU_READ_STICK(vpad->rightStick.x); + state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y] = WIIU_READ_STICK(vpad->rightStick.y); +} + +static void update_touch_state(int16_t state[3][2], uint64_t *buttons, VPADStatus *vpad) +{ + VPADTouchData point = {0}; + bool touch_clamped = false; + + if(!vpad->tpNormal.touched || vpad->tpNormal.validity != VPAD_VALID) + { + *buttons &= ~VPAD_BUTTON_TOUCH; + return; + } + + get_touch_coordinates(&point, vpad, &touch_clamped); + + state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_X] = point.x; + state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_Y] = point.y; + + if(!touch_clamped) { + *buttons |= VPAD_BUTTON_TOUCH; + } else { + *buttons &= ~VPAD_BUTTON_TOUCH; + } +} + +static void get_touch_coordinates(VPADTouchData *point, VPADStatus *vpad, bool *clamped) +{ + struct video_viewport viewport = {0}; + + video_driver_get_viewport_info(&viewport); + get_calibrated_point(point, &viewport, vpad); + apply_clamping(point, &viewport, clamped); +} + +static void get_calibrated_point(VPADTouchData *point, struct video_viewport *viewport, VPADStatus *vpad) { + VPADTouchData calibrated720p = {0}; + + VPADGetTPCalibratedPoint(PAD_GAMEPAD, &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); +} + +static void apply_clamping(VPADTouchData *point, struct video_viewport *viewport, bool *clamped) { + // clamp the x domain to the viewport + if(point->x < viewport->x) { + point->x = viewport->x; + *clamped = true; + } else if(point->x > (viewport->x + viewport->width)) { + point->x = viewport->x + viewport->width; + *clamped = true; + } + // clamp the y domain to the viewport + if(point->y < viewport->y) { + point->y = viewport->y; + *clamped = true; + } else if(point->y > (viewport->y + viewport->height)) { + point->y = viewport->y + viewport->height; + *clamped = true; + } +} + +static void check_panic_button(uint32_t held_buttons) { + if( (held_buttons & PANIC_BUTTON_MASK) == PANIC_BUTTON_MASK) + { + command_event(CMD_EVENT_QUIT, NULL); + } +} + +static int16_t scale_touchpad(int16_t from_min, int16_t from_max, + int16_t to_min, int16_t to_max, int16_t value ) +{ + int32_t from_range = from_max - from_min; + int32_t to_range = to_max - to_min; + + return (((value - from_min) * to_range) / from_range) + to_min; +} + +static const char *wpad_name(unsigned pad) { + return "WiiU Gamepad"; +} + +input_device_driver_t wpad_driver = +{ + wpad_init, + wpad_query_pad, + wpad_destroy, + wpad_button, + wpad_get_buttons, + wpad_axis, + wpad_poll, + NULL, + wpad_name, + "gamepad", +};