diff --git a/.changes/simplify-window-creation.md b/.changes/simplify-window-creation.md new file mode 100644 index 000000000..9db428a1d --- /dev/null +++ b/.changes/simplify-window-creation.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": minor +--- + +Use a random window id instead of `tao::window::WindowId` to not block the thread waiting for the event loop to process the window creation. diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index a424c86b8..bee76031b 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -17,6 +17,7 @@ wry = { version = "0.13.3", default-features = false, features = [ "file-drop", tauri-runtime = { version = "0.3.3", path = "../tauri-runtime" } tauri-utils = { version = "1.0.0-rc.3", path = "../tauri-utils" } uuid = { version = "0.8.2", features = [ "v4" ] } +rand = "0.8" [target."cfg(windows)".dependencies] webview2-com = "0.13.0" diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index add60d7fc..9c58b4a84 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -70,7 +70,7 @@ use wry::{ pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId}; -#[cfg(target_os = "windows")] +#[cfg(windows)] use wry::webview::WebviewExtWindows; #[cfg(target_os = "macos")] @@ -96,6 +96,8 @@ use std::{ thread::{current as current_thread, ThreadId}, }; +type WebviewId = u64; + #[cfg(feature = "system-tray")] mod system_tray; #[cfg(feature = "system-tray")] @@ -105,14 +107,27 @@ type WebContextStore = Arc, WebContext>>>; // window type WindowEventHandler = Box; type WindowEventListenersMap = Arc>>; -type WindowEventListeners = Arc>>; +type WindowEventListeners = Arc>>; // global shortcut type GlobalShortcutListeners = Arc>>>; // menu pub type MenuEventHandler = Box; -pub type MenuEventListeners = Arc>>; +pub type MenuEventListeners = Arc>>; pub type WindowMenuEventListeners = Arc>>; +#[derive(Debug, Clone, Default)] +struct WebviewIdStore(Arc>>); + +impl WebviewIdStore { + fn insert(&self, w: WindowId, id: WebviewId) { + self.0.lock().unwrap().insert(w, id); + } + + fn get(&self, w: &WindowId) -> WebviewId { + *self.0.lock().unwrap().get(w).unwrap() + } +} + macro_rules! getter { ($self: ident, $rx: expr, $message: expr) => {{ send_user_message(&$self.context, $message)?; @@ -154,6 +169,7 @@ fn send_user_message(context: &Context, message: Message) -> #[derive(Clone)] struct Context { + webview_id_map: WebviewIdStore, main_thread_id: ThreadId, proxy: WryEventLoopProxy>, window_event_listeners: WindowEventListeners, @@ -161,13 +177,60 @@ struct Context { main_thread: DispatcherMainThreadContext, } +impl Context { + fn prepare_window(&self, window_id: WebviewId) { + self + .window_event_listeners + .lock() + .unwrap() + .insert(window_id, WindowEventListenersMap::default()); + + self + .menu_event_listeners + .lock() + .unwrap() + .insert(window_id, WindowMenuEventListeners::default()); + } + + fn create_webview(&self, pending: PendingWindow>) -> Result>> { + let label = pending.label.clone(); + let menu_ids = pending.menu_ids.clone(); + let js_event_listeners = pending.js_event_listeners.clone(); + let context = self.clone(); + let window_id = rand::random(); + + self.prepare_window(window_id); + + self + .proxy + .send_event(Message::CreateWebview( + window_id, + Box::new(move |event_loop, web_context| { + create_webview(window_id, event_loop, web_context, context, pending) + }), + )) + .map_err(|_| Error::FailedToSendMessage)?; + + let dispatcher = WryDispatcher { + window_id, + context: self.clone(), + }; + Ok(DetachedWindow { + label, + dispatcher, + menu_ids, + js_event_listeners, + }) + } +} + #[derive(Debug, Clone)] struct DispatcherMainThreadContext { window_target: EventLoopWindowTarget>, web_context: WebContextStore, global_shortcut_manager: Arc>, clipboard_manager: Arc>, - windows: Arc>>, + windows: Arc>>, #[cfg(feature = "system-tray")] tray_context: TrayContext, } @@ -827,7 +890,7 @@ impl WindowBuilder for WindowBuilderWrapper { Ok(self) } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(any(windows, target_os = "linux"))] fn skip_taskbar(mut self, skip: bool) -> Self { self.inner = self.inner.with_skip_taskbar(skip); self @@ -996,12 +1059,13 @@ pub type CreateWebviewClosure = Box< pub enum Message { Task(Box), - Window(WindowId, WindowMessage), - Webview(WindowId, WebviewMessage), + Window(WebviewId, WindowMessage), + Webview(WebviewId, WebviewMessage), #[cfg(feature = "system-tray")] Tray(TrayMessage), - CreateWebview(CreateWebviewClosure, Sender), + CreateWebview(WebviewId, CreateWebviewClosure), CreateWindow( + WebviewId, Box (String, WryWindowBuilder) + Send>, Sender>>, ), @@ -1028,7 +1092,7 @@ impl Clone for Message { /// The Tauri [`Dispatch`] for [`Wry`]. #[derive(Debug, Clone)] pub struct WryDispatcher { - window_id: WindowId, + window_id: WebviewId, context: Context, } @@ -1198,33 +1262,7 @@ impl Dispatch for WryDispatcher { &mut self, pending: PendingWindow, ) -> Result> { - let (tx, rx) = channel(); - let label = pending.label.clone(); - let menu_ids = pending.menu_ids.clone(); - let js_event_listeners = pending.js_event_listeners.clone(); - let context = self.context.clone(); - - send_user_message( - &self.context, - Message::CreateWebview( - Box::new(move |event_loop, web_context| { - create_webview(event_loop, web_context, context, pending) - }), - tx, - ), - )?; - let window_id = rx.recv().unwrap(); - - let dispatcher = WryDispatcher { - window_id, - context: self.context.clone(), - }; - Ok(DetachedWindow { - label, - dispatcher, - menu_ids, - js_event_listeners, - }) + self.context.create_webview(pending) } fn set_resizable(&self, resizable: bool) -> Result<()> { @@ -1475,7 +1513,8 @@ pub struct Wry { clipboard_manager: Arc>, clipboard_manager_handle: ClipboardManagerWrapper, event_loop: EventLoop>, - windows: Arc>>, + windows: Arc>>, + webview_id_map: WebviewIdStore, web_context: WebContextStore, window_event_listeners: WindowEventListeners, menu_event_listeners: MenuEventListeners, @@ -1520,10 +1559,25 @@ impl WryHandle { f: F, ) -> Result> { let (tx, rx) = channel(); - send_user_message(&self.context, Message::CreateWindow(Box::new(f), tx))?; + send_user_message( + &self.context, + Message::CreateWindow(rand::random(), Box::new(f), tx), + )?; rx.recv().unwrap() } + /// Gets the [`WebviewId'] associated with the given [`WindowId`]. + pub fn window_id(&self, window_id: WindowId) -> WebviewId { + *self + .context + .webview_id_map + .0 + .lock() + .unwrap() + .get(&window_id) + .unwrap() + } + /// Send a message to the event loop. pub fn send_event(&self, message: Message) -> Result<()> { self @@ -1548,32 +1602,7 @@ impl RuntimeHandle for WryHandle { &self, pending: PendingWindow, ) -> Result> { - let (tx, rx) = channel(); - let label = pending.label.clone(); - let menu_ids = pending.menu_ids.clone(); - let js_event_listeners = pending.js_event_listeners.clone(); - let context = self.context.clone(); - send_user_message( - &self.context, - Message::CreateWebview( - Box::new(move |event_loop, web_context| { - create_webview(event_loop, web_context, context, pending) - }), - tx, - ), - )?; - let window_id = rx.recv().unwrap(); - - let dispatcher = WryDispatcher { - window_id, - context: self.context.clone(), - }; - Ok(DetachedWindow { - label, - dispatcher, - menu_ids, - js_event_listeners, - }) + self.context.create_webview(pending) } fn run_on_main_thread(&self, f: F) -> Result<()> { @@ -1595,6 +1624,7 @@ impl Wry { let global_shortcut_manager = Arc::new(Mutex::new(WryShortcutManager::new(&event_loop))); let clipboard_manager = Arc::new(Mutex::new(Clipboard::new())); let windows = Arc::new(Mutex::new(HashMap::default())); + let webview_id_map = WebviewIdStore::default(); let window_event_listeners = WindowEventListeners::default(); let menu_event_listeners = MenuEventListeners::default(); @@ -1602,6 +1632,7 @@ impl Wry { let tray_context = TrayContext::default(); let event_loop_context = Context { + webview_id_map: webview_id_map.clone(), main_thread_id, proxy, window_event_listeners: window_event_listeners.clone(), @@ -1634,6 +1665,7 @@ impl Wry { clipboard_manager_handle, event_loop, windows, + webview_id_map, web_context, window_event_listeners, menu_event_listeners, @@ -1674,6 +1706,7 @@ impl Runtime for Wry { fn handle(&self) -> Self::Handle { WryHandle { context: Context { + webview_id_map: self.webview_id_map.clone(), main_thread_id: self.main_thread_id, proxy: self.event_loop.create_proxy(), window_event_listeners: self.window_event_listeners.clone(), @@ -1704,89 +1737,38 @@ impl Runtime for Wry { let menu_ids = pending.menu_ids.clone(); let js_event_listeners = pending.js_event_listeners.clone(); let proxy = self.event_loop.create_proxy(); - let webview = create_webview( - &self.event_loop, - &self.web_context, - Context { - main_thread_id: self.main_thread_id, - proxy: proxy.clone(), - window_event_listeners: self.window_event_listeners.clone(), - menu_event_listeners: self.menu_event_listeners.clone(), - main_thread: DispatcherMainThreadContext { - window_target: self.event_loop.deref().clone(), - web_context: self.web_context.clone(), - global_shortcut_manager: self.global_shortcut_manager.clone(), - clipboard_manager: self.clipboard_manager.clone(), - windows: self.windows.clone(), - #[cfg(feature = "system-tray")] - tray_context: self.tray_context.clone(), - }, - }, - pending, - )?; + let window_id = rand::random(); - #[cfg(target_os = "windows")] - { - let id = webview.inner.window().id(); - if let WindowHandle::Webview(ref webview) = webview.inner { - if let Some(controller) = webview.controller() { - let proxy = self.event_loop.create_proxy(); - let mut token = EventRegistrationToken::default(); - unsafe { - controller.GotFocus( - FocusChangedEventHandler::create(Box::new(move |_, _| { - let _ = proxy.send_event(Message::Webview( - id, - WebviewMessage::WebviewEvent(WebviewEvent::Focused(true)), - )); - Ok(()) - })), - &mut token, - ) - } - .unwrap(); - let proxy = self.event_loop.create_proxy(); - unsafe { - controller.LostFocus( - FocusChangedEventHandler::create(Box::new(move |_, _| { - let _ = proxy.send_event(Message::Webview( - id, - WebviewMessage::WebviewEvent(WebviewEvent::Focused(false)), - )); - Ok(()) - })), - &mut token, - ) - } - .unwrap(); - } - } - } - - let dispatcher = WryDispatcher { - window_id: webview.inner.window().id(), - context: Context { - main_thread_id: self.main_thread_id, - proxy, - window_event_listeners: self.window_event_listeners.clone(), - menu_event_listeners: self.menu_event_listeners.clone(), - main_thread: DispatcherMainThreadContext { - window_target: self.event_loop.deref().clone(), - web_context: self.web_context.clone(), - global_shortcut_manager: self.global_shortcut_manager.clone(), - clipboard_manager: self.clipboard_manager.clone(), - windows: self.windows.clone(), - #[cfg(feature = "system-tray")] - tray_context: self.tray_context.clone(), - }, + let context = Context { + webview_id_map: self.webview_id_map.clone(), + main_thread_id: self.main_thread_id, + proxy, + window_event_listeners: self.window_event_listeners.clone(), + menu_event_listeners: self.menu_event_listeners.clone(), + main_thread: DispatcherMainThreadContext { + window_target: self.event_loop.deref().clone(), + web_context: self.web_context.clone(), + global_shortcut_manager: self.global_shortcut_manager.clone(), + clipboard_manager: self.clipboard_manager.clone(), + windows: self.windows.clone(), + #[cfg(feature = "system-tray")] + tray_context: self.tray_context.clone(), }, }; - self - .windows - .lock() - .unwrap() - .insert(webview.inner.window().id(), webview); + context.prepare_window(window_id); + + let webview = create_webview( + window_id, + &self.event_loop, + &self.web_context, + context.clone(), + pending, + )?; + + let dispatcher = WryDispatcher { window_id, context }; + + self.windows.lock().unwrap().insert(window_id, webview); Ok(DetachedWindow { label, @@ -1861,6 +1843,7 @@ impl Runtime for Wry { fn run_iteration) + 'static>(&mut self, mut callback: F) -> RunIteration { use wry::application::platform::run_return::EventLoopExtRunReturn; let windows = self.windows.clone(); + let webview_id_map = self.webview_id_map.clone(); let web_context = &self.web_context; let window_event_listeners = self.window_event_listeners.clone(); let menu_event_listeners = self.menu_event_listeners.clone(); @@ -1886,6 +1869,7 @@ impl Runtime for Wry { EventLoopIterationContext { callback: &mut callback, windows: windows.clone(), + webview_id_map: webview_id_map.clone(), window_event_listeners: &window_event_listeners, global_shortcut_manager: global_shortcut_manager.clone(), global_shortcut_manager_handle: &global_shortcut_manager_handle, @@ -1903,6 +1887,7 @@ impl Runtime for Wry { fn run) + 'static>(self, mut callback: F) { let windows = self.windows.clone(); + let webview_id_map = self.webview_id_map.clone(); let web_context = self.web_context; let window_event_listeners = self.window_event_listeners.clone(); let menu_event_listeners = self.menu_event_listeners.clone(); @@ -1919,6 +1904,7 @@ impl Runtime for Wry { control_flow, EventLoopIterationContext { callback: &mut callback, + webview_id_map: webview_id_map.clone(), windows: windows.clone(), window_event_listeners: &window_event_listeners, global_shortcut_manager: global_shortcut_manager.clone(), @@ -1936,7 +1922,8 @@ impl Runtime for Wry { pub struct EventLoopIterationContext<'a, T: UserEvent> { callback: &'a mut (dyn FnMut(RunEvent) + 'static), - windows: Arc>>, + webview_id_map: WebviewIdStore, + windows: Arc>>, window_event_listeners: &'a WindowEventListeners, global_shortcut_manager: Arc>, global_shortcut_manager_handle: &'a GlobalShortcutManagerHandle, @@ -1951,7 +1938,7 @@ struct UserMessageContext<'a> { global_shortcut_manager: Arc>, clipboard_manager: Arc>, menu_event_listeners: &'a MenuEventListeners, - windows: Arc>>, + windows: Arc>>, #[cfg(feature = "system-tray")] tray_context: &'a TrayContext, } @@ -2081,7 +2068,7 @@ fn handle_user_message( window.set_window_icon(Some(icon)); } WindowMessage::SetSkipTaskbar(_skip) => { - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(any(windows, target_os = "linux"))] window.set_skip_taskbar(_skip); } WindowMessage::DragWindow => { @@ -2147,34 +2134,30 @@ fn handle_user_message( } } }, - Message::CreateWebview(handler, sender) => match handler(event_loop, web_context) { + Message::CreateWebview(window_id, handler) => match handler(event_loop, web_context) { Ok(webview) => { - let window_id = webview.inner.window().id(); windows .lock() .expect("poisoned webview collection") .insert(window_id, webview); - sender.send(window_id).unwrap(); } Err(e) => { #[cfg(debug_assertions)] eprintln!("{}", e); } }, - Message::CreateWindow(handler, sender) => { + Message::CreateWindow(window_id, handler, sender) => { let (label, builder) = handler(); if let Ok(window) = builder.build(event_loop) { - let window_id = window.id(); - window_event_listeners .lock() .unwrap() - .insert(window.id(), WindowEventListenersMap::default()); + .insert(window_id, WindowEventListenersMap::default()); menu_event_listeners .lock() .unwrap() - .insert(window.id(), WindowMenuEventListeners::default()); + .insert(window_id, WindowMenuEventListeners::default()); let w = Arc::new(window); @@ -2299,6 +2282,7 @@ fn handle_event_loop( ) -> RunIteration { let EventLoopIterationContext { callback, + webview_id_map, windows, window_event_listeners, global_shortcut_manager, @@ -2347,6 +2331,7 @@ fn handle_event_loop( menu_item_id: menu_id.0, }; let window_menu_event_listeners = { + let window_id = webview_id_map.get(&window_id); let listeners = menu_event_listeners.lock().unwrap(); listeners.get(&window_id).cloned().unwrap_or_default() }; @@ -2390,6 +2375,7 @@ fn handle_event_loop( Event::WindowEvent { event, window_id, .. } => { + let window_id = webview_id_map.get(&window_id); // NOTE(amrbashir): we handle this event here instead of `match` statement below because // we want to focus the webview as soon as possible, especially on windows. if event == WryWindowEvent::Focused(true) { @@ -2496,8 +2482,8 @@ fn handle_event_loop( fn on_close_requested<'a, T: UserEvent>( callback: &'a mut (dyn FnMut(RunEvent) + 'static), - window_id: WindowId, - windows: Arc>>, + window_id: WebviewId, + windows: Arc>>, control_flow: &mut ControlFlow, window_event_listeners: &WindowEventListeners, menu_event_listeners: MenuEventListeners, @@ -2545,8 +2531,8 @@ fn on_close_requested<'a, T: UserEvent>( fn on_window_close<'a, T: UserEvent>( callback: &'a mut (dyn FnMut(RunEvent) + 'static), - window_id: WindowId, - mut windows: MutexGuard<'a, HashMap>, + window_id: WebviewId, + mut windows: MutexGuard<'a, HashMap>, control_flow: &mut ControlFlow, #[cfg(target_os = "linux")] window_event_listeners: &WindowEventListeners, menu_event_listeners: MenuEventListeners, @@ -2641,6 +2627,7 @@ fn to_wry_menu( } fn create_webview( + window_id: WebviewId, event_loop: &EventLoopWindowTarget>, web_context: &WebContextStore, context: Context, @@ -2658,6 +2645,9 @@ fn create_webview( js_event_listeners, .. } = pending; + let webview_id_map = context.webview_id_map.clone(); + #[cfg(windows)] + let proxy = context.proxy.clone(); let is_window_transparent = window_builder.inner.window.transparent; let menu_items = if let Some(menu) = window_builder.menu { @@ -2670,18 +2660,6 @@ fn create_webview( }; let window = window_builder.inner.build(event_loop).unwrap(); - context - .window_event_listeners - .lock() - .unwrap() - .insert(window.id(), WindowEventListenersMap::default()); - - context - .menu_event_listeners - .lock() - .unwrap() - .insert(window.id(), WindowMenuEventListeners::default()); - if window_builder.center { let _ = center_window(&window, window.inner_size()); } @@ -2753,6 +2731,42 @@ fn create_webview( .build() .map_err(|e| Error::CreateWebview(Box::new(e)))?; + webview_id_map.insert(webview.window().id(), window_id); + + #[cfg(windows)] + { + if let Some(controller) = webview.controller() { + let proxy_ = proxy.clone(); + let mut token = EventRegistrationToken::default(); + unsafe { + controller.GotFocus( + FocusChangedEventHandler::create(Box::new(move |_, _| { + let _ = proxy_.send_event(Message::Webview( + window_id, + WebviewMessage::WebviewEvent(WebviewEvent::Focused(true)), + )); + Ok(()) + })), + &mut token, + ) + } + .unwrap(); + unsafe { + controller.LostFocus( + FocusChangedEventHandler::create(Box::new(move |_, _| { + let _ = proxy.send_event(Message::Webview( + window_id, + WebviewMessage::WebviewEvent(WebviewEvent::Focused(false)), + )); + Ok(()) + })), + &mut token, + ) + } + .unwrap(); + } + } + Ok(WindowWrapper { label, inner: WindowHandle::Webview(webview), @@ -2772,7 +2786,13 @@ fn create_ipc_handler( handler( DetachedWindow { dispatcher: WryDispatcher { - window_id: window.id(), + window_id: *context + .webview_id_map + .0 + .lock() + .unwrap() + .get(&window.id()) + .unwrap(), context: context.clone(), }, label: label.clone(), @@ -2789,11 +2809,12 @@ fn create_file_drop_handler( context: &Context, ) -> Box bool + 'static> { let window_event_listeners = context.window_event_listeners.clone(); + let webview_id_map = context.webview_id_map.clone(); Box::new(move |window, event| { let event: FileDropEvent = FileDropEventWrapper(event).into(); let window_event = WindowEvent::FileDrop(event); let listeners = window_event_listeners.lock().unwrap(); - if let Some(window_listeners) = listeners.get(&window.id()) { + if let Some(window_listeners) = listeners.get(&webview_id_map.get(&window.id())) { let listeners_map = window_listeners.lock().unwrap(); let has_listener = !listeners_map.is_empty(); for listener in listeners_map.values() { diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index a5c3f87c4..9798c783f 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -51,7 +51,7 @@ uuid = { version = "0.8", features = [ "v4" ] } url = { version = "2.2" } anyhow = "1.0" thiserror = "1.0" -once_cell = "1.9" +once_cell = "1.10" tauri-runtime = { version = "0.3.3", path = "../tauri-runtime" } tauri-macros = { version = "1.0.0-rc.3", path = "../tauri-macros" } tauri-utils = { version = "1.0.0-rc.3", path = "../tauri-utils" } diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 42d3cfcbe..edde7a00f 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -247,7 +247,10 @@ impl AppHandle { ) -> crate::Result<()> { self .runtime_handle - .send_event(tauri_runtime_wry::Message::Window(window_id, message)) + .send_event(tauri_runtime_wry::Message::Window( + self.runtime_handle.window_id(window_id), + message, + )) .map_err(Into::into) } }