mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 773c9902a5 | |||
| e05d5bd143 | |||
| 3e244d7d3d | |||
| ba4589f986 | |||
| 083134fc73 | |||
| 3cc04fba60 | |||
| 3a00e4f1a3 | |||
| eb78fb613c | |||
| d0b9aee85a | |||
| 3e94ef05fb | |||
| fbb025875b | |||
| ae816bd13c | |||
| 14f0693511 | |||
| de7fb4a942 | |||
| 4164b4645d | |||
| 649b14fd0d | |||
| 6d97ef13a1 | |||
| 7abad979c8 | |||
| 0ec1574219 | |||
| 03042dd5c3 | |||
| 3330e4973f | |||
| 5e06aeabe9 | |||
| e99d8766fc | |||
| 8f65b7e334 | |||
| 5a54b830bf | |||
| 85e08510f7 | |||
| d56eeb7fb2 | |||
| bbc520a7f2 | |||
| 10e43c64ca | |||
| 38be25174a | |||
| 2584d69930 | |||
| 523f39cf9c | |||
| 669760223e | |||
| 71ec13fa9f | |||
| 409528b286 | |||
| 17df3cf01d | |||
| 808a1d2470 | |||
| 840c500b5e | |||
| 42dc360d16 | |||
| f6183597c9 | |||
| 79a45c4f10 | |||
| 19370215c0 | |||
| 030dd661b8 | |||
| 1fc12d9855 | |||
| 6c1b2b70ea | |||
| 23f9af35bf | |||
| 526626b80c | |||
| 10eaaac54b | |||
| 901a3ddcc9 | |||
| e6ebf72a11 | |||
| a313359ef6 |
@@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [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
|
||||
- Clear command for anime `asusctl anime --clear` will clear the display
|
||||
|
||||
Generated
+1508
-80
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"]
|
||||
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-supported", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
|
||||
|
||||
[profile.release]
|
||||
# thin = 57s, asusd = 9.0M
|
||||
|
||||
@@ -90,7 +90,7 @@ where the number is a percentage.
|
||||
|
||||
Some options that you find in Armory Crate are available under this controller, so far there is:
|
||||
|
||||
- POST sound: this is the sound you here on bios boot post
|
||||
- POST sound: this is the sound you hear on bios boot post
|
||||
- G-Sync: this controls if the dGPU (Nvidia) is the *only* GPU, making it the main GPU and disabling the iGPU
|
||||
|
||||
These options are not written to the config file as they are stored in efivars. The only way to change these is to use the exposed safe dbus methods, or use the `asusctl` CLI tool.
|
||||
@@ -293,7 +293,7 @@ A plain non-float integer.
|
||||
|
||||
## asusctl
|
||||
|
||||
`asusctl` is a commandline interface which intends to be the main method of interacting with `asusd`. I can be used in any place a terminal app can be used.
|
||||
`asusctl` is a commandline interface which intends to be the main method of interacting with `asusd`. It can be used in any place a terminal app can be used.
|
||||
|
||||
This program will query `asusd` for the `Support` level of the laptop and show or hide options according to this support level.
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ datarootdir = $(prefix)/share
|
||||
libdir = $(exec_prefix)/lib
|
||||
zshcpl = $(datarootdir)/zsh/site-functions
|
||||
|
||||
BIN_ROG := rog-control-center
|
||||
BIN_C := asusctl
|
||||
BIN_D := asusd
|
||||
BIN_U := asusd-user
|
||||
@@ -39,6 +40,11 @@ distclean:
|
||||
rm -rf .cargo vendor vendor.tar.xz
|
||||
|
||||
install:
|
||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)"
|
||||
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).desktop" "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
|
||||
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
|
||||
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)"
|
||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
|
||||
@@ -63,11 +69,13 @@ 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)"
|
||||
rm -r "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
|
||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
|
||||
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_N)"
|
||||
@@ -85,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
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
|
||||
but can also be used with non-asus laptops with reduced features.
|
||||
|
||||
Now includes a GUI, `rog-control-center`.
|
||||
|
||||
## Kernel support
|
||||
|
||||
**The minimum supported kernel version is 5.15**
|
||||
|
||||
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
|
||||
|
||||
@@ -42,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
|
||||
|
||||
+2
-2
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
+74
-8
@@ -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, AuraPowerDev};
|
||||
|
||||
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
|
||||
use rog_aura::{self, AuraEffect};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_profiles::error::ProfileError;
|
||||
@@ -159,12 +160,17 @@ fn do_parsed(
|
||||
if let Some(cmdlist) = CliStart::command_list() {
|
||||
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
|
||||
for command in commands.iter().filter(|command| {
|
||||
if supported.keyboard_led.prod_id != "1866"
|
||||
&& command.trim().starts_with("led-pow-1")
|
||||
if !matches!(
|
||||
supported.keyboard_led.prod_id,
|
||||
AuraDevice::X1854
|
||||
| AuraDevice::X1869
|
||||
| AuraDevice::X1866
|
||||
| AuraDevice::Tuf
|
||||
) && command.trim().starts_with("led-pow-1")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if supported.keyboard_led.prod_id != "19b6"
|
||||
if supported.keyboard_led.prod_id != AuraDevice::X19B6
|
||||
&& command.trim().starts_with("led-pow-2")
|
||||
{
|
||||
return false;
|
||||
@@ -455,11 +461,27 @@ fn handle_led_power1(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if supported.prod_id != "1866" {
|
||||
println!("These options are for keyboards of product ID 0x1866 only");
|
||||
if matches!(
|
||||
supported.prod_id,
|
||||
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
|
||||
) {
|
||||
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();
|
||||
|
||||
@@ -482,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)?;
|
||||
|
||||
@@ -523,8 +587,8 @@ fn handle_led_power2(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if supported.prod_id == "1866" {
|
||||
println!("This option does not apply to keyboards with product ID 0x1866")
|
||||
if supported.prod_id != AuraDevice::X19B6 {
|
||||
println!("This option applies only to keyboards with product ID 0x19b6")
|
||||
}
|
||||
|
||||
let mut enabled: Vec<AuraDev19b6> = Vec::new();
|
||||
@@ -564,6 +628,7 @@ fn handle_led_power2(
|
||||
|
||||
if !enabled.is_empty() {
|
||||
let data = AuraPowerDev {
|
||||
tuf: vec![],
|
||||
x1866: vec![],
|
||||
x19b6: enabled,
|
||||
};
|
||||
@@ -572,6 +637,7 @@ fn handle_led_power2(
|
||||
|
||||
if !disabled.is_empty() {
|
||||
let data = AuraPowerDev {
|
||||
tuf: vec![],
|
||||
x1866: vec![],
|
||||
x19b6: disabled,
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "daemon"
|
||||
version = "4.3.0"
|
||||
version = "4.3.3"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
@@ -10,7 +12,7 @@ use async_trait::async_trait;
|
||||
use log::{error, info, warn};
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use rog_aura::{
|
||||
usb::{LED_APPLY, LED_SET},
|
||||
usb::{AuraDevice, LED_APPLY, LED_SET},
|
||||
AuraEffect, LedBrightness, LED_MSG_LEN,
|
||||
};
|
||||
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
|
||||
@@ -40,14 +42,18 @@ impl GetSupported for CtrlKbdLed {
|
||||
let multizone_led_mode = laptop.multizone;
|
||||
let per_key_led_mode = laptop.per_key;
|
||||
|
||||
let mut prod_id = String::new();
|
||||
let mut prod_id = AuraDevice::Unknown;
|
||||
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
||||
if let Ok(_) = Self::find_led_node(prod) {
|
||||
prod_id = prod.to_string();
|
||||
prod_id = AuraDevice::from(*prod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if Self::get_tuf_mode_path().is_some() {
|
||||
prod_id = AuraDevice::Tuf;
|
||||
}
|
||||
|
||||
LedSupportedFunctions {
|
||||
prod_id,
|
||||
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
|
||||
@@ -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
|
||||
@@ -417,10 +467,10 @@ impl CtrlKbdLed {
|
||||
let next = self.supported_modes.standard[idx];
|
||||
|
||||
self.config.read();
|
||||
if self.config.builtins.contains_key(&next) {
|
||||
self.config.current_mode = next;
|
||||
self.write_current_config_mode()?;
|
||||
}
|
||||
// if self.config.builtins.contains_key(&next) {
|
||||
self.config.current_mode = next;
|
||||
self.write_current_config_mode()?;
|
||||
// }
|
||||
self.config.write();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::warn;
|
||||
use rog_aura::{usb::AuraPowerDev, AuraEffect, LedBrightness};
|
||||
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness};
|
||||
use zbus::{dbus_interface, Connection, SignalContext};
|
||||
|
||||
use super::controller::CtrlKbdLedZbus;
|
||||
@@ -27,8 +29,21 @@ impl CtrlKbdLedZbus {
|
||||
}
|
||||
|
||||
/// Set a variety of states, input is array of enum.
|
||||
/// `enabled` sets if the sent array should be disabled or enabled
|
||||
///
|
||||
/// enum AuraControl {
|
||||
/// ```text
|
||||
/// pub struct AuraPowerDev {
|
||||
/// pub x1866: Vec<AuraDev1866>,
|
||||
/// pub x19b6: Vec<AuraDev19b6>,
|
||||
/// }
|
||||
/// pub enum AuraDev1866 {
|
||||
/// Awake,
|
||||
/// Keyboard,
|
||||
/// Lightbar,
|
||||
/// Boot,
|
||||
/// Sleep,
|
||||
/// }
|
||||
/// enum AuraDev19b6 {
|
||||
/// BootLogo,
|
||||
/// BootKeyb,
|
||||
/// AwakeLogo,
|
||||
@@ -42,6 +57,7 @@ impl CtrlKbdLedZbus {
|
||||
/// SleepBar,
|
||||
/// ShutdownBar,
|
||||
/// }
|
||||
/// ```
|
||||
async fn set_leds_power(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
@@ -50,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);
|
||||
}
|
||||
@@ -166,7 +185,7 @@ impl CtrlKbdLedZbus {
|
||||
|
||||
// As property doesn't work for AuraPowerDev (complexity of serialization?)
|
||||
// #[dbus_interface(property)]
|
||||
async fn leds_power(&self) -> AuraPowerDev {
|
||||
async fn leds_enabled(&self) -> AuraPowerDev {
|
||||
loop {
|
||||
if let Ok(ctrl) = self.0.try_lock() {
|
||||
return AuraPowerDev::from(&ctrl.config.enabled);
|
||||
@@ -175,29 +194,20 @@ impl CtrlKbdLedZbus {
|
||||
}
|
||||
|
||||
/// Return the current mode data
|
||||
#[dbus_interface(property)]
|
||||
async fn led_mode(&self) -> String {
|
||||
async fn led_mode(&self) -> AuraModeNum {
|
||||
if let Ok(ctrl) = self.0.try_lock() {
|
||||
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||
if let Ok(json) = serde_json::to_string(&mode) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
return ctrl.config.current_mode;
|
||||
}
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
"SetKeyBacklight could not deserialise".to_string()
|
||||
AuraModeNum::Static
|
||||
}
|
||||
|
||||
/// Return a list of available modes
|
||||
#[dbus_interface(property)]
|
||||
async fn led_modes(&self) -> String {
|
||||
if let Ok(ctrl) = self.0.try_lock() {
|
||||
if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) {
|
||||
return json;
|
||||
async fn led_modes(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
|
||||
loop {
|
||||
if let Ok(ctrl) = self.0.try_lock() {
|
||||
return ctrl.config.builtins.clone();
|
||||
}
|
||||
}
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
"SetKeyBacklight could not serialise".to_string()
|
||||
}
|
||||
|
||||
/// Return the current LED brightness
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"]
|
||||
@@ -50,14 +64,14 @@ per_key = false
|
||||
|
||||
[[led_data]]
|
||||
prod_family = "ROG Strix"
|
||||
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM"]
|
||||
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM", "G713IC"]
|
||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
||||
multizone = []
|
||||
per_key = true
|
||||
|
||||
[[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 = []
|
||||
|
||||
+19
-2
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
+4
-4
@@ -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 }
|
||||
@@ -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',
|
||||
]
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
@@ -63,6 +63,34 @@ impl FromStr for Colour {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[f32; 3]> for Colour {
|
||||
fn from(c: &[f32; 3]) -> Self {
|
||||
Self(
|
||||
(255.0 * c[0]) as u8,
|
||||
(255.0 * c[1]) as u8,
|
||||
(255.0 * c[2]) as u8,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Colour> for [f32; 3] {
|
||||
fn from(c: Colour) -> Self {
|
||||
[c.0 as f32 / 255.0, c.1 as f32 / 255.0, c.2 as f32 / 255.0]
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -122,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,
|
||||
@@ -330,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:
|
||||
|
||||
+16
-2
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
],
|
||||
),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
],
|
||||
),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
],
|
||||
),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -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);
|
||||
|
||||
+109
-173
@@ -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,
|
||||
],
|
||||
])
|
||||
}
|
||||
|
||||
+47
-1
@@ -20,14 +20,60 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize, Default)]
|
||||
pub enum AuraDevice {
|
||||
Tuf,
|
||||
X1854,
|
||||
X1869,
|
||||
X1866,
|
||||
#[default]
|
||||
X19B6,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
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,
|
||||
"19b6" => AuraDevice::X19B6,
|
||||
"0x1866" => AuraDevice::X1866,
|
||||
"0x1869" => AuraDevice::X1869,
|
||||
"0x1854" => AuraDevice::X1854,
|
||||
"0x19b6" => AuraDevice::X19B6,
|
||||
_ => AuraDevice::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct is intended as a helper to pass args to generic dbus interface
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[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
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "rog-control-center"
|
||||
version = "1.0.0"
|
||||
authors = ["Luke D. Jones <luke@ljones.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
mocking = []
|
||||
|
||||
[dependencies]
|
||||
egui = { git = "https://github.com/emilk/egui" }
|
||||
eframe= { git = "https://github.com/emilk/egui" }
|
||||
#eframe= { git = "https://github.com/emilk/egui", default-features = false, features = ["dark-light", "default_fonts", "wgpu"] }
|
||||
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
rog_supported = { path = "../rog-supported" }
|
||||
# supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git" }
|
||||
|
||||
smol = "^1.2"
|
||||
|
||||
serde = "^1.0"
|
||||
toml = "^0.5"
|
||||
serde_json = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
zbus = "^2.3"
|
||||
nix = "^0.20.0"
|
||||
tempfile = "3.2.0"
|
||||
dirs = "3.0.1"
|
||||
|
||||
[dependencies.notify-rust]
|
||||
version = "^4.3"
|
||||
default-features = false
|
||||
features = ["z"]
|
||||
@@ -0,0 +1,22 @@
|
||||
# App template
|
||||
|
||||
This is a trial app cut down to bare essentials to show how to create an app that can run in the background
|
||||
with some user-config options.
|
||||
|
||||
egui based. Keep in mind that this is very much a bit of a mess due to experimenting.
|
||||
|
||||
## Running
|
||||
|
||||
Use `WINIT_UNIX_BACKEND=x11 rog-control-center`. `WINIT_UNIX_BACKEND` is required due to window decorations not updating and the window not really being set as visible/invisible on wayland.
|
||||
|
||||
## Build features
|
||||
|
||||
For testing some features that are typically not available on all laptops:
|
||||
|
||||
```rust
|
||||
cargo run --features mocking
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- Add notification watch for certain UI elements to enforce an update (for example when a user changes Aura via a hot key).
|
||||
@@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
|
||||
Name=ROG Control Center
|
||||
Comment=Make your ASUS ROG Laptop go Brrrrr!
|
||||
Categories=Settings
|
||||
|
||||
Icon=rog-control-center
|
||||
Exec=env WINIT_UNIX_BACKEND=x11 rog-control-center
|
||||
Terminal=false
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,196 @@
|
||||
use std::{
|
||||
f64::consts::PI,
|
||||
io::Write,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU8, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use egui::{Button, RichText};
|
||||
use rog_supported::SupportedFunctions;
|
||||
|
||||
use crate::{
|
||||
config::Config, error::Result, get_ipc_file, page_states::PageDataStates, Page,
|
||||
RogDbusClientBlocking, SHOWING_GUI,
|
||||
};
|
||||
|
||||
pub struct RogApp<'a> {
|
||||
pub page: Page,
|
||||
pub states: PageDataStates,
|
||||
pub supported: SupportedFunctions,
|
||||
/// Should the app begin showing the GUI
|
||||
pub begin_show_gui: Arc<AtomicBool>,
|
||||
/// Is the app GUI closed (and running in bg)
|
||||
pub running_in_bg: bool,
|
||||
// TODO: can probably just open and read whenever
|
||||
pub config: Config,
|
||||
pub asus_dbus: RogDbusClientBlocking<'a>,
|
||||
/// 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> {
|
||||
/// Called once before the first frame.
|
||||
pub fn new(
|
||||
start_closed: bool,
|
||||
config: Config,
|
||||
show_gui: Arc<AtomicBool>,
|
||||
states: PageDataStates,
|
||||
_cc: &eframe::CreationContext<'_>,
|
||||
) -> Result<Self> {
|
||||
let (dbus, _) = RogDbusClientBlocking::new()?;
|
||||
let supported = dbus.proxies().supported().supported_functions()?;
|
||||
|
||||
// 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,
|
||||
begin_show_gui: show_gui,
|
||||
running_in_bg: start_closed,
|
||||
config,
|
||||
asus_dbus: dbus,
|
||||
oscillator1,
|
||||
oscillator2,
|
||||
oscillator3,
|
||||
oscillator_toggle,
|
||||
oscillator_freq,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> eframe::App for RogApp<'a> {
|
||||
fn on_exit_event(&mut self) -> bool {
|
||||
if self.config.run_in_background {
|
||||
self.running_in_bg = true;
|
||||
get_ipc_file().unwrap().write_all(&[0]).unwrap();
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Called each time the UI needs repainting, which may be many times per second.
|
||||
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
let Self {
|
||||
begin_show_gui: should_show_gui,
|
||||
supported,
|
||||
asus_dbus: dbus,
|
||||
states,
|
||||
..
|
||||
} = self;
|
||||
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;
|
||||
|
||||
if should_show_gui.load(Ordering::SeqCst) {
|
||||
let mut ipc_file = get_ipc_file().unwrap();
|
||||
ipc_file.write_all(&[SHOWING_GUI]).unwrap();
|
||||
should_show_gui.store(false, Ordering::SeqCst);
|
||||
frame.set_visible(true);
|
||||
self.running_in_bg = false;
|
||||
}
|
||||
if self.running_in_bg {
|
||||
// Request to draw nothing at all
|
||||
ctx.request_repaint_after(Duration::from_millis(500));
|
||||
frame.set_visible(false);
|
||||
return;
|
||||
}
|
||||
// Do all GUI display after this point
|
||||
|
||||
self.top_bar(ctx, frame);
|
||||
self.side_panel(ctx);
|
||||
|
||||
if let Some(err) = self.states.error.clone() {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading(RichText::new("Error!").size(28.0));
|
||||
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.label(RichText::new(format!("The error was: {:?}", err)).size(22.0));
|
||||
});
|
||||
});
|
||||
egui::TopBottomPanel::bottom("error_bar")
|
||||
.default_height(26.0)
|
||||
.show(ctx, |ui| {
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
|
||||
if ui
|
||||
.add(Button::new(RichText::new("Okay").size(20.0)))
|
||||
.clicked()
|
||||
{
|
||||
self.states.error = None;
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if page == Page::System {
|
||||
self.system_page(ctx);
|
||||
} else if page == Page::AuraEffects {
|
||||
self.aura_page(ctx);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
use std::{
|
||||
fs::{create_dir, OpenOptions},
|
||||
io::{Read, Write},
|
||||
};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
//use log::{error, info, warn};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
const CFG_DIR: &str = "rog";
|
||||
const CFG_FILE_NAME: &str = "rog-control-center.cfg";
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Config {
|
||||
pub run_in_background: bool,
|
||||
pub startup_in_background: bool,
|
||||
pub enable_notifications: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
run_in_background: true,
|
||||
startup_in_background: false,
|
||||
enable_notifications: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load() -> Result<Config, Error> {
|
||||
let mut path = if let Some(dir) = dirs::config_dir() {
|
||||
dir
|
||||
} else {
|
||||
return Err(Error::XdgVars);
|
||||
};
|
||||
|
||||
path.push(CFG_DIR);
|
||||
if !path.exists() {
|
||||
create_dir(path.clone())?;
|
||||
}
|
||||
|
||||
path.push(CFG_FILE_NAME);
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)?;
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||
if read_len == 0 {
|
||||
let default = Config::default();
|
||||
let t = toml::to_string_pretty(&default).unwrap();
|
||||
file.write_all(t.as_bytes())?;
|
||||
return Ok(default);
|
||||
} else if let Ok(data) = toml::from_str::<Config>(&buf) {
|
||||
return Ok(data);
|
||||
}
|
||||
}
|
||||
Err(Error::ConfigLoadFail)
|
||||
}
|
||||
|
||||
pub fn save(&self) -> Result<(), Error> {
|
||||
let mut path = if let Some(dir) = dirs::config_dir() {
|
||||
dir
|
||||
} else {
|
||||
return Err(Error::XdgVars);
|
||||
};
|
||||
|
||||
path.push(CFG_DIR);
|
||||
if !path.exists() {
|
||||
create_dir(path.clone())?;
|
||||
}
|
||||
|
||||
path.push(CFG_FILE_NAME);
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&path)?;
|
||||
|
||||
let t = toml::to_string_pretty(&self).unwrap();
|
||||
file.write_all(t.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use std::fmt;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Io(std::io::Error),
|
||||
Nix(nix::Error),
|
||||
ConfigLoadFail,
|
||||
ConfigLockFail,
|
||||
XdgVars,
|
||||
Zbus(zbus::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::Io(err) => write!(f, "Failed to open: {}", err),
|
||||
Error::Nix(err) => write!(f, "Error: {}", err),
|
||||
Error::ConfigLoadFail => write!(f, "Failed to load user config"),
|
||||
Error::ConfigLockFail => write!(f, "Failed to lock user config"),
|
||||
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
|
||||
Error::Zbus(err) => write!(f, "Error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
pub mod app;
|
||||
use std::{
|
||||
fs::{remove_dir_all, File, OpenOptions},
|
||||
io::{Read, Write},
|
||||
process::exit,
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub use app::RogApp;
|
||||
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
#[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};
|
||||
|
||||
pub const SHOWING_GUI: u8 = 1;
|
||||
pub const SHOW_GUI: u8 = 2;
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum Page {
|
||||
System,
|
||||
AuraEffects,
|
||||
AnimeMatrix,
|
||||
FanCurves,
|
||||
}
|
||||
|
||||
/// Either exit the process, or return with a refreshed tmp-dir
|
||||
pub fn on_tmp_dir_exists() -> Result<TempDir, std::io::Error> {
|
||||
let mut buf = [0u8; 4];
|
||||
let path = std::env::temp_dir().join("rog-gui");
|
||||
|
||||
let mut ipc_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(false)
|
||||
.open(path.join("ipc.pipe"))?;
|
||||
|
||||
// If the app is running this ends up stacked on top of SHOWING_GUI
|
||||
ipc_file.write_all(&[SHOW_GUI])?;
|
||||
// tiny sleep to give the app a chance to respond
|
||||
sleep(Duration::from_millis(10));
|
||||
ipc_file.read(&mut buf).ok();
|
||||
|
||||
// First entry is the actual state
|
||||
if buf[0] == SHOWING_GUI {
|
||||
ipc_file.write_all(&[SHOWING_GUI])?; // Store state again as we drained the fifo
|
||||
exit(0);
|
||||
} else if buf[0] == SHOW_GUI {
|
||||
remove_dir_all(&path)?;
|
||||
return tempfile::Builder::new()
|
||||
.prefix("rog-gui")
|
||||
.rand_bytes(0)
|
||||
.tempdir();
|
||||
}
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
pub fn get_ipc_file() -> Result<File, crate::error::Error> {
|
||||
let tmp_dir = std::env::temp_dir().join("rog-gui");
|
||||
let fifo_path = tmp_dir.join("ipc.pipe");
|
||||
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
|
||||
if !matches!(e, nix::Error::Sys(nix::errno::Errno::EEXIST)) {
|
||||
return Err(e)?;
|
||||
}
|
||||
}
|
||||
Ok(OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&fifo_path)?)
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_control_center::{
|
||||
config::Config, get_ipc_file, notify::start_notifications, on_tmp_dir_exists,
|
||||
page_states::PageDataStates, startup_error::AppErrorShow, RogApp, RogDbusClientBlocking,
|
||||
SHOW_GUI,
|
||||
};
|
||||
|
||||
use std::{
|
||||
fs::{self, OpenOptions},
|
||||
io::Read,
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread::spawn,
|
||||
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;
|
||||
|
||||
if config.startup_in_background {
|
||||
config.run_in_background = true;
|
||||
config.save()?;
|
||||
}
|
||||
|
||||
// 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));
|
||||
let bios_notified = Arc::new(AtomicBool::new(false));
|
||||
let aura_notified = Arc::new(AtomicBool::new(false));
|
||||
let anime_notified = Arc::new(AtomicBool::new(false));
|
||||
let profiles_notified = Arc::new(AtomicBool::new(false));
|
||||
let fans_notified = Arc::new(AtomicBool::new(false));
|
||||
let notifs_enabled = Arc::new(AtomicBool::new(config.enable_notifications));
|
||||
|
||||
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(
|
||||
charge_notified,
|
||||
bios_notified,
|
||||
aura_notified,
|
||||
anime_notified,
|
||||
profiles_notified,
|
||||
fans_notified,
|
||||
notifs_enabled,
|
||||
)?;
|
||||
}
|
||||
|
||||
// tmp-dir must live to the end of program life
|
||||
let _tmp_dir = match tempfile::Builder::new()
|
||||
.prefix("rog-gui")
|
||||
.rand_bytes(0)
|
||||
.tempdir()
|
||||
{
|
||||
Ok(tmp) => tmp,
|
||||
Err(_) => on_tmp_dir_exists().unwrap(),
|
||||
};
|
||||
|
||||
let should_show_gui = Arc::new(AtomicBool::new(!start_closed));
|
||||
let should = should_show_gui.clone();
|
||||
spawn(move || {
|
||||
// Loop is blocked here until a single byte is read
|
||||
loop {
|
||||
let mut buf = [0u8; 4];
|
||||
if get_ipc_file().unwrap().read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
|
||||
should_show_gui.store(true, Ordering::SeqCst);
|
||||
// Give the starting app a change to read or we'll race it
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
eframe::run_native(
|
||||
"ROG Control Center",
|
||||
native_options,
|
||||
Box::new(move |cc| {
|
||||
Box::new(RogApp::new(start_closed, config, should, states, cc).unwrap())
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
//TODO: a lot of app state refresh depends on this so there needs
|
||||
// to be an extra AtomicBool for checking if notifications are enabled
|
||||
|
||||
use notify_rust::{Hint, Notification, NotificationHandle};
|
||||
use rog_aura::AuraEffect;
|
||||
use rog_dbus::{
|
||||
zbus_anime::AnimeProxy, zbus_charge::ChargeProxy, zbus_led::LedProxy,
|
||||
zbus_profile::ProfileProxy, zbus_rogbios::RogBiosProxy,
|
||||
};
|
||||
use rog_profiles::Profile;
|
||||
use smol::{future, Executor};
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
thread::spawn,
|
||||
};
|
||||
use zbus::export::futures_util::StreamExt;
|
||||
|
||||
const NOTIF_HEADER: &str = "ROG Control";
|
||||
|
||||
macro_rules! notify {
|
||||
($notifier:ident, $last_notif:ident, $data:expr) => {
|
||||
if let Some(notif) = $last_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
if let Ok(x) = $notifier($data) {
|
||||
$last_notif.replace(x);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! base_notification {
|
||||
($body:expr) => {
|
||||
Notification::new()
|
||||
.summary(NOTIF_HEADER)
|
||||
.body($body)
|
||||
.timeout(2000)
|
||||
.show()
|
||||
};
|
||||
}
|
||||
|
||||
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
|
||||
|
||||
pub fn start_notifications(
|
||||
charge_notified: Arc<AtomicBool>,
|
||||
bios_notified: Arc<AtomicBool>,
|
||||
aura_notified: Arc<AtomicBool>,
|
||||
anime_notified: Arc<AtomicBool>,
|
||||
profiles_notified: Arc<AtomicBool>,
|
||||
_fans_notified: Arc<AtomicBool>,
|
||||
notifs_enabled: Arc<AtomicBool>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
|
||||
|
||||
let executor = Executor::new();
|
||||
// BIOS notif
|
||||
let last_notif = last_notification.clone();
|
||||
let notifs_enabled1 = notifs_enabled.clone();
|
||||
let bios_notified1 = bios_notified.clone();
|
||||
// TODO: make a macro or generic function or something...
|
||||
executor
|
||||
.spawn(async move {
|
||||
let conn = zbus::Connection::system().await.unwrap();
|
||||
let proxy = RogBiosProxy::new(&conn).await.unwrap();
|
||||
if let Ok(p) = proxy.receive_notify_post_boot_sound().await {
|
||||
p.for_each(|e| {
|
||||
if let Ok(out) = e.args() {
|
||||
if notifs_enabled1.load(Ordering::SeqCst) {
|
||||
if let Ok(ref mut lock) = last_notif.try_lock() {
|
||||
notify!(do_post_sound_notif, lock, &out.sound());
|
||||
}
|
||||
}
|
||||
bios_notified1.store(true, Ordering::SeqCst);
|
||||
}
|
||||
future::ready(())
|
||||
})
|
||||
.await;
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
let conn = zbus::Connection::system().await.unwrap();
|
||||
let proxy = RogBiosProxy::new(&conn).await.unwrap();
|
||||
if let Ok(p) = proxy.receive_notify_panel_overdrive().await {
|
||||
p.for_each(|_| {
|
||||
bios_notified.store(true, Ordering::SeqCst);
|
||||
future::ready(())
|
||||
})
|
||||
.await;
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Charge notif
|
||||
let last_notif = last_notification.clone();
|
||||
let notifs_enabled1 = notifs_enabled.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
let conn = zbus::Connection::system().await.unwrap();
|
||||
let proxy = ChargeProxy::new(&conn).await.unwrap();
|
||||
if let Ok(p) = proxy.receive_notify_charge().await {
|
||||
p.for_each(|e| {
|
||||
if let Ok(out) = e.args() {
|
||||
if notifs_enabled1.load(Ordering::SeqCst) {
|
||||
if let Ok(ref mut lock) = last_notif.try_lock() {
|
||||
notify!(do_charge_notif, lock, &out.limit);
|
||||
}
|
||||
}
|
||||
charge_notified.store(true, Ordering::SeqCst);
|
||||
}
|
||||
future::ready(())
|
||||
})
|
||||
.await;
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Profile notif
|
||||
let last_notif = last_notification.clone();
|
||||
let notifs_enabled1 = notifs_enabled.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
let conn = zbus::Connection::system().await.unwrap();
|
||||
let proxy = ProfileProxy::new(&conn).await.unwrap();
|
||||
if let Ok(p) = proxy.receive_notify_profile().await {
|
||||
p.for_each(|e| {
|
||||
if let Ok(out) = e.args() {
|
||||
if notifs_enabled1.load(Ordering::SeqCst) {
|
||||
if let Ok(ref mut lock) = last_notif.try_lock() {
|
||||
notify!(do_thermal_notif, lock, &out.profile);
|
||||
}
|
||||
}
|
||||
profiles_notified.store(true, Ordering::SeqCst);
|
||||
}
|
||||
future::ready(())
|
||||
})
|
||||
.await;
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
// LED notif
|
||||
let last_notif = last_notification.clone();
|
||||
let aura_notif = aura_notified.clone();
|
||||
let notifs_enabled1 = notifs_enabled.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
let conn = zbus::Connection::system().await.unwrap();
|
||||
let proxy = LedProxy::new(&conn).await.unwrap();
|
||||
if let Ok(p) = proxy.receive_notify_led().await {
|
||||
p.for_each(|e| {
|
||||
if let Ok(out) = e.args() {
|
||||
if notifs_enabled1.load(Ordering::SeqCst) {
|
||||
if let Ok(ref mut lock) = last_notif.try_lock() {
|
||||
notify!(do_led_notif, lock, &out.data);
|
||||
}
|
||||
}
|
||||
aura_notif.store(true, Ordering::SeqCst);
|
||||
}
|
||||
future::ready(())
|
||||
})
|
||||
.await;
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
let aura_notif = aura_notified.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
let conn = zbus::Connection::system().await.unwrap();
|
||||
let proxy = LedProxy::new(&conn).await.unwrap();
|
||||
if let Ok(p) = proxy.receive_notify_led().await {
|
||||
p.for_each(|_| {
|
||||
aura_notif.store(true, Ordering::SeqCst);
|
||||
future::ready(())
|
||||
})
|
||||
.await;
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
let conn = zbus::Connection::system().await.unwrap();
|
||||
let proxy = LedProxy::new(&conn).await.unwrap();
|
||||
if let Ok(p) = proxy.receive_all_signals().await {
|
||||
p.for_each(|_| {
|
||||
aura_notified.store(true, Ordering::SeqCst);
|
||||
future::ready(())
|
||||
})
|
||||
.await;
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
let conn = zbus::Connection::system().await.unwrap();
|
||||
let proxy = AnimeProxy::new(&conn).await.unwrap();
|
||||
if let Ok(p) = proxy.receive_power_states().await {
|
||||
p.for_each(|_| {
|
||||
anime_notified.store(true, Ordering::SeqCst);
|
||||
future::ready(())
|
||||
})
|
||||
.await;
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
spawn(move || loop {
|
||||
smol::block_on(executor.tick());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
|
||||
let icon = match profile {
|
||||
Profile::Balanced => "asus_notif_yellow",
|
||||
Profile::Performance => "asus_notif_red",
|
||||
Profile::Quiet => "asus_notif_green",
|
||||
};
|
||||
let profile: &str = (*profile).into();
|
||||
let x = Notification::new()
|
||||
.summary("ASUS ROG")
|
||||
.body(&format!(
|
||||
"Thermal profile changed to {}",
|
||||
profile.to_uppercase(),
|
||||
))
|
||||
.hint(Hint::Resident(true))
|
||||
.timeout(2000)
|
||||
.hint(Hint::Category("device".into()))
|
||||
//.hint(Hint::Transient(true))
|
||||
.icon(icon)
|
||||
.show()?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||
base_notification!(&format!(
|
||||
"Keyboard LED mode changed to {}",
|
||||
ledmode.mode_name()
|
||||
))
|
||||
}
|
||||
|
||||
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||
base_notification!(&format!("Battery charge limit changed to {}", limit))
|
||||
}
|
||||
|
||||
fn do_post_sound_notif(on: &bool) -> Result<NotificationHandle, notify_rust::error::Error> {
|
||||
base_notification!(&format!("BIOS Post sound {}", on))
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use egui::Vec2;
|
||||
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.
|
||||
/// It's a bit general in that it won't provide *what* was
|
||||
/// updated, so the full state needs refresh
|
||||
pub was_notified: Arc<AtomicBool>,
|
||||
pub post_sound: bool,
|
||||
pub dedicated_gfx: bool,
|
||||
pub panel_overdrive: bool,
|
||||
pub dgpu_disable: bool,
|
||||
pub egpu_enable: bool,
|
||||
}
|
||||
|
||||
impl BiosState {
|
||||
pub fn new(
|
||||
was_notified: Arc<AtomicBool>,
|
||||
supported: &SupportedFunctions,
|
||||
dbus: &RogDbusClientBlocking,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
was_notified,
|
||||
post_sound: if supported.rog_bios_ctrl.post_sound {
|
||||
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()? != 0
|
||||
} else {
|
||||
false
|
||||
},
|
||||
panel_overdrive: if supported.rog_bios_ctrl.panel_overdrive {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ProfilesState {
|
||||
pub was_notified: Arc<AtomicBool>,
|
||||
pub list: Vec<Profile>,
|
||||
pub current: Profile,
|
||||
}
|
||||
|
||||
impl ProfilesState {
|
||||
pub fn new(
|
||||
was_notified: Arc<AtomicBool>,
|
||||
supported: &SupportedFunctions,
|
||||
dbus: &RogDbusClientBlocking,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
was_notified,
|
||||
list: if supported.platform_profile.platform_profile {
|
||||
dbus.proxies().profile().profiles()?
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
current: if supported.platform_profile.platform_profile {
|
||||
dbus.proxies().profile().active_profile()?
|
||||
} else {
|
||||
Profile::Balanced
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FanCurvesState {
|
||||
pub was_notified: Arc<AtomicBool>,
|
||||
pub show_curve: Profile,
|
||||
pub show_graph: FanCurvePU,
|
||||
pub enabled: HashSet<Profile>,
|
||||
pub curves: HashMap<Profile, FanCurveSet>,
|
||||
pub drag_delta: Vec2,
|
||||
}
|
||||
|
||||
impl FanCurvesState {
|
||||
pub fn new(
|
||||
was_notified: Arc<AtomicBool>,
|
||||
supported: &SupportedFunctions,
|
||||
dbus: &RogDbusClientBlocking,
|
||||
) -> Result<Self> {
|
||||
let profiles = if supported.platform_profile.platform_profile {
|
||||
dbus.proxies().profile().profiles()?
|
||||
} else {
|
||||
vec![Profile::Balanced, Profile::Quiet, Profile::Performance]
|
||||
};
|
||||
let enabled = if supported.platform_profile.fan_curves {
|
||||
HashSet::from_iter(
|
||||
dbus.proxies()
|
||||
.profile()
|
||||
.enabled_fan_profiles()?
|
||||
.iter()
|
||||
.cloned(),
|
||||
)
|
||||
} else {
|
||||
HashSet::from([Profile::Balanced, Profile::Quiet, Profile::Performance])
|
||||
};
|
||||
|
||||
let mut curves: HashMap<Profile, FanCurveSet> = HashMap::new();
|
||||
profiles.iter().for_each(|p| {
|
||||
if supported.platform_profile.fan_curves {
|
||||
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];
|
||||
curve.cpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
|
||||
curve.gpu.pwm = [40, 80, 100, 140, 170, 200, 230, 250];
|
||||
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
|
||||
curves.insert(*p, curve);
|
||||
}
|
||||
});
|
||||
|
||||
let show_curve = if supported.platform_profile.fan_curves {
|
||||
dbus.proxies().profile().active_profile()?
|
||||
} else {
|
||||
Profile::Balanced
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
was_notified,
|
||||
show_curve,
|
||||
show_graph: FanCurvePU::CPU,
|
||||
enabled,
|
||||
curves,
|
||||
drag_delta: Vec2::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AuraState {
|
||||
pub was_notified: Arc<AtomicBool>,
|
||||
pub current_mode: AuraModeNum,
|
||||
pub modes: BTreeMap<AuraModeNum, AuraEffect>,
|
||||
pub enabled: AuraPowerDev,
|
||||
/// Brightness from 0-3
|
||||
pub bright: i16,
|
||||
pub wave_red: [u8; 22],
|
||||
pub wave_green: [u8; 22],
|
||||
pub wave_blue: [u8; 22],
|
||||
}
|
||||
|
||||
impl AuraState {
|
||||
pub fn new(
|
||||
was_notified: Arc<AtomicBool>,
|
||||
supported: &SupportedFunctions,
|
||||
dbus: &RogDbusClientBlocking,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
was_notified,
|
||||
current_mode: if !supported.keyboard_led.stock_led_modes.is_empty() {
|
||||
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_or_default()
|
||||
} else {
|
||||
BTreeMap::new()
|
||||
},
|
||||
enabled: dbus.proxies().led().leds_enabled().unwrap_or_default(),
|
||||
bright: if !supported.keyboard_led.brightness_set {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnimeState {
|
||||
pub was_notified: Arc<AtomicBool>,
|
||||
pub bright: u8,
|
||||
pub boot: bool,
|
||||
pub awake: bool,
|
||||
pub sleep: bool,
|
||||
}
|
||||
|
||||
impl AnimeState {
|
||||
pub fn new(
|
||||
was_notified: Arc<AtomicBool>,
|
||||
supported: &SupportedFunctions,
|
||||
dbus: &RogDbusClientBlocking,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
was_notified,
|
||||
boot: if supported.anime_ctrl.0 {
|
||||
dbus.proxies().anime().boot_enabled()?
|
||||
} else {
|
||||
false
|
||||
},
|
||||
awake: if supported.anime_ctrl.0 {
|
||||
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`
|
||||
/// we can re-use that structure.
|
||||
pub bios: BiosState,
|
||||
pub aura: AuraState,
|
||||
pub anime: AnimeState,
|
||||
pub profiles: ProfilesState,
|
||||
pub fan_curves: FanCurvesState,
|
||||
pub charge_limit: i16,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl PageDataStates {
|
||||
pub fn new(
|
||||
keyboard_layout: KeyLayout,
|
||||
notifs_enabled: Arc<AtomicBool>,
|
||||
charge_notified: Arc<AtomicBool>,
|
||||
bios_notified: Arc<AtomicBool>,
|
||||
aura_notified: Arc<AtomicBool>,
|
||||
anime_notified: Arc<AtomicBool>,
|
||||
profiles_notified: Arc<AtomicBool>,
|
||||
fans_notified: Arc<AtomicBool>,
|
||||
supported: &SupportedFunctions,
|
||||
dbus: &RogDbusClientBlocking,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
keyboard_layout,
|
||||
notifs_enabled,
|
||||
was_notified: charge_notified,
|
||||
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,
|
||||
) -> Result<bool> {
|
||||
let mut notified = false;
|
||||
if self.was_notified.load(Ordering::SeqCst) {
|
||||
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.was_notified.store(false, Ordering::SeqCst);
|
||||
notified = true;
|
||||
}
|
||||
|
||||
if self.bios.was_notified.load(Ordering::SeqCst) {
|
||||
self.bios = BiosState::new(self.bios.was_notified.clone(), supported, dbus)?;
|
||||
self.bios.was_notified.store(false, Ordering::SeqCst);
|
||||
notified = true;
|
||||
}
|
||||
|
||||
if self.profiles.was_notified.load(Ordering::SeqCst) {
|
||||
self.profiles =
|
||||
ProfilesState::new(self.profiles.was_notified.clone(), supported, dbus)?;
|
||||
self.profiles.was_notified.store(false, Ordering::SeqCst);
|
||||
notified = true;
|
||||
}
|
||||
|
||||
if self.fan_curves.was_notified.load(Ordering::SeqCst) {
|
||||
self.fan_curves =
|
||||
FanCurvesState::new(self.fan_curves.was_notified.clone(), supported, dbus)?;
|
||||
self.fan_curves.was_notified.store(false, Ordering::SeqCst);
|
||||
notified = true;
|
||||
}
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
mod anime_power;
|
||||
mod aura_modes;
|
||||
mod aura_power;
|
||||
mod fan_graph;
|
||||
mod keyboard_layout;
|
||||
mod rog_bios;
|
||||
mod side_panel;
|
||||
mod top_bar;
|
||||
|
||||
pub use anime_power::*;
|
||||
pub use aura_modes::*;
|
||||
pub use aura_power::*;
|
||||
pub use fan_graph::*;
|
||||
pub use keyboard_layout::*;
|
||||
pub use rog_bios::*;
|
||||
pub use side_panel::*;
|
||||
pub use top_bar::*;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use crate::{Page, RogApp};
|
||||
|
||||
impl<'a> RogApp<'a> {
|
||||
pub fn side_panel(&mut self, ctx: &egui::Context) {
|
||||
egui::SidePanel::left("side_panel")
|
||||
.resizable(false)
|
||||
.default_width(60.0) // TODO: set size to match icon buttons when done
|
||||
.show(ctx, |ui| {
|
||||
let Self { page, .. } = self;
|
||||
|
||||
ui.heading("Functions");
|
||||
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_value(page, Page::System, "System Settings")
|
||||
.clicked()
|
||||
{
|
||||
*page = Page::System;
|
||||
}
|
||||
|
||||
if self.supported.platform_profile.fan_curves {
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_value(page, Page::FanCurves, "Fan Curves")
|
||||
.clicked()
|
||||
{
|
||||
*page = Page::FanCurves;
|
||||
}
|
||||
}
|
||||
|
||||
if !self.supported.keyboard_led.stock_led_modes.is_empty() {
|
||||
ui.separator();
|
||||
if ui
|
||||
.selectable_value(page, Page::AuraEffects, "Keyboard Aura")
|
||||
.clicked()
|
||||
{
|
||||
*page = Page::AuraEffects;
|
||||
}
|
||||
}
|
||||
|
||||
// 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| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.label("Source code ");
|
||||
ui.hyperlink_to("rog-gui.", "https://gitlab.com/asus-linux/rog-gui");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use egui::{vec2, Align2, Button, FontId, Id, Rect, RichText, Sense, Vec2};
|
||||
|
||||
use crate::RogApp;
|
||||
|
||||
impl<'a> RogApp<'a> {
|
||||
pub fn top_bar(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
let Self { states, config, .. } = self;
|
||||
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
// The top panel is often a good place for a menu bar:
|
||||
egui::menu::bar(ui, |ui| {
|
||||
ui.menu_button("File", |ui| {
|
||||
if ui.button("Quit").clicked() {
|
||||
frame.quit();
|
||||
}
|
||||
});
|
||||
ui.menu_button("Settings", |ui| {
|
||||
if ui
|
||||
.checkbox(&mut config.run_in_background, "Run in Background")
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(&mut config.startup_in_background, "Startup Hidden")
|
||||
.clicked()
|
||||
|| ui
|
||||
.checkbox(&mut config.enable_notifications, "Enable Notifications")
|
||||
.clicked()
|
||||
{
|
||||
states
|
||||
.notifs_enabled
|
||||
.store(config.enable_notifications, Ordering::SeqCst);
|
||||
config
|
||||
.save()
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rog_dbus"
|
||||
version = "4.2.0"
|
||||
version = "4.2.1"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
//!
|
||||
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use zbus::{blocking::Connection, Result};
|
||||
use zbus_macros::dbus_proxy;
|
||||
|
||||
use rog_aura::{usb::AuraPowerDev, AuraEffect, KeyColourArray, LedBrightness};
|
||||
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, KeyColourArray, LedBrightness};
|
||||
|
||||
const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 FPS
|
||||
|
||||
@@ -63,12 +65,10 @@ trait Led {
|
||||
fn led_brightness(&self) -> zbus::Result<i16>;
|
||||
|
||||
/// LedMode property
|
||||
#[dbus_proxy(property)]
|
||||
fn led_mode(&self) -> zbus::Result<String>;
|
||||
fn led_mode(&self) -> zbus::Result<AuraModeNum>;
|
||||
|
||||
/// LedModes property
|
||||
#[dbus_proxy(property)]
|
||||
fn led_modes(&self) -> zbus::Result<String>;
|
||||
fn led_modes(&self) -> zbus::Result<BTreeMap<AuraModeNum, AuraEffect>>;
|
||||
|
||||
// As property doesn't work for AuraPowerDev (complexity of serialization?)
|
||||
// #[dbus_proxy(property)]
|
||||
|
||||
@@ -29,9 +29,34 @@ pub(crate) fn temp_str(fan: char, index: usize) -> String {
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
|
||||
pub struct CurveData {
|
||||
pub(crate) fan: FanCurvePU,
|
||||
pub(crate) pwm: [u8; 8],
|
||||
pub(crate) temp: [u8; 8],
|
||||
pub fan: FanCurvePU,
|
||||
pub pwm: [u8; 8],
|
||||
pub temp: [u8; 8],
|
||||
}
|
||||
|
||||
impl 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 {
|
||||
@@ -166,9 +191,9 @@ impl CurveData {
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct FanCurveSet {
|
||||
pub(crate) enabled: bool,
|
||||
pub(crate) cpu: CurveData,
|
||||
pub(crate) gpu: CurveData,
|
||||
pub enabled: bool,
|
||||
pub cpu: CurveData,
|
||||
pub gpu: CurveData,
|
||||
}
|
||||
|
||||
impl Default for FanCurveSet {
|
||||
@@ -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%");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn find_fan_curve_node() -> Result<Option<Device>, ProfileError> {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Copy)]
|
||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum Profile {
|
||||
Balanced,
|
||||
Performance,
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
use rog_aura::{AuraModeNum, AuraZone};
|
||||
use rog_aura::{usb::AuraDevice, AuraModeNum, AuraZone};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use zvariant_derive::Type;
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug)]
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default)]
|
||||
pub struct SupportedFunctions {
|
||||
pub anime_ctrl: AnimeSupportedFunctions,
|
||||
pub charge_ctrl: ChargeSupportedFunctions,
|
||||
@@ -14,30 +14,30 @@ 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: String,
|
||||
pub prod_id: AuraDevice,
|
||||
pub brightness_set: bool,
|
||||
pub stock_led_modes: Vec<AuraModeNum>,
|
||||
pub multizone_led_mode: Vec<AuraZone>,
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user