1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00
Files
DankMaterialShell/quickshell/Services/NiriService.qml

1051 lines
31 KiB
QML

pragma Singleton
pragma ComponentBehavior: Bound
import QtCore
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
Singleton {
id: root
readonly property string socketPath: Quickshell.env("NIRI_SOCKET")
property var workspaces: ({})
property var allWorkspaces: []
property int focusedWorkspaceIndex: 0
property string focusedWorkspaceId: ""
property var currentOutputWorkspaces: []
property string currentOutput: ""
property var outputs: ({})
property var windows: []
property var displayScales: ({})
property bool inOverview: false
property int currentKeyboardLayoutIndex: 0
property var keyboardLayoutNames: []
property string configValidationOutput: ""
property bool hasInitialConnection: false
property bool suppressConfigToast: true
property bool suppressNextConfigToast: false
property bool matugenSuppression: false
property bool configGenerationPending: false
readonly property string screenshotsDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.PicturesLocation)) + "/Screenshots"
property string pendingScreenshotPath: ""
signal windowUrgentChanged
signal configReloaded
function setWorkspaces(newMap) {
root.workspaces = newMap;
allWorkspaces = Object.values(newMap).sort((a, b) => a.idx - b.idx);
}
Component.onCompleted: fetchOutputs()
Timer {
id: suppressToastTimer
interval: 3000
onTriggered: root.suppressConfigToast = false
}
Timer {
id: suppressResetTimer
interval: 2000
onTriggered: root.matugenSuppression = false
}
Timer {
id: configGenerationDebounce
interval: 100
onTriggered: root.doGenerateNiriLayoutConfig()
}
Process {
id: validateProcess
command: ["niri", "validate"]
running: false
stderr: StdioCollector {
onStreamFinished: {
const lines = text.split('\n');
const trimmedLines = lines.map(line => line.replace(/\s+$/, '')).filter(line => line.length > 0);
configValidationOutput = trimmedLines.join('\n').trim();
if (hasInitialConnection) {
ToastService.showError("niri: failed to load config", configValidationOutput, "", "niri-config");
}
}
}
onExited: exitCode => {
if (exitCode === 0) {
configValidationOutput = "";
}
}
}
Process {
id: writeConfigProcess
property string configContent: ""
property string configPath: ""
onExited: exitCode => {
if (exitCode === 0) {
console.info("NiriService: Generated layout config at", configPath);
return;
}
console.warn("NiriService: Failed to write layout config, exit code:", exitCode);
}
}
Process {
id: writeAlttabProcess
property string alttabContent: ""
property string alttabPath: ""
onExited: exitCode => {
if (exitCode === 0) {
console.info("NiriService: Generated alttab config at", alttabPath);
return;
}
console.warn("NiriService: Failed to write alttab config, exit code:", exitCode);
}
}
Process {
id: writeBlurruleProcess
property string blurrulePath: ""
onExited: exitCode => {
if (exitCode === 0) {
console.info("NiriService: Generated wpblur config at", blurrulePath);
return;
}
console.warn("NiriService: Failed to write wpblur config, exit code:", exitCode);
}
}
DankSocket {
id: eventStreamSocket
path: root.socketPath
connected: CompositorService.isNiri
onConnectionStateChanged: {
if (connected) {
send('"EventStream"');
fetchOutputs();
}
}
parser: SplitParser {
onRead: line => {
try {
const event = JSON.parse(line);
handleNiriEvent(event);
} catch (e) {
console.warn("NiriService: Failed to parse event:", line, e);
}
}
}
}
DankSocket {
id: requestSocket
path: root.socketPath
connected: CompositorService.isNiri
}
function fetchOutputs() {
if (!CompositorService.isNiri)
return;
Proc.runCommand("niri-fetch-outputs", ["niri", "msg", "-j", "outputs"], (output, exitCode) => {
if (exitCode !== 0) {
console.warn("NiriService: Failed to fetch outputs, exit code:", exitCode);
return;
}
try {
const outputsData = JSON.parse(output);
outputs = outputsData;
console.info("NiriService: Loaded", Object.keys(outputsData).length, "outputs");
updateDisplayScales();
if (windows.length > 0) {
windows = sortWindowsByLayout(windows);
}
} catch (e) {
console.warn("NiriService: Failed to parse outputs:", e);
}
});
}
function updateDisplayScales() {
if (!outputs || Object.keys(outputs).length === 0)
return;
const scales = {};
for (const outputName in outputs) {
const output = outputs[outputName];
if (output.logical && output.logical.scale !== undefined) {
scales[outputName] = output.logical.scale;
}
}
displayScales = scales;
}
function sortWindowsByLayout(windowList) {
const enriched = windowList.map(w => {
const ws = workspaces[w.workspace_id];
if (!ws) {
return {
window: w,
outputX: 999999,
outputY: 999999,
wsIdx: 999999,
col: 999999,
row: 999999
};
}
const outputInfo = outputs[ws.output];
const outputX = (outputInfo && outputInfo.logical) ? outputInfo.logical.x : 999999;
const outputY = (outputInfo && outputInfo.logical) ? outputInfo.logical.y : 999999;
const pos = w.layout?.pos_in_scrolling_layout;
const col = (pos && pos.length >= 2) ? pos[0] : 999999;
const row = (pos && pos.length >= 2) ? pos[1] : 999999;
return {
window: w,
outputX: outputX,
outputY: outputY,
wsIdx: ws.idx,
col: col,
row: row
};
});
enriched.sort((a, b) => {
if (a.outputX !== b.outputX)
return a.outputX - b.outputX;
if (a.outputY !== b.outputY)
return a.outputY - b.outputY;
if (a.wsIdx !== b.wsIdx)
return a.wsIdx - b.wsIdx;
if (a.col !== b.col)
return a.col - b.col;
if (a.row !== b.row)
return a.row - b.row;
return a.window.id - b.window.id;
});
return enriched.map(e => e.window);
}
function handleNiriEvent(event) {
const eventType = Object.keys(event)[0];
switch (eventType) {
case 'WorkspacesChanged':
handleWorkspacesChanged(event.WorkspacesChanged);
break;
case 'WorkspaceActivated':
handleWorkspaceActivated(event.WorkspaceActivated);
break;
case 'WorkspaceActiveWindowChanged':
handleWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged);
break;
case 'WindowFocusChanged':
handleWindowFocusChanged(event.WindowFocusChanged);
break;
case 'WindowsChanged':
handleWindowsChanged(event.WindowsChanged);
break;
case 'WindowClosed':
handleWindowClosed(event.WindowClosed);
break;
case 'WindowOpenedOrChanged':
handleWindowOpenedOrChanged(event.WindowOpenedOrChanged);
break;
case 'WindowLayoutsChanged':
handleWindowLayoutsChanged(event.WindowLayoutsChanged);
break;
case 'OutputsChanged':
handleOutputsChanged(event.OutputsChanged);
break;
case 'OverviewOpenedOrClosed':
handleOverviewChanged(event.OverviewOpenedOrClosed);
break;
case 'ConfigLoaded':
handleConfigLoaded(event.ConfigLoaded);
break;
case 'KeyboardLayoutsChanged':
handleKeyboardLayoutsChanged(event.KeyboardLayoutsChanged);
break;
case 'KeyboardLayoutSwitched':
handleKeyboardLayoutSwitched(event.KeyboardLayoutSwitched);
break;
case 'WorkspaceUrgencyChanged':
handleWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged);
break;
case 'ScreenshotCaptured':
handleScreenshotCaptured(event.ScreenshotCaptured);
break;
}
}
function handleWorkspacesChanged(data) {
const newWorkspaces = {};
for (const ws of data.workspaces) {
const oldWs = root.workspaces[ws.id];
newWorkspaces[ws.id] = ws;
if (oldWs && oldWs.active_window_id !== undefined) {
newWorkspaces[ws.id].active_window_id = oldWs.active_window_id;
}
}
setWorkspaces(newWorkspaces);
focusedWorkspaceIndex = allWorkspaces.findIndex(w => w.is_focused);
if (focusedWorkspaceIndex >= 0) {
const focusedWs = allWorkspaces[focusedWorkspaceIndex];
focusedWorkspaceId = focusedWs.id;
currentOutput = focusedWs.output || "";
} else {
focusedWorkspaceIndex = 0;
focusedWorkspaceId = "";
}
updateCurrentOutputWorkspaces();
}
function handleWorkspaceActivated(data) {
const ws = root.workspaces[data.id];
if (!ws) {
return;
}
const output = ws.output;
const updatedWorkspaces = {};
for (const id in root.workspaces) {
const workspace = root.workspaces[id];
const got_activated = workspace.id === data.id;
const updatedWs = {};
for (let prop in workspace) {
updatedWs[prop] = workspace[prop];
}
if (workspace.output === output) {
updatedWs.is_active = got_activated;
}
if (data.focused) {
updatedWs.is_focused = got_activated;
}
updatedWorkspaces[id] = updatedWs;
}
setWorkspaces(updatedWorkspaces);
focusedWorkspaceId = data.id;
focusedWorkspaceIndex = allWorkspaces.findIndex(w => w.id === data.id);
if (focusedWorkspaceIndex >= 0) {
currentOutput = allWorkspaces[focusedWorkspaceIndex].output || "";
}
updateCurrentOutputWorkspaces();
}
function handleWindowFocusChanged(data) {
const focusedWindowId = data.id;
let focusedWindow = null;
const updatedWindows = [];
for (var i = 0; i < windows.length; i++) {
const w = windows[i];
const updatedWindow = {};
for (let prop in w) {
updatedWindow[prop] = w[prop];
}
updatedWindow.is_focused = (w.id === focusedWindowId);
if (updatedWindow.is_focused) {
focusedWindow = updatedWindow;
}
updatedWindows.push(updatedWindow);
}
windows = updatedWindows;
if (focusedWindow) {
const ws = root.workspaces[focusedWindow.workspace_id];
if (ws && ws.active_window_id !== focusedWindowId) {
const updatedWs = {};
for (let prop in ws) {
updatedWs[prop] = ws[prop];
}
updatedWs.active_window_id = focusedWindowId;
const updatedWorkspaces = {};
for (const id in root.workspaces) {
updatedWorkspaces[id] = id === focusedWindow.workspace_id ? updatedWs : root.workspaces[id];
}
setWorkspaces(updatedWorkspaces);
}
}
}
function handleWorkspaceActiveWindowChanged(data) {
const ws = root.workspaces[data.workspace_id];
if (ws) {
const updatedWs = {};
for (let prop in ws) {
updatedWs[prop] = ws[prop];
}
updatedWs.active_window_id = data.active_window_id;
const updatedWorkspaces = {};
for (const id in root.workspaces) {
updatedWorkspaces[id] = id === data.workspace_id ? updatedWs : root.workspaces[id];
}
setWorkspaces(updatedWorkspaces);
}
const updatedWindows = [];
for (var i = 0; i < windows.length; i++) {
const w = windows[i];
const updatedWindow = {};
for (let prop in w) {
updatedWindow[prop] = w[prop];
}
if (data.active_window_id !== null && data.active_window_id !== undefined) {
updatedWindow.is_focused = (w.id == data.active_window_id);
} else {
updatedWindow.is_focused = w.workspace_id == data.workspace_id ? false : w.is_focused;
}
updatedWindows.push(updatedWindow);
}
windows = updatedWindows;
}
function handleWindowsChanged(data) {
windows = sortWindowsByLayout(data.windows);
}
function handleWindowClosed(data) {
windows = windows.filter(w => w.id !== data.id);
}
function handleWindowOpenedOrChanged(data) {
if (!data.window)
return;
const window = data.window;
const existingIndex = windows.findIndex(w => w.id === window.id);
if (existingIndex >= 0) {
const updatedWindows = [...windows];
updatedWindows[existingIndex] = window;
windows = sortWindowsByLayout(updatedWindows);
} else {
windows = sortWindowsByLayout([...windows, window]);
}
}
function handleWindowLayoutsChanged(data) {
if (!data.changes)
return;
const updatedWindows = [...windows];
let hasChanges = false;
for (const change of data.changes) {
const windowId = change[0];
const layoutData = change[1];
const windowIndex = updatedWindows.findIndex(w => w.id === windowId);
if (windowIndex < 0)
continue;
const updatedWindow = {};
for (var prop in updatedWindows[windowIndex]) {
updatedWindow[prop] = updatedWindows[windowIndex][prop];
}
updatedWindow.layout = layoutData;
updatedWindows[windowIndex] = updatedWindow;
hasChanges = true;
}
if (!hasChanges)
return;
windows = sortWindowsByLayout(updatedWindows);
}
function handleOutputsChanged(data) {
if (!data.outputs)
return;
outputs = data.outputs;
updateDisplayScales();
windows = sortWindowsByLayout(windows);
}
function handleOverviewChanged(data) {
inOverview = data.is_open;
}
function handleConfigLoaded(data) {
if (data.failed) {
validateProcess.running = true;
return;
}
configValidationOutput = "";
ToastService.dismissCategory("niri-config");
fetchOutputs();
configReloaded();
if (hasInitialConnection && !suppressConfigToast && !suppressNextConfigToast && !matugenSuppression) {
ToastService.showInfo("niri: config reloaded", "", "", "niri-config");
} else if (suppressNextConfigToast) {
suppressNextConfigToast = false;
suppressResetTimer.stop();
}
if (!hasInitialConnection) {
hasInitialConnection = true;
suppressToastTimer.start();
}
}
function handleKeyboardLayoutsChanged(data) {
keyboardLayoutNames = data.keyboard_layouts.names;
currentKeyboardLayoutIndex = data.keyboard_layouts.current_idx;
}
function handleKeyboardLayoutSwitched(data) {
currentKeyboardLayoutIndex = data.idx;
}
function handleWorkspaceUrgencyChanged(data) {
const ws = root.workspaces[data.id];
if (!ws)
return;
const updatedWs = {};
for (let prop in ws) {
updatedWs[prop] = ws[prop];
}
updatedWs.is_urgent = data.urgent;
const updatedWorkspaces = {};
for (const id in root.workspaces) {
updatedWorkspaces[id] = id === data.id ? updatedWs : root.workspaces[id];
}
setWorkspaces(updatedWorkspaces);
windowUrgentChanged();
}
function handleScreenshotCaptured(data) {
if (!data.path)
return;
if (pendingScreenshotPath && data.path === pendingScreenshotPath) {
const editor = Quickshell.env("DMS_SCREENSHOT_EDITOR");
const command = editor === "satty" ? ["satty", "-f", data.path] : ["swappy", "-f", data.path];
Quickshell.execDetached({
command: command
});
pendingScreenshotPath = "";
}
}
function updateCurrentOutputWorkspaces() {
if (!currentOutput) {
currentOutputWorkspaces = allWorkspaces;
return;
}
const outputWs = allWorkspaces.filter(w => w.output === currentOutput);
currentOutputWorkspaces = outputWs;
}
function send(request) {
if (!CompositorService.isNiri || !requestSocket.connected)
return false;
requestSocket.send(request);
return true;
}
function doScreenTransition() {
return send({
"Action": {
"DoScreenTransition": {
"delay_ms": 0
}
}
});
}
function toggleOverview() {
return send({
"Action": {
"ToggleOverview": {}
}
});
}
function moveColumnLeft() {
return send({
"Action": {
"FocusColumnLeft": {}
}
});
}
function moveColumnRight() {
return send({
"Action": {
"FocusColumnRight": {}
}
});
}
function moveWorkspaceDown() {
return send({
"Action": {
"FocusWorkspaceDown": {}
}
});
}
function moveWorkspaceUp() {
return send({
"Action": {
"FocusWorkspaceUp": {}
}
});
}
function switchToWorkspace(workspaceIndex) {
return send({
"Action": {
"FocusWorkspace": {
"reference": {
"Index": workspaceIndex
}
}
}
});
}
function focusWindow(windowId) {
return send({
"Action": {
"FocusWindow": {
"id": windowId
}
}
});
}
function powerOffMonitors() {
return send({
"Action": {
"PowerOffMonitors": {}
}
});
}
function powerOnMonitors() {
return send({
"Action": {
"PowerOnMonitors": {}
}
});
}
function cycleKeyboardLayout() {
return send({
"Action": {
"SwitchLayout": {
"layout": "Next"
}
}
});
}
function quit() {
return send({
"Action": {
"Quit": {
"skip_confirmation": true
}
}
});
}
function screenshot() {
pendingScreenshotPath = "";
const timestamp = Date.now();
const path = `${screenshotsDir}/dms-screenshot-${timestamp}.png`;
pendingScreenshotPath = path;
return send({
"Action": {
"Screenshot": {
"show_pointer": true,
"path": path
}
}
});
}
function screenshotScreen() {
pendingScreenshotPath = "";
const timestamp = Date.now();
const path = `${screenshotsDir}/dms-screenshot-${timestamp}.png`;
pendingScreenshotPath = path;
return send({
"Action": {
"ScreenshotScreen": {
"write_to_disk": true,
"show_pointer": true,
"path": path
}
}
});
}
function screenshotWindow() {
pendingScreenshotPath = "";
const timestamp = Date.now();
const path = `${screenshotsDir}/dms-screenshot-${timestamp}.png`;
pendingScreenshotPath = path;
return send({
"Action": {
"ScreenshotWindow": {
"write_to_disk": true,
"show_pointer": true,
"path": path
}
}
});
}
function getCurrentOutputWorkspaceNumbers() {
return currentOutputWorkspaces.map(w => w.idx + 1);
}
function getCurrentWorkspaceNumber() {
if (focusedWorkspaceIndex >= 0 && focusedWorkspaceIndex < allWorkspaces.length) {
return allWorkspaces[focusedWorkspaceIndex].idx + 1;
}
return 1;
}
function getCurrentKeyboardLayoutName() {
if (currentKeyboardLayoutIndex >= 0 && currentKeyboardLayoutIndex < keyboardLayoutNames.length) {
return keyboardLayoutNames[currentKeyboardLayoutIndex];
}
return "";
}
function suppressNextToast() {
matugenSuppression = true;
suppressResetTimer.restart();
}
function findNiriWindow(toplevel) {
if (!toplevel.appId)
return null;
for (var j = 0; j < windows.length; j++) {
const niriWindow = windows[j];
if (niriWindow.app_id === toplevel.appId) {
if (!niriWindow.title || niriWindow.title === toplevel.title) {
return {
"niriIndex": j,
"niriWindow": niriWindow
};
}
}
}
return null;
}
function sortToplevels(toplevels) {
if (!toplevels || toplevels.length === 0 || !CompositorService.isNiri || windows.length === 0) {
return [...toplevels];
}
const usedToplevels = new Set();
const enrichedToplevels = [];
for (const niriWindow of sortWindowsByLayout(windows)) {
let bestMatch = null;
let bestScore = -1;
for (const toplevel of toplevels) {
if (usedToplevels.has(toplevel))
continue;
if (toplevel.appId === niriWindow.app_id) {
let score = 1;
if (niriWindow.title && toplevel.title) {
if (toplevel.title === niriWindow.title) {
score = 3;
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
score = 2;
}
}
if (score > bestScore) {
bestScore = score;
bestMatch = toplevel;
if (score === 3)
break;
}
}
}
if (!bestMatch)
continue;
usedToplevels.add(bestMatch);
const workspace = workspaces[niriWindow.workspace_id];
const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false;
const enrichedToplevel = {
"appId": bestMatch.appId,
"title": bestMatch.title,
"activated": isFocused,
"niriWindowId": niriWindow.id,
"niriWorkspaceId": niriWindow.workspace_id,
"activate": function () {
return NiriService.focusWindow(niriWindow.id);
},
"close": function () {
if (bestMatch.close) {
return bestMatch.close();
}
return false;
}
};
for (let prop in bestMatch) {
if (!(prop in enrichedToplevel)) {
enrichedToplevel[prop] = bestMatch[prop];
}
}
enrichedToplevels.push(enrichedToplevel);
}
for (const toplevel of toplevels) {
if (!usedToplevels.has(toplevel)) {
enrichedToplevels.push(toplevel);
}
}
return enrichedToplevels;
}
function filterCurrentWorkspace(toplevels, screenName) {
let currentWorkspaceId = null;
for (var i = 0; i < allWorkspaces.length; i++) {
const ws = allWorkspaces[i];
if (ws.output === screenName && ws.is_active) {
currentWorkspaceId = ws.id;
break;
}
}
if (currentWorkspaceId === null)
return toplevels;
const workspaceWindows = windows.filter(niriWindow => niriWindow.workspace_id === currentWorkspaceId);
const usedToplevels = new Set();
const result = [];
for (const niriWindow of workspaceWindows) {
let bestMatch = null;
let bestScore = -1;
for (const toplevel of toplevels) {
if (usedToplevels.has(toplevel))
continue;
if (toplevel.appId === niriWindow.app_id) {
let score = 1;
if (niriWindow.title && toplevel.title) {
if (toplevel.title === niriWindow.title) {
score = 3;
} else if (toplevel.title.includes(niriWindow.title) || niriWindow.title.includes(toplevel.title)) {
score = 2;
}
}
if (score > bestScore) {
bestScore = score;
bestMatch = toplevel;
if (score === 3)
break;
}
}
}
if (!bestMatch)
continue;
usedToplevels.add(bestMatch);
const workspace = workspaces[niriWindow.workspace_id];
const isFocused = niriWindow.is_focused ?? (workspace && workspace.active_window_id === niriWindow.id) ?? false;
const enrichedToplevel = {
"appId": bestMatch.appId,
"title": bestMatch.title,
"activated": isFocused,
"niriWindowId": niriWindow.id,
"niriWorkspaceId": niriWindow.workspace_id,
"activate": function () {
return NiriService.focusWindow(niriWindow.id);
},
"close": function () {
if (bestMatch.close) {
return bestMatch.close();
}
return false;
}
};
for (let prop in bestMatch) {
if (!(prop in enrichedToplevel)) {
enrichedToplevel[prop] = bestMatch[prop];
}
}
result.push(enrichedToplevel);
}
return result;
}
function generateNiriLayoutConfig() {
if (!CompositorService.isNiri || configGenerationPending)
return;
suppressNextToast();
configGenerationPending = true;
configGenerationDebounce.restart();
}
function doGenerateNiriLayoutConfig() {
console.log("NiriService: Generating layout config...");
const cornerRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
const gaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
const configContent = `layout {
gaps ${gaps}
border {
width 2
}
focus-ring {
width 2
}
}
window-rule {
geometry-corner-radius ${cornerRadius}
clip-to-geometry true
tiled-state true
draw-border-with-background false
}`;
const alttabContent = `recent-windows {
highlight {
corner-radius ${cornerRadius}
}
}`;
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
const niriDmsDir = configDir + "/niri/dms";
const configPath = niriDmsDir + "/layout.kdl";
const alttabPath = niriDmsDir + "/alttab.kdl";
writeConfigProcess.configContent = configContent;
writeConfigProcess.configPath = configPath;
writeConfigProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${configPath}" << 'EOF'\n${configContent}\nEOF`];
writeConfigProcess.running = true;
writeAlttabProcess.alttabContent = alttabContent;
writeAlttabProcess.alttabPath = alttabPath;
writeAlttabProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${alttabPath}" << 'EOF'\n${alttabContent}\nEOF`];
writeAlttabProcess.running = true;
configGenerationPending = false;
}
function generateNiriBlurrule() {
console.log("NiriService: Generating wpblur config...");
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
const niriDmsDir = configDir + "/niri/dms";
const blurrulePath = niriDmsDir + "/wpblur.kdl";
const sourceBlurrulePath = Paths.strip(Qt.resolvedUrl("niri-wpblur.kdl"));
writeBlurruleProcess.blurrulePath = blurrulePath;
writeBlurruleProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cp --no-preserve=mode "${sourceBlurrulePath}" "${blurrulePath}"`];
writeBlurruleProcess.running = true;
}
IpcHandler {
function screenshot(): string {
if (!CompositorService.isNiri) {
return "NIRI_NOT_AVAILABLE";
}
if (NiriService.screenshot()) {
return "SCREENSHOT_SUCCESS";
}
return "SCREENSHOT_FAILED";
}
function screenshotScreen(): string {
if (!CompositorService.isNiri) {
return "NIRI_NOT_AVAILABLE";
}
if (NiriService.screenshotScreen()) {
return "SCREENSHOT_SCREEN_SUCCESS";
}
return "SCREENSHOT_SCREEN_FAILED";
}
function screenshotWindow(): string {
if (!CompositorService.isNiri) {
return "NIRI_NOT_AVAILABLE";
}
if (NiriService.screenshotWindow()) {
return "SCREENSHOT_WINDOW_SUCCESS";
}
return "SCREENSHOT_WINDOW_FAILED";
}
target: "niri"
}
}