1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-12 08:42:13 -04:00

miraclewm: add support for Miracle WM

This commit is contained in:
bbedward
2026-02-16 23:00:07 -05:00
parent d62bdda56b
commit 0b33d3f905
26 changed files with 1889 additions and 163 deletions

View File

@@ -66,7 +66,7 @@ Singleton {
return Hyprland.focusedWorkspace.monitor.name;
if (CompositorService.isNiri && NiriService.currentOutput)
return NiriService.currentOutput;
if (CompositorService.isSway || CompositorService.isScroll) {
if (CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true);
return focusedWs?.monitor?.name || "";
}

View File

@@ -16,6 +16,7 @@ Singleton {
property bool isDwl: false
property bool isSway: false
property bool isScroll: false
property bool isMiracle: false
property bool isLabwc: false
property string compositor: "unknown"
readonly property bool useHyprlandFocusGrab: isHyprland && Quickshell.env("DMS_HYPRLAND_EXCLUSIVE_FOCUS") !== "1"
@@ -24,6 +25,7 @@ Singleton {
readonly property string niriSocket: Quickshell.env("NIRI_SOCKET")
readonly property string swaySocket: Quickshell.env("SWAYSOCK")
readonly property string scrollSocket: Quickshell.env("SWAYSOCK")
readonly property string miracleSocket: Quickshell.env("MIRACLESOCK")
readonly property string labwcPid: Quickshell.env("LABWC_PID")
property bool useNiriSorting: isNiri && NiriService
@@ -74,7 +76,7 @@ Singleton {
screenName = Hyprland.focusedWorkspace.monitor.name;
else if (isNiri && NiriService.currentOutput)
screenName = NiriService.currentOutput;
else if (isSway || isScroll) {
else if (isSway || isScroll || isMiracle) {
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true);
screenName = focusedWs?.monitor?.name || "";
} else if (isDwl && DwlService.activeOutput)
@@ -443,12 +445,13 @@ Singleton {
}
function detectCompositor() {
if (hyprlandSignature && hyprlandSignature.length > 0 && !niriSocket && !swaySocket && !scrollSocket && !labwcPid) {
if (hyprlandSignature && hyprlandSignature.length > 0 && !niriSocket && !swaySocket && !scrollSocket && !miracleSocket && !labwcPid) {
isHyprland = true;
isNiri = false;
isDwl = false;
isSway = false;
isScroll = false;
isMiracle = false;
isLabwc = false;
compositor = "hyprland";
console.info("CompositorService: Detected Hyprland");
@@ -463,6 +466,7 @@ Singleton {
isDwl = false;
isSway = false;
isScroll = false;
isMiracle = false;
isLabwc = false;
compositor = "niri";
console.info("CompositorService: Detected Niri with socket:", niriSocket);
@@ -472,7 +476,7 @@ Singleton {
return;
}
if (swaySocket && swaySocket.length > 0 && !scrollSocket && scrollSocket.length == 0) {
if (swaySocket && swaySocket.length > 0 && !scrollSocket && scrollSocket.length == 0 && !miracleSocket) {
Proc.runCommand("swaySocketCheck", ["test", "-S", swaySocket], (output, exitCode) => {
if (exitCode === 0) {
isNiri = false;
@@ -480,6 +484,7 @@ Singleton {
isDwl = false;
isSway = true;
isScroll = false;
isMiracle = false;
isLabwc = false;
compositor = "sway";
console.info("CompositorService: Detected Sway with socket:", swaySocket);
@@ -488,7 +493,24 @@ Singleton {
return;
}
if (scrollSocket && scrollSocket.length > 0) {
if (miracleSocket && miracleSocket.length > 0) {
Proc.runCommand("miracleSocketCheck", ["test", "-S", miracleSocket], (output, exitCode) => {
if (exitCode === 0) {
isNiri = false;
isHyprland = false;
isDwl = false;
isSway = false;
isScroll = false;
isMiracle = true;
isLabwc = false;
compositor = "miracle";
console.info("CompositorService: Detected Miracle WM with socket:", miracleSocket);
}
}, 0);
return;
}
if (scrollSocket && scrollSocket.length > 0 && !miracleSocket) {
Proc.runCommand("scrollSocketCheck", ["test", "-S", scrollSocket], (output, exitCode) => {
if (exitCode === 0) {
isNiri = false;
@@ -496,6 +518,7 @@ Singleton {
isDwl = false;
isSway = false;
isScroll = true;
isMiracle = false;
isLabwc = false;
compositor = "scroll";
console.info("CompositorService: Detected Scroll with socket:", scrollSocket);
@@ -510,6 +533,7 @@ Singleton {
isDwl = false;
isSway = false;
isScroll = false;
isMiracle = false;
isLabwc = true;
compositor = "labwc";
console.info("CompositorService: Detected LabWC with PID:", labwcPid);
@@ -524,6 +548,7 @@ Singleton {
isDwl = false;
isSway = false;
isScroll = false;
isMiracle = false;
isLabwc = false;
compositor = "unknown";
console.warn("CompositorService: No compositor detected");
@@ -546,6 +571,7 @@ Singleton {
isDwl = true;
isSway = false;
isScroll = false;
isMiracle = false;
isLabwc = false;
compositor = "dwl";
console.info("CompositorService: Detected DWL via DMS capability");
@@ -559,7 +585,7 @@ Singleton {
return Hyprland.dispatch("dpms off");
if (isDwl)
return _dwlPowerOffMonitors();
if (isSway || isScroll) {
if (isSway || isScroll || isMiracle) {
try {
I3.dispatch("output * dpms off");
} catch (_) {}
@@ -578,7 +604,7 @@ Singleton {
return Hyprland.dispatch("dpms on");
if (isDwl)
return _dwlPowerOnMonitors();
if (isSway || isScroll) {
if (isSway || isScroll || isMiracle) {
try {
I3.dispatch("output * dpms on");
} catch (_) {}

View File

@@ -11,83 +11,83 @@ Singleton {
property var groups: []
property var _cachedWorkspaces: ({})
signal stateChanged()
signal stateChanged
Connections {
target: DMSService
function onCapabilitiesReceived() {
checkCapabilities()
checkCapabilities();
}
function onConnectionStateChanged() {
if (DMSService.isConnected) {
checkCapabilities()
checkCapabilities();
} else {
extWorkspaceAvailable = false
extWorkspaceAvailable = false;
}
}
function onExtWorkspaceStateUpdate(data) {
if (extWorkspaceAvailable) {
handleStateUpdate(data)
handleStateUpdate(data);
}
}
}
Component.onCompleted: {
if (DMSService.dmsAvailable) {
checkCapabilities()
checkCapabilities();
}
}
function checkCapabilities() {
if (!DMSService.capabilities || !Array.isArray(DMSService.capabilities)) {
extWorkspaceAvailable = false
return
extWorkspaceAvailable = false;
return;
}
const hasExtWorkspace = DMSService.capabilities.includes("extworkspace")
const hasExtWorkspace = DMSService.capabilities.includes("extworkspace");
if (hasExtWorkspace && !extWorkspaceAvailable) {
if (typeof CompositorService !== "undefined") {
const useExtWorkspace = DMSService.forceExtWorkspace || (!CompositorService.isNiri && !CompositorService.isHyprland && !CompositorService.isDwl && !CompositorService.isSway && !CompositorService.isScroll)
const useExtWorkspace = DMSService.forceExtWorkspace || (!CompositorService.isNiri && !CompositorService.isHyprland && !CompositorService.isDwl && !CompositorService.isSway && !CompositorService.isScroll && !CompositorService.isMiracle);
if (!useExtWorkspace) {
console.info("ExtWorkspaceService: ext-workspace available but compositor has native support")
extWorkspaceAvailable = false
return
console.info("ExtWorkspaceService: ext-workspace available but compositor has native support");
extWorkspaceAvailable = false;
return;
}
}
extWorkspaceAvailable = true
console.info("ExtWorkspaceService: ext-workspace capability detected")
DMSService.addSubscription("extworkspace")
requestState()
extWorkspaceAvailable = true;
console.info("ExtWorkspaceService: ext-workspace capability detected");
DMSService.addSubscription("extworkspace");
requestState();
} else if (!hasExtWorkspace) {
extWorkspaceAvailable = false
extWorkspaceAvailable = false;
}
}
function requestState() {
if (!DMSService.isConnected || !extWorkspaceAvailable) {
return
return;
}
DMSService.sendRequest("extworkspace.getState", null, response => {
if (response.result) {
handleStateUpdate(response.result)
handleStateUpdate(response.result);
}
})
});
}
function handleStateUpdate(state) {
groups = state.groups || []
groups = state.groups || [];
if (groups.length === 0) {
console.warn("ExtWorkspaceService: Received empty workspace groups from backend")
console.warn("ExtWorkspaceService: Received empty workspace groups from backend");
} else {
console.log("ExtWorkspaceService: Updated with", groups.length, "workspace groups")
console.log("ExtWorkspaceService: Updated with", groups.length, "workspace groups");
}
stateChanged()
stateChanged();
}
function activateWorkspace(workspaceID, groupID = "") {
if (!DMSService.isConnected || !extWorkspaceAvailable) {
return
return;
}
DMSService.sendRequest("extworkspace.activateWorkspace", {
@@ -95,14 +95,14 @@ Singleton {
"groupID": groupID
}, response => {
if (response.error) {
console.warn("ExtWorkspaceService: activateWorkspace error:", response.error)
console.warn("ExtWorkspaceService: activateWorkspace error:", response.error);
}
})
});
}
function deactivateWorkspace(workspaceID, groupID = "") {
if (!DMSService.isConnected || !extWorkspaceAvailable) {
return
return;
}
DMSService.sendRequest("extworkspace.deactivateWorkspace", {
@@ -110,14 +110,14 @@ Singleton {
"groupID": groupID
}, response => {
if (response.error) {
console.warn("ExtWorkspaceService: deactivateWorkspace error:", response.error)
console.warn("ExtWorkspaceService: deactivateWorkspace error:", response.error);
}
})
});
}
function removeWorkspace(workspaceID, groupID = "") {
if (!DMSService.isConnected || !extWorkspaceAvailable) {
return
return;
}
DMSService.sendRequest("extworkspace.removeWorkspace", {
@@ -125,14 +125,14 @@ Singleton {
"groupID": groupID
}, response => {
if (response.error) {
console.warn("ExtWorkspaceService: removeWorkspace error:", response.error)
console.warn("ExtWorkspaceService: removeWorkspace error:", response.error);
}
})
});
}
function createWorkspace(groupID, name) {
if (!DMSService.isConnected || !extWorkspaceAvailable) {
return
return;
}
DMSService.sendRequest("extworkspace.createWorkspace", {
@@ -140,134 +140,138 @@ Singleton {
"name": name
}, response => {
if (response.error) {
console.warn("ExtWorkspaceService: createWorkspace error:", response.error)
console.warn("ExtWorkspaceService: createWorkspace error:", response.error);
}
})
});
}
function getGroupForOutput(outputName) {
for (const group of groups) {
if (group.outputs && group.outputs.includes(outputName)) {
return group
return group;
}
}
return null
return null;
}
function getWorkspacesForOutput(outputName) {
const group = getGroupForOutput(outputName)
return group ? (group.workspaces || []) : []
const group = getGroupForOutput(outputName);
return group ? (group.workspaces || []) : [];
}
function getActiveWorkspaces() {
const active = []
const active = [];
for (const group of groups) {
if (!group.workspaces) continue
if (!group.workspaces)
continue;
for (const ws of group.workspaces) {
if (ws.active) {
active.push({
workspace: ws,
group: group,
outputs: group.outputs || []
})
});
}
}
}
return active
return active;
}
function getActiveWorkspaceForOutput(outputName) {
const group = getGroupForOutput(outputName)
if (!group || !group.workspaces) return null
const group = getGroupForOutput(outputName);
if (!group || !group.workspaces)
return null;
for (const ws of group.workspaces) {
if (ws.active) {
return ws
return ws;
}
}
return null
return null;
}
function getVisibleWorkspaces(outputName) {
const workspaces = getWorkspacesForOutput(outputName)
let visible = workspaces.filter(ws => !ws.hidden)
const workspaces = getWorkspacesForOutput(outputName);
let visible = workspaces.filter(ws => !ws.hidden);
const hasValidCoordinates = visible.some(ws => ws.coordinates && ws.coordinates.length > 0)
const hasValidCoordinates = visible.some(ws => ws.coordinates && ws.coordinates.length > 0);
if (hasValidCoordinates) {
visible = visible.sort((a, b) => {
const coordsA = a.coordinates || [0, 0]
const coordsB = b.coordinates || [0, 0]
if (coordsA[0] !== coordsB[0]) return coordsA[0] - coordsB[0]
return coordsA[1] - coordsB[1]
})
const coordsA = a.coordinates || [0, 0];
const coordsB = b.coordinates || [0, 0];
if (coordsA[0] !== coordsB[0])
return coordsA[0] - coordsB[0];
return coordsA[1] - coordsB[1];
});
}
const cacheKey = outputName
const cacheKey = outputName;
if (!_cachedWorkspaces[cacheKey]) {
_cachedWorkspaces[cacheKey] = {
workspaces: [],
lastNames: []
}
};
}
const cache = _cachedWorkspaces[cacheKey]
const currentNames = visible.map(ws => ws.name || ws.id)
const namesChanged = JSON.stringify(cache.lastNames) !== JSON.stringify(currentNames)
const cache = _cachedWorkspaces[cacheKey];
const currentNames = visible.map(ws => ws.name || ws.id);
const namesChanged = JSON.stringify(cache.lastNames) !== JSON.stringify(currentNames);
if (namesChanged || cache.workspaces.length !== visible.length) {
cache.workspaces = visible.map(ws => ({
id: ws.id,
name: ws.name,
coordinates: ws.coordinates,
state: ws.state,
active: ws.active,
urgent: ws.urgent,
hidden: ws.hidden
}))
cache.lastNames = currentNames
return cache.workspaces
id: ws.id,
name: ws.name,
coordinates: ws.coordinates,
state: ws.state,
active: ws.active,
urgent: ws.urgent,
hidden: ws.hidden
}));
cache.lastNames = currentNames;
return cache.workspaces;
}
for (let i = 0; i < visible.length; i++) {
const src = visible[i]
const dst = cache.workspaces[i]
dst.id = src.id
dst.name = src.name
dst.coordinates = src.coordinates
dst.state = src.state
dst.active = src.active
dst.urgent = src.urgent
dst.hidden = src.hidden
const src = visible[i];
const dst = cache.workspaces[i];
dst.id = src.id;
dst.name = src.name;
dst.coordinates = src.coordinates;
dst.state = src.state;
dst.active = src.active;
dst.urgent = src.urgent;
dst.hidden = src.hidden;
}
return cache.workspaces
return cache.workspaces;
}
function getUrgentWorkspaces() {
const urgent = []
const urgent = [];
for (const group of groups) {
if (!group.workspaces) continue
if (!group.workspaces)
continue;
for (const ws of group.workspaces) {
if (ws.urgent) {
urgent.push({
workspace: ws,
group: group,
outputs: group.outputs || []
})
});
}
}
}
return urgent
return urgent;
}
function switchToWorkspace(outputName, workspaceName) {
const workspaces = getWorkspacesForOutput(outputName)
const workspaces = getWorkspacesForOutput(outputName);
for (const ws of workspaces) {
if (ws.name === workspaceName || ws.id === workspaceName) {
activateWorkspace(ws.name || ws.id)
return
activateWorkspace(ws.name || ws.id);
return;
}
}
console.warn("ExtWorkspaceService: workspace not found:", workspaceName)
console.warn("ExtWorkspaceService: workspace not found:", workspaceName);
}
}

View File

@@ -314,7 +314,7 @@ Singleton {
return;
}
if (CompositorService.isSway || CompositorService.isScroll) {
if (CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) {
try {
I3.dispatch("exit");
} catch (_) {}