Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/Hid/DualShock.cpp
5692 views
1
#include "Windows/Hid/DualShock.h"
2
#include "Windows/Hid/HidInputDevice.h"
3
#include "Windows/Hid/HidCommon.h"
4
5
static const ButtonInputMapping g_psInputMappings[] = {
6
{PS_DPAD_UP, NKCODE_DPAD_UP},
7
{PS_DPAD_DOWN, NKCODE_DPAD_DOWN},
8
{PS_DPAD_LEFT, NKCODE_DPAD_LEFT},
9
{PS_DPAD_RIGHT, NKCODE_DPAD_RIGHT},
10
{PS_BTN_SQUARE, NKCODE_BUTTON_4},
11
{PS_BTN_TRIANGLE, NKCODE_BUTTON_3},
12
{PS_BTN_CIRCLE, NKCODE_BUTTON_1},
13
{PS_BTN_CROSS, NKCODE_BUTTON_2},
14
{PS_BTN_PS_BUTTON, NKCODE_HOME},
15
{PS_BTN_SHARE, NKCODE_BUTTON_9},
16
{PS_BTN_OPTIONS, NKCODE_BUTTON_10},
17
{PS_BTN_L1, NKCODE_BUTTON_7},
18
{PS_BTN_R1, NKCODE_BUTTON_8},
19
{PS_BTN_TOUCHPAD, NKCODE_BUTTON_11},
20
// {PS_BTN_L2, NKCODE_BUTTON_L2}, // These are done by the analog triggers.
21
// {PS_BTN_R2, NKCODE_BUTTON_R2},
22
{PS_BTN_L3, NKCODE_BUTTON_THUMBL},
23
{PS_BTN_R3, NKCODE_BUTTON_THUMBR},
24
};
25
26
void GetPSButtonInputMappings(const ButtonInputMapping **mappings, size_t *size) {
27
*mappings = g_psInputMappings;
28
*size = ARRAY_SIZE(g_psInputMappings);
29
}
30
31
enum class DS4FeatureBits : u8 {
32
VOL_L = 0x10,
33
VOL_R = 0x20,
34
MIC_VOL = 0x40,
35
SPEAKER_VOL = 0x80,
36
RUMBLE = 0x1,
37
LIGHTBAR = 0x2,
38
FLASH = 0x4,
39
};
40
ENUM_CLASS_BITOPS(DS4FeatureBits);
41
42
struct DualshockOutputReport {
43
u8 reportId;
44
u8 featureBits;
45
u8 two;
46
u8 pad;
47
u8 rumbleRight;
48
u8 rumbleLeft;
49
u8 lightbarRed;
50
u8 lightbarGreen;
51
u8 lightbarBlue;
52
u8 padding[23];
53
};
54
static_assert(sizeof(DualshockOutputReport) == 32);
55
56
struct DualShockInputReport {
57
u8 lx;
58
u8 ly;
59
u8 rx;
60
u8 ry;
61
u8 buttons[3]; // note, starts at 5 so not aligned
62
u8 l2_analog;
63
u8 r2_analog;
64
u8 pad[2];
65
u8 battery;
66
// Then there's motion and all kinds of stuff.
67
};
68
69
static void FillDualShockOutputReport(DualshockOutputReport *report, bool lightsOn) {
70
report->reportId = 0x05; // USB default
71
report->featureBits = (u8)(DS4FeatureBits::RUMBLE | DS4FeatureBits::LIGHTBAR | DS4FeatureBits::FLASH); // Flags: enable lightbar, rumble, etc.
72
report->two = 2;
73
report->rumbleRight = 0;
74
report->rumbleLeft = 0;
75
76
// Turn on or off the lights.
77
report->lightbarRed = lightsOn ? LED_R : 0;
78
report->lightbarGreen = lightsOn ? LED_G : 0;
79
report->lightbarBlue = lightsOn ? LED_B : 0;
80
}
81
82
bool InitializeDualShock(HANDLE handle, int outReportSize) {
83
if (outReportSize != sizeof(DualshockOutputReport)) {
84
WARN_LOG(Log::UI, "DS4 unexpected report size %d", outReportSize);
85
return false;
86
}
87
88
DualshockOutputReport report{};
89
FillDualShockOutputReport(&report, true);
90
return WriteReport(handle, report);
91
}
92
93
bool ShutdownDualShock(HANDLE handle, int outReportSize) {
94
if (outReportSize != sizeof(DualshockOutputReport)) {
95
WARN_LOG(Log::UI, "DS4 unexpected report size %d", outReportSize);
96
return false;
97
}
98
99
DualshockOutputReport report{};
100
FillDualShockOutputReport(&report, false);
101
return WriteReport(handle, report);
102
}
103
104
bool ReadDualShockInput(HANDLE handle, HIDControllerState *state, int inReportSize) {
105
if (inReportSize > 1024) {
106
return false;
107
}
108
BYTE inputReport[1024]{};
109
DWORD bytesRead = 0;
110
if (!ReadFile(handle, inputReport, inReportSize, &bytesRead, nullptr)) {
111
u32 error = GetLastError();
112
return false;
113
}
114
DualShockInputReport report{};
115
static_assert(sizeof(report) < sizeof(inputReport));
116
if (bytesRead < 14) {
117
return false;
118
}
119
120
// OK, check the first byte to figure out what we're dealing with here.
121
int offset = 1;
122
int reportId;
123
if (inputReport[0] == 0xA1) {
124
// 2-byte bluetooth frame
125
offset = 2;
126
reportId = inputReport[1];
127
} else {
128
offset = 1;
129
reportId = inputReport[0];
130
}
131
// const bool isBluetooth = (reportId == 0x11 || reportId == 0x31);
132
133
memcpy(&report, inputReport + offset, sizeof(report));
134
135
// Center the sticks.
136
state->stickAxes[HID_STICK_LX] = report.lx - 128;
137
state->stickAxes[HID_STICK_LY] = report.ly - 128;
138
state->stickAxes[HID_STICK_RX] = report.rx - 128;
139
state->stickAxes[HID_STICK_RY] = report.ry - 128;
140
141
// Copy over the triggers.
142
state->triggerAxes[HID_TRIGGER_L2] = report.l2_analog;
143
state->triggerAxes[HID_TRIGGER_R2] = report.r2_analog;
144
145
state->accValid = false;
146
147
u32 buttons{};
148
int frameCounter = report.buttons[2] >> 2;
149
report.buttons[2] &= 3;
150
memcpy(&buttons, &report.buttons[0], 3);
151
152
// Clear out and re-fill the DPAD, it works differently somehow
153
buttons &= ~0xF;
154
buttons |= DecodePSHatSwitch(report.buttons[0] & 0xF);
155
156
state->buttons = buttons;
157
return true;
158
}
159
160