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_get_devnode(
1225    dev: *const LibinputDevice,
1226) -> *const libc::c_char {
1227    if dev.is_null() {
1228        return std::ptr::null();
1229    }
1230    (*dev).devnode.as_ptr()
1231}
1232
1233#[no_mangle]
1234pub unsafe extern "C" fn libinput_device_touch_get_touch_count(
1235    dev: *const LibinputDevice,
1236) -> libc::c_int {
1237    if dev.is_null() || !(*dev).has_touch {
1238        return -1;
1239    }
1240    (*dev).touch_count
1241}
1242
1243#[no_mangle]
1244pub unsafe extern "C" fn libinput_device_has_capability(
1245    dev: *const LibinputDevice,
1246    capability: u32,
1247) -> libc::c_int {
1248    if dev.is_null() {
1249        return 0;
1250    }
1251    let has = match capability {
1252        0 => (*dev).has_keyboard,
1253        1 => (*dev).has_pointer,
1254        2 => (*dev).has_touch,
1255        3 => (*dev).has_tablet,
1256        4 => (*dev).has_tablet_pad,
1257        5 => (*dev).has_gesture,
1258        6 => (*dev).has_switch,
1259        _ => false,
1260    };
1261    has as libc::c_int
1262}
1263
1264// ---------------------------------------------------------------------------
1265// Device configuration — tap
1266// ---------------------------------------------------------------------------
1267
1268#[no_mangle]
1269pub unsafe extern "C" fn libinput_device_config_tap_get_finger_count(
1270    dev: *const LibinputDevice,
1271) -> libc::c_int {
1272    if dev.is_null() {
1273        return 0;
1274    }
1275    (*dev).tap_finger_count
1276}
1277
1278#[no_mangle]
1279pub unsafe extern "C" fn libinput_device_config_tap_set_enabled(
1280    dev: *mut LibinputDevice,
1281    enabled: u32,
1282) -> u32 {
1283    if dev.is_null() {
1284        return 1;
1285    }
1286    if enabled > 1 {
1287        return 2;
1288    }
1289    if (*dev).tap_finger_count == 0 {
1290        return if enabled == 0 { 0 } else { 1 };
1291    }
1292    (*dev).tap_enabled = enabled != 0;
1293    0
1294}
1295
1296#[no_mangle]
1297pub unsafe extern "C" fn libinput_device_config_tap_get_enabled(dev: *const LibinputDevice) -> u32 {
1298    if dev.is_null() {
1299        return 0;
1300    }
1301    (*dev).tap_enabled as u32
1302}
1303
1304#[no_mangle]
1305pub unsafe extern "C" fn libinput_device_config_tap_get_default_enabled(
1306    dev: *const LibinputDevice,
1307) -> u32 {
1308    if dev.is_null() {
1309        return 0;
1310    }
1311    (*dev).tap_default_enabled as u32
1312}
1313
1314#[no_mangle]
1315pub unsafe extern "C" fn libinput_device_config_tap_set_drag_enabled(
1316    dev: *mut LibinputDevice,
1317    enabled: u32,
1318) -> u32 {
1319    if dev.is_null() {
1320        return 1;
1321    }
1322    if enabled > 1 {
1323        return 2;
1324    }
1325    if (*dev).tap_finger_count == 0 {
1326        return if enabled == 0 { 0 } else { 1 };
1327    }
1328    (*dev).tap_drag_enabled = enabled != 0;
1329    0
1330}
1331
1332#[no_mangle]
1333pub unsafe extern "C" fn libinput_device_config_tap_get_drag_enabled(
1334    dev: *const LibinputDevice,
1335) -> u32 {
1336    if dev.is_null() {
1337        return 0;
1338    }
1339    (*dev).tap_drag_enabled as u32
1340}
1341
1342#[no_mangle]
1343pub unsafe extern "C" fn libinput_device_config_tap_get_default_drag_enabled(
1344    dev: *const LibinputDevice,
1345) -> u32 {
1346    if dev.is_null() {
1347        return 0;
1348    }
1349    ((*dev).tap_finger_count != 0) as u32
1350}
1351
1352#[no_mangle]
1353pub unsafe extern "C" fn libinput_device_config_tap_set_drag_lock_enabled(
1354    dev: *mut LibinputDevice,
1355    enabled: u32,
1356) -> u32 {
1357    if dev.is_null() {
1358        return 1;
1359    }
1360    if enabled > 2 {
1361        return 2;
1362    }
1363    if (*dev).tap_finger_count == 0 {
1364        return if enabled == 0 { 0 } else { 1 };
1365    }
1366    (*dev).tap_drag_lock_enabled = if enabled == 1 { 1 } else { enabled };
1367    0
1368}
1369
1370#[no_mangle]
1371pub unsafe extern "C" fn libinput_device_config_tap_get_drag_lock_enabled(
1372    dev: *const LibinputDevice,
1373) -> u32 {
1374    if dev.is_null() {
1375        return 0;
1376    }
1377    (*dev).tap_drag_lock_enabled
1378}
1379
1380/// Button map: 0 = LRM (default), 1 = LMR
1381#[no_mangle]
1382pub unsafe extern "C" fn libinput_device_config_tap_set_button_map(
1383    dev: *mut LibinputDevice,
1384    map: u32,
1385) -> u32 {
1386    if dev.is_null() {
1387        return 1;
1388    }
1389    if map > 1 {
1390        return 2;
1391    }
1392    if (*dev).tap_finger_count == 0 {
1393        return 1;
1394    }
1395    (*dev).tap_button_map = map;
1396    0
1397}
1398
1399#[no_mangle]
1400pub unsafe extern "C" fn libinput_device_config_tap_get_button_map(
1401    dev: *const LibinputDevice,
1402) -> u32 {
1403    if dev.is_null() {
1404        return 0;
1405    }
1406    (*dev).tap_button_map
1407}
1408
1409#[no_mangle]
1410pub unsafe extern "C" fn libinput_device_config_tap_get_default_button_map(
1411    _dev: *const LibinputDevice,
1412) -> u32 {
1413    0
1414} // LIBINPUT_CONFIG_TAP_MAP_LRM
1415
1416#[no_mangle]
1417pub unsafe extern "C" fn libinput_device_config_3fg_drag_get_finger_count(
1418    dev: *const LibinputDevice,
1419) -> libc::c_int {
1420    if dev.is_null() || !(*dev).has_gesture {
1421        return 0;
1422    }
1423    (*dev).mt_slot_count
1424}
1425
1426#[no_mangle]
1427pub unsafe extern "C" fn libinput_device_config_3fg_drag_set_enabled(
1428    dev: *mut LibinputDevice,
1429    enable: u32,
1430) -> u32 {
1431    if dev.is_null() {
1432        return 1;
1433    }
1434    if !matches!(enable, 0..=2) {
1435        return 2;
1436    }
1437    if libinput_device_config_3fg_drag_get_finger_count(dev) < 3 {
1438        return 1;
1439    }
1440    (*dev).drag_3fg_enabled = enable;
1441    0
1442}
1443
1444#[no_mangle]
1445pub unsafe extern "C" fn libinput_device_config_3fg_drag_get_enabled(
1446    dev: *const LibinputDevice,
1447) -> u32 {
1448    if dev.is_null() {
1449        return 0;
1450    }
1451    (*dev).drag_3fg_enabled
1452}
1453
1454#[no_mangle]
1455pub unsafe extern "C" fn libinput_device_config_3fg_drag_get_default_enabled(
1456    _dev: *const LibinputDevice,
1457) -> u32 {
1458    0
1459}
1460
1461// ---------------------------------------------------------------------------
1462// Device configuration — pointer acceleration
1463// ---------------------------------------------------------------------------
1464
1465#[no_mangle]
1466pub unsafe extern "C" fn libinput_config_accel_create(profile: u32) -> *mut libc::c_void {
1467    if !matches!(profile, 1 | 2 | 4) {
1468        return std::ptr::null_mut();
1469    }
1470    Box::into_raw(Box::new(crate::ffi_types::AccelConfig::new(profile))) as *mut libc::c_void
1471}
1472
1473#[no_mangle]
1474pub unsafe extern "C" fn libinput_config_accel_destroy(accel_config: *mut libc::c_void) {
1475    if !accel_config.is_null() {
1476        drop(Box::from_raw(
1477            accel_config as *mut crate::ffi_types::AccelConfig,
1478        ));
1479    }
1480}
1481
1482#[no_mangle]
1483pub unsafe extern "C" fn libinput_config_accel_set_points(
1484    accel_config: *mut libc::c_void,
1485    accel_type: u32,
1486    step: f64,
1487    npoints: libc::size_t,
1488    points: *const f64,
1489) -> u32 {
1490    if accel_config.is_null()
1491        || points.is_null()
1492        || !step.is_finite()
1493        || step <= 0.0
1494        || step > 10_000.0
1495        || !(2..=64).contains(&npoints)
1496    {
1497        return 2;
1498    }
1499    let config = &mut *(accel_config as *mut crate::ffi_types::AccelConfig);
1500    if config.profile != 4 || !matches!(accel_type, 0..=2) {
1501        return 2;
1502    }
1503    let points = std::slice::from_raw_parts(points, npoints);
1504    if points
1505        .iter()
1506        .any(|point| !point.is_finite() || *point < 0.0 || *point > 10_000.0)
1507    {
1508        return 2;
1509    }
1510    let curve = crate::ffi_types::AccelCurve::new(step, points.to_vec());
1511    match accel_type {
1512        0 => config.fallback = Some(curve),
1513        1 => config.motion = Some(curve),
1514        2 => config.scroll = Some(curve),
1515        _ => unreachable!(),
1516    }
1517    0
1518}
1519
1520#[no_mangle]
1521pub unsafe extern "C" fn libinput_device_config_accel_apply(
1522    dev: *mut LibinputDevice,
1523    accel_config: *mut libc::c_void,
1524) -> u32 {
1525    if dev.is_null() || accel_config.is_null() {
1526        return 2;
1527    }
1528    if !(*dev).accel_available {
1529        return 1;
1530    }
1531    let config = &*(accel_config as *const crate::ffi_types::AccelConfig);
1532    let profile = config.profile;
1533    if profile & libinput_device_config_accel_get_profiles(dev) == 0 {
1534        return 1;
1535    }
1536    (*dev).accel_profile = profile;
1537    (*dev).accel_speed = 0.0;
1538    (*dev).accel_custom = (profile == 4).then(|| config.clone());
1539    0
1540}
1541
1542#[no_mangle]
1543pub unsafe extern "C" fn libinput_device_config_accel_is_available(
1544    dev: *const LibinputDevice,
1545) -> libc::c_int {
1546    if dev.is_null() {
1547        return 0;
1548    }
1549    (*dev).accel_available as libc::c_int
1550}
1551
1552#[no_mangle]
1553pub unsafe extern "C" fn libinput_device_config_accel_set_speed(
1554    dev: *mut LibinputDevice,
1555    speed: f64,
1556) -> u32 {
1557    if dev.is_null() {
1558        return 1;
1559    }
1560    if !speed.is_finite() || !(-1.0..=1.0).contains(&speed) {
1561        return 2;
1562    }
1563    if !(*dev).accel_available {
1564        return 1;
1565    }
1566    (*dev).accel_speed = speed;
1567    0
1568}
1569
1570#[no_mangle]
1571pub unsafe extern "C" fn libinput_device_config_accel_get_speed(dev: *const LibinputDevice) -> f64 {
1572    if dev.is_null() {
1573        return 0.0;
1574    }
1575    (*dev).accel_speed
1576}
1577
1578#[no_mangle]
1579pub unsafe extern "C" fn libinput_device_config_accel_get_default_speed(
1580    _dev: *const LibinputDevice,
1581) -> f64 {
1582    0.0
1583}
1584
1585#[no_mangle]
1586pub unsafe extern "C" fn libinput_device_config_accel_get_profiles(
1587    dev: *const LibinputDevice,
1588) -> u32 {
1589    if dev.is_null() {
1590        return 0;
1591    }
1592    if (*dev).accel_available
1593        && !((*dev).has_tablet && !(*dev).has_gesture && !(*dev).has_tablet_pad)
1594    {
1595        0b111
1596    } else {
1597        0
1598    }
1599}
1600
1601#[no_mangle]
1602pub unsafe extern "C" fn libinput_device_config_accel_set_profile(
1603    dev: *mut LibinputDevice,
1604    profile: u32,
1605) -> u32 {
1606    if dev.is_null() {
1607        return 1;
1608    }
1609    if profile == 0 || profile & !0b111 != 0 || profile.count_ones() != 1 {
1610        return 2;
1611    }
1612    if profile & libinput_device_config_accel_get_profiles(dev) == 0 {
1613        return 1;
1614    }
1615    (*dev).accel_profile = profile;
1616    (*dev).accel_custom = if profile == 4 {
1617        Some(crate::ffi_types::AccelConfig::new(profile))
1618    } else {
1619        None
1620    };
1621    0
1622}
1623
1624#[no_mangle]
1625pub unsafe extern "C" fn libinput_device_config_accel_get_profile(
1626    dev: *const LibinputDevice,
1627) -> u32 {
1628    if dev.is_null() {
1629        return 0;
1630    }
1631    if (*dev).accel_available
1632        && !((*dev).has_tablet && !(*dev).has_gesture && !(*dev).has_tablet_pad)
1633    {
1634        (*dev).accel_profile
1635    } else {
1636        0
1637    }
1638}
1639
1640#[no_mangle]
1641pub unsafe extern "C" fn libinput_device_config_accel_get_default_profile(
1642    dev: *const LibinputDevice,
1643) -> u32 {
1644    if dev.is_null()
1645        || !(*dev).accel_available
1646        || ((*dev).has_tablet && !(*dev).has_gesture && !(*dev).has_tablet_pad)
1647    {
1648        0
1649    } else {
1650        2
1651    }
1652}
1653
1654// ---------------------------------------------------------------------------
1655// Device configuration — natural scroll
1656// ---------------------------------------------------------------------------
1657
1658#[no_mangle]
1659pub unsafe extern "C" fn libinput_device_config_scroll_has_natural_scroll(
1660    dev: *const LibinputDevice,
1661) -> libc::c_int {
1662    if dev.is_null() {
1663        return 0;
1664    }
1665    (*dev).has_pointer as libc::c_int
1666}
1667
1668#[no_mangle]
1669pub unsafe extern "C" fn libinput_device_config_scroll_set_natural_scroll_enabled(
1670    dev: *mut LibinputDevice,
1671    enabled: libc::c_int,
1672) -> u32 {
1673    if dev.is_null() || !(*dev).has_pointer {
1674        return 1;
1675    }
1676    (*dev).natural_scroll = enabled != 0;
1677    0
1678}
1679
1680#[no_mangle]
1681pub unsafe extern "C" fn libinput_device_config_scroll_get_natural_scroll_enabled(
1682    dev: *const LibinputDevice,
1683) -> libc::c_int {
1684    if dev.is_null() {
1685        return 0;
1686    }
1687    (*dev).natural_scroll as libc::c_int
1688}
1689
1690#[no_mangle]
1691pub unsafe extern "C" fn libinput_device_config_scroll_get_default_natural_scroll_enabled(
1692    dev: *const LibinputDevice,
1693) -> libc::c_int {
1694    if dev.is_null() {
1695        return 0;
1696    }
1697    ((*dev).scroll_methods & 2 != 0 && (*dev).vendor_id == 0x05ac) as libc::c_int
1698}
1699
1700// ---------------------------------------------------------------------------
1701// Device configuration — left-handed
1702// ---------------------------------------------------------------------------
1703
1704#[no_mangle]
1705pub unsafe extern "C" fn libinput_device_config_left_handed_is_available(
1706    dev: *const LibinputDevice,
1707) -> libc::c_int {
1708    if dev.is_null() {
1709        return 0;
1710    }
1711    (*dev).left_handed_available as libc::c_int
1712}
1713
1714#[no_mangle]
1715pub unsafe extern "C" fn libinput_device_config_left_handed_set(
1716    dev: *mut LibinputDevice,
1717    enabled: libc::c_int,
1718) -> u32 {
1719    if dev.is_null() || !(*dev).left_handed_available {
1720        return 1;
1721    }
1722    (*dev).left_handed = enabled != 0;
1723    0
1724}
1725
1726#[no_mangle]
1727pub unsafe extern "C" fn libinput_device_config_left_handed_get(
1728    dev: *const LibinputDevice,
1729) -> libc::c_int {
1730    if dev.is_null() {
1731        return 0;
1732    }
1733    (*dev).left_handed as libc::c_int
1734}
1735
1736#[no_mangle]
1737pub unsafe extern "C" fn libinput_device_config_left_handed_get_default(
1738    _dev: *const LibinputDevice,
1739) -> libc::c_int {
1740    0
1741}
1742
1743// ---------------------------------------------------------------------------
1744// Device configuration — scroll method
1745// ---------------------------------------------------------------------------
1746
1747#[no_mangle]
1748pub unsafe extern "C" fn libinput_device_config_scroll_get_methods(
1749    dev: *const LibinputDevice,
1750) -> u32 {
1751    if dev.is_null() {
1752        return 0;
1753    }
1754    (*dev).scroll_methods
1755}
1756
1757#[no_mangle]
1758pub unsafe extern "C" fn libinput_device_config_scroll_set_method(
1759    dev: *mut LibinputDevice,
1760    method: u32,
1761) -> u32 {
1762    if dev.is_null() {
1763        return 1;
1764    }
1765    if !matches!(method, 0 | 1 | 2 | 4) {
1766        return 2;
1767    }
1768    if method != 0 && method & (*dev).scroll_methods == 0 {
1769        return 1;
1770    }
1771    if (*dev).scroll_method == method {
1772        return 0;
1773    }
1774    let ctx = (*dev).context;
1775    if !ctx.is_null() {
1776        let mut events = std::collections::VecDeque::new();
1777        if let Ok(mut backend) = (*ctx).backend.try_lock() {
1778            backend.stop_scroll_for_device(ctx, dev, &mut events);
1779        }
1780        enqueue_events(ctx, events);
1781    }
1782    (*dev).scroll_method = method;
1783    0
1784}
1785
1786#[no_mangle]
1787pub unsafe extern "C" fn libinput_device_config_scroll_get_method(
1788    dev: *const LibinputDevice,
1789) -> u32 {
1790    if dev.is_null() {
1791        return 0;
1792    }
1793    (*dev).scroll_method
1794}
1795
1796#[no_mangle]
1797pub unsafe extern "C" fn libinput_device_config_scroll_get_default_method(
1798    dev: *const LibinputDevice,
1799) -> u32 {
1800    if dev.is_null() {
1801        0
1802    } else {
1803        (*dev).scroll_default_method
1804    }
1805}
1806
1807#[no_mangle]
1808pub unsafe extern "C" fn libinput_device_config_scroll_set_button(
1809    dev: *mut LibinputDevice,
1810    button: u32,
1811) -> u32 {
1812    if dev.is_null() || !(*dev).supports_button_scroll {
1813        return 1;
1814    }
1815    if button != 0
1816        && match u16::try_from(button) {
1817            Ok(button) => !(*dev).event_codes.contains(&button),
1818            Err(_) => true,
1819        }
1820    {
1821        return 2;
1822    }
1823    (*dev).scroll_button = button;
1824    0
1825}
1826
1827#[no_mangle]
1828pub unsafe extern "C" fn libinput_device_config_scroll_get_button(
1829    dev: *const LibinputDevice,
1830) -> u32 {
1831    if dev.is_null() || !(*dev).supports_button_scroll {
1832        0
1833    } else {
1834        (*dev).scroll_button
1835    }
1836}
1837
1838#[no_mangle]
1839pub unsafe extern "C" fn libinput_device_config_scroll_set_button_lock(
1840    dev: *mut LibinputDevice,
1841    state: u32,
1842) -> u32 {
1843    if dev.is_null() || !(*dev).supports_button_scroll {
1844        return 1;
1845    }
1846    if state > 1 {
1847        return 2;
1848    }
1849    (*dev).scroll_button_lock = state;
1850    0
1851}
1852
1853#[no_mangle]
1854pub unsafe extern "C" fn libinput_device_config_scroll_get_button_lock(
1855    dev: *const LibinputDevice,
1856) -> u32 {
1857    if dev.is_null() || !(*dev).supports_button_scroll {
1858        0
1859    } else {
1860        (*dev).scroll_button_lock
1861    }
1862}
1863
1864#[no_mangle]
1865pub unsafe extern "C" fn libinput_device_config_scroll_get_default_button_lock(
1866    _dev: *const LibinputDevice,
1867) -> u32 {
1868    0
1869}
1870
1871// ---------------------------------------------------------------------------
1872// Device configuration — click method
1873// ---------------------------------------------------------------------------
1874
1875#[no_mangle]
1876pub unsafe extern "C" fn libinput_device_config_click_get_methods(
1877    dev: *const LibinputDevice,
1878) -> u32 {
1879    if dev.is_null() {
1880        return 0;
1881    }
1882    (*dev).click_methods
1883}
1884
1885#[no_mangle]
1886pub unsafe extern "C" fn libinput_device_config_click_set_method(
1887    dev: *mut LibinputDevice,
1888    method: u32,
1889) -> u32 {
1890    if dev.is_null() {
1891        return 1;
1892    }
1893    if !matches!(method, 0..=2) {
1894        return 2;
1895    }
1896    if method != 0 && method & (*dev).click_methods == 0 {
1897        return 1;
1898    }
1899    (*dev).click_method = method;
1900    0
1901}
1902
1903#[no_mangle]
1904pub unsafe extern "C" fn libinput_device_config_click_get_method(
1905    dev: *const LibinputDevice,
1906) -> u32 {
1907    if dev.is_null() {
1908        return 0;
1909    }
1910    (*dev).click_method
1911}
1912
1913#[no_mangle]
1914pub unsafe extern "C" fn libinput_device_config_click_get_default_method(
1915    dev: *const LibinputDevice,
1916) -> u32 {
1917    if dev.is_null() {
1918        0
1919    } else {
1920        (*dev).click_default_method
1921    }
1922}
1923
1924#[no_mangle]
1925pub unsafe extern "C" fn libinput_device_config_click_set_clickfinger_button_map(
1926    dev: *mut LibinputDevice,
1927    map: u32,
1928) -> u32 {
1929    if dev.is_null() {
1930        return 1;
1931    }
1932    if !matches!(map, 0 | 1) {
1933        return 2;
1934    }
1935    if (*dev).click_methods & 2 == 0 {
1936        return 1;
1937    }
1938    (*dev).clickfinger_button_map = map;
1939    0
1940}
1941
1942#[no_mangle]
1943pub unsafe extern "C" fn libinput_device_config_click_get_clickfinger_button_map(
1944    dev: *const LibinputDevice,
1945) -> u32 {
1946    if dev.is_null() || (*dev).click_methods & 2 == 0 {
1947        0
1948    } else {
1949        (*dev).clickfinger_button_map
1950    }
1951}
1952
1953#[no_mangle]
1954pub unsafe extern "C" fn libinput_device_config_click_get_default_clickfinger_button_map(
1955    dev: *const LibinputDevice,
1956) -> u32 {
1957    if dev.is_null() || (*dev).click_methods & 2 == 0 {
1958        0
1959    } else {
1960        (*dev).clickfinger_default_button_map
1961    }
1962}
1963
1964// ---------------------------------------------------------------------------
1965// Device configuration — middle button emulation
1966// ---------------------------------------------------------------------------
1967
1968#[no_mangle]
1969pub unsafe extern "C" fn libinput_device_config_middle_emulation_is_available(
1970    dev: *const LibinputDevice,
1971) -> libc::c_int {
1972    if dev.is_null() {
1973        return 0;
1974    }
1975    (*dev).middle_emulation_available as libc::c_int
1976}
1977
1978#[no_mangle]
1979pub unsafe extern "C" fn libinput_device_config_middle_emulation_set_enabled(
1980    dev: *mut LibinputDevice,
1981    enabled: u32,
1982) -> u32 {
1983    if dev.is_null() {
1984        return 1;
1985    }
1986    if enabled > 1 {
1987        return 2;
1988    }
1989    if enabled == 1 && !(*dev).middle_emulation_available {
1990        return 1;
1991    }
1992    (*dev).middle_emulation = enabled != 0;
1993    0
1994}
1995
1996#[no_mangle]
1997pub unsafe extern "C" fn libinput_device_config_middle_emulation_get_enabled(
1998    dev: *const LibinputDevice,
1999) -> u32 {
2000    if dev.is_null() || !(*dev).middle_emulation_available {
2001        return 0;
2002    }
2003    (*dev).middle_emulation as u32
2004}
2005
2006#[no_mangle]
2007pub unsafe extern "C" fn libinput_device_config_middle_emulation_get_default_enabled(
2008    dev: *const LibinputDevice,
2009) -> u32 {
2010    if dev.is_null() || !(*dev).middle_emulation_available {
2011        return 0;
2012    }
2013    (*dev).middle_emulation_default as u32
2014}
2015
2016// ---------------------------------------------------------------------------
2017// Device configuration — disable-while-typing
2018// ---------------------------------------------------------------------------
2019
2020#[no_mangle]
2021pub unsafe extern "C" fn libinput_device_config_dwt_is_available(
2022    dev: *const LibinputDevice,
2023) -> libc::c_int {
2024    (!dev.is_null() && (*dev).dwt_available) as libc::c_int
2025}
2026
2027#[no_mangle]
2028pub unsafe extern "C" fn libinput_device_config_dwt_set_enabled(
2029    dev: *mut LibinputDevice,
2030    enabled: u32,
2031) -> u32 {
2032    if dev.is_null() {
2033        return 1;
2034    }
2035    if !matches!(enabled, 0 | 1) {
2036        return 2;
2037    }
2038    if !(*dev).dwt_available {
2039        return if enabled == 0 { 0 } else { 1 };
2040    }
2041    (*dev).dwt_enabled = enabled == 1;
2042    0
2043}
2044
2045#[no_mangle]
2046pub unsafe extern "C" fn libinput_device_config_dwt_get_enabled(dev: *const LibinputDevice) -> u32 {
2047    if dev.is_null() || !(*dev).dwt_available {
2048        return 0;
2049    }
2050    (*dev).dwt_enabled as u32
2051}
2052
2053#[no_mangle]
2054pub unsafe extern "C" fn libinput_device_config_dwt_get_default_enabled(
2055    dev: *const LibinputDevice,
2056) -> u32 {
2057    (!dev.is_null() && (*dev).dwt_available) as u32
2058}
2059
2060#[no_mangle]
2061pub unsafe extern "C" fn libinput_device_config_dwt_set_timeout(
2062    dev: *mut LibinputDevice,
2063    millis: u32,
2064) -> u32 {
2065    if dev.is_null() {
2066        return 1;
2067    }
2068    if millis == 0 {
2069        return 2;
2070    }
2071    if !(*dev).dwt_available {
2072        return 1;
2073    }
2074    if !(100..=5000).contains(&millis) {
2075        return 2;
2076    }
2077    (*dev).dwt_timeout = millis;
2078    0
2079}
2080
2081#[no_mangle]
2082pub unsafe extern "C" fn libinput_device_config_dwt_get_timeout(dev: *const LibinputDevice) -> u32 {
2083    if dev.is_null() || !(*dev).dwt_available {
2084        0
2085    } else {
2086        (*dev).dwt_timeout
2087    }
2088}
2089
2090#[no_mangle]
2091pub unsafe extern "C" fn libinput_device_config_dwt_get_default_timeout(
2092    dev: *const LibinputDevice,
2093) -> u32 {
2094    if !dev.is_null() && (*dev).dwt_available {
2095        500
2096    } else {
2097        0
2098    }
2099}
2100
2101#[no_mangle]
2102pub unsafe extern "C" fn libinput_device_config_dwtp_is_available(
2103    dev: *const LibinputDevice,
2104) -> libc::c_int {
2105    (!dev.is_null() && (*dev).dwtp_available) as libc::c_int
2106}
2107
2108#[no_mangle]
2109pub unsafe extern "C" fn libinput_device_config_dwtp_set_enabled(
2110    dev: *mut LibinputDevice,
2111    enabled: u32,
2112) -> u32 {
2113    if dev.is_null() {
2114        return 1;
2115    }
2116    if !matches!(enabled, 0 | 1) {
2117        return 2;
2118    }
2119    if !(*dev).dwtp_available {
2120        return if enabled == 0 { 0 } else { 1 };
2121    }
2122    (*dev).dwtp_enabled = enabled == 1;
2123    0
2124}
2125
2126#[no_mangle]
2127pub unsafe extern "C" fn libinput_device_config_dwtp_get_enabled(
2128    dev: *const LibinputDevice,
2129) -> u32 {
2130    if dev.is_null() || !(*dev).dwtp_available {
2131        0
2132    } else {
2133        (*dev).dwtp_enabled as u32
2134    }
2135}
2136
2137#[no_mangle]
2138pub unsafe extern "C" fn libinput_device_config_dwtp_get_default_enabled(
2139    dev: *const LibinputDevice,
2140) -> u32 {
2141    (!dev.is_null() && (*dev).dwtp_available) as u32
2142}
2143
2144#[no_mangle]
2145pub unsafe extern "C" fn libinput_device_config_dwtp_set_timeout(
2146    dev: *mut LibinputDevice,
2147    millis: u32,
2148) -> u32 {
2149    if dev.is_null() {
2150        return 1;
2151    }
2152    if millis == 0 {
2153        return 2;
2154    }
2155    if !(*dev).dwtp_available {
2156        return 1;
2157    }
2158    if !(100..=5000).contains(&millis) {
2159        return 2;
2160    }
2161    (*dev).dwtp_timeout = millis;
2162    0
2163}
2164
2165#[no_mangle]
2166pub unsafe extern "C" fn libinput_device_config_dwtp_get_timeout(
2167    dev: *const LibinputDevice,
2168) -> u32 {
2169    if dev.is_null() || !(*dev).dwtp_available {
2170        0
2171    } else {
2172        (*dev).dwtp_timeout
2173    }
2174}
2175
2176#[no_mangle]
2177pub unsafe extern "C" fn libinput_device_config_dwtp_get_default_timeout(
2178    dev: *const LibinputDevice,
2179) -> u32 {
2180    if !dev.is_null() && (*dev).dwtp_available {
2181        300
2182    } else {
2183        0
2184    }
2185}
2186
2187// ---------------------------------------------------------------------------
2188// Device configuration — calibration matrix
2189// ---------------------------------------------------------------------------
2190
2191#[no_mangle]
2192pub unsafe extern "C" fn libinput_device_config_calibration_has_matrix(
2193    dev: *const LibinputDevice,
2194) -> libc::c_int {
2195    if dev.is_null() {
2196        return 0;
2197    }
2198    (*dev).calibration_available as libc::c_int
2199}
2200
2201#[no_mangle]
2202pub unsafe extern "C" fn libinput_device_config_calibration_set_matrix(
2203    dev: *mut LibinputDevice,
2204    matrix: *const f32,
2205) -> u32 {
2206    if dev.is_null() || matrix.is_null() || !(*dev).calibration_available {
2207        return 1;
2208    }
2209    (*dev)
2210        .calibration
2211        .copy_from_slice(std::slice::from_raw_parts(matrix, 6));
2212    0
2213}
2214
2215#[no_mangle]
2216pub unsafe extern "C" fn libinput_device_config_calibration_get_matrix(
2217    dev: *const LibinputDevice,
2218    matrix: *mut f32,
2219) -> libc::c_int {
2220    if dev.is_null() || matrix.is_null() || !(*dev).calibration_available {
2221        return 0;
2222    }
2223    std::slice::from_raw_parts_mut(matrix, 6).copy_from_slice(&(*dev).calibration);
2224    ((*dev).calibration != [1.0_f32, 0.0, 0.0, 0.0, 1.0, 0.0]) as libc::c_int
2225}
2226
2227#[no_mangle]
2228pub unsafe extern "C" fn libinput_device_config_calibration_get_default_matrix(
2229    dev: *const LibinputDevice,
2230    matrix: *mut f32,
2231) -> libc::c_int {
2232    if dev.is_null() || matrix.is_null() || !(*dev).calibration_available {
2233        return 0;
2234    }
2235    std::slice::from_raw_parts_mut(matrix, 6).copy_from_slice(&(*dev).default_calibration);
2236    ((*dev).default_calibration != [1.0_f32, 0.0, 0.0, 0.0, 1.0, 0.0]) as libc::c_int
2237}
2238
2239// ---------------------------------------------------------------------------
2240// Seat
2241// ---------------------------------------------------------------------------
2242
2243#[no_mangle]
2244pub unsafe extern "C" fn libinput_device_get_seat(dev: *const LibinputDevice) -> *mut libc::c_void {
2245    if dev.is_null() {
2246        return std::ptr::null_mut();
2247    }
2248    (*dev).seat as *mut libc::c_void
2249}
2250
2251#[no_mangle]
2252pub unsafe extern "C" fn libinput_device_set_seat_logical_name(
2253    dev: *mut LibinputDevice,
2254    name: *const libc::c_char,
2255) -> libc::c_int {
2256    if dev.is_null() || name.is_null() || (*dev).seat.is_null() {
2257        return -1;
2258    }
2259    let name = CStr::from_ptr(name);
2260    if name.to_bytes().is_empty() || name.to_bytes().len() > 255 {
2261        return -1;
2262    }
2263    if (*(*dev).seat).logical_name.as_c_str() == name {
2264        return 0;
2265    }
2266    let Ok(logical_name) = std::ffi::CString::new(name.to_bytes()) else {
2267        return -1;
2268    };
2269    let ctx = (*dev).context;
2270    if ctx.is_null() {
2271        return -1;
2272    }
2273    let physical_name = (*(*dev).seat).physical_name.clone();
2274    let new_seat = (*ctx)
2275        .seats
2276        .iter()
2277        .copied()
2278        .find(|seat| {
2279            !seat.is_null()
2280                && (**seat).physical_name == physical_name
2281                && (**seat).logical_name == logical_name
2282        })
2283        .unwrap_or_else(|| {
2284            let seat = Box::into_raw(Box::new(LibinputSeat {
2285                physical_name,
2286                logical_name,
2287                refcount: std::sync::atomic::AtomicI32::new(1),
2288                user_data: std::ptr::null_mut(),
2289                context: ctx,
2290                button_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
2291                key_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
2292            }));
2293            (*ctx).seats.push(seat);
2294            seat
2295        });
2296
2297    let path = std::path::PathBuf::from((*dev).devnode.to_string_lossy().into_owned());
2298    let mut removed = std::collections::VecDeque::new();
2299    let mut added = Vec::new();
2300    let replaced = if let Ok(mut backend) = (*ctx).backend.lock() {
2301        if backend.remove_device(ctx, dev, &mut removed) {
2302            backend.try_open(ctx, &path, &mut added);
2303            true
2304        } else {
2305            false
2306        }
2307    } else {
2308        false
2309    };
2310    let Some(replacement) = added.first().map(|event| event.device) else {
2311        return -1;
2312    };
2313    if !replaced || replacement.is_null() {
2314        return -1;
2315    }
2316    (*ctx).devices.retain(|candidate| *candidate != dev);
2317    libinput_device_unref(dev);
2318    let old_seat = (*replacement).seat;
2319    if old_seat != new_seat {
2320        libinput_seat_ref(new_seat.cast());
2321        (*replacement).seat = new_seat;
2322        (*replacement).abi.seat = new_seat;
2323        libinput_seat_unref(old_seat.cast());
2324    }
2325    enqueue_events(ctx, removed);
2326    enqueue_events(ctx, added);
2327    (*ctx).signal_fd();
2328    0
2329}
2330
2331#[no_mangle]
2332pub unsafe extern "C" fn libinput_seat_get_physical_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)).physical_name.as_ptr()
2339}
2340
2341#[no_mangle]
2342pub unsafe extern "C" fn libinput_seat_get_logical_name(
2343    seat: *const libc::c_void,
2344) -> *const libc::c_char {
2345    if seat.is_null() {
2346        return std::ptr::null();
2347    }
2348    (*(seat as *const LibinputSeat)).logical_name.as_ptr()
2349}
2350
2351#[no_mangle]
2352pub unsafe extern "C" fn libinput_seat_get_context(
2353    seat: *const libc::c_void,
2354) -> *mut LibinputContext {
2355    if seat.is_null() {
2356        return std::ptr::null_mut();
2357    }
2358    (*(seat as *const LibinputSeat)).context
2359}
2360
2361#[no_mangle]
2362pub unsafe extern "C" fn libinput_seat_ref(seat: *mut libc::c_void) -> *mut libc::c_void {
2363    if !seat.is_null() {
2364        (*(seat as *mut LibinputSeat))
2365            .refcount
2366            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
2367    }
2368    seat
2369}
2370
2371#[no_mangle]
2372pub unsafe extern "C" fn libinput_seat_unref(seat: *mut libc::c_void) -> *mut libc::c_void {
2373    if seat.is_null() {
2374        return std::ptr::null_mut();
2375    }
2376    let seat = seat as *mut LibinputSeat;
2377    if (*seat)
2378        .refcount
2379        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
2380        == 1
2381    {
2382        drop(Box::from_raw(seat));
2383        std::ptr::null_mut()
2384    } else {
2385        seat.cast()
2386    }
2387}
2388
2389#[no_mangle]
2390pub unsafe extern "C" fn libinput_seat_set_user_data(
2391    seat: *mut libc::c_void,
2392    data: *mut libc::c_void,
2393) {
2394    if !seat.is_null() {
2395        (*(seat as *mut LibinputSeat)).user_data = data;
2396    }
2397}
2398
2399#[no_mangle]
2400pub unsafe extern "C" fn libinput_seat_get_user_data(
2401    seat: *const libc::c_void,
2402) -> *mut libc::c_void {
2403    if seat.is_null() {
2404        return std::ptr::null_mut();
2405    }
2406    (*(seat as *const LibinputSeat)).user_data
2407}
2408
2409// ---------------------------------------------------------------------------
2410// Status strings
2411// ---------------------------------------------------------------------------
2412
2413#[no_mangle]
2414pub unsafe extern "C" fn libinput_config_status_to_str(status: u32) -> *const libc::c_char {
2415    match status {
2416        0 => b"success\0".as_ptr().cast(),
2417        1 => b"unsupported\0".as_ptr().cast(),
2418        2 => b"invalid\0".as_ptr().cast(),
2419        _ => std::ptr::null(),
2420    }
2421}
2422
2423// ---------------------------------------------------------------------------
2424// Logging
2425// ---------------------------------------------------------------------------
2426
2427#[no_mangle]
2428pub unsafe extern "C" fn libinput_log_set_priority(ctx: *mut LibinputContext, priority: u32) {
2429    if !ctx.is_null() {
2430        (*ctx).log_priority = priority;
2431    }
2432}
2433
2434#[no_mangle]
2435pub unsafe extern "C" fn libinput_log_get_priority(ctx: *const LibinputContext) -> u32 {
2436    if ctx.is_null() {
2437        return 30;
2438    }
2439    (*ctx).log_priority
2440}
2441
2442#[no_mangle]
2443pub unsafe extern "C" fn libinput_log_set_handler(
2444    ctx: *mut LibinputContext,
2445    handler: Option<
2446        unsafe extern "C" fn(
2447            ctx: *mut LibinputContext,
2448            priority: u32,
2449            format: *const libc::c_char,
2450            args: *mut libc::c_void,
2451        ),
2452    >,
2453) {
2454    if ctx.is_null() {
2455        return;
2456    }
2457    (*ctx).log_handler = handler;
2458}
2459
2460// ---------------------------------------------------------------------------
2461// User data
2462// ---------------------------------------------------------------------------
2463
2464#[no_mangle]
2465pub unsafe extern "C" fn libinput_set_user_data(
2466    ctx: *mut LibinputContext,
2467    data: *mut libc::c_void,
2468) {
2469    if ctx.is_null() {
2470        return;
2471    }
2472    (*ctx).user_data = data;
2473}
2474
2475#[no_mangle]
2476pub unsafe extern "C" fn libinput_get_user_data(ctx: *const LibinputContext) -> *mut libc::c_void {
2477    if ctx.is_null() {
2478        return std::ptr::null_mut();
2479    }
2480    (*ctx).user_data
2481}
2482
2483#[no_mangle]
2484pub unsafe extern "C" fn libinput_device_set_user_data(
2485    dev: *mut LibinputDevice,
2486    data: *mut libc::c_void,
2487) {
2488    if dev.is_null() {
2489        return;
2490    }
2491    (*dev).user_data = data;
2492    (*dev).abi.user_data = data;
2493}
2494
2495#[no_mangle]
2496pub unsafe extern "C" fn libinput_device_get_user_data(
2497    dev: *const LibinputDevice,
2498) -> *mut libc::c_void {
2499    if dev.is_null() {
2500        return std::ptr::null_mut();
2501    }
2502    (*dev).user_data
2503}
2504
2505// ---------------------------------------------------------------------------
2506// ABI compatibility surface for compositors (KWin/GNOME)
2507// ---------------------------------------------------------------------------
2508
2509#[no_mangle]
2510pub unsafe extern "C" fn libinput_device_config_area_has_rectangle(
2511    dev: *const LibinputDevice,
2512) -> libc::c_int {
2513    if dev.is_null() {
2514        return 0;
2515    }
2516    (*dev).area_available as libc::c_int
2517}
2518
2519#[no_mangle]
2520pub unsafe extern "C" fn libinput_device_config_area_set_rectangle(
2521    dev: *mut LibinputDevice,
2522    rectangle: *const LibinputConfigAreaRectangle,
2523) -> u32 {
2524    if dev.is_null() || !(*dev).area_available {
2525        return 1;
2526    }
2527    if rectangle.is_null() {
2528        return 2;
2529    }
2530    let rectangle = &*rectangle;
2531    if rectangle.x1 >= rectangle.x2
2532        || rectangle.y1 >= rectangle.y2
2533        || rectangle.x1 < 0.0
2534        || rectangle.x2 > 1.0
2535        || rectangle.y1 < 0.0
2536        || rectangle.y2 > 1.0
2537    {
2538        return 2;
2539    }
2540    (*dev).wanted_area = [rectangle.x1, rectangle.y1, rectangle.x2, rectangle.y2];
2541    if !(*dev).tablet_in_proximity {
2542        (*dev).area = (*dev).wanted_area;
2543    }
2544    0
2545}
2546
2547#[no_mangle]
2548pub unsafe extern "C" fn libinput_device_config_area_get_rectangle(
2549    dev: *const LibinputDevice,
2550) -> LibinputConfigAreaRectangle {
2551    let area = if dev.is_null() || !(*dev).area_available {
2552        [0.0, 0.0, 1.0, 1.0]
2553    } else {
2554        (*dev).area
2555    };
2556    LibinputConfigAreaRectangle {
2557        x1: area[0],
2558        y1: area[1],
2559        x2: area[2],
2560        y2: area[3],
2561    }
2562}
2563
2564#[no_mangle]
2565pub unsafe extern "C" fn libinput_device_config_area_get_default_rectangle(
2566    _dev: *const LibinputDevice,
2567) -> LibinputConfigAreaRectangle {
2568    LibinputConfigAreaRectangle {
2569        x1: 0.0,
2570        y1: 0.0,
2571        x2: 1.0,
2572        y2: 1.0,
2573    }
2574}
2575
2576#[no_mangle]
2577pub unsafe extern "C" fn libinput_device_config_rotation_is_available(
2578    dev: *const LibinputDevice,
2579) -> libc::c_int {
2580    if dev.is_null() {
2581        return 0;
2582    }
2583    (*dev).rotation_available as libc::c_int
2584}
2585
2586#[no_mangle]
2587pub unsafe extern "C" fn libinput_device_config_rotation_set_angle(
2588    dev: *mut LibinputDevice,
2589    degrees_cw: u32,
2590) -> u32 {
2591    if dev.is_null() {
2592        return 1;
2593    }
2594    if degrees_cw >= 360 {
2595        return 2;
2596    }
2597    if !(*dev).rotation_available && degrees_cw != 0 {
2598        return 1;
2599    }
2600    (*dev).rotation_angle = degrees_cw;
2601    0
2602}
2603
2604#[no_mangle]
2605pub unsafe extern "C" fn libinput_device_config_rotation_get_angle(
2606    dev: *const LibinputDevice,
2607) -> u32 {
2608    if dev.is_null() {
2609        0
2610    } else {
2611        (*dev).rotation_angle
2612    }
2613}
2614
2615#[no_mangle]
2616pub unsafe extern "C" fn libinput_device_config_rotation_get_default_angle(
2617    _dev: *const LibinputDevice,
2618) -> u32 {
2619    0
2620}
2621
2622#[no_mangle]
2623pub unsafe extern "C" fn libinput_device_config_scroll_get_default_button(
2624    dev: *const LibinputDevice,
2625) -> u32 {
2626    if dev.is_null() || !(*dev).supports_button_scroll {
2627        0
2628    } else {
2629        (*dev).scroll_default_button
2630    }
2631}
2632
2633#[no_mangle]
2634pub unsafe extern "C" fn libinput_device_config_send_events_get_modes(
2635    dev: *const LibinputDevice,
2636) -> u32 {
2637    if dev.is_null() {
2638        return 0;
2639    }
2640    (*dev).send_events_modes
2641}
2642
2643#[no_mangle]
2644pub unsafe extern "C" fn libinput_device_config_send_events_set_mode(
2645    dev: *mut LibinputDevice,
2646    mode: u32,
2647) -> u32 {
2648    if dev.is_null() {
2649        return 1;
2650    }
2651    let supported = (*dev).send_events_modes;
2652    if mode & !supported != 0 {
2653        return 1;
2654    }
2655    let previous = (*dev).send_events_mode;
2656    let next = if mode & 1 != 0 { 1 } else { mode };
2657    (*dev).send_events_mode = next;
2658    if previous != next && matches!(next, 1 | 2) {
2659        let ctx = (*dev).context;
2660        if !ctx.is_null() {
2661            let mut events = std::collections::VecDeque::new();
2662            if let Ok(mut backend) = (*ctx).backend.lock() {
2663                if next == 1 || backend.has_external_mouse() {
2664                    backend.release_active_inputs(ctx, dev, &mut events);
2665                }
2666            }
2667            enqueue_events(ctx, events);
2668        }
2669    }
2670    0
2671}
2672
2673#[no_mangle]
2674pub unsafe extern "C" fn libinput_device_config_send_events_get_mode(
2675    dev: *const LibinputDevice,
2676) -> u32 {
2677    if dev.is_null() {
2678        return 0;
2679    }
2680    (*dev).send_events_mode
2681}
2682
2683#[no_mangle]
2684pub unsafe extern "C" fn libinput_device_config_send_events_get_default_mode(
2685    _dev: *const LibinputDevice,
2686) -> u32 {
2687    0
2688}
2689
2690#[no_mangle]
2691pub unsafe extern "C" fn libinput_device_config_tap_get_default_drag_lock_enabled(
2692    _dev: *const LibinputDevice,
2693) -> u32 {
2694    0
2695}
2696
2697#[no_mangle]
2698pub unsafe extern "C" fn libinput_device_get_device_group(
2699    dev: *const LibinputDevice,
2700) -> *mut libc::c_void {
2701    if dev.is_null() {
2702        return std::ptr::null_mut();
2703    }
2704    (*dev).group.cast()
2705}
2706
2707#[no_mangle]
2708pub unsafe extern "C" fn libinput_device_group_ref(group: *mut libc::c_void) -> *mut libc::c_void {
2709    if group.is_null() {
2710        return std::ptr::null_mut();
2711    }
2712    let group = group.cast::<LibinputDeviceGroup>();
2713    (*group)
2714        .refcount
2715        .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2716    group.cast()
2717}
2718
2719#[no_mangle]
2720pub unsafe extern "C" fn libinput_device_group_unref(
2721    group: *mut libc::c_void,
2722) -> *mut libc::c_void {
2723    if group.is_null() {
2724        return std::ptr::null_mut();
2725    }
2726    let group = group.cast::<LibinputDeviceGroup>();
2727    if (*group)
2728        .refcount
2729        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
2730        == 1
2731    {
2732        drop(Box::from_raw(group));
2733        std::ptr::null_mut()
2734    } else {
2735        group.cast()
2736    }
2737}
2738
2739#[no_mangle]
2740pub unsafe extern "C" fn libinput_device_group_set_user_data(
2741    group: *mut libc::c_void,
2742    data: *mut libc::c_void,
2743) {
2744    if !group.is_null() {
2745        (*group.cast::<LibinputDeviceGroup>()).user_data = data;
2746    }
2747}
2748
2749#[no_mangle]
2750pub unsafe extern "C" fn libinput_device_group_get_user_data(
2751    group: *const libc::c_void,
2752) -> *mut libc::c_void {
2753    if group.is_null() {
2754        return std::ptr::null_mut();
2755    }
2756    (*group.cast::<LibinputDeviceGroup>()).user_data
2757}
2758
2759#[no_mangle]
2760pub unsafe extern "C" fn libinput_device_get_id_bustype(dev: *const LibinputDevice) -> u32 {
2761    if dev.is_null() {
2762        return 0;
2763    }
2764    (*dev).bus_type
2765}
2766
2767#[no_mangle]
2768pub unsafe extern "C" fn libinput_device_get_size(
2769    dev: *const LibinputDevice,
2770    width: *mut f64,
2771    height: *mut f64,
2772) -> libc::c_int {
2773    if dev.is_null() || width.is_null() || height.is_null() {
2774        return 0;
2775    }
2776    match ((*dev).width_mm, (*dev).height_mm) {
2777        (Some(w), Some(h)) => {
2778            *width = w;
2779            *height = h;
2780            0
2781        }
2782        _ => -1,
2783    }
2784}
2785
2786#[no_mangle]
2787pub unsafe extern "C" fn libinput_device_get_udev_device(
2788    dev: *const LibinputDevice,
2789) -> *mut libc::c_void {
2790    if dev.is_null() || (*dev).udev_device.is_null() {
2791        return std::ptr::null_mut();
2792    }
2793    udev::udev_device_ref((*dev).udev_device)
2794}
2795
2796#[no_mangle]
2797pub unsafe extern "C" fn libinput_device_keyboard_has_key(
2798    dev: *const LibinputDevice,
2799    key: u32,
2800) -> libc::c_int {
2801    if dev.is_null() || !(*dev).has_keyboard {
2802        return -1;
2803    }
2804    if key > u16::MAX as u32 {
2805        return 0;
2806    }
2807    (*dev).event_codes.contains(&(key as u16)) as libc::c_int
2808}
2809
2810#[no_mangle]
2811pub unsafe extern "C" fn libinput_device_led_update(dev: *mut LibinputDevice, leds: u32) {
2812    if dev.is_null() || (*dev).context.is_null() {
2813        return;
2814    }
2815    let ctx = (*dev).context;
2816    if let Ok(mut backend) = (*ctx).backend.lock() {
2817        backend.update_leds(dev, leds);
2818    }
2819}
2820
2821#[no_mangle]
2822pub unsafe extern "C" fn libinput_device_pointer_has_button(
2823    dev: *const LibinputDevice,
2824    button: u32,
2825) -> libc::c_int {
2826    if dev.is_null() || button > u16::MAX as u32 {
2827        return 0;
2828    }
2829    if !(*dev).has_pointer {
2830        return -1;
2831    }
2832    (*dev).event_codes.contains(&(button as u16)) as libc::c_int
2833}
2834
2835#[no_mangle]
2836pub unsafe extern "C" fn libinput_device_switch_has_switch(
2837    dev: *const LibinputDevice,
2838    sw: u32,
2839) -> libc::c_int {
2840    if dev.is_null() {
2841        return 0;
2842    }
2843    if !(*dev).has_switch {
2844        return 0;
2845    }
2846    let kernel_code = match sw {
2847        1 => 0,
2848        2 => 1,
2849        3 => 10,
2850        _ => return 0,
2851    };
2852    (*dev).switch_codes.contains(&kernel_code) as libc::c_int
2853}
2854
2855#[no_mangle]
2856pub unsafe extern "C" fn libinput_device_tablet_pad_get_mode_group(
2857    dev: *const LibinputDevice,
2858    index: u32,
2859) -> *mut libc::c_void {
2860    if dev.is_null() || !(*dev).has_tablet_pad {
2861        return std::ptr::null_mut();
2862    }
2863    (*dev)
2864        .tablet_pad_mode_groups
2865        .iter()
2866        .copied()
2867        .find(|group| !group.is_null() && (**group).index == index)
2868        .unwrap_or(std::ptr::null_mut())
2869        .cast()
2870}
2871
2872#[no_mangle]
2873pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_buttons(
2874    dev: *const LibinputDevice,
2875) -> u32 {
2876    if dev.is_null() || !(*dev).has_tablet_pad {
2877        return u32::MAX;
2878    }
2879    (*dev).tablet_pad_button_codes.len() as u32
2880}
2881
2882#[no_mangle]
2883pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_dials(
2884    dev: *const LibinputDevice,
2885) -> u32 {
2886    if dev.is_null() || !(*dev).has_tablet_pad {
2887        return u32::MAX;
2888    }
2889    (*dev).tablet_pad_num_dials
2890}
2891
2892#[no_mangle]
2893pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_mode_groups(
2894    dev: *const LibinputDevice,
2895) -> u32 {
2896    if dev.is_null() || !(*dev).has_tablet_pad {
2897        return u32::MAX;
2898    }
2899    (*dev).tablet_pad_mode_groups.len() as u32
2900}
2901
2902#[no_mangle]
2903pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_rings(
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_rings
2910}
2911
2912#[no_mangle]
2913pub unsafe extern "C" fn libinput_device_tablet_pad_get_num_strips(
2914    dev: *const LibinputDevice,
2915) -> u32 {
2916    if dev.is_null() || !(*dev).has_tablet_pad {
2917        return u32::MAX;
2918    }
2919    (*dev).tablet_pad_num_strips
2920}
2921
2922#[no_mangle]
2923pub unsafe extern "C" fn libinput_device_tablet_pad_has_key(
2924    dev: *const LibinputDevice,
2925    code: u32,
2926) -> libc::c_int {
2927    if dev.is_null() || !(*dev).has_tablet_pad {
2928        return -1;
2929    }
2930    (code <= u16::MAX as u32 && (*dev).event_codes.contains(&(code as u16))) as libc::c_int
2931}
2932
2933#[no_mangle]
2934pub unsafe extern "C" fn libinput_event_get_tablet_pad_event(
2935    event: *mut LibinputEvent,
2936) -> *mut LibinputEvent {
2937    if event.is_null() {
2938        return std::ptr::null_mut();
2939    }
2940    match (*event).event_type {
2941        LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_BUTTON
2942        | LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_RING
2943        | LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_STRIP
2944        | LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_KEY
2945        | LibinputEventType::LIBINPUT_EVENT_TABLET_PAD_DIAL => event,
2946        _ => std::ptr::null_mut(),
2947    }
2948}
2949
2950#[no_mangle]
2951pub unsafe extern "C" fn libinput_event_tablet_pad_get_base_event(
2952    event: *mut LibinputEvent,
2953) -> *mut LibinputEvent {
2954    event
2955}
2956
2957#[no_mangle]
2958pub unsafe extern "C" fn libinput_event_get_tablet_tool_event(
2959    event: *mut LibinputEvent,
2960) -> *mut LibinputEvent {
2961    if event.is_null() {
2962        return std::ptr::null_mut();
2963    }
2964    match (*event).event_type {
2965        LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS
2966        | LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY
2967        | LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_TIP
2968        | LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_BUTTON => event,
2969        _ => std::ptr::null_mut(),
2970    }
2971}
2972
2973#[no_mangle]
2974pub unsafe extern "C" fn libinput_event_tablet_tool_get_base_event(
2975    event: *mut LibinputEvent,
2976) -> *mut LibinputEvent {
2977    event
2978}
2979
2980#[no_mangle]
2981pub unsafe extern "C" fn libinput_event_pointer_get_absolute_x_transformed(
2982    event: *const LibinputEvent,
2983    width: u32,
2984) -> f64 {
2985    if event.is_null() {
2986        return 0.0;
2987    }
2988    if let EventPayload::PointerMotionAbsolute(e) = &(*event).payload {
2989        let range = e.x_max - e.x_min;
2990        if range > 0.0 {
2991            (e.abs_x - e.x_min) * f64::from(width) / range
2992        } else {
2993            0.0
2994        }
2995    } else {
2996        0.0
2997    }
2998}
2999
3000#[no_mangle]
3001pub unsafe extern "C" fn libinput_event_pointer_get_absolute_y_transformed(
3002    event: *const LibinputEvent,
3003    height: u32,
3004) -> f64 {
3005    if event.is_null() {
3006        return 0.0;
3007    }
3008    if let EventPayload::PointerMotionAbsolute(e) = &(*event).payload {
3009        let range = e.y_max - e.y_min;
3010        if range > 0.0 {
3011            (e.abs_y - e.y_min) * f64::from(height) / range
3012        } else {
3013            0.0
3014        }
3015    } else {
3016        0.0
3017    }
3018}
3019
3020#[no_mangle]
3021pub unsafe extern "C" fn libinput_event_pointer_get_scroll_value(
3022    event: *const LibinputEvent,
3023    axis: u32,
3024) -> f64 {
3025    libinput_event_pointer_get_axis_value(event, axis)
3026}
3027
3028#[no_mangle]
3029pub unsafe extern "C" fn libinput_event_pointer_get_scroll_value_v120(
3030    event: *const LibinputEvent,
3031    axis: u32,
3032) -> f64 {
3033    if event.is_null() {
3034        return 0.0;
3035    }
3036    if let EventPayload::PointerAxis(e) = &(*event).payload {
3037        return e.value_v120(axis);
3038    }
3039    0.0
3040}
3041
3042#[no_mangle]
3043pub unsafe extern "C" fn libinput_event_switch_get_time_usec(event: *const LibinputEvent) -> u64 {
3044    if event.is_null() {
3045        return 0;
3046    }
3047    if let EventPayload::SwitchToggle(e) = &(*event).payload {
3048        e.time_usec
3049    } else {
3050        0
3051    }
3052}
3053
3054#[no_mangle]
3055pub unsafe extern "C" fn libinput_event_switch_get_time(event: *const LibinputEvent) -> u32 {
3056    (libinput_event_switch_get_time_usec(event) / 1000) as u32
3057}
3058
3059#[no_mangle]
3060pub unsafe extern "C" fn libinput_event_tablet_pad_get_button_number(
3061    event: *const LibinputEvent,
3062) -> u32 {
3063    if event.is_null() {
3064        return 0;
3065    }
3066    match &(*event).payload {
3067        EventPayload::TabletPad(pad) => pad.button,
3068        _ => 0,
3069    }
3070}
3071
3072#[no_mangle]
3073pub unsafe extern "C" fn libinput_event_tablet_pad_get_button_state(
3074    event: *const LibinputEvent,
3075) -> u32 {
3076    if event.is_null() {
3077        return 0;
3078    }
3079    match &(*event).payload {
3080        EventPayload::TabletPad(pad) => pad.button_state,
3081        _ => 0,
3082    }
3083}
3084
3085#[no_mangle]
3086pub unsafe extern "C" fn libinput_event_tablet_pad_get_key(event: *const LibinputEvent) -> u32 {
3087    if event.is_null() {
3088        return 0;
3089    }
3090    match &(*event).payload {
3091        EventPayload::TabletPad(pad) => pad.key,
3092        _ => 0,
3093    }
3094}
3095
3096#[no_mangle]
3097pub unsafe extern "C" fn libinput_event_tablet_pad_get_key_state(
3098    event: *const LibinputEvent,
3099) -> u32 {
3100    if event.is_null() {
3101        return 0;
3102    }
3103    match &(*event).payload {
3104        EventPayload::TabletPad(pad) => pad.key_state,
3105        _ => 0,
3106    }
3107}
3108
3109#[no_mangle]
3110pub unsafe extern "C" fn libinput_event_tablet_pad_get_dial_delta_v120(
3111    event: *const LibinputEvent,
3112) -> f64 {
3113    if event.is_null() {
3114        return 0.0;
3115    }
3116    match &(*event).payload {
3117        EventPayload::TabletPad(pad) => pad.dial_delta_v120,
3118        _ => 0.0,
3119    }
3120}
3121
3122#[no_mangle]
3123pub unsafe extern "C" fn libinput_event_tablet_pad_get_dial_number(
3124    event: *const LibinputEvent,
3125) -> u32 {
3126    if event.is_null() {
3127        return 0;
3128    }
3129    match &(*event).payload {
3130        EventPayload::TabletPad(pad) => pad.dial_number,
3131        _ => 0,
3132    }
3133}
3134
3135#[no_mangle]
3136pub unsafe extern "C" fn libinput_event_tablet_pad_get_mode(event: *const LibinputEvent) -> u32 {
3137    if event.is_null() {
3138        return 0;
3139    }
3140    match &(*event).payload {
3141        EventPayload::TabletPad(pad) => pad.mode,
3142        _ => 0,
3143    }
3144}
3145
3146#[no_mangle]
3147pub unsafe extern "C" fn libinput_event_tablet_pad_get_mode_group(
3148    event: *const LibinputEvent,
3149) -> *mut libc::c_void {
3150    if event.is_null() {
3151        return std::ptr::null_mut();
3152    }
3153    match &(*event).payload {
3154        EventPayload::TabletPad(pad) => pad.mode_group.cast(),
3155        _ => std::ptr::null_mut(),
3156    }
3157}
3158
3159#[no_mangle]
3160pub unsafe extern "C" fn libinput_event_tablet_pad_get_ring_number(
3161    event: *const LibinputEvent,
3162) -> u32 {
3163    if event.is_null() {
3164        return 0;
3165    }
3166    match &(*event).payload {
3167        EventPayload::TabletPad(pad) => pad.ring_number,
3168        _ => 0,
3169    }
3170}
3171
3172#[no_mangle]
3173pub unsafe extern "C" fn libinput_event_tablet_pad_get_ring_position(
3174    event: *const LibinputEvent,
3175) -> f64 {
3176    if event.is_null() {
3177        return 0.0;
3178    }
3179    match &(*event).payload {
3180        EventPayload::TabletPad(pad) => pad.ring_position,
3181        _ => 0.0,
3182    }
3183}
3184
3185#[no_mangle]
3186pub unsafe extern "C" fn libinput_event_tablet_pad_get_ring_source(
3187    event: *const LibinputEvent,
3188) -> u32 {
3189    if event.is_null() {
3190        return 0;
3191    }
3192    match &(*event).payload {
3193        EventPayload::TabletPad(pad) => pad.ring_source,
3194        _ => 0,
3195    }
3196}
3197
3198#[no_mangle]
3199pub unsafe extern "C" fn libinput_event_tablet_pad_get_strip_number(
3200    event: *const LibinputEvent,
3201) -> u32 {
3202    if event.is_null() {
3203        return 0;
3204    }
3205    match &(*event).payload {
3206        EventPayload::TabletPad(pad) => pad.strip_number,
3207        _ => 0,
3208    }
3209}
3210
3211#[no_mangle]
3212pub unsafe extern "C" fn libinput_event_tablet_pad_get_strip_position(
3213    event: *const LibinputEvent,
3214) -> f64 {
3215    if event.is_null() {
3216        return 0.0;
3217    }
3218    match &(*event).payload {
3219        EventPayload::TabletPad(pad) => pad.strip_position,
3220        _ => 0.0,
3221    }
3222}
3223
3224#[no_mangle]
3225pub unsafe extern "C" fn libinput_event_tablet_pad_get_strip_source(
3226    event: *const LibinputEvent,
3227) -> u32 {
3228    if event.is_null() {
3229        return 0;
3230    }
3231    match &(*event).payload {
3232        EventPayload::TabletPad(pad) => pad.strip_source,
3233        _ => 0,
3234    }
3235}
3236
3237#[no_mangle]
3238pub unsafe extern "C" fn libinput_event_tablet_pad_get_time_usec(
3239    event: *const LibinputEvent,
3240) -> u64 {
3241    if event.is_null() {
3242        return 0;
3243    }
3244    match &(*event).payload {
3245        EventPayload::TabletPad(pad) => pad.time_usec,
3246        _ => 0,
3247    }
3248}
3249
3250#[no_mangle]
3251pub unsafe extern "C" fn libinput_event_tablet_pad_get_time(event: *const LibinputEvent) -> u32 {
3252    (libinput_event_tablet_pad_get_time_usec(event) / 1000) as u32
3253}
3254
3255#[no_mangle]
3256pub unsafe extern "C" fn libinput_event_tablet_tool_get_button(event: *const LibinputEvent) -> u32 {
3257    if event.is_null() {
3258        return 0;
3259    }
3260    match &(*event).payload {
3261        EventPayload::TabletTool(tablet) => tablet.button,
3262        _ => 0,
3263    }
3264}
3265
3266#[no_mangle]
3267pub unsafe extern "C" fn libinput_event_tablet_tool_get_button_state(
3268    event: *const LibinputEvent,
3269) -> u32 {
3270    if event.is_null() {
3271        return 0;
3272    }
3273    match &(*event).payload {
3274        EventPayload::TabletTool(tablet) => tablet.button_state,
3275        _ => 0,
3276    }
3277}
3278
3279#[no_mangle]
3280pub unsafe extern "C" fn libinput_event_tablet_tool_get_seat_button_count(
3281    event: *const LibinputEvent,
3282) -> u32 {
3283    if event.is_null() {
3284        return 0;
3285    }
3286    match &(*event).payload {
3287        EventPayload::TabletTool(tablet) => tablet.seat_button_count,
3288        _ => 0,
3289    }
3290}
3291
3292#[no_mangle]
3293pub unsafe extern "C" fn libinput_event_tablet_tool_get_distance(
3294    event: *const LibinputEvent,
3295) -> f64 {
3296    if event.is_null() {
3297        return 0.0;
3298    }
3299    match &(*event).payload {
3300        EventPayload::TabletTool(tablet) => tablet.distance,
3301        _ => 0.0,
3302    }
3303}
3304
3305#[no_mangle]
3306pub unsafe extern "C" fn libinput_event_tablet_tool_get_dx(event: *const LibinputEvent) -> f64 {
3307    if event.is_null() || (*event).event_type != LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS
3308    {
3309        return 0.0;
3310    }
3311    match &(*event).payload {
3312        EventPayload::TabletTool(tablet) => tablet.dx,
3313        _ => 0.0,
3314    }
3315}
3316
3317#[no_mangle]
3318pub unsafe extern "C" fn libinput_event_tablet_tool_get_dy(event: *const LibinputEvent) -> f64 {
3319    if event.is_null() || (*event).event_type != LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS
3320    {
3321        return 0.0;
3322    }
3323    match &(*event).payload {
3324        EventPayload::TabletTool(tablet) => tablet.dy,
3325        _ => 0.0,
3326    }
3327}
3328
3329#[no_mangle]
3330pub unsafe extern "C" fn libinput_event_tablet_tool_get_pressure(
3331    event: *const LibinputEvent,
3332) -> f64 {
3333    if event.is_null() {
3334        return 0.0;
3335    }
3336    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3337        let range = tablet.pressure_max - tablet.pressure_min;
3338        if range > 0.0 {
3339            ((tablet.pressure - tablet.pressure_min) / range).clamp(0.0, 1.0)
3340        } else {
3341            0.0
3342        }
3343    } else {
3344        0.0
3345    }
3346}
3347
3348#[no_mangle]
3349pub unsafe extern "C" fn libinput_event_tablet_tool_get_x(event: *const LibinputEvent) -> f64 {
3350    if event.is_null() {
3351        return 0.0;
3352    }
3353    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3354        if tablet.x_resolution > 0.0 {
3355            (tablet.x - tablet.x_min) / tablet.x_resolution
3356        } else {
3357            tablet.x - tablet.x_min
3358        }
3359    } else {
3360        0.0
3361    }
3362}
3363
3364#[no_mangle]
3365pub unsafe extern "C" fn libinput_event_tablet_tool_get_y(event: *const LibinputEvent) -> f64 {
3366    if event.is_null() {
3367        return 0.0;
3368    }
3369    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3370        if tablet.y_resolution > 0.0 {
3371            (tablet.y - tablet.y_min) / tablet.y_resolution
3372        } else {
3373            tablet.y - tablet.y_min
3374        }
3375    } else {
3376        0.0
3377    }
3378}
3379
3380#[no_mangle]
3381pub unsafe extern "C" fn libinput_event_tablet_tool_get_proximity_state(
3382    event: *const LibinputEvent,
3383) -> u32 {
3384    if event.is_null() {
3385        return 0;
3386    }
3387    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3388        tablet.proximity_state
3389    } else {
3390        0
3391    }
3392}
3393
3394#[no_mangle]
3395pub unsafe extern "C" fn libinput_event_tablet_tool_get_rotation(
3396    event: *const LibinputEvent,
3397) -> f64 {
3398    if event.is_null() {
3399        return 0.0;
3400    }
3401    match &(*event).payload {
3402        EventPayload::TabletTool(tablet) => tablet.rotation,
3403        _ => 0.0,
3404    }
3405}
3406
3407#[no_mangle]
3408pub unsafe extern "C" fn libinput_event_tablet_tool_get_slider_position(
3409    event: *const LibinputEvent,
3410) -> f64 {
3411    if event.is_null() {
3412        return 0.0;
3413    }
3414    match &(*event).payload {
3415        EventPayload::TabletTool(tablet) => tablet.slider,
3416        _ => 0.0,
3417    }
3418}
3419
3420#[no_mangle]
3421pub unsafe extern "C" fn libinput_event_tablet_tool_get_wheel_delta(
3422    event: *const LibinputEvent,
3423) -> f64 {
3424    if event.is_null() {
3425        return 0.0;
3426    }
3427    match &(*event).payload {
3428        EventPayload::TabletTool(tablet) => tablet.wheel_delta,
3429        _ => 0.0,
3430    }
3431}
3432
3433#[no_mangle]
3434pub unsafe extern "C" fn libinput_event_tablet_tool_get_wheel_delta_discrete(
3435    event: *const LibinputEvent,
3436) -> i32 {
3437    if event.is_null() {
3438        return 0;
3439    }
3440    match &(*event).payload {
3441        EventPayload::TabletTool(tablet) => tablet.wheel_discrete,
3442        _ => 0,
3443    }
3444}
3445
3446#[no_mangle]
3447pub unsafe extern "C" fn libinput_event_tablet_tool_get_size_major(
3448    event: *const LibinputEvent,
3449) -> f64 {
3450    if event.is_null() {
3451        return 0.0;
3452    }
3453    match &(*event).payload {
3454        EventPayload::TabletTool(tablet) => tablet.size_major,
3455        _ => 0.0,
3456    }
3457}
3458
3459#[no_mangle]
3460pub unsafe extern "C" fn libinput_event_tablet_tool_get_size_minor(
3461    event: *const LibinputEvent,
3462) -> f64 {
3463    if event.is_null() {
3464        return 0.0;
3465    }
3466    match &(*event).payload {
3467        EventPayload::TabletTool(tablet) => tablet.size_minor,
3468        _ => 0.0,
3469    }
3470}
3471
3472#[no_mangle]
3473pub unsafe extern "C" fn libinput_event_tablet_tool_get_tilt_x(event: *const LibinputEvent) -> f64 {
3474    if event.is_null() {
3475        return 0.0;
3476    }
3477    match &(*event).payload {
3478        EventPayload::TabletTool(tablet) => tablet.tilt_x,
3479        _ => 0.0,
3480    }
3481}
3482
3483#[no_mangle]
3484pub unsafe extern "C" fn libinput_event_tablet_tool_get_tilt_y(event: *const LibinputEvent) -> f64 {
3485    if event.is_null() {
3486        return 0.0;
3487    }
3488    match &(*event).payload {
3489        EventPayload::TabletTool(tablet) => tablet.tilt_y,
3490        _ => 0.0,
3491    }
3492}
3493
3494#[no_mangle]
3495pub unsafe extern "C" fn libinput_event_tablet_tool_get_time_usec(
3496    event: *const LibinputEvent,
3497) -> u64 {
3498    if event.is_null() {
3499        return 0;
3500    }
3501    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3502        tablet.time_usec
3503    } else {
3504        0
3505    }
3506}
3507
3508#[no_mangle]
3509pub unsafe extern "C" fn libinput_event_tablet_tool_get_time(event: *const LibinputEvent) -> u32 {
3510    (libinput_event_tablet_tool_get_time_usec(event) / 1000) as u32
3511}
3512
3513#[no_mangle]
3514pub unsafe extern "C" fn libinput_event_tablet_tool_get_tip_state(
3515    event: *const LibinputEvent,
3516) -> u32 {
3517    if event.is_null() {
3518        return 0;
3519    }
3520    match &(*event).payload {
3521        EventPayload::TabletTool(tablet) => tablet.tip_state,
3522        _ => 0,
3523    }
3524}
3525
3526#[no_mangle]
3527pub unsafe extern "C" fn libinput_event_tablet_tool_get_tool(
3528    event: *const LibinputEvent,
3529) -> *mut libc::c_void {
3530    if event.is_null() {
3531        return std::ptr::null_mut();
3532    }
3533    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3534        tablet.tool.cast()
3535    } else {
3536        std::ptr::null_mut()
3537    }
3538}
3539
3540#[no_mangle]
3541pub unsafe extern "C" fn libinput_event_tablet_tool_get_x_transformed(
3542    event: *const LibinputEvent,
3543    width: u32,
3544) -> f64 {
3545    if event.is_null() {
3546        return 0.0;
3547    }
3548    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3549        let range = tablet.x_max - tablet.x_min + 1.0;
3550        if range > 0.0 {
3551            (tablet.x - tablet.x_min) * f64::from(width) / range
3552        } else {
3553            0.0
3554        }
3555    } else {
3556        0.0
3557    }
3558}
3559
3560#[no_mangle]
3561pub unsafe extern "C" fn libinput_event_tablet_tool_get_y_transformed(
3562    event: *const LibinputEvent,
3563    height: u32,
3564) -> f64 {
3565    if event.is_null() {
3566        return 0.0;
3567    }
3568    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3569        let range = tablet.y_max - tablet.y_min + 1.0;
3570        if range > 0.0 {
3571            (tablet.y - tablet.y_min) * f64::from(height) / range
3572        } else {
3573            0.0
3574        }
3575    } else {
3576        0.0
3577    }
3578}
3579
3580#[no_mangle]
3581pub unsafe extern "C" fn libinput_event_tablet_tool_x_has_changed(
3582    event: *const LibinputEvent,
3583) -> libc::c_int {
3584    if event.is_null() {
3585        return 0;
3586    }
3587    match &(*event).payload {
3588        EventPayload::TabletTool(tablet) => tablet.x_changed as libc::c_int,
3589        _ => 0,
3590    }
3591}
3592
3593#[no_mangle]
3594pub unsafe extern "C" fn libinput_event_tablet_tool_y_has_changed(
3595    event: *const LibinputEvent,
3596) -> libc::c_int {
3597    if event.is_null() {
3598        return 0;
3599    }
3600    match &(*event).payload {
3601        EventPayload::TabletTool(tablet) => tablet.y_changed as libc::c_int,
3602        _ => 0,
3603    }
3604}
3605
3606#[no_mangle]
3607pub unsafe extern "C" fn libinput_event_tablet_tool_pressure_has_changed(
3608    event: *const LibinputEvent,
3609) -> libc::c_int {
3610    if event.is_null() {
3611        return 0;
3612    }
3613    match &(*event).payload {
3614        EventPayload::TabletTool(tablet) => tablet.pressure_changed as libc::c_int,
3615        _ => 0,
3616    }
3617}
3618
3619#[no_mangle]
3620pub unsafe extern "C" fn libinput_event_tablet_tool_distance_has_changed(
3621    event: *const LibinputEvent,
3622) -> libc::c_int {
3623    if event.is_null() {
3624        return 0;
3625    }
3626    match &(*event).payload {
3627        EventPayload::TabletTool(tablet) => tablet.distance_changed as libc::c_int,
3628        _ => 0,
3629    }
3630}
3631
3632#[no_mangle]
3633pub unsafe extern "C" fn libinput_event_tablet_tool_tilt_x_has_changed(
3634    event: *const LibinputEvent,
3635) -> libc::c_int {
3636    if event.is_null() {
3637        return 0;
3638    }
3639    match &(*event).payload {
3640        EventPayload::TabletTool(tablet) => tablet.tilt_x_changed as libc::c_int,
3641        _ => 0,
3642    }
3643}
3644
3645#[no_mangle]
3646pub unsafe extern "C" fn libinput_event_tablet_tool_tilt_y_has_changed(
3647    event: *const LibinputEvent,
3648) -> libc::c_int {
3649    if event.is_null() {
3650        return 0;
3651    }
3652    match &(*event).payload {
3653        EventPayload::TabletTool(tablet) => tablet.tilt_y_changed as libc::c_int,
3654        _ => 0,
3655    }
3656}
3657
3658#[no_mangle]
3659pub unsafe extern "C" fn libinput_event_tablet_tool_rotation_has_changed(
3660    event: *const LibinputEvent,
3661) -> libc::c_int {
3662    if event.is_null() {
3663        return 0;
3664    }
3665    match &(*event).payload {
3666        EventPayload::TabletTool(tablet) => tablet.rotation_changed as libc::c_int,
3667        _ => 0,
3668    }
3669}
3670
3671#[no_mangle]
3672pub unsafe extern "C" fn libinput_event_tablet_tool_slider_has_changed(
3673    event: *const LibinputEvent,
3674) -> libc::c_int {
3675    if event.is_null() {
3676        return 0;
3677    }
3678    match &(*event).payload {
3679        EventPayload::TabletTool(tablet) => tablet.slider_changed as libc::c_int,
3680        _ => 0,
3681    }
3682}
3683
3684#[no_mangle]
3685pub unsafe extern "C" fn libinput_event_tablet_tool_wheel_has_changed(
3686    event: *const LibinputEvent,
3687) -> libc::c_int {
3688    if event.is_null() {
3689        return 0;
3690    }
3691    match &(*event).payload {
3692        EventPayload::TabletTool(tablet) => tablet.wheel_changed as libc::c_int,
3693        _ => 0,
3694    }
3695}
3696
3697#[no_mangle]
3698pub unsafe extern "C" fn libinput_event_tablet_tool_size_major_has_changed(
3699    event: *const LibinputEvent,
3700) -> libc::c_int {
3701    if event.is_null() {
3702        return 0;
3703    }
3704    match &(*event).payload {
3705        EventPayload::TabletTool(tablet) => tablet.size_major_changed as libc::c_int,
3706        _ => 0,
3707    }
3708}
3709
3710#[no_mangle]
3711pub unsafe extern "C" fn libinput_event_tablet_tool_size_minor_has_changed(
3712    event: *const LibinputEvent,
3713) -> libc::c_int {
3714    if event.is_null() {
3715        return 0;
3716    }
3717    match &(*event).payload {
3718        EventPayload::TabletTool(tablet) => tablet.size_minor_changed as libc::c_int,
3719        _ => 0,
3720    }
3721}
3722
3723#[no_mangle]
3724pub unsafe extern "C" fn libinput_plugin_system_append_default_paths(ctx: *mut LibinputContext) {
3725    if ctx.is_null() || (*ctx).plugins_loaded {
3726        return;
3727    }
3728    for path in ["/etc/libinput/plugins", "/usr/lib64/libinput/plugins"] {
3729        let path = std::ffi::CString::new(path).expect("static plugin path");
3730        if !(*ctx)
3731            .plugin_paths
3732            .iter()
3733            .any(|candidate| candidate.as_bytes() == path.as_bytes())
3734        {
3735            (*ctx).plugin_paths.push(path);
3736        }
3737    }
3738}
3739
3740#[no_mangle]
3741pub unsafe extern "C" fn libinput_plugin_system_append_path(
3742    ctx: *mut LibinputContext,
3743    path: *const libc::c_char,
3744) {
3745    if ctx.is_null() || path.is_null() || (*ctx).plugins_loaded {
3746        return;
3747    }
3748    let path = CStr::from_ptr(path);
3749    if path.to_bytes().is_empty()
3750        || (*ctx)
3751            .plugin_paths
3752            .iter()
3753            .any(|candidate| candidate.as_bytes() == path.to_bytes())
3754    {
3755        return;
3756    }
3757    if let Ok(path) = std::ffi::CString::new(path.to_bytes()) {
3758        (*ctx).plugin_paths.push(path);
3759    }
3760}
3761
3762#[no_mangle]
3763pub unsafe extern "C" fn libinput_plugin_system_load_plugins(
3764    ctx: *mut LibinputContext,
3765    flags: libc::c_uint,
3766) -> libc::c_int {
3767    if ctx.is_null() || flags != 0 {
3768        return -libc::EINVAL;
3769    }
3770    if (*ctx).plugins_loaded {
3771        return 0;
3772    }
3773    // Built-in stages are always active, but this build has no Lua plugin
3774    // loader. Match an upstream build configured without plugin support:
3775    // freeze the plugin system and report ENOSYS instead of claiming success.
3776    (*ctx).plugins_loaded = true;
3777    -libc::ENOSYS
3778}
3779
3780#[no_mangle]
3781pub unsafe extern "C" fn libinput_tablet_pad_mode_group_button_is_toggle(
3782    group: *const libc::c_void,
3783    button: u32,
3784) -> libc::c_int {
3785    if group.is_null() || button >= u32::BITS {
3786        return 0;
3787    }
3788    ((*group.cast::<LibinputTabletPadModeGroup>()).toggle_button_mask & (1_u32 << button) != 0)
3789        as libc::c_int
3790}
3791
3792#[no_mangle]
3793pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_index(
3794    group: *const libc::c_void,
3795) -> u32 {
3796    if group.is_null() {
3797        return 0;
3798    }
3799    (*group.cast::<LibinputTabletPadModeGroup>()).index
3800}
3801
3802#[no_mangle]
3803pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_mode(
3804    group: *const libc::c_void,
3805) -> u32 {
3806    if group.is_null() {
3807        return 0;
3808    }
3809    (*group.cast::<LibinputTabletPadModeGroup>()).current_mode
3810}
3811
3812#[no_mangle]
3813pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_num_modes(
3814    group: *const libc::c_void,
3815) -> u32 {
3816    if group.is_null() {
3817        return 0;
3818    }
3819    (*group.cast::<LibinputTabletPadModeGroup>()).num_modes
3820}
3821
3822#[no_mangle]
3823pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_button(
3824    group: *const libc::c_void,
3825    button: u32,
3826) -> libc::c_int {
3827    if group.is_null() {
3828        return 0;
3829    }
3830    if button >= u32::BITS {
3831        return 0;
3832    }
3833    ((*group.cast::<LibinputTabletPadModeGroup>()).button_mask & (1_u32 << button) != 0)
3834        as libc::c_int
3835}
3836
3837#[no_mangle]
3838pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_dial(
3839    group: *const libc::c_void,
3840    dial: u32,
3841) -> libc::c_int {
3842    if group.is_null() {
3843        return 0;
3844    }
3845    if dial >= u32::BITS {
3846        return 0;
3847    }
3848    ((*group.cast::<LibinputTabletPadModeGroup>()).dial_mask & (1_u32 << dial) != 0) as libc::c_int
3849}
3850
3851#[no_mangle]
3852pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_ring(
3853    group: *const libc::c_void,
3854    ring: u32,
3855) -> libc::c_int {
3856    if group.is_null() {
3857        return 0;
3858    }
3859    if ring >= u32::BITS {
3860        return 0;
3861    }
3862    ((*group.cast::<LibinputTabletPadModeGroup>()).ring_mask & (1_u32 << ring) != 0) as libc::c_int
3863}
3864
3865#[no_mangle]
3866pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_strip(
3867    group: *const libc::c_void,
3868    strip: u32,
3869) -> libc::c_int {
3870    if group.is_null() {
3871        return 0;
3872    }
3873    if strip >= u32::BITS {
3874        return 0;
3875    }
3876    ((*group.cast::<LibinputTabletPadModeGroup>()).strip_mask & (1_u32 << strip) != 0)
3877        as libc::c_int
3878}
3879
3880#[no_mangle]
3881pub unsafe extern "C" fn libinput_tablet_pad_mode_group_ref(
3882    group: *mut libc::c_void,
3883) -> *mut libc::c_void {
3884    if !group.is_null() {
3885        (*group.cast::<LibinputTabletPadModeGroup>())
3886            .refcount
3887            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3888    }
3889    group
3890}
3891
3892#[no_mangle]
3893pub unsafe extern "C" fn libinput_tablet_pad_mode_group_unref(
3894    group: *mut libc::c_void,
3895) -> *mut libc::c_void {
3896    if group.is_null() {
3897        return std::ptr::null_mut();
3898    }
3899    let group = group.cast::<LibinputTabletPadModeGroup>();
3900    if (*group)
3901        .refcount
3902        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
3903        == 1
3904    {
3905        drop(Box::from_raw(group));
3906        std::ptr::null_mut()
3907    } else {
3908        group.cast()
3909    }
3910}
3911
3912#[no_mangle]
3913pub unsafe extern "C" fn libinput_tablet_pad_mode_group_set_user_data(
3914    group: *mut libc::c_void,
3915    data: *mut libc::c_void,
3916) {
3917    if !group.is_null() {
3918        (*group.cast::<LibinputTabletPadModeGroup>()).user_data = data;
3919    }
3920}
3921
3922#[no_mangle]
3923pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_user_data(
3924    group: *const libc::c_void,
3925) -> *mut libc::c_void {
3926    if group.is_null() {
3927        return std::ptr::null_mut();
3928    }
3929    (*group.cast::<LibinputTabletPadModeGroup>()).user_data
3930}
3931
3932#[no_mangle]
3933pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_is_available(
3934    tool: *const LibinputTabletTool,
3935) -> libc::c_int {
3936    (!tool.is_null() && (*tool).has_pressure) as libc::c_int
3937}
3938
3939#[no_mangle]
3940pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_set(
3941    tool: *mut LibinputTabletTool,
3942    minimum: f64,
3943    maximum: f64,
3944) -> u32 {
3945    if tool.is_null() || !(*tool).has_pressure {
3946        return 1;
3947    }
3948    if minimum < 0.0 || maximum > 1.0 || minimum >= maximum {
3949        return 2;
3950    }
3951    (*tool).wanted_pressure_range_minimum = minimum;
3952    (*tool).wanted_pressure_range_maximum = maximum;
3953    0
3954}
3955
3956#[no_mangle]
3957pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_minimum(
3958    tool: *const LibinputTabletTool,
3959) -> f64 {
3960    if tool.is_null() {
3961        0.0
3962    } else {
3963        (*tool).wanted_pressure_range_minimum
3964    }
3965}
3966
3967#[no_mangle]
3968pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_maximum(
3969    tool: *const LibinputTabletTool,
3970) -> f64 {
3971    if tool.is_null() {
3972        1.0
3973    } else {
3974        (*tool).wanted_pressure_range_maximum
3975    }
3976}
3977
3978#[no_mangle]
3979pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_default_minimum(
3980    _tool: *const libc::c_void,
3981) -> f64 {
3982    0.0
3983}
3984
3985#[no_mangle]
3986pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_default_maximum(
3987    _tool: *const libc::c_void,
3988) -> f64 {
3989    1.0
3990}
3991
3992#[no_mangle]
3993pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_modes(
3994    tool: *const libc::c_void,
3995) -> u32 {
3996    let tool = tool.cast::<LibinputTabletTool>();
3997    if tool.is_null() {
3998        0
3999    } else {
4000        (*tool).eraser_button_modes
4001    }
4002}
4003
4004#[no_mangle]
4005pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_set_mode(
4006    tool: *mut libc::c_void,
4007    mode: u32,
4008) -> u32 {
4009    let tool = tool.cast::<LibinputTabletTool>();
4010    if tool.is_null() || (mode != 0 && ((*tool).eraser_button_modes & mode) == 0) {
4011        return 1;
4012    }
4013    if !matches!(mode, 0 | 1) {
4014        return 2;
4015    }
4016    (*tool).wanted_eraser_button_mode = mode;
4017    if !(*tool).in_proximity {
4018        (*tool).eraser_button_mode = mode;
4019    }
4020    0
4021}
4022
4023#[no_mangle]
4024pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_mode(
4025    tool: *const libc::c_void,
4026) -> u32 {
4027    let tool = tool.cast::<LibinputTabletTool>();
4028    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4029        0
4030    } else {
4031        (*tool).wanted_eraser_button_mode
4032    }
4033}
4034
4035#[no_mangle]
4036pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_default_mode(
4037    _tool: *const libc::c_void,
4038) -> u32 {
4039    0
4040}
4041
4042#[no_mangle]
4043pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_set_button(
4044    tool: *mut libc::c_void,
4045    button: u32,
4046) -> u32 {
4047    let tool = tool.cast::<LibinputTabletTool>();
4048    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4049        return 1;
4050    }
4051    let is_button = matches!(button, 0x149 | 0x14b | 0x14c)
4052        || (0x100..0x140).contains(&button)
4053        || (0x150..=0x151).contains(&button)
4054        || (0x220..=0x223).contains(&button)
4055        || (0x2c0..=0x2e7).contains(&button);
4056    if !is_button {
4057        return 2;
4058    }
4059    (*tool).wanted_eraser_button = button;
4060    if !(*tool).in_proximity {
4061        (*tool).eraser_button = button;
4062    }
4063    0
4064}
4065
4066#[no_mangle]
4067pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_button(
4068    tool: *const libc::c_void,
4069) -> u32 {
4070    let tool = tool.cast::<LibinputTabletTool>();
4071    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4072        0
4073    } else {
4074        (*tool).wanted_eraser_button
4075    }
4076}
4077
4078#[no_mangle]
4079pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_default_button(
4080    tool: *const libc::c_void,
4081) -> u32 {
4082    let tool = tool.cast::<LibinputTabletTool>();
4083    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4084        0
4085    } else {
4086        (*tool).default_eraser_button
4087    }
4088}
4089
4090#[no_mangle]
4091pub unsafe extern "C" fn libinput_tablet_tool_get_name(
4092    tool: *const libc::c_void,
4093) -> *const libc::c_char {
4094    let tool = tool.cast::<LibinputTabletTool>();
4095    if tool.is_null() {
4096        return std::ptr::null();
4097    }
4098    let tool = tool.cast::<LibinputTabletTool>();
4099    let tool = tool as *mut LibinputTabletTool;
4100    if let Some(name) = (*tool).name.as_ref() {
4101        return name.as_ptr();
4102    }
4103    if let Some(name) = crate::backend::tablet_tool_name_for_id((*tool).tool_id) {
4104        (*tool).name = Some(name);
4105        (*tool)
4106            .name
4107            .as_ref()
4108            .map_or(std::ptr::null(), |name| name.as_ptr())
4109    } else {
4110        std::ptr::null()
4111    }
4112}
4113
4114#[no_mangle]
4115pub unsafe extern "C" fn libinput_tablet_tool_get_serial(tool: *const libc::c_void) -> u64 {
4116    let tool = tool.cast::<LibinputTabletTool>();
4117    if tool.is_null() {
4118        0
4119    } else {
4120        (*tool).serial
4121    }
4122}
4123
4124#[no_mangle]
4125pub unsafe extern "C" fn libinput_tablet_tool_get_tool_id(tool: *const libc::c_void) -> u64 {
4126    let tool = tool.cast::<LibinputTabletTool>();
4127    if tool.is_null() {
4128        0
4129    } else {
4130        (*tool).tool_id
4131    }
4132}
4133
4134#[no_mangle]
4135pub unsafe extern "C" fn libinput_tablet_tool_get_type(tool: *const libc::c_void) -> u32 {
4136    let tool = tool.cast::<LibinputTabletTool>();
4137    if tool.is_null() {
4138        0
4139    } else {
4140        (*tool).tool_type
4141    }
4142}
4143
4144#[no_mangle]
4145pub unsafe extern "C" fn libinput_tablet_tool_has_distance(
4146    tool: *const libc::c_void,
4147) -> libc::c_int {
4148    let tool = tool.cast::<LibinputTabletTool>();
4149    (!tool.is_null() && (*tool).has_distance) as libc::c_int
4150}
4151
4152#[no_mangle]
4153pub unsafe extern "C" fn libinput_tablet_tool_has_button(
4154    tool: *const libc::c_void,
4155    button: u32,
4156) -> libc::c_int {
4157    let tool = tool.cast::<LibinputTabletTool>();
4158    (!tool.is_null() && (*tool).buttons.contains(&button)) as libc::c_int
4159}
4160
4161#[no_mangle]
4162pub unsafe extern "C" fn libinput_tablet_tool_has_size(tool: *const libc::c_void) -> libc::c_int {
4163    let tool = tool.cast::<LibinputTabletTool>();
4164    (!tool.is_null() && (*tool).has_size) as libc::c_int
4165}
4166
4167#[no_mangle]
4168pub unsafe extern "C" fn libinput_tablet_tool_is_unique(tool: *const libc::c_void) -> libc::c_int {
4169    let tool = tool.cast::<LibinputTabletTool>();
4170    (!tool.is_null() && (*tool).serial != 0) as libc::c_int
4171}
4172
4173#[no_mangle]
4174pub unsafe extern "C" fn libinput_tablet_tool_set_user_data(
4175    tool: *mut libc::c_void,
4176    data: *mut libc::c_void,
4177) {
4178    let tool = tool.cast::<LibinputTabletTool>();
4179    if !tool.is_null() {
4180        (*tool).user_data = data;
4181    }
4182}
4183
4184#[no_mangle]
4185pub unsafe extern "C" fn libinput_tablet_tool_get_user_data(
4186    tool: *const libc::c_void,
4187) -> *mut libc::c_void {
4188    let tool = tool.cast::<LibinputTabletTool>();
4189    if tool.is_null() {
4190        std::ptr::null_mut()
4191    } else {
4192        (*tool).user_data
4193    }
4194}
4195
4196#[no_mangle]
4197pub unsafe extern "C" fn libinput_tablet_tool_has_pressure(
4198    tool: *const libc::c_void,
4199) -> libc::c_int {
4200    let tool = tool.cast::<LibinputTabletTool>();
4201    (!tool.is_null() && (*tool).has_pressure) as libc::c_int
4202}
4203
4204#[no_mangle]
4205pub unsafe extern "C" fn libinput_tablet_tool_has_rotation(
4206    tool: *const libc::c_void,
4207) -> libc::c_int {
4208    let tool = tool.cast::<LibinputTabletTool>();
4209    (!tool.is_null() && (*tool).has_rotation) as libc::c_int
4210}
4211
4212#[no_mangle]
4213pub unsafe extern "C" fn libinput_tablet_tool_has_slider(tool: *const libc::c_void) -> libc::c_int {
4214    let tool = tool.cast::<LibinputTabletTool>();
4215    (!tool.is_null() && (*tool).has_slider) as libc::c_int
4216}
4217
4218#[no_mangle]
4219pub unsafe extern "C" fn libinput_tablet_tool_has_tilt(tool: *const libc::c_void) -> libc::c_int {
4220    let tool = tool.cast::<LibinputTabletTool>();
4221    (!tool.is_null() && (*tool).has_tilt) as libc::c_int
4222}
4223
4224#[no_mangle]
4225pub unsafe extern "C" fn libinput_tablet_tool_has_wheel(tool: *const libc::c_void) -> libc::c_int {
4226    let tool = tool.cast::<LibinputTabletTool>();
4227    (!tool.is_null() && (*tool).has_wheel) as libc::c_int
4228}
4229
4230#[no_mangle]
4231pub unsafe extern "C" fn libinput_tablet_tool_ref(tool: *mut libc::c_void) -> *mut libc::c_void {
4232    let tablet_tool = tool.cast::<LibinputTabletTool>();
4233    if !tablet_tool.is_null() {
4234        (*tablet_tool)
4235            .refcount
4236            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4237    }
4238    tool
4239}
4240
4241#[no_mangle]
4242pub unsafe extern "C" fn libinput_tablet_tool_unref(tool: *mut libc::c_void) -> *mut libc::c_void {
4243    let tablet_tool = tool.cast::<LibinputTabletTool>();
4244    if tablet_tool.is_null() {
4245        return std::ptr::null_mut();
4246    }
4247    if (*tablet_tool)
4248        .refcount
4249        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
4250        == 1
4251    {
4252        if !(*tablet_tool).device.is_null() {
4253            libinput_device_unref((*tablet_tool).device);
4254            (*tablet_tool).device = std::ptr::null_mut();
4255        }
4256        drop(Box::from_raw(tablet_tool));
4257        std::ptr::null_mut()
4258    } else {
4259        tool
4260    }
4261}
4262
4263// ---------------------------------------------------------------------------
4264// Suspend / resume
4265// ---------------------------------------------------------------------------
4266
4267#[no_mangle]
4268pub unsafe extern "C" fn libinput_suspend(ctx: *mut LibinputContext) {
4269    if ctx.is_null() {
4270        return;
4271    }
4272    let mut events = std::collections::VecDeque::new();
4273    if let Ok(mut backend) = (*ctx).backend.lock() {
4274        backend.suspend(ctx, &mut events);
4275    }
4276    enqueue_events(ctx, events);
4277}
4278
4279#[no_mangle]
4280pub unsafe extern "C" fn libinput_resume(ctx: *mut LibinputContext) -> libc::c_int {
4281    if ctx.is_null() {
4282        return -1;
4283    }
4284    let mut events = std::collections::VecDeque::new();
4285    let status = if let Ok(mut backend) = (*ctx).backend.lock() {
4286        backend.resume(ctx, &mut events)
4287    } else {
4288        return -1;
4289    };
4290    enqueue_events(ctx, events);
4291    if !(*ctx).event_queue.is_empty() {
4292        (*ctx).signal_fd();
4293    }
4294    status
4295}
4296
4297#[cfg(test)]
4298mod tests {
4299    use super::*;
4300
4301    unsafe extern "C" fn deny_open(
4302        _path: *const libc::c_char,
4303        _flags: libc::c_int,
4304        _user_data: *mut libc::c_void,
4305    ) -> libc::c_int {
4306        -libc::EACCES
4307    }
4308
4309    unsafe extern "C" fn close_fd(_fd: libc::c_int, _user_data: *mut libc::c_void) {}
4310
4311    static INTERFACE: LibinputInterface = LibinputInterface {
4312        open_restricted: Some(deny_open),
4313        close_restricted: Some(close_fd),
4314    };
4315
4316    #[test]
4317    fn udev_context_requires_interface_and_udev() {
4318        unsafe {
4319            let fake_udev = 1usize as *mut libc::c_void;
4320            assert!(libinput_udev_create_context(
4321                std::ptr::null(),
4322                std::ptr::null_mut(),
4323                fake_udev,
4324            )
4325            .is_null());
4326            assert!(libinput_udev_create_context(
4327                &INTERFACE,
4328                std::ptr::null_mut(),
4329                std::ptr::null_mut(),
4330            )
4331            .is_null());
4332        }
4333    }
4334
4335    #[test]
4336    fn seat_assignment_is_udev_only_and_happens_once() {
4337        unsafe {
4338            let fake_udev = 1usize as *mut libc::c_void;
4339            let seat = std::ffi::CString::new("seat0").unwrap();
4340            let udev_ctx =
4341                libinput_udev_create_context(&INTERFACE, std::ptr::null_mut(), fake_udev);
4342            assert!(!udev_ctx.is_null());
4343            assert_eq!(libinput_udev_assign_seat(udev_ctx, seat.as_ptr()), 0);
4344            assert_eq!(libinput_udev_assign_seat(udev_ctx, seat.as_ptr()), -1);
4345            libinput_unref(udev_ctx);
4346
4347            let path_ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4348            assert!(!path_ctx.is_null());
4349            assert_eq!(libinput_udev_assign_seat(path_ctx, seat.as_ptr()), -1);
4350            libinput_unref(path_ctx);
4351        }
4352    }
4353
4354    #[test]
4355    fn overlong_seat_name_does_not_assign_or_queue_devices() {
4356        unsafe {
4357            let fake_udev = 1usize as *mut libc::c_void;
4358            let seat = std::ffi::CString::new("a".repeat(257)).unwrap();
4359            let ctx = libinput_udev_create_context(&INTERFACE, std::ptr::null_mut(), fake_udev);
4360            assert!(!ctx.is_null());
4361
4362            assert_eq!(libinput_udev_assign_seat(ctx, seat.as_ptr()), -1);
4363            assert!(!(*ctx).seat_assigned);
4364            assert!((*ctx).event_queue.is_empty());
4365
4366            libinput_unref(ctx);
4367        }
4368    }
4369
4370    #[test]
4371    fn suspend_and_resume_are_null_safe() {
4372        unsafe {
4373            libinput_suspend(std::ptr::null_mut());
4374            assert_eq!(libinput_resume(std::ptr::null_mut()), -1);
4375        }
4376    }
4377
4378    #[test]
4379    fn shared_device_groups_preserve_identity_and_user_data() {
4380        unsafe {
4381            let first = Box::new(LibinputDevice::new(
4382                "first",
4383                "/dev/input/event0",
4384                std::ptr::null_mut(),
4385                std::ptr::null_mut(),
4386            ));
4387            let group = first.group;
4388            let mut second = Box::new(LibinputDevice::new(
4389                "second",
4390                "/dev/input/event1",
4391                std::ptr::null_mut(),
4392                std::ptr::null_mut(),
4393            ));
4394            second.share_group(group);
4395
4396            assert_eq!(second.group, group);
4397            assert_eq!(second.abi.group, group);
4398            assert_eq!(
4399                (*group).refcount.load(std::sync::atomic::Ordering::Relaxed),
4400                2
4401            );
4402            let marker = 0x5ausize as *mut libc::c_void;
4403            libinput_device_group_set_user_data(group.cast(), marker);
4404            assert_eq!(
4405                libinput_device_group_get_user_data(second.group.cast()),
4406                marker
4407            );
4408
4409            drop(second);
4410            assert_eq!(
4411                (*group).refcount.load(std::sync::atomic::Ordering::Relaxed),
4412                1
4413            );
4414            drop(first);
4415        }
4416    }
4417
4418    #[test]
4419    fn custom_acceleration_validates_and_copies_curves() {
4420        unsafe {
4421            assert!(libinput_config_accel_create(0).is_null());
4422            let config = libinput_config_accel_create(4);
4423            assert!(!config.is_null());
4424
4425            let one_point = [1.0];
4426            assert_eq!(
4427                libinput_config_accel_set_points(config, 1, 1.0, 1, one_point.as_ptr()),
4428                2
4429            );
4430            let points = [0.0, 2.0, 6.0];
4431            assert_eq!(
4432                libinput_config_accel_set_points(config, 1, 1.0, points.len(), points.as_ptr()),
4433                0
4434            );
4435            let scroll_points = [0.0, 3.0, 9.0];
4436            assert_eq!(
4437                libinput_config_accel_set_points(
4438                    config,
4439                    2,
4440                    1.0,
4441                    scroll_points.len(),
4442                    scroll_points.as_ptr(),
4443                ),
4444                0
4445            );
4446
4447            let mut device = Box::new(LibinputDevice::new(
4448                "pointer",
4449                "/dev/input/event0",
4450                std::ptr::null_mut(),
4451                std::ptr::null_mut(),
4452            ));
4453            device.accel_available = true;
4454            assert_eq!(libinput_device_config_accel_apply(&mut *device, config), 0);
4455            libinput_config_accel_destroy(config);
4456
4457            let custom = device.accel_custom.as_mut().expect("custom config copied");
4458            let curve = custom.curve_mut(1).expect("motion curve copied");
4459            assert_eq!(curve.points, points);
4460            assert!((curve.factor(7.0, 0.0, 7_000) - 2.0).abs() < 1e-9);
4461            let scroll_curve = custom.curve_mut(2).expect("scroll curve copied");
4462            assert_eq!(scroll_curve.points, scroll_points);
4463            assert!((scroll_curve.factor(7.0, 0.0, 7_000) - 3.0).abs() < 1e-9);
4464        }
4465    }
4466
4467    #[test]
4468    fn custom_acceleration_uses_fallback_and_extrapolates() {
4469        let mut config = crate::ffi_types::AccelConfig::new(4);
4470        config.fallback = Some(crate::ffi_types::AccelCurve::new(1.0, vec![0.0, 1.0]));
4471        let curve = config.curve_mut(1).expect("fallback curve");
4472        assert!((curve.factor(14.0, 0.0, 7_000) - 1.0).abs() < 1e-9);
4473    }
4474
4475    #[test]
4476    fn plugin_paths_are_ordered_unique_and_frozen_after_load() {
4477        unsafe {
4478            let ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4479            let custom = std::ffi::CString::new("/tmp/plugins").unwrap();
4480            libinput_plugin_system_append_path(ctx, custom.as_ptr());
4481            libinput_plugin_system_append_path(ctx, custom.as_ptr());
4482            libinput_plugin_system_append_default_paths(ctx);
4483            assert_eq!((*ctx).plugin_paths.len(), 3);
4484            assert_eq!((&(*ctx).plugin_paths)[0].as_bytes(), b"/tmp/plugins");
4485            assert_eq!(libinput_plugin_system_load_plugins(ctx, 0), -libc::ENOSYS);
4486            let ignored = std::ffi::CString::new("/ignored").unwrap();
4487            libinput_plugin_system_append_path(ctx, ignored.as_ptr());
4488            assert_eq!((*ctx).plugin_paths.len(), 3);
4489            assert_eq!(libinput_plugin_system_load_plugins(ctx, 0), 0);
4490            libinput_unref(ctx);
4491        }
4492    }
4493
4494    #[test]
4495    fn queued_events_retain_devices_until_event_destroy() {
4496        unsafe {
4497            let ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4498            let device = Box::into_raw(Box::new(LibinputDevice::new(
4499                "keyboard",
4500                "/dev/input/event0",
4501                std::ptr::null_mut(),
4502                ctx,
4503            )));
4504            enqueue_event(
4505                ctx,
4506                LibinputEvent {
4507                    event_type: LibinputEventType::LIBINPUT_EVENT_KEYBOARD_KEY,
4508                    payload: EventPayload::KeyboardKey(crate::ffi_types::KeyboardKeyEvent {
4509                        time_usec: 1,
4510                        key: 30,
4511                        state: 1,
4512                        seat_key_count: 1,
4513                    }),
4514                    context: ctx,
4515                    device,
4516                },
4517            );
4518            assert_eq!(
4519                (*device).refcount.load(std::sync::atomic::Ordering::SeqCst),
4520                2
4521            );
4522            let event = libinput_get_event(ctx);
4523            assert_eq!(libinput_event_get_device(event), device);
4524            libinput_event_destroy(event);
4525            assert_eq!(
4526                (*device).refcount.load(std::sync::atomic::Ordering::SeqCst),
4527                1
4528            );
4529            assert!(libinput_device_unref(device).is_null());
4530            libinput_unref(ctx);
4531        }
4532    }
4533
4534    #[test]
4535    fn ref_and_unref_return_the_upstream_object_lifecycle() {
4536        unsafe {
4537            let seat = Box::into_raw(Box::new(LibinputSeat {
4538                physical_name: std::ffi::CString::new("seat0").unwrap(),
4539                logical_name: std::ffi::CString::new("default").unwrap(),
4540                refcount: std::sync::atomic::AtomicI32::new(1),
4541                user_data: std::ptr::null_mut(),
4542                context: std::ptr::null_mut(),
4543                button_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4544                key_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4545            }));
4546            assert_eq!(libinput_seat_ref(seat.cast()), seat.cast());
4547            assert_eq!(libinput_seat_unref(seat.cast()), seat.cast());
4548            assert!(libinput_seat_unref(seat.cast()).is_null());
4549
4550            let group = Box::into_raw(Box::new(LibinputTabletPadModeGroup {
4551                refcount: std::sync::atomic::AtomicI32::new(1),
4552                user_data: std::ptr::null_mut(),
4553                device: std::ptr::null_mut(),
4554                index: 0,
4555                num_modes: 1,
4556                current_mode: 0,
4557                button_mask: 0,
4558                dial_mask: 0,
4559                ring_mask: 0,
4560                strip_mask: 0,
4561                toggle_button_mask: 0,
4562                toggle_modes: Vec::new(),
4563            }));
4564            assert_eq!(
4565                libinput_tablet_pad_mode_group_ref(group.cast()),
4566                group.cast()
4567            );
4568            assert_eq!(
4569                libinput_tablet_pad_mode_group_unref(group.cast()),
4570                group.cast()
4571            );
4572            assert!(libinput_tablet_pad_mode_group_unref(group.cast()).is_null());
4573        }
4574    }
4575
4576    #[test]
4577    fn devices_retain_their_seat_until_device_destruction() {
4578        unsafe {
4579            let seat = Box::into_raw(Box::new(LibinputSeat {
4580                physical_name: std::ffi::CString::new("seat0").unwrap(),
4581                logical_name: std::ffi::CString::new("default").unwrap(),
4582                refcount: std::sync::atomic::AtomicI32::new(1),
4583                user_data: std::ptr::null_mut(),
4584                context: std::ptr::null_mut(),
4585                button_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4586                key_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4587            }));
4588            let device = Box::into_raw(Box::new(LibinputDevice::new(
4589                "keyboard",
4590                "/dev/input/event0",
4591                seat,
4592                std::ptr::null_mut(),
4593            )));
4594            assert_eq!(
4595                (*seat).refcount.load(std::sync::atomic::Ordering::SeqCst),
4596                2
4597            );
4598            assert!(libinput_device_unref(device).is_null());
4599            assert_eq!(
4600                (*seat).refcount.load(std::sync::atomic::Ordering::SeqCst),
4601                1
4602            );
4603            assert!(libinput_seat_unref(seat.cast()).is_null());
4604        }
4605    }
4606}