Fix: Never pair exclusionMode with exclusiveZone
Quickshell's exclusiveZone setter flips exclusionMode back to Normal as a side effect, so a window declaring both is at the mercy of which property the QML engine applies last. The 2026-09-14 Qt update changed that order and every full-screen overlay slid under the bar. Each overlay now declares ExclusionMode.Ignore alone. The capture picker was the visible failure: it started under the bar, the full-output freeze frame was squeezed into a shorter box, and every selection landed one bar-height off in the real capture. tests/quickshell/exclusion-idiom-contract fails any file that pairs them.
This commit is contained in:
@@ -82,6 +82,7 @@ live-desktop tests/quickshell/dock-position-contract
|
||||
# Reads Hyprland option descriptions from the live compositor to verify enum
|
||||
# mappings.
|
||||
live-compositor tests/quickshell/enum-hypr-map-contract
|
||||
hermetic tests/quickshell/exclusion-idiom-contract
|
||||
hermetic tests/quickshell/fingerprint-contract
|
||||
# Reads the host firewall state through the production firewall helper.
|
||||
live-host tests/quickshell/firewall-contract
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# A window that wants the whole output must ask for ExclusionMode.Ignore and
|
||||
# nothing else. Quickshell's exclusiveZone setter forces exclusionMode back to
|
||||
# Normal as a side effect, so a window declaring both
|
||||
#
|
||||
# exclusiveZone: 0
|
||||
# exclusionMode: ExclusionMode.Ignore
|
||||
#
|
||||
# ends up with whichever property the QML engine applied last, and that order
|
||||
# is not ours to control: the 2026-09-14 Qt/Quickshell update flipped it, every
|
||||
# full-screen overlay slid down under the bar, and the screenshot picker's
|
||||
# frozen frame stopped lining up with the screen it was a picture of. This pins
|
||||
# the order-independent idiom: Ignore alone, never paired with exclusiveZone.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
qs="$repo_dir/config/dot/quickshell"
|
||||
|
||||
fail() { printf 'exclusion idiom contract: %s\n' "$1" >&2; exit 1; }
|
||||
[[ -d "$qs/modules" ]] || fail "missing $qs/modules"
|
||||
|
||||
python3 - "$qs" <<'PY'
|
||||
import re, sys, pathlib
|
||||
qs = pathlib.Path(sys.argv[1])
|
||||
problems = []
|
||||
ignore = re.compile(r'\bexclusionMode\s*:\s*ExclusionMode\.Ignore\b')
|
||||
zone = re.compile(r'^\s*(?:WlrLayershell\.)?exclusiveZone\s*:', re.M)
|
||||
checked = 0
|
||||
for path in sorted(qs.rglob('*.qml')):
|
||||
text = path.read_text()
|
||||
if not ignore.search(text):
|
||||
continue
|
||||
checked += 1
|
||||
for m in zone.finditer(text):
|
||||
line = text.count('\n', 0, m.start()) + 1
|
||||
problems.append(f"{path.relative_to(qs)}:{line}: exclusiveZone set in a file that uses "
|
||||
"ExclusionMode.Ignore; drop it, Ignore already means -1")
|
||||
if checked == 0:
|
||||
problems.append("no window uses ExclusionMode.Ignore; the overlays this pins are gone")
|
||||
if problems:
|
||||
print('\n'.join(problems), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
PY
|
||||
Reference in New Issue
Block a user