import Quickshell import Quickshell.Io import QtQuick import qs.config import qs.services ShellRoot { id: root property var resetCalls: [] property var appliedBatches: [] property bool displayBlocked: false function recordReset(name: string): void { const next = root.resetCalls.slice(); next.push(name); root.resetCalls = next; } Component.onCompleted: { // Keep compositor verification entirely inside the isolated harness. // Production applyOptions is covered separately by the Hyprland write // contract; this seam proves commit/reset routing without changing the // desktop that is running the test. if (Quickshell.env("PANAMA_SETTINGS_TEST_ISOLATE_COMPOSITOR") === "1") { SystemSettings.compositorApplyOverride = function(requested) { const batches = root.appliedBatches.slice(); batches.push(requested); root.appliedBatches = batches; for (const key in requested) DesktopPreferences.set(key, requested[key]); return true; }; } SystemSettings.displayBusy = function() { return false; }; SystemSettings.readDisplays = function() { return DesktopPreferences.get("displays"); }; SystemSettings.protectDisplays = function(value) { root.recordReset("display.protect"); return DesktopPreferences.set("displays", value); }; SystemSettings.setDisplayBlocked = function(blocked) { root.recordReset("display.block:" + blocked); root.displayBlocked = blocked; }; // Recorded so the contract can prove the snapshot happens BEFORE the // stores are cleared, not merely that it happens. SystemSettings.takeSafetySnapshot = function() { root.recordReset("snapshot"); return true; }; SystemSettings.reloadKeybinds = function() { root.recordReset("keybinds.reload"); }; SystemSettings.keybindsReloading = function() { return false; }; SystemSettings.applyWallpaper = function(path) { root.recordReset("wallpaper.set:" + path); }; SystemSettings.regenerateLock = function() { root.recordReset("lock.regenerate"); }; SystemSettings.lockBusy = function() { return false; }; } IpcHandler { target: "settings-system-test" function refresh(): void { SystemSettings.refresh(); } // One batch, matching how the shell applies persisted policy. Three // separate setters would be refused as overlapping writes, since each // one is only complete after its value has been read back. function apply(autoHdr: bool, vrr: int, directScanout: int): void { SystemSettings.applyOptions({ autoHdr: autoHdr, vrrPolicy: vrr, directScanoutPolicy: directScanout }); } // Applies an arbitrary batch of schema keys, so the contract can cover // every getoption answer shape -- int, bool, float, str, and the css // box that gaps read back as -- not just the display policies. function applyJson(payload: string): bool { return SystemSettings.applyOptions(JSON.parse(payload)); } // The routing entry point the settings rows use: a compositor-backed // key must be applied and verified before it is stored, a local one is // written directly. The contract checks both halves. function commit(key: string, payload: string): bool { return SystemSettings.commitPreference(key, JSON.parse(payload)); } function stored(key: string): string { return JSON.stringify(DesktopPreferences.get(key)); } function seedHome(): void { HomePreferences.favorites = [{ id: "light.contract_probe", alias: "Probe" }]; HomePreferences.initialized = true; } function homeState(): string { return JSON.stringify({ count: HomePreferences.favorites.length, initialized: HomePreferences.initialized }); } function restoreDefaults(): bool { root.resetCalls = []; return SystemSettings.restoreDefaults(); } function resetState(): string { return JSON.stringify({ calls: root.resetCalls, displayBlocked: root.displayBlocked, appliedBatches: root.appliedBatches }); } function applyState(): string { return JSON.stringify(root.appliedBatches); } function panelAllowed(panel: string): bool { return SystemSettings.isGnomePanelAllowed(panel); } function status(): string { return JSON.stringify({ monitorName: SystemSettings.monitorName, monitorDescription: SystemSettings.monitorDescription, width: SystemSettings.monitorWidth, height: SystemSettings.monitorHeight, scale: SystemSettings.monitorScale, format: SystemSettings.monitorFormat, busy: SystemSettings.busy, lastError: SystemSettings.lastError }); } } }