Build the Panama Hyprland desktop

This commit is contained in:
Gabriel Brown
2026-08-17 10:32:55 -04:00
parent 67033f2a31
commit 5248883e4b
190 changed files with 18554 additions and 34 deletions
@@ -0,0 +1,178 @@
pragma Singleton
// Persistent privacy state is separate from transient Signal Glass events.
// The low-frequency PipeWire probe only assigns when state changes, so the bar
// remains completely idle when nothing is capturing.
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
property bool microphoneActive: false
property bool cameraActive: false
property bool screenSharingActive: false
readonly property bool recordingActive: Capture.recording
property string microphoneApp: ""
property string cameraApp: ""
property string screenSharingApp: ""
property bool initialized: false
property bool fixtureMode: false
property bool warned: false
readonly property bool anyActive: root.microphoneActive || root.cameraActive
|| root.screenSharingActive || root.recordingActive
readonly property var activeKinds: {
const kinds = [];
if (root.recordingActive)
kinds.push("Recording");
if (root.screenSharingActive)
kinds.push("Screen sharing");
if (root.cameraActive)
kinds.push("Camera");
if (root.microphoneActive)
kinds.push("Microphone");
return kinds;
}
function applyStates(microphone: bool, camera: bool, screen: bool,
micApp: string, cameraApp: string, screenApp: string,
announce: bool): void {
root.updateKind("microphone", microphone, micApp, announce);
root.updateKind("camera", camera, cameraApp, announce);
root.updateKind("screen", screen, screenApp, announce);
root.initialized = true;
}
function updateKind(kind: string, active: bool, app: string, announce: bool): void {
let previous = false;
let label = "";
if (kind === "microphone") {
previous = root.microphoneActive;
root.microphoneActive = active;
root.microphoneApp = app;
label = "Microphone";
} else if (kind === "camera") {
previous = root.cameraActive;
root.cameraActive = active;
root.cameraApp = app;
label = "Camera";
} else {
previous = root.screenSharingActive;
root.screenSharingActive = active;
root.screenSharingApp = app;
label = "Screen sharing";
}
if (!announce || previous === active)
return;
StatusEvents.publish({
key: "privacy-" + kind,
glyph: kind === "microphone" ? "\u{F036C}" : (kind === "camera" ? "\u{F0100}" : "\u{F0379}"),
title: active ? label + " in use" : label + " released",
detail: active ? (app || "An application is using " + label.toLowerCase()) : "No application is using " + label.toLowerCase(),
tone: active ? "danger" : "ok",
priority: StatusEvents.criticalPriority,
durationMs: active ? 3600 : 2400,
actionId: active ? "open-activity" : ""
});
}
function applyProbe(text: string): void {
if (root.fixtureMode)
return;
let nodes = [];
try {
nodes = JSON.parse(text);
} catch (error) {
if (!root.warned) {
console.warn("PrivacyState: could not parse PipeWire state");
root.warned = true;
}
return;
}
let microphone = false;
let camera = false;
let screen = false;
let micApp = "";
let cameraApp = "";
let screenApp = "";
for (const object of nodes) {
if (object?.type !== "PipeWire:Interface:Node" || object?.info?.state !== "running")
continue;
const props = object.info.props ?? {};
const mediaClass = String(props["media.class"] ?? "");
const nodeName = String(props["node.name"] ?? "").toLowerCase();
const role = String(props["media.role"] ?? "").toLowerCase();
const app = String(props["application.name"] ?? props["node.description"] ?? "");
const looksLikeScreen = role.includes("screen") || nodeName.includes("screencast")
|| nodeName.includes("screen-cast") || nodeName.includes("xdg-desktop-portal");
if (mediaClass === "Stream/Input/Audio") {
microphone = true;
micApp = micApp || app;
} else if (mediaClass === "Stream/Input/Video" && !looksLikeScreen) {
camera = true;
cameraApp = cameraApp || app;
} else if ((mediaClass === "Stream/Input/Video" || mediaClass === "Stream/Output/Video") && looksLikeScreen) {
screen = true;
screenApp = screenApp || app;
}
}
root.applyStates(microphone, camera, screen, micApp, cameraApp, screenApp, root.initialized);
}
function applyFixture(name: string): void {
root.fixtureMode = true;
if (name === "microphone-on")
root.applyStates(true, false, false, "Contract fixture", "", "", true);
else if (name === "camera-on")
root.applyStates(false, true, false, "", "Contract fixture", "", true);
else if (name === "screen-on")
root.applyStates(false, false, true, "", "", "Contract fixture", true);
else if (name === "all-off")
root.applyStates(false, false, false, "", "", "", true);
}
function clearFixture(): void {
root.fixtureMode = false;
// The next real probe is discovery, not a transition away from test
// data; otherwise contract cleanup would flash a bogus release event.
root.initialized = false;
probe.running = false;
probe.running = true;
}
Timer {
interval: 2500
repeat: true
running: true
triggeredOnStart: true
onTriggered: {
if (!root.fixtureMode && !probe.running)
probe.running = true;
}
}
Process {
id: probe
command: ["pw-dump"]
stdout: StdioCollector {
onStreamFinished: root.applyProbe(this.text)
}
onExited: (code, status) => {
if (code !== 0 && !root.warned) {
console.warn("PrivacyState: pw-dump exited with", code);
root.warned = true;
}
}
}
}