mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-11 17:28:36 +12:00
ds3: add led battery indicators (Linux only atm)
needs testing. maybe doesn't work
This commit is contained in:
parent
22b8cfd0ba
commit
c2467b7b38
9 changed files with 137 additions and 37 deletions
|
@ -278,6 +278,11 @@ bool PadHandlerBase::has_led() const
|
||||||
return b_has_led;
|
return b_has_led;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PadHandlerBase::has_rgb() const
|
||||||
|
{
|
||||||
|
return b_has_rgb;
|
||||||
|
}
|
||||||
|
|
||||||
bool PadHandlerBase::has_battery() const
|
bool PadHandlerBase::has_battery() const
|
||||||
{
|
{
|
||||||
return b_has_battery;
|
return b_has_battery;
|
||||||
|
|
|
@ -75,6 +75,7 @@ protected:
|
||||||
int m_thumb_threshold = 0;
|
int m_thumb_threshold = 0;
|
||||||
|
|
||||||
bool b_has_led = false;
|
bool b_has_led = false;
|
||||||
|
bool b_has_rgb = false;
|
||||||
bool b_has_battery = false;
|
bool b_has_battery = false;
|
||||||
bool b_has_deadzones = false;
|
bool b_has_deadzones = false;
|
||||||
bool b_has_rumble = false;
|
bool b_has_rumble = false;
|
||||||
|
@ -145,6 +146,7 @@ public:
|
||||||
bool has_rumble() const;
|
bool has_rumble() const;
|
||||||
bool has_deadzones() const;
|
bool has_deadzones() const;
|
||||||
bool has_led() const;
|
bool has_led() const;
|
||||||
|
bool has_rgb() const;
|
||||||
bool has_battery() const;
|
bool has_battery() const;
|
||||||
|
|
||||||
void set_player(u32 player_id) { m_player_id = player_id; }
|
void set_player(u32 player_id) { m_player_id = player_id; }
|
||||||
|
|
|
@ -35,6 +35,8 @@ struct ds3_output_report
|
||||||
ds3_led led_5; // reserved for another LED
|
ds3_led led_5; // reserved for another LED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr u8 battery_capacity[] = {0, 1, 25, 50, 75, 100};
|
||||||
|
|
||||||
constexpr u16 DS3_VID = 0x054C;
|
constexpr u16 DS3_VID = 0x054C;
|
||||||
constexpr u16 DS3_PID = 0x0268;
|
constexpr u16 DS3_PID = 0x0268;
|
||||||
|
|
||||||
|
@ -76,6 +78,9 @@ ds3_pad_handler::ds3_pad_handler()
|
||||||
b_has_config = true;
|
b_has_config = true;
|
||||||
b_has_rumble = true;
|
b_has_rumble = true;
|
||||||
b_has_deadzones = true;
|
b_has_deadzones = true;
|
||||||
|
b_has_battery = true;
|
||||||
|
b_has_led = true;
|
||||||
|
b_has_rgb = false;
|
||||||
|
|
||||||
m_name_string = "DS3 Pad #";
|
m_name_string = "DS3 Pad #";
|
||||||
m_max_devices = CELL_PAD_MAX_PORT_NUM;
|
m_max_devices = CELL_PAD_MAX_PORT_NUM;
|
||||||
|
@ -98,6 +103,16 @@ ds3_pad_handler::~ds3_pad_handler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 ds3_pad_handler::get_battery_level(const std::string& padId)
|
||||||
|
{
|
||||||
|
std::shared_ptr<ds3_device> device = get_hid_device(padId);
|
||||||
|
if (!device || !device->hidDevice)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return std::clamp<u32>(device->battery_level, 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
void ds3_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32/* r*/, s32/* g*/, s32 /* b*/, bool /*battery_led*/, u32 /*battery_led_brightness*/)
|
void ds3_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32/* r*/, s32/* g*/, s32 /* b*/, bool /*battery_led*/, u32 /*battery_led_brightness*/)
|
||||||
{
|
{
|
||||||
std::shared_ptr<ds3_device> device = get_hid_device(padId);
|
std::shared_ptr<ds3_device> device = get_hid_device(padId);
|
||||||
|
@ -123,19 +138,34 @@ void ds3_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensure(device->config);
|
||||||
|
|
||||||
// Start/Stop the engines :)
|
// Start/Stop the engines :)
|
||||||
send_output_report(device.get());
|
send_output_report(device.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
int ds3_pad_handler::send_output_report(ds3_device* ds3dev)
|
int ds3_pad_handler::send_output_report(ds3_device* ds3dev)
|
||||||
{
|
{
|
||||||
if (!ds3dev || !ds3dev->hidDevice)
|
if (!ds3dev || !ds3dev->hidDevice || !ds3dev->config)
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
ds3_output_report output_report;
|
ds3_output_report output_report;
|
||||||
output_report.rumble.small_motor_on = ds3dev->small_motor;
|
output_report.rumble.small_motor_on = ds3dev->small_motor;
|
||||||
output_report.rumble.large_motor_force = ds3dev->large_motor;
|
output_report.rumble.large_motor_force = ds3dev->large_motor;
|
||||||
|
|
||||||
|
if (ds3dev->config->led_battery_indicator)
|
||||||
|
{
|
||||||
|
if (ds3dev->battery_level >= 75)
|
||||||
|
output_report.led_enabled = 0b00010000;
|
||||||
|
else if (ds3dev->battery_level >= 50)
|
||||||
|
output_report.led_enabled = 0b00001000;
|
||||||
|
else if (ds3dev->battery_level >= 25)
|
||||||
|
output_report.led_enabled = 0b00000100;
|
||||||
|
else
|
||||||
|
output_report.led_enabled = 0b00000010;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
switch (m_player_id)
|
switch (m_player_id)
|
||||||
{
|
{
|
||||||
case 0: output_report.led_enabled = 0b00000010; break;
|
case 0: output_report.led_enabled = 0b00000010; break;
|
||||||
|
@ -148,6 +178,14 @@ int ds3_pad_handler::send_output_report(ds3_device* ds3dev)
|
||||||
default:
|
default:
|
||||||
fmt::throw_exception("DS3 is using forbidden player id %d", m_player_id);
|
fmt::throw_exception("DS3 is using forbidden player id %d", m_player_id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ds3dev->config->led_low_battery_blink && ds3dev->battery_level < 25)
|
||||||
|
{
|
||||||
|
output_report.led[3].interval_duration = 0x14; // 2 seconds
|
||||||
|
output_report.led[3].interval_portion_on = ds3dev->led_delay_on;
|
||||||
|
output_report.led[3].interval_portion_off = ds3dev->led_delay_off;
|
||||||
|
}
|
||||||
|
|
||||||
return hid_write(ds3dev->hidDevice, &output_report.report_id, sizeof(output_report));
|
return hid_write(ds3dev->hidDevice, &output_report.report_id, sizeof(output_report));
|
||||||
}
|
}
|
||||||
|
@ -194,10 +232,9 @@ void ds3_pad_handler::init_config(pad_config* cfg, const std::string& name)
|
||||||
cfg->lpadsquircling.def = 0;
|
cfg->lpadsquircling.def = 0;
|
||||||
cfg->rpadsquircling.def = 0;
|
cfg->rpadsquircling.def = 0;
|
||||||
|
|
||||||
// Set color value
|
// Set default LED options
|
||||||
cfg->colorR.def = 0;
|
cfg->led_battery_indicator.def = false;
|
||||||
cfg->colorG.def = 0;
|
cfg->led_low_battery_blink.def = true;
|
||||||
cfg->colorB.def = 0;
|
|
||||||
|
|
||||||
// apply defaults
|
// apply defaults
|
||||||
cfg->from_default();
|
cfg->from_default();
|
||||||
|
@ -386,6 +423,23 @@ void ds3_pad_handler::get_extended_info(const std::shared_ptr<PadDevice>& device
|
||||||
if (!ds3dev || !pad)
|
if (!ds3dev || !pad)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const u8 battery_status = ds3dev->padData[12 + DS3_HID_OFFSET];
|
||||||
|
|
||||||
|
if (battery_status >= 0xEE)
|
||||||
|
{
|
||||||
|
// Charging (0xEE) or full (0xEF). Let's set the level to 100%.
|
||||||
|
ds3dev->battery_level = 100;
|
||||||
|
ds3dev->cable_state = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ds3dev->battery_level = battery_capacity[std::min<u8>(battery_status, 5)];
|
||||||
|
ds3dev->cable_state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pad->m_battery_level = ds3dev->battery_level;
|
||||||
|
pad->m_cable_state = ds3dev->cable_state;
|
||||||
|
|
||||||
// For unknown reasons the sixaxis values seem to be in little endian on linux
|
// For unknown reasons the sixaxis values seem to be in little endian on linux
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -507,6 +561,38 @@ void ds3_pad_handler::apply_pad_data(const std::shared_ptr<PadDevice>& device, c
|
||||||
const int speed_large = config->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : vibration_min;
|
const int speed_large = config->enable_vibration_motor_large ? pad->m_vibrateMotors[idx_l].m_value : vibration_min;
|
||||||
const int speed_small = config->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value : vibration_min;
|
const int speed_small = config->enable_vibration_motor_small ? pad->m_vibrateMotors[idx_s].m_value : vibration_min;
|
||||||
|
|
||||||
|
const bool wireless = dev->cable_state == 0;
|
||||||
|
const bool low_battery = dev->battery_level < 25;
|
||||||
|
const bool is_blinking = dev->led_delay_on > 0 || dev->led_delay_off > 0;
|
||||||
|
|
||||||
|
// Blink LED when battery is low
|
||||||
|
if (config->led_low_battery_blink)
|
||||||
|
{
|
||||||
|
// we are now wired or have okay battery level -> stop blinking
|
||||||
|
if (is_blinking && !(wireless && low_battery))
|
||||||
|
{
|
||||||
|
dev->led_delay_on = 0;
|
||||||
|
dev->led_delay_off = 0;
|
||||||
|
dev->new_output_data = true;
|
||||||
|
}
|
||||||
|
// we are now wireless and low on battery -> blink
|
||||||
|
else if (!is_blinking && wireless && low_battery)
|
||||||
|
{
|
||||||
|
dev->led_delay_on = 0x80;
|
||||||
|
dev->led_delay_off = 0xFF - dev->led_delay_on;
|
||||||
|
dev->new_output_data = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use LEDs to indicate battery level
|
||||||
|
if (config->led_battery_indicator)
|
||||||
|
{
|
||||||
|
if (dev->last_battery_level != dev->battery_level)
|
||||||
|
{
|
||||||
|
dev->new_output_data = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dev->new_output_data |= dev->large_motor != speed_large || dev->small_motor != speed_small;
|
dev->new_output_data |= dev->large_motor != speed_large || dev->small_motor != speed_small;
|
||||||
|
|
||||||
dev->large_motor = speed_large;
|
dev->large_motor = speed_large;
|
||||||
|
|
|
@ -81,6 +81,7 @@ public:
|
||||||
~ds3_pad_handler();
|
~ds3_pad_handler();
|
||||||
|
|
||||||
void SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b, bool battery_led, u32 battery_led_brightness) override;
|
void SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b, bool battery_led, u32 battery_led_brightness) override;
|
||||||
|
u32 get_battery_level(const std::string& padId) override;
|
||||||
void init_config(pad_config* cfg, const std::string& name) override;
|
void init_config(pad_config* cfg, const std::string& name) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -118,6 +118,7 @@ ds4_pad_handler::ds4_pad_handler()
|
||||||
b_has_rumble = true;
|
b_has_rumble = true;
|
||||||
b_has_deadzones = true;
|
b_has_deadzones = true;
|
||||||
b_has_led = true;
|
b_has_led = true;
|
||||||
|
b_has_rgb = true;
|
||||||
b_has_battery = true;
|
b_has_battery = true;
|
||||||
|
|
||||||
m_name_string = "DS4 Pad #";
|
m_name_string = "DS4 Pad #";
|
||||||
|
|
|
@ -137,6 +137,7 @@ dualsense_pad_handler::dualsense_pad_handler()
|
||||||
b_has_rumble = true;
|
b_has_rumble = true;
|
||||||
b_has_deadzones = true;
|
b_has_deadzones = true;
|
||||||
b_has_led = true;
|
b_has_led = true;
|
||||||
|
b_has_rgb = true;
|
||||||
b_has_battery = false;
|
b_has_battery = false;
|
||||||
|
|
||||||
m_name_string = "DualSense Pad #";
|
m_name_string = "DualSense Pad #";
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
|
|
||||||
pad_led_settings_dialog::pad_led_settings_dialog(QDialog* parent, int colorR, int colorG, int colorB, bool has_battery, bool led_low_battery_blink, bool led_battery_indicator, int led_battery_indicator_brightness)
|
pad_led_settings_dialog::pad_led_settings_dialog(QDialog* parent, int colorR, int colorG, int colorB, bool has_rgb, bool has_battery, bool led_low_battery_blink, bool led_battery_indicator, int led_battery_indicator_brightness)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, ui(new Ui::pad_led_settings_dialog)
|
, ui(new Ui::pad_led_settings_dialog)
|
||||||
, m_initial{colorR, colorG, colorB, led_low_battery_blink, led_battery_indicator, led_battery_indicator_brightness}
|
, m_initial{colorR, colorG, colorB, led_low_battery_blink, led_battery_indicator, led_battery_indicator_brightness}
|
||||||
|
@ -18,11 +18,11 @@ pad_led_settings_dialog::pad_led_settings_dialog(QDialog* parent, int colorR, in
|
||||||
ui->cb_led_blink->setChecked(m_new.low_battery_blink);
|
ui->cb_led_blink->setChecked(m_new.low_battery_blink);
|
||||||
ui->cb_led_indicate->setChecked(m_new.battery_indicator);
|
ui->cb_led_indicate->setChecked(m_new.battery_indicator);
|
||||||
|
|
||||||
switch_groupboxes(m_new.battery_indicator);
|
|
||||||
update_slider_label(m_new.battery_indicator_brightness);
|
update_slider_label(m_new.battery_indicator_brightness);
|
||||||
|
|
||||||
|
ui->gb_led_color->setEnabled(has_rgb);
|
||||||
ui->gb_battery_status->setEnabled(has_battery);
|
ui->gb_battery_status->setEnabled(has_battery);
|
||||||
ui->gb_indicator_brightness->setEnabled(has_battery);
|
ui->gb_indicator_brightness->setEnabled(has_battery && has_rgb); // Let's restrict this to rgb capable devices for now
|
||||||
|
|
||||||
connect(ui->bb_dialog_buttons, &QDialogButtonBox::clicked, [this](QAbstractButton* button)
|
connect(ui->bb_dialog_buttons, &QDialogButtonBox::clicked, [this](QAbstractButton* button)
|
||||||
{
|
{
|
||||||
|
@ -42,6 +42,9 @@ pad_led_settings_dialog::pad_led_settings_dialog(QDialog* parent, int colorR, in
|
||||||
}
|
}
|
||||||
Q_EMIT pass_led_settings(m_new.cR, m_new.cG, m_new.cB, m_new.low_battery_blink, m_new.battery_indicator, m_new.battery_indicator_brightness);
|
Q_EMIT pass_led_settings(m_new.cR, m_new.cG, m_new.cB, m_new.low_battery_blink, m_new.battery_indicator, m_new.battery_indicator_brightness);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (has_rgb)
|
||||||
|
{
|
||||||
connect(ui->b_colorpicker, &QPushButton::clicked, [this]()
|
connect(ui->b_colorpicker, &QPushButton::clicked, [this]()
|
||||||
{
|
{
|
||||||
const QColor led_color(m_new.cR, m_new.cG, m_new.cB);
|
const QColor led_color(m_new.cR, m_new.cG, m_new.cB);
|
||||||
|
@ -57,7 +60,8 @@ pad_led_settings_dialog::pad_led_settings_dialog(QDialog* parent, int colorR, in
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(ui->hs_indicator_brightness, &QAbstractSlider::valueChanged, this, &pad_led_settings_dialog::update_slider_label);
|
connect(ui->hs_indicator_brightness, &QAbstractSlider::valueChanged, this, &pad_led_settings_dialog::update_slider_label);
|
||||||
connect(ui->cb_led_indicate, &QCheckBox::toggled, this, &pad_led_settings_dialog::switch_groupboxes);
|
connect(ui->cb_led_indicate, &QCheckBox::toggled, this, &pad_led_settings_dialog::battery_indicator_checked);
|
||||||
|
}
|
||||||
|
|
||||||
// Draw color sample after showing the dialog, in order to have proper dimensions
|
// Draw color sample after showing the dialog, in order to have proper dimensions
|
||||||
show();
|
show();
|
||||||
|
@ -107,9 +111,9 @@ void pad_led_settings_dialog::update_slider_label(int val)
|
||||||
ui->l_indicator_brightness->setText(label);
|
ui->l_indicator_brightness->setText(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pad_led_settings_dialog::switch_groupboxes(bool tick)
|
void pad_led_settings_dialog::battery_indicator_checked(bool checked)
|
||||||
{
|
{
|
||||||
ui->gb_indicator_brightness->setEnabled(tick);
|
ui->gb_indicator_brightness->setEnabled(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pad_led_settings_dialog::read_form_values()
|
void pad_led_settings_dialog::read_form_values()
|
||||||
|
|
|
@ -14,7 +14,7 @@ class pad_led_settings_dialog : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit pad_led_settings_dialog(QDialog* parent, int colorR, int colorG, int colorB, bool has_battery, bool led_low_battery_blink, bool led_battery_indicator, int led_battery_indicator_brightness);
|
explicit pad_led_settings_dialog(QDialog* parent, int colorR, int colorG, int colorB, bool has_rgb, bool has_battery, bool led_low_battery_blink, bool led_battery_indicator, int led_battery_indicator_brightness);
|
||||||
~pad_led_settings_dialog();
|
~pad_led_settings_dialog();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
@ -22,7 +22,7 @@ Q_SIGNALS:
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void update_slider_label(int val);
|
void update_slider_label(int val);
|
||||||
void switch_groupboxes(bool tick);
|
void battery_indicator_checked(bool checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void redraw_color_sample();
|
void redraw_color_sample();
|
||||||
|
|
|
@ -378,7 +378,7 @@ void pad_settings_dialog::InitButtons()
|
||||||
{
|
{
|
||||||
// Allow LED battery indication while the dialog is open
|
// Allow LED battery indication while the dialog is open
|
||||||
m_handler->SetPadData(m_device_name, 0, 0, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB, m_handler_cfg.led_battery_indicator.get(), m_handler_cfg.led_battery_indicator_brightness);
|
m_handler->SetPadData(m_device_name, 0, 0, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB, m_handler_cfg.led_battery_indicator.get(), m_handler_cfg.led_battery_indicator_brightness);
|
||||||
pad_led_settings_dialog dialog(this, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB, m_handler->has_battery(), m_handler_cfg.led_low_battery_blink.get(), m_handler_cfg.led_battery_indicator.get(), m_handler_cfg.led_battery_indicator_brightness);
|
pad_led_settings_dialog dialog(this, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB, m_handler->has_rgb(), m_handler->has_battery(), m_handler_cfg.led_low_battery_blink.get(), m_handler_cfg.led_battery_indicator.get(), m_handler_cfg.led_battery_indicator_brightness);
|
||||||
connect(&dialog, &pad_led_settings_dialog::pass_led_settings, this, &pad_settings_dialog::apply_led_settings);
|
connect(&dialog, &pad_led_settings_dialog::pass_led_settings, this, &pad_settings_dialog::apply_led_settings);
|
||||||
dialog.exec();
|
dialog.exec();
|
||||||
m_handler->SetPadData(m_device_name, 0, 0, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB, false, m_handler_cfg.led_battery_indicator_brightness);
|
m_handler->SetPadData(m_device_name, 0, 0, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB, false, m_handler_cfg.led_battery_indicator_brightness);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue