input/
device.rs

1// Allow unnecessary casts since ffi types may differ by C ABI
2// TODO Error type instead of `Result<_, ()>`
3// TODO Better way to handle `SendEventsMode::ENABLED` being 0?
4#![allow(
5    clippy::bad_bit_mask,
6    clippy::result_unit_err,
7    clippy::unnecessary_cast
8)]
9
10#[cfg(feature = "libinput_1_23")]
11use crate::accel_config::AccelConfig;
12use crate::{
13    event::{switch::Switch, tablet_pad::TabletPadModeGroup},
14    ffi, AsRaw, FromRaw, Libinput, Seat,
15};
16use bitflags::bitflags;
17use std::{
18    borrow::Cow,
19    ffi::{CStr, CString},
20};
21#[cfg(feature = "udev")]
22use udev::{
23    ffi::{udev as udev_context, udev_device, udev_device_get_udev, udev_ref},
24    Device as UdevDevice, FromRawWithContext as UdevFromRawWithContext,
25};
26
27/// Capabilities on a device.
28///
29/// A device may have one or more capabilities at a time, capabilities
30/// remain static for the lifetime of the device.
31#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
32#[non_exhaustive]
33pub enum DeviceCapability {
34    /// Keyboard capability
35    Keyboard,
36    /// Pointer capability
37    Pointer,
38    /// Touch capability
39    Touch,
40    /// TabletTool capability
41    TabletTool,
42    /// TabletPad capability
43    TabletPad,
44    /// Gesture capability
45    Gesture,
46    /// Switch capability
47    Switch,
48}
49
50/// Pointer Acceleration Profile
51#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
52#[non_exhaustive]
53pub enum AccelProfile {
54    /// A flat acceleration profile.
55    ///
56    /// Pointer motion is accelerated by a constant (device-specific)
57    /// factor, depending on the current speed.
58    Flat,
59    /// An adaptive acceleration profile.
60    ///
61    /// Pointer acceleration depends on the input speed. This is the
62    /// default profile for most devices.
63    Adaptive,
64    /// A custom acceleration profile.
65    ///
66    /// Device movement acceleration depends on user defined custom
67    /// acceleration functions for each movement type.
68    #[cfg(feature = "libinput_1_23")]
69    Custom,
70}
71
72/// Acceleration types are categories of movement by a device that may have
73/// specific acceleration functions applied. A device always supports the
74/// [`AccelType::Motion`] type (for regular pointer motion).
75/// Other types (e.g. scrolling) may be added in the future.
76///
77/// The special type [`AccelType::Fallback`] specifies the acceleration function
78/// to be moved for any movement produced by the device that does not have a
79/// specific acceleration type defined.
80///
81/// Use to specify the acceleration function type in [`AccelConfig::set_points`].
82///
83/// Each device implements a subset of those types, see a list of supported
84/// devices for each movement type definition.
85#[cfg(feature = "libinput_1_23")]
86#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
87#[non_exhaustive]
88pub enum AccelType {
89    /// The default acceleration type used as a fallback when other acceleration
90    /// types are not provided.
91    Fallback,
92    /// Acceleration type for regular pointer movement.
93    ///
94    /// This type is always supported.
95    Motion,
96    /// Acceleration type for scroll movement.
97    ///
98    /// This type is supported by mouse and touchpad.
99    Scroll,
100}
101
102/// The click method defines when to generate software-emulated
103/// buttons, usually on a device that does not have a specific
104/// physical button available.
105#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
106#[non_exhaustive]
107pub enum ClickMethod {
108    /// Use software-button areas (see [Clickfinger behavior](https://wayland.freedesktop.org/libinput/doc/latest/clickpad_softbuttons.html#clickfinger))
109    /// to generate button events.
110    ButtonAreas,
111    /// The number of fingers decides which button press to generate.
112    Clickfinger,
113}
114
115/// The scroll method of a device selects when to generate scroll axis
116/// events instead of pointer motion events.
117#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
118#[non_exhaustive]
119pub enum ScrollMethod {
120    /// Never send scroll events instead of pointer motion events.
121    ///
122    /// This has no effect on events generated by scroll wheels.
123    NoScroll,
124    /// Send scroll events when two fingers are logically down on the
125    /// device.
126    TwoFinger,
127    /// Send scroll events when a finger moves along the bottom or
128    /// right edge of a device.
129    Edge,
130    /// Send scroll events when a button is down and the device moves
131    /// along a scroll-capable axis.
132    OnButtonDown,
133}
134
135/// Errors returned when applying configuration settings.
136#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
137pub enum DeviceConfigError {
138    /// Configuration not available on this device.
139    Unsupported,
140    /// Invalid parameter range.
141    Invalid,
142}
143
144impl DeviceConfigError {
145    #[cfg(feature = "libinput_1_29")]
146    pub(crate) fn from_ffi(v: ffi::libinput_config_status) -> DeviceConfigResult {
147        match v {
148            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
149            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
150                Err(DeviceConfigError::Unsupported)
151            }
152            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
153                Err(DeviceConfigError::Invalid)
154            }
155            _ => unreachable!("libinput returned invalid 'libinput_config_status'"),
156        }
157    }
158}
159
160bitflags! {
161    /// The send-event mode of a device defines when a device may generate
162    /// events and pass those events to the caller.
163    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
164    pub struct SendEventsMode: u32 {
165        /// Send events from this device normally.
166        ///
167        /// This is a placeholder mode only, any device detected by
168        /// libinput can be enabled. Do not test for this value as bitmask.
169        const ENABLED = ffi::libinput_config_send_events_mode_LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
170        /// Do not send events through this device.
171        ///
172        /// Depending on the device, this may close all file descriptors
173        /// on the device or it may leave the file descriptors open and
174        /// route events through a different device.
175        ///
176        /// If this mode is set, other disable modes may be ignored.
177        /// For example, if both `Disabled` and `DisabledOnExternalMouse`
178        /// are set, the device remains disabled when all external pointer
179        /// devices are unplugged.
180        const DISABLED = ffi::libinput_config_send_events_mode_LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
181        /// If an external pointer device is plugged in, do not send
182        /// events from this device.
183        ///
184        /// This option may be available on built-in touchpads.
185        const DISABLED_ON_EXTERNAL_MOUSE = ffi::libinput_config_send_events_mode_LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
186    }
187}
188
189/// Map 1/2/3 finger tips to buttons
190#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
191#[non_exhaustive]
192pub enum TapButtonMap {
193    /// 1/2/3 finger tap maps to left/right/middle
194    LeftRightMiddle,
195    /// 1/2/3 finger tap maps to left/middle/right
196    LeftMiddleRight,
197}
198
199/// Map 1/2/3 finger clicks to buttons
200#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
201#[non_exhaustive]
202#[cfg(feature = "libinput_1_26")]
203pub enum ClickfingerButtonMap {
204    /// 1/2/3 finger click maps to left/right/middle
205    LeftRightMiddle,
206    /// 1/2/3 finger click maps to left/middle/right
207    LeftMiddleRight,
208}
209
210/// Drag lock state
211#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
212#[non_exhaustive]
213pub enum DragLockState {
214    /// Drag lock is to be disabled, or is currently disabled
215    Disabled,
216    /// Drag lock is to be enabled in timeout mode,
217    /// or is currently enabled in timeout mode
218    EnabledTimeout,
219    /// Drag lock is to be enabled in sticky mode,
220    /// or is currently enabled in sticky mode
221    #[cfg(feature = "libinput_1_27")]
222    EnabledSticky,
223}
224
225/// A config status to distinguish or set 3-finger dragging on a device.
226#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
227#[non_exhaustive]
228#[cfg(feature = "libinput_1_28")]
229pub enum ThreeFingerDragState {
230    /// Drag is to be disabled, or is currently disabled
231    Disabled,
232    /// Drag is to be enabled for 3 fingers, or is currently enabled
233    EnabledThreeFinger,
234    /// Drag is to be enabled for 4 fingers, or is currently enabled
235    EnabledFourFinger,
236}
237
238/// Whenever scroll button lock is enabled or not
239#[cfg(feature = "libinput_1_15")]
240#[allow(missing_docs)]
241#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
242pub enum ScrollButtonLockState {
243    Disabled,
244    Enabled,
245}
246
247/// Result returned when applying configuration settings.
248pub type DeviceConfigResult = Result<(), DeviceConfigError>;
249
250bitflags! {
251    /// Mask reflecting LEDs on a device.
252    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
253    pub struct Led: u32 {
254        /// Num Lock Led
255        const NUMLOCK = ffi::libinput_led_LIBINPUT_LED_NUM_LOCK;
256        /// Caps Lock Led
257        const CAPSLOCK = ffi::libinput_led_LIBINPUT_LED_CAPS_LOCK;
258        /// Scroll Lock Led
259        const SCROLLLOCK = ffi::libinput_led_LIBINPUT_LED_SCROLL_LOCK;
260        /// Compose Key Led
261        #[cfg(feature = "libinput_1_26")]
262        const COMPOSE = ffi::libinput_led_LIBINPUT_LED_COMPOSE;
263        /// Kana Key Led
264        #[cfg(feature = "libinput_1_26")]
265        const KANA = ffi::libinput_led_LIBINPUT_LED_KANA;
266    }
267}
268
269/// Describes a rectangle to configure a device’s area, see [`Device::config_area_set_rectangle`].
270///
271/// This struct describes a rectangle via the upper left points (x1, y1) and the lower right point (x2, y2).
272///
273/// All arguments are normalized to the range [0.0, 1.0] to represent the corresponding proportion of the device’s width and height, respectively. A rectangle covering the whole device thus comprises of the points (0.0, 0.0) and (1.0, 1.0).
274///
275/// The conditions x1 < x2 and y1 < y2 must be true.
276#[derive(Debug, Copy, Clone, PartialEq)]
277#[cfg(feature = "libinput_1_27")]
278pub struct AreaRectangle {
279    /// x1 coordinate
280    pub x1: f64,
281    /// y1 coordinate
282    pub y1: f64,
283    /// x2 coordinate
284    pub x2: f64,
285    /// y2 coordinate
286    pub y2: f64,
287}
288
289#[cfg(feature = "libinput_1_27")]
290impl From<AreaRectangle> for ffi::libinput_config_area_rectangle {
291    fn from(rect: AreaRectangle) -> Self {
292        input_sys::libinput_config_area_rectangle {
293            x1: rect.x1,
294            y1: rect.y1,
295            x2: rect.x2,
296            y2: rect.y2,
297        }
298    }
299}
300
301#[cfg(feature = "libinput_1_27")]
302impl From<ffi::libinput_config_area_rectangle> for AreaRectangle {
303    fn from(rect: ffi::libinput_config_area_rectangle) -> Self {
304        AreaRectangle {
305            x1: rect.x1,
306            y1: rect.y1,
307            x2: rect.x2,
308            y2: rect.y2,
309        }
310    }
311}
312
313ffi_ref_struct!(
314/// Device group
315///
316/// Some physical devices like graphics tablets are represented by
317/// multiple kernel devices and thus by multiple `Device`s.
318///
319/// libinput assigns these devices to the same `DeviceGroup` allowing
320/// the caller to identify such devices and adjust configuration
321/// settings accordingly. For example, setting a tablet to left-handed
322/// often means turning it upside down. A touch device on the same
323/// tablet would need to be turned upside down too to work correctly.
324///
325/// All devices are part of a device group though for most devices the
326/// group will be a singleton. A device is assigned to a device group
327/// on `DeviceAddedEvent` and removed from that group on
328/// `DeviceRemovedEvent`. It is up to the caller to track how many
329/// devices are in each device group.
330///
331/// Device groups do not get re-used once the last device in the group
332/// was removed, i.e. unplugging and re-plugging a physical device
333/// with grouped devices will return a different device group after
334/// every unplug.
335///
336/// Device groups are assigned based on the LIBINPUT_DEVICE_GROUP udev
337/// property, see [Static device configuration](https://wayland.freedesktop.org/libinput/doc/latest/udev_config.html) via udev.
338struct DeviceGroup, ffi::libinput_device_group, ffi::libinput_device_group_ref, ffi::libinput_device_group_unref);
339
340ffi_ref_struct!(
341/// Representation of a single input device as seen by the kernel.
342///
343/// A single physical device might consist out of multiple input
344/// devices like a keyboard-touchpad combination. See `DeviceGroup`
345/// if you want to track such combined physical devices.
346struct Device, ffi::libinput_device, ffi::libinput_device_ref, ffi::libinput_device_unref);
347
348impl Device {
349    /// Get the libinput context from the device.
350    pub fn context(&self) -> Libinput {
351        self.context.clone()
352    }
353
354    /// Get the device group this device is assigned to.
355    ///
356    /// Some physical devices like graphics tablets are represented by
357    /// multiple kernel devices and thus by multiple `Device`s.
358    ///
359    /// libinput assigns these devices to the same `DeviceGroup`
360    /// allowing the caller to identify such devices and adjust
361    /// configuration settings accordingly. For example, setting a
362    /// tablet to left-handed often means turning it upside down. A
363    /// touch device on the same tablet would need to be turned upside
364    /// down too to work correctly.
365    ///
366    /// All devices are part of a device group though for most devices
367    /// the group will be a singleton. A device is assigned to a
368    /// device group on `DeviceAddedEvent` and removed from that group
369    /// on `DeviceRemovedEvent`. It is up to the caller to track how
370    /// many devices are in each device group.
371    ///
372    /// Device groups do not get re-used once the last device in the
373    /// group was removed, i.e. unplugging and re-plugging a physical
374    /// device with grouped devices will return a different device
375    /// group after every unplug.
376    ///
377    /// Device groups are assigned based on the `LIBINPUT_DEVICE_GROUP`
378    /// udev property, see [Static device configuration](https://wayland.freedesktop.org/libinput/doc/latest/udev_config.html) via udev.
379    pub fn device_group(&self) -> DeviceGroup {
380        unsafe {
381            DeviceGroup::from_raw(
382                ffi::libinput_device_get_device_group(self.as_raw_mut()),
383                &self.context,
384            )
385        }
386    }
387
388    /// Get the system name of the device.
389    ///
390    /// To get the descriptive device name, use `name`.
391    pub fn sysname(&self) -> &str {
392        unsafe {
393            CStr::from_ptr(ffi::libinput_device_get_sysname(self.as_raw_mut()))
394                .to_str()
395                .expect("Device sysname is no valid utf8")
396        }
397    }
398
399    /// The descriptive device name as advertised by the kernel and/or
400    /// the hardware itself.
401    ///
402    /// To get the sysname for this device, use `sysname`.
403    pub fn name(&self) -> Cow<'_, str> {
404        unsafe {
405            CStr::from_ptr(ffi::libinput_device_get_name(self.as_raw_mut())).to_string_lossy()
406        }
407    }
408
409    /// A device may be mapped to a single output, or all available
410    /// outputs.
411    ///
412    /// If a device is mapped to a single output only, a relative
413    /// device may not move beyond the boundaries of this output. An
414    /// absolute device has its input coordinates mapped to the
415    /// extents of this output.
416    pub fn output_name(&self) -> Option<Cow<'_, str>> {
417        unsafe {
418            let ptr = ffi::libinput_device_get_output_name(self.as_raw_mut());
419            if !ptr.is_null() {
420                Some(CStr::from_ptr(ptr).to_string_lossy())
421            } else {
422                None
423            }
424        }
425    }
426
427    ffi_func!(
428    /// Get the product ID for this device.
429    pub fn id_product, ffi::libinput_device_get_id_product, u32);
430
431    #[cfg(feature = "libinput_1_26")]
432    ffi_func!(
433    /// Get the bus type ID for this device.
434    pub fn id_bustype, ffi::libinput_device_get_id_bustype, u32);
435
436    ffi_func!(
437    /// Get the vendor ID for this device.
438    pub fn id_vendor, ffi::libinput_device_get_id_vendor, u32);
439
440    /// Get the seat associated with this input device, see
441    /// [Seats](https://wayland.freedesktop.org/libinput/doc/latest/seats.html)
442    /// for details.
443    ///
444    /// A seat can be uniquely identified by the physical and logical
445    /// seat name. There will ever be only one seat instance with a
446    /// given physical and logical seat name pair at any given time,
447    /// but if no external reference is kept, it may be destroyed if
448    /// no device belonging to it is left.
449    pub fn seat(&self) -> Seat {
450        unsafe {
451            Seat::from_raw(
452                ffi::libinput_device_get_seat(self.as_raw_mut()),
453                &self.context,
454            )
455        }
456    }
457
458    /// Change the logical seat associated with this device by removing the device and adding it to the new seat.
459    ///
460    /// This command is identical to physically unplugging the device,
461    /// then re-plugging it as a member of the new seat. libinput will
462    /// generate a `DeviceRemovedEvent` event and this `Device` is
463    /// considered removed from the context; it will not generate
464    /// further events and will be freed when it goes out of scope.
465    /// A `DeviceAddedEvent` event is generated with a new `Device`
466    /// handle. It is the caller's responsibility to update references
467    /// to the new device accordingly.
468    ///
469    /// If the logical seat name already exists in the device's
470    /// physical seat, the device is added to this seat. Otherwise, a
471    /// new seat is created.
472    ///
473    /// ## Note
474    /// This change applies to this device until removal or `suspend`,
475    /// whichever happens earlier.
476    pub fn set_seat_logical_name(&mut self, name: &str) -> Result<(), ()> {
477        let name = CString::new(name).expect("New logical_seat name contained a null-byte");
478        unsafe {
479            if ffi::libinput_device_set_seat_logical_name(self.as_raw_mut(), name.as_ptr()) == 0 {
480                Ok(())
481            } else {
482                Err(())
483            }
484        }
485    }
486
487    /// Return a udev handle to the device that is this libinput
488    /// device, if any.
489    ///
490    /// Some devices may not have a udev device, or the udev device
491    /// may be unobtainable. This function returns `None` if no udev
492    /// device was available.
493    ///
494    /// Calling this function multiple times for the same device may
495    /// not return the same udev handle each time.
496    ///
497    /// # Safety
498    ///
499    /// The result of this function is not definied if the passed udev `Context`
500    /// is not the same as the one the libinput `Context` was created from.
501    #[cfg(feature = "udev")]
502    pub unsafe fn udev_device(&self) -> Option<UdevDevice> {
503        let dev: *mut udev_device = ffi::libinput_device_get_udev_device(self.ffi) as *mut _;
504        if dev.is_null() {
505            None
506        } else {
507            // We have to ref the returned udev context as udev_device_get_udev does not
508            // increase the ref_count but dropping a UdevDevice will unref it
509            let ctx: *mut udev_context = udev_ref(udev_device_get_udev(dev));
510            Some(UdevDevice::from_raw_with_context(ctx, dev))
511        }
512    }
513
514    /// Update the LEDs on the device, if any.
515    ///
516    /// If the device does not have LEDs, or does not have one or more
517    /// of the LEDs given in the mask, this function does nothing.
518    ///
519    /// ## Arguments
520    ///
521    /// leds: Leds to turn on
522    ///
523    /// Missing `Led`s will be turned off.
524    /// The slice is interpreted as a bitmap.
525    pub fn led_update(&mut self, leds: Led) {
526        unsafe { ffi::libinput_device_led_update(self.as_raw_mut(), leds.bits()) }
527    }
528
529    /// Check if the given device has the specified capability.
530    pub fn has_capability(&self, cap: DeviceCapability) -> bool {
531        unsafe {
532            ffi::libinput_device_has_capability(
533                self.as_raw_mut(),
534                match cap {
535                    DeviceCapability::Keyboard => {
536                        ffi::libinput_device_capability_LIBINPUT_DEVICE_CAP_KEYBOARD
537                    }
538                    DeviceCapability::Pointer => {
539                        ffi::libinput_device_capability_LIBINPUT_DEVICE_CAP_POINTER
540                    }
541                    DeviceCapability::Touch => {
542                        ffi::libinput_device_capability_LIBINPUT_DEVICE_CAP_TOUCH
543                    }
544                    DeviceCapability::TabletTool => {
545                        ffi::libinput_device_capability_LIBINPUT_DEVICE_CAP_TABLET_TOOL
546                    }
547                    DeviceCapability::TabletPad => {
548                        ffi::libinput_device_capability_LIBINPUT_DEVICE_CAP_TABLET_PAD
549                    }
550                    DeviceCapability::Gesture => {
551                        ffi::libinput_device_capability_LIBINPUT_DEVICE_CAP_GESTURE
552                    }
553                    DeviceCapability::Switch => {
554                        ffi::libinput_device_capability_LIBINPUT_DEVICE_CAP_SWITCH
555                    }
556                },
557            ) != 0
558        }
559    }
560
561    /// Get the physical size of a device in mm, where meaningful.
562    ///
563    /// This function only succeeds on devices with the required data,
564    /// i.e. tablets, touchpads and touchscreens.
565    pub fn size(&self) -> Option<(f64, f64)> {
566        let mut width = 0.0;
567        let mut height = 0.0;
568
569        match unsafe {
570            ffi::libinput_device_get_size(
571                self.as_raw_mut(),
572                &mut width as *mut _,
573                &mut height as *mut _,
574            )
575        } {
576            0 => Some((width, height)),
577            _ => None,
578        }
579    }
580
581    /// Check if a `DeviceCapability::Pointer` device has a button
582    /// with the given code (see linux/input.h).
583    pub fn pointer_has_button(&self, button: u32) -> Result<bool, ()> {
584        match unsafe { ffi::libinput_device_pointer_has_button(self.as_raw_mut(), button) } {
585            1 => Ok(true),
586            0 => Ok(false),
587            -1 => Err(()),
588            _ => unreachable!(),
589        }
590    }
591
592    /// Check if a `DeviceCapability::Keyboard` device has a key with
593    /// the given code (see linux/input.h).
594    pub fn keyboard_has_key(&self, key: u32) -> Result<bool, ()> {
595        match unsafe { ffi::libinput_device_keyboard_has_key(self.as_raw_mut(), key) } {
596            1 => Ok(true),
597            0 => Ok(false),
598            -1 => Err(()),
599            _ => unreachable!(),
600        }
601    }
602
603    /// Check if a `DeviceCapability::Switch` device has a switch of the
604    /// given type.
605    pub fn switch_has_switch(&self, switch: Switch) -> Result<bool, ()> {
606        match unsafe { ffi::libinput_device_switch_has_switch(self.as_raw_mut(), switch as u32) } {
607            1 => Ok(true),
608            0 => Ok(false),
609            -1 => Err(()),
610            _ => unreachable!(),
611        }
612    }
613
614    ffi_func!(
615    /// Return the number of buttons on a device with the
616    /// `DeviceCapability::TabletPad` capability.
617    ///
618    /// Buttons on a pad device are numbered sequentially, see Tablet
619    /// pad button numbers for details.
620    pub fn tablet_pad_number_of_buttons, ffi::libinput_device_tablet_pad_get_num_buttons, i32);
621    ffi_func!(
622    /// Return the number of rings a device with the
623    /// `DeviceCapability::TabletPad` capability provides.
624    pub fn tablet_pad_number_of_rings, ffi::libinput_device_tablet_pad_get_num_rings, i32);
625    ffi_func!(
626    /// Return the number of strips a device with the
627    /// `DeviceCapability::TabletPad` capability provides.
628    pub fn tablet_pad_number_of_strips, ffi::libinput_device_tablet_pad_get_num_strips, i32);
629    ffi_func!(
630    /// Most devices only provide a single mode group, however devices
631    /// such as the Wacom Cintiq 22HD provide two mode groups.
632    ///
633    /// If multiple mode groups are available, a caller should use
634    /// `TabletPadModeGroup::has_button`,
635    /// `TabletPadModeGroup::has_ring`,
636    /// `TabletPadModeGroup::has_dial` (since libinput 1.26.0) and
637    /// `TabletPadModeGroup::has_strip()` to associate each button,
638    /// ring and strip with the correct mode group.
639    pub fn tablet_pad_number_of_mode_groups, ffi::libinput_device_tablet_pad_get_num_mode_groups, i32);
640
641    /// Return the current mode this mode group is in.
642    ///
643    /// Note that the returned mode is the mode valid as of completing
644    /// the last `dispatch`. The returned mode may thus be
645    /// different than the mode returned by
646    /// `TabletPadEventTrait::mode`.
647    ///
648    /// For example, if the mode was toggled three times between the
649    /// call to `dispatch`, this function returns the third mode but
650    /// the events in the event queue will return the modes 1, 2 and
651    /// 3, respectively.
652    pub fn tablet_pad_mode_group(&self, index: u32) -> Option<TabletPadModeGroup> {
653        let ptr =
654            unsafe { ffi::libinput_device_tablet_pad_get_mode_group(self.as_raw_mut(), index) };
655        if ptr.is_null() {
656            None
657        } else {
658            Some(unsafe { TabletPadModeGroup::from_raw(ptr, &self.context) })
659        }
660    }
661
662    /// Check if a `DeviceCapability::TabletPad`-device has a key with the given code (see linux/input-event-codes.h).
663    ///
664    /// ## Returns
665    /// - `Some(true)` if it has the requested key
666    /// - `Some(false)` if it has not
667    /// - `None` on error (no TabletPad device)
668    #[cfg(feature = "libinput_1_15")]
669    pub fn tablet_pad_has_key(&self, code: u32) -> Option<bool> {
670        match unsafe { ffi::libinput_device_tablet_pad_has_key(self.as_raw_mut(), code) } {
671            -1 => None,
672            0 => Some(false),
673            1 => Some(true),
674            _ => panic!(
675                "libinput returned invalid return code for libinput_device_tablet_pad_has_key"
676            ),
677        }
678    }
679
680    /// Check how many touches a `DeviceCapability::Touch`-exposing Device supports simultaneously.
681    ///
682    /// ## Returns
683    /// - `Some(n)` amount of touches
684    /// - `Some(0)` if unknown
685    /// - `None` on error (no touch device)
686    #[cfg(feature = "libinput_1_11")]
687    pub fn touch_count(&mut self) -> Option<u32> {
688        match unsafe { ffi::libinput_device_touch_get_touch_count(self.as_raw_mut()) } {
689            -1 => None,
690            n => Some(n as u32),
691        }
692    }
693
694    /// Apply this pointer acceleration configuration to the device.
695    ///
696    /// This changes the device’s pointer acceleration method to the method
697    /// given in [`AccelConfig::new`] and applies all other configuration
698    /// settings.
699    #[cfg(feature = "libinput_1_23")]
700    pub fn config_accel_apply(&self, accel_config: AccelConfig) -> DeviceConfigResult {
701        match unsafe {
702            ffi::libinput_device_config_accel_apply(self.as_raw_mut(), accel_config.as_raw_mut())
703        } {
704            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
705            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
706                Err(DeviceConfigError::Unsupported)
707            }
708            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
709                Err(DeviceConfigError::Invalid)
710            }
711            _ => panic!("libinput returned invalid 'libinput_config_status'"),
712        }
713    }
714
715    /// Return the default pointer acceleration profile for this
716    /// pointer device.
717    pub fn config_accel_default_profile(&self) -> Option<AccelProfile> {
718        match unsafe { ffi::libinput_device_config_accel_get_default_profile(self.as_raw_mut()) } {
719            ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_NONE => None,
720            ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT => {
721                Some(AccelProfile::Flat)
722            }
723            ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE => {
724                Some(AccelProfile::Adaptive)
725            }
726            #[cfg(feature = "libinput_1_23")]
727            ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM => {
728                Some(AccelProfile::Custom)
729            }
730            _x => {
731                #[cfg(feature = "log")]
732                log::warn!(
733                    "Unknown AccelProfile ({}). Unsupported libinput version?",
734                    _x
735                );
736                None
737            }
738        }
739    }
740
741    /// Get the current pointer acceleration profile for this pointer
742    /// device.
743    pub fn config_accel_profile(&self) -> Option<AccelProfile> {
744        match unsafe { ffi::libinput_device_config_accel_get_profile(self.as_raw_mut()) } {
745            ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_NONE => None,
746            ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT => {
747                Some(AccelProfile::Flat)
748            }
749            ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE => {
750                Some(AccelProfile::Adaptive)
751            }
752            #[cfg(feature = "libinput_1_23")]
753            ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM => {
754                Some(AccelProfile::Custom)
755            }
756            _x => {
757                #[cfg(feature = "log")]
758                log::warn!(
759                    "Unknown AccelProfile ({}). Unsupported libinput version?",
760                    _x
761                );
762                None
763            }
764        }
765    }
766
767    /// Returns a bitmask of the configurable acceleration modes
768    /// available on this device.
769    pub fn config_accel_profiles(&self) -> Vec<AccelProfile> {
770        let mut profiles = Vec::new();
771        let bitmask = unsafe { ffi::libinput_device_config_accel_get_profiles(self.as_raw_mut()) };
772        if bitmask & ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT as u32
773            != 0
774        {
775            profiles.push(AccelProfile::Flat);
776        }
777        if bitmask
778            & ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE as u32
779            != 0
780        {
781            profiles.push(AccelProfile::Adaptive);
782        }
783        #[cfg(feature = "libinput_1_23")]
784        if bitmask & ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM as u32
785            != 0
786        {
787            profiles.push(AccelProfile::Custom);
788        }
789        profiles
790    }
791
792    /// Set the pointer acceleration profile of this pointer device to
793    /// the given mode.
794    pub fn config_accel_set_profile(&mut self, profile: AccelProfile) -> DeviceConfigResult {
795        match unsafe {
796            ffi::libinput_device_config_accel_set_profile(
797                self.as_raw_mut(),
798                match profile {
799                    AccelProfile::Flat => {
800                        ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
801                    }
802                    AccelProfile::Adaptive => {
803                        ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
804                    }
805                    #[cfg(feature = "libinput_1_23")]
806                    AccelProfile::Custom => {
807                        ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM
808                    }
809                },
810            )
811        } {
812            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
813            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
814                Err(DeviceConfigError::Unsupported)
815            }
816            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
817                Err(DeviceConfigError::Invalid)
818            }
819            _ => panic!("libinput returned invalid 'libinput_config_status'"),
820        }
821    }
822
823    ffi_func!(
824    /// Return the default speed setting for this device, normalized
825    /// to a range of [-1, 1].
826    pub fn config_accel_default_speed, ffi::libinput_device_config_accel_get_default_speed, f64);
827    ffi_func!(
828    /// Get the current pointer acceleration setting for this pointer
829    /// device.
830    ///
831    /// The returned value is normalized to a range of [-1, 1]. See
832    /// `config_accel_set_speed` for details.
833    pub fn config_accel_speed, ffi::libinput_device_config_accel_get_speed, f64);
834
835    /// Set the pointer acceleration speed of this pointer device
836    /// within a range of [-1, 1], where 0 is the default acceleration
837    /// for this device, -1 is the slowest acceleration and 1 is the
838    /// maximum acceleration available on this device.
839    ///
840    /// The actual pointer acceleration mechanism is
841    /// implementation-dependent, as is the number of steps available
842    /// within the range. libinput picks the semantically closest
843    /// acceleration step if the requested value does not match a
844    /// discrete setting.
845    pub fn config_accel_set_speed(&mut self, speed: f64) -> DeviceConfigResult {
846        match unsafe { ffi::libinput_device_config_accel_set_speed(self.as_raw_mut(), speed) } {
847            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
848            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
849                Err(DeviceConfigError::Unsupported)
850            }
851            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
852                Err(DeviceConfigError::Invalid)
853            }
854            _ => panic!("libinput returned invalid 'libinput_config_status'"),
855        }
856    }
857
858    ffi_func!(
859    /// Check if a device uses libinput-internal pointer-acceleration.
860    pub fn config_accel_is_available, ffi::libinput_device_config_accel_is_available, bool);
861
862    /// Return the default calibration matrix for this device.
863    ///
864    /// On most devices, this is the identity matrix. If the udev
865    /// property `LIBINPUT_CALIBRATION_MATRIX` is set on the respective u
866    /// dev device, that property's value becomes the default matrix,
867    /// see [Static device configuration via udev](https://wayland.freedesktop.org/libinput/doc/latest/udev_config.html).
868    pub fn config_calibration_default_matrix(&self) -> Option<[f32; 6]> {
869        let mut matrix = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
870        if unsafe {
871            ffi::libinput_device_config_calibration_get_default_matrix(
872                self.as_raw_mut(),
873                matrix.as_mut_ptr(),
874            ) != 0
875        } {
876            Some(matrix)
877        } else {
878            None
879        }
880    }
881
882    /// Return the current calibration matrix for this device.
883    pub fn config_calibration_matrix(&self) -> Option<[f32; 6]> {
884        let mut matrix = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
885        if unsafe {
886            ffi::libinput_device_config_calibration_get_matrix(
887                self.as_raw_mut(),
888                matrix.as_mut_ptr(),
889            ) != 0
890        } {
891            Some(matrix)
892        } else {
893            None
894        }
895    }
896
897    ffi_func!(
898    /// Check if the device can be calibrated via a calibration matrix.
899    pub fn config_calibration_has_matrix, ffi::libinput_device_config_calibration_has_matrix, bool);
900
901    #[cfg(feature = "libinput_1_27")]
902    ffi_func!(
903    /// Check if the device can change its logical input area via a rectangle.
904    pub fn config_area_has_rectangle, ffi::libinput_device_config_area_has_rectangle, bool);
905
906    /// Set the given rectangle as the logical input area of this device.
907    ///
908    /// Future interactions by a tablet tool on this devices are scaled to only
909    /// consider events within this logical input area - as if the logical input
910    /// area were the available physical area.
911    ///
912    /// The coordinates of the rectangle represent the proportion of the
913    /// available maximum physical area, normalized to the range [0.0, 1.0].
914    /// For example, a rectangle with the two points 0.25, 0.5, 0.75, 1.0 adds
915    /// a 25% dead zone to the left and right and a 50% dead zone on the top:
916    ///
917    /// ```text
918    /// +----------------------------------+
919    /// |                                  |
920    /// |                50%               |
921    /// |                                  |
922    /// |        +-----------------+       |
923    /// |        |                 |       |
924    /// |   25%  |                 |  25%  |
925    /// |        |                 |       |
926    /// +--------+-----------------+-------+
927    /// ```
928    /// The area applies in the tablet’s current logical rotation, i.e. the
929    /// above example is always at the bottom of the tablet.
930    ///
931    /// Once applied, the logical area’s top-left coordinate (in the current
932    /// logical rotation) becomes the new offset (0/0) and the return values
933    /// of [`TabletToolEventTrait::x`](crate::event::tablet_tool::TabletToolEventTrait::x) and
934    /// [`TabletToolEventTrait::y`](crate::event::tablet_tool::TabletToolEventTrait::y)
935    /// are in relation to this new offset.
936    ///
937    /// Likewise, [`TabletToolEventTrait::x_transformed`](crate::event::tablet_tool::TabletToolEventTrait::x_transformed)
938    /// and [`TabletToolEventTrait::y_transformed`](crate::event::tablet_tool::TabletToolEventTrait::y_transformed)
939    /// represent the value scaled into the configured logical area.
940    ///
941    /// The return value of libinput_device_get_size() is not affected by the configured area.
942    ///
943    /// Changing the area may not take effect immediately, the device may wait until it is in a
944    /// neutral state before applying any changes.
945    #[cfg(feature = "libinput_1_27")]
946    pub fn config_area_set_rectangle(&self, area: AreaRectangle) -> DeviceConfigResult {
947        let area = area.into();
948        match unsafe {
949            ffi::libinput_device_config_area_set_rectangle(self.as_raw_mut(), &raw const area)
950        } {
951            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
952            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
953                Err(DeviceConfigError::Unsupported)
954            }
955            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
956                Err(DeviceConfigError::Invalid)
957            }
958            _ => panic!("libinput returned invalid 'libinput_config_status'"),
959        }
960    }
961
962    /// Return the current area rectangle for this device.
963    ///
964    /// The return value for a device that does not support area rectangles is
965    /// a rectangle with the points 0/0 and 1/1.
966    #[cfg(feature = "libinput_1_27")]
967    pub fn config_area_get_rectangle(&self) -> AreaRectangle {
968        unsafe { ffi::libinput_device_config_area_get_rectangle(self.as_raw_mut()) }.into()
969    }
970
971    /// Return the default area rectangle for this device.
972    ///
973    /// The return value for a device that does not support area rectangles is
974    /// a rectangle with the points 0/0 and 1/1.
975    #[cfg(feature = "libinput_1_27")]
976    pub fn config_area_get_default_rectangle(&self) -> AreaRectangle {
977        unsafe { ffi::libinput_device_config_area_get_default_rectangle(self.as_raw_mut()) }.into()
978    }
979
980    /// Apply the 3x3 transformation matrix to absolute device
981    /// coordinates.
982    ///
983    /// This matrix has no effect on relative events.
984    ///
985    /// Given a 6-element array [a, b, c, d, e, f], the matrix is
986    /// applied as
987    /// ```text
988    /// [ a  b  c ]   [ x ]
989    /// [ d  e  f ] * [ y ]
990    /// [ 0  0  1 ]   [ 1 ]
991    /// # *
992    /// ```
993    /// The translation component (c, f) is expected to be normalized
994    /// to the device coordinate range. For example, the matrix
995    /// ```text
996    /// [ 1 0  1 ]
997    /// [ 0 1 -1 ]
998    /// [ 0 0  1 ]
999    /// ```
1000    /// moves all coordinates by 1 device-width to the right and 1
1001    /// device-height up.
1002    ///
1003    /// The rotation matrix for rotation around the origin is defined
1004    /// as
1005    /// ```text
1006    /// [ cos(a) -sin(a) 0 ]
1007    /// [ sin(a)  cos(a) 0 ]
1008    /// [   0      0     1 ]
1009    /// ```
1010    /// Note that any rotation requires an additional translation
1011    /// component to translate the rotated coordinates back into the
1012    /// original device space. The rotation matrixes for 90, 180 and
1013    /// 270 degrees clockwise are:
1014    /// ```text
1015    /// 90 deg cw:              180 deg cw:             270 deg cw:
1016    /// [ 0 -1 1]               [ -1  0 1]              [  0 1 0 ]
1017    /// [ 1  0 0]               [  0 -1 1]              [ -1 0 1 ]
1018    /// [ 0  0 1]               [  0  0 1]              [  0 0 1 ]
1019    /// ```
1020    pub fn config_calibration_set_matrix(&mut self, matrix: [f32; 6]) -> DeviceConfigResult {
1021        match unsafe {
1022            ffi::libinput_device_config_calibration_set_matrix(self.as_raw_mut(), matrix.as_ptr())
1023        } {
1024            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1025            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1026                Err(DeviceConfigError::Unsupported)
1027            }
1028            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1029                Err(DeviceConfigError::Invalid)
1030            }
1031            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1032        }
1033    }
1034
1035    /// Get the default button click method for this device.
1036    ///
1037    /// The button click method defines when to generate
1038    /// software-emulated buttons, usually on a device that does not
1039    /// have a specific physical button available.
1040    pub fn config_click_default_method(&self) -> Option<ClickMethod> {
1041        match unsafe { ffi::libinput_device_config_click_get_default_method(self.as_raw_mut()) } {
1042            ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_NONE => None,
1043            ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS => {
1044                Some(ClickMethod::ButtonAreas)
1045            }
1046            ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER => {
1047                Some(ClickMethod::Clickfinger)
1048            }
1049            _x => {
1050                #[cfg(feature = "log")]
1051                log::warn!(
1052                    "Unknown ClickMethod ({}). Unsupported libinput version?",
1053                    _x
1054                );
1055                None
1056            }
1057        }
1058    }
1059
1060    /// Get the button click method for this device.
1061    ///
1062    /// The button click method defines when to generate
1063    /// software-emulated buttons, usually on a device that does not
1064    /// have a specific physical button available.
1065    pub fn config_click_method(&self) -> Option<ClickMethod> {
1066        match unsafe { ffi::libinput_device_config_click_get_method(self.as_raw_mut()) } {
1067            ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_NONE => None,
1068            ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS => {
1069                Some(ClickMethod::ButtonAreas)
1070            }
1071            ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER => {
1072                Some(ClickMethod::Clickfinger)
1073            }
1074            _x => {
1075                #[cfg(feature = "log")]
1076                log::warn!(
1077                    "Unknown ClickMethod ({}). Unsupported libinput version?",
1078                    _x
1079                );
1080                None
1081            }
1082        }
1083    }
1084
1085    /// Check which button click methods a device supports.
1086    ///
1087    /// The button click method defines when to generate
1088    /// software-emulated buttons, usually on a device that does not
1089    /// have a specific physical button available.
1090    pub fn config_click_methods(&self) -> Vec<ClickMethod> {
1091        let mut methods = Vec::new();
1092        let bitmask = unsafe { ffi::libinput_device_config_click_get_methods(self.as_raw_mut()) };
1093        if bitmask
1094            & ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER as u32
1095            != 0
1096        {
1097            methods.push(ClickMethod::Clickfinger);
1098        }
1099        if bitmask
1100            & ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS as u32
1101            != 0
1102        {
1103            methods.push(ClickMethod::ButtonAreas);
1104        }
1105        methods
1106    }
1107
1108    /// Set the button click method for this device.
1109    ///
1110    /// The button click method defines when to generate
1111    /// software-emulated buttons, usually on a device that does not
1112    /// have a specific physical button available.
1113    ///
1114    /// ## Note
1115    ///
1116    /// The selected click method may not take effect immediately. The
1117    /// device may require changing to a neutral state first before
1118    /// activating the new method.
1119    pub fn config_click_set_method(&mut self, method: ClickMethod) -> DeviceConfigResult {
1120        match unsafe {
1121            ffi::libinput_device_config_click_set_method(
1122                self.as_raw_mut(),
1123                match method {
1124                    ClickMethod::ButtonAreas => {
1125                        ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
1126                    }
1127                    ClickMethod::Clickfinger => {
1128                        ffi::libinput_config_click_method_LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
1129                    }
1130                },
1131            )
1132        } {
1133            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1134            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1135                Err(DeviceConfigError::Unsupported)
1136            }
1137            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1138                Err(DeviceConfigError::Invalid)
1139            }
1140            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1141        }
1142    }
1143
1144    /// Get the finger number to button number mapping for clickfinger.
1145    ///
1146    /// The return value for a device that does not support clickfinger is
1147    /// always [`ClickfingerButtonMap::LeftRightMiddle`].
1148    #[cfg(feature = "libinput_1_26")]
1149    pub fn config_click_clickfinger_default_button_map(&self) -> ClickfingerButtonMap {
1150        match unsafe {
1151            ffi::libinput_device_config_click_get_default_clickfinger_button_map(self.as_raw_mut())
1152        } {
1153            ffi::libinput_config_clickfinger_button_map_LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM => {
1154                ClickfingerButtonMap::LeftRightMiddle
1155            }
1156            ffi::libinput_config_clickfinger_button_map_LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR => {
1157                ClickfingerButtonMap::LeftMiddleRight
1158            }
1159            _ => panic!("libinput returned invalid 'libinput_config_tap_button_map'"),
1160        }
1161    }
1162
1163    /// Get the default finger number to button number mapping for clickfinger.
1164    ///
1165    /// The return value for a device that does not support clickfinger is
1166    /// always [`ClickfingerButtonMap::LeftRightMiddle`].
1167    #[cfg(feature = "libinput_1_26")]
1168    pub fn config_click_clickfinger_button_map(&self) -> ClickfingerButtonMap {
1169        match unsafe {
1170            ffi::libinput_device_config_click_get_clickfinger_button_map(self.as_raw_mut())
1171        } {
1172            ffi::libinput_config_clickfinger_button_map_LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM => {
1173                ClickfingerButtonMap::LeftRightMiddle
1174            }
1175            ffi::libinput_config_clickfinger_button_map_LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR => {
1176                ClickfingerButtonMap::LeftMiddleRight
1177            }
1178            _ => panic!("libinput returned invalid 'libinput_config_tap_button_map'"),
1179        }
1180    }
1181
1182    /// Set the finger number to button number mapping for clickfinger.
1183    ///
1184    /// The default mapping on most devices is to have a 1, 2 and 3 finger tap
1185    /// to map to the left, right and middle button, respectively. A device may
1186    /// permit changing the button mapping but disallow specific maps. In this
1187    /// case [`DeviceConfigError::Unsupported`] is returned, the caller is expected
1188    /// to handle this case correctly.
1189    ///
1190    /// Changing the button mapping may not take effect immediately, the device may
1191    /// wait until it is in a neutral state before applying any changes.
1192    #[cfg(feature = "libinput_1_26")]
1193    pub fn config_click_clickfinger_set_button_map(
1194        &self,
1195        map: ClickfingerButtonMap,
1196    ) -> DeviceConfigResult {
1197        match unsafe {
1198            ffi::libinput_device_config_click_set_clickfinger_button_map(self.as_raw_mut(), match map {
1199                ClickfingerButtonMap::LeftRightMiddle => ffi::libinput_config_clickfinger_button_map_LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM,
1200                ClickfingerButtonMap::LeftMiddleRight => ffi::libinput_config_clickfinger_button_map_LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR,
1201            })
1202        } {
1203            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1204            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1205                Err(DeviceConfigError::Unsupported)
1206            }
1207            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1208                Err(DeviceConfigError::Invalid)
1209            }
1210            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1211        }
1212    }
1213
1214    /// Check if the disable-while typing feature is enabled on this
1215    /// device by default.
1216    ///
1217    /// If the device does not support disable-while-typing, this
1218    /// function returns `false`.
1219    pub fn config_dwt_default_enabled(&self) -> bool {
1220        match unsafe { ffi::libinput_device_config_dwt_get_default_enabled(self.as_raw_mut()) } {
1221            ffi::libinput_config_dwt_state_LIBINPUT_CONFIG_DWT_ENABLED => true,
1222            ffi::libinput_config_dwt_state_LIBINPUT_CONFIG_DWT_DISABLED => false,
1223            _ => panic!("libinput returned invalid 'libinput_config_dwt_state'"),
1224        }
1225    }
1226
1227    /// Check if the disable-while typing feature is currently enabled
1228    /// on this device.
1229    ///
1230    /// If the device does not support disable-while-typing, this
1231    /// function returns `false`.
1232    pub fn config_dwt_enabled(&self) -> bool {
1233        match unsafe { ffi::libinput_device_config_dwt_get_enabled(self.as_raw_mut()) } {
1234            ffi::libinput_config_dwt_state_LIBINPUT_CONFIG_DWT_ENABLED => true,
1235            ffi::libinput_config_dwt_state_LIBINPUT_CONFIG_DWT_DISABLED => false,
1236            _ => panic!("libinput returned invalid 'libinput_config_dwt_state'"),
1237        }
1238    }
1239
1240    ffi_func!(
1241    /// Check if this device supports configurable
1242    /// disable-while-typing feature.
1243    ///
1244    /// This feature is usually available on built-in touchpads and
1245    /// disables the touchpad while typing. See [Disable-while-typing](https://wayland.freedesktop.org/libinput/doc/latest/palm_detection.html#disable-while-typing)
1246    /// for details.
1247    pub fn config_dwt_is_available, ffi::libinput_device_config_dwt_is_available, bool);
1248
1249    /// Enable or disable the disable-while-typing feature.
1250    ///
1251    /// When enabled, the device will be disabled while typing and
1252    /// for a short period after. See Disable-while-typing for
1253    /// details.
1254    ///
1255    /// ## Note
1256    ///
1257    /// Enabling or disabling disable-while-typing may not take
1258    /// effect immediately.
1259    pub fn config_dwt_set_enabled(&self, enabled: bool) -> DeviceConfigResult {
1260        match unsafe {
1261            ffi::libinput_device_config_dwt_set_enabled(
1262                self.as_raw_mut(),
1263                if enabled {
1264                    ffi::libinput_config_dwt_state_LIBINPUT_CONFIG_DWT_ENABLED
1265                } else {
1266                    ffi::libinput_config_dwt_state_LIBINPUT_CONFIG_DWT_DISABLED
1267                },
1268            )
1269        } {
1270            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1271            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1272                Err(DeviceConfigError::Unsupported)
1273            }
1274            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1275                Err(DeviceConfigError::Invalid)
1276            }
1277            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1278        }
1279    }
1280
1281    /// Check if the disable-while trackpointing feature is enabled on this
1282    /// device by default.
1283    ///
1284    /// If the device does not support disable-while-trackpointing, this
1285    /// function returns `false`.
1286    #[cfg(feature = "libinput_1_21")]
1287    pub fn config_dwtp_default_enabled(&self) -> bool {
1288        match unsafe { ffi::libinput_device_config_dwtp_get_default_enabled(self.as_raw_mut()) } {
1289            ffi::libinput_config_dwtp_state_LIBINPUT_CONFIG_DWTP_ENABLED => true,
1290            ffi::libinput_config_dwtp_state_LIBINPUT_CONFIG_DWTP_DISABLED => false,
1291            _ => panic!("libinput returned invalid 'libinput_config_dwtp_state'"),
1292        }
1293    }
1294
1295    /// Check if the disable-while trackpointing feature is currently enabled
1296    /// on this device.
1297    ///
1298    /// If the device does not support disable-while-trackpointing, this
1299    /// function returns `false`.
1300    #[cfg(feature = "libinput_1_21")]
1301    pub fn config_dwtp_enabled(&self) -> bool {
1302        match unsafe { ffi::libinput_device_config_dwtp_get_enabled(self.as_raw_mut()) } {
1303            ffi::libinput_config_dwtp_state_LIBINPUT_CONFIG_DWTP_ENABLED => true,
1304            ffi::libinput_config_dwtp_state_LIBINPUT_CONFIG_DWTP_DISABLED => false,
1305            _ => panic!("libinput returned invalid 'libinput_config_dwtp_state'"),
1306        }
1307    }
1308
1309    ffi_func!(
1310    /// Check if this device supports configurable
1311    /// disable-while-trackpointing feature.
1312    ///
1313    /// This feature is usually available on Thinkpads and
1314    /// disables the touchpad while using the trackpoint.
1315    #[cfg(feature = "libinput_1_21")]
1316    pub fn config_dwtp_is_available, ffi::libinput_device_config_dwtp_is_available, bool);
1317
1318    /// Enable or disable the disable-while-trackpointing feature.
1319    ///
1320    /// When enabled, the device will be disabled while using the trackpoint and
1321    /// for a short period after.
1322    ///
1323    /// ## Note
1324    ///
1325    /// Enabling or disabling disable-while-trackpointing may not take
1326    /// effect immediately.
1327    #[cfg(feature = "libinput_1_21")]
1328    pub fn config_dwtp_set_enabled(&self, enabled: bool) -> DeviceConfigResult {
1329        match unsafe {
1330            ffi::libinput_device_config_dwtp_set_enabled(
1331                self.as_raw_mut(),
1332                if enabled {
1333                    ffi::libinput_config_dwtp_state_LIBINPUT_CONFIG_DWTP_ENABLED
1334                } else {
1335                    ffi::libinput_config_dwtp_state_LIBINPUT_CONFIG_DWTP_DISABLED
1336                },
1337            )
1338        } {
1339            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1340            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1341                Err(DeviceConfigError::Unsupported)
1342            }
1343            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1344                Err(DeviceConfigError::Invalid)
1345            }
1346            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1347        }
1348    }
1349
1350    ffi_func!(
1351    /// Get the current left-handed configuration of the device.
1352    pub fn config_left_handed, ffi::libinput_device_config_left_handed_get, bool);
1353    ffi_func!(
1354    /// Get the default left-handed configuration of the device.
1355    pub fn config_left_handed_default, ffi::libinput_device_config_left_handed_get_default, bool);
1356    ffi_func!(
1357    /// Check if a device has a configuration that supports
1358    /// left-handed usage.
1359    pub fn config_left_handed_is_available, ffi::libinput_device_config_left_handed_is_available, bool);
1360
1361    /// Set the left-handed configuration of the device.
1362    ///
1363    /// The exact behavior is device-dependent. On a mouse and most
1364    /// pointing devices, left and right buttons are swapped but the
1365    /// middle button is unmodified. On a touchpad, physical buttons
1366    /// (if present) are swapped. On a clickpad, the top and bottom
1367    /// software-emulated buttons are swapped where present, the main
1368    /// area of the touchpad remains a left button. Tapping and
1369    /// clickfinger behavior is not affected by this setting.
1370    ///
1371    /// Changing the left-handed configuration of a device may not
1372    /// take effect until all buttons have been logically released.
1373    pub fn config_left_handed_set(&self, enabled: bool) -> DeviceConfigResult {
1374        match unsafe {
1375            ffi::libinput_device_config_left_handed_set(self.as_raw_mut(), enabled as i32)
1376        } {
1377            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1378            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1379                Err(DeviceConfigError::Unsupported)
1380            }
1381            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1382                Err(DeviceConfigError::Invalid)
1383            }
1384            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1385        }
1386    }
1387
1388    /// Check if configurable middle button emulation is enabled by
1389    /// default on this device.
1390    ///
1391    /// See [Middle button emulation](https://wayland.freedesktop.org/libinput/doc/latest/middle-button-emulation.html) for details.
1392    ///
1393    /// If the device does not have configurable middle button
1394    /// emulation, this function returns `false`.
1395    ///
1396    /// ## Note
1397    ///
1398    /// Some devices provide middle mouse button emulation but do not
1399    /// allow enabling/disabling that emulation. These devices always
1400    /// return `false`.
1401    pub fn config_middle_emulation_default_enabled(&self) -> bool {
1402        match unsafe {
1403            ffi::libinput_device_config_middle_emulation_get_default_enabled(self.as_raw_mut())
1404        } {
1405            ffi::libinput_config_middle_emulation_state_LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED => true,
1406            ffi::libinput_config_middle_emulation_state_LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED => false,
1407            _ => panic!("libinput returned invalid 'libinput_config_middle_emulation_state'"),
1408        }
1409    }
1410
1411    /// Check if configurable middle button emulation is enabled on
1412    /// this device.
1413    ///
1414    /// See [Middle button emulation](https://wayland.freedesktop.org/libinput/doc/latest/middle-button-emulation.html)
1415    /// for details.
1416    ///
1417    /// If the device does not have configurable middle button
1418    /// emulation, this function returns `false`.
1419    ///
1420    /// ## Note
1421    ///
1422    /// Some devices provide middle mouse button emulation but do not
1423    /// allow enabling/disabling that emulation. These devices always
1424    /// return `false`.
1425    pub fn config_middle_emulation_enabled(&self) -> bool {
1426        match unsafe { ffi::libinput_device_config_middle_emulation_get_enabled(self.as_raw_mut()) } {
1427            ffi::libinput_config_middle_emulation_state_LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED => true,
1428            ffi::libinput_config_middle_emulation_state_LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED => false,
1429            _ => panic!("libinput returned invalid 'libinput_config_middle_emulation_state'"),
1430        }
1431    }
1432
1433    ffi_func!(
1434    /// Check if middle mouse button emulation configuration is
1435    /// available on this device.
1436    ///
1437    /// See [Middle button emulation](https://wayland.freedesktop.org/libinput/doc/latest/middle-button-emulation.html)
1438    /// for details.
1439    ///
1440    /// ## Note
1441    ///
1442    /// Some devices provide middle mouse button emulation but do not
1443    /// allow enabling/disabling that emulation. These devices return
1444    /// `false` in `config_middle_emulation_is_available`.
1445    pub fn config_middle_emulation_is_available, ffi::libinput_device_config_middle_emulation_is_available, bool);
1446
1447    /// Enable or disable middle button emulation on this device.
1448    ///
1449    /// When enabled, a simultaneous press of the left and right
1450    /// button generates a middle mouse button event. Releasing the
1451    /// buttons generates a middle mouse button release, the left and
1452    /// right button events are discarded otherwise.
1453    ///
1454    /// See [Middle button emulation](https://wayland.freedesktop.org/libinput/doc/latest/middle-button-emulation.html)
1455    /// for details.
1456    pub fn config_middle_emulation_set_enabled(&self, enabled: bool) -> DeviceConfigResult {
1457        match unsafe {
1458            ffi::libinput_device_config_middle_emulation_set_enabled(
1459                self.as_raw_mut(),
1460                if enabled {
1461                    ffi::libinput_config_middle_emulation_state_LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED
1462                } else {
1463                    ffi::libinput_config_middle_emulation_state_LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED
1464                },
1465            )
1466        } {
1467            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1468            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1469                Err(DeviceConfigError::Unsupported)
1470            }
1471            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1472                Err(DeviceConfigError::Invalid)
1473            }
1474            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1475        }
1476    }
1477
1478    ffi_func!(
1479    /// Get the current rotation of a device in degrees clockwise off
1480    /// the logical neutral position.
1481    ///
1482    /// If this device does not support rotation, the return value is
1483    /// always 0.
1484    pub fn config_rotation_angle, ffi::libinput_device_config_rotation_get_angle, u32);
1485    ffi_func!(
1486    /// Get the default rotation of a device in degrees clockwise off
1487    /// the logical neutral position.
1488    ///
1489    /// If this device does not support rotation, the return value is
1490    /// always 0.
1491    pub fn config_rotation_default_angle, ffi::libinput_device_config_rotation_get_default_angle, u32);
1492    ffi_func!(
1493    /// Check whether a device can have a custom rotation applied.
1494    pub fn config_rotation_is_available, ffi::libinput_device_config_rotation_is_available, bool);
1495
1496    /// Set the rotation of a device in degrees clockwise off the
1497    /// logical neutral position.
1498    ///
1499    /// Any subsequent motion events are adjusted according to the
1500    /// given angle.
1501    ///
1502    /// The angle has to be in the range of [0, 360] degrees,
1503    /// otherwise this function returns `DeviceConfigError::Invalid`.
1504    /// If the angle is a multiple of 360 or negative, the caller
1505    /// must ensure the correct ranging before calling this function.
1506    ///
1507    /// The rotation angle is applied to all motion events emitted by
1508    /// the device. Thus, rotating the device also changes the angle
1509    /// required or presented by scrolling, gestures, etc.
1510    ///
1511    /// Setting a rotation of 0 degrees on a device that does not
1512    /// support rotation always succeeds.
1513    pub fn config_rotation_set_angle(&self, angle: u32) -> DeviceConfigResult {
1514        match unsafe { ffi::libinput_device_config_rotation_set_angle(self.as_raw_mut(), angle) } {
1515            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1516            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1517                Err(DeviceConfigError::Unsupported)
1518            }
1519            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1520                Err(DeviceConfigError::Invalid)
1521            }
1522            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1523        }
1524    }
1525
1526    ffi_func!(
1527    /// Get the button for the `ScrollMethod::OnButtonDown` method
1528    /// for this device.
1529    ///
1530    /// If `ScrollMethod::OnButtonDown` scroll method is not
1531    /// supported, or no button is set, this function returns 0.
1532    ///
1533    /// ## Note
1534    ///
1535    /// The return value is independent of the currently selected
1536    /// scroll-method. For button scrolling to activate, a device
1537    /// must have the `ScrollMethod::OnButtonDown` method enabled,
1538    /// and a non-zero button set as scroll button.
1539    pub fn config_scroll_button, ffi::libinput_device_config_scroll_get_button, u32);
1540    ffi_func!(
1541    /// Get the default button for the `ScrollMethod::OnButtonDown`
1542    /// method for this device.
1543    ///
1544    /// If `ScrollMethod::OnButtonDown` scroll method is not
1545    /// supported, or no default button is set,
1546    /// this function returns 0.
1547    pub fn config_scroll_default_button, ffi::libinput_device_config_scroll_get_default_button, u32);
1548
1549    /// Get the default scroll method for this device.
1550    ///
1551    /// The method defines when to generate scroll axis events
1552    /// instead of pointer motion events.
1553    ///
1554    /// A return value of `None` means the scroll method is not known
1555    pub fn config_scroll_default_method(&self) -> Option<ScrollMethod> {
1556        match unsafe { ffi::libinput_device_config_scroll_get_default_method(self.as_raw_mut()) } {
1557            ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_NO_SCROLL => {
1558                Some(ScrollMethod::NoScroll)
1559            }
1560            ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_2FG => {
1561                Some(ScrollMethod::TwoFinger)
1562            }
1563            ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_EDGE => {
1564                Some(ScrollMethod::Edge)
1565            }
1566            ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN => {
1567                Some(ScrollMethod::OnButtonDown)
1568            }
1569            _x => {
1570                #[cfg(feature = "log")]
1571                log::warn!(
1572                    "Unknown ScrollMethod ({}). Unsupported libinput version?",
1573                    _x
1574                );
1575                None
1576            }
1577        }
1578    }
1579
1580    /// Get the scroll method for this device.
1581    ///
1582    /// The method defines when to generate scroll axis events
1583    /// instead of pointer motion events.
1584    ///
1585    /// A return value of `None` means the scroll method is not known
1586    pub fn config_scroll_method(&self) -> Option<ScrollMethod> {
1587        match unsafe { ffi::libinput_device_config_scroll_get_method(self.as_raw_mut()) } {
1588            ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_NO_SCROLL => {
1589                Some(ScrollMethod::NoScroll)
1590            }
1591            ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_2FG => {
1592                Some(ScrollMethod::TwoFinger)
1593            }
1594            ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_EDGE => {
1595                Some(ScrollMethod::Edge)
1596            }
1597            ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN => {
1598                Some(ScrollMethod::OnButtonDown)
1599            }
1600            _ => panic!("libinput returned invalid 'libinput_config_scroll_method'"),
1601        }
1602    }
1603
1604    /// Check which scroll methods a device supports.
1605    ///
1606    /// The method defines when to generate scroll axis events
1607    /// instead of pointer motion events.
1608    pub fn config_scroll_methods(&self) -> Vec<ScrollMethod> {
1609        let mut methods = Vec::new();
1610        let bitmask = unsafe { ffi::libinput_device_config_scroll_get_methods(self.as_raw_mut()) };
1611        if bitmask & ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_NO_SCROLL as u32 != 0
1612        {
1613            methods.push(ScrollMethod::NoScroll);
1614        }
1615        if bitmask & ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_2FG as u32 != 0 {
1616            methods.push(ScrollMethod::TwoFinger);
1617        }
1618        if bitmask & ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_EDGE as u32 != 0 {
1619            methods.push(ScrollMethod::Edge);
1620        }
1621        if bitmask & ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN as u32
1622            != 0
1623        {
1624            methods.push(ScrollMethod::OnButtonDown);
1625        }
1626        methods
1627    }
1628
1629    /// Set the scroll method for this device.
1630    ///
1631    /// The method defines when to generate scroll axis events
1632    /// instead of pointer motion events.
1633    ///
1634    /// ## Note
1635    ///
1636    /// Setting `ScrollMethod::OnButtonDown` enables the scroll
1637    /// method, but scrolling is only activated when the configured
1638    /// button is held down. If no button is set, i.e.
1639    /// `config_scroll_button` returns 0, scrolling cannot activate.
1640    pub fn config_scroll_set_method(&mut self, method: ScrollMethod) -> DeviceConfigResult {
1641        match unsafe {
1642            ffi::libinput_device_config_scroll_set_method(
1643                self.as_raw_mut(),
1644                match method {
1645                    ScrollMethod::NoScroll => {
1646                        ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_NO_SCROLL
1647                    }
1648                    ScrollMethod::TwoFinger => {
1649                        ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_2FG
1650                    }
1651                    ScrollMethod::Edge => {
1652                        ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_EDGE
1653                    }
1654                    ScrollMethod::OnButtonDown => {
1655                        ffi::libinput_config_scroll_method_LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
1656                    }
1657                },
1658            )
1659        } {
1660            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1661            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1662                Err(DeviceConfigError::Unsupported)
1663            }
1664            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1665                Err(DeviceConfigError::Invalid)
1666            }
1667            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1668        }
1669    }
1670
1671    ffi_func!(
1672    /// Get the default mode for scrolling on this device.
1673    pub fn config_scroll_default_natural_scroll_enabled, ffi::libinput_device_config_scroll_get_default_natural_scroll_enabled, bool);
1674    ffi_func!(
1675    /// Get the current mode for scrolling on this device.
1676    pub fn config_scroll_natural_scroll_enabled, ffi::libinput_device_config_scroll_get_natural_scroll_enabled, bool);
1677    ffi_func!(
1678    /// Return non-zero if the device supports "natural scrolling".
1679    ///
1680    /// In traditional scroll mode, the movement of fingers on a
1681    /// touchpad when scrolling matches the movement of the scroll
1682    /// bars. When the fingers move down, the scroll bar moves down,
1683    /// a line of text on the screen moves towards the upper end of
1684    /// the screen. This also matches scroll wheels on mice (wheel
1685    /// down, content moves up).
1686    ///
1687    /// Natural scrolling is the term coined by Apple for inverted
1688    /// scrolling. In this mode, the effect of scrolling movement of
1689    /// fingers on a touchpad resemble physical manipulation of
1690    /// paper. When the fingers move down, a line of text on the
1691    /// screen moves down (scrollbars move up). This is the opposite
1692    /// of scroll wheels on mice.
1693    ///
1694    /// A device supporting natural scrolling can be switched between
1695    /// traditional scroll mode and natural scroll mode.
1696    pub fn config_scroll_has_natural_scroll, ffi::libinput_device_config_scroll_has_natural_scroll, bool);
1697
1698    /// Enable or disable natural scrolling on the device.
1699    pub fn config_scroll_set_natural_scroll_enabled(
1700        &mut self,
1701        enabled: bool,
1702    ) -> DeviceConfigResult {
1703        match unsafe {
1704            ffi::libinput_device_config_scroll_set_natural_scroll_enabled(
1705                self.as_raw_mut(),
1706                enabled as i32,
1707            )
1708        } {
1709            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1710            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1711                Err(DeviceConfigError::Unsupported)
1712            }
1713            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1714                Err(DeviceConfigError::Invalid)
1715            }
1716            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1717        }
1718    }
1719
1720    /// Set the button for the `ScrollMethod::OnButtonDown` method
1721    /// for this device.
1722    ///
1723    /// When the current scroll method is set to
1724    /// `ScrollMethod::OnButtonDown`, no button press/release events
1725    /// will be send for the configured button.
1726    ///
1727    /// When the configured button is pressed, any motion events
1728    /// along a scroll-capable axis are turned into scroll axis
1729    /// events.
1730    ///
1731    /// ## Note
1732    ///
1733    /// Setting the button does not change the scroll method. To
1734    /// change the scroll method call `config_scroll_set_method`.
1735    /// If the button is 0, button scrolling is effectively disabled.
1736    pub fn config_scroll_set_button(&mut self, button: u32) -> DeviceConfigResult {
1737        match unsafe { ffi::libinput_device_config_scroll_set_button(self.as_raw_mut(), button) } {
1738            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1739            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1740                Err(DeviceConfigError::Unsupported)
1741            }
1742            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1743                Err(DeviceConfigError::Invalid)
1744            }
1745            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1746        }
1747    }
1748
1749    /// Get the current scroll button lock state
1750    ///
1751    /// If `ScrollMethod::OnButtonDown` is not supported, or no button is set,
1752    /// this functions returns `Disabled`.
1753    ///
1754    /// ## Note
1755    ///
1756    /// The return value is independent of the currently selected scroll-method.
1757    /// For the scroll button lock to activate, a device must have the
1758    /// `ScrollMethod::OnButtonDown` enabled, and a non-zero button set as scroll button.
1759    #[cfg(feature = "libinput_1_15")]
1760    pub fn config_scroll_button_lock(&self) -> ScrollButtonLockState {
1761        match unsafe { ffi::libinput_device_config_scroll_get_button_lock(self.as_raw() as *mut _) } {
1762            ffi::libinput_config_scroll_button_lock_state_LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED => ScrollButtonLockState::Disabled,
1763            ffi::libinput_config_scroll_button_lock_state_LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED => ScrollButtonLockState::Enabled,
1764            _ => panic!("libinput returned invalid libinput_config_scroll_button_lock_state"),
1765        }
1766    }
1767
1768    /// Get the default scroll button lock state
1769    ///
1770    /// If `ScrollMethod::OnButtonDown` is not supported, or no button is set,
1771    /// this functions returns `Disabled`.
1772    #[cfg(feature = "libinput_1_15")]
1773    pub fn config_scroll_default_button_lock(&self) -> ScrollButtonLockState {
1774        match unsafe { ffi::libinput_device_config_scroll_get_default_button_lock(self.as_raw() as *mut _) } {
1775            ffi::libinput_config_scroll_button_lock_state_LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED => ScrollButtonLockState::Disabled,
1776            ffi::libinput_config_scroll_button_lock_state_LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED => ScrollButtonLockState::Enabled,
1777            _ => panic!("libinput returned invalid libinput_config_scroll_button_lock_state"),
1778        }
1779    }
1780
1781    /// Set the scroll button lock.
1782    ///
1783    /// If the state is `Disabled` the button must physically be held down for
1784    /// button scrolling to work. If the state is `Enabled`, the button is considered
1785    /// logically down after the first press and release sequence, and logically
1786    /// up after the second press and release sequence.
1787    #[cfg(feature = "libinput_1_15")]
1788    pub fn config_scroll_set_button_lock(
1789        &mut self,
1790        state: ScrollButtonLockState,
1791    ) -> DeviceConfigResult {
1792        match unsafe {
1793            ffi::libinput_device_config_scroll_set_button_lock(self.as_raw_mut(),
1794            match state {
1795                ScrollButtonLockState::Enabled => ffi::libinput_config_scroll_button_lock_state_LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED,
1796                ScrollButtonLockState::Disabled => ffi::libinput_config_scroll_button_lock_state_LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED,
1797            }
1798        )
1799        } {
1800            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1801            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1802                Err(DeviceConfigError::Unsupported)
1803            }
1804            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1805                Err(DeviceConfigError::Invalid)
1806            }
1807            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1808        }
1809    }
1810
1811    /// Get the send-event mode for this device.
1812    ///
1813    /// The mode defines when the device processes and sends events
1814    /// to the caller.
1815    ///
1816    /// If a caller enables the bits for multiple modes, some of
1817    /// which are subsets of another mode libinput may drop the bits
1818    /// that are subsets. In other words, don't expect
1819    /// `config_send_events_mode` to always return exactly the same
1820    /// as passed into `config_send_events_set_mode`.
1821    pub fn config_send_events_mode(&self) -> SendEventsMode {
1822        SendEventsMode::from_bits_truncate(unsafe {
1823            ffi::libinput_device_config_send_events_get_mode(self.as_raw_mut())
1824        })
1825    }
1826
1827    /// Return the possible send-event modes for this device.
1828    ///
1829    /// These modes define when a device may process and send events.
1830    pub fn config_send_events_modes(&self) -> SendEventsMode {
1831        SendEventsMode::from_bits_truncate(unsafe {
1832            ffi::libinput_device_config_send_events_get_modes(self.as_raw_mut())
1833        })
1834    }
1835
1836    /// Set the send-event mode for this device.
1837    ///
1838    /// The mode defines when the device processes and sends events
1839    /// to the caller.
1840    ///
1841    /// The selected mode may not take effect immediately. Events
1842    /// already received and processed from this device are
1843    /// unaffected and will be passed to the caller on the next call
1844    /// to `<Libinput as Iterator>::next()`.
1845    ///
1846    /// If the mode is a mixture of `SendEventsMode`s, the device may
1847    /// wait for or generate events until it is in a neutral state.
1848    /// For example, this may include waiting for or generating
1849    /// button release events.
1850    ///
1851    /// If the device is already suspended, this function does
1852    /// nothing and returns success. Changing the send-event mode on
1853    /// a device that has been removed is permitted.
1854    pub fn config_send_events_set_mode(&self, mode: SendEventsMode) -> DeviceConfigResult {
1855        match unsafe {
1856            ffi::libinput_device_config_send_events_set_mode(self.as_raw_mut(), mode.bits())
1857        } {
1858            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
1859            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
1860                Err(DeviceConfigError::Unsupported)
1861            }
1862            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
1863                Err(DeviceConfigError::Invalid)
1864            }
1865            _ => panic!("libinput returned invalid 'libinput_config_status'"),
1866        }
1867    }
1868
1869    /// Get the finger number to button number mapping for
1870    /// tap-to-click.
1871    ///
1872    /// The return value for a device that does not support tapping
1873    /// is always `TapButtonMap::LeftRightMiddle`.
1874    ///
1875    /// This will return `None` for devices
1876    /// where `config_tap_finger_count` returns 0.
1877    pub fn config_tap_button_map(&self) -> Option<TapButtonMap> {
1878        if self.config_tap_finger_count() == 0 {
1879            None
1880        } else {
1881            match unsafe { ffi::libinput_device_config_tap_get_button_map(self.as_raw_mut()) } {
1882                ffi::libinput_config_tap_button_map_LIBINPUT_CONFIG_TAP_MAP_LRM => {
1883                    Some(TapButtonMap::LeftRightMiddle)
1884                }
1885                ffi::libinput_config_tap_button_map_LIBINPUT_CONFIG_TAP_MAP_LMR => {
1886                    Some(TapButtonMap::LeftMiddleRight)
1887                }
1888                _ => panic!("libinput returned invalid 'libinput_config_tap_button_map'"),
1889            }
1890        }
1891    }
1892
1893    /// Get the default finger number to button number mapping for
1894    /// tap-to-click.
1895    ///
1896    /// The return value for a device that does not support tapping
1897    /// is always `TapButtonMap::LeftRightMiddle`.
1898    ///
1899    /// This will return `None` for devices
1900    /// where `config_tap_finger_count` returns 0.
1901    pub fn config_tap_default_button_map(&self) -> Option<TapButtonMap> {
1902        if self.config_tap_finger_count() == 0 {
1903            None
1904        } else {
1905            match unsafe {
1906                ffi::libinput_device_config_tap_get_default_button_map(self.as_raw_mut())
1907            } {
1908                ffi::libinput_config_tap_button_map_LIBINPUT_CONFIG_TAP_MAP_LRM => {
1909                    Some(TapButtonMap::LeftRightMiddle)
1910                }
1911                ffi::libinput_config_tap_button_map_LIBINPUT_CONFIG_TAP_MAP_LMR => {
1912                    Some(TapButtonMap::LeftMiddleRight)
1913                }
1914                _ => panic!("libinput returned invalid 'libinput_config_tap_button_map'"),
1915            }
1916        }
1917    }
1918
1919    /// Return whether tap-and-drag is enabled or disabled by default
1920    /// on this device.
1921    pub fn config_tap_default_drag_enabled(&self) -> bool {
1922        match unsafe { ffi::libinput_device_config_tap_get_default_drag_enabled(self.as_raw_mut()) }
1923        {
1924            ffi::libinput_config_drag_state_LIBINPUT_CONFIG_DRAG_ENABLED => true,
1925            ffi::libinput_config_drag_state_LIBINPUT_CONFIG_DRAG_DISABLED => false,
1926            _ => panic!("libinput returned invalid 'libinput_config_drag_state'"),
1927        }
1928    }
1929
1930    /// Check if drag-lock during tapping is enabled by default on
1931    /// this device.
1932    ///
1933    /// If the device does not support tapping, this function always
1934    /// returns `false`.
1935    ///
1936    /// Drag lock may be enabled by default even when tapping is
1937    /// disabled by default.
1938    pub fn config_tap_default_drag_lock_enabled(&self) -> DragLockState {
1939        match unsafe {
1940            ffi::libinput_device_config_tap_get_default_drag_lock_enabled(self.as_raw_mut())
1941        } {
1942            ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_DISABLED => {
1943                DragLockState::Disabled
1944            }
1945            // legacy spelling for LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT
1946            #[cfg(not(feature = "libinput_1_27"))]
1947            ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED => {
1948                DragLockState::EnabledTimeout
1949            }
1950            #[cfg(feature = "libinput_1_27")]
1951            ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT => {
1952                DragLockState::EnabledTimeout
1953            }
1954            #[cfg(feature = "libinput_1_27")]
1955            ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY => {
1956                DragLockState::EnabledSticky
1957            }
1958            _ => panic!("libinput returned invalid 'libinput_config_drag_lock_state'"),
1959        }
1960    }
1961
1962    /// Return the default setting for whether tap-to-click is
1963    /// enabled on this device.
1964    pub fn config_tap_default_enabled(&self) -> bool {
1965        match unsafe { ffi::libinput_device_config_tap_get_default_enabled(self.as_raw_mut()) } {
1966            ffi::libinput_config_tap_state_LIBINPUT_CONFIG_TAP_ENABLED => true,
1967            ffi::libinput_config_tap_state_LIBINPUT_CONFIG_TAP_DISABLED => false,
1968            _ => panic!("libinput returned invalid 'libinput_config_tap_state'"),
1969        }
1970    }
1971
1972    /// Return whether tap-and-drag is enabled or disabled on this
1973    /// device.
1974    pub fn config_tap_drag_enabled(&self) -> bool {
1975        match unsafe { ffi::libinput_device_config_tap_get_drag_enabled(self.as_raw_mut()) } {
1976            ffi::libinput_config_drag_state_LIBINPUT_CONFIG_DRAG_ENABLED => true,
1977            ffi::libinput_config_drag_state_LIBINPUT_CONFIG_DRAG_DISABLED => false,
1978            _ => panic!("libinput returned invalid 'libinput_config_drag_state'"),
1979        }
1980    }
1981
1982    /// Check if drag-lock during tapping is enabled on this device.
1983    ///
1984    /// If the device does not support tapping, this function always
1985    /// returns `false`.
1986    ///
1987    /// Drag lock may be enabled even when tapping is disabled.
1988    pub fn config_tap_drag_lock_enabled(&self) -> bool {
1989        match unsafe { ffi::libinput_device_config_tap_get_drag_lock_enabled(self.as_raw_mut()) } {
1990            ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED => true,
1991            ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_DISABLED => false,
1992            _ => panic!("libinput returned invalid 'libinput_config_drag_lock_state'"),
1993        }
1994    }
1995
1996    /// Check if tap-to-click is enabled on this device.
1997    ///
1998    /// If the device does not support tapping, this function always
1999    /// returns `false`.
2000    pub fn config_tap_enabled(&self) -> bool {
2001        match unsafe { ffi::libinput_device_config_tap_get_enabled(self.as_raw_mut()) } {
2002            ffi::libinput_config_tap_state_LIBINPUT_CONFIG_TAP_ENABLED => true,
2003            ffi::libinput_config_tap_state_LIBINPUT_CONFIG_TAP_DISABLED => false,
2004            _ => panic!("libinput returned invalid 'libinput_config_tap_state'"),
2005        }
2006    }
2007
2008    ffi_func!(
2009    /// Check if the device supports tap-to-click and how many
2010    /// fingers can be used for tapping.
2011    ///
2012    /// See `config_tap_set_enabled` for more information.
2013    pub fn config_tap_finger_count, ffi::libinput_device_config_tap_get_finger_count, u32);
2014
2015    #[cfg(feature = "libinput_1_28")]
2016    ffi_func!(
2017    /// Returns the maximum number of fingers available for 3-finger dragging.
2018    pub fn config_3fg_drag_get_finger_count, ffi::libinput_device_config_3fg_drag_get_finger_count, u32);
2019
2020    /// Enable or disable 3-finger drag on this device.
2021    ///
2022    /// When enabled, three fingers down will result in a button down event,
2023    /// subsequent finger motion triggers a drag. The button is released shortly
2024    /// after all fingers are logically up.
2025    ///
2026    /// See [Three-finger drag](https://wayland.freedesktop.org/libinput/doc/latest/configuration.html#three-finger-drag) for details.
2027    #[cfg(feature = "libinput_1_28")]
2028    pub fn config_3fg_drag_set_enabled(&self, state: ThreeFingerDragState) -> DeviceConfigResult {
2029        match unsafe {
2030            ffi::libinput_device_config_3fg_drag_set_enabled(
2031                self.as_raw_mut(),
2032                match state {
2033                    ThreeFingerDragState::Disabled => {
2034                        ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_DISABLED
2035                    }
2036                    ThreeFingerDragState::EnabledThreeFinger => {
2037                        ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG
2038                    }
2039                    ThreeFingerDragState::EnabledFourFinger => {
2040                        ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_4FG
2041                    }
2042                },
2043            )
2044        } {
2045            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
2046            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
2047                Err(DeviceConfigError::Unsupported)
2048            }
2049            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
2050                Err(DeviceConfigError::Invalid)
2051            }
2052            _ => panic!("libinput returned invalid 'libinput_config_status'"),
2053        }
2054    }
2055
2056    /// Return whether 3-finger drag is enabled or disabled on this device.
2057    #[cfg(feature = "libinput_1_28")]
2058    pub fn config_3fg_drag_get_enabled(&self) -> ThreeFingerDragState {
2059        match unsafe { ffi::libinput_device_config_3fg_drag_get_enabled(self.as_raw_mut()) } {
2060            ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_DISABLED => {
2061                ThreeFingerDragState::Disabled
2062            }
2063            ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG => {
2064                ThreeFingerDragState::EnabledThreeFinger
2065            }
2066            ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_4FG => {
2067                ThreeFingerDragState::EnabledFourFinger
2068            }
2069            _ => panic!("libinput returned invalid 'libinput_config_3fg_drag_state'"),
2070        }
2071    }
2072
2073    /// Return whether 3-finger drag is enabled or disabled by default on this device.
2074    #[cfg(feature = "libinput_1_28")]
2075    pub fn config_3fg_drag_get_default_enabled(&self) -> ThreeFingerDragState {
2076        match unsafe { ffi::libinput_device_config_3fg_drag_get_default_enabled(self.as_raw_mut()) }
2077        {
2078            ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_DISABLED => {
2079                ThreeFingerDragState::Disabled
2080            }
2081            ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_3FG => {
2082                ThreeFingerDragState::EnabledThreeFinger
2083            }
2084            ffi::libinput_config_3fg_drag_state_LIBINPUT_CONFIG_3FG_DRAG_ENABLED_4FG => {
2085                ThreeFingerDragState::EnabledFourFinger
2086            }
2087            _ => panic!("libinput returned invalid 'libinput_config_3fg_drag_state'"),
2088        }
2089    }
2090
2091    /// Set the finger number to button number mapping for
2092    /// tap-to-click.
2093    ///
2094    /// The default mapping on most devices is to have a 1, 2 and 3
2095    /// finger tap to map to the left, right and middle button,
2096    /// respectively. A device may permit changing the button mapping
2097    /// but disallow specific maps. In this case
2098    /// `DeviceConfigError::Disabled` is returned, the caller is
2099    /// expected to handle this case correctly.
2100    ///
2101    /// Changing the button mapping may not take effect immediately,
2102    /// the device may wait until it is in a neutral state before
2103    /// applying any changes.
2104    ///
2105    /// The mapping may be changed when tap-to-click is disabled. The
2106    /// new mapping takes effect when tap-to-click is enabled in the
2107    /// future.
2108    ///
2109    /// ## Note
2110    ///
2111    /// This will return `None` for devices where
2112    /// `config_tap_finger_count` returns 0.
2113    pub fn config_tap_set_button_map(&mut self, map: TapButtonMap) -> DeviceConfigResult {
2114        match unsafe {
2115            ffi::libinput_device_config_tap_set_button_map(
2116                self.as_raw_mut(),
2117                match map {
2118                    TapButtonMap::LeftRightMiddle => {
2119                        ffi::libinput_config_tap_button_map_LIBINPUT_CONFIG_TAP_MAP_LRM
2120                    }
2121                    TapButtonMap::LeftMiddleRight => {
2122                        ffi::libinput_config_tap_button_map_LIBINPUT_CONFIG_TAP_MAP_LMR
2123                    }
2124                },
2125            )
2126        } {
2127            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
2128            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
2129                Err(DeviceConfigError::Unsupported)
2130            }
2131            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
2132                Err(DeviceConfigError::Invalid)
2133            }
2134            _ => panic!("libinput returned invalid 'libinput_config_status'"),
2135        }
2136    }
2137
2138    /// Enable or disable tap-and-drag on this device.
2139    ///
2140    /// When enabled, a single-finger tap immediately followed by a
2141    /// finger down results in a button down event, subsequent finger
2142    /// motion thus triggers a drag. The button is released on finger
2143    /// up.
2144    /// See [Tap-and-drag](https://wayland.freedesktop.org/libinput/doc/latest/tapping.html#tapndrag)
2145    /// for more details.
2146    pub fn config_tap_set_drag_enabled(&mut self, enabled: bool) -> DeviceConfigResult {
2147        match unsafe {
2148            ffi::libinput_device_config_tap_set_drag_enabled(
2149                self.as_raw_mut(),
2150                if enabled {
2151                    ffi::libinput_config_drag_state_LIBINPUT_CONFIG_DRAG_ENABLED
2152                } else {
2153                    ffi::libinput_config_drag_state_LIBINPUT_CONFIG_DRAG_DISABLED
2154                },
2155            )
2156        } {
2157            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
2158            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
2159                Err(DeviceConfigError::Unsupported)
2160            }
2161            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
2162                Err(DeviceConfigError::Invalid)
2163            }
2164            _ => panic!("libinput returned invalid 'libinput_config_status'"),
2165        }
2166    }
2167
2168    /// Enable or disable drag-lock during tapping on this device.
2169    ///
2170    /// When enabled, a finger may be lifted and put back on the
2171    /// touchpad within a timeout and the drag process continues.
2172    /// When disabled, lifting the finger during a tap-and-drag will
2173    /// immediately stop the drag.
2174    /// See [Tap-and-drag](https://wayland.freedesktop.org/libinput/doc/latest/tapping.html#tapndrag)
2175    /// for details.
2176    ///
2177    /// Enabling drag lock on a device that has tapping disabled is
2178    /// permitted, but has no effect until tapping is enabled.
2179    pub fn config_tap_set_drag_lock_enabled(&mut self, state: DragLockState) -> DeviceConfigResult {
2180        match unsafe {
2181            ffi::libinput_device_config_tap_set_drag_lock_enabled(
2182                self.as_raw_mut(),
2183                match state {
2184                    DragLockState::Disabled => {
2185                        ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_DISABLED
2186                    }
2187                    #[cfg(not(feature = "libinput_1_27"))]
2188                    DragLockState::EnabledTimeout => {
2189                        // legacy spelling for LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT
2190                        ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED
2191                    }
2192                    #[cfg(feature = "libinput_1_27")]
2193                    DragLockState::EnabledTimeout => {
2194                        ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT
2195                    }
2196                    #[cfg(feature = "libinput_1_27")]
2197                    DragLockState::EnabledSticky => {
2198                        ffi::libinput_config_drag_lock_state_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY
2199                    }
2200                },
2201            )
2202        } {
2203            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
2204            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
2205                Err(DeviceConfigError::Unsupported)
2206            }
2207            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
2208                Err(DeviceConfigError::Invalid)
2209            }
2210            _ => panic!("libinput returned invalid 'libinput_config_status'"),
2211        }
2212    }
2213
2214    /// Enable or disable tap-to-click on this device, with a default
2215    /// mapping of 1, 2, 3 finger tap mapping to left, right, middle
2216    /// click, respectively.
2217    ///
2218    /// Tapping is limited by the number of simultaneous touches
2219    /// supported by the device, see `config_tap_finger_count`.
2220    pub fn config_tap_set_enabled(&mut self, enabled: bool) -> DeviceConfigResult {
2221        match unsafe {
2222            ffi::libinput_device_config_tap_set_enabled(
2223                self.as_raw_mut(),
2224                if enabled {
2225                    ffi::libinput_config_tap_state_LIBINPUT_CONFIG_TAP_ENABLED
2226                } else {
2227                    ffi::libinput_config_tap_state_LIBINPUT_CONFIG_TAP_DISABLED
2228                },
2229            )
2230        } {
2231            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_SUCCESS => Ok(()),
2232            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED => {
2233                Err(DeviceConfigError::Unsupported)
2234            }
2235            ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_INVALID => {
2236                Err(DeviceConfigError::Invalid)
2237            }
2238            _ => panic!("libinput returned invalid 'libinput_config_status'"),
2239        }
2240    }
2241}