Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
src/server/libinput_config.rs (5.3K)
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, WlList, wl_list_insert, wl_list_remove};
6
7 pub struct LibinputConfig {
8 pub server: *mut Server,
9 pub global: *mut ffi::wl_global,
10 pub objects: ffi::wl_list,
11 pub devices: ffi::wl_list,
12 }
13
14 pub struct LibinputConfigObject {
15 pub config: *mut LibinputConfig,
16 pub resource: *mut ffi::wl_resource,
17 pub link: ffi::wl_list,
18 }
19
20 impl Default for LibinputConfig {
21 fn default() -> Self {
22 unsafe { std::mem::zeroed() }
23 }
24 }
25
26 impl LibinputConfig {
27 pub unsafe fn init(&mut self, server: *mut Server) -> Result<(), &'static str> {
28 self.server = server;
29 ffi::wl_list_init(&mut self.objects);
30 ffi::wl_list_init(&mut self.devices);
31
32 self.global = ffi::wl_global_create(
33 (*server).wl_server,
34 &ffi::river_libinput_config_v1_interface,
35 2,
36 self as *mut LibinputConfig as *mut _,
37 Some(bind_libinput_config),
38 );
39 if self.global.is_null() {
40 return Err("Failed to create river_libinput_config_v1 global");
41 }
42
43 Ok(())
44 }
45
46 pub unsafe fn deinit(&mut self) {
47 if !self.global.is_null() {
48 ffi::wl_global_destroy(self.global);
49 self.global = std::ptr::null_mut();
50 }
51
52 let objects_head = &mut self.objects as *mut ffi::wl_list as *mut WlList;
53 let mut curr = (*objects_head).next;
54 while curr != objects_head {
55 let next = (*curr).next;
56 let obj = crate::container_of!(curr, LibinputConfigObject, link);
57 ffi::wl_resource_destroy((*obj).resource);
58 curr = next;
59 }
60 }
61 }
62
63 unsafe extern "C" fn bind_libinput_config(
64 client: *mut ffi::wl_client,
65 data: *mut std::ffi::c_void,
66 version: u32,
67 id: u32,
68 ) {
69 let config = data as *mut LibinputConfig;
70 let resource = ffi::wl_resource_create(client, &ffi::river_libinput_config_v1_interface, version as i32, id);
71 if resource.is_null() {
72 ffi::wl_client_post_no_memory(client);
73 return;
74 }
75
76 let obj = Box::into_raw(Box::new(LibinputConfigObject {
77 config,
78 resource,
79 link: std::mem::zeroed(),
80 }));
81
82 ffi::wl_resource_set_implementation(
83 resource,
84 &LIBINPUT_CONFIG_INTERFACE as *const _ as *const _,
85 obj as *mut _,
86 Some(handle_object_destroy),
87 );
88
89 let list_head = &mut (*config).objects as *mut ffi::wl_list as *mut WlList;
90 wl_list_insert((*list_head).prev, &mut (*obj).link as *mut ffi::wl_list as *mut WlList);
91
92 let devices_head = &mut (*config).devices as *mut ffi::wl_list as *mut WlList;
93 let mut curr = (*devices_head).next;
94 while curr != devices_head {
95 let next = (*curr).next;
96 let dev = crate::container_of!(curr, crate::libinput_device::LibinputDevice, link);
97 (*dev).create_object(resource);
98 curr = next;
99 }
100 }
101
102 unsafe extern "C" fn handle_object_destroy(resource: *mut ffi::wl_resource) {
103 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputConfigObject;
104 if !obj.is_null() {
105 wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
106 let _ = Box::from_raw(obj);
107 }
108 }
109
110 static LIBINPUT_CONFIG_INTERFACE: ffi::river_libinput_config_v1_interface = ffi::river_libinput_config_v1_interface {
111 stop: Some(libinput_config_stop),
112 destroy: Some(libinput_config_destroy),
113 create_accel_config: Some(libinput_config_create_accel_config),
114 };
115
116 static LIBINPUT_CONFIG_INERT_INTERFACE: ffi::river_libinput_config_v1_interface = ffi::river_libinput_config_v1_interface {
117 stop: None,
118 destroy: Some(libinput_config_inert_destroy),
119 create_accel_config: None,
120 };
121
122 unsafe extern "C" fn libinput_config_stop(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
123 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputConfigObject;
124 if obj.is_null() {
125 return;
126 }
127
128 wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
129 ffi::wl_list_init(&mut (*obj).link);
130
131 ffi::wl_resource_post_event(resource, 0); // finished event
132
133 ffi::wl_resource_set_implementation(
134 resource,
135 &LIBINPUT_CONFIG_INERT_INTERFACE as *const _ as *const _,
136 obj as *mut _,
137 Some(handle_object_destroy),
138 );
139 }
140
141 unsafe extern "C" fn libinput_config_destroy(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
142 ffi::wl_resource_post_error(
143 resource,
144 ffi::river_libinput_config_v1_error_RIVER_LIBINPUT_CONFIG_V1_ERROR_INVALID_DESTROY,
145 b"destroy before finished event sent\0".as_ptr() as *const _,
146 );
147 }
148
149 unsafe extern "C" fn libinput_config_inert_destroy(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
150 ffi::wl_resource_destroy(resource);
151 }
152
153 unsafe extern "C" fn libinput_config_create_accel_config(
154 client: *mut ffi::wl_client,
155 resource: *mut ffi::wl_resource,
156 id: u32,
157 profile: u32,
158 ) {
159 if let Err(e) = crate::libinput_accel_config::LibinputAccelConfig::create(
160 client,
161 ffi::wl_resource_get_version(resource) as u32,
162 id,
163 profile,
164 ) {
165 log::error!("Failed to create LibinputAccelConfig: {}", e);
166 ffi::wl_resource_post_no_memory(resource);
167 }
168 }