Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions bin/omarchy-launch-screensaver
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ wait_for_screensaver_window() {
done
}

for m in $(hyprctl monitors -j | jq -r '.[] | .name'); do
# Spawn on every monitor, saving the focused one for last: the last window to
# map keeps focus, and omarchy-screensaver exits whenever it is not the active
# window, so focus has to end on a screensaver terminal. Restoring focus to
# the previously focused window instead kills the freshly launched screensaver
# about a second after it opens, leaving a windowed flash instead of a
# screensaver.
for m in $(hyprctl monitors -j | jq -r --arg focused "$focused" 'sort_by(.name == $focused) | .[].name'); do
hypr_focus_monitor "$m"

case $terminal in
Expand All @@ -69,5 +75,3 @@ for m in $(hyprctl monitors -j | jq -r '.[] | .name'); do

wait_for_screensaver_window
done

hypr_focus_monitor "$focused"
11 changes: 10 additions & 1 deletion shell/plugins/services/idle/IdleModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,19 @@ function screensaverWindowsAfter(windows, address, visible) {
}
}

function openPanelIdsToClose(openPanelIds) {
var ids = []
for (var id in openPanelIds || {}) {
if (openPanelIds[id]) ids.push(id)
}
return ids
}

if (typeof module !== "undefined") {
module.exports = {
secondsFromConfig: secondsFromConfig,
eventParts: eventParts,
screensaverWindowsAfter: screensaverWindowsAfter
screensaverWindowsAfter: screensaverWindowsAfter,
openPanelIdsToClose: openPanelIdsToClose
}
}
23 changes: 23 additions & 0 deletions shell/plugins/services/idle/Service.qml
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,36 @@ Item {
runProcess(lockProcess, "lock", "omarchy-system-lock")
}

// An open bar popout (battery, audio, network menu, ...) or summoned panel
// (menu, emojis, clipboard, ...) is a keyboard-focused overlay layer that
// keeps drawing on top of the fullscreen screensaver. Close them before the
// idle cycle takes over the screen.
function closeOpenPanels() {
if (!shell) return

if (shell.bar && shell.bar.activePopout) {
var popout = shell.bar.activePopout
if (typeof popout.close === "function") {
logEvent("close-open-panel", "bar popout")
popout.close()
}
}

var ids = IdleModel.openPanelIdsToClose(shell.openPanelIds)
for (var i = 0; i < ids.length; i++) {
logEvent("close-open-panel", ids[i])
shell.hide(ids[i])
}
}

function startIdleCycle() {
if (root.idledThisCycle) {
logEvent("idle-cycle-already-running")
return
}

logEvent("idle-cycle-start", "screensaver=" + root.screensaverTimeoutSeconds + " lock=" + root.lockTimeoutSeconds)
closeOpenPanels()
root.idledThisCycle = true
root.screensaverStartedThisCycle = false
resetScreensaverWindows()
Expand Down
8 changes: 8 additions & 0 deletions test/shell.d/idle-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ assertDeepEqual(
{ windows: { a: true }, count: 1 },
'idle leaves screensaver windows unchanged without an address'
)

assertDeepEqual(
idle.openPanelIdsToClose({ 'omarchy.emojis': true, 'omarchy.menu': false, 'pablo.custom': true }),
['omarchy.emojis', 'pablo.custom'],
'idle lists only open panels to close'
)
assertDeepEqual(idle.openPanelIdsToClose(null), [], 'idle closes nothing without open panels')
assertDeepEqual(idle.openPanelIdsToClose({}), [], 'idle closes nothing with no open panels')
JS

test_tmp=$(mktemp -d)
Expand Down