mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
feat: support for power profile heuristics configuration (#361)
* feat: basic support for power profile heuristics configuration * attempt to support RDNA heuristics * chore: finalization
This commit is contained in:
parent
32e25dc269
commit
44704a101e
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -53,9 +53,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
|
||||
[[package]]
|
||||
name = "amdgpu-sysfs"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd30ccf0f663fbe9465c7819c0f52cfcb9bf890c7445192f23ff381fad0b3204"
|
||||
checksum = "13879cbb65d9f6ebfb4e15a4096fefe442c45b4b93f7b273673d56e50aec3c5a"
|
||||
dependencies = [
|
||||
"enum_dispatch",
|
||||
"serde",
|
||||
|
@ -10,7 +10,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
amdgpu-sysfs = { version = "0.16.0", features = ["serde"] }
|
||||
amdgpu-sysfs = { version = "0.16.1", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_with = { version = "3.5.0", default-features = false, features = [
|
||||
"macros",
|
||||
|
@ -164,9 +164,18 @@ impl DaemonClient {
|
||||
.inner()
|
||||
}
|
||||
|
||||
pub fn set_power_profile_mode(&self, id: &str, index: Option<u16>) -> anyhow::Result<u64> {
|
||||
self.make_request(Request::SetPowerProfileMode { id, index })?
|
||||
.inner()
|
||||
pub fn set_power_profile_mode(
|
||||
&self,
|
||||
id: &str,
|
||||
index: Option<u16>,
|
||||
custom_heuristics: Vec<Vec<Option<i32>>>,
|
||||
) -> anyhow::Result<u64> {
|
||||
self.make_request(Request::SetPowerProfileMode {
|
||||
id,
|
||||
index,
|
||||
custom_heuristics,
|
||||
})?
|
||||
.inner()
|
||||
}
|
||||
|
||||
pub fn confirm_pending_config(&self, command: ConfirmCommand) -> anyhow::Result<()> {
|
||||
|
@ -67,6 +67,9 @@ pub struct Gpu {
|
||||
#[serde(default, flatten)]
|
||||
pub clocks_configuration: ClocksConfiguration,
|
||||
pub power_profile_mode_index: Option<u16>,
|
||||
/// Outer vector is for power profile components, inner vector is for the heuristics within a component
|
||||
#[serde(default)]
|
||||
pub custom_power_profile_mode_hueristics: Vec<Vec<Option<i32>>>,
|
||||
#[serde(default)]
|
||||
pub power_states: HashMap<PowerLevelKind, Vec<u8>>,
|
||||
}
|
||||
@ -287,6 +290,7 @@ mod tests {
|
||||
performance_level: None,
|
||||
clocks_configuration: ClocksConfiguration::default(),
|
||||
power_profile_mode_index: None,
|
||||
custom_power_profile_mode_hueristics: vec![],
|
||||
power_states: HashMap::new(),
|
||||
};
|
||||
|
||||
|
@ -783,9 +783,17 @@ impl GpuController {
|
||||
));
|
||||
}
|
||||
|
||||
self.handle
|
||||
.set_active_power_profile_mode(mode_index)
|
||||
.context("Failed to set active power profile mode")?;
|
||||
if config.custom_power_profile_mode_hueristics.is_empty() {
|
||||
self.handle
|
||||
.set_active_power_profile_mode(mode_index)
|
||||
.context("Failed to set active power profile mode")?;
|
||||
} else {
|
||||
self.handle
|
||||
.set_custom_power_profile_mode_heuristics(
|
||||
&config.custom_power_profile_mode_hueristics,
|
||||
)
|
||||
.context("Failed to set custom power profile mode heuristics")?;
|
||||
}
|
||||
}
|
||||
|
||||
for (kind, states) in &config.power_states {
|
||||
|
@ -473,9 +473,11 @@ impl<'a> Handler {
|
||||
&self,
|
||||
id: &str,
|
||||
index: Option<u16>,
|
||||
custom_heuristics: Vec<Vec<Option<i32>>>,
|
||||
) -> anyhow::Result<u64> {
|
||||
self.edit_gpu_config(id.to_owned(), |gpu_config| {
|
||||
gpu_config.power_profile_mode_index = index;
|
||||
gpu_config.custom_power_profile_mode_hueristics = custom_heuristics;
|
||||
})
|
||||
.await
|
||||
.context("Failed to edit GPU config and set power profile mode")
|
||||
|
@ -99,9 +99,15 @@ async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyho
|
||||
Request::BatchSetClocksValue { id, commands } => {
|
||||
ok_response(handler.batch_set_clocks_value(id, commands).await?)
|
||||
}
|
||||
Request::SetPowerProfileMode { id, index } => {
|
||||
ok_response(handler.set_power_profile_mode(id, index).await?)
|
||||
}
|
||||
Request::SetPowerProfileMode {
|
||||
id,
|
||||
index,
|
||||
custom_heuristics,
|
||||
} => ok_response(
|
||||
handler
|
||||
.set_power_profile_mode(id, index, custom_heuristics)
|
||||
.await?,
|
||||
),
|
||||
Request::GetPowerStates { id } => ok_response(handler.get_power_states(id)?),
|
||||
Request::SetEnabledPowerStates { id, kind, states } => {
|
||||
ok_response(handler.set_enabled_power_states(id, kind, states).await?)
|
||||
|
@ -502,7 +502,7 @@ impl App {
|
||||
|
||||
// Reset the power profile mode for switching to/from manual performance level
|
||||
self.daemon_client
|
||||
.set_power_profile_mode(&gpu_id, None)
|
||||
.set_power_profile_mode(&gpu_id, None, vec![])
|
||||
.context("Could not set default power profile mode")?;
|
||||
self.daemon_client
|
||||
.confirm_pending_config(ConfirmCommand::Confirm)
|
||||
@ -521,8 +521,14 @@ impl App {
|
||||
.oc_page
|
||||
.performance_frame
|
||||
.get_selected_power_profile_mode();
|
||||
let custom_heuristics = self
|
||||
.root_stack
|
||||
.oc_page
|
||||
.performance_frame
|
||||
.get_power_profile_mode_custom_heuristics();
|
||||
|
||||
self.daemon_client
|
||||
.set_power_profile_mode(&gpu_id, mode_index)
|
||||
.set_power_profile_mode(&gpu_id, mode_index, custom_heuristics)
|
||||
.context("Could not set active power profile mode")?;
|
||||
self.daemon_client
|
||||
.confirm_pending_config(ConfirmCommand::Confirm)
|
||||
|
@ -1,11 +1,17 @@
|
||||
use crate::app::page_section::PageSection;
|
||||
use amdgpu_sysfs::gpu_handle::{power_profile_mode::PowerProfileModesTable, PerformanceLevel};
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use gtk::subclass::prelude::ObjectSubclassIsExt;
|
||||
use gtk::{
|
||||
glib, DropDown, Label, ListBox, MenuButton, Notebook, NotebookPage, Popover, SelectionMode,
|
||||
StringObject,
|
||||
};
|
||||
use gtk::{prelude::*, Align, Orientation, StringList};
|
||||
use std::{cell::RefCell, rc::Rc, str::FromStr};
|
||||
|
||||
use super::power_profile::power_profile_component_grid::PowerProfileComponentGrid;
|
||||
use super::power_profile::power_profile_heuristics_grid::PowerProfileHeuristicsGrid;
|
||||
|
||||
type ValuesChangedCallback = Rc<dyn Fn()>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PerformanceFrame {
|
||||
@ -15,9 +21,11 @@ pub struct PerformanceFrame {
|
||||
mode_menu_button: MenuButton,
|
||||
description_label: Label,
|
||||
manual_info_button: MenuButton,
|
||||
mode_box: Box,
|
||||
mode_box: gtk::Box,
|
||||
modes_table: Rc<RefCell<Option<PowerProfileModesTable>>>,
|
||||
power_mode_info_notebook: Notebook,
|
||||
|
||||
values_changed_callback: Rc<RefCell<Option<ValuesChangedCallback>>>,
|
||||
}
|
||||
|
||||
impl PerformanceFrame {
|
||||
@ -28,7 +36,7 @@ impl PerformanceFrame {
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let level_box = Box::new(Orientation::Horizontal, 10);
|
||||
let level_box = gtk::Box::new(Orientation::Horizontal, 10);
|
||||
|
||||
let level_drop_down = DropDown::builder()
|
||||
.model(&levels_model)
|
||||
@ -43,7 +51,7 @@ impl PerformanceFrame {
|
||||
|
||||
container.append(&level_box);
|
||||
|
||||
let mode_box = Box::new(Orientation::Horizontal, 10);
|
||||
let mode_box = gtk::Box::new(Orientation::Horizontal, 10);
|
||||
|
||||
let mode_menu_button = MenuButton::builder()
|
||||
.sensitive(false)
|
||||
@ -99,6 +107,7 @@ impl PerformanceFrame {
|
||||
mode_box,
|
||||
modes_table: Rc::new(RefCell::new(None)),
|
||||
power_mode_info_notebook,
|
||||
values_changed_callback: Rc::default(),
|
||||
};
|
||||
|
||||
frame.level_drop_down.connect_selected_notify(clone!(
|
||||
@ -173,6 +182,8 @@ impl PerformanceFrame {
|
||||
self.modes_listbox.connect_row_selected(clone!(
|
||||
#[strong(rename_to = modes_table)]
|
||||
self.modes_table,
|
||||
#[strong]
|
||||
f,
|
||||
move |_, row| {
|
||||
let modes_table = modes_table.borrow();
|
||||
|
||||
@ -185,6 +196,8 @@ impl PerformanceFrame {
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
*self.values_changed_callback.borrow_mut() = Some(Rc::new(f));
|
||||
}
|
||||
|
||||
pub fn get_selected_performance_level(&self) -> PerformanceLevel {
|
||||
@ -207,6 +220,37 @@ impl PerformanceFrame {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_power_profile_mode_custom_heuristics(&self) -> Vec<Vec<Option<i32>>> {
|
||||
let modes_table = self.modes_table.borrow();
|
||||
if let Some(table) = modes_table.as_ref() {
|
||||
if let Some(row) = self.modes_listbox.selected_row() {
|
||||
let active_index = row.index() as u16;
|
||||
if let Some(active_profile) = table.modes.get(&active_index) {
|
||||
if active_profile.is_custom() {
|
||||
let mut components = vec![];
|
||||
|
||||
for page in self
|
||||
.power_mode_info_notebook
|
||||
.pages()
|
||||
.iter::<NotebookPage>()
|
||||
.flatten()
|
||||
{
|
||||
let values_grid = page
|
||||
.child()
|
||||
.downcast::<PowerProfileHeuristicsGrid>()
|
||||
.unwrap();
|
||||
components.push(values_grid.imp().component.borrow().values.clone());
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn update_from_selection(&self) {
|
||||
self.power_mode_info_notebook.set_visible(false);
|
||||
|
||||
@ -229,11 +273,14 @@ impl PerformanceFrame {
|
||||
self.mode_menu_button.set_sensitive(enable_mode_control);
|
||||
self.mode_menu_button.set_hexpand(enable_mode_control);
|
||||
|
||||
let values_changed_callback = self.values_changed_callback.borrow();
|
||||
|
||||
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);
|
||||
let active_index = row.index() as u16;
|
||||
if let Some(active_profile) = table.modes.get(&active_index) {
|
||||
self.mode_menu_button.set_label(&active_profile.name);
|
||||
|
||||
self.power_mode_info_notebook.set_visible(true);
|
||||
|
||||
@ -244,8 +291,8 @@ impl PerformanceFrame {
|
||||
self.power_mode_info_notebook.remove_page(None);
|
||||
}
|
||||
|
||||
for component in &profile.components {
|
||||
let values_grid = PowerProfileComponentGrid::new();
|
||||
for (i, component) in active_profile.components.iter().enumerate() {
|
||||
let values_grid = PowerProfileHeuristicsGrid::new();
|
||||
values_grid.set_component(component, table);
|
||||
|
||||
let title = component.clock_type.as_deref().unwrap_or("All");
|
||||
@ -256,10 +303,45 @@ impl PerformanceFrame {
|
||||
.build();
|
||||
self.power_mode_info_notebook
|
||||
.append_page(&values_grid, Some(&title_label));
|
||||
|
||||
if let Some(f) = &*values_changed_callback {
|
||||
values_grid.connect_component_values_changed(clone!(
|
||||
#[strong]
|
||||
f,
|
||||
#[strong(rename_to = modes_table)]
|
||||
self.modes_table,
|
||||
#[strong]
|
||||
values_grid,
|
||||
move || {
|
||||
let mut modes_table = modes_table.borrow_mut();
|
||||
if let Some(current_table) = &mut *modes_table {
|
||||
let changed_component =
|
||||
values_grid.imp().component.borrow().clone();
|
||||
current_table
|
||||
.modes
|
||||
.get_mut(&active_index)
|
||||
.unwrap()
|
||||
.components[i] = changed_component;
|
||||
}
|
||||
|
||||
f();
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
self.power_mode_info_notebook
|
||||
.set_show_tabs(profile.components.len() > 1);
|
||||
.set_show_tabs(active_profile.components.len() > 1);
|
||||
|
||||
let is_custom = active_profile.is_custom();
|
||||
for page in self
|
||||
.power_mode_info_notebook
|
||||
.pages()
|
||||
.iter::<NotebookPage>()
|
||||
.flatten()
|
||||
{
|
||||
page.child().set_sensitive(is_custom);
|
||||
}
|
||||
|
||||
// Restore selected page
|
||||
if current_page.is_some() {
|
||||
|
@ -1 +1 @@
|
||||
pub mod power_profile_component_grid;
|
||||
pub mod power_profile_heuristics_grid;
|
||||
|
@ -1,90 +0,0 @@
|
||||
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,166 @@
|
||||
use amdgpu_sysfs::gpu_handle::power_profile_mode::{PowerProfileComponent, PowerProfileModesTable};
|
||||
use gtk::{
|
||||
glib::{self, clone, Object},
|
||||
prelude::{AdjustmentExt, BoxExt, CheckButtonExt, GridExt, WidgetExt},
|
||||
subclass::prelude::ObjectSubclassIsExt,
|
||||
Adjustment, CheckButton, Label, SpinButton,
|
||||
};
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct PowerProfileHeuristicsGrid(ObjectSubclass<imp::PowerProfileHeuristicsGrid>)
|
||||
@extends gtk::Grid,
|
||||
@implements gtk::Accessible, gtk::Buildable, gtk::Widget;
|
||||
}
|
||||
|
||||
impl PowerProfileHeuristicsGrid {
|
||||
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 connect_component_values_changed<F: Fn() + 'static + Clone>(&self, f: F) {
|
||||
for (adj, toggle_button) in self.imp().adjustments.borrow().iter() {
|
||||
adj.connect_value_changed(clone!(
|
||||
#[strong]
|
||||
f,
|
||||
move |_| f()
|
||||
));
|
||||
toggle_button.connect_toggled(clone!(
|
||||
#[strong]
|
||||
f,
|
||||
move |_| f()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_component(&self, component: &PowerProfileComponent, table: &PowerProfileModesTable) {
|
||||
self.imp().component.replace(component.clone());
|
||||
|
||||
while let Some(child) = self.first_child() {
|
||||
self.remove(&child);
|
||||
}
|
||||
|
||||
let mut adjustments = Vec::with_capacity(component.values.len());
|
||||
|
||||
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 value_box = gtk::Box::new(gtk::Orientation::Horizontal, 5);
|
||||
value_box.set_hexpand(true);
|
||||
|
||||
let value_checkbutton = CheckButton::new();
|
||||
|
||||
let adj = Adjustment::new(0.0, f64::MIN, f64::MAX, 1.0, 1.0, 1.0);
|
||||
let value_spinbutton = SpinButton::new(Some(&adj), 1.0, 0);
|
||||
|
||||
value_checkbutton.connect_toggled(clone!(
|
||||
#[strong]
|
||||
value_spinbutton,
|
||||
#[strong(rename_to = this)]
|
||||
self,
|
||||
move |check| {
|
||||
this.imp().update_values(&value_spinbutton, check, i);
|
||||
}
|
||||
));
|
||||
value_spinbutton.connect_value_changed(clone!(
|
||||
#[strong]
|
||||
value_checkbutton,
|
||||
#[strong(rename_to = this)]
|
||||
self,
|
||||
move |spin_button| {
|
||||
this.imp().update_values(spin_button, &value_checkbutton, i);
|
||||
}
|
||||
));
|
||||
|
||||
value_box.append(&value_checkbutton);
|
||||
value_box.append(&value_spinbutton);
|
||||
|
||||
value_checkbutton.set_active(value.is_some());
|
||||
if let Some(value) = value {
|
||||
adj.set_value(*value as f64);
|
||||
}
|
||||
|
||||
self.imp()
|
||||
.update_values(&value_spinbutton, &value_checkbutton, i);
|
||||
|
||||
self.attach(&value_box, 1, i as i32, 1, 1);
|
||||
|
||||
adjustments.push((adj, value_checkbutton));
|
||||
}
|
||||
|
||||
*self.imp().adjustments.borrow_mut() = adjustments;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PowerProfileHeuristicsGrid {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use amdgpu_sysfs::gpu_handle::power_profile_mode::PowerProfileComponent;
|
||||
use gtk::{
|
||||
glib::{self, subclass::InitializingObject},
|
||||
prelude::{CheckButtonExt, WidgetExt},
|
||||
subclass::{
|
||||
prelude::*,
|
||||
widget::{CompositeTemplateClass, WidgetImpl},
|
||||
},
|
||||
Adjustment, CheckButton, CompositeTemplate, SpinButton,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
#[derive(CompositeTemplate, Default)]
|
||||
#[template(file = "ui/oc_page/power_profile/power_profile_heuristics_grid.blp")]
|
||||
pub struct PowerProfileHeuristicsGrid {
|
||||
pub component: Rc<RefCell<PowerProfileComponent>>,
|
||||
pub(super) adjustments: Rc<RefCell<Vec<(Adjustment, CheckButton)>>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for PowerProfileHeuristicsGrid {
|
||||
const NAME: &'static str = "PowerProfileHeuristicsGrid";
|
||||
type Type = super::PowerProfileHeuristicsGrid;
|
||||
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 PowerProfileHeuristicsGrid {}
|
||||
|
||||
impl WidgetImpl for PowerProfileHeuristicsGrid {}
|
||||
impl GridImpl for PowerProfileHeuristicsGrid {}
|
||||
|
||||
impl PowerProfileHeuristicsGrid {
|
||||
pub fn update_values(&self, spin_button: &SpinButton, check: &CheckButton, i: usize) {
|
||||
let mut component = self.component.borrow_mut();
|
||||
|
||||
spin_button.set_sensitive(check.is_active());
|
||||
|
||||
if check.is_active() {
|
||||
component.values[i] = Some(spin_button.value() as i32);
|
||||
} else {
|
||||
component.values[i] = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using Gtk 4.0;
|
||||
|
||||
template $PowerProfileComponentGrid: Grid {
|
||||
template $PowerProfileHeuristicsGrid: Grid {
|
||||
row-spacing: 5;
|
||||
column-spacing: 5;
|
||||
}
|
@ -43,6 +43,8 @@ pub enum Request<'a> {
|
||||
SetPowerProfileMode {
|
||||
id: &'a str,
|
||||
index: Option<u16>,
|
||||
#[serde(default)]
|
||||
custom_heuristics: Vec<Vec<Option<i32>>>,
|
||||
},
|
||||
GetPowerStates {
|
||||
id: &'a str,
|
||||
|
Loading…
Reference in New Issue
Block a user