global_hotkey/
lib.rs

1// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5#![allow(clippy::uninlined_format_args)]
6
7//! global_hotkey lets you register Global HotKeys for Desktop Applications.
8//!
9//! ## Platforms-supported:
10//!
11//! - Windows
12//! - macOS
13//! - Linux (X11 Only)
14//!
15//! ## Platform-specific notes:
16//!
17//! - On Windows a win32 event loop must be running on the thread. It doesn't need to be the main thread but you have to create the global hotkey manager on the same thread as the event loop.
18//! - On macOS, an event loop must be running on the main thread so you also need to create the global hotkey manager on the main thread.
19//!
20//! # Example
21//!
22//! ```no_run
23//! use global_hotkey::{GlobalHotKeyManager, hotkey::{HotKey, Modifiers, Code}};
24//!
25//! // initialize the hotkeys manager
26//! let manager = GlobalHotKeyManager::new().unwrap();
27//!
28//! // construct the hotkey
29//! let hotkey = HotKey::new(Some(Modifiers::SHIFT), Code::KeyD);
30//!
31//! // register it
32//! manager.register(hotkey);
33//! ```
34//!
35//!
36//! # Processing global hotkey events
37//!
38//! You can also listen for the menu events using [`GlobalHotKeyEvent::receiver`] to get events for the hotkey pressed events.
39//! ```no_run
40//! use global_hotkey::GlobalHotKeyEvent;
41//!
42//! if let Ok(event) = GlobalHotKeyEvent::receiver().try_recv() {
43//!     println!("{:?}", event);
44//! }
45//! ```
46//!
47//! # Platforms-supported:
48//!
49//! - Windows
50//! - macOS
51//! - Linux (X11 Only)
52
53use crossbeam_channel::{unbounded, Receiver, Sender};
54use once_cell::sync::{Lazy, OnceCell};
55
56mod error;
57pub mod hotkey;
58mod platform_impl;
59
60pub use self::error::*;
61use hotkey::HotKey;
62
63/// Describes the state of the [`HotKey`].
64#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
65#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
66pub enum HotKeyState {
67    /// The [`HotKey`] is pressed (the key is down).
68    Pressed,
69    /// The [`HotKey`] is released (the key is up).
70    Released,
71}
72
73/// Describes a global hotkey event emitted when a [`HotKey`] is pressed or released.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
75#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
76pub struct GlobalHotKeyEvent {
77    /// Id of the associated [`HotKey`].
78    pub id: u32,
79    /// State of the associated [`HotKey`].
80    pub state: HotKeyState,
81}
82
83/// A reciever that could be used to listen to global hotkey events.
84pub type GlobalHotKeyEventReceiver = Receiver<GlobalHotKeyEvent>;
85type GlobalHotKeyEventHandler = Box<dyn Fn(GlobalHotKeyEvent) + Send + Sync + 'static>;
86
87static GLOBAL_HOTKEY_CHANNEL: Lazy<(Sender<GlobalHotKeyEvent>, GlobalHotKeyEventReceiver)> =
88    Lazy::new(unbounded);
89static GLOBAL_HOTKEY_EVENT_HANDLER: OnceCell<Option<GlobalHotKeyEventHandler>> = OnceCell::new();
90
91impl GlobalHotKeyEvent {
92    /// Returns the id of the associated [`HotKey`].
93    pub fn id(&self) -> u32 {
94        self.id
95    }
96
97    /// Returns the state of the associated [`HotKey`].
98    pub fn state(&self) -> HotKeyState {
99        self.state
100    }
101
102    /// Gets a reference to the event channel's [`GlobalHotKeyEventReceiver`]
103    /// which can be used to listen for global hotkey events.
104    ///
105    /// ## Note
106    ///
107    /// This will not receive any events if [`GlobalHotKeyEvent::set_event_handler`] has been called with a `Some` value.
108    pub fn receiver<'a>() -> &'a GlobalHotKeyEventReceiver {
109        &GLOBAL_HOTKEY_CHANNEL.1
110    }
111
112    /// Set a handler to be called for new events. Useful for implementing custom event sender.
113    ///
114    /// ## Note
115    ///
116    /// Calling this function with a `Some` value,
117    /// will not send new events to the channel associated with [`GlobalHotKeyEvent::receiver`]
118    pub fn set_event_handler<F: Fn(GlobalHotKeyEvent) + Send + Sync + 'static>(f: Option<F>) {
119        if let Some(f) = f {
120            let _ = GLOBAL_HOTKEY_EVENT_HANDLER.set(Some(Box::new(f)));
121        } else {
122            let _ = GLOBAL_HOTKEY_EVENT_HANDLER.set(None);
123        }
124    }
125
126    pub(crate) fn send(event: GlobalHotKeyEvent) {
127        if let Some(handler) = GLOBAL_HOTKEY_EVENT_HANDLER.get_or_init(|| None) {
128            handler(event);
129        } else {
130            let _ = GLOBAL_HOTKEY_CHANNEL.0.send(event);
131        }
132    }
133}
134
135pub struct GlobalHotKeyManager {
136    platform_impl: platform_impl::GlobalHotKeyManager,
137}
138
139impl GlobalHotKeyManager {
140    pub fn new() -> crate::Result<Self> {
141        Ok(Self {
142            platform_impl: platform_impl::GlobalHotKeyManager::new()?,
143        })
144    }
145
146    pub fn register(&self, hotkey: HotKey) -> crate::Result<()> {
147        self.platform_impl.register(hotkey)
148    }
149
150    pub fn unregister(&self, hotkey: HotKey) -> crate::Result<()> {
151        self.platform_impl.unregister(hotkey)
152    }
153
154    pub fn register_all(&self, hotkeys: &[HotKey]) -> crate::Result<()> {
155        self.platform_impl.register_all(hotkeys)?;
156        Ok(())
157    }
158
159    pub fn unregister_all(&self, hotkeys: &[HotKey]) -> crate::Result<()> {
160        self.platform_impl.unregister_all(hotkeys)?;
161        Ok(())
162    }
163}