diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e48cb0be..8c27871a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -24,6 +24,7 @@ assignees: "" - [ ] niri - [ ] Hyprland +- [ ] dwl (MangoWC) - [ ] Other (specify) ## Distribution diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index cc18b8d2..33664034 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -21,6 +21,7 @@ Is this feature specific to one compositor? - [ ] All compositors - [ ] niri - [ ] Hyprland +- [ ] dwl (MangoWC) ## Proposed Solution diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index 048a8e62..5b777b51 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -10,6 +10,7 @@ assignees: "" - [ ] niri - [ ] Hyprland +- [ ] dwl (MangoWC) - [ ] other ## Distribution diff --git a/Modules/DankBar/DankBar.qml b/Modules/DankBar/DankBar.qml index 286f4c5c..0c9f4e5b 100644 --- a/Modules/DankBar/DankBar.qml +++ b/Modules/DankBar/DankBar.qml @@ -580,6 +580,11 @@ Item { } return monitorWorkspaces.sort((a, b) => a.id - b.id) + } else if (CompositorService.isDwl) { + if (!DwlService.dwlAvailable || DwlService.tagCount === 0) { + return Array.from({length: 9}, (_, i) => i) + } + return Array.from({length: DwlService.tagCount}, (_, i) => i) } return [1] } @@ -595,6 +600,12 @@ Item { const monitors = Hyprland.monitors?.values || [] const currentMonitor = monitors.find(monitor => monitor.name === barWindow.screenName) return currentMonitor?.activeWorkspace?.id ?? 1 + } else if (CompositorService.isDwl) { + if (!DwlService.dwlAvailable) return 0 + const outputState = DwlService.getOutputState(barWindow.screenName) + if (!outputState || !outputState.tags) return 0 + const activeTags = DwlService.getActiveTags(barWindow.screenName) + return activeTags.length > 0 ? activeTags[0] : 0 } return 1 } @@ -623,6 +634,15 @@ Item { if (nextIndex !== validIndex) { Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`) } + } else if (CompositorService.isDwl) { + const currentTag = getCurrentWorkspace() + const currentIndex = realWorkspaces.findIndex(tag => tag === currentTag) + const validIndex = currentIndex === -1 ? 0 : currentIndex + const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0) + + if (nextIndex !== validIndex) { + DwlService.switchToTag(barWindow.screenName, realWorkspaces[nextIndex]) + } } } diff --git a/Modules/DankBar/Widgets/LauncherButton.qml b/Modules/DankBar/Widgets/LauncherButton.qml index 15dac659..da27f424 100644 --- a/Modules/DankBar/Widgets/LauncherButton.qml +++ b/Modules/DankBar/Widgets/LauncherButton.qml @@ -37,7 +37,7 @@ BasePill { } IconImage { - visible: SettingsData.launcherLogoMode === "compositor" + visible: SettingsData.launcherLogoMode === "compositor" && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl) anchors.centerIn: parent width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset) height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset) @@ -48,6 +48,8 @@ BasePill { return "file://" + Theme.shellDir + "/assets/niri.svg" } else if (CompositorService.isHyprland) { return "file://" + Theme.shellDir + "/assets/hyprland.svg" + } else if (CompositorService.isDwl) { + return "file://" + Theme.shellDir + "/assets/mango.png" } return "" } diff --git a/Modules/DankBar/Widgets/WorkspaceSwitcher.qml b/Modules/DankBar/Widgets/WorkspaceSwitcher.qml index 1fb3fcf1..a8c3a7eb 100644 --- a/Modules/DankBar/Widgets/WorkspaceSwitcher.qml +++ b/Modules/DankBar/Widgets/WorkspaceSwitcher.qml @@ -28,11 +28,14 @@ Item { _desktopEntriesUpdateTrigger++ } } + property int currentWorkspace: { if (CompositorService.isNiri) { return getNiriActiveWorkspace() } else if (CompositorService.isHyprland) { return getHyprlandActiveWorkspace() + } else if (CompositorService.isDwl) { + return getDwlActiveTag() } return 1 } @@ -47,6 +50,10 @@ Item { const filteredList = baseList.filter(ws => ws.id > -1) return SettingsData.showWorkspacePadding ? padWorkspaces(filteredList) : filteredList } + if (CompositorService.isDwl) { + const baseList = getDwlTags() + return SettingsData.showWorkspacePadding ? padWorkspaces(baseList) : baseList + } return [1] } @@ -69,15 +76,30 @@ Item { targetWorkspaceId = workspace.id } else if (CompositorService.isHyprland) { targetWorkspaceId = ws.id !== undefined ? ws.id : ws + } else if (CompositorService.isDwl) { + if (typeof ws !== "object" || ws.tag === undefined) { + return [] + } + targetWorkspaceId = ws.tag } else { return [] } const wins = CompositorService.isNiri ? (NiriService.windows || []) : CompositorService.sortedToplevels - const byApp = {} - const isActiveWs = CompositorService.isNiri ? NiriService.allWorkspaces.some(ws => ws.id === targetWorkspaceId && ws.is_active) : targetWorkspaceId === root.currentWorkspace + let isActiveWs = false + if (CompositorService.isNiri) { + isActiveWs = NiriService.allWorkspaces.some(ws => ws.id === targetWorkspaceId && ws.is_active) + } else if (CompositorService.isDwl) { + const output = DwlService.getOutputState(root.screenName) + if (output && output.tags) { + const tag = output.tags.find(t => t.tag === targetWorkspaceId) + isActiveWs = tag ? (tag.state === 1) : false + } + } else { + isActiveWs = targetWorkspaceId === root.currentWorkspace + } wins.forEach((w, i) => { if (!w) { @@ -128,10 +150,14 @@ Item { function padWorkspaces(list) { const padded = list.slice() - const placeholder = CompositorService.isHyprland ? { - "id": -1, - "name": "" - } : -1 + let placeholder + if (CompositorService.isHyprland) { + placeholder = {"id": -1, "name": ""} + } else if (CompositorService.isDwl) { + placeholder = {"tag": -1} + } else { + placeholder = -1 + } while (padded.length < 3) { padded.push(placeholder) } @@ -199,7 +225,6 @@ Item { return Hyprland.focusedWorkspace ? Hyprland.focusedWorkspace.id : 1 } - // Find the monitor object for this screen const monitors = Hyprland.monitors?.values || [] const currentMonitor = monitors.find(monitor => monitor.name === root.screenName) @@ -207,10 +232,41 @@ Item { return 1 } - // Use the monitor's active workspace ID (like original config) return currentMonitor.activeWorkspace?.id ?? 1 } + function getDwlTags() { + if (!DwlService.dwlAvailable) { + return [{"tag": 0}, {"tag": 1}] + } + + const output = DwlService.getOutputState(root.screenName) + if (!output || !output.tags || output.tags.length === 0) { + const tagCount = DwlService.tagCount || 9 + const tags = [] + for (let i = 0; i < tagCount; i++) { + tags.push({"tag": i}) + } + return tags + } + + return output.tags.map(tag => ({"tag": tag.tag, "state": tag.state, "clients": tag.clients, "focused": tag.focused})) + } + + function getDwlActiveTag() { + if (!DwlService.dwlAvailable) { + return 0 + } + + const output = DwlService.getOutputState(root.screenName) + if (!output || !output.tags) { + return 0 + } + + const activeTag = output.tags.find(tag => tag.state === 1) + return activeTag ? activeTag.tag : 0 + } + readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30)) readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2) readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight @@ -219,6 +275,8 @@ Item { return root.workspaceList.filter(ws => { if (CompositorService.isHyprland) { return ws && ws.id !== -1 + } else if (CompositorService.isDwl) { + return ws && ws.tag !== -1 } return ws !== -1 }) @@ -255,12 +313,27 @@ Item { } Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`) + } else if (CompositorService.isDwl) { + const realWorkspaces = getRealWorkspaces() + if (realWorkspaces.length < 2) { + return + } + + const currentIndex = realWorkspaces.findIndex(ws => ws.tag === root.currentWorkspace) + const validIndex = currentIndex === -1 ? 0 : currentIndex + const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0) + + if (nextIndex === validIndex) { + return + } + + DwlService.switchToTag(root.screenName, realWorkspaces[nextIndex].tag) } } width: isVertical ? barThickness : visualWidth height: isVertical ? visualHeight : barThickness - visible: CompositorService.isNiri || CompositorService.isHyprland + visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl Rectangle { id: visualBackground @@ -303,12 +376,16 @@ Item { property bool isActive: { if (CompositorService.isHyprland) { return modelData && modelData.id === root.currentWorkspace + } else if (CompositorService.isDwl) { + return modelData && modelData.tag === root.currentWorkspace } return modelData === root.currentWorkspace } property bool isPlaceholder: { if (CompositorService.isHyprland) { return modelData && modelData.id === -1 + } else if (CompositorService.isDwl) { + return modelData && modelData.tag === -1 } return modelData === -1 } @@ -319,9 +396,10 @@ Item { property bool isUrgent: { if (CompositorService.isHyprland) { return modelData?.urgent ?? false - } - if (CompositorService.isNiri) { + } else if (CompositorService.isNiri) { return loadedIsUrgent + } else if (CompositorService.isDwl) { + return modelData?.state === 2 } return false } @@ -372,6 +450,8 @@ Item { NiriService.switchToWorkspace(modelData - 1) } else if (CompositorService.isHyprland && modelData?.id) { Hyprland.dispatch(`workspace ${modelData.id}`) + } else if (CompositorService.isDwl && modelData?.tag !== undefined) { + DwlService.switchToTag(root.screenName, modelData.tag) } } } @@ -394,6 +474,8 @@ Item { wsData = NiriService.allWorkspaces.find(ws => ws.idx + 1 === modelData && ws.output === root.screenName) || null; } else if (CompositorService.isHyprland) { wsData = modelData; + } else if (CompositorService.isDwl) { + wsData = modelData; } delegateRoot.loadedWorkspaceData = wsData; delegateRoot.loadedIsUrgent = wsData?.is_urgent ?? false; @@ -406,7 +488,11 @@ Item { delegateRoot.loadedHasIcon = icData !== null; if (SettingsData.showWorkspaceApps) { - delegateRoot.loadedIcons = root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData)); + if (CompositorService.isDwl) { + delegateRoot.loadedIcons = root.getWorkspaceIcons(modelData); + } else { + delegateRoot.loadedIcons = root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData)); + } } else { delegateRoot.loadedIcons = []; } @@ -651,11 +737,25 @@ Item { StyledText { anchors.centerIn: parent text: { - const isPlaceholder = CompositorService.isHyprland ? (modelData?.id === -1) : (modelData === -1) + let isPlaceholder + if (CompositorService.isHyprland) { + isPlaceholder = modelData?.id === -1 + } else if (CompositorService.isDwl) { + isPlaceholder = modelData?.tag === -1 + } else { + isPlaceholder = modelData === -1 + } + if (isPlaceholder) { return index + 1 } - return CompositorService.isHyprland ? (modelData?.id || "") : (modelData - 1); + + if (CompositorService.isHyprland) { + return modelData?.id || "" + } else if (CompositorService.isDwl) { + return (modelData?.tag !== undefined) ? (modelData.tag + 1) : "" + } + return modelData - 1 } color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium font.pixelSize: Theme.barTextSize(barThickness) @@ -683,6 +783,11 @@ Item { function onShowWorkspaceAppsChanged() { delegateRoot.updateAllData() } function onWorkspaceNameIconsChanged() { delegateRoot.updateAllData() } } + Connections { + target: DwlService + enabled: CompositorService.isDwl + function onStateChanged() { delegateRoot.updateAllData() } + } } } } diff --git a/Modules/Settings/LauncherTab.qml b/Modules/Settings/LauncherTab.qml index 23655ed6..16112d19 100644 --- a/Modules/Settings/LauncherTab.qml +++ b/Modules/Settings/LauncherTab.qml @@ -87,9 +87,14 @@ Item { anchors.horizontalCenter: parent.horizontalCenter model: { const modes = [I18n.tr("Apps Icon"), I18n.tr("OS Logo")] - if (CompositorService.isNiri || CompositorService.isHyprland) { - const compositorName = CompositorService.isNiri ? "niri" : "Hyprland" - modes.push(compositorName) + if (CompositorService.isNiri) { + modes.push("niri") + } else if (CompositorService.isHyprland) { + modes.push("Hyprland") + } else if (CompositorService.isDwl) { + modes.push("mango") + } else { + modes.push(I18n.tr("Compositor")) } modes.push(I18n.tr("Custom")) return modes @@ -97,12 +102,8 @@ Item { currentIndex: { if (SettingsData.launcherLogoMode === "apps") return 0 if (SettingsData.launcherLogoMode === "os") return 1 - if (SettingsData.launcherLogoMode === "compositor") { - return (CompositorService.isNiri || CompositorService.isHyprland) ? 2 : -1 - } - if (SettingsData.launcherLogoMode === "custom") { - return (CompositorService.isNiri || CompositorService.isHyprland) ? 3 : 2 - } + if (SettingsData.launcherLogoMode === "compositor") return 2 + if (SettingsData.launcherLogoMode === "custom") return 3 return 0 } onSelectionChanged: (index, selected) => { @@ -111,13 +112,9 @@ Item { SettingsData.setLauncherLogoMode("apps") } else if (index === 1) { SettingsData.setLauncherLogoMode("os") - } else if (CompositorService.isNiri || CompositorService.isHyprland) { - if (index === 2) { - SettingsData.setLauncherLogoMode("compositor") - } else if (index === 3) { - SettingsData.setLauncherLogoMode("custom") - } } else if (index === 2) { + SettingsData.setLauncherLogoMode("compositor") + } else if (index === 3) { SettingsData.setLauncherLogoMode("custom") } } diff --git a/README.md b/README.md index b8119be4..611966cc 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ -A modern Wayland desktop shell built with [Quickshell](https://quickshell.org/) and [Go](https://go.dev/). Optimized for the [niri](https://github.com/YaLTeR/niri) and [Hyprland](https://hyprland.org/) compositors. +A modern Wayland desktop shell built with [Quickshell](https://quickshell.org/) and [Go](https://go.dev/). Optimized for the [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), and [dwl/mangowc](https://github.com/DreamMaoMao/mangowc) compositors. Features notifications, app launcher, wallpaper customization, and fully customizable with [plugins](https://github.com/AvengeMedia/dms-plugin-registry). diff --git a/Services/CompositorService.qml b/Services/CompositorService.qml index a55e699b..867601d9 100644 --- a/Services/CompositorService.qml +++ b/Services/CompositorService.qml @@ -12,6 +12,7 @@ Singleton { property bool isHyprland: false property bool isNiri: false + property bool isDwl: false property string compositor: "unknown" readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE") @@ -87,6 +88,15 @@ Singleton { Qt.callLater(() => NiriService.generateNiriLayoutConfig()) } + Connections { + target: DwlService + function onStateChanged() { + if (isDwl && !isHyprland && !isNiri) { + scheduleSort() + } + } + } + function computeSortedToplevels() { if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values) return [] @@ -331,6 +341,7 @@ Singleton { if (hyprlandSignature && hyprlandSignature.length > 0) { isHyprland = true isNiri = false + isDwl = false compositor = "hyprland" console.info("CompositorService: Detected Hyprland") try { @@ -344,6 +355,7 @@ Singleton { if (exitCode === 0) { isNiri = true isHyprland = false + isDwl = false compositor = "niri" console.info("CompositorService: Detected Niri with socket:", niriSocket) NiriService.generateNiriBinds() @@ -351,27 +363,82 @@ Singleton { } else { isHyprland = false isNiri = true + isDwl = false compositor = "niri" console.warn("CompositorService: Niri socket check failed, defaulting to Niri anyway") } }, 0) } else { + if (DMSService.dmsAvailable) { + Qt.callLater(checkForDwl) + } else { + isHyprland = false + isNiri = false + isDwl = false + compositor = "unknown" + console.warn("CompositorService: No compositor detected") + } + } + } + + Connections { + target: DMSService + function onCapabilitiesReceived() { + if (!isHyprland && !isNiri && !isDwl) { + checkForDwl() + } + } + } + + function checkForDwl() { + if (DMSService.apiVersion >= 12 && DMSService.capabilities.includes("dwl")) { isHyprland = false isNiri = false - compositor = "unknown" - console.warn("CompositorService: No compositor detected") + isDwl = true + compositor = "dwl" + console.info("CompositorService: Detected DWL via DMS capability") } } function powerOffMonitors() { if (isNiri) return NiriService.powerOffMonitors() if (isHyprland) return Hyprland.dispatch("dpms off") + if (isDwl) return _dwlPowerOffMonitors() console.warn("CompositorService: Cannot power off monitors, unknown compositor") } function powerOnMonitors() { if (isNiri) return NiriService.powerOnMonitors() if (isHyprland) return Hyprland.dispatch("dpms on") + if (isDwl) return _dwlPowerOnMonitors() console.warn("CompositorService: Cannot power on monitors, unknown compositor") } + + function _dwlPowerOffMonitors() { + if (!Quickshell.screens || Quickshell.screens.length === 0) { + console.warn("CompositorService: No screens available for DWL power off") + return + } + + for (let i = 0; i < Quickshell.screens.length; i++) { + const screen = Quickshell.screens[i] + if (screen && screen.name) { + Quickshell.execDetached(["wlr-randr", "--output", screen.name, "--off"]) + } + } + } + + function _dwlPowerOnMonitors() { + if (!Quickshell.screens || Quickshell.screens.length === 0) { + console.warn("CompositorService: No screens available for DWL power on") + return + } + + for (let i = 0; i < Quickshell.screens.length; i++) { + const screen = Quickshell.screens[i] + if (screen && screen.name) { + Quickshell.execDetached(["wlr-randr", "--output", screen.name, "--on"]) + } + } + } } diff --git a/Services/DMSService.qml b/Services/DMSService.qml index 7b4bc406..e31cc2ee 100644 --- a/Services/DMSService.qml +++ b/Services/DMSService.qml @@ -42,6 +42,7 @@ Singleton { signal capabilitiesReceived() signal credentialsRequest(var data) signal bluetoothPairingRequest(var data) + signal dwlStateUpdate(var data) Component.onCompleted: { if (socketPath && socketPath.length > 0) { @@ -266,6 +267,8 @@ Singleton { } } else if (service === "bluetooth.pairing") { bluetoothPairingRequest(data) + } else if (service === "dwl") { + dwlStateUpdate(data) } } diff --git a/Services/DwlService.qml b/Services/DwlService.qml new file mode 100644 index 00000000..c7b82f91 --- /dev/null +++ b/Services/DwlService.qml @@ -0,0 +1,170 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell + +Singleton { + id: root + + property bool dwlAvailable: false + property var outputs: ({}) + property var tagCount: 0 + property var layouts: [] + property string activeOutput: "" + + signal stateChanged() + + Connections { + target: DMSService + function onCapabilitiesReceived() { + checkCapabilities() + } + function onConnectionStateChanged() { + if (DMSService.isConnected) { + checkCapabilities() + } else { + dwlAvailable = false + } + } + function onDwlStateUpdate(data) { + if (dwlAvailable) { + handleStateUpdate(data) + } + } + } + + Component.onCompleted: { + if (DMSService.dmsAvailable) { + checkCapabilities() + } + } + + function checkCapabilities() { + if (!DMSService.capabilities || !Array.isArray(DMSService.capabilities)) { + dwlAvailable = false + return + } + + const hasDwl = DMSService.capabilities.includes("dwl") + if (hasDwl && !dwlAvailable) { + dwlAvailable = true + console.info("DwlService: DWL capability detected") + requestState() + } else if (!hasDwl) { + dwlAvailable = false + } + } + + function requestState() { + if (!DMSService.isConnected || !dwlAvailable) { + return + } + + DMSService.sendRequest("dwl.getState", null, response => { + if (response.result) { + handleStateUpdate(response.result) + } + }) + } + + function handleStateUpdate(state) { + outputs = state.outputs || {} + tagCount = state.tagCount || 0 + layouts = state.layouts || [] + activeOutput = state.activeOutput || "" + stateChanged() + } + + function setTags(outputName, tagmask, toggleTagset) { + if (!DMSService.isConnected || !dwlAvailable) { + return + } + + DMSService.sendRequest("dwl.setTags", { + "output": outputName, + "tagmask": tagmask, + "toggleTagset": toggleTagset + }, response => { + if (response.error) { + console.warn("DwlService: setTags error:", response.error) + } + }) + } + + function setClientTags(outputName, andTags, xorTags) { + if (!DMSService.isConnected || !dwlAvailable) { + return + } + + DMSService.sendRequest("dwl.setClientTags", { + "output": outputName, + "andTags": andTags, + "xorTags": xorTags + }, response => { + if (response.error) { + console.warn("DwlService: setClientTags error:", response.error) + } + }) + } + + function setLayout(outputName, index) { + if (!DMSService.isConnected || !dwlAvailable) { + return + } + + DMSService.sendRequest("dwl.setLayout", { + "output": outputName, + "index": index + }, response => { + if (response.error) { + console.warn("DwlService: setLayout error:", response.error) + } + }) + } + + function getOutputState(outputName) { + if (!outputs || !outputs[outputName]) { + return null + } + return outputs[outputName] + } + + function getActiveTags(outputName) { + const output = getOutputState(outputName) + if (!output || !output.tags) { + return [] + } + return output.tags.filter(tag => tag.state === 1).map(tag => tag.tag) + } + + function getTagsWithClients(outputName) { + const output = getOutputState(outputName) + if (!output || !output.tags) { + return [] + } + return output.tags.filter(tag => tag.clients > 0).map(tag => tag.tag) + } + + function getUrgentTags(outputName) { + const output = getOutputState(outputName) + if (!output || !output.tags) { + return [] + } + return output.tags.filter(tag => tag.state === 2).map(tag => tag.tag) + } + + function switchToTag(outputName, tagIndex) { + const tagmask = 1 << tagIndex + setTags(outputName, tagmask, 0) + } + + function toggleTag(outputName, tagIndex) { + const tagmask = 1 << tagIndex + setTags(outputName, tagmask, 1) + } + + function quit() { + Quickshell.execDetached(["mmsg", "-d", "quit"]) + } +} diff --git a/Services/SessionService.qml b/Services/SessionService.qml index bd75a650..b34e8ce1 100644 --- a/Services/SessionService.qml +++ b/Services/SessionService.qml @@ -184,7 +184,11 @@ Singleton { return } - // Hyprland fallback + if (CompositorService.isDwl) { + DwlService.quit() + return + } + Hyprland.dispatch("exit") } else { Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout]) diff --git a/assets/danklogo.svg b/assets/danklogo.svg new file mode 100644 index 00000000..f120b5d7 --- /dev/null +++ b/assets/danklogo.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/mango.png b/assets/mango.png new file mode 100644 index 00000000..7b9b4c64 Binary files /dev/null and b/assets/mango.png differ