import Quickshell import Quickshell.Io import QtQuick import "services/DisplayLayout.js" as DisplayLayout ShellRoot { readonly property var fixture: [ { name: "DP-2", width: 4500, height: 3000, scale: 1.5, transform: 0, x: 140, y: 80, primary: true }, { name: "HDMI-A-1", width: 2560, height: 1440, scale: 1, transform: 1, x: 3140, y: 80, primary: false } ] // The same two displays, with the second mirroring the first. A mirrored // display has no position of its own: the compositor puts it on top of its // target, so its stored coordinates are stale the moment mirroring is on. readonly property var mirrored: [ { name: "DP-2", width: 4500, height: 3000, scale: 1.5, transform: 0, x: 140, y: 80, primary: true, mirrorOf: "" }, { name: "HDMI-A-1", width: 2560, height: 1440, scale: 1, transform: 0, x: 3140, y: 80, primary: false, mirrorOf: "DP-2" } ] IpcHandler { target: "display-layout-test" function mirror(): string { const normalized = DisplayLayout.normalize(mirrored); const canvas = DisplayLayout.canvasRects(normalized, 800, 500, 20); return JSON.stringify({ valid: DisplayLayout.validate(mirrored), normalized: normalized.map(record => ({ name: record.name, x: record.x, y: record.y, primary: record.primary, mirrorOf: record.mirrorOf ?? "" })), bounds: canvas.bounds, rects: canvas.rects }); } function invalidMirrors(): string { const base = mirrored.map(record => Object.assign({}, record)); const cases = []; const add = layout => cases.push(DisplayLayout.validate(layout)); // Mirroring itself. add([base[0], Object.assign({}, base[1], { mirrorOf: "HDMI-A-1" })]); // Mirroring an output that is not in the layout. add([base[0], Object.assign({}, base[1], { mirrorOf: "NOPE-1" })]); // The primary may not mirror: the desktop is anchored on it. add([Object.assign({}, base[0], { mirrorOf: "HDMI-A-1" }), base[1]]); // No chains. Hyprland resolves a mirror to one target, and a chain // is a question nobody can answer from the canvas. add([ base[0], Object.assign({}, base[1], { mirrorOf: "DP-3" }), { name: "DP-3", width: 1920, height: 1080, scale: 1, transform: 0, x: 6000, y: 0, primary: false, mirrorOf: "DP-2" } ]); // Not a name at all. add([base[0], Object.assign({}, base[1], { mirrorOf: 5 })]); return JSON.stringify(cases); } function status(): string { const normalized = DisplayLayout.normalize(fixture); const canvas = DisplayLayout.canvasRects(normalized, 800, 500, 20); const near = normalized.map(record => Object.assign({}, record)); near[1].x = 3016; const far = normalized.map(record => Object.assign({}, record)); far[1].x = 3017; return JSON.stringify({ valid: DisplayLayout.validate(fixture), sizes: fixture.map(DisplayLayout.logicalSize), normalized: normalized.map(record => ({ name: record.name, x: record.x, y: record.y, primary: record.primary })), bounds: canvas.bounds, canvasScale: canvas.scale, canvasRects: canvas.rects, near: DisplayLayout.snap(near, "HDMI-A-1", 16).find(record => record.name === "HDMI-A-1").x, far: DisplayLayout.snap(far, "HDMI-A-1", 16).find(record => record.name === "HDMI-A-1").x }); } function invalid(): string { const base = fixture.map(record => Object.assign({}, record)); const cases = []; const add = layout => cases.push(DisplayLayout.validate(layout)); add([base[0], Object.assign({}, base[1], { name: "DP-2" })]); add(base.map(record => Object.assign({}, record, { primary: false }))); add(base.map(record => Object.assign({}, record, { primary: true }))); add([Object.assign({}, base[0], { x: 0.5 }), base[1]]); add([Object.assign({}, base[0], { scale: 0 }), base[1]]); add([Object.assign({}, base[0], { transform: 4 }), base[1]]); add([Object.assign({}, base[0], { width: Infinity }), base[1]]); add([Object.assign({}, base[0], { width: 0 }), base[1]]); return JSON.stringify(cases); } } }