Skip to main content

input/
lib.rs

1//! Rust implementation of the libinput.so.10 ABI.
2//!
3//! Version 0.3.10 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() || (*event).event_type != LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS
3393    {
3394        return 0.0;
3395    }
3396    match &(*event).payload {
3397        EventPayload::TabletTool(tablet) => tablet.dx,
3398        _ => 0.0,
3399    }
3400}
3401
3402#[no_mangle]
3403pub unsafe extern "C" fn libinput_event_tablet_tool_get_dy(event: *const LibinputEvent) -> f64 {
3404    if event.is_null() || (*event).event_type != LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS
3405    {
3406        return 0.0;
3407    }
3408    match &(*event).payload {
3409        EventPayload::TabletTool(tablet) => tablet.dy,
3410        _ => 0.0,
3411    }
3412}
3413
3414#[no_mangle]
3415pub unsafe extern "C" fn libinput_event_tablet_tool_get_pressure(
3416    event: *const LibinputEvent,
3417) -> f64 {
3418    if event.is_null() {
3419        return 0.0;
3420    }
3421    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3422        let range = tablet.pressure_max - tablet.pressure_min;
3423        if range > 0.0 {
3424            ((tablet.pressure - tablet.pressure_min) / range).clamp(0.0, 1.0)
3425        } else {
3426            0.0
3427        }
3428    } else {
3429        0.0
3430    }
3431}
3432
3433#[no_mangle]
3434pub unsafe extern "C" fn libinput_event_tablet_tool_get_x(event: *const LibinputEvent) -> f64 {
3435    if event.is_null() {
3436        return 0.0;
3437    }
3438    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3439        if tablet.x_resolution > 0.0 {
3440            (tablet.x - tablet.x_min) / tablet.x_resolution
3441        } else {
3442            tablet.x - tablet.x_min
3443        }
3444    } else {
3445        0.0
3446    }
3447}
3448
3449#[no_mangle]
3450pub unsafe extern "C" fn libinput_event_tablet_tool_get_y(event: *const LibinputEvent) -> f64 {
3451    if event.is_null() {
3452        return 0.0;
3453    }
3454    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3455        if tablet.y_resolution > 0.0 {
3456            (tablet.y - tablet.y_min) / tablet.y_resolution
3457        } else {
3458            tablet.y - tablet.y_min
3459        }
3460    } else {
3461        0.0
3462    }
3463}
3464
3465#[no_mangle]
3466pub unsafe extern "C" fn libinput_event_tablet_tool_get_proximity_state(
3467    event: *const LibinputEvent,
3468) -> u32 {
3469    if event.is_null() {
3470        return 0;
3471    }
3472    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3473        tablet.proximity_state
3474    } else {
3475        0
3476    }
3477}
3478
3479#[no_mangle]
3480pub unsafe extern "C" fn libinput_event_tablet_tool_get_rotation(
3481    event: *const LibinputEvent,
3482) -> f64 {
3483    if event.is_null() {
3484        return 0.0;
3485    }
3486    match &(*event).payload {
3487        EventPayload::TabletTool(tablet) => tablet.rotation,
3488        _ => 0.0,
3489    }
3490}
3491
3492#[no_mangle]
3493pub unsafe extern "C" fn libinput_event_tablet_tool_get_slider_position(
3494    event: *const LibinputEvent,
3495) -> f64 {
3496    if event.is_null() {
3497        return 0.0;
3498    }
3499    match &(*event).payload {
3500        EventPayload::TabletTool(tablet) => tablet.slider,
3501        _ => 0.0,
3502    }
3503}
3504
3505#[no_mangle]
3506pub unsafe extern "C" fn libinput_event_tablet_tool_get_wheel_delta(
3507    event: *const LibinputEvent,
3508) -> f64 {
3509    if event.is_null() {
3510        return 0.0;
3511    }
3512    match &(*event).payload {
3513        EventPayload::TabletTool(tablet) => tablet.wheel_delta,
3514        _ => 0.0,
3515    }
3516}
3517
3518#[no_mangle]
3519pub unsafe extern "C" fn libinput_event_tablet_tool_get_wheel_delta_discrete(
3520    event: *const LibinputEvent,
3521) -> i32 {
3522    if event.is_null() {
3523        return 0;
3524    }
3525    match &(*event).payload {
3526        EventPayload::TabletTool(tablet) => tablet.wheel_discrete,
3527        _ => 0,
3528    }
3529}
3530
3531#[no_mangle]
3532pub unsafe extern "C" fn libinput_event_tablet_tool_get_size_major(
3533    event: *const LibinputEvent,
3534) -> f64 {
3535    if event.is_null() {
3536        return 0.0;
3537    }
3538    match &(*event).payload {
3539        EventPayload::TabletTool(tablet) => tablet.size_major,
3540        _ => 0.0,
3541    }
3542}
3543
3544#[no_mangle]
3545pub unsafe extern "C" fn libinput_event_tablet_tool_get_size_minor(
3546    event: *const LibinputEvent,
3547) -> f64 {
3548    if event.is_null() {
3549        return 0.0;
3550    }
3551    match &(*event).payload {
3552        EventPayload::TabletTool(tablet) => tablet.size_minor,
3553        _ => 0.0,
3554    }
3555}
3556
3557#[no_mangle]
3558pub unsafe extern "C" fn libinput_event_tablet_tool_get_tilt_x(event: *const LibinputEvent) -> f64 {
3559    if event.is_null() {
3560        return 0.0;
3561    }
3562    match &(*event).payload {
3563        EventPayload::TabletTool(tablet) => tablet.tilt_x,
3564        _ => 0.0,
3565    }
3566}
3567
3568#[no_mangle]
3569pub unsafe extern "C" fn libinput_event_tablet_tool_get_tilt_y(event: *const LibinputEvent) -> f64 {
3570    if event.is_null() {
3571        return 0.0;
3572    }
3573    match &(*event).payload {
3574        EventPayload::TabletTool(tablet) => tablet.tilt_y,
3575        _ => 0.0,
3576    }
3577}
3578
3579#[no_mangle]
3580pub unsafe extern "C" fn libinput_event_tablet_tool_get_time_usec(
3581    event: *const LibinputEvent,
3582) -> u64 {
3583    if event.is_null() {
3584        return 0;
3585    }
3586    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3587        tablet.time_usec
3588    } else {
3589        0
3590    }
3591}
3592
3593#[no_mangle]
3594pub unsafe extern "C" fn libinput_event_tablet_tool_get_time(event: *const LibinputEvent) -> u32 {
3595    (libinput_event_tablet_tool_get_time_usec(event) / 1000) as u32
3596}
3597
3598#[no_mangle]
3599pub unsafe extern "C" fn libinput_event_tablet_tool_get_tip_state(
3600    event: *const LibinputEvent,
3601) -> u32 {
3602    if event.is_null() {
3603        return 0;
3604    }
3605    match &(*event).payload {
3606        EventPayload::TabletTool(tablet) => tablet.tip_state,
3607        _ => 0,
3608    }
3609}
3610
3611#[no_mangle]
3612pub unsafe extern "C" fn libinput_event_tablet_tool_get_tool(
3613    event: *const LibinputEvent,
3614) -> *mut libc::c_void {
3615    if event.is_null() {
3616        return std::ptr::null_mut();
3617    }
3618    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3619        tablet.tool.cast()
3620    } else {
3621        std::ptr::null_mut()
3622    }
3623}
3624
3625#[no_mangle]
3626pub unsafe extern "C" fn libinput_event_tablet_tool_get_x_transformed(
3627    event: *const LibinputEvent,
3628    width: u32,
3629) -> f64 {
3630    if event.is_null() {
3631        return 0.0;
3632    }
3633    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3634        let range = tablet.x_max - tablet.x_min + 1.0;
3635        if range > 0.0 {
3636            (tablet.x - tablet.x_min) * f64::from(width) / range
3637        } else {
3638            0.0
3639        }
3640    } else {
3641        0.0
3642    }
3643}
3644
3645#[no_mangle]
3646pub unsafe extern "C" fn libinput_event_tablet_tool_get_y_transformed(
3647    event: *const LibinputEvent,
3648    height: u32,
3649) -> f64 {
3650    if event.is_null() {
3651        return 0.0;
3652    }
3653    if let EventPayload::TabletTool(tablet) = &(*event).payload {
3654        let range = tablet.y_max - tablet.y_min + 1.0;
3655        if range > 0.0 {
3656            (tablet.y - tablet.y_min) * f64::from(height) / range
3657        } else {
3658            0.0
3659        }
3660    } else {
3661        0.0
3662    }
3663}
3664
3665#[no_mangle]
3666pub unsafe extern "C" fn libinput_event_tablet_tool_x_has_changed(
3667    event: *const LibinputEvent,
3668) -> libc::c_int {
3669    if event.is_null() {
3670        return 0;
3671    }
3672    match &(*event).payload {
3673        EventPayload::TabletTool(tablet) => tablet.x_changed as libc::c_int,
3674        _ => 0,
3675    }
3676}
3677
3678#[no_mangle]
3679pub unsafe extern "C" fn libinput_event_tablet_tool_y_has_changed(
3680    event: *const LibinputEvent,
3681) -> libc::c_int {
3682    if event.is_null() {
3683        return 0;
3684    }
3685    match &(*event).payload {
3686        EventPayload::TabletTool(tablet) => tablet.y_changed as libc::c_int,
3687        _ => 0,
3688    }
3689}
3690
3691#[no_mangle]
3692pub unsafe extern "C" fn libinput_event_tablet_tool_pressure_has_changed(
3693    event: *const LibinputEvent,
3694) -> libc::c_int {
3695    if event.is_null() {
3696        return 0;
3697    }
3698    match &(*event).payload {
3699        EventPayload::TabletTool(tablet) => tablet.pressure_changed as libc::c_int,
3700        _ => 0,
3701    }
3702}
3703
3704#[no_mangle]
3705pub unsafe extern "C" fn libinput_event_tablet_tool_distance_has_changed(
3706    event: *const LibinputEvent,
3707) -> libc::c_int {
3708    if event.is_null() {
3709        return 0;
3710    }
3711    match &(*event).payload {
3712        EventPayload::TabletTool(tablet) => tablet.distance_changed as libc::c_int,
3713        _ => 0,
3714    }
3715}
3716
3717#[no_mangle]
3718pub unsafe extern "C" fn libinput_event_tablet_tool_tilt_x_has_changed(
3719    event: *const LibinputEvent,
3720) -> libc::c_int {
3721    if event.is_null() {
3722        return 0;
3723    }
3724    match &(*event).payload {
3725        EventPayload::TabletTool(tablet) => tablet.tilt_x_changed as libc::c_int,
3726        _ => 0,
3727    }
3728}
3729
3730#[no_mangle]
3731pub unsafe extern "C" fn libinput_event_tablet_tool_tilt_y_has_changed(
3732    event: *const LibinputEvent,
3733) -> libc::c_int {
3734    if event.is_null() {
3735        return 0;
3736    }
3737    match &(*event).payload {
3738        EventPayload::TabletTool(tablet) => tablet.tilt_y_changed as libc::c_int,
3739        _ => 0,
3740    }
3741}
3742
3743#[no_mangle]
3744pub unsafe extern "C" fn libinput_event_tablet_tool_rotation_has_changed(
3745    event: *const LibinputEvent,
3746) -> libc::c_int {
3747    if event.is_null() {
3748        return 0;
3749    }
3750    match &(*event).payload {
3751        EventPayload::TabletTool(tablet) => tablet.rotation_changed as libc::c_int,
3752        _ => 0,
3753    }
3754}
3755
3756#[no_mangle]
3757pub unsafe extern "C" fn libinput_event_tablet_tool_slider_has_changed(
3758    event: *const LibinputEvent,
3759) -> libc::c_int {
3760    if event.is_null() {
3761        return 0;
3762    }
3763    match &(*event).payload {
3764        EventPayload::TabletTool(tablet) => tablet.slider_changed as libc::c_int,
3765        _ => 0,
3766    }
3767}
3768
3769#[no_mangle]
3770pub unsafe extern "C" fn libinput_event_tablet_tool_wheel_has_changed(
3771    event: *const LibinputEvent,
3772) -> libc::c_int {
3773    if event.is_null() {
3774        return 0;
3775    }
3776    match &(*event).payload {
3777        EventPayload::TabletTool(tablet) => tablet.wheel_changed as libc::c_int,
3778        _ => 0,
3779    }
3780}
3781
3782#[no_mangle]
3783pub unsafe extern "C" fn libinput_event_tablet_tool_size_major_has_changed(
3784    event: *const LibinputEvent,
3785) -> libc::c_int {
3786    if event.is_null() {
3787        return 0;
3788    }
3789    match &(*event).payload {
3790        EventPayload::TabletTool(tablet) => tablet.size_major_changed as libc::c_int,
3791        _ => 0,
3792    }
3793}
3794
3795#[no_mangle]
3796pub unsafe extern "C" fn libinput_event_tablet_tool_size_minor_has_changed(
3797    event: *const LibinputEvent,
3798) -> libc::c_int {
3799    if event.is_null() {
3800        return 0;
3801    }
3802    match &(*event).payload {
3803        EventPayload::TabletTool(tablet) => tablet.size_minor_changed as libc::c_int,
3804        _ => 0,
3805    }
3806}
3807
3808#[no_mangle]
3809pub unsafe extern "C" fn libinput_plugin_system_append_default_paths(ctx: *mut LibinputContext) {
3810    if ctx.is_null() || (*ctx).plugins_loaded {
3811        return;
3812    }
3813    for path in ["/etc/libinput/plugins", "/usr/lib64/libinput/plugins"] {
3814        let path = std::ffi::CString::new(path).expect("static plugin path");
3815        if !(*ctx)
3816            .plugin_paths
3817            .iter()
3818            .any(|candidate| candidate.as_bytes() == path.as_bytes())
3819        {
3820            (*ctx).plugin_paths.push(path);
3821        }
3822    }
3823}
3824
3825#[no_mangle]
3826pub unsafe extern "C" fn libinput_plugin_system_append_path(
3827    ctx: *mut LibinputContext,
3828    path: *const libc::c_char,
3829) {
3830    if ctx.is_null() || path.is_null() || (*ctx).plugins_loaded {
3831        return;
3832    }
3833    let path = CStr::from_ptr(path);
3834    if (*ctx)
3835        .plugin_paths
3836        .iter()
3837        .any(|candidate| candidate.as_bytes() == path.to_bytes())
3838    {
3839        return;
3840    }
3841    if let Ok(path) = std::ffi::CString::new(path.to_bytes()) {
3842        (*ctx).plugin_paths.push(path);
3843    }
3844}
3845
3846#[no_mangle]
3847pub unsafe extern "C" fn libinput_plugin_system_load_plugins(
3848    ctx: *mut LibinputContext,
3849    _flags: libc::c_uint,
3850) -> libc::c_int {
3851    if ctx.is_null() {
3852        return -libc::EINVAL;
3853    }
3854    if (*ctx).plugins_loaded {
3855        return 0;
3856    }
3857    // Built-in stages are always active, but this build has no Lua plugin
3858    // loader. Match an upstream build configured without plugin support:
3859    // freeze the plugin system and report ENOSYS instead of claiming success.
3860    (*ctx).plugins_loaded = true;
3861    -libc::ENOSYS
3862}
3863
3864#[no_mangle]
3865pub unsafe extern "C" fn libinput_tablet_pad_mode_group_button_is_toggle(
3866    group: *const libc::c_void,
3867    button: u32,
3868) -> libc::c_int {
3869    if group.is_null() || button >= u32::BITS {
3870        return 0;
3871    }
3872    ((*group.cast::<LibinputTabletPadModeGroup>()).toggle_button_mask & (1_u32 << button) != 0)
3873        as libc::c_int
3874}
3875
3876#[no_mangle]
3877pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_index(
3878    group: *const libc::c_void,
3879) -> u32 {
3880    if group.is_null() {
3881        return 0;
3882    }
3883    (*group.cast::<LibinputTabletPadModeGroup>()).index
3884}
3885
3886#[no_mangle]
3887pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_mode(
3888    group: *const libc::c_void,
3889) -> u32 {
3890    if group.is_null() {
3891        return 0;
3892    }
3893    (*group.cast::<LibinputTabletPadModeGroup>()).current_mode
3894}
3895
3896#[no_mangle]
3897pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_num_modes(
3898    group: *const libc::c_void,
3899) -> u32 {
3900    if group.is_null() {
3901        return 0;
3902    }
3903    (*group.cast::<LibinputTabletPadModeGroup>()).num_modes
3904}
3905
3906#[no_mangle]
3907pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_button(
3908    group: *const libc::c_void,
3909    button: u32,
3910) -> libc::c_int {
3911    if group.is_null() {
3912        return 0;
3913    }
3914    if button >= u32::BITS {
3915        return 0;
3916    }
3917    ((*group.cast::<LibinputTabletPadModeGroup>()).button_mask & (1_u32 << button) != 0)
3918        as libc::c_int
3919}
3920
3921#[no_mangle]
3922pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_dial(
3923    group: *const libc::c_void,
3924    dial: u32,
3925) -> libc::c_int {
3926    if group.is_null() {
3927        return 0;
3928    }
3929    if dial >= u32::BITS {
3930        return 0;
3931    }
3932    ((*group.cast::<LibinputTabletPadModeGroup>()).dial_mask & (1_u32 << dial) != 0) as libc::c_int
3933}
3934
3935#[no_mangle]
3936pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_ring(
3937    group: *const libc::c_void,
3938    ring: u32,
3939) -> libc::c_int {
3940    if group.is_null() {
3941        return 0;
3942    }
3943    if ring >= u32::BITS {
3944        return 0;
3945    }
3946    ((*group.cast::<LibinputTabletPadModeGroup>()).ring_mask & (1_u32 << ring) != 0) as libc::c_int
3947}
3948
3949#[no_mangle]
3950pub unsafe extern "C" fn libinput_tablet_pad_mode_group_has_strip(
3951    group: *const libc::c_void,
3952    strip: u32,
3953) -> libc::c_int {
3954    if group.is_null() {
3955        return 0;
3956    }
3957    if strip >= u32::BITS {
3958        return 0;
3959    }
3960    ((*group.cast::<LibinputTabletPadModeGroup>()).strip_mask & (1_u32 << strip) != 0)
3961        as libc::c_int
3962}
3963
3964#[no_mangle]
3965pub unsafe extern "C" fn libinput_tablet_pad_mode_group_ref(
3966    group: *mut libc::c_void,
3967) -> *mut libc::c_void {
3968    if !group.is_null() {
3969        (*group.cast::<LibinputTabletPadModeGroup>())
3970            .refcount
3971            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3972    }
3973    group
3974}
3975
3976#[no_mangle]
3977pub unsafe extern "C" fn libinput_tablet_pad_mode_group_unref(
3978    group: *mut libc::c_void,
3979) -> *mut libc::c_void {
3980    if group.is_null() {
3981        return std::ptr::null_mut();
3982    }
3983    let group = group.cast::<LibinputTabletPadModeGroup>();
3984    if (*group)
3985        .refcount
3986        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
3987        == 1
3988    {
3989        drop(Box::from_raw(group));
3990        std::ptr::null_mut()
3991    } else {
3992        group.cast()
3993    }
3994}
3995
3996#[no_mangle]
3997pub unsafe extern "C" fn libinput_tablet_pad_mode_group_set_user_data(
3998    group: *mut libc::c_void,
3999    data: *mut libc::c_void,
4000) {
4001    if !group.is_null() {
4002        (*group.cast::<LibinputTabletPadModeGroup>()).user_data = data;
4003    }
4004}
4005
4006#[no_mangle]
4007pub unsafe extern "C" fn libinput_tablet_pad_mode_group_get_user_data(
4008    group: *const libc::c_void,
4009) -> *mut libc::c_void {
4010    if group.is_null() {
4011        return std::ptr::null_mut();
4012    }
4013    (*group.cast::<LibinputTabletPadModeGroup>()).user_data
4014}
4015
4016#[no_mangle]
4017pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_is_available(
4018    tool: *const LibinputTabletTool,
4019) -> libc::c_int {
4020    (!tool.is_null() && (*tool).has_pressure) as libc::c_int
4021}
4022
4023#[no_mangle]
4024pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_set(
4025    tool: *mut LibinputTabletTool,
4026    minimum: f64,
4027    maximum: f64,
4028) -> u32 {
4029    if tool.is_null() || !(*tool).has_pressure {
4030        return 1;
4031    }
4032    if minimum < 0.0 || maximum > 1.0 || minimum >= maximum {
4033        return 2;
4034    }
4035    (*tool).wanted_pressure_range_minimum = minimum;
4036    (*tool).wanted_pressure_range_maximum = maximum;
4037    0
4038}
4039
4040#[no_mangle]
4041pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_minimum(
4042    tool: *const LibinputTabletTool,
4043) -> f64 {
4044    if tool.is_null() {
4045        0.0
4046    } else {
4047        (*tool).wanted_pressure_range_minimum
4048    }
4049}
4050
4051#[no_mangle]
4052pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_maximum(
4053    tool: *const LibinputTabletTool,
4054) -> f64 {
4055    if tool.is_null() {
4056        1.0
4057    } else {
4058        (*tool).wanted_pressure_range_maximum
4059    }
4060}
4061
4062#[no_mangle]
4063pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_default_minimum(
4064    _tool: *const libc::c_void,
4065) -> f64 {
4066    0.0
4067}
4068
4069#[no_mangle]
4070pub unsafe extern "C" fn libinput_tablet_tool_config_pressure_range_get_default_maximum(
4071    _tool: *const libc::c_void,
4072) -> f64 {
4073    1.0
4074}
4075
4076#[no_mangle]
4077pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_modes(
4078    tool: *const libc::c_void,
4079) -> u32 {
4080    let tool = tool.cast::<LibinputTabletTool>();
4081    if tool.is_null() {
4082        0
4083    } else {
4084        (*tool).eraser_button_modes
4085    }
4086}
4087
4088#[no_mangle]
4089pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_set_mode(
4090    tool: *mut libc::c_void,
4091    mode: u32,
4092) -> u32 {
4093    let tool = tool.cast::<LibinputTabletTool>();
4094    if tool.is_null() || (mode != 0 && ((*tool).eraser_button_modes & mode) == 0) {
4095        return 1;
4096    }
4097    if !matches!(mode, 0 | 1) {
4098        return 2;
4099    }
4100    (*tool).wanted_eraser_button_mode = mode;
4101    if !(*tool).in_proximity {
4102        (*tool).eraser_button_mode = mode;
4103    }
4104    0
4105}
4106
4107#[no_mangle]
4108pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_mode(
4109    tool: *const libc::c_void,
4110) -> u32 {
4111    let tool = tool.cast::<LibinputTabletTool>();
4112    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4113        0
4114    } else {
4115        (*tool).wanted_eraser_button_mode
4116    }
4117}
4118
4119#[no_mangle]
4120pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_default_mode(
4121    _tool: *const libc::c_void,
4122) -> u32 {
4123    0
4124}
4125
4126#[no_mangle]
4127pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_set_button(
4128    tool: *mut libc::c_void,
4129    button: u32,
4130) -> u32 {
4131    let tool = tool.cast::<LibinputTabletTool>();
4132    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4133        return 1;
4134    }
4135    let is_button = matches!(button, 0x149 | 0x14b | 0x14c)
4136        || (0x100..0x140).contains(&button)
4137        || (0x150..=0x151).contains(&button)
4138        || (0x220..=0x223).contains(&button)
4139        || (0x2c0..=0x2e7).contains(&button);
4140    if !is_button {
4141        return 2;
4142    }
4143    (*tool).wanted_eraser_button = button;
4144    if !(*tool).in_proximity {
4145        (*tool).eraser_button = button;
4146    }
4147    0
4148}
4149
4150#[no_mangle]
4151pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_button(
4152    tool: *const libc::c_void,
4153) -> u32 {
4154    let tool = tool.cast::<LibinputTabletTool>();
4155    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4156        0
4157    } else {
4158        (*tool).wanted_eraser_button
4159    }
4160}
4161
4162#[no_mangle]
4163pub unsafe extern "C" fn libinput_tablet_tool_config_eraser_button_get_default_button(
4164    tool: *const libc::c_void,
4165) -> u32 {
4166    let tool = tool.cast::<LibinputTabletTool>();
4167    if tool.is_null() || (*tool).eraser_button_modes == 0 {
4168        0
4169    } else {
4170        (*tool).default_eraser_button
4171    }
4172}
4173
4174#[no_mangle]
4175pub unsafe extern "C" fn libinput_tablet_tool_get_name(
4176    tool: *const libc::c_void,
4177) -> *const libc::c_char {
4178    let tool = tool.cast::<LibinputTabletTool>();
4179    if tool.is_null() {
4180        return std::ptr::null();
4181    }
4182    let tool = tool.cast::<LibinputTabletTool>();
4183    let tool = tool as *mut LibinputTabletTool;
4184    if let Some(name) = (*tool).name.as_ref() {
4185        return name.as_ptr();
4186    }
4187    if let Some(name) = crate::backend::tablet_tool_name_for_id((*tool).tool_id) {
4188        (*tool).name = Some(name);
4189        (*tool)
4190            .name
4191            .as_ref()
4192            .map_or(std::ptr::null(), |name| name.as_ptr())
4193    } else {
4194        std::ptr::null()
4195    }
4196}
4197
4198#[no_mangle]
4199pub unsafe extern "C" fn libinput_tablet_tool_get_serial(tool: *const libc::c_void) -> u64 {
4200    let tool = tool.cast::<LibinputTabletTool>();
4201    if tool.is_null() {
4202        0
4203    } else {
4204        (*tool).serial
4205    }
4206}
4207
4208#[no_mangle]
4209pub unsafe extern "C" fn libinput_tablet_tool_get_tool_id(tool: *const libc::c_void) -> u64 {
4210    let tool = tool.cast::<LibinputTabletTool>();
4211    if tool.is_null() {
4212        0
4213    } else {
4214        (*tool).tool_id
4215    }
4216}
4217
4218#[no_mangle]
4219pub unsafe extern "C" fn libinput_tablet_tool_get_type(tool: *const libc::c_void) -> u32 {
4220    let tool = tool.cast::<LibinputTabletTool>();
4221    if tool.is_null() {
4222        0
4223    } else {
4224        (*tool).tool_type
4225    }
4226}
4227
4228#[no_mangle]
4229pub unsafe extern "C" fn libinput_tablet_tool_has_distance(
4230    tool: *const libc::c_void,
4231) -> libc::c_int {
4232    let tool = tool.cast::<LibinputTabletTool>();
4233    (!tool.is_null() && (*tool).has_distance) as libc::c_int
4234}
4235
4236#[no_mangle]
4237pub unsafe extern "C" fn libinput_tablet_tool_has_button(
4238    tool: *const libc::c_void,
4239    button: u32,
4240) -> libc::c_int {
4241    let tool = tool.cast::<LibinputTabletTool>();
4242    (!tool.is_null() && (*tool).buttons.contains(&button)) as libc::c_int
4243}
4244
4245#[no_mangle]
4246pub unsafe extern "C" fn libinput_tablet_tool_has_size(tool: *const libc::c_void) -> libc::c_int {
4247    let tool = tool.cast::<LibinputTabletTool>();
4248    (!tool.is_null() && (*tool).has_size) as libc::c_int
4249}
4250
4251#[no_mangle]
4252pub unsafe extern "C" fn libinput_tablet_tool_is_unique(tool: *const libc::c_void) -> libc::c_int {
4253    let tool = tool.cast::<LibinputTabletTool>();
4254    (!tool.is_null() && (*tool).serial != 0) as libc::c_int
4255}
4256
4257#[no_mangle]
4258pub unsafe extern "C" fn libinput_tablet_tool_set_user_data(
4259    tool: *mut libc::c_void,
4260    data: *mut libc::c_void,
4261) {
4262    let tool = tool.cast::<LibinputTabletTool>();
4263    if !tool.is_null() {
4264        (*tool).user_data = data;
4265    }
4266}
4267
4268#[no_mangle]
4269pub unsafe extern "C" fn libinput_tablet_tool_get_user_data(
4270    tool: *const libc::c_void,
4271) -> *mut libc::c_void {
4272    let tool = tool.cast::<LibinputTabletTool>();
4273    if tool.is_null() {
4274        std::ptr::null_mut()
4275    } else {
4276        (*tool).user_data
4277    }
4278}
4279
4280#[no_mangle]
4281pub unsafe extern "C" fn libinput_tablet_tool_has_pressure(
4282    tool: *const libc::c_void,
4283) -> libc::c_int {
4284    let tool = tool.cast::<LibinputTabletTool>();
4285    (!tool.is_null() && (*tool).has_pressure) as libc::c_int
4286}
4287
4288#[no_mangle]
4289pub unsafe extern "C" fn libinput_tablet_tool_has_rotation(
4290    tool: *const libc::c_void,
4291) -> libc::c_int {
4292    let tool = tool.cast::<LibinputTabletTool>();
4293    (!tool.is_null() && (*tool).has_rotation) as libc::c_int
4294}
4295
4296#[no_mangle]
4297pub unsafe extern "C" fn libinput_tablet_tool_has_slider(tool: *const libc::c_void) -> libc::c_int {
4298    let tool = tool.cast::<LibinputTabletTool>();
4299    (!tool.is_null() && (*tool).has_slider) as libc::c_int
4300}
4301
4302#[no_mangle]
4303pub unsafe extern "C" fn libinput_tablet_tool_has_tilt(tool: *const libc::c_void) -> libc::c_int {
4304    let tool = tool.cast::<LibinputTabletTool>();
4305    (!tool.is_null() && (*tool).has_tilt) as libc::c_int
4306}
4307
4308#[no_mangle]
4309pub unsafe extern "C" fn libinput_tablet_tool_has_wheel(tool: *const libc::c_void) -> libc::c_int {
4310    let tool = tool.cast::<LibinputTabletTool>();
4311    (!tool.is_null() && (*tool).has_wheel) as libc::c_int
4312}
4313
4314#[no_mangle]
4315pub unsafe extern "C" fn libinput_tablet_tool_ref(tool: *mut libc::c_void) -> *mut libc::c_void {
4316    let tablet_tool = tool.cast::<LibinputTabletTool>();
4317    if !tablet_tool.is_null() {
4318        (*tablet_tool)
4319            .refcount
4320            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4321    }
4322    tool
4323}
4324
4325#[no_mangle]
4326pub unsafe extern "C" fn libinput_tablet_tool_unref(tool: *mut libc::c_void) -> *mut libc::c_void {
4327    let tablet_tool = tool.cast::<LibinputTabletTool>();
4328    if tablet_tool.is_null() {
4329        return std::ptr::null_mut();
4330    }
4331    if (*tablet_tool)
4332        .refcount
4333        .fetch_sub(1, std::sync::atomic::Ordering::AcqRel)
4334        == 1
4335    {
4336        if !(*tablet_tool).device.is_null() {
4337            libinput_device_unref((*tablet_tool).device);
4338            (*tablet_tool).device = std::ptr::null_mut();
4339        }
4340        drop(Box::from_raw(tablet_tool));
4341        std::ptr::null_mut()
4342    } else {
4343        tool
4344    }
4345}
4346
4347// ---------------------------------------------------------------------------
4348// Suspend / resume
4349// ---------------------------------------------------------------------------
4350
4351#[no_mangle]
4352pub unsafe extern "C" fn libinput_suspend(ctx: *mut LibinputContext) {
4353    if ctx.is_null() {
4354        return;
4355    }
4356    let mut events = std::collections::VecDeque::new();
4357    if let Ok(mut backend) = (*ctx).backend.lock() {
4358        backend.suspend(ctx, &mut events);
4359    }
4360    enqueue_events(ctx, events);
4361}
4362
4363#[no_mangle]
4364pub unsafe extern "C" fn libinput_resume(ctx: *mut LibinputContext) -> libc::c_int {
4365    if ctx.is_null() {
4366        return -1;
4367    }
4368    let mut events = std::collections::VecDeque::new();
4369    let status = if let Ok(mut backend) = (*ctx).backend.lock() {
4370        backend.resume(ctx, &mut events)
4371    } else {
4372        return -1;
4373    };
4374    enqueue_events(ctx, events);
4375    if !(*ctx).event_queue.is_empty() {
4376        (*ctx).signal_fd();
4377    }
4378    status
4379}
4380
4381#[cfg(test)]
4382mod tests {
4383    use super::*;
4384
4385    unsafe extern "C" fn deny_open(
4386        _path: *const libc::c_char,
4387        _flags: libc::c_int,
4388        _user_data: *mut libc::c_void,
4389    ) -> libc::c_int {
4390        -libc::EACCES
4391    }
4392
4393    unsafe extern "C" fn close_fd(_fd: libc::c_int, _user_data: *mut libc::c_void) {}
4394
4395    static INTERFACE: LibinputInterface = LibinputInterface {
4396        open_restricted: Some(deny_open),
4397        close_restricted: Some(close_fd),
4398    };
4399
4400    #[test]
4401    fn udev_context_requires_interface_and_udev() {
4402        unsafe {
4403            let fake_udev = 1usize as *mut libc::c_void;
4404            assert!(libinput_udev_create_context(
4405                std::ptr::null(),
4406                std::ptr::null_mut(),
4407                fake_udev,
4408            )
4409            .is_null());
4410            assert!(libinput_udev_create_context(
4411                &INTERFACE,
4412                std::ptr::null_mut(),
4413                std::ptr::null_mut(),
4414            )
4415            .is_null());
4416        }
4417    }
4418
4419    #[test]
4420    fn seat_assignment_is_udev_only_and_happens_once() {
4421        unsafe {
4422            let fake_udev = 1usize as *mut libc::c_void;
4423            let seat = std::ffi::CString::new("seat0").unwrap();
4424            let udev_ctx =
4425                libinput_udev_create_context(&INTERFACE, std::ptr::null_mut(), fake_udev);
4426            assert!(!udev_ctx.is_null());
4427            assert_eq!(libinput_udev_assign_seat(udev_ctx, seat.as_ptr()), 0);
4428            assert_eq!(libinput_udev_assign_seat(udev_ctx, seat.as_ptr()), -1);
4429            libinput_unref(udev_ctx);
4430
4431            let path_ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4432            assert!(!path_ctx.is_null());
4433            assert_eq!(libinput_udev_assign_seat(path_ctx, seat.as_ptr()), -1);
4434            libinput_unref(path_ctx);
4435        }
4436    }
4437
4438    #[test]
4439    fn overlong_seat_name_does_not_assign_or_queue_devices() {
4440        unsafe {
4441            let fake_udev = 1usize as *mut libc::c_void;
4442            let seat = std::ffi::CString::new("a".repeat(257)).unwrap();
4443            let ctx = libinput_udev_create_context(&INTERFACE, std::ptr::null_mut(), fake_udev);
4444            assert!(!ctx.is_null());
4445
4446            assert_eq!(libinput_udev_assign_seat(ctx, seat.as_ptr()), -1);
4447            assert!(!(*ctx).seat_assigned);
4448            assert!((*ctx).event_queue.is_empty());
4449
4450            libinput_unref(ctx);
4451        }
4452    }
4453
4454    #[test]
4455    fn suspend_and_resume_are_null_safe() {
4456        unsafe {
4457            libinput_suspend(std::ptr::null_mut());
4458            assert_eq!(libinput_resume(std::ptr::null_mut()), -1);
4459        }
4460    }
4461
4462    #[test]
4463    fn dispatch_returns_negative_errno_on_epoll_failure() {
4464        unsafe {
4465            let ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4466            assert!(!ctx.is_null());
4467            libc::close((*ctx).epoll_fd);
4468            (*ctx).epoll_fd = -1;
4469            assert_eq!(libinput_dispatch(ctx), -libc::EBADF);
4470            libinput_unref(ctx);
4471        }
4472    }
4473
4474    #[test]
4475    fn shared_device_groups_preserve_identity_and_user_data() {
4476        unsafe {
4477            let first = Box::new(LibinputDevice::new(
4478                "first",
4479                "/dev/input/event0",
4480                std::ptr::null_mut(),
4481                std::ptr::null_mut(),
4482            ));
4483            let group = first.group;
4484            let mut second = Box::new(LibinputDevice::new(
4485                "second",
4486                "/dev/input/event1",
4487                std::ptr::null_mut(),
4488                std::ptr::null_mut(),
4489            ));
4490            second.share_group(group);
4491
4492            assert_eq!(second.group, group);
4493            assert_eq!(second.abi.group, group);
4494            assert_eq!(
4495                (*group).refcount.load(std::sync::atomic::Ordering::Relaxed),
4496                2
4497            );
4498            let marker = 0x5ausize as *mut libc::c_void;
4499            libinput_device_group_set_user_data(group.cast(), marker);
4500            assert_eq!(
4501                libinput_device_group_get_user_data(second.group.cast()),
4502                marker
4503            );
4504
4505            drop(second);
4506            assert_eq!(
4507                (*group).refcount.load(std::sync::atomic::Ordering::Relaxed),
4508                1
4509            );
4510            drop(first);
4511        }
4512    }
4513
4514    #[test]
4515    fn custom_acceleration_validates_and_copies_curves() {
4516        unsafe {
4517            assert!(libinput_config_accel_create(0).is_null());
4518            let config = libinput_config_accel_create(4);
4519            assert!(!config.is_null());
4520
4521            let one_point = [1.0];
4522            assert_eq!(
4523                libinput_config_accel_set_points(config, 1, 1.0, 1, one_point.as_ptr()),
4524                2
4525            );
4526            let points = [0.0, 2.0, 6.0];
4527            assert_eq!(
4528                libinput_config_accel_set_points(config, 1, 1.0, points.len(), points.as_ptr()),
4529                0
4530            );
4531            let scroll_points = [0.0, 3.0, 9.0];
4532            assert_eq!(
4533                libinput_config_accel_set_points(
4534                    config,
4535                    2,
4536                    1.0,
4537                    scroll_points.len(),
4538                    scroll_points.as_ptr(),
4539                ),
4540                0
4541            );
4542
4543            let mut device = Box::new(LibinputDevice::new(
4544                "pointer",
4545                "/dev/input/event0",
4546                std::ptr::null_mut(),
4547                std::ptr::null_mut(),
4548            ));
4549            device.accel_available = true;
4550            assert_eq!(libinput_device_config_accel_apply(&mut *device, config), 0);
4551            libinput_config_accel_destroy(config);
4552
4553            let custom = device.accel_custom.as_mut().expect("custom config copied");
4554            let curve = custom.curve_mut(1).expect("motion curve copied");
4555            assert_eq!(curve.points, points);
4556            assert!((curve.factor(7.0, 0.0, 7_000) - 2.0).abs() < 1e-9);
4557            let scroll_curve = custom.curve_mut(2).expect("scroll curve copied");
4558            assert_eq!(scroll_curve.points, scroll_points);
4559            assert!((scroll_curve.factor(7.0, 0.0, 7_000) - 3.0).abs() < 1e-9);
4560        }
4561    }
4562
4563    #[test]
4564    fn custom_acceleration_uses_fallback_and_extrapolates() {
4565        let mut config = crate::ffi_types::AccelConfig::new(4);
4566        config.fallback = Some(crate::ffi_types::AccelCurve::new(1.0, vec![0.0, 1.0]));
4567        let curve = config.curve_mut(1).expect("fallback curve");
4568        assert!((curve.factor(14.0, 0.0, 7_000) - 1.0).abs() < 1e-9);
4569    }
4570
4571    #[test]
4572    fn plugin_paths_are_ordered_unique_and_frozen_after_load() {
4573        unsafe {
4574            let ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4575            let custom = std::ffi::CString::new("/tmp/plugins").unwrap();
4576            let empty = std::ffi::CString::new("").unwrap();
4577            libinput_plugin_system_append_path(ctx, custom.as_ptr());
4578            libinput_plugin_system_append_path(ctx, custom.as_ptr());
4579            libinput_plugin_system_append_path(ctx, empty.as_ptr());
4580            libinput_plugin_system_append_default_paths(ctx);
4581            assert_eq!((*ctx).plugin_paths.len(), 4);
4582            assert_eq!((&(*ctx).plugin_paths)[0].as_bytes(), b"/tmp/plugins");
4583            assert_eq!((&(*ctx).plugin_paths)[1].as_bytes(), b"");
4584            assert_eq!(
4585                libinput_plugin_system_load_plugins(ctx, u32::MAX),
4586                -libc::ENOSYS
4587            );
4588            let ignored = std::ffi::CString::new("/ignored").unwrap();
4589            libinput_plugin_system_append_path(ctx, ignored.as_ptr());
4590            assert_eq!((*ctx).plugin_paths.len(), 4);
4591            assert_eq!(libinput_plugin_system_load_plugins(ctx, 0), 0);
4592            libinput_unref(ctx);
4593        }
4594    }
4595
4596    #[test]
4597    fn queued_events_retain_devices_until_event_destroy() {
4598        unsafe {
4599            let ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4600            let device = Box::into_raw(Box::new(LibinputDevice::new(
4601                "keyboard",
4602                "/dev/input/event0",
4603                std::ptr::null_mut(),
4604                ctx,
4605            )));
4606            enqueue_event(
4607                ctx,
4608                LibinputEvent {
4609                    event_type: LibinputEventType::LIBINPUT_EVENT_KEYBOARD_KEY,
4610                    payload: EventPayload::KeyboardKey(crate::ffi_types::KeyboardKeyEvent {
4611                        time_usec: 1,
4612                        key: 30,
4613                        state: 1,
4614                        seat_key_count: 1,
4615                    }),
4616                    context: ctx,
4617                    device,
4618                },
4619            );
4620            assert_eq!(
4621                (*device).refcount.load(std::sync::atomic::Ordering::SeqCst),
4622                2
4623            );
4624            let event = libinput_get_event(ctx);
4625            assert_eq!(libinput_event_get_device(event), device);
4626            libinput_event_destroy(event);
4627            assert_eq!(
4628                (*device).refcount.load(std::sync::atomic::Ordering::SeqCst),
4629                1
4630            );
4631            assert!(libinput_device_unref(device).is_null());
4632            libinput_unref(ctx);
4633        }
4634    }
4635
4636    #[test]
4637    fn ref_and_unref_return_the_upstream_object_lifecycle() {
4638        unsafe {
4639            let seat = Box::into_raw(Box::new(LibinputSeat {
4640                physical_name: std::ffi::CString::new("seat0").unwrap(),
4641                logical_name: std::ffi::CString::new("default").unwrap(),
4642                refcount: std::sync::atomic::AtomicI32::new(1),
4643                user_data: std::ptr::null_mut(),
4644                context: std::ptr::null_mut(),
4645                button_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4646                key_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4647            }));
4648            assert_eq!(libinput_seat_ref(seat.cast()), seat.cast());
4649            assert_eq!(libinput_seat_unref(seat.cast()), seat.cast());
4650            assert!(libinput_seat_unref(seat.cast()).is_null());
4651
4652            let group = Box::into_raw(Box::new(LibinputTabletPadModeGroup {
4653                refcount: std::sync::atomic::AtomicI32::new(1),
4654                user_data: std::ptr::null_mut(),
4655                device: std::ptr::null_mut(),
4656                index: 0,
4657                num_modes: 1,
4658                current_mode: 0,
4659                button_mask: 0,
4660                dial_mask: 0,
4661                ring_mask: 0,
4662                strip_mask: 0,
4663                toggle_button_mask: 0,
4664                toggle_modes: Vec::new(),
4665            }));
4666            assert_eq!(
4667                libinput_tablet_pad_mode_group_ref(group.cast()),
4668                group.cast()
4669            );
4670            assert_eq!(
4671                libinput_tablet_pad_mode_group_unref(group.cast()),
4672                group.cast()
4673            );
4674            assert!(libinput_tablet_pad_mode_group_unref(group.cast()).is_null());
4675        }
4676    }
4677
4678    #[test]
4679    fn devices_retain_their_seat_until_device_destruction() {
4680        unsafe {
4681            let seat = Box::into_raw(Box::new(LibinputSeat {
4682                physical_name: std::ffi::CString::new("seat0").unwrap(),
4683                logical_name: std::ffi::CString::new("default").unwrap(),
4684                refcount: std::sync::atomic::AtomicI32::new(1),
4685                user_data: std::ptr::null_mut(),
4686                context: std::ptr::null_mut(),
4687                button_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4688                key_counts: std::sync::Mutex::new(crate::evtrans::empty_seat_code_counts()),
4689            }));
4690            let device = Box::into_raw(Box::new(LibinputDevice::new(
4691                "keyboard",
4692                "/dev/input/event0",
4693                seat,
4694                std::ptr::null_mut(),
4695            )));
4696            assert_eq!(
4697                (*seat).refcount.load(std::sync::atomic::Ordering::SeqCst),
4698                2
4699            );
4700            assert!(libinput_device_unref(device).is_null());
4701            assert_eq!(
4702                (*seat).refcount.load(std::sync::atomic::Ordering::SeqCst),
4703                1
4704            );
4705            assert!(libinput_seat_unref(seat.cast()).is_null());
4706        }
4707    }
4708
4709    #[test]
4710    fn config_status_strings_match_upstream() {
4711        unsafe {
4712            assert_eq!(
4713                std::ffi::CStr::from_ptr(libinput_config_status_to_str(0)).to_bytes(),
4714                b"Success"
4715            );
4716            assert_eq!(
4717                std::ffi::CStr::from_ptr(libinput_config_status_to_str(1)).to_bytes(),
4718                b"Unsupported configuration option"
4719            );
4720            assert_eq!(
4721                std::ffi::CStr::from_ptr(libinput_config_status_to_str(2)).to_bytes(),
4722                b"Invalid argument range"
4723            );
4724            assert!(libinput_config_status_to_str(3).is_null());
4725        }
4726    }
4727
4728    #[test]
4729    fn absolute_pointer_coordinates_snapshot_calibration_and_use_inclusive_ranges() {
4730        unsafe {
4731            let mut device = Box::new(LibinputDevice::new(
4732                "pointer",
4733                "/dev/input/event0",
4734                std::ptr::null_mut(),
4735                std::ptr::null_mut(),
4736            ));
4737            device.abs_x_resolution = Some(20);
4738            device.abs_y_resolution = Some(40);
4739            let event = LibinputEvent {
4740                event_type: LibinputEventType::LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE,
4741                payload: EventPayload::PointerMotionAbsolute(
4742                    crate::ffi_types::PointerMotionAbsoluteEvent {
4743                        time_usec: 1,
4744                        abs_x: 110.0,
4745                        abs_y: 220.0,
4746                        x_min: 10.0,
4747                        x_max: 1009.0,
4748                        y_min: 20.0,
4749                        y_max: 2019.0,
4750                        calibration: [1.0, 0.0, 0.1, 0.0, 1.0, 0.0],
4751                    },
4752                ),
4753                context: std::ptr::null_mut(),
4754                device: &mut *device,
4755            };
4756            device.calibration = [1.0, 0.0, 0.9, 0.0, 1.0, 0.0];
4757
4758            assert!((libinput_event_pointer_get_absolute_x(&event) - 10.0).abs() < 1e-6);
4759            assert_eq!(libinput_event_pointer_get_absolute_y(&event), 5.0);
4760            assert!(
4761                (libinput_event_pointer_get_absolute_x_transformed(&event, 100) - 20.0).abs()
4762                    < 1e-6
4763            );
4764            assert_eq!(
4765                libinput_event_pointer_get_absolute_y_transformed(&event, 100),
4766                10.0
4767            );
4768        }
4769    }
4770
4771    #[test]
4772    fn touch_cancel_retains_slot_information() {
4773        unsafe {
4774            let event = LibinputEvent {
4775                event_type: LibinputEventType::LIBINPUT_EVENT_TOUCH_CANCEL,
4776                payload: EventPayload::TouchCancel(crate::ffi_types::TouchEvent {
4777                    time_usec: 1,
4778                    slot: 7,
4779                    seat_slot: 11,
4780                    x: 0.0,
4781                    y: 0.0,
4782                    calibration: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
4783                }),
4784                context: std::ptr::null_mut(),
4785                device: std::ptr::null_mut(),
4786            };
4787
4788            assert_eq!(libinput_event_touch_get_slot(&event), 7);
4789            assert_eq!(libinput_event_touch_get_seat_slot(&event), 11);
4790        }
4791    }
4792
4793    #[test]
4794    fn touch_coordinates_use_event_time_calibration() {
4795        unsafe {
4796            let mut device = Box::new(LibinputDevice::new(
4797                "touch",
4798                "/dev/input/event0",
4799                std::ptr::null_mut(),
4800                std::ptr::null_mut(),
4801            ));
4802            device.abs_x_range = Some((10, 1009));
4803            device.abs_y_range = Some((20, 2019));
4804            device.abs_x_resolution = Some(20);
4805            device.abs_y_resolution = Some(40);
4806            let event = LibinputEvent {
4807                event_type: LibinputEventType::LIBINPUT_EVENT_TOUCH_MOTION,
4808                payload: EventPayload::TouchMotion(crate::ffi_types::TouchEvent {
4809                    time_usec: 1,
4810                    slot: 0,
4811                    seat_slot: 0,
4812                    x: 110.0,
4813                    y: 220.0,
4814                    calibration: [1.0, 0.0, 0.1, 0.0, 1.0, 0.0],
4815                }),
4816                context: std::ptr::null_mut(),
4817                device: &mut *device,
4818            };
4819            device.calibration = [1.0, 0.0, 0.9, 0.0, 1.0, 0.0];
4820
4821            assert!((libinput_event_touch_get_x(&event) - 10.0).abs() < 1e-6);
4822            assert_eq!(libinput_event_touch_get_y(&event), 5.0);
4823            assert!((libinput_event_touch_get_x_transformed(&event, 100) - 20.0).abs() < 1e-6);
4824            assert_eq!(libinput_event_touch_get_y_transformed(&event, 100), 10.0);
4825        }
4826    }
4827
4828    #[test]
4829    fn tablet_deltas_are_exposed_only_for_axis_events() {
4830        unsafe {
4831            let mut event = LibinputEvent {
4832                event_type: LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_AXIS,
4833                payload: EventPayload::TabletTool(crate::ffi_types::TabletToolEvent {
4834                    time_usec: 1,
4835                    tool: std::ptr::null_mut(),
4836                    proximity_state: 0,
4837                    x: 0.0,
4838                    y: 0.0,
4839                    dx: 2.5,
4840                    dy: -1.5,
4841                    x_min: 0.0,
4842                    x_max: 1.0,
4843                    y_min: 0.0,
4844                    y_max: 1.0,
4845                    x_resolution: 1.0,
4846                    y_resolution: 1.0,
4847                    x_changed: true,
4848                    y_changed: true,
4849                    pressure: 0.0,
4850                    pressure_min: 0.0,
4851                    pressure_max: 1.0,
4852                    pressure_changed: false,
4853                    distance: 0.0,
4854                    distance_changed: false,
4855                    tilt_x: 0.0,
4856                    tilt_y: 0.0,
4857                    tilt_x_changed: false,
4858                    tilt_y_changed: false,
4859                    rotation: 0.0,
4860                    rotation_changed: false,
4861                    slider: 0.0,
4862                    slider_changed: false,
4863                    wheel_delta: 0.0,
4864                    wheel_discrete: 0,
4865                    wheel_changed: false,
4866                    size_major: 0.0,
4867                    size_minor: 0.0,
4868                    size_major_changed: false,
4869                    size_minor_changed: false,
4870                    tip_state: 1,
4871                    button: 0,
4872                    button_state: 0,
4873                    seat_button_count: 0,
4874                }),
4875                context: std::ptr::null_mut(),
4876                device: std::ptr::null_mut(),
4877            };
4878
4879            assert_eq!(libinput_event_tablet_tool_get_dx(&event), 2.5);
4880            assert_eq!(libinput_event_tablet_tool_get_dy(&event), -1.5);
4881
4882            for event_type in [
4883                LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY,
4884                LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_TIP,
4885                LibinputEventType::LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
4886            ] {
4887                event.event_type = event_type;
4888                assert_eq!(libinput_event_tablet_tool_get_dx(&event), 0.0);
4889                assert_eq!(libinput_event_tablet_tool_get_dy(&event), 0.0);
4890            }
4891        }
4892    }
4893
4894    #[test]
4895    fn unsupported_configs_report_unsupported_before_invalid_values() {
4896        unsafe {
4897            let mut device = Box::new(LibinputDevice::new(
4898                "device",
4899                "/dev/input/event0",
4900                std::ptr::null_mut(),
4901                std::ptr::null_mut(),
4902            ));
4903
4904            assert_eq!(
4905                libinput_device_config_rotation_set_angle(&mut *device, 360),
4906                1
4907            );
4908            assert_eq!(
4909                libinput_device_config_rotation_set_angle(&mut *device, 0),
4910                0
4911            );
4912            assert_eq!(
4913                libinput_device_config_3fg_drag_set_enabled(&mut *device, u32::MAX),
4914                1
4915            );
4916            assert_eq!(libinput_device_pointer_has_button(&*device, u32::MAX), -1);
4917            assert_eq!(libinput_device_switch_has_switch(&*device, 1), -1);
4918            assert_eq!(libinput_device_switch_has_switch(&*device, u32::MAX), -1);
4919        }
4920    }
4921
4922    #[test]
4923    fn setting_a_null_log_handler_disables_the_default_handler() {
4924        unsafe {
4925            let ctx = libinput_path_create_context(&INTERFACE, std::ptr::null_mut());
4926            assert!((*ctx).default_log_handler_enabled);
4927            libinput_log_set_handler(ctx, None);
4928            assert!(!(*ctx).default_log_handler_enabled);
4929            libinput_unref(ctx);
4930        }
4931    }
4932}