mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 09:35:09 +02:00
159 lines
4.2 KiB
C++
159 lines
4.2 KiB
C++
#include "Windows/Hid/DualShock.h"
|
|
#include "Windows/Hid/HidInputDevice.h"
|
|
#include "Windows/Hid/HidCommon.h"
|
|
|
|
static const ButtonInputMapping g_psInputMappings[] = {
|
|
{PS_DPAD_UP, NKCODE_DPAD_UP},
|
|
{PS_DPAD_DOWN, NKCODE_DPAD_DOWN},
|
|
{PS_DPAD_LEFT, NKCODE_DPAD_LEFT},
|
|
{PS_DPAD_RIGHT, NKCODE_DPAD_RIGHT},
|
|
{PS_BTN_SQUARE, NKCODE_BUTTON_4},
|
|
{PS_BTN_TRIANGLE, NKCODE_BUTTON_3},
|
|
{PS_BTN_CIRCLE, NKCODE_BUTTON_1},
|
|
{PS_BTN_CROSS, NKCODE_BUTTON_2},
|
|
{PS_BTN_PS_BUTTON, NKCODE_HOME},
|
|
{PS_BTN_SHARE, NKCODE_BUTTON_9},
|
|
{PS_BTN_OPTIONS, NKCODE_BUTTON_10},
|
|
{PS_BTN_L1, NKCODE_BUTTON_7},
|
|
{PS_BTN_R1, NKCODE_BUTTON_8},
|
|
{PS_BTN_TOUCHPAD, NKCODE_BUTTON_11},
|
|
// {PS_BTN_L2, NKCODE_BUTTON_L2}, // These are done by the analog triggers.
|
|
// {PS_BTN_R2, NKCODE_BUTTON_R2},
|
|
{PS_BTN_L3, NKCODE_BUTTON_THUMBL},
|
|
{PS_BTN_R3, NKCODE_BUTTON_THUMBR},
|
|
};
|
|
|
|
void GetPSButtonInputMappings(const ButtonInputMapping **mappings, size_t *size) {
|
|
*mappings = g_psInputMappings;
|
|
*size = ARRAY_SIZE(g_psInputMappings);
|
|
}
|
|
|
|
enum class DS4FeatureBits : u8 {
|
|
VOL_L = 0x10,
|
|
VOL_R = 0x20,
|
|
MIC_VOL = 0x40,
|
|
SPEAKER_VOL = 0x80,
|
|
RUMBLE = 0x1,
|
|
LIGHTBAR = 0x2,
|
|
FLASH = 0x4,
|
|
};
|
|
ENUM_CLASS_BITOPS(DS4FeatureBits);
|
|
|
|
struct DualshockOutputReport {
|
|
u8 reportId;
|
|
u8 featureBits;
|
|
u8 two;
|
|
u8 pad;
|
|
u8 rumbleRight;
|
|
u8 rumbleLeft;
|
|
u8 lightbarRed;
|
|
u8 lightbarGreen;
|
|
u8 lightbarBlue;
|
|
u8 padding[23];
|
|
};
|
|
static_assert(sizeof(DualshockOutputReport) == 32);
|
|
|
|
struct DualShockInputReport {
|
|
u8 lx;
|
|
u8 ly;
|
|
u8 rx;
|
|
u8 ry;
|
|
u8 buttons[3]; // note, starts at 5 so not aligned
|
|
u8 l2_analog;
|
|
u8 r2_analog;
|
|
u8 pad[2];
|
|
u8 battery;
|
|
// Then there's motion and all kinds of stuff.
|
|
};
|
|
|
|
static void FillDualShockOutputReport(DualshockOutputReport *report, bool lightsOn) {
|
|
report->reportId = 0x05; // USB default
|
|
report->featureBits = (u8)(DS4FeatureBits::RUMBLE | DS4FeatureBits::LIGHTBAR | DS4FeatureBits::FLASH); // Flags: enable lightbar, rumble, etc.
|
|
report->two = 2;
|
|
report->rumbleRight = 0;
|
|
report->rumbleLeft = 0;
|
|
|
|
// Turn on or off the lights.
|
|
report->lightbarRed = lightsOn ? LED_R : 0;
|
|
report->lightbarGreen = lightsOn ? LED_G : 0;
|
|
report->lightbarBlue = lightsOn ? LED_B : 0;
|
|
}
|
|
|
|
bool InitializeDualShock(HANDLE handle, int outReportSize) {
|
|
if (outReportSize != sizeof(DualshockOutputReport)) {
|
|
WARN_LOG(Log::UI, "DS4 unexpected report size %d", outReportSize);
|
|
return false;
|
|
}
|
|
|
|
DualshockOutputReport report{};
|
|
FillDualShockOutputReport(&report, true);
|
|
return WriteReport(handle, report);
|
|
}
|
|
|
|
bool ShutdownDualShock(HANDLE handle, int outReportSize) {
|
|
if (outReportSize != sizeof(DualshockOutputReport)) {
|
|
WARN_LOG(Log::UI, "DS4 unexpected report size %d", outReportSize);
|
|
return false;
|
|
}
|
|
|
|
DualshockOutputReport report{};
|
|
FillDualShockOutputReport(&report, false);
|
|
return WriteReport(handle, report);
|
|
}
|
|
|
|
bool ReadDualShockInput(HANDLE handle, HIDControllerState *state, int inReportSize) {
|
|
if (inReportSize > 1024) {
|
|
return false;
|
|
}
|
|
BYTE inputReport[1024]{};
|
|
DWORD bytesRead = 0;
|
|
if (!ReadFile(handle, inputReport, inReportSize, &bytesRead, nullptr)) {
|
|
u32 error = GetLastError();
|
|
return false;
|
|
}
|
|
DualShockInputReport report{};
|
|
static_assert(sizeof(report) < sizeof(inputReport));
|
|
if (bytesRead < 14) {
|
|
return false;
|
|
}
|
|
|
|
// OK, check the first byte to figure out what we're dealing with here.
|
|
int offset = 1;
|
|
int reportId;
|
|
if (inputReport[0] == 0xA1) {
|
|
// 2-byte bluetooth frame
|
|
offset = 2;
|
|
reportId = inputReport[1];
|
|
} else {
|
|
offset = 1;
|
|
reportId = inputReport[0];
|
|
}
|
|
// const bool isBluetooth = (reportId == 0x11 || reportId == 0x31);
|
|
|
|
memcpy(&report, inputReport + offset, sizeof(report));
|
|
|
|
// Center the sticks.
|
|
state->stickAxes[HID_STICK_LX] = report.lx - 128;
|
|
state->stickAxes[HID_STICK_LY] = report.ly - 128;
|
|
state->stickAxes[HID_STICK_RX] = report.rx - 128;
|
|
state->stickAxes[HID_STICK_RY] = report.ry - 128;
|
|
|
|
// Copy over the triggers.
|
|
state->triggerAxes[HID_TRIGGER_L2] = report.l2_analog;
|
|
state->triggerAxes[HID_TRIGGER_R2] = report.r2_analog;
|
|
|
|
state->accValid = false;
|
|
|
|
u32 buttons{};
|
|
int frameCounter = report.buttons[2] >> 2;
|
|
report.buttons[2] &= 3;
|
|
memcpy(&buttons, &report.buttons[0], 3);
|
|
|
|
// Clear out and re-fill the DPAD, it works differently somehow
|
|
buttons &= ~0xF;
|
|
buttons |= DecodePSHatSwitch(report.buttons[0] & 0xF);
|
|
|
|
state->buttons = buttons;
|
|
return true;
|
|
}
|