Skip to main content

xkb_type/
lib.rs

1//! Virtual keyboard typing via evdev/uinput with XKB layout-aware key mapping.
2//!
3//! This crate provides:
4//! - [`Keyboard`] — a virtual uinput device that injects keystrokes.
5//! - [`XkbKeymap`] — reverse char→keycode lookup from the active XKB layout.
6//! - [`ClipboardBackend`] — trait + auto-detected clipboard implementations.
7//!
8//! # Example (auto-detect layout and clipboard)
9//!
10//! ```no_run
11//! use xkb_type::{Keyboard, Key};
12//! use std::time::Duration;
13//!
14//! let mut kb = Keyboard::new(Duration::from_millis(2))?;
15//! kb.type_text("Hello — こんにちは — €100 — 😀")?;
16//! kb.backspace(5)?;
17//! kb.send_combo(&[Key::KEY_LEFTCTRL, Key::KEY_C])?;
18//! # Ok::<(), anyhow::Error>(())
19//! ```
20//!
21//! # Example (explicit layout)
22//!
23//! ```no_run
24//! use xkb_type::Keyboard;
25//! use std::time::Duration;
26//!
27//! let mut kb = Keyboard::with_layout("de", None, Duration::from_millis(5))?;
28//! kb.type_text("Schöne Grüße")?;
29//! # Ok::<(), anyhow::Error>(())
30//! ```
31
32pub mod clipboard;
33pub mod keyboard;
34pub mod keymap;
35
36// ---------------------------------------------------------------------------
37// Core types
38// ---------------------------------------------------------------------------
39
40/// A single keypress with optional Shift and/or AltGr modifiers.
41#[derive(Debug, Clone, Copy)]
42pub struct KeyTap {
43    pub keycode: u16,
44    pub shift: bool,
45    pub altgr: bool,
46}
47
48/// Information needed to produce a character at the cursor.
49///
50/// For most characters this is a single [`KeyTap`]. For characters that
51/// XKB only exposes as a dead-key combination (e.g. `ã` = `dead_tilde + a`
52/// on `us:intl`, or `'` = `dead_acute + space`), a `follow` tap is
53/// recorded so the typer emits the dead-key keypress followed by the
54/// base-letter (or space) keypress in sequence.
55#[derive(Debug, Clone, Copy)]
56pub struct KeyMapping {
57    pub main: KeyTap,
58    pub follow: Option<KeyTap>,
59}
60
61// ---------------------------------------------------------------------------
62// ClipboardBackend trait
63// ---------------------------------------------------------------------------
64
65/// Trait for clipboard get/set operations.
66pub trait ClipboardBackend: Send + Sync {
67    /// Read the current clipboard text content.
68    fn get_text(&self) -> anyhow::Result<String>;
69
70    /// Set the clipboard to the given text.
71    fn set_text(&self, text: &str) -> anyhow::Result<()>;
72
73    /// Read the primary selection (highlighted text, no Ctrl+C needed).
74    fn get_primary_selection(&self) -> anyhow::Result<String>;
75}
76
77// ---------------------------------------------------------------------------
78// Re-exports
79// ---------------------------------------------------------------------------
80
81pub use clipboard::{default_clipboard, NoopClipboard, WaylandClipboard, X11Clipboard};
82pub use keyboard::Keyboard;
83pub use keymap::{KeyboardLayout, XkbKeymap};
84
85// Re-export evdev's `Key` enum (and the full `evdev` module) so callers
86// of [`Keyboard::send_combo`] don't need a direct `evdev` dependency.
87pub use evdev;
88pub use evdev::Key;
89
90/// Convenience: build a [`Keyboard`] from the detected layout with a
91/// sensible default key delay (5 ms).
92///
93/// Returns an error if the XKB keymap cannot be built (missing locale data)
94/// or if `/dev/uinput` is not writable.
95///
96/// Equivalent to `Keyboard::new(Duration::from_millis(5))`.
97pub fn keyboard_from_detected_layout() -> anyhow::Result<Keyboard> {
98    Keyboard::new(std::time::Duration::from_millis(5))
99}