git.lucas.co / cce-compositor
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 }