mirror of
https://github.com/belsabbagh/dotfiles.git
synced 2026-04-11 09:36:46 +00:00
quickshell and hyprland additions
This commit is contained in:
242
.config/quickshell/nucleus-shell/services/Niri.qml
Normal file
242
.config/quickshell/nucleus-shell/services/Niri.qml
Normal file
@@ -0,0 +1,242 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Io
|
||||
pragma Singleton
|
||||
|
||||
Singleton {
|
||||
id: niriItem
|
||||
|
||||
signal stateChanged()
|
||||
|
||||
property string title: ""
|
||||
property bool isFullscreen: false
|
||||
property string layout: "Tiled"
|
||||
property int focusedWorkspaceId: 1
|
||||
|
||||
property var workspaces: []
|
||||
property var workspaceCache: ({})
|
||||
property var windows: [] // tracked windows
|
||||
|
||||
property bool initialized: false
|
||||
property int screenW: 0
|
||||
property int screenH: 0
|
||||
property real screenScale: 1
|
||||
|
||||
function changeWorkspace(id) {
|
||||
sendSocketCommand(niriCommandSocket, {
|
||||
"Action": {
|
||||
"focus_workspace": {
|
||||
"reference": { "Id": id }
|
||||
}
|
||||
}
|
||||
})
|
||||
dispatchProc.command = ["niri", "msg", "action", "focus-workspace", id.toString()]
|
||||
dispatchProc.running = true
|
||||
}
|
||||
|
||||
function changeWorkspaceRelative(delta) {
|
||||
const cmd = delta > 0 ? "focus-workspace-down" : "focus-workspace-up"
|
||||
dispatchProc.command = ["niri", "msg", "action", cmd]
|
||||
dispatchProc.running = true
|
||||
}
|
||||
|
||||
function isWorkspaceOccupied(id) {
|
||||
for (const ws of workspaces)
|
||||
if (ws.id === id) return true
|
||||
return false
|
||||
}
|
||||
|
||||
function focusedWindowForWorkspace(id) {
|
||||
// focused window in workspace
|
||||
for (const win of windows) {
|
||||
if (win.workspaceId === id && win.isFocused) {
|
||||
return { class: win.appId, title: win.title }
|
||||
}
|
||||
}
|
||||
|
||||
// fallback: any window in workspace
|
||||
for (const win of windows) {
|
||||
if (win.workspaceId === id) {
|
||||
return { class: win.appId, title: win.title }
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function sendSocketCommand(sock, command) {
|
||||
if (sock.connected)
|
||||
sock.write(JSON.stringify(command) + "\n")
|
||||
}
|
||||
|
||||
function startEventStream() {
|
||||
sendSocketCommand(niriEventStream, "EventStream")
|
||||
}
|
||||
|
||||
function updateWorkspaces() {
|
||||
sendSocketCommand(niriCommandSocket, "Workspaces")
|
||||
}
|
||||
|
||||
function updateWindows() {
|
||||
sendSocketCommand(niriCommandSocket, "Windows")
|
||||
}
|
||||
|
||||
function updateFocusedWindow() {
|
||||
sendSocketCommand(niriCommandSocket, "FocusedWindow")
|
||||
}
|
||||
|
||||
function recollectWorkspaces(workspacesData) {
|
||||
const list = []
|
||||
workspaceCache = {}
|
||||
|
||||
for (const ws of workspacesData) {
|
||||
const data = {
|
||||
id: ws.idx !== undefined ? ws.idx + 1 : ws.id,
|
||||
internalId: ws.id,
|
||||
idx: ws.idx,
|
||||
name: ws.name || "",
|
||||
output: ws.output || "",
|
||||
isFocused: ws.is_focused === true,
|
||||
isActive: ws.is_active === true
|
||||
}
|
||||
|
||||
list.push(data)
|
||||
workspaceCache[ws.id] = data
|
||||
if (data.isFocused)
|
||||
focusedWorkspaceId = data.id
|
||||
}
|
||||
|
||||
list.sort((a, b) => a.id - b.id)
|
||||
workspaces = list
|
||||
stateChanged()
|
||||
}
|
||||
|
||||
function recollectWindows(windowsData) {
|
||||
const list = []
|
||||
|
||||
for (const win of windowsData) {
|
||||
list.push({
|
||||
appId: win.app_id || "",
|
||||
title: win.title || "",
|
||||
workspaceId: win.workspace_id,
|
||||
isFocused: win.is_focused === true
|
||||
})
|
||||
}
|
||||
|
||||
windows = list
|
||||
stateChanged()
|
||||
}
|
||||
|
||||
function recollectFocusedWindow(win) {
|
||||
if (win && win.title) {
|
||||
title = win.title
|
||||
isFullscreen = win.is_fullscreen || false
|
||||
layout = "Tiled"
|
||||
} else {
|
||||
title = "~"
|
||||
isFullscreen = false
|
||||
layout = "Tiled"
|
||||
}
|
||||
stateChanged()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (Quickshell.env("NIRI_SOCKET")) {
|
||||
niriCommandSocket.connected = true
|
||||
niriEventStream.connected = true
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: niriOutputsProc
|
||||
command: ["niri", "msg", "outputs"]
|
||||
running: true
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = this.text.split("\n")
|
||||
let sizeRe = /Logical size:\s*(\d+)x(\d+)/
|
||||
let scaleRe = /Scale:\s*([\d.]+)/
|
||||
|
||||
for (const line of lines) {
|
||||
let m
|
||||
if ((m = sizeRe.exec(line))) {
|
||||
screenW = parseInt(m[1], 10)
|
||||
screenH = parseInt(m[2], 10)
|
||||
} else if ((m = scaleRe.exec(line))) {
|
||||
screenScale = parseFloat(m[1])
|
||||
}
|
||||
}
|
||||
stateChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Socket {
|
||||
id: niriCommandSocket
|
||||
path: Quickshell.env("NIRI_SOCKET") || ""
|
||||
connected: false
|
||||
|
||||
onConnectedChanged: {
|
||||
if (connected) {
|
||||
updateWorkspaces()
|
||||
updateWindows()
|
||||
updateFocusedWindow()
|
||||
}
|
||||
}
|
||||
|
||||
parser: SplitParser {
|
||||
onRead: (line) => {
|
||||
if (!line.trim()) return
|
||||
try {
|
||||
const data = JSON.parse(line)
|
||||
if (data?.Ok) {
|
||||
const res = data.Ok
|
||||
if (res.Workspaces) recollectWorkspaces(res.Workspaces)
|
||||
else if (res.Windows) recollectWindows(res.Windows)
|
||||
else if (res.FocusedWindow) recollectFocusedWindow(res.FocusedWindow)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Niri socket parse error:", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Socket {
|
||||
id: niriEventStream
|
||||
path: Quickshell.env("NIRI_SOCKET") || ""
|
||||
connected: false
|
||||
|
||||
onConnectedChanged: {
|
||||
if (connected)
|
||||
startEventStream()
|
||||
}
|
||||
|
||||
parser: SplitParser {
|
||||
onRead: (data) => {
|
||||
if (!data.trim()) return
|
||||
try {
|
||||
const event = JSON.parse(data.trim())
|
||||
|
||||
if (event.WorkspacesChanged)
|
||||
recollectWorkspaces(event.WorkspacesChanged.workspaces)
|
||||
else if (event.WorkspaceActivated)
|
||||
updateWorkspaces()
|
||||
else if (
|
||||
event.WindowFocusChanged ||
|
||||
event.WindowOpenedOrChanged ||
|
||||
event.WindowClosed
|
||||
)
|
||||
updateWindows()
|
||||
} catch (e) {
|
||||
console.warn("Niri event stream parse error:", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process { id: dispatchProc }
|
||||
}
|
||||
Reference in New Issue
Block a user