pixelactions 0.5.0

Execute desktop interactions from pixelcoords sessions: resolve a labeled region, act at the verified point, confirm it landed. Cross-platform mouse and keyboard automation for macOS, Windows, and Linux (X11 and Wayland)
[package]
name = "pixelactions"
description = "Execute desktop interactions from pixelcoords sessions: resolve a labeled region, act at the verified point, confirm it landed. Cross-platform mouse and keyboard automation for macOS, Windows, and Linux (X11 and Wayland)"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
readme.workspace = true
authors.workspace = true
homepage.workspace = true
repository.workspace = true
# Set HERE and deliberately not in [workspace.package], because the two
# crates want different answers. This one is bin-only, so docs.rs runs
# `cargo doc`, finds no library target, and records a failed build — there
# is nothing to document and no way to opt out of the attempt. With this
# field unset crates.io links "Documentation" at that failure page; with it
# set, it links the command reference, which is what someone clicking it
# actually wants. pixelactions-core leaves it unset on purpose: it IS a
# library, its docs.rs build is green, and the default link is correct.
documentation = "https://github.com/nolindnaidoo/pixelactions/blob/main/docs/CLI.md"
# The tool's five slots, chosen against what each keyword page actually
# contains. `input` and `desktop` are gone — the first is crossterm and
# gamepads, the second is app frameworks like dioxus. `desktop-automation`
# replaces both: the browse page is exact-match, but free-text search
# splits the hyphen, so it also answers the query "desktop automation".
# `rpa` is the industry's own word for this and the page uiautomation
# sits on; `mouse` is the page enigo and rdev sit on. `testing` is 2000+
# crates deep and unrankable as a keyword — it earns its place as a
# category instead.
keywords = ["automation", "desktop-automation", "rpa", "computer-use", "mouse"]
categories = ["command-line-utilities", "development-tools", "development-tools::testing"]

[dependencies]
pixelactions-core = { path = "../pixelactions-core", version = "0.5.0" }
pixelcoords-core = "0.7"
anyhow = "1"
clap = { version = "4", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
enigo = "0.6"

# Wayland input is a negotiation with the compositor, not a library call,
# so the Linux platform layer owns the protocols directly. Each of these
# is here because nothing already in the tree does the job:
#
# - zbus: the portal handshake is D-Bus. Its blocking API is what keeps
#   AGENTS.md's "no async runtime" rule intact; ashpd is async-only.
# - reis: pure-Rust libei. Its core is synchronous and it forbids unsafe.
#   enigo's own libei path is feature-gated for bugs and GNOME-46-only,
#   and abstracts away the region geometry this tool exists to be exact
#   about (design/02 §5 recommends owning this layer directly).
# - xkbcommon: an EI keyboard carries the compositor's keymap, and typing
#   a character means finding it in that keymap. Already in the tree via
#   enigo; named here because this crate calls it directly. The `wayland`
#   feature is what reads a keymap from an fd, and is stated rather than
#   inherited so a future default change cannot silently remove it.
#   Needs libxkbcommon-dev to link, which CI already installs.
[target.'cfg(target_os = "linux")'.dependencies]
zbus = "5"
reis = "0.7"
xkbcommon = { version = "0.9", features = ["wayland"] }

# Not a new crate in the tree: enigo already builds `windows` 0.61 with
# every feature named here except the two token ones. It is named directly
# because the Windows pointer path is written here rather than taken from
# enigo — see `win.rs` — and `INPUT` is a struct wrapping a union whose
# layout must be exactly right. Declaring it by hand to save a dependency
# that is already compiled would trade a real risk for no saving.
#
# - Input_KeyboardAndMouse: SendInput and MOUSEEVENTF_VIRTUALDESK.
# - WindowsAndMessaging: the SM_*VIRTUALSCREEN metrics and GetCursorPos.
# - HiDpi: per-monitor-v2 awareness, without which every one of the above
#   returns coordinates virtualized against the primary monitor's scale.
# - Security + System_Threading: the process elevation bit `doctor` reports,
#   which is the whole UIPI story in one answer.
[target.'cfg(target_os = "windows")'.dependencies]
windows = { version = "0.61", features = [
    "Win32_Foundation",
    "Win32_Security",
    "Win32_System_Threading",
    "Win32_UI_HiDpi",
    "Win32_UI_Input_KeyboardAndMouse",
    "Win32_UI_WindowsAndMessaging",
] }

[lints]
workspace = true