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