Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
verify/clients/src/bin/vkey.rs (5.5K)
1 // vkey — inject key events through zwp_virtual_keyboard_v1 (wtype-style),
2 // for exercising the compositor's keyboard stack in cce-shadow sessions.
3 //
4 // Usage: vkey <keycode|mod:MASK> ...
5 // keycode evdev code, pressed then released (h=35 e=18 l=38 o=24, 1=Escape)
6 // mod:MASK set held modifiers for the keys that follow (xkb depressed mask
7 // under the us keymap: shift=1 ctrl=4 alt=8 super=64); cleared on exit
8 // hold after the events, keep the virtual keyboard alive until killed.
9 // A headless shadow seat has no keyboard at all, so a client that
10 // gains focus there gets wl_keyboard.modifiers with no keymap
11 // before it — Chromium/Electron crash in xkb_state_update_mask on
12 // that. Holding one keyboard gives every later client a keymap.
13
14 use std::io::Write;
15 use std::os::fd::{BorrowedFd, FromRawFd, OwnedFd};
16 use wayland_client::{
17 protocol::{wl_registry, wl_seat},
18 Connection, Dispatch, QueueHandle,
19 };
20 use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{
21 zwp_virtual_keyboard_manager_v1, zwp_virtual_keyboard_v1,
22 };
23
24 #[derive(Default)]
25 struct State {
26 seat: Option<wl_seat::WlSeat>,
27 manager: Option<zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1>,
28 }
29
30 impl Dispatch<wl_registry::WlRegistry, ()> for State {
31 fn event(
32 state: &mut Self,
33 registry: &wl_registry::WlRegistry,
34 event: wl_registry::Event,
35 _: &(),
36 _: &Connection,
37 qh: &QueueHandle<Self>,
38 ) {
39 if let wl_registry::Event::Global {
40 name, interface, version,
41 } = event
42 {
43 match interface.as_str() {
44 "wl_seat" => {
45 state.seat =
46 Some(registry.bind::<wl_seat::WlSeat, _, _>(name, version.min(7), qh, ()));
47 }
48 "zwp_virtual_keyboard_manager_v1" => {
49 state.manager = Some(
50 registry
51 .bind::<zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1, _, _>(
52 name, 1, qh, (),
53 ),
54 );
55 }
56 _ => {}
57 }
58 }
59 }
60 }
61
62 impl Dispatch<wl_seat::WlSeat, ()> for State {
63 fn event(
64 _: &mut Self, _: &wl_seat::WlSeat, _: wl_seat::Event, _: &(), _: &Connection,
65 _: &QueueHandle<Self>,
66 ) {
67 }
68 }
69
70 impl Dispatch<zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1, ()> for State {
71 fn event(
72 _: &mut Self, _: &zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1,
73 _: zwp_virtual_keyboard_manager_v1::Event, _: &(), _: &Connection, _: &QueueHandle<Self>,
74 ) {
75 }
76 }
77
78 impl Dispatch<zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1, ()> for State {
79 fn event(
80 _: &mut Self, _: &zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
81 _: zwp_virtual_keyboard_v1::Event, _: &(), _: &Connection, _: &QueueHandle<Self>,
82 ) {
83 }
84 }
85
86 fn keymap_fd() -> (OwnedFd, u32) {
87 let ctx = xkbcommon::xkb::Context::new(xkbcommon::xkb::CONTEXT_NO_FLAGS);
88 let keymap = xkbcommon::xkb::Keymap::new_from_names(
89 &ctx, "", "", "us", "", None, xkbcommon::xkb::KEYMAP_COMPILE_NO_FLAGS,
90 )
91 .expect("compile keymap");
92 let string = keymap.get_as_string(xkbcommon::xkb::KEYMAP_FORMAT_TEXT_V1);
93
94 let fd = unsafe { libc::memfd_create(b"vkey-keymap\0".as_ptr() as *const _, 0) };
95 assert!(fd >= 0, "memfd_create failed");
96 let mut file = unsafe { std::fs::File::from_raw_fd(fd) };
97 file.write_all(string.as_bytes()).unwrap();
98 file.write_all(b"\0").unwrap();
99 let size = string.len() as u32 + 1;
100 (OwnedFd::from(file), size)
101 }
102
103 fn main() {
104 // args: keycodes, or "mod:<depressed-mask>" to change held modifiers
105 let args: Vec<String> = std::env::args().skip(1).collect();
106 assert!(!args.is_empty(), "usage: vkey <keycode|mod:MASK> ...");
107
108 let conn = Connection::connect_to_env().expect("connect to wayland display");
109 let display = conn.display();
110 let mut queue = conn.new_event_queue();
111 let qh = queue.handle();
112 let _registry = display.get_registry(&qh, ());
113
114 let mut state = State::default();
115 queue.roundtrip(&mut state).unwrap();
116
117 let seat = state.seat.clone().expect("no wl_seat advertised");
118 let manager = state
119 .manager
120 .clone()
121 .expect("no zwp_virtual_keyboard_manager_v1 advertised");
122
123 let vk = manager.create_virtual_keyboard(&seat, &qh, ());
124
125 let (fd, size) = keymap_fd();
126 vk.keymap(1 /* XKB_V1 */, unsafe { BorrowedFd::borrow_raw(std::os::fd::AsRawFd::as_raw_fd(&fd)) }, size);
127 queue.roundtrip(&mut state).unwrap();
128
129 let mut t = 0u32;
130 let hold = args.iter().any(|a| a == "hold");
131 for arg in args.iter().filter(|a| *a != "hold") {
132 if let Some(mask) = arg.strip_prefix("mod:") {
133 let depressed: u32 = mask.parse().expect("mod mask must be a number");
134 vk.modifiers(depressed, 0, 0, 0);
135 } else {
136 let keycode: u32 = arg.parse().expect("keycode must be a number");
137 vk.key(t, keycode, 1); // pressed
138 vk.key(t + 5, keycode, 0); // released
139 t += 10;
140 }
141 }
142 vk.modifiers(0, 0, 0, 0);
143 queue.roundtrip(&mut state).unwrap();
144
145 if hold {
146 println!("holding virtual keyboard");
147 std::io::stdout().flush().ok();
148 loop {
149 queue.blocking_dispatch(&mut state).unwrap();
150 }
151 }
152
153 vk.destroy();
154 queue.roundtrip(&mut state).unwrap();
155 println!("sent {} event(s)", args.len());
156 }