git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

src/server/keyboard.rs (12.7K)

  1 // SPDX-FileCopyrightText: © 2026 The River Developers
  2 // SPDX-License-Identifier: GPL-3.0-only
  3 
  4 use crate::ffi;
  5 use crate::input_device::InputDevice;
  6 use crate::server::{WlListener, wl_listener_remove, wl_signal_add};
  7 use crate::keyboard_group::KeyboardGroup;
  8 use std::collections::HashSet;
  9 
 10 /// Key repeat defaults (keys/s, ms before the first repeat). A hardware
 11 /// keyboard takes `input { repeat_rate repeat_delay }` over these
 12 /// (`InputConfig::repeat_info`); the synthetic and virtual keyboards keep
 13 /// them. A client can still change a device's via river's
 14 /// `input_device.set_repeat_info`, until the next config load.
 15 pub const DEFAULT_REPEAT_RATE: i32 = 40;
 16 pub const DEFAULT_REPEAT_DELAY: i32 = 250;
 17 
 18 #[derive(Clone, Copy)]
 19 pub struct KeyboardConfig {
 20     pub keymap: *mut ffi::xkb_keymap,
 21     pub repeat_rate: i32,
 22     pub repeat_delay: i32,
 23 }
 24 
 25 pub struct Keyboard {
 26     pub device: *mut InputDevice,
 27     pub wlr_keyboard: *mut ffi::wlr_keyboard,
 28     pub key_listener: ffi::wl_listener,
 29     pub modifiers_listener: ffi::wl_listener,
 30     /// Registered for virtual keyboards only: their keymap arrives from the
 31     /// client *after* creation (the zwp_virtual_keyboard_v1 keymap request),
 32     /// so it must be forwarded to the group once it lands.
 33     pub keymap_listener: ffi::wl_listener,
 34     pub pressed: HashSet<u32>,
 35     pub group: *mut KeyboardGroup,
 36     pub group_link: ffi::wl_list,
 37     pub config: KeyboardConfig,
 38 }
 39 
 40 impl Keyboard {
 41     pub unsafe fn create(device: *mut InputDevice) -> *mut Self {
 42         let wlr_keyboard = ffi::wlr_keyboard_from_input_device((*device).wlr_device);
 43         
 44         let virtual_device = (*device).virtual_device;
 45         let (repeat_rate, repeat_delay) = if virtual_device {
 46             (DEFAULT_REPEAT_RATE, DEFAULT_REPEAT_DELAY)
 47         } else {
 48             (*(*(*device).seat).server).wm.input_config.repeat_info()
 49         };
 50         let mut keymap = std::ptr::null_mut();
 51         if virtual_device {
 52             let kbd_keymap = ffi::river_wlr_keyboard_get_keymap(wlr_keyboard);
 53             if !kbd_keymap.is_null() {
 54                 keymap = kbd_keymap;
 55                 ffi::xkb_keymap_ref(keymap);
 56             }
 57         } else {
 58             let server = (*(*device).seat).server;
 59             keymap = (*server).xkb_config.default_keymap;
 60             if !keymap.is_null() {
 61                 ffi::xkb_keymap_ref(keymap);
 62             }
 63         }
 64 
 65         let keyboard = Box::into_raw(Box::new(Self {
 66             device,
 67             wlr_keyboard,
 68             key_listener: std::mem::zeroed(),
 69             modifiers_listener: std::mem::zeroed(),
 70             keymap_listener: std::mem::zeroed(),
 71             pressed: HashSet::new(),
 72             group: std::ptr::null_mut(),
 73             group_link: std::mem::zeroed(),
 74             config: KeyboardConfig {
 75                 keymap,
 76                 repeat_rate,
 77                 repeat_delay,
 78             },
 79         }));
 80 
 81         ffi::wl_list_init(&mut (*keyboard).group_link);
 82         ffi::river_wlr_keyboard_set_data(wlr_keyboard, keyboard as *mut _);
 83 
 84         let key_listener_ptr = &mut (*keyboard).key_listener as *mut ffi::wl_listener as *mut WlListener;
 85         (*key_listener_ptr).notify = Some(handle_key);
 86 
 87         let modifiers_listener_ptr = &mut (*keyboard).modifiers_listener as *mut ffi::wl_listener as *mut WlListener;
 88         (*modifiers_listener_ptr).notify = Some(handle_modifiers);
 89 
 90         let key_signal = ffi::river_wlr_keyboard_get_key_signal(wlr_keyboard);
 91         wl_signal_add(key_signal, &mut (*keyboard).key_listener);
 92 
 93         let modifiers_signal = ffi::river_wlr_keyboard_get_modifiers_signal(wlr_keyboard);
 94         wl_signal_add(modifiers_signal, &mut (*keyboard).modifiers_listener);
 95 
 96         if virtual_device {
 97             let keymap_listener_ptr = &mut (*keyboard).keymap_listener as *mut ffi::wl_listener as *mut WlListener;
 98             (*keymap_listener_ptr).notify = Some(handle_keymap);
 99             let keymap_signal = ffi::river_wlr_keyboard_get_keymap_signal(wlr_keyboard);
100             wl_signal_add(keymap_signal, &mut (*keyboard).keymap_listener);
101         }
102 
103         if !virtual_device && should_set_keymap((*(*device).seat).server) {
104             if !keymap.is_null() {
105                 ffi::wlr_keyboard_set_keymap(wlr_keyboard, keymap);
106             }
107         }
108 
109         (*device).destroy_fn = Some(destroy_callback);
110         (*device).destroy_data = keyboard as *mut _;
111 
112         keyboard
113     }
114 
115     pub unsafe fn destroy(keyboard: *mut Self) {
116         wl_listener_remove(&mut (*keyboard).key_listener);
117         wl_listener_remove(&mut (*keyboard).modifiers_listener);
118         // destroy_fn runs before the InputDevice is freed, so device is valid.
119         if (*(*keyboard).device).virtual_device {
120             wl_listener_remove(&mut (*keyboard).keymap_listener);
121         }
122 
123         if !(*keyboard).group.is_null() {
124             let keys: Vec<u32> = (*keyboard).pressed.iter().cloned().collect();
125             (*(*keyboard).group).unref(&keys);
126             (*keyboard).group = std::ptr::null_mut();
127         }
128 
129         if !(*keyboard).config.keymap.is_null() {
130             ffi::xkb_keymap_unref((*keyboard).config.keymap);
131         }
132 
133         let _boxed = Box::from_raw(keyboard);
134     }
135 
136     pub unsafe fn set_group(&mut self) {
137         assert!(self.group.is_null());
138         let seat = (*self.device).seat;
139 
140         if !(*self.device).virtual_device {
141             let seat_groups_head = &mut (*seat).keyboard_groups as *mut ffi::wl_list as *mut crate::server::WlList;
142             let mut curr = (*seat_groups_head).next;
143             while curr != seat_groups_head {
144                 let next = (*curr).next;
145                 let group = crate::container_of!(curr, KeyboardGroup, link);
146                 // A virtual keyboard's group is private to it (its keymap can
147                 // change under it, and its IME grab is disabled) — hardware
148                 // keyboards must never join one.
149                 if (*group).virtual_device {
150                     curr = next;
151                     continue;
152                 }
153                 let config_ptr = &mut self.config as *mut KeyboardConfig;
154                 if (*group).match_config(config_ptr) {
155                     self.group = (*group).ref_group();
156                     let keyboards_head = &mut (*self.group).keyboards as *mut ffi::wl_list as *mut crate::server::WlList;
157                     crate::server::wl_list_insert(keyboards_head, &mut self.group_link as *mut ffi::wl_list as *mut crate::server::WlList);
158                     return;
159                 }
160                 curr = next;
161             }
162         }
163 
164         match KeyboardGroup::create(seat, self.config, (*self.device).virtual_device) {
165             Ok(group_ptr) => {
166                 self.group = group_ptr;
167                 let keyboards_head = &mut (*self.group).keyboards as *mut ffi::wl_list as *mut crate::server::WlList;
168                 crate::server::wl_list_insert(keyboards_head, &mut self.group_link as *mut ffi::wl_list as *mut crate::server::WlList);
169             }
170             Err(err) => {
171                 log::error!("failed to create KeyboardGroup: {}", err);
172             }
173         }
174     }
175 
176     pub unsafe fn set_repeat_info(&mut self, rate: i32, delay: i32) {
177         assert!(!(*self.device).virtual_device);
178         self.config.repeat_rate = rate;
179         self.config.repeat_delay = delay;
180         if !self.group.is_null() {
181             let keys: Vec<u32> = self.pressed.iter().cloned().collect();
182             crate::server::wl_list_remove(&mut self.group_link as *mut ffi::wl_list as *mut crate::server::WlList);
183             (*self.group).unref(&keys);
184             self.group = std::ptr::null_mut();
185         }
186         self.set_group();
187     }
188 
189     pub unsafe fn set_keymap(&mut self, keymap: *mut ffi::xkb_keymap) {
190         assert!(!(*self.device).virtual_device);
191         if should_set_keymap((*(*self.device).seat).server) {
192             ffi::wlr_keyboard_set_keymap(self.wlr_keyboard, keymap);
193         }
194         if !self.config.keymap.is_null() {
195             ffi::xkb_keymap_unref(self.config.keymap);
196         }
197         self.config.keymap = keymap;
198         if !keymap.is_null() {
199             ffi::xkb_keymap_ref(keymap);
200         }
201         if !self.group.is_null() {
202             let keys: Vec<u32> = self.pressed.iter().cloned().collect();
203             crate::server::wl_list_remove(&mut self.group_link as *mut ffi::wl_list as *mut crate::server::WlList);
204             (*self.group).unref(&keys);
205             self.group = std::ptr::null_mut();
206         }
207         self.set_group();
208     }
209 }
210 
211 unsafe extern "C" fn destroy_callback(data: *mut std::ffi::c_void) {
212     Keyboard::destroy(data as *mut Keyboard);
213 }
214 
215 unsafe fn should_set_keymap(server: *mut crate::server::Server) -> bool {
216     let backend = (*server).backend;
217     ffi::wlr_backend_is_wl(backend) || ffi::wlr_backend_is_x11(backend)
218 }
219 
220 pub unsafe fn keysym_is_modifier(sym: u32) -> bool {
221     match sym {
222         ffi::XKB_KEY_Shift_L |
223         ffi::XKB_KEY_Shift_R |
224         ffi::XKB_KEY_Control_L |
225         ffi::XKB_KEY_Control_R |
226         ffi::XKB_KEY_Caps_Lock |
227         ffi::XKB_KEY_Shift_Lock |
228 
229         ffi::XKB_KEY_Meta_L |
230         ffi::XKB_KEY_Meta_R |
231         ffi::XKB_KEY_Alt_L |
232         ffi::XKB_KEY_Alt_R |
233         ffi::XKB_KEY_Super_L |
234         ffi::XKB_KEY_Super_R |
235         ffi::XKB_KEY_Hyper_L |
236         ffi::XKB_KEY_Hyper_R |
237 
238         ffi::XKB_KEY_Num_Lock |
239 
240         ffi::XKB_KEY_ISO_Lock |
241         ffi::XKB_KEY_ISO_Level2_Latch |
242         ffi::XKB_KEY_ISO_Level3_Shift |
243         ffi::XKB_KEY_ISO_Level3_Latch |
244         ffi::XKB_KEY_ISO_Level3_Lock |
245         ffi::XKB_KEY_ISO_Level5_Shift |
246         ffi::XKB_KEY_ISO_Level5_Latch |
247         ffi::XKB_KEY_ISO_Level5_Lock |
248         ffi::XKB_KEY_ISO_Group_Shift |
249         ffi::XKB_KEY_ISO_Group_Latch |
250         ffi::XKB_KEY_ISO_Group_Lock |
251         ffi::XKB_KEY_ISO_Next_Group |
252         ffi::XKB_KEY_ISO_Next_Group_Lock |
253         ffi::XKB_KEY_ISO_Prev_Group |
254         ffi::XKB_KEY_ISO_Prev_Group_Lock |
255         ffi::XKB_KEY_ISO_First_Group |
256         ffi::XKB_KEY_ISO_First_Group_Lock |
257         ffi::XKB_KEY_ISO_Last_Group |
258         ffi::XKB_KEY_ISO_Last_Group_Lock => true,
259         _ => false,
260     }
261 }
262 
263 unsafe extern "C" fn handle_key(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
264     let keyboard = &mut *crate::container_of!(listener, Keyboard, key_listener);
265     let event = data as *mut ffi::wlr_keyboard_key_event;
266 
267     if (*event).state == ffi::wl_keyboard_key_state_WL_KEYBOARD_KEY_STATE_RELEASED {
268         keyboard.pressed.remove(&(*event).keycode);
269     } else if (*event).state == ffi::wl_keyboard_key_state_WL_KEYBOARD_KEY_STATE_PRESSED {
270         if keyboard.pressed.len() < 32 {
271             keyboard.pressed.insert((*event).keycode);
272         }
273         // First deliberate input ends the session-restore settling phase
274         // (see the focus gate in Window::map).
275         if !keyboard.group.is_null() && !(*keyboard.group).seat.is_null() {
276             (*(*(*keyboard.group).seat).server).wm.startup_input_seen = true;
277         }
278     }
279 
280     if !keyboard.group.is_null() {
281         (*keyboard.group).process_key(event);
282     }
283 }
284 
285 /// Virtual keyboards only. The client's keymap request lands after the group
286 /// already exists, and the group's own wlr_keyboard is what processes keys —
287 /// with no keymap it has no xkb_state and every key is dropped. The group is
288 /// private to this keyboard (see set_group), so its config can be rewritten
289 /// in place instead of regrouping.
290 unsafe extern "C" fn handle_keymap(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
291     let keyboard = &mut *crate::container_of!(listener, Keyboard, keymap_listener);
292     let keymap = ffi::river_wlr_keyboard_get_keymap(keyboard.wlr_keyboard);
293 
294     if !keymap.is_null() {
295         ffi::xkb_keymap_ref(keymap);
296     }
297     if !keyboard.config.keymap.is_null() {
298         ffi::xkb_keymap_unref(keyboard.config.keymap);
299     }
300     keyboard.config.keymap = keymap;
301 
302     if !keyboard.group.is_null() {
303         let group = &mut *keyboard.group;
304         if !keymap.is_null() {
305             ffi::xkb_keymap_ref(keymap);
306         }
307         if !group.config.keymap.is_null() {
308             ffi::xkb_keymap_unref(group.config.keymap);
309         }
310         group.config.keymap = keymap;
311         if !keymap.is_null() {
312             group.process_keymap(keymap);
313         }
314     }
315 }
316 
317 unsafe extern "C" fn handle_modifiers(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
318     let keyboard = &mut *crate::container_of!(listener, Keyboard, modifiers_listener);
319 
320     if !keyboard.group.is_null() {
321         let modifiers = ffi::river_wlr_keyboard_get_modifiers(keyboard.wlr_keyboard);
322         (*keyboard.group).process_modifiers(*modifiers);
323         
324         let seat = (*keyboard.group).seat;
325         if !seat.is_null() && !(*seat).server.is_null() {
326             (*(*seat).server).wm.update_status();
327             (*(*seat).server).wm.refresh_adjust_held();
328         }
329     }
330 }