From 73fd60eef2b60f5dc84525ef9c315f4d80c4414f Mon Sep 17 00:00:00 2001 From: filip Date: Tue, 13 Dec 2022 17:57:32 +0100 Subject: [PATCH] expose set_device_event_filter in tauri (#5562) Co-authored-by: Lucas Nogueira Closes https://github.com/tauri-apps/tauri/issues/5496 --- ...se-set_device_event_filter-in-tauri-api.md | 7 ++ core/tauri-runtime-wry/src/lib.rs | 26 ++++++-- core/tauri-runtime/src/lib.rs | 30 +++++++++ core/tauri/src/app.rs | 65 +++++++++++++++++-- core/tauri/src/lib.rs | 2 +- core/tauri/src/test/mock_runtime.rs | 6 +- 6 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 .changes/expose-set_device_event_filter-in-tauri-api.md diff --git a/.changes/expose-set_device_event_filter-in-tauri-api.md b/.changes/expose-set_device_event_filter-in-tauri-api.md new file mode 100644 index 000000000..cf1993c02 --- /dev/null +++ b/.changes/expose-set_device_event_filter-in-tauri-api.md @@ -0,0 +1,7 @@ +--- +"tauri-runtime-wry": minor +"tauri-runtime": minor +"tauri": minor +--- + +Added `Builder::device_event_filter` and `App::set_device_event_filter` methods. diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index ed9391db5..10941d927 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -14,8 +14,8 @@ use tauri_runtime::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, CursorIcon, DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent, }, - Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Icon, Result, RunEvent, RunIteration, - Runtime, RuntimeHandle, UserAttentionType, UserEvent, + DeviceEventFilter, Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Icon, Result, + RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType, UserEvent, }; use tauri_runtime::window::MenuEvent; @@ -47,7 +47,8 @@ use wry::{ }, event::{Event, StartCause, WindowEvent as WryWindowEvent}, event_loop::{ - ControlFlow, EventLoop, EventLoopProxy as WryEventLoopProxy, EventLoopWindowTarget, + ControlFlow, DeviceEventFilter as WryDeviceEventFilter, EventLoop, + EventLoopProxy as WryEventLoopProxy, EventLoopWindowTarget, }, menu::{ AboutMetadata as WryAboutMetadata, CustomMenuItem as WryCustomMenuItem, MenuBar, @@ -64,7 +65,6 @@ use wry::{ webview::{FileDropEvent as WryFileDropEvent, WebContext, WebView, WebViewBuilder}, }; -pub use wry; pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId}; #[cfg(windows)] @@ -364,6 +364,18 @@ impl From for MenuItemWrapper { } } +pub struct DeviceEventFilterWrapper(pub WryDeviceEventFilter); + +impl From for DeviceEventFilterWrapper { + fn from(item: DeviceEventFilter) -> Self { + match item { + DeviceEventFilter::Always => Self(WryDeviceEventFilter::Always), + DeviceEventFilter::Never => Self(WryDeviceEventFilter::Never), + DeviceEventFilter::Unfocused => Self(WryDeviceEventFilter::Unfocused), + } + } +} + #[cfg(target_os = "macos")] pub struct NativeImageWrapper(pub WryNativeImage); @@ -2055,6 +2067,12 @@ impl Runtime for Wry { self.event_loop.hide_application(); } + fn set_device_event_filter(&mut self, filter: DeviceEventFilter) { + self + .event_loop + .set_device_event_filter(DeviceEventFilterWrapper::from(filter).0); + } + #[cfg(desktop)] fn run_iteration) + 'static>(&mut self, mut callback: F) -> RunIteration { use wry::application::platform::run_return::EventLoopExtRunReturn; diff --git a/core/tauri-runtime/src/lib.rs b/core/tauri-runtime/src/lib.rs index 49edf7308..a0a70f266 100644 --- a/core/tauri-runtime/src/lib.rs +++ b/core/tauri-runtime/src/lib.rs @@ -184,6 +184,23 @@ pub enum UserAttentionType { Informational, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] +#[serde(tag = "type")] +pub enum DeviceEventFilter { + /// Always filter out device events. + Always, + /// Filter out device events while the window is not focused. + Unfocused, + /// Report all device events regardless of window focus. + Never, +} + +impl Default for DeviceEventFilter { + fn default() -> Self { + Self::Unfocused + } +} + #[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum Error { @@ -461,6 +478,19 @@ pub trait Runtime: Debug + Sized + 'static { #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))] fn hide(&self); + /// Change the device event filter mode. + /// + /// Since the DeviceEvent capture can lead to high CPU usage for unfocused windows, [`tao`] + /// will ignore them by default for unfocused windows on Windows. This method allows changing + /// the filter to explicitly capture them again. + /// + /// ## Platform-specific + /// + /// - ** Linux / macOS / iOS / Android**: Unsupported. + /// + /// [`tao`]: https://crates.io/crates/tao + fn set_device_event_filter(&mut self, filter: DeviceEventFilter); + /// Runs the one step of the webview runtime event loop and returns control flow to the caller. #[cfg(desktop)] fn run_iteration) + 'static>(&mut self, callback: F) -> RunIteration; diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 14eb6b071..955c660a6 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -23,8 +23,8 @@ use crate::{ sealed::{ManagerBase, RuntimeOrDispatch}, utils::config::Config, utils::{assets::Assets, resources::resource_relpath, Env}, - Context, EventLoopMessage, Invoke, InvokeError, InvokeResponse, Manager, Runtime, Scopes, - StateManager, Theme, Window, + Context, DeviceEventFilter, EventLoopMessage, Invoke, InvokeError, InvokeResponse, Manager, + Runtime, Scopes, StateManager, Theme, Window, }; #[cfg(shell_scope)] @@ -805,6 +805,35 @@ impl App { .set_activation_policy(activation_policy); } + /// Change the device event filter mode. + /// + /// Since the DeviceEvent capture can lead to high CPU usage for unfocused windows, [`tao`] + /// will ignore them by default for unfocused windows on Windows. This method allows changing + /// the filter to explicitly capture them again. + /// + /// ## Platform-specific + /// + /// - ** Linux / macOS / iOS / Android**: Unsupported. + /// + /// # Examples + /// ```,no_run + /// let mut app = tauri::Builder::default() + /// // on an actual app, remove the string argument + /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")) + /// .expect("error while building tauri application"); + /// app.set_device_event_filter(tauri::DeviceEventFilter::Always); + /// app.run(|_app_handle, _event| {}); + /// ``` + /// + /// [`tao`]: https://crates.io/crates/tao + pub fn set_device_event_filter(&mut self, filter: DeviceEventFilter) { + self + .runtime + .as_mut() + .unwrap() + .set_device_event_filter(filter); + } + /// Gets the argument matches of the CLI definition configured in `tauri.conf.json`. /// /// # Examples @@ -1008,6 +1037,9 @@ pub struct Builder { /// The updater configuration. #[cfg(updater)] updater_settings: UpdaterSettings, + + /// The device event filter. + device_event_filter: DeviceEventFilter, } impl Builder { @@ -1036,6 +1068,7 @@ impl Builder { system_tray_event_listeners: Vec::new(), #[cfg(updater)] updater_settings: Default::default(), + device_event_filter: Default::default(), } } @@ -1486,6 +1519,28 @@ impl Builder { self } + /// Change the device event filter mode. + /// + /// Since the DeviceEvent capture can lead to high CPU usage for unfocused windows, [`tao`] + /// will ignore them by default for unfocused windows on Windows. This method allows changing + /// the filter to explicitly capture them again. + /// + /// ## Platform-specific + /// + /// - ** Linux / macOS / iOS / Android**: Unsupported. + /// + /// # Examples + /// ```,no_run + /// tauri::Builder::default() + /// .device_event_filter(tauri::DeviceEventFilter::Always); + /// ``` + /// + /// [`tao`]: https://crates.io/crates/tao + pub fn device_event_filter(mut self, filter: DeviceEventFilter) -> Self { + self.device_event_filter = filter; + self + } + /// Builds the application. #[allow(clippy::type_complexity)] pub fn build(mut self, context: Context) -> crate::Result> { @@ -1531,13 +1586,15 @@ impl Builder { } #[cfg(any(windows, target_os = "linux"))] - let runtime = if self.runtime_any_thread { + let mut runtime = if self.runtime_any_thread { R::new_any_thread()? } else { R::new()? }; #[cfg(not(any(windows, target_os = "linux")))] - let runtime = R::new()?; + let mut runtime = R::new()?; + + runtime.set_device_event_filter(self.device_event_filter); let runtime_handle = runtime.handle(); diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 4013c644c..3ccfb74dd 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -244,7 +244,7 @@ pub use { dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size}, CursorIcon, FileDropEvent, }, - RunIteration, UserAttentionType, + DeviceEventFilter, RunIteration, UserAttentionType, }, self::state::{State, StateManager}, self::utils::{ diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index 4944130ad..41c892b88 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -12,8 +12,8 @@ use tauri_runtime::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, CursorIcon, DetachedWindow, MenuEvent, PendingWindow, WindowEvent, }, - Dispatch, EventLoopProxy, Icon, Result, RunEvent, Runtime, RuntimeHandle, UserAttentionType, - UserEvent, + DeviceEventFilter, Dispatch, EventLoopProxy, Icon, Result, RunEvent, Runtime, RuntimeHandle, + UserAttentionType, UserEvent, }; #[cfg(all(desktop, feature = "system-tray"))] use tauri_runtime::{ @@ -711,6 +711,8 @@ impl Runtime for MockRuntime { #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))] fn hide(&self) {} + fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {} + #[cfg(any( target_os = "macos", windows,