From 45a1316bf1df6ec32a133f8e648bbac0bbc988d6 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 30 Aug 2025 22:07:54 +0200 Subject: ... --- .../shell/quickshell/Services/NiriService.qml | 190 +++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml (limited to 'accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml') diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml new file mode 100644 index 00000000..914152e1 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml @@ -0,0 +1,190 @@ +pragma Singleton + +import Quickshell +import Quickshell.Io +import QtQuick + +Singleton { + id: root + + property var workspaces: [] + property var outputs: {} + property var keyboardLayouts: {} + property var windows: [] + readonly property string socketPath: Quickshell.env("NIRI_SOCKET") + + onKeyboardLayoutsChanged: { + console.log(JSON.stringify(keyboardLayouts)); + } + + function refreshOutputs() { + commandSocket.sendCommand("Outputs", data => { + outputs = data.Ok.Outputs; + }); + } + + function sendCommand(command, callback) { + commandSocket.sendCommand(command, callback); + } + + Socket { + id: eventStreamSocket + path: root.socketPath + connected: true + + onConnectionStateChanged: { + if (connected) { + write('"EventStream"\n') + } + } + + parser: SplitParser { + onRead: line => { + try { + const event = JSON.parse(line) + + // console.log(JSON.stringify(event)) + + if (event.WorkspacesChanged) { + root.workspaces = event.WorkspacesChanged.workspaces + root.refreshOutputs(); + } else if (event.WorkspaceActivated) + eventWorkspaceActivated(event.WorkspaceActivated); + else if (event.WorkspaceUrgencyChanged) + eventWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged); + else if (event.WorkspaceActiveWindowChanged) + eventWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged); + else if (event.KeyboardLayoutsChanged) + root.keyboardLayouts = event.KeyboardLayoutsChanged.keyboard_layouts; + else if (event.KeyboardLayoutSwitched) + root.keyboardLayouts = Object.assign({}, root.keyboardLayouts, {"current_idx": event.KeyboardLayoutSwitched.idx }); + else if (event.WindowsChanged) + root.windows = event.WindowsChanged.windows + else if (event.WindowOpenedOrChanged) + eventWindowOpenedOrChanged(event.WindowOpenedOrChanged); + else if (event.WindowClosed) + eventWindowClosed(event.WindowClosed); + else if (event.WindowFocusChanged) + eventWindowFocusChanged(event.WindowFocusChanged); + else if (event.WindowUrgencyChanged) + eventWindowUrgencyChanged(event.WindowUrgencyChanged); + else if (event.WindowLayoutsChanged) + eventWindowLayoutsChanged(event.WindowLayoutsChanged); + } catch (e) { + console.warn("NiriService: Failed to parse event:", line, e) + } + } + } + } + + Socket { + id: commandSocket + path: root.socketPath + connected: true + + property var awaitingAnswer: null + property var cmdQueue: [] + + parser: SplitParser { + onRead: line => { + if (commandSocket.awaitingAnswer === null) + return; + + try { + const response = JSON.parse(line); + commandSocket.awaitingAnswer.callback(response); + commandSocket.awaitingAnswer = null; + } catch (e) { + console.warn("NiriService: Failed to parse response:", line, e) + } + commandSocket._handleQueue(); + } + } + + onCmdQueueChanged: { + _handleQueue(); + } + onAwaitingAnswerChanged: { + _handleQueue(); + } + + function _handleQueue() { + if (cmdQueue.length <= 0 || awaitingAnswer !== null) + return; + + let localQueue = Array.from(cmdQueue); + awaitingAnswer = localQueue.shift(); + cmdQueue = localQueue; + write(JSON.stringify(awaitingAnswer.command) + '\n'); + } + + function sendCommand(command, callback) { + cmdQueue = Array.from(cmdQueue).concat([{ "command": command, "callback": callback }]) + } + } + + function eventWorkspaceActivated(data) { + let relevant_output = null; + Array.from(root.workspaces).forEach(ws => { + if (data.id === ws.id) + relevant_output = ws.output; + }); + root.workspaces = Array.from(root.workspaces).map(ws => { + if (ws.output === relevant_output) { + ws.is_active = false; + ws.is_focused = false; + } + if (data.id === ws.id) { + ws.is_active = true; + ws.is_focused = data.focused; + } + return ws; + }); + } + function eventWorkspaceUrgencyChanged(data) { + root.workspaces = Array.from(root.workspaces).map(ws => { + if (data.id == ws.id) + ws.is_urgent = data.urgent; + return ws; + }); + } + function eventWorkspaceActiveWindowChanged(data) { + root.workspaces = Array.from(root.workspaces).map(ws => { + if (data.workspace_id === ws.id) + ws.active_window_id = data.active_window_id; + return ws; + }); + } + function eventWindowOpenedOrChanged(data) { + root.windows = Array.from(root.windows).map(win => { + if (win.id === data.window.id) + return data.window; + return win; + }); + } + function eventWindowClosed(data) { + root.windows = Array.from(root.windows).filter(win => win.id !== data.id); + } + function eventWindowFocusChanged(data) { + root.windows = Array.from(root.windows).map(win => { + win.is_focused = win.id === data.id; + return win; + }); + } + function eventWindowUrgencyChanged(data) { + root.windows = Array.from(root.windows).map(win => { + if (win.id === data.id) + win.is_urgent = data.urgent; + return win; + }); + } + function eventWindowLayoutsChanged(data) { + root.windows = Array.from(root.windows).map(win => { + Array.from(data.changes).forEach(change => { + if (win.id === change[0]) + win.layout = change[1]; + }); + return win; + }); + } +} -- cgit v1.2.3