diff --git a/Utilities/Config.h b/Utilities/Config.h index 3bba385825..49f316ff70 100644 --- a/Utilities/Config.h +++ b/Utilities/Config.h @@ -126,7 +126,7 @@ namespace cfg atomic_t m_value; public: - const bool def; + bool def; _bool(node* owner, const std::string& name, bool def = false, bool dynamic = false) : _base(type::_bool, owner, name, dynamic) diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 3c71304960..23ca2b0b3a 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -276,6 +276,11 @@ bool PadHandlerBase::has_led() const return b_has_led; } +bool PadHandlerBase::has_battery() const +{ + return b_has_battery; +} + std::string PadHandlerBase::get_config_dir(pad_handler type, const std::string& title_id) { if (!title_id.empty()) diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 3b3b0027af..0a713d7569 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -63,6 +63,7 @@ protected: int m_thumb_threshold = 0; bool b_has_led = false; + bool b_has_battery = false; bool b_has_deadzones = false; bool b_has_rumble = false; bool b_has_config = false; @@ -134,6 +135,7 @@ public: bool has_rumble() const; bool has_deadzones() const; bool has_led() const; + bool has_battery() const; static std::string get_config_dir(pad_handler type, const std::string& title_id = ""); static std::string get_config_filename(int i, const std::string& title_id = ""); @@ -142,7 +144,9 @@ public: PadHandlerBase(pad_handler type = pad_handler::null); virtual ~PadHandlerBase() = default; // Sets window to config the controller(optional) - virtual void SetPadData(const std::string& /*padId*/, u32 /*largeMotor*/, u32 /*smallMotor*/, s32 /*r*/, s32 /*g*/, s32 /*b*/) {} + virtual void SetPadData(const std::string& /*padId*/, u32 /*largeMotor*/, u32 /*smallMotor*/, s32 /*r*/, s32 /*g*/, s32 /*b*/, bool /*battery_led*/, u32 /*battery_led_brightness*/) {} + virtual u32 get_battery_level(const std::string& /*padId*/) { return 0; } + virtual bool get_device_init(const std::string& /*padId*/) { return false; }; // Return list of devices for that handler virtual std::vector ListDevices() = 0; // Callback called during pad_thread::ThreadFunc diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 0bfec62e5a..c7abe7f602 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -75,6 +75,10 @@ struct pad_config final : cfg::node cfg::_int<0, 255> colorG{ this, "Color Value G", 0 }; cfg::_int<0, 255> colorB{ this, "Color Value B", 0 }; + cfg::_bool led_low_battery_blink{ this, "Blink LED when battery is below 20%", true }; + cfg::_bool led_battery_indicator{ this, "Use LED as a battery indicator", false }; + cfg::_int<0, 100> led_battery_indicator_brightness{ this, "LED battery indicator brightness", 50 }; + cfg::_bool enable_vibration_motor_large{ this, "Enable Large Vibration Motor", true }; cfg::_bool enable_vibration_motor_small{ this, "Enable Small Vibration Motor", true }; cfg::_bool switch_vibration_motors{ this, "Switch Vibration Motors", false }; diff --git a/rpcs3/Input/ds3_pad_handler.cpp b/rpcs3/Input/ds3_pad_handler.cpp index b10d355fdf..92739d0ed6 100644 --- a/rpcs3/Input/ds3_pad_handler.cpp +++ b/rpcs3/Input/ds3_pad_handler.cpp @@ -153,7 +153,7 @@ std::vector ds3_pad_handler::ListDevices() return ds3_pads_list; } -void ds3_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32/* r*/, s32/* g*/, s32 /* b*/) +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 device = get_ds3_device(padId); if (device == nullptr || device->handle == nullptr) diff --git a/rpcs3/Input/ds3_pad_handler.h b/rpcs3/Input/ds3_pad_handler.h index c74c3e57ba..7ea6f96f06 100644 --- a/rpcs3/Input/ds3_pad_handler.h +++ b/rpcs3/Input/ds3_pad_handler.h @@ -98,7 +98,7 @@ public: bool Init() override; std::vector ListDevices() override; - void SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b) 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; void init_config(pad_config* cfg, const std::string& name) override; private: diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index 1bad3afe91..02a7fc0a15 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -126,6 +126,7 @@ ds4_pad_handler::ds4_pad_handler() : PadHandlerBase(pad_handler::ds4) b_has_rumble = true; b_has_deadzones = true; b_has_led = true; + b_has_battery = true; m_name_string = "DS4 Pad #"; m_max_devices = CELL_PAD_MAX_PORT_NUM; @@ -173,16 +174,31 @@ void ds4_pad_handler::init_config(pad_config* cfg, const std::string& name) cfg->rtriggerthreshold.def = 0; // between 0 and 255 cfg->padsquircling.def = 8000; - // Set color value + // Set default color value cfg->colorR.def = 0; cfg->colorG.def = 0; cfg->colorB.def = 20; + // Set default LED options + cfg->led_battery_indicator.def = false; + cfg->led_battery_indicator_brightness.def = 10; + cfg->led_low_battery_blink.def = true; + // apply defaults cfg->from_default(); } -void ds4_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b) +u32 ds4_pad_handler::get_battery_level(const std::string& padId) +{ + std::shared_ptr device = GetDS4Device(padId); + if (device == nullptr || device->hidDevice == nullptr) + { + return 0; + } + return std::min(device->batteryLevel * 10, 100); +} + +void ds4_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 device = GetDS4Device(padId); if (device == nullptr || device->hidDevice == nullptr) @@ -208,7 +224,14 @@ void ds4_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 s } // Set new LED color - if (r >= 0 && g >= 0 && b >= 0 && r <= 255 && g <= 255 && b <= 255) + if (battery_led) + { + const u32 combined_color = get_battery_color(device->batteryLevel, battery_led_brightness); + device->config->colorR.set(combined_color >> 8); + device->config->colorG.set(combined_color & 0xff); + device->config->colorB.set(0); + } + else if (r >= 0 && g >= 0 && b >= 0 && r <= 255 && g <= 255 && b <= 255) { device->config->colorR.set(r); device->config->colorG.set(g); @@ -720,7 +743,7 @@ ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr else if (!device->btCon && buf[0] == 0x01 && res == 64) { // Ds4 Dongle uses this bit to actually report whether a controller is connected - bool connected = (buf[31] & 0x04) ? false : true; + const bool connected = (buf[31] & 0x04) ? false : true; if (connected && !device->hasCalibData) device->hasCalibData = GetCalibrationData(device); @@ -729,7 +752,8 @@ ds4_pad_handler::DS4DataStatus ds4_pad_handler::GetRawData(const std::shared_ptr else return DS4DataStatus::NoNewData; - int battery_offset = offset + DS4_INPUT_REPORT_BATTERY_OFFSET; + const int battery_offset = offset + DS4_INPUT_REPORT_BATTERY_OFFSET; + device->is_initialized = true; device->cableState = (buf[battery_offset] >> 4) & 0x01; device->batteryLevel = buf[battery_offset] & 0x0F; @@ -758,6 +782,16 @@ std::shared_ptr ds4_pad_handler::get_device(const std::string& device return ds4device; } +bool ds4_pad_handler::get_device_init(const std::string& padId) +{ + std::shared_ptr device = GetDS4Device(padId); + if (device == nullptr || device->hidDevice == nullptr) + { + return false; + } + return device->is_initialized; +} + bool ds4_pad_handler::get_is_left_trigger(u64 keyCode) { return keyCode == DS4KeyCodes::L2; @@ -796,6 +830,20 @@ bool ds4_pad_handler::get_is_right_stick(u64 keyCode) } } +u32 ds4_pad_handler::get_battery_color(u8 battery_level, int brightness) +{ + static const std::array battery_level_clr = {0xff00, 0xff33, 0xff66, 0xff99, 0xffcc, 0xffff, 0xccff, 0x99ff, 0x66ff, 0x33ff, 0x00ff, 0x00ff}; + u32 combined_color = battery_level_clr[0]; + // Check if we got a weird value + if (battery_level < battery_level_clr.size()) + { + combined_color = battery_level_clr[battery_level]; + } + const u32 red = (combined_color >> 8) * brightness / 100; + const u32 green = (combined_color & 0xff) * brightness / 100; + return ((red << 8) | green); +} + PadHandlerBase::connection ds4_pad_handler::update_connection(const std::shared_ptr& device) { auto ds4_dev = std::static_pointer_cast(device); @@ -893,19 +941,36 @@ void ds4_pad_handler::apply_pad_data(const std::shared_ptr& device, c bool isBlinking = ds4_dev->led_delay_on > 0 || ds4_dev->led_delay_off > 0; bool newBlinkData = false; - // we are now wired or have okay battery level -> stop blinking - if (isBlinking && !(wireless && lowBattery)) + // Blink LED when battery is low + if (ds4_dev->config->led_low_battery_blink) { - ds4_dev->led_delay_on = 0; - ds4_dev->led_delay_off = 0; - newBlinkData = true; + // we are now wired or have okay battery level -> stop blinking + if (isBlinking && !(wireless && lowBattery)) + { + ds4_dev->led_delay_on = 0; + ds4_dev->led_delay_off = 0; + newBlinkData = true; + } + // we are now wireless and low on battery -> blink + if (!isBlinking && wireless && lowBattery) + { + ds4_dev->led_delay_on = 100; + ds4_dev->led_delay_off = 100; + newBlinkData = true; + } } - // we are now wireless and low on battery -> blink - if (!isBlinking && wireless && lowBattery) + + // Use LEDs to indicate battery level + if (ds4_dev->config->led_battery_indicator) { - ds4_dev->led_delay_on = 100; - ds4_dev->led_delay_off = 100; - newBlinkData = true; + // This makes sure that the LED color doesn't update every 1ms. DS4 only reports battery level in 10% increments + if (ds4_dev->last_battery_level != ds4_dev->batteryLevel) + { + const u32 combined_color = get_battery_color(ds4_dev->batteryLevel, ds4_dev->config->led_battery_indicator_brightness); + ds4_dev->config->colorR.set(combined_color >> 8); + ds4_dev->config->colorG.set(combined_color & 0xff); + ds4_dev->config->colorB.set(0); + } } ds4_dev->newVibrateData |= ds4_dev->largeVibrate != speed_large || ds4_dev->smallVibrate != speed_small || newBlinkData; diff --git a/rpcs3/Input/ds4_pad_handler.h b/rpcs3/Input/ds4_pad_handler.h index 1270fe4692..802fde3e83 100644 --- a/rpcs3/Input/ds4_pad_handler.h +++ b/rpcs3/Input/ds4_pad_handler.h @@ -84,9 +84,11 @@ class ds4_pad_handler final : public PadHandlerBase u8 smallVibrate{ 0 }; std::array padData{}; u8 batteryLevel{ 0 }; + u8 last_battery_level { 0 }; u8 cableState{ 0 }; u8 led_delay_on{ 0 }; u8 led_delay_off{ 0 }; + bool is_initialized{ false }; }; const u16 DS4_VID = 0x054C; @@ -105,12 +107,15 @@ public: bool Init() override; std::vector ListDevices() override; - void SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b) 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; + bool get_device_init(const std::string& padId) override; void init_config(pad_config* cfg, const std::string& name) override; private: bool is_init = false; DS4DataStatus status; + u32 get_battery_color(u8 battery_level, int brightness); private: std::shared_ptr GetDS4Device(const std::string& padId, bool try_reconnect = false); diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index f35e1327d7..82f05a5ce6 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -493,7 +493,7 @@ void evdev_joystick_handler::SetRumble(std::shared_ptrdevice, u16 l device->force_small = small; } -void evdev_joystick_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32/* r*/, s32/* g*/, s32/* b*/) +void evdev_joystick_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 /* r*/, s32 /* g*/, s32 /* b*/, bool /*battery_led*/, u32 /*battery_led_brightness*/) { // Get our evdev device auto dev = get_evdev_device(padId); diff --git a/rpcs3/Input/evdev_joystick_handler.h b/rpcs3/Input/evdev_joystick_handler.h index aa9e25a0e9..69b4d708d1 100644 --- a/rpcs3/Input/evdev_joystick_handler.h +++ b/rpcs3/Input/evdev_joystick_handler.h @@ -336,7 +336,7 @@ public: bool bindPadToDevice(std::shared_ptr pad, const std::string& device) override; void Close(); void get_next_button_press(const std::string& padId, const std::function)>& callback, const std::function& fail_callback, bool get_blacklist = false, const std::vector& buttons = {}) override; - void SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b) 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; private: std::shared_ptr get_evdev_device(const std::string& device); diff --git a/rpcs3/Input/xinput_pad_handler.cpp b/rpcs3/Input/xinput_pad_handler.cpp index 95e9dfc469..0f268f9b33 100644 --- a/rpcs3/Input/xinput_pad_handler.cpp +++ b/rpcs3/Input/xinput_pad_handler.cpp @@ -124,7 +124,7 @@ void xinput_pad_handler::init_config(pad_config* cfg, const std::string& name) cfg->from_default(); } -void xinput_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32/* r*/, s32/* g*/, s32/* b*/) +void xinput_pad_handler::SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32/* r*/, s32/* g*/, s32/* b*/, bool /*battery_led*/, u32 /*battery_led_brightness*/) { int device_number = GetDeviceNumber(padId); if (device_number < 0) diff --git a/rpcs3/Input/xinput_pad_handler.h b/rpcs3/Input/xinput_pad_handler.h index 870c605fd9..bece78ca53 100644 --- a/rpcs3/Input/xinput_pad_handler.h +++ b/rpcs3/Input/xinput_pad_handler.h @@ -98,7 +98,7 @@ public: bool Init() override; std::vector ListDevices() override; - void SetPadData(const std::string& padId, u32 largeMotor, u32 smallMotor, s32 r, s32 g, s32 b) 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; void init_config(pad_config* cfg, const std::string& name) override; private: diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 6dc1db8ab6..2075781b41 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -476,6 +476,11 @@ true true + + true + true + true + true true @@ -696,6 +701,11 @@ true true + + true + true + true + true true @@ -936,6 +946,11 @@ true true + + true + true + true + true true @@ -1156,6 +1171,11 @@ true true + + true + true + true + true true @@ -1256,6 +1276,7 @@ + @@ -1437,6 +1458,24 @@ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + + Moc%27ing %(Identity)... + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + Moc%27ing %(Identity)... + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB "-DBRANCH=$(BRANCH)" -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) + $(QTDIR)\bin\moc.exe;%(FullPath) + $(QTDIR)\bin\moc.exe;%(FullPath) + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing settings_dialog.h... Moc%27ing settings_dialog.h... @@ -1647,6 +1686,7 @@ + @@ -2384,6 +2424,26 @@ "$(QTDIR)\bin\rcc.exe" -name "%(Filename)" -no-compress "%(FullPath)" -o .\QTGeneratedFiles\qrc_%(Filename).cpp + + + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 89714dab20..2b9191716d 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -868,6 +868,21 @@ Gui\settings + + Gui\settings + + + Generated Files\Release - LLVM + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\Debug - LLVM + @@ -888,6 +903,9 @@ Generated Files + + Generated Files + Generated Files @@ -995,6 +1013,9 @@ Form Files + + Form Files + Form Files @@ -1037,6 +1058,9 @@ Gui\settings + + Gui\settings + Gui\log diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index f37d3ecefd..faef80c45e 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -29,6 +29,7 @@ memory_viewer_panel.cpp msg_dialog_frame.cpp osk_dialog_frame.cpp + pad_led_settings_dialog.cpp pad_settings_dialog.cpp persistent_settings.cpp pkg_install_dialog.cpp @@ -59,6 +60,7 @@ set(UI_FILES about_dialog.ui main_window.ui + pad_led_settings_dialog.ui pad_settings_dialog.ui settings_dialog.ui welcome_dialog.ui diff --git a/rpcs3/rpcs3qt/pad_led_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_led_settings_dialog.cpp new file mode 100644 index 0000000000..bff38dc7a1 --- /dev/null +++ b/rpcs3/rpcs3qt/pad_led_settings_dialog.cpp @@ -0,0 +1,99 @@ +#include "pad_led_settings_dialog.h" + +#include +#include +#include + +pad_led_settings_dialog::pad_led_settings_dialog(const int& colorR, const int& colorG, const int& colorB, const bool& led_low_battery_blink, const bool& led_battery_indicator, const int& led_battery_indicator_brightness, QDialog * parent) + : QDialog(parent) + , ui(new Ui::pad_led_settings_dialog) + , m_initial{colorR, colorG, colorB, led_low_battery_blink, led_battery_indicator, led_battery_indicator_brightness} +{ + ui->setupUi(this); + setFixedSize(size()); + m_new = m_initial; + redraw_color_sample(); + ui->hs_indicator_brightness->setValue(m_new.battery_indicator_brightness); + ui->cb_led_blink->setChecked(m_new.low_battery_blink); + ui->cb_led_indicate->setChecked(m_new.battery_indicator); + switch_groupboxes(m_new.battery_indicator); + update_slider_label(m_new.battery_indicator_brightness); + connect(ui->bb_dialog_buttons, &QDialogButtonBox::clicked, [this](QAbstractButton* button) + { + if (button == ui->bb_dialog_buttons->button(QDialogButtonBox::Ok)) + { + read_form_values(); + accept(); + } + else if (button == ui->bb_dialog_buttons->button(QDialogButtonBox::Cancel)) + { + m_new = m_initial; + reject(); + } + else if (button == ui->bb_dialog_buttons->button(QDialogButtonBox::Apply)) + { + read_form_values(); + } + 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); + }); + connect(ui->b_colorpicker, &QPushButton::clicked, [this]() + { + const QColor led_color(m_new.cR, m_new.cG, m_new.cB); + QColorDialog dlg(led_color, this); + dlg.setWindowTitle(tr("LED Color")); + if (dlg.exec() == QColorDialog::Accepted) + { + const QColor new_color = dlg.selectedColor(); + m_new.cR = new_color.red(); + m_new.cG = new_color.green(); + m_new.cB = new_color.blue(); + redraw_color_sample(); + } + }); + 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); +} + +pad_led_settings_dialog::~pad_led_settings_dialog() +{ + delete ui; +} + +void pad_led_settings_dialog::redraw_color_sample() +{ + const qreal w = ui->w_color_sample->width(); + const qreal h = ui->w_color_sample->height(); + const qreal padding = 5; + const qreal radius = 5; + QColor led_color; + QPixmap color_sample(w, h); + color_sample.fill(QColor("transparent")); + QPainter painter(&color_sample); + QPainterPath path; + QPen border(Qt::black, 1); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(border); + led_color.setRgb(m_new.cR, m_new.cG, m_new.cB); + path.addRoundedRect(QRectF(padding, padding, w - padding * 2, h - padding * 2), radius, radius); + painter.fillPath(path, led_color); + painter.drawPath(path); + ui->w_color_sample->setPixmap(color_sample); +} + +void pad_led_settings_dialog::update_slider_label(int val) +{ + const QString label = QString::number(val) + QStringLiteral("%"); + ui->l_indicator_brightness->setText(label); +} + +void pad_led_settings_dialog::switch_groupboxes(bool tick) +{ + ui->gb_indicator_brightness->setEnabled(tick); +} + +void pad_led_settings_dialog::read_form_values() +{ + m_new.low_battery_blink = ui->cb_led_blink->isChecked(); + m_new.battery_indicator = ui->cb_led_indicate->isChecked(); + m_new.battery_indicator_brightness = ui->hs_indicator_brightness->value(); +} diff --git a/rpcs3/rpcs3qt/pad_led_settings_dialog.h b/rpcs3/rpcs3qt/pad_led_settings_dialog.h new file mode 100644 index 0000000000..a9e3e96c6c --- /dev/null +++ b/rpcs3/rpcs3qt/pad_led_settings_dialog.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "ui_pad_led_settings_dialog.h" + +namespace Ui +{ + class pad_led_settings_dialog; +} + +class pad_led_settings_dialog : public QDialog +{ + Q_OBJECT + +public: + explicit pad_led_settings_dialog(const int& colorR, const int& colorG, const int& colorB, const bool& led_low_battery_blink, const bool& led_battery_indicator, const int& led_battery_indicator_brightness, QDialog* parent = Q_NULLPTR); + ~pad_led_settings_dialog(); + +signals: + void pass_led_settings(int m_cR, int m_cG, int m_cB, bool m_low_battery_blink, bool m_battery_indicator, int m_battery_indicator_brightness); + +private Q_SLOTS: + void update_slider_label(int val); + void switch_groupboxes(bool tick); + +private: + void redraw_color_sample(); + void read_form_values(); + Ui::pad_led_settings_dialog *ui; + struct led_settings + { + int cR = 255; + int cG = 255; + int cB = 255; + bool low_battery_blink = true; + bool battery_indicator = false; + int battery_indicator_brightness = 50; + }; + led_settings m_initial; + led_settings m_new; +}; diff --git a/rpcs3/rpcs3qt/pad_led_settings_dialog.ui b/rpcs3/rpcs3qt/pad_led_settings_dialog.ui new file mode 100644 index 0000000000..6a593e16d7 --- /dev/null +++ b/rpcs3/rpcs3qt/pad_led_settings_dialog.ui @@ -0,0 +1,132 @@ + + + pad_led_settings_dialog + + + + 0 + 0 + 292 + 287 + + + + LED Settings + + + + + + + 0 + 100 + + + + LED Color + + + + + + + 249 + 39 + + + + + + + + + + + Select color... + + + + + + + + + + + 0 + 75 + + + + In-game battery status + + + + + + Blink LED when battery is low + + + + + + + Use LED as a battery indicator + + + + + + + + + + LED battery indicator brightness + + + + + + 100% + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + 100 + + + Qt::Horizontal + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 26b534ce91..cc808777e6 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -9,6 +9,7 @@ #include "qt_utils.h" #include "pad_settings_dialog.h" +#include "pad_led_settings_dialog.h" #include "ui_pad_settings_dialog.h" #include "tooltips.h" @@ -112,6 +113,11 @@ pad_settings_dialog::pad_settings_dialog(QWidget *parent, const GameInfo *game) cfg_log.error("Failed to convert device string: %s", m_device_name); return; } + // Update battery indicator (if one is present) if device selection is changed + if (m_enable_battery) + { + ui->pb_battery->setValue(m_handler->get_battery_level(m_device_name)); + } }); // Combobox: Profiles @@ -252,7 +258,6 @@ void pad_settings_dialog::InitButtons() insertButton(button_ids::id_pad_rstick_right, ui->b_rstick_right); insertButton(button_ids::id_pad_rstick_up, ui->b_rstick_up); - m_padButtons->addButton(ui->b_led, button_ids::id_led); m_padButtons->addButton(ui->b_reset, button_ids::id_reset_parameters); m_padButtons->addButton(ui->b_blacklist, button_ids::id_blacklist); m_padButtons->addButton(ui->b_refresh, button_ids::id_refresh); @@ -331,22 +336,15 @@ void pad_settings_dialog::InitButtons() RepaintPreviewLabel(ui->preview_stick_right, value, ui->slider_stick_right->size().width(), rx, ry); }); - connect(ui->b_led, &QPushButton::clicked, [this]() + // Open LED settings + connect(ui->b_led_settings, &QPushButton::clicked, [this]() { - QColor led_color(m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB); - if (ui->b_led->property("led").canConvert()) - { - led_color = ui->b_led->property("led").value(); - } - QColorDialog dlg(led_color, this); - dlg.setWindowTitle(tr("LED Color")); - if (dlg.exec() == QColorDialog::Accepted) - { - const QColor newColor = dlg.selectedColor(); - m_handler->SetPadData(m_device_name, 0, 0, newColor.red(), newColor.green(), newColor.blue()); - ui->b_led->setIcon(gui::utils::get_colorized_icon(QIcon(":/Icons/controllers.png"), Qt::black, newColor)); - ui->b_led->setProperty("led", newColor); - } + // 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, static_cast(m_handler_cfg.led_battery_indicator), m_handler_cfg.led_battery_indicator_brightness); + pad_led_settings_dialog dialog(m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB, static_cast(m_handler_cfg.led_low_battery_blink), static_cast(m_handler_cfg.led_battery_indicator), m_handler_cfg.led_battery_indicator_brightness, this); + connect(&dialog, &pad_led_settings_dialog::pass_led_settings, this, &pad_settings_dialog::apply_led_settings); + 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); }); // Enable Button Remapping @@ -397,6 +395,10 @@ void pad_settings_dialog::InitButtons() { SwitchButtons(false); } + if (m_enable_battery) + { + ui->pb_battery->setValue(0); + } }; // Use timer to get button input @@ -428,16 +430,33 @@ void pad_settings_dialog::InitButtons() [this](std::string pad_name) { SwitchPadInfo(pad_name, false); }, false); } }); + + connect(&m_timer_ds4_battery, &QTimer::timeout, [this]() + { + if (m_handler->get_device_init(m_device_name)) + { + ui->pb_battery->setValue(m_handler->get_battery_level(m_device_name)); + pad_settings_dialog::m_timer_ds4_battery.stop(); + } + }); } void pad_settings_dialog::SetPadData(u32 large_motor, u32 small_motor) { QColor led_color(m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB); - if (ui->b_led->property("led").canConvert()) - { - led_color = ui->b_led->property("led").value(); - } - m_handler->SetPadData(m_device_name, large_motor, small_motor, led_color.red(), led_color.green(), led_color.blue()); + m_handler->SetPadData(m_device_name, large_motor, small_motor, led_color.red(), led_color.green(), led_color.blue(), static_cast(m_handler_cfg.led_battery_indicator), m_handler_cfg.led_battery_indicator_brightness); +} + +// Slot to handle the data from a signal in the led settings dialog +void pad_settings_dialog::apply_led_settings(int colorR, int colorG, int colorB, bool led_low_battery_blink, bool led_battery_indicator, int led_battery_indicator_brightness) +{ + m_handler_cfg.colorR.set(colorR); + m_handler_cfg.colorG.set(colorG); + m_handler_cfg.colorB.set(colorB); + m_handler_cfg.led_battery_indicator.set(led_battery_indicator); + m_handler_cfg.led_battery_indicator_brightness.set(led_battery_indicator_brightness); + m_handler_cfg.led_low_battery_blink.set(led_low_battery_blink); + m_handler->SetPadData(m_device_name, 0, 0, colorR, colorG, colorB, led_battery_indicator, led_battery_indicator_brightness); } void pad_settings_dialog::SwitchPadInfo(const std::string& pad_name, bool is_connected) @@ -567,14 +586,28 @@ void pad_settings_dialog::ReloadButtons() RepaintPreviewLabel(ui->preview_stick_left, ui->slider_stick_left->value(), ui->slider_stick_left->size().width(), lx, ly); RepaintPreviewLabel(ui->preview_stick_right, ui->slider_stick_right->value(), ui->slider_stick_right->size().width(), rx, ry); - // Enable and repaint the LED Button + // Apply stored/default LED settings to the device m_enable_led = m_handler->has_led(); - m_handler->SetPadData(m_device_name, 0, 0, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB); + 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); - const QColor led_color(m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB); - ui->b_led->setIcon(gui::utils::get_colorized_icon(QIcon(":/Icons/controllers.png"), Qt::black, led_color)); - ui->b_led->setProperty("led", led_color); - ui->gb_led->setVisible(m_enable_led); + // Enable battery and LED group box + m_enable_battery = m_handler->has_battery(); + ui->gb_battery->setVisible(m_enable_battery || m_enable_led); + + // Poll the battery level if one is available and fill the progress bar + if (m_enable_battery) + { + if (m_handler->m_type == pad_handler::ds4) + { + // If DS4 is connected via BT, the battery level isn't available for a while. + // This ensures that the battery level isn't indicated prematurely. + m_timer_ds4_battery.start(4); + } + else + { + ui->pb_battery->setValue(m_handler->get_battery_level(m_device_name)); + } + } } void pad_settings_dialog::ReactivateButtons() @@ -840,10 +873,7 @@ void pad_settings_dialog::UpdateLabel(bool is_reset) if (m_handler->has_led()) { - const QColor led_color(m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB); - ui->b_led->setProperty("led", led_color); - ui->b_led->setIcon(gui::utils::get_colorized_icon(QIcon(":/Icons/controllers.png"), Qt::black, led_color)); - m_handler->SetPadData(m_device_name, 0, 0, m_handler_cfg.colorR, m_handler_cfg.colorG, m_handler_cfg.colorB); + 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); } } @@ -868,7 +898,9 @@ void pad_settings_dialog::SwitchButtons(bool is_enabled) ui->gb_vibration->setEnabled(is_enabled && m_enable_rumble); ui->gb_sticks->setEnabled(is_enabled && m_enable_deadzones); ui->gb_triggers->setEnabled(is_enabled && m_enable_deadzones); - ui->gb_led->setEnabled(is_enabled && m_enable_led); + ui->gb_battery->setEnabled(is_enabled && (m_enable_battery || m_enable_led)); + ui->pb_battery->setEnabled(is_enabled && m_enable_battery); + ui->b_led_settings->setEnabled(is_enabled && m_enable_led); ui->gb_mouse_accel->setEnabled(is_enabled && m_handler->m_type == pad_handler::keyboard); ui->gb_mouse_dz->setEnabled(is_enabled && m_handler->m_type == pad_handler::keyboard); ui->gb_stick_lerp->setEnabled(is_enabled && m_handler->m_type == pad_handler::keyboard); @@ -1239,14 +1271,6 @@ void pad_settings_dialog::SaveProfile() m_handler_cfg.rstickdeadzone.set(ui->slider_stick_right->value()); } - if (m_handler->has_led() && ui->b_led->property("led").canConvert()) - { - const QColor led_color = ui->b_led->property("led").value(); - m_handler_cfg.colorR.set(led_color.red()); - m_handler_cfg.colorG.set(led_color.green()); - m_handler_cfg.colorB.set(led_color.blue()); - } - if (m_handler->m_type == pad_handler::keyboard) { m_handler_cfg.mouse_acceleration_x.set(ui->mouse_accel_x->value() * 100); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 1edbdc311f..a98f554eca 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -90,6 +90,9 @@ public: explicit pad_settings_dialog(QWidget *parent = nullptr, const GameInfo *game = nullptr); ~pad_settings_dialog(); +public Q_SLOTS: + void apply_led_settings(int colorR, int colorG, int colorB, bool led_low_battery_blink, bool led_battery_indicator, int led_battery_indicator_brightness); + private Q_SLOTS: void OnPadButtonClicked(int id); void OnTabChanged(int index); @@ -112,6 +115,7 @@ private: bool m_enable_rumble{ false }; bool m_enable_deadzones{ false }; bool m_enable_led{ false }; + bool m_enable_battery{ false }; // Button Mapping QButtonGroup* m_padButtons = nullptr; @@ -149,6 +153,9 @@ private: // Input timer. Its Callback handles the input QTimer m_timer_input; + // DS4 workaround timer + QTimer m_timer_ds4_battery; + // Set vibrate data while keeping the current color void SetPadData(u32 large_motor, u32 small_motor); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index fddf5695f5..dbccd7da8e 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -25,7 +25,7 @@ 60 10 - 886 + 889 612 @@ -1342,8 +1342,8 @@ - 430 - 265 + 415 + 256 @@ -1415,41 +1415,6 @@ - - - - LED - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - :/Icons/controllers.png:/Icons/controllers.png - - - false - - - - - - @@ -1497,7 +1462,7 @@ - + 10 @@ -1507,12 +1472,56 @@ Device Class: - + + + + + + 104 + 0 + + + + Battery status and LED + + + + + + + 16777215 + 15 + + + + 0 + + + false + + + + + + + + 16777215 + 23 + + + + LED Settings + + + + + + @@ -2256,7 +2265,7 @@ - +