pinnacle_api/
input.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//! Input management.
6//!
7//! This module provides ways to manage bindings, input devices, and other input settings.
8
9use num_enum::{FromPrimitive, IntoPrimitive};
10use pinnacle_api_defs::pinnacle::input::{
11    self,
12    v1::{
13        BindProperties, BindRequest, EnterBindLayerRequest, GetBindInfosRequest,
14        KeybindOnPressRequest, KeybindStreamRequest, MousebindOnPressRequest,
15        MousebindStreamRequest, SetBindPropertiesRequest, SetRepeatRateRequest, SetXcursorRequest,
16        SetXkbConfigRequest, SetXkbKeymapRequest, SwitchXkbLayoutRequest,
17        switch_xkb_layout_request,
18    },
19};
20use tokio::sync::mpsc::{UnboundedSender, unbounded_channel};
21use tokio_stream::StreamExt;
22
23use crate::{
24    BlockOnTokio,
25    client::Client,
26    signal::{InputSignal, SignalHandle},
27};
28
29pub mod libinput;
30
31pub use xkbcommon::xkb::Keysym;
32
33/// A mouse button.
34#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
35#[repr(u32)]
36pub enum MouseButton {
37    /// The left mouse button
38    Left = 0x110,
39    /// The right mouse button
40    Right = 0x111,
41    /// The middle mouse button
42    Middle = 0x112,
43    /// The side mouse button
44    Side = 0x113,
45    /// The extra mouse button
46    Extra = 0x114,
47    /// The forward mouse button
48    Forward = 0x115,
49    /// The backward mouse button
50    Back = 0x116,
51    /// Some other mouse button
52    #[num_enum(catch_all)]
53    Other(u32),
54}
55
56bitflags::bitflags! {
57    /// A keyboard modifier for use in binds.
58    ///
59    /// Binds can be configured to require certain keyboard modifiers to be held down to trigger.
60    /// For example, a bind with `Mod::SUPER | Mod::CTRL` requires both the super and control keys
61    /// to be held down.
62    ///
63    /// Normally, modifiers must be in the exact same state as passed in to trigger a bind.
64    /// This means if you use `Mod::SUPER` in a bind, *only* super must be held down; holding
65    /// down any other modifier will invalidate the bind.
66    ///
67    /// To circumvent this, you can ignore certain modifiers by OR-ing with the respective
68    /// `Mod::IGNORE_*`.
69    #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Default)]
70    pub struct Mod: u16 {
71        /// The shift key
72        const SHIFT = 1;
73        /// The ctrl key
74        const CTRL = 1 << 1;
75        /// The alt key
76        const ALT = 1 << 2;
77        /// The super key, aka meta, win, mod4
78        const SUPER = 1 << 3;
79        /// The IsoLevel3Shift modifier
80        const ISO_LEVEL3_SHIFT = 1 << 4;
81        /// The IsoLevel5Shift modifer
82        const ISO_LEVEL5_SHIFT = 1 << 5;
83
84        /// Ignore the shift key
85        const IGNORE_SHIFT = 1 << 6;
86        /// Ignore the ctrl key
87        const IGNORE_CTRL = 1 << 7;
88        /// Ignore the alt key
89        const IGNORE_ALT = 1 << 8;
90        /// Ignore the super key
91        const IGNORE_SUPER = 1 << 9;
92        /// Ignore the IsoLevel3Shift modifier
93        const IGNORE_ISO_LEVEL3_SHIFT = 1 << 10;
94        /// Ignore the IsoLevel5Shift modifier
95        const IGNORE_ISO_LEVEL5_SHIFT = 1 << 11;
96    }
97}
98
99impl Mod {
100    fn api_mods(&self) -> Vec<input::v1::Modifier> {
101        let mut mods = Vec::new();
102        if self.contains(Mod::SHIFT) {
103            mods.push(input::v1::Modifier::Shift);
104        }
105        if self.contains(Mod::CTRL) {
106            mods.push(input::v1::Modifier::Ctrl);
107        }
108        if self.contains(Mod::ALT) {
109            mods.push(input::v1::Modifier::Alt);
110        }
111        if self.contains(Mod::SUPER) {
112            mods.push(input::v1::Modifier::Super);
113        }
114        if self.contains(Mod::ISO_LEVEL3_SHIFT) {
115            mods.push(input::v1::Modifier::IsoLevel3Shift);
116        }
117        if self.contains(Mod::ISO_LEVEL5_SHIFT) {
118            mods.push(input::v1::Modifier::IsoLevel5Shift);
119        }
120        mods
121    }
122
123    fn api_ignore_mods(&self) -> Vec<input::v1::Modifier> {
124        let mut mods = Vec::new();
125        if self.contains(Mod::IGNORE_SHIFT) {
126            mods.push(input::v1::Modifier::Shift);
127        }
128        if self.contains(Mod::IGNORE_CTRL) {
129            mods.push(input::v1::Modifier::Ctrl);
130        }
131        if self.contains(Mod::IGNORE_ALT) {
132            mods.push(input::v1::Modifier::Alt);
133        }
134        if self.contains(Mod::IGNORE_SUPER) {
135            mods.push(input::v1::Modifier::Super);
136        }
137        if self.contains(Mod::IGNORE_ISO_LEVEL3_SHIFT) {
138            mods.push(input::v1::Modifier::IsoLevel3Shift);
139        }
140        if self.contains(Mod::IGNORE_ISO_LEVEL5_SHIFT) {
141            mods.push(input::v1::Modifier::IsoLevel5Shift);
142        }
143        mods
144    }
145}
146
147/// A bind layer, also known as a bind mode.
148///
149/// Normally all binds belong to the [`DEFAULT`][Self::DEFAULT] mode.
150/// You can bind binding to different layers and switch between them to enable modal binds.
151#[derive(Debug, Clone, Hash, PartialEq, Eq)]
152pub struct BindLayer {
153    name: Option<String>,
154}
155
156impl BindLayer {
157    /// The default bind layer.
158    ///
159    /// This is the layer [`input::keybind`][self::keybind] uses.
160    pub const DEFAULT: Self = Self { name: None };
161
162    /// Gets the bind layer with the given `name`.
163    pub fn get(name: impl ToString) -> Self {
164        Self {
165            name: Some(name.to_string()),
166        }
167    }
168
169    /// Creates a keybind on this layer.
170    pub fn keybind(&self, mods: Mod, key: impl ToKeysym) -> Keybind {
171        new_keybind(mods, key, self).block_on_tokio()
172    }
173
174    /// Creates a mousebind on this layer.
175    pub fn mousebind(&self, mods: Mod, button: MouseButton) -> Mousebind {
176        new_mousebind(mods, button, self).block_on_tokio()
177    }
178
179    /// Enters this layer, causing only its binds to be in effect.
180    pub fn enter(&self) {
181        Client::input()
182            .enter_bind_layer(EnterBindLayerRequest {
183                layer_name: self.name.clone(),
184            })
185            .block_on_tokio()
186            .unwrap();
187    }
188
189    /// Returns this bind layer's name, or `None` if this is the default bind layer.
190    pub fn name(&self) -> Option<String> {
191        self.name.clone()
192    }
193}
194
195/// Functionality common to all bind types.
196pub trait Bind {
197    /// Sets this bind's group.
198    fn group(&mut self, group: impl ToString) -> &mut Self;
199    /// Sets this bind's description.
200    fn description(&mut self, desc: impl ToString) -> &mut Self;
201    /// Sets this bind as a quit bind.
202    fn set_as_quit(&mut self) -> &mut Self;
203    /// Sets this bind as a reload config bind.
204    fn set_as_reload_config(&mut self) -> &mut Self;
205    /// Allows this bind to trigger when the session is locked.
206    fn allow_when_locked(&mut self) -> &mut Self;
207}
208
209macro_rules! bind_impl {
210    ($ty:ty) => {
211        impl Bind for $ty {
212            fn group(&mut self, group: impl ToString) -> &mut Self {
213                Client::input()
214                    .set_bind_properties(SetBindPropertiesRequest {
215                        bind_id: self.bind_id,
216                        properties: Some(BindProperties {
217                            group: Some(group.to_string()),
218                            ..Default::default()
219                        }),
220                    })
221                    .block_on_tokio()
222                    .unwrap();
223                self
224            }
225
226            fn description(&mut self, desc: impl ToString) -> &mut Self {
227                Client::input()
228                    .set_bind_properties(SetBindPropertiesRequest {
229                        bind_id: self.bind_id,
230                        properties: Some(BindProperties {
231                            description: Some(desc.to_string()),
232                            ..Default::default()
233                        }),
234                    })
235                    .block_on_tokio()
236                    .unwrap();
237                self
238            }
239
240            fn set_as_quit(&mut self) -> &mut Self {
241                Client::input()
242                    .set_bind_properties(SetBindPropertiesRequest {
243                        bind_id: self.bind_id,
244                        properties: Some(BindProperties {
245                            quit: Some(true),
246                            ..Default::default()
247                        }),
248                    })
249                    .block_on_tokio()
250                    .unwrap();
251                self
252            }
253
254            fn set_as_reload_config(&mut self) -> &mut Self {
255                Client::input()
256                    .set_bind_properties(SetBindPropertiesRequest {
257                        bind_id: self.bind_id,
258                        properties: Some(BindProperties {
259                            reload_config: Some(true),
260                            ..Default::default()
261                        }),
262                    })
263                    .block_on_tokio()
264                    .unwrap();
265                self
266            }
267
268            fn allow_when_locked(&mut self) -> &mut Self {
269                Client::input()
270                    .set_bind_properties(SetBindPropertiesRequest {
271                        bind_id: self.bind_id,
272                        properties: Some(BindProperties {
273                            allow_when_locked: Some(true),
274                            ..Default::default()
275                        }),
276                    })
277                    .block_on_tokio()
278                    .unwrap();
279                self
280            }
281        }
282    };
283}
284
285enum Edge {
286    Press,
287    Release,
288}
289
290type KeybindCallback = (Box<dyn FnMut() + Send + 'static>, Edge);
291
292/// A keybind.
293pub struct Keybind {
294    bind_id: u32,
295    callback_sender: Option<UnboundedSender<KeybindCallback>>,
296}
297
298bind_impl!(Keybind);
299
300/// Creates a keybind on the [`DEFAULT`][BindLayer::DEFAULT] bind layer.
301pub fn keybind(mods: Mod, key: impl ToKeysym) -> Keybind {
302    BindLayer::DEFAULT.keybind(mods, key)
303}
304
305impl Keybind {
306    /// Runs a closure whenever this keybind is pressed.
307    pub fn on_press<F: FnMut() + Send + 'static>(&mut self, on_press: F) -> &mut Self {
308        let sender = self
309            .callback_sender
310            .get_or_insert_with(|| new_keybind_stream(self.bind_id).block_on_tokio());
311        let _ = sender.send((Box::new(on_press), Edge::Press));
312
313        Client::input()
314            .keybind_on_press(KeybindOnPressRequest {
315                bind_id: self.bind_id,
316            })
317            .block_on_tokio()
318            .unwrap();
319
320        self
321    }
322
323    /// Runs a closure whenever this keybind is released.
324    pub fn on_release<F: FnMut() + Send + 'static>(&mut self, on_release: F) -> &mut Self {
325        let sender = self
326            .callback_sender
327            .get_or_insert_with(|| new_keybind_stream(self.bind_id).block_on_tokio());
328        let _ = sender.send((Box::new(on_release), Edge::Release));
329
330        self
331    }
332}
333
334async fn new_keybind(mods: Mod, key: impl ToKeysym, layer: &BindLayer) -> Keybind {
335    let ignore_mods = mods.api_ignore_mods();
336    let mods = mods.api_mods();
337
338    let bind_id = Client::input()
339        .bind(BindRequest {
340            bind: Some(input::v1::Bind {
341                mods: mods.into_iter().map(|m| m.into()).collect(),
342                ignore_mods: ignore_mods.into_iter().map(|m| m.into()).collect(),
343                layer_name: layer.name.clone(),
344                properties: Some(BindProperties::default()),
345                bind: Some(input::v1::bind::Bind::Key(input::v1::Keybind {
346                    key_code: Some(key.to_keysym().raw()),
347                    xkb_name: None,
348                })),
349            }),
350        })
351        .await
352        .unwrap()
353        .into_inner()
354        .bind_id;
355
356    Keybind {
357        bind_id,
358        callback_sender: None,
359    }
360}
361
362async fn new_keybind_stream(
363    bind_id: u32,
364) -> UnboundedSender<(Box<dyn FnMut() + Send + 'static>, Edge)> {
365    let mut from_server = Client::input()
366        .keybind_stream(KeybindStreamRequest { bind_id })
367        .await
368        .unwrap()
369        .into_inner();
370
371    let (send, mut recv) = unbounded_channel();
372
373    tokio::spawn(async move {
374        let mut on_presses = Vec::<Box<dyn FnMut() + Send + 'static>>::new();
375        let mut on_releases = Vec::<Box<dyn FnMut() + Send + 'static>>::new();
376
377        loop {
378            tokio::select! {
379                Some(Ok(response)) = from_server.next() => {
380                    match response.edge() {
381                        input::v1::Edge::Unspecified => (),
382                        input::v1::Edge::Press => {
383                            for on_press in on_presses.iter_mut() {
384                                on_press();
385                            }
386                        }
387                        input::v1::Edge::Release => {
388                            for on_release in on_releases.iter_mut() {
389                                on_release();
390                            }
391                        }
392                    }
393                }
394                Some((cb, edge)) = recv.recv() => {
395                    match edge {
396                        Edge::Press => on_presses.push(cb),
397                        Edge::Release => on_releases.push(cb),
398                    }
399                }
400                else => break,
401            }
402        }
403    });
404
405    send
406}
407
408// Mousebinds
409
410type MousebindCallback = (Box<dyn FnMut() + Send + 'static>, Edge);
411
412/// A mousebind.
413pub struct Mousebind {
414    bind_id: u32,
415    callback_sender: Option<UnboundedSender<MousebindCallback>>,
416}
417
418bind_impl!(Mousebind);
419
420/// Creates a mousebind on the [`DEFAULT`][BindLayer::DEFAULT] bind layer.
421pub fn mousebind(mods: Mod, button: MouseButton) -> Mousebind {
422    BindLayer::DEFAULT.mousebind(mods, button)
423}
424
425impl Mousebind {
426    /// Runs a closure whenever this mousebind is pressed.
427    pub fn on_press<F: FnMut() + Send + 'static>(&mut self, on_press: F) -> &mut Self {
428        let sender = self
429            .callback_sender
430            .get_or_insert_with(|| new_mousebind_stream(self.bind_id).block_on_tokio());
431        let _ = sender.send((Box::new(on_press), Edge::Press));
432
433        Client::input()
434            .mousebind_on_press(MousebindOnPressRequest {
435                bind_id: self.bind_id,
436            })
437            .block_on_tokio()
438            .unwrap();
439
440        self
441    }
442
443    /// Runs a closure whenever this mousebind is released.
444    pub fn on_release<F: FnMut() + Send + 'static>(&mut self, on_release: F) -> &mut Self {
445        let sender = self
446            .callback_sender
447            .get_or_insert_with(|| new_mousebind_stream(self.bind_id).block_on_tokio());
448        let _ = sender.send((Box::new(on_release), Edge::Release));
449
450        self
451    }
452}
453
454async fn new_mousebind(mods: Mod, button: MouseButton, layer: &BindLayer) -> Mousebind {
455    let ignore_mods = mods.api_ignore_mods();
456    let mods = mods.api_mods();
457
458    let bind_id = Client::input()
459        .bind(BindRequest {
460            bind: Some(input::v1::Bind {
461                mods: mods.into_iter().map(|m| m.into()).collect(),
462                ignore_mods: ignore_mods.into_iter().map(|m| m.into()).collect(),
463                layer_name: layer.name.clone(),
464                properties: Some(BindProperties::default()),
465                bind: Some(input::v1::bind::Bind::Mouse(input::v1::Mousebind {
466                    button: button.into(),
467                })),
468            }),
469        })
470        .await
471        .unwrap()
472        .into_inner()
473        .bind_id;
474
475    Mousebind {
476        bind_id,
477        callback_sender: None,
478    }
479}
480
481async fn new_mousebind_stream(
482    bind_id: u32,
483) -> UnboundedSender<(Box<dyn FnMut() + Send + 'static>, Edge)> {
484    let mut from_server = Client::input()
485        .mousebind_stream(MousebindStreamRequest { bind_id })
486        .await
487        .unwrap()
488        .into_inner();
489
490    let (send, mut recv) = unbounded_channel();
491
492    tokio::spawn(async move {
493        let mut on_presses = Vec::<Box<dyn FnMut() + Send + 'static>>::new();
494        let mut on_releases = Vec::<Box<dyn FnMut() + Send + 'static>>::new();
495
496        loop {
497            tokio::select! {
498                Some(Ok(response)) = from_server.next() => {
499                    match response.edge() {
500                        input::v1::Edge::Unspecified => (),
501                        input::v1::Edge::Press => {
502                            for on_press in on_presses.iter_mut() {
503                                on_press();
504                            }
505                        }
506                        input::v1::Edge::Release => {
507                            for on_release in on_releases.iter_mut() {
508                                on_release();
509                            }
510                        }
511                    }
512                }
513                Some((cb, edge)) = recv.recv() => {
514                    match edge {
515                        Edge::Press => on_presses.push(cb),
516                        Edge::Release => on_releases.push(cb),
517                    }
518                }
519                else => break,
520            }
521        }
522    });
523
524    send
525}
526
527/// A struct that lets you define xkeyboard config options.
528///
529/// See `xkeyboard-config(7)` for more information.
530#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
531pub struct XkbConfig {
532    /// Files of rules to be used for keyboard mapping composition
533    pub rules: Option<String>,
534    /// Name of the model of your keyboard type
535    pub model: Option<String>,
536    /// Layout(s) you intend to use
537    pub layout: Option<String>,
538    /// Variant(s) of the layout you intend to use
539    pub variant: Option<String>,
540    /// Extra xkb configuration options
541    pub options: Option<String>,
542}
543
544impl XkbConfig {
545    /// Creates a new, empty [`XkbConfig`].
546    pub fn new() -> Self {
547        Default::default()
548    }
549
550    /// Sets this config's `rules`.
551    pub fn with_rules(mut self, rules: impl ToString) -> Self {
552        self.rules = Some(rules.to_string());
553        self
554    }
555
556    /// Sets this config's `model`.
557    pub fn with_model(mut self, model: impl ToString) -> Self {
558        self.model = Some(model.to_string());
559        self
560    }
561
562    /// Sets this config's `layout`.
563    pub fn with_layout(mut self, layout: impl ToString) -> Self {
564        self.layout = Some(layout.to_string());
565        self
566    }
567
568    /// Sets this config's `variant`.
569    pub fn with_variant(mut self, variant: impl ToString) -> Self {
570        self.variant = Some(variant.to_string());
571        self
572    }
573
574    /// Sets this config's `options`.
575    pub fn with_options(mut self, options: impl ToString) -> Self {
576        self.options = Some(options.to_string());
577        self
578    }
579}
580
581/// Sets the xkeyboard config.
582///
583/// This allows you to set several xkeyboard options like `layout` and `rules`.
584///
585/// See `xkeyboard-config(7)` for more information.
586///
587/// # Examples
588///
589/// ```no_run
590/// # use pinnacle_api::input;
591/// # use pinnacle_api::input::XkbConfig;
592/// input::set_xkb_config(XkbConfig::new()
593///     .with_layout("us,fr,ge")
594///     .with_options("ctrl:swapcaps,caps:shift"));
595/// ```
596pub fn set_xkb_config(xkb_config: XkbConfig) {
597    Client::input()
598        .set_xkb_config(SetXkbConfigRequest {
599            rules: xkb_config.rules,
600            variant: xkb_config.variant,
601            layout: xkb_config.layout,
602            model: xkb_config.model,
603            options: xkb_config.options,
604        })
605        .block_on_tokio()
606        .unwrap();
607}
608
609/// Sets the XKB keymap.
610///
611/// # Examples
612///
613/// ```no_run
614/// # use pinnacle_api::input;
615/// input::set_xkb_keymap("keymap here...");
616///
617/// // From a file
618/// # || {
619/// input::set_xkb_keymap(std::fs::read_to_string("/path/to/keymap.xkb")?);
620/// # Ok::<_, std::io::Error>(())
621/// # };
622/// ```
623pub fn set_xkb_keymap(keymap: impl ToString) {
624    Client::input()
625        .set_xkb_keymap(SetXkbKeymapRequest {
626            keymap: keymap.to_string(),
627        })
628        .block_on_tokio()
629        .unwrap();
630}
631
632/// Cycles the current XKB layout forward.
633pub fn cycle_xkb_layout_forward() {
634    Client::input()
635        .switch_xkb_layout(SwitchXkbLayoutRequest {
636            action: Some(switch_xkb_layout_request::Action::Next(())),
637        })
638        .block_on_tokio()
639        .unwrap();
640}
641
642/// Cycles the current XKB layout backward.
643pub fn cycle_xkb_layout_backward() {
644    Client::input()
645        .switch_xkb_layout(SwitchXkbLayoutRequest {
646            action: Some(switch_xkb_layout_request::Action::Prev(())),
647        })
648        .block_on_tokio()
649        .unwrap();
650}
651
652/// Switches the current XKB layout to the one at the provided `index`.
653///
654/// Fails if the index is out of bounds.
655pub fn switch_xkb_layout(index: u32) {
656    Client::input()
657        .switch_xkb_layout(SwitchXkbLayoutRequest {
658            action: Some(switch_xkb_layout_request::Action::Index(index)),
659        })
660        .block_on_tokio()
661        .unwrap();
662}
663
664/// Bind information.
665///
666/// Mainly used for the bind overlay.
667#[derive(Debug, Clone, PartialEq, Eq, Hash)]
668pub struct BindInfo {
669    /// The group to place this bind in. Empty if it is not in one.
670    pub group: String,
671    /// The description of this bind. Empty if it does not have one.
672    pub description: String,
673    /// The bind's modifiers.
674    pub mods: Mod,
675    /// The bind's layer.
676    pub layer: BindLayer,
677    /// Whether this bind is a quit bind.
678    pub quit: bool,
679    /// Whether this bind is a reload config bind.
680    pub reload_config: bool,
681    /// Whether this bind is allowed when the session is locked.
682    pub allow_when_locked: bool,
683    /// What kind of bind this is.
684    pub kind: BindInfoKind,
685}
686
687/// The kind of a bind (hey that rhymes).
688#[derive(Debug, Clone, PartialEq, Eq, Hash)]
689pub enum BindInfoKind {
690    /// This is a keybind.
691    Key {
692        /// The numeric key code.
693        key_code: u32,
694        /// The xkeyboard name of this key.
695        xkb_name: String,
696    },
697    /// This is a mousebind.
698    Mouse {
699        /// Which mouse button this bind uses.
700        button: MouseButton,
701    },
702}
703
704/// Sets the keyboard's repeat rate.
705///
706/// This allows you to set the time between holding down a key and it repeating
707/// as well as the time between each repeat.
708///
709/// Units are in milliseconds.
710///
711/// # Examples
712///
713/// ```no_run
714/// # use pinnacle_api::input;
715/// // Set keyboard to repeat after holding down for half a second,
716/// // and repeat once every 25ms (40 times a second)
717/// input::set_repeat_rate(25, 500);
718/// ```
719pub fn set_repeat_rate(rate: i32, delay: i32) {
720    Client::input()
721        .set_repeat_rate(SetRepeatRateRequest {
722            rate: Some(rate),
723            delay: Some(delay),
724        })
725        .block_on_tokio()
726        .unwrap();
727}
728
729/// Sets the xcursor theme.
730///
731/// Pinnacle reads `$XCURSOR_THEME` on startup to determine the theme.
732/// This allows you to set it at runtime.
733///
734/// # Examples
735///
736/// ```no_run
737/// # use pinnacle_api::input;
738/// input::set_xcursor_theme("Adwaita");
739/// ```
740pub fn set_xcursor_theme(theme: impl ToString) {
741    Client::input()
742        .set_xcursor(SetXcursorRequest {
743            theme: Some(theme.to_string()),
744            size: None,
745        })
746        .block_on_tokio()
747        .unwrap();
748}
749
750/// Sets the xcursor size.
751///
752/// Pinnacle reads `$XCURSOR_SIZE` on startup to determine the cursor size.
753/// This allows you to set it at runtime.
754///
755/// # Examples
756///
757/// ```no_run
758/// # use pinnacle_api::input;
759/// input::set_xcursor_size(64);
760/// ```
761pub fn set_xcursor_size(size: u32) {
762    Client::input()
763        .set_xcursor(SetXcursorRequest {
764            theme: None,
765            size: Some(size),
766        })
767        .block_on_tokio()
768        .unwrap();
769}
770
771/// A trait that designates anything that can be converted into a [`Keysym`].
772pub trait ToKeysym {
773    /// Converts this into a [`Keysym`].
774    fn to_keysym(&self) -> Keysym;
775}
776
777impl ToKeysym for Keysym {
778    fn to_keysym(&self) -> Keysym {
779        *self
780    }
781}
782
783impl ToKeysym for char {
784    fn to_keysym(&self) -> Keysym {
785        Keysym::from_char(*self)
786    }
787}
788
789impl ToKeysym for &str {
790    fn to_keysym(&self) -> Keysym {
791        xkbcommon::xkb::keysym_from_name(self, xkbcommon::xkb::KEYSYM_NO_FLAGS)
792    }
793}
794
795impl ToKeysym for String {
796    fn to_keysym(&self) -> Keysym {
797        xkbcommon::xkb::keysym_from_name(self, xkbcommon::xkb::KEYSYM_NO_FLAGS)
798    }
799}
800
801impl ToKeysym for u32 {
802    fn to_keysym(&self) -> Keysym {
803        Keysym::from(*self)
804    }
805}
806
807/// Gets all bind information.
808pub fn bind_infos() -> impl Iterator<Item = BindInfo> {
809    let infos = Client::input()
810        .get_bind_infos(GetBindInfosRequest {})
811        .block_on_tokio()
812        .unwrap()
813        .into_inner()
814        .bind_infos;
815
816    infos.into_iter().filter_map(|info| {
817        let info = info.bind?;
818        let mut mods = info.mods().fold(Mod::empty(), |acc, m| match m {
819            input::v1::Modifier::Unspecified => acc,
820            input::v1::Modifier::Shift => acc | Mod::SHIFT,
821            input::v1::Modifier::Ctrl => acc | Mod::CTRL,
822            input::v1::Modifier::Alt => acc | Mod::ALT,
823            input::v1::Modifier::Super => acc | Mod::SUPER,
824            input::v1::Modifier::IsoLevel3Shift => acc | Mod::ISO_LEVEL3_SHIFT,
825            input::v1::Modifier::IsoLevel5Shift => acc | Mod::ISO_LEVEL5_SHIFT,
826        });
827
828        for ignore_mod in info.ignore_mods() {
829            match ignore_mod {
830                input::v1::Modifier::Unspecified => (),
831                input::v1::Modifier::Shift => mods |= Mod::IGNORE_SHIFT,
832                input::v1::Modifier::Ctrl => mods |= Mod::IGNORE_CTRL,
833                input::v1::Modifier::Alt => mods |= Mod::IGNORE_ALT,
834                input::v1::Modifier::Super => mods |= Mod::IGNORE_SUPER,
835                input::v1::Modifier::IsoLevel3Shift => mods |= Mod::ISO_LEVEL3_SHIFT,
836                input::v1::Modifier::IsoLevel5Shift => mods |= Mod::ISO_LEVEL5_SHIFT,
837            }
838        }
839
840        let bind_kind = match info.bind? {
841            input::v1::bind::Bind::Key(keybind) => BindInfoKind::Key {
842                key_code: keybind.key_code(),
843                xkb_name: keybind.xkb_name().to_string(),
844            },
845            input::v1::bind::Bind::Mouse(mousebind) => BindInfoKind::Mouse {
846                button: MouseButton::from(mousebind.button),
847            },
848        };
849
850        let layer = BindLayer {
851            name: info.layer_name,
852        };
853        let group = info
854            .properties
855            .as_ref()
856            .and_then(|props| props.group.clone())
857            .unwrap_or_default();
858        let description = info
859            .properties
860            .as_ref()
861            .and_then(|props| props.description.clone())
862            .unwrap_or_default();
863        let quit = info
864            .properties
865            .as_ref()
866            .and_then(|props| props.quit)
867            .unwrap_or_default();
868        let reload_config = info
869            .properties
870            .as_ref()
871            .and_then(|props| props.reload_config)
872            .unwrap_or_default();
873        let allow_when_locked = info
874            .properties
875            .as_ref()
876            .and_then(|props| props.allow_when_locked)
877            .unwrap_or_default();
878
879        Some(BindInfo {
880            group,
881            description,
882            mods,
883            layer,
884            quit,
885            reload_config,
886            allow_when_locked,
887            kind: bind_kind,
888        })
889    })
890}
891
892/// Connects to an [`InputSignal`].
893///
894/// # Examples
895///
896/// ```no_run
897/// # use pinnacle_api::input;
898/// # use pinnacle_api::signal::InputSignal;
899/// input::connect_signal(InputSignal::DeviceAdded(Box::new(|device| {
900///     println!("New device: {}", device.name());
901/// })));
902/// ```
903pub fn connect_signal(signal: InputSignal) -> SignalHandle {
904    let mut signal_state = Client::signal_state();
905
906    match signal {
907        InputSignal::DeviceAdded(f) => signal_state.input_device_added.add_callback(f),
908    }
909}