From 8ba0a0a51f9bd8b3f7da6988bbe8eafb3e70780e Mon Sep 17 00:00:00 2001 From: atheeq-rhxn Date: Thu, 13 Nov 2025 20:27:40 +0530 Subject: [PATCH] refactor: Improve MangoWC implementation --- Services/Compositor/MangoService.qml | 861 +++++++++++++-------------- 1 file changed, 410 insertions(+), 451 deletions(-) diff --git a/Services/Compositor/MangoService.qml b/Services/Compositor/MangoService.qml index 3c7967b4..b7c9c1c7 100644 --- a/Services/Compositor/MangoService.qml +++ b/Services/Compositor/MangoService.qml @@ -2,17 +2,17 @@ import QtQuick import Quickshell import Quickshell.Io import qs.Commons -import qs.Services.Keyboard +import qs.Services.UI Item { id: root - // Properties that match the facade interface + // Properties matching facade interface property ListModel workspaces: ListModel {} property var windows: [] property int focusedWindowIndex: -1 - // Signals that match the facade interface + // Signals matching facade interface signal workspaceChanged signal activeWindowChanged signal windowListChanged @@ -21,12 +21,36 @@ Item { // Mango-specific properties property bool initialized: false property bool overviewActive: false - property var tagCache: ({}) + property var workspaceCache: ({}) property var windowCache: ({}) property var monitorCache: ({}) property string currentLayout: "" + property string currentLayoutSymbol: "" property string currentKeyboardLayout: "" + // Constants + readonly property var mmsgCommands: ({ + query: { + workspaces: ["mmsg", "-g", "-t"], + windows: ["mmsg", "-g", "-c"], + layout: ["mmsg", "-g", "-l"], + keyboard: ["mmsg", "-g", "-k"], + outputs: ["mmsg", "-g", "-A"], + eventStream: ["mmsg", "-w"] + }, + action: { + view: ["mmsg", "-d", "view"], + focusMaster: ["mmsg", "-d", "focusmaster"], + killClient: ["mmsg", "-d", "killclient"], + toggleOverview: ["mmsg", "-d", "toggleoverview"], + setLayout: ["mmsg", "-d", "setlayout"], + quit: ["mmsg", "-d", "quit"] + } + }) + + readonly property string overviewLayoutSymbol: "󰃇" + readonly property int defaultWorkspaceId: 1 + // Debounce timer for updates Timer { id: updateTimer @@ -35,53 +59,195 @@ Item { onTriggered: safeUpdate() } - // Initialization - function initialize() { - if (initialized) - return + // Event stream for real-time updates + Process { + id: eventStream + running: false + command: mmsgCommands.query.eventStream - try { - // Initial data fetch - updateWorkspaces() - updateWindows() - queryDisplayScales() - queryKeyboardLayout() - - // Start event watching - mangoEventStream.running = true - - initialized = true - Logger.i("MangoService", "Service started") - } catch (e) { - Logger.e("MangoService", "Failed to initialize:", e) + stdout: SplitParser { + onRead: function (line) { + try { + handleEvent(line.trim()) + } catch (e) { + Logger.e("MangoService", "Event parsing error:", e, line) + } + } + } + + onExited: function (exitCode) { + if (exitCode !== 0) { + Logger.e("MangoService", "Event stream exited, restarting...") + restartTimer.start() + } } } - // Update workspaces (tags in MangoWC) - function updateWorkspaces() { - mangoTagsProcess.running = true + Timer { + id: restartTimer + interval: 1000 + onTriggered: { + if (initialized) { + eventStream.running = true + } + } } - // Update windows - function updateWindows() { - mangoWindowsProcess.running = true - } - - // Query display scales - function queryDisplayScales() { - mangoOutputsProcess.running = true - } - - // Query keyboard layout - function queryKeyboardLayout() { - mangoKeyboardProcess.running = true - } - - // Mango outputs process for display scale detection + // Query processes Process { - id: mangoOutputsProcess + id: workspacesProcess running: false - command: ["mmsg", "-g", "-A"] + command: mmsgCommands.query.workspaces + property string accumulatedOutput: "" + + stdout: SplitParser { + onRead: function (line) { + workspacesProcess.accumulatedOutput += line + "\n" + } + } + + onExited: function (exitCode) { + if (exitCode === 0) { + parseWorkspaces(accumulatedOutput) + } else { + Logger.e("MangoService", "Workspaces query failed:", exitCode) + } + accumulatedOutput = "" + } + } + + Process { + id: windowsProcess + running: false + command: mmsgCommands.query.windows + property string accumulatedOutput: "" + property var currentWindow: ({}) + + onRunningChanged: { + if (running) { + currentWindow = {} + } + } + + stdout: SplitParser { + onRead: function (line) { + const trimmed = line.trim() + if (!trimmed) return + + const parts = trimmed.split(' ') + if (parts.length >= 3) { + const outputName = parts[0] + const property = parts[1] + const value = parts.slice(2).join(' ') + + if (!currentWindow[outputName]) { + currentWindow[outputName] = { + id: outputName, + output: outputName + } + } + + switch (property) { + case "title": + currentWindow[outputName].title = value + break + case "appid": + currentWindow[outputName].appId = value + currentWindow[outputName].class = value + break + case "fullscreen": + currentWindow[outputName].fullscreen = (value === "1") + break + case "floating": + currentWindow[outputName].floating = (value === "1") + break + case "x": + currentWindow[outputName].x = parseInt(value) + break + case "y": + currentWindow[outputName].y = parseInt(value) + break + case "width": + currentWindow[outputName].width = parseInt(value) + break + case "height": + currentWindow[outputName].height = parseInt(value) + break + } + } + } + } + + onExited: function (exitCode) { + if (exitCode === 0) { + parseWindows(currentWindow) + } else { + Logger.e("MangoService", "Windows query failed:", exitCode) + } + accumulatedOutput = "" + currentWindow = {} + } + } + + Process { + id: layoutProcess + running: false + command: mmsgCommands.query.layout + + stdout: SplitParser { + onRead: function (line) { + try { + const parts = line.trim().split(/\s+/) + if (parts.length >= 2) { + const layoutSymbol = parts.slice(1).join(' ') + handleLayoutChange(layoutSymbol) + } + } catch (e) { + Logger.e("MangoService", "Layout parsing error:", e, line) + } + } + } + + onExited: function (exitCode) { + if (exitCode !== 0) { + Logger.e("MangoService", "Layout query failed:", exitCode) + } + } + } + + Process { + id: keyboardProcess + running: false + command: mmsgCommands.query.keyboard + + stdout: SplitParser { + onRead: function (line) { + try { + const parts = line.trim().split(/\s+/) + if (parts.length >= 2 && parts[1] === "kb_layout") { + const layoutName = parts.slice(2).join(' ') + if (layoutName && layoutName !== currentKeyboardLayout) { + currentKeyboardLayout = layoutName + KeyboardLayoutService.setCurrentLayout(layoutName) + } + } + } catch (e) { + Logger.e("MangoService", "Keyboard layout parsing error:", e, line) + } + } + } + + onExited: function (exitCode) { + if (exitCode !== 0) { + Logger.e("MangoService", "Keyboard query failed:", exitCode) + } + } + } + + Process { + id: outputsProcess + running: false + command: mmsgCommands.query.outputs stdout: SplitParser { onRead: function (line) { @@ -99,364 +265,201 @@ Item { monitorCache[outputName].name = outputName } } catch (e) { - Logger.e("MangoService", "Failed to parse output scale:", e, line) + Logger.e("MangoService", "Output parsing error:", e, line) } } } onExited: function (exitCode) { - if (exitCode !== 0) { - Logger.e("MangoService", "Failed to query outputs, exit code:", exitCode) - return - } - - // Convert to expected format and notify - const scales = {} - for (const [outputName, data] of Object.entries(monitorCache)) { - scales[outputName] = { - "name": data.name, - "scale": data.scale || 1.0, - "width": data.width || 0, - "height": data.height || 0, - "refresh_rate": data.refresh_rate || 0, - "x": data.x || 0, - "y": data.y || 0, - "active": data.active || false, - "focused": data.focused || false - } - } - - // Notify CompositorService - if (CompositorService && CompositorService.onDisplayScalesUpdated) { - CompositorService.onDisplayScalesUpdated(scales) + if (exitCode === 0) { + updateDisplayScales() + } else { + Logger.e("MangoService", "Outputs query failed:", exitCode) } } } - // Mango tags process (workspaces) - Process { - id: mangoTagsProcess - running: false - command: ["mmsg", "-g", "-t"] - - property string accumulatedOutput: "" - - stdout: SplitParser { - onRead: function (line) { - mangoTagsProcess.accumulatedOutput += line + "\n" - } + // Initialization + function initialize() { + if (initialized) { + Logger.w("MangoService", "Already initialized") + return } - onExited: function (exitCode) { - if (exitCode !== 0) { - Logger.e("MangoService", "Failed to query tags, exit code:", exitCode) - accumulatedOutput = "" - return - } - - try { - parseTagsData(accumulatedOutput) - } catch (e) { - Logger.e("MangoService", "Failed to parse tags:", e) - } finally { - accumulatedOutput = "" - } + try { + Logger.i("MangoService", "Initializing MangoWC service...") + + eventStream.running = true + queryWorkspaces() + queryWindows() + queryLayout() + queryKeyboard() + queryOutputs() + + initialized = true + Logger.i("MangoService", "Service initialized successfully") + } catch (e) { + Logger.e("MangoService", "Initialization failed:", e) + eventStream.running = true } } - // Mango windows process - Process { - id: mangoWindowsProcess - running: false - command: ["mmsg", "-g", "-c"] - - property string accumulatedOutput: "" - property var currentWindow: ({}) - - onRunningChanged: { - if (running) { - currentWindow = {} - } + // Workspace operations + function switchToWorkspace(workspace) { + try { + const tagId = workspace.idx || workspace.id || defaultWorkspaceId + const command = mmsgCommands.action.view.concat([tagId.toString()]) + Quickshell.execDetached(command) + Logger.d("MangoService", `Switching to workspace ${tagId}`) + } catch (e) { + Logger.e("MangoService", "Failed to switch workspace:", e) } + } - stdout: SplitParser { - onRead: function (line) { - const trimmed = line.trim() - if (!trimmed) return - - // Format: output property value - // Example: eDP-1 title joyous-triceratops | ~/.config/quickshell> y - // Example: eDP-1 appid com.mitchellh.ghostty - const firstSpace = trimmed.indexOf(' ') - if (firstSpace === -1) return + // Window operations + function focusWindow(window) { + try { + if (window && window.workspaceId) { + const command = mmsgCommands.action.view.concat([window.workspaceId.toString()]) + Quickshell.execDetached(command) - const outputName = trimmed.substring(0, firstSpace) - const rest = trimmed.substring(firstSpace + 1).trim() - - const secondSpace = rest.indexOf(' ') - if (secondSpace === -1) return - - const property = rest.substring(0, secondSpace) - const value = rest.substring(secondSpace + 1).trim() - - - - if (!mangoWindowsProcess.currentWindow[outputName]) { - mangoWindowsProcess.currentWindow[outputName] = {} - } - - if (property === "title") { - mangoWindowsProcess.currentWindow[outputName].title = value - } else if (property === "appid") { - mangoWindowsProcess.currentWindow[outputName].appId = value - } else if (property === "fullscreen") { - mangoWindowsProcess.currentWindow[outputName].isFullscreen = value === "1" - } else if (property === "floating") { - mangoWindowsProcess.currentWindow[outputName].isFloating = value === "1" - } else if (property === "x") { - mangoWindowsProcess.currentWindow[outputName].x = parseInt(value) - } else if (property === "y") { - mangoWindowsProcess.currentWindow[outputName].y = parseInt(value) - } else if (property === "width") { - mangoWindowsProcess.currentWindow[outputName].width = parseInt(value) - } else if (property === "height") { - mangoWindowsProcess.currentWindow[outputName].height = parseInt(value) - } - } - } - - onExited: function (exitCode) { - if (exitCode !== 0) { - Logger.e("MangoService", "Failed to query windows, exit code:", exitCode) - accumulatedOutput = "" - currentWindow = {} - return - } - - try { - parseWindowsData(currentWindow) - } catch (e) { - Logger.e("MangoService", "Failed to parse windows:", e) - } finally { - currentWindow = {} + Qt.callLater(() => { + Quickshell.execDetached(mmsgCommands.action.focusMaster) + }) } + } catch (e) { + Logger.e("MangoService", "Failed to focus window:", e) } } - // Mango keyboard layout process - Process { - id: mangoKeyboardProcess - running: false - command: ["mmsg", "-g", "-k"] - - stdout: SplitParser { - onRead: function (line) { - try { - const parts = line.trim().split(/\s+/) - if (parts.length >= 2 && parts[1] === "kb_layout") { - const layoutName = parts.slice(2).join(' ') - if (layoutName && layoutName !== currentKeyboardLayout) { - currentKeyboardLayout = layoutName - KeyboardLayoutService.setCurrentLayout(layoutName) - - } - } - } catch (e) { - Logger.e("MangoService", "Failed to parse keyboard layout:", e, line) - } - } - } - - onExited: function (exitCode) { - if (exitCode !== 0) { - Logger.e("MangoService", "Failed to query keyboard layout, exit code:", exitCode) - } + function closeWindow() { + try { + Quickshell.execDetached(mmsgCommands.action.killClient) + } catch (e) { + Logger.e("MangoService", "Failed to close window:", e) } } - - - // Mango event stream process - Process { - id: mangoEventStream - running: false - command: ["mmsg", "-w"] - - stdout: SplitParser { - onRead: function (line) { - try { - handleEvent(line.trim()) - } catch (e) { - Logger.e("MangoService", "Error parsing event:", e, line) - } - } - } - - onExited: function (exitCode) { - if (exitCode !== 0) { - Logger.e("MangoService", "Event stream exited, exit code:", exitCode) - // Restart event stream after a delay - restartTimer.start() - } + // MangoWC-specific operations + function toggleOverview() { + try { + Quickshell.execDetached(mmsgCommands.action.toggleOverview) + } catch (e) { + Logger.e("MangoService", "Failed to toggle overview:", e) } } - // Timer to restart event stream - Timer { - id: restartTimer - interval: 1000 - onTriggered: { - if (initialized) { - mangoEventStream.running = true - } + function setLayout(layoutName) { + try { + const command = mmsgCommands.action.setLayout.concat([layoutName]) + Quickshell.execDetached(command) + } catch (e) { + Logger.e("MangoService", "Failed to set layout:", e) } } - // Parse tags data and convert to workspace format - function parseTagsData(output) { + function logout() { + try { + Quickshell.execDetached(mmsgCommands.action.quit) + } catch (e) { + Logger.e("MangoService", "Failed to logout:", e) + } + } + + // Data parsing + function parseWorkspaces(output) { const lines = output.trim().split('\n') const workspacesList = [] - const outputTags = {} - tagCache = {} + const newWorkspaceCache = {} for (const line of lines) { const trimmed = line.trim() if (!trimmed) continue - // Parse tag information - // Format: output tag - // Example: eDP-1 tag 1 1 2 1 - const tagMatch = trimmed.match(/^(\S+)\s+tag\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/) - if (tagMatch) { - const [, outputName, tagNum, state, clients, focused] = tagMatch + const match = trimmed.match(/^(\S+)\s+tag\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/) + if (match) { + const [, outputName, tagNum, state, clients, focused] = match const tagId = parseInt(tagNum) - // Store tag info per output - if (!outputTags[outputName]) { - outputTags[outputName] = [] - } - - // Convert MangoWC tag state to workspace properties - // Based on dwl-ipc protocol: bit 0=active, bit 1=urgent, bit 2=occupied const isActive = (parseInt(state) & 1) !== 0 - const isUrgent = (parseInt(state) & 2) !== 0 // Note: might be bit 1 for urgent + const isUrgent = (parseInt(state) & 2) !== 0 const isOccupied = parseInt(clients) > 0 + const isFocused = isActive && parseInt(focused) === 1 const workspaceData = { - "id": tagId, - "idx": tagId, - "name": tagId.toString(), - "output": outputName, - "isActive": isActive, - "isFocused": isActive && parseInt(focused) === 1, - "isUrgent": isUrgent, - "isOccupied": isOccupied + id: tagId, + idx: tagId, + name: tagId.toString(), + output: outputName, + isActive: isActive, + isFocused: isFocused, + isUrgent: isUrgent, + isOccupied: isOccupied, + clients: parseInt(clients) } - tagCache[tagId] = workspaceData - outputTags[outputName].push(workspaceData) - + newWorkspaceCache[tagId] = workspaceData + workspacesList.push(workspaceData) } - // Parse layout information - // Format: output layout const layoutMatch = trimmed.match(/^(\S+)\s+layout\s+(\S+)$/) if (layoutMatch) { - const [, outputName, layoutName] = layoutMatch - currentLayout = layoutName - - // Detect overview state - MangoWC uses "󰃇" symbol for overview - const wasOverviewActive = overviewActive - overviewActive = (layoutName === "󰃇") - - // Emit signal if overview state changed - if (wasOverviewActive !== overviewActive) { - Logger.d("MangoService", `Overview state changed: ${overviewActive}`) - } - } - - // Parse clients count information - // Format: output clients - const clientsMatch = trimmed.match(/^(\S+)\s+clients\s+(\d+)$/) - if (clientsMatch) { - const [, outputName, clientsCount] = clientsMatch - // Store clients count for the output - if (!monitorCache[outputName]) { - monitorCache[outputName] = {} - } - monitorCache[outputName].clientsCount = parseInt(clientsCount) - } - - // Parse tags mask information - // Format: output tags - const tagsMaskMatch = trimmed.match(/^(\S+)\s+tags\s+(\d+)\s+(\d+)\s+(\d+)$/) - if (tagsMaskMatch) { - const [, outputName, occupiedMask, selectedMask, urgentMask] = tagsMaskMatch - if (!monitorCache[outputName]) { - monitorCache[outputName] = {} - } - monitorCache[outputName].occupiedMask = parseInt(occupiedMask) - monitorCache[outputName].selectedMask = parseInt(selectedMask) - monitorCache[outputName].urgentMask = parseInt(urgentMask) + const [, , layoutSymbol] = layoutMatch + handleLayoutChange(layoutSymbol) } } - // Flatten all tags from all outputs into a single list - for (const [outputName, tags] of Object.entries(outputTags)) { - for (const tag of tags) { - workspacesList.push(tag) + if (JSON.stringify(newWorkspaceCache) !== JSON.stringify(workspaceCache)) { + workspaceCache = newWorkspaceCache + + workspacesList.sort((a, b) => { + if (a.id !== b.id) return a.id - b.id + return a.output.localeCompare(b.output) + }) + + workspaces.clear() + for (var i = 0; i < workspacesList.length; i++) { + workspaces.append(workspacesList[i]) } + + workspaceChanged() } - - // Sort workspaces by tag ID, then by output name - workspacesList.sort((a, b) => { - if (a.id !== b.id) { - return a.id - b.id - } - return a.output.localeCompare(b.output) - }) - - // Update workspaces ListModel - workspaces.clear() - for (var i = 0; i < workspacesList.length; i++) { - workspaces.append(workspacesList[i]) - } - - workspaceChanged() } - // Parse windows data - function parseWindowsData(windowData) { - + function parseWindows(windowData) { const windowsList = [] - windowCache = {} + const newWindowCache = {} let newFocusedIndex = -1 for (const [outputName, data] of Object.entries(windowData)) { if (data.title || data.appId) { const windowInfo = { - "id": outputName, // Use output name as unique identifier for now - "title": data.title || "", - "appId": data.appId || "", - "workspaceId": getCurrentTagId(), // Get current active tag - "isFocused": false, // Will be determined by focused window detection - "output": outputName, - "class": data.appId || "", - "fullscreen": data.isFullscreen || false, - "floating": data.isFloating || false, - "x": data.x || 0, - "y": data.y || 0, - "width": data.width || 0, - "height": data.height || 0 + id: outputName, + title: data.title || "", + appId: data.appId || "", + class: data.appId || "", + workspaceId: getCurrentActiveTagId(), + isFocused: false, + output: outputName, + fullscreen: data.fullscreen || false, + floating: data.floating || false, + x: data.x || 0, + y: data.y || 0, + width: data.width || 0, + height: data.height || 0, + geometry: { + x: data.x || 0, + y: data.y || 0, + width: data.width || 0, + height: data.height || 0 + } } windowsList.push(windowInfo) - windowCache[outputName] = windowInfo + newWindowCache[outputName] = windowInfo } } - // Try to determine focused window by checking which output is focused - // This is a heuristic approach since mmsg doesn't provide direct focus info for (let i = 0; i < windowsList.length; i++) { const window = windowsList[i] const outputData = monitorCache[window.output] @@ -467,161 +470,117 @@ Item { } } - // Fallback: assume first window is focused if no output focus info - if (newFocusedIndex === -1 && windowsList.length > 0) { - windowsList[0].isFocused = true - newFocusedIndex = 0 + if (JSON.stringify(newWindowCache) !== JSON.stringify(windowCache)) { + windowCache = newWindowCache + windows = windowsList + + if (newFocusedIndex !== focusedWindowIndex) { + focusedWindowIndex = newFocusedIndex + activeWindowChanged() + } + + windowListChanged() } - - windows = windowsList - - if (newFocusedIndex !== focusedWindowIndex) { - focusedWindowIndex = newFocusedIndex - activeWindowChanged() - } - - windowListChanged() } - // Get current active tag ID - function getCurrentTagId() { - for (const [tagId, tagData] of Object.entries(tagCache)) { - if (tagData.isActive) { - return parseInt(tagId) + function handleLayoutChange(layoutSymbol) { + const wasOverview = overviewActive + const isOverview = (layoutSymbol === overviewLayoutSymbol) + + if (wasOverview !== isOverview) { + overviewActive = isOverview + Logger.d("MangoService", `Overview mode: ${overviewActive}`) + } + + if (layoutSymbol !== currentLayoutSymbol) { + currentLayoutSymbol = layoutSymbol + currentLayout = layoutSymbol + } + } + + function updateDisplayScales() { + const scales = {} + for (const [outputName, data] of Object.entries(monitorCache)) { + scales[outputName] = { + name: data.name || outputName, + scale: data.scale || 1.0, + width: data.width || 0, + height: data.height || 0, + refresh_rate: data.refresh_rate || 0, + x: data.x || 0, + y: data.y || 0, + active: data.active || false, + focused: data.focused || false } } - return 1 // Default to tag 1 + + if (CompositorService && CompositorService.onDisplayScalesUpdated) { + CompositorService.onDisplayScalesUpdated(scales) + } + displayScalesChanged() } - // Handle events from mmsg -w + // Event handling function handleEvent(eventLine) { const parts = eventLine.trim().split(/\s+/) if (parts.length < 2) return - const outputName = parts[0] const eventType = parts[1] - // Handle different event types switch (eventType) { case "tag": - // Tag state changed - updateTimer.restart() - break case "title": case "appid": case "fullscreen": case "floating": - // Window properties changed - updateTimer.restart() - break case "layout": - // Layout changed - updateTimer.restart() - break case "kb_layout": - // Keyboard layout changed - const layoutName = parts.slice(2).join(' ') - if (layoutName && layoutName !== currentKeyboardLayout) { - currentKeyboardLayout = layoutName - KeyboardLayoutService.setCurrentLayout(layoutName) - } - break case "scale_factor": - // Display scale changed - queryDisplayScales() - break case "monitor": - // Monitor configuration changed - queryDisplayScales() - updateTimer.restart() - break case "client": - // Client (window) focus or state changed - updateTimer.restart() - break case "selmon": - // Selected monitor changed - updateTimer.restart() - break - default: - // Unknown event type, trigger general update - updateTimer.restart() break } } - // Safe update wrapper + // Queries + function queryWorkspaces() { + workspacesProcess.running = true + } + + function queryWindows() { + windowsProcess.running = true + } + + function queryLayout() { + layoutProcess.running = true + } + + function queryKeyboard() { + keyboardProcess.running = true + } + + function queryOutputs() { + outputsProcess.running = true + } + + // Utilities function safeUpdate() { - safeUpdateWindows() - safeUpdateWorkspaces() - windowListChanged() - } - - // Safe workspace update - function safeUpdateWorkspaces() { try { - updateWorkspaces() + queryWorkspaces() + queryWindows() } catch (e) { - Logger.e("MangoService", "Error updating workspaces:", e) + Logger.e("MangoService", "Safe update failed:", e) } } - // Safe window update - function safeUpdateWindows() { - try { - updateWindows() - } catch (e) { - Logger.e("MangoService", "Error updating windows:", e) - } - } - - // Public functions - function switchToWorkspace(workspace) { - try { - // MangoWC uses tags 1-9, so switch to tag by ID - const tagId = workspace.idx || workspace.id || 1 - Quickshell.execDetached(["mmsg", "-d", "view", tagId.toString()]) - } catch (e) { - Logger.e("MangoService", "Failed to switch workspace:", e) - } - } - - function focusWindow(window) { - try { - // For MangoWC, we can try to focus windows by switching to their workspace - // and then using focus commands, or by cycling through windows - if (window && window.workspaceId) { - // First switch to the window's workspace/tag - Quickshell.execDetached(["mmsg", "-d", "view", window.workspaceId.toString()]) - - // Then try to focus the window by cycling or using window-specific commands - // This is a limitation of the mmsg interface - we can't directly focus by window ID - Qt.callLater(() => { - // Give the workspace switch a moment to complete, then try to find the window - // For now, we'll use a generic focus command that focuses the main window - Quickshell.execDetached(["mmsg", "-d", "focusmaster"]) - }) + function getCurrentActiveTagId() { + for (const [tagId, tagData] of Object.entries(workspaceCache)) { + if (tagData.isActive) { + return parseInt(tagId) } - } catch (e) { - Logger.e("MangoService", "Failed to focus window:", e) - } - } - - function closeWindow(window) { - try { - // Close focused window - Quickshell.execDetached(["mmsg", "-d", "killclient"]) - } catch (e) { - Logger.e("MangoService", "Failed to close window:", e) - } - } - - function logout() { - try { - Quickshell.execDetached(["mmsg", "-d", "quit"]) - } catch (e) { - Logger.e("MangoService", "Failed to logout:", e) } + return defaultWorkspaceId } } \ No newline at end of file