// Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. import Quickshell import Quickshell.Io import QtQuick import "../.." // Every shell in the repo, running or not, with a restart through reload.sh, // wrapped by restart.sh, which notifies once the shells are back. Started // detached, so it outlives the qs that started it: // restarting the desktop shell from its own settings window works, the // window just closes until the new process is up. Module { id: sm name: "shells" label: "Shells" // nf-fa-refresh, present in Inconsolata Nerd Font. icon: "" settings: true group: "Desktop" keywords: ["restart", "reload", "quickshell", "qs", "process", "start"] readonly property string root: Quickshell.shellDir + "/.." // [{name, running}], one per directory holding a shell.qml. property var shells: [] // An empty name restarts every shell the page lists. function restart(name) { Quickshell.execDetached(["sh", Quickshell.shellDir + "/modules/shells/restart.sh", sm.root] .concat(name ? [name] : sm.shells.map(s => s.name))); repoll.restart(); } function refresh() { proc.rows = []; proc.running = false; proc.running = true; } // reload.sh waits a second before it returns; read the list after that. property Timer repoll: Timer { interval: 2000 onTriggered: sm.refresh() } // A shell is running when qs lists an instance on its config path. property Process proc: Process { property var rows: [] command: ["sh", "-c", 'cd "$1" || exit 1; up=$(qs list --all 2>/dev/null); ' + 'for f in */shell.qml; do case "$up" in ' + '*"Config path: $PWD/$f"*) s=running ;; *) s=stopped ;; esac; ' + 'printf "%s\\t%s\\n" "${f%/shell.qml}" "$s"; done', "sh", sm.root] stdout: SplitParser { onRead: line => { const f = line.split("\t"); if (f.length === 2) proc.rows.push({ name: f[0], running: f[1] === "running" }); } } onExited: { sm.shells = proc.rows; sm.searchItems = proc.rows.map(s => ({ text: s.name, key: "shell:" + s.name })); } } page: Component { Page { title: "Shells" ShellsPage { width: parent.width; shells: sm } } } }