mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -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:
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -24,6 +24,7 @@ assignees: ""
|
|||||||
|
|
||||||
- [ ] niri
|
- [ ] niri
|
||||||
- [ ] Hyprland
|
- [ ] Hyprland
|
||||||
|
- [ ] dwl (MangoWC)
|
||||||
- [ ] Other (specify)
|
- [ ] Other (specify)
|
||||||
|
|
||||||
## Distribution
|
## Distribution
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -21,6 +21,7 @@ Is this feature specific to one compositor?
|
|||||||
- [ ] All compositors
|
- [ ] All compositors
|
||||||
- [ ] niri
|
- [ ] niri
|
||||||
- [ ] Hyprland
|
- [ ] Hyprland
|
||||||
|
- [ ] dwl (MangoWC)
|
||||||
|
|
||||||
## Proposed Solution
|
## Proposed Solution
|
||||||
|
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/support_request.md
vendored
1
.github/ISSUE_TEMPLATE/support_request.md
vendored
@@ -10,6 +10,7 @@ assignees: ""
|
|||||||
|
|
||||||
- [ ] niri
|
- [ ] niri
|
||||||
- [ ] Hyprland
|
- [ ] Hyprland
|
||||||
|
- [ ] dwl (MangoWC)
|
||||||
- [ ] other
|
- [ ] other
|
||||||
|
|
||||||
## Distribution
|
## Distribution
|
||||||
|
|||||||
@@ -580,6 +580,11 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return monitorWorkspaces.sort((a, b) => a.id - b.id)
|
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]
|
return [1]
|
||||||
}
|
}
|
||||||
@@ -595,6 +600,12 @@ Item {
|
|||||||
const monitors = Hyprland.monitors?.values || []
|
const monitors = Hyprland.monitors?.values || []
|
||||||
const currentMonitor = monitors.find(monitor => monitor.name === barWindow.screenName)
|
const currentMonitor = monitors.find(monitor => monitor.name === barWindow.screenName)
|
||||||
return currentMonitor?.activeWorkspace?.id ?? 1
|
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
|
return 1
|
||||||
}
|
}
|
||||||
@@ -623,6 +634,15 @@ Item {
|
|||||||
if (nextIndex !== validIndex) {
|
if (nextIndex !== validIndex) {
|
||||||
Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`)
|
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])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ BasePill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
visible: SettingsData.launcherLogoMode === "compositor"
|
visible: SettingsData.launcherLogoMode === "compositor" && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl)
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
|
||||||
height: 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"
|
return "file://" + Theme.shellDir + "/assets/niri.svg"
|
||||||
} else if (CompositorService.isHyprland) {
|
} else if (CompositorService.isHyprland) {
|
||||||
return "file://" + Theme.shellDir + "/assets/hyprland.svg"
|
return "file://" + Theme.shellDir + "/assets/hyprland.svg"
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return "file://" + Theme.shellDir + "/assets/mango.png"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,11 +28,14 @@ Item {
|
|||||||
_desktopEntriesUpdateTrigger++
|
_desktopEntriesUpdateTrigger++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property int currentWorkspace: {
|
property int currentWorkspace: {
|
||||||
if (CompositorService.isNiri) {
|
if (CompositorService.isNiri) {
|
||||||
return getNiriActiveWorkspace()
|
return getNiriActiveWorkspace()
|
||||||
} else if (CompositorService.isHyprland) {
|
} else if (CompositorService.isHyprland) {
|
||||||
return getHyprlandActiveWorkspace()
|
return getHyprlandActiveWorkspace()
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return getDwlActiveTag()
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -47,6 +50,10 @@ Item {
|
|||||||
const filteredList = baseList.filter(ws => ws.id > -1)
|
const filteredList = baseList.filter(ws => ws.id > -1)
|
||||||
return SettingsData.showWorkspacePadding ? padWorkspaces(filteredList) : filteredList
|
return SettingsData.showWorkspacePadding ? padWorkspaces(filteredList) : filteredList
|
||||||
}
|
}
|
||||||
|
if (CompositorService.isDwl) {
|
||||||
|
const baseList = getDwlTags()
|
||||||
|
return SettingsData.showWorkspacePadding ? padWorkspaces(baseList) : baseList
|
||||||
|
}
|
||||||
return [1]
|
return [1]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,15 +76,30 @@ Item {
|
|||||||
targetWorkspaceId = workspace.id
|
targetWorkspaceId = workspace.id
|
||||||
} else if (CompositorService.isHyprland) {
|
} else if (CompositorService.isHyprland) {
|
||||||
targetWorkspaceId = ws.id !== undefined ? ws.id : ws
|
targetWorkspaceId = ws.id !== undefined ? ws.id : ws
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
if (typeof ws !== "object" || ws.tag === undefined) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
targetWorkspaceId = ws.tag
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const wins = CompositorService.isNiri ? (NiriService.windows || []) : CompositorService.sortedToplevels
|
const wins = CompositorService.isNiri ? (NiriService.windows || []) : CompositorService.sortedToplevels
|
||||||
|
|
||||||
|
|
||||||
const byApp = {}
|
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) => {
|
wins.forEach((w, i) => {
|
||||||
if (!w) {
|
if (!w) {
|
||||||
@@ -128,10 +150,14 @@ Item {
|
|||||||
|
|
||||||
function padWorkspaces(list) {
|
function padWorkspaces(list) {
|
||||||
const padded = list.slice()
|
const padded = list.slice()
|
||||||
const placeholder = CompositorService.isHyprland ? {
|
let placeholder
|
||||||
"id": -1,
|
if (CompositorService.isHyprland) {
|
||||||
"name": ""
|
placeholder = {"id": -1, "name": ""}
|
||||||
} : -1
|
} else if (CompositorService.isDwl) {
|
||||||
|
placeholder = {"tag": -1}
|
||||||
|
} else {
|
||||||
|
placeholder = -1
|
||||||
|
}
|
||||||
while (padded.length < 3) {
|
while (padded.length < 3) {
|
||||||
padded.push(placeholder)
|
padded.push(placeholder)
|
||||||
}
|
}
|
||||||
@@ -199,7 +225,6 @@ Item {
|
|||||||
return Hyprland.focusedWorkspace ? Hyprland.focusedWorkspace.id : 1
|
return Hyprland.focusedWorkspace ? Hyprland.focusedWorkspace.id : 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the monitor object for this screen
|
|
||||||
const monitors = Hyprland.monitors?.values || []
|
const monitors = Hyprland.monitors?.values || []
|
||||||
const currentMonitor = monitors.find(monitor => monitor.name === root.screenName)
|
const currentMonitor = monitors.find(monitor => monitor.name === root.screenName)
|
||||||
|
|
||||||
@@ -207,10 +232,41 @@ Item {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the monitor's active workspace ID (like original config)
|
|
||||||
return currentMonitor.activeWorkspace?.id ?? 1
|
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 padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
||||||
readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
|
readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
|
||||||
readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
|
readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
|
||||||
@@ -219,6 +275,8 @@ Item {
|
|||||||
return root.workspaceList.filter(ws => {
|
return root.workspaceList.filter(ws => {
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
return ws && ws.id !== -1
|
return ws && ws.id !== -1
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return ws && ws.tag !== -1
|
||||||
}
|
}
|
||||||
return ws !== -1
|
return ws !== -1
|
||||||
})
|
})
|
||||||
@@ -255,12 +313,27 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Hyprland.dispatch(`workspace ${realWorkspaces[nextIndex].id}`)
|
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
|
width: isVertical ? barThickness : visualWidth
|
||||||
height: isVertical ? visualHeight : barThickness
|
height: isVertical ? visualHeight : barThickness
|
||||||
visible: CompositorService.isNiri || CompositorService.isHyprland
|
visible: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: visualBackground
|
id: visualBackground
|
||||||
@@ -303,12 +376,16 @@ Item {
|
|||||||
property bool isActive: {
|
property bool isActive: {
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
return modelData && modelData.id === root.currentWorkspace
|
return modelData && modelData.id === root.currentWorkspace
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return modelData && modelData.tag === root.currentWorkspace
|
||||||
}
|
}
|
||||||
return modelData === root.currentWorkspace
|
return modelData === root.currentWorkspace
|
||||||
}
|
}
|
||||||
property bool isPlaceholder: {
|
property bool isPlaceholder: {
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
return modelData && modelData.id === -1
|
return modelData && modelData.id === -1
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return modelData && modelData.tag === -1
|
||||||
}
|
}
|
||||||
return modelData === -1
|
return modelData === -1
|
||||||
}
|
}
|
||||||
@@ -319,9 +396,10 @@ Item {
|
|||||||
property bool isUrgent: {
|
property bool isUrgent: {
|
||||||
if (CompositorService.isHyprland) {
|
if (CompositorService.isHyprland) {
|
||||||
return modelData?.urgent ?? false
|
return modelData?.urgent ?? false
|
||||||
}
|
} else if (CompositorService.isNiri) {
|
||||||
if (CompositorService.isNiri) {
|
|
||||||
return loadedIsUrgent
|
return loadedIsUrgent
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
return modelData?.state === 2
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -372,6 +450,8 @@ Item {
|
|||||||
NiriService.switchToWorkspace(modelData - 1)
|
NiriService.switchToWorkspace(modelData - 1)
|
||||||
} else if (CompositorService.isHyprland && modelData?.id) {
|
} else if (CompositorService.isHyprland && modelData?.id) {
|
||||||
Hyprland.dispatch(`workspace ${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;
|
wsData = NiriService.allWorkspaces.find(ws => ws.idx + 1 === modelData && ws.output === root.screenName) || null;
|
||||||
} else if (CompositorService.isHyprland) {
|
} else if (CompositorService.isHyprland) {
|
||||||
wsData = modelData;
|
wsData = modelData;
|
||||||
|
} else if (CompositorService.isDwl) {
|
||||||
|
wsData = modelData;
|
||||||
}
|
}
|
||||||
delegateRoot.loadedWorkspaceData = wsData;
|
delegateRoot.loadedWorkspaceData = wsData;
|
||||||
delegateRoot.loadedIsUrgent = wsData?.is_urgent ?? false;
|
delegateRoot.loadedIsUrgent = wsData?.is_urgent ?? false;
|
||||||
@@ -406,7 +488,11 @@ Item {
|
|||||||
delegateRoot.loadedHasIcon = icData !== null;
|
delegateRoot.loadedHasIcon = icData !== null;
|
||||||
|
|
||||||
if (SettingsData.showWorkspaceApps) {
|
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 {
|
} else {
|
||||||
delegateRoot.loadedIcons = [];
|
delegateRoot.loadedIcons = [];
|
||||||
}
|
}
|
||||||
@@ -651,11 +737,25 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: {
|
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) {
|
if (isPlaceholder) {
|
||||||
return index + 1
|
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
|
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)
|
font.pixelSize: Theme.barTextSize(barThickness)
|
||||||
@@ -683,6 +783,11 @@ Item {
|
|||||||
function onShowWorkspaceAppsChanged() { delegateRoot.updateAllData() }
|
function onShowWorkspaceAppsChanged() { delegateRoot.updateAllData() }
|
||||||
function onWorkspaceNameIconsChanged() { delegateRoot.updateAllData() }
|
function onWorkspaceNameIconsChanged() { delegateRoot.updateAllData() }
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: DwlService
|
||||||
|
enabled: CompositorService.isDwl
|
||||||
|
function onStateChanged() { delegateRoot.updateAllData() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,9 +87,14 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
model: {
|
model: {
|
||||||
const modes = [I18n.tr("Apps Icon"), I18n.tr("OS Logo")]
|
const modes = [I18n.tr("Apps Icon"), I18n.tr("OS Logo")]
|
||||||
if (CompositorService.isNiri || CompositorService.isHyprland) {
|
if (CompositorService.isNiri) {
|
||||||
const compositorName = CompositorService.isNiri ? "niri" : "Hyprland"
|
modes.push("niri")
|
||||||
modes.push(compositorName)
|
} 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"))
|
modes.push(I18n.tr("Custom"))
|
||||||
return modes
|
return modes
|
||||||
@@ -97,12 +102,8 @@ Item {
|
|||||||
currentIndex: {
|
currentIndex: {
|
||||||
if (SettingsData.launcherLogoMode === "apps") return 0
|
if (SettingsData.launcherLogoMode === "apps") return 0
|
||||||
if (SettingsData.launcherLogoMode === "os") return 1
|
if (SettingsData.launcherLogoMode === "os") return 1
|
||||||
if (SettingsData.launcherLogoMode === "compositor") {
|
if (SettingsData.launcherLogoMode === "compositor") return 2
|
||||||
return (CompositorService.isNiri || CompositorService.isHyprland) ? 2 : -1
|
if (SettingsData.launcherLogoMode === "custom") return 3
|
||||||
}
|
|
||||||
if (SettingsData.launcherLogoMode === "custom") {
|
|
||||||
return (CompositorService.isNiri || CompositorService.isHyprland) ? 3 : 2
|
|
||||||
}
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
onSelectionChanged: (index, selected) => {
|
onSelectionChanged: (index, selected) => {
|
||||||
@@ -111,13 +112,9 @@ Item {
|
|||||||
SettingsData.setLauncherLogoMode("apps")
|
SettingsData.setLauncherLogoMode("apps")
|
||||||
} else if (index === 1) {
|
} else if (index === 1) {
|
||||||
SettingsData.setLauncherLogoMode("os")
|
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) {
|
} else if (index === 2) {
|
||||||
|
SettingsData.setLauncherLogoMode("compositor")
|
||||||
|
} else if (index === 3) {
|
||||||
SettingsData.setLauncherLogoMode("custom")
|
SettingsData.setLauncherLogoMode("custom")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
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).
|
Features notifications, app launcher, wallpaper customization, and fully customizable with [plugins](https://github.com/AvengeMedia/dms-plugin-registry).
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Singleton {
|
|||||||
|
|
||||||
property bool isHyprland: false
|
property bool isHyprland: false
|
||||||
property bool isNiri: false
|
property bool isNiri: false
|
||||||
|
property bool isDwl: false
|
||||||
property string compositor: "unknown"
|
property string compositor: "unknown"
|
||||||
|
|
||||||
readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
|
readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
|
||||||
@@ -87,6 +88,15 @@ Singleton {
|
|||||||
Qt.callLater(() => NiriService.generateNiriLayoutConfig())
|
Qt.callLater(() => NiriService.generateNiriLayoutConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: DwlService
|
||||||
|
function onStateChanged() {
|
||||||
|
if (isDwl && !isHyprland && !isNiri) {
|
||||||
|
scheduleSort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function computeSortedToplevels() {
|
function computeSortedToplevels() {
|
||||||
if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values)
|
if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values)
|
||||||
return []
|
return []
|
||||||
@@ -331,6 +341,7 @@ Singleton {
|
|||||||
if (hyprlandSignature && hyprlandSignature.length > 0) {
|
if (hyprlandSignature && hyprlandSignature.length > 0) {
|
||||||
isHyprland = true
|
isHyprland = true
|
||||||
isNiri = false
|
isNiri = false
|
||||||
|
isDwl = false
|
||||||
compositor = "hyprland"
|
compositor = "hyprland"
|
||||||
console.info("CompositorService: Detected Hyprland")
|
console.info("CompositorService: Detected Hyprland")
|
||||||
try {
|
try {
|
||||||
@@ -344,6 +355,7 @@ Singleton {
|
|||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
isNiri = true
|
isNiri = true
|
||||||
isHyprland = false
|
isHyprland = false
|
||||||
|
isDwl = false
|
||||||
compositor = "niri"
|
compositor = "niri"
|
||||||
console.info("CompositorService: Detected Niri with socket:", niriSocket)
|
console.info("CompositorService: Detected Niri with socket:", niriSocket)
|
||||||
NiriService.generateNiriBinds()
|
NiriService.generateNiriBinds()
|
||||||
@@ -351,27 +363,82 @@ Singleton {
|
|||||||
} else {
|
} else {
|
||||||
isHyprland = false
|
isHyprland = false
|
||||||
isNiri = true
|
isNiri = true
|
||||||
|
isDwl = false
|
||||||
compositor = "niri"
|
compositor = "niri"
|
||||||
console.warn("CompositorService: Niri socket check failed, defaulting to Niri anyway")
|
console.warn("CompositorService: Niri socket check failed, defaulting to Niri anyway")
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0)
|
||||||
} else {
|
} 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
|
isHyprland = false
|
||||||
isNiri = false
|
isNiri = false
|
||||||
compositor = "unknown"
|
isDwl = true
|
||||||
console.warn("CompositorService: No compositor detected")
|
compositor = "dwl"
|
||||||
|
console.info("CompositorService: Detected DWL via DMS capability")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function powerOffMonitors() {
|
function powerOffMonitors() {
|
||||||
if (isNiri) return NiriService.powerOffMonitors()
|
if (isNiri) return NiriService.powerOffMonitors()
|
||||||
if (isHyprland) return Hyprland.dispatch("dpms off")
|
if (isHyprland) return Hyprland.dispatch("dpms off")
|
||||||
|
if (isDwl) return _dwlPowerOffMonitors()
|
||||||
console.warn("CompositorService: Cannot power off monitors, unknown compositor")
|
console.warn("CompositorService: Cannot power off monitors, unknown compositor")
|
||||||
}
|
}
|
||||||
|
|
||||||
function powerOnMonitors() {
|
function powerOnMonitors() {
|
||||||
if (isNiri) return NiriService.powerOnMonitors()
|
if (isNiri) return NiriService.powerOnMonitors()
|
||||||
if (isHyprland) return Hyprland.dispatch("dpms on")
|
if (isHyprland) return Hyprland.dispatch("dpms on")
|
||||||
|
if (isDwl) return _dwlPowerOnMonitors()
|
||||||
console.warn("CompositorService: Cannot power on monitors, unknown compositor")
|
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"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ Singleton {
|
|||||||
signal capabilitiesReceived()
|
signal capabilitiesReceived()
|
||||||
signal credentialsRequest(var data)
|
signal credentialsRequest(var data)
|
||||||
signal bluetoothPairingRequest(var data)
|
signal bluetoothPairingRequest(var data)
|
||||||
|
signal dwlStateUpdate(var data)
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (socketPath && socketPath.length > 0) {
|
if (socketPath && socketPath.length > 0) {
|
||||||
@@ -266,6 +267,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
} else if (service === "bluetooth.pairing") {
|
} else if (service === "bluetooth.pairing") {
|
||||||
bluetoothPairingRequest(data)
|
bluetoothPairingRequest(data)
|
||||||
|
} else if (service === "dwl") {
|
||||||
|
dwlStateUpdate(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
170
Services/DwlService.qml
Normal file
170
Services/DwlService.qml
Normal 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"])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -184,7 +184,11 @@ Singleton {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hyprland fallback
|
if (CompositorService.isDwl) {
|
||||||
|
DwlService.quit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Hyprland.dispatch("exit")
|
Hyprland.dispatch("exit")
|
||||||
} else {
|
} else {
|
||||||
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout])
|
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout])
|
||||||
|
|||||||
3
assets/danklogo.svg
Normal file
3
assets/danklogo.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" fill="#fff">
|
||||||
|
<path fill-rule="evenodd" style="shape-rendering:geometricPrecision" d="M580 70q8-3 15 2l15 58 20 60 20 50q8 7 30 12l40 13 35 13q19 9 10 17l-145 2-160-2h-80l-35 13q-6 8 5 12l55-10q-1-8 5-8l110 2 30 10 40-4 10-4 100-2q8 0 8 6t-6 8v22l-4 30-8 25q-7 11-30 15l40 15 70 15q21 7 20 22 1 14-20 18l-130 2-5 18-10 40-5 40q-7 31-30 55a48 48 0 0 1-40 13l-70-28-80-45-60-45-12-30-10-60-6-60q0-21 6-50l-73-2q-19-3-13-18l28-15 60-20 15-40 25-95 10-40q8-24 30-25l80 7h40l25-6Zm-135 130 95 2 90 8 12 30-32-2-90-3-110 3-50 6-15-29 25-9Zm120 135q8-4 13 3l17 27 15 20q15 15 45 30l50 17 65 16q15 6 15 17 0 5-15 5l-130-5-60-10-60-3q-15 0-28-6-6-6-4-18l12-18 30-25 15-25Zm-143 0q3 5 6 20l7 25q8 15 30 18 5 0 5 2-15 3-28-5l-12-17-8-30Zm283-3v20l-5 23-10 15q-8 8-28 8 0-3 8-4l20-8 8-16Zm-375 218 30 50 45 70 40 60 35 70 35 55 20-55 10-60-15-30 15-20 15-15 30 20-5 25 15 50 15 60 7-50 3-60v-120l10 10 15 15 25 25 35 30 40 25-5 25-25 50-25 45-30 50-35 55-35 40-20 5-35-25-70-55-80-60-70-55-80-65-60-40 20-15 50-30 35-25 5-15 15-25Zm238-182q4 3 4 8-3 3-9 0Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/mango.png
Normal file
BIN
assets/mango.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
Reference in New Issue
Block a user