pinnacle_api/input/
libinput.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Libinput device configuration.
6
7use pinnacle_api_defs::pinnacle::input::{
8    self,
9    v1::{
10        set_device_libinput_setting_request::Setting, set_device_map_target_request::Target,
11        GetDeviceCapabilitiesRequest, GetDeviceInfoRequest, GetDeviceTypeRequest,
12        GetDevicesRequest, SetDeviceLibinputSettingRequest, SetDeviceMapTargetRequest,
13    },
14};
15
16use crate::{client::Client, output::OutputHandle, signal::InputSignal, util::Rect, BlockOnTokio};
17
18/// A pointer acceleration profile.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub enum AccelProfile {
21    /// A flat acceleration profile.
22    ///
23    /// Pointer motion is accelerated by a constant (device-specific) factor, depending on the current speed.
24    Flat,
25    /// An adaptive acceleration profile.
26    ///
27    /// Pointer acceleration depends on the input speed. This is the default profile for most devices.
28    Adaptive,
29}
30
31impl From<AccelProfile> for input::v1::AccelProfile {
32    fn from(value: AccelProfile) -> Self {
33        match value {
34            AccelProfile::Flat => input::v1::AccelProfile::Flat,
35            AccelProfile::Adaptive => input::v1::AccelProfile::Adaptive,
36        }
37    }
38}
39
40/// The click method defines when to generate software-emulated buttons, usually on a device
41/// that does not have a specific physical button available.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub enum ClickMethod {
44    /// Use software-button areas to generate button events.
45    ButtonAreas,
46    /// The number of fingers decides which button press to generate.
47    Clickfinger,
48}
49
50impl From<ClickMethod> for input::v1::ClickMethod {
51    fn from(value: ClickMethod) -> Self {
52        match value {
53            ClickMethod::ButtonAreas => input::v1::ClickMethod::ButtonAreas,
54            ClickMethod::Clickfinger => input::v1::ClickMethod::ClickFinger,
55        }
56    }
57}
58
59/// The scroll method of a device selects when to generate scroll axis events instead of pointer motion events.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61pub enum ScrollMethod {
62    /// Never send scroll events instead of pointer motion events.
63    ///
64    /// This has no effect on events generated by scroll wheels.
65    NoScroll,
66    /// Send scroll events when two fingers are logically down on the device.
67    TwoFinger,
68    /// Send scroll events when a finger moves along the bottom or right edge of a device.
69    Edge,
70    /// Send scroll events when a button is down and the device moves along a scroll-capable axis.
71    OnButtonDown,
72}
73
74impl From<ScrollMethod> for input::v1::ScrollMethod {
75    fn from(value: ScrollMethod) -> Self {
76        match value {
77            ScrollMethod::NoScroll => input::v1::ScrollMethod::NoScroll,
78            ScrollMethod::TwoFinger => input::v1::ScrollMethod::TwoFinger,
79            ScrollMethod::Edge => input::v1::ScrollMethod::Edge,
80            ScrollMethod::OnButtonDown => input::v1::ScrollMethod::OnButtonDown,
81        }
82    }
83}
84
85/// Map 1/2/3 finger taps to buttons.
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
87pub enum TapButtonMap {
88    /// 1/2/3 finger tap maps to left/right/middle
89    LeftRightMiddle,
90    /// 1/2/3 finger tap maps to left/middle/right
91    LeftMiddleRight,
92}
93
94impl From<TapButtonMap> for input::v1::TapButtonMap {
95    fn from(value: TapButtonMap) -> Self {
96        match value {
97            TapButtonMap::LeftRightMiddle => input::v1::TapButtonMap::LeftRightMiddle,
98            TapButtonMap::LeftMiddleRight => input::v1::TapButtonMap::LeftMiddleRight,
99        }
100    }
101}
102
103/// A libinput send events mode.
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105pub enum SendEventsMode {
106    /// Enable this device.
107    Enabled,
108    /// Disable this device.
109    Disabled,
110    /// Disable this device only when an external mouse is connected.
111    DisabledOnExternalMouse,
112}
113
114impl From<SendEventsMode> for input::v1::SendEventsMode {
115    fn from(value: SendEventsMode) -> Self {
116        match value {
117            SendEventsMode::Enabled => input::v1::SendEventsMode::Enabled,
118            SendEventsMode::Disabled => input::v1::SendEventsMode::Disabled,
119            SendEventsMode::DisabledOnExternalMouse => {
120                input::v1::SendEventsMode::DisabledOnExternalMouse
121            }
122        }
123    }
124}
125
126bitflags::bitflags! {
127    /// A device's libinput capabilities.
128    #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
129    pub struct Capability: u16 {
130        /// This device has keyboard capabilities.
131        const KEYBOARD = 1;
132        /// This device has pointer capabilities.
133        const POINTER = 1 << 1;
134        /// This device has touch capabilities.
135        const TOUCH = 1 << 2;
136        /// This device has tablet tool capabilities.
137        const TABLET_TOOL = 1 << 3;
138        /// This device has tablet pad capabilities.
139        const TABLET_PAD = 1 << 4;
140        /// This device has gesture capabilities.
141        const GESTURE = 1 << 5;
142        /// This device has switch capabilities.
143        const SWITCH = 1 << 6;
144    }
145}
146
147/// A device's type.
148///
149/// Note: this uses heuristics to determine device type.
150/// *This may be incorrect*. For example, a device with both pointer
151/// and keyboard capabilities will be labeled as a `Mouse` when it might actually be
152/// a keyboard.
153#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
154pub enum DeviceType {
155    /// The device type is unknown.
156    #[default]
157    Unknown,
158    /// This device is a touchpad.
159    Touchpad,
160    /// This device is a trackball.
161    Trackball,
162    /// This device is a trackpoint.
163    Trackpoint,
164    /// This device is a mouse.
165    Mouse,
166    /// This device is a tablet.
167    Tablet,
168    /// This device is a keyboard.
169    Keyboard,
170    /// This device is a switch.
171    Switch,
172}
173
174impl DeviceType {
175    /// Returns `true` if the device type is [`Unknown`].
176    ///
177    /// [`Unknown`]: DeviceType::Unknown
178    #[must_use]
179    pub fn is_unknown(&self) -> bool {
180        matches!(self, Self::Unknown)
181    }
182
183    /// Returns `true` if the device type is [`Touchpad`].
184    ///
185    /// [`Touchpad`]: DeviceType::Touchpad
186    #[must_use]
187    pub fn is_touchpad(&self) -> bool {
188        matches!(self, Self::Touchpad)
189    }
190
191    /// Returns `true` if the device type is [`Trackball`].
192    ///
193    /// [`Trackball`]: DeviceType::Trackball
194    #[must_use]
195    pub fn is_trackball(&self) -> bool {
196        matches!(self, Self::Trackball)
197    }
198
199    /// Returns `true` if the device type is [`Trackpoint`].
200    ///
201    /// [`Trackpoint`]: DeviceType::Trackpoint
202    #[must_use]
203    pub fn is_trackpoint(&self) -> bool {
204        matches!(self, Self::Trackpoint)
205    }
206
207    /// Returns `true` if the device type is [`Mouse`].
208    ///
209    /// [`Mouse`]: DeviceType::Mouse
210    #[must_use]
211    pub fn is_mouse(&self) -> bool {
212        matches!(self, Self::Mouse)
213    }
214
215    /// Returns `true` if the device type is [`Tablet`].
216    ///
217    /// [`Tablet`]: DeviceType::Tablet
218    #[must_use]
219    pub fn is_tablet(&self) -> bool {
220        matches!(self, Self::Tablet)
221    }
222
223    /// Returns `true` if the device type is [`Keyboard`].
224    ///
225    /// [`Keyboard`]: DeviceType::Keyboard
226    #[must_use]
227    pub fn is_keyboard(&self) -> bool {
228        matches!(self, Self::Keyboard)
229    }
230
231    /// Returns `true` if the device type is [`Switch`].
232    ///
233    /// [`Switch`]: DeviceType::Switch
234    #[must_use]
235    pub fn is_switch(&self) -> bool {
236        matches!(self, Self::Switch)
237    }
238}
239
240impl From<input::v1::DeviceType> for DeviceType {
241    fn from(value: input::v1::DeviceType) -> Self {
242        match value {
243            input::v1::DeviceType::Unspecified => DeviceType::Unknown,
244            input::v1::DeviceType::Touchpad => DeviceType::Touchpad,
245            input::v1::DeviceType::Trackball => DeviceType::Trackball,
246            input::v1::DeviceType::Trackpoint => DeviceType::Trackpoint,
247            input::v1::DeviceType::Mouse => DeviceType::Mouse,
248            input::v1::DeviceType::Tablet => DeviceType::Tablet,
249            input::v1::DeviceType::Keyboard => DeviceType::Keyboard,
250            input::v1::DeviceType::Switch => DeviceType::Switch,
251        }
252    }
253}
254
255/// A libinput device.
256#[derive(Clone, PartialEq, Eq, Hash, Debug)]
257pub struct DeviceHandle {
258    pub(crate) sysname: String,
259}
260
261impl DeviceHandle {
262    /// Gets the [capabilities][Capability] of this device.
263    pub fn capabilities(&self) -> Capability {
264        self.capabilities_async().block_on_tokio()
265    }
266
267    /// Async impl for [`Self::capabilities`].
268    pub async fn capabilities_async(&self) -> Capability {
269        let caps = Client::input()
270            .get_device_capabilities(GetDeviceCapabilitiesRequest {
271                device_sysname: self.sysname.clone(),
272            })
273            .await
274            .unwrap()
275            .into_inner();
276
277        let mut capability = Capability::default();
278
279        if caps.keyboard {
280            capability |= Capability::KEYBOARD;
281        }
282        if caps.pointer {
283            capability |= Capability::POINTER;
284        }
285        if caps.touch {
286            capability |= Capability::TOUCH;
287        }
288        if caps.tablet_tool {
289            capability |= Capability::TABLET_TOOL;
290        }
291        if caps.tablet_pad {
292            capability |= Capability::TABLET_PAD;
293        }
294        if caps.gesture {
295            capability |= Capability::GESTURE;
296        }
297        if caps.switch {
298            capability |= Capability::SWITCH;
299        }
300
301        capability
302    }
303
304    /// Gets this device's name.
305    pub fn name(&self) -> String {
306        self.name_async().block_on_tokio()
307    }
308
309    /// Async impl for [`Self::name`].
310    pub async fn name_async(&self) -> String {
311        Client::input()
312            .get_device_info(GetDeviceInfoRequest {
313                device_sysname: self.sysname.clone(),
314            })
315            .await
316            .unwrap()
317            .into_inner()
318            .name
319    }
320
321    /// Gets this device's product id.
322    pub fn product_id(&self) -> u32 {
323        self.product_id_async().block_on_tokio()
324    }
325
326    /// Async impl for [`Self::product_id`].
327    pub async fn product_id_async(&self) -> u32 {
328        Client::input()
329            .get_device_info(GetDeviceInfoRequest {
330                device_sysname: self.sysname.clone(),
331            })
332            .await
333            .unwrap()
334            .into_inner()
335            .product_id
336    }
337
338    /// Gets this device's vendor id.
339    pub fn vendor_id(&self) -> u32 {
340        self.vendor_id_async().block_on_tokio()
341    }
342
343    /// Async impl for [`Self::vendor_id`].
344    pub async fn vendor_id_async(&self) -> u32 {
345        Client::input()
346            .get_device_info(GetDeviceInfoRequest {
347                device_sysname: self.sysname.clone(),
348            })
349            .await
350            .unwrap()
351            .into_inner()
352            .vendor_id
353    }
354
355    /// Gets this device's [`DeviceType`].
356    pub fn device_type(&self) -> DeviceType {
357        self.device_type_async().block_on_tokio()
358    }
359
360    /// Async impl for [`Self::device_type`].
361    pub async fn device_type_async(&self) -> DeviceType {
362        Client::input()
363            .get_device_type(GetDeviceTypeRequest {
364                device_sysname: self.sysname.clone(),
365            })
366            .await
367            .unwrap()
368            .into_inner()
369            .device_type()
370            .into()
371    }
372
373    /// Maps the absolute input from this device to the corresponding output.
374    ///
375    /// This will cause touch input from this device to map proportionally
376    /// to the area of an output. For example, tapping in the middle of the device
377    /// will generate a tap event at the middle of the output.
378    ///
379    /// This only affects devices with touch capability.
380    ///
381    /// If you want to map the device to an arbitrary region, see [`Self::map_to_region`].
382    pub fn map_to_output(&self, output: &OutputHandle) {
383        Client::input()
384            .set_device_map_target(SetDeviceMapTargetRequest {
385                device_sysname: self.sysname.clone(),
386                target: Some(Target::OutputName(output.name())),
387            })
388            .block_on_tokio()
389            .unwrap();
390    }
391
392    /// Maps the absolute input from this device to the corresponding region
393    /// in the global space.
394    ///
395    /// This will cause touch input from this device to map proportionally
396    /// to the given region within the global space. For example, tapping in the middle of the device
397    /// will generate a tap event at the middle of the region. This can be used
398    /// to map a touch device to more than one output, for example.
399    ///
400    /// This only affects devices with touch capability.
401    ///
402    /// If you want to map the device to a single output, see [`Self::map_to_output`].
403    pub fn map_to_region(&self, region: Rect) {
404        Client::input()
405            .set_device_map_target(SetDeviceMapTargetRequest {
406                device_sysname: self.sysname.clone(),
407                target: Some(Target::Region(region.into())),
408            })
409            .block_on_tokio()
410            .unwrap();
411    }
412
413    /// Sets this device's acceleration profile.
414    pub fn set_accel_profile(&self, accel_profile: AccelProfile) {
415        Client::input()
416            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
417                device_sysname: self.sysname.clone(),
418                setting: Some(Setting::AccelProfile(
419                    input::v1::AccelProfile::from(accel_profile).into(),
420                )),
421            })
422            .block_on_tokio()
423            .unwrap();
424    }
425
426    /// Sets this device's acceleration speed.
427    pub fn set_accel_speed(&self, accel_speed: f64) {
428        Client::input()
429            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
430                device_sysname: self.sysname.clone(),
431                setting: Some(Setting::AccelSpeed(accel_speed)),
432            })
433            .block_on_tokio()
434            .unwrap();
435    }
436
437    /// Sets this device's calibration matrix.
438    pub fn set_calibration_matrix(&self, calibration_matrix: [f32; 6]) {
439        Client::input()
440            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
441                device_sysname: self.sysname.clone(),
442                setting: Some(Setting::CalibrationMatrix(input::v1::CalibrationMatrix {
443                    matrix: calibration_matrix.to_vec(),
444                })),
445            })
446            .block_on_tokio()
447            .unwrap();
448    }
449
450    /// Sets this device's click method.
451    pub fn set_click_method(&self, click_method: ClickMethod) {
452        Client::input()
453            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
454                device_sysname: self.sysname.clone(),
455                setting: Some(Setting::ClickMethod(
456                    input::v1::ClickMethod::from(click_method).into(),
457                )),
458            })
459            .block_on_tokio()
460            .unwrap();
461    }
462
463    /// Sets whether or not this device is disabled while typing.
464    pub fn set_disable_while_typing(&self, disable_while_typing: bool) {
465        Client::input()
466            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
467                device_sysname: self.sysname.clone(),
468                setting: Some(Setting::DisableWhileTyping(disable_while_typing)),
469            })
470            .block_on_tokio()
471            .unwrap();
472    }
473
474    /// Sets this device to left-handed or not.
475    pub fn set_left_handed(&self, left_handed: bool) {
476        Client::input()
477            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
478                device_sysname: self.sysname.clone(),
479                setting: Some(Setting::LeftHanded(left_handed)),
480            })
481            .block_on_tokio()
482            .unwrap();
483    }
484
485    /// Sets whether or not middle emulation is enabled.
486    pub fn set_middle_emulation(&self, middle_emulation: bool) {
487        Client::input()
488            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
489                device_sysname: self.sysname.clone(),
490                setting: Some(Setting::MiddleEmulation(middle_emulation)),
491            })
492            .block_on_tokio()
493            .unwrap();
494    }
495
496    /// Sets this device's rotation angle.
497    pub fn set_rotation_angle(&self, rotation_angle: u32) {
498        Client::input()
499            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
500                device_sysname: self.sysname.clone(),
501                setting: Some(Setting::RotationAngle(rotation_angle)),
502            })
503            .block_on_tokio()
504            .unwrap();
505    }
506
507    /// Sets this device's scroll button.
508    pub fn set_scroll_button(&self, scroll_button: u32) {
509        Client::input()
510            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
511                device_sysname: self.sysname.clone(),
512                setting: Some(Setting::ScrollButton(scroll_button)),
513            })
514            .block_on_tokio()
515            .unwrap();
516    }
517
518    /// Sets whether or not the scroll button locks on this device.
519    pub fn set_scroll_button_lock(&self, scroll_button_lock: bool) {
520        Client::input()
521            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
522                device_sysname: self.sysname.clone(),
523                setting: Some(Setting::ScrollButtonLock(scroll_button_lock)),
524            })
525            .block_on_tokio()
526            .unwrap();
527    }
528
529    /// Sets this device's scroll method.
530    pub fn set_scroll_method(&self, scroll_method: ScrollMethod) {
531        Client::input()
532            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
533                device_sysname: self.sysname.clone(),
534                setting: Some(Setting::ScrollMethod(
535                    input::v1::ScrollMethod::from(scroll_method).into(),
536                )),
537            })
538            .block_on_tokio()
539            .unwrap();
540    }
541
542    /// Enables or disables natural scroll on this device.
543    pub fn set_natural_scroll(&self, natural_scroll: bool) {
544        Client::input()
545            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
546                device_sysname: self.sysname.clone(),
547                setting: Some(Setting::NaturalScroll(natural_scroll)),
548            })
549            .block_on_tokio()
550            .unwrap();
551    }
552
553    /// Sets this device's tap button map.
554    pub fn set_tap_button_map(&self, tap_button_map: TapButtonMap) {
555        Client::input()
556            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
557                device_sysname: self.sysname.clone(),
558                setting: Some(Setting::TapButtonMap(
559                    input::v1::TapButtonMap::from(tap_button_map).into(),
560                )),
561            })
562            .block_on_tokio()
563            .unwrap();
564    }
565
566    /// Enables or disables tap dragging on this device.
567    pub fn set_tap_drag(&self, tap_drag: bool) {
568        Client::input()
569            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
570                device_sysname: self.sysname.clone(),
571                setting: Some(Setting::TapDrag(tap_drag)),
572            })
573            .block_on_tokio()
574            .unwrap();
575    }
576
577    /// Sets whether or not tap dragging locks on this device.
578    pub fn set_tap_drag_lock(&self, tap_drag_lock: bool) {
579        Client::input()
580            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
581                device_sysname: self.sysname.clone(),
582                setting: Some(Setting::TapDragLock(tap_drag_lock)),
583            })
584            .block_on_tokio()
585            .unwrap();
586    }
587
588    /// Enables or disables tap-to-click on this device.
589    pub fn set_tap(&self, tap: bool) {
590        Client::input()
591            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
592                device_sysname: self.sysname.clone(),
593                setting: Some(Setting::Tap(tap)),
594            })
595            .block_on_tokio()
596            .unwrap();
597    }
598
599    /// Sets this device's send events mode.
600    pub fn set_send_events_mode(&self, send_events_mode: SendEventsMode) {
601        Client::input()
602            .set_device_libinput_setting(SetDeviceLibinputSettingRequest {
603                device_sysname: self.sysname.clone(),
604                setting: Some(Setting::SendEventsMode(
605                    input::v1::SendEventsMode::from(send_events_mode).into(),
606                )),
607            })
608            .block_on_tokio()
609            .unwrap();
610    }
611}
612
613/// Gets handles to all connected input devices.
614pub fn get_devices() -> impl Iterator<Item = DeviceHandle> {
615    Client::input()
616        .get_devices(GetDevicesRequest {})
617        .block_on_tokio()
618        .unwrap()
619        .into_inner()
620        .device_sysnames
621        .into_iter()
622        .map(|sysname| DeviceHandle { sysname })
623}
624
625/// Runs a closure for all current and future input devices.
626///
627/// This function does two things:
628///   1. Runs `for_all` with all currently connected input devices, and
629///   2. Runs it with all newly connected devices.
630///
631/// Use this function for input device setup.
632pub fn for_each_device<F: FnMut(&DeviceHandle) + Send + 'static>(mut for_all: F) {
633    for device in get_devices() {
634        for_all(&device);
635    }
636
637    super::connect_signal(InputSignal::DeviceAdded(Box::new(for_all)));
638}