agent-seat-linux 0.2.0

App-scoped Wayland capture and input for Linux automation agents
Documentation
# Using `agent-seat-linux` from other languages

The crate's high-level boundary is deliberately small so any language with a
Rust FFI layer can wrap it without inheriting a host-app data model:

1. Convert your tool call into a `LaunchConfig`.
2. `ComputerUse::launch` returns one `ControlledApp`.
3. Route screenshot / click / scroll / drag / key / text tools to that handle.
4. Drop `ControlledApp` to stop the app; drop `ComputerUse` to close the seat,
   bridges, threads, sockets, and credentials.

No bindings are shipped yet; the recommended pattern is a thin `extern "C"`
shim over the high-level API. Keep the shim to the five verbs above and pass
opaque handles across the boundary.

## Recommended C-ABI shim (sketch)

```rust
// ffi.rs — build as a cdylib
use agent_seat_linux::{ComputerUse, LaunchConfig, PointerButton};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

pub struct Handle { seat: ComputerUse }          // opaque to callers
pub struct AppHandle { app: agent_seat_linux::ControlledApp }

#[no_mangle] pub extern "C" fn asl_new() -> *mut Handle {
    match ComputerUse::new() { Ok(s) => Box::into_raw(Box::new(Handle{seat:s})), Err(_) => std::ptr::null_mut() }
}

#[no_mangle] pub extern "C" fn asl_launch(h: *mut Handle, program: *const c_char) -> *mut AppHandle {
    let h = unsafe { &*h };
    let prog = unsafe { CStr::from_ptr(program) };
    match h.seat.launch(LaunchConfig::new(prog.to_string_lossy().into_owned())) {
        Ok(app) => Box::into_raw(Box::new(AppHandle{app})),
        Err(_) => std::ptr::null_mut(),
    }
}

#[no_mangle] pub extern "C" fn asl_click(a: *mut AppHandle, x: f64, y: f64) -> i32 {
    let a = unsafe { &*a };
    match a.app.click(x, y, PointerButton::Left, 1) { Ok(()) => 0, Err(_) => -1 }
}

#[no_mangle] pub extern "C" fn asl_type_text(a: *mut AppHandle, text: *const c_char) -> i32 {
    let a = unsafe { &*a };
    let t = unsafe { CStr::from_ptr(text) };
    match a.app.type_text(&t.to_string_lossy()) { Ok(()) => 0, Err(_) => -1 }
}

#[no_mangle] pub extern "C" fn asl_free_app(a: *mut AppHandle) { if !a.is_null() { unsafe { drop(Box::from_raw(a)); } } }
#[no_mangle] pub extern "C" fn asl_free(h: *mut Handle)      { if !h.is_null() { unsafe { drop(Box::from_raw(h)); } } }
```

Corresponding C header (abridged):

```c
typedef struct Handle Handle;
typedef struct AppHandle AppHandle;
Handle    *asl_new(void);
AppHandle *asl_launch(Handle *h, const char *program);
int        asl_click(AppHandle *a, double x, double y);
int        asl_type_text(AppHandle *a, const char *text);
void       asl_free_app(AppHandle *a);
void       asl_free(Handle *h);
```

## Conventions for a good binding

- **Errors:** return an integer/`Result` code and expose `asl_last_error()` that
  returns a thread-local `CString` message; never return Rust `String`/`&str`.
- **Ownership:** every `*_new`/`launch` has a matching `*_free`; document that
  dropping the app handle stops the controlled application.
- **Frames:** expose `capture` as `(width, height, *const u8 rgb, len)` plus a
  `free_frame`, or write PNG to a caller-provided path to avoid crossing with
  large buffers.
- **Threading:** the seat runs background threads; call from a single owner
  thread or guard the handles with a mutex in the binding.

Generate the header automatically with [`cbindgen`](https://github.com/mozilla/cbindgen)
if you prefer, but keep the exported surface limited to the verbs above.