Skip to content

Commit af44bf8

Browse files
authored
feat(core): allow app run on any thread on Linux & Windows, closes #3172 (#3353)
1 parent 8483fde commit af44bf8

6 files changed

Lines changed: 92 additions & 22 deletions

File tree

.changes/app-any-thread.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
Added `any_thread()` to the `tauri::Builder` to run applications on any thread (only exposed on Linux and Windows).

.changes/runtime-any-thread.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-runtime": patch
3+
"tauri-runtime-wry": patch
4+
---
5+
6+
Added `any_thread` constructor on the `Runtime` trait (only possible on Linux and Windows).

core/tauri-runtime-wry/src/lib.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,16 +1569,8 @@ impl RuntimeHandle for WryHandle {
15691569
}
15701570
}
15711571

1572-
impl Runtime for Wry {
1573-
type Dispatcher = WryDispatcher;
1574-
type Handle = WryHandle;
1575-
type GlobalShortcutManager = GlobalShortcutManagerHandle;
1576-
type ClipboardManager = ClipboardManagerWrapper;
1577-
#[cfg(feature = "system-tray")]
1578-
type TrayHandler = SystemTrayHandle;
1579-
1580-
fn new() -> Result<Self> {
1581-
let event_loop = EventLoop::<Message>::with_user_event();
1572+
impl Wry {
1573+
fn init(event_loop: EventLoop<Message>) -> Result<Self> {
15821574
let proxy = event_loop.create_proxy();
15831575
let main_thread_id = current_thread().id();
15841576
let web_context = WebContextStore::default();
@@ -1631,6 +1623,30 @@ impl Runtime for Wry {
16311623
tray_context,
16321624
})
16331625
}
1626+
}
1627+
1628+
impl Runtime for Wry {
1629+
type Dispatcher = WryDispatcher;
1630+
type Handle = WryHandle;
1631+
type GlobalShortcutManager = GlobalShortcutManagerHandle;
1632+
type ClipboardManager = ClipboardManagerWrapper;
1633+
#[cfg(feature = "system-tray")]
1634+
type TrayHandler = SystemTrayHandle;
1635+
1636+
fn new() -> Result<Self> {
1637+
let event_loop = EventLoop::<Message>::with_user_event();
1638+
Self::init(event_loop)
1639+
}
1640+
1641+
#[cfg(any(windows, target_os = "linux"))]
1642+
fn new_any_thread() -> Result<Self> {
1643+
#[cfg(target_os = "linux")]
1644+
use wry::application::platform::unix::EventLoopExtUnix;
1645+
#[cfg(windows)]
1646+
use wry::application::platform::windows::EventLoopExtWindows;
1647+
let event_loop = EventLoop::<Message>::new_any_thread();
1648+
Self::init(event_loop)
1649+
}
16341650

16351651
fn handle(&self) -> Self::Handle {
16361652
WryHandle {

core/tauri-runtime/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,14 @@ pub trait Runtime: Sized + 'static {
310310
#[cfg(feature = "system-tray")]
311311
type TrayHandler: menu::TrayHandle + Clone + Send;
312312

313-
/// Creates a new webview runtime.
313+
/// Creates a new webview runtime. Must be used on the main thread.
314314
fn new() -> crate::Result<Self>;
315315

316+
/// Creates a new webview runtime on any thread.
317+
#[cfg(any(windows, target_os = "linux"))]
318+
#[cfg_attr(doc_cfg, doc(cfg(any(windows, target_os = "linux"))))]
319+
fn new_any_thread() -> crate::Result<Self>;
320+
316321
/// Gets a runtime handle.
317322
fn handle(&self) -> Self::Handle;
318323

core/tauri/src/app.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,11 @@ impl<R: Runtime> App<R> {
596596
/// ```
597597
#[allow(clippy::type_complexity)]
598598
pub struct Builder<R: Runtime> {
599+
/// A flag indicating that the runtime must be started on an environment that supports the event loop not on the main thread.
600+
#[cfg(any(windows, target_os = "linux"))]
601+
#[cfg_attr(doc_cfg, doc(any(windows, target_os = "linux")))]
602+
runtime_any_thread: bool,
603+
599604
/// The JS message handler.
600605
invoke_handler: Box<InvokeHandler<R>>,
601606

@@ -645,6 +650,8 @@ impl<R: Runtime> Builder<R> {
645650
/// Creates a new App builder.
646651
pub fn new() -> Self {
647652
Self {
653+
#[cfg(any(windows, target_os = "linux"))]
654+
runtime_any_thread: false,
648655
setup: Box::new(|_| Ok(())),
649656
invoke_handler: Box::new(|_| ()),
650657
invoke_responder: Arc::new(window_invoke_responder),
@@ -665,6 +672,18 @@ impl<R: Runtime> Builder<R> {
665672
}
666673
}
667674

675+
/// Builds a new Tauri application running on any thread, bypassing the main thread requirement.
676+
///
677+
/// ## Platform-specific
678+
///
679+
/// - **macOS**: on macOS the application *must* be executed on the main thread, so this function is not exposed.
680+
#[cfg(any(windows, target_os = "linux"))]
681+
#[cfg_attr(doc_cfg, doc(any(windows, target_os = "linux")))]
682+
pub fn any_thread(mut self) -> Self {
683+
self.runtime_any_thread = true;
684+
self
685+
}
686+
668687
/// Defines the JS message handler callback.
669688
///
670689
/// # Example
@@ -1098,7 +1117,15 @@ impl<R: Runtime> Builder<R> {
10981117
));
10991118
}
11001119

1120+
#[cfg(any(windows, target_os = "linux"))]
1121+
let runtime = if self.runtime_any_thread {
1122+
R::new_any_thread()?
1123+
} else {
1124+
R::new()?
1125+
};
1126+
#[cfg(not(any(windows, target_os = "linux")))]
11011127
let runtime = R::new()?;
1128+
11021129
let runtime_handle = runtime.handle();
11031130
let global_shortcut_manager = runtime.global_shortcut_manager();
11041131
let clipboard_manager = runtime.clipboard_manager();

core/tauri/src/test/mock_runtime.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -492,20 +492,13 @@ pub struct MockRuntime {
492492
tray_handler: MockTrayHandler,
493493
}
494494

495-
impl Runtime for MockRuntime {
496-
type Dispatcher = MockDispatcher;
497-
type Handle = MockRuntimeHandle;
498-
type GlobalShortcutManager = MockGlobalShortcutManager;
499-
type ClipboardManager = MockClipboardManager;
500-
#[cfg(feature = "system-tray")]
501-
type TrayHandler = MockTrayHandler;
502-
503-
fn new() -> Result<Self> {
495+
impl MockRuntime {
496+
fn init() -> Self {
504497
let context = RuntimeContext {
505498
shortcuts: Default::default(),
506499
clipboard: Default::default(),
507500
};
508-
Ok(Self {
501+
Self {
509502
global_shortcut_manager: MockGlobalShortcutManager {
510503
context: context.clone(),
511504
},
@@ -517,7 +510,25 @@ impl Runtime for MockRuntime {
517510
context: context.clone(),
518511
},
519512
context,
520-
})
513+
}
514+
}
515+
}
516+
517+
impl Runtime for MockRuntime {
518+
type Dispatcher = MockDispatcher;
519+
type Handle = MockRuntimeHandle;
520+
type GlobalShortcutManager = MockGlobalShortcutManager;
521+
type ClipboardManager = MockClipboardManager;
522+
#[cfg(feature = "system-tray")]
523+
type TrayHandler = MockTrayHandler;
524+
525+
fn new() -> Result<Self> {
526+
Ok(Self::init())
527+
}
528+
529+
#[cfg(any(windows, target_os = "linux"))]
530+
fn new_any_thread() -> Result<Self> {
531+
Ok(Self::init())
521532
}
522533

523534
fn handle(&self) -> Self::Handle {

0 commit comments

Comments
 (0)