Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement global shortcut on Linux, close #307 #308

Merged
merged 5 commits into from Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/linux-shortcuts.md
@@ -0,0 +1,5 @@
---
"tao": patch
---

Add global shortcut support on Linux (both x11 and wayland).
wusyong marked this conversation as resolved.
Show resolved Hide resolved
74 changes: 54 additions & 20 deletions src/platform_impl/linux/event_loop.rs
Expand Up @@ -3,7 +3,7 @@

use std::{
cell::RefCell,
collections::{HashSet, VecDeque},
collections::{HashMap, HashSet, VecDeque},
error::Error,
process,
rc::Rc,
Expand All @@ -17,7 +17,6 @@ use glib::{source::Priority, Continue, MainContext};
use gtk::{prelude::*, AboutDialog, Inhibit};

use crate::{
accelerator::AcceleratorId,
dpi::{LogicalPosition, LogicalSize},
event::{ElementState, Event, MouseButton, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
Expand All @@ -29,6 +28,7 @@ use crate::{
};

use super::{
global_shortcut::ShortcutEvent,
keyboard,
monitor::MonitorHandle,
window::{WindowId, WindowRequest},
Expand All @@ -44,6 +44,8 @@ pub struct EventLoopWindowTarget<T> {
pub(crate) windows: Rc<RefCell<HashSet<WindowId>>>,
/// Window requests sender
pub(crate) window_requests_tx: glib::Sender<(WindowId, WindowRequest)>,
/// Global shortcut requests sender
pub(crate) shortcut_requests_tx: glib::Sender<ShortcutEvent>,
_marker: std::marker::PhantomData<T>,
}

Expand Down Expand Up @@ -116,13 +118,17 @@ impl<T: 'static> EventLoop<T> {
// Create event loop window target.
let (window_requests_tx, window_requests_rx) = glib::MainContext::channel(Priority::default());
let window_requests_tx_ = window_requests_tx.clone();
let (shortcut_requests_tx, shortcut_requests_rx) =
glib::MainContext::channel(Priority::default());
let shortcuts = Rc::new(RefCell::new(HashMap::new()));
let display = gdk::Display::default()
.expect("GdkDisplay not found. This usually means `gkt_init` hasn't called yet.");
let window_target = EventLoopWindowTarget {
display,
app,
windows: Rc::new(RefCell::new(HashSet::new())),
window_requests_tx,
shortcut_requests_tx,
_marker: std::marker::PhantomData,
};

Expand All @@ -136,6 +142,25 @@ impl<T: 'static> EventLoop<T> {
Continue(true)
});

// Global Shortcut Event Request
let shortcuts_ = shortcuts.clone();
shortcut_requests_rx.attach(Some(&context), move |event| {
let mut shortcuts = shortcuts_.borrow_mut();
match event {
ShortcutEvent::Register(accelerator) => {
shortcuts.insert(accelerator.clone().id(), accelerator);
}
ShortcutEvent::UnRegister(accelerator) => {
shortcuts.remove(&accelerator.id());
}
ShortcutEvent::UnRegisterAll => {
shortcuts.clear();
}
}

Continue(true)
});

// Window Request
window_requests_rx.attach(Some(&context), move |(id, request)| {
if let Some(window) = app_.window_by_id(id.0) {
Expand Down Expand Up @@ -562,10 +587,11 @@ impl<T: 'static> EventLoop<T> {
});

let tx_clone = event_tx.clone();
let shortcuts_ = shortcuts.clone();
let keyboard_handler = Rc::new(move |event_key: EventKey, element_state| {
// if we have a modifier lets send it
let mut mods = keyboard::get_modifiers(event_key.clone());
if !mods.is_empty() {
wusyong marked this conversation as resolved.
Show resolved Hide resolved
// if we have a modifier lets send it
if event_key.is_modifier() {
// if we release the modifier tell the world
if ElementState::Released == element_state {
mods = ModifiersState::empty();
Expand All @@ -590,6 +616,8 @@ impl<T: 'static> EventLoop<T> {
let event = keyboard::make_key_event(&event_key, false, None, element_state);

if let Some(event) = event {
let key = event.physical_key;

if let Err(e) = tx_clone.send(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::KeyboardInput {
Expand All @@ -600,6 +628,21 @@ impl<T: 'static> EventLoop<T> {
}) {
log::warn!("Failed to send keyboard event to event channel: {}", e);
}

if let Ok(shortcuts) = shortcuts_.try_borrow() {
for accelerator in (*shortcuts).values() {
if accelerator.mods.contains(mods) && accelerator.key == key {
if let Err(e) =
tx_clone.send(Event::GlobalShortcutEvent(accelerator.clone().id()))
{
log::warn!(
"Failed to send global shortcut event to event channel: {}",
e
);
}
}
}
}
amrbashir marked this conversation as resolved.
Show resolved Hide resolved
}
Continue(true)
});
Expand Down Expand Up @@ -723,25 +766,16 @@ impl<T: 'static> EventLoop<T> {
menubar.show_all();
}
}
WindowRequest::GlobalHotKey(_hotkey_id) => {}
}
} else if id == WindowId::dummy() {
match request {
WindowRequest::GlobalHotKey(hotkey_id) => {
if let Err(e) = event_tx.send(Event::GlobalShortcutEvent(AcceleratorId(hotkey_id))) {
log::warn!("Failed to send global hotkey event to event channel: {}", e);
}
}
WindowRequest::Menu((None, Some(menu_id))) => {
if let Err(e) = event_tx.send(Event::MenuEvent {
window_id: None,
menu_id,
origin: MenuType::ContextMenu,
}) {
log::warn!("Failed to send status bar event to event channel: {}", e);
}
if let WindowRequest::Menu((None, Some(menu_id))) = request {
if let Err(e) = event_tx.send(Event::MenuEvent {
window_id: None,
menu_id,
origin: MenuType::ContextMenu,
}) {
log::warn!("Failed to send status bar event to event channel: {}", e);
}
_ => {}
}
}
Continue(true)
Expand Down