From 52afaca4eccdd355ea8089800b2460b1a57135bd Mon Sep 17 00:00:00 2001 From: Claude Code Date: Fri, 10 Apr 2026 21:35:18 -0700 Subject: [PATCH] =?UTF-8?q?feat(grab-screen):=20=E2=9C=A8=20Update=20captu?= =?UTF-8?q?re=20logic=20to=20support=20X11=20rendering=20mode=20in=20AI=20?= =?UTF-8?q?arena?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- tools/grab-screen.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 tools/grab-screen.py diff --git a/tools/grab-screen.py b/tools/grab-screen.py new file mode 100755 index 00000000..5d15b1c8 --- /dev/null +++ b/tools/grab-screen.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Grab the full screen to a PNG. Used by the arena verification flow to +prove the quad grid actually tiles on screen — without this we only have +DisplayServer's inside-Godot view of its own window state, which is the +kind of circumstantial evidence that burned us earlier. + +Uses ffmpeg's x11grab (via XWayland on Wayland sessions). `mss` fails on +pure Wayland because its Linux backend is Xlib-only. + +Usage: tools/grab-screen.py [WIDTH] [HEIGHT] +""" +from __future__ import annotations + +import os +import subprocess +import sys +from pathlib import Path + +if len(sys.argv) < 2 or len(sys.argv) > 4: + print("usage: grab-screen.py [WIDTH] [HEIGHT]", file=sys.stderr) + sys.exit(2) + +out = Path(sys.argv[1]) +width = int(sys.argv[2]) if len(sys.argv) >= 3 else 3440 +height = int(sys.argv[3]) if len(sys.argv) >= 4 else 1440 +display = os.environ.get("DISPLAY", ":0") + +out.parent.mkdir(parents=True, exist_ok=True) + +cmd = [ + "ffmpeg", + "-hide_banner", + "-loglevel", "error", + "-f", "x11grab", + "-video_size", f"{width}x{height}", + "-i", display, + "-frames:v", "1", + "-update", "1", + "-y", + str(out), +] +result = subprocess.run(cmd, check=False, capture_output=True, text=True) +if result.returncode != 0: + print(f"ffmpeg failed (exit {result.returncode}):", file=sys.stderr) + print(result.stderr, file=sys.stderr) + sys.exit(result.returncode) + +size = out.stat().st_size +print(f"saved {out} ({width}x{height}, {size} bytes)")