Skip to content

Add a refresh rate section to the Display panel - #10902

Open
langri-sha wants to merge 5 commits into
omacom:quattrofrom
langri-sha:add-monitor-refresh-rate
Open

Add a refresh rate section to the Display panel#10902
langri-sha wants to merge 5 commits into
omacom:quattrofrom
langri-sha:add-monitor-refresh-rate

Conversation

@langri-sha

@langri-sha langri-sha commented Sep 8, 2026

Copy link
Copy Markdown

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.lua and 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/l to walk them, click or Return to apply, and the active pill carries the current rate — no second readout beside the header, for the same reason SCALE doesn't have one. A display with only one rate isn't offering a choice, so the section doesn't render at all.

Display panel with a Refresh rate section beneath Scale, offering 144Hz and 60Hz with the 60Hz pill active

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-state now 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.js gained 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.91Hz while reporting the driver's live rate as 143.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-output hl.monitor entry instead. Happy to wire up persistence if you'd rather it survived a reboot — it'd want a per-output block in monitors.lua, which is a bigger change than this.

Verification

./test/all passes. New coverage in test/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 in monitor-test.sh and the new state fields in monitor-state-test.sh. qmllint is 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.00101 for a mode listed as 144.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.sh launches one, reading live hyprctl state.

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
langri-sha force-pushed the add-monitor-refresh-rate branch from 10ffe47 to 4841739 Compare September 8, 2026 23:52
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.

1 participant