evilfreelancer/screenshotting-gui/screenshotting-gui

Claude Code and agent skill by evilfreelancer/screenshotting-gui

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when you must produce clean, repeatable screenshots of a running GUI desktop application (an IDE like Cursor or VS Code, a native app, a browser window) for documentation, a course, a README, or a bug report - captured on an isolated Xvfb virtual display so synthetic input and captures never touch the operator's real desktop, scripted with python-Xlib (XTEST), and annotated (rounded highlight box, crop, arrow) with ImageMagick. Triggers include "take / redo a screenshot of an app's UI", "screenshot a menu or dialog in a specific state", "automate app screenshots for docs", "highlight a region on a screenshot". Not for grabbing the operator's live :0 desktop, and not for pure web-page shots (use a headless browser for those).

Install

npx vskill@latest install evilfreelancer/screenshotting-gui/screenshotting-gui

SKILL.md

Screenshotting a GUI app

Produce documentation-grade screenshots of a running GUI application without a window manager and without touching the operator's screen. The app runs on a throwaway Xvfb display; you drive it to the exact state you want with synthetic X input, capture the frame, and annotate it.

Everything the model already knows (what X11, ImageMagick or a clipboard are) is omitted on purpose - this file is only the parts that are easy to get wrong.

The one rule that keeps it safe

Run on display `:99` (Xvfb), never `:0`. :0 is the operator's live desktop - clicking, typing and screenshotting it is a privacy and correctness disaster.

The subtle trap: xctl.py / xpaste.py default their own target to :99, but any GUI app you launch reads `$DISPLAY`. If $DISPLAY is still :0, the app opens on the real screen while your captures read :99 - winlist comes back empty and you capture a black frame. Always `export DISPLAY=:99` for the app launch, not just XCTL_DISPLAY.

Toolchain

Deterministic pieces live in scripts/ (run any with --help):

ScriptRole
scripts/xctl.pyDrive the app: move click key type scroll drag place shot winlist via python-Xlib XTEST.
scripts/xpaste.pyServe UTF-8 on the CLIPBOARD so key ctrl+v pastes unicode / non-Latin text.
scripts/annotate.pyImageMagick post-processing: box (rounded highlight), crop, vstack, arrow.

Requirements: Xvfb, python3 + python-Xlib, ImageMagick (import, convert). Capture default is ImageMagick import (no extra dependency, no max-size ceiling). Fallback if ImageMagick is unavailable: python-Xlib root.get_image + Pillow (~2x faster, but needs Pillow and must tile a root above ~4.2 MP; pixel order is BGRX, not RGB).

Workflow

bash
DISP=:99
export XCTL_DISPLAY=$DISP
S=scripts # path to this skill's scripts
# 1. Isolated display
Xvfb $DISP -screen 0 1920x1080x24 -ac >/tmp/xvfb.log 2>&1 &
sleep 1; DISPLAY=$DISP xdpyinfo >/dev/null || { echo "Xvfb down"; exit 1; }
# 2. Launch the app ON :99 (export DISPLAY, not only XCTL_DISPLAY)
DISPLAY=$DISP <app> & # e.g. an IDE, a browser, a native app
sleep 2 # heavy Electron/JVM apps need 3-8 s; poll with winlist
python3 $S/xctl.py winlist # confirm the window is on :99, note its geometry
# 3. Drive it to the exact state you want (see command table below)
python3 $S/xctl.py click 1490 1003 # open a menu, toggle a panel, etc.
sleep 0.5
# 4. Capture (full, or crop to the region)
python3 $S/xctl.py shot /tmp/raw.png --crop 1270 560 650 520
# 5. Annotate and save
python3 $S/annotate.py box /tmp/raw.png out.png 1300 600 1720 680

Find pixel coordinates from a winlist geometry plus a full shot you eyeball; without a window manager, screen coordinate = window origin + widget offset.

Driving the app - xctl.py

CommandPurpose
winlistList mapped windows with class, name, geometry - find your target and its origin.
move X YMove the pointer (origin = top-left of the display).
click X Y [--button N] [--double]Move then click (1 left, 2 middle, 3 right).
key COMBO ...Key chords: ctrl+shift+p, ctrl+v, Return, Escape, BackSpace.
type TEXTType ASCII only (layout-correct via the live keymap).
`scroll up\down [N]`Wheel N notches at the current pointer - move over the region first.
drag X1 Y1 X2 Y2Press-move-release - resize a panel by dragging its splitter.
place SUBSTR X Y W HMove+resize the first window matching name/class - fixes windows opened off-screen without a WM.
shot OUT [--crop X Y W H]Capture the display; crop to a region.

Focus follows X input focus: click into the target widget before `type` or `ctrl+v`, or the keys go nowhere.

Unicode / non-Latin text - xpaste.py

type is ASCII-only by design. For Cyrillic or any symbol-heavy string (@, {}), never type it - paste it:

bash
python3 $S/xpaste.py "любой текст с @ и {скобками}" 15 & # own CLIPBOARD ~15 s
sleep 0.6 # let it acquire first
python3 $S/xctl.py click 1580 200 # focus the input
python3 $S/xctl.py key ctrl+v

Annotating - annotate.py

bash
python3 $S/annotate.py box in.png out.png X1 Y1 X2 Y2 # rounded #ff6b35 outline
python3 $S/annotate.py crop in.png out.png X Y W H # crop a region
python3 $S/annotate.py arrow in.png out.png X1 Y1 X2 Y2 # line + arrowhead at the tip
python3 $S/annotate.py vstack out.png top.png bottom.png # stack two honest crops

Default accent #ff6b35, width 5, radius 14.

Gotchas

  • `$DISPLAY` vs `$XCTL_DISPLAY`. The app reads $DISPLAY; the scripts default to

:99. A mismatch silently launches the app on :0 while you capture :99 (empty winlist, black frames, and your keystrokes leaking to the real desktop). Export DISPLAY=:99 for the launch.

  • No window manager is the simpler default. Without a WM there is no reparenting,

so screen coordinate = window origin + widget offset (clean math for place). Apps that need a WM for focus/stacking may want openbox/twm; try without first.

  • Focus before keys. XTEST keystrokes and ctrl+v route to the focused window -

click the widget first.

  • `scroll` ignores coordinates - it wheels at the current pointer; move first.
  • Timing. Give the app ~1-2 s (heavy Electron/JVM apps 3-8 s) before winlist;

give xpaste.py ~0.5-1 s to acquire the selection before ctrl+v, or you paste stale or empty clipboard.

  • `type` skips non-ASCII (with a stderr warning) - use xpaste.py. Shifted ASCII

(_ { } @ =) is handled.

  • Blank-frame check. A real capture has non-zero pixel stddev; a near-black frame

means the app is not mapped or you shot the wrong display.

  • `pkill -f xpaste.py` footgun - it also matches the killing shell's own command

line. Kill background helpers by numeric PID.

  • UI coordinates drift between app versions and window sizes - never hardcode from

memory; winlist + a fresh full shot first.

  • Reset app state between shots. For repo-mutating apps (an IDE agent editing

files), snapshot and restore - e.g. git -C <project> checkout . && git clean -fd.

Quick reference

bash
export XCTL_DISPLAY=:99
Xvfb :99 -screen 0 1920x1080x24 -ac & sleep 1
DISPLAY=:99 <app> & sleep 3
python3 scripts/xctl.py winlist
python3 scripts/xpaste.py "текст" 15 & sleep 0.6; python3 scripts/xctl.py key ctrl+v
python3 scripts/xctl.py shot raw.png --crop X Y W H
python3 scripts/annotate.py box raw.png final.png X1 Y1 X2 Y2
pkill -x Xvfb # or kill the specific PID when done

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

  • SKILL.md in the repositoryCheckingSKILL.md

Popularity

Repository stars
5
7-day trend score
9

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/evilfreelancer/screenshotting-gui/screenshotting-gui/badge)](https://verified-skill.com/skills/evilfreelancer/screenshotting-gui/screenshotting-gui)

Something wrong with this skill or its page? Tell us.