// Isolated behavioral harness for SettingsBackup's live restore handoff. // Every external consumer is replaced before restore output is exercised, so // this file never writes the real compositor, wallpaper, keymap, or shell. import Quickshell import Quickshell.Io import QtQuick import qs.services ShellRoot { id: root property var calls: [] property bool homeInitialized: false property var homeFavorites: [] property bool displayOperationBusy: false property bool displayBlocked: false property bool displayApplyAccepted: true property bool displayConfirmationReady: false property var originalDisplays: ({ "DP-2": { mode: "4500x3000@60", scale: 1.5, transform: 0, x: 0, y: 0, primary: true }, "HDMI-A-1": { mode: "2560x1440@60", scale: 1, transform: 0, x: 3000, y: 0, primary: false } }) property var restoredDisplays: ({ "DP-2": { mode: "4500x3000@60", scale: 1.5, transform: 0, x: -2560, y: 0, primary: false }, "HDMI-A-1": { mode: "2560x1440@60", scale: 1, transform: 0, x: 0, y: 0, primary: true } }) property var displayGeneration: originalDisplays property var liveLayout: [ { name: "DP-2", width: 4500, height: 3000, refreshRate: 60, mode: "4500x3000@60", scale: 1.5, transform: 0, x: 0, y: 0, primary: true }, { name: "HDMI-A-1", width: 2560, height: 1440, refreshRate: 60, mode: "2560x1440@60", scale: 1, transform: 0, x: 3000, y: 0, primary: false } ] function record(name: string): void { const next = root.calls.slice(); next.push(name); root.calls = next; } Component.onCompleted: { SettingsBackup.readHomeState = function() { return { initialized: root.homeInitialized, favorites: root.homeFavorites }; }; SettingsBackup.resetHome = function() { root.record("home.reset"); root.homeInitialized = false; root.homeFavorites = []; }; SettingsBackup.initializeHome = function(ids) { root.record("home.initialize:" + ids.join(",")); root.homeInitialized = true; root.homeFavorites = ids.map(id => ({ id: id, alias: "" })); }; SettingsBackup.aliasHome = function(id, alias) { root.record("home.alias:" + id + "=" + alias); root.homeFavorites = root.homeFavorites.map(favorite => favorite.id === id ? { id: id, alias: alias } : favorite); }; SettingsBackup.reloadDesktop = function() { root.record("desktop.reload"); root.displayGeneration = JSON.parse(JSON.stringify(root.restoredDisplays)); }; SettingsBackup.readDisplays = function() { return root.displayGeneration; }; SettingsBackup.protectDisplays = function(value) { root.record("display.protect:" + JSON.stringify(value)); root.displayGeneration = value; return true; }; SettingsBackup.displayBusy = function() { return root.displayOperationBusy; }; SettingsBackup.readLiveDisplayLayout = function() { return root.liveLayout.map(record => Object.assign({}, record)); }; SettingsBackup.applyDisplayLayout = function(layout) { root.record("display.apply:" + JSON.stringify(layout)); if (!root.displayApplyAccepted) return false; root.liveLayout = layout.map(record => Object.assign({}, record)); root.displayOperationBusy = true; root.displayConfirmationReady = true; return true; }; SettingsBackup.displayCanConfirm = function() { return root.displayConfirmationReady; }; SettingsBackup.confirmDisplayLayout = function() { root.record("display.confirm"); root.displayOperationBusy = false; root.displayConfirmationReady = false; return true; }; SettingsBackup.setDisplayBlocked = function(blocked) { root.record("display.block:" + blocked); root.displayBlocked = blocked; }; SettingsBackup.applyIdle = function() { root.record("idle.apply"); }; SettingsBackup.idleBusy = function() { return false; }; SettingsBackup.applyCompositor = function() { root.record("system.apply"); }; SettingsBackup.reloadKeybinds = function() { root.record("keybinds.reload"); }; SettingsBackup.keybindsReloading = function() { return false; }; SettingsBackup.systemBusy = function() { return false; }; SettingsBackup.applyWallpaperPolicy = function() { root.record("wallpaper.apply-policy"); }; SettingsBackup.wallpaperBusy = function() { return false; }; SettingsBackup.regenerateLock = function() { root.record("lock.regenerate"); }; SettingsBackup.lockBusy = function() { return false; }; SettingsBackup.reloadShell = function() { root.record("shell.reload"); }; } IpcHandler { target: "settings-backup-behavior" function reset(): void { root.calls = []; root.homeInitialized = false; root.homeFavorites = []; root.displayOperationBusy = false; root.displayBlocked = false; root.displayApplyAccepted = true; root.displayConfirmationReady = false; root.displayGeneration = JSON.parse(JSON.stringify(root.originalDisplays)); root.liveLayout = [ { name: "DP-2", width: 4500, height: 3000, refreshRate: 60, mode: "4500x3000@60", scale: 1.5, transform: 0, x: 0, y: 0, primary: true }, { name: "HDMI-A-1", width: 2560, height: 1440, refreshRate: 60, mode: "2560x1440@60", scale: 1, transform: 0, x: 3000, y: 0, primary: false } ]; SettingsBackup.protectedDisplays = root.displayGeneration; SettingsBackup.protectedDisplayLayout = root.liveLayout; } function apply(output: string): bool { return SettingsBackup.handleRestoreOutput(output); } function restoreWhileDisplayBusy(): bool { root.displayOperationBusy = true; SettingsBackup.snapshots = [{ name: "settings-20260818-010203004.json" }]; return SettingsBackup.restore("settings-20260818-010203004.json"); } function applyDisplayFailure(output: string): bool { root.displayApplyAccepted = false; return SettingsBackup.handleRestoreOutput(output); } // The two pure functions behind a display restore, exposed directly. // Everything else here is exercised through handleRestoreOutput, but // the colour half of a layout is invisible from there: a snapshot // whose colour fields were dropped restores geometry perfectly and // reports success, which is exactly why nothing caught it for as long // as it did. function layoutFor(storedJson: string): string { return JSON.stringify( SettingsBackup.layoutFromStoredDisplays(JSON.parse(storedJson))); } // Both layouts in one object rather than two arguments: the IPC client // flattens a top-level JSON array into one argument per element, so a // pair of two-monitor layouts arrives as four arguments. function layoutsMatch(pairJson: string): bool { const pair = JSON.parse(pairJson); return SettingsBackup.layoutsEqual(pair.left, pair.right); } function status(): string { return JSON.stringify({ calls: root.calls, initialized: root.homeInitialized, favorites: root.homeFavorites, displayBlocked: root.displayBlocked, displays: root.displayGeneration, lastError: SettingsBackup.lastError }); } } }