Compare commits

..

2 Commits

Author SHA1 Message Date
mihai2mn
f5f997e057 fix(ci): Resolve yaml invalid error by adding explicit stages 2026-01-16 20:43:46 +01:00
mihai2mn
3d0caa39e1 feat(rog-control-center): Major UI/UX improvements and new features
- Add software RGB animations for static-only keyboards (rainbow, color cycle)
- Add custom fan curve control via direct sysfs for unsupported laptops
- Add real-time system status bar (CPU/GPU temps, fan speeds, power draw)
- Add tray icon tooltip with live system stats
- Add power profile change notifications (Fn+F5)
- Add dGPU status notifications
- Add ROG theme with dark palette and accent colors
- Add Screenpad, Slash, and SuperGFX page stubs
- Improve fan curve graph UI
- Various UI refinements and fixes

Co-Authored-By: Gemini <noreply@google.com>
2026-01-15 20:09:40 +01:00
50 changed files with 2871 additions and 1641 deletions

1
.gitignore vendored
View File

@@ -10,6 +10,7 @@ vendor_*
.~lock.* .~lock.*
*.ods# *.ods#
*.patch *.patch
*.log
# gnome extension # gnome extension
node-modules node-modules

View File

@@ -31,6 +31,7 @@ stages:
- deploy - deploy
format: format:
stage: format
except: except:
- tags - tags
<<: *rust_cache <<: *rust_cache
@@ -42,6 +43,7 @@ format:
- rm -rf "$CI_PROJECT_DIR/ci-target" || true - rm -rf "$CI_PROJECT_DIR/ci-target" || true
check: check:
stage: check
except: except:
- tags - tags
<<: *rust_cache <<: *rust_cache
@@ -55,6 +57,7 @@ check:
- rm -rf "$CI_PROJECT_DIR/ci-target" || true - rm -rf "$CI_PROJECT_DIR/ci-target" || true
test: test:
stage: test
except: except:
- tags - tags
<<: *rust_cache <<: *rust_cache
@@ -65,6 +68,7 @@ test:
- rm -rf "$CI_PROJECT_DIR/ci-target" || true - rm -rf "$CI_PROJECT_DIR/ci-target" || true
release: release:
stage: release
only: only:
- tags - tags
<<: *rust_cache <<: *rust_cache
@@ -90,7 +94,7 @@ pages:
- rm -rf public - rm -rf public
- mkdir public - mkdir public
- cp -R ci-target/doc/* public - cp -R ci-target/doc/* public
- if [ -f extra/index.html ]; then cp extra/index.html public; else echo "no extra/index.html to copy"; fi - cp extra/index.html public
artifacts: artifacts:
paths: paths:
- public - public

View File

@@ -1,13 +1,5 @@
# Changelog # Changelog
## [6.3.1]
### Changes
- Removed a lighting mode that is unavailable in windows to G835L: thanks to @shevchenko0013 again!
- Added translations for Ukranian language, thanks @shevchenko0013!
- Added LEDs definition for G615LR, thanks @btnrv
- Fix improper usage of Quiet when only LowPower is available
## [6.3.0] ## [6.3.0]
### Changed ### Changed

135
Cargo.lock generated
View File

@@ -212,7 +212,7 @@ dependencies = [
[[package]] [[package]]
name = "asusctl" name = "asusctl"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"argh", "argh",
"dmi_id", "dmi_id",
@@ -232,7 +232,7 @@ dependencies = [
[[package]] [[package]]
name = "asusd" name = "asusd"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"cargo-husky", "cargo-husky",
"concat-idents", "concat-idents",
@@ -259,7 +259,7 @@ dependencies = [
[[package]] [[package]]
name = "asusd-user" name = "asusd-user"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"config-traits", "config-traits",
"dirs", "dirs",
@@ -816,9 +816,9 @@ checksum = "7b02b629252fe8ef6460461409564e2c21d0c8e77e0944f3d189ff06c4e932ad"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.53" version = "1.2.52"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@@ -864,9 +864,9 @@ dependencies = [
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.43" version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
@@ -938,7 +938,7 @@ dependencies = [
[[package]] [[package]]
name = "config-traits" name = "config-traits"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"log", "log",
"ron", "ron",
@@ -987,7 +987,7 @@ dependencies = [
[[package]] [[package]]
name = "const-field-offset" name = "const-field-offset"
version = "0.1.5" version = "0.1.5"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"const-field-offset-macro", "const-field-offset-macro",
"field-offset", "field-offset",
@@ -996,7 +996,7 @@ dependencies = [
[[package]] [[package]]
name = "const-field-offset-macro" name = "const-field-offset-macro"
version = "0.1.5" version = "0.1.5"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1274,7 +1274,7 @@ dependencies = [
[[package]] [[package]]
name = "dmi_id" name = "dmi_id"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"log", "log",
"udev 0.8.0", "udev 0.8.0",
@@ -1545,9 +1545,9 @@ dependencies = [
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.8" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
[[package]] [[package]]
name = "flate2" name = "flate2"
@@ -2246,7 +2246,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-backend-linuxkms" name = "i-slint-backend-linuxkms"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"calloop 0.14.3", "calloop 0.14.3",
"drm", "drm",
@@ -2264,7 +2264,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-backend-selector" name = "i-slint-backend-selector"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"i-slint-backend-linuxkms", "i-slint-backend-linuxkms",
@@ -2277,7 +2277,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-backend-winit" name = "i-slint-backend-winit"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"block2 0.6.2", "block2 0.6.2",
"cfg-if", "cfg-if",
@@ -2316,7 +2316,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-common" name = "i-slint-common"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"fontique", "fontique",
"ttf-parser 0.25.1", "ttf-parser 0.25.1",
@@ -2325,7 +2325,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-compiler" name = "i-slint-compiler"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"annotate-snippets", "annotate-snippets",
"by_address", "by_address",
@@ -2353,7 +2353,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-core" name = "i-slint-core"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"auto_enums", "auto_enums",
"bitflags 2.10.0", "bitflags 2.10.0",
@@ -2402,7 +2402,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-core-macros" name = "i-slint-core-macros"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"quote", "quote",
"serde_json", "serde_json",
@@ -2412,7 +2412,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-renderer-femtovg" name = "i-slint-renderer-femtovg"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"const-field-offset", "const-field-offset",
@@ -2434,7 +2434,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-renderer-skia" name = "i-slint-renderer-skia"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
@@ -2469,7 +2469,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-renderer-software" name = "i-slint-renderer-software"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"clru", "clru",
@@ -2632,8 +2632,8 @@ dependencies = [
"rayon", "rayon",
"rgb", "rgb",
"tiff", "tiff",
"zune-core 0.5.1", "zune-core 0.5.0",
"zune-jpeg 0.5.9", "zune-jpeg 0.5.8",
] ]
[[package]] [[package]]
@@ -2819,9 +2819,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.85" version = "0.3.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@@ -4482,7 +4482,7 @@ dependencies = [
[[package]] [[package]]
name = "rog-control-center" name = "rog-control-center"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"asusd", "asusd",
"concat-idents", "concat-idents",
@@ -4502,6 +4502,8 @@ dependencies = [
"rog_dbus", "rog_dbus",
"rog_platform", "rog_platform",
"rog_profiles", "rog_profiles",
"rog_slash",
"ron",
"serde", "serde",
"slint", "slint",
"slint-build", "slint-build",
@@ -4513,7 +4515,7 @@ dependencies = [
[[package]] [[package]]
name = "rog_anime" name = "rog_anime"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"dmi_id", "dmi_id",
"gif 0.12.0", "gif 0.12.0",
@@ -4527,7 +4529,7 @@ dependencies = [
[[package]] [[package]]
name = "rog_aura" name = "rog_aura"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"dmi_id", "dmi_id",
"log", "log",
@@ -4538,7 +4540,7 @@ dependencies = [
[[package]] [[package]]
name = "rog_dbus" name = "rog_dbus"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"asusd", "asusd",
"rog_anime", "rog_anime",
@@ -4552,7 +4554,7 @@ dependencies = [
[[package]] [[package]]
name = "rog_platform" name = "rog_platform"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"concat-idents", "concat-idents",
"inotify", "inotify",
@@ -4565,7 +4567,7 @@ dependencies = [
[[package]] [[package]]
name = "rog_profiles" name = "rog_profiles"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"log", "log",
"rog_platform", "rog_platform",
@@ -4576,7 +4578,7 @@ dependencies = [
[[package]] [[package]]
name = "rog_scsi" name = "rog_scsi"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"ron", "ron",
"serde", "serde",
@@ -4586,7 +4588,7 @@ dependencies = [
[[package]] [[package]]
name = "rog_simulators" name = "rog_simulators"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"log", "log",
"rog_anime", "rog_anime",
@@ -4596,7 +4598,7 @@ dependencies = [
[[package]] [[package]]
name = "rog_slash" name = "rog_slash"
version = "6.3.1" version = "6.3.0"
dependencies = [ dependencies = [
"dmi_id", "dmi_id",
"serde", "serde",
@@ -4975,7 +4977,7 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]] [[package]]
name = "slint" name = "slint"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"const-field-offset", "const-field-offset",
"i-slint-backend-selector", "i-slint-backend-selector",
@@ -4995,7 +4997,7 @@ dependencies = [
[[package]] [[package]]
name = "slint-build" name = "slint-build"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"derive_more", "derive_more",
"i-slint-compiler", "i-slint-compiler",
@@ -5006,7 +5008,7 @@ dependencies = [
[[package]] [[package]]
name = "slint-macros" name = "slint-macros"
version = "1.15.0" version = "1.15.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"i-slint-compiler", "i-slint-compiler",
"proc-macro2", "proc-macro2",
@@ -5778,9 +5780,9 @@ dependencies = [
[[package]] [[package]]
name = "typed-index-collections" name = "typed-index-collections"
version = "3.5.0" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "898160f1dfd383b4e92e17f0512a7d62f3c51c44937b23b6ffc3a1614a8eaccd" checksum = "5318ee4ce62a4e948a33915574021a7a953d83e84fba6e25c72ffcfd7dad35ff"
dependencies = [ dependencies = [
"bincode", "bincode",
"serde", "serde",
@@ -6030,7 +6032,7 @@ dependencies = [
[[package]] [[package]]
name = "vtable" name = "vtable"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"const-field-offset", "const-field-offset",
"portable-atomic", "portable-atomic",
@@ -6041,7 +6043,7 @@ dependencies = [
[[package]] [[package]]
name = "vtable-macro" name = "vtable-macro"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/slint-ui/slint.git#75fb4125d8082c5c64b4ce8220c6fe607c8caac0" source = "git+https://github.com/slint-ui/slint.git#f181d5e7b200b8986ba856fa0574425e0d6389aa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -6075,18 +6077,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "wasip2" name = "wasip2"
version = "1.0.2+wasi-0.2.9" version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [ dependencies = [
"wit-bindgen", "wit-bindgen",
] ]
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.108" version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
@@ -6097,12 +6099,11 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.58" version = "0.4.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"futures-util",
"js-sys", "js-sys",
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@@ -6111,9 +6112,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.108" version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -6121,9 +6122,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.108" version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
@@ -6134,9 +6135,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.108" version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -6278,9 +6279,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.85" version = "0.3.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -6981,9 +6982,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.51.0" version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]] [[package]]
name = "write-fonts" name = "write-fonts"
@@ -7294,9 +7295,9 @@ dependencies = [
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.15" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2" checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea"
[[package]] [[package]]
name = "zune-core" name = "zune-core"
@@ -7306,9 +7307,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]] [[package]]
name = "zune-core" name = "zune-core"
version = "0.5.1" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773"
[[package]] [[package]]
name = "zune-inflate" name = "zune-inflate"
@@ -7330,11 +7331,11 @@ dependencies = [
[[package]] [[package]]
name = "zune-jpeg" name = "zune-jpeg"
version = "0.5.9" version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87c86acb70a85b2c16f071f171847d1945e8f44812630463cd14ec83900ad01c" checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
dependencies = [ dependencies = [
"zune-core 0.5.1", "zune-core 0.5.0",
] ]
[[package]] [[package]]

View File

@@ -1,5 +1,5 @@
[workspace.package] [workspace.package]
version = "6.3.1" version = "6.3.0"
rust-version = "1.82" rust-version = "1.82"
license = "MPL-2.0" license = "MPL-2.0"
readme = "README.md" readme = "README.md"
@@ -40,6 +40,9 @@ tokio = { version = "^1.39.0", default-features = false, features = [
"time", "time",
"rt", "rt",
"rt-multi-thread", "rt-multi-thread",
"fs",
"io-util",
"io-util",
] } ] }
concat-idents = "^1.1" concat-idents = "^1.1"
dirs = "^4.0" dirs = "^4.0"

View File

@@ -13,7 +13,9 @@ Now includes a GUI, `rog-control-center`.
Due to on-going driver work the minimum suggested kernel version is always **the latest*, as improvements and fixes are continuous. Due to on-going driver work the minimum suggested kernel version is always **the latest*, as improvements and fixes are continuous.
Support for TDP is tied to the new asus-armoury driver: available mainline since linux 6.19: everything older is not supported. Support for some new features is not avilable unless you run a patched kernel with the work I am doing [in this github repo](https://github.com/flukejones/linux/tree/wip/ally-6.13). Use the linked branch, or `wip/ally-6.12`. Everything that is done here is upstreamed eventually (a long process).
Z13 devices will need [these](https://lore.kernel.org/linux-input/20240416090402.31057-1-luke@ljones.dev/T/#t)
## X11 support ## X11 support
@@ -178,7 +180,3 @@ Reference to any ASUS products, services, processes, or other information and/or
The use of ROG and ASUS trademarks within this website and associated tools and libraries is only to provide a recognisable identifier to users to enable them to associate that these tools will work with ASUS ROG laptops. The use of ROG and ASUS trademarks within this website and associated tools and libraries is only to provide a recognisable identifier to users to enable them to associate that these tools will work with ASUS ROG laptops.
--- ---
## AI Disaclaimer
Portions of this code have been written by various AI tools and reviewed by the maintainer exaclty as with every other contribution.

View File

@@ -459,18 +459,8 @@ impl CtrlPlatform {
#[zbus(signal_context)] ctxt: SignalEmitter<'_>, #[zbus(signal_context)] ctxt: SignalEmitter<'_>,
policy: PlatformProfile, policy: PlatformProfile,
) -> Result<(), FdoErr> { ) -> Result<(), FdoErr> {
// If the requested profile isn't available on this platform, and it's self.config.lock().await.platform_profile_on_battery = policy;
// `Quiet`, fall back to `LowPower` so we don't write an unavailable self.set_platform_profile(ctxt, policy).await?;
// profile into the config file.
let mut chosen = policy;
if let Ok(choices) = self.platform.get_platform_profile_choices() {
if chosen == PlatformProfile::Quiet && !choices.contains(&PlatformProfile::Quiet) {
chosen = PlatformProfile::LowPower;
}
}
self.config.lock().await.platform_profile_on_battery = chosen;
self.set_platform_profile(ctxt, chosen).await?;
self.config.lock().await.write(); self.config.lock().await.write();
Ok(()) Ok(())
} }
@@ -498,16 +488,8 @@ impl CtrlPlatform {
#[zbus(signal_context)] ctxt: SignalEmitter<'_>, #[zbus(signal_context)] ctxt: SignalEmitter<'_>,
policy: PlatformProfile, policy: PlatformProfile,
) -> Result<(), FdoErr> { ) -> Result<(), FdoErr> {
// Mirror the same fallback behavior for AC profile changes. self.config.lock().await.platform_profile_on_ac = policy;
let mut chosen = policy; self.set_platform_profile(ctxt, policy).await?;
if let Ok(choices) = self.platform.get_platform_profile_choices() {
if chosen == PlatformProfile::Quiet && !choices.contains(&PlatformProfile::Quiet) {
chosen = PlatformProfile::LowPower;
}
}
self.config.lock().await.platform_profile_on_ac = chosen;
self.set_platform_profile(ctxt, chosen).await?;
self.config.lock().await.write(); self.config.lock().await.write();
Ok(()) Ok(())
} }

View File

@@ -19,4 +19,11 @@ LABEL="asusd_start"
ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}+="asusd.service" ACTION=="add|change", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}+="asusd.service"
ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}+="asusd.service" ACTION=="add|remove", DRIVER=="asus-nb-wmi", TAG+="systemd", ENV{SYSTEMD_WANTS}+="asusd.service"
# ASUS Custom Fan Curve Control - Grant user write access
# This allows rog-control-center to adjust fan curves without sudo
SUBSYSTEM=="hwmon", ATTR{name}=="asus_custom_fan_curve", \
RUN+="/bin/sh -c 'chmod 0666 /sys%p/pwm*'", \
RUN+="/bin/sh -c 'chmod 0666 /sys%p/temp*_auto_point*_pwm'", \
RUN+="/bin/sh -c 'chmod 0666 /sys%p/temp*_auto_point*_temp'"
LABEL="asusd_end" LABEL="asusd_end"

View File

@@ -20,7 +20,7 @@
%global debug_package %{nil} %global debug_package %{nil}
%endif %endif
%define version 6.3.1 %define version 6.3.0
%define specrelease %{?dist} %define specrelease %{?dist}
%define pkg_release 1%{specrelease} %define pkg_release 1%{specrelease}

View File

@@ -1,23 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>asusctl docs</title>
<!-- Redirect to the generated crate docs -->
<meta http-equiv="refresh" content="0;url=asusctl/index.html">
<link rel="canonical" href="asusctl/index.html">
<style>
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; color:#222; display:flex; align-items:center; justify-content:center; height:100vh; margin:0 }
.box { text-align:center }
a { color: #0366d6 }
</style>
</head>
<body>
<div class="box">
<h1>asusctl documentation</h1>
<p>Redirecting to the generated docs — if your browser doesn't redirect automatically, <a href="asusctl/index.html">click here</a>.</p>
<p>If you expected a different landing page, update <code>extra/index.html</code> accordingly.</p>
</div>
</body>
</html>

View File

@@ -332,15 +332,6 @@
advanced_type: r#None, advanced_type: r#None,
power_zones: [Keyboard, Lightbar], power_zones: [Keyboard, Lightbar],
), ),
(
device_name: "G615LR",
product_id: "",
layout_name: "g634j-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave],
basic_zones: [Key1, Key2, Key3, Key4],
advanced_type: r#None,
power_zones: [Keyboard, Lightbar],
),
( (
device_name: "G634J", device_name: "G634J",
product_id: "", product_id: "",
@@ -579,7 +570,7 @@
device_name: "G835L", device_name: "G835L",
product_id: "", product_id: "",
layout_name: "g814ji-per-key", layout_name: "g814ji-per-key",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Comet, Flash], basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Star, Rain, Highlight, Laser, Ripple, Pulse, Comet, Flash],
basic_zones: [], basic_zones: [],
advanced_type: PerKey, advanced_type: PerKey,
power_zones: [Keyboard, Lightbar, Logo], power_zones: [Keyboard, Lightbar, Logo],

View File

@@ -29,6 +29,7 @@ rog_dbus = { path = "../rog-dbus" }
rog_aura = { path = "../rog-aura" } rog_aura = { path = "../rog-aura" }
rog_profiles = { path = "../rog-profiles" } rog_profiles = { path = "../rog-profiles" }
rog_platform = { path = "../rog-platform" } rog_platform = { path = "../rog-platform" }
rog_slash = { path = "../rog-slash" }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false } supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false }
dmi_id = { path = "../dmi-id" } dmi_id = { path = "../dmi-id" }
@@ -39,6 +40,7 @@ env_logger.workspace = true
tokio.workspace = true tokio.workspace = true
serde.workspace = true serde.workspace = true
zbus.workspace = true zbus.workspace = true
ron.workspace = true
dirs.workspace = true dirs.workspace = true
notify-rust.workspace = true notify-rust.workspace = true
concat-idents.workspace = true concat-idents.workspace = true

View File

@@ -11,16 +11,21 @@ use gumdrop::Options;
use log::{debug, info, warn, LevelFilter}; use log::{debug, info, warn, LevelFilter};
use rog_control_center::cli_options::CliStart; use rog_control_center::cli_options::CliStart;
use rog_control_center::config::Config; use rog_control_center::config::Config;
use tokio::runtime::Runtime;
thread_local! {
pub static UI: std::cell::RefCell<Option<MainWindow>> = Default::default();
pub static TRAY_TOOLTIP: std::cell::RefCell<Option<TrayTooltip>> = Default::default();
}
use rog_control_center::error::Result; use rog_control_center::error::Result;
use rog_control_center::notify::start_notifications; use rog_control_center::notify::start_notifications;
use rog_control_center::slint::ComponentHandle; use rog_control_center::slint::ComponentHandle;
use rog_control_center::tray::init_tray; use rog_control_center::tray::{init_tray, TrayEvent, TrayStats};
use rog_control_center::ui::setup_window; use rog_control_center::ui::setup_window;
use rog_control_center::zbus_proxies::{ use rog_control_center::zbus_proxies::{
AppState, ROGCCZbus, ROGCCZbusProxyBlocking, ZBUS_IFACE, ZBUS_PATH, AppState, ROGCCZbus, ROGCCZbusProxyBlocking, ZBUS_IFACE, ZBUS_PATH,
}; };
use rog_control_center::{print_versions, MainWindow}; use rog_control_center::{print_versions, MainWindow, TrayTooltip};
use tokio::runtime::Runtime;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
@@ -165,17 +170,33 @@ async fn main() -> Result<()> {
start_notifications(config.clone(), &rt)?; start_notifications(config.clone(), &rt)?;
if enable_tray_icon { let (tray_tx, mut tray_rx) = tokio::sync::mpsc::unbounded_channel();
init_tray(supported_properties, config.clone()); // Channel for broadcasting system stats to the tray tooltip
} let (stats_tx, stats_rx) = tokio::sync::watch::channel(TrayStats::default());
thread_local! { pub static UI: std::cell::RefCell<Option<MainWindow>> = Default::default()}; if enable_tray_icon {
init_tray(supported_properties, config.clone(), tray_tx, stats_rx);
}
// i_slint_backend_selector::with_platform(|_| Ok(())).unwrap(); // i_slint_backend_selector::with_platform(|_| Ok(())).unwrap();
if !startup_in_background { if !startup_in_background {
if let Ok(mut app_state) = app_state.lock() { if let Ok(mut app_state) = app_state.lock() {
*app_state = AppState::MainWindowShouldOpen; *app_state = AppState::MainWindowShouldOpen;
} }
} else {
// Even in background, we need the UI handle for status polling and tray sync
let config_copy = config.clone();
let stats_tx_copy = stats_tx.clone();
slint::invoke_from_event_loop(move || {
UI.with(|ui_cell| {
let mut ui = ui_cell.borrow_mut();
if ui.is_none() {
let newui = setup_window(config_copy, stats_tx_copy.clone());
ui.replace(newui);
}
});
})
.ok();
} }
if std::env::var("RUST_TRANSLATIONS").is_ok() { if std::env::var("RUST_TRANSLATIONS").is_ok() {
@@ -190,9 +211,18 @@ async fn main() -> Result<()> {
thread::spawn(move || { thread::spawn(move || {
let mut state = AppState::StartingUp; let mut state = AppState::StartingUp;
loop { loop {
// Handle tray events
while let Ok(event) = tray_rx.try_recv() {
match event {
TrayEvent::ToggleTooltip(_, _) => {
// Native tooltip handled by ksni, no custom window needed
}
}
}
if is_rog_ally { if is_rog_ally {
let config_copy_2 = config.clone(); let config_copy_2 = config.clone();
let newui = setup_window(config.clone()); let newui = setup_window(config.clone(), stats_tx.clone());
newui.window().on_close_requested(move || { newui.window().on_close_requested(move || {
exit(0); exit(0);
}); });
@@ -233,6 +263,7 @@ async fn main() -> Result<()> {
let config_copy = config.clone(); let config_copy = config.clone();
let app_state_copy = app_state.clone(); let app_state_copy = app_state.clone();
let stats_tx_loop = stats_tx.clone();
slint::invoke_from_event_loop(move || { slint::invoke_from_event_loop(move || {
UI.with(|ui| { UI.with(|ui| {
let app_state_copy = app_state_copy.clone(); let app_state_copy = app_state_copy.clone();
@@ -247,7 +278,7 @@ async fn main() -> Result<()> {
}); });
} else { } else {
let config_copy_2 = config_copy.clone(); let config_copy_2 = config_copy.clone();
let newui = setup_window(config_copy); let newui = setup_window(config_copy, stats_tx_loop.clone());
newui.window().on_close_requested(move || { newui.window().on_close_requested(move || {
if let Ok(mut app_state) = app_state_copy.lock() { if let Ok(mut app_state) = app_state_copy.lock() {
*app_state = AppState::MainWindowClosed; *app_state = AppState::MainWindowClosed;

View File

@@ -11,6 +11,8 @@ use std::time::Duration;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use notify_rust::{Hint, Notification, Timeout}; use notify_rust::{Hint, Notification, Timeout};
use rog_dbus::zbus_platform::PlatformProxy;
use rog_platform::platform::PlatformProfile;
use rog_platform::power::AsusPower; use rog_platform::power::AsusPower;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use supergfxctl::pci_device::GfxPower; use supergfxctl::pci_device::GfxPower;
@@ -28,6 +30,7 @@ pub struct EnabledNotifications {
pub enabled: bool, pub enabled: bool,
pub receive_notify_gfx: bool, pub receive_notify_gfx: bool,
pub receive_notify_gfx_status: bool, pub receive_notify_gfx_status: bool,
pub receive_notify_platform_profile: bool,
} }
impl Default for EnabledNotifications { impl Default for EnabledNotifications {
@@ -36,6 +39,7 @@ impl Default for EnabledNotifications {
enabled: true, enabled: true,
receive_notify_gfx: true, receive_notify_gfx: true,
receive_notify_gfx_status: true, receive_notify_gfx_status: true,
receive_notify_platform_profile: true,
} }
} }
} }
@@ -86,6 +90,57 @@ fn start_dpu_status_mon(config: Arc<Mutex<Config>>) {
} }
} }
/// Start monitoring for platform profile changes (triggered by Fn+F5 or software)
/// and display an OSD notification when the profile changes.
fn start_platform_profile_mon(config: Arc<Mutex<Config>>, rt: &Runtime) {
let enabled_notifications_copy = config.clone();
rt.spawn(async move {
let conn = match zbus::Connection::system().await {
Ok(c) => c,
Err(e) => {
error!("zbus signal: platform_profile_mon: {e}");
return;
}
};
let proxy = match PlatformProxy::builder(&conn).build().await {
Ok(p) => p,
Err(e) => {
error!("zbus signal: platform_profile_mon proxy: {e}");
return;
}
};
// Get initial profile to avoid notification on startup
let mut last_profile = proxy.platform_profile().await.ok();
info!("Started platform profile change monitor");
use futures_util::StreamExt;
let mut stream = proxy.receive_platform_profile_changed().await;
while let Some(e) = stream.next().await {
if let Ok(config) = enabled_notifications_copy.lock() {
if !config.notifications.enabled
|| !config.notifications.receive_notify_platform_profile
{
continue;
}
}
if let Ok(new_profile) = e.get().await {
// Only show notification if profile actually changed
if last_profile != Some(new_profile) {
debug!("Platform profile changed to: {:?}", new_profile);
if let Err(e) = do_platform_profile_notif("Power Profile:", &new_profile)
.show()
.map(|n| n.on_close(|_| ()))
{
warn!("Failed to show platform profile notification: {e}");
}
last_profile = Some(new_profile);
}
}
}
});
}
pub fn start_notifications( pub fn start_notifications(
config: Arc<Mutex<Config>>, config: Arc<Mutex<Config>>,
rt: &Runtime, rt: &Runtime,
@@ -144,6 +199,9 @@ pub fn start_notifications(
info!("Attempting to start plain dgpu status monitor"); info!("Attempting to start plain dgpu status monitor");
start_dpu_status_mon(config.clone()); start_dpu_status_mon(config.clone());
info!("Starting platform profile change monitor");
start_platform_profile_mon(config.clone(), rt);
// GPU MUX Mode notif // GPU MUX Mode notif
// TODO: need to get armoury attrs and iter to find // TODO: need to get armoury attrs and iter to find
// let enabled_notifications_copy = config.clone(); // let enabled_notifications_copy = config.clone();
@@ -209,3 +267,28 @@ fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Notification {
notif.icon(icon); notif.icon(icon);
notif notif
} }
/// Create a notification for platform profile (power mode) changes.
/// Uses profile-specific icons and user-friendly names.
fn do_platform_profile_notif(message: &str, profile: &PlatformProfile) -> Notification {
let profile_name = match profile {
PlatformProfile::Balanced => "Balanced",
PlatformProfile::Performance => "Performance",
PlatformProfile::Quiet => "Quiet",
PlatformProfile::LowPower => "Low Power",
PlatformProfile::Custom => "Custom",
};
let mut notif = base_notification(message, &profile_name.to_owned());
// Use appropriate icons for each profile
// These icons should be available in the system or ROG icon pack
let icon = match profile {
PlatformProfile::Balanced => "asus_notif_blue", // Blue for balanced
PlatformProfile::Performance => "asus_notif_red", // Red for performance
PlatformProfile::Quiet => "asus_notif_green", // Green for quiet/power saving
PlatformProfile::LowPower => "asus_notif_green", // Green for low power
PlatformProfile::Custom => "asus_notif_white", // White for custom
};
notif.icon(icon);
notif
}

View File

@@ -59,6 +59,29 @@ struct AsusTray {
current_title: String, current_title: String,
current_icon: Icon, current_icon: Icon,
proxy: ROGCCZbusProxyBlocking<'static>, proxy: ROGCCZbusProxyBlocking<'static>,
tray_channel: Option<tokio::sync::mpsc::UnboundedSender<TrayEvent>>,
// System stats for native tooltip
cpu_temp: String,
gpu_temp: String,
cpu_fan: String,
gpu_fan: String,
power_w: String,
power_profile: String,
}
#[derive(Debug, Clone, Copy)]
pub enum TrayEvent {
ToggleTooltip(i32, i32),
}
#[derive(Debug, Clone, Default)]
pub struct TrayStats {
pub cpu_temp: String,
pub gpu_temp: String,
pub cpu_fan: String,
pub gpu_fan: String,
pub power_w: String,
pub power_profile: String,
} }
impl ksni::Tray for AsusTray { impl ksni::Tray for AsusTray {
@@ -78,6 +101,26 @@ impl ksni::Tray for AsusTray {
ksni::Status::Active ksni::Status::Active
} }
fn activate(&mut self, x: i32, y: i32) {
if let Some(tx) = &self.tray_channel {
let _ = tx.send(TrayEvent::ToggleTooltip(x, y));
}
}
fn tool_tip(&self) -> ksni::ToolTip {
ksni::ToolTip {
title: "ROG Control Center".into(),
description: format!(
"<b>Profile:</b> {}\n<b>CPU:</b> {}°C | <b>GPU:</b> {}°C\n<b>Fans:</b> {} / {} RPM\n<b>Power:</b> {} W",
self.power_profile,
self.cpu_temp, self.gpu_temp,
self.cpu_fan, self.gpu_fan,
self.power_w
),
..Default::default()
}
}
fn menu(&self) -> Vec<ksni::MenuItem<Self>> { fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
use ksni::menu::*; use ksni::menu::*;
vec![ vec![
@@ -155,7 +198,12 @@ fn find_dgpu() -> Option<Device> {
} }
/// The tray is controlled somewhat by `Arc<Mutex<SystemState>>` /// The tray is controlled somewhat by `Arc<Mutex<SystemState>>`
pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Config>>) { pub fn init_tray(
_supported_properties: Vec<Properties>,
config: Arc<Mutex<Config>>,
tray_channel: tokio::sync::mpsc::UnboundedSender<TrayEvent>,
mut stats_rx: tokio::sync::watch::Receiver<TrayStats>,
) {
tokio::spawn(async move { tokio::spawn(async move {
let user_con = zbus::blocking::Connection::session().unwrap(); let user_con = zbus::blocking::Connection::session().unwrap();
let proxy = ROGCCZbusProxyBlocking::new(&user_con).unwrap(); let proxy = ROGCCZbusProxyBlocking::new(&user_con).unwrap();
@@ -166,6 +214,14 @@ pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Confi
current_title: TRAY_LABEL.to_string(), current_title: TRAY_LABEL.to_string(),
current_icon: rog_red.clone(), current_icon: rog_red.clone(),
proxy, proxy,
tray_channel: Some(tray_channel),
// Initialize stats fields
cpu_temp: "--".into(),
gpu_temp: "--".into(),
cpu_fan: "--".into(),
gpu_fan: "--".into(),
power_w: "--".into(),
power_profile: "Unknown".into(),
}; };
// TODO: return an error to the UI // TODO: return an error to the UI
@@ -225,28 +281,45 @@ pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Confi
info!("Started ROGTray"); info!("Started ROGTray");
let mut last_power = GfxPower::Unknown; let mut last_power = GfxPower::Unknown;
let dev = find_dgpu(); let dev = find_dgpu();
// Loop with select! to handle both periodic checks and stats updates
loop { loop {
tokio::time::sleep(Duration::from_millis(1000)).await; tokio::select! {
if let Ok(lock) = config.try_lock() { _ = stats_rx.changed() => {
if !lock.enable_tray_icon { let stats = stats_rx.borrow().clone();
return; tray.update(move |t| {
t.cpu_temp = stats.cpu_temp;
t.gpu_temp = stats.gpu_temp;
t.cpu_fan = stats.cpu_fan;
t.gpu_fan = stats.gpu_fan;
t.power_w = stats.power_w;
t.power_profile = stats.power_profile;
}).await;
} }
} _ = tokio::time::sleep(Duration::from_millis(1000)) => {
if has_supergfx { if let Ok(lock) = config.try_lock() {
if let Ok(mode) = gfx_proxy.mode().await { if !lock.enable_tray_icon {
if let Ok(power) = gfx_proxy.power().await { return;
if last_power != power {
set_tray_icon_and_tip(mode, power, &mut tray, has_supergfx).await;
last_power = power;
} }
} }
} // Handle GPU icon updates
} else if let Some(dev) = dev.as_ref() { if has_supergfx {
if let Ok(power) = dev.get_runtime_status() { if let Ok(mode) = gfx_proxy.mode().await {
if last_power != power { if let Ok(power) = gfx_proxy.power().await {
set_tray_icon_and_tip(GfxMode::Hybrid, power, &mut tray, has_supergfx) if last_power != power {
.await; set_tray_icon_and_tip(mode, power, &mut tray, has_supergfx).await;
last_power = power; last_power = power;
}
}
}
} else if let Some(dev) = dev.as_ref() {
if let Ok(power) = dev.get_runtime_status() {
if last_power != power {
set_tray_icon_and_tip(GfxMode::Hybrid, power, &mut tray, has_supergfx)
.await;
last_power = power;
}
}
} }
} }
} }

View File

@@ -0,0 +1,200 @@
//! Software-based keyboard animation for keyboards that only support Static mode.
//! Provides Rainbow and Color Cycle animations via timer-based color updates.
use log::{info, warn};
use slint::Weak;
use std::process::Command;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Arc;
use std::time::Duration;
use crate::MainWindow;
/// Animation mode enum matching the UI
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum AnimationMode {
#[default]
None,
Rainbow,
ColorCycle,
}
impl From<i32> for AnimationMode {
fn from(v: i32) -> Self {
match v {
1 => AnimationMode::Rainbow,
2 => AnimationMode::ColorCycle,
_ => AnimationMode::None,
}
}
}
/// Shared state for the animator
pub struct AnimatorState {
/// Current animation mode
pub mode: AtomicU32,
/// Animation speed in milliseconds (update interval)
pub speed_ms: AtomicU32,
/// Stop signal
pub stop: AtomicBool,
/// Current hue for rainbow mode (0-360)
hue: AtomicU32,
}
impl Default for AnimatorState {
fn default() -> Self {
Self {
mode: AtomicU32::new(0),
speed_ms: AtomicU32::new(200),
stop: AtomicBool::new(false),
hue: AtomicU32::new(0),
}
}
}
/// Convert HSV to RGB (H: 0-360, S: 0-100, V: 0-100)
fn hsv_to_rgb(h: u32, s: u32, v: u32) -> (u8, u8, u8) {
let s = s as f32 / 100.0;
let v = v as f32 / 100.0;
let c = v * s;
let h_prime = (h as f32 / 60.0) % 6.0;
let x = c * (1.0 - ((h_prime % 2.0) - 1.0).abs());
let m = v - c;
let (r, g, b) = match h_prime as u32 {
0 => (c, x, 0.0),
1 => (x, c, 0.0),
2 => (0.0, c, x),
3 => (0.0, x, c),
4 => (x, 0.0, c),
_ => (c, 0.0, x),
};
(
((r + m) * 255.0) as u8,
((g + m) * 255.0) as u8,
((b + m) * 255.0) as u8,
)
}
/// Format RGB as hex color string for asusctl
fn rgb_to_hex(r: u8, g: u8, b: u8) -> String {
format!("{:02x}{:02x}{:02x}", r, g, b)
}
// Simple LCG for random numbers to avoid pulling in rand crate
fn next_random(seed: &mut u64) -> u32 {
*seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
(*seed >> 32) as u32
}
/// Start the animation loop (runs in tokio task)
pub fn start_animator(state: Arc<AnimatorState>, _ui_weak: Weak<MainWindow>) {
info!("Starting keyboard animator");
tokio::spawn(async move {
// Local state for Color Cycle (RGB)
let mut current_r: f32 = 255.0;
let mut current_g: f32 = 0.0;
let mut current_b: f32 = 0.0;
let mut target_r: f32 = 0.0;
let mut target_g: f32 = 255.0;
let mut target_b: f32 = 0.0;
let mut seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(12345);
loop {
// Check for stop signal
if state.stop.load(Ordering::Relaxed) {
info!("Animator stopping");
break;
}
let mode = AnimationMode::from(state.mode.load(Ordering::Relaxed) as i32);
// Cap speed at 150ms for stability
let raw_speed = state.speed_ms.load(Ordering::Relaxed);
let effective_speed = raw_speed.max(150) as u64;
if mode == AnimationMode::None {
// No animation, sleep longer
tokio::time::sleep(Duration::from_millis(500)).await;
continue;
}
// Calculate next color
let hex_color = match mode {
AnimationMode::Rainbow => {
// Hue step 1 for smooth, granular transitions
let hue = state.hue.fetch_add(1, Ordering::Relaxed) % 360;
let (r, g, b) = hsv_to_rgb(hue, 100, 100);
rgb_to_hex(r, g, b)
}
AnimationMode::ColorCycle => {
// RGB Linear Interpolation (Fading) - NOT Rainbow
// 1. Check distance to target
let dist_sq = (target_r - current_r).powi(2)
+ (target_g - current_g).powi(2)
+ (target_b - current_b).powi(2);
// If close, pick new random target color
if dist_sq < 100.0 {
let next_h = next_random(&mut seed) % 360;
let (r, g, b) = hsv_to_rgb(next_h, 100, 100);
target_r = r as f32;
target_g = g as f32;
target_b = b as f32;
}
// 2. Lerp towards target (5% per frame for smooth ease-out)
let factor = 0.05;
current_r += (target_r - current_r) * factor;
current_g += (target_g - current_g) * factor;
current_b += (target_b - current_b) * factor;
rgb_to_hex(current_r as u8, current_g as u8, current_b as u8)
}
AnimationMode::None => continue,
};
// Send color update via asusctl command (blocking, AWAITED to prevent races)
let hex = hex_color.clone();
let _ = tokio::task::spawn_blocking(move || {
let result = Command::new("asusctl")
.args([
"aura", "static", "-c", &hex,
])
.output();
if let Err(e) = result {
warn!("Failed to set aura color: {}", e);
}
})
.await;
// Sleep for the animation speed interval
tokio::time::sleep(Duration::from_millis(effective_speed)).await;
}
});
}
/// Stop the animator
pub fn stop_animator(state: &Arc<AnimatorState>) {
state.stop.store(true, Ordering::Relaxed);
state.mode.store(0, Ordering::Relaxed);
}
/// Set animation mode
pub fn set_animation_mode(state: &Arc<AnimatorState>, mode: AnimationMode) {
state.mode.store(mode as u32, Ordering::Relaxed);
// Reset stop flag in case we're restarting
state.stop.store(false, Ordering::Relaxed);
}
/// Set animation speed
pub fn set_animation_speed(state: &Arc<AnimatorState>, speed_ms: u32) {
let clamped = speed_ms.clamp(50, 2000);
state.speed_ms.store(clamped, Ordering::Relaxed);
}

View File

@@ -1,6 +1,12 @@
pub mod aura_animator;
pub mod setup_anime; pub mod setup_anime;
pub mod setup_aura; pub mod setup_aura;
pub mod setup_fan_curve_custom;
pub mod setup_fans; pub mod setup_fans;
pub mod setup_screenpad;
pub mod setup_slash;
pub mod setup_status;
pub mod setup_supergfx;
pub mod setup_system; pub mod setup_system;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -11,9 +17,14 @@ use rog_dbus::list_iface_blocking;
use slint::{ComponentHandle, SharedString, Weak}; use slint::{ComponentHandle, SharedString, Weak};
use crate::config::Config; use crate::config::Config;
use crate::tray::TrayStats;
use crate::ui::setup_anime::setup_anime_page; use crate::ui::setup_anime::setup_anime_page;
use crate::ui::setup_aura::setup_aura_page; use crate::ui::setup_aura::setup_aura_page;
use crate::ui::setup_fans::setup_fan_curve_page; use crate::ui::setup_fans::setup_fan_curve_page;
use crate::ui::setup_screenpad::setup_screenpad;
use crate::ui::setup_slash::setup_slash;
use crate::ui::setup_status::setup_status;
use crate::ui::setup_supergfx::setup_supergfx;
use crate::ui::setup_system::{setup_system_page, setup_system_page_callbacks}; use crate::ui::setup_system::{setup_system_page, setup_system_page_callbacks};
use crate::{AppSettingsPageData, MainWindow}; use crate::{AppSettingsPageData, MainWindow};
@@ -82,7 +93,10 @@ pub fn show_toast(
}; };
} }
pub fn setup_window(config: Arc<Mutex<Config>>) -> MainWindow { pub fn setup_window(
config: Arc<Mutex<Config>>,
stats_tx: tokio::sync::watch::Sender<TrayStats>,
) -> MainWindow {
slint::set_xdg_app_id("rog-control-center") slint::set_xdg_app_id("rog-control-center")
.map_err(|e| warn!("Couldn't set application ID: {e:?}")) .map_err(|e| warn!("Couldn't set application ID: {e:?}"))
.ok(); .ok();
@@ -101,9 +115,27 @@ pub fn setup_window(config: Arc<Mutex<Config>>) -> MainWindow {
available.contains(&"xyz.ljones.Platform".to_string()), available.contains(&"xyz.ljones.Platform".to_string()),
available.contains(&"xyz.ljones.Aura".to_string()), available.contains(&"xyz.ljones.Aura".to_string()),
available.contains(&"xyz.ljones.Anime".to_string()), available.contains(&"xyz.ljones.Anime".to_string()),
available.contains(&"xyz.ljones.Slash".to_string()),
// Supergfx check
{
if let Ok(conn) = zbus::blocking::Connection::system() {
zbus::blocking::fdo::DBusProxy::new(&conn)
.ok()
.and_then(|p| {
p.name_has_owner("org.supergfxctl.Daemon".try_into().ok()?)
.ok()
})
.unwrap_or(false)
} else {
false
}
},
// Screenpad check (Backlight interface)
available.contains(&"xyz.ljones.Backlight".to_string()),
available.contains(&"xyz.ljones.FanCurves".to_string()), available.contains(&"xyz.ljones.FanCurves".to_string()),
true, true,
true, true,
true,
] ]
.into(), .into(),
); );
@@ -112,6 +144,33 @@ pub fn setup_window(config: Arc<Mutex<Config>>) -> MainWindow {
slint::quit_event_loop().unwrap(); slint::quit_event_loop().unwrap();
}); });
// Auto-hide toast logic
let toast_gen = Arc::new(Mutex::new(0u64));
let ui_weak = ui.as_weak();
ui.on_start_toast_timer(move || {
let toast_gen_clone = toast_gen.clone();
let ui_weak_clone = ui_weak.clone();
let my_gen = {
if let Ok(mut g) = toast_gen.lock() {
*g += 1;
*g
} else {
0
}
};
if my_gen > 0 {
tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_secs(4)).await;
if let Ok(g) = toast_gen_clone.lock() {
if *g == my_gen {
let _ =
ui_weak_clone.upgrade_in_event_loop(move |ui| ui.invoke_hide_toast());
}
}
});
}
});
setup_app_settings_page(&ui, config.clone()); setup_app_settings_page(&ui, config.clone());
if available.contains(&"xyz.ljones.Platform".to_string()) { if available.contains(&"xyz.ljones.Platform".to_string()) {
setup_system_page(&ui, config.clone()); setup_system_page(&ui, config.clone());
@@ -123,10 +182,24 @@ pub fn setup_window(config: Arc<Mutex<Config>>) -> MainWindow {
if available.contains(&"xyz.ljones.Anime".to_string()) { if available.contains(&"xyz.ljones.Anime".to_string()) {
setup_anime_page(&ui, config.clone()); setup_anime_page(&ui, config.clone());
} }
if available.contains(&"xyz.ljones.FanCurves".to_string()) { if available.contains(&"xyz.ljones.Slash".to_string()) {
setup_fan_curve_page(&ui, config); setup_slash(&ui, config.clone());
} }
// Always try to setup supergfx if detected above, but for simplicity here we assume if sidebar has it (re-check or just run)
// We didn't capture the boolean above. Let's just run it, it handles its own availability check internally via async proxy creation.
setup_supergfx(&ui, config.clone());
if available.contains(&"xyz.ljones.Backlight".to_string()) {
setup_screenpad(&ui, config.clone());
}
if available.contains(&"xyz.ljones.FanCurves".to_string()) {
setup_fan_curve_page(&ui, config.clone());
}
setup_status(&ui, config, stats_tx);
ui ui
} }
@@ -153,18 +226,49 @@ pub fn setup_app_settings_page(ui: &MainWindow, config: Arc<Mutex<Config>>) {
lock.write(); lock.write();
} }
}); });
// Master notifications toggle
let config_copy = config.clone(); let config_copy = config.clone();
global.on_set_enable_dgpu_notifications(move |enable| { global.on_set_notifications_enabled(move |enable| {
if let Ok(mut lock) = config_copy.try_lock() { if let Ok(mut lock) = config_copy.try_lock() {
lock.notifications.enabled = enable; lock.notifications.enabled = enable;
lock.write(); lock.write();
} }
}); });
// Granular notification toggles
let config_copy = config.clone();
global.on_set_notify_gfx_switch(move |enable| {
if let Ok(mut lock) = config_copy.try_lock() {
lock.notifications.receive_notify_gfx = enable;
lock.write();
}
});
let config_copy = config.clone();
global.on_set_notify_gfx_status(move |enable| {
if let Ok(mut lock) = config_copy.try_lock() {
lock.notifications.receive_notify_gfx_status = enable;
lock.write();
}
});
let config_copy = config.clone();
global.on_set_notify_platform_profile(move |enable| {
if let Ok(mut lock) = config_copy.try_lock() {
lock.notifications.receive_notify_platform_profile = enable;
lock.write();
}
});
// Initialize UI values from config
if let Ok(lock) = config.try_lock() { if let Ok(lock) = config.try_lock() {
global.set_run_in_background(lock.run_in_background); global.set_run_in_background(lock.run_in_background);
global.set_startup_in_background(lock.startup_in_background); global.set_startup_in_background(lock.startup_in_background);
global.set_enable_tray_icon(lock.enable_tray_icon); global.set_enable_tray_icon(lock.enable_tray_icon);
global.set_enable_dgpu_notifications(lock.notifications.enabled); global.set_notifications_enabled(lock.notifications.enabled);
global.set_notify_gfx_switch(lock.notifications.receive_notify_gfx);
global.set_notify_gfx_status(lock.notifications.receive_notify_gfx_status);
global.set_notify_platform_profile(lock.notifications.receive_notify_platform_profile);
} }
} }

View File

@@ -7,6 +7,9 @@ use rog_dbus::zbus_aura::AuraProxy;
use slint::{ComponentHandle, Model, RgbaColor, SharedString}; use slint::{ComponentHandle, Model, RgbaColor, SharedString};
use crate::config::Config; use crate::config::Config;
use crate::ui::aura_animator::{
set_animation_mode, set_animation_speed, start_animator, AnimationMode, AnimatorState,
};
use crate::ui::show_toast; use crate::ui::show_toast;
use crate::{ use crate::{
set_ui_callbacks, set_ui_props_async, AuraPageData, MainWindow, PowerZones as SlintPowerZones, set_ui_callbacks, set_ui_props_async, AuraPageData, MainWindow, PowerZones as SlintPowerZones,
@@ -123,8 +126,17 @@ pub fn setup_aura_page(ui: &MainWindow, _states: Arc<Mutex<Config>>) {
.ok(); .ok();
} }
// Create animator state (shared across callbacks)
let animator_state = Arc::new(AnimatorState::default());
if let Ok(modes) = aura.supported_basic_modes().await { if let Ok(modes) = aura.supported_basic_modes().await {
log::debug!("Available LED modes {modes:?}"); log::debug!("Available LED modes {modes:?}");
// Check if only Static mode is available (enable software animation)
let static_only = modes.len() == 1 && modes.iter().any(|m| *m == 0.into());
let handle_for_anim = handle.clone();
let animator_state_clone = animator_state.clone();
handle handle
.upgrade_in_event_loop(move |handle| { .upgrade_in_event_loop(move |handle| {
let m: Vec<i32> = modes.iter().map(|n| (*n).into()).collect(); let m: Vec<i32> = modes.iter().map(|n| (*n).into()).collect();
@@ -143,6 +155,33 @@ pub fn setup_aura_page(ui: &MainWindow, _states: Arc<Mutex<Config>>) {
handle handle
.global::<AuraPageData>() .global::<AuraPageData>()
.set_available_mode_names(res.as_slice().into()); .set_available_mode_names(res.as_slice().into());
// Enable software animation if only Static mode is available
if static_only {
info!("Only Static mode available - enabling software animation controls");
handle
.global::<AuraPageData>()
.set_soft_animation_available(true);
// Start the animator thread
start_animator(animator_state_clone.clone(), handle_for_anim.clone());
// Connect mode callback
let state_for_mode = animator_state_clone.clone();
handle
.global::<AuraPageData>()
.on_cb_soft_animation_mode(move |mode| {
set_animation_mode(&state_for_mode, AnimationMode::from(mode));
});
// Connect speed callback
let state_for_speed = animator_state_clone.clone();
handle
.global::<AuraPageData>()
.on_cb_soft_animation_speed(move |speed| {
set_animation_speed(&state_for_speed, speed as u32);
});
}
}) })
.map_err(|e| error!("{e:}")) .map_err(|e| error!("{e:}"))
.ok(); .ok();

View File

@@ -0,0 +1,241 @@
use std::fs;
use std::path::{Path, PathBuf};
use log::{error, info};
use serde::{Deserialize, Serialize};
use crate::{FanType, MainWindow, Node};
const ASUS_CUSTOM_FAN_NAME: &str = "asus_custom_fan_curve";
const CONFIG_FILE_NAME: &str = "custom_fans.ron";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomCurvePoint {
pub temp: u8,
pub pwm: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CustomFanConfig {
pub cpu_curve: Vec<CustomCurvePoint>,
pub gpu_curve: Vec<CustomCurvePoint>,
pub enabled: bool,
}
#[derive(Clone)]
struct SysfsPaths {
root: PathBuf,
}
impl SysfsPaths {
fn new() -> Option<Self> {
let hwmon = Path::new("/sys/class/hwmon");
if let Ok(entries) = fs::read_dir(hwmon) {
for entry in entries.flatten() {
let path = entry.path();
let name_path = path.join("name");
if let Ok(name) = fs::read_to_string(&name_path) {
if name.trim() == ASUS_CUSTOM_FAN_NAME {
info!("Found ASUS Custom Fan Control at {:?}", path);
return Some(Self { root: path });
}
}
}
}
None
}
fn enable_path(&self, index: u8) -> PathBuf {
self.root.join(format!("pwm{}_enable", index))
}
fn point_pwm_path(&self, fan_idx: u8, point_idx: u8) -> PathBuf {
self.root
.join(format!("pwm{}_auto_point{}_pwm", fan_idx, point_idx))
}
fn point_temp_path(&self, fan_idx: u8, point_idx: u8) -> PathBuf {
self.root
.join(format!("pwm{}_auto_point{}_temp", fan_idx, point_idx))
}
}
// Helper to write with logging
fn write_sysfs(path: &Path, value: &str) -> std::io::Result<()> {
// debug!("Writing {} to {:?}", value, path);
fs::write(path, value)
}
fn _read_sysfs_u8(path: &Path) -> std::io::Result<u8> {
let s = fs::read_to_string(path)?;
s.trim()
.parse::<u8>()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
}
pub fn is_custom_fan_supported() -> bool {
SysfsPaths::new().is_some()
}
// Logic to apply a full curve to a specific fan (1=CPU, 2=GPU usually)
// Implements the "Gradual Descent" algorithm
fn apply_curve_to_fan(
paths: &SysfsPaths,
fan_idx: u8,
points: &[CustomCurvePoint],
) -> std::io::Result<()> {
// Sort target points by temp (Hardware Requirement)
let mut sorted_target = points.to_vec();
sorted_target.sort_by_key(|p| p.temp);
// Ensure we have 8 points (fill with last if needed, or sensible default)
while sorted_target.len() < 8 {
if let Some(last) = sorted_target.last() {
sorted_target.push(last.clone());
} else {
sorted_target.push(CustomCurvePoint {
temp: 100,
pwm: 255,
});
}
}
sorted_target.truncate(8);
// Validate Temp Order (Synchronous Check)
for (i, p) in sorted_target.iter().enumerate() {
if i > 0 {
let prev_temp = sorted_target[i - 1].temp;
if p.temp < prev_temp {
error!("Invalid temp order");
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Temp disorder",
));
}
}
}
// Spawn completely detached thread for ALL I/O
let paths_clone = paths.clone();
let sorted_target = sorted_target.clone();
std::thread::spawn(move || {
let paths = paths_clone;
// 1. Enable custom mode
if let Err(e) = write_sysfs(&paths.enable_path(fan_idx), "1") {
error!("Failed to enable custom fan mode: {}", e);
return;
}
// 2. Write Temps
for (i, p) in sorted_target.iter().enumerate() {
let point_idx = (i + 1) as u8;
if let Err(e) = write_sysfs(
&paths.point_temp_path(fan_idx, point_idx),
&p.temp.to_string(),
) {
error!("Failed to write temp point {}: {}", point_idx, e);
}
}
// 3. Write PWMs directly (hardware handles gradual transition)
for (i, target_p) in sorted_target.iter().enumerate() {
let point_idx = (i + 1) as u8;
if let Err(e) = write_sysfs(
&paths.point_pwm_path(fan_idx, point_idx),
&target_p.pwm.to_string(),
) {
error!("Failed to write PWM point {}: {}", point_idx, e);
}
}
// 4. Ensure enable is set
let _ = write_sysfs(&paths.enable_path(fan_idx), "1");
});
Ok(())
}
fn set_fan_auto(paths: &SysfsPaths, fan_idx: u8) -> std::io::Result<()> {
// 2 = Auto (usually)
write_sysfs(&paths.enable_path(fan_idx), "2")
}
fn load_config() -> CustomFanConfig {
if let Some(config_dir) = dirs::config_dir() {
let path = config_dir.join("rog").join(CONFIG_FILE_NAME);
if let Ok(content) = fs::read_to_string(path) {
if let Ok(cfg) = ron::from_str(&content) {
return cfg;
}
}
}
CustomFanConfig::default()
}
fn save_config(config: &CustomFanConfig) {
if let Some(config_dir) = dirs::config_dir() {
let rog_dir = config_dir.join("rog");
let _ = fs::create_dir_all(&rog_dir);
let path = rog_dir.join(CONFIG_FILE_NAME);
if let Ok(s) = ron::ser::to_string_pretty(config, ron::ser::PrettyConfig::default()) {
let _ = fs::write(path, s);
}
}
}
// Public entry point called from setup_fans.rs or similar
// Returns immediately - all work is done in a detached thread
pub fn apply_custom_fan_curve(
_handle_weak: slint::Weak<MainWindow>,
fan_type: FanType,
enabled: bool,
nodes: Vec<Node>,
) {
// Fan Index: 1=CPU, 2=GPU usually.
let fan_idx = match fan_type {
FanType::CPU => 1,
FanType::GPU => 2,
_ => return, // Ignore others
};
// Convert nodes to points (fast, CPU-only)
let points: Vec<CustomCurvePoint> = nodes
.iter()
.map(|n| CustomCurvePoint {
temp: n.x as u8,
pwm: n.y as u8,
})
.collect();
// Spawn a completely detached thread for ALL I/O
std::thread::spawn(move || {
// Get paths (blocking FS operation)
let Some(paths) = SysfsPaths::new() else {
error!("No custom fan support found");
return;
};
// Save config
let mut cfg = load_config();
if enabled {
match fan_type {
FanType::CPU => cfg.cpu_curve = points.clone(),
FanType::GPU => cfg.gpu_curve = points.clone(),
_ => {}
}
}
cfg.enabled = enabled;
save_config(&cfg);
// Apply curve or set auto
if enabled {
if let Err(e) = apply_curve_to_fan(&paths, fan_idx, &points) {
error!("Failed to apply fan curve: {}", e);
}
} else if let Err(e) = set_fan_auto(&paths, fan_idx) {
error!("Failed to set fan auto: {}", e);
}
});
}

View File

@@ -1,4 +1,6 @@
use std::sync::{Arc, Mutex}; use crate::ui::show_toast;
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
use log::error; use log::error;
use rog_dbus::zbus_fan_curves::FanCurvesProxy; use rog_dbus::zbus_fan_curves::FanCurvesProxy;
@@ -8,7 +10,25 @@ use rog_profiles::fan_curve_set::CurveData;
use slint::{ComponentHandle, Model, Weak}; use slint::{ComponentHandle, Model, Weak};
use crate::config::Config; use crate::config::Config;
use crate::{FanPageData, FanType, MainWindow, Node}; use crate::{FanPageData, FanType, MainWindow, Node, Profile};
// Isolated Rust-side cache for fan curves (not affected by Slint reactivity)
type FanCacheKey = (i32, i32); // (Profile as i32, FanType as i32)
static FAN_CACHE: OnceLock<Mutex<HashMap<FanCacheKey, Vec<Node>>>> = OnceLock::new();
fn fan_cache() -> &'static Mutex<HashMap<FanCacheKey, Vec<Node>>> {
FAN_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
}
fn cache_fan_curve(profile: Profile, fan_type: FanType, nodes: Vec<Node>) {
let key = (profile as i32, fan_type as i32);
fan_cache().lock().unwrap().insert(key, nodes);
}
fn get_cached_fan_curve(profile: Profile, fan_type: FanType) -> Option<Vec<Node>> {
let key = (profile as i32, fan_type as i32);
fan_cache().lock().unwrap().get(&key).cloned()
}
pub fn update_fan_data( pub fn update_fan_data(
handle: Weak<MainWindow>, handle: Weak<MainWindow>,
@@ -19,7 +39,7 @@ pub fn update_fan_data(
handle handle
.upgrade_in_event_loop(move |handle| { .upgrade_in_event_loop(move |handle| {
let global = handle.global::<FanPageData>(); let global = handle.global::<FanPageData>();
let collect = |temp: &[u8], pwm: &[u8]| -> slint::ModelRc<Node> { let _collect = |temp: &[u8], pwm: &[u8]| -> slint::ModelRc<Node> {
let tmp: Vec<Node> = temp let tmp: Vec<Node> = temp
.iter() .iter()
.zip(pwm.iter()) .zip(pwm.iter())
@@ -33,61 +53,100 @@ pub fn update_fan_data(
for fan in bal { for fan in bal {
global.set_balanced_available(true); global.set_balanced_available(true);
let nodes_vec: Vec<Node> = fan
.temp
.iter()
.zip(fan.pwm.iter())
.map(|(x, y)| Node {
x: *x as f32,
y: *y as f32,
})
.collect();
let nodes: slint::ModelRc<Node> = nodes_vec.as_slice().into();
match fan.fan { match fan.fan {
rog_profiles::FanCurvePU::CPU => { rog_profiles::FanCurvePU::CPU => {
global.set_cpu_fan_available(true); global.set_cpu_fan_available(true);
global.set_balanced_cpu_enabled(fan.enabled); global.set_balanced_cpu_enabled(fan.enabled);
global.set_balanced_cpu(collect(&fan.temp, &fan.pwm)) global.set_balanced_cpu(nodes.clone());
cache_fan_curve(Profile::Balanced, FanType::CPU, nodes_vec);
} }
rog_profiles::FanCurvePU::GPU => { rog_profiles::FanCurvePU::GPU => {
global.set_gpu_fan_available(true); global.set_gpu_fan_available(true);
global.set_balanced_gpu_enabled(fan.enabled); global.set_balanced_gpu_enabled(fan.enabled);
global.set_balanced_gpu(collect(&fan.temp, &fan.pwm)) global.set_balanced_gpu(nodes.clone());
cache_fan_curve(Profile::Balanced, FanType::GPU, nodes_vec);
} }
rog_profiles::FanCurvePU::MID => { rog_profiles::FanCurvePU::MID => {
global.set_mid_fan_available(true); global.set_mid_fan_available(true);
global.set_balanced_mid_enabled(fan.enabled); global.set_balanced_mid_enabled(fan.enabled);
global.set_balanced_mid(collect(&fan.temp, &fan.pwm)) global.set_balanced_mid(nodes.clone());
cache_fan_curve(Profile::Balanced, FanType::Middle, nodes_vec);
} }
} }
} }
for fan in perf { for fan in perf {
global.set_performance_available(true); global.set_performance_available(true);
let nodes_vec: Vec<Node> = fan
.temp
.iter()
.zip(fan.pwm.iter())
.map(|(x, y)| Node {
x: *x as f32,
y: *y as f32,
})
.collect();
let nodes: slint::ModelRc<Node> = nodes_vec.as_slice().into();
match fan.fan { match fan.fan {
rog_profiles::FanCurvePU::CPU => { rog_profiles::FanCurvePU::CPU => {
global.set_cpu_fan_available(true); global.set_cpu_fan_available(true);
global.set_performance_cpu_enabled(fan.enabled); global.set_performance_cpu_enabled(fan.enabled);
global.set_performance_cpu(collect(&fan.temp, &fan.pwm)) global.set_performance_cpu(nodes.clone());
cache_fan_curve(Profile::Performance, FanType::CPU, nodes_vec);
} }
rog_profiles::FanCurvePU::GPU => { rog_profiles::FanCurvePU::GPU => {
global.set_gpu_fan_available(true); global.set_gpu_fan_available(true);
global.set_performance_gpu_enabled(fan.enabled); global.set_performance_gpu_enabled(fan.enabled);
global.set_performance_gpu(collect(&fan.temp, &fan.pwm)) global.set_performance_gpu(nodes.clone());
cache_fan_curve(Profile::Performance, FanType::GPU, nodes_vec);
} }
rog_profiles::FanCurvePU::MID => { rog_profiles::FanCurvePU::MID => {
global.set_mid_fan_available(true); global.set_mid_fan_available(true);
global.set_performance_mid_enabled(fan.enabled); global.set_performance_mid_enabled(fan.enabled);
global.set_performance_mid(collect(&fan.temp, &fan.pwm)) global.set_performance_mid(nodes.clone());
cache_fan_curve(Profile::Performance, FanType::Middle, nodes_vec);
} }
} }
} }
for fan in quiet { for fan in quiet {
global.set_quiet_available(true); global.set_quiet_available(true);
let nodes_vec: Vec<Node> = fan
.temp
.iter()
.zip(fan.pwm.iter())
.map(|(x, y)| Node {
x: *x as f32,
y: *y as f32,
})
.collect();
let nodes: slint::ModelRc<Node> = nodes_vec.as_slice().into();
match fan.fan { match fan.fan {
rog_profiles::FanCurvePU::CPU => { rog_profiles::FanCurvePU::CPU => {
global.set_cpu_fan_available(true); global.set_cpu_fan_available(true);
global.set_quiet_cpu_enabled(fan.enabled); global.set_quiet_cpu_enabled(fan.enabled);
global.set_quiet_cpu(collect(&fan.temp, &fan.pwm)) global.set_quiet_cpu(nodes.clone());
cache_fan_curve(Profile::Quiet, FanType::CPU, nodes_vec);
} }
rog_profiles::FanCurvePU::GPU => { rog_profiles::FanCurvePU::GPU => {
global.set_gpu_fan_available(true); global.set_gpu_fan_available(true);
global.set_quiet_gpu_enabled(fan.enabled); global.set_quiet_gpu_enabled(fan.enabled);
global.set_quiet_gpu(collect(&fan.temp, &fan.pwm)) global.set_quiet_gpu(nodes.clone());
cache_fan_curve(Profile::Quiet, FanType::GPU, nodes_vec);
} }
rog_profiles::FanCurvePU::MID => { rog_profiles::FanCurvePU::MID => {
global.set_mid_fan_available(true); global.set_mid_fan_available(true);
global.set_quiet_mid_enabled(fan.enabled); global.set_quiet_mid_enabled(fan.enabled);
global.set_quiet_mid(collect(&fan.temp, &fan.pwm)) global.set_quiet_mid(nodes.clone());
cache_fan_curve(Profile::Quiet, FanType::Middle, nodes_vec);
} }
} }
} }
@@ -171,6 +230,7 @@ pub fn setup_fan_curve_page(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
let choices_for_ui = platform_profile_choices.clone(); let choices_for_ui = platform_profile_choices.clone();
let handle_next1 = handle_copy.clone(); let handle_next1 = handle_copy.clone();
if let Err(e) = handle_copy.upgrade_in_event_loop(move |handle| { if let Err(e) = handle_copy.upgrade_in_event_loop(move |handle| {
let handle_weak_for_fans = handle.as_weak();
let global = handle.global::<FanPageData>(); let global = handle.global::<FanPageData>();
let fans1 = fans.clone(); let fans1 = fans.clone();
let choices = choices_for_ui.clone(); let choices = choices_for_ui.clone();
@@ -212,17 +272,103 @@ pub fn setup_fan_curve_page(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
update_fan_data(handle_next, balanced, perf, quiet); update_fan_data(handle_next, balanced, perf, quiet);
}); });
}); });
let handle_weak_for_cancel = handle_weak_for_fans.clone();
global.on_set_fan_data(move |fan, profile, enabled, data| { global.on_set_fan_data(move |fan, profile, enabled, data| {
if crate::ui::setup_fan_curve_custom::is_custom_fan_supported() {
let handle_weak = handle_weak_for_fans.clone();
let data: Vec<Node> = data.iter().collect();
use log::info;
info!("MainThread: Request to apply custom curve for {:?}", fan);
// Explicitly spawn a thread to handle this, preventing ANY main thread blocking
std::thread::spawn(move || {
info!("WorkerThread: applying curve for {:?}", fan);
crate::ui::setup_fan_curve_custom::apply_custom_fan_curve(
handle_weak.clone(),
fan,
enabled,
data,
);
info!("WorkerThread: returned from apply (async), clearing busy flag for {:?}", fan);
// Clear busy flag
let _ = handle_weak.upgrade_in_event_loop(move |h| {
let g = h.global::<FanPageData>();
match fan {
FanType::CPU => g.set_is_busy_cpu(false),
FanType::GPU => g.set_is_busy_gpu(false),
FanType::Middle => g.set_is_busy_mid(false),
}
info!("MainThread: cleared busy flag for {:?}", fan);
});
});
return;
}
let fans = fans.clone(); let fans = fans.clone();
let data: Vec<Node> = data.iter().collect(); let handle_weak = handle_weak_for_fans.clone();
let data = fan_data_for(fan, enabled, data); let nodes_vec: Vec<Node> = data.iter().collect();
let _data_copy = nodes_vec.clone();
let cache_copy = nodes_vec.clone(); // Clone for cache update
let fan_data = fan_data_for(fan, enabled, nodes_vec);
tokio::spawn(async move { tokio::spawn(async move {
fans.set_fan_curve(profile.into(), data) show_toast(
.await "Fan curve applied".into(),
.map_err(|e| error!("{e:}")) "Failed to apply fan curve".into(),
.ok() handle_weak.clone(),
fans.set_fan_curve(profile.into(), fan_data).await,
);
let _ = handle_weak.upgrade_in_event_loop(move |h| {
let g = h.global::<FanPageData>();
// Update Rust-side cache (isolated from Slint properties)
cache_fan_curve(profile, fan, cache_copy);
match fan {
FanType::CPU => g.set_is_busy_cpu(false),
FanType::GPU => g.set_is_busy_gpu(false),
FanType::Middle => g.set_is_busy_mid(false),
}
});
}); });
}); });
global.on_cancel(move |fan_type, profile| {
let handle_weak = handle_weak_for_cancel.clone();
let _ = handle_weak.upgrade_in_event_loop(move |h: MainWindow| {
let global = h.global::<FanPageData>();
// Retrieve from isolated Rust cache
let nodes_opt = get_cached_fan_curve(profile, fan_type);
if let Some(nodes_vec) = nodes_opt {
use log::info;
info!("Canceling {:?} {:?} - restoring {} nodes from isolated cache", fan_type, profile, nodes_vec.len());
let new_model: slint::ModelRc<Node> = nodes_vec.as_slice().into();
match (profile, fan_type) {
(crate::Profile::Balanced, FanType::CPU) => global.set_balanced_cpu(new_model),
(crate::Profile::Balanced, FanType::GPU) => global.set_balanced_gpu(new_model),
(crate::Profile::Balanced, FanType::Middle) => global.set_balanced_mid(new_model),
(crate::Profile::Performance, FanType::CPU) => global.set_performance_cpu(new_model),
(crate::Profile::Performance, FanType::GPU) => global.set_performance_gpu(new_model),
(crate::Profile::Performance, FanType::Middle) => global.set_performance_mid(new_model),
(crate::Profile::Quiet, FanType::CPU) => global.set_quiet_cpu(new_model),
(crate::Profile::Quiet, FanType::GPU) => global.set_quiet_gpu(new_model),
(crate::Profile::Quiet, FanType::Middle) => global.set_quiet_mid(new_model),
_ => {}
}
} else {
log::warn!("Cancel failed: No cached data for {:?} {:?}", fan_type, profile);
}
});
});
// Initialize warning
if crate::ui::setup_fan_curve_custom::is_custom_fan_supported() {
global.set_show_custom_warning(true);
}
}) { }) {
error!("setup_fan_curve_page: upgrade_in_event_loop: {e:?}"); error!("setup_fan_curve_page: upgrade_in_event_loop: {e:?}");
} }

View File

@@ -0,0 +1,143 @@
use log::{debug, error};
use rog_dbus::zbus_backlight::BacklightProxy;
use slint::ComponentHandle;
use std::sync::{Arc, Mutex};
use crate::config::Config;
use crate::ui::show_toast;
use crate::{MainWindow, ScreenpadPageData};
pub fn setup_screenpad(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
let handle = ui.as_weak();
tokio::spawn(async move {
// Create the connections/proxies here
let conn = match zbus::Connection::system().await {
Ok(conn) => conn,
Err(e) => {
error!("Failed to connect to system bus for Screenpad: {e:}");
return;
}
};
let backlight = match BacklightProxy::builder(&conn).build().await {
Ok(backlight) => backlight,
Err(e) => {
error!("Failed to create backlight proxy for Screenpad: {e:}");
return;
}
};
// Initialize state
debug!("Initializing Screenpad page data");
// Use helper to set initial properties
if let Ok(val) = backlight.screenpad_brightness().await {
handle
.upgrade_in_event_loop(move |h| {
h.global::<ScreenpadPageData>().set_brightness(val);
// Assume power is on if brightness > 0
h.global::<ScreenpadPageData>().set_power(val > 0);
})
.ok();
}
if let Ok(gamma_str) = backlight.screenpad_gamma().await {
if let Ok(gamma) = gamma_str.parse::<f32>() {
handle
.upgrade_in_event_loop(move |h| {
h.global::<ScreenpadPageData>().set_gamma(gamma);
})
.ok();
}
}
if let Ok(sync) = backlight.screenpad_sync_with_primary().await {
handle
.upgrade_in_event_loop(move |h| {
h.global::<ScreenpadPageData>().set_sync_with_primary(sync);
})
.ok();
}
// Set up callbacks
let handle_copy = handle.clone();
let backlight_copy = backlight.clone();
handle
.upgrade_in_event_loop(move |h| {
let global = h.global::<ScreenpadPageData>();
// Brightness Callback
let hl = handle_copy.clone();
let bl = backlight_copy.clone();
global.on_cb_brightness(move |val| {
let bl = bl.clone();
let hl = hl.clone();
tokio::spawn(async move {
show_toast(
format!("Screenpad brightness set to {}", val).into(),
"Failed to set Screenpad brightness".into(),
hl,
bl.set_screenpad_brightness(val).await,
);
});
});
// Gamma Callback
let hl = handle_copy.clone();
let bl = backlight_copy.clone();
global.on_cb_gamma(move |val| {
let bl = bl.clone();
let hl = hl.clone();
tokio::spawn(async move {
show_toast(
format!("Screenpad gamma set to {:.2}", val).into(),
"Failed to set Screenpad gamma".into(),
hl,
bl.set_screenpad_gamma(&val.to_string()).await,
);
});
});
// Sync Callback
let hl = handle_copy.clone();
let bl = backlight_copy.clone();
global.on_cb_sync_with_primary(move |val| {
let bl = bl.clone();
let hl = hl.clone();
tokio::spawn(async move {
show_toast(
format!(
"Screenpad sync {}",
if val { "enabled" } else { "disabled" }
)
.into(),
"Failed to toggle Screenpad sync".into(),
hl,
bl.set_screenpad_sync_with_primary(val).await,
);
});
});
// Power Callback (Toggle brightness to 0/last or 100)
let hl = handle_copy.clone();
let bl = backlight_copy.clone();
global.on_cb_power(move |val| {
let bl = bl.clone();
let hl = hl.clone();
tokio::spawn(async move {
let target = if val { 100 } else { 0 };
let _ = bl.set_screenpad_brightness(target).await;
hl.upgrade_in_event_loop(move |h| {
h.global::<ScreenpadPageData>().set_brightness(target);
})
.ok();
});
});
})
.ok();
// Optional: Value watches for external changes
// (Similar to setup_system.rs if needed)
});
}

View File

@@ -0,0 +1,132 @@
use crate::config::Config;
use crate::set_ui_callbacks;
use crate::ui::show_toast;
use crate::{MainWindow, SlashPageData};
use rog_dbus::{find_iface_async, zbus_slash::SlashProxy};
use rog_slash::SlashMode;
use slint::{ComponentHandle, Model};
use std::str::FromStr;
use std::sync::{Arc, Mutex};
pub fn setup_slash(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
let ui_weak = ui.as_weak();
tokio::spawn(async move {
// Find the Slash interface proxy
let proxies = match find_iface_async::<SlashProxy>("xyz.ljones.Slash").await {
Ok(p) => p,
Err(e) => {
log::warn!("Failed to find Slash interface: {}", e);
return;
}
};
let proxy = match proxies.first() {
Some(p) => p.clone(),
None => return,
};
// UI Callbacks (MUST be done on UI thread to access global state)
{
let proxy_copy = proxy.clone();
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
let proxy = proxy_copy.clone();
let ui_handle = ui;
set_ui_callbacks!(ui_handle, SlashPageData(), proxy.enabled(), "Slash enabled {}", "Failed to enable slash");
// Fix: Cast f32 (UI) to u8 (DBus)
set_ui_callbacks!(ui_handle, SlashPageData(as f32), proxy.brightness(as u8), "Slash brightness set to {}", "Failed to set slash brightness");
set_ui_callbacks!(ui_handle, SlashPageData(as f32), proxy.interval(as u8), "Slash interval set to {}", "Failed to set slash interval");
set_ui_callbacks!(ui_handle, SlashPageData(), proxy.show_battery_warning(), "Battery warning set to {}", "Failed to set battery warning");
set_ui_callbacks!(ui_handle, SlashPageData(), proxy.show_on_battery(), "Show on battery set to {}", "Failed to set show on battery");
set_ui_callbacks!(ui_handle, SlashPageData(), proxy.show_on_boot(), "Show on boot set to {}", "Failed to set show on boot");
set_ui_callbacks!(ui_handle, SlashPageData(), proxy.show_on_shutdown(), "Show on shutdown set to {}", "Failed to set show on shutdown");
set_ui_callbacks!(ui_handle, SlashPageData(), proxy.show_on_sleep(), "Show on sleep set to {}", "Failed to set show on sleep");
set_ui_callbacks!(ui_handle, SlashPageData(), proxy.show_on_lid_closed(), "Show on lid closed set to {}", "Failed to set show on lid closed");
});
}
// Custom Mode Logic - Callback setup
{
let proxy_copy = proxy.clone();
let ui_weak_copy = ui_weak.clone();
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
let data = ui.global::<SlashPageData>();
data.on_cb_mode_index(move |idx| {
let proxy_copy = proxy_copy.clone();
let handle_weak = ui_weak_copy.clone();
let mode_str_opt = if let Some(h) = handle_weak.upgrade() {
let d = h.global::<SlashPageData>();
if idx >= 0 && (idx as usize) < d.get_modes().row_count() {
Some(d.get_modes().row_data(idx as usize).unwrap_or_default())
} else {
None
}
} else {
None
};
if let Some(mode_str) = mode_str_opt {
if let Ok(mode) = SlashMode::from_str(&mode_str) {
tokio::spawn(async move {
show_toast(
format!("Slash mode set to {}", mode).into(),
"Failed to set slash mode".into(),
handle_weak,
proxy_copy.set_mode(mode).await,
);
});
}
}
});
});
}
// D-Bus Signal -> UI
let proxy_copy = proxy.clone();
let handle_copy = ui_weak.clone();
tokio::spawn(async move {
let mut changes = proxy_copy.receive_mode_changed().await;
use futures_util::StreamExt;
while let Some(change) = changes.next().await {
if let Ok(mode) = change.get().await {
let mode_str = mode.to_string();
let handle_copy = handle_copy.clone();
let _ = slint::invoke_from_event_loop(move || {
if let Some(h) = handle_copy.upgrade() {
let d = h.global::<SlashPageData>();
let model = d.get_modes();
for (i, m) in model.iter().enumerate() {
if m == mode_str {
d.set_mode_index(i as i32);
break;
}
}
}
});
}
}
});
if let Ok(m) = proxy.mode().await {
let mode_str = m.to_string();
let _ = slint::invoke_from_event_loop(move || {
if let Some(h) = ui_weak.upgrade() {
let d = h.global::<SlashPageData>();
let model = d.get_modes();
for (i, m) in model.iter().enumerate() {
if m == mode_str {
d.set_mode_index(i as i32);
break;
}
}
}
});
}
});
}

View File

@@ -0,0 +1,150 @@
use crate::config::Config;
use crate::tray::TrayStats;
use crate::{MainWindow, SystemStatus};
use rog_dbus::zbus_platform::PlatformProxy;
use slint::ComponentHandle;
use std::collections::VecDeque;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use tokio::fs;
use tokio::time::Duration;
use zbus::Connection;
pub fn setup_status(
ui: &MainWindow,
_config: Arc<Mutex<Config>>,
stats_tx: tokio::sync::watch::Sender<TrayStats>,
) {
let ui_weak = ui.as_weak();
tokio::spawn(async move {
let mut power_history: VecDeque<i32> = VecDeque::with_capacity(150); // 300s window at 2s poll
// DBus connection for profile
let conn = Connection::system().await.ok();
let platform = if let Some(c) = &conn {
PlatformProxy::new(c).await.ok()
} else {
None
};
loop {
let (cpu_temp, gpu_temp, cpu_fan, gpu_fan) = read_hwmon().await;
let power_microwatts = read_power().await;
// Rolling average logic
if power_history.len() >= 150 {
power_history.pop_front();
}
power_history.push_back(power_microwatts);
let sum: i64 = power_history.iter().map(|&x| x as i64).sum();
let avg_microwatts = if !power_history.is_empty() {
sum / power_history.len() as i64
} else {
0
};
// Convert to Watts
let power_w = power_microwatts as f64 / 1_000_000.0;
let avg_w = avg_microwatts as f64 / 1_000_000.0;
// Fetch profile
let mut profile_str = "Unknown".to_string();
if let Some(p) = &platform {
if let Ok(prof) = p.platform_profile().await {
profile_str = format!("{:?}", prof);
}
}
let ui_weak_loop = ui_weak.clone(); // Clone ui_weak for this iteration
// Send to Tray
let _ = stats_tx.send(TrayStats {
cpu_temp: format!("{}", cpu_temp),
gpu_temp: format!("{}", gpu_temp),
cpu_fan: format!("{}", cpu_fan),
gpu_fan: format!("{}", gpu_fan),
power_w: format!("{:.1}", power_w),
power_profile: profile_str,
});
let _ = slint::invoke_from_event_loop(move || {
if let Some(ui) = ui_weak_loop.upgrade() {
let global = ui.global::<SystemStatus>();
global.set_cpu_temp(cpu_temp);
global.set_gpu_temp(gpu_temp);
global.set_cpu_fan(cpu_fan);
global.set_gpu_fan(gpu_fan);
global.set_power_w(slint::SharedString::from(format!("{:.1}", power_w)));
global.set_power_avg_w(slint::SharedString::from(format!("{:.1}", avg_w)));
}
});
tokio::time::sleep(Duration::from_secs(2)).await;
}
});
}
async fn read_hwmon() -> (i32, i32, i32, i32) {
let mut cpu_temp = 0;
let mut gpu_temp = 0;
let mut cpu_fan = 0;
let mut gpu_fan = 0;
let mut entries = match fs::read_dir("/sys/class/hwmon").await {
Ok(e) => e,
Err(_) => return (0, 0, 0, 0),
};
while let Ok(Some(entry)) = entries.next_entry().await {
let path = entry.path();
let name_path = path.join("name");
if let Ok(name_str) = fs::read_to_string(&name_path).await {
let name = name_str.trim();
if name == "k10temp" || name == "coretemp" || name == "zenpower" {
// Try temp1_input (TCtl/Package)
if let Ok(temp) = read_val(&path.join("temp1_input")).await {
cpu_temp = temp / 1000;
}
} else if name == "amdgpu" || name == "nvidia" {
if let Ok(temp) = read_val(&path.join("temp1_input")).await {
gpu_temp = temp / 1000;
}
} else if name == "asus" || name == "asus_custom_fan_curve" {
if let Ok(fan) = read_val(&path.join("fan1_input")).await {
cpu_fan = fan;
}
if let Ok(fan) = read_val(&path.join("fan2_input")).await {
gpu_fan = fan;
}
}
}
}
(cpu_temp, gpu_temp, cpu_fan, gpu_fan)
}
async fn read_val(path: &PathBuf) -> Result<i32, ()> {
let s = fs::read_to_string(path).await.map_err(|_| ())?;
s.trim().parse::<i32>().map_err(|_| ())
}
async fn read_power() -> i32 {
let mut p = 0;
// Try BAT0 then BAT1
if let Ok(v) = read_val(&PathBuf::from("/sys/class/power_supply/BAT0/power_now")).await {
p = v.abs();
} else if let Ok(v) = read_val(&PathBuf::from("/sys/class/power_supply/BAT1/power_now")).await {
p = v.abs();
}
// Check status
if let Ok(s) = fs::read_to_string("/sys/class/power_supply/BAT0/status").await {
if s.trim() == "Discharging" {
return -p;
}
}
p
}

View File

@@ -0,0 +1,102 @@
use crate::config::Config;
use crate::ui::show_toast;
use crate::{MainWindow, SupergfxPageData};
use slint::{ComponentHandle, Model, SharedString, VecModel};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use zbus::proxy;
#[proxy(
interface = "org.supergfxctl.Daemon",
default_service = "org.supergfxctl.Daemon",
default_path = "/org/supergfxctl/Gfx"
)]
trait Supergfx {
fn supported(&self) -> zbus::Result<Vec<String>>;
fn mode(&self) -> zbus::Result<String>;
fn set_mode(&self, mode: &str) -> zbus::Result<()>;
fn vendor(&self) -> zbus::Result<String>;
}
pub fn setup_supergfx(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
let ui_weak = ui.as_weak();
tokio::spawn(async move {
let conn = match zbus::Connection::system().await {
Ok(c) => c,
Err(e) => {
log::warn!("Failed to connect to system bus: {}", e);
return;
}
};
let proxy = match SupergfxProxy::new(&conn).await {
Ok(p) => p,
Err(e) => {
log::warn!("Failed to create Supergfx proxy: {}", e);
return;
}
};
// Register Callbacks on UI Thread
{
let proxy_copy = proxy.clone();
let ui_weak_copy = ui_weak.clone();
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
let handle_copy = ui_weak_copy.clone();
ui.global::<SupergfxPageData>()
.on_set_mode(move |mode_str| {
let proxy = proxy_copy.clone();
let handle = handle_copy.clone();
tokio::spawn(async move {
show_toast(
format!("Switching to {}. Logout required.", mode_str).into(),
"Failed to set mode".into(),
handle,
proxy.set_mode(&mode_str).await,
);
});
});
});
}
// Fetch Initial State
// Vendor
if let Ok(vendor) = proxy.vendor().await {
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
ui.global::<SupergfxPageData>().set_vendor(vendor.into())
});
}
// Supported Modes
if let Ok(supported) = proxy.supported().await {
let modes: Vec<SharedString> = supported
.iter()
.map(|s| SharedString::from(s.as_str()))
.collect();
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
let mode_model = Rc::new(VecModel::from(modes));
ui.global::<SupergfxPageData>()
.set_supported_modes(mode_model.into())
});
}
// Current Mode
if let Ok(mode) = proxy.mode().await {
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
let g = ui.global::<SupergfxPageData>();
g.set_current_mode(mode.clone().into());
// Update selection index
let model = g.get_supported_modes();
for (i, m) in model.iter().enumerate() {
if m == mode.as_str() {
g.set_selected_index(i as i32);
break;
}
}
});
}
// No signal monitoring implemented as supergfxctl state changes usually require user action/logout
});
}

View File

@@ -47,7 +47,6 @@ pub fn setup_system_page(ui: &MainWindow, _config: Arc<Mutex<Config>>) {
ui.global::<SystemPageData>().set_screen_auto_brightness(-1); ui.global::<SystemPageData>().set_screen_auto_brightness(-1);
ui.global::<SystemPageData>().set_mcu_powersave(-1); ui.global::<SystemPageData>().set_mcu_powersave(-1);
ui.global::<SystemPageData>().set_mini_led_mode(-1); ui.global::<SystemPageData>().set_mini_led_mode(-1);
ui.global::<SystemPageData>().set_screenpad_brightness(-1);
ui.global::<SystemPageData>().set_ppt_pl1_spl(MINMAX); ui.global::<SystemPageData>().set_ppt_pl1_spl(MINMAX);
ui.global::<SystemPageData>().set_ppt_pl2_sppt(MINMAX); ui.global::<SystemPageData>().set_ppt_pl2_sppt(MINMAX);
ui.global::<SystemPageData>().set_ppt_pl3_fppt(MINMAX); ui.global::<SystemPageData>().set_ppt_pl3_fppt(MINMAX);
@@ -296,7 +295,7 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
log::error!("Failed to create platform proxy: {}", e); log::error!("Failed to create platform proxy: {}", e);
}) })
.unwrap(); .unwrap();
let backlight = BacklightProxy::builder(&conn) let _backlight = BacklightProxy::builder(&conn)
.build() .build()
.await .await
.map_err(|e| { .map_err(|e| {
@@ -397,23 +396,7 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
set_ui_props_async!(handle, platform, SystemPageData, enable_ppt_group); set_ui_props_async!(handle, platform, SystemPageData, enable_ppt_group);
set_ui_props_async!(handle, backlight, SystemPageData, screenpad_brightness); set_ui_props_async!(handle, platform, SystemPageData, enable_ppt_group);
if let Ok(value) = backlight.screenpad_gamma().await {
handle
.upgrade_in_event_loop(move |handle| {
handle
.global::<SystemPageData>()
.set_screenpad_gamma(value.parse().unwrap_or(1.0));
})
.ok();
}
set_ui_props_async!(
handle,
backlight,
SystemPageData,
screenpad_sync_with_primary
);
let platform_copy = platform.clone(); let platform_copy = platform.clone();
handle handle
@@ -536,25 +519,11 @@ pub fn setup_system_page_callbacks(ui: &MainWindow, _states: Arc<Mutex<Config>>)
"Setting Throttle policy on AC failed" "Setting Throttle policy on AC failed"
); );
set_ui_callbacks!(handle,
SystemPageData(as i32),
backlight.screenpad_brightness(as i32),
"Screenpad successfully set to {}",
"Setting screenpad brightness failed"
);
set_ui_callbacks!(handle, set_ui_callbacks!(handle,
SystemPageData(as bool), SystemPageData(as bool),
backlight.screenpad_sync_with_primary(as bool), platform_copy.change_platform_profile_on_battery(.into()),
"Screenpad successfully set to {}", "Throttle policy on battery enabled: {}",
"Setting screenpad brightness failed" "Setting Throttle policy on AC failed"
);
set_ui_callbacks!(handle,
SystemPageData(.parse().unwrap_or(1.0)),
backlight.screenpad_gamma(.to_string().as_str()),
"Screenpad successfully set to {}",
"Setting screenpad brightness failed"
); );
}) })
.ok(); .ok();

View File

@@ -1,810 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: rog-control-center\n"
"POT-Creation-Date: 2026-01-16 22:25+0000\n"
"PO-Revision-Date: 2024-07-28 12:00+0300\n"
"Last-Translator: Mykola Shevchenko\n"
"Language-Team: Ukrainian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: uk_UA\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: rog-control-center/ui/main_window.slint:55
msgctxt "MainWindow"
msgid "ROG"
msgstr "ROG"
#: rog-control-center/ui/main_window.slint:57
msgctxt "Menu1"
msgid "System Control"
msgstr "Системні"
#: rog-control-center/ui/main_window.slint:58
msgctxt "Menu2"
msgid "Keyboard Aura"
msgstr "Aura клавіатури"
#: rog-control-center/ui/main_window.slint:59
msgctxt "Menu3"
msgid "AniMe Matrix"
msgstr "AniMe матриця"
#: rog-control-center/ui/main_window.slint:60
msgctxt "Menu4"
msgid "Fan Curves"
msgstr "Криві вентиляторів"
#: rog-control-center/ui/main_window.slint:61
msgctxt "Menu5"
msgid "App Settings"
msgstr "Налаштування"
#: rog-control-center/ui/main_window.slint:62
msgctxt "Menu6"
msgid "About"
msgstr "Про додаток"
#: rog-control-center/ui/main_window.slint:74
msgctxt "MainWindow"
msgid "Quit App"
msgstr "Вийти"
#: rog-control-center/ui/pages/anime.slint:6
msgctxt "Anime Brightness"
msgid "Off"
msgstr "Вимкнено"
#: rog-control-center/ui/pages/anime.slint:7
msgctxt "Anime Brightness"
msgid "Low"
msgstr "Низька"
#: rog-control-center/ui/pages/anime.slint:8
msgctxt "Anime Brightness"
msgid "Med"
msgstr "Середня"
#: rog-control-center/ui/pages/anime.slint:9
msgctxt "Anime Brightness"
msgid "High"
msgstr "Висока"
#: rog-control-center/ui/pages/anime.slint:23
msgctxt "AnimePageData"
msgid "Glitch Construction"
msgstr "Збій конструкції"
#: rog-control-center/ui/pages/anime.slint:23
msgctxt "AnimePageData"
msgid "Static Emergence"
msgstr "Статична поява"
#: rog-control-center/ui/pages/anime.slint:25
msgctxt "AnimePageData"
msgid "Binary Banner Scroll"
msgstr "Прокрутка бінарного банера"
#: rog-control-center/ui/pages/anime.slint:25
msgctxt "AnimePageData"
msgid "Rog Logo Glitch"
msgstr "Збій логотипу Rog"
#: rog-control-center/ui/pages/anime.slint:27
msgctxt "AnimePageData"
msgid "Banner Swipe"
msgstr "Прогортання банера"
#: rog-control-center/ui/pages/anime.slint:27
msgctxt "AnimePageData"
msgid "Starfield"
msgstr "Зоряне поле"
#: rog-control-center/ui/pages/anime.slint:29
msgctxt "AnimePageData"
msgid "Glitch Out"
msgstr "Збій"
#: rog-control-center/ui/pages/anime.slint:29
msgctxt "AnimePageData"
msgid "See Ya"
msgstr "Бувай"
#: rog-control-center/ui/pages/anime.slint:50
msgctxt "Anime Brightness"
msgid "Brightness"
msgstr "Яскравість"
#: rog-control-center/ui/pages/anime.slint:66
msgctxt "PageAnime"
msgid "Enable display"
msgstr "Увімкнути показ"
#: rog-control-center/ui/pages/anime.slint:74 rog-control-center/ui/pages/anime.slint:97
msgctxt "PageAnime"
msgid "Advanced"
msgstr "Розширені"
#: rog-control-center/ui/pages/anime.slint:89
msgctxt "PageAnime"
msgid "Use built-in animations"
msgstr "Використовувати вбудовані анімації"
#: rog-control-center/ui/pages/anime.slint:146
msgctxt "PageAnime"
msgid "Set which builtin animations are played"
msgstr "Встановити, які вбудовані анімації відтворювати"
#: rog-control-center/ui/pages/anime.slint:150
msgctxt "Anime built-in selection"
msgid "Boot Animation"
msgstr "Анімація завантаження"
#: rog-control-center/ui/pages/anime.slint:160
msgctxt "Anime built-in selection"
msgid "Running Animation"
msgstr "Анімація роботи"
#: rog-control-center/ui/pages/anime.slint:170
msgctxt "Anime built-in selection"
msgid "Sleep Animation"
msgstr "Анімація сну"
#: rog-control-center/ui/pages/anime.slint:180
msgctxt "Anime built-in selection"
msgid "Shutdown Animation"
msgstr "Анімація вимкнення"
#: rog-control-center/ui/pages/anime.slint:220
msgctxt "PageAnime"
msgid "Advanced Display Settings"
msgstr "Розширені налаштування показу"
#: rog-control-center/ui/pages/anime.slint:225
msgctxt "PageAnime"
msgid "Off when lid closed"
msgstr "Вимикати при закритій кришці"
#: rog-control-center/ui/pages/anime.slint:234
msgctxt "PageAnime"
msgid "Off when suspended"
msgstr "Вимикати в режимі сну"
#: rog-control-center/ui/pages/anime.slint:243
msgctxt "PageAnime"
msgid "Off when on battery"
msgstr "Вимикати при роботі від батареї"
#: rog-control-center/ui/pages/system.slint:20
msgctxt "SystemPageData"
msgid "Balanced"
msgstr "Збалансований"
#: rog-control-center/ui/pages/system.slint:20 rog-control-center/ui/pages/system.slint:27
msgctxt "SystemPageData"
msgid "Performance"
msgstr "Продуктивний"
#: rog-control-center/ui/pages/system.slint:20
msgctxt "SystemPageData"
msgid "Quiet"
msgstr "Тихий"
#: rog-control-center/ui/pages/system.slint:20
msgctxt "SystemPageData"
msgid "LowPower"
msgstr "Низьке споживання"
#: rog-control-center/ui/pages/system.slint:26
msgctxt "SystemPageData"
msgid "Default"
msgstr "За замовчуванням"
#: rog-control-center/ui/pages/system.slint:28
msgctxt "SystemPageData"
msgid "BalancePerformance"
msgstr "Баланс-Продуктивність"
#: rog-control-center/ui/pages/system.slint:29
msgctxt "SystemPageData"
msgid "BalancePower"
msgstr "Баланс-Енергозбереження"
#: rog-control-center/ui/pages/system.slint:30
msgctxt "SystemPageData"
msgid "Power"
msgstr "Енергозбереження"
#: rog-control-center/ui/pages/system.slint:159
msgctxt "PageSystem"
msgid "Power settings"
msgstr "Налаштування живлення"
#: rog-control-center/ui/pages/system.slint:164
msgctxt "PageSystem"
msgid "Charge limit"
msgstr "Ліміт заряду"
#: rog-control-center/ui/pages/system.slint:179
msgctxt "PageSystem"
msgid "Platform Profile"
msgstr "Профіль платформи"
#: rog-control-center/ui/pages/system.slint:189
msgctxt "PageSystem"
msgid "Advanced"
msgstr "Розширені"
#: rog-control-center/ui/pages/system.slint:209
msgctxt "PageSystem"
msgid "Screenpad brightness"
msgstr "Яскравість екранної панелі"
#: rog-control-center/ui/pages/system.slint:233
msgctxt "PageSystem"
msgid "Sync with primary"
msgstr "Синхронізувати з основним"
#: rog-control-center/ui/pages/system.slint:253
msgctxt "PageSystem"
msgid "Armoury settings"
msgstr "Налаштування Armoury"
#: rog-control-center/ui/pages/system.slint:253
msgctxt "PageSystem"
msgid "Keyboard Power Management"
msgstr "Керування живленням клавіатури"
#: rog-control-center/ui/pages/system.slint:263
msgctxt "no_asus_armoury_driver_1"
msgid "The asus-armoury driver is not loaded"
msgstr "Драйвер asus-armoury не завантажено"
#: rog-control-center/ui/pages/system.slint:269
msgctxt "no_asus_armoury_driver_2"
msgid "For advanced features you will require a kernel with this driver added."
msgstr "Для розширених функцій вам знадобиться ядро з доданим цим драйвером."
#: rog-control-center/ui/pages/system.slint:280
msgctxt "PageSystem"
msgid "Panel Overdrive"
msgstr "Розгін матриці"
#: rog-control-center/ui/pages/system.slint:288
msgctxt "PageSystem"
msgid "MiniLED Mode"
msgstr "Режим MiniLED"
#: rog-control-center/ui/pages/system.slint:296
msgctxt "PageSystem"
msgid "POST boot sound"
msgstr "Звук при завантаженні"
#: rog-control-center/ui/pages/system.slint:312
msgctxt "ppt_warning"
msgid "The following settings are not applied until the toggle is enabled."
msgstr "Наступні налаштування не застосовуються, доки перемикач не буде увімкнено."
#: rog-control-center/ui/pages/system.slint:317 rog-control-center/ui/pages/system.slint:324
msgctxt "ppt_group_enabled"
msgid "Enable Tuning"
msgstr "Увімкнути налаштування"
#: rog-control-center/ui/pages/system.slint:334 rog-control-center/ui/pages/system.slint:335
msgctxt "ppt_pl1_spl"
msgid "CPU Sustained Power Limit"
msgstr "Тривалий ліміт потужності CPU"
#: rog-control-center/ui/pages/system.slint:336
msgctxt "ppt_pl1_spl_help"
msgid ""
"Long-term CPU power limit that affects sustained workload performance. "
"Higher values may increase heat and power consumption."
msgstr ""
"Довготривалий ліміт потужності CPU, що впливає на продуктивність при тривалих навантаженнях."
"Вищі значення можуть збільшити нагрівання та споживання енергії."
#: rog-control-center/ui/pages/system.slint:352 rog-control-center/ui/pages/system.slint:353
msgctxt "ppt_pl2_sppt"
msgid "CPU Turbo Power Limit"
msgstr "Турбо ліміт потужності CPU"
#: rog-control-center/ui/pages/system.slint:354
msgctxt "ppt_pl2_sppt_help"
msgid ""
"Short-term CPU power limit for boost periods. Controls maximum power during "
"brief high-performance bursts."
msgstr ""
"Короткочасний ліміт потужності CPU для періодів прискорення. Контролює максимальну"
"потужність під час коротких сплесків високої продуктивності."
#: rog-control-center/ui/pages/system.slint:370 rog-control-center/ui/pages/system.slint:371
msgctxt "ppt_pl3_fppt"
msgid "CPU Fast Burst Power Limit"
msgstr "Швидкий ліміт потужності CPU"
#: rog-control-center/ui/pages/system.slint:372
msgctxt "ppt_pl3_fppt_help"
msgid ""
"Ultra-short duration power limit for instantaneous CPU bursts. Affects "
"responsiveness during sudden workload spikes."
msgstr ""
"Надкороткий ліміт потужності для миттєвих сплесків CPU."
"Впливає на чутливість під час раптових піків навантаження."
#: rog-control-center/ui/pages/system.slint:387 rog-control-center/ui/pages/system.slint:388
msgctxt "ppt_fppt"
msgid "Fast Package Power Limit"
msgstr "Швидкий ліміт потужності пакета"
#: rog-control-center/ui/pages/system.slint:389
msgctxt "ppt_fppt_help"
msgid ""
"Ultra-short duration power limit for system package. Controls maximum power "
"during millisecond-scale load spikes."
msgstr ""
"Надкороткий ліміт потужності для системного пакета. Контролює максимальну"
"потужність під час пікових навантажень мілісекундного масштабу."
#: rog-control-center/ui/pages/system.slint:405 rog-control-center/ui/pages/system.slint:406
msgctxt "ppt_apu_sppt"
msgid "APU Sustained Power Limit"
msgstr "Тривалий ліміт потужності APU"
#: rog-control-center/ui/pages/system.slint:407
msgctxt "ppt_apu_sppt_help"
msgid ""
"Long-term power limit for integrated graphics and CPU combined. Affects "
"sustained performance of APU-based workloads."
msgstr ""
"Довготривалий ліміт потужності для інтегрованої графіки та CPU разом."
"Впливає на тривалу продуктивність навантажень на базі APU."
#: rog-control-center/ui/pages/system.slint:423 rog-control-center/ui/pages/system.slint:424
msgctxt "ppt_platform_sppt"
msgid "Platform Sustained Power Limit"
msgstr "Тривалий ліміт потужності платформи"
#: rog-control-center/ui/pages/system.slint:425
msgctxt "ppt_platform_sppt_help"
msgid ""
"Overall system power limit for sustained operations. Controls total platform "
"power consumption over extended periods."
msgstr ""
"Загальний ліміт потужності системи для тривалих операцій. Контролює загальне"
"споживання енергії платформою протягом тривалих періодів."
#: rog-control-center/ui/pages/system.slint:441 rog-control-center/ui/pages/system.slint:442
msgctxt "nv_dynamic_boost"
msgid "GPU Power Boost"
msgstr "Прискорення потужності GPU"
#: rog-control-center/ui/pages/system.slint:443
msgctxt "nv_dynamic_boost_help"
msgid ""
"Additional power allocation for GPU dynamic boost. Higher values increase "
"GPU performance but generate more heat."
msgstr ""
"Додаткове виділення потужності для динамічного прискорення GPU. Вищі значення"
"збільшують продуктивність GPU, але генерують більше тепла."
#: rog-control-center/ui/pages/system.slint:459 rog-control-center/ui/pages/system.slint:460
msgctxt "nv_temp_target"
msgid "GPU Temperature Limit"
msgstr "Ліміт температури GPU"
#: rog-control-center/ui/pages/system.slint:461
msgctxt "nv_temp_target_help"
msgid ""
"Maximum GPU temperature threshold in Celsius. GPU will throttle to maintain "
"temperature below this limit."
msgstr ""
"Максимальний поріг температури GPU у градусах Цельсія. GPU буде знижувати"
"частоту для підтримки температури нижче цього ліміту."
#: rog-control-center/ui/pages/system.slint:513
msgctxt "PageSystem"
msgid "Energy Performance Preference linked to Throttle Policy"
msgstr "Перевага енергоефективності пов'язана з Політикою Тротлінгу"
#: rog-control-center/ui/pages/system.slint:517
msgctxt "PageSystem"
msgid "Change EPP based on Throttle Policy"
msgstr "Змінювати EPP на основі Політики Тротлінгу"
#: rog-control-center/ui/pages/system.slint:525
msgctxt "PageSystem"
msgid "EPP for Balanced Policy"
msgstr "EPP для Збалансованої Політики"
#: rog-control-center/ui/pages/system.slint:535
msgctxt "PageSystem"
msgid "EPP for Performance Policy"
msgstr "EPP для Політики Продуктивності"
#: rog-control-center/ui/pages/system.slint:545
msgctxt "PageSystem"
msgid "EPP for Quiet Policy"
msgstr "EPP для Тихої Політики"
#: rog-control-center/ui/pages/system.slint:563
msgctxt "PageSystem"
msgid "Throttle Policy for power state"
msgstr "Політика Тротлінгу для стану живлення"
#: rog-control-center/ui/pages/system.slint:569
msgctxt "PageSystem"
msgid "Throttle Policy on Battery"
msgstr "Політика Тротлінгу при живленні від батареї"
#: rog-control-center/ui/pages/system.slint:579 rog-control-center/ui/pages/system.slint:600
msgctxt "PageSystem"
msgid "Enabled"
msgstr "Увімкнено"
#: rog-control-center/ui/pages/system.slint:590
msgctxt "PageSystem"
msgid "Throttle Policy on AC"
msgstr "Політика Тротлінгу при живленні від мережі"
#: rog-control-center/ui/pages/aura.slint:28
msgctxt "PageAura"
msgid "Brightness"
msgstr "Яскравість"
#: rog-control-center/ui/pages/aura.slint:39
msgctxt "PageAura"
msgid "Aura mode"
msgstr "Режим Aura"
#: rog-control-center/ui/pages/aura.slint:59
msgctxt "PageAura"
msgid "Colour 1"
msgstr "Колір 1"
#: rog-control-center/ui/pages/aura.slint:85
msgctxt "PageAura"
msgid "Colour 2"
msgstr "Колір 2"
#: rog-control-center/ui/pages/aura.slint:119
msgctxt "PageAura"
msgid "Zone"
msgstr "Зона"
#: rog-control-center/ui/pages/aura.slint:142
msgctxt "PageAura"
msgid "Direction"
msgstr "Напрямок"
#: rog-control-center/ui/pages/aura.slint:164
msgctxt "PageAura"
msgid "Speed"
msgstr "Швидкість"
#: rog-control-center/ui/pages/aura.slint:185
msgctxt "PageAura"
msgid "Power Settings"
msgstr "Налаштування живлення"
#: rog-control-center/ui/pages/aura.slint:270
msgctxt "PageAura"
msgid "Power Zones"
msgstr "Зони живлення"
#: rog-control-center/ui/pages/app_settings.slint:26
msgctxt "PageAppSettings"
msgid "Run in background after closing"
msgstr "Працювати у фоні після закриття"
#: rog-control-center/ui/pages/app_settings.slint:34
msgctxt "PageAppSettings"
msgid "Start app in background (UI closed)"
msgstr "Запускати у фоні (без інтерфейсу)"
#: rog-control-center/ui/pages/app_settings.slint:42
msgctxt "PageAppSettings"
msgid "Enable system tray icon"
msgstr "Увімкнути іконку в треї"
#: rog-control-center/ui/pages/app_settings.slint:50
msgctxt "PageAppSettings"
msgid "Enable dGPU notifications"
msgstr "Увімкнути сповіщення про dGPU"
#: rog-control-center/ui/pages/fans.slint:26
msgctxt "FanTab"
msgid "This fan is not avilable on this machine"
msgstr "Цей вентилятор недоступний на цьому пристрої"
#: rog-control-center/ui/pages/fans.slint:34
msgctxt "FanTab"
msgid "Enabled"
msgstr "Увімкнено"
#: rog-control-center/ui/pages/fans.slint:43
msgctxt "FanTab"
msgid "Apply"
msgstr "Застосувати"
#: rog-control-center/ui/pages/fans.slint:51
msgctxt "FanTab"
msgid "Cancel"
msgstr "Скасувати"
#: rog-control-center/ui/pages/fans.slint:59
msgctxt "FanTab"
msgid "Factory Default (all fans)"
msgstr "Заводські налаштування (всі вентилятори)"
#: rog-control-center/ui/pages/fans.slint:72
msgctxt "PageFans"
msgid "Balanced"
msgstr "Збалансований"
#: rog-control-center/ui/pages/fans.slint:75 rog-control-center/ui/pages/fans.slint:134 rog-control-center/ui/pages/fans.slint:193
msgctxt "PageFans"
msgid "CPU"
msgstr "CPU"
#: rog-control-center/ui/pages/fans.slint:93 rog-control-center/ui/pages/fans.slint:152 rog-control-center/ui/pages/fans.slint:211
msgctxt "PageFans"
msgid "Mid"
msgstr "Середній"
#: rog-control-center/ui/pages/fans.slint:111 rog-control-center/ui/pages/fans.slint:170 rog-control-center/ui/pages/fans.slint:229
msgctxt "PageFans"
msgid "GPU"
msgstr "GPU"
#: rog-control-center/ui/pages/fans.slint:131
msgctxt "PageFans"
msgid "Performance"
msgstr "Продуктивний"
#: rog-control-center/ui/pages/fans.slint:190
msgctxt "PageFans"
msgid "Quiet"
msgstr "Тихий"
#: rog-control-center/ui/widgets/common.slint:126
msgctxt "confirm_reset"
msgid "Are you sure you want to reset this?"
msgstr "Ви впевнені, що хочете скинути це?"
#: rog-control-center/ui/widgets/aura_power.slint:33
msgctxt "AuraPowerGroup"
msgid "Boot"
msgstr "Запуск"
#: rog-control-center/ui/widgets/aura_power.slint:43
msgctxt "AuraPowerGroup"
msgid "Awake"
msgstr "Робота"
#: rog-control-center/ui/widgets/aura_power.slint:53
msgctxt "AuraPowerGroup"
msgid "Sleep"
msgstr "Сон"
#: rog-control-center/ui/widgets/aura_power.slint:63
msgctxt "AuraPowerGroup"
msgid "Shutdown"
msgstr "Вимкнення"
#: rog-control-center/ui/widgets/aura_power.slint:102
msgctxt "AuraPowerGroupOld"
msgid "Zone Selection"
msgstr "Вибір зони"
#: rog-control-center/ui/widgets/aura_power.slint:114
msgctxt "AuraPowerGroupOld"
msgid "Boot"
msgstr "Завантаження"
#: rog-control-center/ui/widgets/aura_power.slint:124
msgctxt "AuraPowerGroupOld"
msgid "Awake"
msgstr "Робота"
#: rog-control-center/ui/widgets/aura_power.slint:134
msgctxt "AuraPowerGroupOld"
msgid "Sleep"
msgstr "Сон"
#: rog-control-center/ui/types/aura_types.slint:52
msgctxt "Aura power zone"
msgid "Logo"
msgstr "Логотип"
#: rog-control-center/ui/types/aura_types.slint:53 rog-control-center/ui/types/aura_types.slint:63
msgctxt "Aura power zone"
msgid "Keyboard"
msgstr "Клавіатура"
#: rog-control-center/ui/types/aura_types.slint:54 rog-control-center/ui/types/aura_types.slint:64
msgctxt "Aura power zone"
msgid "Lightbar"
msgstr "Світлова панель"
#: rog-control-center/ui/types/aura_types.slint:55
msgctxt "Aura power zone"
msgid "Lid"
msgstr "Кришка"
#: rog-control-center/ui/types/aura_types.slint:56
msgctxt "Aura power zone"
msgid "Rear Glow"
msgstr "Заднє світіння"
#: rog-control-center/ui/types/aura_types.slint:57 rog-control-center/ui/types/aura_types.slint:65
msgctxt "Aura power zone"
msgid "Keyboard and Lightbar"
msgstr "Клавіатура та світлова панель"
#: rog-control-center/ui/types/aura_types.slint:58
msgctxt "Aura power zone"
msgid "Ally"
msgstr "Ally"
#: rog-control-center/ui/types/aura_types.slint:68
msgctxt "Aura brightness"
msgid "Off"
msgstr "Вимкнено"
#: rog-control-center/ui/types/aura_types.slint:69
msgctxt "Aura brightness"
msgid "Low"
msgstr "Низька"
#: rog-control-center/ui/types/aura_types.slint:70
msgctxt "Aura brightness"
msgid "Med"
msgstr "Середня"
#: rog-control-center/ui/types/aura_types.slint:71
msgctxt "Aura brightness"
msgid "High"
msgstr "Висока"
#: rog-control-center/ui/types/aura_types.slint:76 rog-control-center/ui/types/aura_types.slint:91
msgctxt "Basic aura mode"
msgid "Static"
msgstr "Статичний"
#: rog-control-center/ui/types/aura_types.slint:77 rog-control-center/ui/types/aura_types.slint:92
msgctxt "Basic aura mode"
msgid "Breathe"
msgstr "Дихання"
#: rog-control-center/ui/types/aura_types.slint:78 rog-control-center/ui/types/aura_types.slint:93
msgctxt "Basic aura mode"
msgid "Strobe"
msgstr "Стробоскоп"
#: rog-control-center/ui/types/aura_types.slint:79
msgctxt "Basic aura mode"
msgid "Rainbow"
msgstr "Веселка"
#: rog-control-center/ui/types/aura_types.slint:80
msgctxt "Basic aura mode"
msgid "Star"
msgstr "Зірка"
#: rog-control-center/ui/types/aura_types.slint:81
msgctxt "Basic aura mode"
msgid "Rain"
msgstr "Дощ"
#: rog-control-center/ui/types/aura_types.slint:82
msgctxt "Basic aura mode"
msgid "Highlight"
msgstr "Підсвічування"
#: rog-control-center/ui/types/aura_types.slint:83
msgctxt "Basic aura mode"
msgid "Laser"
msgstr "Лазер"
#: rog-control-center/ui/types/aura_types.slint:84
msgctxt "Basic aura mode"
msgid "Ripple"
msgstr "Хвиля"
#: rog-control-center/ui/types/aura_types.slint:85
msgctxt "Basic aura mode"
msgid "Nothing"
msgstr "Нічого"
#: rog-control-center/ui/types/aura_types.slint:86
msgctxt "Basic aura mode"
msgid "Pulse"
msgstr "Пульс"
#: rog-control-center/ui/types/aura_types.slint:87
msgctxt "Basic aura mode"
msgid "Comet"
msgstr "Комета"
#: rog-control-center/ui/types/aura_types.slint:88
msgctxt "Basic aura mode"
msgid "Flash"
msgstr "Спалах"
#: rog-control-center/ui/types/aura_types.slint:100
msgctxt "Aura zone"
msgid "None"
msgstr "Немає"
#: rog-control-center/ui/types/aura_types.slint:101
msgctxt "Aura zone"
msgid "Key1"
msgstr "Клавіша 1"
#: rog-control-center/ui/types/aura_types.slint:102
msgctxt "Aura zone"
msgid "Key2"
msgstr "Клавіша 2"
#: rog-control-center/ui/types/aura_types.slint:103
msgctxt "Aura zone"
msgid "Key3"
msgstr "Клавіша 3"
#: rog-control-center/ui/types/aura_types.slint:104
msgctxt "Aura zone"
msgid "Key4"
msgstr "Клавіша 4"
#: rog-control-center/ui/types/aura_types.slint:105
msgctxt "Aura zone"
msgid "Logo"
msgstr "Логотип"
#: rog-control-center/ui/types/aura_types.slint:106
msgctxt "Aura zone"
msgid "Lightbar Left"
msgstr "Світлова панель зліва"
#: rog-control-center/ui/types/aura_types.slint:107
msgctxt "Aura zone"
msgid "Lightbar Right"
msgstr "Світлова панель справа"
#: rog-control-center/ui/types/aura_types.slint:111
msgctxt "Aura direction"
msgid "Right"
msgstr "Вправо"
#: rog-control-center/ui/types/aura_types.slint:112
msgctxt "Aura direction"
msgid "Left"
msgstr "Вліво"
#: rog-control-center/ui/types/aura_types.slint:113
msgctxt "Aura direction"
msgid "Up"
msgstr "Вгору"
#: rog-control-center/ui/types/aura_types.slint:114
msgctxt "Aura direction"
msgid "Down"
msgstr "Вниз"
#: rog-control-center/ui/types/aura_types.slint:118
msgctxt "Aura speed"
msgid "Low"
msgstr "Низька"
#: rog-control-center/ui/types/aura_types.slint:119
msgctxt "Aura speed"
msgid "Medium"
msgstr "Середня"
#: rog-control-center/ui/types/aura_types.slint:120
msgctxt "Aura speed"
msgid "High"
msgstr "Висока"

View File

@@ -1,9 +1,12 @@
import { Palette, Button, VerticalBox } from "std-widgets.slint"; import { Button, VerticalBox } from "std-widgets.slint";
import { AppSize } from "globals.slint"; import { AppSize } from "globals.slint";
import { PageSystem, SystemPageData, AttrMinMax } from "pages/system.slint"; import { PageSystem, SystemPageData, AttrMinMax } from "pages/system.slint";
import { SideBar } from "widgets/sidebar.slint"; import { SideBar } from "widgets/sidebar.slint";
import { PageAbout } from "pages/about.slint"; import { PageAbout } from "pages/about.slint";
import { PageFans } from "pages/fans.slint"; import { PageFans } from "pages/fans.slint";
import { PageSlash, SlashPageData } from "pages/slash.slint";
import { PageSupergfx, SupergfxPageData } from "pages/supergfx.slint";
import { PageScreenpad, ScreenpadPageData } from "pages/screenpad.slint";
import { PageAnime, AnimePageData } from "pages/anime.slint"; import { PageAnime, AnimePageData } from "pages/anime.slint";
import { RogItem } from "widgets/common.slint"; import { RogItem } from "widgets/common.slint";
import { PageAura } from "pages/aura.slint"; import { PageAura } from "pages/aura.slint";
@@ -14,8 +17,18 @@ export { FanPageData, FanType, Profile }
import { AuraPageData, AuraDevType, LaptopAuraPower, AuraPowerState, PowerZones, AuraEffect } from "types/aura_types.slint"; import { AuraPageData, AuraDevType, LaptopAuraPower, AuraPowerState, PowerZones, AuraEffect } from "types/aura_types.slint";
export { AuraPageData, AuraDevType, LaptopAuraPower, AuraPowerState, PowerZones, AuraEffect } export { AuraPageData, AuraDevType, LaptopAuraPower, AuraPowerState, PowerZones, AuraEffect }
import { PageAppSettings, AppSettingsPageData } from "pages/app_settings.slint"; import { PageAppSettings, AppSettingsPageData } from "pages/app_settings.slint";
import { StatusBar, SystemStatus } from "widgets/status_bar.slint";
import { TrayTooltip } from "windows/tray_tooltip.slint";
export { TrayTooltip }
export { AppSize, AttrMinMax, SystemPageData, AnimePageData, AppSettingsPageData } import { RogPalette } from "themes/rog_theme.slint";
export { AppSize, AttrMinMax, SystemPageData, AnimePageData, AppSettingsPageData, SystemStatus, SlashPageData, SupergfxPageData, ScreenpadPageData }
export global SomeError {
in property <string> error_message: "";
in property <string> error_help: "";
}
export component MainWindow inherits Window { export component MainWindow inherits Window {
title: "ROG Control"; title: "ROG Control";
@@ -24,93 +37,133 @@ export component MainWindow inherits Window {
default-font-size: 14px; default-font-size: 14px;
default-font-weight: 400; default-font-weight: 400;
icon: @image-url("../data/rog-control-center.png"); icon: @image-url("../data/rog-control-center.png");
in property <[bool]> sidebar_items_avilable: [true, true, true, true, true, true]; in property <[bool]> sidebar_items_avilable: [true, true, true, true, true, true, true, true, true];
private property <bool> show_notif; private property <bool> show_notif;
private property <bool> fade_cover; private property <bool> fade_cover;
private property <bool> toast: false; private property <bool> toast: false;
private property <string> toast_text: "I show when something is waiting"; private property <string> toast_text: "I show when something is waiting";
callback show_toast(string); callback show_toast(string);
callback start_toast_timer();
callback hide_toast();
hide_toast() => {
toast = false;
}
show_toast(text) => { show_toast(text) => {
toast = text != ""; toast = text != "";
toast_text = text; toast_text = text;
if (toast) {
start_toast_timer();
}
} }
callback exit-app(); callback exit-app();
callback show_notification(bool); callback show_notification(bool);
show_notification(yes) => { show_notification(yes) => {
show_notif = yes; show_notif = yes;
fade_cover = yes; fade_cover = yes;
} }
callback external_colour_change(); callback external_colour_change();
external_colour_change() => { external_colour_change() => {
aura.external_colour_change(); aura.external_colour_change();
aura.external_colour_change(); aura.external_colour_change();
} }
min-height: AppSize.height; min-height: AppSize.height;
min-width: AppSize.width; min-width: AppSize.width;
background: Colors.black; background: RogPalette.background;
HorizontalLayout {
padding: 0px;
VerticalLayout {
side-bar := SideBar {
title: @tr("ROG");
model: [
@tr("Menu1" => "System Control"),
@tr("Menu2" => "Keyboard Aura"),
@tr("Menu3" => "AniMe Matrix"),
@tr("Menu4" => "Fan Curves"),
@tr("Menu5" => "App Settings"),
@tr("Menu6" => "About"),
];
available: root.sidebar_items_avilable;
}
Rectangle { VerticalLayout {
max-height: 40px; HorizontalLayout {
width: side-bar.width; padding: 0px;
background: Palette.control-background;
Text { // Left Column: Sidebar + Quit Button
vertical-alignment: center; VerticalLayout {
horizontal-alignment: center; side-bar := SideBar {
text: @tr("Quit App"); title: @tr("ROG");
model: [
@tr("Menu1" => "System Control"),
@tr("Menu2" => "Keyboard Aura"),
@tr("Menu3" => "AniMe Matrix"),
@tr("Menu7" => "Slash Lighting"),
@tr("Menu8" => "Graphics Control"),
@tr("Menu9" => "Screenpad Control"),
@tr("Menu4" => "Fan Curves"),
@tr("Menu5" => "App Settings"),
@tr("Menu6" => "About"),
];
available: root.sidebar_items_avilable;
} }
TouchArea { Rectangle {
clicked => { max-height: 40px;
root.exit-app(); width: side-bar.width;
background: RogPalette.control-background;
Text {
vertical-alignment: center;
horizontal-alignment: center;
text: @tr("Quit App");
color: RogPalette.text-primary;
}
TouchArea {
clicked => {
root.exit-app();
}
} }
} }
} }
}
Rectangle { // Right Column: Content Pages
background: Palette.background; Rectangle {
if(side-bar.current-item == 0): page := PageSystem { background: RogPalette.background;
width: root.width - side-bar.width; if(side-bar.current-item == 0): page := PageSystem {
height: root.height + 12px; width: root.width - side-bar.width;
} height: root.height + 12px;
}
aura := PageAura { aura := PageAura {
width: root.width - side-bar.width; width: root.width - side-bar.width;
visible: side-bar.current-item == 1; visible: side-bar.current-item == 1;
} }
if(side-bar.current-item == 2): PageAnime { if(side-bar.current-item == 2): PageAnime {
width: root.width - side-bar.width; width: root.width - side-bar.width;
} }
fans := PageFans { if(side-bar.current-item == 3): PageSlash {
width: root.width - side-bar.width; width: root.width - side-bar.width;
visible: side-bar.current-item == 3; }
}
if(side-bar.current-item == 4): PageAppSettings { if(side-bar.current-item == 4): PageSupergfx {
width: root.width - side-bar.width; width: root.width - side-bar.width;
} }
if(side-bar.current-item == 5): PageAbout { if(side-bar.current-item == 5): PageScreenpad {
width: root.width - side-bar.width; width: root.width - side-bar.width;
}
fans := PageFans {
width: root.width - side-bar.width;
visible: side-bar.current-item == 6;
}
if(side-bar.current-item == 7): PageAppSettings {
width: root.width - side-bar.width;
}
if(side-bar.current-item == 8): PageAbout {
width: root.width - side-bar.width;
}
} }
} }
// Bottom: Status Bar
StatusBar {}
} }
if fade_cover: Rectangle { if fade_cover: Rectangle {
@@ -118,7 +171,7 @@ export component MainWindow inherits Window {
y: 0px; y: 0px;
width: root.width; width: root.width;
height: root.height; height: root.height;
background: Colors.rgba(25,33,23,20); background: Colors.rgba(0,0,0,0.7);
opacity: 0.7; opacity: 0.7;
TouchArea { TouchArea {
height: 100%; height: 100%;
@@ -133,13 +186,24 @@ export component MainWindow inherits Window {
} }
} }
if toast: Rectangle { // Modern floating toast/snackbar notification
x: 0px; // Shows at the bottom center, non-intrusive
y: 0px; Rectangle {
width: root.width; visible: self.opacity > 0;
height: 32px; opacity: root.toast ? 1 : 0;
opacity: 1.0; animate opacity { duration: 300ms; }
background: Colors.grey;
x: (root.width - 400px) / 2; // Center horizontally
y: root.height - 80px; // Bottom padding
width: 400px;
height: 48px;
border-radius: RogPalette.border-radius;
border-width: 1px;
border-color: RogPalette.accent;
background: RogPalette.control-background;
drop-shadow-blur: 10px;
drop-shadow-color: Colors.black;
TouchArea { TouchArea {
height: 100%; height: 100%;
width: 100%; width: 100%;
@@ -148,13 +212,23 @@ export component MainWindow inherits Window {
} }
} }
Rectangle { HorizontalLayout {
height: 100%; padding-left: 16px;
width: 100%; padding-right: 16px;
background: Palette.control-background; alignment: space-between;
Text { Text {
color: Palette.control-foreground; vertical-alignment: center;
color: RogPalette.text-primary;
text: root.toast_text; text: root.toast_text;
overflow: elide;
}
Text {
vertical-alignment: center;
text: "Dismiss";
color: RogPalette.text-secondary;
font-size: 12px;
} }
} }
} }
@@ -174,13 +248,20 @@ export component MainWindow inherits Window {
} }
} }
// TODO: add properties to display
Rectangle { Rectangle {
height: 100%; height: 100%;
width: 100%; width: 100%;
background: Palette.background; background: RogPalette.control-background;
Text { border-radius: 8px;
text: "Click here to exit";
VerticalLayout {
alignment: center;
Text {
horizontal-alignment: center;
text: "Click here to exit";
color: RogPalette.text-primary;
font-size: 16px;
}
} }
} }
} }
@@ -190,35 +271,45 @@ export component MainWindow inherits Window {
y: 0px; y: 0px;
width: root.width; width: root.width;
height: root.height; height: root.height;
//padding only has effect on layout elements
//padding: 10px;
background: Palette.background; background: RogPalette.background;
border-color: Palette.border; border-color: RogPalette.accent;
border-width: 3px; border-width: 2px;
border-radius: 10px; border-radius: 8px;
VerticalBox { VerticalBox {
RogItem { padding: 20px;
min-height: 50px; spacing: 15px;
max-height: 100px; alignment: center;
Text {
text: "Error";
font-size: 22px;
font-weight: 700;
color: RogPalette.accent;
horizontal-alignment: center;
}
Rectangle {
background: RogPalette.control-background;
border-radius: 8px;
min-height: 60px;
Text { Text {
text <=> SomeError.error_message; text <=> SomeError.error_message;
font-size: 18px; font-size: 16px;
color: RogPalette.text-primary;
horizontal-alignment: center;
vertical-alignment: center;
} }
} }
Text { Text {
text <=> SomeError.error_help; text <=> SomeError.error_help;
horizontal-alignment: TextHorizontalAlignment.center; color: RogPalette.text-secondary;
vertical-alignment: TextVerticalAlignment.center; horizontal-alignment: center;
vertical-alignment: center;
} }
} }
} }
} }
export global SomeError {
in property <string> error_message: "";
in property <string> error_help: "";
}

View File

@@ -1,62 +1,128 @@
import { AboutSlint, VerticalBox, HorizontalBox } from "std-widgets.slint"; import { VerticalBox, HorizontalBox, ScrollView } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export component PageAbout inherits VerticalLayout { export component PageAbout inherits Rectangle {
padding: 10px; background: RogPalette.background;
spacing: 10px;
Text {
vertical-alignment: TextVerticalAlignment.center;
horizontal-alignment: TextHorizontalAlignment.center;
text: "A UI for asusctl made with slint";
font-size: 22px;
}
HorizontalBox { ScrollView {
alignment: LayoutAlignment.center;
VerticalBox { VerticalBox {
alignment: LayoutAlignment.center; padding: 30px;
spacing: 20px;
alignment: center;
// Title
Text { Text {
wrap: TextWrap.word-wrap; horizontal-alignment: center;
text: "You need to use kernel version 6.19 to use this software"; text: "ROG Control Center";
font-size: 28px;
font-weight: 800;
color: RogPalette.accent;
} }
Text { Text {
vertical-alignment: TextVerticalAlignment.center; horizontal-alignment: center;
horizontal-alignment: TextHorizontalAlignment.center; text: "A modern UI for asusctl built with Slint";
text: "Todo:"; font-size: 16px;
font-size: 22px; color: RogPalette.text-secondary;
} }
Text { // Version info
text: "- [ ] Theme the widgets"; Rectangle {
height: 60px;
background: RogPalette.control-background;
border-radius: 8px;
border-width: 1px;
border-color: RogPalette.control-border;
HorizontalBox {
padding: 15px;
alignment: center;
Text {
text: "Version 6.3.0";
font-size: 14px;
color: RogPalette.text-primary;
}
Text {
text: " | ";
color: RogPalette.text-secondary;
}
Text {
text: "Requires kernel 6.10+";
font-size: 14px;
color: RogPalette.text-secondary;
}
}
} }
Text { // Features section
text: "- [ ] Add a cpu/gpu temp/fan speed info bar"; Rectangle {
background: RogPalette.control-background;
border-radius: 8px;
border-width: 1px;
border-color: RogPalette.control-border;
VerticalBox {
padding: 20px;
spacing: 12px;
Text {
text: "Features";
font-size: 18px;
font-weight: 700;
color: RogPalette.accent;
}
// Completed features
Text { text: "[x] ROG-themed dark UI"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] System status bar (CPU/GPU temps & fan speeds)"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] Power profile management"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] Aura RGB keyboard lighting"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] AniMe Matrix display"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] Slash LED control"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] Supergfx graphics switching"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] Screenpad brightness & gamma"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] Custom fan curves"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] Desktop notifications (KDE OSD)"; color: RogPalette.text-primary; font-size: 13px; }
Text { text: "[x] System tray integration"; color: RogPalette.text-primary; font-size: 13px; }
// Pending features
Rectangle { height: 10px; }
Text { text: "Planned:"; font-size: 14px; font-weight: 600; color: RogPalette.text-secondary; }
Text { text: "[ ] ROG Ally specific settings"; color: RogPalette.text-secondary; font-size: 13px; }
Text { text: "[ ] Advanced Aura zone editing"; color: RogPalette.text-secondary; font-size: 13px; }
}
} }
Text { // Credits
text: "- [ ] Include fan speeds, temps in a bottom bar"; Rectangle {
} background: RogPalette.control-background;
border-radius: 8px;
border-width: 1px;
border-color: RogPalette.control-border;
Text { VerticalBox {
text: "- [ ] Slash control"; padding: 20px;
} spacing: 8px;
Text { Text {
text: "- [ ] Screenpad controls"; text: "Credits";
} font-size: 18px;
font-weight: 700;
color: RogPalette.accent;
}
Text { Text {
text: "- [ ] ROG Ally specific settings"; text: "asusctl & asusd by Luke Jones";
font-size: 13px;
color: RogPalette.text-primary;
}
Text {
text: "UI built with Slint";
font-size: 13px;
color: RogPalette.text-secondary;
}
}
} }
} }
} }
Text {
vertical-alignment: TextVerticalAlignment.center;
horizontal-alignment: TextHorizontalAlignment.center;
text: "Work in progress";
font-size: 22px;
}
} }

View File

@@ -1,5 +1,6 @@
import { SystemDropdown, SystemToggle } from "../widgets/common.slint"; import { SystemDropdown, SystemToggle } from "../widgets/common.slint";
import { Palette, GroupBox, VerticalBox, Button, HorizontalBox } from "std-widgets.slint"; import { Palette, GroupBox, VerticalBox, Button, HorizontalBox } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export global AnimePageData { export global AnimePageData {
in-out property <[string]> brightness_names: [ in-out property <[string]> brightness_names: [
@@ -109,7 +110,7 @@ export component PageAnime inherits Rectangle {
if root.show_fade_cover: Rectangle { if root.show_fade_cover: Rectangle {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: Palette.background; background: RogPalette.background;
opacity: 0.8; opacity: 0.8;
TouchArea { TouchArea {
height: 100%; height: 100%;
@@ -142,7 +143,7 @@ export component PageAnime inherits Rectangle {
alignment: LayoutAlignment.start; alignment: LayoutAlignment.start;
Text { Text {
font-size: 18px; font-size: 18px;
color: Palette.control-foreground; color: RogPalette.text-primary;
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
text: @tr("Set which builtin animations are played"); text: @tr("Set which builtin animations are played");
} }
@@ -216,7 +217,7 @@ export component PageAnime inherits Rectangle {
alignment: LayoutAlignment.start; alignment: LayoutAlignment.start;
Text { Text {
font-size: 18px; font-size: 18px;
color: Palette.control-foreground; color: RogPalette.text-primary;
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
text: @tr("Advanced Display Settings"); text: @tr("Advanced Display Settings");
} }

View File

@@ -1,5 +1,6 @@
import { Palette } from "std-widgets.slint"; import { VerticalBox, ScrollView, HorizontalBox, Button } from "std-widgets.slint";
import { SystemToggle } from "../widgets/common.slint"; import { SystemToggle, RogItem } from "../widgets/common.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export global AppSettingsPageData { export global AppSettingsPageData {
in-out property <bool> run_in_background; in-out property <bool> run_in_background;
@@ -8,56 +9,134 @@ export global AppSettingsPageData {
callback set_startup_in_background(bool); callback set_startup_in_background(bool);
in-out property <bool> enable_tray_icon; in-out property <bool> enable_tray_icon;
callback set_enable_tray_icon(bool); callback set_enable_tray_icon(bool);
in-out property <bool> enable_dgpu_notifications;
callback set_enable_dgpu_notifications(bool); // Master notification toggle
in-out property <bool> notifications_enabled;
callback set_notifications_enabled(bool);
// Granular notification toggles
in-out property <bool> notify_gfx_switch;
callback set_notify_gfx_switch(bool);
in-out property <bool> notify_gfx_status;
callback set_notify_gfx_status(bool);
in-out property <bool> notify_platform_profile;
callback set_notify_platform_profile(bool);
} }
export component PageAppSettings inherits VerticalLayout { export component PageAppSettings inherits Rectangle {
Rectangle { background: RogPalette.background;
clip: true;
// TODO: slow with border-radius ScrollView {
//padding only has effect on layout elements VerticalBox {
//padding: 8px; padding: 20px;
spacing: 20px;
alignment: start;
// height: parent.height - infobar.height - mainview.padding - self.padding * 2; // General Section
// TODO: border-radius: 8px; VerticalBox {
mainview := VerticalLayout { spacing: 10px;
padding: 10px; padding: 0px;
spacing: 10px;
SystemToggle { Rectangle {
text: @tr("Run in background after closing"); height: 30px;
checked <=> AppSettingsPageData.run_in_background; background: RogPalette.control-background;
toggled => { border-radius: 4px;
AppSettingsPageData.set_run_in_background(AppSettingsPageData.run_in_background) border-width: 1px;
border-color: RogPalette.control-border;
Text {
x: 10px;
vertical-alignment: center;
text: "General Settings";
color: RogPalette.accent;
font-weight: 700;
}
}
SystemToggle {
text: @tr("Run in background after closing");
checked <=> AppSettingsPageData.run_in_background;
toggled => {
AppSettingsPageData.set_run_in_background(AppSettingsPageData.run_in_background)
}
}
SystemToggle {
text: @tr("Start app in background (UI closed)");
checked <=> AppSettingsPageData.startup_in_background;
toggled => {
AppSettingsPageData.set_startup_in_background(AppSettingsPageData.startup_in_background)
}
}
SystemToggle {
text: @tr("Enable system tray icon");
checked <=> AppSettingsPageData.enable_tray_icon;
toggled => {
AppSettingsPageData.set_enable_tray_icon(AppSettingsPageData.enable_tray_icon)
}
} }
} }
SystemToggle { // Notifications Section
text: @tr("Start app in background (UI closed)"); VerticalBox {
checked <=> AppSettingsPageData.startup_in_background; spacing: 10px;
toggled => { padding: 0px;
AppSettingsPageData.set_startup_in_background(AppSettingsPageData.startup_in_background)
}
}
SystemToggle { Rectangle {
text: @tr("Enable system tray icon"); height: 30px;
checked <=> AppSettingsPageData.enable_tray_icon; background: RogPalette.control-background;
toggled => { border-radius: 4px;
AppSettingsPageData.set_enable_tray_icon(AppSettingsPageData.enable_tray_icon) border-width: 1px;
} border-color: RogPalette.control-border;
}
SystemToggle { Text {
text: @tr("Enable dGPU notifications"); x: 10px;
checked <=> AppSettingsPageData.enable_dgpu_notifications; vertical-alignment: center;
toggled => { text: "Notifications";
AppSettingsPageData.set_enable_dgpu_notifications(AppSettingsPageData.enable_dgpu_notifications) color: RogPalette.accent;
font-weight: 700;
}
}
SystemToggle {
text: @tr("Enable Notifications");
checked <=> AppSettingsPageData.notifications_enabled;
toggled => {
AppSettingsPageData.set_notifications_enabled(AppSettingsPageData.notifications_enabled)
}
}
// Sub-toggles container
VerticalBox {
padding-left: 30px; // Indent
spacing: 10px;
visible: AppSettingsPageData.notifications_enabled;
SystemToggle {
text: @tr("Notify on Graphics Switch");
checked <=> AppSettingsPageData.notify_gfx_switch;
toggled => {
AppSettingsPageData.set_notify_gfx_switch(AppSettingsPageData.notify_gfx_switch)
}
}
SystemToggle {
text: @tr("Notify on GPU Status Change");
checked <=> AppSettingsPageData.notify_gfx_status;
toggled => {
AppSettingsPageData.set_notify_gfx_status(AppSettingsPageData.notify_gfx_status)
}
}
SystemToggle {
text: @tr("Notify on Power Profile Change");
checked <=> AppSettingsPageData.notify_platform_profile;
toggled => {
AppSettingsPageData.set_notify_platform_profile(AppSettingsPageData.notify_platform_profile)
}
}
} }
}
Text {
text: "WIP: some features like notifications are not complete";
} }
} }
} }

View File

@@ -1,5 +1,6 @@
import { SystemDropdown, RogItem, SystemToggle, SystemToggleVert } from "../widgets/common.slint"; import { SystemDropdown, RogItem, SystemToggle, SystemToggleVert } from "../widgets/common.slint";
import { Palette, Button, ComboBox, VerticalBox, GroupBox } from "std-widgets.slint"; import { Button, ComboBox, VerticalBox, GroupBox } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
import { StyleMetrics, Slider, HorizontalBox, TextEdit, SpinBox, LineEdit, ScrollView } from "std-widgets.slint"; import { StyleMetrics, Slider, HorizontalBox, TextEdit, SpinBox, LineEdit, ScrollView } from "std-widgets.slint";
import { ColourSlider } from "../widgets/colour_picker.slint"; import { ColourSlider } from "../widgets/colour_picker.slint";
import { AuraPageData, AuraDevType, PowerZones, LaptopAuraPower, AuraEffect } from "../types/aura_types.slint"; import { AuraPageData, AuraDevType, PowerZones, LaptopAuraPower, AuraEffect } from "../types/aura_types.slint";
@@ -183,6 +184,57 @@ export component PageAura inherits Rectangle {
} }
} }
// Software Animation Controls (for Static-only keyboards)
if AuraPageData.soft_animation_available: RogItem {
min-height: 100px;
VerticalLayout {
padding: 10px;
spacing: 8px;
Text {
text: @tr("Software Animation (Static-only keyboards)");
font-size: 14px;
font-weight: 600;
color: RogPalette.accent;
}
HorizontalLayout {
spacing: 20px;
VerticalLayout {
Text {
text: @tr("Animation Mode");
color: RogPalette.text-secondary;
}
ComboBox {
current_index <=> AuraPageData.soft_animation_mode;
current_value: AuraPageData.soft_animation_modes[self.current-index];
model <=> AuraPageData.soft_animation_modes;
selected => {
AuraPageData.cb_soft_animation_mode(AuraPageData.soft_animation_mode);
}
}
}
VerticalLayout {
horizontal-stretch: 1;
Text {
text: @tr("Speed: ") + Math.round(AuraPageData.soft_animation_speed) + "ms";
color: RogPalette.text-secondary;
}
Slider {
minimum: 150;
maximum: 1000;
value <=> AuraPageData.soft_animation_speed;
released => {
AuraPageData.cb_soft_animation_speed(Math.round(AuraPageData.soft_animation_speed));
}
}
}
}
}
}
HorizontalLayout { HorizontalLayout {
Button { Button {
text: @tr("Power Settings"); text: @tr("Power Settings");
@@ -195,11 +247,15 @@ export component PageAura inherits Rectangle {
} }
if root.show_fade_cover: Rectangle { if root.show_fade_cover: Rectangle {
background: Palette.background; background: RogPalette.background;
opacity: 0.8; opacity: 0.8;
TouchArea { TouchArea {
height: 100%; height: 100%;
width: 100%; width: 100%;
clicked => {
root.show_fade_cover = false;
root.show_aura_power = false;
}
} }
} }
} }
@@ -266,7 +322,10 @@ export component PageAura inherits Rectangle {
alignment: LayoutAlignment.start; alignment: LayoutAlignment.start;
Text { Text {
text: "TODO: In progress"; text: "LED Power Zones (Legacy)";
font-size: 16px;
font-weight: 600;
color: #ff0033;
} }
for state[idx] in AuraPageData.led_power.states: old_zone := AuraPowerGroupOld { for state[idx] in AuraPageData.led_power.states: old_zone := AuraPowerGroupOld {

View File

@@ -1,7 +1,10 @@
import { Palette, TabWidget, Button, CheckBox } from "std-widgets.slint"; import { Palette, TabWidget, Button, CheckBox, Slider } from "std-widgets.slint";
import { Graph, Node } from "../widgets/graph.slint"; import { Graph, Node } from "../widgets/graph.slint";
import { SystemToggle } from "../widgets/common.slint"; import { SystemToggle } from "../widgets/common.slint";
import { Profile, FanType, FanPageData } from "../types/fan_types.slint"; import { Profile, FanType, FanPageData } from "../types/fan_types.slint";
import { RogPalette } from "../themes/rog_theme.slint";
component FanTab inherits Rectangle { component FanTab inherits Rectangle {
in-out property <bool> enabled: false; in-out property <bool> enabled: false;
@@ -16,10 +19,81 @@ component FanTab inherits Rectangle {
in-out property <[Node]> nodes; in-out property <[Node]> nodes;
VerticalLayout { VerticalLayout {
private property <bool> local_busy:
(root.fan_type == FanType.CPU && FanPageData.is_busy_cpu) ||
(root.fan_type == FanType.GPU && FanPageData.is_busy_gpu) ||
(root.fan_type == FanType.Middle && FanPageData.is_busy_mid);
if FanPageData.show_custom_warning: Rectangle {
background: RogPalette.control-background;
border-radius: 4px;
height: 48px;
HorizontalLayout {
padding: 10px;
Text {
color: #ffd700; // Gold/Yellow
text: @tr("Zero RPM Mode Enabled: Fans will take ~25s to spin down entirely.");
vertical-alignment: TextVerticalAlignment.center;
horizontal-alignment: TextHorizontalAlignment.center;
wrap: word-wrap;
}
}
}
HorizontalLayout { HorizontalLayout {
spacing: 10px;
if root.tab_enabled: Graph { if root.tab_enabled: Graph {
nodes <=> root.nodes; nodes <=> root.nodes;
} }
if root.tab_enabled: VerticalLayout {
width: 40px;
alignment: center;
spacing: 10px;
Button {
text: "+";
height: 40px;
width: 40px;
clicked => {
root.nodes = [
{ x: root.nodes[0].x, y: min(255px, root.nodes[0].y + 13px) },
{ x: root.nodes[1].x, y: min(255px, root.nodes[1].y + 13px) },
{ x: root.nodes[2].x, y: min(255px, root.nodes[2].y + 13px) },
{ x: root.nodes[3].x, y: min(255px, root.nodes[3].y + 13px) },
{ x: root.nodes[4].x, y: min(255px, root.nodes[4].y + 13px) },
{ x: root.nodes[5].x, y: min(255px, root.nodes[5].y + 13px) },
{ x: root.nodes[6].x, y: min(255px, root.nodes[6].y + 13px) },
{ x: root.nodes[7].x, y: min(255px, root.nodes[7].y + 13px) }
];
}
}
Text {
text: "All";
font-size: 10px;
horizontal-alignment: center;
color: white;
}
Button {
text: "-";
height: 40px;
width: 40px;
clicked => {
root.nodes = [
{ x: root.nodes[0].x, y: max(0px, root.nodes[0].y - 13px) },
{ x: root.nodes[1].x, y: max(0px, root.nodes[1].y - 13px) },
{ x: root.nodes[2].x, y: max(0px, root.nodes[2].y - 13px) },
{ x: root.nodes[3].x, y: max(0px, root.nodes[3].y - 13px) },
{ x: root.nodes[4].x, y: max(0px, root.nodes[4].y - 13px) },
{ x: root.nodes[5].x, y: max(0px, root.nodes[5].y - 13px) },
{ x: root.nodes[6].x, y: max(0px, root.nodes[6].y - 13px) },
{ x: root.nodes[7].x, y: max(0px, root.nodes[7].y - 13px) }
];
}
}
}
if !root.tab_enabled: Rectangle { if !root.tab_enabled: Rectangle {
Text { Text {
font-size: 24px; font-size: 24px;
@@ -29,19 +103,20 @@ component FanTab inherits Rectangle {
} }
HorizontalLayout { HorizontalLayout {
spacing: 20px;
alignment: LayoutAlignment.end; alignment: LayoutAlignment.end;
CheckBox { CheckBox {
text: @tr("Enabled"); text: @tr("Enable Manual Control");
checked <=> root.enabled; checked <=> root.enabled;
enabled <=> root.tab_enabled; enabled: root.tab_enabled && !local_busy;
toggled => { toggled => {
root.toggled(); root.toggled();
} }
} }
Button { Button {
text: @tr("Apply"); text: local_busy ? @tr("Applying...") : @tr("Apply Curve");
enabled <=> root.tab_enabled; enabled: root.tab_enabled && root.enabled && !local_busy;
clicked => { clicked => {
root.apply(); root.apply();
} }
@@ -49,7 +124,7 @@ component FanTab inherits Rectangle {
Button { Button {
text: @tr("Cancel"); text: @tr("Cancel");
enabled <=> root.tab_enabled; enabled: root.tab_enabled && !local_busy;
clicked => { clicked => {
root.cancel() root.cancel()
} }
@@ -57,7 +132,7 @@ component FanTab inherits Rectangle {
Button { Button {
text: @tr("Factory Default (all fans)"); text: @tr("Factory Default (all fans)");
enabled <=> root.tab_enabled; enabled: root.tab_enabled && !local_busy;
clicked => { clicked => {
root.default(); root.default();
} }
@@ -86,6 +161,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Balanced); FanPageData.set_profile_default(Profile.Balanced);
} }
cancel => {
FanPageData.cancel(FanType.CPU, Profile.Balanced);
}
} }
} }
@@ -104,6 +182,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Balanced); FanPageData.set_profile_default(Profile.Balanced);
} }
cancel => {
FanPageData.cancel(FanType.Middle, Profile.Balanced);
}
} }
} }
@@ -122,6 +203,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Balanced); FanPageData.set_profile_default(Profile.Balanced);
} }
cancel => {
FanPageData.cancel(FanType.GPU, Profile.Balanced);
}
} }
} }
} }
@@ -145,6 +229,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Performance); FanPageData.set_profile_default(Profile.Performance);
} }
cancel => {
FanPageData.cancel(FanType.CPU, Profile.Performance);
}
} }
} }
@@ -163,6 +250,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Performance); FanPageData.set_profile_default(Profile.Performance);
} }
cancel => {
FanPageData.cancel(FanType.Middle, Profile.Performance);
}
} }
} }
@@ -181,6 +271,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Performance); FanPageData.set_profile_default(Profile.Performance);
} }
cancel => {
FanPageData.cancel(FanType.GPU, Profile.Performance);
}
} }
} }
} }
@@ -204,6 +297,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Quiet); FanPageData.set_profile_default(Profile.Quiet);
} }
cancel => {
FanPageData.cancel(FanType.CPU, Profile.Quiet);
}
} }
} }
@@ -222,6 +318,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Quiet); FanPageData.set_profile_default(Profile.Quiet);
} }
cancel => {
FanPageData.cancel(FanType.Middle, Profile.Quiet);
}
} }
} }
@@ -240,6 +339,9 @@ export component PageFans inherits VerticalLayout {
default => { default => {
FanPageData.set_profile_default(Profile.Quiet); FanPageData.set_profile_default(Profile.Quiet);
} }
cancel => {
FanPageData.cancel(FanType.GPU, Profile.Quiet);
}
} }
} }
} }

View File

@@ -0,0 +1,102 @@
import { Button, VerticalBox, Slider, Switch } from "std-widgets.slint";
import { ScreenpadPageData } from "../types/screenpad_types.slint";
import { RogPalette } from "../themes/rog_theme.slint";
import { RogItem, SystemSlider } from "../widgets/common.slint";
export { ScreenpadPageData }
export component PageScreenpad inherits Rectangle {
background: RogPalette.background;
VerticalBox {
alignment: LayoutAlignment.start;
padding: 20px;
spacing: 20px;
Text {
text: @tr("Screenpad Controls");
font-size: 24px;
font-weight: 700;
color: RogPalette.accent;
}
RogItem {
HorizontalLayout {
padding: 15px;
spacing: 20px;
alignment: LayoutAlignment.space-between;
Text {
text: @tr("Enable Screenpad");
font-size: 16px;
vertical-alignment: TextVerticalAlignment.center;
color: RogPalette.text-primary;
}
Switch {
checked <=> ScreenpadPageData.power;
toggled => {
ScreenpadPageData.cb_power(self.checked);
}
}
}
}
VerticalLayout {
spacing: 15px;
// Brightness Slider
SystemSlider {
enabled: ScreenpadPageData.power;
text: @tr("Brightness");
minimum: 0;
maximum: 255;
value: ScreenpadPageData.brightness;
help_text: ScreenpadPageData.brightness == -1 ? @tr("Not available") : "";
released => {
ScreenpadPageData.cb_brightness(Math.round(self.value));
}
}
// Gamma Slider (New)
SystemSlider {
enabled: ScreenpadPageData.power;
text: @tr("Gamma");
minimum: 0.1;
maximum: 2.5;
value: ScreenpadPageData.gamma;
help_text: @tr("Adjust color intensity");
released => {
ScreenpadPageData.cb_gamma(self.value);
}
}
RogItem {
enabled: ScreenpadPageData.power;
HorizontalLayout {
padding: 15px;
spacing: 20px;
alignment: LayoutAlignment.space-between;
Text {
text: @tr("Sync with Primary Display");
font-size: 16px;
vertical-alignment: TextVerticalAlignment.center;
color: RogPalette.text-primary;
}
Switch {
enabled: ScreenpadPageData.power;
checked <=> ScreenpadPageData.sync_with_primary;
toggled => {
ScreenpadPageData.cb_sync_with_primary(self.checked);
}
}
}
}
}
// Spacer
Rectangle {}
}
}

View File

@@ -0,0 +1,114 @@
import { SystemToggle, SystemSlider, SystemDropdown, RogItem } from "../widgets/common.slint";
import { VerticalBox, ScrollView, GroupBox } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
import { SlashPageData } from "../types/slash_types.slint";
export { SlashPageData }
export component PageSlash inherits Rectangle {
background: RogPalette.background;
ScrollView {
VerticalBox {
padding: 20px;
spacing: 20px;
alignment: start;
// Header
Rectangle {
height: 40px;
background: RogPalette.control-background;
border-radius: RogPalette.border-radius;
border-width: 1px;
border-color: RogPalette.control-border;
Text {
text: @tr("Slash Lighting Control");
color: RogPalette.accent;
font-size: 18px;
font-weight: 700;
horizontal-alignment: center;
vertical-alignment: center;
}
}
// Main Control
RogItem {
VerticalBox {
SystemToggle {
text: @tr("Enable Slash Lighting");
checked <=> SlashPageData.enabled;
toggled => { SlashPageData.cb_enabled(self.checked); }
}
SystemDropdown {
text: @tr("Lighting Mode");
model <=> SlashPageData.modes;
current_index <=> SlashPageData.mode_index;
current_value: SlashPageData.modes[SlashPageData.mode_index];
selected => {
SlashPageData.cb_mode_index(self.current_index);
}
}
SystemSlider {
title: @tr("Brightness");
text: @tr("Brightness");
value <=> SlashPageData.brightness;
minimum: 0;
maximum: 255;
help_text: "";
released(val) => { SlashPageData.cb_brightness(val); }
}
SystemSlider {
title: @tr("Interval / Speed");
text: @tr("Interval / Speed");
value <=> SlashPageData.interval;
minimum: 0;
maximum: 255;
help_text: "";
released(val) => { SlashPageData.cb_interval(val); }
}
}
}
// Behaviors
GroupBox {
title: @tr("Behavior Settings");
VerticalBox {
SystemToggle {
text: @tr("Show Battery Warning");
checked <=> SlashPageData.show_battery_warning;
toggled => { SlashPageData.cb_show_battery_warning(self.checked); }
}
SystemToggle {
text: @tr("Active on Battery");
checked <=> SlashPageData.show_on_battery;
toggled => { SlashPageData.cb_show_on_battery(self.checked); }
}
SystemToggle {
text: @tr("Active on Boot");
checked <=> SlashPageData.show_on_boot;
toggled => { SlashPageData.cb_show_on_boot(self.checked); }
}
SystemToggle {
text: @tr("Active on Shutdown");
checked <=> SlashPageData.show_on_shutdown;
toggled => { SlashPageData.cb_show_on_shutdown(self.checked); }
}
SystemToggle {
text: @tr("Active on Sleep");
checked <=> SlashPageData.show_on_sleep;
toggled => { SlashPageData.cb_show_on_sleep(self.checked); }
}
SystemToggle {
text: @tr("Active when Lid Closed");
checked <=> SlashPageData.show_on_lid_closed;
toggled => { SlashPageData.cb_show_on_lid_closed(self.checked); }
}
}
}
}
}
}

View File

@@ -0,0 +1,73 @@
import { SystemDropdown, RogItem } from "../widgets/common.slint";
import { VerticalBox, ScrollView, HorizontalBox } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
import { SupergfxPageData } from "../types/supergfx_types.slint";
export { SupergfxPageData }
export component PageSupergfx inherits Rectangle {
background: RogPalette.background;
ScrollView {
VerticalBox {
padding: 20px;
spacing: 20px;
alignment: start;
// Header
Rectangle {
height: 40px;
background: RogPalette.control-background;
border-radius: RogPalette.border-radius;
border-width: 1px;
border-color: RogPalette.control-border;
Text {
text: @tr("Graphics Control (supergfx)");
color: RogPalette.accent;
font-size: 18px;
font-weight: 700;
horizontal-alignment: center;
vertical-alignment: center;
}
}
RogItem {
HorizontalBox {
Text {
text: @tr("Vendor: ") + SupergfxPageData.vendor;
color: RogPalette.text-secondary;
vertical-alignment: center;
}
}
}
// Main Control
RogItem {
VerticalBox {
Text {
text: @tr("Current Mode: ") + SupergfxPageData.current_mode;
color: RogPalette.text-primary;
}
SystemDropdown {
text: @tr("Graphics Mode");
model <=> SupergfxPageData.supported_modes;
current_index <=> SupergfxPageData.selected_index;
current_value: SupergfxPageData.supported_modes[SupergfxPageData.selected_index];
selected => {
SupergfxPageData.set_mode(self.current_value);
}
}
Text {
text: @tr("Note: Changing modes requires a logout.");
color: RogPalette.text-secondary;
font-size: 12px;
wrap: word-wrap;
}
}
}
}
}
}

View File

@@ -1,5 +1,6 @@
import { SystemSlider, SystemDropdown, SystemToggle, SystemToggleInt, RogItem } from "../widgets/common.slint"; import { SystemSlider, SystemDropdown, SystemToggle, SystemToggleInt, RogItem } from "../widgets/common.slint";
import { Palette, HorizontalBox , VerticalBox, ScrollView, Slider, Button, Switch, ComboBox, GroupBox, StandardButton} from "std-widgets.slint"; import { Palette, HorizontalBox , VerticalBox, ScrollView, Slider, Button, Switch, ComboBox, GroupBox, StandardButton} from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export struct AttrMinMax { export struct AttrMinMax {
min: int, min: int,
@@ -66,13 +67,6 @@ export global SystemPageData {
in-out property <int> mini_led_mode; in-out property <int> mini_led_mode;
callback cb_mini_led_mode(int); callback cb_mini_led_mode(int);
in-out property <float> screenpad_gamma;
callback cb_screenpad_gamma(float);
// percentage
in-out property <int> screenpad_brightness: 50;
callback cb_screenpad_brightness(int);
in-out property <bool> screenpad_sync_with_primary: false;
callback cb_screenpad_sync_with_primary(bool);
in-out property <bool> asus_armoury_loaded: false; in-out property <bool> asus_armoury_loaded: false;
@@ -157,18 +151,22 @@ export component PageSystem inherits Rectangle {
ScrollView { ScrollView {
VerticalLayout { VerticalLayout {
padding: 10px; padding: 10px;
padding-top: 40px;
padding-bottom: 40px;
spacing: 10px; spacing: 10px;
alignment: LayoutAlignment.start; alignment: LayoutAlignment.start;
Rectangle { Rectangle {
background: Palette.alternate-background; background: RogPalette.control-background;
border-color: Palette.accent-background; border-color: RogPalette.control-border;
border-width: 3px; border-width: 1px;
border-radius: 10px; border-radius: 8px;
height: 40px; height: 46px;
Text { Text {
font-size: 18px; font-size: 18px;
color: Palette.control-foreground; color: RogPalette.accent;
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
vertical-alignment: TextVerticalAlignment.center;
font-weight: 700;
text: @tr("Power settings"); text: @tr("Power settings");
} }
} }
@@ -207,62 +205,18 @@ export component PageSystem inherits Rectangle {
} }
} }
if SystemPageData.screenpad_brightness != -1: RogItem {
HorizontalLayout {
padding-left: 10px;
padding-right: 20px;
HorizontalLayout {
width: 38%;
alignment: LayoutAlignment.space-between;
padding-right: 15px;
Text {
font-size: 16px;
vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground;
text: @tr("Screenpad brightness");
}
}
HorizontalLayout {
width: 38%;
alignment: LayoutAlignment.stretch;
screen_bright := Slider {
enabled: true;
minimum: 0;
maximum: 100;
value: SystemPageData.screenpad_brightness;
released(value) => {
// SystemPageData.screenpad_brightness = self.value;
SystemPageData.cb_screenpad_brightness(Math.floor(self.value));
}
}
}
HorizontalLayout {
width: 20%;
padding-left: 10px;
alignment: LayoutAlignment.stretch;
Switch {
text: @tr("Sync with primary");
checked <=> SystemPageData.screenpad_sync_with_primary;
toggled => {
SystemPageData.cb_screenpad_sync_with_primary(self.checked);
}
}
}
}
}
Rectangle { Rectangle {
background: Palette.alternate-background; background: RogPalette.control-background;
border-color: Palette.accent-background; border-color: RogPalette.control-border;
border-width: 3px; border-width: 1px;
border-radius: 10px; border-radius: 8px;
height: 40px; height: 46px;
Text { Text {
font-size: 18px; font-size: 18px;
color: Palette.control-foreground; color: RogPalette.accent;
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
vertical-alignment: TextVerticalAlignment.center;
font-weight: 700;
text: @tr("Armoury settings"); text: @tr("Armoury settings");
} }
} }
@@ -377,6 +331,7 @@ export component PageSystem inherits Rectangle {
Text { Text {
font-size: 16px; font-size: 16px;
text: @tr("ppt_warning" => "The following settings are not applied until the toggle is enabled."); text: @tr("ppt_warning" => "The following settings are not applied until the toggle is enabled.");
color: RogPalette.text-primary;
} }
} }
@@ -545,7 +500,7 @@ export component PageSystem inherits Rectangle {
if root.show_fade_cover: Rectangle { if root.show_fade_cover: Rectangle {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: Palette.background; background: RogPalette.background;
opacity: 0.9; opacity: 0.9;
TouchArea { TouchArea {
height: 100%; height: 100%;
@@ -578,6 +533,7 @@ export component PageSystem inherits Rectangle {
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
vertical-alignment: TextVerticalAlignment.center; vertical-alignment: TextVerticalAlignment.center;
text: @tr("Energy Performance Preference linked to Throttle Policy"); text: @tr("Energy Performance Preference linked to Throttle Policy");
color: RogPalette.text-primary;
} }
SystemToggle { SystemToggle {
@@ -628,6 +584,7 @@ export component PageSystem inherits Rectangle {
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
vertical-alignment: TextVerticalAlignment.center; vertical-alignment: TextVerticalAlignment.center;
text: @tr("Throttle Policy for power state"); text: @tr("Throttle Policy for power state");
color: RogPalette.text-primary;
} }
HorizontalLayout { HorizontalLayout {

View File

@@ -0,0 +1,13 @@
export global RogPalette {
out property <brush> background: #0a0a0a;
out property <brush> alternate-background: #111111;
out property <brush> control-background: #1e1e1e;
out property <brush> control-border: #333333;
out property <brush> control-border-hover: #555555;
out property <brush> control-border-checked: #ff0033; // ROG Red
out property <brush> text-primary: #ffffff;
out property <brush> text-secondary: #aaaaaa;
out property <brush> accent: #ff0033;
out property <brush> accent-hover: #d60000;
out property <length> border-radius: 4px;
}

View File

@@ -8,6 +8,13 @@ export enum AuraDevType {
AnimeOrSlash, AnimeOrSlash,
} }
// Software animation modes for keyboards that only support Static
export enum SoftAnimationMode {
None,
Rainbow,
ColorCycle,
}
export struct AuraEffect { export struct AuraEffect {
/// The effect type /// The effect type
mode: int, mode: int,
@@ -166,4 +173,16 @@ export global AuraPageData {
}] }]
}; };
callback cb_led_power(LaptopAuraPower); callback cb_led_power(LaptopAuraPower);
// Software animation properties (for Static-only keyboards)
in-out property <[string]> soft_animation_modes: [
@tr("Animation mode" => "None"),
@tr("Animation mode" => "Rainbow"),
@tr("Animation mode" => "Color Cycle"),
];
in-out property <int> soft_animation_mode: 0;
in-out property <float> soft_animation_speed: 200; // ms between updates
in-out property <bool> soft_animation_available: false; // Set true when only Static mode is supported
callback cb_soft_animation_mode(int);
callback cb_soft_animation_speed(int);
} }

View File

@@ -35,314 +35,64 @@ export global FanPageData {
in-out property <bool> quiet_gpu_enabled: true; in-out property <bool> quiet_gpu_enabled: true;
in-out property <bool> quiet_mid_enabled: false; in-out property <bool> quiet_mid_enabled: false;
in-out property <bool> is_busy_cpu: false;
in-out property <bool> is_busy_gpu: false;
in-out property <bool> is_busy_mid: false;
in-out property <bool> show_custom_warning: false;
callback set_fan_data(FanType, Profile, bool, [Node]); callback set_fan_data(FanType, Profile, bool, [Node]);
callback set_profile_default(Profile); callback set_profile_default(Profile);
callback set_is_busy(FanType, bool);
// Last applied cache for Cancel button
in-out property <[Node]> last_applied_cpu_balanced: [];
in-out property <[Node]> last_applied_gpu_balanced: [];
in-out property <[Node]> last_applied_mid_balanced: [];
in-out property <[Node]> last_applied_cpu_performance: [];
in-out property <[Node]> last_applied_gpu_performance: [];
in-out property <[Node]> last_applied_mid_performance: [];
in-out property <[Node]> last_applied_cpu_quiet: [];
in-out property <[Node]> last_applied_gpu_quiet: [];
in-out property <[Node]> last_applied_mid_quiet: [];
callback cancel(FanType, Profile);
in-out property <[Node]> balanced_cpu: [ in-out property <[Node]> balanced_cpu: [
{ { x: 30px, y: 0px }, { x: 40px, y: 0px }, { x: 50px, y: 0px }, { x: 60px, y: 0px },
x: 10px, { x: 70px, y: 25px }, { x: 80px, y: 55px }, { x: 90px, y: 85px }, { x: 98px, y: 100px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
in-out property <[Node]> balanced_mid: [ in-out property <[Node]> balanced_mid: [
{ { x: 30px, y: 0px }, { x: 40px, y: 0px }, { x: 50px, y: 0px }, { x: 60px, y: 0px },
x: 10px, { x: 70px, y: 25px }, { x: 80px, y: 55px }, { x: 90px, y: 85px }, { x: 98px, y: 100px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
in-out property <[Node]> balanced_gpu: [ in-out property <[Node]> balanced_gpu: [
{ { x: 30px, y: 0px }, { x: 40px, y: 0px }, { x: 50px, y: 0px }, { x: 60px, y: 0px },
x: 10px, { x: 70px, y: 25px }, { x: 80px, y: 55px }, { x: 90px, y: 85px }, { x: 98px, y: 100px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
in-out property <[Node]> performance_cpu: [ in-out property <[Node]> performance_cpu: [
{ { x: 30px, y: 0px }, { x: 40px, y: 10px }, { x: 50px, y: 30px }, { x: 60px, y: 50px },
x: 10px, { x: 70px, y: 70px }, { x: 80px, y: 85px }, { x: 90px, y: 95px }, { x: 98px, y: 100px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
in-out property <[Node]> performance_mid: [ in-out property <[Node]> performance_mid: [
{ { x: 30px, y: 0px }, { x: 40px, y: 10px }, { x: 50px, y: 30px }, { x: 60px, y: 50px },
x: 10px, { x: 70px, y: 70px }, { x: 80px, y: 85px }, { x: 90px, y: 95px }, { x: 98px, y: 100px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
in-out property <[Node]> performance_gpu: [ in-out property <[Node]> performance_gpu: [
{ { x: 30px, y: 0px }, { x: 40px, y: 10px }, { x: 50px, y: 30px }, { x: 60px, y: 50px },
x: 10px, { x: 70px, y: 70px }, { x: 80px, y: 85px }, { x: 90px, y: 95px }, { x: 98px, y: 100px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
in-out property <[Node]> quiet_cpu: [ in-out property <[Node]> quiet_cpu: [
{ { x: 30px, y: 0px }, { x: 40px, y: 0px }, { x: 50px, y: 0px }, { x: 60px, y: 0px },
x: 10px, { x: 70px, y: 20px }, { x: 80px, y: 40px }, { x: 90px, y: 70px }, { x: 98px, y: 90px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
in-out property <[Node]> quiet_mid: [ in-out property <[Node]> quiet_mid: [
{ { x: 30px, y: 0px }, { x: 40px, y: 0px }, { x: 50px, y: 0px }, { x: 60px, y: 0px },
x: 10px, { x: 70px, y: 20px }, { x: 80px, y: 40px }, { x: 90px, y: 70px }, { x: 98px, y: 90px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
in-out property <[Node]> quiet_gpu: [ in-out property <[Node]> quiet_gpu: [
{ { x: 30px, y: 0px }, { x: 40px, y: 0px }, { x: 50px, y: 0px }, { x: 60px, y: 0px },
x: 10px, { x: 70px, y: 20px }, { x: 80px, y: 40px }, { x: 90px, y: 70px }, { x: 98px, y: 90px },
y: 10px,
},
{
x: 40px,
y: 30px,
},
{
x: 50px,
y: 50px,
},
{
x: 55px,
y: 50px,
},
{
x: 60px,
y: 60px,
},
{
x: 65px,
y: 70px,
},
{
x: 70px,
y: 80px,
},
{
x: 90px,
y: 100px,
},
]; ];
function set_fan(profile: Profile, fan: FanType, data: [Node]) { function set_fan(profile: Profile, fan: FanType, data: [Node]) {

View File

@@ -0,0 +1,13 @@
import { RogPalette } from "../themes/rog_theme.slint";
export global ScreenpadPageData {
in-out property <int> brightness: -1;
in-out property <float> gamma: 1.0;
in-out property <bool> sync_with_primary: false;
in-out property <bool> power: true;
callback cb_brightness(int);
callback cb_gamma(float);
callback cb_sync_with_primary(bool);
callback cb_power(bool);
}

View File

@@ -0,0 +1,49 @@
export global SlashPageData {
in-out property <bool> enabled;
callback cb_enabled(bool);
in-out property <float> brightness;
callback cb_brightness(float);
in-out property <float> interval;
callback cb_interval(float);
in-out property <[string]> modes: [
@tr("Static"),
@tr("Bounce"),
@tr("Slash"),
@tr("Loading"),
@tr("BitStream"),
@tr("Transmission"),
@tr("Flow"),
@tr("Flux"),
@tr("Phantom"),
@tr("Spectrum"),
@tr("Hazard"),
@tr("Interfacing"),
@tr("Ramp"),
@tr("GameOver"),
@tr("Start"),
@tr("Buzzer"),
];
in-out property <int> mode_index;
callback cb_mode_index(int);
in-out property <bool> show_battery_warning;
callback cb_show_battery_warning(bool);
in-out property <bool> show_on_battery;
callback cb_show_on_battery(bool);
in-out property <bool> show_on_boot;
callback cb_show_on_boot(bool);
in-out property <bool> show_on_shutdown;
callback cb_show_on_shutdown(bool);
in-out property <bool> show_on_sleep;
callback cb_show_on_sleep(bool);
in-out property <bool> show_on_lid_closed;
callback cb_show_on_lid_closed(bool);
}

View File

@@ -0,0 +1,10 @@
export global SupergfxPageData {
in-out property <string> current_mode: "Hybrid";
in-out property <[string]> supported_modes: ["Hybrid", "Integrated"];
in-out property <int> selected_index: 0;
in-out property <string> vendor: "Unknown";
callback set_mode(string);
callback refresh();
}

View File

@@ -1,12 +1,14 @@
import { Palette, VerticalBox, HorizontalBox, GroupBox } from "std-widgets.slint"; import { VerticalBox, HorizontalBox, GroupBox } from "std-widgets.slint";
import { SystemToggleVert, SystemDropdown } from "common.slint"; import { SystemToggleVert, SystemDropdown } from "common.slint";
import { PowerZones } from "../types/aura_types.slint"; import { PowerZones } from "../types/aura_types.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export component AuraPowerGroup inherits Rectangle { export component AuraPowerGroup inherits Rectangle {
min-width: row.min-width; min-width: row.min-width;
border-radius: 20px; border-radius: 8px;
background: Palette.alternate-background; background: RogPalette.control-background;
opacity: 0.9; border-width: 1px;
border-color: RogPalette.control-border;
in-out property <string> group-title; in-out property <string> group-title;
in-out property <bool> boot_checked; in-out property <bool> boot_checked;
in-out property <bool> awake_checked; in-out property <bool> awake_checked;
@@ -20,7 +22,7 @@ export component AuraPowerGroup inherits Rectangle {
spacing: 10px; spacing: 10px;
Text { Text {
font-size: 18px; font-size: 18px;
color: Palette.alternate-foreground; color: RogPalette.text-primary;
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
text <=> root.group-title; text <=> root.group-title;
} }
@@ -72,9 +74,10 @@ export component AuraPowerGroup inherits Rectangle {
export component AuraPowerGroupOld inherits Rectangle { export component AuraPowerGroupOld inherits Rectangle {
min-width: row.min-width; min-width: row.min-width;
border-radius: 20px; border-radius: 8px;
background: Palette.alternate-background; background: RogPalette.control-background;
opacity: 0.9; border-width: 1px;
border-color: RogPalette.control-border;
in-out property <int> current_zone; in-out property <int> current_zone;
in-out property <[int]> zones; in-out property <[int]> zones;
in-out property <[string]> zone_strings; in-out property <[string]> zone_strings;
@@ -90,7 +93,7 @@ export component AuraPowerGroupOld inherits Rectangle {
spacing: 10px; spacing: 10px;
Text { Text {
font-size: 18px; font-size: 18px;
color: Palette.alternate-foreground; color: RogPalette.text-primary;
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
text <=> root.group-title; text <=> root.group-title;
} }

View File

@@ -1,12 +1,15 @@
import { Palette, VerticalBox , StandardButton, Button, HorizontalBox, ComboBox, Switch, Slider} from "std-widgets.slint"; import { VerticalBox , StandardButton, Button, HorizontalBox, ComboBox, Switch, Slider} from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export component RogItem inherits Rectangle { export component RogItem inherits Rectangle {
background: Palette.control-background; in property <bool> enabled: true;
border-color: Palette.border; background: root.enabled ? RogPalette.control-background : RogPalette.control-background.darker(0.5);
border-width: 3px; border-color: root.enabled ? RogPalette.control-border : RogPalette.control-border.darker(0.3);
border-radius: 10px; border-width: 1px; // Thinner border for modern look
border-radius: RogPalette.border-radius;
min-height: 48px; min-height: 48px;
max-height: 56px; max-height: 56px;
opacity: root.enabled ? 1.0 : 0.6;
} }
export component SystemSlider inherits RogItem { export component SystemSlider inherits RogItem {
@@ -18,7 +21,6 @@ export component SystemSlider inherits RogItem {
callback released(float); callback released(float);
in property <string> help_text; in property <string> help_text;
in property <bool> enabled: true;
in property <bool> has_reset: false; in property <bool> has_reset: false;
callback cb_do_reset(); callback cb_do_reset();
@@ -32,7 +34,7 @@ export component SystemSlider inherits RogItem {
Text { Text {
font-size: 16px; font-size: 16px;
vertical-alignment: TextVerticalAlignment.center; vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground; color: RogPalette.text-primary;
text: root.text; text: root.text;
} }
@@ -40,7 +42,7 @@ export component SystemSlider inherits RogItem {
font-size: 16px; font-size: 16px;
horizontal-alignment: TextHorizontalAlignment.right; horizontal-alignment: TextHorizontalAlignment.right;
vertical-alignment: TextVerticalAlignment.center; vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground; color: RogPalette.accent;
text: "\{Math.round(root.value)}"; text: "\{Math.round(root.value)}";
} }
} }
@@ -64,10 +66,11 @@ export component SystemSlider inherits RogItem {
y: help.y - self.height + help.height - 10px; y: help.y - self.height + help.height - 10px;
Rectangle { Rectangle {
drop-shadow-blur: 10px; drop-shadow-blur: 10px;
drop-shadow-color: black; drop-shadow-color: Colors.black;
border-radius: 10px; border-radius: RogPalette.border-radius;
border-color: Palette.accent-background; border-width: 1px;
background: Palette.background; border-color: RogPalette.accent;
background: RogPalette.control-background;
Dialog { Dialog {
title: root.title; title: root.title;
VerticalBox { VerticalBox {
@@ -77,12 +80,12 @@ export component SystemSlider inherits RogItem {
wrap: TextWrap.word-wrap; wrap: TextWrap.word-wrap;
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
text: root.title; text: root.title;
color: RogPalette.text-primary;
} }
Rectangle { Rectangle {
height: 1px; height: 1px;
border-color: black; background: RogPalette.control-border;
border-width: 1px;
} }
Text { Text {
@@ -90,6 +93,7 @@ export component SystemSlider inherits RogItem {
font-size: 16px; font-size: 16px;
wrap: TextWrap.word-wrap; wrap: TextWrap.word-wrap;
text: root.help_text; text: root.help_text;
color: RogPalette.text-secondary;
} }
} }
@@ -114,16 +118,18 @@ export component SystemSlider inherits RogItem {
y: reset.y - self.height + reset.height; y: reset.y - self.height + reset.height;
Rectangle { Rectangle {
drop-shadow-blur: 10px; drop-shadow-blur: 10px;
drop-shadow-color: black; drop-shadow-color: Colors.black;
border-radius: 10px; border-radius: RogPalette.border-radius;
border-color: Palette.accent-background; border-width: 1px;
background: Palette.background; border-color: RogPalette.accent;
background: RogPalette.control-background;
Dialog { Dialog {
Text { Text {
max-width: 420px; max-width: 420px;
font-size: 16px; font-size: 16px;
wrap: TextWrap.word-wrap; wrap: TextWrap.word-wrap;
text: @tr("confirm_reset" => "Are you sure you want to reset this?"); text: @tr("confirm_reset" => "Are you sure you want to reset this?");
color: RogPalette.text-primary;
} }
StandardButton { StandardButton {
@@ -164,7 +170,7 @@ export component SystemToggle inherits RogItem {
Text { Text {
font-size: 16px; font-size: 16px;
vertical-alignment: TextVerticalAlignment.center; vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground; color: RogPalette.text-primary;
text: root.text; text: root.text;
} }
} }
@@ -195,7 +201,7 @@ export component SystemToggleInt inherits RogItem {
Text { Text {
font-size: 16px; font-size: 16px;
vertical-alignment: TextVerticalAlignment.center; vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground; color: RogPalette.text-primary;
text: root.text; text: root.text;
} }
} }
@@ -226,7 +232,7 @@ export component SystemToggleVert inherits RogItem {
font-size: 16px; font-size: 16px;
vertical-alignment: TextVerticalAlignment.bottom; vertical-alignment: TextVerticalAlignment.bottom;
horizontal-alignment: TextHorizontalAlignment.center; horizontal-alignment: TextHorizontalAlignment.center;
color: Palette.control-foreground; color: RogPalette.text-primary;
text: root.text; text: root.text;
} }
@@ -256,7 +262,7 @@ export component SystemDropdown inherits RogItem {
Text { Text {
font-size: 16px; font-size: 16px;
vertical-alignment: TextVerticalAlignment.center; vertical-alignment: TextVerticalAlignment.center;
color: Palette.control-foreground; color: RogPalette.text-primary;
text: root.text; text: root.text;
} }
} }
@@ -288,9 +294,9 @@ export component PopupNotification {
height: root.height; height: root.height;
// TODO: add properties to display // TODO: add properties to display
Rectangle { Rectangle {
border-width: 2px; border-width: 1px;
border-color: Palette.accent-background; border-color: RogPalette.accent;
background: Palette.background; background: RogPalette.background;
// TODO: drop shadows slow // TODO: drop shadows slow
// drop-shadow-offset-x: 7px; // drop-shadow-offset-x: 7px;
// drop-shadow-offset-y: 7px; // drop-shadow-offset-y: 7px;
@@ -302,14 +308,14 @@ export component PopupNotification {
alignment: start; alignment: start;
Text { Text {
text: heading; text: heading;
color: Palette.control-foreground; color: RogPalette.text-primary;
font-size: 32px; font-size: 32px;
font-weight: 900; font-weight: 900;
} }
Text { Text {
text: content; text: content;
color: Palette.control-foreground; color: RogPalette.text-secondary;
font-size: 18px; font-size: 18px;
} }
} }

View File

@@ -1,4 +1,5 @@
import { Palette } from "std-widgets.slint"; import { Palette } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
export struct Node { x: length, y: length} export struct Node { x: length, y: length}
@@ -44,7 +45,7 @@ export component Graph inherits Rectangle {
for n in 11: Path { for n in 11: Path {
viewbox-width: self.width / 1px; viewbox-width: self.width / 1px;
viewbox-height: self.height / 1px; viewbox-height: self.height / 1px;
stroke: Palette.alternate-foreground.darker(200%); stroke: RogPalette.control-border;
stroke-width: 1px; stroke-width: 1px;
MoveTo { MoveTo {
x: scale_x_to_graph(n * 10px) / 1px; x: scale_x_to_graph(n * 10px) / 1px;
@@ -60,7 +61,7 @@ export component Graph inherits Rectangle {
} }
for n in 11: Text { for n in 11: Text {
color: Palette.accent-background; color: RogPalette.text-secondary;
font-size <=> root.axis_font_size; font-size <=> root.axis_font_size;
text: "\{n * 10}c"; text: "\{n * 10}c";
x: scale_x_to_graph(n * 10px) - self.width / 3; x: scale_x_to_graph(n * 10px) - self.width / 3;
@@ -70,7 +71,7 @@ export component Graph inherits Rectangle {
for n in 11: Path { for n in 11: Path {
viewbox-width: self.width / 1px; viewbox-width: self.width / 1px;
viewbox-height: self.height / 1px; viewbox-height: self.height / 1px;
stroke: Palette.alternate-foreground.darker(200%); stroke: RogPalette.control-border;
stroke-width: 1px; stroke-width: 1px;
MoveTo { MoveTo {
x: 0; x: 0;
@@ -86,7 +87,7 @@ export component Graph inherits Rectangle {
} }
for n in 11: Text { for n in 11: Text {
color: Palette.accent-background; color: RogPalette.text-secondary;
font-size <=> root.axis_font_size; font-size <=> root.axis_font_size;
text: "\{n * 10}%"; text: "\{n * 10}%";
x: - self.width; x: - self.width;
@@ -97,7 +98,7 @@ export component Graph inherits Rectangle {
if idx + 1 != nodes.length: Path { if idx + 1 != nodes.length: Path {
viewbox-width: self.width / 1px; viewbox-width: self.width / 1px;
viewbox-height: self.height / 1px; viewbox-height: self.height / 1px;
stroke: Palette.control-foreground; stroke: RogPalette.accent;
stroke-width: 2px; stroke-width: 2px;
MoveTo { MoveTo {
x: scale_x_to_graph(nodes[idx].x) / 1px; x: scale_x_to_graph(nodes[idx].x) / 1px;
@@ -114,19 +115,19 @@ export component Graph inherits Rectangle {
for n[idx] in nodes: Rectangle { for n[idx] in nodes: Rectangle {
states [ states [
pressed when touch.pressed: { pressed when touch.pressed: {
point.background: Palette.selection-background; point.background: RogPalette.accent;
tip.background: Palette.selection-background; tip.background: RogPalette.accent;
tip.opacity: 1.0; tip.opacity: 1.0;
} }
hover when touch.has-hover: { hover when touch.has-hover: {
point.background: Palette.accent-background; point.background: RogPalette.accent;
tip.background: Palette.accent-background; tip.background: RogPalette.accent;
tip.opacity: 1.0; tip.opacity: 1.0;
} }
] ]
// //
point := Rectangle { point := Rectangle {
background: Palette.control-foreground; background: RogPalette.text-primary;
x: scale_x_to_graph(n.x) - self.width / 2; x: scale_x_to_graph(n.x) - self.width / 2;
y: graph.height - scale_y_to_graph(n.y) - self.height / 2; y: graph.height - scale_y_to_graph(n.y) - self.height / 2;
width: 18px; width: 18px;
@@ -142,10 +143,14 @@ export component Graph inherits Rectangle {
} else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < nodes[idx - 1].x { } else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < nodes[idx - 1].x {
n.x = nodes[idx - 1].x + pad; n.x = nodes[idx - 1].x + pad;
} }
// Y-Axis: Monotonic Non-Decreasing
if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) > nodes[idx + 1].y { if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) > nodes[idx + 1].y {
n.y = nodes[idx + 1].y - pad; n.y = nodes[idx + 1].y; // Allow equality
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < nodes[idx - 1].y { } else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < nodes[idx - 1].y {
n.y = nodes[idx - 1].y + pad; n.y = nodes[idx - 1].y; // Allow equality
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < 0.0 {
n.y = 0px;
} }
} else if idx == 0 { } else if idx == 0 {
if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < 0.0 { if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < 0.0 {
@@ -153,10 +158,12 @@ export component Graph inherits Rectangle {
} else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) > nodes[idx + 1].x { } else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) > nodes[idx + 1].x {
n.x = nodes[idx + 1].x - pad; n.x = nodes[idx + 1].x - pad;
} }
// Y-Axis: <= Next Point
if n.y - scale_y_to_node(self.mouse-y - self.pressed-y) < 0.0 { if n.y - scale_y_to_node(self.mouse-y - self.pressed-y) < 0.0 {
n.y = 1px; n.y = 0px; // Allow 0 RPM
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) > nodes[idx + 1].y { } else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) > nodes[idx + 1].y {
n.y = nodes[idx + 1].y - pad; n.y = nodes[idx + 1].y; // Allow equality
} }
} else if idx == nodes.length - 1 { } else if idx == nodes.length - 1 {
if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) > scale_x_to_node(graph.width) { if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) > scale_x_to_node(graph.width) {
@@ -164,10 +171,14 @@ export component Graph inherits Rectangle {
} else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < nodes[idx - 1].x { } else if n.x + scale_x_to_node(self.mouse-x - self.pressed-x) < nodes[idx - 1].x {
n.x = nodes[idx - 1].x + pad; n.x = nodes[idx - 1].x + pad;
} }
// Y-Axis: >= Previous Point
if n.y - scale_y_to_node(self.mouse-y - self.pressed-y) > scale_y_to_node(graph.height) { if n.y - scale_y_to_node(self.mouse-y - self.pressed-y) > scale_y_to_node(graph.height) {
n.y = scale_y_to_node(graph.height - 1px); n.y = scale_y_to_node(graph.height);
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < nodes[idx - 1].y { } else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < nodes[idx - 1].y {
n.y = nodes[idx - 1].y + pad; n.y = nodes[idx - 1].y; // Allow equality
} else if n.y + scale_y_to_node(self.height - self.mouse-y - self.pressed-y) < 0.0 {
n.y = 0px;
} }
} }
} }
@@ -189,7 +200,7 @@ export component Graph inherits Rectangle {
} }
tip := Rectangle { tip := Rectangle {
background: Palette.control-foreground; background: RogPalette.control-background;
opacity: 0.3; opacity: 0.3;
x: final_x_pos(); x: final_x_pos();
y: final_y_pos(); y: final_y_pos();
@@ -224,7 +235,7 @@ export component Graph inherits Rectangle {
} }
// //
label := Text { label := Text {
color: Palette.accent-foreground; color: RogPalette.text-primary;
font-size: 16px; font-size: 16px;
text: "\{Math.floor(n.x / 1px)}c, \{fan_pct()}%"; text: "\{Math.floor(n.x / 1px)}c, \{fan_pct()}%";
} }

View File

@@ -1,7 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { Palette, HorizontalBox, VerticalBox } from "std-widgets.slint"; import { HorizontalBox, VerticalBox } from "std-widgets.slint";
import { RogPalette } from "../themes/rog_theme.slint";
component SideBarItem inherits Rectangle { component SideBarItem inherits Rectangle {
// padding only has effect on layout elements // padding only has effect on layout elements
@@ -28,12 +29,21 @@ component SideBarItem inherits Rectangle {
] ]
state := Rectangle { state := Rectangle {
opacity: 0; opacity: 0;
border-width: 2px; border-width: 0px; // Modern look: no full border, maybe just a left bar?
border-radius: 10px; // Or keep the ROG style border
border-color: Palette.accent-background; border-color: RogPalette.accent;
background: Palette.alternate-background; background: root.selected ? RogPalette.control-background : RogPalette.alternate-background;
// Add a red indicator line on the left for selected items
Rectangle {
x: 0;
width: 4px;
height: 100%;
background: root.selected ? RogPalette.accent : Colors.transparent;
}
animate opacity { duration: 150ms; } animate opacity { duration: 150ms; }
animate border-width { duration: 150ms; } // animate border-width { duration: 150ms; }
height: l.preferred-height; height: l.preferred-height;
} }
@@ -41,9 +51,10 @@ component SideBarItem inherits Rectangle {
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
spacing: 0px; spacing: 0px;
label := Text { label := Text {
color: Palette.foreground; color: root.selected ? RogPalette.accent : RogPalette.text-primary;
vertical-alignment: center; vertical-alignment: center;
font-size: 14px; font-size: 14px;
font-weight: root.selected ? 700 : 400;
} }
} }
@@ -66,10 +77,10 @@ export component SideBar inherits Rectangle {
accessible-role: tab; accessible-role: tab;
accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current-item; accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current-item;
Rectangle { Rectangle {
border-width: 2px; border-width: 0px;
border-color: Palette.accent-background; // border-color: RogPalette.accent;
border-radius: 0px; border-radius: 0px;
background: Palette.background.darker(0.2); background: RogPalette.alternate-background; // Darker sidebar
fs := FocusScope { fs := FocusScope {
key-pressed(event) => { key-pressed(event) => {
if (event.text == "\n") { if (event.text == "\n") {
@@ -104,12 +115,19 @@ export component SideBar inherits Rectangle {
spacing: 4px; spacing: 4px;
alignment: start; alignment: start;
label := Text { label := Text {
font-size: 16px; font-size: 24px; // Larger brand text
font-weight: 800;
horizontal-alignment: center; horizontal-alignment: center;
color: RogPalette.accent; // ROG Red brand text
}
// Spacer after brand text
Rectangle {
height: 20px;
} }
navigation := VerticalLayout { navigation := VerticalLayout {
spacing: -6px; spacing: 4px; // Spacing between items
alignment: start; alignment: start;
vertical-stretch: 0; vertical-stretch: 0;
for item[index] in root.model: SideBarItem { for item[index] in root.model: SideBarItem {

View File

@@ -0,0 +1,58 @@
import { RogPalette } from "../themes/rog_theme.slint";
export global SystemStatus {
in property <int> cpu_temp: 0;
in property <int> gpu_temp: 0;
in property <int> cpu_fan: 0;
in property <int> gpu_fan: 0;
in property <string> power_w: "--";
in property <string> power_avg_w: "--";
}
component StatusItem inherits Rectangle {
in property <string> label;
in property <string> value;
in property <string> unit;
HorizontalLayout {
spacing: 5px;
Text { text: label; color: RogPalette.text-secondary; font-weight: 700; vertical-alignment: center; }
Text { text: value; color: RogPalette.text-primary; vertical-alignment: center; }
Text { text: unit; color: RogPalette.text-secondary; font-size: 12px; vertical-alignment: center; }
}
}
export component StatusBar inherits Rectangle {
background: RogPalette.control-background;
height: 30px;
// Simulated top border
Rectangle {
y: 0px;
x: 0px;
width: parent.width;
height: 1px;
background: RogPalette.control-border;
}
HorizontalLayout {
padding-left: 20px;
padding-right: 20px;
spacing: 20px;
alignment: space-between;
HorizontalLayout {
spacing: 20px;
StatusItem { label: "CPU"; value: SystemStatus.cpu_temp; unit: "°C"; }
StatusItem { label: "GPU"; value: SystemStatus.gpu_temp; unit: "°C"; }
}
HorizontalLayout {
spacing: 20px;
StatusItem { label: "PWR"; value: SystemStatus.power_w; unit: "W"; }
StatusItem { label: "AVG"; value: SystemStatus.power_avg_w; unit: "W"; }
StatusItem { label: "CPU Fan"; value: SystemStatus.cpu_fan; unit: "RPM"; }
StatusItem { label: "GPU Fan"; value: SystemStatus.gpu_fan; unit: "RPM"; }
}
}
}

View File

@@ -0,0 +1,76 @@
import { RogPalette } from "../themes/rog_theme.slint";
import { SystemStatus } from "../widgets/status_bar.slint";
component StatusItem inherits Rectangle {
in property <string> label;
in property <string> value;
in property <string> unit;
HorizontalLayout {
spacing: 8px;
Text {
text: label;
color: RogPalette.text-secondary;
font-weight: 700;
vertical-alignment: center;
font-size: 13px;
}
Text {
text: value;
color: RogPalette.text-primary;
vertical-alignment: center;
font-size: 14px;
}
Text {
text: unit;
color: RogPalette.text-secondary;
font-size: 11px;
vertical-alignment: center;
}
}
}
export component TrayTooltip inherits Window {
always-on-top: true;
no-frame: true;
background: transparent;
width: 280px;
height: 160px;
Rectangle {
background: RogPalette.control-background;
border-radius: 8px;
border-width: 1px;
border-color: RogPalette.control-border;
drop-shadow-blur: 10px;
drop-shadow-color: rgba(0,0,0,0.5);
VerticalLayout {
padding: 15px;
spacing: 12px;
Text {
text: "System Statistics";
color: RogPalette.accent;
font-size: 16px;
font-weight: 800;
}
GridLayout {
spacing: 15px;
Row {
StatusItem { label: "CPU"; value: SystemStatus.cpu_temp; unit: "°C"; }
StatusItem { label: "GPU"; value: SystemStatus.gpu_temp; unit: "°C"; }
}
Row {
StatusItem { label: "FAN"; value: SystemStatus.cpu_fan; unit: "RPM"; }
StatusItem { label: "GPU"; value: SystemStatus.gpu_fan; unit: "RPM"; }
}
Row {
StatusItem { label: "PWR"; value: SystemStatus.power_w; unit: "W"; }
StatusItem { label: "AVG"; value: SystemStatus.power_avg_w; unit: "W"; }
}
}
}
}
}

View File

@@ -1 +0,0 @@
stable