RetroArch/input/drivers_joypad/switch_joypad.c
zoltanvb e5e7e66199
Add sensor function placeholders for all joypad drivers. (#16812)
In some cases, set_sensor_state and get_sensor_input are
more related to the joypad driver, e.g. in desktop platforms
where sensors are associated rather with the joypad.

If input driver supports the sensors, it is still preferred.
Placeholder inserted for all input drivers, no functionality
added yet.
2024-07-24 15:13:16 -07:00

474 lines
15 KiB
C

#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#include "../../config.def.h"
#ifdef HAVE_LIBNX
#include <switch.h>
#else
#include <libtransistor/nx.h>
#endif
#include "../configuration.h"
#include "../../tasks/tasks_internal.h"
#include "../../retroarch.h"
#include "../../command.h"
/* TODO/FIXME - weird header include */
#include "string.h"
/* TODO/FIXME - global referenced outside */
extern uint64_t lifecycle_state;
/* TODO/FIXME - static globals */
static uint16_t button_state[DEFAULT_MAX_PADS];
static int16_t analog_state[DEFAULT_MAX_PADS][2][2];
#ifdef HAVE_LIBNX
static PadState pad_states[DEFAULT_MAX_PADS];
static HidVibrationDeviceHandle vibration_handles[DEFAULT_MAX_PADS][2];
static HidVibrationDeviceHandle vibration_handleheld[2];
static HidVibrationValue vibration_values[DEFAULT_MAX_PADS][2];
static HidVibrationValue vibration_stop;
static int previous_handheld = -1;
/* 1 = handheld, 0 = docked, -1 = first use */
static uint previous_split_joycon_setting[MAX_USERS] = { 0 };
#endif
static const char *switch_joypad_name(unsigned pad)
{
return "Switch Controller";
}
static void switch_joypad_autodetect_add(unsigned autoconf_pad)
{
input_autoconfigure_connect(
switch_joypad_name(autoconf_pad), /* name */
NULL, /* display name */
switch_joypad.ident, /* driver */
autoconf_pad, /* idx */
0, /* vid */
0); /* pid */
}
static void *switch_joypad_init(void *data)
{
#ifdef HAVE_LIBNX
unsigned i;
hidSetNpadJoyHoldType(HidNpadJoyHoldType_Horizontal);
padConfigureInput(DEFAULT_MAX_PADS, HidNpadStyleSet_NpadStandard);
/* Switch like stop behavior with muted band channels
* and frequencies set to default. */
vibration_stop.amp_low = 0.0f;
vibration_stop.freq_low = 160.0f;
vibration_stop.amp_high = 0.0f;
vibration_stop.freq_high = 320.0f;
for (i = 0; i < DEFAULT_MAX_PADS; i++)
{
if (i == 0)
padInitializeDefault(&pad_states[0]);
else
padInitialize(&pad_states[i], i);
padUpdate(&pad_states[i]);
switch_joypad_autodetect_add(i);
hidInitializeVibrationDevices(
vibration_handles[i], 2, i,
HidNpadStyleTag_NpadHandheld | HidNpadStyleTag_NpadJoyDual);
memcpy(&vibration_values[i][0],
&vibration_stop, sizeof(HidVibrationValue));
memcpy(&vibration_values[i][1],
&vibration_stop, sizeof(HidVibrationValue));
}
hidInitializeVibrationDevices(vibration_handleheld,
2, HidNpadIdType_Handheld, HidNpadStyleTag_NpadHandheld | HidNpadStyleTag_NpadJoyDual);
#else
hid_init();
switch_joypad_autodetect_add(0);
switch_joypad_autodetect_add(1);
#endif
return (void*)-1;
}
static int32_t switch_joypad_button(unsigned port_num, uint16_t joykey)
{
if (port_num >= DEFAULT_MAX_PADS)
return 0;
return (button_state[port_num] & (1 << joykey));
}
static void switch_joypad_get_buttons(unsigned port_num, input_bits_t *state)
{
if (port_num < DEFAULT_MAX_PADS)
{
BITS_COPY16_PTR(state, button_state[port_num]);
}
else
{
BIT256_CLEAR_ALL_PTR(state);
}
}
static int16_t switch_joypad_axis_state(unsigned port, uint32_t joyaxis)
{
if (AXIS_NEG_GET(joyaxis) < 4)
{
int16_t val = 0;
int16_t axis = AXIS_NEG_GET(joyaxis);
switch (axis)
{
case 0:
case 1:
val = analog_state[port][0][axis];
break;
case 2:
case 3:
val = analog_state[port][1][axis - 2];
break;
}
if (val < 0)
return val;
}
else if (AXIS_POS_GET(joyaxis) < 4)
{
int16_t val = 0;
int16_t axis = AXIS_POS_GET(joyaxis);
switch (axis)
{
case 0:
case 1:
val = analog_state[port][0][axis];
break;
case 2:
case 3:
val = analog_state[port][1][axis - 2];
break;
}
if (val > 0)
return val;
}
return 0;
}
static int16_t switch_joypad_axis(unsigned port, uint32_t joyaxis)
{
if (port >= DEFAULT_MAX_PADS)
return 0;
return switch_joypad_axis_state(port, joyaxis);
}
static int16_t switch_joypad_state(
rarch_joypad_info_t *joypad_info,
const struct retro_keybind *binds,
unsigned port)
{
unsigned i;
int16_t ret = 0;
uint16_t port_idx = joypad_info->joy_idx;
if (port_idx >= DEFAULT_MAX_PADS)
return 0;
for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
{
/* Auto-binds are per joypad, not per user. */
const uint64_t joykey = (binds[i].joykey != NO_BTN)
? binds[i].joykey : joypad_info->auto_binds[i].joykey;
const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
if (
(uint16_t)joykey != NO_BTN
&& (button_state[port_idx] & (1 << (uint16_t)joykey))
)
ret |= ( 1 << i);
else if (joyaxis != AXIS_NONE &&
((float)abs(switch_joypad_axis_state(port_idx, joyaxis))
/ 0x8000) > joypad_info->axis_threshold)
ret |= (1 << i);
}
return ret;
}
static bool switch_joypad_query_pad(unsigned pad)
{
return pad < DEFAULT_MAX_PADS && button_state[pad];
}
static void switch_joypad_destroy(void)
{
#ifdef HAVE_LIBNX
unsigned i;
previous_handheld = -1;
for (i = 0; i < MAX_USERS; i++)
previous_split_joycon_setting[i] = 0;
for (i = 0; i < DEFAULT_MAX_PADS; i++)
{
memcpy(&vibration_values[i][0],
&vibration_stop, sizeof(HidVibrationValue));
memcpy(&vibration_values[i][1],
&vibration_stop, sizeof(HidVibrationValue));
hidSendVibrationValues(vibration_handles[i], vibration_values[i], 2);
}
hidSendVibrationValues(vibration_handleheld, vibration_values[0], 2);
#else
hid_finalize();
#endif
}
#ifdef HAVE_LIBNX
static void switch_joypad_poll(void)
{
int i, handheld;
settings_t *settings = config_get_ptr();
for(i = 0; i < DEFAULT_MAX_PADS; i++)
padUpdate(&pad_states[i]);
handheld = padIsHandheld(&pad_states[0]);
if (previous_handheld == -1)
{
/* First call of this function, apply joycon settings
* according to preferences, init variables */
if (!handheld)
{
for (i = 0; i < MAX_USERS; i += 2)
{
unsigned input_split_joycon =
settings->uints.input_split_joycon[i];
if (input_split_joycon)
{
hidSetNpadJoyAssignmentModeSingleByDefault(i);
hidSetNpadJoyAssignmentModeSingleByDefault(i + 1);
}
else if (!input_split_joycon)
{
hidSetNpadJoyAssignmentModeDual(i);
hidSetNpadJoyAssignmentModeDual(i + 1);
hidMergeSingleJoyAsDualJoy(i, i + 1);
}
}
}
previous_handheld = handheld;
for (i = 0; i < MAX_USERS; i += 2)
previous_split_joycon_setting[i] = settings->uints.input_split_joycon[i];
}
if (!handheld && previous_handheld)
{
/* switching out of handheld, so make sure
* joycons are correctly split. */
for (i = 0; i < MAX_USERS; i += 2)
{
unsigned input_split_joycon = settings->uints.input_split_joycon[i];
/* CONTROLLER_PLAYER_X, X == i++ */
if (input_split_joycon)
{
hidSetNpadJoyAssignmentModeSingleByDefault(i);
hidSetNpadJoyAssignmentModeSingleByDefault(i + 1);
}
else
{
hidSetNpadJoyAssignmentModeDual(i);
hidSetNpadJoyAssignmentModeDual(i + 1);
hidMergeSingleJoyAsDualJoy(i, i + 1);
}
}
}
else if (!handheld)
{
/* split or join joycons every time the user changes a setting */
for (i = 0; i < MAX_USERS; i += 2)
{
unsigned input_split_joycon = settings->uints.input_split_joycon[i];
if (input_split_joycon
&& !previous_split_joycon_setting[i])
{
hidSetNpadJoyAssignmentModeSingleByDefault(i);
hidSetNpadJoyAssignmentModeSingleByDefault(i + 1);
}
else if (!input_split_joycon
&& previous_split_joycon_setting[i])
{
hidSetNpadJoyAssignmentModeDual(i);
hidSetNpadJoyAssignmentModeDual(i + 1);
hidMergeSingleJoyAsDualJoy(i, i + 1);
}
}
}
for (i = 0; i < MAX_USERS; i += 2)
previous_split_joycon_setting[i] = settings->uints.input_split_joycon[i];
previous_handheld = handheld;
for (i = 0; i < DEFAULT_MAX_PADS; i++)
{
HidAnalogStickState stick_left_state = padGetStickPos(&pad_states[i], 0);
HidAnalogStickState stick_right_state = padGetStickPos(&pad_states[i], 1);
unsigned input_split_joycon = settings->uints.input_split_joycon[i];
int pad_button = padGetButtons(&pad_states[i]);
if (input_split_joycon && !handheld) {
button_state[i] = 0;
if (hidGetNpadDeviceType((HidNpadIdType)i) & HidDeviceTypeBits_JoyLeft) {
if (pad_button & HidNpadButton_Left)
button_state[i] |= (uint16_t)HidNpadButton_B;
if (pad_button & HidNpadButton_Up)
button_state[i] |= (uint16_t)HidNpadButton_Y;
if (pad_button & HidNpadButton_Right)
button_state[i] |= (uint16_t)HidNpadButton_X;
if (pad_button & HidNpadButton_Down)
button_state[i] |= (uint16_t)HidNpadButton_A;
if (pad_button & HidNpadButton_LeftSL)
button_state[i] |= (uint16_t)HidNpadButton_L;
if (pad_button & HidNpadButton_LeftSR)
button_state[i] |= (uint16_t)HidNpadButton_R;
if (pad_button & HidNpadButton_StickL)
button_state[i] |= (uint16_t)HidNpadButton_StickL;
if (pad_button & HidNpadButton_Minus)
button_state[i] |= (uint16_t)HidNpadButton_Plus;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X] = -stick_left_state.y;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y] = -stick_left_state.x;
} else if (hidGetNpadDeviceType((HidNpadIdType)i) & HidDeviceTypeBits_JoyRight) {
if (pad_button & HidNpadButton_A)
button_state[i] |= (uint16_t)HidNpadButton_B;
if (pad_button & HidNpadButton_B)
button_state[i] |= (uint16_t)HidNpadButton_Y;
if (pad_button & HidNpadButton_X)
button_state[i] |= (uint16_t)HidNpadButton_A;
if (pad_button & HidNpadButton_Y)
button_state[i] |= (uint16_t)HidNpadButton_X;
if (pad_button & HidNpadButton_RightSL)
button_state[i] |= (uint16_t)HidNpadButton_L;
if (pad_button & HidNpadButton_RightSR)
button_state[i] |= (uint16_t)HidNpadButton_R;
if (pad_button & HidNpadButton_StickR)
button_state[i] |= (uint16_t)HidNpadButton_StickL;
if (pad_button & HidNpadButton_Plus)
button_state[i] |= (uint16_t)HidNpadButton_Plus;
/* Throw JoyRight state into retro left analog */
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X] = stick_right_state.y;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y] = stick_right_state.x;
} else {
/* Handle other types via Default Input Handling */
goto lblDefaultInputHandling;
}
}
else
{
/* Default Input Handling */
lblDefaultInputHandling:
button_state[i] = pad_button;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X] = stick_left_state.x;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y] = -stick_left_state.y;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X] = stick_right_state.x;
analog_state[i][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y] = -stick_right_state.y;
}
}
}
#else
static void switch_joypad_poll(void)
{
int16_t lsx, lsy, rsx, rsy;
hid_controller_t *controllers = hid_get_shared_memory()->controllers;
hid_controller_t *cont = &controllers[0];
hid_controller_state_entry_t ent = cont->main.entries[cont->main.latest_idx];
hid_controller_state_entry_t ent8 = (cont+8)->main.entries[(cont+8)->main.latest_idx];
button_state[0] = ent.button_state | ent8.button_state;
lsx = ent.left_stick_x;
lsy = ent.left_stick_y;
rsx = ent.right_stick_x;
rsy = ent.right_stick_y;
if (ent8.left_stick_x != 0 || ent8.left_stick_y != 0)
{
/* handheld overrides player 1 */
lsx = ent8.left_stick_x;
lsy = ent8.left_stick_y;
}
if (ent8.right_stick_x != 0 || ent8.right_stick_y != 0)
{
/* handheld overrides player 1 */
rsx = ent8.right_stick_x;
rsy = ent8.right_stick_y;
}
analog_state[0][RETRO_DEVICE_INDEX_ANALOG_LEFT]
[RETRO_DEVICE_ID_ANALOG_X] = lsx;
analog_state[0][RETRO_DEVICE_INDEX_ANALOG_LEFT]
[RETRO_DEVICE_ID_ANALOG_Y] = -lsy;
analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT]
[RETRO_DEVICE_ID_ANALOG_X] = rsx;
analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT]
[RETRO_DEVICE_ID_ANALOG_Y] = -rsy;
}
#endif
#ifdef HAVE_LIBNX
bool switch_joypad_set_rumble(unsigned pad,
enum retro_rumble_effect type, uint16_t strength)
{
HidVibrationDeviceHandle* handle;
float amp;
if (pad >= DEFAULT_MAX_PADS || !vibration_handles[pad])
return false;
amp = (float)strength / 65535.0f;
amp *= 0.5f; /* Max strength is too strong */
if (type == RETRO_RUMBLE_STRONG)
{
vibration_values[pad][0].amp_low = amp;
vibration_values[pad][1].amp_low = amp;
}
else
{
vibration_values[pad][0].amp_high = amp;
vibration_values[pad][1].amp_high = amp;
}
handle = (pad == 0 && !padIsNpadActive(&pad_states[0], HidNpadIdType_No1))
? vibration_handleheld : vibration_handles[pad];
return R_SUCCEEDED(hidSendVibrationValues(handle, vibration_values[pad], 2));
}
#endif
input_device_driver_t switch_joypad = {
switch_joypad_init,
switch_joypad_query_pad,
switch_joypad_destroy,
switch_joypad_button,
switch_joypad_state,
switch_joypad_get_buttons,
switch_joypad_axis,
switch_joypad_poll,
#ifdef HAVE_LIBNX
switch_joypad_set_rumble,
#else
NULL, /* set_rumble */
#endif
NULL, /* set_rumble_gain */
NULL, /* set_sensor_state */
NULL, /* get_sensor_input */
switch_joypad_name,
"switch"
};