From 7d0f15d73879bedb8ec9dcac95f274d3497db91f Mon Sep 17 00:00:00 2001 From: Luke D Jones Date: Wed, 7 Apr 2021 22:20:29 +1200 Subject: [PATCH] anime: CLI and user-daemon work --- CHANGELOG.md | 3 + Cargo.lock | 117 +++++++++----- Cargo.toml | 2 +- Makefile | 5 + asusctl/examples/animatrix-diag-png.rs | 7 +- asusctl/examples/animatrix-diag.rs | 2 +- asusctl/examples/animatrix-gif.rs | 23 +-- asusctl/examples/animatrix-grid.rs | 2 +- asusctl/examples/animatrix-png-gif.rs | 78 +++++++-- asusctl/examples/controller.png | Bin 9033 -> 0 bytes asusctl/examples/diag_sq.png | Bin 9033 -> 0 bytes asusctl/examples/levelup.png | Bin 9613 -> 0 bytes asusctl/examples/sunset.gif | Bin 40406 -> 0 bytes daemon-user/Cargo.toml | 25 +++ daemon-user/src/error.rs | 37 +++++ daemon-user/src/lib.rs | 3 + daemon-user/src/main.rs | 60 +++++++ daemon-user/src/user_config.rs | 151 ++++++++++++++++++ daemon/src/ctrl_anime.rs | 7 +- daemon/src/ctrl_charge.rs | 7 +- daemon/src/ctrl_fan_cpu.rs | 7 +- daemon/src/ctrl_gfx/gfx.rs | 2 +- daemon/src/ctrl_leds.rs | 2 +- daemon/src/ctrl_rog_bios.rs | 7 +- daemon/src/ctrl_supported.rs | 5 +- daemon/src/daemon.rs | 11 +- .../anime/{Festive => asus/festive}/Cupid.gif | Bin .../{Festive => asus/festive}/Firework.gif | Bin .../{Festive => asus/festive}/Halloween.gif | Bin .../festive}/Happy Holiday.gif | Bin .../festive}/Happy new year.gif | Bin .../{Festive => asus/festive}/Lantern.gif | Bin .../festive}/Valentine's Day.gif | Bin .../festive}/Year of the Ox.gif | Bin data/anime/{Gaming => asus/gaming}/Bird.gif | Bin .../{Gaming => asus/gaming}/Controller.gif | Bin data/anime/{Gaming => asus/gaming}/FPS.gif | Bin data/anime/{Gaming => asus/gaming}/Fight.gif | Bin .../{Gaming => asus/gaming}/Keyboard.gif | Bin data/anime/{Gaming => asus/gaming}/MOBA.gif | Bin data/anime/{Gaming => asus/gaming}/UFO.gif | Bin data/anime/{Music => asus/music}/DJ.gif | Bin .../{Music => asus/music}/Music-player.gif | Bin .../rog}/For-those-who-dare.gif | Bin .../rog}/For-those-who-dare_2.gif | Bin .../{ROG Gallery => asus/rog}/Fragment.gif | Bin .../rog}/Infinite-triangle.gif | Bin .../rog}/Kaleidoscope1.gif | Bin .../rog}/Kaleidoscope2.gif | Bin .../rog}/Kaleidoscope2.png | Bin .../{ROG Gallery => asus/rog}/Sunset.gif | Bin data/anime/{Trend => asus/trend}/Dog.gif | Bin data/anime/{Trend => asus/trend}/Ski.gif | Bin data/anime/{Trend => asus/trend}/Wave.gif | Bin data/anime/custom/rust.png | Bin 0 -> 29814 bytes data/asusd-user.service | 14 ++ data/user-example.json | 45 ++++++ rog-anime/Cargo.toml | 4 +- rog-anime/src/{anime_data.rs => data.rs} | 0 .../src/{anime_diagonal.rs => diagonal.rs} | 21 ++- rog-anime/src/{anime_gif.rs => gif.rs} | 31 +++- rog-anime/src/{anime_grid.rs => grid.rs} | 18 ++- rog-anime/src/{anime_image.rs => image.rs} | 15 +- rog-anime/src/lib.rs | 90 ++--------- rog-anime/src/sequencer.rs | 108 +++++++++++++ 65 files changed, 721 insertions(+), 188 deletions(-) delete mode 100644 asusctl/examples/controller.png delete mode 100644 asusctl/examples/diag_sq.png delete mode 100644 asusctl/examples/levelup.png delete mode 100644 asusctl/examples/sunset.gif create mode 100644 daemon-user/Cargo.toml create mode 100644 daemon-user/src/error.rs create mode 100644 daemon-user/src/lib.rs create mode 100644 daemon-user/src/main.rs create mode 100644 daemon-user/src/user_config.rs rename data/anime/{Festive => asus/festive}/Cupid.gif (100%) rename data/anime/{Festive => asus/festive}/Firework.gif (100%) rename data/anime/{Festive => asus/festive}/Halloween.gif (100%) rename data/anime/{Festive => asus/festive}/Happy Holiday.gif (100%) rename data/anime/{Festive => asus/festive}/Happy new year.gif (100%) rename data/anime/{Festive => asus/festive}/Lantern.gif (100%) rename data/anime/{Festive => asus/festive}/Valentine's Day.gif (100%) rename data/anime/{Festive => asus/festive}/Year of the Ox.gif (100%) rename data/anime/{Gaming => asus/gaming}/Bird.gif (100%) rename data/anime/{Gaming => asus/gaming}/Controller.gif (100%) rename data/anime/{Gaming => asus/gaming}/FPS.gif (100%) rename data/anime/{Gaming => asus/gaming}/Fight.gif (100%) rename data/anime/{Gaming => asus/gaming}/Keyboard.gif (100%) rename data/anime/{Gaming => asus/gaming}/MOBA.gif (100%) rename data/anime/{Gaming => asus/gaming}/UFO.gif (100%) rename data/anime/{Music => asus/music}/DJ.gif (100%) rename data/anime/{Music => asus/music}/Music-player.gif (100%) rename data/anime/{ROG Gallery => asus/rog}/For-those-who-dare.gif (100%) rename data/anime/{ROG Gallery => asus/rog}/For-those-who-dare_2.gif (100%) rename data/anime/{ROG Gallery => asus/rog}/Fragment.gif (100%) rename data/anime/{ROG Gallery => asus/rog}/Infinite-triangle.gif (100%) rename data/anime/{ROG Gallery => asus/rog}/Kaleidoscope1.gif (100%) rename data/anime/{ROG Gallery => asus/rog}/Kaleidoscope2.gif (100%) rename data/anime/{ROG Gallery => asus/rog}/Kaleidoscope2.png (100%) rename data/anime/{ROG Gallery => asus/rog}/Sunset.gif (100%) rename data/anime/{Trend => asus/trend}/Dog.gif (100%) rename data/anime/{Trend => asus/trend}/Ski.gif (100%) rename data/anime/{Trend => asus/trend}/Wave.gif (100%) create mode 100644 data/anime/custom/rust.png create mode 100644 data/asusd-user.service create mode 100644 data/user-example.json rename rog-anime/src/{anime_data.rs => data.rs} (100%) rename rog-anime/src/{anime_diagonal.rs => diagonal.rs} (90%) rename rog-anime/src/{anime_gif.rs => gif.rs} (84%) rename rog-anime/src/{anime_grid.rs => grid.rs} (95%) rename rog-anime/src/{anime_image.rs => image.rs} (99%) create mode 100644 rog-anime/src/sequencer.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ac1b52bd..2d5cb5b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Display PNG images on AniMe with scale, position, angle, and brightness - AniMe display parts split out to individual crate in preparation for publishing on crates.io +- Revert zbus to 1.9.1 +- Use enum to show power states, and catch missing pci path for nvidia. +- Partial user-daemon for anime/per-key done, asusd-user. Includes asusd-user systemd unit. # [3.3.0] - 2021-04-3 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 9a926b9e..afb346f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -226,6 +226,18 @@ dependencies = [ "zvariant", ] +[[package]] +name = "daemon-user" +version = "1.0.0" +dependencies = [ + "dirs 3.0.1", + "rog_anime", + "rog_dbus", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "derivative" version = "2.2.0" @@ -234,7 +246,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] @@ -248,6 +260,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "enumflags2" version = "0.6.4" @@ -266,7 +298,7 @@ checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] @@ -292,7 +324,7 @@ dependencies = [ "proc-macro2", "quote 1.0.9", "rustversion", - "syn 1.0.64", + "syn 1.0.68", "synstructure", ] @@ -377,7 +409,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] @@ -420,7 +452,7 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -439,6 +471,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70155b56080764b8b758e91e4c63d06da0262c0c939f2cd991cd1382087147df" dependencies = [ + "serde", "spirv-std", ] @@ -459,7 +492,7 @@ checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] @@ -510,9 +543,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "libm" @@ -573,7 +606,7 @@ checksum = "3dfb6b71a9a89cd38b395d994214297447e8e63b1ba5708a9a2b0b1048ceda76" dependencies = [ "cc", "chrono", - "dirs", + "dirs 1.0.5", "objc-foundation", ] @@ -604,9 +637,9 @@ dependencies = [ [[package]] name = "nb-connect" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670361df1bc2399ee1ff50406a0d422587dd3bb0da596e1978fe8e05dabddf4f" +checksum = "a19900e7eee95eb2b3c2e26d12a874cc80aaf750e31be6fcbe743ead369fa45d" dependencies = [ "libc", "socket2", @@ -752,11 +785,11 @@ dependencies = [ [[package]] name = "polling" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" +checksum = "4fc12d774e799ee9ebae13f4076ca003b40d18a11ac0f3641e6f899618580b7b" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", "log", "wepoll-sys", @@ -781,7 +814,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", "version_check", ] @@ -810,9 +843,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid 0.2.1", ] @@ -956,22 +989,22 @@ checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] name = "serde" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] @@ -993,7 +1026,7 @@ checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] @@ -1010,16 +1043,15 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] name = "socket2" -version = "0.3.19" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ - "cfg-if 1.0.0", "libc", "winapi", ] @@ -1042,7 +1074,7 @@ checksum = "f4972082b5236fd57a46cc47fbc315ad78b5ad07b33e51077c688a2fe28d6f2d" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] @@ -1074,9 +1106,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.64" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" dependencies = [ "proc-macro2", "quote 1.0.9", @@ -1100,7 +1132,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", "unicode-xid 0.2.1", ] @@ -1124,11 +1156,12 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -1152,9 +1185,9 @@ dependencies = [ [[package]] name = "udev" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307c2b8c8a320a38365def5bb3ee92d146d405655196230f7a445fe4da6749f6" +checksum = "3193363f52bb34c6708ac2ffedcb5f7e5874f0329ef68e1315f27d8d768eb568" dependencies = [ "libc", "libudev-sys", @@ -1181,9 +1214,9 @@ checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" [[package]] name = "vec-arena" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" +checksum = "34b2f665b594b07095e3ac3f718e13c2197143416fae4c5706cffb7b1af8d7f1" [[package]] name = "version_check" @@ -1209,6 +1242,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "weezl" version = "0.1.4" @@ -1327,7 +1366,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] [[package]] @@ -1351,5 +1390,5 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote 1.0.9", - "syn 1.0.64", + "syn 1.0.68", ] diff --git a/Cargo.toml b/Cargo.toml index 3c6d9912..1daec777 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["asusctl", "asus-notify", "daemon", "rog-types", "rog-dbus", "rog-anime"] +members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-types", "rog-dbus", "rog-anime"] [profile.release] lto = true diff --git a/Makefile b/Makefile index d84544ec..310242a4 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ zshcpl = $(datarootdir)/zsh/site-functions BIN_C := asusctl BIN_D := asusd +BIN_U := asusd-user BIN_N := asus-notify LEDCFG := asusd-ledmodes.toml X11CFG := 90-nvidia-screen-G05.conf @@ -42,6 +43,7 @@ distclean: install: $(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)" $(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)" + $(INSTALL_PROGRAM) "./target/release/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)" $(INSTALL_PROGRAM) "./target/release/$(BIN_N)" "$(DESTDIR)$(bindir)/$(BIN_N)" $(INSTALL_DATA) "./data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)" $(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules" @@ -50,11 +52,13 @@ install: $(INSTALL_DATA) "./data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)" $(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service" $(INSTALL_DATA) "./data/$(BIN_N).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service" + $(INSTALL_DATA) "./data/$(BIN_U).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_U).service" $(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png" $(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png" $(INSTALL_DATA) "./data/icons/asus_notif_red.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png" $(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl" $(INSTALL_DATA) "./data/completions/asusctl.fish" "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish" + cd data && find "./anime" -type f -exec install -Dm 755 "{}" "$(DESTDIR)$(datarootdir)/asusd/{}" \; uninstall: rm -f "$(DESTDIR)$(bindir)/$(BIN_C)" @@ -72,6 +76,7 @@ uninstall: rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png" rm -f "$(DESTDIR)$(zshcpl)/_asusctl" rm -f "$(DESTDIR)$(datarootdir)/fish/vendor_completions.d/asusctl.fish" + rm -rf "$(DESTDIR)$(datarootdir)/asusd" update: cargo update diff --git a/asusctl/examples/animatrix-diag-png.rs b/asusctl/examples/animatrix-diag-png.rs index 5d8d8fa3..bbfa7f7b 100644 --- a/asusctl/examples/animatrix-diag-png.rs +++ b/asusctl/examples/animatrix-diag-png.rs @@ -1,8 +1,6 @@ use std::{env, error::Error, path::Path, process::exit}; -use rog_anime::{ - AniMeDataBuffer, {AniMeDiagonal, Vec2}, -}; +use rog_anime::{AniMeDataBuffer, AniMeDiagonal}; use rog_dbus::AuraDbusClient; fn main() -> Result<(), Box> { @@ -15,7 +13,8 @@ fn main() -> Result<(), Box> { exit(-1); } - let matrix = AniMeDiagonal::from_png(Path::new(&args[1]), args[2].parse::().unwrap())?; + let matrix = + AniMeDiagonal::from_png(Path::new(&args[1]), None, args[2].parse::().unwrap())?; client .proxies() diff --git a/asusctl/examples/animatrix-diag.rs b/asusctl/examples/animatrix-diag.rs index fc61282a..e823ff81 100644 --- a/asusctl/examples/animatrix-diag.rs +++ b/asusctl/examples/animatrix-diag.rs @@ -12,7 +12,7 @@ fn main() { let (client, _) = AuraDbusClient::new().unwrap(); for step in (2..50).rev() { - let mut matrix = AniMeDiagonal::new(); + let mut matrix = AniMeDiagonal::new(None); for c in (0..60).into_iter().step_by(step) { for i in matrix.get_mut().iter_mut() { i[c] = 50; diff --git a/asusctl/examples/animatrix-gif.rs b/asusctl/examples/animatrix-gif.rs index 1cc387a5..3bd1f63e 100644 --- a/asusctl/examples/animatrix-gif.rs +++ b/asusctl/examples/animatrix-gif.rs @@ -1,6 +1,6 @@ use std::{env, path::Path, thread::sleep}; -use rog_anime::AniMeBlock; +use rog_anime::{Action, Sequences}; use rog_dbus::AuraDbusClient; fn main() { @@ -14,16 +14,21 @@ fn main() { let path = Path::new(&args[1]); let brightness = args[2].parse::().unwrap(); - let gif = AniMeBlock::asus_gif(path, brightness).unwrap(); + let mut seq = Sequences::new(); + seq.add_asus_gif(path, None, brightness).unwrap(); loop { - for frame in gif.get_animation().unwrap().frames() { - client - .proxies() - .anime() - .write(frame.frame().clone()) - .unwrap(); - sleep(frame.delay()); + for action in seq.iter() { + if let Action::Animation(frames) = action { + for frame in frames.frames() { + client + .proxies() + .anime() + .write(frame.frame().clone()) + .unwrap(); + sleep(frame.delay()); + } + } } } } diff --git a/asusctl/examples/animatrix-grid.rs b/asusctl/examples/animatrix-grid.rs index 8678f1cd..8769fc06 100644 --- a/asusctl/examples/animatrix-grid.rs +++ b/asusctl/examples/animatrix-grid.rs @@ -8,7 +8,7 @@ use rog_dbus::AuraDbusClient; fn main() { let (client, _) = AuraDbusClient::new().unwrap(); - let mut matrix = AniMeGrid::new(); + let mut matrix = AniMeGrid::new(None); let tmp = matrix.get_mut(); let mut i = 0; diff --git a/asusctl/examples/animatrix-png-gif.rs b/asusctl/examples/animatrix-png-gif.rs index 5d1a8ef9..b2af2eaa 100644 --- a/asusctl/examples/animatrix-png-gif.rs +++ b/asusctl/examples/animatrix-png-gif.rs @@ -1,20 +1,28 @@ -use std::{env, path::Path, thread::sleep}; +use std::{ + env, + path::Path, + thread::sleep, + time::{Duration, Instant}, +}; use glam::Vec2; -use rog_anime::AniMeBlock; +use rog_anime::{Action, Sequences}; use rog_dbus::AuraDbusClient; fn main() { let (client, _) = AuraDbusClient::new().unwrap(); let args: Vec = env::args().into_iter().collect(); - if args.len() != 7 { - println!("Usage: "); - println!("e.g, asusctl/examples/file.gif 0.9 0.4 0.0 0.0 0.8"); + if args.len() < 7 { + println!( + "Usage: " + ); + println!("e.g, asusctl/examples/file.gif 0.9 0.4 0.0 0.0 0.8 0"); return; } - let gif = AniMeBlock::image_gif( + let mut seq = Sequences::new(); + seq.add_image_gif( Path::new(&args[1]), args[2].parse::().unwrap(), args[3].parse::().unwrap(), @@ -22,18 +30,62 @@ fn main() { args[4].parse::().unwrap(), args[5].parse::().unwrap(), ), + if let Ok(time) = args[7].parse::() { + if time != 0 { + Some(Duration::from_secs(time)) + } else { + None + } + } else { + None + }, args[6].parse::().unwrap(), ) .unwrap(); + if args.len() == 9 { + seq.add_image_gif( + Path::new(&args[8]), + args[2].parse::().unwrap(), + args[3].parse::().unwrap(), + Vec2::new( + args[4].parse::().unwrap(), + args[5].parse::().unwrap(), + ), + if let Ok(time) = args[7].parse::() { + if time != 0 { + Some(Duration::from_secs(time)) + } else { + None + } + } else { + None + }, + args[6].parse::().unwrap(), + ) + .unwrap(); + } + loop { - for frame in gif.get_animation().unwrap().frames() { - client - .proxies() - .anime() - .write(frame.frame().clone()) - .unwrap(); - sleep(frame.delay()); + for action in seq.iter() { + if let Action::Animation(frames) = action { + let start = Instant::now(); + 'outer: loop { + for frame in frames.frames() { + client + .proxies() + .anime() + .write(frame.frame().clone()) + .unwrap(); + if let Some(time) = frames.duration() { + if Instant::now().duration_since(start) > time { + break 'outer; + } + } + sleep(frame.delay()); + } + } + } } } } diff --git a/asusctl/examples/controller.png b/asusctl/examples/controller.png deleted file mode 100644 index 36e29a15a62e32c38fed5f8a68e3a3890e458dec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9033 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 diff --git a/asusctl/examples/diag_sq.png b/asusctl/examples/diag_sq.png deleted file mode 100644 index 36e29a15a62e32c38fed5f8a68e3a3890e458dec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9033 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 diff --git a/asusctl/examples/levelup.png b/asusctl/examples/levelup.png deleted file mode 100644 index b7a8d333db4910a7b388eb07335e43c476fe4706..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9613 zcmeHrXH=70yKSga1f(gTXef%(5<*SrgkA*%1cDHf&_hBJNB~6<6p$*SfYK49NK>kS zQlv={RHTS>q!$rI1n~y++uQHneZO<|80Y-k86#z_HJ|yccdq%YH}8r#YpTb_BESLw z0N4!lwJoUMhSawx6Fv1aR1Dn<0C24YoI6jlK$3xY0?x(V3j-wi<1s)C#oYw}pbXF4 zwY|er&v|XEf(4|RY1`RHi6M{AY}eZM_kIt-HU!DCku2f4JU&zG;v-vaV

1pIJnX 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 diff --git a/asusctl/examples/sunset.gif b/asusctl/examples/sunset.gif deleted file mode 100644 index f114859c249e13b8dd6a6766086b75526b7dd897..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40406 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%0D-AtiAj^m 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 ipWz0M;lD7Dn1M~fL2e~B`1KgbfBcU{|9}6r#Qy"] +edition = "2018" +description = "Usermode daemon for user settings, anime, per-key lighting" + +[lib] +name = "rog_user" +path = "src/lib.rs" + +[[bin]] +name = "asusd-user" +path = "src/main.rs" + +[dependencies] +# serialisation +serde = "^1.0" +serde_json = "^1.0" +serde_derive = "^1.0" + +rog_anime = { path = "../rog-anime" } +rog_dbus = { path = "../rog-dbus" } + +dirs = "3.0.1" \ No newline at end of file diff --git a/daemon-user/src/error.rs b/daemon-user/src/error.rs new file mode 100644 index 00000000..86496894 --- /dev/null +++ b/daemon-user/src/error.rs @@ -0,0 +1,37 @@ +use std::fmt; + +use rog_anime::error::AnimeError; + +#[derive(Debug)] +pub enum Error { + Io(std::io::Error), + ConfigLoadFail, + XdgVars, + Anime(AnimeError), +} + +impl fmt::Display for Error { + // This trait requires `fmt` with this exact signature. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Io(err) => write!(f, "Failed to open: {}", err), + Error::ConfigLoadFail => write!(f, "Failed to load user config"), + Error::XdgVars => write!(f, "XDG environment vars appear unset"), + Error::Anime(err) => write!(f, "Anime error: {}", err), + } + } +} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: AnimeError) -> Self { + Error::Anime(err) + } +} diff --git a/daemon-user/src/lib.rs b/daemon-user/src/lib.rs new file mode 100644 index 00000000..3a8034bb --- /dev/null +++ b/daemon-user/src/lib.rs @@ -0,0 +1,3 @@ +pub mod user_config; + +pub mod error; diff --git a/daemon-user/src/main.rs b/daemon-user/src/main.rs new file mode 100644 index 00000000..db224b72 --- /dev/null +++ b/daemon-user/src/main.rs @@ -0,0 +1,60 @@ +use rog_anime::Action; +use rog_dbus::AuraDbusClient; +use rog_user::user_config::*; + +use std::{ + thread::sleep, + time::{Duration, Instant}, +}; + +fn main() -> Result<(), Box> { + println!(" rog-dbus version {}", rog_dbus::VERSION); + + let (client, _) = AuraDbusClient::new().unwrap(); + + let mut config = UserConfig::new(); + config.load_config()?; + let anime = config.create_anime()?; + + // TODO: + // - find user config dir with xdg + // - load user config + // - start anime + // A way to reload when the config changes + + loop { + for action in anime.iter() { + let start = Instant::now(); + + match action { + Action::Animation(frames) => 'animation: loop { + for frame in frames.frames() { + client.proxies().anime().write(frame.frame().clone())?; + if let Some(time) = frames.duration() { + if Instant::now().duration_since(start) > time { + break 'animation; + } + } + sleep(frame.delay()); + } + if frames.duration().is_none() { + break 'animation; + } + }, + Action::Image(image) => { + client.proxies().anime().write(image.as_ref().clone())?; + } + Action::Pause(duration) => 'pause: loop { + if Instant::now().duration_since(start) > *duration { + break 'pause; + } + sleep(Duration::from_millis(10)); + }, + Action::AudioEq => {} + Action::SystemInfo => {} + Action::TimeDate => {} + Action::Matrix => {} + } + } + } +} diff --git a/daemon-user/src/user_config.rs b/daemon-user/src/user_config.rs new file mode 100644 index 00000000..f346b055 --- /dev/null +++ b/daemon-user/src/user_config.rs @@ -0,0 +1,151 @@ +use std::{ + fs::{create_dir, OpenOptions}, + io::{Read, Write}, + path::PathBuf, + time::Duration, +}; + +use rog_anime::{Sequences, Vec2}; +use serde_derive::{Deserialize, Serialize}; + +use crate::error::Error; + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct UserConfig { + anime: Vec, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub enum AnimeAction { + /// Full gif sequence. Immutable. + AsusAnimation { + file: PathBuf, + duration: Option, + brightness: f32, + }, + /// Basic image, can have properties changed + ImageAnimation { + file: PathBuf, + scale: f32, + angle: f32, + translation: Vec2, + duration: Option, + brightness: f32, + }, + Image { + file: PathBuf, + scale: f32, + angle: f32, + translation: Vec2, + brightness: f32, + }, + /// A pause to be used between sequences + Pause(Duration), +} + +impl UserConfig { + pub fn new() -> Self { + Self { + anime: vec![ + AnimeAction::AsusAnimation { + file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(), + brightness: 0.5, + duration: None, + }, + AnimeAction::ImageAnimation { + file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), + scale: 0.9, + angle: 0.65, + translation: Vec2::default(), + brightness: 0.5, + duration: Some(Duration::from_secs(5)), + }, + AnimeAction::Image { + file: "/usr/share/asusd/anime/custom/rust.png".into(), + scale: 1.0, + angle: 0.0, + translation: Vec2::default(), + brightness: 0.6, + }, + AnimeAction::Pause(Duration::from_secs(6)), + AnimeAction::ImageAnimation { + file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(), + scale: 0.9, + angle: 0.0, + translation: Vec2::new(3.0, 2.0), + brightness: 0.5, + duration: None, + }, + ], + } + } + + pub fn load_config(&mut self) -> Result<(), Error> { + let mut path = if let Some(dir) = dirs::config_dir() { + dir + } else { + return Err(Error::XdgVars); + }; + + path.push("rog"); + if !path.exists() { + create_dir(path.clone())?; + } + + path.push("rog-user.cfg"); + + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&path)?; + + let mut buf = String::new(); + + if let Ok(read_len) = file.read_to_string(&mut buf) { + if read_len == 0 { + let json = serde_json::to_string_pretty(&self).unwrap(); + file.write_all(json.as_bytes())?; + } else if let Ok(data) = serde_json::from_str::(&buf) { + self.anime = data.anime; + return Ok(()); + } + } + Ok(()) + //Err(Error::ConfigLoadFail) + } + + pub fn create_anime(&self) -> Result { + let mut seq = Sequences::new(); + + for anime in self.anime.iter() { + match anime { + AnimeAction::AsusAnimation { + file, + duration, + brightness, + } => seq.add_asus_gif(&file, *duration, *brightness)?, + AnimeAction::ImageAnimation { + file, + scale, + angle, + translation, + duration, + brightness, + } => { + seq.add_image_gif(&file, *scale, *angle, *translation, *duration, *brightness)? + } + AnimeAction::Image { + file, + scale, + angle, + translation, + brightness, + } => seq.add_png(&file, *scale, *angle, *translation, *brightness)?, + AnimeAction::Pause(duration) => seq.add_pause(*duration)?, + } + } + + Ok(seq) + } +} diff --git a/daemon/src/ctrl_anime.rs b/daemon/src/ctrl_anime.rs index 8ee2fdc5..b84ac5ab 100644 --- a/daemon/src/ctrl_anime.rs +++ b/daemon/src/ctrl_anime.rs @@ -16,10 +16,10 @@ const ON_OFF: u8 = 0x04; use log::{error, info, warn}; use rog_anime::{AniMeDataBuffer, AniMePacketType}; use rusb::{Device, DeviceHandle}; -use zvariant::ObjectPath; use std::error::Error; use std::time::Duration; use zbus::dbus_interface; +use zvariant::ObjectPath; use crate::GetSupported; @@ -52,7 +52,10 @@ pub trait Dbus { impl crate::ZbusAdd for CtrlAnimeDisplay { fn add_to_server(self, server: &mut zbus::ObjectServer) { server - .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"), self) + .at( + &ObjectPath::from_str_unchecked("/org/asuslinux/Anime"), + self, + ) .map_err(|err| { warn!("CtrlAnimeDisplay: add_to_server {}", err); err diff --git a/daemon/src/ctrl_charge.rs b/daemon/src/ctrl_charge.rs index e5509634..e583f584 100644 --- a/daemon/src/ctrl_charge.rs +++ b/daemon/src/ctrl_charge.rs @@ -2,13 +2,13 @@ use crate::{config::Config, error::RogError, GetSupported}; //use crate::dbus::DbusEvents; use log::{info, warn}; use serde_derive::{Deserialize, Serialize}; -use zvariant::ObjectPath; use std::fs::OpenOptions; use std::io::Write; use std::path::Path; use std::sync::Arc; use std::sync::Mutex; use zbus::dbus_interface; +use zvariant::ObjectPath; static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold"; @@ -64,7 +64,10 @@ impl CtrlCharge { impl crate::ZbusAdd for CtrlCharge { fn add_to_server(self, server: &mut zbus::ObjectServer) { server - .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Charge"), self) + .at( + &ObjectPath::from_str_unchecked("/org/asuslinux/Charge"), + self, + ) .map_err(|err| { warn!("CtrlCharge: add_to_server {}", err); err diff --git a/daemon/src/ctrl_fan_cpu.rs b/daemon/src/ctrl_fan_cpu.rs index 601d1614..f01d67ed 100644 --- a/daemon/src/ctrl_fan_cpu.rs +++ b/daemon/src/ctrl_fan_cpu.rs @@ -3,13 +3,13 @@ use crate::{config::Config, GetSupported}; use log::{info, warn}; use rog_types::profile::{FanLevel, Profile, ProfileEvent}; use serde_derive::{Deserialize, Serialize}; -use zvariant::ObjectPath; use std::fs::OpenOptions; use std::io::Write; use std::path::Path; use std::sync::Arc; use std::sync::Mutex; use zbus::{dbus_interface, fdo::Error}; +use zvariant::ObjectPath; static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy"; static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode"; @@ -181,7 +181,10 @@ impl DbusFanAndCpu { impl crate::ZbusAdd for DbusFanAndCpu { fn add_to_server(self, server: &mut zbus::ObjectServer) { server - .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Profile"), self) + .at( + &ObjectPath::from_str_unchecked("/org/asuslinux/Profile"), + self, + ) .map_err(|err| { warn!("DbusFanAndCpu: add_to_server {}", err); err diff --git a/daemon/src/ctrl_gfx/gfx.rs b/daemon/src/ctrl_gfx/gfx.rs index e9c8a3e9..866e1ba5 100644 --- a/daemon/src/ctrl_gfx/gfx.rs +++ b/daemon/src/ctrl_gfx/gfx.rs @@ -7,7 +7,6 @@ use logind_zbus::{ ManagerProxy, SessionProxy, }; use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; -use zvariant::ObjectPath; use std::{io::Write, ops::Add, path::Path, time::Instant}; use std::{iter::FromIterator, thread::JoinHandle}; use std::{process::Command, thread::sleep, time::Duration}; @@ -16,6 +15,7 @@ use std::{sync::Arc, sync::Mutex}; use sysfs_class::{PciDevice, SysClass}; use system::{GraphicsDevice, PciBus}; use zbus::{dbus_interface, Connection}; +use zvariant::ObjectPath; use crate::*; diff --git a/daemon/src/ctrl_leds.rs b/daemon/src/ctrl_leds.rs index 8643e339..cb5d0502 100644 --- a/daemon/src/ctrl_leds.rs +++ b/daemon/src/ctrl_leds.rs @@ -14,13 +14,13 @@ use rog_types::{ aura_modes::{AuraEffect, AuraModeNum, LedBrightness}, LED_MSG_LEN, }; -use zvariant::ObjectPath; use std::fs::OpenOptions; use std::io::{Read, Write}; use std::path::Path; use std::sync::Arc; use std::sync::Mutex; use zbus::dbus_interface; +use zvariant::ObjectPath; use crate::GetSupported; diff --git a/daemon/src/ctrl_rog_bios.rs b/daemon/src/ctrl_rog_bios.rs index c12d6607..fce680fa 100644 --- a/daemon/src/ctrl_rog_bios.rs +++ b/daemon/src/ctrl_rog_bios.rs @@ -1,7 +1,6 @@ use crate::{config::Config, error::RogError, GetSupported}; use log::{error, info, warn}; use serde_derive::{Deserialize, Serialize}; -use zvariant::ObjectPath; use std::fs::OpenOptions; use std::io::BufRead; use std::io::{Read, Write}; @@ -10,6 +9,7 @@ use std::process::Command; use std::sync::Arc; use std::sync::Mutex; use zbus::dbus_interface; +use zvariant::ObjectPath; const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs"; const DRACUT_PATH: &str = "/usr/bin/dracut"; @@ -102,7 +102,10 @@ impl CtrlRogBios { impl crate::ZbusAdd for CtrlRogBios { fn add_to_server(self, server: &mut zbus::ObjectServer) { server - .at(&ObjectPath::from_str_unchecked("/org/asuslinux/RogBios"), self) + .at( + &ObjectPath::from_str_unchecked("/org/asuslinux/RogBios"), + self, + ) .map_err(|err| { warn!("CtrlRogBios: add_to_server {}", err); err diff --git a/daemon/src/ctrl_supported.rs b/daemon/src/ctrl_supported.rs index 1c1c8f7d..335827a1 100644 --- a/daemon/src/ctrl_supported.rs +++ b/daemon/src/ctrl_supported.rs @@ -31,7 +31,10 @@ impl SupportedFunctions { impl crate::ZbusAdd for SupportedFunctions { fn add_to_server(self, server: &mut zbus::ObjectServer) { server - .at(&ObjectPath::from_str_unchecked("/org/asuslinux/Supported"), self) + .at( + &ObjectPath::from_str_unchecked("/org/asuslinux/Supported"), + self, + ) .map_err(|err| { warn!("SupportedFunctions: add_to_server {}", err); err diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 64cd601f..7bd8b29c 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -175,10 +175,13 @@ fn start_daemon() -> Result<(), Box> { }); object_server - .with(&ObjectPath::from_str_unchecked("/org/asuslinux/Charge"), |obj: &CtrlCharge| { - let x = obj.limit(); - obj.notify_charge(x as u8) - }) + .with( + &ObjectPath::from_str_unchecked("/org/asuslinux/Charge"), + |obj: &CtrlCharge| { + let x = obj.limit(); + obj.notify_charge(x as u8) + }, + ) .map_err(|err| { warn!("object_server notify_charge error: {}", err); }) diff --git a/data/anime/Festive/Cupid.gif b/data/anime/asus/festive/Cupid.gif similarity index 100% rename from data/anime/Festive/Cupid.gif rename to data/anime/asus/festive/Cupid.gif diff --git a/data/anime/Festive/Firework.gif b/data/anime/asus/festive/Firework.gif similarity index 100% rename from data/anime/Festive/Firework.gif rename to data/anime/asus/festive/Firework.gif diff --git a/data/anime/Festive/Halloween.gif b/data/anime/asus/festive/Halloween.gif similarity index 100% rename from data/anime/Festive/Halloween.gif rename to data/anime/asus/festive/Halloween.gif diff --git a/data/anime/Festive/Happy Holiday.gif b/data/anime/asus/festive/Happy Holiday.gif similarity index 100% rename from data/anime/Festive/Happy Holiday.gif rename to data/anime/asus/festive/Happy Holiday.gif diff --git a/data/anime/Festive/Happy new year.gif b/data/anime/asus/festive/Happy new year.gif similarity index 100% rename from data/anime/Festive/Happy new year.gif rename to data/anime/asus/festive/Happy new year.gif diff --git a/data/anime/Festive/Lantern.gif b/data/anime/asus/festive/Lantern.gif similarity index 100% rename from data/anime/Festive/Lantern.gif rename to data/anime/asus/festive/Lantern.gif diff --git a/data/anime/Festive/Valentine's Day.gif b/data/anime/asus/festive/Valentine's Day.gif similarity index 100% rename from data/anime/Festive/Valentine's Day.gif rename to data/anime/asus/festive/Valentine's Day.gif diff --git a/data/anime/Festive/Year of the Ox.gif b/data/anime/asus/festive/Year of the Ox.gif similarity index 100% rename from data/anime/Festive/Year of the Ox.gif rename to data/anime/asus/festive/Year of the Ox.gif diff --git a/data/anime/Gaming/Bird.gif b/data/anime/asus/gaming/Bird.gif similarity index 100% rename from data/anime/Gaming/Bird.gif rename to data/anime/asus/gaming/Bird.gif diff --git a/data/anime/Gaming/Controller.gif b/data/anime/asus/gaming/Controller.gif similarity index 100% rename from data/anime/Gaming/Controller.gif rename to data/anime/asus/gaming/Controller.gif diff --git a/data/anime/Gaming/FPS.gif b/data/anime/asus/gaming/FPS.gif similarity index 100% rename from data/anime/Gaming/FPS.gif rename to data/anime/asus/gaming/FPS.gif diff --git a/data/anime/Gaming/Fight.gif b/data/anime/asus/gaming/Fight.gif similarity index 100% rename from data/anime/Gaming/Fight.gif rename to data/anime/asus/gaming/Fight.gif diff --git a/data/anime/Gaming/Keyboard.gif b/data/anime/asus/gaming/Keyboard.gif similarity index 100% rename from data/anime/Gaming/Keyboard.gif rename to data/anime/asus/gaming/Keyboard.gif diff --git a/data/anime/Gaming/MOBA.gif b/data/anime/asus/gaming/MOBA.gif similarity index 100% rename from data/anime/Gaming/MOBA.gif rename to data/anime/asus/gaming/MOBA.gif diff --git a/data/anime/Gaming/UFO.gif b/data/anime/asus/gaming/UFO.gif similarity index 100% rename from data/anime/Gaming/UFO.gif rename to data/anime/asus/gaming/UFO.gif diff --git a/data/anime/Music/DJ.gif b/data/anime/asus/music/DJ.gif similarity index 100% rename from data/anime/Music/DJ.gif rename to data/anime/asus/music/DJ.gif diff --git a/data/anime/Music/Music-player.gif b/data/anime/asus/music/Music-player.gif similarity index 100% rename from data/anime/Music/Music-player.gif rename to data/anime/asus/music/Music-player.gif diff --git a/data/anime/ROG Gallery/For-those-who-dare.gif b/data/anime/asus/rog/For-those-who-dare.gif similarity index 100% rename from data/anime/ROG Gallery/For-those-who-dare.gif rename to data/anime/asus/rog/For-those-who-dare.gif diff --git a/data/anime/ROG Gallery/For-those-who-dare_2.gif b/data/anime/asus/rog/For-those-who-dare_2.gif similarity index 100% rename from data/anime/ROG Gallery/For-those-who-dare_2.gif rename to data/anime/asus/rog/For-those-who-dare_2.gif diff --git a/data/anime/ROG Gallery/Fragment.gif b/data/anime/asus/rog/Fragment.gif similarity index 100% rename from data/anime/ROG Gallery/Fragment.gif rename to data/anime/asus/rog/Fragment.gif diff --git a/data/anime/ROG Gallery/Infinite-triangle.gif b/data/anime/asus/rog/Infinite-triangle.gif similarity index 100% rename from data/anime/ROG Gallery/Infinite-triangle.gif rename to data/anime/asus/rog/Infinite-triangle.gif diff --git a/data/anime/ROG Gallery/Kaleidoscope1.gif b/data/anime/asus/rog/Kaleidoscope1.gif similarity index 100% rename from data/anime/ROG Gallery/Kaleidoscope1.gif rename to data/anime/asus/rog/Kaleidoscope1.gif diff --git a/data/anime/ROG Gallery/Kaleidoscope2.gif b/data/anime/asus/rog/Kaleidoscope2.gif similarity index 100% rename from data/anime/ROG Gallery/Kaleidoscope2.gif rename to data/anime/asus/rog/Kaleidoscope2.gif diff --git a/data/anime/ROG Gallery/Kaleidoscope2.png b/data/anime/asus/rog/Kaleidoscope2.png similarity index 100% rename from data/anime/ROG Gallery/Kaleidoscope2.png rename to data/anime/asus/rog/Kaleidoscope2.png diff --git a/data/anime/ROG Gallery/Sunset.gif b/data/anime/asus/rog/Sunset.gif similarity index 100% rename from data/anime/ROG Gallery/Sunset.gif rename to data/anime/asus/rog/Sunset.gif diff --git a/data/anime/Trend/Dog.gif b/data/anime/asus/trend/Dog.gif similarity index 100% rename from data/anime/Trend/Dog.gif rename to data/anime/asus/trend/Dog.gif diff --git a/data/anime/Trend/Ski.gif b/data/anime/asus/trend/Ski.gif similarity index 100% rename from data/anime/Trend/Ski.gif rename to data/anime/asus/trend/Ski.gif diff --git a/data/anime/Trend/Wave.gif b/data/anime/asus/trend/Wave.gif similarity index 100% rename from data/anime/Trend/Wave.gif rename to data/anime/asus/trend/Wave.gif diff --git a/data/anime/custom/rust.png b/data/anime/custom/rust.png new file mode 100644 index 0000000000000000000000000000000000000000..e9a4c2e37547372823a2740421543c6a44a7c692 GIT binary patch literal 29814 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 _Il%gsJhq!VR_LXI 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 literal 0 HcmV?d00001 diff --git a/data/asusd-user.service b/data/asusd-user.service new file mode 100644 index 00000000..ba845176 --- /dev/null +++ b/data/asusd-user.service @@ -0,0 +1,14 @@ +[Unit] +Description=ASUS User Daemon +StartLimitInterval=200 +StartLimitBurst=2 + +[Service] +ExecStartPre=/usr/bin/sleep 2 +ExecStart=/usr/bin/asusd-user +Restart=on-failure +RestartSec=1 +Type=simple + +[Install] +WantedBy=default.target \ No newline at end of file diff --git a/data/user-example.json b/data/user-example.json new file mode 100644 index 00000000..b80d8e3e --- /dev/null +++ b/data/user-example.json @@ -0,0 +1,45 @@ +{ + "anime": [ + { + "AsusAnimation": { + "file": "/usr/share/asusd/anime/asus/Controller.gif", + "duration": { + "secs": 5, + "nanos": 0 + } + } + }, + { + "ImageAnimation": { + "file": "/usr/share/asusd/anime/sonic.gif", + "scale": 0.9, + "angle": 0.65, + "translation": [ + 0.0, + 0.0 + ], + "duration": { + "secs": 5, + "nanos": 0 + }, + "brightness": 0.5 + } + }, + { + "Image": { + "file_path": "/usr/share/asusd/anime/doom.png", + "scale": 0.7, + "angle": 0.0, + "translation": [ + 0.0, + 0.0 + ], + "duration": { + "secs": 5, + "nanos": 0 + }, + "brightness": 0.6 + } + } + ] + } \ No newline at end of file diff --git a/rog-anime/Cargo.toml b/rog-anime/Cargo.toml index 50fd09ae..2ced82eb 100644 --- a/rog-anime/Cargo.toml +++ b/rog-anime/Cargo.toml @@ -11,15 +11,17 @@ edition = "2018" [dependencies] png_pong = "^0.8.0" -glam = "*" pix = "0.13" gif = "^0.11.2" + serde = "^1.0" serde_derive = "^1.0" zvariant = "^2.5" zvariant_derive = "^2.5" +glam = { version = "*", features = ["serde"] } + [features] default = ["zbus"] zbus = [] \ No newline at end of file diff --git a/rog-anime/src/anime_data.rs b/rog-anime/src/data.rs similarity index 100% rename from rog-anime/src/anime_data.rs rename to rog-anime/src/data.rs diff --git a/rog-anime/src/anime_diagonal.rs b/rog-anime/src/diagonal.rs similarity index 90% rename from rog-anime/src/anime_diagonal.rs rename to rog-anime/src/diagonal.rs index 31b22982..d21c89a3 100644 --- a/rog-anime/src/anime_diagonal.rs +++ b/rog-anime/src/diagonal.rs @@ -1,25 +1,26 @@ -use std::path::Path; +use std::{path::Path, time::Duration}; use crate::{ - anime_data::{AniMeDataBuffer, ANIME_DATA_LEN}, + data::{AniMeDataBuffer, ANIME_DATA_LEN}, error::AnimeError, }; const WIDTH: usize = 74; const HEIGHT: usize = 36; +/// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images) #[derive(Debug, Clone)] -pub struct AniMeDiagonal([[u8; WIDTH]; HEIGHT]); +pub struct AniMeDiagonal([[u8; WIDTH]; HEIGHT], Option); impl Default for AniMeDiagonal { fn default() -> Self { - Self::new() + Self::new(None) } } impl AniMeDiagonal { - pub fn new() -> Self { - Self([[0u8; WIDTH]; HEIGHT]) + pub fn new(duration: Option) -> Self { + Self([[0u8; WIDTH]; HEIGHT], duration) } pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] { @@ -38,14 +39,18 @@ impl AniMeDiagonal { /// Generate the base image from inputs. The result can be displayed as is or /// updated via scale, position, or angle then displayed again after `update()`. - pub fn from_png(path: &Path, bright: f32) -> Result { + pub fn from_png( + path: &Path, + duration: Option, + bright: f32, + ) -> Result { use pix::el::Pixel; let data = std::fs::read(path)?; let data = std::io::Cursor::new(data); let decoder = png_pong::Decoder::new(data)?.into_steps(); let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??; - let mut matrix = AniMeDiagonal::new(); + let mut matrix = AniMeDiagonal::new(duration); let width; match raster { diff --git a/rog-anime/src/anime_gif.rs b/rog-anime/src/gif.rs similarity index 84% rename from rog-anime/src/anime_gif.rs rename to rog-anime/src/gif.rs index e5e7e07e..59b837a7 100644 --- a/rog-anime/src/anime_gif.rs +++ b/rog-anime/src/gif.rs @@ -22,13 +22,19 @@ impl AniMeFrame { } } +/// A gif animation. This is a collection of frames from the gif, and a duration +/// that the animation should be shown for. #[derive(Debug, Clone, Deserialize, Serialize)] -pub struct AniMeGif(Vec); +pub struct AniMeGif(Vec, Option); impl AniMeGif { /// Create an animation using the 74x36 ASUS gif format - pub fn create_diagonal_gif(file_name: &Path, brightness: f32) -> Result { - let mut matrix = AniMeDiagonal::new(); + pub fn create_diagonal_gif( + file_name: &Path, + duration: Option, + brightness: f32, + ) -> Result { + let mut matrix = AniMeDiagonal::new(None); let mut decoder = gif::DecodeOptions::new(); // Configure the decoder such that it will expand the image to RGBA. @@ -59,7 +65,7 @@ impl AniMeGif { delay: Duration::from_millis(wait as u64), }); } - Ok(Self(frames)) + Ok(Self(frames, duration)) } /// Create an animation using a gif of any size. This method must precompute the @@ -69,6 +75,7 @@ impl AniMeGif { scale: f32, angle: f32, translation: Vec2, + duration: Option, brightness: f32, ) -> Result { let mut frames = Vec::new(); @@ -98,8 +105,14 @@ impl AniMeGif { if matches!(frame.dispose, gif::DisposalMethod::Background) { let pixels: Vec = vec![Pixel::default(); (width as u32 * height as u32) as usize]; - image = - AniMeImage::new(Vec2::new(scale,scale), angle, translation, brightness, pixels, width as u32); + image = AniMeImage::new( + Vec2::new(scale, scale), + angle, + translation, + brightness, + pixels, + width as u32, + ); } for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() { for (x, px) in row.chunks(4).enumerate() { @@ -122,10 +135,14 @@ impl AniMeGif { delay: Duration::from_millis(wait as u64), }); } - Ok(Self(frames)) + Ok(Self(frames, duration)) } pub fn frames(&self) -> &[AniMeFrame] { &self.0 } + + pub fn duration(&self) -> Option { + self.1 + } } diff --git a/rog-anime/src/anime_grid.rs b/rog-anime/src/grid.rs similarity index 95% rename from rog-anime/src/anime_grid.rs rename to rog-anime/src/grid.rs index 253f6406..36bc6198 100644 --- a/rog-anime/src/anime_grid.rs +++ b/rog-anime/src/grid.rs @@ -1,5 +1,7 @@ -use crate::anime_data::{AniMeDataBuffer, ANIME_DATA_LEN}; -use crate::anime_image::LED_IMAGE_POSITIONS; +use std::time::Duration; + +use crate::data::{AniMeDataBuffer, ANIME_DATA_LEN}; +use crate::image::LED_IMAGE_POSITIONS; const WIDTH: usize = 33; const HEIGHT: usize = 55; @@ -10,17 +12,17 @@ const HEIGHT: usize = 55; /// Width = 33 /// height = 55 #[derive(Debug, Clone)] -pub struct AniMeGrid([[u8; WIDTH]; HEIGHT]); +pub struct AniMeGrid([[u8; WIDTH]; HEIGHT], Option); impl Default for AniMeGrid { fn default() -> Self { - Self::new() + Self::new(None) } } impl AniMeGrid { - pub fn new() -> Self { - AniMeGrid([[0u8; WIDTH]; HEIGHT]) + pub fn new(duration: Option) -> Self { + AniMeGrid([[0u8; WIDTH]; HEIGHT], duration) } pub fn set(&mut self, x: usize, y: usize, b: u8) { @@ -97,11 +99,11 @@ impl From for AniMeDataBuffer { #[cfg(test)] mod tests { - use crate::anime_grid::*; + use crate::grid::*; #[test] fn check_data_alignment() { - let mut matrix = AniMeGrid::new(); + let mut matrix = AniMeGrid::new(None); { let tmp = matrix.get_mut(); for row in tmp.iter_mut() { diff --git a/rog-anime/src/anime_image.rs b/rog-anime/src/image.rs similarity index 99% rename from rog-anime/src/anime_image.rs rename to rog-anime/src/image.rs index d9901348..595edf37 100644 --- a/rog-anime/src/anime_image.rs +++ b/rog-anime/src/image.rs @@ -4,7 +4,7 @@ pub use glam::Vec2; use glam::{Mat3, Vec3}; use crate::{ - anime_data::{AniMeDataBuffer, ANIME_DATA_LEN}, + data::{AniMeDataBuffer, ANIME_DATA_LEN}, error::AnimeError, }; @@ -252,7 +252,7 @@ impl AniMeImage { width = ras.width(); ras.pixels() .iter() - .map(|px| crate::anime_image::Pixel { + .map(|px| crate::image::Pixel { color: ::from(px.one()) as u32, alpha: ::from(px.alpha()), }) @@ -261,7 +261,14 @@ impl AniMeImage { _ => return Err(AnimeError::Format), }; - let mut matrix = AniMeImage::new(Vec2::new(scale, scale), angle, translation, bright, pixels, width); + let mut matrix = AniMeImage::new( + Vec2::new(scale, scale), + angle, + translation, + bright, + pixels, + width, + ); matrix.update(); Ok(matrix) @@ -1539,7 +1546,7 @@ pub const LED_IMAGE_POSITIONS: [Option; LED_PIXEL_LEN] = [ #[cfg(test)] mod tests { - use crate::anime_image::*; + use crate::image::*; #[test] fn led_positions() { diff --git a/rog-anime/src/lib.rs b/rog-anime/src/lib.rs index f44a51b0..9bba7e0c 100644 --- a/rog-anime/src/lib.rs +++ b/rog-anime/src/lib.rs @@ -1,89 +1,25 @@ -use serde_derive::{Deserialize, Serialize}; /// The main data conversion for transfering in shortform over dbus or other, /// or writing directly to the USB device -mod anime_data; -use std::{path::Path, time::Duration}; +mod data; -pub use anime_data::*; +pub use data::*; /// Useful for specialised effects that required a grid of data -mod anime_grid; -pub use anime_grid::*; +mod grid; +pub use grid::*; /// Transform a PNG image for displaying on AniMe matrix display -mod anime_image; -pub use anime_image::*; +mod image; +pub use image::*; -mod anime_diagonal; -pub use anime_diagonal::*; +mod diagonal; +pub use diagonal::*; -mod anime_gif; -pub use anime_gif::*; -use error::AnimeError; +mod gif; +pub use crate::gif::*; + +mod sequencer; +pub use sequencer::*; /// Base errors that are possible pub mod error; - -// TODO: make schema to rebuild the full sequence without requiring saving the actual -// packet data - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub enum AniMeBlock { - /// Full gif sequence. Immutable. - Animation(AniMeGif), - /// Basic image, can have properties changed - Image(Box), - /// A pause to be used between sequences - Pause(Duration), -} - -impl AniMeBlock { - pub fn asus_gif(file: &Path, brightness: f32) -> Result { - let frames = AniMeGif::create_diagonal_gif(file, brightness)?; - Ok(Self::Animation(frames)) - } - - pub fn png( - file: &Path, - scale: f32, - angle: f32, - translation: Vec2, - brightness: f32, - ) -> Result { - let image = AniMeImage::from_png(file, scale, angle, translation, brightness)?; - let data = ::from(&image); - Ok(Self::Image(Box::new(data))) - } - - pub fn image_gif( - file: &Path, - scale: f32, - angle: f32, - translation: Vec2, - brightness: f32, - ) -> Result { - let frames = AniMeGif::create_png_gif(file, scale, angle, translation, brightness)?; - Ok(Self::Animation(frames)) - } - - pub fn get_animation(&self) -> Option<&AniMeGif> { - match self { - AniMeBlock::Animation(anim) => Some(anim), - _ => None, - } - } - - pub fn get_image(&self) -> Option<&AniMeDataBuffer> { - match self { - AniMeBlock::Image(image) => Some(image), - _ => None, - } - } - - pub fn get_pause(&self) -> Option { - match self { - AniMeBlock::Pause(pause) => Some(*pause), - _ => None, - } - } -} diff --git a/rog-anime/src/sequencer.rs b/rog-anime/src/sequencer.rs new file mode 100644 index 00000000..0db9fcde --- /dev/null +++ b/rog-anime/src/sequencer.rs @@ -0,0 +1,108 @@ +use std::{path::Path, time::Duration}; + +use glam::Vec2; +use serde_derive::{Deserialize, Serialize}; + +use crate::{error::AnimeError, AniMeDataBuffer, AniMeGif, AniMeImage}; + +/// +#[derive(Debug, Deserialize, Serialize)] +pub enum Action { + /// Full gif sequence. Immutable. + Animation(AniMeGif), + /// Basic image, can have properties changed and image updated via those properties + Image(Box), + /// A pause to be used between sequences + Pause(Duration), + /// Placeholder + AudioEq, + /// Placeholder + SystemInfo, + /// Placeholder + TimeDate, + /// Placeholder + Matrix, +} + +/// An optimised precomputed set of actions +#[derive(Debug, Deserialize, Serialize, Default)] +pub struct Sequences(Vec); + +impl Sequences { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn add_asus_gif( + &mut self, + file: &Path, + duration: Option, + brightness: f32, + ) -> Result<(), AnimeError> { + let frames = AniMeGif::create_diagonal_gif(file, duration, brightness)?; + self.0.push(Action::Animation(frames)); + Ok(()) + } + + pub fn add_png( + &mut self, + file: &Path, + scale: f32, + angle: f32, + translation: Vec2, + brightness: f32, + ) -> Result<(), AnimeError> { + let image = AniMeImage::from_png(file, scale, angle, translation, brightness)?; + let data = ::from(&image); + self.0.push(Action::Image(Box::new(data))); + Ok(()) + } + + pub fn add_image_gif( + &mut self, + file: &Path, + scale: f32, + angle: f32, + translation: Vec2, + duration: Option, + brightness: f32, + ) -> Result<(), AnimeError> { + let frames = + AniMeGif::create_png_gif(file, scale, angle, translation, duration, brightness)?; + self.0.push(Action::Animation(frames)); + Ok(()) + } + + pub fn add_pause(&mut self, duration: Duration) -> Result<(), AnimeError> { + self.0.push(Action::Pause(duration)); + Ok(()) + } + + pub fn iter(&self) -> ActionIterator { + ActionIterator { + actions: &self, + next_idx: 0, + } + } +} + +pub struct ActionIterator<'a> { + actions: &'a Sequences, + next_idx: usize, +} + +impl<'a> Iterator for ActionIterator<'a> { + type Item = &'a Action; + + fn next(&mut self) -> Option<&'a Action> { + if self.next_idx == self.actions.0.len() { + self.next_idx = 0; + return None; + } + + let current = self.next_idx; + self.next_idx += 1; + + Some(&self.actions.0[current]) + } +}