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.
This commit is contained in:
gblues 2017-12-27 15:15:01 -08:00 committed by twinaphex
parent 8f900de65e
commit 52c754cfa8
9 changed files with 807 additions and 288 deletions

View File

@ -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

View File

@ -25,6 +25,7 @@
#include <retro_endianness.h>
#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)

View File

@ -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?
}

View File

@ -14,320 +14,131 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#include <wiiu/vpad.h>
#include <wiiu/kpad.h>
#include "../input_driver.h"
#include "../../tasks/tasks_internal.h"
#include "../../retroarch.h"
#include "../../command.h"
#include "../../gfx/video_driver.h"
#include "string.h"
#include <wiiu/pad_driver.h>
#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",
};

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef __PAD_DRIVER__H
#define __PAD_DRIVER__H
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif // HAVE_CONFIG_H
#include <wiiu/vpad.h>
#include <wiiu/kpad.h>
#include <string.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <wiiu/pad_driver.h>
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"
};

195
wiiu/input/kpad_driver.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/**
* 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 <wiiu/pad_driver.h>
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",
};

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <wiiu/pad_driver.h>
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
};

208
wiiu/input/wpad_driver.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/**
* This driver handles the Wii U Gamepad.
*
* - For wiimote and wiimote attachments, see kpad_driver.c
* - For HID controllers, see hid_driver.c
*/
#include <wiiu/pad_driver.h>
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",
};