Skip to main content

input/
lib.rs

1//! Rust implementation of the libinput.so.10 ABI.
2//!
3//! Version 0.3.1 is a tested drop-in replacement for libinput 1.31.3 on the
4//! supported x86_64 DNF/RPM targets. The release gate covers the complete
5//! public ABI and the pinned upstream public-ABI behavioral corpus.
6
7#![allow(non_snake_case, clippy::missing_safety_doc)]
8
9mod backend;
10pub mod capforge;
11pub mod chwd_input;
12#[doc(hidden)]
13pub mod elan_recover;
14pub mod evdev;
15mod evtrans;
16mod ffi_types;
17mod hwdetect;
18mod motion;
19mod quirks;
20mod tpad;
21mod udev;
22#[doc(hidden)]
23pub mod udev_callout;
24
25use crate::ffi_types::{
26    BackendKind, EventPayload, LibinputContext, LibinputDevice, LibinputDeviceGroup, LibinputEvent,
27    LibinputEventType, LibinputInterface, LibinputSeat, LibinputTabletPadModeGroup,
28    LibinputTabletTool,
29};
30
31use std::ffi::CStr;
32use std::os::unix::io::RawFd;
33
34extern "C" {
35    fn input_emit_log(
36        handler: *mut libc::c_void,
37        context: *mut libc::c_void,
38        priority: u32,
39        format: *const libc::c_char,
40        ...
41    );
42}
43
44#[repr(C)]
45pub struct LibinputConfigAreaRectangle {
46    pub x1: f64,
47    pub y1: f64,
48    pub x2: f64,
49    pub y2: f64,
50}
51
52// ---------------------------------------------------------------------------
53// Internal helpers
54// ---------------------------------------------------------------------------
55
56unsafe fn populate_events(ctx: *mut LibinputContext) {
57    if ctx.is_null() {
58        return;
59    }
60    let ctx_ref = &mut *ctx;
61    let mut tmp: std::collections::VecDeque<LibinputEvent> = std::collections::VecDeque::new();
62    if let Ok(mut backend) = ctx_ref.backend.lock() {
63        backend.drain_into_queue(ctx, &mut tmp);
64    }
65    enqueue_events(ctx, tmp);
66}
67
68unsafe fn enqueue_event(ctx: *mut LibinputContext, event: LibinputEvent) {
69    if !event.device.is_null()
70        && event.event_type != LibinputEventType::LIBINPUT_EVENT_DEVICE_REMOVED
71    {
72        libinput_device_ref(event.device);
73    }
74    (*ctx).event_queue.push_back(event);
75}
76
77unsafe fn enqueue_events(
78    ctx: *mut LibinputContext,
79    events: impl IntoIterator<Item = LibinputEvent>,
80) {
81    for event in events {
82        enqueue_event(ctx, event);
83    }
84}
85
86pub(crate) unsafe fn emit_debug_log(ctx: *mut LibinputContext, message: &str) {
87    if ctx.is_null() || (*ctx).log_priority > 10 {
88        return;
89    }
90    let Some(handler) = (*ctx).log_handler else {
91        return;
92    };
93    let Ok(message) = std::ffi::CString::new(format!("{}\n", message.replace('%', "%%"))) else {
94        return;
95    };
96    input_emit_log(
97        handler as *mut libc::c_void,
98        ctx.cast(),
99        10,
100        message.as_ptr(),
101    );
102}
103
104pub(crate) unsafe fn emit_error_log(ctx: *mut LibinputContext, message: &str) {
105    if ctx.is_null() || (*ctx).log_priority > 30 {
106        return;
107    }
108    let Some(handler) = (*ctx).log_handler else {
109        return;
110    };
111    let Ok(message) = std::ffi::CString::new(format!("{}\n", message.replace('%', "%%"))) else {
112        return;
113    };
114    input_emit_log(
115        handler as *mut libc::c_void,
116        ctx.cast(),
117        30,
118        message.as_ptr(),
119    );
120}
121
122pub(crate) unsafe fn emit_info_log(ctx: *mut LibinputContext, message: &str) {
123    if ctx.is_null() || (*ctx).log_priority > 20 {
124        return;
125    }
126    let Some(handler) = (*ctx).log_handler else {
127        return;
128    };
129    let Ok(message) = std::ffi::CString::new(format!("{}\n", message.replace('%', "%%"))) else {
130        return;
131    };
132    input_emit_log(
133        handler as *mut libc::c_void,
134        ctx.cast(),
135        20,
136        message.as_ptr(),
137    );
138}
139
140// ---------------------------------------------------------------------------
141// Context lifecycle
142// ---------------------------------------------------------------------------
143
144#[no_mangle]
145pub unsafe extern "C" fn libinput_udev_create_context(
146    interface: *const LibinputInterface,
147    user_data: *mut libc::c_void,
148    udev: *mut libc::c_void,
149) -> *mut LibinputContext {
150    if interface.is_null() || udev.is_null() {
151        return std::ptr::null_mut();
152    }
153    let ctx = Box::into_raw(Box::new(LibinputContext::new(
154        interface,
155        user_data,
156        BackendKind::Udev,
157    )));
158    (*(*ctx).seat).context = ctx;
159    ctx
160}
161
162#[no_mangle]
163pub unsafe extern "C" fn libinput_path_create_context(
164    interface: *const LibinputInterface,
165    user_data: *mut libc::c_void,
166) -> *mut LibinputContext {
167    if interface.is_null() {
168        return std::ptr::null_mut();
169    }
170    let ctx = Box::into_raw(Box::new(LibinputContext::new(
171        interface,
172        user_data,
173        BackendKind::Path,
174    )));
175    (*(*ctx).seat).context = ctx;
176    ctx
177}
178
179#[no_mangle]
180pub unsafe extern "C" fn libinput_ref(ctx: *mut LibinputContext) -> *mut LibinputContext {
181    if ctx.is_null() {
182        return std::ptr::null_mut();
183    }
184    (*ctx).inc_ref();
185    ctx
186}
187
188#[no_mangle]
189pub unsafe extern "C" fn libinput_unref(ctx: *mut LibinputContext) -> *mut LibinputContext {
190    if ctx.is_null() {
191        return std::ptr::null_mut();
192    }
193    if (*ctx).dec_ref() == 0 {
194        drop(Box::from_raw(ctx));
195        return std::ptr::null_mut();
196    }
197    ctx
198}
199
200#[no_mangle]
201pub unsafe extern "C" fn libinput_udev_assign_seat(
202    ctx: *mut LibinputContext,
203    seat_name: *const libc::c_char,
204) -> libc::c_int {
205    if ctx.is_null()
206        || seat_name.is_null()
207        || (*ctx).backend_kind != BackendKind::Udev
208        || (*ctx).seat_assigned
209    {
210        return -1;
211    }
212    let seat_name = CStr::from_ptr(seat_name);
213    if seat_name.to_bytes().len() > 255 {
214        return -1;
215    }
216    let name = seat_name.to_string_lossy().into_owned();
217    if let Ok(cname) = std::ffi::CString::new(name) {
218        (*(*ctx).seat).physical_name = cname;
219    }
220    (*ctx).seat_assigned = true;
221    (*ctx).plugins_loaded = true;
222    let mut tmp: Vec<LibinputEvent> = Vec::new();
223    if let Ok(mut backend) = (*ctx).backend.lock() {
224        backend.scan_and_open(ctx, &mut tmp);
225    }
226    for ev in tmp {
227        enqueue_event(ctx, ev);
228    }
229    0
230}
231
232#[no_mangle]
233pub unsafe extern "C" fn libinput_path_add_device(
234    ctx: *mut LibinputContext,
235    path: *const libc::c_char,
236) -> *mut LibinputDevice {
237    if ctx.is_null() || path.is_null() || (*ctx).backend_kind != BackendKind::Path {
238        return std::ptr::null_mut();
239    }
240    let path = CStr::from_ptr(path);
241    if path.to_bytes().len() > libc::PATH_MAX as usize {
242        emit_error_log(
243            ctx,
244            &format!(
245                "client bug: Unexpected path, limited to {} characters.",
246                libc::PATH_MAX
247            ),
248        );
249        return std::ptr::null_mut();
250    }
251    let devnode = path.to_string_lossy().into_owned();
252    (*ctx).plugins_loaded = true;
253    let p = std::path::PathBuf::from(&devnode);
254    use std::os::unix::fs::FileTypeExt;
255    if !p
256        .metadata()
257        .is_ok_and(|metadata| metadata.file_type().is_char_device())
258    {
259        emit_error_log(ctx, "failed to add device");
260        return std::ptr::null_mut();
261    }
262    let mut tmp: Vec<LibinputEvent> = Vec::new();
263    let old_len = (*ctx).devices.len();
264    if let Ok(mut backend) = (*ctx).backend.lock() {
265        backend.try_open(ctx, &p, &mut tmp);
266    }
267    for ev in tmp {
268        enqueue_event(ctx, ev);
269    }
270    if (*ctx).devices.len() == old_len + 1 {
271        if let Ok(mut backend) = (*ctx).backend.lock() {
272            backend.remember_path(&p);
273        }
274        emit_info_log(ctx, "device added");
275        (&(*ctx).devices)[old_len]
276    } else {
277        emit_error_log(ctx, "failed to add device");
278        std::ptr::null_mut()
279    }
280}
281
282#[no_mangle]
283pub unsafe extern "C" fn libinput_path_remove_device(dev: *mut LibinputDevice) {
284    if dev.is_null() {
285        return;
286    }
287    let ctx = (*dev).context;
288    if ctx.is_null() || (*ctx).backend_kind != BackendKind::Path {
289        return;
290    }
291    let path = std::path::PathBuf::from((*dev).devnode.to_string_lossy().into_owned());
292    let mut events = std::collections::VecDeque::new();
293    let removed = if let Ok(mut backend) = (*ctx).backend.lock() {
294        backend.forget_path(&path);
295        backend.remove_device(ctx, dev, &mut events)
296    } else {
297        false
298    };
299    if removed {
300        (*ctx).devices.retain(|candidate| *candidate != dev);
301        enqueue_events(ctx, events);
302        libinput_device_unref(dev);
303    }
304}
305
306// ---------------------------------------------------------------------------
307// FD & dispatch
308// ---------------------------------------------------------------------------
309
310#[no_mangle]
311pub unsafe extern "C" fn libinput_get_fd(ctx: *mut LibinputContext) -> RawFd {
312    if ctx.is_null() {
313        return -1;
314    }
315    (*ctx).epoll_fd
316}
317
318#[no_mangle]
319pub unsafe extern "C" fn libinput_dispatch(ctx: *mut LibinputContext) -> libc::c_int {
320    if ctx.is_null() {
321        return -1;
322    }
323    let mut events: [libc::epoll_event; 16] = std::mem::zeroed();
324    libc::epoll_wait((*ctx).epoll_fd, events.as_mut_ptr(), 16, 0);
325    (*ctx).drain_fd();
326    populate_events(ctx);
327    0
328}
329
330// ---------------------------------------------------------------------------
331// Event retrieval & destruction
332// ---------------------------------------------------------------------------
333
334#[no_mangle]
335pub unsafe extern "C" fn libinput_get_event(ctx: *mut LibinputContext) -> *mut LibinputEvent {
336    if ctx.is_null() {
337        return std::ptr::null_mut();
338    }
339    match (*ctx).event_queue.pop_front() {
340        Some(ev) => Box::into_raw(Box::new(ev)),
341        None => std::ptr::null_mut(),
342    }
343}
344
345#[no_mangle]
346pub unsafe extern "C" fn libinput_next_event_type(ctx: *mut LibinputContext) -> LibinputEventType {
347    if ctx.is_null() {
348        return LibinputEventType::LIBINPUT_EVENT_NONE;
349    }
350    (*ctx)
351        .event_queue
352        .front()
353        .map(|e| e.event_type)
354        .unwrap_or(LibinputEventType::LIBINPUT_EVENT_NONE)
355}
356
357#[no_mangle]
358pub unsafe extern "C" fn libinput_event_destroy(event: *mut LibinputEvent) {
359    if !event.is_null() {
360        let mut event = Box::from_raw(event);
361        event.release_queued_device_ref();
362    }
363}
364
365#[no_mangle]
366pub unsafe extern "C" fn libinput_event_get_type(event: *const LibinputEvent) -> LibinputEventType {
367    if event.is_null() {
368        return LibinputEventType::LIBINPUT_EVENT_NONE;
369    }
370    (*event).event_type
371}
372
373#[no_mangle]
374pub unsafe extern "C" fn libinput_event_get_context(
375    event: *const LibinputEvent,
376) -> *mut LibinputContext {
377    if event.is_null() {
378        return std::ptr::null_mut();
379    }
380    (*event).context
381}
382
383#[no_mangle]
384pub unsafe extern "C" fn libinput_event_get_device(
385    event: *const LibinputEvent,
386) -> *mut LibinputDevice {
387    if event.is_null() {
388        return std::ptr::null_mut();
389    }
390    (*event).device
391}
392
393#[no_mangle]
394pub unsafe extern "C" fn libinput_event_get_device_notify_event(
395    event: *mut LibinputEvent,
396) -> *mut LibinputEvent {
397    if event.is_null() {
398        return std::ptr::null_mut();
399    }
400    match (*event).event_type {
401        LibinputEventType::LIBINPUT_EVENT_DEVICE_ADDED
402        | LibinputEventType::LIBINPUT_EVENT_DEVICE_REMOVED => event,
403        _ => std::ptr::null_mut(),
404    }
405}
406
407#[no_mangle]
408pub unsafe extern "C" fn libinput_event_device_notify_get_base_event(
409    event: *mut LibinputEvent,
410) -> *mut LibinputEvent {
411    event
412}
413
414// ---------------------------------------------------------------------------
415// Pointer event accessors
416// ---------------------------------------------------------------------------
417
418#[no_mangle]
419pub unsafe extern "C" fn libinput_event_get_pointer_event(
420    event: *mut LibinputEvent,
421) -> *mut LibinputEvent {
422    if event.is_null() {
423        return std::ptr::null_mut();
424    }
425    match (*event).event_type {
426        LibinputEventType::LIBINPUT_EVENT_POINTER_MOTION
427        | LibinputEventType::LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE
428        | LibinputEventType::LIBINPUT_EVENT_POINTER_BUTTON
429        | LibinputEventType::LIBINPUT_EVENT_POINTER_AXIS
430        | LibinputEventType::LIBINPUT_EVENT_POINTER_SCROLL_WHEEL
431        | LibinputEventType::LIBINPUT_EVENT_POINTER_SCROLL_FINGER
432        | LibinputEventType::LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS => event,
433        _ => std::ptr::null_mut(),
434    }
435}
436
437#[no_mangle]
438pub unsafe extern "C" fn libinput_event_pointer_get_base_event(
439    event: *mut LibinputEvent,
440) -> *mut LibinputEvent {
441    event
442}
443
444#[no_mangle]
445pub unsafe extern "C" fn libinput_event_pointer_get_time(event: *const LibinputEvent) -> u32 {
446    if event.is_null() {
447        return 0;
448    }
449    match &(*event).payload {
450        EventPayload::PointerMotion(e) => (e.time_usec / 1000) as u32,
451        EventPayload::PointerMotionAbsolute(e) => (e.time_usec / 1000) as u32,
452        EventPayload::PointerButton(e) => (e.time_usec / 1000) as u32,
453        EventPayload::PointerAxis(e) => (e.time_usec / 1000) as u32,
454        _ => 0,
455    }
456}
457
458#[no_mangle]
459pub unsafe extern "C" fn libinput_event_pointer_get_time_usec(event: *const LibinputEvent) -> u64 {
460    if event.is_null() {
461        return 0;
462    }
463    match &(*event).payload {
464        EventPayload::PointerMotion(e) => e.time_usec,
465        EventPayload::PointerMotionAbsolute(e) => e.time_usec,
466        EventPayload::PointerButton(e) => e.time_usec,
467        EventPayload::PointerAxis(e) => e.time_usec,
468        _ => 0,
469    }
470}
471
472#[no_mangle]
473pub unsafe extern "C" fn libinput_event_pointer_get_dx(event: *const LibinputEvent) -> f64 {
474    if event.is_null() {
475        return 0.0;
476    }
477    if let EventPayload::PointerMotion(e) = &(*event).payload {
478        e.dx
479    } else {
480        0.0
481    }
482}
483
484#[no_mangle]
485pub unsafe extern "C" fn libinput_event_pointer_get_dy(event: *const LibinputEvent) -> f64 {
486    if event.is_null() {
487        return 0.0;
488    }
489    if let EventPayload::PointerMotion(e) = &(*event).payload {
490        e.dy
491    } else {
492        0.0
493    }
494}
495
496#[no_mangle]
497pub unsafe extern "C" fn libinput_event_pointer_get_dx_unaccelerated(
498    event: *const LibinputEvent,
499) -> f64 {
500    if event.is_null() {
501        return 0.0;
502    }
503    if let EventPayload::PointerMotion(e) = &(*event).payload {
504        e.dx_unaccel
505    } else {
506        0.0
507    }
508}
509
510#[no_mangle]
511pub unsafe extern "C" fn libinput_event_pointer_get_dy_unaccelerated(
512    event: *const LibinputEvent,
513) -> f64 {
514    if event.is_null() {
515        return 0.0;
516    }
517    if let EventPayload::PointerMotion(e) = &(*event).payload {
518        e.dy_unaccel
519    } else {
520        0.0
521    }
522}
523
524#[no_mangle]
525pub unsafe extern "C" fn libinput_event_pointer_get_absolute_x(event: *const LibinputEvent) -> f64 {
526    if event.is_null() {
527        return 0.0;
528    }
529    if let EventPayload::PointerMotionAbsolute(e) = &(*event).payload {
530        e.abs_x
531    } else {
532        0.0
533    }
534}
535
536#[no_mangle]
537pub unsafe extern "C" fn libinput_event_pointer_get_absolute_y(event: *const LibinputEvent) -> f64 {
538    if event.is_null() {
539        return 0.0;
540    }
541    if let EventPayload::PointerMotionAbsolute(e) = &(*event).payload {
542        e.abs_y
543    } else {
544        0.0
545    }
546}
547
548#[no_mangle]
549pub unsafe extern "C" fn libinput_event_pointer_get_button(event: *const LibinputEvent) -> u32 {
550    if event.is_null() {
551        return 0;
552    }
553    if let EventPayload::PointerButton(e) = &(*event).payload {
554        e.button
555    } else {
556        0
557    }
558}
559
560#[no_mangle]
561pub unsafe extern "C" fn libinput_event_pointer_get_button_state(
562    event: *const LibinputEvent,
563) -> u32 {
564    if event.is_null() {
565        return 0;
566    }
567    if let EventPayload::PointerButton(e) = &(*event).payload {
568        e.state
569    } else {
570        0
571    }
572}
573
574#[no_mangle]
575pub unsafe extern "C" fn libinput_event_pointer_get_seat_button_count(
576    event: *const LibinputEvent,
577) -> u32 {
578    if event.is_null() {
579        return 0;
580    }
581    if let EventPayload::PointerButton(e) = &(*event).payload {
582        e.seat_button_count
583    } else {
584        0
585    }
586}
587
588#[no_mangle]
589pub unsafe extern "C" fn libinput_event_pointer_get_axis_value(
590    event: *const LibinputEvent,
591    axis: u32,
592) -> f64 {
593    if event.is_null() {
594        return 0.0;
595    }
596    if let EventPayload::PointerAxis(e) = &(*event).payload {
597        e.value(axis)
598    } else {
599        0.0
600    }
601}
602
603#[no_mangle]
604pub unsafe extern "C" fn libinput_event_pointer_get_axis_value_discrete(
605    event: *const LibinputEvent,
606    axis: u32,
607) -> f64 {
608    if event.is_null() {
609        return 0.0;
610    }
611    if let EventPayload::PointerAxis(e) = &(*event).payload {
612        e.value_discrete(axis) as f64
613    } else {
614        0.0
615    }
616}
617
618#[no_mangle]
619pub unsafe extern "C" fn libinput_event_pointer_get_axis_source(
620    event: *const LibinputEvent,
621) -> u32 {
622    if event.is_null() {
623        return 0;
624    }
625    if let EventPayload::PointerAxis(e) = &(*event).payload {
626        e.source
627    } else {
628        0
629    }
630}
631
632#[no_mangle]
633pub unsafe extern "C" fn libinput_event_pointer_has_axis(
634    event: *const LibinputEvent,
635    axis: u32,
636) -> libc::c_int {
637    if event.is_null() {
638        return 0;
639    }
640    matches!(&(*event).payload, EventPayload::PointerAxis(e) if e.has_axis(axis)) as libc::c_int
641}
642
643// ---------------------------------------------------------------------------
644// Keyboard event accessors
645// ---------------------------------------------------------------------------
646
647#[no_mangle]
648pub unsafe extern "C" fn libinput_event_get_keyboard_event(
649    event: *mut LibinputEvent,
650) -> *mut LibinputEvent {
651    if event.is_null() {
652        return std::ptr::null_mut();
653    }
654    if (*event).event_type == LibinputEventType::LIBINPUT_EVENT_KEYBOARD_KEY {
655        event
656    } else {
657        std::ptr::null_mut()
658    }
659}
660
661#[no_mangle]
662pub unsafe extern "C" fn libinput_event_keyboard_get_base_event(
663    event: *mut LibinputEvent,
664) -> *mut LibinputEvent {
665    event
666}
667
668#[no_mangle]
669pub unsafe extern "C" fn libinput_event_keyboard_get_time(event: *const LibinputEvent) -> u32 {
670    if event.is_null() {
671        return 0;
672    }
673    if let EventPayload::KeyboardKey(e) = &(*event).payload {
674        (e.time_usec / 1000) as u32
675    } else {
676        0
677    }
678}
679
680#[no_mangle]
681pub unsafe extern "C" fn libinput_event_keyboard_get_time_usec(event: *const LibinputEvent) -> u64 {
682    if event.is_null() {
683        return 0;
684    }
685    if let EventPayload::KeyboardKey(e) = &(*event).payload {
686        e.time_usec
687    } else {
688        0
689    }
690}
691
692#[no_mangle]
693pub unsafe extern "C" fn libinput_event_keyboard_get_key(event: *const LibinputEvent) -> u32 {
694    if event.is_null() {
695        return 0;
696    }
697    if let EventPayload::KeyboardKey(e) = &(*event).payload {
698        e.key
699    } else {
700        0
701    }
702}
703
704#[no_mangle]
705pub unsafe extern "C" fn libinput_event_keyboard_get_key_state(event: *const LibinputEvent) -> u32 {
706    if event.is_null() {
707        return 0;
708    }
709    if let EventPayload::KeyboardKey(e) = &(*event).payload {
710        e.state
711    } else {
712        0
713    }
714}
715
716#[no_mangle]
717pub unsafe extern "C" fn libinput_event_keyboard_get_seat_key_count(
718    event: *const LibinputEvent,
719) -> u32 {
720    if event.is_null() {
721        return 0;
722    }
723    if let EventPayload::KeyboardKey(e) = &(*event).payload {
724        e.seat_key_count
725    } else {
726        0
727    }
728}
729
730// ---------------------------------------------------------------------------
731// Touch event accessors
732// ---------------------------------------------------------------------------
733
734#[no_mangle]
735pub unsafe extern "C" fn libinput_event_get_touch_event(
736    event: *mut LibinputEvent,
737) -> *mut LibinputEvent {
738    if event.is_null() {
739        return std::ptr::null_mut();
740    }
741    match (*event).event_type {
742        LibinputEventType::LIBINPUT_EVENT_TOUCH_DOWN
743        | LibinputEventType::LIBINPUT_EVENT_TOUCH_UP
744        | LibinputEventType::LIBINPUT_EVENT_TOUCH_MOTION
745        | LibinputEventType::LIBINPUT_EVENT_TOUCH_CANCEL
746        | LibinputEventType::LIBINPUT_EVENT_TOUCH_FRAME => event,
747        _ => std::ptr::null_mut(),
748    }
749}
750
751#[no_mangle]
752pub unsafe extern "C" fn libinput_event_touch_get_base_event(
753    event: *mut LibinputEvent,
754) -> *mut LibinputEvent {
755    event
756}
757
758#[no_mangle]
759pub unsafe extern "C" fn libinput_event_touch_get_time(event: *const LibinputEvent) -> u32 {
760    if event.is_null() {
761        return 0;
762    }
763    match &(*event).payload {
764        EventPayload::TouchDown(e)
765        | EventPayload::TouchUp(e)
766        | EventPayload::TouchMotion(e)
767        | EventPayload::TouchCancel(e) => (e.time_usec / 1000) as u32,
768        EventPayload::TouchFrame { time_usec } => (*time_usec / 1000) as u32,
769        _ => 0,
770    }
771}
772
773#[no_mangle]
774pub unsafe extern "C" fn libinput_event_touch_get_time_usec(event: *const LibinputEvent) -> u64 {
775    if event.is_null() {
776        return 0;
777    }
778    match &(*event).payload {
779        EventPayload::TouchDown(e)
780        | EventPayload::TouchUp(e)
781        | EventPayload::TouchMotion(e)
782        | EventPayload::TouchCancel(e) => e.time_usec,
783        EventPayload::TouchFrame { time_usec } => *time_usec,
784        _ => 0,
785    }
786}
787
788#[no_mangle]
789pub unsafe extern "C" fn libinput_event_touch_get_slot(event: *const LibinputEvent) -> i32 {
790    if event.is_null() {
791        return -1;
792    }
793    match &(*event).payload {
794        EventPayload::TouchDown(e) | EventPayload::TouchMotion(e) | EventPayload::TouchUp(e) => {
795            e.slot
796        }
797        _ => -1,
798    }
799}
800
801#[no_mangle]
802pub unsafe extern "C" fn libinput_event_touch_get_seat_slot(event: *const LibinputEvent) -> i32 {
803    if event.is_null() {
804        return -1;
805    }
806    match &(*event).payload {
807        EventPayload::TouchDown(e) | EventPayload::TouchMotion(e) | EventPayload::TouchUp(e) => {
808            e.seat_slot
809        }
810        _ => -1,
811    }
812}
813
814#[no_mangle]
815pub unsafe extern "C" fn libinput_event_touch_get_x(event: *const LibinputEvent) -> f64 {
816    if event.is_null() {
817        return 0.0;
818    }
819    match &(*event).payload {
820        EventPayload::TouchDown(e) | EventPayload::TouchMotion(e) => {
821            let device = (*event).device;
822            if !device.is_null() {
823                if let (Some((minimum, _)), Some(resolution)) =
824                    ((*device).abs_x_range, (*device).abs_x_resolution)
825                {
826                    return (e.x - minimum as f64) / resolution as f64;
827                }
828            }
829            e.x
830        }
831        _ => 0.0,
832    }
833}
834
835#[no_mangle]
836pub unsafe extern "C" fn libinput_event_touch_get_y(event: *const LibinputEvent) -> f64 {
837    if event.is_null() {
838        return 0.0;
839    }
840    match &(*event).payload {
841        EventPayload::TouchDown(e) | EventPayload::TouchMotion(e) => {
842            let device = (*event).device;
843            if !device.is_null() {
844                if let (Some((minimum, _)), Some(resolution)) =
845                    ((*device).abs_y_range, (*device).abs_y_resolution)
846                {
847                    return (e.y - minimum as f64) / resolution as f64;
848                }
849            }
850            e.y
851        }
852        _ => 0.0,
853    }
854}
855
856unsafe fn transformed_touch_coordinates(event: *const LibinputEvent) -> Option<(f64, f64)> {
857    let touch = match &(*event).payload {
858        EventPayload::TouchDown(e) | EventPayload::TouchMotion(e) => e,
859        _ => return None,
860    };
861    let device = (*event).device;
862    if device.is_null() {
863        return None;
864    }
865    let ((xmin, xmax), (ymin, ymax)) = ((*device).abs_x_range?, (*device).abs_y_range?);
866    let x_span = (xmax as i64 - xmin as i64 + 1).max(1) as f64;
867    let y_span = (ymax as i64 - ymin as i64 + 1).max(1) as f64;
868    let x = (touch.x - xmin as f64) / x_span;
869    let y = (touch.y - ymin as f64) / y_span;
870    let matrix = (*device).calibration;
871    Some((
872        matrix[0] as f64 * x + matrix[1] as f64 * y + matrix[2] as f64,
873        matrix[3] as f64 * x + matrix[4] as f64 * y + matrix[5] as f64,
874    ))
875}
876
877#[no_mangle]
878pub unsafe extern "C" fn libinput_event_touch_get_x_transformed(
879    event: *const LibinputEvent,
880    width: u32,
881) -> f64 {
882    if event.is_null() {
883        return 0.0;
884    }
885    transformed_touch_coordinates(event)
886        .map(|(x, _)| x * width as f64)
887        .unwrap_or(0.0)
888}
889
890#[no_mangle]
891pub unsafe extern "C" fn libinput_event_touch_get_y_transformed(
892    event: *const LibinputEvent,
893    height: u32,
894) -> f64 {
895    if event.is_null() {
896        return 0.0;
897    }
898    transformed_touch_coordinates(event)
899        .map(|(_, y)| y * height as f64)
900        .unwrap_or(0.0)
901}
902
903// ---------------------------------------------------------------------------
904// Gesture event accessors
905// ---------------------------------------------------------------------------
906
907#[no_mangle]
908pub unsafe extern "C" fn libinput_event_get_gesture_event(
909    event: *mut LibinputEvent,
910) -> *mut LibinputEvent {
911    if event.is_null() {
912        return std::ptr::null_mut();
913    }
914    match (*event).event_type {
915        LibinputEventType::LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN
916        | LibinputEventType::LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE
917        | LibinputEventType::LIBINPUT_EVENT_GESTURE_SWIPE_END
918        | LibinputEventType::LIBINPUT_EVENT_GESTURE_PINCH_BEGIN
919        | LibinputEventType::LIBINPUT_EVENT_GESTURE_PINCH_UPDATE
920        | LibinputEventType::LIBINPUT_EVENT_GESTURE_PINCH_END
921        | LibinputEventType::LIBINPUT_EVENT_GESTURE_HOLD_BEGIN
922        | LibinputEventType::LIBINPUT_EVENT_GESTURE_HOLD_END => event,
923        _ => std::ptr::null_mut(),
924    }
925}
926
927#[no_mangle]
928pub unsafe extern "C" fn libinput_event_gesture_get_base_event(
929    event: *mut LibinputEvent,
930) -> *mut LibinputEvent {
931    event
932}
933
934#[no_mangle]
935pub unsafe extern "C" fn libinput_event_gesture_get_time(event: *const LibinputEvent) -> u32 {
936    if event.is_null() {
937        return 0;
938    }
939    match &(*event).payload {
940        EventPayload::GestureSwipeBegin(e)
941        | EventPayload::GestureSwipeUpdate(e)
942        | EventPayload::GestureSwipeEnd(e)
943        | EventPayload::GesturePinchBegin(e)
944        | EventPayload::GesturePinchUpdate(e)
945        | EventPayload::GesturePinchEnd(e)
946        | EventPayload::GestureHoldBegin(e)
947        | EventPayload::GestureHoldEnd(e) => (e.time_usec / 1000) as u32,
948        _ => 0,
949    }
950}
951
952#[no_mangle]
953pub unsafe extern "C" fn libinput_event_gesture_get_time_usec(event: *const LibinputEvent) -> u64 {
954    if event.is_null() {
955        return 0;
956    }
957    match &(*event).payload {
958        EventPayload::GestureSwipeBegin(e)
959        | EventPayload::GestureSwipeUpdate(e)
960        | EventPayload::GestureSwipeEnd(e)
961        | EventPayload::GesturePinchBegin(e)
962        | EventPayload::GesturePinchUpdate(e)
963        | EventPayload::GesturePinchEnd(e)
964        | EventPayload::GestureHoldBegin(e)
965        | EventPayload::GestureHoldEnd(e) => e.time_usec,
966        _ => 0,
967    }
968}
969
970#[no_mangle]
971pub unsafe extern "C" fn libinput_event_gesture_get_finger_count(
972    event: *const LibinputEvent,
973) -> libc::c_int {
974    if event.is_null() {
975        return 0;
976    }
977    match &(*event).payload {
978        EventPayload::GestureSwipeBegin(e)
979        | EventPayload::GestureSwipeUpdate(e)
980        | EventPayload::GestureSwipeEnd(e)
981        | EventPayload::GesturePinchBegin(e)
982        | EventPayload::GesturePinchUpdate(e)
983        | EventPayload::GesturePinchEnd(e)
984        | EventPayload::GestureHoldBegin(e)
985        | EventPayload::GestureHoldEnd(e) => e.finger_count,
986        _ => 0,
987    }
988}
989
990#[no_mangle]
991pub unsafe extern "C" fn libinput_event_gesture_get_dx(event: *const LibinputEvent) -> f64 {
992    if event.is_null() {
993        return 0.0;
994    }
995    match &(*event).payload {
996        EventPayload::GestureSwipeUpdate(e) | EventPayload::GesturePinchUpdate(e) => e.dx,
997        _ => 0.0,
998    }
999}
1000
1001#[no_mangle]
1002pub unsafe extern "C" fn libinput_event_gesture_get_dy(event: *const LibinputEvent) -> f64 {
1003    if event.is_null() {
1004        return 0.0;
1005    }
1006    match &(*event).payload {
1007        EventPayload::GestureSwipeUpdate(e) | EventPayload::GesturePinchUpdate(e) => e.dy,
1008        _ => 0.0,
1009    }
1010}
1011
1012#[no_mangle]
1013pub unsafe extern "C" fn libinput_event_gesture_get_dx_unaccelerated(
1014    event: *const LibinputEvent,
1015) -> f64 {
1016    if event.is_null() {
1017        return 0.0;
1018    }
1019    match &(*event).payload {
1020        EventPayload::GestureSwipeUpdate(e) | EventPayload::GesturePinchUpdate(e) => e.dx_unaccel,
1021        _ => 0.0,
1022    }
1023}
1024
1025#[no_mangle]
1026pub unsafe extern "C" fn libinput_event_gesture_get_dy_unaccelerated(
1027    event: *const LibinputEvent,
1028) -> f64 {
1029    if event.is_null() {
1030        return 0.0;
1031    }
1032    match &(*event).payload {
1033        EventPayload::GestureSwipeUpdate(e) | EventPayload::GesturePinchUpdate(e) => e.dy_unaccel,
1034        _ => 0.0,
1035    }
1036}
1037
1038#[no_mangle]
1039pub unsafe extern "C" fn libinput_event_gesture_get_scale(event: *const LibinputEvent) -> f64 {
1040    if event.is_null() {
1041        return 1.0;
1042    }
1043    match &(*event).payload {
1044        EventPayload::GesturePinchUpdate(e) | EventPayload::GesturePinchEnd(e) => e.scale,
1045        _ => 1.0,
1046    }
1047}
1048
1049#[no_mangle]
1050pub unsafe extern "C" fn libinput_event_gesture_get_angle_delta(
1051    event: *const LibinputEvent,
1052) -> f64 {
1053    if event.is_null() {
1054        return 0.0;
1055    }
1056    match &(*event).payload {
1057        EventPayload::GesturePinchUpdate(e) => e.angle,
1058        _ => 0.0,
1059    }
1060}
1061
1062#[no_mangle]
1063pub unsafe extern "C" fn libinput_event_gesture_get_cancelled(
1064    event: *const LibinputEvent,
1065) -> libc::c_int {
1066    if event.is_null() {
1067        return 0;
1068    }
1069    match &(*event).payload {
1070        EventPayload::GestureSwipeEnd(e)
1071        | EventPayload::GesturePinchEnd(e)
1072        | EventPayload::GestureHoldEnd(e) => e.cancelled as libc::c_int,
1073        _ => 0,
1074    }
1075}
1076
1077// ---------------------------------------------------------------------------
1078// Switch event accessors
1079// ---------------------------------------------------------------------------
1080
1081#[no_mangle]
1082pub unsafe extern "C" fn libinput_event_get_switch_event(
1083    event: *mut LibinputEvent,
1084) -> *mut LibinputEvent {
1085    if event.is_null() {
1086        return std::ptr::null_mut();
1087    }
1088    if (*event).event_type == LibinputEventType::LIBINPUT_EVENT_SWITCH_TOGGLE {
1089        event
1090    } else {
1091        std::ptr::null_mut()
1092    }
1093}
1094
1095#[no_mangle]
1096pub unsafe extern "C" fn libinput_event_switch_get_base_event(
1097    event: *mut LibinputEvent,
1098) -> *mut LibinputEvent {
1099    event
1100}
1101
1102#[no_mangle]
1103pub unsafe extern "C" fn libinput_event_switch_get_switch(event: *const LibinputEvent) -> u32 {
1104    if event.is_null() {
1105        return 0;
1106    }
1107    if let EventPayload::SwitchToggle(e) = &(*event).payload {
1108        e.switch
1109    } else {
1110        0
1111    }
1112}
1113
1114#[no_mangle]
1115pub unsafe extern "C" fn libinput_event_switch_get_switch_state(
1116    event: *const LibinputEvent,
1117) -> u32 {
1118    if event.is_null() {
1119        return 0;
1120    }
1121    if let EventPayload::SwitchToggle(e) = &(*event).payload {
1122        e.state
1123    } else {
1124        0
1125    }
1126}
1127
1128// ---------------------------------------------------------------------------
1129// Device info
1130// ---------------------------------------------------------------------------
1131
1132#[no_mangle]
1133pub unsafe extern "C" fn libinput_device_ref(dev: *mut LibinputDevice) -> *mut LibinputDevice {
1134    if dev.is_null() {
1135        return std::ptr::null_mut();
1136    }
1137    let current = (*dev)
1138        .refcount
1139        .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1140    (*dev).abi.refcount = current + 1;
1141    dev
1142}
1143
1144#[no_mangle]
1145pub unsafe extern "C" fn libinput_device_unref(dev: *mut LibinputDevice) -> *mut LibinputDevice {
1146    if dev.is_null() {
1147        return std::ptr::null_mut();
1148    }
1149    let remaining = (*dev)
1150        .refcount
1151        .fetch_sub(1, std::sync::atomic::Ordering::SeqCst)
1152        - 1;
1153    (*dev).abi.refcount = remaining;
1154    if remaining <= 0 {
1155        drop(Box::from_raw(dev));
1156        std::ptr::null_mut()
1157    } else {
1158        dev
1159    }
1160}
1161
1162#[no_mangle]
1163pub unsafe extern "C" fn libinput_device_get_name(
1164    dev: *const LibinputDevice,
1165) -> *const libc::c_char {
1166    if dev.is_null() {
1167        return std::ptr::null();
1168    }
1169    (*dev).name.as_ptr()
1170}
1171
1172#[no_mangle]
1173pub unsafe extern "C" fn libinput_device_get_sysname(
1174    dev: *const LibinputDevice,
1175) -> *const libc::c_char {
1176    if dev.is_null() {
1177        return std::ptr::null();
1178    }
1179    (*dev).sysname.as_ptr()
1180}
1181
1182#[no_mangle]
1183pub unsafe extern "C" fn libinput_device_get_output_name(
1184    dev: *const LibinputDevice,
1185) -> *const libc::c_char {
1186    if dev.is_null() {
1187        return std::ptr::null();
1188    }
1189    (*dev)
1190        .output_name
1191        .as_ref()
1192        .map_or(std::ptr::null(), |name| name.as_ptr())
1193}
1194
1195#[no_mangle]
1196pub unsafe extern "C" fn libinput_device_get_id_vendor(dev: *const LibinputDevice) -> libc::c_uint {
1197    if dev.is_null() {
1198        return 0;
1199    }
1200    (*dev).vendor_id
1201}
1202
1203#[no_mangle]
1204pub unsafe extern "C" fn libinput_device_get_id_product(
1205    dev: *const LibinputDevice,
1206) -> libc::c_uint {
1207    if dev.is_null() {
1208        return 0;
1209    }
1210    (*dev).product_id
1211}
1212
1213#[no_mangle]
1214pub unsafe extern "C" fn libinput_device_get_context(
1215    dev: *const LibinputDevice,
1216) -> *mut LibinputContext {
1217    if dev.is_null() {
1218        return std::ptr::null_mut();
1219    }
1220    (*dev).context
1221}
1222
1223#[no_mangle]
1224pub unsafe extern "C" fn libinput_device_touch_get_touch_count(
1225    dev: *const LibinputDevice,
1226) -> libc::c_int {
1227    if dev.is_null() || !(*dev).has_touch {
1228        return -1;
1229    }
1230    (*dev).touch_count
1231}
1232
1233#[no_mangle]
1234pub unsafe extern "C" fn libinput_device_has_capability(
1235    dev: *const LibinputDevice,
1236    capability: u32,
1237) -> libc::c_int {
1238    if dev.is_null() {
1239        return 0;
1240    }
1241    let has = match capability {
1242        0 => (*dev).has_keyboard,
1243        1 => (*dev).has_pointer,
1244        2 => (*dev).has_touch,
1245        3 => (*dev).has_tablet,
1246        4 => (*dev).has_tablet_pad,
1247        5 => (*dev).has_gesture,
1248        6 => (*dev).has_switch,
1249        _ => false,
1250    };
1251    has as libc::c_int
1252}
1253
1254// ---------------------------------------------------------------------------
1255// Device configuration — tap
1256// ---------------------------------------------------------------------------
1257
1258#[no_mangle]
1259pub unsafe extern "C" fn libinput_device_config_tap_get_finger_count(
1260    dev: *const LibinputDevice,
1261) -> libc::c_int {
1262    if dev.is_null() {
1263        return 0;
1264    }
1265    (*dev).tap_finger_count
1266}
1267
1268#[no_mangle]
1269pub unsafe extern "C" fn libinput_device_config_tap_set_enabled(
1270    dev: *mut LibinputDevice,
1271    enabled: u32,
1272) -> u32 {
1273    if dev.is_null() {
1274        return 1;
1275    }
1276    if enabled > 1 {
1277        return 2;
1278    }
1279    if (*dev).tap_finger_count == 0 {
1280        return if enabled == 0 { 0 } else { 1 };
1281    }
1282    (*dev).tap_enabled = enabled != 0;
1283    0
1284}
1285
1286#[no_mangle]
1287pub unsafe extern "C" fn libinput_device_config_tap_get_enabled(dev: *const LibinputDevice) -> u32 {
1288    if dev.is_null() {
1289        return 0;
1290    }
1291    (*dev).tap_enabled as u32
1292}
1293
1294#[no_mangle]
1295pub unsafe extern "C" fn libinput_device_config_tap_get_default_enabled(
1296    dev: *const LibinputDevice,
1297) -> u32 {
1298    if dev.is_null() {
1299        return 0;
1300    }
1301    (*dev).tap_default_enabled as u32
1302}
1303
1304#[no_mangle]
1305pub unsafe extern "C" fn libinput_device_config_tap_set_drag_enabled(
1306    dev: *mut LibinputDevice,
1307    enabled: u32,
1308) -> u32 {
1309    if dev.is_null() {
1310        return 1;
1311    }
1312    if enabled > 1 {
1313        return 2;
1314    }
1315    if (*dev).tap_finger_count == 0 {
1316        return if enabled == 0 { 0 } else { 1 };
1317    }
1318    (*dev).tap_drag_enabled = enabled != 0;
1319    0
1320}
1321
1322#[no_mangle]
1323pub unsafe extern "C" fn libinput_device_config_tap_get_drag_enabled(
1324    dev: *const LibinputDevice,
1325) -> u32 {
1326    if dev.is_null() {
1327        return 0;
1328    }
1329    (*dev).tap_drag_enabled as u32
1330}
1331
1332#[no_mangle]
1333pub unsafe extern "C" fn libinput_device_config_tap_get_default_drag_enabled(
1334    dev: *const LibinputDevice,
1335) -> u32 {
1336    if dev.is_null() {
1337        return 0;
1338    }
1339    ((*dev).tap_finger_count != 0) as u32
1340}
1341
1342#[no_mangle]
1343pub unsafe extern "C" fn libinput_device_config_tap_set_drag_lock_enabled(
1344    dev: *mut LibinputDevice,
1345    enabled: u32,
1346) -> u32 {
1347    if dev.is_null() {
1348        return 1;
1349    }
1350    if enabled > 2 {
1351        return 2;
1352    }
1353    if (*dev).tap_finger_count == 0 {
1354        return if enabled == 0 { 0 } else { 1 };
1355    }
1356    (*dev).tap_drag_lock_enabled = if enabled == 1 { 1 } else { enabled };
1357    0
1358}
1359
1360#[no_mangle]
1361pub unsafe extern "C" fn libinput_device_config_tap_get_drag_lock_enabled(
1362    dev: *const LibinputDevice,
1363) -> u32 {
1364    if dev.is_null() {
1365        return 0;
1366    }
1367    (*dev).tap_drag_lock_enabled
1368}
1369
1370/// Button map: 0 = LRM (default), 1 = LMR
1371#[no_mangle]
1372pub unsafe extern "C" fn libinput_device_config_tap_set_button_map(
1373    dev: *mut LibinputDevice,
1374    map: u32,
1375) -> u32 {
1376    if dev.is_null() {
1377        return 1;
1378    }
1379    if map > 1 {
1380        return 2;
1381    }
1382    if (*dev).tap_finger_count == 0 {
1383        return 1;
1384    }
1385    (*dev).tap_button_map = map;
1386    0
1387}
1388
1389#[no_mangle]
1390pub unsafe extern "C" fn libinput_device_config_tap_get_button_map(
1391    dev: *const LibinputDevice,
1392) -> u32 {
1393    if dev.is_null() {
1394        return 0;
1395    }
1396    (*dev).tap_button_map
1397}
1398
1399#[no_mangle]
1400pub unsafe extern "C" fn libinput_device_config_tap_get_default_button_map(
1401    _dev: *const LibinputDevice,
1402) -> u32 {
1403    0
1404} // LIBINPUT_CONFIG_TAP_MAP_LRM
1405
1406#[no_mangle]
1407pub unsafe extern "C" fn libinput_device_config_3fg_drag_get_finger_count(
1408    dev: *const LibinputDevice,
1409) -> libc::c_int {
1410    if dev.is_null() || !(*dev).has_gesture {
1411        return 0;
1412    }
1413    (*dev).mt_slot_count
1414}
1415
1416#[no_mangle]
1417pub unsafe extern "C" fn libinput_device_config_3fg_drag_set_enabled(
1418    dev: *mut LibinputDevice,
1419    enable: u32,
1420) -> u32 {
1421    if dev.is_null() {
1422        return 1;
1423    }
1424    if !matches!(enable, 0..=2) {
1425        return 2;
1426    }
1427    if libinput_device_config_3fg_drag_get_finger_count(dev) < 3 {
1428        return 1;
1429    }
1430    (*dev).drag_3fg_enabled = enable;
1431    0
1432}
1433
1434#[no_mangle]
1435pub unsafe extern "C" fn libinput_device_config_3fg_drag_get_enabled(
1436    dev: *const LibinputDevice,
1437) -> u32 {
1438    if dev.is_null() {
1439        return 0;
1440    }
1441    (*dev).drag_3fg_enabled
1442}
1443
1444#[no_mangle]
1445pub unsafe extern "C" fn libinput_device_config_3fg_drag_get_default_enabled(
1446    _dev: *const LibinputDevice,
1447) -> u32 {
1448    0
1449}
1450
1451// ---------------------------------------------------------------------------
1452// Device configuration — pointer acceleration
1453// ---------------------------------------------------------------------------
1454
1455#[no_mangle]
1456pub unsafe extern "C" fn libinput_config_accel_create(profile: u32) -> *mut libc::c_void {
1457    if !matches!(profile, 1 | 2 | 4) {
1458        return std::ptr::null_mut();
1459    }
1460    Box::into_raw(Box::new(crate::ffi_types::AccelConfig::new(profile))) as *mut libc::c_void
1461}
1462
1463#[no_mangle]
1464pub unsafe extern "C" fn libinput_config_accel_destroy(accel_config: *mut libc::c_void) {
1465    if !accel_config.is_null() {
1466        drop(Box::from_raw(
1467            accel_config as *mut crate::ffi_types::AccelConfig,
1468        ));
1469    }
1470}
1471
1472#[no_mangle]
1473pub unsafe extern "C" fn libinput_config_accel_set_points(
1474    accel_config: *mut libc::c_void,
1475    accel_type: u32,
1476    step: f64,
1477    npoints: libc::size_t,
1478    points: *const f64,
1479) -> u32 {
1480    if accel_config.is_null()
1481        || points.is_null()
1482        || !step.is_finite()
1483        || step <= 0.0
1484        || step > 10_000.0
1485        || !(2..=64).contains(&npoints)
1486    {
1487        return 2;
1488    }
1489    let config = &mut *(accel_config as *mut crate::ffi_types::AccelConfig);
1490    if config.profile != 4 || !matches!(accel_type, 0..=2) {
1491        return 2;
1492    }
1493    let points = std::slice::from_raw_parts(points, npoints);
1494    if points
1495        .iter()
1496        .any(|point| !point.is_finite() || *point < 0.0 || *point > 10_000.0)
1497    {
1498        return 2;
1499    }
1500    let curve = crate::ffi_types::AccelCurve::new(step, points.to_vec());
1501    match accel_type {
1502        0 => config.fallback = Some(curve),
1503        1 => config.motion = Some(curve),
1504        2 => config.scroll = Some(curve),
1505        _ => unreachable!(),
1506    }
1507    0
1508}
1509
1510#[no_mangle]
1511pub unsafe extern "C" fn libinput_device_config_accel_apply(
1512    dev: *mut LibinputDevice,
1513    accel_config: *mut libc::c_void,
1514) -> u32 {
1515    if dev.is_null() || accel_config.is_null() {
1516        return 2;
1517    }
1518    if !(*dev).accel_available {
1519        return 1;
1520    }
1521    let config = &*(accel_config as *const crate::ffi_types::AccelConfig);
1522    let profile = config.profile;
1523    if profile & libinput_device_config_accel_get_profiles(dev) == 0 {
1524        return 1;
1525    }
1526    (*dev).accel_profile = profile;
1527    (*dev).accel_speed = 0.0;
1528    (*dev).accel_custom = (profile == 4).then(|| config.clone());
1529    0
1530}
1531
1532#[no_mangle]
1533pub unsafe extern "C" fn libinput_device_config_accel_is_available(
1534    dev: *const LibinputDevice,
1535) -> libc::c_int {
1536    if dev.is_null() {
1537        return 0;
1538    }
1539    (*dev).accel_available as libc::c_int
1540}
1541
1542#[no_mangle]
1543pub unsafe extern "C" fn libinput_device_config_accel_set_speed(
1544    dev: *mut LibinputDevice,
1545    speed: f64,
1546) -> u32 {
1547    if dev.is_null() {
1548        return 1;
1549    }
1550    if !speed.is_finite() || !(-1.0..=1.0).contains(&speed) {
1551        return 2;
1552    }
1553    if !(*dev).accel_available {
1554        return 1;
1555    }
1556    (*dev).accel_speed = speed;
1557    0
1558}
1559
1560#[no_mangle]
1561pub unsafe extern "C" fn libinput_device_config_accel_get_speed(dev: *const LibinputDevice) -> f64 {
1562    if dev.is_null() {
1563        return 0.0;
1564    }
1565    (*dev).accel_speed
1566}
1567
1568#[no_mangle]
1569pub unsafe extern "C" fn libinput_device_config_accel_get_default_speed(
1570    _dev: *const LibinputDevice,
1571) -> f64 {
1572    0.0
1573}
1574
1575#[no_mangle]
1576pub unsafe extern "C" fn libinput_device_config_accel_get_profiles(
1577    dev: *const LibinputDevice,
1578) -> u32 {
1579    if dev.is_null() {
1580        return 0;
1581    }
1582    if (*dev).accel_available
1583        && !((*dev).has_tablet && !(*dev).has_gesture && !(*dev).has_tablet_pad)
1584    {
1585        0b111
1586    } else {
1587        0
1588    }
1589}
1590
1591#[no_mangle]
1592pub unsafe extern "C" fn libinput_device_config_accel_set_profile(
1593    dev: *mut LibinputDevice,
1594    profile: u32,
1595) -> u32 {
1596    if dev.is_null() {
1597        return 1;
1598    }
1599    if profile == 0 || profile & !0b111 != 0 || profile.count_ones() != 1 {
1600        return 2;
1601    }
1602    if profile & libinput_device_config_accel_get_profiles(dev) == 0 {
1603        return 1;
1604    }
1605    (*dev).accel_profile = profile;
1606    (*dev).accel_custom = if profile == 4 {
1607        Some(crate::ffi_types::AccelConfig::new(profile))
1608    } else {
1609        None
1610    };
1611    0
1612}
1613
1614#[no_mangle]
1615pub unsafe extern "C" fn libinput_device_config_accel_get_profile(
1616    dev: *const LibinputDevice,
1617) -> u32 {
1618    if dev.is_null() {
1619        return 0;
1620    }
1621    if (*dev).accel_available
1622        && !((*dev).has_tablet && !(*dev).has_gesture && !(*dev).has_tablet_pad)
1623    {
1624        (*dev).accel_profile
1625    } else {
1626        0
1627    }
1628}
1629
1630#[no_mangle]
1631pub unsafe extern "C" fn libinput_device_config_accel_get_default_profile(
1632    dev: *const LibinputDevice,
1633) -> u32 {
1634    if dev.is_null()
1635        || !(*dev).accel_available
1636        || ((*dev).has_tablet && !(*dev).has_gesture && !(*dev).has_tablet_pad)
1637    {
1638        0
1639    } else {
1640        2
1641    }
1642}
1643
1644// ---------------------------------------------------------------------------
1645// Device configuration — natural scroll
1646// ---------------------------------------------------------------------------
1647
1648#[no_mangle]
1649pub unsafe extern "C" fn libinput_device_config_scroll_has_natural_scroll(
1650    dev: *const LibinputDevice,
1651) -> libc::c_int {
1652    if dev.is_null() {
1653        return 0;
1654    }
1655    (*dev).has_pointer as libc::c_int
1656}
1657
1658#[no_mangle]
1659pub unsafe extern "C" fn libinput_device_config_scroll_set_natural_scroll_enabled(
1660    dev: *mut LibinputDevice,
1661    enabled: libc::c_int,
1662) -> u32 {
1663    if dev.is_null() || !(*dev).has_pointer {
1664        return 1;
1665    }
1666    (*dev).natural_scroll = enabled != 0;
1667    0
1668}
1669
1670#[no_mangle]
1671pub unsafe extern "C" fn libinput_device_config_scroll_get_natural_scroll_enabled(
1672    dev: *const LibinputDevice,
1673) -> libc::c_int {
1674    if dev.is_null() {
1675        return 0;
1676    }
1677    (*dev).natural_scroll as libc::c_int
1678}
1679
1680#[no_mangle]
1681pub unsafe extern "C" fn libinput_device_config_scroll_get_default_natural_scroll_enabled(
1682    dev: *const LibinputDevice,
1683) -> libc::c_int {
1684    if dev.is_null() {
1685        return 0;
1686    }
1687    ((*dev).scroll_methods & 2 != 0 && (*dev).vendor_id == 0x05ac) as libc::c_int
1688}
1689
1690// ---------------------------------------------------------------------------
1691// Device configuration — left-handed
1692// ---------------------------------------------------------------------------
1693
1694#[no_mangle]
1695pub unsafe extern "C" fn libinput_device_config_left_handed_is_available(
1696    dev: *const LibinputDevice,
1697) -> libc::c_int {
1698    if dev.is_null() {
1699        return 0;
1700    }
1701    (*dev).left_handed_available as libc::c_int
1702}
1703
1704#[no_mangle]
1705pub unsafe extern "C" fn libinput_device_config_left_handed_set(
1706    dev: *mut LibinputDevice,
1707    enabled: libc::c_int,
1708) -> u32 {
1709    if dev.is_null() || !(*dev).left_handed_available {
1710        return 1;
1711    }
1712    (*dev).left_handed = enabled != 0;
1713    0
1714}
1715
1716#[no_mangle]
1717pub unsafe extern "C" fn libinput_device_config_left_handed_get(
1718    dev: *const LibinputDevice,
1719) -> libc::c_int {
1720    if dev.is_null() {
1721        return 0;
1722    }
1723    (*dev).left_handed as libc::c_int
1724}
1725
1726#[no_mangle]
1727pub unsafe extern "C" fn libinput_device_config_left_handed_get_default(
1728    _dev: *const LibinputDevice,
1729) -> libc::c_int {
1730    0
1731}
1732
1733// ---------------------------------------------------------------------------
1734// Device configuration — scroll method
1735// ---------------------------------------------------------------------------
1736
1737#[no_mangle]
1738pub unsafe extern "C" fn libinput_device_config_scroll_get_methods(
1739    dev: *const LibinputDevice,
1740) -> u32 {
1741    if dev.is_null() {
1742        return 0;
1743    }
1744    (*dev).scroll_methods
1745}
1746
1747#[no_mangle]
1748pub unsafe extern "C" fn libinput_device_config_scroll_set_method(
1749    dev: *mut LibinputDevice,
1750    method: u32,
1751) -> u32 {
1752    if dev.is_null() {
1753        return 1;
1754    }
1755    if !matches!(method, 0 | 1 | 2 | 4) {
1756        return 2;
1757    }
1758    if method != 0 && method & (*dev).scroll_methods == 0 {
1759        return 1;
1760    }
1761    if (*dev).scroll_method == method {
1762        return 0;
1763    }
1764    let ctx = (*dev).context;
1765    if !ctx.is_null() {
1766        let mut events = std::collections::VecDeque::new();
1767        if let Ok(mut backend) = (*ctx).backend.try_lock() {
1768            backend.stop_scroll_for_device(ctx, dev, &mut events);
1769        }
1770        enqueue_events(ctx, events);
1771    }
1772    (*dev).scroll_method = method;
1773    0
1774}
1775
1776#[no_mangle]
1777pub unsafe extern "C" fn libinput_device_config_scroll_get_method(
1778    dev: *const LibinputDevice,
1779) -> u32 {
1780    if dev.is_null() {
1781        return 0;
1782    }
1783    (*dev).scroll_method
1784}
1785
1786#[no_mangle]
1787pub unsafe extern "C" fn libinput_device_config_scroll_get_default_method(
1788    dev: *const LibinputDevice,
1789) -> u32 {
1790    if dev.is_null() {
1791        0
1792    } else {
1793        (*dev).scroll_default_method
1794    }
1795}
1796
1797#[no_mangle]
1798pub unsafe extern "C" fn libinput_device_config_scroll_set_button(
1799    dev: *mut LibinputDevice,
1800    button: u32,
1801) -> u32 {
1802    if dev.is_null() || !(*dev).supports_button_scroll {
1803        return 1;
1804    }
1805    if button != 0
1806        && match u16::try_from(button) {
1807            Ok(button) => !(*dev).event_codes.contains(&button),
1808            Err(_) => true,
1809        }
1810    {
1811        return 2;
1812    }
1813    (*dev).scroll_button = button;
1814    0
1815}
1816
1817#[no_mangle]
1818pub unsafe extern "C" fn libinput_device_config_scroll_get_button(
1819    dev: *const LibinputDevice,
1820) -> u32 {
1821    if dev.is_null() || !(*dev).supports_button_scroll {
1822        0
1823    } else {
1824        (*dev).scroll_button
1825    }
1826}
1827
1828#[no_mangle]
1829pub unsafe extern "C" fn libinput_device_config_scroll_set_button_lock(
1830    dev: *mut LibinputDevice,
1831    state: u32,
1832) -> u32 {
1833    if dev.is_null() || !(*dev).supports_button_scroll {
1834        return 1;
1835    }
1836    if state > 1 {
1837        return 2;
1838    }
1839    (*dev).scroll_button_lock = state;
1840    0
1841}
1842
1843#[no_mangle]
1844pub unsafe extern "C" fn libinput_device_config_scroll_get_button_lock(
1845    dev: *const LibinputDevice,
1846) -> u32 {
1847    if dev.is_null() || !(*dev).supports_button_scroll {
1848        0
1849    } else {
1850        (*dev).scroll_button_lock
1851    }
1852}
1853
1854#[no_mangle]
1855pub unsafe extern "C" fn libinput_device_config_scroll_get_default_button_lock(
1856    _dev: *const LibinputDevice,
1857) -> u32 {
1858    0
1859}
1860
1861// ---------------------------------------------------------------------------
1862// Device configuration — click method
1863// ---------------------------------------------------------------------------
1864
1865#[no_mangle]
1866pub unsafe extern "C" fn libinput_device_config_click_get_methods(
1867    dev: *const LibinputDevice,
1868) -> u32 {
1869    if dev.is_null() {
1870        return 0;
1871    }
1872    (*dev).click_methods
1873}
1874
1875#[no_mangle]
1876pub unsafe extern "C" fn libinput_device_config_click_set_method(
1877    dev: *mut LibinputDevice,
1878    method: u32,
1879) -> u32 {
1880    if dev.is_null() {
1881        return 1;
1882    }
1883    if !matches!(method, 0..=2) {
1884        return 2;
1885    }
1886    if method != 0 && method & (*dev).click_methods == 0 {
1887        return 1;
1888    }
1889    (*dev).click_method = method;
1890    0
1891}
1892
1893#[no_mangle]
1894pub unsafe extern "C" fn libinput_device_config_click_get_method(
1895    dev: *const LibinputDevice,
1896) -> u32 {
1897    if dev.is_null() {
1898        return 0;
1899    }
1900    (*dev).click_method
1901}
1902
1903#[no_mangle]
1904pub unsafe extern "C" fn libinput_device_config_click_get_default_method(
1905    dev: *const LibinputDevice,
1906) -> u32 {
1907    if dev.is_null() {
1908        0
1909    } else {
1910        (*dev).click_default_method
1911    }
1912}
1913
1914#[no_mangle]
1915pub unsafe extern "C" fn libinput_device_config_click_set_clickfinger_button_map(
1916    dev: *mut LibinputDevice,
1917    map: u32,
1918) -> u32 {
1919    if dev.is_null() {
1920        return 1;
1921    }
1922    if !matches!(map, 0 | 1) {
1923        return 2;
1924    }
1925    if (*dev).click_methods & 2 == 0 {
1926        return 1;
1927    }
1928    (*dev).clickfinger_button_map = map;
1929    0
1930}
1931
1932#[no_mangle]
1933pub unsafe extern "C" fn libinput_device_config_click_get_clickfinger_button_map(
1934    dev: *const LibinputDevice,
1935) -> u32 {
1936    if dev.is_null() || (*dev).click_methods & 2 == 0 {
1937        0
1938    } else {
1939        (*dev).clickfinger_button_map
1940    }
1941}
1942
1943#[no_mangle]
1944pub unsafe extern "C" fn libinput_device_config_click_get_default_clickfinger_button_map(
1945    dev: *const LibinputDevice,
1946) -> u32 {
1947    if dev.is_null() || (*dev).click_methods & 2 == 0 {
1948        0
1949    } else {
1950        (*dev).clickfinger_default_button_map
1951    }
1952}
1953
1954// ---------------------------------------------------------------------------
1955// Device configuration — middle button emulation
1956// ---------------------------------------------------------------------------
1957
1958#[no_mangle]
1959pub unsafe extern "C" fn libinput_device_config_middle_emulation_is_available(
1960    dev: *const LibinputDevice,
1961) -> libc::c_int {
1962    if dev.is_null() {
1963        return 0;
1964    }
1965    (*dev).middle_emulation_available as libc::c_int
1966}
1967
1968#[no_mangle]
1969pub unsafe extern "C" fn libinput_device_config_middle_emulation_set_enabled(
1970    dev: *mut LibinputDevice,
1971    enabled: u32,
1972) -> u32 {
1973    if dev.is_null() {
1974        return 1;
1975    }
1976    if enabled > 1 {
1977        return 2;
1978    }
1979    if enabled == 1 && !(*dev).middle_emulation_available {
1980        return 1;
1981    }
1982    (*dev).middle_emulation = enabled != 0;
1983    0
1984}
1985
1986#[no_mangle]
1987pub unsafe extern "C" fn libinput_device_config_middle_emulation_get_enabled(
1988    dev: *const LibinputDevice,
1989) -> u32 {
1990    if dev.is_null() || !(*dev).middle_emulation_available {
1991        return 0;
1992    }
1993    (*dev).middle_emulation as u32
1994}
1995
1996#[no_mangle]
1997pub unsafe extern "C" fn libinput_device_config_middle_emulation_get_default_enabled(
1998    dev: *const LibinputDevice,
1999) -> u32 {
2000    if dev.is_null() || !(*dev).middle_emulation_available {
2001        return 0;
2002    }
2003    (*dev).middle_emulation_default as u32
2004}
2005
2006// ---------------------------------------------------------------------------
2007// Device configuration — disable-while-typing
2008// ---------------------------------------------------------------------------
2009
2010#[no_mangle]
2011pub unsafe extern "C" fn libinput_device_config_dwt_is_available(
2012    dev: *const LibinputDevice,
2013) -> libc::c_int {
2014    (!dev.is_null() && (*dev).dwt_available) as libc::c_int
2015}
2016
2017#[no_mangle]
2018pub unsafe extern "C" fn libinput_device_config_dwt_set_enabled(
2019    dev: *mut LibinputDevice,
2020    enabled: u32,
2021) -> u32 {
2022    if dev.is_null() {
2023        return 1;
2024    }
2025    if !matches!(enabled, 0 | 1) {
2026        return 2;
2027    }
2028    if !(*dev).dwt_available {
2029        return if enabled == 0 { 0 } else { 1 };
2030    }
2031    (*dev).dwt_enabled = enabled == 1;
2032    0
2033}
2034
2035#[no_mangle]
2036pub unsafe extern "C" fn libinput_device_config_dwt_get_enabled(dev: *const LibinputDevice) -> u32 {
2037    if dev.is_null() || !(*dev).dwt_available {
2038        return 0;
2039    }
2040    (*dev).dwt_enabled as u32
2041}
2042
2043#[no_mangle]
2044pub unsafe extern "C" fn libinput_device_config_dwt_get_default_enabled(
2045    dev: *const LibinputDevice,
2046) -> u32 {
2047    (!dev.is_null() && (*dev).dwt_available) as u32
2048}
2049
2050#[no_mangle]
2051pub unsafe extern "C" fn libinput_device_config_dwt_set_timeout(
2052    dev: *mut LibinputDevice,
2053    millis: u32,
2054) -> u32 {
2055    if dev.is_null() {
2056        return 1;
2057    }
2058    if millis == 0 {
2059        return 2;
2060    }
2061    if !(*dev).dwt_available {
2062        return 1;
2063    }
2064    if !(100..=5000).contains(&millis) {
2065        return 2;
2066    }
2067    (*dev).dwt_timeout = millis;
2068    0
2069}
2070
2071#[no_mangle]
2072pub unsafe extern "C" fn libinput_device_config_dwt_get_timeout(dev: *const LibinputDevice) -> u32 {
2073    if dev.is_null() || !(*dev).dwt_available {
2074        0
2075    } else {
2076        (*dev).dwt_timeout
2077    }
2078}
2079
2080#[no_mangle]
2081pub unsafe extern "C" fn libinput_device_config_dwt_get_default_timeout(
2082    dev: *const LibinputDevice,
2083) -> u32 {
2084    if !dev.is_null() && (*dev).dwt_available {
2085        500
2086    } else {
2087        0
2088    }
2089}
2090
2091#[no_mangle]
2092pub unsafe extern "C" fn libinput_device_config_dwtp_is_available(
2093    dev: *const LibinputDevice,
2094) -> libc::c_int {
2095    (!dev.is_null() && (*dev).dwtp_available) as libc::c_int
2096}
2097
2098#[no_mangle]
2099pub unsafe extern "C" fn libinput_device_config_dwtp_set_enabled(
2100    dev: *mut LibinputDevice,
2101    enabled: u32,
2102) -> u32 {
2103    if dev.is_null() {
2104        return 1;
2105    }
2106    if !matches!(enabled, 0 | 1) {
2107        return 2;
2108    }
2109    if !(*dev).dwtp_available {
2110        return if enabled == 0 { 0 } else { 1 };
2111    }
2112    (*dev).dwtp_enabled = enabled == 1;
2113    0
2114}
2115
2116#[no_mangle]
2117pub unsafe extern "C" fn libinput_device_config_dwtp_get_enabled(
2118    dev: *const LibinputDevice,
2119) -> u32 {
2120    if dev.is_null() || !(*dev).dwtp_available {
2121        0
2122    } else {
2123        (*dev).dwtp_enabled as u32
2124    }
2125}
2126
2127#[no_mangle]
2128pub unsafe extern "C" fn libinput_device_config_dwtp_get_default_enabled(
2129    dev: *const LibinputDevice,
2130) -> u32 {
2131    (!dev.is_null() && (*dev).dwtp_available) as u32
2132}
2133
2134#[no_mangle]
2135pub unsafe extern "C" fn libinput_device_config_dwtp_set_timeout(
2136    dev: *mut LibinputDevice,
2137    millis: u32,
2138) -> u32 {
2139    if dev.is_null() {
2140        return 1;
2141    }
2142    if millis == 0 {
2143        return 2;
2144    }
2145    if !(*dev).dwtp_available {
2146        return 1;
2147    }
2148    if !(100..=5000).contains(&millis) {
2149        return 2;
2150    }
2151    (*dev).dwtp_timeout = millis;
2152    0
2153}
2154
2155#[no_mangle]
2156pub unsafe extern "C" fn libinput_device_config_dwtp_get_timeout(
2157    dev: *const LibinputDevice,
2158) -> u32 {
2159    if dev.is_null() || !(*dev).dwtp_available {
2160        0
2161    } else {
2162        (*dev).dwtp_timeout
2163    }
2164}
2165
2166#[no_mangle]
2167pub unsafe extern "C" fn libinput_device_config_dwtp_get_default_timeout(
2168    dev: *const LibinputDevice,
2169) -> u32 {
2170    if !dev.is_null() && (*dev).dwtp_available {
2171        300
2172    } else {
2173        0
2174    }
2175}
2176
2177// ---------------------------------------------------------------------------
2178// Device configuration — calibration matrix
2179// ---------------------------------------------------------------------------
2180
2181#[no_mangle]
2182pub unsafe extern "C" fn libinput_device_config_calibration_has_matrix(
2183    dev: *const LibinputDevice,
2184) -> libc::c_int {
2185    if dev.is_null() {
2186        return 0;
2187    }
2188    (*dev).calibration_available as libc::c_int
2189}
2190
2191#[no_mangle]
2192pub unsafe extern "C" fn libinput_device_config_calibration_set_matrix(
2193    dev: *mut LibinputDevice,
2194    matrix: *const f32,
2195) -> u32 {
2196    if dev.is_null() || matrix.is_null() || !(*dev).calibration_available {
2197        return 1;
2198    }
2199    (*dev)
2200        .calibration
2201        .copy_from_slice(std::slice::from_raw_parts(matrix, 6));
2202    0
2203}
2204
2205#[no_mangle]
2206pub unsafe extern "C" fn libinput_device_config_calibration_get_matrix(
2207    dev: *const LibinputDevice,
2208    matrix: *mut f32,
2209) -> libc::c_int {
2210    if dev.is_null() || matrix.is_null() || !(*dev).calibration_available {
2211        return 0;
2212    }
2213    std::slice::from_raw_parts_mut(matrix, 6).copy_from_slice(&(*dev).calibration);
2214    ((*dev).calibration != [1.0_f32, 0.0, 0.0, 0.0, 1.0, 0.0]) as libc::c_int
2215}
2216
2217#[no_mangle]
2218pub unsafe extern "C" fn libinput_device_config_calibration_get_default_matrix(
2219    dev: *const LibinputDevice,
2220    matrix: *mut f32,
2221) -> libc::c_int {
2222    if dev.is_null() || matrix.is_null() || !(*dev).calibration_available {
2223        return 0;
2224    }
2225    std::slice::from_raw_parts_mut(matrix, 6).copy_from_slice(&(*dev).default_calibration);
2226    ((*dev).default_calibration != [1.0_f32, 0.0, 0.0, 0.0, 1.0, 0.0]) as libc::c_int
2227}
2228
2229// ---------------------------------------------------------------------------
2230// Seat
2231// ---------------------------------------------------------------------------
2232
2233#[no_mangle]
2234pub unsafe extern "C" fn libinput_device_get_seat(dev: *const LibinputDevice) -> *mut libc::c_void {
2235    if dev.is_null() {
2236        return std::ptr::null_mut();
2237    }
2238    (*dev).seat as *mut libc::c_void
2239}
2240
2241#[no_mangle]
2242pub unsafe extern "C" fn libinput_device_set_seat_logical_name(
2243    dev: *mut LibinputDevice,
2244    name: *const libc::c_char,
2245) -> libc::c_int {
2246    if dev.is_null() || name.is_null() || (*dev).seat.is_null() {
2247        return -1;
2248    }
2249    let name = CStr::from_ptr(name);
2250    if name.to_bytes().is_empty() || name.to_bytes().len() > 255 {
2251        return -1;
2252    }
2253    if (*(*dev).seat).logical_name.as_c_str() == name {
2254        return 0;
2255    }
2256    let Ok(logical_name) = std::ffi::CString::new(name.to_bytes()) else {
2257        return -1;
2258    };
2259    let ctx = (*dev).context;
2260    if ctx.is_null() {
2261        return -1;
2262    }
2263    let physical_name = (*(*dev).seat).physical_name.clone();
2264    let new_seat = (*ctx)
2265        .seats
2266        .iter()
2267        .copied()
2268        .find(|seat| {
2269            !seat.is_null()
2270                && (**seat).physical_name == physical_name
2271                && (**seat).logical_name == logical_name
2272        })
2273        .unwrap_or_else(|| {
2274            let seat = Box::into_raw(Box::new(LibinputSeat {
2275                physical_name,
2276                logical_name,
2277                refcount: std::sync::atomic::AtomicI32::new(1),
2278                user_data: std::ptr::null_mut(),
2279                context: ctx,
2280                button_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
2281                key_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
2282            }));
2283            (*ctx).seats.push(seat);
2284            seat
2285        });
2286
2287    let path = std::path::PathBuf::from((*dev).devnode.to_string_lossy().into_owned());
2288    let mut removed = std::collections::VecDeque::new();
2289    let mut added = Vec::new();
2290    let replaced = if let Ok(mut backend) = (*ctx).backend.lock() {
2291        if backend.remove_device(ctx, dev, &mut removed) {
2292            backend.try_open(ctx, &path, &mut added);
2293            true
2294        } else {
2295            false
2296        }
2297    } else {
2298        false
2299    };
2300    let Some(replacement) = added.first().map(|event| event.device) else {
2301        return -1;
2302    };
2303    if !replaced || replacement.is_null() {
2304        return -1;
2305    }
2306    (*ctx).devices.retain(|candidate| *candidate != dev);
2307    libinput_device_unref(dev);
2308    let old_seat = (*replacement).seat;
2309    if old_seat != new_seat {
2310        libinput_seat_ref(new_seat.cast());
2311        (*replacement).seat = new_seat;
2312        (*replacement).abi.seat = new_seat;
2313        libinput_seat_unref(old_seat.cast());
2314    }
2315    enqueue_events(ctx, removed);
2316    enqueue_events(ctx, added);
2317    (*ctx).signal_fd();
2318    0
2319}
2320
2321#[no_mangle]
2322pub unsafe extern "C" fn libinput_seat_get_physical_name(
2323    seat: *const libc::c_void,
2324) -> *const libc::c_char {
2325    if seat.is_null() {
2326        return std::ptr::null();
2327    }
2328    (*(seat as *const LibinputSeat)).physical_name.as_ptr()
2329}
2330
2331#[no_mangle]
2332pub unsafe extern "C" fn libinput_seat_get_logical_name(
2333    seat: *const libc::c_void,
2334) -> *const libc::c_char {
2335    if seat.is_null() {
2336        return std::ptr::null();
2337    }
2338    (*(seat as *const LibinputSeat)).logical_name.as_ptr()
2339}
2340
2341#[no_mangle]
2342pub unsafe extern "C" fn libinput_seat_get_context(
2343    seat: *const libc::c_void,
2344) -> *mut LibinputContext {
2345    if seat.is_null() {
2346        return std::ptr::null_mut();
2347    }
2348    (*(seat as *const LibinputSeat)).context
2349}
2350
2351#[no_mangle]
2352pub unsafe extern "C" fn libinput_seat_ref(seat: *mut libc::c_void) -> *mut libc::c_void {
2353    if !seat.is_null() {
2354        (*(seat as *mut LibinputSeat))
2355            .refcount
2356            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
2357    }
2358    seat
2359}
2360
2361#[no_mangle]
2362pub unsafe extern "C" fn libinput_seat_unref(seat: *mut libc::c_void) -> *mut libc::c_void {
2363    if seat.is_null() {
2364        return std::ptr::null_mut();
2365    }
2366    let seat = seat as *mut LibinputSeat;
2367    if (*seat)
2368        .refcount
2369        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
2370        == 1
2371    {
2372        drop(Box::from_raw(seat));
2373        std::ptr::null_mut()
2374    } else {
2375        seat.cast()
2376    }
2377}
2378
2379#[no_mangle]
2380pub unsafe extern "C" fn libinput_seat_set_user_data(
2381    seat: *mut libc::c_void,
2382    data: *mut libc::c_void,
2383) {
2384    if !seat.is_null() {
2385        (*(seat as *mut LibinputSeat)).user_data = data;
2386    }
2387}
2388
2389#[no_mangle]
2390pub unsafe extern "C" fn libinput_seat_get_user_data(
2391    seat: *const libc::c_void,
2392) -> *mut libc::c_void {
2393    if seat.is_null() {
2394        return std::ptr::null_mut();
2395    }
2396    (*(seat as *const LibinputSeat)).user_data
2397}
2398
2399// ---------------------------------------------------------------------------
2400// Status strings
2401// ---------------------------------------------------------------------------
2402
2403#[no_mangle]
2404pub unsafe extern "C" fn libinput_config_status_to_str(status: u32) -> *const libc::c_char {
2405    match status {
2406        0 => b"success\0".as_ptr().cast(),
2407        1 => b"unsupported\0".as_ptr().cast(),
2408        2 => b"invalid\0".as_ptr().cast(),
2409        _ => std::ptr::null(),
2410    }
2411}
2412
2413// ---------------------------------------------------------------------------
2414// Logging
2415// ---------------------------------------------------------------------------
2416
2417#[no_mangle]
2418pub unsafe extern "C" fn libinput_log_set_priority(ctx: *mut LibinputContext, priority: u32) {
2419    if !ctx.is_null() {
2420        (*ctx).log_priority = priority;
2421    }
2422}
2423
2424#[no_mangle]
2425pub unsafe extern "C" fn libinput_log_get_priority(ctx: *const LibinputContext) -> u32 {
2426    if ctx.is_null() {
2427        return 30;
2428    }
2429    (*ctx).log_priority
2430}
2431
2432#[no_mangle]
2433pub unsafe extern "C" fn libinput_log_set_handler(
2434    ctx: *mut LibinputContext,
2435    handler: Option<
2436        unsafe extern "C" fn(
2437            ctx: *mut LibinputContext,
2438            priority: u32,
2439            format: *const libc::c_char,
2440            args: *mut libc::c_void,
2441        ),
2442    >,
2443) {
2444    if ctx.is_null() {
2445        return;
2446    }
2447    (*ctx).log_handler = handler;
2448}
2449
2450// ---------------------------------------------------------------------------
2451// User data
2452// ---------------------------------------------------------------------------
2453
2454#[no_mangle]
2455pub unsafe extern "C" fn libinput_set_user_data(
2456    ctx: *mut LibinputContext,
2457    data: *mut libc::c_void,
2458) {
2459    if ctx.is_null() {
2460        return;
2461    }
2462    (*ctx).user_data = data;
2463}
2464
2465#[no_mangle]
2466pub unsafe extern "C" fn libinput_get_user_data(ctx: *const LibinputContext) -> *mut libc::c_void {
2467    if ctx.is_null() {
2468        return std::ptr::null_mut();
2469    }
2470    (*ctx).user_data
2471}
2472
2473#[no_mangle]
2474pub unsafe extern "C" fn libinput_device_set_user_data(
2475    dev: *mut LibinputDevice,
2476    data: *mut libc::c_void,
2477) {
2478    if dev.is_null() {
2479        return;
2480    }
2481    (*dev).user_data = data;
2482    (*dev).abi.user_data = data;
2483}
2484
2485#[no_mangle]
2486pub unsafe extern "C" fn libinput_device_get_user_data(
2487    dev: *const LibinputDevice,
2488) -> *mut libc::c_void {
2489    if dev.is_null() {
2490        return std::ptr::null_mut();
2491    }
2492    (*dev).user_data
2493}
2494
2495// ---------------------------------------------------------------------------
2496// ABI compatibility surface for compositors (KWin/GNOME)
2497// ---------------------------------------------------------------------------
2498
2499#[no_mangle]
2500pub unsafe extern "C" fn libinput_device_config_area_has_rectangle(
2501    dev: *const LibinputDevice,
2502) -> libc::c_int {
2503    if dev.is_null() {
2504        return 0;
2505    }
2506    (*dev).area_available as libc::c_int
2507}
2508
2509#[no_mangle]
2510pub unsafe extern "C" fn libinput_device_config_area_set_rectangle(
2511    dev: *mut LibinputDevice,
2512    rectangle: *const LibinputConfigAreaRectangle,
2513) -> u32 {
2514    if dev.is_null() || !(*dev).area_available {
2515        return 1;
2516    }
2517    if rectangle.is_null() {
2518        return 2;
2519    }
2520    let rectangle = &*rectangle;
2521    if rectangle.x1 >= rectangle.x2
2522        || rectangle.y1 >= rectangle.y2
2523        || rectangle.x1 < 0.0
2524        || rectangle.x2 > 1.0
2525        || rectangle.y1 < 0.0
2526        || rectangle.y2 > 1.0
2527    {
2528        return 2;
2529    }
2530    (*dev).wanted_area = [rectangle.x1, rectangle.y1, rectangle.x2, rectangle.y2];
2531    if !(*dev).tablet_in_proximity {
2532        (*dev).area = (*dev).wanted_area;
2533    }
2534    0
2535}
2536
2537#[no_mangle]
2538pub unsafe extern "C" fn libinput_device_config_area_get_rectangle(
2539    dev: *const LibinputDevice,
2540) -> LibinputConfigAreaRectangle {
2541    let area = if dev.is_null() || !(*dev).area_available {
2542        [0.0, 0.0, 1.0, 1.0]
2543    } else {
2544        (*dev).area
2545    };
2546    LibinputConfigAreaRectangle {
2547        x1: area[0],
2548        y1: area[1],
2549        x2: area[2],
2550        y2: area[3],
2551    }
2552}
2553
2554#[no_mangle]
2555pub unsafe extern "C" fn libinput_device_config_area_get_default_rectangle(
2556    _dev: *const LibinputDevice,
2557) -> LibinputConfigAreaRectangle {
2558    LibinputConfigAreaRectangle {
2559        x1: 0.0,
2560        y1: 0.0,
2561        x2: 1.0,
2562        y2: 1.0,
2563    }
2564}
2565
2566#[no_mangle]
2567pub unsafe extern "C" fn libinput_device_config_rotation_is_available(
2568    dev: *const LibinputDevice,
2569) -> libc::c_int {
2570    if dev.is_null() {
2571        return 0;
2572    }
2573    (*dev).rotation_available as libc::c_int
2574}
2575
2576#[no_mangle]
2577pub unsafe extern "C" fn libinput_device_config_rotation_set_angle(
2578    dev: *mut LibinputDevice,
2579    degrees_cw: u32,
2580) -> u32 {
2581    if dev.is_null() {
2582        return 1;
2583    }
2584    if degrees_cw >= 360 {
2585        return 2;
2586    }
2587    if !(*dev).rotation_available && degrees_cw != 0 {
2588        return 1;
2589    }
2590    (*dev).rotation_angle = degrees_cw;
2591    0
2592}
2593
2594#[no_mangle]
2595pub unsafe extern "C" fn libinput_device_config_rotation_get_angle(
2596    dev: *const LibinputDevice,
2597) -> u32 {
2598    if dev.is_null() {
2599        0
2600    } else {
2601        (*dev).rotation_angle
2602    }
2603}
2604
2605#[no_mangle]
2606pub unsafe extern "C" fn libinput_device_config_rotation_get_default_angle(
2607    _dev: *const LibinputDevice,
2608) -> u32 {
2609    0
2610}
2611
2612#[no_mangle]
2613pub unsafe extern "C" fn libinput_device_config_scroll_get_default_button(
2614    dev: *const LibinputDevice,
2615) -> u32 {
2616    if dev.is_null() || !(*dev).supports_button_scroll {
2617        0
2618    } else {
2619        (*dev).scroll_default_button
2620    }
2621}
2622
2623#[no_mangle]
2624pub unsafe extern "C" fn libinput_device_config_send_events_get_modes(
2625    dev: *const LibinputDevice,
2626) -> u32 {
2627    if dev.is_null() {
2628        return 0;
2629    }
2630    (*dev).send_events_modes
2631}
2632
2633#[no_mangle]
2634pub unsafe extern "C" fn libinput_device_config_send_events_set_mode(
2635    dev: *mut LibinputDevice,
2636    mode: u32,
2637) -> u32 {
2638    if dev.is_null() {
2639        return 1;
2640    }
2641    let supported = (*dev).send_events_modes;
2642    if mode & !supported != 0 {
2643        return 1;
2644    }
2645    let previous = (*dev).send_events_mode;
2646    let next = if mode & 1 != 0 { 1 } else { mode };
2647    (*dev).send_events_mode = next;
2648    if previous != next && matches!(next, 1 | 2) {
2649        let ctx = (*dev).context;
2650        if !ctx.is_null() {
2651            let mut events = std::collections::VecDeque::new();
2652            if let Ok(mut backend) = (*ctx).backend.lock() {
2653                if next == 1 || backend.has_external_mouse() {
2654                    backend.release_active_inputs(ctx, dev, &mut events);
2655                }
2656            }
2657            enqueue_events(ctx, events);
2658        }
2659    }
2660    0
2661}
2662
2663#[no_mangle]
2664pub unsafe extern "C" fn libinput_device_config_send_events_get_mode(
2665    dev: *const LibinputDevice,
2666) -> u32 {
2667    if dev.is_null() {
2668        return 0;
2669    }
2670    (*dev).send_events_mode
2671}
2672
2673#[no_mangle]
2674pub unsafe extern "C" fn libinput_device_config_send_events_get_default_mode(
2675    _dev: *const LibinputDevice,
2676) -> u32 {
2677    0
2678}
2679
2680#[no_mangle]
2681pub unsafe extern "C" fn libinput_device_config_tap_get_default_drag_lock_enabled(
2682    _dev: *const LibinputDevice,
2683) -> u32 {
2684    0
2685}
2686
2687#[no_mangle]
2688pub unsafe extern "C" fn libinput_device_get_device_group(
2689    dev: *const LibinputDevice,
2690) -> *mut libc::c_void {
2691    if dev.is_null() {
2692        return std::ptr::null_mut();
2693    }
2694    (*dev).group.cast()
2695}
2696
2697#[no_mangle]
2698pub unsafe extern "C" fn libinput_device_group_ref(group: *mut libc::c_void) -> *mut libc::c_void {
2699    if group.is_null() {
2700        return std::ptr::null_mut();
2701    }
2702    let group = group.cast::<LibinputDeviceGroup>();
2703    (*group)
2704        .refcount
2705        .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2706    group.cast()
2707}
2708
2709#[no_mangle]
2710pub unsafe extern "C" fn libinput_device_group_unref(
2711    group: *mut libc::c_void,
2712) -> *mut libc::c_void {
2713    if group.is_null() {
2714        return std::ptr::null_mut();
2715    }
2716    let group = group.cast::<LibinputDeviceGroup>();
2717    if (*group)
2718        .refcount
2719        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
2720        == 1
2721    {
2722        drop(Box::from_raw(group));
2723        std::ptr::null_mut()
2724    } else {
2725        group.cast()
2726    }
2727}
2728
2729#[no_mangle]
2730pub unsafe extern "C" fn libinput_device_group_set_user_data(
2731    group: *mut libc::c_void,
2732    data: *mut libc::c_void,
2733) {
2734    if !group.is_null() {
2735        (*group.cast::<LibinputDeviceGroup>()).user_data = data;
2736    }
2737}
2738
2739#[no_mangle]
2740pub unsafe extern "C" fn libinput_device_group_get_user_data(
2741    group: *const libc::c_void,
2742) -> *mut libc::c_void {
2743    if group.is_null() {
2744        return std::ptr::null_mut();
2745    }
2746    (*group.cast::<LibinputDeviceGroup>()).user_data
2747}
2748
2749#[no_mangle]
2750pub unsafe extern "C" fn libinput_device_get_id_bustype(dev: *const LibinputDevice) -> u32 {
2751    if dev.is_null() {
2752        return 0;
2753    }
2754    (*dev).bus_type
2755}
2756
2757#[no_mangle]
2758pub unsafe extern "C" fn libinput_device_get_size(
2759    dev: *const LibinputDevice,
2760    width: *mut f64,
2761    height: *mut f64,
2762) -> libc::c_int {
2763    if dev.is_null() || width.is_null() || height.is_null() {
2764        return 0;
2765    }
2766    match ((*dev).width_mm, (*dev).height_mm) {
2767        (Some(w), Some(h)) => {
2768            *width = w;
2769            *height = h;
2770            0
2771        }
2772        _ => -1,
2773    }
2774}
2775
2776#[no_mangle]
2777pub unsafe extern "C" fn libinput_device_get_udev_device(
2778    dev: *const LibinputDevice,
2779) -> *mut libc::c_void {
2780    if dev.is_null() || (*dev).udev_device.is_null() {
2781        return std::ptr::null_mut();
2782    }
2783    udev::udev_device_ref((*dev).udev_device)
2784}
2785
2786#[no_mangle]
2787pub unsafe extern "C" fn libinput_device_keyboard_has_key(
2788    dev: *const LibinputDevice,
2789    key: u32,
2790) -> libc::c_int {
2791    if dev.is_null() || !(*dev).has_keyboard {
2792        return -1;
2793    }
2794    if key > u16::MAX as u32 {
2795        return 0;
2796    }
2797    (*dev).event_codes.contains(&(key as u16)) as libc::c_int
2798}
2799
2800#[no_mangle]
2801pub unsafe extern "C" fn libinput_device_led_update(dev: *mut LibinputDevice, leds: u32) {
2802    if dev.is_null() || (*dev).context.is_null() {
2803        return;
2804    }
2805    let ctx = (*dev).context;
2806    if let Ok(mut backend) = (*ctx).backend.lock() {
2807        backend.update_leds(dev, leds);
2808    }
2809}
2810
2811#[no_mangle]
2812pub unsafe extern "C" fn libinput_device_pointer_has_button(
2813    dev: *const LibinputDevice,
2814    button: u32,
2815) -> libc::c_int {
2816    if dev.is_null() || button > u16::MAX as u32 {
2817        return 0;
2818    }
2819    if !(*dev).has_pointer {
2820        return -1;
2821    }
2822    (*dev).event_codes.contains(&(button as u16)) as libc::c_int
2823}
2824
2825#[no_mangle]
2826pub unsafe extern "C" fn libinput_device_switch_has_switch(
2827    dev: *const LibinputDevice,
2828    sw: u32,
2829) -> libc::c_int {
2830    if dev.is_null() {
2831        return 0;
2832    }
2833    if !(*dev).has_switch {
2834        return 0;
2835    }
2836    let kernel_code = match sw {
2837        1 => 0,
2838        2 => 1,
2839        3 => 10,
2840        _ => return 0,
2841    };
2842    (*dev).switch_codes.contains(&kernel_code) as libc::c_int
2843}
2844
2845#[no_mangle]
2846pub unsafe extern "C" fn libinput_device_tablet_pad_get_mode_group(
2847    dev: *const LibinputDevice,
2848    index: u32,
2849) -> *mut libc::c_void {
2850    if dev.is_null() || !(*dev).has_tablet_pad {
2851        return std::ptr::null_mut();
2852    }
2853    (*dev)
2854        .tablet_pad_mode_groups
2855        .iter()
2856        .copied()
2857        .find(|group| !group.is_null() && (**group).index == index)
2858        .unwrap_or(std::ptr::null_mut())
2859        .cast()
2860}
2861
2862#[no_mangle]
2863pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_buttons(
2864    dev: *const LibinputDevice,
2865) -> u32 {
2866    if dev.is_null() || !(*dev).has_tablet_pad {
2867        return u32::MAX;
2868    }
2869    (*dev).tablet_pad_button_codes.len() as u32
2870}
2871
2872#[no_mangle]
2873pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_dials(
2874    dev: *const LibinputDevice,
2875) -> u32 {
2876    if dev.is_null() || !(*dev).has_tablet_pad {
2877        return u32::MAX;
2878    }
2879    (*dev).tablet_pad_num_dials
2880}
2881
2882#[no_mangle]
2883pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_mode_groups(
2884    dev: *const LibinputDevice,
2885) -> u32 {
2886    if dev.is_null() || !(*dev).has_tablet_pad {
2887        return u32::MAX;
2888    }
2889    (*dev).tablet_pad_mode_groups.len() as u32
2890}
2891
2892#[no_mangle]
2893pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_rings(
2894    dev: *const LibinputDevice,
2895) -> u32 {
2896    if dev.is_null() || !(*dev).has_tablet_pad {
2897        return u32::MAX;
2898    }
2899    (*dev).tablet_pad_num_rings
2900}
2901
2902#[no_mangle]
2903pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_strips(
2904    dev: *const LibinputDevice,
2905) -> u32 {
2906    if dev.is_null() || !(*dev).has_tablet_pad {
2907        return u32::MAX;
2908    }
2909    (*dev).tablet_pad_num_strips
2910}
2911
2912#[no_mangle]
2913pub unsafe extern "C" fn libinput_device_tablet_pad_has_key(
2914    dev: *const LibinputDevice,
2915    code: u32,
2916) -> libc::c_int {
2917    if dev.is_null() || !(*dev).has_tablet_pad {
2918        return -1;
2919    }
2920    (code <= u16::MAX as u32 && (*dev).event_codes.contains(&(code as u16))) as libc::c_int
2921}
2922
2923#[no_mangle]
2924pub unsafe extern "C" fn libinput_event_get_tablet_pad_event(
2925    event: *mut LibinputEvent,
2926) -> *mut LibinputEvent {
2927    if event.is_null() {
2928        return std::ptr::null_mut();
2929    }
2930    match (*event).event_type {
2931        LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_BUTTON
2932        | LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_RING
2933        | LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_STRIP
2934        | LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_KEY
2935        | LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_DIAL => event,
2936        _ => std::ptr::null_mut(),
2937    }
2938}
2939
2940#[no_mangle]
2941pub unsafe extern "C" fn libinput_event_tablet_pad_get_base_event(
2942    event: *mut LibinputEvent,
2943) -> *mut LibinputEvent {
2944    event
2945}
2946
2947#[no_mangle]
2948pub unsafe extern "C" fn libinput_event_get_tablet_tool_event(
2949    event: *mut LibinputEvent,
2950) -> *mut LibinputEvent {
2951    if event.is_null() {
2952        return std::ptr::null_mut();
2953    }
2954    match (*event).event_type {
2955        LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS
2956        | LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY
2957        | LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_TIP
2958        | LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_BUTTON => event,
2959        _ => std::ptr::null_mut(),
2960    }
2961}
2962
2963#[no_mangle]
2964pub unsafe extern "C" fn libinput_event_tablet_tool_get_base_event(
2965    event: *mut LibinputEvent,
2966) -> *mut LibinputEvent {
2967    event
2968}
2969
2970#[no_mangle]
2971pub unsafe extern "C" fn libinput_event_pointer_get_absolute_x_transformed(
2972    event: *const LibinputEvent,
2973    width: u32,
2974) -> f64 {
2975    if event.is_null() {
2976        return 0.0;
2977    }
2978    if let EventPayload::PointerMotionAbsolute(e) = &(*event).payload {
2979        let range = e.x_max - e.x_min;
2980        if range > 0.0 {
2981            (e.abs_x - e.x_min) * f64::from(width) / range
2982        } else {
2983            0.0
2984        }
2985    } else {
2986        0.0
2987    }
2988}
2989
2990#[no_mangle]
2991pub unsafe extern "C" fn libinput_event_pointer_get_absolute_y_transformed(
2992    event: *const LibinputEvent,
2993    height: u32,
2994) -> f64 {
2995    if event.is_null() {
2996        return 0.0;
2997    }
2998    if let EventPayload::PointerMotionAbsolute(e) = &(*event).payload {
2999        let range = e.y_max - e.y_min;
3000        if range > 0.0 {
3001            (e.abs_y - e.y_min) * f64::from(height) / range
3002        } else {
3003            0.0
3004        }
3005    } else {
3006        0.0
3007    }
3008}
3009
3010#[no_mangle]
3011pub unsafe extern "C" fn libinput_event_pointer_get_scroll_value(
3012    event: *const LibinputEvent,
3013    axis: u32,
3014) -> f64 {
3015    libinput_event_pointer_get_axis_value(event, axis)
3016}
3017
3018#[no_mangle]
3019pub unsafe extern "C" fn libinput_event_pointer_get_scroll_value_v120(
3020    event: *const LibinputEvent,
3021    axis: u32,
3022) -> f64 {
3023    if event.is_null() {
3024        return 0.0;
3025    }
3026    if let EventPayload::PointerAxis(e) = &(*event).payload {
3027        return e.value_v120(axis);
3028    }
3029    0.0
3030}
3031
3032#[no_mangle]
3033pub unsafe extern "C" fn libinput_event_switch_get_time_usec(event: *const LibinputEvent) -> u64 {
3034    if event.is_null() {
3035        return 0;
3036    }
3037    if let EventPayload::SwitchToggle(e) = &(*event).payload {
3038        e.time_usec
3039    } else {
3040        0
3041    }
3042}
3043
3044#[no_mangle]
3045pub unsafe extern "C" fn libinput_event_switch_get_time(event: *const LibinputEvent) -> u32 {
3046    (libinput_event_switch_get_time_usec(event) / 1000) as u32
3047}
3048
3049#[no_mangle]
3050pub unsafe extern "C" fn libinput_event_tablet_pad_get_button_number(
3051    event: *const LibinputEvent,
3052) -> u32 {
3053    if event.is_null() {
3054        return 0;
3055    }
3056    match &(*event).payload {
3057        EventPayload::TabletPad(pad) => pad.button,
3058        _ => 0,
3059    }
3060}
3061
3062#[no_mangle]
3063pub unsafe extern "C" fn libinput_event_tablet_pad_get_button_state(
3064    event: *const LibinputEvent,
3065) -> u32 {
3066    if event.is_null() {
3067        return 0;
3068    }
3069    match &(*event).payload {
3070        EventPayload::TabletPad(pad) => pad.button_state,
3071        _ => 0,
3072    }
3073}
3074
3075#[no_mangle]
3076pub unsafe extern "C" fn libinput_event_tablet_pad_get_key(event: *const LibinputEvent) -> u32 {
3077    if event.is_null() {
3078        return 0;
3079    }
3080    match &(*event).payload {
3081        EventPayload::TabletPad(pad) => pad.key,
3082        _ => 0,
3083    }
3084}
3085
3086#[no_mangle]
3087pub unsafe extern "C" fn libinput_event_tablet_pad_get_key_state(
3088    event: *const LibinputEvent,
3089) -> u32 {
3090    if event.is_null() {
3091        return 0;
3092    }
3093    match &(*event).payload {
3094        EventPayload::TabletPad(pad) => pad.key_state,
3095        _ => 0,
3096    }
3097}
3098
3099#[no_mangle]
3100pub unsafe extern "C" fn libinput_event_tablet_pad_get_dial_delta_v120(
3101    event: *const LibinputEvent,
3102) -> f64 {
3103    if event.is_null() {
3104        return 0.0;
3105    }
3106    match &(*event).payload {
3107        EventPayload::TabletPad(pad) => pad.dial_delta_v120,
3108        _ => 0.0,
3109    }
3110}
3111
3112#[no_mangle]
3113pub unsafe extern "C" fn libinput_event_tablet_pad_get_dial_number(
3114    event: *const LibinputEvent,
3115) -> u32 {
3116    if event.is_null() {
3117        return 0;
3118    }
3119    match &(*event).payload {
3120        EventPayload::TabletPad(pad) => pad.dial_number,
3121        _ => 0,
3122    }
3123}
3124
3125#[no_mangle]
3126pub unsafe extern "C" fn libinput_event_tablet_pad_get_mode(event: *const LibinputEvent) -> u32 {
3127    if event.is_null() {
3128        return 0;
3129    }
3130    match &(*event).payload {
3131        EventPayload::TabletPad(pad) => pad.mode,
3132        _ => 0,
3133    }
3134}
3135
3136#[no_mangle]
3137pub unsafe extern "C" fn libinput_event_tablet_pad_get_mode_group(
3138    event: *const LibinputEvent,
3139) -> *mut libc::c_void {
3140    if event.is_null() {
3141        return std::ptr::null_mut();
3142    }
3143    match &(*event).payload {
3144        EventPayload::TabletPad(pad) => pad.mode_group.cast(),
3145        _ => std::ptr::null_mut(),
3146    }
3147}
3148
3149#[no_mangle]
3150pub unsafe extern "C" fn libinput_event_tablet_pad_get_ring_number(
3151    event: *const LibinputEvent,
3152) -> u32 {
3153    if event.is_null() {
3154        return 0;
3155    }
3156    match &(*event).payload {
3157        EventPayload::TabletPad(pad) => pad.ring_number,
3158        _ => 0,
3159    }
3160}
3161
3162#[no_mangle]
3163pub unsafe extern "C" fn libinput_event_tablet_pad_get_ring_position(
3164    event: *const LibinputEvent,
3165) -> f64 {
3166    if event.is_null() {
3167        return 0.0;
3168    }
3169    match &(*event).payload {
3170        EventPayload::TabletPad(pad) => pad.ring_position,
3171        _ => 0.0,
3172    }
3173}
3174
3175#[no_mangle]
3176pub unsafe extern "C" fn libinput_event_tablet_pad_get_ring_source(
3177    event: *const LibinputEvent,
3178) -> u32 {
3179    if event.is_null() {
3180        return 0;
3181    }
3182    match &(*event).payload {
3183        EventPayload::TabletPad(pad) => pad.ring_source,
3184        _ => 0,
3185    }
3186}
3187
3188#[no_mangle]
3189pub unsafe extern "C" fn libinput_event_tablet_pad_get_strip_number(
3190    event: *const LibinputEvent,
3191) -> u32 {
3192    if event.is_null() {
3193        return 0;
3194    }
3195    match &(*event).payload {
3196        EventPayload::TabletPad(pad) => pad.strip_number,
3197        _ => 0,
3198    }
3199}
3200
3201#[no_mangle]
3202pub unsafe extern "C" fn libinput_event_tablet_pad_get_strip_position(
3203    event: *const LibinputEvent,
3204) -> f64 {
3205    if event.is_null() {
3206        return 0.0;
3207    }
3208    match &(*event).payload {
3209        EventPayload::TabletPad(pad) => pad.strip_position,
3210        _ => 0.0,
3211    }
3212}
3213
3214#[no_mangle]
3215pub unsafe extern "C" fn libinput_event_tablet_pad_get_strip_source(
3216    event: *const LibinputEvent,
3217) -> u32 {
3218    if event.is_null() {
3219        return 0;
3220    }
3221    match &(*event).payload {
3222        EventPayload::TabletPad(pad) => pad.strip_source,
3223        _ => 0,
3224    }
3225}
3226
3227#[no_mangle]
3228pub unsafe extern "C" fn libinput_event_tablet_pad_get_time_usec(
3229    event: *const LibinputEvent,
3230) -> u64 {
3231    if event.is_null() {
3232        return 0;
3233    }
3234    match &(*event).payload {
3235        EventPayload::TabletPad(pad) => pad.time_usec,
3236        _ => 0,
3237    }
3238}
3239
3240#[no_mangle]
3241pub unsafe extern "C" fn libinput_event_tablet_pad_get_time(event: *const LibinputEvent) -> u32 {
3242    (libinput_event_tablet_pad_get_time_usec(event) / 1000) as u32
3243}
3244
3245#[no_mangle]
3246pub unsafe extern "C" fn libinput_event_tablet_tool_get_button(event: *const LibinputEvent) -> u32 {
3247    if event.is_null() {
3248        return 0;
3249    }
3250    match &(*event).payload {
3251        EventPayload::TabletTool(tablet) => tablet.button,
3252        _ => 0,
3253    }
3254}
3255
3256#[no_mangle]
3257pub unsafe extern "C" fn libinput_event_tablet_tool_get_button_state(
3258    event: *const LibinputEvent,
3259) -> u32 {
3260    if event.is_null() {
3261        return 0;
3262    }
3263    match &(*event).payload {
3264        EventPayload::TabletTool(tablet) => tablet.button_state,
3265        _ => 0,
3266    }
3267}
3268
3269#[no_mangle]
3270pub unsafe extern "C" fn libinput_event_tablet_tool_get_seat_button_count(
3271    event: *const LibinputEvent,
3272) -> u32 {
3273    if event.is_null() {
3274        return 0;
3275    }
3276    match &(*event).payload {
3277        EventPayload::TabletTool(tablet) => tablet.seat_button_count,
3278        _ => 0,
3279    }
3280}
3281
3282#[no_mangle]
3283pub unsafe extern "C" fn libinput_event_tablet_tool_get_distance(
3284    event: *const LibinputEvent,
3285) -> f64 {
3286    if event.is_null() {
3287        return 0.0;
3288    }
3289    match &(*event).payload {
3290        EventPayload::TabletTool(tablet) => tablet.distance,
3291        _ => 0.0,
3292    }
3293}
3294
3295#[no_mangle]
3296pub unsafe extern "C" fn libinput_event_tablet_tool_get_dx(event: *const LibinputEvent) -> f64 {
3297    if event.is_null() || (*event).event_type != LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS
3298    {
3299        return 0.0;
3300    }
3301    match &(*event).payload {
3302        EventPayload::TabletTool(tablet) => tablet.dx,
3303        _ => 0.0,
3304    }
3305}
3306
3307#[no_mangle]
3308pub unsafe extern "C" fn libinput_event_tablet_tool_get_dy(event: *const LibinputEvent) -> f64 {
3309    if event.is_null() || (*event).event_type != LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS
3310    {
3311        return 0.0;
3312    }
3313    match &(*event).payload {
3314        EventPayload::TabletTool(tablet) => tablet.dy,
3315        _ => 0.0,
3316    }
3317}
3318
3319#[no_mangle]
3320pub unsafe extern "C" fn libinput_event_tablet_tool_get_pressure(
3321    event: *const LibinputEvent,
3322) -> f64 {
3323    if event.is_null() {
3324        return 0.0;
3325    }
3326    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3327        let range = tablet.pressure_max - tablet.pressure_min;
3328        if range > 0.0 {
3329            ((tablet.pressure - tablet.pressure_min) / range).clamp(0.0, 1.0)
3330        } else {
3331            0.0
3332        }
3333    } else {
3334        0.0
3335    }
3336}
3337
3338#[no_mangle]
3339pub unsafe extern "C" fn libinput_event_tablet_tool_get_x(event: *const LibinputEvent) -> f64 {
3340    if event.is_null() {
3341        return 0.0;
3342    }
3343    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3344        if tablet.x_resolution > 0.0 {
3345            (tablet.x - tablet.x_min) / tablet.x_resolution
3346        } else {
3347            tablet.x - tablet.x_min
3348        }
3349    } else {
3350        0.0
3351    }
3352}
3353
3354#[no_mangle]
3355pub unsafe extern "C" fn libinput_event_tablet_tool_get_y(event: *const LibinputEvent) -> f64 {
3356    if event.is_null() {
3357        return 0.0;
3358    }
3359    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3360        if tablet.y_resolution > 0.0 {
3361            (tablet.y - tablet.y_min) / tablet.y_resolution
3362        } else {
3363            tablet.y - tablet.y_min
3364        }
3365    } else {
3366        0.0
3367    }
3368}
3369
3370#[no_mangle]
3371pub unsafe extern "C" fn libinput_event_tablet_tool_get_proximity_state(
3372    event: *const LibinputEvent,
3373) -> u32 {
3374    if event.is_null() {
3375        return 0;
3376    }
3377    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3378        tablet.proximity_state
3379    } else {
3380        0
3381    }
3382}
3383
3384#[no_mangle]
3385pub unsafe extern "C" fn libinput_event_tablet_tool_get_rotation(
3386    event: *const LibinputEvent,
3387) -> f64 {
3388    if event.is_null() {
3389        return 0.0;
3390    }
3391    match &(*event).payload {
3392        EventPayload::TabletTool(tablet) => tablet.rotation,
3393        _ => 0.0,
3394    }
3395}
3396
3397#[no_mangle]
3398pub unsafe extern "C" fn libinput_event_tablet_tool_get_slider_position(
3399    event: *const LibinputEvent,
3400) -> f64 {
3401    if event.is_null() {
3402        return 0.0;
3403    }
3404    match &(*event).payload {
3405        EventPayload::TabletTool(tablet) => tablet.slider,
3406        _ => 0.0,
3407    }
3408}
3409
3410#[no_mangle]
3411pub unsafe extern "C" fn libinput_event_tablet_tool_get_wheel_delta(
3412    event: *const LibinputEvent,
3413) -> f64 {
3414    if event.is_null() {
3415        return 0.0;
3416    }
3417    match &(*event).payload {
3418        EventPayload::TabletTool(tablet) => tablet.wheel_delta,
3419        _ => 0.0,
3420    }
3421}
3422
3423#[no_mangle]
3424pub unsafe extern "C" fn libinput_event_tablet_tool_get_wheel_delta_discrete(
3425    event: *const LibinputEvent,
3426) -> i32 {
3427    if event.is_null() {
3428        return 0;
3429    }
3430    match &(*event).payload {
3431        EventPayload::TabletTool(tablet) => tablet.wheel_discrete,
3432        _ => 0,
3433    }
3434}
3435
3436#[no_mangle]
3437pub unsafe extern "C" fn libinput_event_tablet_tool_get_size_major(
3438    event: *const LibinputEvent,
3439) -> f64 {
3440    if event.is_null() {
3441        return 0.0;
3442    }
3443    match &(*event).payload {
3444        EventPayload::TabletTool(tablet) => tablet.size_major,
3445        _ => 0.0,
3446    }
3447}
3448
3449#[no_mangle]
3450pub unsafe extern "C" fn libinput_event_tablet_tool_get_size_minor(
3451    event: *const LibinputEvent,
3452) -> f64 {
3453    if event.is_null() {
3454        return 0.0;
3455    }
3456    match &(*event).payload {
3457        EventPayload::TabletTool(tablet) => tablet.size_minor,
3458        _ => 0.0,
3459    }
3460}
3461
3462#[no_mangle]
3463pub unsafe extern "C" fn libinput_event_tablet_tool_get_tilt_x(event: *const LibinputEvent) -> f64 {
3464    if event.is_null() {
3465        return 0.0;
3466    }
3467    match &(*event).payload {
3468        EventPayload::TabletTool(tablet) => tablet.tilt_x,
3469        _ => 0.0,
3470    }
3471}
3472
3473#[no_mangle]
3474pub unsafe extern "C" fn libinput_event_tablet_tool_get_tilt_y(event: *const LibinputEvent) -> f64 {
3475    if event.is_null() {
3476        return 0.0;
3477    }
3478    match &(*event).payload {
3479        EventPayload::TabletTool(tablet) => tablet.tilt_y,
3480        _ => 0.0,
3481    }
3482}
3483
3484#[no_mangle]
3485pub unsafe extern "C" fn libinput_event_tablet_tool_get_time_usec(
3486    event: *const LibinputEvent,
3487) -> u64 {
3488    if event.is_null() {
3489        return 0;
3490    }
3491    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3492        tablet.time_usec
3493    } else {
3494        0
3495    }
3496}
3497
3498#[no_mangle]
3499pub unsafe extern "C" fn libinput_event_tablet_tool_get_time(event: *const LibinputEvent) -> u32 {
3500    (libinput_event_tablet_tool_get_time_usec(event) / 1000) as u32
3501}
3502
3503#[no_mangle]
3504pub unsafe extern "C" fn libinput_event_tablet_tool_get_tip_state(
3505    event: *const LibinputEvent,
3506) -> u32 {
3507    if event.is_null() {
3508        return 0;
3509    }
3510    match &(*event).payload {
3511        EventPayload::TabletTool(tablet) => tablet.tip_state,
3512        _ => 0,
3513    }
3514}
3515
3516#[no_mangle]
3517pub unsafe extern "C" fn libinput_event_tablet_tool_get_tool(
3518    event: *const LibinputEvent,
3519) -> *mut libc::c_void {
3520    if event.is_null() {
3521        return std::ptr::null_mut();
3522    }
3523    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3524        tablet.tool.cast()
3525    } else {
3526        std::ptr::null_mut()
3527    }
3528}
3529
3530#[no_mangle]
3531pub unsafe extern "C" fn libinput_event_tablet_tool_get_x_transformed(
3532    event: *const LibinputEvent,
3533    width: u32,
3534) -> f64 {
3535    if event.is_null() {
3536        return 0.0;
3537    }
3538    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3539        let range = tablet.x_max - tablet.x_min + 1.0;
3540        if range > 0.0 {
3541            (tablet.x - tablet.x_min) * f64::from(width) / range
3542        } else {
3543            0.0
3544        }
3545    } else {
3546        0.0
3547    }
3548}
3549
3550#[no_mangle]
3551pub unsafe extern "C" fn libinput_event_tablet_tool_get_y_transformed(
3552    event: *const LibinputEvent,
3553    height: u32,
3554) -> f64 {
3555    if event.is_null() {
3556        return 0.0;
3557    }
3558    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3559        let range = tablet.y_max - tablet.y_min + 1.0;
3560        if range > 0.0 {
3561            (tablet.y - tablet.y_min) * f64::from(height) / range
3562        } else {
3563            0.0
3564        }
3565    } else {
3566        0.0
3567    }
3568}
3569
3570#[no_mangle]
3571pub unsafe extern "C" fn libinput_event_tablet_tool_x_has_changed(
3572    event: *const LibinputEvent,
3573) -> libc::c_int {
3574    if event.is_null() {
3575        return 0;
3576    }
3577    match &(*event).payload {
3578        EventPayload::TabletTool(tablet) => tablet.x_changed as libc::c_int,
3579        _ => 0,
3580    }
3581}
3582
3583#[no_mangle]
3584pub unsafe extern "C" fn libinput_event_tablet_tool_y_has_changed(
3585    event: *const LibinputEvent,
3586) -> libc::c_int {
3587    if event.is_null() {
3588        return 0;
3589    }
3590    match &(*event).payload {
3591        EventPayload::TabletTool(tablet) => tablet.y_changed as libc::c_int,
3592        _ => 0,
3593    }
3594}
3595
3596#[no_mangle]
3597pub unsafe extern "C" fn libinput_event_tablet_tool_pressure_has_changed(
3598    event: *const LibinputEvent,
3599) -> libc::c_int {
3600    if event.is_null() {
3601        return 0;
3602    }
3603    match &(*event).payload {
3604        EventPayload::TabletTool(tablet) => tablet.pressure_changed as libc::c_int,
3605        _ => 0,
3606    }
3607}
3608
3609#[no_mangle]
3610pub unsafe extern "C" fn libinput_event_tablet_tool_distance_has_changed(
3611    event: *const LibinputEvent,
3612) -> libc::c_int {
3613    if event.is_null() {
3614        return 0;
3615    }
3616    match &(*event).payload {
3617        EventPayload::TabletTool(tablet) => tablet.distance_changed as libc::c_int,
3618        _ => 0,
3619    }
3620}
3621
3622#[no_mangle]
3623pub unsafe extern "C" fn libinput_event_tablet_tool_tilt_x_has_changed(
3624    event: *const LibinputEvent,
3625) -> libc::c_int {
3626    if event.is_null() {
3627        return 0;
3628    }
3629    match &(*event).payload {
3630        EventPayload::TabletTool(tablet) => tablet.tilt_x_changed as libc::c_int,
3631        _ => 0,
3632    }
3633}
3634
3635#[no_mangle]
3636pub unsafe extern "C" fn libinput_event_tablet_tool_tilt_y_has_changed(
3637    event: *const LibinputEvent,
3638) -> libc::c_int {
3639    if event.is_null() {
3640        return 0;
3641    }
3642    match &(*event).payload {
3643        EventPayload::TabletTool(tablet) => tablet.tilt_y_changed as libc::c_int,
3644        _ => 0,
3645    }
3646}
3647
3648#[no_mangle]
3649pub unsafe extern "C" fn libinput_event_tablet_tool_rotation_has_changed(
3650    event: *const LibinputEvent,
3651) -> libc::c_int {
3652    if event.is_null() {
3653        return 0;
3654    }
3655    match &(*event).payload {
3656        EventPayload::TabletTool(tablet) => tablet.rotation_changed as libc::c_int,
3657        _ => 0,
3658    }
3659}
3660
3661#[no_mangle]
3662pub unsafe extern "C" fn libinput_event_tablet_tool_slider_has_changed(
3663    event: *const LibinputEvent,
3664) -> libc::c_int {
3665    if event.is_null() {
3666        return 0;
3667    }
3668    match &(*event).payload {
3669        EventPayload::TabletTool(tablet) => tablet.slider_changed as libc::c_int,
3670        _ => 0,
3671    }
3672}
3673
3674#[no_mangle]
3675pub unsafe extern "C" fn libinput_event_tablet_tool_wheel_has_changed(
3676    event: *const LibinputEvent,
3677) -> libc::c_int {
3678    if event.is_null() {
3679        return 0;
3680    }
3681    match &(*event).payload {
3682        EventPayload::TabletTool(tablet) => tablet.wheel_changed as libc::c_int,
3683        _ => 0,
3684    }
3685}
3686
3687#[no_mangle]
3688pub unsafe extern "C" fn libinput_event_tablet_tool_size_major_has_changed(
3689    event: *const LibinputEvent,
3690) -> libc::c_int {
3691    if event.is_null() {
3692        return 0;
3693    }
3694    match &(*event).payload {
3695        EventPayload::TabletTool(tablet) => tablet.size_major_changed as libc::c_int,
3696        _ => 0,
3697    }
3698}
3699
3700#[no_mangle]
3701pub unsafe extern "C" fn libinput_event_tablet_tool_size_minor_has_changed(
3702    event: *const LibinputEvent,
3703) -> libc::c_int {
3704    if event.is_null() {
3705        return 0;
3706    }
3707    match &(*event).payload {
3708        EventPayload::TabletTool(tablet) => tablet.size_minor_changed as libc::c_int,
3709        _ => 0,
3710    }
3711}
3712
3713#[no_mangle]
3714pub unsafe extern "C" fn libinput_plugin_system_append_default_paths(ctx: *mut LibinputContext) {
3715    if ctx.is_null() || (*ctx).plugins_loaded {
3716        return;
3717    }
3718    for path in ["/etc/libinput/plugins", "/usr/lib64/libinput/plugins"] {
3719        let path = std::ffi::CString::new(path).expect("static plugin path");
3720        if !(*ctx)
3721            .plugin_paths
3722            .iter()
3723            .any(|candidate| candidate.as_bytes() == path.as_bytes())
3724        {
3725            (*ctx).plugin_paths.push(path);
3726        }
3727    }
3728}
3729
3730#[no_mangle]
3731pub unsafe extern "C" fn libinput_plugin_system_append_path(
3732    ctx: *mut LibinputContext,
3733    path: *const libc::c_char,
3734) {
3735    if ctx.is_null() || path.is_null() || (*ctx).plugins_loaded {
3736        return;
3737    }
3738    let path = CStr::from_ptr(path);
3739    if path.to_bytes().is_empty()
3740        || (*ctx)
3741            .plugin_paths
3742            .iter()
3743            .any(|candidate| candidate.as_bytes() == path.to_bytes())
3744    {
3745        return;
3746    }
3747    if let Ok(path) = std::ffi::CString::new(path.to_bytes()) {
3748        (*ctx).plugin_paths.push(path);
3749    }
3750}
3751
3752#[no_mangle]
3753pub unsafe extern "C" fn libinput_plugin_system_load_plugins(
3754    ctx: *mut LibinputContext,
3755    flags: libc::c_uint,
3756) -> libc::c_int {
3757    if ctx.is_null() || flags != 0 {
3758        return -libc::EINVAL;
3759    }
3760    if (*ctx).plugins_loaded {
3761        return 0;
3762    }
3763    // Built-in stages are always active, but this build has no Lua plugin
3764    // loader. Match an upstream build configured without plugin support:
3765    // freeze the plugin system and report ENOSYS instead of claiming success.
3766    (*ctx).plugins_loaded = true;
3767    -libc::ENOSYS
3768}
3769
3770#[no_mangle]
3771pub unsafe extern "C" fn libinput_tablet_pad_mode_group_button_is_toggle(
3772    group: *const libc::c_void,
3773    button: u32,
3774) -> libc::c_int {
3775    if group.is_null() || button >= u32::BITS {
3776        return 0;
3777    }
3778    ((*group.cast::<LibinputTabletPadModeGroup>()).toggle_button_mask & (1_u32 << button) != 0)
3779        as libc::c_int
3780}
3781
3782#[no_mangle]
3783pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_index(
3784    group: *const libc::c_void,
3785) -> u32 {
3786    if group.is_null() {
3787        return 0;
3788    }
3789    (*group.cast::<LibinputTabletPadModeGroup>()).index
3790}
3791
3792#[no_mangle]
3793pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_mode(
3794    group: *const libc::c_void,
3795) -> u32 {
3796    if group.is_null() {
3797        return 0;
3798    }
3799    (*group.cast::<LibinputTabletPadModeGroup>()).current_mode
3800}
3801
3802#[no_mangle]
3803pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_num_modes(
3804    group: *const libc::c_void,
3805) -> u32 {
3806    if group.is_null() {
3807        return 0;
3808    }
3809    (*group.cast::<LibinputTabletPadModeGroup>()).num_modes
3810}
3811
3812#[no_mangle]
3813pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_button(
3814    group: *const libc::c_void,
3815    button: u32,
3816) -> libc::c_int {
3817    if group.is_null() {
3818        return 0;
3819    }
3820    if button >= u32::BITS {
3821        return 0;
3822    }
3823    ((*group.cast::<LibinputTabletPadModeGroup>()).button_mask & (1_u32 << button) != 0)
3824        as libc::c_int
3825}
3826
3827#[no_mangle]
3828pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_dial(
3829    group: *const libc::c_void,
3830    dial: u32,
3831) -> libc::c_int {
3832    if group.is_null() {
3833        return 0;
3834    }
3835    if dial >= u32::BITS {
3836        return 0;
3837    }
3838    ((*group.cast::<LibinputTabletPadModeGroup>()).dial_mask & (1_u32 << dial) != 0) as libc::c_int
3839}
3840
3841#[no_mangle]
3842pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_ring(
3843    group: *const libc::c_void,
3844    ring: u32,
3845) -> libc::c_int {
3846    if group.is_null() {
3847        return 0;
3848    }
3849    if ring >= u32::BITS {
3850        return 0;
3851    }
3852    ((*group.cast::<LibinputTabletPadModeGroup>()).ring_mask & (1_u32 << ring) != 0) as libc::c_int
3853}
3854
3855#[no_mangle]
3856pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_strip(
3857    group: *const libc::c_void,
3858    strip: u32,
3859) -> libc::c_int {
3860    if group.is_null() {
3861        return 0;
3862    }
3863    if strip >= u32::BITS {
3864        return 0;
3865    }
3866    ((*group.cast::<LibinputTabletPadModeGroup>()).strip_mask & (1_u32 << strip) != 0)
3867        as libc::c_int
3868}
3869
3870#[no_mangle]
3871pub unsafe extern "C" fn libinput_tablet_pad_mode_group_ref(
3872    group: *mut libc::c_void,
3873) -> *mut libc::c_void {
3874    if !group.is_null() {
3875        (*group.cast::<LibinputTabletPadModeGroup>())
3876            .refcount
3877            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3878    }
3879    group
3880}
3881
3882#[no_mangle]
3883pub unsafe extern "C" fn libinput_tablet_pad_mode_group_unref(
3884    group: *mut libc::c_void,
3885) -> *mut libc::c_void {
3886    if group.is_null() {
3887        return std::ptr::null_mut();
3888    }
3889    let group = group.cast::<LibinputTabletPadModeGroup>();
3890    if (*group)
3891        .refcount
3892        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
3893        == 1
3894    {
3895        drop(Box::from_raw(group));
3896        std::ptr::null_mut()
3897    } else {
3898        group.cast()
3899    }
3900}
3901
3902#[no_mangle]
3903pub unsafe extern "C" fn libinput_tablet_pad_mode_group_set_user_data(
3904    group: *mut libc::c_void,
3905    data: *mut libc::c_void,
3906) {
3907    if !group.is_null() {
3908        (*group.cast::<LibinputTabletPadModeGroup>()).user_data = data;
3909    }
3910}
3911
3912#[no_mangle]
3913pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_user_data(
3914    group: *const libc::c_void,
3915) -> *mut libc::c_void {
3916    if group.is_null() {
3917        return std::ptr::null_mut();
3918    }
3919    (*group.cast::<LibinputTabletPadModeGroup>()).user_data
3920}
3921
3922#[no_mangle]
3923pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_is_available(
3924    tool: *const LibinputTabletTool,
3925) -> libc::c_int {
3926    (!tool.is_null() && (*tool).has_pressure) as libc::c_int
3927}
3928
3929#[no_mangle]
3930pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_set(
3931    tool: *mut LibinputTabletTool,
3932    minimum: f64,
3933    maximum: f64,
3934) -> u32 {
3935    if tool.is_null() || !(*tool).has_pressure {
3936        return 1;
3937    }
3938    if minimum < 0.0 || maximum > 1.0 || minimum >= maximum {
3939        return 2;
3940    }
3941    (*tool).wanted_pressure_range_minimum = minimum;
3942    (*tool).wanted_pressure_range_maximum = maximum;
3943    0
3944}
3945
3946#[no_mangle]
3947pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_minimum(
3948    tool: *const LibinputTabletTool,
3949) -> f64 {
3950    if tool.is_null() {
3951        0.0
3952    } else {
3953        (*tool).wanted_pressure_range_minimum
3954    }
3955}
3956
3957#[no_mangle]
3958pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_maximum(
3959    tool: *const LibinputTabletTool,
3960) -> f64 {
3961    if tool.is_null() {
3962        1.0
3963    } else {
3964        (*tool).wanted_pressure_range_maximum
3965    }
3966}
3967
3968#[no_mangle]
3969pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_default_minimum(
3970    _tool: *const libc::c_void,
3971) -> f64 {
3972    0.0
3973}
3974
3975#[no_mangle]
3976pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_default_maximum(
3977    _tool: *const libc::c_void,
3978) -> f64 {
3979    1.0
3980}
3981
3982#[no_mangle]
3983pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_modes(
3984    tool: *const libc::c_void,
3985) -> u32 {
3986    let tool = tool.cast::<LibinputTabletTool>();
3987    if tool.is_null() {
3988        0
3989    } else {
3990        (*tool).eraser_button_modes
3991    }
3992}
3993
3994#[no_mangle]
3995pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_set_mode(
3996    tool: *mut libc::c_void,
3997    mode: u32,
3998) -> u32 {
3999    let tool = tool.cast::<LibinputTabletTool>();
4000    if tool.is_null() || (mode != 0 && ((*tool).eraser_button_modes & mode) == 0) {
4001        return 1;
4002    }
4003    if !matches!(mode, 0 | 1) {
4004        return 2;
4005    }
4006    (*tool).wanted_eraser_button_mode = mode;
4007    if !(*tool).in_proximity {
4008        (*tool).eraser_button_mode = mode;
4009    }
4010    0
4011}
4012
4013#[no_mangle]
4014pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_mode(
4015    tool: *const libc::c_void,
4016) -> u32 {
4017    let tool = tool.cast::<LibinputTabletTool>();
4018    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4019        0
4020    } else {
4021        (*tool).wanted_eraser_button_mode
4022    }
4023}
4024
4025#[no_mangle]
4026pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_default_mode(
4027    _tool: *const libc::c_void,
4028) -> u32 {
4029    0
4030}
4031
4032#[no_mangle]
4033pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_set_button(
4034    tool: *mut libc::c_void,
4035    button: u32,
4036) -> u32 {
4037    let tool = tool.cast::<LibinputTabletTool>();
4038    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4039        return 1;
4040    }
4041    let is_button = matches!(button, 0x149 | 0x14b | 0x14c)
4042        || (0x100..0x140).contains(&button)
4043        || (0x150..=0x151).contains(&button)
4044        || (0x220..=0x223).contains(&button)
4045        || (0x2c0..=0x2e7).contains(&button);
4046    if !is_button {
4047        return 2;
4048    }
4049    (*tool).wanted_eraser_button = button;
4050    if !(*tool).in_proximity {
4051        (*tool).eraser_button = button;
4052    }
4053    0
4054}
4055
4056#[no_mangle]
4057pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_button(
4058    tool: *const libc::c_void,
4059) -> u32 {
4060    let tool = tool.cast::<LibinputTabletTool>();
4061    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4062        0
4063    } else {
4064        (*tool).wanted_eraser_button
4065    }
4066}
4067
4068#[no_mangle]
4069pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_default_button(
4070    tool: *const libc::c_void,
4071) -> u32 {
4072    let tool = tool.cast::<LibinputTabletTool>();
4073    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4074        0
4075    } else {
4076        (*tool).default_eraser_button
4077    }
4078}
4079
4080#[no_mangle]
4081pub unsafe extern "C" fn libinput_tablet_tool_get_name(
4082    tool: *const libc::c_void,
4083) -> *const libc::c_char {
4084    let tool = tool.cast::<LibinputTabletTool>();
4085    if tool.is_null() {
4086        return std::ptr::null();
4087    }
4088    let tool = tool.cast::<LibinputTabletTool>();
4089    let tool = tool as *mut LibinputTabletTool;
4090    if let Some(name) = (*tool).name.as_ref() {
4091        return name.as_ptr();
4092    }
4093    if let Some(name) = crate::backend::tablet_tool_name_for_id((*tool).tool_id) {
4094        (*tool).name = Some(name);
4095        (*tool)
4096            .name
4097            .as_ref()
4098            .map_or(std::ptr::null(), |name| name.as_ptr())
4099    } else {
4100        std::ptr::null()
4101    }
4102}
4103
4104#[no_mangle]
4105pub unsafe extern "C" fn libinput_tablet_tool_get_serial(tool: *const libc::c_void) -> u64 {
4106    let tool = tool.cast::<LibinputTabletTool>();
4107    if tool.is_null() {
4108        0
4109    } else {
4110        (*tool).serial
4111    }
4112}
4113
4114#[no_mangle]
4115pub unsafe extern "C" fn libinput_tablet_tool_get_tool_id(tool: *const libc::c_void) -> u64 {
4116    let tool = tool.cast::<LibinputTabletTool>();
4117    if tool.is_null() {
4118        0
4119    } else {
4120        (*tool).tool_id
4121    }
4122}
4123
4124#[no_mangle]
4125pub unsafe extern "C" fn libinput_tablet_tool_get_type(tool: *const libc::c_void) -> u32 {
4126    let tool = tool.cast::<LibinputTabletTool>();
4127    if tool.is_null() {
4128        0
4129    } else {
4130        (*tool).tool_type
4131    }
4132}
4133
4134#[no_mangle]
4135pub unsafe extern "C" fn libinput_tablet_tool_has_distance(
4136    tool: *const libc::c_void,
4137) -> libc::c_int {
4138    let tool = tool.cast::<LibinputTabletTool>();
4139    (!tool.is_null() && (*tool).has_distance) as libc::c_int
4140}
4141
4142#[no_mangle]
4143pub unsafe extern "C" fn libinput_tablet_tool_has_button(
4144    tool: *const libc::c_void,
4145    button: u32,
4146) -> libc::c_int {
4147    let tool = tool.cast::<LibinputTabletTool>();
4148    (!tool.is_null() && (*tool).buttons.contains(&button)) as libc::c_int
4149}
4150
4151#[no_mangle]
4152pub unsafe extern "C" fn libinput_tablet_tool_has_size(tool: *const libc::c_void) -> libc::c_int {
4153    let tool = tool.cast::<LibinputTabletTool>();
4154    (!tool.is_null() && (*tool).has_size) as libc::c_int
4155}
4156
4157#[no_mangle]
4158pub unsafe extern "C" fn libinput_tablet_tool_is_unique(tool: *const libc::c_void) -> libc::c_int {
4159    let tool = tool.cast::<LibinputTabletTool>();
4160    (!tool.is_null() && (*tool).serial != 0) as libc::c_int
4161}
4162
4163#[no_mangle]
4164pub unsafe extern "C" fn libinput_tablet_tool_set_user_data(
4165    tool: *mut libc::c_void,
4166    data: *mut libc::c_void,
4167) {
4168    let tool = tool.cast::<LibinputTabletTool>();
4169    if !tool.is_null() {
4170        (*tool).user_data = data;
4171    }
4172}
4173
4174#[no_mangle]
4175pub unsafe extern "C" fn libinput_tablet_tool_get_user_data(
4176    tool: *const libc::c_void,
4177) -> *mut libc::c_void {
4178    let tool = tool.cast::<LibinputTabletTool>();
4179    if tool.is_null() {
4180        std::ptr::null_mut()
4181    } else {
4182        (*tool).user_data
4183    }
4184}
4185
4186#[no_mangle]
4187pub unsafe extern "C" fn libinput_tablet_tool_has_pressure(
4188    tool: *const libc::c_void,
4189) -> libc::c_int {
4190    let tool = tool.cast::<LibinputTabletTool>();
4191    (!tool.is_null() && (*tool).has_pressure) as libc::c_int
4192}
4193
4194#[no_mangle]
4195pub unsafe extern "C" fn libinput_tablet_tool_has_rotation(
4196    tool: *const libc::c_void,
4197) -> libc::c_int {
4198    let tool = tool.cast::<LibinputTabletTool>();
4199    (!tool.is_null() && (*tool).has_rotation) as libc::c_int
4200}
4201
4202#[no_mangle]
4203pub unsafe extern "C" fn libinput_tablet_tool_has_slider(tool: *const libc::c_void) -> libc::c_int {
4204    let tool = tool.cast::<LibinputTabletTool>();
4205    (!tool.is_null() && (*tool).has_slider) as libc::c_int
4206}
4207
4208#[no_mangle]
4209pub unsafe extern "C" fn libinput_tablet_tool_has_tilt(tool: *const libc::c_void) -> libc::c_int {
4210    let tool = tool.cast::<LibinputTabletTool>();
4211    (!tool.is_null() && (*tool).has_tilt) as libc::c_int
4212}
4213
4214#[no_mangle]
4215pub unsafe extern "C" fn libinput_tablet_tool_has_wheel(tool: *const libc::c_void) -> libc::c_int {
4216    let tool = tool.cast::<LibinputTabletTool>();
4217    (!tool.is_null() && (*tool).has_wheel) as libc::c_int
4218}
4219
4220#[no_mangle]
4221pub unsafe extern "C" fn libinput_tablet_tool_ref(tool: *mut libc::c_void) -> *mut libc::c_void {
4222    let tablet_tool = tool.cast::<LibinputTabletTool>();
4223    if !tablet_tool.is_null() {
4224        (*tablet_tool)
4225            .refcount
4226            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4227    }
4228    tool
4229}
4230
4231#[no_mangle]
4232pub unsafe extern "C" fn libinput_tablet_tool_unref(tool: *mut libc::c_void) -> *mut libc::c_void {
4233    let tablet_tool = tool.cast::<LibinputTabletTool>();
4234    if tablet_tool.is_null() {
4235        return std::ptr::null_mut();
4236    }
4237    if (*tablet_tool)
4238        .refcount
4239        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
4240        == 1
4241    {
4242        if !(*tablet_tool).device.is_null() {
4243            libinput_device_unref((*tablet_tool).device);
4244            (*tablet_tool).device = std::ptr::null_mut();
4245        }
4246        drop(Box::from_raw(tablet_tool));
4247        std::ptr::null_mut()
4248    } else {
4249        tool
4250    }
4251}
4252
4253// ---------------------------------------------------------------------------
4254// Suspend / resume
4255// ---------------------------------------------------------------------------
4256
4257#[no_mangle]
4258pub unsafe extern "C" fn libinput_suspend(ctx: *mut LibinputContext) {
4259    if ctx.is_null() {
4260        return;
4261    }
4262    let mut events = std::collections::VecDeque::new();
4263    if let Ok(mut backend) = (*ctx).backend.lock() {
4264        backend.suspend(ctx, &mut events);
4265    }
4266    enqueue_events(ctx, events);
4267}
4268
4269#[no_mangle]
4270pub unsafe extern "C" fn libinput_resume(ctx: *mut LibinputContext) -> libc::c_int {
4271    if ctx.is_null() {
4272        return -1;
4273    }
4274    let mut events = std::collections::VecDeque::new();
4275    let status = if let Ok(mut backend) = (*ctx).backend.lock() {
4276        backend.resume(ctx, &mut events)
4277    } else {
4278        return -1;
4279    };
4280    enqueue_events(ctx, events);
4281    if !(*ctx).event_queue.is_empty() {
4282        (*ctx).signal_fd();
4283    }
4284    status
4285}
4286
4287#[cfg(test)]
4288mod tests {
4289    use super::*;
4290
4291    unsafe extern "C" fn deny_open(
4292        _path: *const libc::c_char,
4293        _flags: libc::c_int,
4294        _user_data: *mut libc::c_void,
4295    ) -> libc::c_int {
4296        -libc::EACCES
4297    }
4298
4299    unsafe extern "C" fn close_fd(_fd: libc::c_int, _user_data: *mut libc::c_void) {}
4300
4301    static INTERFACE: LibinputInterface = LibinputInterface {
4302        open_restricted: Some(deny_open),
4303        close_restricted: Some(close_fd),
4304    };
4305
4306    #[test]
4307    fn udev_context_requires_interface_and_udev() {
4308        unsafe {
4309            let fake_udev = 1usize as *mut libc::c_void;
4310            assert!(libinput_udev_create_context(
4311                std::ptr::null(),
4312                std::ptr::null_mut(),
4313                fake_udev,
4314            )
4315            .is_null());
4316            assert!(libinput_udev_create_context(
4317                &INTERFACE,
4318                std::ptr::null_mut(),
4319                std::ptr::null_mut(),
4320            )
4321            .is_null());
4322        }
4323    }
4324
4325    #[test]
4326    fn seat_assignment_is_udev_only_and_happens_once() {
4327        unsafe {
4328            let fake_udev = 1usize as *mut libc::c_void;
4329            let seat = std::ffi::CString::new("seat0").unwrap();
4330            let udev_ctx =
4331                libinput_udev_create_context(&INTERFACE, std::ptr::null_mut(), fake_udev);
4332            assert!(!udev_ctx.is_null());
4333            assert_eq!(libinput_udev_assign_seat(udev_ctx, seat.as_ptr()), 0);
4334            assert_eq!(libinput_udev_assign_seat(udev_ctx, seat.as_ptr()), -1);
4335            libinput_unref(udev_ctx);
4336
4337            let path_ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4338            assert!(!path_ctx.is_null());
4339            assert_eq!(libinput_udev_assign_seat(path_ctx, seat.as_ptr()), -1);
4340            libinput_unref(path_ctx);
4341        }
4342    }
4343
4344    #[test]
4345    fn overlong_seat_name_does_not_assign_or_queue_devices() {
4346        unsafe {
4347            let fake_udev = 1usize as *mut libc::c_void;
4348            let seat = std::ffi::CString::new("a".repeat(257)).unwrap();
4349            let ctx = libinput_udev_create_context(&INTERFACE, std::ptr::null_mut(), fake_udev);
4350            assert!(!ctx.is_null());
4351
4352            assert_eq!(libinput_udev_assign_seat(ctx, seat.as_ptr()), -1);
4353            assert!(!(*ctx).seat_assigned);
4354            assert!((*ctx).event_queue.is_empty());
4355
4356            libinput_unref(ctx);
4357        }
4358    }
4359
4360    #[test]
4361    fn suspend_and_resume_are_null_safe() {
4362        unsafe {
4363            libinput_suspend(std::ptr::null_mut());
4364            assert_eq!(libinput_resume(std::ptr::null_mut()), -1);
4365        }
4366    }
4367
4368    #[test]
4369    fn shared_device_groups_preserve_identity_and_user_data() {
4370        unsafe {
4371            let first = Box::new(LibinputDevice::new(
4372                "first",
4373                "/dev/input/event0",
4374                std::ptr::null_mut(),
4375                std::ptr::null_mut(),
4376            ));
4377            let group = first.group;
4378            let mut second = Box::new(LibinputDevice::new(
4379                "second",
4380                "/dev/input/event1",
4381                std::ptr::null_mut(),
4382                std::ptr::null_mut(),
4383            ));
4384            second.share_group(group);
4385
4386            assert_eq!(second.group, group);
4387            assert_eq!(second.abi.group, group);
4388            assert_eq!(
4389                (*group).refcount.load(std::sync::atomic::Ordering::Relaxed),
4390                2
4391            );
4392            let marker = 0x5ausize as *mut libc::c_void;
4393            libinput_device_group_set_user_data(group.cast(), marker);
4394            assert_eq!(
4395                libinput_device_group_get_user_data(second.group.cast()),
4396                marker
4397            );
4398
4399            drop(second);
4400            assert_eq!(
4401                (*group).refcount.load(std::sync::atomic::Ordering::Relaxed),
4402                1
4403            );
4404            drop(first);
4405        }
4406    }
4407
4408    #[test]
4409    fn custom_acceleration_validates_and_copies_curves() {
4410        unsafe {
4411            assert!(libinput_config_accel_create(0).is_null());
4412            let config = libinput_config_accel_create(4);
4413            assert!(!config.is_null());
4414
4415            let one_point = [1.0];
4416            assert_eq!(
4417                libinput_config_accel_set_points(config, 1, 1.0, 1, one_point.as_ptr()),
4418                2
4419            );
4420            let points = [0.0, 2.0, 6.0];
4421            assert_eq!(
4422                libinput_config_accel_set_points(config, 1, 1.0, points.len(), points.as_ptr()),
4423                0
4424            );
4425            let scroll_points = [0.0, 3.0, 9.0];
4426            assert_eq!(
4427                libinput_config_accel_set_points(
4428                    config,
4429                    2,
4430                    1.0,
4431                    scroll_points.len(),
4432                    scroll_points.as_ptr(),
4433                ),
4434                0
4435            );
4436
4437            let mut device = Box::new(LibinputDevice::new(
4438                "pointer",
4439                "/dev/input/event0",
4440                std::ptr::null_mut(),
4441                std::ptr::null_mut(),
4442            ));
4443            device.accel_available = true;
4444            assert_eq!(libinput_device_config_accel_apply(&mut *device, config), 0);
4445            libinput_config_accel_destroy(config);
4446
4447            let custom = device.accel_custom.as_mut().expect("custom config copied");
4448            let curve = custom.curve_mut(1).expect("motion curve copied");
4449            assert_eq!(curve.points, points);
4450            assert!((curve.factor(7.0, 0.0, 7_000) - 2.0).abs() < 1e-9);
4451            let scroll_curve = custom.curve_mut(2).expect("scroll curve copied");
4452            assert_eq!(scroll_curve.points, scroll_points);
4453            assert!((scroll_curve.factor(7.0, 0.0, 7_000) - 3.0).abs() < 1e-9);
4454        }
4455    }
4456
4457    #[test]
4458    fn custom_acceleration_uses_fallback_and_extrapolates() {
4459        let mut config = crate::ffi_types::AccelConfig::new(4);
4460        config.fallback = Some(crate::ffi_types::AccelCurve::new(1.0, vec![0.0, 1.0]));
4461        let curve = config.curve_mut(1).expect("fallback curve");
4462        assert!((curve.factor(14.0, 0.0, 7_000) - 1.0).abs() < 1e-9);
4463    }
4464
4465    #[test]
4466    fn plugin_paths_are_ordered_unique_and_frozen_after_load() {
4467        unsafe {
4468            let ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4469            let custom = std::ffi::CString::new("/tmp/plugins").unwrap();
4470            libinput_plugin_system_append_path(ctx, custom.as_ptr());
4471            libinput_plugin_system_append_path(ctx, custom.as_ptr());
4472            libinput_plugin_system_append_default_paths(ctx);
4473            assert_eq!((*ctx).plugin_paths.len(), 3);
4474            assert_eq!((&(*ctx).plugin_paths)[0].as_bytes(), b"/tmp/plugins");
4475            assert_eq!(libinput_plugin_system_load_plugins(ctx, 0), -libc::ENOSYS);
4476            let ignored = std::ffi::CString::new("/ignored").unwrap();
4477            libinput_plugin_system_append_path(ctx, ignored.as_ptr());
4478            assert_eq!((*ctx).plugin_paths.len(), 3);
4479            assert_eq!(libinput_plugin_system_load_plugins(ctx, 0), 0);
4480            libinput_unref(ctx);
4481        }
4482    }
4483
4484    #[test]
4485    fn queued_events_retain_devices_until_event_destroy() {
4486        unsafe {
4487            let ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4488            let device = Box::into_raw(Box::new(LibinputDevice::new(
4489                "keyboard",
4490                "/dev/input/event0",
4491                std::ptr::null_mut(),
4492                ctx,
4493            )));
4494            enqueue_event(
4495                ctx,
4496                LibinputEvent {
4497                    event_type: LibinputEventType::LIBINPUT_EVENT_KEYBOARD_KEY,
4498                    payload: EventPayload::KeyboardKey(crate::ffi_types::KeyboardKeyEvent {
4499                        time_usec: 1,
4500                        key: 30,
4501                        state: 1,
4502                        seat_key_count: 1,
4503                    }),
4504                    context: ctx,
4505                    device,
4506                },
4507            );
4508            assert_eq!(
4509                (*device).refcount.load(std::sync::atomic::Ordering::SeqCst),
4510                2
4511            );
4512            let event = libinput_get_event(ctx);
4513            assert_eq!(libinput_event_get_device(event), device);
4514            libinput_event_destroy(event);
4515            assert_eq!(
4516                (*device).refcount.load(std::sync::atomic::Ordering::SeqCst),
4517                1
4518            );
4519            assert!(libinput_device_unref(device).is_null());
4520            libinput_unref(ctx);
4521        }
4522    }
4523
4524    #[test]
4525    fn ref_and_unref_return_the_upstream_object_lifecycle() {
4526        unsafe {
4527            let seat = Box::into_raw(Box::new(LibinputSeat {
4528                physical_name: std::ffi::CString::new("seat0").unwrap(),
4529                logical_name: std::ffi::CString::new("default").unwrap(),
4530                refcount: std::sync::atomic::AtomicI32::new(1),
4531                user_data: std::ptr::null_mut(),
4532                context: std::ptr::null_mut(),
4533                button_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4534                key_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4535            }));
4536            assert_eq!(libinput_seat_ref(seat.cast()), seat.cast());
4537            assert_eq!(libinput_seat_unref(seat.cast()), seat.cast());
4538            assert!(libinput_seat_unref(seat.cast()).is_null());
4539
4540            let group = Box::into_raw(Box::new(LibinputTabletPadModeGroup {
4541                refcount: std::sync::atomic::AtomicI32::new(1),
4542                user_data: std::ptr::null_mut(),
4543                device: std::ptr::null_mut(),
4544                index: 0,
4545                num_modes: 1,
4546                current_mode: 0,
4547                button_mask: 0,
4548                dial_mask: 0,
4549                ring_mask: 0,
4550                strip_mask: 0,
4551                toggle_button_mask: 0,
4552                toggle_modes: Vec::new(),
4553            }));
4554            assert_eq!(
4555                libinput_tablet_pad_mode_group_ref(group.cast()),
4556                group.cast()
4557            );
4558            assert_eq!(
4559                libinput_tablet_pad_mode_group_unref(group.cast()),
4560                group.cast()
4561            );
4562            assert!(libinput_tablet_pad_mode_group_unref(group.cast()).is_null());
4563        }
4564    }
4565
4566    #[test]
4567    fn devices_retain_their_seat_until_device_destruction() {
4568        unsafe {
4569            let seat = Box::into_raw(Box::new(LibinputSeat {
4570                physical_name: std::ffi::CString::new("seat0").unwrap(),
4571                logical_name: std::ffi::CString::new("default").unwrap(),
4572                refcount: std::sync::atomic::AtomicI32::new(1),
4573                user_data: std::ptr::null_mut(),
4574                context: std::ptr::null_mut(),
4575                button_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4576                key_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4577            }));
4578            let device = Box::into_raw(Box::new(LibinputDevice::new(
4579                "keyboard",
4580                "/dev/input/event0",
4581                seat,
4582                std::ptr::null_mut(),
4583            )));
4584            assert_eq!(
4585                (*seat).refcount.load(std::sync::atomic::Ordering::SeqCst),
4586                2
4587            );
4588            assert!(libinput_device_unref(device).is_null());
4589            assert_eq!(
4590                (*seat).refcount.load(std::sync::atomic::Ordering::SeqCst),
4591                1
4592            );
4593            assert!(libinput_seat_unref(seat.cast()).is_null());
4594        }
4595    }
4596}