Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.

Inbox
ReportR-5159GitHub issuePublicDecided· Out of scope

Screensaver quits ~1.4s after launch, opens windowed, and is drawn over, whenever a bar panel holds keyboard focus

Reported by crazybadger · filed Oct 5, 2026 · open at source

Decided: Out of scope — decided by agents · verified 3 days ago.

At a glance

1 · What is claimed?
Only the report text below — no validator has restated it as checkable claims yet.
2 · What does it affect?
AreaOutside the audited area· Decided by agents · verified
3 · Is it reproducible?
No validation has been run yet.
4 · What remains unclear?
  • Not validated yet: no claims, no reproducer.
5 · What happens next?
Decided by agents and verified by a second, independent agent — see the decision.

Original report

Screensaver quits ~1.4s after launch, opens windowed, and is drawn over, whenever a bar panel holds keyboard focusrevision 1 · captured 2026-10-05 16:41 UTC via poll

System details: AMD Ryzen 5 230 w/ Radeon 760M (HP EliteBook 6 G1a), omarchy 4.0.4-1, Hyprland 0.56.2, ttfx 0.3.2-1, Ghostty

How this report was produced. I chased this on my own machine with Claude Code, working from Omarchy's idle journal, hyprctl state captured while the screensaver was up, and a temporary one-line exit log added to my own copy of omarchy-screensaver. Every command is included so you can re-run it. It is machine-authored and I have reviewed it before filing.

What happens

Omarchy hosts a focused bar panel on a full-screen layer-shell surface called
omarchy-keyboard-panel. While one is open it holds keyboard focus, and being a layer it
is drawn above ordinary windows. If the screensaver fires in that state, three things go
wrong at once:

  1. It quits about 1.4s after launch.
  2. It opens in a floating, terminal-sized window instead of fullscreen.
  3. The panel is drawn over it, so even once fullscreen it isn't alone on screen.

All three come from the same condition, so they're in one report.

1. Quits ~1.4s after launch

bin/omarchy-screensaver:44 ends the session if the screensaver is not the active window:

# bin/omarchy-screensaver:5-7
screensaver_in_focus() {
  hyprctl activewindow -j | jq -e '.class == "org.omarchy.screensaver"' >/dev/null 2>&1
}

# bin/omarchy-screensaver:43-47
while pgrep -t "${tty#/dev/}" -x ttfx >/dev/null; do
  if read -n1 -t 1 || ! screensaver_in_focus; then
    exit_screensaver
  fi
done

When a panel holds focus, the screensaver window never becomes active, so the first pass
of that loop exits: roughly 0.4s of startup plus the 1s read timeout. The timing is
strikingly consistent because it is a fixed timeout, not user input.

From Omarchy's own idle log (idle-cycle-start to `idle-cycle-cancel:
screensaver-dismissed`), six consecutive sessions on my machine while a panel was open:

13:42  up 1.4s
13:57  up 1.4s
14:12  up 1.4s
14:27  up 1.4s
15:11  up 1.4s
15:26  up 1.4s

Normal sessions on the same machine, same day, with no panel open, run as long as you'd
expect — 18.6s, 43.9s, 271.4s — ending when I actually came back.

To confirm the cause I added one line to exit_screensaver in my own copy, logging why it
exited and what held focus:

15:11:47.998 exit why=focus focus=[tabby]
15:26:49.411 exit why=focus focus=[tabby]

why=focus is the ! screensaver_in_focus branch, and the window named is whatever had
focus when the panel opened — the panel does not itself appear as a window, so
activewindow simply reports the last ordinary window.

Reproduce without patching anything: open a bar panel, trigger the screensaver, and poll

hyprctl activewindow -j | jq -r '.class'

It never reports org.omarchy.screensaver.

2. Opens windowed, not fullscreen

The windowrule is in place:

-- default/hypr/apps/system.lua:35-37
o.window("org.omarchy.screensaver", { fullscreen = true })
o.window("org.omarchy.screensaver", { float = true })
o.window("org.omarchy.screensaver", { animation = "slide" })

but it does not land when another surface holds focus as the window maps. Measured with a
panel open, the screensaver window is floating at default terminal size. With no panel
open, the same check gives fullscreen=2 at the monitor origin and the full logical size:

hyprctl clients -j | jq -r '.[] | select(.class=="org.omarchy.screensaver")
  | "size=\(.size) at=\(.at) fullscreen=\(.fullscreen)"'
# panel open : floating, terminal-sized
# no panel   : size=[1280,800] at=[1536,937] fullscreen=2   (monitor origin, logical size)

3. The panel is drawn over the screensaver

Layer-shell surfaces render above ordinary windows, so an open panel stays visible on top
of the screensaver. Nothing in the screensaver path dismisses open bar panels. Detect it
with:

hyprctl layers -j | jq -e 'any(.[].levels | to_entries[] | .value[];
  .namespace | test("keyboard-panel"))'

Related: the resize guard is too narrow

Independent of the above, wait_for_terminal_resize only waits while the size is exactly
the pty default:

# bin/omarchy-screensaver:29-34
wait_for_terminal_resize() {
  local deadline=$((SECONDS + 2))
  while ((SECONDS < deadline)) && [[ $(stty size 2>/dev/null) == "24 80" ]]; do
    sleep 0.02
  done
}

Any intermediate size on the way to fullscreen falls straight through, ttfx measures early,
and you get a small canvas painted into the corner of a fullscreen window — exactly what the
comment above the function is trying to prevent. Waiting for the size to settle (the same
non-default value twice running) fixes it.

Steps to reproduce

  1. Open a bar panel that takes keyboard focus.
  2. Leave the machine idle until the screensaver fires (default 900s), or run

omarchy-launch-screensaver force.
3. The screensaver appears in a small floating window, with the panel on top, and is gone
again about 1.4s later.

I hit this with my own clock plugin's panel, but omarchy-keyboard-panel is Omarchy's own
surface rather than my plugin's, so any focused bar panel should do it — worth confirming
against a stock one.

Suggested fixes

  • Focus check: treat an absent or unparseable activewindow as still in focus, and only

let losing focus end the session once focus has actually been held. If the window
never gets focus, stay up and let input or Omarchy's own idle dismissal end it.
- Fullscreen: after mapping, if the screensaver window is still fullscreen == 0, focus
it by address and fullscreen it.
- Panels: dismiss open bar panels when the screensaver starts. Gating on the
keyboard-panel layer existing keeps the usual case to a single cheap check.
- Resize guard: wait for stty size to settle rather than only while it is 24 80.

Two traps if you implement the fullscreen fix on Hyprland 0.56. The legacy forms
hyprctl dispatch fullscreen 0 and hyprctl dispatch focuswindow class:... no longer parse
— they are read as Lua and fail — so it needs
hl.dsp.window.fullscreen({ mode = "fullscreen" }) and
hl.dsp.focus({ window = "address:0x..." }), with the 0x prefix hyprctl clients -j
already provides. And an invalid dispatch still answers ok, so the return value proves
nothing; check the effect instead. window.fullscreen acts on the active window, so it is
worth confirming focus landed before calling it.

I have all four changes running locally if a PR would be useful.

View raw text
**System details:** AMD Ryzen 5 230 w/ Radeon 760M (HP EliteBook 6 G1a), omarchy 4.0.4-1, Hyprland 0.56.2, ttfx 0.3.2-1, Ghostty

> **How this report was produced.** I chased this on my own machine with Claude Code,
> working from Omarchy's idle journal, `hyprctl` state captured while the screensaver was
> up, and a temporary one-line exit log added to my own copy of `omarchy-screensaver`.
> Every command is included so you can re-run it. It is machine-authored and I have
> reviewed it before filing.

## What happens

Omarchy hosts a focused bar panel on a full-screen layer-shell surface called
`omarchy-keyboard-panel`. While one is open it holds keyboard focus, and being a layer it
is drawn above ordinary windows. If the screensaver fires in that state, three things go
wrong at once:

1. **It quits about 1.4s after launch.**
2. **It opens in a floating, terminal-sized window** instead of fullscreen.
3. **The panel is drawn over it**, so even once fullscreen it isn't alone on screen.

All three come from the same condition, so they're in one report.

## 1. Quits ~1.4s after launch

`bin/omarchy-screensaver:44` ends the session if the screensaver is not the active window:

```bash
# bin/omarchy-screensaver:5-7
screensaver_in_focus() {
  hyprctl activewindow -j | jq -e '.class == "org.omarchy.screensaver"' >/dev/null 2>&1
}

# bin/omarchy-screensaver:43-47
while pgrep -t "${tty#/dev/}" -x ttfx >/dev/null; do
  if read -n1 -t 1 || ! screensaver_in_focus; then
    exit_screensaver
  fi
done
```

When a panel holds focus, the screensaver window never becomes active, so the *first* pass
of that loop exits: roughly 0.4s of startup plus the 1s `read` timeout. The timing is
strikingly consistent because it is a fixed timeout, not user input.

From Omarchy's own idle log (`idle-cycle-start` to `idle-cycle-cancel:
screensaver-dismissed`), six consecutive sessions on my machine while a panel was open:

```
13:42  up 1.4s
13:57  up 1.4s
14:12  up 1.4s
14:27  up 1.4s
15:11  up 1.4s
15:26  up 1.4s
```

Normal sessions on the same machine, same day, with no panel open, run as long as you'd
expect — 18.6s, 43.9s, 271.4s — ending when I actually came back.

To confirm the cause I added one line to `exit_screensaver` in my own copy, logging why it
exited and what held focus:

```
15:11:47.998 exit why=focus focus=[tabby]
15:26:49.411 exit why=focus focus=[tabby]
```

`why=focus` is the `! screensaver_in_focus` branch, and the window named is whatever had
focus when the panel opened — the panel does not itself appear as a window, so
`activewindow` simply reports the last ordinary window.

Reproduce without patching anything: open a bar panel, trigger the screensaver, and poll

```bash
hyprctl activewindow -j | jq -r '.class'
```

It never reports `org.omarchy.screensaver`.

## 2. Opens windowed, not fullscreen

The windowrule is in place:

```lua
-- default/hypr/apps/system.lua:35-37
o.window("org.omarchy.screensaver", { fullscreen = true })
o.window("org.omarchy.screensaver", { float = true })
o.window("org.omarchy.screensaver", { animation = "slide" })
```

but it does not land when another surface holds focus as the window maps. Measured with a
panel open, the screensaver window is floating at default terminal size. With no panel
open, the same check gives `fullscreen=2` at the monitor origin and the full logical size:

```bash
hyprctl clients -j | jq -r '.[] | select(.class=="org.omarchy.screensaver")
  | "size=\(.size) at=\(.at) fullscreen=\(.fullscreen)"'
# panel open : floating, terminal-sized
# no panel   : size=[1280,800] at=[1536,937] fullscreen=2   (monitor origin, logical size)
```

## 3. The panel is drawn over the screensaver

Layer-shell surfaces render above ordinary windows, so an open panel stays visible on top
of the screensaver. Nothing in the screensaver path dismisses open bar panels. Detect it
with:

```bash
hyprctl layers -j | jq -e 'any(.[].levels | to_entries[] | .value[];
  .namespace | test("keyboard-panel"))'
```

## Related: the resize guard is too narrow

Independent of the above, `wait_for_terminal_resize` only waits *while* the size is exactly
the pty default:

```bash
# bin/omarchy-screensaver:29-34
wait_for_terminal_resize() {
  local deadline=$((SECONDS + 2))
  while ((SECONDS < deadline)) && [[ $(stty size 2>/dev/null) == "24 80" ]]; do
    sleep 0.02
  done
}
```

Any intermediate size on the way to fullscreen falls straight through, ttfx measures early,
and you get a small canvas painted into the corner of a fullscreen window — exactly what the
comment above the function is trying to prevent. Waiting for the size to *settle* (the same
non-default value twice running) fixes it.

## Steps to reproduce

1. Open a bar panel that takes keyboard focus.
2. Leave the machine idle until the screensaver fires (default 900s), or run
   `omarchy-launch-screensaver force`.
3. The screensaver appears in a small floating window, with the panel on top, and is gone
   again about 1.4s later.

I hit this with my own clock plugin's panel, but `omarchy-keyboard-panel` is Omarchy's own
surface rather than my plugin's, so any focused bar panel should do it — worth confirming
against a stock one.

## Suggested fixes

- **Focus check:** treat an absent or unparseable `activewindow` as still in focus, and only
  let *losing* focus end the session once focus has actually been **held**. If the window
  never gets focus, stay up and let input or Omarchy's own idle dismissal end it.
- **Fullscreen:** after mapping, if the screensaver window is still `fullscreen == 0`, focus
  it by address and fullscreen it.
- **Panels:** dismiss open bar panels when the screensaver starts. Gating on the
  `keyboard-panel` layer existing keeps the usual case to a single cheap check.
- **Resize guard:** wait for `stty size` to settle rather than only while it is `24 80`.

Two traps if you implement the fullscreen fix on Hyprland 0.56. The legacy forms
`hyprctl dispatch fullscreen 0` and `hyprctl dispatch focuswindow class:...` no longer parse
— they are read as Lua and fail — so it needs
`hl.dsp.window.fullscreen({ mode = "fullscreen" })` and
`hl.dsp.focus({ window = "address:0x..." })`, with the `0x` prefix `hyprctl clients -j`
already provides. And an invalid dispatch still answers `ok`, so the return value proves
nothing; check the effect instead. `window.fullscreen` acts on the *active* window, so it is
worth confirming focus landed before calling it.

I have all four changes running locally if a PR would be useful.

Validation

Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.

Linked

Not linked to any known issue, other report or change yet.

Decision

Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.

  • Out of scopeDecided by agents · verified · 2026-10-09 21:37 UTC