The audit's second tier: values that were measurements of the author's desktop, shipped to every machine as if they were defaults. Settings greeted every human as Gabriel; it now greets whoever accountsservice says is signed in, and nobody when it says nothing. The weather shipped his home coordinates and confidently reported his forecast anywhere on earth; it now ships unset, fetches nothing until a location is chosen, and the location row says so. The GTK bookmarks carried seven /home/gib paths and his file server into every file dialog; they are now generated per machine from a template and gitignored -- Nautilus edits the instance freely, the way settings.ini already worked one file over. Web search routed through his personal bang redirector; the engine is now the webSearchUrl preference with a DuckDuckGo default, read by both the script command and the suggestions extension, which the launcher-search contract already pins to one another. The GPU vitals path defaulted to his card1 and lost the readout on any machine enumerated differently; a machine with exactly one GPU now adopts it. And the Containers and Snapshots pages hide once a scan proves their backing stack absent, instead of rendering permanently empty on machines that never had podman or snapper. Lesser residue swept in the same pass: the DP-2 hyprpaper block one machine needed, the author's username-typo expansions (moved to his personal seed in user/, where personal content belongs), a capture fallback into /home/gib, and a parity table asserting one machine's hardware as fact. Claude-Session: https://claude.ai/code/session_01Epx9ZC1gwm81K3jm9x9CKh
97 lines
3.6 KiB
QML
97 lines
3.6 KiB
QML
pragma Singleton
|
|
|
|
// The GPUs that can report utilisation.
|
|
//
|
|
// The vitals readout needs one specific sysfs file, and card numbering is
|
|
// neither stable across machines nor meaningful to a person -- this machine has
|
|
// two amdgpu cards, one discrete and one integrated, and picking a number
|
|
// blindly measures whichever the kernel enumerated first.
|
|
//
|
|
// Enumerated on demand rather than polled: hardware does not appear while you
|
|
// are looking at a settings page.
|
|
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import QtQuick
|
|
import qs.config
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
readonly property string helperPath: Quickshell.shellDir + "/scripts/panama-gpus"
|
|
|
|
// [{ card, path, name, busy }]
|
|
property var devices: []
|
|
property bool scanning: false
|
|
property string lastError: ""
|
|
|
|
readonly property string selectedPath: DesktopPreferences.get("gpuBusyPath")
|
|
|
|
readonly property var selected: root.devices.find(device => device.path === root.selectedPath) ?? null
|
|
|
|
// True when a GPU is stored that this machine does not have -- after moving
|
|
// the settings file between machines, say.
|
|
readonly property bool selectionMissing: root.devices.length > 0 && root.selected === null
|
|
|
|
Process {
|
|
id: scan
|
|
command: [root.helperPath]
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
try {
|
|
const parsed = JSON.parse(this.text);
|
|
root.devices = Array.isArray(parsed) ? parsed : [];
|
|
root.lastError = "";
|
|
// The shipped default is one machine's card index, and
|
|
// card numbering means nothing across machines. When the
|
|
// stored path is not present here and there is exactly
|
|
// one real answer, adopt it -- a single-GPU laptop should
|
|
// not lose its vitals readout to a desktop's enumeration.
|
|
if (root.selectionMissing && root.devices.length === 1)
|
|
root.select(root.devices[0].path);
|
|
} catch (error) {
|
|
root.devices = [];
|
|
root.lastError = "The graphics devices could not be read.";
|
|
}
|
|
root.scanning = false;
|
|
}
|
|
}
|
|
onExited: (exitCode, exitStatus) => {
|
|
root.scanning = false;
|
|
if (exitCode !== 0)
|
|
root.lastError = "The graphics devices could not be read.";
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: root.refresh()
|
|
|
|
function refresh(): void {
|
|
if (scan.running)
|
|
return;
|
|
root.scanning = true;
|
|
scan.running = true;
|
|
}
|
|
|
|
// Only a path this machine actually reported is accepted, so a hand-edited
|
|
// settings file cannot point the readout at an arbitrary file.
|
|
function select(path: string): bool {
|
|
if (!root.devices.some(device => device.path === path)) {
|
|
root.lastError = "That graphics device is not present.";
|
|
return false;
|
|
}
|
|
if (!DesktopPreferences.set("gpuBusyPath", path)) {
|
|
root.lastError = "That graphics device could not be saved.";
|
|
return false;
|
|
}
|
|
root.lastError = "";
|
|
return true;
|
|
}
|
|
|
|
// "AMD ... [Radeon RX 7700 XT / 7800 XT] (rev c8)" is what lspci gives; the
|
|
// bracketed marketing name is the part anyone recognizes.
|
|
function shortName(name: string): string {
|
|
const bracketed = String(name).match(/\[([^\]]+)\]\s*(?:\(rev[^)]*\))?\s*$/);
|
|
return bracketed ? bracketed[1] : String(name).replace(/\s*\(rev[^)]*\)\s*$/, "");
|
|
}
|
|
}
|