diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml index ed657b3..0667cb4 100644 --- a/Modules/Bar/Widgets/ActiveWindow.qml +++ b/Modules/Bar/Widgets/ActiveWindow.qml @@ -38,7 +38,12 @@ RowLayout { readonly property real maxWidth: minWidth * 2 function getTitle() { - return CompositorService.focusedWindowTitle !== "(No active window)" ? CompositorService.focusedWindowTitle : "" + try { + return CompositorService.focusedWindowTitle !== "(No active window)" ? CompositorService.focusedWindowTitle : "" + } catch (e) { + Logger.warn("ActiveWindow", "Error getting title:", e) + return "" + } } Layout.alignment: Qt.AlignVCenter @@ -46,25 +51,44 @@ RowLayout { visible: getTitle() !== "" function getAppIcon() { - // Try CompositorService first - const focusedWindow = CompositorService.getFocusedWindow() - if (focusedWindow && focusedWindow.appId) { - const idValue = focusedWindow.appId - const normalizedId = (typeof idValue === 'string') ? idValue : String(idValue) - return AppIcons.iconForAppId(normalizedId.toLowerCase()) - } - - // Fallback to ToplevelManager - if (ToplevelManager && ToplevelManager.activeToplevel) { - const activeToplevel = ToplevelManager.activeToplevel - if (activeToplevel.appId) { - const idValue2 = activeToplevel.appId - const normalizedId2 = (typeof idValue2 === 'string') ? idValue2 : String(idValue2) - return AppIcons.iconForAppId(normalizedId2.toLowerCase()) + try { + // Try CompositorService first + const focusedWindow = CompositorService.getFocusedWindow() + if (focusedWindow && focusedWindow.appId) { + try { + const idValue = focusedWindow.appId + const normalizedId = (typeof idValue === 'string') ? idValue : String(idValue) + const iconResult = AppIcons.iconForAppId(normalizedId.toLowerCase()) + if (iconResult && iconResult !== "") { + return iconResult + } + } catch (iconError) { + Logger.warn("ActiveWindow", "Error getting icon from CompositorService:", iconError) + } } - } - return "" + // Fallback to ToplevelManager + if (ToplevelManager && ToplevelManager.activeToplevel) { + try { + const activeToplevel = ToplevelManager.activeToplevel + if (activeToplevel.appId) { + const idValue2 = activeToplevel.appId + const normalizedId2 = (typeof idValue2 === 'string') ? idValue2 : String(idValue2) + const iconResult2 = AppIcons.iconForAppId(normalizedId2.toLowerCase()) + if (iconResult2 && iconResult2 !== "") { + return iconResult2 + } + } + } catch (fallbackError) { + Logger.warn("ActiveWindow", "Error getting icon from ToplevelManager:", fallbackError) + } + } + + return "" + } catch (e) { + Logger.warn("ActiveWindow", "Error in getAppIcon:", e) + return "" + } } // A hidden text element to safely measure the full title width @@ -110,16 +134,28 @@ RowLayout { asynchronous: true smooth: true visible: source !== "" + + // Handle loading errors gracefully + onStatusChanged: { + if (status === Image.Error) { + Logger.warn("ActiveWindow", "Failed to load icon:", source) + } + } } } NText { id: titleText Layout.preferredWidth: { - if (mouseArea.containsMouse) { - return Math.round(Math.min(fullTitleMetrics.contentWidth, root.maxWidth * scaling)) - } else { - return Math.round(Math.min(fullTitleMetrics.contentWidth, root.minWidth * scaling)) + try { + if (mouseArea.containsMouse) { + return Math.round(Math.min(fullTitleMetrics.contentWidth, root.maxWidth * scaling)) + } else { + return Math.round(Math.min(fullTitleMetrics.contentWidth, root.minWidth * scaling)) + } + } catch (e) { + Logger.warn("ActiveWindow", "Error calculating width:", e) + return root.minWidth * scaling } } Layout.alignment: Qt.AlignVCenter @@ -154,10 +190,18 @@ RowLayout { Connections { target: CompositorService function onActiveWindowChanged() { - windowIcon.source = Qt.binding(getAppIcon) + try { + windowIcon.source = Qt.binding(getAppIcon) + } catch (e) { + Logger.warn("ActiveWindow", "Error in onActiveWindowChanged:", e) + } } function onWindowListChanged() { - windowIcon.source = Qt.binding(getAppIcon) + try { + windowIcon.source = Qt.binding(getAppIcon) + } catch (e) { + Logger.warn("ActiveWindow", "Error in onWindowListChanged:", e) + } } } -} +} \ No newline at end of file diff --git a/Services/CompositorService.qml b/Services/CompositorService.qml index bc6a4d5..03f4287 100644 --- a/Services/CompositorService.qml +++ b/Services/CompositorService.qml @@ -29,6 +29,21 @@ Singleton { signal windowListChanged signal windowTitleChanged + // Debounce timer for updates + property Timer updateTimer: Timer { + interval: 50 // 50ms debounce + repeat: false + onTriggered: { + try { + updateHyprlandWindows() + updateHyprlandWorkspaces() + windowListChanged() + } catch (e) { + Logger.error("Compositor", "Error in debounced update:", e) + } + } + } + // Compositor detection Component.onCompleted: { detectCompositor() @@ -39,8 +54,12 @@ Singleton { target: Hyprland.workspaces enabled: isHyprland function onValuesChanged() { - updateHyprlandWorkspaces() - workspaceChanged() + try { + updateHyprlandWorkspaces() + workspaceChanged() + } catch (e) { + Logger.error("Compositor", "Error in workspaces onValuesChanged:", e) + } } } @@ -48,10 +67,12 @@ Singleton { target: Hyprland.toplevels enabled: isHyprland function onValuesChanged() { - updateHyprlandWindows() - // Keep workspace occupancy up to date when windows change - updateHyprlandWorkspaces() - windowListChanged() + try { + // Use debounced update to prevent too frequent calls + updateTimer.restart() + } catch (e) { + Logger.error("Compositor", "Error in toplevels onValuesChanged:", e) + } } } @@ -59,10 +80,13 @@ Singleton { target: Hyprland enabled: isHyprland function onRawEvent(event) { - updateHyprlandWorkspaces() - workspaceChanged() - updateHyprlandWindows() - windowListChanged() + try { + updateHyprlandWorkspaces() + workspaceChanged() + updateTimer.restart() + } catch (e) { + Logger.error("Compositor", "Error in rawEvent:", e) + } } } @@ -77,7 +101,6 @@ Singleton { return } } catch (e) { - // Hyprland not available } @@ -111,7 +134,8 @@ Singleton { } } - function setupHyprlandConnections() {// Connections are set up at the top level, this function just marks that Hyprland is ready + function setupHyprlandConnections() { + // Connections are set up at the top level, this function just marks that Hyprland is ready } function updateHyprlandWorkspaces() { @@ -121,34 +145,50 @@ Singleton { workspaces.clear() try { const hlWorkspaces = Hyprland.workspaces.values + // Determine occupied workspace ids from current toplevels const occupiedIds = {} try { const hlToplevels = Hyprland.toplevels.values for (var t = 0; t < hlToplevels.length; t++) { - const tws = hlToplevels[t].workspace?.id - if (tws !== undefined && tws !== null) { - occupiedIds[tws] = true + const toplevel = hlToplevels[t] + if (toplevel) { + try { + const tws = toplevel.workspace?.id + if (tws !== undefined && tws !== null) { + occupiedIds[tws] = true + } + } catch (toplevelError) { + // Ignore errors from individual toplevels + continue + } } } } catch (e2) { - // ignore occupancy errors; fall back to false } + for (var i = 0; i < hlWorkspaces.length; i++) { const ws = hlWorkspaces[i] - // Only append workspaces with id >= 1 - if (ws.id >= 1) { - workspaces.append({ - "id": i, - "idx": ws.id, - "name": ws.name || "", - "output": ws.monitor?.name || "", - "isActive": ws.active === true, - "isFocused": ws.focused === true, - "isUrgent": ws.urgent === true, - "isOccupied": occupiedIds[ws.id] === true - }) + if (!ws) continue + + try { + // Only append workspaces with id >= 1 + if (ws.id >= 1) { + workspaces.append({ + "id": i, + "idx": ws.id, + "name": ws.name || "", + "output": ws.monitor?.name || "", + "isActive": ws.active === true, + "isFocused": ws.focused === true, + "isUrgent": ws.urgent === true, + "isOccupied": occupiedIds[ws.id] === true + }) + } + } catch (workspaceError) { + Logger.warn("Compositor", "Error processing workspace at index", i, ":", workspaceError) + continue } } } catch (e) { @@ -167,39 +207,83 @@ Singleton { for (var i = 0; i < hlToplevels.length; i++) { const toplevel = hlToplevels[i] - // Try to get appId from various sources - let appId = "" - - // First try the direct properties - if (toplevel.class) { - appId = toplevel.class - } else if (toplevel.initialClass) { - appId = toplevel.initialClass - } else if (toplevel.appId) { - appId = toplevel.appId + // Skip if toplevel is null or invalid + if (!toplevel) { + continue } - // If still no appId, try to get it from the lastIpcObject - if (!appId && toplevel.lastIpcObject) { + try { + // Try to get appId from various sources with proper null checks + let appId = "" + + // First try the direct properties with null/undefined checks try { - const ipcData = toplevel.lastIpcObject - // Try different possible property names for the application identifier - appId = ipcData.class || ipcData.initialClass || ipcData.appId || ipcData.wm_class || "" - } catch (e) { - - // Ignore errors when accessing lastIpcObject + if (toplevel.class !== undefined && toplevel.class !== null) { + appId = String(toplevel.class) + } else if (toplevel.initialClass !== undefined && toplevel.initialClass !== null) { + appId = String(toplevel.initialClass) + } else if (toplevel.appId !== undefined && toplevel.appId !== null) { + appId = String(toplevel.appId) + } + } catch (propertyError) { + // Ignore property access errors and continue with empty appId } - } - windowsList.push({ - "id": (toplevel.address !== undefined - && toplevel.address !== null) ? String(toplevel.address) : "", - "title": (toplevel.title !== undefined && toplevel.title !== null) ? String( - toplevel.title) : "", - "appId": (appId !== undefined && appId !== null) ? String(appId) : "", - "workspaceId": toplevel.workspace?.id || null, - "isFocused": toplevel.activated === true - }) + // If still no appId, try to get it from the lastIpcObject + if (!appId) { + try { + const ipcData = toplevel.lastIpcObject + if (ipcData) { + appId = String(ipcData.class || ipcData.initialClass || ipcData.appId || ipcData.wm_class || "") + } + } catch (ipcError) { + // Ignore errors when accessing lastIpcObject + } + } + + // Safely get other properties with fallbacks + let windowId = "" + let windowTitle = "" + let workspaceId = null + let isActivated = false + + try { + windowId = (toplevel.address !== undefined && toplevel.address !== null) ? String(toplevel.address) : "" + } catch (e) { + windowId = "" + } + + try { + windowTitle = (toplevel.title !== undefined && toplevel.title !== null) ? String(toplevel.title) : "" + } catch (e) { + windowTitle = "" + } + + try { + workspaceId = toplevel.workspace?.id || null + } catch (e) { + workspaceId = null + } + + try { + isActivated = toplevel.activated === true + } catch (e) { + isActivated = false + } + + windowsList.push({ + "id": windowId, + "title": windowTitle, + "appId": appId, + "workspaceId": workspaceId, + "isFocused": isActivated + }) + + } catch (toplevelError) { + // Log the error but continue processing other toplevels + Logger.warn("Compositor", "Error processing toplevel at index", i, ":", toplevelError) + continue + } } windows = windowsList @@ -217,6 +301,7 @@ Singleton { activeWindowChanged() } catch (e) { Logger.error("Compositor", "Error updating Hyprland windows:", e) + // Don't crash, just keep the previous windows list } } @@ -266,23 +351,23 @@ Singleton { for (const ws of workspacesData) { workspacesList.push({ - "id": ws.id, - "idx": ws.idx, - "name": ws.name || "", - "output": ws.output || "", - "isFocused": ws.is_focused === true, - "isActive": ws.is_active === true, - "isUrgent": ws.is_urgent === true, - "isOccupied": ws.active_window_id ? true : false - }) + "id": ws.id, + "idx": ws.idx, + "name": ws.name || "", + "output": ws.output || "", + "isFocused": ws.is_focused === true, + "isActive": ws.is_active === true, + "isUrgent": ws.is_urgent === true, + "isOccupied": ws.active_window_id ? true : false + }) } workspacesList.sort((a, b) => { - if (a.output !== b.output) { - return a.output.localeCompare(b.output) - } - return a.idx - b.idx - }) + if (a.output !== b.output) { + return a.output.localeCompare(b.output) + } + return a.idx - b.idx + }) // Update the workspaces ListModel workspaces.clear() @@ -387,12 +472,12 @@ Singleton { const windowsList = [] for (const win of windowsData) { windowsList.push({ - "id": win.id, - "title": win.title || "", - "appId": win.app_id || "", - "workspaceId": win.workspace_id || null, - "isFocused": win.is_focused === true - }) + "id": win.id, + "title": win.title || "", + "appId": win.app_id || "", + "workspaceId": win.workspace_id || null, + "isFocused": win.is_focused === true + }) } windowsList.sort((a, b) => a.id - b.id) @@ -457,12 +542,12 @@ Singleton { const windowsList = [] for (const win of windowsData) { windowsList.push({ - "id": win.id, - "title": win.title || "", - "appId": win.app_id || "", - "workspaceId": win.workspace_id || null, - "isFocused": win.is_focused === true - }) + "id": win.id, + "title": win.title || "", + "appId": win.app_id || "", + "workspaceId": win.workspace_id || null, + "isFocused": win.is_focused === true + }) } windowsList.sort((a, b) => a.id - b.id) @@ -567,4 +652,4 @@ Singleton { function suspend() { Quickshell.execDetached(["systemctl", "suspend"]) } -} +} \ No newline at end of file diff --git a/Services/KeyboardLayoutService.qml b/Services/KeyboardLayoutService.qml index 5a8975b..7f07707 100644 --- a/Services/KeyboardLayoutService.qml +++ b/Services/KeyboardLayoutService.qml @@ -35,7 +35,6 @@ Singleton { const layoutName = data.names[data.current_idx] root.currentLayout = extractLayoutCode(layoutName) } catch (e) { - console.log("Niri layout error:", e) root.currentLayout = "Unknown" } } @@ -59,7 +58,6 @@ Singleton { root.currentLayout = "Unknown" } } catch (e) { - console.log("Hyprland layout error:", e) root.currentLayout = "Unknown" } } @@ -84,7 +82,6 @@ Singleton { } root.currentLayout = "Unknown" } catch (e) { - console.log("X11 layout error:", e) root.currentLayout = "Unknown" } } @@ -118,7 +115,6 @@ Singleton { } root.currentLayout = "Unknown" } catch (e) { - console.log("Localectl error:", e) root.currentLayout = "Unknown" } } @@ -136,7 +132,6 @@ Singleton { const currentIndex = parseInt(text.trim()) gsettingsSourcesProcess.running = true } catch (e) { - console.log("Gsettings current error:", e) fallbackToLocalectl() } } @@ -163,7 +158,6 @@ Singleton { fallbackToLocalectl() } } catch (e) { - console.log("Gsettings sources error:", e) fallbackToLocalectl() } }