diff --git a/lact-gui/i18n/en/lact_gui.ftl b/lact-gui/i18n/en/lact_gui.ftl index aa111337..1ff09f6f 100644 --- a/lact-gui/i18n/en/lact_gui.ftl +++ b/lact-gui/i18n/en/lact_gui.ftl @@ -151,9 +151,9 @@ performance-level-high-description = Always use the highest clockspeeds for GPU performance-level-low-description = Always use the lowest clockspeeds for GPU and VRAM performance-level-manual-description = Manual performance control performance-level-profile-standard-description = Fixed profiling mode -performance-level-profile-min-sclk-description = Profiling mode that forces the GPU clock to lowest level -performance-level-profile-min-mclk-description = Profiling mode that forces the VRAM clock to lowest level -performance-level-profile-peak-description = Profiling mode that forces GPU and VRAM clocks to highest levels +performance-level-profile-min-sclk-description = Forces the GPU clock to lowest level +performance-level-profile-min-mclk-description = Forces the VRAM clock to lowest level +performance-level-profile-peak-description = Forces GPU and VRAM clocks to highest levels performance-level = Performance Level power-profile-mode = Power Profile Mode: @@ -180,7 +180,6 @@ nvidia-oc-description = It is possible to achieve a pseudo-undervolt by combining the locked clocks option with a positive clockspeed offset. This will force the GPU to run at a voltage that's constrained by the locked clocks, while achieving a higher clockspeed due to the offset. This can cause system instability if pushed too high. -oc-warning = Changing these values may lead to system instability and can potentially damage your hardware! show-all-pstates = Show all P-States enable-gpu-locked-clocks = Enable GPU Locked Clocks enable-vram-locked-clocks = Enable VRAM Locked Clocks @@ -221,8 +220,8 @@ mem-pstate-clock-voltage = VRAM P-State {$pstate} Voltage (mV) pstates = Power States gpu-pstates = GPU Power States vram-pstates = VRAM Power States -pstates-manual-needed = Performance level must be set to 'manual' to toggle power states -enable-pstate-config = Enable power state configuration +pstates-manual-needed = To enable P-State configuration, the performance level must be set to Manual. +enable-pstate-config = P-State Configuration menu = Menu show-historical-charts = Show Graphs diff --git a/lact-gui/src/app.css b/lact-gui/src/app.css index 8abc2fcc..9f6f8b10 100644 --- a/lact-gui/src/app.css +++ b/lact-gui/src/app.css @@ -13,3 +13,12 @@ .main-sidebar-container .sidebar-section { margin: 4px 8px 8px 8px; } + +.oc-warning-button > button { + color: @warning_color; + background: alpha(@warning_color, 0.15); +} + +.oc-warning-button > button:hover { + background: alpha(@warning_color, 0.25); +} diff --git a/lact-gui/src/app.rs b/lact-gui/src/app.rs index 8b758028..7a63c152 100644 --- a/lact-gui/src/app.rs +++ b/lact-gui/src/app.rs @@ -850,6 +850,24 @@ impl AppModel { self.overdrive_dialog.emit(OverdriveDialogMsg::Loaded); result?; } + AppMsg::EnablePstateConfig => { + self.info_dialog + .emit(InfoDialogMsg::Show(Box::new(InfoDialogData { + id: InfoDialogId::EnablePstateConfigConfirmation, + heading: fl!(I18N, "enable-pstate-config"), + body: fl!(I18N, "pstates-manual-needed"), + confirmation: Some(InfoDialogConfirmation { + confirm_label: fl!(I18N, "confirm"), + cancel_label: fl!(I18N, "cancel"), + appearance: adw::ResponseAppearance::Suggested, + confirm_msg: AppMsg::EnablePstateConfigConfirmed, + }), + ..Default::default() + }))); + } + AppMsg::EnablePstateConfigConfirmed => { + self.oc_page.emit(OcPageMsg::EnablePstateConfig); + } AppMsg::ResetConfig => { self.info_dialog .emit(InfoDialogMsg::Show(Box::new(InfoDialogData { diff --git a/lact-gui/src/app/info_dialog.rs b/lact-gui/src/app/info_dialog.rs index 7c7baa1a..91a8b263 100644 --- a/lact-gui/src/app/info_dialog.rs +++ b/lact-gui/src/app/info_dialog.rs @@ -17,6 +17,7 @@ pub enum InfoDialogId { Unknown, Error, ResetConfigConfirmation, + EnablePstateConfigConfirmation, } #[derive(Clone, Debug, Default)] diff --git a/lact-gui/src/app/msg.rs b/lact-gui/src/app/msg.rs index bc2f5b52..b2482eb6 100644 --- a/lact-gui/src/app/msg.rs +++ b/lact-gui/src/app/msg.rs @@ -35,6 +35,8 @@ pub enum AppMsg { DisableOverdrive, ResetConfig, ResetConfigConfirmed, + EnablePstateConfig, + EnablePstateConfigConfirmed, FetchProcessList, ReloadProfiles { state_sender: Option>, diff --git a/lact-gui/src/app/pages/oc_page.rs b/lact-gui/src/app/pages/oc_page.rs index 6ed53ca3..fa54b8e4 100644 --- a/lact-gui/src/app/pages/oc_page.rs +++ b/lact-gui/src/app/pages/oc_page.rs @@ -4,6 +4,7 @@ mod power_cap_section; mod power_states; mod vf_curve; +use crate::APP_BROKER; use crate::app::components::gpu_stats_section::{ GpuStat, GpuStatsSection, GpuStatsSectionConfig, GpuStatsSectionMsg, }; @@ -57,6 +58,8 @@ pub enum OcPageMsg { configured: bool, }, PerformanceLevelChanged, + SetPerformanceLevel(PerformanceLevel), + EnablePstateConfig, ShowVfCurveEditor, VfCurveEditingToggled(bool), } @@ -110,7 +113,8 @@ impl relm4::Component for OcPage { vf_curve_editing: vf_curve_editing.clone(), }) .forward(sender.input_sender(), |msg| msg); - let power_states_frame = PowerStatesFrame::detach_default(); + let power_states_frame = + PowerStatesFrame::launch_default().forward(sender.input_sender(), |msg| msg); let performance_frame = PerformanceFrame::launch_default().forward(sender.input_sender(), |msg| msg); @@ -230,6 +234,15 @@ impl relm4::Component for OcPage { self.get_performance_level(), )); } + OcPageMsg::SetPerformanceLevel(level) => { + self.performance_frame + .emit(PerformanceFrameMsg::PerformanceLevel(Some(level))); + APP_BROKER.send(AppMsg::SettingsChanged); + } + OcPageMsg::EnablePstateConfig => { + self.power_states_frame + .emit(PowerStatesFrameMsg::EnableWithManualPerformanceLevel); + } OcPageMsg::ShowVfCurveEditor => { self.vf_curve_editor.emit(VfCurveEditorMsg::Show); } diff --git a/lact-gui/src/app/pages/oc_page/clocks_frame/mod.rs b/lact-gui/src/app/pages/oc_page/clocks_frame/mod.rs index 90b449a0..bee4d0a7 100644 --- a/lact-gui/src/app/pages/oc_page/clocks_frame/mod.rs +++ b/lact-gui/src/app/pages/oc_page/clocks_frame/mod.rs @@ -112,14 +112,6 @@ impl relm4::Component for ClocksFrame { }, }, - append_child = >k::Label { - set_label: &fl!(I18N, "oc-warning"), - set_wrap_mode: pango::WrapMode::Word, - set_halign: gtk::Align::Start, - add_css_class: css::WARNING, - add_css_class: css::DIM_LABEL, - }, - append_child = >k::Box { set_orientation: gtk::Orientation::Vertical, set_spacing: 5, diff --git a/lact-gui/src/app/pages/oc_page/performance_frame/mod.rs b/lact-gui/src/app/pages/oc_page/performance_frame/mod.rs index d321c7ce..ec70a77c 100644 --- a/lact-gui/src/app/pages/oc_page/performance_frame/mod.rs +++ b/lact-gui/src/app/pages/oc_page/performance_frame/mod.rs @@ -14,7 +14,7 @@ use gtk::{ use heuristics_list::PowerProfileHeuristicsList; use i18n_embed_fl::fl; use nvml_wrapper::enums::device::PowerMizerMode; -use relm4::{Component, ComponentController, ComponentParts, ComponentSender, RelmWidgetExt}; +use relm4::{Component, ComponentController, ComponentParts, ComponentSender, RelmWidgetExt, css}; const PERFORMANCE_LEVELS: [PerformanceLevel; 8] = [ PerformanceLevel::Auto, @@ -67,28 +67,39 @@ impl relm4::Component for PerformanceFrame { #[watch] set_visible: model.performance_level.is_some(), - gtk::Label { - set_label: &fl!(I18N, "performance-level"), - }, - - gtk::Label { - #[watch] - set_label: &match model.performance_level { - Some(PerformanceLevel::Auto) => fl!(I18N, "performance-level-auto-description"), - Some(PerformanceLevel::High) => fl!(I18N, "performance-level-high-description"), - Some(PerformanceLevel::Low) => fl!(I18N, "performance-level-low-description"), - Some(PerformanceLevel::Manual) => fl!(I18N, "performance-level-manual-description"), - Some(PerformanceLevel::ProfileStandard) => fl!(I18N, "performance-level-profile-standard-description"), - Some(PerformanceLevel::ProfileMinSclk) => fl!(I18N, "performance-level-profile-min-sclk-description"), - Some(PerformanceLevel::ProfileMinMclk) => fl!(I18N, "performance-level-profile-min-mclk-description"), - Some(PerformanceLevel::ProfilePeak) => fl!(I18N, "performance-level-profile-peak-description"), - _ => String::new(), - }, + gtk::Box { + set_orientation: gtk::Orientation::Vertical, set_hexpand: true, - set_halign: gtk::Align::End, + set_valign: gtk::Align::Center, + + gtk::Label { + set_label: &fl!(I18N, "performance-level"), + set_halign: gtk::Align::Start, + }, + + gtk::Label { + #[watch] + set_label: &match model.performance_level { + Some(PerformanceLevel::Auto) => fl!(I18N, "performance-level-auto-description"), + Some(PerformanceLevel::High) => fl!(I18N, "performance-level-high-description"), + Some(PerformanceLevel::Low) => fl!(I18N, "performance-level-low-description"), + Some(PerformanceLevel::Manual) => fl!(I18N, "performance-level-manual-description"), + Some(PerformanceLevel::ProfileStandard) => fl!(I18N, "performance-level-profile-standard-description"), + Some(PerformanceLevel::ProfileMinSclk) => fl!(I18N, "performance-level-profile-min-sclk-description"), + Some(PerformanceLevel::ProfileMinMclk) => fl!(I18N, "performance-level-profile-min-mclk-description"), + Some(PerformanceLevel::ProfilePeak) => fl!(I18N, "performance-level-profile-peak-description"), + _ => String::new(), + }, + set_halign: gtk::Align::Start, + set_xalign: 0.0, + set_wrap: true, + add_css_class: css::DIM_LABEL, + add_css_class: css::CAPTION, + }, }, gtk::DropDown::from_strings(&level_names_ref) { + set_valign: gtk::Align::Center, #[watch] #[block_signal(level_select_handler)] set_selected: PERFORMANCE_LEVELS.iter().position(|level| model.performance_level == Some(*level)).unwrap_or(0) as u32, @@ -97,7 +108,6 @@ impl relm4::Component for PerformanceFrame { let idx = dropdown.selected(); if let Some(level) = PERFORMANCE_LEVELS.get(idx as usize) { sender.input(PerformanceFrameMsg::PerformanceLevel(Some(*level))); - sender.output(OcPageMsg::PerformanceLevelChanged).unwrap(); APP_BROKER.send(AppMsg::SettingsChanged); } } @ level_select_handler, @@ -179,25 +189,36 @@ impl relm4::Component for PerformanceFrame { #[watch] set_visible: model.active_power_mizer_mode.is_some(), - gtk::Label { - set_label: &fl!(I18N, "power-mizer-mode"), - }, - - gtk::Label { - #[watch] - set_label: &match model.active_power_mizer_mode { - Some(PowerMizerMode::Auto) => fl!(I18N, "power-mizer-mode-auto-description"), - Some(PowerMizerMode::Adaptive) => fl!(I18N, "power-mizer-mode-adaptive-description"), - Some(PowerMizerMode::PreferMaximumPerformance) => fl!(I18N, "power-mizer-mode-prefer-maximum-performance-description"), - Some(PowerMizerMode::PreferConsistentPerformance) => fl!(I18N, "power-mizer-mode-prefer-consistent-performance-description"), - None => String::new(), - }, + gtk::Box { + set_orientation: gtk::Orientation::Vertical, set_hexpand: true, - set_halign: gtk::Align::End, + set_valign: gtk::Align::Center, + + gtk::Label { + set_label: &fl!(I18N, "power-mizer-mode"), + set_halign: gtk::Align::Start, + }, + + gtk::Label { + #[watch] + set_label: &match model.active_power_mizer_mode { + Some(PowerMizerMode::Auto) => fl!(I18N, "power-mizer-mode-auto-description"), + Some(PowerMizerMode::Adaptive) => fl!(I18N, "power-mizer-mode-adaptive-description"), + Some(PowerMizerMode::PreferMaximumPerformance) => fl!(I18N, "power-mizer-mode-prefer-maximum-performance-description"), + Some(PowerMizerMode::PreferConsistentPerformance) => fl!(I18N, "power-mizer-mode-prefer-consistent-performance-description"), + None => String::new(), + }, + set_halign: gtk::Align::Start, + set_xalign: 0.0, + set_wrap: true, + add_css_class: css::DIM_LABEL, + add_css_class: css::CAPTION, + }, }, #[name = "power_mizer_dropdown"] gtk::DropDown { + set_valign: gtk::Align::Center, #[watch] #[block_signal(power_mizer_select_handler)] set_model: Some(&model.power_mizer_modes), @@ -251,6 +272,7 @@ impl relm4::Component for PerformanceFrame { match msg { PerformanceFrameMsg::PerformanceLevel(level) => { self.performance_level = level; + sender.output(OcPageMsg::PerformanceLevelChanged).unwrap(); } PerformanceFrameMsg::PowerProfileModes(table) => { while self.power_profile_modes.n_items() != 0 { diff --git a/lact-gui/src/app/pages/oc_page/power_states/power_states_frame.rs b/lact-gui/src/app/pages/oc_page/power_states/power_states_frame.rs index 8530d7d4..fa9676a3 100644 --- a/lact-gui/src/app/pages/oc_page/power_states/power_states_frame.rs +++ b/lact-gui/src/app/pages/oc_page/power_states/power_states_frame.rs @@ -4,7 +4,7 @@ use crate::{ APP_BROKER, I18N, app::{ components::page_section_expander::PageSectionExpander, msg::AppMsg, - utils::ext::RelmLaunchable as _, + pages::oc_page::OcPageMsg, utils::ext::RelmLaunchable as _, }, }; use amdgpu_sysfs::gpu_handle::{PerformanceLevel, PowerLevelKind}; @@ -16,9 +16,8 @@ use i18n_embed_fl::fl; use indexmap::IndexMap; use lact_schema::{DeviceStats, PowerStates}; use relm4::{ - ComponentController, ComponentParts, ComponentSender, RelmObjectExt, + ComponentController, ComponentParts, ComponentSender, binding::{Binding, BoolBinding}, - css, }; use std::sync::Arc; @@ -42,6 +41,10 @@ pub enum PowerStatesFrameMsg { PerformanceLevel(Option), VramClockRatio(f64), Configurable(bool), + ConfiguredToggled { + configured: bool, + }, + EnableWithManualPerformanceLevel, InternalConfigurableChanged(bool), } @@ -49,7 +52,7 @@ pub enum PowerStatesFrameMsg { impl relm4::SimpleComponent for PowerStatesFrame { type Init = (); type Input = PowerStatesFrameMsg; - type Output = (); + type Output = OcPageMsg; view! { PageSectionExpander::new(&fl!(I18N, "pstates")) { @@ -57,21 +60,20 @@ impl relm4::SimpleComponent for PowerStatesFrame { set_orientation: gtk::Orientation::Vertical, set_spacing: 5, - gtk::Label { - set_label: &fl!(I18N, "pstates-manual-needed"), - add_css_class: css::DIM_LABEL, - set_halign: gtk::Align::Start, - #[watch] - set_visible: model.performance_level.is_some_and(|level| level != PerformanceLevel::Manual), - }, - gtk::CheckButton { set_label: Some(&fl!(I18N, "enable-pstate-config")), - add_binding: (&model.states_configured, "active"), + #[watch] + #[block_signal(configured_toggled_handler)] + set_active: model.states_configured.value(), + + connect_toggled[sender] => move |button| { + sender.input(PowerStatesFrameMsg::ConfiguredToggled { + configured: button.is_active(), + }); + } @ configured_toggled_handler, + #[watch] set_visible: model.performance_level.is_some(), - #[watch] - set_sensitive: model.performance_level.is_some_and(|level| level == PerformanceLevel::Manual), }, gtk::Box { @@ -110,8 +112,9 @@ impl relm4::SimpleComponent for PowerStatesFrame { let states_configured = BoolBinding::new(false); + let configured_sender = sender.clone(); let configured_signal = states_configured.connect_value_notify(move |states_configured| { - sender.input(PowerStatesFrameMsg::InternalConfigurableChanged( + configured_sender.input(PowerStatesFrameMsg::InternalConfigurableChanged( states_configured.get(), )); APP_BROKER.send(AppMsg::SettingsChanged); @@ -132,7 +135,7 @@ impl relm4::SimpleComponent for PowerStatesFrame { ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { + fn update(&mut self, msg: Self::Input, sender: ComponentSender) { match msg { PowerStatesFrameMsg::PowerStates { pstates, @@ -182,6 +185,19 @@ impl relm4::SimpleComponent for PowerStatesFrame { PowerStatesFrameMsg::PerformanceLevel(level) => { self.performance_level = level; } + PowerStatesFrameMsg::ConfiguredToggled { configured } => { + if !configured || self.performance_level == Some(PerformanceLevel::Manual) { + self.states_configured.set_value(configured); + } else { + APP_BROKER.send(AppMsg::EnablePstateConfig); + } + } + PowerStatesFrameMsg::EnableWithManualPerformanceLevel => { + sender + .output(OcPageMsg::SetPerformanceLevel(PerformanceLevel::Manual)) + .unwrap(); + self.states_configured.set_value(true); + } PowerStatesFrameMsg::InternalConfigurableChanged(configurable) => { self.core_states_list .emit(PowerStatesListMsg::Configurable(configurable));