From cc1ee5926c24f0223477a5edaa8959279a122b38 Mon Sep 17 00:00:00 2001 From: Johannes Haase Date: Fri, 29 May 2026 21:37:01 +0200 Subject: [PATCH 1/3] feat: Add winit Event handler --- Cargo.lock | 1 + packages/native/Cargo.toml | 1 + packages/native/src/dioxus_application.rs | 3 + packages/native/src/event_handlers.rs | 71 +++++++++++++++++++++++ packages/native/src/hooks.rs | 24 ++++++++ packages/native/src/lib.rs | 5 ++ 6 files changed, 105 insertions(+) create mode 100644 packages/native/src/event_handlers.rs create mode 100644 packages/native/src/hooks.rs diff --git a/Cargo.lock b/Cargo.lock index 106b8a9bda..e32d6fc743 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3878,6 +3878,7 @@ dependencies = [ "keyboard-types 0.7.0", "manganis", "rustc-hash 2.1.2", + "slab", "tokio", "tracing", "webbrowser", diff --git a/packages/native/Cargo.toml b/packages/native/Cargo.toml index 9fb5d048a8..3d33a1d0d4 100644 --- a/packages/native/Cargo.toml +++ b/packages/native/Cargo.toml @@ -111,6 +111,7 @@ tracing = { workspace = true, optional = true } rustc-hash = { workspace = true } futures-util = { workspace = true } cfg-if = "1.0.4" +slab = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { workspace = true, features = ["rt-multi-thread"], optional = true } diff --git a/packages/native/src/dioxus_application.rs b/packages/native/src/dioxus_application.rs index cfb277f923..585a29d7a3 100644 --- a/packages/native/src/dioxus_application.rs +++ b/packages/native/src/dioxus_application.rs @@ -190,6 +190,9 @@ impl ApplicationHandler for DioxusNativeApplication { window_id: WindowId, event: WindowEvent, ) { + crate::event_handlers::WINDOW_EVENT_HANDLERS.with(|h| { + h.borrow_mut().apply_event(window_id, &event, event_loop); + }); self.inner.window_event(event_loop, window_id, event); } diff --git a/packages/native/src/event_handlers.rs b/packages/native/src/event_handlers.rs new file mode 100644 index 0000000000..0335b7c21e --- /dev/null +++ b/packages/native/src/event_handlers.rs @@ -0,0 +1,71 @@ +use slab::Slab; +use std::cell::RefCell; +use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::WindowId}; + +/// The unique identifier of a window event handler. This can be used to later remove the handler. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct WinitEventHandler(usize); + +impl WinitEventHandler { + /// Unregister this event handler from the window + pub fn remove(&self) { + WINDOW_EVENT_HANDLERS.with(|h| { + h.borrow_mut().remove(*self); + }); + } +} + +struct WinitWindowEventHandlerInner { + window_id: WindowId, + + #[allow(clippy::type_complexity)] + handler: Box, +} + +pub(crate) struct WindowEventHandlers { + handlers: Slab, +} + +impl Default for WindowEventHandlers { + fn default() -> Self { + Self { + handlers: Slab::new(), + } + } +} + +impl WindowEventHandlers { + pub(crate) fn add( + &mut self, + window_id: WindowId, + handler: impl FnMut(&WindowEvent, &dyn ActiveEventLoop) + 'static, + ) -> WinitEventHandler { + WinitEventHandler(self.handlers.insert(WinitWindowEventHandlerInner { + window_id, + handler: Box::new(handler), + })) + } + + pub(crate) fn remove(&mut self, id: WinitEventHandler) { + self.handlers.try_remove(id.0); + } + + pub fn apply_event( + &mut self, + window_id: WindowId, + event: &WindowEvent, + target: &dyn ActiveEventLoop, + ) { + for (_, handler) in self.handlers.iter_mut() { + if handler.window_id != window_id { + continue; + } + (handler.handler)(event, target) + } + } +} + +thread_local! { + pub(crate) static WINDOW_EVENT_HANDLERS: RefCell = + RefCell::new(WindowEventHandlers::default()); +} diff --git a/packages/native/src/hooks.rs b/packages/native/src/hooks.rs new file mode 100644 index 0000000000..3f065fd845 --- /dev/null +++ b/packages/native/src/hooks.rs @@ -0,0 +1,24 @@ +use crate::event_handlers::{WINDOW_EVENT_HANDLERS, WinitEventHandler}; + +use dioxus_core::{Runtime, current_scope_id, use_hook_with_cleanup}; +use winit::{event::WindowEvent, event_loop::ActiveEventLoop}; + +/// Register an event handler that runs when a winit event is processed. +pub fn use_winit_event_handler( + mut handler: impl FnMut(&WindowEvent, &dyn ActiveEventLoop) + 'static, +) -> WinitEventHandler { + let runtime = Runtime::current(); + let scope_id = current_scope_id(); + let window_id = crate::use_window().id(); + + use_hook_with_cleanup( + move || { + WINDOW_EVENT_HANDLERS.with(|h| { + h.borrow_mut().add(window_id, move |event, target| { + runtime.in_scope(scope_id, || handler(event, target)) + }) + }) + }, + move |handler| handler.remove(), + ) +} diff --git a/packages/native/src/lib.rs b/packages/native/src/lib.rs index 4bd8a31d40..e7c0151aa7 100644 --- a/packages/native/src/lib.rs +++ b/packages/native/src/lib.rs @@ -14,6 +14,8 @@ mod config; mod contexts; mod dioxus_application; mod dioxus_renderer; +mod event_handlers; +mod hooks; mod link_handler; #[cfg(feature = "prelude")] @@ -55,6 +57,9 @@ pub use { pub use blitz_dom::{FontContext, Widget, build_single_font_ctx}; pub use config::Config; +pub use event_handlers::WinitEventHandler; +pub use hooks::use_winit_event_handler; +pub use winit; pub use winit::dpi::{LogicalSize, PhysicalSize}; pub use winit::window::WindowAttributes; From c137a5190e6e13e1a2a5fb2a556c31d15a6c70b9 Mon Sep 17 00:00:00 2001 From: Johannes Haase Date: Fri, 19 Jun 2026 18:18:28 +0200 Subject: [PATCH 2/3] add `winit-event-handler` feature flag --- packages/native/Cargo.toml | 3 ++- packages/native/src/dioxus_application.rs | 1 + packages/native/src/lib.rs | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/native/Cargo.toml b/packages/native/Cargo.toml index 3d33a1d0d4..fb9b317ab6 100644 --- a/packages/native/Cargo.toml +++ b/packages/native/Cargo.toml @@ -43,6 +43,7 @@ font-embolden = ["blitz-paint/font-embolden"] apple-font-embolden = ["blitz-paint/apple-font-embolden"] # Other features +winit-event-handler = ["dep:slab"] # No-op on wasm32 (blitz_net::Provider needs tokio's threaded runtime); launch_cfg # falls back to the in-process DioxusNativeNetProvider there. net = ["dep:tokio", "dep:blitz-net"] @@ -111,7 +112,7 @@ tracing = { workspace = true, optional = true } rustc-hash = { workspace = true } futures-util = { workspace = true } cfg-if = "1.0.4" -slab = { workspace = true } +slab = { workspace = true, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { workspace = true, features = ["rt-multi-thread"], optional = true } diff --git a/packages/native/src/dioxus_application.rs b/packages/native/src/dioxus_application.rs index 585a29d7a3..0a05171dd6 100644 --- a/packages/native/src/dioxus_application.rs +++ b/packages/native/src/dioxus_application.rs @@ -190,6 +190,7 @@ impl ApplicationHandler for DioxusNativeApplication { window_id: WindowId, event: WindowEvent, ) { + #[cfg(feature = "winit-event-handler")] crate::event_handlers::WINDOW_EVENT_HANDLERS.with(|h| { h.borrow_mut().apply_event(window_id, &event, event_loop); }); diff --git a/packages/native/src/lib.rs b/packages/native/src/lib.rs index e7c0151aa7..961c42198d 100644 --- a/packages/native/src/lib.rs +++ b/packages/native/src/lib.rs @@ -14,7 +14,9 @@ mod config; mod contexts; mod dioxus_application; mod dioxus_renderer; +#[cfg(feature = "winit-event-handler")] mod event_handlers; +#[cfg(feature = "winit-event-handler")] mod hooks; mod link_handler; @@ -57,7 +59,9 @@ pub use { pub use blitz_dom::{FontContext, Widget, build_single_font_ctx}; pub use config::Config; +#[cfg(feature = "winit-event-handler")] pub use event_handlers::WinitEventHandler; +#[cfg(feature = "winit-event-handler")] pub use hooks::use_winit_event_handler; pub use winit; pub use winit::dpi::{LogicalSize, PhysicalSize}; From f4734ffd3d31512792c713e0b5c7df0e087d6a96 Mon Sep 17 00:00:00 2001 From: Johannes Haase Date: Thu, 2 Jul 2026 19:37:18 +0200 Subject: [PATCH 3/3] replace thread_local with Rc context tree for winit event handlers --- packages/native/src/dioxus_application.rs | 14 +++++-- packages/native/src/event_handlers.rs | 46 ++++++++++------------- packages/native/src/hooks.rs | 12 +++--- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/native/src/dioxus_application.rs b/packages/native/src/dioxus_application.rs index 0a05171dd6..a654d11e96 100644 --- a/packages/native/src/dioxus_application.rs +++ b/packages/native/src/dioxus_application.rs @@ -14,6 +14,9 @@ use winit::platform::macos::ApplicationHandlerExtMacOS; use crate::DioxusNativeWindowRenderer; use crate::{BlitzShellEvent, DioxusDocument, WindowConfig, contexts::DioxusNativeDocument}; +#[cfg(feature = "winit-event-handler")] +use crate::event_handlers::WindowEventHandlers; + /// Dioxus-native specific event type pub enum DioxusNativeEvent { /// A hotreload event, basically telling us to update our templates. @@ -34,6 +37,8 @@ pub enum DioxusNativeEvent { pub struct DioxusNativeApplication { pending_window: Option>, inner: BlitzApplication, + #[cfg(feature = "winit-event-handler")] + event_handler: Rc, } impl DioxusNativeApplication { @@ -45,6 +50,8 @@ impl DioxusNativeApplication { Self { pending_window: Some(config), inner: BlitzApplication::new(proxy, event_queue), + #[cfg(feature = "winit-event-handler")] + event_handler: Rc::new(WindowEventHandlers::default()), } } @@ -147,6 +154,8 @@ impl ApplicationHandler for DioxusNativeApplication { window_id, )); provide_context(shared); + #[cfg(feature = "winit-event-handler")] + provide_context(self.event_handler.clone()); }); // Add shell provider @@ -191,9 +200,8 @@ impl ApplicationHandler for DioxusNativeApplication { event: WindowEvent, ) { #[cfg(feature = "winit-event-handler")] - crate::event_handlers::WINDOW_EVENT_HANDLERS.with(|h| { - h.borrow_mut().apply_event(window_id, &event, event_loop); - }); + self.event_handler + .apply_event(window_id, &event, event_loop); self.inner.window_event(event_loop, window_id, event); } diff --git a/packages/native/src/event_handlers.rs b/packages/native/src/event_handlers.rs index 0335b7c21e..ea70cdfd92 100644 --- a/packages/native/src/event_handlers.rs +++ b/packages/native/src/event_handlers.rs @@ -1,17 +1,17 @@ use slab::Slab; use std::cell::RefCell; +use std::rc::Rc; use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::WindowId}; /// The unique identifier of a window event handler. This can be used to later remove the handler. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct WinitEventHandler(usize); +pub struct WinitEventHandler(pub(crate) usize); impl WinitEventHandler { /// Unregister this event handler from the window pub fn remove(&self) { - WINDOW_EVENT_HANDLERS.with(|h| { - h.borrow_mut().remove(*self); - }); + let handlers: Rc = dioxus_core::consume_context(); + handlers.remove(*self); } } @@ -22,41 +22,38 @@ struct WinitWindowEventHandlerInner { handler: Box, } +#[derive(Default)] pub(crate) struct WindowEventHandlers { - handlers: Slab, -} - -impl Default for WindowEventHandlers { - fn default() -> Self { - Self { - handlers: Slab::new(), - } - } + handlers: RefCell>, } impl WindowEventHandlers { pub(crate) fn add( - &mut self, + &self, window_id: WindowId, handler: impl FnMut(&WindowEvent, &dyn ActiveEventLoop) + 'static, ) -> WinitEventHandler { - WinitEventHandler(self.handlers.insert(WinitWindowEventHandlerInner { - window_id, - handler: Box::new(handler), - })) + WinitEventHandler( + self.handlers + .borrow_mut() + .insert(WinitWindowEventHandlerInner { + window_id, + handler: Box::new(handler), + }), + ) } - pub(crate) fn remove(&mut self, id: WinitEventHandler) { - self.handlers.try_remove(id.0); + pub(crate) fn remove(&self, id: WinitEventHandler) { + self.handlers.borrow_mut().try_remove(id.0); } pub fn apply_event( - &mut self, + &self, window_id: WindowId, event: &WindowEvent, target: &dyn ActiveEventLoop, ) { - for (_, handler) in self.handlers.iter_mut() { + for (_, handler) in self.handlers.borrow_mut().iter_mut() { if handler.window_id != window_id { continue; } @@ -64,8 +61,3 @@ impl WindowEventHandlers { } } } - -thread_local! { - pub(crate) static WINDOW_EVENT_HANDLERS: RefCell = - RefCell::new(WindowEventHandlers::default()); -} diff --git a/packages/native/src/hooks.rs b/packages/native/src/hooks.rs index 3f065fd845..a0192aec87 100644 --- a/packages/native/src/hooks.rs +++ b/packages/native/src/hooks.rs @@ -1,6 +1,7 @@ -use crate::event_handlers::{WINDOW_EVENT_HANDLERS, WinitEventHandler}; +use crate::event_handlers::{WindowEventHandlers, WinitEventHandler}; -use dioxus_core::{Runtime, current_scope_id, use_hook_with_cleanup}; +use dioxus_core::{Runtime, consume_context, current_scope_id, use_hook_with_cleanup}; +use std::rc::Rc; use winit::{event::WindowEvent, event_loop::ActiveEventLoop}; /// Register an event handler that runs when a winit event is processed. @@ -13,10 +14,9 @@ pub fn use_winit_event_handler( use_hook_with_cleanup( move || { - WINDOW_EVENT_HANDLERS.with(|h| { - h.borrow_mut().add(window_id, move |event, target| { - runtime.in_scope(scope_id, || handler(event, target)) - }) + let handlers: Rc = consume_context(); + handlers.add(window_id, move |event, target| { + runtime.in_scope(scope_id, || handler(event, target)) }) }, move |handler| handler.remove(),