Compare commits

...

30 Commits

Author SHA1 Message Date
Luke D. Jones
773c9902a5 Release 4.3.3 2022-08-02 15:11:02 +12:00
Luke D. Jones
e05d5bd143 Version bump. Add early-error display 2022-08-02 15:09:25 +12:00
Luke D. Jones
3e244d7d3d ROGCC: effect visuals. daemon: support TUF RGB 2022-08-02 14:25:27 +12:00
Luke D. Jones
ba4589f986 ROGCC: effect visual test 2022-08-02 09:27:10 +12:00
Luke Jones
083134fc73 Merge branch 'support-g713rw' into 'main'
Add G713RW to asusd-ledmodes.toml

See merge request asus-linux/asusctl!125
2022-07-31 21:27:43 +00:00
Luke Jones
3cc04fba60 Merge branch 'danielphan2003-main-patch-53505' into 'main'
Use INSTALL_DATA for toml and gif files

See merge request asus-linux/asusctl!127
2022-07-31 21:27:17 +00:00
Daniel Phan
3a00e4f1a3 Use INSTALL_DATA for toml and gif files 2022-07-31 15:34:25 +00:00
Luke D. Jones
eb78fb613c New udev rules to work with both TUF and ROG 2022-07-30 23:08:47 +12:00
Luke D. Jones
d0b9aee85a daemon: Re-enable aura control for TUF
Closes #228
2022-07-30 20:52:43 +12:00
Luke Jones
3e94ef05fb Merge branch 'fix/anime-brightness-ignores-first-6-leds' into 'main'
Fix brightness setting ignoring the first 6 leds

Closes #230

See merge request asus-linux/asusctl!126
2022-07-29 21:15:06 +00:00
Ivan Voskoboinyk
fbb025875b Fix brightness setting ignoring the first 6 leds
Fixes #230
2022-07-29 15:29:16 +00:00
Yevhen Kolomeiko
ae816bd13c Add G713RW to asusd-ledmodes.toml 2022-07-29 11:41:19 +00:00
Luke D. Jones
14f0693511 rog-aura: add gap between numpad on gl504 2022-07-29 19:22:47 +12:00
Luke D. Jones
de7fb4a942 ROGCC: use the correct colourspace for colour picker 2022-07-29 18:59:52 +12:00
Luke D. Jones
4164b4645d ROGCC: split keyboard layout into widget 2022-07-29 16:08:55 +12:00
Luke D. Jones
649b14fd0d rog-aura: stand-off the rog row 2022-07-29 16:03:39 +12:00
Luke D. Jones
6d97ef13a1 rog-aura: adjustment of layouts and key sizes 2022-07-29 15:56:03 +12:00
Luke D. Jones
7abad979c8 rog-aura: adjustment of layouts and key sizes 2022-07-29 15:35:03 +12:00
Luke D. Jones
0ec1574219 rog-aura: Cleanup layouts, add gl504_US.toml 2022-07-29 12:07:16 +12:00
Luke D. Jones
03042dd5c3 Remove accidental board name test 2022-07-29 11:26:38 +12:00
Luke D. Jones
3330e4973f rog-aura: add proper labels for keys via &str into 2022-07-29 11:15:37 +12:00
Luke D. Jones
5e06aeabe9 rog-aura: fix up G533 layout 2022-07-29 10:54:59 +12:00
Luke D. Jones
e99d8766fc ROGCC: rog-aura: Keyboard layout templates and definitions
This also removes shell completitions as these are not maintained.
2022-07-29 09:19:49 +12:00
Luke D. Jones
8f65b7e334 ROGCC: add enable/disable aura options depending on mode 2022-07-27 11:39:55 +12:00
Luke D. Jones
5a54b830bf ROGCC: style change 2022-07-26 22:35:51 +12:00
Luke D. Jones
85e08510f7 ROGCC: style change 2022-07-26 22:20:00 +12:00
Luke D. Jones
d56eeb7fb2 ROGCC: split widgets from pages 2022-07-26 21:39:30 +12:00
Luke D. Jones
bbc520a7f2 Update deps 2022-07-26 19:30:50 +12:00
Luke D. Jones
10e43c64ca ROGCC: rename config file 2022-07-26 19:16:46 +12:00
Luke D. Jones
38be25174a Add verbose output for fan-curve detection. Add mocking to GUI.
asusd: Verbose output of fan-curves on startup
ROGCC: Try to mock more of GUI state
2022-07-26 18:59:21 +12:00
68 changed files with 4380 additions and 1470 deletions

View File

@@ -5,10 +5,17 @@ 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 ]
## [4.3.3] - 2022-08-02
### Added
- `rog-control-center` has now been moved in to the main workspace due to
the heavy dependencies on most of the rog crates
- Preliminary support of TUF RGB keyboards + power states
- Support for G713RW LED modes (Author: jarvis2709)
- Support for G713IC LED modes
### Changed
- The udev rules have been changed to make asusd load with all gamer variants when asus-nb-wmi is loaded
- TUF, ROG, Zephyrus, Strix
## [4.3.0] - 2022-07-21
### Added

67
Cargo.lock generated
View File

@@ -89,7 +89,7 @@ dependencies = [
[[package]]
name = "asusctl"
version = "4.3.0"
version = "4.3.3"
dependencies = [
"daemon",
"gif",
@@ -268,9 +268,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "az"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4"
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
[[package]]
name = "bitflags"
@@ -542,7 +542,7 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "daemon"
version = "4.3.2"
version = "4.3.3"
dependencies = [
"async-trait",
"env_logger",
@@ -740,7 +740,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "eframe"
version = "0.18.0"
source = "git+https://github.com/emilk/egui#0bf9fc9428423b375ae079782c095504e85ca42c"
source = "git+https://github.com/emilk/egui#a7012cf8a628d125b211fd164ac12fbb898f316e"
dependencies = [
"bytemuck",
"dark-light",
@@ -761,7 +761,7 @@ dependencies = [
[[package]]
name = "egui"
version = "0.18.1"
source = "git+https://github.com/emilk/egui#0bf9fc9428423b375ae079782c095504e85ca42c"
source = "git+https://github.com/emilk/egui#a7012cf8a628d125b211fd164ac12fbb898f316e"
dependencies = [
"ahash 0.7.6",
"epaint",
@@ -772,7 +772,7 @@ dependencies = [
[[package]]
name = "egui-winit"
version = "0.18.0"
source = "git+https://github.com/emilk/egui#0bf9fc9428423b375ae079782c095504e85ca42c"
source = "git+https://github.com/emilk/egui#a7012cf8a628d125b211fd164ac12fbb898f316e"
dependencies = [
"arboard",
"egui",
@@ -786,7 +786,7 @@ dependencies = [
[[package]]
name = "egui_glow"
version = "0.18.1"
source = "git+https://github.com/emilk/egui#0bf9fc9428423b375ae079782c095504e85ca42c"
source = "git+https://github.com/emilk/egui#a7012cf8a628d125b211fd164ac12fbb898f316e"
dependencies = [
"bytemuck",
"egui",
@@ -800,7 +800,7 @@ dependencies = [
[[package]]
name = "emath"
version = "0.18.0"
source = "git+https://github.com/emilk/egui#0bf9fc9428423b375ae079782c095504e85ca42c"
source = "git+https://github.com/emilk/egui#a7012cf8a628d125b211fd164ac12fbb898f316e"
dependencies = [
"bytemuck",
]
@@ -865,7 +865,7 @@ dependencies = [
[[package]]
name = "epaint"
version = "0.18.1"
source = "git+https://github.com/emilk/egui#0bf9fc9428423b375ae079782c095504e85ca42c"
source = "git+https://github.com/emilk/egui#a7012cf8a628d125b211fd164ac12fbb898f316e"
dependencies = [
"ab_glyph",
"ahash 0.7.6",
@@ -1038,9 +1038,9 @@ dependencies = [
[[package]]
name = "glam"
version = "0.20.5"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f"
checksum = "781aa11be58ef14b0cd7326618afcbd9cdb5ba686bdab7193d87cdc322cd7033"
dependencies = [
"serde",
]
@@ -1245,9 +1245,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "js-sys"
version = "0.3.58"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
dependencies = [
"wasm-bindgen",
]
@@ -1850,9 +1850,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.41"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdcc2916cde080c1876ff40292a396541241fe0072ef928cd76582e9ea5d60d2"
checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
dependencies = [
"unicode-ident",
]
@@ -1953,7 +1953,7 @@ dependencies = [
[[package]]
name = "rog-control-center"
version = "0.1.4"
version = "1.0.0"
dependencies = [
"dirs 3.0.2",
"eframe",
@@ -1992,10 +1992,11 @@ dependencies = [
[[package]]
name = "rog_aura"
version = "1.2.0"
version = "1.2.2"
dependencies = [
"serde",
"serde_derive",
"toml",
"zvariant",
]
@@ -2547,9 +2548,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.81"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
@@ -2557,13 +2558,13 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.81"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
@@ -2572,9 +2573,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.31"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f"
checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@@ -2584,9 +2585,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.81"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2594,9 +2595,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.81"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
dependencies = [
"proc-macro2",
"quote",
@@ -2607,9 +2608,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.81"
version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
[[package]]
name = "wayland-client"
@@ -2696,9 +2697,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.58"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
dependencies = [
"js-sys",
"wasm-bindgen",

View File

@@ -43,6 +43,7 @@ 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"
cd rog-aura/data/layouts && find . -type f -name "*.toml" -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/rog-gui/layouts/{}" \;
$(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
@@ -68,9 +69,7 @@ install:
$(INSTALL_DATA) "./data/icons/scalable/gpu-vfio.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
$(INSTALL_DATA) "./data/icons/scalable/notification-reboot.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
$(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl"
$(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
cd rog-anime/data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
cd rog-anime/data && find "./anime" -type f -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
uninstall:
rm -f "$(DESTDIR)$(bindir)/$(BIN_ROG)"
@@ -94,9 +93,8 @@ uninstall:
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-nvidia.svg"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-vfio.svg"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/notification-reboot.svg"
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish"
rm -rf "$(DESTDIR)$(datarootdir)/asusd"
rm -rf "$(DESTDIR)$(datarootdir)/rog-gui"
update:
cargo update

View File

@@ -9,9 +9,7 @@ Now includes a GUI, `rog-control-center`.
## Kernel support
**The minimum supported kernel version is 5.15**
Fan curve control on laptops with this feature require [this patch](https://lkml.org/lkml/2021/10/23/250) which has been merged for 5.17 upstream.
**The minimum supported kernel version is 5.17**
## Goals
@@ -44,6 +42,16 @@ then it may work without tweaks. Technically all other functions except the LED
and AniMe parts should work regardless of your latop make. Eventually this project
will probably suffer another rename once it becomes generic enough to do so.
### Unsupported (e.g, TUF laptops)
- `sudo systemctl mask asusd && sudo systemctl stop asusd`
- `copy asusd-alt.service to /etc/systemd/system/`
- `sudo systemctl enable asusd-alt`
- `sudo systemctl start asusd-alt`
- `asusctl -s`
`asusd-alt.service` is in the `data` dir of this repo.
## Implemented
- [X] System daemon

View File

@@ -1,6 +1,6 @@
[package]
name = "asusctl"
version = "4.3.0"
version = "4.3.3"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
@@ -21,6 +21,6 @@ sysfs-class = "^0.1.2"
[dev-dependencies]
tinybmp = "^0.3.3"
glam = "0.20.5"
glam = "^0.21.2"
rog_dbus = { path = "../rog-dbus" }
gif = "^0.11.2"

View File

@@ -56,7 +56,6 @@ pub struct AuraEnabled {
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// let s = s.to_lowercase();
// dbg!(s);
// Ok(Self {
// help: false,
// keyboard: None,

View File

@@ -10,7 +10,8 @@ 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, AuraDevice, AuraPowerDev};
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
use rog_aura::{self, AuraEffect};
use rog_dbus::RogDbusClientBlocking;
use rog_profiles::error::ProfileError;
@@ -161,7 +162,10 @@ fn do_parsed(
for command in commands.iter().filter(|command| {
if !matches!(
supported.keyboard_led.prod_id,
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
AuraDevice::X1854
| AuraDevice::X1869
| AuraDevice::X1866
| AuraDevice::Tuf
) && command.trim().starts_with("led-pow-1")
{
return false;
@@ -457,14 +461,27 @@ fn handle_led_power1(
return Ok(());
}
if !matches!(
if matches!(
supported.prod_id,
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
) {
println!("These options are for keyboards of product ID 0x1866 only");
handle_led_power_1_do_1866(dbus, power)?;
return Ok(());
}
if matches!(supported.prod_id, AuraDevice::Tuf) {
handle_led_power_1_do_tuf(dbus, power)?;
return Ok(());
}
println!("These options are for keyboards of product ID 0x1866 or TUF only");
return Ok(());
}
fn handle_led_power_1_do_1866(
dbus: &RogDbusClientBlocking,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDev1866> = Vec::new();
let mut disabled: Vec<AuraDev1866> = Vec::new();
@@ -487,12 +504,54 @@ fn handle_led_power1(
let data = AuraPowerDev {
x1866: enabled,
x19b6: vec![],
tuf: vec![],
};
dbus.proxies().led().set_leds_power(data, true)?;
let data = AuraPowerDev {
x1866: disabled,
x19b6: vec![],
tuf: vec![],
};
dbus.proxies().led().set_leds_power(data, false)?;
Ok(())
}
fn handle_led_power_1_do_tuf(
dbus: &RogDbusClientBlocking,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDevTuf> = Vec::new();
let mut disabled: Vec<AuraDevTuf> = Vec::new();
let mut check = |e: Option<bool>, a: AuraDevTuf| {
if let Some(arg) = e {
if arg {
enabled.push(a);
} else {
disabled.push(a);
}
}
};
check(power.awake, AuraDevTuf::Awake);
check(power.boot, AuraDevTuf::Boot);
check(power.sleep, AuraDevTuf::Sleep);
check(power.keyboard, AuraDevTuf::Keyboard);
let data = AuraPowerDev {
x1866: vec![],
x19b6: vec![],
tuf: enabled,
};
dbg!(&data);
dbus.proxies().led().set_leds_power(data, true)?;
let data = AuraPowerDev {
x1866: vec![],
x19b6: vec![],
tuf: disabled,
};
dbus.proxies().led().set_leds_power(data, false)?;
@@ -569,6 +628,7 @@ fn handle_led_power2(
if !enabled.is_empty() {
let data = AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: enabled,
};
@@ -577,6 +637,7 @@ fn handle_led_power2(
if !disabled.is_empty() {
let data = AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: disabled,
};

View File

@@ -1,6 +1,6 @@
[package]
name = "daemon"
version = "4.3.2"
version = "4.3.3"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]

View File

@@ -272,7 +272,7 @@ impl CtrlAnime {
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut()[7..].iter_mut() {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32 * self.config.brightness;
if bright > 254.0 {
bright = 254.0;

View File

@@ -1,6 +1,6 @@
use crate::laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES};
use log::{error, warn};
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraPowerDev};
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT};
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashSet};
@@ -16,13 +16,16 @@ pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
/// booting.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuraPowerConfig {
AuraDevTuf(HashSet<AuraDevTuf>),
AuraDev1866(HashSet<AuraDev1866>),
AuraDev19b6(HashSet<AuraDev19b6>),
}
impl AuraPowerConfig {
/// Invalid for TUF laptops
pub fn to_bytes(control: &Self) -> [u8; 3] {
match control {
AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0],
AuraPowerConfig::AuraDev1866(c) => {
let c: Vec<AuraDev1866> = c.iter().map(|v| *v).collect();
AuraDev1866::to_bytes(&c)
@@ -34,6 +37,39 @@ impl AuraPowerConfig {
}
}
pub fn to_tuf_bool_array(control: &Self) -> Option<[bool; 5]> {
if let Self::AuraDevTuf(c) = control {
return Some([
true,
c.contains(&AuraDevTuf::Boot),
c.contains(&AuraDevTuf::Awake),
c.contains(&AuraDevTuf::Sleep),
c.contains(&AuraDevTuf::Keyboard),
]);
}
if let Self::AuraDev1866(c) = control {
return Some([
true,
c.contains(&AuraDev1866::Boot),
c.contains(&AuraDev1866::Awake),
c.contains(&AuraDev1866::Sleep),
c.contains(&AuraDev1866::Keyboard),
]);
}
None
}
pub fn set_tuf(&mut self, power: AuraDevTuf, on: bool) {
if let Self::AuraDevTuf(p) = self {
if on {
p.insert(power);
} else {
p.remove(&power);
}
}
}
pub fn set_0x1866(&mut self, power: AuraDev1866, on: bool) {
if let Self::AuraDev1866(p) = self {
if on {
@@ -58,11 +94,18 @@ impl AuraPowerConfig {
impl From<&AuraPowerConfig> for AuraPowerDev {
fn from(config: &AuraPowerConfig) -> Self {
match config {
AuraPowerConfig::AuraDevTuf(d) => AuraPowerDev {
tuf: d.iter().map(|o| *o).collect(),
x1866: vec![],
x19b6: vec![],
},
AuraPowerConfig::AuraDev1866(d) => AuraPowerDev {
tuf: vec![],
x1866: d.iter().map(|o| *o).collect(),
x19b6: vec![],
},
AuraPowerConfig::AuraDev19b6(d) => AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: d.iter().map(|o| *o).collect(),
},
@@ -83,15 +126,19 @@ pub struct AuraConfig {
impl Default for AuraConfig {
fn default() -> Self {
let mut prod_id = String::new();
let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if let Ok(_) = CtrlKbdLed::find_led_node(prod) {
prod_id = prod.to_string();
prod_id = AuraDevice::from(*prod);
break;
}
}
let enabled = if prod_id == "19b6" {
if CtrlKbdLed::get_tuf_mode_path().is_some() {
prod_id = AuraDevice::Tuf;
}
let enabled = if prod_id == AuraDevice::X19B6 {
AuraPowerConfig::AuraDev19b6(HashSet::from([
AuraDev19b6::BootLogo,
AuraDev19b6::BootKeyb,
@@ -106,6 +153,13 @@ impl Default for AuraConfig {
AuraDev19b6::SleepBar,
AuraDev19b6::ShutdownBar,
]))
} else if prod_id == AuraDevice::Tuf {
AuraPowerConfig::AuraDevTuf(HashSet::from([
AuraDevTuf::Awake,
AuraDevTuf::Boot,
AuraDevTuf::Sleep,
AuraDevTuf::Keyboard,
]))
} else {
AuraPowerConfig::AuraDev1866(HashSet::from([
AuraDev1866::Awake,

View File

@@ -1,5 +1,7 @@
// Only these two packets must be 17 bytes
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
static TUF_RGB_MODE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/tuf_krgb_mode";
static TUF_RGB_STATE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/tuf_krgb_state";
use crate::{
error::RogError,
@@ -40,16 +42,20 @@ impl GetSupported for CtrlKbdLed {
let multizone_led_mode = laptop.multizone;
let per_key_led_mode = laptop.per_key;
let mut prod_id = "";
let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if let Ok(_) = Self::find_led_node(prod) {
prod_id = *prod;
prod_id = AuraDevice::from(*prod);
break;
}
}
if Self::get_tuf_mode_path().is_some() {
prod_id = AuraDevice::Tuf;
}
LedSupportedFunctions {
prod_id: AuraDevice::from(prod_id),
prod_id,
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
stock_led_modes,
multizone_led_mode,
@@ -58,10 +64,17 @@ impl GetSupported for CtrlKbdLed {
}
}
#[derive(Debug, PartialEq, PartialOrd)]
pub enum LEDNode {
Tuf,
Rog(String),
None,
}
pub struct CtrlKbdLed {
// TODO: config stores the keyboard type as an AuraPower, use or update this
pub led_prod: Option<String>,
pub led_node: Option<String>,
pub led_node: LEDNode,
pub bright_node: String,
pub supported_modes: LaptopLedData,
pub flip_effect_write: bool,
@@ -194,7 +207,6 @@ impl CtrlKbdLedZbus {
impl CtrlKbdLed {
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
// TODO: return error if *all* nodes are None
let mut led_prod = None;
let mut led_node = None;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
@@ -210,16 +222,27 @@ impl CtrlKbdLed {
}
let bright_node = Self::get_kbd_bright_path();
let tuf_node = Self::get_tuf_mode_path().is_some() || Self::get_tuf_state_path().is_some();
if led_node.is_none() {
if led_node.is_none() && tuf_node && bright_node.is_none() {
let dmi = sysfs_class::DmiId::default();
if let Ok(prod_family) = dmi.product_family() {
if prod_family.contains("TUF") {
warn!("A kernel patch is in progress for TUF RGB support");
}
}
return Err(RogError::NoAuraKeyboard);
}
if bright_node.is_none() {
return Err(RogError::MissingFunction(
"No brightness control, you may require a v5.11 series kernel or newer".into(),
));
}
let led_node = if let Some(rog) = led_node {
info!("Found ROG USB keyboard");
LEDNode::Rog(rog)
} else if tuf_node {
info!("Found TUF keyboard");
LEDNode::Tuf
} else {
LEDNode::None
};
let ctrl = CtrlKbdLed {
led_prod,
@@ -239,6 +262,20 @@ impl CtrlKbdLed {
None
}
pub fn get_tuf_mode_path() -> Option<String> {
if Path::new(TUF_RGB_MODE_PATH).exists() {
return Some(TUF_RGB_MODE_PATH.to_string());
}
None
}
fn get_tuf_state_path() -> Option<String> {
if Path::new(TUF_RGB_STATE_PATH).exists() {
return Some(TUF_RGB_STATE_PATH.to_string());
}
None
}
pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
let mut file = OpenOptions::new()
.read(true)
@@ -296,13 +333,26 @@ impl CtrlKbdLed {
/// Set combination state for boot animation/sleep animation/all leds/keys leds/side leds LED active
pub(super) fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
let bytes = AuraPowerConfig::to_bytes(&config.enabled);
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2]];
if LEDNode::Tuf == self.led_node {
if let Some(path) = Self::get_tuf_state_path() {
let mut file = OpenOptions::new().write(true).open(path)?;
if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&config.enabled) {
let buf = format!(
"1 {} {} {} {}",
pwr[1] as u8, pwr[2] as u8, pwr[3] as u8, pwr[4] as u8,
);
file.write_all(buf.as_bytes())?;
}
}
} else {
let bytes = AuraPowerConfig::to_bytes(&config.enabled);
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2]];
self.write_bytes(&message)?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
self.write_bytes(&message)?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
}
Ok(())
}
@@ -366,7 +416,7 @@ impl CtrlKbdLed {
/// Should only be used if the bytes you are writing are verified correct
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(led_node) = &self.led_node {
if let LEDNode::Rog(led_node) = &self.led_node {
if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) {
// println!("write: {:02x?}", &message);
return file
@@ -428,11 +478,20 @@ impl CtrlKbdLed {
}
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
let bytes: [u8; LED_MSG_LEN] = mode.into();
self.write_bytes(&bytes)?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
if LEDNode::Tuf == self.led_node {
let mut file = OpenOptions::new().write(true).open(TUF_RGB_MODE_PATH)?;
let buf = format!(
"1 {} {} {} {} {}",
mode.mode as u8, mode.colour1.0, mode.colour1.1, mode.colour1.2, mode.speed as u8,
);
file.write_all(buf.as_bytes())?;
} else {
let bytes: [u8; LED_MSG_LEN] = mode.into();
self.write_bytes(&bytes)?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY)?;
}
Ok(())
}
@@ -504,7 +563,10 @@ impl CtrlKbdLed {
mod tests {
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use crate::{ctrl_aura::config::AuraConfig, laptops::LaptopLedData};
use crate::{
ctrl_aura::{config::AuraConfig, controller::LEDNode},
laptops::LaptopLedData,
};
use super::CtrlKbdLed;
@@ -522,7 +584,7 @@ mod tests {
};
let mut controller = CtrlKbdLed {
led_prod: None,
led_node: None,
led_node: LEDNode::None,
bright_node: String::new(),
supported_modes,
flip_effect_write: false,
@@ -584,7 +646,7 @@ mod tests {
};
let mut controller = CtrlKbdLed {
led_prod: None,
led_node: None,
led_node: LEDNode::None,
bright_node: String::new(),
supported_modes,
flip_effect_write: false,
@@ -621,7 +683,7 @@ mod tests {
};
let mut controller = CtrlKbdLed {
led_prod: None,
led_node: None,
led_node: LEDNode::None,
bright_node: String::new(),
supported_modes,
flip_effect_write: false,

View File

@@ -66,6 +66,9 @@ impl CtrlKbdLedZbus {
) -> zbus::fdo::Result<()> {
let mut states = None;
if let Ok(mut ctrl) = self.0.try_lock() {
for p in options.tuf {
ctrl.config.enabled.set_tuf(p, enabled);
}
for p in options.x1866 {
ctrl.config.enabled.set_0x1866(p, enabled);
}

View File

@@ -23,17 +23,6 @@ impl ProfileConfig {
}
}
pub fn set_defaults_and_save(&mut self) {
self.active_profile = Profile::get_active_profile().unwrap_or(Profile::Balanced);
if let Ok(res) = FanCurveProfiles::is_supported() {
if res {
let curves = FanCurveProfiles::default();
self.fan_curves = Some(curves);
}
}
self.write();
}
pub fn load(config_path: String) -> Self {
let mut file = OpenOptions::new()
.read(true)
@@ -46,7 +35,6 @@ impl ProfileConfig {
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
config = Self::new(config_path);
config.set_defaults_and_save();
} else if let Ok(data) = toml::from_str(&buf) {
config = data;
config.config_path = config_path;
@@ -63,11 +51,9 @@ impl ProfileConfig {
)
});
config = Self::new(config_path);
config.set_defaults_and_save();
}
} else {
config = Self::new(config_path);
config.set_defaults_and_save();
}
config
}

View File

@@ -20,12 +20,7 @@ impl GetSupported for CtrlPlatformProfile {
fn get_supported() -> Self::A {
if !Profile::is_platform_profile_supported() {
warn!(
r#"
platform_profile kernel interface not found, your laptop does not support this, or the interface is missing.
To enable profile support you require a kernel version 5.15.2 minimum.
"#
);
warn!("platform_profile kernel interface not found, your laptop does not support this, or the interface is missing.");
}
let res = FanCurveProfiles::is_supported();
@@ -35,14 +30,7 @@ To enable profile support you require a kernel version 5.15.2 minimum.
};
if !fan_curve_supported {
info!(
r#"
fan curves kernel interface not found, your laptop does not support this, or the interface is missing.
To enable fan-curve support you require a kernel with the following patch applied:
https://lkml.org/lkml/2021/10/23/250
This patch has been accepted upstream for 5.17 kernel release.
"#
);
info!("fan curves kernel interface not found, your laptop does not support this, or the interface is missing.");
}
PlatformProfileFunctions {
@@ -68,12 +56,45 @@ impl crate::Reloadable for CtrlPlatformProfile {
}
impl CtrlPlatformProfile {
pub fn new(config: ProfileConfig) -> Result<Self, RogError> {
pub fn new(mut config: ProfileConfig) -> Result<Self, RogError> {
if Profile::is_platform_profile_supported() {
info!("Device has profile control available");
if FanCurveProfiles::get_device().is_ok() {
info!("Device has fan curves available");
if config.fan_curves.is_none() {
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
let dev = FanCurveProfiles::get_device()?;
let mut curves = FanCurveProfiles::default();
warn!("No default fan-curves: cycling profiles to set defaults");
Profile::set_profile(Profile::Balanced)?;
curves.read_from_dev_profile(Profile::Balanced, &dev);
info!(
"{:?}: {}",
config.active_profile,
String::from(curves.get_fan_curves_for(Profile::Balanced))
);
Profile::set_profile(Profile::Performance)?;
curves.read_from_dev_profile(Profile::Performance, &dev);
info!(
"{:?}: {}",
config.active_profile,
String::from(curves.get_fan_curves_for(Profile::Performance))
);
Profile::set_profile(Profile::Quiet)?;
curves.read_from_dev_profile(Profile::Quiet, &dev);
info!(
"{:?}: {}",
config.active_profile,
String::from(curves.get_fan_curves_for(Profile::Quiet))
);
Profile::set_profile(active)?;
config.fan_curves = Some(curves);
config.write();
info!("Set fan curve defaults");
}
}
return Ok(CtrlPlatformProfile { config });

View File

@@ -1,50 +0,0 @@
function _asusctl() {
local line
_arguments -C \
{-h,--help}'[print help message]' \
{-v,--version}'[print version number]' \
{-k,--kbd-bright}':[Set keyboard brightness (off, low, med, high)]' \
{-p,--pwr-profile}':[Set power profile (silent, normal, boost)]' \
{-c,--chg-limit}':[Set charging limit (20-100)]' \
': :((led-mode\:"Set the keyboard lighting from built-in modes" profile\:"Create and configure profiles" graphics\:"Set the graphics mode"))' \
'*::arg:->args'
case $line[1] in
led-mode)
_arguments ': :((static\:"set a single static colour"
breathe\:"pulse between one or two colours"
strobe\:"strobe through all colours"
rainbow\:"rainbow cycling in one of four directions"
star\:"rain pattern mimicking raindrops"
rain\:"rain pattern of three preset colours"
highlight\:"pressed keys are highlighted to fade"
laser\:"pressed keys generate horizontal laser"
ripple\:"pressed keys ripple outwards like a splash"
pulse\:"set a rapid pulse"
comet\:"set a vertical line zooming from left"
flash\:"set a wide vertical line zooming from left"
multi-static\:"4-zone multi-colour"))' \
{-h,--help}'[print help message]' \
'-c:[set the RGB value e.g, ff00ff]' \
'-s:[set the speed (low, med, high)]'
;;
profile)
_arguments {-h,--help}'[print help message]' \
{-c,--create}"[create the profile if it doesn't exist]" \
{-t,--turbo}':[enable or disable cpu turbo]' \
{-m,--min-percentage}':[set min cpu scaling (intel)]' \
{-M,--max-percentage}':[set max cpu scaling (intel)]' \
{-p,--preset}':[<silent, normal, boost>]' \
{-C,--curve}':[set fan curve]'
;;
graphics)
_arguments {-h,--help}'[print help message]' \
{-m,--mode}':[Set graphics mode (nvidia, hybrid, compute, integrated)]' \
{-g,--get}'[Get the current mode]' \
{-p,--pow}'[Get the current power status]' \
{-f,--force}'[Do not ask for confirmation]'
;;
esac
}
compdef _asusctl asusctl

View File

@@ -1,13 +0,0 @@
[Unit]
Description=ASUS Notebook Control
After=basic.target syslog.target
[Service]
Environment=IS_SERVICE=1
ExecStart=/usr/bin/asusd
Restart=on-failure
Type=dbus
BusName=org.asuslinux.Daemon
[Install]
WantedBy=multi-user.target

View File

@@ -1,3 +1,17 @@
[[led_data]]
prod_family = "TUF"
board_names = ["FA507"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "TUF Gaming"
board_names = ["FX505D"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "Zephyrus S"
board_names = ["GX502", "GX701", "G531", "GL531", "G532"]
@@ -57,7 +71,7 @@ per_key = true
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512"]
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512", "G713RW"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
@@ -162,7 +176,7 @@ multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Strix"
prod_family = "ROG Strix"
board_names = ["G513IC"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []

View File

@@ -1,2 +1,19 @@
ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service"
#ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
#ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service"
ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}"
ENV{DMI_VENDOR}!="ASUSTeK COMPUTER INC.", GOTO="asusd_end"
ENV{DMI_FAMILY}="$attr{[dmi/id]product_family}"
ENV{DMI_FAMILY}=="*TUF*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
# No match so
GOTO="asusd_end"
LABEL="asusd_start"
ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", RUN+="systemctl restart asusd.service"
LABEL="asusd_end"

View File

@@ -1,77 +0,0 @@
# Author: AlenPaulVarghese <alenpaul2001@gmail.com>
set -l progname asusctl
set -l noopt "not __fish_contains_opt -s -s h -s v -s s -s k -s f -s c help version show-supported kbd-bright fan-mode chg-limit; and not __fish_seen_subcommand_from led-mode profile graphics;"
set -l gmod_options '__fish_contains_opt -s m mode;'
set -l fan_options '__fish_contains_opt -s f fan-mode;'
set -l led_options '__fish_seen_subcommand_from led-mode;'
set -l profile_options '__fish_seen_subcommand_from profile;'
set -l keyboard_options '__fish_contains_opt -s k kbd-bright;'
set -l graphics_options '__fish_seen_subcommand_from graphics;'
set -l fan_modes 'silent normal boost'
set -l brightness_modes 'off low med high'
set -l led_modes 'static breathe strobe rainbow comet'
set -l graphics_modes 'nvidia hybird compute integrated'
complete -c $progname -e
complete -c $progname -f
# asusctl completion
complete -c $progname -s h -f -l help -n "$noopt" -d "print help message"
complete -c $progname -s v -f -l version -n "$noopt" -d "show program version number"
complete -c $progname -s s -f -l show-supported -n "$noopt" -d "show supported functions of this laptop"
complete -c $progname -s k -f -l kbd-bright -n "$noopt" -d "set led brightness"
complete -c $progname -s f -f -l fan-mode -n "$noopt" -d "set fan mode independent of profile"
complete -c $progname -s c -f -l chg-limit -n "$noopt" -d "set charge limit <20-100>"
complete -c $progname -f -a "led-mode" -n "$noopt" -d "Set the keyboard lighting from built-in modes"
complete -c $progname -f -a "profile" -n "$noopt" -d "Create and configure profiles"
complete -c $progname -f -a "graphics" -n "$noopt" -d "Set the graphics mode"
# brightness completion
complete -c $progname -n "$keyboard_options" -d "available brightness modes" -a "$brightness_modes"
# fan completion
complete -c $progname -n "$fan_options" -d "available fan modes" -a $fan_modes
# graphics completion
set -l gopt 'not __fish_contains_opt -s h -s g -s m -s p help mode get pow;'
complete -c $progname -n "$graphics_options and $gopt" -a "-h" -d "print help message"
complete -c $progname -n "$graphics_options and $gopt" -a "-g" -d "Get the current mode"
complete -c $progname -s h -f -l help -n "$graphics_options and $gopt" -d "print help message"
complete -c $progname -s m -f -l mode -n "$graphics_options and $gopt" -d "Set graphics mode: <nvidia, hybrid, compute, integrated>"
complete -c $progname -s g -f -l get -n "$graphics_options and $gopt" -d "Get the current mode"
complete -c $progname -s p -f -l pow -n "$graphics_options and $gopt" -d "Get the current power status"
complete -c $progname -n "$graphics_options and $gmod_options" -d "available graphics modes" -a "$graphics_modes"
# led-mode completion
complete -c $progname -n "$led_options" -a "-h" -d "print help message"
complete -c $progname -n "$led_options" -a "-n" -d "switch to next aura mode"
complete -c $progname -s h -f -l help -n "$led_options" -d "print help message"
complete -c $progname -s n -f -l next-mode -n "$led_options" -d "switch to nex aura mode"
complete -c $progname -s p -f -l prev-mode -n "$led_options" -d "switch to previous aura mode"
complete -c $progname -n "$led_options" -d "available led modes" -a "$led_modes"
# profile completion
set -l popt 'not __fish_contains_opt -s h -s n -s c -s t -s m -s M -s f help next create turbo min-percentage max-percentage fan-preset;'
complete -c $progname -n "$profile_options and $popt" -a "-h" -d "print help message"
complete -c $progname -n "$profile_options and $popt" -a "-n" -d "toggle to next profile in list"
complete -c $progname -s h -f -l help -n "$profile_options and $popt" -d "print help message"
complete -c $progname -s n -f -l next -n "$profile_options and $popt" -d "toggle to next profile in list"
complete -c $progname -s c -f -l create -n "$profile_options and $popt" -d "create the profile if it doesn't exist"
complete -c $progname -s t -f -l turbo -n "$profile_options and $popt" -d "enable or disable cpu turbo"
complete -c $progname -s m -f -l min-percentage -n "$profile_options and $popt" -d "set min cpu scaling (intel)"
complete -c $progname -s M -f -l max-percentage -n "$profile_options and $popt" -d "set max cpu scaling (intel)"
complete -c $progname -s f -f -l fan-preset -n "$profile_options and $popt" -d "<silent, normal, boost>"
complete -c $progname -n "$profile_option and __fish_contains_opt fan-preset" -d "available fan modes" -a $fan_modes

View File

@@ -26,7 +26,7 @@ log = "*"
serde = "^1.0"
serde_derive = "^1.0"
glam = { version = "0.20.5", features = ["serde"] }
glam = { version = "^0.21.2", features = ["serde"] }
zvariant = { version = "^3.0", optional = true }
zbus = { version = "^2.2", optional = true }

View File

@@ -1,6 +1,6 @@
[package]
name = "rog_aura"
version = "1.2.0"
version = "1.2.2"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
@@ -13,11 +13,11 @@ edition = "2018"
exclude = ["data"]
[features]
default = ["dbus"]
default = ["dbus", "toml"]
dbus = ["zvariant"]
[dependencies]
serde = "^1.0"
serde_derive = "^1.0"
zvariant = { version = "^3.0", optional = true }
toml = { version = "^0.5", optional = true }
zvariant = { version = "^3.0", optional = true }

View File

@@ -0,0 +1,161 @@
matches = [
'G513',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Fan',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
'RowEndSpacer',
'NumPadDel',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc',
'RowEndSpacer',
'Home',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
'RowEndSpacer',
'PgUp',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return',
'RowEndSpacer',
'PgDn',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'Rshift',
'RowEndSpacer',
'End',
]
[[rows]]
height = 1.2
row = [
'LCtrl',
'LFn',
'Meta',
'LAlt',
'Space',
'RAlt',
'PrtSc',
'RCtrl',
'ArrowSpacer',
'Up',
'ArrowSpacer',
'RowEndSpacer',
'RFn',
]
[[rows]]
height = 0.8
row = [
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'ArrowSpacer',
'Left',
'Down',
'Right',
'ArrowSpacer',
]

View File

@@ -0,0 +1,135 @@
matches = [
'G533',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Fan',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
'Del',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc',
'MediaPlay',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
'MediaStop',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return',
'MediaPrev',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'RshiftSmall',
'UpRegular',
'MediaNext',
]
[[rows]]
height = 1.0
row = [
'LCtrlMed',
'LFn',
'Meta',
'LAlt',
'Space',
'RAlt',
'PrtSc',
'RCtrl',
'ArrowRegularSpacer',
'LeftRegular',
'DownRegular',
'RightRegular',
]

View File

@@ -0,0 +1,154 @@
matches = [
'GA401',
'GA402',
'GU603',
'GV301',
'GA502',
'GA503',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'Rshift',
]
[[rows]]
height = 1.2
row = [
'LCtrl',
'LFn',
'Meta',
'LAlt',
'Space',
'RAlt',
'PrtSc',
'RCtrl',
'ArrowSpacer',
'Up',
'ArrowSpacer',
]
[[rows]]
height = 0.8
row = [
'FuncSpacer',
'FuncSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'Left',
'Down',
'Right',
'ArrowSpacer',
]

View File

@@ -0,0 +1,180 @@
matches = [
'GL504',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
'RowEndSpacer',
'Del',
'NumPadPause',
'NumPadPrtSc',
'NumPadHome',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc',
'RowEndSpacer',
'NumLock',
'FwdSlash',
'Star',
'Hyphen',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
'RowEndSpacer',
'N7',
'N8',
'N9',
'NumPadPlus',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return',
'RowEndSpacer',
'N4',
'N5',
'N6',
'NumPadPlus',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'Rshift',
'RowEndSpacer',
'N1',
'N2',
'N3',
'NumPadEnter',
]
[[rows]]
height = 1.0
row = [
'LCtrl',
'LFn',
'Meta',
'LAlt',
'Space',
'RAlt',
'RFn',
'RFn',
'RCtrlLarge',
'RowEndSpacer',
'UpRegular',
'N0',
'NumPadDel',
'NumPadEnter',
]
[[rows]]
height = 1.0
row = [
'FuncSpacer',
'FuncSpacer',
'FuncSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'LeftRegular',
'RowEndSpacer',
'DownRegular',
'RightRegular',
'NormalSpacer',
]

View File

@@ -0,0 +1,172 @@
matches = [
'GX502',
'GU502',
]
locale = "US"
[[rows]]
height = 0.8
row = [
'NormalSpacer',
'FuncSpacer',
'VolDown',
'VolUp',
'MicMute',
'Rog',
]
[[rows]]
height = 0.8
row = [
'Esc',
'FuncSpacer',
'F1',
'F2',
'F3',
'F4',
'FuncSpacer',
'F5',
'F6',
'F7',
'F8',
'FuncSpacer',
'F9',
'F10',
'F11',
'F12',
'RowEndSpacer',
'Del',
]
[[rows]]
height = 1.0
row = [
'Tilde',
'N1',
'N2',
'N3',
'N4',
'N5',
'N6',
'N7',
'N8',
'N9',
'N0',
'Hyphen',
'Equals',
'BkSpc3_1',
'BkSpc3_2',
'BkSpc3_3',
'RowEndSpacer',
'Home',
]
[[rows]]
height = 1.0
row = [
'Tab',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'LBracket',
'RBracket',
'BackSlash',
'RowEndSpacer',
'PgUp',
]
[[rows]]
height = 1.0
row = [
'Caps',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'SemiColon',
'Quote',
'Return3_1',
'Return3_2',
'Return3_3',
'RowEndSpacer',
'PgDn',
]
[[rows]]
height = 1.0
row = [
'LShift',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'Comma',
'Period',
'FwdSlash',
'Rshift3_1',
'Rshift3_2',
'Rshift3_3',
'RowEndSpacer',
'End',
]
[[rows]]
height = 1.2
row = [
'LCtrl',
'LFn',
'Meta',
'LAlt',
'Space5_1',
'Space5_2',
'Space5_3',
'Space5_4',
'Space5_5',
'RAlt',
'PrtSc',
'RCtrl',
'ArrowSpacer',
'Up',
'ArrowSpacer',
'RowEndSpacer',
'RFn',
]
[[rows]]
height = 0.8
row = [
'FuncSpacer',
'FuncSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'NormalSpacer',
'Left',
'Down',
'Right',
'ArrowSpacer',
]

View File

@@ -79,6 +79,18 @@ impl From<Colour> for [f32; 3] {
}
}
impl From<&[u8; 3]> for Colour {
fn from(c: &[u8; 3]) -> Self {
Self(c[0], c[1], c[2])
}
}
impl From<Colour> for [u8; 3] {
fn from(c: Colour) -> Self {
[c.0, c.1, c.2]
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
pub enum Speed {
@@ -138,8 +150,11 @@ impl FromStr for Direction {
/// Enum of modes that convert to the actual number required by a USB HID packet
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize)]
#[derive(
Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize,
)]
pub enum AuraModeNum {
#[default]
Static = 0,
Breathe = 1,
Strobe = 2,
@@ -346,6 +361,53 @@ impl Default for AuraEffect {
}
}
pub struct AuraParameters {
pub zone: bool,
pub colour1: bool,
pub colour2: bool,
pub speed: bool,
pub direction: bool,
}
impl AuraParameters {
pub const fn new(
zone: bool,
colour1: bool,
colour2: bool,
speed: bool,
direction: bool,
) -> Self {
Self {
zone,
colour1,
colour2,
speed,
direction,
}
}
}
impl AuraEffect {
/// A helper to provide detail on what effects have which parameters, e.g the static
/// factory mode accepts only one colour.
pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters {
match mode {
AuraModeNum::Static => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
AuraModeNum::Strobe => AuraParameters::new(true, false, false, true, false),
AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true),
AuraModeNum::Star => AuraParameters::new(true, true, true, true, true),
AuraModeNum::Rain => AuraParameters::new(true, false, false, true, false),
AuraModeNum::Highlight => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Laser => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Ripple => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Pulse => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Comet => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
}
}
}
/// Parses `AuraEffect` in to packet data for writing to the USB interface
///
/// Byte structure where colour is RGB, one byte per R, G, B:

View File

@@ -7,7 +7,8 @@ pub enum Error {
ParseSpeed,
ParseDirection,
ParseBrightness,
ParseAnime,
Io(std::io::Error),
Toml(toml::de::Error),
}
impl fmt::Display for Error {
@@ -18,9 +19,22 @@ impl fmt::Display for Error {
Error::ParseSpeed => write!(f, "Could not parse speed"),
Error::ParseDirection => write!(f, "Could not parse direction"),
Error::ParseBrightness => write!(f, "Could not parse brightness"),
Error::ParseAnime => write!(f, "Could not parse anime"),
Error::Io(io) => write!(f, "IO Error: {io}"),
Error::Toml(e) => write!(f, "TOML Parse Error: {e}"),
}
}
}
impl error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Self::Io(e)
}
}
impl From<toml::de::Error> for Error {
fn from(e: toml::de::Error) -> Self {
Self::Toml(e)
}
}

155
rog-aura/src/key_to_str.rs Normal file
View File

@@ -0,0 +1,155 @@
use crate::keys::Key;
impl From<Key> for &str {
fn from(k: Key) -> Self {
(&k).into()
}
}
impl From<&Key> for &str {
fn from(k: &Key) -> Self {
match k {
Key::VolUp => "Volume Up",
Key::VolDown => "Volume Down",
Key::MicMute => "Mute Mic",
Key::Rog => "ROG",
Key::Fan => "Fan Control",
Key::Esc => "Escape",
Key::F1 => "F1",
Key::F2 => "F2",
Key::F3 => "F3",
Key::F4 => "F4",
Key::F5 => "F5",
Key::F6 => "F6",
Key::F7 => "F7",
Key::F8 => "F8",
Key::F9 => "F9",
Key::F10 => "F10",
Key::F11 => "F11",
Key::F12 => "F12",
Key::Del => "Delete",
Key::Tilde => "Tilde",
Key::N1 => "1",
Key::N2 => "2",
Key::N3 => "3",
Key::N4 => "4",
Key::N5 => "5",
Key::N6 => "6",
Key::N7 => "7",
Key::N8 => "8",
Key::N9 => "9",
Key::N0 => "0",
Key::Hyphen => "-",
Key::Equals => "=",
Key::BkSpc => "Backspace",
Key::BkSpc3_1 => "Backspace LED 1",
Key::BkSpc3_2 => "Backspace LED 2",
Key::BkSpc3_3 => "Backspace LED 3",
Key::Home => "Home",
Key::Tab => "Tab",
Key::Q => "Q",
Key::W => "W",
Key::E => "E",
Key::R => "R",
Key::T => "T",
Key::Y => "Y",
Key::U => "U",
Key::I => "I",
Key::O => "O",
Key::P => "P",
Key::LBracket => "[",
Key::RBracket => "]",
Key::BackSlash => "\\",
Key::PgUp => "Page Up",
Key::Caps => "Caps Lock",
Key::A => "A",
Key::S => "S",
Key::D => "D",
Key::F => "F",
Key::G => "G",
Key::H => "H",
Key::J => "J",
Key::K => "K",
Key::L => "L",
Key::SemiColon => ";",
Key::Quote => "'",
Key::Return => "Return",
Key::Return3_1 => "Return LED 1",
Key::Return3_2 => "Return LED 2",
Key::Return3_3 => "Return LED 3",
Key::PgDn => "Page Down",
Key::LShift => "Left Shift",
Key::LShift3_1 => "Left Shift LED 1",
Key::LShift3_2 => "Left Shift LED 2",
Key::LShift3_3 => "Left Shift LED 3",
Key::Z => "Z",
Key::X => "X",
Key::C => "C",
Key::V => "V",
Key::B => "B",
Key::N => "N",
Key::M => "M",
Key::Comma => ",",
Key::Period => ".",
Key::Star => "*",
Key::NumPadDel => "Delete",
Key::NumPadPlus => "+",
Key::NumPadEnter => "Enter",
Key::NumPadPause => "Pause",
Key::NumPadPrtSc => "Print Screen",
Key::NumPadHome => "Home",
Key::NumLock => "Num-Lock",
Key::FwdSlash => "/",
Key::Rshift => "Right Shift",
Key::RshiftSmall => "Right Shift",
Key::Rshift3_1 => "Right Shift LED 1",
Key::Rshift3_2 => "Right Shift LED 2",
Key::Rshift3_3 => "Right Shift LED 3",
Key::End => "End",
Key::LCtrl => "Left Control",
Key::LCtrlMed => "Left Control",
Key::LFn => "Left Fn",
Key::Meta => "Meta",
Key::LAlt => "Left Alt",
Key::Space => "Space",
Key::Space5_1 => "Space LED 1",
Key::Space5_2 => "Space LED 2",
Key::Space5_3 => "Space LED 3",
Key::Space5_4 => "Space LED 4",
Key::Space5_5 => "Space LED 5",
Key::RAlt => "Right Alt",
Key::PrtSc => "Print Screen",
Key::RCtrl => "Right Control",
Key::RCtrlLarge => "Right Control",
Key::Pause => "Pause",
Key::Up => "Up",
Key::Down => "Down",
Key::Left => "Left",
Key::Right => "Right",
Key::UpRegular => "Up",
Key::DownRegular => "Down",
Key::LeftRegular => "Left",
Key::RightRegular => "Right",
Key::UpSplit => "Up",
Key::DownSplit => "Down",
Key::LeftSplit => "Left",
Key::RightSplit => "Right",
Key::RFn => "Right Fn",
Key::MediaPlay => "Media Play",
Key::MediaStop => "Media Stop",
Key::MediaNext => "Media Next",
Key::MediaPrev => "Media Previous",
Key::NormalBlank => "",
Key::NormalSpacer => "",
Key::FuncBlank => "",
Key::FuncSpacer => "",
Key::ArrowBlank => "",
Key::ArrowSpacer => "",
Key::ArrowRegularBlank => "",
Key::ArrowRegularSpacer => "",
Key::ArrowSplitBlank => "",
Key::ArrowSplitSpacer => "",
Key::RowEndSpacer => "",
}
}
}

355
rog-aura/src/keys.rs Normal file
View File

@@ -0,0 +1,355 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum Key {
VolUp,
VolDown,
MicMute,
Rog,
Fan,
Esc,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Del,
Tilde,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Hyphen,
Equals,
BkSpc,
BkSpc3_1,
BkSpc3_2,
BkSpc3_3,
Home,
Tab,
Q,
W,
E,
R,
T,
Y,
U,
I,
O,
P,
LBracket,
RBracket,
BackSlash,
PgUp,
Caps,
A,
S,
D,
F,
G,
H,
J,
K,
L,
SemiColon,
Quote,
Return,
Return3_1,
Return3_2,
Return3_3,
PgDn,
LShift,
LShift3_1,
LShift3_2,
LShift3_3,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
FwdSlash,
Star,
NumPadDel,
NumPadPlus,
NumPadEnter,
NumPadPause,
NumPadPrtSc,
NumPadHome,
NumLock,
Rshift,
RshiftSmall,
Rshift3_1,
Rshift3_2,
Rshift3_3,
End,
LCtrl,
LCtrlMed,
LFn,
Meta,
LAlt,
Space,
Space5_1,
Space5_2,
Space5_3,
Space5_4,
Space5_5,
Pause,
RAlt,
PrtSc,
RCtrl,
RCtrlLarge,
Up,
Down,
Left,
Right,
UpRegular,
DownRegular,
LeftRegular,
RightRegular,
UpSplit,
DownSplit,
LeftSplit,
RightSplit,
RFn,
MediaPlay,
MediaStop,
MediaNext,
MediaPrev,
NormalBlank,
/// To be ignored by per-key effects
NormalSpacer,
FuncBlank,
/// To be ignored by per-key effects
FuncSpacer,
ArrowBlank,
/// To be ignored by per-key effects
ArrowSpacer,
ArrowRegularBlank,
/// To be ignored by per-key effects
ArrowRegularSpacer,
ArrowSplitBlank,
/// To be ignored by per-key effects
ArrowSplitSpacer,
/// A gap between regular rows and the rightside buttons
RowEndSpacer,
}
/// Types of shapes of LED on keyboards. The shape is used for visual representations
///
/// A post fix of Spacer *must be ignored by per-key effects
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize)]
pub enum KeyShape {
Tilde,
#[default]
Normal,
NormalBlank,
NormalSpacer,
Func,
FuncBlank,
FuncSpacer,
Space,
Space5,
LCtrlMed,
LShift,
/// Used in a group of 3 (LED's)
LShift3,
RShift,
RshiftSmall,
/// Used in a group of 3 (LED's)
RShift3,
Return,
Return3,
Tab,
Caps,
Backspace,
/// Used in a group of 3 (LED's)
Backspace3,
Arrow,
ArrowBlank,
ArrowSpacer,
ArrowSplit,
ArrowSplitBlank,
ArrowSplitSpacer,
ArrowRegularBlank,
ArrowRegularSpacer,
RowEndSpacer,
}
impl KeyShape {
pub const fn width(&self) -> f32 {
match self {
Self::Tilde => 0.8,
Self::Normal => 1.0,
Self::NormalBlank => 1.0,
Self::NormalSpacer => 1.0,
Self::Func => 1.0,
Self::FuncBlank => 1.0,
Self::FuncSpacer => 0.6,
Self::Space => 5.0,
Self::Space5 => 1.0,
Self::LCtrlMed => 1.1,
Self::LShift => 2.0,
Self::LShift3 => 0.67,
Self::RShift => 2.8,
Self::RshiftSmall => 1.8,
Self::RShift3 => 0.93,
Self::Return => 2.2,
Self::Return3 => 0.7333,
Self::Tab => 1.4,
Self::Caps => 1.6,
Self::Backspace => 2.0,
Self::Backspace3 => 0.666,
Self::ArrowRegularBlank | Self::ArrowRegularSpacer => 0.7,
Self::Arrow => 0.8,
Self::ArrowBlank | Self::ArrowSpacer => 1.0,
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => 1.0,
Self::RowEndSpacer => 0.1,
}
}
/// A blank is used to space keys out in GUI's and can be used or ignored
/// depednign on the per-key effect
pub const fn is_blank(&self) -> bool {
match self {
Self::NormalBlank
| Self::FuncBlank
| Self::ArrowBlank
| Self::ArrowSplitBlank
| Self::ArrowRegularBlank => true,
_ => false,
}
}
/// A spacer is used to space keys out in GUI's, but ignored in per-key effects
pub const fn is_spacer(&self) -> bool {
match self {
Self::FuncSpacer
| Self::NormalSpacer
| Self::ArrowSpacer
| Self::ArrowSplitSpacer
| Self::ArrowRegularSpacer => true,
_ => false,
}
}
/// All keys with a postfix of some number
pub const fn is_group(&self) -> bool {
match self {
Self::LShift3 | Self::RShift3 => true,
Self::Return3 | Self::Space5 | Self::Backspace3 => true,
_ => false,
}
}
/// Mostly intended as a helper for signalling when to draw a
/// split/compact arrow cluster
pub const fn is_arrow_cluster(&self) -> bool {
match self {
Self::Arrow | Self::ArrowBlank | Self::ArrowSpacer => true,
_ => false,
}
}
pub const fn is_arrow_splits(&self) -> bool {
match self {
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => true,
_ => false,
}
}
}
impl From<Key> for KeyShape {
fn from(k: Key) -> Self {
match k {
Key::VolUp
| Key::VolDown
| Key::MicMute
| Key::Rog
| Key::Fan
| Key::Esc
| Key::F1
| Key::F2
| Key::F3
| Key::F4
| Key::F5
| Key::F6
| Key::F7
| Key::F8
| Key::F9
| Key::F10
| Key::F11
| Key::F12
| Key::Del => KeyShape::Func,
Key::Tilde => KeyShape::Tilde,
Key::BkSpc => KeyShape::Backspace,
Key::BkSpc3_1 | Key::BkSpc3_2 | Key::BkSpc3_3 => KeyShape::Backspace3,
Key::Tab | Key::BackSlash => KeyShape::Tab,
Key::Caps => KeyShape::Caps,
Key::Return => KeyShape::Return,
Key::Return3_1 | Key::Return3_2 | Key::Return3_3 => KeyShape::Return3,
Key::LCtrlMed => KeyShape::LCtrlMed,
Key::LShift => KeyShape::LShift,
Key::Rshift | Key::RCtrlLarge => KeyShape::RShift,
Key::RshiftSmall => KeyShape::RshiftSmall,
Key::Rshift3_1 | Key::Rshift3_2 | Key::Rshift3_3 => KeyShape::RShift3,
Key::Space => KeyShape::Space,
Key::Space5_1 | Key::Space5_2 | Key::Space5_3 | Key::Space5_4 | Key::Space5_5 => {
KeyShape::Space5
}
Key::NumPadPause | Key::NumPadPrtSc | Key::NumPadHome | Key::NumPadDel => {
KeyShape::Func
}
Key::NormalBlank => KeyShape::NormalBlank,
Key::NormalSpacer => KeyShape::NormalSpacer,
Key::FuncBlank => KeyShape::FuncBlank,
Key::FuncSpacer => KeyShape::FuncSpacer,
Key::Up | Key::Down | Key::Left | Key::Right => KeyShape::Arrow,
Key::ArrowBlank => KeyShape::ArrowBlank,
Key::ArrowSpacer => KeyShape::ArrowSpacer,
Key::ArrowRegularBlank => KeyShape::ArrowRegularBlank,
Key::ArrowRegularSpacer => KeyShape::ArrowRegularSpacer,
Key::UpSplit | Key::LeftSplit | Key::DownSplit | Key::RightSplit => {
KeyShape::ArrowSplit
}
Key::ArrowSplitBlank => KeyShape::ArrowSplitBlank,
Key::ArrowSplitSpacer => KeyShape::ArrowSplitSpacer,
Key::RowEndSpacer => KeyShape::RowEndSpacer,
_ => KeyShape::Normal,
}
}
}
impl From<&Key> for KeyShape {
fn from(k: &Key) -> Self {
(*k).into()
}
}

View File

@@ -0,0 +1,170 @@
use super::{KeyLayout, KeyRow};
use crate::keys::Key;
impl KeyLayout {
/// Similar to GX502, but not per-key enabled
pub fn g513_layout() -> Self {
Self {
matches: vec!["G513".into()],
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,
vec![
Key::NormalSpacer,
Key::FuncSpacer,
Key::VolDown,
Key::VolUp,
Key::MicMute,
Key::Fan,
Key::Rog,
],
),
KeyRow::new(
0.8,
vec![
Key::Esc,
Key::FuncSpacer,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::FuncSpacer, // not sure which key to put here
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::FuncSpacer,
Key::F9,
Key::F10,
Key::F11,
Key::F12,
Key::RowEndSpacer,
Key::Del,
],
),
KeyRow::new(
1.0,
vec![
Key::Tilde,
Key::N1,
Key::N2,
Key::N3,
Key::N4,
Key::N5,
Key::N6,
Key::N7,
Key::N8,
Key::N9,
Key::N0,
Key::Hyphen,
Key::Equals,
Key::BkSpc,
Key::RowEndSpacer,
Key::Home,
],
),
KeyRow::new(
1.0,
vec![
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I,
Key::O,
Key::P,
Key::LBracket,
Key::RBracket,
Key::BackSlash,
Key::RowEndSpacer,
Key::PgUp,
],
),
KeyRow::new(
1.0,
vec![
Key::Caps,
Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L,
Key::SemiColon,
Key::Quote,
Key::Return,
Key::RowEndSpacer,
Key::PgDn,
],
),
KeyRow::new(
1.0,
vec![
Key::LShift,
Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Comma,
Key::Period,
Key::FwdSlash,
Key::Rshift,
Key::RowEndSpacer,
Key::End,
],
),
KeyRow::new(
1.0,
vec![
Key::LCtrl,
Key::LFn,
Key::Meta,
Key::LAlt,
Key::Space,
Key::RAlt,
Key::PrtSc,
Key::RCtrl,
Key::ArrowSpacer,
Key::Up,
Key::ArrowSpacer,
Key::RowEndSpacer,
Key::RFn,
],
),
KeyRow::new(
1.0,
vec![
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::Left,
Key::Down,
Key::Right,
Key::ArrowSpacer,
],
),
],
}
}
}

View File

@@ -0,0 +1,156 @@
use super::{KeyLayout, KeyRow};
use crate::keys::Key;
impl KeyLayout {
pub fn ga401_layout() -> Self {
Self {
matches: vec!["GA401".into(), "GA402".into()],
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,
vec![
Key::NormalSpacer,
Key::FuncSpacer,
Key::VolDown,
Key::VolUp,
Key::MicMute,
Key::Rog,
],
),
KeyRow::new(
0.8,
vec![
Key::Esc,
Key::FuncSpacer,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::FuncSpacer, // not sure which key to put here
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::FuncSpacer,
Key::F9,
Key::F10,
Key::F11,
Key::F12,
],
),
KeyRow::new(
1.0,
vec![
Key::Tilde,
Key::N1,
Key::N2,
Key::N3,
Key::N4,
Key::N5,
Key::N6,
Key::N7,
Key::N8,
Key::N9,
Key::N0,
Key::Hyphen,
Key::Equals,
Key::BkSpc,
],
),
KeyRow::new(
1.0,
vec![
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I,
Key::O,
Key::P,
Key::LBracket,
Key::RBracket,
Key::BackSlash,
],
),
KeyRow::new(
1.0,
vec![
Key::Caps,
Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L,
Key::SemiColon,
Key::Quote,
Key::Return,
],
),
KeyRow::new(
1.0,
vec![
Key::LShift,
Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Comma,
Key::Period,
Key::FwdSlash,
Key::Rshift,
],
),
KeyRow::new(
1.0,
vec![
Key::LCtrl,
Key::LFn,
Key::Meta,
Key::LAlt,
Key::Space,
Key::RAlt,
Key::PrtSc,
Key::RCtrl,
Key::ArrowSpacer,
Key::Up,
Key::ArrowSpacer,
],
),
KeyRow::new(
1.2,
vec![
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::Left,
Key::Down,
Key::Right,
Key::ArrowSpacer,
],
),
],
}
}
}

View File

@@ -0,0 +1,178 @@
use super::{KeyLayout, KeyRow};
use crate::keys::Key;
impl KeyLayout {
pub fn gx502_layout() -> Self {
Self {
matches: vec!["GX502".into(), "GU502".into()],
locale: "US".to_string(),
rows: vec![
KeyRow::new(
0.8,
vec![
Key::NormalSpacer,
Key::FuncSpacer,
Key::VolDown,
Key::VolUp,
Key::MicMute,
Key::Rog,
],
),
KeyRow::new(
0.8,
vec![
Key::Esc,
Key::FuncSpacer,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::FuncSpacer, // not sure which key to put here
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::FuncSpacer,
Key::F9,
Key::F10,
Key::F11,
Key::F12,
Key::RowEndSpacer,
Key::Del,
],
),
KeyRow::new(
1.0,
vec![
Key::Tilde,
Key::N1,
Key::N2,
Key::N3,
Key::N4,
Key::N5,
Key::N6,
Key::N7,
Key::N8,
Key::N9,
Key::N0,
Key::Hyphen,
Key::Equals,
Key::BkSpc3_1,
Key::BkSpc3_2,
Key::BkSpc3_3,
Key::RowEndSpacer,
Key::Home,
],
),
KeyRow::new(
1.0,
vec![
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I,
Key::O,
Key::P,
Key::LBracket,
Key::RBracket,
Key::BackSlash,
Key::RowEndSpacer,
Key::PgUp,
],
),
KeyRow::new(
1.0,
vec![
Key::Caps,
Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L,
Key::SemiColon,
Key::Quote,
Key::Return3_1,
Key::Return3_2,
Key::Return3_3,
Key::RowEndSpacer,
Key::PgDn,
],
),
KeyRow::new(
1.0,
vec![
Key::LShift,
Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Comma,
Key::Period,
Key::FwdSlash,
Key::Rshift3_1,
Key::Rshift3_2,
Key::Rshift3_3,
Key::RowEndSpacer,
Key::End,
],
),
KeyRow::new(
1.0,
vec![
Key::LCtrl,
Key::LFn,
Key::Meta,
Key::LAlt,
Key::Space5_1,
Key::Space5_2,
Key::Space5_3,
Key::Space5_4,
Key::Space5_5,
Key::RAlt,
Key::PrtSc,
Key::RCtrl,
Key::ArrowSpacer,
Key::Up,
Key::ArrowSpacer,
Key::RowEndSpacer,
Key::RFn,
],
),
KeyRow::new(
1.0,
vec![
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::ArrowSpacer,
Key::Left,
Key::Down,
Key::Right,
Key::ArrowSpacer,
],
),
],
}
}
}

View File

@@ -0,0 +1,69 @@
/// Hardcoded layout. Was used to generate a toml default
pub mod g513;
/// Hardcoded layout. Was used to generate a toml default
pub mod ga401;
/// Hardcoded layout. Was used to generate a toml default
pub mod gx502;
use crate::{error::Error, keys::Key};
use serde::{Deserialize, Serialize};
use std::{fs::OpenOptions, io::Read, path::Path, slice::Iter};
#[derive(Debug, Deserialize, Serialize)]
pub struct KeyLayout {
/// A series of board names that this layout can be used for. The board names
/// stored with the layout can be globbed, e.g, GA401 will match all of the
/// GA401I and GA401Q range variants.
///
/// `/sys/class/dmi/id/board_name`
matches: Vec<String>,
locale: String,
rows: Vec<KeyRow>,
}
impl KeyLayout {
pub fn from_file(path: &Path) -> Result<Self, Error> {
let mut file = OpenOptions::new().read(true).open(path)?;
let mut buf = String::new();
let read_len = file.read_to_string(&mut buf)?;
if read_len == 0 {
return Err(Error::Io(std::io::ErrorKind::InvalidData.into()));
} else {
return Ok(toml::from_str::<Self>(&buf)?);
}
}
pub fn matches(&self, board_name: &str) -> bool {
let board = board_name.to_ascii_uppercase();
for tmp in self.matches.iter() {
if board.contains(tmp.as_str()) {
return true;
}
}
false
}
pub fn rows(&self) -> Iter<KeyRow> {
self.rows.iter()
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct KeyRow {
height: f32,
row: Vec<Key>,
}
impl KeyRow {
pub fn new(height: f32, row: Vec<Key>) -> Self {
Self { height, row }
}
pub fn row(&self) -> Iter<Key> {
self.row.iter()
}
pub fn height(&self) -> f32 {
self.height
}
}

View File

@@ -8,13 +8,14 @@ pub use builtin_modes::*;
mod per_key_rgb;
pub use per_key_rgb::*;
pub mod error;
pub mod key_to_str;
pub mod keys;
pub use key_to_str::*;
pub mod layouts;
pub mod usb;
pub mod error;
pub const LED_MSG_LEN: usize = 17;
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RED: Colour = Colour(0xff, 0x00, 0x00);

View File

@@ -1,3 +1,5 @@
use crate::keys::Key;
/// A `KeyColourArray` contains all data to change the full set of keyboard
/// key colours individually.
///
@@ -45,16 +47,16 @@ impl KeyColourArray {
#[inline]
pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) {
if let Some((rr, gg, bb)) = self.key(key) {
*rr = r;
*gg = g;
*bb = b;
if let Some(c) = self.rgb(key) {
c[0] = r;
c[1] = g;
c[2] = b;
}
}
/// Indexes in to `KeyColourArray` at the correct row and column
/// to set a series of three bytes to the chosen R,G,B values
pub fn key(&mut self, key: Key) -> Option<(&mut u8, &mut u8, &mut u8)> {
pub fn rgb(&mut self, key: Key) -> Option<&mut [u8]> {
// Tuples are indexes in to array
let (row, col) = match key {
Key::VolDown => (0, 15),
@@ -91,9 +93,9 @@ impl KeyColourArray {
Key::N0 => (3, 21),
Key::Hyphen => (3, 24),
Key::Equals => (3, 27),
Key::BkSpc1 => (3, 30),
Key::BkSpc2 => (3, 33),
Key::BkSpc3 => (3, 36),
Key::BkSpc3_1 => (3, 30),
Key::BkSpc3_2 => (3, 33),
Key::BkSpc3_3 => (3, 36),
Key::Home => (3, 39),
Key::Tab => (3, 54),
//
@@ -125,11 +127,16 @@ impl KeyColourArray {
Key::SemiColon => (5, 51),
Key::Quote => (5, 54),
//
Key::Ret1 => (6, 12),
Key::Ret2 => (6, 15),
Key::Ret3 => (6, 18),
Key::Return => (6, 9),
Key::Return3_1 => (6, 12),
Key::Return3_2 => (6, 15),
Key::Return3_3 => (6, 18),
Key::PgDn => (6, 21),
Key::LShift => (6, 36),
// TODO: Find correct locations
Key::LShift3_1 => (6, 36),
Key::LShift3_2 => (6, 36),
Key::LShift3_3 => (6, 36),
Key::Z => (6, 42),
Key::X => (6, 45),
Key::C => (6, 48),
@@ -141,19 +148,21 @@ impl KeyColourArray {
Key::Comma => (7, 15),
Key::Period => (7, 18),
Key::FwdSlash => (7, 21),
Key::Rshift1 => (7, 27),
Key::Rshift2 => (7, 30),
Key::Rshift3 => (7, 33),
Key::Rshift => (7, 24),
Key::Rshift3_1 => (7, 27),
Key::Rshift3_2 => (7, 30),
Key::Rshift3_3 => (7, 33),
Key::End => (7, 36),
Key::LCtrl => (7, 51),
Key::LFn => (7, 54),
//
Key::Meta => (8, 9),
Key::LAlt => (8, 12),
Key::Space1 => (8, 15),
Key::Space2 => (8, 18),
Key::Space3 => (8, 21),
Key::Space4 => (8, 24),
Key::Space5_1 => (8, 15),
Key::Space5_2 => (8, 18),
Key::Space5_3 => (8, 21),
Key::Space5_4 => (8, 24),
Key::Space5_5 => (8, 27),
Key::RAlt => (8, 30),
Key::PrtSc => (8, 33),
Key::RCtrl => (8, 36),
@@ -164,16 +173,45 @@ impl KeyColourArray {
//
Key::Down => (10, 9),
Key::Right => (10, 12),
Key::None => return None,
Key::NormalBlank
| Key::FuncBlank
| Key::NormalSpacer
| Key::FuncSpacer
| Key::ArrowBlank
| Key::ArrowSpacer
| Key::UpRegular
| Key::DownRegular
| Key::LeftRegular
| Key::RightRegular
| Key::UpSplit
| Key::DownSplit
| Key::LeftSplit
| Key::RightSplit
| Key::ArrowRegularBlank
| Key::ArrowRegularSpacer
| Key::ArrowSplitBlank
| Key::ArrowSplitSpacer
| Key::RshiftSmall
| Key::LCtrlMed
| Key::MediaPlay
| Key::MediaStop
| Key::MediaPrev
| Key::MediaNext
| Key::Pause
| Key::NumLock
| Key::Star
| Key::NumPadDel
| Key::NumPadPlus
| Key::NumPadEnter
| Key::NumPadPause
| Key::NumPadPrtSc
| Key::NumPadHome
| Key::RCtrlLarge
| Key::RowEndSpacer => return None,
Key::Fan | Key::Space | Key::BkSpc => return None,
};
// LOLOLOLOLOLOLOL! Look it's safe okay
unsafe {
Some((
&mut *(&mut self.0[row][col] as *mut u8),
&mut *(&mut self.0[row][col + 1] as *mut u8),
&mut *(&mut self.0[row][col + 2] as *mut u8),
))
}
Some(&mut self.0[row][col..2])
}
#[inline]
@@ -182,108 +220,6 @@ impl KeyColourArray {
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Key {
VolUp,
VolDown,
MicMute,
Rog,
Esc,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Del,
Tilde,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Hyphen,
Equals,
BkSpc1,
BkSpc2,
BkSpc3,
Home,
Tab,
Q,
W,
E,
R,
T,
Y,
U,
I,
O,
P,
LBracket,
RBracket,
BackSlash,
PgUp,
Caps,
A,
S,
D,
F,
G,
H,
J,
K,
L,
SemiColon,
Quote,
Ret1,
Ret2,
Ret3,
PgDn,
LShift,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
FwdSlash,
Rshift1,
Rshift2,
Rshift3,
End,
LCtrl,
LFn,
Meta,
LAlt,
Space1,
Space2,
Space3,
Space4,
RAlt,
PrtSc,
RCtrl,
Up,
Down,
Left,
Right,
RFn,
None,
}
pub trait KeyLayout {
fn get_rows(&self) -> &Vec<[Key; 17]>;
}
@@ -301,37 +237,37 @@ impl Default for GX502Layout {
fn default() -> Self {
GX502Layout(vec![
[
Key::None,
Key::None,
Key::NormalSpacer,
Key::FuncSpacer,
Key::VolDown,
Key::VolUp,
Key::MicMute,
Key::Rog,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
],
[
Key::Esc,
Key::None,
Key::NormalBlank,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::None, // not sure which key to put here
Key::NormalBlank, // not sure which key to put here
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::F9,
Key::NormalBlank,
Key::F9,
Key::F10,
Key::F11,
@@ -352,9 +288,9 @@ impl Default for GX502Layout {
Key::N0,
Key::Hyphen,
Key::Equals,
Key::BkSpc1,
Key::BkSpc2,
Key::BkSpc3,
Key::BkSpc3_1,
Key::BkSpc3_2,
Key::BkSpc3_3,
Key::Home,
],
[
@@ -390,9 +326,9 @@ impl Default for GX502Layout {
Key::SemiColon,
Key::Quote,
Key::Quote,
Key::Ret1,
Key::Ret2,
Key::Ret3,
Key::Return3_1,
Key::Return3_2,
Key::Return3_3,
Key::PgDn,
],
[
@@ -409,9 +345,9 @@ impl Default for GX502Layout {
Key::Period,
Key::FwdSlash,
Key::FwdSlash,
Key::Rshift1,
Key::Rshift2,
Key::Rshift3,
Key::Rshift3_1,
Key::Rshift3_2,
Key::Rshift3_3,
Key::End,
],
[
@@ -419,11 +355,11 @@ impl Default for GX502Layout {
Key::LFn,
Key::Meta,
Key::LAlt,
Key::Space1,
Key::Space2,
Key::Space3,
Key::Space4,
Key::Space4,
Key::Space5_1,
Key::Space5_2,
Key::Space5_3,
Key::Space5_4,
Key::Space5_5,
Key::RAlt,
Key::PrtSc,
Key::RCtrl,
@@ -434,23 +370,23 @@ impl Default for GX502Layout {
Key::RFn,
],
[
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::NormalBlank,
Key::Left,
Key::Down,
Key::Right,
Key::None,
Key::NormalBlank,
],
])
}

View File

@@ -21,11 +21,13 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize, Default)]
pub enum AuraDevice {
Tuf,
X1854,
X1869,
X1866,
#[default]
X19B6,
Unknown,
}
@@ -33,6 +35,7 @@ pub enum AuraDevice {
impl From<&str> for AuraDevice {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"tuf" => AuraDevice::Tuf,
"1866" => AuraDevice::X1866,
"1869" => AuraDevice::X1869,
"1854" => AuraDevice::X1854,
@@ -48,12 +51,29 @@ impl From<&str> for AuraDevice {
/// 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)]
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct AuraPowerDev {
pub tuf: Vec<AuraDevTuf>,
pub x1866: Vec<AuraDev1866>,
pub x19b6: Vec<AuraDev19b6>,
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum AuraDevTuf {
Boot,
Awake,
Sleep,
Keyboard,
}
impl AuraDevTuf {
pub const fn dev_id() -> &'static str {
"tuf"
}
}
/// # Bits for older 0x1866 keyboard model
///
/// Keybord and Lightbar require Awake, Boot and Sleep apply to both

View File

@@ -1,6 +1,6 @@
[package]
name = "rog-control-center"
version = "0.1.4"
version = "1.0.0"
authors = ["Luke D. Jones <luke@ljones.dev>"]
edition = "2021"
@@ -33,13 +33,3 @@ dirs = "3.0.1"
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -1,17 +1,20 @@
use std::{
f64::consts::PI,
io::Write,
sync::{
atomic::{AtomicBool, Ordering},
atomic::{AtomicBool, AtomicU8, Ordering},
Arc,
},
time::Duration,
time::{Duration, Instant},
};
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};
use crate::{
config::Config, error::Result, get_ipc_file, page_states::PageDataStates, Page,
RogDbusClientBlocking, SHOWING_GUI,
};
pub struct RogApp<'a> {
pub page: Page,
@@ -24,6 +27,14 @@ pub struct RogApp<'a> {
// TODO: can probably just open and read whenever
pub config: Config,
pub asus_dbus: RogDbusClientBlocking<'a>,
/// 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>,
}
impl<'a> RogApp<'a> {
@@ -34,11 +45,57 @@ impl<'a> RogApp<'a> {
show_gui: Arc<AtomicBool>,
states: PageDataStates,
_cc: &eframe::CreationContext<'_>,
) -> Self {
let (dbus, _) = RogDbusClientBlocking::new().unwrap();
let supported = dbus.proxies().supported().supported_functions().unwrap();
) -> Result<Self> {
let (dbus, _) = RogDbusClientBlocking::new()?;
let supported = dbus.proxies().supported().supported_functions()?;
Self {
// 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,
states,
page: Page::System,
@@ -46,7 +103,12 @@ impl<'a> RogApp<'a> {
running_in_bg: start_closed,
config,
asus_dbus: dbus,
}
oscillator1,
oscillator2,
oscillator3,
oscillator_toggle,
oscillator_freq,
})
}
}
@@ -70,9 +132,15 @@ impl<'a> eframe::App for RogApp<'a> {
states,
..
} = self;
if states.refresh_if_notfied(supported, dbus) {
ctx.request_repaint();
}
states
.refresh_if_notfied(supported, dbus)
.map(|repaint| {
if repaint {
ctx.request_repaint();
}
})
.map_err(|e| self.states.error = Some(e.to_string()))
.ok();
let page = self.page;
@@ -118,8 +186,9 @@ impl<'a> eframe::App for RogApp<'a> {
self.system_page(ctx);
} else if page == Page::AuraEffects {
self.aura_page(ctx);
} else if page == Page::AnimeMatrix {
self.anime_page(ctx);
// TODO: Anime page is not complete
// } else if page == Page::AnimeMatrix {
// self.anime_page(ctx);
} else if page == Page::FanCurves {
self.fan_curve_page(ctx);
}

View File

@@ -9,7 +9,7 @@ use serde_derive::{Deserialize, Serialize};
use crate::error::Error;
const CFG_DIR: &str = "rog";
const CFG_FILE_NAME: &str = "app-template.cfg";
const CFG_FILE_NAME: &str = "rog-control-center.cfg";
#[derive(Debug, Deserialize, Serialize)]
#[serde(default)]

View File

@@ -1,5 +1,7 @@
use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
@@ -7,6 +9,7 @@ pub enum Error {
ConfigLoadFail,
ConfigLockFail,
XdgVars,
Zbus(zbus::Error),
}
impl fmt::Display for Error {
@@ -18,6 +21,7 @@ impl fmt::Display for Error {
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"),
Error::Zbus(err) => write!(f, "Error: {}", err),
}
}
}
@@ -30,14 +34,14 @@ impl From<std::io::Error> for Error {
}
}
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)
}
}
impl From<zbus::Error> for Error {
fn from(err: zbus::Error) -> Self {
Error::Zbus(err)
}
}

View File

@@ -11,10 +11,19 @@ pub use app::RogApp;
pub mod config;
pub mod error;
#[cfg(feature = "mocking")]
pub mod mocking;
pub mod notify;
pub mod page_states;
pub mod pages;
pub mod startup_error;
pub mod widgets;
#[cfg(feature = "mocking")]
pub use mocking::RogDbusClientBlocking;
#[cfg(not(feature = "mocking"))]
pub use rog_dbus::RogDbusClientBlocking;
use nix::{sys::stat, unistd};
use tempfile::TempDir;
//use log::{error, info, warn};

View File

@@ -1,10 +1,14 @@
use rog_aura::layouts::KeyLayout;
use rog_control_center::{
config::Config, get_ipc_file, notify::start_notifications, on_tmp_dir_exists,
page_states::PageDataStates, RogApp, SHOW_GUI,
page_states::PageDataStates, startup_error::AppErrorShow, RogApp, RogDbusClientBlocking,
SHOW_GUI,
};
use rog_dbus::RogDbusClientBlocking;
use std::{
fs::{self, OpenOptions},
io::Read,
path::PathBuf,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
@@ -13,7 +17,31 @@ use std::{
time::Duration,
};
#[cfg(not(feature = "mocking"))]
const DATA_DIR: &str = "/usr/share/rog-gui/";
#[cfg(feature = "mocking")]
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
fn main() -> Result<(), Box<dyn std::error::Error>> {
let native_options = eframe::NativeOptions {
decorated: false,
transparent: false,
min_window_size: Some(egui::vec2(840.0, 600.0)),
max_window_size: Some(egui::vec2(840.0, 600.0)),
..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()))),
);
})
.unwrap();
// Startup
let mut config = Config::load()?;
let start_closed = config.startup_in_background;
@@ -23,8 +51,39 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
config.save()?;
}
let (dbus, _) = RogDbusClientBlocking::new().unwrap();
let supported = dbus.proxies().supported().supported_functions().unwrap();
// Find and load a matching layout for laptop
let mut file = OpenOptions::new()
.read(true)
.open(PathBuf::from(BOARD_NAME))
.map_err(|e| {
println!("{BOARD_NAME}, {e}");
e
})?;
let mut board_name = String::new();
file.read_to_string(&mut board_name)?;
let mut layout = KeyLayout::ga401_layout(); // default
let mut path = PathBuf::from(DATA_DIR);
#[cfg(feature = "mocking")]
{
board_name = "gl504".to_string();
path.pop();
path.push("rog-aura");
path.push("data");
}
path.push("layouts");
let path = path.as_path();
for p in fs::read_dir(path).map_err(|e| {
println!("{:?}, {e}", path);
e
})? {
let tmp = KeyLayout::from_file(&p?.path()).unwrap();
if tmp.matches(board_name.as_str()) {
layout = tmp;
break;
}
}
// 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));
@@ -34,19 +93,33 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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,
);
let states = {
let supported = dbus
.proxies()
.supported()
.supported_functions()
.map_err(|e| {
eframe::run_native(
"ROG Control Center",
native_options.clone(),
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
);
})
.unwrap();
PageDataStates::new(
layout,
notifs_enabled.clone(),
charge_notified.clone(),
bios_notified.clone(),
aura_notified.clone(),
anime_notified.clone(),
profiles_notified.clone(),
fans_notified.clone(),
&supported,
&dbus,
)? // TODO: if error, show alt GUI containing the error message
};
if config.enable_notifications {
start_notifications(
@@ -70,11 +143,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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 || {
@@ -92,6 +160,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
eframe::run_native(
"ROG Control Center",
native_options,
Box::new(move |cc| Box::new(RogApp::new(start_closed, config, should, states, cc))),
Box::new(move |cc| {
Box::new(RogApp::new(start_closed, config, should, states, cc).unwrap())
}),
);
}

View File

@@ -0,0 +1,226 @@
use std::collections::BTreeMap;
use rog_aura::{
usb::{AuraDev19b6, AuraDevice, AuraPowerDev},
AuraEffect, AuraModeNum, AuraZone,
};
use rog_profiles::fan_curve_set::{CurveData, FanCurveSet};
use rog_supported::{
AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions,
PlatformProfileFunctions, RogBiosSupportedFunctions, SupportedFunctions,
};
use crate::error::Result;
const NOPE: &'static str = "";
pub struct RogDbusClientBlocking<'a> {
_phantom: &'a str,
}
impl<'a> Default for RogDbusClientBlocking<'a> {
fn default() -> Self {
Self {
_phantom: Default::default(),
}
}
}
impl<'a> RogDbusClientBlocking<'a> {
pub fn new() -> Result<(Self, bool)> {
Ok((Self { _phantom: NOPE }, true))
}
pub fn proxies(&self) -> Proxies {
Proxies
}
}
pub struct Proxies;
impl Proxies {
pub fn rog_bios(&self) -> Bios {
Bios
}
pub fn profile(&self) -> Profile {
Profile
}
pub fn led(&self) -> Led {
Led
}
pub fn anime(&self) -> Anime {
Anime
}
pub fn charge(&self) -> Profile {
Profile
}
pub fn supported(&self) -> Supported {
Supported
}
}
pub struct Bios;
impl Bios {
pub fn post_boot_sound(&self) -> Result<i16> {
Ok(1)
}
pub fn dedicated_graphic_mode(&self) -> Result<i16> {
Ok(1)
}
pub fn panel_overdrive(&self) -> Result<i16> {
Ok(1)
}
pub fn set_post_boot_sound(&self, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_dedicated_graphic_mode(&self, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_panel_overdrive(&self, _b: bool) -> Result<()> {
Ok(())
}
}
pub struct Profile;
impl Profile {
pub fn profiles(&self) -> Result<Vec<rog_profiles::Profile>> {
Ok(vec![
rog_profiles::Profile::Balanced,
rog_profiles::Profile::Performance,
rog_profiles::Profile::Quiet,
])
}
pub fn active_profile(&self) -> Result<rog_profiles::Profile> {
Ok(rog_profiles::Profile::Performance)
}
pub fn enabled_fan_profiles(&self) -> Result<Vec<rog_profiles::Profile>> {
Ok(vec![
rog_profiles::Profile::Performance,
rog_profiles::Profile::Balanced,
])
}
pub fn fan_curve_data(&self, _p: rog_profiles::Profile) -> Result<FanCurveSet> {
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];
Ok(curve)
}
pub fn set_fan_curve(&self, _p: rog_profiles::Profile, _c: CurveData) -> Result<()> {
Ok(())
}
pub fn set_fan_curve_enabled(&self, _p: rog_profiles::Profile, _b: bool) -> Result<()> {
Ok(())
}
pub fn limit(&self) -> Result<i16> {
Ok(66)
}
pub fn set_limit(&self, _l: u8) -> Result<()> {
Ok(())
}
pub fn set_active_profile(&self, _p: rog_profiles::Profile) -> Result<()> {
Ok(())
}
}
pub struct Led;
impl Led {
pub fn led_modes(&self) -> Result<BTreeMap<AuraModeNum, AuraEffect>> {
let mut data = BTreeMap::new();
data.insert(AuraModeNum::Static, AuraEffect::default());
data.insert(AuraModeNum::Star, AuraEffect::default());
data.insert(AuraModeNum::Strobe, AuraEffect::default());
data.insert(AuraModeNum::Rain, AuraEffect::default());
data.insert(AuraModeNum::Rainbow, AuraEffect::default());
data.insert(AuraModeNum::Ripple, AuraEffect::default());
data.insert(AuraModeNum::Breathe, AuraEffect::default());
data.insert(AuraModeNum::Comet, AuraEffect::default());
data.insert(AuraModeNum::Flash, AuraEffect::default());
data.insert(AuraModeNum::Laser, AuraEffect::default());
data.insert(AuraModeNum::Pulse, AuraEffect::default());
Ok(data)
}
pub fn led_mode(&self) -> Result<AuraModeNum> {
Ok(AuraModeNum::Rainbow)
}
pub fn led_brightness(&self) -> Result<i16> {
Ok(1)
}
pub fn leds_enabled(&self) -> Result<AuraPowerDev> {
Ok(AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: vec![
AuraDev19b6::BootKeyb,
AuraDev19b6::AwakeKeyb,
AuraDev19b6::SleepLogo,
AuraDev19b6::AwakeLogo,
],
})
}
pub fn set_leds_power(&self, _a: AuraPowerDev, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_led_mode(&self, _a: &AuraEffect) -> Result<()> {
Ok(())
}
}
pub struct Anime;
impl Anime {
pub fn boot_enabled(&self) -> Result<bool> {
Ok(true)
}
pub fn awake_enabled(&self) -> Result<bool> {
Ok(true)
}
pub fn set_on_off(&self, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_boot_on_off(&self, _b: bool) -> Result<()> {
Ok(())
}
}
pub struct Supported;
impl Supported {
pub fn supported_functions(&self) -> Result<SupportedFunctions> {
Ok(SupportedFunctions {
anime_ctrl: AnimeSupportedFunctions(true),
charge_ctrl: ChargeSupportedFunctions {
charge_level_set: true,
},
platform_profile: PlatformProfileFunctions {
platform_profile: true,
fan_curves: true,
},
keyboard_led: LedSupportedFunctions {
prod_id: AuraDevice::X19B6,
brightness_set: true,
stock_led_modes: vec![
AuraModeNum::Rain,
AuraModeNum::Rainbow,
AuraModeNum::Star,
AuraModeNum::Static,
AuraModeNum::Strobe,
],
multizone_led_mode: vec![
AuraZone::Key1,
AuraZone::Key2,
AuraZone::Key3,
AuraZone::Key4,
AuraZone::BarLeft,
AuraZone::BarRight,
AuraZone::Logo,
],
per_key_led_mode: true,
},
rog_bios_ctrl: RogBiosSupportedFunctions {
post_sound: true,
dedicated_gfx: true,
panel_overdrive: true,
dgpu_disable: true,
egpu_enable: true,
},
})
}
}

View File

@@ -7,11 +7,12 @@ use std::{
};
use egui::Vec2;
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum};
use rog_dbus::RogDbusClientBlocking;
use rog_aura::{layouts::KeyLayout, usb::AuraPowerDev, AuraEffect, AuraModeNum};
use rog_profiles::{fan_curve_set::FanCurveSet, FanCurvePU, Profile};
use rog_supported::SupportedFunctions;
use crate::{error::Result, RogDbusClientBlocking};
#[derive(Clone, Debug)]
pub struct BiosState {
/// To be shared to a thread that checks notifications.
@@ -30,28 +31,28 @@ impl BiosState {
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
) -> Result<Self> {
Ok(Self {
was_notified,
post_sound: if supported.rog_bios_ctrl.post_sound {
dbus.proxies().rog_bios().post_boot_sound().unwrap() != 0
dbus.proxies().rog_bios().post_boot_sound()? != 0
} else {
false
},
dedicated_gfx: if supported.rog_bios_ctrl.dedicated_gfx {
dbus.proxies().rog_bios().dedicated_graphic_mode().unwrap() != 0
dbus.proxies().rog_bios().dedicated_graphic_mode()? != 0
} else {
false
},
panel_overdrive: if supported.rog_bios_ctrl.panel_overdrive {
dbus.proxies().rog_bios().panel_overdrive().unwrap() != 0
dbus.proxies().rog_bios().panel_overdrive()? != 0
} else {
false
},
// TODO: needs supergfx
dgpu_disable: supported.rog_bios_ctrl.dgpu_disable,
egpu_enable: supported.rog_bios_ctrl.egpu_enable,
}
})
}
}
@@ -67,20 +68,20 @@ impl ProfilesState {
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
) -> Result<Self> {
Ok(Self {
was_notified,
list: if supported.platform_profile.platform_profile {
dbus.proxies().profile().profiles().unwrap()
dbus.proxies().profile().profiles()?
} else {
vec![]
},
current: if supported.platform_profile.platform_profile {
dbus.proxies().profile().active_profile().unwrap()
dbus.proxies().profile().active_profile()?
} else {
Profile::Balanced
},
}
})
}
}
@@ -99,9 +100,9 @@ impl FanCurvesState {
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
) -> Result<Self> {
let profiles = if supported.platform_profile.platform_profile {
dbus.proxies().profile().profiles().unwrap()
dbus.proxies().profile().profiles()?
} else {
vec![Profile::Balanced, Profile::Quiet, Profile::Performance]
};
@@ -109,8 +110,7 @@ impl FanCurvesState {
HashSet::from_iter(
dbus.proxies()
.profile()
.enabled_fan_profiles()
.unwrap()
.enabled_fan_profiles()?
.iter()
.cloned(),
)
@@ -121,8 +121,9 @@ impl FanCurvesState {
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);
if let Ok(curve) = dbus.proxies().profile().fan_curve_data(*p) {
curves.insert(*p, curve);
}
} else {
let mut curve = FanCurveSet::default();
curve.cpu.pwm = [30, 40, 60, 100, 140, 180, 200, 250];
@@ -134,19 +135,19 @@ impl FanCurvesState {
});
let show_curve = if supported.platform_profile.fan_curves {
dbus.proxies().profile().active_profile().unwrap()
dbus.proxies().profile().active_profile()?
} else {
Profile::Balanced
};
Self {
Ok(Self {
was_notified,
show_curve,
show_graph: FanCurvePU::CPU,
enabled,
curves,
drag_delta: Vec2::default(),
}
})
}
}
@@ -158,6 +159,9 @@ pub struct AuraState {
pub enabled: AuraPowerDev,
/// Brightness from 0-3
pub bright: i16,
pub wave_red: [u8; 22],
pub wave_green: [u8; 22],
pub wave_blue: [u8; 22],
}
impl AuraState {
@@ -165,27 +169,44 @@ impl AuraState {
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
) -> Result<Self> {
Ok(Self {
was_notified,
current_mode: if !supported.keyboard_led.stock_led_modes.is_empty() {
dbus.proxies().led().led_mode().unwrap()
dbus.proxies().led().led_mode().unwrap_or_default()
} else {
AuraModeNum::Static
},
modes: if !supported.keyboard_led.stock_led_modes.is_empty() {
dbus.proxies().led().led_modes().unwrap()
dbus.proxies().led().led_modes().unwrap_or_default()
} else {
BTreeMap::new()
},
enabled: dbus.proxies().led().leds_enabled().unwrap(),
enabled: dbus.proxies().led().leds_enabled().unwrap_or_default(),
bright: if !supported.keyboard_led.brightness_set {
dbus.proxies().led().led_brightness().unwrap()
dbus.proxies().led().led_brightness().unwrap_or_default()
} else {
2
},
wave_red: [0u8; 22],
wave_green: [0u8; 22],
wave_blue: [0u8; 22],
})
}
/// Bump value in to the wave and surf all along.
pub fn nudge_wave(&mut self, r: u8, g: u8, b: u8) {
for i in (0..self.wave_red.len()).rev() {
if i > 0 {
self.wave_red[i] = self.wave_red[i - 1];
self.wave_green[i] = self.wave_green[i - 1];
self.wave_blue[i] = self.wave_blue[i - 1];
}
}
self.wave_red[0] = r;
self.wave_green[0] = g;
self.wave_blue[0] = b;
}
}
@@ -203,28 +224,29 @@ impl AnimeState {
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
) -> Result<Self> {
Ok(Self {
was_notified,
boot: if supported.anime_ctrl.0 {
dbus.proxies().anime().boot_enabled().unwrap()
dbus.proxies().anime().boot_enabled()?
} else {
false
},
awake: if supported.anime_ctrl.0 {
dbus.proxies().anime().awake_enabled().unwrap()
dbus.proxies().anime().awake_enabled()?
} else {
false
},
// TODO:
sleep: false,
bright: 200,
}
})
}
}
#[derive(Debug)]
pub struct PageDataStates {
pub keyboard_layout: KeyLayout,
pub notifs_enabled: Arc<AtomicBool>,
pub was_notified: Arc<AtomicBool>,
/// Because much of the app state here is the same as `RogBiosSupportedFunctions`
@@ -240,6 +262,7 @@ pub struct PageDataStates {
impl PageDataStates {
pub fn new(
keyboard_layout: KeyLayout,
notifs_enabled: Arc<AtomicBool>,
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
@@ -249,56 +272,112 @@ impl PageDataStates {
fans_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Self {
Self {
) -> Result<Self> {
Ok(Self {
keyboard_layout,
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),
charge_limit: dbus.proxies().charge().limit()?,
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 {
) -> Result<bool> {
let mut notified = false;
if self.was_notified.load(Ordering::SeqCst) {
self.charge_limit = dbus.proxies().charge().limit().unwrap();
self.charge_limit = dbus.proxies().charge().limit()?;
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 = 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 = 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 =
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);
FanCurvesState::new(self.fan_curves.was_notified.clone(), supported, dbus)?;
self.fan_curves.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
notified
Ok(notified)
}
}
impl Default for PageDataStates {
fn default() -> Self {
Self {
keyboard_layout: KeyLayout::ga401_layout(),
notifs_enabled: Default::default(),
was_notified: Default::default(),
bios: BiosState {
was_notified: Default::default(),
post_sound: Default::default(),
dedicated_gfx: Default::default(),
panel_overdrive: Default::default(),
dgpu_disable: Default::default(),
egpu_enable: Default::default(),
},
aura: AuraState {
was_notified: Default::default(),
current_mode: AuraModeNum::Static,
modes: Default::default(),
enabled: AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: vec![],
},
bright: Default::default(),
wave_red: Default::default(),
wave_green: Default::default(),
wave_blue: Default::default(),
},
anime: AnimeState {
was_notified: Default::default(),
bright: Default::default(),
boot: Default::default(),
awake: Default::default(),
sleep: Default::default(),
},
profiles: ProfilesState {
was_notified: Default::default(),
list: Default::default(),
current: Default::default(),
},
fan_curves: FanCurvesState {
was_notified: Default::default(),
show_curve: Default::default(),
show_graph: Default::default(),
enabled: Default::default(),
curves: Default::default(),
drag_delta: Default::default(),
},
charge_limit: Default::default(),
error: Default::default(),
}
}
}

View File

@@ -0,0 +1,9 @@
use crate::RogApp;
impl<'a> RogApp<'a> {
pub fn anime_page(&mut self, ctx: &egui::Context) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("In progress");
});
}
}

View File

@@ -0,0 +1,83 @@
use std::{sync::atomic::Ordering, time::Duration};
use egui::Color32;
use rog_aura::AuraModeNum;
use crate::{
widgets::{aura_modes_group, keyboard},
RogApp,
};
impl<'a> RogApp<'a> {
pub fn aura_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
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.0 * 255 / osc;
// dbg!(osc);
let c1 = states
.aura
.modes
.get(&states.aura.current_mode)
.unwrap()
.colour1;
let c2 = states
.aura
.modes
.get(&states.aura.current_mode)
.unwrap()
.colour2;
let mut colour = Color32::from_rgb(c1.0, c1.1, c1.2);
if states.aura.current_mode == AuraModeNum::Pulse {
colour = Color32::from_rgb(
(red * c1.0 as u32 / 100) as u8,
(red * c1.1 as u32 / 100) as u8,
(red * c1.2 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.0 as u32 / 100) as u8,
(red * c2.1 as u32 / 100) as u8,
(red * c2.2 as u32 / 100) as u8,
);
} else {
colour = Color32::from_rgb(
(red * c1.0 as u32 / 100) as u8,
(red * c1.1 as u32 / 100) as u8,
(red * c1.2 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(supported, states, oscillator_freq, dbus, ui);
keyboard(ui, &states.keyboard_layout, &mut states.aura, colour);
});
// Only do repaint request if on this page
ctx.request_repaint_after(Duration::from_millis(33));
}
}

View File

@@ -0,0 +1,82 @@
use crate::{
page_states::{FanCurvesState, ProfilesState},
widgets::fan_graphs,
RogApp, RogDbusClientBlocking,
};
use egui::Ui;
use rog_profiles::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,
);
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();
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 {
let notif = curves.was_notified.clone();
match FanCurvesState::new(notif, supported, dbus) {
Ok(f) => *curves = f,
Err(e) => *do_error = Some(e.to_string()),
}
}
}
}

View File

@@ -0,0 +1,9 @@
mod anime_page;
mod aura_page;
mod fan_curve_page;
mod system_page;
pub use anime_page::*;
pub use aura_page::*;
pub use fan_curve_page::*;
pub use system_page::*;

View File

@@ -0,0 +1,53 @@
use crate::{
widgets::{anime_power_group, aura_power_group, platform_profile, rog_bios_group},
RogApp,
};
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| {
ui.heading("Experimental application for asusd");
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 supported.platform_profile.platform_profile {
platform_profile(states, dbus, ui);
}
});
ui.vertical(|ui| {
ui.separator();
aura_power_group(supported, states, dbus, ui);
});
ui.end_row();
ui.vertical(|ui| {
ui.separator();
rog_bios_group(supported, states, dbus, ui);
});
ui.vertical(|ui| {
ui.separator();
if supported.anime_ctrl.0 {
anime_power_group(supported, states, dbus, ui);
}
});
ui.end_row();
});
});
});
}
}

View File

@@ -0,0 +1,36 @@
use egui::{Button, 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.quit();
}
});
});
});
}
}

View File

@@ -1,77 +0,0 @@
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;
}
});
});
});
});
}
}

View File

@@ -0,0 +1,73 @@
use egui::{RichText, Ui};
use rog_supported::SupportedFunctions;
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
pub fn anime_power_group(
_supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
ui.heading("AniMe Matrix Settings");
ui.label("Options are incomplete. Awake + Boot should work");
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;
}
});
});
});
}

View File

@@ -0,0 +1,213 @@
use std::sync::{
atomic::{AtomicU8, Ordering},
Arc,
};
use egui::{RichText, Ui};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour, Speed};
use rog_supported::SupportedFunctions;
use crate::{
page_states::{AuraState, PageDataStates},
RogDbusClientBlocking,
};
pub fn aura_modes_group(
supported: &SupportedFunctions,
states: &mut PageDataStates,
freq: &mut Arc<AtomicU8>,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
let mut changed = false;
let mut selected = states.aura.current_mode;
let allowed = AuraEffect::allowed_parameters(selected);
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);
}
});
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| {
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);
}
};
let mut dir_button = |a: rog_aura::Direction, ui: &mut Ui| {
ui.selectable_value(&mut effect.direction, a, format!("{:?}", a));
};
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| {
egui::color_picker::color_edit_button_srgb(ui, &mut c1)
});
ui.add_enabled_ui(allowed.colour2, |ui| {
egui::color_picker::color_edit_button_srgb(ui, &mut c2)
});
ui.add_enabled_ui(allowed.speed, |ui| {
ui.horizontal_wrapped(|ui| {
speed_button(Speed::Low, ui);
speed_button(Speed::Med, ui);
speed_button(Speed::High, ui);
});
});
ui.add_enabled_ui(allowed.direction, |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();
match AuraState::new(notif, supported, 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;
}
});
// 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();
}
}

View File

@@ -1,480 +0,0 @@
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();
}
}
}

View File

@@ -0,0 +1,345 @@
use egui::{RichText, Ui};
use rog_aura::{
usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev},
AuraZone,
};
use rog_supported::SupportedFunctions;
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
pub fn aura_power_group(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
ui.heading("LED settings");
match supported.keyboard_led.prod_id {
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866 => {
aura_power1(supported, states, dbus, ui)
}
AuraDevice::X19B6 => aura_power2(supported, states, dbus, ui),
AuraDevice::Tuf => aura_power1(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;
let mut boot = enabled_states.x1866.contains(&AuraDev1866::Boot);
let mut sleep = enabled_states.x1866.contains(&AuraDev1866::Sleep);
let mut keyboard = enabled_states.x1866.contains(&AuraDev1866::Keyboard);
let mut lightbar = enabled_states.x1866.contains(&AuraDev1866::Lightbar);
if supported.keyboard_led.prod_id == 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 !supported.keyboard_led.multizone_led_mode.is_empty() {
if 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 supported.keyboard_led.prod_id == 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,
x1866: vec![],
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);
} else {
let mut enabled = Vec::new();
let mut disabled = Vec::new();
let mut modify_x1866 = |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_x1866(boot, AuraDev1866::Boot);
modify_x1866(sleep, AuraDev1866::Sleep);
modify_x1866(keyboard, AuraDev1866::Keyboard);
if !supported.keyboard_led.multizone_led_mode.is_empty() {
modify_x1866(lightbar, AuraDev1866::Lightbar);
}
let mut send = |enable: bool, data: Vec<AuraDev1866>| {
let options = AuraPowerDev {
tuf: vec![],
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;
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 {
tuf: vec![],
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);
}
}

View File

@@ -1,199 +0,0 @@
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");
}
});
}
}

View File

@@ -0,0 +1,119 @@
use egui::{plot::Points, Ui};
use rog_profiles::{FanCurvePU, Profile};
use crate::{
page_states::{FanCurvesState, ProfilesState},
RogDbusClientBlocking,
};
pub 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("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 = 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() {
dbus.proxies()
.profile()
.set_fan_curve(profiles.current, data.clone())
.map_err(|err| {
*do_error = Some(err.to_string());
})
.ok();
}
});
}

View File

@@ -0,0 +1,123 @@
use egui::{Align, Color32, Vec2};
use rog_aura::{keys::KeyShape, layouts::KeyLayout, AuraModeNum};
use crate::page_states::AuraState;
pub fn keyboard(
ui: &mut egui::Ui,
keyboard_layout: &KeyLayout,
states: &mut AuraState,
mut colour: Color32,
) {
ui.spacing_mut().item_spacing = egui::vec2(0.0, 0.0);
let mut arrows_done = false;
let mut rog_done = false;
for row in keyboard_layout.rows() {
ui.horizontal_top(|ui| {
for (i, key) in row.row().enumerate() {
if states.current_mode == AuraModeNum::Rainbow {
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,
);
}
// your boat
let height = if rog_done {
row.height()
} else {
// Use the first item (always a blank) to stand off the row
rog_done = true;
1.2
};
let shape = KeyShape::from(key);
let label = <&str>::from(key);
if shape.is_arrow_cluster() {
if !arrows_done {
arrow_cluster(ui, colour);
arrows_done = true;
}
} else if shape.is_blank() || shape.is_spacer() {
blank(ui, shape.width(), height);
} else if shape.is_group() {
key_group(ui, colour, shape.width(), height).on_hover_text(label);
} else {
key_shape(ui, colour, shape.width(), height).on_hover_text(label);
}
}
});
}
}
fn key_shape(ui: &mut egui::Ui, colour: Color32, ux: f32, uy: f32) -> egui::Response {
let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0 * ux, 2.0 * uy);
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) {
let visuals = ui.style().interact_selectable(&response, true);
let rect = rect.expand(visuals.expansion);
ui.painter().rect(rect, 0.1, colour, visuals.fg_stroke);
}
response
}
fn key_group(ui: &mut egui::Ui, colour: Color32, ux: f32, uy: f32) -> egui::Response {
let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0 * ux, 2.0 * uy);
let (mut rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
rect = rect.shrink2(Vec2::new(3.0, 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) {
let visuals = ui.style().interact_selectable(&response, true);
let rect = rect.expand(visuals.expansion);
let mut stroke = visuals.fg_stroke;
stroke.color = visuals.bg_fill;
ui.painter().rect(rect, 0.1, colour, stroke);
}
response
}
fn blank(ui: &mut egui::Ui, ux: f32, uy: f32) {
let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0 * ux, 2.0 * uy);
ui.allocate_exact_size(desired_size, egui::Sense::click());
}
/// Draws entire arrow cluster block. This is visibly different to the split-arrows.
fn arrow_cluster(ui: &mut egui::Ui, colour: Color32) {
let height = 0.7;
let space = KeyShape::ArrowSpacer;
let shape = KeyShape::Arrow;
ui.horizontal_top(|ui| {
ui.with_layout(egui::Layout::top_down(Align::LEFT), |ui| {
blank(ui, space.width(), height);
ui.horizontal(|ui| {
blank(ui, KeyShape::RowEndSpacer.width(), height);
blank(ui, KeyShape::RowEndSpacer.width(), height);
key_shape(ui, colour, shape.width(), height).on_hover_text("Left");
});
});
ui.with_layout(egui::Layout::top_down(Align::LEFT), |ui| {
key_shape(ui, colour, shape.width(), height).on_hover_text("Up");
key_shape(ui, colour, shape.width(), height).on_hover_text("Down");
});
ui.with_layout(egui::Layout::top_down(Align::LEFT), |ui| {
blank(ui, space.width(), height);
key_shape(ui, colour, shape.width(), height).on_hover_text("Right");
});
});
}

View File

@@ -1,13 +1,17 @@
mod anime_page;
mod aura_page;
mod fan_curve_page;
mod anime_power;
mod aura_modes;
mod aura_power;
mod fan_graph;
mod keyboard_layout;
mod rog_bios;
mod side_panel;
mod system_page;
mod top_bar;
pub use anime_page::*;
pub use aura_page::*;
pub use fan_curve_page::*;
pub use anime_power::*;
pub use aura_modes::*;
pub use aura_power::*;
pub use fan_graph::*;
pub use keyboard_layout::*;
pub use rog_bios::*;
pub use side_panel::*;
pub use system_page::*;
pub use top_bar::*;

View File

@@ -0,0 +1,110 @@
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
use egui::Ui;
use rog_profiles::Profile;
use rog_supported::SupportedFunctions;
pub fn platform_profile(states: &mut PageDataStates, dbus: &RogDbusClientBlocking, ui: &mut Ui) {
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();
};
}
pub fn rog_bios_group(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
ui.heading("Bios options");
let slider = egui::Slider::new(&mut states.charge_limit, 20..=100)
.text("Charging 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();
}
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();
}
}
}

View File

@@ -18,7 +18,7 @@ impl<'a> RogApp<'a> {
*page = Page::System;
}
if self.supported.platform_profile.fan_curves || cfg!(feature = "mocking") {
if self.supported.platform_profile.fan_curves {
ui.separator();
if ui
.selectable_value(page, Page::FanCurves, "Fan Curves")
@@ -28,9 +28,7 @@ impl<'a> RogApp<'a> {
}
}
if !self.supported.keyboard_led.stock_led_modes.is_empty()
|| cfg!(feature = "mocking")
{
if !self.supported.keyboard_led.stock_led_modes.is_empty() {
ui.separator();
if ui
.selectable_value(page, Page::AuraEffects, "Keyboard Aura")
@@ -40,15 +38,16 @@ impl<'a> RogApp<'a> {
}
}
if self.supported.anime_ctrl.0 || cfg!(feature = "mocking") {
ui.separator();
if ui
.selectable_value(page, Page::AnimeMatrix, "AniMe Matrix")
.clicked()
{
*page = Page::AnimeMatrix;
}
}
// TODO: Anime page is not complete
// if self.supported.anime_ctrl.0 {
// 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| {

View File

@@ -1,135 +0,0 @@
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();
};
}
}

View File

@@ -1,5 +1,7 @@
use std::sync::atomic::Ordering;
use egui::{vec2, Align2, Button, FontId, Id, Rect, RichText, Sense, Vec2};
use crate::RogApp;
impl<'a> RogApp<'a> {
@@ -36,6 +38,41 @@ impl<'a> RogApp<'a> {
.ok();
}
});
ui.horizontal(|ui| {
egui::global_dark_light_mode_switch(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.center_top() + vec2(0.0, height / 2.0),
Align2::CENTER_CENTER,
"ROG Control Center",
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.quit();
}
});
});
}

View File

@@ -34,6 +34,31 @@ pub struct CurveData {
pub temp: [u8; 8],
}
impl From<&CurveData> for String {
fn from(c: &CurveData) -> Self {
format!(
"{:?}: {}c:{}%,{}c:{}%,{}c:{}%,{}c:{}%,{}c:{}%,{}c:{}%,{}c:{}%,{}c:{}%",
c.fan,
c.temp[0],
(c.pwm[0] as u32) * 100 / 255,
c.temp[1],
(c.pwm[1] as u32) * 100 / 255,
c.temp[2],
(c.pwm[2] as u32) * 100 / 255,
c.temp[3],
(c.pwm[3] as u32) * 100 / 255,
c.temp[4],
(c.pwm[4] as u32) * 100 / 255,
c.temp[5],
(c.pwm[5] as u32) * 100 / 255,
c.temp[6],
(c.pwm[6] as u32) * 100 / 255,
c.temp[7],
(c.pwm[7] as u32) * 100 / 255,
)
}
}
impl std::str::FromStr for CurveData {
type Err = ProfileError;
@@ -189,6 +214,17 @@ impl Default for FanCurveSet {
}
}
impl From<&FanCurveSet> for String {
fn from(s: &FanCurveSet) -> Self {
format!(
"Enabled: {}, {}, {}",
s.enabled,
String::from(&s.cpu),
String::from(&s.gpu),
)
}
}
impl FanCurveSet {
pub(crate) fn read_cpu_from_device(&mut self, device: &Device) {
self.cpu.read_from_device(device);
@@ -216,13 +252,20 @@ mod tests {
use super::*;
#[test]
fn curve_data_from_str() {
fn curve_data_from_str_to_str() {
let curve =
CurveData::from_str("30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%")
.unwrap();
assert_eq!(curve.fan, FanCurvePU::CPU);
assert_eq!(curve.temp, [30, 49, 59, 69, 79, 89, 99, 109]);
assert_eq!(curve.pwm, [3, 5, 8, 10, 79, 125, 143, 148]);
let string: String = (&curve).into();
// End result is slightly different due to type conversions and rounding errors
assert_eq!(
string.as_str(),
"CPU: 30c:1%,49c:1%,59c:3%,69c:3%,79c:30%,89c:49%,99c:56%,109c:58%"
)
}
#[test]
@@ -257,4 +300,11 @@ mod tests {
assert_eq!(temp_str('1', 4), "pwm1_auto_point5_temp");
assert_eq!(temp_str('1', 7), "pwm1_auto_point8_temp");
}
#[test]
fn set_to_string() {
let set = FanCurveSet::default();
let string = String::from(&set);
assert_eq!(string.as_str(), "Enabled: false, CPU: 0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%, GPU: 0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%,0c:0%");
}
}

View File

@@ -163,13 +163,13 @@ impl FanCurveProfiles {
enumerator.match_subsystem("hwmon")?;
for device in enumerator.scan_devices()? {
if device.parent_with_subsystem("platform")?.is_some() {
if let Some(name) = device.attribute_value("name") {
if name == "asus_custom_fan_curve" {
return Ok(device);
}
// if device.parent_with_subsystem("platform")?.is_some() {
if let Some(name) = device.attribute_value("name") {
if name == "asus_custom_fan_curve" {
return Ok(device);
}
}
// }
}
Err(ProfileError::NotSupported)
}

View File

@@ -5,7 +5,7 @@ use serde_derive::{Deserialize, Serialize};
use std::fmt;
use zvariant_derive::Type;
#[derive(Serialize, Deserialize, Type, Debug)]
#[derive(Serialize, Deserialize, Type, Debug, Default)]
pub struct SupportedFunctions {
pub anime_ctrl: AnimeSupportedFunctions,
pub charge_ctrl: ChargeSupportedFunctions,
@@ -14,21 +14,21 @@ pub struct SupportedFunctions {
pub rog_bios_ctrl: RogBiosSupportedFunctions,
}
#[derive(Serialize, Deserialize, Type, Debug)]
#[derive(Serialize, Deserialize, Type, Debug, Default)]
pub struct AnimeSupportedFunctions(pub bool);
#[derive(Serialize, Deserialize, Type, Debug)]
#[derive(Serialize, Deserialize, Type, Debug, Default)]
pub struct ChargeSupportedFunctions {
pub charge_level_set: bool,
}
#[derive(Serialize, Deserialize, Type, Debug)]
#[derive(Serialize, Deserialize, Type, Debug, Default)]
pub struct PlatformProfileFunctions {
pub platform_profile: bool,
pub fan_curves: bool,
}
#[derive(Serialize, Deserialize, Type, Debug)]
#[derive(Serialize, Deserialize, Type, Debug, Default)]
pub struct LedSupportedFunctions {
pub prod_id: AuraDevice,
pub brightness_set: bool,
@@ -37,7 +37,7 @@ pub struct LedSupportedFunctions {
pub per_key_led_mode: bool,
}
#[derive(Serialize, Deserialize, Type, Debug)]
#[derive(Serialize, Deserialize, Type, Debug, Default)]
pub struct RogBiosSupportedFunctions {
pub post_sound: bool,
pub dedicated_gfx: bool,