mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
feat: show detailed power profile information (#360)
This commit is contained in:
parent
cd9a3b2f31
commit
32e25dc269
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -53,9 +53,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "amdgpu-sysfs"
|
name = "amdgpu-sysfs"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "64b277cf650bff281b49b12f4a2a4a004485e0b3ecfd34961c5f9b258800ea09"
|
checksum = "cd30ccf0f663fbe9465c7819c0f52cfcb9bf890c7445192f23ff381fad0b3204"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enum_dispatch",
|
"enum_dispatch",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -10,7 +10,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
amdgpu-sysfs = { version = "0.15.0", features = ["serde"] }
|
amdgpu-sysfs = { version = "0.16.0", features = ["serde"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_with = { version = "3.5.0", default-features = false, features = [
|
serde_with = { version = "3.5.0", default-features = false, features = [
|
||||||
"macros",
|
"macros",
|
||||||
|
@ -2,7 +2,7 @@ mod clocks_frame;
|
|||||||
mod gpu_stats_section;
|
mod gpu_stats_section;
|
||||||
mod performance_frame;
|
mod performance_frame;
|
||||||
mod power_cap_section;
|
mod power_cap_section;
|
||||||
// mod power_cap_frame;
|
mod power_profile;
|
||||||
mod power_states;
|
mod power_states;
|
||||||
|
|
||||||
use self::power_cap_section::PowerCapSection;
|
use self::power_cap_section::PowerCapSection;
|
||||||
|
@ -5,15 +5,19 @@ use gtk::prelude::*;
|
|||||||
use gtk::*;
|
use gtk::*;
|
||||||
use std::{cell::RefCell, rc::Rc, str::FromStr};
|
use std::{cell::RefCell, rc::Rc, str::FromStr};
|
||||||
|
|
||||||
|
use super::power_profile::power_profile_component_grid::PowerProfileComponentGrid;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PerformanceFrame {
|
pub struct PerformanceFrame {
|
||||||
pub container: PageSection,
|
pub container: PageSection,
|
||||||
level_drop_down: DropDown,
|
level_drop_down: DropDown,
|
||||||
mode_drop_down: DropDown,
|
modes_listbox: ListBox,
|
||||||
|
mode_menu_button: MenuButton,
|
||||||
description_label: Label,
|
description_label: Label,
|
||||||
manual_info_button: MenuButton,
|
manual_info_button: MenuButton,
|
||||||
mode_box: Box,
|
mode_box: Box,
|
||||||
modes_table: Rc<RefCell<Option<PowerProfileModesTable>>>,
|
modes_table: Rc<RefCell<Option<PowerProfileModesTable>>>,
|
||||||
|
power_mode_info_notebook: Notebook,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerformanceFrame {
|
impl PerformanceFrame {
|
||||||
@ -41,11 +45,32 @@ impl PerformanceFrame {
|
|||||||
|
|
||||||
let mode_box = Box::new(Orientation::Horizontal, 10);
|
let mode_box = Box::new(Orientation::Horizontal, 10);
|
||||||
|
|
||||||
let mode_drop_down = DropDown::builder()
|
let mode_menu_button = MenuButton::builder()
|
||||||
.sensitive(false)
|
.sensitive(false)
|
||||||
.halign(Align::End)
|
.halign(Align::End)
|
||||||
|
.always_show_arrow(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
let modes_listbox = ListBox::builder()
|
||||||
|
.selection_mode(SelectionMode::Single)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let modes_popover_content = gtk::Box::builder()
|
||||||
|
.orientation(Orientation::Horizontal)
|
||||||
|
.spacing(10)
|
||||||
|
.margin_start(5)
|
||||||
|
.margin_end(5)
|
||||||
|
.margin_top(5)
|
||||||
|
.margin_bottom(5)
|
||||||
|
.build();
|
||||||
|
modes_popover_content.append(&modes_listbox);
|
||||||
|
|
||||||
|
let power_mode_info_notebook = Notebook::new();
|
||||||
|
modes_popover_content.append(&power_mode_info_notebook);
|
||||||
|
|
||||||
|
let modes_popover = Popover::builder().child(&modes_popover_content).build();
|
||||||
|
mode_menu_button.set_popover(Some(&modes_popover));
|
||||||
|
|
||||||
let unavailable_label = Label::new(Some(
|
let unavailable_label = Label::new(Some(
|
||||||
"Performance level has to be set to \"manual\" to use power states and modes",
|
"Performance level has to be set to \"manual\" to use power states and modes",
|
||||||
));
|
));
|
||||||
@ -60,18 +85,20 @@ impl PerformanceFrame {
|
|||||||
let mode_title_label = Label::new(Some("Power level mode:"));
|
let mode_title_label = Label::new(Some("Power level mode:"));
|
||||||
mode_box.append(&mode_title_label);
|
mode_box.append(&mode_title_label);
|
||||||
mode_box.append(&manual_info_button);
|
mode_box.append(&manual_info_button);
|
||||||
mode_box.append(&mode_drop_down);
|
mode_box.append(&mode_menu_button);
|
||||||
|
|
||||||
container.append(&mode_box);
|
container.append(&mode_box);
|
||||||
|
|
||||||
let frame = Self {
|
let frame = Self {
|
||||||
container,
|
container,
|
||||||
level_drop_down,
|
level_drop_down,
|
||||||
mode_drop_down,
|
mode_menu_button,
|
||||||
description_label,
|
description_label,
|
||||||
manual_info_button,
|
manual_info_button,
|
||||||
|
modes_listbox,
|
||||||
mode_box,
|
mode_box,
|
||||||
modes_table: Rc::new(RefCell::new(None)),
|
modes_table: Rc::new(RefCell::new(None)),
|
||||||
|
power_mode_info_notebook,
|
||||||
};
|
};
|
||||||
|
|
||||||
frame.level_drop_down.connect_selected_notify(clone!(
|
frame.level_drop_down.connect_selected_notify(clone!(
|
||||||
@ -82,12 +109,10 @@ impl PerformanceFrame {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
frame.mode_drop_down.connect_selected_notify(clone!(
|
frame.modes_listbox.connect_row_selected(clone!(
|
||||||
#[strong]
|
#[strong]
|
||||||
frame,
|
frame,
|
||||||
move |_| {
|
move |_, _| frame.update_from_selection()
|
||||||
frame.update_from_selection();
|
|
||||||
}
|
|
||||||
));
|
));
|
||||||
|
|
||||||
frame
|
frame
|
||||||
@ -107,25 +132,36 @@ impl PerformanceFrame {
|
|||||||
pub fn set_power_profile_modes(&self, table: Option<PowerProfileModesTable>) {
|
pub fn set_power_profile_modes(&self, table: Option<PowerProfileModesTable>) {
|
||||||
self.mode_box.set_visible(table.is_some());
|
self.mode_box.set_visible(table.is_some());
|
||||||
|
|
||||||
|
while let Some(row) = self.modes_listbox.row_at_index(0) {
|
||||||
|
self.modes_listbox.remove(&row);
|
||||||
|
}
|
||||||
|
|
||||||
match &table {
|
match &table {
|
||||||
Some(table) => {
|
Some(table) => {
|
||||||
let model: StringList = table.modes.values().cloned().collect();
|
for profile in table.modes.values() {
|
||||||
let active_pos = table
|
let profile_label = Label::builder()
|
||||||
.modes
|
.label(&profile.name)
|
||||||
.keys()
|
.margin_start(5)
|
||||||
.position(|key| *key == table.active)
|
.margin_end(5)
|
||||||
.expect("No active mode") as u32;
|
.build();
|
||||||
|
self.modes_listbox.append(&profile_label);
|
||||||
|
}
|
||||||
|
|
||||||
self.mode_drop_down.set_model(Some(&model));
|
let active_row = self
|
||||||
self.mode_drop_down.set_selected(active_pos);
|
.modes_listbox
|
||||||
|
.row_at_index(table.active as i32)
|
||||||
|
.unwrap();
|
||||||
|
self.modes_listbox.select_row(Some(&active_row));
|
||||||
|
|
||||||
self.mode_drop_down.show();
|
self.mode_menu_button.show();
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.mode_drop_down.hide();
|
self.mode_menu_button.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.modes_table.replace(table);
|
self.modes_table.replace(table);
|
||||||
|
|
||||||
|
self.update_from_selection();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect_settings_changed<F: Fn() + 'static + Clone>(&self, f: F) {
|
pub fn connect_settings_changed<F: Fn() + 'static + Clone>(&self, f: F) {
|
||||||
@ -134,7 +170,21 @@ impl PerformanceFrame {
|
|||||||
f,
|
f,
|
||||||
move |_| f()
|
move |_| f()
|
||||||
));
|
));
|
||||||
self.mode_drop_down.connect_selected_notify(move |_| f());
|
self.modes_listbox.connect_row_selected(clone!(
|
||||||
|
#[strong(rename_to = modes_table)]
|
||||||
|
self.modes_table,
|
||||||
|
move |_, row| {
|
||||||
|
let modes_table = modes_table.borrow();
|
||||||
|
|
||||||
|
if let Some(row) = row {
|
||||||
|
if let Some(table) = modes_table.as_ref() {
|
||||||
|
if row.index() != table.active as i32 {
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_selected_performance_level(&self) -> PerformanceLevel {
|
pub fn get_selected_performance_level(&self) -> PerformanceLevel {
|
||||||
@ -148,21 +198,18 @@ impl PerformanceFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_selected_power_profile_mode(&self) -> Option<u16> {
|
pub fn get_selected_power_profile_mode(&self) -> Option<u16> {
|
||||||
if self.mode_drop_down.is_sensitive() {
|
if self.mode_menu_button.is_sensitive() {
|
||||||
self.modes_table.borrow().as_ref().map(|table| {
|
self.modes_listbox
|
||||||
let selected_index = table
|
.selected_row()
|
||||||
.modes
|
.map(|row| row.index() as u16)
|
||||||
.keys()
|
|
||||||
.nth(self.mode_drop_down.selected() as usize)
|
|
||||||
.expect("Selected mode out of range");
|
|
||||||
*selected_index
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_from_selection(&self) {
|
fn update_from_selection(&self) {
|
||||||
|
self.power_mode_info_notebook.set_visible(false);
|
||||||
|
|
||||||
let mut enable_mode_control = false;
|
let mut enable_mode_control = false;
|
||||||
|
|
||||||
let text = match self.level_drop_down.selected() {
|
let text = match self.level_drop_down.selected() {
|
||||||
@ -176,10 +223,51 @@ impl PerformanceFrame {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
self.description_label.set_text(text);
|
self.description_label.set_text(text);
|
||||||
self.mode_drop_down.set_sensitive(enable_mode_control);
|
|
||||||
|
|
||||||
self.manual_info_button.set_visible(!enable_mode_control);
|
self.manual_info_button.set_visible(!enable_mode_control);
|
||||||
self.mode_drop_down.set_hexpand(enable_mode_control);
|
|
||||||
|
self.mode_menu_button.set_sensitive(enable_mode_control);
|
||||||
|
self.mode_menu_button.set_hexpand(enable_mode_control);
|
||||||
|
|
||||||
|
let modes_table = self.modes_table.borrow();
|
||||||
|
if let Some(table) = modes_table.as_ref() {
|
||||||
|
if let Some(row) = self.modes_listbox.selected_row() {
|
||||||
|
if let Some(profile) = table.modes.get(&(row.index() as u16)) {
|
||||||
|
self.mode_menu_button.set_label(&profile.name);
|
||||||
|
|
||||||
|
self.power_mode_info_notebook.set_visible(true);
|
||||||
|
|
||||||
|
// Save current page to be restored after being refilled
|
||||||
|
let current_page = self.power_mode_info_notebook.current_page();
|
||||||
|
// Remove pages
|
||||||
|
while self.power_mode_info_notebook.n_pages() != 0 {
|
||||||
|
self.power_mode_info_notebook.remove_page(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
for component in &profile.components {
|
||||||
|
let values_grid = PowerProfileComponentGrid::new();
|
||||||
|
values_grid.set_component(component, table);
|
||||||
|
|
||||||
|
let title = component.clock_type.as_deref().unwrap_or("All");
|
||||||
|
let title_label = Label::builder()
|
||||||
|
.label(title)
|
||||||
|
.margin_start(5)
|
||||||
|
.margin_end(5)
|
||||||
|
.build();
|
||||||
|
self.power_mode_info_notebook
|
||||||
|
.append_page(&values_grid, Some(&title_label));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.power_mode_info_notebook
|
||||||
|
.set_show_tabs(profile.components.len() > 1);
|
||||||
|
|
||||||
|
// Restore selected page
|
||||||
|
if current_page.is_some() {
|
||||||
|
self.power_mode_info_notebook.set_current_page(current_page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&self) {
|
pub fn show(&self) {
|
||||||
|
1
lact-gui/src/app/root_stack/oc_page/power_profile/mod.rs
Normal file
1
lact-gui/src/app/root_stack/oc_page/power_profile/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod power_profile_component_grid;
|
@ -0,0 +1,90 @@
|
|||||||
|
use amdgpu_sysfs::gpu_handle::power_profile_mode::{PowerProfileComponent, PowerProfileModesTable};
|
||||||
|
use gtk::{
|
||||||
|
glib::{self, Object},
|
||||||
|
prelude::GridExt,
|
||||||
|
prelude::WidgetExt,
|
||||||
|
Label,
|
||||||
|
};
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct PowerProfileComponentGrid(ObjectSubclass<imp::PowerProfileComponentGrid>)
|
||||||
|
@extends gtk::Grid,
|
||||||
|
@implements gtk::Accessible, gtk::Buildable, gtk::Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerProfileComponentGrid {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Object::builder()
|
||||||
|
.property("margin-start", 5)
|
||||||
|
.property("margin-end", 5)
|
||||||
|
.property("margin-top", 5)
|
||||||
|
.property("margin-bottom", 5)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_component(&self, component: &PowerProfileComponent, table: &PowerProfileModesTable) {
|
||||||
|
while let Some(child) = self.first_child() {
|
||||||
|
self.remove(&child);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, value) in component.values.iter().enumerate() {
|
||||||
|
let name = &table.value_names[i];
|
||||||
|
|
||||||
|
let name_label = Label::builder()
|
||||||
|
.label(&format!("{name}:"))
|
||||||
|
.hexpand(true)
|
||||||
|
.halign(gtk::Align::Start)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
self.attach(&name_label, 0, i as i32, 1, 1);
|
||||||
|
|
||||||
|
let mut value_label_builder = Label::builder().halign(gtk::Align::End);
|
||||||
|
value_label_builder = match value {
|
||||||
|
Some(value) => value_label_builder.label(value.to_string()),
|
||||||
|
None => value_label_builder.label("Not set"),
|
||||||
|
};
|
||||||
|
self.attach(&value_label_builder.build(), 1, i as i32, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PowerProfileComponentGrid {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod imp {
|
||||||
|
use gtk::{
|
||||||
|
glib::{self, subclass::InitializingObject},
|
||||||
|
subclass::{
|
||||||
|
prelude::*,
|
||||||
|
widget::{CompositeTemplateClass, WidgetImpl},
|
||||||
|
},
|
||||||
|
CompositeTemplate,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(CompositeTemplate, Default)]
|
||||||
|
#[template(file = "ui/oc_page/power_profile/power_profile_component_grid.blp")]
|
||||||
|
pub struct PowerProfileComponentGrid {}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for PowerProfileComponentGrid {
|
||||||
|
const NAME: &'static str = "PowerProfileComponentGrid";
|
||||||
|
type Type = super::PowerProfileComponentGrid;
|
||||||
|
type ParentType = gtk::Grid;
|
||||||
|
|
||||||
|
fn class_init(class: &mut Self::Class) {
|
||||||
|
class.bind_template();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instance_init(obj: &InitializingObject<Self>) {
|
||||||
|
obj.init_template();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for PowerProfileComponentGrid {}
|
||||||
|
|
||||||
|
impl WidgetImpl for PowerProfileComponentGrid {}
|
||||||
|
impl GridImpl for PowerProfileComponentGrid {}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
using Gtk 4.0;
|
||||||
|
|
||||||
|
template $PowerProfileComponentGrid: Grid {
|
||||||
|
row-spacing: 5;
|
||||||
|
column-spacing: 5;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user