mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
More updating to zbus 4.0.1
This commit is contained in:
@@ -1,178 +0,0 @@
|
||||
use std::f64::consts::PI;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use egui::{Button, RichText};
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_platform::platform::Properties;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::Result;
|
||||
use crate::system_state::SystemState;
|
||||
use crate::{Page, RogDbusClientBlocking};
|
||||
|
||||
pub struct RogApp {
|
||||
pub page: Page,
|
||||
pub states: Arc<Mutex<SystemState>>,
|
||||
// TODO: can probably just open and read whenever
|
||||
pub config: Config,
|
||||
/// Oscillator in percentage
|
||||
pub oscillator1: Arc<AtomicU8>,
|
||||
pub oscillator2: Arc<AtomicU8>,
|
||||
pub oscillator3: Arc<AtomicU8>,
|
||||
/// Frequency of oscillation
|
||||
pub oscillator_freq: Arc<AtomicU8>,
|
||||
/// A toggle that toggles true/false when the oscillator reaches 0
|
||||
pub oscillator_toggle: Arc<AtomicBool>,
|
||||
pub supported_interfaces: Vec<String>,
|
||||
pub supported_properties: Vec<Properties>,
|
||||
}
|
||||
|
||||
impl RogApp {
|
||||
/// Called once before the first frame.
|
||||
pub fn new(
|
||||
config: Config,
|
||||
states: Arc<Mutex<SystemState>>,
|
||||
_cc: &eframe::CreationContext<'_>,
|
||||
) -> Result<Self> {
|
||||
let (dbus, _) = RogDbusClientBlocking::new()?;
|
||||
let supported_interfaces = dbus.proxies().platform().supported_interfaces()?;
|
||||
let supported_properties = dbus.proxies().platform().supported_properties()?;
|
||||
|
||||
// Set up an oscillator to run on a thread.
|
||||
// Helpful for visual effects like colour pulse.
|
||||
let oscillator1 = Arc::new(AtomicU8::new(0));
|
||||
let oscillator2 = Arc::new(AtomicU8::new(0));
|
||||
let oscillator3 = Arc::new(AtomicU8::new(0));
|
||||
|
||||
let oscillator1_1 = oscillator1.clone();
|
||||
let oscillator1_2 = oscillator2.clone();
|
||||
let oscillator1_3 = oscillator3.clone();
|
||||
|
||||
let oscillator_freq = Arc::new(AtomicU8::new(5));
|
||||
let oscillator_freq1 = oscillator_freq.clone();
|
||||
let oscillator_toggle = Arc::new(AtomicBool::new(false));
|
||||
let oscillator_toggle1 = oscillator_toggle.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let started = Instant::now();
|
||||
let mut toggled = false;
|
||||
loop {
|
||||
let time = started.elapsed();
|
||||
// 32 = slow, 16 = med, 8 = fast
|
||||
let scale = oscillator_freq1.load(Ordering::SeqCst) as f64;
|
||||
let elapsed1 = (time.as_millis() as f64 + 333.0) / 10000.0;
|
||||
let elapsed2 = (time.as_millis() as f64 + 666.0) / 10000.0;
|
||||
let elapsed3 = (time.as_millis() as f64 + 999.0) / 10000.0;
|
||||
let tmp1 = ((scale * elapsed1 * PI).cos()).abs();
|
||||
let tmp2 = ((scale * 0.85 * elapsed2 * PI).cos()).abs();
|
||||
let tmp3 = ((scale * 0.7 * elapsed3 * PI).cos()).abs();
|
||||
if tmp1 <= 0.1 && !toggled {
|
||||
let s = oscillator_toggle1.load(Ordering::SeqCst);
|
||||
oscillator_toggle1.store(!s, Ordering::SeqCst);
|
||||
toggled = true;
|
||||
} else if tmp1 > 0.9 {
|
||||
toggled = false;
|
||||
}
|
||||
|
||||
let tmp1 = (255.0 * tmp1 * 100.0 / 255.0) as u8;
|
||||
let tmp2 = (255.0 * tmp2 * 100.0 / 255.0) as u8;
|
||||
let tmp3 = (255.0 * tmp3 * 100.0 / 255.0) as u8;
|
||||
|
||||
oscillator1_1.store(tmp1, Ordering::SeqCst);
|
||||
oscillator1_2.store(tmp2, Ordering::SeqCst);
|
||||
oscillator1_3.store(tmp3, Ordering::SeqCst);
|
||||
|
||||
std::thread::sleep(Duration::from_millis(33));
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
supported_interfaces,
|
||||
supported_properties,
|
||||
states,
|
||||
page: Page::System,
|
||||
config,
|
||||
oscillator1,
|
||||
oscillator2,
|
||||
oscillator3,
|
||||
oscillator_toggle,
|
||||
oscillator_freq,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for RogApp {
|
||||
/// Called each time the UI needs repainting, which may be many times per
|
||||
/// second. Put your widgets into a `SidePanel`, `TopPanel`,
|
||||
/// `CentralPanel`, `Window` or `Area`.
|
||||
fn update(&mut self, ctx: &eframe::egui::Context, frame: &mut eframe::Frame) {
|
||||
let states = self.states.clone();
|
||||
|
||||
if let Ok(mut states) = states.try_lock() {
|
||||
if states.app_should_update {
|
||||
states.app_should_update = false;
|
||||
ctx.request_repaint();
|
||||
}
|
||||
}
|
||||
|
||||
// Shortcut typical display stuff
|
||||
if let Ok(mut states) = states.try_lock() {
|
||||
let layout_testing = states.aura_creation.layout_testing.clone();
|
||||
if let Some(path) = &layout_testing {
|
||||
let modified = path.metadata().unwrap().modified().unwrap();
|
||||
if states.aura_creation.layout_last_modified < modified {
|
||||
states.aura_creation.layout_last_modified = modified;
|
||||
// time to reload the config
|
||||
states.aura_creation.keyboard_layout = KeyLayout::from_file(path).unwrap();
|
||||
}
|
||||
self.aura_page(&mut states, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
self.top_bar(ctx, frame);
|
||||
self.side_panel(ctx);
|
||||
}
|
||||
let page = self.page;
|
||||
|
||||
let mut was_error = false;
|
||||
|
||||
if let Ok(mut states) = states.try_lock() {
|
||||
if let Some(err) = states.error.clone() {
|
||||
was_error = true;
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading(RichText::new("Error!").size(28.0));
|
||||
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.label(RichText::new(format!("The error was: {:?}", err)).size(22.0));
|
||||
});
|
||||
});
|
||||
egui::TopBottomPanel::bottom("error_bar")
|
||||
.default_height(26.0)
|
||||
.show(ctx, |ui| {
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
|
||||
if ui
|
||||
.add(Button::new(RichText::new("Okay").size(20.0)))
|
||||
.clicked()
|
||||
{
|
||||
states.error = None;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !was_error {
|
||||
if let Ok(mut states) = states.try_lock() {
|
||||
match page {
|
||||
Page::AppSettings => self.app_settings_page(&mut states, ctx),
|
||||
Page::System => self.system_page(&mut states, ctx),
|
||||
Page::AuraEffects => self.aura_page(&mut states, ctx),
|
||||
Page::AnimeMatrix => self.anime_page(ctx),
|
||||
Page::FanCurves => self.fan_curve_page(&mut states, ctx),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ pub enum Error {
|
||||
XdgVars,
|
||||
Zbus(zbus::Error),
|
||||
Notification(notify_rust::error::Error),
|
||||
Eframe(eframe::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@@ -25,7 +24,6 @@ impl fmt::Display for Error {
|
||||
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
|
||||
Error::Zbus(err) => write!(f, "Error: {}", err),
|
||||
Error::Notification(err) => write!(f, "Notification Error: {}", err),
|
||||
Error::Eframe(err) => write!(f, "Eframe Error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,9 +53,3 @@ impl From<notify_rust::error::Error> for Error {
|
||||
Error::Notification(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<eframe::Error> for Error {
|
||||
fn from(err: eframe::Error) -> Self {
|
||||
Error::Eframe(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
pub mod app;
|
||||
// These lints need to be allowed due to the generated sources
|
||||
#![allow(clippy::redundant_clone, clippy::cmp_owned)]
|
||||
slint::include_modules!();
|
||||
|
||||
// Intentionally reexport slint so that GUI consumers don't need to add to
|
||||
// `Cargo.toml`
|
||||
use std::fs::{remove_dir_all, File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
use std::process::exit;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
pub use app::RogApp;
|
||||
pub use slint;
|
||||
|
||||
pub mod cli_options;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
#[cfg(feature = "mocking")]
|
||||
pub mod mocking;
|
||||
pub mod pages;
|
||||
pub mod startup_error;
|
||||
pub mod system_state;
|
||||
pub mod tray;
|
||||
pub mod update_and_notify;
|
||||
pub mod widgets;
|
||||
|
||||
#[cfg(feature = "mocking")]
|
||||
pub use mocking::RogDbusClientBlocking;
|
||||
@@ -44,6 +46,7 @@ pub fn print_versions() {
|
||||
|
||||
pub const SHOWING_GUI: u8 = 1;
|
||||
pub const SHOW_GUI: u8 = 2;
|
||||
pub const QUIT_APP: u8 = 3;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Page {
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
use std::borrow::BorrowMut;
|
||||
use std::env::args;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::thread::{self, sleep, spawn};
|
||||
use std::time::Duration;
|
||||
|
||||
use eframe::HardwareAcceleration;
|
||||
use gumdrop::Options;
|
||||
use log::{debug, error, warn, LevelFilter};
|
||||
use log::{warn, LevelFilter};
|
||||
use rog_aura::aura_detection::{LaptopLedData, LedSupportFile};
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_control_center::cli_options::CliStart;
|
||||
use rog_control_center::config::Config;
|
||||
use rog_control_center::error::Result;
|
||||
use rog_control_center::startup_error::AppErrorShow;
|
||||
use rog_control_center::slint::ComponentHandle;
|
||||
use rog_control_center::system_state::SystemState;
|
||||
use rog_control_center::tray::init_tray;
|
||||
use rog_control_center::update_and_notify::{start_notifications, EnabledNotifications};
|
||||
use rog_control_center::{
|
||||
get_ipc_file, on_tmp_dir_exists, print_versions, RogApp, RogDbusClientBlocking, SHOWING_GUI,
|
||||
SHOW_GUI,
|
||||
get_ipc_file, on_tmp_dir_exists, print_versions, MainWindow, RogDbusClientBlocking, QUIT_APP,
|
||||
SHOWING_GUI, SHOW_GUI,
|
||||
};
|
||||
#[cfg(not(feature = "mocking"))]
|
||||
use supergfxctl::zbus_proxy::DaemonProxyBlocking as GfxProxyBlocking;
|
||||
use tokio::runtime::Runtime;
|
||||
// use winit::monitor::VideoMode;
|
||||
// use winit::window::{Fullscreen, WindowLevel};
|
||||
|
||||
#[cfg(not(feature = "mocking"))]
|
||||
const DATA_DIR: &str = "/usr/share/rog-gui/";
|
||||
@@ -61,44 +60,22 @@ fn main() -> Result<()> {
|
||||
// Enter the runtime so that `tokio::spawn` is available immediately.
|
||||
let _enter = rt.enter();
|
||||
|
||||
let native_options = eframe::NativeOptions {
|
||||
vsync: true,
|
||||
hardware_acceleration: HardwareAcceleration::Preferred,
|
||||
min_window_size: Some(egui::vec2(980.0, 670.0)),
|
||||
max_window_size: Some(egui::vec2(980.0, 670.0)),
|
||||
run_and_return: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let (dbus, _) = RogDbusClientBlocking::new()
|
||||
.map_err(|e| {
|
||||
eframe::run_native(
|
||||
"ROG Control Center",
|
||||
native_options.clone(),
|
||||
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
|
||||
)
|
||||
.map_err(|e| error!("{e}"))
|
||||
.ok();
|
||||
.map_err(|_| {
|
||||
// TODO: show an error window
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let supported_properties = match dbus.proxies().platform().supported_properties() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eframe::run_native(
|
||||
"ROG Control Center",
|
||||
native_options.clone(),
|
||||
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
|
||||
)
|
||||
.map_err(|e| error!("{e}"))
|
||||
.ok();
|
||||
vec![]
|
||||
Err(_e) => {
|
||||
// TODO: show an error window
|
||||
Vec::default()
|
||||
}
|
||||
};
|
||||
|
||||
// Startup
|
||||
let mut config = Config::load()?;
|
||||
let running_in_bg = Arc::new(AtomicBool::new(config.startup_in_background));
|
||||
|
||||
if config.startup_in_background {
|
||||
config.run_in_background = true;
|
||||
@@ -179,6 +156,7 @@ fn main() -> Result<()> {
|
||||
Ok(tmp) => tmp,
|
||||
Err(_) => on_tmp_dir_exists().unwrap(),
|
||||
};
|
||||
dbg!();
|
||||
|
||||
let states = setup_page_state_and_notifs(
|
||||
layout_name,
|
||||
@@ -188,77 +166,107 @@ fn main() -> Result<()> {
|
||||
&config,
|
||||
)?;
|
||||
|
||||
if cli_parsed.board_name.is_some() || cli_parsed.layout_viewing {
|
||||
if let Ok(mut lock) = states.lock() {
|
||||
lock.run_in_bg = false;
|
||||
running_in_bg.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
if config.enable_tray_icon {
|
||||
init_tray(supported_properties, states.clone());
|
||||
}
|
||||
dbg!();
|
||||
|
||||
if let Ok(mut states) = states.lock() {
|
||||
// For some reason the gui is causing a broke pipe error on dbus send, so
|
||||
// replace it.
|
||||
let (asus_dbus, conn) =
|
||||
rog_dbus::RogDbusClientBlocking::new().expect("Couldn't connect to asusd");
|
||||
states.asus_dbus = asus_dbus;
|
||||
let gfx_dbus = GfxProxyBlocking::new(&conn).expect("Couldn't connect to supergfxd");
|
||||
states.gfx_dbus = gfx_dbus;
|
||||
}
|
||||
thread_local! { pub static UI: std::cell::RefCell<Option<MainWindow>> = Default::default()};
|
||||
i_slint_backend_selector::with_platform(|_| Ok(())).unwrap();
|
||||
|
||||
let mut bg_check_spawned = false;
|
||||
loop {
|
||||
if !running_in_bg.load(Ordering::Relaxed) {
|
||||
// blocks until window is closed
|
||||
let states = states.clone();
|
||||
let mut ipc_file = get_ipc_file()?;
|
||||
ipc_file.write_all(&[SHOWING_GUI])?;
|
||||
eframe::run_native(
|
||||
"ROG Control Center",
|
||||
native_options.clone(),
|
||||
Box::new(move |cc| {
|
||||
let cfg = Config::load().unwrap();
|
||||
let app = RogApp::new(cfg, states, cc);
|
||||
Box::new(app.unwrap())
|
||||
}),
|
||||
)?;
|
||||
dbg!();
|
||||
thread::spawn(move || {
|
||||
let mut buf = [0u8; 2];
|
||||
// blocks until it is read, typically the read will happen after a second
|
||||
// process writes to the IPC (so there is data to actually read)
|
||||
loop {
|
||||
get_ipc_file().unwrap().read_exact(&mut buf).unwrap();
|
||||
if buf[0] == SHOW_GUI {
|
||||
println!("Should show window {buf:?}");
|
||||
let mut ipc_file = get_ipc_file().unwrap();
|
||||
ipc_file.write_all(&[SHOWING_GUI, 0]).unwrap();
|
||||
|
||||
running_in_bg.store(true, Ordering::SeqCst);
|
||||
bg_check_spawned = false;
|
||||
}
|
||||
|
||||
if let Ok(lock) = states.try_lock() {
|
||||
if !lock.run_in_bg {
|
||||
break;
|
||||
}
|
||||
|
||||
if lock.run_in_bg && running_in_bg.load(Ordering::Acquire) && !bg_check_spawned {
|
||||
let running_in_bg = running_in_bg.clone();
|
||||
thread::spawn(move || {
|
||||
let mut buf = [0u8; 4];
|
||||
// blocks until it is read, typically the read will happen after a second
|
||||
// process writes to the IPC (so there is data to actually read)
|
||||
loop {
|
||||
if get_ipc_file().unwrap().read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
|
||||
running_in_bg.store(false, Ordering::Release);
|
||||
debug!("Wait thread got from tray {buf:#?}");
|
||||
break;
|
||||
let states = states.clone();
|
||||
i_slint_core::api::invoke_from_event_loop(move || {
|
||||
UI.with(|ui| {
|
||||
let mut ui = ui.borrow_mut();
|
||||
if let Some(ui) = ui.as_mut() {
|
||||
ui.window().show().unwrap();
|
||||
ui.window().on_close_requested(|| {
|
||||
let mut ipc_file = get_ipc_file().unwrap();
|
||||
ipc_file.write_all(&[0, 0]).unwrap();
|
||||
slint::CloseRequestResponse::HideWindow
|
||||
});
|
||||
} else {
|
||||
let newui = setup_window(states.clone());
|
||||
newui.window().show().unwrap();
|
||||
println!("New window should be showing now"); // but it isn't
|
||||
newui.window().on_close_requested(|| {
|
||||
let mut ipc_file = get_ipc_file().unwrap();
|
||||
ipc_file.write_all(&[0, 0]).unwrap();
|
||||
slint::CloseRequestResponse::HideWindow
|
||||
});
|
||||
ui.replace(newui);
|
||||
}
|
||||
}
|
||||
});
|
||||
bg_check_spawned = true;
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
} else if buf[1] == QUIT_APP {
|
||||
slint::quit_event_loop().unwrap();
|
||||
} else if buf[0] != SHOWING_GUI {
|
||||
println!("Should hide window {buf:?}");
|
||||
i_slint_core::api::invoke_from_event_loop(move || {
|
||||
UI.with(|ui| {
|
||||
let mut ui = ui.take();
|
||||
if let Some(ui) = ui.borrow_mut() {
|
||||
ui.window().hide().unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent hogging CPU
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
}
|
||||
slint::run_event_loop_until_quit().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_window(_states: Arc<Mutex<SystemState>>) -> MainWindow {
|
||||
// slint::platform::set_platform(Box::new(i_slint_backend_winit::Backend::new().
|
||||
// unwrap())).unwrap();
|
||||
let ui = MainWindow::new().unwrap();
|
||||
// Example of how to do work in another thread.
|
||||
// The thread itself can keep its own state, and then update vars in the UI
|
||||
// when required.
|
||||
let ui_handle = ui.as_weak();
|
||||
spawn(move || loop {
|
||||
sleep(Duration::from_secs(1));
|
||||
// This is where the actual update happens
|
||||
ui_handle
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
// handle.set_counter(handle.get_counter() + 1);
|
||||
use i_slint_backend_winit::WinitWindowAccessor;
|
||||
handle
|
||||
.window()
|
||||
.with_winit_window(|winit_window: &winit::window::Window| {
|
||||
// winit_window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||
if !winit_window.has_focus() {
|
||||
// slint::quit_event_loop().unwrap();
|
||||
// handle.hide().unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
|
||||
ui.on_exit_app(move || {
|
||||
slint::quit_event_loop().unwrap();
|
||||
});
|
||||
|
||||
ui
|
||||
}
|
||||
|
||||
fn setup_page_state_and_notifs(
|
||||
layout_testing: Option<PathBuf>,
|
||||
keyboard_layout: KeyLayout,
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
use crate::RogApp;
|
||||
|
||||
impl RogApp {
|
||||
pub fn anime_page(&mut self, ctx: &eframe::egui::Context) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.label("In progress");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
use crate::system_state::SystemState;
|
||||
use crate::widgets::app_settings;
|
||||
use crate::RogApp;
|
||||
|
||||
impl RogApp {
|
||||
pub fn app_settings_page(&mut self, states: &mut SystemState, ctx: &eframe::egui::Context) {
|
||||
let Self { config, .. } = self;
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.spacing_mut().item_spacing = egui::vec2(8.0, 10.0);
|
||||
let rect = ui.available_rect_before_wrap();
|
||||
egui::Grid::new("grid_of_bits")
|
||||
.min_col_width(rect.width() / 2.0)
|
||||
.show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
app_settings(config, states, ui);
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use egui::Color32;
|
||||
use rog_aura::{AuraEffect, AuraModeNum};
|
||||
|
||||
use crate::system_state::SystemState;
|
||||
use crate::widgets::{aura_modes_group, keyboard};
|
||||
use crate::RogApp;
|
||||
|
||||
impl RogApp {
|
||||
pub fn aura_page(&mut self, states: &mut SystemState, ctx: &eframe::egui::Context) {
|
||||
let Self {
|
||||
oscillator1,
|
||||
oscillator2,
|
||||
oscillator3,
|
||||
oscillator_freq,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let red = oscillator1.load(Ordering::SeqCst) as u32;
|
||||
let green = oscillator2.load(Ordering::SeqCst) as u32;
|
||||
let blue = oscillator3.load(Ordering::SeqCst) as u32;
|
||||
states.aura.nudge_wave(red as u8, green as u8, blue as u8);
|
||||
// let osc = c.r * 255 / osc;
|
||||
let c1 = states
|
||||
.aura
|
||||
.modes
|
||||
.get(&states.aura.current_mode)
|
||||
.unwrap_or(&AuraEffect::default())
|
||||
.colour1;
|
||||
|
||||
let c2 = states
|
||||
.aura
|
||||
.modes
|
||||
.get(&states.aura.current_mode)
|
||||
.unwrap_or(&AuraEffect::default())
|
||||
.colour2;
|
||||
|
||||
let mut colour = Color32::from_rgb(c1.r, c1.g, c1.b);
|
||||
if states.aura.current_mode == AuraModeNum::Pulse {
|
||||
colour = Color32::from_rgb(
|
||||
(red * c1.r as u32 / 100) as u8,
|
||||
(red * c1.g as u32 / 100) as u8,
|
||||
(red * c1.b as u32 / 100) as u8,
|
||||
);
|
||||
} else if states.aura.current_mode == AuraModeNum::Breathe {
|
||||
if self.oscillator_toggle.load(Ordering::SeqCst) {
|
||||
colour = Color32::from_rgb(
|
||||
(red * c2.r as u32 / 100) as u8,
|
||||
(red * c2.g as u32 / 100) as u8,
|
||||
(red * c2.b as u32 / 100) as u8,
|
||||
);
|
||||
} else {
|
||||
colour = Color32::from_rgb(
|
||||
(red * c1.r as u32 / 100) as u8,
|
||||
(red * c1.g as u32 / 100) as u8,
|
||||
(red * c1.b as u32 / 100) as u8,
|
||||
);
|
||||
}
|
||||
} else if states.aura.current_mode == AuraModeNum::Strobe {
|
||||
colour = Color32::from_rgb(
|
||||
(red * 255 / 100) as u8,
|
||||
(green * 255 / 100) as u8,
|
||||
(blue * 255 / 100) as u8,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: animation of colour changes/periods/blending
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
aura_modes_group(states, oscillator_freq, ui);
|
||||
keyboard(
|
||||
ui,
|
||||
&states.aura_creation.keyboard_layout,
|
||||
&mut states.aura,
|
||||
colour,
|
||||
);
|
||||
});
|
||||
|
||||
// Only do repaint request if on this page
|
||||
ctx.request_repaint_after(Duration::from_millis(33));
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
use egui::{RichText, Ui};
|
||||
use rog_platform::platform::ThrottlePolicy;
|
||||
|
||||
use crate::system_state::{FanCurvesState, SystemState};
|
||||
use crate::widgets::fan_graphs;
|
||||
use crate::{RogApp, RogDbusClientBlocking};
|
||||
|
||||
impl RogApp {
|
||||
pub fn fan_curve_page(&mut self, states: &mut SystemState, ctx: &eframe::egui::Context) {
|
||||
if let Some(mut throttle) = states.bios.throttle {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Custom fan curves");
|
||||
Self::fan_curve(
|
||||
&mut throttle,
|
||||
&mut states.fan_curves,
|
||||
&states.asus_dbus,
|
||||
&mut states.error,
|
||||
ui,
|
||||
);
|
||||
|
||||
fan_graphs(
|
||||
&mut states.fan_curves,
|
||||
&states.asus_dbus,
|
||||
&mut states.error,
|
||||
ui,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn fan_curve(
|
||||
current: &mut ThrottlePolicy,
|
||||
curves: &mut FanCurvesState,
|
||||
dbus: &RogDbusClientBlocking<'_>,
|
||||
do_error: &mut Option<String>,
|
||||
ui: &mut Ui,
|
||||
) {
|
||||
ui.separator();
|
||||
|
||||
let mut changed = false;
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Current profile: ");
|
||||
ui.label(RichText::new(format!("{}", current)).strong());
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Enabled fan-curves: ");
|
||||
let mut checked = false;
|
||||
let mut label = String::default();
|
||||
if let Some(curves) = curves.curves.get_mut(current) {
|
||||
for curve in curves.iter() {
|
||||
label.push_str(&<&str>::from(curve.fan).to_ascii_uppercase());
|
||||
label.push(' ');
|
||||
if curve.enabled {
|
||||
// TODO: it's possible to set just one fan to active
|
||||
checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ui
|
||||
.add(egui::Checkbox::new(
|
||||
&mut checked,
|
||||
RichText::new(label).strong(),
|
||||
))
|
||||
.changed()
|
||||
{
|
||||
dbus.proxies()
|
||||
.fan_curves()
|
||||
.set_fan_curves_enabled(*current, checked)
|
||||
.map_err(|err| {
|
||||
*do_error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
if changed {
|
||||
let selected_profile = curves.show_curve;
|
||||
let selected_pu = curves.show_graph;
|
||||
|
||||
match FanCurvesState::new(dbus) {
|
||||
Ok(f) => *curves = f,
|
||||
Err(e) => *do_error = Some(e.to_string()),
|
||||
}
|
||||
|
||||
curves.show_curve = selected_profile;
|
||||
curves.show_graph = selected_pu;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
mod anime_page;
|
||||
mod app_settings;
|
||||
mod aura_page;
|
||||
mod fan_curve_page;
|
||||
mod system_page;
|
||||
@@ -1,50 +0,0 @@
|
||||
use crate::system_state::SystemState;
|
||||
use crate::widgets::{anime_power_group, aura_power_group, platform_profile, rog_bios_group};
|
||||
use crate::RogApp;
|
||||
|
||||
impl RogApp {
|
||||
pub fn system_page(&mut self, states: &mut SystemState, ctx: &eframe::egui::Context) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Laptop settings");
|
||||
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.spacing_mut().item_spacing = egui::vec2(8.0, 10.0);
|
||||
let rect = ui.available_rect_before_wrap();
|
||||
egui::Grid::new("grid_of_bits")
|
||||
.min_col_width(rect.width() / 2.0)
|
||||
.show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
// if self.supported_interfaces {
|
||||
platform_profile(states, ui);
|
||||
// }
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
if self.supported_interfaces.contains(&"Aura".to_string()) {
|
||||
aura_power_group(states, ui);
|
||||
}
|
||||
});
|
||||
ui.end_row();
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
rog_bios_group(states, ui);
|
||||
});
|
||||
ui.end_row();
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
if self.supported_interfaces.contains(&"Anime".to_string()) {
|
||||
anime_power_group(states, ui);
|
||||
}
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
});
|
||||
ui.end_row();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
use egui::RichText;
|
||||
|
||||
pub struct AppErrorShow {
|
||||
error: String,
|
||||
}
|
||||
|
||||
impl AppErrorShow {
|
||||
pub fn new(error: String) -> Self {
|
||||
Self { error }
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for AppErrorShow {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("ROG ERROR");
|
||||
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.label(RichText::new(format!("The error was: {:?}", self.error)).size(22.0));
|
||||
});
|
||||
|
||||
// egui::TopBottomPanel::bottom("error_bar_2")
|
||||
// .default_height(26.0)
|
||||
// .show(ctx, |ui| {
|
||||
// ui.
|
||||
// with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
|
||||
// if ui
|
||||
// .add(Button::new(RichText::new("Okay").size(20.0)))
|
||||
// .clicked()
|
||||
// {
|
||||
// // frame.close();
|
||||
// // ui.close_menu();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,11 @@ use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::SystemTime;
|
||||
|
||||
use egui::Vec2;
|
||||
use log::error;
|
||||
use rog_anime::{Animations, DeviceState};
|
||||
use rog_aura::aura_detection::PowerZones;
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_aura::usb::{AuraDevice, AuraPowerDev};
|
||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
|
||||
use rog_aura::usb::AuraPowerDev;
|
||||
use rog_aura::{AuraEffect, AuraModeNum, LedBrightness};
|
||||
use rog_platform::platform::{GpuMode, ThrottlePolicy};
|
||||
use rog_profiles::fan_curve_set::CurveData;
|
||||
use rog_profiles::FanCurvePU;
|
||||
@@ -69,7 +67,7 @@ pub struct FanCurvesState {
|
||||
pub show_graph: FanCurvePU,
|
||||
pub curves: BTreeMap<ThrottlePolicy, Vec<CurveData>>,
|
||||
pub available_fans: HashSet<FanCurvePU>,
|
||||
pub drag_delta: Vec2,
|
||||
// pub drag_delta: Vec2,
|
||||
}
|
||||
|
||||
impl FanCurvesState {
|
||||
@@ -102,7 +100,7 @@ impl FanCurvesState {
|
||||
show_graph: FanCurvePU::CPU,
|
||||
curves,
|
||||
available_fans,
|
||||
drag_delta: Vec2::default(),
|
||||
// drag_delta: Vec2::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -112,9 +110,6 @@ pub struct AuraState {
|
||||
pub current_mode: AuraModeNum,
|
||||
pub modes: BTreeMap<AuraModeNum, AuraEffect>,
|
||||
pub enabled: AuraPowerDev,
|
||||
pub dev_type: AuraDevice,
|
||||
pub supported_basic_zones: Vec<AuraZone>,
|
||||
pub supported_power_zones: Vec<PowerZones>,
|
||||
/// Brightness from 0-3
|
||||
pub bright: LedBrightness,
|
||||
pub wave_red: [u8; 22],
|
||||
@@ -126,7 +121,10 @@ impl AuraState {
|
||||
pub fn new(layout: &KeyLayout, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
current_mode: if !layout.basic_modes().is_empty() {
|
||||
dbus.proxies().aura().led_mode().unwrap_or_default()
|
||||
dbg!();
|
||||
let x = dbus.proxies().aura().led_mode().unwrap_or_default();
|
||||
dbg!();
|
||||
x
|
||||
} else {
|
||||
AuraModeNum::Static
|
||||
},
|
||||
@@ -137,18 +135,7 @@ impl AuraState {
|
||||
BTreeMap::new()
|
||||
},
|
||||
enabled: dbus.proxies().aura().led_power().unwrap_or_default(),
|
||||
supported_basic_zones: dbus
|
||||
.proxies()
|
||||
.aura()
|
||||
.supported_basic_zones()
|
||||
.unwrap_or_default(),
|
||||
supported_power_zones: dbus
|
||||
.proxies()
|
||||
.aura()
|
||||
.supported_power_zones()
|
||||
.unwrap_or_default(),
|
||||
dev_type: dbus.proxies().aura().device_type().unwrap_or_default(),
|
||||
bright: dbus.proxies().aura().brightness().unwrap_or_default(),
|
||||
bright: Default::default(),
|
||||
wave_red: [0u8; 22],
|
||||
wave_green: [0u8; 22],
|
||||
wave_blue: [0u8; 22],
|
||||
@@ -291,9 +278,9 @@ impl SystemState {
|
||||
tray_enabled: bool,
|
||||
run_in_bg: bool,
|
||||
) -> Result<Self> {
|
||||
dbg!();
|
||||
let (asus_dbus, conn) = RogDbusClientBlocking::new()?;
|
||||
let gfx_dbus = GfxProxyBlocking::new(&conn).expect("Couldn't connect to supergfxd");
|
||||
|
||||
dbg!();
|
||||
let aura = AuraState::new(&keyboard_layout, &asus_dbus)
|
||||
.map_err(|e| {
|
||||
let e = format!("Could not get AuraState state: {e}");
|
||||
@@ -301,6 +288,12 @@ impl SystemState {
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
dbg!();
|
||||
let gfx_dbus = GfxProxyBlocking::builder(&conn)
|
||||
.destination(":org.supergfxctl.Daemon")?
|
||||
.build()
|
||||
.expect("Couldn't connect to supergfxd");
|
||||
dbg!();
|
||||
Ok(Self {
|
||||
aura_creation: AuraCreation::new(layout_testing, keyboard_layout, keyboard_layouts),
|
||||
enabled_notifications,
|
||||
@@ -348,7 +341,9 @@ impl SystemState {
|
||||
impl Default for SystemState {
|
||||
fn default() -> Self {
|
||||
let (asus_dbus, conn) = RogDbusClientBlocking::new().expect("Couldn't connect to asusd");
|
||||
let gfx_dbus = GfxProxyBlocking::new(&conn).expect("Couldn't connect to supergfxd");
|
||||
let gfx_dbus = GfxProxyBlocking::builder(&conn)
|
||||
.build()
|
||||
.expect("Couldn't connect to supergfxd");
|
||||
|
||||
Self {
|
||||
aura_creation: AuraCreation {
|
||||
|
||||
@@ -86,7 +86,7 @@ impl ROGTray {
|
||||
e
|
||||
})?;
|
||||
|
||||
let gfx_proxy = GfxProxyBlocking::new(&conn).map_err(|e| {
|
||||
let gfx_proxy = GfxProxyBlocking::builder(&conn).build().map_err(|e| {
|
||||
error!("ROGTray: {e}");
|
||||
e
|
||||
})?;
|
||||
|
||||
@@ -98,7 +98,7 @@ macro_rules! recv_notif {
|
||||
log::error!("zbus signal: {}: {e}", stringify!($signal));
|
||||
e
|
||||
}).unwrap();
|
||||
let proxy = $proxy::new(&conn).await.map_err(|e| {
|
||||
let proxy = $proxy::builder(&conn).build().await.map_err(|e| {
|
||||
log::error!("zbus signal: {}: {e}", stringify!($signal));
|
||||
e
|
||||
}).unwrap();
|
||||
@@ -446,7 +446,8 @@ pub fn start_notifications(
|
||||
e
|
||||
})
|
||||
.unwrap();
|
||||
let proxy = SuperProxy::new(&conn)
|
||||
let proxy = SuperProxy::builder(&conn)
|
||||
.build()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("zbus signal: receive_notify_action: {e}");
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
use egui::{RichText, Ui};
|
||||
use rog_anime::usb::Brightness;
|
||||
|
||||
use crate::system_state::SystemState;
|
||||
|
||||
pub fn anime_power_group(states: &mut SystemState, ui: &mut Ui) {
|
||||
ui.heading("AniMe Matrix Settings");
|
||||
ui.label("Options are incomplete. Awake + Boot should work");
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
let h = 16.0;
|
||||
ui.set_row_height(22.0);
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Display brightness").size(h));
|
||||
});
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Display enabled").size(h));
|
||||
});
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Animations enabled").size(h));
|
||||
});
|
||||
// ui.horizontal_wrapped(|ui| {
|
||||
// ui.label(RichText::new("Sleep").size(h));
|
||||
// });
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.set_row_height(22.0);
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
let slider =
|
||||
egui::Slider::new(&mut states.anime.display_brightness, 0..=3).step_by(1.0);
|
||||
if ui.add(slider).drag_released() {
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.anime()
|
||||
.set_brightness(Brightness::from(states.anime.display_brightness))
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
if ui
|
||||
.checkbox(&mut states.anime.display_enabled, "Enable")
|
||||
.changed()
|
||||
{
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.anime()
|
||||
.set_enable_display(states.anime.display_enabled)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
if ui
|
||||
.checkbox(&mut states.anime.builtin_anims_enabled, "Enable")
|
||||
.changed()
|
||||
{
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.anime()
|
||||
.set_builtins_enabled(states.anime.builtin_anims_enabled)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
// ui.horizontal_wrapped(|ui| {
|
||||
// if ui.checkbox(&mut states.anime.sleep, "Enable").changed() {
|
||||
// changed = true;
|
||||
// }
|
||||
// });
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
use egui::Ui;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::system_state::SystemState;
|
||||
|
||||
pub fn app_settings(config: &mut Config, states: &mut SystemState, ui: &mut Ui) {
|
||||
ui.heading("App Settings");
|
||||
|
||||
let mut enabled_notifications = if let Ok(lock) = states.enabled_notifications.lock() {
|
||||
lock.clone()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
ui.label("Application settings");
|
||||
let app_changed = ui
|
||||
.checkbox(
|
||||
&mut config.enable_tray_icon,
|
||||
"Enable Tray Icon (restart required)",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(&mut config.run_in_background, "Run in Background")
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(&mut config.startup_in_background, "Startup Hidden")
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.all_enabled,
|
||||
"Enable Notifications",
|
||||
)
|
||||
.clicked();
|
||||
|
||||
// if ui.button("Quit").clicked() {
|
||||
// states.run_in_bg = false;
|
||||
// }
|
||||
|
||||
ui.label("Notification settings");
|
||||
let notif_changed = ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_notify_gfx_status,
|
||||
"Enable dGPU status notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_led_mode_data_changed,
|
||||
"Enable LED mode change notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_dgpu_disable_changed,
|
||||
"Enable dGPU disablement notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_egpu_enable_changed,
|
||||
"Enable eGPU enablement notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_notify_mains_online,
|
||||
"Enable mains (AC) power notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_charge_control_end_threshold_changed,
|
||||
"Enable charge threshold notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_throttle_thermal_policy_changed,
|
||||
"Enable profile change notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_panel_od_changed,
|
||||
"Enable panel overdrive notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_mini_led_mode_changed,
|
||||
"Enable MiniLED mode notification",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(
|
||||
&mut enabled_notifications.receive_post_animation_sound_changed,
|
||||
"Enable BIOS post sound notification",
|
||||
)
|
||||
.clicked();
|
||||
|
||||
if app_changed || notif_changed {
|
||||
if let Ok(mut lock) = states.enabled_notifications.lock() {
|
||||
// Replace inner content before save
|
||||
*lock = enabled_notifications;
|
||||
|
||||
config
|
||||
.save(&lock)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
|
||||
states.tray_enabled = config.enable_tray_icon;
|
||||
states.run_in_bg = config.run_in_background;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use egui::{RichText, Ui};
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour, Speed};
|
||||
|
||||
use crate::system_state::SystemState;
|
||||
|
||||
pub fn aura_modes_group(states: &mut SystemState, freq: &mut Arc<AtomicU8>, ui: &mut Ui) {
|
||||
let mut changed = false;
|
||||
let mut selected = states.aura.current_mode;
|
||||
let allowed = AuraEffect::allowed_parameters(selected);
|
||||
|
||||
let SystemState { aura_creation, .. } = states;
|
||||
|
||||
let has_keyzones = aura_creation
|
||||
.keyboard_layout
|
||||
.basic_zones()
|
||||
.contains(&AuraZone::Key2);
|
||||
let has_logo = aura_creation
|
||||
.keyboard_layout
|
||||
.basic_zones()
|
||||
.contains(&AuraZone::Logo);
|
||||
let has_lightbar = aura_creation
|
||||
.keyboard_layout
|
||||
.basic_zones()
|
||||
.contains(&AuraZone::BarLeft)
|
||||
|| aura_creation
|
||||
.keyboard_layout
|
||||
.basic_zones()
|
||||
.contains(&AuraZone::BarRight);
|
||||
|
||||
if let Some(p) = aura_creation.layout_testing.as_ref() {
|
||||
ui.heading(format!("{p:?}"));
|
||||
} else {
|
||||
ui.heading("Aura modes");
|
||||
ui.label(
|
||||
"Please note that this section is incomplete and the displayed effects won't match \
|
||||
actual effect",
|
||||
);
|
||||
}
|
||||
let mut item = |a: AuraModeNum, ui: &mut Ui| {
|
||||
if ui
|
||||
.selectable_value(&mut selected, a, format!("{:?}", a))
|
||||
.clicked()
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
};
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for a in states.aura.modes.keys() {
|
||||
item(*a, ui);
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(effect) = states.aura.modes.get_mut(&selected) {
|
||||
let mut zone_button = |a: AuraZone, ui: &mut Ui| {
|
||||
ui.selectable_value(&mut effect.zone, a, format!("{:?}", a));
|
||||
};
|
||||
let mut speed_button = |a: Speed, ui: &mut Ui| -> bool {
|
||||
if ui
|
||||
.selectable_value(&mut effect.speed, a, format!("{:?}", a))
|
||||
.clicked()
|
||||
{
|
||||
let val = match effect.speed {
|
||||
Speed::Low => 6,
|
||||
Speed::Med => 8,
|
||||
Speed::High => 10,
|
||||
};
|
||||
freq.store(val, Ordering::SeqCst);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
};
|
||||
let mut dir_button = |a: rog_aura::Direction, ui: &mut Ui| -> bool {
|
||||
ui.selectable_value(&mut effect.direction, a, format!("{:?}", a))
|
||||
.clicked()
|
||||
};
|
||||
|
||||
let mut c1: [u8; 3] = effect.colour1.into();
|
||||
let mut c2: [u8; 3] = effect.colour2.into();
|
||||
|
||||
ui.separator();
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
let h = 16.0;
|
||||
ui.set_row_height(22.0);
|
||||
ui.add_enabled_ui(allowed.zone, |ui| {
|
||||
if has_keyzones || has_lightbar || has_logo {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Zone").size(h));
|
||||
});
|
||||
}
|
||||
});
|
||||
ui.add_enabled_ui(allowed.colour1, |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Colour 1").size(h));
|
||||
});
|
||||
});
|
||||
ui.add_enabled_ui(allowed.colour2, |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Colour 2").size(h));
|
||||
});
|
||||
});
|
||||
ui.add_enabled_ui(allowed.speed, |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.set_enabled(allowed.speed);
|
||||
ui.label(RichText::new("Speed").size(h));
|
||||
});
|
||||
});
|
||||
ui.add_enabled_ui(allowed.direction, |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.set_enabled(allowed.direction);
|
||||
ui.label(RichText::new("Direction").size(h));
|
||||
});
|
||||
});
|
||||
ui.set_enabled(true);
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.set_row_height(22.0);
|
||||
ui.add_enabled_ui(allowed.zone, |ui| {
|
||||
if has_keyzones || has_lightbar || has_logo {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
zone_button(AuraZone::None, ui);
|
||||
if has_keyzones {
|
||||
zone_button(AuraZone::Key1, ui);
|
||||
zone_button(AuraZone::Key2, ui);
|
||||
zone_button(AuraZone::Key3, ui);
|
||||
zone_button(AuraZone::Key4, ui);
|
||||
}
|
||||
if has_logo {
|
||||
zone_button(AuraZone::Logo, ui);
|
||||
}
|
||||
if has_lightbar {
|
||||
zone_button(AuraZone::BarLeft, ui);
|
||||
zone_button(AuraZone::BarRight, ui);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ui.add_enabled_ui(allowed.colour1, |ui| {
|
||||
if egui::color_picker::color_edit_button_srgb(ui, &mut c1).changed() {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
ui.add_enabled_ui(allowed.colour2, |ui| {
|
||||
if egui::color_picker::color_edit_button_srgb(ui, &mut c2).changed() {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
ui.add_enabled_ui(allowed.speed, |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
if speed_button(Speed::Low, ui)
|
||||
|| speed_button(Speed::Med, ui)
|
||||
|| speed_button(Speed::High, ui)
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ui.add_enabled_ui(allowed.direction, |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
if dir_button(rog_aura::Direction::Left, ui)
|
||||
|| dir_button(rog_aura::Direction::Down, ui)
|
||||
|| dir_button(rog_aura::Direction::Right, ui)
|
||||
|| dir_button(rog_aura::Direction::Up, ui)
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
effect.colour1 = Colour::from(&c1);
|
||||
effect.colour2 = Colour::from(&c2);
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
|
||||
// if ui.add(egui::Button::new("Cancel")).clicked() {
|
||||
// match AuraState::new(&aura_creation.keyboard_layout, &states.asus_dbus) {
|
||||
// Ok(a) => states.aura.modes = a.modes,
|
||||
// Err(e) => states.error = Some(e.to_string()),
|
||||
// }
|
||||
// }
|
||||
|
||||
// if ui.add(egui::Button::new("Apply")).clicked() {
|
||||
// changed = true;
|
||||
// }
|
||||
|
||||
if aura_creation.layout_testing.is_some() {
|
||||
if ui.add(egui::Button::new("Next layout")).clicked() {
|
||||
if aura_creation.keyboard_layout_index < aura_creation.keyboard_layouts.len() - 1 {
|
||||
aura_creation.keyboard_layout_index += 1;
|
||||
}
|
||||
aura_creation.layout_testing = Some(
|
||||
aura_creation.keyboard_layouts[aura_creation.keyboard_layout_index].clone(),
|
||||
);
|
||||
aura_creation.keyboard_layout =
|
||||
KeyLayout::from_file(aura_creation.layout_testing.as_ref().unwrap().as_path())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if ui.add(egui::Button::new("Prev layout")).clicked() {
|
||||
if aura_creation.keyboard_layout_index > 0 {
|
||||
aura_creation.keyboard_layout_index -= 1;
|
||||
}
|
||||
aura_creation.layout_testing = Some(
|
||||
aura_creation.keyboard_layouts[aura_creation.keyboard_layout_index].clone(),
|
||||
);
|
||||
aura_creation.keyboard_layout =
|
||||
KeyLayout::from_file(aura_creation.layout_testing.as_ref().unwrap().as_path())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if changed {
|
||||
states.aura.current_mode = selected;
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.aura()
|
||||
.set_led_mode_data(states.aura.modes.get(&selected).unwrap().clone())
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
use egui::{RichText, Ui};
|
||||
use rog_aura::power::{AuraPower, KbAuraPowerState};
|
||||
use rog_aura::usb::{AuraDevRog1, AuraDevTuf, AuraDevice, AuraPowerDev};
|
||||
|
||||
use crate::system_state::SystemState;
|
||||
|
||||
pub fn aura_power_group(states: &mut SystemState, ui: &mut Ui) {
|
||||
ui.heading("Keyboard LED power settings");
|
||||
|
||||
if states.aura.dev_type.is_old_style() || states.aura.dev_type.is_tuf_style() {
|
||||
aura_power1(states, ui);
|
||||
} else if states.aura.dev_type.is_new_style() {
|
||||
aura_power2(states, ui);
|
||||
}
|
||||
}
|
||||
|
||||
fn aura_power1(states: &mut SystemState, ui: &mut Ui) {
|
||||
let enabled_states = &mut states.aura.enabled;
|
||||
let mut boot = enabled_states.old_rog.contains(&AuraDevRog1::Boot);
|
||||
let mut sleep = enabled_states.old_rog.contains(&AuraDevRog1::Sleep);
|
||||
let mut keyboard = enabled_states.old_rog.contains(&AuraDevRog1::Keyboard);
|
||||
let mut lightbar = enabled_states.old_rog.contains(&AuraDevRog1::Lightbar);
|
||||
if states.aura.dev_type == AuraDevice::Tuf {
|
||||
boot = enabled_states.tuf.contains(&AuraDevTuf::Boot);
|
||||
sleep = enabled_states.tuf.contains(&AuraDevTuf::Sleep);
|
||||
keyboard = enabled_states.tuf.contains(&AuraDevTuf::Awake);
|
||||
}
|
||||
let mut changed = false;
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
let h = 16.0;
|
||||
ui.set_row_height(22.0);
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Boot").size(h));
|
||||
});
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Awake").size(h));
|
||||
});
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new("Sleep").size(h));
|
||||
});
|
||||
// if supported.keyboard_led.brightness_set {
|
||||
// ui.horizontal_wrapped(|ui| {
|
||||
// ui.label(RichText::new("Brightness").size(h));
|
||||
// });
|
||||
// }
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.set_row_height(22.0);
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
if ui.checkbox(&mut boot, "Enable").changed() {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
if ui.toggle_value(&mut keyboard, "Keyboard").changed() {
|
||||
changed = true;
|
||||
}
|
||||
if !states.aura.supported_basic_zones.is_empty()
|
||||
&& ui.toggle_value(&mut lightbar, "Lightbar").changed()
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
if ui.checkbox(&mut sleep, "Enable").changed() {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
// We currently don't have a watch for system changes here
|
||||
// if supported.keyboard_led.brightness_set {
|
||||
// if ui
|
||||
// .add(egui::Slider::new(
|
||||
// &mut states.aura.bright,
|
||||
// 0..=3,
|
||||
// ))
|
||||
// .changed()
|
||||
// {
|
||||
// let bright = LedBrightness::from(states.aura.bright as
|
||||
// u32); dbus.proxies()
|
||||
// .led()
|
||||
// .set_brightness(bright)
|
||||
// .map_err(|err| {
|
||||
// states.error = Some(err.to_string());
|
||||
// })
|
||||
// .ok();
|
||||
// }
|
||||
// }
|
||||
});
|
||||
});
|
||||
|
||||
if changed {
|
||||
if states.aura.dev_type == AuraDevice::Tuf {
|
||||
let mut enabled = Vec::new();
|
||||
let mut disabled = Vec::new();
|
||||
|
||||
let mut modify_tuf = |b: bool, a: AuraDevTuf| {
|
||||
if b {
|
||||
enabled.push(a);
|
||||
if !enabled_states.tuf.contains(&a) {
|
||||
enabled_states.tuf.push(a);
|
||||
}
|
||||
} else {
|
||||
disabled.push(a);
|
||||
// This would be so much better as a hashset
|
||||
if enabled_states.tuf.contains(&a) {
|
||||
let mut idx = 0;
|
||||
for (i, n) in enabled_states.tuf.iter().enumerate() {
|
||||
if *n == a {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enabled_states.tuf.remove(idx);
|
||||
}
|
||||
}
|
||||
};
|
||||
modify_tuf(boot, AuraDevTuf::Boot);
|
||||
modify_tuf(sleep, AuraDevTuf::Sleep);
|
||||
modify_tuf(keyboard, AuraDevTuf::Awake);
|
||||
|
||||
let mut send = |enable: bool, data: Vec<AuraDevTuf>| {
|
||||
let options = AuraPowerDev {
|
||||
tuf: data,
|
||||
..Default::default()
|
||||
};
|
||||
// build data to send
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.aura()
|
||||
.set_led_power((options, enable))
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
};
|
||||
send(true, enabled);
|
||||
send(false, disabled);
|
||||
} else {
|
||||
let mut enabled = Vec::new();
|
||||
let mut disabled = Vec::new();
|
||||
|
||||
let mut modify_x1866 = |b: bool, a: AuraDevRog1| {
|
||||
if b {
|
||||
enabled.push(a);
|
||||
if !enabled_states.old_rog.contains(&a) {
|
||||
enabled_states.old_rog.push(a);
|
||||
}
|
||||
} else {
|
||||
disabled.push(a);
|
||||
// This would be so much better as a hashset
|
||||
if enabled_states.old_rog.contains(&a) {
|
||||
let mut idx = 0;
|
||||
for (i, n) in enabled_states.old_rog.iter().enumerate() {
|
||||
if *n == a {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enabled_states.old_rog.remove(idx);
|
||||
}
|
||||
}
|
||||
};
|
||||
modify_x1866(boot, AuraDevRog1::Boot);
|
||||
modify_x1866(sleep, AuraDevRog1::Sleep);
|
||||
modify_x1866(keyboard, AuraDevRog1::Keyboard);
|
||||
if !states.aura.supported_basic_zones.is_empty() {
|
||||
modify_x1866(lightbar, AuraDevRog1::Lightbar);
|
||||
}
|
||||
|
||||
let mut send = |enable: bool, data: Vec<AuraDevRog1>| {
|
||||
let options = AuraPowerDev {
|
||||
old_rog: data,
|
||||
..Default::default()
|
||||
};
|
||||
// build data to send
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.aura()
|
||||
.set_led_power((options, enable))
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
};
|
||||
send(true, enabled);
|
||||
send(false, disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn aura_power2(states: &mut SystemState, ui: &mut Ui) {
|
||||
let AuraPower {
|
||||
keyboard,
|
||||
logo,
|
||||
lightbar,
|
||||
lid,
|
||||
rear_glow,
|
||||
} = &mut states.aura.enabled.rog;
|
||||
|
||||
const LABELS: [&str; 4] = ["Boot", "Awake", "Sleep", "Shutdown"];
|
||||
|
||||
let mut changed = false;
|
||||
let mut item = |power: &mut KbAuraPowerState, ui: &mut Ui| {
|
||||
ui.vertical(|ui| {
|
||||
if states.aura.supported_power_zones.contains(&power.zone) {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(RichText::new(format!("{:?}", power.zone)).size(14.0));
|
||||
});
|
||||
if ui.checkbox(&mut power.boot, LABELS[0]).changed() {
|
||||
changed = true;
|
||||
}
|
||||
if ui.checkbox(&mut power.awake, LABELS[1]).changed() {
|
||||
changed = true;
|
||||
}
|
||||
if ui.checkbox(&mut power.sleep, LABELS[2]).changed() {
|
||||
changed = true;
|
||||
}
|
||||
if ui.checkbox(&mut power.shutdown, LABELS[3]).changed() {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
item(lid, ui);
|
||||
item(logo, ui);
|
||||
item(keyboard, ui);
|
||||
item(lightbar, ui);
|
||||
item(rear_glow, ui);
|
||||
});
|
||||
|
||||
if changed {
|
||||
let mut send = |enable: bool, data: AuraPower| {
|
||||
let options = AuraPowerDev {
|
||||
rog: data,
|
||||
..Default::default()
|
||||
};
|
||||
// build data to send
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.aura()
|
||||
.set_led_power((options, enable))
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
};
|
||||
send(
|
||||
true,
|
||||
AuraPower {
|
||||
keyboard: *keyboard,
|
||||
logo: *logo,
|
||||
lightbar: *lightbar,
|
||||
lid: *lid,
|
||||
rear_glow: *rear_glow,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
use egui::plot::{Line, Plot, Points};
|
||||
use egui::Ui;
|
||||
use rog_platform::platform::ThrottlePolicy;
|
||||
use rog_profiles::fan_curve_set::CurveData;
|
||||
use rog_profiles::FanCurvePU;
|
||||
|
||||
use crate::system_state::FanCurvesState;
|
||||
use crate::RogDbusClientBlocking;
|
||||
|
||||
pub fn fan_graphs(
|
||||
curves: &mut FanCurvesState,
|
||||
dbus: &RogDbusClientBlocking<'_>,
|
||||
do_error: &mut Option<String>,
|
||||
ui: &mut Ui,
|
||||
) {
|
||||
if curves.available_fans.is_empty() {
|
||||
return; // TODO:
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
let mut item = |profile: ThrottlePolicy, ui: &mut Ui| {
|
||||
ui.group(|ui| {
|
||||
if ui
|
||||
.selectable_value(&mut curves.show_curve, profile, format!("{profile:?}"))
|
||||
.clicked()
|
||||
{
|
||||
dbus.proxies()
|
||||
.platform()
|
||||
.set_throttle_thermal_policy(profile)
|
||||
.ok();
|
||||
}
|
||||
ui.add_enabled_ui(curves.show_curve == profile, |ui| {
|
||||
if curves.available_fans.contains(&FanCurvePU::CPU) {
|
||||
ui.selectable_value(
|
||||
&mut curves.show_graph,
|
||||
FanCurvePU::CPU,
|
||||
format!("{:?}", FanCurvePU::CPU),
|
||||
);
|
||||
}
|
||||
if curves.available_fans.contains(&FanCurvePU::GPU) {
|
||||
ui.selectable_value(
|
||||
&mut curves.show_graph,
|
||||
FanCurvePU::GPU,
|
||||
format!("{:?}", FanCurvePU::GPU),
|
||||
);
|
||||
}
|
||||
if curves.available_fans.contains(&FanCurvePU::MID) {
|
||||
ui.selectable_value(
|
||||
&mut curves.show_graph,
|
||||
FanCurvePU::MID,
|
||||
format!("{:?}", FanCurvePU::MID),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for a in &curves.curves {
|
||||
item(*a.0, ui);
|
||||
}
|
||||
});
|
||||
|
||||
let curve = curves.curves.get_mut(&curves.show_curve).unwrap();
|
||||
|
||||
let mut data = &mut CurveData::default();
|
||||
for c in curve {
|
||||
if c.fan == curves.show_graph {
|
||||
data = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut points: Vec<[f64; 2]> = data
|
||||
.temp
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, x)| {
|
||||
let x = *x as f64;
|
||||
let y = ((data.pwm[idx] as u32) * 100 / 255) as f64;
|
||||
[x, y]
|
||||
})
|
||||
.collect();
|
||||
|
||||
for i in 0..points.len() - 1 {
|
||||
if i > 0 && i < points.len() - 1 {
|
||||
if points[i][0] < points[i - 1][0] {
|
||||
points[i][0] = points[i - 1][0] + 1.0;
|
||||
data.temp[i] = points[i - 1][0] as u8;
|
||||
}
|
||||
if points[i][0] >= points[i + 1][0] {
|
||||
points[i + 1][0] = points[i][0] + 1.0;
|
||||
data.temp[i + 1] = points[i][0] as u8;
|
||||
}
|
||||
if points[i][1] < points[i - 1][1] {
|
||||
points[i][1] = points[i - 1][1] + 1.0;
|
||||
data.pwm[i] = (points[i - 1][1] * 255.0 / 100.0 + 1.0).floor() as u8;
|
||||
}
|
||||
if points[i][1] >= points[i + 1][1] {
|
||||
points[i + 1][1] = points[i][1] + 1.0;
|
||||
data.pwm[i + 1] = (points[i][1] * 255.0 / 100.0 + 1.0).floor() as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let line = Line::new(points.clone()).width(2.0);
|
||||
let points = Points::new(points).radius(3.0);
|
||||
|
||||
Plot::new("fan_curves")
|
||||
.view_aspect(1.666)
|
||||
// .center_x_axis(true)
|
||||
// .center_y_axis(true)
|
||||
.include_x(0.0)
|
||||
.include_x(104.0)
|
||||
.include_y(0.0)
|
||||
.include_y(106.0)
|
||||
.allow_scroll(false)
|
||||
.allow_drag(false)
|
||||
.allow_boxed_zoom(false)
|
||||
// .x_axis_formatter(|d, _r| format!("{}", d))
|
||||
// .y_axis_formatter(|d, _r| format!("{:.*}%", 1, d))
|
||||
.label_formatter(|name, value| {
|
||||
if !name.is_empty() {
|
||||
format!("{}: {:.*}%", name, 1, value.y)
|
||||
} else {
|
||||
format!("Temp {}c\nFan {:.*}%", value.x as u8, 1, value.y)
|
||||
}
|
||||
})
|
||||
.show(ui, |plot_ui| {
|
||||
if plot_ui.plot_hovered() {
|
||||
let mut idx = 0;
|
||||
|
||||
if let Some(point) = plot_ui.pointer_coordinate() {
|
||||
let mut x: i32 = point.x as i32;
|
||||
for (i, n) in data.temp.iter().enumerate() {
|
||||
let tmp = x.min((point.x as i32 - *n as i32).abs());
|
||||
if tmp < x {
|
||||
x = tmp;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if plot_ui.plot_clicked() {
|
||||
data.temp[idx] = point.x as u8;
|
||||
data.pwm[idx] = (point.y * 255.0 / 100.0) as u8;
|
||||
} else {
|
||||
let drag = plot_ui.pointer_coordinate_drag_delta();
|
||||
if drag.length_sq() != 0.0 {
|
||||
data.temp[idx] = (point.x as f32 + drag.x) as u8;
|
||||
data.pwm[idx] = ((point.y as f32 + drag.y) * 255.0 / 100.0) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
plot_ui.line(line);
|
||||
plot_ui.points(points);
|
||||
});
|
||||
|
||||
let mut set = false;
|
||||
let mut clear = false;
|
||||
let mut reset = false;
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
|
||||
set = ui.add(egui::Button::new("Apply Profile")).clicked();
|
||||
clear = ui.add(egui::Button::new("Clear Profile Changes")).clicked();
|
||||
reset = ui.add(egui::Button::new("Factory Reset Profile")).clicked();
|
||||
});
|
||||
|
||||
if set {
|
||||
dbus.proxies()
|
||||
.fan_curves()
|
||||
.set_fan_curve(curves.show_curve, data.clone())
|
||||
.map_err(|err| {
|
||||
*do_error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
if clear {
|
||||
if let Ok(curve) = dbus
|
||||
.proxies()
|
||||
.fan_curves()
|
||||
.fan_curve_data(curves.show_curve)
|
||||
.map_err(|err| {
|
||||
*do_error = Some(err.to_string());
|
||||
})
|
||||
{
|
||||
if let Some(value) = curves.curves.get_mut(&curves.show_curve) {
|
||||
*value = curve;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if reset {
|
||||
dbus.proxies()
|
||||
.fan_curves()
|
||||
.reset_profile_curves(curves.show_curve)
|
||||
.map_err(|err| {
|
||||
*do_error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
|
||||
match FanCurvesState::new(dbus) {
|
||||
Ok(f) => *curves = f,
|
||||
Err(e) => *do_error = Some(e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
use egui::{Color32, Pos2};
|
||||
use rog_aura::advanced::LedCode;
|
||||
use rog_aura::layouts::{KeyLayout, KeyShape};
|
||||
use rog_aura::{AdvancedAuraType, AuraModeNum};
|
||||
|
||||
use crate::system_state::AuraState;
|
||||
|
||||
const SCALE: f32 = 2.0;
|
||||
|
||||
// TODO:
|
||||
// - Multizone: draw regions? While iterating keys check if located in one of
|
||||
// the 4 regions and mark
|
||||
// - Tab for advanced effects
|
||||
// - Keys need to select colour themselves
|
||||
|
||||
pub fn keyboard(
|
||||
ui: &mut egui::Ui,
|
||||
keyboard_layout: &KeyLayout,
|
||||
states: &mut AuraState,
|
||||
colour: Color32,
|
||||
) {
|
||||
let (keyboard_is_multizoned, keyboard_width, keyboard_is_per_key) =
|
||||
match keyboard_layout.advanced_type() {
|
||||
AdvancedAuraType::PerKey => (false, 0.0, true),
|
||||
AdvancedAuraType::None => (false, keyboard_layout.max_width(), false),
|
||||
AdvancedAuraType::Zoned(zones) => {
|
||||
let width = if let Some(row) = keyboard_layout.rows_ref().get(2) {
|
||||
row.width()
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
(!zones.contains(&LedCode::SingleZone), width, false)
|
||||
}
|
||||
};
|
||||
let mut start_pos = None;
|
||||
|
||||
let y = ui.spacing().interact_size.y;
|
||||
let this_size = ui.available_size();
|
||||
let keys_width = keyboard_layout.max_width() * SCALE * y;
|
||||
let keys_height = keyboard_layout.max_height() * SCALE * y;
|
||||
let keyboard_height = keyboard_layout.keyboard_height() * SCALE;
|
||||
let x_start = (this_size.x - keys_width) / SCALE;
|
||||
let y_start = (this_size.y - keys_height) / SCALE;
|
||||
|
||||
// Initial colour states
|
||||
let mut input_colour = colour;
|
||||
let mut key_colour = colour;
|
||||
if states.current_mode == AuraModeNum::Rainbow && !keyboard_is_per_key {
|
||||
key_colour = Color32::from_rgb(
|
||||
(states.wave_red[0] as u32 * 255 / 100) as u8,
|
||||
(states.wave_green[0] as u32 * 255 / 100) as u8,
|
||||
(states.wave_blue[0] as u32 * 255 / 100) as u8,
|
||||
);
|
||||
}
|
||||
|
||||
ui.spacing_mut().item_spacing = egui::vec2(0.0, 0.0);
|
||||
blank(ui, 0.0, y_start / y);
|
||||
|
||||
for row in keyboard_layout.rows() {
|
||||
ui.horizontal_top(|ui| {
|
||||
blank(ui, x_start / y, 0.0);
|
||||
for (i, key) in row.row().enumerate() {
|
||||
// For per-key rainbow which cascades across
|
||||
if states.current_mode == AuraModeNum::Rainbow && keyboard_is_per_key
|
||||
|| key.0.is_lightbar_zone()
|
||||
{
|
||||
key_colour = Color32::from_rgb(
|
||||
(states.wave_red[i] as u32 * 255 / 100) as u8,
|
||||
(states.wave_green[i] as u32 * 255 / 100) as u8,
|
||||
(states.wave_blue[i] as u32 * 255 / 100) as u8,
|
||||
);
|
||||
}
|
||||
|
||||
if (keyboard_is_multizoned && !key.0.is_lightbar_zone())
|
||||
&& states.current_mode == AuraModeNum::Rainbow
|
||||
{
|
||||
input_colour = key_colour;
|
||||
key_colour = Color32::TRANSPARENT;
|
||||
}
|
||||
|
||||
let label = <&str>::from(key.0);
|
||||
let mut shape = key.1.clone();
|
||||
shape.scale(SCALE);
|
||||
|
||||
match shape {
|
||||
KeyShape::Led {
|
||||
width,
|
||||
height,
|
||||
pad_left,
|
||||
pad_right,
|
||||
pad_top,
|
||||
pad_bottom,
|
||||
} => {
|
||||
let (pos, response) = key_shape(
|
||||
ui, key_colour, width, height, pad_left, pad_right, pad_top, pad_bottom,
|
||||
);
|
||||
if start_pos.is_none() {
|
||||
start_pos = Some(pos);
|
||||
} else if let Some(old_pos) = start_pos.as_mut() {
|
||||
if !key.0.is_lightbar_zone() {
|
||||
if pos.x < old_pos.x {
|
||||
old_pos.x = pos.x;
|
||||
}
|
||||
if pos.y < old_pos.y {
|
||||
old_pos.y = pos.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
if response.on_hover_text(label).clicked() && keyboard_is_per_key {
|
||||
// TODO: set an effect on the LedCode
|
||||
}
|
||||
}
|
||||
KeyShape::Blank { width, height } => {
|
||||
blank(ui, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if keyboard_is_multizoned {
|
||||
let zone_width = keyboard_width * SCALE / 4.0 - 0.1;
|
||||
for n in 0..4 {
|
||||
if states.current_mode == AuraModeNum::Rainbow {
|
||||
input_colour = Color32::from_rgba_unmultiplied(
|
||||
(states.wave_red[n] as u32 * 255 / 100) as u8,
|
||||
(states.wave_green[n] as u32 * 255 / 100) as u8,
|
||||
(states.wave_blue[n] as u32 * 255 / 100) as u8,
|
||||
70,
|
||||
);
|
||||
}
|
||||
if let Some(mut pos) = start_pos {
|
||||
pos.x += n as f32 * zone_width * y;
|
||||
let response = zone_shape(ui, input_colour, pos, zone_width, keyboard_height);
|
||||
let label = format!("Zone {}", 1 + n);
|
||||
if response.on_hover_text(label).clicked() {
|
||||
// TODO: set an effect on the zone
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn key_shape(
|
||||
ui: &mut egui::Ui,
|
||||
colour: Color32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
pad_left: f32,
|
||||
pad_right: f32,
|
||||
pad_top: f32,
|
||||
pad_bottom: f32,
|
||||
) -> (egui::Pos2, egui::Response) {
|
||||
// First, get some space
|
||||
let y = ui.spacing().interact_size.y;
|
||||
let desired_size = y * egui::vec2(width + pad_left + pad_right, height + pad_top + pad_bottom);
|
||||
let (mut rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
|
||||
// rect = rect.shrink(3.0);
|
||||
if response.clicked() {
|
||||
response.mark_changed();
|
||||
}
|
||||
response.widget_info(|| {
|
||||
egui::WidgetInfo::selected(egui::WidgetType::Checkbox, response.clicked(), "")
|
||||
});
|
||||
|
||||
if ui.is_rect_visible(rect) {
|
||||
// Now set the actual visible rect
|
||||
let visuals = ui.style().interact_selectable(&response, true);
|
||||
let size = y * egui::vec2(width, height);
|
||||
rect.set_width(size.x);
|
||||
rect.set_height(size.y);
|
||||
let center = Pos2::new(
|
||||
rect.center().x + pad_left * y,
|
||||
rect.center().y + pad_top * y,
|
||||
);
|
||||
rect.set_center(center);
|
||||
// let rect = rect.expand(visuals.expansion);
|
||||
ui.painter().rect(rect, 0.1, colour, visuals.fg_stroke);
|
||||
}
|
||||
|
||||
(rect.left_top(), response)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn zone_shape(
|
||||
ui: &mut egui::Ui,
|
||||
mut colour: Color32,
|
||||
pos: Pos2,
|
||||
width: f32,
|
||||
height: f32,
|
||||
) -> egui::Response {
|
||||
// First, get some space
|
||||
let y = ui.spacing().interact_size.y;
|
||||
let desired_size = y * egui::vec2(width, height);
|
||||
let rect = egui::Rect::from_min_size(pos, desired_size);
|
||||
let mut response = ui.allocate_rect(rect, egui::Sense::click());
|
||||
// rect = rect.shrink(3.0);
|
||||
if response.clicked() {
|
||||
response.mark_changed();
|
||||
}
|
||||
response.widget_info(|| {
|
||||
egui::WidgetInfo::selected(egui::WidgetType::Checkbox, response.clicked(), "")
|
||||
});
|
||||
|
||||
if ui.is_rect_visible(rect) {
|
||||
// Now set the actual visible rect
|
||||
let visuals = ui.style().interact_selectable(&response, true);
|
||||
// let rect = rect.expand(visuals.expansion);
|
||||
colour[3] = 20;
|
||||
ui.painter().rect(rect, 0.1, colour, visuals.fg_stroke);
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
fn blank(ui: &mut egui::Ui, ux: f32, uy: f32) {
|
||||
let desired_size = ui.spacing().interact_size.y * egui::vec2(ux, uy);
|
||||
ui.allocate_exact_size(desired_size, egui::Sense::click());
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
mod anime_power;
|
||||
mod app_settings;
|
||||
mod aura_modes;
|
||||
mod aura_power;
|
||||
mod fan_graph;
|
||||
mod keyboard_layout;
|
||||
mod rog_bios;
|
||||
mod side_panel;
|
||||
mod top_bar;
|
||||
|
||||
pub use anime_power::*;
|
||||
pub use app_settings::*;
|
||||
pub use aura_modes::*;
|
||||
pub use aura_power::*;
|
||||
pub use fan_graph::*;
|
||||
pub use keyboard_layout::*;
|
||||
pub use rog_bios::*;
|
||||
@@ -1,153 +0,0 @@
|
||||
use egui::Ui;
|
||||
use rog_platform::platform::{GpuMode, ThrottlePolicy};
|
||||
|
||||
use crate::system_state::SystemState;
|
||||
|
||||
pub fn platform_profile(states: &mut SystemState, ui: &mut Ui) {
|
||||
if let Some(mut throttle) = states.bios.throttle {
|
||||
ui.heading("Platform profile");
|
||||
|
||||
let mut item = |p: ThrottlePolicy, ui: &mut Ui| {
|
||||
if ui
|
||||
.selectable_value(&mut throttle, p, format!("{p:?}"))
|
||||
.clicked()
|
||||
{
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.platform()
|
||||
.set_throttle_thermal_policy(throttle)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
};
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for a in ThrottlePolicy::list() {
|
||||
item(a, ui);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rog_bios_group(states: &mut SystemState, ui: &mut Ui) {
|
||||
ui.heading("Bios options");
|
||||
|
||||
if let Some(mut limit) = states.bios.charge_limit {
|
||||
let slider = egui::Slider::new(&mut limit, 20..=100)
|
||||
.text("Charging limit")
|
||||
.step_by(1.0);
|
||||
if ui.add(slider).drag_released() {
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.platform()
|
||||
.set_charge_control_end_threshold(limit)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut sound) = states.bios.post_sound {
|
||||
if ui
|
||||
.add(egui::Checkbox::new(&mut sound, "POST sound"))
|
||||
.changed()
|
||||
{
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.platform()
|
||||
.set_post_animation_sound(sound)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut overdrive) = states.bios.panel_overdrive {
|
||||
if ui
|
||||
.add(egui::Checkbox::new(&mut overdrive, "Panel overdrive"))
|
||||
.changed()
|
||||
{
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.platform()
|
||||
.set_panel_od(overdrive)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut mini_led_mode) = states.bios.mini_led_mode {
|
||||
if ui
|
||||
.add(egui::Checkbox::new(&mut mini_led_mode, "MiniLED backlight"))
|
||||
.changed()
|
||||
{
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.platform()
|
||||
.set_mini_led_mode(mini_led_mode)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut gpu_mux_mode) = states.bios.gpu_mux_mode {
|
||||
let mut changed = false;
|
||||
|
||||
let mut reboot_required = false;
|
||||
if let Ok(mode) = states.asus_dbus.proxies().platform().gpu_mux_mode() {
|
||||
reboot_required = GpuMode::from(mode) != gpu_mux_mode;
|
||||
}
|
||||
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal_wrapped(|ui| ui.label("GPU MUX mode"));
|
||||
ui.horizontal_wrapped(|ui| ui.label("NOTE: Value does not change until rebooted"));
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
changed = ui
|
||||
.selectable_value(
|
||||
&mut gpu_mux_mode,
|
||||
GpuMode::Discrete,
|
||||
"Dedicated (Ultimate)",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.selectable_value(
|
||||
&mut gpu_mux_mode,
|
||||
GpuMode::Optimus,
|
||||
"Optimus (Hybrid)",
|
||||
)
|
||||
.clicked();
|
||||
});
|
||||
|
||||
if reboot_required {
|
||||
ui.horizontal_wrapped(|ui| ui.heading("REBOOT REQUIRED"));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if changed {
|
||||
states
|
||||
.asus_dbus
|
||||
.proxies()
|
||||
.platform()
|
||||
.set_gpu_mux_mode(gpu_mux_mode)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
use crate::{Page, RogApp};
|
||||
|
||||
impl RogApp {
|
||||
pub fn side_panel(&mut self, ctx: &eframe::egui::Context) {
|
||||
egui::SidePanel::left("side_panel")
|
||||
.resizable(false)
|
||||
.default_width(60.0) // TODO: set size to match icon buttons when done
|
||||
.show(ctx, |ui| {
|
||||
let Self { page, .. } = self;
|
||||
|
||||
ui.heading("Functions");
|
||||
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_value(page, Page::System, "System Settings")
|
||||
.clicked()
|
||||
{
|
||||
*page = Page::System;
|
||||
}
|
||||
|
||||
if self.supported_interfaces.contains(&"FanCurves".to_owned()) {
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_value(page, Page::FanCurves, "Fan Curves")
|
||||
.clicked()
|
||||
{
|
||||
*page = Page::FanCurves;
|
||||
}
|
||||
}
|
||||
|
||||
if self.supported_interfaces.contains(&"Aura".to_owned()) {
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_value(page, Page::AuraEffects, "Keyboard Aura")
|
||||
.clicked()
|
||||
{
|
||||
*page = Page::AuraEffects;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Anime page is not complete
|
||||
if self.supported_interfaces.contains(&"Anime".to_owned()) {
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_value(page, Page::AnimeMatrix, "AniMe Matrix")
|
||||
.clicked()
|
||||
{
|
||||
*page = Page::AnimeMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_value(page, Page::AppSettings, "App Settings")
|
||||
.clicked()
|
||||
{
|
||||
*page = Page::AppSettings;
|
||||
}
|
||||
|
||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.label("Source code ");
|
||||
ui.hyperlink_to(
|
||||
"rog-gui.",
|
||||
"https://gitlab.com/asus-linux/asusctl/-/tree/main/rog-control-center",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
use egui::{vec2, Align2, Button, FontId, Id, Rect, RichText, Sense, Vec2};
|
||||
|
||||
use crate::{RogApp, VERSION};
|
||||
|
||||
impl RogApp {
|
||||
pub fn top_bar(&mut self, ctx: &eframe::egui::Context, frame: &mut eframe::Frame) {
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
// The top panel is often a good place for a menu bar:
|
||||
egui::menu::bar(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
self.dark_light_mode_buttons(ui);
|
||||
egui::warn_if_debug_build(ui);
|
||||
});
|
||||
|
||||
// Drag area
|
||||
let text_color = ctx.style().visuals.text_color();
|
||||
let mut titlebar_rect = ui.available_rect_before_wrap();
|
||||
titlebar_rect.max.x -= titlebar_rect.height();
|
||||
if ui
|
||||
.interact(titlebar_rect, Id::new("title_bar"), Sense::drag())
|
||||
.drag_started()
|
||||
{
|
||||
frame.drag_window();
|
||||
}
|
||||
|
||||
let height = titlebar_rect.height();
|
||||
|
||||
// Paint the title:
|
||||
ui.painter().text(
|
||||
titlebar_rect.right_top() + vec2(0.0, height / 2.0),
|
||||
Align2::RIGHT_CENTER,
|
||||
format!("v{}", VERSION),
|
||||
FontId::proportional(height - 2.0),
|
||||
text_color,
|
||||
);
|
||||
// Add the close button:
|
||||
let close_response = ui.put(
|
||||
Rect::from_min_size(titlebar_rect.right_top(), Vec2::splat(height)),
|
||||
Button::new(RichText::new("❌").size(height - 4.0)).frame(false),
|
||||
);
|
||||
if close_response.clicked() {
|
||||
frame.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn dark_light_mode_buttons(&mut self, ui: &mut egui::Ui) {
|
||||
let load_from_cfg = self.config.dark_mode != ui.ctx().style().visuals.dark_mode;
|
||||
|
||||
if ui
|
||||
.add(egui::SelectableLabel::new(
|
||||
!self.config.dark_mode,
|
||||
"☀ Light",
|
||||
))
|
||||
.clicked()
|
||||
|| (load_from_cfg && !self.config.dark_mode)
|
||||
{
|
||||
ui.ctx().set_visuals(egui::Visuals::light());
|
||||
}
|
||||
if ui
|
||||
.add(egui::SelectableLabel::new(self.config.dark_mode, "🌙 Dark"))
|
||||
.clicked()
|
||||
|| (load_from_cfg && self.config.dark_mode)
|
||||
{
|
||||
ui.ctx().set_visuals(egui::Visuals::dark());
|
||||
}
|
||||
|
||||
let applied_dark_mode = ui.ctx().style().visuals.dark_mode;
|
||||
|
||||
if self.config.dark_mode != applied_dark_mode {
|
||||
self.config.dark_mode = applied_dark_mode;
|
||||
let tmp = self.config.enabled_notifications.clone();
|
||||
self.config.save(&tmp).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user