mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
d2a5150f10
This reverts commit b391d03967.
230 lines
5.9 KiB
QML
230 lines
5.9 KiB
QML
pragma Singleton
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import qs.Commons
|
|
import qs.Services
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
// Compositor detection
|
|
property bool isHyprland: false
|
|
property bool isNiri: false
|
|
|
|
// Generic workspace and window data
|
|
property ListModel workspaces: ListModel {}
|
|
property ListModel windows: ListModel {}
|
|
property int focusedWindowIndex: -1
|
|
|
|
// Generic events
|
|
signal workspaceChanged
|
|
signal activeWindowChanged
|
|
signal windowListChanged
|
|
|
|
// Backend service loader
|
|
property var backend: null
|
|
|
|
Component.onCompleted: {
|
|
detectCompositor()
|
|
}
|
|
|
|
function detectCompositor() {
|
|
const hyprlandSignature = Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
|
|
if (hyprlandSignature && hyprlandSignature.length > 0) {
|
|
isHyprland = true
|
|
isNiri = false
|
|
backendLoader.sourceComponent = hyprlandComponent
|
|
} else {
|
|
// Default to Niri
|
|
isHyprland = false
|
|
isNiri = true
|
|
backendLoader.sourceComponent = niriComponent
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: backendLoader
|
|
onLoaded: {
|
|
if (item) {
|
|
root.backend = item
|
|
setupBackendConnections()
|
|
backend.initialize()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hyprland backend component
|
|
Component {
|
|
id: hyprlandComponent
|
|
HyprlandService {
|
|
id: hyprlandBackend
|
|
}
|
|
}
|
|
|
|
// Niri backend component
|
|
Component {
|
|
id: niriComponent
|
|
NiriService {
|
|
id: niriBackend
|
|
}
|
|
}
|
|
|
|
function setupBackendConnections() {
|
|
if (!backend)
|
|
return
|
|
|
|
// Connect backend signals to facade signals
|
|
backend.workspaceChanged.connect(() => {
|
|
// Sync workspaces when they change
|
|
syncWorkspaces()
|
|
// Forward the signal
|
|
workspaceChanged()
|
|
})
|
|
|
|
backend.activeWindowChanged.connect(() => {
|
|
// Sync active window when it changes
|
|
// TODO: Avoid re-syncing all windows
|
|
syncWindows()
|
|
// Forward the signal
|
|
activeWindowChanged()
|
|
})
|
|
|
|
backend.windowListChanged.connect(() => {
|
|
// Sync windows when they change
|
|
syncWindows()
|
|
// Forward the signal
|
|
windowListChanged()
|
|
})
|
|
|
|
// Property bindings
|
|
backend.focusedWindowIndexChanged.connect(() => {
|
|
focusedWindowIndex = backend.focusedWindowIndex
|
|
})
|
|
|
|
// Initial sync
|
|
syncWorkspaces()
|
|
syncWindows()
|
|
focusedWindowIndex = backend.focusedWindowIndex
|
|
}
|
|
|
|
function syncWorkspaces() {
|
|
workspaces.clear()
|
|
const ws = backend.workspaces
|
|
for (var i = 0; i < ws.count; i++) {
|
|
workspaces.append(ws.get(i))
|
|
}
|
|
// Emit signal to notify listeners that workspace list has been updated
|
|
workspacesChanged()
|
|
}
|
|
|
|
function syncWindows() {
|
|
windows.clear()
|
|
const ws = backend.windows
|
|
for (var i = 0; i < ws.length; i++) {
|
|
windows.append(ws[i])
|
|
}
|
|
// Emit signal to notify listeners that workspace list has been updated
|
|
windowListChanged()
|
|
}
|
|
|
|
// Get focused window
|
|
function getFocusedWindow() {
|
|
if (focusedWindowIndex >= 0 && focusedWindowIndex < windows.count) {
|
|
return windows.get(focusedWindowIndex)
|
|
}
|
|
return null
|
|
}
|
|
|
|
// Get focused window title
|
|
function getFocusedWindowTitle() {
|
|
if (focusedWindowIndex >= 0 && focusedWindowIndex < windows.count) {
|
|
return windows.get(focusedWindowIndex).title || ""
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Generic workspace switching
|
|
function switchToWorkspace(workspaceId) {
|
|
if (backend && backend.switchToWorkspace) {
|
|
backend.switchToWorkspace(workspaceId)
|
|
} else {
|
|
Logger.warn("Compositor", "No backend available for workspace switching")
|
|
}
|
|
}
|
|
|
|
// Get current workspace
|
|
function getCurrentWorkspace() {
|
|
for (var i = 0; i < workspaces.count; i++) {
|
|
const ws = workspaces.get(i)
|
|
if (ws.isFocused) {
|
|
return ws
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
// Get active workspaces
|
|
function getActiveWorkspaces() {
|
|
const activeWorkspaces = []
|
|
for (var i = 0; i < workspaces.count; i++) {
|
|
const ws = workspaces.get(i)
|
|
if (ws.isActive) {
|
|
activeWorkspaces.push(ws)
|
|
}
|
|
}
|
|
return activeWorkspaces
|
|
}
|
|
|
|
// Set focused window
|
|
function focusWindow(windowId) {
|
|
if (backend && backend.focusWindow) {
|
|
backend.focusWindow(windowId)
|
|
} else {
|
|
Logger.warn("Compositor", "No backend available for window focus")
|
|
}
|
|
}
|
|
|
|
// Close window
|
|
function closeWindow(windowId) {
|
|
if (backend && backend.closeWindow) {
|
|
backend.closeWindow(windowId)
|
|
} else {
|
|
Logger.warn("Compositor", "No backend available for window closing")
|
|
}
|
|
}
|
|
|
|
// Session management
|
|
function logout() {
|
|
if (backend && backend.logout) {
|
|
backend.logout()
|
|
} else {
|
|
Logger.warn("Compositor", "No backend available for logout")
|
|
}
|
|
}
|
|
|
|
function shutdown() {
|
|
Quickshell.execDetached(["shutdown", "-h", "now"])
|
|
}
|
|
|
|
function reboot() {
|
|
Quickshell.execDetached(["reboot"])
|
|
}
|
|
|
|
function suspend() {
|
|
Quickshell.execDetached(["systemctl", "suspend"])
|
|
}
|
|
|
|
function lockAndSuspend() {
|
|
try {
|
|
if (PanelService && PanelService.lockScreen && !PanelService.lockScreen.active) {
|
|
PanelService.lockScreen.active = true
|
|
}
|
|
} catch (e) {
|
|
Logger.warn("Compositor", "Failed to activate lock screen before suspend: " + e)
|
|
}
|
|
// Queue suspend to the next event loop cycle to allow lock UI to render
|
|
Qt.callLater(suspend)
|
|
}
|
|
}
|