Add refresh-rate control to the display panel - #6402
Open
startupfoundry wants to merge 2 commits into
Open
Conversation
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.
Contributor
There was a problem hiding this comment.
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-rateCLI 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds refresh-rate control to the display panel, alongside the existing scale
control, and the
omarchy hyprland monitor refresh ratecommand behind it.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 alsowhat 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 RATErow of presets, hidden unless thedisplay 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
confirmKeepIndexthat both the buttons andthe 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 monitorsreportscurrentFormatrather than the configured rule, those cannot be faithfullysnapshotted 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 appendedcarrying
scale = omarchy_monitor_scalesomonitor scalingkeeps 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.shgains 14 assertions for the parsingand 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.