feat: oc page QoL (#1193)

* feat: move warning to sidebar and make it more specific

(cherry picked from commit ac745e669d)

* phrasing

(cherry picked from commit 44d87a13d9)

* remove wrapper

(cherry picked from commit 3bcad769e3)

* shorten description a bit

(cherry picked from commit f37485c34f)

* use info-dialog to guard manual p-lvl for p-states conf

(cherry picked from commit 563dfac8f4)

* p-level/powermizer: use more compact description

(cherry picked from commit e8c858d7a0)

* p-level fix formatting

* less agressive wording for warning

* fix typo

* remove oc-warning
This commit is contained in:
Roman Makarov authored and GitHub committed 2026-09-06 00:18:42 +03:00
1 parent 4d326a46f7
commit b81a3771ed
9 files changed
+138 -66

No files matched your search

+5 -6
View File
@@ -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
+9
View File
@@ -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);
}
+18
View File
@@ -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 {
+1
View File
@@ -17,6 +17,7 @@ pub enum InfoDialogId {
Unknown,
Error,
ResetConfigConfirmation,
EnablePstateConfigConfirmation,
}
#[derive(Clone, Debug, Default)]
+2
View File
@@ -35,6 +35,8 @@ pub enum AppMsg {
DisableOverdrive,
ResetConfig,
ResetConfigConfirmed,
EnablePstateConfig,
EnablePstateConfigConfirmed,
FetchProcessList,
ReloadProfiles {
state_sender: Option<relm4::Sender<ProfileRuleRowMsg>>,
+14 -1
View File
@@ -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);
}
@@ -112,14 +112,6 @@ impl relm4::Component for ClocksFrame {
},
},
append_child = &gtk::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 = &gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 5,
@@ -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 {
@@ -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<PerformanceLevel>),
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<Self>) {
fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
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));