Add a refresh rate section to the Display panel - #10902
Open
langri-sha wants to merge 5 commits into
Open
Conversation
Scale, its clean-scale rounding, and its active preset each walked the display list for the focused entry. One binding answers that now, so anything else keyed to the focused display has a single place to read.
Reads the focused monitor's rate, or switches it to another rate the current mode already offers. Requests snap to the nearest listed mode within half a hertz, since Hyprland lists 143.91Hz for the mode a driver reports as 143.912, and a rate no mode carries is refused rather than left to Hyprland's fallback.
The panel needs both the live rate and the rates the current mode can reach. Only modes at the display's own resolution are carried, since a mode at another resolution is a resolution change rather than a rate.
Sits beneath SCALE and offers the rates the focused display's current mode can reach, as a pill row that walks with h/l like the scale presets do. A display with a single rate is not offering a choice, so the section stays out of the panel entirely.
langri-sha
force-pushed
the
add-monitor-refresh-rate
branch
from
September 8, 2026 23:52
10ffe47 to
4841739
Compare
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.
The Display panel can already change scale, but not the rate the screen runs at. Plenty of hardware doesn't come up at the rate it's sold as — a 144Hz laptop panel that boots at 60Hz, or a screen that drops down on battery — and the fix today is hand-editing
monitors.luaand reloading.This adds a Refresh rate row beneath Scale, listing the rates the focused display's current mode can reach. It behaves like the scale presets: pills in a row,
h/lto walk them, click orReturnto apply, and the active pill carries the current rate — no second readout beside the header, for the same reasonSCALEdoesn't have one. A display with only one rate isn't offering a choice, so the section doesn't render at all.That's the panel running on a 1920x1200 laptop panel that advertises both 144Hz and 60Hz, sitting at 60.
Pieces
omarchy-hyprland-monitor-refresh-rate— shows the focused monitor's rate, or switches it. It only accepts a rate the current resolution already offers, so a bad request is refused rather than handed to Hyprland to resolve into a fallback mode.omarchy-monitor-statenow reports each display's live rate and the modes at its own resolution. Modes at other resolutions are dropped: those are resolution changes, not rates.Model.jsgained the rate helpers, so the parsing and matching are unit-tested alongside the scale ones.Two details worth flagging
Rates are matched, not compared. Hyprland lists a mode as
143.91Hzwhile reporting the driver's live rate as143.912, so a request snaps to the nearest listed mode within half a hertz and the active pill is chosen the same way. Pills are labelled by whole hertz — the number the display is sold as — and two modes that round to the same label (59.94 and 60) collapse to the faster one.Refresh rate is deliberately not persisted to
monitors.lua. Scaling can be, because the default config carries one scale for every output. A refresh rate belongs to a single monitor's mode, and writing it into that same catch-all would force one display's rate on all of them. The manual explains how to pin a rate with a per-outputhl.monitorentry instead. Happy to wire up persistence if you'd rather it survived a reboot — it'd want a per-output block inmonitors.lua, which is a bigger change than this.Verification
./test/allpasses. New coverage intest/shell.d/monitor-refresh-rate-test.sh(the CLI: reading, snapping, scale preservation, refusing an out-of-mode rate, refusing an unsafe output name) plus rate helpers inmonitor-test.shand the new state fields inmonitor-state-test.sh.qmllintis clean on the panel.The CLI was exercised against real Hyprland on the display above: reading the live rate, refusing 100Hz, switching 60 → 144 and back, with scale held at 1.25 across both changes. Worth noting that after switching, the driver reports
144.00101for a mode listed as144.00Hz— the half-hertz match resolves that to the right pill, which is exactly why it isn't an equality check.The screenshot is the panel itself, rendered from this branch in a real Quickshell instance the way
test/shell.d/runtime-smoke-test.shlaunches one, reading livehyprctlstate.