1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

dwl: add dwl/MangoWC support

- Requires dms api v12
- Tags/Workspace support
- MangoWC launcher logo
- dpms off/on support
- logout support
This commit is contained in:
bbedward
2025-10-29 12:39:31 -04:00
parent 76b168020c
commit aede6b064a
14 changed files with 408 additions and 34 deletions

View File

@@ -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"])
}
}
}
}

View File

@@ -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)
}
}

170
Services/DwlService.qml Normal file
View File

@@ -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"])
}
}

View File

@@ -184,7 +184,11 @@ Singleton {
return
}
// Hyprland fallback
if (CompositorService.isDwl) {
DwlService.quit()
return
}
Hyprland.dispatch("exit")
} else {
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout])