Windows: Make joystick autoconfigure work with DualSense / DualShock devices

This commit is contained in:
Henrik Rydgård
2026-01-25 16:18:31 +01:00
parent 7d4cfdaf91
commit 0543b34a27
4 changed files with 76 additions and 21 deletions
+1
View File
@@ -260,6 +260,7 @@ enum class SystemNotification {
UI_STATE_CHANGED,
AUDIO_MODE_CHANGED,
APP_SWITCH_MODE_CHANGED,
PAD_STATE_CHANGED,
};
// I guess it's not super great architecturally to centralize this, since it's not general - but same with a lot of
+51 -10
View File
@@ -839,25 +839,64 @@ bool HasBuiltinController(std::string_view name) {
}
void NotifyPadConnected(InputDeviceID deviceId, std::string_view name) {
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
g_seenPads.insert(std::string(name));
g_padNames[deviceId] = name;
{
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
g_seenPads.insert(std::string(name));
g_padNames[deviceId] = name;
}
System_Notify(SystemNotification::PAD_STATE_CHANGED);
}
void NotifyPadDisconnected(InputDeviceID deviceId) {
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
auto iter = g_padNames.find(deviceId);
if (iter != g_padNames.end()) {
g_seenPads.erase(iter->second);
{
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
auto iter = g_padNames.find(deviceId);
if (iter != g_padNames.end()) {
g_seenPads.erase(iter->second);
}
g_padNames.erase(deviceId);
}
System_Notify(SystemNotification::PAD_STATE_CHANGED);
}
void ClearControlsWithDeviceId(InputDeviceID deviceId) {
bool modified = false;
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) {
auto &mappings = iter->second;
for (auto mapIter = mappings.begin(); mapIter != mappings.end(); ) {
bool found = false;
for (auto &mapping : mapIter->mappings) {
if (mapping.deviceId == deviceId) {
found = true;
break;
}
}
if (found) {
mapIter = mappings.erase(mapIter);
modified = true;
} else {
++mapIter;
}
}
}
if (modified) {
g_controllerMapGeneration++;
}
g_padNames.erase(deviceId);
}
void AutoConfForPad(std::string_view name) {
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
g_controllerMap.clear();
INFO_LOG(Log::System, "Autoconfiguring pad for '%.*s'", STR_VIEW(name));
InputDeviceID deviceId = DEVICE_ID_PAD_0;
for (auto [padDeviceId, padName] : g_padNames) {
if (padName == name) {
// Already configured.
deviceId = padDeviceId;
}
}
ClearControlsWithDeviceId(deviceId);
#if PPSSPP_PLATFORM(ANDROID)
if (name.find("Xbox") != std::string::npos) {
@@ -881,8 +920,10 @@ void AutoConfForPad(std::string_view name) {
#endif
// Add a couple of convenient keyboard mappings by default, too.
#if !defined(MOBILE_DEVICE)
g_controllerMap[VIRTKEY_PAUSE].push_back(MultiInputMapping(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ESCAPE)));
g_controllerMap[VIRTKEY_FASTFORWARD].push_back(MultiInputMapping(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_TAB)));
#endif
g_controllerMapGeneration++;
}
+23 -11
View File
@@ -15,6 +15,7 @@
#include "Common/Common.h"
#include "Common/System/NativeApp.h"
#include "Common/System/OSD.h"
#include "Core/KeyMap.h"
constexpr u8 LED_R = 0x05;
constexpr u8 LED_G = 0x10;
@@ -184,16 +185,14 @@ static const HIDControllerInfo g_psInfos[] = {
// {PSSubType::DS5, DUALSENSE_EDGE_WIRELESS},
};
static bool IsSupportedGamepad(HANDLE handle, USHORT *pidOut, HIDControllerType *subType) {
static const HIDControllerInfo *GetGamepadInfo(HANDLE handle) {
HIDD_ATTRIBUTES attr{sizeof(HIDD_ATTRIBUTES)};
if (!HidD_GetAttributes(handle, &attr)) {
return false;
}
for (const auto &info : g_psInfos) {
if (attr.VendorID == info.vendorId && attr.ProductID == info.productId) {
*pidOut = attr.ProductID;
*subType = info.type;
return true;
return &info;
}
}
return false;
@@ -279,6 +278,10 @@ static bool ShutdownDualsense(HANDLE handle, int outReportSize) {
return WriteReport(handle, report);
}
static bool InitializeSwitchPro(HANDLE handle) {
return true;
}
enum class DS4FeatureBits : u8 {
VOL_L = 0x10,
VOL_R = 0x20,
@@ -350,7 +353,7 @@ static bool ShutdownDualShock(HANDLE handle, int outReportSize) {
return WriteReport(handle, report);
}
HANDLE OpenFirstHIDController(HIDControllerType *subType, int *reportSize, int *outReportSize) {
static HANDLE OpenFirstHIDController(HIDControllerType *subType, int *reportSize, int *outReportSize, const HIDControllerInfo **outInfo) {
GUID hidGuid;
HidD_GetHidGuid(&hidGuid);
@@ -373,9 +376,11 @@ HANDLE OpenFirstHIDController(HIDControllerType *subType, int *reportSize, int *
HANDLE handle = CreateFile(detailData->DevicePath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle != INVALID_HANDLE_VALUE) {
USHORT pid;
if (IsSupportedGamepad(handle, &pid, subType)) {
INFO_LOG(Log::UI, "Found supported gamepad. PID: %04x", pid);
const HIDControllerInfo *info = GetGamepadInfo(handle);
*outInfo = info;
if (info) {
*subType = info->type;
INFO_LOG(Log::UI, "Found supported gamepad. PID: %04x", info->productId);
HIDP_CAPS caps;
PHIDP_PREPARSED_DATA preparsedData;
@@ -396,7 +401,7 @@ HANDLE OpenFirstHIDController(HIDControllerType *subType, int *reportSize, int *
result = InitializeDualShock(handle, *outReportSize);
break;
case HIDControllerType::SwitchPro:
result = true; // InitializeSwitchPro(handle, *outReportSize);
result = InitializeSwitchPro(handle);
break;
}
@@ -677,13 +682,20 @@ InputDeviceID HidInputDevice::DeviceID(int pad) {
}
int HidInputDevice::UpdateState() {
const InputDeviceID deviceID = DeviceID(pad_);
if (!controller_) {
// Poll for controllers from time to time.
if (pollCount_ == 0) {
pollCount_ = POLL_FREQ;
HANDLE newController = OpenFirstHIDController(&subType_, &reportSize_, &outReportSize_);
const HIDControllerInfo *info{};
HANDLE newController = OpenFirstHIDController(&subType_, &reportSize_, &outReportSize_, &info);
if (newController) {
controller_ = newController;
if (info) {
name_ = info->name;
}
KeyMap::NotifyPadConnected(deviceID, name_);
}
} else {
pollCount_--;
@@ -706,7 +718,6 @@ int HidInputDevice::UpdateState() {
}
if (result) {
const InputDeviceID deviceID = DeviceID(pad_);
// Process the input and generate input events.
const u32 downMask = state.buttons & (~prevState_.buttons);
const u32 upMask = (~state.buttons) & prevState_.buttons;
@@ -757,6 +768,7 @@ int HidInputDevice::UpdateState() {
return UPDATESTATE_NO_SLEEP; // The ReadFile sleeps for us, effectively.
} else {
// might have been disconnected. retry later.
KeyMap::NotifyPadDisconnected(deviceID);
ReleaseAllKeys(buttonMappings, buttonMappingsSize);
CloseHandle(controller_);
controller_ = NULL;
+1
View File
@@ -47,6 +47,7 @@ private:
HIDControllerType subType_{};
HANDLE controller_;
std::string name_;
int pad_ = 0;
int pollCount_ = 0;
int reportSize_ = 0;