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}