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

src/server/input_manager.rs (18.9K)

  1 // SPDX-FileCopyrightText: © 2026 The River Developers
  2 // SPDX-License-Identifier: GPL-3.0-only
  3 
  4 use crate::ffi;
  5 use crate::server::{Server, wl_signal_add, WlListener, wl_listener_remove, WlList, wl_list_insert, wl_list_remove};
  6 use crate::seat::Seat;
  7 
  8 pub struct InputManager {
  9     pub server: *mut Server,
 10     pub default_seat: *mut Seat,
 11     pub seats: ffi::wl_list,
 12     pub devices: ffi::wl_list, // list of InputDevice
 13     pub objects: ffi::wl_list, // list of InputManagerObject
 14     pub global: *mut ffi::wl_global,
 15     
 16     pub idle_notifier: *mut ffi::wlr_idle_notifier_v1,
 17     pub relative_pointer_manager: *mut ffi::wlr_relative_pointer_manager_v1,
 18     pub pointer_gestures: *mut ffi::wlr_pointer_gestures_v1,
 19     pub virtual_pointer_manager: *mut ffi::wlr_virtual_pointer_manager_v1,
 20     pub virtual_keyboard_manager: *mut ffi::wlr_virtual_keyboard_manager_v1,
 21     pub pointer_constraints: *mut ffi::wlr_pointer_constraints_v1,
 22     pub input_method_manager: *mut ffi::wlr_input_method_manager_v2,
 23     pub text_input_manager: *mut ffi::wlr_text_input_manager_v3,
 24     pub tablet_manager: *mut ffi::wlr_tablet_manager_v2,
 25 
 26     pub new_input_listener: ffi::wl_listener,
 27     pub new_text_input: ffi::wl_listener,
 28     pub new_input_method: ffi::wl_listener,
 29     pub new_virtual_pointer_listener: ffi::wl_listener,
 30     pub new_virtual_keyboard_listener: ffi::wl_listener,
 31 
 32     /// Pending deferred pointer-focus re-evaluation (see
 33     /// [`InputManager::schedule_pointer_refresh`]); null when none. One idle
 34     /// source coalesces every scene-mapping change of a dispatch.
 35     pub pointer_refresh_idle: *mut ffi::wl_event_source,
 36 }
 37 
 38 pub struct InputManagerObject {
 39     pub manager: *mut InputManager,
 40     pub resource: *mut ffi::wl_resource,
 41     pub link: ffi::wl_list,
 42 }
 43 
 44 impl Default for InputManager {
 45     fn default() -> Self {
 46         unsafe { std::mem::zeroed() }
 47     }
 48 }
 49 
 50 impl InputManager {
 51     pub unsafe fn init(&mut self, server: *mut Server) -> Result<(), &'static str> {
 52         self.server = server;
 53         let wl_server = (*server).wl_server;
 54 
 55         ffi::wl_list_init(&mut self.seats);
 56         ffi::wl_list_init(&mut self.devices);
 57         ffi::wl_list_init(&mut self.objects);
 58 
 59         self.idle_notifier = ffi::wlr_idle_notifier_v1_create(wl_server);
 60         self.relative_pointer_manager = ffi::wlr_relative_pointer_manager_v1_create(wl_server);
 61         self.pointer_gestures = ffi::wlr_pointer_gestures_v1_create(wl_server);
 62         self.virtual_pointer_manager = ffi::wlr_virtual_pointer_manager_v1_create(wl_server);
 63         self.virtual_keyboard_manager = ffi::wlr_virtual_keyboard_manager_v1_create(wl_server);
 64         self.pointer_constraints = ffi::wlr_pointer_constraints_v1_create(wl_server);
 65         self.input_method_manager = ffi::wlr_input_method_manager_v2_create(wl_server);
 66         self.text_input_manager = ffi::wlr_text_input_manager_v3_create(wl_server);
 67         self.tablet_manager = ffi::wlr_tablet_v2_create(wl_server);
 68 
 69         // Create default seat
 70         self.default_seat = Seat::create(server, "default")?;
 71 
 72         if !(*server).xwayland.is_null() {
 73             ffi::wlr_xwayland_set_seat((*server).xwayland, (*self.default_seat).wlr_seat);
 74         }
 75 
 76         self.global = ffi::wl_global_create(
 77             wl_server,
 78             &ffi::river_input_manager_v1_interface,
 79             2,
 80             self as *mut InputManager as *mut _,
 81             Some(bind_input_manager),
 82         );
 83         if self.global.is_null() {
 84             return Err("Failed to create river_input_manager_v1 global");
 85         }
 86 
 87         let new_input_ptr = &mut self.new_input_listener as *mut ffi::wl_listener as *mut WlListener;
 88         (*new_input_ptr).notify = Some(handle_new_input);
 89         
 90         let signal = ffi::river_wlr_backend_get_new_input_signal((*server).backend);
 91         wl_signal_add(signal, &mut self.new_input_listener);
 92 
 93         // Connect new_text_input listener
 94         let new_text_input_ptr = &mut self.new_text_input as *mut ffi::wl_listener as *mut WlListener;
 95         (*new_text_input_ptr).notify = Some(handle_new_text_input);
 96         wl_signal_add(&mut (*self.text_input_manager).events.new_text_input, &mut self.new_text_input);
 97 
 98         // Connect new_input_method listener
 99         let new_input_method_ptr = &mut self.new_input_method as *mut ffi::wl_listener as *mut WlListener;
100         (*new_input_method_ptr).notify = Some(handle_new_input_method);
101         wl_signal_add(&mut (*self.input_method_manager).events.new_input_method, &mut self.new_input_method);
102 
103         // Connect new_virtual_pointer listener
104         let new_virtual_pointer_ptr = &mut self.new_virtual_pointer_listener as *mut ffi::wl_listener as *mut WlListener;
105         (*new_virtual_pointer_ptr).notify = Some(handle_new_virtual_pointer);
106         wl_signal_add(&mut (*self.virtual_pointer_manager).events.new_virtual_pointer, &mut self.new_virtual_pointer_listener);
107 
108         // Connect new_virtual_keyboard listener
109         let new_virtual_keyboard_ptr = &mut self.new_virtual_keyboard_listener as *mut ffi::wl_listener as *mut WlListener;
110         (*new_virtual_keyboard_ptr).notify = Some(handle_new_virtual_keyboard);
111         wl_signal_add(&mut (*self.virtual_keyboard_manager).events.new_virtual_keyboard, &mut self.new_virtual_keyboard_listener);
112 
113         Ok(())
114     }
115 
116     /// Schedule a pointer-focus re-evaluation for every seat, deferred to an
117     /// idle callback and coalesced (one source no matter how many commits
118     /// land in a dispatch). Used when a commit changes the surface↔frame
119     /// mapping under a stationary cursor — geometry re-anchor or surface
120     /// extent change. Deferred, NOT inline: wlroots' own scene commit
121     /// listeners re-anchor the surface tree AFTER the compositor's toplevel
122     /// commit handler runs, so an inline refresh would query the stale
123     /// mapping.
124     pub unsafe fn schedule_pointer_refresh(&mut self) {
125         if !self.pointer_refresh_idle.is_null() {
126             return;
127         }
128         let event_loop = ffi::wl_display_get_event_loop((*self.server).wl_server);
129         let idle = ffi::wl_event_loop_add_idle(
130             event_loop,
131             Some(handle_pointer_refresh_idle),
132             self as *mut InputManager as *mut _,
133         );
134         if idle.is_null() {
135             log::error!("Failed to schedule pointer-refresh idle source");
136             return;
137         }
138         self.pointer_refresh_idle = idle;
139     }
140 
141     pub unsafe fn deinit(&mut self) {
142         log::info!("[deinit] InputManager::deinit started");
143         if !self.pointer_refresh_idle.is_null() {
144             ffi::wl_event_source_remove(self.pointer_refresh_idle);
145             self.pointer_refresh_idle = std::ptr::null_mut();
146         }
147         if !self.global.is_null() {
148             log::info!("[deinit] destroying input manager global");
149             ffi::wl_global_destroy(self.global);
150             self.global = std::ptr::null_mut();
151         }
152 
153         let objects_head = &mut self.objects as *mut ffi::wl_list as *mut WlList;
154         let mut curr = (*objects_head).next;
155         while curr != objects_head {
156             let next = (*curr).next;
157             let obj = crate::container_of!(curr, InputManagerObject, link);
158             ffi::wl_resource_destroy((*obj).resource);
159             curr = next;
160         }
161 
162         // Detach all devices from their seats and set their seat pointer to null
163         // so they do not attempt to access a freed seat during backend destruction.
164         log::info!("[deinit] detaching devices");
165         let devices_head = &mut self.devices as *mut ffi::wl_list as *mut WlList;
166         let mut curr_dev = (*devices_head).next;
167         while curr_dev != devices_head {
168             let next_dev = (*curr_dev).next;
169             let device = crate::container_of!(curr_dev, crate::input_device::InputDevice, link);
170             if !(*device).seat.is_null() {
171                 log::info!("[deinit] detaching device from seat");
172                 (*(*device).seat).detach_device(device);
173                 (*device).seat = std::ptr::null_mut();
174             }
175             curr_dev = next_dev;
176         }
177 
178         // Destroy all seats
179         log::info!("[deinit] destroying seats");
180         let seats_head = &mut self.seats as *mut ffi::wl_list as *mut WlList;
181         let mut curr_seat = (*seats_head).next;
182         while curr_seat != seats_head {
183             let next_seat = (*curr_seat).next;
184             let seat = crate::container_of!(curr_seat, Seat, link);
185             log::info!("[deinit] calling Seat::destroy for {:?}", (*seat).wlr_seat);
186             Seat::destroy(seat);
187             curr_seat = next_seat;
188         }
189         self.default_seat = std::ptr::null_mut();
190         log::info!("[deinit] seats destroyed");
191 
192         log::info!("[deinit] removing input manager listeners");
193         wl_listener_remove(&mut self.new_input_listener);
194         wl_listener_remove(&mut self.new_text_input);
195         wl_listener_remove(&mut self.new_input_method);
196         wl_listener_remove(&mut self.new_virtual_pointer_listener);
197         wl_listener_remove(&mut self.new_virtual_keyboard_listener);
198         log::info!("[deinit] InputManager::deinit finished");
199     }
200 }
201 
202 unsafe extern "C" fn bind_input_manager(
203     client: *mut ffi::wl_client,
204     data: *mut std::ffi::c_void,
205     version: u32,
206     id: u32,
207 ) {
208     let im = data as *mut InputManager;
209     let resource = ffi::wl_resource_create(client, &ffi::river_input_manager_v1_interface, version as i32, id);
210     if resource.is_null() {
211         ffi::wl_client_post_no_memory(client);
212         return;
213     }
214 
215     let obj = Box::into_raw(Box::new(InputManagerObject {
216         manager: im,
217         resource,
218         link: std::mem::zeroed(),
219     }));
220 
221     ffi::wl_resource_set_implementation(
222         resource,
223         &INPUT_MANAGER_INTERFACE as *const _ as *const _,
224         obj as *mut _,
225         Some(handle_manager_object_destroy),
226     );
227 
228     let list_head = &mut (*im).objects as *mut ffi::wl_list as *mut WlList;
229     wl_list_insert((*list_head).prev, &mut (*obj).link as *mut ffi::wl_list as *mut WlList);
230 
231     // Send existing devices to the client
232     let devices_head = &mut (*im).devices as *mut ffi::wl_list as *mut WlList;
233     let mut curr = (*devices_head).next;
234     while curr != devices_head {
235         let next = (*curr).next;
236         let device = crate::container_of!(curr, crate::input_device::InputDevice, link);
237         if !(*device).virtual_device {
238             (*device).create_object(resource);
239         }
240         curr = next;
241     }
242 }
243 
244 unsafe extern "C" fn handle_manager_object_destroy(resource: *mut ffi::wl_resource) {
245     let obj = ffi::wl_resource_get_user_data(resource) as *mut InputManagerObject;
246     if !obj.is_null() {
247         wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
248         let _ = Box::from_raw(obj);
249     }
250 }
251 
252 static INPUT_MANAGER_INTERFACE: ffi::river_input_manager_v1_interface = ffi::river_input_manager_v1_interface {
253     stop: Some(input_manager_stop),
254     destroy: Some(input_manager_destroy),
255     create_seat: Some(input_manager_create_seat),
256     destroy_seat: Some(input_manager_destroy_seat),
257 };
258 
259 static INPUT_MANAGER_INERT_INTERFACE: ffi::river_input_manager_v1_interface = ffi::river_input_manager_v1_interface {
260     stop: None,
261     destroy: Some(input_manager_inert_destroy),
262     create_seat: None,
263     destroy_seat: None,
264 };
265 
266 unsafe extern "C" fn input_manager_stop(
267     _client: *mut ffi::wl_client,
268     resource: *mut ffi::wl_resource,
269 ) {
270     let obj = ffi::wl_resource_get_user_data(resource) as *mut InputManagerObject;
271     if obj.is_null() {
272         return;
273     }
274 
275     wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
276     ffi::wl_list_init(&mut (*obj).link);
277 
278     ffi::wl_resource_post_event(resource, 0); // finished event
279 
280     ffi::wl_resource_set_implementation(
281         resource,
282         &INPUT_MANAGER_INERT_INTERFACE as *const _ as *const _,
283         obj as *mut _,
284         Some(handle_manager_object_destroy),
285     );
286 }
287 
288 unsafe extern "C" fn input_manager_destroy(
289     _client: *mut ffi::wl_client,
290     resource: *mut ffi::wl_resource,
291 ) {
292     ffi::wl_resource_post_error(
293         resource,
294         ffi::river_input_manager_v1_error_RIVER_INPUT_MANAGER_V1_ERROR_INVALID_DESTROY,
295         b"destroy before finished event sent\0".as_ptr() as *const _,
296     );
297 }
298 
299 unsafe extern "C" fn input_manager_inert_destroy(
300     _client: *mut ffi::wl_client,
301     resource: *mut ffi::wl_resource,
302 ) {
303     ffi::wl_resource_destroy(resource);
304 }
305 
306 unsafe extern "C" fn input_manager_create_seat(
307     _client: *mut ffi::wl_client,
308     resource: *mut ffi::wl_resource,
309     name: *const std::os::raw::c_char,
310 ) {
311     let obj = ffi::wl_resource_get_user_data(resource) as *mut InputManagerObject;
312     if obj.is_null() || name.is_null() {
313         return;
314     }
315     let im = (*obj).manager;
316     let name_str = std::ffi::CStr::from_ptr(name).to_string_lossy();
317 
318     // Check if seat already exists
319     let seats_head = &mut (*im).seats as *mut ffi::wl_list as *mut WlList;
320     let mut curr = (*seats_head).next;
321     let mut exists = false;
322     while curr != seats_head {
323         let next = (*curr).next;
324         let seat = crate::container_of!(curr, Seat, link);
325         let seat_name = std::ffi::CStr::from_ptr(ffi::river_wlr_seat_get_name((*seat).wlr_seat)).to_string_lossy();
326         if seat_name == name_str {
327             exists = true;
328             break;
329         }
330         curr = next;
331     }
332 
333     if !exists {
334         let server = (*im).server;
335         if let Err(e) = Seat::create(server, &name_str) {
336             log::error!("failed to create seat '{}': {}", name_str, e);
337             ffi::wl_resource_post_no_memory(resource);
338         }
339     }
340 }
341 
342 unsafe extern "C" fn input_manager_destroy_seat(
343     _client: *mut ffi::wl_client,
344     resource: *mut ffi::wl_resource,
345     name: *const std::os::raw::c_char,
346 ) {
347     let obj = ffi::wl_resource_get_user_data(resource) as *mut InputManagerObject;
348     if obj.is_null() || name.is_null() {
349         return;
350     }
351     let im = (*obj).manager;
352     let name_str = std::ffi::CStr::from_ptr(name).to_string_lossy();
353 
354     if name_str == "default" {
355         return;
356     }
357 
358     let seats_head = &mut (*im).seats as *mut ffi::wl_list as *mut WlList;
359     let mut curr = (*seats_head).next;
360     // Skip default seat (which is the first seat in the list)
361     curr = (*curr).next;
362     while curr != seats_head {
363         let next = (*curr).next;
364         let seat = crate::container_of!(curr, Seat, link);
365         let seat_name = std::ffi::CStr::from_ptr(ffi::river_wlr_seat_get_name((*seat).wlr_seat)).to_string_lossy();
366         if seat_name == name_str {
367             (*seat).destroying = true;
368             (*(*im).server).wm.dirty_windowing();
369             break;
370         }
371         curr = next;
372     }
373 }
374 
375 unsafe extern "C" fn handle_new_input(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
376     let im = &mut *crate::container_of!(listener, InputManager, new_input_listener);
377     let wlr_device = data as *mut ffi::wlr_input_device;
378 
379     log::info!("new input device connected");
380 
381     let device = crate::input_device::InputDevice::new(im.default_seat, wlr_device, false);
382     
383     let name_ptr = ffi::river_wlr_input_device_get_name(wlr_device);
384     if !name_ptr.is_null() {
385         let name = std::ffi::CStr::from_ptr(name_ptr).to_string_lossy();
386         let wm = &mut (*im.server).wm;
387         for rule in &wm.input_rules {
388             if rule.name == "*" || name.contains(&rule.name) {
389                 if let Some(factor) = rule.scroll_factor {
390                     (*device).config.scroll_factor = factor;
391                 }
392             }
393         }
394     }
395     
396     let dev_type = ffi::river_wlr_input_device_get_type(wlr_device);
397     if dev_type == ffi::wlr_input_device_type_WLR_INPUT_DEVICE_KEYBOARD {
398         crate::keyboard::Keyboard::create(device);
399     } else if dev_type == ffi::wlr_input_device_type_WLR_INPUT_DEVICE_TABLET {
400         let _ = crate::tablet::Tablet::create(im.default_seat, wlr_device, false);
401     }
402 
403     // Attach device to the default seat
404     (*im.default_seat).attach_device(device);
405 }
406 
407 unsafe extern "C" fn handle_new_text_input(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
408     let _im = crate::container_of!(listener, InputManager, new_text_input);
409     let wlr_text_input = data as *mut ffi::wlr_text_input_v3;
410 
411     if let Err(e) = crate::text_input::TextInput::create(wlr_text_input) {
412         log::error!("failed to create text input: {}", e);
413         ffi::wl_resource_post_no_memory((*wlr_text_input).resource);
414     }
415 }
416 
417 unsafe extern "C" fn handle_new_input_method(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
418     let im = &mut *crate::container_of!(listener, InputManager, new_input_method);
419     let input_method = data as *mut ffi::wlr_input_method_v2;
420     let seat = ffi::river_wlr_seat_get_data((*input_method).seat) as *mut Seat;
421     if !seat.is_null() {
422         (*seat).relay.new_input_method(input_method);
423     } else {
424         // Fallback to default seat if input_method.seat has no data set yet
425         (*im.default_seat).relay.new_input_method(input_method);
426     }
427 }
428 
429 unsafe extern "C" fn handle_new_virtual_pointer(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
430     let im = &mut *crate::container_of!(listener, InputManager, new_virtual_pointer_listener);
431     let event = data as *mut ffi::wlr_virtual_pointer_v1_new_pointer_event;
432 
433     log::info!("new virtual pointer device connected");
434 
435     let virtual_pointer = (*event).new_pointer;
436     let wlr_device = &mut (*virtual_pointer).pointer.base as *mut ffi::wlr_input_device;
437 
438     let device = crate::input_device::InputDevice::new(im.default_seat, wlr_device, true);
439 
440     // Attach device to the default seat
441     (*im.default_seat).attach_device(device);
442 }
443 
444 unsafe extern "C" fn handle_new_virtual_keyboard(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
445     let im = &mut *crate::container_of!(listener, InputManager, new_virtual_keyboard_listener);
446     let virtual_keyboard = data as *mut ffi::wlr_virtual_keyboard_v1;
447 
448     log::info!("new virtual keyboard device connected");
449 
450     let wlr_device = &mut (*virtual_keyboard).keyboard.base as *mut ffi::wlr_input_device;
451 
452     // Honor the seat the client bound the virtual keyboard to, like input
453     // methods do; fall back to the default seat if it carries no Seat data.
454     let mut seat = ffi::river_wlr_seat_get_data((*virtual_keyboard).seat) as *mut Seat;
455     if seat.is_null() {
456         seat = im.default_seat;
457     }
458 
459     let device = crate::input_device::InputDevice::new(seat, wlr_device, true);
460     crate::keyboard::Keyboard::create(device);
461 
462     // Same path a hardware keyboard takes: attach_device puts the keyboard in
463     // a (private, virtual) KeyboardGroup, so compositor keybindings apply.
464     (*seat).attach_device(device);
465 }
466 
467 unsafe extern "C" fn handle_pointer_refresh_idle(data: *mut std::ffi::c_void) {
468     let manager = data as *mut InputManager;
469     (*manager).pointer_refresh_idle = std::ptr::null_mut();
470     let seats_head = &mut (*manager).seats as *mut ffi::wl_list as *mut WlList;
471     let mut curr = (*seats_head).next;
472     while curr != seats_head {
473         let next = (*curr).next;
474         let seat = crate::container_of!(curr, Seat, link);
475         (*seat).cursor.refresh_after_scene_change();
476         curr = next;
477     }
478 }