Compare commits

...

92 Commits
4.4.0 ... 4.5.5

Author SHA1 Message Date
Luke D. Jones
c7893b16f9 Fix: ROGCC: Remove unwrap causing panic on main thread
Closes #293
2022-12-08 11:14:01 +13:00
Luke Jones
8e8681c190 Merge branch 'main' into 'main'
add led modes for FX506HC

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

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

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

See merge request asus-linux/asusctl!141
2022-12-04 01:48:10 +00:00
maxbachmann
9dab120bcf add led modes for G513RM 2022-12-03 22:03:35 +00:00
Luke D. Jones
14bf07ba79 Version bump for dep updates 2022-12-02 16:39:46 +13:00
Luke D. Jones
e76d01eaed Update dependencies 2022-12-02 16:37:33 +13:00
Luke Jones
072a066f28 Merge branch 'fixed_readme_dependency_list_for_fedora' into 'main'
rust/cargo is also needed

See merge request asus-linux/asusctl!140
2022-12-01 20:11:39 +00:00
A. Binzxxxxxx
165c6f8ab3 rust/cargo is also needed 2022-12-01 20:06:28 +00:00
Luke Jones
5728a9af62 Update README.md 2022-11-22 21:54:51 +00:00
Luke Jones
17a880b2c5 Merge branch 'main' into 'main'
Fix VivoBook detection

See merge request asus-linux/asusctl!139
2022-11-20 19:48:29 +00:00
RushingAlien
6ba9b9d75d Fix VivoBook detection 2022-11-19 22:06:34 +07:00
Luke Jones
eb1f6c83ce Merge branch 'fix-gitlab-ci' into 'main'
Fix GitLab CI

See merge request asus-linux/asusctl!138
2022-11-16 20:06:06 +00:00
Herohtar
af653ea405 Don't install unnecessary packages 2022-11-16 12:26:14 -06:00
Herohtar
bc13891cdf Install required libgtk-3-dev package 2022-11-16 12:25:29 -06:00
Luke D. Jones
aad4dc909e Bump version 2022-11-16 20:33:23 +13:00
Luke D. Jones
ad79adcbfa ROGCC: splatter log messages everywhere. Rename state control 2022-11-16 20:32:11 +13:00
Luke D. Jones
73b1a7050a ROGCC: Make zbus notifications fully manage pagestates 2022-11-15 22:26:17 +13:00
Luke D. Jones
762bfea102 ROGCC: share PageState so tray can use it. zbus notifs update this 2022-11-15 11:12:41 +13:00
Luke D. Jones
b41fdf5cfe ROGCC: add status for dgpu, charge ctl, panel-od to systray 2022-11-14 11:05:52 +13:00
Luke D. Jones
bf13ebebd3 Set tray icon after init 2022-11-13 21:00:33 +13:00
Luke D. Jones
3a73e3a526 Try to prevent tray loop stalling 2022-11-13 12:52:52 +13:00
Luke D. Jones
1211400d7b 4.5.1-RC1 2022-11-11 20:13:00 +13:00
Luke D. Jones
ff9edb9876 Enable system tray status for dGPU and actions 2022-11-11 20:09:43 +13:00
Luke D. Jones
20f8251dd3 Adjust FA506IE led mode match to FA506I 2022-11-10 09:14:17 +13:00
Luke Jones
902dfed67c Merge branch 'add-asus-tuf-gaming-a15-led-modes' into 'main'
Add led_data for 2022 ASUS TUF Gaming A15 FA506IE

See merge request asus-linux/asusctl!137
2022-11-09 20:12:04 +00:00
Luke D. Jones
5e08b4416d Bump version 2022-11-10 09:11:52 +13:00
Herohtar
44c3ab7294 Add led_data for 2022 ASUS TUF Gaming A15 FA506IE 2022-11-09 12:03:23 -06:00
Luke D. Jones
be40474f79 Update app icons 2022-11-09 21:47:41 +13:00
Luke Jones
3654da2ff8 Merge branch 'tuxfanou/building_opensuse' into 'main'
Add openSUSE requirements to build asusctl

See merge request asus-linux/asusctl!136
2022-11-08 20:20:46 +00:00
Luke Jones
60bbad4ab6 Merge branch 'feat-add-vivobook-family' into 'main'
Add Vivobook to asusd rules

See merge request asus-linux/asusctl!135
2022-11-08 20:19:00 +00:00
Sarang S
0a59d5c041 Add Vivobook to asusd rules 2022-11-08 20:19:00 +00:00
Stéphane Burdin
1506fc44d3 Add openSUSE requirements to build asusctl 2022-11-08 16:14:53 +01:00
Luke D. Jones
aad31610f2 Add missing file 2022-11-08 22:03:42 +13:00
Luke D. Jones
a6fe7645e9 Tray icons 2022-11-08 21:55:09 +13:00
Luke D. Jones
4f8745ae19 Prep release 4.5.0 2022-11-07 21:36:28 +13:00
Luke D. Jones
5f4e950819 Update deps. Fixes to runtime 2022-11-07 09:47:15 +13:00
Luke D. Jones
efc752cce6 ROGCC: Use tokio instead of smol 2022-11-07 09:00:46 +13:00
Luke D. Jones
37553a5fdd Remove some dbg! statements 2022-11-06 22:28:00 +13:00
Luke D. Jones
cd5a85a843 Clarify gpu mux notif 2022-11-06 22:17:46 +13:00
Luke D. Jones
7385844a9b Fix rogcc not closing when run-in-background 2022-11-06 21:58:33 +13:00
Luke D. Jones
0b71104833 Fix rog-control-center notifs 2022-11-06 15:21:43 +13:00
Luke D. Jones
688e3a7358 Send signals using the correct context for each 2022-11-06 12:48:19 +13:00
Luke D. Jones
58ff566d65 Fix inclusion of supergfxctl lib 2022-11-04 21:31:45 +13:00
Luke D. Jones
1332ac803c Add notification of dGPU state change 2022-11-04 21:29:47 +13:00
Luke D. Jones
ba1d3f045d Add missing file 2022-10-11 22:25:49 +13:00
Luke D. Jones
e0ed52092a Refined AC monitoring 2022-10-11 22:13:54 +13:00
Luke D. Jones
921637f979 Make some ledmodes more generic matched across models 2022-10-10 13:19:15 +13:00
Luke D. Jones
f6498337fe RCC: disable vsync due to NoAvailablePixelFormat error: 2022-10-04 11:37:23 +13:00
Luke D. Jones
3a640a3269 Bump rc version 2022-10-01 14:57:49 +13:00
Luke D. Jones
e938f1f9ec Minor fixes to attr writes 2022-10-01 14:57:25 +13:00
Luke D. Jones
600d0ae3d9 Clippy run 2022-09-30 15:10:56 +13:00
Luke D. Jones
8569edf684 Try official latest docker image 2022-09-29 18:17:28 +13:00
Luke D. Jones
52af4ad2b2 Use 'latest' rustdocker image 2022-09-29 18:06:54 +13:00
Luke D. Jones
cde1b4f252 Shift all deps to workspace versioning 2022-09-29 17:08:28 +13:00
Luke Jones
2a4754cfc4 Merge branch 'ledmodes_for_rogflowx16' into 'main'
Added LED modes for ROG Flow X16

See merge request asus-linux/asusctl!134
2022-09-27 20:05:12 +00:00
Rino
51c97fa350 Added LED modes for ROG Flow X16 2022-09-27 12:44:49 +00:00
Luke D. Jones
c968dce009 Cleanup notifications some 2022-09-24 18:44:10 +12:00
Luke Jones
b2b6707f2e Merge branch 'fluke/inotify' into 'main'
Fluke/inotify

See merge request asus-linux/asusctl!133
2022-09-24 02:39:44 +00:00
Luke D. Jones
7939b00aa3 Check inotify paths are valid. Add dgu/egpu/ac_online checks 2022-09-24 14:34:15 +12:00
Luke D. Jones
30550aaa91 Further improve the daemon controller pattern and reduce cloned code 2022-09-23 20:07:43 +12:00
Luke D. Jones
7ea1f41286 Convert chunk of daemon to use async mutex 2022-09-23 10:50:09 +12:00
Luke D. Jones
9608d190b9 Use tokio in asusctl 2022-09-22 22:36:16 +12:00
Luke D. Jones
3b9cf474a7 inotify relies on tokio, so a switch is required.. 2022-09-22 12:55:15 +12:00
Luke D. Jones
283cb7e589 Previous inotify macro was blocking. Needs async closures... 2022-09-21 22:41:24 +12:00
Luke D. Jones
5d87747d96 Is smol blocking or inotify blocking it? 2022-09-21 22:17:55 +12:00
Luke D. Jones
56285916cd daemon: inotify for panel_od and gu_mux_mode 2022-09-21 19:04:28 +12:00
Luke D. Jones
a44a1bfa89 Add GU603Z to ledmodes 2022-09-20 21:02:58 +12:00
Luke D. Jones
0c97cf710d Trial single inotify test 2022-09-20 20:57:39 +12:00
Luke D. Jones
62c7338b2d Use loops to ensure settings apply where a mutex is tried 2022-09-15 13:29:00 +12:00
Luke Jones
af24623178 Merge branch 'zephyrus-m16-ledmodes' into 'main'
Add ledmodes for 2021 Zephyrus M16 models

See merge request asus-linux/asusctl!132
2022-09-14 04:52:47 +00:00
Albert Geantă
facb7f7f49 Add ledmodes for 2021 Zephyrus M16 models 2022-09-13 11:05:05 +03:00
Luke D. Jones
e38ab624e9 Add libfontconfig1-dev to CI env 2022-08-29 21:18:43 +12:00
127 changed files with 5296 additions and 3202 deletions

View File

@@ -1,27 +1,63 @@
image: rustdocker/rust:stable
image: rust:latest
cache:
key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths:
# Don't include `incremental` to save space
# Debug
- target/debug/build/
- target/debug/deps/
- target/debug/.fingerprint/
- target/debug/.cargo-lock
- target/debug/df_storyteller
# Release
- target/release/build/
- target/release/deps/
- target/release/.fingerprint/
- target/release/.cargo-lock
before_script:
- apt-get update -qq && apt-get install -y -qq libdbus-1-dev libclang-dev libudev-dev
- apt-get update -qq && apt-get install -y -qq libudev-dev libgtk-3-dev grep
stages:
- format
- check
- test
- build
- release
format:
except:
- tags
script:
- echo "nightly" > rust-toolchain
- rustup component add rustfmt
- cargo fmt --check
check:
except:
- tags
script:
- rustup component add clippy
- cargo check
# deny currently catches too much
#- cargo install cargo-deny && cargo deny
- cargo install cargo-cranky && cargo cranky
test:
except:
- tags
script:
- cargo check #+nightly check --features "clippy"
- cargo test
build:
release:
only:
- main
- tags
script:
- make && make vendor
artifacts:
paths:
- vendor_asus-nb-ctrl_*.tar.xz
- vendor_asusctl_*.tar.xz
- cargo-config
variables:
GIT_SUBMODULE_STRATEGY: normal

View File

@@ -4,7 +4,76 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased ]
## [Unreleased]
## [v4.5.5]
- remove an unwrap() causing panic on main ROGCC thread
## [v4.5.4]
### Changed
- ROGCC:: Allow ROGCC to run without supergfxd
- ROGCC: Tray/notifs now reads dGPU status directly via supergfx crate (supergfxd not required)
- Add rust-toolchain to force minimum rust version
## [v4.5.3]
### Changed
- Adjust how fan graph in ROGCC works, deny incorrect graphs
- Fix to apply the fan curve change in ROGCC to the correct profile
- Support for G713RS LED modes (Author: Peter Ivanov)
- Support for G713RM LED modes (Author: maxbachmann)
- Fix VivoBook detection
- Update dependencies to get latest winit crate (fixes various small issues)
## [v4.5.2]
### Changed
- Update dependencies and bump version
## [v4.5.1]
### Added
- Support for FA506IE LED modes (Author: Herohtar)
### Changed
- Add a basic system tray with dGPU status and gpu mode switch actions
- Fixup some notifications in ROGCC
- Add config options for notifications for ROGCC
- Share states with tray process in ROGCC
- Share tates with tray process in ROGCC
## [v4.5.0]
### Added
- intofy watches on:
- `charge_control_end_threshold`
- `panel_od`
- `gpu_mux_mode`
- `platform_profile`
- keyboard brightness
- These allow for updating any associated config and sending dbus notifications.
- New dbus methods
- `DgpuDisable`
- `SetDgpuDisable`
- `NotifyDgpuDisable`
- `EgpuEnable`
- `SetEgpuEnable`
- `NotifyEgpuEnable`
- `MainsOnline` (This is AC, check if plugged in or not)
- `NotifyMainsOnline`
- `nvidia-powerd.service` will now enable or disable depending on the AC power state
and on resume/boot (hybrid boot). It has been proven that this nvidia daemon can be
problematic when on battery, not allowing the dgpu to suspend within decent time and
sometimes blocking it completely.
- Notification to rog-control-center of dGPU state change
### Changed
- Use loops to ensure that mutex is gained for LED changes.
- asusctl now uses tokio for async runtime. This helps simplify some code.
- Properly fix notifs used in rog-control-center
### Breaking
- DBUS: all charge control methods renamed to:
- `ChargeControlEndThreshold`
- `SetChargeControlEndThreshold`
- `NotifyChargeControlEndThreshold`
- DBUS: all panel overdrive methods renamed to:
- `PanelOd` (from PanelOverdrive)
- `SetPanelOd`
- `NotifyPanelOd`
- Path `/org/asuslinux/Charge` changed to `/org/asuslinux/Power`
## [v4.4.0] - 2022-08-29
### Added

1819
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,40 @@
[workspace]
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
members = ["asusctl", "daemon", "daemon-user", "rog-platform", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "rog-control-center"]
[workspace.package]
version = "4.5.5"
[workspace.dependencies]
async-trait = "^0.1"
tokio = { version = "^1.22.0", features = ["macros", "rt-multi-thread", "time", "sync"]}
concat-idents = "^1.1"
dirs = "^4.0"
smol = "^1.3"
zbus = "^3.5"
logind-zbus = { version = "^3.0.3" } #, default-features = false, features = ["non_blocking"] }
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
toml = "^0.5.9"
log = "^0.4"
env_logger = "^0.10.0"
glam = { version = "^0.22", features = ["serde"] }
gumdrop = "^0.8"
udev = "^0.7"
rusb = "^0.9"
sysfs-class = "^0.1.3"
inotify = "^0.10.0"
png_pong = "^0.8"
pix = "^0.13"
tinybmp = "^0.4.0"
gif = "^0.12.0"
notify-rust = { git = "https://github.com/flukejones/notify-rust.git", default-features = false, features = ["z"] }
[profile.release]
# thin = 57s, asusd = 9.0M
@@ -10,7 +45,7 @@ opt-level = 3
panic = "abort"
[profile.dev]
debug = false
debug = true
opt-level = 1
[profile.bench]

121
Cranky.toml Normal file
View File

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

View File

@@ -1,4 +1,4 @@
VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' daemon/Cargo.toml | cut -d'"' -f2)
VERSION := $(shell /usr/bin/grep -Pm1 'version = "(\d.\d.\d)"' Cargo.toml | cut -d'"' -f2)
INSTALL = install
INSTALL_PROGRAM = ${INSTALL} -D -m 0755
@@ -15,7 +15,6 @@ BIN_ROG := rog-control-center
BIN_C := asusctl
BIN_D := asusd
BIN_U := asusd-user
BIN_N := asus-notify
LEDCFG := asusd-ledmodes.toml
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
@@ -48,19 +47,20 @@ 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/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
$(INSTALL_DATA) "./data/$(LEDCFG)" "$(DESTDIR)/etc/asusd/$(LEDCFG)"
$(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
$(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
$(INSTALL_DATA) "./data/$(BIN_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_blue.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_blue.png"
$(INSTALL_DATA) "./data/icons/asus_notif_red.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
$(INSTALL_DATA) "./data/icons/asus_notif_orange.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_orange.png"
$(INSTALL_DATA) "./data/icons/asus_notif_white.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_white.png"
$(INSTALL_DATA) "./data/icons/scalable/gpu-compute.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-compute.svg"
$(INSTALL_DATA) "./data/icons/scalable/gpu-hybrid.svg" "$(DESTDIR)$(datarootdir)/icons/hicolor/scalable/status/gpu-hybrid.svg"
@@ -78,12 +78,10 @@ uninstall:
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
rm -f "$(DESTDIR)$(bindir)/$(BIN_N)"
rm -f "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
rm -f "$(DESTDIR)/etc/asusd/$(LEDCFG)"
rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
rm -r "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
@@ -114,5 +112,9 @@ ifeq ($(VENDORED),1)
tar pxf vendor_asusctl_$(VERSION).tar.xz
endif
cargo build $(ARGS)
strip -s ./target/release/$(BIN_C)
strip -s ./target/release/$(BIN_D)
strip -s ./target/release/$(BIN_U)
strip -s ./target/release/$(BIN_ROG)
.PHONY: all clean distclean install uninstall update build

View File

@@ -2,7 +2,7 @@
[![](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/)
**WARNING:** Do not run the main branch of this repo unless you have all the asus-wmi kernel patches. You should use stable kernels + tagged releases.
**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.
`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.
@@ -81,7 +81,16 @@ Requirements are rust >= 1.57 installed from rustup.io if the distro provided ve
**fedora:**
dnf install clang-devel systemd-devel cargo
dnf install cmake clang-devel systemd-devel gtk3-devel cargo
make
sudo make install
**openSUSE:**
Works with KDE Plasma (without GTK packages)
zypper in -t pattern devel_basis
zypper in rustup cmake clang-devel systemd-devel glib2-devel cairo-devel atkmm-devel pangomm-devel gdk-pixbuf-devel gtk3-devel
make
sudo make install

View File

@@ -1,22 +0,0 @@
[package]
name = "asus-notify"
version = "3.1.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zbus = "^2.2"
# serialisation
serde_json = "^1.0"
rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" }
rog_platform = { path = "../rog-platform" }
rog_profiles = { path = "../rog-profiles" }
smol = "^1.2"
[dependencies.notify-rust]
version = "^4.3"
default-features = false
features = ["z"]

View File

@@ -1,166 +0,0 @@
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_aura::AuraEffect;
use rog_dbus::{
zbus_charge::ChargeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy,
zbus_profile::ProfileProxy,
};
use rog_profiles::Profile;
use smol::{future, Executor};
use std::{
error::Error,
sync::{Arc, Mutex},
};
use zbus::export::futures_util::StreamExt;
const NOTIF_HEADER: &str = "ROG Control";
macro_rules! notify {
($notifier:ident, $last_notif:ident, $data:expr) => {
if let Some(notif) = $last_notif.take() {
notif.close();
}
if let Ok(x) = $notifier($data) {
$last_notif.replace(x);
}
};
}
macro_rules! base_notification {
($body:expr) => {
Notification::new()
.summary(NOTIF_HEADER)
.body($body)
.timeout(2000)
.show()
};
}
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("asus-notify version {}", env!("CARGO_PKG_VERSION"));
println!(" rog-dbus version {}", rog_dbus::VERSION);
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
let executor = Executor::new();
// BIOS notif
let x = last_notification.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = RogBiosProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_post_boot_sound().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if let Ok(ref mut lock) = x.try_lock() {
notify!(do_post_sound_notif, lock, &out.sound());
}
}
future::ready(())
})
.await;
};
})
.detach();
// Charge notif
let x = last_notification.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ChargeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_charge().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if let Ok(ref mut lock) = x.try_lock() {
notify!(do_charge_notif, lock, &out.limit);
}
}
future::ready(())
})
.await;
};
})
.detach();
// Profile notif
let x = last_notification.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ProfileProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_profile().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if let Ok(ref mut lock) = x.try_lock() {
notify!(do_thermal_notif, lock, &out.profile);
}
}
future::ready(())
})
.await;
};
})
.detach();
// LED notif
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_led().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if let Ok(ref mut lock) = last_notification.try_lock() {
notify!(do_led_notif, lock, &out.data);
}
}
future::ready(())
})
.await;
};
})
.detach();
loop {
smol::block_on(executor.tick());
}
}
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let x = Notification::new()
.summary("ASUS ROG")
.body(&format!(
"Thermal profile changed to {}",
profile.to_uppercase(),
))
.hint(Hint::Resident(true))
.timeout(2000)
.hint(Hint::Category("device".into()))
//.hint(Hint::Transient(true))
.icon(icon)
.show()?;
Ok(x)
}
fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!(
"Keyboard LED mode changed to {}",
ledmode.mode_name()
))
}
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("Battery charge limit changed to {}", limit))
}
fn do_post_sound_notif(on: &bool) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("BIOS Post sound {}", on))
}

View File

@@ -1,26 +1,24 @@
[package]
name = "asusctl"
version = "4.3.3"
license = "MPL-2.0"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
edition = "2021"
version.workspace = true
[dependencies]
zbus = "^2.2"
rog_anime = { path = "../rog-anime" }
rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" }
rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" }
daemon = { path = "../daemon" }
gumdrop = "^0.8"
toml = "^0.5.8"
sysfs-class = "^0.1.2"
gumdrop.workspace = true
toml.workspace = true
sysfs-class.workspace = true
[dev-dependencies]
tinybmp = "^0.3.3"
glam = "^0.21.2"
gif.workspace = true
tinybmp.workspace = true
glam.workspace = true
rog_dbus = { path = "../rog-dbus" }
gif = "^0.11.2"

View File

@@ -20,7 +20,7 @@ fn main() {
}
for c in (0..35).into_iter().step_by(step) {
for i in matrix.get_mut()[c].iter_mut() {
for i in &mut matrix.get_mut()[c] {
*i = 50;
}
}

View File

@@ -17,7 +17,6 @@ fn main() {
for (y, row) in tmp.iter_mut().enumerate() {
if y % 2 == 0 && i + 1 != row.len() - 1 {
i += 1;
dbg!(i);
}
row[row.len() - i] = 0x22;
if i > 5 {

View File

@@ -35,7 +35,7 @@ fn main() -> Result<(), Box<dyn Error>> {
loop {
matrix.angle += 0.05;
if matrix.angle > PI * 2.0 {
matrix.angle = 0.0
matrix.angle = 0.0;
}
matrix.update();

View File

@@ -6,17 +6,17 @@ use rog_aura::{
KeyColourArray,
};
use rog_dbus::RogDbusClientBlocking;
use std::collections::LinkedList;
use std::collections::VecDeque;
#[derive(Debug, Clone)]
struct Ball {
position: (f32, f32),
direction: (f32, f32),
trail: LinkedList<(f32, f32)>,
trail: VecDeque<(f32, f32)>,
}
impl Ball {
fn new(x: f32, y: f32, trail_len: u32) -> Self {
let mut trail = LinkedList::new();
let mut trail = VecDeque::new();
for _ in 1..=trail_len {
trail.push_back((x, y));
}

View File

@@ -105,7 +105,7 @@ impl ToString for LedBrightness {
Some(0x02) => "high",
_ => "unknown",
};
s.to_string()
s.to_owned()
}
}

View File

@@ -26,30 +26,25 @@ mod aura_cli;
mod cli_opts;
mod profiles_cli;
fn main() -> Result<(), Box<dyn std::error::Error>> {
fn main() {
let args: Vec<String> = args().skip(1).collect();
let parsed: CliStart;
let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k'));
match CliStart::parse_args_default(&args) {
Ok(p) => {
parsed = p;
}
Err(err) if err.to_string() == missing_argument_k.to_string() => {
parsed = CliStart {
kbd_bright: Some(LedBrightness::new(None)),
..Default::default()
};
}
let parsed = match CliStart::parse_args_default(&args) {
Ok(p) => p,
Err(err) if err.to_string() == missing_argument_k.to_string() => CliStart {
kbd_bright: Some(LedBrightness::new(None)),
..Default::default()
},
Err(err) => {
eprintln!("source {}", err);
std::process::exit(2);
}
}
};
let (dbus, _) = RogDbusClientBlocking::new()
.map_err(|e| {
print_error_help(Box::new(e), None);
print_error_help(&e, None);
std::process::exit(3);
})
.unwrap();
@@ -59,7 +54,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.supported()
.supported_functions()
.map_err(|e| {
print_error_help(Box::new(e), None);
print_error_help(&e, None);
std::process::exit(4);
})
.unwrap();
@@ -68,17 +63,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
print_versions();
println!();
print_laptop_info();
return Ok(());
}
if let Err(err) = do_parsed(&parsed, &supported, &dbus) {
print_error_help(err, Some(&supported));
print_error_help(&*err, Some(&supported));
}
Ok(())
}
fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) {
fn print_error_help(err: &dyn std::error::Error, supported: Option<&SupportedFunctions>) {
check_service("asusd");
println!("\nError: {}\n", err);
print_versions();
@@ -131,7 +123,7 @@ fn check_service(name: &str) -> bool {
fn do_parsed(
parsed: &CliStart,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
) -> Result<(), Box<dyn std::error::Error>> {
match &parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(dbus, &supported.keyboard_led, mode)?,
@@ -139,9 +131,9 @@ fn do_parsed(
Some(CliCommand::LedPow2(pow)) => handle_led_power2(dbus, &supported.keyboard_led, pow)?,
Some(CliCommand::Profile(cmd)) => handle_profile(dbus, &supported.platform_profile, cmd)?,
Some(CliCommand::FanCurve(cmd)) => {
handle_fan_curve(dbus, &supported.platform_profile, cmd)?
handle_fan_curve(dbus, &supported.platform_profile, cmd)?;
}
Some(CliCommand::Graphics(_)) => do_gfx()?,
Some(CliCommand::Graphics(_)) => do_gfx(),
Some(CliCommand::Anime(cmd)) => handle_anime(dbus, &supported.anime_ctrl, cmd)?,
Some(CliCommand::Bios(cmd)) => handle_bios_option(dbus, &supported.rog_bios_ctrl, cmd)?,
None => {
@@ -155,7 +147,7 @@ fn do_parsed(
println!("{}", CliStart::usage());
println!();
if let Some(cmdlist) = CliStart::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in commands.iter().filter(|command| {
if !matches!(
supported.keyboard_led.prod_id,
@@ -211,20 +203,21 @@ fn do_parsed(
}
if let Some(chg_limit) = parsed.chg_limit {
dbus.proxies().charge().set_limit(chg_limit)?;
dbus.proxies()
.charge()
.set_charge_control_end_threshold(chg_limit)?;
}
Ok(())
}
fn do_gfx() -> Result<(), Box<dyn std::error::Error>> {
fn do_gfx() {
println!("Please use supergfxctl for graphics switching. supergfxctl is the result of making asusctl graphics switching generic so all laptops can use it");
println!("This command will be removed in future");
Ok(())
}
fn handle_anime(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
_supported: &AnimeSupportedFunctions,
cmd: &AnimeCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -240,14 +233,14 @@ fn handle_anime(
}
}
if let Some(anime_turn) = cmd.enable {
dbus.proxies().anime().set_on_off(anime_turn)?
dbus.proxies().anime().set_on_off(anime_turn)?;
}
if let Some(anime_boot) = cmd.boot_enable {
dbus.proxies().anime().set_boot_on_off(anime_boot)?
dbus.proxies().anime().set_boot_on_off(anime_boot)?;
}
if let Some(bright) = cmd.brightness {
verify_brightness(bright);
dbus.proxies().anime().set_brightness(bright)?
dbus.proxies().anime().set_brightness(bright)?;
}
if cmd.clear {
let anime_type = get_anime_type()?;
@@ -374,7 +367,7 @@ fn handle_anime(
}
fn verify_brightness(brightness: f32) {
if brightness < 0.0 || brightness > 1.0 {
if !(0.0..=1.0).contains(&brightness) {
println!(
"Image and global brightness must be between 0.0 and 1.0 (inclusive), was {}",
brightness
@@ -384,7 +377,7 @@ fn verify_brightness(brightness: f32) {
}
fn handle_led_mode(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions,
mode: &LedModeCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -441,7 +434,7 @@ fn handle_led_mode(
}
fn handle_led_power1(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -472,11 +465,11 @@ fn handle_led_power1(
}
println!("These options are for keyboards of product ID 0x1866 or TUF only");
return Ok(());
Ok(())
}
fn handle_led_power_1_do_1866(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDev1866> = Vec::new();
@@ -516,7 +509,7 @@ fn handle_led_power_1_do_1866(
}
fn handle_led_power_1_do_tuf(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
let mut enabled: Vec<AuraDevTuf> = Vec::new();
@@ -542,7 +535,6 @@ fn handle_led_power_1_do_tuf(
x19b6: vec![],
tuf: enabled,
};
dbg!(&data);
dbus.proxies().led().set_leds_power(data, true)?;
let data = AuraPowerDev {
@@ -556,7 +548,7 @@ fn handle_led_power_1_do_tuf(
}
fn handle_led_power2(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
supported: &LedSupportedFunctions,
power: &LedPowerCommand2,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -568,8 +560,8 @@ fn handle_led_power2(
println!("Commands available");
if let Some(cmdlist) = LedPowerCommand2::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_string()).collect();
for command in commands.iter() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
for command in &commands {
println!("{}", command);
}
}
@@ -585,7 +577,7 @@ fn handle_led_power2(
}
if supported.prod_id != AuraDevice::X19B6 {
println!("This option applies only to keyboards with product ID 0x19b6")
println!("This option applies only to keyboards with product ID 0x19b6");
}
let mut enabled: Vec<AuraDev19b6> = Vec::new();
@@ -650,7 +642,7 @@ fn handle_led_power2(
}
fn handle_profile(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
supported: &PlatformProfileFunctions,
cmd: &ProfileCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -679,7 +671,9 @@ fn handle_profile(
if cmd.list {
let res = dbus.proxies().profile().profiles()?;
res.iter().for_each(|p| println!("{:?}", p));
for p in &res {
println!("{:?}", p);
}
}
if cmd.profile_get {
@@ -691,7 +685,7 @@ fn handle_profile(
}
fn handle_fan_curve(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
supported: &PlatformProfileFunctions,
cmd: &FanCurveCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -753,7 +747,7 @@ fn handle_fan_curve(
}
fn handle_bios_option(
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
supported: &RogBiosSupportedFunctions,
cmd: &BiosCommand,
) -> Result<(), Box<dyn std::error::Error>> {
@@ -768,10 +762,7 @@ fn handle_bios_option(
{
println!("Missing arg or command\n");
let usage: Vec<String> = BiosCommand::usage()
.lines()
.map(|s| s.to_string())
.collect();
let usage: Vec<String> = BiosCommand::usage().lines().map(|s| s.to_owned()).collect();
for line in usage.iter().filter(|line| {
line.contains("sound") && supported.post_sound
@@ -803,10 +794,10 @@ fn handle_bios_option(
}
if let Some(opt) = cmd.panel_overdrive_set {
dbus.proxies().rog_bios().set_panel_overdrive(opt)?;
dbus.proxies().rog_bios().set_panel_od(opt)?;
}
if cmd.panel_overdrive_get {
let res = dbus.proxies().rog_bios().panel_overdrive()?;
let res = dbus.proxies().rog_bios().panel_od()?;
println!("Panel overdrive on: {}", res);
}
}

View File

@@ -1,8 +1,9 @@
[package]
name = "daemon-user"
version = "1.3.1"
license = "MPL-2.0"
version.workspace = true
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
edition = "2021"
description = "Usermode daemon for user settings, anime, per-key lighting"
[lib]
@@ -14,20 +15,17 @@ name = "asusd-user"
path = "src/daemon.rs"
[dependencies]
dirs.workspace = true
smol.workspace = true
# serialisation
serde = "^1.0"
serde_json = "^1.0"
serde_derive = "^1.0"
serde.workspace = true
serde_json.workspace = true
serde_derive.workspace = true
rog_anime = { path = "../rog-anime" }
rog_aura = { path = "../rog-aura" }
rog_dbus = { path = "../rog-dbus" }
rog_platform = { path = "../rog-platform" }
dirs = "^4.0"
zbus = "^2.2"
zvariant = "^3.0"
zvariant_derive = "^3.0"
smol = "^1.2"
zbus.workspace = true

View File

@@ -11,9 +11,10 @@ use std::{
},
};
use std::{sync::Arc, thread::sleep, time::Instant};
use zbus::dbus_interface;
use zvariant::ObjectPath;
use zvariant_derive::Type;
use zbus::{
dbus_interface,
zvariant::{ObjectPath, Type},
};
use crate::user_config::ConfigLoadSave;
use crate::{error::Error, user_config::UserAnimeConfig};
@@ -102,7 +103,7 @@ impl<'a> CtrlAnimeInner<'static> {
.write(output)
.map_err(|e| AnimeError::Dbus(format!("{}", e)))
.map(|_| false)
})?;
});
}
ActionData::Image(image) => {
self.client
@@ -123,10 +124,10 @@ impl<'a> CtrlAnimeInner<'static> {
sleep(Duration::from_millis(1));
}
}
ActionData::AudioEq => {}
ActionData::SystemInfo => {}
ActionData::TimeDate => {}
ActionData::Matrix => {}
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
@@ -142,7 +143,7 @@ pub struct CtrlAnime<'a> {
inner_early_return: Arc<AtomicBool>,
}
impl<'a> CtrlAnime<'static> {
impl CtrlAnime<'static> {
pub fn new(
config: Arc<Mutex<UserAnimeConfig>>,
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
@@ -184,7 +185,7 @@ impl CtrlAnime<'static> {
pub fn insert_asus_gif(
&mut self,
index: u32,
file: String,
file: &str,
time: Timer,
brightness: f32,
) -> zbus::fdo::Result<String> {
@@ -222,7 +223,7 @@ impl CtrlAnime<'static> {
pub fn insert_image_gif(
&mut self,
index: u32,
file: String,
file: &str,
scale: f32,
angle: f32,
xy: (f32, f32),
@@ -268,7 +269,7 @@ impl CtrlAnime<'static> {
pub fn insert_image(
&mut self,
index: u32,
file: String,
file: &str,
scale: f32,
angle: f32,
xy: (f32, f32),

View File

@@ -86,7 +86,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.map_err(|e| {
println!("{BOARD_NAME}, {e}");
})
.unwrap_or(KeyLayout::ga401_layout());
.unwrap_or_else(|_| KeyLayout::ga401_layout());
executor
.spawn(async move {

View File

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

View File

@@ -30,7 +30,7 @@ pub trait ConfigLoadSave<T: DeserializeOwned + serde::Serialize> {
if !path.exists() {
create_dir(path.clone())?;
}
let name = self.name().clone();
let name = self.name();
path.push(name + ".cfg");
let mut file = OpenOptions::new()
@@ -114,7 +114,7 @@ impl ConfigLoadSave<UserAnimeConfig> for UserAnimeConfig {
impl Default for UserAnimeConfig {
fn default() -> Self {
Self {
name: "default".to_string(),
name: "default".to_owned(),
anime: vec![
ActionLoader::AsusImage {
file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(),
@@ -230,10 +230,10 @@ impl Default for UserAuraConfig {
80,
40,
));
seq.push(key.clone());
seq.push(key);
Self {
name: "default".to_string(),
name: "default".to_owned(),
aura: seq,
}
}
@@ -251,8 +251,8 @@ pub struct UserConfig {
impl UserConfig {
pub fn new() -> Self {
Self {
active_anime: Some("anime-default".to_string()),
active_aura: Some("aura-default".to_string()),
active_anime: Some("anime-default".to_owned()),
active_aura: Some("aura-default".to_owned()),
}
}

View File

@@ -1,13 +1,13 @@
[package]
name = "daemon"
version = "4.4.0"
license = "MPL-2.0"
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 = "2018"
edition = "2021"
[lib]
name = "daemon"
@@ -24,22 +24,23 @@ rog_platform = { path = "../rog-platform" }
rog_profiles = { path = "../rog-profiles" }
rog_dbus = { path = "../rog-dbus" }
async-trait = "^0.1"
smol = "^1.2"
async-trait.workspace = true
tokio.workspace = true
# cli and logging
log = "^0.4"
env_logger = "^0.9"
log.workspace = true
env_logger.workspace = true
zbus = "^2.2"
zvariant = "^3.2"
logind-zbus = { version = "^3.0" } #, default-features = false, features = ["non_blocking"] }
zbus.workspace = true
logind-zbus.workspace = true
# serialisation
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
toml = "^0.5.8"
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
toml.workspace = true
# Device control
sysfs-class = "^0.1.2" # used for backlight control and baord ID
sysfs-class.workspace = true # used for backlight control and baord ID
concat-idents.workspace = true

View File

@@ -42,7 +42,7 @@ impl Config {
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
CONFIG_PATH, CONFIG_PATH
);
let cfg_old = CONFIG_PATH.to_string() + "-old";
let cfg_old = CONFIG_PATH.to_owned() + "-old";
std::fs::rename(CONFIG_PATH, cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",
@@ -52,7 +52,7 @@ impl Config {
config = Self::new();
}
} else {
config = Self::new()
config = Self::new();
}
config.write();
config
@@ -61,7 +61,7 @@ impl Config {
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(&CONFIG_PATH)
.open(CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {

View File

@@ -86,25 +86,25 @@ impl AnimeConfigCached {
anime_type: AnimeType,
) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len());
for ani in config.system.iter() {
for ani in &config.system {
sys.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.system = sys;
let mut boot = Vec::with_capacity(config.boot.len());
for ani in config.boot.iter() {
for ani in &config.boot {
boot.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.boot = boot;
let mut wake = Vec::with_capacity(config.wake.len());
for ani in config.wake.iter() {
for ani in &config.wake {
wake.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.wake = wake;
let mut shutdown = Vec::with_capacity(config.shutdown.len());
for ani in config.shutdown.iter() {
for ani in &config.shutdown {
shutdown.push(ActionData::from_anime_action(anime_type, ani)?);
}
self.shutdown = shutdown;
@@ -145,7 +145,7 @@ impl AnimeConfig {
.read(true)
.write(true)
.create(true)
.open(&ANIME_CONFIG_PATH)
.open(ANIME_CONFIG_PATH)
.unwrap_or_else(|_| {
panic!(
"The file {} or directory /etc/asusd/ is missing",
@@ -249,7 +249,7 @@ impl AnimeConfig {
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(&ANIME_CONFIG_PATH)
.open(ANIME_CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", ANIME_CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {

View File

@@ -1,29 +1,19 @@
pub mod config;
pub mod zbus;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use async_trait::async_trait;
use self::config::{AnimeConfig, AnimeConfigCached};
use crate::{error::RogError, GetSupported};
use ::zbus::export::futures_util::lock::Mutex;
use log::{error, info, warn};
use rog_anime::{
error::AnimeError,
usb::{
get_anime_type, pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on,
pkts_for_init,
},
usb::{get_anime_type, pkt_for_flush, pkts_for_init},
ActionData, AnimeDataBuffer, AnimePacketType, AnimeType,
};
use rog_platform::{hid_raw::HidRaw, supported::AnimeSupportedFunctions, usb_raw::USBRaw};
use smol::Executor;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{
convert::TryFrom,
error::Error,
sync::{Arc, Mutex, MutexGuard},
thread::sleep,
};
use crate::{error::RogError, GetSupported};
use self::config::{AnimeConfig, AnimeConfigCached};
use std::{convert::TryFrom, error::Error, sync::Arc, thread::sleep};
impl GetSupported for CtrlAnime {
type A = AnimeSupportedFunctions;
@@ -72,7 +62,7 @@ impl CtrlAnime {
/// one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display only) *must*
/// get the mutex lock and set the thread_exit atomic.
/// get the mutex lock and set the `thread_exit` atomic.
fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
@@ -85,6 +75,7 @@ impl CtrlAnime {
// The only reason for this outer thread is to prevent blocking while waiting for the
// next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main thread)
std::thread::Builder::new()
.name("AniMe system thread start".into())
.spawn(move || {
@@ -95,7 +86,7 @@ impl CtrlAnime {
let thread_running;
let anime_type;
loop {
if let Ok(lock) = inner.try_lock() {
if let Some(lock) = inner.try_lock() {
thread_exit = lock.thread_exit.clone();
thread_running = lock.thread_running.clone();
anime_type = lock.anime_type;
@@ -115,13 +106,13 @@ impl CtrlAnime {
'main: loop {
thread_running.store(true, Ordering::SeqCst);
for action in actions.iter() {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
if let Err(err) = rog_anime::run_animation(frames, &|frame| {
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: frame-loop was asked to exit");
return Ok(true); // Do safe exit
@@ -139,28 +130,28 @@ impl CtrlAnime {
.ok();
false // Don't exit yet
})
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
AnimeError::NoFrames
})
}) {
warn!("rog_anime::run_animation:Animation {}", err);
break 'main;
};
.map_or_else(
|| {
warn!("rog_anime::run_animation:callback failed");
Err(AnimeError::NoFrames)
},
Ok,
)
});
}
ActionData::Image(image) => {
once = false;
if let Ok(lock) = inner.try_lock() {
if let Some(lock) = inner.try_lock() {
lock.write_data_buffer(image.as_ref().clone())
.map_err(|e| error!("{}", e))
.ok();
}
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq => {}
ActionData::SystemInfo => {}
ActionData::TimeDate => {}
ActionData::Matrix => {}
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
@@ -171,7 +162,7 @@ impl CtrlAnime {
}
}
// Clear the display on exit
if let Ok(lock) = inner.try_lock() {
if let Some(lock) = inner.try_lock() {
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
@@ -202,7 +193,7 @@ impl CtrlAnime {
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in data.iter() {
for row in &data {
self.node.write_bytes(row)?;
}
self.node.write_bytes(&pkt_for_flush())?;
@@ -216,85 +207,3 @@ impl CtrlAnime {
Ok(())
}
}
pub struct CtrlAnimeTask {
inner: Arc<Mutex<CtrlAnime>>,
}
impl CtrlAnimeTask {
pub async fn new(inner: Arc<Mutex<CtrlAnime>>) -> CtrlAnimeTask {
Self { inner }
}
}
#[async_trait]
impl crate::CtrlTask for CtrlAnimeTask {
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
let run_action =
|start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
if start {
info!("CtrlAnimeTask running sleep animation");
CtrlAnime::run_thread(inner.clone(), lock.cache.shutdown.clone(), true);
} else {
info!("CtrlAnimeTask running wake animation");
CtrlAnime::run_thread(inner.clone(), lock.cache.wake.clone(), true);
}
};
let inner1 = self.inner.clone();
let inner2 = self.inner.clone();
let inner3 = self.inner.clone();
let inner4 = self.inner.clone();
self.create_sys_event_tasks(
executor,
// Loop is required to try an attempt to get the mutex *without* blocking
// other threads - it is possible to end up with deadlocks otherwise.
move || loop {
if let Ok(lock) = inner1.clone().try_lock() {
run_action(true, lock, inner1.clone());
break;
}
},
move || loop {
if let Ok(lock) = inner2.clone().try_lock() {
run_action(false, lock, inner2.clone());
break;
}
},
move || loop {
if let Ok(lock) = inner3.clone().try_lock() {
run_action(true, lock, inner3.clone());
break;
}
},
move || loop {
if let Ok(lock) = inner4.clone().try_lock() {
run_action(false, lock, inner4.clone());
break;
}
},
)
.await;
Ok(())
}
}
pub struct CtrlAnimeReloader(pub Arc<Mutex<CtrlAnime>>);
impl crate::Reloadable for CtrlAnimeReloader {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(lock) = self.0.try_lock() {
lock.node
.write_bytes(&pkt_for_set_on(lock.config.awake_enabled))?;
lock.node.write_bytes(&pkt_for_apply())?;
lock.node
.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled))?;
lock.node.write_bytes(&pkt_for_apply())?;
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true);
}
Ok(())
}
}

View File

@@ -0,0 +1,216 @@
use super::CtrlAnime;
use crate::error::RogError;
use async_trait::async_trait;
use log::{info, warn};
use rog_anime::{
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
AnimeDataBuffer, AnimePowerStates,
};
use std::sync::{atomic::Ordering, Arc};
use zbus::{
dbus_interface,
export::futures_util::lock::{Mutex, MutexGuard},
Connection, SignalContext,
};
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime";
#[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;
}
}
// 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")]
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
})?;
Ok(())
}
/// Set the global AniMe brightness
async fn set_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 whether the AniMe is displaying images/data
async fn set_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, status: bool) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_for_set_on(status))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
lock.config.awake_enabled = status;
lock.config.write();
Self::notify_power_states(
&ctxt,
AnimePowerStates {
brightness: lock.config.brightness.floor() as u8,
enabled: lock.config.awake_enabled,
boot_anim_enabled: lock.config.boot_anim_enabled,
},
)
.await
.ok();
}
/// Set whether the AniMe will show boot, suspend, or off animations
async fn set_boot_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, on: bool) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_for_set_boot(on))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
lock.node
.write_bytes(&pkt_for_apply())
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
lock.config.boot_anim_enabled = on;
lock.config.write();
Self::notify_power_states(
&ctxt,
AnimePowerStates {
brightness: lock.config.brightness.floor() as u8,
enabled: lock.config.awake_enabled,
boot_anim_enabled: lock.config.boot_anim_enabled,
},
)
.await
.ok();
}
/// 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);
}
}
/// Get status of if the AniMe LEDs are on/displaying while system is awake
#[dbus_interface(property)]
async fn awake_enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.awake_enabled
}
/// Get the status of if factory system-status animations are enabled
#[dbus_interface(property)]
async fn boot_enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.boot_anim_enabled
}
/// Notify listeners of the status of AniMe LED power and factory system-status animations
#[dbus_interface(signal)]
async fn notify_power_states(
ctxt: &SignalContext<'_>,
data: AnimePowerStates,
) -> zbus::Result<()>;
}
#[async_trait]
impl crate::CtrlTask for CtrlAnimeZbus {
fn zbus_path() -> &'static str {
ZBUS_PATH
}
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let run_action =
|start: bool, lock: MutexGuard<'_, CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
if start {
info!("CtrlAnimeTask running sleep animation");
CtrlAnime::run_thread(inner, lock.cache.shutdown.clone(), true);
} else {
info!("CtrlAnimeTask running wake animation");
CtrlAnime::run_thread(inner, lock.cache.wake.clone(), true);
}
};
let inner1 = self.0.clone();
let inner2 = self.0.clone();
let inner3 = self.0.clone();
let inner4 = self.0.clone();
self.create_sys_event_tasks(
// Loop is required to try an attempt to get the mutex *without* blocking
// other threads - it is possible to end up with deadlocks otherwise.
move || loop {
if let Some(lock) = inner1.try_lock() {
run_action(true, lock, inner1.clone());
break;
}
},
move || loop {
if let Some(lock) = inner2.try_lock() {
run_action(false, lock, inner2.clone());
break;
}
},
move || loop {
if let Some(lock) = inner3.try_lock() {
run_action(true, lock, inner3.clone());
break;
}
},
move || loop {
if let Some(lock) = inner4.try_lock() {
run_action(false, lock, inner4.clone());
break;
}
},
)
.await;
Ok(())
}
}
#[async_trait]
impl crate::Reloadable for CtrlAnimeZbus {
async fn reload(&mut self) -> Result<(), RogError> {
if let Some(lock) = self.0.try_lock() {
lock.node
.write_bytes(&pkt_for_set_on(lock.config.awake_enabled))?;
lock.node.write_bytes(&pkt_for_apply())?;
lock.node
.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled))?;
lock.node.write_bytes(&pkt_for_apply())?;
let action = lock.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true);
}
Ok(())
}
}

View File

@@ -1,160 +0,0 @@
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use log::warn;
use rog_anime::{
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
AnimeDataBuffer, AnimePowerStates,
};
use zbus::{dbus_interface, Connection, SignalContext};
use std::sync::atomic::Ordering;
use super::CtrlAnime;
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
/// The struct with the main dbus methods requires this trait
#[async_trait]
impl crate::ZbusAdd for CtrlAnimeZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/Anime", 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")]
impl CtrlAnimeZbus {
/// Writes a data stream of length. Will force system thread to exit until it is restarted
fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
'outer: loop {
if let Ok(lock) = self.0.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst);
lock.write_data_buffer(input).map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
err
})?;
break 'outer;
}
}
Ok(())
}
/// Set the global AniMe brightness
fn set_brightness(&self, bright: f32) {
'outer: loop {
if let Ok(mut lock) = self.0.try_lock() {
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();
break 'outer;
}
}
}
/// Set whether the AniMe is displaying images/data
async fn set_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, status: bool) {
let states;
'outer: loop {
if let Ok(mut lock) = self.0.try_lock() {
lock.node
.write_bytes(&pkt_for_set_on(status))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
lock.config.awake_enabled = status;
lock.config.write();
states = Some(AnimePowerStates {
brightness: lock.config.brightness.floor() as u8,
enabled: lock.config.awake_enabled,
boot_anim_enabled: lock.config.boot_anim_enabled,
});
break 'outer;
}
}
if let Some(state) = states {
Self::notify_power_states(&ctxt, state).await.ok();
}
}
/// Set whether the AniMe will show boot, suspend, or off animations
async fn set_boot_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, on: bool) {
let states;
'outer: loop {
if let Ok(mut lock) = self.0.try_lock() {
lock.node
.write_bytes(&pkt_for_set_boot(on))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
lock.node
.write_bytes(&pkt_for_apply())
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
lock.config.boot_anim_enabled = on;
lock.config.write();
states = Some(AnimePowerStates {
brightness: lock.config.brightness.floor() as u8,
enabled: lock.config.awake_enabled,
boot_anim_enabled: lock.config.boot_anim_enabled,
});
break 'outer;
}
}
if let Some(state) = states {
Self::notify_power_states(&ctxt, state).await.ok();
}
}
/// The main loop is the base system set action if the user isn't running
/// the user daemon
fn run_main_loop(&self, start: bool) {
if start {
'outer: loop {
if let Ok(lock) = self.0.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false);
break 'outer;
}
}
}
}
/// Get status of if the AniMe LEDs are on/displaying while system is awake
#[dbus_interface(property)]
fn awake_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.awake_enabled;
}
true
}
/// Get the status of if factory system-status animations are enabled
#[dbus_interface(property)]
fn boot_enabled(&self) -> bool {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.boot_anim_enabled;
}
true
}
/// Notify listeners of the status of AniMe LED power and factory system-status animations
#[dbus_interface(signal)]
async fn notify_power_states(
ctxt: &SignalContext<'_>,
data: AnimePowerStates,
) -> zbus::Result<()>;
}

View File

@@ -27,11 +27,11 @@ impl AuraPowerConfig {
match control {
AuraPowerConfig::AuraDevTuf(_) => [0, 0, 0],
AuraPowerConfig::AuraDev1866(c) => {
let c: Vec<AuraDev1866> = c.iter().map(|v| *v).collect();
let c: Vec<AuraDev1866> = c.iter().copied().collect();
AuraDev1866::to_bytes(&c)
}
AuraPowerConfig::AuraDev19b6(c) => {
let c: Vec<AuraDev19b6> = c.iter().map(|v| *v).collect();
let c: Vec<AuraDev19b6> = c.iter().copied().collect();
AuraDev19b6::to_bytes(&c)
}
}
@@ -95,26 +95,26 @@ impl From<&AuraPowerConfig> for AuraPowerDev {
fn from(config: &AuraPowerConfig) -> Self {
match config {
AuraPowerConfig::AuraDevTuf(d) => AuraPowerDev {
tuf: d.iter().map(|o| *o).collect(),
tuf: d.iter().copied().collect(),
x1866: vec![],
x19b6: vec![],
},
AuraPowerConfig::AuraDev1866(d) => AuraPowerDev {
tuf: vec![],
x1866: d.iter().map(|o| *o).collect(),
x1866: d.iter().copied().collect(),
x19b6: vec![],
},
AuraPowerConfig::AuraDev19b6(d) => AuraPowerDev {
tuf: vec![],
x1866: vec![],
x19b6: d.iter().map(|o| *o).collect(),
x19b6: d.iter().copied().collect(),
},
}
}
}
#[derive(Deserialize, Serialize)]
#[serde(default)]
// #[serde(default)]
pub struct AuraConfig {
pub brightness: LedBrightness,
pub current_mode: AuraModeNum,
@@ -127,8 +127,8 @@ pub struct AuraConfig {
impl Default for AuraConfig {
fn default() -> Self {
let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if let Ok(_) = HidRaw::new(prod) {
for prod in &ASUS_KEYBOARD_DEVICES {
if HidRaw::new(prod).is_ok() {
prod_id = AuraDevice::from(*prod);
break;
}
@@ -192,7 +192,7 @@ impl AuraConfig {
.read(true)
.write(true)
.create(true)
.open(&AURA_CONFIG_PATH)
.open(AURA_CONFIG_PATH)
.unwrap_or_else(|_| {
panic!(
"The file {} or directory /etc/asusd/ is missing",
@@ -242,7 +242,7 @@ impl AuraConfig {
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
})
});
}
if let Some(m) = config.multizone.as_mut() {
m.insert(*n, default);
@@ -264,14 +264,14 @@ impl AuraConfig {
pub fn read(&mut self) {
let mut file = OpenOptions::new()
.read(true)
.open(&AURA_CONFIG_PATH)
.open(AURA_CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err));
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
warn!("File is empty {}", AURA_CONFIG_PATH);
} else {
let x: AuraConfig = serde_json::from_str(&buf)
let x = serde_json::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH));
*self = x;
}
@@ -287,34 +287,31 @@ impl AuraConfig {
/// Set the mode data, current mode, and if multizone enabled.
///
/// Multipurpose, will accept AuraEffect with zones and put in the correct store.
/// Multipurpose, will accept `AuraEffect` with zones and put in the correct store.
pub fn set_builtin(&mut self, effect: AuraEffect) {
self.current_mode = effect.mode;
match effect.zone() {
AuraZone::None => {
self.builtins.insert(*effect.mode(), effect);
self.multizone_on = false;
}
_ => {
if let Some(multi) = self.multizone.as_mut() {
if let Some(fx) = multi.get_mut(effect.mode()) {
for fx in fx.iter_mut() {
if fx.zone == effect.zone {
*fx = effect;
return;
}
if effect.zone() == AuraZone::None {
self.builtins.insert(*effect.mode(), effect);
self.multizone_on = false;
} else {
if let Some(multi) = self.multizone.as_mut() {
if let Some(fx) = multi.get_mut(effect.mode()) {
for fx in fx.iter_mut() {
if fx.zone == effect.zone {
*fx = effect;
return;
}
fx.push(effect);
} else {
multi.insert(*effect.mode(), vec![effect]);
}
fx.push(effect);
} else {
let mut tmp = BTreeMap::new();
tmp.insert(*effect.mode(), vec![effect]);
self.multizone = Some(tmp);
multi.insert(*effect.mode(), vec![effect]);
}
self.multizone_on = true;
} else {
let mut tmp = BTreeMap::new();
tmp.insert(*effect.mode(), vec![effect]);
self.multizone = Some(tmp);
}
self.multizone_on = true;
}
}
@@ -335,26 +332,34 @@ mod tests {
fn set_multizone_4key_config() {
let mut config = AuraConfig::default();
let mut effect = AuraEffect::default();
effect.colour1 = Colour(0xff, 0x00, 0xff);
effect.zone = AuraZone::Key1;
let effect = AuraEffect {
colour1: Colour(0xff, 0x00, 0xff),
zone: AuraZone::Key1,
..Default::default()
};
config.set_builtin(effect);
assert!(config.multizone.is_some());
let mut effect = AuraEffect::default();
effect.colour1 = Colour(0x00, 0xff, 0xff);
effect.zone = AuraZone::Key2;
let effect = AuraEffect {
colour1: Colour(0x00, 0xff, 0xff),
zone: AuraZone::Key2,
..Default::default()
};
config.set_builtin(effect);
let mut effect = AuraEffect::default();
effect.colour1 = Colour(0xff, 0xff, 0x00);
effect.zone = AuraZone::Key3;
let effect = AuraEffect {
colour1: Colour(0xff, 0xff, 0x00),
zone: AuraZone::Key3,
..Default::default()
};
config.set_builtin(effect);
let mut effect = AuraEffect::default();
effect.colour1 = Colour(0x00, 0xff, 0x00);
effect.zone = AuraZone::Key4;
let effect = AuraEffect {
colour1: Colour(0x00, 0xff, 0x00),
zone: AuraZone::Key4,
..Default::default()
};
let effect_clone = effect.clone();
config.set_builtin(effect);
// This should replace existing
@@ -373,25 +378,33 @@ mod tests {
fn set_multizone_multimode_config() {
let mut config = AuraConfig::default();
let mut effect = AuraEffect::default();
effect.zone = AuraZone::Key1;
let effect = AuraEffect {
zone: AuraZone::Key1,
..Default::default()
};
config.set_builtin(effect);
assert!(config.multizone.is_some());
let mut effect = AuraEffect::default();
effect.zone = AuraZone::Key2;
effect.mode = AuraModeNum::Breathe;
let effect = AuraEffect {
zone: AuraZone::Key2,
mode: AuraModeNum::Breathe,
..Default::default()
};
config.set_builtin(effect);
let mut effect = AuraEffect::default();
effect.zone = AuraZone::Key3;
effect.mode = AuraModeNum::Comet;
let effect = AuraEffect {
zone: AuraZone::Key3,
mode: AuraModeNum::Comet,
..Default::default()
};
config.set_builtin(effect);
let mut effect = AuraEffect::default();
effect.zone = AuraZone::Key4;
effect.mode = AuraModeNum::Pulse;
let effect = AuraEffect {
zone: AuraZone::Key4,
mode: AuraModeNum::Pulse,
..Default::default()
};
config.set_builtin(effect);
let res = config.multizone.unwrap();

View File

@@ -1,21 +1,15 @@
use crate::{
error::RogError,
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
CtrlTask,
};
use async_trait::async_trait;
use log::{error, info, warn};
use log::{info, warn};
use rog_aura::{
usb::{AuraDevice, LED_APPLY, LED_SET},
AuraEffect, KeyColourArray, LedBrightness, PerKeyRaw, LED_MSG_LEN,
};
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
use rog_platform::{hid_raw::HidRaw, keyboard_led::KeyboardLed, supported::LedSupportedFunctions};
use smol::Executor;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use crate::GetSupported;
@@ -32,8 +26,8 @@ impl GetSupported for CtrlKbdLed {
let per_key_led_mode = laptop.per_key;
let mut prod_id = AuraDevice::Unknown;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
if let Ok(_) = HidRaw::new(prod) {
for prod in &ASUS_KEYBOARD_DEVICES {
if HidRaw::new(prod).is_ok() {
prod_id = AuraDevice::from(*prod);
break;
}
@@ -56,7 +50,7 @@ impl GetSupported for CtrlKbdLed {
}
}
#[derive(Debug, PartialEq, PartialOrd)]
#[derive(Debug, PartialEq, Eq, PartialOrd)]
pub enum LEDNode {
KbdLed(KeyboardLed),
Rog(HidRaw),
@@ -74,112 +68,14 @@ pub struct CtrlKbdLed {
pub config: AuraConfig,
}
pub struct CtrlKbdLedTask {
inner: Arc<Mutex<CtrlKbdLed>>,
}
impl CtrlKbdLedTask {
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
Self { inner }
}
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let bright = lock.kd_brightness.get_brightness()?;
lock.config.read();
lock.config.brightness = (bright as u32).into();
lock.config.write();
return Ok(());
}
}
#[async_trait]
impl CtrlTask for CtrlKbdLedTask {
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
let 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 inner1 = self.inner.clone();
let inner2 = self.inner.clone();
let inner3 = self.inner.clone();
let inner4 = self.inner.clone();
self.create_sys_event_tasks(
executor,
// Loop so that we do aquire the lock but also don't block other
// threads (prevents potential deadlocks)
move || loop {
if let Ok(lock) = inner1.clone().try_lock() {
load_save(true, lock);
break;
}
},
move || loop {
if let Ok(lock) = inner2.clone().try_lock() {
load_save(false, lock);
break;
}
},
move || loop {
if let Ok(lock) = inner3.clone().try_lock() {
load_save(false, lock);
break;
}
},
move || loop {
if let Ok(lock) = inner4.clone().try_lock() {
load_save(false, lock);
break;
}
},
)
.await;
Ok(())
}
}
pub struct CtrlKbdLedReloader(pub Arc<Mutex<CtrlKbdLed>>);
impl crate::Reloadable for CtrlKbdLedReloader {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.write_current_config_mode()?;
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
}
Ok(())
}
}
pub struct CtrlKbdLedZbus(pub Arc<Mutex<CtrlKbdLed>>);
impl CtrlKbdLedZbus {
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
Self(inner)
}
}
impl CtrlKbdLed {
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
let mut led_prod = None;
let mut led_node = None;
for prod in ASUS_KEYBOARD_DEVICES.iter() {
for prod in &ASUS_KEYBOARD_DEVICES {
match HidRaw::new(prod) {
Ok(node) => {
led_prod = Some(prod.to_string());
led_prod = Some((*prod).to_owned());
led_node = Some(node);
info!("Looked for keyboard controller 0x{prod}: Found");
break;
@@ -225,13 +121,13 @@ impl CtrlKbdLed {
pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
self.kd_brightness
.get_brightness()
.map_err(|e| RogError::Platform(e))
.map_err(RogError::Platform)
}
pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> {
self.kd_brightness
.set_brightness(brightness as u8)
.map_err(|e| RogError::Platform(e))
.map_err(RogError::Platform)
}
pub fn next_brightness(&mut self) -> Result<(), RogError> {
@@ -280,10 +176,9 @@ impl CtrlKbdLed {
/// 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.standard.contains(&effect.mode) {
return Err(RogError::AuraEffectNotSupported);
} else if effect.zone != AuraZone::None
&& !self.supported_modes.multizone.contains(&effect.zone)
if !self.supported_modes.standard.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !self.supported_modes.multizone.contains(&effect.zone)
{
return Err(RogError::AuraEffectNotSupported);
}
@@ -393,7 +288,7 @@ impl CtrlKbdLed {
Ok(())
}
fn write_current_config_mode(&mut self) -> Result<(), RogError> {
pub(super) fn write_current_config_mode(&mut self) -> Result<(), RogError> {
if self.config.multizone_on {
let mode = self.config.current_mode;
let mut create = false;
@@ -440,7 +335,7 @@ impl CtrlKbdLed {
colour2: *GRADIENT.get(GRADIENT.len() - i).unwrap_or(&GRADIENT[6]),
speed: Speed::Med,
direction: Direction::Left,
})
});
}
if default.is_empty() {
return Err(RogError::AuraEffectNotSupported);
@@ -475,7 +370,7 @@ mod tests {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: "".into(),
prod_family: String::new(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![],
@@ -491,9 +386,11 @@ mod tests {
config,
};
let mut effect = AuraEffect::default();
effect.colour1 = Colour(0xff, 0x00, 0xff);
effect.zone = AuraZone::None;
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!(
@@ -525,10 +422,7 @@ mod tests {
controller.supported_modes.multizone.push(AuraZone::Key2);
assert_eq!(
controller
.set_effect(effect.clone())
.unwrap_err()
.to_string(),
controller.set_effect(effect).unwrap_err().to_string(),
"No supported Aura keyboard"
);
}
@@ -538,7 +432,7 @@ mod tests {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: "".into(),
prod_family: String::new(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![],
@@ -576,7 +470,7 @@ mod tests {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::default();
let supported_modes = LaptopLedData {
prod_family: "".into(),
prod_family: String::new(),
board_names: vec![],
standard: vec![AuraModeNum::Static],
multizone: vec![AuraZone::Key1, AuraZone::Key2],

View File

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

View File

@@ -0,0 +1,320 @@
use async_trait::async_trait;
use log::{error, info, warn};
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness, PerKeyRaw};
use std::{collections::BTreeMap, sync::Arc};
use zbus::{
dbus_interface,
export::futures_util::{
lock::{Mutex, MutexGuard},
StreamExt,
},
Connection, SignalContext,
};
use crate::{error::RogError, CtrlTask};
use super::controller::CtrlKbdLed;
pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Aura";
#[derive(Clone)]
pub struct CtrlKbdLedZbus(pub Arc<Mutex<CtrlKbdLed>>);
impl CtrlKbdLedZbus {
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let bright = lock.kd_brightness.get_brightness()?;
lock.config.read();
lock.config.brightness = (bright as u32).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
///
/// 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) {
let ctrl = self.0.lock().await;
ctrl.set_brightness(brightness)
.map_err(|err| warn!("{}", err))
.ok();
}
/// 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,
/// }
/// ```
async fn set_leds_power(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
options: AuraPowerDev,
enabled: bool,
) -> zbus::fdo::Result<()> {
let mut ctrl = self.0.lock().await;
for p in options.tuf {
ctrl.config.enabled.set_tuf(p, enabled);
}
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| {
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
})?;
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()
}
async fn per_key_raw(&self, data: PerKeyRaw) -> zbus::fdo::Result<()> {
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 {
fn zbus_path() -> &'static str {
ZBUS_PATH
}
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 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 || loop {
if let Some(lock) = inner1.try_lock() {
load_save(true, lock);
break;
}
},
move || loop {
if let Some(lock) = inner2.try_lock() {
load_save(false, lock);
break;
}
},
move || loop {
if let Some(lock) = inner3.try_lock() {
load_save(true, lock);
break;
}
},
move || loop {
if let Some(lock) = inner4.try_lock() {
load_save(false, lock);
break;
}
},
)
.await;
let ctrl2 = self.0.clone();
let ctrl = self.0.lock().await;
let mut watch = ctrl.kd_brightness.monitor_brightness()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
if let Some(lock) = ctrl2.try_lock() {
load_save(true, lock);
}
})
.await;
});
Ok(())
}
}
#[async_trait]
impl crate::Reloadable for CtrlKbdLedZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
ctrl.write_current_config_mode()?;
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
Ok(())
}
}

View File

@@ -1,238 +0,0 @@
use std::collections::BTreeMap;
use async_trait::async_trait;
use log::warn;
use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness, PerKeyRaw};
use zbus::{dbus_interface, Connection, SignalContext};
use super::controller::CtrlKbdLedZbus;
#[async_trait]
impl crate::ZbusAdd for CtrlKbdLedZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/Aura", server).await;
}
}
/// The main interface for changing, reading, or notfying signals
///
/// 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) {
if let Ok(ctrl) = self.0.try_lock() {
ctrl.set_brightness(brightness)
.map_err(|err| warn!("{}", err))
.ok();
}
}
/// 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,
/// }
/// ```
async fn set_leds_power(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
options: AuraPowerDev,
enabled: bool,
) -> zbus::fdo::Result<()> {
let mut states = None;
if let Ok(mut ctrl) = self.0.try_lock() {
for p in options.tuf {
ctrl.config.enabled.set_tuf(p, enabled);
}
for p in options.x1866 {
ctrl.config.enabled.set_0x1866(p, enabled);
}
for p in options.x19b6 {
ctrl.config.enabled.set_0x19b6(p, enabled);
}
ctrl.config.write();
ctrl.set_power_states().map_err(|e| {
warn!("{}", e);
e
})?;
states = Some(AuraPowerDev::from(&ctrl.config.enabled));
}
// Need to pull state out like this due to MutexGuard
if let Some(states) = states {
Self::notify_power_states(&ctxt, &states)
.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 led = None;
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.set_effect(effect).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
led = Some(mode.clone());
}
}
if let Some(led) = led {
Self::notify_led(&ctxt, led)
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn next_led_mode(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
) -> zbus::fdo::Result<()> {
let mut led = None;
if let Ok(mut ctrl) = self.0.lock() {
ctrl.toggle_mode(false).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
led = Some(mode.clone());
}
}
if let Some(led) = led {
Self::notify_led(&ctxt, led)
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn prev_led_mode(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
) -> zbus::fdo::Result<()> {
let mut led = None;
if let Ok(mut ctrl) = self.0.lock() {
ctrl.toggle_mode(true).map_err(|e| {
warn!("{}", e);
e
})?;
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
led = Some(mode.clone());
}
}
if let Some(led) = led {
Self::notify_led(&ctxt, led)
.await
.unwrap_or_else(|err| warn!("{}", err));
}
Ok(())
}
async fn next_led_brightness(&self) -> zbus::fdo::Result<()> {
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.next_brightness().map_err(|e| {
warn!("{}", e);
e
})?;
}
Ok(())
}
async fn prev_led_brightness(&self) -> zbus::fdo::Result<()> {
if let Ok(mut ctrl) = self.0.try_lock() {
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 {
loop {
if let Ok(ctrl) = self.0.try_lock() {
return AuraPowerDev::from(&ctrl.config.enabled);
}
}
}
/// Return the current mode data
async fn led_mode(&self) -> AuraModeNum {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.current_mode;
}
AuraModeNum::Static
}
/// Return a list of available modes
async fn led_modes(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
loop {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.config.builtins.clone();
}
}
}
async fn per_key_raw(&self, data: PerKeyRaw) -> zbus::fdo::Result<()> {
if let Ok(mut ctrl) = self.0.try_lock() {
ctrl.write_effect_block(&data)?;
}
Ok(())
}
/// Return the current LED brightness
#[dbus_interface(property)]
async fn led_brightness(&self) -> i8 {
if let Ok(ctrl) = self.0.try_lock() {
return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1);
}
warn!("SetKeyBacklight could not serialise");
-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<()>;
}

View File

@@ -1,29 +1,29 @@
use crate::CtrlTask;
use crate::{config::Config, error::RogError, GetSupported};
use crate::{task_watch_item, CtrlTask};
use async_trait::async_trait;
use log::{info, warn};
use rog_platform::platform::{AsusPlatform, GpuMode};
use rog_platform::supported::RogBiosSupportedFunctions;
use smol::Executor;
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::export::futures_util::lock::Mutex;
use zbus::Connection;
use zbus::{dbus_interface, SignalContext};
static ASUS_POST_LOGO_SOUND: &str =
const ZBUS_PATH: &str = "/org/asuslinux/Platform";
const ASUS_POST_LOGO_SOUND: &str =
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
#[derive(Clone)]
pub struct CtrlRogBios {
pub struct CtrlPlatform {
platform: AsusPlatform,
config: Arc<Mutex<Config>>,
}
impl GetSupported for CtrlRogBios {
impl GetSupported for CtrlPlatform {
type A = RogBiosSupportedFunctions;
fn get_supported() -> Self::A {
@@ -49,7 +49,7 @@ impl GetSupported for CtrlRogBios {
}
}
impl CtrlRogBios {
impl CtrlPlatform {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
let platform = AsusPlatform::new()?;
@@ -59,12 +59,12 @@ impl CtrlRogBios {
}
if Path::new(ASUS_POST_LOGO_SOUND).exists() {
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
CtrlPlatform::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
} else {
info!("Switch for POST boot sound not detected");
}
Ok(CtrlRogBios { platform, config })
Ok(CtrlPlatform { platform, config })
}
fn set_path_mutable(path: &str) -> Result<(), RogError> {
@@ -78,7 +78,7 @@ impl CtrlRogBios {
}
fn set_gfx_mode(&self, mode: GpuMode) -> Result<(), RogError> {
self.platform.set_gpu_mux_mode(mode.to_mux())?;
self.platform.set_gpu_mux_mode(mode.to_mux_attr())?;
// self.update_initramfs(enable)?;
if mode == GpuMode::Discrete {
info!("Set system-level graphics mode: Dedicated Nvidia");
@@ -129,7 +129,7 @@ impl CtrlRogBios {
Ok(())
}
fn set_panel_od(&self, enable: bool) -> Result<(), RogError> {
fn set_panel_overdrive(&self, enable: bool) -> Result<(), RogError> {
self.platform.set_panel_od(enable).map_err(|err| {
warn!("CtrlRogBios: set_panel_overdrive {}", err);
err
@@ -139,7 +139,7 @@ impl CtrlRogBios {
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlRogBios {
impl CtrlPlatform {
async fn set_gpu_mux_mode(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
@@ -147,7 +147,7 @@ impl CtrlRogBios {
) {
self.set_gfx_mode(mode)
.map_err(|err| {
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
warn!("CtrlRogBios: set_gpu_mux_mode {}", err);
err
})
.ok();
@@ -156,7 +156,7 @@ impl CtrlRogBios {
fn gpu_mux_mode(&self) -> GpuMode {
match self.platform.get_gpu_mux_mode() {
Ok(m) => GpuMode::from_mux(m),
Ok(m) => GpuMode::from_mux(m as u8),
Err(e) => {
warn!("CtrlRogBios: get_gfx_mode {}", e);
GpuMode::Error
@@ -197,80 +197,148 @@ impl CtrlRogBios {
#[dbus_interface(signal)]
async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
async fn set_panel_overdrive(
async fn set_panel_od(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
overdrive: bool,
) {
if self
.set_panel_od(overdrive)
.map_err(|err| {
warn!("CtrlRogBios: set_panel_overdrive {}", err);
err
})
.is_ok()
{
if let Ok(mut lock) = self.config.try_lock() {
lock.panel_od = overdrive;
lock.write();
match self.platform.set_panel_od(overdrive) {
Ok(_) => {
if let Some(mut lock) = self.config.try_lock() {
lock.panel_od = overdrive;
lock.write();
}
Self::notify_panel_od(&ctxt, overdrive).await.ok();
}
Self::notify_panel_overdrive(&ctxt, overdrive).await.ok();
}
Err(err) => warn!("CtrlRogBios: set_panel_overdrive {}", err),
};
}
fn panel_overdrive(&self) -> bool {
self.platform
/// Get the `panel_od` value from platform. Updates the stored value in internal config also.
fn panel_od(&self) -> bool {
let od = self
.platform
.get_panel_od()
.map_err(|err| {
warn!("CtrlRogBios: get panel overdrive {}", err);
warn!("CtrlRogBios: get_panel_od {}", err);
err
})
.unwrap_or(false);
if let Some(mut lock) = self.config.try_lock() {
lock.panel_od = od;
lock.write();
}
od
}
#[dbus_interface(signal)]
async fn notify_panel_od(signal_ctxt: &SignalContext<'_>, overdrive: bool) -> zbus::Result<()> {
}
async fn set_dgpu_disable(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
disable: bool,
) {
match self.platform.set_dgpu_disable(disable) {
Ok(_) => {
Self::notify_dgpu_disable(&ctxt, disable).await.ok();
}
Err(err) => warn!("CtrlRogBios: set_dgpu_disable {}", err),
};
}
fn dgpu_disable(&self) -> bool {
self.platform
.get_dgpu_disable()
.map_err(|err| {
warn!("CtrlRogBios: get_dgpu_disable {}", err);
err
})
.unwrap_or(false)
}
#[dbus_interface(signal)]
async fn notify_panel_overdrive(
async fn notify_dgpu_disable(
signal_ctxt: &SignalContext<'_>,
overdrive: bool,
disable: bool,
) -> zbus::Result<()> {
}
async fn set_egpu_enable(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
enable: bool,
) {
match self.platform.set_egpu_enable(enable) {
Ok(_) => {
Self::notify_egpu_enable(&ctxt, enable).await.ok();
}
Err(err) => warn!("CtrlRogBios: set_egpu_enable {}", err),
};
}
fn egpu_enable(&self) -> bool {
self.platform
.get_egpu_enable()
.map_err(|err| {
warn!("CtrlRogBios: get_egpu_enable {}", err);
err
})
.unwrap_or(false)
}
#[dbus_interface(signal)]
async fn notify_egpu_enable(signal_ctxt: &SignalContext<'_>, enable: bool) -> zbus::Result<()> {
}
}
#[async_trait]
impl crate::ZbusAdd for CtrlRogBios {
impl crate::ZbusRun for CtrlPlatform {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/Platform", server).await;
}
}
impl crate::Reloadable for CtrlRogBios {
fn reload(&mut self) -> Result<(), RogError> {
#[async_trait]
impl crate::Reloadable for CtrlPlatform {
async fn reload(&mut self) -> Result<(), RogError> {
if self.platform.has_panel_od() {
let p = if let Ok(lock) = self.config.try_lock() {
let p = if let Some(lock) = self.config.try_lock() {
lock.panel_od
} else {
false
};
self.set_panel_od(p)?;
self.set_panel_overdrive(p)?;
}
Ok(())
}
}
impl CtrlPlatform {
task_watch_item!(panel_od platform);
task_watch_item!(dgpu_disable platform);
task_watch_item!(egpu_enable platform);
task_watch_item!(gpu_mux_mode platform);
}
#[async_trait]
impl CtrlTask for CtrlRogBios {
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
impl CtrlTask for CtrlPlatform {
fn zbus_path() -> &'static str {
ZBUS_PATH
}
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let platform1 = self.clone();
let platform2 = self.clone();
self.create_sys_event_tasks(
executor,
move || {},
move || {
info!("CtrlRogBios reloading panel_od");
if let Ok(lock) = platform1.config.try_lock() {
if let Some(lock) = platform1.config.try_lock() {
if platform1.platform.has_panel_od() {
platform1
.set_panel_od(lock.panel_od)
.set_panel_overdrive(lock.panel_od)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
@@ -282,10 +350,10 @@ impl CtrlTask for CtrlRogBios {
move || {},
move || {
info!("CtrlRogBios reloading panel_od");
if let Ok(lock) = platform2.config.try_lock() {
if let Some(lock) = platform2.config.try_lock() {
if platform2.platform.has_panel_od() {
platform2
.set_panel_od(lock.panel_od)
.set_panel_overdrive(lock.panel_od)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
@@ -297,6 +365,11 @@ impl CtrlTask for CtrlRogBios {
)
.await;
self.watch_panel_od(signal_ctxt.clone()).await?;
self.watch_dgpu_disable(signal_ctxt.clone()).await?;
self.watch_egpu_enable(signal_ctxt.clone()).await?;
self.watch_gpu_mux_mode(signal_ctxt.clone()).await?;
Ok(())
}
}

View File

@@ -1,16 +1,21 @@
use crate::CtrlTask;
use crate::systemd::{do_systemd_unit_action, SystemdUnitAction};
use crate::{config::Config, error::RogError, GetSupported};
use crate::{task_watch_item, CtrlTask};
use async_trait::async_trait;
use log::{info, warn};
use rog_platform::power::AsusPower;
use rog_platform::supported::ChargeSupportedFunctions;
use smol::Executor;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use tokio::time::sleep;
use zbus::dbus_interface;
use zbus::export::futures_util::lock::Mutex;
use zbus::Connection;
use zbus::SignalContext;
const ZBUS_PATH: &str = "/org/asuslinux/Power";
const NVIDIA_POWERD: &str = "nvidia-powerd.service";
impl GetSupported for CtrlPower {
type A = ChargeSupportedFunctions;
@@ -33,7 +38,7 @@ pub struct CtrlPower {
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlPower {
async fn set_limit(
async fn set_charge_control_end_threshold(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
limit: u8,
@@ -47,31 +52,63 @@ impl CtrlPower {
err
})
.ok();
Self::notify_charge(&ctxt, limit).await?;
Self::notify_charge_control_end_threshold(&ctxt, limit)
.await
.ok();
Ok(())
}
fn limit(&self) -> i8 {
if let Ok(config) = self.config.try_lock() {
return config.bat_charge_limit as i8;
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;
}
}
-1
}
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(ctxt: &SignalContext<'_>, limit: u8) -> zbus::Result<()>;
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::ZbusAdd for CtrlPower {
impl crate::ZbusRun for CtrlPower {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/Charge", server).await;
Self::add_to_server_helper(self, ZBUS_PATH, server).await;
}
}
#[async_trait]
impl crate::Reloadable for CtrlPower {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut config) = self.config.try_lock() {
async fn reload(&mut self) -> Result<(), RogError> {
if let Some(mut config) = self.config.try_lock() {
config.read();
self.set(config.bat_charge_limit)?;
}
@@ -96,7 +133,7 @@ impl CtrlPower {
info!("Battery charge limit: {}", limit);
if let Ok(mut config) = self.config.try_lock() {
if let Some(mut config) = self.config.try_lock() {
config.read();
config.bat_charge_limit = limit;
config.write();
@@ -104,19 +141,24 @@ impl CtrlPower {
Ok(())
}
task_watch_item!(charge_control_end_threshold power);
}
#[async_trait]
impl CtrlTask for CtrlPower {
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
fn zbus_path() -> &'static str {
ZBUS_PATH
}
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let power1 = self.clone();
let power2 = self.clone();
self.create_sys_event_tasks(
executor,
move || {},
move || {
info!("CtrlCharge reloading charge limit");
if let Ok(lock) = power1.config.try_lock() {
if let Some(lock) = power1.config.try_lock() {
power1
.set(lock.bat_charge_limit)
.map_err(|err| {
@@ -125,11 +167,21 @@ impl CtrlTask for CtrlPower {
})
.ok();
}
if let Ok(value) = power1.power.get_online() {
let action = if value == 1 {
SystemdUnitAction::Restart
} else {
SystemdUnitAction::Stop
};
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
}
}
},
move || {},
move || {
info!("CtrlCharge reloading charge limit");
if let Ok(lock) = power2.config.try_lock() {
if let Some(lock) = power2.config.try_lock() {
power2
.set(lock.bat_charge_limit)
.map_err(|err| {
@@ -138,10 +190,50 @@ impl CtrlTask for CtrlPower {
})
.ok();
}
if let Ok(value) = power2.power.get_online() {
let action = if value == 1 {
SystemdUnitAction::Restart
} else {
SystemdUnitAction::Stop
};
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
}
}
},
)
.await;
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 action = if value == 1 {
SystemdUnitAction::Restart
} else {
SystemdUnitAction::Stop
};
if do_systemd_unit_action(action, NVIDIA_POWERD).is_ok() {
info!("CtrlPower task: did {action:?} on {NVIDIA_POWERD}");
}
Self::notify_mains_online(&signal_ctxt, value == 1)
.await
.unwrap();
}
}
// 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(())
}
}

View File

@@ -43,7 +43,8 @@ impl ProfileConfig {
"Could not deserialise {}.\nWill rename to {}-old and recreate config",
config_path, config_path
);
let cfg_old = config_path.clone() + "-old";
let mut cfg_old = config_path.clone();
cfg_old.push_str("-old");
std::fs::rename(config_path.clone(), cfg_old).unwrap_or_else(|err| {
panic!(
"Could not rename. Please remove {} then restart service: Error {}",

View File

@@ -1,17 +1,16 @@
use crate::error::RogError;
use crate::{CtrlTask, GetSupported};
use async_trait::async_trait;
use crate::GetSupported;
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 smol::Executor;
use std::sync::{Arc, Mutex};
use super::config::ProfileConfig;
pub struct CtrlPlatformProfile {
pub config: ProfileConfig,
pub platform: AsusPlatform,
}
impl GetSupported for CtrlPlatformProfile {
@@ -39,27 +38,13 @@ impl GetSupported for CtrlPlatformProfile {
}
}
impl crate::Reloadable for CtrlPlatformProfile {
/// Fetch the active profile and use that to set all related components up
fn reload(&mut self) -> Result<(), RogError> {
if let Some(curves) = &mut self.config.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.write_profile_curve_to_platform(self.config.active_profile, &mut device)?;
self.config.write();
}
}
Ok(())
}
}
impl CtrlPlatformProfile {
pub fn new(config: ProfileConfig) -> Result<Self, RogError> {
if Profile::is_platform_profile_supported() {
let platform = AsusPlatform::new()?;
if platform.has_platform_profile() || platform.has_throttle_thermal_policy() {
info!("Device has profile control available");
let mut controller = CtrlPlatformProfile { config };
let mut controller = CtrlPlatformProfile { config, platform };
if FanCurveProfiles::get_device().is_ok() {
info!("Device has fan curves available");
if controller.config.fan_curves.is_none() {
@@ -129,32 +114,3 @@ impl CtrlPlatformProfile {
Ok(())
}
}
pub struct CtrlProfileTask {
ctrl: Arc<Mutex<CtrlPlatformProfile>>,
}
impl CtrlProfileTask {
pub fn new(ctrl: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
Self { ctrl }
}
}
#[async_trait]
impl CtrlTask for CtrlProfileTask {
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
let ctrl = self.ctrl.clone();
self.repeating_task(666, executor, move || {
if let Ok(ref mut lock) = ctrl.try_lock() {
let new_profile = Profile::get_active_profile().unwrap();
if new_profile != lock.config.active_profile {
lock.config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
}
}
})
.await;
Ok(())
}
}

View File

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

View File

@@ -0,0 +1,246 @@
use async_trait::async_trait;
use log::warn;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::fan_curve_set::FanCurveSet;
use rog_profiles::FanCurveProfiles;
use rog_profiles::Profile;
use zbus::export::futures_util::lock::Mutex;
use zbus::export::futures_util::StreamExt;
use zbus::Connection;
use zbus::SignalContext;
use std::sync::Arc;
use zbus::{dbus_interface, fdo::Error};
use crate::error::RogError;
use crate::CtrlTask;
use super::controller::CtrlPlatformProfile;
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;
ctrl.set_next_profile()
.unwrap_or_else(|err| warn!("{}", err));
ctrl.save_config();
Self::notify_profile(&ctxt, ctrl.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.config.read();
Ok(ctrl.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.config.read();
Profile::set_profile(profile)
.map_err(|e| warn!("set_profile, {}", e))
.ok();
ctrl.config.active_profile = profile;
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
.ok();
ctrl.save_config();
Self::notify_profile(&ctxt, ctrl.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.config.read();
if let Some(curves) = &ctrl.config.fan_curves {
return Ok(curves.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.config.read();
if let Some(curves) = &mut ctrl.config.fan_curves {
curves.set_profile_curve_enabled(profile, enabled);
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("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.config.read();
if let Some(curves) = &ctrl.config.fan_curves {
let curve = curves.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.config.read();
if let Some(curves) = &mut ctrl.config.fan_curves {
curves
.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!("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.config.read();
ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("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.config.read();
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
Profile::set_profile(profile)
.map_err(|e| warn!("set_profile, {}", e))
.ok();
ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e))
.ok();
Profile::set_profile(active)
.map_err(|e| warn!("set_profile, {}", e))
.ok();
ctrl.save_config();
Ok(())
}
#[dbus_interface(signal)]
async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> {
}
}
#[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 mut watch = self.0.lock().await.platform.monitor_platform_profile()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
let mut lock = ctrl.lock().await;
let new_profile = Profile::get_active_profile().unwrap();
if new_profile != lock.config.active_profile {
lock.config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
}
Self::notify_profile(&signal_ctxt.clone(), lock.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.config.active_profile;
if let Some(curves) = &mut ctrl.config.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.write_profile_curve_to_platform(active, &mut device)?;
ctrl.config.write();
}
}
Ok(())
}
}

View File

@@ -1,211 +0,0 @@
use async_trait::async_trait;
use log::warn;
use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::fan_curve_set::FanCurveSet;
use rog_profiles::Profile;
use zbus::Connection;
use zbus::SignalContext;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::{dbus_interface, fdo::Error};
use super::controller::CtrlPlatformProfile;
static UNSUPPORTED_MSG: &str =
"Fan curves are not supported on this laptop or you require a patched kernel";
pub struct ProfileZbus {
inner: Arc<Mutex<CtrlPlatformProfile>>,
}
impl ProfileZbus {
pub fn new(inner: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
Self { inner }
}
}
#[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_string(),
))
}
/// 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 profile = None;
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.set_next_profile()
.unwrap_or_else(|err| warn!("{}", err));
ctrl.save_config();
profile = Some(ctrl.config.active_profile);
}
if let Some(profile) = profile {
Self::notify_profile(&ctxt, profile).await.ok();
}
}
/// Fetch the active profile name
fn active_profile(&mut self) -> zbus::fdo::Result<Profile> {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.config.read();
return Ok(ctrl.config.active_profile);
}
Err(Error::Failed(
"Failed to get active profile name".to_string(),
))
}
/// Set this platform_profile name as active
async fn set_active_profile(
&self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
profile: Profile,
) {
let mut tmp = None;
if let Ok(mut ctrl) = self.inner.try_lock() {
// Read first just incase the user has modified the config before calling this
ctrl.config.read();
Profile::set_profile(profile)
.map_err(|e| warn!("set_profile, {}", e))
.ok();
ctrl.config.active_profile = profile;
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
.ok();
ctrl.save_config();
tmp = Some(ctrl.config.active_profile);
}
if let Some(profile) = tmp {
Self::notify_profile(&ctxt, profile).await.ok();
}
}
/// Get a list of profiles that have fan-curves enabled.
fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.config.read();
if let Some(curves) = &ctrl.config.fan_curves {
return Ok(curves.get_enabled_curve_profiles().to_vec());
}
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
}
Err(Error::Failed(
"Failed to get enabled fan curve names".to_string(),
))
}
/// Set a profile fan curve enabled status. Will also activate a fan curve if in the
/// same profile mode
fn set_fan_curve_enabled(&mut self, profile: Profile, enabled: bool) -> zbus::fdo::Result<()> {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.config.read();
return if let Some(curves) = &mut ctrl.config.fan_curves {
curves.set_profile_curve_enabled(profile, enabled);
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("write_profile_curve_to_platform, {}", e))
.ok();
ctrl.save_config();
Ok(())
} else {
Err(Error::Failed(UNSUPPORTED_MSG.to_string()))
};
}
Err(Error::Failed(
"Failed to get enabled fan curve names".to_string(),
))
}
/// Get the fan-curve data for the currently active Profile
fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result<FanCurveSet> {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.config.read();
if let Some(curves) = &ctrl.config.fan_curves {
let curve = curves.get_fan_curves_for(profile);
return Ok(curve.clone());
}
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
}
Err(Error::Failed("Failed to get fan curve data".to_string()))
}
/// Set the fan curve for the specified profile.
/// Will also activate the fan curve if the user is in the same mode.
fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.config.read();
if let Some(curves) = &mut ctrl.config.fan_curves {
curves
.save_fan_curve(curve, profile)
.map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?;
} else {
return Err(Error::Failed(UNSUPPORTED_MSG.to_string()));
}
ctrl.write_profile_curve_to_platform()
.map_err(|e| warn!("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.
fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.config.read();
ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("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.
fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> {
if let Ok(mut ctrl) = self.inner.try_lock() {
ctrl.config.read();
let active = Profile::get_active_profile().unwrap_or(Profile::Balanced);
Profile::set_profile(profile)
.map_err(|e| warn!("set_profile, {}", e))
.ok();
ctrl.set_active_curve_to_defaults()
.map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e))
.ok();
Profile::set_profile(active)
.map_err(|e| warn!("set_profile, {}", e))
.ok();
ctrl.save_config();
}
Ok(())
}
#[dbus_interface(signal)]
async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> {
}
}
#[async_trait]
impl crate::ZbusAdd for ProfileZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, "/org/asuslinux/Profile", server).await;
}
}

View File

@@ -1,11 +1,9 @@
use async_trait::async_trait;
use serde_derive::{Deserialize, Serialize};
use zbus::dbus_interface;
use zbus::Connection;
use zvariant::Type;
use zbus::{dbus_interface, zvariant::Type, Connection};
use crate::{
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_platform::CtrlRogBios,
ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_platform::CtrlPlatform,
ctrl_power::CtrlPower, ctrl_profiles::controller::CtrlPlatformProfile, GetSupported,
};
@@ -28,7 +26,7 @@ impl SupportedFunctions {
}
#[async_trait]
impl crate::ZbusAdd for SupportedFunctions {
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;
}
@@ -43,7 +41,7 @@ impl GetSupported for SupportedFunctions {
keyboard_led: CtrlKbdLed::get_supported(),
charge_ctrl: CtrlPower::get_supported(),
platform_profile: CtrlPlatformProfile::get_supported(),
rog_bios_ctrl: CtrlRogBios::get_supported(),
rog_bios_ctrl: CtrlPlatform::get_supported(),
}
}
}

View File

@@ -1,38 +1,36 @@
use std::env;
use std::error::Error;
use std::io::Write;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::time::Duration;
use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection;
use daemon::ctrl_profiles::controller::CtrlProfileTask;
use daemon::ctrl_anime::CtrlAnime;
use log::LevelFilter;
use log::{error, info, warn};
use smol::Executor;
use tokio::time::sleep;
use zbus::SignalContext;
use daemon::ctrl_anime::config::AnimeConfig;
use daemon::ctrl_anime::zbus::CtrlAnimeZbus;
use daemon::ctrl_anime::*;
use daemon::ctrl_aura::config::AuraConfig;
use daemon::ctrl_aura::controller::{
CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus,
};
use daemon::ctrl_platform::CtrlRogBios;
use daemon::ctrl_anime::{config::AnimeConfig, trait_impls::CtrlAnimeZbus};
use daemon::ctrl_aura::{config::AuraConfig, controller::CtrlKbdLed, trait_impls::CtrlKbdLedZbus};
use daemon::ctrl_platform::CtrlPlatform;
use daemon::ctrl_power::CtrlPower;
use daemon::ctrl_profiles::config::ProfileConfig;
use daemon::ctrl_profiles::{
config::ProfileConfig, controller::CtrlPlatformProfile, trait_impls::ProfileZbus,
};
use daemon::laptops::LaptopLedData;
use daemon::{
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
};
use daemon::{
ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus},
laptops::LaptopLedData,
};
use daemon::{CtrlTask, Reloadable, ZbusAdd};
use daemon::{CtrlTask, Reloadable, ZbusRun};
use rog_dbus::DBUS_NAME;
use rog_profiles::Profile;
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
logger
.target(env_logger::Target::Stdout)
@@ -61,14 +59,12 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
info!(" rog-profiles v{}", rog_profiles::VERSION);
info!("rog-platform v{}", rog_platform::VERSION);
let mut executor = Executor::new();
smol::block_on(start_daemon(&mut executor))?;
start_daemon().await?;
Ok(())
}
/// The actual main loop for the daemon
async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>> {
async fn start_daemon() -> Result<(), Box<dyn Error>> {
let supported = SupportedFunctions::get_supported();
print_board_info();
println!("{}", serde_json::to_string_pretty(&supported)?);
@@ -81,77 +77,50 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
supported.add_to_server(&mut connection).await;
match CtrlRogBios::new(config.clone()) {
Ok(mut ctrl) => {
// Do a reload of any settings
ctrl.reload()
.unwrap_or_else(|err| warn!("CtrlRogBios: {}", err));
// Then register to dbus server
ctrl.add_to_server(&mut connection).await;
let task = CtrlRogBios::new(config.clone())?;
task.create_tasks(executor).await.ok();
match CtrlPlatform::new(config.clone()) {
Ok(ctrl) => {
let sig_ctx = CtrlPlatform::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("rog_bios_control: {}", err);
error!("CtrlPlatform: {}", err);
}
}
match CtrlPower::new(config.clone()) {
Ok(mut ctrl) => {
// Do a reload of any settings
ctrl.reload()
.unwrap_or_else(|err| warn!("CtrlPower: {}", err));
// Then register to dbus server
ctrl.add_to_server(&mut connection).await;
let task = CtrlPower::new(config)?;
task.create_tasks(executor).await.ok();
Ok(ctrl) => {
let sig_ctx = CtrlPower::signal_context(&connection)?;
start_tasks(ctrl, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("charge_control: {}", err);
error!("CtrlPower: {}", err);
}
}
if Profile::is_platform_profile_supported() {
let profile_config = ProfileConfig::load(PROFILE_CONFIG_PATH.into());
match CtrlPlatformProfile::new(profile_config) {
Ok(mut ctrl) => {
ctrl.reload()
.unwrap_or_else(|err| warn!("Profile control: {}", err));
let tmp = Arc::new(Mutex::new(ctrl));
let task = CtrlProfileTask::new(tmp.clone());
task.create_tasks(executor).await.ok();
let task = ProfileZbus::new(tmp.clone());
task.add_to_server(&mut connection).await;
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. This requires kernel 5.15.x or the patch applied: https://lkml.org/lkml/2021/8/18/1022");
warn!("platform_profile support not found");
}
match CtrlAnime::new(AnimeConfig::load()) {
Ok(ctrl) => {
let inner = Arc::new(Mutex::new(ctrl));
let mut reload = CtrlAnimeReloader(inner.clone());
reload
.reload()
.unwrap_or_else(|err| warn!("AniMe: {}", err));
let zbus = CtrlAnimeZbus(inner.clone());
zbus.add_to_server(&mut connection).await;
let task = CtrlAnimeTask::new(inner).await;
task.create_tasks(executor).await.ok();
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("AniMe control: {}", err);
info!("AniMe control: {}", err);
}
}
@@ -159,19 +128,9 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
let aura_config = AuraConfig::load(&laptop);
match CtrlKbdLed::new(laptop, aura_config) {
Ok(ctrl) => {
let inner = Arc::new(Mutex::new(ctrl));
let mut reload = CtrlKbdLedReloader(inner.clone());
reload
.reload()
.unwrap_or_else(|err| warn!("Keyboard LED control: {}", err));
CtrlKbdLedZbus::new(inner.clone())
.add_to_server(&mut connection)
.await;
let task = CtrlKbdLedTask::new(inner);
task.create_tasks(executor).await.ok();
let zbus = CtrlKbdLedZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlKbdLedZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
error!("Keyboard control: {}", err);
@@ -180,7 +139,28 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
// Request dbus name after finishing initalizing all functions
connection.request_name(DBUS_NAME).await?;
loop {
smol::block_on(executor.tick());
// This is just a blocker to idle and ensure the reator reacts
sleep(Duration::from_millis(1000)).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(())
}

View File

@@ -30,11 +30,14 @@ pub enum RogError {
NoAuraNode,
Anime(AnimeError),
Platform(PlatformError),
SystemdUnitAction(String),
SystemdUnitWaitTimeout(String),
Command(String, std::io::Error),
}
impl fmt::Display for RogError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
RogError::ParseLed => write!(f, "Parse LED error"),
@@ -48,7 +51,7 @@ impl fmt::Display for RogError {
RogError::DoTask(deets) => write!(f, "Task error: {}", deets),
RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets),
RogError::ReloadFail(deets) => write!(f, "Reload error: {}", deets),
RogError::Profiles(deets) => write!(f, "Profile error: {}", deets),
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
@@ -60,6 +63,17 @@ impl fmt::Display for RogError {
RogError::NoAuraNode => write!(f, "No Aura keyboard node found"),
RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets),
RogError::Platform(deets) => write!(f, "Asus Platform error: {}", deets),
RogError::SystemdUnitAction(action) => {
write!(f, "systemd unit action {} failed", action)
}
RogError::SystemdUnitWaitTimeout(state) => {
write!(
f,
"Timed out waiting for systemd unit change {} state",
state
)
}
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
}
}
}

View File

@@ -71,7 +71,7 @@ impl LaptopLedData {
}
impl LedSupportFile {
/// Consumes the LEDModes
/// Consumes the `LEDModes`
fn matcher(self, prod_family: &str, board_name: &str) -> Option<LaptopLedData> {
for config in self.led_data {
if prod_family.contains(&config.prod_family) {
@@ -90,7 +90,7 @@ impl LedSupportFile {
let mut loaded = false;
let mut data = LedSupportFile::default();
// Load user configs first so they are first to be checked
if let Ok(mut file) = OpenOptions::new().read(true).open(&ASUS_LED_MODE_USER_CONF) {
if let Ok(mut file) = OpenOptions::new().read(true).open(ASUS_LED_MODE_USER_CONF) {
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
@@ -107,7 +107,7 @@ impl LedSupportFile {
}
}
// Load and append the default LED support data
if let Ok(mut file) = OpenOptions::new().read(true).open(&ASUS_LED_MODE_CONF) {
if let Ok(mut file) = OpenOptions::new().read(true).open(ASUS_LED_MODE_CONF) {
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {

View File

@@ -1,7 +1,7 @@
#![deny(unused_must_use)]
/// Configuration loading, saving
pub mod config;
/// Control of AniMe matrix display
/// Control of anime matrix display
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
@@ -14,30 +14,79 @@ pub mod ctrl_profiles;
/// Laptop matching to determine capabilities
pub mod laptops;
pub mod systemd;
/// Fetch all supported functions for the laptop
pub mod ctrl_supported;
pub mod error;
use std::time::Duration;
use crate::error::RogError;
use async_trait::async_trait;
use config::Config;
use log::warn;
use logind_zbus::manager::ManagerProxy;
use smol::{stream::StreamExt, Executor, Timer};
use zbus::Connection;
use zvariant::ObjectPath;
use zbus::{export::futures_util::StreamExt, zvariant::ObjectPath, Connection, SignalContext};
/// This macro adds a function which spawns an `inotify` task on the passed in `Executor`.
///
/// The generated function is `watch_<name>()`. Self requires the following methods to be available:
/// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have side effects.
/// - `notify_<name>(SignalContext, SomeValue)`
///
/// In most cases if `SomeValue` is stored in a config then `<name>()` getter is expected to update it.
/// The getter should *never* write back to the path or attribute that is being watched or an
/// infinite loop will occur.
///
/// # Example
///
/// ```ignore
/// impl CtrlRogBios {
/// task_watch_item!(panel_od platform);
/// task_watch_item!(gpu_mux_mode platform);
/// }
/// ```
#[macro_export]
macro_rules! task_watch_item {
($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(mut watch) => {
tokio::spawn(async move {
let mut buffer = [0; 32];
watch.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();
});
}).await;
});
}
Err(e) => info!("inotify watch failed: {}. You can ignore this if your device does not support the feature", e),
}
});
Ok(())
}
});
};
}
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[async_trait]
pub trait Reloadable {
fn reload(&mut self) -> Result<(), RogError>;
async fn reload(&mut self) -> Result<(), RogError>;
}
#[async_trait]
pub trait ZbusAdd {
pub trait ZbusRun {
async fn add_to_server(self, server: &mut Connection);
async fn add_to_server_helper(
@@ -60,29 +109,33 @@ pub trait ZbusAdd {
/// Set up a task to run on the async executor
#[async_trait]
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, executor: &mut Executor) -> Result<(), RogError>;
fn zbus_path() -> &'static str;
/// Create a timed repeating task
async fn repeating_task(
&self,
millis: u64,
executor: &mut Executor,
mut task: impl FnMut() + Send + 'static,
) {
let timer = Timer::interval(Duration::from_millis(millis));
executor
.spawn(async move {
timer.for_each(|_| task()).await;
})
.detach();
fn signal_context(connection: &Connection) -> Result<SignalContext<'static>, zbus::Error> {
SignalContext::new(connection, Self::zbus_path())
}
/// 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>;
// /// Create a timed repeating task
// async fn repeating_task(&self, millis: u64, mut task: impl FnMut() + Send + 'static) {
// use std::time::Duration;
// use tokio::time;
// let mut timer = time::interval(Duration::from_millis(millis));
// tokio::spawn(async move {
// timer.tick().await;
// task();
// });
// }
/// Free helper method to create tasks to run on: sleep, wake, shutdown, boot
///
/// The closures can potentially block, so execution time should be the minimal possible
/// such as save a variable.
async fn create_sys_event_tasks(
&self,
executor: &mut Executor,
mut on_sleep: impl FnMut() + Send + 'static,
mut on_wake: impl FnMut() + Send + 'static,
mut on_shutdown: impl FnMut() + Send + 'static,
@@ -96,54 +149,40 @@ pub trait CtrlTask {
.await
.expect("Controller could not create ManagerProxy");
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
if args.start {
on_sleep();
} else if !args.start() {
on_wake();
}
}
})
.await;
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 {
on_sleep();
} else if !args.start() {
on_wake();
}
}
}
})
.detach();
}
});
let manager = ManagerProxy::new(&connection)
.await
.expect("Controller could not create ManagerProxy");
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
if args.start {
on_shutdown();
} else if !args.start() {
on_boot();
}
}
})
.await;
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 {
on_shutdown();
} else if !args.start() {
on_boot();
}
}
}
})
.detach();
}
});
}
}
pub trait CtrlTaskComplex {
type A;
fn do_task(&mut self, config: &mut Config, event: Self::A);
}
pub trait GetSupported {
type A;

90
daemon/src/systemd.rs Normal file
View File

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

View File

@@ -1,14 +0,0 @@
[Unit]
Description=ASUS Notifications
StartLimitInterval=200
StartLimitBurst=2
[Service]
ExecStartPre=/usr/bin/sleep 2
ExecStart=/usr/bin/asus-notify
Restart=on-failure
RestartSec=1
Type=simple
[Install]
WantedBy=default.target

View File

@@ -1,3 +1,10 @@
[[led_data]]
prod_family = "ASUS TUF Gaming F15"
board_names = ["FX506HC"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "TUF"
board_names = ["FA507"]
@@ -12,6 +19,13 @@ standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ASUS TUF Gaming A15"
board_names = ["FA506I"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "Zephyrus S"
board_names = ["GX502", "GX701", "G531", "GL531", "G532"]
@@ -21,21 +35,7 @@ per_key = true
[[led_data]]
prod_family = "Zephyrus M"
board_names = ["GU502GV"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
[[led_data]]
prod_family = "Zephyrus M"
board_names = ["GM501GS"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus M15"
board_names = ["GU502LW", "GU502LV"]
board_names = ["GU502G"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
@@ -47,9 +47,23 @@ standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus M15"
board_names = ["GU502L"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
[[led_data]]
prod_family = "ROG Zephyrus M16"
board_names = ["GU603Z", "GU603H"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Zephyrus S17"
board_names = ["GX703HS"]
board_names = ["GX703H"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = false
@@ -57,21 +71,21 @@ per_key = false
[[led_data]]
prod_family = "Zephyrus"
board_names = ["GM501GM", "GX531"]
board_names = ["GM501G", "GX531"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G531GW", "G533QR", "G533QS", "G733QS", "G733QR", "G513QR", "G713QR", "G513QM", "G713IC", "G713RS"]
board_names = ["G531GW", "G533QR", "G533QS", "G733Q", "G513QR", "G713QR", "G513QM", "G713IC", "G713RS"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Star", "Rain", "Highlight", "Laser", "Ripple", "Pulse", "Comet", "Flash"]
multizone = []
per_key = true
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512", "G713RW"]
board_names = ["G513QE", "GX531", "G512LV", "G712LV", "G712LW", "G513IH", "G513QY", "G713QM", "G512", "G713RM", "G713RW"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = ["Key1", "Key2", "Key3", "Key4"]
per_key = false
@@ -170,14 +184,21 @@ per_key = true
[[led_data]]
prod_family = "ROG Flow X13"
board_names = ["GV301QH", "GV301QE"]
board_names = ["GV301Q"]
standard = ["Static", "Breathe", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Strix"
board_names = ["G513IC", "G513RC"]
board_names = ["G513IC", "G513RC", "G513RM"]
standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"]
multizone = []
per_key = false
[[led_data]]
prod_family = "ROG Flow X16"
board_names = ["GV601R"]
standard = ["Static", "Breathe", "Strobe", "Pulse"]
multizone = []
per_key = false

View File

@@ -9,6 +9,7 @@ ENV{DMI_FAMILY}=="*TUF*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*ROG*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Zephyrus*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Strix*", GOTO="asusd_start"
ENV{DMI_FAMILY}=="*Vivo*ook*", GOTO="asusd_start"
# No match so
GOTO="asusd_end"
@@ -16,4 +17,4 @@ LABEL="asusd_start"
ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", RUN+="systemctl restart asusd.service"
LABEL="asusd_end"
LABEL="asusd_end"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 74 KiB

69
deny.toml Normal file
View File

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

View File

@@ -18,81 +18,112 @@ Then for each trait that is required a new struct is required that can have the
Main controller:
For a very simple controller that doesn't need exclusive access you can clone across threads
```rust
#[derive(Clone)]
pub struct CtrlAnime {
<things the controller requires>
config: Arc<Mutex<Config>>,
}
// This is the task trait used for such things as file watches, or logind
// notifications (boot/suspend/shutdown etc)
impl crate::CtrlTask for CtrlAnime {}
// The trait to easily add the controller to Zbus to enable the zbus derived functions
// to be polled, run, react etc.
impl crate::ZbusAdd for CtrlAnime {}
impl CtrlAnime {}
```
Otherwise, you will need to share the controller via mutex
```rust
pub struct CtrlAnime {
<things the controller requires>
}
// Like this
#[derive(Clone)]
pub struct CtrlAnimeTask(Arc<Mutex<CtrlAnime>>);
impl CtrlAnime {
<functions the controller exposes>
}
#[derive(Clone)]
pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>);
impl CtrlAnime {}
```
The task trait. There are three ways to implement this:
The task trait:
```rust
// Mutex should always be async mutex
pub struct CtrlAnimeTask(Arc<Mutex<CtrlAnime>>);
impl crate::CtrlTask for CtrlAnimeTask {
// This will run once only
fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
if let Ok(lock) = self.inner.try_lock() {
<some action>
}
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let lock self.inner.lock().await;
<some action>
Ok(())
}
// This will run until the notification stream closes (which in most cases will be never)
fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
let connection = Connection::system().await.unwrap();
let manager = ManagerProxy::new(&connection).await.unwrap();
let inner = self.inner.clone();
executor
.spawn(async move {
// A notification from logind dbus interface
if let Ok(p) = manager.receive_prepare_for_sleep().await {
// A stream that will continuously output events
p.for_each(|_| {
if let Ok(lock) = inner.try_lock() {
// Do stuff here
}
})
.await;
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let inner1 = self.inner.clone();
let inner2 = self.inner.clone();
let inner3 = self.inner.clone();
let inner4 = self.inner.clone();
// This is a free method on CtrlTask trait
self.create_sys_event_tasks(
// Loop is required to try an attempt to get the mutex *without* blocking
// other threads - it is possible to end up with deadlocks otherwise.
move || loop {
if let Some(lock) = inner1.try_lock() {
run_action(true, lock, inner1.clone());
break;
}
})
.detach();
}
// This task will run every 500 milliseconds
fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
let inner = self.inner.clone();
// This is a provided free trait to help set up a repeating task
self.repeating_task(500, executor, move || {
if let Ok(lock) = inner.try_lock() {
// Do stuff here
}
})
},
move || loop {
if let Some(lock) = inner2.try_lock() {
run_action(false, lock, inner2.clone());
break;
}
},
move || loop {
if let Some(lock) = inner3.try_lock() {
run_action(true, lock, inner3.clone());
break;
}
},
move || loop {
if let Some(lock) = inner4.try_lock() {
run_action(false, lock, inner4.clone());
break;
}
},
)
.await;
}
}
```
The reloader trait
```rust
pub struct CtrlAnimeReloader(Arc<Mutex<CtrlAnime>>);
impl crate::Reloadable for CtrlAnimeReloader {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(lock) = self.inner.try_lock() {
<some action>
}
async fn reload(&mut self) -> Result<(), RogError> {
let lock = self.inner.lock().await;
<some action>
Ok(())
}
}
```
The Zbus requirements:
```rust
pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>);
@@ -106,10 +137,9 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus {
fn <zbus method>() {
if let Ok(lock) = self.inner.try_lock() {
<some action>
}
async fn <zbus method>() {
let lock = self.inner.lock().await;
<some action>
}
}
```

View File

@@ -1,7 +1,7 @@
[package]
name = "rog_anime"
version = "1.3.5"
license = "MPL-2.0"
version.workspace = true
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
@@ -9,26 +9,25 @@ homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl"
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 = "2018"
edition = "2021"
exclude = ["data"]
[features]
default = ["dbus", "detect"]
dbus = ["zvariant", "zbus"]
dbus = ["zbus"]
detect = ["sysfs-class"]
[dependencies]
png_pong = "^0.8.0"
pix = "0.13"
gif = "^0.11.2"
log = "*"
png_pong.workspace = true
pix.workspace = true
gif.workspace = true
log.workspace = true
serde = "^1.0"
serde_derive = "^1.0"
serde.workspace = true
serde_derive.workspace = true
glam = { version = "^0.21.2", features = ["serde"] }
glam.workspace = true
zvariant = { version = "^3.0", optional = true }
zbus = { version = "^2.2", optional = true }
zbus = { workspace = true, optional = true }
sysfs-class = { version = "^0.1", optional = true }
sysfs-class = { workspace = true, optional = true }

View File

@@ -7,7 +7,7 @@ use std::{
use log::info;
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zvariant::Type;
use zbus::zvariant::Type;
use crate::{
error::{AnimeError, Result},
@@ -29,7 +29,7 @@ const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
const USB_PREFIX3: [u8; 7] = [0x5e, 0xc0, 0x02, 0xe7, 0x04, 0x73, 0x02];
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, PartialEq, Copy, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
pub struct AnimePowerStates {
pub brightness: u8,
pub enabled: bool,
@@ -47,8 +47,7 @@ impl AnimeType {
/// The width of diagonal images
pub fn width(&self) -> usize {
match self {
AnimeType::GA401 => 74,
AnimeType::GA402 => 74,
AnimeType::GA401 | AnimeType::GA402 => 74,
}
}
@@ -103,8 +102,8 @@ impl AnimeDataBuffer {
/// Create from a vector of bytes
///
/// # Panics
/// Will panic if the vector length is not `ANIME_DATA_LEN`
/// # Errors
/// Will error if the vector length is not `ANIME_DATA_LEN`
#[inline]
pub fn from_vec(anime: AnimeType, data: Vec<u8>) -> Result<Self> {
if data.len() != anime.data_length() {
@@ -147,10 +146,7 @@ impl TryFrom<AnimeDataBuffer> for AnimePacketType {
/// This runs the animations as a blocking loop by using the `callback` to write data
///
/// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early.
pub fn run_animation(
frames: &AnimeGif,
callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>,
) -> Result<()> {
pub fn run_animation(frames: &AnimeGif, callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>) {
let mut count = 0;
let start = Instant::now();
@@ -215,9 +211,10 @@ pub fn run_animation(
}
}
// TODO: Log this error
if matches!(callback(output), Ok(true)) {
info!("rog-anime: frame-loop callback asked to exit early");
return Ok(());
return;
}
if timed && Instant::now().duration_since(start) > run_time {
@@ -232,5 +229,4 @@ pub fn run_animation(
}
}
}
Ok(())
}

View File

@@ -52,39 +52,43 @@ impl AnimeDiagonal {
let mut matrix = AnimeDiagonal::new(anime_type, duration);
match raster {
match &raster {
png_pong::PngRaster::Gray8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
Self::pixels_from_8bit(ras, &mut matrix, bright, true);
}
png_pong::PngRaster::Graya8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
Self::pixels_from_8bit(ras, &mut matrix, bright, true);
}
png_pong::PngRaster::Rgb8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
Self::pixels_from_8bit(ras, &mut matrix, bright, false);
}
png_pong::PngRaster::Rgba8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
Self::pixels_from_8bit(ras, &mut matrix, bright, false);
}
png_pong::PngRaster::Gray16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, true)
Self::pixels_from_16bit(ras, &mut matrix, bright, true);
}
png_pong::PngRaster::Rgb16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, false)
Self::pixels_from_16bit(ras, &mut matrix, bright, false);
}
png_pong::PngRaster::Graya16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, true)
Self::pixels_from_16bit(ras, &mut matrix, bright, true);
}
png_pong::PngRaster::Rgba16(ras) => {
Self::pixels_from_16bit(ras, &mut matrix, bright, false)
Self::pixels_from_16bit(ras, &mut matrix, bright, false);
}
_ => return Err(AnimeError::Format),
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
};
Ok(matrix)
}
fn pixels_from_8bit<P>(ras: pix::Raster<P>, matrix: &mut AnimeDiagonal, bright: f32, grey: bool)
where
fn pixels_from_8bit<P>(
ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal,
bright: f32,
grey: bool,
) where
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{
let width = ras.width();
@@ -105,7 +109,7 @@ impl AnimeDiagonal {
}
fn pixels_from_16bit<P>(
ras: pix::Raster<P>,
ras: &pix::Raster<P>,
matrix: &mut AnimeDiagonal,
bright: f32,
grey: bool,
@@ -131,14 +135,14 @@ impl AnimeDiagonal {
#[inline]
pub fn into_data_buffer(&self, anime_type: AnimeType) -> Result<AnimeDataBuffer> {
match anime_type {
AnimeType::GA401 => self.into_ga401_packets(),
AnimeType::GA402 => self.into_ga402_packets(),
AnimeType::GA401 => self.to_ga401_packets(),
AnimeType::GA402 => self.to_ga402_packets(),
}
}
/// Do conversion from the nested Vec in AnimeMatrix to the two required
/// Do conversion from the nested Vec in `AnimeMatrix` to the two required
/// packets suitable for sending over USB
fn into_ga401_packets(&self) -> Result<AnimeDataBuffer> {
fn to_ga401_packets(&self) -> Result<AnimeDataBuffer> {
let mut buf = vec![0u8; AnimeType::GA401.data_length()];
buf[1..=32].copy_from_slice(&self.get_row(0, 3, 32));
@@ -200,12 +204,12 @@ impl AnimeDiagonal {
AnimeDataBuffer::from_vec(crate::AnimeType::GA401, buf)
}
fn into_ga402_packets(&self) -> Result<AnimeDataBuffer> {
fn to_ga402_packets(&self) -> Result<AnimeDataBuffer> {
let mut buf = vec![0u8; AnimeType::GA402.data_length()];
let mut start_index: usize = 0;
fn copy_slice(
buf: &mut Vec<u8>,
buf: &mut [u8],
anime: &AnimeDiagonal,
x: usize,
y: usize,

View File

@@ -26,7 +26,7 @@ pub enum AnimeError {
impl fmt::Display for AnimeError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AnimeError::NoFrames => write!(f, "No frames in PNG"),
AnimeError::Io(e) => write!(f, "Could not open: {}", e),

View File

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

View File

@@ -30,7 +30,7 @@ impl Default for Pixel {
/// is to be used to sample an image and set the LED brightness.
///
/// The position of the Led in `LedPositions` determines the placement in the final
/// data packets when written to the AniMe.
/// data packets when written to the `AniMe`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Led(f32, f32, u8);
@@ -58,7 +58,7 @@ impl Led {
/// Container of `Led`, each of which specifies a position within the image
/// The main use of this is to position and sample colours for the final image
/// to show on AniMe
/// to show on `AniMe`
pub struct AnimeImage {
pub scale: Vec2,
/// Angle in radians
@@ -88,7 +88,7 @@ impl AnimeImage {
width: u32,
anime_type: AnimeType,
) -> Result<Self> {
if bright < 0.0 || bright > 1.0 {
if !(0.0..=1.0).contains(&bright) {
return Err(AnimeError::InvalidBrightness(bright));
}
@@ -294,8 +294,8 @@ impl AnimeImage {
let x0 = led_from_px.mul_vec3(pos + Vec3::new(0.0, -0.5, 0.0));
const GROUP: [f32; 4] = [0.0, 0.5, 1.0, 1.5];
for u in GROUP.iter() {
for v in GROUP.iter() {
for u in &GROUP {
for v in &GROUP {
let sample = x0 + *u * du + *v * dv;
let x = sample.x as i32;
@@ -399,7 +399,7 @@ impl AnimeImage {
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
let width;
let pixels = match raster {
let pixels = match &raster {
png_pong::PngRaster::Gray8(ras) => {
width = ras.width();
Self::pixels_from_8bit(ras, true)
@@ -432,7 +432,7 @@ impl AnimeImage {
width = ras.width();
Self::pixels_from_16bit(ras, false)
}
_ => return Err(AnimeError::Format),
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
};
let mut matrix = AnimeImage::new(
@@ -449,7 +449,7 @@ impl AnimeImage {
Ok(matrix)
}
fn pixels_from_8bit<P>(ras: pix::Raster<P>, grey: bool) -> Vec<Pixel>
fn pixels_from_8bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
{
@@ -468,7 +468,7 @@ impl AnimeImage {
.collect()
}
fn pixels_from_16bit<P>(ras: pix::Raster<P>, grey: bool) -> Vec<Pixel>
fn pixels_from_16bit<P>(ras: &pix::Raster<P>, grey: bool) -> Vec<Pixel>
where
P: pix::el::Pixel<Chan = pix::chan::Ch16>,
{
@@ -491,7 +491,7 @@ impl AnimeImage {
impl TryFrom<&AnimeImage> for AnimeDataBuffer {
type Error = AnimeError;
/// Do conversion from the nested Vec in AnimeDataBuffer to the two required
/// Do conversion from the nested Vec in `AnimeDataBuffer` to the two required
/// packets suitable for sending over USB
fn try_from(leds: &AnimeImage) -> Result<Self> {
let mut l: Vec<u8> = leds

View File

@@ -7,11 +7,11 @@ pub use data::*;
mod grid;
pub use grid::*;
/// Transform a PNG image for displaying on AniMe matrix display
/// Transform a PNG image for displaying on `AniMe` matrix display
mod image;
pub use image::*;
/// A grid of data that is intended to be read out and displayed on the ANiMe as
/// A grid of data that is intended to be read out and displayed on the `AniMe` as
/// a diagonal
mod diagonal;
pub use diagonal::*;

View File

@@ -7,7 +7,7 @@ use crate::{
error::Result, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType,
};
/// All the possible AniMe actions that can be used. This enum is intended to be
/// All the possible `AniMe` actions that can be used. This enum is intended to be
/// a helper for loading up `ActionData`.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ActionLoader {
@@ -44,7 +44,7 @@ pub enum ActionLoader {
Pause(Duration),
}
/// All the possible AniMe actions that can be used. The enum is intended to be
/// All the possible `AniMe` actions that can be used. The enum is intended to be
/// used in a array allowing the user to cycle through a series of actions.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ActionData {
@@ -194,7 +194,7 @@ impl Sequences {
None
}
pub fn iter(&self) -> ActionIterator {
pub fn iter(&self) -> ActionIterator<'_> {
ActionIterator {
actions: self,
next_idx: 0,

View File

@@ -1,4 +1,4 @@
//! Utils for writing to the AniMe USB device
//! Utils for writing to the `AniMe` USB device
//!
//! Use of the device requires a few steps:
//! 1. Initialise the device by writing the two packets from `get_init_packets()`
@@ -26,9 +26,7 @@ pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name()?;
if board_name.contains("GA401I") {
return Ok(AnimeType::GA401);
} else if board_name.contains("GA401Q") {
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
return Ok(AnimeType::GA401);
} else if board_name.contains("GA402R") {
return Ok(AnimeType::GA402);
@@ -65,7 +63,7 @@ pub const fn pkt_for_flush() -> [u8; PACKET_SIZE] {
}
/// Get the packet required for setting the device to on, on boot. Requires
/// pkt_for_apply()` to be written after.
/// `pkt_for_apply()` to be written after.
#[inline]
pub const fn pkt_for_set_boot(status: bool) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE];

View File

@@ -1,7 +1,7 @@
[package]
name = "rog_aura"
version = "1.3.3"
license = "MPL-2.0"
version.workspace = true
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asusctl"
@@ -9,18 +9,18 @@ homepage = "https://gitlab.com/asus-linux/asusctl"
documentation = "https://docs.rs/rog-anime"
description = "Types useful for fancy keyboards on ASUS ROG laptops"
keywords = ["ROG", "ASUS", "Aura"]
edition = "2018"
edition = "2021"
exclude = ["data"]
[features]
default = ["dbus", "toml"]
dbus = ["zvariant"]
dbus = ["zbus"]
[dependencies]
serde = "^1.0"
serde_derive = "^1.0"
toml = { version = "^0.5", optional = true }
zvariant = { version = "^3.0", optional = true }
serde.workspace = true
serde_derive.workspace = true
toml = { workspace = true, optional = true }
zbus = { workspace = true, optional = true }
[dev-dependencies]
serde_json = "^1.0"
serde_json.workspace = true

View File

@@ -5,14 +5,14 @@ pub const LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e
pub const LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
use serde_derive::{Deserialize, Serialize};
use std::str::FromStr;
use std::{fmt::Display, str::FromStr};
#[cfg(feature = "dbus")]
use zvariant::Type;
use zbus::zvariant::Type;
use crate::{error::Error, LED_MSG_LEN};
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum LedBrightness {
Off,
Low,
@@ -25,7 +25,6 @@ impl From<u32> for LedBrightness {
match bright {
0 => LedBrightness::Off,
1 => LedBrightness::Low,
2 => LedBrightness::Med,
3 => LedBrightness::High,
_ => LedBrightness::Med,
}
@@ -33,7 +32,7 @@ impl From<u32> for LedBrightness {
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, PartialEq, PartialOrd, Copy, Deserialize, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Copy, Deserialize, Serialize)]
pub struct Colour(pub u8, pub u8, pub u8);
impl Default for Colour {
@@ -85,7 +84,7 @@ impl From<Colour> for [u8; 3] {
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum Speed {
Low = 0xe1,
Med = 0xeb,
@@ -123,7 +122,7 @@ impl From<Speed> for u8 {
///
/// Enum corresponds to the required integer value
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum Direction {
Right,
Left,
@@ -171,23 +170,15 @@ pub enum AuraModeNum {
Flash = 12,
}
impl Display for AuraModeNum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", <&str>::from(self))
}
}
impl From<AuraModeNum> for String {
fn from(mode: AuraModeNum) -> Self {
match mode {
AuraModeNum::Static => "Static",
AuraModeNum::Breathe => "Breathe",
AuraModeNum::Strobe => "Strobe",
AuraModeNum::Rainbow => "Rainbow",
AuraModeNum::Star => "Stars",
AuraModeNum::Rain => "Rain",
AuraModeNum::Highlight => "Highlight",
AuraModeNum::Laser => "Laser",
AuraModeNum::Ripple => "Ripple",
AuraModeNum::Pulse => "Pulse",
AuraModeNum::Comet => "Comet",
AuraModeNum::Flash => "Flash",
}
.to_string()
<&str>::from(&mode).to_owned()
}
}
@@ -212,7 +203,6 @@ impl From<&AuraModeNum> for &str {
impl From<&str> for AuraModeNum {
fn from(mode: &str) -> Self {
match mode {
"Static" => AuraModeNum::Static,
"Breathe" => AuraModeNum::Breathe,
"Strobe" => AuraModeNum::Strobe,
"Rainbow" => AuraModeNum::Rainbow,
@@ -232,7 +222,6 @@ impl From<&str> for AuraModeNum {
impl From<u8> for AuraModeNum {
fn from(mode: u8) -> Self {
match mode {
0 => AuraModeNum::Static,
1 => AuraModeNum::Breathe,
2 => AuraModeNum::Strobe,
3 => AuraModeNum::Rainbow,
@@ -251,7 +240,7 @@ impl From<u8> for AuraModeNum {
/// Base effects have no zoning, while multizone is 1-4
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Default, Copy, Clone, PartialEq, Deserialize, Serialize)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum AuraZone {
/// Used if keyboard has no zones, or if setting all
#[default]
@@ -278,22 +267,14 @@ impl FromStr for AuraZone {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.to_ascii_lowercase().as_str() {
"0" => Ok(AuraZone::None),
"none" => Ok(AuraZone::None),
"1" => Ok(AuraZone::Key1),
"one" => Ok(AuraZone::Key1),
"2" => Ok(AuraZone::Key2),
"two" => Ok(AuraZone::Key2),
"3" => Ok(AuraZone::Key3),
"three" => Ok(AuraZone::Key3),
"4" => Ok(AuraZone::Key4),
"four" => Ok(AuraZone::Key4),
"5" => Ok(AuraZone::Logo),
"logo" => Ok(AuraZone::Logo),
"6" => Ok(AuraZone::BarLeft),
"lightbar-left" => Ok(AuraZone::BarLeft),
"7" => Ok(AuraZone::BarRight),
"lightbar-right" => Ok(AuraZone::BarRight),
"0" | "none" => Ok(AuraZone::None),
"1" | "one" => Ok(AuraZone::Key1),
"2" | "two" => Ok(AuraZone::Key2),
"3" | "three" => Ok(AuraZone::Key3),
"4" | "four" => Ok(AuraZone::Key4),
"5" | "logo" => Ok(AuraZone::Logo),
"6" | "lightbar-left" => Ok(AuraZone::BarLeft),
"7" | "lightbar-right" => Ok(AuraZone::BarRight),
_ => Err(Error::ParseSpeed),
}
}
@@ -325,8 +306,8 @@ impl AuraEffect {
&self.mode
}
pub fn mode_name(&self) -> String {
(<&str>::from(&self.mode)).to_string()
pub fn mode_name(&self) -> &str {
<&str>::from(&self.mode)
}
pub fn mode_num(&self) -> u8 {
@@ -389,18 +370,20 @@ impl AuraEffect {
/// factory mode accepts only one colour.
pub const fn allowed_parameters(mode: AuraModeNum) -> AuraParameters {
match mode {
AuraModeNum::Static => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Static
| AuraModeNum::Highlight
| AuraModeNum::Pulse
| AuraModeNum::Comet
| AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Breathe => AuraParameters::new(true, true, true, true, false),
AuraModeNum::Strobe => AuraParameters::new(true, false, false, true, false),
AuraModeNum::Strobe | AuraModeNum::Rain => {
AuraParameters::new(true, false, false, true, false)
}
AuraModeNum::Rainbow => AuraParameters::new(true, false, false, true, true),
AuraModeNum::Star => AuraParameters::new(true, true, true, true, true),
AuraModeNum::Rain => AuraParameters::new(true, false, false, true, false),
AuraModeNum::Highlight => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Laser => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Ripple => AuraParameters::new(true, true, false, true, false),
AuraModeNum::Pulse => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Comet => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Flash => AuraParameters::new(true, true, false, false, false),
AuraModeNum::Laser | AuraModeNum::Ripple => {
AuraParameters::new(true, true, false, true, false)
}
}
}
}

View File

@@ -13,7 +13,7 @@ pub enum Error {
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::ParseColour => write!(f, "Could not parse colour"),
Error::ParseSpeed => write!(f, "Could not parse speed"),

View File

@@ -139,17 +139,17 @@ impl From<&Key> for &str {
Key::MediaStop => "Media Stop",
Key::MediaNext => "Media Next",
Key::MediaPrev => "Media Previous",
Key::NormalBlank => "",
Key::NormalSpacer => "",
Key::FuncBlank => "",
Key::FuncSpacer => "",
Key::ArrowBlank => "",
Key::ArrowSpacer => "",
Key::ArrowRegularBlank => "",
Key::ArrowRegularSpacer => "",
Key::ArrowSplitBlank => "",
Key::ArrowSplitSpacer => "",
Key::RowEndSpacer => "",
Key::NormalBlank
| Key::NormalSpacer
| Key::FuncBlank
| Key::FuncSpacer
| Key::ArrowBlank
| Key::ArrowSpacer
| Key::ArrowRegularBlank
| Key::ArrowRegularSpacer
| Key::ArrowSplitBlank
| Key::ArrowSplitSpacer
| Key::RowEndSpacer => "",
}
}
}

View File

@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum Key {
VolUp,
VolDown,
@@ -203,17 +203,22 @@ pub enum KeyShape {
impl KeyShape {
pub const fn width(&self) -> f32 {
match self {
Self::Tilde => 0.8,
Self::Normal => 1.0,
Self::NormalBlank => 1.0,
Self::NormalSpacer => 1.0,
Self::Func => 1.0,
Self::FuncBlank => 1.0,
Self::Tilde | Self::Arrow => 0.8,
Self::Normal
| Self::NormalBlank
| Self::NormalSpacer
| Self::Func
| Self::FuncBlank
| Self::Space5
| Self::ArrowBlank
| Self::ArrowSpacer
| Self::ArrowSplit
| Self::ArrowSplitBlank
| Self::ArrowSplitSpacer => 1.0,
Self::FuncSpacer => 0.6,
Self::Space => 5.0,
Self::Space5 => 1.0,
Self::LCtrlMed => 1.1,
Self::LShift => 2.0,
Self::LShift | Self::Backspace => 2.0,
Self::LShift3 => 0.67,
Self::RShift => 2.8,
Self::RshiftSmall => 1.8,
@@ -222,12 +227,9 @@ impl KeyShape {
Self::Return3 => 0.7333,
Self::Tab => 1.4,
Self::Caps => 1.6,
Self::Backspace => 2.0,
Self::Backspace3 => 0.666,
Self::ArrowRegularBlank | Self::ArrowRegularSpacer => 0.7,
Self::Arrow => 0.8,
Self::ArrowBlank | Self::ArrowSpacer => 1.0,
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => 1.0,
Self::RowEndSpacer => 0.1,
}
}
@@ -235,51 +237,44 @@ impl KeyShape {
/// A blank is used to space keys out in GUI's and can be used or ignored
/// depednign on the per-key effect
pub const fn is_blank(&self) -> bool {
match self {
matches!(
self,
Self::NormalBlank
| Self::FuncBlank
| Self::ArrowBlank
| Self::ArrowSplitBlank
| Self::ArrowRegularBlank => true,
_ => false,
}
| Self::FuncBlank
| Self::ArrowBlank
| Self::ArrowSplitBlank
| Self::ArrowRegularBlank
)
}
/// A spacer is used to space keys out in GUI's, but ignored in per-key effects
pub const fn is_spacer(&self) -> bool {
match self {
matches!(
self,
Self::FuncSpacer
| Self::NormalSpacer
| Self::ArrowSpacer
| Self::ArrowSplitSpacer
| Self::ArrowRegularSpacer => true,
_ => false,
}
| Self::NormalSpacer
| Self::ArrowSpacer
| Self::ArrowSplitSpacer
| Self::ArrowRegularSpacer
)
}
/// All keys with a postfix of some number
pub const fn is_group(&self) -> bool {
match self {
Self::LShift3 | Self::RShift3 => true,
Self::Return3 | Self::Space5 | Self::Backspace3 => true,
_ => false,
}
matches!(
self,
Self::LShift3 | Self::RShift3 | Self::Return3 | Self::Space5 | Self::Backspace3
)
}
/// Mostly intended as a helper for signalling when to draw a
/// split/compact arrow cluster
pub const fn is_arrow_cluster(&self) -> bool {
match self {
Self::Arrow | Self::ArrowBlank | Self::ArrowSpacer => true,
_ => false,
}
matches!(self, Self::Arrow | Self::ArrowBlank | Self::ArrowSpacer)
}
pub const fn is_arrow_splits(&self) -> bool {
match self {
Self::ArrowSplit | Self::ArrowSplitBlank | Self::ArrowSplitSpacer => true,
_ => false,
}
matches!(self, Self::Arrow | Self::ArrowBlank | Self::ArrowSpacer)
}
}

View File

@@ -6,7 +6,7 @@ impl KeyLayout {
pub fn g513_layout() -> Self {
Self {
matches: vec!["G513".into()],
locale: "US".to_string(),
locale: "US".to_owned(),
rows: vec![
KeyRow::new(
0.8,

View File

@@ -5,7 +5,7 @@ impl KeyLayout {
pub fn ga401_layout() -> Self {
Self {
matches: vec!["GA401".into(), "GA402".into()],
locale: "US".to_string(),
locale: "US".to_owned(),
rows: vec![
KeyRow::new(
0.8,

View File

@@ -5,7 +5,7 @@ impl KeyLayout {
pub fn gx502_layout() -> Self {
Self {
matches: vec!["GX502".into(), "GU502".into()],
locale: "US".to_string(),
locale: "US".to_owned(),
rows: vec![
KeyRow::new(
0.8,

View File

@@ -35,15 +35,15 @@ impl KeyLayout {
let mut buf = String::new();
let read_len = file.read_to_string(&mut buf)?;
if read_len == 0 {
return Err(Error::Io(std::io::ErrorKind::InvalidData.into()));
Err(Error::Io(std::io::ErrorKind::InvalidData.into()))
} else {
return Ok(toml::from_str::<Self>(&buf)?);
Ok(toml::from_str::<Self>(&buf)?)
}
}
pub fn matches(&self, board_name: &str) -> bool {
let board = board_name.to_ascii_uppercase();
for tmp in self.matches.iter() {
for tmp in &self.matches {
if board.contains(tmp.as_str()) {
return true;
}
@@ -91,7 +91,7 @@ impl KeyRow {
Self { height, row }
}
pub fn row(&self) -> Iter<Key> {
pub fn row(&self) -> Iter<'_, Key> {
self.row.iter()
}

View File

@@ -1,7 +1,7 @@
use crate::keys::Key;
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zvariant::Type;
use zbus::zvariant::Type;
/// Represents the per-key raw USB packets
pub type PerKeyRaw = Vec<Vec<u8>>;

View File

@@ -1,6 +1,6 @@
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zvariant::Type;
use zbus::zvariant::Type;
/// Represents the zoned raw USB packets
pub type ZonedRaw = Vec<u8>;
@@ -43,8 +43,7 @@ impl ZonedColourArray {
pub fn rgb_for_zone(&mut self, zone: PerZone) -> &mut [u8] {
match zone {
PerZone::None => &mut self.0[9..=11],
PerZone::KeyboardLeft => &mut self.0[9..=11],
PerZone::None | PerZone::KeyboardLeft => &mut self.0[9..=11],
PerZone::KeyboardCenterLeft => &mut self.0[12..=14],
PerZone::KeyboardCenterRight => &mut self.0[15..=17],
PerZone::KeyboardRight => &mut self.0[18..=20],

View File

@@ -138,16 +138,11 @@ impl EffectState for Breathe {
let speed = 4 - <u8>::from(*speed);
let colour: &mut Colour;
if *colour_actual == Colour(0, 0, 0) {
*use_colour1 = !*use_colour1;
}
if !*use_colour1 {
colour = colour2;
} else {
colour = colour1;
}
let colour = if !*use_colour1 { colour2 } else { colour1 };
let r1_scale = colour.0 / speed / 2;
let g1_scale = colour.1 / speed / 2;

View File

@@ -9,7 +9,7 @@ use serde_derive::{Deserialize, Serialize};
// static mut RNDINDEX: usize = 0;
static mut PRNDINDEX: usize = 0;
/// Pseudo random table ripped straight out of Room4Doom
/// Pseudo random table ripped straight out of room4doom
pub const RNDTABLE: [i32; 256] = [
0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, 21, 211, 47, 80, 242, 154,
27, 205, 128, 161, 89, 77, 36, 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188,
@@ -88,7 +88,7 @@ impl Sequences {
}
pub fn next_state(&mut self, layout: &KeyLayout) {
for effect in self.0.iter_mut() {
for effect in &mut self.0 {
effect.next_state(layout);
}
}
@@ -97,7 +97,7 @@ impl Sequences {
let mut keys = KeyColourArray::new();
let mut zones = ZonedColourArray::new();
let mut is_per_key = false;
for effect in self.0.iter() {
for effect in &self.0 {
match effect.get_led_type() {
LedType::Key(key) => {
is_per_key = true;

View File

@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use std::ops::{BitAnd, BitOr};
#[cfg(feature = "dbus")]
use zvariant::Type;
use zbus::zvariant::Type;
pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9];
pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
@@ -21,7 +21,7 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
}
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize, Default)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize, Default)]
pub enum AuraDevice {
Tuf,
X1854,
@@ -36,14 +36,10 @@ impl From<&str> for AuraDevice {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"tuf" => AuraDevice::Tuf,
"1866" => AuraDevice::X1866,
"1869" => AuraDevice::X1869,
"1854" => AuraDevice::X1854,
"19b6" => AuraDevice::X19B6,
"0x1866" => AuraDevice::X1866,
"0x1869" => AuraDevice::X1869,
"0x1854" => AuraDevice::X1854,
"0x19b6" => AuraDevice::X19B6,
"1866" | "0x1866" => AuraDevice::X1866,
"1869" | "0x1869" => AuraDevice::X1869,
"1854" | "0x1854" => AuraDevice::X1854,
"19b6" | "0x19b6" => AuraDevice::X19B6,
_ => AuraDevice::Unknown,
}
}
@@ -108,9 +104,9 @@ impl From<AuraDev1866> for u32 {
impl AuraDev1866 {
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
let mut a: u32 = 0;
control.iter().for_each(|n| {
for n in control {
a |= *n as u32;
});
}
[
((a & 0xff0000) >> 16) as u8,
((a & 0xff00) >> 8) as u8,
@@ -127,7 +123,7 @@ impl BitOr<AuraDev1866> for AuraDev1866 {
type Output = u32;
fn bitor(self, rhs: AuraDev1866) -> Self::Output {
return self as u32 | rhs as u32;
self as u32 | rhs as u32
}
}
@@ -135,7 +131,7 @@ impl BitAnd<AuraDev1866> for AuraDev1866 {
type Output = u32;
fn bitand(self, rhs: AuraDev1866) -> Self::Output {
return self as u32 & rhs as u32;
self as u32 & rhs as u32
}
}
@@ -178,14 +174,14 @@ pub enum AuraDev19b6 {
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,
BootBar = 1 << (7 + 2),
AwakeBar = 1 << (7 + 3),
SleepBar = 1 << (7 + 4),
ShutdownBar = 1 << (7 + 5),
BootLid = 1 << (15 + 1),
AwakeLid = 1 << (15 + 2),
SleepLid = 1 << (15 + 3),
ShutdownLid = 1 << (15 + 4),
}
impl From<AuraDev19b6> for u32 {
@@ -197,9 +193,9 @@ impl From<AuraDev19b6> for u32 {
impl AuraDev19b6 {
pub fn to_bytes(control: &[Self]) -> [u8; 3] {
let mut a: u32 = 0;
control.iter().for_each(|n| {
for n in control {
a |= *n as u32;
});
}
[
(a & 0xff) as u8,
((a & 0xff00) >> 8) as u8,
@@ -216,7 +212,7 @@ impl BitOr<AuraDev19b6> for AuraDev19b6 {
type Output = u16;
fn bitor(self, rhs: AuraDev19b6) -> Self::Output {
return self as u16 | rhs as u16;
self as u16 | rhs as u16
}
}
@@ -224,7 +220,7 @@ impl BitAnd<AuraDev19b6> for AuraDev19b6 {
type Output = u16;
fn bitand(self, rhs: AuraDev19b6) -> Self::Output {
return self as u16 & rhs as u16;
self as u16 & rhs as u16
}
}

View File

@@ -1,6 +1,7 @@
[package]
name = "rog-control-center"
version = "1.1.1"
license = "MPL-2.0"
version.workspace = true
authors = ["Luke D. Jones <luke@ljones.dev>"]
edition = "2021"
@@ -8,9 +9,11 @@ edition = "2021"
mocking = []
[dependencies]
egui = { git = "https://github.com/emilk/egui" }
eframe= { git = "https://github.com/emilk/egui" }
#eframe= { git = "https://github.com/emilk/egui", default-features = false, features = ["dark-light", "default_fonts", "wgpu"] }
egui = { git = "https://github.com/flukejones/egui", branch = "wayland_dark_theme" }
eframe= { git = "https://github.com/flukejones/egui", branch = "wayland_dark_theme" }
libappindicator = "0.7" # Tray icon
gtk = "0.15.5"
daemon = { path = "../daemon" }
rog_anime = { path = "../rog-anime" }
@@ -18,20 +21,24 @@ 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" }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false }
#supergfxctl = { path = "../../supergfxctl", default-features = false }
smol = "^1.2"
log.workspace = true
env_logger.workspace = true
serde = "^1.0"
toml = "^0.5"
serde_json = "^1.0"
serde_derive = "^1.0"
zbus = "^2.3"
nix = "^0.20.0"
tempfile = "3.2.0"
dirs = "3.0.1"
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
[dependencies.notify-rust]
version = "^4.3"
default-features = false
features = ["z"]
png_pong.workspace = true
nix = "^0.26.1"
tempfile = "3.3.0"
once_cell = "1.10.0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -2,7 +2,7 @@ use std::{
f64::consts::PI,
sync::{
atomic::{AtomicBool, AtomicU8, Ordering},
Arc,
Arc, Mutex,
},
time::{Duration, Instant},
};
@@ -11,16 +11,15 @@ use egui::{Button, RichText};
use rog_platform::supported::SupportedFunctions;
use crate::{
config::Config, error::Result, page_states::PageDataStates, Page, RogDbusClientBlocking,
config::Config, error::Result, system_state::SystemState, Page, RogDbusClientBlocking,
};
pub struct RogApp<'a> {
pub struct RogApp {
pub page: Page,
pub states: PageDataStates,
pub states: Arc<Mutex<SystemState>>,
pub supported: SupportedFunctions,
// TODO: can probably just open and read whenever
pub config: Config,
pub asus_dbus: RogDbusClientBlocking<'a>,
/// Oscillator in percentage
pub oscillator1: Arc<AtomicU8>,
pub oscillator2: Arc<AtomicU8>,
@@ -31,11 +30,11 @@ pub struct RogApp<'a> {
pub oscillator_toggle: Arc<AtomicBool>,
}
impl<'a> RogApp<'a> {
impl RogApp {
/// Called once before the first frame.
pub fn new(
config: Config,
states: PageDataStates,
states: Arc<Mutex<SystemState>>,
_cc: &eframe::CreationContext<'_>,
) -> Result<Self> {
let (dbus, _) = RogDbusClientBlocking::new()?;
@@ -55,6 +54,7 @@ impl<'a> RogApp<'a> {
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;
@@ -83,6 +83,7 @@ impl<'a> RogApp<'a> {
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));
}
});
@@ -92,7 +93,6 @@ impl<'a> RogApp<'a> {
states,
page: Page::System,
config,
asus_dbus: dbus,
oscillator1,
oscillator2,
oscillator3,
@@ -102,60 +102,63 @@ impl<'a> RogApp<'a> {
}
}
impl<'a> eframe::App for RogApp<'a> {
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 Self {
supported,
asus_dbus: dbus,
states,
..
} = self;
states
.refresh_if_notfied(supported, dbus)
.map(|repaint| {
if repaint {
ctx.request_repaint();
}
})
.map_err(|e| self.states.error = Some(e.to_string()))
.ok();
let 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();
}
}
let page = self.page;
self.top_bar(ctx, frame);
self.side_panel(ctx);
if let Some(err) = self.states.error.clone() {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading(RichText::new("Error!").size(28.0));
let mut was_error = false;
ui.centered_and_justified(|ui| {
ui.label(RichText::new(format!("The error was: {:?}", err)).size(22.0));
});
});
egui::TopBottomPanel::bottom("error_bar")
.default_height(26.0)
.show(ctx, |ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
self.states.error = None;
}
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));
});
});
} else if page == Page::System {
self.system_page(ctx);
} else if page == Page::AuraEffects {
self.aura_page(ctx);
// TODO: Anime page is not complete
// } else if page == Page::AnimeMatrix {
// self.anime_page(ctx);
} else if page == Page::FanCurves {
self.fan_curve_page(ctx);
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);
}
}
}
}
}

View File

@@ -1,22 +1,23 @@
use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use std::{
fs::{create_dir, OpenOptions},
io::{Read, Write},
};
use serde_derive::{Deserialize, Serialize};
//use log::{error, info, warn};
use crate::error::Error;
use crate::{error::Error, update_and_notify::EnabledNotifications};
const CFG_DIR: &str = "rog";
const CFG_FILE_NAME: &str = "rog-control-center.cfg";
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct Config {
pub run_in_background: bool,
pub startup_in_background: bool,
pub enable_notifications: bool,
pub enabled_notifications: EnabledNotifications,
}
impl Default for Config {
@@ -25,6 +26,7 @@ impl Default for Config {
run_in_background: true,
startup_in_background: false,
enable_notifications: true,
enabled_notifications: EnabledNotifications::default(),
}
}
}
@@ -32,14 +34,17 @@ impl Default for Config {
impl Config {
pub fn load() -> Result<Config, Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
info!("Found XDG config dir {dir:?}");
dir
} else {
error!("Could not get XDG config dir");
return Err(Error::XdgVars);
};
path.push(CFG_DIR);
if !path.exists() {
create_dir(path.clone())?;
info!("Created {path:?}");
}
path.push(CFG_FILE_NAME);
@@ -54,18 +59,20 @@ impl Config {
if let Ok(read_len) = file.read_to_string(&mut buf) {
if read_len == 0 {
warn!("Zero len read of Config file");
let default = Config::default();
let t = toml::to_string_pretty(&default).unwrap();
file.write_all(t.as_bytes())?;
return Ok(default);
} else if let Ok(data) = toml::from_str::<Config>(&buf) {
info!("Loaded config file {path:?}");
return Ok(data);
}
}
Err(Error::ConfigLoadFail)
}
pub fn save(&self) -> Result<(), Error> {
pub fn save(&mut self, enabled_notifications: &EnabledNotifications) -> Result<(), Error> {
let mut path = if let Some(dir) = dirs::config_dir() {
dir
} else {
@@ -75,6 +82,7 @@ impl Config {
path.push(CFG_DIR);
if !path.exists() {
create_dir(path.clone())?;
info!("Created {path:?}");
}
path.push(CFG_FILE_NAME);
@@ -85,8 +93,10 @@ impl Config {
.truncate(true)
.open(&path)?;
self.enabled_notifications = enabled_notifications.clone();
let t = toml::to_string_pretty(&self).unwrap();
file.write_all(t.as_bytes())?;
info!("Saved config file {path:?}");
Ok(())
}
}

View File

@@ -10,11 +10,12 @@ pub enum Error {
ConfigLockFail,
XdgVars,
Zbus(zbus::Error),
Notification(notify_rust::error::Error),
}
impl fmt::Display for Error {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "Failed to open: {}", err),
Error::Nix(err) => write!(f, "Error: {}", err),
@@ -22,6 +23,7 @@ impl fmt::Display for Error {
Error::ConfigLockFail => write!(f, "Failed to lock user config"),
Error::XdgVars => write!(f, "XDG environment vars appear unset"),
Error::Zbus(err) => write!(f, "Error: {}", err),
Error::Notification(err) => write!(f, "Notification Error: {}", err),
}
}
}
@@ -45,3 +47,9 @@ impl From<zbus::Error> for Error {
Error::Zbus(err)
}
}
impl From<notify_rust::error::Error> for Error {
fn from(err: notify_rust::error::Error) -> Self {
Error::Notification(err)
}
}

View File

@@ -13,10 +13,11 @@ pub mod config;
pub mod error;
#[cfg(feature = "mocking")]
pub mod mocking;
pub mod notify;
pub mod page_states;
pub mod pages;
pub mod startup_error;
pub mod system_state;
pub mod tray;
pub mod update_and_notify;
pub mod widgets;
#[cfg(feature = "mocking")]
@@ -45,7 +46,7 @@ pub fn print_versions() {
pub const SHOWING_GUI: u8 = 1;
pub const SHOW_GUI: u8 = 2;
#[derive(PartialEq, Clone, Copy)]
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Page {
System,
AuraEffects,
@@ -75,7 +76,7 @@ pub fn on_tmp_dir_exists() -> Result<TempDir, std::io::Error> {
// If the app is running this ends up stacked on top of SHOWING_GUI
ipc_file.write_all(&[SHOW_GUI])?;
// tiny sleep to give the app a chance to respond
sleep(Duration::from_millis(10));
sleep(Duration::from_millis(100));
ipc_file.read(&mut buf).ok();
// First entry is the actual state
@@ -96,7 +97,7 @@ pub fn get_ipc_file() -> Result<File, crate::error::Error> {
let tmp_dir = std::env::temp_dir().join("rog-gui");
let fifo_path = tmp_dir.join("ipc.pipe");
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
if !matches!(e, nix::Error::Sys(nix::errno::Errno::EEXIST)) {
if !matches!(e, nix::errno::Errno::EEXIST) {
return Err(e)?;
}
}

View File

@@ -1,32 +1,52 @@
use eframe::{IconData, NativeOptions};
use log::{error, info, LevelFilter};
use rog_aura::layouts::KeyLayout;
use rog_control_center::tray::init_tray;
use rog_control_center::update_and_notify::EnabledNotifications;
use rog_control_center::{
config::Config, get_ipc_file, notify::start_notifications, on_tmp_dir_exists,
page_states::PageDataStates, print_versions, startup_error::AppErrorShow, RogApp,
RogDbusClientBlocking, SHOWING_GUI, SHOW_GUI,
config::Config, error::Result, get_ipc_file, on_tmp_dir_exists, print_versions,
startup_error::AppErrorShow, system_state::SystemState, update_and_notify::start_notifications,
RogApp, RogDbusClientBlocking, SHOWING_GUI, SHOW_GUI,
};
use rog_platform::supported::SupportedFunctions;
use std::sync::Mutex;
use std::{
fs::OpenOptions,
io::{Read, Write},
path::PathBuf,
sync::{atomic::AtomicBool, Arc},
sync::Arc,
};
use tokio::runtime::Runtime;
#[cfg(not(feature = "mocking"))]
const DATA_DIR: &str = "/usr/share/rog-gui/";
#[cfg(feature = "mocking")]
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
const APP_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/rog-control-center.png";
fn main() -> Result<(), Box<dyn std::error::Error>> {
fn main() -> Result<()> {
print_versions();
let mut logger = env_logger::Builder::new();
logger
.target(env_logger::Target::Stdout)
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
.filter(None, LevelFilter::Info)
.init();
// start tokio
let rt = Runtime::new().expect("Unable to create Runtime");
// Enter the runtime so that `tokio::spawn` is available immediately.
let _enter = rt.enter();
let native_options = eframe::NativeOptions {
decorated: false,
vsync: true,
decorated: true,
transparent: false,
min_window_size: Some(egui::vec2(840.0, 600.0)),
max_window_size: Some(egui::vec2(840.0, 600.0)),
run_and_return: true,
icon_data: Some(load_icon()),
..Default::default()
};
@@ -40,14 +60,28 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
})
.unwrap();
let supported = match dbus.proxies().supported().supported_functions() {
Ok(s) => s,
Err(e) => {
eframe::run_native(
"ROG Control Center",
native_options.clone(),
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
);
SupportedFunctions::default()
}
};
// Startup
let mut config = Config::load()?;
let mut start_closed = config.startup_in_background;
if config.startup_in_background {
config.run_in_background = true;
config.save()?;
let tmp = config.enabled_notifications.clone(); // ends up being a double clone, oh well.
config.save(&tmp)?;
}
let enabled_notifications = EnabledNotifications::tokio_mutex(&config);
// Find and load a matching layout for laptop
let mut file = OpenOptions::new()
@@ -72,27 +106,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.map_err(|e| {
println!("{BOARD_NAME}, {e}");
})
.unwrap_or(KeyLayout::ga401_layout());
// Cheap method to alert to notifications rather than spinning a thread for each
// This is quite different when done in a retained mode app
let charge_notified = Arc::new(AtomicBool::new(false));
let bios_notified = Arc::new(AtomicBool::new(false));
let aura_notified = Arc::new(AtomicBool::new(false));
let anime_notified = Arc::new(AtomicBool::new(false));
let profiles_notified = Arc::new(AtomicBool::new(false));
let fans_notified = Arc::new(AtomicBool::new(false));
let notifs_enabled = Arc::new(AtomicBool::new(config.enable_notifications));
start_notifications(
charge_notified.clone(),
bios_notified.clone(),
aura_notified.clone(),
anime_notified.clone(),
profiles_notified.clone(),
fans_notified.clone(),
notifs_enabled.clone(),
)?;
.unwrap_or_else(|_| KeyLayout::ga401_layout());
// tmp-dir must live to the end of program life
let _tmp_dir = match tempfile::Builder::new()
@@ -104,59 +118,100 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Err(_) => on_tmp_dir_exists().unwrap(),
};
let states = setup_page_state_and_notifs(layout, enabled_notifications, &supported)?;
init_tray(supported, states.clone());
loop {
let states = {
let supported = match dbus.proxies().supported().supported_functions() {
Ok(s) => s,
Err(e) => {
eframe::run_native(
"ROG Control Center",
native_options.clone(),
Box::new(move |_| Box::new(AppErrorShow::new(e.to_string()))),
);
return Ok(());
}
};
PageDataStates::new(
layout.clone(),
notifs_enabled.clone(),
charge_notified.clone(),
bios_notified.clone(),
aura_notified.clone(),
anime_notified.clone(),
profiles_notified.clone(),
fans_notified.clone(),
&supported,
&dbus,
)?
};
if !start_closed {
let mut ipc_file = get_ipc_file().unwrap();
ipc_file.write_all(&[SHOWING_GUI]).unwrap();
eframe::run_native(
"ROG Control Center",
native_options.clone(),
Box::new(move |cc| {
Box::new(RogApp::new(Config::load().unwrap(), states, cc).unwrap())
}),
);
start_app(states.clone(), native_options.clone())?;
}
let config = Config::load().unwrap();
let config = Config::load()?;
if !config.run_in_background {
break;
}
let mut buf = [0u8; 4];
// blocks until it is read, typically the read will happen after a second
// process writes to the IPC (so there is data to actually read)
if get_ipc_file().unwrap().read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
start_closed = false;
continue;
if config.run_in_background {
let mut buf = [0u8; 4];
// blocks until it is read, typically the read will happen after a second
// process writes to the IPC (so there is data to actually read)
if get_ipc_file()?.read(&mut buf).is_ok() && buf[0] == SHOW_GUI {
start_closed = false;
continue;
}
}
dbg!("asda");
}
// loop {
// // This is just a blocker to idle and ensure the reator reacts
// sleep(Duration::from_millis(1000)).await;
// }
Ok(())
}
fn setup_page_state_and_notifs(
keyboard_layout: KeyLayout,
enabled_notifications: Arc<Mutex<EnabledNotifications>>,
supported: &SupportedFunctions,
) -> Result<Arc<Mutex<SystemState>>> {
let page_states = Arc::new(Mutex::new(SystemState::new(
keyboard_layout,
enabled_notifications.clone(),
supported,
)?));
start_notifications(page_states.clone(), enabled_notifications)?;
Ok(page_states)
}
fn start_app(states: Arc<Mutex<SystemState>>, native_options: NativeOptions) -> Result<()> {
let mut ipc_file = get_ipc_file()?;
ipc_file.write_all(&[SHOWING_GUI])?;
eframe::run_native(
"ROG Control Center",
native_options,
Box::new(move |cc| Box::new(RogApp::new(Config::load().unwrap(), states, cc).unwrap())),
);
Ok(())
}
/// Bah.. the icon dosn't work on wayland anyway, but we'll leave it in for now.
fn load_icon() -> IconData {
let path = PathBuf::from(APP_ICON_PATH);
let mut buf = Vec::new();
let mut rgba = Vec::new();
let mut height = 512;
let mut width = 512;
if path.exists() {
if let Ok(mut file) = OpenOptions::new()
.read(true)
.open(path)
.map_err(|e| error!("Error opening app icon: {e:?}"))
{
file.read_to_end(&mut buf)
.map_err(|e| error!("Error reading app icon: {e:?}"))
.ok();
let data = std::io::Cursor::new(buf);
let decoder = png_pong::Decoder::new(data).unwrap().into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().unwrap().unwrap();
if let png_pong::PngRaster::Rgba8(ras) = raster {
rgba = ras.as_u8_slice().to_vec();
width = ras.width();
height = ras.height();
info!("Loaded app icon. Not actually supported in Wayland yet");
}
}
} else {
error!("Missing {APP_ICON_PATH}");
}
IconData {
height,
width,
rgba,
}
}

View File

@@ -65,7 +65,7 @@ impl Bios {
pub fn gpu_mux_mode(&self) -> Result<i16> {
Ok(1)
}
pub fn panel_overdrive(&self) -> Result<i16> {
pub fn panel_od(&self) -> Result<i16> {
Ok(1)
}
pub fn set_post_boot_sound(&self, _b: bool) -> Result<()> {
@@ -74,7 +74,7 @@ impl Bios {
pub fn set_gpu_mux_mode(&self, _b: bool) -> Result<()> {
Ok(())
}
pub fn set_panel_overdrive(&self, _b: bool) -> Result<()> {
pub fn set_panel_od(&self, _b: bool) -> Result<()> {
Ok(())
}
}
@@ -217,7 +217,7 @@ impl Supported {
rog_bios_ctrl: RogBiosSupportedFunctions {
post_sound: true,
dedicated_gfx: true,
panel_overdrive: true,
panel_od: true,
dgpu_disable: true,
egpu_enable: true,
},

View File

@@ -1,256 +0,0 @@
//TODO: a lot of app state refresh depends on this so there needs
// to be an extra AtomicBool for checking if notifications are enabled
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_aura::AuraEffect;
use rog_dbus::{
zbus_anime::AnimeProxy, zbus_charge::ChargeProxy, zbus_led::LedProxy,
zbus_platform::RogBiosProxy, zbus_profile::ProfileProxy,
};
use rog_profiles::Profile;
use smol::{future, Executor};
use std::{
error::Error,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
thread::spawn,
};
use zbus::export::futures_util::StreamExt;
const NOTIF_HEADER: &str = "ROG Control";
macro_rules! notify {
($notifier:ident, $last_notif:ident, $data:expr) => {
if let Some(notif) = $last_notif.take() {
notif.close();
}
if let Ok(x) = $notifier($data) {
$last_notif.replace(x);
}
};
}
macro_rules! base_notification {
($body:expr) => {
Notification::new()
.summary(NOTIF_HEADER)
.body($body)
.timeout(2000)
.show()
};
}
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
pub fn start_notifications(
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
aura_notified: Arc<AtomicBool>,
anime_notified: Arc<AtomicBool>,
profiles_notified: Arc<AtomicBool>,
_fans_notified: Arc<AtomicBool>,
notifs_enabled: Arc<AtomicBool>,
) -> Result<(), Box<dyn std::error::Error>> {
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
let executor = Executor::new();
// BIOS notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
let bios_notified1 = bios_notified.clone();
// TODO: make a macro or generic function or something...
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = RogBiosProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_post_boot_sound().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_post_sound_notif, lock, &out.sound());
}
}
bios_notified1.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = RogBiosProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_panel_overdrive().await {
p.for_each(|_| {
bios_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
// Charge notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ChargeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_charge().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_charge_notif, lock, &out.limit);
}
}
charge_notified.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
// Profile notif
let last_notif = last_notification.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = ProfileProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_profile().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_thermal_notif, lock, &out.profile);
}
}
profiles_notified.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
// LED notif
let last_notif = last_notification.clone();
let aura_notif = aura_notified.clone();
let notifs_enabled1 = notifs_enabled.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_led().await {
p.for_each(|e| {
if let Ok(out) = e.args() {
if notifs_enabled1.load(Ordering::SeqCst) {
if let Ok(ref mut lock) = last_notif.try_lock() {
notify!(do_led_notif, lock, &out.data);
}
}
aura_notif.store(true, Ordering::SeqCst);
}
future::ready(())
})
.await;
};
})
.detach();
let aura_notif = aura_notified.clone();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_notify_led().await {
p.for_each(|_| {
aura_notif.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = LedProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_all_signals().await {
p.for_each(|_| {
aura_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
executor
.spawn(async move {
let conn = zbus::Connection::system().await.unwrap();
let proxy = AnimeProxy::new(&conn).await.unwrap();
if let Ok(p) = proxy.receive_power_states().await {
p.for_each(|_| {
anime_notified.store(true, Ordering::SeqCst);
future::ready(())
})
.await;
};
})
.detach();
spawn(move || loop {
smol::block_on(executor.tick());
});
Ok(())
}
fn do_thermal_notif(profile: &Profile) -> Result<NotificationHandle, Box<dyn Error>> {
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let x = Notification::new()
.summary("ASUS ROG")
.body(&format!(
"Thermal profile changed to {}",
profile.to_uppercase(),
))
.hint(Hint::Resident(true))
.timeout(2000)
.hint(Hint::Category("device".into()))
//.hint(Hint::Transient(true))
.icon(icon)
.show()?;
Ok(x)
}
fn do_led_notif(ledmode: &AuraEffect) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!(
"Keyboard LED mode changed to {}",
ledmode.mode_name()
))
}
fn do_charge_notif(limit: &u8) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("Battery charge limit changed to {}", limit))
}
fn do_post_sound_notif(on: &bool) -> Result<NotificationHandle, notify_rust::error::Error> {
base_notification!(&format!("BIOS Post sound {}", on))
}

View File

@@ -1,6 +1,6 @@
use crate::RogApp;
impl<'a> RogApp<'a> {
impl RogApp {
pub fn anime_page(&mut self, ctx: &egui::Context) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("In progress");

View File

@@ -4,16 +4,15 @@ use egui::Color32;
use rog_aura::{AuraEffect, AuraModeNum};
use crate::{
system_state::SystemState,
widgets::{aura_modes_group, keyboard},
RogApp,
};
impl<'a> RogApp<'a> {
pub fn aura_page(&mut self, ctx: &egui::Context) {
impl RogApp {
pub fn aura_page(&mut self, states: &mut SystemState, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
oscillator1,
oscillator2,
oscillator3,
@@ -26,7 +25,6 @@ impl<'a> RogApp<'a> {
let blue = oscillator3.load(Ordering::SeqCst) as u32;
states.aura.nudge_wave(red as u8, green as u8, blue as u8);
// let osc = c.0 * 255 / osc;
// dbg!(osc);
let c1 = states
.aura
.modes
@@ -72,7 +70,7 @@ impl<'a> RogApp<'a> {
// TODO: animation of colour changes/periods/blending
egui::CentralPanel::default().show(ctx, |ui| {
aura_modes_group(supported, states, oscillator_freq, dbus, ui);
aura_modes_group(supported, states, oscillator_freq, ui);
keyboard(ui, &states.keyboard_layout, &mut states.aura, colour);
});

View File

@@ -1,5 +1,5 @@
use crate::{
page_states::{FanCurvesState, ProfilesState},
system_state::{FanCurvesState, ProfilesState, SystemState},
widgets::fan_graphs,
RogApp, RogDbusClientBlocking,
};
@@ -7,14 +7,9 @@ use egui::Ui;
use rog_platform::supported::SupportedFunctions;
use rog_profiles::Profile;
impl<'a> RogApp<'a> {
pub fn fan_curve_page(&mut self, ctx: &egui::Context) {
let Self {
supported,
states,
asus_dbus: dbus,
..
} = self;
impl RogApp {
pub fn fan_curve_page(&mut self, states: &mut SystemState, ctx: &egui::Context) {
let Self { supported, .. } = self;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Custom fan curves");
@@ -23,11 +18,11 @@ impl<'a> RogApp<'a> {
supported,
&mut states.profiles,
&mut states.fan_curves,
dbus, &mut states.error,
&states.asus_dbus, &mut states.error,
ui,
);
fan_graphs(supported, &mut states.profiles, &mut states.fan_curves, dbus, &mut states.error, ui);
fan_graphs(supported, &mut states.fan_curves, &states.asus_dbus, &mut states.error, ui);
});
}
@@ -35,7 +30,7 @@ impl<'a> RogApp<'a> {
supported: &SupportedFunctions,
profiles: &mut ProfilesState,
curves: &mut FanCurvesState,
dbus: &RogDbusClientBlocking,
dbus: &RogDbusClientBlocking<'_>,
do_error: &mut Option<String>,
ui: &mut Ui,
) {
@@ -67,7 +62,7 @@ impl<'a> RogApp<'a> {
};
profiles.list.sort();
for f in profiles.list.iter() {
for f in &profiles.list {
item(*f, curves, curves.enabled.contains(f));
}
});
@@ -76,8 +71,7 @@ impl<'a> RogApp<'a> {
let selected_profile = curves.show_curve;
let selected_pu = curves.show_graph;
let notif = curves.was_notified.clone();
match FanCurvesState::new(notif, supported, dbus) {
match FanCurvesState::new(supported, dbus) {
Ok(f) => *curves = f,
Err(e) => *do_error = Some(e.to_string()),
}

View File

@@ -1,22 +1,19 @@
use crate::{
system_state::SystemState,
widgets::{
anime_power_group, app_settings, aura_power_group, platform_profile, rog_bios_group,
},
RogApp,
};
impl<'a> RogApp<'a> {
pub fn system_page(&mut self, ctx: &egui::Context) {
impl RogApp {
pub fn system_page(&mut self, states: &mut SystemState, ctx: &egui::Context) {
let Self {
config,
supported,
states,
asus_dbus: dbus,
..
config, supported, ..
} = self;
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Experimental application for asusd");
ui.heading("Base settings");
egui::ScrollArea::vertical().show(ui, |ui| {
ui.spacing_mut().item_spacing = egui::vec2(8.0, 10.0);
@@ -27,26 +24,24 @@ impl<'a> RogApp<'a> {
/******************************************************/
ui.vertical(|ui| {
ui.separator();
app_settings(config, states, ui);
if supported.platform_profile.platform_profile {
platform_profile(states, ui);
}
});
ui.vertical(|ui| {
ui.separator();
if supported.platform_profile.platform_profile {
platform_profile(states, dbus, ui);
}
aura_power_group(supported, states, ui);
});
ui.end_row();
/******************************************************/
ui.vertical(|ui| {
ui.separator();
aura_power_group(supported, states, dbus, ui);
app_settings(config, states, ui);
});
ui.vertical(|ui| {
ui.separator();
rog_bios_group(supported, states, dbus, ui);
rog_bios_group(supported, states, ui);
});
ui.end_row();
@@ -54,7 +49,7 @@ impl<'a> RogApp<'a> {
ui.vertical(|ui| {
ui.separator();
if supported.anime_ctrl.0 {
anime_power_group(supported, states, dbus, ui);
anime_power_group(supported, states, ui);
}
});
ui.vertical(|ui| {

View File

@@ -27,7 +27,7 @@ impl eframe::App for AppErrorShow {
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
frame.quit();
frame.close();
}
});
});

View File

@@ -1,24 +1,25 @@
use std::{
collections::{BTreeMap, HashSet},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
sync::{Arc, Mutex},
};
use egui::Vec2;
use rog_aura::{layouts::KeyLayout, usb::AuraPowerDev, AuraEffect, AuraModeNum};
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
use rog_profiles::{fan_curve_set::FanCurveSet, FanCurvePU, Profile};
use supergfxctl::{
pci_device::{GfxMode, GfxPower},
zbus_proxy::DaemonProxyBlocking as GfxProxyBlocking,
};
use crate::{error::Result, RogDbusClientBlocking};
use crate::{error::Result, update_and_notify::EnabledNotifications, RogDbusClientBlocking};
use log::error;
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct BiosState {
/// To be shared to a thread that checks notifications.
/// It's a bit general in that it won't provide *what* was
/// updated, so the full state needs refresh
pub was_notified: Arc<AtomicBool>,
pub post_sound: bool,
pub dedicated_gfx: GpuMode,
pub panel_overdrive: bool,
@@ -27,13 +28,8 @@ pub struct BiosState {
}
impl BiosState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
Ok(Self {
was_notified,
post_sound: if supported.rog_bios_ctrl.post_sound {
dbus.proxies().rog_bios().post_boot_sound()? != 0
} else {
@@ -45,7 +41,7 @@ impl BiosState {
GpuMode::NotSupported
},
panel_overdrive: if supported.rog_bios_ctrl.panel_overdrive {
dbus.proxies().rog_bios().panel_overdrive()?
dbus.proxies().rog_bios().panel_od()?
} else {
false
},
@@ -56,21 +52,15 @@ impl BiosState {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct ProfilesState {
pub was_notified: Arc<AtomicBool>,
pub list: Vec<Profile>,
pub current: Profile,
}
impl ProfilesState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
Ok(Self {
was_notified,
list: if supported.platform_profile.platform_profile {
let mut list = dbus.proxies().profile().profiles()?;
list.sort();
@@ -87,9 +77,8 @@ impl ProfilesState {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct FanCurvesState {
pub was_notified: Arc<AtomicBool>,
pub show_curve: Profile,
pub show_graph: FanCurvePU,
pub enabled: HashSet<Profile>,
@@ -98,30 +87,25 @@ pub struct FanCurvesState {
}
impl FanCurvesState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
let profiles = if supported.platform_profile.platform_profile {
dbus.proxies().profile().profiles()?
} else {
vec![Profile::Balanced, Profile::Quiet, Profile::Performance]
};
let enabled = if supported.platform_profile.fan_curves {
HashSet::from_iter(
dbus.proxies()
.profile()
.enabled_fan_profiles()?
.iter()
.cloned(),
)
dbus.proxies()
.profile()
.enabled_fan_profiles()?
.iter()
.cloned()
.collect::<HashSet<_>>()
} else {
HashSet::from([Profile::Balanced, Profile::Quiet, Profile::Performance])
};
let mut curves: BTreeMap<Profile, FanCurveSet> = BTreeMap::new();
profiles.iter().for_each(|p| {
for p in &profiles {
if supported.platform_profile.fan_curves {
if let Ok(curve) = dbus.proxies().profile().fan_curve_data(*p) {
curves.insert(*p, curve);
@@ -134,7 +118,7 @@ impl FanCurvesState {
curve.gpu.temp = [20, 30, 40, 50, 70, 80, 90, 100];
curves.insert(*p, curve);
}
});
}
let show_curve = if supported.platform_profile.fan_curves {
dbus.proxies().profile().active_profile()?
@@ -143,7 +127,6 @@ impl FanCurvesState {
};
Ok(Self {
was_notified,
show_curve,
show_graph: FanCurvePU::CPU,
enabled,
@@ -153,9 +136,8 @@ impl FanCurvesState {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct AuraState {
pub was_notified: Arc<AtomicBool>,
pub current_mode: AuraModeNum,
pub modes: BTreeMap<AuraModeNum, AuraEffect>,
pub enabled: AuraPowerDev,
@@ -167,13 +149,8 @@ pub struct AuraState {
}
impl AuraState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
Ok(Self {
was_notified,
current_mode: if !supported.keyboard_led.stock_led_modes.is_empty() {
dbus.proxies().led().led_mode().unwrap_or_default()
} else {
@@ -212,9 +189,8 @@ impl AuraState {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct AnimeState {
pub was_notified: Arc<AtomicBool>,
pub bright: u8,
pub boot: bool,
pub awake: bool,
@@ -222,13 +198,8 @@ pub struct AnimeState {
}
impl AnimeState {
pub fn new(
was_notified: Arc<AtomicBool>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
pub fn new(supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
Ok(Self {
was_notified,
boot: if supported.anime_ctrl.0 {
dbus.proxies().anime().boot_enabled()?
} else {
@@ -246,11 +217,53 @@ impl AnimeState {
}
}
#[derive(Debug)]
pub struct PageDataStates {
#[derive(Clone, Debug)]
pub struct GfxState {
pub has_supergfx: bool,
pub mode: GfxMode,
pub power_status: GfxPower,
}
impl GfxState {
pub fn new(_supported: &SupportedFunctions, dbus: &GfxProxyBlocking<'_>) -> Result<Self> {
Ok(Self {
has_supergfx: dbus.mode().is_ok(),
mode: dbus.mode().unwrap_or(GfxMode::None),
power_status: dbus.power().unwrap_or(GfxPower::Unknown),
})
}
}
impl Default for GfxState {
fn default() -> Self {
Self {
has_supergfx: false,
mode: GfxMode::None,
power_status: GfxPower::Unknown,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct PowerState {
pub charge_limit: u8,
pub ac_power: bool,
}
impl PowerState {
pub fn new(_supported: &SupportedFunctions, dbus: &RogDbusClientBlocking<'_>) -> Result<Self> {
Ok(Self {
charge_limit: dbus.proxies().charge().charge_control_end_threshold()?,
ac_power: dbus.proxies().charge().mains_online()?,
})
}
}
/// State stored from system daemons. This is shared with: tray, zbus notifications thread
/// and the GUI app thread.
pub struct SystemState {
pub keyboard_layout: KeyLayout,
pub notifs_enabled: Arc<AtomicBool>,
pub was_notified: Arc<AtomicBool>,
pub enabled_notifications: Arc<Mutex<EnabledNotifications>>,
/// Because much of the app state here is the same as `RogBiosSupportedFunctions`
/// we can re-use that structure.
pub bios: BiosState,
@@ -258,86 +271,102 @@ pub struct PageDataStates {
pub anime: AnimeState,
pub profiles: ProfilesState,
pub fan_curves: FanCurvesState,
pub charge_limit: i16,
pub gfx_state: GfxState,
pub power_state: PowerState,
pub error: Option<String>,
/// Specific field for the tray only so that we can know when it does need update.
/// The tray should set this to false when done.
pub tray_should_update: bool,
pub app_should_update: bool,
pub asus_dbus: RogDbusClientBlocking<'static>,
pub gfx_dbus: GfxProxyBlocking<'static>,
}
impl PageDataStates {
impl SystemState {
/// Creates self, including the relevant dbus connections and proixies for internal use
pub fn new(
keyboard_layout: KeyLayout,
notifs_enabled: Arc<AtomicBool>,
charge_notified: Arc<AtomicBool>,
bios_notified: Arc<AtomicBool>,
aura_notified: Arc<AtomicBool>,
anime_notified: Arc<AtomicBool>,
profiles_notified: Arc<AtomicBool>,
fans_notified: Arc<AtomicBool>,
enabled_notifications: Arc<Mutex<EnabledNotifications>>,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<Self> {
let (asus_dbus, conn) = RogDbusClientBlocking::new()?;
let mut error = None;
let gfx_dbus = GfxProxyBlocking::new(&conn).expect("Couldn't connect to supergfxd");
Ok(Self {
keyboard_layout,
notifs_enabled,
was_notified: charge_notified,
charge_limit: dbus.proxies().charge().limit()?,
bios: BiosState::new(bios_notified, supported, dbus)?,
aura: AuraState::new(aura_notified, supported, dbus)?,
anime: AnimeState::new(anime_notified, supported, dbus)?,
profiles: ProfilesState::new(profiles_notified, supported, dbus)?,
fan_curves: FanCurvesState::new(fans_notified, supported, dbus)?,
error: None,
enabled_notifications,
power_state: PowerState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get PowerState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
bios: BiosState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get BiosState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
aura: AuraState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get AuraState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
anime: AnimeState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get AanimeState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
profiles: ProfilesState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get ProfilesState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
fan_curves: FanCurvesState::new(supported, &asus_dbus)
.map_err(|e| {
let e = format!("Could not get FanCurvesState state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
gfx_state: GfxState::new(supported, &gfx_dbus)
.map_err(|e| {
let e = format!("Could not get supergfxd state: {e}");
error!("{e}");
error = Some(e);
})
.unwrap_or_default(),
error,
tray_should_update: true,
app_should_update: true,
asus_dbus,
gfx_dbus,
})
}
pub fn refresh_if_notfied(
&mut self,
supported: &SupportedFunctions,
dbus: &RogDbusClientBlocking,
) -> Result<bool> {
let mut notified = false;
if self.was_notified.load(Ordering::SeqCst) {
self.charge_limit = dbus.proxies().charge().limit()?;
self.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.aura.was_notified.load(Ordering::SeqCst) {
self.aura = AuraState::new(self.aura.was_notified.clone(), supported, dbus)?;
self.aura.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.bios.was_notified.load(Ordering::SeqCst) {
self.bios = BiosState::new(self.bios.was_notified.clone(), supported, dbus)?;
self.bios.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.profiles.was_notified.load(Ordering::SeqCst) {
self.profiles =
ProfilesState::new(self.profiles.was_notified.clone(), supported, dbus)?;
self.profiles.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
if self.fan_curves.was_notified.load(Ordering::SeqCst) {
self.fan_curves =
FanCurvesState::new(self.fan_curves.was_notified.clone(), supported, dbus)?;
self.fan_curves.was_notified.store(false, Ordering::SeqCst);
notified = true;
}
Ok(notified)
pub fn set_notified(&mut self) {
self.tray_should_update = true;
self.app_should_update = true;
}
}
impl Default for PageDataStates {
impl Default for SystemState {
fn default() -> Self {
let (asus_dbus, conn) = RogDbusClientBlocking::new().expect("Couldn't connect to asusd");
let gfx_dbus = GfxProxyBlocking::new(&conn).expect("Couldn't connect to supergfxd");
Self {
keyboard_layout: KeyLayout::ga401_layout(),
notifs_enabled: Default::default(),
was_notified: Default::default(),
enabled_notifications: Default::default(),
bios: BiosState {
was_notified: Default::default(),
post_sound: Default::default(),
dedicated_gfx: GpuMode::NotSupported,
panel_overdrive: Default::default(),
@@ -345,7 +374,6 @@ impl Default for PageDataStates {
egpu_enable: Default::default(),
},
aura: AuraState {
was_notified: Default::default(),
current_mode: AuraModeNum::Static,
modes: Default::default(),
enabled: AuraPowerDev {
@@ -359,27 +387,36 @@ impl Default for PageDataStates {
wave_blue: Default::default(),
},
anime: AnimeState {
was_notified: Default::default(),
bright: Default::default(),
boot: Default::default(),
awake: Default::default(),
sleep: Default::default(),
},
profiles: ProfilesState {
was_notified: Default::default(),
list: Default::default(),
current: Default::default(),
},
fan_curves: FanCurvesState {
was_notified: Default::default(),
show_curve: Default::default(),
show_graph: Default::default(),
enabled: Default::default(),
curves: Default::default(),
drag_delta: Default::default(),
},
charge_limit: Default::default(),
gfx_state: GfxState {
has_supergfx: false,
mode: GfxMode::None,
power_status: GfxPower::Unknown,
},
power_state: PowerState {
charge_limit: 99,
ac_power: false,
},
error: Default::default(),
tray_should_update: true,
app_should_update: true,
asus_dbus,
gfx_dbus,
}
}
}

View File

@@ -0,0 +1,455 @@
//! A seld-contained tray icon with menus. The control of app<->tray is done via
//! commands over an MPSC channel.
use std::{
io::Write,
sync::{
mpsc::{channel, Receiver},
Arc, Mutex,
},
time::Duration,
};
use gtk::{gio::Icon, prelude::*};
use rog_dbus::zbus_platform::RogBiosProxyBlocking;
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
use crate::{error::Result, get_ipc_file, system_state::SystemState, SHOW_GUI};
use libappindicator::{AppIndicator, AppIndicatorStatus};
use supergfxctl::{
pci_device::{GfxMode, GfxPower},
zbus_proxy::DaemonProxyBlocking as GfxProxyBlocking,
};
use log::{debug, error, info, trace};
const TRAY_APP_ICON: &str = "rog-control-center";
const TRAY_LABEL: &str = "ROG Control Center";
pub enum AppToTray {
DgpuStatus(GfxPower),
}
pub enum TrayToApp {
Open,
Quit,
}
pub struct RadioGroup(Vec<gtk::RadioMenuItem>);
impl RadioGroup {
/// Add new radio menu item. `set_no_show_all()` is true until added to menu
/// to prevent teh callback from running
pub fn new<F>(first_label: &str, cb: F) -> Self
where
F: Fn(&gtk::RadioMenuItem) + Send + 'static,
{
let item = gtk::RadioMenuItem::with_label(first_label);
item.set_active(false);
item.set_no_show_all(true);
item.connect_activate(move |this| {
if this.is_active() && !this.is_no_show_all() {
cb(this);
}
});
Self(vec![item])
}
/// Add new radio menu item. `set_no_show_all()` is true until added to menu
/// to prevent teh callback from running
pub fn add<F>(&mut self, label: &str, cb: F)
where
F: Fn(&gtk::RadioMenuItem) + Send + 'static,
{
debug_assert!(!self.0.is_empty());
let group = self.0[0].group();
let item = gtk::RadioMenuItem::with_label_from_widget(&group[0], Some(label));
item.set_active(false);
item.set_no_show_all(true);
item.connect_activate(move |this| {
if this.is_active() && !this.is_no_show_all() {
cb(this);
}
});
self.0.push(item);
}
}
pub struct ROGTray {
tray: AppIndicator,
menu: gtk::Menu,
icon: &'static str,
bios_proxy: RogBiosProxyBlocking<'static>,
gfx_proxy: GfxProxyBlocking<'static>,
}
impl ROGTray {
pub fn new() -> Result<Self> {
let conn = zbus::blocking::Connection::system().map_err(|e| {
error!("ROGTray: {e}");
e
})?;
let rog_tray = Self {
tray: AppIndicator::new(TRAY_LABEL, TRAY_APP_ICON),
menu: gtk::Menu::new(),
icon: TRAY_APP_ICON,
bios_proxy: RogBiosProxyBlocking::new(&conn).map_err(|e| {
error!("ROGTray: {e}");
e
})?,
gfx_proxy: GfxProxyBlocking::new(&conn).map_err(|e| {
error!("ROGTray: {e}");
e
})?,
};
Ok(rog_tray)
}
pub fn set_icon(&mut self, icon: &'static str) {
self.icon = icon;
self.tray.set_icon(self.icon);
self.tray.set_status(AppIndicatorStatus::Active);
}
/// Add a non-interactive label
fn add_inactive_label(&mut self, label: &str) {
let item = gtk::MenuItem::with_label(label);
item.set_sensitive(false);
self.menu.append(&item);
self.menu.show_all();
}
/// Add a separator
fn add_separator(&mut self) {
let item = gtk::SeparatorMenuItem::new();
item.set_sensitive(false);
self.menu.append(&item);
self.menu.show_all();
}
fn add_radio_sub_menu(
&mut self,
header_label: &str,
active_label: &str,
sub_menu: &RadioGroup,
) {
let header_item = gtk::MenuItem::with_label(header_label);
header_item.show_all();
self.menu.add(&header_item);
let menu = gtk::Menu::new();
for item in &sub_menu.0 {
if let Some(label) = item.label() {
item.set_active(label == active_label);
} else {
item.set_active(false);
}
item.set_no_show_all(false);
item.show_all();
menu.add(item);
}
menu.show_all();
header_item.set_submenu(Some(&menu));
}
fn _add_menu_item<F>(&mut self, label: &str, cb: F)
where
F: Fn() + Send + 'static,
{
let item = gtk::MenuItem::with_label(label);
item.connect_activate(move |_| {
cb();
});
self.menu.append(&item);
self.menu.show_all();
}
fn add_check_menu_item<F>(&mut self, label: &str, is_active: bool, cb: F)
where
F: Fn(&gtk::CheckMenuItem) + Send + 'static,
{
let item = gtk::CheckMenuItem::with_label(label);
item.set_active(is_active);
item.connect_activate(move |this| {
cb(this);
});
self.menu.append(&item);
self.menu.show_all();
}
/// Add a menu item with an icon to the right
fn add_icon_menu_item<F>(&mut self, label: &str, icon: &str, cb: F)
where
F: Fn() + Send + 'static,
{
let g_box = gtk::Box::new(gtk::Orientation::Horizontal, 6);
let icon = gtk::Image::from_gicon(&Icon::for_string(icon).unwrap(), gtk::IconSize::Menu);
let label = gtk::Label::new(Some(label));
let item = gtk::MenuItem::new();
g_box.add(&icon);
g_box.add(&label);
item.add(&g_box);
item.show_all();
item.connect_activate(move |_| {
cb();
});
self.menu.append(&item);
self.menu.show_all();
self.tray.set_menu(&mut self.menu);
}
fn _set_status(&mut self, status: AppIndicatorStatus) {
self.tray.set_status(status);
}
fn menu_add_base(&mut self) {
self.add_icon_menu_item("Open app", "asus_notif_red", move || {
if let Ok(mut ipc) = get_ipc_file().map_err(|e| {
error!("ROGTray: get_ipc_file: {}", e);
}) {
ipc.write_all(&[SHOW_GUI]).ok();
}
});
self.add_separator();
debug!("ROGTray: built base menu");
}
fn menu_add_charge_limit(&mut self, supported: &SupportedFunctions, limit: u8) {
if supported.charge_ctrl.charge_level_set {
self.add_inactive_label(&format!("Charge limit: {limit}"));
debug!("ROGTray: appended charge limit menu");
}
}
fn menu_add_panel_od(&mut self, supported: &SupportedFunctions, panel_od: bool) {
if supported.rog_bios_ctrl.panel_overdrive {
let bios = self.bios_proxy.clone();
self.add_check_menu_item("Panel Overdrive", panel_od, move |this| {
bios.set_panel_od(this.is_active())
.map_err(|e| {
error!("ROGTray: set_panel_od: {e}");
e
})
.ok();
});
debug!("ROGTray: appended panel overdrive menu");
}
}
fn menu_add_gpu(&mut self, supported: &SupportedFunctions, current_mode: GfxMode) {
let gfx_dbus = self.gfx_proxy.clone();
let mut gpu_menu = RadioGroup::new("Integrated", move |_| {
let mode = gfx_dbus
.mode()
.map_err(|e| {
error!("ROGTray: mode: {e}");
e
})
.unwrap_or(GfxMode::None);
if mode != GfxMode::Integrated {
gfx_dbus
.set_mode(&GfxMode::Integrated)
.map_err(|e| {
error!("ROGTray: srt_mode: {e}");
e
})
.ok();
}
});
let gfx_dbus = self.gfx_proxy.clone();
gpu_menu.add("Hybrid", move |_| {
let mode = gfx_dbus
.mode()
.map_err(|e| {
error!("ROGTray: mode: {e}");
e
})
.unwrap_or(GfxMode::None);
if mode != GfxMode::Hybrid {
gfx_dbus
.set_mode(&GfxMode::Hybrid)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
}
});
if supported.rog_bios_ctrl.gpu_mux {
let gfx_dbus = self.bios_proxy.clone();
gpu_menu.add("Ultimate (Reboot required)", move |_| {
let mode = gfx_dbus
.gpu_mux_mode()
.map_err(|e| {
error!("ROGTray: mode: {e}");
e
})
.unwrap_or(GpuMode::Error);
if mode != GpuMode::Discrete {
gfx_dbus
.set_gpu_mux_mode(GpuMode::Discrete)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
}
});
}
if supported.rog_bios_ctrl.egpu_enable {
let gfx_dbus = self.gfx_proxy.clone();
gpu_menu.add("eGPU", move |_| {
let mode = gfx_dbus
.mode()
.map_err(|e| {
error!("ROGTray: mode: {e}");
e
})
.unwrap_or(GfxMode::None);
if mode != GfxMode::Egpu {
gfx_dbus
.set_mode(&GfxMode::Egpu)
.map_err(|e| {
error!("ROGTray: set_mode: {e}");
e
})
.ok();
}
});
}
let active = match current_mode {
GfxMode::AsusMuxDiscreet => "Discreet".to_owned(),
_ => current_mode.to_string(),
};
self.add_radio_sub_menu(
&format!("GPU Mode: {current_mode}"),
active.as_str(),
&gpu_menu,
);
debug!("ROGTray: appended gpu menu");
}
fn menu_clear(&mut self) {
self.menu = gtk::Menu::new();
debug!("ROGTray: cleared self");
}
/// Reset GTK menu to internal state, this can be called after clearing and rebuilding the menu too.
fn menu_update(&mut self) {
self.tray.set_menu(&mut self.menu);
self.set_icon(self.icon);
}
/// Do a flush, build, and update of the tray menu
fn rebuild_and_update(
&mut self,
supported: &SupportedFunctions,
has_supergfx: bool,
current_gfx_mode: GfxMode,
charge_limit: u8,
panel_od: bool,
) {
self.menu_clear();
self.menu_add_base();
self.menu_add_charge_limit(supported, charge_limit);
self.menu_add_panel_od(supported, panel_od);
if has_supergfx {
self.menu_add_gpu(supported, current_gfx_mode);
}
self.menu_update();
}
}
pub fn init_tray(
supported: SupportedFunctions,
states: Arc<Mutex<SystemState>>,
) -> Receiver<TrayToApp> {
let (send, recv) = channel();
let _send = Arc::new(Mutex::new(send));
let has_supergfx = if let Ok(lock) = states.try_lock() {
lock.gfx_state.has_supergfx
} else {
false
};
std::thread::spawn(move || {
if gtk::init()
.map_err(|e| {
error!("ROGTray: gtk init {e}");
e
})
.is_err()
{
return;
} // Make this the main thread for gtk
debug!("init_tray gtk");
let mut tray = match ROGTray::new() {
Ok(t) => {
info!("init_tray: built menus");
t
}
Err(e) => {
error!("ROGTray: tray init {e}");
if let Ok(mut states) = states.lock() {
states.error = Some(format!("Could not start tray: {e}"));
}
return;
}
};
tray.rebuild_and_update(&supported, has_supergfx, GfxMode::Hybrid, 100, false);
tray.set_icon(TRAY_APP_ICON);
info!("Started ROGTray");
loop {
if let Ok(mut lock) = states.lock() {
if lock.tray_should_update {
tray.rebuild_and_update(
&supported,
has_supergfx,
lock.gfx_state.mode,
lock.power_state.charge_limit,
lock.bios.panel_overdrive,
);
lock.tray_should_update = false;
debug!("ROGTray: rebuilt menus due to state change");
match lock.gfx_state.power_status {
GfxPower::Suspended => tray.set_icon("asus_notif_blue"),
GfxPower::Off => tray.set_icon("asus_notif_green"),
GfxPower::AsusDisabled => tray.set_icon("asus_notif_white"),
GfxPower::AsusMuxDiscreet | GfxPower::Active => {
tray.set_icon("asus_notif_red");
}
GfxPower::Unknown => {
if has_supergfx {
tray.set_icon("gpu-integrated");
} else {
tray.set_icon("asus_notif_red");
}
}
};
}
}
if gtk::events_pending() {
// This is blocking until any events are available
gtk::main_iteration();
continue;
}
// Don't spool at max speed if no gtk events
std::thread::sleep(Duration::from_millis(300));
trace!("Tray loop ticked");
}
});
recv
}

View File

@@ -0,0 +1,465 @@
//! `update_and_notify` is responsible for both notifications *and* updating stored statuses
//! about the system state. This is done through either direct, intoify, zbus notifications
//! or similar methods.
use crate::{config::Config, error::Result, system_state::SystemState};
use log::{error, info, trace, warn};
use notify_rust::{Hint, Notification, NotificationHandle, Urgency};
use rog_dbus::{
zbus_anime::AnimeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy,
zbus_power::PowerProxy, zbus_profile::ProfileProxy,
};
use rog_platform::platform::GpuMode;
use rog_profiles::Profile;
use serde::{Deserialize, Serialize};
use std::{
fmt::Display,
process::Command,
sync::{Arc, Mutex},
time::Duration,
};
use supergfxctl::{pci_device::GfxPower, zbus_proxy::DaemonProxy as SuperProxy};
use tokio::time::sleep;
use zbus::export::futures_util::{future, StreamExt};
const NOTIF_HEADER: &str = "ROG Control";
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct EnabledNotifications {
pub receive_notify_post_boot_sound: bool,
pub receive_notify_panel_od: bool,
pub receive_notify_dgpu_disable: bool,
pub receive_notify_egpu_enable: bool,
pub receive_notify_gpu_mux_mode: bool,
pub receive_notify_charge_control_end_threshold: bool,
pub receive_notify_mains_online: bool,
pub receive_notify_profile: bool,
pub receive_notify_led: bool,
/// Anime
pub receive_power_states: bool,
pub receive_notify_gfx: bool,
pub receive_notify_gfx_status: bool,
pub all_enabled: bool,
}
impl Default for EnabledNotifications {
fn default() -> Self {
Self {
receive_notify_post_boot_sound: false,
receive_notify_panel_od: true,
receive_notify_dgpu_disable: true,
receive_notify_egpu_enable: true,
receive_notify_gpu_mux_mode: true,
receive_notify_charge_control_end_threshold: true,
receive_notify_mains_online: false,
receive_notify_profile: true,
receive_notify_led: false,
receive_power_states: false,
receive_notify_gfx: false,
receive_notify_gfx_status: false,
all_enabled: false,
}
}
}
impl EnabledNotifications {
pub fn tokio_mutex(config: &Config) -> Arc<Mutex<Self>> {
Arc::new(Mutex::new(config.enabled_notifications.clone()))
}
}
macro_rules! notify {
($notifier:expr, $last_notif:ident) => {
if let Some(notif) = $last_notif.take() {
notif.close();
}
if let Ok(x) = $notifier {
$last_notif.replace(x);
}
};
}
// TODO: drop the macro and use generics plus closure
macro_rules! recv_notif {
($proxy:ident,
$signal:ident,
$last_notif:ident,
$notif_enabled:ident,
$page_states:ident,
($($args: tt)*),
($($out_arg:tt)+),
$msg:literal,
$notifier:ident) => {
let last_notif = $last_notif.clone();
let notifs_enabled1 = $notif_enabled.clone();
let page_states1 = $page_states.clone();
tokio::spawn(async move {
let conn = zbus::Connection::system().await.map_err(|e| {
log::error!("zbus signal: {}: {e}", stringify!($signal));
e
}).unwrap();
let proxy = $proxy::new(&conn).await.map_err(|e| {
log::error!("zbus signal: {}: {e}", stringify!($signal));
e
}).unwrap();
if let Ok(mut p) = proxy.$signal().await {
info!("Started zbus signal thread: {}", stringify!($signal));
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
if let Ok(config) = notifs_enabled1.lock() {
if config.all_enabled && config.$signal {
if let Ok(ref mut lock) = last_notif.lock() {
trace!("zbus signal {} locked last_notif", stringify!($signal));
notify!($notifier($msg, &out.$($out_arg)+()), lock);
}
}
}
if let Ok(mut lock) = page_states1.lock() {
lock.$($args)+ = *out.$($out_arg)+();
lock.set_notified();
}
}
}
};
});
};
}
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
pub fn start_notifications(
page_states: Arc<Mutex<SystemState>>,
enabled_notifications: Arc<Mutex<EnabledNotifications>>,
) -> Result<()> {
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
// BIOS notif
recv_notif!(
RogBiosProxy,
receive_notify_post_boot_sound,
last_notification,
enabled_notifications,
page_states,
(bios.post_sound),
(on),
"BIOS Post sound",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_panel_od,
last_notification,
enabled_notifications,
page_states,
(bios.panel_overdrive),
(overdrive),
"Panel Overdrive enabled:",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_dgpu_disable,
last_notification,
enabled_notifications,
page_states,
(bios.dgpu_disable),
(disable),
"BIOS dGPU disabled",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_egpu_enable,
last_notification,
enabled_notifications,
page_states,
(bios.egpu_enable),
(enable),
"BIOS eGPU enabled",
do_notification
);
recv_notif!(
RogBiosProxy,
receive_notify_gpu_mux_mode,
last_notification,
enabled_notifications,
page_states,
(bios.dedicated_gfx),
(mode),
"Reboot required. BIOS GPU MUX mode set to",
do_mux_notification
);
// Charge notif
recv_notif!(
PowerProxy,
receive_notify_charge_control_end_threshold,
last_notification,
enabled_notifications,
page_states,
(power_state.charge_limit),
(limit),
"Battery charge limit changed to",
do_notification
);
recv_notif!(
PowerProxy,
receive_notify_mains_online,
last_notification,
enabled_notifications,
page_states,
(power_state.ac_power),
(on),
"AC Power power is",
ac_power_notification
);
// Profile notif
recv_notif!(
ProfileProxy,
receive_notify_profile,
last_notification,
enabled_notifications,
page_states,
(profiles.current),
(profile),
"Profile changed to",
do_thermal_notif
);
// notify!(do_thermal_notif(&out.profile), lock);
// LED notif
recv_notif!(
LedProxy,
receive_notify_led,
last_notification,
enabled_notifications,
page_states,
(aura.current_mode),
(data.mode),
"Keyboard LED mode changed to",
do_notification
);
let page_states1 = page_states.clone();
tokio::spawn(async move {
let conn = zbus::Connection::system()
.await
.map_err(|e| {
error!("zbus signal: receive_power_states: {e}");
e
})
.unwrap();
let proxy = AnimeProxy::new(&conn)
.await
.map_err(|e| {
error!("zbus signal: receive_power_states: {e}");
e
})
.unwrap();
if let Ok(p) = proxy.receive_power_states().await {
info!("Started zbus signal thread: receive_power_states");
p.for_each(|_| {
if let Ok(_lock) = page_states1.lock() {
// TODO: lock.anime.
}
future::ready(())
})
.await;
};
});
if let Ok(lock) = page_states.try_lock() {
use supergfxctl::pci_device::Device;
let dev = Device::find().unwrap_or_default();
let mut found_dgpu = false; // just for logging
for dev in dev {
if dev.is_dgpu() {
let notifs_enabled1 = enabled_notifications.clone();
let last_notif = last_notification.clone();
let page_states1 = page_states.clone();
// Plain old thread is perfectly fine since most of this is potentially blocking
tokio::spawn(async move {
let mut last_status = GfxPower::Unknown;
loop {
if let Ok(status) = dev.get_runtime_status() {
if status != GfxPower::Unknown && status != last_status {
if let Ok(config) = notifs_enabled1.lock() {
if config.all_enabled && config.receive_notify_gfx_status {
// Required check because status cycles through active/unknown/suspended
if let Ok(ref mut lock) = last_notif.lock() {
notify!(
do_gpu_status_notif(
"dGPU status changed:",
&status
),
lock
);
}
}
}
if let Ok(mut lock) = page_states1.lock() {
lock.gfx_state.power_status = status;
lock.set_notified();
}
last_status = status;
}
}
sleep(Duration::from_millis(500)).await;
}
});
found_dgpu = true;
break;
}
}
if !found_dgpu {
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
}
if lock.gfx_state.has_supergfx {
recv_notif!(
SuperProxy,
receive_notify_gfx,
last_notification,
enabled_notifications,
page_states,
(gfx_state.mode),
(mode),
"Gfx mode changed to",
do_notification
);
tokio::spawn(async move {
let conn = zbus::Connection::system()
.await
.map_err(|e| {
error!("zbus signal: receive_notify_action: {e}");
e
})
.unwrap();
let proxy = SuperProxy::new(&conn)
.await
.map_err(|e| {
error!("zbus signal: receive_notify_action: {e}");
e
})
.unwrap();
if let Ok(mut p) = proxy.receive_notify_action().await {
info!("Started zbus signal thread: receive_notify_action");
while let Some(e) = p.next().await {
if let Ok(out) = e.args() {
let action = out.action();
do_gfx_action_notif(
"Gfx mode change requires",
&format!("{action:?}",),
)
.map_err(|e| {
error!("zbus signal: do_gfx_action_notif: {e}");
e
})
.unwrap();
}
}
};
});
}
}
Ok(())
}
fn base_notification<T>(message: &str, data: &T) -> Notification
where
T: Display,
{
let mut notif = Notification::new();
notif
.summary(NOTIF_HEADER)
.body(&format!("{message} {data}"))
.timeout(2000)
//.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()));
notif
}
fn do_notification<T>(message: &str, data: &T) -> Result<NotificationHandle>
where
T: Display,
{
Ok(base_notification(message, data).show()?)
}
fn ac_power_notification(message: &str, on: &bool) -> Result<NotificationHandle> {
let data = if *on {
"plugged".to_owned()
} else {
"unplugged".to_owned()
};
Ok(base_notification(message, &data).show()?)
}
fn do_thermal_notif(message: &str, profile: &Profile) -> Result<NotificationHandle> {
let icon = match profile {
Profile::Balanced => "asus_notif_yellow",
Profile::Performance => "asus_notif_red",
Profile::Quiet => "asus_notif_green",
};
let profile: &str = (*profile).into();
let mut notif = base_notification(message, &profile.to_uppercase());
Ok(notif.icon(icon).show()?)
}
fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Result<NotificationHandle> {
// eww
let mut notif = base_notification(message, &<&str>::from(data).to_owned());
let icon = match data {
GfxPower::Suspended => "asus_notif_blue",
GfxPower::Off => "asus_notif_green",
GfxPower::AsusDisabled => "asus_notif_white",
GfxPower::AsusMuxDiscreet | GfxPower::Active => "asus_notif_red",
GfxPower::Unknown => "gpu-integrated",
};
notif.icon(icon);
Ok(Notification::show(&notif)?)
}
fn do_gfx_action_notif<T>(message: &str, data: &T) -> Result<()>
where
T: Display,
{
let mut notif = base_notification(message, data);
notif.action("gnome-session-quit", "Logout");
notif.urgency(Urgency::Critical);
notif.timeout(3000);
notif.icon("dialog-warning");
notif.hint(Hint::Transient(true));
let handle = notif.show()?;
handle.wait_for_action(|id| {
if id == "gnome-session-quit" {
let mut cmd = Command::new("gnome-session-quit");
cmd.spawn().ok();
} else if id == "__closed" {
// TODO: cancel the switching
}
});
Ok(())
}
/// Actual `GpuMode` unused as data is never correct until switched by reboot
fn do_mux_notification(message: &str, _: &GpuMode) -> Result<NotificationHandle> {
let mut notif = base_notification(message, &"");
notif.urgency(Urgency::Critical);
notif.icon("system-reboot-symbolic");
notif.hint(Hint::Transient(true));
Ok(notif.show()?)
}

View File

@@ -1,14 +1,9 @@
use egui::{RichText, Ui};
use rog_platform::supported::SupportedFunctions;
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
use crate::system_state::SystemState;
pub fn anime_power_group(
_supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
pub fn anime_power_group(_supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
ui.heading("AniMe Matrix Settings");
ui.label("Options are incomplete. Awake + Boot should work");
@@ -43,7 +38,9 @@ pub fn anime_power_group(
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.boot, "Enable").changed() {
dbus.proxies()
states
.asus_dbus
.proxies()
.anime()
.set_boot_on_off(states.anime.boot)
.map_err(|err| {
@@ -54,7 +51,9 @@ pub fn anime_power_group(
});
ui.horizontal_wrapped(|ui| {
if ui.checkbox(&mut states.anime.awake, "Enable").changed() {
dbus.proxies()
states
.asus_dbus
.proxies()
.anime()
.set_on_off(states.anime.awake)
.map_err(|err| {

View File

@@ -1,13 +1,17 @@
use std::sync::atomic::Ordering;
use egui::Ui;
use crate::{config::Config, page_states::PageDataStates};
use crate::{config::Config, system_state::SystemState};
pub fn app_settings(config: &mut Config, states: &mut PageDataStates, ui: &mut Ui) {
pub fn app_settings(config: &mut Config, states: &mut SystemState, ui: &mut Ui) {
ui.heading("ROG GUI Settings");
// ui.label("Options are incomplete. Awake + Boot should work");
let mut enabled_notifications = if let Ok(lock) = states.enabled_notifications.lock() {
lock.clone()
} else {
Default::default()
};
if ui
.checkbox(&mut config.run_in_background, "Run in Background")
.clicked()
@@ -15,17 +19,70 @@ pub fn app_settings(config: &mut Config, states: &mut PageDataStates, ui: &mut U
.checkbox(&mut config.startup_in_background, "Startup Hidden")
.clicked()
|| ui
.checkbox(&mut config.enable_notifications, "Enable Notifications")
.checkbox(
&mut enabled_notifications.all_enabled,
"Enable Notifications",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_gfx_status,
"Enable dGPU status notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_dgpu_disable,
"Enable dGPU disablement notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_egpu_enable,
"Enable eGPU enablement notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_mains_online,
"Enable mains (AC) power notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_charge_control_end_threshold,
"Enable charge threshold notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_profile,
"Enable profile change notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_panel_od,
"Enable panel overdrive notification",
)
.clicked()
|| ui
.checkbox(
&mut enabled_notifications.receive_notify_post_boot_sound,
"Enable BIOS post sound notification",
)
.clicked()
{
states
.notifs_enabled
.store(config.enable_notifications, Ordering::SeqCst);
config
.save()
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
if let Ok(mut lock) = states.enabled_notifications.lock() {
// Replace inner content before save
*lock = enabled_notifications;
config
.save(&lock)
.map_err(|err| {
states.error = Some(err.to_string());
})
.ok();
}
}
}

View File

@@ -7,16 +7,12 @@ use egui::{RichText, Ui};
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, Colour, Speed};
use rog_platform::supported::SupportedFunctions;
use crate::{
page_states::{AuraState, PageDataStates},
RogDbusClientBlocking,
};
use crate::system_state::{AuraState, SystemState};
pub fn aura_modes_group(
supported: &SupportedFunctions,
states: &mut PageDataStates,
states: &mut SystemState,
freq: &mut Arc<AtomicU8>,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
let mut changed = false;
@@ -172,8 +168,7 @@ pub fn aura_modes_group(
ui.separator();
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
if ui.add(egui::Button::new("Cancel")).clicked() {
let notif = states.aura.was_notified.clone();
match AuraState::new(notif, supported, dbus) {
match AuraState::new(supported, &states.asus_dbus) {
Ok(a) => states.aura.modes = a.modes,
Err(e) => states.error = Some(e.to_string()),
}
@@ -202,7 +197,9 @@ pub fn aura_modes_group(
if changed {
states.aura.current_mode = selected;
dbus.proxies()
states
.asus_dbus
.proxies()
.led()
.set_led_mode(states.aura.modes.get(&selected).unwrap())
.map_err(|err| {

View File

@@ -5,32 +5,22 @@ use rog_aura::{
};
use rog_platform::supported::SupportedFunctions;
use crate::{page_states::PageDataStates, RogDbusClientBlocking};
use crate::system_state::SystemState;
pub fn aura_power_group(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
pub fn aura_power_group(supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
ui.heading("LED settings");
match supported.keyboard_led.prod_id {
AuraDevice::X1854 | AuraDevice::X1869 | AuraDevice::X1866 => {
aura_power1(supported, states, dbus, ui)
aura_power1(supported, states, ui);
}
AuraDevice::X19B6 => aura_power2(supported, states, dbus, ui),
AuraDevice::Tuf => aura_power1(supported, states, dbus, ui),
AuraDevice::X19B6 => aura_power2(supported, states, ui),
AuraDevice::Tuf => aura_power1(supported, states, ui),
AuraDevice::Unknown => {}
}
}
fn aura_power1(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
fn aura_power1(supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
let enabled_states = &mut states.aura.enabled;
let mut boot = enabled_states.x1866.contains(&AuraDev1866::Boot);
let mut sleep = enabled_states.x1866.contains(&AuraDev1866::Sleep);
@@ -73,10 +63,10 @@ fn aura_power1(
if ui.toggle_value(&mut keyboard, "Keyboard").changed() {
changed = true;
}
if !supported.keyboard_led.multizone_led_mode.is_empty() {
if ui.toggle_value(&mut lightbar, "Lightbar").changed() {
changed = true;
}
if !supported.keyboard_led.multizone_led_mode.is_empty()
&& ui.toggle_value(&mut lightbar, "Lightbar").changed()
{
changed = true;
}
});
ui.horizontal_wrapped(|ui| {
@@ -144,7 +134,9 @@ fn aura_power1(
x19b6: vec![],
};
// build data to send
dbus.proxies()
states
.asus_dbus
.proxies()
.led()
.set_leds_power(options, enable)
.map_err(|err| {
@@ -193,7 +185,9 @@ fn aura_power1(
x19b6: vec![],
};
// build data to send
dbus.proxies()
states
.asus_dbus
.proxies()
.led()
.set_leds_power(options, enable)
.map_err(|err| {
@@ -207,12 +201,7 @@ fn aura_power1(
}
}
fn aura_power2(
supported: &SupportedFunctions,
states: &mut PageDataStates,
dbus: &mut RogDbusClientBlocking,
ui: &mut Ui,
) {
fn aura_power2(supported: &SupportedFunctions, states: &mut SystemState, ui: &mut Ui) {
let enabled_states = &mut states.aura.enabled;
let has_logo = supported
.keyboard_led
@@ -331,7 +320,9 @@ fn aura_power2(
x19b6: data,
};
// build data to send
dbus.proxies()
states
.asus_dbus
.proxies()
.led()
.set_leds_power(options, enable)
.map_err(|err| {

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