From fb8a32cc75b228030220ecfbca95b08999db0ec6 Mon Sep 17 00:00:00 2001
From: Eri the Switch <[EMAIL REDACTED]>
Date: Mon, 24 Aug 2026 11:38:03 +0300
Subject: [PATCH] joystick: Accept partial user calibration for sticks on
Switch controllers
This allows user stick calibration to be applied on JoyCons and possibly
partially user calibrated Pro controllers.
---
src/joystick/hidapi/SDL_hidapi_switch.c | 31 ++++++++++++++++---------
1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
index e3918ae7246c9..0eb7796dd0122 100644
--- a/src/joystick/hidapi/SDL_hidapi_switch.c
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -978,14 +978,14 @@ static bool SetIMUEnabled(SDL_DriverSwitch_Context *ctx, bool enabled)
static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
{
- Uint8 *pLeftStickCal = NULL;
- Uint8 *pRightStickCal = NULL;
+ Uint8 pLeftStickCal[9];
+ Uint8 pRightStickCal[9];
+ bool have_left_stick_cal = false, have_right_stick_cal = false;
size_t stick, axis;
SwitchSubcommandInputPacket_t *user_reply = NULL;
SwitchSubcommandInputPacket_t *factory_reply = NULL;
SwitchSPIOpData_t readUserParams;
SwitchSPIOpData_t readFactoryParams;
- Uint8 userParamsReadSuccessCount = 0;
// Read User Calibration Info
readUserParams.unAddress = k_unSPIStickUserCalibrationStartOffset;
@@ -1000,17 +1000,17 @@ static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
// Automatically select the user calibration if magic bytes are set
if (user_reply && user_reply->stickUserCalibration.rgucLeftMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucLeftMagic[1] == 0xA1) {
- userParamsReadSuccessCount += 1;
- pLeftStickCal = user_reply->stickUserCalibration.rgucLeftCalibration;
+ have_left_stick_cal = true;
+ SDL_memcpy(pLeftStickCal, user_reply->stickUserCalibration.rgucLeftCalibration, sizeof(pLeftStickCal));
}
if (user_reply && user_reply->stickUserCalibration.rgucRightMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucRightMagic[1] == 0xA1) {
- userParamsReadSuccessCount += 1;
- pRightStickCal = user_reply->stickUserCalibration.rgucRightCalibration;
+ have_right_stick_cal = true;
+ SDL_memcpy(pRightStickCal, user_reply->stickUserCalibration.rgucRightCalibration, sizeof(pRightStickCal));
}
// Only read the factory calibration info if we failed to receive the correct magic bytes
- if (userParamsReadSuccessCount < 2) {
+ if (!have_left_stick_cal || !have_right_stick_cal) {
// Read Factory Calibration Info
readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset;
readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength;
@@ -1023,8 +1023,17 @@ static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
if (factory_reply->stickFactoryCalibration.opData.unAddress == k_unSPIStickFactoryCalibrationStartOffset) {
// We successfully read the calibration data
- pLeftStickCal = factory_reply->stickFactoryCalibration.rgucLeftCalibration;
- pRightStickCal = factory_reply->stickFactoryCalibration.rgucRightCalibration;
+ // Avoid overriding user calibration if it's present for only one stick (known to happen on JoyCons)
+ if (!have_left_stick_cal) {
+ have_left_stick_cal = true;
+ SDL_memcpy(pLeftStickCal, user_reply->stickFactoryCalibration.rgucLeftCalibration, sizeof(pLeftStickCal));
+ }
+
+ if (!have_right_stick_cal) {
+ have_right_stick_cal = true;
+ SDL_memcpy(pRightStickCal, user_reply->stickFactoryCalibration.rgucRightCalibration, sizeof(pRightStickCal));
+ }
+
break;
}
@@ -1035,7 +1044,7 @@ static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
}
// If we still don't have calibration data, return false
- if (pLeftStickCal == NULL || pRightStickCal == NULL)
+ if (!have_left_stick_cal || !have_right_stick_cal)
{
return false;
}