pragma Singleton // The system power profile: power-saver, balanced, or performance. // // The same daemon GNOME's Power panel drives. On Fedora 44 the implementation // is tuned-ppd rather than power-profiles-daemon, but it serves the same // net.hadess.PowerProfiles interface -- so this talks to the interface, not to // either binary. powerprofilesctl is not installed here at all. // // Not a stored preference. The profile lives in the daemon, survives Panama // restarts, and can be changed by anything else on the system; keeping a copy // in settings.json would mean restoring a value the daemon had moved past. // Same reasoning as monitor brightness. // // Read on demand and after each change. The daemon does emit PropertiesChanged, // but subscribing to it would mean holding a bus connection open for a value // that changes only when someone chooses it. import Quickshell import Quickshell.Io import QtQuick Singleton { id: root readonly property string helperPath: Quickshell.shellDir + "/scripts/panama-power-profile" property var profiles: [] property string active: "" property bool scanned: false property bool busy: false property string lastError: "" // Non-empty when the machine cannot actually deliver the profile it is set // to -- thermal throttling, or a laptop running on battery. Worth showing, // because otherwise "performance" is a claim the hardware is not honoring. property string degraded: "" readonly property bool available: root.profiles.length > 0 // Presentation lives here rather than in the page so the Control Center and // Settings cannot disagree about what a profile is called. function label(profile: string): string { switch (profile) { case "power-saver": return "Power Saver"; case "balanced": return "Balanced"; case "performance": return "Performance"; default: return profile; } } function detail(profile: string): string { switch (profile) { case "power-saver": return "Reduces performance to save energy and run quieter"; case "balanced": return "Standard behavior, scaling up only when needed"; case "performance": return "Holds higher clocks, using more power and making more noise"; default: return ""; } } function refresh(): void { if (!query.running) query.running = true; } function set(profile: string): void { if (root.busy || profile === root.active) return; root.busy = true; apply.command = [root.helperPath, "set", profile]; apply.running = true; } Process { id: query command: [root.helperPath, "list"] stdout: StdioCollector { onStreamFinished: { try { const parsed = JSON.parse(this.text); root.profiles = Array.isArray(parsed.profiles) ? parsed.profiles : []; root.active = String(parsed.active ?? ""); root.degraded = String(parsed.degraded ?? ""); root.lastError = String(parsed.error ?? ""); } catch (error) { root.profiles = []; root.lastError = "Could not read the power profile helper's output."; console.warn("PowerProfiles: could not parse helper output:", error); } root.scanned = true; } } } Process { id: apply stderr: StdioCollector { onStreamFinished: if (this.text.trim() !== "") root.lastError = this.text.trim() } // Re-read rather than assuming: the daemon may refuse, or may land on a // different profile than the one asked for. onExited: { root.busy = false; root.refresh(); } } }