mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2025-12-06 06:36:15 +00:00
@@ -105,13 +105,13 @@ Rectangle {
|
||||
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
try {
|
||||
CompositorService.focusWindow(taskbarItem.modelData.id)
|
||||
CompositorService.focusWindow(taskbarItem.modelData)
|
||||
} catch (error) {
|
||||
Logger.error("Taskbar", "Failed to activate toplevel: " + error)
|
||||
}
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
try {
|
||||
CompositorService.closeWindow(taskbarItem.modelData.id)
|
||||
CompositorService.closeWindow(taskbarItem.modelData)
|
||||
} catch (error) {
|
||||
Logger.error("Taskbar", "Failed to close toplevel: " + error)
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ Item {
|
||||
next = localWorkspaces.count - 1
|
||||
const ws = localWorkspaces.get(next)
|
||||
if (ws && ws.idx !== undefined)
|
||||
CompositorService.switchToWorkspace(ws.idx)
|
||||
CompositorService.switchToWorkspace(ws)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -323,7 +323,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
CompositorService.switchToWorkspace(model.idx)
|
||||
CompositorService.switchToWorkspace(model)
|
||||
}
|
||||
hoverEnabled: true
|
||||
}
|
||||
@@ -467,7 +467,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
CompositorService.switchToWorkspace(model.idx)
|
||||
CompositorService.switchToWorkspace(model)
|
||||
}
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ Singleton {
|
||||
// Compositor detection
|
||||
property bool isHyprland: false
|
||||
property bool isNiri: false
|
||||
property bool isSway: false
|
||||
|
||||
// Generic workspace and window data
|
||||
property ListModel workspaces: ListModel {}
|
||||
@@ -31,14 +32,22 @@ Singleton {
|
||||
|
||||
function detectCompositor() {
|
||||
const hyprlandSignature = Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
|
||||
const swaySock = Quickshell.env("SWAYSOCK")
|
||||
if (hyprlandSignature && hyprlandSignature.length > 0) {
|
||||
isHyprland = true
|
||||
isNiri = false
|
||||
isSway = false
|
||||
backendLoader.sourceComponent = hyprlandComponent
|
||||
} else if (swaySock && swaySock.length > 0) {
|
||||
isHyprland = false
|
||||
isNiri = false
|
||||
isSway = true
|
||||
backendLoader.sourceComponent = swayComponent
|
||||
} else {
|
||||
// Default to Niri
|
||||
isHyprland = false
|
||||
isNiri = true
|
||||
isSway = false
|
||||
backendLoader.sourceComponent = niriComponent
|
||||
}
|
||||
}
|
||||
@@ -70,6 +79,14 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Sway backend component
|
||||
Component {
|
||||
id: swayComponent
|
||||
SwayService {
|
||||
id: swayBackend
|
||||
}
|
||||
}
|
||||
|
||||
function setupBackendConnections() {
|
||||
if (!backend)
|
||||
return
|
||||
@@ -145,9 +162,9 @@ Singleton {
|
||||
}
|
||||
|
||||
// Generic workspace switching
|
||||
function switchToWorkspace(workspaceId) {
|
||||
function switchToWorkspace(workspace) {
|
||||
if (backend && backend.switchToWorkspace) {
|
||||
backend.switchToWorkspace(workspaceId)
|
||||
backend.switchToWorkspace(workspace)
|
||||
} else {
|
||||
Logger.warn("Compositor", "No backend available for workspace switching")
|
||||
}
|
||||
@@ -177,18 +194,18 @@ Singleton {
|
||||
}
|
||||
|
||||
// Set focused window
|
||||
function focusWindow(windowId) {
|
||||
function focusWindow(window) {
|
||||
if (backend && backend.focusWindow) {
|
||||
backend.focusWindow(windowId)
|
||||
backend.focusWindow(window)
|
||||
} else {
|
||||
Logger.warn("Compositor", "No backend available for window focus")
|
||||
}
|
||||
}
|
||||
|
||||
// Close window
|
||||
function closeWindow(windowId) {
|
||||
function closeWindow(window) {
|
||||
if (backend && backend.closeWindow) {
|
||||
backend.closeWindow(windowId)
|
||||
backend.closeWindow(window)
|
||||
} else {
|
||||
Logger.warn("Compositor", "No backend available for window closing")
|
||||
}
|
||||
|
||||
@@ -272,25 +272,25 @@ Item {
|
||||
}
|
||||
|
||||
// Public functions
|
||||
function switchToWorkspace(workspaceId) {
|
||||
function switchToWorkspace(workspace) {
|
||||
try {
|
||||
Hyprland.dispatch(`workspace ${workspaceId}`)
|
||||
Hyprland.dispatch(`workspace ${workspace.idx}`)
|
||||
} catch (e) {
|
||||
Logger.error("HyprlandService", "Failed to switch workspace:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function focusWindow(windowId) {
|
||||
function focusWindow(window) {
|
||||
try {
|
||||
Hyprland.dispatch(`focuswindow address:0x${windowId.toString()}`)
|
||||
Hyprland.dispatch(`focuswindow address:0x${window.id.toString()}`)
|
||||
} catch (e) {
|
||||
Logger.error("HyprlandService", "Failed to switch window:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function closeWindow(windowId) {
|
||||
function closeWindow(window) {
|
||||
try {
|
||||
Hyprland.dispatch(`killwindow address:0x${windowId}`)
|
||||
Hyprland.dispatch(`killwindow address:0x${window.id}`)
|
||||
} catch (e) {
|
||||
Logger.error("HyprlandService", "Failed to close window:", e)
|
||||
}
|
||||
|
||||
@@ -332,25 +332,25 @@ Item {
|
||||
}
|
||||
|
||||
// Public functions
|
||||
function switchToWorkspace(workspaceId) {
|
||||
function switchToWorkspace(workspace) {
|
||||
try {
|
||||
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", workspaceId.toString()])
|
||||
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", workspace.idx.toString()])
|
||||
} catch (e) {
|
||||
Logger.error("NiriService", "Failed to switch workspace:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function focusWindow(windowId) {
|
||||
function focusWindow(window) {
|
||||
try {
|
||||
Quickshell.execDetached(["niri", "msg", "action", "focus-window", "--id", windowId.toString()])
|
||||
Quickshell.execDetached(["niri", "msg", "action", "focus-window", "--id", window.id.toString()])
|
||||
} catch (e) {
|
||||
Logger.error("NiriService", "Failed to switch window:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function closeWindow(windowId) {
|
||||
function closeWindow(window) {
|
||||
try {
|
||||
Quickshell.execDetached(["niri", "msg", "action", "close-window", "--id", windowId.toString()])
|
||||
Quickshell.execDetached(["niri", "msg", "action", "close-window", "--id", window.id.toString()])
|
||||
} catch (e) {
|
||||
Logger.error("NiriService", "Failed to close window:", e)
|
||||
}
|
||||
|
||||
235
Services/SwayService.qml
Normal file
235
Services/SwayService.qml
Normal file
@@ -0,0 +1,235 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.I3
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// Properties that match the facade interface
|
||||
property ListModel workspaces: ListModel {}
|
||||
property var windows: []
|
||||
property int focusedWindowIndex: -1
|
||||
|
||||
// Signals that match the facade interface
|
||||
signal workspaceChanged
|
||||
signal activeWindowChanged
|
||||
signal windowListChanged
|
||||
|
||||
// I3-specific properties
|
||||
property bool initialized: false
|
||||
|
||||
// Debounce timer for updates
|
||||
Timer {
|
||||
id: updateTimer
|
||||
interval: 50
|
||||
repeat: false
|
||||
onTriggered: safeUpdate()
|
||||
}
|
||||
|
||||
// Initialization
|
||||
function initialize() {
|
||||
if (initialized)
|
||||
return
|
||||
|
||||
try {
|
||||
I3.refreshWorkspaces()
|
||||
Qt.callLater(() => {
|
||||
safeUpdateWorkspaces()
|
||||
safeUpdateWindows()
|
||||
})
|
||||
initialized = true
|
||||
Logger.log("SwayService", "Initialized successfully")
|
||||
} catch (e) {
|
||||
Logger.error("SwayService", "Failed to initialize:", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Safe update wrapper
|
||||
function safeUpdate() {
|
||||
safeUpdateWindows()
|
||||
safeUpdateWorkspaces()
|
||||
windowListChanged()
|
||||
}
|
||||
|
||||
// Safe workspace update
|
||||
function safeUpdateWorkspaces() {
|
||||
try {
|
||||
workspaces.clear()
|
||||
|
||||
if (!I3.workspaces || !I3.workspaces.values) {
|
||||
return
|
||||
}
|
||||
|
||||
const hlWorkspaces = I3.workspaces.values
|
||||
|
||||
for (var i = 0; i < hlWorkspaces.length; i++) {
|
||||
const ws = hlWorkspaces[i]
|
||||
if (!ws || ws.id < 1)
|
||||
continue
|
||||
|
||||
const wsData = {
|
||||
"id": i,
|
||||
"idx": ws.id,
|
||||
"name": ws.name || "",
|
||||
"output": (ws.monitor && ws.monitor.name) ? ws.monitor.name : "",
|
||||
"isActive": ws.active === true,
|
||||
"isFocused": ws.focused === true,
|
||||
"isUrgent": ws.urgent === true,
|
||||
"isOccupied": true,
|
||||
"handle": ws
|
||||
}
|
||||
|
||||
workspaces.append(wsData)
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.error("SwayService", "Error updating workspaces:", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Safe window update
|
||||
function safeUpdateWindows() {
|
||||
try {
|
||||
const windowsList = []
|
||||
|
||||
if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values) {
|
||||
windows = []
|
||||
focusedWindowIndex = -1
|
||||
return
|
||||
}
|
||||
|
||||
const hlToplevels = ToplevelManager.toplevels.values
|
||||
let newFocusedIndex = -1
|
||||
|
||||
for (var i = 0; i < hlToplevels.length; i++) {
|
||||
const toplevel = hlToplevels[i]
|
||||
if (!toplevel)
|
||||
continue
|
||||
|
||||
const windowData = extractWindowData(toplevel)
|
||||
if (windowData) {
|
||||
windowsList.push(windowData)
|
||||
|
||||
if (windowData.isFocused) {
|
||||
newFocusedIndex = windowsList.length - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
windows = windowsList
|
||||
|
||||
if (newFocusedIndex !== focusedWindowIndex) {
|
||||
focusedWindowIndex = newFocusedIndex
|
||||
activeWindowChanged()
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.error("SwayService", "Error updating windows:", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract window data safely from a toplevel
|
||||
function extractWindowData(toplevel) {
|
||||
if (!toplevel)
|
||||
return null
|
||||
|
||||
try {
|
||||
// Safely extract properties
|
||||
const appId = extractAppId(toplevel)
|
||||
const title = safeGetProperty(toplevel, "title", "")
|
||||
const focused = toplevel.activated === true
|
||||
|
||||
return {
|
||||
"title": title,
|
||||
"appId": appId,
|
||||
"isFocused": focused,
|
||||
"handle": toplevel
|
||||
}
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Extract app ID from various possible sources
|
||||
function extractAppId(toplevel) {
|
||||
if (!toplevel)
|
||||
return ""
|
||||
|
||||
return toplevel.appId;
|
||||
}
|
||||
|
||||
// Safe property getter
|
||||
function safeGetProperty(obj, prop, defaultValue) {
|
||||
try {
|
||||
const value = obj[prop]
|
||||
if (value !== undefined && value !== null) {
|
||||
return String(value)
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
// Property access failed
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// Connections to I3
|
||||
Connections {
|
||||
target: I3.workspaces
|
||||
enabled: initialized
|
||||
function onValuesChanged() {
|
||||
safeUpdateWorkspaces()
|
||||
workspaceChanged()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ToplevelManager
|
||||
enabled: initialized
|
||||
function onActiveToplevelChanged() {
|
||||
updateTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: I3
|
||||
enabled: initialized
|
||||
function onRawEvent(event) {
|
||||
safeUpdateWorkspaces()
|
||||
workspaceChanged()
|
||||
updateTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
// Public functions
|
||||
function switchToWorkspace(workspace) {
|
||||
try {
|
||||
workspace.handle.activate()
|
||||
} catch (e) {
|
||||
Logger.error("SwayService", "Failed to switch workspace:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function focusWindow(window) {
|
||||
try {
|
||||
window.handle.activate()
|
||||
} catch (e) {
|
||||
Logger.error("SwayService", "Failed to switch window:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function closeWindow(window) {
|
||||
try {
|
||||
window.handle.close()
|
||||
} catch (e) {
|
||||
Logger.error("SwayService", "Failed to close window:", e)
|
||||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
try {
|
||||
Quickshell.execDetached(["swaymsg", "exit"])
|
||||
} catch (e) {
|
||||
Logger.error("SwayService", "Failed to logout:", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user