Skip to content

Add refresh-rate control to the display panel - #6402

Open
startupfoundry wants to merge 2 commits into
basecamp:quattrofrom
startupfoundry:monitor-refresh-rate
Open

Add refresh-rate control to the display panel#6402
startupfoundry wants to merge 2 commits into
basecamp:quattrofrom
startupfoundry:monitor-refresh-rate

Conversation

@startupfoundry

@startupfoundry startupfoundry commented Jul 27, 2026

Copy link
Copy Markdown

Adds refresh-rate control to the display panel, alongside the existing scale
control, and the omarchy hyprland monitor refresh rate command behind it.

omarchy hyprland monitor refresh rate            # current rate
omarchy hyprland monitor refresh rate list       # rates at the current resolution
omarchy hyprland monitor refresh rate 144        # apply live, auto-reverts unless confirmed
omarchy hyprland monitor refresh rate up|down    # step
omarchy hyprland monitor refresh rate confirm    # keep
omarchy hyprland monitor refresh rate revert     # drop now

Only the rates the display advertises for the resolution already in use are
offered, so a request cannot silently fall back to preferred.

Applying a rate is reversible by default

A refresh rate the link cannot carry leaves a black screen, and unlike a bad
scale the compositor cannot tell: Hyprland reports the mode it asked for
whether or not the panel is showing it. So a rate is applied live, nothing is
written to disk, and it reverts on its own after 15 seconds
(OMARCHY_MONITOR_REFRESH_TIMEOUT) unless confirmed. That ordering is also
what makes reverting reliable — the config on disk is still the last known
good state, so revert is just hyprctl reload.

The panel mirrors this: a REFRESH RATE row of presets, hidden unless the
display offers more than one rate, plus a Keep / Revert row with a countdown
that appears only while a change is in flight. Revert sits on the left and
Keep on the right, with a single confirmKeepIndex that both the buttons and
the keyboard handler read, so the row cannot be reordered into disagreeing
with what its buttons do.

Persisting only when the rule is ours to own

Hyprland replaces a matching monitor rule wholesale rather than merging it, so
a rate cannot be layered on as its own rule without dropping that monitor's
scale, bitdepth, and colour settings. And since hyprctl monitors reports
currentFormat rather than the configured rule, those cannot be faithfully
snapshotted and written back.

Persisting therefore means owning the rule outright, which is only safe when
the user has not written one. On confirm, a desc:-keyed rule is appended
carrying scale = omarchy_monitor_scale so monitor scaling keeps composing,
marked with a comment so later changes update that line rather than stacking
duplicates. A hand-configured monitor is left alone: the change is kept for
the session and says why.

Testing

test/shell.d/monitor-refresh-rate-test.sh (16 assertions) covers listing,
filtering to the active resolution, applying without persisting, the pending
report, persistence and in-place rule updates, leaving user rules untouched,
stepping and clamping, snapping an unsupported rate onto an advertised mode,
revert, auto-revert, superseded countdowns, and confirming with nothing
pending. test/shell.d/monitor-test.sh gains 14 assertions for the parsing
and formatting helpers.

Verified on a 2560x1440 display advertising 59.95/99.95/120/144: applying,
keeping, and reverting from both the panel and the CLI, by pointer and by
keyboard, and the auto-revert on timeout.

screenshot-2026-07-27_13-49-56

Offers only the rates the display advertises for the resolution already in
use, so a request cannot silently fall back to preferred.

A refresh rate the link cannot carry leaves a black screen, and unlike a bad
scale the compositor cannot tell: Hyprland reports the mode it asked for
whether or not the panel is showing it. A rate is therefore applied live and
reverts on its own unless it is confirmed, and nothing reaches monitors.lua
until then. That ordering is also what makes reverting reliable, since the
config on disk is still the last known good state.

Hyprland replaces a matching monitor rule wholesale rather than merging it,
so a rate cannot be layered on as its own rule without dropping that
monitor's scale, bitdepth, and colour settings. Persisting therefore means
owning the rule, which is only safe when the user has not written one; a
hand-configured monitor keeps the change for the session and says why.
Adds a REFRESH RATE row to the display panel, next to SCALE and following the
same conventions: presets in a horizontal row, keyboard and pointer sharing
one cursor, and the focused monitor named when more than one is in play. The
row is hidden unless the display offers more than one rate.

Because a change reverts itself unless confirmed, the panel grows a Keep /
Revert row with a countdown while one is in flight. Revert sits on the left
and Keep on the right, with a single confirmKeepIndex that both the buttons
and the keyboard handler read, so the row cannot be reordered into
disagreeing with what its buttons do.

The rate, the available rates, and any pending change are appended to
omarchy-monitor-state so the panel keeps polling one command rather than
shelling out separately.
Copilot AI review requested due to automatic review settings July 27, 2026 18:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds reversible refresh-rate control for the focused Hyprland monitor, exposed both in the monitor panel UI and via a new omarchy hyprland monitor refresh rate CLI command, with auto-revert semantics to avoid persistent black-screen states.

Changes:

  • Added omarchy-hyprland-monitor-refresh-rate CLI with list/apply/step/pending/confirm/revert workflows and 15s auto-revert default.
  • Extended the monitor panel to show refresh-rate presets (when >1 available) plus an in-flight Keep/Revert confirmation row with countdown.
  • Added/extended shell tests covering refresh-rate parsing helpers and the new CLI behavior.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Reviewed changes

Copilot reviewed 4 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/shell.d/monitor-test.sh Adds unit coverage for refresh-rate label/parse/match/pending helpers in the monitor panel model.
test/shell.d/monitor-refresh-rate-test.sh Adds end-to-end shell tests for the new refresh-rate CLI behavior (apply/pending/confirm/revert/auto-revert/stepping).
shell/plugins/panels/monitor/Panel.qml Adds refresh-rate UI row, pending confirmation UI, keyboard integration, and local countdown behavior.
shell/plugins/panels/monitor/Model.js Adds refresh-rate normalization/parsing helpers used by the panel.
bin/omarchy-monitor-state Extends the monitor panel state output with current rate, available rates, and pending-change JSON.
bin/omarchy-hyprland-monitor-refresh-rate New CLI implementing safe, reversible refresh-rate changes and persistence rules.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants