From 8867749e0e68deb63c5354e60473b381bb3d7072 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Sun, 25 Jan 2026 03:48:17 +0100 Subject: [PATCH] feat: improve GPU selection --- rog-control-center/src/ui/mod.rs | 6 +- rog-control-center/src/ui/setup_gpu.rs | 182 ++++++++++++++++++++++++ rog-control-center/ui/main_window.slint | 4 +- rog-control-center/ui/pages/gpu.slint | 38 +++-- 4 files changed, 215 insertions(+), 15 deletions(-) create mode 100644 rog-control-center/src/ui/setup_gpu.rs diff --git a/rog-control-center/src/ui/mod.rs b/rog-control-center/src/ui/mod.rs index 417c25ed..27e108c1 100644 --- a/rog-control-center/src/ui/mod.rs +++ b/rog-control-center/src/ui/mod.rs @@ -1,6 +1,7 @@ pub mod setup_anime; pub mod setup_aura; pub mod setup_fans; +pub mod setup_gpu; pub mod setup_system; use std::sync::atomic::{AtomicU64, Ordering}; @@ -162,9 +163,12 @@ pub fn setup_window( setup_anime_page(&ui, config.clone()); } if available.contains(&"xyz.ljones.FanCurves".to_string()) { - setup_fan_curve_page(&ui, config); + setup_fan_curve_page(&ui, config.clone()); } + // Populate GPU page choices and callbacks + setup_gpu::setup_gpu_page(&ui); + ui } diff --git a/rog-control-center/src/ui/setup_gpu.rs b/rog-control-center/src/ui/setup_gpu.rs new file mode 100644 index 00000000..36c7339c --- /dev/null +++ b/rog-control-center/src/ui/setup_gpu.rs @@ -0,0 +1,182 @@ +use log::error; +use rog_platform::asus_armoury::{AttrValue, FirmwareAttributes}; +use slint::{ComponentHandle, ModelRc, SharedString}; + +use crate::{GPUPageData, MainWindow}; + +// Populate GPU page choices and wire the `cb_set_gpu_mode` callback +pub fn setup_gpu_page(ui: &MainWindow) { + let handle = ui.as_weak(); + + tokio::spawn(async move { + // Read available attributes + let attrs = FirmwareAttributes::new(); + let gpu_mux_available = attrs + .gpu_mux_mode() + .map(|a| a.base_path_exists()) + .unwrap_or(false); + + // Prepare choice strings + let mut choices: Vec = Vec::new(); + choices.push(SharedString::from("Integrated")); + if gpu_mux_available { + choices.push(SharedString::from("Ultimate")); + } + choices.push(SharedString::from("Hybrid")); + + // Read current attribute values to initialise UI state + let current_dgpu = attrs + .dgpu_disable() + .and_then(|a| a.current_value().ok()) + .unwrap_or(AttrValue::Integer(0)); + let current_mux = attrs + .gpu_mux_mode() + .and_then(|a| a.current_value().ok()) + .unwrap_or(AttrValue::Integer(1)); + + // Convert to UI-able values + let dgpu_disabled = matches!(current_dgpu, AttrValue::Integer(v) if v == 1); + // Determine initial index for gpu_mux_mode property + let initial_index: i32 = if gpu_mux_available { + // If mux attr says 0 -> Ultimate, else try dgpu to refine + match current_mux { + AttrValue::Integer(0) => 1, // Ultimate + _ => { + match current_dgpu { + AttrValue::Integer(1) => 0, // Integrated + _ => 2, // Hybrid/Optimus fallback + } + } + } + } else { + // Only Integrated / Hybrid + match current_dgpu { + AttrValue::Integer(1) => 0, + _ => 1, + } + }; + + let handle_copy = handle.clone(); + if let Err(e) = handle.upgrade_in_event_loop(move |handle| { + let global = handle.global::(); + + // set choices model + let model: ModelRc = choices.as_slice().into(); + global.set_gpu_modes_choises(model); + global.set_gpu_mux_available(gpu_mux_available); + + // set initial state + global.set_dgpu_disabled(if dgpu_disabled { 1 } else { 0 }); + global.set_gpu_mux_mode(initial_index); + + // register callback + let handle_cb = handle_copy.clone(); + global.on_cb_set_gpu_mode(move |index: i32| { + // show a blue toast informing user a reboot is required (auto-clears) + let toast_handle = handle_cb.clone(); + crate::ui::show_toast( + SharedString::from( + "GPU mode change scheduled — reboot required for changes to apply.", + ), + SharedString::from("Failed to set GPU mode"), + toast_handle.clone(), + Ok(()), + ); + + let handle_next = handle_cb.clone(); + tokio::spawn(async move { + let attrs = FirmwareAttributes::new(); + let mux_avail = attrs + .gpu_mux_mode() + .map(|a| a.base_path_exists()) + .unwrap_or(false); + + // helper to set attribute ignoring errors + if mux_avail { + match index { + 0 => { + // Integrated + if let Some(attr) = attrs.dgpu_disable() { + let _ = attr.set_current_value(&AttrValue::Integer(1)); + } + if let Some(attr) = attrs.gpu_mux_mode() { + let _ = attr.set_current_value(&AttrValue::Integer(1)); + } + } + 1 => { + // Ultimate + if let Some(attr) = attrs.dgpu_disable() { + let _ = attr.set_current_value(&AttrValue::Integer(0)); + } + if let Some(attr) = attrs.gpu_mux_mode() { + let _ = attr.set_current_value(&AttrValue::Integer(0)); + } + } + 2 => { + // Dynamic + if let Some(attr) = attrs.dgpu_disable() { + let _ = attr.set_current_value(&AttrValue::Integer(0)); + } + if let Some(attr) = attrs.gpu_mux_mode() { + let _ = attr.set_current_value(&AttrValue::Integer(1)); + } + } + _ => {} + } + } else { + match index { + 0 => { + if let Some(attr) = attrs.dgpu_disable() { + let _ = attr.set_current_value(&AttrValue::Integer(1)); + } + } + 1 => { + if let Some(attr) = attrs.dgpu_disable() { + let _ = attr.set_current_value(&AttrValue::Integer(0)); + } + } + _ => {} + } + }; + + // After attempting write(s), refresh UI from attributes + let attrs2 = FirmwareAttributes::new(); + let cur_dgpu = attrs2 + .dgpu_disable() + .and_then(|a| a.current_value().ok()) + .unwrap_or(AttrValue::Integer(0)); + let cur_mux = attrs2 + .gpu_mux_mode() + .and_then(|a| a.current_value().ok()) + .unwrap_or(AttrValue::Integer(1)); + + let dgpu_disabled = matches!(cur_dgpu, AttrValue::Integer(v) if v == 1); + let new_index: i32 = if mux_avail { + match cur_mux { + AttrValue::Integer(0) => 1, + _ => match cur_dgpu { + AttrValue::Integer(1) => 0, + _ => 2, + }, + } + } else { + match cur_dgpu { + AttrValue::Integer(1) => 0, + _ => 1, + } + }; + + if let Err(e) = handle_next.upgrade_in_event_loop(move |h| { + let g = h.global::(); + g.set_dgpu_disabled(if dgpu_disabled { 1 } else { 0 }); + g.set_gpu_mux_mode(new_index); + }) { + error!("setup_gpu callback: upgrade_in_event_loop: {e:?}"); + } + }); + }); + }) { + error!("setup_gpu_page: upgrade_in_event_loop: {e:?}"); + } + }); +} diff --git a/rog-control-center/ui/main_window.slint b/rog-control-center/ui/main_window.slint index 7549d495..e476448b 100644 --- a/rog-control-center/ui/main_window.slint +++ b/rog-control-center/ui/main_window.slint @@ -7,7 +7,7 @@ import { PageFans } from "pages/fans.slint"; import { PageAnime, AnimePageData } from "pages/anime.slint"; import { RogItem } from "widgets/common.slint"; import { PageAura } from "pages/aura.slint"; -import { PageGPU } from "pages/gpu.slint"; +import { PageGPU, GPUPageData } from "pages/gpu.slint"; import { Node } from "widgets/graph.slint"; export { Node } import { FanPageData, FanType, Profile } from "types/fan_types.slint"; @@ -16,7 +16,7 @@ import { AuraPageData, AuraDevType, LaptopAuraPower, AuraPowerState, PowerZones, export { AuraPageData, AuraDevType, LaptopAuraPower, AuraPowerState, PowerZones, AuraEffect } import { PageAppSettings, AppSettingsPageData } from "pages/app_settings.slint"; -export { AppSize, AttrMinMax, SystemPageData, AnimePageData, AppSettingsPageData } +export { AppSize, AttrMinMax, SystemPageData, AnimePageData, AppSettingsPageData, GPUPageData } export component MainWindow inherits Window { title: "ROG Control"; diff --git a/rog-control-center/ui/pages/gpu.slint b/rog-control-center/ui/pages/gpu.slint index 16dd967f..5b846b86 100644 --- a/rog-control-center/ui/pages/gpu.slint +++ b/rog-control-center/ui/pages/gpu.slint @@ -8,10 +8,9 @@ export global GPUPageData { in-out property gpu_mux_mode: 1; // 0 = Ultra/Discreet, 1 = Integrated/Optimus in-out property dgpu_disabled: 0; // 1 == dGPU disabled in-out property egpu_enabled: 0; // 1 == eGPU (XGMobile) enabled - in-out property <[string]> gpu_modes_choises: [@tr("Ultra"), @tr("Integrated")]; - callback cb_gpu_mux_mode(int); - callback cb_dgpu_disabled(int); - callback cb_egpu_enabled(int); + in-out property <[string]> gpu_modes_choises: [@tr("Integrated"), @tr("Hybrid")]; + in-out property gpu_mux_available: false; + callback cb_set_gpu_mode(int); } export component PageGPU inherits Rectangle { @@ -21,14 +20,29 @@ export component PageGPU inherits Rectangle { VerticalLayout { padding: 10px; spacing: 10px; - SystemDropdown { - text: @tr("GPU mode"); - current_index <=> GPUPageData.gpu_mux_mode; - current_value: GPUPageData.gpu_modes_choises[GPUPageData.gpu_mux_mode]; - model <=> GPUPageData.gpu_modes_choises; - selected => { - GPUPageData.cb_gpu_mux_mode(0); - GPUPageData.cb_gpu_mux_mode(1); + + HorizontalLayout { + padding-right: 10px; + padding-left: 10px; + alignment: LayoutAlignment.space-between; + Rectangle { + height: 32px; + Text { + font-size: 16px; + text: @tr("Note: Changes take effect after a reboot. The dropdown always shows the current mode."); + } + } + } + + HorizontalLayout { + SystemDropdown { + text: @tr("GPU mode"); + model <=> GPUPageData.gpu_modes_choises; + current_index <=> GPUPageData.gpu_mux_mode; + current_value: GPUPageData.gpu_modes_choises[GPUPageData.gpu_mux_mode]; + selected => { + GPUPageData.cb_set_gpu_mode(GPUPageData.gpu_mux_mode) + } } } }