From a5ffd18596cdb6a33dcae339eb8a5533f6e8c21e Mon Sep 17 00:00:00 2001 From: Harsh Kumar <89091351+aarshivv@users.noreply.github.com> Date: Sat, 12 Sep 2026 19:29:55 +0530 Subject: [PATCH] display config: read live layout from hyprland instead of the cached wlr snapshot (#3418) (cherry picked from commit d7b581256196436414daa7f16e84cfc361cb7128) --- .../DisplayConfig/DisplayConfigState.qml | 14 +++++++++ quickshell/Services/CompositorService.qml | 2 +- quickshell/Services/HyprlandService.qml | 29 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml b/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml index d85e36432..f40c7034f 100644 --- a/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml +++ b/quickshell/Modules/Settings/DisplayConfig/DisplayConfigState.qml @@ -1113,6 +1113,13 @@ Singleton { } } + Connections { + target: HyprlandService + function onMonitorLayoutChanged() { + root.outputs = root.buildOutputsMap(); + } + } + Connections { target: CompositorService function onCompositorChanged() { @@ -1806,6 +1813,13 @@ Singleton { "transform": mapWlrTransform(output.transform) } }; + const live = HyprlandService.liveMonitor(output.name); + if (!live) + continue; + map[output.name].logical.x = live.x; + map[output.name].logical.y = live.y; + map[output.name].logical.scale = live.scale || 1.0; + map[output.name].logical.transform = hyprlandToTransform(live.lastIpcObject?.transform ?? 0); } return map; } diff --git a/quickshell/Services/CompositorService.qml b/quickshell/Services/CompositorService.qml index 39da32054..d6f6cdd82 100644 --- a/quickshell/Services/CompositorService.qml +++ b/quickshell/Services/CompositorService.qml @@ -275,7 +275,7 @@ Singleton { enabled: isHyprland function onRawEvent(event) { - if (event.name === "monitoraddedv2" || event.name === "monitorremoved") { + if (event.name === "monitoraddedv2" || event.name === "monitorremoved" || event.name === "configreloaded") { root.refreshHyprlandMonitorLayout(); return; } diff --git a/quickshell/Services/HyprlandService.qml b/quickshell/Services/HyprlandService.qml index 7736ebb00..98190abf1 100644 --- a/quickshell/Services/HyprlandService.qml +++ b/quickshell/Services/HyprlandService.qml @@ -21,6 +21,8 @@ Singleton { readonly property bool luaConfigActive: CompositorService.isHyprland && (Hyprland.usingLua === true || luaConfigDetected) property int _lastGapValue: -1 + property string _monitorLayoutSignature: "" + signal monitorLayoutChanged property bool luaConfigDetected: false property bool luaConfigStatusReady: false property bool luaConfigStatusLoading: false @@ -296,11 +298,38 @@ Singleton { Proc.runCommand("hyprctl-reload", ["hyprctl", "reload"], (output, exitCode) => { if (exitCode !== 0) log.warn("hyprctl reload failed:", output); + else + Hyprland.refreshMonitors(); if (callback) callback(exitCode === 0); }); } + function liveMonitor(name) { + if (!CompositorService.isHyprland) + return null; + return Hyprland.monitors.values.find(m => m.name === name) ?? null; + } + + function _syncMonitorLayout() { + const signature = Hyprland.monitors.values.map(m => `${m.name}:${m.x},${m.y},${m.scale},${m.lastIpcObject?.transform ?? 0}`).join("|"); + if (signature === _monitorLayoutSignature) + return; + _monitorLayoutSignature = signature; + monitorLayoutChanged(); + } + + Instantiator { + model: CompositorService.isHyprland ? Hyprland.monitors : null + delegate: Connections { + required property HyprlandMonitor modelData + target: modelData + function onLastIpcObjectChanged() { + root._syncMonitorLayout(); + } + } + } + function setLayoutXray(enabled) { layoutXrayEnabled = enabled; _layoutXrayLoaded = true;