wayrs_utils/
keyboard.rs

1//! wl_keyboard helper
2
3use std::fmt::{self, Debug};
4use std::time::Duration;
5
6use wayrs_client::object::ObjectId;
7use wayrs_client::object::Proxy;
8use wayrs_client::protocol::wl_keyboard::{EnterArgs, LeaveArgs};
9use wayrs_client::protocol::*;
10use wayrs_client::{Connection, EventCtx};
11
12pub use xkbcommon::xkb;
13
14use crate::timer::Timer;
15
16pub trait KeyboardHandler: Sized + 'static {
17    /// Get a reference to a [`Keyboard`]. It is guaranteed that the requested keyboard was created
18    /// in [`Keyboard::new`].
19    ///
20    /// Returning a reference to a wrong object may cause [`Connection::dispatch_events`] to panic.
21    fn get_keyboard(&mut self, wl_keyboard: WlKeyboard) -> &mut Keyboard;
22
23    fn key_presed(&mut self, conn: &mut Connection<Self>, event: KeyboardEvent);
24
25    fn key_released(&mut self, conn: &mut Connection<Self>, event: KeyboardEvent);
26
27    fn enter_surface(&mut self, _: &mut Connection<Self>, _: WlKeyboard, _: EnterArgs) {}
28
29    fn leave_surface(&mut self, _: &mut Connection<Self>, _: WlKeyboard, _: LeaveArgs) {}
30}
31
32/// A wrapper of `wl_keyboard`.
33///
34/// Manages `xkb::Context` and `xkb::State`.
35pub struct Keyboard {
36    seat: WlSeat,
37    wl: WlKeyboard,
38    focused_surface: Option<ObjectId>,
39    xkb_context: xkb::Context,
40    xkb_state: Option<xkb::State>,
41    repeat_info: Option<RepeatInfo>,
42}
43
44#[derive(Debug, Clone, Copy)]
45pub struct RepeatInfo {
46    pub delay: Duration,
47    pub interval: Duration,
48}
49
50#[derive(Clone)]
51#[non_exhaustive]
52pub struct KeyboardEvent {
53    pub seat: WlSeat,
54    pub keyboard: WlKeyboard,
55    pub surface: ObjectId,
56    pub serial: u32,
57    pub time: u32,
58    pub keycode: xkb::Keycode,
59    pub keysym: xkb::Keysym,
60    /// How this key should be repeated.
61    ///
62    /// Present if the compositor advertised repeat info AND this key should be repeated (as defined
63    /// by the current keymap).
64    pub repeat_info: Option<RepeatInfo>,
65    pub xkb_state: xkb::State,
66}
67
68impl Keyboard {
69    /// Create a new `Keyboard`.
70    ///
71    /// Call this only when `wl_seat` advertises a keyboard capability.
72    #[inline]
73    pub fn new<D: KeyboardHandler>(conn: &mut Connection<D>, seat: WlSeat) -> Self {
74        Self {
75            seat,
76            wl: seat.get_keyboard_with_cb(conn, wl_keyboard_cb),
77            focused_surface: None,
78            xkb_context: xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
79            xkb_state: None,
80            repeat_info: None,
81        }
82    }
83
84    #[inline]
85    pub fn seat(&self) -> WlSeat {
86        self.seat
87    }
88
89    #[inline]
90    pub fn wl_keyboard(&self) -> WlKeyboard {
91        self.wl
92    }
93
94    #[inline]
95    pub fn destroy<D>(self, conn: &mut Connection<D>) {
96        if self.wl.version() >= 3 {
97            self.wl.release(conn);
98        }
99    }
100}
101
102impl RepeatInfo {
103    /// Create a timer.
104    pub fn timer(self) -> Timer {
105        Timer::new(self.delay, self.interval)
106    }
107}
108
109fn wl_keyboard_cb<D: KeyboardHandler>(ctx: EventCtx<D, WlKeyboard>) {
110    let kbd = ctx.state.get_keyboard(ctx.proxy);
111    assert_eq!(
112        kbd.wl, ctx.proxy,
113        "invalid KeyboardHandler::get_keyboard() implementation"
114    );
115
116    match ctx.event {
117        wl_keyboard::Event::Keymap(args) if args.format == wl_keyboard::KeymapFormat::XkbV1 => {
118            let keymap = unsafe {
119                xkb::Keymap::new_from_fd(
120                    &kbd.xkb_context,
121                    args.fd,
122                    args.size as usize,
123                    xkb::FORMAT_TEXT_V1,
124                    xkb::KEYMAP_COMPILE_NO_FLAGS,
125                )
126            };
127            if let Ok(Some(keymap)) = keymap {
128                kbd.xkb_state = Some(xkb::State::new(&keymap));
129            }
130        }
131        wl_keyboard::Event::Enter(args) => {
132            kbd.focused_surface = Some(args.surface);
133            ctx.state.enter_surface(ctx.conn, ctx.proxy, args);
134        }
135        wl_keyboard::Event::Leave(args) => {
136            kbd.focused_surface = None;
137            ctx.state.leave_surface(ctx.conn, ctx.proxy, args);
138            // TODO: clear modifiers (how?)
139        }
140        wl_keyboard::Event::Key(args) => {
141            let Some(xkb_state) = kbd.xkb_state.clone() else {
142                return;
143            };
144            let Some(surface) = kbd.focused_surface else {
145                return;
146            };
147
148            let keycode = xkb::Keycode::new(args.key + 8);
149            let keysym = xkb_state.key_get_one_sym(keycode);
150
151            let repeat_info = if xkb_state.get_keymap().key_repeats(keycode) {
152                kbd.repeat_info
153            } else {
154                None
155            };
156
157            let event = KeyboardEvent {
158                seat: kbd.seat,
159                keyboard: kbd.wl,
160                surface,
161                serial: args.serial,
162                time: args.time,
163                keycode,
164                keysym,
165                repeat_info,
166                xkb_state,
167            };
168
169            match args.state {
170                wl_keyboard::KeyState::Released => ctx.state.key_released(ctx.conn, event),
171                wl_keyboard::KeyState::Pressed => ctx.state.key_presed(ctx.conn, event),
172                _ => (),
173            }
174        }
175        wl_keyboard::Event::Modifiers(args) => {
176            if let Some(xkb_state) = &mut kbd.xkb_state {
177                xkb_state.update_mask(
178                    args.mods_depressed,
179                    args.mods_latched,
180                    args.mods_locked,
181                    0,
182                    0,
183                    args.group,
184                );
185            }
186        }
187        wl_keyboard::Event::RepeatInfo(args) => {
188            if args.rate == 0 {
189                kbd.repeat_info = None;
190            } else if args.rate > 0 && args.delay > 0 {
191                kbd.repeat_info = Some(RepeatInfo {
192                    delay: Duration::from_millis(args.delay as u64),
193                    interval: Duration::from_micros(1_000_000 / args.rate as u64),
194                });
195            }
196        }
197        _ => (),
198    }
199}
200
201impl Debug for KeyboardEvent {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        f.debug_struct("KeyboardEvent")
204            .field("seat", &self.seat)
205            .field("keyboard", &self.keyboard)
206            .field("serial", &self.serial)
207            .field("time", &self.time)
208            .field("keycode", &self.keycode)
209            .field("repeat_info", &self.repeat_info)
210            .field("xkb_state", &"???")
211            .finish()
212    }
213}