Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vertical mode for Nintendo Joy-Con #6303

Merged
merged 3 commits into from
Oct 2, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/SDL_hints.h
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,15 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS"

/**
* \brief A variable controlling whether Nintendo Switch Joy-Con controllers will be in vertical mode when using the HIDAPI driver
*
* This variable can be set to the following values:
* "0" - Left and right Joy-Con controllers will not be in vertical mode (the default)
* "1" - Left and right Joy-Con controllers will be in vertical mode
*/
#define SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS "SDL_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS"

/**
* \brief A variable controlling whether the HIDAPI driver for Amazon Luna controllers connected via Bluetooth should be used.
*
Expand Down
46 changes: 43 additions & 3 deletions src/joystick/SDL_gamecontroller.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "controller_type.h"
#include "usb_ids.h"
#include "hidapi/SDL_hidapi_nintendo.h"
#include "../SDL_hints_c.h"

#if !SDL_EVENTS_DISABLED
#include "../events/SDL_events_c.h"
Expand Down Expand Up @@ -115,6 +116,7 @@ static ControllerMapping_t *s_pSupportedControllers = NULL;
static ControllerMapping_t *s_pDefaultMapping = NULL;
static ControllerMapping_t *s_pXInputMapping = NULL;
static char gamecontroller_magic;
SDL_bool SDL_HIDAPI_vertical_joycons = SDL_FALSE;

/* The SDL game controller structure */
struct _SDL_GameController
Expand Down Expand Up @@ -606,8 +608,22 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI
}
break;
default:
/* Mini gamepad mode */
SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,", sizeof(mapping_string));
if (SDL_HIDAPI_vertical_joycons) {
/* Vertical mode */
if (guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
SDL_strlcat(mapping_string, "back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b15,leftshoulder:b16,rightshoulder:b17,", sizeof(mapping_string));
} else {
SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,leftshoulder:b18,rightshoulder:b19,", sizeof(mapping_string));
}
} else {
/* Mini gamepad mode */
SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,", sizeof(mapping_string));
if (guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
SDL_strlcat(mapping_string, "lefttrigger:a4,leftshoulder:b16,", sizeof(mapping_string));
happyharryh marked this conversation as resolved.
Show resolved Hide resolved
} else {
SDL_strlcat(mapping_string, "righttrigger:a5,rightshoulder:b19,", sizeof(mapping_string));
}
}
break;
}
} else {
Expand All @@ -625,7 +641,7 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI
SDL_strlcat(mapping_string, "paddle1:b16,paddle2:b15,", sizeof(mapping_string));
} else if (SDL_IsJoystickNintendoSwitchJoyConPair(vendor, product)) {
/* The Nintendo Switch Joy-Con combined controller has a share button */
SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
SDL_strlcat(mapping_string, "misc1:b15,leftshoulder:b16,rightshoulder:b17,leftshoulder:b18,rightshoulder:b19,", sizeof(mapping_string));
} else {
switch (SDL_GetJoystickGameControllerTypeFromGUID(guid, NULL)) {
case SDL_CONTROLLER_TYPE_PS4:
Expand Down Expand Up @@ -1798,6 +1814,26 @@ static SDL_bool SDL_GetControllerMappingFilePath(char *path, size_t size)
#endif
}

static void SDLCALL
SDL_JoystickHidapiVerticalJoyConsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_HIDAPI_vertical_joycons = SDL_GetStringBoolean(hint, SDL_FALSE);

Uint16 vendor;
Uint16 product;
for (SDL_GameController *controller = SDL_gamecontrollers; controller; controller = controller->next) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please lose this C99'ism (that for loop initial declaration)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would work, but isn't thread-safe and would override any custom controller mapping. For now let's remove this code and say this hint is only read at initialization and call it good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would work, but isn't thread-safe and would override any custom controller mapping. For now let's remove this code and say this hint is only read at initialization and call it good.

Updating of the mapping has been removed.

if (SDL_IsJoystickHIDAPI(controller->joystick->guid)) {
SDL_GetJoystickGUIDInfo(controller->joystick->guid, &vendor, &product, NULL, NULL);
if (vendor == USB_VENDOR_NINTENDO &&
(product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||
product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT)) {
ControllerMapping_t* controllerMapping = SDL_CreateMappingForHIDAPIController(controller->joystick->guid);
SDL_PrivateLoadButtonMapping(controller, controllerMapping->name, controllerMapping->mapping);
}
}
}
}

/*
* Initialize the game controller system, mostly load our DB of controller config mappings
*/
Expand Down Expand Up @@ -1826,6 +1862,8 @@ SDL_GameControllerInitMappings(void)
SDL_GameControllerIgnoreDevicesChanged, NULL);
SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT,
SDL_GameControllerIgnoreDevicesExceptChanged, NULL);
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS,
SDL_JoystickHidapiVerticalJoyConsChanged, NULL);

return (0);
}
Expand Down Expand Up @@ -2866,6 +2904,8 @@ SDL_GameControllerQuitMappings(void)
SDL_GameControllerIgnoreDevicesChanged, NULL);
SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT,
SDL_GameControllerIgnoreDevicesExceptChanged, NULL);
SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS,
SDL_JoystickHidapiVerticalJoyConsChanged, NULL);

if (SDL_allowed_controllers.entries) {
SDL_free(SDL_allowed_controllers.entries);
Expand Down
18 changes: 15 additions & 3 deletions src/joystick/hidapi/SDL_hidapi_switch.c
Original file line number Diff line number Diff line change
Expand Up @@ -1410,7 +1410,7 @@ HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
SDL_PlayerLEDHintChanged, ctx);

/* Initialize the joystick capabilities */
joystick->nbuttons = 16;
joystick->nbuttons = 20;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = device->is_bluetooth ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;

Expand Down Expand Up @@ -1824,6 +1824,8 @@ static void HandleCombinedControllerStateL(SDL_Joystick *joystick, SDL_DriverSwi
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_PADDLE2, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_PADDLE1, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
axis = (data & 0x80) ? 32767 : -32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
Expand Down Expand Up @@ -1857,6 +1859,9 @@ static void HandleMiniControllerStateL(SDL_Joystick *joystick, SDL_DriverSwitch_
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_PADDLE1, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
axis = (data & 0x80) ? 32767 : -32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
}

axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
Expand All @@ -1878,6 +1883,8 @@ static void HandleCombinedControllerStateR(SDL_Joystick *joystick, SDL_DriverSwi
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_PADDLE4, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_PADDLE3, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
axis = (data & 0x80) ? 32767 : -32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
Expand Down Expand Up @@ -1911,6 +1918,9 @@ static void HandleMiniControllerStateR(SDL_Joystick *joystick, SDL_DriverSwitch_
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_PADDLE4, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
axis = (data & 0x80) ? 32767 : -32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
}

if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
Expand All @@ -1931,14 +1941,16 @@ static void HandleMiniControllerStateR(SDL_Joystick *joystick, SDL_DriverSwitch_

static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
{
extern SDL_bool SDL_HIDAPI_vertical_joycons;

if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
if (ctx->device->parent) {
if (ctx->device->parent || SDL_HIDAPI_vertical_joycons) {
HandleCombinedControllerStateL(joystick, ctx, packet);
} else {
HandleMiniControllerStateL(joystick, ctx, packet);
}
} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
if (ctx->device->parent) {
if (ctx->device->parent || SDL_HIDAPI_vertical_joycons) {
HandleCombinedControllerStateR(joystick, ctx, packet);
} else {
HandleMiniControllerStateR(joystick, ctx, packet);
Expand Down