Skip to content

Commit

Permalink
feat(tauri-runtime-wry): add plugin API (#4094)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed May 10, 2022
1 parent 87a2c2f commit c8e0e5b
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 54 deletions.
5 changes: 5 additions & 0 deletions .changes/tauri-runtime-wry-plugin.md
@@ -0,0 +1,5 @@
---
"tauri-runtime-wry": patch
---

Added the `plugin` method to the `Wry` runtime, allowing extensions to the event loop.
5 changes: 5 additions & 0 deletions .changes/wry-plugin.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Adds the `App#wry_plugin` API to inject a plugin for the wry integration.
139 changes: 105 additions & 34 deletions core/tauri-runtime-wry/src/lib.rs
Expand Up @@ -70,6 +70,7 @@ use wry::{
webview::{FileDropEvent as WryFileDropEvent, WebContext, WebView, WebViewBuilder},
};

pub use wry;
pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId};

#[cfg(windows)]
Expand Down Expand Up @@ -98,7 +99,7 @@ use std::{
thread::{current as current_thread, ThreadId},
};

type WebviewId = u64;
pub type WebviewId = u64;

mod webview;
pub use webview::Webview;
Expand All @@ -118,25 +119,25 @@ mod clipboard;
#[cfg(feature = "clipboard")]
use clipboard::*;

type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
pub type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
// window
type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
type WindowEventListenersMap = Arc<Mutex<HashMap<Uuid, WindowEventHandler>>>;
type WindowEventListeners = Arc<Mutex<HashMap<WebviewId, WindowEventListenersMap>>>;
pub type WindowEventListeners = Arc<Mutex<HashMap<WebviewId, WindowEventListenersMap>>>;
// menu
pub type MenuEventHandler = Box<dyn Fn(&MenuEvent) + Send>;
pub type MenuEventListeners = Arc<Mutex<HashMap<WebviewId, WindowMenuEventListeners>>>;
pub type WindowMenuEventListeners = Arc<Mutex<HashMap<Uuid, MenuEventHandler>>>;

#[derive(Debug, Clone, Default)]
struct WebviewIdStore(Arc<Mutex<HashMap<WindowId, WebviewId>>>);
pub struct WebviewIdStore(Arc<Mutex<HashMap<WindowId, WebviewId>>>);

impl WebviewIdStore {
fn insert(&self, w: WindowId, id: WebviewId) {
pub fn insert(&self, w: WindowId, id: WebviewId) {
self.0.lock().unwrap().insert(w, id);
}

fn get(&self, w: &WindowId) -> WebviewId {
pub fn get(&self, w: &WindowId) -> WebviewId {
*self.0.lock().unwrap().get(w).unwrap()
}

Expand Down Expand Up @@ -192,7 +193,7 @@ fn send_user_message<T: UserEvent>(context: &Context<T>, message: Message<T>) ->

#[derive(Clone)]
pub struct Context<T: UserEvent> {
webview_id_map: WebviewIdStore,
pub webview_id_map: WebviewIdStore,
main_thread_id: ThreadId,
proxy: WryEventLoopProxy<Message<T>>,
window_event_listeners: WindowEventListeners,
Expand Down Expand Up @@ -495,7 +496,7 @@ impl TryFrom<WindowIcon> for WryIcon {
}
}

struct WindowEventWrapper(Option<WindowEvent>);
pub struct WindowEventWrapper(pub Option<WindowEvent>);

impl WindowEventWrapper {
fn parse(webview: &Option<WindowHandle>, event: &WryWindowEvent<'_>) -> Self {
Expand Down Expand Up @@ -1555,7 +1556,7 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {

#[cfg(feature = "system-tray")]
#[derive(Clone, Default)]
struct TrayContext {
pub struct TrayContext {
tray: Arc<Mutex<Option<Arc<Mutex<WrySystemTray>>>>>,
listeners: SystemTrayEventListeners,
items: SystemTrayItems,
Expand Down Expand Up @@ -1616,10 +1617,24 @@ impl<T: UserEvent> EventLoopProxy<T> for EventProxy<T> {
}
}

pub trait Plugin<T: UserEvent> {
fn on_event(
&mut self,
event: &Event<Message<T>>,
event_loop: &EventLoopWindowTarget<Message<T>>,
proxy: &WryEventLoopProxy<Message<T>>,
control_flow: &mut ControlFlow,
context: EventLoopIterationContext<'_, T>,
web_context: &WebContextStore,
) -> bool;
}

/// A Tauri [`Runtime`] wrapper around wry.
pub struct Wry<T: UserEvent> {
main_thread_id: ThreadId,

plugins: Vec<Box<dyn Plugin<T>>>,

#[cfg(feature = "global-shortcut")]
global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
#[cfg(feature = "global-shortcut")]
Expand Down Expand Up @@ -1791,6 +1806,8 @@ impl<T: UserEvent> Wry<T> {
Ok(Self {
main_thread_id,

plugins: Default::default(),

#[cfg(feature = "global-shortcut")]
global_shortcut_manager,
#[cfg(feature = "global-shortcut")]
Expand All @@ -1815,6 +1832,10 @@ impl<T: UserEvent> Wry<T> {
tray_context,
})
}

pub fn plugin<P: Plugin<T> + 'static>(&mut self, plugin: P) {
self.plugins.push(Box::new(plugin));
}
}

impl<T: UserEvent> Runtime<T> for Wry<T> {
Expand Down Expand Up @@ -1999,6 +2020,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
let windows = self.windows.clone();
let webview_id_map = self.webview_id_map.clone();
let web_context = &self.web_context;
let plugins = &mut self.plugins;
let window_event_listeners = self.window_event_listeners.clone();
let menu_event_listeners = self.menu_event_listeners.clone();
#[cfg(feature = "system-tray")]
Expand All @@ -2013,6 +2035,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
let clipboard_manager = self.clipboard_manager.clone();
let mut iteration = RunIteration::default();

let proxy = self.event_loop.create_proxy();

self
.event_loop
.run_return(|event, event_loop, control_flow| {
Expand All @@ -2021,6 +2045,34 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
*control_flow = ControlFlow::Exit;
}

for p in plugins.iter_mut() {
let prevent_default = p.on_event(
&event,
event_loop,
&proxy,
control_flow,
EventLoopIterationContext {
callback: &mut callback,
webview_id_map: webview_id_map.clone(),
windows: windows.clone(),
window_event_listeners: &window_event_listeners,
#[cfg(feature = "global-shortcut")]
global_shortcut_manager: global_shortcut_manager.clone(),
#[cfg(feature = "global-shortcut")]
global_shortcut_manager_handle: &global_shortcut_manager_handle,
#[cfg(feature = "clipboard")]
clipboard_manager: clipboard_manager.clone(),
menu_event_listeners: &menu_event_listeners,
#[cfg(feature = "system-tray")]
tray_context: &tray_context,
},
web_context,
);
if prevent_default {
return;
}
}

iteration = handle_event_loop(
event,
event_loop,
Expand Down Expand Up @@ -2051,6 +2103,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
let windows = self.windows.clone();
let webview_id_map = self.webview_id_map.clone();
let web_context = self.web_context;
let mut plugins = self.plugins;
let window_event_listeners = self.window_event_listeners.clone();
let menu_event_listeners = self.menu_event_listeners.clone();

Expand All @@ -2065,7 +2118,36 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
#[cfg(feature = "clipboard")]
let clipboard_manager = self.clipboard_manager.clone();

let proxy = self.event_loop.create_proxy();

self.event_loop.run(move |event, event_loop, control_flow| {
for p in &mut plugins {
let prevent_default = p.on_event(
&event,
event_loop,
&proxy,
control_flow,
EventLoopIterationContext {
callback: &mut callback,
webview_id_map: webview_id_map.clone(),
windows: windows.clone(),
window_event_listeners: &window_event_listeners,
#[cfg(feature = "global-shortcut")]
global_shortcut_manager: global_shortcut_manager.clone(),
#[cfg(feature = "global-shortcut")]
global_shortcut_manager_handle: &global_shortcut_manager_handle,
#[cfg(feature = "clipboard")]
clipboard_manager: clipboard_manager.clone(),
menu_event_listeners: &menu_event_listeners,
#[cfg(feature = "system-tray")]
tray_context: &tray_context,
},
&web_context,
);
if prevent_default {
return;
}
}
handle_event_loop(
event,
event_loop,
Expand All @@ -2092,19 +2174,19 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
}

pub struct EventLoopIterationContext<'a, T: UserEvent> {
callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
webview_id_map: WebviewIdStore,
windows: Arc<Mutex<HashMap<WebviewId, WindowWrapper>>>,
window_event_listeners: &'a WindowEventListeners,
pub callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
pub webview_id_map: WebviewIdStore,
pub windows: Arc<Mutex<HashMap<WebviewId, WindowWrapper>>>,
pub window_event_listeners: &'a WindowEventListeners,
#[cfg(feature = "global-shortcut")]
global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
pub global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
#[cfg(feature = "global-shortcut")]
global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle<T>,
pub global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle<T>,
#[cfg(feature = "clipboard")]
clipboard_manager: Arc<Mutex<Clipboard>>,
menu_event_listeners: &'a MenuEventListeners,
pub clipboard_manager: Arc<Mutex<Clipboard>>,
pub menu_event_listeners: &'a MenuEventListeners,
#[cfg(feature = "system-tray")]
tray_context: &'a TrayContext,
pub tray_context: &'a TrayContext,
}

struct UserMessageContext<'a> {
Expand Down Expand Up @@ -2653,16 +2735,13 @@ fn handle_event_loop<T: UserEvent>(

match event {
WryWindowEvent::CloseRequested => {
on_close_requested(
callback,
window_id,
windows.clone(),
window_event_listeners,
menu_event_listeners.clone(),
);
on_close_requested(callback, window_id, windows.clone(), window_event_listeners);
}
WryWindowEvent::Destroyed => {
if windows.lock().unwrap().remove(&window_id).is_some() {
menu_event_listeners.lock().unwrap().remove(&window_id);
window_event_listeners.lock().unwrap().remove(&window_id);

let is_empty = windows.lock().unwrap().is_empty();
if is_empty {
let (tx, rx) = channel();
Expand Down Expand Up @@ -2695,11 +2774,7 @@ fn handle_event_loop<T: UserEvent>(
}
Event::UserEvent(message) => match message {
Message::Window(id, WindowMessage::Close) => {
on_window_close(
id,
windows.lock().expect("poisoned webview collection"),
menu_event_listeners.clone(),
);
on_window_close(id, windows.lock().expect("poisoned webview collection"));
}
Message::UserEvent(t) => callback(RunEvent::UserEvent(t)),
message => {
Expand Down Expand Up @@ -2736,7 +2811,6 @@ fn on_close_requested<'a, T: UserEvent>(
window_id: WebviewId,
windows: Arc<Mutex<HashMap<WebviewId, WindowWrapper>>>,
window_event_listeners: &WindowEventListeners,
menu_event_listeners: MenuEventListeners,
) {
let (tx, rx) = channel();
let windows_guard = windows.lock().expect("poisoned webview collection");
Expand Down Expand Up @@ -2765,7 +2839,6 @@ fn on_close_requested<'a, T: UserEvent>(
on_window_close(
window_id,
windows.lock().expect("poisoned webview collection"),
menu_event_listeners,
);
}
}
Expand All @@ -2774,11 +2847,9 @@ fn on_close_requested<'a, T: UserEvent>(
fn on_window_close(
window_id: WebviewId,
mut windows: MutexGuard<'_, HashMap<WebviewId, WindowWrapper>>,
menu_event_listeners: MenuEventListeners,
) {
if let Some(mut window_wrapper) = windows.get_mut(&window_id) {
window_wrapper.inner = None;
menu_event_listeners.lock().unwrap().remove(&window_id);
}
}

Expand Down
15 changes: 15 additions & 0 deletions core/tauri/src/app.rs
Expand Up @@ -470,6 +470,21 @@ impl<R: Runtime> ManagerBase<R> for App<R> {
}
}

#[cfg(feature = "wry")]
impl App<crate::Wry> {
/// Adds a [`tauri_runtime_wry::Plugin`].
///
/// # Stability
///
/// This API is unstable.
pub fn wry_plugin<P: tauri_runtime_wry::Plugin<EventLoopMessage> + 'static>(
&mut self,
plugin: P,
) {
self.runtime.as_mut().unwrap().plugin(plugin);
}
}

macro_rules! shared_app_impl {
($app: ty) => {
impl<R: Runtime> $app {
Expand Down
36 changes: 16 additions & 20 deletions core/tests/app-updater/tests/update.rs
Expand Up @@ -116,20 +116,18 @@ fn update_app() {

let cli_bin_path = if let Some(p) = get_cli_bin_path(&cli_dir, false) {
p
} else if let Some(p) = get_cli_bin_path(&cli_dir, true) {
p
} else {
if let Some(p) = get_cli_bin_path(&cli_dir, true) {
p
} else {
let status = Command::new("cargo")
.arg("build")
.current_dir(&cli_dir)
.status()
.expect("failed to run cargo");
if !status.success() {
panic!("failed to build CLI");
}
get_cli_bin_path(&cli_dir, true).expect("cargo did not build the Tauri CLI")
let status = Command::new("cargo")
.arg("build")
.current_dir(&cli_dir)
.status()
.expect("failed to run cargo");
if !status.success() {
panic!("failed to build CLI");
}
get_cli_bin_path(&cli_dir, true).expect("cargo did not build the Tauri CLI")
};

let mut config = Config {
Expand Down Expand Up @@ -214,15 +212,13 @@ fn update_app() {
Command::new(root_dir.join("target/debug/app-updater.exe"))
} else if cfg!(target_os = "macos") {
Command::new(bundle_path(&root_dir, "0.1.0").join("Contents/MacOS/app-updater"))
} else if std::env::var("CI").map(|v| v == "true").unwrap_or_default() {
let mut c = Command::new("xvfb-run");
c.arg("--auto-servernum")
.arg(bundle_path(&root_dir, "0.1.0"));
c
} else {
if std::env::var("CI").map(|v| v == "true").unwrap_or_default() {
let mut c = Command::new("xvfb-run");
c.arg("--auto-servernum")
.arg(bundle_path(&root_dir, "0.1.0"));
c
} else {
Command::new(bundle_path(&root_dir, "0.1.0"))
}
Command::new(bundle_path(&root_dir, "0.1.0"))
};

let status = binary_cmd.status().expect("failed to run app");
Expand Down

0 comments on commit c8e0e5b

Please sign in to comment.