SDL: vita: Treat the d-pad as a hat switch at the SDL_Joystick level.

From d629eb3bf093bc71c3d88e1f680b6bbacb33e0a0 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 22 Jun 2026 13:45:00 -0400
Subject: [PATCH] vita: Treat the d-pad as a hat switch at the SDL_Joystick
 level.

Reference Issue #14830.

[sdl-ci-filter *]
---
 src/joystick/SDL_gamepad_db.h       |  2 +-
 src/joystick/vita/SDL_sysjoystick.c | 33 +++++++++++++++++++++++------
 2 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/src/joystick/SDL_gamepad_db.h b/src/joystick/SDL_gamepad_db.h
index 59a2e92b0b709..2297442ab021c 100644
--- a/src/joystick/SDL_gamepad_db.h
+++ b/src/joystick/SDL_gamepad_db.h
@@ -874,7 +874,7 @@ static const char *s_GamepadMappings[] = {
     "00000000505350206275696c74696e00,PSP builtin joypad,crc:bb86,a:b2,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b7,x:b3,y:b0,",
 #endif
 #ifdef SDL_JOYSTICK_VITA
-    "0000000050535669746120436f6e7400,PSVita Controller,crc:d598,a:b2,b:b1,back:b10,dpdown:b6,dpleft:b7,dpright:b9,dpup:b8,leftshoulder:b4,leftstick:b14,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,",
+    "0000000050535669746120436f6e7400,PSVita Controller,crc:d598,a:b2,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b3,y:b0,",
 #endif
 #ifdef SDL_JOYSTICK_N3DS
     "000000004e696e74656e646f20334400,Nintendo 3DS,crc:3210,a:b1,b:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b11,rightx:a2,righty:a3,start:b3,x:b7,y:b6,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
diff --git a/src/joystick/vita/SDL_sysjoystick.c b/src/joystick/vita/SDL_sysjoystick.c
index 59f553fb5328e..4a3f0d50b20fe 100644
--- a/src/joystick/vita/SDL_sysjoystick.c
+++ b/src/joystick/vita/SDL_sysjoystick.c
@@ -43,6 +43,8 @@ static int ext_port_map[4] = { 1, 2, 3, 4 }; // index: SDL joy number, entry: Vi
 
 static int SDL_numjoysticks = 1;
 
+#define VITA_HAT_MASK (SCE_CTRL_DOWN | SCE_CTRL_LEFT | SCE_CTRL_UP | SCE_CTRL_RIGHT)  // we offer the dpad as a hat switch.
+
 static const unsigned int ext_button_map[] = {
     SCE_CTRL_TRIANGLE,
     SCE_CTRL_CIRCLE,
@@ -50,10 +52,6 @@ static const unsigned int ext_button_map[] = {
     SCE_CTRL_SQUARE,
     SCE_CTRL_L1,
     SCE_CTRL_R1,
-    SCE_CTRL_DOWN,
-    SCE_CTRL_LEFT,
-    SCE_CTRL_UP,
-    SCE_CTRL_RIGHT,
     SCE_CTRL_SELECT,
     SCE_CTRL_START,
     SCE_CTRL_L2,
@@ -205,7 +203,7 @@ static bool VITA_JoystickOpen(SDL_Joystick *joystick, int device_index)
 {
     joystick->nbuttons = SDL_arraysize(ext_button_map);
     joystick->naxes = 6;
-    joystick->nhats = 0;
+    joystick->nhats = 1;  // we treat the dpad as a hat switch, even though its just buttons.
 
     SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true);
     SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);
@@ -297,14 +295,35 @@ static void VITA_JoystickUpdate(SDL_Joystick *joystick)
     changed = old_buttons[index] ^ buttons;
     old_buttons[index] = buttons;
 
-    if (changed) {
+    if (changed & ~VITA_HAT_MASK) {
         for (i = 0; i < SDL_arraysize(ext_button_map); i++) {
             if (changed & ext_button_map[i]) {
-                bool down = ((buttons & ext_button_map[i]) != 0);
+                const bool down = ((buttons & ext_button_map[i]) != 0);
                 SDL_SendJoystickButton(timestamp, joystick, i, down);
             }
         }
     }
+
+    if (changed & VITA_HAT_MASK) {
+        // The Vita dpad looks like 4 buttons at this level, but we treat it as a hat switch, so apps that are talking to SDL_Joystick can hope to do basic directional things without a configuration step.
+        // (but they should _really_ be using the gamepad API.)
+        Uint8 hat = SDL_HAT_CENTERED;
+        #define HATSTATE(name) if (buttons & SCE_CTRL_##name) { hat |= SDL_HAT_##name; }
+        HATSTATE(UP);
+        HATSTATE(RIGHT);
+        HATSTATE(DOWN);
+        HATSTATE(LEFT);
+        #undef HATSTATE
+
+        // this is a physical d-pad on the device, so it probably _can't_ send opposing buttons at the same time, but just in case, cancel them out.
+        if ((hat & (SDL_HAT_UP|SDL_HAT_DOWN)) == (SDL_HAT_UP|SDL_HAT_DOWN)) {
+            hat &= ~(SDL_HAT_UP|SDL_HAT_DOWN);
+        }
+        if ((hat & (SDL_HAT_LEFT|SDL_HAT_RIGHT)) == (SDL_HAT_LEFT|SDL_HAT_RIGHT)) {
+            hat &= ~(SDL_HAT_LEFT|SDL_HAT_RIGHT);
+        }
+        SDL_SendJoystickHat(timestamp, joystick, 0, hat);
+    }
 }
 
 // Function to close a joystick after use