ZyrisDocs
Addon crate guides

zyris-input

Serve keyboard and pointer tools, choose Linux backends, restore portal grants and keep screenshot coordinates aligned.

zyris-input implements input v1 using EnigoInput. It types text, presses chords, moves the pointer, clicks and scrolls. Input acts on the real desktop; it is not scoped to a single application.

Dependencies and publication status

[dependencies]
zyris = { git = "https://github.com/attacca-cc/zyris-protocol", features = ["caps"] }
zyris-input = { git = "https://github.com/attacca-cc/zyris-protocol" }
zyris-screen = { git = "https://github.com/attacca-cc/zyris-protocol" }

This crate explicitly sets publish = false. It needs an enigo fork for output-relative Wayland motion and libei key handling. A dependency with both version and git can be packaged, but Cargo removes the git source in the packaged manifest; that would select upstream behavior instead of the required fork. This is why you must not replace it with a registry dependency or describe the limitation simply as “Cargo rejects every git dependency.”

For reproducible releases, pin all git Zyris dependencies to the same tested revision and keep Cargo.lock. These docs follow main; a floating git dependency is not a release guarantee.

Choose the Linux backend

FeatureIntended sessionRequirements / limitations
DefaultX11, or Xwayland where XTEST is honoredlibxkbcommon; GNOME/KDE Wayland may silently ignore XTEST.
waylandwlroots compositorsVirtual keyboard/pointer protocols, Wayland client library; enables per-output pointer support.
libeiGNOME/KDE WaylandRemoteDesktop portal and a user-approved session; current fractional-scaling limitation below.

For example, opt into GNOME/KDE support with:

zyris-input = { git = "https://github.com/attacca-cc/zyris-protocol", features = ["libei"] }

Use wayland instead for a wlroots-only build. Features are additive; they are backend support, not independent tools announced to the peer. On Windows and macOS the Linux flags do not replace the existing platform input backend.

Debian / Ubuntu input build dependencies
sudo apt-get install libxkbcommon-dev
# Also needed when enabling the wayland backend:
sudo apt-get install libwayland-dev

Adding zyris-screen additionally requires its capture build dependencies. macOS needs Accessibility permission for the process.

Register screen and input together

use zyris::caps::{InputServer, ScreenCaptureServer};
use zyris::{Node, NodeKind};
use zyris_input::EnigoInput;
use zyris_screen::{HostDisplays, HostScreenCapture};

fn desktop_node() -> zyris::Result<Node> {
    let capture = HostScreenCapture::default().with_max_width(1280);
    let input = EnigoInput::new(HostDisplays(capture.backend()))?;
    Node::builder()
        .name("desktop")
        .kind(NodeKind::Desktop)
        .capability(ScreenCaptureServer(capture))
        .capability(InputServer(input))
        .build()
}

Connect using Connecting. EnigoInput::new opens the host input backend and can fail during initialization. On a headless host, omit this capability rather than announcing input that cannot act. Use the same backend for HostDisplays and capture.

An input-only node does not have to depend on zyris-screen: pass any implementation of zyris::caps::Displays, including a Vec<Display> containing a known static layout. You are then responsible for keeping that layout consistent with the desktop; a stale layout aims at the wrong pixel.

Tools and coordinates

ToolArgumentsMeaning
type_texttext: StringType text into the focused application.
keychord: StringPress a chord such as ctrl+c, ctrl+shift+t or Enter.
move_todisplay: String, x: i32, y: i32Move to display-local captured pixels, not global/logical coordinates.
clickbutton: MouseButtonLeft, right or middle click at the current position.
scrolldx: i32, dy: i32Horizontal/vertical scroll deltas.

The typed caller imports Input and obtains InputClient through conn.wait_capability. Display resolution tries ID before name. An out-of-display point is rejected. Undo screenshot downscaling and add a crop's region origin before calling move_to; do not apply scale_factor or add the display's global origin yourself.

Call input from a typed client

The display ID and point must come from the same geometry as your screenshot. This helper moves and clicks only; it deliberately does not type into an application whose focus you have not checked.

use std::time::Duration;
use zyris::caps::{Input, InputClient, MouseButton};
use zyris::Connection;

async fn click_point(conn: &Connection, display: String, x: i32, y: i32) -> zyris::Result<()> {
    let input: InputClient = conn.wait_capability(Duration::from_secs(5)).await?;
    input.move_to(display, x, y).await?;
    input.click(MouseButton::Left).await
}

Once the intended editable control has focus, input.type_text("Hello".into()).await? types text, and input.key("Enter".into()).await? submits it. These are separate actions: typing does not implicitly press Enter. input.scroll(0, 3).await? sends a vertical scroll delta; validate the host/backend's resulting movement rather than treating it as a fixed number of pixels.

Restore a libei permission grant

This helper requires the libei feature:

use zyris_input::{settings_for_session, EnigoInput};
use zyris_screen::HostDisplays;

fn input_with_grant(saved_token: Option<String>) -> zyris::Result<(EnigoInput, Option<String>)> {
    let settings = settings_for_session(saved_token);
    let input = EnigoInput::with_settings(&settings, HostDisplays::default())?;
    let token = input.restore_token();
    Ok((input, token))
}

Save a returned token in protected application storage and pass it on the next startup. Do not log it or hard-code it in source. Portal restore is subject to the desktop's approval/revocation policy; it is not a guarantee of permanent unattended access. settings_for_session also disables the X11 fallback in a libei Wayland session so silently ignored XTEST calls do not look like a working backend.

Current limitation and safety

The current libei backend does not resolve absolute motion against the device's logical-pixel region, so fractional scaling on GNOME/KDE can put the pointer short of the screenshot position. Do not “fix” this by changing the shared captured-pixel contract; validate or restrict unsupported layouts until the backend is corrected.

Input tools carry the host process's desktop privileges. A filesystem root does not restrict them. Provide an application permission boundary and a user-visible way to stop control; do not expose the tools merely because backend initialization succeeded. Check focus and display state before acting.

Reference

Source, features and limitations · Shared display contract

On this page