mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
Re-org niri service & handle reconnects to socket
This commit is contained in:
@@ -12,6 +12,8 @@ import qs.Common
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property string socketPath: Quickshell.env("NIRI_SOCKET")
|
||||||
|
|
||||||
property var workspaces: ({})
|
property var workspaces: ({})
|
||||||
property var allWorkspaces: []
|
property var allWorkspaces: []
|
||||||
property int focusedWorkspaceIndex: 0
|
property int focusedWorkspaceIndex: 0
|
||||||
@@ -20,11 +22,8 @@ Singleton {
|
|||||||
property string currentOutput: ""
|
property string currentOutput: ""
|
||||||
|
|
||||||
property var outputs: ({})
|
property var outputs: ({})
|
||||||
|
|
||||||
property var windows: []
|
property var windows: []
|
||||||
|
|
||||||
signal windowUrgentChanged()
|
|
||||||
|
|
||||||
property bool inOverview: false
|
property bool inOverview: false
|
||||||
|
|
||||||
property int currentKeyboardLayoutIndex: 0
|
property int currentKeyboardLayoutIndex: 0
|
||||||
@@ -37,16 +36,42 @@ Singleton {
|
|||||||
property bool matugenSuppression: false
|
property bool matugenSuppression: false
|
||||||
property bool configGenerationPending: false
|
property bool configGenerationPending: false
|
||||||
|
|
||||||
readonly property string socketPath: Quickshell.env("NIRI_SOCKET")
|
property bool _wantSockets: true
|
||||||
|
property int _reconnectAttempt: 0
|
||||||
|
|
||||||
Component.onCompleted: {
|
readonly property int _reconnectBaseMs: 400
|
||||||
fetchOutputs()
|
readonly property int _reconnectMaxMs: 15000
|
||||||
|
|
||||||
|
signal windowUrgentChanged()
|
||||||
|
|
||||||
|
Component.onCompleted: fetchOutputs()
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: reconnectTimer
|
||||||
|
interval: 0
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
root._wantSockets = false
|
||||||
|
Qt.callLater(() => root._wantSockets = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchOutputs() {
|
Timer {
|
||||||
if (CompositorService.isNiri) {
|
id: suppressToastTimer
|
||||||
outputsProcess.running = true
|
interval: 3000
|
||||||
}
|
onTriggered: root.suppressConfigToast = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: suppressResetTimer
|
||||||
|
interval: 2000
|
||||||
|
onTriggered: root.matugenSuppression = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: configGenerationDebounce
|
||||||
|
interval: 100
|
||||||
|
onTriggered: root.doGenerateNiriLayoutConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
@@ -75,14 +100,70 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: exitCode => {
|
||||||
|
if (exitCode === 0) {
|
||||||
|
configValidationOutput = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: writeConfigProcess
|
||||||
|
property string configContent: ""
|
||||||
|
property string configPath: ""
|
||||||
|
|
||||||
|
onExited: exitCode => {
|
||||||
|
if (exitCode === 0) {
|
||||||
|
console.log("NiriService: Generated layout config at", configPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn("NiriService: Failed to write layout config, exit code:", exitCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: writeBindsProcess
|
||||||
|
property string bindsPath: ""
|
||||||
|
|
||||||
|
onExited: exitCode => {
|
||||||
|
if (exitCode === 0) {
|
||||||
|
console.log("NiriService: Generated binds config at", bindsPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.warn("NiriService: Failed to write binds config, exit code:", exitCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Socket {
|
Socket {
|
||||||
id: eventStreamSocket
|
id: eventStreamSocket
|
||||||
path: root.socketPath
|
path: root.socketPath
|
||||||
connected: CompositorService.isNiri
|
connected: CompositorService.isNiri && root._wantSockets
|
||||||
|
|
||||||
onConnectionStateChanged: {
|
onConnectionStateChanged: {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
|
_reconnectAttempt = 0
|
||||||
write('"EventStream"\n')
|
write('"EventStream"\n')
|
||||||
|
fetchOutputs()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (CompositorService.isNiri) {
|
||||||
|
_scheduleReconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,99 +182,123 @@ Singleton {
|
|||||||
Socket {
|
Socket {
|
||||||
id: requestSocket
|
id: requestSocket
|
||||||
path: root.socketPath
|
path: root.socketPath
|
||||||
connected: CompositorService.isNiri
|
connected: CompositorService.isNiri && root._wantSockets
|
||||||
|
|
||||||
|
onConnectionStateChanged: {
|
||||||
|
if (connected) {
|
||||||
|
_reconnectAttempt = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (CompositorService.isNiri) {
|
||||||
|
_scheduleReconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchOutputs() {
|
||||||
|
if (!CompositorService.isNiri) return
|
||||||
|
outputsProcess.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function _scheduleReconnect() {
|
||||||
|
const pow = Math.min(_reconnectAttempt, 10)
|
||||||
|
const base = Math.min(_reconnectBaseMs * Math.pow(2, pow), _reconnectMaxMs)
|
||||||
|
const jitter = Math.floor(Math.random() * Math.floor(base / 4))
|
||||||
|
reconnectTimer.interval = base + jitter
|
||||||
|
reconnectTimer.restart()
|
||||||
|
_reconnectAttempt++
|
||||||
|
console.warn("NiriService: scheduling reconnect in ~", reconnectTimer.interval, "ms (attempt", _reconnectAttempt, ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortWindowsByLayout(windowList) {
|
function sortWindowsByLayout(windowList) {
|
||||||
return [...windowList].sort((a, b) => {
|
return [...windowList].sort((a, b) => {
|
||||||
const aWorkspace = workspaces[a.workspace_id]
|
const aWorkspace = workspaces[a.workspace_id]
|
||||||
const bWorkspace = workspaces[b.workspace_id]
|
const bWorkspace = workspaces[b.workspace_id]
|
||||||
|
|
||||||
if (aWorkspace && bWorkspace) {
|
if (aWorkspace && bWorkspace) {
|
||||||
const aOutput = aWorkspace.output
|
const aOutput = aWorkspace.output
|
||||||
const bOutput = bWorkspace.output
|
const bOutput = bWorkspace.output
|
||||||
|
|
||||||
const aOutputInfo = outputs[aOutput]
|
const aOutputInfo = outputs[aOutput]
|
||||||
const bOutputInfo = outputs[bOutput]
|
const bOutputInfo = outputs[bOutput]
|
||||||
|
|
||||||
if (aOutputInfo && bOutputInfo && aOutputInfo.logical && bOutputInfo.logical) {
|
if (aOutputInfo && bOutputInfo && aOutputInfo.logical && bOutputInfo.logical) {
|
||||||
if (aOutputInfo.logical.x !== bOutputInfo.logical.x) {
|
if (aOutputInfo.logical.x !== bOutputInfo.logical.x) {
|
||||||
return aOutputInfo.logical.x - bOutputInfo.logical.x
|
return aOutputInfo.logical.x - bOutputInfo.logical.x
|
||||||
}
|
}
|
||||||
if (aOutputInfo.logical.y !== bOutputInfo.logical.y) {
|
if (aOutputInfo.logical.y !== bOutputInfo.logical.y) {
|
||||||
return aOutputInfo.logical.y - bOutputInfo.logical.y
|
return aOutputInfo.logical.y - bOutputInfo.logical.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aOutput === bOutput && aWorkspace.idx !== bWorkspace.idx) {
|
if (aOutput === bOutput && aWorkspace.idx !== bWorkspace.idx) {
|
||||||
return aWorkspace.idx - bWorkspace.idx
|
return aWorkspace.idx - bWorkspace.idx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.workspace_id === b.workspace_id && a.layout && b.layout) {
|
if (a.workspace_id === b.workspace_id && a.layout && b.layout) {
|
||||||
|
if (a.layout.pos_in_scrolling_layout && b.layout.pos_in_scrolling_layout) {
|
||||||
|
const aPos = a.layout.pos_in_scrolling_layout
|
||||||
|
const bPos = b.layout.pos_in_scrolling_layout
|
||||||
|
|
||||||
if (a.layout.pos_in_scrolling_layout && b.layout.pos_in_scrolling_layout) {
|
if (aPos.length > 1 && bPos.length > 1) {
|
||||||
const aPos = a.layout.pos_in_scrolling_layout
|
if (aPos[0] !== bPos[0]) {
|
||||||
const bPos = b.layout.pos_in_scrolling_layout
|
return aPos[0] - bPos[0]
|
||||||
|
}
|
||||||
|
if (aPos[1] !== bPos[1]) {
|
||||||
|
return aPos[1] - bPos[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (aPos.length > 1 && bPos.length > 1) {
|
return a.id - b.id
|
||||||
if (aPos[0] !== bPos[0]) {
|
})
|
||||||
return aPos[0] - bPos[0]
|
|
||||||
}
|
|
||||||
if (aPos[1] !== bPos[1]) {
|
|
||||||
return aPos[1] - bPos[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.id - b.id
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNiriEvent(event) {
|
function handleNiriEvent(event) {
|
||||||
const eventType = Object.keys(event)[0];
|
const eventType = Object.keys(event)[0]
|
||||||
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'WorkspacesChanged':
|
case 'WorkspacesChanged':
|
||||||
handleWorkspacesChanged(event.WorkspacesChanged);
|
handleWorkspacesChanged(event.WorkspacesChanged)
|
||||||
break;
|
break
|
||||||
case 'WorkspaceActivated':
|
case 'WorkspaceActivated':
|
||||||
handleWorkspaceActivated(event.WorkspaceActivated);
|
handleWorkspaceActivated(event.WorkspaceActivated)
|
||||||
break;
|
break
|
||||||
case 'WorkspaceActiveWindowChanged':
|
case 'WorkspaceActiveWindowChanged':
|
||||||
handleWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged);
|
handleWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged)
|
||||||
break;
|
break
|
||||||
case 'WindowsChanged':
|
case 'WindowsChanged':
|
||||||
handleWindowsChanged(event.WindowsChanged);
|
handleWindowsChanged(event.WindowsChanged)
|
||||||
break;
|
break
|
||||||
case 'WindowClosed':
|
case 'WindowClosed':
|
||||||
handleWindowClosed(event.WindowClosed);
|
handleWindowClosed(event.WindowClosed)
|
||||||
break;
|
break
|
||||||
case 'WindowOpenedOrChanged':
|
case 'WindowOpenedOrChanged':
|
||||||
handleWindowOpenedOrChanged(event.WindowOpenedOrChanged);
|
handleWindowOpenedOrChanged(event.WindowOpenedOrChanged)
|
||||||
break;
|
break
|
||||||
case 'WindowLayoutsChanged':
|
case 'WindowLayoutsChanged':
|
||||||
handleWindowLayoutsChanged(event.WindowLayoutsChanged);
|
handleWindowLayoutsChanged(event.WindowLayoutsChanged)
|
||||||
break;
|
break
|
||||||
case 'OutputsChanged':
|
case 'OutputsChanged':
|
||||||
handleOutputsChanged(event.OutputsChanged);
|
handleOutputsChanged(event.OutputsChanged)
|
||||||
break;
|
break
|
||||||
case 'OverviewOpenedOrClosed':
|
case 'OverviewOpenedOrClosed':
|
||||||
handleOverviewChanged(event.OverviewOpenedOrClosed);
|
handleOverviewChanged(event.OverviewOpenedOrClosed)
|
||||||
break;
|
break
|
||||||
case 'ConfigLoaded':
|
case 'ConfigLoaded':
|
||||||
handleConfigLoaded(event.ConfigLoaded);
|
handleConfigLoaded(event.ConfigLoaded)
|
||||||
break;
|
break
|
||||||
case 'KeyboardLayoutsChanged':
|
case 'KeyboardLayoutsChanged':
|
||||||
handleKeyboardLayoutsChanged(event.KeyboardLayoutsChanged);
|
handleKeyboardLayoutsChanged(event.KeyboardLayoutsChanged)
|
||||||
break;
|
break
|
||||||
case 'KeyboardLayoutSwitched':
|
case 'KeyboardLayoutSwitched':
|
||||||
handleKeyboardLayoutSwitched(event.KeyboardLayoutSwitched);
|
handleKeyboardLayoutSwitched(event.KeyboardLayoutSwitched)
|
||||||
break;
|
break
|
||||||
case 'WorkspaceUrgencyChanged':
|
case 'WorkspaceUrgencyChanged':
|
||||||
handleWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged);
|
handleWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,31 +359,26 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleWorkspaceActiveWindowChanged(data) {
|
function handleWorkspaceActiveWindowChanged(data) {
|
||||||
if (data.active_window_id !== null && data.active_window_id !== undefined) {
|
const updatedWindows = []
|
||||||
const updatedWindows = []
|
|
||||||
for (var i = 0; i < windows.length; i++) {
|
for (var i = 0; i < windows.length; i++) {
|
||||||
const w = windows[i]
|
const w = windows[i]
|
||||||
const updatedWindow = {}
|
const updatedWindow = {}
|
||||||
for (let prop in w) {
|
|
||||||
updatedWindow[prop] = w[prop]
|
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)
|
updatedWindow.is_focused = (w.id == data.active_window_id)
|
||||||
updatedWindows.push(updatedWindow)
|
} else {
|
||||||
}
|
|
||||||
windows = updatedWindows
|
|
||||||
} else {
|
|
||||||
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.workspace_id == data.workspace_id ? false : w.is_focused
|
updatedWindow.is_focused = w.workspace_id == data.workspace_id ? false : w.is_focused
|
||||||
updatedWindows.push(updatedWindow)
|
|
||||||
}
|
}
|
||||||
windows = updatedWindows
|
|
||||||
|
updatedWindows.push(updatedWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
windows = updatedWindows
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleWindowsChanged(data) {
|
function handleWindowsChanged(data) {
|
||||||
@@ -290,9 +390,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleWindowOpenedOrChanged(data) {
|
function handleWindowOpenedOrChanged(data) {
|
||||||
if (!data.window) {
|
if (!data.window) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const window = data.window
|
const window = data.window
|
||||||
const existingIndex = windows.findIndex(w => w.id === window.id)
|
const existingIndex = windows.findIndex(w => w.id === window.id)
|
||||||
@@ -301,15 +399,14 @@ Singleton {
|
|||||||
const updatedWindows = [...windows]
|
const updatedWindows = [...windows]
|
||||||
updatedWindows[existingIndex] = window
|
updatedWindows[existingIndex] = window
|
||||||
windows = sortWindowsByLayout(updatedWindows)
|
windows = sortWindowsByLayout(updatedWindows)
|
||||||
} else {
|
return
|
||||||
windows = sortWindowsByLayout([...windows, window])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
windows = sortWindowsByLayout([...windows, window])
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleWindowLayoutsChanged(data) {
|
function handleWindowLayoutsChanged(data) {
|
||||||
if (!data.changes) {
|
if (!data.changes) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedWindows = [...windows]
|
const updatedWindows = [...windows]
|
||||||
let hasChanges = false
|
let hasChanges = false
|
||||||
@@ -319,28 +416,27 @@ Singleton {
|
|||||||
const layoutData = change[1]
|
const layoutData = change[1]
|
||||||
|
|
||||||
const windowIndex = updatedWindows.findIndex(w => w.id === windowId)
|
const windowIndex = updatedWindows.findIndex(w => w.id === windowId)
|
||||||
if (windowIndex >= 0) {
|
if (windowIndex < 0) continue
|
||||||
const updatedWindow = {}
|
|
||||||
for (var prop in updatedWindows[windowIndex]) {
|
const updatedWindow = {}
|
||||||
updatedWindow[prop] = updatedWindows[windowIndex][prop]
|
for (var prop in updatedWindows[windowIndex]) {
|
||||||
}
|
updatedWindow[prop] = updatedWindows[windowIndex][prop]
|
||||||
updatedWindow.layout = layoutData
|
|
||||||
updatedWindows[windowIndex] = updatedWindow
|
|
||||||
hasChanges = true
|
|
||||||
}
|
}
|
||||||
|
updatedWindow.layout = layoutData
|
||||||
|
updatedWindows[windowIndex] = updatedWindow
|
||||||
|
hasChanges = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasChanges) {
|
if (!hasChanges) return
|
||||||
windows = sortWindowsByLayout(updatedWindows)
|
|
||||||
windowsChanged()
|
windows = sortWindowsByLayout(updatedWindows)
|
||||||
}
|
windowsChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOutputsChanged(data) {
|
function handleOutputsChanged(data) {
|
||||||
if (data.outputs) {
|
if (!data.outputs) return
|
||||||
outputs = data.outputs
|
outputs = data.outputs
|
||||||
windows = sortWindowsByLayout(windows)
|
windows = sortWindowsByLayout(windows)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOverviewChanged(data) {
|
function handleOverviewChanged(data) {
|
||||||
@@ -380,9 +476,7 @@ Singleton {
|
|||||||
|
|
||||||
function handleWorkspaceUrgencyChanged(data) {
|
function handleWorkspaceUrgencyChanged(data) {
|
||||||
const ws = root.workspaces[data.id]
|
const ws = root.workspaces[data.id]
|
||||||
if (!ws) {
|
if (!ws) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.is_urgent = data.urgent
|
ws.is_urgent = data.urgent
|
||||||
|
|
||||||
@@ -394,29 +488,6 @@ Singleton {
|
|||||||
windowUrgentChanged()
|
windowUrgentChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: exitCode => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
configValidationOutput = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCurrentOutputWorkspaces() {
|
function updateCurrentOutputWorkspaces() {
|
||||||
if (!currentOutput) {
|
if (!currentOutput) {
|
||||||
currentOutputWorkspaces = allWorkspaces
|
currentOutputWorkspaces = allWorkspaces
|
||||||
@@ -428,59 +499,37 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function send(request) {
|
function send(request) {
|
||||||
if (!CompositorService.isNiri || !requestSocket.connected) {
|
if (!CompositorService.isNiri || !requestSocket.connected) return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
requestSocket.write(JSON.stringify(request) + "\n")
|
requestSocket.write(JSON.stringify(request) + "\n")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function doScreenTransition() {
|
function doScreenTransition() {
|
||||||
return send({
|
return send({"Action": {"DoScreenTransition": {"delay_ms": 0}}})
|
||||||
"Action": {
|
|
||||||
"DoScreenTransition": {
|
|
||||||
"delay_ms": 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchToWorkspace(workspaceIndex) {
|
function switchToWorkspace(workspaceIndex) {
|
||||||
return send({
|
return send({"Action": {"FocusWorkspace": {"reference": {"Index": workspaceIndex}}}})
|
||||||
"Action": {
|
|
||||||
"FocusWorkspace": {
|
|
||||||
"reference": {
|
|
||||||
"Index": workspaceIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusWindow(windowId) {
|
function focusWindow(windowId) {
|
||||||
return send({
|
return send({"Action": {"FocusWindow": {"id": windowId}}})
|
||||||
"Action": {
|
|
||||||
"FocusWindow": {
|
|
||||||
"id": windowId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function powerOffMonitors() {
|
function powerOffMonitors() {
|
||||||
return send({
|
return send({"Action": {"PowerOffMonitors": {}}})
|
||||||
"Action": {
|
|
||||||
"PowerOffMonitors": {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function powerOnMonitors() {
|
function powerOnMonitors() {
|
||||||
return send({
|
return send({"Action": {"PowerOnMonitors": {}}})
|
||||||
"Action": {
|
}
|
||||||
"PowerOnMonitors": {}
|
|
||||||
}
|
function cycleKeyboardLayout() {
|
||||||
})
|
return send({"Action": {"SwitchLayout": {"layout": "Next"}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
function quit() {
|
||||||
|
return send({"Action": {"Quit": {"skip_confirmation": true}}})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentOutputWorkspaceNumbers() {
|
function getCurrentOutputWorkspaceNumbers() {
|
||||||
@@ -498,48 +547,22 @@ Singleton {
|
|||||||
if (currentKeyboardLayoutIndex >= 0 && currentKeyboardLayoutIndex < keyboardLayoutNames.length) {
|
if (currentKeyboardLayoutIndex >= 0 && currentKeyboardLayoutIndex < keyboardLayoutNames.length) {
|
||||||
return keyboardLayoutNames[currentKeyboardLayoutIndex]
|
return keyboardLayoutNames[currentKeyboardLayoutIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function cycleKeyboardLayout() {
|
|
||||||
return send({
|
|
||||||
"Action": {
|
|
||||||
"SwitchLayout": {
|
|
||||||
"layout": "Next"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function quit() {
|
|
||||||
return send({
|
|
||||||
"Action": {
|
|
||||||
"Quit": {
|
|
||||||
"skip_confirmation": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function suppressNextToast() {
|
function suppressNextToast() {
|
||||||
matugenSuppression = true
|
matugenSuppression = true
|
||||||
suppressResetTimer.restart()
|
suppressResetTimer.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
function findNiriWindow(toplevel) {
|
function findNiriWindow(toplevel) {
|
||||||
if (!toplevel.appId) {
|
if (!toplevel.appId) return null
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var j = 0; j < windows.length; j++) {
|
for (var j = 0; j < windows.length; j++) {
|
||||||
const niriWindow = windows[j]
|
const niriWindow = windows[j]
|
||||||
if (niriWindow.app_id === toplevel.appId) {
|
if (niriWindow.app_id === toplevel.appId) {
|
||||||
if (!niriWindow.title || niriWindow.title === toplevel.title) {
|
if (!niriWindow.title || niriWindow.title === toplevel.title) {
|
||||||
return {
|
return {"niriIndex": j, "niriWindow": niriWindow}
|
||||||
"niriIndex": j,
|
|
||||||
"niriWindow": niriWindow
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,40 +587,41 @@ Singleton {
|
|||||||
if (niriWindow.title && toplevel.title === niriWindow.title) {
|
if (niriWindow.title && toplevel.title === niriWindow.title) {
|
||||||
bestMatch = toplevel
|
bestMatch = toplevel
|
||||||
break
|
break
|
||||||
} else if (!niriWindow.title && !bestMatch) {
|
}
|
||||||
|
if (!niriWindow.title && !bestMatch) {
|
||||||
bestMatch = toplevel
|
bestMatch = toplevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestMatch) {
|
if (!bestMatch) continue
|
||||||
usedToplevels.add(bestMatch)
|
|
||||||
|
|
||||||
const enrichedToplevel = {
|
usedToplevels.add(bestMatch)
|
||||||
appId: bestMatch.appId,
|
|
||||||
title: bestMatch.title,
|
const enrichedToplevel = {
|
||||||
activated: bestMatch.activated,
|
appId: bestMatch.appId,
|
||||||
niriWindowId: niriWindow.id,
|
title: bestMatch.title,
|
||||||
niriWorkspaceId: niriWindow.workspace_id,
|
activated: bestMatch.activated,
|
||||||
activate: function() {
|
niriWindowId: niriWindow.id,
|
||||||
return NiriService.focusWindow(niriWindow.id)
|
niriWorkspaceId: niriWindow.workspace_id,
|
||||||
},
|
activate: function() {
|
||||||
close: function() {
|
return NiriService.focusWindow(niriWindow.id)
|
||||||
if (bestMatch.close) {
|
},
|
||||||
return bestMatch.close()
|
close: function() {
|
||||||
}
|
if (bestMatch.close) {
|
||||||
return false
|
return bestMatch.close()
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let prop in bestMatch) {
|
|
||||||
if (!(prop in enrichedToplevel)) {
|
|
||||||
enrichedToplevel[prop] = bestMatch[prop]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enrichedToplevels.push(enrichedToplevel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let prop in bestMatch) {
|
||||||
|
if (!(prop in enrichedToplevel)) {
|
||||||
|
enrichedToplevel[prop] = bestMatch[prop]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enrichedToplevels.push(enrichedToplevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const toplevel of toplevels) {
|
for (const toplevel of toplevels) {
|
||||||
@@ -611,6 +635,7 @@ Singleton {
|
|||||||
|
|
||||||
function filterCurrentWorkspace(toplevels, screenName) {
|
function filterCurrentWorkspace(toplevels, screenName) {
|
||||||
let currentWorkspaceId = null
|
let currentWorkspaceId = null
|
||||||
|
|
||||||
for (var i = 0; i < allWorkspaces.length; i++) {
|
for (var i = 0; i < allWorkspaces.length; i++) {
|
||||||
const ws = allWorkspaces[i]
|
const ws = allWorkspaces[i]
|
||||||
if (ws.output === screenName && ws.is_active) {
|
if (ws.output === screenName && ws.is_active) {
|
||||||
@@ -619,9 +644,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentWorkspaceId === null) {
|
if (currentWorkspaceId === null) return toplevels
|
||||||
return toplevels
|
|
||||||
}
|
|
||||||
|
|
||||||
const workspaceWindows = windows.filter(niriWindow => niriWindow.workspace_id === currentWorkspaceId)
|
const workspaceWindows = windows.filter(niriWindow => niriWindow.workspace_id === currentWorkspaceId)
|
||||||
const usedToplevels = new Set()
|
const usedToplevels = new Set()
|
||||||
@@ -637,73 +660,50 @@ Singleton {
|
|||||||
if (niriWindow.title && toplevel.title === niriWindow.title) {
|
if (niriWindow.title && toplevel.title === niriWindow.title) {
|
||||||
bestMatch = toplevel
|
bestMatch = toplevel
|
||||||
break
|
break
|
||||||
} else if (!niriWindow.title && !bestMatch) {
|
}
|
||||||
|
if (!niriWindow.title && !bestMatch) {
|
||||||
bestMatch = toplevel
|
bestMatch = toplevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bestMatch) {
|
if (!bestMatch) continue
|
||||||
usedToplevels.add(bestMatch)
|
|
||||||
|
|
||||||
const enrichedToplevel = {
|
usedToplevels.add(bestMatch)
|
||||||
appId: bestMatch.appId,
|
|
||||||
title: bestMatch.title,
|
const enrichedToplevel = {
|
||||||
activated: bestMatch.activated,
|
appId: bestMatch.appId,
|
||||||
niriWindowId: niriWindow.id,
|
title: bestMatch.title,
|
||||||
niriWorkspaceId: niriWindow.workspace_id,
|
activated: bestMatch.activated,
|
||||||
activate: function() {
|
niriWindowId: niriWindow.id,
|
||||||
return NiriService.focusWindow(niriWindow.id)
|
niriWorkspaceId: niriWindow.workspace_id,
|
||||||
},
|
activate: function() {
|
||||||
close: function() {
|
return NiriService.focusWindow(niriWindow.id)
|
||||||
if (bestMatch.close) {
|
},
|
||||||
return bestMatch.close()
|
close: function() {
|
||||||
}
|
if (bestMatch.close) {
|
||||||
return false
|
return bestMatch.close()
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let prop in bestMatch) {
|
|
||||||
if (!(prop in enrichedToplevel)) {
|
|
||||||
enrichedToplevel[prop] = bestMatch[prop]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push(enrichedToplevel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let prop in bestMatch) {
|
||||||
|
if (!(prop in enrichedToplevel)) {
|
||||||
|
enrichedToplevel[prop] = bestMatch[prop]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(enrichedToplevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateNiriLayoutConfig() {
|
function generateNiriLayoutConfig() {
|
||||||
const niriSocket = Quickshell.env("NIRI_SOCKET")
|
const niriSocket = Quickshell.env("NIRI_SOCKET")
|
||||||
if (!niriSocket || niriSocket.length === 0) {
|
if (!niriSocket || niriSocket.length === 0) return
|
||||||
return
|
if (configGenerationPending) return
|
||||||
}
|
|
||||||
|
|
||||||
if (configGenerationPending) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
configGenerationPending = true
|
configGenerationPending = true
|
||||||
configGenerationDebounce.restart()
|
configGenerationDebounce.restart()
|
||||||
@@ -757,32 +757,4 @@ window-rule {
|
|||||||
writeBindsProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cp "${sourceBindsPath}" "${bindsPath}"`]
|
writeBindsProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cp "${sourceBindsPath}" "${bindsPath}"`]
|
||||||
writeBindsProcess.running = true
|
writeBindsProcess.running = true
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
|
||||||
id: writeConfigProcess
|
|
||||||
property string configContent: ""
|
|
||||||
property string configPath: ""
|
|
||||||
|
|
||||||
onExited: exitCode => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
console.log("NiriService: Generated layout config at", configPath)
|
|
||||||
} else {
|
|
||||||
console.warn("NiriService: Failed to write layout config, exit code:", exitCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: writeBindsProcess
|
|
||||||
property string bindsPath: ""
|
|
||||||
|
|
||||||
onExited: exitCode => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
console.log("NiriService: Generated binds config at", bindsPath)
|
|
||||||
} else {
|
|
||||||
console.warn("NiriService: Failed to write binds config, exit code:", exitCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user