mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-26 03:38:39 +01:00
Merge pull request #20620 from hrydgard/ui-fixes
Control the LEDs on DualShock/DualSense gamepads
This commit is contained in:
4 files changed
+142
-58
No files matched your search
@@ -56,27 +56,27 @@ static const char * const condnames[16] = {
|
||||
"al", // Always (unconditional) 14
|
||||
};
|
||||
|
||||
int SignExtend26(int x) {
|
||||
static int SignExtend26(int x) {
|
||||
return (x & 0x02000000) ? (0xFC000000 | x) : (x & 0x3FFFFFF);
|
||||
}
|
||||
|
||||
int SignExtend19(int x) {
|
||||
static int SignExtend19(int x) {
|
||||
return (x & 0x00040000) ? (0xFFF80000 | x) : (x & 0x7FFFF);
|
||||
}
|
||||
|
||||
int SignExtend9(int x) {
|
||||
static int SignExtend9(int x) {
|
||||
return (x & 0x00000100) ? (0xFFFFFE00 | x) : (x & 0x1FF);
|
||||
}
|
||||
|
||||
int SignExtend7(int x) {
|
||||
static int SignExtend7(int x) {
|
||||
return (x & 0x00000040) ? (0xFFFFFF80 | x) : (x & 0x7F);
|
||||
}
|
||||
|
||||
int SignExtend12(int x) {
|
||||
static int SignExtend12(int x) {
|
||||
return (x & 0x00000800) ? (0xFFFFF000 | x) : (x & 0xFFF);
|
||||
}
|
||||
|
||||
int HighestSetBit(int value) {
|
||||
static int HighestSetBit(int value) {
|
||||
int highest = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (value & (1 << i))
|
||||
@@ -85,7 +85,7 @@ int HighestSetBit(int value) {
|
||||
return highest;
|
||||
}
|
||||
|
||||
int LowestSetBit(int value, int maximum = 32) {
|
||||
static int LowestSetBit(int value, int maximum = 32) {
|
||||
for (int i = 0; i < maximum; i++) {
|
||||
if (value & (1 << i))
|
||||
return i;
|
||||
|
||||
+131
-49
@@ -7,6 +7,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Windows/HidInputDevice.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Input/InputState.h"
|
||||
@@ -14,6 +15,10 @@
|
||||
#include "Common/System/NativeApp.h"
|
||||
#include "Common/System/OSD.h"
|
||||
|
||||
constexpr u8 LED_R = 0x05;
|
||||
constexpr u8 LED_G = 0x10;
|
||||
constexpr u8 LED_B = 0x40;
|
||||
|
||||
enum PSButton : u32 {
|
||||
PS_DPAD_UP = 1, // These dpad ones are not real, we convert from hat switch format.
|
||||
PS_DPAD_DOWN = 2,
|
||||
@@ -135,41 +140,15 @@ static bool IsSupportedGamepad(HANDLE handle, USHORT *pidOut, PSSubType *subType
|
||||
return false;
|
||||
}
|
||||
|
||||
struct DualSenseOutputReport{
|
||||
uint8_t reportId;
|
||||
uint8_t flags1;
|
||||
uint8_t flags2;
|
||||
uint8_t headphone[4];
|
||||
uint8_t muteLED;
|
||||
uint8_t micMute;
|
||||
};
|
||||
|
||||
// https://github.com/ds4windowsapp/DS4Windows/blob/65609b470f53a4f832fb07ac24085d3e28ec15bd/DS4Windows/DS4Library/InputDevices/DualSenseDevice.cs#L905
|
||||
|
||||
// Sends initialization packet to DualSense
|
||||
static bool InitializeDualSense(HANDLE handle, int outReportSize) {
|
||||
if (outReportSize != 48) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Output report (0x05) – sets lightbar, enables full report mode
|
||||
uint8_t reportData[48] = {0};
|
||||
|
||||
DualSenseOutputReport report;
|
||||
report.reportId = 2;
|
||||
report.flags1 = 0x0C;
|
||||
report.flags2 = 0x15;
|
||||
report.muteLED = 1;
|
||||
|
||||
memcpy(reportData, &report, sizeof(report));
|
||||
|
||||
template<class T>
|
||||
static bool WriteReport(HANDLE handle, const T &report) {
|
||||
DWORD written;
|
||||
bool result = WriteFile(handle, reportData, sizeof(reportData), &written, NULL);
|
||||
bool result = WriteFile(handle, &report, sizeof(report), &written, NULL);
|
||||
if (!result) {
|
||||
u32 errorCode = GetLastError();
|
||||
|
||||
if (errorCode == ERROR_INVALID_PARAMETER) {
|
||||
if (!HidD_SetOutputReport(handle, reportData, outReportSize)) {
|
||||
if (!HidD_SetOutputReport(handle, (PVOID)&report, sizeof(T))) {
|
||||
errorCode = GetLastError();
|
||||
}
|
||||
}
|
||||
@@ -180,6 +159,67 @@ static bool InitializeDualSense(HANDLE handle, int outReportSize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct DualSenseOutputReport {
|
||||
u8 reportId;
|
||||
u8 flags1;
|
||||
u8 flags2;
|
||||
u8 rumbleRight;
|
||||
u8 rumbleLeft;
|
||||
u8 pad[2];
|
||||
u8 muteLED;
|
||||
u8 micMute;
|
||||
u8 other[32];
|
||||
u8 enableBrightness;
|
||||
u8 fade;
|
||||
u8 brightness;
|
||||
u8 playerLights;
|
||||
u8 lightbarRed;
|
||||
u8 lightbarGreen;
|
||||
u8 lightbarBlue;
|
||||
};
|
||||
static_assert(sizeof(DualSenseOutputReport) == 48);
|
||||
|
||||
// https://github.com/ds4windowsapp/DS4Windows/blob/65609b470f53a4f832fb07ac24085d3e28ec15bd/DS4Windows/DS4Library/InputDevices/DualSenseDevice.cs#L905
|
||||
|
||||
// Sends initialization packet to DualSense
|
||||
static bool InitializeDualSense(HANDLE handle, int outReportSize) {
|
||||
if (outReportSize != sizeof(DualSenseOutputReport)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DualSenseOutputReport report{};
|
||||
report.reportId = 2;
|
||||
report.flags1 = 0x0C;
|
||||
report.flags2 = 0x15;
|
||||
report.muteLED = 1;
|
||||
report.playerLights = 1;
|
||||
report.enableBrightness = 1;
|
||||
report.brightness = 0; // 0 = high, 1 = medium, 2 = low
|
||||
report.lightbarRed = LED_R;
|
||||
report.lightbarGreen = LED_G;
|
||||
report.lightbarBlue = LED_B;
|
||||
return WriteReport(handle, report);
|
||||
}
|
||||
|
||||
static bool ShutdownDualsense(HANDLE handle, int outReportSize) {
|
||||
if (outReportSize != sizeof(DualSenseOutputReport)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DualSenseOutputReport report{};
|
||||
report.reportId = 2;
|
||||
report.flags1 = 0x0C;
|
||||
report.flags2 = 0x15;
|
||||
report.muteLED = 1;
|
||||
report.playerLights = 0;
|
||||
report.enableBrightness = 1;
|
||||
report.brightness = 2; // 0 = high, 1 = medium, 2 = low
|
||||
report.lightbarRed = 0;
|
||||
report.lightbarGreen = 0;
|
||||
report.lightbarBlue = 0;
|
||||
return WriteReport(handle, report);
|
||||
}
|
||||
|
||||
enum class DS4FeatureBits : u8 {
|
||||
VOL_L = 0x10,
|
||||
VOL_R = 0x20,
|
||||
@@ -191,33 +231,67 @@ enum class DS4FeatureBits : u8 {
|
||||
};
|
||||
ENUM_CLASS_BITOPS(DS4FeatureBits);
|
||||
|
||||
struct DualshockOutputReport {
|
||||
u8 reportID;
|
||||
u8 featureBits;
|
||||
u8 two;
|
||||
u8 pad;
|
||||
u8 rumbleRight;
|
||||
u8 rumbleLeft;
|
||||
u8 ledR;
|
||||
u8 ledG;
|
||||
u8 ledB;
|
||||
u8 padding[23];
|
||||
};
|
||||
static_assert(sizeof(DualshockOutputReport) == 32);
|
||||
|
||||
static bool InitializeDualShock(HANDLE handle, int outReportSize) {
|
||||
if (outReportSize != 32) {
|
||||
if (outReportSize != sizeof(DualshockOutputReport)) {
|
||||
WARN_LOG(Log::UI, "DS4 unexpected report size %d", outReportSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
// DS4 USB output report (report ID 0x05)
|
||||
// Total size: 32 bytes (must match device buffer size)
|
||||
uint8_t report[32] = {0};
|
||||
DualshockOutputReport report{};
|
||||
report.reportID = 0x05; // Report ID (DS4 output)
|
||||
report.featureBits = (u8)(DS4FeatureBits::RUMBLE | DS4FeatureBits::LIGHTBAR | DS4FeatureBits::FLASH); // Flags: enable lightbar, rumble, etc.
|
||||
report.two = 2;
|
||||
|
||||
report[0] = 0x05; // Report ID (DS4 output)
|
||||
report[1] = (u8)(DS4FeatureBits::RUMBLE | DS4FeatureBits::LIGHTBAR | DS4FeatureBits::FLASH); // Flags: enable lightbar, rumble, etc.
|
||||
report[2] = 0x02;
|
||||
// Rumble
|
||||
report[4] = 0x00; // Right (weak)
|
||||
report[5] = 0x00; // Left (strong)
|
||||
report.rumbleRight = 0x00; // Right (weak)
|
||||
report.rumbleLeft = 0x00; // Left (strong)
|
||||
|
||||
// Lightbar (RGB)
|
||||
report[6] = 0x00; // Red
|
||||
report[7] = 0x10; // Green
|
||||
report[8] = 0x40; // Blue (dim blue)
|
||||
report.ledR = LED_R;
|
||||
report.ledG = LED_G;
|
||||
report.ledB = LED_B;
|
||||
|
||||
DWORD written;
|
||||
return WriteFile(handle, report, sizeof(report), &written, NULL);
|
||||
return WriteReport(handle, report);
|
||||
}
|
||||
|
||||
HANDLE OpenFirstDualShockOrSense(PSSubType *subType, int *reportSize) {
|
||||
static bool ShutdownDualShock(HANDLE handle, int outReportSize) {
|
||||
if (outReportSize != sizeof(DualshockOutputReport)) {
|
||||
WARN_LOG(Log::UI, "DS4 unexpected report size %d", outReportSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
DualshockOutputReport report{};
|
||||
report.reportID = 0x05; // Report ID (DS4 output)
|
||||
report.featureBits = (u8)(DS4FeatureBits::RUMBLE | DS4FeatureBits::LIGHTBAR | DS4FeatureBits::FLASH); // Flags: enable lightbar, rumble, etc.
|
||||
report.two = 2;
|
||||
|
||||
// Rumble
|
||||
report.rumbleRight = 0x00; // Right (weak)
|
||||
report.rumbleLeft = 0x00; // Left (strong)
|
||||
|
||||
// Lightbar (RGB)
|
||||
report.ledR = 0;
|
||||
report.ledG = 0;
|
||||
report.ledB = 0;
|
||||
|
||||
return WriteReport(handle, report);
|
||||
}
|
||||
|
||||
HANDLE OpenFirstDualShockOrSense(PSSubType *subType, int *reportSize, int *outReportSize) {
|
||||
GUID hidGuid;
|
||||
HidD_GetHidGuid(&hidGuid);
|
||||
|
||||
@@ -251,14 +325,14 @@ HANDLE OpenFirstDualShockOrSense(PSSubType *subType, int *reportSize) {
|
||||
HidD_FreePreparsedData(preparsedData);
|
||||
|
||||
*reportSize = caps.InputReportByteLength;
|
||||
int outReportSize = caps.OutputReportByteLength;
|
||||
*outReportSize = caps.OutputReportByteLength;
|
||||
|
||||
INFO_LOG(Log::UI, "Initializing gamepad. out report size=%d", outReportSize);
|
||||
bool result;
|
||||
if (*subType == PSSubType::DS5) {
|
||||
result = InitializeDualSense(handle, outReportSize);
|
||||
result = InitializeDualSense(handle, *outReportSize);
|
||||
} else {
|
||||
result = InitializeDualShock(handle, outReportSize);
|
||||
result = InitializeDualShock(handle, *outReportSize);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
||||
@@ -428,6 +502,14 @@ bool HidInputDevice::ReadDualSenseInput(HANDLE handle, PSControllerState *state)
|
||||
void HidInputDevice::Init() {}
|
||||
void HidInputDevice::Shutdown() {
|
||||
if (controller_) {
|
||||
switch (subType_) {
|
||||
case PSSubType::DS4:
|
||||
ShutdownDualShock(controller_, outReportSize_);
|
||||
break;
|
||||
case PSSubType::DS5:
|
||||
ShutdownDualsense(controller_, outReportSize_);
|
||||
break;
|
||||
}
|
||||
CloseHandle(controller_);
|
||||
controller_ = nullptr;
|
||||
}
|
||||
@@ -452,7 +534,7 @@ int HidInputDevice::UpdateState() {
|
||||
// Poll for controllers from time to time.
|
||||
if (pollCount_ == 0) {
|
||||
pollCount_ = POLL_FREQ;
|
||||
HANDLE newController = OpenFirstDualShockOrSense(&subType_, &reportSize_);
|
||||
HANDLE newController = OpenFirstDualShockOrSense(&subType_, &reportSize_, &outReportSize_);
|
||||
if (newController) {
|
||||
controller_ = newController;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ private:
|
||||
int pad_ = 0;
|
||||
int pollCount_ = 0;
|
||||
int reportSize_ = 0;
|
||||
int outReportSize_ = 0;
|
||||
enum {
|
||||
POLL_FREQ = 283, // a prime number.
|
||||
};
|
||||
|
||||
@@ -37,7 +37,6 @@ public class TextRenderer {
|
||||
}
|
||||
|
||||
private static Point measureLine(String string, double textSize) {
|
||||
textPaint.setTextSize((float) textSize);
|
||||
int w = (int) textPaint.measureText(string);
|
||||
// Round width up to even already here to avoid annoyances from odd-width 16-bit textures
|
||||
// which OpenGL does not like - each line must be 4-byte aligned
|
||||
@@ -73,11 +72,13 @@ public class TextRenderer {
|
||||
}
|
||||
|
||||
public static int measureText(String string, double textSize) {
|
||||
textPaint.setTextSize((float) textSize);
|
||||
Point s = measure(string, textSize);
|
||||
return (s.x << 16) | s.y;
|
||||
}
|
||||
|
||||
public static int[] renderText(String string, double textSize) {
|
||||
textPaint.setTextSize((float) textSize);
|
||||
Point s = measure(string, textSize);
|
||||
|
||||
int w = s.x;
|
||||
@@ -87,7 +88,7 @@ public class TextRenderer {
|
||||
Canvas canvas = new Canvas(bmp);
|
||||
canvas.drawRect(0.0f, 0.0f, w, h, bg);
|
||||
|
||||
String lines[] = string.replaceAll("\\r", "").split("\n");
|
||||
String [] lines = string.replaceAll("\\r", "").split("\n");
|
||||
float y = 1.0f;
|
||||
|
||||
Path path = null;
|
||||
|
||||
Reference in new issue
Block a user