Explorar o código

PlayStation compatibility and input behavior (#272)

Co-authored-by: ixelyth <314675972+ixelyth@users.noreply.github.com>
Edgar hai 1 mes
pai
achega
742e6d064d

+ 37 - 3
OpenPuck/gamepad_util.cpp

@@ -1,6 +1,40 @@
 #include "gamepad_util.h"
 #include "triton.h"
 #include "config.h"
+#include "haptics.h"
+#include "bonds.h"
+
+PsImuFrame psImuFromSteam(const PuckInput &in)
+{
+	PsImuFrame out = { in.gx, in.gz, (int16_t)(-in.gy),
+			   in.ax, in.az, (int16_t)(-in.ay) };
+	return out;
+}
+
+static bool g_psPadClickDown[NSLOT] = {};
+static unsigned long g_psPadClickReplyMs[NSLOT] = {};
+
+void psPadClickEdge(uint8_t slot, bool pressed)
+{
+	if (slot >= NSLOT)
+		return;
+	const unsigned long replyMs = g_connReplyMs[slot];
+	const bool linkUp = hapticLinkUp(slot);
+	if (!linkUp ||
+	    (g_psPadClickReplyMs[slot] && replyMs &&
+	     (unsigned long)(replyMs - g_psPadClickReplyMs[slot]) >= 300u))
+		g_psPadClickDown[slot] = false;
+	if (replyMs)
+		g_psPadClickReplyMs[slot] = replyMs;
+	if (!linkUp)
+		return;
+	bool rising = pressed && !g_psPadClickDown[slot];
+	g_psPadClickDown[slot] = pressed;
+	if (!rising || !g_padHaptics || haptic82Blocked(slot))
+		return;
+	static const uint8_t click[3] = { 0x01, 0x01, 0xF7 };
+	relayEnqueue(0x82, click, sizeof click, true, slot);
+}
 
 void psNeutralCalib(uint8_t *buf)
 {
@@ -186,7 +220,7 @@ static void psOrBackCode(uint32_t *b, uint8_t c)
 		*b |= TB_VIEW;
 		break;
 	case 10:
-		*b |= TB_QAM;
+		*b |= TB_MENU;
 		break;
 	case 11:
 		*b |= TB_STEAM;
@@ -224,7 +258,7 @@ uint32_t psButtonsFromSteam(uint32_t raw)
 	uint32_t b = raw;
 	if (g_qamMap && (b & TB_QAM)) {
 		b &= ~(uint32_t)TB_QAM;
-		b |= tritonFromCode(g_qamMap);
+		psOrBackCode(&b, g_qamMap);
 	}
 	if ((b & CHORD_BACK4) == CHORD_BACK4)
 		b &= ~(uint32_t)(TB_A | TB_B | TB_X | TB_Y | TB_DUP | TB_DDN |
@@ -248,7 +282,7 @@ uint8_t psShouldersByte(uint32_t b, uint8_t lt, uint8_t rt)
 	       ((rt > SW_TRIG_ON || (b & 0x800000u)) ? 0x08 : 0) |
 	       // 0x10 = Create/Share, 0x20 = Options; TB_MENU/TB_VIEW are the
 	       // Select/Start-side buttons respectively (see triton.h)
-	       ((b & TB_MENU) ? 0x10 : 0) | ((b & TB_VIEW) ? 0x20 : 0) |
+	       ((b & TB_VIEW) ? 0x10 : 0) | ((b & TB_MENU) ? 0x20 : 0) |
 	       ((b & TB_L3) ? 0x40 : 0) | ((b & TB_R3) ? 0x80 : 0);
 }
 uint8_t psHatNibble(uint32_t b)

+ 9 - 0
OpenPuck/gamepad_util.h

@@ -25,6 +25,15 @@ static inline void le16(uint8_t *p, int16_t v)
 // and 0x05 (DualSense) -- identical layout; the caller returns size-1 and leaves trailing bytes zero.
 void psNeutralCalib(uint8_t *buf);
 
+// Shared Steam-controller -> Sony sensor coordinate transform.
+// Gyro and accelerometer use the same signed permutation for coherent fusion.
+struct PsImuFrame {
+	int16_t gx, gy, gz;
+	int16_t ax, ay, az;
+};
+PsImuFrame psImuFromSteam(const PuckInput &in);
+void psPadClickEdge(uint8_t slot, bool pressed);
+
 // Steam trackpad s16 coords -> absolute touch surface. TOUCH_PAD_W is split into left/right halves so both
 // pads can co-exist as two contacts on a single DualSense/DS4 touchpad.
 #define TOUCH_PAD_W 1920u

+ 19 - 22
OpenPuck/mode_hidgyro.cpp

@@ -13,16 +13,17 @@
 HidGyroController g_hidGyroCtl;
 
 static const uint8_t GYRO_HID_DESC[] = {
-	0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09,
-	0x31, 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x25, 0x07, 0x75, 0x04,
-	0x95, 0x01, 0x81, 0x42, 0x65, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29,
-	0x0E, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0E, 0x81, 0x02,
-	0x06, 0x00, 0xFF, 0x09, 0x20, 0x75, 0x06, 0x95, 0x01, 0x15, 0x00,
-	0x25, 0x7F, 0x81, 0x02, 0x05, 0x01, 0x09, 0x33, 0x09, 0x34, 0x15,
-	0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02, 0x06,
-	0x00, 0xFF, 0x09, 0x21, 0x95, 0x36, 0x81, 0x02, 0x85, 0x05, 0x09,
-	0x22, 0x95, 0x1F, 0x91, 0x02, 0x85, 0x04, 0x09, 0x23, 0x95, 0x24,
-	0xB1, 0x02, 0x85, 0x02, 0x09, 0x24, 0x95, 0x24, 0xB1, 0x02, 0xC0
+	0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31,
+	0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95,
+	0x04, 0x81, 0x02, 0x09, 0x39, 0x15, 0x00, 0x25, 0x07, 0x75, 0x04, 0x95,
+	0x01, 0x81, 0x42, 0x65, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0E, 0x15,
+	0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0E, 0x81, 0x02, 0x06, 0x00, 0xFF,
+	0x09, 0x20, 0x75, 0x06, 0x95, 0x01, 0x15, 0x00, 0x25, 0x7F, 0x81, 0x02,
+	0x05, 0x01, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+	0x08, 0x95, 0x02, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x21, 0x95, 0x36,
+	0x81, 0x02, 0x85, 0x05, 0x09, 0x22, 0x95, 0x1F, 0x91, 0x02, 0x85, 0x04,
+	0x09, 0x23, 0x95, 0x24, 0xB1, 0x02, 0x85, 0x02, 0x09, 0x24, 0x95, 0x24,
+	0xB1, 0x02, 0xC0
 };
 #define DS4_TOUCH_H 942
 #define DS4_STATUS_USB 0x1B // cable + level 11 (full)
@@ -143,6 +144,7 @@ static ds4_setcb_t const DS4_SETCB[NSLOT] = { hidGyroSet0, hidGyroSet1,
 static void hidGyroBuild(uint8_t usbSlot, uint8_t slot, uint8_t out[63])
 {
 	uint32_t b = psButtonsFromSteam(g_in[slot].buttons);
+	psPadClickEdge(slot, (b & (TB_LPADC | TB_RPADC)) != 0);
 	// A pad mapped to a stick must NOT also report as a touchpad contact -- the host would read the same
 	// finger twice (stick deflection AND a cursor drag).
 	bool lTouch = g_padStick[0] == PS_OFF &&
@@ -164,18 +166,13 @@ static void hidGyroBuild(uint8_t usbSlot, uint8_t slot, uint8_t out[63])
 		 ((b & TB_STEAM) ? 0x01 : 0);
 	out[7] = g_in[slot].lt;
 	out[8] = g_in[slot].rt;
-	out[12] = g_in[slot].gx & 0xFF;
-	out[13] = g_in[slot].gx >> 8;
-	out[14] = g_in[slot].gz & 0xFF;
-	out[15] = g_in[slot].gz >> 8;
-	out[16] = (-g_in[slot].gy) & 0xFF;
-	out[17] = (-g_in[slot].gy) >> 8;
-	out[18] = g_in[slot].ax & 0xFF;
-	out[19] = g_in[slot].ax >> 8;
-	out[20] = g_in[slot].ay & 0xFF;
-	out[21] = g_in[slot].ay >> 8;
-	out[22] = g_in[slot].az & 0xFF;
-	out[23] = g_in[slot].az >> 8;
+	PsImuFrame imu = psImuFromSteam(g_in[slot]);
+	le16(out + 12, imu.gx);
+	le16(out + 14, imu.gy);
+	le16(out + 16, imu.gz);
+	le16(out + 18, imu.ax);
+	le16(out + 20, imu.ay);
+	le16(out + 22, imu.az);
 	out[29] = DS4_STATUS_USB;
 	if (lTouch || rTouch) {
 		uint16_t tlx, tly, trx, trry;

+ 9 - 5
OpenPuck/mode_ps3.cpp

@@ -305,10 +305,11 @@ static void ds3Build(uint8_t slot, uint8_t out[48])
 	out[23] = crs ? 0xFF : 0;
 	out[24] = sqr ? 0xFF : 0;
 
-	// out[28..29] (rd[29..30]): connection/charge status. Battery "full"; cosmetic on the PS3 over USB
-	// (the console knows it's wired from enumeration, not this byte).
-	out[28] = 0x00;
-	out[29] = 0x05; // battery level: full
+	// out[28..30] (rd[29..31]): genuine DS3 link/power state.
+	// 0x02 = plugged, 0x05 = full power, 0x10 = wired + rumble capable.
+	out[28] = 0x02;
+	out[29] = 0x05;
+	out[30] = 0x10;
 
 	// out[40..47] (rd[41..48]): accel X, accel Z, accel Y, gyro Z -- each 10-bit LE, center 511.
 	ds3Imu(out + 40, g_in[slot].ax);
@@ -323,7 +324,10 @@ static void ds3Neutral(uint8_t out[48])
 {
 	memset(out, 0, 48);
 	out[5] = out[6] = out[7] = out[8] = 0x80; // sticks centered
-	out[29] = 0x05; // battery full
+	// Keep neutral reports semantically identical to an attached wired DS3.
+	out[28] = 0x02; // plugged
+	out[29] = 0x05; // full power
+	out[30] = 0x10; // wired + rumble capable
 	ds3Imu(out + 40, 0);
 	ds3Imu(out + 42, 0);
 	ds3Imu(out + 44, 0);

+ 8 - 0
OpenPuck/mode_ps5.cpp

@@ -164,6 +164,7 @@ static ps5_setcb_t const PS5_SETCB[NSLOT] = { ps5Set0, ps5Set1, ps5Set2,
 static void ps5Build(uint8_t usbSlot, uint8_t slot, uint8_t out[63])
 {
 	uint32_t b = psButtonsFromSteam(g_in[slot].buttons);
+	psPadClickEdge(slot, (b & (TB_LPADC | TB_RPADC)) != 0);
 	// A pad mapped to a stick must NOT also report as a touchpad contact -- the host would read the same
 	// finger twice (stick deflection AND a cursor drag).
 	bool lTouch = g_padStick[0] == PS_OFF &&
@@ -204,6 +205,13 @@ static void ps5Build(uint8_t usbSlot, uint8_t slot, uint8_t out[63])
 			 &trry);
 	touchPackPads(out + 32, lTouch, rTouch, tlx, tly, trx, trry);
 	out[52] = PS5_STATUS_USB;
+	PsImuFrame imu = psImuFromSteam(g_in[slot]);
+	le16(out + 15, imu.gx);
+	le16(out + 17, imu.gy);
+	le16(out + 19, imu.gz);
+	le16(out + 21, imu.ax);
+	le16(out + 23, imu.ay);
+	le16(out + 25, imu.az);
 }
 
 // Dynamic-mount mode: begin() is unused (setup() calls beginPool()+usbReenumerate instead).