1use std::collections::{HashMap, HashSet};
23use std::os::fd::AsFd;
24use std::os::unix::net::UnixStream;
25use std::sync::atomic::{AtomicU64, Ordering};
26use std::sync::{Mutex, OnceLock};
27use std::time::Duration;
28
29use wayland_client::protocol::{wl_registry, wl_seat};
30use wayland_client::{Connection, Dispatch, EventQueue, QueueHandle, delegate_noop};
31use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{
32 zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1,
33 zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
34};
35
36use crate::wayland::keymap::{KeymapPolicy, compile_rmlvo};
37use crate::wayland::wlclient::{SyncState, bounded_roundtrip, impl_sync_callback, memfd_with};
38
39const OVERLAY_KEYCODES: [u32; 47] = [
47 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
48 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
49];
50const OVERLAY_SLOTS: usize = OVERLAY_KEYCODES.len();
51const EVDEV_OFFSET: u32 = 8;
53const KEYMAP_FORMAT_XKB_V1: u32 = 1;
54
55#[derive(Default)]
56struct Globals {
57 seat: Option<wl_seat::WlSeat>,
58 manager: Option<ZwpVirtualKeyboardManagerV1>,
59 sync_done: bool,
60}
61
62impl SyncState for Globals {
63 fn sync_done_mut(&mut self) -> &mut bool {
64 &mut self.sync_done
65 }
66}
67impl_sync_callback!(Globals);
68
69impl Dispatch<wl_registry::WlRegistry, ()> for Globals {
70 fn event(
71 state: &mut Self,
72 registry: &wl_registry::WlRegistry,
73 event: wl_registry::Event,
74 _: &(),
75 _: &Connection,
76 qh: &QueueHandle<Self>,
77 ) {
78 if let wl_registry::Event::Global {
79 name, interface, ..
80 } = event
81 {
82 match interface.as_str() {
84 "wl_seat" if state.seat.is_none() => {
85 state.seat = Some(registry.bind(name, 1, qh, ()));
86 }
87 "zwp_virtual_keyboard_manager_v1" if state.manager.is_none() => {
88 state.manager = Some(registry.bind(name, 1, qh, ()));
89 }
90 _ => {}
91 }
92 }
93 }
94}
95
96delegate_noop!(Globals: ignore wl_seat::WlSeat);
97delegate_noop!(Globals: ZwpVirtualKeyboardManagerV1);
98delegate_noop!(Globals: ZwpVirtualKeyboardV1);
99
100pub(crate) fn us_base_text() -> Option<&'static str> {
103 static CACHE: OnceLock<Option<String>> = OnceLock::new();
104 CACHE
105 .get_or_init(|| compile_rmlvo("", "", "us", "", ""))
106 .as_deref()
107}
108
109fn shared_policy() -> Option<&'static Mutex<KeymapPolicy>> {
114 static POLICY: OnceLock<Option<Mutex<KeymapPolicy>>> = OnceLock::new();
115 POLICY
116 .get_or_init(|| {
117 let mut policy = KeymapPolicy::with_overlay_codes(OVERLAY_KEYCODES.to_vec());
118 policy
119 .rebuild_base(us_base_text()?.to_string())
120 .then(|| Mutex::new(policy))
121 })
122 .as_ref()
123}
124
125fn upload_keymap(
126 vk: &ZwpVirtualKeyboardV1,
127 queue: &mut EventQueue<Globals>,
128 text: &str,
129) -> Result<(), String> {
130 let mut data = text.as_bytes().to_vec();
131 data.push(0);
133 let fd = memfd_with(&data)?;
134 vk.keymap(KEYMAP_FORMAT_XKB_V1, fd.as_fd(), data.len() as u32);
135 queue.flush().map_err(|e| format!("flush keymap: {e}"))
136}
137
138fn universal_keysym(c: char) -> Option<u32> {
144 match c {
145 '\n' | '\r' => Some(0xFF0D),
146 '\t' => Some(0xFF09),
147 '\x1b' => Some(0xFF1B),
148 _ => match c as u32 {
149 cp @ 0x20..=0xFF => Some(cp),
150 cp @ 0x100.. => Some(0x0100_0000 | cp),
151 _ => None,
152 },
153 }
154}
155
156static KEYMAP_GENERATION: AtomicU64 = AtomicU64::new(1);
159
160struct Typer {
161 conn: Connection,
162 queue: EventQueue<Globals>,
163 state: Globals,
164 vk: ZwpVirtualKeyboardV1,
165 uploaded_generation: u64,
166}
167
168fn typers() -> &'static Mutex<HashMap<String, Typer>> {
169 static TYPERS: OnceLock<Mutex<HashMap<String, Typer>>> = OnceLock::new();
170 TYPERS.get_or_init(|| Mutex::new(HashMap::new()))
171}
172
173fn connect_typer(socket_path: &str) -> Result<Typer, String> {
174 let stream =
175 UnixStream::connect(socket_path).map_err(|e| format!("connect {socket_path}: {e}"))?;
176 let conn = Connection::from_socket(stream).map_err(|e| format!("wayland setup: {e}"))?;
177 let mut queue = conn.new_event_queue();
178 let qh = queue.handle();
179 let _registry = conn.display().get_registry(&qh, ());
180 let mut state = Globals::default();
181 bounded_roundtrip(&conn, &mut queue, &mut state)?;
182 let seat = state
183 .seat
184 .take()
185 .ok_or("app compositor advertises no wl_seat")?;
186 let manager = state
187 .manager
188 .take()
189 .ok_or("app compositor does not advertise zwp_virtual_keyboard_manager_v1")?;
190 let vk = manager.create_virtual_keyboard(&seat, &qh, ());
191 bounded_roundtrip(&conn, &mut queue, &mut state)?;
193 Ok(Typer {
194 conn,
195 queue,
196 state,
197 vk,
198 uploaded_generation: 0,
199 })
200}
201
202fn flush_keysyms(
203 typer: &mut Typer,
204 policy: &mut KeymapPolicy,
205 keysyms: &[u32],
206) -> Result<(), String> {
207 let none = HashSet::new();
208 for chunk in keysyms.chunks(OVERLAY_SLOTS) {
211 let (keycodes, changed) = policy.bind_many_plain(chunk, &none);
212 if changed {
213 KEYMAP_GENERATION.fetch_add(1, Ordering::Relaxed);
214 }
215 let generation = KEYMAP_GENERATION.load(Ordering::Relaxed);
219 if typer.uploaded_generation != generation {
220 upload_keymap(&typer.vk, &mut typer.queue, &policy.keymap_text())?;
221 bounded_roundtrip(&typer.conn, &mut typer.queue, &mut typer.state)?;
222 std::thread::sleep(Duration::from_millis(10));
223 typer.uploaded_generation = generation;
224 }
225 for &kc in &keycodes {
228 if kc < EVDEV_OFFSET {
230 continue;
231 }
232 typer.vk.key(0, kc - EVDEV_OFFSET, 1);
233 typer.vk.key(0, kc - EVDEV_OFFSET, 0);
234 }
235 typer
236 .queue
237 .flush()
238 .map_err(|e| format!("flush keys: {e}"))?;
239 bounded_roundtrip(&typer.conn, &mut typer.queue, &mut typer.state)?;
240 }
241 Ok(())
242}
243
244pub fn type_text_to(socket_path: &str, text: &str) -> Result<(), String> {
248 let syms: Vec<u32> = text.chars().filter_map(universal_keysym).collect();
249 type_keysyms_to(socket_path, &syms)
250}
251
252pub fn type_keysyms_to(socket_path: &str, keysyms: &[u32]) -> Result<(), String> {
256 let mut policy = shared_policy()
257 .ok_or("us base keymap failed to compile")?
258 .lock()
259 .unwrap_or_else(|poisoned| poisoned.into_inner());
260 let mut typers = typers()
261 .lock()
262 .unwrap_or_else(|poisoned| poisoned.into_inner());
263 let mut last_err = None;
264 for _ in 0..2 {
265 if !typers.contains_key(socket_path) {
266 typers.insert(socket_path.to_string(), connect_typer(socket_path)?);
267 }
268 let typer = typers.get_mut(socket_path).expect("typer just ensured");
269 match flush_keysyms(typer, &mut policy, keysyms) {
270 Ok(()) => return Ok(()),
271 Err(e) => {
272 typers.remove(socket_path);
276 last_err = Some(e);
277 }
278 }
279 }
280 Err(last_err.unwrap_or_else(|| "virtual-keyboard flush failed".to_string()))
281}