01 / PROBLEM
Problem#
Multiple desktop shells can draw panels at the same time and compete for the notification service. When stopping the old service and starting the new one by hand, you also need to check their states. A command returning is not enough.
02 / STRUCTURE
Structure#
The program reads a mapping of names to systemd user services from TOML and identifies the session through environment variables. If the target is already the only active service, it only attempts to update the saved selection. Otherwise, it stops all registered services, checks that none still reports active, starts the target, waits for active, and finally attempts to save the selection. The boot entry point chooses a valid saved selection or the default service and issues a start command, without the same stop-and-poll sequence.
// 确认真的起来了(systemd 可能还在 activating)
let deadline = Instant::now() + START_TIMEOUT;
loop {
if is_active(&target_entry.service) {
println!("{}", t!("switched", target = target));
write_current(target);
return ExitCode::SUCCESS;
}
if Instant::now() >= deadline {
eprintln!("{}", t!("start_timeout", target = target));
return ExitCode::from(1);
}
sleep(Duration::from_millis(200));
}After the command returns, keep checking for active. The timeout branch only returns an error.
03 / TRADEOFF
Trade-offs#
systemd manages the processes; the Rust program handles sequencing and state checks. Stopping before starting reduces deliberate overlap, at the cost of a brief gap in the interface. Only Hyprland and niri are currently recognized, and session checks and fixed timeouts further limit where the tool applies.
04 / RESULT
Outcome#
The source provides entry points for state queries, idempotent switching, and startup from a saved selection. If stop confirmation times out or the command to start the target fails, the program attempts to start the default service and returns an error, without checking whether recovery succeeded. If waiting for the target to become active times out, it returns an error directly, without that recovery attempt.
05 / SOURCE
Source & evidence#
These notes refer to revision 1668dbdc42. They do not establish results for every runtime environment.
- README: use cases and switching flow
- main.rs: state polling and failure branches
- The state check tests whether stdout from systemctl --user is-active equals active. A failed query also returns false.
- Errors when creating the selection directory or writing its file are ignored. A successful exit does not guarantee that the selection was saved to disk.
- boot chooses a valid saved selection, or otherwise the default service, then calls start directly. It neither stops all services nor polls for active.