tools capa

tools-gui-harness

Native desktop GUI test harness: find/focus a window (composes infrastructure-screencast), synthesize real hardware mouse/keyboard input via xdotool (physical focus, not synthetic events), screenshot via infrastructure-screen-capture, and read rendered text back via infrastructure-ocr-tesseract. For driving/verifying a real running desktop app (egui/eframe, GTK, Qt, ...) end to end, the same way a human would.

Native desktop GUI test harness: find/focus a window (composes infrastructure-screencast), synthesize real hardware mouse/keyboard input via xdotool (physical focus, not synthetic events), screenshot via infrastructure-screen-capture, and read rendered text back via infrastructure-ocr-tesseract. For driving/verifying a real running desktop app (egui/eframe, GTK, Qt, ...) end to end, the same way a human would.

Tiertools
Roleunclassified (baselined)
Pathcrates/tools/gui-harness
Edition2021
Targetsguarded_click, smoke, verify_window, tools_gui_harness
Public items21 across 4 modules
Tests16

What it is for

Native desktop GUI test harness: find/focus a window, drive it with real hardware mouse/keyboard input, screenshot it, and read its rendered text back via OCR. For verifying a real running desktop app (egui/eframe, GTK, Qt, ...) end to end, the way a human tester would -- not a mock, not a synthetic-event framework.

Composes existing library crates rather than re-implementing them (Gate 0.5): window find/focus is infrastructure-screencast, screenshot is infrastructure-screen-capture, OCR is infrastructure-ocr-tesseract. The one genuinely new piece is input -- hardware mouse/keyboard actuation via xdotool, which existed before only as a private helper inside tools-oauth-consent (Gate 4.5 follow-up: that crate should migrate to this one).

Capabilities

capture (other)

Screenshot capture, thin over infrastructure-screen-capture.

Item
fn capture() -> Result <Shot, CaptureError>

CaptureError

Screenshot capture, thin over infrastructure-screen-capture.

Item
pub enum CaptureError
fn capture_to_file(path : & Path) -> Result <(u32, u32), CaptureError>

Shot

Screenshot capture, thin over infrastructure-screen-capture.

Item
pub struct Shot

Input

Hardware mouse/keyboard actuation via xdotool (argv subprocess, never a

Item
pub struct Input
Input :: fn default() -> Self
Input :: fn new() -> Self
Input :: fn with_bin(bin : impl Into <String>) -> Self
Input :: fn click(& self, x : i64, y : i64, button : u8) -> Result <(), InputError>
Input :: fn move_mouse(& self, x : i64, y : i64) -> Result <(), InputError>
Input :: fn key(& self, sequence : & str) -> Result <(), InputError>
Input :: fn type_text(& self, text : & str) -> Result <(), InputError>
Input :: fn verified_click(& self, x : i64, y : i64, button : u8, expected_window_title_contains : & str,) -> Result <(), VerifiedClickError>
Input :: fn click_and_settle(& self, x : i64, y : i64, button : u8, settle : Duration,) -> Result <(), InputError>

InputError

Hardware mouse/keyboard actuation via xdotool (argv subprocess, never a

Item
pub enum InputError

VerifiedClickError

Hardware mouse/keyboard actuation via xdotool (argv subprocess, never a

Item
pub enum VerifiedClickError

OcrHarnessError

Read rendered text back out of a screenshot, via infrastructure-ocr-tesseract.

Item
pub enum OcrHarnessError
fn recognize_png(png : & u8, width : u32, height : u32) -> Result <String, OcrHarnessError>

WindowError

Window find/focus: composes infrastructure-screencast::window rather than

Item
pub enum WindowError
fn active_window_title(xdotool_bin : & str) -> Result <String, WindowError>
fn find_and_focus(needle : & str) -> Result <WindowInfo, WindowError>

How to use it

From `examples/guarded_click.rs`:

use std::env;
use tools_gui_harness::input::Input;

fn main() {
    let mut args = env::args().skip(1);
    let x: i64 = args
        .next()
        .expect("usage: guarded_click <x> <y> <expected-title-substring>")
        .parse()
        .expect("x must be an integer");
    let y: i64 = args
        .next()
        .expect("missing y")
        .parse()
        .expect("y must be an integer");
    let expected = args.next().expect("missing expected-title-substring");

    match Input::new().verified_click(x, y, 1, &expected) {

From `examples/smoke.rs`:

use tools_gui_harness::{capture, ocr};

fn main() {
    let shot = capture::capture().expect("capture failed");
    println!(
        "captured {}x{} ({} PNG bytes)",
        shot.width,
        shot.height,
        shot.png.len()
    );

    std::fs::write("/tmp/gui-harness-smoke.png", &shot.png).expect("write failed");
    println!("wrote /tmp/gui-harness-smoke.png");

    let text = ocr::recognize_png(&shot.png, shot.width, shot.height).expect("ocr failed");
    println!("OCR recognized {} chars", text.len());
    println!("--- first 300 chars ---");
    println!("{}", text.chars().take(300).collect::<String>());

Module structure

tools_gui_harness

flowchart TD
  n_tools_gui_harness["tools_gui_harness"]
  n_tools_gui_harness --> n_capture["capture"]
  n_tools_gui_harness --> n_input["input"]
  n_tools_gui_harness --> n_ocr["ocr"]
  n_tools_gui_harness --> n_window["window"]

Public surface

`capture`

ItemWhat it is
pub enum CaptureError
pub struct Shot
fn capture() -> Result <Shot, CaptureError>Capture the primary monitor and return the encoded PNG + its dimensions.
fn capture_to_file(path : & Path) -> Result <(u32, u32), CaptureError>Capture the primary monitor and write the PNG to path (for the agent/ operator to visually inspect)

`input`

ItemWhat it is
pub enum InputError
pub enum VerifiedClickErrorInput::verified_click's refusal reasons
pub struct Input
Input :: fn default() -> Self
Input :: fn new() -> Self
Input :: fn with_bin(bin : impl Into <String>) -> Self
Input :: fn click(& self, x : i64, y : i64, button : u8) -> Result <(), InputError>Move the mouse to (x, y) (screen coordinates) and click
Input :: fn move_mouse(& self, x : i64, y : i64) -> Result <(), InputError>Move the mouse to (x, y) without clicking.
Input :: fn key(& self, sequence : & str) -> Result <(), InputError>Send an xdotool key sequence (e.g
Input :: fn type_text(& self, text : & str) -> Result <(), InputError>Type literal text (e.g
Input :: fn verified_click(& self, x : i64, y : i64, button : u8, expected_window_title_contains : & str,) -> Result <(), VerifiedClickError>Click, but only if the window that currently, actually has X11 input focus is the one the caller intends -- checked immediately before the click, not "focused a while ago" (a real incident, 2026-08-09: a fixed-coordinate click during a long-running automation landed in whatever window had stolen focus in the meantime, not the intended target)
Input :: fn click_and_settle(& self, x : i64, y : i64, button : u8, settle : Duration,) -> Result <(), InputError>Convenience: click, then a short settle delay for the app to react before the next screenshot/assertion.

`ocr`

ItemWhat it is
pub enum OcrHarnessError
fn recognize_png(png : & u8, width : u32, height : u32) -> Result <String, OcrHarnessError>Recognize text in a PNG screenshot

`window`

ItemWhat it is
pub enum WindowError
fn active_window_title(xdotool_bin : & str) -> Result <String, WindowError>The title of whichever window currently has X11 input focus, right now -- not "the window we last told to focus" (a focus() call earlier in the program proves nothing about who has focus this instant; another window can steal it in between)
fn find_and_focus(needle : & str) -> Result <WindowInfo, WindowError>Find a window whose title/class contains needle and bring it to the front

No pub use re-exports: every item above is declared in this crate.

Boundary

Reaches into infrastructure.

Shares tier tools with 84 other crates: tools-advisory-reach, tools-archive-guard, tools-artifact-scaffold, tools-ask-ai-core, tools-ask-ais, tools-ask-gemini, tools-book, tools-book-report, … (84 total).

_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._

Where it sits

Tier (ontology)tools
Architectural role (taxonomy)unclassified (baselined)
Locationcrates/tools/gui-harness
Vocabulary in force (lexicon)current

Tier flow. Which tiers this crate's own edges cross.

flowchart LR
  n_tools["tools"] --> n_infrastructure["infrastructure"]

Dependencies

Runtime, in this workspace.

CrateTierOptionalOnly on
`infrastructure-ocr-engine`infrastructurenoalways
`infrastructure-ocr-tesseract`infrastructurenoalways
`infrastructure-screen-capture`infrastructurenoalways
`infrastructure-screencast`infrastructurenoalways

Runtime, from outside the workspace.

CrateRequirementFeaturesOptionalOnly on
thiserror^2noalways
tokio^1fullnoalways

Development. None.

Build. None.

Depended on by. 1 workspace crate.

Signal flow — what reaches this crate, and what it reaches.

flowchart LR
  n_tools_demo_systats["tools-demo-systats"] -->|uses| SELF
  SELF["tools-gui-harness"]
  SELF -->|runtime| n_infrastructure_ocr_engine["infrastructure-ocr-engine"]
  SELF -->|runtime| n_infrastructure_ocr_tesseract["infrastructure-ocr-tesseract"]
  SELF -->|runtime| n_infrastructure_screen_capture["infrastructure-screen-capture"]
  SELF -->|runtime| n_infrastructure_screencast["infrastructure-screencast"]
  classDef self fill:#1f883d,stroke:#1f883d,color:#fff;
  class SELF self;

Feature flags

No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.

Targets

KindNameSource
exampleguarded_click`examples/guarded_click.rs`
examplesmoke`examples/smoke.rs`
exampleverify_window`examples/verify_window.rs`
libtools_gui_harness`src/lib.rs`

Error model

Error typeNamed by
CaptureErrorcapture, capture_to_file
InputErrordeclared, no public signature returns it
OcrHarnessErrorrecognize_png
VerifiedClickErrordeclared, no public signature returns it
WindowErroractive_window_title, find_and_focus

Operational characteristics

PropertyEvidence
async public surfacenone detected
async runtimeyes
database accessnone detected
network I/Onone detected
unsafe codenone detected
environment variablesnone detected

No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.

Configuration

No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.

1 workspace crate depends on this one: tools-demo-systats.

Verification

KindCount
Unit tests16
Integration tests0
Examples3
Doctests0

Evidence by module. How often each public module is named by something executable.

ModuleTestsExamplesConsumers
capture411
input311
ocr210
window310

What the tests establish, by name:

Documentation coverage

MeasureDocumentedTotal
Public items with rustdoc1221
Public modules with a //! block44
pie showData
    title Public items with rustdoc
    "Documented" : 12
    "No rustdoc detected" : 9

Metrics

MetricValue
Rust source files5
Source lines468
Code lines341
Public API items21
Public modules4
Tests16
Examples3
Cargo features0
Direct runtime dependencies6
Workspace reverse dependencies1
pie showData
    title Public API by kind
    "enum" : 5
    "function" : 5
    "method" : 9
    "struct" : 2
pie showData
    title Rust source composition
    "Code" : 341
    "Blank or comment" : 127

Generation

Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.

Todas las tools · Manual