Skip to content

Commit

Permalink
fix(tauri-runtime-wry): ensure tray is created when event loop ready (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog authored Aug 15, 2024
1 parent 937849f commit c3a90e5
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changes/ensure-tray-created-ready.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri-runtime-wry": patch:bug
---

Ensure system tray is created when the event loop is ready. Menu item modifications are not applied unless it is ready.
100 changes: 77 additions & 23 deletions core/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
//! The [`wry`] Tauri [`Runtime`].
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle};
use std::{collections::BTreeMap, rc::Rc};
use std::{
collections::BTreeMap,
rc::Rc,
sync::atomic::{AtomicBool, Ordering},
};
use tauri_runtime::{
http::{header::CONTENT_TYPE, Request as HttpRequest, RequestParts, Response as HttpResponse},
menu::{AboutMetadata, CustomMenuItem, Menu, MenuEntry, MenuHash, MenuId, MenuItem, MenuUpdate},
Expand Down Expand Up @@ -196,6 +200,7 @@ pub struct Context<T: UserEvent> {
main_thread_id: ThreadId,
pub proxy: WryEventLoopProxy<Message<T>>,
main_thread: DispatcherMainThreadContext<T>,
pub is_event_loop_ready: Arc<AtomicBool>,
}

impl<T: UserEvent> Context<T> {
Expand Down Expand Up @@ -1835,8 +1840,12 @@ pub struct Wry<T: UserEvent> {
clipboard_manager_handle: ClipboardManagerWrapper,

event_loop: EventLoop<Message<T>>,

pending_ready_tasks: RefCell<Vec<PendingReadyTask<T>>>,
}

type PendingReadyTask<T> = Box<dyn FnOnce(&EventLoopWindowTarget<Message<T>>)>;

impl<T: UserEvent> fmt::Debug for Wry<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Wry");
Expand Down Expand Up @@ -1952,6 +1961,8 @@ impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
context: self.context.clone(),
id,
proxy: self.context.proxy.clone(),
#[allow(clippy::arc_with_non_send_sync)]
pending: PendingSystemTray(Arc::new(RefCell::new(None))),
})
}

Expand Down Expand Up @@ -2006,6 +2017,7 @@ impl<T: UserEvent> Wry<T> {
#[cfg(feature = "tracing")]
active_tracing_spans: Default::default(),
},
is_event_loop_ready: Arc::new(AtomicBool::new(false)),
};

#[cfg(all(desktop, feature = "global-shortcut"))]
Expand Down Expand Up @@ -2033,6 +2045,8 @@ impl<T: UserEvent> Wry<T> {
clipboard_manager_handle,

event_loop,

pending_ready_tasks: RefCell::new(Vec::new()),
})
}

Expand Down Expand Up @@ -2129,34 +2143,48 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
}

#[cfg(all(desktop, feature = "system-tray"))]
fn system_tray(&self, mut system_tray: SystemTray) -> Result<Self::TrayHandler> {
fn system_tray(&self, system_tray: SystemTray) -> Result<Self::TrayHandler> {
let system_tray_manager = self.context.main_thread.system_tray_manager.clone();

let id = system_tray.id;
let mut listeners = Vec::new();
if let Some(l) = system_tray.on_event.take() {
#[allow(clippy::arc_with_non_send_sync)]
listeners.push(Arc::new(l));
}
let (tray, items) = create_tray(WryTrayId(id), system_tray, &self.event_loop)?;
#[allow(clippy::arc_with_non_send_sync)]
let pending = PendingSystemTray(Arc::new(RefCell::new(Some(system_tray))));

let pending_ = pending.clone();
self
.context
.main_thread
.system_tray_manager
.trays
.lock()
.unwrap()
.insert(
id,
TrayContext {
tray: Arc::new(TrayCell(RefCell::new(Some(tray)))),
listeners: Arc::new(TrayListenersCell(RefCell::new(listeners))),
items: Arc::new(TrayItemsCell(RefCell::new(items))),
},
);
.pending_ready_tasks
.borrow_mut()
.push(Box::new(move |event_loop| {
if let Some(mut system_tray) = pending_.0.borrow_mut().take() {
let mut listeners = Vec::new();
if let Some(l) = system_tray.on_event.take() {
#[allow(clippy::arc_with_non_send_sync)]
listeners.push(Arc::new(l));
}

match create_tray(WryTrayId(id), system_tray, event_loop) {
Ok((tray, items)) => {
system_tray_manager.trays.lock().unwrap().insert(
id,
TrayContext {
tray: Arc::new(TrayCell(RefCell::new(Some(tray)))),
listeners: Arc::new(TrayListenersCell(RefCell::new(listeners))),
items: Arc::new(TrayItemsCell(RefCell::new(items))),
},
);
}
Err(e) => {
eprintln!("failed to build tray: {e}");
}
}
}
}));

Ok(SystemTrayHandle {
context: self.context.clone(),
id,
proxy: self.event_loop.create_proxy(),
proxy: self.context.proxy.clone(),
pending,
})
}

Expand Down Expand Up @@ -2202,6 +2230,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {

#[cfg(desktop)]
fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, mut callback: F) -> RunIteration {
use std::sync::atomic::Ordering;

use wry::application::platform::run_return::EventLoopExtRunReturn;
let windows = self.context.main_thread.windows.clone();
let webview_id_map = self.context.webview_id_map.clone();
Expand All @@ -2210,6 +2240,11 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
#[cfg(all(desktop, feature = "system-tray"))]
let system_tray_manager = self.context.main_thread.system_tray_manager.clone();

let is_event_loop_ready = self.context.is_event_loop_ready.clone();
is_event_loop_ready.store(false, Ordering::Relaxed);

let pending_ready_tasks = &self.pending_ready_tasks;

#[cfg(feature = "tracing")]
let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone();

Expand Down Expand Up @@ -2248,6 +2283,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
system_tray_manager: system_tray_manager.clone(),
#[cfg(feature = "tracing")]
active_tracing_spans: active_tracing_spans.clone(),
pending_ready_tasks,
is_event_loop_ready: is_event_loop_ready.clone(),
},
web_context,
);
Expand All @@ -2272,6 +2309,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
system_tray_manager: system_tray_manager.clone(),
#[cfg(feature = "tracing")]
active_tracing_spans: active_tracing_spans.clone(),
pending_ready_tasks,
is_event_loop_ready: is_event_loop_ready.clone(),
},
web_context,
);
Expand All @@ -2284,6 +2323,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
let windows = self.context.main_thread.windows.clone();
let webview_id_map = self.context.webview_id_map.clone();
let web_context = self.context.main_thread.web_context.clone();
let is_event_loop_ready = self.context.is_event_loop_ready.clone();
let pending_ready_tasks = self.pending_ready_tasks;
let mut plugins = self.plugins;

#[cfg(feature = "tracing")]
Expand Down Expand Up @@ -2318,6 +2359,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
system_tray_manager: system_tray_manager.clone(),
#[cfg(feature = "tracing")]
active_tracing_spans: active_tracing_spans.clone(),
pending_ready_tasks: &pending_ready_tasks,
is_event_loop_ready: is_event_loop_ready.clone(),
},
&web_context,
);
Expand All @@ -2341,6 +2384,8 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
system_tray_manager: system_tray_manager.clone(),
#[cfg(feature = "tracing")]
active_tracing_spans: active_tracing_spans.clone(),
pending_ready_tasks: &pending_ready_tasks,
is_event_loop_ready: is_event_loop_ready.clone(),
},
&web_context,
);
Expand All @@ -2360,6 +2405,8 @@ pub struct EventLoopIterationContext<'a, T: UserEvent> {
pub system_tray_manager: SystemTrayManager,
#[cfg(feature = "tracing")]
pub active_tracing_spans: ActiveTraceSpanStore,
pub is_event_loop_ready: Arc<AtomicBool>,
pub pending_ready_tasks: &'a RefCell<Vec<PendingReadyTask<T>>>,
}

struct UserMessageContext {
Expand Down Expand Up @@ -2817,13 +2864,20 @@ fn handle_event_loop<T: UserEvent>(
system_tray_manager,
#[cfg(feature = "tracing")]
active_tracing_spans,
is_event_loop_ready,
pending_ready_tasks,
} = context;
if *control_flow != ControlFlow::Exit {
*control_flow = ControlFlow::Wait;
}

match event {
Event::NewEvents(StartCause::Init) => {
is_event_loop_ready.store(true, Ordering::Relaxed);
for task in pending_ready_tasks.replace(Vec::new()) {
task(event_loop);
}

callback(RunEvent::Ready);
}

Expand Down
94 changes: 87 additions & 7 deletions core/tauri-runtime-wry/src/system_tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use std::{
cell::RefCell,
collections::HashMap,
fmt,
sync::{Arc, Mutex},
sync::{atomic::Ordering, Arc, Mutex},
};

pub type GlobalSystemTrayEventHandler = Box<dyn Fn(TrayId, &SystemTrayEvent) + Send>;
Expand Down Expand Up @@ -161,24 +161,68 @@ pub struct SystemTrayHandle<T: UserEvent> {
pub(crate) context: Context<T>,
pub(crate) id: TrayId,
pub(crate) proxy: EventLoopProxy<super::Message<T>>,
pub(crate) pending: PendingSystemTray,
}

impl<T: UserEvent> TrayHandle for SystemTrayHandle<T> {
fn set_icon(&self, icon: Icon) -> Result<()> {
if !self.context.is_event_loop_ready.load(Ordering::Relaxed) {
if let Some(pending) = &mut *self.pending.0.borrow_mut() {
pending.icon.replace(icon);
return Ok(());
}
}

self
.proxy
.send_event(Message::Tray(self.id, TrayMessage::UpdateIcon(icon)))
.map_err(|_| Error::FailedToSendMessage)
}

fn set_menu(&self, menu: SystemTrayMenu) -> Result<()> {
if !self.context.is_event_loop_ready.load(Ordering::Relaxed) {
if let Some(pending) = &mut *self.pending.0.borrow_mut() {
pending.menu.replace(menu);
return Ok(());
}
}

self
.proxy
.send_event(Message::Tray(self.id, TrayMessage::UpdateMenu(menu)))
.map_err(|_| Error::FailedToSendMessage)
}

fn update_item(&self, id: u16, update: MenuUpdate) -> Result<()> {
if !self.context.is_event_loop_ready.load(Ordering::Relaxed) {
if let Some(pending) = &mut *self.pending.0.borrow_mut() {
if let Some(menu) = &mut pending.menu {
for item in &mut menu.items {
if let SystemTrayMenuEntry::CustomItem(item) = item {
if item.id == id {
match update {
MenuUpdate::SetEnabled(enabled) => {
item.enabled = enabled;
}
MenuUpdate::SetTitle(title) => {
item.title = title;
}
MenuUpdate::SetSelected(selected) => {
item.selected = selected;
}
#[cfg(target_os = "macos")]
MenuUpdate::SetNativeImage(img) => {
item.native_image.replace(img);
}
}
break;
}
}
}
}
return Ok(());
}
}
self
.proxy
.send_event(Message::Tray(self.id, TrayMessage::UpdateItem(id, update)))
Expand All @@ -187,6 +231,13 @@ impl<T: UserEvent> TrayHandle for SystemTrayHandle<T> {

#[cfg(target_os = "macos")]
fn set_icon_as_template(&self, is_template: bool) -> tauri_runtime::Result<()> {
if !self.context.is_event_loop_ready.load(Ordering::Relaxed) {
if let Some(pending) = &mut *self.pending.0.borrow_mut() {
pending.icon_as_template = is_template;
return Ok(());
}
}

self
.proxy
.send_event(Message::Tray(
Expand All @@ -198,6 +249,13 @@ impl<T: UserEvent> TrayHandle for SystemTrayHandle<T> {

#[cfg(target_os = "macos")]
fn set_title(&self, title: &str) -> tauri_runtime::Result<()> {
if !self.context.is_event_loop_ready.load(Ordering::Relaxed) {
if let Some(pending) = &mut *self.pending.0.borrow_mut() {
pending.title.replace(title.to_string());
return Ok(());
}
}

self
.proxy
.send_event(Message::Tray(
Expand All @@ -208,6 +266,13 @@ impl<T: UserEvent> TrayHandle for SystemTrayHandle<T> {
}

fn set_tooltip(&self, tooltip: &str) -> Result<()> {
if !self.context.is_event_loop_ready.load(Ordering::Relaxed) {
if let Some(pending) = &mut *self.pending.0.borrow_mut() {
pending.tooltip.replace(tooltip.to_string());
return Ok(());
}
}

self
.proxy
.send_event(Message::Tray(
Expand All @@ -218,12 +283,16 @@ impl<T: UserEvent> TrayHandle for SystemTrayHandle<T> {
}

fn destroy(&self) -> Result<()> {
let (tx, rx) = std::sync::mpsc::channel();
send_user_message(
&self.context,
Message::Tray(self.id, TrayMessage::Destroy(tx)),
)?;
rx.recv().unwrap()?;
if self.context.is_event_loop_ready.load(Ordering::Relaxed)
&& self.pending.0.borrow_mut().take().is_none()
{
let (tx, rx) = std::sync::mpsc::channel();
send_user_message(
&self.context,
Message::Tray(self.id, TrayMessage::Destroy(tx)),
)?;
rx.recv().unwrap()?;
}
Ok(())
}
}
Expand Down Expand Up @@ -267,3 +336,14 @@ pub fn to_wry_context_menu(
}
tray_menu
}

#[derive(Debug, Clone)]
pub struct PendingSystemTray(pub Arc<RefCell<Option<SystemTray>>>);

// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for PendingSystemTray {}

// SAFETY: we ensure this type is only used on the main thread.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Sync for PendingSystemTray {}

0 comments on commit c3a90e5

Please sign in to comment.