win_hotkey/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#![allow(clippy::uninlined_format_args)]

//! win_hotkey lets you register Global HotKeys for Desktop Applications.
//!
//! # Example
//!
//! ```no_run
//! use win_hotkey::{WinHotKeyManager, hotkey::{HotKey, Modifiers, Code}};
//!
//! // initialize the hotkeys manager
//! let manager = WinHotKeyManager::new().unwrap();
//!
//! // construct the hotkey
//! let hotkey = HotKey::new(Some(Modifiers::SHIFT), Code::KeyD);
//!
//! // register it
//! manager.register(hotkey);
//! ```
//!
//!
//! # Processing global hotkey events
//!
//! You can also listen for the menu events using [`WinHotKeyEvent::receiver`] to get events for the hotkey pressed events.
//! ```no_run
//! use win_hotkey::WinHotKeyEvent;
//!
//! if let Ok(event) = WinHotKeyEvent::receiver().try_recv() {
//!     println!("{:?}", event);
//! }
//! ```
mod error;
pub mod hotkey;
mod manager;

use crossbeam_channel::unbounded;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use once_cell::sync::Lazy;
use once_cell::sync::OnceCell;

pub use self::error::*;
pub use hotkey::HotKey;
pub use manager::WinHotKeyManager;

/// Describes the state of the [`HotKey`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum HotKeyState {
    /// The [`HotKey`] is pressed (the key is down).
    Pressed,
    /// The [`HotKey`] is released (the key is up).
    Released,
}

/// Describes a global hotkey event emitted when a [`HotKey`] is pressed or released.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct WinHotKeyEvent {
    /// Id of the associated [`HotKey`].
    pub id: u32,

    /// Name of the associated [`HotKey`] if available
    pub name: Option<String>,

    /// State of the associated [`HotKey`].
    pub state: HotKeyState,
}

/// A reciever that could be used to listen to global hotkey events.
pub type WinHotKeyEventReceiver = Receiver<WinHotKeyEvent>;
type WinHotKeyEventHandler = Box<dyn Fn(WinHotKeyEvent) + Send + Sync + 'static>;

static WIN_HOTKEY_CHANNEL: Lazy<(Sender<WinHotKeyEvent>, WinHotKeyEventReceiver)> =
    Lazy::new(unbounded);

static WIN_HOTKEY_EVENT_HANDLER: OnceCell<Option<WinHotKeyEventHandler>> = OnceCell::new();

impl WinHotKeyEvent {
    /// Returns the id of the associated [`HotKey`].
    pub fn id(&self) -> u32 {
        self.id
    }

    /// Returns the name of the associated [`HotKey`] if exists
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }

    /// Returns the state of the associated [`HotKey`].

    pub fn state(&self) -> HotKeyState {
        self.state
    }

    /// Gets a reference to the event channel's [`WinHotKeyEventReceiver`]
    /// which can be used to listen for global hotkey events.
    ///
    /// ## Note
    ///
    /// This will not receive any events if [`WinHotKeyEvent::set_event_handler`] has been called with a `Some` value.
    pub fn receiver<'a>() -> &'a WinHotKeyEventReceiver {
        &WIN_HOTKEY_CHANNEL.1
    }

    /// Set a handler to be called for new events. Useful for implementing custom event sender.
    ///
    /// ## Note
    ///
    /// Calling this function with a `Some` value,
    /// will not send new events to the channel associated with [`WinHotKeyEvent::receiver`]
    pub fn set_event_handler<F: Fn(WinHotKeyEvent) + Send + Sync + 'static>(f: Option<F>) {
        if let Some(f) = f {
            let _ = WIN_HOTKEY_EVENT_HANDLER.set(Some(Box::new(f)));
        } else {
            let _ = WIN_HOTKEY_EVENT_HANDLER.set(None);
        }
    }

    pub(crate) fn send(event: WinHotKeyEvent) {
        if let Some(handler) = WIN_HOTKEY_EVENT_HANDLER.get_or_init(|| None) {
            handler(event);
        } else {
            let _ = WIN_HOTKEY_CHANNEL.0.send(event);
        }
    }
}