Skip to content

Commit

Permalink
refactor(core): add global-shortcut Cargo feature, enhancing binary s…
Browse files Browse the repository at this point in the history
…ize (#3956)
  • Loading branch information
lucasfernog committed Apr 24, 2022
1 parent c23f139 commit e11878b
Show file tree
Hide file tree
Showing 11 changed files with 261 additions and 152 deletions.
7 changes: 7 additions & 0 deletions .changes/global-shortcut-feature.md
@@ -0,0 +1,7 @@
---
"tauri": patch
"tauri-runtime": minor
"tauri-runtime-wry": minor
---

**Breaking change::* Added the `global-shortcut` Cargo feature.
1 change: 1 addition & 0 deletions core/tauri-codegen/src/context.rs
Expand Up @@ -56,6 +56,7 @@ fn map_core_assets(
if path.extension() == Some(OsStr::new("html")) {
let mut document = parse_html(String::from_utf8_lossy(input).into_owned());

#[allow(clippy::collapsible_if)]
if csp {
#[cfg(target_os = "linux")]
::tauri_utils::html::inject_csp_token(&mut document);
Expand Down
1 change: 1 addition & 0 deletions core/tauri-runtime-wry/Cargo.toml
Expand Up @@ -41,3 +41,4 @@ macos-private-api = [
objc-exception = [ "wry/objc-exception" ]
gtk-tray = [ "wry/gtk-tray" ]
ayatana-tray = [ "wry/ayatana-tray" ]
global-shortcut = [ "tauri-runtime/global-shortcut" ]
164 changes: 164 additions & 0 deletions core/tauri-runtime-wry/src/global_shortcut.rs
@@ -0,0 +1,164 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

//! Global shortcut implementation.

use std::{
collections::HashMap,
fmt,
sync::{
mpsc::{channel, Sender},
Arc, Mutex,
},
};

use crate::{getter, Context, Message};

use tauri_runtime::{Error, GlobalShortcutManager, Result, UserEvent};
pub use wry::application::global_shortcut::ShortcutManager as WryShortcutManager;
use wry::application::{
accelerator::{Accelerator, AcceleratorId},
global_shortcut::GlobalShortcut,
};

pub type GlobalShortcutListeners = Arc<Mutex<HashMap<AcceleratorId, Box<dyn Fn() + Send>>>>;

#[derive(Debug, Clone)]
pub enum GlobalShortcutMessage {
IsRegistered(Accelerator, Sender<bool>),
Register(Accelerator, Sender<Result<GlobalShortcutWrapper>>),
Unregister(GlobalShortcutWrapper, Sender<Result<()>>),
UnregisterAll(Sender<Result<()>>),
}

#[derive(Debug, Clone)]
pub struct GlobalShortcutWrapper(GlobalShortcut);

// SAFETY: usage outside of main thread is guarded, we use the event loop on such cases.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for GlobalShortcutWrapper {}

/// Wrapper around [`WryShortcutManager`].
#[derive(Clone)]
pub struct GlobalShortcutManagerHandle<T: UserEvent> {
pub context: Context<T>,
pub shortcuts: Arc<Mutex<HashMap<String, (AcceleratorId, GlobalShortcutWrapper)>>>,
pub listeners: GlobalShortcutListeners,
}

// SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl<T: UserEvent> Sync for GlobalShortcutManagerHandle<T> {}

impl<T: UserEvent> fmt::Debug for GlobalShortcutManagerHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GlobalShortcutManagerHandle")
.field("context", &self.context)
.field("shortcuts", &self.shortcuts)
.finish()
}
}

impl<T: UserEvent> GlobalShortcutManager for GlobalShortcutManagerHandle<T> {
fn is_registered(&self, accelerator: &str) -> Result<bool> {
let (tx, rx) = channel();
getter!(
self,
rx,
Message::GlobalShortcut(GlobalShortcutMessage::IsRegistered(
accelerator.parse().expect("invalid accelerator"),
tx
))
)
}

fn register<F: Fn() + Send + 'static>(&mut self, accelerator: &str, handler: F) -> Result<()> {
let wry_accelerator: Accelerator = accelerator.parse().expect("invalid accelerator");
let id = wry_accelerator.clone().id();
let (tx, rx) = channel();
let shortcut = getter!(
self,
rx,
Message::GlobalShortcut(GlobalShortcutMessage::Register(wry_accelerator, tx))
)??;

self.listeners.lock().unwrap().insert(id, Box::new(handler));
self
.shortcuts
.lock()
.unwrap()
.insert(accelerator.into(), (id, shortcut));

Ok(())
}

fn unregister_all(&mut self) -> Result<()> {
let (tx, rx) = channel();
getter!(
self,
rx,
Message::GlobalShortcut(GlobalShortcutMessage::UnregisterAll(tx))
)??;
self.listeners.lock().unwrap().clear();
self.shortcuts.lock().unwrap().clear();
Ok(())
}

fn unregister(&mut self, accelerator: &str) -> Result<()> {
if let Some((accelerator_id, shortcut)) = self.shortcuts.lock().unwrap().remove(accelerator) {
let (tx, rx) = channel();
getter!(
self,
rx,
Message::GlobalShortcut(GlobalShortcutMessage::Unregister(shortcut, tx))
)??;
self.listeners.lock().unwrap().remove(&accelerator_id);
}
Ok(())
}
}

pub fn handle_global_shortcut_message(
message: GlobalShortcutMessage,
global_shortcut_manager: &Arc<Mutex<WryShortcutManager>>,
) {
match message {
GlobalShortcutMessage::IsRegistered(accelerator, tx) => tx
.send(
global_shortcut_manager
.lock()
.unwrap()
.is_registered(&accelerator),
)
.unwrap(),
GlobalShortcutMessage::Register(accelerator, tx) => tx
.send(
global_shortcut_manager
.lock()
.unwrap()
.register(accelerator)
.map(GlobalShortcutWrapper)
.map_err(|e| Error::GlobalShortcut(Box::new(e))),
)
.unwrap(),
GlobalShortcutMessage::Unregister(shortcut, tx) => tx
.send(
global_shortcut_manager
.lock()
.unwrap()
.unregister(shortcut.0)
.map_err(|e| Error::GlobalShortcut(Box::new(e))),
)
.unwrap(),
GlobalShortcutMessage::UnregisterAll(tx) => tx
.send(
global_shortcut_manager
.lock()
.unwrap()
.unregister_all()
.map_err(|e| Error::GlobalShortcut(Box::new(e))),
)
.unwrap(),
}
}

0 comments on commit e11878b

Please sign in to comment.