Skip to content

fix(brightness): guard against null screen reference during disconnect - #193

Open
xdvi wants to merge 2 commits into
snowarch:mainfrom
xdvi:fix/brightness-null-screen
Open

fix(brightness): guard against null screen reference during disconnect#193
xdvi wants to merge 2 commits into
snowarch:mainfrom
xdvi:fix/brightness-null-screen

Conversation

@xdvi

@xdvi xdvi commented Jun 30, 2026

Copy link
Copy Markdown

Summary

This PR fixes a bug where disconnecting a monitor (e.g., when the screens are powered off by swayidle during idle) causes a flood of TypeError: Cannot read property 'model' of null warnings in Brightness.qml, leading to 100% CPU usage, memory leaks, and processes being killed by the OOM killer.

By adding optional chaining (screen?.model), we safely handle cases where the screen reference becomes null during destruction.

Root cause and the actual leak

The optional chaining stops the crash, but not the leak itself. monitors was a binding that recreated a BrightnessMonitor for every screen on every Quickshell.screens change — the previous set of monitor objects was never destroyed (no .destroy()), just orphaned while still parented to root, with their Timer/Behavior/Process still alive. Each screen connect/disconnect added another batch of zombie objects reacting to a dead screen reference, which is what actually drove CPU and memory up over time, not a single error.

Fixed by reconciling monitors against the live screen list instead of rebuilding it from scratch: screens that are still connected keep their existing BrightnessMonitor, and monitors whose screen went away are explicitly destroy()ed.

Testing

  • inir restart && inir logs — no errors
  • Verified brightness IPC increment/decrement still works after the change
  • Real physical monitor disconnect/reconnect (HDMI) on a dual-monitor setup (laptop + external) — confirmed via niri msg outputs that the screen list actually changed, not just DPMS
  • No TypeError/ReferenceError during or after the disconnect/reconnect cycle
  • Compared against the pre-leak-fix version with the same physical disconnect/reconnect: a qt.qml.listvalueconversion warning appears in both versions, confirming it's a pre-existing Quickshell/Qt quirk unrelated to this change, not a regression

@xdvi
xdvi force-pushed the fix/brightness-null-screen branch from 45aa88d to bf02233 Compare July 2, 2026 19:30
The optional-chaining guard stops the TypeError flood, but doesn't
address why it happened: monitors was a binding that recreated a
BrightnessMonitor for every screen on each Quickshell.screens change,
leaking the previous set (never destroyed, still parented to root,
Timers/Behaviors still running) since none of them get destroy()ed.
Each subsequent screen change added more zombies reacting to a dead
screen reference, which is what actually drove CPU/memory up.

Reconcile against the live screen list instead: reuse existing
monitors for screens that are still connected, and destroy() the
ones whose screen went away.
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