Skip to main content

blitz_shell/
event.rs

1use blitz_traits::navigation::NavigationOptions;
2use blitz_traits::net::NetWaker;
3use futures_util::task::ArcWake;
4use std::sync::mpsc::{Receiver, Sender, channel};
5use std::{any::Any, sync::Arc};
6use winit::{event_loop::EventLoopProxy, window::WindowId};
7
8#[cfg(feature = "accessibility")]
9use accesskit_xplat::WindowEvent as AccessKitEvent;
10
11#[derive(Debug, Clone)]
12pub enum BlitzShellEvent {
13    Poll {
14        window_id: WindowId,
15    },
16
17    RequestRedraw {
18        doc_id: usize,
19    },
20
21    /// An accessibility event from `accesskit`.
22    #[cfg(feature = "accessibility")]
23    Accessibility {
24        window_id: WindowId,
25        data: Arc<AccessKitEvent>,
26    },
27
28    /// An arbitary event from the Blitz embedder
29    Embedder(Arc<dyn Any + Send + Sync>),
30
31    /// Navigate to another URL (triggered by e.g. clicking a link)
32    Navigate(Box<NavigationOptions>),
33
34    /// Navigate to another URL (triggered by e.g. clicking a link)
35    NavigationLoad {
36        url: String,
37        contents: String,
38        retain_scroll_position: bool,
39        is_md: bool,
40    },
41}
42impl BlitzShellEvent {
43    pub fn embedder_event<T: Any + Send + Sync>(value: T) -> Self {
44        let boxed = Arc::new(value) as Arc<dyn Any + Send + Sync>;
45        Self::Embedder(boxed)
46    }
47}
48
49#[derive(Clone)]
50pub struct BlitzShellProxy(Arc<BlitzShellProxyInner>);
51pub struct BlitzShellProxyInner {
52    winit_proxy: EventLoopProxy,
53    sender: Sender<BlitzShellEvent>,
54}
55
56impl BlitzShellProxy {
57    pub fn new(winit_proxy: EventLoopProxy) -> (Self, Receiver<BlitzShellEvent>) {
58        let (sender, receiver) = channel();
59        let proxy = Self(Arc::new(BlitzShellProxyInner {
60            winit_proxy,
61            sender,
62        }));
63        (proxy, receiver)
64    }
65
66    pub fn wake_up(&self) {
67        self.0.winit_proxy.wake_up();
68    }
69    pub fn send_event(&self, event: impl Into<BlitzShellEvent>) {
70        self.send_event_impl(event.into());
71    }
72    fn send_event_impl(&self, event: BlitzShellEvent) {
73        let _ = self.0.sender.send(event);
74        self.wake_up();
75    }
76}
77
78impl NetWaker for BlitzShellProxy {
79    fn wake(&self, client_id: usize) {
80        self.send_event_impl(BlitzShellEvent::RequestRedraw { doc_id: client_id })
81    }
82}
83
84/// Create a waker that will send a poll event to the event loop.
85///
86/// This lets the VirtualDom "come up for air" and process events while the main thread is blocked by the WebView.
87///
88/// All other IO lives in the Tokio runtime,
89pub fn create_waker(proxy: &BlitzShellProxy, id: WindowId) -> std::task::Waker {
90    struct DomHandle {
91        proxy: BlitzShellProxy,
92        id: WindowId,
93    }
94    impl ArcWake for DomHandle {
95        fn wake_by_ref(arc_self: &Arc<Self>) {
96            let event = BlitzShellEvent::Poll {
97                window_id: arc_self.id,
98            };
99            arc_self.proxy.send_event(event)
100        }
101    }
102
103    let proxy = proxy.clone();
104    futures_util::task::waker(Arc::new(DomHandle { id, proxy }))
105}