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

src/server/xkb_config.rs (10.4K)

  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 XkbConfig {
  8     pub server: *mut Server,
  9     pub global: *mut ffi::wl_global,
 10     pub context: *mut ffi::xkb_context,
 11     pub default_keymap: *mut ffi::xkb_keymap,
 12     pub objects: ffi::wl_list,
 13     pub keyboards: ffi::wl_list,
 14 }
 15 
 16 pub struct XkbConfigObject {
 17     pub config: *mut XkbConfig,
 18     pub resource: *mut ffi::wl_resource,
 19     pub link: ffi::wl_list,
 20 }
 21 
 22 impl Default for XkbConfig {
 23     fn default() -> Self {
 24         unsafe { std::mem::zeroed() }
 25     }
 26 }
 27 
 28 impl XkbConfig {
 29     pub unsafe fn init(&mut self, server: *mut Server) -> Result<(), &'static str> {
 30         self.server = server;
 31         ffi::wl_list_init(&mut self.objects);
 32         ffi::wl_list_init(&mut self.keyboards);
 33 
 34         let context = ffi::xkb_context_new(ffi::xkb_context_flags_XKB_CONTEXT_NO_FLAGS);
 35         if context.is_null() {
 36             return Err("Failed to create xkb_context");
 37         }
 38         self.context = context;
 39 
 40         let default_keymap = ffi::xkb_keymap_new_from_names(
 41             context,
 42             std::ptr::null(),
 43             ffi::xkb_keymap_compile_flags_XKB_KEYMAP_COMPILE_NO_FLAGS,
 44         );
 45         if default_keymap.is_null() {
 46             ffi::xkb_context_unref(context);
 47             self.context = std::ptr::null_mut();
 48             return Err("Failed to create default xkb_keymap");
 49         }
 50         self.default_keymap = default_keymap;
 51 
 52         self.global = ffi::wl_global_create(
 53             (*server).wl_server,
 54             &ffi::river_xkb_config_v1_interface,
 55             2,
 56             self as *mut XkbConfig as *mut _,
 57             Some(bind_xkb_config),
 58         );
 59         if self.global.is_null() {
 60             ffi::xkb_keymap_unref(self.default_keymap);
 61             ffi::xkb_context_unref(self.context);
 62             self.default_keymap = std::ptr::null_mut();
 63             self.context = std::ptr::null_mut();
 64             return Err("Failed to create river_xkb_config_v1 global");
 65         }
 66 
 67         Ok(())
 68     }
 69 
 70     pub unsafe fn deinit(&mut self) {
 71         if !self.global.is_null() {
 72             ffi::wl_global_destroy(self.global);
 73             self.global = std::ptr::null_mut();
 74         }
 75 
 76         let objects_head = &mut self.objects as *mut ffi::wl_list as *mut WlList;
 77         let mut curr = (*objects_head).next;
 78         while curr != objects_head {
 79             let next = (*curr).next;
 80             let obj = crate::container_of!(curr, XkbConfigObject, link);
 81             ffi::wl_resource_destroy((*obj).resource);
 82             curr = next;
 83         }
 84 
 85         if !self.default_keymap.is_null() {
 86             ffi::xkb_keymap_unref(self.default_keymap);
 87             self.default_keymap = std::ptr::null_mut();
 88         }
 89         if !self.context.is_null() {
 90             ffi::xkb_context_unref(self.context);
 91             self.context = std::ptr::null_mut();
 92         }
 93     }
 94 }
 95 
 96 unsafe extern "C" fn bind_xkb_config(
 97     client: *mut ffi::wl_client,
 98     data: *mut std::ffi::c_void,
 99     version: u32,
100     id: u32,
101 ) {
102     let config = data as *mut XkbConfig;
103     let resource = ffi::wl_resource_create(client, &ffi::river_xkb_config_v1_interface, version as i32, id);
104     if resource.is_null() {
105         ffi::wl_client_post_no_memory(client);
106         return;
107     }
108 
109     let obj = Box::into_raw(Box::new(XkbConfigObject {
110         config,
111         resource,
112         link: std::mem::zeroed(),
113     }));
114 
115     ffi::wl_resource_set_implementation(
116         resource,
117         &XKB_CONFIG_INTERFACE as *const _ as *const _,
118         obj as *mut _,
119         Some(handle_object_destroy),
120     );
121 
122     let list_head = &mut (*config).objects as *mut ffi::wl_list as *mut WlList;
123     wl_list_insert((*list_head).prev, &mut (*obj).link as *mut ffi::wl_list as *mut WlList);
124 
125     let keyboards_head = &mut (*config).keyboards as *mut ffi::wl_list as *mut WlList;
126     let mut curr = (*keyboards_head).next;
127     while curr != keyboards_head {
128         let next = (*curr).next;
129         let xkb_kbd = crate::container_of!(curr, crate::xkb_keyboard::XkbKeyboard, link);
130         (*xkb_kbd).create_object(resource);
131         curr = next;
132     }
133 }
134 
135 unsafe extern "C" fn handle_object_destroy(resource: *mut ffi::wl_resource) {
136     let obj = ffi::wl_resource_get_user_data(resource) as *mut XkbConfigObject;
137     if !obj.is_null() {
138         wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
139         let _ = Box::from_raw(obj);
140     }
141 }
142 
143 static XKB_CONFIG_INTERFACE: ffi::river_xkb_config_v1_interface = ffi::river_xkb_config_v1_interface {
144     stop: Some(xkb_config_stop),
145     destroy: Some(xkb_config_destroy),
146     create_keymap: Some(xkb_config_create_keymap),
147 };
148 
149 static XKB_CONFIG_INERT_INTERFACE: ffi::river_xkb_config_v1_interface = ffi::river_xkb_config_v1_interface {
150     stop: None,
151     destroy: Some(xkb_config_inert_destroy),
152     create_keymap: None,
153 };
154 
155 unsafe extern "C" fn xkb_config_stop(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
156     let obj = ffi::wl_resource_get_user_data(resource) as *mut XkbConfigObject;
157     if obj.is_null() {
158         return;
159     }
160 
161     wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
162     ffi::wl_list_init(&mut (*obj).link);
163 
164     ffi::wl_resource_post_event(resource, 0); // finished event
165 
166     ffi::wl_resource_set_implementation(
167         resource,
168         &XKB_CONFIG_INERT_INTERFACE as *const _ as *const _,
169         obj as *mut _,
170         Some(handle_object_destroy),
171     );
172 }
173 
174 unsafe extern "C" fn xkb_config_destroy(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
175     ffi::wl_resource_post_error(
176         resource,
177         ffi::river_xkb_config_v1_error_RIVER_XKB_CONFIG_V1_ERROR_INVALID_DESTROY,
178         b"destroy before finished event sent\0".as_ptr() as *const _,
179     );
180 }
181 
182 unsafe extern "C" fn xkb_config_inert_destroy(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
183     ffi::wl_resource_destroy(resource);
184 }
185 
186 struct FdGuard(std::os::raw::c_int);
187 impl Drop for FdGuard {
188     fn drop(&mut self) {
189         unsafe {
190             libc::close(self.0);
191         }
192     }
193 }
194 
195 pub struct XkbKeymap {
196     pub xkb_keymap: *mut ffi::xkb_keymap,
197 }
198 
199 static XKB_KEYMAP_INTERFACE: ffi::river_xkb_keymap_v1_interface = ffi::river_xkb_keymap_v1_interface {
200     destroy: Some(xkb_keymap_destroy),
201 };
202 
203 unsafe extern "C" fn xkb_keymap_destroy(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
204     ffi::wl_resource_destroy(resource);
205 }
206 
207 unsafe extern "C" fn xkb_keymap_destroy_func(resource: *mut ffi::wl_resource) {
208     let keymap = ffi::wl_resource_get_user_data(resource) as *mut XkbKeymap;
209     if !keymap.is_null() {
210         ffi::xkb_keymap_unref((*keymap).xkb_keymap);
211         let _ = Box::from_raw(keymap);
212     }
213 }
214 
215 unsafe extern "C" fn xkb_config_create_keymap(
216     client: *mut ffi::wl_client,
217     resource: *mut ffi::wl_resource,
218     id: u32,
219     fd: i32,
220     format: u32,
221 ) {
222     let _fd_guard = FdGuard(fd);
223     let obj = ffi::wl_resource_get_user_data(resource) as *mut XkbConfigObject;
224     if obj.is_null() {
225         return;
226     }
227     let config = (*obj).config;
228 
229     let keymap_res = ffi::wl_resource_create(
230         client,
231         &ffi::river_xkb_keymap_v1_interface,
232         ffi::wl_resource_get_version(resource),
233         id,
234     );
235     if keymap_res.is_null() {
236         ffi::wl_client_post_no_memory(client);
237         return;
238     }
239 
240     if format != ffi::river_xkb_config_v1_keymap_format_RIVER_XKB_CONFIG_V1_KEYMAP_FORMAT_TEXT_V1 {
241         ffi::wl_resource_post_error(
242             resource,
243             ffi::river_xkb_config_v1_error_RIVER_XKB_CONFIG_V1_ERROR_INVALID_FORMAT,
244             b"invalid format enum value\0".as_ptr() as *const _,
245         );
246         return;
247     }
248 
249     let mut statbuf = std::mem::MaybeUninit::<libc::stat>::uninit();
250     if libc::fstat(fd, statbuf.as_mut_ptr()) != 0 {
251         ffi::wl_resource_set_implementation(
252             keymap_res,
253             &XKB_KEYMAP_INTERFACE as *const _ as *const _,
254             std::ptr::null_mut(),
255             Some(xkb_keymap_destroy_func),
256         );
257         ffi::wl_resource_post_event(keymap_res, 1, b"failed to stat keymap fd\0".as_ptr() as *const _);
258         return;
259     }
260     let stat = statbuf.assume_init();
261     if stat.st_size < 1 {
262         ffi::wl_resource_set_implementation(
263             keymap_res,
264             &XKB_KEYMAP_INTERFACE as *const _ as *const _,
265             std::ptr::null_mut(),
266             Some(xkb_keymap_destroy_func),
267         );
268         ffi::wl_resource_post_event(keymap_res, 1, b"keymap too small\0".as_ptr() as *const _);
269         return;
270     }
271     if stat.st_size > 1024 * 1024 {
272         ffi::wl_resource_set_implementation(
273             keymap_res,
274             &XKB_KEYMAP_INTERFACE as *const _ as *const _,
275             std::ptr::null_mut(),
276             Some(xkb_keymap_destroy_func),
277         );
278         ffi::wl_resource_post_event(keymap_res, 1, b"keymap too large\0".as_ptr() as *const _);
279         return;
280     }
281 
282     let keymap_len = stat.st_size as usize;
283     let map = libc::mmap(
284         std::ptr::null_mut(),
285         keymap_len,
286         libc::PROT_READ,
287         libc::MAP_PRIVATE,
288         fd,
289         0,
290     );
291     if map == libc::MAP_FAILED {
292         ffi::wl_resource_set_implementation(
293             keymap_res,
294             &XKB_KEYMAP_INTERFACE as *const _ as *const _,
295             std::ptr::null_mut(),
296             Some(xkb_keymap_destroy_func),
297         );
298         ffi::wl_resource_post_event(keymap_res, 1, b"failed to mmap() keymap fd\0".as_ptr() as *const _);
299         return;
300     }
301 
302     let context = (*config).context;
303     let xkb_keymap = ffi::xkb_keymap_new_from_buffer(
304         context,
305         map as *const _,
306         keymap_len - 1,
307         ffi::xkb_keymap_format_XKB_KEYMAP_FORMAT_TEXT_V1,
308         ffi::xkb_keymap_compile_flags_XKB_KEYMAP_COMPILE_NO_FLAGS,
309     );
310 
311     libc::munmap(map, keymap_len);
312 
313     if xkb_keymap.is_null() {
314         ffi::wl_resource_set_implementation(
315             keymap_res,
316             &XKB_KEYMAP_INTERFACE as *const _ as *const _,
317             std::ptr::null_mut(),
318             Some(xkb_keymap_destroy_func),
319         );
320         ffi::wl_resource_post_event(keymap_res, 1, b"failed to parse xkb keymap\0".as_ptr() as *const _);
321         return;
322     }
323 
324     let xkb_keymap_struct = Box::into_raw(Box::new(XkbKeymap {
325         xkb_keymap,
326     }));
327 
328     ffi::wl_resource_set_implementation(
329         keymap_res,
330         &XKB_KEYMAP_INTERFACE as *const _ as *const _,
331         xkb_keymap_struct as *mut _,
332         Some(xkb_keymap_destroy_func),
333     );
334 
335     ffi::wl_resource_post_event(keymap_res, 0); // success event
336 }