Compare commits

...

21 Commits

Author SHA1 Message Date
Luke D. Jones 2584d69930 Update deps 2022-07-25 21:38:00 +12:00
Luke D. Jones 523f39cf9c Version bump for RC 2022-07-25 21:34:33 +12:00
Luke D. Jones 669760223e Clean up erroneously included files 2022-07-25 21:32:58 +12:00
Luke D. Jones 71ec13fa9f ROGCC: Attempt to add LED brightness 2022-07-25 21:21:32 +12:00
Luke D. Jones 409528b286 ROGCC: Better control of notifs, add panel_od 2022-07-25 20:55:30 +12:00
Luke D. Jones 17df3cf01d Add rog-control-center to the workspace 2022-07-25 16:43:48 +12:00
Luke D. Jones 808a1d2470 Fix misnamed led dbus method 2022-07-25 14:22:29 +12:00
Luke D. Jones 840c500b5e Switch a keyboard prod_id to enum 2022-07-25 14:07:29 +12:00
Luke D. Jones 42dc360d16 Bump daemon version 2022-07-25 12:43:17 +12:00
Luke D. Jones f6183597c9 Trial BTreeMap<AuraModeNum, AuraEffect> return for led dbus 2022-07-25 10:26:47 +12:00
Luke D. Jones 79a45c4f10 Add to/from [f32;3] for Colour] 2022-07-25 09:51:25 +12:00
Luke D. Jones 19370215c0 Cleanup 2022-07-24 20:55:09 +12:00
Luke D. Jones 030dd661b8 Switch zbus led_mode to return AuraModeNum 2022-07-24 20:45:20 +12:00
Luke D. Jones 1fc12d9855 Make CurveData members public 2022-07-24 10:50:28 +12:00
Luke D. Jones 6c1b2b70ea Make FanCurveSet members public 2022-07-24 10:47:40 +12:00
Luke D. Jones 23f9af35bf Add Hash derive to Profile 2022-07-24 09:43:22 +12:00
Luke D. Jones 526626b80c Minor tweaks on derives 2022-07-24 09:28:00 +12:00
Luke Jones 10eaaac54b Merge branch 'sova/G713IC-led-support' into 'main'
Add LED support for G713IC

See merge request asus-linux/asusctl!124
2022-07-21 12:39:43 +00:00
SoVa 901a3ddcc9 Add LED support for G713IC 2022-07-21 14:18:33 +02:00
Luke Jones e6ebf72a11 Merge branch 'main' into 'main'
Fix some typos.

See merge request asus-linux/asusctl!123
2022-07-21 07:59:16 +00:00
成超(Cheng Chao) a313359ef6 Fix some typos. 2022-07-21 12:46:44 +08:00
36 changed files with 3705 additions and 127 deletions
+4
View File
@@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased ]
### Added
- `rog-control-center` has now been moved in to the main workspace due to
the heavy dependencies on most of the rog crates
## [4.3.0] - 2022-07-21
### Added
Generated
+1500 -73
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,5 +1,5 @@
[workspace]
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"]
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
[profile.release]
# thin = 57s, asusd = 9.0M
+2 -2
View File
@@ -90,7 +90,7 @@ where the number is a percentage.
Some options that you find in Armory Crate are available under this controller, so far there is:
- POST sound: this is the sound you here on bios boot post
- POST sound: this is the sound you hear on bios boot post
- G-Sync: this controls if the dGPU (Nvidia) is the *only* GPU, making it the main GPU and disabling the iGPU
These options are not written to the config file as they are stored in efivars. The only way to change these is to use the exposed safe dbus methods, or use the `asusctl` CLI tool.
@@ -293,7 +293,7 @@ A plain non-float integer.
## asusctl
`asusctl` is a commandline interface which intends to be the main method of interacting with `asusd`. I can be used in any place a terminal app can be used.
`asusctl` is a commandline interface which intends to be the main method of interacting with `asusd`. It can be used in any place a terminal app can be used.
This program will query `asusd` for the `Support` level of the laptop and show or hide options according to this support level.
+9
View File
@@ -11,6 +11,7 @@ datarootdir = $(prefix)/share
libdir = $(exec_prefix)/lib
zshcpl = $(datarootdir)/zsh/site-functions
BIN_ROG := rog-control-center
BIN_C := asusctl
BIN_D := asusd
BIN_U := asusd-user
@@ -39,6 +40,10 @@ distclean:
rm -rf .cargo vendor vendor.tar.xz
install:
$(INSTALL_PROGRAM) "./target/release/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)"
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).desktop" "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
$(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
@@ -68,6 +73,10 @@ install:
cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
uninstall:
rm -f "$(DESTDIR)$(bindir)/$(BIN_ROG)"
rm -r "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
rm -f "$(DESTDIR)$(bindir)/$(BIN_N)"
+2
View File
@@ -5,6 +5,8 @@
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
but can also be used with non-asus laptops with reduced features.
Now includes a GUI, `rog-control-center`.
## Kernel support
**The minimum supported kernel version is 5.15**
+12 -7
View File
@@ -10,7 +10,7 @@ use anime_cli::{AnimeActions, AnimeCommand};
use profiles_cli::{FanCurveCommand, ProfileCommand};
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, Vec2};
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraPowerDev};
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevice, AuraPowerDev};
use rog_aura::{self, AuraEffect};
use rog_dbus::RogDbusClientBlocking;
use rog_profiles::error::ProfileError;
@@ -159,12 +159,14 @@ fn do_parsed(
if let Some(cmdlist) = CliStart::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
for command in commands.iter().filter(|command| {
if supported.keyboard_led.prod_id != "1866"
&& command.trim().starts_with("led-pow-1")
if !matches!(
supported.keyboard_led.prod_id,
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
) && command.trim().starts_with("led-pow-1")
{
return false;
}
if supported.keyboard_led.prod_id != "19b6"
if supported.keyboard_led.prod_id != AuraDevice::X19B6
&& command.trim().starts_with("led-pow-2")
{
return false;
@@ -455,7 +457,10 @@ fn handle_led_power1(
return Ok(());
}
if supported.prod_id != "1866" {
if !matches!(
supported.prod_id,
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
) {
println!("These options are for keyboards of product ID 0x1866 only");
return Ok(());
}
@@ -523,8 +528,8 @@ fn handle_led_power2(
return Ok(());
}
if supported.prod_id == "1866" {
println!("This option does not apply to keyboards with product ID 0x1866")
if supported.prod_id != AuraDevice::X19B6 {
println!("This option applies only to keyboards with product ID 0x19b6")
}
let mut enabled: Vec<AuraDev19b6> = Vec::new();
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "daemon"
version = "4.3.0"
version = "4.3.2"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
+8 -8
View File
@@ -10,7 +10,7 @@ use async_trait::async_trait;
use log::{error, info, warn};
use logind_zbus::manager::ManagerProxy;
use rog_aura::{
usb::{LED_APPLY, LED_SET},
usb::{AuraDevice, LED_APPLY, LED_SET},
AuraEffect, LedBrightness, LED_MSG_LEN,
};
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
@@ -40,16 +40,16 @@ impl GetSupported for CtrlKbdLed {
let multizone_led_mode = laptop.multizone;
let per_key_led_mode = laptop.per_key;
let mut prod_id = String::new();
let mut prod_id = "";
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if let Ok(_) = Self::find_led_node(prod) {
prod_id = prod.to_string();
prod_id = *prod;
break;
}
}
LedSupportedFunctions {
prod_id,
prod_id: AuraDevice::from(prod_id),
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
stock_led_modes,
multizone_led_mode,
@@ -417,10 +417,10 @@ impl CtrlKbdLed {
let next = self.supported_modes.standard[idx];
self.config.read();
if self.config.builtins.contains_key(&next) {
self.config.current_mode = next;
self.write_current_config_mode()?;
}
// if self.config.builtins.contains_key(&next) {
self.config.current_mode = next;
self.write_current_config_mode()?;
// }
self.config.write();
}
+26 -19
View File
@@ -1,6 +1,8 @@
use std::collections::BTreeMap;
use async_trait::async_trait;
use log::warn;
use rog_aura::{usb::AuraPowerDev, AuraEffect, LedBrightness};
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness};
use zbus::{dbus_interface, Connection, SignalContext};
use super::controller::CtrlKbdLedZbus;
@@ -27,8 +29,21 @@ impl CtrlKbdLedZbus {
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// enum AuraControl {
/// ```text
/// pub struct AuraPowerDev {
/// pub x1866: Vec<AuraDev1866>,
/// pub x19b6: Vec<AuraDev19b6>,
/// }
/// pub enum AuraDev1866 {
/// Awake,
/// Keyboard,
/// Lightbar,
/// Boot,
/// Sleep,
/// }
/// enum AuraDev19b6 {
/// BootLogo,
/// BootKeyb,
/// AwakeLogo,
@@ -42,6 +57,7 @@ impl CtrlKbdLedZbus {
/// SleepBar,
/// ShutdownBar,
/// }
/// ```
async fn set_leds_power(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
@@ -166,7 +182,7 @@ impl CtrlKbdLedZbus {
// As property doesn't work for AuraPowerDev (complexity of serialization?)
// #[dbus_interface(property)]
async fn leds_power(&self) -> AuraPowerDev {
async fn leds_enabled(&self) -> AuraPowerDev {
loop {
if let Ok(ctrl) = self.0.try_lock() {
return AuraPowerDev::from(&ctrl.config.enabled);
@@ -175,29 +191,20 @@ impl CtrlKbdLedZbus {
}
/// Return the current mode data
#[dbus_interface(property)]
async fn led_mode(&self) -> String {
async fn led_mode(&self) -> AuraModeNum {
if let Ok(ctrl) = self.0.try_lock() {
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
if let Ok(json) = serde_json::to_string(&mode) {
return json;
}
}
return ctrl.config.current_mode;
}
warn!("SetKeyBacklight could not deserialise");
"SetKeyBacklight could not deserialise".to_string()
AuraModeNum::Static
}
/// Return a list of available modes
#[dbus_interface(property)]
async fn led_modes(&self) -> String {
if let Ok(ctrl) = self.0.try_lock() {
if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) {
return json;
async fn led_modes(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
loop {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.builtins.clone();
}
}
warn!("SetKeyBacklight could not deserialise");
"SetKeyBacklight could not serialise".to_string()
}
/// Return the current LED brightness
+1 -1
View File
@@ -50,7 +50,7 @@ per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM"]
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM", "G713IC"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
+16
View File
@@ -63,6 +63,22 @@ impl FromStr for Colour {
}
}
impl From<&[f32; 3]> for Colour {
fn from(c: &[f32; 3]) -> Self {
Self(
(255.0 * c[0]) as u8,
(255.0 * c[1]) as u8,
(255.0 * c[2]) as u8,
)
}
}
impl From<Colour> for [f32; 3] {
fn from(c: Colour) -> Self {
[c.0 as f32 / 255.0, c.1 as f32 / 255.0, c.2 as f32 / 255.0]
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
pub enum Speed {
+26
View File
@@ -20,6 +20,32 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
]
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum AuraDevice {
X1854,
X1869,
X1866,
X19B6,
Unknown,
}
impl From<&str> for AuraDevice {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"1866" => AuraDevice::X1866,
"1869" => AuraDevice::X1869,
"1854" => AuraDevice::X1854,
"19b6" => AuraDevice::X19B6,
"0x1866" => AuraDevice::X1866,
"0x1869" => AuraDevice::X1869,
"0x1854" => AuraDevice::X1854,
"0x19b6" => AuraDevice::X19B6,
_ => AuraDevice::Unknown,
}
}
}
/// This struct is intended as a helper to pass args to generic dbus interface
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Debug, Serialize, Deserialize)]
+45
View File
@@ -0,0 +1,45 @@
[package]
name = "rog-control-center"
version = "0.1.4"
authors = ["Luke D. Jones <luke@ljones.dev>"]
edition = "2021"
[features]
mocking = []
[dependencies]
egui = { git = "https://github.com/emilk/egui" }
eframe= { git = "https://github.com/emilk/egui" }
#eframe= { git = "https://github.com/emilk/egui", default-features = false, features = ["dark-light", "default_fonts", "wgpu"] }
rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" }
rog_supported = { path = "../rog-supported" }
# supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git" }
smol = "^1.2"
serde = "^1.0"
toml = "^0.5"
serde_json = "^1.0"
serde_derive = "^1.0"
zbus = "^2.3"
nix = "^0.20.0"
tempfile = "3.2.0"
dirs = "3.0.1"
[dependencies.notify-rust]
version = "^4.3"
default-features = false
features = ["z"]
[profile.release]
lto = true
debug = false
opt-level = 3
panic = "abort"
[profile.bench]
debug = false
opt-level = 3
+22
View File
@@ -0,0 +1,22 @@
# App template
This is a trial app cut down to bare essentials to show how to create an app that can run in the background
with some user-config options.
egui based. Keep in mind that this is very much a bit of a mess due to experimenting.
## Running
Use `WINIT_UNIX_BACKEND=x11 rog-control-center`. `WINIT_UNIX_BACKEND` is required due to window decorations not updating and the window not really being set as visible/invisible on wayland.
## Build features
For testing some features that are typically not available on all laptops:
```rust
cargo run --features mocking
```
## TODO
- Add notification watch for certain UI elements to enforce an update (for example when a user changes Aura via a hot key).
@@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=ROG Control Center
Comment=Make your ASUS ROG Laptop go Brrrrr!
Categories=Settings
Icon=rog-control-center
Exec=env WINIT_UNIX_BACKEND=x11 rog-control-center
Terminal=false
Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

+127
View File
@@ -0,0 +1,127 @@
use std::{
io::Write,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
time::Duration,
};
use egui::{Button, RichText};
use rog_dbus::RogDbusClientBlocking;
use rog_supported::SupportedFunctions;
use crate::{config::Config, get_ipc_file, page_states::PageDataStates, Page, SHOWING_GUI};
pub struct RogApp<'a> {
pub page: Page,
pub states: PageDataStates,
pub supported: SupportedFunctions,
/// Should the app begin showing the GUI
pub begin_show_gui: Arc<AtomicBool>,
/// Is the app GUI closed (and running in bg)
pub running_in_bg: bool,
// TODO: can probably just open and read whenever
pub config: Config,
pub asus_dbus: RogDbusClientBlocking<'a>,
}
impl<'a> RogApp<'a> {
/// Called once before the first frame.
pub fn new(
start_closed: bool,
config: Config,
show_gui: Arc<AtomicBool>,
states: PageDataStates,
_cc: &eframe::CreationContext<'_>,
) -> Self {
let (dbus, _) = RogDbusClientBlocking::new().unwrap();
let supported = dbus.proxies().supported().supported_functions().unwrap();
Self {
supported,
states,
page: Page::System,
begin_show_gui: show_gui,
running_in_bg: start_closed,
config,
asus_dbus: dbus,
}
}
}
impl<'a> eframe::App for RogApp<'a> {
fn on_exit_event(&mut self) -> bool {
if self.config.run_in_background {
self.running_in_bg = true;
get_ipc_file().unwrap().write_all(&[0]).unwrap();
return false;
}
true
}
/// 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: &egui::Context, frame: &mut eframe::Frame) {
let Self {
begin_show_gui: should_show_gui,
supported,
asus_dbus: dbus,
states,
..
} = self;
if states.refresh_if_notfied(supported, dbus) {
ctx.request_repaint();
}
let page = self.page;
if should_show_gui.load(Ordering::SeqCst) {
let mut ipc_file = get_ipc_file().unwrap();
ipc_file.write_all(&[SHOWING_GUI]).unwrap();
should_show_gui.store(false, Ordering::SeqCst);
frame.set_visible(true);
self.running_in_bg = false;
}
if self.running_in_bg {
// Request to draw nothing at all
ctx.request_repaint_after(Duration::from_millis(500));
frame.set_visible(false);
return;
}
// Do all GUI display after this point
self.top_bar(ctx, frame);
self.side_panel(ctx);
if let Some(err) = self.states.error.clone() {
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()
{
self.states.error = None;
}
});
});
} else if page == Page::System {
self.system_page(ctx);
} else if page == Page::AuraEffects {
self.aura_page(ctx);
} else if page == Page::AnimeMatrix {
self.anime_page(ctx);
} else if page == Page::FanCurves {
self.fan_curve_page(ctx);
}
}
}
+92
View File
@@ -0,0 +1,92 @@
use std::{
fs::{create_dir, OpenOptions},
io::{Read, Write},
};
use serde_derive::{Deserialize, Serialize};
//use log::{error, info, warn};
use crate::error::Error;
const CFG_DIR: &str = "rog";
const CFG_FILE_NAME: &str = "app-template.cfg";
#[derive(Debug, Deserialize, Serialize)]
#[serde(default)]
pub struct Config {
pub run_in_background: bool,
pub startup_in_background: bool,
pub enable_notifications: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
run_in_background: true,
startup_in_background: false,
enable_notifications: true,
}
}
}
impl Config {
pub fn load() -> Result<Config, Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push(CFG_DIR);
if !path.exists() {
create_dir(path.clone())?;
}
path.push(CFG_FILE_NAME);
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
let default = Config::default();
let t = toml::to_string_pretty(&default).unwrap();
file.write_all(t.as_bytes())?;
return Ok(default);
} else if let Ok(data) = toml::from_str::<Config>(&buf) {
return Ok(data);
}
}
Err(Error::ConfigLoadFail)
}
pub fn save(&self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push(CFG_DIR);
if !path.exists() {
create_dir(path.clone())?;
}
path.push(CFG_FILE_NAME);
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
let t = toml::to_string_pretty(&self).unwrap();
file.write_all(t.as_bytes())?;
Ok(())
}
}
+43
View File
@@ -0,0 +1,43 @@
use std::fmt;
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
Nix(nix::Error),
ConfigLoadFail,
ConfigLockFail,
XdgVars,
}
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::Nix(err) => write!(f, "Error: {}", err),
Error::ConfigLoadFail => write!(f, "Failed to load user config"),
Error::ConfigLockFail => write!(f, "Failed to lock user config"),
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
}
}
}
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io(err)
}
}
impl From<Error> for zbus::fdo::Error {
fn from(err: Error) -> Self {
zbus::fdo::Error::Failed(format!("Anime zbus error: {}", err))
}
}
impl From<nix::Error> for Error {
fn from(err: nix::Error) -> Self {
Error::Nix(err)
}
}
+77
View File
@@ -0,0 +1,77 @@
pub mod app;
use std::{
fs::{remove_dir_all, File, OpenOptions},
io::{Read, Write},
process::exit,
thread::sleep,
time::Duration,
};
pub use app::RogApp;
pub mod config;
pub mod error;
pub mod notify;
pub mod page_states;
pub mod widgets;
use nix::{sys::stat, unistd};
use tempfile::TempDir;
//use log::{error, info, warn};
pub const SHOWING_GUI: u8 = 1;
pub const SHOW_GUI: u8 = 2;
#[derive(PartialEq, Clone, Copy)]
pub enum Page {
System,
AuraEffects,
AnimeMatrix,
FanCurves,
}
/// Either exit the process, or return with a refreshed tmp-dir
pub fn on_tmp_dir_exists() -> Result<TempDir, std::io::Error> {
let mut buf = [0u8; 4];
let path = std::env::temp_dir().join("rog-gui");
let mut ipc_file = OpenOptions::new()
.read(true)
.write(true)
.create(false)
.open(path.join("ipc.pipe"))?;
// If the app is running this ends up stacked on top of SHOWING_GUI
ipc_file.write_all(&[SHOW_GUI])?;
// tiny sleep to give the app a chance to respond
sleep(Duration::from_millis(10));
ipc_file.read(&mut buf).ok();
// First entry is the actual state
if buf[0] == SHOWING_GUI {
ipc_file.write_all(&[SHOWING_GUI])?; // Store state again as we drained the fifo
exit(0);
} else if buf[0] == SHOW_GUI {
remove_dir_all(&path)?;
return tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir();
}
exit(-1);
}
pub fn get_ipc_file() -> Result<File, crate::error::Error> {
let tmp_dir = std::env::temp_dir().join("rog-gui");
let fifo_path = tmp_dir.join("ipc.pipe");
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
if !matches!(e, nix::Error::Sys(nix::errno::Errno::EEXIST)) {
return Err(e)?;
}
}
Ok(OpenOptions::new()
.read(true)
.write(true)
.truncate(true)
.open(&fifo_path)?)
}
+97
View File
@@ -0,0 +1,97 @@
use rog_control_center::{
config::Config, get_ipc_file, notify::start_notifications, on_tmp_dir_exists,
page_states::PageDataStates, RogApp, SHOW_GUI,
};
use rog_dbus::RogDbusClientBlocking;
use std::{
io::Read,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread::spawn,
time::Duration,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Startup
let mut config = Config::load()?;
let start_closed = config.startup_in_background;
if config.startup_in_background {
config.run_in_background = true;
config.save()?;
}
let (dbus, _) = RogDbusClientBlocking::new().unwrap();
let supported = dbus.proxies().supported().supported_functions().unwrap();
// Cheap method to alert to notifications rather than spinning a thread for each
// This is quite different when done in a retained mode app
let charge_notified = Arc::new(AtomicBool::new(false));
let bios_notified = Arc::new(AtomicBool::new(false));
let aura_notified = Arc::new(AtomicBool::new(false));
let anime_notified = Arc::new(AtomicBool::new(false));
let profiles_notified = Arc::new(AtomicBool::new(false));
let fans_notified = Arc::new(AtomicBool::new(false));
let notifs_enabled = Arc::new(AtomicBool::new(config.enable_notifications));
// TODO: change this to an error instead of the nested unwraps, then use to
// display a bare box app with error message.
let states = PageDataStates::new(
notifs_enabled.clone(),
charge_notified.clone(),
bios_notified.clone(),
aura_notified.clone(),
anime_notified.clone(),
profiles_notified.clone(),
fans_notified.clone(),
&supported,
&dbus,
);
if config.enable_notifications {
start_notifications(
charge_notified,
bios_notified,
aura_notified,
anime_notified,
profiles_notified,
fans_notified,
notifs_enabled,
)?;
}
// tmp-dir must live to the end of program life
let _tmp_dir = match tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir()
{
Ok(tmp) => tmp,
Err(_) => on_tmp_dir_exists().unwrap(),
};
let native_options = eframe::NativeOptions {
decorated: true,
..Default::default()
};
let should_show_gui = Arc::new(AtomicBool::new(!start_closed));
let should = should_show_gui.clone();
spawn(move || {
// Loop is blocked here until a single byte is read
loop {
let mut buf = [0u8; 4];
if get_ipc_file().unwrap().read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
should_show_gui.store(true, Ordering::SeqCst);
// Give the starting app a change to read or we'll race it
std::thread::sleep(Duration::from_millis(10));
}
}
});
eframe::run_native(
"ROG Control Center",
native_options,
Box::new(move |cc| Box::new(RogApp::new(start_closed, config, should, states, cc))),
);
}
+256
View File
@@ -0,0 +1,256 @@
//TODO: a lot of app state refresh depends on this so there needs
// to be an extra AtomicBool for checking if notifications are enabled
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_aura::AuraEffect;
use rog_dbus::{
zbus_anime::AnimeProxy, zbus_charge::ChargeProxy, zbus_led::LedProxy,
zbus_profile::ProfileProxy, zbus_rogbios::RogBiosProxy,
};
use rog_profiles::Profile;
use smol::{future, Executor};
use std::{
error::Error,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
thread::spawn,
};
use zbus::export::futures_util::StreamExt;
const NOTIF_HEADER: &str = "ROG Control";
macro_rules! notify {
($notifier:ident, $last_notif:ident, $data:expr) => {
if let Some(notif) = $last_notif.take() {
notif.close();
}
if let Ok(x) = $notifier($data) {
$last_notif.replace(x);
}
};
}
macro_rules! base_notification {
($body:expr) => {
Notification::new()
.summary(NOTIF_HEADER)
.body($body)
.timeout(2000)
.show()
};
}
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
pub fn start_notifications(
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
aura_notified: Arc<AtomicBool>,
anime_notified: Arc<AtomicBool>,
profiles_notified: Arc<AtomicBool>,
_fans_notified: Arc<AtomicBool>,
notifs_enabled: Arc<AtomicBool>,
) -> Result<(), Box<dyn std::error::Error>> {
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
let executor = Executor::new();
// BIOS notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
let bios_notified1 = bios_notified.clone();
// TODO: make a macro or generic function or something...
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = RogBiosProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_post_boot_sound().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_post_sound_notif, lock, &out.sound());
}
}
bios_notified1.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = RogBiosProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_panel_overdrive().await {
p.for_each(|_| {
bios_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
// Charge notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ChargeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_charge().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_charge_notif, lock, &out.limit);
}
}
charge_notified.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
// Profile notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ProfileProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_profile().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_thermal_notif, lock, &out.profile);
}
}
profiles_notified.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
// LED notif
let last_notif = last_notification.clone();
let aura_notif = aura_notified.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_led().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_led_notif, lock, &out.data);
}
}
aura_notif.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
let aura_notif = aura_notified.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_led().await {
p.for_each(|_| {
aura_notif.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_all_signals().await {
p.for_each(|_| {
aura_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = AnimeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_power_states().await {
p.for_each(|_| {
anime_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
spawn(move || loop {
smol::block_on(executor.tick());
});
Ok(())
}
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let x = Notification::new()
.summary("ASUS ROG")
.body(&format!(
"Thermal profile changed to {}",
profile.to_uppercase(),
))
.hint(Hint::Resident(true))
.timeout(2000)
.hint(Hint::Category("device".into()))
//.hint(Hint::Transient(true))
.icon(icon)
.show()?;
Ok(x)
}
fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!(
"Keyboard LED mode changed to {}",
ledmode.mode_name()
))
}
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("Battery charge limit changed to {}", limit))
}
fn do_post_sound_notif(on: &bool) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("BIOS Post sound {}", on))
}
+304
View File
@@ -0,0 +1,304 @@
use std::{
collections::{BTreeMap, HashMap, HashSet},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use egui::Vec2;
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum};
use rog_dbus::RogDbusClientBlocking;
use rog_profiles::{fan_curve_set::FanCurveSet, FanCurvePU, Profile};
use rog_supported::SupportedFunctions;
#[derive(Clone, Debug)]
pub struct BiosState {
/// To be shared to a thread that checks notifications.
/// It's a bit general in that it won't provide *what* was
/// updated, so the full state needs refresh
pub was_notified: Arc<AtomicBool>,
pub post_sound: bool,
pub dedicated_gfx: bool,
pub panel_overdrive: bool,
pub dgpu_disable: bool,
pub egpu_enable: bool,
}
impl BiosState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
was_notified,
post_sound: if supported.rog_bios_ctrl.post_sound {
dbus.proxies().rog_bios().post_boot_sound().unwrap() != 0
} else {
false
},
dedicated_gfx: if supported.rog_bios_ctrl.dedicated_gfx {
dbus.proxies().rog_bios().dedicated_graphic_mode().unwrap() != 0
} else {
false
},
panel_overdrive: if supported.rog_bios_ctrl.panel_overdrive {
dbus.proxies().rog_bios().panel_overdrive().unwrap() != 0
} else {
false
},
// TODO: needs supergfx
dgpu_disable: supported.rog_bios_ctrl.dgpu_disable,
egpu_enable: supported.rog_bios_ctrl.egpu_enable,
}
}
}
#[derive(Clone, Debug)]
pub struct ProfilesState {
pub was_notified: Arc<AtomicBool>,
pub list: Vec<Profile>,
pub current: Profile,
}
impl ProfilesState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
was_notified,
list: if supported.platform_profile.platform_profile {
dbus.proxies().profile().profiles().unwrap()
} else {
vec![]
},
current: if supported.platform_profile.platform_profile {
dbus.proxies().profile().active_profile().unwrap()
} else {
Profile::Balanced
},
}
}
}
#[derive(Clone, Debug)]
pub struct FanCurvesState {
pub was_notified: Arc<AtomicBool>,
pub show_curve: Profile,
pub show_graph: FanCurvePU,
pub enabled: HashSet<Profile>,
pub curves: HashMap<Profile, FanCurveSet>,
pub drag_delta: Vec2,
}
impl FanCurvesState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
let profiles = if supported.platform_profile.platform_profile {
dbus.proxies().profile().profiles().unwrap()
} else {
vec![Profile::Balanced, Profile::Quiet, Profile::Performance]
};
let enabled = if supported.platform_profile.fan_curves {
HashSet::from_iter(
dbus.proxies()
.profile()
.enabled_fan_profiles()
.unwrap()
.iter()
.cloned(),
)
} else {
HashSet::from([Profile::Balanced, Profile::Quiet, Profile::Performance])
};
let mut curves: HashMap<Profile, FanCurveSet> = HashMap::new();
profiles.iter().for_each(|p| {
if supported.platform_profile.fan_curves {
let curve = dbus.proxies().profile().fan_curve_data(*p).unwrap();
curves.insert(*p, curve);
} else {
let mut curve = FanCurveSet::default();
curve.cpu.pwm = [30, 40, 60, 100, 140, 180, 200, 250];
curve.cpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
curve.gpu.pwm = [40, 80, 100, 140, 170, 200, 230, 250];
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
curves.insert(*p, curve);
}
});
let show_curve = if supported.platform_profile.fan_curves {
dbus.proxies().profile().active_profile().unwrap()
} else {
Profile::Balanced
};
Self {
was_notified,
show_curve,
show_graph: FanCurvePU::CPU,
enabled,
curves,
drag_delta: Vec2::default(),
}
}
}
#[derive(Clone, Debug)]
pub struct AuraState {
pub was_notified: Arc<AtomicBool>,
pub current_mode: AuraModeNum,
pub modes: BTreeMap<AuraModeNum, AuraEffect>,
pub enabled: AuraPowerDev,
/// Brightness from 0-3
pub bright: i16,
}
impl AuraState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
was_notified,
current_mode: if !supported.keyboard_led.stock_led_modes.is_empty() {
dbus.proxies().led().led_mode().unwrap()
} else {
AuraModeNum::Static
},
modes: if !supported.keyboard_led.stock_led_modes.is_empty() {
dbus.proxies().led().led_modes().unwrap()
} else {
BTreeMap::new()
},
enabled: dbus.proxies().led().leds_enabled().unwrap(),
bright: if !supported.keyboard_led.brightness_set {
dbus.proxies().led().led_brightness().unwrap()
} else {
2
},
}
}
}
#[derive(Clone, Debug)]
pub struct AnimeState {
pub was_notified: Arc<AtomicBool>,
pub bright: u8,
pub boot: bool,
pub awake: bool,
pub sleep: bool,
}
impl AnimeState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
was_notified,
boot: if supported.anime_ctrl.0 {
dbus.proxies().anime().boot_enabled().unwrap()
} else {
false
},
awake: if supported.anime_ctrl.0 {
dbus.proxies().anime().awake_enabled().unwrap()
} else {
false
},
// TODO:
sleep: false,
bright: 200,
}
}
}
#[derive(Debug)]
pub struct PageDataStates {
pub notifs_enabled: Arc<AtomicBool>,
pub was_notified: Arc<AtomicBool>,
/// Because much of the app state here is the same as `RogBiosSupportedFunctions`
/// we can re-use that structure.
pub bios: BiosState,
pub aura: AuraState,
pub anime: AnimeState,
pub profiles: ProfilesState,
pub fan_curves: FanCurvesState,
pub charge_limit: i16,
pub error: Option<String>,
}
impl PageDataStates {
pub fn new(
notifs_enabled: Arc<AtomicBool>,
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
aura_notified: Arc<AtomicBool>,
anime_notified: Arc<AtomicBool>,
profiles_notified: Arc<AtomicBool>,
fans_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
notifs_enabled,
was_notified: charge_notified,
charge_limit: dbus.proxies().charge().limit().unwrap(),
bios: BiosState::new(bios_notified, supported, dbus),
aura: AuraState::new(aura_notified, supported, dbus),
anime: AnimeState::new(anime_notified, supported, dbus),
profiles: ProfilesState::new(profiles_notified, supported, dbus),
fan_curves: FanCurvesState::new(fans_notified, supported, dbus),
error: None,
}
}
pub fn refresh_if_notfied(
&mut self,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> bool {
let mut notified = false;
if self.was_notified.load(Ordering::SeqCst) {
self.charge_limit = dbus.proxies().charge().limit().unwrap();
self.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.aura.was_notified.load(Ordering::SeqCst) {
self.aura = AuraState::new(self.aura.was_notified.clone(), supported, dbus);
self.aura.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.bios.was_notified.load(Ordering::SeqCst) {
self.bios = BiosState::new(self.bios.was_notified.clone(), supported, dbus);
self.bios.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.profiles.was_notified.load(Ordering::SeqCst) {
self.profiles = ProfilesState::new(self.profiles.was_notified.clone(), supported, dbus);
self.profiles.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.fan_curves.was_notified.load(Ordering::SeqCst) {
self.fan_curves =
FanCurvesState::new(self.fan_curves.was_notified.clone(), supported, dbus);
self.fan_curves.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
notified
}
}
@@ -0,0 +1,77 @@
use egui::RichText;
use crate::RogApp;
impl<'a> RogApp<'a> {
pub fn anime_page(&mut self, ctx: &egui::Context) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("AniMe Matrix Settings");
ui.label("Options are incomplete. Awake + Boot should work");
let Self {
states,
asus_dbus: dbus,
..
} = self;
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("Brightness").size(h));
});
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));
});
});
ui.vertical(|ui| {
ui.set_row_height(22.0);
ui.horizontal_wrapped(|ui| {
if ui
.add(egui::Slider::new(&mut states.anime.bright, 0..=254))
.changed()
{
changed = true;
}
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.boot, "Enable").changed() {
dbus.proxies()
.anime()
.set_boot_on_off(states.anime.boot)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.awake, "Enable").changed() {
dbus.proxies()
.anime()
.set_on_off(states.anime.awake)
.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;
}
});
});
});
});
}
}
+480
View File
@@ -0,0 +1,480 @@
use egui::{RichText, Ui};
use rog_aura::{
usb::{AuraDev1866, AuraDev19b6, AuraDevice, AuraPowerDev},
AuraModeNum, AuraZone, Colour, Speed,
};
use rog_dbus::RogDbusClientBlocking;
use rog_supported::SupportedFunctions;
use crate::{
page_states::{AuraState, PageDataStates},
RogApp,
};
impl<'a> RogApp<'a> {
pub fn aura_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
..
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
Self::aura_power(supported, states, dbus, ui);
ui.separator();
Self::aura_modes(supported, states, dbus, ui);
});
}
fn aura_power(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
match supported.keyboard_led.prod_id {
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866 => {
Self::aura_power1(supported, states, dbus, ui)
}
AuraDevice::X19B6 => Self::aura_power2(supported, states, dbus, ui),
AuraDevice::Unknown => {}
}
}
fn aura_power1(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
let enabled_states = &mut states.aura.enabled;
ui.heading("Aura go brrrrr! (incomplete)");
ui.separator();
let boot = &mut enabled_states.x1866.contains(&AuraDev1866::Boot);
let sleep = &mut enabled_states.x1866.contains(&AuraDev1866::Sleep);
let keyboard = &mut enabled_states.x1866.contains(&AuraDev1866::Keyboard);
let lightbar = &mut enabled_states.x1866.contains(&AuraDev1866::Lightbar);
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(boot, "Enable").changed() {
changed = true;
}
});
ui.horizontal_wrapped(|ui| {
if ui.toggle_value(keyboard, "Keyboard").changed() {
changed = true;
}
if !supported.keyboard_led.multizone_led_mode.is_empty() {
if ui.toggle_value(lightbar, "Lightbar").changed() {
changed = true;
}
}
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(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 {
let mut enabled = Vec::new();
let mut disabled = Vec::new();
let mut modify = |b: bool, a: AuraDev1866| {
if b {
enabled.push(a);
if !enabled_states.x1866.contains(&a) {
enabled_states.x1866.push(a);
}
} else {
disabled.push(a);
// This would be so much better as a hashset
if enabled_states.x1866.contains(&a) {
let mut idx = 0;
for (i, n) in enabled_states.x1866.iter().enumerate() {
if *n == a {
idx = i;
break;
}
}
enabled_states.x1866.remove(idx);
}
}
};
modify(*boot, AuraDev1866::Boot);
modify(*sleep, AuraDev1866::Sleep);
modify(*keyboard, AuraDev1866::Keyboard);
if !supported.keyboard_led.multizone_led_mode.is_empty() {
modify(*lightbar, AuraDev1866::Lightbar);
}
let mut send = |enable: bool, data: Vec<AuraDev1866>| {
let options = AuraPowerDev {
x1866: data,
x19b6: vec![],
};
// build data to send
dbus.proxies()
.led()
.set_leds_power(options, enable)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
};
send(true, enabled);
send(false, disabled);
}
}
fn aura_power2(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
let enabled_states = &mut states.aura.enabled;
ui.heading("Lights go brrrrr! (incomplete)");
ui.separator();
let has_logo = supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::Logo);
let has_lightbar = supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::BarLeft)
|| supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::BarRight);
let boot_bar = &mut enabled_states.x19b6.contains(&AuraDev19b6::BootBar);
let boot_logo = &mut enabled_states.x19b6.contains(&AuraDev19b6::BootLogo);
let boot_keyb = &mut enabled_states.x19b6.contains(&AuraDev19b6::BootKeyb);
let awake_bar = &mut enabled_states.x19b6.contains(&AuraDev19b6::AwakeBar);
let awake_logo = &mut enabled_states.x19b6.contains(&AuraDev19b6::AwakeLogo);
let awake_keyb = &mut enabled_states.x19b6.contains(&AuraDev19b6::AwakeKeyb);
let sleep_bar = &mut enabled_states.x19b6.contains(&AuraDev19b6::SleepBar);
let sleep_logo = &mut enabled_states.x19b6.contains(&AuraDev19b6::SleepLogo);
let sleep_keyb = &mut enabled_states.x19b6.contains(&AuraDev19b6::SleepKeyb);
let mut changed = false;
let mut item = |keyboard: &mut bool, logo: &mut bool, lightbar: &mut bool, ui: &mut Ui| {
ui.horizontal_wrapped(|ui| {
if ui.checkbox(keyboard, "Keyboard").changed() {
changed = true;
}
if has_logo && ui.checkbox(logo, "Logo").changed() {
changed = true;
}
if has_lightbar && ui.checkbox(lightbar, "Lightbar").changed() {
changed = true;
}
});
};
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));
});
});
ui.vertical(|ui| {
ui.set_row_height(22.0);
item(boot_keyb, boot_logo, boot_bar, ui);
item(awake_keyb, awake_logo, awake_bar, ui);
item(sleep_keyb, sleep_logo, sleep_bar, ui);
});
});
if changed {
let mut enabled = Vec::new();
let mut disabled = Vec::new();
let mut modify = |b: bool, a: AuraDev19b6| {
if b {
enabled.push(a);
if !enabled_states.x19b6.contains(&a) {
enabled_states.x19b6.push(a);
}
} else {
disabled.push(a);
// This would be so much better as a hashset
if enabled_states.x19b6.contains(&a) {
let mut idx = 0;
for (i, n) in enabled_states.x19b6.iter().enumerate() {
if *n == a {
idx = i;
break;
}
}
enabled_states.x1866.remove(idx);
}
}
};
modify(*boot_keyb, AuraDev19b6::BootKeyb);
modify(*sleep_keyb, AuraDev19b6::SleepKeyb);
modify(*awake_keyb, AuraDev19b6::AwakeKeyb);
if supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::Logo)
{
modify(*boot_logo, AuraDev19b6::BootLogo);
modify(*sleep_logo, AuraDev19b6::SleepLogo);
modify(*awake_logo, AuraDev19b6::AwakeLogo);
}
if supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::BarLeft)
{
modify(*boot_bar, AuraDev19b6::BootBar);
modify(*sleep_bar, AuraDev19b6::SleepBar);
modify(*awake_bar, AuraDev19b6::AwakeBar);
}
let mut send = |enable: bool, data: Vec<AuraDev19b6>| {
let options = AuraPowerDev {
x1866: vec![],
x19b6: data,
};
// build data to send
dbus.proxies()
.led()
.set_leds_power(options, enable)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
};
send(true, enabled);
send(false, disabled);
}
}
fn aura_modes(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
let mut changed = false;
let mut selected = states.aura.current_mode;
let has_keyzones = supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::Key2);
let has_logo = supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::Logo);
let has_lightbar = supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::BarLeft)
|| supported
.keyboard_led
.multizone_led_mode
.contains(&AuraZone::BarRight);
ui.heading("Aura modes");
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);
}
});
// TODO: Need some sort of mapping to enable options only if
// they actually work.
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| {
ui.selectable_value(&mut effect.speed, a, format!("{:?}", a));
};
let mut dir_button = |a: rog_aura::Direction, ui: &mut Ui| {
ui.selectable_value(&mut effect.direction, a, format!("{:?}", a));
};
let mut c1: [f32; 3] = effect.colour1.into();
let mut c2: [f32; 3] = effect.colour2.into();
ui.separator();
ui.horizontal_wrapped(|ui| {
ui.vertical(|ui| {
let h = 16.0;
ui.set_row_height(22.0);
if has_keyzones || has_lightbar || has_logo {
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Zone").size(h));
});
}
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Colour 1").size(h));
});
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Colour 2").size(h));
});
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Speed").size(h));
});
ui.horizontal_wrapped(|ui| {
ui.label(RichText::new("Direction").size(h));
});
});
ui.vertical(|ui| {
ui.set_row_height(22.0);
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);
}
});
}
egui::color_picker::color_edit_button_rgb(ui, &mut c1);
egui::color_picker::color_edit_button_rgb(ui, &mut c2);
ui.horizontal_wrapped(|ui| {
speed_button(Speed::Low, ui);
speed_button(Speed::Med, ui);
speed_button(Speed::High, ui);
});
ui.horizontal_wrapped(|ui| {
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);
});
});
});
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() {
let notif = states.aura.was_notified.clone();
states.aura.modes = AuraState::new(notif, supported, dbus).modes;
}
if ui.add(egui::Button::new("Apply")).clicked() {
changed = true;
}
});
// 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(egui::Button::new("Cancel")).clicked() {
// let notif = states.aura.was_notified.clone();
// states.aura.modes = AuraState::new(notif, supported, dbus).modes;
// }
// if ui.add(egui::Button::new("Apply")).clicked() {
// changed = true;
// }
// });
// });
if changed {
states.aura.current_mode = selected;
dbus.proxies()
.led()
.set_led_mode(states.aura.modes.get(&selected).unwrap())
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
}
}
@@ -0,0 +1,199 @@
use crate::{
page_states::{FanCurvesState, ProfilesState},
RogApp,
};
use egui::{plot::Points, Ui};
use rog_dbus::RogDbusClientBlocking;
use rog_profiles::{FanCurvePU, Profile};
use rog_supported::SupportedFunctions;
impl<'a> RogApp<'a> {
pub fn fan_curve_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
..
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Custom fan curves");
ui.label("A fan curve is only active when the related profile is active and the curve is enabled");
Self::fan_curve(
supported,
&mut states.profiles,
&mut states.fan_curves,
dbus, &mut states.error,
ui,
);
Self::fan_graphs(&mut states.profiles, &mut states.fan_curves, dbus, &mut states.error, ui);
});
}
fn fan_curve(
supported: &SupportedFunctions,
profiles: &mut ProfilesState,
curves: &mut FanCurvesState,
dbus: &RogDbusClientBlocking,
do_error: &mut Option<String>,
ui: &mut Ui,
) {
ui.separator();
ui.label("Enabled fan-curves");
let mut changed = false;
ui.horizontal(|ui| {
let mut item = |p: Profile, _curves: &mut FanCurvesState, mut checked: bool| {
if ui
.add(egui::Checkbox::new(&mut checked, format!("{:?}", p)))
.changed()
{
dbus.proxies()
.profile()
.set_fan_curve_enabled(p, checked)
.map_err(|err| {
*do_error = Some(err.to_string());
})
.ok();
#[cfg(feature = "mocking")]
if !checked {
_curves.enabled.remove(&p);
} else {
_curves.enabled.insert(p);
}
changed = true;
}
};
for f in profiles.list.iter() {
item(*f, curves, curves.enabled.contains(f));
}
});
if changed {
// Need to update app data if change made
#[cfg(not(feature = "mocking"))]
{
let notif = curves.was_notified.clone();
*curves = FanCurvesState::new(notif, supported, dbus);
}
}
}
fn fan_graphs(
profiles: &mut ProfilesState,
curves: &mut FanCurvesState,
dbus: &RogDbusClientBlocking,
do_error: &mut Option<String>,
ui: &mut Ui,
) {
ui.separator();
let mut item = |p: Profile, ui: &mut Ui| {
ui.selectable_value(&mut curves.show_curve, p, format!("{p:?}"));
};
ui.horizontal_wrapped(|ui| {
for a in curves.curves.iter() {
item(*a.0, ui);
}
ui.selectable_value(
&mut curves.show_graph,
FanCurvePU::CPU,
format!("{:?}", FanCurvePU::CPU),
);
ui.selectable_value(
&mut curves.show_graph,
FanCurvePU::GPU,
format!("{:?}", FanCurvePU::GPU),
);
});
let curve = curves.curves.get_mut(&curves.show_curve).unwrap();
use egui::plot::{Line, Plot, PlotPoints};
let data = if curves.show_graph == FanCurvePU::CPU {
&mut curve.cpu
} else {
&mut curve.gpu
};
let points = 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]
});
let line = Line::new(PlotPoints::from_iter(points.clone())).width(2.0);
let points = Points::new(PlotPoints::from_iter(points)).radius(3.0);
Plot::new("my_plot")
.view_aspect(2.0)
// .center_x_axis(true)
// .center_y_axis(true)
.include_x(0.0)
.include_x(110.0)
.include_y(0.0)
.include_y(110.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 = 255;
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)
});
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui.add(egui::Button::new("Apply Fan-curve")).clicked() {
#[cfg(not(feature = "mocking"))]
dbus.proxies()
.profile()
.set_fan_curve(profiles.current, data.clone())
.map_err(|err| {
*do_error = Some(err.to_string());
})
.ok();
#[cfg(feature = "mocking")]
dbg!("Applied");
}
});
}
}
+13
View File
@@ -0,0 +1,13 @@
mod anime_page;
mod aura_page;
mod fan_curve_page;
mod side_panel;
mod system_page;
mod top_bar;
pub use anime_page::*;
pub use aura_page::*;
pub use fan_curve_page::*;
pub use side_panel::*;
pub use system_page::*;
pub use top_bar::*;
@@ -0,0 +1,62 @@
use crate::{Page, RogApp};
impl<'a> RogApp<'a> {
pub fn side_panel(&mut self, ctx: &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.platform_profile.fan_curves || cfg!(feature = "mocking") {
ui.separator();
if ui
.selectable_value(page, Page::FanCurves, "Fan Curves")
.clicked()
{
*page = Page::FanCurves;
}
}
if !self.supported.keyboard_led.stock_led_modes.is_empty()
|| cfg!(feature = "mocking")
{
ui.separator();
if ui
.selectable_value(page, Page::AuraEffects, "Keyboard Aura")
.clicked()
{
*page = Page::AuraEffects;
}
}
if self.supported.anime_ctrl.0 || cfg!(feature = "mocking") {
ui.separator();
if ui
.selectable_value(page, Page::AnimeMatrix, "AniMe Matrix")
.clicked()
{
*page = Page::AnimeMatrix;
}
}
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/rog-gui");
});
});
});
}
}
@@ -0,0 +1,135 @@
use crate::{page_states::PageDataStates, RogApp};
use egui::Ui;
use rog_dbus::RogDbusClientBlocking;
use rog_profiles::Profile;
impl<'a> RogApp<'a> {
pub fn system_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
..
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
// The central panel the region left after adding TopPanel's and SidePanel's
ui.heading("Experimental application for asusd");
ui.horizontal(|ui| {
egui::global_dark_light_mode_buttons(ui);
egui::warn_if_debug_build(ui);
});
ui.separator();
egui::ScrollArea::vertical().show(ui, |ui| {
ui.heading("Charge control");
let slider = egui::Slider::new(&mut states.charge_limit, 20..=100)
.text("Limit")
.step_by(1.0);
if ui.add(slider).drag_released() {
dbus.proxies()
.charge()
.set_limit(states.charge_limit as u8)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
ui.separator();
ui.heading("Bios options");
if supported.rog_bios_ctrl.post_sound {
if ui
.add(egui::Checkbox::new(
&mut states.bios.post_sound,
"POST sound",
))
.changed()
{
dbus.proxies()
.rog_bios()
.set_post_boot_sound(states.bios.post_sound)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
}
if supported.rog_bios_ctrl.post_sound {
if ui
.add(egui::Checkbox::new(
&mut states.bios.panel_overdrive,
"Panel overdrive",
))
.changed()
{
dbus.proxies()
.rog_bios()
.set_panel_overdrive(states.bios.panel_overdrive)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
}
if supported.rog_bios_ctrl.dedicated_gfx {
if ui
.add(egui::Checkbox::new(
&mut states.bios.dedicated_gfx,
"G-Sync Dedicated GPU mode",
))
.changed()
{
dbus.proxies()
.rog_bios()
.set_dedicated_graphic_mode(states.bios.dedicated_gfx)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
}
if supported.platform_profile.platform_profile {
Self::platform_profile(states, dbus, ui);
}
});
});
}
fn platform_profile(states: &mut PageDataStates, dbus: &RogDbusClientBlocking, ui: &mut Ui) {
ui.separator();
ui.heading("Platform profile");
let mut changed = false;
let mut item = |p: Profile, ui: &mut Ui| {
if ui
.selectable_value(&mut states.profiles.current, p, format!("{p:?}"))
.clicked()
{
changed = true;
}
};
ui.horizontal_wrapped(|ui| {
for a in states.profiles.list.iter() {
item(*a, ui);
}
});
if changed {
dbus.proxies()
.profile()
.set_active_profile(states.profiles.current)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
};
}
}
+42
View File
@@ -0,0 +1,42 @@
use std::sync::atomic::Ordering;
use crate::RogApp;
impl<'a> RogApp<'a> {
pub fn top_bar(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
let Self { states, config, .. } = self;
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.menu_button("File", |ui| {
if ui.button("Quit").clicked() {
frame.quit();
}
});
ui.menu_button("Settings", |ui| {
if ui
.checkbox(&mut config.run_in_background, "Run in Background")
.clicked()
|| ui
.checkbox(&mut config.startup_in_background, "Startup Hidden")
.clicked()
|| ui
.checkbox(&mut config.enable_notifications, "Enable Notifications")
.clicked()
{
states
.notifs_enabled
.store(config.enable_notifications, Ordering::SeqCst);
config
.save()
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
});
});
});
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "rog_dbus"
version = "4.2.0"
version = "4.2.1"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
+5 -5
View File
@@ -19,10 +19,12 @@
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use std::collections::BTreeMap;
use zbus::{blocking::Connection, Result};
use zbus_macros::dbus_proxy;
use rog_aura::{usb::AuraPowerDev, AuraEffect, KeyColourArray, LedBrightness};
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, KeyColourArray, LedBrightness};
const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 FPS
@@ -63,12 +65,10 @@ trait Led {
fn led_brightness(&self) -> zbus::Result<i16>;
/// LedMode property
#[dbus_proxy(property)]
fn led_mode(&self) -> zbus::Result<String>;
fn led_mode(&self) -> zbus::Result<AuraModeNum>;
/// LedModes property
#[dbus_proxy(property)]
fn led_modes(&self) -> zbus::Result<String>;
fn led_modes(&self) -> zbus::Result<BTreeMap<AuraModeNum, AuraEffect>>;
// As property doesn't work for AuraPowerDev (complexity of serialization?)
// #[dbus_proxy(property)]
+6 -6
View File
@@ -29,9 +29,9 @@ pub(crate) fn temp_str(fan: char, index: usize) -> String {
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
pub struct CurveData {
pub(crate) fan: FanCurvePU,
pub(crate) pwm: [u8; 8],
pub(crate) temp: [u8; 8],
pub fan: FanCurvePU,
pub pwm: [u8; 8],
pub temp: [u8; 8],
}
impl std::str::FromStr for CurveData {
@@ -166,9 +166,9 @@ impl CurveData {
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct FanCurveSet {
pub(crate) enabled: bool,
pub(crate) cpu: CurveData,
pub(crate) gpu: CurveData,
pub enabled: bool,
pub cpu: CurveData,
pub gpu: CurveData,
}
impl Default for FanCurveSet {
+1 -1
View File
@@ -38,7 +38,7 @@ pub fn find_fan_curve_node() -> Result<Option<Device>, ProfileError> {
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Copy)]
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum Profile {
Balanced,
Performance,
+2 -2
View File
@@ -1,6 +1,6 @@
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
use rog_aura::{AuraModeNum, AuraZone};
use rog_aura::{usb::AuraDevice, AuraModeNum, AuraZone};
use serde_derive::{Deserialize, Serialize};
use std::fmt;
use zvariant_derive::Type;
@@ -30,7 +30,7 @@ pub struct PlatformProfileFunctions {
#[derive(Serialize, Deserialize, Type, Debug)]
pub struct LedSupportedFunctions {
pub prod_id: String,
pub prod_id: AuraDevice,
pub brightness_set: bool,
pub stock_led_modes: Vec<AuraModeNum>,
pub multizone_led_mode: Vec<AuraZone>,