From 8e690022f1a7c47b73aef2def4ac8be97641ada8 Mon Sep 17 00:00:00 2001 From: bbedward Date: Fri, 16 Oct 2026 10:23:02 -0400 Subject: [PATCH] displays: fall back to a matching named profile when the active one loses an output fixes #3705 --- .../DisplayConfig/DisplayConfigState.qml | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml b/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml index 6c788a36d..418c15814 100644 --- a/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml +++ b/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml @@ -793,9 +793,40 @@ Singleton { return OutputModel.configEntryMatchesLiveLayout(configEntry, outputs, SettingsData.displayNameMode, CompositorService.compositor); } - function applyAutoConfig() { - if (!profilesReady || !SettingsData.displayProfileAutoSelect || manualActivation || !currentOutputSet.length) + function profileEnabledOutputsConnected(profile) { + const connected = new Set(currentOutputSet); + const cfg = profile.outputs || {}; + return Object.keys(cfg).every(id => cfg[id].disabled || connected.has(id)); + } + + function profileCoversConnectedOutputs(profile) { + const cfg = profile.outputs || {}; + // Hyprland adds a make/model-less FALLBACK head once the last enabled output is gone + return currentOutputSet.filter(id => profileOutputIsReal(id)).every(id => id in cfg); + } + + function fallbackFromDisconnectedProfile() { + const activeId = SessionData.getActiveDisplayProfile(CompositorService.compositor); + const active = validatedProfiles[activeId]; + if (!active?.name || profileEnabledOutputsConnected(active)) return; + const fallbackId = Object.keys(validatedProfiles).find(id => { + const p = validatedProfiles[id]; + return p.name && profileEnabledOutputsConnected(p) && profileCoversConnectedOutputs(p); + }); + if (!fallbackId) + return; + log.info("Profile", activeId, "lost an output, falling back to", fallbackId); + activateProfile(fallbackId); + } + + function applyAutoConfig() { + if (!profilesReady || manualActivation || !currentOutputSet.length) + return; + if (!SettingsData.displayProfileAutoSelect) { + fallbackFromDisconnectedProfile(); + return; + } readMonitorsJson(data => { const match = findConfigEntryByFingerprint(data, currentOutputSet, false);