Fixed bug 4600 - Dualshock 4 touchpad press is not detectable with SDL_JoystickGetButton
Dexter Friedman
When using a Dualshock 4 controller (model numbers CUH-ZCT1U and CUH-ZCT2U), pressing anywhere on the center touchpad does not send an SDL_JOYBUTTONDOWN event. I have verified this with testjoystick:
Repro steps:
1. Plug in a DS4 over USB
2. Compile testjoystick and run: testjoystick.exe 0
3. Press and hold the touchpad. Observe that no lime green box appears
Expected behavior:
A lime green box appears while the touchpad is pressed.
Notes:
I've attached a patch here that works on my PC and produces the expected behavior in testjoystick, for both DS4 model numbers I listed earlier.
If I understand correctly, by exposing this as a joystick button, the gamecontroller API mapping can be modified with a change to gamecontrollerdb.txt in the future.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 /* This driver supports both simplified reports and the extended input reports enabled by Steam.
22 Code and logic contributed by Valve Corporation under the SDL zlib license.
24 #include "../../SDL_internal.h"
26 #ifdef SDL_JOYSTICK_HIDAPI
28 #include "SDL_hints.h"
30 #include "SDL_events.h"
31 #include "SDL_timer.h"
32 #include "SDL_joystick.h"
33 #include "SDL_gamecontroller.h"
34 #include "../SDL_sysjoystick.h"
35 #include "SDL_hidapijoystick_c.h"
38 #ifdef SDL_JOYSTICK_HIDAPI_PS4
40 #define SONY_USB_VID 0x054C
41 #define SONY_DS4_PID 0x05C4
42 #define SONY_DS4_DONGLE_PID 0x0BA0
43 #define SONY_DS4_SLIM_PID 0x09CC
45 #define RAZER_USB_VID 0x1532
46 #define RAZER_PANTHERA_PID 0X0401
47 #define RAZER_PANTHERA_EVO_PID 0x1008
49 #define USB_PACKET_LENGTH 64
51 #define VOLUME_CHECK_INTERVAL_MS (10 * 1000)
55 k_EPS4ReportIdUsbState = 1,
56 k_EPS4ReportIdUsbEffects = 5,
57 k_EPS4ReportIdBluetoothState = 17,
58 k_EPS4ReportIdBluetoothEffects = 17,
59 k_EPS4ReportIdDisconnectMessage = 226,
64 k_ePS4FeatureReportIdGyroCalibration_USB = 0x02,
65 k_ePS4FeatureReportIdGyroCalibration_BT = 0x05,
66 k_ePS4FeatureReportIdSerialNumber = 0x12,
67 } EPS4FeatureReportID;
71 Uint8 ucLeftJoystickX;
72 Uint8 ucLeftJoystickY;
73 Uint8 ucRightJoystickX;
74 Uint8 ucRightJoystickY;
75 Uint8 rgucButtonsHatAndCounter[ 3 ];
88 Uint8 ucTrackpadCounter1;
89 Uint8 rgucTrackpadData1[ 3 ];
90 Uint8 ucTrackpadCounter2;
91 Uint8 rgucTrackpadData2[ 3 ];
103 Uint8 _rgucPad0[ 8 ];
107 Uint8 ucVolumeSpeaker;
111 SDL_JoystickID joystickID;
113 SDL_bool is_bluetooth;
114 SDL_bool audio_supported;
115 SDL_bool rumble_supported;
117 Uint32 last_volume_check;
118 Uint32 rumble_expiration;
119 PS4StatePacket_t last_state;
120 } SDL_DriverPS4_Context;
123 /* Public domain CRC implementation adapted from:
124 http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
126 static Uint32 crc32_for_byte(Uint32 r)
129 for(i = 0; i < 8; ++i) {
130 r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
132 return r ^ (Uint32)0xFF000000L;
135 static Uint32 crc32(Uint32 crc, const void *data, int count)
138 for(i = 0; i < count; ++i) {
139 crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
144 #if defined(__WIN32__) && defined(HAVE_ENDPOINTVOLUME_H)
145 #include "../../core/windows/SDL_windows.h"
148 #define NTDDI_VISTA 0x06000000
150 #ifndef _WIN32_WINNT_VISTA
151 #define _WIN32_WINNT_VISTA 0x0600
154 /* Define Vista for the Audio related includes below to work */
156 #define NTDDI_VERSION NTDDI_VISTA
158 #define _WIN32_WINNT _WIN32_WINNT_VISTA
160 #include <mmdeviceapi.h>
161 #include <audioclient.h>
162 #include <endpointvolume.h>
165 #define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
166 DEFINE_GUID(SDL_CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
167 DEFINE_GUID(SDL_IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);
168 DEFINE_GUID(SDL_IID_IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A);
173 static float GetSystemVolume(void)
175 float volume = -1.0f; /* Return this if we can't get system volume */
177 #if defined(__WIN32__) && defined(HAVE_ENDPOINTVOLUME_H)
178 HRESULT hr = WIN_CoInitialize();
180 IMMDeviceEnumerator *pEnumerator;
182 /* This should gracefully fail on XP and succeed on everything Vista and above */
183 hr = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &SDL_IID_IMMDeviceEnumerator, (LPVOID*)&pEnumerator);
187 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice);
189 IAudioEndpointVolume *pEndpointVolume;
191 hr = IMMDevice_Activate(pDevice, &SDL_IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (LPVOID*)&pEndpointVolume);
193 IAudioEndpointVolume_GetMasterVolumeLevelScalar(pEndpointVolume, &volume);
194 IUnknown_Release(pEndpointVolume);
196 IUnknown_Release(pDevice);
198 IUnknown_Release(pEnumerator);
200 WIN_CoUninitialize();
202 #endif /* __WIN32__ */
207 static uint8_t GetPlaystationVolumeFromFloat(float fVolume)
209 const int k_nVolumeFitRatio = 15;
210 const int k_nVolumeFitOffset = 9;
213 if (fVolume > 1.0f || fVolume < 0.0f) {
216 fVolLog = SDL_logf(fVolume * 100);
218 return (Uint8)((fVolLog * k_nVolumeFitRatio) + k_nVolumeFitOffset);
222 HIDAPI_DriverPS4_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
224 return SDL_IsJoystickPS4(vendor_id, product_id);
228 HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
230 if (vendor_id == SONY_USB_VID) {
231 return "PS4 Controller";
236 static SDL_bool ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *data, size_t size)
238 Uint8 report[USB_PACKET_LENGTH + 1];
240 SDL_memset(report, 0, sizeof(report));
241 report[0] = report_id;
242 if (hid_get_feature_report(dev, report, sizeof(report)) < 0) {
245 SDL_memcpy(data, report, SDL_min(size, sizeof(report)));
249 static SDL_bool CheckUSBConnected(hid_device *dev)
254 /* This will fail if we're on Bluetooth */
255 if (ReadFeatureReport(dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data))) {
256 for (i = 0; i < sizeof(data); ++i) {
257 if (data[i] != 0x00) {
261 /* Maybe the dongle without a connected controller? */
266 static SDL_bool HIDAPI_DriverPS4_CanRumble(Uint16 vendor_id, Uint16 product_id)
268 /* The Razer Panthera fight stick hangs when trying to rumble */
269 if (vendor_id == RAZER_USB_VID &&
270 (product_id == RAZER_PANTHERA_PID || product_id == RAZER_PANTHERA_EVO_PID)) {
277 HIDAPI_DriverPS4_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks)
279 SDL_DriverPS4_Context *ctx;
281 ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
286 context->context = ctx;
288 /* Check for type of connection */
289 ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID);
290 if (ctx->is_dongle) {
291 ctx->is_bluetooth = SDL_FALSE;
292 } else if (vendor_id == SONY_USB_VID) {
293 ctx->is_bluetooth = !CheckUSBConnected(context->device);
295 /* Third party controllers appear to all be wired */
296 ctx->is_bluetooth = SDL_FALSE;
299 SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE");
302 /* Check to see if audio is supported */
303 if (vendor_id == SONY_USB_VID &&
304 (product_id == SONY_DS4_SLIM_PID || product_id == SONY_DS4_DONGLE_PID )) {
305 ctx->audio_supported = SDL_TRUE;
308 if (HIDAPI_DriverPS4_CanRumble(vendor_id, product_id)) {
309 if (ctx->is_bluetooth) {
310 ctx->rumble_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE);
312 ctx->rumble_supported = SDL_TRUE;
316 ctx->joystickID = SDL_GetNextJoystickInstanceID();
318 SDL_PrivateJoystickAdded(ctx->joystickID);
324 HIDAPI_DriverPS4_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks)
326 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context;
330 SDL_PrivateJoystickRemoved(ctx->joystickID);
332 SDL_free(context->context);
336 HIDAPI_DriverPS4_NumJoysticks(SDL_HIDAPI_DriverData *context)
342 HIDAPI_DriverPS4_PlayerIndexForIndex(SDL_HIDAPI_DriverData *context, int index)
347 static SDL_JoystickID
348 HIDAPI_DriverPS4_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index)
350 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context;
351 return ctx->joystickID;
354 static int HIDAPI_DriverPS4_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
357 HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick)
359 /* Initialize LED and effect state */
360 HIDAPI_DriverPS4_Rumble(context, joystick, 0, 0, 0);
362 /* Initialize the joystick capabilities */
363 joystick->nbuttons = 16;
364 joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
365 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
371 HIDAPI_DriverPS4_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
373 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context;
374 DS4EffectsState_t *effects;
376 int report_size, offset;
378 if (!ctx->rumble_supported) {
379 return SDL_Unsupported();
382 /* In order to send rumble, we have to send a complete effect packet */
383 SDL_memset(data, 0, sizeof(data));
385 if (ctx->is_bluetooth) {
386 data[0] = k_EPS4ReportIdBluetoothEffects;
387 data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */
388 data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
393 data[0] = k_EPS4ReportIdUsbEffects;
394 data[1] = 0x07; /* Magic value */
399 effects = (DS4EffectsState_t *)&data[offset];
401 effects->ucRumbleLeft = (low_frequency_rumble >> 8);
402 effects->ucRumbleRight = (high_frequency_rumble >> 8);
404 effects->ucLedRed = 0;
405 effects->ucLedGreen = 0;
406 effects->ucLedBlue = 80;
408 if (ctx->audio_supported) {
409 Uint32 now = SDL_GetTicks();
410 if (!ctx->last_volume_check ||
411 SDL_TICKS_PASSED(now, ctx->last_volume_check + VOLUME_CHECK_INTERVAL_MS)) {
412 ctx->volume = GetPlaystationVolumeFromFloat(GetSystemVolume());
413 ctx->last_volume_check = now;
416 effects->ucVolumeRight = ctx->volume;
417 effects->ucVolumeLeft = ctx->volume;
418 effects->ucVolumeSpeaker = ctx->volume;
419 effects->ucVolumeMic = 0xFF;
422 if (ctx->is_bluetooth) {
423 /* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
424 Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
426 unCRC = crc32(0, &ubHdr, 1);
427 unCRC = crc32(unCRC, data, (Uint32)(report_size - sizeof(unCRC)));
428 SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
431 if (hid_write(context->device, data, report_size) != report_size) {
432 return SDL_SetError("Couldn't send rumble packet");
435 if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
436 ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
438 ctx->rumble_expiration = 0;
444 HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
448 if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
450 Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
452 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
453 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
454 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
455 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
458 Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
459 SDL_bool dpad_up = SDL_FALSE;
460 SDL_bool dpad_down = SDL_FALSE;
461 SDL_bool dpad_left = SDL_FALSE;
462 SDL_bool dpad_right = SDL_FALSE;
470 dpad_right = SDL_TRUE;
473 dpad_right = SDL_TRUE;
476 dpad_right = SDL_TRUE;
477 dpad_down = SDL_TRUE;
480 dpad_down = SDL_TRUE;
483 dpad_left = SDL_TRUE;
484 dpad_down = SDL_TRUE;
487 dpad_left = SDL_TRUE;
491 dpad_left = SDL_TRUE;
496 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
497 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
498 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
499 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
503 if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
504 Uint8 data = packet->rgucButtonsHatAndCounter[1];
506 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
507 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
508 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
509 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
510 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
511 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
514 if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
515 Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
517 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
518 SDL_PrivateJoystickButton(joystick, 15, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
521 axis = ((int)packet->ucTriggerLeft * 257) - 32768;
522 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
523 axis = ((int)packet->ucTriggerRight * 257) - 32768;
524 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
525 axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
526 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
527 axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
528 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
529 axis = ((int)packet->ucRightJoystickX * 257) - 32768;
530 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
531 axis = ((int)packet->ucRightJoystickY * 257) - 32768;
532 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
534 if (packet->ucBatteryLevel & 0x10) {
535 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
537 /* Battery level ranges from 0 to 10 */
538 int level = (packet->ucBatteryLevel & 0xF);
540 joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
541 } else if (level <= 2) {
542 joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
543 } else if (level <= 7) {
544 joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
546 joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
550 SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
554 HIDAPI_DriverPS4_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks)
556 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context;
557 SDL_Joystick *joystick = SDL_JoystickFromInstanceID(ctx->joystickID);
558 Uint8 data[USB_PACKET_LENGTH];
561 if (joystick == NULL) {
562 return SDL_TRUE; /* Nothing to do right now! */
565 while ((size = hid_read_timeout(context->device, data, sizeof(data), 0)) > 0) {
567 case k_EPS4ReportIdUsbState:
568 HIDAPI_DriverPS4_HandleStatePacket(joystick, context->device, ctx, (PS4StatePacket_t *)&data[1]);
570 case k_EPS4ReportIdBluetoothState:
571 /* Bluetooth state packets have two additional bytes at the beginning */
572 HIDAPI_DriverPS4_HandleStatePacket(joystick, context->device, ctx, (PS4StatePacket_t *)&data[3]);
575 #ifdef DEBUG_JOYSTICK
576 SDL_Log("Unknown PS4 packet: 0x%.2x\n", data[0]);
582 if (ctx->rumble_expiration) {
583 Uint32 now = SDL_GetTicks();
584 if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
585 HIDAPI_DriverPS4_Rumble(context, joystick, 0, 0, 0);
592 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
594 SDL_HINT_JOYSTICK_HIDAPI_PS4,
596 HIDAPI_DriverPS4_IsSupportedDevice,
597 HIDAPI_DriverPS4_GetDeviceName,
598 HIDAPI_DriverPS4_InitDriver,
599 HIDAPI_DriverPS4_QuitDriver,
600 HIDAPI_DriverPS4_UpdateDriver,
601 HIDAPI_DriverPS4_NumJoysticks,
602 HIDAPI_DriverPS4_PlayerIndexForIndex,
603 HIDAPI_DriverPS4_InstanceIDForIndex,
604 HIDAPI_DriverPS4_OpenJoystick,
605 HIDAPI_DriverPS4_Rumble
608 #endif /* SDL_JOYSTICK_HIDAPI_PS4 */
610 #endif /* SDL_JOYSTICK_HIDAPI */
612 /* vi: set ts=4 sw=4 expandtab: */