The menu, emoji picker, clipboard, OSD, reminder flow and Wi-Fi QR code mapped a fresh surface on every open. Qt drew its first frames before Hyprland sent the surface's fractional scale: the pixel ratio stepped 2, 1, then 1.6, so on a 1.6 display each overlay showed blurry for ~350ms before going sharp. A new OverlayWindow keeps the surface. Hidden, it parks as a 1x1, input-less layer below windows, off the overlay layer so it never blocks direct scanout. Showing only resizes and raises it, so the scale is already settled. Content stays hidden until the surface has grown, so no 1x1 frame is stretched across the screen, and the window follows the focused monitor each time it is shown. The image picker's own fullscreen parking moves onto it too. Each parked overlay keeps a Qt window alive: at rest the shell holds ~40 MiB more RSS and ~20 MiB more GPU memory. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
138 lines
3.8 KiB
Bash
138 lines
3.8 KiB
Bash
#!/bin/bash
|
|
|
|
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
|
|
echo "source test/acceptance.d/base-test.sh from an acceptance test; do not run it directly" >&2
|
|
exit 1
|
|
fi
|
|
|
|
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
|
|
ARTIFACTS="${OMARCHY_ACCEPTANCE_DIR:-/tmp/omarchy-acceptance}"
|
|
|
|
mkdir -p "$ARTIFACTS"
|
|
|
|
pass() {
|
|
printf 'ok - %s\n' "$1"
|
|
}
|
|
|
|
fail() {
|
|
local description="$1"
|
|
local detail="${2:-}"
|
|
local step=${description,,}
|
|
|
|
step=${step// /-}
|
|
step=${step//[^a-z0-9-]/}
|
|
|
|
[[ -n $detail ]] && printf '%s\n' "$detail" >&2
|
|
screenshot "failure-$step"
|
|
printf 'not ok - %s\n' "$description" >&2
|
|
exit 1
|
|
}
|
|
|
|
screenshot() {
|
|
timeout 10 grim "$ARTIFACTS/$1.png" 2>/dev/null || true
|
|
}
|
|
|
|
screen_contains() {
|
|
local text="$1"
|
|
local snapshot="/tmp/omarchy-acceptance-ocr-$$.png"
|
|
|
|
# Capture at 2x scale: tesseract routinely drops small caption text at
|
|
# native resolution (the weather panel's detail labels, for one).
|
|
if ! timeout 10 grim -s 2 "$snapshot" 2>/dev/null; then
|
|
rm -f "$snapshot"
|
|
return 1
|
|
fi
|
|
tesseract "$snapshot" stdout --psm 11 2>/dev/null | grep -Fi -- "$text" >/dev/null
|
|
local status=$?
|
|
rm -f "$snapshot"
|
|
return $status
|
|
}
|
|
|
|
# Poll a command until it succeeds; screenshot and fail on timeout.
|
|
wait_until() {
|
|
local description="$1" timeout="$2"
|
|
shift 2
|
|
|
|
local deadline=$((SECONDS + timeout))
|
|
|
|
until "$@" >/dev/null 2>&1; do
|
|
if ((SECONDS >= deadline)); then
|
|
fail "$description" "timed out after ${timeout}s waiting for: $*"
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
pass "$description"
|
|
}
|
|
|
|
window_present() {
|
|
hyprctl -j clients | jq -e --arg class "$1" '[.[] | select(.class | test($class))] | length > 0'
|
|
}
|
|
|
|
window_absent() {
|
|
! window_present "$1"
|
|
}
|
|
|
|
layer_present() {
|
|
hyprctl -j layers | jq -e --arg ns "$1" '[.. | objects | select(.namespace? == $ns)] | length > 0'
|
|
}
|
|
|
|
layer_absent() {
|
|
! layer_present "$1"
|
|
}
|
|
|
|
# Overlays built on OverlayWindow (the menu, image selector, emoji picker,
|
|
# clipboard and the like) stay mapped between opens, parked 1x1 on the bottom
|
|
# layer, so they keep their fractional scale. Showing means on the overlay.
|
|
layer_on_overlay() {
|
|
hyprctl -j layers | jq -e --arg ns "$1" '[.[].levels["3"][]? | select(.namespace == $ns)] | length > 0'
|
|
}
|
|
|
|
layer_off_overlay() {
|
|
! layer_on_overlay "$1"
|
|
}
|
|
|
|
# A layer can be mapped but parked off the monitor: the bar hides that way so
|
|
# revealing it does not have to rebuild the surface. Assert on geometry when
|
|
# what matters is that the user can actually see it. Layer boxes are local to
|
|
# their monitor and in logical coordinates, so compare them with local bounds
|
|
# derived from the monitor's scaled pixel size.
|
|
layer_on_screen() {
|
|
local monitors
|
|
monitors=$(hyprctl -j monitors) || return 1
|
|
|
|
hyprctl -j layers | jq -e --arg ns "$1" --argjson monitors "$monitors" '
|
|
to_entries[]
|
|
| .key as $name
|
|
| .value as $levels
|
|
| ($monitors[] | select(.name == $name)) as $m
|
|
| (if ($m.transform // 0) % 2 == 1 then $m.height else $m.width end) / $m.scale | round as $width
|
|
| (if ($m.transform // 0) % 2 == 1 then $m.width else $m.height end) / $m.scale | round as $height
|
|
| [$levels | .. | objects | select(.namespace? == $ns)][]
|
|
| select(
|
|
.x + .w > 0 and .x < $width and
|
|
.y + .h > 0 and .y < $height
|
|
)
|
|
' >/dev/null
|
|
}
|
|
|
|
layer_off_screen() {
|
|
! layer_on_screen "$1"
|
|
}
|
|
|
|
# Close every window matching a class regex, by address so multi-window apps
|
|
# are fully closed. Tries the quattro Lua dispatcher first, then classic.
|
|
close_windows() {
|
|
local class="$1"
|
|
local addr
|
|
|
|
while read -r addr; do
|
|
hyprctl dispatch "hl.dsp.window.close({ window = \"address:$addr\" })" >/dev/null 2>&1 ||
|
|
hyprctl dispatch closewindow "address:$addr" >/dev/null 2>&1 || true
|
|
done < <(hyprctl -j clients | jq -r --arg class "$class" '.[] | select(.class | test($class)) | .address')
|
|
}
|
|
|
|
launch_app() {
|
|
setsid -f bash -c "$1" >/dev/null 2>&1
|
|
}
|