Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
src/server/libinput_device.rs (46.4K)
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::WlList;
7
8 pub struct LibinputDevice {
9 pub parent_device: *mut InputDevice,
10 pub libinput: *mut ffi::libinput_device,
11 pub objects: ffi::wl_list, // list of LibinputDeviceObject
12 pub link: ffi::wl_list, // link inside LibinputConfig::devices
13 }
14
15 pub struct LibinputDeviceObject {
16 pub device: *mut LibinputDevice,
17 pub resource: *mut ffi::wl_resource,
18 pub link: ffi::wl_list,
19 }
20
21 impl LibinputDevice {
22 pub unsafe fn init(
23 parent_device: *mut InputDevice,
24 handle: *mut ffi::libinput_device,
25 ) -> Box<Self> {
26 let mut dev = Box::new(Self {
27 parent_device,
28 libinput: handle,
29 objects: std::mem::zeroed(),
30 link: std::mem::zeroed(),
31 });
32
33 ffi::wl_list_init(&mut dev.objects);
34 ffi::wl_list_init(&mut dev.link);
35
36 let server = (*(*parent_device).seat).server;
37 let devices_head = &mut (*server).libinput_config.devices as *mut ffi::wl_list as *mut WlList;
38 crate::server::wl_list_insert((*devices_head).prev, &mut dev.link as *mut ffi::wl_list as *mut WlList);
39
40 let config_objects = &mut (*server).libinput_config.objects as *mut ffi::wl_list as *mut WlList;
41 let mut curr = (*config_objects).next;
42 while curr != config_objects {
43 let next = (*curr).next;
44 let config_obj = crate::container_of!(curr, crate::libinput_config::LibinputConfigObject, link);
45 dev.create_object((*config_obj).resource);
46 curr = next;
47 }
48
49 dev
50 }
51
52 pub unsafe fn apply_config(&self, config: &crate::config::InputConfig) {
53 let handle = self.libinput;
54 if handle.is_null() {
55 return;
56 }
57
58 // Tap to click
59 if let Some(tap) = config.touchpad.as_ref().and_then(|t| t.tap_to_click) {
60 if ffi::libinput_device_config_tap_get_finger_count(handle) > 0 {
61 let state = if tap { 1 } else { 0 };
62 ffi::libinput_device_config_tap_set_enabled(handle, state);
63 }
64 }
65
66 // Natural scroll
67 if let Some(natural) = config.touchpad.as_ref().and_then(|t| t.natural_scroll) {
68 if ffi::libinput_device_config_scroll_has_natural_scroll(handle) != 0 {
69 let state = if natural { 1 } else { 0 };
70 ffi::libinput_device_config_scroll_set_natural_scroll_enabled(handle, state);
71 }
72 }
73
74 // Dwt (Disable while typing)
75 if let Some(dwt) = config.touchpad.as_ref().and_then(|t| t.dwt) {
76 if ffi::libinput_device_config_dwt_is_available(handle) != 0 {
77 let state = if dwt { 1 } else { 0 };
78 ffi::libinput_device_config_dwt_set_enabled(handle, state);
79 }
80 }
81
82 // Dwtp (Disable while trackpointing)
83 if let Some(dwtp) = config.touchpad.as_ref().and_then(|t| t.dwtp) {
84 if ffi::libinput_device_config_dwtp_is_available(handle) != 0 {
85 let state = if dwtp { 1 } else { 0 };
86 ffi::libinput_device_config_dwtp_set_enabled(handle, state);
87 }
88 }
89
90 // Device class: trackpoint by name, touchpad by tap capability,
91 // mouse otherwise. Per-class config blocks override the top-level
92 // values for devices of that class.
93 let name_ptr = ffi::river_wlr_input_device_get_name((*self.parent_device).wlr_device);
94 let name = if !name_ptr.is_null() {
95 std::ffi::CStr::from_ptr(name_ptr).to_string_lossy().to_lowercase()
96 } else {
97 String::new()
98 };
99 let is_trackpoint = name.contains("trackpoint");
100 let is_touchpad = !is_trackpoint && ffi::libinput_device_config_tap_get_finger_count(handle) > 0;
101
102 // Acceleration Speed
103 let class_speed = if is_trackpoint {
104 config.trackpoint.as_ref().and_then(|t| t.accel_speed)
105 } else if is_touchpad {
106 config.touchpad.as_ref().and_then(|t| t.accel_speed)
107 } else {
108 config.mouse.as_ref().and_then(|m| m.accel_speed)
109 };
110 if let Some(s) = class_speed.or(config.accel_speed) {
111 if ffi::libinput_device_config_accel_get_profiles(handle) != 0 {
112 ffi::libinput_device_config_accel_set_speed(handle, s);
113 }
114 }
115
116 // Scroll factor (cce's own wheel-delta scaling, not libinput):
117 // class value -> top-level value. Name-based `device` rules run
118 // after this and stay the most specific override.
119 let class_scroll = if is_trackpoint {
120 config.trackpoint.as_ref().and_then(|t| t.scroll_factor)
121 } else if is_touchpad {
122 config.touchpad.as_ref().and_then(|t| t.scroll_factor)
123 } else {
124 config.mouse.as_ref().and_then(|m| m.scroll_factor)
125 };
126 if let Some(f) = class_scroll.or(config.scroll_factor) {
127 if f.is_finite() && f > 0.0 {
128 (*self.parent_device).config.scroll_factor = f;
129 }
130 }
131
132 // Acceleration Profile
133 let class_profile = if is_trackpoint {
134 config.trackpoint.as_ref().and_then(|t| t.accel_profile.as_ref())
135 } else if is_touchpad {
136 config.touchpad.as_ref().and_then(|t| t.accel_profile.as_ref())
137 } else {
138 config.mouse.as_ref().and_then(|m| m.accel_profile.as_ref())
139 };
140 let profile_str = class_profile.or(config.accel_profile.as_ref());
141 if let Some(ref p_str) = profile_str {
142 if ffi::libinput_device_config_accel_get_profiles(handle) != 0 {
143 let profile = match p_str.as_str() {
144 "flat" => Some(ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT),
145 "adaptive" => Some(ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE),
146 "none" => Some(ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_NONE),
147 "custom" => Some(ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM),
148 _ => None,
149 };
150 if let Some(p) = profile {
151 ffi::libinput_device_config_accel_set_profile(handle, p);
152 }
153 }
154 }
155
156 // Scroll method. Only the trackpoint and mouse classes carry it: a
157 // touchpad's two-finger default is what everyone wants, and a
158 // pointing stick's default -- scroll on middle-button-down -- is
159 // what nobody clicking a middle button wants (see TrackpointConfig).
160 let class_method = if is_trackpoint {
161 config.trackpoint.as_ref().and_then(|t| t.scroll_method.as_ref())
162 } else if is_touchpad {
163 None
164 } else {
165 config.mouse.as_ref().and_then(|m| m.scroll_method.as_ref())
166 };
167 if let Some(m_str) = class_method {
168 let method = match m_str.as_str() {
169 "none" => Some(ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_NO_SCROLL),
170 "button" | "on_button_down" => Some(ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN),
171 "two_finger" | "2fg" => Some(ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_2FG),
172 "edge" => Some(ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_EDGE),
173 other => {
174 log::warn!("input: unknown scroll_method {:?} (none, button, two_finger, edge)", other);
175 None
176 }
177 };
178 if let Some(m) = method {
179 let supported = ffi::libinput_device_config_scroll_get_methods(handle);
180 if m == ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_NO_SCROLL || (supported & m) != 0 {
181 ffi::libinput_device_config_scroll_set_method(handle, m);
182 } else {
183 log::warn!("input: {} does not support scroll_method {:?}", name, m_str);
184 }
185 }
186 }
187 }
188
189 pub unsafe fn deinit(&mut self) {
190 let objects_head = &mut self.objects as *mut ffi::wl_list as *mut WlList;
191 let mut curr = (*objects_head).next;
192 while curr != objects_head {
193 let next = (*curr).next;
194 let obj = crate::container_of!(curr, LibinputDeviceObject, link);
195
196 crate::server::wl_list_remove(curr);
197 ffi::wl_resource_post_event((*obj).resource, ffi::RIVER_LIBINPUT_DEVICE_V1_REMOVED);
198
199 // Mark the object as inert by setting device to NULL, but do NOT set the resource's
200 // implementation to NULL or free the `obj` memory here. Let handle_device_object_destroy
201 // do the cleanup when the resource is eventually destroyed by the client/server.
202 (*obj).device = std::ptr::null_mut();
203
204 // Re-initialize the link so it doesn't point to the freed self.objects list.
205 ffi::wl_list_init(&mut (*obj).link);
206
207 curr = next;
208 }
209
210 crate::server::wl_list_remove(&mut self.link as *mut ffi::wl_list as *mut WlList);
211 }
212
213 pub unsafe fn create_object(&mut self, config_v1_resource: *mut ffi::wl_resource) {
214 let client = ffi::wl_resource_get_client(config_v1_resource);
215 let version = ffi::wl_resource_get_version(config_v1_resource);
216
217 let resource = ffi::wl_resource_create(
218 client,
219 &ffi::river_libinput_device_v1_interface,
220 version,
221 0,
222 );
223 if resource.is_null() {
224 log::error!("out of memory creating river_libinput_device_v1");
225 ffi::wl_client_post_no_memory(client);
226 return;
227 }
228
229 let obj = Box::into_raw(Box::new(LibinputDeviceObject {
230 device: self,
231 resource,
232 link: std::mem::zeroed(),
233 }));
234
235 ffi::wl_resource_set_implementation(
236 resource,
237 &LIBINPUT_DEVICE_INTERFACE as *const _ as *const _,
238 obj as *mut _,
239 Some(handle_device_object_destroy),
240 );
241
242 let objects_head = &mut self.objects as *mut ffi::wl_list as *mut WlList;
243 let link_ptr = &mut (*obj).link as *mut ffi::wl_list as *mut WlList;
244 crate::server::wl_list_insert(objects_head, link_ptr);
245
246 // Send libinput_device event
247 ffi::wl_resource_post_event(config_v1_resource, ffi::RIVER_LIBINPUT_CONFIG_V1_LIBINPUT_DEVICE, resource);
248
249 // Send corresponding river input device
250 let input_dev_objects = &mut (*self.parent_device).objects as *mut ffi::wl_list as *mut WlList;
251 let mut curr = (*input_dev_objects).next;
252 while curr != input_dev_objects {
253 let next = (*curr).next;
254 let input_dev_obj = crate::container_of!(curr, crate::input_device::InputDeviceObject, link);
255 if ffi::wl_resource_get_client((*input_dev_obj).resource) == client {
256 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_INPUT_DEVICE, (*input_dev_obj).resource);
257 }
258 curr = next;
259 }
260
261 // SendEvents support and defaults
262 let send_events_modes = ffi::libinput_device_config_send_events_get_modes(self.libinput);
263 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SEND_EVENTS_SUPPORT, send_events_modes);
264 let send_events_default = ffi::libinput_device_config_send_events_get_default_mode(self.libinput);
265 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SEND_EVENTS_DEFAULT, send_events_default);
266 let send_events_current = ffi::libinput_device_config_send_events_get_mode(self.libinput);
267 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SEND_EVENTS_CURRENT, send_events_current);
268
269 // Tap support and defaults
270 let tap_finger_count = ffi::libinput_device_config_tap_get_finger_count(self.libinput);
271 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_TAP_SUPPORT, tap_finger_count as i32);
272 if tap_finger_count > 0 {
273 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_TAP_DEFAULT, ffi::libinput_device_config_tap_get_default_enabled(self.libinput));
274 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_TAP_CURRENT, ffi::libinput_device_config_tap_get_enabled(self.libinput));
275 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_TAP_BUTTON_MAP_DEFAULT, ffi::libinput_device_config_tap_get_default_button_map(self.libinput));
276 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_TAP_BUTTON_MAP_CURRENT, ffi::libinput_device_config_tap_get_button_map(self.libinput));
277 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DRAG_DEFAULT, ffi::libinput_device_config_tap_get_default_drag_enabled(self.libinput));
278 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DRAG_CURRENT, ffi::libinput_device_config_tap_get_drag_enabled(self.libinput));
279 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DRAG_LOCK_DEFAULT, ffi::libinput_device_config_tap_get_default_drag_lock_enabled(self.libinput));
280 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DRAG_LOCK_CURRENT, ffi::libinput_device_config_tap_get_drag_lock_enabled(self.libinput));
281 }
282
283 // Three finger drag
284 let three_finger_drag_finger_count = ffi::libinput_device_config_3fg_drag_get_finger_count(self.libinput);
285 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_THREE_FINGER_DRAG_SUPPORT, three_finger_drag_finger_count as i32);
286 if three_finger_drag_finger_count >= 3 {
287 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_THREE_FINGER_DRAG_DEFAULT, ffi::libinput_device_config_3fg_drag_get_default_enabled(self.libinput));
288 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_THREE_FINGER_DRAG_CURRENT, ffi::libinput_device_config_3fg_drag_get_enabled(self.libinput));
289 }
290
291 // Calibration matrix
292 let has_matrix = ffi::libinput_device_config_calibration_has_matrix(self.libinput);
293 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CALIBRATION_MATRIX_SUPPORT, has_matrix as i32);
294 if has_matrix != 0 {
295 let mut matrix = [0.0f32; 6];
296 ffi::libinput_device_config_calibration_get_default_matrix(self.libinput, matrix.as_mut_ptr());
297 let mut arr_default = ffi::wl_array {
298 size: std::mem::size_of_val(&matrix),
299 alloc: std::mem::size_of_val(&matrix),
300 data: matrix.as_mut_ptr() as *mut _,
301 };
302 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CALIBRATION_MATRIX_DEFAULT, &mut arr_default as *mut _);
303
304 ffi::libinput_device_config_calibration_get_matrix(self.libinput, matrix.as_mut_ptr());
305 let mut arr_current = ffi::wl_array {
306 size: std::mem::size_of_val(&matrix),
307 alloc: std::mem::size_of_val(&matrix),
308 data: matrix.as_mut_ptr() as *mut _,
309 };
310 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CALIBRATION_MATRIX_CURRENT, &mut arr_current as *mut _);
311 }
312
313 // Acceleration configs
314 let profiles = ffi::libinput_device_config_accel_get_profiles(self.libinput);
315 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ACCEL_PROFILES_SUPPORT, profiles);
316 if profiles != 0 {
317 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ACCEL_PROFILE_DEFAULT, ffi::libinput_device_config_accel_get_default_profile(self.libinput));
318 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ACCEL_PROFILE_CURRENT, ffi::libinput_device_config_accel_get_profile(self.libinput));
319
320 let default_speed = ffi::libinput_device_config_accel_get_default_speed(self.libinput);
321 let mut arr_default = ffi::wl_array {
322 size: std::mem::size_of::<f64>(),
323 alloc: std::mem::size_of::<f64>(),
324 data: &default_speed as *const f64 as *mut _,
325 };
326 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ACCEL_SPEED_DEFAULT, &mut arr_default as *mut _);
327
328 let current_speed = ffi::libinput_device_config_accel_get_speed(self.libinput);
329 let mut arr_current = ffi::wl_array {
330 size: std::mem::size_of::<f64>(),
331 alloc: std::mem::size_of::<f64>(),
332 data: ¤t_speed as *const f64 as *mut _,
333 };
334 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ACCEL_SPEED_CURRENT, &mut arr_current as *mut _);
335 }
336
337 // Natural scroll
338 let natural_scroll = ffi::libinput_device_config_scroll_has_natural_scroll(self.libinput);
339 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_NATURAL_SCROLL_SUPPORT, natural_scroll as i32);
340 if natural_scroll != 0 {
341 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_NATURAL_SCROLL_DEFAULT, ffi::libinput_device_config_scroll_get_default_natural_scroll_enabled(self.libinput) as u32);
342 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_NATURAL_SCROLL_CURRENT, ffi::libinput_device_config_scroll_get_natural_scroll_enabled(self.libinput) as u32);
343 }
344
345 // Left handed mode
346 let left_handed = ffi::libinput_device_config_left_handed_is_available(self.libinput);
347 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_LEFT_HANDED_SUPPORT, left_handed as i32);
348 if left_handed != 0 {
349 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_LEFT_HANDED_DEFAULT, ffi::libinput_device_config_left_handed_get_default(self.libinput) as u32);
350 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_LEFT_HANDED_CURRENT, ffi::libinput_device_config_left_handed_get(self.libinput) as u32);
351 }
352
353 // Click methods
354 let click_methods = ffi::libinput_device_config_click_get_methods(self.libinput);
355 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CLICK_METHOD_SUPPORT, click_methods);
356 if click_methods != 0 {
357 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CLICK_METHOD_DEFAULT, ffi::libinput_device_config_click_get_default_method(self.libinput));
358 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CLICK_METHOD_CURRENT, ffi::libinput_device_config_click_get_method(self.libinput));
359 if (click_methods & ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) != 0 {
360 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CLICKFINGER_BUTTON_MAP_DEFAULT, ffi::libinput_device_config_click_get_default_clickfinger_button_map(self.libinput));
361 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CLICKFINGER_BUTTON_MAP_CURRENT, ffi::libinput_device_config_click_get_clickfinger_button_map(self.libinput));
362 }
363 }
364
365 // Middle mouse emulation
366 let middle_emulation = ffi::libinput_device_config_middle_emulation_is_available(self.libinput);
367 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_MIDDLE_EMULATION_SUPPORT, middle_emulation as i32);
368 if middle_emulation != 0 {
369 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_MIDDLE_EMULATION_DEFAULT, ffi::libinput_device_config_middle_emulation_get_default_enabled(self.libinput));
370 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_MIDDLE_EMULATION_CURRENT, ffi::libinput_device_config_middle_emulation_get_enabled(self.libinput));
371 }
372
373 // Scroll methods
374 let scroll_methods = ffi::libinput_device_config_scroll_get_methods(self.libinput);
375 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_METHOD_SUPPORT, scroll_methods);
376 if scroll_methods != 0 {
377 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_METHOD_DEFAULT, ffi::libinput_device_config_scroll_get_default_method(self.libinput));
378 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_METHOD_CURRENT, ffi::libinput_device_config_scroll_get_method(self.libinput));
379 if (scroll_methods & ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) != 0 {
380 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_BUTTON_DEFAULT, ffi::libinput_device_config_scroll_get_default_button(self.libinput));
381 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_BUTTON_CURRENT, ffi::libinput_device_config_scroll_get_button(self.libinput));
382 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_BUTTON_LOCK_DEFAULT, ffi::libinput_device_config_scroll_get_default_button_lock(self.libinput));
383 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_BUTTON_LOCK_CURRENT, ffi::libinput_device_config_scroll_get_button_lock(self.libinput));
384 }
385 }
386
387 // Disable While Typing (DWT)
388 let dwt = ffi::libinput_device_config_dwt_is_available(self.libinput);
389 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DWT_SUPPORT, dwt as i32);
390 if dwt != 0 {
391 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DWT_DEFAULT, ffi::libinput_device_config_dwt_get_default_enabled(self.libinput) as u32);
392 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DWT_CURRENT, ffi::libinput_device_config_dwt_get_enabled(self.libinput) as u32);
393 }
394
395 // Disable While Trackpointing (DWTP)
396 let dwtp = ffi::libinput_device_config_dwtp_is_available(self.libinput);
397 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DWTP_SUPPORT, dwtp as i32);
398 if dwtp != 0 {
399 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DWTP_DEFAULT, ffi::libinput_device_config_dwtp_get_default_enabled(self.libinput) as u32);
400 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DWTP_CURRENT, ffi::libinput_device_config_dwtp_get_enabled(self.libinput) as u32);
401 }
402
403 // Rotation
404 let rotation = ffi::libinput_device_config_rotation_is_available(self.libinput);
405 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ROTATION_SUPPORT, rotation as i32);
406 if rotation != 0 {
407 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ROTATION_DEFAULT, ffi::libinput_device_config_rotation_get_default_angle(self.libinput) as u32);
408 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ROTATION_CURRENT, ffi::libinput_device_config_rotation_get_angle(self.libinput) as u32);
409 }
410
411 if version >= 2 {
412 ffi::wl_resource_post_event(resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DONE);
413 }
414 }
415 }
416
417 unsafe extern "C" fn handle_device_object_destroy(resource: *mut ffi::wl_resource) {
418 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
419 if !obj.is_null() {
420 if !(*obj).device.is_null() {
421 crate::server::wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
422 }
423 let _ = Box::from_raw(obj);
424 }
425 }
426
427 static LIBINPUT_DEVICE_INTERFACE: ffi::river_libinput_device_v1_interface = ffi::river_libinput_device_v1_interface {
428 destroy: Some(device_destroy),
429 set_send_events: Some(device_set_send_events),
430 set_tap: Some(device_set_tap),
431 set_tap_button_map: Some(device_set_tap_button_map),
432 set_drag: Some(device_set_drag),
433 set_drag_lock: Some(device_set_drag_lock),
434 set_three_finger_drag: Some(device_set_three_finger_drag),
435 set_calibration_matrix: Some(device_set_calibration_matrix),
436 set_accel_profile: Some(device_set_accel_profile),
437 set_accel_speed: Some(device_set_accel_speed),
438 apply_accel_config: Some(device_apply_accel_config),
439 set_natural_scroll: Some(device_set_natural_scroll),
440 set_left_handed: Some(device_set_left_handed),
441 set_click_method: Some(device_set_click_method),
442 set_clickfinger_button_map: Some(device_set_clickfinger_button_map),
443 set_middle_emulation: Some(device_set_middle_emulation),
444 set_scroll_method: Some(device_set_scroll_method),
445 set_scroll_button: Some(device_set_scroll_button),
446 set_scroll_button_lock: Some(device_set_scroll_button_lock),
447 set_dwt: Some(device_set_dwt),
448 set_dwtp: Some(device_set_dwtp),
449 set_rotation: Some(device_set_rotation),
450 };
451
452 unsafe fn make_result(
453 client: *mut ffi::wl_client,
454 resource: *mut ffi::wl_resource,
455 result_id: u32,
456 status: ffi::libinput_config_status,
457 ) -> bool {
458 let result_res = ffi::wl_resource_create(
459 client,
460 &ffi::river_libinput_result_v1_interface,
461 ffi::wl_resource_get_version(resource),
462 result_id,
463 );
464 if result_res.is_null() {
465 ffi::wl_client_post_no_memory(client);
466 return false;
467 }
468
469 match status {
470 ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => {
471 ffi::wl_resource_post_event(result_res, 0); // success
472 }
473 ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
474 ffi::wl_resource_post_event(result_res, 1); // unsupported
475 }
476 _ => {
477 ffi::wl_resource_post_event(result_res, 2); // invalid
478 }
479 }
480 ffi::wl_resource_destroy(result_res);
481 status == ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS
482 }
483
484 unsafe fn broadcast_update(dev: *mut LibinputDevice, opcode: u32, arg: u32) {
485 let objects_head = &mut (*dev).objects as *mut ffi::wl_list as *mut WlList;
486 let mut curr = (*objects_head).next;
487 while curr != objects_head {
488 let next = (*curr).next;
489 let obj = crate::container_of!(curr, LibinputDeviceObject, link);
490 ffi::wl_resource_post_event((*obj).resource, opcode, arg);
491 if ffi::wl_resource_get_version((*obj).resource) >= 2 {
492 ffi::wl_resource_post_event((*obj).resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DONE);
493 }
494 curr = next;
495 }
496 }
497
498 unsafe extern "C" fn device_destroy(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
499 ffi::wl_resource_destroy(resource);
500 }
501
502 unsafe extern "C" fn device_set_send_events(
503 client: *mut ffi::wl_client,
504 resource: *mut ffi::wl_resource,
505 result_id: u32,
506 mode: u32,
507 ) {
508 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
509 if obj.is_null() { return; }
510 let dev = (*obj).device;
511 if dev.is_null() {
512 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
513 return;
514 }
515
516 let status = ffi::libinput_device_config_send_events_set_mode((*dev).libinput, mode);
517 if make_result(client, resource, result_id, status) {
518 let current = ffi::libinput_device_config_send_events_get_mode((*dev).libinput);
519 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_SEND_EVENTS_CURRENT, current);
520 }
521 }
522
523 unsafe extern "C" fn device_set_tap(
524 client: *mut ffi::wl_client,
525 resource: *mut ffi::wl_resource,
526 result_id: u32,
527 state: u32,
528 ) {
529 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
530 if obj.is_null() { return; }
531 let dev = (*obj).device;
532 if dev.is_null() {
533 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
534 return;
535 }
536
537 let status = ffi::libinput_device_config_tap_set_enabled((*dev).libinput, state);
538 if make_result(client, resource, result_id, status) {
539 let current = ffi::libinput_device_config_tap_get_enabled((*dev).libinput);
540 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_TAP_CURRENT, current);
541 }
542 }
543
544 unsafe extern "C" fn device_set_tap_button_map(
545 client: *mut ffi::wl_client,
546 resource: *mut ffi::wl_resource,
547 result_id: u32,
548 button_map: u32,
549 ) {
550 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
551 if obj.is_null() { return; }
552 let dev = (*obj).device;
553 if dev.is_null() {
554 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
555 return;
556 }
557
558 let status = ffi::libinput_device_config_tap_set_button_map((*dev).libinput, button_map);
559 if make_result(client, resource, result_id, status) {
560 let current = ffi::libinput_device_config_tap_get_button_map((*dev).libinput);
561 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_TAP_BUTTON_MAP_CURRENT, current);
562 }
563 }
564
565 unsafe extern "C" fn device_set_drag(
566 client: *mut ffi::wl_client,
567 resource: *mut ffi::wl_resource,
568 result_id: u32,
569 state: u32,
570 ) {
571 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
572 if obj.is_null() { return; }
573 let dev = (*obj).device;
574 if dev.is_null() {
575 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
576 return;
577 }
578
579 let status = ffi::libinput_device_config_tap_set_drag_enabled((*dev).libinput, state);
580 if make_result(client, resource, result_id, status) {
581 let current = ffi::libinput_device_config_tap_get_drag_enabled((*dev).libinput);
582 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_DRAG_CURRENT, current);
583 }
584 }
585
586 unsafe extern "C" fn device_set_drag_lock(
587 client: *mut ffi::wl_client,
588 resource: *mut ffi::wl_resource,
589 result_id: u32,
590 state: u32,
591 ) {
592 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
593 if obj.is_null() { return; }
594 let dev = (*obj).device;
595 if dev.is_null() {
596 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
597 return;
598 }
599
600 let status = ffi::libinput_device_config_tap_set_drag_lock_enabled((*dev).libinput, state);
601 if make_result(client, resource, result_id, status) {
602 let current = ffi::libinput_device_config_tap_get_drag_lock_enabled((*dev).libinput);
603 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_DRAG_LOCK_CURRENT, current);
604 }
605 }
606
607 unsafe extern "C" fn device_set_three_finger_drag(
608 client: *mut ffi::wl_client,
609 resource: *mut ffi::wl_resource,
610 result_id: u32,
611 state: u32,
612 ) {
613 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
614 if obj.is_null() { return; }
615 let dev = (*obj).device;
616 if dev.is_null() {
617 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
618 return;
619 }
620
621 let status = ffi::libinput_device_config_3fg_drag_set_enabled((*dev).libinput, state);
622 if make_result(client, resource, result_id, status) {
623 let current = ffi::libinput_device_config_3fg_drag_get_enabled((*dev).libinput);
624 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_THREE_FINGER_DRAG_CURRENT, current);
625 }
626 }
627
628 unsafe extern "C" fn device_set_calibration_matrix(
629 client: *mut ffi::wl_client,
630 resource: *mut ffi::wl_resource,
631 result_id: u32,
632 matrix_arr: *mut ffi::wl_array,
633 ) {
634 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
635 if obj.is_null() { return; }
636 let dev = (*obj).device;
637 if dev.is_null() {
638 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
639 return;
640 }
641
642 if (*matrix_arr).size != std::mem::size_of::<[f32; 6]>() {
643 ffi::wl_resource_post_error(
644 resource,
645 ffi::river_libinput_device_v1_error_RIVER_LIBINPUT_DEVICE_V1_ERROR_INVALID_ARG,
646 b"invalid calibration matrix\0".as_ptr() as *const _,
647 );
648 return;
649 }
650
651 let matrix_ptr = (*matrix_arr).data as *const f32;
652 let status = ffi::libinput_device_config_calibration_set_matrix((*dev).libinput, matrix_ptr);
653 if make_result(client, resource, result_id, status) {
654 let mut current = [0.0f32; 6];
655 ffi::libinput_device_config_calibration_get_matrix((*dev).libinput, current.as_mut_ptr());
656 let mut arr = ffi::wl_array {
657 size: std::mem::size_of_val(¤t),
658 alloc: std::mem::size_of_val(¤t),
659 data: current.as_mut_ptr() as *mut _,
660 };
661
662 let objects_head = &mut (*dev).objects as *mut ffi::wl_list as *mut WlList;
663 let mut curr = (*objects_head).next;
664 while curr != objects_head {
665 let next = (*curr).next;
666 let dest_obj = crate::container_of!(curr, LibinputDeviceObject, link);
667 ffi::wl_resource_post_event((*dest_obj).resource, ffi::RIVER_LIBINPUT_DEVICE_V1_CALIBRATION_MATRIX_CURRENT, &mut arr as *mut _);
668 if ffi::wl_resource_get_version((*dest_obj).resource) >= 2 {
669 ffi::wl_resource_post_event((*dest_obj).resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DONE);
670 }
671 curr = next;
672 }
673 }
674 }
675
676 unsafe extern "C" fn device_set_accel_profile(
677 client: *mut ffi::wl_client,
678 resource: *mut ffi::wl_resource,
679 result_id: u32,
680 profile: u32,
681 ) {
682 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
683 if obj.is_null() { return; }
684 let dev = (*obj).device;
685 if dev.is_null() {
686 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
687 return;
688 }
689
690 let status = ffi::libinput_device_config_accel_set_profile((*dev).libinput, profile);
691 if make_result(client, resource, result_id, status) {
692 let current = ffi::libinput_device_config_accel_get_profile((*dev).libinput);
693 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_ACCEL_PROFILE_CURRENT, current);
694 }
695 }
696
697 unsafe extern "C" fn device_set_accel_speed(
698 client: *mut ffi::wl_client,
699 resource: *mut ffi::wl_resource,
700 result_id: u32,
701 speed_arr: *mut ffi::wl_array,
702 ) {
703 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
704 if obj.is_null() { return; }
705 let dev = (*obj).device;
706 if dev.is_null() {
707 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
708 return;
709 }
710
711 if (*speed_arr).size != std::mem::size_of::<f64>() {
712 ffi::wl_resource_post_error(
713 resource,
714 ffi::river_libinput_device_v1_error_RIVER_LIBINPUT_DEVICE_V1_ERROR_INVALID_ARG,
715 b"invalid accel speed\0".as_ptr() as *const _,
716 );
717 return;
718 }
719
720 let speed = *((*speed_arr).data as *const f64);
721 let status = ffi::libinput_device_config_accel_set_speed((*dev).libinput, speed);
722 if make_result(client, resource, result_id, status) {
723 let current = ffi::libinput_device_config_accel_get_speed((*dev).libinput);
724 let mut arr = ffi::wl_array {
725 size: std::mem::size_of::<f64>(),
726 alloc: std::mem::size_of::<f64>(),
727 data: ¤t as *const f64 as *mut _,
728 };
729
730 let objects_head = &mut (*dev).objects as *mut ffi::wl_list as *mut WlList;
731 let mut curr = (*objects_head).next;
732 while curr != objects_head {
733 let next = (*curr).next;
734 let dest_obj = crate::container_of!(curr, LibinputDeviceObject, link);
735 ffi::wl_resource_post_event((*dest_obj).resource, ffi::RIVER_LIBINPUT_DEVICE_V1_ACCEL_SPEED_CURRENT, &mut arr as *mut _);
736 if ffi::wl_resource_get_version((*dest_obj).resource) >= 2 {
737 ffi::wl_resource_post_event((*dest_obj).resource, ffi::RIVER_LIBINPUT_DEVICE_V1_DONE);
738 }
739 curr = next;
740 }
741 }
742 }
743
744 unsafe extern "C" fn device_apply_accel_config(
745 client: *mut ffi::wl_client,
746 resource: *mut ffi::wl_resource,
747 result_id: u32,
748 config_res: *mut ffi::wl_resource,
749 ) {
750 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
751 if obj.is_null() { return; }
752 let dev = (*obj).device;
753
754 let result_res = ffi::wl_resource_create(
755 client,
756 &ffi::river_libinput_result_v1_interface,
757 ffi::wl_resource_get_version(resource),
758 result_id,
759 );
760 if result_res.is_null() {
761 ffi::wl_client_post_no_memory(client);
762 return;
763 }
764
765 if dev.is_null() {
766 ffi::wl_resource_post_event(result_res, 1); // unsupported
767 ffi::wl_resource_destroy(result_res);
768 return;
769 }
770
771 let accel_config = ffi::wl_resource_get_user_data(config_res) as *mut crate::libinput_accel_config::LibinputAccelConfig;
772 let config = if !accel_config.is_null() {
773 (*accel_config).libinput
774 } else {
775 std::ptr::null_mut()
776 };
777
778 if config.is_null() {
779 ffi::wl_resource_post_event(result_res, 2); // invalid
780 ffi::wl_resource_destroy(result_res);
781 return;
782 }
783
784 let status = ffi::libinput_device_config_accel_apply((*dev).libinput, config);
785
786 let success = match status {
787 ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => {
788 ffi::wl_resource_post_event(result_res, 0); // success
789 true
790 }
791 ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
792 ffi::wl_resource_post_event(result_res, 1); // unsupported
793 false
794 }
795 _ => {
796 ffi::wl_resource_post_event(result_res, 2); // invalid
797 false
798 }
799 };
800
801 ffi::wl_resource_destroy(result_res);
802
803 if success {
804 let current = ffi::libinput_device_config_accel_get_profile((*dev).libinput);
805 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_ACCEL_PROFILE_CURRENT, current);
806 }
807 }
808
809 unsafe extern "C" fn device_set_natural_scroll(
810 client: *mut ffi::wl_client,
811 resource: *mut ffi::wl_resource,
812 result_id: u32,
813 state: u32,
814 ) {
815 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
816 if obj.is_null() { return; }
817 let dev = (*obj).device;
818 if dev.is_null() {
819 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
820 return;
821 }
822
823 let status = ffi::libinput_device_config_scroll_set_natural_scroll_enabled((*dev).libinput, state as i32);
824 if make_result(client, resource, result_id, status) {
825 let current = ffi::libinput_device_config_scroll_get_natural_scroll_enabled((*dev).libinput);
826 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_NATURAL_SCROLL_CURRENT, current as u32);
827 }
828 }
829
830 unsafe extern "C" fn device_set_left_handed(
831 client: *mut ffi::wl_client,
832 resource: *mut ffi::wl_resource,
833 result_id: u32,
834 state: u32,
835 ) {
836 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
837 if obj.is_null() { return; }
838 let dev = (*obj).device;
839 if dev.is_null() {
840 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
841 return;
842 }
843
844 let status = ffi::libinput_device_config_left_handed_set((*dev).libinput, state as i32);
845 if make_result(client, resource, result_id, status) {
846 let current = ffi::libinput_device_config_left_handed_get((*dev).libinput);
847 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_LEFT_HANDED_CURRENT, current as u32);
848 }
849 }
850
851 unsafe extern "C" fn device_set_click_method(
852 client: *mut ffi::wl_client,
853 resource: *mut ffi::wl_resource,
854 result_id: u32,
855 method: u32,
856 ) {
857 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
858 if obj.is_null() { return; }
859 let dev = (*obj).device;
860 if dev.is_null() {
861 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
862 return;
863 }
864
865 let status = ffi::libinput_device_config_click_set_method((*dev).libinput, method);
866 if make_result(client, resource, result_id, status) {
867 let current = ffi::libinput_device_config_click_get_method((*dev).libinput);
868 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_CLICK_METHOD_CURRENT, current);
869 }
870 }
871
872 unsafe extern "C" fn device_set_clickfinger_button_map(
873 client: *mut ffi::wl_client,
874 resource: *mut ffi::wl_resource,
875 result_id: u32,
876 button_map: u32,
877 ) {
878 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
879 if obj.is_null() { return; }
880 let dev = (*obj).device;
881 if dev.is_null() {
882 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
883 return;
884 }
885
886 let status = ffi::libinput_device_config_click_set_clickfinger_button_map((*dev).libinput, button_map);
887 if make_result(client, resource, result_id, status) {
888 let current = ffi::libinput_device_config_click_get_clickfinger_button_map((*dev).libinput);
889 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_CLICKFINGER_BUTTON_MAP_CURRENT, current);
890 }
891 }
892
893 unsafe extern "C" fn device_set_middle_emulation(
894 client: *mut ffi::wl_client,
895 resource: *mut ffi::wl_resource,
896 result_id: u32,
897 state: u32,
898 ) {
899 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
900 if obj.is_null() { return; }
901 let dev = (*obj).device;
902 if dev.is_null() {
903 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
904 return;
905 }
906
907 let status = ffi::libinput_device_config_middle_emulation_set_enabled((*dev).libinput, state);
908 if make_result(client, resource, result_id, status) {
909 let current = ffi::libinput_device_config_middle_emulation_get_enabled((*dev).libinput);
910 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_MIDDLE_EMULATION_CURRENT, current);
911 }
912 }
913
914 unsafe extern "C" fn device_set_scroll_method(
915 client: *mut ffi::wl_client,
916 resource: *mut ffi::wl_resource,
917 result_id: u32,
918 method: u32,
919 ) {
920 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
921 if obj.is_null() { return; }
922 let dev = (*obj).device;
923 if dev.is_null() {
924 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
925 return;
926 }
927
928 let status = ffi::libinput_device_config_scroll_set_method((*dev).libinput, method);
929 if make_result(client, resource, result_id, status) {
930 let current = ffi::libinput_device_config_scroll_get_method((*dev).libinput);
931 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_METHOD_CURRENT, current);
932 }
933 }
934
935 unsafe extern "C" fn device_set_scroll_button(
936 client: *mut ffi::wl_client,
937 resource: *mut ffi::wl_resource,
938 result_id: u32,
939 button: u32,
940 ) {
941 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
942 if obj.is_null() { return; }
943 let dev = (*obj).device;
944 if dev.is_null() {
945 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
946 return;
947 }
948
949 let status = ffi::libinput_device_config_scroll_set_button((*dev).libinput, button);
950 if make_result(client, resource, result_id, status) {
951 let current = ffi::libinput_device_config_scroll_get_button((*dev).libinput);
952 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_BUTTON_CURRENT, current);
953 }
954 }
955
956 unsafe extern "C" fn device_set_scroll_button_lock(
957 client: *mut ffi::wl_client,
958 resource: *mut ffi::wl_resource,
959 result_id: u32,
960 state: u32,
961 ) {
962 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
963 if obj.is_null() { return; }
964 let dev = (*obj).device;
965 if dev.is_null() {
966 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
967 return;
968 }
969
970 let status = ffi::libinput_device_config_scroll_set_button_lock((*dev).libinput, state);
971 if make_result(client, resource, result_id, status) {
972 let current = ffi::libinput_device_config_scroll_get_button_lock((*dev).libinput);
973 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_SCROLL_BUTTON_LOCK_CURRENT, current);
974 }
975 }
976
977 unsafe extern "C" fn device_set_dwt(
978 client: *mut ffi::wl_client,
979 resource: *mut ffi::wl_resource,
980 result_id: u32,
981 state: u32,
982 ) {
983 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
984 if obj.is_null() { return; }
985 let dev = (*obj).device;
986 if dev.is_null() {
987 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
988 return;
989 }
990
991 let status = ffi::libinput_device_config_dwt_set_enabled((*dev).libinput, state);
992 if make_result(client, resource, result_id, status) {
993 let current = ffi::libinput_device_config_dwt_get_enabled((*dev).libinput);
994 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_DWT_CURRENT, current as u32);
995 }
996 }
997
998 unsafe extern "C" fn device_set_dwtp(
999 client: *mut ffi::wl_client,
1000 resource: *mut ffi::wl_resource,
1001 result_id: u32,
1002 state: u32,
1003 ) {
1004 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
1005 if obj.is_null() { return; }
1006 let dev = (*obj).device;
1007 if dev.is_null() {
1008 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
1009 return;
1010 }
1011
1012 let status = ffi::libinput_device_config_dwtp_set_enabled((*dev).libinput, state);
1013 if make_result(client, resource, result_id, status) {
1014 let current = ffi::libinput_device_config_dwtp_get_enabled((*dev).libinput);
1015 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_DWTP_CURRENT, current as u32);
1016 }
1017 }
1018
1019 unsafe extern "C" fn device_set_rotation(
1020 client: *mut ffi::wl_client,
1021 resource: *mut ffi::wl_resource,
1022 result_id: u32,
1023 angle: u32,
1024 ) {
1025 let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
1026 if obj.is_null() { return; }
1027 let dev = (*obj).device;
1028 if dev.is_null() {
1029 make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
1030 return;
1031 }
1032
1033 let status = ffi::libinput_device_config_rotation_set_angle((*dev).libinput, angle);
1034 if make_result(client, resource, result_id, status) {
1035 let current = ffi::libinput_device_config_rotation_get_angle((*dev).libinput);
1036 broadcast_update(dev, ffi::RIVER_LIBINPUT_DEVICE_V1_ROTATION_CURRENT, current);
1037 }
1038 }