This commit is contained in:
Your Name
2026-03-15 13:59:06 +01:00
parent a6eb991dfc
commit 055ba54853
6 changed files with 539 additions and 58 deletions

369
Cargo.lock generated
View File

@@ -32,6 +32,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"config",
"ctrlc",
"grim-rs",
"image",
"image-compare",
@@ -42,6 +43,7 @@ dependencies = [
"serde",
"serde_json",
"tokio",
"tray-item",
]
[[package]]
@@ -53,6 +55,15 @@ dependencies = [
"libc",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.102"
@@ -114,6 +125,17 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.5.0"
@@ -221,6 +243,12 @@ dependencies = [
"core2",
]
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -230,6 +258,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block2"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
dependencies = [
"objc2",
]
[[package]]
name = "built"
version = "0.8.0"
@@ -303,6 +340,21 @@ dependencies = [
"windows-link",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "cmake"
version = "0.1.57"
@@ -312,6 +364,36 @@ dependencies = [
"cc",
]
[[package]]
name = "cocoa"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c"
dependencies = [
"bitflags 1.3.2",
"block",
"cocoa-foundation",
"core-foundation 0.9.4",
"core-graphics",
"foreign-types",
"libc",
"objc",
]
[[package]]
name = "cocoa-foundation"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
dependencies = [
"bitflags 1.3.2",
"block",
"core-foundation 0.9.4",
"core-graphics-types",
"libc",
"objc",
]
[[package]]
name = "color_quant"
version = "1.1.0"
@@ -403,6 +485,30 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core-graphics"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"core-graphics-types",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics-types"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"libc",
]
[[package]]
name = "core2"
version = "0.4.0"
@@ -471,6 +577,48 @@ dependencies = [
"typenum",
]
[[package]]
name = "ctrlc"
version = "3.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162"
dependencies = [
"dispatch2",
"nix",
"windows-sys 0.61.2",
]
[[package]]
name = "dbus"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4"
dependencies = [
"libc",
"libdbus-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "dbus-codegen"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a49da9fdfbe872d4841d56605dc42efa5e6ca3291299b87f44e1cde91a28617c"
dependencies = [
"clap",
"dbus",
"xml-rs",
]
[[package]]
name = "dbus-tree"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f456e698ae8e54575e19ddb1f9b7bce2298568524f215496b248eb9498b4f508"
dependencies = [
"dbus",
]
[[package]]
name = "digest"
version = "0.10.7"
@@ -481,6 +629,18 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "dispatch2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
dependencies = [
"bitflags 2.11.0",
"block2",
"libc",
"objc2",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
@@ -659,6 +819,33 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foreign-types"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared",
]
[[package]]
name = "foreign-types-macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@@ -845,6 +1032,15 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "http"
version = "1.4.0"
@@ -1236,6 +1432,18 @@ dependencies = [
"serde",
]
[[package]]
name = "ksni"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4934310bdd016e55725482b8d35ac0c16fd058c1b955d8959aa2d953b918c85b"
dependencies = [
"dbus",
"dbus-codegen",
"dbus-tree",
"thiserror 1.0.69",
]
[[package]]
name = "lebe"
version = "0.5.3"
@@ -1248,6 +1456,15 @@ version = "0.2.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
[[package]]
name = "libdbus-sys"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043"
dependencies = [
"pkg-config",
]
[[package]]
name = "libfuzzer-sys"
version = "0.4.12"
@@ -1300,6 +1517,15 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "maybe-rayon"
version = "0.1.1"
@@ -1368,6 +1594,18 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nix"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "nom"
version = "8.0.0"
@@ -1433,6 +1671,50 @@ dependencies = [
"autocfg",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc2"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
dependencies = [
"objc2-encode",
]
[[package]]
name = "objc2-encode"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]]
name = "oklab"
version = "1.1.2"
@@ -1479,6 +1761,12 @@ dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "padlock"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c10569378a1dacd9f30dbe7ae49e054d2c45dc2f8ee49899903e09c3924e8b6f"
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -2281,6 +2569,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "subtle"
version = "2.6.1"
@@ -2352,6 +2646,15 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.69"
@@ -2586,6 +2889,23 @@ dependencies = [
"once_cell",
]
[[package]]
name = "tray-item"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59d4bd406170690dc30eabb3badc67a085beaf9b2c3b1923afcc9c26a2191353"
dependencies = [
"cocoa",
"core-graphics",
"ksni",
"libc",
"objc",
"objc-foundation",
"objc_id",
"padlock",
"windows-sys 0.52.0",
]
[[package]]
name = "try-lock"
version = "0.2.5"
@@ -2622,6 +2942,12 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -2657,6 +2983,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.5"
@@ -2861,6 +3193,22 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.11"
@@ -2870,6 +3218,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -2958,6 +3312,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
@@ -3183,6 +3546,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]]
name = "xml-rs"
version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]]
name = "y4m"
version = "0.8.0"

View File

@@ -6,8 +6,9 @@ edition = "2021"
[dependencies]
anyhow = "1.0"
config = "0.15.21"
ctrlc = "3.5.2"
grim-rs = "0.1.6"
image = { version = "0.25.10", default-features = false }
image = "0.25.10"
image-compare = "0.5.0"
libc = "0.2.183"
okmain = "0.2.0"
@@ -16,3 +17,4 @@ rgb = "0.8"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
tokio = { version = "1.50.0", features = ["full"] }
tray-item = { version = "0.10.0", features = ["ksni"] }

View File

@@ -27,4 +27,5 @@ restore_on_exit = true
transition = 0.1
# Minimum percentage of difference required between frames to trigger an update (e.g. 1.0 = 1%)
# Helps with flickering on static images, from the non deterministic color algorithm.
min_diff_percent = 5.0

View File

@@ -30,8 +30,20 @@
rustfmt
clippy
rustPlatform.bindgenHook
dbus
gtk3
glib
atk
pango
cairo
gdk-pixbuf
xdotool
libappindicator-gtk3
libayatana-appindicator
];
shellHook = ''
export LD_LIBRARY_PATH="${pkgs.libappindicator-gtk3}/lib:${pkgs.libayatana-appindicator}/lib:$LD_LIBRARY_PATH"
'';
};
}
);

View File

@@ -1,15 +1,23 @@
use crate::homeassistant::HaClient;
use crate::screenshot;
use crate::settings::Settings;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::mpsc;
use tokio::time::sleep;
pub async fn run_loop(settings: &Settings, ha_client: &HaClient, target_lights: &[String]) {
pub async fn run_loop(
settings: &Settings,
ha_client: &HaClient,
target_lights: &[String],
is_running: Arc<AtomicBool>,
mut exit_rx: mpsc::Receiver<()>,
) {
let fps = settings.target_fps.max(1);
let target_duration = Duration::from_millis((1000 / fps) as u64);
let mut current_color: Option<(f32, f32, f32)> = None;
let mut exit_signal = std::pin::pin!(tokio::signal::ctrl_c());
let mut last_screenshot: Option<screenshot::Screenshot> = None;
println!("Starting Ambilight loop at {} FPS...", fps);
@@ -17,54 +25,57 @@ pub async fn run_loop(settings: &Settings, ha_client: &HaClient, target_lights:
loop {
let start_time = Instant::now();
match screenshot::get_screenshot(0) {
Ok(current_screenshot) => {
let mut should_update = true;
if is_running.load(Ordering::Relaxed) {
match screenshot::get_screenshot(0) {
Ok(current_screenshot) => {
let mut should_update = true;
if let Some(prev) = &last_screenshot {
let diff = current_screenshot.diff_percent(prev);
if diff < settings.min_diff_percent {
should_update = false;
}
}
if should_update {
let color = current_screenshot.dominant_color();
let (new_r, new_g, new_b) = (color.r as f32, color.g as f32, color.b as f32);
let (r, g, b) = match current_color {
None => (new_r, new_g, new_b),
Some((cr, cg, cb)) => {
let s_factor = settings.smoothing.clamp(0.0, 1.0);
(
cr * s_factor + new_r * (1.0 - s_factor),
cg * s_factor + new_g * (1.0 - s_factor),
cb * s_factor + new_b * (1.0 - s_factor),
)
if let Some(prev) = &last_screenshot {
let diff = current_screenshot.diff_percent(prev);
if diff < settings.min_diff_percent {
should_update = false;
}
};
current_color = Some((r, g, b));
let (final_r, final_g, final_b) =
(r.round() as u8, g.round() as u8, b.round() as u8);
if let Err(e) = ha_client
.set_lights_color(
target_lights,
final_r,
final_g,
final_b,
settings.transition,
)
.await
{
eprintln!("Failed to update lights: {}", e);
}
last_screenshot = Some(current_screenshot);
if should_update {
let color = current_screenshot.dominant_color();
let (new_r, new_g, new_b) =
(color.r as f32, color.g as f32, color.b as f32);
let (r, g, b) = match current_color {
None => (new_r, new_g, new_b),
Some((cr, cg, cb)) => {
let s_factor = settings.smoothing.clamp(0.0, 1.0);
(
cr * s_factor + new_r * (1.0 - s_factor),
cg * s_factor + new_g * (1.0 - s_factor),
cb * s_factor + new_b * (1.0 - s_factor),
)
}
};
current_color = Some((r, g, b));
let (final_r, final_g, final_b) =
(r.round() as u8, g.round() as u8, b.round() as u8);
if let Err(e) = ha_client
.set_lights_color(
target_lights,
final_r,
final_g,
final_b,
settings.transition,
)
.await
{
eprintln!("Failed to update lights: {}", e);
}
last_screenshot = Some(current_screenshot);
}
}
Err(e) => eprintln!("Screenshot error: {}", e),
}
Err(e) => eprintln!("Screenshot error: {}", e),
}
// Sleep to maintain target FPS, saturating_sub prevents negative duration panics if we run slow
@@ -73,7 +84,7 @@ pub async fn run_loop(settings: &Settings, ha_client: &HaClient, target_lights:
tokio::select! {
_ = sleep(sleep_duration) => {}
_ = &mut exit_signal => {
_ = exit_rx.recv() => {
println!("\nExit signal received, stopping loop...");
break;
}

View File

@@ -3,8 +3,14 @@ mod homeassistant;
mod screenshot;
mod settings;
#[tokio::main]
async fn main() {
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tray_item::{IconSource, TrayItem};
fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
let rt_handle = rt.handle().clone();
let settings = settings::Settings::new().unwrap_or_else(|err| {
eprintln!("Error loading configuration: {}", err);
std::process::exit(1);
@@ -16,7 +22,7 @@ async fn main() {
settings.lights.clone()
} else {
println!("Fetching color-supported lights from Home Assistant...");
match ha_client.get_color_lights().await {
match rt.block_on(ha_client.get_color_lights()) {
Ok(lights) => lights.into_iter().map(|l| l.entity_id).collect(),
Err(e) => {
eprintln!("Failed to fetch color lights: {}", e);
@@ -32,24 +38,104 @@ async fn main() {
if settings.restore_on_exit {
println!("Creating scene snapshot for restore on exit...");
if let Err(e) = ha_client
.create_scene_snapshot("ambilight_restore", &target_lights)
.await
if let Err(e) =
rt.block_on(ha_client.create_scene_snapshot("ambilight_restore", &target_lights))
{
eprintln!("Failed to create scene snapshot: {}", e);
}
}
// Run the main application event loop
eventloop::run_loop(&settings, &ha_client, &target_lights).await;
let is_running = Arc::new(AtomicBool::new(true));
let (exit_tx, exit_rx) = tokio::sync::mpsc::channel(1);
// The event loop returns when a shutdown signal (like Ctrl+C) is received
if settings.restore_on_exit {
// Leak variables to give them a 'static lifetime for the thread
let settings_ref = &*Box::leak(Box::new(settings));
let ha_client_ref = &*Box::leak(Box::new(ha_client));
let target_lights_ref = &*Box::leak(Box::new(target_lights));
let is_running_clone = is_running.clone();
// Spawn the main application event loop on a separate thread
let app_thread = std::thread::spawn(move || {
let rt2 = tokio::runtime::Runtime::new().unwrap();
rt2.block_on(eventloop::run_loop(
settings_ref,
ha_client_ref,
target_lights_ref,
is_running_clone,
exit_rx,
));
});
// Create the tray application
let mut tray = TrayItem::new("Ambiligth", IconSource::Resource("")).unwrap();
let is_running_toggle = is_running.clone();
let restore_on_toggle = settings_ref.restore_on_exit;
let ha_client_toggle = ha_client_ref;
let target_lights_toggle = target_lights_ref;
let rt_handle_toggle = rt_handle.clone();
tray.add_menu_item("Toggle Ambilight", move || {
let current = is_running_toggle.load(Ordering::Relaxed);
is_running_toggle.store(!current, Ordering::Relaxed);
if current {
println!("Ambilight disabled.");
if restore_on_toggle {
println!("Restoring lights to original state...");
if let Err(e) = rt_handle_toggle
.block_on(ha_client_toggle.turn_on_scene("scene.ambilight_restore"))
{
eprintln!("Failed to restore scene: {}", e);
} else {
println!("Lights restored successfully.");
}
}
} else {
println!("Ambilight enabled.");
if restore_on_toggle {
println!("Creating scene snapshot for restore on exit...");
if let Err(e) = rt_handle_toggle.block_on(
ha_client_toggle
.create_scene_snapshot("ambilight_restore", target_lights_toggle),
) {
eprintln!("Failed to create scene snapshot: {}", e);
}
}
}
})
.unwrap();
let (sync_exit_tx, sync_exit_rx) = std::sync::mpsc::channel();
let sync_exit_tx_clone = sync_exit_tx.clone();
tray.add_menu_item("Quit", move || {
let _ = sync_exit_tx_clone.send(());
})
.unwrap();
// Ctrl+C handling to cleanly exit
ctrlc::set_handler(move || {
let _ = sync_exit_tx.send(());
})
.expect("Error setting Ctrl-C handler");
// Block the main thread until a quit signal is received
let _ = sync_exit_rx.recv();
println!("Shutting down...");
// Stop the event loop
let _ = exit_tx.try_send(());
if settings_ref.restore_on_exit {
println!("Restoring lights to original state...");
if let Err(e) = ha_client.turn_on_scene("scene.ambilight_restore").await {
if let Err(e) = rt.block_on(ha_client_ref.turn_on_scene("scene.ambilight_restore")) {
eprintln!("Failed to restore scene: {}", e);
} else {
println!("Lights restored successfully.");
}
}
let _ = app_thread.join();
}