mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d76cb3b95a | |||
| 910f529a9b | |||
| 7583d070d3 | |||
| 1f85e30e42 | |||
| d1bdf4dc7e | |||
| 79b108ceb7 | |||
| 7d14e8d900 | |||
| 493d61cf19 | |||
| fb08d83999 | |||
| 71241b7127 | |||
| 09963534d8 | |||
| 64322044ac | |||
| 1a132d847f | |||
| 952a974e83 | |||
| ebbfa58a76 | |||
| 414d610bd2 | |||
| bff98ddf7b | |||
| 97481cd45e | |||
| 4f39c01139 | |||
| 40987ecd5d | |||
| f378c54815 | |||
| a8a99ac1d1 | |||
| 503aa20257 | |||
| 8c67836650 | |||
| 3fc839820e | |||
| 0ef524a94b | |||
| f8cfacda47 | |||
| f3876100ae | |||
| fa1feaf9d9 | |||
| 45641c928d | |||
| eba9dc8a52 | |||
| a32527d1df | |||
| 1f697b5ff1 | |||
| 92009ef96c | |||
| 3fe5896596 | |||
| f8cdde2adf | |||
| 033f2141ef | |||
| f86bab6f8c | |||
| 4951bce961 | |||
| fb92d65fa0 | |||
| 24fa075a44 | |||
| a0f7cf3acd | |||
| d35707f2e4 | |||
| b20bde8116 | |||
| 308fba9413 | |||
| 45268bfb2b | |||
| 004982cea7 |
@@ -5,3 +5,4 @@ cargo-config
|
||||
vendor-*
|
||||
vendor_*
|
||||
.vscode-ctags
|
||||
.vscode
|
||||
@@ -6,6 +6,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased ]
|
||||
|
||||
## [v4.4.0] - 2022-08-29
|
||||
### Added
|
||||
- Support for per-key config has been added to `asusd-user`. At the moment it is
|
||||
basic with only a few effects done. Please see the manual for more information.
|
||||
- Support for unzoned and per-zone effects on some laptops. As above.
|
||||
- Added three effects to use with Zoned or Per-Key:
|
||||
+ Static, Breathe, Flicker. More to come.
|
||||
- Support for G713RS LED modes
|
||||
- Support for TUF laptop RGB (kernel patches required, these are submitted upstream)
|
||||
### Changed
|
||||
- Create new rog-platform crate to manage all i/o in a universal way
|
||||
+ kbd-led handling (requires kernel patches, TUF specific)
|
||||
+ platform handling (asus-nb-wmi)
|
||||
+ power (basic, can be extended in future)
|
||||
+ hidraw
|
||||
+ usbraw
|
||||
- Refactor how ROGCC handles IPC for background open, run-in-bg
|
||||
- Refactor daemon task creation to be simpler (for development)
|
||||
- Rename dpu_only to gpu_mux. Update all related messages and info.
|
||||
### Breaking
|
||||
- DBUS: rename path `/org/asuslinux/RogBios` to `/org/asuslinux/Platform`
|
||||
- DBUS: renamed `dedicated_graphic_mode` to `gpu_mux_mode` (`GpuMuxMode`)
|
||||
- DBUS: renamed `set_dedicated_graphic_mode` to `set_gpu_mux_mode` (`SetGpuMuxMode`)
|
||||
+ The methods above take an enum: 0 = Discrete, 1 = Optimus
|
||||
|
||||
## [4.3.4] - 2022-08-03
|
||||
### Bugfix
|
||||
- ROGCC: Remove power setting from correct array
|
||||
|
||||
Generated
+571
-429
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", "rog-control-center"]
|
||||
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
|
||||
|
||||
[profile.release]
|
||||
# thin = 57s, asusd = 9.0M
|
||||
|
||||
@@ -91,7 +91,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 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
|
||||
- GPU MUX: this controls if the dGPU 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.
|
||||
|
||||
@@ -131,6 +131,101 @@ As of now only AniMe is active in this with configuration in `~/.config/rog/`. O
|
||||
|
||||
The main config is `~/.config/rog/rog-user.cfg`
|
||||
|
||||
#### Config options: Aura, per-key and zoned
|
||||
|
||||
I'm unsure of how many laptops this works on, so please try it.
|
||||
|
||||
`led_type: Key` works only on actual per-key RGB keyboards.
|
||||
|
||||
`led_type: Zone` works on zoned laptops.
|
||||
|
||||
`led_type: Zone` set to `None` works on zoned ROG laptops, unzoned ROG laptops, and TUF laptops (and yes this does mean an audio EQ can be done now).
|
||||
|
||||
`~/.config/rog/rog-user.cfg` contains a setting `"active_aura": "<FILENAME>"` where `<FILENAME>` is the name of the Aura config to use, located in the same directory and without the file postfix, e.g, `"active_anime": "aura-default"`
|
||||
|
||||
An Aura config itself is a file with contents:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "aura-default",
|
||||
"aura": [
|
||||
{
|
||||
"Breathe": {
|
||||
"led_type": {
|
||||
"Key": "W"
|
||||
},
|
||||
"start_colour1": [
|
||||
255,
|
||||
0,
|
||||
20
|
||||
],
|
||||
"start_colour2": [
|
||||
20,
|
||||
255,
|
||||
0
|
||||
],
|
||||
"speed": "Low"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Static": {
|
||||
"led_type": {
|
||||
"Key": "Esc"
|
||||
},
|
||||
"colour": [
|
||||
0,
|
||||
0,
|
||||
255
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Flicker": {
|
||||
"led_type": {
|
||||
"Key": "N9"
|
||||
},
|
||||
"start_colour": [
|
||||
0,
|
||||
0,
|
||||
255
|
||||
],
|
||||
"max_percentage": 80,
|
||||
"min_percentage": 40
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If your laptop supports multizone, `"led_type"` can also be `"Zone": <one of the following>`
|
||||
- `"None"`
|
||||
- `"KeyboardLeft"`
|
||||
- `"KeyboardCenterLeft"`
|
||||
- `"KeyboardCenterRight"`
|
||||
- `"KeyboardRight"`
|
||||
- `"LightbarRight"`
|
||||
- `"LightbarRightCorner"`
|
||||
- `"LightbarRightBottom"`
|
||||
- `"LightbarLeftBottom"`
|
||||
- `"LightbarLeftCorner"`
|
||||
- `"LightbarLeft"`
|
||||
|
||||
At the moment there are only three effects available as shown in the example. More will come in the future
|
||||
but this may take me some time.
|
||||
|
||||
**Aura layouts**: `asusd-user` does its best to find a suitable layout to use based on `/sys/class/dmi/id/board_name`.
|
||||
It looks at each of the files in `/usr/share/rog-gui/layouts/` and matches against the toml block looking like:
|
||||
```toml
|
||||
matches = [
|
||||
'GX502',
|
||||
'GU502',
|
||||
]
|
||||
```
|
||||
|
||||
My laptop is a `GX502GW`, so `GX502` is a match. Note that these layouts are the physical representation of
|
||||
the keyboard and are used in the GUI also. The config that tells if per-key is supported is located in
|
||||
`/etc/asusd/asusd-ledmodes.toml`
|
||||
|
||||
#### Config options: AniMe
|
||||
|
||||
`~/.config/rog/rog-user.cfg` contains a setting `"active_anime": "<FILENAME>"` where `<FILENAME>` is the name of the AniMe config to use, located in the same directory and without the file postfix, e.g, `"active_anime": "anime-doom"`
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
[](https://www.paypal.com/donate/?hosted_button_id=4V2DEPS7K6APC) - [Asus Linux Website](https://asus-linux.org/)
|
||||
|
||||
**WARNING:** Do not run the main branch of this repo unless you have all the asus-wmi kernel patches. You should use stable kernels + tagged releases.
|
||||
|
||||
`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.
|
||||
|
||||
@@ -19,8 +21,8 @@ Now includes a GUI, `rog-control-center`.
|
||||
4. Respect the users resources: be small, light, and fast
|
||||
|
||||
Point 3 means that the list of supported distros is very narrow - fedora is explicitly
|
||||
supported, while Ubuntu and openSUSE are level-2 support. All other distros are *not*
|
||||
supported (while asusd might still run fine on them). For best support use fedora 32+ Workstation.
|
||||
supported. All other distros are *not* supported (while asusd might still run fine on them).
|
||||
For best support use fedora 36+ Workstation.
|
||||
|
||||
Point 4? asusd currently uses a tiny fraction of cpu time, and less than 1Mb of ram, the way
|
||||
a system-level daemon should.
|
||||
@@ -39,22 +41,14 @@ Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
|
||||
```
|
||||
|
||||
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.
|
||||
and AniMe parts should work regardless of your latop make.
|
||||
|
||||
### 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.
|
||||
**TUF Laptops**: now supported provided the kernel is patched. These patches are submitted upstream and will be in version 6.1.x of the kernel (or thereabouts). See the blog on asus-linux.org for more info.
|
||||
|
||||
## Implemented
|
||||
|
||||
- [X] System daemon
|
||||
- [X] GUI app
|
||||
- [X] User notifications daemon
|
||||
- [X] Setting/modifying built-in LED modes
|
||||
- [X] Per-key LED setting
|
||||
@@ -62,9 +56,17 @@ will probably suffer another rename once it becomes generic enough to do so.
|
||||
- [X] Saving settings for reload
|
||||
- [X] AniMatrix display on G14 models that include it
|
||||
- [X] Set battery charge limit (with kernel supporting this)
|
||||
- [X] Fan curve control on G14 + G15. Requires kernel patch (should reach 5.15 kernel)
|
||||
- [X] Fan curve control on supported laptops (G14/G15, some TUF like FA507)
|
||||
- [X] Toggle bios setting for boot/POST sound
|
||||
- [X] Toggle bios setting for "dedicated gfx" mode on supported laptops (g-sync)
|
||||
- [X] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops)
|
||||
|
||||
# GUI
|
||||
|
||||
A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
# BUILDING
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ zbus = "^2.2"
|
||||
serde_json = "^1.0"
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_supported = { path = "../rog-supported" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
smol = "^1.2"
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use notify_rust::{Hint, Notification, NotificationHandle};
|
||||
use rog_aura::AuraEffect;
|
||||
use rog_dbus::{
|
||||
zbus_charge::ChargeProxy, zbus_led::LedProxy, zbus_profile::ProfileProxy,
|
||||
zbus_rogbios::RogBiosProxy,
|
||||
zbus_charge::ChargeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy,
|
||||
zbus_profile::ProfileProxy,
|
||||
};
|
||||
use rog_profiles::Profile;
|
||||
use smol::{future, Executor};
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ rog_anime = { path = "../rog-anime" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
rog_supported = { path = "../rog-supported" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
daemon = { path = "../daemon" }
|
||||
gumdrop = "^0.8"
|
||||
toml = "^0.5.8"
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
//! Very bad rushed example. The better way to do this would be to have
|
||||
//! the balles move on their own square grid, then translate that to the
|
||||
//! key layout via shape by pitch etc.
|
||||
use rog_aura::{
|
||||
layouts::{KeyLayout, KeyRow},
|
||||
KeyColourArray,
|
||||
};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use std::collections::LinkedList;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Ball {
|
||||
position: (f32, f32),
|
||||
direction: (f32, f32),
|
||||
trail: LinkedList<(f32, f32)>,
|
||||
}
|
||||
impl Ball {
|
||||
fn new(x: f32, y: f32, trail_len: u32) -> Self {
|
||||
let mut trail = LinkedList::new();
|
||||
for _ in 1..=trail_len {
|
||||
trail.push_back((x, y));
|
||||
}
|
||||
|
||||
Ball {
|
||||
position: (x, y),
|
||||
direction: (1.0, 1.0),
|
||||
trail,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
fn update(&mut self, key_map: &[KeyRow]) {
|
||||
self.position.0 += self.direction.0;
|
||||
self.position.1 += self.direction.1;
|
||||
|
||||
if self.position.1.abs() as usize >= key_map.len() {
|
||||
self.direction.1 *= -1.0;
|
||||
self.position.1 += self.direction.1;
|
||||
self.direction.0 *= -1.0;
|
||||
self.position.0 += self.direction.0;
|
||||
}
|
||||
if self.position.0.abs() as usize >= key_map[self.position.1.abs() as usize].row_ref().len()
|
||||
{
|
||||
self.direction.1 *= -1.0;
|
||||
self.position.1 += self.direction.1;
|
||||
}
|
||||
if self.position.0 as usize >= key_map[self.position.1.abs() as usize].row_ref().len() {
|
||||
self.direction.0 *= -1.0;
|
||||
self.position.0 += self.direction.0;
|
||||
}
|
||||
|
||||
let pos = self.position;
|
||||
|
||||
if pos.1 == key_map[pos.1.abs() as usize].row_ref().len() as f32 - 1.0 || pos.1 <= 0.0 {
|
||||
self.direction.0 *= -1.0;
|
||||
} else if key_map[(pos.1) as usize].row_ref()[(pos.0) as usize].is_placeholder() {
|
||||
self.direction.0 *= -1.0;
|
||||
}
|
||||
|
||||
if pos.0 == key_map.len() as f32 - 1.0 || pos.0 <= 0.0 {
|
||||
self.direction.1 *= -1.0;
|
||||
} else if key_map[(pos.1) as usize].row_ref()[(pos.0) as usize].is_placeholder() {
|
||||
self.direction.1 *= -1.0;
|
||||
}
|
||||
|
||||
self.trail.pop_front();
|
||||
self.trail.push_back(self.position);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (dbus, _) = RogDbusClientBlocking::new()?;
|
||||
|
||||
let mut colours = KeyColourArray::new();
|
||||
let layout = KeyLayout::gx502_layout();
|
||||
|
||||
let mut balls = [Ball::new(2.0, 1.0, 12), Ball::new(5.0, 2.0, 12)];
|
||||
// let mut balls = [Ball::new(2, 1, 12)];
|
||||
|
||||
loop {
|
||||
for (n, ball) in balls.iter_mut().enumerate() {
|
||||
ball.update(layout.rows_ref());
|
||||
for (i, pos) in ball.trail.iter().enumerate() {
|
||||
if let Some(c) = colours
|
||||
.rgb_for_key(layout.rows_ref()[pos.1.abs() as usize].row_ref()[pos.0 as usize])
|
||||
{
|
||||
c[0] = 0;
|
||||
c[1] = 0;
|
||||
c[2] = 0;
|
||||
if n == 0 {
|
||||
c[0] = i as u8 * (255 / ball.trail.len() as u8);
|
||||
} else if n == 1 {
|
||||
c[1] = i as u8 * (255 / ball.trail.len() as u8);
|
||||
} else if n == 2 {
|
||||
c[2] = i as u8 * (255 / ball.trail.len() as u8);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(c) = colours.rgb_for_key(
|
||||
layout.rows_ref()[ball.position.1.abs() as usize].row_ref()
|
||||
[ball.position.0 as usize],
|
||||
) {
|
||||
c[0] = 255;
|
||||
c[1] = 255;
|
||||
c[2] = 255;
|
||||
};
|
||||
}
|
||||
dbus.proxies().led().per_key_raw(colours.get())?;
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(150));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//! Using a combination of key-colour array plus a key layout to generate outputs.
|
||||
|
||||
use rog_aura::{keys::Key, layouts::KeyLayout, Breathe, Colour, Effect, LedType, Sequences, Speed};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let layout = KeyLayout::gx502_layout();
|
||||
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
|
||||
let mut seq = Sequences::new();
|
||||
let mut key = Effect::Breathe(Breathe::new(
|
||||
LedType::Key(Key::W),
|
||||
Colour(255, 127, 0),
|
||||
Colour(127, 0, 255),
|
||||
Speed::Med,
|
||||
));
|
||||
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::A));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::S));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::D));
|
||||
seq.push(key.clone());
|
||||
|
||||
let mut key = Effect::Breathe(Breathe::new(
|
||||
LedType::Key(Key::Q),
|
||||
Colour(127, 127, 127),
|
||||
Colour(127, 255, 255),
|
||||
Speed::Low,
|
||||
));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::E));
|
||||
seq.push(key.clone());
|
||||
|
||||
let mut key = Effect::Breathe(Breathe::new(
|
||||
LedType::Key(Key::N1),
|
||||
Colour(166, 127, 166),
|
||||
Colour(127, 155, 20),
|
||||
Speed::High,
|
||||
));
|
||||
key.set_led_type(LedType::Key(Key::Tilde));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::N2));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::N3));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::N4));
|
||||
seq.push(key.clone());
|
||||
|
||||
loop {
|
||||
seq.next_state(&layout);
|
||||
let packets = seq.create_packets();
|
||||
|
||||
client.proxies().led().per_key_raw(packets)?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(60));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//! Using a combination of key-colour array plus a key layout to generate outputs.
|
||||
|
||||
use rog_aura::{layouts::KeyLayout, KeyColourArray};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let layout = KeyLayout::gx502_layout();
|
||||
loop {
|
||||
let mut key_colours = KeyColourArray::new();
|
||||
for row in layout.rows() {
|
||||
for (k, key) in row.row().enumerate() {
|
||||
if k != 0 {
|
||||
if let Some(prev) = row.row().nth(k - 1) {
|
||||
if let Some(c) = key_colours.rgb_for_key(*prev) {
|
||||
c[0] = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if key.is_placeholder() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(c) = key_colours.rgb_for_key(*key) {
|
||||
c[0] = 255;
|
||||
};
|
||||
|
||||
client.proxies().led().per_key_raw(key_colours.get())?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//! Using a combination of key-colour array plus a key layout to generate outputs.
|
||||
|
||||
use rog_aura::{layouts::KeyLayout, Breathe, Colour, Effect, LedType, PerZone, Sequences, Speed};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let layout = KeyLayout::gx502_layout();
|
||||
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
|
||||
let mut seq = Sequences::new();
|
||||
|
||||
let zone = Effect::Breathe(Breathe::new(
|
||||
LedType::Zone(PerZone::KeyboardLeft),
|
||||
Colour(166, 127, 166),
|
||||
Colour(127, 155, 20),
|
||||
Speed::High,
|
||||
));
|
||||
seq.push(zone);
|
||||
|
||||
let zone = Effect::Breathe(Breathe::new(
|
||||
LedType::Zone(PerZone::KeyboardCenterLeft),
|
||||
Colour(16, 127, 255),
|
||||
Colour(127, 15, 20),
|
||||
Speed::Low,
|
||||
));
|
||||
seq.push(zone);
|
||||
|
||||
let zone = Effect::Breathe(Breathe::new(
|
||||
LedType::Zone(PerZone::LightbarRightCorner),
|
||||
Colour(0, 255, 255),
|
||||
Colour(255, 0, 255),
|
||||
Speed::Med,
|
||||
));
|
||||
seq.push(zone);
|
||||
|
||||
loop {
|
||||
seq.next_state(&layout);
|
||||
let packets = seq.create_packets();
|
||||
|
||||
client.proxies().led().per_key_raw(packets)?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(60));
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,8 @@ pub struct AuraEnabled {
|
||||
pub logo: Option<bool>,
|
||||
#[options(meta = "", help = "<true/false>")]
|
||||
pub lightbar: Option<bool>,
|
||||
#[options(meta = "", help = "<true/false>")]
|
||||
pub lid: Option<bool>,
|
||||
}
|
||||
|
||||
// impl FromStr for AuraEnabled {
|
||||
|
||||
@@ -80,11 +80,11 @@ pub struct BiosCommand {
|
||||
meta = "",
|
||||
short = "D",
|
||||
no_long,
|
||||
help = "activate dGPU dedicated/G-Sync: asusctl -d <true/false>, reboot required"
|
||||
help = "Switch GPU MUX mode: 0 = Discrete, 1 = Optimus, reboot required"
|
||||
)]
|
||||
pub dedicated_gfx_set: Option<bool>,
|
||||
pub gpu_mux_mode_set: Option<u8>,
|
||||
#[options(no_long, short = "d", help = "get GPU mode")]
|
||||
pub dedicated_gfx_get: bool,
|
||||
pub gpu_mux_mode_get: bool,
|
||||
#[options(
|
||||
meta = "",
|
||||
short = "O",
|
||||
|
||||
+18
-22
@@ -14,12 +14,9 @@ use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage,
|
||||
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
|
||||
use rog_aura::{self, AuraEffect};
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_platform::platform::GpuMode;
|
||||
use rog_platform::supported::*;
|
||||
use rog_profiles::error::ProfileError;
|
||||
use rog_supported::SupportedFunctions;
|
||||
use rog_supported::{
|
||||
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
|
||||
RogBiosSupportedFunctions,
|
||||
};
|
||||
|
||||
use crate::aura_cli::LedBrightness;
|
||||
use crate::cli_opts::*;
|
||||
@@ -102,7 +99,7 @@ fn print_versions() {
|
||||
println!(" rog-aura v{}", rog_aura::VERSION);
|
||||
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||
println!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||
println!("rog-supported v{}", rog_supported::VERSION);
|
||||
println!("rog-platform v{}", rog_platform::VERSION);
|
||||
}
|
||||
|
||||
fn print_laptop_info() {
|
||||
@@ -608,21 +605,25 @@ fn handle_led_power2(
|
||||
check(arg.keyboard, AuraDev19b6::BootKeyb);
|
||||
check(arg.logo, AuraDev19b6::BootLogo);
|
||||
check(arg.lightbar, AuraDev19b6::BootBar);
|
||||
check(arg.lid, AuraDev19b6::AwakeLid);
|
||||
}
|
||||
aura_cli::SetAuraEnabled::Sleep(arg) => {
|
||||
check(arg.keyboard, AuraDev19b6::SleepKeyb);
|
||||
check(arg.logo, AuraDev19b6::SleepLogo);
|
||||
check(arg.lightbar, AuraDev19b6::SleepBar);
|
||||
check(arg.lid, AuraDev19b6::SleepLid);
|
||||
}
|
||||
aura_cli::SetAuraEnabled::Awake(arg) => {
|
||||
check(arg.keyboard, AuraDev19b6::AwakeKeyb);
|
||||
check(arg.logo, AuraDev19b6::AwakeLogo);
|
||||
check(arg.lightbar, AuraDev19b6::AwakeBar);
|
||||
check(arg.lid, AuraDev19b6::AwakeLid);
|
||||
}
|
||||
aura_cli::SetAuraEnabled::Shutdown(arg) => {
|
||||
check(arg.keyboard, AuraDev19b6::ShutdownKeyb);
|
||||
check(arg.logo, AuraDev19b6::ShutdownLogo);
|
||||
check(arg.lightbar, AuraDev19b6::ShutdownBar);
|
||||
check(arg.lid, AuraDev19b6::ShutdownBar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,8 +758,8 @@ fn handle_bios_option(
|
||||
cmd: &BiosCommand,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
{
|
||||
if (cmd.dedicated_gfx_set.is_none()
|
||||
&& !cmd.dedicated_gfx_get
|
||||
if (cmd.gpu_mux_mode_set.is_none()
|
||||
&& !cmd.gpu_mux_mode_get
|
||||
&& cmd.post_sound_set.is_none()
|
||||
&& !cmd.post_sound_get
|
||||
&& cmd.panel_overdrive_set.is_none()
|
||||
@@ -774,7 +775,7 @@ fn handle_bios_option(
|
||||
|
||||
for line in usage.iter().filter(|line| {
|
||||
line.contains("sound") && supported.post_sound
|
||||
|| line.contains("GPU") && supported.dedicated_gfx
|
||||
|| line.contains("GPU") && supported.gpu_mux
|
||||
|| line.contains("panel") && supported.panel_overdrive
|
||||
}) {
|
||||
println!("{}", line);
|
||||
@@ -789,28 +790,23 @@ fn handle_bios_option(
|
||||
println!("Bios POST sound on: {}", res);
|
||||
}
|
||||
|
||||
if let Some(opt) = cmd.dedicated_gfx_set {
|
||||
if let Some(opt) = cmd.gpu_mux_mode_set {
|
||||
println!("Rebuilding initrd to include drivers");
|
||||
dbus.proxies().rog_bios().set_dedicated_graphic_mode(opt)?;
|
||||
dbus.proxies()
|
||||
.rog_bios()
|
||||
.set_gpu_mux_mode(GpuMode::from_mux(opt))?;
|
||||
println!("The mode change is not active until you reboot, on boot the bios will make the required change");
|
||||
if opt {
|
||||
println!(
|
||||
"NOTE: on reboot your display manager will be forced to use Nvidia drivers"
|
||||
);
|
||||
} else {
|
||||
println!("NOTE: after reboot you can then select regular graphics modes");
|
||||
}
|
||||
}
|
||||
if cmd.dedicated_gfx_get {
|
||||
let res = dbus.proxies().rog_bios().dedicated_graphic_mode()? == 1;
|
||||
println!("Bios dedicated GPU on: {}", res);
|
||||
if cmd.gpu_mux_mode_get {
|
||||
let res = dbus.proxies().rog_bios().gpu_mux_mode()?;
|
||||
println!("Bios GPU MUX: {:?}", res);
|
||||
}
|
||||
|
||||
if let Some(opt) = cmd.panel_overdrive_set {
|
||||
dbus.proxies().rog_bios().set_panel_overdrive(opt)?;
|
||||
}
|
||||
if cmd.panel_overdrive_get {
|
||||
let res = dbus.proxies().rog_bios().panel_overdrive()? == 1;
|
||||
let res = dbus.proxies().rog_bios().panel_overdrive()?;
|
||||
println!("Panel overdrive on: {}", res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "daemon-user"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||
edition = "2018"
|
||||
description = "Usermode daemon for user settings, anime, per-key lighting"
|
||||
@@ -20,8 +20,9 @@ serde_json = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
|
||||
rog_anime = { path = "../rog-anime" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_supported = { path = "../rog-supported" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
|
||||
dirs = "^4.0"
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use zbus::dbus_interface;
|
||||
use zvariant::ObjectPath;
|
||||
use zvariant_derive::Type;
|
||||
|
||||
use crate::user_config::ConfigLoadSave;
|
||||
use crate::{error::Error, user_config::UserAnimeConfig};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
|
||||
|
||||
+69
-28
@@ -1,4 +1,5 @@
|
||||
use rog_anime::usb::get_anime_type;
|
||||
use rog_aura::layouts::KeyLayout;
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_user::{
|
||||
ctrl_anime::{CtrlAnime, CtrlAnimeInner},
|
||||
@@ -6,64 +7,104 @@ use rog_user::{
|
||||
DBUS_NAME,
|
||||
};
|
||||
use smol::Executor;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::{fs::OpenOptions, io::Read, path::PathBuf, sync::Arc};
|
||||
use zbus::Connection;
|
||||
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
#[cfg(not(feature = "local_data"))]
|
||||
const DATA_DIR: &str = "/usr/share/rog-gui/";
|
||||
#[cfg(feature = "local_data")]
|
||||
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>> {
|
||||
println!(" user daemon v{}", rog_user::VERSION);
|
||||
println!(" rog-anime v{}", rog_anime::VERSION);
|
||||
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||
println!("rog-supported v{}", rog_supported::VERSION);
|
||||
println!("rog-platform v{}", rog_platform::VERSION);
|
||||
|
||||
let (client, _) = RogDbusClientBlocking::new()?;
|
||||
let supported = client.proxies().supported().supported_functions()?;
|
||||
|
||||
let mut config = UserConfig::new();
|
||||
config.load_config()?;
|
||||
config.load()?;
|
||||
|
||||
let executor = Executor::new();
|
||||
|
||||
let early_return = Arc::new(AtomicBool::new(false));
|
||||
// Set up the anime data and run loop/thread
|
||||
if supported.anime_ctrl.0 {
|
||||
let anime_type = get_anime_type()?;
|
||||
let anime_config = UserAnimeConfig::load_config(config.active_anime)?;
|
||||
let anime = anime_config.create_anime(anime_type)?;
|
||||
let anime_config = Arc::new(Mutex::new(anime_config));
|
||||
if let Some(cfg) = config.active_anime {
|
||||
let anime_type = get_anime_type()?;
|
||||
let anime_config = UserAnimeConfig::load(cfg)?;
|
||||
let anime = anime_config.create(anime_type)?;
|
||||
let anime_config = Arc::new(Mutex::new(anime_config));
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
// Create server
|
||||
let mut connection = Connection::session().await.unwrap();
|
||||
connection.request_name(DBUS_NAME).await.unwrap();
|
||||
|
||||
// Inner behind mutex required for thread safety
|
||||
let inner = Arc::new(Mutex::new(
|
||||
CtrlAnimeInner::new(anime, client, early_return.clone()).unwrap(),
|
||||
));
|
||||
// Need new client object for dbus control part
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let anime_control =
|
||||
CtrlAnime::new(anime_config, inner.clone(), client, early_return).unwrap();
|
||||
anime_control.add_to_server(&mut connection).await;
|
||||
loop {
|
||||
if let Ok(inner) = inner.clone().try_lock() {
|
||||
inner.run().ok();
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
// if supported.keyboard_led.per_key_led_mode {
|
||||
if let Some(cfg) = config.active_aura {
|
||||
let mut aura_config = UserAuraConfig::load(cfg)?;
|
||||
|
||||
// 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 layout = KeyLayout::find_layout(board_name.as_str(), PathBuf::from(DATA_DIR))
|
||||
.map_err(|e| {
|
||||
println!("{BOARD_NAME}, {e}");
|
||||
})
|
||||
.unwrap_or(KeyLayout::ga401_layout());
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
// Create server
|
||||
let mut connection = Connection::session().await.unwrap();
|
||||
connection.request_name(DBUS_NAME).await.unwrap();
|
||||
|
||||
// Inner behind mutex required for thread safety
|
||||
let inner = Arc::new(Mutex::new(
|
||||
CtrlAnimeInner::new(anime, client, early_return.clone()).unwrap(),
|
||||
));
|
||||
// Need new client object for dbus control part
|
||||
let (client, _) = RogDbusClientBlocking::new().unwrap();
|
||||
let anime_control =
|
||||
CtrlAnime::new(anime_config, inner.clone(), client, early_return).unwrap();
|
||||
anime_control.add_to_server(&mut connection).await;
|
||||
// let connection = Connection::session().await.unwrap();
|
||||
// connection.request_name(DBUS_NAME).await.unwrap();
|
||||
|
||||
loop {
|
||||
if let Ok(inner) = inner.clone().try_lock() {
|
||||
inner.run().ok();
|
||||
}
|
||||
aura_config.aura.next_state(&layout);
|
||||
let packets = aura_config.aura.create_packets();
|
||||
|
||||
client.proxies().led().per_key_raw(packets).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(33));
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
// if supported.keyboard_led.per_key_led_mode {
|
||||
// executor
|
||||
// .spawn(async move {
|
||||
// //
|
||||
// })
|
||||
// .detach();
|
||||
// }
|
||||
|
||||
loop {
|
||||
|
||||
+119
-26
@@ -5,28 +5,21 @@ use std::{
|
||||
};
|
||||
|
||||
use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences, Vec2};
|
||||
use rog_aura::{keys::Key, Breathe, Colour, Effect, Flicker, LedType, Speed, Static};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct UserAnimeConfig {
|
||||
pub name: String,
|
||||
pub anime: Vec<ActionLoader>,
|
||||
}
|
||||
pub trait ConfigLoadSave<T: DeserializeOwned + serde::Serialize> {
|
||||
fn name(&self) -> String;
|
||||
|
||||
impl UserAnimeConfig {
|
||||
pub fn create_anime(&self, anime_type: AnimeType) -> Result<Sequences, Error> {
|
||||
let mut seq = Sequences::new(anime_type);
|
||||
fn default_with_name(name: String) -> T;
|
||||
|
||||
for (idx, action) in self.anime.iter().enumerate() {
|
||||
seq.insert(idx, action)?;
|
||||
}
|
||||
|
||||
Ok(seq)
|
||||
}
|
||||
|
||||
pub fn write(&self) -> Result<(), Error> {
|
||||
fn write(&self) -> Result<(), Error>
|
||||
where
|
||||
Self: serde::Serialize,
|
||||
{
|
||||
let mut path = if let Some(dir) = dirs::config_dir() {
|
||||
dir
|
||||
} else {
|
||||
@@ -37,7 +30,7 @@ impl UserAnimeConfig {
|
||||
if !path.exists() {
|
||||
create_dir(path.clone())?;
|
||||
}
|
||||
let name = self.name.clone();
|
||||
let name = self.name().clone();
|
||||
path.push(name + ".cfg");
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
@@ -51,7 +44,7 @@ impl UserAnimeConfig {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_config(name: String) -> Result<UserAnimeConfig, Error> {
|
||||
fn load(name: String) -> Result<T, Error> {
|
||||
let mut path = if let Some(dir) = dirs::config_dir() {
|
||||
dir
|
||||
} else {
|
||||
@@ -75,14 +68,11 @@ impl UserAnimeConfig {
|
||||
|
||||
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||
if read_len == 0 {
|
||||
let default = UserAnimeConfig {
|
||||
name,
|
||||
..Default::default()
|
||||
};
|
||||
let default = Self::default_with_name(name);
|
||||
let json = serde_json::to_string_pretty(&default).unwrap();
|
||||
file.write_all(json.as_bytes())?;
|
||||
return Ok(default);
|
||||
} else if let Ok(data) = serde_json::from_str::<UserAnimeConfig>(&buf) {
|
||||
} else if let Ok(data) = serde_json::from_str::<T>(&buf) {
|
||||
return Ok(data);
|
||||
}
|
||||
}
|
||||
@@ -90,6 +80,37 @@ impl UserAnimeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct UserAnimeConfig {
|
||||
pub name: String,
|
||||
pub anime: Vec<ActionLoader>,
|
||||
}
|
||||
|
||||
impl UserAnimeConfig {
|
||||
pub fn create(&self, anime_type: AnimeType) -> Result<Sequences, Error> {
|
||||
let mut seq = Sequences::new(anime_type);
|
||||
|
||||
for (idx, action) in self.anime.iter().enumerate() {
|
||||
seq.insert(idx, action)?;
|
||||
}
|
||||
|
||||
Ok(seq)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigLoadSave<UserAnimeConfig> for UserAnimeConfig {
|
||||
fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn default_with_name(name: String) -> Self {
|
||||
UserAnimeConfig {
|
||||
name,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UserAnimeConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -151,20 +172,91 @@ impl Default for UserAnimeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct UserAuraConfig {
|
||||
pub name: String,
|
||||
pub aura: rog_aura::Sequences,
|
||||
}
|
||||
|
||||
impl ConfigLoadSave<UserAuraConfig> for UserAuraConfig {
|
||||
fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn default_with_name(name: String) -> Self {
|
||||
UserAuraConfig {
|
||||
name,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UserAuraConfig {
|
||||
fn default() -> Self {
|
||||
let mut seq = rog_aura::Sequences::new();
|
||||
let mut key = Effect::Breathe(Breathe::new(
|
||||
LedType::Key(Key::W),
|
||||
Colour(255, 0, 20),
|
||||
Colour(20, 255, 0),
|
||||
Speed::Low,
|
||||
));
|
||||
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::A));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::S));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::D));
|
||||
seq.push(key);
|
||||
|
||||
let key = Effect::Breathe(Breathe::new(
|
||||
LedType::Key(Key::F),
|
||||
Colour(255, 0, 0),
|
||||
Colour(255, 0, 0),
|
||||
Speed::High,
|
||||
));
|
||||
seq.push(key);
|
||||
|
||||
let mut key = Effect::Static(Static::new(LedType::Key(Key::RCtrl), Colour(0, 0, 255)));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::LCtrl));
|
||||
seq.push(key.clone());
|
||||
key.set_led_type(LedType::Key(Key::Esc));
|
||||
seq.push(key);
|
||||
|
||||
let key = Effect::Flicker(Flicker::new(
|
||||
LedType::Key(Key::N9),
|
||||
Colour(0, 0, 255),
|
||||
80,
|
||||
40,
|
||||
));
|
||||
seq.push(key.clone());
|
||||
|
||||
Self {
|
||||
name: "default".to_string(),
|
||||
aura: seq,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct UserConfig {
|
||||
/// Name of active anime config file in the user config directory
|
||||
pub active_anime: String,
|
||||
pub active_anime: Option<String>,
|
||||
/// Name of active aura config file in the user config directory
|
||||
pub active_aura: Option<String>,
|
||||
}
|
||||
|
||||
impl UserConfig {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
active_anime: "anime-default".to_string(),
|
||||
active_anime: Some("anime-default".to_string()),
|
||||
active_aura: Some("aura-default".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_config(&mut self) -> Result<(), Error> {
|
||||
pub fn load(&mut self) -> Result<(), Error> {
|
||||
let mut path = if let Some(dir) = dirs::config_dir() {
|
||||
dir
|
||||
} else {
|
||||
@@ -192,6 +284,7 @@ impl UserConfig {
|
||||
file.write_all(json.as_bytes())?;
|
||||
} else if let Ok(data) = serde_json::from_str::<UserConfig>(&buf) {
|
||||
self.active_anime = data.active_anime;
|
||||
self.active_aura = data.active_aura;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "daemon"
|
||||
version = "4.3.4"
|
||||
version = "4.4.0"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
@@ -20,16 +20,13 @@ path = "src/daemon.rs"
|
||||
[dependencies]
|
||||
rog_anime = { path = "../rog-anime", features = ["dbus"] }
|
||||
rog_aura = { path = "../rog-aura", features = ["dbus"] }
|
||||
rog_supported = { path = "../rog-supported" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
|
||||
async-trait = "^0.1"
|
||||
smol = "^1.2"
|
||||
|
||||
rusb = "^0.9"
|
||||
udev = "^0.6"
|
||||
|
||||
# cli and logging
|
||||
log = "^0.4"
|
||||
env_logger = "^0.9"
|
||||
|
||||
@@ -7,15 +7,18 @@ use std::path::PathBuf;
|
||||
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||
|
||||
#[derive(Deserialize, Serialize, Default)]
|
||||
#[serde(default)]
|
||||
pub struct Config {
|
||||
/// Save charge limit for restoring on boot
|
||||
pub bat_charge_limit: u8,
|
||||
pub panel_od: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn new() -> Self {
|
||||
Config {
|
||||
bat_charge_limit: 100,
|
||||
panel_od: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+55
-148
@@ -1,32 +1,25 @@
|
||||
pub mod config;
|
||||
pub mod zbus;
|
||||
|
||||
use ::zbus::Connection;
|
||||
use async_trait::async_trait;
|
||||
use log::{error, info, warn};
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use rog_anime::{
|
||||
error::AnimeError,
|
||||
usb::{
|
||||
find_node, get_anime_type, pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on,
|
||||
pkts_for_init, PROD_ID, VENDOR_ID,
|
||||
get_anime_type, pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on,
|
||||
pkts_for_init,
|
||||
},
|
||||
ActionData, AnimeDataBuffer, AnimePacketType, AnimeType,
|
||||
};
|
||||
use rog_supported::AnimeSupportedFunctions;
|
||||
use rusb::{Device, DeviceHandle};
|
||||
use smol::{stream::StreamExt, Executor};
|
||||
use rog_platform::{hid_raw::HidRaw, supported::AnimeSupportedFunctions, usb_raw::USBRaw};
|
||||
use smol::Executor;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
convert::TryFrom,
|
||||
error::Error,
|
||||
sync::{Arc, Mutex, MutexGuard},
|
||||
thread::sleep,
|
||||
};
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{error::RogError, GetSupported};
|
||||
|
||||
@@ -36,14 +29,13 @@ impl GetSupported for CtrlAnime {
|
||||
type A = AnimeSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
AnimeSupportedFunctions(CtrlAnime::get_device(VENDOR_ID, PROD_ID).is_ok())
|
||||
AnimeSupportedFunctions(HidRaw::new("193b").is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CtrlAnime {
|
||||
_node: String,
|
||||
node: USBRaw,
|
||||
anime_type: AnimeType,
|
||||
handle: RefCell<DeviceHandle<rusb::GlobalContext>>,
|
||||
cache: AnimeConfigCached,
|
||||
config: AnimeConfig,
|
||||
// set to force thread to exit
|
||||
@@ -55,57 +47,26 @@ pub struct CtrlAnime {
|
||||
impl CtrlAnime {
|
||||
#[inline]
|
||||
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
||||
let node = find_node("193b")?;
|
||||
let node = USBRaw::new(0x193b)?;
|
||||
let anime_type = get_anime_type()?;
|
||||
let device = Self::get_dev_handle()?;
|
||||
|
||||
info!("Device has an AniMe Matrix display");
|
||||
let mut cache = AnimeConfigCached::default();
|
||||
cache.init_from_config(&config, anime_type)?;
|
||||
|
||||
let ctrl = CtrlAnime {
|
||||
_node: node,
|
||||
node,
|
||||
anime_type,
|
||||
handle: RefCell::new(device),
|
||||
cache,
|
||||
config,
|
||||
thread_exit: Arc::new(AtomicBool::new(false)),
|
||||
thread_running: Arc::new(AtomicBool::new(false)),
|
||||
};
|
||||
ctrl.do_initialization();
|
||||
ctrl.do_initialization()?;
|
||||
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
fn get_dev_handle() -> Result<DeviceHandle<rusb::GlobalContext>, Box<dyn Error>> {
|
||||
// We don't expect this ID to ever change
|
||||
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
||||
|
||||
let mut device = device.open()?;
|
||||
device.reset()?;
|
||||
|
||||
device.set_auto_detach_kernel_driver(true).map_err(|err| {
|
||||
error!("Auto-detach kernel driver failed: {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
device.claim_interface(0).map_err(|err| {
|
||||
error!("Could not claim device interface: {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
||||
for device in rusb::devices()?.iter() {
|
||||
let device_desc = device.device_descriptor()?;
|
||||
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
|
||||
return Ok(device);
|
||||
}
|
||||
}
|
||||
Err(rusb::Error::NoDevice)
|
||||
}
|
||||
// let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
||||
|
||||
/// Start an action thread. This is classed as a singleton and there should be only
|
||||
/// one running - so the thread uses atomics to signal run/exit.
|
||||
@@ -230,45 +191,6 @@ impl CtrlAnime {
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn write_bytes(&self, message: &[u8]) {
|
||||
// if let Ok(mut file) = OpenOptions::new().write(true).open(&self.node) {
|
||||
// println!("write: {:02x?}", &message);
|
||||
// return file
|
||||
// .write_all(message).unwrap();
|
||||
// }
|
||||
let mut error = false;
|
||||
|
||||
match self.handle.borrow().write_control(
|
||||
0x21, // request_type
|
||||
0x09, // request
|
||||
0x35e, // value
|
||||
0x00, // index
|
||||
message,
|
||||
Duration::from_millis(200),
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(err) => match err {
|
||||
rusb::Error::Timeout => {}
|
||||
_ => {
|
||||
error = true;
|
||||
error!("Failed to write to led interrupt: {}", err);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if error {
|
||||
warn!("Will attempt to get AniMe device handle again");
|
||||
match Self::get_dev_handle() {
|
||||
Ok(dev) => {
|
||||
self.handle.replace(dev);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to get AniMe device: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
@@ -281,16 +203,17 @@ impl CtrlAnime {
|
||||
}
|
||||
let data = AnimePacketType::try_from(buffer)?;
|
||||
for row in data.iter() {
|
||||
self.write_bytes(row);
|
||||
self.node.write_bytes(row)?;
|
||||
}
|
||||
self.write_bytes(&pkt_for_flush());
|
||||
self.node.write_bytes(&pkt_for_flush())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_initialization(&self) {
|
||||
fn do_initialization(&self) -> Result<(), RogError> {
|
||||
let pkts = pkts_for_init();
|
||||
self.write_bytes(&pkts[0]);
|
||||
self.write_bytes(&pkts[1]);
|
||||
self.node.write_bytes(&pkts[0])?;
|
||||
self.node.write_bytes(&pkts[1])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,14 +230,6 @@ impl CtrlAnimeTask {
|
||||
#[async_trait]
|
||||
impl crate::CtrlTask for CtrlAnimeTask {
|
||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||
let connection = Connection::system()
|
||||
.await
|
||||
.expect("CtrlAnimeTask could not create dbus connection");
|
||||
|
||||
let manager = ManagerProxy::new(&connection)
|
||||
.await
|
||||
.expect("CtrlAnimeTask could not create ManagerProxy");
|
||||
|
||||
let run_action =
|
||||
|start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
|
||||
if start {
|
||||
@@ -326,50 +241,40 @@ impl crate::CtrlTask for CtrlAnimeTask {
|
||||
}
|
||||
};
|
||||
|
||||
let inner = self.inner.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
||||
notif
|
||||
.for_each(|event| {
|
||||
if let Ok(args) = event.args() {
|
||||
// Loop is required to try an attempt to get the mutex *without* blocking
|
||||
// other threads - it is possible to end up with deadlocks otherwise.
|
||||
loop {
|
||||
if let Ok(lock) = inner.clone().try_lock() {
|
||||
run_action(args.start, lock, inner.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
let inner1 = self.inner.clone();
|
||||
let inner2 = self.inner.clone();
|
||||
let inner3 = self.inner.clone();
|
||||
let inner4 = self.inner.clone();
|
||||
self.create_sys_event_tasks(
|
||||
executor,
|
||||
// Loop is required to try an attempt to get the mutex *without* blocking
|
||||
// other threads - it is possible to end up with deadlocks otherwise.
|
||||
move || loop {
|
||||
if let Ok(lock) = inner1.clone().try_lock() {
|
||||
run_action(true, lock, inner1.clone());
|
||||
break;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let manager = ManagerProxy::new(&connection)
|
||||
.await
|
||||
.expect("CtrlAnimeTask could not create ManagerProxy");
|
||||
|
||||
let inner = self.inner.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
||||
notif
|
||||
.for_each(|event| {
|
||||
if let Ok(args) = event.args() {
|
||||
loop {
|
||||
if let Ok(lock) = inner.clone().try_lock() {
|
||||
run_action(args.start, lock, inner.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
},
|
||||
move || loop {
|
||||
if let Ok(lock) = inner2.clone().try_lock() {
|
||||
run_action(false, lock, inner2.clone());
|
||||
break;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
},
|
||||
move || loop {
|
||||
if let Ok(lock) = inner3.clone().try_lock() {
|
||||
run_action(true, lock, inner3.clone());
|
||||
break;
|
||||
}
|
||||
},
|
||||
move || loop {
|
||||
if let Ok(lock) = inner4.clone().try_lock() {
|
||||
run_action(false, lock, inner4.clone());
|
||||
break;
|
||||
}
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -380,10 +285,12 @@ pub struct CtrlAnimeReloader(pub Arc<Mutex<CtrlAnime>>);
|
||||
impl crate::Reloadable for CtrlAnimeReloader {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Ok(lock) = self.0.try_lock() {
|
||||
lock.write_bytes(&pkt_for_set_on(lock.config.awake_enabled));
|
||||
lock.write_bytes(&pkt_for_apply());
|
||||
lock.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled));
|
||||
lock.write_bytes(&pkt_for_apply());
|
||||
lock.node
|
||||
.write_bytes(&pkt_for_set_on(lock.config.awake_enabled))?;
|
||||
lock.node.write_bytes(&pkt_for_apply())?;
|
||||
lock.node
|
||||
.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled))?;
|
||||
lock.node.write_bytes(&pkt_for_apply())?;
|
||||
|
||||
let action = lock.cache.boot.clone();
|
||||
CtrlAnime::run_thread(self.0.clone(), action, true);
|
||||
|
||||
@@ -64,7 +64,12 @@ impl CtrlAnimeZbus {
|
||||
let states;
|
||||
'outer: loop {
|
||||
if let Ok(mut lock) = self.0.try_lock() {
|
||||
lock.write_bytes(&pkt_for_set_on(status));
|
||||
lock.node
|
||||
.write_bytes(&pkt_for_set_on(status))
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
lock.config.awake_enabled = status;
|
||||
lock.config.write();
|
||||
|
||||
@@ -86,8 +91,18 @@ impl CtrlAnimeZbus {
|
||||
let states;
|
||||
'outer: loop {
|
||||
if let Ok(mut lock) = self.0.try_lock() {
|
||||
lock.write_bytes(&pkt_for_set_boot(on));
|
||||
lock.write_bytes(&pkt_for_apply());
|
||||
lock.node
|
||||
.write_bytes(&pkt_for_set_boot(on))
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
lock.node
|
||||
.write_bytes(&pkt_for_apply())
|
||||
.map_err(|err| {
|
||||
warn!("rog_anime::run_animation:callback {}", err);
|
||||
})
|
||||
.ok();
|
||||
lock.config.boot_anim_enabled = on;
|
||||
lock.config.write();
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ use crate::laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES};
|
||||
use log::{error, warn};
|
||||
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
|
||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT};
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::keyboard_led::KeyboardLed;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use super::controller::CtrlKbdLed;
|
||||
|
||||
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
|
||||
|
||||
/// Enable/disable LED control in various states such as
|
||||
@@ -128,14 +128,18 @@ impl Default for AuraConfig {
|
||||
fn default() -> Self {
|
||||
let mut prod_id = AuraDevice::Unknown;
|
||||
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
||||
if let Ok(_) = CtrlKbdLed::find_led_node(prod) {
|
||||
if let Ok(_) = HidRaw::new(prod) {
|
||||
prod_id = AuraDevice::from(*prod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if CtrlKbdLed::get_tuf_mode_path().is_some() {
|
||||
prod_id = AuraDevice::Tuf;
|
||||
if prod_id == AuraDevice::Unknown {
|
||||
if let Ok(p) = KeyboardLed::new() {
|
||||
if p.has_kbd_rgb_mode() {
|
||||
prod_id = AuraDevice::Tuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let enabled = if prod_id == AuraDevice::X19B6 {
|
||||
@@ -148,8 +152,8 @@ impl Default for AuraConfig {
|
||||
AuraDev19b6::AwakeKeyb,
|
||||
AuraDev19b6::ShutdownLogo,
|
||||
AuraDev19b6::ShutdownKeyb,
|
||||
AuraDev19b6::AwakeBar,
|
||||
AuraDev19b6::BootBar,
|
||||
AuraDev19b6::AwakeBar,
|
||||
AuraDev19b6::SleepBar,
|
||||
AuraDev19b6::ShutdownBar,
|
||||
]))
|
||||
|
||||
+142
-240
@@ -1,8 +1,3 @@
|
||||
// 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,
|
||||
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
|
||||
@@ -10,23 +5,17 @@ use crate::{
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use log::{error, info, warn};
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use rog_aura::{
|
||||
usb::{AuraDevice, LED_APPLY, LED_SET},
|
||||
AuraEffect, LedBrightness, LED_MSG_LEN,
|
||||
AuraEffect, KeyColourArray, LedBrightness, PerKeyRaw, LED_MSG_LEN,
|
||||
};
|
||||
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
|
||||
use rog_supported::LedSupportedFunctions;
|
||||
use smol::{stream::StreamExt, Executor};
|
||||
use std::path::Path;
|
||||
use rog_platform::{hid_raw::HidRaw, keyboard_led::KeyboardLed, supported::LedSupportedFunctions};
|
||||
use smol::Executor;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
io::{Read, Write},
|
||||
};
|
||||
use std::{fs::OpenOptions, sync::MutexGuard};
|
||||
use zbus::Connection;
|
||||
use std::sync::MutexGuard;
|
||||
|
||||
use crate::GetSupported;
|
||||
|
||||
@@ -44,19 +33,22 @@ impl GetSupported for CtrlKbdLed {
|
||||
|
||||
let mut prod_id = AuraDevice::Unknown;
|
||||
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
||||
if let Ok(_) = Self::find_led_node(prod) {
|
||||
if let Ok(_) = HidRaw::new(prod) {
|
||||
prod_id = AuraDevice::from(*prod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if Self::get_tuf_mode_path().is_some() {
|
||||
prod_id = AuraDevice::Tuf;
|
||||
let rgb = KeyboardLed::new();
|
||||
if let Ok(p) = rgb.as_ref() {
|
||||
if p.has_kbd_rgb_mode() {
|
||||
prod_id = AuraDevice::Tuf;
|
||||
}
|
||||
}
|
||||
|
||||
LedSupportedFunctions {
|
||||
prod_id,
|
||||
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
|
||||
brightness_set: rgb.is_ok(),
|
||||
stock_led_modes,
|
||||
multizone_led_mode,
|
||||
per_key_led_mode,
|
||||
@@ -66,8 +58,8 @@ impl GetSupported for CtrlKbdLed {
|
||||
|
||||
#[derive(Debug, PartialEq, PartialOrd)]
|
||||
pub enum LEDNode {
|
||||
Tuf,
|
||||
Rog(String),
|
||||
KbdLed(KeyboardLed),
|
||||
Rog(HidRaw),
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -75,9 +67,10 @@ pub struct CtrlKbdLed {
|
||||
// TODO: config stores the keyboard type as an AuraPower, use or update this
|
||||
pub led_prod: Option<String>,
|
||||
pub led_node: LEDNode,
|
||||
pub bright_node: String,
|
||||
pub kd_brightness: KeyboardLed,
|
||||
pub supported_modes: LaptopLedData,
|
||||
pub flip_effect_write: bool,
|
||||
pub per_key_mode_active: bool,
|
||||
pub config: AuraConfig,
|
||||
}
|
||||
|
||||
@@ -91,41 +84,17 @@ impl CtrlKbdLedTask {
|
||||
}
|
||||
|
||||
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(&lock.bright_node)
|
||||
.map_err(|err| match err.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
RogError::MissingLedBrightNode((&lock.bright_node).into(), err)
|
||||
}
|
||||
_ => RogError::Path((&lock.bright_node).into(), err),
|
||||
})?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)
|
||||
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if lock.config.brightness != num.into() {
|
||||
lock.config.read();
|
||||
lock.config.brightness = num.into();
|
||||
lock.config.write();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
Err(RogError::ParseLed)
|
||||
let bright = lock.kd_brightness.get_brightness()?;
|
||||
lock.config.read();
|
||||
lock.config.brightness = (bright as u32).into();
|
||||
lock.config.write();
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CtrlTask for CtrlKbdLedTask {
|
||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||
let connection = Connection::system()
|
||||
.await
|
||||
.expect("CtrlKbdLedTask could not create dbus connection");
|
||||
|
||||
let manager = ManagerProxy::new(&connection)
|
||||
.await
|
||||
.expect("CtrlKbdLedTask could not create ManagerProxy");
|
||||
|
||||
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| {
|
||||
// If waking up
|
||||
if !start {
|
||||
@@ -144,41 +113,41 @@ impl CtrlTask for CtrlKbdLedTask {
|
||||
}
|
||||
};
|
||||
|
||||
let inner = self.inner.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
||||
notif
|
||||
.for_each(|event| {
|
||||
if let Ok(args) = event.args() {
|
||||
loop {
|
||||
// Loop so that we do aquire the lock but also don't block other
|
||||
// threads (prevents potential deadlocks)
|
||||
if let Ok(lock) = inner.clone().try_lock() {
|
||||
load_save(args.start, lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
let inner1 = self.inner.clone();
|
||||
let inner2 = self.inner.clone();
|
||||
let inner3 = self.inner.clone();
|
||||
let inner4 = self.inner.clone();
|
||||
self.create_sys_event_tasks(
|
||||
executor,
|
||||
// Loop so that we do aquire the lock but also don't block other
|
||||
// threads (prevents potential deadlocks)
|
||||
move || loop {
|
||||
if let Ok(lock) = inner1.clone().try_lock() {
|
||||
load_save(true, lock);
|
||||
break;
|
||||
}
|
||||
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
||||
notif
|
||||
.for_each(|event| {
|
||||
if let Ok(args) = event.args() {
|
||||
loop {
|
||||
if let Ok(lock) = inner.clone().try_lock() {
|
||||
load_save(args.start, lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
},
|
||||
move || loop {
|
||||
if let Ok(lock) = inner2.clone().try_lock() {
|
||||
load_save(false, lock);
|
||||
break;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
},
|
||||
move || loop {
|
||||
if let Ok(lock) = inner3.clone().try_lock() {
|
||||
load_save(false, lock);
|
||||
break;
|
||||
}
|
||||
},
|
||||
move || loop {
|
||||
if let Ok(lock) = inner4.clone().try_lock() {
|
||||
load_save(false, lock);
|
||||
break;
|
||||
}
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -189,9 +158,7 @@ impl crate::Reloadable for CtrlKbdLedReloader {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||
ctrl.write_current_config_mode()?;
|
||||
ctrl.set_power_states(&ctrl.config)
|
||||
.map_err(|err| warn!("{err}"))
|
||||
.ok();
|
||||
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -210,7 +177,7 @@ impl CtrlKbdLed {
|
||||
let mut led_prod = None;
|
||||
let mut led_node = None;
|
||||
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
||||
match Self::find_led_node(prod) {
|
||||
match HidRaw::new(prod) {
|
||||
Ok(node) => {
|
||||
led_prod = Some(prod.to_string());
|
||||
led_node = Some(node);
|
||||
@@ -221,10 +188,9 @@ 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();
|
||||
let rgb_led = KeyboardLed::new()?;
|
||||
|
||||
if led_node.is_none() && tuf_node && bright_node.is_none() {
|
||||
if led_node.is_none() && !rgb_led.has_kbd_rgb_mode() {
|
||||
let dmi = sysfs_class::DmiId::default();
|
||||
if let Ok(prod_family) = dmi.product_family() {
|
||||
if prod_family.contains("TUF") {
|
||||
@@ -237,9 +203,9 @@ impl CtrlKbdLed {
|
||||
let led_node = if let Some(rog) = led_node {
|
||||
info!("Found ROG USB keyboard");
|
||||
LEDNode::Rog(rog)
|
||||
} else if tuf_node {
|
||||
} else if rgb_led.has_kbd_rgb_mode() {
|
||||
info!("Found TUF keyboard");
|
||||
LEDNode::Tuf
|
||||
LEDNode::KbdLed(rgb_led.clone())
|
||||
} else {
|
||||
LEDNode::None
|
||||
};
|
||||
@@ -247,66 +213,25 @@ impl CtrlKbdLed {
|
||||
let ctrl = CtrlKbdLed {
|
||||
led_prod,
|
||||
led_node,
|
||||
bright_node: bright_node.unwrap(), // If was none then we already returned above
|
||||
kd_brightness: rgb_led, // If was none then we already returned above
|
||||
supported_modes,
|
||||
flip_effect_write: false,
|
||||
per_key_mode_active: false,
|
||||
config,
|
||||
};
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
fn get_kbd_bright_path() -> Option<String> {
|
||||
if Path::new(KBD_BRIGHT_PATH).exists() {
|
||||
return Some(KBD_BRIGHT_PATH.to_string());
|
||||
}
|
||||
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)
|
||||
.open(&self.bright_node)
|
||||
.map_err(|err| match err.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
|
||||
}
|
||||
_ => RogError::Path((&self.bright_node).into(), err),
|
||||
})?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)
|
||||
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||
Ok(buf[0])
|
||||
self.kd_brightness
|
||||
.get_brightness()
|
||||
.map_err(|e| RogError::Platform(e))
|
||||
}
|
||||
|
||||
pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> {
|
||||
let path = Path::new(&self.bright_node);
|
||||
let mut file =
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.open(&path)
|
||||
.map_err(|err| match err.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
|
||||
}
|
||||
_ => RogError::Path((&self.bright_node).into(), err),
|
||||
})?;
|
||||
file.write_all(&[brightness.as_char_code()])
|
||||
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||
Ok(())
|
||||
self.kd_brightness
|
||||
.set_brightness(brightness as u8)
|
||||
.map_err(|e| RogError::Platform(e))
|
||||
}
|
||||
|
||||
pub fn next_brightness(&mut self) -> Result<(), RogError> {
|
||||
@@ -332,68 +257,24 @@ 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> {
|
||||
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())?;
|
||||
}
|
||||
pub(super) fn set_power_states(&mut self) -> Result<(), RogError> {
|
||||
if let LEDNode::KbdLed(platform) = &mut self.led_node {
|
||||
if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) {
|
||||
let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8, pwr[4] as u8];
|
||||
platform.set_kbd_rgb_state(&buf)?;
|
||||
}
|
||||
} else {
|
||||
let bytes = AuraPowerConfig::to_bytes(&config.enabled);
|
||||
} else if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
let bytes = AuraPowerConfig::to_bytes(&self.config.enabled);
|
||||
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2]];
|
||||
|
||||
self.write_bytes(&message)?;
|
||||
self.write_bytes(&LED_SET)?;
|
||||
hid_raw.write_bytes(&message)?;
|
||||
hid_raw.write_bytes(&LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
self.write_bytes(&LED_APPLY)?;
|
||||
hid_raw.write_bytes(&LED_APPLY)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn find_led_node(id_product: &str) -> Result<String, RogError> {
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
enumerator.match_subsystem("hidraw").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator.scan_devices().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("scan_devices failed".into(), err)
|
||||
})? {
|
||||
if let Some(parent) = device
|
||||
.parent_with_subsystem_devtype("usb", "usb_device")
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("parent_with_subsystem_devtype failed".into(), err)
|
||||
})?
|
||||
{
|
||||
if parent
|
||||
.attribute_value("idProduct")
|
||||
.ok_or_else(|| RogError::NotFound("LED idProduct".into()))?
|
||||
== id_product
|
||||
{
|
||||
if let Some(dev_node) = device.devnode() {
|
||||
info!("Using device at: {:?} for LED control", dev_node);
|
||||
return Ok(dev_node.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(RogError::MissingFunction(
|
||||
"ASUS LED device node not found".into(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Set an Aura effect if the effect mode or zone is supported.
|
||||
///
|
||||
/// On success the aura config file is read to refresh cached values, then the effect is
|
||||
@@ -414,31 +295,42 @@ impl CtrlKbdLed {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Should only be used if the bytes you are writing are verified correct
|
||||
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||
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
|
||||
.write_all(message)
|
||||
.map_err(|err| RogError::Write("write_bytes".into(), err));
|
||||
}
|
||||
}
|
||||
Err(RogError::NoAuraNode)
|
||||
}
|
||||
/// Write an effect block. This is for per-key, but can be repurposed to
|
||||
/// write the raw factory mode packets - when doing this it is expected that
|
||||
/// only the first `Vec` (`effect[0]`) is valid.
|
||||
pub fn write_effect_block(&mut self, effect: &PerKeyRaw) -> Result<(), RogError> {
|
||||
let pkt_type = effect[0][1];
|
||||
const PER_KEY_TYPE: u8 = 0xbc;
|
||||
|
||||
/// Write an effect block. This is for per-key
|
||||
fn _write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
|
||||
if self.flip_effect_write {
|
||||
for row in effect.iter().rev() {
|
||||
self.write_bytes(row)?;
|
||||
if pkt_type != PER_KEY_TYPE {
|
||||
self.per_key_mode_active = false;
|
||||
if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
hid_raw.write_bytes(&effect[0])?;
|
||||
hid_raw.write_bytes(&LED_SET)?;
|
||||
// hid_raw.write_bytes(&LED_APPLY)?;
|
||||
}
|
||||
} else {
|
||||
for row in effect.iter() {
|
||||
self.write_bytes(row)?;
|
||||
if !self.per_key_mode_active {
|
||||
if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
let init = KeyColourArray::get_init_msg();
|
||||
hid_raw.write_bytes(&init)?;
|
||||
}
|
||||
self.per_key_mode_active = true;
|
||||
}
|
||||
if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
for row in effect.iter() {
|
||||
hid_raw.write_bytes(row)?;
|
||||
}
|
||||
} else if let LEDNode::KbdLed(tuf) = &self.led_node {
|
||||
for row in effect.iter() {
|
||||
let r = row[9];
|
||||
let g = row[10];
|
||||
let b = row[11];
|
||||
tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
|
||||
}
|
||||
}
|
||||
self.flip_effect_write = !self.flip_effect_write;
|
||||
}
|
||||
self.flip_effect_write = !self.flip_effect_write;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -477,21 +369,27 @@ impl CtrlKbdLed {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
|
||||
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 {
|
||||
fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
|
||||
if let LEDNode::KbdLed(platform) = &self.led_node {
|
||||
let buf = [
|
||||
1,
|
||||
mode.mode as u8,
|
||||
mode.colour1.0,
|
||||
mode.colour1.1,
|
||||
mode.colour1.2,
|
||||
mode.speed as u8,
|
||||
];
|
||||
platform.set_kbd_rgb_mode(&buf)?;
|
||||
} else if let LEDNode::Rog(hid_raw) = &self.led_node {
|
||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||
self.write_bytes(&bytes)?;
|
||||
self.write_bytes(&LED_SET)?;
|
||||
hid_raw.write_bytes(&bytes)?;
|
||||
hid_raw.write_bytes(&LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
self.write_bytes(&LED_APPLY)?;
|
||||
hid_raw.write_bytes(&LED_APPLY)?;
|
||||
} else {
|
||||
return Err(RogError::NoAuraKeyboard);
|
||||
}
|
||||
self.per_key_mode_active = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -514,17 +412,17 @@ impl CtrlKbdLed {
|
||||
self.create_multizone_default()?;
|
||||
}
|
||||
|
||||
if let Some(multizones) = self.config.multizone.as_ref() {
|
||||
if let Some(multizones) = self.config.multizone.as_mut() {
|
||||
if let Some(set) = multizones.get(&mode) {
|
||||
for mode in set {
|
||||
self.write_mode(mode)?;
|
||||
for mode in set.clone() {
|
||||
self.write_mode(&mode)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mode = self.config.current_mode;
|
||||
if let Some(effect) = self.config.builtins.get(&mode) {
|
||||
self.write_mode(effect)?;
|
||||
if let Some(effect) = self.config.builtins.get(&mode).cloned() {
|
||||
self.write_mode(&effect)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,6 +460,7 @@ impl CtrlKbdLed {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
|
||||
use rog_platform::keyboard_led::KeyboardLed;
|
||||
|
||||
use crate::{
|
||||
ctrl_aura::{config::AuraConfig, controller::LEDNode},
|
||||
@@ -585,9 +484,10 @@ mod tests {
|
||||
let mut controller = CtrlKbdLed {
|
||||
led_prod: None,
|
||||
led_node: LEDNode::None,
|
||||
bright_node: String::new(),
|
||||
kd_brightness: KeyboardLed::default(),
|
||||
supported_modes,
|
||||
flip_effect_write: false,
|
||||
per_key_mode_active: false,
|
||||
config,
|
||||
};
|
||||
|
||||
@@ -601,7 +501,7 @@ mod tests {
|
||||
.set_effect(effect.clone())
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"No Aura keyboard node found"
|
||||
"No supported Aura keyboard"
|
||||
);
|
||||
|
||||
effect.mode = AuraModeNum::Laser;
|
||||
@@ -629,7 +529,7 @@ mod tests {
|
||||
.set_effect(effect.clone())
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"No Aura keyboard node found"
|
||||
"No supported Aura keyboard"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -647,9 +547,10 @@ mod tests {
|
||||
let mut controller = CtrlKbdLed {
|
||||
led_prod: None,
|
||||
led_node: LEDNode::None,
|
||||
bright_node: String::new(),
|
||||
kd_brightness: KeyboardLed::default(),
|
||||
supported_modes,
|
||||
flip_effect_write: false,
|
||||
per_key_mode_active: false,
|
||||
config,
|
||||
};
|
||||
|
||||
@@ -684,9 +585,10 @@ mod tests {
|
||||
let mut controller = CtrlKbdLed {
|
||||
led_prod: None,
|
||||
led_node: LEDNode::None,
|
||||
bright_node: String::new(),
|
||||
kd_brightness: KeyboardLed::default(),
|
||||
supported_modes,
|
||||
flip_effect_write: false,
|
||||
per_key_mode_active: false,
|
||||
config,
|
||||
};
|
||||
|
||||
@@ -699,7 +601,7 @@ mod tests {
|
||||
.write_current_config_mode()
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
"No Aura keyboard node found"
|
||||
"No supported Aura keyboard"
|
||||
);
|
||||
assert!(controller.config.multizone.is_some());
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::warn;
|
||||
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness};
|
||||
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness, PerKeyRaw};
|
||||
use zbus::{dbus_interface, Connection, SignalContext};
|
||||
|
||||
use super::controller::CtrlKbdLedZbus;
|
||||
@@ -78,7 +78,7 @@ impl CtrlKbdLedZbus {
|
||||
|
||||
ctrl.config.write();
|
||||
|
||||
ctrl.set_power_states(&ctrl.config).map_err(|e| {
|
||||
ctrl.set_power_states().map_err(|e| {
|
||||
warn!("{}", e);
|
||||
e
|
||||
})?;
|
||||
@@ -210,6 +210,13 @@ impl CtrlKbdLedZbus {
|
||||
}
|
||||
}
|
||||
|
||||
async fn per_key_raw(&self, data: PerKeyRaw) -> zbus::fdo::Result<()> {
|
||||
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||
ctrl.write_effect_block(&data)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the current LED brightness
|
||||
#[dbus_interface(property)]
|
||||
async fn led_brightness(&self) -> i8 {
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
use crate::CtrlTask;
|
||||
use crate::{config::Config, error::RogError, GetSupported};
|
||||
use async_trait::async_trait;
|
||||
use log::{info, warn};
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use rog_supported::ChargeSupportedFunctions;
|
||||
use smol::stream::StreamExt;
|
||||
use smol::Executor;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use zbus::dbus_interface;
|
||||
use zbus::Connection;
|
||||
use zbus::SignalContext;
|
||||
|
||||
static BAT_CHARGE_PATH0: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
|
||||
static BAT_CHARGE_PATH1: &str = "/sys/class/power_supply/BAT1/charge_control_end_threshold";
|
||||
static BAT_CHARGE_PATH2: &str = "/sys/class/power_supply/BAT2/charge_control_end_threshold";
|
||||
|
||||
impl GetSupported for CtrlCharge {
|
||||
type A = ChargeSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
ChargeSupportedFunctions {
|
||||
charge_level_set: CtrlCharge::get_battery_path().is_ok(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CtrlCharge {
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl CtrlCharge {
|
||||
async fn set_limit(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
limit: u8,
|
||||
) -> zbus::fdo::Result<()> {
|
||||
if !(20..=100).contains(&limit) {
|
||||
return Err(RogError::ChargeLimit(limit))?;
|
||||
}
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
Self::set(limit, &mut config)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Self::notify_charge(&ctxt, limit).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn limit(&self) -> i8 {
|
||||
if let Ok(config) = self.config.try_lock() {
|
||||
return config.bat_charge_limit as i8;
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_charge(ctxt: &SignalContext<'_>, limit: u8) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ZbusAdd for CtrlCharge {
|
||||
async fn add_to_server(self, server: &mut Connection) {
|
||||
Self::add_to_server_helper(self, "/org/asuslinux/Charge", server).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlCharge {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
config.read();
|
||||
Self::set(config.bat_charge_limit, &mut config)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlCharge {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
CtrlCharge::get_battery_path()?;
|
||||
Ok(CtrlCharge { config })
|
||||
}
|
||||
|
||||
fn get_battery_path() -> Result<&'static str, RogError> {
|
||||
if Path::new(BAT_CHARGE_PATH0).exists() {
|
||||
Ok(BAT_CHARGE_PATH0)
|
||||
} else if Path::new(BAT_CHARGE_PATH1).exists() {
|
||||
Ok(BAT_CHARGE_PATH1)
|
||||
} else if Path::new(BAT_CHARGE_PATH2).exists() {
|
||||
Ok(BAT_CHARGE_PATH2)
|
||||
} else {
|
||||
Err(RogError::MissingFunction(
|
||||
"Charge control not available, you may require a v5.8.10 series kernel or newer"
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set(limit: u8, config: &mut Config) -> Result<(), RogError> {
|
||||
if !(20..=100).contains(&limit) {
|
||||
return Err(RogError::ChargeLimit(limit));
|
||||
}
|
||||
|
||||
let path = Self::get_battery_path()?;
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
file.write_all(limit.to_string().as_bytes())
|
||||
.map_err(|err| RogError::Write(path.into(), err))?;
|
||||
info!("Battery charge limit: {}", limit);
|
||||
|
||||
config.read();
|
||||
config.bat_charge_limit = limit;
|
||||
config.write();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CtrlTask for CtrlCharge {
|
||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||
let connection = Connection::system()
|
||||
.await
|
||||
.expect("CtrlCharge could not create dbus connection");
|
||||
|
||||
let manager = ManagerProxy::new(&connection)
|
||||
.await
|
||||
.expect("CtrlCharge could not create ManagerProxy");
|
||||
|
||||
let config1 = self.config.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
||||
notif
|
||||
.for_each(|event| {
|
||||
if let Ok(args) = event.args() {
|
||||
// If waking up
|
||||
if !args.start {
|
||||
info!("CtrlCharge reloading charge limit");
|
||||
if let Ok(mut lock) = config1.try_lock() {
|
||||
Self::set(lock.bat_charge_limit, &mut lock)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let manager = ManagerProxy::new(&connection)
|
||||
.await
|
||||
.expect("CtrlCharge could not create ManagerProxy");
|
||||
|
||||
let config = self.config.clone();
|
||||
executor
|
||||
.spawn(async move {
|
||||
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
||||
notif
|
||||
.for_each(|event| {
|
||||
if let Ok(args) = event.args() {
|
||||
// If waking up - intention is to catch hibernation event
|
||||
if !args.start {
|
||||
info!("CtrlCharge reloading charge limit");
|
||||
loop {
|
||||
if let Ok(mut lock) = config.clone().try_lock() {
|
||||
Self::set(lock.bat_charge_limit, &mut lock)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
use crate::CtrlTask;
|
||||
use crate::{config::Config, error::RogError, GetSupported};
|
||||
use async_trait::async_trait;
|
||||
use log::{info, warn};
|
||||
use rog_platform::platform::{AsusPlatform, GpuMode};
|
||||
use rog_platform::supported::RogBiosSupportedFunctions;
|
||||
use smol::Executor;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use zbus::Connection;
|
||||
use zbus::{dbus_interface, SignalContext};
|
||||
|
||||
static ASUS_POST_LOGO_SOUND: &str =
|
||||
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CtrlRogBios {
|
||||
platform: AsusPlatform,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
impl GetSupported for CtrlRogBios {
|
||||
type A = RogBiosSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
let mut panel_overdrive = false;
|
||||
let mut dgpu_disable = false;
|
||||
let mut egpu_enable = false;
|
||||
let mut gpu_mux = false;
|
||||
|
||||
if let Ok(platform) = AsusPlatform::new() {
|
||||
panel_overdrive = platform.has_panel_od();
|
||||
dgpu_disable = platform.has_dgpu_disable();
|
||||
egpu_enable = platform.has_egpu_enable();
|
||||
gpu_mux = platform.has_gpu_mux_mode();
|
||||
}
|
||||
|
||||
RogBiosSupportedFunctions {
|
||||
post_sound: Path::new(ASUS_POST_LOGO_SOUND).exists(),
|
||||
gpu_mux,
|
||||
panel_overdrive,
|
||||
dgpu_disable,
|
||||
egpu_enable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlRogBios {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
let platform = AsusPlatform::new()?;
|
||||
|
||||
if !platform.has_gpu_mux_mode() {
|
||||
info!("G-Sync Switchable Graphics or GPU MUX not detected");
|
||||
info!("Standard graphics switching will still work.");
|
||||
}
|
||||
|
||||
if Path::new(ASUS_POST_LOGO_SOUND).exists() {
|
||||
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
|
||||
} else {
|
||||
info!("Switch for POST boot sound not detected");
|
||||
}
|
||||
|
||||
Ok(CtrlRogBios { platform, config })
|
||||
}
|
||||
|
||||
fn set_path_mutable(path: &str) -> Result<(), RogError> {
|
||||
let output = Command::new("/usr/bin/chattr")
|
||||
.arg("-i")
|
||||
.arg(path)
|
||||
.output()
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
info!("Set {} writeable: status: {}", path, output.status);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_gfx_mode(&self, mode: GpuMode) -> Result<(), RogError> {
|
||||
self.platform.set_gpu_mux_mode(mode.to_mux())?;
|
||||
// self.update_initramfs(enable)?;
|
||||
if mode == GpuMode::Discrete {
|
||||
info!("Set system-level graphics mode: Dedicated Nvidia");
|
||||
} else {
|
||||
info!("Set system-level graphics mode: Optimus");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_boot_sound() -> Result<i8, RogError> {
|
||||
let path = ASUS_POST_LOGO_SOUND;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)
|
||||
.map_err(|err| RogError::Read(path.into(), err))?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
Ok(data[idx] as i8)
|
||||
}
|
||||
|
||||
pub(super) fn set_boot_sound(on: bool) -> Result<(), RogError> {
|
||||
let path = ASUS_POST_LOGO_SOUND;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)
|
||||
.map_err(|err| RogError::Read(path.into(), err))?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
if on {
|
||||
data[idx] = 1;
|
||||
info!("Set boot POST sound on");
|
||||
} else {
|
||||
data[idx] = 0;
|
||||
info!("Set boot POST sound off");
|
||||
}
|
||||
file.write_all(&data)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_panel_od(&self, enable: bool) -> Result<(), RogError> {
|
||||
self.platform.set_panel_od(enable).map_err(|err| {
|
||||
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||
err
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl CtrlRogBios {
|
||||
async fn set_gpu_mux_mode(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
mode: GpuMode,
|
||||
) {
|
||||
self.set_gfx_mode(mode)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
Self::notify_gpu_mux_mode(&ctxt, mode).await.ok();
|
||||
}
|
||||
|
||||
fn gpu_mux_mode(&self) -> GpuMode {
|
||||
match self.platform.get_gpu_mux_mode() {
|
||||
Ok(m) => GpuMode::from_mux(m),
|
||||
Err(e) => {
|
||||
warn!("CtrlRogBios: get_gfx_mode {}", e);
|
||||
GpuMode::Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_gpu_mux_mode(
|
||||
signal_ctxt: &SignalContext<'_>,
|
||||
mode: GpuMode,
|
||||
) -> zbus::Result<()> {
|
||||
}
|
||||
|
||||
async fn set_post_boot_sound(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
on: bool,
|
||||
) {
|
||||
Self::set_boot_sound(on)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_post_boot_sound {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
Self::notify_post_boot_sound(&ctxt, on).await.ok();
|
||||
}
|
||||
|
||||
fn post_boot_sound(&self) -> i8 {
|
||||
Self::get_boot_sound()
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: get_boot_sound {}", err);
|
||||
err
|
||||
})
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
|
||||
|
||||
async fn set_panel_overdrive(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
overdrive: bool,
|
||||
) {
|
||||
if self
|
||||
.set_panel_od(overdrive)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||
err
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
if let Ok(mut lock) = self.config.try_lock() {
|
||||
lock.panel_od = overdrive;
|
||||
lock.write();
|
||||
}
|
||||
Self::notify_panel_overdrive(&ctxt, overdrive).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn panel_overdrive(&self) -> bool {
|
||||
self.platform
|
||||
.get_panel_od()
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: get panel overdrive {}", err);
|
||||
err
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_panel_overdrive(
|
||||
signal_ctxt: &SignalContext<'_>,
|
||||
overdrive: bool,
|
||||
) -> zbus::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ZbusAdd for CtrlRogBios {
|
||||
async fn add_to_server(self, server: &mut Connection) {
|
||||
Self::add_to_server_helper(self, "/org/asuslinux/Platform", server).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlRogBios {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if self.platform.has_panel_od() {
|
||||
let p = if let Ok(lock) = self.config.try_lock() {
|
||||
lock.panel_od
|
||||
} else {
|
||||
false
|
||||
};
|
||||
self.set_panel_od(p)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CtrlTask for CtrlRogBios {
|
||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||
let platform1 = self.clone();
|
||||
let platform2 = self.clone();
|
||||
self.create_sys_event_tasks(
|
||||
executor,
|
||||
move || {},
|
||||
move || {
|
||||
info!("CtrlRogBios reloading panel_od");
|
||||
if let Ok(lock) = platform1.config.try_lock() {
|
||||
if platform1.platform.has_panel_od() {
|
||||
platform1
|
||||
.set_panel_od(lock.panel_od)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
},
|
||||
move || {},
|
||||
move || {
|
||||
info!("CtrlRogBios reloading panel_od");
|
||||
if let Ok(lock) = platform2.config.try_lock() {
|
||||
if platform2.platform.has_panel_od() {
|
||||
platform2
|
||||
.set_panel_od(lock.panel_od)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
use crate::CtrlTask;
|
||||
use crate::{config::Config, error::RogError, GetSupported};
|
||||
use async_trait::async_trait;
|
||||
use log::{info, warn};
|
||||
use rog_platform::power::AsusPower;
|
||||
use rog_platform::supported::ChargeSupportedFunctions;
|
||||
use smol::Executor;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use zbus::dbus_interface;
|
||||
use zbus::Connection;
|
||||
use zbus::SignalContext;
|
||||
|
||||
impl GetSupported for CtrlPower {
|
||||
type A = ChargeSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
ChargeSupportedFunctions {
|
||||
charge_level_set: if let Ok(power) = AsusPower::new() {
|
||||
power.has_charge_control_end_threshold()
|
||||
} else {
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CtrlPower {
|
||||
power: AsusPower,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl CtrlPower {
|
||||
async fn set_limit(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
limit: u8,
|
||||
) -> zbus::fdo::Result<()> {
|
||||
if !(20..=100).contains(&limit) {
|
||||
return Err(RogError::ChargeLimit(limit))?;
|
||||
}
|
||||
self.set(limit)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
Self::notify_charge(&ctxt, limit).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn limit(&self) -> i8 {
|
||||
if let Ok(config) = self.config.try_lock() {
|
||||
return config.bat_charge_limit as i8;
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_charge(ctxt: &SignalContext<'_>, limit: u8) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ZbusAdd for CtrlPower {
|
||||
async fn add_to_server(self, server: &mut Connection) {
|
||||
Self::add_to_server_helper(self, "/org/asuslinux/Charge", server).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlPower {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
config.read();
|
||||
self.set(config.bat_charge_limit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlPower {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
Ok(CtrlPower {
|
||||
power: AsusPower::new()?,
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn set(&self, limit: u8) -> Result<(), RogError> {
|
||||
if !(20..=100).contains(&limit) {
|
||||
return Err(RogError::ChargeLimit(limit));
|
||||
}
|
||||
|
||||
self.power.set_charge_control_end_threshold(limit)?;
|
||||
|
||||
info!("Battery charge limit: {}", limit);
|
||||
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
config.read();
|
||||
config.bat_charge_limit = limit;
|
||||
config.write();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CtrlTask for CtrlPower {
|
||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||
let power1 = self.clone();
|
||||
let power2 = self.clone();
|
||||
self.create_sys_event_tasks(
|
||||
executor,
|
||||
move || {},
|
||||
move || {
|
||||
info!("CtrlCharge reloading charge limit");
|
||||
if let Ok(lock) = power1.config.try_lock() {
|
||||
power1
|
||||
.set(lock.bat_charge_limit)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
},
|
||||
move || {},
|
||||
move || {
|
||||
info!("CtrlCharge reloading charge limit");
|
||||
if let Ok(lock) = power2.config.try_lock() {
|
||||
power2
|
||||
.set(lock.bat_charge_limit)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::error::RogError;
|
||||
use crate::{CtrlTask, GetSupported};
|
||||
use async_trait::async_trait;
|
||||
use log::{info, warn};
|
||||
use rog_platform::supported::PlatformProfileFunctions;
|
||||
use rog_profiles::error::ProfileError;
|
||||
use rog_profiles::{FanCurveProfiles, Profile};
|
||||
use rog_supported::PlatformProfileFunctions;
|
||||
use smol::Executor;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::config::ProfileConfig;
|
||||
|
||||
@@ -56,48 +55,31 @@ impl crate::Reloadable for CtrlPlatformProfile {
|
||||
}
|
||||
|
||||
impl CtrlPlatformProfile {
|
||||
pub fn new(mut config: ProfileConfig) -> Result<Self, RogError> {
|
||||
pub fn new(config: ProfileConfig) -> Result<Self, RogError> {
|
||||
if Profile::is_platform_profile_supported() {
|
||||
info!("Device has profile control available");
|
||||
|
||||
let mut controller = CtrlPlatformProfile { config };
|
||||
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();
|
||||
if controller.config.fan_curves.is_none() {
|
||||
controller.config.fan_curves = Some(Default::default());
|
||||
for _ in [Profile::Balanced, Profile::Performance, Profile::Quiet] {
|
||||
controller.set_next_profile()?;
|
||||
controller.set_active_curve_to_defaults()?;
|
||||
|
||||
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");
|
||||
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
|
||||
if let Some(curves) = controller.config.fan_curves.as_ref() {
|
||||
info!(
|
||||
"{active:?}: {}",
|
||||
String::from(curves.get_fan_curves_for(active))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(CtrlPlatformProfile { config });
|
||||
return Ok(controller);
|
||||
}
|
||||
|
||||
Err(ProfileError::NotSupported.into())
|
||||
|
||||
@@ -174,6 +174,30 @@ impl ProfileZbus {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset the stored (self) and device curve to the defaults of the platform.
|
||||
///
|
||||
/// Each platform_profile has a different default and the defualt can be read
|
||||
/// only for the currently active profile.
|
||||
fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> {
|
||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||
ctrl.config.read();
|
||||
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
|
||||
|
||||
Profile::set_profile(profile)
|
||||
.map_err(|e| warn!("set_profile, {}", e))
|
||||
.ok();
|
||||
ctrl.set_active_curve_to_defaults()
|
||||
.map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e))
|
||||
.ok();
|
||||
|
||||
Profile::set_profile(active)
|
||||
.map_err(|e| warn!("set_profile, {}", e))
|
||||
.ok();
|
||||
ctrl.save_config();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> {
|
||||
}
|
||||
|
||||
@@ -1,399 +0,0 @@
|
||||
use crate::{config::Config, error::RogError, GetSupported};
|
||||
use async_trait::async_trait;
|
||||
use log::{error, info, warn};
|
||||
use rog_supported::RogBiosSupportedFunctions;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::BufRead;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use zbus::Connection;
|
||||
use zbus::{dbus_interface, SignalContext};
|
||||
|
||||
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
|
||||
const DRACUT_PATH: &str = "/usr/bin/dracut";
|
||||
|
||||
static ASUS_SWITCH_GRAPHIC_MODE: &str =
|
||||
"/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
||||
static ASUS_POST_LOGO_SOUND: &str =
|
||||
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
||||
static ASUS_PANEL_OD_PATH: &str = "/sys/devices/platform/asus-nb-wmi/panel_od";
|
||||
static ASUS_DGPU_DISABLE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/dgpu_disable";
|
||||
static ASUS_EGPU_ENABLE_PATH: &str = "/sys/devices/platform/asus-nb-wmi/egpu_enable";
|
||||
|
||||
pub struct CtrlRogBios {
|
||||
_config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
impl GetSupported for CtrlRogBios {
|
||||
type A = RogBiosSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
RogBiosSupportedFunctions {
|
||||
post_sound: Path::new(ASUS_POST_LOGO_SOUND).exists(),
|
||||
dedicated_gfx: Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists(),
|
||||
panel_overdrive: Path::new(ASUS_PANEL_OD_PATH).exists(),
|
||||
dgpu_disable: Path::new(ASUS_DGPU_DISABLE_PATH).exists(),
|
||||
egpu_enable: Path::new(ASUS_EGPU_ENABLE_PATH).exists(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl CtrlRogBios {
|
||||
async fn set_dedicated_graphic_mode(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
dedicated: bool,
|
||||
) {
|
||||
self.set_gfx_mode(dedicated)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
Self::notify_dedicated_graphic_mode(&ctxt, dedicated)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn dedicated_graphic_mode(&self) -> i8 {
|
||||
Self::get_gfx_mode()
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: get_gfx_mode {}", err);
|
||||
err
|
||||
})
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_dedicated_graphic_mode(
|
||||
signal_ctxt: &SignalContext<'_>,
|
||||
dedicated: bool,
|
||||
) -> zbus::Result<()> {
|
||||
}
|
||||
|
||||
async fn set_post_boot_sound(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
on: bool,
|
||||
) {
|
||||
Self::set_boot_sound(on)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_post_boot_sound {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
Self::notify_post_boot_sound(&ctxt, on).await.ok();
|
||||
}
|
||||
|
||||
fn post_boot_sound(&self) -> i8 {
|
||||
Self::get_boot_sound()
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: get_boot_sound {}", err);
|
||||
err
|
||||
})
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
|
||||
|
||||
async fn set_panel_overdrive(
|
||||
&mut self,
|
||||
#[zbus(signal_context)] ctxt: SignalContext<'_>,
|
||||
overdrive: bool,
|
||||
) {
|
||||
if self
|
||||
.set_panel_od(overdrive)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||
err
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
Self::notify_panel_overdrive(&ctxt, overdrive).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn panel_overdrive(&self) -> i8 {
|
||||
let path = ASUS_PANEL_OD_PATH;
|
||||
if let Ok(mut file) = OpenOptions::new().read(true).open(path).map_err(|err| {
|
||||
warn!("CtrlRogBios: panel_overdrive {}", err);
|
||||
err
|
||||
}) {
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
|
||||
if buf.len() >= 1 {
|
||||
let tmp = String::from_utf8_lossy(&buf[0..1]);
|
||||
return tmp.parse::<i8>().unwrap_or(-1);
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
async fn notify_panel_overdrive(
|
||||
signal_ctxt: &SignalContext<'_>,
|
||||
overdrive: bool,
|
||||
) -> zbus::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ZbusAdd for CtrlRogBios {
|
||||
async fn add_to_server(self, server: &mut Connection) {
|
||||
Self::add_to_server_helper(self, "/org/asuslinux/RogBios", server).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlRogBios {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlRogBios {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
if Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists() {
|
||||
CtrlRogBios::set_path_mutable(ASUS_SWITCH_GRAPHIC_MODE)?;
|
||||
} else {
|
||||
info!("G-Sync Switchable Graphics not detected");
|
||||
info!("If your laptop is not a G-Sync enabled laptop then you can ignore this. Standard graphics switching will still work.");
|
||||
}
|
||||
|
||||
if Path::new(ASUS_POST_LOGO_SOUND).exists() {
|
||||
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
|
||||
} else {
|
||||
info!("Switch for POST boot sound not detected");
|
||||
}
|
||||
|
||||
Ok(CtrlRogBios { _config: config })
|
||||
}
|
||||
|
||||
fn set_path_mutable(path: &str) -> Result<(), RogError> {
|
||||
let output = Command::new("/usr/bin/chattr")
|
||||
.arg("-i")
|
||||
.arg(path)
|
||||
.output()
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
info!("Set {} writeable: status: {}", path, output.status);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn has_dedicated_gfx_toggle() -> bool {
|
||||
Path::new(ASUS_SWITCH_GRAPHIC_MODE).exists()
|
||||
}
|
||||
|
||||
pub fn get_gfx_mode() -> Result<i8, RogError> {
|
||||
let path = ASUS_SWITCH_GRAPHIC_MODE;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)
|
||||
.map_err(|err| RogError::Read(path.into(), err))?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
Ok(data[idx] as i8)
|
||||
}
|
||||
|
||||
pub(super) fn set_gfx_mode(&self, dedicated: bool) -> Result<(), RogError> {
|
||||
let path = ASUS_SWITCH_GRAPHIC_MODE;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
if dedicated {
|
||||
data[idx] = 1;
|
||||
info!("Set system-level graphics mode: Dedicated Nvidia");
|
||||
} else {
|
||||
data[idx] = 0;
|
||||
info!("Set system-level graphics mode: Optimus");
|
||||
}
|
||||
file.write_all(&data)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
self.update_initramfs(dedicated)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_boot_sound() -> Result<i8, RogError> {
|
||||
let path = ASUS_POST_LOGO_SOUND;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)
|
||||
.map_err(|err| RogError::Read(path.into(), err))?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
Ok(data[idx] as i8)
|
||||
}
|
||||
|
||||
pub(super) fn set_boot_sound(on: bool) -> Result<(), RogError> {
|
||||
let path = ASUS_POST_LOGO_SOUND;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)
|
||||
.map_err(|err| RogError::Read(path.into(), err))?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
if on {
|
||||
data[idx] = 1;
|
||||
info!("Set boot POST sound on");
|
||||
} else {
|
||||
data[idx] = 0;
|
||||
info!("Set boot POST sound off");
|
||||
}
|
||||
file.write_all(&data)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// required for g-sync mode
|
||||
fn update_initramfs(&self, dedicated: bool) -> Result<(), RogError> {
|
||||
let mut initfs_cmd = None;
|
||||
|
||||
if Path::new(INITRAMFS_PATH).exists() {
|
||||
let mut cmd = Command::new("update-initramfs");
|
||||
cmd.arg("-u");
|
||||
initfs_cmd = Some(cmd);
|
||||
info!("Using initramfs update command 'update-initramfs'");
|
||||
} else if Path::new(DRACUT_PATH).exists() {
|
||||
let mut cmd = Command::new("dracut");
|
||||
cmd.arg("-f");
|
||||
cmd.arg("-q");
|
||||
initfs_cmd = Some(cmd);
|
||||
info!("Using initramfs update command 'dracut'");
|
||||
}
|
||||
|
||||
if let Some(mut cmd) = initfs_cmd {
|
||||
info!("Updating initramfs");
|
||||
|
||||
// If switching to Nvidia dedicated we need these modules included
|
||||
if Path::new(DRACUT_PATH).exists() && dedicated {
|
||||
cmd.arg("--add-drivers");
|
||||
cmd.arg("nvidia nvidia-drm nvidia-modeset nvidia-uvm");
|
||||
info!("System uses dracut, forcing nvidia modules to be included in init");
|
||||
} else if Path::new(INITRAMFS_PATH).exists() {
|
||||
let modules = vec![
|
||||
"nvidia\n",
|
||||
"nvidia-drm\n",
|
||||
"nvidia-modeset\n",
|
||||
"nvidia-uvm\n",
|
||||
];
|
||||
|
||||
let module_include = Path::new("/etc/initramfs-tools/modules");
|
||||
|
||||
if dedicated {
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.open(module_include)
|
||||
.map_err(|err| {
|
||||
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
||||
})?;
|
||||
// add nvidia modules to module_include
|
||||
file.write_all(modules.concat().as_bytes())?;
|
||||
} else {
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.open(module_include)
|
||||
.map_err(|err| {
|
||||
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
||||
})?;
|
||||
|
||||
let mut buf = Vec::new();
|
||||
// remove modules
|
||||
for line in std::io::BufReader::new(file).lines().flatten() {
|
||||
if !modules.contains(&line.as_str()) {
|
||||
buf.append(&mut line.as_bytes().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(module_include)
|
||||
.map_err(|err| {
|
||||
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
||||
})?;
|
||||
std::io::BufWriter::new(file).write_all(&buf)?;
|
||||
}
|
||||
}
|
||||
|
||||
let status = cmd
|
||||
.status()
|
||||
.map_err(|err| RogError::Write(format!("{:?}", cmd), err))?;
|
||||
if !status.success() {
|
||||
error!("Ram disk update failed");
|
||||
return Err(RogError::Initramfs("Ram disk update failed".into()));
|
||||
} else {
|
||||
info!("Successfully updated initramfs");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_panel_od(&mut self, overdrive: bool) -> Result<(), RogError> {
|
||||
let path = ASUS_PANEL_OD_PATH;
|
||||
let mut file = OpenOptions::new().write(true).open(path).map_err(|err| {
|
||||
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
let s = if overdrive { '1' } else { '0' };
|
||||
file.write(&[s as u8]).map_err(|err| {
|
||||
warn!("CtrlRogBios: set_panel_overdrive {}", err);
|
||||
err
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::CtrlRogBios;
|
||||
use crate::config::Config;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[test]
|
||||
#[ignore = "Must be manually tested"]
|
||||
fn set_multizone_4key_config() {
|
||||
let config = Config::default();
|
||||
|
||||
let controller = CtrlRogBios {
|
||||
_config: Arc::new(Mutex::new(config)),
|
||||
};
|
||||
|
||||
let res = controller.panel_overdrive();
|
||||
assert_eq!(res, 1);
|
||||
|
||||
// controller.set_panel_od(false).unwrap();
|
||||
// let res = controller.panel_overdrive();
|
||||
// assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,11 @@ use zbus::Connection;
|
||||
use zvariant::Type;
|
||||
|
||||
use crate::{
|
||||
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_charge::CtrlCharge,
|
||||
ctrl_profiles::controller::CtrlPlatformProfile, ctrl_rog_bios::CtrlRogBios, GetSupported,
|
||||
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_platform::CtrlRogBios,
|
||||
ctrl_power::CtrlPower, ctrl_profiles::controller::CtrlPlatformProfile, GetSupported,
|
||||
};
|
||||
|
||||
use rog_supported::{
|
||||
AnimeSupportedFunctions, ChargeSupportedFunctions, LedSupportedFunctions,
|
||||
PlatformProfileFunctions, RogBiosSupportedFunctions,
|
||||
};
|
||||
use rog_platform::supported::*;
|
||||
|
||||
#[derive(Serialize, Deserialize, Type)]
|
||||
pub struct SupportedFunctions {
|
||||
@@ -44,7 +41,7 @@ impl GetSupported for SupportedFunctions {
|
||||
SupportedFunctions {
|
||||
anime_ctrl: CtrlAnime::get_supported(),
|
||||
keyboard_led: CtrlKbdLed::get_supported(),
|
||||
charge_ctrl: CtrlCharge::get_supported(),
|
||||
charge_ctrl: CtrlPower::get_supported(),
|
||||
platform_profile: CtrlPlatformProfile::get_supported(),
|
||||
rog_bios_ctrl: CtrlRogBios::get_supported(),
|
||||
}
|
||||
|
||||
+10
-7
@@ -16,9 +16,9 @@ use daemon::ctrl_aura::config::AuraConfig;
|
||||
use daemon::ctrl_aura::controller::{
|
||||
CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus,
|
||||
};
|
||||
use daemon::ctrl_charge::CtrlCharge;
|
||||
use daemon::ctrl_platform::CtrlRogBios;
|
||||
use daemon::ctrl_power::CtrlPower;
|
||||
use daemon::ctrl_profiles::config::ProfileConfig;
|
||||
use daemon::ctrl_rog_bios::CtrlRogBios;
|
||||
use daemon::{
|
||||
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
||||
};
|
||||
@@ -59,7 +59,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!(" rog-aura v{}", rog_aura::VERSION);
|
||||
info!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||
info!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||
info!("rog-supported v{}", rog_supported::VERSION);
|
||||
info!("rog-platform v{}", rog_platform::VERSION);
|
||||
|
||||
let mut executor = Executor::new();
|
||||
|
||||
@@ -85,24 +85,27 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
|
||||
Ok(mut ctrl) => {
|
||||
// Do a reload of any settings
|
||||
ctrl.reload()
|
||||
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
|
||||
.unwrap_or_else(|err| warn!("CtrlRogBios: {}", err));
|
||||
// Then register to dbus server
|
||||
ctrl.add_to_server(&mut connection).await;
|
||||
|
||||
let task = CtrlRogBios::new(config.clone())?;
|
||||
task.create_tasks(executor).await.ok();
|
||||
}
|
||||
Err(err) => {
|
||||
error!("rog_bios_control: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
match CtrlCharge::new(config.clone()) {
|
||||
match CtrlPower::new(config.clone()) {
|
||||
Ok(mut ctrl) => {
|
||||
// Do a reload of any settings
|
||||
ctrl.reload()
|
||||
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
|
||||
.unwrap_or_else(|err| warn!("CtrlPower: {}", err));
|
||||
// Then register to dbus server
|
||||
ctrl.add_to_server(&mut connection).await;
|
||||
|
||||
let task = CtrlCharge::new(config)?;
|
||||
let task = CtrlPower::new(config)?;
|
||||
task.create_tasks(executor).await.ok();
|
||||
}
|
||||
Err(err) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use rog_anime::error::AnimeError;
|
||||
use rog_platform::error::PlatformError;
|
||||
use rog_profiles::error::ProfileError;
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
@@ -28,6 +29,7 @@ pub enum RogError {
|
||||
NoAuraKeyboard,
|
||||
NoAuraNode,
|
||||
Anime(AnimeError),
|
||||
Platform(PlatformError),
|
||||
}
|
||||
|
||||
impl fmt::Display for RogError {
|
||||
@@ -57,6 +59,7 @@ impl fmt::Display for RogError {
|
||||
RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
|
||||
RogError::NoAuraNode => write!(f, "No Aura keyboard node found"),
|
||||
RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets),
|
||||
RogError::Platform(deets) => write!(f, "Asus Platform error: {}", deets),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,6 +78,12 @@ impl From<AnimeError> for RogError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PlatformError> for RogError {
|
||||
fn from(err: PlatformError) -> Self {
|
||||
RogError::Platform(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<zbus::Error> for RogError {
|
||||
fn from(err: zbus::Error) -> Self {
|
||||
RogError::Zbus(err)
|
||||
|
||||
+65
-16
@@ -5,22 +5,12 @@ pub mod config;
|
||||
pub mod ctrl_anime;
|
||||
/// Keyboard LED brightness control, RGB, and LED display modes
|
||||
pub mod ctrl_aura;
|
||||
/// Control of battery charge level
|
||||
pub mod ctrl_charge;
|
||||
/// Control CPU min/max freq and turbo, fan mode, fan curves
|
||||
///
|
||||
/// Intel machines can control:
|
||||
/// - CPU min/max frequency
|
||||
/// - CPU turbo enable/disable
|
||||
/// - Fan mode (normal, boost, silent)
|
||||
///
|
||||
/// AMD machines can control:
|
||||
/// - CPU turbo enable/disable
|
||||
/// - Fan mode (normal, boost, silent)
|
||||
/// - Fan min/max RPM curve
|
||||
pub mod ctrl_profiles;
|
||||
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
|
||||
pub mod ctrl_rog_bios;
|
||||
pub mod ctrl_platform;
|
||||
/// Control of battery charge level
|
||||
pub mod ctrl_power;
|
||||
/// Control platform profiles + fan-curves if available
|
||||
pub mod ctrl_profiles;
|
||||
/// Laptop matching to determine capabilities
|
||||
pub mod laptops;
|
||||
|
||||
@@ -35,11 +25,12 @@ use crate::error::RogError;
|
||||
use async_trait::async_trait;
|
||||
use config::Config;
|
||||
use log::warn;
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use smol::{stream::StreamExt, Executor, Timer};
|
||||
use zbus::Connection;
|
||||
use zvariant::ObjectPath;
|
||||
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub trait Reloadable {
|
||||
fn reload(&mut self) -> Result<(), RogError>;
|
||||
@@ -87,6 +78,64 @@ pub trait CtrlTask {
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
/// Free helper method to create tasks to run on: sleep, wake, shutdown, boot
|
||||
async fn create_sys_event_tasks(
|
||||
&self,
|
||||
executor: &mut Executor,
|
||||
mut on_sleep: impl FnMut() + Send + 'static,
|
||||
mut on_wake: impl FnMut() + Send + 'static,
|
||||
mut on_shutdown: impl FnMut() + Send + 'static,
|
||||
mut on_boot: impl FnMut() + Send + 'static,
|
||||
) {
|
||||
let connection = Connection::system()
|
||||
.await
|
||||
.expect("Controller could not create dbus connection");
|
||||
|
||||
let manager = ManagerProxy::new(&connection)
|
||||
.await
|
||||
.expect("Controller could not create ManagerProxy");
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
||||
notif
|
||||
.for_each(|event| {
|
||||
if let Ok(args) = event.args() {
|
||||
if args.start {
|
||||
on_sleep();
|
||||
} else if !args.start() {
|
||||
on_wake();
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let manager = ManagerProxy::new(&connection)
|
||||
.await
|
||||
.expect("Controller could not create ManagerProxy");
|
||||
|
||||
executor
|
||||
.spawn(async move {
|
||||
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
||||
notif
|
||||
.for_each(|event| {
|
||||
if let Ok(args) = event.args() {
|
||||
if args.start {
|
||||
on_shutdown();
|
||||
} else if !args.start() {
|
||||
on_boot();
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CtrlTaskComplex {
|
||||
|
||||
@@ -64,7 +64,7 @@ per_key = false
|
||||
|
||||
[[led_data]]
|
||||
prod_family = "ROG Strix"
|
||||
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM", "G713IC"]
|
||||
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM", "G713IC", "G713RS"]
|
||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
|
||||
multizone = []
|
||||
per_key = true
|
||||
@@ -177,7 +177,7 @@ per_key = false
|
||||
|
||||
[[led_data]]
|
||||
prod_family = "ROG Strix"
|
||||
board_names = ["G513IC"]
|
||||
board_names = ["G513IC", "G513RC"]
|
||||
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
|
||||
multizone = []
|
||||
per_key = false
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@@ -15,7 +15,7 @@ exclude = ["data"]
|
||||
[features]
|
||||
default = ["dbus", "detect"]
|
||||
dbus = ["zvariant", "zbus"]
|
||||
detect = ["udev", "sysfs-class"]
|
||||
detect = ["sysfs-class"]
|
||||
|
||||
[dependencies]
|
||||
png_pong = "^0.8.0"
|
||||
@@ -31,5 +31,4 @@ glam = { version = "^0.21.2", features = ["serde"] }
|
||||
zvariant = { version = "^3.0", optional = true }
|
||||
zbus = { version = "^2.2", optional = true }
|
||||
|
||||
udev = { version = "^0.6", optional = true }
|
||||
sysfs-class = { version = "^0.1", optional = true }
|
||||
@@ -32,4 +32,4 @@ pub mod error;
|
||||
/// Provides const methods to create the USB HID control packets
|
||||
pub mod usb;
|
||||
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
@@ -36,30 +36,6 @@ pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
|
||||
Err(AnimeError::UnsupportedDevice)
|
||||
}
|
||||
|
||||
/// Find the USB device node - known devices so far: `19b6`
|
||||
#[inline]
|
||||
pub fn find_node(id_product: &str) -> Result<String, AnimeError> {
|
||||
let mut enumerator =
|
||||
udev::Enumerator::new().map_err(|err| AnimeError::Udev("enumerator failed".into(), err))?;
|
||||
enumerator
|
||||
.match_subsystem("usb")
|
||||
.map_err(|err| AnimeError::Udev("match_subsystem failed".into(), err))?;
|
||||
|
||||
for device in enumerator
|
||||
.scan_devices()
|
||||
.map_err(|err| AnimeError::Udev("scan_devices failed".into(), err))?
|
||||
{
|
||||
if let Some(attr) = device.attribute_value("idProduct") {
|
||||
if attr == id_product {
|
||||
if let Some(dev_node) = device.devnode() {
|
||||
return Ok(dev_node.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(AnimeError::NoDevice)
|
||||
}
|
||||
|
||||
/// Get the two device initialization packets. These are required for device start
|
||||
/// after the laptop boots.
|
||||
#[inline]
|
||||
|
||||
+5
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rog_aura"
|
||||
version = "1.2.2"
|
||||
version = "1.3.3"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
@@ -20,4 +20,7 @@ dbus = ["zvariant"]
|
||||
serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
toml = { version = "^0.5", optional = true }
|
||||
zvariant = { version = "^3.0", optional = true }
|
||||
zvariant = { version = "^3.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "^1.0"
|
||||
@@ -20,13 +20,6 @@ pub enum LedBrightness {
|
||||
High,
|
||||
}
|
||||
|
||||
impl LedBrightness {
|
||||
pub fn as_char_code(&self) -> u8 {
|
||||
std::char::from_digit(*self as u32, 10)
|
||||
.expect("LedBrightness.as_char_code failed to convert") as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for LedBrightness {
|
||||
fn from(bright: u32) -> Self {
|
||||
match bright {
|
||||
@@ -40,7 +33,7 @@ impl From<u32> for LedBrightness {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Debug, Clone, PartialEq, Copy, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Copy, Deserialize, Serialize)]
|
||||
pub struct Colour(pub u8, pub u8, pub u8);
|
||||
|
||||
impl Default for Colour {
|
||||
@@ -117,6 +110,15 @@ impl FromStr for Speed {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Speed> for u8 {
|
||||
fn from(s: Speed) -> u8 {
|
||||
match s {
|
||||
Speed::Low => 0,
|
||||
Speed::Med => 1,
|
||||
Speed::High => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Used for Rainbow mode.
|
||||
///
|
||||
/// Enum corresponds to the required integer value
|
||||
@@ -249,9 +251,10 @@ impl From<u8> for AuraModeNum {
|
||||
|
||||
/// Base effects have no zoning, while multizone is 1-4
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub enum AuraZone {
|
||||
/// Used if keyboard has no zones, or if setting all
|
||||
#[default]
|
||||
None,
|
||||
/// Leftmost zone
|
||||
Key1,
|
||||
@@ -269,12 +272,6 @@ pub enum AuraZone {
|
||||
BarRight,
|
||||
}
|
||||
|
||||
impl Default for AuraZone {
|
||||
fn default() -> Self {
|
||||
AuraZone::None
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for AuraZone {
|
||||
type Err = Error;
|
||||
|
||||
@@ -435,6 +432,25 @@ impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&AuraEffect> for Vec<u8> {
|
||||
fn from(aura: &AuraEffect) -> Self {
|
||||
let mut msg = vec![0u8; LED_MSG_LEN];
|
||||
msg[0] = 0x5d;
|
||||
msg[1] = 0xb3;
|
||||
msg[2] = aura.zone as u8;
|
||||
msg[3] = aura.mode as u8;
|
||||
msg[4] = aura.colour1.0;
|
||||
msg[5] = aura.colour1.1;
|
||||
msg[6] = aura.colour1.2;
|
||||
msg[7] = aura.speed as u8;
|
||||
msg[8] = aura.direction as u8;
|
||||
msg[10] = aura.colour2.0;
|
||||
msg[11] = aura.colour2.1;
|
||||
msg[12] = aura.colour2.2;
|
||||
msg
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, LED_MSG_LEN};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Default, PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum Key {
|
||||
VolUp,
|
||||
VolDown,
|
||||
MicMute,
|
||||
#[default]
|
||||
Rog,
|
||||
Fan,
|
||||
Esc,
|
||||
@@ -151,6 +152,13 @@ pub enum Key {
|
||||
RowEndSpacer,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn is_placeholder(&self) -> bool {
|
||||
let shape = KeyShape::from(self);
|
||||
shape.is_blank() || shape.is_spacer()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! A series of pre-defined layouts. These were mostly used to generate an editable
|
||||
//! config.
|
||||
|
||||
/// Hardcoded layout. Was used to generate a toml default
|
||||
pub mod g513;
|
||||
/// Hardcoded layout. Was used to generate a toml default
|
||||
@@ -7,9 +10,14 @@ pub mod gx502;
|
||||
|
||||
use crate::{error::Error, keys::Key};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs::OpenOptions, io::Read, path::Path, slice::Iter};
|
||||
use std::{
|
||||
fs::{self, OpenOptions},
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
slice::Iter,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
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
|
||||
@@ -46,9 +54,33 @@ impl KeyLayout {
|
||||
pub fn rows(&self) -> Iter<KeyRow> {
|
||||
self.rows.iter()
|
||||
}
|
||||
|
||||
pub fn rows_ref(&self) -> &[KeyRow] {
|
||||
&self.rows
|
||||
}
|
||||
|
||||
/// Find a layout matching the provided board name in the provided dir
|
||||
pub fn find_layout(board_name: &str, mut data_path: PathBuf) -> Result<Self, Error> {
|
||||
let mut layout = KeyLayout::ga401_layout(); // default
|
||||
|
||||
data_path.push("layouts");
|
||||
let path = data_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) {
|
||||
layout = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(layout)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct KeyRow {
|
||||
height: f32,
|
||||
row: Vec<Key>,
|
||||
@@ -63,6 +95,10 @@ impl KeyRow {
|
||||
self.row.iter()
|
||||
}
|
||||
|
||||
pub fn row_ref(&self) -> &[Key] {
|
||||
&self.row
|
||||
}
|
||||
|
||||
pub fn height(&self) -> f32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct GX502Layout(Vec<[Key; 17]>);
|
||||
|
||||
impl KeyLayout for GX502Layout {
|
||||
fn get_rows(&self) -> &Vec<[Key; 17]> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GX502Layout {
|
||||
fn default() -> Self {
|
||||
GX502Layout(vec![
|
||||
[
|
||||
Key::NormalSpacer,
|
||||
Key::FuncSpacer,
|
||||
Key::VolDown,
|
||||
Key::VolUp,
|
||||
Key::MicMute,
|
||||
Key::Rog,
|
||||
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::NormalBlank,
|
||||
Key::F1,
|
||||
Key::F2,
|
||||
Key::F3,
|
||||
Key::F4,
|
||||
Key::NormalBlank, // not sure which key to put here
|
||||
Key::F5,
|
||||
Key::F6,
|
||||
Key::F7,
|
||||
Key::F8,
|
||||
Key::NormalBlank,
|
||||
Key::F9,
|
||||
Key::F10,
|
||||
Key::F11,
|
||||
Key::F12,
|
||||
Key::Del,
|
||||
],
|
||||
[
|
||||
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::Home,
|
||||
],
|
||||
[
|
||||
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::BackSlash,
|
||||
Key::BackSlash,
|
||||
Key::PgUp,
|
||||
],
|
||||
[
|
||||
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::Quote,
|
||||
Key::Return3_1,
|
||||
Key::Return3_2,
|
||||
Key::Return3_3,
|
||||
Key::PgDn,
|
||||
],
|
||||
[
|
||||
Key::LShift,
|
||||
Key::LShift,
|
||||
Key::Z,
|
||||
Key::X,
|
||||
Key::C,
|
||||
Key::V,
|
||||
Key::B,
|
||||
Key::N,
|
||||
Key::M,
|
||||
Key::Comma,
|
||||
Key::Period,
|
||||
Key::FwdSlash,
|
||||
Key::FwdSlash,
|
||||
Key::Rshift3_1,
|
||||
Key::Rshift3_2,
|
||||
Key::Rshift3_3,
|
||||
Key::End,
|
||||
],
|
||||
[
|
||||
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::RCtrl,
|
||||
Key::Left,
|
||||
Key::Up,
|
||||
Key::Right,
|
||||
Key::RFn,
|
||||
],
|
||||
[
|
||||
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::NormalBlank,
|
||||
],
|
||||
])
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -8,6 +8,10 @@ pub use builtin_modes::*;
|
||||
|
||||
mod per_key_rgb;
|
||||
pub use per_key_rgb::*;
|
||||
|
||||
mod per_zone;
|
||||
pub use per_zone::*;
|
||||
|
||||
pub mod error;
|
||||
pub mod key_to_str;
|
||||
pub mod keys;
|
||||
@@ -16,7 +20,7 @@ pub mod layouts;
|
||||
pub mod usb;
|
||||
|
||||
pub const LED_MSG_LEN: usize = 17;
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub const RED: Colour = Colour(0xff, 0x00, 0x00);
|
||||
pub const GREEN: Colour = Colour(0x00, 0xff, 0x00);
|
||||
|
||||
+28
-174
@@ -1,4 +1,10 @@
|
||||
use crate::keys::Key;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
#[cfg(feature = "dbus")]
|
||||
use zvariant::Type;
|
||||
|
||||
/// Represents the per-key raw USB packets
|
||||
pub type PerKeyRaw = Vec<Vec<u8>>;
|
||||
|
||||
/// A `KeyColourArray` contains all data to change the full set of keyboard
|
||||
/// key colours individually.
|
||||
@@ -7,16 +13,20 @@ use crate::keys::Key;
|
||||
/// to the keyboard EC. One row controls one group of keys, these keys are not
|
||||
/// necessarily all on the same row of the keyboard, with some splitting between
|
||||
/// two rows.
|
||||
#[derive(Clone)]
|
||||
pub struct KeyColourArray([[u8; 64]; 11]);
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct KeyColourArray(PerKeyRaw);
|
||||
|
||||
impl Default for KeyColourArray {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyColourArray {
|
||||
pub fn new() -> Self {
|
||||
let mut set = [[0u8; 64]; 11];
|
||||
let mut set = vec![vec![0u8; 64]; 11];
|
||||
// set[0].copy_from_slice(&KeyColourArray::get_init_msg());
|
||||
for (count, row) in set.iter_mut().enumerate() {
|
||||
row[0] = 0x5d; // Report ID
|
||||
row[1] = 0xbc; // Mode = custom??, 0xb3 is builtin
|
||||
@@ -47,7 +57,7 @@ impl KeyColourArray {
|
||||
|
||||
#[inline]
|
||||
pub fn set(&mut self, key: Key, r: u8, g: u8, b: u8) {
|
||||
if let Some(c) = self.rgb(key) {
|
||||
if let Some(c) = self.rgb_for_key(key) {
|
||||
c[0] = r;
|
||||
c[1] = g;
|
||||
c[2] = b;
|
||||
@@ -56,7 +66,7 @@ impl KeyColourArray {
|
||||
|
||||
/// 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 rgb(&mut self, key: Key) -> Option<&mut [u8]> {
|
||||
pub fn rgb_for_key(&mut self, key: Key) -> Option<&mut [u8]> {
|
||||
// Tuples are indexes in to array
|
||||
let (row, col) = match key {
|
||||
Key::VolDown => (0, 15),
|
||||
@@ -211,183 +221,27 @@ impl KeyColourArray {
|
||||
Key::Fan | Key::Space | Key::BkSpc => return None,
|
||||
};
|
||||
|
||||
Some(&mut self.0[row][col..2])
|
||||
Some(&mut self.0[row][col..=col + 2])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> &[[u8; 64]; 11] {
|
||||
pub fn get(&self) -> PerKeyRaw {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> &PerKeyRaw {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub trait KeyLayout {
|
||||
fn get_rows(&self) -> &Vec<[Key; 17]>;
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct GX502Layout(Vec<[Key; 17]>);
|
||||
|
||||
impl KeyLayout for GX502Layout {
|
||||
fn get_rows(&self) -> &Vec<[Key; 17]> {
|
||||
&self.0
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> &mut PerKeyRaw {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GX502Layout {
|
||||
fn default() -> Self {
|
||||
GX502Layout(vec![
|
||||
[
|
||||
Key::NormalSpacer,
|
||||
Key::FuncSpacer,
|
||||
Key::VolDown,
|
||||
Key::VolUp,
|
||||
Key::MicMute,
|
||||
Key::Rog,
|
||||
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::NormalBlank,
|
||||
Key::F1,
|
||||
Key::F2,
|
||||
Key::F3,
|
||||
Key::F4,
|
||||
Key::NormalBlank, // not sure which key to put here
|
||||
Key::F5,
|
||||
Key::F6,
|
||||
Key::F7,
|
||||
Key::F8,
|
||||
Key::NormalBlank,
|
||||
Key::F9,
|
||||
Key::F10,
|
||||
Key::F11,
|
||||
Key::F12,
|
||||
Key::Del,
|
||||
],
|
||||
[
|
||||
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::Home,
|
||||
],
|
||||
[
|
||||
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::BackSlash,
|
||||
Key::BackSlash,
|
||||
Key::PgUp,
|
||||
],
|
||||
[
|
||||
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::Quote,
|
||||
Key::Return3_1,
|
||||
Key::Return3_2,
|
||||
Key::Return3_3,
|
||||
Key::PgDn,
|
||||
],
|
||||
[
|
||||
Key::LShift,
|
||||
Key::LShift,
|
||||
Key::Z,
|
||||
Key::X,
|
||||
Key::C,
|
||||
Key::V,
|
||||
Key::B,
|
||||
Key::N,
|
||||
Key::M,
|
||||
Key::Comma,
|
||||
Key::Period,
|
||||
Key::FwdSlash,
|
||||
Key::FwdSlash,
|
||||
Key::Rshift3_1,
|
||||
Key::Rshift3_2,
|
||||
Key::Rshift3_3,
|
||||
Key::End,
|
||||
],
|
||||
[
|
||||
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::RCtrl,
|
||||
Key::Left,
|
||||
Key::Up,
|
||||
Key::Right,
|
||||
Key::RFn,
|
||||
],
|
||||
[
|
||||
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::NormalBlank,
|
||||
],
|
||||
])
|
||||
impl From<KeyColourArray> for PerKeyRaw {
|
||||
fn from(k: KeyColourArray) -> Self {
|
||||
k.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
#[cfg(feature = "dbus")]
|
||||
use zvariant::Type;
|
||||
|
||||
/// Represents the zoned raw USB packets
|
||||
pub type ZonedRaw = Vec<u8>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
|
||||
pub enum PerZone {
|
||||
None,
|
||||
KeyboardLeft,
|
||||
KeyboardCenterLeft,
|
||||
KeyboardCenterRight,
|
||||
KeyboardRight,
|
||||
LightbarRight,
|
||||
LightbarRightCorner,
|
||||
LightbarRightBottom,
|
||||
LightbarLeftBottom,
|
||||
LightbarLeftCorner,
|
||||
LightbarLeft,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct ZonedColourArray(ZonedRaw);
|
||||
|
||||
impl Default for ZonedColourArray {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ZonedColourArray {
|
||||
pub fn new() -> Self {
|
||||
let mut pkt = vec![0u8; 64];
|
||||
pkt[0] = 0x5d; // Report ID
|
||||
pkt[1] = 0xbc; // Mode = custom??, 0xb3 is builtin
|
||||
pkt[2] = 0x01;
|
||||
pkt[3] = 0x01; // ??
|
||||
pkt[4] = 0x04; // ??, 4,5,6 are normally RGB for builtin mode colours
|
||||
ZonedColourArray(pkt)
|
||||
}
|
||||
|
||||
pub fn rgb_for_zone(&mut self, zone: PerZone) -> &mut [u8] {
|
||||
match zone {
|
||||
PerZone::None => &mut self.0[9..=11],
|
||||
PerZone::KeyboardLeft => &mut self.0[9..=11],
|
||||
PerZone::KeyboardCenterLeft => &mut self.0[12..=14],
|
||||
PerZone::KeyboardCenterRight => &mut self.0[15..=17],
|
||||
PerZone::KeyboardRight => &mut self.0[18..=20],
|
||||
// Two sections missing here?
|
||||
PerZone::LightbarRight => &mut self.0[27..=29],
|
||||
PerZone::LightbarRightCorner => &mut self.0[30..=32],
|
||||
PerZone::LightbarRightBottom => &mut self.0[33..=35],
|
||||
PerZone::LightbarLeftBottom => &mut self.0[36..=38],
|
||||
PerZone::LightbarLeftCorner => &mut self.0[39..=41],
|
||||
PerZone::LightbarLeft => &mut self.0[42..=44],
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> ZonedRaw {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> &ZonedRaw {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> &mut ZonedRaw {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ZonedColourArray> for ZonedRaw {
|
||||
fn from(k: ZonedColourArray) -> Self {
|
||||
k.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{PerZone, ZonedColourArray, ZonedRaw};
|
||||
|
||||
macro_rules! colour_check {
|
||||
($zone:expr, $pkt_idx_start:expr) => {
|
||||
let mut zone = ZonedColourArray::new();
|
||||
let c = zone.rgb_for_zone($zone);
|
||||
c[0] = 255;
|
||||
c[1] = 255;
|
||||
c[2] = 255;
|
||||
|
||||
let pkt: ZonedRaw = zone.get();
|
||||
assert_eq!(pkt[$pkt_idx_start], 0xff);
|
||||
assert_eq!(pkt[$pkt_idx_start + 1], 0xff);
|
||||
assert_eq!(pkt[$pkt_idx_start + 2], 0xff);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zone_to_packet_check() {
|
||||
let zone = ZonedColourArray::new();
|
||||
let pkt: ZonedRaw = zone.into();
|
||||
assert_eq!(pkt[0], 0x5d);
|
||||
assert_eq!(pkt[1], 0xbc);
|
||||
assert_eq!(pkt[2], 0x01);
|
||||
assert_eq!(pkt[3], 0x01);
|
||||
assert_eq!(pkt[4], 0x04);
|
||||
|
||||
colour_check!(PerZone::KeyboardLeft, 9);
|
||||
colour_check!(PerZone::KeyboardCenterLeft, 12);
|
||||
colour_check!(PerZone::KeyboardCenterRight, 15);
|
||||
colour_check!(PerZone::KeyboardRight, 18);
|
||||
|
||||
colour_check!(PerZone::LightbarRight, 27);
|
||||
colour_check!(PerZone::LightbarRightCorner, 30);
|
||||
colour_check!(PerZone::LightbarRightBottom, 33);
|
||||
colour_check!(PerZone::LightbarLeftBottom, 36);
|
||||
colour_check!(PerZone::LightbarLeftCorner, 39);
|
||||
colour_check!(PerZone::LightbarLeft, 42);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
/// All the possible AniMe actions that can be used. The enum is intended to be
|
||||
/// used in a array allowing the user to cycle through a series of actions.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum ActionData {
|
||||
Static,
|
||||
}
|
||||
|
||||
/// An optimised precomputed set of actions that the user can cycle through
|
||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||
pub struct Sequences(Vec<ActionData>);
|
||||
|
||||
impl Sequences {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
/// Use a base `AnimeAction` to generate the precomputed data and insert in to
|
||||
/// the run buffer
|
||||
#[inline]
|
||||
pub fn insert(&mut self, _index: usize) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove an item at this position from the run buffer. If the `index` supplied
|
||||
/// is not in range then `None` is returned, otherwise the `ActionData` at that location
|
||||
/// is yeeted and returned.
|
||||
#[inline]
|
||||
pub fn remove_item(&mut self, index: usize) -> Option<ActionData> {
|
||||
if index < self.0.len() {
|
||||
return Some(self.0.remove(index));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> ActionIterator {
|
||||
ActionIterator {
|
||||
actions: self,
|
||||
next_idx: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iteractor helper for iterating over all the actions in `Sequences`
|
||||
pub struct ActionIterator<'a> {
|
||||
actions: &'a Sequences,
|
||||
next_idx: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ActionIterator<'a> {
|
||||
type Item = &'a ActionData;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a ActionData> {
|
||||
if self.next_idx == self.actions.0.len() {
|
||||
self.next_idx = 0;
|
||||
return None;
|
||||
}
|
||||
|
||||
let current = self.next_idx;
|
||||
self.next_idx += 1;
|
||||
|
||||
Some(&self.actions.0[current])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
use crate::{layouts::KeyLayout, p_random, Colour, EffectState, LedType, Speed};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
macro_rules! effect_state_impl {
|
||||
() => {
|
||||
fn get_colour(&self) -> Colour {
|
||||
self.colour
|
||||
}
|
||||
|
||||
fn get_led_type(&self) -> LedType {
|
||||
self.led_type.clone()
|
||||
}
|
||||
|
||||
/// Change the led type
|
||||
fn set_led_type(&mut self, led_type: LedType) {
|
||||
self.led_type = led_type;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! effect_impl {
|
||||
($($effect:ident),*) => {
|
||||
impl Effect {
|
||||
/// Get the type of LED set
|
||||
pub fn get_led_type(&self) -> LedType {
|
||||
match self {
|
||||
$(Effect::$effect(c) => c.get_led_type(),)*
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the led type
|
||||
pub fn set_led_type(&mut self, led_type: LedType) {
|
||||
match self {
|
||||
$(Effect::$effect(c) => c.set_led_type(led_type),)*
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the next state of the effect
|
||||
pub fn next_state(&mut self, layout: &KeyLayout) {
|
||||
match self {
|
||||
$(Effect::$effect(c) => c.next_colour_state(layout),)*
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the calculated colour
|
||||
pub fn get_colour(&self) -> Colour {
|
||||
match self {
|
||||
$(Effect::$effect(c) => c.get_colour(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum Effect {
|
||||
Static(Static),
|
||||
Breathe(Breathe),
|
||||
Flicker(Flicker),
|
||||
}
|
||||
|
||||
impl Default for Effect {
|
||||
fn default() -> Self {
|
||||
Self::Static(Static::new(LedType::default(), Colour::default()))
|
||||
}
|
||||
}
|
||||
|
||||
effect_impl!(Static, Breathe, Flicker);
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Static {
|
||||
led_type: LedType,
|
||||
/// The starting colour
|
||||
colour: Colour,
|
||||
}
|
||||
|
||||
impl Static {
|
||||
pub fn new(led_type: LedType, colour: Colour) -> Self {
|
||||
Self { led_type, colour }
|
||||
}
|
||||
}
|
||||
|
||||
impl EffectState for Static {
|
||||
fn next_colour_state(&mut self, _layout: &KeyLayout) {}
|
||||
|
||||
effect_state_impl!();
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Breathe {
|
||||
led_type: LedType,
|
||||
/// The starting colour
|
||||
start_colour1: Colour,
|
||||
/// The secondary starting colour
|
||||
start_colour2: Colour,
|
||||
/// The speed at which to cycle between the colours
|
||||
speed: Speed,
|
||||
/// Temporary data to help keep state
|
||||
#[serde(skip)]
|
||||
colour: Colour,
|
||||
#[serde(skip)]
|
||||
count_flipped: bool,
|
||||
#[serde(skip)]
|
||||
use_colour1: bool,
|
||||
}
|
||||
|
||||
impl Breathe {
|
||||
pub fn new(led_type: LedType, colour1: Colour, colour2: Colour, speed: Speed) -> Self {
|
||||
Self {
|
||||
led_type,
|
||||
start_colour1: colour1,
|
||||
start_colour2: colour2,
|
||||
speed,
|
||||
colour: colour1,
|
||||
count_flipped: false,
|
||||
use_colour1: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EffectState for Breathe {
|
||||
fn next_colour_state(&mut self, _layout: &KeyLayout) {
|
||||
let Self {
|
||||
start_colour1: colour1,
|
||||
start_colour2: colour2,
|
||||
speed,
|
||||
colour: colour_actual,
|
||||
count_flipped: flipped,
|
||||
use_colour1,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let speed = 4 - <u8>::from(*speed);
|
||||
|
||||
let colour: &mut Colour;
|
||||
if *colour_actual == Colour(0, 0, 0) {
|
||||
*use_colour1 = !*use_colour1;
|
||||
}
|
||||
|
||||
if !*use_colour1 {
|
||||
colour = colour2;
|
||||
} else {
|
||||
colour = colour1;
|
||||
}
|
||||
|
||||
let r1_scale = colour.0 / speed / 2;
|
||||
let g1_scale = colour.1 / speed / 2;
|
||||
let b1_scale = colour.2 / speed / 2;
|
||||
|
||||
if *colour_actual == Colour(0, 0, 0) {
|
||||
*flipped = true;
|
||||
} else if colour_actual >= colour {
|
||||
*flipped = false;
|
||||
}
|
||||
|
||||
if !*flipped {
|
||||
colour_actual.0 = colour_actual.0.saturating_sub(r1_scale);
|
||||
colour_actual.1 = colour_actual.1.saturating_sub(g1_scale);
|
||||
colour_actual.2 = colour_actual.2.saturating_sub(b1_scale);
|
||||
} else {
|
||||
colour_actual.0 = colour_actual.0.saturating_add(r1_scale);
|
||||
colour_actual.1 = colour_actual.1.saturating_add(g1_scale);
|
||||
colour_actual.2 = colour_actual.2.saturating_add(b1_scale);
|
||||
}
|
||||
}
|
||||
|
||||
effect_state_impl!();
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Flicker {
|
||||
led_type: LedType,
|
||||
start_colour: Colour,
|
||||
max_percentage: u8,
|
||||
min_percentage: u8,
|
||||
#[serde(skip)]
|
||||
count: u8,
|
||||
#[serde(skip)]
|
||||
colour: Colour,
|
||||
}
|
||||
|
||||
impl Flicker {
|
||||
pub fn new(led_type: LedType, colour: Colour, max_percentage: u8, min_percentage: u8) -> Self {
|
||||
Self {
|
||||
led_type,
|
||||
colour,
|
||||
count: 4,
|
||||
max_percentage,
|
||||
min_percentage,
|
||||
start_colour: colour,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EffectState for Flicker {
|
||||
fn next_colour_state(&mut self, _layout: &KeyLayout) {
|
||||
let Self {
|
||||
max_percentage,
|
||||
min_percentage,
|
||||
colour,
|
||||
start_colour,
|
||||
..
|
||||
} = self;
|
||||
|
||||
if self.count == 0 {
|
||||
self.count = 4;
|
||||
}
|
||||
self.count -= 1;
|
||||
if self.count != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: make a "percentage" method on Colour.
|
||||
let max_light = Colour(
|
||||
(start_colour.0 as f32 / 100.0 * *max_percentage as f32) as u8,
|
||||
(start_colour.1 as f32 / 100.0 * *max_percentage as f32) as u8,
|
||||
(start_colour.2 as f32 / 100.0 * *max_percentage as f32) as u8,
|
||||
);
|
||||
// min light is a percentage of the set colour
|
||||
let min_light = Colour(
|
||||
(start_colour.0 as f32 / 100.0 * *min_percentage as f32) as u8,
|
||||
(start_colour.1 as f32 / 100.0 * *min_percentage as f32) as u8,
|
||||
(start_colour.2 as f32 / 100.0 * *min_percentage as f32) as u8,
|
||||
);
|
||||
|
||||
// Convert the 255 to percentage
|
||||
let amount = (p_random() & 7) as f32 * 8.0;
|
||||
|
||||
let set_colour = |colour: &mut u8, max: f32, min: f32| {
|
||||
let pc = amount / max * 100.0;
|
||||
let min_amount = pc * min / 100.0; // percentage of min colour
|
||||
let max_amount = pc * max / 100.0; // percentage of max colour
|
||||
if *colour as f32 - min_amount < min {
|
||||
*colour = min as u8;
|
||||
} else {
|
||||
*colour = (max - max_amount) as u8;
|
||||
}
|
||||
};
|
||||
set_colour(&mut colour.0, max_light.0 as f32, min_light.0 as f32);
|
||||
set_colour(&mut colour.1, max_light.1 as f32, min_light.1 as f32);
|
||||
set_colour(&mut colour.2, max_light.2 as f32, min_light.2 as f32);
|
||||
|
||||
self.count = 4;
|
||||
}
|
||||
|
||||
effect_state_impl!();
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
mod effects;
|
||||
pub use effects::*;
|
||||
|
||||
use crate::{
|
||||
keys::Key, layouts::KeyLayout, Colour, KeyColourArray, PerKeyRaw, PerZone, ZonedColourArray,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
// static mut RNDINDEX: usize = 0;
|
||||
static mut PRNDINDEX: usize = 0;
|
||||
|
||||
/// Pseudo random table ripped straight out of Room4Doom
|
||||
pub const RNDTABLE: [i32; 256] = [
|
||||
0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, 21, 211, 47, 80, 242, 154,
|
||||
27, 205, 128, 161, 89, 77, 36, 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188,
|
||||
52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224, 149, 104, 25, 178, 252, 182,
|
||||
202, 182, 141, 197, 4, 81, 181, 242, 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175,
|
||||
249, 0, 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235, 25, 92, 20, 145, 138,
|
||||
77, 69, 166, 78, 176, 173, 212, 166, 113, 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37,
|
||||
171, 75, 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196, 135, 106, 63, 197, 195,
|
||||
86, 96, 203, 113, 101, 170, 247, 181, 113, 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112,
|
||||
166, 103, 241, 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224, 145, 224, 81,
|
||||
206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253,
|
||||
54, 14, 109, 226, 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17, 46, 52,
|
||||
231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242, 98, 43, 39, 175, 254, 145, 190,
|
||||
84, 118, 222, 187, 136, 120, 163, 236, 249,
|
||||
];
|
||||
|
||||
pub fn p_random() -> i32 {
|
||||
unsafe {
|
||||
PRNDINDEX = (PRNDINDEX + 1) & 0xFF;
|
||||
RNDTABLE[PRNDINDEX]
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait EffectState {
|
||||
/// Calculate the next colour state
|
||||
fn next_colour_state(&mut self, _layout: &KeyLayout);
|
||||
|
||||
/// Return the resulting colour. Implementers should store the colour to return it.
|
||||
fn get_colour(&self) -> Colour;
|
||||
|
||||
fn get_led_type(&self) -> LedType;
|
||||
|
||||
fn set_led_type(&mut self, led_type: LedType);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum LedType {
|
||||
Key(Key),
|
||||
Zone(PerZone),
|
||||
}
|
||||
|
||||
impl Default for LedType {
|
||||
fn default() -> Self {
|
||||
Self::Zone(PerZone::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||
pub struct Sequences(Vec<Effect>);
|
||||
|
||||
impl Sequences {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, action: Effect) {
|
||||
self.0.push(action);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn insert(&mut self, index: usize, action: Effect) {
|
||||
self.0.insert(index, action);
|
||||
}
|
||||
|
||||
/// Remove an item at this position from the run buffer. If the `index` supplied
|
||||
/// is not in range then `None` is returned, otherwise the `ActionData` at that location
|
||||
/// is yeeted and returned.
|
||||
#[inline]
|
||||
pub fn remove_item(&mut self, index: usize) -> Option<Effect> {
|
||||
if index < self.0.len() {
|
||||
return Some(self.0.remove(index));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn next_state(&mut self, layout: &KeyLayout) {
|
||||
for effect in self.0.iter_mut() {
|
||||
effect.next_state(layout);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_packets(&self) -> PerKeyRaw {
|
||||
let mut keys = KeyColourArray::new();
|
||||
let mut zones = ZonedColourArray::new();
|
||||
let mut is_per_key = false;
|
||||
for effect in self.0.iter() {
|
||||
match effect.get_led_type() {
|
||||
LedType::Key(key) => {
|
||||
is_per_key = true;
|
||||
if let Some(rgb) = keys.rgb_for_key(key) {
|
||||
let c = effect.get_colour();
|
||||
rgb[0] = c.0;
|
||||
rgb[1] = c.1;
|
||||
rgb[2] = c.2;
|
||||
}
|
||||
}
|
||||
LedType::Zone(z) => {
|
||||
let rgb = zones.rgb_for_zone(z);
|
||||
let c = effect.get_colour();
|
||||
rgb[0] = c.0;
|
||||
rgb[1] = c.1;
|
||||
rgb[2] = c.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_per_key {
|
||||
keys.into()
|
||||
} else {
|
||||
vec![zones.into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
keys::Key, layouts::KeyLayout, Breathe, Colour, Effect, Flicker, LedType, Sequences, Speed,
|
||||
Static,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn single_key_next_state_then_create() {
|
||||
let layout = KeyLayout::gx502_layout();
|
||||
let mut seq = Sequences::new();
|
||||
seq.0.push(Effect::Static(Static::new(
|
||||
LedType::Key(Key::F),
|
||||
Colour(255, 127, 0),
|
||||
)));
|
||||
|
||||
seq.next_state(&layout);
|
||||
let packets = seq.create_packets();
|
||||
|
||||
assert_eq!(packets[0][0], 0x5d);
|
||||
assert_eq!(packets[5][33], 255);
|
||||
assert_eq!(packets[5][34], 127);
|
||||
assert_eq!(packets[5][35], 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cycle_breathe() {
|
||||
let layout = KeyLayout::gx502_layout();
|
||||
let mut seq = Sequences::new();
|
||||
seq.0.push(Effect::Breathe(Breathe::new(
|
||||
LedType::Key(Key::F),
|
||||
Colour(255, 127, 0),
|
||||
Colour(127, 0, 255),
|
||||
Speed::Med,
|
||||
)));
|
||||
|
||||
let s = serde_json::to_string_pretty(&seq).unwrap();
|
||||
println!("{s}");
|
||||
|
||||
seq.next_state(&layout);
|
||||
let packets = seq.create_packets();
|
||||
|
||||
assert_eq!(packets[0][0], 0x5d);
|
||||
assert_eq!(packets[5][33], 213);
|
||||
assert_eq!(packets[5][34], 106);
|
||||
assert_eq!(packets[5][35], 0);
|
||||
|
||||
// dbg!(&packets[5][33..=35]);
|
||||
|
||||
seq.next_state(&layout);
|
||||
let packets = seq.create_packets();
|
||||
|
||||
assert_eq!(packets[0][0], 0x5d);
|
||||
assert_eq!(packets[5][33], 171);
|
||||
assert_eq!(packets[5][34], 85);
|
||||
assert_eq!(packets[5][35], 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cycle_flicker() {
|
||||
let layout = KeyLayout::gx502_layout();
|
||||
let mut seq = Sequences::new();
|
||||
seq.0.push(Effect::Flicker(Flicker::new(
|
||||
LedType::Key(Key::F),
|
||||
Colour(255, 127, 80),
|
||||
100,
|
||||
10,
|
||||
)));
|
||||
|
||||
seq.next_state(&layout);
|
||||
let packets = seq.create_packets();
|
||||
|
||||
assert_eq!(packets[0][0], 0x5d);
|
||||
assert_eq!(packets[5][33], 255);
|
||||
assert_eq!(packets[5][34], 127);
|
||||
assert_eq!(packets[5][35], 80);
|
||||
|
||||
// The random is deterministic
|
||||
seq.next_state(&layout);
|
||||
seq.next_state(&layout);
|
||||
seq.next_state(&layout);
|
||||
seq.next_state(&layout);
|
||||
seq.next_state(&layout);
|
||||
seq.next_state(&layout);
|
||||
seq.next_state(&layout);
|
||||
|
||||
let packets = seq.create_packets();
|
||||
assert_eq!(packets[5][33], 215);
|
||||
assert_eq!(packets[5][34], 87);
|
||||
assert_eq!(packets[5][35], 40);
|
||||
}
|
||||
}
|
||||
+71
-13
@@ -168,7 +168,7 @@ impl BitAnd<AuraDev1866> for AuraDev1866 {
|
||||
///
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||
#[repr(u16)]
|
||||
#[repr(u32)]
|
||||
pub enum AuraDev19b6 {
|
||||
BootLogo = 1,
|
||||
BootKeyb = 1 << 1,
|
||||
@@ -178,25 +178,33 @@ pub enum AuraDev19b6 {
|
||||
SleepKeyb = 1 << 5,
|
||||
ShutdownLogo = 1 << 6,
|
||||
ShutdownKeyb = 1 << 7,
|
||||
AwakeBar = 1 << 7 + 2,
|
||||
BootBar = 1 << 7 + 3,
|
||||
BootBar = 1 << 7 + 2,
|
||||
AwakeBar = 1 << 7 + 3,
|
||||
SleepBar = 1 << 7 + 4,
|
||||
ShutdownBar = 1 << 7 + 5,
|
||||
BootLid = 1 << 15 + 1,
|
||||
AwakeLid = 1 << 15 + 2,
|
||||
SleepLid = 1 << 15 + 3,
|
||||
ShutdownLid = 1 << 15 + 4,
|
||||
}
|
||||
|
||||
impl From<AuraDev19b6> for u16 {
|
||||
impl From<AuraDev19b6> for u32 {
|
||||
fn from(a: AuraDev19b6) -> Self {
|
||||
a as u16
|
||||
a as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl AuraDev19b6 {
|
||||
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
|
||||
let mut a: u16 = 0;
|
||||
let mut a: u32 = 0;
|
||||
control.iter().for_each(|n| {
|
||||
a |= *n as u16;
|
||||
a |= *n as u32;
|
||||
});
|
||||
[(a & 0xff) as u8, ((a & 0xff00) >> 8) as u8, 0x00]
|
||||
[
|
||||
(a & 0xff) as u8,
|
||||
((a & 0xff00) >> 8) as u8,
|
||||
((a & 0xff0000) >> 16) as u8,
|
||||
]
|
||||
}
|
||||
|
||||
pub const fn dev_id() -> &'static str {
|
||||
@@ -275,7 +283,7 @@ mod tests {
|
||||
AuraDev19b6::ShutdownKeyb,
|
||||
];
|
||||
let bytes = AuraDev19b6::to_bytes(&byte1);
|
||||
println!("{:08b}", bytes[0]);
|
||||
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||
assert_eq!(bytes[0], 0xff);
|
||||
|
||||
//
|
||||
@@ -336,23 +344,73 @@ mod tests {
|
||||
assert_eq!(bytes[0], 0xdf);
|
||||
|
||||
let byte2 = [
|
||||
AuraDev19b6::AwakeBar,
|
||||
AuraDev19b6::BootBar,
|
||||
AuraDev19b6::AwakeBar,
|
||||
AuraDev19b6::SleepBar,
|
||||
AuraDev19b6::ShutdownBar,
|
||||
];
|
||||
let bytes = AuraDev19b6::to_bytes(&byte2);
|
||||
println!("{:08b}", bytes[1]);
|
||||
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||
assert_eq!(bytes[1], 0x1e);
|
||||
|
||||
let byte2 = [
|
||||
AuraDev19b6::AwakeBar,
|
||||
AuraDev19b6::BootBar,
|
||||
AuraDev19b6::AwakeBar,
|
||||
// AuraControl::SleepBar,
|
||||
AuraDev19b6::ShutdownBar,
|
||||
];
|
||||
let bytes = AuraDev19b6::to_bytes(&byte2);
|
||||
println!("{:08b}", bytes[1]);
|
||||
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||
assert_eq!(bytes[1], 0x16);
|
||||
|
||||
let byte3 = [
|
||||
AuraDev19b6::AwakeLid,
|
||||
AuraDev19b6::BootLid,
|
||||
AuraDev19b6::SleepLid,
|
||||
AuraDev19b6::ShutdownLid,
|
||||
];
|
||||
let bytes = AuraDev19b6::to_bytes(&byte3);
|
||||
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||
assert_eq!(bytes[2], 0x0f);
|
||||
|
||||
let byte3 = [
|
||||
//AuraDev19b6::AwakeLid,
|
||||
AuraDev19b6::BootLid,
|
||||
AuraDev19b6::SleepLid,
|
||||
AuraDev19b6::ShutdownLid,
|
||||
];
|
||||
let bytes = AuraDev19b6::to_bytes(&byte3);
|
||||
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||
assert_eq!(bytes[2], 0x0d);
|
||||
|
||||
let byte3 = [
|
||||
AuraDev19b6::AwakeLid,
|
||||
AuraDev19b6::BootLid,
|
||||
// AuraControl::SleepLid,
|
||||
AuraDev19b6::ShutdownLid,
|
||||
];
|
||||
let bytes = AuraDev19b6::to_bytes(&byte3);
|
||||
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||
assert_eq!(bytes[2], 0x0b);
|
||||
|
||||
let byte3 = [
|
||||
AuraDev19b6::AwakeLid,
|
||||
AuraDev19b6::BootLid,
|
||||
AuraDev19b6::SleepLid,
|
||||
//AuraDev19b6::ShutdownLid,
|
||||
];
|
||||
let bytes = AuraDev19b6::to_bytes(&byte3);
|
||||
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||
assert_eq!(bytes[2], 0x07);
|
||||
|
||||
let byte3 = [
|
||||
AuraDev19b6::AwakeLid,
|
||||
//AuraDev19b6::BootLid,
|
||||
AuraDev19b6::SleepLid,
|
||||
//AuraDev19b6::ShutdownLid,
|
||||
];
|
||||
let bytes = AuraDev19b6::to_bytes(&byte3);
|
||||
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
|
||||
assert_eq!(bytes[2], 0x06);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rog-control-center"
|
||||
version = "1.0.1"
|
||||
version = "1.1.1"
|
||||
authors = ["Luke D. Jones <luke@ljones.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -12,10 +12,12 @@ 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"] }
|
||||
|
||||
daemon = { path = "../daemon" }
|
||||
rog_anime = { path = "../rog-anime" }
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
rog_supported = { path = "../rog-supported" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
# supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git" }
|
||||
|
||||
smol = "^1.2"
|
||||
|
||||
@@ -7,5 +7,5 @@ Comment=Make your ASUS ROG Laptop go Brrrrr!
|
||||
Categories=Settings
|
||||
|
||||
Icon=rog-control-center
|
||||
Exec=env WINIT_UNIX_BACKEND=x11 rog-control-center
|
||||
Exec=rog-control-center
|
||||
Terminal=false
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::{
|
||||
f64::consts::PI,
|
||||
io::Write,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU8, Ordering},
|
||||
Arc,
|
||||
@@ -9,21 +8,16 @@ use std::{
|
||||
};
|
||||
|
||||
use egui::{Button, RichText};
|
||||
use rog_supported::SupportedFunctions;
|
||||
use rog_platform::supported::SupportedFunctions;
|
||||
|
||||
use crate::{
|
||||
config::Config, error::Result, get_ipc_file, page_states::PageDataStates, Page,
|
||||
RogDbusClientBlocking, SHOWING_GUI,
|
||||
config::Config, error::Result, page_states::PageDataStates, Page, RogDbusClientBlocking,
|
||||
};
|
||||
|
||||
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>,
|
||||
@@ -40,9 +34,7 @@ pub struct RogApp<'a> {
|
||||
impl<'a> RogApp<'a> {
|
||||
/// Called once before the first frame.
|
||||
pub fn new(
|
||||
start_closed: bool,
|
||||
config: Config,
|
||||
show_gui: Arc<AtomicBool>,
|
||||
states: PageDataStates,
|
||||
_cc: &eframe::CreationContext<'_>,
|
||||
) -> Result<Self> {
|
||||
@@ -99,8 +91,6 @@ impl<'a> RogApp<'a> {
|
||||
supported,
|
||||
states,
|
||||
page: Page::System,
|
||||
begin_show_gui: show_gui,
|
||||
running_in_bg: start_closed,
|
||||
config,
|
||||
asus_dbus: dbus,
|
||||
oscillator1,
|
||||
@@ -113,20 +103,10 @@ impl<'a> RogApp<'a> {
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -144,21 +124,6 @@ impl<'a> eframe::App for RogApp<'a> {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -28,6 +28,20 @@ use nix::{sys::stat, unistd};
|
||||
use tempfile::TempDir;
|
||||
//use log::{error, info, warn};
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub fn print_versions() {
|
||||
println!("App and daemon versions:");
|
||||
println!(" rog-gui v{}", VERSION);
|
||||
println!(" asusd v{}", daemon::VERSION);
|
||||
println!("\nComponent crate versions:");
|
||||
println!(" rog-anime v{}", rog_anime::VERSION);
|
||||
println!(" rog-aura v{}", rog_aura::VERSION);
|
||||
println!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||
println!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||
println!("rog-platform v{}", rog_platform::VERSION);
|
||||
}
|
||||
|
||||
pub const SHOWING_GUI: u8 = 1;
|
||||
pub const SHOW_GUI: u8 = 2;
|
||||
|
||||
@@ -44,6 +58,14 @@ 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");
|
||||
|
||||
if path.read_dir()?.next().is_none() {
|
||||
std::fs::remove_dir_all(path)?;
|
||||
return tempfile::Builder::new()
|
||||
.prefix("rog-gui")
|
||||
.rand_bytes(0)
|
||||
.tempdir();
|
||||
}
|
||||
|
||||
let mut ipc_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
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,
|
||||
page_states::PageDataStates, print_versions, startup_error::AppErrorShow, RogApp,
|
||||
RogDbusClientBlocking, SHOWING_GUI, SHOW_GUI,
|
||||
};
|
||||
|
||||
use std::{
|
||||
fs::{self, OpenOptions},
|
||||
io::Read,
|
||||
fs::OpenOptions,
|
||||
io::{Read, Write},
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread::spawn,
|
||||
time::Duration,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "mocking"))]
|
||||
@@ -24,11 +19,14 @@ 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>> {
|
||||
print_versions();
|
||||
|
||||
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)),
|
||||
run_and_return: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -44,7 +42,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Startup
|
||||
let mut config = Config::load()?;
|
||||
let start_closed = config.startup_in_background;
|
||||
let mut start_closed = config.startup_in_background;
|
||||
|
||||
if config.startup_in_background {
|
||||
config.run_in_background = true;
|
||||
@@ -62,8 +60,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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();
|
||||
@@ -71,18 +67,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
let layout = KeyLayout::find_layout(board_name.as_str(), PathBuf::from(DATA_DIR))
|
||||
.map_err(|e| {
|
||||
println!("{BOARD_NAME}, {e}");
|
||||
})
|
||||
.unwrap_or(KeyLayout::ga401_layout());
|
||||
|
||||
// Cheap method to alert to notifications rather than spinning a thread for each
|
||||
// This is quite different when done in a retained mode app
|
||||
@@ -94,44 +84,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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,
|
||||
)?;
|
||||
}
|
||||
start_notifications(
|
||||
charge_notified.clone(),
|
||||
bios_notified.clone(),
|
||||
aura_notified.clone(),
|
||||
anime_notified.clone(),
|
||||
profiles_notified.clone(),
|
||||
fans_notified.clone(),
|
||||
notifs_enabled.clone(),
|
||||
)?;
|
||||
|
||||
// tmp-dir must live to the end of program life
|
||||
let _tmp_dir = match tempfile::Builder::new()
|
||||
@@ -143,25 +104,59 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
});
|
||||
loop {
|
||||
let states = {
|
||||
let supported = match dbus.proxies().supported().supported_functions() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eframe::run_native(
|
||||
"ROG Control Center",
|
||||
native_options.clone(),
|
||||
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
eframe::run_native(
|
||||
"ROG Control Center",
|
||||
native_options,
|
||||
Box::new(move |cc| {
|
||||
Box::new(RogApp::new(start_closed, config, should, states, cc).unwrap())
|
||||
}),
|
||||
);
|
||||
PageDataStates::new(
|
||||
layout.clone(),
|
||||
notifs_enabled.clone(),
|
||||
charge_notified.clone(),
|
||||
bios_notified.clone(),
|
||||
aura_notified.clone(),
|
||||
anime_notified.clone(),
|
||||
profiles_notified.clone(),
|
||||
fans_notified.clone(),
|
||||
&supported,
|
||||
&dbus,
|
||||
)?
|
||||
};
|
||||
|
||||
if !start_closed {
|
||||
let mut ipc_file = get_ipc_file().unwrap();
|
||||
ipc_file.write_all(&[SHOWING_GUI]).unwrap();
|
||||
eframe::run_native(
|
||||
"ROG Control Center",
|
||||
native_options.clone(),
|
||||
Box::new(move |cc| {
|
||||
Box::new(RogApp::new(Config::load().unwrap(), states, cc).unwrap())
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let config = Config::load().unwrap();
|
||||
if !config.run_in_background {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut buf = [0u8; 4];
|
||||
// blocks until it is read, typically the read will happen after a second
|
||||
// process writes to the IPC (so there is data to actually read)
|
||||
if get_ipc_file().unwrap().read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
|
||||
start_closed = false;
|
||||
continue;
|
||||
}
|
||||
dbg!("asda");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ impl Bios {
|
||||
pub fn post_boot_sound(&self) -> Result<i16> {
|
||||
Ok(1)
|
||||
}
|
||||
pub fn dedicated_graphic_mode(&self) -> Result<i16> {
|
||||
pub fn gpu_mux_mode(&self) -> Result<i16> {
|
||||
Ok(1)
|
||||
}
|
||||
pub fn panel_overdrive(&self) -> Result<i16> {
|
||||
@@ -71,7 +71,7 @@ impl Bios {
|
||||
pub fn set_post_boot_sound(&self, _b: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_dedicated_graphic_mode(&self, _b: bool) -> Result<()> {
|
||||
pub fn set_gpu_mux_mode(&self, _b: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_panel_overdrive(&self, _b: bool) -> Result<()> {
|
||||
|
||||
@@ -5,7 +5,7 @@ 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,
|
||||
zbus_platform::RogBiosProxy, zbus_profile::ProfileProxy,
|
||||
};
|
||||
use rog_profiles::Profile;
|
||||
use smol::{future, Executor};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
collections::{BTreeMap, HashSet},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
@@ -8,8 +8,8 @@ use std::{
|
||||
|
||||
use egui::Vec2;
|
||||
use rog_aura::{layouts::KeyLayout, usb::AuraPowerDev, AuraEffect, AuraModeNum};
|
||||
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
|
||||
use rog_profiles::{fan_curve_set::FanCurveSet, FanCurvePU, Profile};
|
||||
use rog_supported::SupportedFunctions;
|
||||
|
||||
use crate::{error::Result, RogDbusClientBlocking};
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct BiosState {
|
||||
/// updated, so the full state needs refresh
|
||||
pub was_notified: Arc<AtomicBool>,
|
||||
pub post_sound: bool,
|
||||
pub dedicated_gfx: bool,
|
||||
pub dedicated_gfx: GpuMode,
|
||||
pub panel_overdrive: bool,
|
||||
pub dgpu_disable: bool,
|
||||
pub egpu_enable: bool,
|
||||
@@ -39,13 +39,13 @@ impl BiosState {
|
||||
} else {
|
||||
false
|
||||
},
|
||||
dedicated_gfx: if supported.rog_bios_ctrl.dedicated_gfx {
|
||||
dbus.proxies().rog_bios().dedicated_graphic_mode()? != 0
|
||||
dedicated_gfx: if supported.rog_bios_ctrl.gpu_mux {
|
||||
dbus.proxies().rog_bios().gpu_mux_mode()?
|
||||
} else {
|
||||
false
|
||||
GpuMode::NotSupported
|
||||
},
|
||||
panel_overdrive: if supported.rog_bios_ctrl.panel_overdrive {
|
||||
dbus.proxies().rog_bios().panel_overdrive()? != 0
|
||||
dbus.proxies().rog_bios().panel_overdrive()?
|
||||
} else {
|
||||
false
|
||||
},
|
||||
@@ -72,7 +72,9 @@ impl ProfilesState {
|
||||
Ok(Self {
|
||||
was_notified,
|
||||
list: if supported.platform_profile.platform_profile {
|
||||
dbus.proxies().profile().profiles()?
|
||||
let mut list = dbus.proxies().profile().profiles()?;
|
||||
list.sort();
|
||||
list
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
@@ -91,7 +93,7 @@ pub struct FanCurvesState {
|
||||
pub show_curve: Profile,
|
||||
pub show_graph: FanCurvePU,
|
||||
pub enabled: HashSet<Profile>,
|
||||
pub curves: HashMap<Profile, FanCurveSet>,
|
||||
pub curves: BTreeMap<Profile, FanCurveSet>,
|
||||
pub drag_delta: Vec2,
|
||||
}
|
||||
|
||||
@@ -118,7 +120,7 @@ impl FanCurvesState {
|
||||
HashSet::from([Profile::Balanced, Profile::Quiet, Profile::Performance])
|
||||
};
|
||||
|
||||
let mut curves: HashMap<Profile, FanCurveSet> = HashMap::new();
|
||||
let mut curves: BTreeMap<Profile, FanCurveSet> = BTreeMap::new();
|
||||
profiles.iter().for_each(|p| {
|
||||
if supported.platform_profile.fan_curves {
|
||||
if let Ok(curve) = dbus.proxies().profile().fan_curve_data(*p) {
|
||||
@@ -337,7 +339,7 @@ impl Default for PageDataStates {
|
||||
bios: BiosState {
|
||||
was_notified: Default::default(),
|
||||
post_sound: Default::default(),
|
||||
dedicated_gfx: Default::default(),
|
||||
dedicated_gfx: GpuMode::NotSupported,
|
||||
panel_overdrive: Default::default(),
|
||||
dgpu_disable: Default::default(),
|
||||
egpu_enable: Default::default(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{sync::atomic::Ordering, time::Duration};
|
||||
|
||||
use egui::Color32;
|
||||
use rog_aura::AuraModeNum;
|
||||
use rog_aura::{AuraEffect, AuraModeNum};
|
||||
|
||||
use crate::{
|
||||
widgets::{aura_modes_group, keyboard},
|
||||
@@ -31,14 +31,14 @@ impl<'a> RogApp<'a> {
|
||||
.aura
|
||||
.modes
|
||||
.get(&states.aura.current_mode)
|
||||
.unwrap()
|
||||
.unwrap_or(&AuraEffect::default())
|
||||
.colour1;
|
||||
|
||||
let c2 = states
|
||||
.aura
|
||||
.modes
|
||||
.get(&states.aura.current_mode)
|
||||
.unwrap()
|
||||
.unwrap_or(&AuraEffect::default())
|
||||
.colour2;
|
||||
|
||||
let mut colour = Color32::from_rgb(c1.0, c1.1, c1.2);
|
||||
|
||||
@@ -4,8 +4,8 @@ use crate::{
|
||||
RogApp, RogDbusClientBlocking,
|
||||
};
|
||||
use egui::Ui;
|
||||
use rog_platform::supported::SupportedFunctions;
|
||||
use rog_profiles::Profile;
|
||||
use rog_supported::SupportedFunctions;
|
||||
|
||||
impl<'a> RogApp<'a> {
|
||||
pub fn fan_curve_page(&mut self, ctx: &egui::Context) {
|
||||
@@ -27,7 +27,7 @@ impl<'a> RogApp<'a> {
|
||||
ui,
|
||||
);
|
||||
|
||||
fan_graphs(&mut states.profiles, &mut states.fan_curves, dbus, &mut states.error, ui);
|
||||
fan_graphs(supported, &mut states.profiles, &mut states.fan_curves, dbus, &mut states.error, ui);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,17 +66,24 @@ impl<'a> RogApp<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
profiles.list.sort();
|
||||
for f in profiles.list.iter() {
|
||||
item(*f, curves, curves.enabled.contains(f));
|
||||
}
|
||||
});
|
||||
|
||||
if changed {
|
||||
let selected_profile = curves.show_curve;
|
||||
let selected_pu = curves.show_graph;
|
||||
|
||||
let notif = curves.was_notified.clone();
|
||||
match FanCurvesState::new(notif, supported, dbus) {
|
||||
Ok(f) => *curves = f,
|
||||
Err(e) => *do_error = Some(e.to_string()),
|
||||
}
|
||||
|
||||
curves.show_curve = selected_profile;
|
||||
curves.show_graph = selected_pu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use crate::{
|
||||
widgets::{anime_power_group, aura_power_group, platform_profile, rog_bios_group},
|
||||
widgets::{
|
||||
anime_power_group, app_settings, aura_power_group, platform_profile, rog_bios_group,
|
||||
},
|
||||
RogApp,
|
||||
};
|
||||
|
||||
impl<'a> RogApp<'a> {
|
||||
pub fn system_page(&mut self, ctx: &egui::Context) {
|
||||
let Self {
|
||||
config,
|
||||
supported,
|
||||
states,
|
||||
asus_dbus: dbus,
|
||||
@@ -21,30 +24,42 @@ impl<'a> RogApp<'a> {
|
||||
egui::Grid::new("grid_of_bits")
|
||||
.min_col_width(rect.width() / 2.0)
|
||||
.show(ui, |ui| {
|
||||
/******************************************************/
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
app_settings(config, states, ui);
|
||||
});
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
if supported.platform_profile.platform_profile {
|
||||
platform_profile(states, dbus, ui);
|
||||
}
|
||||
});
|
||||
ui.end_row();
|
||||
|
||||
/******************************************************/
|
||||
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.end_row();
|
||||
|
||||
/******************************************************/
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
if supported.anime_ctrl.0 {
|
||||
anime_power_group(supported, states, dbus, ui);
|
||||
}
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.separator();
|
||||
});
|
||||
ui.end_row();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use egui::{RichText, Ui};
|
||||
use rog_supported::SupportedFunctions;
|
||||
use rog_platform::supported::SupportedFunctions;
|
||||
|
||||
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use egui::Ui;
|
||||
|
||||
use crate::{config::Config, page_states::PageDataStates};
|
||||
|
||||
pub fn app_settings(config: &mut Config, states: &mut PageDataStates, ui: &mut Ui) {
|
||||
ui.heading("ROG GUI Settings");
|
||||
// ui.label("Options are incomplete. Awake + Boot should work");
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use std::sync::{
|
||||
|
||||
use egui::{RichText, Ui};
|
||||
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour, Speed};
|
||||
use rog_supported::SupportedFunctions;
|
||||
use rog_platform::supported::SupportedFunctions;
|
||||
|
||||
use crate::{
|
||||
page_states::{AuraState, PageDataStates},
|
||||
|
||||
@@ -3,7 +3,7 @@ use rog_aura::{
|
||||
usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev},
|
||||
AuraZone,
|
||||
};
|
||||
use rog_supported::SupportedFunctions;
|
||||
use rog_platform::supported::SupportedFunctions;
|
||||
|
||||
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
|
||||
|
||||
@@ -227,11 +227,11 @@ fn aura_power2(
|
||||
.multizone_led_mode
|
||||
.contains(&AuraZone::BarRight);
|
||||
|
||||
let boot_bar = &mut enabled_states.x19b6.contains(&AuraDev19b6::BootBar);
|
||||
let boot_bar = &mut enabled_states.x19b6.contains(&AuraDev19b6::AwakeBar);
|
||||
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_bar = &mut enabled_states.x19b6.contains(&AuraDev19b6::BootBar);
|
||||
let awake_logo = &mut enabled_states.x19b6.contains(&AuraDev19b6::AwakeLogo);
|
||||
let awake_keyb = &mut enabled_states.x19b6.contains(&AuraDev19b6::AwakeKeyb);
|
||||
|
||||
@@ -319,9 +319,9 @@ fn aura_power2(
|
||||
.multizone_led_mode
|
||||
.contains(&AuraZone::BarLeft)
|
||||
{
|
||||
modify(*boot_bar, AuraDev19b6::BootBar);
|
||||
modify(*boot_bar, AuraDev19b6::AwakeBar);
|
||||
modify(*sleep_bar, AuraDev19b6::SleepBar);
|
||||
modify(*awake_bar, AuraDev19b6::AwakeBar);
|
||||
modify(*awake_bar, AuraDev19b6::BootBar);
|
||||
}
|
||||
|
||||
let mut send = |enable: bool, data: Vec<AuraDev19b6>| {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use egui::{plot::Points, Ui};
|
||||
use rog_platform::supported::SupportedFunctions;
|
||||
use rog_profiles::{FanCurvePU, Profile};
|
||||
|
||||
use crate::{
|
||||
@@ -7,6 +8,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub fn fan_graphs(
|
||||
supported: &SupportedFunctions,
|
||||
profiles: &mut ProfilesState,
|
||||
curves: &mut FanCurvesState,
|
||||
dbus: &RogDbusClientBlocking,
|
||||
@@ -16,24 +18,27 @@ pub fn fan_graphs(
|
||||
ui.separator();
|
||||
|
||||
let mut item = |p: Profile, ui: &mut Ui| {
|
||||
ui.selectable_value(&mut curves.show_curve, p, format!("{p:?}"));
|
||||
ui.group(|ui| {
|
||||
ui.selectable_value(&mut curves.show_curve, p, format!("{p:?}"));
|
||||
ui.add_enabled_ui(curves.show_curve == p, |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),
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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();
|
||||
@@ -105,15 +110,36 @@ pub fn fan_graphs(
|
||||
plot_ui.points(points)
|
||||
});
|
||||
|
||||
let mut set = false;
|
||||
let mut reset = false;
|
||||
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();
|
||||
}
|
||||
set = ui.add(egui::Button::new("Apply Fan-curve")).clicked();
|
||||
reset = ui.add(egui::Button::new("Reset Profile")).clicked();
|
||||
});
|
||||
|
||||
if set {
|
||||
dbus.proxies()
|
||||
.profile()
|
||||
.set_fan_curve(profiles.current, data.clone())
|
||||
.map_err(|err| {
|
||||
*do_error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
if reset {
|
||||
dbus.proxies()
|
||||
.profile()
|
||||
.reset_profile_curves(profiles.current)
|
||||
.map_err(|err| {
|
||||
*do_error = Some(err.to_string());
|
||||
})
|
||||
.ok();
|
||||
|
||||
let notif = curves.was_notified.clone();
|
||||
match FanCurvesState::new(notif, supported, dbus) {
|
||||
Ok(f) => *curves = f,
|
||||
Err(e) => *do_error = Some(e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
mod anime_power;
|
||||
mod app_settings;
|
||||
mod aura_modes;
|
||||
mod aura_power;
|
||||
mod fan_graph;
|
||||
@@ -8,6 +9,7 @@ mod side_panel;
|
||||
mod top_bar;
|
||||
|
||||
pub use anime_power::*;
|
||||
pub use app_settings::*;
|
||||
pub use aura_modes::*;
|
||||
pub use aura_power::*;
|
||||
pub use fan_graph::*;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
|
||||
use egui::Ui;
|
||||
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
|
||||
use rog_profiles::Profile;
|
||||
use rog_supported::SupportedFunctions;
|
||||
|
||||
pub fn platform_profile(states: &mut PageDataStates, dbus: &RogDbusClientBlocking, ui: &mut Ui) {
|
||||
ui.heading("Platform profile");
|
||||
@@ -90,17 +90,34 @@ pub fn rog_bios_group(
|
||||
}
|
||||
}
|
||||
|
||||
if supported.rog_bios_ctrl.dedicated_gfx {
|
||||
if ui
|
||||
.add(egui::Checkbox::new(
|
||||
&mut states.bios.dedicated_gfx,
|
||||
"G-Sync Dedicated GPU mode",
|
||||
))
|
||||
.changed()
|
||||
{
|
||||
if supported.rog_bios_ctrl.gpu_mux {
|
||||
let mut changed = false;
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal_wrapped(|ui| ui.label("GPU MUX mode (reboot required)"));
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
changed = ui
|
||||
.selectable_value(
|
||||
&mut states.bios.dedicated_gfx,
|
||||
GpuMode::Discrete,
|
||||
"Dedicated (Ultimate)",
|
||||
)
|
||||
.clicked()
|
||||
|| ui
|
||||
.selectable_value(
|
||||
&mut states.bios.dedicated_gfx,
|
||||
GpuMode::Optimus,
|
||||
"Optimus (Hybrid)",
|
||||
)
|
||||
.clicked();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if changed {
|
||||
dbus.proxies()
|
||||
.rog_bios()
|
||||
.set_dedicated_graphic_mode(states.bios.dedicated_gfx)
|
||||
.set_gpu_mux_mode(states.bios.dedicated_gfx)
|
||||
.map_err(|err| {
|
||||
states.error = Some(err.to_string());
|
||||
})
|
||||
|
||||
@@ -53,7 +53,10 @@ impl<'a> RogApp<'a> {
|
||||
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");
|
||||
ui.hyperlink_to(
|
||||
"rog-gui.",
|
||||
"https://gitlab.com/asus-linux/asusctl/-/tree/main/rog-control-center",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,46 +1,14 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use egui::{vec2, Align2, Button, FontId, Id, Rect, RichText, Sense, Vec2};
|
||||
|
||||
use crate::RogApp;
|
||||
use crate::{RogApp, VERSION};
|
||||
|
||||
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::global_dark_light_mode_buttons(ui);
|
||||
egui::warn_if_debug_build(ui);
|
||||
});
|
||||
|
||||
@@ -61,7 +29,7 @@ impl<'a> RogApp<'a> {
|
||||
ui.painter().text(
|
||||
titlebar_rect.center_top() + vec2(0.0, height / 2.0),
|
||||
Align2::CENTER_CENTER,
|
||||
"ROG Control Center",
|
||||
format!("ROG Control Center v{}", VERSION),
|
||||
FontId::proportional(height - 2.0),
|
||||
text_color,
|
||||
);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rog_dbus"
|
||||
version = "4.2.1"
|
||||
version = "4.4.0"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
@@ -13,7 +13,7 @@ edition = "2018"
|
||||
rog_anime = { path = "../rog-anime" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
rog_supported = { path = "../rog-supported" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
zbus = "^2.2"
|
||||
zbus_macros = "^2.0"
|
||||
zvariant = "^3.0"
|
||||
|
||||
+9
-8
@@ -5,8 +5,8 @@ pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
|
||||
pub mod zbus_anime;
|
||||
pub mod zbus_charge;
|
||||
pub mod zbus_led;
|
||||
pub mod zbus_platform;
|
||||
pub mod zbus_profile;
|
||||
pub mod zbus_rogbios;
|
||||
pub mod zbus_supported;
|
||||
|
||||
// use rog_anime::AnimePowerStates;
|
||||
@@ -15,13 +15,14 @@ pub mod zbus_supported;
|
||||
// use std::sync::mpsc::{channel, Receiver};
|
||||
use zbus::{blocking, Connection, Result};
|
||||
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub struct DbusProxiesBlocking<'a> {
|
||||
anime: zbus_anime::AnimeProxyBlocking<'a>,
|
||||
charge: zbus_charge::ChargeProxyBlocking<'a>,
|
||||
led: zbus_led::LedProxyBlocking<'a>,
|
||||
profile: zbus_profile::ProfileProxyBlocking<'a>,
|
||||
rog_bios: zbus_rogbios::RogBiosProxyBlocking<'a>,
|
||||
rog_bios: zbus_platform::RogBiosProxyBlocking<'a>,
|
||||
supported: zbus_supported::SupportedProxyBlocking<'a>,
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ impl<'a> DbusProxiesBlocking<'a> {
|
||||
led: zbus_led::LedProxyBlocking::new(&conn)?,
|
||||
charge: zbus_charge::ChargeProxyBlocking::new(&conn)?,
|
||||
profile: zbus_profile::ProfileProxyBlocking::new(&conn)?,
|
||||
rog_bios: zbus_rogbios::RogBiosProxyBlocking::new(&conn)?,
|
||||
rog_bios: zbus_platform::RogBiosProxyBlocking::new(&conn)?,
|
||||
supported: zbus_supported::SupportedProxyBlocking::new(&conn)?,
|
||||
},
|
||||
conn,
|
||||
@@ -59,7 +60,7 @@ impl<'a> DbusProxiesBlocking<'a> {
|
||||
&self.profile
|
||||
}
|
||||
|
||||
pub fn rog_bios(&self) -> &zbus_rogbios::RogBiosProxyBlocking<'a> {
|
||||
pub fn rog_bios(&self) -> &zbus_platform::RogBiosProxyBlocking<'a> {
|
||||
&self.rog_bios
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ pub struct DbusProxies<'a> {
|
||||
charge: zbus_charge::ChargeProxy<'a>,
|
||||
led: zbus_led::LedProxy<'a>,
|
||||
profile: zbus_profile::ProfileProxy<'a>,
|
||||
rog_bios: zbus_rogbios::RogBiosProxy<'a>,
|
||||
rog_bios: zbus_platform::RogBiosProxy<'a>,
|
||||
supported: zbus_supported::SupportedProxy<'a>,
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ impl<'a> DbusProxies<'a> {
|
||||
led: zbus_led::LedProxy::new(&conn).await?,
|
||||
charge: zbus_charge::ChargeProxy::new(&conn).await?,
|
||||
profile: zbus_profile::ProfileProxy::new(&conn).await?,
|
||||
rog_bios: zbus_rogbios::RogBiosProxy::new(&conn).await?,
|
||||
rog_bios: zbus_platform::RogBiosProxy::new(&conn).await?,
|
||||
supported: zbus_supported::SupportedProxy::new(&conn).await?,
|
||||
},
|
||||
conn,
|
||||
@@ -128,7 +129,7 @@ impl<'a> DbusProxies<'a> {
|
||||
&self.profile
|
||||
}
|
||||
|
||||
pub fn rog_bios(&self) -> &zbus_rogbios::RogBiosProxy<'a> {
|
||||
pub fn rog_bios(&self) -> &zbus_platform::RogBiosProxy<'a> {
|
||||
&self.rog_bios
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use std::collections::BTreeMap;
|
||||
use zbus::{blocking::Connection, Result};
|
||||
use zbus_macros::dbus_proxy;
|
||||
|
||||
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, KeyColourArray, LedBrightness};
|
||||
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness, PerKeyRaw};
|
||||
|
||||
const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 FPS
|
||||
|
||||
@@ -53,6 +53,8 @@ trait Led {
|
||||
|
||||
fn set_leds_power(&self, options: AuraPowerDev, enabled: bool) -> zbus::Result<()>;
|
||||
|
||||
fn per_key_raw(&self, data: PerKeyRaw) -> zbus::fdo::Result<()>;
|
||||
|
||||
/// NotifyLed signal
|
||||
#[dbus_proxy(signal)]
|
||||
fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>;
|
||||
@@ -93,30 +95,9 @@ impl<'a> LedProxyPerkey<'a> {
|
||||
/// Intentionally blocks for 10ms after sending to allow the block to
|
||||
/// be written to the keyboard EC. This should not be async.
|
||||
#[inline]
|
||||
pub fn set_per_key(&self, key_colour_array: &KeyColourArray) -> Result<()> {
|
||||
let group = key_colour_array.get();
|
||||
let mut vecs = Vec::with_capacity(group.len());
|
||||
for v in group {
|
||||
vecs.push(v.to_vec());
|
||||
}
|
||||
// TODO: let mode = AuraModes::PerKey(vecs);
|
||||
// self.set_led_mode(&mode)?;
|
||||
|
||||
pub fn set_per_key(&self, per_key_raw: PerKeyRaw) -> Result<()> {
|
||||
self.0.per_key_raw(per_key_raw)?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(BLOCKING_TIME));
|
||||
|
||||
// if self.stop.load(Ordering::Relaxed) {
|
||||
// println!("Keyboard backlight was changed, exiting");
|
||||
// std::process::exit(1)
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method must always be called before the very first write to initialise
|
||||
/// the keyboard LED EC in the correct mode
|
||||
#[inline]
|
||||
pub fn init_effect(&self) -> Result<()> {
|
||||
// TODO: let mode = AuraModes::PerKey(vec![vec![]]);
|
||||
// self.0.set_led_mode(&serde_json::to_string(&mode).unwrap())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! # DBus interface proxy for: `org.asuslinux.Daemon`
|
||||
//!
|
||||
//! This code was generated by `zbus-xmlgen` `1.0.0` from DBus introspection data.
|
||||
//! Source: `Interface '/org/asuslinux/RogBios' from service 'org.asuslinux.Daemon' on system bus`.
|
||||
//! Source: `Interface '/org/asuslinux/Platform' from service 'org.asuslinux.Daemon' on system bus`.
|
||||
//!
|
||||
//! You may prefer to adapt it, instead of using it verbatim.
|
||||
//!
|
||||
@@ -19,34 +19,35 @@
|
||||
//!
|
||||
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||
|
||||
use rog_platform::platform::GpuMode;
|
||||
use zbus_macros::dbus_proxy;
|
||||
|
||||
#[dbus_proxy(
|
||||
interface = "org.asuslinux.Daemon",
|
||||
default_path = "/org/asuslinux/RogBios"
|
||||
default_path = "/org/asuslinux/Platform"
|
||||
)]
|
||||
trait RogBios {
|
||||
/// DedicatedGraphicMode method
|
||||
fn dedicated_graphic_mode(&self) -> zbus::Result<i16>;
|
||||
fn gpu_mux_mode(&self) -> zbus::Result<GpuMode>;
|
||||
|
||||
/// PostBootSound method
|
||||
fn post_boot_sound(&self) -> zbus::Result<i16>;
|
||||
|
||||
/// SetDedicatedGraphicMode method
|
||||
fn set_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()>;
|
||||
fn set_gpu_mux_mode(&self, mode: GpuMode) -> zbus::Result<()>;
|
||||
|
||||
/// SetPostBootSound method
|
||||
fn set_post_boot_sound(&self, on: bool) -> zbus::Result<()>;
|
||||
|
||||
/// PanelOverdrive method
|
||||
fn panel_overdrive(&self) -> zbus::Result<i8>;
|
||||
fn panel_overdrive(&self) -> zbus::Result<bool>;
|
||||
|
||||
/// SetPanelOverdrive method
|
||||
fn set_panel_overdrive(&self, overdrive: bool) -> zbus::Result<()>;
|
||||
|
||||
/// NotifyDedicatedGraphicMode signal
|
||||
#[dbus_proxy(signal)]
|
||||
fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()>;
|
||||
fn notify_gpu_mux_mode(&self, mode: GpuMode) -> zbus::Result<()>;
|
||||
|
||||
/// NotifyPostBootSound signal
|
||||
#[dbus_proxy(signal)]
|
||||
@@ -62,6 +62,12 @@ trait Profile {
|
||||
/// only for the currently active profile.
|
||||
fn set_active_curve_to_defaults(&self) -> zbus::Result<()>;
|
||||
|
||||
/// Reset the stored (self) and device curve to the defaults of the platform.
|
||||
///
|
||||
/// Each platform_profile has a different default and the defualt can be read
|
||||
/// only for the currently active profile.
|
||||
fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()>;
|
||||
|
||||
/// NotifyProfile signal
|
||||
#[dbus_proxy(signal)]
|
||||
fn notify_profile(&self, profile: Profile) -> zbus::Result<Profile>;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//!
|
||||
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||
|
||||
use rog_supported::SupportedFunctions;
|
||||
use rog_platform::supported::SupportedFunctions;
|
||||
use zbus_macros::dbus_proxy;
|
||||
|
||||
#[dbus_proxy(
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "rog_platform"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "*"
|
||||
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
zvariant = "^3.0"
|
||||
zvariant_derive = "^3.0"
|
||||
|
||||
udev = "^0.6"
|
||||
rusb = "^0.9"
|
||||
sysfs-class = "^0.1"
|
||||
inotify = "^0.10.0"
|
||||
|
||||
concat-idents = "1.1.3"
|
||||
@@ -0,0 +1,51 @@
|
||||
use std::fmt;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, PlatformError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PlatformError {
|
||||
ParseVendor,
|
||||
ParseNum,
|
||||
Udev(String, std::io::Error),
|
||||
USB(rusb::Error),
|
||||
Path(String, std::io::Error),
|
||||
Read(String, std::io::Error),
|
||||
Write(String, std::io::Error),
|
||||
NotSupported,
|
||||
AttrNotFound(String),
|
||||
MissingFunction(String),
|
||||
MissingLedBrightNode(String, std::io::Error),
|
||||
Io(String, std::io::Error),
|
||||
NoAuraKeyboard,
|
||||
NoAuraNode,
|
||||
}
|
||||
|
||||
impl fmt::Display for PlatformError {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
PlatformError::ParseVendor => write!(f, "Parse gfx vendor error"),
|
||||
PlatformError::ParseNum => write!(f, "Parse number error"),
|
||||
PlatformError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
|
||||
PlatformError::USB(error) => write!(f, "usb {}", error),
|
||||
PlatformError::Path(path, error) => write!(f, "Path {}: {}", path, error),
|
||||
PlatformError::Read(path, error) => write!(f, "Read {}: {}", path, error),
|
||||
PlatformError::Write(path, error) => write!(f, "Write {}: {}", path, error),
|
||||
PlatformError::NotSupported => write!(f, "Not supported"),
|
||||
PlatformError::AttrNotFound(deets) => write!(f, "Attribute not found: {}", deets),
|
||||
PlatformError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
|
||||
PlatformError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
|
||||
PlatformError::Io(path, detail) => write!(f, "std::io error: {} {}", path, detail),
|
||||
PlatformError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
|
||||
PlatformError::NoAuraNode => write!(f, "No Aura keyboard node found"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for PlatformError {}
|
||||
|
||||
impl From<rusb::Error> for PlatformError {
|
||||
fn from(err: rusb::Error) -> Self {
|
||||
PlatformError::USB(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
use std::{fs::OpenOptions, io::Write, path::PathBuf};
|
||||
|
||||
use log::{info, warn};
|
||||
|
||||
use crate::error::{PlatformError, Result};
|
||||
|
||||
#[derive(Debug, PartialEq, PartialOrd, Clone)]
|
||||
pub struct HidRaw(PathBuf);
|
||||
|
||||
impl HidRaw {
|
||||
pub fn new(id_product: &str) -> Result<Self> {
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
|
||||
enumerator.match_subsystem("hidraw").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator
|
||||
.scan_devices()
|
||||
.map_err(|e| PlatformError::Io("enumerator".to_owned(), e))?
|
||||
{
|
||||
if let Some(parent) = device
|
||||
.parent_with_subsystem_devtype("usb", "usb_device")
|
||||
.map_err(|e| PlatformError::Io(device.devpath().to_string_lossy().to_string(), e))?
|
||||
{
|
||||
if let Some(parent) = parent.attribute_value("idProduct") {
|
||||
if parent == id_product {
|
||||
if let Some(dev_node) = device.devnode() {
|
||||
info!("Using device at: {:?} for LED control", dev_node);
|
||||
return Ok(Self(dev_node.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(PlatformError::MissingFunction(format!(
|
||||
"hidraw dev {} not found",
|
||||
id_product
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn write_bytes(&self, message: &[u8]) -> Result<()> {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(&self.0)
|
||||
.map_err(|e| PlatformError::Io(self.0.to_string_lossy().to_string(), e))?;
|
||||
// println!("write: {:02x?}", &message);
|
||||
file.write_all(message)
|
||||
.map_err(|e| PlatformError::Io(self.0.to_string_lossy().to_string(), e))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use log::{info, warn};
|
||||
|
||||
use crate::{
|
||||
attr_u8,
|
||||
error::{PlatformError, Result},
|
||||
has_attr, set_attr_u8_array, to_device,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
|
||||
pub struct KeyboardLed {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl KeyboardLed {
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
|
||||
enumerator.match_subsystem("leds").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
enumerator
|
||||
.match_sysname("asus::kbd_backlight")
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator.scan_devices().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("scan_devices failed".into(), err)
|
||||
})? {
|
||||
info!("Found keyboard LED controls at {:?}", device.sysname());
|
||||
return Ok(Self {
|
||||
path: device.syspath().to_owned(),
|
||||
});
|
||||
}
|
||||
Err(PlatformError::MissingFunction(
|
||||
"asus::kbd_backlight not found".into(),
|
||||
))
|
||||
}
|
||||
|
||||
attr_u8!("brightness", path);
|
||||
|
||||
has_attr!("kbd_rgb_mode" path);
|
||||
set_attr_u8_array!(
|
||||
/// kbd_rgb_mode can only be set, not read back
|
||||
"kbd_rgb_mode"
|
||||
path
|
||||
);
|
||||
|
||||
has_attr!("kbd_rgb_state" path);
|
||||
set_attr_u8_array!(
|
||||
/// kbd_rgb_state can only be set, not read back
|
||||
"kbd_rgb_state"
|
||||
path
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//! This crate functions as a wrapper of all the relevant ASUS functionality
|
||||
//! on ROG, Strix, and TUF laptops.
|
||||
|
||||
pub mod error;
|
||||
pub mod hid_raw;
|
||||
pub mod keyboard_led;
|
||||
pub(crate) mod macros;
|
||||
pub mod platform;
|
||||
pub mod power;
|
||||
pub mod supported;
|
||||
pub mod usb_raw;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use error::{PlatformError, Result};
|
||||
use udev::Device;
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub(crate) fn to_device(sys_path: &Path) -> Result<Device> {
|
||||
Device::from_syspath(sys_path)
|
||||
.map_err(|e| PlatformError::Udev("Couldn't transform syspath to device".to_string(), e))
|
||||
}
|
||||
|
||||
pub fn has_attr(device: &Device, attr_name: &str) -> bool {
|
||||
for attr in device.attributes() {
|
||||
if attr.name() == attr_name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn read_attr_bool(device: &Device, attr_name: &str) -> Result<bool> {
|
||||
if let Some(value) = device.attribute_value(attr_name) {
|
||||
let tmp = value.to_string_lossy();
|
||||
if tmp.trim() == "0" {
|
||||
return Ok(false);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
Err(PlatformError::AttrNotFound(attr_name.to_string()))
|
||||
}
|
||||
|
||||
pub fn write_attr_bool(device: &mut Device, attr: &str, value: bool) -> Result<()> {
|
||||
device
|
||||
.set_attribute_value(attr, &(value as u8).to_string())
|
||||
.map_err(|e| PlatformError::Io(attr.into(), e))
|
||||
}
|
||||
|
||||
pub fn read_attr_u8(device: &Device, attr_name: &str) -> Result<u8> {
|
||||
if let Some(value) = device.attribute_value(attr_name) {
|
||||
let tmp = value.to_string_lossy();
|
||||
return Ok(tmp.parse::<u8>().map_err(|_| PlatformError::ParseNum)?);
|
||||
}
|
||||
Err(PlatformError::AttrNotFound(attr_name.to_string()))
|
||||
}
|
||||
|
||||
pub fn write_attr_u8(device: &mut Device, attr: &str, value: u8) -> Result<()> {
|
||||
device
|
||||
.set_attribute_value(attr, &(value).to_string())
|
||||
.map_err(|e| PlatformError::Io(attr.into(), e))
|
||||
}
|
||||
|
||||
pub fn read_attr_u8_array(device: &Device, attr_name: &str) -> Result<Vec<u8>> {
|
||||
if let Some(value) = device.attribute_value(attr_name) {
|
||||
let tmp = value.to_string_lossy();
|
||||
let tmp = tmp
|
||||
.split(' ')
|
||||
.map(|v| u8::from_str_radix(v, 10).unwrap_or(0))
|
||||
.collect();
|
||||
return Ok(tmp);
|
||||
}
|
||||
Err(PlatformError::AttrNotFound(attr_name.to_string()))
|
||||
}
|
||||
|
||||
pub fn write_attr_u8_array(device: &mut Device, attr: &str, values: &[u8]) -> Result<()> {
|
||||
let tmp: String = values.iter().map(|v| format!("{} ", v)).collect();
|
||||
let tmp = tmp.trim();
|
||||
device
|
||||
.set_attribute_value(attr, &tmp)
|
||||
.map_err(|e| PlatformError::Io(attr.into(), e))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn check() {
|
||||
let data = [1, 2, 3, 4, 5];
|
||||
|
||||
let tmp: String = data.iter().map(|v| format!("{} ", v)).collect();
|
||||
let tmp = tmp.trim();
|
||||
assert_eq!(tmp, "1 2 3 4 5");
|
||||
|
||||
let tmp: Vec<u8> = tmp
|
||||
.split(' ')
|
||||
.map(|v| u8::from_str_radix(v, 10).unwrap_or(0))
|
||||
.collect();
|
||||
assert_eq!(tmp, &[1, 2, 3, 4, 5]);
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn find_led_node(id_product: &str) -> Result<Device, PlatformError> {
|
||||
// let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
// warn!("{}", err);
|
||||
// PlatformError::Udev("enumerator failed".into(), err)
|
||||
// })?;
|
||||
// enumerator.match_subsystem("hidraw").map_err(|err| {
|
||||
// warn!("{}", err);
|
||||
// PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
// })?;
|
||||
|
||||
// for device in enumerator.scan_devices().map_err(|err| {
|
||||
// warn!("{}", err);
|
||||
// PlatformError::Udev("scan_devices failed".into(), err)
|
||||
// })? {
|
||||
// if let Some(parent) = device
|
||||
// .parent_with_subsystem_devtype("usb", "usb_device")
|
||||
// .map_err(|err| {
|
||||
// warn!("{}", err);
|
||||
// PlatformError::Udev("parent_with_subsystem_devtype failed".into(), err)
|
||||
// })?
|
||||
// {
|
||||
// if parent
|
||||
// .attribute_value("idProduct")
|
||||
// .ok_or_else(|| PlatformError::NotFound("LED idProduct".into()))?
|
||||
// == id_product
|
||||
// {
|
||||
// if let Some(dev_node) = device.devnode() {
|
||||
// info!("Using device at: {:?} for LED control", dev_node);
|
||||
// return Ok(device);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Err(PlatformError::MissingFunction(
|
||||
// "ASUS LED device node not found".into(),
|
||||
// ))
|
||||
// }
|
||||
@@ -0,0 +1,131 @@
|
||||
#[macro_export]
|
||||
macro_rules! has_attr {
|
||||
($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = has_, $attr_name {
|
||||
$(#[$doc_comment])*
|
||||
pub fn fn_name(&self) -> bool {
|
||||
match to_device(&self.$item) {
|
||||
Ok(p) => crate::has_attr(&p, $attr_name),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! watch_attr {
|
||||
($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = monitor_, $attr_name {
|
||||
$(#[$doc_comment])*
|
||||
pub fn fn_name(&self) -> Result<inotify::Inotify> {
|
||||
let mut path = self.$item.clone();
|
||||
path.push($attr_name);
|
||||
let mut inotify = inotify::Inotify::init().unwrap();
|
||||
inotify.add_watch(path.to_str().unwrap(), inotify::WatchMask::MODIFY).unwrap();
|
||||
Ok(inotify)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! get_attr_bool {
|
||||
($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = get_, $attr_name {
|
||||
$(#[$doc_comment])*
|
||||
pub fn fn_name(&self) -> Result<bool> {
|
||||
crate::read_attr_bool(&to_device(&self.$item)?, $attr_name)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! set_attr_bool {
|
||||
($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = set_, $attr_name {
|
||||
$(#[$doc_comment])*
|
||||
pub fn fn_name(&self, value: bool) -> Result<()> {
|
||||
crate::write_attr_bool(&mut to_device(&self.$item)?, $attr_name, value)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! attr_bool {
|
||||
($attr_name:literal, $item:ident) => {
|
||||
crate::has_attr!($attr_name $item);
|
||||
crate::get_attr_bool!( $attr_name $item);
|
||||
crate::set_attr_bool!($attr_name $item);
|
||||
crate::watch_attr!($attr_name $item);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! get_attr_u8 {
|
||||
($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = get_, $attr_name {
|
||||
$(#[$doc_comment])*
|
||||
pub fn fn_name(&self) -> Result<u8> {
|
||||
crate::read_attr_u8(&to_device(&self.$item)?, $attr_name)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! set_attr_u8 {
|
||||
($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = set_, $attr_name {
|
||||
$(#[$doc_comment])*
|
||||
pub fn fn_name(&self, value: u8) -> Result<()> {
|
||||
crate::write_attr_u8(&mut to_device(&self.$item)?, $attr_name, value)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! attr_u8 {
|
||||
($attr_name:literal, $item:ident) => {
|
||||
crate::has_attr!($attr_name $item);
|
||||
crate::get_attr_u8!($attr_name $item);
|
||||
crate::set_attr_u8!($attr_name $item);
|
||||
crate::watch_attr!($attr_name $item);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! get_attr_u8_array {
|
||||
($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = get_, $attr_name {
|
||||
$(#[$doc_comment])*
|
||||
pub fn fn_name(&self) -> Result<Vec<u8>> {
|
||||
crate::read_attr_u8_array(&to_device(&self.$item)?, $attr_name)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! set_attr_u8_array {
|
||||
($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => {
|
||||
concat_idents::concat_idents!(fn_name = set_, $attr_name {
|
||||
$(#[$doc_comment])*
|
||||
pub fn fn_name(&self, values: &[u8]) -> Result<()> {
|
||||
crate::write_attr_u8_array(&mut to_device(&self.$item)?, $attr_name, values)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! attr_u8_array {
|
||||
($attr_name:literal, $item:ident) => {
|
||||
crate::has_attr!($attr_name $item);
|
||||
crate::get_attr_u8_array!($attr_name $item);
|
||||
crate::set_attr_u8_array!($attr_name $item);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use log::{info, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zvariant::Type;
|
||||
|
||||
use crate::{
|
||||
attr_bool, attr_u8,
|
||||
error::{PlatformError, Result},
|
||||
to_device,
|
||||
};
|
||||
|
||||
/// The "platform" device provides access to things like:
|
||||
/// - dgpu_disable
|
||||
/// - egpu_enable
|
||||
/// - panel_od
|
||||
/// - gpu_mux
|
||||
/// - keyboard_mode, set keyboard RGB mode and speed
|
||||
/// - keyboard_state, set keyboard power states
|
||||
#[derive(Debug, PartialEq, PartialOrd, Clone)]
|
||||
pub struct AsusPlatform {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl AsusPlatform {
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
enumerator.match_subsystem("platform").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
enumerator.match_sysname("asus-nb-wmi").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator.scan_devices().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("scan_devices failed".into(), err)
|
||||
})? {
|
||||
info!("Found platform support at {:?}", device.sysname());
|
||||
return Ok(Self {
|
||||
path: device.syspath().to_owned(),
|
||||
});
|
||||
}
|
||||
Err(PlatformError::MissingFunction(
|
||||
"asus-nb-wmi not found".into(),
|
||||
))
|
||||
}
|
||||
|
||||
attr_bool!("dgpu_disable", path);
|
||||
attr_bool!("egpu_enable", path);
|
||||
attr_bool!("panel_od", path);
|
||||
attr_u8!("gpu_mux_mode", path);
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug, PartialEq, Clone, Copy)]
|
||||
pub enum GpuMode {
|
||||
Discrete,
|
||||
Optimus,
|
||||
Integrated,
|
||||
Egpu,
|
||||
Error,
|
||||
NotSupported,
|
||||
}
|
||||
|
||||
impl GpuMode {
|
||||
pub fn to_mux(&self) -> u8 {
|
||||
if *self == Self::Discrete {
|
||||
return 0;
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
pub fn to_dgpu(&self) -> u8 {
|
||||
if *self == Self::Integrated {
|
||||
return 1;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub fn to_egpu(&self) -> u8 {
|
||||
if *self == Self::Egpu {
|
||||
return 1;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub fn from_mux(num: u8) -> Self {
|
||||
if num == 0 {
|
||||
return Self::Discrete;
|
||||
}
|
||||
Self::Optimus
|
||||
}
|
||||
|
||||
pub fn from_dgpu(num: u8) -> Self {
|
||||
if num == 1 {
|
||||
return Self::Integrated;
|
||||
}
|
||||
Self::Optimus
|
||||
}
|
||||
|
||||
// `from_dgpu()` should be called also, and should take precedence if result
|
||||
// are not equal.
|
||||
pub fn from_egpu(num: u8) -> Self {
|
||||
if num == 1 {
|
||||
return Self::Egpu;
|
||||
}
|
||||
Self::Optimus
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use log::{info, warn};
|
||||
|
||||
use crate::{
|
||||
attr_u8,
|
||||
error::{PlatformError, Result},
|
||||
to_device,
|
||||
};
|
||||
|
||||
/// The "platform" device provides access to things like:
|
||||
/// - dgpu_disable
|
||||
/// - egpu_enable
|
||||
/// - panel_od
|
||||
/// - gpu_mux
|
||||
/// - keyboard_mode, set keyboard RGB mode and speed
|
||||
/// - keyboard_state, set keyboard power states
|
||||
#[derive(Debug, PartialEq, PartialOrd, Clone)]
|
||||
pub struct AsusPower {
|
||||
mains: PathBuf,
|
||||
battery: PathBuf,
|
||||
usb: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl AsusPower {
|
||||
/// When checking for battery this will look in order:
|
||||
/// - if attr `manufacturer` contains `asus`
|
||||
/// - if attr `charge_control_end_threshold` exists and `energy_full_design` >= 50 watt
|
||||
/// - if syspath end conatins `BAT`
|
||||
/// - if attr `type` is `battery` (last resort)
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut mains = PathBuf::new();
|
||||
let mut battery = None;
|
||||
let mut usb = None;
|
||||
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
enumerator.match_subsystem("power_supply").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator.scan_devices().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
PlatformError::Udev("scan_devices failed".into(), err)
|
||||
})? {
|
||||
if let Some(attr) = device.attribute_value("type") {
|
||||
info!("Power: Checking {:?}", device.syspath());
|
||||
match attr.to_string_lossy().to_ascii_lowercase().trim() {
|
||||
"mains" => {
|
||||
info!("Found mains power at {:?}", device.sysname());
|
||||
mains = device.syspath().to_path_buf();
|
||||
}
|
||||
"battery" => {
|
||||
// Priortised list of checks
|
||||
info!("Found a battery");
|
||||
if battery.is_none() {
|
||||
info!("Checking battery attributes");
|
||||
if device
|
||||
.attribute_value("charge_control_end_threshold")
|
||||
.is_some()
|
||||
{
|
||||
info!("Found battery power at {:?}, matched charge_control_end_threshold", device.sysname());
|
||||
battery = Some(device.syspath().to_path_buf());
|
||||
} else if device.sysname().to_string_lossy().starts_with("BAT") {
|
||||
info!(
|
||||
"Found battery power at {:?}, sysfs path ended with BAT<n>",
|
||||
device.sysname()
|
||||
);
|
||||
battery = Some(device.syspath().to_path_buf());
|
||||
} else {
|
||||
info!(
|
||||
"Last resort: Found battery power at {:?} using type = Battery",
|
||||
device.sysname()
|
||||
);
|
||||
battery = Some(device.syspath().to_path_buf());
|
||||
}
|
||||
}
|
||||
}
|
||||
"usb" => {
|
||||
info!("Found USB-C power at {:?}", device.sysname());
|
||||
usb = Some(device.syspath().to_path_buf());
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(battery) = battery {
|
||||
return Ok(Self {
|
||||
mains,
|
||||
battery,
|
||||
usb,
|
||||
});
|
||||
}
|
||||
|
||||
Err(PlatformError::MissingFunction(
|
||||
"Did not find a battery".to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
attr_u8!("charge_control_end_threshold", battery);
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
use rog_aura::{usb::AuraDevice, AuraModeNum, AuraZone};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use zvariant_derive::Type;
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)]
|
||||
pub struct SupportedFunctions {
|
||||
pub anime_ctrl: AnimeSupportedFunctions,
|
||||
pub charge_ctrl: ChargeSupportedFunctions,
|
||||
@@ -14,21 +12,21 @@ pub struct SupportedFunctions {
|
||||
pub rog_bios_ctrl: RogBiosSupportedFunctions,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)]
|
||||
pub struct AnimeSupportedFunctions(pub bool);
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)]
|
||||
pub struct ChargeSupportedFunctions {
|
||||
pub charge_level_set: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)]
|
||||
pub struct PlatformProfileFunctions {
|
||||
pub platform_profile: bool,
|
||||
pub fan_curves: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)]
|
||||
pub struct LedSupportedFunctions {
|
||||
pub prod_id: AuraDevice,
|
||||
pub brightness_set: bool,
|
||||
@@ -37,10 +35,10 @@ pub struct LedSupportedFunctions {
|
||||
pub per_key_led_mode: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default)]
|
||||
#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)]
|
||||
pub struct RogBiosSupportedFunctions {
|
||||
pub post_sound: bool,
|
||||
pub dedicated_gfx: bool,
|
||||
pub gpu_mux: bool,
|
||||
pub panel_overdrive: bool,
|
||||
pub dgpu_disable: bool,
|
||||
pub egpu_enable: bool,
|
||||
@@ -95,7 +93,7 @@ impl fmt::Display for RogBiosSupportedFunctions {
|
||||
writeln!(f, "\tPanel Overdrive: {}", self.panel_overdrive)?;
|
||||
writeln!(f, "\tdGPU disable switch: {}", self.dgpu_disable)?;
|
||||
writeln!(f, "\teGPU enable switch: {}", self.egpu_enable)?;
|
||||
writeln!(f, "\tDedicated GFX switch: {}", self.dedicated_gfx)?;
|
||||
writeln!(f, "\tGPU MUX control: {}", self.gpu_mux)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use rusb::{Device, DeviceHandle};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::error::{PlatformError, Result};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct USBRaw(DeviceHandle<rusb::GlobalContext>);
|
||||
|
||||
impl USBRaw {
|
||||
pub fn new(id_product: u16) -> Result<Self> {
|
||||
for device in rusb::devices()?.iter() {
|
||||
let device_desc = device.device_descriptor()?;
|
||||
if device_desc.vendor_id() == 0x0b05 && device_desc.product_id() == id_product {
|
||||
let handle = Self::get_dev_handle(device)?;
|
||||
return Ok(Self(handle));
|
||||
}
|
||||
}
|
||||
|
||||
Err(PlatformError::MissingFunction(format!(
|
||||
"USBRaw dev {} not found",
|
||||
id_product
|
||||
)))
|
||||
}
|
||||
|
||||
fn get_dev_handle(
|
||||
device: Device<rusb::GlobalContext>,
|
||||
) -> Result<DeviceHandle<rusb::GlobalContext>> {
|
||||
// We don't expect this ID to ever change
|
||||
let mut device = device.open()?;
|
||||
device.reset()?;
|
||||
device.set_auto_detach_kernel_driver(true)?;
|
||||
device.claim_interface(0)?;
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
pub fn write_bytes(&self, message: &[u8]) -> Result<usize> {
|
||||
self.0
|
||||
.write_control(
|
||||
0x21, // request_type
|
||||
0x09, // request
|
||||
0x35e, // value
|
||||
0x00, // index
|
||||
message,
|
||||
Duration::from_millis(200),
|
||||
)
|
||||
.map_err(|e| PlatformError::USB(e))
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ pub enum ProfileError {
|
||||
/// (pwm/temp, prev, next)
|
||||
ParseFanCurvePrevHigher(&'static str, u8, u8),
|
||||
ParseFanCurvePercentOver100(u8),
|
||||
NotEnoughPoints,
|
||||
// Zbus(zbus::Error),
|
||||
}
|
||||
|
||||
@@ -24,6 +25,7 @@ impl fmt::Display for ProfileError {
|
||||
ProfileError::Read(path, error) => write!(f, "Read {}: {}", path, error),
|
||||
ProfileError::Write(path, error) => write!(f, "Write {}: {}", path, error),
|
||||
ProfileError::NotSupported => write!(f, "Not supported"),
|
||||
ProfileError::NotEnoughPoints => write!(f, "Less than 8 curve points supplied"),
|
||||
ProfileError::NotFound(deets) => write!(f, "Not found: {}", deets),
|
||||
ProfileError::Io(detail) => write!(f, "std::io error: {}", detail),
|
||||
ProfileError::ParseProfileName => write!(f, "Invalid profile name"),
|
||||
|
||||
@@ -75,6 +75,10 @@ impl std::str::FromStr for CurveData {
|
||||
let mut pwm_prev = 0;
|
||||
let mut percentages = false;
|
||||
|
||||
if input.split(',').count() < 8 {
|
||||
return Err(ProfileError::NotEnoughPoints);
|
||||
}
|
||||
|
||||
for (index, value) in input.split(',').enumerate() {
|
||||
for (select, num) in value.splitn(2, |c| c == 'c' || c == ':').enumerate() {
|
||||
if num.contains('%') {
|
||||
@@ -265,7 +269,11 @@ mod tests {
|
||||
assert_eq!(
|
||||
string.as_str(),
|
||||
"CPU: 30c:1%,49c:1%,59c:3%,69c:3%,79c:30%,89c:49%,99c:56%,109c:58%"
|
||||
)
|
||||
);
|
||||
|
||||
let curve = CurveData::from_str("30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%");
|
||||
|
||||
assert!(curve.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -15,10 +15,10 @@ use udev::Device;
|
||||
#[cfg(feature = "dbus")]
|
||||
use zvariant_derive::Type;
|
||||
|
||||
pub static PLATFORM_PROFILE: &str = "/sys/firmware/acpi/platform_profile";
|
||||
pub static PLATFORM_PROFILES: &str = "/sys/firmware/acpi/platform_profile_choices";
|
||||
pub const PLATFORM_PROFILE: &str = "/sys/firmware/acpi/platform_profile";
|
||||
pub const PLATFORM_PROFILES: &str = "/sys/firmware/acpi/platform_profile_choices";
|
||||
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub fn find_fan_curve_node() -> Result<Option<Device>, ProfileError> {
|
||||
let mut enumerator = udev::Enumerator::new()?;
|
||||
@@ -68,7 +68,6 @@ impl Profile {
|
||||
|
||||
pub fn set_profile(profile: Profile) -> Result<(), ProfileError> {
|
||||
let mut file = OpenOptions::new().write(true).open(PLATFORM_PROFILE)?;
|
||||
|
||||
file.write_all(<&str>::from(profile).as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "rog_supported"
|
||||
version = "4.2.1"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
|
||||
homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl"
|
||||
description = "Helper to determine what is supported by asus laptops"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
zvariant = "^3.0"
|
||||
zvariant_derive = "^3.0"
|
||||
Reference in New Issue
Block a user