mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-05-30 09:19:08 +00:00
Niri: receive events directly from socket
This commit is contained in:
+110
-111
@@ -27,155 +27,154 @@ Item {
|
||||
|
||||
// Initialization
|
||||
function initialize() {
|
||||
niriEventStream.running = true;
|
||||
niriEventStream.connected = true;
|
||||
niriCommandSocket.connected = true;
|
||||
|
||||
startEventStream();
|
||||
updateWorkspaces();
|
||||
updateWindows();
|
||||
queryDisplayScales();
|
||||
Logger.i("NiriService", "Service started");
|
||||
}
|
||||
|
||||
// command from https://yalter.github.io/niri/niri_ipc/enum.Request.html
|
||||
function sendSocketCommand(sock, command) {
|
||||
sock.write(JSON.stringify(command) + "\n");
|
||||
sock.flush();
|
||||
}
|
||||
|
||||
function startEventStream() {
|
||||
sendSocketCommand(niriEventStream, "EventStream");
|
||||
}
|
||||
|
||||
// Update workspaces
|
||||
function updateWorkspaces() {
|
||||
niriWorkspaceProcess.running = true;
|
||||
sendSocketCommand(niriCommandSocket, "Workspaces");
|
||||
}
|
||||
|
||||
// Update windows
|
||||
function updateWindows() {
|
||||
niriWindowsProcess.running = true;
|
||||
sendSocketCommand(niriCommandSocket, "Windows");
|
||||
}
|
||||
|
||||
// Query display scales
|
||||
function queryDisplayScales() {
|
||||
niriOutputsProcess.running = true;
|
||||
sendSocketCommand(niriCommandSocket, "Outputs");
|
||||
}
|
||||
|
||||
// Niri outputs process for display scale detection
|
||||
Process {
|
||||
id: niriOutputsProcess
|
||||
running: false
|
||||
command: ["niri", "msg", "--json", "outputs"]
|
||||
function recollectOutputs(outputsData) {
|
||||
const scales = {};
|
||||
|
||||
stdout: SplitParser {
|
||||
// Niri returns an object with display names as keys
|
||||
for (const outputName in outputsData) {
|
||||
const output = outputsData[outputName];
|
||||
if (output && output.name) {
|
||||
const logical = output.logical || {};
|
||||
const currentModeIdx = output.current_mode || 0;
|
||||
const modes = output.modes || [];
|
||||
const currentMode = modes[currentModeIdx] || {};
|
||||
|
||||
scales[output.name] = {
|
||||
"name": output.name,
|
||||
"scale": logical.scale || 1.0,
|
||||
"width": logical.width || 0,
|
||||
"height": logical.height || 0,
|
||||
"x": logical.x || 0,
|
||||
"y": logical.y || 0,
|
||||
"physical_width": (output.physical_size && output.physical_size[0]) || 0,
|
||||
"physical_height": (output.physical_size && output.physical_size[1]) || 0,
|
||||
"refresh_rate": currentMode.refresh_rate || 0,
|
||||
"vrr_supported": output.vrr_supported || false,
|
||||
"vrr_enabled": output.vrr_enabled || false,
|
||||
"transform": logical.transform || "Normal"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Notify CompositorService (it will emit displayScalesChanged)
|
||||
if (CompositorService && CompositorService.onDisplayScalesUpdated) {
|
||||
CompositorService.onDisplayScalesUpdated(scales);
|
||||
}
|
||||
}
|
||||
|
||||
function recollectWorkspaces(workspacesData) {
|
||||
const workspacesList = [];
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
// Sort workspaces by output, then by index
|
||||
workspacesList.sort((a, b) => {
|
||||
if (a.output !== b.output) {
|
||||
return a.output.localeCompare(b.output);
|
||||
}
|
||||
return a.idx - b.idx;
|
||||
});
|
||||
|
||||
// Update the workspaces ListModel
|
||||
workspaces.clear();
|
||||
for (var i = 0; i < workspacesList.length; i++) {
|
||||
workspaces.append(workspacesList[i]);
|
||||
}
|
||||
|
||||
workspaceChanged();
|
||||
}
|
||||
|
||||
// Niri command socket
|
||||
Socket {
|
||||
id: niriCommandSocket
|
||||
path: Quickshell.env("NIRI_SOCKET")
|
||||
connected: false
|
||||
|
||||
parser: SplitParser {
|
||||
onRead: function (line) {
|
||||
try {
|
||||
const outputsData = JSON.parse(line);
|
||||
const scales = {};
|
||||
const data = JSON.parse(line);
|
||||
|
||||
// Niri returns an object with display names as keys
|
||||
for (const outputName in outputsData) {
|
||||
const output = outputsData[outputName];
|
||||
if (output && output.name) {
|
||||
const logical = output.logical || {};
|
||||
const currentModeIdx = output.current_mode || 0;
|
||||
const modes = output.modes || [];
|
||||
const currentMode = modes[currentModeIdx] || {};
|
||||
|
||||
scales[output.name] = {
|
||||
"name": output.name,
|
||||
"scale": logical.scale || 1.0,
|
||||
"width": logical.width || 0,
|
||||
"height": logical.height || 0,
|
||||
"x": logical.x || 0,
|
||||
"y": logical.y || 0,
|
||||
"physical_width": (output.physical_size && output.physical_size[0]) || 0,
|
||||
"physical_height": (output.physical_size && output.physical_size[1]) || 0,
|
||||
"refresh_rate": currentMode.refresh_rate || 0,
|
||||
"vrr_supported": output.vrr_supported || false,
|
||||
"vrr_enabled": output.vrr_enabled || false,
|
||||
"transform": logical.transform || "Normal"
|
||||
};
|
||||
if (data && data.Ok) {
|
||||
const res = data.Ok;
|
||||
if (res.Windows) {
|
||||
recollectWindows(res.Windows);
|
||||
} else if (res.Outputs) {
|
||||
recollectOutputs(res.Outputs);
|
||||
} else if (res.Workspaces) {
|
||||
recollectWorkspaces(res.Workspaces);
|
||||
}
|
||||
} else {
|
||||
Logger.e("NiriService", "Niri returned an error:", data.Err, line);
|
||||
}
|
||||
|
||||
// Notify CompositorService (it will emit displayScalesChanged)
|
||||
if (CompositorService && CompositorService.onDisplayScalesUpdated) {
|
||||
CompositorService.onDisplayScalesUpdated(scales);
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.e("NiriService", "Failed to parse outputs:", e, line);
|
||||
Logger.e("NiriService", "Failed to parse data from socket:", e, line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Niri workspace process
|
||||
Process {
|
||||
id: niriWorkspaceProcess
|
||||
running: false
|
||||
command: ["niri", "msg", "--json", "workspaces"]
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: function (line) {
|
||||
try {
|
||||
const workspacesData = JSON.parse(line);
|
||||
const workspacesList = [];
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
// Sort workspaces by output, then by index
|
||||
workspacesList.sort((a, b) => {
|
||||
if (a.output !== b.output) {
|
||||
return a.output.localeCompare(b.output);
|
||||
}
|
||||
return a.idx - b.idx;
|
||||
});
|
||||
|
||||
// Update the workspaces ListModel
|
||||
workspaces.clear();
|
||||
for (var i = 0; i < workspacesList.length; i++) {
|
||||
workspaces.append(workspacesList[i]);
|
||||
}
|
||||
|
||||
workspaceChanged();
|
||||
} catch (e) {
|
||||
Logger.e("NiriService", "Failed to parse workspaces:", e, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Niri windows process
|
||||
Process {
|
||||
id: niriWindowsProcess
|
||||
running: false
|
||||
command: ["niri", "msg", "--json", "windows"]
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: function (line) {
|
||||
try {
|
||||
const windowsData = JSON.parse(line);
|
||||
recollectWindows(windowsData);
|
||||
} catch (e) {
|
||||
Logger.e("NiriService", "Failed to parse windows:", e, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Niri event stream process
|
||||
Process {
|
||||
// Niri event stream socket
|
||||
Socket {
|
||||
id: niriEventStream
|
||||
running: false
|
||||
command: ["niri", "msg", "--json", "event-stream"]
|
||||
path: Quickshell.env("NIRI_SOCKET")
|
||||
connected: false
|
||||
|
||||
stdout: SplitParser {
|
||||
parser: SplitParser {
|
||||
onRead: data => {
|
||||
try {
|
||||
const event = JSON.parse(data.trim());
|
||||
|
||||
if (event.WorkspacesChanged) {
|
||||
updateWorkspaces();
|
||||
recollectWorkspaces(event.WorkspacesChanged.workspaces);
|
||||
} else if (event.WindowOpenedOrChanged) {
|
||||
handleWindowOpenedOrChanged(event.WindowOpenedOrChanged);
|
||||
} else if (event.WindowClosed) {
|
||||
|
||||
Reference in New Issue
Block a user