Compare commits

...

263 Commits

Author SHA1 Message Date
Luke D. Jones 1b023d0f5f Fix and prep new 6.0.2 release 2024-05-08 16:27:40 +12:00
Luke D. Jones 74f74e73c4 Update deps, point release 2024-05-07 13:56:25 +12:00
Luke D. Jones 9c7df9ad39 Update deps, point release 2024-05-07 12:58:17 +12:00
Luke D. Jones 94adf5d24d Revert back to zbus 4.1 2024-05-06 23:54:36 +12:00
Luke D. Jones 8dbdb68175 Increase tray icon sleep time 2024-05-06 23:14:51 +12:00
Luke D. Jones 89002eb5ec Use fluent-dark instead of cosmic due to compile times 2024-05-06 23:01:03 +12:00
Luke D. Jones dc9ef8cf54 Remove unused deps 2024-05-06 13:25:07 +12:00
Luke D. Jones 667697d042 Cleanup deps 2024-05-06 10:58:11 +12:00
Luke D. Jones bc92fa11f9 Update all cargo.toml. Support G713P 2024-05-06 09:45:35 +12:00
Luke D. Jones f5d5681b49 Prep changelog 2024-05-05 20:52:38 +12:00
Luke D. Jones 1c8e50843b Cleanup files, prep new release 2024-05-05 20:22:56 +12:00
Luke D. Jones 487d140bd5 Anime: Better prevent await blocking. Prevent Slash taking over anime USB dev 2024-05-05 10:56:55 +12:00
Luke D. Jones 661ea8d3bf Fix fedora install instruction 2024-04-19 11:11:58 +12:00
Luke D. Jones 28d1ed6ab3 Fix broke logix 2024-04-18 14:40:39 +12:00
Luke D. Jones 903b978e86 Add missing files 2024-04-18 13:55:02 +12:00
Luke D. Jones 519f6bd46b Cleanup notifs, sys state, ac/bat commands 2024-04-18 13:48:23 +12:00
Luke D. Jones a94a8ca28d Support the GU605M keyboard 2024-04-18 10:08:40 +12:00
Luke D. Jones f9dca2da5d Re-add TUF keyboard support 2024-04-17 21:39:58 +12:00
Luke Jones df88ff1acb Update asusd.service 2024-04-17 06:11:00 +00:00
Luke Jones cb5aa0f170 Update asusd.service 2024-04-17 06:10:44 +00:00
Luke D. Jones 4ea79f966e Add updated translation + aura_support.ron 2024-04-17 14:18:27 +12:00
Luke D. Jones b8bc1a01b3 Fix colour hex in gui 2024-04-17 12:46:36 +12:00
Luke D. Jones 0e5d1815bd Bump alpha version 2024-04-17 11:55:19 +12:00
Luke D. Jones 64e8cb65d0 Many cleanup and fix 2024-04-17 11:54:14 +12:00
Luke D. Jones 3142353f98 Fix tests 2024-04-11 13:18:03 +12:00
Luke D. Jones 484ca692ad Refactoring led support data 2024-04-10 21:14:47 +12:00
Luke D. Jones 1ebdfada96 Update discord invite 2024-04-10 18:09:41 +12:00
Luke D. Jones 3bc9dfcda1 Support GA402N keyboard 2024-04-10 17:04:21 +12:00
Luke D. Jones 895e5d2ca3 Support GA402N keyboard 2024-04-10 17:04:08 +12:00
Luke D. Jones 564992719e Fixes to slash merge 2024-04-10 09:13:26 +12:00
Luke Jones a737d240be Merge pull request #23 from jschoubben/main
Add support for 2024 G14 Slash lighting (GA403UI)
2024-04-10 08:55:17 +12:00
Luke Jones d89c1ebf26 Merge branch 'main' into main 2024-04-10 08:54:54 +12:00
Luke D. Jones be7686bb46 Dump deps and version 2024-04-09 21:24:55 +12:00
Luke D. Jones 4f70055f85 Complete building 2024-04-09 21:02:09 +12:00
Luke D. Jones 91ca049298 Unify the laptop aura power stuff 2024-04-09 12:13:42 +12:00
Luke D. Jones 635d0378ac Bump deps 2024-04-06 14:10:18 +13:00
Luke D. Jones 1c729316f7 Refactor, rename, organise rog-aura stuff better 2024-04-05 21:20:34 +13:00
Luke D. Jones 4701c019a8 Major cleanup of older gui state code 2024-04-05 20:19:07 +13:00
Luke D. Jones ca0d8bda4b Update readme. Fix tray startup 2024-04-05 16:00:20 +13:00
Luke D. Jones a271ffbb10 rcc: run as app only on Ally 2024-04-04 09:58:40 +13:00
Luke D. Jones 00babaf949 Update translations 2024-04-03 16:15:09 +13:00
Luke D. Jones 2f844ac151 Cleanup 2024-04-03 16:15:09 +13:00
Luke D. Jones 5178bf1d1a Update readme 2024-04-03 16:15:09 +13:00
jochen@g14 116afb9b6c Initialize slash from config file 2024-03-31 01:08:26 +01:00
jochen@g14 4468a58487 Use enable/disable commands and cleanup build warnings 2024-03-31 00:34:44 +01:00
jochen@g14 2f73577e91 Fix Cargo.toml 2024-03-30 23:33:30 +01:00
jochen@g14 c1cffc8f59 Remove example file since it's not correct 2024-03-30 23:29:55 +01:00
jochen@g14 6d8f85c154 Minor changes to reflect original files 2024-03-30 23:27:33 +01:00
jochen@g14 0674e7f61c Changes after my own PR review 2024-03-30 23:23:37 +01:00
jochen@g14 fde2f3ba15 Fixed issues with asusctl-slash command 2024-03-30 21:19:55 +01:00
jochen@g14 70493d1a93 Fix error in makefile 2024-03-30 19:39:42 +01:00
jochen@g14 c20d0a76a0 Fix build after merge 2024-03-30 19:00:22 +01:00
jochen@g14 e6952e241a Merge from main 2024-03-30 18:44:15 +01:00
jochen@g14 b40812928a Imlement Display and FromStr traits for SlashMode 2024-03-30 18:42:42 +01:00
jochen@g14 8cdc9773c9 Added working implementation of the G14 Slash ledstrip 2024-03-30 18:41:31 +01:00
Luke Jones 8d30282edf Update default.md 2024-03-28 01:03:30 +00:00
Luke D. Jones 4ba44560a9 Update deps 2024-03-27 19:27:22 +13:00
jochen@g14 cdc9ca7b58 Try to implement slash bar functionality - part 1 2024-03-25 01:54:05 +01:00
Luke D. Jones 7eae7c5664 Change aura manager task to blocking. Remove idle tasks that keep hanging 2024-03-24 21:14:54 +13:00
Luke D. Jones 739a0ffa63 aura debugging 2024-03-24 10:55:20 +13:00
Luke D. Jones 637360095c Shift init actions up a few calls to prevent over-eager init 2024-03-23 23:27:26 +13:00
Luke D. Jones 4b34ab83fb Initial pass of async task sync in aura 2024-03-23 23:15:38 +13:00
Luke D. Jones ac605cbc00 Narrow the search space of aura devices down 2024-03-23 14:30:00 +13:00
Luke D. Jones 4b38e5daa6 Further adjustments to aura 2024-03-23 13:07:20 +13:00
Luke D. Jones 1c007b4216 Small refinement to aura control init 2024-03-23 11:45:20 +13:00
Luke D. Jones 193f9dfa1e Extra logging for aura 2024-03-23 11:27:20 +13:00
Jochen@Jinbe 1366422d96 Play around and add rog-slash 2024-03-22 22:05:27 +01:00
Luke D. Jones 4e778a3d28 Refactor HidRaw 2024-03-22 19:47:24 +13:00
Luke D. Jones be05508110 Try to ensure all aura are detected at start 2024-03-22 17:36:58 +13:00
Luke D. Jones 9119229d41 Update deps 2024-03-20 23:12:52 +13:00
Luke D. Jones 5c43c31331 Manage add/remove aura
Serialize aura config filename
2024-03-20 23:00:25 +13:00
Luke D. Jones 014604724f Fix clippy lints 2024-03-15 19:41:38 +13:00
Luke D. Jones 7d076368e9 Adjust organization of rog control src 2024-03-15 17:19:54 +13:00
Luke D. Jones 5d6ed5c365 Remove old screenshits 2024-03-14 22:17:42 +13:00
Luke D. Jones a2b8f0f93c Minor updates 2024-03-14 22:10:14 +13:00
Luke D. Jones 5fe8416c65 Only apply fan curve for profile if *on* that profile, but still save config 2024-03-14 21:28:06 +13:00
Luke D. Jones 1b4d7a95af Minor corrections 2024-03-14 21:24:28 +13:00
Luke D. Jones e8627fde4c Fix applying disabled and enabled fan curves 2024-03-14 21:11:18 +13:00
Luke D. Jones 6b0edc6da1 Enable fan curves 2024-03-14 17:17:55 +13:00
Luke D. Jones f6ad631a0f Update readme 2024-03-14 11:19:31 +13:00
Luke D. Jones f6393a3926 Further fan-curve graph work 2024-03-13 23:30:06 +13:00
Luke D. Jones d51384c3a1 Fix the fan curve defaults again 2024-03-13 21:19:01 +13:00
Luke D. Jones 78f18959fb Populate fan curve data 2024-03-13 20:18:41 +13:00
Luke D. Jones 7a661a585e Add missing file 2024-03-13 19:14:29 +13:00
Luke D. Jones f4f7a1e648 Check in the fan curve work 2024-03-13 18:57:38 +13:00
Luke D. Jones b6e3e5e823 Remove a dbg 2024-03-13 14:08:43 +13:00
Luke D. Jones 41b1bd23d6 Fix fancurves 2024-03-13 14:08:08 +13:00
Luke D. Jones 69458a0595 Minor fix to graph widget 2024-03-12 22:06:56 +13:00
Luke D. Jones 5fd107df27 Initial fan graph widget 2024-03-12 21:33:39 +13:00
Luke D. Jones 2558057e9f Use mix instead of interpolate 2024-03-12 00:09:54 +13:00
Luke D. Jones 8111daaf1d Use mix instead of interpolate 2024-03-12 00:08:54 +13:00
Luke D. Jones 672acb234f Make aura settings apply instantly 2024-03-11 23:27:07 +13:00
Luke D. Jones 9725062fb9 Refactor and cleanup theming 2024-03-11 22:26:26 +13:00
Luke D. Jones c7b1624313 Remove dbg statements 2024-03-11 12:53:38 +13:00
Luke D. Jones 67b97f1d43 Update ci and readme 2024-03-10 23:29:50 +13:00
Luke D. Jones 6498fd1349 Move rog<->slint type conversions to module 2024-03-10 21:12:36 +13:00
Luke D. Jones e371229b6c Update support for boot_sound kernel patch 2024-03-10 20:53:03 +13:00
Luke D. Jones 0fac33a8ff Update pipeline 2024-03-09 23:49:42 +13:00
Luke D. Jones b0da062577 Clean up debug info 2024-03-09 23:30:15 +13:00
Luke D. Jones ca41bd59de Begin implmenting keyboard power states 2024-03-09 23:18:30 +13:00
Luke D. Jones efcad3f6f9 Small colour correction 2024-03-06 22:41:55 +13:00
Luke D. Jones fa2255cbaf Atempt better aura colour mix 2024-03-06 22:35:16 +13:00
Luke D. Jones 02b9bac899 Clean up the tray code 2024-03-04 13:28:19 +13:00
Luke D. Jones a1fcf5023c Force tray process to exit if Quit 2024-03-03 22:41:57 +13:00
Luke D. Jones 2f8ea80e6d Replace shitty gtk tray with betrayer 2024-03-03 22:26:52 +13:00
Luke D. Jones b798cf6a4e Minor adjust to rgb bright slider 2024-03-03 13:30:46 +13:00
Luke D. Jones 3da848d131 Init with colour sliders in approx position 2024-03-03 12:57:45 +13:00
Luke D. Jones a88c33c201 MOrE 2024-03-02 23:49:11 +13:00
Luke D. Jones 7b0f037cba Trying different strategies for non-blocking UI 2024-02-25 23:21:11 +13:00
Luke D. Jones 91b1456d06 Formulate slint patterns 2024-02-25 19:24:20 +13:00
Luke D. Jones c3b02a2bb0 Fix the IPC 2024-02-25 13:09:13 +13:00
Luke D. Jones 8e4b7d53f4 More updating to zbus 4.0.1 2024-02-24 21:56:52 +13:00
Luke D. Jones a44145f487 Update to zbus 4.0.1 2024-02-24 21:56:29 +13:00
Luke D. Jones bb7b3a81fb Bump version 2024-02-24 21:50:52 +13:00
Luke D. Jones 19607d71c3 Prep 5.0.8 release 2024-02-24 17:09:24 +13:00
Luke D. Jones 96f281d789 Remove the use of bytes in zbus signatures 2024-02-23 21:50:53 +13:00
Luke D. Jones 7613eded95 Sane defaults for asusd config 2024-02-23 14:40:00 +13:00
Luke D. Jones 50eccd2b1d Formatting and fixes 2024-02-23 12:36:16 +13:00
Luke Jones ba54007102 Merge branch 'bugfix/persistent-theme' into 'main'
Reintroduce persistent dark/light mode

See merge request asus-linux/asusctl!180
2024-02-19 19:44:59 +00:00
Filip Binkiewicz a028f5375f Reintroduce persistent dark/light mode 2024-02-19 19:27:56 +00:00
Luke Jones 9ec02cd727 Merge branch 'dearner-main-patch-22870' into 'main'
Added G814JZ to aura_support.ron (uses G814JI keymap)

See merge request asus-linux/asusctl!179
2024-02-14 01:40:26 +00:00
Chris Dearner 4b46ece09a Added G814JZ to aura_support.ron (uses G814JI keymap) 2024-02-05 12:49:49 +00:00
Luke D. Jones 086bbd0908 Fix the broken pipe error 2024-02-02 23:26:41 +13:00
Luke D. Jones c94eaa473e Update TS bindings 2024-01-24 22:37:10 +13:00
Luke D. Jones b1b809834b Reload and apply settings if config file externally changed 2024-01-24 12:19:34 +13:00
Luke D. Jones 84183288ec Fix inotify watch failing thanks to vim idiocy 2024-01-23 22:44:20 +13:00
Luke D. Jones 86cbef83b6 Reload asusd.ron if changed 2024-01-22 21:54:19 +13:00
Luke D. Jones 006fb632c4 Doc and feature fixes 2024-01-22 19:33:40 +13:00
Luke D. Jones e3636ed8ce Update smithay-client-toolkit 2024-01-22 12:06:42 +13:00
Luke D. Jones cfd207f251 Remove async-trait crate and set min rustc 1.75 2024-01-15 18:22:41 +13:00
Luke D. Jones d4c68546e7 Added ability to change what EPP is linked with each throttle profile 2024-01-15 18:00:27 +13:00
Luke D. Jones 6f4a7e16dc Fixes to RCC 2024-01-05 14:21:31 +13:00
Luke D. Jones f64253d633 Various bugfixes 2024-01-05 13:53:57 +13:00
Luke D. Jones 124c17aadc Add default issue template 2024-01-04 14:30:11 +13:00
Luke D. Jones ab40f9fcbf Add FX705D led support 2024-01-04 09:23:37 +13:00
Luke D. Jones 5cdfa5a8d4 Fix to suspend process in anime thread to let custom anims run on wake 2023-12-27 11:24:39 +13:00
Luke D. Jones ce870cd5ed Revert egui update due to a lot of issues arising from window closing 2023-12-27 10:13:26 +13:00
Luke D. Jones 4541d2e1ba Update dbus introspection 2023-12-26 11:56:47 +13:00
Luke D. Jones b525411fd3 Minor fixes in asusctl 2023-12-25 21:24:05 +13:00
Luke D. Jones a867496f13 Re-enable ROGCC fan curves 2023-12-24 10:30:33 +13:00
Luke D. Jones f421b8ee3b Fix to apply led effect in rogcc 2023-12-23 21:45:19 +13:00
Luke D. Jones 82780feb4b Update readme 2023-12-23 10:37:46 +13:00
Luke D. Jones 1e5443e206 Bugfix release 2023-12-22 11:39:17 +13:00
Luke D. Jones 027a591d26 Add cargo-vendor-filterer to pipeline 2023-12-17 21:44:33 +13:00
Luke D. Jones e90375828d Fix: nuke some async deadlocks in fan-curves 2023-12-17 21:41:07 +13:00
Luke D. Jones 75b4d67072 Fix: force Anime power/wakeup disabled to prevent idiotic random wakes 2023-12-15 19:15:14 +13:00
Luke D. Jones 9aa332de3b New release 2023-12-15 11:50:49 +13:00
Luke D. Jones 5efd7fc6a7 Fix: Corrections to dbus signature for some keyboard power settings
Closes #423
2023-12-15 11:48:20 +13:00
Luke D. Jones 0aafe24a02 Fix: correctiosn to asusd.service
Closes #424
2023-12-15 11:48:20 +13:00
Luke D. Jones dda6d343d9 Fix: correction to switching next fan profile
Closes #425
2023-12-15 11:48:18 +13:00
Luke D. Jones 6f39307080 remember how to tag releases 2023-12-12 22:00:37 +13:00
Luke Jones ef63789faa Merge branch 'main' into 'main'
Added missing button, and fixed layout for Strix G18

See merge request asus-linux/asusctl!176
2023-12-12 08:58:31 +00:00
Sunehildeep Singh c422e77ba6 Added missing button, and fixed layout for Strix G18 2023-12-12 08:58:31 +00:00
Luke D. Jones 3c234dd3c4 Release 5.0.0 2023-12-12 21:54:24 +13:00
Luke Jones c420dd820a Merge branch '419-support-for-strix-g18-aura-g814ji-already-done-need-merge' into 'main'
Draft: Resolve "Support For Strix G18 AURA (G814JI) - Already Done, need merge"

Closes #419

See merge request asus-linux/asusctl!175
2023-12-10 20:03:55 +00:00
Luke D. Jones a3e6fec163 Add g814ji and layout 2023-12-11 08:56:17 +13:00
Luke D. Jones 9b4e76be87 Update examples 2023-12-11 08:49:32 +13:00
Luke D. Jones 7b2125cbdf fic async deadlock in platform control 2023-12-08 20:06:22 +13:00
Luke D. Jones 694a644cc6 Revert "Fix: change epp change watch to a loop to prevent deadlock"
This reverts commit c06f78990f.
2023-12-08 20:06:22 +13:00
Luke D. Jones 922aa0c352 platform example testing 2023-12-08 17:33:27 +13:00
Luke D. Jones c06f78990f Fix: change epp change watch to a loop to prevent deadlock 2023-12-07 20:04:21 +13:00
Luke D. Jones 5c8bb6e6ea Ensure thermal policy is set on resume and relaod 2023-12-05 19:26:49 +13:00
Luke Jones 993131d0a9 Merge branch 'fluke/dbus-refactor' into 'main'
Fluke/dbus refactor

See merge request asus-linux/asusctl!173
2023-12-03 20:44:01 +00:00
Luke Jones 0a69c23288 Fluke/dbus refactor 2023-12-03 20:44:01 +00:00
Luke D. Jones f6e4cc0626 Cleanup after changes from Platform dbus rework 2023-11-18 22:36:55 +13:00
Luke D. Jones 1f696508e7 rog-platform: refactor all related parts 2023-11-18 21:57:46 +13:00
Luke D. Jones fa043adc99 rog-platform: add CPU and GPU tunings
rog-platform: add tunables to supported dat

Anime: fixes to how some power options work
2023-11-17 17:16:03 +13:00
Luke D. Jones b9c2d929b3 anime: rework sleep logic 2023-11-16 13:57:42 +13:00
Luke D. Jones eda1e920df Update changelog 2023-11-15 18:29:35 +13:00
Luke D. Jones 0de2c9e424 Anime: remove sleep animation config 2023-11-15 18:27:58 +13:00
Luke D. Jones e88e7be8ae Anime: expose new options in CLI 2023-11-15 17:45:52 +13:00
Luke D. Jones e470d3acc0 Anime: add dbus methods 2023-11-15 17:23:44 +13:00
Luke D. Jones f5b3f0bc38 Fix lints on examples 2023-11-15 17:23:35 +13:00
Luke D. Jones 19497c94e0 Anime: fix data struct 2023-11-15 16:56:50 +13:00
Luke D. Jones 670eee23e8 Anime: small cleanup 2023-11-15 16:45:30 +13:00
Luke D. Jones 26309776e9 Fix test 2023-11-15 14:47:52 +13:00
Luke D. Jones a7d5057976 Anime: propagate property config 2023-11-15 14:46:42 +13:00
Luke D. Jones 8eb9b1d4eb Anime: refactor power stuff 2023-11-15 14:29:38 +13:00
Luke D. Jones 71ee9e43ba Fix gnome-45 extension version 2023-11-14 16:49:17 +13:00
Luke D. Jones f1b0e1288a Add missing crates 2023-11-13 11:28:34 +13:00
Luke D. Jones 35c7fd10b3 Drop sysfs_class and create dmi_id for getting identifying info with udev 2023-11-08 14:00:35 +13:00
Luke D. Jones 4c50dc259c rog-control-center: ensure brightness slider works correctly 2023-11-08 13:59:07 +13:00
Luke D. Jones 0fd0aeff88 Support Rog Ally LED modes (basic) 2023-11-07 17:24:38 +13:00
Luke Jones fd37f41ef1 Merge branch 'sctk-0.16.1' into 'main'
cargo update -p smithay-client-toolkit v0.16.0 -> v0.16.1

Closes #407

See merge request asus-linux/asusctl!172
2023-10-11 06:30:37 +00:00
Cole Mickens 1307997122 cargo update -p smithay-client-toolkit v0.16.0 -> v0.16.1 2023-10-11 08:15:04 +02:00
Luke Jones 85187d2d8d Merge branch 'add_laptop_G513RW' into 'main'
Added preliminary support for G513RW

See merge request asus-linux/asusctl!171
2023-09-16 05:51:15 +00:00
Edwin Clement e29a568195 Added preliminary support for G513RW 2023-09-15 20:03:02 -04:00
Luke D. Jones 6c375a9951 Prep new release 2023-09-08 13:22:15 +12:00
Luke D. Jones 4641e19c43 Fix bad keybaord layout 2023-09-04 10:19:57 +12:00
Luke D. Jones 91f0c2ea14 Fix loading of fan curves
Closes #402

Signed-off-by: Luke D. Jones <luke@ljones.dev>
2023-09-04 10:18:48 +12:00
Luke Jones b4a8cb9de2 Merge branch 'srivatsarog-main-patch-78688' into 'main'
Update aura_support.ron

See merge request asus-linux/asusctl!169
2023-09-03 08:45:39 +00:00
Luke Jones 67bbcdb964 Merge branch 'main' into 'srivatsarog-main-patch-78688'
# Conflicts:
#   rog-aura/data/aura_support.ron
2023-09-03 08:37:52 +00:00
Luke Jones 8acfe0a9e8 Merge branch 'main' into 'main'
aura: Support for G733PZ

See merge request asus-linux/asusctl!167
2023-09-03 08:36:20 +00:00
Luke D. Jones 5f6e6ec382 gex: rename gnome-44, add gnome-45 2023-08-31 14:36:01 +12:00
Luke D. Jones cf92526d87 Set cargo resolver 2023-08-31 12:42:31 +12:00
Luke D. Jones f290594562 Prep release 2023-08-26 21:40:45 +12:00
Luke D. Jones 423bd54f79 Fixes: bugfixes in aura config loading 2023-08-26 21:35:38 +12:00
Luke Jones ea0eaef8a6 Update README.md 2023-08-24 10:22:26 +00:00
Luke Jones ef62a26148 Merge branch 'main' into 'main'
add aura support for GU603VV

See merge request asus-linux/asusctl!170
2023-08-20 06:13:20 +00:00
Yifan Zhu 2f916fa4e5 add aura support for GU603VV 2023-08-20 06:13:20 +00:00
Luke D. Jones e42fd10404 Update deps 2023-08-14 13:20:40 +12:00
Luke D. Jones 93edd1b632 Add gv601v singlezone advanced 2023-08-13 18:22:21 +12:00
Luke D. Jones 6957a08d83 Fix: Corrections in anime detection
Closes #387
2023-08-11 09:48:22 +12:00
Luke D. Jones 98569b98e7 Remove a dbg!() x2 2023-08-10 09:50:01 +12:00
Luke D. Jones 573568d6e2 Remove a dbg!() 2023-08-10 09:49:13 +12:00
Luke D. Jones 3ec37a4dac Fix: For anime look for usbraw device before hidraw device
Closes #387
2023-08-02 14:14:08 +12:00
Luke D. Jones 11483b28a6 Fix: Further refine the CLI for fan curve control
Should close #385
2023-08-01 19:30:33 +12:00
Luke D. Jones 2cce83d164 Fix: asusd should reload defualt fan-curves if the config file fails
Closes #385
2023-08-01 09:30:54 +12:00
Luke D. Jones 42b92f3b87 Fix: reimplement fetching of fan curves on CLI
Partial close of #385
2023-08-01 09:18:27 +12:00
gabi b652fd15a2 Update aura_support.ron 2023-07-28 06:16:39 +00:00
Luke D. Jones 9154aaa97c Add distro packaging dir + spec file for fedora 2023-07-27 10:13:10 +12:00
Interfector18 9e65921c0a aura: Support for G733PZ 2023-07-24 17:44:59 +02:00
Luke D. Jones 8d8b5e5f51 aura: support GV601V LED modes 2023-07-24 14:07:01 +12:00
Luke D. Jones 9418b63454 Bump new release 2023-07-24 10:23:05 +12:00
Luke D. Jones 2d396a49da Update crate versions 2023-07-24 10:14:24 +12:00
Luke D. Jones a6d89a622b Various cleanup. Add GA402X LED modes 2023-07-24 10:04:10 +12:00
Luke D. Jones 51bcb0082b profiles: add mid fan curve support 2023-07-24 09:29:56 +12:00
Luke D. Jones eb54250e4d aura: support FX505G 2023-07-24 09:28:09 +12:00
Luke D. Jones eafc831231 gex: update xml and aura power parsing 2023-07-12 14:21:57 +12:00
Luke D. Jones c625e97b7b Update changelog 2023-07-11 21:08:10 +12:00
Luke D. Jones fea56879d6 Update changelog 2023-07-11 21:06:27 +12:00
Luke D. Jones 03052e129b aura: update dbus xml 2023-07-11 21:02:40 +12:00
Luke D. Jones 3dbc893905 aura: update bindings 2023-07-11 21:00:42 +12:00
Luke D. Jones 50152961c7 aura: refactor modern rog power settings 2023-07-11 20:59:43 +12:00
Luke D. Jones 8be0e7e6bf gex: update laptop feature toggle to switch primary 2023-07-06 22:00:59 +12:00
Luke D. Jones d6d4a00fc3 gex: add keyboard LED mode setting 2023-07-04 21:22:30 +12:00
Luke D. Jones 611140716c gex: map more of dbus methods and notifs 2023-07-04 20:08:42 +12:00
Luke D. Jones a09f7b5275 gex: parse led power from dbus 2023-07-04 18:49:52 +12:00
Luke D. Jones 823958492e rename keyboard led power dbus methods 2023-07-04 10:17:11 +12:00
Luke D. Jones 668135eab3 Update readme 2023-07-03 23:06:19 +12:00
Luke D. Jones 622e07505d gex: begin adding aura control dbus 2023-07-03 23:04:06 +12:00
Luke D. Jones 5c159d2294 Set toolchain to stable 2023-07-03 20:14:14 +12:00
Luke D. Jones 6cfa09a02b Update readme 2023-07-03 16:20:46 +12:00
Luke D. Jones 95666cc40b Update deps 2023-07-03 16:07:38 +12:00
Luke D. Jones 458d58c87c Update changelog 2023-07-03 16:05:46 +12:00
Luke D. Jones 7328ebdda3 gex: cleanup and rename 2023-07-03 15:51:26 +12:00
Luke D. Jones 14d043bbc3 RCC: add tray enable/disable, move app settings to page 2023-07-03 15:02:25 +12:00
Luke D. Jones 98dec6403c gex: adjust tsconfig 2023-07-02 21:58:50 +12:00
Luke D. Jones 3546f5bd21 gex: add toggle for anime powersave anim 2023-07-02 21:09:17 +12:00
Luke D. Jones db1683de46 gex: temporary checking of which primary quicktoggle to use 2023-07-02 19:15:55 +12:00
Luke D. Jones 669c8ac3c7 gex: make quickmenu a module 2023-07-02 17:30:20 +12:00
Luke D. Jones 4f3a6ce1c6 gex: trial of using qucik submenu toggles 2023-07-02 11:37:36 +12:00
Luke Jones 6d3918ccf0 clean up bindings 2023-07-01 12:00:46 +00:00
Luke D. Jones b002187b39 gex: trial of updating quicktoggle with dbus signal 2023-07-01 23:14:54 +12:00
Luke D. Jones 13965b2261 gex: add eslint, cleanup parsing of some stuff 2023-07-01 21:47:31 +12:00
Luke D. Jones b182fbd323 gex: add anime power quicktoggle 2023-07-01 20:09:28 +12:00
Luke D. Jones 19b28f202c gex: Add slider for charge control 2023-07-01 19:17:02 +12:00
Luke D. Jones ed51a7fa14 gex: fix and update 2023-07-01 18:03:05 +12:00
Luke D. Jones c94358a8f3 gex: itemize the quicks and indicators 2023-07-01 17:18:11 +12:00
Luke D. Jones 2a8ca0a39a asusd: cleanup platform notifs, prevent more duplications 2023-07-01 12:31:40 +12:00
Luke D. Jones 83455a5ba3 Update deps 2023-06-30 15:57:18 +12:00
Luke D. Jones bc776deb70 gex: more cleanup 2023-06-30 11:30:09 +12:00
Luke D. Jones 59b1059aee gex: cleanup 2023-06-30 10:43:19 +12:00
Luke D. Jones dbe80d1914 gex: simplify build 2023-06-29 23:05:00 +12:00
Luke D. Jones e325ebed18 gex: working quicktoggle example 2023-06-29 20:48:15 +12:00
Luke D. Jones a743bda6e0 gex: cleanup 2023-06-29 17:42:57 +12:00
Luke D. Jones 15e6782e10 gex: map anime dbus data 2023-06-29 15:16:21 +12:00
Luke D. Jones 6b058c9922 gex: trial of dbus mthods 2023-06-29 12:58:11 +12:00
Luke D. Jones fc8879ac24 gex: dbus class template 2023-06-28 22:50:56 +12:00
Luke D. Jones 4d2d5707a1 Begin reimplement gex to use generated bindings and dbus xml 2023-06-28 21:54:17 +12:00
Luke D. Jones 439c830311 Cleanup rog-aura usb tests 2023-06-28 10:07:11 +12:00
Luke D. Jones 91223d4ced Regenerate dbus xml 2023-06-27 22:19:29 +12:00
Luke D. Jones 7b17a13ce7 ridiculous refactor to allow enums to be dbus strings for better TS generation 2023-06-27 21:16:13 +12:00
190 changed files with 17503 additions and 10883 deletions
+6
View File
@@ -2,11 +2,17 @@
set -e
echo 'find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po'
find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po
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
+11 -1
View File
@@ -8,4 +8,14 @@ vendor_*
.vscode-ctags
.vscode
.~lock.*
*.ods#
*.ods#
# gnome extension
node-modules
bindings/ts/*.d.ts
bindings/ts/*.js.map
desktop-extensions/gnome*/dist
desktop-extensions/gnome*/@types/gir-generated
desktop-extensions/gnome*/node_modules
desktop-extensions/gnome*/schemas/gschemas.compiled
desktop-extensions/gnome*/*.zip
+4 -3
View File
@@ -17,7 +17,7 @@ image: rust:latest
- target/release/.cargo-lock
before_script:
- apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev
- apt-get update -qq && apt-get install -y -qq libinput-dev libseat-dev libudev-dev libgtk-3-dev grep llvm clang libclang-dev libsdl2-dev libsdl2-gfx-dev
stages:
- format
@@ -59,11 +59,12 @@ release:
- tags
<<: *rust_cache
script:
- cargo install cargo-vendor-filterer
- make && make vendor
artifacts:
paths:
- vendor_asusctl*.tar.xz
- cargo-config
- vendor_asusctl*.tar.xz
- cargo-config
pages:
stage: deploy
+32
View File
@@ -0,0 +1,32 @@
## Issue description
(** I can not support distros which are outdated by default. This includes Ubuntu at least 50% of the time, and definitely includes Mint. **)
(Summarize the bug encountered)
## Steps to reproduce
(How can the issue be reproduced)
## What is the current bug behavior?
(What actually happens)
## What is the expected correct behavior?
(What you should see instead)
## Relevant logs and/or screenshots
(run `journalctl -b -u asusd > ~/asusd.log` and attach `~/asusd.log`)
(Paste any relevant logs - use code blocks (```) to format console output, logs, and code, as
it's very hard to read otherwise.)
## System details
- Distro:
- Kernel: (`uname -r`)
- Desktop:
- Xorg or wayland: ??
/label ~bug ~reproducable ~needs-investigation
+443 -55
View File
File diff suppressed because it is too large Load Diff
Generated
+2790 -1519
View File
File diff suppressed because it is too large Load Diff
+55 -15
View File
@@ -1,34 +1,67 @@
[workspace]
members = ["asusctl", "asusd", "asusd-user", "config-traits", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center", "simulators"]
default-members = ["asusctl", "asusd", "asusd-user", "rog-control-center"]
members = [
"asusctl",
"asusd",
"asusd-user",
"config-traits",
"cpuctl",
"dmi-id",
"rog-platform",
"rog-dbus",
"rog-anime",
"rog-aura",
"rog-profiles",
"rog-control-center",
"rog-slash",
"simulators",
]
default-members = [
"asusctl",
"asusd",
"asusd-user",
"cpuctl",
"rog-control-center",
]
resolver = "2"
[workspace.package]
version = "4.7.0-RC1"
version = "6.0.2"
rust-version = "1.77"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asusctl"
homepage = "https://gitlab.com/asus-linux/asusctl"
description = "Laptop feature control for ASUS ROG laptops and others"
edition = "2021"
[workspace.dependencies]
async-trait = "^0.1"
tokio = { version = "^1.23.0", features = ["macros", "rt-multi-thread", "time", "sync"]}
tokio = { version = "^1.36.0", default-features = false, features = [
"macros",
"sync",
"time",
"rt-multi-thread",
] }
concat-idents = "^1.1"
dirs = "^4.0"
smol = "^1.3"
mio = "0.8.11"
zbus = "~3.13.1"
logind-zbus = { version = "^3.1.0" } #, default-features = false, features = ["non_blocking"] }
zbus = "4.1"
logind-zbus = { version = "4.0.2" } #, default-features = false, features = ["non_blocking"] }
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
toml = "^0.5.10"
ron = "*"
typeshare = "1.0.0"
log = "^0.4"
env_logger = "^0.10.0"
glam = { version = "^0.22", features = ["serde"] }
gumdrop = "^0.8"
udev = "^0.7"
udev = { version = "^0.8", features = ["mio"] }
rusb = "^0.9"
sysfs-class = "^0.1.3"
inotify = "^0.10.0"
png_pong = "^0.8"
@@ -36,9 +69,11 @@ pix = "^0.13"
tinybmp = "^0.4.0"
gif = "^0.12.0"
versions = "4.1"
versions = "6.2"
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", rev = "54176413b81189a3e4edbdc20a0b4f7e2e35c063", default-features = false, features = [
"z",
] }
[profile.release]
# thin = 57s, asusd = 9.0M
@@ -47,10 +82,15 @@ lto = "fat"
debug = false
opt-level = 3
panic = "abort"
codegen-units = 1
[profile.dev]
debug = true
opt-level = 1
codegen-units = 16
[profile.dev.package."*"]
opt-level = 1
codegen-units = 16
[profile.bench]
debug = false
@@ -59,4 +99,4 @@ opt-level = 3
[workspace.dependencies.cargo-husky]
version = "1"
default-features = false
features = ["user-hooks"]
features = ["user-hooks"]
+22 -15
View File
@@ -1,5 +1,7 @@
# asusctrl manual
**NOTE:** this manual is in need of an update in some places. If you find issues please file issue reports.
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
but can also be used with non-asus laptops with reduced features.
@@ -8,11 +10,10 @@ but can also be used with non-asus laptops with reduced features.
- `asusd`: The main system daemon. It is autostarted by a udev rule and systemd unit.
- `asusd-user`: The user level daemon. Currently will run an anime sequence, with RGB keyboard sequences soon.
- `asusctl`: The CLI for interacting with the system daemon
- `asus-notify`: A notification daemon with a user systemd unit that can be enabled.
## `asusd`
`asusd` is the main system-level daemon which will control/load/save various settings in a safe way for the user, along with exposing a *safe* dbus interface for these interactions. This section covers only the daemon plus the various configuration file options.
`asusd` is the main system-level daemon which will control/load/save various settings in a safe way for the user, along with exposing a _safe_ dbus interface for these interactions. This section covers only the daemon plus the various configuration file options.
The functionality that `asusd` exposes is:
@@ -56,6 +57,7 @@ Almost all modern ASUS laptops have charging limit control now. This can be cont
```json
"bat_charge_limit": 80,
```
where the number is a percentage.
### Bios control
@@ -63,7 +65,7 @@ where the number is a percentage.
Some options that you find in Armory Crate are available under this controller, so far there is:
- POST sound: this is the sound you hear on bios boot post
- GPU MUX: this controls if the dGPU is the *only* GPU, making it the main GPU and disabling the iGPU
- GPU MUX: this controls if the dGPU is the _only_ GPU, making it the main GPU and disabling the iGPU
These options are not written to the config file as they are stored in efivars. The only way to change these is to use the exposed safe dbus methods, or use the `asusctl` CLI tool.
@@ -72,6 +74,7 @@ These options are not written to the config file as they are stored in efivars.
asusctl can support setting a power profile via platform_profile drivers. This requires [power-profiles-daemon](https://gitlab.freedesktop.org/hadess/power-profiles-daemon) v0.10.0 minimum. It also requires the kernel patch for platform_profile support to be applied form [here](https://lkml.org/lkml/2021/8/18/1022) - this patch is merged to 5.15 kernel upstream.
A common use of asusctl is to bind the `fn+f5` (fan) key to `asusctl profile -n` to cycle through the 3 profiles:
1. Balanced
2. Performance
3. Quiet
@@ -97,7 +100,7 @@ There is one more controller; the support controller. The sole pupose of this co
## asusd-user
`asusd-user` is a usermode daemon. The intended purpose is to provide a method for users to run there own custom per-key keyboard effects and modes, AniMe sequences, and possibly their own profiles - all without overwriting the *base* system config. As such some parts of the system daemon will migrate to the user daemon over time with the expectation that the Linux system runs both.
`asusd-user` is a usermode daemon. The intended purpose is to provide a method for users to run there own custom per-key keyboard effects and modes, AniMe sequences, and possibly their own profiles - all without overwriting the _base_ system config. As such some parts of the system daemon will migrate to the user daemon over time with the expectation that the Linux system runs both.
As of now only AniMe is active in this with configuration in `~/.config/rog/`. On first run defaults are created that are intended to work as examples.
@@ -177,6 +180,7 @@ An Aura config itself is a file with contents:
```
If your laptop supports multizone, `"led"` can also be `"Zone": <one of the following>`
- `SingleZone` // Keyboards with only one zone
- `ZonedKbLeft` // keyboard left
- `ZonedKbLeftMid` // keyboard left-middle
@@ -238,6 +242,7 @@ Each object in the array can be one of:
##### AsusAnimation
`AsusAnimation` is specifically for running the gif files that Armory Crate comes with. `asusctl` includes all of these in `/usr/share/asusd/anime/asus/`
```json
"AsusAnimation": {
"file": "<FILE_PATH>",
@@ -260,7 +265,7 @@ Virtually the same as `AsusAnimation` but for png files, typically created in th
##### ImageAnimation
`ImageAnimation` can play *any* gif of any size.
`ImageAnimation` can play _any_ gif of any size.
```json
"ImageAnimation": {
@@ -319,6 +324,7 @@ Must be full path: `"/usr/share/asusd/anime/asus/gaming/Controller.gif"` or `/ho
**<FLOAT>**
A number from 0.0-1.0.
- `brightness`: If it is brightness it is combined with the system daemon global brightness
- `scale`: 1.0 is the original size with lower number shrinking, larger growing
- `angle`: Rotation angle in radians
@@ -327,6 +333,7 @@ A number from 0.0-1.0.
**<TIME>**
Time is the length of time to run the gif for:
```json
"time": {
"Time": {
@@ -335,17 +342,23 @@ Time is the length of time to run the gif for:
}
},
```
A cycle is how many gif loops to run:
```json
"time": {
"Cycles": 2
},
```
`Infinite` means that this gif will never end:
```json
"time": "Infinite",
```
`Fade` allows an image or gif to fade in and out, and remain at max brightness to n time:
```json
"time": {
"Fade": {
@@ -364,6 +377,7 @@ A cycle is how many gif loops to run:
}
},
```
`show_for` can be `null`, if it is `null` then the `show_for` becomes `gif_time_length - fade_in - fade_out`.
This is period for which the gif or image will be max brightness (as set).
@@ -404,26 +418,19 @@ asusctl <command> <subcommand> --help
To switch to next/previous Aura modes you will need to bind both the aura keys (if available) to one of:
**Next**
```
asusctl led-mode -n
```
**Previous**
```
asusctl led-mode -p
```
To switch Fan/Thermal profiles you need to bind the Fn+F5 key to `asusctl profile -n`.
## User NOTIFICATIONS via dbus
If you have a notifications handler set up, or are using KDE or Gnome then you
can enable the user service to get basic notifications when something changes.
```
systemctl --user enable asus-notify.service
systemctl --user start asus-notify.service
```
# License & Trademarks
Mozilla Public License 2 (MPL-2.0)
+13 -1
View File
@@ -112,11 +112,23 @@ vendor:
echo 'directory = "vendor"' >> .cargo/config
mv .cargo/config ./cargo-config
rm -rf .cargo
rm -rf vendor
cargo vendor-filterer --platform x86_64-unknown-linux-gnu vendor
tar pcfJ vendor_asusctl_$(VERSION).tar.xz vendor
rm -rf vendor
bindings:
typeshare ./rog-anime/src/ --lang=typescript --output-file=bindings/ts/anime.ts
typeshare ./rog-aura/src/ --lang=typescript --output-file=bindings/ts/aura.ts
typeshare ./rog-profiles/src/ --lang=typescript --output-file=bindings/ts/profiles.ts
typeshare ./rog-platform/src/ --lang=typescript --output-file=bindings/ts/platform.ts
translate:
find -name \*.slint | xargs slint-tr-extractor -o rog-control-center/translations/en/rog-control-center.po
build:
ifeq ($(VENDORED),1)
cargo vendor
@echo "version = $(VERSION)"
tar pxf vendor_asusctl_$(VERSION).tar.xz
endif
@@ -129,4 +141,4 @@ ifneq ($(STRIP_BINARIES),0)
endif
.PHONY: all clean distclean install uninstall update build
.PHONY: all clean distclean install uninstall update build bindings
+54 -52
View File
@@ -1,8 +1,8 @@
# `asusctl` for ASUS ROG
[![](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/donate/?hosted_button_id=4V2DEPS7K6APC) - [Asus Linux Website](https://asus-linux.org/)
[Become a Patron!](https://www.patreon.com/bePatron?u=7602281) - [Asus Linux Website](https://asus-linux.org/)
**WARNING:** Many features are developed in tandem with kernel patches. If you see a feature is missing you either need a patched kernel, or v6.1 which has all my work merged upstream.
**WARNING:** Many features are developed in tandem with kernel patches. If you see a feature is missing you either need a patched kernel or latest release.
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
but can also be used with non-asus laptops with reduced features.
@@ -11,27 +11,29 @@ Now includes a GUI, `rog-control-center`.
## Kernel support
**The minimum supported kernel version is 5.17**
**The minimum supported kernel version is 6.10**, which will contain the patches from [here](https://lore.kernel.org/platform-driver-x86/20240404001652.86207-1-luke@ljones.dev/). This is especially required for 2023+ devices and possibly some lat 2022 devices.
**For TUF laptops, the minimum supported kernel version is 6.1**
Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
## 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.
2. Enable third-party apps to use the above with dbus methods
3. To make the above as easy as possible for new users
4. Respect the users resources: be small, light, and fast
The main goal of this work is to provide a safe and easy to use abstraction over various laptop features via DBUS, and to provide some helpful defaults and other behaviour such as toggling throttle/profile on AC/battery change.
Point 3 means that the list of supported distros is very narrow - fedora is explicitly
supported. All other distros are *not* supported (while asusd might still run fine on them).
For best support use fedora 36+ Workstation.
1. Provide safe dbus interface
2. Respect the users resources: be small, light, and fast
Point 4? asusd currently uses a tiny fraction of cpu time, and less than 1Mb of ram, the way
a system-level daemon should.
a system-level daemon should. Languages such as JS and python should never be used for system level daemons (please stop).
## Keyboard LEDs
The level of support for laptops is dependent on folks submitting data to include in [`./rog-aura/data/layouts/aura_support.ron`](./rog-aura/data/layouts/aura_support.ron), typically installed in `/usr/share/asusd/aura_support.ron`. This is because the controller used for keyboards and LEDs is used across many years and many laptop models, all with different firmware configurations - the only way to track this is with the file mentioned above. Why not just enable all by default? Because it confuses people.
See the [rog-aura readme](./rog-aura/README.md) for more details.
## Discord
[Discord server link](https://discord.gg/WTHnqabm)
[![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/z8y99XqPb7)
## SUPPORTED LAPTOPS
@@ -42,56 +44,42 @@ to this:
Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
```
or
```
Bus 003 Device 002: ID 0b05:19b6 ASUSTek Computer, Inc. [unknown]
```
then it may work without tweaks. Technically all other functions except the LED
and AniMe parts should work regardless of your latop make.
## Implemented
- [X] System daemon
- [X] GUI app
- [X] User notifications daemon
- [X] Setting/modifying built-in LED modes
- [X] Per-key LED setting
- [X] Fancy LED modes (See examples) (currently being reworked)
- [X] Saving settings for reload
- [X] AniMatrix display on G14 models that include it
- [X] Set battery charge limit (with kernel supporting this)
- [X] Fan curve control on supported laptops (G14/G15, some TUF like FA507)
- [X] Toggle bios setting for boot/POST sound
- [X] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops)
The list is a bit outdated as many features have been enabled in the Linux kernel with upstream patches and then supported in asusctl suite.
- [x] System daemon
- [x] GUI app (includes tray and notifications)
- [x] Setting/modifying built-in LED modes
- [x] Per-key LED setting
- [x] Fancy LED modes (See examples) (currently being reworked)
- [x] AniMatrix display on G14 and M16 models that include it
- [x] Set battery charge limit (with kernel supporting this)
- [x] Fan curve control on supported laptops (G14/G15, some TUF like FA507)
- [x] Toggle bios setting for boot/POST sound
- [x] Toggle GPU MUX (g-sync, or called MUX on 2022+ laptops)
# GUI
A gui is now in the repo - ROG Control Center. At this time it is still a WIP, but it has almost all features in place already.
![](/extra/system.png)
![](/extra/fan-curves.png)
![](/extra/keyboard.png)
# BUILDING
Requirements are rust >= 1.57 installed from rustup.io if the distro provided version is too old, and `make`.
**Ubuntu (unsuported):**
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
source "$HOME/.cargo/env"
make
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
Rust and cargo are required, they can be installed from [rustup.rs](https://rustup.rs/) or from the distro repos if newer than 1.75.
**fedora:**
dnf install cmake clang-devel systemd-devel glib2-devel cairo-devel atkmm-devel pangomm-devel gdk-pixbuf2-devel gtk3-devel libappindicator-gtk3
dnf install cmake clang-devel libinput-devel libseat-devel libgbm-devel libxkbcommon-devel systemd-devel \
libdrm-devel expat-devel pcre2-devel libzstd-devel libappindicator-gtk3
make
sudo make install
@@ -100,28 +88,34 @@ Requirements are rust >= 1.57 installed from rustup.io if the distro provided ve
Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis
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
zypper in rustup make cmake libinput-devel libseat-devel libgbm-devel systemd-devel clang-devel llvm-devel gdk-pixbuf-devel cairo-devel pango-devel freetype-devel libexpat-devel libayatana-indicator3-7
make
sudo make install
**Ubuntu, Popos (unsuported):**
instructions removed as outdated
## Installing
- Fedora copr = https://copr.fedorainfracloud.org/coprs/lukenukem/asus-linux/
- openSUSE = https://download.opensuse.org/repositories/home:/luke_nukem:/asus/
- Ubuntu = not supported due to packaging woes, but you can build and install on your own.
=======
The default init method is to use the udev rule, this ensures that the service is
started when the device is initialised and ready.
You may also need to activate the service for debian install. If running Pop!\_OS, I suggest disabling `system76-power` gnome-shell extension and systemd service.
## Upgrading
If you are upgrading from a previous installed version, you will need to restart the service or reboot.
```
$ systemctl daemon-reload && systemctl restart asusd
```
You may also need to activate the service for debian install. If running Pop!_OS, I suggest disabling `system76-power` gnome-shell extension and systemd service.
## Uninstalling
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
@@ -130,8 +124,16 @@ Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
See `CONTRIBUTING.md`. Additionally, also do `cargo clean` and `cargo test` on first checkout to ensure the commit hooks are used (via `cargo-husky`).
Generation of the bindings with `make bindings` requires `typeshare` to be installed.
Dbus introsepction XML requires with `make introspection` requires `anime_sim` to be running before starting `asusd`.
# OTHER
## AniMe Matrix simulator
A simulator using SDL2 can be built using `cargo build --package rog_simulators` and run with `./target/debug/anime_sim`. Once started `asusd` will need restarting to pick it up. If running this sim on a laptop _with_ the display, the simulated display will be used instead of the physical display.
## Supporting more laptops
Please file a support request.
+11 -9
View File
@@ -1,26 +1,28 @@
[package]
name = "asusctl"
license = "MPL-2.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2021"
license.workspace = true
version.workspace = true
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
[dependencies]
rog_anime = { path = "../rog-anime" }
rog_slash = { path = "../rog-slash" }
rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
asusd = { path = "../asusd" }
dmi_id = { path = "../dmi-id" }
ron.workspace = true
gumdrop.workspace = true
toml.workspace = true
sysfs-class.workspace = true
zbus.workspace = true
[dev-dependencies]
gif.workspace = true
tinybmp.workspace = true
glam.workspace = true
rog_dbus = { path = "../rog-dbus" }
cargo-husky.workspace = true
cargo-husky.workspace = true
+6 -8
View File
@@ -5,12 +5,14 @@ use std::process::exit;
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDiagonal, AnimeType};
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let args: Vec<String> = env::args().into_iter().collect();
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
println!("Usage: <filepath> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.8");
@@ -26,11 +28,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let anime_type = get_anime_type()?;
client
.proxies()
.anime()
.write(matrix.into_data_buffer(anime_type)?)
.unwrap();
proxy.write(matrix.into_data_buffer(anime_type)?).unwrap();
Ok(())
}
+7 -7
View File
@@ -3,7 +3,8 @@ use std::time::Duration;
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDiagonal, AnimeType};
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection;
// In usable data:
// Top row start at 1, ends at 32
@@ -11,26 +12,25 @@ use rog_dbus::RogDbusClientBlocking;
// 74w x 36h diagonal used by the windows app
fn main() {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
for step in (2..50).rev() {
let mut matrix = AnimeDiagonal::new(AnimeType::GA401, None);
for c in (0..60).into_iter().step_by(step) {
for c in (0..60).step_by(step) {
for i in matrix.get_mut().iter_mut() {
i[c] = 50;
}
}
for c in (0..35).into_iter().step_by(step) {
for c in (0..35).step_by(step) {
for i in &mut matrix.get_mut()[c] {
*i = 50;
}
}
let anime_type = get_anime_type().unwrap();
client
.proxies()
.anime()
proxy
.write(matrix.into_data_buffer(anime_type).unwrap())
.unwrap();
sleep(Duration::from_millis(300));
+6 -8
View File
@@ -4,12 +4,14 @@ use std::thread::sleep;
use rog_anime::usb::get_anime_type;
use rog_anime::{ActionData, ActionLoader, Sequences};
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection;
fn main() {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let args: Vec<String> = env::args().into_iter().collect();
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
println!("Please supply filepath and brightness");
return;
@@ -33,11 +35,7 @@ fn main() {
for action in seq.iter() {
if let ActionData::Animation(frames) = action {
for frame in frames.frames() {
client
.proxies()
.anime()
.write(frame.frame().clone())
.unwrap();
proxy.write(frame.frame().clone()).unwrap();
sleep(frame.delay());
}
}
+6 -3
View File
@@ -2,7 +2,8 @@ use std::convert::TryFrom;
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDataBuffer, AnimeGrid};
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection;
// In usable data:
// Top row start at 1, ends at 32
@@ -10,7 +11,9 @@ use rog_dbus::RogDbusClientBlocking;
// 74w x 36h diagonal used by the windows app
fn main() {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_anime_type().unwrap();
let mut matrix = AnimeGrid::new(anime_type);
let tmp = matrix.get_mut();
@@ -43,5 +46,5 @@ fn main() {
let matrix = <AnimeDataBuffer>::try_from(matrix).unwrap();
client.proxies().anime().write(matrix).unwrap();
proxy.write(matrix).unwrap();
}
+5 -3
View File
@@ -1,12 +1,14 @@
use rog_anime::usb::get_anime_type;
use rog_anime::AnimeDataBuffer;
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection;
// In usable data:
// Top row start at 1, ends at 32
fn main() {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_anime_type().unwrap();
let mut matrix = AnimeDataBuffer::new(anime_type);
matrix.data_mut()[1] = 100; // start = 1
@@ -127,5 +129,5 @@ fn main() {
matrix.data_mut()[1244] = 100; // end
println!("{:?}", &matrix);
client.proxies().anime().write(matrix).unwrap();
proxy.write(matrix).unwrap();
}
+6 -8
View File
@@ -6,12 +6,14 @@ use std::process::exit;
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2};
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let args: Vec<String> = env::args().into_iter().collect();
let args: Vec<String> = env::args().collect();
if args.len() != 7 {
println!("Usage: <filepath> <scale> <angle> <x pos> <y pos> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.9 0.4 0.0 0.0 0.8");
@@ -31,11 +33,7 @@ fn main() -> Result<(), Box<dyn Error>> {
anime_type,
)?;
client
.proxies()
.anime()
.write(<AnimeDataBuffer>::try_from(&matrix)?)
.unwrap();
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap();
Ok(())
}
+6 -8
View File
@@ -9,12 +9,14 @@ use std::time::Duration;
use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2};
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = RogDbusClientBlocking::new().unwrap();
let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let args: Vec<String> = env::args().into_iter().collect();
let args: Vec<String> = env::args().collect();
if args.len() != 7 {
println!("Usage: <filepath> <scale> <angle> <x pos> <y pos> <brightness>");
println!("e.g, asusctl/examples/doom_large.png 0.9 0.4 0.0 0.0 0.8");
@@ -41,11 +43,7 @@ fn main() -> Result<(), Box<dyn Error>> {
}
matrix.update();
client
.proxies()
.anime()
.write(<AnimeDataBuffer>::try_from(&matrix)?)
.unwrap();
proxy.write(<AnimeDataBuffer>::try_from(&matrix)?).unwrap();
sleep(Duration::from_micros(500));
}
}
+11 -6
View File
@@ -1,16 +1,17 @@
//! Using a combination of key-colour array plus a key layout to generate
//! outputs.
use rog_aura::advanced::LedCode;
use rog_aura::effects::{AdvancedEffects, Effect};
use rog_aura::layouts::KeyLayout;
use rog_aura::keyboard::{KeyLayout, LedCode};
use rog_aura::Colour;
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_aura::AuraProxyBlocking;
use zbus::blocking::Connection;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let layout = KeyLayout::default_layout();
let (client, _) = RogDbusClientBlocking::new().unwrap();
let conn = Connection::system().unwrap();
let proxy = AuraProxyBlocking::new(&conn).unwrap();
let mut seq = AdvancedEffects::new(true);
@@ -32,7 +33,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let zone = Effect::DoomFlicker(rog_aura::effects::DoomFlicker::new(
LedCode::SingleZone,
Colour(200, 110, 0),
Colour {
r: 200,
g: 110,
b: 0,
},
100,
10,
));
@@ -58,7 +63,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
seq.next_state(&layout);
let packets = seq.create_packets();
client.proxies().led().direct_addressing_raw(packets)?;
proxy.direct_addressing_raw(packets)?;
std::thread::sleep(std::time::Duration::from_millis(33));
}
}
+37 -7
View File
@@ -17,10 +17,28 @@ pub struct AnimeCommand {
help = "set global base brightness value <Off, Low, Med, High>"
)]
pub brightness: Option<Brightness>,
#[options(meta = "", help = "set global (image) brightness value")]
pub image_brightness: Option<f32>,
#[options(help = "clear the display")]
pub clear: bool,
#[options(
no_short,
meta = "",
help = "turn the anime off when external power is unplugged"
)]
pub off_when_unplugged: Option<bool>,
#[options(
no_short,
meta = "",
help = "turn the anime off when the laptop suspends"
)]
pub off_when_suspended: Option<bool>,
#[options(
no_short,
meta = "",
help = "turn the anime off when the lid is closed"
)]
pub off_when_lid_closed: Option<bool>,
#[options(no_short, meta = "", help = "Off with his head!!!")]
pub off_with_his_head: Option<bool>,
#[options(command)]
pub command: Option<AnimeActions>,
}
@@ -43,15 +61,27 @@ pub enum AnimeActions {
pub struct Builtins {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = " <GlitchConstruction, StaticEmergence>")]
#[options(
meta = "",
help = "Default is used if unspecified, <default:GlitchConstruction, StaticEmergence>"
)]
pub boot: AnimBooting,
#[options(meta = "", help = "<BinaryBannerScroll, RogLogoGlitch>")]
#[options(
meta = "",
help = "Default is used if unspecified, <default:BinaryBannerScroll, RogLogoGlitch>"
)]
pub awake: AnimAwake,
#[options(meta = "", help = "<BannerSwipe, Starfield>")]
#[options(
meta = "",
help = "Default is used if unspecified, <default:BannerSwipe, Starfield>"
)]
pub sleep: AnimSleeping,
#[options(meta = "", help = "<GlitchOut, SeeYa>")]
#[options(
meta = "",
help = "Default is used if unspecified, <default:GlitchOut, SeeYa>"
)]
pub shutdown: AnimShutdown,
#[options(meta = "", help = "set/apply the animations")]
#[options(meta = "", help = "set/apply the animations <true/false>")]
pub set: Option<bool>,
}
+45 -64
View File
@@ -4,89 +4,69 @@ use gumdrop::Options;
use rog_aura::error::Error;
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed};
#[derive(Options)]
#[derive(Options, Debug)]
pub struct LedPowerCommand1 {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "Control if LEDs enabled while awake <true/false>")]
pub awake: Option<bool>,
#[options(meta = "", help = "Use with awake option <true/false>")]
pub keyboard: Option<bool>,
#[options(meta = "", help = "Use with awake option <true/false>")]
pub lightbar: Option<bool>,
#[options(help = "Use with awake option, if excluded defaults to false")]
pub keyboard: bool,
#[options(help = "Use with awake option, if excluded defaults to false")]
pub lightbar: bool,
#[options(meta = "", help = "Control boot animations <true/false>")]
pub boot: Option<bool>,
#[options(meta = "", help = "Control suspend animations <true/false>")]
pub sleep: Option<bool>,
}
#[derive(Options)]
#[derive(Options, Debug)]
pub struct LedPowerCommand2 {
#[options(help = "print help message")]
pub help: bool,
#[options(command)]
pub command: Option<SetAuraEnabled>,
pub command: Option<SetAuraZoneEnabled>,
}
#[derive(Options)]
pub enum SetAuraEnabled {
#[derive(Options, Debug)]
pub enum SetAuraZoneEnabled {
/// Applies to both old and new models
#[options(help = "set <keyboard, logo, lightbar, rearglow> to enabled while device is awake")]
Awake(AuraEnabled),
#[options(
help = "set <keyboard, logo, lightbar, rearglow> to enabled while the device is booting"
)]
Boot(AuraEnabled),
#[options(
help = "set <keyboard, logo, lightbar, rearglow> to animate while the device is suspended"
)]
Sleep(AuraEnabled),
#[options(
help = "set <keyboard, logo, lightbar, rearglow> to animate while the device is shutdown"
)]
Shutdown(AuraEnabled),
#[options(help = "")]
Keyboard(AuraPowerStates),
#[options(help = "")]
Logo(AuraPowerStates),
#[options(help = "")]
Lightbar(AuraPowerStates),
#[options(help = "")]
Lid(AuraPowerStates),
#[options(help = "")]
RearGlow(AuraPowerStates),
}
#[derive(Debug, Clone, Default, Options)]
pub struct AuraEnabled {
#[derive(Debug, Clone, Options)]
pub struct AuraPowerStates {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "<true/false>")]
pub keyboard: Option<bool>,
#[options(meta = "", help = "<true/false>")]
pub logo: Option<bool>,
#[options(meta = "", help = "<true/false>")]
pub frontglow: Option<bool>,
#[options(meta = "", help = "<true/false>")]
pub rearglow: Option<bool>,
#[options(meta = "", help = "<true/false>")]
pub lid: Option<bool>,
#[options(help = "defaults to false if option unused")]
pub boot: bool,
#[options(help = "defaults to false if option unused")]
pub awake: bool,
#[options(help = "defaults to false if option unused")]
pub sleep: bool,
#[options(help = "defaults to false if option unused")]
pub shutdown: bool,
}
// impl FromStr for AuraEnabled {
// type Err = Error;
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// let s = s.to_lowercase();
// Ok(Self {
// help: false,
// keyboard: None,
// logo: None,
// lightbar: None,
// })
// }
// }
#[derive(Options)]
pub struct LedBrightness {
level: Option<u32>,
level: Option<u8>,
}
impl LedBrightness {
pub fn new(level: Option<u32>) -> Self {
pub fn new(level: Option<u8>) -> Self {
LedBrightness { level }
}
pub fn level(&self) -> Option<u32> {
pub fn level(&self) -> Option<u8> {
self.level
}
}
@@ -107,6 +87,7 @@ impl FromStr for LedBrightness {
}
}
}
#[allow(clippy::to_string_trait_impl)]
impl ToString for LedBrightness {
fn to_string(&self) -> String {
let s = match self.level {
@@ -234,29 +215,29 @@ pub struct MultiColourSpeed {
#[derive(Options)]
pub enum SetAuraBuiltin {
#[options(help = "set a single static colour")]
Static(SingleColour),
Static(SingleColour), // 0
#[options(help = "pulse between one or two colours")]
Breathe(TwoColourSpeed),
Breathe(TwoColourSpeed), // 1
#[options(help = "strobe through all colours")]
Strobe(SingleSpeed),
Strobe(SingleSpeed), // 2
#[options(help = "rainbow cycling in one of four directions")]
Rainbow(SingleSpeedDirection),
Rainbow(SingleSpeedDirection), // 3
#[options(help = "rain pattern mimicking raindrops")]
Stars(TwoColourSpeed),
Stars(TwoColourSpeed), // 4
#[options(help = "rain pattern of three preset colours")]
Rain(SingleSpeed),
Rain(SingleSpeed), // 5
#[options(help = "pressed keys are highlighted to fade")]
Highlight(SingleColourSpeed),
Highlight(SingleColourSpeed), // 6
#[options(help = "pressed keys generate horizontal laser")]
Laser(SingleColourSpeed),
Laser(SingleColourSpeed), // 7
#[options(help = "pressed keys ripple outwards like a splash")]
Ripple(SingleColourSpeed),
Ripple(SingleColourSpeed), // 8
#[options(help = "set a rapid pulse")]
Pulse(SingleColour),
Pulse(SingleColour), // 10
#[options(help = "set a vertical line zooming from left")]
Comet(SingleColour),
Comet(SingleColour), // 11
#[options(help = "set a wide vertical line zooming from left")]
Flash(SingleColour),
Flash(SingleColour), // 12
}
impl Default for SetAuraBuiltin {
+23 -1
View File
@@ -1,8 +1,10 @@
use gumdrop::Options;
use rog_platform::platform::ThrottlePolicy;
use crate::anime_cli::AnimeCommand;
use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin};
use crate::profiles_cli::{FanCurveCommand, ProfileCommand};
use crate::fan_curve_cli::FanCurveCommand;
use crate::slash_cli::SlashCommand;
#[derive(Default, Options)]
pub struct CliStart {
@@ -40,10 +42,30 @@ pub enum CliCommand {
Graphics(GraphicsCommand),
#[options(name = "anime", help = "Manage AniMe Matrix")]
Anime(AnimeCommand),
#[options(name = "slash", help = "Manage Slash Ledbar")]
Slash(SlashCommand),
#[options(help = "Change bios settings")]
Bios(BiosCommand),
}
#[derive(Debug, Clone, Options)]
pub struct ProfileCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "toggle to next profile in list")]
pub next: bool,
#[options(help = "list available profiles")]
pub list: bool,
#[options(help = "get profile")]
pub profile_get: bool,
#[options(meta = "", help = "set the active profile")]
pub profile_set: Option<ThrottlePolicy>,
}
#[derive(Options)]
pub struct LedModeCommand {
#[options(help = "print help message")]
+49
View File
@@ -0,0 +1,49 @@
use gumdrop::Options;
use rog_platform::platform::ThrottlePolicy;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::FanCurvePU;
#[derive(Debug, Clone, Options)]
pub struct FanCurveCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "get enabled fan profiles")]
pub get_enabled: bool,
#[options(help = "set the active profile's fan curve to default")]
pub default: bool,
#[options(
meta = "",
help = "profile to modify fan-curve for. Shows data if no options provided"
)]
pub mod_profile: Option<ThrottlePolicy>,
#[options(
meta = "",
help = "enable or disable <true/false> fan all curves for a profile. `--mod_profile` \
required"
)]
pub enable_fan_curves: Option<bool>,
#[options(
meta = "",
help = "enable or disable <true/false> a single fan curve for a profile. `--mod_profile` \
and `--fan` required"
)]
pub enable_fan_curve: Option<bool>,
#[options(
meta = "",
help = "select fan <cpu/gpu/mid> to modify. `--mod_profile` required"
)]
pub fan: Option<FanCurvePU>,
#[options(
meta = "",
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%. \
`--mod-profile` required. If '%' is omitted the fan range is 0-255"
)]
pub data: Option<CurveData>,
}
+411 -316
View File
File diff suppressed because it is too large Load Diff
-52
View File
@@ -1,52 +0,0 @@
use gumdrop::Options;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{FanCurvePU, Profile};
#[derive(Debug, Clone, Options)]
pub struct ProfileCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "toggle to next profile in list")]
pub next: bool,
#[options(help = "list available profiles")]
pub list: bool,
#[options(help = "get profile")]
pub profile_get: bool,
#[options(meta = "", help = "set the active profile")]
pub profile_set: Option<Profile>,
}
#[derive(Debug, Clone, Options)]
pub struct FanCurveCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "get enabled fan profiles")]
pub get_enabled: bool,
#[options(help = "set the active profile's fan curve to default")]
pub default: bool,
#[options(
meta = "",
help = "profile to modify fan-curve for. Shows data if no options provided"
)]
pub mod_profile: Option<Profile>,
#[options(
meta = "",
help = "enable or disable <true/false> fan curve. `mod-profile` required"
)]
pub enabled: Option<bool>,
#[options(
meta = "",
help = "select fan <cpu/gpu> to modify. `mod-profile` required"
)]
pub fan: Option<FanCurvePU>,
#[options(
meta = "",
help = "data format = 30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%,109c:58%.
`--mod-profile` required. If '%' is omitted the fan range is 0-255"
)]
pub data: Option<CurveData>,
}
+20
View File
@@ -0,0 +1,20 @@
use gumdrop::Options;
use rog_slash::SlashMode;
#[derive(Options)]
pub struct SlashCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(help = "Enable the Slash Ledbar")]
pub enable: bool,
#[options(help = "Ddisable the Slash Ledbar")]
pub disable: bool,
#[options(meta = "", help = "Set brightness value <0-255>")]
pub brightness: Option<u8>,
#[options(meta = "", help = "Set interval value <0-255>")]
pub interval: Option<u8>,
#[options(help = "Set SlashMode (so 'list' for all options)")]
pub slash_mode: Option<SlashMode>,
#[options(help = "list available animations")]
pub list: bool,
}
+11 -9
View File
@@ -1,10 +1,12 @@
[package]
name = "asusd-user"
license = "MPL-2.0"
license.workspace = true
version.workspace = true
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2021"
description = "Usermode daemon for user settings, anime, per-key lighting"
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
[[bin]]
name = "asusd-user"
@@ -16,8 +18,8 @@ smol.workspace = true
# serialisation
serde.workspace = true
serde_json.workspace = true
serde_derive.workspace = true
ron.workspace = true
rog_anime = { path = "../rog-anime" }
rog_aura = { path = "../rog-aura" }
@@ -26,10 +28,10 @@ rog_platform = { path = "../rog-platform" }
config-traits = { path = "../config-traits" }
zbus.workspace = true
# cli and logging
log.workspace = true
env_logger.workspace = true
[dev-dependencies]
cargo-husky.workspace = true
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
+20 -7
View File
@@ -3,8 +3,8 @@ 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::keyboard::LedCode;
use rog_aura::{Colour, Speed};
use serde_derive::{Deserialize, Serialize};
@@ -136,8 +136,16 @@ impl Default for ConfigAura {
let mut seq = AuraSequences::new(false);
let mut key = Effect::Breathe(Breathe::new(
LedCode::W,
Colour(255, 0, 20),
Colour(20, 255, 0),
Colour {
r: 255,
g: 0,
b: 20,
},
Colour {
r: 20,
g: 255,
b: 0,
},
Speed::Low,
));
@@ -151,20 +159,25 @@ impl Default for ConfigAura {
let key = Effect::Breathe(Breathe::new(
LedCode::F,
Colour(255, 0, 0),
Colour(255, 0, 0),
Colour { r: 255, g: 0, b: 0 },
Colour { r: 255, g: 0, b: 0 },
Speed::High,
));
seq.push(key);
let mut key = Effect::Static(Static::new(LedCode::RCtrl, Colour(0, 0, 255)));
let mut key = Effect::Static(Static::new(LedCode::RCtrl, Colour { r: 0, g: 0, b: 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));
let key = Effect::DoomFlicker(DoomFlicker::new(
LedCode::N9,
Colour { r: 0, g: 0, b: 255 },
80,
40,
));
seq.push(key);
Self {
+31 -43
View File
@@ -7,9 +7,10 @@ use std::time::{Duration, Instant};
use config_traits::StdConfig;
use rog_anime::error::AnimeError;
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
use rog_dbus::RogDbusClientBlocking;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use ron::ser::PrettyConfig;
use serde_derive::{Deserialize, Serialize};
use zbus::dbus_interface;
use zbus::interface;
use zbus::zvariant::{ObjectPath, Type};
use crate::config::ConfigAnime;
@@ -22,22 +23,18 @@ pub struct Timer {
/// animation loop count
count: u64,
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for
fade_in: Option<u64>,
fade_in: u64,
/// Used only for `TimeType::Timer`, milliseonds to fade the image out for
fade_out: Option<u64>,
fade_out: u64,
}
impl From<Timer> for AnimTime {
fn from(time: Timer) -> Self {
match time.type_of {
TimeType::Timer => {
if time.fade_in.is_some() || time.fade_out.is_some() {
let fade_in = time
.fade_in
.map_or(Duration::from_secs(0), Duration::from_millis);
let fade_out = time
.fade_out
.map_or(Duration::from_secs(0), Duration::from_millis);
if time.fade_in != 0 && time.fade_out != 0 {
let fade_in = Duration::from_millis(time.fade_in);
let fade_out = Duration::from_millis(time.fade_out);
let show_for = if time.count != 0 {
Some(Duration::from_millis(time.count))
} else {
@@ -65,14 +62,14 @@ pub enum TimeType {
/// thread and a zbus server behind `Arc<Mutex<T>>`
pub struct CtrlAnimeInner<'a> {
sequences: Sequences,
client: RogDbusClientBlocking<'a>,
client: AnimeProxyBlocking<'a>,
do_early_return: Arc<AtomicBool>,
}
impl<'a> CtrlAnimeInner<'static> {
pub fn new(
sequences: Sequences,
client: RogDbusClientBlocking<'static>,
client: AnimeProxyBlocking<'static>,
do_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> {
Ok(Self {
@@ -97,19 +94,13 @@ impl<'a> CtrlAnimeInner<'static> {
return Ok(true); // Do safe exit
}
self.client
.proxies()
.anime()
.write(output)
.map_err(|e| AnimeError::Dbus(format!("{}", e)))
.map(|_| false)
});
}
ActionData::Image(image) => {
self.client
.proxies()
.anime()
.write(image.as_ref().clone())
.ok();
self.client.write(image.as_ref().clone()).ok();
}
ActionData::Pause(duration) => {
let start = Instant::now();
@@ -136,7 +127,7 @@ impl<'a> CtrlAnimeInner<'static> {
pub struct CtrlAnime<'a> {
config: Arc<Mutex<ConfigAnime>>,
client: RogDbusClientBlocking<'a>,
client: AnimeProxyBlocking<'a>,
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
/// Must be the same Atomic as in CtrlAnimeInner
inner_early_return: Arc<AtomicBool>,
@@ -146,7 +137,7 @@ impl CtrlAnime<'static> {
pub fn new(
config: Arc<Mutex<ConfigAnime>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
client: RogDbusClientBlocking<'static>,
client: AnimeProxyBlocking<'static>,
inner_early_return: Arc<AtomicBool>,
) -> Result<Self, Error> {
Ok(CtrlAnime {
@@ -179,7 +170,7 @@ impl CtrlAnime<'static> {
// - Do actions
// - Write config if required
// - Unset inner_early_return
#[dbus_interface(name = "org.asuslinux.Daemon")]
#[interface(name = "org.asuslinux.Daemon")]
impl CtrlAnime<'static> {
pub fn insert_asus_gif(
&mut self,
@@ -209,11 +200,12 @@ impl CtrlAnime<'static> {
config.anime.push(action);
config.write();
let json = serde_json::to_string_pretty(&*config).expect("Parse config to JSON failed");
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
.expect("Parse config to RON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
return Ok(ron);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
@@ -254,12 +246,11 @@ impl CtrlAnime<'static> {
config.anime.push(action);
config.write();
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
.expect("Parse config to RON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
return Ok(ron);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
@@ -299,12 +290,11 @@ impl CtrlAnime<'static> {
config.anime.push(action);
config.write();
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
.expect("Parse config to RON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
return Ok(ron);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
@@ -324,12 +314,11 @@ impl CtrlAnime<'static> {
config.anime.push(action);
config.write();
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
.expect("Parse config to RON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
return Ok(ron);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
@@ -347,12 +336,11 @@ impl CtrlAnime<'static> {
}
config.write();
let json =
serde_json::to_string_pretty(&*config.anime).expect("Parse config to JSON failed");
let ron = ron::ser::to_string_pretty(&*config, PrettyConfig::new().depth_limit(4))
.expect("Parse config to RON failed");
// Release the inner run loop again
self.inner_early_return.store(false, Ordering::SeqCst);
return Ok(json);
return Ok(ron);
}
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
}
@@ -360,13 +348,13 @@ impl CtrlAnime<'static> {
pub fn set_state(&mut self, on: bool) -> zbus::fdo::Result<()> {
// Operations here need to be in specific order
if on {
self.client.proxies().anime().set_enable_display(on).ok();
self.client.set_enable_display(on).ok();
// Let the inner loop run
self.inner_early_return.store(false, Ordering::SeqCst);
} else {
// Must make the inner run loop return early
self.inner_early_return.store(true, Ordering::SeqCst);
self.client.proxies().anime().set_enable_display(on).ok();
self.client.set_enable_display(on).ok();
}
Ok(())
}
+30 -23
View File
@@ -5,12 +5,14 @@ use std::sync::{Arc, Mutex};
use asusd_user::config::*;
use asusd_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner};
use asusd_user::DBUS_NAME;
use config_traits::{StdConfig, StdConfigLoad};
use rog_anime::usb::get_anime_type;
use rog_aura::aura_detection::LaptopLedData;
use rog_aura::layouts::KeyLayout;
use rog_dbus::RogDbusClientBlocking;
use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::KeyLayout;
use rog_dbus::zbus_anime::AnimeProxyBlocking;
use rog_dbus::zbus_aura::AuraProxyBlocking;
use rog_dbus::zbus_platform::PlatformProxyBlocking;
use rog_dbus::DBUS_NAME;
use smol::Executor;
use zbus::Connection;
@@ -33,22 +35,26 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" rog-dbus v{}", rog_dbus::VERSION);
println!("rog-platform v{}", rog_platform::VERSION);
let (client, _) = RogDbusClientBlocking::new()?;
let supported = client.proxies().supported().supported_functions()?;
let conn = zbus::blocking::Connection::system().unwrap();
let platform_proxy = PlatformProxyBlocking::new(&conn).unwrap();
let supported = platform_proxy
.supported_interfaces()
.unwrap_or_default()
.contains(&"Anime".to_string());
let config = ConfigBase::new().load();
let executor = Executor::new();
let early_return = Arc::new(AtomicBool::new(false));
// Set up the anime data and run loop/thread
if supported.anime_ctrl.0 {
if supported {
if let Some(cfg) = config.active_anime {
let anime_type = get_anime_type()?;
let anime_config = ConfigAnime::new().set_name(cfg).load();
let anime = anime_config.create(anime_type)?;
let anime_config = Arc::new(Mutex::new(anime_config));
let anime_proxy_blocking = AnimeProxyBlocking::new(&conn).unwrap();
executor
.spawn(async move {
// Create server
@@ -57,12 +63,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Inner behind mutex required for thread safety
let inner = Arc::new(Mutex::new(
CtrlAnimeInner::new(anime, client, early_return.clone()).unwrap(),
CtrlAnimeInner::new(
anime,
anime_proxy_blocking.clone(),
early_return.clone(),
)
.unwrap(),
));
// Need new client object for dbus control part
let (client, _) = RogDbusClientBlocking::new().unwrap();
let anime_control =
CtrlAnime::new(anime_config, inner.clone(), client, early_return).unwrap();
let anime_control = CtrlAnime::new(
anime_config,
inner.clone(),
anime_proxy_blocking,
early_return,
)
.unwrap();
anime_control.add_to_server(&mut connection).await;
loop {
if let Ok(inner) = inner.clone().try_lock() {
@@ -79,7 +94,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut aura_config = ConfigAura::new().set_name(cfg).load();
// let baord_name = std::fs::read_to_string(BOARD_NAME)?;
let led_support = LaptopLedData::get_data();
let led_support = LedSupportData::get_data("");
let layout = KeyLayout::find_layout(led_support, PathBuf::from(DATA_DIR))
.map_err(|e| {
@@ -87,22 +102,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
})
.unwrap_or_else(|_| KeyLayout::default_layout());
let aura_proxy_blocking = AuraProxyBlocking::new(&conn).unwrap();
executor
.spawn(async move {
// Create server
let (client, _) = RogDbusClientBlocking::new().unwrap();
// let connection = Connection::session().await.unwrap();
// connection.request_name(DBUS_NAME).await.unwrap();
loop {
aura_config.aura.next_state(&layout);
let packets = aura_config.aura.create_packets();
client
.proxies()
.led()
.direct_addressing_raw(packets)
.unwrap();
aura_proxy_blocking.direct_addressing_raw(packets).unwrap();
std::thread::sleep(std::time::Duration::from_millis(33));
}
})
-2
View File
@@ -6,6 +6,4 @@ pub mod ctrl_anime;
pub mod zbus_anime;
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
+5 -2
View File
@@ -21,9 +21,12 @@
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
#![allow(clippy::too_many_arguments)]
use zbus::dbus_proxy;
use zbus::proxy;
#[dbus_proxy(interface = "org.asuslinux.Daemon")]
#[proxy(
interface = "org.asuslinux.Daemon",
default_path = "/org/asuslinux/Anime"
)]
trait Daemon {
/// InsertAsusGif method
fn insert_asus_gif(
+18 -15
View File
@@ -1,13 +1,13 @@
[package]
name = "asusd"
license = "MPL-2.0"
license.workspace = true
version.workspace = true
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl"
description = "A daemon app for ASUS GX502 and similar laptops to control missing features"
edition = "2021"
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
description.workspace = true
edition.workspace = true
[[bin]]
name = "asusd"
@@ -16,13 +16,18 @@ path = "src/daemon.rs"
[dependencies]
config-traits = { path = "../config-traits" }
rog_anime = { path = "../rog-anime", features = ["dbus"] }
rog_slash = { path = "../rog-slash", features = ["dbus"] }
rog_aura = { path = "../rog-aura", features = ["dbus"] }
rog_platform = { path = "../rog-platform" }
rog_profiles = { path = "../rog-profiles" }
rog_dbus = { path = "../rog-dbus" }
dmi_id = { path = "../dmi-id" }
futures-lite = "*"
udev.workspace = true
inotify.workspace = true
async-trait.workspace = true
mio.workspace = true
tokio.workspace = true
# console-subscriber = "0.2.0"
# cli and logging
log.workspace = true
@@ -35,12 +40,10 @@ logind-zbus.workspace = true
serde.workspace = true
serde_derive.workspace = true
# Device control
sysfs-class.workspace = true # used for backlight control and baord ID
concat-idents.workspace = true
systemd-zbus = "*"
[dev-dependencies]
cargo-husky.workspace = true
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
+213 -61
View File
@@ -1,10 +1,217 @@
use config_traits::{StdConfig, StdConfigLoad2};
use config_traits::{StdConfig, StdConfigLoad3};
use rog_platform::cpu::CPUEPP;
use rog_platform::platform::ThrottlePolicy;
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "asusd.ron";
#[derive(Deserialize, Serialize, Default, Debug)]
#[derive(Deserialize, Serialize, Debug, PartialEq, PartialOrd)]
pub struct Config {
/// Save charge limit for restoring on boot/resume
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub boot_sound: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
/// An optional command/script to run when power is changed to AC
pub ac_command: String,
/// An optional command/script to run when power is changed to battery
pub bat_command: String,
/// Set true if energy_performance_preference should be set if the
/// throttle/platform profile is changed
pub throttle_policy_linked_epp: bool,
/// Which throttle/profile to use on battery power
pub throttle_policy_on_battery: ThrottlePolicy,
/// Which throttle/profile to use on AC power
pub throttle_policy_on_ac: ThrottlePolicy,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_quiet_epp: CPUEPP,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_balanced_epp: CPUEPP,
/// The energy_performance_preference for this throttle/platform profile
pub throttle_performance_epp: CPUEPP,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl1_spl: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_pl2_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_fppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_apu_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ppt_platform_sppt: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_dynamic_boost: Option<u8>,
/// Defaults to `None` if not supported
#[serde(skip_serializing_if = "Option::is_none", default)]
pub nv_temp_target: Option<u8>,
/// Temporary state for AC/Batt
#[serde(skip)]
pub last_power_plugged: u8,
}
impl Default for Config {
fn default() -> Self {
Self {
charge_control_end_threshold: 100,
panel_od: false,
boot_sound: false,
mini_led_mode: false,
disable_nvidia_powerd_on_battery: true,
ac_command: Default::default(),
bat_command: Default::default(),
throttle_policy_linked_epp: true,
throttle_policy_on_battery: ThrottlePolicy::Quiet,
throttle_policy_on_ac: ThrottlePolicy::Performance,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: Default::default(),
ppt_pl2_sppt: Default::default(),
ppt_fppt: Default::default(),
ppt_apu_sppt: Default::default(),
ppt_platform_sppt: Default::default(),
nv_dynamic_boost: Default::default(),
nv_temp_target: Default::default(),
last_power_plugged: Default::default(),
}
}
}
impl StdConfig for Config {
fn new() -> Self {
Config {
charge_control_end_threshold: 100,
disable_nvidia_powerd_on_battery: true,
throttle_policy_on_battery: ThrottlePolicy::Quiet,
throttle_policy_on_ac: ThrottlePolicy::Performance,
ac_command: String::new(),
bat_command: String::new(),
..Default::default()
}
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad3<Config472, Config506, Config507> for Config {}
#[derive(Deserialize, Serialize)]
pub struct Config507 {
/// Save charge limit for restoring on boot
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
pub platform_policy_linked_epp: bool,
pub platform_policy_on_battery: ThrottlePolicy,
pub platform_policy_on_ac: ThrottlePolicy,
//
pub ppt_pl1_spl: Option<u8>,
pub ppt_pl2_sppt: Option<u8>,
pub ppt_fppt: Option<u8>,
pub ppt_apu_sppt: Option<u8>,
pub ppt_platform_sppt: Option<u8>,
pub nv_dynamic_boost: Option<u8>,
pub nv_temp_target: Option<u8>,
}
impl From<Config507> for Config {
fn from(c: Config507) -> Self {
Self {
charge_control_end_threshold: c.charge_control_end_threshold,
panel_od: c.panel_od,
boot_sound: false,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command,
bat_command: c.bat_command,
mini_led_mode: c.mini_led_mode,
throttle_policy_linked_epp: true,
throttle_policy_on_battery: c.platform_policy_on_battery,
throttle_policy_on_ac: c.platform_policy_on_ac,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: c.ppt_pl1_spl,
ppt_pl2_sppt: c.ppt_pl2_sppt,
ppt_fppt: c.ppt_fppt,
ppt_apu_sppt: c.ppt_apu_sppt,
ppt_platform_sppt: c.ppt_platform_sppt,
nv_dynamic_boost: c.nv_dynamic_boost,
nv_temp_target: c.nv_temp_target,
last_power_plugged: 0,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config506 {
/// Save charge limit for restoring on boot
pub charge_control_end_threshold: u8,
pub panel_od: bool,
pub mini_led_mode: bool,
pub disable_nvidia_powerd_on_battery: bool,
pub ac_command: String,
pub bat_command: String,
/// Restored on boot as well as when power is plugged
#[serde(skip)]
pub platform_policy_to_restore: ThrottlePolicy,
pub platform_policy_on_battery: ThrottlePolicy,
pub platform_policy_on_ac: ThrottlePolicy,
//
pub ppt_pl1_spl: Option<u8>,
pub ppt_pl2_sppt: Option<u8>,
pub ppt_fppt: Option<u8>,
pub ppt_apu_sppt: Option<u8>,
pub ppt_platform_sppt: Option<u8>,
pub nv_dynamic_boost: Option<u8>,
pub nv_temp_target: Option<u8>,
}
impl From<Config506> for Config {
fn from(c: Config506) -> Self {
Self {
charge_control_end_threshold: c.charge_control_end_threshold,
panel_od: c.panel_od,
boot_sound: false,
disable_nvidia_powerd_on_battery: c.disable_nvidia_powerd_on_battery,
ac_command: c.ac_command,
bat_command: c.bat_command,
mini_led_mode: c.mini_led_mode,
throttle_policy_linked_epp: true,
throttle_policy_on_battery: c.platform_policy_on_battery,
throttle_policy_on_ac: c.platform_policy_on_ac,
throttle_quiet_epp: CPUEPP::Power,
throttle_balanced_epp: CPUEPP::BalancePower,
throttle_performance_epp: CPUEPP::Performance,
ppt_pl1_spl: c.ppt_pl1_spl,
ppt_pl2_sppt: c.ppt_pl2_sppt,
ppt_fppt: c.ppt_fppt,
ppt_apu_sppt: c.ppt_apu_sppt,
ppt_platform_sppt: c.ppt_platform_sppt,
nv_dynamic_boost: c.nv_dynamic_boost,
nv_temp_target: c.nv_temp_target,
last_power_plugged: 0,
}
}
}
#[derive(Deserialize, Serialize)]
pub struct Config472 {
/// Save charge limit for restoring on boot
pub bat_charge_limit: u8,
pub panel_od: bool,
@@ -14,70 +221,15 @@ pub struct Config {
pub bat_command: String,
}
impl StdConfig for Config {
fn new() -> Self {
Config {
bat_charge_limit: 100,
panel_od: false,
mini_led_mode: 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<Config458, Config462> for Config {}
#[derive(Deserialize, Serialize)]
pub struct Config462 {
/// 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 From<Config462> for Config {
fn from(c: Config462) -> Self {
impl From<Config472> for Config {
fn from(c: Config472) -> Self {
Self {
bat_charge_limit: c.bat_charge_limit,
charge_control_end_threshold: c.bat_charge_limit,
panel_od: c.panel_od,
mini_led_mode: false,
disable_nvidia_powerd_on_battery: true,
ac_command: String::new(),
bat_command: String::new(),
}
}
}
#[derive(Deserialize, Serialize)]
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,
mini_led_mode: false,
disable_nvidia_powerd_on_battery: true,
ac_command: c.ac_command,
bat_command: c.bat_command,
..Default::default()
}
}
}
+47 -34
View File
@@ -3,7 +3,9 @@ use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad2};
use rog_anime::error::AnimeError;
use rog_anime::usb::Brightness;
use rog_anime::{ActionData, ActionLoader, AnimTime, Animations, AnimeType, Fade, Vec2};
use rog_anime::{
ActionData, ActionLoader, AnimTime, Animations, AnimeType, DeviceState, Fade, Vec2,
};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron";
@@ -24,7 +26,6 @@ impl From<AnimeConfigV460> for AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
sleep: c.sleep,
shutdown: c.shutdown,
..Default::default()
}
@@ -32,25 +33,32 @@ impl From<AnimeConfigV460> for AnimeConfig {
}
#[derive(Deserialize, Serialize, Debug)]
pub struct AnimeConfigV5 {
pub struct AnimeConfigV472 {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
pub awake_enabled: bool,
pub boot_anim_enabled: bool,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub builtin_anims: Animations,
}
impl From<AnimeConfigV5> for AnimeConfig {
fn from(c: AnimeConfigV5) -> AnimeConfig {
impl From<AnimeConfigV472> for AnimeConfig {
fn from(c: AnimeConfigV472) -> AnimeConfig {
AnimeConfig {
system: c.system,
boot: c.boot,
wake: c.wake,
sleep: c.sleep,
shutdown: c.shutdown,
model_override: c.model_override,
display_enabled: c.display_enabled,
display_brightness: c.display_brightness,
builtin_anims_enabled: c.builtin_anims_enabled,
builtin_anims: c.builtin_anims,
..Default::default()
}
}
@@ -61,7 +69,6 @@ pub struct AnimeConfigCached {
pub system: Vec<ActionData>,
pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>,
pub sleep: Vec<ActionData>,
pub shutdown: Vec<ActionData>,
}
@@ -89,12 +96,6 @@ impl AnimeConfigCached {
}
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());
for ani in &config.shutdown {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
@@ -105,18 +106,21 @@ impl AnimeConfigCached {
}
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct AnimeConfig {
pub model_override: Option<AnimeType>,
pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>,
pub sleep: Vec<ActionLoader>,
pub shutdown: Vec<ActionLoader>,
pub brightness: f32,
// pub brightness: f32,
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub off_when_unplugged: bool,
pub off_when_suspended: bool,
pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness,
pub builtin_anims: Animations,
}
@@ -127,12 +131,15 @@ impl Default for AnimeConfig {
system: Vec::new(),
boot: Vec::new(),
wake: Vec::new(),
sleep: Vec::new(),
shutdown: Vec::new(),
brightness: 1.0,
// brightness: 1.0,
display_enabled: true,
display_brightness: Brightness::Med,
builtin_anims_enabled: true,
off_when_unplugged: true,
off_when_suspended: true,
off_when_lid_closed: true,
brightness_on_battery: Brightness::Low,
builtin_anims: Animations::default(),
}
}
@@ -143,16 +150,31 @@ impl StdConfig for AnimeConfig {
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()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV5> for AnimeConfig {}
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV472> for AnimeConfig {}
impl From<&AnimeConfig> for DeviceState {
fn from(config: &AnimeConfig) -> Self {
DeviceState {
display_enabled: config.display_enabled,
display_brightness: config.display_brightness,
builtin_anims_enabled: config.builtin_anims_enabled,
builtin_anims: config.builtin_anims,
off_when_unplugged: config.off_when_unplugged,
off_when_suspended: config.off_when_suspended,
off_when_lid_closed: config.off_when_lid_closed,
brightness_on_battery: config.brightness_on_battery,
}
}
}
impl AnimeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) {
@@ -193,14 +215,6 @@ impl AnimeConfig {
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 {
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
scale: 0.9,
@@ -209,7 +223,6 @@ impl AnimeConfig {
brightness: 1.0,
time: AnimTime::Infinite,
}],
brightness: 1.0,
..Default::default()
}
}
+39 -14
View File
@@ -8,25 +8,19 @@ use std::sync::Arc;
use std::thread::sleep;
use ::zbus::export::futures_util::lock::Mutex;
use config_traits::{StdConfig, StdConfigLoad2};
use log::{error, info, warn};
use rog_anime::error::AnimeError;
use rog_anime::usb::{get_anime_type, pkt_flush, pkt_set_enable_powersave_anim, pkts_for_init};
use rog_anime::usb::{
get_anime_type, pkt_flush, pkt_set_brightness, pkt_set_enable_display,
pkt_set_enable_powersave_anim, pkts_for_init, Brightness,
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::supported::AnimeSupportedFunctions;
use rog_platform::usb_raw::USBRaw;
use self::config::{AnimeConfig, AnimeConfigCached};
use crate::error::RogError;
use crate::GetSupported;
impl GetSupported for CtrlAnime {
type A = AnimeSupportedFunctions;
fn get_supported() -> Self::A {
AnimeSupportedFunctions(HidRaw::new("193b").is_ok())
}
}
enum Node {
Usb(USBRaw),
@@ -46,6 +40,13 @@ impl Node {
}
Ok(())
}
pub fn set_builtins_enabled(&self, enabled: bool, bright: Brightness) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
self.write_bytes(&pkt_set_enable_display(enabled))?;
self.write_bytes(&pkt_set_brightness(bright))?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
}
}
pub struct CtrlAnime {
@@ -62,18 +63,34 @@ pub struct CtrlAnime {
impl CtrlAnime {
#[inline]
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, RogError> {
// let node = HidRaw::new("193b")?;
pub fn new() -> Result<CtrlAnime, RogError> {
let usb = USBRaw::new(0x193b).ok();
let hid = HidRaw::new("193b").ok();
let node = if usb.is_some() {
info!("Anime using the USB interface");
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
info!("Anime using the HID interface");
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::Anime(AnimeError::NoDevice));
};
// TODO: something better to set wakeups disabled
// if matches!(node, Node::Usb(_)) {
// if let Ok(mut enumerator) = udev::Enumerator::new() {
// enumerator.match_subsystem("usb").ok();
// enumerator.match_attribute("idProduct", "193b").ok();
// if let Ok(mut enumer) = enumerator.scan_devices() {
// if let Some(mut dev) = enumer.next() {
// dev.set_attribute_value("power/wakeup", "disabled").ok();
// }
// }
// }
// }
let config = AnimeConfig::new().load();
let mut anime_type = get_anime_type()?;
if let AnimeType::Unknown = anime_type {
if let Some(model) = config.model_override {
@@ -231,6 +248,14 @@ impl CtrlAnime {
})
.ok();
}
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(
lock.config.builtin_anims_enabled,
))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
@@ -244,7 +269,7 @@ impl CtrlAnime {
/// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32 * self.config.brightness;
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
+402 -179
View File
@@ -1,227 +1,300 @@
use std::sync::atomic::Ordering;
use std::sync::Arc;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::warn;
use logind_zbus::manager::ManagerProxy;
use rog_anime::usb::{
pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display,
pkt_set_enable_powersave_anim, AnimAwake, AnimBooting, AnimShutdown, AnimSleeping, Brightness,
pkt_set_enable_powersave_anim, Brightness,
};
use rog_anime::{AnimeDataBuffer, DeviceState};
use rog_anime::{Animations, AnimeDataBuffer, DeviceState};
use zbus::export::futures_util::lock::Mutex;
use zbus::{dbus_interface, Connection, SignalContext};
use zbus::{interface, CacheProperties, Connection, SignalContext};
use super::config::AnimeConfig;
use super::CtrlAnime;
use crate::error::RogError;
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime";
pub const ANIME_ZBUS_NAME: &str = "Anime";
pub const ANIME_ZBUS_PATH: &str = "/org/asuslinux";
async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
let connection = Connection::system()
.await
.expect("Controller could not create dbus connection");
ManagerProxy::builder(&connection)
.cache_properties(CacheProperties::No)
.build()
.await
.expect("Controller could not create ManagerProxy")
}
#[derive(Clone)]
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
/// The struct with the main dbus methods requires this trait
#[async_trait]
impl crate::ZbusRun for CtrlAnimeZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
Self::add_to_server_helper(self, ANIME_ZBUS_PATH, server).await;
}
}
// 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 until we finish.
#[dbus_interface(name = "org.asuslinux.Daemon")]
#[interface(name = "org.asuslinux.Anime")]
impl CtrlAnimeZbus {
/// 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<()> {
let lock = self.0.lock().await;
lock.thread_exit.store(true, Ordering::SeqCst);
lock.write_data_buffer(input).map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
err
})?;
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::SeqCst);
self.0
.lock()
.await
.write_data_buffer(input)
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
err
})?;
Ok(())
}
/// Set the global AniMe brightness
async fn set_image_brightness(&self, bright: f32) {
let mut lock = self.0.lock().await;
let mut bright = bright;
if bright < 0.0 {
bright = 0.0;
} else if bright > 1.0 {
bright = 1.0;
}
lock.config.brightness = bright;
lock.config.write();
/// Set base brightness level
#[zbus(property)]
async fn brightness(&self) -> Brightness {
self.0.lock().await.config.display_brightness
}
/// Set base brightness level
// TODO: enum for brightness
async fn set_brightness(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
brightness: Brightness,
) {
let mut lock = self.0.lock().await;
lock.node
#[zbus(property)]
async fn set_brightness(&self, brightness: Brightness) {
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err);
})
.ok();
lock.config.display_brightness = brightness;
lock.config.write();
Self::notify_device_state(
&ctxt,
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
},
)
.await
.ok();
self.0.lock().await.config.display_enabled = brightness != Brightness::Off;
self.0.lock().await.config.display_brightness = brightness;
self.0.lock().await.config.write();
}
/// Enable the builtin animations or not
async fn set_builtins_enabled(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: bool,
) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(enabled))
#[zbus(property)]
async fn builtins_enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.builtin_anims_enabled
}
/// Enable the builtin animations or not. This is quivalent to "Powersave
/// animations" in Armory crate
#[zbus(property)]
async fn set_builtins_enabled(&self, enabled: bool) {
let brightness = self.0.lock().await.config.display_brightness;
self.0
.lock()
.await
.node
.set_builtins_enabled(enabled, brightness)
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
lock.config.builtin_anims_enabled = enabled;
lock.config.write();
if enabled {
lock.thread_exit.store(true, Ordering::Release);
if !enabled {
let anime_type = self.0.lock().await.anime_type;
let data = vec![255u8; anime_type.data_length()];
if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
}) {
self.0
.lock()
.await
.node
.write_bytes(tmp.data())
.map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err);
})
.ok();
}
}
Self::notify_device_state(
&ctxt,
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
},
)
.await
.ok();
self.0.lock().await.config.builtin_anims_enabled = enabled;
self.0.lock().await.config.write();
if enabled {
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::Release);
}
}
#[zbus(property)]
async fn builtin_animations(&self) -> Animations {
self.0.lock().await.config.builtin_anims
}
/// Set which builtin animation is used for each stage
async fn set_builtin_animations(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
boot: AnimBooting,
awake: AnimAwake,
sleep: AnimSleeping,
shutdown: AnimShutdown,
) {
let mut lock = self.0.lock().await;
lock.node
#[zbus(property)]
async fn set_builtin_animations(&self, settings: Animations) {
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_builtin_animations(
settings.boot,
settings.awake,
settings.sleep,
settings.shutdown,
))
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(true))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
lock.node
.write_bytes(&pkt_set_builtin_animations(boot, awake, sleep, shutdown))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
lock.config.builtin_anims.boot = boot;
lock.config.builtin_anims.sleep = sleep;
lock.config.builtin_anims.awake = awake;
lock.config.builtin_anims.shutdown = shutdown;
lock.config.write();
self.0.lock().await.config.display_enabled = true;
self.0.lock().await.config.builtin_anims = settings;
self.0.lock().await.config.write();
}
Self::notify_device_state(
&ctxt,
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
},
)
.await
.ok();
#[zbus(property)]
async fn enable_display(&self) -> bool {
self.0.lock().await.config.display_enabled
}
/// Set whether the AniMe is enabled at all
async fn set_enable_display(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enabled: bool,
) {
let mut lock = self.0.lock().await;
lock.node
#[zbus(property)]
async fn set_enable_display(&self, enabled: bool) {
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(enabled))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
warn!("ctrl_anime::run_animation:callback {}", err);
})
.ok();
lock.config.display_enabled = enabled;
lock.config.write();
self.0.lock().await.config.display_enabled = enabled;
self.0.lock().await.config.write();
}
Self::notify_device_state(
&ctxt,
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
},
)
.await
.ok();
#[zbus(property)]
async fn off_when_unplugged(&self) -> bool {
self.0.lock().await.config.off_when_unplugged
}
/// Set if to turn the AniMe Matrix off when external power is unplugged
#[zbus(property)]
async fn set_off_when_unplugged(&self, enabled: bool) {
let manager = get_logind_manager().await;
let pow = manager.on_external_power().await.unwrap_or_default();
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!pow && !enabled))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
self.0.lock().await.config.off_when_unplugged = enabled;
self.0.lock().await.config.write();
}
#[zbus(property)]
async fn off_when_suspended(&self) -> bool {
self.0.lock().await.config.off_when_suspended
}
/// Set if to turn the AniMe Matrix off when the laptop is suspended
#[zbus(property)]
async fn set_off_when_suspended(&self, enabled: bool) {
self.0.lock().await.config.off_when_suspended = enabled;
self.0.lock().await.config.write();
}
#[zbus(property)]
async fn off_when_lid_closed(&self) -> bool {
self.0.lock().await.config.off_when_lid_closed
}
/// Set if to turn the AniMe Matrix off when the lid is closed
#[zbus(property)]
async fn set_off_when_lid_closed(&self, enabled: bool) {
let manager = get_logind_manager().await;
let lid = manager.lid_closed().await.unwrap_or_default();
self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(lid && !enabled))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
self.0.lock().await.config.off_when_lid_closed = enabled;
self.0.lock().await.config.write();
}
/// The main loop is the base system set action if the user isn't running
/// the user daemon
async fn run_main_loop(&self, start: bool) {
if start {
let lock = self.0.lock().await;
lock.thread_exit.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false).await;
self.0
.lock()
.await
.thread_exit
.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(
self.0.clone(),
self.0.lock().await.cache.system.clone(),
false,
)
.await;
}
}
/// Get the device state as stored by asusd
// #[dbus_interface(property)]
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let lock = self.0.lock().await;
DeviceState {
display_enabled: lock.config.display_enabled,
display_brightness: lock.config.display_brightness,
builtin_anims_enabled: lock.config.builtin_anims_enabled,
builtin_anims: lock.config.builtin_anims,
}
DeviceState::from(&self.0.lock().await.config)
}
/// Notify listeners of the status of AniMe LED power and factory
/// system-status animations
#[dbus_interface(signal)]
async fn notify_device_state(ctxt: &SignalContext<'_>, data: DeviceState) -> zbus::Result<()>;
}
#[async_trait]
impl crate::CtrlTask for CtrlAnimeZbus {
fn zbus_path() -> &'static str {
ZBUS_PATH
ANIME_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
@@ -230,36 +303,164 @@ impl crate::CtrlTask for CtrlAnimeZbus {
let inner3 = self.0.clone();
let inner4 = self.0.clone();
self.create_sys_event_tasks(
move || {
move |sleeping| {
// on_sleep
let inner1 = inner1.clone();
let inner = inner1.clone();
async move {
let lock = inner1.lock().await;
CtrlAnime::run_thread(inner1.clone(), lock.cache.sleep.clone(), true).await;
let config = inner.lock().await.config.clone();
if config.display_enabled {
inner
.lock()
.await
.thread_exit
.store(true, Ordering::Release); // ensure clean slate
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(
!(sleeping && config.off_when_suspended),
))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
if config.builtin_anims_enabled {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(
!(sleeping && config.off_when_suspended),
))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
} else if !sleeping && !config.builtin_anims_enabled {
// Run custom wake animation
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.ok(); // ensure builtins are disabled
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.wake.clone(),
true,
)
.await;
}
}
}
},
move || {
// on_wake
let inner2 = inner2.clone();
async move {
let lock = inner2.lock().await;
CtrlAnime::run_thread(inner2.clone(), lock.cache.wake.clone(), true).await;
}
},
move || {
move |shutting_down| {
// on_shutdown
let inner3 = inner3.clone();
let inner = inner2.clone();
async move {
let lock = inner3.lock().await;
CtrlAnime::run_thread(inner3.clone(), lock.cache.shutdown.clone(), true).await;
let AnimeConfig {
display_enabled,
builtin_anims_enabled,
..
} = inner.lock().await.config;
if display_enabled && !builtin_anims_enabled {
if shutting_down {
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.shutdown.clone(),
true,
)
.await;
} else {
CtrlAnime::run_thread(
inner.clone(),
inner.lock().await.cache.boot.clone(),
true,
)
.await;
}
}
}
},
move || {
// on_boot
let inner4 = inner4.clone();
move |lid_closed| {
let inner = inner3.clone();
// on lid change
async move {
let lock = inner4.lock().await;
CtrlAnime::run_thread(inner4.clone(), lock.cache.boot.clone(), true).await;
let AnimeConfig {
off_when_lid_closed,
builtin_anims_enabled,
..
} = inner.lock().await.config;
if off_when_lid_closed {
if builtin_anims_enabled {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
}
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!lid_closed))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
})
.ok();
}
}
},
move |power_plugged| {
let inner = inner4.clone();
// on power change
async move {
let AnimeConfig {
off_when_unplugged,
builtin_anims_enabled,
brightness_on_battery,
..
} = inner.lock().await.config;
if off_when_unplugged {
if builtin_anims_enabled {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err);
})
.ok();
}
inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(power_plugged))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
} else {
inner
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness_on_battery))
.map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err);
})
.ok();
}
}
},
)
@@ -269,30 +470,52 @@ impl crate::CtrlTask for CtrlAnimeZbus {
}
}
#[async_trait]
impl crate::Reloadable for CtrlAnimeZbus {
async fn reload(&mut self) -> Result<(), RogError> {
if let Some(lock) = self.0.try_lock() {
let anim = &lock.config.builtin_anims;
lock.node
.write_bytes(&pkt_set_enable_display(lock.config.display_enabled))?;
lock.node.write_bytes(&pkt_set_enable_powersave_anim(
// Set builtins
if lock.config.builtin_anims_enabled {
lock.node.write_bytes(&pkt_set_builtin_animations(
anim.boot,
anim.awake,
anim.sleep,
anim.shutdown,
))?;
}
// Builtins enabled or na?
lock.node.set_builtins_enabled(
lock.config.builtin_anims_enabled,
))?;
lock.node.write_bytes(&pkt_set_builtin_animations(
anim.boot,
anim.awake,
anim.sleep,
anim.shutdown,
))?;
lock.config.display_brightness,
)?;
if lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() {
let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default();
let turn_off = (lid_closed && lock.config.off_when_lid_closed)
|| (!power_plugged && lock.config.off_when_unplugged);
lock.node
.write_bytes(&pkt_set_enable_display(!turn_off))
.map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err);
})
.ok();
if turn_off || !lock.config.display_enabled {
lock.node.write_bytes(&pkt_set_enable_display(false))?;
// early return so we don't run animation thread
return Ok(());
}
if !lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.ok();
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true).await;
}
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true).await;
}
Ok(())
}
+85 -165
View File
@@ -1,183 +1,60 @@
use std::collections::{BTreeMap, HashSet};
use std::collections::BTreeMap;
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 log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::LaptopAuraPower;
use rog_aura::{
AuraDeviceType, 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
/// when the device is awake, suspended, shutting down or
/// booting.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuraPowerConfig {
AuraDevTuf(HashSet<AuraDevTuf>),
AuraDevRog1(HashSet<AuraDevRog1>),
AuraDevRog2(HashSet<AuraDevRog2>),
}
impl AuraPowerConfig {
/// Invalid for TUF laptops
pub fn to_bytes(control: &Self) -> [u8; 4] {
match control {
AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0, 0],
AuraPowerConfig::AuraDevRog1(c) => {
let c: Vec<AuraDevRog1> = c.iter().copied().collect();
AuraDevRog1::to_bytes(&c)
}
AuraPowerConfig::AuraDevRog2(c) => {
let c: Vec<AuraDevRog2> = c.iter().copied().collect();
AuraDevRog2::to_bytes(&c)
}
}
}
pub fn to_tuf_bool_array(control: &Self) -> Option<[bool; 5]> {
if let Self::AuraDevTuf(c) = control {
return Some([
true,
c.contains(&AuraDevTuf::Boot),
c.contains(&AuraDevTuf::Awake),
c.contains(&AuraDevTuf::Sleep),
c.contains(&AuraDevTuf::Keyboard),
]);
}
if let Self::AuraDevRog1(c) = control {
return Some([
true,
c.contains(&AuraDevRog1::Boot),
c.contains(&AuraDevRog1::Awake),
c.contains(&AuraDevRog1::Sleep),
c.contains(&AuraDevRog1::Keyboard),
]);
}
None
}
pub fn set_tuf(&mut self, power: AuraDevTuf, on: bool) {
if let Self::AuraDevTuf(p) = self {
if on {
p.insert(power);
} else {
p.remove(&power);
}
}
}
pub fn set_0x1866(&mut self, power: AuraDevRog1, on: bool) {
if let Self::AuraDevRog1(p) = self {
if on {
p.insert(power);
} else {
p.remove(&power);
}
}
}
pub fn set_0x19b6(&mut self, power: AuraDevRog2, on: bool) {
if let Self::AuraDevRog2(p) = self {
if on {
p.insert(power);
} else {
p.remove(&power);
}
}
}
}
impl From<&AuraPowerConfig> for AuraPowerDev {
fn from(config: &AuraPowerConfig) -> Self {
match config {
AuraPowerConfig::AuraDevTuf(d) => AuraPowerDev {
tuf: d.iter().copied().collect(),
x1866: vec![],
x19b6: vec![],
},
AuraPowerConfig::AuraDevRog1(d) => AuraPowerDev {
tuf: vec![],
x1866: d.iter().copied().collect(),
x19b6: vec![],
},
AuraPowerConfig::AuraDevRog2(d) => AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: d.iter().copied().collect(),
},
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
// #[serde(default)]
pub struct AuraConfig {
pub config_name: String,
pub brightness: LedBrightness,
pub current_mode: AuraModeNum,
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
pub multizone_on: bool,
pub enabled: AuraPowerConfig,
pub enabled: LaptopAuraPower,
}
impl StdConfig for AuraConfig {
/// Detect the keyboard type and load from default DB if data available
fn new() -> Self {
// Self::create_default(AuraDevice::X19b6, &LaptopLedData::get_data())
panic!("AuraConfig::new() should not be used, use AuraConfig::create_default() instead");
panic!("This should not be used");
}
fn file_name(&self) -> String {
if self.config_name.is_empty() {
panic!("Config file name should not be empty");
}
self.config_name.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
}
impl StdConfigLoad for AuraConfig {}
impl AuraConfig {
pub fn create_default(prod_id: AuraDevice, support_data: &LaptopLedData) -> Self {
/// Detect the keyboard type and load from default DB if data available
pub fn new(prod_id: &str) -> Self {
info!("Setting up AuraConfig for {prod_id:?}");
// create a default config here
let enabled = if prod_id == AuraDevice::X19b6 {
AuraPowerConfig::AuraDevRog2(HashSet::from([
AuraDevRog2::BootLogo,
AuraDevRog2::BootKeyb,
AuraDevRog2::SleepLogo,
AuraDevRog2::SleepKeyb,
AuraDevRog2::AwakeLogo,
AuraDevRog2::AwakeKeyb,
AuraDevRog2::ShutdownLogo,
AuraDevRog2::ShutdownKeyb,
AuraDevRog2::BootBar,
AuraDevRog2::AwakeBar,
AuraDevRog2::SleepBar,
AuraDevRog2::ShutdownBar,
AuraDevRog2::BootRearGlow,
AuraDevRog2::AwakeRearGlow,
AuraDevRog2::SleepRearGlow,
AuraDevRog2::ShutdownRearGlow,
]))
} else if prod_id == AuraDevice::Tuf {
AuraPowerConfig::AuraDevTuf(HashSet::from([
AuraDevTuf::Awake,
AuraDevTuf::Boot,
AuraDevTuf::Sleep,
AuraDevTuf::Keyboard,
]))
} else {
AuraPowerConfig::AuraDevRog1(HashSet::from([
AuraDevRog1::Awake,
AuraDevRog1::Boot,
AuraDevRog1::Sleep,
AuraDevRog1::Keyboard,
AuraDevRog1::Lightbar,
]))
};
let device_type = AuraDeviceType::from(prod_id);
if device_type == AuraDeviceType::Unknown {
warn!("idProduct:{prod_id:?} is unknown");
}
let support_data = LedSupportData::get_data(prod_id);
let enabled = LaptopAuraPower::new(device_type, &support_data);
let mut config = AuraConfig {
config_name: format!("aura_{prod_id}.ron"),
brightness: LedBrightness::Med,
current_mode: AuraModeNum::Static,
builtins: BTreeMap::new(),
@@ -187,6 +64,7 @@ impl AuraConfig {
};
for n in &support_data.basic_modes {
debug!("creating default for {n}");
config
.builtins
.insert(*n, AuraEffect::default_with_mode(*n));
@@ -256,18 +134,20 @@ impl AuraConfig {
#[cfg(test)]
mod tests {
use rog_aura::aura_detection::LaptopLedData;
use rog_aura::usb::AuraDevice;
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use super::AuraConfig;
#[test]
fn set_multizone_4key_config() {
let mut config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let mut config = AuraConfig::new("19b6");
let effect = AuraEffect {
colour1: Colour(0xff, 0x00, 0xff),
colour1: Colour {
r: 0xff,
g: 0x00,
b: 0xff,
},
zone: AuraZone::Key1,
..Default::default()
};
@@ -276,21 +156,33 @@ mod tests {
assert!(config.multizone.is_some());
let effect = AuraEffect {
colour1: Colour(0x00, 0xff, 0xff),
colour1: Colour {
r: 0x00,
g: 0xff,
b: 0xff,
},
zone: AuraZone::Key2,
..Default::default()
};
config.set_builtin(effect);
let effect = AuraEffect {
colour1: Colour(0xff, 0xff, 0x00),
colour1: Colour {
r: 0xff,
g: 0xff,
b: 0x00,
},
zone: AuraZone::Key3,
..Default::default()
};
config.set_builtin(effect);
let effect = AuraEffect {
colour1: Colour(0x00, 0xff, 0x00),
colour1: Colour {
r: 0x00,
g: 0xff,
b: 0x00,
},
zone: AuraZone::Key4,
..Default::default()
};
@@ -302,15 +194,43 @@ mod tests {
let res = config.multizone.unwrap();
let sta = res.get(&AuraModeNum::Static).unwrap();
assert_eq!(sta.len(), 4);
assert_eq!(sta[0].colour1, Colour(0xff, 0x00, 0xff));
assert_eq!(sta[1].colour1, Colour(0x00, 0xff, 0xff));
assert_eq!(sta[2].colour1, Colour(0xff, 0xff, 0x00));
assert_eq!(sta[3].colour1, Colour(0x00, 0xff, 0x00));
assert_eq!(
sta[0].colour1,
Colour {
r: 0xff,
g: 0x00,
b: 0xff
}
);
assert_eq!(
sta[1].colour1,
Colour {
r: 0x00,
g: 0xff,
b: 0xff
}
);
assert_eq!(
sta[2].colour1,
Colour {
r: 0xff,
g: 0xff,
b: 0x00
}
);
assert_eq!(
sta[3].colour1,
Colour {
r: 0x00,
g: 0xff,
b: 0x00
}
);
}
#[test]
fn set_multizone_multimode_config() {
let mut config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let mut config = AuraConfig::new("19b6");
let effect = AuraEffect {
zone: AuraZone::Key1,
+195 -295
View File
@@ -1,128 +1,185 @@
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashSet};
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 inotify::Inotify;
use log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::{LedUsbPackets, UsbPackets};
use rog_aura::usb::{LED_APPLY, LED_SET};
use rog_aura::{
AuraDeviceType, AuraEffect, 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 zbus::zvariant::OwnedObjectPath;
use super::config::{AuraConfig, AuraPowerConfig};
use super::config::AuraConfig;
use crate::ctrl_aura::manager::{dbus_path_for_dev, dbus_path_for_tuf};
use crate::error::RogError;
use crate::GetSupported;
impl GetSupported for CtrlKbdLed {
type A = LedSupportedFunctions;
#[derive(Debug)]
pub enum LEDNode {
/// Brightness and/or TUF RGB controls
KbdLed(KeyboardLed),
/// Raw HID handle
Rog(KeyboardLed, HidRaw),
}
fn get_supported() -> Self::A {
// let mode = <&str>::from(&<AuraModes>::from(*mode));
let laptop = LaptopLedData::get_data();
let stock_led_modes = laptop.basic_modes;
let multizone_led_mode = laptop.basic_zones;
let advanced_type = laptop.advanced_type;
let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES {
if HidRaw::new(prod.into()).is_ok() {
prod_id = prod;
break;
}
impl LEDNode {
// TODO: move various methods upwards to this
pub fn set_brightness(&self, value: u8) -> Result<(), RogError> {
match self {
LEDNode::KbdLed(k) => k.set_brightness(value)?,
LEDNode::Rog(k, _) => k.set_brightness(value)?,
}
Ok(())
}
let rgb = KeyboardLed::new();
if let Ok(p) = rgb.as_ref() {
if p.has_kbd_rgb_mode() {
prod_id = AuraDevice::Tuf;
}
}
pub fn get_brightness(&self) -> Result<u8, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.get_brightness()?,
LEDNode::Rog(k, _) => k.get_brightness()?,
})
}
LedSupportedFunctions {
dev_id: prod_id,
brightness: rgb.is_ok(),
basic_modes: stock_led_modes,
basic_zones: multizone_led_mode,
advanced_type: advanced_type.into(),
}
pub fn monitor_brightness(&self) -> Result<Inotify, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.monitor_brightness()?,
LEDNode::Rog(k, _) => k.monitor_brightness()?,
})
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd)]
pub enum LEDNode {
KbdLed(KeyboardLed),
Rog(HidRaw),
None,
}
/// Individual controller for one Aura device
pub struct CtrlKbdLed {
// TODO: config stores the keyboard type as an AuraPower, use or update this
pub led_prod: AuraDevice,
pub led_type: AuraDeviceType,
pub led_node: LEDNode,
pub kd_brightness: KeyboardLed,
pub supported_modes: LaptopLedData,
pub flip_effect_write: bool,
pub supported_data: LedSupportData, // TODO: is storing this really required?
pub per_key_mode_active: bool,
pub config: AuraConfig,
pub dbus_path: OwnedObjectPath,
}
impl CtrlKbdLed {
pub fn new(supported_modes: LaptopLedData) -> Result<Self, RogError> {
let mut led_prod = AuraDevice::Unknown;
let mut usb_node = None;
for prod in ASUS_KEYBOARD_DEVICES {
match HidRaw::new(prod.into()) {
Ok(node) => {
led_prod = prod;
usb_node = Some(node);
info!(
"Looked for keyboard controller 0x{}: Found",
<&str>::from(prod)
);
break;
pub fn find_all() -> Result<Vec<Self>, RogError> {
info!("Searching for all Aura devices");
let mut devices = Vec::new();
let mut found = HashSet::new(); // track and ensure we use only one hidraw per prod_id
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
err
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
err
})?;
for end_point in enumerator.scan_devices()? {
// usb_device gives us a product and vendor ID
if let Some(usb_device) =
end_point.parent_with_subsystem_devtype("usb", "usb_device")?
{
// The asus_wmi driver latches MCU that controls the USB endpoints
if let Some(parent) = end_point.parent() {
if let Some(driver) = parent.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
continue;
}
} else {
continue;
}
}
Err(err) => info!(
"Looked for keyboard controller 0x{}: {err}",
<&str>::from(prod)
),
// Device is something like 002, while its parent is the MCU
// Think of it like the device is an endpoint of the USB device attached
let mut prod_id = String::new();
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
prod_id = usb_id.to_string_lossy().to_string();
let aura_dev = AuraDeviceType::from(prod_id.as_str());
if aura_dev == AuraDeviceType::Unknown || found.contains(&aura_dev) {
log::debug!("Unknown or invalid device: {usb_id:?}, skipping");
continue;
}
found.insert(aura_dev);
}
let dev_node = if let Some(dev_node) = usb_device.devnode() {
dev_node
} else {
debug!("Device has no devnode, skipping");
continue;
};
info!("AuraControl found device at: {:?}", dev_node);
let dbus_path = dbus_path_for_dev(&usb_device).unwrap_or_default();
let dev = HidRaw::from_device(end_point)?;
let mut dev = Self::from_hidraw(dev, dbus_path)?;
dev.config = Self::init_config(&prod_id);
devices.push(dev);
}
}
// Check for a TUF laptop LED. Assume there is only ever one.
if let Ok(kbd_backlight) = KeyboardLed::new() {
if kbd_backlight.has_kbd_rgb_mode() {
info!("AuraControl found a TUF laptop keyboard");
let ctrl = CtrlKbdLed {
led_type: AuraDeviceType::LaptopTuf,
led_node: LEDNode::KbdLed(kbd_backlight),
supported_data: LedSupportData::get_data("tuf"),
per_key_mode_active: false,
config: Self::init_config("tuf"),
dbus_path: dbus_path_for_tuf(),
};
devices.push(ctrl);
}
}
info!("Found {} Aura devices", devices.len());
Ok(devices)
}
/// The generated data from this function has a default config. This config
/// should be overwritten. The reason for the default config is because
/// of async issues between this and udev/hidraw
pub fn from_hidraw(device: HidRaw, dbus_path: OwnedObjectPath) -> Result<Self, RogError> {
let rgb_led = KeyboardLed::new()?;
if usb_node.is_none() && !rgb_led.has_kbd_rgb_mode() {
let dmi = sysfs_class::DmiId::default();
if let Ok(prod_family) = dmi.product_family() {
if prod_family.contains("TUF") {
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);
let prod_id = AuraDeviceType::from(device.prod_id());
if prod_id == AuraDeviceType::Unknown {
log::error!("{} is AuraDevice::Unknown", device.prod_id());
return Err(RogError::NoAuraNode);
}
let led_node = if let Some(rog) = usb_node {
info!("Found ROG USB keyboard");
LEDNode::Rog(rog)
} else if rgb_led.has_kbd_rgb_mode() {
info!("Found TUF keyboard");
LEDNode::KbdLed(rgb_led.clone())
} else {
LEDNode::None
// New loads data from the DB also
// let config = Self::init_config(prod_id, data);
let data = LedSupportData::get_data(device.prod_id());
let ctrl = CtrlKbdLed {
led_type: prod_id,
led_node: LEDNode::Rog(rgb_led, device),
supported_data: data.clone(),
per_key_mode_active: false,
config: AuraConfig::default(),
dbus_path,
};
Ok(ctrl)
}
let mut config_init = AuraConfig::create_default(led_prod, &supported_modes);
pub fn init_config(prod_id: &str) -> AuraConfig {
// New loads data from the DB also
let mut config_init = AuraConfig::new(prod_id);
// config_init.set_filename(prod_id);
let mut config_loaded = config_init.clone().load();
// update the initialised data with what we loaded from disk
for mode in &mut config_init.builtins {
// update init values from loaded values if they exist
if let Some(loaded) = config_loaded.builtins.get(mode.0) {
*mode.1 = loaded.clone();
}
}
// Then replace just incase the initialised data contains new modes added
config_loaded.builtins = config_init.builtins;
if let (Some(mut multizone_init), Some(multizone_loaded)) =
@@ -132,9 +189,10 @@ impl CtrlKbdLed {
// update init values from loaded values if they exist
if let Some(loaded) = multizone_loaded.get(mode.0) {
let mut new_set = Vec::new();
let data = LedSupportData::get_data(prod_id);
// only reuse a zone mode if the mode is supported
for mode in loaded {
if supported_modes.basic_modes.contains(&mode.mode) {
if data.basic_modes.contains(&mode.mode) {
new_set.push(mode.clone());
}
}
@@ -144,62 +202,21 @@ impl CtrlKbdLed {
*multizone_loaded = multizone_init;
}
let ctrl = CtrlKbdLed {
led_prod,
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
supported_modes,
flip_effect_write: false,
per_key_mode_active: false,
config: config_loaded,
};
Ok(ctrl)
}
pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
self.kd_brightness
.get_brightness()
.map_err(RogError::Platform)
}
pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> {
self.kd_brightness
.set_brightness(brightness as u8)
.map_err(RogError::Platform)
}
pub fn next_brightness(&mut self) -> Result<(), RogError> {
let mut bright = (self.config.brightness as u32) + 1;
if bright > 3 {
bright = 0;
}
self.config.brightness = <LedBrightness>::from(bright);
self.config.write();
self.set_brightness(self.config.brightness)
}
pub fn prev_brightness(&mut self) -> Result<(), RogError> {
let mut bright = self.config.brightness as u32;
if bright == 0 {
bright = 3;
} else {
bright -= 1;
}
self.config.brightness = <LedBrightness>::from(bright);
self.config.write();
self.set_brightness(self.config.brightness)
config_loaded
}
/// 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> {
if let LEDNode::KbdLed(platform) = &mut self.led_node {
if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) {
let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8, pwr[4] as u8];
platform.set_kbd_rgb_state(&buf)?;
}
} else if let LEDNode::Rog(hid_raw) = &self.led_node {
let bytes = AuraPowerConfig::to_bytes(&self.config.enabled);
if let LEDNode::KbdLed(_platform) = &mut self.led_node {
// TODO: tuf bool array
// if let Some(pwr) =
// AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) {
// let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8,
// pwr[4] as u8]; platform.set_kbd_rgb_state(&buf)?;
// }
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes = self.config.enabled.to_bytes(self.led_type);
let message = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]];
hid_raw.write_bytes(&message)?;
@@ -210,29 +227,6 @@ impl CtrlKbdLed {
Ok(())
}
/// 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 stored and config written to disk.
pub(crate) fn set_effect(&mut self, effect: AuraEffect) -> Result<(), RogError> {
if !self.supported_modes.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !self.supported_modes.basic_zones.contains(&effect.zone)
{
return Err(RogError::AuraEffectNotSupported);
}
self.write_mode(&effect)?;
self.config.read(); // refresh config if successful
self.config.set_builtin(effect);
if self.config.brightness == LedBrightness::Off {
self.config.brightness = LedBrightness::Med;
}
self.config.write();
self.set_brightness(self.config.brightness)?;
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
@@ -247,20 +241,20 @@ impl CtrlKbdLed {
if pkt_type != PER_KEY_TYPE {
self.per_key_mode_active = false;
if let LEDNode::Rog(hid_raw) = &self.led_node {
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
}
} else {
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 = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
}
self.per_key_mode_active = true;
}
if let LEDNode::Rog(hid_raw) = &self.led_node {
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
@@ -272,58 +266,22 @@ impl CtrlKbdLed {
tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
}
}
self.flip_effect_write = !self.flip_effect_write;
}
Ok(())
}
pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> {
let current = self.config.current_mode;
if let Some(idx) = self
.supported_modes
.basic_modes
.iter()
.position(|v| *v == current)
{
let mut idx = idx;
// goes past end of array
if reverse {
if idx == 0 {
idx = self.supported_modes.basic_modes.len() - 1;
} else {
idx -= 1;
}
} else {
idx += 1;
if idx == self.supported_modes.basic_modes.len() {
idx = 0;
}
}
let next = self.supported_modes.basic_modes[idx];
self.config.read();
// if self.config.builtins.contains_key(&next) {
self.config.current_mode = next;
self.write_current_config_mode()?;
// }
self.config.write();
}
Ok(())
}
fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
pub fn write_mode(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
if let LEDNode::KbdLed(platform) = &self.led_node {
let buf = [
1,
mode.mode as u8,
mode.colour1.0,
mode.colour1.1,
mode.colour1.2,
mode.colour1.r,
mode.colour1.g,
mode.colour1.b,
mode.speed as u8,
];
platform.set_kbd_rgb_mode(&buf)?;
} else if let LEDNode::Rog(hid_raw) = &self.led_node {
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes: [u8; LED_MSG_LEN] = mode.into();
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&LED_SET)?;
@@ -376,7 +334,7 @@ impl CtrlKbdLed {
/// exists.
fn create_multizone_default(&mut self) -> Result<(), RogError> {
let mut default = vec![];
for (i, tmp) in self.supported_modes.basic_zones.iter().enumerate() {
for (i, tmp) in self.supported_data.basic_zones.iter().enumerate() {
default.push(AuraEffect {
mode: self.config.current_mode,
zone: *tmp,
@@ -403,106 +361,45 @@ impl CtrlKbdLed {
#[cfg(test)]
mod tests {
use rog_aura::aura_detection::LaptopLedData;
use rog_aura::usb::AuraDevice;
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour};
use rog_aura::aura_detection::LedSupportData;
use rog_aura::{AuraDeviceType, AuraModeNum, AuraZone, PowerZones};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardLed;
use zbus::zvariant::OwnedObjectPath;
use super::CtrlKbdLed;
use crate::ctrl_aura::config::AuraConfig;
use crate::ctrl_aura::controller::LEDNode;
#[test]
// #[ignore = "Must be manually run due to detection stage"]
fn check_set_mode_errors() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let supported_modes = LaptopLedData {
board_name: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: rog_aura::AdvancedAuraType::None,
};
let mut controller = CtrlKbdLed {
led_prod: AuraDevice::X19b6,
led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(),
supported_modes,
flip_effect_write: false,
per_key_mode_active: false,
config,
};
let mut effect = AuraEffect {
colour1: Colour(0xff, 0x00, 0xff),
zone: AuraZone::None,
..Default::default()
};
// This error comes from write_bytes because we don't have a keyboard node
// stored
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"No supported Aura keyboard"
);
effect.mode = AuraModeNum::Laser;
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"Aura effect not supported"
);
effect.mode = AuraModeNum::Static;
effect.zone = AuraZone::Key2;
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
"Aura effect not supported"
);
controller.supported_modes.basic_zones.push(AuraZone::Key2);
assert_eq!(
controller.set_effect(effect).unwrap_err().to_string(),
"No supported Aura keyboard"
);
}
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
fn create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let supported_modes = LaptopLedData {
board_name: String::new(),
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: rog_aura::AdvancedAuraType::None,
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_prod: AuraDevice::X19b6,
led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(),
supported_modes,
flip_effect_write: false,
led_type: AuraDeviceType::LaptopPost2021,
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("19b6").unwrap()),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
assert!(controller.create_multizone_default().is_err());
assert!(controller.config.multizone.is_none());
controller.supported_modes.basic_zones.push(AuraZone::Key1);
controller.supported_modes.basic_zones.push(AuraZone::Key2);
controller.supported_data.basic_zones.push(AuraZone::Key1);
controller.supported_data.basic_zones.push(AuraZone::Key2);
assert!(controller.create_multizone_default().is_ok());
assert!(controller.config.multizone.is_some());
@@ -515,24 +412,27 @@ mod tests {
}
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
// TODO: use sim device
fn next_mode_create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::create_default(AuraDevice::X19b6, &LaptopLedData::default());
let supported_modes = LaptopLedData {
board_name: String::new(),
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![AuraZone::Key1, AuraZone::Key2],
advanced_type: rog_aura::AdvancedAuraType::None,
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_prod: AuraDevice::X19b6,
led_node: LEDNode::None,
kd_brightness: KeyboardLed::default(),
supported_modes,
flip_effect_write: false,
led_type: AuraDeviceType::LaptopPost2021,
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("19b6").unwrap()),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
+187
View File
@@ -0,0 +1,187 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::collections::HashSet;
use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_aura::AuraDeviceType;
use rog_platform::hid_raw::HidRaw;
use tokio::task::spawn_blocking;
use udev::{Device, MonitorBuilder};
use zbus::object_server::SignalContext;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::ctrl_aura::controller::CtrlKbdLed;
use crate::ctrl_aura::trait_impls::{CtrlAuraZbus, AURA_ZBUS_PATH};
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub struct AuraManager {
_connection: Connection,
}
impl AuraManager {
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let mut interfaces = HashSet::new();
// Do the initial keyboard detection:
let all = CtrlKbdLed::find_all()?;
for ctrl in all {
let path = ctrl.dbus_path.clone();
interfaces.insert(path.clone()); // ensure we record the initial stuff
let sig_ctx = CtrlAuraZbus::signal_context(&connection)?;
let sig_ctx2 = sig_ctx.clone();
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
start_tasks(zbus, connection.clone(), sig_ctx2, path).await?;
}
let manager = Self {
_connection: connection,
};
// detect all plugged in aura devices (eventually)
// only USB devices are detected for here
spawn_blocking(move || {
let mut monitor = MonitorBuilder::new()?.match_subsystem("hidraw")?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let parent = if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
parent
} else {
continue;
};
let action = if let Some(action) = event.action() {
action
} else {
continue;
};
let id_product = if let Some(id_product) = parent.attribute_value("idProduct") {
id_product.to_string_lossy()
} else {
continue;
};
let path = if let Some(path) = dbus_path_for_dev(&parent) {
path
} else {
continue;
};
let aura_device = AuraDeviceType::from(&*id_product);
if aura_device == AuraDeviceType::Unknown {
warn!("idProduct:{id_product:?} is unknown, not using");
continue;
}
if action == "remove" {
if interfaces.remove(&path) {
info!("AuraManager removing: {path:?}");
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
let res = conn_copy
.object_server()
.remove::<CtrlAuraZbus, _>(&path)
.await
.map_err(|e| {
error!("Failed to remove {path:?}, {e:?}");
e
})?;
info!("AuraManager removed: {path:?}, {res}");
Ok::<(), RogError>(())
});
}
} else if action == "add" {
if interfaces.contains(&path) {
debug!("Already a ctrl at {path:?}");
continue;
}
// Need to check the driver is asus to prevent using hid_generic
if let Some(p2) = event.parent() {
if let Some(driver) = p2.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
debug!("{id_product:?} driver was not asus, skipping");
continue;
}
} else {
continue;
}
}
if let Some(dev_node) = event.devnode() {
if let Ok(raw) = HidRaw::from_device(event.device())
.map_err(|e| error!("device path error: {e:?}"))
{
if let Ok(mut ctrl) = CtrlKbdLed::from_hidraw(raw, path.clone()) {
ctrl.config = CtrlKbdLed::init_config(&id_product);
interfaces.insert(path.clone());
info!("AuraManager starting device at: {dev_node:?}, {path:?}");
let sig_ctx = CtrlAuraZbus::signal_context(&conn_copy)?;
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
let sig_ctx = CtrlAuraZbus::signal_context(&conn_copy)?;
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
start_tasks(zbus, conn_copy.clone(), sig_ctx, path).await
});
}
}
}
};
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}
pub(crate) fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = super::filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/{filename}")).into(),
);
}
None
}
pub(crate) fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/tuf")).into()
}
async fn start_tasks(
mut zbus: CtrlAuraZbus,
connection: Connection,
_signal_ctx: SignalContext<'static>,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
zbus.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection.object_server().at(path, zbus).await.unwrap();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}
+25
View File
@@ -1,4 +1,29 @@
use udev::Device;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
pub mod config;
pub mod controller;
pub mod manager;
/// Implements `CtrlTask`, `Reloadable`, `ZbusRun`
pub mod trait_impls;
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub(super) fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}
+203 -244
View File
@@ -1,313 +1,270 @@
use std::collections::BTreeMap;
use std::sync::Arc;
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 log::{debug, error, info, warn};
use rog_aura::keyboard::{LaptopAuraPower, UsbPackets};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::export::futures_util::lock::{Mutex, MutexGuard};
use zbus::export::futures_util::StreamExt;
use zbus::{dbus_interface, Connection, SignalContext};
use zbus::fdo::Error as ZbErr;
use zbus::{interface, SignalContext};
use super::controller::CtrlKbdLed;
use crate::error::RogError;
use crate::CtrlTask;
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Aura";
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlKbdLedZbus(pub Arc<Mutex<CtrlKbdLed>>);
pub struct CtrlAuraZbus(Arc<Mutex<CtrlKbdLed>>, SignalContext<'static>);
impl CtrlAuraZbus {
pub fn new(controller: CtrlKbdLed, signal: SignalContext<'static>) -> Self {
Self(Arc::new(Mutex::new(controller)), signal)
}
impl CtrlKbdLedZbus {
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let bright = lock.kd_brightness.get_brightness()?;
let bright = lock.led_node.get_brightness()?;
lock.config.read();
lock.config.brightness = (bright as u32).into();
lock.config.brightness = bright.into();
lock.config.write();
Ok(())
}
}
#[async_trait]
impl crate::ZbusRun for CtrlKbdLedZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
}
}
/// The main interface for changing, reading, or notfying signals
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlKbdLedZbus {
/// Set the keyboard brightness level (0-3)
async fn set_brightness(&mut self, brightness: LedBrightness) {
#[interface(name = "org.asuslinux.Aura")]
impl CtrlAuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
let ctrl = self.0.lock().await;
ctrl.set_brightness(brightness)
.map_err(|err| warn!("{}", err))
.ok();
ctrl.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.get_brightness().map(|n| n.into())?)
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.set_brightness(brightness.into())?)
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.current_mode)
}
/// 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 stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.config.current_mode = num;
ctrl.write_current_config_mode()?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.write();
self.led_mode_data_invalidate(&self.1).await.ok();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
let ctrl = self.0.lock().await;
let mode = ctrl.config.current_mode;
match ctrl.config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
if !ctrl.supported_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !ctrl.supported_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
ctrl.write_mode(&effect)?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
ctrl.config.set_builtin(effect);
ctrl.config.write();
self.led_mode_invalidate(&self.1).await.ok();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let ctrl = self.0.lock().await;
ctrl.config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let ctrl = self.0.lock().await;
ctrl.config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// ```text
/// pub struct AuraPowerDev {
/// pub x1866: Vec<AuraDev1866>,
/// pub x19b6: Vec<AuraDev19b6>,
/// }
/// pub enum AuraDev1866 {
/// Awake,
/// Keyboard,
/// Lightbar,
/// Boot,
/// Sleep,
/// }
/// enum AuraDev19b6 {
/// BootLogo,
/// BootKeyb,
/// AwakeLogo,
/// AwakeKeyb,
/// SleepLogo,
/// SleepKeyb,
/// ShutdownLogo,
/// ShutdownKeyb,
/// AwakeBar,
/// BootBar,
/// SleepBar,
/// ShutdownBar,
/// BootRearBar,
/// AwakeRearBar,
/// SleepRearBar,
/// ShutdownRearBar,
/// }
/// ```
async fn set_leds_power(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
options: AuraPowerDev,
enabled: bool,
) -> zbus::fdo::Result<()> {
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
for p in options.tuf {
ctrl.config.enabled.set_tuf(p, enabled);
for opt in options.states {
let zone = opt.zone;
for config in ctrl.config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
for p in options.x1866 {
ctrl.config.enabled.set_0x1866(p, enabled);
}
for p in options.x19b6 {
ctrl.config.enabled.set_0x19b6(p, enabled);
}
ctrl.config.write();
ctrl.set_power_states().map_err(|e| {
Ok(ctrl.set_power_states().map_err(|e| {
warn!("{}", e);
e
})?;
Self::notify_power_states(&ctxt, &AuraPowerDev::from(&ctrl.config.enabled))
.await
.unwrap_or_else(|err| warn!("{}", err));
Ok(())
}
async fn set_led_mode(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
effect: AuraEffect,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.set_effect(effect).map_err(|e| {
warn!("{}", e);
e
})?;
ctrl.set_brightness(ctrl.config.brightness).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
Self::notify_led(&ctxt, mode.clone())
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn next_led_mode(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.toggle_mode(false).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
Self::notify_led(&ctxt, mode.clone())
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn prev_led_mode(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.toggle_mode(true).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
Self::notify_led(&ctxt, mode.clone())
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn next_led_brightness(&self) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.next_brightness().map_err(|e| {
warn!("{}", e);
e
})?;
Ok(())
}
async fn prev_led_brightness(&self) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.prev_brightness().map_err(|e| {
warn!("{}", e);
e
})?;
Ok(())
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
// #[dbus_interface(property)]
async fn leds_enabled(&self) -> AuraPowerDev {
let ctrl = self.0.lock().await;
AuraPowerDev::from(&ctrl.config.enabled)
}
/// Return the current mode data
async fn led_mode(&self) -> AuraModeNum {
let ctrl = self.0.lock().await;
ctrl.config.current_mode
}
/// Return a list of available modes
async fn led_modes(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let ctrl = self.0.lock().await;
ctrl.config.builtins.clone()
})?)
}
/// 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<()> {
async fn direct_addressing_raw(&self, data: UsbPackets) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.write_effect_block(&data)?;
Ok(())
}
/// Return the current LED brightness
#[dbus_interface(property)]
async fn led_brightness(&self) -> i8 {
let ctrl = self.0.lock().await;
ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1)
}
#[dbus_interface(signal)]
async fn notify_led(signal_ctxt: &SignalContext<'_>, data: AuraEffect) -> zbus::Result<()>;
#[dbus_interface(signal)]
async fn notify_power_states(
signal_ctxt: &SignalContext<'_>,
data: &AuraPowerDev,
) -> zbus::Result<()>;
}
#[async_trait]
impl CtrlTask for CtrlKbdLedZbus {
impl CtrlTask for CtrlAuraZbus {
fn zbus_path() -> &'static str {
ZBUS_PATH
"/org/asuslinux"
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let load_save = |start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
lock.set_brightness(lock.config.brightness)
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
.ok();
lock.write_current_config_mode()
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
.ok();
} else if start {
info!("CtrlKbdLedTask saving last brightness");
Self::update_config(&mut lock)
.map_err(|e| error!("CtrlKbdLedTask: {e}"))
.ok();
}
};
let load_save =
|start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| -> Result<(), RogError> {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
lock.led_node
.set_brightness(lock.config.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
lock.write_current_config_mode().map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
} else if start {
Self::update_config(&mut lock).map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
}
Ok(())
};
let inner1 = self.0.clone();
let inner2 = self.0.clone();
let inner3 = self.0.clone();
let inner4 = self.0.clone();
self.create_sys_event_tasks(
// Loop so that we do aquire the lock but also don't block other
// threads (prevents potential deadlocks)
move || {
move |sleeping| {
let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
load_save(true, lock);
load_save(sleeping, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move || {
let inner2 = inner2.clone();
async move {
let lock = inner2.lock().await;
load_save(false, lock);
}
},
move || {
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
let lock = inner3.lock().await;
load_save(false, lock);
load_save(false, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move || {
let inner4 = inner4.clone();
async move {
let lock = inner4.lock().await;
load_save(false, lock);
}
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
},
)
.await;
let ctrl2 = self.0.clone();
let ctrl = self.0.lock().await;
let watch = ctrl.kd_brightness.monitor_brightness()?;
let watch = ctrl.led_node.monitor_brightness()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
@@ -315,7 +272,8 @@ impl CtrlTask for CtrlKbdLedZbus {
.unwrap()
.for_each(|_| async {
if let Some(lock) = ctrl2.try_lock() {
load_save(true, lock);
load_save(true, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
})
.await;
@@ -325,11 +283,12 @@ impl CtrlTask for CtrlKbdLedZbus {
}
}
#[async_trait]
impl crate::Reloadable for CtrlKbdLedZbus {
impl crate::Reloadable for CtrlAuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
debug!("reloading keyboard mode");
ctrl.write_current_config_mode()?;
debug!("reloading power states");
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
Ok(())
}
+286
View File
@@ -0,0 +1,286 @@
use std::path::PathBuf;
use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad};
use futures_lite::StreamExt;
use log::{debug, error, info, warn};
use rog_platform::platform::{RogPlatform, ThrottlePolicy};
use rog_profiles::error::ProfileError;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::{find_fan_curve_node, FanCurvePU, FanCurveProfiles};
use serde_derive::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zbus::{interface, Connection, SignalContext};
use crate::error::RogError;
use crate::{CtrlTask, CONFIG_PATH_BASE};
pub const FAN_CURVE_ZBUS_NAME: &str = "FanCurves";
pub const FAN_CURVE_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Deserialize, Serialize, Debug, Default)]
pub struct FanCurveConfig {
pub profiles: FanCurveProfiles,
#[serde(skip)]
pub current: u8,
}
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 file_name(&self) -> String {
"fan_curves.ron".to_owned()
}
fn config_dir() -> std::path::PathBuf {
PathBuf::from(CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for FanCurveConfig {}
#[derive(Debug, Clone)]
pub struct CtrlFanCurveZbus {
config: Arc<Mutex<FanCurveConfig>>,
platform: RogPlatform,
}
// Non-zbus-derive impl
impl CtrlFanCurveZbus {
pub fn new() -> Result<Self, RogError> {
let platform = RogPlatform::new()?;
if platform.has_throttle_thermal_policy() {
info!("Device has profile control available");
find_fan_curve_node()?;
info!("Device has fan curves available");
let mut config = FanCurveConfig::new().load();
let mut fan_curves = FanCurveProfiles::default();
// Only do defaults if the config doesn't already exist\
if config.profiles.balanced.is_empty() || !config.file_path().exists() {
info!("Fetching default fan curves");
let current = platform.get_throttle_thermal_policy()?;
for this in [
ThrottlePolicy::Balanced,
ThrottlePolicy::Performance,
ThrottlePolicy::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.
platform.set_throttle_thermal_policy(this.into())?;
let mut dev = find_fan_curve_node()?;
fan_curves.set_active_curve_to_defaults(this, &mut dev)?;
info!("{this:?}:");
for curve in fan_curves.get_fan_curves_for(this) {
info!("{}", String::from(curve));
}
}
platform.set_throttle_thermal_policy(current)?;
config.profiles = fan_curves;
config.write();
} else {
info!("Fan curves previously stored, loading...");
config = config.load();
}
return Ok(Self {
config: Arc::new(Mutex::new(config)),
platform,
});
}
Err(ProfileError::NotSupported.into())
}
}
#[interface(name = "org.asuslinux.FanCurves")]
impl CtrlFanCurveZbus {
/// Set all fan curves for a profile to enabled status. Will also activate a
/// fan curve if in the same profile mode
async fn set_fan_curves_enabled(
&mut self,
profile: ThrottlePolicy,
enabled: bool,
) -> zbus::fdo::Result<()> {
self.config
.lock()
.await
.profiles
.set_profile_curves_enabled(profile, enabled);
self.config
.lock()
.await
.profiles
.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
self.config.lock().await.write();
Ok(())
}
/// Set a single fan curve for a profile to enabled status. Will also
/// activate a fan curve if in the same profile mode
async fn set_profile_fan_curve_enabled(
&mut self,
profile: ThrottlePolicy,
fan: FanCurvePU,
enabled: bool,
) -> zbus::fdo::Result<()> {
self.config
.lock()
.await
.profiles
.set_profile_fan_curve_enabled(profile, fan, enabled);
self.config
.lock()
.await
.profiles
.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
self.config.lock().await.write();
Ok(())
}
/// Get the fan-curve data for the currently active ThrottlePolicy
async fn fan_curve_data(
&mut self,
profile: ThrottlePolicy,
) -> zbus::fdo::Result<Vec<CurveData>> {
let curve = self
.config
.lock()
.await
.profiles
.get_fan_curves_for(profile)
.to_vec();
Ok(curve)
}
/// Set the fan curve for the specified profile.
/// Will also activate the fan curve if the user is in the same mode.
async fn set_fan_curve(
&mut self,
profile: ThrottlePolicy,
curve: CurveData,
) -> zbus::fdo::Result<()> {
self.config
.lock()
.await
.profiles
.save_fan_curve(curve, profile)?;
let active: ThrottlePolicy = self.platform.get_throttle_thermal_policy()?.into();
if active == profile {
self.config
.lock()
.await
.profiles
.write_profile_curve_to_platform(profile, &mut find_fan_curve_node()?)?;
}
self.config.lock().await.write();
Ok(())
}
/// Reset the stored (self) and device curves to the defaults of the
/// platform.
///
/// Each platform_profile has a different default and the default can be
/// read only for the currently active profile.
async fn set_curves_to_defaults(&mut self, profile: ThrottlePolicy) -> zbus::fdo::Result<()> {
let active = self.platform.get_throttle_thermal_policy()?;
self.platform.set_throttle_thermal_policy(profile.into())?;
self.config
.lock()
.await
.profiles
.set_active_curve_to_defaults(profile, &mut find_fan_curve_node()?)?;
self.platform.set_throttle_thermal_policy(active)?;
self.config.lock().await.write();
Ok(())
}
/// Reset the stored (self) and device curve to the defaults of the
/// platform.
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn reset_profile_curves(&self, profile: ThrottlePolicy) -> zbus::fdo::Result<()> {
let active = self.platform.get_throttle_thermal_policy()?;
self.platform.set_throttle_thermal_policy(profile.into())?;
self.config
.lock()
.await
.profiles
.set_active_curve_to_defaults(active.into(), &mut find_fan_curve_node()?)?;
self.platform.set_throttle_thermal_policy(active)?;
self.config.lock().await.write();
Ok(())
}
}
impl crate::ZbusRun for CtrlFanCurveZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, FAN_CURVE_ZBUS_PATH, server).await;
}
}
impl CtrlTask for CtrlFanCurveZbus {
fn zbus_path() -> &'static str {
FAN_CURVE_ZBUS_PATH
}
async fn create_tasks(&self, _signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let watch_throttle_thermal_policy = self.platform.monitor_throttle_thermal_policy()?;
let platform = self.platform.clone();
let config = self.config.clone();
let fan_curves = self.config.clone();
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(mut stream) = watch_throttle_thermal_policy.into_event_stream(&mut buffer) {
while (stream.next().await).is_some() {
debug!("watch_throttle_thermal_policy changed");
if let Ok(profile) = platform.get_throttle_thermal_policy().map_err(|e| {
error!("get_throttle_thermal_policy error: {e}");
}) {
if profile != config.lock().await.current {
fan_curves
.lock()
.await
.profiles
.write_profile_curve_to_platform(
profile.into(),
&mut find_fan_curve_node().unwrap(),
)
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
.ok();
config.lock().await.current = profile;
}
}
}
}
});
Ok(())
}
}
impl crate::Reloadable for CtrlFanCurveZbus {
/// Fetch the active profile and use that to set all related components up
async fn reload(&mut self) -> Result<(), RogError> {
let active = self.platform.get_throttle_thermal_policy()?.into();
let mut config = self.config.lock().await;
if let Ok(mut device) = find_fan_curve_node() {
config
.profiles
.write_profile_curve_to_platform(active, &mut device)?;
}
Ok(())
}
}
+884 -273
View File
File diff suppressed because it is too large Load Diff
-287
View File
@@ -1,287 +0,0 @@
use std::process::Command;
use std::sync::Arc;
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 zbus::export::futures_util::lock::Mutex;
use zbus::{dbus_interface, Connection, SignalContext};
use crate::config::Config;
use crate::error::RogError;
use crate::{CtrlTask, GetSupported};
const ZBUS_PATH: &str = "/org/asuslinux/Power";
const NVIDIA_POWERD: &str = "nvidia-powerd.service";
impl GetSupported for CtrlPower {
type A = ChargeSupportedFunctions;
fn get_supported() -> Self::A {
ChargeSupportedFunctions {
charge_level_set: if let Ok(power) = AsusPower::new() {
power.has_charge_control_end_threshold()
} else {
false
},
}
}
}
#[derive(Clone)]
pub struct CtrlPower {
power: AsusPower,
config: Arc<Mutex<Config>>,
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlPower {
async fn set_charge_control_end_threshold(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
limit: u8,
) -> zbus::fdo::Result<()> {
if !(20..=100).contains(&limit) {
return Err(RogError::ChargeLimit(limit))?;
}
self.set(limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
Self::notify_charge_control_end_threshold(&ctxt, limit)
.await
.ok();
Ok(())
}
fn charge_control_end_threshold(&self) -> u8 {
loop {
if let Some(mut config) = self.config.try_lock() {
let limit = self
.power
.get_charge_control_end_threshold()
.map_err(|err| {
warn!("CtrlCharge: get_charge_control_end_threshold {}", err);
err
})
.unwrap_or(100);
config.read();
config.bat_charge_limit = limit;
config.write();
return config.bat_charge_limit;
}
}
}
fn mains_online(&self) -> bool {
if self.power.has_online() {
if let Ok(v) = self.power.get_online() {
return v == 1;
}
}
false
}
#[dbus_interface(signal)]
async fn notify_charge_control_end_threshold(
ctxt: &SignalContext<'_>,
limit: u8,
) -> zbus::Result<()>;
#[dbus_interface(signal)]
async fn notify_mains_online(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()>;
}
#[async_trait]
impl crate::ZbusRun for CtrlPower {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
}
}
#[async_trait]
impl crate::Reloadable for CtrlPower {
async fn reload(&mut self) -> Result<(), RogError> {
if let Some(mut config) = self.config.try_lock() {
config.read();
self.set(config.bat_charge_limit)?;
}
Ok(())
}
}
impl CtrlPower {
// task_watch_item!(charge_control_end_threshold power);
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
Ok(CtrlPower {
power: AsusPower::new()?,
config,
})
}
pub(super) fn set(&self, limit: u8) -> Result<(), RogError> {
if !(20..=100).contains(&limit) {
return Err(RogError::ChargeLimit(limit));
}
self.power.set_charge_control_end_threshold(limit)?;
info!("Battery charge limit: {}", limit);
if let Some(mut config) = self.config.try_lock() {
config.read();
config.bat_charge_limit = limit;
config.write();
}
Ok(())
}
}
#[async_trait]
impl CtrlTask for CtrlPower {
fn zbus_path() -> &'static str {
ZBUS_PATH
}
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 power2 = self.clone();
self.create_sys_event_tasks(
move || async {},
move || {
let power = power1.clone();
let sysd = sysd1.clone();
async move {
info!("CtrlCharge reloading charge limit");
let lock = power.config.lock().await;
power
.set(lock.bat_charge_limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
if lock.disable_nvidia_powerd_on_battery {
if let Ok(value) = power.power.get_online() {
do_nvidia_powerd_action(&sysd, value == 1).await;
}
}
}
},
move || async {},
move || {
let power = power2.clone();
let sysd = sysd2.clone();
async move {
info!("CtrlCharge reloading charge limit");
let lock = power.config.lock().await;
power
.set(lock.bat_charge_limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
if lock.disable_nvidia_powerd_on_battery {
if let Ok(value) = power.power.get_online() {
do_nvidia_powerd_action(&sysd, value == 1).await;
}
}
}
},
)
.await;
let config = self.config.clone();
// self.watch_charge_control_end_threshold(signal_ctxt.clone())
// .await?;
let ctrl = self.clone();
tokio::spawn(async move {
let mut online = 10;
loop {
if let Ok(value) = ctrl.power.get_online() {
if online != value {
online = value;
let mut config = config.lock().await;
config.read();
if config.disable_nvidia_powerd_on_battery {
do_nvidia_powerd_action(&sysd3, value == 1).await;
}
Self::notify_mains_online(&signal_ctxt, value == 1)
.await
.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
// so we need to watch it with a thread and sleep unfortunately
sleep(Duration::from_secs(1)).await;
}
});
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();
}
}
}
}
-60
View File
@@ -1,60 +0,0 @@
use std::path::PathBuf;
use config_traits::{StdConfig, StdConfigLoad};
use rog_profiles::fan_curve_set::FanCurveSet;
use rog_profiles::Profile;
use serde_derive::{Deserialize, Serialize};
use crate::CONFIG_PATH_BASE;
const CONFIG_FILE: &str = "profile.ron";
const CONFIG_FAN_FILE: &str = "fan_curves.ron";
#[derive(Deserialize, Serialize, Debug)]
pub struct ProfileConfig {
/// For restore on boot
pub active_profile: Profile,
}
impl StdConfig for ProfileConfig {
fn new() -> Self {
Self {
active_profile: Profile::Balanced,
}
}
fn config_dir() -> std::path::PathBuf {
PathBuf::from(CONFIG_PATH_BASE)
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
}
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 {}
-175
View File
@@ -1,175 +0,0 @@
use config_traits::{StdConfig, StdConfigLoad};
use log::{info, warn};
use rog_platform::platform::AsusPlatform;
use rog_platform::supported::PlatformProfileFunctions;
use rog_profiles::error::ProfileError;
use rog_profiles::{FanCurveProfiles, Profile};
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 profile_config: ProfileConfig,
pub fan_curves: Option<FanCurves>,
pub platform: AsusPlatform,
}
impl GetSupported for CtrlPlatformProfile {
type A = PlatformProfileFunctions;
fn get_supported() -> Self::A {
if !Profile::is_platform_profile_supported() {
warn!(
"platform_profile kernel interface not found, your laptop does not support this, \
or the interface is missing."
);
}
let res = FanCurveProfiles::is_supported();
let mut fan_curve_supported = res.is_err();
if let Ok(r) = res {
fan_curve_supported = r;
};
if !fan_curve_supported {
info!(
"fan curves kernel interface not found, your laptop does not support this, or the \
interface is missing."
);
}
PlatformProfileFunctions {
platform_profile: Profile::is_platform_profile_supported(),
fan_curves: fan_curve_supported,
}
}
}
impl CtrlPlatformProfile {
pub fn new(config: ProfileConfig) -> Result<Self, RogError> {
let platform = AsusPlatform::new()?;
if platform.has_platform_profile() || platform.has_throttle_thermal_policy() {
info!("{MOD_NAME}: Device has profile control available");
let mut controller = CtrlPlatformProfile {
profile_config: config,
fan_curves: None,
platform,
};
if FanCurveProfiles::get_device().is_ok() {
info!("{MOD_NAME}: Device has fan curves available");
let fan_config = FanCurveConfig::new();
// 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 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.
let next =
Profile::get_next_profile(controller.profile_config.active_profile);
Profile::set_profile(next)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
controller.profile_config.active_profile = next;
// Make sure to set the baseline to default
controller.set_active_curve_to_defaults()?;
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
if let Some(curves) = controller.fan_curves.as_ref() {
info!(
"{MOD_NAME}: {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);
}
}
return Ok(controller);
}
Err(ProfileError::NotSupported.into())
}
pub fn save_config(&mut self) {
self.profile_config.write();
if let Some(fans) = self.fan_curves.as_mut() {
fans.update_config_from_profiles();
fans.config_file.write(); // config write
}
}
/// Set the curve for the active profile active
pub(super) fn write_profile_curve_to_platform(&mut self) -> Result<(), RogError> {
if let Some(curves) = &mut self.fan_curves {
if let Ok(mut device) = FanCurveProfiles::get_device() {
curves.profiles_mut().write_profile_curve_to_platform(
self.profile_config.active_profile,
&mut device,
)?;
}
}
Ok(())
}
pub(super) fn set_active_curve_to_defaults(&mut self) -> Result<(), RogError> {
if let Some(curves) = self.fan_curves.as_mut() {
if let Ok(mut device) = FanCurveProfiles::get_device() {
curves.profiles_mut().set_active_curve_to_defaults(
self.profile_config.active_profile,
&mut device,
)?;
curves.update_config_from_profiles();
}
}
Ok(())
}
}
-4
View File
@@ -1,4 +0,0 @@
pub mod config;
pub mod controller;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
-316
View File
@@ -1,316 +0,0 @@
use std::str::FromStr;
use std::sync::Arc;
use async_trait::async_trait;
use config_traits::StdConfig;
use log::{error, info, warn};
use rog_profiles::fan_curve_set::{CurveData, FanCurveSet};
use rog_profiles::{FanCurveProfiles, Profile};
use zbus::export::futures_util::lock::Mutex;
use zbus::export::futures_util::StreamExt;
use zbus::fdo::Error;
use zbus::{dbus_interface, Connection, SignalContext};
use super::controller::CtrlPlatformProfile;
use crate::error::RogError;
use crate::CtrlTask;
const MOD_NAME: &str = "ProfileZbus";
const ZBUS_PATH: &str = "/org/asuslinux/Profile";
const UNSUPPORTED_MSG: &str =
"Fan curves are not supported on this laptop or you require a patched kernel";
#[derive(Clone)]
pub struct ProfileZbus(pub Arc<Mutex<CtrlPlatformProfile>>);
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl ProfileZbus {
/// Fetch profile names
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
if let Ok(profiles) = Profile::get_profile_names() {
return Ok(profiles);
}
Err(Error::Failed(
"Failed to get all profile details".to_owned(),
))
}
/// Toggle to next platform_profile. Names provided by `Profiles`.
/// If fan-curves are supported will also activate a fan curve for profile.
async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) {
let mut ctrl = self.0.lock().await;
let next = Profile::get_next_profile(ctrl.profile_config.active_profile);
Profile::set_profile(next)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
ctrl.profile_config.active_profile = next;
ctrl.save_config();
Self::notify_profile(&ctxt, ctrl.profile_config.active_profile)
.await
.ok();
}
/// Fetch the active profile name
async fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
Ok(ctrl.profile_config.active_profile)
}
/// Set this platform_profile name as active
async fn set_active_profile(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
profile: Profile,
) {
let mut ctrl = self.0.lock().await;
// Read first just incase the user has modified the config before calling this
ctrl.profile_config.read();
Profile::set_profile(profile)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
ctrl.profile_config.active_profile = profile;
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
.ok();
ctrl.save_config();
Self::notify_profile(&ctxt, ctrl.profile_config.active_profile)
.await
.ok();
}
/// Get a list of profiles that have fan-curves enabled.
async fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.fan_curves {
return Ok(curves.profiles().get_enabled_curve_profiles());
}
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
}
/// Set a profile fan curve enabled status. Will also activate a fan curve
/// if in the same profile mode
async fn set_fan_curve_enabled(
&mut self,
profile: Profile,
enabled: bool,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.fan_curves {
curves
.profiles_mut()
.set_profile_curve_enabled(profile, enabled);
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("{MOD_NAME}: write_profile_curve_to_platform, {}", e))
.ok();
ctrl.save_config();
Ok(())
} else {
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
}
}
/// Get the fan-curve data for the currently active Profile
async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result<FanCurveSet> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.fan_curves {
let curve = curves.profiles().get_fan_curves_for(profile);
return Ok(curve.clone());
}
Err(Error::Failed(UNSUPPORTED_MSG.to_owned()))
}
/// Set the fan curve for the specified profile.
/// 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<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
if let Some(curves) = &mut ctrl.fan_curves {
curves
.profiles_mut()
.save_fan_curve(curve, profile)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} else {
return Err(Error::Failed(UNSUPPORTED_MSG.to_owned()));
}
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("{MOD_NAME}: Profile::set_profile, {}", e))
.ok();
ctrl.save_config();
Ok(())
}
/// Reset the stored (self) and device curve to the defaults of the
/// platform.
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e))
.ok();
ctrl.save_config();
Ok(())
}
/// Reset the stored (self) and device curve to the defaults of the
/// platform.
///
/// Each platform_profile has a different default and the defualt can be
/// read only for the currently active profile.
async fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
ctrl.profile_config.read();
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
Profile::set_profile(profile)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("{MOD_NAME}: Profile::set_active_curve_to_defaults, {}", e))
.ok();
Profile::set_profile(active)
.map_err(|e| warn!("{MOD_NAME}: set_profile, {}", e))
.ok();
ctrl.save_config();
Ok(())
}
#[dbus_interface(signal)]
async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> {
}
}
#[async_trait]
impl crate::ZbusRun for ProfileZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
}
}
#[async_trait]
impl CtrlTask for ProfileZbus {
fn zbus_path() -> &'static str {
ZBUS_PATH
}
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let ctrl = self.0.clone();
let sig_ctx = signal_ctxt.clone();
let watch = self
.0
.lock()
.await
.platform
.monitor_throttle_thermal_policy()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(stream) = watch.into_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 watch = self.0.lock().await.platform.monitor_platform_profile()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
if let Ok(stream) = watch.into_event_stream(&mut buffer) {
stream
.for_each(|_| async {
let mut lock = ctrl.lock().await;
if let Ok(profile) = lock.platform.get_platform_profile().map_err(|e| {
error!("get_platform_profile error: {e}");
}) {
if let Ok(new_profile) = Profile::from_str(&profile).map_err(|e| {
error!("Profile::from_str(&profile) error: {e}");
}) {
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(
&signal_ctxt,
lock.profile_config.active_profile,
)
.await
.ok();
}
}
}
})
.await;
}
});
Ok(())
}
}
#[async_trait]
impl crate::Reloadable for ProfileZbus {
/// Fetch the active profile and use that to set all related components up
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
let active = ctrl.profile_config.active_profile;
if let Some(curves) = &mut ctrl.fan_curves {
if let Ok(mut device) = FanCurveProfiles::get_device() {
// There is a possibility that the curve was default zeroed, so this call
// initialises the data from system read and we need to save it
// after
curves
.profiles_mut()
.write_profile_curve_to_platform(active, &mut device)?;
ctrl.profile_config.write();
}
}
Ok(())
}
}
+51
View File
@@ -0,0 +1,51 @@
use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode};
use serde_derive::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig {
pub slash_enabled: bool,
pub slash_brightness: u8,
pub slash_interval: u8,
pub slash_mode: SlashMode,
}
impl Default for SlashConfig {
fn default() -> Self {
SlashConfig {
slash_enabled: true,
slash_brightness: 255,
slash_interval: 0,
slash_mode: SlashMode::Bounce,
}
}
}
impl StdConfig for SlashConfig {
fn new() -> Self {
Self::default()
}
fn file_name(&self) -> String {
CONFIG_FILE.to_owned()
}
fn config_dir() -> std::path::PathBuf {
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
}
}
impl StdConfigLoad for SlashConfig {}
impl From<&SlashConfig> for DeviceState {
fn from(config: &SlashConfig) -> Self {
DeviceState {
slash_enabled: config.slash_enabled,
slash_brightness: config.slash_brightness,
slash_interval: config.slash_interval,
slash_mode: config.slash_mode,
}
}
}
+98
View File
@@ -0,0 +1,98 @@
pub mod config;
pub mod trait_impls;
use config_traits::{StdConfig, StdConfigLoad};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::error::SlashError;
use rog_slash::usb::{get_slash_type, pkt_set_mode, pkt_set_options, pkts_for_init};
use rog_slash::{SlashMode, SlashType};
use crate::ctrl_slash::config::SlashConfig;
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
}
pub struct CtrlSlash {
node: Node,
config: SlashConfig,
}
impl CtrlSlash {
#[inline]
pub fn new() -> Result<CtrlSlash, RogError> {
let slash_type = get_slash_type()?;
if matches!(slash_type, SlashType::Unknown | SlashType::Unsupported) {
return Err(RogError::Slash(SlashError::NoDevice));
}
let usb = USBRaw::new(rog_slash::usb::PROD_ID).ok();
let hid = HidRaw::new(rog_slash::usb::PROD_ID_STR).ok();
let node = if usb.is_some() {
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::NotSupported);
};
let ctrl = CtrlSlash {
node,
config: SlashConfig::new().load(),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
fn do_initialization(&self) -> Result<(), RogError> {
let init_packets = pkts_for_init();
self.node.write_bytes(&init_packets[0])?;
self.node.write_bytes(&init_packets[1])?;
// Apply config upon initialization
let option_packets = pkt_set_options(
self.config.slash_enabled,
self.config.slash_brightness,
self.config.slash_interval,
);
self.node.write_bytes(&option_packets)?;
let mode_packets = pkt_set_mode(self.config.slash_mode);
self.node.write_bytes(&mode_packets[0])?;
self.node.write_bytes(&mode_packets[1])?;
Ok(())
}
pub fn set_options(&self, enabled: bool, brightness: u8, interval: u8) -> Result<(), RogError> {
let command_packets = pkt_set_options(enabled, brightness, interval);
self.node.write_bytes(&command_packets)?;
Ok(())
}
pub fn set_slash_mode(&self, slash_mode: SlashMode) -> Result<(), RogError> {
let command_packets = pkt_set_mode(slash_mode);
self.node.write_bytes(&command_packets[0])?;
self.node.write_bytes(&command_packets[1])?;
Ok(())
}
}
+161
View File
@@ -0,0 +1,161 @@
use std::sync::Arc;
use config_traits::StdConfig;
use log::warn;
use rog_slash::usb::{pkt_set_mode, pkt_set_options};
use rog_slash::{DeviceState, SlashMode};
use zbus::export::futures_util::lock::Mutex;
use zbus::{interface, Connection, SignalContext};
use crate::ctrl_slash::CtrlSlash;
use crate::error::RogError;
pub const SLASH_ZBUS_NAME: &str = "Slash";
pub const SLASH_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlSlashZbus(pub Arc<Mutex<CtrlSlash>>);
/// The struct with the main dbus methods requires this trait
impl crate::ZbusRun for CtrlSlashZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, SLASH_ZBUS_PATH, server).await;
}
}
#[interface(name = "org.asuslinux.Slash")]
impl CtrlSlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.slash_enabled
}
/// Set enabled true or false
async fn set_enabled(&self, enabled: bool) {
let mut lock = self.0.lock().await;
let brightness = if enabled && lock.config.slash_brightness == 0 {
0x88
} else {
lock.config.slash_brightness
};
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_brightness
}
/// Set brightness level
async fn set_brightness(&self, brightness: u8) {
let mut lock = self.0.lock().await;
let enabled = brightness > 0;
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
async fn set_interval(&self, interval: u8) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_options(
lock.config.slash_enabled,
lock.config.slash_brightness,
interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_interval = interval;
lock.config.write();
}
#[zbus(property)]
async fn slash_mode(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
async fn set_slash_mode(&self, slash_mode: SlashMode) {
let mut lock = self.0.lock().await;
let command_packets = pkt_set_mode(slash_mode);
lock.node
.write_bytes(&command_packets[0])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.node
.write_bytes(&command_packets[1])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_mode = slash_mode;
lock.config.write();
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let lock = self.0.lock().await;
DeviceState::from(&lock.config)
}
}
impl crate::CtrlTask for CtrlSlashZbus {
fn zbus_path() -> &'static str {
SLASH_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
Ok(())
}
}
impl crate::Reloadable for CtrlSlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
Ok(())
}
}
-42
View File
@@ -1,42 +0,0 @@
use async_trait::async_trait;
use serde_derive::{Deserialize, Serialize};
use zbus::zvariant::Type;
use zbus::{dbus_interface, Connection};
use crate::ctrl_anime::CtrlAnime;
use crate::ctrl_aura::controller::CtrlKbdLed;
use crate::ctrl_platform::CtrlPlatform;
use crate::ctrl_power::CtrlPower;
use crate::ctrl_profiles::controller::CtrlPlatformProfile;
use crate::GetSupported;
#[derive(Serialize, Deserialize, Debug, Type)]
pub struct SupportedFunctions(rog_platform::supported::SupportedFunctions);
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl SupportedFunctions {
pub fn supported_functions(&self) -> &rog_platform::supported::SupportedFunctions {
&self.0
}
}
#[async_trait]
impl crate::ZbusRun for SupportedFunctions {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/Supported", server).await;
}
}
impl GetSupported for SupportedFunctions {
type A = SupportedFunctions;
fn get_supported() -> Self::A {
Self(rog_platform::supported::SupportedFunctions {
anime_ctrl: CtrlAnime::get_supported(),
keyboard_led: CtrlKbdLed::get_supported(),
charge_ctrl: CtrlPower::get_supported(),
platform_profile: CtrlPlatformProfile::get_supported(),
rog_bios_ctrl: CtrlPlatform::get_supported(),
})
}
}
+47 -78
View File
@@ -1,39 +1,30 @@
use std::env;
use std::error::Error;
use std::io::Write;
use std::sync::Arc;
use std::time::Duration;
use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection;
use asusd::config::Config;
use asusd::ctrl_anime::config::AnimeConfig;
use asusd::ctrl_anime::trait_impls::CtrlAnimeZbus;
use asusd::ctrl_anime::CtrlAnime;
use asusd::ctrl_aura::controller::CtrlKbdLed;
use asusd::ctrl_aura::trait_impls::CtrlKbdLedZbus;
use asusd::ctrl_aura::manager::AuraManager;
use asusd::ctrl_fancurves::CtrlFanCurveZbus;
use asusd::ctrl_platform::CtrlPlatform;
use asusd::ctrl_power::CtrlPower;
use asusd::ctrl_profiles::config::ProfileConfig;
use asusd::ctrl_profiles::controller::CtrlPlatformProfile;
use asusd::ctrl_profiles::trait_impls::ProfileZbus;
use asusd::ctrl_supported::SupportedFunctions;
use asusd::{print_board_info, CtrlTask, GetSupported, Reloadable, ZbusRun};
use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2};
use log::{error, info, warn};
use rog_aura::aura_detection::LaptopLedData;
use rog_dbus::DBUS_NAME;
use rog_profiles::Profile;
use tokio::time::sleep;
use zbus::SignalContext;
use asusd::ctrl_slash::trait_impls::CtrlSlashZbus;
use asusd::ctrl_slash::CtrlSlash;
use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME};
use config_traits::{StdConfig, StdConfigLoad3};
use log::{error, info};
use zbus::fdo::ObjectManager;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// console_subscriber::init();
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()))
.format_timestamp(None)
.init();
let is_service = match env::var_os("IS_SERVICE") {
@@ -52,8 +43,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
info!(" daemon v{}", asusd::VERSION);
info!(" rog-anime v{}", rog_anime::VERSION);
info!(" rog-slash v{}", rog_slash::VERSION);
info!(" rog-aura v{}", rog_aura::VERSION);
info!(" rog-dbus v{}", rog_dbus::VERSION);
info!(" rog-profiles v{}", rog_profiles::VERSION);
info!("rog-platform v{}", rog_platform::VERSION);
@@ -63,19 +54,39 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// The actual main loop for the daemon
async fn start_daemon() -> Result<(), Box<dyn Error>> {
let supported = SupportedFunctions::get_supported();
// let supported = SupportedFunctions::get_supported();
print_board_info();
println!("{}", supported.supported_functions());
// println!("{:?}", supported.supported_functions());
// Start zbus server
let mut connection = Connection::system().await?;
connection
.object_server()
.at("/org/asuslinux", ObjectManager)
.await
.unwrap();
let config = Config::new().load();
let cfg_path = config.file_path();
let config = Arc::new(Mutex::new(config));
supported.add_to_server(&mut connection).await;
// supported.add_to_server(&mut connection).await;
match CtrlPlatform::new(config.clone()) {
match CtrlFanCurveZbus::new() {
Ok(ctrl) => {
let sig_ctx = CtrlFanCurveZbus::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("FanCurves: {}", err);
}
}
match CtrlPlatform::new(
config.clone(),
&cfg_path,
CtrlPlatform::signal_context(&connection)?,
) {
Ok(ctrl) => {
let sig_ctx = CtrlPlatform::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
@@ -85,33 +96,7 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
}
}
match CtrlPower::new(config.clone()) {
Ok(ctrl) => {
let sig_ctx = CtrlPower::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("CtrlPower: {}", err);
}
}
if Profile::is_platform_profile_supported() {
let profile_config = ProfileConfig::new().load();
match CtrlPlatformProfile::new(profile_config) {
Ok(ctrl) => {
let zbus = ProfileZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = ProfileZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("Profile control: {}", err);
}
}
} else {
warn!("platform_profile support not found");
}
match CtrlAnime::new(AnimeConfig::new().load()) {
match CtrlAnime::new() {
Ok(ctrl) => {
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?;
@@ -122,44 +107,28 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
}
}
let laptop = LaptopLedData::get_data();
// CtrlKbdLed deviates from the config pattern above due to requiring a keyboard
// detection first
match CtrlKbdLed::new(laptop) {
match CtrlSlash::new() {
Ok(ctrl) => {
let zbus = CtrlKbdLedZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlKbdLedZbus::signal_context(&connection)?;
let zbus = CtrlSlashZbus(Arc::new(Mutex::new(ctrl)));
// Currently, the Slash has no need for a loop watching power events, however,
// it could be cool to have the slash do some power-on/off animation
// (It has a built-in power on animation which plays when u plug in the power
// supply)
let sig_ctx = CtrlSlashZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("Keyboard control: {}", err);
info!("AniMe control: {}", err);
}
}
let _ = AuraManager::new(connection.clone()).await?;
// Request dbus name after finishing initalizing all functions
connection.request_name(DBUS_NAME).await?;
loop {
// This is just a blocker to idle and ensure the reator reacts
sleep(Duration::from_millis(1000)).await;
connection.executor().tick().await;
}
}
async fn start_tasks<T>(
mut zbus: T,
connection: &mut Connection,
signal_ctx: SignalContext<'static>,
) -> Result<(), Box<dyn Error>>
where
T: ZbusRun + Reloadable + CtrlTask + Clone,
{
let task = zbus.clone();
zbus.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
zbus.add_to_server(connection).await;
task.create_tasks(signal_ctx).await.ok();
Ok(())
}
+9
View File
@@ -5,6 +5,7 @@ use config_traits::ron;
use rog_anime::error::AnimeError;
use rog_platform::error::PlatformError;
use rog_profiles::error::ProfileError;
use rog_slash::error::SlashError;
#[derive(Debug)]
pub enum RogError {
@@ -31,6 +32,7 @@ pub enum RogError {
NoAuraKeyboard,
NoAuraNode,
Anime(AnimeError),
Slash(SlashError),
Platform(PlatformError),
SystemdUnitAction(String),
SystemdUnitWaitTimeout(String),
@@ -72,6 +74,7 @@ impl fmt::Display for RogError {
RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
RogError::NoAuraNode => write!(f, "No Aura keyboard node found"),
RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets),
RogError::Slash(deets) => write!(f, "Slash error: {}", deets),
RogError::Platform(deets) => write!(f, "Asus Platform error: {}", deets),
RogError::SystemdUnitAction(action) => {
write!(f, "systemd unit action {} failed", action)
@@ -103,6 +106,12 @@ impl From<AnimeError> for RogError {
}
}
impl From<SlashError> for RogError {
fn from(err: SlashError) -> Self {
RogError::Slash(err)
}
}
impl From<PlatformError> for RogError {
fn from(err: PlatformError) -> Self {
RogError::Platform(err)
+173 -92
View File
@@ -5,30 +5,32 @@ pub mod config;
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
/// Control platform profiles + fan-curves if available
pub mod ctrl_fancurves;
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
pub mod ctrl_platform;
/// Control of battery charge level
pub mod ctrl_power;
/// Control platform profiles + fan-curves if available
pub mod ctrl_profiles;
/// Fetch all supported functions for the laptop
pub mod ctrl_supported;
/// Control of Slash led bar
pub mod ctrl_slash;
pub mod error;
use std::future::Future;
use std::time::Duration;
use async_trait::async_trait;
use dmi_id::DMIID;
use futures_lite::stream::StreamExt;
use log::{debug, info, warn};
use logind_zbus::manager::ManagerProxy;
use zbus::export::futures_util::StreamExt;
use tokio::time::sleep;
use zbus::zvariant::ObjectPath;
use zbus::{Connection, SignalContext};
use zbus::{CacheProperties, Connection, SignalContext};
use crate::error::RogError;
const CONFIG_PATH_BASE: &str = "/etc/asusd/";
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
/// This macro adds a function which spawns an `inotify` task on the passed in
/// `Executor`.
@@ -46,11 +48,11 @@ const CONFIG_PATH_BASE: &str = "/etc/asusd/";
/// # Example
///
/// ```ignore
/// impl CtrlRogBios {
/// impl RogPlatform {
/// task_watch_item!(panel_od platform);
/// task_watch_item!(gpu_mux_mode platform);
/// }
/// ```
/// ```\
/// // TODO: this is kind of useless if it can't trigger some action
#[macro_export]
macro_rules! task_watch_item {
@@ -69,9 +71,45 @@ macro_rules! task_watch_item {
tokio::spawn(async move {
let mut buffer = [0; 32];
watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async {
let value = ctrl.$name();
concat_idents::concat_idents!(notif_fn = notify_, $name {
Self::notif_fn(&signal_ctxt, value).await.ok();
if let Ok(value) = ctrl.$name() { // get new value from zbus method
concat_idents::concat_idents!(notif_fn = $name, _changed {
ctrl.notif_fn(&signal_ctxt).await.ok();
});
let mut lock = ctrl.config.lock().await;
lock.$name = value;
lock.write();
}
}).await;
});
}
Err(e) => info!("inotify watch failed: {}. You can ignore this if your device does not support the feature", e),
}
});
Ok(())
}
});
};
}
#[macro_export]
macro_rules! task_watch_item_notify {
($name:ident $self_inner:ident) => {
concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name(
&self,
signal_ctxt: SignalContext<'static>,
) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt;
let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name {
match self.$self_inner.watch_fn() {
Ok(watch) => {
tokio::spawn(async move {
let mut buffer = [0; 32];
watch.into_event_stream(&mut buffer).unwrap().for_each(|_| async {
concat_idents::concat_idents!(notif_fn = $name, _changed {
ctrl.notif_fn(&signal_ctxt).await.ok();
});
}).await;
});
@@ -88,42 +126,48 @@ macro_rules! task_watch_item {
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());
let dmi = DMIID::new().unwrap_or_default();
info!("Product family: {}", dmi.product_family);
info!("Board name: {}", dmi.board_name);
}
#[async_trait]
pub trait Reloadable {
async fn reload(&mut self) -> Result<(), RogError>;
fn reload(&mut self) -> impl Future<Output = Result<(), RogError>> + Send;
}
#[async_trait]
pub trait ZbusRun {
async fn add_to_server(self, server: &mut Connection);
pub trait ReloadAndNotify {
type Data: Send;
async fn add_to_server_helper(
fn reload_and_notify(
&mut self,
signal_context: &SignalContext<'static>,
data: Self::Data,
) -> impl Future<Output = Result<(), RogError>> + Send;
}
pub trait ZbusRun {
fn add_to_server(self, server: &mut Connection) -> impl Future<Output = ()> + Send;
fn add_to_server_helper(
iface: impl zbus::Interface,
path: &str,
server: &mut Connection,
) {
server
.object_server()
.at(&ObjectPath::from_str_unchecked(path), iface)
.await
.map_err(|err| {
warn!("{}: add_to_server {}", path, err);
err
})
.ok();
) -> impl Future<Output = ()> + Send {
async move {
server
.object_server()
.at(&ObjectPath::from_str_unchecked(path), iface)
.await
.map_err(|err| {
warn!("{}: add_to_server {}", path, err);
err
})
.ok();
}
}
}
/// Set up a task to run on the async executor
#[async_trait]
pub trait CtrlTask {
fn zbus_path() -> &'static str;
@@ -134,7 +178,10 @@ pub trait CtrlTask {
/// Implement to set up various tasks that may be required, using the
/// `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>;
fn create_tasks(
&self,
signal: SignalContext<'static>,
) -> impl Future<Output = Result<(), RogError>> + Send;
// /// Create a timed repeating task
// async fn repeating_task(&self, millis: u64, mut task: impl FnMut() + Send +
@@ -152,74 +199,89 @@ pub trait CtrlTask {
///
/// The closures can potentially block, so execution time should be the
/// minimal possible such as save a variable.
async fn create_sys_event_tasks<
Fut1,
Fut2,
Fut3,
Fut4,
F1: Send + 'static,
F2: Send + 'static,
F3: Send + 'static,
F4: Send + 'static,
>(
fn create_sys_event_tasks<Fut1, Fut2, Fut3, Fut4, F1, F2, F3, F4>(
&self,
mut on_sleep: F1,
mut on_wake: F2,
mut on_shutdown: F3,
mut on_boot: F4,
) where
F1: FnMut() -> Fut1,
F2: FnMut() -> Fut2,
F3: FnMut() -> Fut3,
F4: FnMut() -> Fut4,
mut on_prepare_for_sleep: F1,
mut on_prepare_for_shutdown: F2,
mut on_lid_change: F3,
mut on_external_power_change: F4,
) -> impl Future<Output = ()> + Send
where
F1: FnMut(bool) -> Fut1 + Send + 'static,
F2: FnMut(bool) -> Fut2 + Send + 'static,
F3: FnMut(bool) -> Fut3 + Send + 'static,
F4: FnMut(bool) -> Fut4 + Send + 'static,
Fut1: Future<Output = ()> + Send,
Fut2: Future<Output = ()> + Send,
Fut3: Future<Output = ()> + Send,
Fut4: Future<Output = ()> + Send,
{
let connection = Connection::system()
.await
.expect("Controller could not create dbus connection");
async {
let connection = Connection::system()
.await
.expect("Controller could not create dbus connection");
let manager = ManagerProxy::new(&connection)
.await
.expect("Controller could not create ManagerProxy");
let manager = ManagerProxy::builder(&connection)
.cache_properties(CacheProperties::No)
.build()
.await
.expect("Controller could not create ManagerProxy");
tokio::spawn(async move {
if let Ok(mut notif) = manager.receive_prepare_for_sleep().await {
while let Some(event) = notif.next().await {
if let Ok(args) = event.args() {
if args.start {
debug!("Doing on_sleep()");
on_sleep().await;
} else if !args.start() {
debug!("Doing on_wake()");
on_wake().await;
let manager1 = manager.clone();
tokio::spawn(async move {
if let Ok(mut notif) = manager1.receive_prepare_for_shutdown().await {
while let Some(event) = notif.next().await {
// blocks thread :|
if let Ok(args) = event.args() {
debug!("Doing on_prepare_for_shutdown({})", args.start);
on_prepare_for_shutdown(args.start).await;
}
}
}
}
});
});
let manager = ManagerProxy::new(&connection)
.await
.expect("Controller could not create ManagerProxy");
tokio::spawn(async move {
if let Ok(mut notif) = manager.receive_prepare_for_shutdown().await {
while let Some(event) = notif.next().await {
if let Ok(args) = event.args() {
if args.start {
debug!("Doing on_shutdown()");
on_shutdown().await;
} else if !args.start() {
debug!("Doing on_boot()");
on_boot().await;
let manager2 = manager.clone();
tokio::spawn(async move {
if let Ok(mut notif) = manager2.receive_prepare_for_sleep().await {
while let Some(event) = notif.next().await {
// blocks thread :|
if let Ok(args) = event.args() {
debug!("Doing on_prepare_for_sleep({})", args.start);
on_prepare_for_sleep(args.start).await;
}
}
}
}
});
});
let manager3 = manager.clone();
tokio::spawn(async move {
let mut last_power = manager3.on_external_power().await.unwrap_or_default();
loop {
if let Ok(next) = manager3.on_external_power().await {
if next != last_power {
last_power = next;
on_external_power_change(next).await;
}
}
sleep(Duration::from_secs(2)).await;
}
});
tokio::spawn(async move {
let mut last_lid = manager.lid_closed().await.unwrap_or_default();
// need to loop on these as they don't emit signals
loop {
if let Ok(next) = manager.lid_closed().await {
if next != last_lid {
last_lid = next;
on_lid_change(next).await;
}
}
sleep(Duration::from_secs(2)).await;
}
});
}
}
}
@@ -228,3 +290,22 @@ pub trait GetSupported {
fn get_supported() -> Self::A;
}
pub async fn start_tasks<T>(
mut zbus: T,
connection: &mut Connection,
signal_ctx: SignalContext<'static>,
) -> Result<(), RogError>
where
T: ZbusRun + Reloadable + CtrlTask + Clone,
{
let zbus_clone = zbus.clone();
zbus.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
zbus.add_to_server(connection).await;
zbus_clone.create_tasks(signal_ctx).await.ok();
Ok(())
}
+7 -7
View File
@@ -1,18 +1,18 @@
[package]
name = "config-traits"
license = "MPL-2.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2021"
license.workspace = true
version.workspace = true
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.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
cargo-husky.workspace = true
+17 -17
View File
@@ -3,10 +3,7 @@
//! 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.
//! The end canonical file format is `.ron` as this supports rust types well
use std::fs::{self, create_dir, File, OpenOptions};
use std::io::{Read, Write};
@@ -92,6 +89,7 @@ where
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(self.file_path())
.unwrap_or_else(|e| panic!("Could not open {:?} {e}", self.file_path()))
}
@@ -109,6 +107,20 @@ where
}
}
/// Open and parse the config file to self from ron format
fn read_new(&self) -> Option<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) {
return Some(data);
} else {
warn!("Could not deserialise {:?}", self.file_path());
}
}
None
}
/// Write the config file data to pretty ron format
fn write(&self) {
let mut file = match File::create(self.file_path()) {
@@ -206,21 +218,9 @@ macro_rules! std_config_load {
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) {
} $(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();
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "cpuctl"
license.workspace = true
version.workspace = true
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
[dependencies]
+14
View File
@@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
-3
View File
@@ -1,6 +1,3 @@
#ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
#ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service"
ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}"
ENV{DMI_VENDOR}!="ASUSTeK COMPUTER INC.", GOTO="asusd_end"
+6 -7
View File
@@ -1,19 +1,18 @@
[Unit]
Description=ASUS Notebook Control
StartLimitInterval=200
StartLimitBurst=2
Before=multi-user.target
After=power-profiles-daemon.service
After=nvidia-powerd.service
StartLimitInterval=500
StartLimitBurst=5
After=nvidia-powerd.service systemd-udevd.service
[Service]
Environment=IS_SERVICE=1
Environment=RUST_LOG="info"
ExecStartPre=/bin/sleep 2
# required to prevent init issues with hid_asus and MCU
ExecStartPre=/bin/sleep 1
ExecStart=/usr/bin/asusd
Restart=on-failure
RestartSec=1
Type=dbus
BusName=org.asuslinux.Daemon
SELinuxContext=system_u:system_r:unconfined_t:s0
#SELinuxContext=system_u:object_r:modules_object_t:s0
#SELinuxContext=system_u:object_r:modules_object_t:s0
+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="0 0 512 512" width="512pt" height="512pt"><defs><clipPath id="_clipPath_8C5izdJ0sH9w7nD5UuBTlpktuZWEWJn8"><rect width="512" height="512"/></clipPath></defs><g clip-path="url(#_clipPath_8C5izdJ0sH9w7nD5UuBTlpktuZWEWJn8)"><path d=" M 113.969 270.246 C 113.969 301.718 88.456 327.231 56.985 327.231 C 25.513 327.231 0 301.718 0 270.246 C 0 238.775 25.513 213.262 56.985 213.262 C 88.456 213.262 113.969 238.775 113.969 270.246 Z M 56.985 384.215 C 41.249 384.215 28.492 396.972 28.492 412.708 C 28.492 428.443 41.249 441.2 56.985 441.2 C 72.72 441.2 85.477 428.443 85.477 412.708 C 85.477 396.972 72.72 384.215 56.985 384.215 Z M 56.985 70.8 C 25.513 70.8 0 96.313 0 127.785 C 0 159.256 25.513 184.769 56.985 184.769 C 88.456 184.769 113.969 159.256 113.969 127.785 C 113.969 96.313 88.456 70.8 56.985 70.8 Z M 199.446 241.754 C 183.71 241.754 170.954 254.51 170.954 270.246 C 170.954 285.982 183.71 298.738 199.446 298.738 C 215.182 298.738 227.938 285.982 227.938 270.246 C 227.938 254.51 215.182 241.754 199.446 241.754 Z M 199.446 384.215 C 183.71 384.215 170.954 396.972 170.954 412.708 C 170.954 428.443 183.71 441.2 199.446 441.2 C 215.182 441.2 227.938 428.443 227.938 412.708 C 227.938 396.972 215.182 384.215 199.446 384.215 Z M 199.446 70.8 C 167.975 70.8 142.462 96.313 142.462 127.785 C 142.462 159.256 167.975 184.769 199.446 184.769 C 230.918 184.769 256.431 159.256 256.431 127.785 C 256.431 96.313 230.918 70.8 199.446 70.8 Z M 339.904 241.754 C 324.169 241.754 311.412 254.51 311.412 270.246 C 311.412 285.982 324.169 298.738 339.904 298.738 C 355.64 298.738 368.397 285.982 368.397 270.246 C 368.397 254.51 355.64 241.754 339.904 241.754 Z M 339.904 384.215 C 324.169 384.215 311.412 396.972 311.412 412.708 C 311.412 428.443 324.169 441.2 339.904 441.2 C 355.64 441.2 368.397 428.443 368.397 412.708 C 368.397 396.972 355.64 384.215 339.904 384.215 Z M 339.904 70.8 C 308.433 70.8 282.92 96.313 282.92 127.785 C 282.92 159.256 308.433 184.769 339.904 184.769 C 371.376 184.769 396.889 159.256 396.889 127.785 C 396.889 96.313 371.376 70.8 339.904 70.8 Z M 482.366 241.754 C 466.63 241.754 453.874 254.51 453.874 270.246 C 453.874 285.982 466.63 298.738 482.366 298.738 C 498.102 298.738 510.858 285.982 510.858 270.246 C 510.858 254.51 498.102 241.754 482.366 241.754 Z M 482.366 384.215 C 466.63 384.215 453.874 396.972 453.874 412.708 C 453.874 428.443 466.63 441.2 482.366 441.2 C 498.102 441.2 510.858 428.443 510.858 412.708 C 510.858 396.972 498.102 384.215 482.366 384.215 Z M 482.366 99.292 C 466.63 99.292 453.874 112.049 453.874 127.785 C 453.874 143.52 466.63 156.277 482.366 156.277 C 498.102 156.277 510.858 143.52 510.858 127.785 C 510.858 112.049 498.102 99.292 482.366 99.292 Z " fill="#F2F2F2"/></g></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="141 297.96 507.339 471.04" width="507.339pt" height="471.04pt"><g><g><g><g><path d=" M 537.075 443.463 L 537.075 443.463 L 564.723 427.335 C 504.903 324.571 375.966 285.313 269.027 337.304 L 269.027 297.96 L 237.027 297.96 L 237.027 393.96 L 333.027 393.96 L 333.027 361.96 L 292.147 361.96 C 382.665 323.563 487.612 358.486 537.075 443.463 Z " fill="rgb(221,221,221)"/><path d=" M 245.027 692.775 C 170.84 632.524 151.314 527.353 198.931 444.488 L 171.187 428.488 C 116.876 523.063 137.426 642.887 220.147 713.96 L 181.027 713.96 L 181.027 745.96 L 277.027 745.96 L 277.027 649.96 L 245.027 649.96 L 245.027 692.775 L 245.027 692.775 Z " fill="rgb(221,221,221)"/><path d=" M 580.451 499.048 L 512.579 566.936 L 535.203 589.56 L 562.403 562.36 C 551.734 661.49 468.201 736.724 368.499 737 L 368.499 769 C 487.289 768.688 585.833 677.011 594.707 558.552 L 625.715 589.56 L 648.339 566.936 L 580.451 499.048 Z " fill="rgb(221,221,221)"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="0 0 128 128" width="128pt" height="128pt"><defs><clipPath id="_clipPath_CHRnLOITGqiXLlZjYLaUfMylStiUiIRD"><rect width="128" height="128"/></clipPath></defs><g clip-path="url(#_clipPath_CHRnLOITGqiXLlZjYLaUfMylStiUiIRD)"><path d=" M 69.096 31.645 C 68.865 31.712 68.805 31.816 68.577 32.011 C 68.393 32.168 68.238 32.28 68.059 32.434 C 67.651 32.785 67.411 33.003 67.052 33.305 C 65.269 34.805 62.85 37.142 61.102 38.778 C 58.391 41.317 55.947 43.762 53.346 46.287 C 48.441 51.049 42.882 56.766 37.972 61.357 C 33.914 65.151 30.434 68.558 26.194 72.351 C 24.574 73.801 23.553 75.121 21.346 75.197 C 17.705 75.323 12.902 72.224 10.446 70.814 C 8.299 69.583 2.598 66.23 -0.15 66.137 L 1.864 68.903 C 3.303 71.1 5.724 75.885 6.887 78.214 C 10.881 86.213 16.273 94.589 23.136 100.161 C 24.891 101.586 26.481 102.783 28.71 104.132 C 29.687 104.723 30.685 105.215 31.778 105.834 C 32.157 106.049 34.786 107.277 35.11 107.276 C 34.39 106.554 33.649 105.673 33.064 104.985 C 30.452 101.912 24.64 95.938 22.894 92.889 C 21.428 90.329 21.091 88.761 22.72 86.192 C 24.582 83.254 28.924 79.619 31.648 77.289 C 38.277 71.62 44.256 66.485 51.191 61.102 C 54.478 58.551 57.895 55.949 61.429 53.427 C 64.896 50.954 68.381 48.57 72 46.178 L 77.465 42.7 C 77.972 42.409 82.95 39.446 83.029 39.347 L 87.9 36.66 C 89.536 35.736 91.124 34.964 92.857 34.031 C 97.94 31.294 108.035 26.833 113.82 24.792 C 115.718 24.122 117.528 23.489 119.449 22.833 L 125.205 21.03 C 124.739 20.79 118.379 20.724 117.207 20.724 C 112.491 20.723 109.598 20.855 105.095 21.211 C 101.65 21.483 95.237 22.19 92.104 22.948 C 91.54 23.084 90.872 23.142 90.304 23.292 C 89.218 23.578 87.974 23.775 86.861 24.052 C 84.567 24.623 82.484 25.271 80.328 25.914 C 78.108 26.575 76.101 27.36 74.115 28.177 C 72.942 28.659 69.397 31.029 69.096 31.645 L 69.096 31.645 Z M 115.16 43.036 L 119.345 40.899 C 120.797 40.208 122.299 39.413 123.667 38.745 C 126.441 37.389 128.04 32.995 127.999 30.179 C 127.509 30.199 125.516 31.144 124.975 31.342 C 124.603 31.479 124.259 31.596 123.911 31.744 C 121.439 32.794 118.139 34.036 115.795 35.142 C 115.115 35.463 114.457 35.729 113.769 36.024 C 113.228 36.256 112.036 36.724 111.814 36.999 C 111.07 36.962 95.925 44.467 94.037 45.443 C 91.35 46.833 87.97 48.499 85.386 49.936 C 85.08 50.106 77.204 54.275 76.891 54.676 C 76.372 54.762 70.974 57.968 70.112 58.454 C 59.43 64.482 47.599 72.065 37.367 79.004 C 34.524 80.933 30.892 83.463 28.034 85.617 L 25.739 87.329 C 25.476 87.53 25.165 87.663 25.013 87.965 C 26.033 88.843 32.791 91.628 34.403 92.209 C 43.451 95.47 51.92 97.742 61.441 99.376 L 67.483 100.3 L 72.241 100.72 L 68.366 100.378 C 69.105 100.462 69.839 100.582 70.559 100.651 C 71.624 100.753 72.679 100.84 73.776 100.949 C 75.841 101.157 78.238 101.298 80.361 101.335 C 82.999 101.381 84.453 101.516 86.617 100.603 C 88.294 99.894 89.659 99.013 90.992 97.992 C 95.957 94.187 100.375 88.235 103.899 82.849 C 107.587 77.213 110.909 70.915 113.773 64.675 C 115.231 61.498 116.599 58.182 117.888 54.804 C 118.541 53.092 119.107 51.439 119.697 49.622 C 120.023 48.617 121.212 44.945 121.302 44.125 C 119.948 44.31 96.265 57.088 94.012 58.297 C 90.956 59.938 88.029 61.647 85.064 63.258 C 82.182 64.823 79.134 66.672 76.267 68.266 C 70.441 71.504 64.575 75.11 58.757 78.479 C 57.76 79.056 50.352 83.387 50.086 83.691 C 50.584 83.611 74.07 73.525 74.639 73.29 C 75.746 72.832 98.806 62.937 99.184 62.92 C 99.052 63.454 97.624 66.118 97.264 66.901 C 96.664 68.206 95.94 69.692 95.284 70.989 C 91.565 78.346 87.529 87.178 79.411 90.439 L 78.923 90.732 C 78.488 90.742 77.299 91.225 76.775 91.369 C 75.986 91.586 75.223 91.736 74.428 91.899 C 70.686 92.665 65.028 92.266 61.129 91.656 C 56.682 90.96 53.568 90.179 49.258 89.137 C 48.229 88.889 40.792 86.799 40.362 86.516 C 40.604 86.093 44.858 83.368 45.23 83.109 C 53.46 77.389 67.13 68.983 76.02 63.925 C 76.961 63.389 77.835 62.903 78.696 62.41 C 79.411 62 80.72 61.317 81.298 60.897 L 85.457 58.616 C 86.864 57.835 88.155 57.159 89.637 56.314 C 92.438 54.716 95.275 53.376 98.032 51.823 C 98.437 51.595 114.179 43.323 115.159 43.037 L 115.16 43.036 Z " fill-rule="evenodd" fill="#F2F2F2"/></g></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

+128
View File
@@ -0,0 +1,128 @@
#
# spec file for package asus-nb-ctrl
#
# Copyright (c) 2020-2021 Luke Jones <luke@ljones.dev>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
%if %{defined fedora}
%global debug_package %{nil}
%endif
%global rpm_dkms_opt 1
# Use hardening ldflags.
%global rustflags -Clink-arg=-Wl,-z,relro,-z,now
Name: asusctl
Version: 4.7.0
Release: 2
Summary: Control fan speeds, LEDs, graphics modes, and charge levels for ASUS notebooks
License: MPLv2
Group: System Environment/Kernel
URL: https://gitlab.com/asus-linux/asusctl
Source: %{name}-%{version}.tar.gz
Source1: vendor-%{name}-%{version}.tar.gz
Source2: cargo_config
BuildRequires: cargo
BuildRequires: rust-packaging
BuildRequires: systemd-rpm-macros
BuildRequires: clang-devel
BuildRequires: cmake
BuildRequires: rust
BuildRequires: rust-std-static
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(gtk+-3.0)
BuildRequires: pkgconfig(gdk-3.0)
BuildRequires: desktop-file-utils
Requires: libappindicator-gtk3
%description
asus-nb-ctrl is a utility for Linux to control many aspects of various
ASUS laptops but can also be used with non-Asus laptops with reduced features.
It provides an interface for rootless control of some system functions such as
fan speeds, keyboard LEDs, battery charge level, and graphics modes.
asus-nb-ctrl enables third-party apps to use the above with dbus methods.
%package rog-gui
Summary: An experimental GUI for %{name}
%description rog-gui
A one-stop-shop GUI tool for asusd/asusctl. It aims to provide most controls,
a notification service, and ability to run in the background.
%prep
%autosetup
%setup -D -T -a 1 -c -n %{name}-%{version}/vendor
cd ..
mv Cargo.lock{,.bak}
%cargo_prep
mv Cargo.lock{.bak,}
sed -i 's|replace-with = "local-registry"|replace-with = "vendored-sources"|' .cargo/config
cat %{SOURCE2} >> .cargo/config
%build
export RUSTFLAGS="%{rustflags}"
%cargo_build
#cargo build --release --frozen --offline --config .cargo/config.toml
%install
export RUSTFLAGS="%{rustflags}"
mkdir -p "%{buildroot}/%{_bindir}" "%{buildroot}%{_docdir}"
%make_install
install -D -m 0644 README.md %{buildroot}/%{_docdir}/%{name}/README.md
install -D -m 0644 rog-anime/README.md %{buildroot}/%{_docdir}/%{name}/README-anime.md
install -D -m 0644 rog-anime/data/diagonal-template.png %{buildroot}/%{_docdir}/%{name}/diagonal-template.png
desktop-file-validate %{buildroot}/%{_datadir}/applications/rog-control-center.desktop
%files
%license LICENSE
%{_bindir}/asusd
%{_bindir}/asusd-user
%{_bindir}/asusctl
%{_unitdir}/asusd.service
%{_userunitdir}/asusd-user.service
%{_udevrulesdir}/99-asusd.rules
#%dir %{_sysconfdir}/asusd/
%{_datadir}/asusd/aura_support.ron
%{_datadir}/dbus-1/system.d/asusd.conf
%{_datadir}/icons/hicolor/512x512/apps/asus_notif_yellow.png
%{_datadir}/icons/hicolor/512x512/apps/asus_notif_green.png
%{_datadir}/icons/hicolor/512x512/apps/asus_notif_red.png
%{_datadir}/icons/hicolor/512x512/apps/asus_notif_blue.png
%{_datadir}/icons/hicolor/512x512/apps/asus_notif_orange.png
%{_datadir}/icons/hicolor/512x512/apps/asus_notif_white.png
%{_datadir}/icons/hicolor/scalable/status/gpu-compute.svg
%{_datadir}/icons/hicolor/scalable/status/gpu-hybrid.svg
%{_datadir}/icons/hicolor/scalable/status/gpu-integrated.svg
%{_datadir}/icons/hicolor/scalable/status/gpu-nvidia.svg
%{_datadir}/icons/hicolor/scalable/status/gpu-vfio.svg
%{_datadir}/icons/hicolor/scalable/status/notification-reboot.svg
%{_docdir}/%{name}/
%{_datadir}/asusd/
%files rog-gui
%{_bindir}/rog-control-center
%{_datadir}/applications/rog-control-center.desktop
%{_datadir}/icons/hicolor/512x512/apps/rog-control-center.png
%{_datadir}/rog-gui
%changelog
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "dmi_id"
license.workspace = true
version.workspace = true
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
[dependencies]
log.workspace = true
udev.workspace = true
+113
View File
@@ -0,0 +1,113 @@
use log::{info, warn};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)]
pub struct DMIID {
pub id_model: String,
pub dmi_family: String,
pub dmi_vendor: String,
pub board_name: String,
pub board_vendor: String,
pub bios_date: String,
pub bios_release: String,
pub bios_vendor: String,
pub bios_version: String,
pub product_family: String,
pub product_name: String,
}
impl DMIID {
pub fn new() -> Result<Self, String> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
format!("dmi enumerator failed: {err}")
})?;
enumerator.match_subsystem("dmi").map_err(|err| {
warn!("{}", err);
format!("dmi match_subsystem failed: {err}")
})?;
let mut result = enumerator.scan_devices().map_err(|err| {
warn!("{}", err);
format!("dmi scan_devices failed: {err}")
})?;
if let Some(device) = (result).next() {
info!("Found dmi ID info at {:?}", device.sysname());
return Ok(Self {
id_model: device
.property_value("ID_MODEL")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
dmi_family: device
.property_value("DMI_FAMILY")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
dmi_vendor: device
.property_value("DMI_VENDOR")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
board_name: device
.attribute_value("board_name")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
board_vendor: device
.attribute_value("board_vendor")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
bios_date: device
.attribute_value("bios_date")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
bios_release: device
.attribute_value("bios_release")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
bios_vendor: device
.attribute_value("bios_vendor")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
bios_version: device
.attribute_value("bios_version")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
product_family: device
.attribute_value("product_family")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
product_name: device
.attribute_value("product_name")
.map(|s| s.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string()),
});
}
Err("dmi not found".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore = "Does not run in docker images"]
fn dmi_sysfs_properties_not_unknown() {
let dmi = DMIID::new().unwrap();
assert_ne!(dmi.id_model, "Unknown".to_string());
dbg!(dmi.id_model);
assert_ne!(dmi.dmi_family, "Unknown".to_string());
dbg!(dmi.dmi_family);
assert_ne!(dmi.dmi_vendor, "Unknown".to_string());
dbg!(dmi.dmi_vendor);
assert_ne!(dmi.board_name, "Unknown".to_string());
dbg!(dmi.board_name);
assert_ne!(dmi.board_vendor, "Unknown".to_string());
dbg!(dmi.board_vendor);
assert_ne!(dmi.product_family, "Unknown".to_string());
dbg!(dmi.product_family);
assert_ne!(dmi.product_name, "Unknown".to_string());
dbg!(dmi.product_name);
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

-12
View File
@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<script language="javascript" type="text/javascript">
<!--
window.setTimeout('window.location="room4doom/index.html"; ', 0);
// -->
</script>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

+13 -9
View File
@@ -1,21 +1,21 @@
[package]
name = "rog_anime"
license = "MPL-2.0"
license.workspace = true
version.workspace = true
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl"
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
documentation = "https://docs.rs/rog-anime"
description = "Types useful for translating images and other data for display on the ASUS AniMe Matrix display"
keywords = ["ROG", "ASUS", "AniMe"]
edition = "2021"
exclude = ["data"]
[features]
default = ["dbus", "detect"]
dbus = ["zbus"]
detect = ["sysfs-class"]
detect = ["dmi_id"]
[lib]
name = "rog_anime"
@@ -31,10 +31,14 @@ serde.workspace = true
serde_derive.workspace = true
glam.workspace = true
typeshare.workspace = true
zbus = { workspace = true, optional = true }
sysfs-class = { workspace = true, optional = true }
dmi_id = { path = "../dmi-id", optional = true }
[dev-dependencies]
cargo-husky.workspace = true
cargo-husky.workspace = true
[package.metadata.cargo-machete]
ignored = ["serde"]
+15 -4
View File
@@ -5,8 +5,9 @@ use std::time::{Duration, Instant};
use log::info;
use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
use zbus::zvariant::{OwnedValue, Type, Value};
use crate::error::{AnimeError, Result};
use crate::usb::{AnimAwake, AnimBooting, AnimShutdown, AnimSleeping, Brightness};
@@ -27,7 +28,9 @@ pub const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
/// Third packet is for GA402 matrix
pub const USB_PREFIX3: [u8; 7] = [0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02];
#[cfg_attr(feature = "dbus", derive(Type))]
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[typeshare]
#[derive(Default, Deserialize, PartialEq, Eq, Clone, Copy, Serialize, Debug)]
pub struct Animations {
pub boot: AnimBooting,
@@ -36,17 +39,25 @@ pub struct Animations {
pub shutdown: AnimShutdown,
}
// TODO: move this out
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type))]
#[typeshare]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub struct DeviceState {
pub display_enabled: bool,
pub display_brightness: Brightness,
pub builtin_anims_enabled: bool,
pub builtin_anims: Animations,
pub off_when_unplugged: bool,
pub off_when_suspended: bool,
pub off_when_lid_closed: bool,
pub brightness_on_battery: Brightness,
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum AnimeType {
GA401,
GA402,
+1 -1
View File
@@ -9,7 +9,7 @@ use crate::AnimeType;
/// Mostly intended to be used with ASUS gifs, but can be used for other
/// purposes (like images)
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct AnimeDiagonal(AnimeType, Vec<Vec<u8>>, Option<Duration>);
impl AnimeDiagonal {
+24
View File
@@ -33,3 +33,27 @@ pub mod error;
pub mod usb;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
// #[cfg(feature = "typescript")]
// #[cfg(test)]
// mod tests {
// use crate::usb::{AnimShutdown, Brightness, AnimSleeping, AnimAwake,
// AnimBooting}; use crate::{AnimeType, Animations, DeviceState};
// use ts_rs::SingleFileExporter;
// #[test]
// fn test_singlefile() {
// let out = SingleFileExporter::new(false)
// .and::<AnimeType>()
// .and::<Animations>()
// .and::<DeviceState>()
// .and::<Brightness>()
// .and::<AnimShutdown>()
// .and::<AnimSleeping>()
// .and::<AnimAwake>()
// .and::<AnimBooting>()
// .finish()
// .unwrap();
// println!("{out}");
// assert_eq!(out, "");
// }
// }
+124 -21
View File
@@ -10,9 +10,11 @@
use std::str::FromStr;
use dmi_id::DMIID;
use serde_derive::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
use zbus::zvariant::{OwnedValue, Type, Value};
use crate::error::AnimeError;
use crate::AnimeType;
@@ -22,15 +24,20 @@ const DEV_PAGE: u8 = 0x5e;
pub const VENDOR_ID: u16 = 0x0b05;
pub const PROD_ID: u16 = 0x193b;
#[cfg_attr(feature = "dbus", derive(Type))]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
/// Base LED brightness of the display
pub enum Brightness {
Off,
Low,
Off = 0,
Low = 1,
#[default]
Med,
High,
Med = 2,
High = 3,
}
impl FromStr for Brightness {
@@ -51,19 +58,36 @@ impl From<u8> for Brightness {
fn from(v: u8) -> Brightness {
match v {
0 => Brightness::Off,
2 => Brightness::Low,
1 => Brightness::Low,
3 => Brightness::High,
_ => Brightness::Med,
}
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
impl From<i32> for Brightness {
fn from(v: i32) -> Brightness {
(v as u8).into()
}
}
impl From<Brightness> for i32 {
fn from(v: Brightness) -> i32 {
v as i32
}
}
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimBooting {
#[default]
GlitchConstruction,
StaticEmergence,
GlitchConstruction = 0,
StaticEmergence = 1,
}
impl FromStr for AnimBooting {
@@ -78,12 +102,33 @@ impl FromStr for AnimBooting {
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
impl From<i32> for AnimBooting {
fn from(value: i32) -> Self {
match value {
0 => Self::GlitchConstruction,
1 => Self::StaticEmergence,
_ => Self::default(),
}
}
}
impl From<AnimBooting> for i32 {
fn from(value: AnimBooting) -> Self {
value as i32
}
}
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimAwake {
#[default]
BinaryBannerScroll,
RogLogoGlitch,
BinaryBannerScroll = 0,
RogLogoGlitch = 1,
}
impl FromStr for AnimAwake {
@@ -98,12 +143,33 @@ impl FromStr for AnimAwake {
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
impl From<i32> for AnimAwake {
fn from(value: i32) -> Self {
match value {
0 => Self::BinaryBannerScroll,
1 => Self::RogLogoGlitch,
_ => Self::default(),
}
}
}
impl From<AnimAwake> for i32 {
fn from(value: AnimAwake) -> Self {
value as i32
}
}
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimSleeping {
#[default]
BannerSwipe,
Starfield,
BannerSwipe = 0,
Starfield = 1,
}
impl FromStr for AnimSleeping {
@@ -118,12 +184,33 @@ impl FromStr for AnimSleeping {
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
impl From<i32> for AnimSleeping {
fn from(value: i32) -> Self {
match value {
0 => Self::BannerSwipe,
1 => Self::Starfield,
_ => Self::default(),
}
}
}
impl From<AnimSleeping> for i32 {
fn from(value: AnimSleeping) -> Self {
value as i32
}
}
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[typeshare]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub enum AnimShutdown {
#[default]
GlitchOut,
SeeYa,
GlitchOut = 0,
SeeYa = 1,
}
impl FromStr for AnimShutdown {
@@ -138,6 +225,22 @@ impl FromStr for AnimShutdown {
}
}
impl From<i32> for AnimShutdown {
fn from(value: i32) -> Self {
match value {
0 => Self::GlitchOut,
1 => Self::SeeYa,
_ => Self::default(),
}
}
}
impl From<AnimShutdown> for i32 {
fn from(value: AnimShutdown) -> Self {
value as i32
}
}
/// `get_anime_type` is very broad, matching on part of the laptop board name
/// only. For this reason `find_node()` must be used also to verify if the USB
/// device is available.
@@ -145,8 +248,8 @@ impl FromStr for AnimShutdown {
/// The currently known USB device is `19b6`.
#[inline]
pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name()?;
let dmi = DMIID::new().map_err(|_| AnimeError::NoDevice)?; // TODO: better error
let board_name = dmi.board_name;
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
return Ok(AnimeType::GA401);
+3 -4
View File
@@ -20,14 +20,13 @@ dbus = ["zbus"]
serde.workspace = true
serde_derive.workspace = true
zbus = { workspace = true, optional = true }
dmi_id = { path = "../dmi-id" }
# cli and logging
log.workspace = true
# Device control
sysfs-class.workspace = true # used for backlight control and baord ID
typeshare.workspace = true
ron = { version = "*", optional = true }
[dev-dependencies]
cargo-husky.workspace = true
cargo-husky.workspace = true
+421 -83
View File
@@ -1,555 +1,893 @@
([
(
board_name: "FA506I",
device_name: "FA506I",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "FA506Q",
device_name: "FA506Q",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "FA507",
device_name: "FA507",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "FX505D",
device_name: "FX505D",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "FX506H",
device_name: "FX505G",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "FX506H",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "FX506L",
device_name: "FX506L",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "FX507Z",
device_name: "FX507Z",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "FX516P",
device_name: "FX516P",
product_id: "",
layout_name: "fa506i",
basic_modes: [Static, Breathe, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G512",
device_name: "FX705D",
product_id: "",
layout_name: "fx505d",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G512",
product_id: "",
layout_name: "g512",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G512LV",
device_name: "G512LV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G513I",
device_name: "G513I",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
),
(
board_name: "G513QE",
device_name: "G513QE",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G513QM",
device_name: "G513QM",
product_id: "",
layout_name: "g513i",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G513QR",
device_name: "G513QR",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G513QY",
device_name: "G513QY",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G513RC",
device_name: "G513RC",
product_id: "",
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]),
power_zones: [Keyboard, Lightbar],
),
(
board_name: "G513RM",
device_name: "G513RM",
product_id: "",
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]),
power_zones: [Keyboard, Lightbar],
),
(
board_name: "G531",
device_name: "G513RW",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G531",
device_name: "G531",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
device_name: "G531",
product_id: "",
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,
power_zones: [Keyboard],
),
(
board_name: "G531GD",
device_name: "G531GD",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G531GT",
device_name: "G531GT",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G531GU",
device_name: "G531GU",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G531GV",
device_name: "G531GV",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G531GW",
device_name: "G531GW",
product_id: "",
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,
power_zones: [Keyboard],
),
(
board_name: "G532",
device_name: "G532",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G533Q",
device_name: "G533Q",
product_id: "",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G533Z",
device_name: "G533Z",
product_id: "",
layout_name: "g533q-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G634J",
device_name: "G614J",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, Pulse, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G634J",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow],
),
(
board_name: "G712LI",
device_name: "G712LI",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G712LV",
device_name: "G712LV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G712LW",
device_name: "G712LW",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G713IC",
device_name: "G713IC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G713QM",
device_name: "G713P",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G713QR",
device_name: "G713QM",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "G713QR",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G713RM",
device_name: "G713RC",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([ZonedKbLeft, ZonedKbLeftMid, ZonedKbRightMid, ZonedKbRight, LightbarRight, LightbarRightCorner, LightbarRightBottom, LightbarLeftBottom, LightbarLeftCorner, LightbarLeft]),
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G713RM",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G713RS",
device_name: "G713RS",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G713RW",
device_name: "G713RW",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G731",
device_name: "G731",
product_id: "",
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,
power_zones: [Keyboard],
),
(
board_name: "G731GT",
device_name: "G731GT",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G731GU",
device_name: "G731GU",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G731GV",
device_name: "G731GV",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G731GW",
device_name: "G731GW",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "G733C",
device_name: "G733C",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [Logo, BarLeft, BarRight],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G733Q",
device_name: "G733PZ",
product_id: "",
layout_name: "g733pz-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G733Q",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "G733Z",
device_name: "G733Z",
product_id: "",
layout_name: "g513i-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GA401Q",
device_name: "G814JI",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G814JZ",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar],
),
(
device_name: "G834JZ",
product_id: "",
layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo, RearGlow],
),
(
device_name: "GA401Q",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GA402R",
device_name: "GA402N",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA402R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GA503Q",
device_name: "GA402X",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_modes: [Static, Breathe, Pulse, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GA503QE",
device_name: "GA402XV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GA503R",
device_name: "GA403UI",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA503Q",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GL503",
device_name: "GA503QE",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GA503R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Rainbow, Strobe],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GL503",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GL503V",
device_name: "GL503V",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GL504G",
device_name: "GL504G",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4, Logo, BarLeft, BarRight],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GL531",
device_name: "GL531",
product_id: "",
layout_name: "g512",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GL553VE",
device_name: "GL553V",
product_id: "",
layout_name: "g533q",
basic_modes: [Static, Breathe, Strobe],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GL703G",
device_name: "GL703G",
product_id: "",
layout_name: "gl503",
basic_modes: [Static, Breathe, Strobe, Rainbow],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GM501G",
device_name: "GM501G",
product_id: "",
layout_name: "fa507",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GU502",
device_name: "GU502",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GU502G",
device_name: "GU502G",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GU502L",
device_name: "GU502L",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GU502LU",
device_name: "GU502LU",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GU603H",
device_name: "GU603H",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
board_name: "GU603Z",
device_name: "GU603VV",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
board_name: "GU604V",
device_name: "GU603Z",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
board_name: "GV301Q",
device_name: "GU604V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
device_name: "GU605M",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
device_name: "GV301Q",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GV301V",
device_name: "GV301V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GV301VIC",
device_name: "GV301VIC",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GV601R",
device_name: "GV601R",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GV601VI",
device_name: "GV601V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse, Strobe],
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
(
board_name: "GV604V",
device_name: "GV604V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Strobe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GX502",
device_name: "GX502",
product_id: "",
layout_name: "gx502",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GX531",
device_name: "GX531",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GX550L",
device_name: "GX550L",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GX551Q",
device_name: "GX551Q",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GX650P",
device_name: "GX650P",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GX701",
device_name: "GX701",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: PerKey,
power_zones: [Keyboard],
),
(
board_name: "GX703H",
device_name: "GX703H",
product_id: "",
layout_name: "gx531-per-key",
basic_modes: [Static, Breathe, Strobe, Rainbow, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GZ301V",
device_name: "GZ301V",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
board_name: "GZ301VIC",
device_name: "GZ301VIC",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
])
(
device_name: "GZ301Z",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "GZ301Z",
product_id: "18c6",
layout_name: "",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [None],
),
(
device_name: "GZ301Z",
product_id: "1a30",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
(
device_name: "RC71L",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, Pulse],
basic_zones: [],
advanced_type: None,
power_zones: [Keyboard],
),
])
+398
View File
@@ -0,0 +1,398 @@
(
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.1,
pad_bottom: 0.1,
),
"rog_row": Led(
width: 1.0,
height: 0.6,
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_key": Led(
width: 1.0,
height: 0.6,
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.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
pad_bottom: 0.1,
),
"spacebar": Led(
width: 6.0,
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: 0.6,
pad_left: 1.5,
pad_right: 1.3,
pad_top: 0.1,
pad_bottom: 0.0,
),
"arrows_spacer": Blank(
width: 14.2,
height: 0.0,
),
"arrows": Led(
width: 1.0,
height: 0.6,
pad_left: 0.1,
pad_right: 0.1,
pad_top: -0.3,
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,
),
"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: 11.85,
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_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"),
],
),
(
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"),
],
),
],
)
+343
View File
@@ -0,0 +1,343 @@
(
locale: "US",
key_shapes: {
"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: 11.6,
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,
),
"rog_row_spacing": Blank(
width: 1.2,
height: 0.0,
),
"arrow_spacing": Blank(
width: 0.7,
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,
),
"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,
),
"rshift": Led(
width: 1.7,
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.0,
pad_left: 0.1,
pad_right: 0.1,
pad_top: 0.1,
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,
),
},
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"),
(RogFan, "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"),
(Up, "regular"),
(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"),
(PrtSc, "regular"),
(RCtrl, "regular"),
(Spacing, "arrow_spacing"),
(Left, "regular"),
(Down, "regular"),
(Right, "regular"),
(N0, "regular"),
(NumPadDel, "regular"),
],
),
(
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"),
],
),
],
)
-149
View File
@@ -1,149 +0,0 @@
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)",
}
}
}
+81 -48
View File
@@ -1,84 +1,114 @@
use dmi_id::DMIID;
use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use crate::usb::AuraDevice;
use crate::{AdvancedAuraType, AuraModeNum, AuraZone};
use crate::keyboard::AdvancedAuraType;
use crate::{AuraModeNum, AuraZone, PowerZones};
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:
pub struct LedSupportData {
/// This can be many different types of name:
/// - `/sys/class/dmi/id/board_name` (must use for laptops)
/// - The device name from `lsusb`
///
/// The laptop board_name is 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,
///
/// If using a device name the match is similar to the above where it can be
/// partial, so `ASUSTek Computer, Inc. ROG STRIX Arion` can be `STRIX
/// Arion` for short. Case insensitive.
pub device_name: String,
/// The product ID (usb only)
/// Example of using a product ID is:
/// ```ignore
/// $ lsusb
/// $ Bus 003 Device 003: ID 0b05:19b6 ASUSTek Computer, Inc. N-KEY Device
/// ```
/// here `19b6` is all that is required. Case insensitive.
#[serde(default)]
pub product_id: String,
/// Keyboard or device LED layout, this is the name of the externally
/// defined layout file. Optional, can be an empty string
#[serde(default)]
pub layout_name: String,
/// If empty will default to `Static` mode
pub basic_modes: Vec<AuraModeNum>,
/// Available on some laptops. This is where the keyboard may be split in to
/// 4 zones and may have a logo and lightbar.
///
/// Ignored if empty.
#[serde(default)]
pub basic_zones: Vec<AuraZone>,
/// `Zoned` or `PerKey`.
// TODO: remove and use layouts only
#[serde(default)]
pub advanced_type: AdvancedAuraType,
/// If empty will default to `Keyboard` power zone
pub power_zones: Vec<PowerZones>,
}
#[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");
impl LedSupportData {
/// Find the data for the device. This function will check DMI info for
/// matches against laptops first, then will proceed with matching the
/// `device_name` if there are no DMI matches.
pub fn get_data(product_id: &str) -> Self {
let dmi = DMIID::new().unwrap_or_default();
// 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) {
if let Some(data) = LedSupportFile::load_from_supoprt_db() {
if let Some(data) = data.match_device(&dmi.board_name, product_id) {
return data;
}
}
info!("Using generic LED control for keyboard brightness only");
LaptopLedData::default()
LedSupportData::default()
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct LedSupportFile(Vec<LedSupportData>);
impl LedSupportFile {
pub fn get(&self) -> &[LaptopLedData] {
pub fn get(&self) -> &[LedSupportData] {
&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> {
fn match_device(&self, device_name: &str, product_id: &str) -> Option<LedSupportData> {
for config in self.0.iter().rev() {
if board_name.contains(&config.board_name) {
info!("LedSupport: Matched to {}", config.board_name);
if device_name.contains(&config.device_name) {
info!("Matched to {}", config.device_name);
if !config.product_id.is_empty() {
info!("Checking product ID");
if config.product_id == product_id {
info!("Matched to {}", config.product_id);
return Some(config.clone());
} else {
continue;
}
}
return Some(config.clone());
}
}
None
}
pub fn load_from_config() -> Option<Self> {
/// Load `LedSupportFile` from the `aura_support.ron` file at
/// `/usr/share/asusd/aura_support.ron` and append with data from
/// `/etc/asusd/asusd_user_ledmodes.ron` if that file is available.
///
/// Returns `None` if neither file exists or does not parse correctly.
pub fn load_from_supoprt_db() -> Option<Self> {
let mut loaded = false;
let mut data = LedSupportFile::default();
// Load user configs first so they are first to be checked
@@ -111,7 +141,7 @@ impl LedSupportFile {
);
}
}
data.0.sort_by(|a, b| a.board_name.cmp(&b.board_name));
data.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
if loaded {
return Some(data);
@@ -130,20 +160,22 @@ mod tests {
use ron::ser::PrettyConfig;
use super::LaptopLedData;
use crate::advanced::LedCode;
use crate::aura_detection::LedSupportFile;
use super::LedSupportData;
use crate::aura_detection::{LedSupportFile, PowerZones};
use crate::keyboard::{AdvancedAuraType, LedCode};
// use crate::zoned::Zone;
use crate::{AdvancedAuraType, AuraModeNum, AuraZone};
use crate::{AuraModeNum, AuraZone};
#[test]
fn check_data_parse() {
let led = LaptopLedData {
board_name: "Test".to_owned(),
let led = LedSupportData {
device_name: "Test".to_owned(),
product_id: String::new(),
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]),
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
assert!(ron::to_string(&led).is_ok());
@@ -161,7 +193,8 @@ mod tests {
// 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));
tmp_sort.0.sort_by(|a, b| a.product_id.cmp(&b.product_id));
tmp_sort.0.sort_by(|a, b| a.device_name.cmp(&b.device_name));
if tmp != tmp_sort {
let sorted =
ron::ser::to_string_pretty(&tmp_sort, PrettyConfig::new().depth_limit(2)).unwrap();
+264 -81
View File
@@ -1,30 +1,51 @@
pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9];
pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
pub const LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
pub const LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e
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 typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
use zbus::zvariant::{OwnedValue, Type, Value};
use crate::error::Error;
use crate::LED_MSG_LEN;
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[typeshare]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
pub enum LedBrightness {
Off,
Low,
Med,
High,
Off = 0,
Low = 1,
#[default]
Med = 2,
High = 3,
}
impl From<u32> for LedBrightness {
fn from(bright: u32) -> Self {
impl LedBrightness {
pub const fn next(&self) -> Self {
match self {
Self::Off => Self::Low,
Self::Low => Self::Med,
Self::Med => Self::High,
Self::High => Self::Off,
}
}
pub const fn prev(&self) -> Self {
match self {
Self::Off => Self::High,
Self::Low => Self::Off,
Self::Med => Self::Low,
Self::High => Self::Med,
}
}
}
impl From<u8> for LedBrightness {
fn from(bright: u8) -> Self {
match bright {
0 => LedBrightness::Off,
1 => LedBrightness::Low,
@@ -34,13 +55,42 @@ impl From<u32> for LedBrightness {
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Copy, Deserialize, Serialize)]
pub struct Colour(pub u8, pub u8, pub u8);
impl From<LedBrightness> for u8 {
fn from(l: LedBrightness) -> Self {
l as u8
}
}
impl From<LedBrightness> for i32 {
fn from(l: LedBrightness) -> Self {
l as i32
}
}
impl From<i32> for LedBrightness {
fn from(l: i32) -> Self {
match l {
0 => LedBrightness::Off,
1 => LedBrightness::Low,
2 => LedBrightness::Med,
3 => LedBrightness::High,
_ => LedBrightness::Med,
}
}
}
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, PartialEq, Eq, Copy, Deserialize, Serialize)]
pub struct Colour {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Default for Colour {
fn default() -> Self {
Colour(166, 0, 0)
Colour { r: 166, g: 0, b: 0 }
}
}
@@ -54,50 +104,56 @@ impl FromStr for Colour {
let r = u8::from_str_radix(&s[0..2], 16).or(Err(Error::ParseColour))?;
let g = u8::from_str_radix(&s[2..4], 16).or(Err(Error::ParseColour))?;
let b = u8::from_str_radix(&s[4..6], 16).or(Err(Error::ParseColour))?;
Ok(Colour(r, g, b))
Ok(Colour { r, g, b })
}
}
impl From<&[f32; 3]> for Colour {
fn from(c: &[f32; 3]) -> Self {
Self(
(255.0 * c[0]) as u8,
(255.0 * c[1]) as u8,
(255.0 * c[2]) as u8,
)
Self {
r: (255.0 * c[0]) as u8,
g: (255.0 * c[1]) as u8,
b: (255.0 * c[2]) as u8,
}
}
}
impl From<Colour> for [f32; 3] {
fn from(c: Colour) -> Self {
[c.0 as f32 / 255.0, c.1 as f32 / 255.0, c.2 as f32 / 255.0]
[c.r as f32 / 255.0, c.g as f32 / 255.0, c.b as f32 / 255.0]
}
}
impl From<&[u8; 3]> for Colour {
fn from(c: &[u8; 3]) -> Self {
Self(c[0], c[1], c[2])
Self {
r: c[0],
g: c[1],
b: c[2],
}
}
}
impl From<Colour> for [u8; 3] {
fn from(c: Colour) -> Self {
[c.0, c.1, c.2]
[c.r, c.g, c.b]
}
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum Speed {
Low = 0xe1,
#[default]
Med = 0xeb,
High = 0xf5,
}
impl Default for Speed {
fn default() -> Self {
Speed::Med
}
}
impl FromStr for Speed {
type Err = Error;
@@ -112,6 +168,26 @@ impl FromStr for Speed {
}
}
impl From<i32> for Speed {
fn from(value: i32) -> Self {
match value {
0 => Self::Low,
2 => Self::High,
_ => Self::Med,
}
}
}
impl From<Speed> for i32 {
fn from(value: Speed) -> Self {
match value {
Speed::Low => 0,
Speed::Med => 1,
Speed::High => 2,
}
}
}
impl From<Speed> for u8 {
fn from(s: Speed) -> u8 {
match s {
@@ -124,19 +200,21 @@ impl From<Speed> for u8 {
/// Used for Rainbow mode.
///
/// Enum corresponds to the required integer value
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "s")
)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum Direction {
Right,
Left,
Up,
Down,
}
impl Default for Direction {
fn default() -> Self {
Direction::Right
}
#[default]
Right = 0,
Left = 1,
Up = 2,
Down = 3,
}
impl FromStr for Direction {
type Err = Error;
@@ -152,10 +230,32 @@ impl FromStr for Direction {
}
}
impl From<i32> for Direction {
fn from(value: i32) -> Self {
match value {
1 => Self::Left,
2 => Self::Up,
3 => Self::Down,
_ => Self::Right,
}
}
}
impl From<Direction> for i32 {
fn from(value: Direction) -> Self {
value as i32
}
}
/// Enum of modes that convert to the actual number required by a USB HID packet
#[cfg_attr(feature = "dbus", derive(Type))]
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
#[derive(
Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Deserialize, Serialize,
Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Deserialize, Serialize,
)]
pub enum AuraModeNum {
#[default]
@@ -241,27 +341,50 @@ impl From<u8> for AuraModeNum {
}
}
impl From<i32> for AuraModeNum {
fn from(mode: i32) -> Self {
(mode as u8).into()
}
}
impl From<AuraModeNum> for i32 {
fn from(value: AuraModeNum) -> Self {
value as i32
}
}
impl From<AuraEffect> for AuraModeNum {
fn from(value: AuraEffect) -> Self {
value.mode
}
}
/// Base effects have no zoning, while multizone is 1-4
#[cfg_attr(feature = "dbus", derive(Type))]
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum AuraZone {
/// Used if keyboard has no zones, or if setting all
#[default]
None,
None = 0,
/// Leftmost zone
Key1,
Key1 = 1,
/// Zone after leftmost
Key2,
Key2 = 2,
/// Zone second from right
Key3,
Key3 = 3,
/// Rightmost zone
Key4,
Key4 = 4,
/// Logo on the lid (or elsewhere?)
Logo,
Logo = 5,
/// The left part of a lightbar (typically on the front of laptop)
BarLeft,
BarLeft = 6,
/// The right part of a lightbar
BarRight,
BarRight = 7,
}
impl FromStr for AuraZone {
@@ -283,12 +406,34 @@ impl FromStr for AuraZone {
}
}
impl From<i32> for AuraZone {
fn from(value: i32) -> Self {
match value {
1 => Self::Key1,
2 => Self::Key2,
3 => Self::Key3,
4 => Self::Key4,
5 => Self::Logo,
6 => Self::BarLeft,
7 => Self::BarRight,
_ => Self::default(),
}
}
}
impl From<AuraZone> for i32 {
fn from(value: AuraZone) -> Self {
value as i32
}
}
/// Default factory modes structure. This easily converts to an USB HID packet
/// with:
/// ```rust
/// // let bytes: [u8; LED_MSG_LEN] = mode.into();
/// ```
#[cfg_attr(feature = "dbus", derive(Type))]
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AuraEffect {
/// The effect type
@@ -335,14 +480,20 @@ impl Default for AuraEffect {
Self {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour(166, 0, 0),
colour2: Colour(0, 0, 0),
colour1: Colour { r: 166, g: 0, b: 0 },
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Med,
direction: Direction::Right,
}
}
}
impl Display for AuraEffect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
pub struct AuraParameters {
pub zone: bool,
pub colour1: bool,
@@ -408,14 +559,14 @@ impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
msg[1] = 0xb3;
msg[2] = aura.zone as u8;
msg[3] = aura.mode as u8;
msg[4] = aura.colour1.0;
msg[5] = aura.colour1.1;
msg[6] = aura.colour1.2;
msg[4] = aura.colour1.r;
msg[5] = aura.colour1.g;
msg[6] = aura.colour1.b;
msg[7] = aura.speed as u8;
msg[8] = aura.direction as u8;
msg[10] = aura.colour2.0;
msg[11] = aura.colour2.1;
msg[12] = aura.colour2.2;
msg[10] = aura.colour2.r;
msg[11] = aura.colour2.g;
msg[12] = aura.colour2.b;
msg
}
}
@@ -427,14 +578,14 @@ impl From<&AuraEffect> for Vec<u8> {
msg[1] = 0xb3;
msg[2] = aura.zone as u8;
msg[3] = aura.mode as u8;
msg[4] = aura.colour1.0;
msg[5] = aura.colour1.1;
msg[6] = aura.colour1.2;
msg[4] = aura.colour1.r;
msg[5] = aura.colour1.g;
msg[6] = aura.colour1.b;
msg[7] = aura.speed as u8;
msg[8] = aura.direction as u8;
msg[10] = aura.colour2.0;
msg[11] = aura.colour2.1;
msg[12] = aura.colour2.2;
msg[10] = aura.colour2.r;
msg[11] = aura.colour2.g;
msg[12] = aura.colour2.b;
msg
}
}
@@ -448,7 +599,11 @@ mod tests {
let st = AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::None,
colour1: Colour(0xff, 0x11, 0xdd),
colour1: Colour {
r: 0xff,
g: 0x11,
b: 0xdd,
},
colour2: Colour::default(),
speed: Speed::Med,
direction: Direction::Right,
@@ -468,8 +623,12 @@ mod tests {
let mut st = AuraEffect {
mode: AuraModeNum::Static,
zone: AuraZone::Key1,
colour1: Colour(0xff, 0, 0),
colour2: Colour(0, 0, 0),
colour1: Colour {
r: 0xff,
g: 0,
b: 0,
},
colour2: Colour { r: 0, g: 0, b: 0 },
speed: Speed::Low,
direction: Direction::Left,
};
@@ -480,7 +639,11 @@ mod tests {
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key2;
st.colour1 = Colour(0xff, 0xff, 0);
st.colour1 = Colour {
r: 0xff,
g: 0xff,
b: 0,
};
let capture = [
0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
@@ -488,7 +651,11 @@ mod tests {
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key3;
st.colour1 = Colour(0, 0xff, 0xff);
st.colour1 = Colour {
r: 0,
g: 0xff,
b: 0xff,
};
let capture = [
0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
@@ -496,7 +663,11 @@ mod tests {
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Key4;
st.colour1 = Colour(0xff, 0x00, 0xff);
st.colour1 = Colour {
r: 0xff,
g: 0x00,
b: 0xff,
};
let capture = [
0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
@@ -504,7 +675,11 @@ mod tests {
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::Logo;
st.colour1 = Colour(0x2c, 0xff, 0x00);
st.colour1 = Colour {
r: 0x2c,
g: 0xff,
b: 0x00,
};
let capture = [
0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
@@ -512,7 +687,11 @@ mod tests {
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::BarLeft;
st.colour1 = Colour(0xff, 0x00, 0x00);
st.colour1 = Colour {
r: 0xff,
g: 0x00,
b: 0x00,
};
let capture = [
0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
@@ -520,7 +699,11 @@ mod tests {
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]);
st.zone = AuraZone::BarRight;
st.colour1 = Colour(0xff, 0x00, 0xcd);
st.colour1 = Colour {
r: 0xff,
g: 0x00,
b: 0xcd,
};
let capture = [
0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
+2 -2
View File
@@ -1,5 +1,5 @@
use super::{EffectState, InputForEffect};
use crate::advanced::LedCode;
use crate::keyboard::{KeyLayout, LedCode};
use crate::Colour;
pub struct InputBased {
@@ -14,7 +14,7 @@ pub struct InputBased {
}
impl EffectState for InputBased {
fn next_colour_state(&mut self, _layout: &crate::layouts::KeyLayout) {
fn next_colour_state(&mut self, _layout: &KeyLayout) {
self.input.next_colour_state();
self.colour = self.input.get_colour();
}
+16 -14
View File
@@ -1,8 +1,7 @@
use serde::{Deserialize, Serialize};
use super::EffectState;
use crate::advanced::LedCode;
use crate::layouts::KeyLayout;
use crate::keyboard::{KeyLayout, LedCode};
use crate::{effect_state_impl, Colour, Speed};
#[derive(Debug, Clone, Deserialize, Serialize)]
@@ -53,30 +52,33 @@ impl EffectState for Breathe {
let speed = 4 - <u8>::from(*speed);
if *colour_actual == Colour(0, 0, 0) {
if *colour_actual == (Colour { r: 0, g: 0, b: 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;
let r1_scale = colour.r / speed / 2;
let g1_scale = colour.g / speed / 2;
let b1_scale = colour.b / speed / 2;
if *colour_actual == Colour(0, 0, 0) {
if *colour_actual == (Colour { r: 0, g: 0, b: 0 }) {
*flipped = true;
} else if colour_actual >= colour {
} else if colour_actual.r >= colour.r
&& colour_actual.g >= colour.g
&& colour_actual.b >= colour.b
{
*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);
colour_actual.r = colour_actual.r.saturating_sub(r1_scale);
colour_actual.g = colour_actual.g.saturating_sub(g1_scale);
colour_actual.b = colour_actual.b.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);
colour_actual.r = colour_actual.r.saturating_add(r1_scale);
colour_actual.g = colour_actual.g.saturating_add(g1_scale);
colour_actual.b = colour_actual.b.saturating_add(b1_scale);
}
}
}
+24 -25
View File
@@ -1,8 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::advanced::LedCode;
use crate::effects::{p_random, EffectState};
use crate::layouts::KeyLayout;
use crate::keyboard::{KeyLayout, LedCode};
use crate::{effect_state_impl, Colour};
#[derive(Debug, Clone, Deserialize, Serialize)]
@@ -51,17 +50,17 @@ impl EffectState for DoomFlicker {
}
// TODO: make a "percentage" method on Colour.
let max_light = Colour(
(start_colour.0 as f32 / 100.0 * *max_percentage as f32) as u8,
(start_colour.1 as f32 / 100.0 * *max_percentage as f32) as u8,
(start_colour.2 as f32 / 100.0 * *max_percentage as f32) as u8,
);
let max_light = Colour {
r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8,
};
// min light is a percentage of the set colour
let min_light = Colour(
(start_colour.0 as f32 / 100.0 * *min_percentage as f32) as u8,
(start_colour.1 as f32 / 100.0 * *min_percentage as f32) as u8,
(start_colour.2 as f32 / 100.0 * *min_percentage as f32) as u8,
);
let min_light = Colour {
r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8,
};
// Convert the 255 to percentage
let amount = (p_random() & 7) as f32 * 8.0;
@@ -76,9 +75,9 @@ impl EffectState for DoomFlicker {
*colour = (max - max_amount) as u8;
}
};
set_colour(&mut colour.0, max_light.0 as f32, min_light.0 as f32);
set_colour(&mut colour.1, max_light.1 as f32, min_light.1 as f32);
set_colour(&mut colour.2, max_light.2 as f32, min_light.2 as f32);
set_colour(&mut colour.r, max_light.r as f32, min_light.r as f32);
set_colour(&mut colour.g, max_light.g as f32, min_light.g as f32);
set_colour(&mut colour.b, max_light.b as f32, min_light.b as f32);
self.count = 4;
}
@@ -133,17 +132,17 @@ impl EffectState for DoomLightFlash {
}
// TODO: make a "percentage" method on Colour.
let max_light = Colour(
(start_colour.0 as f32 / 100.0 * *max_percentage as f32) as u8,
(start_colour.1 as f32 / 100.0 * *max_percentage as f32) as u8,
(start_colour.2 as f32 / 100.0 * *max_percentage as f32) as u8,
);
let max_light = Colour {
r: (start_colour.r as f32 / 100.0 * *max_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *max_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *max_percentage as f32) as u8,
};
// min light is a percentage of the set colour
let min_light = Colour(
(start_colour.0 as f32 / 100.0 * *min_percentage as f32) as u8,
(start_colour.1 as f32 / 100.0 * *min_percentage as f32) as u8,
(start_colour.2 as f32 / 100.0 * *min_percentage as f32) as u8,
);
let min_light = Colour {
r: (start_colour.r as f32 / 100.0 * *min_percentage as f32) as u8,
g: (start_colour.g as f32 / 100.0 * *min_percentage as f32) as u8,
b: (start_colour.b as f32 / 100.0 * *min_percentage as f32) as u8,
};
if *colour == max_light {
*colour = min_light;
+30 -14
View File
@@ -12,8 +12,7 @@ pub use breathe::*;
mod static_;
pub use static_::*;
use crate::advanced::{LedCode, LedUsbPackets, UsbPackets};
use crate::layouts::KeyLayout;
use crate::keyboard::{KeyLayout, LedCode, LedUsbPackets, UsbPackets};
use crate::Colour;
// static mut RNDINDEX: usize = 0;
@@ -118,7 +117,7 @@ impl AdvancedEffects {
for effect in &self.effects {
let c = effect.colour();
usb_packets.set(effect.led(), c.0, c.1, c.2);
usb_packets.set(effect.led(), c.r, c.g, c.b);
}
usb_packets.into()
}
@@ -132,12 +131,12 @@ macro_rules! effect_state_impl {
self.colour
}
fn get_led(&self) -> $crate::advanced::LedCode {
fn get_led(&self) -> $crate::keyboard::LedCode {
self.led.clone()
}
/// Change the led type
fn set_led(&mut self, address: $crate::advanced::LedCode) {
fn set_led(&mut self, address: $crate::keyboard::LedCode) {
self.led = address;
}
};
@@ -148,14 +147,14 @@ macro_rules! effect_impl {
($($effect:ident),*) => {
impl Effect {
/// Get the type of LED set
pub fn led(&self) -> $crate::advanced::LedCode {
pub fn led(&self) -> $crate::keyboard::LedCode {
match self {
$(Effect::$effect(c) => c.get_led(),)*
}
}
/// Change the led type (can be used to change location of the effect)
pub fn set_led(&mut self, address: $crate::advanced::LedCode) {
pub fn set_led(&mut self, address: $crate::keyboard::LedCode) {
match self {
$(Effect::$effect(c) => c.set_led(address),)*
}
@@ -200,17 +199,22 @@ effect_impl!(Static, Breathe, DoomFlicker, DoomLightFlash);
#[cfg(test)]
mod tests {
use crate::advanced::LedCode;
use crate::effects::{AdvancedEffects, Breathe, DoomFlicker, Effect, Static};
use crate::layouts::KeyLayout;
use crate::keyboard::{KeyLayout, LedCode};
use crate::{Colour, Speed};
#[test]
fn single_key_next_state_then_create() {
let layout = KeyLayout::default_layout();
let mut seq = AdvancedEffects::new(false);
seq.effects
.push(Effect::Static(Static::new(LedCode::F, Colour(255, 127, 0))));
seq.effects.push(Effect::Static(Static::new(
LedCode::F,
Colour {
r: 255,
g: 127,
b: 0,
},
)));
seq.next_state(&layout);
let packets = seq.create_packets();
@@ -227,8 +231,16 @@ mod tests {
let mut seq = AdvancedEffects::new(false);
seq.effects.push(Effect::Breathe(Breathe::new(
LedCode::F,
Colour(255, 127, 0),
Colour(127, 0, 255),
Colour {
r: 255,
g: 127,
b: 0,
},
Colour {
r: 127,
g: 0,
b: 255,
},
Speed::Med,
)));
@@ -261,7 +273,11 @@ mod tests {
let mut seq = AdvancedEffects::new(false);
seq.effects.push(Effect::DoomFlicker(DoomFlicker::new(
LedCode::F,
Colour(255, 127, 80),
Colour {
r: 255,
g: 127,
b: 80,
},
100,
10,
)));
+1 -2
View File
@@ -1,8 +1,7 @@
use serde::{Deserialize, Serialize};
use super::EffectState;
use crate::advanced::LedCode;
use crate::layouts::KeyLayout;
use crate::keyboard::{KeyLayout, LedCode};
use crate::{effect_state_impl, Colour};
#[derive(Debug, Clone, Deserialize, Serialize)]
@@ -1,8 +1,12 @@
use log::warn;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
// TODO: GZ301Z
// 5dbcd0010300000a00ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000ff0000
/// 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).
@@ -190,6 +194,7 @@ impl LedCode {
}
/// Represents the per-key raw USB packets
#[typeshare]
pub type UsbPackets = Vec<Vec<u8>>;
/// A `UsbPackets` contains all data to change the full set of keyboard
@@ -199,6 +204,7 @@ pub type UsbPackets = Vec<Vec<u8>>;
/// 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.
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LedUsbPackets {
@@ -487,9 +493,157 @@ impl From<LedUsbPackets> for UsbPackets {
}
}
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)",
}
}
}
#[cfg(test)]
mod tests {
use crate::advanced::{LedCode, LedUsbPackets, UsbPackets};
use crate::keyboard::{LedCode, LedUsbPackets, UsbPackets};
macro_rules! colour_check_zoned {
($zone:expr, $pkt_idx_start:expr) => {
@@ -8,10 +8,10 @@ use std::slice::Iter;
use log::warn;
use serde::{Deserialize, Serialize};
use crate::advanced::LedCode;
use crate::aura_detection::LaptopLedData;
use crate::aura_detection::LedSupportData;
use crate::error::Error;
use crate::{AdvancedAuraType, AuraModeNum, AuraZone};
use crate::keyboard::{AdvancedAuraType, LedCode};
use crate::{AuraModeNum, AuraZone};
/// The `key_type` plays a role in effects (eventually). You could for example
/// add a `ShapeType::Spacing` to pad out an effect, such as a laserbeam across
@@ -189,7 +189,7 @@ pub struct KeyLayout {
}
impl KeyLayout {
pub fn from_file(path: &Path) -> Result<Self, Error> {
fn from_file(path: &Path) -> Result<Self, Error> {
let buf: String = std::fs::read_to_string(path)
.map_err(|e| Error::IoPath(path.to_string_lossy().to_string(), e))?;
if buf.is_empty() {
@@ -278,7 +278,7 @@ impl KeyLayout {
}
/// Find a layout matching the name in `LaptopLedData` in the provided dir
pub fn find_layout(led_data: LaptopLedData, mut data_path: PathBuf) -> Result<Self, Error> {
pub fn find_layout(led_data: LedSupportData, mut data_path: PathBuf) -> Result<Self, Error> {
// TODO: locales
let layout_name = if led_data.layout_name.is_empty() {
"ga401q".to_owned() // Need some sort of default here due to ROGCC
@@ -459,7 +459,7 @@ mod tests {
use std::path::PathBuf;
use crate::aura_detection::LedSupportFile;
use crate::layouts::KeyLayout;
use crate::keyboard::KeyLayout;
#[test]
fn check_parse_all() {
@@ -531,6 +531,10 @@ mod tests {
data_path.push("loop_prep");
for config in data.get().iter().rev() {
if config.layout_name.is_empty() {
continue;
}
buf.clear();
let layout_file = format!("{}_US.ron", config.layout_name);
@@ -543,7 +547,7 @@ mod tests {
.map_err(|e| {
panic!(
"Error checking {data_path:?} for {} : {e:?}",
config.board_name
config.device_name
)
})
.unwrap();
@@ -551,7 +555,7 @@ mod tests {
if let Err(e) = file.read_to_string(&mut buf) {
panic!(
"Error checking {data_path:?} for {} : {e:?}",
config.board_name
config.device_name
)
}
if let Err(e) = ron::from_str::<KeyLayout>(&buf) {
+20
View File
@@ -0,0 +1,20 @@
/// All handling for `RgbAddress`ing.
mod advanced;
pub use advanced::*;
/// Helpers for consructing keyboard layouts for UI use and effects
mod layouts;
pub use layouts::*;
mod power;
pub use power::*;
#[derive(Debug, Clone, PartialEq, Eq, Default, serde::Deserialize, serde::Serialize)]
pub enum AdvancedAuraType {
/// A `None` will apply the effect to the whole keyboard via basic-static
/// mode
#[default]
None,
Zoned(Vec<LedCode>),
PerKey,
}
+583
View File
@@ -0,0 +1,583 @@
//! Power state for Laptop MCU RGB/LED. This is generally for newer
//! 0x18c6, 0x19B6, 0x1a30, keyboard models (2021+)
use std::fmt::Debug;
use std::ops::{BitAnd, BitOr};
use log::warn;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
use crate::aura_detection::LedSupportData;
use crate::{AuraDeviceType, PowerZones};
/// Meaning of this struct depends on the laptop generation.
/// - 2021+, the struct is a single zone with 4 states
/// - pre-2021, the struct is 1 or 2 zones and 3 states
/// - Tuf, the struct is 1 zone and 3 states
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuraPowerState {
pub zone: PowerZones,
pub boot: bool,
pub awake: bool,
pub sleep: bool,
/// Ignored for pre-2021 and Tuf
pub shutdown: bool,
}
impl Default for AuraPowerState {
/// Defaults all to off
fn default() -> Self {
Self {
zone: PowerZones::Keyboard,
boot: true,
awake: true,
sleep: true,
shutdown: true,
}
}
}
impl AuraPowerState {
fn default_for(zone: PowerZones) -> Self {
Self {
zone,
boot: true,
awake: true,
sleep: true,
shutdown: true,
}
}
fn tuf_to_bytes(&self) -> Vec<u8> {
todo!("0s and 1s for bool array")
}
/// # Bits for older 0x1866 keyboard model
///
/// Keybord and Lightbar require Awake, Boot and Sleep apply to both
/// Keybord and Lightbar regardless of if either are enabled (or Awake is
/// enabled)
///
/// | Byte 1 | Byte 2 | Byte 3 | function | hex |
/// |------------|------------|------------|----------|----------|
/// | 0000, 0000 | 0000, 0000 | 0000, 0010 | Awake | 00,00,02 |
/// | 0000, 1000 | 0000, 0000 | 0000, 0000 | Keyboard | 08,00,00 |
/// | 0000, 0100 | 0000, 0101 | 0000, 0000 | Lightbar | 04,05,00 |
/// | 1100, 0011 | 0001, 0010 | 0000, 1001 | Boot/Sht | c3,12,09 |
/// | 0011, 0000 | 0000, 1000 | 0000, 0100 | Sleep | 30,08,04 |
/// | 1111, 1111 | 0001, 1111 | 0000, 1111 | all on | |
fn old_to_bytes(&self) -> Vec<u8> {
let mut a: u32 = 0;
if self.awake {
a |= OldAuraPower::Awake as u32;
}
if self.boot {
a |= OldAuraPower::Boot as u32;
}
if self.sleep {
a |= OldAuraPower::Sleep as u32;
}
if matches!(
self.zone,
PowerZones::Keyboard | PowerZones::KeyboardAndLightbar
) {
a |= OldAuraPower::Keyboard as u32;
}
if matches!(
self.zone,
PowerZones::Lightbar | PowerZones::KeyboardAndLightbar
) {
a |= OldAuraPower::Lightbar as u32;
}
vec![
((a & 0xff0000) >> 16) as u8,
((a & 0xff00) >> 8) as u8,
(a & 0xff) as u8,
0x00,
]
}
fn new_to_byte(&self) -> u32 {
match self.zone {
PowerZones::Logo => {
self.boot as u32
| (self.awake as u32) << 2
| (self.sleep as u32) << 4
| (self.shutdown as u32) << 6
}
PowerZones::Keyboard => {
(self.boot as u32) << 1
| (self.awake as u32) << 3
| (self.sleep as u32) << 5
| (self.shutdown as u32) << 7
}
PowerZones::Lightbar => {
(self.boot as u32) << (7 + 2)
| (self.awake as u32) << (7 + 3)
| (self.sleep as u32) << (7 + 4)
| (self.shutdown as u32) << (7 + 5)
}
PowerZones::Lid => {
(self.boot as u32) << (15 + 1)
| (self.awake as u32) << (15 + 2)
| (self.sleep as u32) << (15 + 3)
| (self.shutdown as u32) << (15 + 4)
}
PowerZones::RearGlow => {
(self.boot as u32) << (23 + 1)
| (self.awake as u32) << (23 + 2)
| (self.sleep as u32) << (23 + 3)
| (self.shutdown as u32) << (23 + 4)
}
PowerZones::KeyboardAndLightbar | PowerZones::None => 0,
}
}
}
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LaptopAuraPower {
pub states: Vec<AuraPowerState>,
}
impl LaptopAuraPower {
/// # Bits for newer 0x18c6, 0x19B6, 0x1a30, keyboard models
///
/// | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Label |
/// |--------|---------|---------|---------|----------|
/// |00000001| 00000000| 00000000| 00000000|boot_logo_|
/// |00000010| 00000000| 00000000| 00000000|boot_keyb_|
/// |00000100| 00000000| 00000000| 00000000|awake_logo|
/// |00001000| 00000000| 00000000| 00000000|awake_keyb|
/// |00010000| 00000000| 00000000| 00000000|sleep_logo|
/// |00100000| 00000000| 00000000| 00000000|sleep_keyb|
/// |01000000| 00000000| 00000000| 00000000|shut_logo_|
/// |10000000| 00000000| 00000000| 00000000|shut_keyb_|
/// |00000000| 00000010| 00000000| 00000000|boot_bar__|
/// |00000000| 00000100| 00000000| 00000000|awake_bar_|
/// |00000000| 00001000| 00000000| 00000000|sleep_bar_|
/// |00000000| 00010000| 00000000| 00000000|shut_bar__|
/// |00000000| 00000000| 00000001| 00000000|boot_lid__|
/// |00000000| 00000000| 00000010| 00000000|awkae_lid_|
/// |00000000| 00000000| 00000100| 00000000|sleep_lid_|
/// |00000000| 00000000| 00001000| 00000000|shut_lid__|
/// |00000000| 00000000| 00000000| 00000001|boot_rear_|
/// |00000000| 00000000| 00000000| 00000010|awake_rear|
/// |00000000| 00000000| 00000000| 00000100|sleep_rear|
/// |00000000| 00000000| 00000000| 00001000|shut_rear_|
fn new_to_bytes(&self) -> Vec<u8> {
let mut a: u32 = 0;
for state in self.states.iter() {
a |= state.new_to_byte();
}
vec![
(a & 0xff) as u8,
((a & 0xff00) >> 8) as u8,
((a & 0xff0000) >> 16) as u8,
((a & 0xff000000) >> 24) as u8,
]
}
// TODO: use support data to setup correct zones
pub fn new(aura_type: AuraDeviceType, support_data: &LedSupportData) -> Self {
match aura_type {
AuraDeviceType::Unknown | AuraDeviceType::LaptopPost2021 => {
let mut states = Vec::new();
for zone in support_data.power_zones.iter() {
states.push(AuraPowerState::default_for(*zone))
}
Self { states }
}
AuraDeviceType::LaptopPre2021 => {
if support_data.power_zones.contains(&PowerZones::Lightbar) {
Self {
states: vec![AuraPowerState::default_for(PowerZones::KeyboardAndLightbar)],
}
} else {
Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)],
}
}
}
AuraDeviceType::LaptopTuf => Self {
states: vec![AuraPowerState::default_for(PowerZones::Keyboard)],
},
AuraDeviceType::ScsiExtDisk => todo!(),
}
}
pub fn to_bytes(&self, aura_type: AuraDeviceType) -> Vec<u8> {
match aura_type {
AuraDeviceType::LaptopPost2021 => self.new_to_bytes(),
AuraDeviceType::LaptopPre2021 => self
.states
.first()
.cloned()
.unwrap_or_default()
.old_to_bytes(),
AuraDeviceType::LaptopTuf => self
.states
.first()
.cloned()
.unwrap_or_default()
.tuf_to_bytes(),
AuraDeviceType::Unknown => {
warn!("Trying to create bytes for an unknown device");
self.new_to_bytes()
}
AuraDeviceType::ScsiExtDisk => todo!(),
}
}
}
/// | Byte 1 | Byte 2 | Byte 3 | function | hex |
/// |------------|------------|------------|----------|----------|
/// | 0000, 0000 | 0000, 0000 | 0000, 0010 | Awake | 00,00,02 |
/// | 0000, 1000 | 0000, 0000 | 0000, 0000 | Keyboard | 08,00,00 |
/// | 0000, 0100 | 0000, 0101 | 0000, 0000 | Lightbar | 04,05,00 |
/// | 1100, 0011 | 0001, 0010 | 0000, 1001 | Boot/Sht | c3,12,09 |
/// | 0011, 0000 | 0000, 1000 | 0000, 0100 | Sleep | 30,08,04 |
/// | 1111, 1111 | 0001, 1111 | 0000, 1111 | all on | |
#[repr(u32)]
enum OldAuraPower {
Awake = 0x000002,
Boot = 0xc31209,
Sleep = 0x300804,
Keyboard = 0x080000,
Lightbar = 0x040500,
}
impl BitOr<OldAuraPower> for OldAuraPower {
type Output = u32;
fn bitor(self, rhs: OldAuraPower) -> Self::Output {
self as u32 | rhs as u32
}
}
impl BitAnd<OldAuraPower> for OldAuraPower {
type Output = u32;
fn bitand(self, rhs: OldAuraPower) -> Self::Output {
self as u32 & rhs as u32
}
}
impl From<OldAuraPower> for u32 {
fn from(a: OldAuraPower) -> Self {
a as u32
}
}
#[cfg(test)]
mod test {
use crate::keyboard::{AuraPowerState, LaptopAuraPower};
use crate::{AuraDeviceType, PowerZones};
#[test]
fn check_0x1866_control_bytes() {
let state = AuraPowerState {
zone: PowerZones::Keyboard,
awake: true,
boot: false,
sleep: false,
shutdown: false,
};
let bytes = state.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x08, 0x00, 0x02, 0x00]);
let state = AuraPowerState {
zone: PowerZones::Lightbar,
awake: true,
boot: false,
sleep: false,
shutdown: false,
};
let bytes = state.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x04, 0x05, 0x02, 0x00]);
let bytes = AuraPowerState {
zone: PowerZones::None,
awake: false,
boot: false,
sleep: true,
shutdown: false,
};
let bytes = bytes.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x30, 0x08, 0x04, 0x00]);
let bytes = AuraPowerState {
zone: PowerZones::None,
awake: false,
boot: true,
sleep: false,
shutdown: false,
};
let bytes = bytes.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xc3, 0x12, 0x09, 0x00]);
let power = AuraPowerState {
zone: PowerZones::KeyboardAndLightbar,
awake: true,
boot: true,
sleep: true,
shutdown: false,
};
let bytes = power.old_to_bytes();
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xff, 0x1f, 0x000f, 0x00]);
}
#[test]
fn check_0x19b6_control_bytes_binary_rep() {
fn to_binary_string(power: &LaptopAuraPower) -> String {
let bytes = power.to_bytes(AuraDeviceType::LaptopPost2021);
format!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
)
}
let boot_logo_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let boot_keyb_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let sleep_logo = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let sleep_keyb = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let awake_logo = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let awake_keyb = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let shut_logo_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Logo,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
let shut_keyb_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Keyboard,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
let boot_bar__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let awake_bar_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let sleep_bar_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let shut_bar__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lightbar,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
let boot_lid__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let awake_lid_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let sleep_lid_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let shut_lid__ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::Lid,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
let boot_rear_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: true,
awake: false,
sleep: false,
shutdown: false,
}],
});
let awake_rear = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: true,
sleep: false,
shutdown: false,
}],
});
let sleep_rear = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: true,
shutdown: false,
}],
});
let shut_rear_ = to_binary_string(&LaptopAuraPower {
states: vec![AuraPowerState {
zone: PowerZones::RearGlow,
boot: false,
awake: false,
sleep: false,
shutdown: true,
}],
});
assert_eq!(boot_logo_, "00000001, 00000000, 00000000, 00000000");
assert_eq!(boot_keyb_, "00000010, 00000000, 00000000, 00000000");
assert_eq!(awake_logo, "00000100, 00000000, 00000000, 00000000");
assert_eq!(awake_keyb, "00001000, 00000000, 00000000, 00000000");
assert_eq!(sleep_logo, "00010000, 00000000, 00000000, 00000000");
assert_eq!(sleep_keyb, "00100000, 00000000, 00000000, 00000000");
assert_eq!(shut_logo_, "01000000, 00000000, 00000000, 00000000");
assert_eq!(shut_keyb_, "10000000, 00000000, 00000000, 00000000");
//
assert_eq!(boot_bar__, "00000000, 00000010, 00000000, 00000000");
assert_eq!(awake_bar_, "00000000, 00000100, 00000000, 00000000");
assert_eq!(sleep_bar_, "00000000, 00001000, 00000000, 00000000");
assert_eq!(shut_bar__, "00000000, 00010000, 00000000, 00000000");
//
assert_eq!(boot_lid__, "00000000, 00000000, 00000001, 00000000");
assert_eq!(awake_lid_, "00000000, 00000000, 00000010, 00000000");
assert_eq!(sleep_lid_, "00000000, 00000000, 00000100, 00000000");
assert_eq!(shut_lid__, "00000000, 00000000, 00001000, 00000000");
//
assert_eq!(boot_rear_, "00000000, 00000000, 00000000, 00000001");
assert_eq!(awake_rear, "00000000, 00000000, 00000000, 00000010");
assert_eq!(sleep_rear, "00000000, 00000000, 00000000, 00000100");
assert_eq!(shut_rear_, "00000000, 00000000, 00000000, 00001000");
// All on
let byte1 = to_binary_string(&LaptopAuraPower {
states: vec![
AuraPowerState {
zone: PowerZones::Keyboard,
..Default::default()
},
AuraPowerState {
zone: PowerZones::Lid,
..Default::default()
},
AuraPowerState {
zone: PowerZones::Logo,
..Default::default()
},
AuraPowerState {
zone: PowerZones::Lightbar,
..Default::default()
},
AuraPowerState {
zone: PowerZones::RearGlow,
..Default::default()
},
],
});
assert_eq!(byte1, "11111111, 00011110, 00001111, 00001111");
}
}
+116 -25
View File
@@ -1,41 +1,132 @@
mod builtin_modes;
use advanced::LedCode;
pub use builtin_modes::*;
// TODO: Generic builtin modes
// TODO: Traits for finding device + writing generic modes
// TODO: Traits for writing aura_sync
// TODO: separate keyboard and laptop parts?
use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
/// A container of images/grids/gifs/pauses which can be iterated over to
/// generate cool effects
pub mod effects;
/// All handling for `RgbAddress`ing.
pub mod advanced;
/// Convert the `RgbAddress` to `&str` labels
pub mod advanced_to_str;
pub mod error;
pub use advanced_to_str::*;
mod builtin_modes;
pub use builtin_modes::*;
/// Helper for detecting what is available
pub mod aura_detection;
/// Helpers for consructing keyboard layouts for UI use and effects
pub mod layouts;
pub mod error;
pub mod usb;
pub mod keyboard;
pub const LED_MSG_LEN: usize = 17;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RED: Colour = Colour(0xff, 0x00, 0x00);
pub const GREEN: Colour = Colour(0x00, 0xff, 0x00);
pub const BLUE: Colour = Colour(0x00, 0x00, 0xff);
pub const VIOLET: Colour = Colour(0x9b, 0x26, 0xb6);
pub const TEAL: Colour = Colour(0x00, 0x7c, 0x80);
pub const YELLOW: Colour = Colour(0xff, 0xef, 0x00);
pub const ORANGE: Colour = Colour(0xff, 0xa4, 0x00);
pub const RED: Colour = Colour {
r: 0xff,
g: 0x00,
b: 0x00,
};
pub const GREEN: Colour = Colour {
r: 0x00,
g: 0xff,
b: 0x00,
};
pub const BLUE: Colour = Colour {
r: 0x00,
g: 0x00,
b: 0xff,
};
pub const VIOLET: Colour = Colour {
r: 0x9b,
g: 0x26,
b: 0xb6,
};
pub const TEAL: Colour = Colour {
r: 0x00,
g: 0x7c,
b: 0x80,
};
pub const YELLOW: Colour = Colour {
r: 0xff,
g: 0xef,
b: 0x00,
};
pub const ORANGE: Colour = Colour {
r: 0xff,
g: 0xa4,
b: 0x00,
};
pub const GRADIENT: [Colour; 7] = [RED, VIOLET, BLUE, TEAL, GREEN, YELLOW, ORANGE];
#[derive(Debug, Clone, PartialEq, Eq, Default, serde::Deserialize, serde::Serialize)]
pub enum AdvancedAuraType {
/// A `None` will apply the effect to the whole keyboard via basic-static
/// mode
#[typeshare]
#[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AuraDeviceType {
/// Most new laptops
#[default]
None,
Zoned(Vec<LedCode>),
PerKey,
LaptopPost2021 = 0,
LaptopPre2021 = 1,
LaptopTuf = 2,
ScsiExtDisk = 3,
Unknown = 255,
}
impl AuraDeviceType {
pub fn is_old_laptop(&self) -> bool {
*self == Self::LaptopPre2021
}
pub fn is_tuf_laptop(&self) -> bool {
*self == Self::LaptopTuf
}
pub fn is_new_laptop(&self) -> bool {
*self == Self::LaptopPost2021
}
pub fn is_scsi(&self) -> bool {
*self == Self::ScsiExtDisk
}
}
impl From<&str> for AuraDeviceType {
fn from(s: &str) -> Self {
match s.to_lowercase().trim_start_matches("0x") {
"tuf" => AuraDeviceType::LaptopTuf,
"1932" => AuraDeviceType::ScsiExtDisk,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopPre2021,
_ => Self::LaptopPost2021,
}
}
}
/// The powerr zones this laptop supports
#[typeshare]
#[cfg_attr(
feature = "dbus",
derive(Type, Value, OwnedValue),
zvariant(signature = "u")
)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Default, Copy, Clone)]
pub enum PowerZones {
/// The logo on some laptop lids
Logo = 0,
/// The full keyboard (not zones)
#[default]
Keyboard = 1,
/// The lightbar, typically on the front of the laptop
Lightbar = 2,
/// The leds that may be placed around the edge of the laptop lid
Lid = 3,
/// The led strip on the rear of some laptops
RearGlow = 4,
/// On pre-2021 laptops there is either 1 or 2 zones used
KeyboardAndLightbar = 5,
None = 255,
}
-510
View File
@@ -1,16 +1,3 @@
use std::fmt::Debug;
use std::ops::{BitAnd, BitOr};
use serde::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::Type;
pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9];
pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
pub const LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
pub const LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e
pub const LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
// Only these two packets must be 17 bytes
pub const LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
pub const LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
@@ -21,500 +8,3 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
0x5a, 0xba, 0xc5, 0xc4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Serialize, Deserialize, Default)]
pub enum AuraDevice {
Tuf,
X1854,
X1869,
X1866,
X18c6,
#[default]
X19b6,
X1a30,
Unknown,
}
impl From<AuraDevice> for &str {
fn from(a: AuraDevice) -> Self {
match a {
AuraDevice::Tuf => "tuf",
AuraDevice::X1854 => "1854",
AuraDevice::X1869 => "1869",
AuraDevice::X1866 => "1866",
AuraDevice::X18c6 => "18c6",
AuraDevice::X19b6 => "19b6",
AuraDevice::X1a30 => "1a30",
AuraDevice::Unknown => "unknown",
}
}
}
impl From<&str> for AuraDevice {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"tuf" => AuraDevice::Tuf,
"1866" | "0x1866" => AuraDevice::X1866,
"18c6" | "0x18c6" => AuraDevice::X18c6,
"1869" | "0x1869" => AuraDevice::X1869,
"1854" | "0x1854" => AuraDevice::X1854,
"19b6" | "0x19b6" => AuraDevice::X19b6,
"1a30" | "0x1a30" => AuraDevice::X1a30,
_ => AuraDevice::Unknown,
}
}
}
impl Debug for AuraDevice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Tuf => write!(f, "Tuf"),
Self::X1854 => write!(f, "0x1854"),
Self::X1869 => write!(f, "0x1869"),
Self::X1866 => write!(f, "0x1866"),
Self::X18c6 => write!(f, "0x18c6"),
Self::X19b6 => write!(f, "0x19B6"),
Self::X1a30 => write!(f, "0x1A30"),
Self::Unknown => write!(f, "Unknown"),
}
}
}
/// This struct is intended as a helper to pass args to generic dbus interface
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct AuraPowerDev {
pub tuf: Vec<AuraDevTuf>,
pub x1866: Vec<AuraDevRog1>,
pub x19b6: Vec<AuraDevRog2>,
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum AuraDevTuf {
Boot,
Awake,
Sleep,
Keyboard,
}
impl AuraDevTuf {
pub const fn dev_id() -> &'static str {
"tuf"
}
}
/// # Bits for older 0x1866, 0x1869, 0x1854 keyboard models
///
/// Keybord and Lightbar require Awake, Boot and Sleep apply to both
/// Keybord and Lightbar regardless of if either are enabled (or Awake is
/// enabled)
///
/// | Byte 1 | Byte 2 | Byte 3 | Byte 4 | function | hex
/// |
/// |------------|------------|------------|------------|-----------|-------------|
/// | 0000, 0000 | 0000, 0000 | 0000, 0010 | 0000, 0000 | Awake |
/// 00,00,02,00 | | 0000, 1000 | 0000, 0000 | 0000, 0000 | 0000, 0000 | Keyboard
/// | 08,00,00,00 | | 0000, 0100 | 0000, 0101 | 0000, 0000 | 0000, 0000 |
/// Lightbar | 04,05,00,00 | | 1100, 0011 | 0001, 0010 | 0000, 1001 | 0000,
/// 0000 | Boot/Sht | c3,12,09,00 | | 0011, 0000 | 0000, 1000 | 0000, 0100 |
/// 0000, 0000 | Sleep | 30,08,04,00 | | 1111, 1111 | 0001, 1111 | 0000,
/// 1111 | 0000, 0000 | all on | | | 0000, 0000 | 0000, 0000 |
/// 0000, 0000 | 0000, 0010 | Rear Glow | 00,00,00,02 |
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum AuraDevRog1 {
Awake = 0x000002,
Keyboard = 0x080000,
Lightbar = 0x040500,
Boot = 0xc31209,
Sleep = 0x300804,
}
impl From<AuraDevRog1> for u32 {
fn from(a: AuraDevRog1) -> Self {
a as u32
}
}
impl AuraDevRog1 {
pub fn to_bytes(control: &[Self]) -> [u8; 4] {
let mut a: u32 = 0;
for n in control {
a |= *n as u32;
}
[
((a & 0xff0000) >> 16) as u8,
((a & 0xff00) >> 8) as u8,
(a & 0xff) as u8,
0x00,
]
}
pub const fn dev_id() -> &'static str {
"0x1866"
}
}
impl BitOr<AuraDevRog1> for AuraDevRog1 {
type Output = u32;
fn bitor(self, rhs: AuraDevRog1) -> Self::Output {
self as u32 | rhs as u32
}
}
impl BitAnd<AuraDevRog1> for AuraDevRog1 {
type Output = u32;
fn bitand(self, rhs: AuraDevRog1) -> Self::Output {
self as u32 & rhs as u32
}
}
/// # Bits for newer 0x18c6, 0x19B6, 0x1a30, keyboard models
///
/// byte 4 in the USB packet is for keyboard + logo power states
/// default is on, `ff`
/// Keyboard and logo use the full range of bits (almost)
///
/// | n1 | n2 | hex | action | bit |
/// |------|------|-----|-----------------------|-------|
/// | 0000 | 0000 | 00 | all off | |
/// | 0000 | 0001 | 01 | logo boot | bit 1 |
/// | 0000 | 0010 | 02 | keyboard boot | bit 2 |
/// | 0000 | 0100 | 04 | logo awake | bit 3 |
/// | 0000 | 1000 | 08 | keyboard awake | bit 4 |
/// | 0001 | 0000 | 10 | logo sleep off | bit 5 |
/// | 0010 | 0000 | 20 | keyboard sleep | bit 6 |
/// | 0100 | 0000 | 40 | logo shutdown off | bit 7 |
/// | 1000 | 0000 | 80 | keyboard shutdown off | bit 8 |
///
/// byte 5 = lightbar
///
/// | 1 | 2 | hex | action | bit |
/// |------|------|-----|----------------------|-------|
/// | 0000 | 0010 | 02 | lightbar off boot | bit 2 |
/// | 0000 | 0100 | 04 | lightbar on | bit 3 |
/// | 0000 | 1000 | 08 | lightbar off sleep | bit 4 |
/// | 0001 | 0000 | 10 | lightbar shtdn off | bit 5 |
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum AuraDevRog2 {
BootLogo = 1,
BootKeyb = 1 << 1,
AwakeLogo = 1 << 2,
AwakeKeyb = 1 << 3,
SleepLogo = 1 << 4,
SleepKeyb = 1 << 5,
ShutdownLogo = 1 << 6,
ShutdownKeyb = 1 << 7,
BootBar = 1 << (7 + 2),
AwakeBar = 1 << (7 + 3),
SleepBar = 1 << (7 + 4),
ShutdownBar = 1 << (7 + 5),
BootLid = 1 << (15 + 1),
AwakeLid = 1 << (15 + 2),
SleepLid = 1 << (15 + 3),
ShutdownLid = 1 << (15 + 4),
BootRearGlow = 1 << (23 + 1),
AwakeRearGlow = 1 << (23 + 2),
SleepRearGlow = 1 << (23 + 3),
ShutdownRearGlow = 1 << (23 + 4),
}
impl From<AuraDevRog2> for u32 {
fn from(a: AuraDevRog2) -> Self {
a as u32
}
}
impl AuraDevRog2 {
pub fn to_bytes(control: &[Self]) -> [u8; 4] {
let mut a: u32 = 0;
for n in control {
a |= *n as u32;
}
[
(a & 0xff) as u8,
((a & 0xff00) >> 8) as u8,
((a & 0xff0000) >> 16) as u8,
((a & 0xff000000) >> 24) as u8,
]
}
pub const fn dev_id() -> &'static str {
"0x196b"
}
}
impl BitOr<AuraDevRog2> for AuraDevRog2 {
type Output = u16;
fn bitor(self, rhs: AuraDevRog2) -> Self::Output {
self as u16 | rhs as u16
}
}
impl BitAnd<AuraDevRog2> for AuraDevRog2 {
type Output = u16;
fn bitand(self, rhs: AuraDevRog2) -> Self::Output {
self as u16 & rhs as u16
}
}
#[cfg(test)]
mod tests {
use super::AuraDevRog1;
use crate::usb::AuraDevRog2;
#[test]
fn check_0x1866_control_bytes() {
let bytes = [AuraDevRog1::Keyboard, AuraDevRog1::Awake];
let bytes = AuraDevRog1::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x08, 0x00, 0x02, 0x00]);
let bytes = [AuraDevRog1::Lightbar, AuraDevRog1::Awake];
let bytes = AuraDevRog1::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x04, 0x05, 0x02, 0x00]);
let bytes = [AuraDevRog1::Sleep];
let bytes = AuraDevRog1::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0x30, 0x08, 0x04, 0x00]);
let bytes = [AuraDevRog1::Boot];
let bytes = AuraDevRog1::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xc3, 0x12, 0x09, 0x00]);
let bytes = [
AuraDevRog1::Keyboard,
AuraDevRog1::Lightbar,
AuraDevRog1::Awake,
AuraDevRog1::Sleep,
AuraDevRog1::Boot,
];
let bytes = AuraDevRog1::to_bytes(&bytes);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes, [0xff, 0x1f, 0x000f, 0x00]);
}
#[test]
fn check_0x19b6_control_bytes() {
// All on
let byte1 = [
AuraDevRog2::BootLogo,
AuraDevRog2::BootKeyb,
AuraDevRog2::SleepLogo,
AuraDevRog2::SleepKeyb,
AuraDevRog2::AwakeLogo,
AuraDevRog2::AwakeKeyb,
AuraDevRog2::ShutdownLogo,
AuraDevRog2::ShutdownKeyb,
];
let bytes = AuraDevRog2::to_bytes(&byte1);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes[0], 0xff);
//
let byte1 = [
// AuraControl::BootLogo,
AuraDevRog2::BootKeyb,
AuraDevRog2::SleepLogo,
AuraDevRog2::SleepKeyb,
AuraDevRog2::AwakeLogo,
AuraDevRog2::AwakeKeyb,
AuraDevRog2::ShutdownLogo,
AuraDevRog2::ShutdownKeyb,
];
let bytes = AuraDevRog2::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xfe);
let byte1 = [
AuraDevRog2::BootLogo,
// AuraControl::BootKeyb,
AuraDevRog2::SleepLogo,
AuraDevRog2::SleepKeyb,
AuraDevRog2::AwakeLogo,
AuraDevRog2::AwakeKeyb,
AuraDevRog2::ShutdownLogo,
AuraDevRog2::ShutdownKeyb,
];
let bytes = AuraDevRog2::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xfd);
let byte1 = [
AuraDevRog2::BootLogo,
AuraDevRog2::BootKeyb,
// AuraControl::SleepLogo,
AuraDevRog2::SleepKeyb,
AuraDevRog2::AwakeLogo,
AuraDevRog2::AwakeKeyb,
AuraDevRog2::ShutdownLogo,
AuraDevRog2::ShutdownKeyb,
];
let bytes = AuraDevRog2::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xef);
let byte1 = [
AuraDevRog2::BootLogo,
AuraDevRog2::BootKeyb,
AuraDevRog2::SleepLogo,
// AuraControl::SleepKeyb,
AuraDevRog2::AwakeLogo,
AuraDevRog2::AwakeKeyb,
AuraDevRog2::ShutdownLogo,
AuraDevRog2::ShutdownKeyb,
];
let bytes = AuraDevRog2::to_bytes(&byte1);
println!("{:08b}", bytes[0]);
assert_eq!(bytes[0], 0xdf);
let byte2 = [
AuraDevRog2::BootBar,
AuraDevRog2::AwakeBar,
AuraDevRog2::SleepBar,
AuraDevRog2::ShutdownBar,
];
let bytes = AuraDevRog2::to_bytes(&byte2);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes[1], 0x1e);
let byte2 = [
AuraDevRog2::BootBar,
AuraDevRog2::AwakeBar,
// AuraControl::SleepBar,
AuraDevRog2::ShutdownBar,
];
let bytes = AuraDevRog2::to_bytes(&byte2);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes[1], 0x16);
let byte3 = [
AuraDevRog2::AwakeLid,
AuraDevRog2::BootLid,
AuraDevRog2::SleepLid,
AuraDevRog2::ShutdownLid,
];
let bytes = AuraDevRog2::to_bytes(&byte3);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes[2], 0x0f);
let byte3 = [
// AuraDev19b6::AwakeLid,
AuraDevRog2::BootLid,
AuraDevRog2::SleepLid,
AuraDevRog2::ShutdownLid,
];
let bytes = AuraDevRog2::to_bytes(&byte3);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes[2], 0x0d);
let byte3 = [
AuraDevRog2::AwakeLid,
AuraDevRog2::BootLid,
// AuraControl::SleepLid,
AuraDevRog2::ShutdownLid,
];
let bytes = AuraDevRog2::to_bytes(&byte3);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes[2], 0x0b);
let byte3 = [
AuraDevRog2::AwakeLid,
AuraDevRog2::BootLid,
AuraDevRog2::SleepLid,
// AuraDev19b6::ShutdownLid,
];
let bytes = AuraDevRog2::to_bytes(&byte3);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes[2], 0x07);
let byte3 = [
AuraDevRog2::AwakeLid,
// AuraDev19b6::BootLid,
AuraDevRog2::SleepLid,
// AuraDev19b6::ShutdownLid,
];
let bytes = AuraDevRog2::to_bytes(&byte3);
println!("{:08b}, {:08b}, {:08b}", bytes[0], bytes[1], bytes[2]);
assert_eq!(bytes[2], 0x06);
let byte4 = [
// AuraDev19b6::AwakeRearBar,
AuraDevRog2::BootRearGlow,
AuraDevRog2::SleepRearGlow,
AuraDevRog2::ShutdownRearGlow,
];
let bytes = AuraDevRog2::to_bytes(&byte4);
println!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
);
assert_eq!(bytes[3], 0x0d);
let byte4 = [
AuraDevRog2::AwakeRearGlow,
AuraDevRog2::BootRearGlow,
// AuraDevRog2::SleepRearBar,
AuraDevRog2::ShutdownRearGlow,
];
let bytes = AuraDevRog2::to_bytes(&byte4);
println!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
);
assert_eq!(bytes[3], 0x0b);
let byte4 = [
AuraDevRog2::AwakeRearGlow,
AuraDevRog2::BootRearGlow,
AuraDevRog2::SleepRearGlow,
// AuraDevRog2::ShutdownRearBar,
];
let bytes = AuraDevRog2::to_bytes(&byte4);
println!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
);
assert_eq!(bytes[3], 0x07);
let byte4 = [
AuraDevRog2::AwakeRearGlow,
// AuraDevRog2::BootRearBar,
AuraDevRog2::SleepRearGlow,
// AuraDevRog2::ShutdownRearBar,
];
let bytes = AuraDevRog2::to_bytes(&byte4);
println!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
);
assert_eq!(bytes[3], 0x06);
let byte4 = [
AuraDevRog2::AwakeRearGlow,
// AuraDevRog2::BootRearBar,
// AuraDevRog2::SleepRearBar,
// AuraDevRog2::ShutdownRearBar,
];
let bytes = AuraDevRog2::to_bytes(&byte4);
println!(
"{:08b}, {:08b}, {:08b}, {:08b}",
bytes[0], bytes[1], bytes[2], bytes[3]
);
assert_eq!(bytes[3], 0x02);
}
}
+28 -22
View File
@@ -1,30 +1,31 @@
[package]
name = "rog-control-center"
license = "MPL-2.0"
license.workspace = true
version.workspace = true
authors = ["Luke D. Jones <luke@ljones.dev>"]
edition = "2021"
readme.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
edition.workspace = true
[features]
#default = ["mocking"]
mocking = []
#mocking = []
[dependencies]
egui = { git = "https://github.com/emilk/egui", rev = "b8e798777de519de3a1878798097ab2ab0bd4def"}
eframe = { git = "https://github.com/emilk/egui", rev = "b8e798777de519de3a1878798097ab2ab0bd4def"}
# egui = { path = "../../egui/crates/egui" }
# eframe = { path = "../../egui/crates/eframe" }
libappindicator = "0.8" # Tray icon
gtk = "0.16"
nix = { version = "^0.28.0", features = ["fs"] }
tempfile = "3.3.0"
betrayer = { version = "0.2.0" }
asusd = { path = "../asusd" }
config-traits = { path = "../config-traits" }
rog_anime = { path = "../rog-anime" }
rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", rev = "4eb6e97c22b68ae8d1e80500709b0c0580776ad3", default-features = false }
dmi_id = { path = "../dmi-id" }
gumdrop.workspace = true
log.workspace = true
@@ -32,23 +33,28 @@ env_logger.workspace = true
tokio.workspace = true
serde.workspace = true
toml.workspace = true
serde_json.workspace = true
serde_derive.workspace = true
zbus.workspace = true
dirs.workspace = true
notify-rust.workspace = true
png_pong.workspace = true
concat-idents.workspace = true
versions.workspace = true
nix = "^0.26.1"
tempfile = "3.3.0"
[dependencies.slint]
git = "https://github.com/slint-ui/slint.git"
default-features = false
features = [
"gettext",
"compat-1-2",
"backend-linuxkms",
"backend-winit-wayland",
"renderer-winit-femtovg",
# "renderer-skia-opengl",
]
# [patch.crates-io]
# egui = { git = "https://github.com/flukejones/egui" }
# eframe = { git = "https://github.com/flukejones/egui" }
[build-dependencies.slint-build]
git = "https://github.com/slint-ui/slint.git"
[dev-dependencies]
cargo-husky.workspace = true
cargo-husky.workspace = true
+6 -17
View File
@@ -1,22 +1,11 @@
# App template
# ROGALOG
This is a trial app cut down to bare essentials to show how to create an app that can run in the background
with some user-config options.
### Translations
egui based. Keep in mind that this is very much a bit of a mess due to experimenting.
You can help with translations by following https://slint.dev/releases/1.1.0/docs/slint/src/concepts/translations#translate-the-strings
## Running
Begin by copying `rog-control-center/translations/en/rog-control-center.po` to `rog-control-center/translations/<YOUR LOCALE>/rog-control-center.po`, then edit that file.
Use `WINIT_UNIX_BACKEND=x11 rog-control-center`. `WINIT_UNIX_BACKEND` is required due to window decorations not updating and the window not really being set as visible/invisible on wayland.
Run `msgfmt rog-control-center/translations/<YOUR LOCALE>/rog-control-center.po -o rog-control-center/translations/<YOUR LOCALE>/LC_MESSAGES/rog-control-center.mo` to make the binary formatted translation where `<YOUR LOCALE>` is changed to your translation locale.
## Build features
For testing some features that are typically not available on all laptops:
```rust
cargo run --features mocking
```
## TODO
- Add notification watch for certain UI elements to enforce an update (for example when a user changes Aura via a hot key).
To test you local translations run `RUST_TRANSLATIONS=1 rog-control-center`.
+26
View File
@@ -0,0 +1,26 @@
use std::path::PathBuf;
use std::str::FromStr;
use slint_build::CompilerConfiguration;
fn main() {
// write_locales();
let root = env!("CARGO_MANIFEST_DIR");
let mut main = PathBuf::from_str(root).unwrap();
main.push("ui/main_window.slint");
let mut include = PathBuf::from_str(root).unwrap();
include.push("ui");
slint_build::print_rustc_flags().unwrap();
// slint_build::compile("ui/main_window.slint").unwrap();
slint_build::compile_with_config(
main,
CompilerConfiguration::new()
// .embed_resources(EmbedResourcesKind::EmbedFiles)
.with_include_paths(vec![include])
.with_style("fluent-dark".into()),
)
.unwrap();
}
-179
View File
@@ -1,179 +0,0 @@
use std::f64::consts::PI;
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use egui::{Button, RichText};
use rog_aura::layouts::KeyLayout;
use rog_platform::supported::SupportedFunctions;
use crate::config::Config;
use crate::error::Result;
use crate::system_state::SystemState;
use crate::{Page, RogDbusClientBlocking};
pub struct RogApp {
pub page: Page,
pub states: Arc<Mutex<SystemState>>,
pub supported: SupportedFunctions,
// TODO: can probably just open and read whenever
pub config: Config,
/// Oscillator in percentage
pub oscillator1: Arc<AtomicU8>,
pub oscillator2: Arc<AtomicU8>,
pub oscillator3: Arc<AtomicU8>,
/// Frequency of oscillation
pub oscillator_freq: Arc<AtomicU8>,
/// A toggle that toggles true/false when the oscillator reaches 0
pub oscillator_toggle: Arc<AtomicBool>,
}
impl RogApp {
/// Called once before the first frame.
pub fn new(
config: Config,
states: Arc<Mutex<SystemState>>,
_cc: &eframe::CreationContext<'_>,
) -> Result<Self> {
let (dbus, _) = RogDbusClientBlocking::new()?;
let supported = dbus.proxies().supported().supported_functions()?;
// Set up an oscillator to run on a thread.
// Helpful for visual effects like colour pulse.
let oscillator1 = Arc::new(AtomicU8::new(0));
let oscillator2 = Arc::new(AtomicU8::new(0));
let oscillator3 = Arc::new(AtomicU8::new(0));
let oscillator1_1 = oscillator1.clone();
let oscillator1_2 = oscillator2.clone();
let oscillator1_3 = oscillator3.clone();
let oscillator_freq = Arc::new(AtomicU8::new(5));
let oscillator_freq1 = oscillator_freq.clone();
let oscillator_toggle = Arc::new(AtomicBool::new(false));
let oscillator_toggle1 = oscillator_toggle.clone();
std::thread::spawn(move || {
let started = Instant::now();
let mut toggled = false;
loop {
let time = started.elapsed();
// 32 = slow, 16 = med, 8 = fast
let scale = oscillator_freq1.load(Ordering::SeqCst) as f64;
let elapsed1 = (time.as_millis() as f64 + 333.0) / 10000.0;
let elapsed2 = (time.as_millis() as f64 + 666.0) / 10000.0;
let elapsed3 = (time.as_millis() as f64 + 999.0) / 10000.0;
let tmp1 = ((scale * elapsed1 * PI).cos()).abs();
let tmp2 = ((scale * 0.85 * elapsed2 * PI).cos()).abs();
let tmp3 = ((scale * 0.7 * elapsed3 * PI).cos()).abs();
if tmp1 <= 0.1 && !toggled {
let s = oscillator_toggle1.load(Ordering::SeqCst);
oscillator_toggle1.store(!s, Ordering::SeqCst);
toggled = true;
} else if tmp1 > 0.9 {
toggled = false;
}
let tmp1 = (255.0 * tmp1 * 100.0 / 255.0) as u8;
let tmp2 = (255.0 * tmp2 * 100.0 / 255.0) as u8;
let tmp3 = (255.0 * tmp3 * 100.0 / 255.0) as u8;
oscillator1_1.store(tmp1, Ordering::SeqCst);
oscillator1_2.store(tmp2, Ordering::SeqCst);
oscillator1_3.store(tmp3, Ordering::SeqCst);
std::thread::sleep(Duration::from_millis(33));
}
});
Ok(Self {
supported,
states,
page: Page::System,
config,
oscillator1,
oscillator2,
oscillator3,
oscillator_toggle,
oscillator_freq,
})
}
}
impl eframe::App for RogApp {
/// Called each time the UI needs repainting, which may be many times per
/// second. Put your widgets into a `SidePanel`, `TopPanel`,
/// `CentralPanel`, `Window` or `Area`.
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
let states = self.states.clone();
if let Ok(mut states) = states.try_lock() {
if states.app_should_update {
states.app_should_update = false;
ctx.request_repaint();
}
}
// Shortcut typical display stuff
if let Ok(mut states) = states.try_lock() {
let layout_testing = states.aura_creation.layout_testing.clone();
if let Some(path) = &layout_testing {
let modified = path.metadata().unwrap().modified().unwrap();
if states.aura_creation.layout_last_modified < modified {
states.aura_creation.layout_last_modified = modified;
// time to reload the config
states.aura_creation.keyboard_layout = KeyLayout::from_file(path).unwrap();
}
self.aura_page(&mut states, ctx);
return;
}
}
let page = self.page;
self.top_bar(ctx, frame);
self.side_panel(ctx);
let mut was_error = false;
if let Ok(mut states) = states.try_lock() {
if let Some(err) = states.error.clone() {
was_error = true;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading(RichText::new("Error!").size(28.0));
ui.centered_and_justified(|ui| {
ui.label(RichText::new(format!("The error was: {:?}", err)).size(22.0));
});
});
egui::TopBottomPanel::bottom("error_bar")
.default_height(26.0)
.show(ctx, |ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
states.error = None;
}
});
});
}
}
if !was_error {
if let Ok(mut states) = states.try_lock() {
if page == Page::System {
self.system_page(&mut states, ctx);
} else if page == Page::AuraEffects {
self.aura_page(&mut states, ctx);
// TODO: Anime page is not complete
// } else if page == Page::AnimeMatrix {
// self.anime_page(ctx);
} else if page == Page::FanCurves {
self.fan_curve_page(&mut states, ctx);
}
}
}
}
}
+8
View File
@@ -4,6 +4,14 @@ use gumdrop::Options;
pub struct CliStart {
#[options(help_flag, help = "print help message")]
pub help: bool,
#[options(help = "start fullscreen, if used the option is saved")]
pub fullscreen: bool,
#[options(help = "fullscreen width")]
pub width_fullscreen: u32,
#[options(help = "fullscreen height")]
pub height_fullscreen: u32,
#[options(help = "start windowed, if used the option is saved")]
pub windowed: bool,
#[options(help = "show program version number")]
pub version: bool,
#[options(

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