Skip to content

layout: fix crash in dwindle during config reload on monitor disconnect#13591

Open
brunojuliao wants to merge 2 commits intohyprwm:mainfrom
brunojuliao:fix/layout-switchup-crash-on-monitor-disconnect
Open

layout: fix crash in dwindle during config reload on monitor disconnect#13591
brunojuliao wants to merge 2 commits intohyprwm:mainfrom
brunojuliao:fix/layout-switchup-crash-on-monitor-disconnect

Conversation

@brunojuliao
Copy link

@brunojuliao brunojuliao commented Mar 5, 2026

What does this PR do?

Fixes a SEGV crash (bad_variant_access in CDwindleAlgorithm::newTarget()) that occurs when a config reload is triggered while a monitor is disconnecting. This commonly happens when tools like hyprdynamicmonitors rewrite monitors.conf in response to a monitor hotplug event.

How does it do it?

Three targeted changes across two files:

WorkspaceAlgoMatcher::updateWorkspaceLayouts()

  1. Skip inert workspaces: When a monitor disconnects, its workspaces become inert (m_monitor is reset to null, m_id becomes WORKSPACE_INVALID). The previous code only checked for null workspace pointers but did not check inert(), leading to null dereferences when the layout algorithm tried to access the workspace's monitor.

  2. Resolve fallback layout names before comparison: algoForNameTiled() falls back to "dwindle" for unrecognized layout names (e.g., "default"), but the comparison in updateWorkspaceLayouts() used the raw config string. This meant "dwindle" == "default" always failed, causing a redundant layout switchup on every config reload. During a monitor disconnect, this unnecessary switchup iterates all tiled targets on workspaces that may have stale monitor references, triggering the crash.

CDwindleAlgorithm::addTarget()

  1. Defensive null checks: Validates that both the workspace and its monitor exist before accessing their members. Also handles a null focused monitor by falling back to getFirstNode() instead of dereferencing null.

How to reproduce the crash (before this fix)

  1. Configure Hyprland with an external monitor and the internal display disabled (e.g., via hyprdynamicmonitors)
  2. Set general:layout to a value that falls back to dwindle (e.g., "default")
  3. Disconnect the external monitor (or turn it off)
  4. Hyprland crashes with signal 11 (SEGV)

Crash backtrace

Hyprland received signal 11(SEGV)
#4 | std::__throw_bad_variant_access(unsigned int)
#5 | Layout::Tiled::CDwindleAlgorithm::newTarget(...)
#6 | Layout::CAlgorithm::updateTiledAlgo(...)
#7 | Layout::Supplementary::CWorkspaceAlgoMatcher::updateWorkspaceLayouts()
#8 | CConfigManager::postConfigReload(...)
#9 | CConfigManager::reload()
#10 | CConfigWatcher::onInotifyEvent()

Log tail

drm: Connector DP-2 disconnected
drm: Disabling output DP-2
drm: No format for output
drm: Modesetting eDP-2 with 1920x1080@165.00Hz

Tests

Added two hyprtester integration tests in hyprtester/src/tests/main/monitorDisconnect.cpp:

  1. testCrashOnMonitorDisconnectReload — spawns tiled windows on a secondary headless monitor, removes the monitor, then triggers a config reload. Verifies Hyprland stays responsive and windows migrate to the remaining monitor.

  2. testCrashOnFallbackLayoutMonitorDisconnect — sets general:layout to an unrecognized name ("default") that falls back to dwindle, then removes a monitor and reloads. Verifies the fallback name resolution doesn't cause a crash during the layout switchup.

Checklist

  • Code has been formatted with clang-format
  • No test regressions (ctest passes)
  • No new clang-tidy violations introduced
  • Changes are minimal and focused on the fix
  • Integration tests added for the fix

When a monitor disconnects and a config reload is triggered (e.g. by
hyprdynamicmonitors or any tool that rewrites monitor config files),
updateWorkspaceLayouts() could crash with SEGV (bad_variant_access)
inside CDwindleAlgorithm::newTarget().

Two issues contributed to this:

1. updateWorkspaceLayouts() did not skip inert workspaces. When a
   monitor disconnects, its workspaces become inert with their
   m_monitor reset to null. Processing these during a layout switchup
   leads to null dereferences in the tiled algorithm.

2. updateWorkspaceLayouts() compared the raw config layout name against
   registered algo names without resolving fallbacks. An unrecognized
   name (e.g. "default") falls back to "dwindle" when creating the
   algorithm, but the string comparison "dwindle" == "default" fails,
   causing a redundant layout switchup on every config reload. During
   monitor disconnect, this unnecessary switchup triggers the crash.

Additionally, CDwindleAlgorithm::addTarget() now validates that both
the workspace and its monitor are available before proceeding, and
handles a null focused monitor gracefully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Mar 5, 2026

Hello and thank you for making a PR to Hyprland!

Please check the PR Guidelines and make sure your PR follows them.
It will make the entire review process faster. :)

If your code can be tested, please always add tests. See more here.

beep boop, I'm just a bot. A real human will review your PR soon.

Add integration tests that verify Hyprland does not crash when a config
reload is triggered after a monitor disconnects with tiled windows.
Covers both the direct crash path and the fallback layout name mismatch
that caused redundant layout switchups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant