Compare commits

...

105 Commits
4.5.2 ... 4.6.1

Author SHA1 Message Date
Luke D. Jones
bee5508099 Prep new release 2023-04-26 22:00:22 +12:00
Luke D. Jones
c741204200 Prep new release 2023-04-26 21:59:13 +12:00
Luke D. Jones
858c9841a7 Update deps 2023-04-26 21:35:48 +12:00
Luke D. Jones
fdc7d88a70 More tweaks to notifications 2023-04-26 12:49:29 +12:00
Luke D. Jones
da3017bb89 Update supergfx dep 2023-04-26 12:32:26 +12:00
Luke D. Jones
641e762e80 Update deps 2023-04-26 11:24:50 +12:00
Luke D. Jones
25ecfda095 Various tray and notification improvements 2023-04-26 10:57:13 +12:00
Luke D. Jones
31af8f9511 Use egui without wayland feature due to segfault 2023-04-25 14:44:31 +12:00
Luke D. Jones
8db783d9b4 Better handling of supergfx version check, aura config updates 2023-04-25 13:57:07 +12:00
Luke D. Jones
45a354880a Add support for GV604 LEDs 2023-04-25 12:13:20 +12:00
Luke D. Jones
ca1c67e803 Begin fixing up support of basic modes + supergfx 2023-04-25 10:27:09 +12:00
Luke D. Jones
c819fa458a Optimise keyboard detection 2023-04-24 22:23:42 +12:00
Luke D. Jones
869ab90299 Add 0x18c6 keyboard 2023-04-24 20:54:51 +12:00
Luke D. Jones
c40029f5e7 Merge branch 'fluke/18c6-keyboard' 2023-04-24 20:15:11 +12:00
Luke Jones
e864dfb0e7 Merge branch 'feature/persistent-theme' into 'main'
Persistent dark / light mode

See merge request asus-linux/asusctl!160
2023-04-22 21:29:36 +00:00
Filip
476b394add Persistent dark / light mode 2023-04-22 21:29:36 +00:00
Luke D. Jones
4ea5480e66 Add support for GX650P LED modes 2023-04-21 11:01:54 +12:00
Luke Jones
cfc46a2b70 Merge branch 'guv604vi-support' into 'main'
Add led modes for GU604VI

See merge request asus-linux/asusctl!159
2023-04-20 22:32:50 +00:00
bno1
235763a615 Add led modes for GU604V 2023-04-21 00:36:00 +03:00
Luke D. Jones
6e19c16e70 Begin adding 18c6 keyboard support
This keyboard is found in ROG Flow Z13 machines. A kernel patch is also
required for full support.

Addresses #344.
2023-04-19 12:08:30 +12:00
Luke D. Jones
6ea550b6ff Update egui and supergfxctl deps
Requires running with supergfxctl v5.1.0-RC5 if installed
2023-04-19 10:12:14 +12:00
Luke Jones
dd30c8092b Merge branch 'pr-makefile-split' into 'main'
Makefile: split install into install-{program,data}

See merge request asus-linux/asusctl!158
2023-04-10 08:04:40 +00:00
Cole Mickens
2bd751f841 Makefile: split install into install-{program,data} 2023-04-09 18:44:55 -05:00
Luke Jones
7a6aafded7 Merge branch 'fluke/fix-anime-loops' into 'main'
Bugfix: Adjust how sub-llops and part of anime animation handle

See merge request asus-linux/asusctl!157
2023-04-08 03:40:32 +00:00
Luke Jones
940b93a75f Merge branch 'zpl/fix-anime-loops_fix' into 'fluke/fix-anime-loops'
fix: sleep-animation

See merge request asus-linux/asusctl!156
2023-04-04 21:36:48 +00:00
Armas Spann
5c70fec29a fix: sleep-animation 2023-04-04 01:20:25 +02:00
Luke Jones
8ac505e0dd Merge branch 'main' into 'main'
Add support for FX506LH keyboard layout

See merge request asus-linux/asusctl!155
2023-04-03 08:13:07 +00:00
Luke D. Jones
3bdb03b1d8 Bugfix: Adjust how sub-llops and part of anime animation handle
Attempts to address #332
2023-04-03 20:01:06 +12:00
Winfried
4ac4909881 Add support for FX506LH keyboard layout 2023-03-29 15:12:03 +03:00
Luke D. Jones
ec5e6d2e7c Add support for G533Z keyboard and modes
Closes #327
2023-03-20 08:52:23 +01:00
Luke D. Jones
5600c51ba0 Fix remove the leftover initial config writes on new() for some controllers
Closes #320
2023-01-25 09:27:12 +13:00
Luke D. Jones
cb5856c4dc Update fedora build instruction 2023-01-18 20:40:25 +13:00
Luke Jones
aad6f6350b Update README.md 2023-01-17 20:54:46 +00:00
Luke D. Jones
1ec45a6449 Update gitlab CI 2023-01-16 15:17:37 +13:00
Luke Jones
bb612283fe Update .gitlab-ci.yml file 2023-01-16 02:00:34 +00:00
Luke Jones
bcba11d4ec Update .gitlab-ci.yml file 2023-01-16 01:59:50 +00:00
Luke Jones
7eab94bc7f Update .gitlab-ci.yml file 2023-01-16 01:57:55 +00:00
Luke D. Jones
e73bbedb41 Properly enable pipeline cache? 2023-01-16 14:50:50 +13:00
Luke D. Jones
a83ccbd33d Add git hooks via cargo-husky. Many many cleanups. 2023-01-16 13:23:30 +13:00
Luke Jones
b5b7799018 Merge branch 'readme-popos-instructions-typo-fix' into 'main'
Fixed typo in the instructions for Pop_OS installation commands

See merge request asus-linux/asusctl!152
2023-01-15 21:04:36 +00:00
Andres Sanchez
24ecb92621 Fixed typo in the instructions for Pop_OS installation commands 2023-01-15 11:41:10 -05:00
Luke Jones
a811417f5d Merge branch 'fluke/aura_advanced' into 'main'
Fluke/aura advanced

See merge request asus-linux/asusctl!151
2023-01-15 09:36:03 +00:00
Luke D. Jones
b012a01cad Update deps, prep RC 2023-01-15 22:27:44 +13:00
Luke D. Jones
e6a9c88695 RCC: Adjust check for dgpu status change 2023-01-11 18:17:45 +13:00
Luke Jones
048a7afa55 Merge branch 'fluke/aura_advanced' into 'main'
Convert repeated code in config-traits to a macro

See merge request asus-linux/asusctl!149
2023-01-09 10:32:54 +00:00
Luke D. Jones
53b854ef6d Convert repeated code in config-traits to a macro 2023-01-09 23:19:29 +13:00
Luke Jones
fac635d07d Merge branch 'fluke/aura_advanced' into 'main'
Fluke/aura advanced

See merge request asus-linux/asusctl!148
2023-01-08 09:28:23 +00:00
Luke D. Jones
9cc62d63c9 Add checks to rename configs if required 2023-01-08 22:00:32 +13:00
Luke D. Jones
ab3007d53d daemon-user: refactor config files 2023-01-08 21:23:16 +13:00
Luke D. Jones
00839aaa6f Refactor config_trait crate and add doc comment examples 2023-01-08 20:41:17 +13:00
Luke D. Jones
5133d398eb Add extra doc comments to config-trait 2023-01-07 21:20:53 +13:00
Luke D. Jones
90b711c7b9 Break config-traits out in to crate 2023-01-07 20:46:00 +13:00
Luke D. Jones
ea5e5db490 ROGCC: add note re: aura in gui 2023-01-07 12:02:58 +13:00
Luke D. Jones
ef6ca9e51e Add support for GL703GE keyboard layout 2023-01-07 11:56:56 +13:00
Luke D. Jones
022a144705 Fix profile controller not detecting if platform_profile is changed
Closes #313
2023-01-07 11:45:01 +13:00
Luke Jones
8011ba3009 Merge branch 'fluke/config-traits' into 'main'
Fluke/config traits

See merge request asus-linux/asusctl!147
2023-01-06 06:57:07 +00:00
Luke D. Jones
d93b870726 Split fan-curve config to own file 2023-01-06 19:47:42 +13:00
Luke D. Jones
e4f79a3e6f Config files use generic traits 2023-01-06 12:03:12 +13:00
Luke Jones
54273cfb60 Merge branch 'main' into 'main'
Parameter fix for 'asusctl bios --help' (asus-linux/asusctl#299)

See merge request asus-linux/asusctl!146
2023-01-05 07:11:12 +00:00
Luke D. Jones
0dd4b2d6b5 Update readme with popos build instructions
Closes #302
2023-01-05 20:08:39 +13:00
Luke D. Jones
a2d850bbcb Update readme with new build requirements 2023-01-05 20:06:17 +13:00
Luke D. Jones
19f82493de Better config fie handling for the asusd daemon
Should address #304
2023-01-05 19:56:51 +13:00
Luke D. Jones
cbce854d1b Format 2023-01-04 09:34:56 +13:00
Luke D. Jones
ee0600d50d Adjust service file 2023-01-03 20:21:14 +13:00
Luke D. Jones
3d145ab9bd Slightly adjust keyboard widget 2023-01-03 20:21:14 +13:00
Luke D. Jones
1cbffedaeb Advanced Aura feature
Groundwork for 'advanced' aura modes
Add single zone + Doom light flash
Fix mocking for ROGCC
Better prepare & change to mapping of keyboard layouts to models and functions
Refactor and begin using new key layout stuff
Enable first arg to rogcc to set layout in mocking feature mode
Complete refactor of key layouts, and to RON serde
2023-01-03 20:21:11 +13:00
Luke D. Jones
e3ecaa92bd Add disable_nvidia_powerd_on_battery option 2023-01-03 20:17:52 +13:00
Luke D. Jones
067738b94f Fix pipeline 2022-12-28 21:34:16 +13:00
Luke D. Jones
29b22cd18e Fix incorrect stop/start order of nvidia-powerd on AC plug/unplug 2022-12-28 21:30:29 +13:00
Luke D. Jones
c2aa81bfe3 asusd: fixing a blocking op 2022-12-25 22:22:52 +13:00
mclang
5e9c612269 Fixes 'asusctl bios --help' (issue #299) 2022-12-19 20:33:44 +02:00
Luke D. Jones
8dcb209026 ROGCC: Don't notify user if changing to same mux mode 2022-12-10 21:42:52 +13:00
Luke D. Jones
bdb6c5b2ff Prep 4.5.6 release 2022-12-10 21:08:52 +13:00
Luke D. Jones
a318fbceec asusd: check if nvidia-powerd enabled before toggling 2022-12-10 21:05:27 +13:00
Luke D. Jones
8feacf863a asusd: Very basic support for running a command on AC/Battery switching 2022-12-10 20:51:00 +13:00
Luke D. Jones
0c62582515 ROGCC: Very basic support for running a command on AC/Battery switching 2022-12-10 20:17:45 +13:00
Luke D. Jones
3c575e4d2a ROGCC: Minor correction to tray menu 2022-12-10 19:37:20 +13:00
Luke D. Jones
dbfd73da5e ROGCC: Better handle the use of GPU MUX without supergfxd 2022-12-10 19:30:30 +13:00
Luke D. Jones
b1ee449b97 Adjust profile task to help TUF laptops notify 2022-12-09 10:03:45 +13:00
Luke D. Jones
245c035dc9 Fix tasks not always running correctly on boot/sleep/wake/shutdown 2022-12-08 20:12:55 +13:00
Luke D. Jones
07daa0df61 Fix: ROGCC: show option for LED notifications 2022-12-08 16:27:00 +13:00
Luke D. Jones
c7893b16f9 Fix: ROGCC: Remove unwrap causing panic on main thread
Closes #293
2022-12-08 11:14:01 +13:00
Luke Jones
8e8681c190 Merge branch 'main' into 'main'
add led modes for FX506HC

See merge request asus-linux/asusctl!144
2022-12-07 20:29:23 +00:00
HerrWinfried
b26c6a55f0 add led modes for FX506HC 2022-12-07 11:41:49 +00:00
Luke D. Jones
93d472fe74 Use correct defaults for GfxMode and GfxPower 2022-12-07 12:31:52 +13:00
Luke D. Jones
5469c73f11 Adjust gitlab pipeline to ignore checks for tags 2022-12-07 11:55:09 +13:00
Luke D. Jones
ad95765954 Add missing files 2022-12-07 11:50:17 +13:00
Luke D. Jones
e42a5bc3e9 ROGCC: don't require supergfxd to be running
Prep fixes for new tag and release
2022-12-07 11:47:27 +13:00
Luke D. Jones
28347e87eb Prep new minor release 2022-12-06 20:10:03 +13:00
Luke D. Jones
b34cb672c3 Fix: ROGCC: log and show more errors on startup 2022-12-06 14:28:35 +13:00
Luke D. Jones
559ddc9a22 Fix: ROGCC: remove unused arg in fan curve widget 2022-12-06 10:08:11 +13:00
Luke D. Jones
a8c014881f Version bump for RC 2022-12-06 09:48:24 +13:00
Luke D. Jones
f417032ed9 Fix: ROGCC: apply changes to correct fan curve profile
The fan curve profile changes were applying to the currently *active*
profile and not the GUI selected profile being changed. Fixed.

Also clarify the buttons for fan curve apply.
2022-12-06 09:47:48 +13:00
Luke D. Jones
616fb3aea6 chore: cranky cleanups 2022-12-05 20:31:39 +13:00
Luke D. Jones
6e6e057995 Update changelog 2022-12-05 19:44:50 +13:00
Luke D. Jones
085e63ebab Merge commit 'fdadffcdde82' because of borked HEAD after pull 2022-12-05 19:44:08 +13:00
Luke D. Jones
fdadffcdde Fix: ROGCC: Correctly deny badly formed fan graphs
Closes #286
2022-12-05 19:40:00 +13:00
Luke Jones
5f51527dd7 Merge branch 'piivanov-main-patch-89025' into 'main'
Add led modes for G713RM

See merge request asus-linux/asusctl!143
2022-12-04 20:53:46 +00:00
Peter Ivanov
39525980a0 Add led modes for G713RM 2022-12-04 17:39:32 +00:00
Luke D. Jones
83a0b570e0 Cause great pain to self with cargo-deny + cargo-cranky 2022-12-04 22:10:08 +13:00
Luke D. Jones
2bfbce36b0 Bump dependencies 2022-12-04 22:09:28 +13:00
Luke D. Jones
2705b08dca Cause great pain to self with cargo-deny + cargo-cranky 2022-12-04 21:49:47 +13:00
Luke D. Jones
2fca7a09c4 bump dependencies 2022-12-04 20:16:33 +13:00
Luke Jones
ef0da62c55 Merge branch 'maxbachmann-main-patch-99498' into 'main'
add led modes for G513RM

See merge request asus-linux/asusctl!141
2022-12-04 01:48:10 +00:00
maxbachmann
9dab120bcf add led modes for G513RM 2022-12-03 22:03:35 +00:00
165 changed files with 12972 additions and 7474 deletions

12
.cargo-husky/hooks/pre-commit Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
set -e
echo '+cargo +nightly fmt --all -- --check'
cargo +nightly fmt --all -- --check
echo '+cargo clippy --all -- -D warnings'
cargo clippy --all -- -D warnings
echo '+cargo test --all'
cargo test --all
echo '+cargo cranky'
cargo cranky

10
.cargo-husky/hooks/pre-push Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
set -e
echo '+cargo +nightly fmt --all -- --check'
cargo +nightly fmt --all -- --check
echo '+cargo clippy --all -- -D warnings'
cargo clippy --all -- -D warnings
echo '+cargo cranky'
cargo cranky

23
.editorconfig Normal file
View File

@@ -0,0 +1,23 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.rs]
indent_size = 4
[tests/**/*.rs]
charset = utf-8
end_of_line = unset
indent_size = unset
indent_style = unset
trim_trailing_whitespace = unset
insert_final_newline = unset

5
.gitignore vendored
View File

@@ -2,7 +2,10 @@
vendor.tar.xz vendor.tar.xz
cargo-config cargo-config
.idea .idea
vendor
vendor-* vendor-*
vendor_* vendor_*
.vscode-ctags .vscode-ctags
.vscode .vscode
.~lock.*
*.ods#

View File

@@ -1,28 +1,84 @@
image: rust:latest image: rust:latest
.rust_cache: &rust_cache
cache:
# key: $CI_COMMIT_REF_SLUG
paths:
# Don't include `incremental` to save space
# Debug
- target/debug/build/
- target/debug/deps/
- target/debug/.fingerprint/
- target/debug/.cargo-lock
# Release
- target/release/build/
- target/release/deps/
- target/release/.fingerprint/
- target/release/.cargo-lock
before_script: before_script:
- apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev - apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev grep llvm clang libclang-dev
stages: stages:
- test - format
- build - check
- test
- release
- deploy
test: format:
except:
- tags
<<: *rust_cache
script:
- echo "nightly" > rust-toolchain
- rustup component add rustfmt
- cargo fmt --check
check:
except:
- tags
<<: *rust_cache
script: script:
- rustup component add clippy - rustup component add clippy
- cargo check - cargo check
- cargo clippy # deny currently catches too much
#- cargo install cargo-deny && cargo deny
- cargo install cargo-cranky && cargo cranky
test:
except:
- tags
<<: *rust_cache
script:
- mkdir -p .git/hooks > /dev/null
- cargo test - cargo test
build: release:
only: only:
- main - tags
<<: *rust_cache
script: script:
- make && make vendor - make && make vendor
artifacts: artifacts:
paths: paths:
- vendor_asus-nb-ctrl_*.tar.xz - vendor_asusctl_*.tar.xz
- cargo-config - cargo-config
pages:
stage: deploy
only:
- tags
<<: *rust_cache
script:
- cargo doc --document-private-items --no-deps --workspace
- rm -rf public
- mkdir public
- cp -R target/doc/* public
- cp misc/index.html public
artifacts:
paths:
- public
variables: variables:
GIT_SUBMODULE_STRATEGY: normal GIT_SUBMODULE_STRATEGY: normal

View File

@@ -5,6 +5,108 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
## [v4.6.1]
### Added
- Support for G733Z LED modes
- Support for GU604V LED modes
- Support for GX650P LED modes
- Support for GV604I LED modes
- Support for FX516P LED modes (this laptop still has further issues, will require a patched kernel when patch is ready)
- Add device code for the Z13 ACRNM keyboard (requires kernel patch, in progress)
- Support for GV301VIC LED modes
- Add device code for the plain Z13 keyboard (requires kernel patch, in progress)
- Support for GV301V LED modes
### Changed
- Adjustments to Anime system events thread
- Add "sleep" animetion config options to anime config
- rog-control-center dark/light mode persistency
- Adjustments to keyboard detection
- Better support of using supergfxctl when available (tray icon and menu)
- Check supergfx version before enabling use in tray (require 5.1.0+)
- Update allowed Aura modes on asusd restart if changed
- Set tray icon for dgpu to "On" if in Vfio mode to prevent confusion
- Add support for Logout/Reboot in notification for KDE
## [v4.6.0]
### Added
- Support for GL703GE keyboard layout
- Support for G533Z modes and keyboard layout
### Changed
- Better handling of `/etc/asusd` not existing
- Better handling of non-existant config files
- Move all config handling to generic traits for better consistency
- Re-parse all configs to RON format
- Move fan-curve config to own config file
- Added option to set `disable_nvidia_powerd_on_battery`
- Add short log entry to throttle_thermal_policy change detection
- ROGCC: Don't notify user if changing to same mux mode
- ROGCC: Add CLI opt for loading a keyboard layout for testing, with live-reload on file change
- ROGCC: Add CLI opt for viewing all layout files + filenames to help find a layout matching your laptop
+ Both of these options would hopefully be temporary and replaced with a "wizard" GUI helper
- Fix profile controller not detecting if platform_profile is changed
- Fix remove the leftover initial config writes on `new()` for some controllers to prevent resetting settings on startup
+ refactor the loading of systemd curve defaults and config file
### BREAKING
- Rename aura dbus method from `per_key_raw` to `direct_addressing_raw` and add doc comment
- Changes to aura.conf:
- Changes to asusd-ledmodes.toml:
+ Rename `standard` to `basic_modes`
+ Rename `multizone` to `basic_zones`
+ Raname `per_key` to `advanced` and change type from `bool` to `AdvancedAuraType`
+ Removed `prod_family`
+ Split all entries to `board_name` (separating `board_names`) (now a huge file)
+ removed `asusd-ledmodes.toml` in favour of `aura_support.ron` due to an unsupported type in toml
- Rename and adjust `LedSupportedFunctions` to closely match the above
## [v4.5.8]
### Changed
- Fix incorrect stop/start order of nvidia-powerd on AC plug/unplug
## [v4.5.7]
### Changed
- ROGCC: Don't notify user if changing to same mux mode
-
## [v4.5.7]
### Changed
- ROGCC: Don't notify user if changing to same mux mode
- asusd: don't block on systemd-unit change: removes all shoddy external command calls in favour of async dbus calls
## [v4.5.6]
### Changed
- Fix tasks not always running correctly on boot/sleep/wake/shutdown by finishing the move to async
- Change how the profile/fan change task monitors changes due to TUF laptops behaving slightly different
- ROGCC: Better handle the use of GPU MUX without supergfxd
- ROGCC: Track if reboot required when not using supergfxd
- Add env var for logging levels to daemon and gui (`RUST_LOG=<error|warn|info|debug|trace>`)
- ROGCC: Very basic support for running a command on AC/Battery switching, this is in config at `~/.config/rog/rog-control-center.cfg`, and for now must be edited by hand and ROGCC restarted (run ROGCC in BG to use effectively)
+ Run ROGCC from terminal to see errors of the AC/Battery command
+ Support for editing via ROGCC GUI will come in future
+ This is ideal for userspace tasks
- asusd: Very basic support for running a command on AC/Battery switching, this is in config at `/etc/asusd/asusd.conf`. A restart of asusd is not required if edited.
+ This is ideal for tasks that require root access (BE SAFE!)
- The above AC/Battery commands are probably best set to run a script for more complex tasks
- asusd: check if nvidia-powerd enabled before toggling
## [v4.5.5]
### Changed
- remove an unwrap() causing panic on main ROGCC thread
## [v4.5.4]
### Changed
- ROGCC:: Allow ROGCC to run without supergfxd
- ROGCC: Tray/notifs now reads dGPU status directly via supergfx crate (supergfxd not required)
- Add rust-toolchain to force minimum rust version
## [v4.5.3]
### Changed
- Adjust how fan graph in ROGCC works, deny incorrect graphs
- Fix to apply the fan curve change in ROGCC to the correct profile
- Support for G713RS LED modes (Author: Peter Ivanov)
- Support for G713RM LED modes (Author: maxbachmann)
- Fix VivoBook detection
- Update dependencies to get latest winit crate (fixes various small issues)
## [v4.5.2] ## [v4.5.2]
### Changed ### Changed
- Update dependencies and bump version - Update dependencies and bump version
@@ -528,6 +630,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix small deadlock with awaits - Fix small deadlock with awaits
## [1.0.0] - 2020-08-13 ## [1.0.0]
- Major fork and refactor to use asus-hid patch for ASUS N-Key device

2108
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,38 +1,41 @@
[workspace] [workspace]
members = ["asusctl", "daemon", "daemon-user", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"] members = ["asusctl", "config-traits", "daemon", "daemon-user", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
[workspace.package] [workspace.package]
version = "4.5.2" version = "4.6.1"
[workspace.dependencies] [workspace.dependencies]
async-trait = "^0.1" async-trait = "^0.1"
tokio = { version = "^1.21.2", features = ["macros", "rt-multi-thread", "time", "sync"]} tokio = { version = "^1.23.0", features = ["macros", "rt-multi-thread", "time", "sync"]}
concat-idents = "^1.1" concat-idents = "^1.1"
dirs = "^4.0" dirs = "^4.0"
smol = "^1.2" smol = "^1.3"
zbus = "^3.5" zbus = "^3.6"
logind-zbus = { version = "^3.0.3" } #, default-features = false, features = ["non_blocking"] } logind-zbus = { version = "^3.1.0" } #, default-features = false, features = ["non_blocking"] }
serde = "^1.0" serde = "^1.0"
serde_derive = "^1.0" serde_derive = "^1.0"
serde_json = "^1.0" serde_json = "^1.0"
toml = "^0.5.9" toml = "^0.5.10"
ron = "*"
log = "^0.4" log = "^0.4"
env_logger = "^0.9.3" env_logger = "^0.10.0"
glam = { version = "^0.22", features = ["serde"] } glam = { version = "^0.22", features = ["serde"] }
gumdrop = "^0.8" gumdrop = "^0.8"
udev = "^0.6" udev = "^0.7"
rusb = "^0.9" rusb = "^0.9"
sysfs-class = "^0.1.2" sysfs-class = "^0.1.3"
inotify = "^0.10.0" inotify = "^0.10.0"
png_pong = "^0.8" png_pong = "^0.8"
pix = "^0.13" pix = "^0.13"
tinybmp = "^0.3" tinybmp = "^0.4.0"
gif = "^0.11" gif = "^0.12.0"
versions = "4.1"
notify-rust = { git = "https://github.com/flukejones/notify-rust.git", default-features = false, features = ["z"] } notify-rust = { git = "https://github.com/flukejones/notify-rust.git", default-features = false, features = ["z"] }
@@ -51,3 +54,8 @@ opt-level = 1
[profile.bench] [profile.bench]
debug = false debug = false
opt-level = 3 opt-level = 3
[workspace.dependencies.cargo-husky]
version = "1"
default-features = false
features = ["user-hooks"]

121
Cranky.toml Normal file
View File

@@ -0,0 +1,121 @@
# https://github.com/ericseppanen/cargo-cranky
# cargo install cargo-cranky && cargo cranky
error = [
"clippy::all",
"clippy::await_holding_lock",
"clippy::bool_to_int_with_if",
"clippy::char_lit_as_u8",
"clippy::checked_conversions",
"clippy::dbg_macro",
"clippy::debug_assert_with_mut_call",
"clippy::disallowed_methods",
"clippy::disallowed_script_idents",
"clippy::doc_link_with_quotes",
"clippy::doc_markdown",
"clippy::empty_enum",
"clippy::enum_glob_use",
"clippy::equatable_if_let",
"clippy::exit",
"clippy::expl_impl_clone_on_copy",
"clippy::explicit_deref_methods",
"clippy::explicit_into_iter_loop",
"clippy::explicit_iter_loop",
"clippy::fallible_impl_from",
"clippy::filter_map_next",
"clippy::flat_map_option",
"clippy::float_cmp_const",
"clippy::fn_params_excessive_bools",
"clippy::fn_to_numeric_cast_any",
"clippy::from_iter_instead_of_collect",
"clippy::if_let_mutex",
"clippy::implicit_clone",
"clippy::imprecise_flops",
"clippy::index_refutable_slice",
"clippy::inefficient_to_string",
"clippy::invalid_upcast_comparisons",
"clippy::iter_not_returning_iterator",
"clippy::iter_on_empty_collections",
"clippy::iter_on_single_items",
"clippy::large_digit_groups",
"clippy::large_stack_arrays",
"clippy::large_types_passed_by_value",
"clippy::let_unit_value",
"clippy::linkedlist",
"clippy::lossy_float_literal",
"clippy::macro_use_imports",
"clippy::manual_assert",
"clippy::manual_instant_elapsed",
"clippy::manual_ok_or",
"clippy::manual_string_new",
"clippy::map_err_ignore",
"clippy::map_flatten",
"clippy::map_unwrap_or",
"clippy::match_on_vec_items",
"clippy::match_same_arms",
"clippy::match_wild_err_arm",
"clippy::match_wildcard_for_single_variants",
"clippy::mem_forget",
"clippy::mismatched_target_os",
"clippy::mismatching_type_param_order",
"clippy::missing_enforced_import_renames",
# "clippy::missing_errors_doc",
"clippy::missing_safety_doc",
"clippy::mut_mut",
"clippy::mutex_integer",
"clippy::needless_borrow",
"clippy::needless_continue",
"clippy::needless_for_each",
"clippy::needless_pass_by_value",
"clippy::negative_feature_names",
"clippy::nonstandard_macro_braces",
"clippy::option_option",
"clippy::path_buf_push_overwrite",
"clippy::ptr_as_ptr",
"clippy::rc_mutex",
"clippy::ref_option_ref",
"clippy::rest_pat_in_fully_bound_structs",
"clippy::same_functions_in_if_condition",
"clippy::semicolon_if_nothing_returned",
"clippy::single_match_else",
"clippy::str_to_string",
"clippy::string_add_assign",
"clippy::string_add",
"clippy::string_lit_as_bytes",
"clippy::string_to_string",
"clippy::todo",
"clippy::trailing_empty_array",
"clippy::trait_duplication_in_bounds",
"clippy::unimplemented",
"clippy::unnecessary_wraps",
"clippy::unnested_or_patterns",
"clippy::unused_peekable",
"clippy::unused_rounding",
# "clippy::unused_self",
"clippy::useless_transmute",
"clippy::verbose_file_reads",
"clippy::zero_sized_map_values",
"elided_lifetimes_in_paths",
"future_incompatible",
"nonstandard_style",
"rust_2018_idioms",
"rust_2021_prelude_collisions",
"rustdoc::missing_crate_level_docs",
"semicolon_in_expressions_from_macros",
"trivial_numeric_casts",
"unused_extern_crates",
"unused_import_braces",
"unused_lifetimes",
]
allow = [
# TODO(emilk): enable more lints
"clippy::cloned_instead_of_copied",
"clippy::derive_partial_eq_without_eq",
"clippy::type_complexity",
"clippy::undocumented_unsafe_blocks",
"trivial_casts",
"unsafe_op_in_unsafe_fn", # `unsafe_op_in_unsafe_fn` may become the default in future Rust versions: https://github.com/rust-lang/rust/issues/71668
"unused_qualifications",
]

191
MANUAL.md
View File

@@ -47,35 +47,7 @@ The LED controller (e.g, aura) enables setting many of the factory modes availab
#### Supported laptops #### Supported laptops
Models GA401, GA502, GU502 support LED brightness change only (no RGB). However the GA401Q model can actually use three modes; static, breathe, and pulse, plus also use red to control the LED brightness intensity. There are over 60 supported laptops as of 01-01-2023. Please see [the rog-aura crate readme for further details](/rog-aura/README.md).
All models that have any form of LED mode control need to be enabled via the config file at `/etc/asusd/asusd-ledmodes.toml`. Unfortunately ASUS doesn't provide any easy way to find all the supported modes for all laptops (not even through Armory Crate and its various files, that progrma downloads only the required settings for the laptop it runs on) so each model must be added as needed.
#### Config options
The defaults are located at `/etc/asusd/asusd-ledmodes.toml`, and on `asusd` start it creates `/etc/asusd/aura.conf` whcih stores the per-mode settings. If you edit the defaults file you must remove `/etc/asusd/aura.conf` and restart `asusd.service` with `systemctl restart asusd`.
##### /etc/asusd/asusd-ledmodes.toml
Example:
```toml
[[led_data]]
prod_family = "Strix"
board_names = ["GL504G"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4", "Logo", "BarLeft", "BarRight"]
per_key = false
```
1. `prod_family`: you can find this in `journalctl -b -u asusd`, or `cat /sys/class/dmi/id/product_name`. It should be copied as written. There can be multiple `led-data` groups of the same `prod_family` with differing `board_names`.
2. `board_names`: is an array of board names in this product family. Find this in the journal as above or by `cat /sys/class/dmi/id/board_name`.
3. `standard` are the factory preset modes, the names should corrospond to Armory Crate names
4. `multizone`: some models have 4 to 7 zones of LED control as shown in the example. If the laptop has no zones then an empty array will suffice.
5. `per_key`: enable per-key RGB effects. The keyboard must support this or it has no effect.
##### /etc/asusd/aura.conf
This file can be manually edited if desired, but the `asusctl` CLI tool, or dbus methods are the preferred method. Any manual changes to this file mean that the `asusd.service` will need to be restarted, or you need to cycle between modes to force a reload.
### Charge control ### Charge control
@@ -145,87 +117,100 @@ I'm unsure of how many laptops this works on, so please try it.
An Aura config itself is a file with contents: An Aura config itself is a file with contents:
```json ```ron
{ (
"name": "aura-default", name: "aura-default",
"aura": [ aura: (
{ effects: [
"Breathe": { Breathe((
"led_type": { led: W,
"Key": "W" start_colour1: (255, 0, 20),
}, start_colour2: (20, 255, 0),
"start_colour1": [ speed: Low,
255, )),
0, Breathe((
20 led: A,
start_colour1: (255, 0, 20),
start_colour2: (20, 255, 0),
speed: Low,
)),
Breathe((
led: S,
start_colour1: (255, 0, 20),
start_colour2: (20, 255, 0),
speed: Low,
)),
Breathe((
led: D,
start_colour1: (255, 0, 20),
start_colour2: (20, 255, 0),
speed: Low,
)),
Breathe((
led: F,
start_colour1: (255, 0, 0),
start_colour2: (255, 0, 0),
speed: High,
)),
Static((
led: RCtrl,
colour: (0, 0, 255),
)),
Static((
led: LCtrl,
colour: (0, 0, 255),
)),
Static((
led: Esc,
colour: (0, 0, 255),
)),
DoomFlicker((
led: N9,
start_colour: (0, 0, 255),
max_percentage: 80,
min_percentage: 40,
)),
], ],
"start_colour2": [ zoned: false,
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>` If your laptop supports multizone, `"led"` can also be `"Zone": <one of the following>`
- `"None"` - `SingleZone` // Keyboards with only one zone
- `"KeyboardLeft"` - `ZonedKbLeft` // keyboard left
- `"KeyboardCenterLeft"` - `ZonedKbLeftMid` // keyboard left-middle
- `"KeyboardCenterRight"` - `ZonedKbRightMid` // etc
- `"KeyboardRight"` - `ZonedKbRight`
- `"LightbarRight"` - `LightbarRight`
- `"LightbarRightCorner"` - `LightbarRightCorner`
- `"LightbarRightBottom"` - `LightbarRightBottom`
- `"LightbarLeftBottom"` - `LightbarLeftBottom`
- `"LightbarLeftCorner"` - `LightbarLeftCorner`
- `"LightbarLeft"` - `LightbarLeft`
Single zone example:
```ron
(
name: "aura-default",
aura: (
effects: [
DoomFlicker((
led: SingleZone,
start_colour: (200, 40, 5),
max_percentage: 80,
min_percentage: 40,
)),
],
zoned: true,
),
)
```
At the moment there are only three effects available as shown in the example. More will come in the future 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. 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 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"` `~/.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"`

View File

@@ -1,4 +1,4 @@
VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' daemon/Cargo.toml | cut -d'"' -f2) VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d.\d.\d)"' Cargo.toml | cut -d'"' -f2)
INSTALL = install INSTALL = install
INSTALL_PROGRAM = ${INSTALL} -D -m 0755 INSTALL_PROGRAM = ${INSTALL} -D -m 0755
@@ -15,7 +15,7 @@ BIN_ROG := rog-control-center
BIN_C := asusctl BIN_C := asusctl
BIN_D := asusd BIN_D := asusd
BIN_U := asusd-user BIN_U := asusd-user
LEDCFG := asusd-ledmodes.toml LEDCFG := aura_support.ron
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs') SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
@@ -38,18 +38,20 @@ clean:
distclean: distclean:
rm -rf .cargo vendor vendor.tar.xz rm -rf .cargo vendor vendor.tar.xz
install: install-program:
$(INSTALL_PROGRAM) "./target/release/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)" $(INSTALL_PROGRAM) "./target/release/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)"
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).desktop" "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
cd rog-aura/data/layouts && find . -type f -name "*.toml" -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/rog-gui/layouts/{}" \;
$(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)" $(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)" $(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
$(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)" $(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
install-data:
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).desktop" "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"
$(INSTALL_DATA) "./rog-control-center/data/$(BIN_ROG).png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
cd rog-aura/data/layouts && find . -type f -name "*.ron" -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/rog-gui/layouts/{}" \;
$(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules" $(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
$(INSTALL_DATA) "./data/$(LEDCFG)" "$(DESTDIR)/etc/asusd/$(LEDCFG)" $(INSTALL_DATA) "./rog-aura/data/$(LEDCFG)" "$(DESTDIR)$(datarootdir)/asusd/$(LEDCFG)"
$(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf" $(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
$(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service" $(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
@@ -71,6 +73,8 @@ install:
cd rog-anime/data && find "./anime" -type f -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \; cd rog-anime/data && find "./anime" -type f -exec $(INSTALL_DATA) "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \;
install: install-program install-data
uninstall: uninstall:
rm -f "$(DESTDIR)$(bindir)/$(BIN_ROG)" rm -f "$(DESTDIR)$(bindir)/$(BIN_ROG)"
rm -r "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop" rm -r "$(DESTDIR)$(datarootdir)/applications/$(BIN_ROG).desktop"

View File

@@ -13,6 +13,8 @@ Now includes a GUI, `rog-control-center`.
**The minimum supported kernel version is 5.17** **The minimum supported kernel version is 5.17**
**For TUF laptops, the minimum supported kernel version is 6.1**
## Goals ## Goals
1. To provide an interface for rootless control of some system functions most users wish to control such as fan speeds, keyboard LEDs, graphics modes. 1. To provide an interface for rootless control of some system functions most users wish to control such as fan speeds, keyboard LEDs, graphics modes.
@@ -43,8 +45,6 @@ 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 then it may work without tweaks. Technically all other functions except the LED
and AniMe parts should work regardless of your latop make. and AniMe parts should work regardless of your latop make.
**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 ## Implemented
- [X] System daemon - [X] System daemon
@@ -74,14 +74,24 @@ Requirements are rust >= 1.57 installed from rustup.io if the distro provided ve
**Ubuntu (unsuported):** **Ubuntu (unsuported):**
apt install libclang-dev libudev-dev apt install libgtk-3-dev libpango1.0-dev libgdk-pixbuf-2.0-dev libglib2.0-dev cmake libclang-dev libudev-dev libayatana-appindicator3-1
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
make make
sudo make install sudo make install
**popos (unsuported):**
sudo apt install cmake libclang-dev libudev-dev libgtk-3-dev libclang-dev libglib2.0-dev libatkmm-1.6-dev libpangomm-1.4-dev librust-gdk-pixbuf-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
make
sudo make install
**fedora:** **fedora:**
dnf install cmake clang-devel systemd-devel gtk3-devel cargo dnf install cmake clang-devel systemd-devel glib2-devel cairo-devel atkmm-devel pangomm-devel gdk-pixbuf2-devel gtk3-devel libappindicator-gtk3
make make
sudo make install sudo make install
@@ -90,7 +100,7 @@ Requirements are rust >= 1.57 installed from rustup.io if the distro provided ve
Works with KDE Plasma (without GTK packages) Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis zypper in -t pattern devel_basis
zypper in rustup cmake clang-devel systemd-devel glib2-devel cairo-devel atkmm-devel pangomm-devel gdk-pixbuf-devel gtk3-devel zypper in rustup make cmake systemd-devel clang-devel llvm-devel gdk-pixbuf-devel cairo-devel pango-devel freetype-devel gtk3-devel libexpat-devel libayatana-indicator3-7
make make
sudo make install sudo make install
@@ -116,6 +126,10 @@ You may also need to activate the service for debian install. If running Pop!_OS
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`. Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
# Contributing
See `CONTRIBUTING.md`. Additionally, also do `cargo clean` and `cargo test` on first checkout to ensure the commit hooks are used (via `cargo-husky`).
# OTHER # OTHER
## Supporting more laptops ## Supporting more laptops

View File

@@ -1,5 +1,6 @@
[package] [package]
name = "asusctl" name = "asusctl"
license = "MPL-2.0"
authors = ["Luke D Jones <luke@ljones.dev>"] authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2021" edition = "2021"
version.workspace = true version.workspace = true
@@ -21,3 +22,5 @@ gif.workspace = true
tinybmp.workspace = true tinybmp.workspace = true
glam.workspace = true glam.workspace = true
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
cargo-husky.workspace = true

View File

@@ -1,6 +1,10 @@
use std::{env, error::Error, path::Path, process::exit}; use std::env;
use std::error::Error;
use std::path::Path;
use std::process::exit;
use rog_anime::{usb::get_anime_type, AnimeDiagonal, AnimeType}; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDiagonal, AnimeType};
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {

View File

@@ -1,6 +1,8 @@
use std::{thread::sleep, time::Duration}; use std::thread::sleep;
use std::time::Duration;
use rog_anime::{usb::get_anime_type, AnimeDiagonal, AnimeType}; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDiagonal, AnimeType};
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
// In usable data: // In usable data:
@@ -20,7 +22,7 @@ fn main() {
} }
for c in (0..35).into_iter().step_by(step) { for c in (0..35).into_iter().step_by(step) {
for i in matrix.get_mut()[c].iter_mut() { for i in &mut matrix.get_mut()[c] {
*i = 50; *i = 50;
} }
} }

View File

@@ -1,6 +1,9 @@
use std::{env, path::Path, thread::sleep}; use std::env;
use std::path::Path;
use std::thread::sleep;
use rog_anime::{usb::get_anime_type, ActionData, ActionLoader, Sequences}; use rog_anime::usb::get_anime_type;
use rog_anime::{ActionData, ActionLoader, Sequences};
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
fn main() { fn main() {

View File

@@ -1,7 +1,9 @@
use rog_anime::{usb::get_anime_type, AnimeDataBuffer, AnimeGrid};
use rog_dbus::RogDbusClientBlocking;
use std::convert::TryFrom; use std::convert::TryFrom;
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDataBuffer, AnimeGrid};
use rog_dbus::RogDbusClientBlocking;
// In usable data: // In usable data:
// Top row start at 1, ends at 32 // Top row start at 1, ends at 32
@@ -17,7 +19,6 @@ fn main() {
for (y, row) in tmp.iter_mut().enumerate() { for (y, row) in tmp.iter_mut().enumerate() {
if y % 2 == 0 && i + 1 != row.len() - 1 { if y % 2 == 0 && i + 1 != row.len() - 1 {
i += 1; i += 1;
dbg!(i);
} }
row[row.len() - i] = 0x22; row[row.len() - i] = 0x22;
if i > 5 { if i > 5 {

View File

@@ -1,4 +1,5 @@
use rog_anime::{usb::get_anime_type, AnimeDataBuffer}; use rog_anime::usb::get_anime_type;
use rog_anime::AnimeDataBuffer;
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
// In usable data: // In usable data:

View File

@@ -1,10 +1,11 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use std::{env, error::Error, path::Path, process::exit}; use std::env;
use std::error::Error;
use std::path::Path;
use std::process::exit;
use rog_anime::{ use rog_anime::usb::get_anime_type;
usb::get_anime_type, use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2};
AnimeDataBuffer, {AnimeImage, Vec2},
};
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {

View File

@@ -1,12 +1,14 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use std::{ use std::env;
env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration, use std::error::Error;
}; use std::f32::consts::PI;
use std::path::Path;
use std::process::exit;
use std::thread::sleep;
use std::time::Duration;
use rog_anime::{ use rog_anime::usb::get_anime_type;
usb::get_anime_type, use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2};
AnimeDataBuffer, {AnimeImage, Vec2},
};
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
@@ -35,7 +37,7 @@ fn main() -> Result<(), Box<dyn Error>> {
loop { loop {
matrix.angle += 0.05; matrix.angle += 0.05;
if matrix.angle > PI * 2.0 { if matrix.angle > PI * 2.0 {
matrix.angle = 0.0 matrix.angle = 0.0;
} }
matrix.update(); matrix.update();

View File

@@ -6,17 +6,17 @@ use rog_aura::{
KeyColourArray, KeyColourArray,
}; };
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
use std::collections::LinkedList; use std::collections::VecDeque;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Ball { struct Ball {
position: (f32, f32), position: (f32, f32),
direction: (f32, f32), direction: (f32, f32),
trail: LinkedList<(f32, f32)>, trail: VecDeque<(f32, f32)>,
} }
impl Ball { impl Ball {
fn new(x: f32, y: f32, trail_len: u32) -> Self { fn new(x: f32, y: f32, trail_len: u32) -> Self {
let mut trail = LinkedList::new(); let mut trail = VecDeque::new();
for _ in 1..=trail_len { for _ in 1..=trail_len {
trail.push_back((x, y)); trail.push_back((x, y));
} }
@@ -106,7 +106,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
c[2] = 255; c[2] = 255;
}; };
} }
dbus.proxies().led().per_key_raw(colours.get())?; dbus.proxies().led().direct_addressing_raw(colours.get())?;
std::thread::sleep(std::time::Duration::from_millis(150)); std::thread::sleep(std::time::Duration::from_millis(150));
} }

View File

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

View File

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

View File

@@ -1,44 +1,64 @@
//! Using a combination of key-colour array plus a key layout to generate outputs. //! 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_aura::advanced::LedCode;
use rog_aura::effects::{AdvancedEffects, Effect};
use rog_aura::layouts::KeyLayout;
use rog_aura::Colour;
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let layout = KeyLayout::gx502_layout(); let layout = KeyLayout::default_layout();
let (client, _) = RogDbusClientBlocking::new().unwrap(); let (client, _) = RogDbusClientBlocking::new().unwrap();
let mut seq = Sequences::new(); let mut seq = AdvancedEffects::new(true);
let zone = Effect::Breathe(Breathe::new( // let zone = Effect::Breathe(rog_aura::effects::Breathe::new(
LedType::Zone(PerZone::KeyboardLeft), // RgbAddress::Single,
Colour(166, 127, 166), // Colour(166, 127, 166),
Colour(127, 155, 20), // Colour(127, 155, 20),
Speed::High, // rog_aura::Speed::High,
// ));
// seq.push(zone);
// let zone = Effect::DoomLightFlash(rog_aura::effects::DoomLightFlash::new(
// RgbAddress::Single,
// Colour(200, 0, 0),
// 80,
// 10,
// ));
// seq.push(zone);
let zone = Effect::DoomFlicker(rog_aura::effects::DoomFlicker::new(
LedCode::SingleZone,
Colour(200, 110, 0),
100,
10,
)); ));
seq.push(zone); seq.push(zone);
let zone = Effect::Breathe(Breathe::new( // let zone = Effect::Breathe(rog_aura::effects::Breathe::new(
LedType::Zone(PerZone::KeyboardCenterLeft), // RgbAddress::KeyboardCenterLeft,
Colour(16, 127, 255), // Colour(16, 127, 255),
Colour(127, 15, 20), // Colour(127, 15, 20),
Speed::Low, // rog_aura::Speed::Low,
)); // ));
seq.push(zone); // seq.push(zone);
let zone = Effect::Breathe(Breathe::new( // let zone = Effect::Breathe(rog_aura::effects::Breathe::new(
LedType::Zone(PerZone::LightbarRightCorner), // RgbAddress::LightbarRightCorner,
Colour(0, 255, 255), // Colour(0, 255, 255),
Colour(255, 0, 255), // Colour(255, 0, 255),
Speed::Med, // rog_aura::Speed::Med,
)); // ));
seq.push(zone); // seq.push(zone);
loop { loop {
seq.next_state(&layout); seq.next_state(&layout);
let packets = seq.create_packets(); let packets = seq.create_packets();
client.proxies().led().per_key_raw(packets)?; client.proxies().led().direct_addressing_raw(packets)?;
std::thread::sleep(std::time::Duration::from_millis(60)); std::thread::sleep(std::time::Duration::from_millis(33));
} }
} }

View File

@@ -1,7 +1,9 @@
use gumdrop::Options;
use rog_aura::{error::Error, AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
use std::str::FromStr; use std::str::FromStr;
use gumdrop::Options;
use rog_aura::error::Error;
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
#[derive(Options)] #[derive(Options)]
pub struct LedPowerCommand1 { pub struct LedPowerCommand1 {
#[options(help = "print help message")] #[options(help = "print help message")]
@@ -105,7 +107,7 @@ impl ToString for LedBrightness {
Some(0x02) => "high", Some(0x02) => "high",
_ => "unknown", _ => "unknown",
}; };
s.to_string() s.to_owned()
} }
} }
@@ -220,7 +222,6 @@ pub struct MultiColourSpeed {
/// Byte value for setting the built-in mode. /// Byte value for setting the built-in mode.
/// ///
/// Enum corresponds to the required integer value /// Enum corresponds to the required integer value
///
// NOTE: The option names here must match those in rog-aura crate // NOTE: The option names here must match those in rog-aura crate
#[derive(Options)] #[derive(Options)]
pub enum SetAuraBuiltin { pub enum SetAuraBuiltin {

View File

@@ -1,10 +1,9 @@
use crate::{
anime_cli::AnimeCommand,
aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin},
profiles_cli::{FanCurveCommand, ProfileCommand},
};
use gumdrop::Options; use gumdrop::Options;
use crate::anime_cli::AnimeCommand;
use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin};
use crate::profiles_cli::{FanCurveCommand, ProfileCommand};
#[derive(Default, Options)] #[derive(Default, Options)]
pub struct CliStart { pub struct CliStart {
#[options(help_flag, help = "print help message")] #[options(help_flag, help = "print help message")]
@@ -71,7 +70,7 @@ pub struct BiosCommand {
meta = "", meta = "",
short = "S", short = "S",
no_long, no_long,
help = "set bios POST sound: asusctl -p <true/false>" help = "set bios POST sound: asusctl -S <true/false>"
)] )]
pub post_sound_set: Option<bool>, pub post_sound_set: Option<bool>,
#[options(no_long, short = "s", help = "read bios POST sound")] #[options(no_long, short = "s", help = "read bios POST sound")]

View File

@@ -1,17 +1,16 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use std::env::args;
use std::path::Path;
use std::process::Command; use std::process::Command;
use std::thread::sleep; use std::thread::sleep;
use std::{env::args, path::Path};
use aura_cli::{LedPowerCommand1, LedPowerCommand2};
use gumdrop::{Opt, Options};
use anime_cli::{AnimeActions, AnimeCommand}; use anime_cli::{AnimeActions, AnimeCommand};
use aura_cli::{LedPowerCommand1, LedPowerCommand2};
use gumdrop::{Opt, Options};
use profiles_cli::{FanCurveCommand, ProfileCommand}; use profiles_cli::{FanCurveCommand, ProfileCommand};
use rog_anime::usb::get_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, Vec2}; use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, Vec2};
use rog_aura::usb::{AuraDevRog1, AuraDevRog2, AuraDevTuf, AuraDevice, AuraPowerDev};
use rog_aura::usb::{AuraDev1866, AuraDev19b6, AuraDevTuf, AuraDevice, AuraPowerDev};
use rog_aura::{self, AuraEffect}; use rog_aura::{self, AuraEffect};
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
use rog_platform::platform::GpuMode; use rog_platform::platform::GpuMode;
@@ -26,7 +25,7 @@ mod aura_cli;
mod cli_opts; mod cli_opts;
mod profiles_cli; mod profiles_cli;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() {
let args: Vec<String> = args().skip(1).collect(); let args: Vec<String> = args().skip(1).collect();
let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k')); let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k'));
@@ -37,15 +36,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
..Default::default() ..Default::default()
}, },
Err(err) => { Err(err) => {
eprintln!("source {}", err); panic!("source {}", err);
std::process::exit(2);
} }
}; };
let (dbus, _) = RogDbusClientBlocking::new() let (dbus, _) = RogDbusClientBlocking::new()
.map_err(|e| { .map_err(|e| {
print_error_help(Box::new(e), None); print_error_help(&e, None);
std::process::exit(3); panic!("Could not start dbus client");
}) })
.unwrap(); .unwrap();
@@ -54,8 +52,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.supported() .supported()
.supported_functions() .supported_functions()
.map_err(|e| { .map_err(|e| {
print_error_help(Box::new(e), None); print_error_help(&e, None);
std::process::exit(4); panic!("Could not start dbus proxy");
}) })
.unwrap(); .unwrap();
@@ -63,17 +61,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
print_versions(); print_versions();
println!(); println!();
print_laptop_info(); print_laptop_info();
return Ok(());
} }
if let Err(err) = do_parsed(&parsed, &supported, &dbus) { if let Err(err) = do_parsed(&parsed, &supported, &dbus) {
print_error_help(err, Some(&supported)); print_error_help(&*err, Some(&supported));
} }
Ok(())
} }
fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) { fn print_error_help(err: &dyn std::error::Error, supported: Option<&SupportedFunctions>) {
check_service("asusd"); check_service("asusd");
println!("\nError: {}\n", err); println!("\nError: {}\n", err);
print_versions(); print_versions();
@@ -126,7 +121,7 @@ fn check_service(name: &str) -> bool {
fn do_parsed( fn do_parsed(
parsed: &CliStart, parsed: &CliStart,
supported: &SupportedFunctions, supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
match &parsed.command { match &parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?, Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?,
@@ -134,9 +129,9 @@ fn do_parsed(
Some(CliCommand::LedPow2(pow)) => handle_led_power2(dbus, &supported.keyboard_led, pow)?, Some(CliCommand::LedPow2(pow)) => handle_led_power2(dbus, &supported.keyboard_led, pow)?,
Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?, Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?,
Some(CliCommand::FanCurve(cmd)) => { Some(CliCommand::FanCurve(cmd)) => {
handle_fan_curve(dbus, &supported.platform_profile, cmd)? handle_fan_curve(dbus, &supported.platform_profile, cmd)?;
} }
Some(CliCommand::Graphics(_)) => do_gfx()?, Some(CliCommand::Graphics(_)) => do_gfx(),
Some(CliCommand::Anime(cmd)) => handle_anime(dbus, &supported.anime_ctrl, cmd)?, Some(CliCommand::Anime(cmd)) => handle_anime(dbus, &supported.anime_ctrl, cmd)?,
Some(CliCommand::Bios(cmd)) => handle_bios_option(dbus, &supported.rog_bios_ctrl, cmd)?, Some(CliCommand::Bios(cmd)) => handle_bios_option(dbus, &supported.rog_bios_ctrl, cmd)?,
None => { None => {
@@ -150,10 +145,10 @@ fn do_parsed(
println!("{}", CliStart::usage()); println!("{}", CliStart::usage());
println!(); println!();
if let Some(cmdlist) = CliStart::command_list() { if let Some(cmdlist) = CliStart::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect(); let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in commands.iter().filter(|command| { for command in commands.iter().filter(|command| {
if !matches!( if !matches!(
supported.keyboard_led.prod_id, supported.keyboard_led.dev_id,
AuraDevice::X1854 AuraDevice::X1854
| AuraDevice::X1869 | AuraDevice::X1869
| AuraDevice::X1866 | AuraDevice::X1866
@@ -162,7 +157,7 @@ fn do_parsed(
{ {
return false; return false;
} }
if supported.keyboard_led.prod_id != AuraDevice::X19B6 if supported.keyboard_led.dev_id != AuraDevice::X19b6
&& command.trim().starts_with("led-pow-2") && command.trim().starts_with("led-pow-2")
{ {
return false; return false;
@@ -214,14 +209,16 @@ fn do_parsed(
Ok(()) Ok(())
} }
fn do_gfx() -> Result<(), Box<dyn std::error::Error>> { fn do_gfx() {
println!("Please use supergfxctl for graphics switching. supergfxctl is the result of making asusctl graphics switching generic so all laptops can use it"); println!(
"Please use supergfxctl for graphics switching. supergfxctl is the result of making \
asusctl graphics switching generic so all laptops can use it"
);
println!("This command will be removed in future"); println!("This command will be removed in future");
Ok(())
} }
fn handle_anime( fn handle_anime(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
_supported: &AnimeSupportedFunctions, _supported: &AnimeSupportedFunctions,
cmd: &AnimeCommand, cmd: &AnimeCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -237,14 +234,14 @@ fn handle_anime(
} }
} }
if let Some(anime_turn) = cmd.enable { if let Some(anime_turn) = cmd.enable {
dbus.proxies().anime().set_on_off(anime_turn)? dbus.proxies().anime().set_on_off(anime_turn)?;
} }
if let Some(anime_boot) = cmd.boot_enable { if let Some(anime_boot) = cmd.boot_enable {
dbus.proxies().anime().set_boot_on_off(anime_boot)? dbus.proxies().anime().set_boot_on_off(anime_boot)?;
} }
if let Some(bright) = cmd.brightness { if let Some(bright) = cmd.brightness {
verify_brightness(bright); verify_brightness(bright);
dbus.proxies().anime().set_brightness(bright)? dbus.proxies().anime().set_brightness(bright)?;
} }
if cmd.clear { if cmd.clear {
let anime_type = get_anime_type()?; let anime_type = get_anime_type()?;
@@ -262,7 +259,7 @@ fn handle_anime(
if let Some(lst) = image.self_command_list() { if let Some(lst) = image.self_command_list() {
println!("\n{}", lst); println!("\n{}", lst);
} }
std::process::exit(1); return Ok(());
} }
verify_brightness(image.bright); verify_brightness(image.bright);
@@ -285,7 +282,7 @@ fn handle_anime(
if let Some(lst) = image.self_command_list() { if let Some(lst) = image.self_command_list() {
println!("\n{}", lst); println!("\n{}", lst);
} }
std::process::exit(1); return Ok(());
} }
verify_brightness(image.bright); verify_brightness(image.bright);
@@ -306,7 +303,7 @@ fn handle_anime(
if let Some(lst) = gif.self_command_list() { if let Some(lst) = gif.self_command_list() {
println!("\n{}", lst); println!("\n{}", lst);
} }
std::process::exit(1); return Ok(());
} }
verify_brightness(gif.bright); verify_brightness(gif.bright);
@@ -340,7 +337,7 @@ fn handle_anime(
if let Some(lst) = gif.self_command_list() { if let Some(lst) = gif.self_command_list() {
println!("\n{}", lst); println!("\n{}", lst);
} }
std::process::exit(1); return Ok(());
} }
verify_brightness(gif.bright); verify_brightness(gif.bright);
@@ -376,12 +373,11 @@ fn verify_brightness(brightness: f32) {
"Image and global brightness must be between 0.0 and 1.0 (inclusive), was {}", "Image and global brightness must be between 0.0 and 1.0 (inclusive), was {}",
brightness brightness
); );
std::process::exit(1);
} }
} }
fn handle_led_mode( fn handle_led_mode(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions, supported: &LedSupportedFunctions,
mode: &LedModeCommand, mode: &LedModeCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -393,9 +389,9 @@ fn handle_led_mode(
println!("Commands available"); println!("Commands available");
if let Some(cmdlist) = LedModeCommand::command_list() { if let Some(cmdlist) = LedModeCommand::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect(); let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in commands.iter().filter(|command| { for command in commands.iter().filter(|command| {
for mode in &supported.stock_led_modes { for mode in &supported.basic_modes {
if command if command
.trim() .trim()
.starts_with(&<&str>::from(mode).to_lowercase()) .starts_with(&<&str>::from(mode).to_lowercase())
@@ -403,7 +399,7 @@ fn handle_led_mode(
return true; return true;
} }
} }
if !supported.multizone_led_mode.is_empty() && command.trim().starts_with("multi") { if !supported.basic_zones.is_empty() && command.trim().starts_with("multi") {
return true; return true;
} }
false false
@@ -438,7 +434,7 @@ fn handle_led_mode(
} }
fn handle_led_power1( fn handle_led_power1(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions, supported: &LedSupportedFunctions,
power: &LedPowerCommand1, power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -456,14 +452,14 @@ fn handle_led_power1(
} }
if matches!( if matches!(
supported.prod_id, supported.dev_id,
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866 AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866
) { ) {
handle_led_power_1_do_1866(dbus, power)?; handle_led_power_1_do_1866(dbus, power)?;
return Ok(()); return Ok(());
} }
if matches!(supported.prod_id, AuraDevice::Tuf) { if matches!(supported.dev_id, AuraDevice::Tuf) {
handle_led_power_1_do_tuf(dbus, power)?; handle_led_power_1_do_tuf(dbus, power)?;
return Ok(()); return Ok(());
} }
@@ -473,13 +469,13 @@ fn handle_led_power1(
} }
fn handle_led_power_1_do_1866( fn handle_led_power_1_do_1866(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
power: &LedPowerCommand1, power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDev1866> = Vec::new(); let mut enabled: Vec<AuraDevRog1> = Vec::new();
let mut disabled: Vec<AuraDev1866> = Vec::new(); let mut disabled: Vec<AuraDevRog1> = Vec::new();
let mut check = |e: Option<bool>, a: AuraDev1866| { let mut check = |e: Option<bool>, a: AuraDevRog1| {
if let Some(arg) = e { if let Some(arg) = e {
if arg { if arg {
enabled.push(a); enabled.push(a);
@@ -489,11 +485,11 @@ fn handle_led_power_1_do_1866(
} }
}; };
check(power.awake, AuraDev1866::Awake); check(power.awake, AuraDevRog1::Awake);
check(power.boot, AuraDev1866::Boot); check(power.boot, AuraDevRog1::Boot);
check(power.sleep, AuraDev1866::Sleep); check(power.sleep, AuraDevRog1::Sleep);
check(power.keyboard, AuraDev1866::Keyboard); check(power.keyboard, AuraDevRog1::Keyboard);
check(power.lightbar, AuraDev1866::Lightbar); check(power.lightbar, AuraDevRog1::Lightbar);
let data = AuraPowerDev { let data = AuraPowerDev {
x1866: enabled, x1866: enabled,
@@ -513,7 +509,7 @@ fn handle_led_power_1_do_1866(
} }
fn handle_led_power_1_do_tuf( fn handle_led_power_1_do_tuf(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
power: &LedPowerCommand1, power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDevTuf> = Vec::new(); let mut enabled: Vec<AuraDevTuf> = Vec::new();
@@ -552,7 +548,7 @@ fn handle_led_power_1_do_tuf(
} }
fn handle_led_power2( fn handle_led_power2(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions, supported: &LedSupportedFunctions,
power: &LedPowerCommand2, power: &LedPowerCommand2,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -564,8 +560,8 @@ fn handle_led_power2(
println!("Commands available"); println!("Commands available");
if let Some(cmdlist) = LedPowerCommand2::command_list() { if let Some(cmdlist) = LedPowerCommand2::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect(); let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in commands.iter() { for command in &commands {
println!("{}", command); println!("{}", command);
} }
} }
@@ -580,13 +576,13 @@ fn handle_led_power2(
return Ok(()); return Ok(());
} }
if supported.prod_id != AuraDevice::X19B6 { if supported.dev_id != AuraDevice::X19b6 {
println!("This option applies only to keyboards with product ID 0x19b6") println!("This option applies only to keyboards with product ID 0x19b6");
} }
let mut enabled: Vec<AuraDev19b6> = Vec::new(); let mut enabled: Vec<AuraDevRog2> = Vec::new();
let mut disabled: Vec<AuraDev19b6> = Vec::new(); let mut disabled: Vec<AuraDevRog2> = Vec::new();
let mut check = |e: Option<bool>, a: AuraDev19b6| { let mut check = |e: Option<bool>, a: AuraDevRog2| {
if let Some(arg) = e { if let Some(arg) = e {
if arg { if arg {
enabled.push(a); enabled.push(a);
@@ -598,28 +594,28 @@ fn handle_led_power2(
match pow { match pow {
aura_cli::SetAuraEnabled::Boot(arg) => { aura_cli::SetAuraEnabled::Boot(arg) => {
check(arg.keyboard, AuraDev19b6::BootKeyb); check(arg.keyboard, AuraDevRog2::BootKeyb);
check(arg.logo, AuraDev19b6::BootLogo); check(arg.logo, AuraDevRog2::BootLogo);
check(arg.lightbar, AuraDev19b6::BootBar); check(arg.lightbar, AuraDevRog2::BootBar);
check(arg.lid, AuraDev19b6::AwakeLid); check(arg.lid, AuraDevRog2::AwakeLid);
} }
aura_cli::SetAuraEnabled::Sleep(arg) => { aura_cli::SetAuraEnabled::Sleep(arg) => {
check(arg.keyboard, AuraDev19b6::SleepKeyb); check(arg.keyboard, AuraDevRog2::SleepKeyb);
check(arg.logo, AuraDev19b6::SleepLogo); check(arg.logo, AuraDevRog2::SleepLogo);
check(arg.lightbar, AuraDev19b6::SleepBar); check(arg.lightbar, AuraDevRog2::SleepBar);
check(arg.lid, AuraDev19b6::SleepLid); check(arg.lid, AuraDevRog2::SleepLid);
} }
aura_cli::SetAuraEnabled::Awake(arg) => { aura_cli::SetAuraEnabled::Awake(arg) => {
check(arg.keyboard, AuraDev19b6::AwakeKeyb); check(arg.keyboard, AuraDevRog2::AwakeKeyb);
check(arg.logo, AuraDev19b6::AwakeLogo); check(arg.logo, AuraDevRog2::AwakeLogo);
check(arg.lightbar, AuraDev19b6::AwakeBar); check(arg.lightbar, AuraDevRog2::AwakeBar);
check(arg.lid, AuraDev19b6::AwakeLid); check(arg.lid, AuraDevRog2::AwakeLid);
} }
aura_cli::SetAuraEnabled::Shutdown(arg) => { aura_cli::SetAuraEnabled::Shutdown(arg) => {
check(arg.keyboard, AuraDev19b6::ShutdownKeyb); check(arg.keyboard, AuraDevRog2::ShutdownKeyb);
check(arg.logo, AuraDev19b6::ShutdownLogo); check(arg.logo, AuraDevRog2::ShutdownLogo);
check(arg.lightbar, AuraDev19b6::ShutdownBar); check(arg.lightbar, AuraDevRog2::ShutdownBar);
check(arg.lid, AuraDev19b6::ShutdownBar); check(arg.lid, AuraDevRog2::ShutdownBar);
} }
} }
@@ -646,7 +642,7 @@ fn handle_led_power2(
} }
fn handle_profile( fn handle_profile(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
supported: &PlatformProfileFunctions, supported: &PlatformProfileFunctions,
cmd: &ProfileCommand, cmd: &ProfileCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -664,7 +660,7 @@ fn handle_profile(
if let Some(lst) = cmd.self_command_list() { if let Some(lst) = cmd.self_command_list() {
println!("\n{}", lst); println!("\n{}", lst);
} }
std::process::exit(1); return Ok(());
} }
if cmd.next { if cmd.next {
@@ -675,7 +671,9 @@ fn handle_profile(
if cmd.list { if cmd.list {
let res = dbus.proxies().profile().profiles()?; let res = dbus.proxies().profile().profiles()?;
res.iter().for_each(|p| println!("{:?}", p)); for p in &res {
println!("{:?}", p);
}
} }
if cmd.profile_get { if cmd.profile_get {
@@ -687,7 +685,7 @@ fn handle_profile(
} }
fn handle_fan_curve( fn handle_fan_curve(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
supported: &PlatformProfileFunctions, supported: &PlatformProfileFunctions,
cmd: &FanCurveCommand, cmd: &FanCurveCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -706,14 +704,14 @@ fn handle_fan_curve(
if let Some(lst) = cmd.self_command_list() { if let Some(lst) = cmd.self_command_list() {
println!("\n{}", lst); println!("\n{}", lst);
} }
std::process::exit(1); return Ok(());
} }
if (cmd.enabled.is_some() || cmd.fan.is_some() || cmd.data.is_some()) if (cmd.enabled.is_some() || cmd.fan.is_some() || cmd.data.is_some())
&& cmd.mod_profile.is_none() && cmd.mod_profile.is_none()
{ {
println!("--enabled, --fan, and --data options require --mod-profile"); println!("--enabled, --fan, and --data options require --mod-profile");
std::process::exit(666); return Ok(());
} }
if cmd.get_enabled { if cmd.get_enabled {
@@ -749,7 +747,7 @@ fn handle_fan_curve(
} }
fn handle_bios_option( fn handle_bios_option(
dbus: &RogDbusClientBlocking, dbus: &RogDbusClientBlocking<'_>,
supported: &RogBiosSupportedFunctions, supported: &RogBiosSupportedFunctions,
cmd: &BiosCommand, cmd: &BiosCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@@ -764,10 +762,7 @@ fn handle_bios_option(
{ {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
let usage: Vec<String> = BiosCommand::usage() let usage: Vec<String> = BiosCommand::usage().lines().map(|s| s.to_owned()).collect();
.lines()
.map(|s| s.to_string())
.collect();
for line in usage.iter().filter(|line| { for line in usage.iter().filter(|line| {
line.contains("sound") && supported.post_sound line.contains("sound") && supported.post_sound
@@ -791,7 +786,10 @@ fn handle_bios_option(
dbus.proxies() dbus.proxies()
.rog_bios() .rog_bios()
.set_gpu_mux_mode(GpuMode::from_mux(opt))?; .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"); println!(
"The mode change is not active until you reboot, on boot the bios will make the \
required change"
);
} }
if cmd.gpu_mux_mode_get { if cmd.gpu_mux_mode_get {
let res = dbus.proxies().rog_bios().gpu_mux_mode()?; let res = dbus.proxies().rog_bios().gpu_mux_mode()?;

View File

@@ -1,5 +1,6 @@
use gumdrop::Options; use gumdrop::Options;
use rog_profiles::{fan_curve_set::CurveData, FanCurvePU, Profile}; use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{FanCurvePU, Profile};
#[derive(Debug, Clone, Options)] #[derive(Debug, Clone, Options)]
pub struct ProfileCommand { pub struct ProfileCommand {

18
config-traits/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "config-traits"
license = "MPL-2.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2021"
version.workspace = true
[dependencies]
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
toml.workspace = true
ron.workspace = true
log.workspace = true
[dev-dependencies]
cargo-husky.workspace = true

10
config-traits/README.md Normal file
View File

@@ -0,0 +1,10 @@
# config-traits
`config_traits` is a crate that broke out from the requirement to manage various
different config files, including parsing from different formats and updating
them from previous versions where fields or names are changed in some way.
The end canonical file format is `.ron` as this supports rust types well, and includes
the ability to add commenting, and is less verbose than `json`. Currently the crate will
also try to parse from `json` and `toml` if the `ron` parsing fails, then update to `ron`
format.

328
config-traits/src/lib.rs Normal file
View File

@@ -0,0 +1,328 @@
//! `config_traits` is a crate that broke out from the requirement to manage
//! various different config files, including parsing from different formats and
//! updating them from previous versions where fields or names are changed in
//! some way.
//!
//! The end canonical file format is `.ron` as this supports rust types well,
//! and includes the ability to add commenting, and is less verbose than `json`.
//! Currently the crate will also try to parse from `json` and `toml` if the
//! `ron` parsing fails, then update to `ron` format.
use std::fs::{self, create_dir, File, OpenOptions};
use std::io::{Read, Write};
use std::path::PathBuf;
use log::{error, warn};
pub use ron;
use ron::ser::PrettyConfig;
use serde::de::DeserializeOwned;
use serde::Serialize;
/// Config file helper traits. Only `new()` and `file_name()` are required to be
/// implemented, the rest are intended to be free methods.
pub trait StdConfig
where
Self: Serialize + DeserializeOwned,
{
/// Taking over the standard `new()` to ensure things can be generic
fn new() -> Self;
/// Return the config files names, such as `wibble.cfg`
fn file_name(&self) -> String;
/// Return the full path to the directory the config file resides in
fn config_dir() -> PathBuf;
/// Return the full path to the config file
fn file_path(&self) -> PathBuf {
let mut config = Self::config_dir();
if !config.exists() {
create_dir(config.as_path())
.unwrap_or_else(|e| panic!("Could not create {:?} {e}", Self::config_dir()));
}
config.push(self.file_name());
let mut do_rename = !config.exists();
let mut cfg_old = config.clone();
// Migrating all configs to .ron format, so we do need to check for older ones
if do_rename {
warn!("Config {cfg_old:?} does not exist, looking for .cfg next");
cfg_old.pop();
let tmp = self.file_name();
let parts: Vec<_> = tmp.split('.').collect();
cfg_old.push(format!("{}.cfg", parts[0]));
}
if do_rename && cfg_old.exists() {
// Now we gotta rename it
warn!("Renaming {cfg_old:?} to {config:?}");
std::fs::rename(&cfg_old, &config).unwrap_or_else(|err| {
error!(
"Could not rename. Please remove {} then restart service: Error {}",
self.file_name(),
err
);
});
do_rename = false;
}
if do_rename && !cfg_old.exists() {
warn!("Config {cfg_old:?} does not exist, looking for .conf next");
cfg_old.pop();
let tmp = self.file_name();
let parts: Vec<_> = tmp.split('.').collect();
cfg_old.push(format!("{}.conf", parts[0]));
}
if do_rename && cfg_old.exists() {
// Now we gotta rename it
warn!("Renaming {cfg_old:?} to {config:?}");
std::fs::rename(&cfg_old, &config).unwrap_or_else(|err| {
error!(
"Could not rename. Please remove {} then restart service: Error {}",
self.file_name(),
err
);
});
}
config
}
/// Directly open the config file for read and write. If the config file
/// does not exist it is created, including the directories the file
/// resides in.
fn file_open(&self) -> File {
OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(self.file_path())
.unwrap_or_else(|e| panic!("Could not open {:?} {e}", self.file_path()))
}
/// Open and parse the config file to self from ron format
fn read(&mut self) {
if let Ok(data) = fs::read_to_string(self.file_path()) {
if data.is_empty() {
warn!("File is empty {:?}", self.file_path());
} else if let Ok(data) = ron::from_str(&data) {
*self = data;
} else {
warn!("Could not deserialise {:?}", self.file_path());
}
}
}
/// Write the config file data to pretty ron format
fn write(&self) {
let mut file = match File::create(self.file_path()) {
Ok(data) => data,
Err(e) => {
error!(
"Couldn't overwrite config {:?}, error: {e}",
self.file_path()
);
return;
}
};
let ron = match ron::ser::to_string_pretty(&self, PrettyConfig::new().depth_limit(4)) {
Ok(data) => data,
Err(e) => {
error!("Parse {:?} to RON failed, error: {e}", self.file_path());
return;
}
};
file.write_all(ron.as_bytes())
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
/// Renames the existing file to `<file>-old`
fn rename_file_old(&self) {
warn!(
"Renaming {} to {}-old and recreating config",
self.file_name(),
self.file_name()
);
let mut cfg_old = self.file_path().to_string_lossy().to_string();
cfg_old.push_str("-old");
std::fs::rename(self.file_path(), cfg_old).unwrap_or_else(|err| {
error!(
"Could not rename. Please remove {} then restart service: Error {}",
self.file_name(),
err
);
});
}
}
#[macro_export]
macro_rules! std_config_load {
($trait_name:ident: $($generic:ident),*) => {
/// Base trait for loading/parsing. This is intended to be used to help update
/// configs to new versions
///
/// # Example
/// ```rust
/// use std::path::PathBuf;
/// use serde::{Deserialize, Serialize};
/// use config_traits::{StdConfig, StdConfigLoad2};
///
/// #[derive(Deserialize, Serialize, Debug)]
/// struct FanCurveConfigOld {}
///
/// #[derive(Deserialize, Serialize, Debug)]
/// struct FanCurveConfigOlder {}
///
/// #[derive(Deserialize, Serialize, Debug)]
/// struct FanCurveConfig {}
///
/// impl From<FanCurveConfigOld> for FanCurveConfig {
/// fn from(_: FanCurveConfigOld) -> Self { Self {} }
/// }
///
/// impl From<FanCurveConfigOlder> for FanCurveConfig {
/// fn from(_: FanCurveConfigOlder) -> Self { Self {} }
/// }
///
/// impl StdConfig for FanCurveConfig {
/// fn new() -> Self { Self {} }
///
/// fn file_name(&self) -> std::string::String { "test_name.conf".to_owned() }
///
/// fn config_dir() -> PathBuf { PathBuf::from("/tmp") }
/// }
///
/// impl StdConfigLoad2<FanCurveConfigOld, FanCurveConfigOlder> for FanCurveConfig {}
/// ```
///
/// If all of the generics fails to parse, then the old config is renamed and a
/// new one created
pub trait $trait_name<$($generic),*>
where
Self: $crate::StdConfig +std::fmt::Debug + DeserializeOwned + Serialize,
$($generic: DeserializeOwned + Into<Self>),*
{
fn load(mut self) -> Self {
let mut file = self.file_open();
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len != 0 {
if let Ok(data) = ron::from_str(&buf) {
self = data;
log::info!("Parsed RON for {:?}", std::any::type_name::<Self>());
} else if let Ok(data) = serde_json::from_str(&buf) {
self = data;
log::info!("Parsed JSON for {:?}", std::any::type_name::<Self>());
} else if let Ok(data) = toml::from_str(&buf) {
self = data;
log::info!("Parsed TOML for {:?}", std::any::type_name::<Self>());
} $(else if let Ok(data) = ron::from_str::<$generic>(&buf) {
self = data.into();
log::info!("New version failed, trying previous: Parsed RON for {:?}", std::any::type_name::<$generic>());
} else if let Ok(data) = serde_json::from_str::<$generic>(&buf) {
self = data.into();
log::info!("New version failed, trying previous: Parsed JSON for {:?}", std::any::type_name::<$generic>());
} else if let Ok(data) = toml::from_str::<$generic>(&buf) {
self = data.into();
log::info!("Newvious version failed, trying previous: Parsed TOML for {:?}", std::any::type_name::<$generic>());
})* else {
self.rename_file_old();
self = Self::new();
}
} else {
error!("Config file {} zero read length", self.file_name());
}
}
self.write();
self
}
}
};
}
std_config_load!(StdConfigLoad:);
std_config_load!(StdConfigLoad1: T1);
std_config_load!(StdConfigLoad2: T1, T2);
std_config_load!(StdConfigLoad3: T1, T2, T3);
std_config_load!(StdConfigLoad4: T1, T2, T3, T4);
#[cfg(test)]
mod tests {
use std::path::PathBuf;
#[test]
fn check_macro_from_1() {
#[derive(serde::Deserialize, serde::Serialize, Debug)]
struct Test {}
#[derive(serde::Deserialize, serde::Serialize, Debug)]
struct Old1 {}
impl crate::StdConfig for Test {
fn new() -> Self {
Self {}
}
fn file_name(&self) -> String {
String::new()
}
fn config_dir() -> PathBuf {
PathBuf::new()
}
}
impl From<Old1> for Test {
fn from(_: Old1) -> Self {
Self {}
}
}
impl crate::StdConfigLoad1<Old1> for Test {}
}
#[test]
fn check_macro_from_3() {
#[derive(serde::Deserialize, serde::Serialize, Debug)]
struct Test {}
#[derive(serde::Deserialize, serde::Serialize, Debug)]
struct Old1 {}
#[derive(serde::Deserialize, serde::Serialize, Debug)]
struct Old2 {}
#[derive(serde::Deserialize, serde::Serialize, Debug)]
struct Old3 {}
impl crate::StdConfig for Test {
fn new() -> Self {
Self {}
}
fn file_name(&self) -> String {
String::new()
}
fn config_dir() -> PathBuf {
PathBuf::new()
}
}
impl From<Old1> for Test {
fn from(_: Old1) -> Self {
Self {}
}
}
impl From<Old2> for Test {
fn from(_: Old2) -> Self {
Self {}
}
}
impl From<Old3> for Test {
fn from(_: Old3) -> Self {
Self {}
}
}
impl crate::StdConfigLoad3<Old1, Old2, Old3> for Test {}
}
}

View File

@@ -1,5 +1,6 @@
[package] [package]
name = "daemon-user" name = "daemon-user"
license = "MPL-2.0"
version.workspace = true version.workspace = true
authors = ["Luke D Jones <luke@ljones.dev>"] authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2021" edition = "2021"
@@ -26,5 +27,13 @@ rog_anime = { path = "../rog-anime" }
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" } rog_dbus = { path = "../rog-dbus" }
rog_platform = { path = "../rog-platform" } rog_platform = { path = "../rog-platform" }
config-traits = { path = "../config-traits" }
zbus.workspace = true zbus.workspace = true
# cli and logging
log.workspace = true
env_logger.workspace = true
[dev-dependencies]
cargo-husky.workspace = true

219
daemon-user/src/config.rs Normal file
View File

@@ -0,0 +1,219 @@
use std::path::PathBuf;
use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad};
use rog_anime::{ActionLoader, AnimTime, AnimeType, Fade, Sequences as AnimeSequences, Vec2};
use rog_aura::advanced::LedCode;
use rog_aura::effects::{AdvancedEffects as AuraSequences, Breathe, DoomFlicker, Effect, Static};
use rog_aura::{Colour, Speed};
use serde_derive::{Deserialize, Serialize};
use crate::error::Error;
const ROOT_CONF_DIR: &str = "rog";
fn root_conf_dir() -> PathBuf {
let mut dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("/tmp"));
dir.push(ROOT_CONF_DIR);
dir
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ConfigAnime {
pub name: String,
pub anime: Vec<ActionLoader>,
}
impl ConfigAnime {
pub fn create(&self, anime_type: AnimeType) -> Result<AnimeSequences, Error> {
let mut seq = AnimeSequences::new(anime_type);
for (idx, action) in self.anime.iter().enumerate() {
seq.insert(idx, action)?;
}
Ok(seq)
}
pub fn set_name(mut self, name: String) -> Self {
self.name = name;
self
}
}
impl Default for ConfigAnime {
fn default() -> Self {
Self {
name: "anime-default".to_owned(),
anime: vec![
ActionLoader::AsusImage {
file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
None,
Duration::from_secs(2),
)),
},
ActionLoader::AsusAnimation {
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
brightness: 0.5,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(6),
None,
Duration::from_secs(3),
)),
},
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 0.5,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
},
ActionLoader::Image {
file: "/usr/share/asusd/anime/custom/rust.png".into(),
scale: 1.0,
angle: 0.0,
translation: Vec2::default(),
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(1)),
Duration::from_secs(2),
)),
brightness: 0.6,
},
ActionLoader::Pause(Duration::from_secs(1)),
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 0.5,
time: AnimTime::Count(2),
},
],
}
}
}
impl StdConfig for ConfigAnime {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
format!("{}.ron", self.name)
}
fn config_dir() -> std::path::PathBuf {
root_conf_dir()
}
}
impl StdConfigLoad for ConfigAnime {}
#[derive(Debug, Deserialize, Serialize)]
pub struct ConfigAura {
pub name: String,
pub aura: AuraSequences,
}
impl ConfigAura {
pub fn set_name(mut self, name: String) -> Self {
self.name = name;
self
}
}
impl Default for ConfigAura {
fn default() -> Self {
let mut seq = AuraSequences::new(false);
let mut key = Effect::Breathe(Breathe::new(
LedCode::W,
Colour(255, 0, 20),
Colour(20, 255, 0),
Speed::Low,
));
seq.push(key.clone());
key.set_led(LedCode::A);
seq.push(key.clone());
key.set_led(LedCode::S);
seq.push(key.clone());
key.set_led(LedCode::D);
seq.push(key);
let key = Effect::Breathe(Breathe::new(
LedCode::F,
Colour(255, 0, 0),
Colour(255, 0, 0),
Speed::High,
));
seq.push(key);
let mut key = Effect::Static(Static::new(LedCode::RCtrl, Colour(0, 0, 255)));
seq.push(key.clone());
key.set_led(LedCode::LCtrl);
seq.push(key.clone());
key.set_led(LedCode::Esc);
seq.push(key);
let key = Effect::DoomFlicker(DoomFlicker::new(LedCode::N9, Colour(0, 0, 255), 80, 40));
seq.push(key);
Self {
name: "aura-default".to_owned(),
aura: seq,
}
}
}
impl StdConfig for ConfigAura {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
format!("{}.ron", self.name)
}
fn config_dir() -> std::path::PathBuf {
root_conf_dir()
}
}
impl StdConfigLoad for ConfigAura {}
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct ConfigBase {
/// Name of active anime config file in the user config directory
pub active_anime: Option<String>,
/// Name of active aura config file in the user config directory
pub active_aura: Option<String>,
}
impl StdConfig for ConfigBase {
fn new() -> Self {
Self {
active_anime: Some("anime-default".to_owned()),
active_aura: Some("aura-default".to_owned()),
}
}
fn file_name(&self) -> String {
"rog-user.ron".to_owned()
}
fn config_dir() -> std::path::PathBuf {
root_conf_dir()
}
}
impl StdConfigLoad for ConfigBase {}

View File

@@ -1,28 +1,25 @@
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::thread::sleep;
use std::time::{Duration, Instant};
use config_traits::StdConfig;
use rog_anime::error::AnimeError; use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2}; use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::time::Duration; use zbus::dbus_interface;
use std::{ use zbus::zvariant::{ObjectPath, Type};
path::Path,
sync::{
atomic::{AtomicBool, Ordering},
Mutex,
},
};
use std::{sync::Arc, thread::sleep, time::Instant};
use zbus::{
dbus_interface,
zvariant::{ObjectPath, Type},
};
use crate::user_config::ConfigLoadSave; use crate::config::ConfigAnime;
use crate::{error::Error, user_config::UserAnimeConfig}; use crate::error::Error;
#[derive(Debug, Clone, Deserialize, Serialize, Type)] #[derive(Debug, Clone, Deserialize, Serialize, Type)]
pub struct Timer { pub struct Timer {
type_of: TimeType, type_of: TimeType,
/// If time type is Timer then this is milliseonds, otherwise it is animation loop count /// If time type is Timer then this is milliseonds, otherwise it is
/// animation loop count
count: u64, count: u64,
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for /// Used only for `TimeType::Timer`, milliseonds to fade the image in for
fade_in: Option<u64>, fade_in: Option<u64>,
@@ -64,8 +61,8 @@ pub enum TimeType {
Infinite, Infinite,
} }
/// The inner object exists to allow the zbus proxy to share it with a runner thread /// The inner object exists to allow the zbus proxy to share it with a runner
/// and a zbus server behind `Arc<Mutex<T>>` /// thread and a zbus server behind `Arc<Mutex<T>>`
pub struct CtrlAnimeInner<'a> { pub struct CtrlAnimeInner<'a> {
sequences: Sequences, sequences: Sequences,
client: RogDbusClientBlocking<'a>, client: RogDbusClientBlocking<'a>,
@@ -84,7 +81,9 @@ impl<'a> CtrlAnimeInner<'static> {
do_early_return, do_early_return,
}) })
} }
/// To be called on each main loop iteration to pump out commands to the anime
/// To be called on each main loop iteration to pump out commands to the
/// anime
pub fn run(&'a self) -> Result<(), Error> { pub fn run(&'a self) -> Result<(), Error> {
if self.do_early_return.load(Ordering::SeqCst) { if self.do_early_return.load(Ordering::SeqCst) {
return Ok(()); return Ok(());
@@ -103,7 +102,7 @@ impl<'a> CtrlAnimeInner<'static> {
.write(output) .write(output)
.map_err(|e| AnimeError::Dbus(format!("{}", e))) .map_err(|e| AnimeError::Dbus(format!("{}", e)))
.map(|_| false) .map(|_| false)
})?; });
} }
ActionData::Image(image) => { ActionData::Image(image) => {
self.client self.client
@@ -124,10 +123,10 @@ impl<'a> CtrlAnimeInner<'static> {
sleep(Duration::from_millis(1)); sleep(Duration::from_millis(1));
} }
} }
ActionData::AudioEq => {} ActionData::AudioEq
ActionData::SystemInfo => {} | ActionData::SystemInfo
ActionData::TimeDate => {} | ActionData::TimeDate
ActionData::Matrix => {} | ActionData::Matrix => {}
} }
} }
@@ -136,16 +135,16 @@ impl<'a> CtrlAnimeInner<'static> {
} }
pub struct CtrlAnime<'a> { pub struct CtrlAnime<'a> {
config: Arc<Mutex<UserAnimeConfig>>, config: Arc<Mutex<ConfigAnime>>,
client: RogDbusClientBlocking<'a>, client: RogDbusClientBlocking<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>, inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner /// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: Arc<AtomicBool>, inner_early_return: Arc<AtomicBool>,
} }
impl<'a> CtrlAnime<'static> { impl CtrlAnime<'static> {
pub fn new( pub fn new(
config: Arc<Mutex<UserAnimeConfig>>, config: Arc<Mutex<ConfigAnime>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>, inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: RogDbusClientBlocking<'static>, client: RogDbusClientBlocking<'static>,
inner_early_return: Arc<AtomicBool>, inner_early_return: Arc<AtomicBool>,
@@ -185,7 +184,7 @@ impl CtrlAnime<'static> {
pub fn insert_asus_gif( pub fn insert_asus_gif(
&mut self, &mut self,
index: u32, index: u32,
file: String, file: &str,
time: Timer, time: Timer,
brightness: f32, brightness: f32,
) -> zbus::fdo::Result<String> { ) -> zbus::fdo::Result<String> {
@@ -208,7 +207,7 @@ impl CtrlAnime<'static> {
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} }
config.anime.push(action); config.anime.push(action);
config.write()?; config.write();
let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed"); let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed");
@@ -223,7 +222,7 @@ impl CtrlAnime<'static> {
pub fn insert_image_gif( pub fn insert_image_gif(
&mut self, &mut self,
index: u32, index: u32,
file: String, file: &str,
scale: f32, scale: f32,
angle: f32, angle: f32,
xy: (f32, f32), xy: (f32, f32),
@@ -253,7 +252,7 @@ impl CtrlAnime<'static> {
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} }
config.anime.push(action); config.anime.push(action);
config.write()?; config.write();
let json = let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
@@ -269,7 +268,7 @@ impl CtrlAnime<'static> {
pub fn insert_image( pub fn insert_image(
&mut self, &mut self,
index: u32, index: u32,
file: String, file: &str,
scale: f32, scale: f32,
angle: f32, angle: f32,
xy: (f32, f32), xy: (f32, f32),
@@ -298,7 +297,7 @@ impl CtrlAnime<'static> {
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} }
config.anime.push(action); config.anime.push(action);
config.write()?; config.write();
let json = let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
@@ -323,7 +322,7 @@ impl CtrlAnime<'static> {
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} }
config.anime.push(action); config.anime.push(action);
config.write()?; config.write();
let json = let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
@@ -346,7 +345,7 @@ impl CtrlAnime<'static> {
if (index as usize) < config.anime.len() { if (index as usize) < config.anime.len() {
config.anime.remove(index as usize); config.anime.remove(index as usize);
} }
config.write()?; config.write();
let json = let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed"); serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");

View File

@@ -1,18 +1,19 @@
use std::io::Write;
use std::path::PathBuf;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use config_traits::{StdConfig, StdConfigLoad};
use rog_anime::usb::get_anime_type; use rog_anime::usb::get_anime_type;
use rog_aura::aura_detection::LaptopLedData;
use rog_aura::layouts::KeyLayout; use rog_aura::layouts::KeyLayout;
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
use rog_user::{ use rog_user::config::*;
ctrl_anime::{CtrlAnime, CtrlAnimeInner}, use rog_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner};
user_config::*, use rog_user::DBUS_NAME;
DBUS_NAME,
};
use smol::Executor; use smol::Executor;
use std::sync::Mutex;
use std::{fs::OpenOptions, io::Read, path::PathBuf, sync::Arc};
use zbus::Connection; use zbus::Connection;
use std::sync::atomic::AtomicBool;
#[cfg(not(feature = "local_data"))] #[cfg(not(feature = "local_data"))]
const DATA_DIR: &str = "/usr/share/rog-gui/"; const DATA_DIR: &str = "/usr/share/rog-gui/";
#[cfg(feature = "local_data")] #[cfg(feature = "local_data")]
@@ -20,6 +21,13 @@ const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name"; const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
logger
.parse_default_env()
.target(env_logger::Target::Stdout)
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
.init();
println!(" user daemon v{}", rog_user::VERSION); println!(" user daemon v{}", rog_user::VERSION);
println!(" rog-anime v{}", rog_anime::VERSION); println!(" rog-anime v{}", rog_anime::VERSION);
println!(" rog-dbus v{}", rog_dbus::VERSION); println!(" rog-dbus v{}", rog_dbus::VERSION);
@@ -28,8 +36,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let (client, _) = RogDbusClientBlocking::new()?; let (client, _) = RogDbusClientBlocking::new()?;
let supported = client.proxies().supported().supported_functions()?; let supported = client.proxies().supported().supported_functions()?;
let mut config = UserConfig::new(); let config = ConfigBase::new().load();
config.load()?;
let executor = Executor::new(); let executor = Executor::new();
@@ -38,7 +45,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if supported.anime_ctrl.0 { if supported.anime_ctrl.0 {
if let Some(cfg) = config.active_anime { if let Some(cfg) = config.active_anime {
let anime_type = get_anime_type()?; let anime_type = get_anime_type()?;
let anime_config = UserAnimeConfig::load(cfg)?; let anime_config = ConfigAnime::new().set_name(cfg).load();
let anime = anime_config.create(anime_type)?; let anime = anime_config.create(anime_type)?;
let anime_config = Arc::new(Mutex::new(anime_config)); let anime_config = Arc::new(Mutex::new(anime_config));
@@ -69,24 +76,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// if supported.keyboard_led.per_key_led_mode { // if supported.keyboard_led.per_key_led_mode {
if let Some(cfg) = config.active_aura { if let Some(cfg) = config.active_aura {
let mut aura_config = UserAuraConfig::load(cfg)?; let mut aura_config = ConfigAura::new().set_name(cfg).load();
// let baord_name = std::fs::read_to_string(BOARD_NAME)?;
// Find and load a matching layout for laptop let led_support = LaptopLedData::get_data();
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)) let layout = KeyLayout::find_layout(led_support, PathBuf::from(DATA_DIR))
.map_err(|e| { .map_err(|e| {
println!("{BOARD_NAME}, {e}"); println!("{BOARD_NAME}, {e}");
}) })
.unwrap_or_else(|_| KeyLayout::ga401_layout()); .unwrap_or_else(|_| KeyLayout::default_layout());
executor executor
.spawn(async move { .spawn(async move {
@@ -99,7 +98,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
aura_config.aura.next_state(&layout); aura_config.aura.next_state(&layout);
let packets = aura_config.aura.create_packets(); let packets = aura_config.aura.create_packets();
client.proxies().led().per_key_raw(packets).unwrap(); client
.proxies()
.led()
.direct_addressing_raw(packets)
.unwrap();
std::thread::sleep(std::time::Duration::from_millis(33)); std::thread::sleep(std::time::Duration::from_millis(33));
} }
}) })

View File

@@ -13,7 +13,7 @@ pub enum Error {
impl fmt::Display for Error { impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature. // This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Error::Io(err) => write!(f, "Failed to open: {}", err), Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::ConfigLoadFail => write!(f, "Failed to load user config"), Error::ConfigLoadFail => write!(f, "Failed to load user config"),

View File

@@ -1,4 +1,4 @@
pub mod user_config; pub mod config;
pub mod error; pub mod error;

View File

@@ -1,318 +0,0 @@
use std::{
fs::{create_dir, OpenOptions},
io::{Read, Write},
time::Duration,
};
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;
pub trait ConfigLoadSave<T: DeserializeOwned + serde::Serialize> {
fn name(&self) -> String;
fn default_with_name(name: String) -> T;
fn write(&self) -> Result<(), Error>
where
Self: serde::Serialize,
{
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
let name = self.name();
path.push(name + ".cfg");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
let json = serde_json::to_string_pretty(&self).unwrap();
file.write_all(json.as_bytes())?;
Ok(())
}
fn load(name: String) -> Result<T, Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
path.push(name.clone() + ".cfg");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
let default = 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::<T>(&buf) {
return Ok(data);
}
}
Err(Error::ConfigLoadFail)
}
}
#[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 {
name: "default".to_string(),
anime: vec![
ActionLoader::AsusImage {
file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(),
brightness: 1.0,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
None,
Duration::from_secs(2),
)),
},
ActionLoader::AsusAnimation {
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
brightness: 0.5,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(6),
None,
Duration::from_secs(3),
)),
},
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
scale: 0.9,
angle: 0.65,
translation: Vec2::default(),
brightness: 0.5,
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(2)),
Duration::from_secs(2),
)),
},
ActionLoader::Image {
file: "/usr/share/asusd/anime/custom/rust.png".into(),
scale: 1.0,
angle: 0.0,
translation: Vec2::default(),
time: AnimTime::Fade(Fade::new(
Duration::from_secs(2),
Some(Duration::from_secs(1)),
Duration::from_secs(2),
)),
brightness: 0.6,
},
ActionLoader::Pause(Duration::from_secs(1)),
ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 0.5,
time: AnimTime::Count(2),
},
],
}
}
}
#[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);
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: 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: Some("anime-default".to_string()),
active_aura: Some("aura-default".to_string()),
}
}
pub fn load(&mut self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
path.push("rog-user.cfg");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
let json = serde_json::to_string_pretty(&self).unwrap();
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(());
}
}
Ok(())
}
pub fn write(&self) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
return Err(Error::XdgVars);
};
path.push("rog");
if !path.exists() {
create_dir(path.clone())?;
}
path.push("rog-user.cfg");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)?;
let json = serde_json::to_string_pretty(&self).unwrap();
file.write_all(json.as_bytes())?;
Ok(())
}
}

View File

@@ -1,7 +1,8 @@
//! # DBus interface proxy for: `org.asuslinux.Daemon` //! # `DBus` interface proxy for: `org.asuslinux.Daemon`
//! //!
//! This code was generated by `zbus-xmlgen` `1.0.0` from DBus introspection data. //! This code was generated by `zbus-xmlgen` `1.0.0` from `DBus` introspection
//! Source: `Interface '/org/asuslinux/Anime' from service 'org.asuslinux.Daemon' on session bus`. //! data. Source: `Interface '/org/asuslinux/Anime' from service
//! 'org.asuslinux.Daemon' on session bus`.
//! //!
//! You may prefer to adapt it, instead of using it verbatim. //! You may prefer to adapt it, instead of using it verbatim.
//! //!
@@ -9,8 +10,8 @@
//! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html) //! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html)
//! section of the zbus documentation. //! section of the zbus documentation.
//! //!
//! This DBus object implements //! This `DBus` object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html), //! [standard `DBus` interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used: //! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//! //!
//! * [`zbus::fdo::PeerProxy`] //! * [`zbus::fdo::PeerProxy`]

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "daemon" name = "daemon"
version.workspace = true
license = "MPL-2.0" license = "MPL-2.0"
version.workspace = true
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl" repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
@@ -18,6 +18,7 @@ name = "asusd"
path = "src/daemon.rs" path = "src/daemon.rs"
[dependencies] [dependencies]
config-traits = { path = "../config-traits" }
rog_anime = { path = "../rog-anime", features = ["dbus"] } rog_anime = { path = "../rog-anime", features = ["dbus"] }
rog_aura = { path = "../rog-aura", features = ["dbus"] } rog_aura = { path = "../rog-aura", features = ["dbus"] }
rog_platform = { path = "../rog-platform" } rog_platform = { path = "../rog-platform" }
@@ -37,10 +38,13 @@ logind-zbus.workspace = true
# serialisation # serialisation
serde.workspace = true serde.workspace = true
serde_derive.workspace = true serde_derive.workspace = true
serde_json.workspace = true
toml.workspace = true
# Device control # Device control
sysfs-class.workspace = true # used for backlight control and baord ID sysfs-class.workspace = true # used for backlight control and baord ID
concat-idents.workspace = true concat-idents.workspace = true
systemd-zbus = "*"
[dev-dependencies]
cargo-husky.workspace = true

View File

@@ -1,83 +1,77 @@
use log::{error, warn}; use config_traits::{StdConfig, StdConfigLoad2};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::PathBuf;
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf"; const CONFIG_FILE: &str = "asusd.ron";
#[derive(Deserialize, Serialize, Default, Debug)]
pub struct Config {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub panel_od: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
}
impl StdConfig for Config {
fn new() -> Self {
Config {
bat_charge_limit: 100,
panel_od: false,
disable_nvidia_powerd_on_battery: true,
ac_command: String::new(),
bat_command: String::new(),
}
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
}
impl StdConfigLoad2<Config455, Config458> for Config {}
#[derive(Deserialize, Serialize, Default)] #[derive(Deserialize, Serialize, Default)]
#[serde(default)] #[serde(default)]
pub struct Config { pub struct Config455 {
/// Save charge limit for restoring on boot /// Save charge limit for restoring on boot
pub bat_charge_limit: u8, pub bat_charge_limit: u8,
pub panel_od: bool, pub panel_od: bool,
} }
impl Config { impl From<Config455> for Config {
fn new() -> Self { fn from(c: Config455) -> Self {
Config { Self {
bat_charge_limit: 100, bat_charge_limit: c.bat_charge_limit,
panel_od: false, panel_od: c.panel_od,
disable_nvidia_powerd_on_battery: true,
ac_command: String::new(),
bat_command: String::new(),
}
}
}
#[derive(Deserialize, Serialize, Default)]
pub struct Config458 {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub panel_od: bool,
pub ac_command: String,
pub bat_command: String,
}
impl From<Config458> for Config {
fn from(c: Config458) -> Self {
Self {
bat_charge_limit: c.bat_charge_limit,
panel_od: c.panel_od,
disable_nvidia_powerd_on_battery: true,
ac_command: c.ac_command,
bat_command: c.bat_command,
} }
} }
/// `load` will attempt to read the config, and panic if the dir is missing
pub fn load() -> Self {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&PathBuf::from(CONFIG_PATH))
.unwrap_or_else(|e| panic!("Error opening {}, {}", CONFIG_PATH, e)); // okay to cause panic here
let mut buf = String::new();
let config;
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
config = Self::new();
} else if let Ok(data) = serde_json::from_str(&buf) {
config = data;
} else {
warn!(
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
CONFIG_PATH, CONFIG_PATH
);
let cfg_old = CONFIG_PATH.to_string() + "-old";
std::fs::rename(CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",
CONFIG_PATH, err
)
});
config = Self::new();
}
} else {
config = Self::new()
}
config.write();
config
}
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("File is empty {}", CONFIG_PATH);
} else {
*self = serde_json::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH));
}
}
}
pub fn write(&self) {
let mut file = File::create(CONFIG_PATH).expect("Couldn't overwrite config");
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
file.write_all(json.as_bytes())
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
} }

View File

@@ -1,14 +1,11 @@
use crate::VERSION;
use log::{error, info, warn};
use rog_anime::{error::AnimeError, ActionData, ActionLoader, AnimTime, Vec2};
use rog_anime::{AnimeType, Fade};
use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::time::Duration; use std::time::Duration;
pub static ANIME_CONFIG_PATH: &str = "/etc/asusd/anime.conf"; use config_traits::{StdConfig, StdConfigLoad2};
pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf"; use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, AnimeType, Fade, Vec2};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron";
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct AnimeConfigV341 { pub struct AnimeConfigV341 {
@@ -18,25 +15,30 @@ pub struct AnimeConfigV341 {
pub shutdown: Option<ActionLoader>, pub shutdown: Option<ActionLoader>,
} }
impl AnimeConfigV341 { impl From<AnimeConfigV341> for AnimeConfig {
pub(crate) fn into_current(self) -> AnimeConfig { fn from(c: AnimeConfigV341) -> AnimeConfig {
AnimeConfig { AnimeConfig {
system: if let Some(ani) = self.system { system: if let Some(ani) = c.system {
vec![ani] vec![ani]
} else { } else {
vec![] vec![]
}, },
boot: if let Some(ani) = self.boot { boot: if let Some(ani) = c.boot {
vec![ani] vec![ani]
} else { } else {
vec![] vec![]
}, },
wake: if let Some(ani) = self.suspend { wake: if let Some(ani) = c.suspend {
vec![ani] vec![ani]
} else { } else {
vec![] vec![]
}, },
shutdown: if let Some(ani) = self.shutdown { shutdown: if let Some(ani) = c.shutdown.clone() {
vec![ani]
} else {
vec![]
},
sleep: if let Some(ani) = c.shutdown.clone() {
vec![ani] vec![ani]
} else { } else {
vec![] vec![]
@@ -57,13 +59,39 @@ pub struct AnimeConfigV352 {
pub brightness: f32, pub brightness: f32,
} }
impl AnimeConfigV352 { impl From<AnimeConfigV352> for AnimeConfig {
pub(crate) fn into_current(self) -> AnimeConfig { fn from(c: AnimeConfigV352) -> AnimeConfig {
AnimeConfig { AnimeConfig {
system: self.system, system: c.system,
boot: self.boot, boot: c.boot,
wake: self.wake, wake: c.wake,
shutdown: self.shutdown, sleep: c.shutdown.clone(),
shutdown: c.shutdown,
brightness: 1.0,
awake_enabled: true,
boot_anim_enabled: true,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct AnimeConfigV460 {
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
}
impl From<AnimeConfigV460> for AnimeConfig {
fn from(c: AnimeConfigV460) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
sleep: c.sleep,
shutdown: c.shutdown,
brightness: 1.0, brightness: 1.0,
awake_enabled: true, awake_enabled: true,
boot_anim_enabled: true, boot_anim_enabled: true,
@@ -76,6 +104,7 @@ pub struct AnimeConfigCached {
pub system: Vec<ActionData>, pub system: Vec<ActionData>,
pub boot: Vec<ActionData>, pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>, pub wake: Vec<ActionData>,
pub sleep: Vec<ActionData>,
pub shutdown: Vec<ActionData>, pub shutdown: Vec<ActionData>,
} }
@@ -86,25 +115,31 @@ impl AnimeConfigCached {
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<(), AnimeError> { ) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len()); let mut sys = Vec::with_capacity(config.system.len());
for ani in config.system.iter() { for ani in &config.system {
sys.push(ActionData::from_anime_action(anime_type, ani)?); sys.push(ActionData::from_anime_action(anime_type, ani)?);
} }
self.system = sys; self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len()); let mut boot = Vec::with_capacity(config.boot.len());
for ani in config.boot.iter() { for ani in &config.boot {
boot.push(ActionData::from_anime_action(anime_type, ani)?); boot.push(ActionData::from_anime_action(anime_type, ani)?);
} }
self.boot = boot; self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len()); let mut wake = Vec::with_capacity(config.wake.len());
for ani in config.wake.iter() { for ani in &config.wake {
wake.push(ActionData::from_anime_action(anime_type, ani)?); wake.push(ActionData::from_anime_action(anime_type, ani)?);
} }
self.wake = wake; self.wake = wake;
let mut sleep = Vec::with_capacity(config.sleep.len());
for ani in &config.sleep {
sleep.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.sleep = sleep;
let mut shutdown = Vec::with_capacity(config.shutdown.len()); let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in config.shutdown.iter() { for ani in &config.shutdown {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?); shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
} }
self.shutdown = shutdown; self.shutdown = shutdown;
@@ -113,11 +148,12 @@ impl AnimeConfigCached {
} }
/// Config for base system actions for the anime display /// Config for base system actions for the anime display
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Debug)]
pub struct AnimeConfig { pub struct AnimeConfig {
pub system: Vec<ActionLoader>, pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>, pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>, pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>, pub shutdown: Vec<ActionLoader>,
pub brightness: f32, pub brightness: f32,
pub awake_enabled: bool, pub awake_enabled: bool,
@@ -130,6 +166,7 @@ impl Default for AnimeConfig {
system: Vec::new(), system: Vec::new(),
boot: Vec::new(), boot: Vec::new(),
wake: Vec::new(), wake: Vec::new(),
sleep: Vec::new(),
shutdown: Vec::new(), shutdown: Vec::new(),
brightness: 1.0, brightness: 1.0,
awake_enabled: true, awake_enabled: true,
@@ -138,70 +175,36 @@ impl Default for AnimeConfig {
} }
} }
impl StdConfig for AnimeConfig {
fn new() -> Self {
Self::create_default()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
}
impl StdConfigLoad2<AnimeConfigV341, AnimeConfigV352> for AnimeConfig {}
impl AnimeConfig { impl AnimeConfig {
/// `load` will attempt to read the config, and panic if the dir is missing // fn clamp_config_brightness(mut config: &mut AnimeConfig) {
pub fn load() -> Self { // if config.brightness < 0.0 || config.brightness > 1.0 {
let mut file = OpenOptions::new() // warn!(
.read(true) // "Clamped brightness to [0.0 ; 1.0], was {}",
.write(true) // config.brightness
.create(true) // );
.open(ANIME_CONFIG_PATH) // config.brightness = f32::max(0.0, f32::min(1.0, config.brightness));
.unwrap_or_else(|_| { // }
panic!( // }
"The file {} or directory /etc/asusd/ is missing",
ANIME_CONFIG_PATH
)
}); // okay to cause panic here
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
return AnimeConfig::create_default(&mut file);
} else {
if let Ok(mut data) = serde_json::from_str(&buf) {
Self::clamp_config_brightness(&mut data);
return data;
} else if let Ok(data) = serde_json::from_str::<AnimeConfigV341>(&buf) {
let mut config = data.into_current();
config.write();
info!("Updated config version to: {}", VERSION);
Self::clamp_config_brightness(&mut config);
return config;
} else if let Ok(data) = serde_json::from_str::<AnimeConfigV352>(&buf) {
let mut config = data.into_current();
config.write();
info!("Updated config version to: {}", VERSION);
Self::clamp_config_brightness(&mut config);
return config;
}
warn!(
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
ANIME_CONFIG_PATH, ANIME_CONFIG_PATH
);
let cfg_old = ANIME_CONFIG_PATH.to_string() + "-old";
std::fs::rename(ANIME_CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",
ANIME_CONFIG_PATH, err
)
});
}
}
AnimeConfig::create_default(&mut file)
}
fn clamp_config_brightness(mut config: &mut AnimeConfig) { fn create_default() -> Self {
if config.brightness < 0.0 || config.brightness > 1.0 {
warn!(
"Clamped brightness to [0.0 ; 1.0], was {}",
config.brightness
);
config.brightness = f32::max(0.0, f32::min(1.0, config.brightness));
}
}
fn create_default(file: &mut File) -> Self {
// create a default config here // create a default config here
let config = AnimeConfig { AnimeConfig {
system: vec![], system: vec![],
boot: vec![ActionLoader::ImageAnimation { boot: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
@@ -227,6 +230,14 @@ impl AnimeConfig {
Duration::from_secs(2), Duration::from_secs(2),
)), )),
}], }],
sleep: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
angle: 0.0,
translation: Vec2::new(3.0, 2.0),
brightness: 1.0,
time: AnimTime::Infinite,
}],
shutdown: vec![ActionLoader::ImageAnimation { shutdown: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(), file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9, scale: 0.9,
@@ -238,35 +249,6 @@ impl AnimeConfig {
brightness: 1.0, brightness: 1.0,
awake_enabled: true, awake_enabled: true,
boot_anim_enabled: true, boot_anim_enabled: true,
};
// Should be okay to unwrap this as is since it is a Default
let json = serde_json::to_string_pretty(&config).unwrap();
file.write_all(json.as_bytes())
.unwrap_or_else(|_| panic!("Could not write {}", ANIME_CONFIG_PATH));
config
}
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(ANIME_CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", ANIME_CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("File is empty {}", ANIME_CONFIG_PATH);
} else {
let x: AnimeConfig = serde_json::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", ANIME_CONFIG_PATH));
*self = x;
}
} }
} }
pub fn write(&self) {
let mut file = File::create(ANIME_CONFIG_PATH).expect("Couldn't overwrite config");
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
file.write_all(json.as_bytes())
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
} }

View File

@@ -1,19 +1,25 @@
pub mod config; pub mod config;
/// Implements CtrlTask, Reloadable, ZbusRun /// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls; pub mod trait_impls;
use self::config::{AnimeConfig, AnimeConfigCached}; use std::convert::TryFrom;
use crate::{error::RogError, GetSupported}; use std::error::Error;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use ::zbus::export::futures_util::lock::Mutex; use ::zbus::export::futures_util::lock::Mutex;
use log::{error, info, warn}; use log::{error, info, warn};
use rog_anime::{ use rog_anime::error::AnimeError;
error::AnimeError, use rog_anime::usb::{get_anime_type, pkt_for_flush, pkts_for_init};
usb::{get_anime_type, pkt_for_flush, pkts_for_init}, use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType};
ActionData, AnimeDataBuffer, AnimePacketType, AnimeType, use rog_platform::hid_raw::HidRaw;
}; use rog_platform::supported::AnimeSupportedFunctions;
use rog_platform::{hid_raw::HidRaw, supported::AnimeSupportedFunctions, usb_raw::USBRaw}; use rog_platform::usb_raw::USBRaw;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{convert::TryFrom, error::Error, sync::Arc, thread::sleep}; use self::config::{AnimeConfig, AnimeConfigCached};
use crate::error::RogError;
use crate::GetSupported;
impl GetSupported for CtrlAnime { impl GetSupported for CtrlAnime {
type A = AnimeSupportedFunctions; type A = AnimeSupportedFunctions;
@@ -56,13 +62,14 @@ impl CtrlAnime {
Ok(ctrl) Ok(ctrl)
} }
// let device = CtrlAnime::get_device(0x0b05, 0x193b)?; // let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
/// Start an action thread. This is classed as a singleton and there should be only /// Start an action thread. This is classed as a singleton and there should
/// one running - so the thread uses atomics to signal run/exit. /// be only one running - so the thread uses atomics to signal run/exit.
/// ///
/// Because this also writes to the usb device, other write tries (display only) *must* /// Because this also writes to the usb device, other write tries (display
/// get the mutex lock and set the thread_exit atomic. /// only) *must* get the mutex lock and set the `thread_exit` atomic.
fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) { fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() { if actions.is_empty() {
warn!("AniMe system actions was empty"); warn!("AniMe system actions was empty");
@@ -70,12 +77,15 @@ impl CtrlAnime {
} }
// Loop rules: // Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible. // - Lock the mutex **only when required**. That is, the lock must be held for
// - An AtomicBool used for thread exit should be checked in every loop, including nested // the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting for the // The only reason for this outer thread is to prevent blocking while waiting
// next spawned thread to exit // for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main thread) // TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
std::thread::Builder::new() std::thread::Builder::new()
.name("AniMe system thread start".into()) .name("AniMe system thread start".into())
.spawn(move || { .spawn(move || {
@@ -103,18 +113,17 @@ impl CtrlAnime {
info!("AniMe no previous system thread running (now)"); info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst); thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop { 'main: loop {
thread_running.store(true, Ordering::SeqCst); for action in &actions {
for action in actions.iter() {
if thread_exit.load(Ordering::SeqCst) { if thread_exit.load(Ordering::SeqCst) {
break 'main; break 'main;
} }
match action { match action {
ActionData::Animation(frames) => { ActionData::Animation(frames) => {
if let Err(err) = rog_anime::run_animation(frames, &|frame| { rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) { if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: frame-loop was asked to exit"); info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit return Ok(true); // Do safe exit
} }
inner inner
@@ -130,15 +139,18 @@ impl CtrlAnime {
.ok(); .ok();
false // Don't exit yet false // Don't exit yet
}) })
.map(Ok) .map_or_else(
.unwrap_or_else(|| { || {
warn!("rog_anime::run_animation:callback failed"); warn!("rog_anime::run_animation:callback failed");
Err(AnimeError::NoFrames) Err(AnimeError::NoFrames)
}) },
}) { Ok,
warn!("rog_anime::run_animation:Animation {}", err); )
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main; break 'main;
}; }
} }
ActionData::Image(image) => { ActionData::Image(image) => {
once = false; once = false;
@@ -149,10 +161,10 @@ impl CtrlAnime {
} }
} }
ActionData::Pause(duration) => sleep(*duration), ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq => {} ActionData::AudioEq
ActionData::SystemInfo => {} | ActionData::SystemInfo
ActionData::TimeDate => {} | ActionData::TimeDate
ActionData::Matrix => {} | ActionData::Matrix => {}
} }
} }
if thread_exit.load(Ordering::SeqCst) { if thread_exit.load(Ordering::SeqCst) {
@@ -194,7 +206,7 @@ impl CtrlAnime {
*led = bright as u8; *led = bright as u8;
} }
let data = AnimePacketType::try_from(buffer)?; let data = AnimePacketType::try_from(buffer)?;
for row in data.iter() { for row in &data {
self.node.write_bytes(row)?; self.node.write_bytes(row)?;
} }
self.node.write_bytes(&pkt_for_flush())?; self.node.write_bytes(&pkt_for_flush())?;

View File

@@ -1,17 +1,16 @@
use std::sync::atomic::Ordering;
use std::sync::Arc;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::warn;
use rog_anime::usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on};
use rog_anime::{AnimeDataBuffer, AnimePowerStates};
use zbus::export::futures_util::lock::Mutex;
use zbus::{dbus_interface, Connection, SignalContext};
use super::CtrlAnime; use super::CtrlAnime;
use crate::error::RogError; use crate::error::RogError;
use async_trait::async_trait;
use log::{info, warn};
use rog_anime::{
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
AnimeDataBuffer, AnimePowerStates,
};
use std::sync::{atomic::Ordering, Arc};
use zbus::{
dbus_interface,
export::futures_util::lock::{Mutex, MutexGuard},
Connection, SignalContext,
};
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime"; pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime";
@@ -27,11 +26,12 @@ impl crate::ZbusRun for CtrlAnimeZbus {
} }
// None of these calls can be guarnateed to succeed unless we loop until okay // None of these calls can be guarnateed to succeed unless we loop until okay
// If the try_lock *does* succeed then any other thread trying to lock will not grab it // If the try_lock *does* succeed then any other thread trying to lock will not
// until we finish. // grab it until we finish.
#[dbus_interface(name = "org.asuslinux.Daemon")] #[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus { impl CtrlAnimeZbus {
/// Writes a data stream of length. Will force system thread to exit until it is restarted /// Writes a data stream of length. Will force system thread to exit until
/// it is restarted
async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
let lock = self.0.lock().await; let lock = self.0.lock().await;
lock.thread_exit.store(true, Ordering::SeqCst); lock.thread_exit.store(true, Ordering::SeqCst);
@@ -47,7 +47,7 @@ impl CtrlAnimeZbus {
let mut lock = self.0.lock().await; let mut lock = self.0.lock().await;
let mut bright = bright; let mut bright = bright;
if bright < 0.0 { if bright < 0.0 {
bright = 0.0 bright = 0.0;
} else if bright > 1.0 { } else if bright > 1.0 {
bright = 1.0; bright = 1.0;
} }
@@ -133,7 +133,8 @@ impl CtrlAnimeZbus {
lock.config.boot_anim_enabled lock.config.boot_anim_enabled
} }
/// Notify listeners of the status of AniMe LED power and factory system-status animations /// Notify listeners of the status of AniMe LED power and factory
/// system-status animations
#[dbus_interface(signal)] #[dbus_interface(signal)]
async fn notify_power_states( async fn notify_power_states(
ctxt: &SignalContext<'_>, ctxt: &SignalContext<'_>,
@@ -148,46 +149,41 @@ impl crate::CtrlTask for CtrlAnimeZbus {
} }
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> { async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let run_action =
|start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
if start {
info!("CtrlAnimeTask running sleep animation");
CtrlAnime::run_thread(inner, lock.cache.shutdown.clone(), true);
} else {
info!("CtrlAnimeTask running wake animation");
CtrlAnime::run_thread(inner, lock.cache.wake.clone(), true);
}
};
let inner1 = self.0.clone(); let inner1 = self.0.clone();
let inner2 = self.0.clone(); let inner2 = self.0.clone();
let inner3 = self.0.clone(); let inner3 = self.0.clone();
let inner4 = self.0.clone(); let inner4 = self.0.clone();
self.create_sys_event_tasks( self.create_sys_event_tasks(
// Loop is required to try an attempt to get the mutex *without* blocking move || {
// other threads - it is possible to end up with deadlocks otherwise. // on_sleep
move || loop { let inner1 = inner1.clone();
if let Some(lock) = inner1.try_lock() { async move {
run_action(true, lock, inner1.clone()); let lock = inner1.lock().await;
break; CtrlAnime::run_thread(inner1.clone(), lock.cache.sleep.clone(), true);
} }
}, },
move || loop { move || {
if let Some(lock) = inner2.try_lock() { // on_wake
run_action(false, lock, inner2.clone()); let inner2 = inner2.clone();
break; async move {
let lock = inner2.lock().await;
CtrlAnime::run_thread(inner2.clone(), lock.cache.wake.clone(), true);
} }
}, },
move || loop { move || {
if let Some(lock) = inner3.try_lock() { // on_shutdown
run_action(true, lock, inner3.clone()); let inner3 = inner3.clone();
break; async move {
let lock = inner3.lock().await;
CtrlAnime::run_thread(inner3.clone(), lock.cache.shutdown.clone(), true);
} }
}, },
move || loop { move || {
if let Some(lock) = inner4.try_lock() { // on_boot
run_action(false, lock, inner4.clone()); let inner4 = inner4.clone();
break; async move {
let lock = inner4.lock().await;
CtrlAnime::run_thread(inner4.clone(), lock.cache.boot.clone(), true);
} }
}, },
) )

View File

@@ -1,15 +1,12 @@
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::collections::{BTreeMap, HashSet};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf"; use config_traits::{StdConfig, StdConfigLoad};
use rog_aura::aura_detection::LaptopLedData;
use rog_aura::usb::{AuraDevRog1, AuraDevRog2, AuraDevTuf, AuraDevice, AuraPowerDev};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Direction, LedBrightness, Speed, GRADIENT};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "aura.ron";
/// Enable/disable LED control in various states such as /// Enable/disable LED control in various states such as
/// when the device is awake, suspended, shutting down or /// when the device is awake, suspended, shutting down or
@@ -17,8 +14,8 @@ pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuraPowerConfig { pub enum AuraPowerConfig {
AuraDevTuf(HashSet<AuraDevTuf>), AuraDevTuf(HashSet<AuraDevTuf>),
AuraDev1866(HashSet<AuraDev1866>), AuraDevRog1(HashSet<AuraDevRog1>),
AuraDev19b6(HashSet<AuraDev19b6>), AuraDevRog2(HashSet<AuraDevRog2>),
} }
impl AuraPowerConfig { impl AuraPowerConfig {
@@ -26,13 +23,13 @@ impl AuraPowerConfig {
pub fn to_bytes(control: &Self) -> [u8; 3] { pub fn to_bytes(control: &Self) -> [u8; 3] {
match control { match control {
AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0], AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0],
AuraPowerConfig::AuraDev1866(c) => { AuraPowerConfig::AuraDevRog1(c) => {
let c: Vec<AuraDev1866> = c.iter().copied().collect(); let c: Vec<AuraDevRog1> = c.iter().copied().collect();
AuraDev1866::to_bytes(&c) AuraDevRog1::to_bytes(&c)
} }
AuraPowerConfig::AuraDev19b6(c) => { AuraPowerConfig::AuraDevRog2(c) => {
let c: Vec<AuraDev19b6> = c.iter().copied().collect(); let c: Vec<AuraDevRog2> = c.iter().copied().collect();
AuraDev19b6::to_bytes(&c) AuraDevRog2::to_bytes(&c)
} }
} }
} }
@@ -48,13 +45,13 @@ impl AuraPowerConfig {
]); ]);
} }
if let Self::AuraDev1866(c) = control { if let Self::AuraDevRog1(c) = control {
return Some([ return Some([
true, true,
c.contains(&AuraDev1866::Boot), c.contains(&AuraDevRog1::Boot),
c.contains(&AuraDev1866::Awake), c.contains(&AuraDevRog1::Awake),
c.contains(&AuraDev1866::Sleep), c.contains(&AuraDevRog1::Sleep),
c.contains(&AuraDev1866::Keyboard), c.contains(&AuraDevRog1::Keyboard),
]); ]);
} }
@@ -70,8 +67,9 @@ impl AuraPowerConfig {
} }
} }
} }
pub fn set_0x1866(&mut self, power: AuraDev1866, on: bool) {
if let Self::AuraDev1866(p) = self { pub fn set_0x1866(&mut self, power: AuraDevRog1, on: bool) {
if let Self::AuraDevRog1(p) = self {
if on { if on {
p.insert(power); p.insert(power);
} else { } else {
@@ -80,8 +78,8 @@ impl AuraPowerConfig {
} }
} }
pub fn set_0x19b6(&mut self, power: AuraDev19b6, on: bool) { pub fn set_0x19b6(&mut self, power: AuraDevRog2, on: bool) {
if let Self::AuraDev19b6(p) = self { if let Self::AuraDevRog2(p) = self {
if on { if on {
p.insert(power); p.insert(power);
} else { } else {
@@ -99,12 +97,12 @@ impl From<&AuraPowerConfig> for AuraPowerDev {
x1866: vec![], x1866: vec![],
x19b6: vec![], x19b6: vec![],
}, },
AuraPowerConfig::AuraDev1866(d) => AuraPowerDev { AuraPowerConfig::AuraDevRog1(d) => AuraPowerDev {
tuf: vec![], tuf: vec![],
x1866: d.iter().copied().collect(), x1866: d.iter().copied().collect(),
x19b6: vec![], x19b6: vec![],
}, },
AuraPowerConfig::AuraDev19b6(d) => AuraPowerDev { AuraPowerConfig::AuraDevRog2(d) => AuraPowerDev {
tuf: vec![], tuf: vec![],
x1866: vec![], x1866: vec![],
x19b6: d.iter().copied().collect(), x19b6: d.iter().copied().collect(),
@@ -113,7 +111,7 @@ impl From<&AuraPowerConfig> for AuraPowerDev {
} }
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Debug, Clone)]
// #[serde(default)] // #[serde(default)]
pub struct AuraConfig { pub struct AuraConfig {
pub brightness: LedBrightness, pub brightness: LedBrightness,
@@ -124,38 +122,40 @@ pub struct AuraConfig {
pub enabled: AuraPowerConfig, pub enabled: AuraPowerConfig,
} }
impl Default for AuraConfig { impl StdConfig for AuraConfig {
fn default() -> Self { fn new() -> Self {
let mut prod_id = AuraDevice::Unknown; // Self::create_default(AuraDevice::X19b6, &LaptopLedData::get_data())
for prod in ASUS_KEYBOARD_DEVICES.iter() { panic!("AuraConfig::new() should not be used, use AuraConfig::create_default() instead");
if HidRaw::new(prod).is_ok() { }
prod_id = AuraDevice::from(*prod);
break;
}
}
if prod_id == AuraDevice::Unknown { fn config_dir() -> std::path::PathBuf {
if let Ok(p) = KeyboardLed::new() { std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
if p.has_kbd_rgb_mode() { }
prod_id = AuraDevice::Tuf;
}
}
}
let enabled = if prod_id == AuraDevice::X19B6 { fn file_name(&self) -> String {
AuraPowerConfig::AuraDev19b6(HashSet::from([ CONFIG_FILE.to_owned()
AuraDev19b6::BootLogo, }
AuraDev19b6::BootKeyb, }
AuraDev19b6::SleepLogo,
AuraDev19b6::SleepKeyb, impl StdConfigLoad for AuraConfig {}
AuraDev19b6::AwakeLogo,
AuraDev19b6::AwakeKeyb, impl AuraConfig {
AuraDev19b6::ShutdownLogo, pub fn create_default(prod_id: AuraDevice, support_data: &LaptopLedData) -> Self {
AuraDev19b6::ShutdownKeyb, // create a default config here
AuraDev19b6::BootBar, let enabled = if prod_id == AuraDevice::X19b6 {
AuraDev19b6::AwakeBar, AuraPowerConfig::AuraDevRog2(HashSet::from([
AuraDev19b6::SleepBar, AuraDevRog2::BootLogo,
AuraDev19b6::ShutdownBar, AuraDevRog2::BootKeyb,
AuraDevRog2::SleepLogo,
AuraDevRog2::SleepKeyb,
AuraDevRog2::AwakeLogo,
AuraDevRog2::AwakeKeyb,
AuraDevRog2::ShutdownLogo,
AuraDevRog2::ShutdownKeyb,
AuraDevRog2::BootBar,
AuraDevRog2::AwakeBar,
AuraDevRog2::SleepBar,
AuraDevRog2::ShutdownBar,
])) ]))
} else if prod_id == AuraDevice::Tuf { } else if prod_id == AuraDevice::Tuf {
AuraPowerConfig::AuraDevTuf(HashSet::from([ AuraPowerConfig::AuraDevTuf(HashSet::from([
@@ -165,76 +165,31 @@ impl Default for AuraConfig {
AuraDevTuf::Keyboard, AuraDevTuf::Keyboard,
])) ]))
} else { } else {
AuraPowerConfig::AuraDev1866(HashSet::from([ AuraPowerConfig::AuraDevRog1(HashSet::from([
AuraDev1866::Awake, AuraDevRog1::Awake,
AuraDev1866::Boot, AuraDevRog1::Boot,
AuraDev1866::Sleep, AuraDevRog1::Sleep,
AuraDev1866::Keyboard, AuraDevRog1::Keyboard,
AuraDev1866::Lightbar, AuraDevRog1::Lightbar,
])) ]))
}; };
let mut config = AuraConfig {
AuraConfig {
brightness: LedBrightness::Med, brightness: LedBrightness::Med,
current_mode: AuraModeNum::Static, current_mode: AuraModeNum::Static,
builtins: BTreeMap::new(), builtins: BTreeMap::new(),
multizone: None, multizone: None,
multizone_on: false, multizone_on: false,
enabled, enabled,
} };
}
}
impl AuraConfig { for n in &support_data.basic_modes {
/// `load` will attempt to read the config, and panic if the dir is missing
pub fn load(supported_led_modes: &LaptopLedData) -> Self {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(AURA_CONFIG_PATH)
.unwrap_or_else(|_| {
panic!(
"The file {} or directory /etc/asusd/ is missing",
AURA_CONFIG_PATH
)
}); // okay to cause panic here
let mut buf = String::new();
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
return AuraConfig::create_default(&mut file, supported_led_modes);
} else {
if let Ok(data) = serde_json::from_str(&buf) {
return data;
}
warn!(
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
AURA_CONFIG_PATH, AURA_CONFIG_PATH
);
let cfg_old = AURA_CONFIG_PATH.to_string() + "-old";
std::fs::rename(AURA_CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",
AURA_CONFIG_PATH, err
)
});
}
}
AuraConfig::create_default(&mut file, supported_led_modes)
}
fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self {
// create a default config here
let mut config = AuraConfig::default();
for n in &support_data.standard {
config config
.builtins .builtins
.insert(*n, AuraEffect::default_with_mode(*n)); .insert(*n, AuraEffect::default_with_mode(*n));
if !support_data.multizone.is_empty() { if !support_data.basic_zones.is_empty() {
let mut default = vec![]; let mut default = vec![];
for (i, tmp) in support_data.multizone.iter().enumerate() { for (i, tmp) in support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect { default.push(AuraEffect {
mode: *n, mode: *n,
zone: *tmp, zone: *tmp,
@@ -242,7 +197,7 @@ impl AuraConfig {
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]), colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Left, direction: Direction::Left,
}) });
} }
if let Some(m) = config.multizone.as_mut() { if let Some(m) = config.multizone.as_mut() {
m.insert(*n, default); m.insert(*n, default);
@@ -253,68 +208,37 @@ impl AuraConfig {
} }
} }
} }
// Should be okay to unwrap this as is since it is a Default
let json = serde_json::to_string(&config).unwrap();
file.write_all(json.as_bytes())
.unwrap_or_else(|_| panic!("Could not write {}", AURA_CONFIG_PATH));
config config
} }
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(AURA_CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("File is empty {}", AURA_CONFIG_PATH);
} else {
let x = serde_json::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH));
*self = x;
}
}
}
pub fn write(&self) {
let mut file = File::create(AURA_CONFIG_PATH).expect("Couldn't overwrite config");
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
file.write_all(json.as_bytes())
.unwrap_or_else(|err| error!("Could not write config: {}", err));
}
/// Set the mode data, current mode, and if multizone enabled. /// Set the mode data, current mode, and if multizone enabled.
/// ///
/// Multipurpose, will accept AuraEffect with zones and put in the correct store. /// Multipurpose, will accept `AuraEffect` with zones and put in the correct
/// store.
pub fn set_builtin(&mut self, effect: AuraEffect) { pub fn set_builtin(&mut self, effect: AuraEffect) {
self.current_mode = effect.mode; self.current_mode = effect.mode;
match effect.zone() { if effect.zone() == AuraZone::None {
AuraZone::None => { self.builtins.insert(*effect.mode(), effect);
self.builtins.insert(*effect.mode(), effect); self.multizone_on = false;
self.multizone_on = false; } else {
} if let Some(multi) = self.multizone.as_mut() {
_ => { if let Some(fx) = multi.get_mut(effect.mode()) {
if let Some(multi) = self.multizone.as_mut() { for fx in fx.iter_mut() {
if let Some(fx) = multi.get_mut(effect.mode()) { if fx.zone == effect.zone {
for fx in fx.iter_mut() { *fx = effect;
if fx.zone == effect.zone { return;
*fx = effect;
return;
}
} }
fx.push(effect);
} else {
multi.insert(*effect.mode(), vec![effect]);
} }
fx.push(effect);
} else { } else {
let mut tmp = BTreeMap::new(); multi.insert(*effect.mode(), vec![effect]);
tmp.insert(*effect.mode(), vec![effect]);
self.multizone = Some(tmp);
} }
self.multizone_on = true; } else {
let mut tmp = BTreeMap::new();
tmp.insert(*effect.mode(), vec![effect]);
self.multizone = Some(tmp);
} }
self.multizone_on = true;
} }
} }
@@ -328,12 +252,15 @@ impl AuraConfig {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::AuraConfig; use rog_aura::aura_detection::LaptopLedData;
use rog_aura::usb::AuraDevice;
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour}; use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use super::AuraConfig;
#[test] #[test]
fn set_multizone_4key_config() { fn set_multizone_4key_config() {
let mut config = AuraConfig::default(); let mut config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let effect = AuraEffect { let effect = AuraEffect {
colour1: Colour(0xff, 0x00, 0xff), colour1: Colour(0xff, 0x00, 0xff),
@@ -379,7 +306,7 @@ mod tests {
#[test] #[test]
fn set_multizone_multimode_config() { fn set_multizone_multimode_config() {
let mut config = AuraConfig::default(); let mut config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let effect = AuraEffect { let effect = AuraEffect {
zone: AuraZone::Key1, zone: AuraZone::Key1,

View File

@@ -1,19 +1,18 @@
use crate::{
error::RogError,
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
};
use log::{info, warn};
use rog_aura::{
usb::{AuraDevice, LED_APPLY, LED_SET},
AuraEffect, KeyColourArray, LedBrightness, PerKeyRaw, LED_MSG_LEN,
};
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
use rog_platform::{hid_raw::HidRaw, keyboard_led::KeyboardLed, supported::LedSupportedFunctions};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::GetSupported; use config_traits::{StdConfig, StdConfigLoad};
use log::{info, warn};
use rog_aura::advanced::{LedUsbPackets, UsbPackets};
use rog_aura::aura_detection::{LaptopLedData, ASUS_KEYBOARD_DEVICES};
use rog_aura::usb::{AuraDevice, LED_APPLY, LED_SET};
use rog_aura::{AuraEffect, AuraZone, Direction, LedBrightness, Speed, GRADIENT, LED_MSG_LEN};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use rog_platform::supported::LedSupportedFunctions;
use super::config::{AuraConfig, AuraPowerConfig}; use super::config::{AuraConfig, AuraPowerConfig};
use crate::error::RogError;
use crate::GetSupported;
impl GetSupported for CtrlKbdLed { impl GetSupported for CtrlKbdLed {
type A = LedSupportedFunctions; type A = LedSupportedFunctions;
@@ -21,14 +20,14 @@ impl GetSupported for CtrlKbdLed {
fn get_supported() -> Self::A { fn get_supported() -> Self::A {
// let mode = <&str>::from(&<AuraModes>::from(*mode)); // let mode = <&str>::from(&<AuraModes>::from(*mode));
let laptop = LaptopLedData::get_data(); let laptop = LaptopLedData::get_data();
let stock_led_modes = laptop.standard; let stock_led_modes = laptop.basic_modes;
let multizone_led_mode = laptop.multizone; let multizone_led_mode = laptop.basic_zones;
let per_key_led_mode = laptop.per_key; let advanced_type = laptop.advanced_type;
let mut prod_id = AuraDevice::Unknown; let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES.iter() { for prod in ASUS_KEYBOARD_DEVICES {
if HidRaw::new(prod).is_ok() { if HidRaw::new(prod.into()).is_ok() {
prod_id = AuraDevice::from(*prod); prod_id = prod;
break; break;
} }
} }
@@ -41,11 +40,11 @@ impl GetSupported for CtrlKbdLed {
} }
LedSupportedFunctions { LedSupportedFunctions {
prod_id, dev_id: prod_id,
brightness_set: rgb.is_ok(), brightness: rgb.is_ok(),
stock_led_modes, basic_modes: stock_led_modes,
multizone_led_mode, basic_zones: multizone_led_mode,
per_key_led_mode, advanced_type: advanced_type.into(),
} }
} }
} }
@@ -59,7 +58,7 @@ pub enum LEDNode {
pub struct CtrlKbdLed { pub struct CtrlKbdLed {
// TODO: config stores the keyboard type as an AuraPower, use or update this // TODO: config stores the keyboard type as an AuraPower, use or update this
pub led_prod: Option<String>, pub led_prod: AuraDevice,
pub led_node: LEDNode, pub led_node: LEDNode,
pub kd_brightness: KeyboardLed, pub kd_brightness: KeyboardLed,
pub supported_modes: LaptopLedData, pub supported_modes: LaptopLedData,
@@ -69,34 +68,43 @@ pub struct CtrlKbdLed {
} }
impl CtrlKbdLed { impl CtrlKbdLed {
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> { pub fn new(supported_modes: LaptopLedData) -> Result<Self, RogError> {
let mut led_prod = None; let mut led_prod = AuraDevice::Unknown;
let mut led_node = None; let mut usb_node = None;
for prod in ASUS_KEYBOARD_DEVICES.iter() { for prod in ASUS_KEYBOARD_DEVICES {
match HidRaw::new(prod) { match HidRaw::new(prod.into()) {
Ok(node) => { Ok(node) => {
led_prod = Some(prod.to_string()); led_prod = prod;
led_node = Some(node); usb_node = Some(node);
info!("Looked for keyboard controller 0x{prod}: Found"); info!(
"Looked for keyboard controller 0x{}: Found",
<&str>::from(prod)
);
break; break;
} }
Err(err) => info!("Looked for keyboard controller 0x{prod}: {err}"), Err(err) => info!(
"Looked for keyboard controller 0x{}: {err}",
<&str>::from(prod)
),
} }
} }
let rgb_led = KeyboardLed::new()?; let rgb_led = KeyboardLed::new()?;
if led_node.is_none() && !rgb_led.has_kbd_rgb_mode() { if usb_node.is_none() && !rgb_led.has_kbd_rgb_mode() {
let dmi = sysfs_class::DmiId::default(); let dmi = sysfs_class::DmiId::default();
if let Ok(prod_family) = dmi.product_family() { if let Ok(prod_family) = dmi.product_family() {
if prod_family.contains("TUF") { if prod_family.contains("TUF") {
warn!("A kernel patch is in progress for TUF RGB support"); warn!(
"kbd_rgb_mode was not found in the /sys/. You require a minimum 6.1 \
kernel and a supported TUF laptop"
);
} }
} }
return Err(RogError::NoAuraKeyboard); return Err(RogError::NoAuraKeyboard);
} }
let led_node = if let Some(rog) = led_node { let led_node = if let Some(rog) = usb_node {
info!("Found ROG USB keyboard"); info!("Found ROG USB keyboard");
LEDNode::Rog(rog) LEDNode::Rog(rog)
} else if rgb_led.has_kbd_rgb_mode() { } else if rgb_led.has_kbd_rgb_mode() {
@@ -106,9 +114,18 @@ impl CtrlKbdLed {
LEDNode::None LEDNode::None
}; };
let config_init = AuraConfig::create_default(led_prod, &supported_modes);
let mut config = config_init.clone().load();
// Do updates of supported modes if required
for mode in &config_init.builtins {
if !config.builtins.contains_key(mode.0) {
config.builtins.insert(*mode.0, mode.1.clone());
}
}
let ctrl = CtrlKbdLed { let ctrl = CtrlKbdLed {
led_prod, led_prod,
led_node, led_node, // on TUF this is the same as rgb_led / kd_brightness
kd_brightness: rgb_led, // If was none then we already returned above kd_brightness: rgb_led, // If was none then we already returned above
supported_modes, supported_modes,
flip_effect_write: false, flip_effect_write: false,
@@ -152,7 +169,8 @@ impl CtrlKbdLed {
self.set_brightness(self.config.brightness) self.set_brightness(self.config.brightness)
} }
/// Set combination state for boot animation/sleep animation/all leds/keys leds/side leds LED active /// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub(super) fn set_power_states(&mut self) -> Result<(), RogError> { pub(super) fn set_power_states(&mut self) -> Result<(), RogError> {
if let LEDNode::KbdLed(platform) = &mut self.led_node { if let LEDNode::KbdLed(platform) = &mut self.led_node {
if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) { if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) {
@@ -173,12 +191,12 @@ impl CtrlKbdLed {
/// Set an Aura effect if the effect mode or zone is supported. /// 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 /// On success the aura config file is read to refresh cached values, then
/// stored and config written to disk. /// the effect is stored and config written to disk.
pub(crate) fn set_effect(&mut self, effect: AuraEffect) -> Result<(), RogError> { pub(crate) fn set_effect(&mut self, effect: AuraEffect) -> Result<(), RogError> {
if !self.supported_modes.standard.contains(&effect.mode) if !self.supported_modes.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None || effect.zone != AuraZone::None
&& !self.supported_modes.multizone.contains(&effect.zone) && !self.supported_modes.basic_zones.contains(&effect.zone)
{ {
return Err(RogError::AuraEffectNotSupported); return Err(RogError::AuraEffectNotSupported);
} }
@@ -193,7 +211,7 @@ impl CtrlKbdLed {
/// Write an effect block. This is for per-key, but can be repurposed to /// 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 /// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid. /// only the first `Vec` (`effect[0]`) is valid.
pub fn write_effect_block(&mut self, effect: &PerKeyRaw) -> Result<(), RogError> { pub fn write_effect_block(&mut self, effect: &UsbPackets) -> Result<(), RogError> {
let pkt_type = effect[0][1]; let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc; const PER_KEY_TYPE: u8 = 0xbc;
@@ -207,7 +225,7 @@ impl CtrlKbdLed {
} else { } else {
if !self.per_key_mode_active { if !self.per_key_mode_active {
if let LEDNode::Rog(hid_raw) = &self.led_node { if let LEDNode::Rog(hid_raw) = &self.led_node {
let init = KeyColourArray::get_init_msg(); let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?; hid_raw.write_bytes(&init)?;
} }
self.per_key_mode_active = true; self.per_key_mode_active = true;
@@ -233,7 +251,7 @@ impl CtrlKbdLed {
let current = self.config.current_mode; let current = self.config.current_mode;
if let Some(idx) = self if let Some(idx) = self
.supported_modes .supported_modes
.standard .basic_modes
.iter() .iter()
.position(|v| *v == current) .position(|v| *v == current)
{ {
@@ -241,17 +259,17 @@ impl CtrlKbdLed {
// goes past end of array // goes past end of array
if reverse { if reverse {
if idx == 0 { if idx == 0 {
idx = self.supported_modes.standard.len() - 1; idx = self.supported_modes.basic_modes.len() - 1;
} else { } else {
idx -= 1; idx -= 1;
} }
} else { } else {
idx += 1; idx += 1;
if idx == self.supported_modes.standard.len() { if idx == self.supported_modes.basic_modes.len() {
idx = 0; idx = 0;
} }
} }
let next = self.supported_modes.standard[idx]; let next = self.supported_modes.basic_modes[idx];
self.config.read(); self.config.read();
// if self.config.builtins.contains_key(&next) { // if self.config.builtins.contains_key(&next) {
@@ -324,10 +342,11 @@ impl CtrlKbdLed {
Ok(()) Ok(())
} }
/// Create a default for the `current_mode` if multizone and no config exists. /// Create a default for the `current_mode` if multizone and no config
/// exists.
fn create_multizone_default(&mut self) -> Result<(), RogError> { fn create_multizone_default(&mut self) -> Result<(), RogError> {
let mut default = vec![]; let mut default = vec![];
for (i, tmp) in self.supported_modes.multizone.iter().enumerate() { for (i, tmp) in self.supported_modes.basic_zones.iter().enumerate() {
default.push(AuraEffect { default.push(AuraEffect {
mode: self.config.current_mode, mode: self.config.current_mode,
zone: *tmp, zone: *tmp,
@@ -335,7 +354,7 @@ impl CtrlKbdLed {
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]), colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Left, direction: Direction::Left,
}) });
} }
if default.is_empty() { if default.is_empty() {
return Err(RogError::AuraEffectNotSupported); return Err(RogError::AuraEffectNotSupported);
@@ -354,30 +373,29 @@ impl CtrlKbdLed {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rog_aura::aura_detection::LaptopLedData;
use rog_aura::usb::AuraDevice;
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour}; use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use rog_platform::keyboard_led::KeyboardLed; use rog_platform::keyboard_led::KeyboardLed;
use crate::{
ctrl_aura::{config::AuraConfig, controller::LEDNode},
laptops::LaptopLedData,
};
use super::CtrlKbdLed; use super::CtrlKbdLed;
use crate::ctrl_aura::config::AuraConfig;
use crate::ctrl_aura::controller::LEDNode;
#[test] #[test]
// #[ignore = "Must be manually run due to detection stage"] // #[ignore = "Must be manually run due to detection stage"]
fn check_set_mode_errors() { fn check_set_mode_errors() {
// Checking to ensure set_mode errors when unsupported modes are tried // Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default(); let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let supported_modes = LaptopLedData { let supported_modes = LaptopLedData {
prod_family: "".into(), board_name: String::new(),
board_names: vec![], layout_name: "ga401".to_owned(),
standard: vec![AuraModeNum::Static], basic_modes: vec![AuraModeNum::Static],
multizone: vec![], basic_zones: vec![],
per_key: false, advanced_type: rog_aura::AdvancedAuraType::None,
}; };
let mut controller = CtrlKbdLed { let mut controller = CtrlKbdLed {
led_prod: None, led_prod: AuraDevice::X19b6,
led_node: LEDNode::None, led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(), kd_brightness: KeyboardLed::default(),
supported_modes, supported_modes,
@@ -392,7 +410,8 @@ mod tests {
..Default::default() ..Default::default()
}; };
// This error comes from write_bytes because we don't have a keyboard node stored // This error comes from write_bytes because we don't have a keyboard node
// stored
assert_eq!( assert_eq!(
controller controller
.set_effect(effect.clone()) .set_effect(effect.clone())
@@ -420,7 +439,7 @@ mod tests {
"Aura effect not supported" "Aura effect not supported"
); );
controller.supported_modes.multizone.push(AuraZone::Key2); controller.supported_modes.basic_zones.push(AuraZone::Key2);
assert_eq!( assert_eq!(
controller.set_effect(effect).unwrap_err().to_string(), controller.set_effect(effect).unwrap_err().to_string(),
"No supported Aura keyboard" "No supported Aura keyboard"
@@ -430,16 +449,16 @@ mod tests {
#[test] #[test]
fn create_multizone_if_no_config() { fn create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried // Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default(); let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let supported_modes = LaptopLedData { let supported_modes = LaptopLedData {
prod_family: "".into(), board_name: String::new(),
board_names: vec![], layout_name: "ga401".to_owned(),
standard: vec![AuraModeNum::Static], basic_modes: vec![AuraModeNum::Static],
multizone: vec![], basic_zones: vec![],
per_key: false, advanced_type: rog_aura::AdvancedAuraType::None,
}; };
let mut controller = CtrlKbdLed { let mut controller = CtrlKbdLed {
led_prod: None, led_prod: AuraDevice::X19b6,
led_node: LEDNode::None, led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(), kd_brightness: KeyboardLed::default(),
supported_modes, supported_modes,
@@ -452,8 +471,8 @@ mod tests {
assert!(controller.create_multizone_default().is_err()); assert!(controller.create_multizone_default().is_err());
assert!(controller.config.multizone.is_none()); assert!(controller.config.multizone.is_none());
controller.supported_modes.multizone.push(AuraZone::Key1); controller.supported_modes.basic_zones.push(AuraZone::Key1);
controller.supported_modes.multizone.push(AuraZone::Key2); controller.supported_modes.basic_zones.push(AuraZone::Key2);
assert!(controller.create_multizone_default().is_ok()); assert!(controller.create_multizone_default().is_ok());
assert!(controller.config.multizone.is_some()); assert!(controller.config.multizone.is_some());
@@ -468,16 +487,16 @@ mod tests {
#[test] #[test]
fn next_mode_create_multizone_if_no_config() { fn next_mode_create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried // Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default(); let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let supported_modes = LaptopLedData { let supported_modes = LaptopLedData {
prod_family: "".into(), board_name: String::new(),
board_names: vec![], layout_name: "ga401".to_owned(),
standard: vec![AuraModeNum::Static], basic_modes: vec![AuraModeNum::Static],
multizone: vec![AuraZone::Key1, AuraZone::Key2], basic_zones: vec![AuraZone::Key1, AuraZone::Key2],
per_key: false, advanced_type: rog_aura::AdvancedAuraType::None,
}; };
let mut controller = CtrlKbdLed { let mut controller = CtrlKbdLed {
led_prod: None, led_prod: AuraDevice::X19b6,
led_node: LEDNode::None, led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(), kd_brightness: KeyboardLed::default(),
supported_modes, supported_modes,

View File

@@ -1,4 +1,4 @@
pub mod config; pub mod config;
pub mod controller; pub mod controller;
/// Implements CtrlTask, Reloadable, ZbusRun /// Implements `CtrlTask`, `Reloadable`, `ZbusRun`
pub mod trait_impls; pub mod trait_impls;

View File

@@ -1,19 +1,19 @@
use async_trait::async_trait; use std::collections::BTreeMap;
use log::{error, info, warn}; use std::sync::Arc;
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness, PerKeyRaw};
use std::{collections::BTreeMap, sync::Arc};
use zbus::{
dbus_interface,
export::futures_util::{
lock::{Mutex, MutexGuard},
StreamExt,
},
Connection, SignalContext,
};
use crate::{error::RogError, CtrlTask}; use async_trait::async_trait;
use config_traits::StdConfig;
use log::{error, info, warn};
use rog_aura::advanced::UsbPackets;
use rog_aura::usb::AuraPowerDev;
use rog_aura::{AuraEffect, AuraModeNum, LedBrightness};
use zbus::export::futures_util::lock::{Mutex, MutexGuard};
use zbus::export::futures_util::StreamExt;
use zbus::{dbus_interface, Connection, SignalContext};
use super::controller::CtrlKbdLed; use super::controller::CtrlKbdLed;
use crate::error::RogError;
use crate::CtrlTask;
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Aura"; pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Aura";
@@ -207,7 +207,10 @@ impl CtrlKbdLedZbus {
ctrl.config.builtins.clone() ctrl.config.builtins.clone()
} }
async fn per_key_raw(&self, data: PerKeyRaw) -> zbus::fdo::Result<()> { /// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: UsbPackets) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.write_effect_block(&data)?; ctrl.write_effect_block(&data)?;
Ok(()) Ok(())
@@ -237,7 +240,7 @@ impl CtrlTask for CtrlKbdLedZbus {
} }
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> { async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| { let load_save = |start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| {
// If waking up // If waking up
if !start { if !start {
info!("CtrlKbdLedTask reloading brightness and modes"); info!("CtrlKbdLedTask reloading brightness and modes");
@@ -262,28 +265,32 @@ impl CtrlTask for CtrlKbdLedZbus {
self.create_sys_event_tasks( self.create_sys_event_tasks(
// Loop so that we do aquire the lock but also don't block other // Loop so that we do aquire the lock but also don't block other
// threads (prevents potential deadlocks) // threads (prevents potential deadlocks)
move || loop { move || {
if let Some(lock) = inner1.try_lock() { let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
load_save(true, lock); load_save(true, lock);
break;
} }
}, },
move || loop { move || {
if let Some(lock) = inner2.try_lock() { let inner2 = inner2.clone();
async move {
let lock = inner2.lock().await;
load_save(false, lock); load_save(false, lock);
break;
} }
}, },
move || loop { move || {
if let Some(lock) = inner3.try_lock() { let inner3 = inner3.clone();
load_save(true, lock); async move {
break; let lock = inner3.lock().await;
} load_save(false, lock);
}, }
move || loop { },
if let Some(lock) = inner4.try_lock() { move || {
let inner4 = inner4.clone();
async move {
let lock = inner4.lock().await;
load_save(false, lock); load_save(false, lock);
break;
} }
}, },
) )

View File

@@ -1,17 +1,20 @@
use crate::{config::Config, error::RogError, GetSupported};
use crate::{task_watch_item, CtrlTask};
use async_trait::async_trait;
use log::{info, warn};
use rog_platform::platform::{AsusPlatform, GpuMode};
use rog_platform::supported::RogBiosSupportedFunctions;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::{info, warn};
use rog_platform::platform::{AsusPlatform, GpuMode};
use rog_platform::supported::RogBiosSupportedFunctions;
use zbus::export::futures_util::lock::Mutex; use zbus::export::futures_util::lock::Mutex;
use zbus::Connection; use zbus::{dbus_interface, Connection, SignalContext};
use zbus::{dbus_interface, SignalContext};
use crate::config::Config;
use crate::error::RogError;
use crate::{task_watch_item, CtrlTask, GetSupported};
const ZBUS_PATH: &str = "/org/asuslinux/Platform"; const ZBUS_PATH: &str = "/org/asuslinux/Platform";
const ASUS_POST_LOGO_SOUND: &str = const ASUS_POST_LOGO_SOUND: &str =
@@ -89,15 +92,8 @@ impl CtrlPlatform {
} }
pub fn get_boot_sound() -> Result<i8, RogError> { pub fn get_boot_sound() -> Result<i8, RogError> {
let path = ASUS_POST_LOGO_SOUND; let data = std::fs::read(ASUS_POST_LOGO_SOUND)
let mut file = OpenOptions::new() .map_err(|err| RogError::Read(ASUS_POST_LOGO_SOUND.into(), err))?;
.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; let idx = data.len() - 1;
Ok(data[idx] as i8) Ok(data[idx] as i8)
@@ -112,6 +108,7 @@ impl CtrlPlatform {
.map_err(|err| RogError::Path(path.into(), err))?; .map_err(|err| RogError::Path(path.into(), err))?;
let mut data = Vec::new(); let mut data = Vec::new();
#[allow(clippy::verbose_file_reads)]
file.read_to_end(&mut data) file.read_to_end(&mut data)
.map_err(|err| RogError::Read(path.into(), err))?; .map_err(|err| RogError::Read(path.into(), err))?;
@@ -214,7 +211,8 @@ impl CtrlPlatform {
}; };
} }
/// Get the `panel_od` value from platform. Updates the stored value in internal config also. /// Get the `panel_od` value from platform. Updates the stored value in
/// internal config also.
fn panel_od(&self) -> bool { fn panel_od(&self) -> bool {
let od = self let od = self
.platform .platform
@@ -317,9 +315,12 @@ impl crate::Reloadable for CtrlPlatform {
impl CtrlPlatform { impl CtrlPlatform {
task_watch_item!(panel_od platform); task_watch_item!(panel_od platform);
task_watch_item!(dgpu_disable platform); task_watch_item!(dgpu_disable platform);
task_watch_item!(egpu_enable platform); task_watch_item!(egpu_enable platform);
task_watch_item!(gpu_mux_mode platform); // NOTE: see note further below
// task_watch_item!(gpu_mux_mode platform);
} }
#[async_trait] #[async_trait]
@@ -332,10 +333,12 @@ impl CtrlTask for CtrlPlatform {
let platform1 = self.clone(); let platform1 = self.clone();
let platform2 = self.clone(); let platform2 = self.clone();
self.create_sys_event_tasks( self.create_sys_event_tasks(
move || {}, move || async { {} },
move || { move || {
info!("CtrlRogBios reloading panel_od"); let platform1 = platform1.clone();
if let Some(lock) = platform1.config.try_lock() { async move {
info!("CtrlRogBios reloading panel_od");
let lock = platform1.config.lock().await;
if platform1.platform.has_panel_od() { if platform1.platform.has_panel_od() {
platform1 platform1
.set_panel_overdrive(lock.panel_od) .set_panel_overdrive(lock.panel_od)
@@ -347,10 +350,12 @@ impl CtrlTask for CtrlPlatform {
} }
} }
}, },
move || {}, move || async { {} },
move || { move || {
info!("CtrlRogBios reloading panel_od"); let platform2 = platform2.clone();
if let Some(lock) = platform2.config.try_lock() { async move {
info!("CtrlRogBios reloading panel_od");
let lock = platform2.config.lock().await;
if platform2.platform.has_panel_od() { if platform2.platform.has_panel_od() {
platform2 platform2
.set_panel_overdrive(lock.panel_od) .set_panel_overdrive(lock.panel_od)
@@ -368,7 +373,9 @@ impl CtrlTask for CtrlPlatform {
self.watch_panel_od(signal_ctxt.clone()).await?; self.watch_panel_od(signal_ctxt.clone()).await?;
self.watch_dgpu_disable(signal_ctxt.clone()).await?; self.watch_dgpu_disable(signal_ctxt.clone()).await?;
self.watch_egpu_enable(signal_ctxt.clone()).await?; self.watch_egpu_enable(signal_ctxt.clone()).await?;
self.watch_gpu_mux_mode(signal_ctxt.clone()).await?; // NOTE: Can't have this as a watch because on a write to it, it reverts back to
// booted-with value as it does not actually change until reboot.
// self.watch_gpu_mux_mode(signal_ctxt.clone()).await?;
Ok(()) Ok(())
} }

View File

@@ -1,17 +1,20 @@
use crate::systemd::{do_systemd_unit_action, SystemdUnitAction}; use std::process::Command;
use crate::{config::Config, error::RogError, GetSupported};
use crate::{task_watch_item, CtrlTask};
use async_trait::async_trait;
use log::{info, warn};
use rog_platform::power::AsusPower;
use rog_platform::supported::ChargeSupportedFunctions;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::{error, info, warn};
use rog_platform::power::AsusPower;
use rog_platform::supported::ChargeSupportedFunctions;
use systemd_zbus::{ManagerProxy as SystemdProxy, Mode, UnitFileState};
use tokio::time::sleep; use tokio::time::sleep;
use zbus::dbus_interface;
use zbus::export::futures_util::lock::Mutex; use zbus::export::futures_util::lock::Mutex;
use zbus::Connection; use zbus::{dbus_interface, Connection, SignalContext};
use zbus::SignalContext;
use crate::config::Config;
use crate::error::RogError;
use crate::{task_watch_item, CtrlTask, GetSupported};
const ZBUS_PATH: &str = "/org/asuslinux/Power"; const ZBUS_PATH: &str = "/org/asuslinux/Power";
const NVIDIA_POWERD: &str = "nvidia-powerd.service"; const NVIDIA_POWERD: &str = "nvidia-powerd.service";
@@ -117,6 +120,8 @@ impl crate::Reloadable for CtrlPower {
} }
impl CtrlPower { impl CtrlPower {
task_watch_item!(charge_control_end_threshold power);
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> { pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
Ok(CtrlPower { Ok(CtrlPower {
power: AsusPower::new()?, power: AsusPower::new()?,
@@ -141,8 +146,6 @@ impl CtrlPower {
Ok(()) Ok(())
} }
task_watch_item!(charge_control_end_threshold power);
} }
#[async_trait] #[async_trait]
@@ -152,58 +155,62 @@ impl CtrlTask for CtrlPower {
} }
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let conn = zbus::Connection::system().await?;
let sysd1 = SystemdProxy::new(&conn).await?;
let sysd2 = sysd1.clone();
let sysd3 = sysd1.clone();
let power1 = self.clone(); let power1 = self.clone();
let power2 = self.clone(); let power2 = self.clone();
self.create_sys_event_tasks( self.create_sys_event_tasks(
move || {}, move || async {},
move || { move || {
info!("CtrlCharge reloading charge limit"); let power = power1.clone();
if let Some(lock) = power1.config.try_lock() { let sysd = sysd1.clone();
power1 async move {
info!("CtrlCharge reloading charge limit");
let lock = power.config.lock().await;
power
.set(lock.bat_charge_limit) .set(lock.bat_charge_limit)
.map_err(|err| { .map_err(|err| {
warn!("CtrlCharge: set_limit {}", err); warn!("CtrlCharge: set_limit {}", err);
err err
}) })
.ok(); .ok();
}
if let Ok(value) = power1.power.get_online() { if lock.disable_nvidia_powerd_on_battery {
let action = if value == 1 { if let Ok(value) = power.power.get_online() {
SystemdUnitAction::Restart do_nvidia_powerd_action(&sysd, value == 1).await;
} else { }
SystemdUnitAction::Stop
};
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
} }
} }
}, },
move || {}, move || async {},
move || { move || {
info!("CtrlCharge reloading charge limit"); let power = power2.clone();
if let Some(lock) = power2.config.try_lock() { let sysd = sysd2.clone();
power2 async move {
info!("CtrlCharge reloading charge limit");
let lock = power.config.lock().await;
power
.set(lock.bat_charge_limit) .set(lock.bat_charge_limit)
.map_err(|err| { .map_err(|err| {
warn!("CtrlCharge: set_limit {}", err); warn!("CtrlCharge: set_limit {}", err);
err err
}) })
.ok(); .ok();
}
if let Ok(value) = power2.power.get_online() { if lock.disable_nvidia_powerd_on_battery {
let action = if value == 1 { if let Ok(value) = power.power.get_online() {
SystemdUnitAction::Restart do_nvidia_powerd_action(&sysd, value == 1).await;
} else { }
SystemdUnitAction::Stop
};
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
} }
} }
}, },
) )
.await; .await;
let config = self.config.clone();
self.watch_charge_control_end_threshold(signal_ctxt.clone()) self.watch_charge_control_end_threshold(signal_ctxt.clone())
.await?; .await?;
@@ -214,18 +221,39 @@ impl CtrlTask for CtrlPower {
if let Ok(value) = ctrl.power.get_online() { if let Ok(value) = ctrl.power.get_online() {
if online != value { if online != value {
online = value; online = value;
let action = if value == 1 { let mut config = config.lock().await;
SystemdUnitAction::Restart config.read();
} else {
SystemdUnitAction::Stop if config.disable_nvidia_powerd_on_battery {
}; do_nvidia_powerd_action(&sysd3, value == 1).await;
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
} }
Self::notify_mains_online(&signal_ctxt, value == 1) Self::notify_mains_online(&signal_ctxt, value == 1)
.await .await
.unwrap(); .unwrap();
let mut prog: Vec<&str> = Vec::new();
if value == 1 {
// AC ONLINE
prog = config.ac_command.split_whitespace().collect();
} else if value == 0 {
// BATTERY
prog = config.bat_command.split_whitespace().collect();
}
if prog.len() > 1 {
let mut cmd = Command::new(prog[0]);
for arg in prog.iter().skip(1) {
cmd.arg(*arg);
}
if let Err(e) = cmd.spawn() {
if value == 1 {
error!("AC power command error: {e}");
} else {
error!("Battery power command error: {e}");
}
}
}
} }
} }
// The inotify doesn't pick up events when the kernel changes internal value // The inotify doesn't pick up events when the kernel changes internal value
@@ -237,3 +265,23 @@ impl CtrlTask for CtrlPower {
Ok(()) Ok(())
} }
} }
async fn do_nvidia_powerd_action(proxy: &SystemdProxy<'_>, ac_on: bool) {
if let Ok(res) = proxy.get_unit_file_state(NVIDIA_POWERD).await {
if res == UnitFileState::Enabled {
if ac_on {
proxy
.start_unit(NVIDIA_POWERD, Mode::Replace)
.await
.map_err(|e| error!("Error stopping {NVIDIA_POWERD}, {e:?}"))
.ok();
} else {
proxy
.stop_unit(NVIDIA_POWERD, Mode::Replace)
.await
.map_err(|e| error!("Error stopping {NVIDIA_POWERD}, {e:?}"))
.ok();
}
}
}
}

View File

@@ -1,87 +1,60 @@
use log::{error, warn}; use std::path::PathBuf;
use rog_profiles::{FanCurveProfiles, Profile};
use config_traits::{StdConfig, StdConfigLoad};
use rog_profiles::fan_curve_set::FanCurveSet;
use rog_profiles::Profile;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write}; use crate::CONFIG_PATH_BASE;
const CONFIG_FILE: &str = "profile.ron";
const CONFIG_FAN_FILE: &str = "fan_curves.ron";
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct ProfileConfig { pub struct ProfileConfig {
#[serde(skip)]
config_path: String,
/// For restore on boot /// For restore on boot
pub active_profile: Profile, pub active_profile: Profile,
/// States to restore
pub fan_curves: Option<FanCurveProfiles>,
} }
impl ProfileConfig { impl StdConfig for ProfileConfig {
fn new(config_path: String) -> Self { fn new() -> Self {
Self { Self {
config_path,
active_profile: Profile::Balanced, active_profile: Profile::Balanced,
fan_curves: None,
} }
} }
pub fn load(config_path: String) -> Self { fn config_dir() -> std::path::PathBuf {
let mut file = OpenOptions::new() PathBuf::from(CONFIG_PATH_BASE)
.read(true)
.write(true)
.create(true)
.open(&config_path)
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here
let mut buf = String::new();
let mut config;
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
config = Self::new(config_path);
} else if let Ok(data) = toml::from_str(&buf) {
config = data;
config.config_path = config_path;
} else {
warn!(
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
config_path, config_path
);
let cfg_old = config_path.clone() + "-old";
std::fs::rename(config_path.clone(), cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",
config_path, err
)
});
config = Self::new(config_path);
}
} else {
config = Self::new(config_path);
}
config
} }
pub fn read(&mut self) { fn file_name(&self) -> String {
let mut file = OpenOptions::new() CONFIG_FILE.to_owned()
.read(true)
.open(&self.config_path)
.unwrap_or_else(|err| panic!("Error reading {}: {}", self.config_path, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("File is empty {}", self.config_path);
} else {
let mut data: ProfileConfig = toml::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", self.config_path));
// copy over serde skipped values
data.config_path = self.config_path.clone();
*self = data;
}
}
}
pub fn write(&self) {
let mut file = File::create(&self.config_path).expect("Couldn't overwrite config");
let data = toml::to_string(self).expect("Parse config to toml failed");
file.write_all(data.as_bytes())
.unwrap_or_else(|err| error!("Could not write config: {}", err));
} }
} }
impl StdConfigLoad for ProfileConfig {}
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurveConfig {
pub balanced: FanCurveSet,
pub performance: FanCurveSet,
pub quiet: FanCurveSet,
}
impl StdConfig for FanCurveConfig {
/// Create a new config. The defaults are zeroed so the device must be read
/// to get the actual device defaults.
fn new() -> Self {
Self::default()
}
fn config_dir() -> std::path::PathBuf {
PathBuf::from(CONFIG_PATH_BASE)
}
fn file_name(&self) -> String {
CONFIG_FAN_FILE.to_owned()
}
}
impl StdConfigLoad for FanCurveConfig {}

View File

@@ -1,15 +1,47 @@
use crate::error::RogError; use config_traits::{StdConfig, StdConfigLoad};
use crate::GetSupported;
use log::{info, warn}; use log::{info, warn};
use rog_platform::platform::AsusPlatform; use rog_platform::platform::AsusPlatform;
use rog_platform::supported::PlatformProfileFunctions; use rog_platform::supported::PlatformProfileFunctions;
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use rog_profiles::{FanCurveProfiles, Profile}; use rog_profiles::{FanCurveProfiles, Profile};
use super::config::ProfileConfig; use super::config::{FanCurveConfig, ProfileConfig};
use crate::error::RogError;
use crate::GetSupported;
// TODO: macro wrapper for warn/info/error log macros to add module name
const MOD_NAME: &str = "CtrlPlatformProfile";
pub struct FanCurves {
config_file: FanCurveConfig,
profiles: FanCurveProfiles,
}
impl FanCurves {
pub fn update_profiles_from_config(&mut self) {
self.profiles.balanced = self.config_file.balanced.clone();
self.profiles.performance = self.config_file.performance.clone();
self.profiles.quiet = self.config_file.quiet.clone();
}
pub fn update_config_from_profiles(&mut self) {
self.config_file.balanced = self.profiles.balanced.clone();
self.config_file.performance = self.profiles.performance.clone();
self.config_file.quiet = self.profiles.quiet.clone();
}
pub fn profiles(&self) -> &FanCurveProfiles {
&self.profiles
}
pub fn profiles_mut(&mut self) -> &mut FanCurveProfiles {
&mut self.profiles
}
}
pub struct CtrlPlatformProfile { pub struct CtrlPlatformProfile {
pub config: ProfileConfig, pub profile_config: ProfileConfig,
pub fan_curves: Option<FanCurves>,
pub platform: AsusPlatform, pub platform: AsusPlatform,
} }
@@ -18,7 +50,10 @@ impl GetSupported for CtrlPlatformProfile {
fn get_supported() -> Self::A { fn get_supported() -> Self::A {
if !Profile::is_platform_profile_supported() { if !Profile::is_platform_profile_supported() {
warn!("platform_profile kernel interface not found, your laptop does not support this, or the interface is missing."); warn!(
"platform_profile kernel interface not found, your laptop does not support this, \
or the interface is missing."
);
} }
let res = FanCurveProfiles::is_supported(); let res = FanCurveProfiles::is_supported();
@@ -28,7 +63,10 @@ impl GetSupported for CtrlPlatformProfile {
}; };
if !fan_curve_supported { if !fan_curve_supported {
info!("fan curves kernel interface not found, your laptop does not support this, or the interface is missing."); info!(
"fan curves kernel interface not found, your laptop does not support this, or the \
interface is missing."
);
} }
PlatformProfileFunctions { PlatformProfileFunctions {
@@ -42,25 +80,50 @@ impl CtrlPlatformProfile {
pub fn new(config: ProfileConfig) -> Result<Self, RogError> { pub fn new(config: ProfileConfig) -> Result<Self, RogError> {
let platform = AsusPlatform::new()?; let platform = AsusPlatform::new()?;
if platform.has_platform_profile() || platform.has_throttle_thermal_policy() { if platform.has_platform_profile() || platform.has_throttle_thermal_policy() {
info!("Device has profile control available"); info!("{MOD_NAME}: Device has profile control available");
let mut controller = CtrlPlatformProfile { config, platform }; let mut controller = CtrlPlatformProfile {
profile_config: config,
fan_curves: None,
platform,
};
if FanCurveProfiles::get_device().is_ok() { if FanCurveProfiles::get_device().is_ok() {
info!("Device has fan curves available"); info!("{MOD_NAME}: Device has fan curves available");
if controller.config.fan_curves.is_none() { let fan_config = FanCurveConfig::new();
controller.config.fan_curves = Some(Default::default()); // Only do defaults if the config doesn't already exist
if !fan_config.file_path().exists() {
info!("{MOD_NAME}: Fetching default fan curves");
controller.fan_curves = Some(FanCurves {
config_file: fan_config,
profiles: FanCurveProfiles::default(),
});
for _ in [Profile::Balanced, Profile::Performance, Profile::Quiet] { for _ in [Profile::Balanced, Profile::Performance, Profile::Quiet] {
// For each profile we need to switch to it before we
// can read the existing values from hardware. The ACPI method used
// for this is what limits us.
controller.set_next_profile()?; controller.set_next_profile()?;
// Make sure to set the baseline to default
controller.set_active_curve_to_defaults()?; controller.set_active_curve_to_defaults()?;
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
if let Some(curves) = controller.config.fan_curves.as_ref() {
if let Some(curves) = controller.fan_curves.as_ref() {
info!( info!(
"{active:?}: {}", "{MOD_NAME}: {active:?}: {}",
String::from(curves.get_fan_curves_for(active)) String::from(curves.profiles().get_fan_curves_for(active))
); );
} }
} }
if let Some(curves) = controller.fan_curves.as_ref() {
curves.config_file.write();
}
} else {
info!("{MOD_NAME}: Fan curves previously stored, loading...");
let mut fan_curves = FanCurves {
config_file: fan_config.load(),
profiles: FanCurveProfiles::default(),
};
fan_curves.update_profiles_from_config();
controller.fan_curves = Some(fan_curves);
} }
} }
@@ -70,25 +133,30 @@ impl CtrlPlatformProfile {
Err(ProfileError::NotSupported.into()) Err(ProfileError::NotSupported.into())
} }
pub fn save_config(&self) { pub fn save_config(&mut self) {
self.config.write(); self.profile_config.write();
if let Some(fans) = self.fan_curves.as_mut() {
fans.update_config_from_profiles();
fans.config_file.write(); // config write
}
} }
/// Toggle to next profile in list. This will first read the config, switch, then write out /// Toggle to next profile in list. This will first read the config, switch,
/// then write out
pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> { pub(super) fn set_next_profile(&mut self) -> Result<(), RogError> {
// Read first just incase the user has modified the config before calling this // Read first just incase the user has modified the config before calling this
match self.config.active_profile { match self.profile_config.active_profile {
Profile::Balanced => { Profile::Balanced => {
Profile::set_profile(Profile::Performance)?; Profile::set_profile(Profile::Performance)?;
self.config.active_profile = Profile::Performance; self.profile_config.active_profile = Profile::Performance;
} }
Profile::Performance => { Profile::Performance => {
Profile::set_profile(Profile::Quiet)?; Profile::set_profile(Profile::Quiet)?;
self.config.active_profile = Profile::Quiet; self.profile_config.active_profile = Profile::Quiet;
} }
Profile::Quiet => { Profile::Quiet => {
Profile::set_profile(Profile::Balanced)?; Profile::set_profile(Profile::Balanced)?;
self.config.active_profile = Profile::Balanced; self.profile_config.active_profile = Profile::Balanced;
} }
} }
self.write_profile_curve_to_platform()?; self.write_profile_curve_to_platform()?;
@@ -97,18 +165,25 @@ impl CtrlPlatformProfile {
/// Set the curve for the active profile active /// Set the curve for the active profile active
pub(super) fn write_profile_curve_to_platform(&mut self) -> Result<(), RogError> { pub(super) fn write_profile_curve_to_platform(&mut self) -> Result<(), RogError> {
if let Some(curves) = &mut self.config.fan_curves { if let Some(curves) = &mut self.fan_curves {
if let Ok(mut device) = FanCurveProfiles::get_device() { if let Ok(mut device) = FanCurveProfiles::get_device() {
curves.write_profile_curve_to_platform(self.config.active_profile, &mut device)?; curves.profiles_mut().write_profile_curve_to_platform(
self.profile_config.active_profile,
&mut device,
)?;
} }
} }
Ok(()) Ok(())
} }
pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> { pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> {
if let Some(curves) = self.config.fan_curves.as_mut() { if let Some(curves) = self.fan_curves.as_mut() {
if let Ok(mut device) = FanCurveProfiles::get_device() { if let Ok(mut device) = FanCurveProfiles::get_device() {
curves.set_active_curve_to_defaults(self.config.active_profile, &mut device)?; curves.profiles_mut().set_active_curve_to_defaults(
self.profile_config.active_profile,
&mut device,
)?;
curves.update_config_from_profiles();
} }
} }
Ok(()) Ok(())

View File

@@ -1,4 +1,4 @@
pub mod config; pub mod config;
pub mod controller; pub mod controller;
/// Implements CtrlTask, Reloadable, ZbusRun /// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls; pub mod trait_impls;

View File

@@ -1,21 +1,21 @@
use std::str::FromStr;
use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use log::warn; use config_traits::StdConfig;
use rog_profiles::fan_curve_set::CurveData; use log::{error, info, warn};
use rog_profiles::fan_curve_set::FanCurveSet; use rog_profiles::fan_curve_set::{CurveData, FanCurveSet};
use rog_profiles::FanCurveProfiles; use rog_profiles::{FanCurveProfiles, Profile};
use rog_profiles::Profile;
use zbus::export::futures_util::lock::Mutex; use zbus::export::futures_util::lock::Mutex;
use zbus::export::futures_util::StreamExt; use zbus::export::futures_util::StreamExt;
use zbus::Connection; use zbus::fdo::Error;
use zbus::SignalContext; use zbus::{dbus_interface, Connection, SignalContext};
use std::sync::Arc;
use zbus::{dbus_interface, fdo::Error};
use super::controller::CtrlPlatformProfile;
use crate::error::RogError; use crate::error::RogError;
use crate::CtrlTask; use crate::CtrlTask;
use super::controller::CtrlPlatformProfile; const MOD_NAME: &str = "ProfileZbus";
const ZBUS_PATH: &str = "/org/asuslinux/Profile"; const ZBUS_PATH: &str = "/org/asuslinux/Profile";
const UNSUPPORTED_MSG: &str = const UNSUPPORTED_MSG: &str =
@@ -32,7 +32,7 @@ impl ProfileZbus {
return Ok(profiles); return Ok(profiles);
} }
Err(Error::Failed( Err(Error::Failed(
"Failed to get all profile details".to_string(), "Failed to get all profile details".to_owned(),
)) ))
} }
@@ -41,10 +41,10 @@ impl ProfileZbus {
async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) { async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.set_next_profile() ctrl.set_next_profile()
.unwrap_or_else(|err| warn!("{}", err)); .unwrap_or_else(|err| warn!("{MOD_NAME}: {}", err));
ctrl.save_config(); ctrl.save_config();
Self::notify_profile(&ctxt, ctrl.config.active_profile) Self::notify_profile(&ctxt, ctrl.profile_config.active_profile)
.await .await
.ok(); .ok();
} }
@@ -52,8 +52,8 @@ impl ProfileZbus {
/// Fetch the active profile name /// Fetch the active profile name
async fn active_profile(&mut self) -> zbus::fdo::Result<Profile> { async fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.config.read(); ctrl.profile_config.read();
Ok(ctrl.config.active_profile) Ok(ctrl.profile_config.active_profile)
} }
/// Set this platform_profile name as active /// Set this platform_profile name as active
@@ -64,18 +64,18 @@ impl ProfileZbus {
) { ) {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
// Read first just incase the user has modified the config before calling this // Read first just incase the user has modified the config before calling this
ctrl.config.read(); ctrl.profile_config.read();
Profile::set_profile(profile) Profile::set_profile(profile)
.map_err(|e| warn!("set_profile, {}", e)) .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok(); .ok();
ctrl.config.active_profile = profile; ctrl.profile_config.active_profile = profile;
ctrl.write_profile_curve_to_platform() ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) .map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
.ok(); .ok();
ctrl.save_config(); ctrl.save_config();
Self::notify_profile(&ctxt, ctrl.config.active_profile) Self::notify_profile(&ctxt, ctrl.profile_config.active_profile)
.await .await
.ok(); .ok();
} }
@@ -83,99 +83,104 @@ impl ProfileZbus {
/// Get a list of profiles that have fan-curves enabled. /// Get a list of profiles that have fan-curves enabled.
async fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> { async fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.config.read(); ctrl.profile_config.read();
if let Some(curves) = &ctrl.config.fan_curves { if let Some(curves) = &mut ctrl.fan_curves {
return Ok(curves.get_enabled_curve_profiles().to_vec()); return Ok(curves.profiles().get_enabled_curve_profiles());
} }
Err(Error::Failed(UNSUPPORTED_MSG.to_string())) Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
} }
/// Set a profile fan curve enabled status. Will also activate a fan curve if in the /// Set a profile fan curve enabled status. Will also activate a fan curve
/// same profile mode /// if in the same profile mode
async fn set_fan_curve_enabled( async fn set_fan_curve_enabled(
&mut self, &mut self,
profile: Profile, profile: Profile,
enabled: bool, enabled: bool,
) -> zbus::fdo::Result<()> { ) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.config.read(); ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.config.fan_curves { if let Some(curves) = &mut ctrl.fan_curves {
curves.set_profile_curve_enabled(profile, enabled); curves
.profiles_mut()
.set_profile_curve_enabled(profile, enabled);
ctrl.write_profile_curve_to_platform() ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) .map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
.ok(); .ok();
ctrl.save_config(); ctrl.save_config();
Ok(()) Ok(())
} else { } else {
Err(Error::Failed(UNSUPPORTED_MSG.to_string())) Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
} }
} }
/// Get the fan-curve data for the currently active Profile /// Get the fan-curve data for the currently active Profile
async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result<FanCurveSet> { async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result<FanCurveSet> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.config.read(); ctrl.profile_config.read();
if let Some(curves) = &ctrl.config.fan_curves { if let Some(curves) = &mut ctrl.fan_curves {
let curve = curves.get_fan_curves_for(profile); let curve = curves.profiles().get_fan_curves_for(profile);
return Ok(curve.clone()); return Ok(curve.clone());
} }
Err(Error::Failed(UNSUPPORTED_MSG.to_string())) Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
} }
/// Set the fan curve for the specified profile. /// Set the fan curve for the specified profile.
/// Will also activate the fan curve if the user is in the same mode. /// Will also activate the fan curve if the user is in the same mode.
async fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> { async fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.config.read(); ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.config.fan_curves { if let Some(curves) = &mut ctrl.fan_curves {
curves curves
.profiles_mut()
.save_fan_curve(curve, profile) .save_fan_curve(curve, profile)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} else { } else {
return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); return Err(Error::Failed(UNSUPPORTED_MSG.to_owned()));
} }
ctrl.write_profile_curve_to_platform() ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("Profile::set_profile, {}", e)) .map_err(|e| warn!("{MOD_NAME}: Profile::set_profile, {}", e))
.ok(); .ok();
ctrl.save_config(); ctrl.save_config();
Ok(()) Ok(())
} }
/// Reset the stored (self) and device curve to the defaults of the platform. /// 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 /// Each platform_profile has a different default and the defualt can be
/// only for the currently active profile. /// read only for the currently active profile.
async fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> { async fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.config.read(); ctrl.profile_config.read();
ctrl.set_active_curve_to_defaults() ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) .map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e))
.ok(); .ok();
ctrl.save_config(); ctrl.save_config();
Ok(()) Ok(())
} }
/// Reset the stored (self) and device curve to the defaults of the platform. /// 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 /// Each platform_profile has a different default and the defualt can be
/// only for the currently active profile. /// read only for the currently active profile.
async fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> { async fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
ctrl.config.read(); ctrl.profile_config.read();
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
Profile::set_profile(profile) Profile::set_profile(profile)
.map_err(|e| warn!("set_profile, {}", e)) .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok(); .ok();
ctrl.set_active_curve_to_defaults() ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) .map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e))
.ok(); .ok();
Profile::set_profile(active) Profile::set_profile(active)
.map_err(|e| warn!("set_profile, {}", e)) .map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok(); .ok();
ctrl.save_config(); ctrl.save_config();
Ok(()) Ok(())
@@ -200,27 +205,84 @@ impl CtrlTask for ProfileZbus {
} }
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let ctrl = self.0.clone();
let sig_ctx = signal_ctxt.clone();
let mut watch = self
.0
.lock()
.await
.platform
.monitor_throttle_thermal_policy()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(stream) = watch.event_stream(&mut buffer) {
stream
.for_each(|_| async {
let mut lock = ctrl.lock().await;
if let Ok(profile) =
lock.platform.get_throttle_thermal_policy().map_err(|e| {
error!("{MOD_NAME}: get_throttle_thermal_policy error: {e}");
})
{
let new_profile = Profile::from_throttle_thermal_policy(profile);
if new_profile != lock.profile_config.active_profile {
info!("{MOD_NAME}: platform_profile changed to {new_profile}");
lock.profile_config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
Profile::set_profile(lock.profile_config.active_profile)
.map_err(|e| {
error!("Profile::set_profile() error: {e}");
})
.ok();
}
Self::notify_profile(&sig_ctx, lock.profile_config.active_profile)
.await
.ok();
}
})
.await;
}
});
let ctrl = self.0.clone(); let ctrl = self.0.clone();
let mut watch = self.0.lock().await.platform.monitor_platform_profile()?; let mut watch = self.0.lock().await.platform.monitor_platform_profile()?;
tokio::spawn(async move { tokio::spawn(async move {
let mut buffer = [0; 32]; let mut buffer = [0; 32];
watch if let Ok(stream) = watch.event_stream(&mut buffer) {
.event_stream(&mut buffer) stream
.unwrap() .for_each(|_| async {
.for_each(|_| async { let mut lock = ctrl.lock().await;
let mut lock = ctrl.lock().await; if let Ok(profile) = lock.platform.get_platform_profile().map_err(|e| {
let new_profile = Profile::get_active_profile().unwrap(); error!("get_platform_profile error: {e}");
if new_profile != lock.config.active_profile { }) {
lock.config.active_profile = new_profile; if let Ok(new_profile) = Profile::from_str(&profile).map_err(|e| {
lock.write_profile_curve_to_platform().unwrap(); error!("Profile::from_str(&profile) error: {e}");
lock.save_config(); }) {
} if new_profile != lock.profile_config.active_profile {
Self::notify_profile(&signal_ctxt.clone(), lock.config.active_profile) info!("{MOD_NAME}: platform_profile changed to {new_profile}");
.await lock.profile_config.active_profile = new_profile;
.ok(); lock.write_profile_curve_to_platform().unwrap();
}) lock.save_config();
.await; Profile::set_profile(lock.profile_config.active_profile)
.map_err(|e| {
error!("Profile::set_profile() error: {e}");
})
.ok();
}
Self::notify_profile(
&signal_ctxt,
lock.profile_config.active_profile,
)
.await
.ok();
}
}
})
.await;
}
}); });
Ok(()) Ok(())
@@ -232,13 +294,16 @@ impl crate::Reloadable for ProfileZbus {
/// Fetch the active profile and use that to set all related components up /// Fetch the active profile and use that to set all related components up
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await; let mut ctrl = self.0.lock().await;
let active = ctrl.config.active_profile; let active = ctrl.profile_config.active_profile;
if let Some(curves) = &mut ctrl.config.fan_curves { if let Some(curves) = &mut ctrl.fan_curves {
if let Ok(mut device) = FanCurveProfiles::get_device() { if let Ok(mut device) = FanCurveProfiles::get_device() {
// There is a possibility that the curve was default zeroed, so this call initialises // There is a possibility that the curve was default zeroed, so this call
// the data from system read and we need to save it after // initialises the data from system read and we need to save it
curves.write_profile_curve_to_platform(active, &mut device)?; // after
ctrl.config.write(); curves
.profiles_mut()
.write_profile_curve_to_platform(active, &mut device)?;
ctrl.profile_config.write();
} }
} }
Ok(()) Ok(())

View File

@@ -1,27 +1,22 @@
use async_trait::async_trait; use async_trait::async_trait;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use zbus::{dbus_interface, zvariant::Type, Connection}; use zbus::zvariant::Type;
use zbus::{dbus_interface, Connection};
use crate::{ use crate::ctrl_anime::CtrlAnime;
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_platform::CtrlPlatform, use crate::ctrl_aura::controller::CtrlKbdLed;
ctrl_power::CtrlPower, ctrl_profiles::controller::CtrlPlatformProfile, GetSupported, use crate::ctrl_platform::CtrlPlatform;
}; use crate::ctrl_power::CtrlPower;
use crate::ctrl_profiles::controller::CtrlPlatformProfile;
use crate::GetSupported;
use rog_platform::supported::*; #[derive(Serialize, Deserialize, Debug, Type)]
pub struct SupportedFunctions(rog_platform::supported::SupportedFunctions);
#[derive(Serialize, Deserialize, Type)]
pub struct SupportedFunctions {
pub anime_ctrl: AnimeSupportedFunctions,
pub charge_ctrl: ChargeSupportedFunctions,
pub platform_profile: PlatformProfileFunctions,
pub keyboard_led: LedSupportedFunctions,
pub rog_bios_ctrl: RogBiosSupportedFunctions,
}
#[dbus_interface(name = "org.asuslinux.Daemon")] #[dbus_interface(name = "org.asuslinux.Daemon")]
impl SupportedFunctions { impl SupportedFunctions {
fn supported_functions(&self) -> &SupportedFunctions { pub fn supported_functions(&self) -> &rog_platform::supported::SupportedFunctions {
self &self.0
} }
} }
@@ -36,12 +31,12 @@ impl GetSupported for SupportedFunctions {
type A = SupportedFunctions; type A = SupportedFunctions;
fn get_supported() -> Self::A { fn get_supported() -> Self::A {
SupportedFunctions { Self(rog_platform::supported::SupportedFunctions {
anime_ctrl: CtrlAnime::get_supported(), anime_ctrl: CtrlAnime::get_supported(),
keyboard_led: CtrlKbdLed::get_supported(), keyboard_led: CtrlKbdLed::get_supported(),
charge_ctrl: CtrlPower::get_supported(), charge_ctrl: CtrlPower::get_supported(),
platform_profile: CtrlPlatformProfile::get_supported(), platform_profile: CtrlPlatformProfile::get_supported(),
rog_bios_ctrl: CtrlPlatform::get_supported(), rog_bios_ctrl: CtrlPlatform::get_supported(),
} })
} }
} }

View File

@@ -6,36 +6,34 @@ use std::time::Duration;
use ::zbus::export::futures_util::lock::Mutex; use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection; use ::zbus::Connection;
use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2};
use daemon::config::Config;
use daemon::ctrl_anime::config::AnimeConfig;
use daemon::ctrl_anime::trait_impls::CtrlAnimeZbus;
use daemon::ctrl_anime::CtrlAnime; use daemon::ctrl_anime::CtrlAnime;
use log::LevelFilter; use daemon::ctrl_aura::controller::CtrlKbdLed;
use log::{error, info, warn}; use daemon::ctrl_aura::trait_impls::CtrlKbdLedZbus;
use tokio::time::sleep;
use zbus::SignalContext;
use daemon::ctrl_anime::{config::AnimeConfig, trait_impls::CtrlAnimeZbus};
use daemon::ctrl_aura::{config::AuraConfig, controller::CtrlKbdLed, trait_impls::CtrlKbdLedZbus};
use daemon::ctrl_platform::CtrlPlatform; use daemon::ctrl_platform::CtrlPlatform;
use daemon::ctrl_power::CtrlPower; use daemon::ctrl_power::CtrlPower;
use daemon::ctrl_profiles::{ use daemon::ctrl_profiles::config::ProfileConfig;
config::ProfileConfig, controller::CtrlPlatformProfile, trait_impls::ProfileZbus, use daemon::ctrl_profiles::controller::CtrlPlatformProfile;
}; use daemon::ctrl_profiles::trait_impls::ProfileZbus;
use daemon::laptops::LaptopLedData; use daemon::ctrl_supported::SupportedFunctions;
use daemon::{ use daemon::{print_board_info, CtrlTask, GetSupported, Reloadable, ZbusRun};
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, use log::{error, info, warn};
}; use rog_aura::aura_detection::LaptopLedData;
use daemon::{CtrlTask, Reloadable, ZbusRun};
use rog_dbus::DBUS_NAME; use rog_dbus::DBUS_NAME;
use rog_profiles::Profile; use rog_profiles::Profile;
use tokio::time::sleep;
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf"; use zbus::SignalContext;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new(); let mut logger = env_logger::Builder::new();
logger logger
.parse_default_env()
.target(env_logger::Target::Stdout) .target(env_logger::Target::Stdout)
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())) .format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
.filter(None, LevelFilter::Info)
.init(); .init();
let is_service = match env::var_os("IS_SERVICE") { let is_service = match env::var_os("IS_SERVICE") {
@@ -67,12 +65,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn start_daemon() -> Result<(), Box<dyn Error>> { async fn start_daemon() -> Result<(), Box<dyn Error>> {
let supported = SupportedFunctions::get_supported(); let supported = SupportedFunctions::get_supported();
print_board_info(); print_board_info();
println!("{}", serde_json::to_string_pretty(&supported)?); println!("{}", supported.supported_functions());
// Start zbus server // Start zbus server
let mut connection = Connection::system().await?; let mut connection = Connection::system().await?;
let config = Config::load(); let config = Config::new().load();
let config = Arc::new(Mutex::new(config)); let config = Arc::new(Mutex::new(config));
supported.add_to_server(&mut connection).await; supported.add_to_server(&mut connection).await;
@@ -98,7 +96,7 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
} }
if Profile::is_platform_profile_supported() { if Profile::is_platform_profile_supported() {
let profile_config = ProfileConfig::load(PROFILE_CONFIG_PATH.into()); let profile_config = ProfileConfig::new().load();
match CtrlPlatformProfile::new(profile_config) { match CtrlPlatformProfile::new(profile_config) {
Ok(ctrl) => { Ok(ctrl) => {
let zbus = ProfileZbus(Arc::new(Mutex::new(ctrl))); let zbus = ProfileZbus(Arc::new(Mutex::new(ctrl)));
@@ -113,7 +111,7 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
warn!("platform_profile support not found"); warn!("platform_profile support not found");
} }
match CtrlAnime::new(AnimeConfig::load()) { match CtrlAnime::new(AnimeConfig::new().load()) {
Ok(ctrl) => { Ok(ctrl) => {
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl))); let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?; let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?;
@@ -125,8 +123,9 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
} }
let laptop = LaptopLedData::get_data(); let laptop = LaptopLedData::get_data();
let aura_config = AuraConfig::load(&laptop); // CtrlKbdLed deviates from the config pattern above due to requiring a keyboard
match CtrlKbdLed::new(laptop, aura_config) { // detection first
match CtrlKbdLed::new(laptop) {
Ok(ctrl) => { Ok(ctrl) => {
let zbus = CtrlKbdLedZbus(Arc::new(Mutex::new(ctrl))); let zbus = CtrlKbdLedZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlKbdLedZbus::signal_context(&connection)?; let sig_ctx = CtrlKbdLedZbus::signal_context(&connection)?;

View File

@@ -1,8 +1,10 @@
use std::convert::From;
use std::fmt;
use config_traits::ron;
use rog_anime::error::AnimeError; use rog_anime::error::AnimeError;
use rog_platform::error::PlatformError; use rog_platform::error::PlatformError;
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use std::convert::From;
use std::fmt;
#[derive(Debug)] #[derive(Debug)]
pub enum RogError { pub enum RogError {
@@ -33,11 +35,12 @@ pub enum RogError {
SystemdUnitAction(String), SystemdUnitAction(String),
SystemdUnitWaitTimeout(String), SystemdUnitWaitTimeout(String),
Command(String, std::io::Error), Command(String, std::io::Error),
ParseRon(ron::Error),
} }
impl fmt::Display for RogError { impl fmt::Display for RogError {
// This trait requires `fmt` with this exact signature. // This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
RogError::ParseVendor => write!(f, "Parse gfx vendor error"), RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
RogError::ParseLed => write!(f, "Parse LED error"), RogError::ParseLed => write!(f, "Parse LED error"),
@@ -50,14 +53,21 @@ impl fmt::Display for RogError {
RogError::NotFound(deets) => write!(f, "Not found: {}", deets), RogError::NotFound(deets) => write!(f, "Not found: {}", deets),
RogError::DoTask(deets) => write!(f, "Task error: {}", deets), RogError::DoTask(deets) => write!(f, "Task error: {}", deets),
RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets), RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error), RogError::MissingLedBrightNode(path, error) => write!(
RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets), f,
"Led node at {} is missing, please check you have the required patch or dkms \
module installed: {}",
path, error
),
RogError::ReloadFail(deets) => write!(f, "Reload error: {}", deets),
RogError::Profiles(deets) => write!(f, "Profile error: {}", deets), RogError::Profiles(deets) => write!(f, "Profile error: {}", deets),
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail), RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail), RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
RogError::Io(detail) => write!(f, "std::io error: {}", detail), RogError::Io(detail) => write!(f, "std::io error: {}", detail),
RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail), RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail),
RogError::ChargeLimit(value) => write!(f, "Invalid charging limit, not in range 20-100%: {}", value), RogError::ChargeLimit(value) => {
write!(f, "Invalid charging limit, not in range 20-100%: {}", value)
}
RogError::AuraEffectNotSupported => write!(f, "Aura effect not supported"), RogError::AuraEffectNotSupported => write!(f, "Aura effect not supported"),
RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"), RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
RogError::NoAuraNode => write!(f, "No Aura keyboard node found"), RogError::NoAuraNode => write!(f, "No Aura keyboard node found"),
@@ -74,6 +84,7 @@ impl fmt::Display for RogError {
) )
} }
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error), RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
RogError::ParseRon(error) => write!(f, "Parse config error: {}", error),
} }
} }
} }
@@ -110,6 +121,12 @@ impl From<std::io::Error> for RogError {
} }
} }
impl From<ron::Error> for RogError {
fn from(err: ron::Error) -> Self {
RogError::ParseRon(err)
}
}
impl From<RogError> for zbus::fdo::Error { impl From<RogError> for zbus::fdo::Error {
#[inline] #[inline]
fn from(err: RogError) -> Self { fn from(err: RogError) -> Self {

View File

@@ -1,167 +0,0 @@
use log::{info, warn};
use rog_aura::{AuraModeNum, AuraZone};
use serde_derive::{Deserialize, Serialize};
use std::fs::OpenOptions;
use std::io::Read;
pub const ASUS_LED_MODE_CONF: &str = "/etc/asusd/asusd-ledmodes.toml";
pub const ASUS_LED_MODE_USER_CONF: &str = "/etc/asusd/asusd-user-ledmodes.toml";
pub const ASUS_KEYBOARD_DEVICES: [&str; 4] = ["1866", "1869", "1854", "19b6"];
pub fn print_board_info() {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
let prod_family = dmi.product_family().expect("Could not get product_family");
info!("Product family: {}", prod_family.trim());
info!("Board name: {}", board_name.trim());
}
pub fn print_modes(supported_modes: &[u8]) {
if !supported_modes.is_empty() {
info!("Supported Keyboard LED modes are:");
for mode in supported_modes {
let mode = <&str>::from(&<AuraModeNum>::from(*mode));
info!("- {}", mode);
}
info!(
"If these modes are incorrect you can edit {}",
ASUS_LED_MODE_CONF
);
} else {
info!("No RGB control available");
}
}
#[derive(Debug, Default, Deserialize, Serialize)]
struct LedSupportFile {
led_data: Vec<LaptopLedData>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct LaptopLedData {
pub prod_family: String,
pub board_names: Vec<String>,
pub standard: Vec<AuraModeNum>,
pub multizone: Vec<AuraZone>,
pub per_key: bool,
}
impl LaptopLedData {
pub fn get_data() -> Self {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
let prod_family = dmi.product_family().expect("Could not get product_family");
if let Some(modes) = LedSupportFile::load_from_config() {
if let Some(data) = modes.matcher(&prod_family, &board_name) {
return data;
}
}
info!("Using generic LED control for keyboard brightness only");
LaptopLedData {
prod_family,
board_names: vec![board_name],
standard: vec![],
multizone: vec![],
per_key: false,
}
}
}
impl LedSupportFile {
/// Consumes the LEDModes
fn matcher(self, prod_family: &str, board_name: &str) -> Option<LaptopLedData> {
for config in self.led_data {
if prod_family.contains(&config.prod_family) {
for board in &config.board_names {
if board_name.contains(board) {
info!("Matched to {} {}", config.prod_family, board);
return Some(config);
}
}
}
}
None
}
fn load_from_config() -> Option<Self> {
let mut loaded = false;
let mut data = LedSupportFile::default();
// Load user configs first so they are first to be checked
if let Ok(mut file) = OpenOptions::new().read(true).open(ASUS_LED_MODE_USER_CONF) {
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("{} is empty", ASUS_LED_MODE_USER_CONF);
} else {
if let Ok(mut tmp) = toml::from_str::<LedSupportFile>(&buf) {
data.led_data.append(&mut tmp.led_data);
}
info!(
"Loaded user-defined LED support data from {}",
ASUS_LED_MODE_USER_CONF
);
}
}
}
// Load and append the default LED support data
if let Ok(mut file) = OpenOptions::new().read(true).open(ASUS_LED_MODE_CONF) {
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("{} is empty", ASUS_LED_MODE_CONF);
} else {
let mut tmp: LedSupportFile = toml::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", ASUS_LED_MODE_CONF));
data.led_data.append(&mut tmp.led_data);
loaded = true;
info!(
"Loaded default LED support data from {}",
ASUS_LED_MODE_CONF
);
}
}
}
if loaded {
return Some(data);
}
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF);
None
}
}
#[cfg(test)]
mod tests {
use std::{fs::OpenOptions, io::Read, path::PathBuf};
use super::LaptopLedData;
use rog_aura::{AuraModeNum, AuraZone};
#[test]
fn check_data_parse() {
let led = LaptopLedData {
prod_family: "Test".to_owned(),
board_names: vec!["Test".to_owned()],
standard: vec![AuraModeNum::Static],
multizone: vec![AuraZone::Key1, AuraZone::Logo, AuraZone::BarLeft],
per_key: false,
};
let toml = toml::to_string_pretty(&led).unwrap();
println!("{toml}");
let mut data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
data.push("../data/asusd-ledmodes.toml");
let mut file = OpenOptions::new().read(true).open(&data).unwrap();
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
let x = toml::to_string_pretty(&buf).unwrap();
println!("{x}");
}
}

View File

@@ -1,7 +1,7 @@
#![deny(unused_must_use)] #![deny(unused_must_use)]
/// Configuration loading, saving /// Configuration loading, saving
pub mod config; pub mod config;
/// Control of AniMe matrix display /// Control of anime matrix display
pub mod ctrl_anime; pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes /// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura; pub mod ctrl_aura;
@@ -11,31 +11,37 @@ pub mod ctrl_platform;
pub mod ctrl_power; pub mod ctrl_power;
/// Control platform profiles + fan-curves if available /// Control platform profiles + fan-curves if available
pub mod ctrl_profiles; pub mod ctrl_profiles;
/// Laptop matching to determine capabilities
pub mod laptops;
pub mod systemd;
/// Fetch all supported functions for the laptop /// Fetch all supported functions for the laptop
pub mod ctrl_supported; pub mod ctrl_supported;
pub mod error; pub mod error;
use crate::error::RogError; use std::future::Future;
use async_trait::async_trait;
use log::warn;
use logind_zbus::manager::ManagerProxy;
use zbus::{export::futures_util::StreamExt, zvariant::ObjectPath, Connection, SignalContext};
/// This macro adds a function which spawns an `inotify` task on the passed in `Executor`. use async_trait::async_trait;
use log::{debug, info, warn};
use logind_zbus::manager::ManagerProxy;
use zbus::export::futures_util::StreamExt;
use zbus::zvariant::ObjectPath;
use zbus::{Connection, SignalContext};
use crate::error::RogError;
const CONFIG_PATH_BASE: &str = "/etc/asusd/";
/// This macro adds a function which spawns an `inotify` task on the passed in
/// `Executor`.
/// ///
/// The generated function is `watch_<name>()`. Self requires the following methods to be available: /// The generated function is `watch_<name>()`. Self requires the following
/// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have side effects. /// methods to be available:
/// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have
/// side effects.
/// - `notify_<name>(SignalContext, SomeValue)` /// - `notify_<name>(SignalContext, SomeValue)`
/// ///
/// In most cases if `SomeValue` is stored in a config then `<name>()` getter is expected to update it. /// In most cases if `SomeValue` is stored in a config then `<name>()` getter is
/// The getter should *never* write back to the path or attribute that is being watched or an /// expected to update it. The getter should *never* write back to the path or
/// infinite loop will occur. /// attribute that is being watched or an infinite loop will occur.
/// ///
/// # Example /// # Example
/// ///
@@ -80,6 +86,15 @@ macro_rules! task_watch_item {
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn print_board_info() {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
let prod_family = dmi.product_family().expect("Could not get product_family");
info!("Product family: {}", prod_family.trim());
info!("Board name: {}", board_name.trim());
}
#[async_trait] #[async_trait]
pub trait Reloadable { pub trait Reloadable {
async fn reload(&mut self) -> Result<(), RogError>; async fn reload(&mut self) -> Result<(), RogError>;
@@ -115,13 +130,14 @@ pub trait CtrlTask {
SignalContext::new(connection, Self::zbus_path()) SignalContext::new(connection, Self::zbus_path())
} }
/// Implement to set up various tasks that may be required, using the `Executor`. /// Implement to set up various tasks that may be required, using the
/// No blocking loops are allowed, or they must be run on a separate thread. /// `Executor`. No blocking loops are allowed, or they must be run on a
/// separate thread.
async fn create_tasks(&self, signal: SignalContext<'static>) -> Result<(), RogError>; async fn create_tasks(&self, signal: SignalContext<'static>) -> Result<(), RogError>;
// /// Create a timed repeating task // /// Create a timed repeating task
// async fn repeating_task(&self, millis: u64, mut task: impl FnMut() + Send + 'static) { // async fn repeating_task(&self, millis: u64, mut task: impl FnMut() + Send +
// use std::time::Duration; // 'static) { use std::time::Duration;
// use tokio::time; // use tokio::time;
// let mut timer = time::interval(Duration::from_millis(millis)); // let mut timer = time::interval(Duration::from_millis(millis));
// tokio::spawn(async move { // tokio::spawn(async move {
@@ -130,17 +146,36 @@ pub trait CtrlTask {
// }); // });
// } // }
/// Free helper method to create tasks to run on: sleep, wake, shutdown, boot /// Free helper method to create tasks to run on: sleep, wake, shutdown,
/// boot
/// ///
/// The closures can potentially block, so execution time should be the minimal possible /// The closures can potentially block, so execution time should be the
/// such as save a variable. /// minimal possible such as save a variable.
async fn create_sys_event_tasks( async fn create_sys_event_tasks<
Fut1,
Fut2,
Fut3,
Fut4,
F1: Send + 'static,
F2: Send + 'static,
F3: Send + 'static,
F4: Send + 'static,
>(
&self, &self,
mut on_sleep: impl FnMut() + Send + 'static, mut on_sleep: F1,
mut on_wake: impl FnMut() + Send + 'static, mut on_wake: F2,
mut on_shutdown: impl FnMut() + Send + 'static, mut on_shutdown: F3,
mut on_boot: impl FnMut() + Send + 'static, mut on_boot: F4,
) { ) where
F1: FnMut() -> Fut1,
F2: FnMut() -> Fut2,
F3: FnMut() -> Fut3,
F4: FnMut() -> Fut4,
Fut1: Future<Output = ()> + Send,
Fut2: Future<Output = ()> + Send,
Fut3: Future<Output = ()> + Send,
Fut4: Future<Output = ()> + Send,
{
let connection = Connection::system() let connection = Connection::system()
.await .await
.expect("Controller could not create dbus connection"); .expect("Controller could not create dbus connection");
@@ -154,9 +189,11 @@ pub trait CtrlTask {
while let Some(event) = notif.next().await { while let Some(event) = notif.next().await {
if let Ok(args) = event.args() { if let Ok(args) = event.args() {
if args.start { if args.start {
on_sleep(); debug!("Doing on_sleep()");
on_sleep().await;
} else if !args.start() { } else if !args.start() {
on_wake(); debug!("Doing on_wake()");
on_wake().await;
} }
} }
} }
@@ -172,9 +209,11 @@ pub trait CtrlTask {
while let Some(event) = notif.next().await { while let Some(event) = notif.next().await {
if let Ok(args) = event.args() { if let Ok(args) = event.args() {
if args.start { if args.start {
on_shutdown(); debug!("Doing on_shutdown()");
on_shutdown().await;
} else if !args.start() { } else if !args.start() {
on_boot(); debug!("Doing on_boot()");
on_boot().await;
} }
} }
} }

View File

@@ -1,90 +0,0 @@
use std::process::Command;
use crate::error::RogError;
/// An action for `systemctl`
#[derive(Debug, Copy, Clone)]
pub enum SystemdUnitAction {
Stop,
Start,
Restart,
}
impl From<SystemdUnitAction> for &str {
fn from(s: SystemdUnitAction) -> Self {
match s {
SystemdUnitAction::Stop => "stop",
SystemdUnitAction::Start => "start",
SystemdUnitAction::Restart => "restart",
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum SystemdUnitState {
Active,
Inactive,
}
impl From<SystemdUnitState> for &str {
fn from(s: SystemdUnitState) -> Self {
match s {
SystemdUnitState::Active => "active",
SystemdUnitState::Inactive => "inactive",
}
}
}
/// Change the state of a systemd unit. Blocks while running command.
pub fn do_systemd_unit_action(action: SystemdUnitAction, unit: &str) -> Result<(), RogError> {
let mut cmd = Command::new("systemctl");
cmd.arg(<&str>::from(action));
cmd.arg(unit);
let status = cmd
.status()
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
if !status.success() {
let msg = format!("systemctl {action:?} {unit} failed: {status:?}",);
return Err(RogError::SystemdUnitAction(msg));
}
Ok(())
}
/// Get systemd unit state. Blocks while command is run.
pub fn is_systemd_unit_state(state: SystemdUnitState, unit: &str) -> Result<bool, RogError> {
let mut cmd = Command::new("systemctl");
cmd.arg("is-active");
cmd.arg(unit);
let output = cmd
.output()
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
if output.stdout.starts_with(<&str>::from(state).as_bytes()) {
return Ok(true);
}
Ok(false)
}
/// Wait for a systemd unit to change to `state`. Checks state every 250ms for 3 seconds. Blocks while running wait.
pub fn wait_systemd_unit_state(state: SystemdUnitState, unit: &str) -> Result<(), RogError> {
let mut cmd = Command::new("systemctl");
cmd.arg("is-active");
cmd.arg(unit);
let mut count = 0;
while count <= (4 * 3) {
// 3 seconds max
let output = cmd
.output()
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
if output.stdout.starts_with(<&str>::from(state).as_bytes()) {
return Ok(());
}
// fine to block here, nobody doing shit now
std::thread::sleep(std::time::Duration::from_millis(250));
count += 1;
}
Err(RogError::SystemdUnitWaitTimeout(<&str>::from(state).into()))
}

View File

@@ -1,197 +0,0 @@
[[led_data]]
prod_family = "TUF"
board_names = ["FA507"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "TUF Gaming"
board_names = ["FX505D"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ASUS TUF Gaming A15"
board_names = ["FA506I"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "Zephyrus S"
board_names = ["GX502", "GX701", "G531", "GL531", "G532"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
[[led_data]]
prod_family = "Zephyrus M"
board_names = ["GU502G"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
[[led_data]]
prod_family = "ROG Zephyrus M15"
board_names = ["GU502LU"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus M15"
board_names = ["GU502L"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
[[led_data]]
prod_family = "ROG Zephyrus M16"
board_names = ["GU603Z", "GU603H"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus S17"
board_names = ["GX703H"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = false
[[led_data]]
prod_family = "Zephyrus"
board_names = ["GM501G", "GX531"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G531GW", "G533QR", "G533QS", "G733Q", "G513QR", "G713QR", "G513QM", "G713IC", "G713RS"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512", "G713RW"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G512LI", "G712LI", "G531GD"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513IM"]
standard = ["Flash", "Static", "Breathe", "Strobe", "Rainbow"]
multizone = []
per_key = true
[[led_data]]
prod_family = "Strix"
board_names = ["G731GV", "G731GW", "G531GV"]
standard = ["Static", "Breathe", "Strobe", "Rainbow"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
[[led_data]]
prod_family = "Strix"
board_names = ["GL504G"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4", "Logo", "BarLeft", "BarRight"]
per_key = false
[[led_data]]
prod_family = "Strix"
board_names = ["G731GT", "G731GU", "G531GT", "G531GU"]
standard = ["Static", "Breathe", "Strobe", "Rainbow"]
multizone = []
per_key = false
[[led_data]]
prod_family = "Strix Scar"
board_names = ["G531", "G731"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = true
[[led_data]]
prod_family = "ROG"
board_names = ["GL553VE"]
standard = ["Static", "Breathe", "Strobe"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus G14"
board_names = ["GA401Q"]
standard = ["Static", "Breathe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus G14"
board_names = ["GA402R"]
standard = ["Static", "Breathe", "Pulse", "Rainbow"]
multizone = []
per_key = false
# GA503QE at higher priority (first match) than GA503Q
[[led_data]]
prod_family = "ROG Zephyrus G15"
board_names = ["GA503QE"]
standard = ["Static", "Breathe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus G15"
board_names = ["GA503Q", "GA503R"]
standard = ["Static", "Breathe", "Pulse", "Rainbow", "Strobe"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus"
board_names = ["GX550L"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
[[led_data]]
prod_family = "ROG Zephyrus Duo 15 SE"
board_names = ["GX551Q"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = true
[[led_data]]
prod_family = "ROG Flow X13"
board_names = ["GV301Q"]
standard = ["Static", "Breathe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513IC", "G513RC"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Flow X16"
board_names = ["GV601R"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false

View File

@@ -3,13 +3,15 @@ Description=ASUS Notebook Control
StartLimitInterval=200 StartLimitInterval=200
StartLimitBurst=2 StartLimitBurst=2
Before=multi-user.target Before=multi-user.target
After=power-profiles-daemon.service
After=nvidia-powerd.service
[Service] [Service]
Environment=IS_SERVICE=1 Environment=IS_SERVICE=1
Environment=RUST_LOG="info"
ExecStartPre=/bin/sleep 2 ExecStartPre=/bin/sleep 2
ExecStart=/usr/bin/asusd ExecStart=/usr/bin/asusd
Restart=on-failure Restart=on-failure
Restart=always
RestartSec=1 RestartSec=1
Type=dbus Type=dbus
BusName=org.asuslinux.Daemon BusName=org.asuslinux.Daemon

69
deny.toml Normal file
View File

@@ -0,0 +1,69 @@
# https://embarkstudios.github.io/cargo-deny/
targets = [
{ triple = "aarch64-apple-darwin" },
{ triple = "aarch64-linux-android" },
{ triple = "wasm32-unknown-unknown" },
{ triple = "x86_64-apple-darwin" },
{ triple = "x86_64-pc-windows-msvc" },
{ triple = "x86_64-unknown-linux-gnu" },
{ triple = "x86_64-unknown-linux-musl" },
]
[advisories]
vulnerability = "deny"
unmaintained = "warn"
yanked = "deny"
ignore = [
"RUSTSEC-2020-0071", # https://rustsec.org/advisories/RUSTSEC-2020-0071 - chrono/time: Potential segfault in the time crate
"RUSTSEC-2020-0159", # https://rustsec.org/advisories/RUSTSEC-2020-0159 - chrono/time: Potential segfault in localtime_r invocations
"RUSTSEC-2021-0127", # https://rustsec.org/advisories/RUSTSEC-2021-0127 - https://github.com/bheisler/criterion.rs/issues/534
]
[bans]
multiple-versions = "deny"
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
deny = [
{ name = "openssl" }, # prefer rustls
{ name = "openssl-sys" }, # prefer rustls
]
skip-tree = [
{ name = "criterion" }, # dev-dependency
{ name = "glium" }, # legacy crate, lots of old dependencies
{ name = "rfd" }, # example dependency
{ name = "three-d" }, # example dependency
]
[licenses]
unlicensed = "deny"
allow-osi-fsf-free = "neither"
confidence-threshold = 0.92 # We want really high confidence when inferring licenses from text
copyleft = "deny"
allow = [
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
"BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained
"CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
"ISC", # https://tldrlegal.com/license/-isc-license
"LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321
"MIT", # https://tldrlegal.com/license/mit-license
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11
"OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html
"OpenSSL", # https://www.openssl.org/source/license.html
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
"Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)
]
[[licenses.clarify]]
name = "webpki"
expression = "ISC"
license-files = [{ path = "LICENSE", hash = 0x001c7e6c }]
[[licenses.clarify]]
name = "ring"
expression = "MIT AND ISC AND OpenSSL"
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "rog_anime" name = "rog_anime"
version.workspace = true
license = "MPL-2.0" license = "MPL-2.0"
version.workspace = true
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl" repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
@@ -30,4 +30,9 @@ glam.workspace = true
zbus = { workspace = true, optional = true } zbus = { workspace = true, optional = true }
sysfs-class = { workspace = true, optional = true } sysfs-class = { workspace = true, optional = true }
uhid-virt = "^0.0.6"
[dev-dependencies]
cargo-husky.workspace = true

View File

@@ -1,20 +1,17 @@
use std::{ use std::convert::TryFrom;
convert::TryFrom, use std::thread::sleep;
thread::sleep, use std::time::{Duration, Instant};
time::{Duration, Instant},
};
use log::info; use log::info;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::Type; use zbus::zvariant::Type;
use crate::{ use crate::error::{AnimeError, Result};
error::{AnimeError, Result}, use crate::{AnimTime, AnimeGif};
AnimTime, AnimeGif,
};
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2` /// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and
/// `USB_PREFIX2`
const BLOCK_START: usize = 7; const BLOCK_START: usize = 7;
/// *Not* inclusive, the byte before this is the final for each "pane" /// *Not* inclusive, the byte before this is the final for each "pane"
const BLOCK_END: usize = 634; const BLOCK_END: usize = 634;
@@ -47,8 +44,7 @@ impl AnimeType {
/// The width of diagonal images /// The width of diagonal images
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
match self { match self {
AnimeType::GA401 => 74, AnimeType::GA401 | AnimeType::GA402 => 74,
AnimeType::GA402 => 74,
} }
} }
@@ -103,8 +99,8 @@ impl AnimeDataBuffer {
/// Create from a vector of bytes /// Create from a vector of bytes
/// ///
/// # Panics /// # Errors
/// Will panic if the vector length is not `ANIME_DATA_LEN` /// Will error if the vector length is not `ANIME_DATA_LEN`
#[inline] #[inline]
pub fn from_vec(anime: AnimeType, data: Vec<u8>) -> Result<Self> { pub fn from_vec(anime: AnimeType, data: Vec<u8>) -> Result<Self> {
if data.len() != anime.data_length() { if data.len() != anime.data_length() {
@@ -144,13 +140,12 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
} }
} }
/// This runs the animations as a blocking loop by using the `callback` to write data /// This runs the animations as a blocking loop by using the `callback` to write
/// data
/// ///
/// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early. /// If `callback` is `Ok(true)` then `run_animation` will exit the animation
pub fn run_animation( /// loop early.
frames: &AnimeGif, pub fn run_animation(frames: &AnimeGif, callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>) {
callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>,
) -> Result<()> {
let mut count = 0; let mut count = 0;
let start = Instant::now(); let start = Instant::now();
@@ -215,9 +210,10 @@ pub fn run_animation(
} }
} }
// TODO: Log this error
if matches!(callback(output), Ok(true)) { if matches!(callback(output), Ok(true)) {
info!("rog-anime: frame-loop callback asked to exit early"); info!("rog-anime: animation frame-loop callback asked to exit early");
return Ok(()); return;
} }
if timed && Instant::now().duration_since(start) > run_time { if timed && Instant::now().duration_since(start) > run_time {
@@ -232,5 +228,4 @@ pub fn run_animation(
} }
} }
} }
Ok(())
} }

View File

@@ -1,12 +1,12 @@
use std::{path::Path, time::Duration}; use std::path::Path;
use std::time::Duration;
use crate::{ use crate::data::AnimeDataBuffer;
data::AnimeDataBuffer, use crate::error::{AnimeError, Result};
error::{AnimeError, Result}, use crate::AnimeType;
AnimeType,
};
/// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images) /// Mostly intended to be used with ASUS gifs, but can be used for other
/// purposes (like images)
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AnimeDiagonal(AnimeType, Vec<Vec<u8>>, Option<Duration>); pub struct AnimeDiagonal(AnimeType, Vec<Vec<u8>>, Option<Duration>);
@@ -25,7 +25,8 @@ impl AnimeDiagonal {
&mut self.1 &mut self.1
} }
/// Get a full diagonal row where `x` `y` is the starting point and `len` is the length of data. /// Get a full diagonal row where `x` `y` is the starting point and `len`
/// is the length of data.
fn get_row(&self, x: usize, y: usize, len: usize) -> Vec<u8> { fn get_row(&self, x: usize, y: usize, len: usize) -> Vec<u8> {
let mut buf = Vec::with_capacity(len); let mut buf = Vec::with_capacity(len);
for i in 0..len { for i in 0..len {
@@ -36,8 +37,9 @@ impl AnimeDiagonal {
buf buf
} }
/// Generate the base image from inputs. The result can be displayed as is or /// Generate the base image from inputs. The result can be displayed as is
/// updated via scale, position, or angle then displayed again after `update()`. /// or updated via scale, position, or angle then displayed again after
/// `update()`.
#[inline] #[inline]
pub fn from_png( pub fn from_png(
path: &Path, path: &Path,
@@ -52,39 +54,43 @@ impl AnimeDiagonal {
let mut matrix = AnimeDiagonal::new(anime_type, duration); let mut matrix = AnimeDiagonal::new(anime_type, duration);
match raster { match &raster {
png_pong::PngRaster::Gray8(ras) => { png_pong::PngRaster::Gray8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, true) Self::pixels_from_8bit(ras, &mut matrix, bright, true);
} }
png_pong::PngRaster::Graya8(ras) => { png_pong::PngRaster::Graya8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, true) Self::pixels_from_8bit(ras, &mut matrix, bright, true);
} }
png_pong::PngRaster::Rgb8(ras) => { png_pong::PngRaster::Rgb8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, false) Self::pixels_from_8bit(ras, &mut matrix, bright, false);
} }
png_pong::PngRaster::Rgba8(ras) => { png_pong::PngRaster::Rgba8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, false) Self::pixels_from_8bit(ras, &mut matrix, bright, false);
} }
png_pong::PngRaster::Gray16(ras) => { png_pong::PngRaster::Gray16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, true) Self::pixels_from_16bit(ras, &mut matrix, bright, true);
} }
png_pong::PngRaster::Rgb16(ras) => { png_pong::PngRaster::Rgb16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, false) Self::pixels_from_16bit(ras, &mut matrix, bright, false);
} }
png_pong::PngRaster::Graya16(ras) => { png_pong::PngRaster::Graya16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, true) Self::pixels_from_16bit(ras, &mut matrix, bright, true);
} }
png_pong::PngRaster::Rgba16(ras) => { png_pong::PngRaster::Rgba16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, false) Self::pixels_from_16bit(ras, &mut matrix, bright, false);
} }
_ => return Err(AnimeError::Format), png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
}; };
Ok(matrix) Ok(matrix)
} }
fn pixels_from_8bit<P>(ras: pix::Raster<P>, matrix: &mut AnimeDiagonal, bright: f32, grey: bool) fn pixels_from_8bit<P>(
where ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal,
bright: f32,
grey: bool,
) where
P: pix::el::Pixel<Chan = pix::chan::Ch8>, P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{ {
let width = ras.width(); let width = ras.width();
@@ -105,7 +111,7 @@ impl AnimeDiagonal {
} }
fn pixels_from_16bit<P>( fn pixels_from_16bit<P>(
ras: pix::Raster<P>, ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal, matrix: &mut AnimeDiagonal,
bright: f32, bright: f32,
grey: bool, grey: bool,
@@ -136,7 +142,7 @@ impl AnimeDiagonal {
} }
} }
/// Do conversion from the nested Vec in AnimeMatrix to the two required /// Do conversion from the nested Vec in `AnimeMatrix` to the two required
/// packets suitable for sending over USB /// packets suitable for sending over USB
fn to_ga401_packets(&self) -> Result<AnimeDataBuffer> { fn to_ga401_packets(&self) -> Result<AnimeDataBuffer> {
let mut buf = vec![0u8; AnimeType::GA401.data_length()]; let mut buf = vec![0u8; AnimeType::GA401.data_length()];
@@ -286,7 +292,8 @@ impl AnimeDiagonal {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{convert::TryFrom, path::PathBuf}; use std::convert::TryFrom;
use std::path::PathBuf;
use crate::{AnimeDiagonal, AnimePacketType, AnimeType}; use crate::{AnimeDiagonal, AnimePacketType, AnimeType};

View File

@@ -1,8 +1,9 @@
use gif::DecodingError;
use png_pong::decode::Error as PngError;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use gif::DecodingError;
use png_pong::decode::Error as PngError;
pub type Result<T> = std::result::Result<T, AnimeError>; pub type Result<T> = std::result::Result<T, AnimeError>;
#[derive(Debug)] #[derive(Debug)]
@@ -12,7 +13,8 @@ pub enum AnimeError {
Png(PngError), Png(PngError),
Gif(DecodingError), Gif(DecodingError),
Format, Format,
/// The input was incorrect size, expected size is `IncorrectSize(width, height)` /// The input was incorrect size, expected size is `IncorrectSize(width,
/// height)`
IncorrectSize(u32, u32), IncorrectSize(u32, u32),
Dbus(String), Dbus(String),
Udev(String, std::io::Error), Udev(String, std::io::Error),
@@ -26,7 +28,7 @@ pub enum AnimeError {
impl fmt::Display for AnimeError { impl fmt::Display for AnimeError {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
AnimeError::NoFrames => write!(f, "No frames in PNG"), AnimeError::NoFrames => write!(f, "No frames in PNG"),
AnimeError::Io(e) => write!(f, "Could not open: {}", e), AnimeError::Io(e) => write!(f, "Could not open: {}", e),

View File

@@ -1,15 +1,19 @@
use std::convert::TryFrom;
use std::fs::File;
use std::path::Path;
use std::time::Duration;
use glam::Vec2; use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::{fs::File, path::Path, time::Duration};
use crate::error::AnimeError; use crate::error::{AnimeError, Result};
use crate::{error::Result, AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel}; use crate::{AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel};
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeFrame { pub struct AnimeFrame {
/// Precomputed data for the frame. This can be transferred directly to the /// Precomputed data for the frame. This can be transferred directly to the
/// the `asusd` daemon over dbus or converted to USB packet with `AnimePacketType::from(buffer)` /// the `asusd` daemon over dbus or converted to USB packet with
/// `AnimePacketType::from(buffer)`
data: AnimeDataBuffer, data: AnimeDataBuffer,
delay: Duration, delay: Duration,
} }
@@ -33,7 +37,8 @@ impl AnimeFrame {
pub enum AnimTime { pub enum AnimTime {
/// Time in milliseconds for animation to run /// Time in milliseconds for animation to run
Time(Duration), Time(Duration),
/// How many full animation loops to run or how many seconds if image is static /// How many full animation loops to run or how many seconds if image is
/// static
Count(u32), Count(u32),
/// Run for infinite time /// Run for infinite time
Infinite, Infinite,
@@ -169,8 +174,8 @@ impl AnimeGif {
Ok(Self(frames, duration)) Ok(Self(frames, duration))
} }
/// Create an animation using a gif of any size. This method must precompute the /// Create an animation using a gif of any size. This method must precompute
/// result. /// the result.
#[inline] #[inline]
pub fn from_gif( pub fn from_gif(
file_name: &Path, file_name: &Path,
@@ -243,9 +248,10 @@ impl AnimeGif {
Ok(Self(frames, duration)) Ok(Self(frames, duration))
} }
/// Make a static gif out of a greyscale png. If no duration is specified then the default /// Make a static gif out of a greyscale png. If no duration is specified
/// will be 1 second long. If `AnimTime::Cycles` is specified for `duration` then this can /// then the default will be 1 second long. If `AnimTime::Cycles` is
/// be considered how many seconds the image will show for. /// specified for `duration` then this can be considered how many
/// seconds the image will show for.
#[inline] #[inline]
pub fn from_png( pub fn from_png(
file_name: &Path, file_name: &Path,

View File

@@ -93,7 +93,7 @@ impl AnimeGrid {
impl TryFrom<AnimeGrid> for AnimeDataBuffer { impl TryFrom<AnimeGrid> for AnimeDataBuffer {
type Error = AnimeError; type Error = AnimeError;
/// Do conversion from the nested Vec in AniMeMatrix to the two required /// Do conversion from the nested Vec in anime matrix to the two required
/// packets suitable for sending over USB /// packets suitable for sending over USB
fn try_from(anime: AnimeGrid) -> Result<Self> { fn try_from(anime: AnimeGrid) -> Result<Self> {
let mut buf = vec![0u8; anime.anime_type.data_length()]; let mut buf = vec![0u8; anime.anime_type.data_length()];

View File

@@ -1,13 +1,12 @@
use std::{convert::TryFrom, path::Path}; use std::convert::TryFrom;
use std::path::Path;
pub use glam::Vec2; pub use glam::Vec2;
use glam::{Mat3, Vec3}; use glam::{Mat3, Vec3};
use crate::{ use crate::data::AnimeDataBuffer;
data::AnimeDataBuffer, use crate::error::{AnimeError, Result};
error::{AnimeError, Result}, use crate::AnimeType;
AnimeType,
};
/// A single greyscale + alpha pixel in the image /// A single greyscale + alpha pixel in the image
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@@ -29,8 +28,8 @@ impl Default for Pixel {
/// A single LED position and brightness. The intention of this struct /// A single LED position and brightness. The intention of this struct
/// is to be used to sample an image and set the LED brightness. /// is to be used to sample an image and set the LED brightness.
/// ///
/// The position of the Led in `LedPositions` determines the placement in the final /// The position of the Led in `LedPositions` determines the placement in the
/// data packets when written to the AniMe. /// final data packets when written to the `AniMe`.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Led(f32, f32, u8); pub struct Led(f32, f32, u8);
@@ -58,7 +57,7 @@ impl Led {
/// Container of `Led`, each of which specifies a position within the image /// Container of `Led`, each of which specifies a position within the image
/// The main use of this is to position and sample colours for the final image /// The main use of this is to position and sample colours for the final image
/// to show on AniMe /// to show on `AniMe`
pub struct AnimeImage { pub struct AnimeImage {
pub scale: Vec2, pub scale: Vec2,
/// Angle in radians /// Angle in radians
@@ -73,8 +72,9 @@ pub struct AnimeImage {
img_pixels: Vec<Pixel>, img_pixels: Vec<Pixel>,
/// width of the image /// width of the image
width: u32, width: u32,
/// The type of the display. The GA401 and GA402 use the same controller and therefore same ID, /// The type of the display. The GA401 and GA402 use the same controller and
/// so the identifier must be by laptop model in `AnimeType`. /// therefore same ID, so the identifier must be by laptop model in
/// `AnimeType`.
anime_type: AnimeType, anime_type: AnimeType,
} }
@@ -108,9 +108,9 @@ impl AnimeImage {
/// Scale ratio in CM /// Scale ratio in CM
/// ///
/// This is worked out by measuring the physical width of the display from pixel center to /// This is worked out by measuring the physical width of the display from
/// center, then dividing by `<horizontal LED count> + 0.5`, where the LED count is /// pixel center to center, then dividing by `<horizontal LED count> +
/// first/longest row. /// 0.5`, where the LED count is first/longest row.
/// ///
/// For GA401 this is `26.8 / (33 + 0.5) = 0.8` /// For GA401 this is `26.8 / (33 + 0.5) = 0.8`
/// For GA402 this is `27.4 / (35 + 0.5) = 0.77` /// For GA402 this is `27.4 / (35 + 0.5) = 0.77`
@@ -123,9 +123,9 @@ impl AnimeImage {
/// Scale ratio in CM /// Scale ratio in CM
/// ///
/// This is worked out by measuring the physical height of the display from pixel center to /// This is worked out by measuring the physical height of the display from
/// pixel center, then dividing by `<vertical LED count> + 1.0`, where the LED count is /// pixel center to pixel center, then dividing by `<vertical LED
/// first/longest row. /// count> + 1.0`, where the LED count is first/longest row.
/// ///
/// For GA401 this is `16.5 / (54.0 + 1.0) = 0.3` /// For GA401 this is `16.5 / (54.0 + 1.0) = 0.3`
/// For GA402 this is `17.3 / (61.0) = 0.283` /// For GA402 this is `17.3 / (61.0) = 0.283`
@@ -136,11 +136,13 @@ impl AnimeImage {
} }
} }
/// Get the starting X position for the data we actually require when writing /// Get the starting X position for the data we actually require when
/// it out to LEDs. /// writing it out to LEDs.
///
/// In relation to the display itself you should think of it as a full
/// square grid, so `first_x` is the x position on that grid where the
/// LED is actually positioned in relation to the Y.
/// ///
/// In relation to the display itself you should think of it as a full square grid, so `first_x`
/// is the x position on that grid where the LED is actually positioned in relation to the Y.
/// ```text /// ```text
/// +------------+ /// +------------+
/// | | /// | |
@@ -251,8 +253,8 @@ impl AnimeImage {
&mut self.img_pixels &mut self.img_pixels
} }
/// Generate a list of LED positions. These are then used to sample the Image data, /// Generate a list of LED positions. These are then used to sample the
/// and will contain their resulting brightness. /// Image data, and will contain their resulting brightness.
#[inline] #[inline]
pub fn generate_image_positioning(anime_type: AnimeType) -> Vec<Option<Led>> { pub fn generate_image_positioning(anime_type: AnimeType) -> Vec<Option<Led>> {
(0..AnimeImage::height(anime_type)) (0..AnimeImage::height(anime_type))
@@ -274,8 +276,8 @@ impl AnimeImage {
/// samples, the result can then been transformed to the appropriate data /// samples, the result can then been transformed to the appropriate data
/// for displaying. /// for displaying.
/// ///
/// The internal for loop iterates over the LED positions, skipping the blank/dead /// The internal for loop iterates over the LED positions, skipping the
/// pixels if any. /// blank/dead pixels if any.
#[inline] #[inline]
pub fn update(&mut self) { pub fn update(&mut self) {
let width = self.width as i32; let width = self.width as i32;
@@ -294,8 +296,8 @@ impl AnimeImage {
let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0)); let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0));
const GROUP: [f32; 4] = [0.0, 0.5, 1.0, 1.5]; const GROUP: [f32; 4] = [0.0, 0.5, 1.0, 1.5];
for u in GROUP.iter() { for u in &GROUP {
for v in GROUP.iter() { for v in &GROUP {
let sample = x0 + *u * du + *v * dv; let sample = x0 + *u * du + *v * dv;
let x = sample.x as i32; let x = sample.x as i32;
@@ -343,7 +345,7 @@ impl AnimeImage {
} }
last_was_led = true; last_was_led = true;
} else if last_was_led { } else if last_was_led {
//ends.push(idx); // ends.push(idx);
last_was_led = false; last_was_led = false;
} }
} }
@@ -382,8 +384,9 @@ impl AnimeImage {
led_from_px.inverse() led_from_px.inverse()
} }
/// Generate the base image from inputs. The result can be displayed as is or /// Generate the base image from inputs. The result can be displayed as is
/// updated via scale, position, or angle then displayed again after `update()`. /// or updated via scale, position, or angle then displayed again after
/// `update()`.
#[inline] #[inline]
pub fn from_png( pub fn from_png(
path: &Path, path: &Path,
@@ -399,7 +402,7 @@ impl AnimeImage {
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??; let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
let width; let width;
let pixels = match raster { let pixels = match &raster {
png_pong::PngRaster::Gray8(ras) => { png_pong::PngRaster::Gray8(ras) => {
width = ras.width(); width = ras.width();
Self::pixels_from_8bit(ras, true) Self::pixels_from_8bit(ras, true)
@@ -432,7 +435,7 @@ impl AnimeImage {
width = ras.width(); width = ras.width();
Self::pixels_from_16bit(ras, false) Self::pixels_from_16bit(ras, false)
} }
_ => return Err(AnimeError::Format), png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
}; };
let mut matrix = AnimeImage::new( let mut matrix = AnimeImage::new(
@@ -449,7 +452,7 @@ impl AnimeImage {
Ok(matrix) Ok(matrix)
} }
fn pixels_from_8bit<P>(ras: pix::Raster<P>, grey: bool) -> Vec<Pixel> fn pixels_from_8bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where where
P: pix::el::Pixel<Chan = pix::chan::Ch8>, P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{ {
@@ -468,7 +471,7 @@ impl AnimeImage {
.collect() .collect()
} }
fn pixels_from_16bit<P>(ras: pix::Raster<P>, grey: bool) -> Vec<Pixel> fn pixels_from_16bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where where
P: pix::el::Pixel<Chan = pix::chan::Ch16>, P: pix::el::Pixel<Chan = pix::chan::Ch16>,
{ {
@@ -491,8 +494,8 @@ impl AnimeImage {
impl TryFrom<&AnimeImage> for AnimeDataBuffer { impl TryFrom<&AnimeImage> for AnimeDataBuffer {
type Error = AnimeError; type Error = AnimeError;
/// Do conversion from the nested Vec in AnimeDataBuffer to the two required /// Do conversion from the nested Vec in `AnimeDataBuffer` to the two
/// packets suitable for sending over USB /// required packets suitable for sending over USB
fn try_from(leds: &AnimeImage) -> Result<Self> { fn try_from(leds: &AnimeImage) -> Result<Self> {
let mut l: Vec<u8> = leds let mut l: Vec<u8> = leds
.led_pos .led_pos
@@ -511,9 +514,11 @@ impl TryFrom<&AnimeImage> for AnimeDataBuffer {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{convert::TryFrom, path::PathBuf}; use std::convert::TryFrom;
use std::path::PathBuf;
use crate::{image::*, AnimTime, AnimeGif, AnimePacketType}; use crate::image::*;
use crate::{AnimTime, AnimeGif, AnimePacketType};
#[test] #[test]
fn led_positions() { fn led_positions() {

View File

@@ -7,12 +7,12 @@ pub use data::*;
mod grid; mod grid;
pub use grid::*; pub use grid::*;
/// Transform a PNG image for displaying on AniMe matrix display /// Transform a PNG image for displaying on `AniMe` matrix display
mod image; mod image;
pub use image::*; pub use image::*;
/// A grid of data that is intended to be read out and displayed on the ANiMe as /// A grid of data that is intended to be read out and displayed on the `AniMe`
/// a diagonal /// as a diagonal
mod diagonal; mod diagonal;
pub use diagonal::*; pub use diagonal::*;
@@ -21,8 +21,8 @@ pub use diagonal::*;
mod gif; mod gif;
pub use crate::gif::*; pub use crate::gif::*;
/// A container of images/grids/gifs/pauses which can be iterated over to generate /// A container of images/grids/gifs/pauses which can be iterated over to
/// cool effects /// generate cool effects
mod sequencer; mod sequencer;
pub use sequencer::*; pub use sequencer::*;

View File

@@ -1,14 +1,15 @@
use std::convert::TryFrom;
use std::path::PathBuf;
use std::time::Duration;
use glam::Vec2; use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::{path::PathBuf, time::Duration};
use crate::{ use crate::error::Result;
error::Result, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, use crate::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType};
};
/// All the possible AniMe actions that can be used. This enum is intended to be /// All the possible `AniMe` actions that can be used. This enum is intended to
/// a helper for loading up `ActionData`. /// be a helper for loading up `ActionData`.
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ActionLoader { pub enum ActionLoader {
/// Full gif sequence. Immutable. /// Full gif sequence. Immutable.
@@ -23,7 +24,8 @@ pub enum ActionLoader {
time: AnimTime, time: AnimTime,
brightness: f32, brightness: f32,
}, },
/// Animated gif. If the file is a png a static gif is created using the `time` properties /// Animated gif. If the file is a png a static gif is created using the
/// `time` properties
ImageAnimation { ImageAnimation {
file: PathBuf, file: PathBuf,
scale: f32, scale: f32,
@@ -44,13 +46,14 @@ pub enum ActionLoader {
Pause(Duration), Pause(Duration),
} }
/// All the possible AniMe actions that can be used. The enum is intended to be /// All the possible `AniMe` actions that can be used. The enum is intended to
/// used in a array allowing the user to cycle through a series of actions. /// be used in a array allowing the user to cycle through a series of actions.
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ActionData { pub enum ActionData {
/// Full gif sequence. Immutable. /// Full gif sequence. Immutable.
Animation(AnimeGif), Animation(AnimeGif),
/// Basic image, can have properties changed and image updated via those properties /// Basic image, can have properties changed and image updated via those
/// properties
Image(Box<AnimeDataBuffer>), Image(Box<AnimeDataBuffer>),
/// A pause to be used between sequences /// A pause to be used between sequences
Pause(Duration), Pause(Duration),
@@ -174,8 +177,8 @@ impl Sequences {
Self(Vec::new(), anime_type) Self(Vec::new(), anime_type)
} }
/// Use a base `AnimeAction` to generate the precomputed data and insert in to /// Use a base `AnimeAction` to generate the precomputed data and insert in
/// the run buffer /// to the run buffer
#[inline] #[inline]
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<()> { pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<()> {
self.0 self.0
@@ -183,9 +186,9 @@ impl Sequences {
Ok(()) Ok(())
} }
/// Remove an item at this position from the run buffer. If the `index` supplied /// Remove an item at this position from the run buffer. If the `index`
/// is not in range then `None` is returned, otherwise the `ActionData` at that location /// supplied is not in range then `None` is returned, otherwise the
/// is yeeted and returned. /// `ActionData` at that location is yeeted and returned.
#[inline] #[inline]
pub fn remove_item(&mut self, index: usize) -> Option<ActionData> { pub fn remove_item(&mut self, index: usize) -> Option<ActionData> {
if index < self.0.len() { if index < self.0.len() {
@@ -194,7 +197,7 @@ impl Sequences {
None None
} }
pub fn iter(&self) -> ActionIterator { pub fn iter(&self) -> ActionIterator<'_> {
ActionIterator { ActionIterator {
actions: self, actions: self,
next_idx: 0, next_idx: 0,

View File

@@ -1,13 +1,15 @@
//! Utils for writing to the AniMe USB device //! Utils for writing to the `AniMe` USB device
//! //!
//! Use of the device requires a few steps: //! Use of the device requires a few steps:
//! 1. Initialise the device by writing the two packets from `get_init_packets()` //! 1. Initialise the device by writing the two packets from
//! 2. Write data from `AnimePacketType` //! `get_init_packets()` 2. Write data from `AnimePacketType`
//! 3. Write the packet from `get_flush_packet()`, which tells the device to display the data from step 2 //! 3. Write the packet from `get_flush_packet()`, which tells the device to
//! display the data from step 2
//! //!
//! Step 1 need to applied only on fresh system boot. //! Step 1 need to applied only on fresh system boot.
use crate::{error::AnimeError, AnimeType}; use crate::error::AnimeError;
use crate::AnimeType;
const INIT_STR: [u8; 15] = [ const INIT_STR: [u8; 15] = [
0x5e, b'A', b'S', b'U', b'S', b' ', b'T', b'e', b'c', b'h', b'.', b'I', b'n', b'c', b'.', 0x5e, b'A', b'S', b'U', b'S', b' ', b'T', b'e', b'c', b'h', b'.', b'I', b'n', b'c', b'.',
@@ -17,8 +19,9 @@ const DEV_PAGE: u8 = 0x5e;
pub const VENDOR_ID: u16 = 0x0b05; pub const VENDOR_ID: u16 = 0x0b05;
pub const PROD_ID: u16 = 0x193b; pub const PROD_ID: u16 = 0x193b;
/// `get_anime_type` is very broad, matching on part of the laptop board name only. For this /// `get_anime_type` is very broad, matching on part of the laptop board name
/// reason `find_node()` must be used also to verify if the USB device is available. /// only. For this reason `find_node()` must be used also to verify if the USB
/// device is available.
/// ///
/// The currently known USB device is `19b6`. /// The currently known USB device is `19b6`.
#[inline] #[inline]
@@ -34,8 +37,8 @@ pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
Err(AnimeError::UnsupportedDevice) Err(AnimeError::UnsupportedDevice)
} }
/// Get the two device initialization packets. These are required for device start /// Get the two device initialization packets. These are required for device
/// after the laptop boots. /// start after the laptop boots.
#[inline] #[inline]
pub const fn pkts_for_init() -> [[u8; PACKET_SIZE]; 2] { pub const fn pkts_for_init() -> [[u8; PACKET_SIZE]; 2] {
let mut packets = [[0; PACKET_SIZE]; 2]; let mut packets = [[0; PACKET_SIZE]; 2];
@@ -63,7 +66,7 @@ pub const fn pkt_for_flush() -> [u8; PACKET_SIZE] {
} }
/// Get the packet required for setting the device to on, on boot. Requires /// Get the packet required for setting the device to on, on boot. Requires
/// pkt_for_apply()` to be written after. /// `pkt_for_apply()` to be written after.
#[inline] #[inline]
pub const fn pkt_for_set_boot(status: bool) -> [u8; PACKET_SIZE] { pub const fn pkt_for_set_boot(status: bool) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE]; let mut pkt = [0; PACKET_SIZE];
@@ -74,8 +77,8 @@ pub const fn pkt_for_set_boot(status: bool) -> [u8; PACKET_SIZE] {
pkt pkt
} }
/// Get the packet required for setting the device to on. Requires `pkt_for_apply()` /// Get the packet required for setting the device to on. Requires
/// to be written after. /// `pkt_for_apply()` to be written after.
#[inline] #[inline]
pub const fn pkt_for_set_on(on: bool) -> [u8; PACKET_SIZE] { pub const fn pkt_for_set_on(on: bool) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE]; let mut pkt = [0; PACKET_SIZE];

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "rog_aura" name = "rog_aura"
version.workspace = true
license = "MPL-2.0" license = "MPL-2.0"
version.workspace = true
readme = "README.md" readme = "README.md"
authors = ["Luke <luke@ljones.dev>"] authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asusctl" repository = "https://gitlab.com/asus-linux/asusctl"
@@ -13,14 +13,21 @@ edition = "2021"
exclude = ["data"] exclude = ["data"]
[features] [features]
default = ["dbus", "toml"] default = ["dbus", "ron"]
dbus = ["zbus"] dbus = ["zbus"]
[dependencies] [dependencies]
serde.workspace = true serde.workspace = true
serde_derive.workspace = true serde_derive.workspace = true
toml = { workspace = true, optional = true }
zbus = { workspace = true, optional = true } zbus = { workspace = true, optional = true }
# cli and logging
log.workspace = true
# Device control
sysfs-class.workspace = true # used for backlight control and baord ID
ron = { version = "*", optional = true }
[dev-dependencies] [dev-dependencies]
serde_json.workspace = true cargo-husky.workspace = true

View File

@@ -0,0 +1,246 @@
# rog-aura
## What is it?
rog-aura is a helper crate for interacting with the RGB keyboards found on many ASUS ROG gaming laptops such as the Zephyrus, Strix, TUF, and a few others.
The crate is primarily used in the asusctl suite of tools.
The majority of the crate deals with converting from the API to USB packets suitable for sending raw to the USB device.
## Features
- Detect USB keyboard type, or if the kayboard is an I2C connected one (typical on TUF)
- Set various basic modes
- Set various basic zones
- Set advanced/direct addressing of:
+ Single zone
+ Multizone
+ Per-key
- Physical layout mapping
## Config files
The crate includes config files for helping to determine what laptop models support what feature. This is heavily dependant on folks testing and contributing data.
It also includes layouts for some laptops. Also heavily dependant on contributions.
# Support list
`aura_support.ron` is the support listing file. It functions as a database of which models support which features.
```ron
(
board_name: "G513QR",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
```
in the above example the board name is found from `cat /sys/devices/virtual/dmi/id/board_name`. In some model ranges the last letter (which is likely the dGPU/feature variant) can be ommited. `layout_name` is the first part of a related filename for the layout as described in the next section - the filename should be postfixed with a locale such as `g513i_US.ron`.
`basic_modes` are the default inbuilt modes the keyboard supports. Not all keyboards have the same set of modes. `basic_zones` is a secondary part of `basic_modes` where this lists which zones can be set as part of the basic mode. Each zone reauires a full basic mode setting. The zones supported here are
- `Key1`
- `Key2`
- `Key3`
- `Key4`
- `Logo`
- `BarLeft`
- `BarRight`
note that the zone support seems to have changed with new generations of keyboards and is shifted to `advanced_type`. The `advanced_type` field is taken in to account when setting advanced effects. It can be combined with the keyboard layout also to be used in a GUI.
`advanced_type` can be one of:
- `None`, no advanced aura at all
- `PerKey`, can use any of `LedCode` except for the `Zoned` items below which work in a different way
- `Zoned`, takes an array such as:
+ `Zoned([SingleZone])`, only one zone
+ `Zoned([ ... ]),`, array with any combination of:
- `ZonedKbLeft` // keyboard left
- `ZonedKbLeftMid` // keyboard left-middle
- `ZonedKbRightMid` // etc
- `ZonedKbRight`
- `LightbarRight`
- `LightbarRightCorner`
- `LightbarRightBottom`
- `LightbarLeftBottom`
- `LightbarLeftCorner`
- `LightbarLeft`
# Layouts
The layout structure is kept in a `.ron`, which is "rusty object notation". The way this works is best demonstrated:
```ron
(
locale: "US",
key_shapes: {
// This is a regular LED spot, it has a size (width x height), and padding around each edge.
// The final size should be (width + pad_left + pad_right, height + pad_top + pad_bottom)
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
// There is nothing in this space but it takes up room
"func_space": Blank(
width: 0.2,
height: 0.0,
),
// This backspace button is composed of 3 individual LED
"backspace1": Led(
width: 0.65,
height: 1.0,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backspace2": Led(
width: 0.7,
height: 1.0,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backspace3": Led(
width: 0.65,
height: 1.0,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
},
key_rows: [
(
// Padding generally isn't required but is available just in case
pad_left: 0.1,
pad_top: 0.1,
// Each row is a horizontal row of keys of the keyboard
row: [
// Declare a tuple of `Key`, and the String name to use from the hashmap above
(Spacing, "rog_spacer"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(Rog, "rog_row"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Esc, "func_key"),
// There are two non-led types, `Blocking` which is intended to block something like a row-laser
(Blocking, "esc_func_spacing"),
(F1, "func_key"),
(F2, "func_key"),
(F3, "func_key"),
(F4, "func_key"),
// and `Spacing` which is intended to act like a non-visible LED
(Spacing, "func_space"),
(F5, "func_key"),
(F6, "func_key"),
(F7, "func_key"),
(F8, "func_key"),
(Spacing, "func_space"),
(F9, "func_key"),
(F10, "func_key"),
(F11, "func_key"),
(F12, "func_key"),
(Spacing, "func_space"),
(Del, "func_key"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace3_1, "backspace1"),
(Backspace3_2, "backspace2"),
(Backspace3_3, "backspace3"),
(Spacing, "func_space"),
(Home, "regular"),
],
),
]
)
```
**There are two types of layouts to be considered when building one; per-key, and zoned.**
A zoned keyboard layout includes single zoned + no zones (but not per-key). The layout for this is fairly freeform, and can be built using regular keys from `LedCode` but can not include these per-key specific codes:
- `LidLogo`
- `LidLeft`
- `LidRight`
it can include regular keys and:
- `SingleZone`, if this is used then the `ZonedKb*` should not be used
- `ZonedKbLeft`
- `ZonedKbLeftMid`
- `ZonedKbRightMid`
- `ZonedKbRight`
- `LightbarRight`
- `LightbarRightCorner`
- `LightbarRightBottom`
- `LightbarLeftBottom`
- `LightbarLeftCorner`
- `LightbarLeft`
#### `Key`
Every `Key` in the enum maps to a USB packet + RGB index in that packet. The raw mapping is seen in `per_key_raw_bytes.ods` in the data dir, for example there is a single LED backspace, and a 3-LED backspace.
#### `key_shapes`
This is a hashmap of `String`:`ShapeType`, as shown by the previous example such as:
```
// This is a regular LED spot, it has a size (width x height), and padding around each edge.
// The final size should be (width + pad_left + pad_right, height + pad_top + pad_bottom)
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
```
"regular" being the key used by the keys in each key row.
# Testing
When working with Rog Control Center you can test layouts by starting the app on CLI with options:
```
-h, --help print help message
-v, --version show program version number
-b, --board-name set board name for testing, this will make ROGCC show only the keyboard page
-l, --layout-viewing put ROGCC in layout viewing mode - this is helpful for finding existing layouts that might match your laptop
```

View File

@@ -0,0 +1,534 @@
([
(
board_name: "FA506I",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "FA506Q",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
),
(
board_name: "FA507",
layout_name: "fa507",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "FX505D",
layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "FX506HC",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "FX506LH",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "FX516P",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe],
basic_zones: [],
advanced_type: None,
),
(
board_name: "G512",
layout_name: "g512",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G512LI",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "G512LV",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G513IC",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
),
(
board_name: "G513IH",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G513IM",
layout_name: "g513i-per-key",
basic_modes: [Flash, Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G513QE",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G513QM",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G513QR",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G513QY",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G513RC",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
),
(
board_name: "G513RM",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
),
(
board_name: "G531",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G531",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
),
(
board_name: "G531GD",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "G531GT",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
),
(
board_name: "G531GU",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G531GV",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G531GW",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G532",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G533Q",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G533Z",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G712LI",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "G712LV",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G712LW",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G713IC",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G713QM",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G713QR",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G713RM",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G713RS",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G713RW",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G731",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
),
(
board_name: "G731GT",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
),
(
board_name: "G731GU",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
),
(
board_name: "G731GV",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G731GW",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "G733Q",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "G733Z",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GA401Q",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GA402R",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GA503Q",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GA503QE",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GA503R",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GL504G",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, Logo, BarLeft, BarRight],
advanced_type: None,
),
(
board_name: "GL531",
layout_name: "g512",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GL553VE",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "GL703G",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GM501G",
layout_name: "fa507",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "GU502",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GU502G",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GU502L",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GU502LU",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GU603H",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
),
(
board_name: "GU603Z",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
),
(
board_name: "GU604V",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
),
(
board_name: "GV301Q",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GV301V",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GV301VIC",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GV601R",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GV604V",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GX502",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GX531",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
),
(
board_name: "GX550L",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GX551Q",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GX650P",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GX701",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
),
(
board_name: "GX703H",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GZ301V",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
),
(
board_name: "GZ301VIC",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
),
])

View File

@@ -0,0 +1,317 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular2": Led(
width: 1.0,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"func_key": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.0,
pad_bottom: 0.1,
),
"func_space": Blank(
width: 0.5,
height: 0.0,
),
"esc_func_spacing": Blank(
width: 1.2,
height: 0.0,
),
"backspace": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"bkslash": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 1.9,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.3,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.5,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 2.9,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.7,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.2,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 0.8,
height: 0.8,
pad_left: 1.9,
pad_right: 1.4,
pad_top: 0.1,
pad_bottom: 0.0,
),
"arrows_spacer": Blank(
width: 14.5,
height: 0.0,
),
"arrows": Led(
width: 0.8,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.4,
pad_bottom: 0.1,
),
"numpad_tall": Led(
width: 1.0,
height: 2.2,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: -1.2,
),
"numpad_wide": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
},
key_rows: [
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Esc, "func_key"),
(Spacing, "esc_func_spacing"),
(F1, "func_key"),
(F2, "func_key"),
(F3, "func_key"),
(F4, "func_key"),
(Spacing, "func_space"),
(F5, "func_key"),
(F6, "func_key"),
(F7, "func_key"),
(F8, "func_key"),
(Spacing, "func_space"),
(F9, "func_key"),
(F10, "func_key"),
(F11, "func_key"),
(F12, "func_key"),
(Spacing, "func_space"),
(Del, "func_key"),
(NumPadPause, "func_key"),
(NumPadPrtSc, "func_key"),
(NumPadHome, "func_key"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(Spacing, "func_space"),
(NumLock, "regular"),
(FwdSlash, "regular"),
(Star, "regular"),
(Hyphen, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "bkslash"),
(Spacing, "func_space"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(NumPadPlus, "numpad_tall"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(Spacing, "func_space"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
(Spacing, "func_space"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(NumPadEnter, "numpad_tall"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular2"),
(Meta, "regular2"),
(LAlt, "regular2"),
(Spacebar, "spacebar"),
(RAlt, "regular2"),
(RCtrl, "rctrl"),
(Up, "up_arrow"),
(Spacing, "func_space"),
(N0, "numpad_wide"),
(NumPadDel, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
],
)

View File

@@ -0,0 +1,334 @@
(
locale: "US",
key_shapes: {
"rog_spacer": Blank(
width: 2.5,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.2,
pad_bottom: 0.5,
),
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular2": Led(
width: 1.0,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"func_key": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.0,
pad_bottom: 0.1,
),
"func_space": Blank(
width: 0.5,
height: 0.0,
),
"esc_func_spacing": Blank(
width: 1.2,
height: 0.0,
),
"backspace": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"bkslash": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 1.9,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.3,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.5,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 2.9,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.7,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.2,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 0.8,
height: 0.8,
pad_left: 1.9,
pad_right: 1.4,
pad_top: 0.1,
pad_bottom: 0.0,
),
"arrows_spacer": Blank(
width: 14.5,
height: 0.0,
),
"arrows": Led(
width: 0.8,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.4,
pad_bottom: 0.1,
),
"numpad_tall": Led(
width: 1.0,
height: 2.2,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: -1.2,
),
"numpad_wide": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
},
key_rows: [
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Spacing, "rog_spacer"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Esc, "func_key"),
(Spacing, "esc_func_spacing"),
(F1, "func_key"),
(F2, "func_key"),
(F3, "func_key"),
(F4, "func_key"),
(Spacing, "func_space"),
(F5, "func_key"),
(F6, "func_key"),
(F7, "func_key"),
(F8, "func_key"),
(Spacing, "func_space"),
(F9, "func_key"),
(F10, "func_key"),
(F11, "func_key"),
(F12, "func_key"),
(Del, "func_key"),
(NumPadPause, "func_key"),
(NumPadPrtSc, "func_key"),
(NumPadHome, "func_key"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(NumLock, "regular"),
(FwdSlash, "regular"),
(Star, "regular"),
(Hyphen, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "bkslash"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(NumPadPlus, "numpad_tall"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(NumPadEnter, "numpad_tall"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular2"),
(Meta, "regular2"),
(LAlt, "regular2"),
(Spacebar, "spacebar"),
(RAlt, "regular2"),
(RCtrl, "rctrl"),
(Up, "up_arrow"),
(N0, "numpad_wide"),
(NumPadDel, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
],
)

View File

@@ -0,0 +1,281 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular2": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.2,
pad_bottom: 0.1,
),
"func_key": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.0,
pad_bottom: 0.1,
),
"func_space": Blank(
width: 0.5,
height: 0.0,
),
"esc_func_spacing": Blank(
width: 1.2,
height: 0.0,
),
"backspace": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"bkslash": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 1.9,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.3,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.5,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.7,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows_spacer": Blank(
width: 16.6,
height: 0.0,
),
"numpad_tall": Led(
width: 1.0,
height: 2.2,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: -1.2,
),
"right_wide": Led(
width: 2.9,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
},
key_rows: [
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Esc, "func_key"),
(Spacing, "esc_func_spacing"),
(F1, "func_key"),
(F2, "func_key"),
(F3, "func_key"),
(F4, "func_key"),
(Spacing, "func_space"),
(F5, "func_key"),
(F6, "func_key"),
(F7, "func_key"),
(F8, "func_key"),
(Spacing, "func_space"),
(F9, "func_key"),
(F10, "func_key"),
(F11, "func_key"),
(F12, "func_key"),
(Del, "func_key"),
(NumPadPause, "func_key"),
(NumPadPrtSc, "func_key"),
(NumPadHome, "func_key"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(NumLock, "regular"),
(FwdSlash, "regular"),
(Star, "regular"),
(Hyphen, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "bkslash"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(NumPadPlus, "numpad_tall"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "right_wide"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(NumPadEnter, "numpad_tall"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular"),
(Meta, "regular"),
(LAlt, "regular"),
(Spacebar, "spacebar"),
(RAlt, "regular"),
(RAlt, "regular"),
(RAlt, "regular"),
(RCtrl, "right_wide"),
(Up, "regular"),
(N0, "regular"),
(NumPadDel, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "regular2"),
(Down, "regular2"),
(Right, "regular2"),
],
),
],
)

View File

@@ -0,0 +1,312 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular_spacing": Blank(
width: 1.2,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.6,
),
"rog_row_blocking": Blank(
width: 1.2,
height: 0.0,
),
"func_space": Blank(
width: 0.6,
height: 0.0,
),
"backspace": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backslash": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 3.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 0.8,
height: 0.8,
pad_left: 1.1,
pad_right: 1.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows_spacer": Blank(
width: 15.0,
height: 0.0,
),
"arrows": Led(
width: 0.8,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.1,
pad_bottom: 0.1,
),
"row_end_spacing": Blank(
width: 0.4,
height: 0.0,
),
},
key_rows: [
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Blocking, "rog_row_blocking"),
(Blocking, "rog_row_blocking"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogFan, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Esc, "regular"),
(Spacing, "regular_spacing"),
(F1, "regular"),
(F2, "regular"),
(F3, "regular"),
(F4, "regular"),
(Spacing, "func_space"),
(F5, "regular"),
(F6, "regular"),
(F7, "regular"),
(F8, "regular"),
(Spacing, "func_space"),
(F9, "regular"),
(F10, "regular"),
(F11, "regular"),
(F12, "regular"),
(Spacing, "row_end_spacing"),
(Del, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(Spacing, "row_end_spacing"),
(Home, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "backslash"),
(Spacing, "row_end_spacing"),
(PgUp, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(Spacing, "row_end_spacing"),
(PgDn, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
(Spacing, "row_end_spacing"),
(End, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular"),
(Meta, "regular"),
(LAlt, "regular"),
(Spacebar, "spacebar"),
(RAlt, "regular"),
(PrtSc, "regular"),
(RCtrl, "rctrl"),
(Up, "up_arrow"),
(Spacing, "row_end_spacing"),
(PrtSc, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
],
)

View File

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

View File

@@ -0,0 +1,366 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular_spacing": Blank(
width: 1.2,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.6,
),
"rog_row_blocking": Blank(
width: 1.2,
height: 0.0,
),
"func_space": Blank(
width: 0.6,
height: 0.0,
),
"backspace": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backslash": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 3.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 0.8,
height: 0.8,
pad_left: 1.1,
pad_right: 1.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows_spacer": Blank(
width: 15.0,
height: 0.0,
),
"arrows": Led(
width: 0.8,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.1,
pad_bottom: 0.1,
),
"row_end_spacing": Blank(
width: 0.4,
height: 0.0,
),
"lightbar_left": Led(
width: 0.4,
height: 3.0,
pad_left: -1.0,
pad_right: 0.1,
pad_top: -2.7,
pad_bottom: 0.1,
),
"lightbar_corner_left": Led(
width: 0.4,
height: 0.4,
pad_left: -0.5,
pad_right: 0.1,
pad_top: 0.5,
pad_bottom: 0.1,
),
"lightbar_bottom": Led(
width: 10.1,
height: 0.4,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.5,
pad_bottom: 0.1,
),
"lightbar_corner_right": Led(
width: 0.4,
height: 0.4,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.5,
pad_bottom: 0.1,
),
"lightbar_right": Led(
width: 0.4,
height: 3.0,
pad_left: -0.5,
pad_right: 0.1,
pad_top: -2.7,
pad_bottom: 0.1,
),
},
key_rows: [
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Blocking, "rog_row_blocking"),
(Blocking, "rog_row_blocking"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogFan, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Esc, "regular"),
(Spacing, "regular_spacing"),
(F1, "regular"),
(F2, "regular"),
(F3, "regular"),
(F4, "regular"),
(Spacing, "func_space"),
(F5, "regular"),
(F6, "regular"),
(F7, "regular"),
(F8, "regular"),
(Spacing, "func_space"),
(F9, "regular"),
(F10, "regular"),
(F11, "regular"),
(F12, "regular"),
(Spacing, "row_end_spacing"),
(Del, "regular"), // Should be super/insert
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(Spacing, "row_end_spacing"),
(MediaPlay, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "backslash"),
(Spacing, "row_end_spacing"),
(MediaStop, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(Spacing, "row_end_spacing"),
(MediaNext, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
(Spacing, "row_end_spacing"),
(MediaPrev, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular"),
(Meta, "regular"),
(LAlt, "regular"),
(Spacebar, "spacebar"),
(RAlt, "regular"),
(PrtSc, "regular"),
(RCtrl, "rctrl"),
(Up, "up_arrow"),
(Spacing, "row_end_spacing"),
(PrtSc, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LightbarLeft, "lightbar_left"),
(LightbarLeftCorner, "lightbar_corner_left"),
(LightbarLeftBottom, "lightbar_bottom"),
(LightbarRightBottom, "lightbar_bottom"),
(LightbarRightCorner, "lightbar_corner_right"),
(LightbarRight, "lightbar_right"),
],
),
],
)

View File

@@ -0,0 +1,366 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular_spacing": Blank(
width: 1.2,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.6,
),
"rog_row_blocking": Blank(
width: 1.2,
height: 0.0,
),
"func_space": Blank(
width: 0.6,
height: 0.0,
),
"backspace": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backslash": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 3.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 0.8,
height: 0.8,
pad_left: 1.1,
pad_right: 1.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows_spacer": Blank(
width: 15.0,
height: 0.0,
),
"arrows": Led(
width: 0.8,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.1,
pad_bottom: 0.1,
),
"row_end_spacing": Blank(
width: 0.4,
height: 0.0,
),
"lightbar_left": Led(
width: 0.4,
height: 3.0,
pad_left: -1.0,
pad_right: 0.1,
pad_top: -2.7,
pad_bottom: 0.1,
),
"lightbar_corner_left": Led(
width: 0.4,
height: 0.4,
pad_left: -0.5,
pad_right: 0.1,
pad_top: 0.5,
pad_bottom: 0.1,
),
"lightbar_bottom": Led(
width: 10.1,
height: 0.4,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.5,
pad_bottom: 0.1,
),
"lightbar_corner_right": Led(
width: 0.4,
height: 0.4,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.5,
pad_bottom: 0.1,
),
"lightbar_right": Led(
width: 0.4,
height: 3.0,
pad_left: -0.5,
pad_right: 0.1,
pad_top: -2.7,
pad_bottom: 0.1,
),
},
key_rows: [
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Blocking, "rog_row_blocking"),
(Blocking, "rog_row_blocking"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogFan, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Esc, "regular"),
(Spacing, "regular_spacing"),
(F1, "regular"),
(F2, "regular"),
(F3, "regular"),
(F4, "regular"),
(Spacing, "func_space"),
(F5, "regular"),
(F6, "regular"),
(F7, "regular"),
(F8, "regular"),
(Spacing, "func_space"),
(F9, "regular"),
(F10, "regular"),
(F11, "regular"),
(F12, "regular"),
(Spacing, "row_end_spacing"),
(Del, "regular"), // Should be super/insert
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(Spacing, "row_end_spacing"),
(MediaPlay, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "backslash"),
(Spacing, "row_end_spacing"),
(MediaStop, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(Spacing, "row_end_spacing"),
(MediaNext, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
(Spacing, "row_end_spacing"),
(MediaPrev, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular"),
(Meta, "regular"),
(LAlt, "regular"),
(Spacebar, "spacebar"),
(RAlt, "regular"),
(PrtSc, "regular"),
(RCtrl, "rctrl"),
(Up, "up_arrow"),
(Spacing, "row_end_spacing"),
(PrtSc, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LightbarLeft, "lightbar_left"),
(LightbarLeftCorner, "lightbar_corner_left"),
(LightbarLeftBottom, "lightbar_bottom"),
(LightbarRightBottom, "lightbar_bottom"),
(LightbarRightCorner, "lightbar_corner_right"),
(LightbarRight, "lightbar_right"),
],
),
],
)

View File

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

View File

@@ -0,0 +1,293 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular_spacing": Blank(
width: 1.2,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.6,
),
"rog_row_blocking": Blank(
width: 1.2,
height: 0.0,
),
"func_space": Blank(
width: 0.6,
height: 0.0,
),
"backspace": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backslash": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 1.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrow_space": Blank(
width: 0.8,
height: 0.0,
),
},
key_rows: [
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Blocking, "rog_row_blocking"),
(Blocking, "rog_row_blocking"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogFan, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Esc, "regular"),
(Spacing, "regular_spacing"),
(F1, "regular"),
(F2, "regular"),
(F3, "regular"),
(F4, "regular"),
(Spacing, "func_space"),
(F5, "regular"),
(F6, "regular"),
(F7, "regular"),
(F8, "regular"),
(Spacing, "func_space"),
(F9, "regular"),
(F10, "regular"),
(F11, "regular"),
(F12, "regular"),
(Del, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(MediaPlay, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "backslash"),
(MediaStop, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(MediaPrev, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
(Up, "up_arrow"),
(MediaNext, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular"),
(Meta, "regular"),
(LAlt, "regular"),
(Spacebar, "spacebar"),
(RAlt, "regular"),
(PrtSc, "regular"),
(RCtrl, "rctrl"),
(Spacing, "arrow_space"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
],
)

View File

@@ -0,0 +1,294 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular_spacing": Blank(
width: 1.2,
height: 0.0,
),
"rog_row_blocking": Blank(
width: 1.2,
height: 0.0,
),
"func_space": Blank(
width: 0.6,
height: 0.0,
),
"backspace": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backslash": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 3.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 1.0,
height: 1.0,
pad_left: 1.0,
pad_right: 1.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows_spacer": Blank(
width: 14.7,
height: 0.0,
),
"arrows": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
},
key_rows: [
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Blocking, "rog_row_blocking"),
(Blocking, "rog_row_blocking"),
(VolDown, "regular"),
(VolUp, "regular"),
(MicMute, "regular"),
(RogFan, "regular"),
(RogApp, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Esc, "regular"),
(Spacing, "regular_spacing"),
(F1, "regular"),
(F2, "regular"),
(F3, "regular"),
(F4, "regular"),
(Spacing, "func_space"),
(F5, "regular"),
(F6, "regular"),
(F7, "regular"),
(F8, "regular"),
(Spacing, "func_space"),
(F9, "regular"),
(F10, "regular"),
(F11, "regular"),
(F12, "regular"),
(Del, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(Home, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "backslash"),
(PgUp, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(PgDn, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
(End, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular"),
(Meta, "regular"),
(LAlt, "regular"),
(Spacebar, "spacebar"),
(RAlt, "regular"),
(PrtSc, "regular"),
(RCtrl, "rctrl"),
(Up, "up_arrow"),
(RFn, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
],
)

View File

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

View File

@@ -0,0 +1,299 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular2": Led(
width: 1.0,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rog_spacer": Blank(
width: 2.5,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.2,
pad_bottom: 0.5,
),
"func_key": Led(
width: 1.0,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.0,
pad_bottom: 0.1,
),
"func_space": Blank(
width: 0.2,
height: 0.0,
),
"esc_func_spacing": Blank(
width: 0.6,
height: 0.0,
),
"backspace": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"bkslash": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift": Led(
width: 3.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.8,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rctrl": Led(
width: 1.2,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 1.0,
height: 0.6,
pad_left: 1.6,
pad_right: 1.6,
pad_top: 0.1,
pad_bottom: 0.0,
),
"arrows_spacer": Blank(
width: 14.1,
height: 0.0,
),
"arrows": Led(
width: 1.0,
height: 0.6,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.7,
pad_bottom: 0.1,
),
},
key_rows: [
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Spacing, "rog_spacer"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Esc, "func_key"),
(Spacing, "esc_func_spacing"),
(F1, "func_key"),
(F2, "func_key"),
(F3, "func_key"),
(F4, "func_key"),
(Spacing, "func_space"),
(F5, "func_key"),
(F6, "func_key"),
(F7, "func_key"),
(F8, "func_key"),
(Spacing, "func_space"),
(F9, "func_key"),
(F10, "func_key"),
(F11, "func_key"),
(F12, "func_key"),
(Spacing, "func_space"),
(Del, "func_key"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "bkslash"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "rshift"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular2"),
(Meta, "regular2"),
(LAlt, "regular2"),
(Spacebar, "spacebar"),
(RAlt, "regular2"),
(RCtrl, "rctrl"),
(Up, "up_arrow"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
],
)

View File

@@ -0,0 +1,305 @@
(
locale: "US",
key_shapes: {
"rog_row_spacing": Blank(
width: 1.2,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.2,
pad_bottom: 0.6,
),
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"regular2": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.2,
pad_bottom: 0.1,
),
"func_key": Led(
width: 1.0,
height: 0.7,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.0,
pad_bottom: 0.1,
),
"func_space": Blank(
width: 0.5,
height: 0.0,
),
"esc_func_spacing": Blank(
width: 1.2,
height: 0.0,
),
"backspace": Led(
width: 2.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"bkslash": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 1.9,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return": Led(
width: 2.3,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.5,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lctrl": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 5.7,
height: 1.3,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows_spacer": Blank(
width: 16.6,
height: 0.0,
),
"numpad_tall": Led(
width: 1.0,
height: 2.2,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: -1.2,
),
"right_wide": Led(
width: 2.9,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
},
key_rows: [
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Blocking, "rog_row_spacing"),
(Blocking, "rog_row_spacing"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Esc, "func_key"),
(Spacing, "esc_func_spacing"),
(F1, "func_key"),
(F2, "func_key"),
(F3, "func_key"),
(F4, "func_key"),
(Spacing, "func_space"),
(F5, "func_key"),
(F6, "func_key"),
(F7, "func_key"),
(F8, "func_key"),
(Spacing, "func_space"),
(F9, "func_key"),
(F10, "func_key"),
(F11, "func_key"),
(F12, "func_key"),
(Del, "func_key"),
(NumPadPause, "func_key"),
(NumPadPrtSc, "func_key"),
(NumPadHome, "func_key"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tilde, "regular"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace, "backspace"),
(NumLock, "regular"),
(FwdSlash, "regular"),
(Star, "regular"),
(Hyphen, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "bkslash"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(NumPadPlus, "numpad_tall"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return, "return"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift, "right_wide"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(NumPadEnter, "numpad_tall"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(LCtrl, "lctrl"),
(LFn, "regular"),
(Meta, "regular"),
(LAlt, "regular"),
(Spacebar, "spacebar"),
(RAlt, "regular"),
(RAlt, "regular"),
(RAlt, "regular"),
(RCtrl, "right_wide"),
(Up, "regular"),
(N0, "regular"),
(NumPadDel, "regular"),
],
),
(
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
row: [
(Spacing, "arrows_spacer"),
(Left, "regular2"),
(Down, "regular2"),
(Right, "regular2"),
],
),
],
)

View File

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

View File

@@ -0,0 +1,382 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"bottom_row": Led(
width: 1.0,
height: 1.2,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rog_row_spacing": Blank(
width: 0.9,
height: 0.0,
),
"rog_row": Led(
width: 1.0,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.2,
pad_bottom: 0.4,
),
"func_key": Led(
width: 1.0,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.0,
pad_bottom: 0.4,
),
"func_space": Blank(
width: 0.7,
height: 0.0,
),
"esc_func_spacing": Blank(
width: 0.6,
height: 0.0,
),
"end_space": Blank(
width: 0.4,
height: 0.0,
),
"ctrl_bkslash": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tilde": Led(
width: 0.7,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
// The backspace button is composed of 3 individual LED
"backspace1": Led(
width: 0.7,
height: 1.0,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backspace2": Led(
width: 0.7,
height: 1.0,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backspace3": Led(
width: 0.7,
height: 1.0,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return1": Led(
width: 0.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return2": Led(
width: 0.8,
height: 1.0,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return3": Led(
width: 0.8,
height: 1.0,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift1": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift2": Led(
width: 1.0,
height: 1.0,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift3": Led(
width: 1.0,
height: 1.0,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar_left": Led(
width: 1.1,
height: 1.4,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar_mid": Led(
width: 1.2,
height: 1.4,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar_right": Led(
width: 1.1,
height: 1.4,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 0.8,
height: 0.8,
pad_left: 1.2,
pad_right: 1.2,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows": Led(
width: 0.8,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.5,
pad_bottom: 0.1,
),
"arrow_row_blocking": Blank(
width: 1.115,
height: 0.0,
),
},
key_rows: [
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Blocking, "rog_row_spacing"),
(Blocking, "rog_row_spacing"),
(VolDown, "rog_row"),
(VolUp, "rog_row"),
(MicMute, "rog_row"),
(RogApp, "rog_row"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Esc, "func_key"),
(Spacing, "esc_func_spacing"),
(F1, "func_key"),
(F2, "func_key"),
(F3, "func_key"),
(F4, "func_key"),
(Spacing, "func_space"),
(F5, "func_key"),
(F6, "func_key"),
(F7, "func_key"),
(F8, "func_key"),
(Spacing, "func_space"),
(F9, "func_key"),
(F10, "func_key"),
(F11, "func_key"),
(F12, "func_key"),
(Spacing, "end_space"),
(Del, "func_key"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Tilde, "tilde"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace3_1, "backspace1"),
(Backspace3_2, "backspace2"),
(Backspace3_3, "backspace3"),
(Spacing, "end_space"),
(Home, "regular"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "ctrl_bkslash"),
(Spacing, "end_space"),
(PgUp, "regular"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return3_1, "return1"),
(Return3_2, "return2"),
(Return3_3, "return3"),
(Spacing, "end_space"),
(PgDn, "regular"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift3_1, "rshift1"),
(Rshift3_2, "rshift2"),
(Rshift3_3, "rshift3"),
(Spacing, "end_space"),
(End, "regular"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(LCtrl, "bottom_row"),
(LFn, "bottom_row"),
(Meta, "bottom_row"),
(LAlt, "bottom_row"),
(Spacebar5_1, "spacebar_left"),
(Spacebar5_2, "spacebar_mid"),
(Spacebar5_3, "spacebar_mid"),
(Spacebar5_4, "spacebar_mid"),
(Spacebar5_5, "spacebar_right"),
(RAlt, "bottom_row"),
(PrtSc, "bottom_row"),
(RCtrl, "bottom_row"),
(Up, "up_arrow"),
(Spacing, "end_space"),
(RFn, "bottom_row"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
],
)

View File

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

View File

@@ -0,0 +1,346 @@
(
locale: "US",
key_shapes: {
"regular": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"bottom_row": Led(
width: 1.0,
height: 1.2,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"func_key": Led(
width: 1.0,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.0,
pad_bottom: 0.4,
),
"func_space": Blank(
width: 0.7,
height: 0.0,
),
"esc_func_spacing": Blank(
width: 0.6,
height: 0.0,
),
"ctrl_bkslash": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tilde": Led(
width: 0.7,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"tab": Led(
width: 1.4,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"capsplonk": Led(
width: 1.6,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
// The backspace button is composed of 3 individual LED
"backspace1": Led(
width: 0.7,
height: 1.0,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backspace2": Led(
width: 0.7,
height: 1.0,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"backspace3": Led(
width: 0.7,
height: 1.0,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return1": Led(
width: 0.8,
height: 1.0,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return2": Led(
width: 0.8,
height: 1.0,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"return3": Led(
width: 0.8,
height: 1.0,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"lshift": Led(
width: 2.2,
height: 1.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift1": Led(
width: 1.0,
height: 1.0,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift2": Led(
width: 1.0,
height: 1.0,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"rshift3": Led(
width: 1.0,
height: 1.0,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar_left": Led(
width: 1.1,
height: 1.4,
pad_left: 0.1,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar_mid": Led(
width: 1.2,
height: 1.4,
pad_left: 0.0,
pad_right: 0.0,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar_right": Led(
width: 1.1,
height: 1.4,
pad_left: 0.0,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"up_arrow": Led(
width: 0.8,
height: 0.8,
pad_left: 1.2,
pad_right: 1.2,
pad_top: 0.1,
pad_bottom: 0.1,
),
"arrows": Led(
width: 0.8,
height: 0.8,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.5,
pad_bottom: 0.1,
),
"arrow_row_blocking": Blank(
width: 1.115,
height: 0.0,
),
},
key_rows: [
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Esc, "func_key"),
(Spacing, "esc_func_spacing"),
(F1, "func_key"),
(F2, "func_key"),
(F3, "func_key"),
(F4, "func_key"),
(Spacing, "func_space"),
(F5, "func_key"),
(F6, "func_key"),
(F7, "func_key"),
(F8, "func_key"),
(Spacing, "func_space"),
(F9, "func_key"),
(F10, "func_key"),
(F11, "func_key"),
(F12, "func_key"),
(RogApp, "func_key"),
(RogApp, "func_key"),
(Pause, "func_key"),
(PgDn, "func_key"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Tilde, "tilde"),
(N1, "regular"),
(N2, "regular"),
(N3, "regular"),
(N4, "regular"),
(N5, "regular"),
(N6, "regular"),
(N7, "regular"),
(N8, "regular"),
(N9, "regular"),
(N0, "regular"),
(Hyphen, "regular"),
(Equals, "regular"),
(Backspace3_1, "backspace1"),
(Backspace3_2, "backspace2"),
(Backspace3_3, "backspace3"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Tab, "tab"),
(Q, "regular"),
(W, "regular"),
(E, "regular"),
(R, "regular"),
(T, "regular"),
(Y, "regular"),
(U, "regular"),
(I, "regular"),
(O, "regular"),
(P, "regular"),
(LBracket, "regular"),
(RBracket, "regular"),
(BackSlash, "ctrl_bkslash"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Caps, "capsplonk"),
(A, "regular"),
(S, "regular"),
(D, "regular"),
(F, "regular"),
(G, "regular"),
(H, "regular"),
(J, "regular"),
(K, "regular"),
(L, "regular"),
(SemiColon, "regular"),
(Quote, "regular"),
(Return3_1, "return1"),
(Return3_2, "return2"),
(Return3_3, "return3"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(LShift, "lshift"),
(Z, "regular"),
(X, "regular"),
(C, "regular"),
(V, "regular"),
(B, "regular"),
(N, "regular"),
(M, "regular"),
(Comma, "regular"),
(Period, "regular"),
(FwdSlash, "regular"),
(Rshift3_1, "rshift1"),
(Rshift3_2, "rshift2"),
(Rshift3_3, "rshift3"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(LCtrl, "bottom_row"),
(LFn, "bottom_row"),
(Meta, "bottom_row"),
(LAlt, "bottom_row"),
(Spacebar5_1, "spacebar_left"),
(Spacebar5_2, "spacebar_mid"),
(Spacebar5_3, "spacebar_mid"),
(Spacebar5_4, "spacebar_mid"),
(Spacebar5_5, "spacebar_right"),
(RAlt, "bottom_row"),
(PrtSc, "bottom_row"),
(RCtrl, "bottom_row"),
(Up, "up_arrow"),
],
),
(
pad_left: 0.1,
pad_top: 0.1,
row: [
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Blocking, "arrow_row_blocking"),
(Left, "arrows"),
(Down, "arrows"),
(Right, "arrows"),
],
),
],
)

Binary file not shown.

577
rog-aura/src/advanced.rs Normal file
View File

@@ -0,0 +1,577 @@
use log::warn;
use serde::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
/// The `LedCode` used in setting up keyboard layouts is important because it
/// determines the idexing for an RGB value in the final USB packets (for
/// per-key addressable keyboards).
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum LedCode {
VolUp,
VolDown,
MicMute,
#[default]
RogApp,
RogFan,
Esc,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Del,
Tilde,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Hyphen,
Equals,
Backspace,
/// For keyboards where the backspace button has 3 LED
Backspace3_1,
Backspace3_2,
Backspace3_3,
Home,
Tab,
Q,
W,
E,
R,
T,
Y,
U,
I,
O,
P,
LBracket,
RBracket,
BackSlash,
PgUp,
Caps,
A,
S,
D,
F,
G,
H,
J,
K,
L,
SemiColon,
Quote,
Return,
/// For keyboards where the return button has 3 LED
Return3_1,
Return3_2,
Return3_3,
PgDn,
LShift,
/// For keyboards where the left shift button has 3 LED
LShift3_1,
LShift3_2,
LShift3_3,
Z,
X,
C,
V,
B,
N,
M,
Comma,
Period,
FwdSlash,
Star,
NumPadDel,
NumPadPlus,
NumPadEnter,
NumPadPause,
NumPadPrtSc,
NumPadHome,
NumLock,
Rshift,
Rshift3_1,
Rshift3_2,
Rshift3_3,
End,
LCtrl,
LFn,
Meta,
LAlt,
Spacebar,
/// For keyboards where the spacebar button has 5 LED
Spacebar5_1,
Spacebar5_2,
Spacebar5_3,
Spacebar5_4,
Spacebar5_5,
Pause,
RAlt,
PrtSc,
RCtrl,
Up,
Down,
Left,
Right,
RFn,
MediaPlay,
MediaStop,
MediaNext,
MediaPrev,
LidLogo,
LidLeft,
LidRight,
/// Used by per-key and multizoned
LightbarRight,
/// Used by per-key and multizoned
LightbarRightCorner,
/// Used by per-key and multizoned
LightbarRightBottom,
/// Used by per-key and multizoned
LightbarLeftBottom,
/// Used by per-key and multizoned
LightbarLeftCorner,
/// Used by per-key and multizoned
LightbarLeft,
/// Use if the keyboard supports only a single zone. This zone uses the same
/// packet data as the `Zoned*` below
SingleZone,
/// Use if the keyboard supports 4 zones, this is the left zone
ZonedKbLeft,
/// Use if the keyboard supports 4 zones, this is the left-center zone
ZonedKbLeftMid,
/// Use if the keyboard supports 4 zones, this is the right-center zone
ZonedKbRightMid,
/// Use if the keyboard supports 4 zones, this is the right zone
ZonedKbRight,
/// To be ignored by effects
Spacing,
/// To be ignored by effects
Blocking,
}
impl LedCode {
pub fn is_placeholder(&self) -> bool {
matches!(self, Self::Spacing | Self::Blocking)
}
pub fn is_keyboard_zone(&self) -> bool {
matches!(
self,
Self::ZonedKbLeft | Self::ZonedKbLeftMid | Self::ZonedKbRightMid | Self::ZonedKbRight
)
}
pub fn is_lightbar_zone(&self) -> bool {
matches!(
self,
Self::LightbarLeft
| Self::LightbarLeftCorner
| Self::LightbarLeftBottom
| Self::LightbarRightBottom
| Self::LightbarRightCorner
| Self::LightbarRight
)
}
}
/// Represents the per-key raw USB packets
pub type UsbPackets = Vec<Vec<u8>>;
/// A `UsbPackets` contains all data to change the full set of keyboard
/// key colours individually.
///
/// Each row of the internal array is a full HID packet that can be sent
/// 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.
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LedUsbPackets {
/// The packet data used to send data to the USB keyboard
usb_packets: UsbPackets,
/// Wether or not this packet collection is zoned. The determines which
/// starting bytes are used and what the indexing is for lightbar RGB
/// colours
zoned: bool,
}
impl Default for LedUsbPackets {
fn default() -> Self {
Self::new_per_key()
}
}
impl LedUsbPackets {
/// Set up a series of per-key packets. This includes setting all the
/// required starting bytes per packet, but does not set any colours.
///
/// These packets will not work with per-zone keyboards
pub fn new_per_key() -> Self {
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
row[2] = 0x00;
row[3] = 0x01; // ??
row[4] = 0x01; // ??, 4,5,6 are normally RGB for builtin mode colours
row[5] = 0x01; // ??
row[6] = (count as u8) << 4; // Key group
if count == 10 {
row[7] = 0x08; // 0b00001000
} else {
row[7] = 0x10; // 0b00010000 addressing? flips for group a0
}
row[8] = 0x00;
}
Self {
usb_packets: set,
zoned: false,
}
}
/// Create new zoned packets. Although the result is a nested `Vec` only the
/// first vector is available. The final packet is slightly different
/// for single-zoned compared to multizoned.
///
/// This packet will not work with per-key keyboards
///
/// Wireshark captures show:
/// ```ignore
/// 5d,bc,01,01,00,00,00,00,00,ff,00,00, RED, single zone
/// 5d,bc,01,01,04,00,00,00,00,ff,00,00, RED, multizone
/// ```
pub fn new_zoned(multizoned: bool) -> 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; // ??
if !multizoned {
pkt[4] = 0x00; // This doesn't actually seem to matter on this
// keyboard?
} else {
pkt[4] = 0x04; // ??, 4,5,6 are normally RGB for builtin mode
// colours
}
Self {
usb_packets: vec![pkt],
zoned: true,
}
}
/// Initialise and clear the keyboard for custom effects, this must be done
/// for every time mode switches from builtin to custom
#[inline]
pub const fn get_init_msg() -> [u8; 64] {
let mut init = [0u8; 64];
init[0] = 0x5d; // Report ID
init[1] = 0xbc; // Mode = custom??, 0xb3 is builtin
init
}
/// Set the RGB colour of an `LedCode`
#[inline]
pub fn set(&mut self, key: LedCode, r: u8, g: u8, b: u8) {
if let Some(c) = self.rgb_for_led_code(key) {
c[0] = r;
c[1] = g;
c[2] = b;
}
}
/// Indexes in to `UsbPackets` at the correct row and column
/// to set a series of three bytes to the chosen R,G,B values
///
/// Indexing is different for `zoned` and assumes that only one packet is
/// generated for all the zones
fn rgb_for_led_code(&mut self, led_code: LedCode) -> Option<&mut [u8]> {
let zoned = self.zoned;
// Tuples are indexes in to array
#[allow(clippy::match_same_arms)]
let (row, col) = match led_code {
LedCode::VolDown => (0, 15),
LedCode::VolUp => (0, 18),
LedCode::MicMute => (0, 21),
LedCode::RogApp => (0, 24),
//
LedCode::Esc => (1, 24),
LedCode::F1 => (1, 30),
LedCode::F2 => (1, 33),
LedCode::F3 => (1, 36),
LedCode::F4 => (1, 39),
LedCode::F5 => (1, 45),
LedCode::F6 => (1, 48),
LedCode::F7 => (1, 51),
LedCode::F8 => (1, 54),
//
LedCode::F9 => (2, 12),
LedCode::F10 => (2, 15),
LedCode::F11 => (2, 18),
LedCode::F12 => (2, 21),
LedCode::Del => (2, 24),
LedCode::Tilde => (2, 39),
LedCode::N1 => (2, 42),
LedCode::N2 => (2, 45),
LedCode::N3 => (2, 48),
LedCode::N4 => (2, 51),
LedCode::N5 => (2, 54),
//
LedCode::N6 => (3, 9),
LedCode::N7 => (3, 12),
LedCode::N8 => (3, 15),
LedCode::N9 => (3, 18),
LedCode::N0 => (3, 21),
LedCode::Hyphen => (3, 24),
LedCode::Equals => (3, 27),
LedCode::Backspace3_1 => (3, 30),
LedCode::Backspace3_2 => (3, 33),
LedCode::Backspace3_3 => (3, 36),
LedCode::Home => (3, 39),
LedCode::Tab => (3, 54),
//
LedCode::Q => (4, 9),
LedCode::W => (4, 12),
LedCode::E => (4, 15),
LedCode::R => (4, 18),
LedCode::T => (4, 21),
LedCode::Y => (4, 24),
LedCode::U => (4, 27),
LedCode::I => (4, 30),
LedCode::O => (4, 33),
LedCode::P => (4, 36),
LedCode::LBracket => (4, 39),
LedCode::RBracket => (4, 42),
LedCode::BackSlash => (4, 45),
LedCode::PgUp => (4, 54),
//
LedCode::Caps => (5, 21),
LedCode::A => (5, 24),
LedCode::S => (5, 27),
LedCode::D => (5, 30),
LedCode::F => (5, 33),
LedCode::G => (5, 36),
LedCode::H => (5, 39),
LedCode::J => (5, 42),
LedCode::K => (5, 45),
LedCode::L => (5, 48),
LedCode::SemiColon => (5, 51),
LedCode::Quote => (5, 54),
//
LedCode::Return => (6, 9),
LedCode::Return3_1 => (6, 12),
LedCode::Return3_2 => (6, 15),
LedCode::Return3_3 => (6, 18),
LedCode::PgDn => (6, 21),
LedCode::LShift => (6, 36),
// TODO: Find correct locations
LedCode::LShift3_1 => (6, 36),
LedCode::LShift3_2 => (6, 36),
LedCode::LShift3_3 => (6, 36),
LedCode::Z => (6, 42),
LedCode::X => (6, 45),
LedCode::C => (6, 48),
LedCode::V => (6, 51),
LedCode::B => (6, 54),
//
LedCode::N => (7, 9),
LedCode::M => (7, 12),
LedCode::Comma => (7, 15),
LedCode::Period => (7, 18),
LedCode::FwdSlash => (7, 21),
LedCode::Rshift => (7, 24),
LedCode::Rshift3_1 => (7, 27),
LedCode::Rshift3_2 => (7, 30),
LedCode::Rshift3_3 => (7, 33),
LedCode::End => (7, 36),
LedCode::LCtrl => (7, 51),
LedCode::LFn => (7, 54),
//
LedCode::Meta => (8, 9),
LedCode::LAlt => (8, 12),
LedCode::Spacebar5_1 => (8, 15),
LedCode::Spacebar5_2 => (8, 18),
LedCode::Spacebar5_3 => (8, 21),
LedCode::Spacebar5_4 => (8, 24),
LedCode::Spacebar5_5 => (8, 27),
LedCode::RAlt => (8, 30),
LedCode::PrtSc => (8, 33),
LedCode::RCtrl => (8, 36),
LedCode::Up => (8, 42),
LedCode::RFn => (8, 51),
//
LedCode::Left => (9, 54),
//
LedCode::Down => (10, 9),
LedCode::Right => (10, 12),
LedCode::LidLogo => (11, 9),
LedCode::LidLeft => (11, 36),
LedCode::LidRight => (11, 39),
//
LedCode::SingleZone | LedCode::ZonedKbLeft => (0, 9),
LedCode::ZonedKbLeftMid => (0, 12),
LedCode::ZonedKbRightMid => (0, 15),
LedCode::ZonedKbRight => (0, 18),
LedCode::LightbarRight => if zoned {(0, 27)} else { (11, 15)},
LedCode::LightbarRightCorner => if zoned {(0, 30)} else {(11, 18)},
LedCode::LightbarRightBottom => if zoned {(0, 33)} else{(11, 21)},
LedCode::LightbarLeftBottom => if zoned {(0, 36)} else{(11, 24)},
LedCode::LightbarLeftCorner => if zoned {(0, 39)} else{(11, 27)},
LedCode::LightbarLeft => if zoned {(0, 42)} else{(11, 30)},
//
LedCode::Spacing
| LedCode::Blocking
// TODO: the addressing of the following
| LedCode::MediaPlay
| LedCode::MediaStop
| LedCode::MediaPrev
| LedCode::MediaNext
| LedCode::Pause
| LedCode::NumLock
| LedCode::Star
| LedCode::NumPadDel
| LedCode::NumPadPlus
| LedCode::NumPadEnter
| LedCode::NumPadPause
| LedCode::NumPadPrtSc
| LedCode::NumPadHome
| LedCode::RogFan
| LedCode::Spacebar
| LedCode::Backspace => return None,
};
if self.zoned && row > 0 {
warn!(
"LedCode {led_code:?} for zoned is not correct or out of Zone range. Setting to 0",
);
return None;
}
Some(&mut self.usb_packets[row][col..=col + 2])
}
#[inline]
pub fn get(&self) -> UsbPackets {
self.usb_packets.clone()
}
#[inline]
pub fn get_ref(&self) -> &UsbPackets {
&self.usb_packets
}
#[inline]
pub fn get_mut(&mut self) -> &mut UsbPackets {
&mut self.usb_packets
}
}
impl From<LedUsbPackets> for UsbPackets {
fn from(k: LedUsbPackets) -> Self {
k.usb_packets
}
}
#[cfg(test)]
mod tests {
use crate::advanced::{LedCode, LedUsbPackets, UsbPackets};
macro_rules! colour_check_zoned {
($zone:expr, $pkt_idx_start:expr) => {
let mut zone = LedUsbPackets::new_zoned(true);
let c = zone.rgb_for_led_code($zone).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let pkt: UsbPackets = zone.into();
assert_eq!(pkt[0][$pkt_idx_start], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 1], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 2], 0xff);
};
}
#[test]
fn zone_to_packet_check() {
let zone = LedUsbPackets::new_zoned(true);
let pkt: UsbPackets = zone.into();
assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x01);
assert_eq!(pkt[0][3], 0x01);
assert_eq!(pkt[0][4], 0x04);
colour_check_zoned!(LedCode::ZonedKbLeft, 9);
colour_check_zoned!(LedCode::ZonedKbLeftMid, 12);
colour_check_zoned!(LedCode::ZonedKbRightMid, 15);
colour_check_zoned!(LedCode::ZonedKbRight, 18);
colour_check_zoned!(LedCode::LightbarRight, 27);
colour_check_zoned!(LedCode::LightbarRightCorner, 30);
colour_check_zoned!(LedCode::LightbarRightBottom, 33);
colour_check_zoned!(LedCode::LightbarLeftBottom, 36);
colour_check_zoned!(LedCode::LightbarLeftCorner, 39);
colour_check_zoned!(LedCode::LightbarLeft, 42);
}
#[test]
fn perkey_to_packet_check() {
let per_key = LedUsbPackets::new_per_key();
let pkt: UsbPackets = per_key.into();
assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x00);
assert_eq!(pkt[0][3], 0x01);
assert_eq!(pkt[0][4], 0x01);
assert_eq!(pkt[0][5], 0x01);
let mut per_key = LedUsbPackets::new_per_key();
let c = per_key.rgb_for_led_code(LedCode::D).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let c = per_key.rgb_for_led_code(LedCode::O).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let c = per_key.rgb_for_led_code(LedCode::N0).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let c = per_key.rgb_for_led_code(LedCode::M).unwrap();
c[0] = 255;
c[1] = 255;
c[2] = 255;
let pkt: UsbPackets = per_key.into();
assert_eq!(pkt[5][30], 0xff); // D, red
assert_eq!(pkt[5][31], 0xff); // D
assert_eq!(pkt[5][32], 0xff); // D
assert_eq!(pkt[5][33], 0x00); // D
assert_eq!(pkt[4][33], 0xff); // O, red
assert_eq!(pkt[4][34], 0xff); // O
assert_eq!(pkt[4][35], 0xff); // O
assert_eq!(pkt[4][36], 0x00); // O
assert_eq!(pkt[7][12], 0xff); // M, red
assert_eq!(pkt[7][13], 0xff); // M
assert_eq!(pkt[7][14], 0xff); // M
assert_eq!(pkt[7][15], 0x00); // M
}
}

View File

@@ -0,0 +1,149 @@
use crate::advanced::LedCode;
impl From<LedCode> for &str {
fn from(k: LedCode) -> Self {
(&k).into()
}
}
impl From<&LedCode> for &str {
fn from(k: &LedCode) -> Self {
#[allow(clippy::match_same_arms)]
match k {
LedCode::VolUp => "Volume Up",
LedCode::VolDown => "Volume Down",
LedCode::MicMute => "Mute Mic",
LedCode::RogApp => "ROG",
LedCode::RogFan => "Fan Control",
LedCode::Esc => "Escape",
LedCode::F1 => "F1",
LedCode::F2 => "F2",
LedCode::F3 => "F3",
LedCode::F4 => "F4",
LedCode::F5 => "F5",
LedCode::F6 => "F6",
LedCode::F7 => "F7",
LedCode::F8 => "F8",
LedCode::F9 => "F9",
LedCode::F10 => "F10",
LedCode::F11 => "F11",
LedCode::F12 => "F12",
LedCode::Del => "Delete",
LedCode::Tilde => "Tilde",
LedCode::N1 => "1",
LedCode::N2 => "2",
LedCode::N3 => "3",
LedCode::N4 => "4",
LedCode::N5 => "5",
LedCode::N6 => "6",
LedCode::N7 => "7",
LedCode::N8 => "8",
LedCode::N9 => "9",
LedCode::N0 => "0",
LedCode::Hyphen => "-",
LedCode::Equals => "=",
LedCode::Backspace => "Backspace",
LedCode::Backspace3_1 => "Backspace LED 1",
LedCode::Backspace3_2 => "Backspace LED 2",
LedCode::Backspace3_3 => "Backspace LED 3",
LedCode::Home => "Home",
LedCode::Tab => "Tab",
LedCode::Q => "Q",
LedCode::W => "W",
LedCode::E => "E",
LedCode::R => "R",
LedCode::T => "T",
LedCode::Y => "Y",
LedCode::U => "U",
LedCode::I => "I",
LedCode::O => "O",
LedCode::P => "P",
LedCode::LBracket => "[",
LedCode::RBracket => "]",
LedCode::BackSlash => "\\",
LedCode::PgUp => "Page Up",
LedCode::Caps => "Caps Lock",
LedCode::A => "A",
LedCode::S => "S",
LedCode::D => "D",
LedCode::F => "F",
LedCode::G => "G",
LedCode::H => "H",
LedCode::J => "J",
LedCode::K => "K",
LedCode::L => "L",
LedCode::SemiColon => ";",
LedCode::Quote => "'",
LedCode::Return => "Return",
LedCode::Return3_1 => "Return LED 1",
LedCode::Return3_2 => "Return LED 2",
LedCode::Return3_3 => "Return LED 3",
LedCode::PgDn => "Page Down",
LedCode::LShift => "Left Shift",
LedCode::LShift3_1 => "Left Shift LED 1",
LedCode::LShift3_2 => "Left Shift LED 2",
LedCode::LShift3_3 => "Left Shift LED 3",
LedCode::Z => "Z",
LedCode::X => "X",
LedCode::C => "C",
LedCode::V => "V",
LedCode::B => "B",
LedCode::N => "N",
LedCode::M => "M",
LedCode::Comma => ",",
LedCode::Period => ".",
LedCode::Star => "*",
LedCode::NumPadDel => "Delete",
LedCode::NumPadPlus => "+",
LedCode::NumPadEnter => "Enter",
LedCode::NumPadPause => "Pause",
LedCode::NumPadPrtSc => "Print Screen",
LedCode::NumPadHome => "Home",
LedCode::NumLock => "Num-Lock",
LedCode::FwdSlash => "/",
LedCode::Rshift => "Right Shift",
LedCode::Rshift3_1 => "Right Shift LED 1",
LedCode::Rshift3_2 => "Right Shift LED 2",
LedCode::Rshift3_3 => "Right Shift LED 3",
LedCode::End => "End",
LedCode::LCtrl => "Left Control",
LedCode::LFn => "Left Fn",
LedCode::Meta => "Meta",
LedCode::LAlt => "Left Alt",
LedCode::Spacebar => "Space",
LedCode::Spacebar5_1 => "Space LED 1",
LedCode::Spacebar5_2 => "Space LED 2",
LedCode::Spacebar5_3 => "Space LED 3",
LedCode::Spacebar5_4 => "Space LED 4",
LedCode::Spacebar5_5 => "Space LED 5",
LedCode::RAlt => "Right Alt",
LedCode::PrtSc => "Print Screen",
LedCode::RCtrl => "Right Control",
LedCode::Pause => "Pause",
LedCode::Up => "Up",
LedCode::Down => "Down",
LedCode::Left => "Left",
LedCode::Right => "Right",
LedCode::RFn => "Right Fn",
LedCode::MediaPlay => "Media Play",
LedCode::MediaStop => "Media Stop",
LedCode::MediaNext => "Media Next",
LedCode::MediaPrev => "Media Previous",
LedCode::LidLogo => "Lid Logo",
LedCode::LidLeft => "Lid Left",
LedCode::LidRight => "Lid Right",
LedCode::LightbarRight => "Lightbar Right",
LedCode::LightbarRightCorner => "Lightbar Right Corner",
LedCode::LightbarRightBottom => "Lightbar Right Bottom",
LedCode::LightbarLeftBottom => "Lightbar Left Bottom",
LedCode::LightbarLeftCorner => "Lightbar Left Corner",
LedCode::LightbarLeft => "Lightbar Left",
LedCode::Spacing | LedCode::Blocking => "",
LedCode::SingleZone => "Single Zoned Keyboard",
LedCode::ZonedKbLeft => "Left Zone (zone 1)",
LedCode::ZonedKbLeftMid => "Center-left Zone (zone 2)",
LedCode::ZonedKbRightMid => "Center-right Zone (zone 3)",
LedCode::ZonedKbRight => "Right Zone (zone 4)",
}
}
}

View File

@@ -0,0 +1,187 @@
use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use crate::usb::AuraDevice;
use crate::{AdvancedAuraType, AuraModeNum, AuraZone};
pub const ASUS_LED_MODE_CONF: &str = "/usr/share/asusd/aura_support.ron";
pub const ASUS_LED_MODE_USER_CONF: &str = "/etc/asusd/asusd_user_ledmodes.ron";
pub const ASUS_KEYBOARD_DEVICES: [AuraDevice; 7] = [
AuraDevice::Tuf,
AuraDevice::X1854,
AuraDevice::X1869,
AuraDevice::X1866,
AuraDevice::X18c6,
AuraDevice::X19b6,
AuraDevice::X1a30,
];
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct LedSupportFile(Vec<LaptopLedData>);
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct LaptopLedData {
/// Found via `cat /sys/class/dmi/id/board_name`, e.g `GU603ZW`.
/// The match doesn't have to be the complete model number as it is
/// typically broken down such:
/// - GU = product
/// - 603 = model/platform
/// - Z = variant/year or perhaps dGPU model (such as RTX 3xxx)
/// - W = possibly dGPU model (such as RTX 3060Ti)
pub board_name: String,
pub layout_name: String,
pub basic_modes: Vec<AuraModeNum>,
pub basic_zones: Vec<AuraZone>,
pub advanced_type: AdvancedAuraType,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct LaptopLedData456 {
pub prod_family: String,
pub board_names: Vec<String>,
pub standard: Vec<AuraModeNum>,
pub multizone: Vec<AuraZone>,
pub per_key: bool,
}
impl LaptopLedData {
pub fn get_data() -> Self {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().expect("Could not get board_name");
// let prod_family = dmi.product_family().expect("Could not get
// product_family");
if let Some(modes) = LedSupportFile::load_from_config() {
if let Some(data) = modes.matcher(&board_name) {
return data;
}
}
info!("Using generic LED control for keyboard brightness only");
LaptopLedData::default()
}
}
impl LedSupportFile {
pub fn get(&self) -> &[LaptopLedData] {
&self.0
}
/// The list is stored in ordered format, so the iterator must be reversed
/// to ensure we match to *whole names* first before doing a glob match
pub fn matcher(self, board_name: &str) -> Option<LaptopLedData> {
for config in self.0.iter().rev() {
if board_name.contains(&config.board_name) {
info!("LedSupport: Matched to {}", config.board_name);
return Some(config.clone());
}
}
None
}
pub fn load_from_config() -> Option<Self> {
let mut loaded = false;
let mut data = LedSupportFile::default();
// Load user configs first so they are first to be checked
if let Ok(file) = std::fs::read_to_string(ASUS_LED_MODE_USER_CONF) {
if file.is_empty() {
warn!("{} is empty", ASUS_LED_MODE_USER_CONF);
} else {
if let Ok(mut tmp) = ron::from_str::<LedSupportFile>(&file) {
data.0.append(&mut tmp.0);
}
info!(
"Loaded user-defined LED support data from {}",
ASUS_LED_MODE_USER_CONF
);
}
}
// Load and append the default LED support data
if let Ok(file) = std::fs::read_to_string(ASUS_LED_MODE_CONF) {
if file.is_empty() {
warn!("{} is empty", ASUS_LED_MODE_CONF);
} else {
let mut tmp: LedSupportFile = ron::from_str(&file)
.map_err(|e| error!("{e}"))
.unwrap_or_else(|_| panic!("Could not deserialise {}", ASUS_LED_MODE_CONF));
data.0.append(&mut tmp.0);
loaded = true;
info!(
"Loaded default LED support data from {}",
ASUS_LED_MODE_CONF
);
}
}
data.0.sort_by(|a, b| a.board_name.cmp(&b.board_name));
if loaded {
return Some(data);
}
warn!("Does {} exist?", ASUS_LED_MODE_USER_CONF);
None
}
}
#[cfg(test)]
mod tests {
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
use ron::ser::PrettyConfig;
use super::LaptopLedData;
use crate::advanced::LedCode;
use crate::aura_detection::LedSupportFile;
// use crate::zoned::Zone;
use crate::{AdvancedAuraType, AuraModeNum, AuraZone};
#[test]
fn check_data_parse() {
let led = LaptopLedData {
board_name: "Test".to_owned(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![AuraZone::Key1, AuraZone::Logo, AuraZone::BarLeft],
advanced_type: AdvancedAuraType::Zoned(vec![LedCode::LightbarRight]),
};
assert!(ron::to_string(&led).is_ok());
// assert_eq!(json, String::new());
}
#[test]
fn check_data_file_parse() {
let mut data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
data.push("data/aura_support.ron");
let buf = std::fs::read_to_string(&data).unwrap();
let tmp = ron::from_str::<LedSupportFile>(&buf).unwrap();
// Ensure the data is sorted
let mut tmp_sort = tmp.clone();
tmp_sort.0.sort_by(|a, b| a.board_name.cmp(&b.board_name));
if tmp != tmp_sort {
let sorted =
ron::ser::to_string_pretty(&tmp_sort, PrettyConfig::new().depth_limit(2)).unwrap();
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&data)
.unwrap();
file.write_all(sorted.as_bytes()).unwrap();
panic!(
"aura_support.ron not sorted, should be {sorted}. File rewritten with correct \
order, run test again"
)
}
let my_config = PrettyConfig::new().depth_limit(2);
println!(
"RON: {}",
ron::ser::to_string_pretty(&tmp, my_config).unwrap()
);
}
}

View File

@@ -4,12 +4,15 @@ pub const LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
pub const LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e pub const LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e
pub const LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08]; pub const LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
use std::fmt::Display;
use std::str::FromStr;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::{fmt::Display, str::FromStr};
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zbus::zvariant::Type; use zbus::zvariant::Type;
use crate::{error::Error, LED_MSG_LEN}; use crate::error::Error;
use crate::LED_MSG_LEN;
#[cfg_attr(feature = "dbus", derive(Type))] #[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
@@ -25,7 +28,6 @@ impl From<u32> for LedBrightness {
match bright { match bright {
0 => LedBrightness::Off, 0 => LedBrightness::Off,
1 => LedBrightness::Low, 1 => LedBrightness::Low,
2 => LedBrightness::Med,
3 => LedBrightness::High, 3 => LedBrightness::High,
_ => LedBrightness::Med, _ => LedBrightness::Med,
} }
@@ -179,21 +181,7 @@ impl Display for AuraModeNum {
impl From<AuraModeNum> for String { impl From<AuraModeNum> for String {
fn from(mode: AuraModeNum) -> Self { fn from(mode: AuraModeNum) -> Self {
match mode { <&str>::from(&mode).to_owned()
AuraModeNum::Static => "Static",
AuraModeNum::Breathe => "Breathe",
AuraModeNum::Strobe => "Strobe",
AuraModeNum::Rainbow => "Rainbow",
AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Highlight",
AuraModeNum::Laser => "Laser",
AuraModeNum::Ripple => "Ripple",
AuraModeNum::Pulse => "Pulse",
AuraModeNum::Comet => "Comet",
AuraModeNum::Flash => "Flash",
}
.to_string()
} }
} }
@@ -218,7 +206,6 @@ impl From<&AuraModeNum> for &str {
impl From<&str> for AuraModeNum { impl From<&str> for AuraModeNum {
fn from(mode: &str) -> Self { fn from(mode: &str) -> Self {
match mode { match mode {
"Static" => AuraModeNum::Static,
"Breathe" => AuraModeNum::Breathe, "Breathe" => AuraModeNum::Breathe,
"Strobe" => AuraModeNum::Strobe, "Strobe" => AuraModeNum::Strobe,
"Rainbow" => AuraModeNum::Rainbow, "Rainbow" => AuraModeNum::Rainbow,
@@ -238,7 +225,6 @@ impl From<&str> for AuraModeNum {
impl From<u8> for AuraModeNum { impl From<u8> for AuraModeNum {
fn from(mode: u8) -> Self { fn from(mode: u8) -> Self {
match mode { match mode {
0 => AuraModeNum::Static,
1 => AuraModeNum::Breathe, 1 => AuraModeNum::Breathe,
2 => AuraModeNum::Strobe, 2 => AuraModeNum::Strobe,
3 => AuraModeNum::Rainbow, 3 => AuraModeNum::Rainbow,
@@ -284,28 +270,21 @@ impl FromStr for AuraZone {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase(); let s = s.to_lowercase();
match s.to_ascii_lowercase().as_str() { match s.to_ascii_lowercase().as_str() {
"0" => Ok(AuraZone::None), "0" | "none" => Ok(AuraZone::None),
"none" => Ok(AuraZone::None), "1" | "one" => Ok(AuraZone::Key1),
"1" => Ok(AuraZone::Key1), "2" | "two" => Ok(AuraZone::Key2),
"one" => Ok(AuraZone::Key1), "3" | "three" => Ok(AuraZone::Key3),
"2" => Ok(AuraZone::Key2), "4" | "four" => Ok(AuraZone::Key4),
"two" => Ok(AuraZone::Key2), "5" | "logo" => Ok(AuraZone::Logo),
"3" => Ok(AuraZone::Key3), "6" | "lightbar-left" => Ok(AuraZone::BarLeft),
"three" => Ok(AuraZone::Key3), "7" | "lightbar-right" => Ok(AuraZone::BarRight),
"4" => Ok(AuraZone::Key4),
"four" => Ok(AuraZone::Key4),
"5" => Ok(AuraZone::Logo),
"logo" => Ok(AuraZone::Logo),
"6" => Ok(AuraZone::BarLeft),
"lightbar-left" => Ok(AuraZone::BarLeft),
"7" => Ok(AuraZone::BarRight),
"lightbar-right" => Ok(AuraZone::BarRight),
_ => Err(Error::ParseSpeed), _ => Err(Error::ParseSpeed),
} }
} }
} }
/// Default factory modes structure. This easily converts to an USB HID packet with: /// Default factory modes structure. This easily converts to an USB HID packet
/// with:
/// ```rust /// ```rust
/// // let bytes: [u8; LED_MSG_LEN] = mode.into(); /// // let bytes: [u8; LED_MSG_LEN] = mode.into();
/// ``` /// ```
@@ -372,6 +351,7 @@ pub struct AuraParameters {
pub direction: bool, pub direction: bool,
} }
#[allow(clippy::fn_params_excessive_bools)]
impl AuraParameters { impl AuraParameters {
pub const fn new( pub const fn new(
zone: bool, zone: bool,
@@ -391,22 +371,24 @@ impl AuraParameters {
} }
impl AuraEffect { impl AuraEffect {
/// A helper to provide detail on what effects have which parameters, e.g the static /// A helper to provide detail on what effects have which parameters, e.g
/// factory mode accepts only one colour. /// the static factory mode accepts only one colour.
pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters { pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters {
match mode { match mode {
AuraModeNum::Static => AuraParameters::new(true, true, false, false, false), AuraModeNum::Static
| AuraModeNum::Highlight
| AuraModeNum::Pulse
| AuraModeNum::Comet
| AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false), AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
AuraModeNum::Strobe => AuraParameters::new(true, false, false, true, false), AuraModeNum::Strobe | AuraModeNum::Rain => {
AuraParameters::new(true, false, false, true, false)
}
AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true), AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true),
AuraModeNum::Star => AuraParameters::new(true, true, true, true, true), AuraModeNum::Star => AuraParameters::new(true, true, true, true, true),
AuraModeNum::Rain => AuraParameters::new(true, false, false, true, false), AuraModeNum::Laser | AuraModeNum::Ripple => {
AuraModeNum::Highlight => AuraParameters::new(true, true, false, false, false), AuraParameters::new(true, true, false, true, false)
AuraModeNum::Laser => AuraParameters::new(true, true, false, true, false), }
AuraModeNum::Ripple => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Pulse => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Comet => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
} }
} }
} }

View File

@@ -0,0 +1,33 @@
use super::{EffectState, InputForEffect};
use crate::advanced::LedCode;
use crate::Colour;
pub struct InputBased {
led: LedCode,
colour: Colour,
/// - audio
/// - cpu freq
/// - temperature
/// - fan speed
/// - time
input: Box<dyn InputForEffect>,
}
impl EffectState for InputBased {
fn next_colour_state(&mut self, _layout: &crate::layouts::KeyLayout) {
self.input.next_colour_state();
self.colour = self.input.get_colour();
}
fn get_colour(&self) -> Colour {
self.colour
}
fn get_led(&self) -> LedCode {
self.led
}
fn set_led(&mut self, address: LedCode) {
self.led = address;
}
}

View File

@@ -0,0 +1,82 @@
use serde::{Deserialize, Serialize};
use super::EffectState;
use crate::advanced::LedCode;
use crate::layouts::KeyLayout;
use crate::{effect_state_impl, Colour, Speed};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Breathe {
led: LedCode,
/// 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(address: LedCode, colour1: Colour, colour2: Colour, speed: Speed) -> Self {
Self {
led: address,
start_colour1: colour1,
start_colour2: colour2,
speed,
colour: colour1,
count_flipped: false,
use_colour1: true,
}
}
}
impl EffectState for Breathe {
effect_state_impl!();
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);
if *colour_actual == Colour(0, 0, 0) {
*use_colour1 = !*use_colour1;
}
let colour = if !*use_colour1 { colour2 } else { 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);
}
}
}

Some files were not shown because too many files have changed in this diff Show More