From 66e38c5efeacc8a5e2387e7a81953cbdac7bffad Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 14 May 2026 19:08:07 +0200 Subject: [PATCH] Support Hyprland lua dispatching (#2419) --- quickshell/Modules/DankBar/DankBarContent.qml | 2 +- .../DankBar/Widgets/WorkspaceSwitcher.qml | 13 ++-- quickshell/Modules/Dock/DockAppButton.qml | 2 +- .../WorkspaceOverlays/HyprlandOverview.qml | 3 +- .../WorkspaceOverlays/OverviewWidget.qml | 8 +- quickshell/Services/CompositorService.qml | 4 +- quickshell/Services/HyprlandService.qml | 74 +++++++++++++++++-- quickshell/Services/SessionService.qml | 2 +- 8 files changed, 87 insertions(+), 21 deletions(-) diff --git a/quickshell/Modules/DankBar/DankBarContent.qml b/quickshell/Modules/DankBar/DankBarContent.qml index 1602e039..a9668489 100644 --- a/quickshell/Modules/DankBar/DankBarContent.qml +++ b/quickshell/Modules/DankBar/DankBarContent.qml @@ -279,7 +279,7 @@ Item { const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0); if (nextIndex !== validIndex) { - Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`); + HyprlandService.focusWorkspace(realWorkspaces[nextIndex].id); } } else if (CompositorService.isDwl) { const currentTag = getCurrentWorkspace(); diff --git a/quickshell/Modules/DankBar/Widgets/WorkspaceSwitcher.qml b/quickshell/Modules/DankBar/Widgets/WorkspaceSwitcher.qml index f1caa6a3..058557f9 100644 --- a/quickshell/Modules/DankBar/Widgets/WorkspaceSwitcher.qml +++ b/quickshell/Modules/DankBar/Widgets/WorkspaceSwitcher.qml @@ -580,8 +580,9 @@ Item { NiriService.switchToWorkspace(data.id); break; case "hyprland": - if (data.id) - Hyprland.dispatch(`workspace ${data.id}`); + if (data.id) { + HyprlandService.focusWorkspace(data.id); + } break; case "dwl": if (data.tag !== undefined) @@ -670,7 +671,7 @@ Item { return; } - Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`); + HyprlandService.focusWorkspace(realWorkspaces[nextIndex].id); } else if (CompositorService.isDwl) { const realWorkspaces = getRealWorkspaces(); if (realWorkspaces.length < 2) { @@ -1308,7 +1309,7 @@ Item { NiriService.switchToWorkspace(modelData.id); } } else if (CompositorService.isHyprland && modelData?.id) { - Hyprland.dispatch(`workspace ${modelData.id}`); + HyprlandService.focusWorkspace(modelData.id); } else if (CompositorService.isDwl && modelData?.tag !== undefined) { DwlService.switchToTag(root.screenName, modelData.tag); } else if ((CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) && modelData?.num) { @@ -1671,7 +1672,7 @@ Item { if (!winId) return; if (CompositorService.isHyprland) { - Hyprland.dispatch(`focuswindow address:${winId}`); + HyprlandService.focusWindow(winId); } else if (CompositorService.isNiri) { NiriService.focusWindow(winId); } @@ -1840,7 +1841,7 @@ Item { if (!winId) return; if (CompositorService.isHyprland) { - Hyprland.dispatch(`focuswindow address:${winId}`); + HyprlandService.focusWindow(winId); } else if (CompositorService.isNiri) { NiriService.focusWindow(winId); } diff --git a/quickshell/Modules/Dock/DockAppButton.qml b/quickshell/Modules/Dock/DockAppButton.qml index 1d9fb755..60e711dd 100644 --- a/quickshell/Modules/Dock/DockAppButton.qml +++ b/quickshell/Modules/Dock/DockAppButton.qml @@ -164,7 +164,7 @@ Item { if (!specialName) return false; - Hyprland.dispatch("togglespecialworkspace " + specialName); + HyprlandService.toggleSpecial(specialName); Qt.callLater(() => waylandToplevel.activate()); return true; } diff --git a/quickshell/Modules/WorkspaceOverlays/HyprlandOverview.qml b/quickshell/Modules/WorkspaceOverlays/HyprlandOverview.qml index 2c605d11..81af3866 100644 --- a/quickshell/Modules/WorkspaceOverlays/HyprlandOverview.qml +++ b/quickshell/Modules/WorkspaceOverlays/HyprlandOverview.qml @@ -255,7 +255,8 @@ Scope { } const targetId = thisMonitorWorkspaceIds[targetIndex]; - Hyprland.dispatch("workspace " + targetId); + + HyprlandService.focusWorkspace(targetId); event.accepted = true; } } diff --git a/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml b/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml index 08afcd90..8fd3e338 100644 --- a/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml +++ b/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml @@ -223,7 +223,7 @@ Item { onClicked: { if (root.draggingTargetWorkspace === -1) { root.overviewOpen = false; - Hyprland.dispatch(`workspace ${workspaceValue}`); + HyplandService.focusWorkspace(workspaceValue) } } } @@ -352,7 +352,7 @@ Item { root.draggingTargetWorkspace = -1; if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) { - Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace},address:${windowData?.address}`); + HyprlandService.moveToWorkspace(targetWorkspace, windowData?.address, false) Qt.callLater(() => { Hyprland.refreshToplevels(); Hyprland.refreshWorkspaces(); @@ -372,10 +372,10 @@ Item { return; if (event.button === Qt.LeftButton) { root.overviewOpen = false; - Hyprland.dispatch(`focuswindow address:${windowData.address}`); + HyprlandService.focusWindow(windowData.address); event.accepted = true; } else if (event.button === Qt.MiddleButton) { - Hyprland.dispatch(`closewindow address:${windowData.address}`); + HyprlandService.closeWindow(windowData.address); event.accepted = true; } } diff --git a/quickshell/Services/CompositorService.qml b/quickshell/Services/CompositorService.qml index 6017ab7b..90746b11 100644 --- a/quickshell/Services/CompositorService.qml +++ b/quickshell/Services/CompositorService.qml @@ -682,7 +682,7 @@ Singleton { if (isNiri) return NiriService.powerOffMonitors(); if (isHyprland) - return Hyprland.dispatch("dpms off"); + return HyprlandService.dpmsOff(); if (isDwl) return _dwlPowerOffMonitors(); if (isSway || isScroll || isMiracle) { @@ -701,7 +701,7 @@ Singleton { if (isNiri) return NiriService.powerOnMonitors(); if (isHyprland) - return Hyprland.dispatch("dpms on"); + return HyprlandService.dpmsOn(); if (isDwl) return _dwlPowerOnMonitors(); if (isSway || isScroll || isMiracle) { diff --git a/quickshell/Services/HyprlandService.qml b/quickshell/Services/HyprlandService.qml index 21d931be..e74ad94b 100644 --- a/quickshell/Services/HyprlandService.qml +++ b/quickshell/Services/HyprlandService.qml @@ -338,10 +338,74 @@ decoration { if (!wsId) return; const fullName = wsId + " " + newName; - Proc.runCommand("hyprland-rename-ws", ["hyprctl", "dispatch", "renameworkspace", String(wsId), fullName], (output, exitCode) => { - if (exitCode !== 0) { - log.warn("Failed to rename workspace:", output); - } - }); + if (Hyprland.usingLua) { + Hyprland.dispatch(`hl.dsp.workspace.rename({workspace = "${wsId}", name = "${fullName}"})`) + } else { + Proc.runCommand("hyprland-rename-ws", ["hyprctl", "dispatch", "renameworkspace", String(wsId), fullName], (output, exitCode) => { + if (exitCode !== 0) { + log.warn("Failed to rename workspace:", output); + } + }); + } + } + + function focusWorkspace(workspace) { + if (Hyprland.usingLua) { + Hyprland.dispatch(`hl.dsp.focus({workspace = "${workspace}"})`) + } else { + Hyprland.dispatch(`workspace ${workspace}`) + } + } + + function focusWindow(windowAddress) { + if (Hyprland.usingLua) { + Hyprland.dispatch(`hl.dsp.focus({window = "address:${windowAddress}"})`); + } else { + Hyprland.dispatch(`focuswindow address:${windowAddress}`); + } + } + + function closeWindow(windowAddress) { + if (Hyprland.usingLua) { + Hyprland.dispatch(`hl.dsp.window.close("address:${windowAddress}")`); + } else { + Hyprland.dispatch(`closewindow address:${windowAddress}`); + } + } + function moveToWorkspace(workspace, windowAddress, follow = true) { + if (Hyprland.usingLua) { + Hyprland.dispatch(`hl.dsp.window.move({workspace = "${workspace}", window="address:${windowAddress}", follow = ${follow}})`) + } else { + const dispatcher = follow ? "movetoworkspace" : "movetoworkspacesilent" + Hyprland.dispatch(`${dispatcher} ${workspace},address:${windowAddress}`); + } + } + function toggleSpecial(specialName) { + if (Hyprland.usingLua) { + Hyprland.dispatch(`hl.dsp.workspace.toggle_special(${specialName})`) + } else { + Hyprland.dispatch("togglespecialworkspace " + specialName); + } + } + function exit() { + if (Hyprland.usingLua) { + Hyprland.dispatch("hl.dsp.exit()") + } else { + Hyprland.dispatch("exit"); + } + } + function dpmsOff() { + if (Hyprland.usingLua) { + Hyprland.dispatch(`hl.dsp.dpms({action = "disable"})`) + } else { + Hyprland.dispatch("dpms off"); + } + } + function dpmsOn() { + if (Hyprland.usingLua) { + Hyprland.dispatch(`hl.dsp.dpms({action = "enable"})`) + } else { + Hyprland.dispatch("dpms on"); + } } } diff --git a/quickshell/Services/SessionService.qml b/quickshell/Services/SessionService.qml index c96d83bb..07d111fe 100644 --- a/quickshell/Services/SessionService.qml +++ b/quickshell/Services/SessionService.qml @@ -327,7 +327,7 @@ Singleton { return; } - Hyprland.dispatch("exit"); + HyprlandService.exit(); } else { Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout]); }