Skip to content

Choose a tag to compare

@mishan mishan released this 16 Oct 02:59
· 358 commits to main since this release
f538bc3

Sound, and video with it, on X11 and Wayland; flicker, latency and what a
session used, measured; a session's own clock, identity and network;
timed scripts of input; and checks that a picture comes out the same
twice. Pictures of GTK 4 programs change: see the first note below.

Changes to check when upgrading

  • GTK 4 draws text with manual font rendering (GTK 4.16 and later),
    so text is drawn at its color rather than fainter, by however much
    automatic rendering draws it on that GTK release. Every GTK 4 picture
    with text changes, its widths too, so layout can shift, and a11y contrast measures thin text higher: take committed GTK 4 pictures
    again. A seeded home whose GTK 4 settings.ini sets
    gtk-font-rendering keeps its own.
  • Under bubblewrap, a program can't gain privileges, its supplementary
    groups show as nogroup, and one killed by a signal exits with 128
    plus the signal's number. --no-sandbox runs it as before.
  • compare says where pixels differ, after how many: N pixels differ, in WxH at X,Y, on each picture's line with directories too.
  • A session with a display wants ImageMagick, and one with X11 or
    Xwayland xwininfo and xprop (x11-utils), before it starts, rather
    than at its first wait or picture.
  • Screen.record returns a Recording, not the path: its path, and
    with sound what was heard. It stands in for the path where one is
    opened (os.fspath) and in str(), but .suffix and the rest of a
    Path's are on .path.

Changed

  • Waits look faster. wait stable, match and change take their
    pictures straight from the X server (a few milliseconds, where import
    took tens) or from grim's raw output on Wayland, and compare them in
    memory, with no PNG between; match and change look every 10 ms. A
    change is seen within about a frame, and the time change says is that
    close.
  • Every program a session spawns runs under bubblewrap where it can,
    not only with --gamepads: /dev/snd and /run/user/UID are empty
    for it, so it can't play through the machine's speakers or find the
    desktop's Wayland, PulseAudio, PipeWire or bus sockets. Without
    bubblewrap, the session says so and runs it as before.
  • A window asked for and not there is said with the titles, or app ids,
    there are, and that it's matched in full.
  • The README is a front page, the reference moved to docs/commands.md,
    sealed.md, gamepads.md, python.md and node.md, the a11y commands to
    docs/accessibility.md, the Wayland example to docs/wayland.md, and the
    tests to CONTRIBUTING.md.

Added

  • shotbox play SCRIPT: a script of timed input, played in a
    session on one clock, with no command started for each step, so a demo
    plays out the same way each time it's recorded. A line is a time
    (seconds from the start, or +SECS after the step before) and a
    command for inside a session; one ending in & runs on in the
    background, a recording say, and ${NAME} is the session's variable.
    Each line's options are checked before the first runs, and a failure
    says its file and line. Also Screen.play, which takes the text, or a
    Path to the file, and vars for ${NAME}.
  • wait line RE FILE: wait for a line matching RE to be written to
    a file, and print it, for a script that follows a program's log of
    what happens rather than sleeping by a clock the program may not
    keep to. From the file's end when the wait starts (--from-start for
    the top); a file not there yet is waited for, and one rotated or
    rewritten is followed. Also Screen.wait_line, and MCP's wait_for,
    whose file is in the --allow file's directory.
  • shoot --twice (and term --shoot --twice): the picture taken in
    two fresh sessions, and a failure if they differ, beyond --fuzz and
    --max, saying how many pixels and the box they're in, with
    OUT-diff.png beside OUT. If they're alike, one is kept as OUT.
  • --identity USER@HOST: who and where a session's programs are,
    for run, shoot, term, start, exec, Session(identity=...)
    and MCP's start_session: hostname, uname -n, whoami, id -un, getpwuid(), ls -l's owner, $USER, $LOGNAME and
    $HOSTNAME say USER and HOST, so a picture doesn't show who took it.
    It needs bubblewrap, and not --no-sandbox.
  • --clock TIME and --tz ZONE: every program a session runs sees
    the wall clock start at TIME (ISO 8601, in ZONE or UTC) and run on, a
    program spawned later reading the same clock as the first, so dates in
    pictures don't change between shoots. For run, shoot, term,
    start, exec, Session(clock=..., tz=...) and MCP's
    start_session; by libfaketime, which doesn't reach static binaries or
    Go programs.
  • What a command needs, checked before it starts anything. A tool
    not on PATH, /dev/uinput it can't open for --gamepads, or PyGObject
    with VTE (term) or AT-SPI (--a11y) that this Python can't load is
    said at once in a line, with the package to install, or, from a
    virtualenv that can't see the system's packages, how to make one that
    can (for pipx's, pipx install --force --system-site-packages shotbox); Session and MCP's start_session say the same.
    shotbox doctor goes through every check, a line each, and fails
    only if what every session needs is missing.
  • --usage [FILE]: what a session's programs used, together, and
    apart, its own processes: peak memory and CPU time, from a systemd
    slice of the session's where the user's systemd can make one, else
    read from /proc; as JSON to FILE. Session(usage=True) and MCP's
    start_session take it too.
  • shotbox latency move X1 Y1 X2 Y2 --watch G: how long what's
    drawn takes to follow the pointer, a move at a time, as the median and
    95th percentile; Screen.latency from Python.
  • shotbox flicker: a part of the screen that keeps switching
    between two looks, or flashes another once, found from pictures taken
    as fast as they come, a tile at a time, by its mean color and in time:
    its box, how many changes, how often, and both colors; and how many
    pictures it took and how many changed, on stdout whatever's found, a
    rate too slow said. With --save DIR, a sheet of each one's pictures,
    its box outlined and a tile round it magnified, each labeled with when
    it showed.
    Screen.flicker from Python.
  • --net loopback: a network of the session's own, which every
    program it runs shares and nothing else is on, for run, shoot,
    start, exec and MCP's start_session: a server a test starts is
    reached by the rest of the session, and from nowhere else. wait port
    looks in it.
  • shotbox listen IN.wav: a recording's peak and RMS level, samples
    clipped, and gaps, where a program fell behind and left the sink
    silent; it fails on any, or on silence. shotbox.sound.hear from
    Python, and MCP's sound tool, which records a session's sound
    between start and stop and says the same of it.
  • A GitHub Action, uses: mishan/shotbox@vX.Y.Z: installs shotbox
    at that version and what it runs on an Ubuntu runner, with each
    feature (a11y, wayland, audio, record, term, gamepads)
    asked for by name, and lets bubblewrap make its sandbox.
  • shotbox exec -- COMMAND: the session's seal with no display, for
    a headless browser or a test suite: its own environment, home and bus,
    under bubblewrap, and --audio if it's wanted.
  • --no-sandbox, for a program that needs a setuid helper (sudo, an
    AppImage), which can't run under bubblewrap.
  • What's sealed, in docs/sealed.md: what a session keeps from a program,
    and what it doesn't. And, in the README, instructions to give an agent
    so it runs graphical programs, browsers and sound in a session.
  • wait match REF.png and wait change, with --window, --crop,
    --fuzz and --max: wait until the screen looks like a picture, or
    looks different from when the wait started, and say how long it took.
    Also Screen.wait_match and wait_change, and MCP's wait_for,
    whose picture to match is a file in the --allow file's directory.
  • shotbox record OUT.wav: what plays in a session with --audio,
    48 kHz 16-bit stereo, bit for bit for what plays in that format, on X11
    or Wayland, for --seconds (to the sample) or until stopped.
  • Recipes, in docs/recipes.md: checking a change in a GUI, visual
    regression in CI, an accessibility audit, a game smoke test and an
    agent over MCP.
  • How shotbox compares, in docs/comparison.md, with
    linux-x11-harness, agent-workspace-linux, kwin-mcp, agent-desktop,
    dogtail, openQA and others.
  • shotbox record OUT.mp4 in Wayland sessions, and .webm and .gif:
    the same options, the same length for --seconds, and the same ways
    to stop, the file finished however it stops. wf-recorder takes the
    frames over wlroots' screencopy and ffmpeg encodes them, as on X11.
    sway draws no pointer, so --pointer is refused there. The Action's
    record installs wf-recorder when wayland is on too.
  • shotbox record OUT.mp4 --sound, and .webm: in a session with
    --audio, what plays too, in the same file, AAC in an MP4, Opus in a
    WebM, by one ffmpeg, the sound in step with the picture from its first
    frame, within a frame or so. Both are --seconds long, and it stops
    the ways record does, the file finished. Then it says what listen
    would of the sound: its level, clipping and dropouts, by listen's
    --gap, --clipped and --quiet, failing for them with --strict.
    Also Screen.record(..., sound=True), whose Recording has what
    shotbox.sound.hear heard, and its problems().
  • MCP's record tool: a clip of the screen or a window, up to 60
    seconds and 100 MB, saved as an .mp4 in the session's scratch
    directory or as a new file in the --allow file's directory. It
    answers with the path, the clip's length and a sheet of up to 8
    frames from it with their times, as an image, since a model can look
    at frames but not play a file; with sound, in a session with
    audio, what was heard, as the sound tool says it.
    Screen.record(..., size=N) ends a recording at N bytes.

Fixed

  • term --shoot passes on --net, --usage and --no-sandbox,
    which it took and then left out. It now gives shoot every session
    option as it was given, read back through its own options, so one
    added later reaches shoot too.
  • shoot makes OUT's directory if it isn't there, where it failed
    with a traceback.
  • A recording's keeper killed outright takes ffmpeg (and
    wf-recorder) with it, ffmpeg finishing the file, rather than leaving
    them recording after the session has gone.