* fix(screensaver): open in own special workspace instead of active - The screensaver window rule sets fullscreen, and a workspace holds only one fullscreen window - Launching it on the active workspace drops the fullscreen window already there, and leaves it windowed after the screensaver quits * Keep a showing special workspace open through the screensaver A monitor shows one special workspace at a time, so opening the screensaver on its own replaced a visible scratchpad, and it stayed hidden after the screensaver closed. Open it on the special workspace that is already showing instead; it covers that just as well, and the workspace is still there when it goes. The test's stubbed event stream follows the one in #10870. Co-Authored-By: Willem van Ede <37050539+WillemCR@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hand focus back after the screensaver closes Hyprland focuses a monitor when its special workspace empties, so on more than one monitor keyboard focus ended up wherever the last screensaver happened to close, rather than where it was. A detached reader on the launcher's event stream waits for the last one to go and focuses the original monitor again; the launcher itself still exits straight away, as the idle service expects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep a remapped screensaver off the regular workspaces The terminal occasionally maps its window again as it closes, after its special workspace is gone. The launcher's workspace only applies to the first map, so the window landed on the focused monitor's regular workspace, where its fullscreen rule took fullscreen from the window there. On Hyprland 0.56.2 with two monitors this happened in 4 of 45 cycles, each time losing the user's fullscreen. A class rule now sends any later map to a hidden special workspace; the launcher's exec rule still wins on the first map. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check for screensavers before waiting on their close The focus watcher only looked for remaining screensavers after reading a closewindow, so one that never mapped, or closed while the launcher was still waiting on another monitor, left it blocked until some unrelated window closed, and then it pulled focus back to the old monitor. It now checks before each wait, and matches the exact class, passed through the environment so the screensaver's pgrep and pkill never see it in argv. Co-Authored-By: Codex Medium <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: David Heinemeier Hansson <david@hey.com> Co-authored-by: Willem van Ede <37050539+WillemCR@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Codex Medium <noreply@openai.com>
87 lines
3.3 KiB
Bash
Executable File
87 lines
3.3 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Launch the Omarchy screensaver in the default terminal on the system with the correct font configuration.
|
|
|
|
# Exit early if screensaver is already running
|
|
pgrep -f '[o]rg.omarchy.screensaver' && exit 0
|
|
|
|
# Allow screensaver to be turned off but also force started
|
|
if omarchy-toggle-enabled screensaver-off && [[ $1 != "force" ]]; then
|
|
exit 1
|
|
fi
|
|
|
|
focused=$(omarchy-hyprland-monitor-focused)
|
|
terminal=$(xdg-terminal-exec --print-id)
|
|
|
|
case $terminal in
|
|
*Alacritty* | *ghostty* | *foot* | *kitty*) ;;
|
|
*)
|
|
omarchy-notification-send -g ✋ "Screensaver only runs in Alacritty, Foot, Ghostty, or Kitty"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
hypr_focus_monitor() {
|
|
hyprctl dispatch "hl.dsp.focus({ monitor = \"$1\" })" >/dev/null 2>&1 || hyprctl dispatch focusmonitor "$1" >/dev/null
|
|
}
|
|
|
|
hypr_exec() {
|
|
local command
|
|
printf -v command '%q ' "$@"
|
|
|
|
hyprctl dispatch "hl.dsp.exec_cmd([[[workspace $workspace] $command]])" >/dev/null 2>&1 ||
|
|
hyprctl dispatch exec -- "[workspace $workspace]" bash -lc "$command" >/dev/null
|
|
}
|
|
|
|
SOCKET="$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"
|
|
|
|
# Open Hyprland's event stream before spawning anything, so a terminal that maps
|
|
# quickly can't emit its openwindow event before we are listening for it.
|
|
exec {events}< <(socat -U - "UNIX-CONNECT:$SOCKET")
|
|
|
|
# hypr_exec is async and a new window maps on whatever monitor is focused at that
|
|
# moment. Block until this monitor's screensaver actually opens before moving
|
|
# focus on -- otherwise slow-starting terminals all pile onto the last monitor.
|
|
# The deadline is a safety net in case the window never appears.
|
|
wait_for_screensaver_window() {
|
|
local line deadline=$((SECONDS + 5))
|
|
while ((SECONDS < deadline)) && IFS= read -r -t $((deadline - SECONDS)) -u "$events" line; do
|
|
[[ $line == openwindow\>\>*,org.omarchy.screensaver,* ]] && return 0
|
|
done
|
|
}
|
|
|
|
while read -r m special; do
|
|
hypr_focus_monitor "$m"
|
|
# Share a special workspace that is already showing, like the scratchpad, so it is still open afterwards.
|
|
workspace=${special:-special:screensaver-$m}
|
|
|
|
case $terminal in
|
|
*Alacritty*)
|
|
hypr_exec alacritty --class=org.omarchy.screensaver --config-file "$OMARCHY_PATH/default/alacritty/screensaver.toml" -e omarchy-screensaver
|
|
;;
|
|
*ghostty*)
|
|
hypr_exec ghostty --class=org.omarchy.screensaver --config-file="$OMARCHY_PATH/default/ghostty/screensaver" --font-size=18 -e omarchy-screensaver
|
|
;;
|
|
*foot*)
|
|
hypr_exec foot --app-id=org.omarchy.screensaver --config="$OMARCHY_PATH/default/foot/screensaver.ini" -e omarchy-screensaver
|
|
;;
|
|
*kitty*)
|
|
hypr_exec kitty --class=org.omarchy.screensaver --override font_size=18 --override window_padding_width=0 -e omarchy-screensaver
|
|
;;
|
|
esac
|
|
|
|
wait_for_screensaver_window
|
|
done < <(hyprctl monitors -j | jq -r '.[] | "\(.name) \(.specialWorkspace.name)"')
|
|
|
|
hypr_focus_monitor "$focused"
|
|
|
|
# Emptying a special workspace focuses its monitor, so the last screensaver to close would take focus
|
|
# with it. Hand focus back to the monitor that had it once they are all gone.
|
|
# The class goes through the environment, since in argv the screensaver's pgrep and pkill would match it.
|
|
(
|
|
while hyprctl clients -j | SCREENSAVER=org.omarchy.screensaver jq -e 'any(.[]; .mapped and .class == env.SCREENSAVER)' >/dev/null; do
|
|
IFS= read -r -u "$events" line || exit
|
|
done
|
|
hypr_focus_monitor "$focused"
|
|
) >/dev/null 2>&1 &
|