Skip to main content

input/
lib.rs

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