Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/Hid/HidInputDevice.cpp
5692 views
1
// This file in particular along with its header is public domain, use it for whatever you want.
2
3
#include <windows.h>
4
#include <hidsdi.h>
5
#include <setupapi.h>
6
#include <initguid.h>
7
#include <vector>
8
9
#include "Windows/Hid/HidInputDevice.h"
10
#include "Windows/Hid/SwitchPro.h"
11
#include "Windows/Hid/DualSense.h"
12
#include "Windows/Hid/DualShock.h"
13
#include "Windows/Hid/HidCommon.h"
14
#include "Common/CommonTypes.h"
15
#include "Common/TimeUtil.h"
16
#include "Common/Math/math_util.h"
17
#include "Common/Log.h"
18
#include "Common/Input/InputState.h"
19
#include "Common/Common.h"
20
#include "Common/System/NativeApp.h"
21
#include "Common/System/OSD.h"
22
#include "Core/KeyMap.h"
23
24
struct HidStickMapping {
25
HidStickAxis stickAxis;
26
InputAxis inputAxis;
27
};
28
29
// This is the same mapping as DInput etc.
30
static const HidStickMapping g_psStickMappings[] = {
31
{HID_STICK_LX, JOYSTICK_AXIS_X},
32
{HID_STICK_LY, JOYSTICK_AXIS_Y},
33
{HID_STICK_RX, JOYSTICK_AXIS_Z},
34
{HID_STICK_RY, JOYSTICK_AXIS_RX},
35
};
36
37
struct HidTriggerMapping {
38
HidTriggerAxis triggerAxis;
39
InputAxis inputAxis;
40
};
41
42
static const HidTriggerMapping g_psTriggerMappings[] = {
43
{HID_TRIGGER_L2, JOYSTICK_AXIS_LTRIGGER},
44
{HID_TRIGGER_R2, JOYSTICK_AXIS_RTRIGGER},
45
};
46
47
struct HIDControllerInfo {
48
u16 vendorId;
49
u16 productId;
50
HIDControllerType type;
51
const char *name;
52
};
53
54
constexpr u16 SONY_VID = 0x054C;
55
constexpr u16 NINTENDO_VID = 0x57e;
56
constexpr u16 SWITCH_PRO_PID = 0x2009;
57
constexpr u16 DS4_WIRELESS = 0x0BA0;
58
constexpr u16 PS_CLASSIC = 0x0CDA;
59
60
// We pick a few ones from here to support, let's add more later.
61
// https://github.com/ds4windowsapp/DS4Windows/blob/65609b470f53a4f832fb07ac24085d3e28ec15bd/DS4Windows/DS4Library/DS4Devices.cs#L126
62
static const HIDControllerInfo g_psInfos[] = {
63
{SONY_VID, 0x05C4, HIDControllerType::DualShock, "DS4 v.1"},
64
{SONY_VID, 0x09CC, HIDControllerType::DualShock, "DS4 v.2"},
65
{SONY_VID, 0x0CE6, HIDControllerType::DualSense, "DualSense"},
66
{SONY_VID, PS_CLASSIC, HIDControllerType::DualShock, "PS Classic"},
67
{NINTENDO_VID, SWITCH_PRO_PID, HIDControllerType::SwitchPro, "Switch Pro"},
68
// {PSSubType::DS4, DS4_WIRELESS},
69
// {PSSubType::DS5, DUALSENSE_WIRELESS},
70
// {PSSubType::DS5, DUALSENSE_EDGE_WIRELESS},
71
};
72
73
static const HIDControllerInfo *GetGamepadInfo(HANDLE handle) {
74
HIDD_ATTRIBUTES attr{sizeof(HIDD_ATTRIBUTES)};
75
if (!HidD_GetAttributes(handle, &attr)) {
76
return nullptr;
77
}
78
for (const auto &info : g_psInfos) {
79
if (attr.VendorID == info.vendorId && attr.ProductID == info.productId) {
80
return &info;
81
}
82
}
83
return nullptr;
84
}
85
86
static HANDLE OpenFirstHIDController(HIDControllerType *subType, int *reportSize, int *outReportSize, const HIDControllerInfo **outInfo) {
87
GUID hidGuid;
88
HidD_GetHidGuid(&hidGuid);
89
90
HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&hidGuid, nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
91
if (deviceInfoSet == INVALID_HANDLE_VALUE)
92
return nullptr;
93
94
SP_DEVICE_INTERFACE_DATA interfaceData;
95
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
96
97
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(deviceInfoSet, nullptr, &hidGuid, i, &interfaceData); ++i) {
98
DWORD requiredSize = 0;
99
SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &interfaceData, nullptr, 0, &requiredSize, nullptr);
100
101
std::vector<BYTE> buffer(requiredSize);
102
auto* detailData = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(buffer.data());
103
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
104
105
if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &interfaceData, detailData, requiredSize, nullptr, nullptr)) {
106
HANDLE handle = CreateFile(detailData->DevicePath, GENERIC_READ | GENERIC_WRITE,
107
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
108
if (handle != INVALID_HANDLE_VALUE) {
109
const HIDControllerInfo *info = GetGamepadInfo(handle);
110
*outInfo = info;
111
if (info) {
112
*subType = info->type;
113
INFO_LOG(Log::UI, "Found supported gamepad. PID: %04x", info->productId);
114
HIDP_CAPS caps;
115
PHIDP_PREPARSED_DATA preparsedData;
116
117
HidD_GetPreparsedData(handle, &preparsedData);
118
HidP_GetCaps(preparsedData, &caps);
119
HidD_FreePreparsedData(preparsedData);
120
121
*reportSize = caps.InputReportByteLength;
122
*outReportSize = caps.OutputReportByteLength;
123
124
INFO_LOG(Log::UI, "Initializing gamepad. out report size=%d", *outReportSize);
125
bool result;
126
switch (*subType) {
127
case HIDControllerType::DualSense:
128
result = InitializeDualSense(handle, *outReportSize);
129
break;
130
case HIDControllerType::DualShock:
131
result = InitializeDualShock(handle, *outReportSize);
132
break;
133
case HIDControllerType::SwitchPro:
134
result = InitializeSwitchPro(handle);
135
break;
136
}
137
138
if (!result) {
139
ERROR_LOG(Log::UI, "Controller initialization failed");
140
}
141
142
SetupDiDestroyDeviceInfoList(deviceInfoSet);
143
144
return handle;
145
}
146
CloseHandle(handle);
147
}
148
}
149
}
150
SetupDiDestroyDeviceInfoList(deviceInfoSet);
151
return nullptr;
152
}
153
154
void HidInputDevice::AddSupportedDevices(std::set<u32> *deviceVIDPIDs) {
155
for (const auto &info : g_psInfos) {
156
const u32 vidpid = MAKELONG(info.vendorId, info.productId);
157
deviceVIDPIDs->insert(vidpid);
158
}
159
}
160
161
void HidInputDevice::Init() {}
162
void HidInputDevice::Shutdown() {
163
if (controller_) {
164
switch (subType_) {
165
case HIDControllerType::DualShock:
166
ShutdownDualShock(controller_, outReportSize_);
167
break;
168
case HIDControllerType::DualSense:
169
ShutdownDualsense(controller_, outReportSize_);
170
break;
171
}
172
CloseHandle(controller_);
173
controller_ = nullptr;
174
}
175
}
176
177
void HidInputDevice::ReleaseAllKeys(const ButtonInputMapping *buttonMappings, int count) {
178
for (int i = 0; i < count; i++) {
179
const auto &mapping = buttonMappings[i];
180
KeyInput key;
181
key.deviceId = DEVICE_ID_XINPUT_0 + pad_;
182
key.flags = KeyInputFlags::UP;
183
key.keyCode = mapping.keyCode;
184
NativeKey(key);
185
}
186
187
static const InputAxis allAxes[6] = {
188
JOYSTICK_AXIS_X,
189
JOYSTICK_AXIS_Y,
190
JOYSTICK_AXIS_Z,
191
JOYSTICK_AXIS_RX,
192
JOYSTICK_AXIS_LTRIGGER,
193
JOYSTICK_AXIS_RTRIGGER,
194
};
195
196
for (const auto axisId : allAxes) {
197
AxisInput axis;
198
axis.deviceId = DEVICE_ID_XINPUT_0 + pad_;
199
axis.axisId = axisId;
200
axis.value = 0;
201
NativeAxis(&axis, 1);
202
}
203
}
204
205
InputDeviceID HidInputDevice::DeviceID(int pad) {
206
return DEVICE_ID_PAD_0 + pad;
207
}
208
209
int HidInputDevice::UpdateState() {
210
const InputDeviceID deviceID = DeviceID(pad_);
211
212
if (!controller_) {
213
// Poll for controllers from time to time.
214
if (pollCount_ == 0) {
215
pollCount_ = POLL_FREQ;
216
const HIDControllerInfo *info{};
217
HANDLE newController = OpenFirstHIDController(&subType_, &inReportSize_, &outReportSize_, &info);
218
if (newController) {
219
controller_ = newController;
220
if (info) {
221
name_ = info->name;
222
}
223
KeyMap::NotifyPadConnected(deviceID, name_);
224
}
225
} else {
226
pollCount_--;
227
}
228
}
229
230
if (controller_) {
231
HIDControllerState state{};
232
bool result = false;
233
const ButtonInputMapping *buttonMappings = nullptr;
234
size_t buttonMappingsSize = 0;
235
if (subType_ == HIDControllerType::DualShock) {
236
result = ReadDualShockInput(controller_, &state, inReportSize_);
237
GetPSButtonInputMappings(&buttonMappings, &buttonMappingsSize);
238
} else if (subType_ == HIDControllerType::DualSense) {
239
result = ReadDualSenseInput(controller_, &state, inReportSize_);
240
GetPSButtonInputMappings(&buttonMappings, &buttonMappingsSize);
241
} else if (subType_ == HIDControllerType::SwitchPro) {
242
result = ReadSwitchProInput(controller_, &state);
243
GetSwitchButtonInputMappings(&buttonMappings, &buttonMappingsSize);
244
}
245
246
if (result) {
247
// Process the input and generate input events.
248
const u32 downMask = state.buttons & (~prevState_.buttons);
249
const u32 upMask = (~state.buttons) & prevState_.buttons;
250
251
for (u32 i = 0; i < buttonMappingsSize; i++) {
252
const ButtonInputMapping &mapping = buttonMappings[i];
253
if (downMask & mapping.button) {
254
KeyInput key;
255
key.deviceId = deviceID;
256
key.flags = KeyInputFlags::DOWN;
257
key.keyCode = mapping.keyCode;
258
NativeKey(key);
259
}
260
if (upMask & mapping.button) {
261
KeyInput key;
262
key.deviceId = deviceID;
263
key.flags = KeyInputFlags::UP;
264
key.keyCode = mapping.keyCode;
265
NativeKey(key);
266
}
267
}
268
269
for (const auto &mapping : g_psStickMappings) {
270
if (state.stickAxes[mapping.stickAxis] != prevState_.stickAxes[mapping.stickAxis]) {
271
AxisInput axis;
272
axis.deviceId = deviceID;
273
axis.axisId = mapping.inputAxis;
274
axis.value = (float)state.stickAxes[mapping.stickAxis] * (1.0f / 128.0f);
275
NativeAxis(&axis, 1);
276
}
277
}
278
279
for (const auto &mapping : g_psTriggerMappings) {
280
if (state.triggerAxes[mapping.triggerAxis] != prevState_.triggerAxes[mapping.triggerAxis]) {
281
AxisInput axis;
282
axis.deviceId = deviceID;
283
axis.axisId = mapping.inputAxis;
284
axis.value = (float)state.triggerAxes[mapping.triggerAxis] * (1.0f / 255.0f);
285
NativeAxis(&axis, 1);
286
}
287
}
288
289
if (state.accValid) {
290
NativeAccelerometer(state.accelerometer[0], state.accelerometer[1], state.accelerometer[2]);
291
}
292
293
prevState_ = state;
294
return UPDATESTATE_NO_SLEEP; // The ReadFile sleeps for us, effectively.
295
} else {
296
WARN_LOG(Log::System, "Failed to read controller - assuming disconnected.");
297
// might have been disconnected. retry later.
298
KeyMap::NotifyPadDisconnected(deviceID);
299
ReleaseAllKeys(buttonMappings, (int)buttonMappingsSize);
300
CloseHandle(controller_);
301
controller_ = NULL;
302
pollCount_ = POLL_FREQ;
303
}
304
}
305
return 0;
306
}
307
308