Skip to main content

freya_winit/
renderer.rs

1use std::{
2    borrow::Cow,
3    fmt,
4    pin::Pin,
5    task::Waker,
6};
7
8use accesskit_winit::WindowEvent as AccessibilityWindowEvent;
9use freya_core::integration::*;
10use freya_engine::prelude::{
11    FontCollection,
12    FontMgr,
13};
14use futures_lite::future::FutureExt as _;
15use futures_util::{
16    FutureExt as _,
17    StreamExt,
18    select,
19};
20use ragnarok::{
21    EventsExecutorRunner,
22    EventsMeasurerRunner,
23};
24use rustc_hash::FxHashMap;
25use torin::prelude::{
26    CursorPoint,
27    Size2D,
28};
29#[cfg(all(feature = "tray", not(target_os = "linux")))]
30use tray_icon::TrayIcon;
31use winit::{
32    application::ApplicationHandler,
33    dpi::{
34        LogicalPosition,
35        LogicalSize,
36    },
37    event::{
38        ElementState,
39        Ime,
40        MouseScrollDelta,
41        Touch,
42        TouchPhase,
43        WindowEvent,
44    },
45    event_loop::{
46        ActiveEventLoop,
47        EventLoopProxy,
48    },
49    window::{
50        Theme,
51        WindowId,
52    },
53};
54
55use crate::{
56    accessibility::AccessibilityTask,
57    config::{
58        CloseDecision,
59        WindowConfig,
60    },
61    drivers::GraphicsDriver,
62    integration::is_ime_role,
63    plugins::{
64        PluginEvent,
65        PluginHandle,
66        PluginsManager,
67    },
68    window::AppWindow,
69    winit_mappings::{
70        self,
71        map_winit_mouse_button,
72        map_winit_touch_force,
73        map_winit_touch_phase,
74    },
75};
76
77pub struct WinitRenderer {
78    pub windows_configs: Vec<WindowConfig>,
79    #[cfg(feature = "tray")]
80    pub(crate) tray: (
81        Option<crate::config::TrayIconGetter>,
82        Option<crate::config::TrayHandler>,
83    ),
84    #[cfg(all(feature = "tray", not(target_os = "linux")))]
85    pub(crate) tray_icon: Option<TrayIcon>,
86    pub resumed: bool,
87    pub windows: FxHashMap<WindowId, AppWindow>,
88    pub proxy: EventLoopProxy<NativeEvent>,
89    pub plugins: PluginsManager,
90    pub fallback_fonts: Vec<Cow<'static, str>>,
91    pub screen_reader: ScreenReader,
92    pub font_manager: FontMgr,
93    pub font_collection: FontCollection,
94    pub futures: Vec<Pin<Box<dyn std::future::Future<Output = ()>>>>,
95    pub waker: Waker,
96    pub exit_on_close: bool,
97}
98
99pub struct RendererContext<'a> {
100    pub windows: &'a mut FxHashMap<WindowId, AppWindow>,
101    pub proxy: &'a mut EventLoopProxy<NativeEvent>,
102    pub plugins: &'a mut PluginsManager,
103    pub fallback_fonts: &'a mut Vec<Cow<'static, str>>,
104    pub screen_reader: &'a mut ScreenReader,
105    pub font_manager: &'a mut FontMgr,
106    pub font_collection: &'a mut FontCollection,
107    pub active_event_loop: &'a ActiveEventLoop,
108}
109
110impl RendererContext<'_> {
111    pub fn launch_window(&mut self, window_config: WindowConfig) -> WindowId {
112        let app_window = AppWindow::new(
113            window_config,
114            self.active_event_loop,
115            self.proxy,
116            self.plugins,
117            self.font_collection,
118            self.font_manager,
119            self.fallback_fonts,
120            self.screen_reader.clone(),
121        );
122
123        let window_id = app_window.window.id();
124
125        self.proxy
126            .send_event(NativeEvent::Window(NativeWindowEvent {
127                window_id,
128                action: NativeWindowEventAction::PollRunner,
129            }))
130            .ok();
131
132        self.windows.insert(window_id, app_window);
133
134        window_id
135    }
136
137    pub fn windows(&self) -> &FxHashMap<WindowId, AppWindow> {
138        self.windows
139    }
140
141    pub fn windows_mut(&mut self) -> &mut FxHashMap<WindowId, AppWindow> {
142        self.windows
143    }
144
145    pub fn exit(&mut self) {
146        self.active_event_loop.exit();
147    }
148}
149
150#[derive(Debug)]
151pub enum NativeWindowEventAction {
152    PollRunner,
153
154    Accessibility(AccessibilityWindowEvent),
155
156    PlatformEvent(PlatformEvent),
157
158    User(UserEvent),
159}
160
161/// Proxy wrapper provided to launch tasks so they can post callbacks executed inside the renderer.
162#[derive(Clone)]
163pub struct LaunchProxy(pub EventLoopProxy<NativeEvent>);
164
165impl LaunchProxy {
166    /// Queue a callback to be run on the renderer thread with access to a [`RendererContext`].
167    ///
168    /// The call dispatches an event to the winit event loop and returns right away; the
169    /// callback runs later, when the event loop picks it up. Its return value is delivered
170    /// through the returned oneshot [`Receiver`](futures_channel::oneshot::Receiver), which
171    /// can be `.await`ed or dropped.
172    ///
173    /// The callback runs outside any component scope, so you can't call `Platform::get` or
174    /// consume context from inside it; use the [`RendererContext`] argument instead.
175    pub fn post_callback<F, T: 'static>(&self, f: F) -> futures_channel::oneshot::Receiver<T>
176    where
177        F: FnOnce(&mut RendererContext) -> T + 'static,
178    {
179        let (tx, rx) = futures_channel::oneshot::channel::<T>();
180        let cb = Box::new(move |ctx: &mut RendererContext| {
181            let res = (f)(ctx);
182            let _ = tx.send(res);
183        });
184        let _ = self
185            .0
186            .send_event(NativeEvent::Generic(NativeGenericEvent::RendererCallback(
187                cb,
188            )));
189        rx
190    }
191}
192
193pub type RendererCallback = Box<dyn FnOnce(WindowId, &mut RendererContext) + 'static>;
194
195pub enum NativeWindowErasedEventAction {
196    LaunchWindow {
197        window_config: WindowConfig,
198        ack: futures_channel::oneshot::Sender<WindowId>,
199    },
200    CloseWindow(WindowId),
201    RendererCallback(RendererCallback),
202}
203
204#[derive(Debug)]
205pub struct NativeWindowEvent {
206    pub window_id: WindowId,
207    pub action: NativeWindowEventAction,
208}
209
210#[cfg(feature = "tray")]
211#[derive(Debug)]
212pub enum NativeTrayEventAction {
213    TrayEvent(tray_icon::TrayIconEvent),
214    MenuEvent(tray_icon::menu::MenuEvent),
215    LaunchWindow(SingleThreadErasedEvent),
216}
217
218#[cfg(feature = "tray")]
219#[derive(Debug)]
220pub struct NativeTrayEvent {
221    pub action: NativeTrayEventAction,
222}
223
224pub enum NativeGenericEvent {
225    PollFutures,
226    RendererCallback(Box<dyn FnOnce(&mut RendererContext) + 'static>),
227}
228
229impl fmt::Debug for NativeGenericEvent {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        match self {
232            NativeGenericEvent::PollFutures => f.write_str("PollFutures"),
233            NativeGenericEvent::RendererCallback(_) => f.write_str("RendererCallback"),
234        }
235    }
236}
237
238/// # Safety
239/// The values are never sent, received or accessed by other threads other than the main thread.
240/// This is needed to send `Rc<T>` and other non-Send and non-Sync values.
241unsafe impl Send for NativeGenericEvent {}
242unsafe impl Sync for NativeGenericEvent {}
243
244#[derive(Debug)]
245pub enum NativeEvent {
246    Window(NativeWindowEvent),
247    #[cfg(feature = "tray")]
248    Tray(NativeTrayEvent),
249    Generic(NativeGenericEvent),
250    Preferences(mundy::Preferences),
251}
252
253impl From<accesskit_winit::Event> for NativeEvent {
254    fn from(event: accesskit_winit::Event) -> Self {
255        NativeEvent::Window(NativeWindowEvent {
256            window_id: event.window_id,
257            action: NativeWindowEventAction::Accessibility(event.window_event),
258        })
259    }
260}
261
262impl ApplicationHandler<NativeEvent> for WinitRenderer {
263    fn resumed(&mut self, active_event_loop: &winit::event_loop::ActiveEventLoop) {
264        if !self.resumed {
265            #[cfg(feature = "tray")]
266            {
267                #[cfg(not(target_os = "linux"))]
268                if let Some(tray_icon) = self.tray.0.take() {
269                    self.tray_icon = Some((tray_icon)());
270                }
271
272                #[cfg(target_os = "macos")]
273                {
274                    use objc2_core_foundation::CFRunLoop;
275
276                    let rl = CFRunLoop::main().expect("Failed to run CFRunLoop");
277                    CFRunLoop::wake_up(&rl);
278                }
279            }
280
281            for window_config in self.windows_configs.drain(..) {
282                let app_window = AppWindow::new(
283                    window_config,
284                    active_event_loop,
285                    &self.proxy,
286                    &mut self.plugins,
287                    &mut self.font_collection,
288                    &self.font_manager,
289                    &self.fallback_fonts,
290                    self.screen_reader.clone(),
291                );
292
293                self.proxy
294                    .send_event(NativeEvent::Window(NativeWindowEvent {
295                        window_id: app_window.window.id(),
296                        action: NativeWindowEventAction::PollRunner,
297                    }))
298                    .ok();
299
300                self.windows.insert(app_window.window.id(), app_window);
301            }
302            self.resumed = true;
303
304            subscribe_preferences(self.proxy.clone());
305
306            let _ = self
307                .proxy
308                .send_event(NativeEvent::Generic(NativeGenericEvent::PollFutures));
309        } else {
310            // [Android] Recreate the GraphicsDriver when the app gets brought into the foreground after being suspended,
311            // so we don't end up with a completely black surface with broken rendering.
312            let old_windows: Vec<_> = self.windows.drain().collect();
313            for (_, mut app_window) in old_windows {
314                let (new_driver, new_window) =
315                    GraphicsDriver::new(active_event_loop, app_window.window_attributes.clone());
316
317                let new_id = new_window.id();
318                app_window.driver = new_driver;
319                app_window.window = new_window;
320                app_window.process_layout_on_next_render = true;
321                app_window.tree.layout.reset();
322
323                self.windows.insert(new_id, app_window);
324
325                self.proxy
326                    .send_event(NativeEvent::Window(NativeWindowEvent {
327                        window_id: new_id,
328                        action: NativeWindowEventAction::PollRunner,
329                    }))
330                    .ok();
331            }
332        }
333    }
334
335    fn user_event(
336        &mut self,
337        active_event_loop: &winit::event_loop::ActiveEventLoop,
338        event: NativeEvent,
339    ) {
340        match event {
341            NativeEvent::Generic(NativeGenericEvent::RendererCallback(cb)) => {
342                let mut renderer_context = RendererContext {
343                    fallback_fonts: &mut self.fallback_fonts,
344                    active_event_loop,
345                    windows: &mut self.windows,
346                    proxy: &mut self.proxy,
347                    plugins: &mut self.plugins,
348                    screen_reader: &mut self.screen_reader,
349                    font_manager: &mut self.font_manager,
350                    font_collection: &mut self.font_collection,
351                };
352                (cb)(&mut renderer_context);
353            }
354            NativeEvent::Generic(NativeGenericEvent::PollFutures) => {
355                let mut cx = std::task::Context::from_waker(&self.waker);
356                self.futures
357                    .retain_mut(|fut| fut.poll(&mut cx).is_pending());
358            }
359            NativeEvent::Preferences(prefs) => {
360                for app in self.windows.values_mut() {
361                    app.platform
362                        .accent_color
363                        .set_if_modified(prefs.accent_color);
364                }
365            }
366            #[cfg(feature = "tray")]
367            NativeEvent::Tray(NativeTrayEvent { action }) => {
368                let renderer_context = RendererContext {
369                    fallback_fonts: &mut self.fallback_fonts,
370                    active_event_loop,
371                    windows: &mut self.windows,
372                    proxy: &mut self.proxy,
373                    plugins: &mut self.plugins,
374                    screen_reader: &mut self.screen_reader,
375                    font_manager: &mut self.font_manager,
376                    font_collection: &mut self.font_collection,
377                };
378                match action {
379                    NativeTrayEventAction::TrayEvent(icon_event) => {
380                        use crate::tray::TrayEvent;
381                        if let Some(tray_handler) = &mut self.tray.1 {
382                            (tray_handler)(TrayEvent::Icon(icon_event), renderer_context)
383                        }
384                    }
385                    NativeTrayEventAction::MenuEvent(menu_event) => {
386                        use crate::tray::TrayEvent;
387                        if let Some(tray_handler) = &mut self.tray.1 {
388                            (tray_handler)(TrayEvent::Menu(menu_event), renderer_context)
389                        }
390                    }
391                    NativeTrayEventAction::LaunchWindow(data) => {
392                        let window_config = data
393                            .0
394                            .downcast::<WindowConfig>()
395                            .expect("Expected WindowConfig");
396                        let app_window = AppWindow::new(
397                            *window_config,
398                            active_event_loop,
399                            &self.proxy,
400                            &mut self.plugins,
401                            &mut self.font_collection,
402                            &self.font_manager,
403                            &self.fallback_fonts,
404                            self.screen_reader.clone(),
405                        );
406
407                        self.proxy
408                            .send_event(NativeEvent::Window(NativeWindowEvent {
409                                window_id: app_window.window.id(),
410                                action: NativeWindowEventAction::PollRunner,
411                            }))
412                            .ok();
413
414                        self.windows.insert(app_window.window.id(), app_window);
415                    }
416                }
417            }
418            NativeEvent::Window(NativeWindowEvent { action, window_id }) => {
419                if let Some(app) = &mut self.windows.get_mut(&window_id) {
420                    match action {
421                        NativeWindowEventAction::PollRunner => {
422                            let mut cx = std::task::Context::from_waker(&app.waker);
423
424                            #[cfg(feature = "hotreload")]
425                            let hotreload_triggered = app
426                                .hot_reload_pending
427                                .swap(false, std::sync::atomic::Ordering::AcqRel);
428
429                            #[cfg(feature = "hotreload")]
430                            if hotreload_triggered {
431                                app.runner.reload();
432                            }
433
434                            {
435                                let fut = std::pin::pin!(async {
436                                    select! {
437                                        events_chunk = app.events_receiver.next() => {
438                                            match events_chunk {
439                                                Some(EventsChunk::Processed(processed_events)) => {
440                                                    let events_executor_adapter = EventsExecutorAdapter {
441                                                        runner: &mut app.runner,
442                                                    };
443                                                    events_executor_adapter.run(&mut app.nodes_state, processed_events);
444                                                }
445                                                Some(EventsChunk::Batch(events)) => {
446                                                    for event in events {
447                                                        app.runner.handle_event(event.node_id, event.name, event.data, event.bubbles);
448                                                    }
449                                                }
450                                                _ => {}
451                                            }
452                                        },
453                                        _ = app.runner.handle_events().fuse() => {},
454                                    }
455                                });
456
457                                match fut.poll(&mut cx) {
458                                    std::task::Poll::Ready(_) => {
459                                        self.proxy
460                                            .send_event(NativeEvent::Window(NativeWindowEvent {
461                                                window_id: app.window.id(),
462                                                action: NativeWindowEventAction::PollRunner,
463                                            }))
464                                            .ok();
465                                    }
466                                    std::task::Poll::Pending => {}
467                                }
468                            }
469
470                            self.plugins.send(
471                                PluginEvent::StartedUpdatingTree {
472                                    window: &app.window,
473                                    tree: &app.tree,
474                                },
475                                PluginHandle::new(&self.proxy),
476                            );
477                            let mutations = app.runner.sync_and_update();
478                            let result = app.runner.run_in(|| app.tree.apply_mutations(mutations));
479                            if result.needs_render {
480                                app.process_layout_on_next_render = true;
481                                app.window.request_redraw();
482                            }
483                            #[cfg(feature = "hotreload")]
484                            if hotreload_triggered {
485                                // Hot-patches can change closure bodies and custom `ElementExt` impls
486                                // that `PartialEq` can't observe, so force a layout + redraw.
487                                app.process_layout_on_next_render = true;
488                                app.window.request_redraw();
489                            }
490                            if result.needs_accessibility {
491                                app.accessibility_tasks_for_next_render |=
492                                    AccessibilityTask::ProcessUpdate { mode: None };
493                                app.window.request_redraw();
494                            }
495                            self.plugins.send(
496                                PluginEvent::FinishedUpdatingTree {
497                                    window: &app.window,
498                                    tree: &app.tree,
499                                },
500                                PluginHandle::new(&self.proxy),
501                            );
502                            #[cfg(debug_assertions)]
503                            {
504                                tracing::info!("Updated app tree.");
505                                tracing::info!("{:#?}", app.tree);
506                                tracing::info!("{:#?}", app.runner);
507                            }
508                        }
509                        NativeWindowEventAction::Accessibility(
510                            accesskit_winit::WindowEvent::AccessibilityDeactivated,
511                        ) => {
512                            self.screen_reader.set(false);
513                        }
514                        NativeWindowEventAction::Accessibility(
515                            accesskit_winit::WindowEvent::ActionRequested(_),
516                        ) => {}
517                        NativeWindowEventAction::Accessibility(
518                            accesskit_winit::WindowEvent::InitialTreeRequested,
519                        ) => {
520                            app.accessibility_tasks_for_next_render = AccessibilityTask::Init;
521                            app.window.request_redraw();
522                            self.screen_reader.set(true);
523                        }
524                        NativeWindowEventAction::User(user_event) => match user_event {
525                            UserEvent::RequestRedraw => {
526                                app.window.request_redraw();
527                            }
528                            UserEvent::FocusAccessibilityNode(strategy) => {
529                                let task = match strategy {
530                                    AccessibilityFocusStrategy::Backward(_)
531                                    | AccessibilityFocusStrategy::Forward(_) => {
532                                        AccessibilityTask::ProcessUpdate {
533                                            mode: Some(NavigationMode::Keyboard),
534                                        }
535                                    }
536                                    _ => AccessibilityTask::ProcessUpdate { mode: None },
537                                };
538                                app.tree.accessibility_diff.request_focus(strategy);
539                                app.accessibility_tasks_for_next_render = task;
540                                app.window.request_redraw();
541                            }
542                            UserEvent::SetCursorIcon(cursor_icon) => {
543                                app.window.set_cursor(cursor_icon);
544                            }
545                            UserEvent::Erased(data) => {
546                                let action = data
547                                    .0
548                                    .downcast::<NativeWindowErasedEventAction>()
549                                    .expect("Expected NativeWindowErasedEventAction");
550                                match *action {
551                                    NativeWindowErasedEventAction::LaunchWindow {
552                                        window_config,
553                                        ack,
554                                    } => {
555                                        let app_window = AppWindow::new(
556                                            window_config,
557                                            active_event_loop,
558                                            &self.proxy,
559                                            &mut self.plugins,
560                                            &mut self.font_collection,
561                                            &self.font_manager,
562                                            &self.fallback_fonts,
563                                            self.screen_reader.clone(),
564                                        );
565
566                                        let window_id = app_window.window.id();
567
568                                        let _ = self.proxy.send_event(NativeEvent::Window(
569                                            NativeWindowEvent {
570                                                window_id,
571                                                action: NativeWindowEventAction::PollRunner,
572                                            },
573                                        ));
574
575                                        self.windows.insert(window_id, app_window);
576                                        let _ = ack.send(window_id);
577                                    }
578                                    NativeWindowErasedEventAction::CloseWindow(window_id) => {
579                                        // Its fine to ignore if the window doesnt exist anymore
580                                        let _ = self.windows.remove(&window_id);
581                                        let has_windows = !self.windows.is_empty();
582
583                                        let has_tray = {
584                                            #[cfg(feature = "tray")]
585                                            {
586                                                self.tray.1.is_some()
587                                            }
588                                            #[cfg(not(feature = "tray"))]
589                                            {
590                                                false
591                                            }
592                                        };
593
594                                        // Only exit when there is no window and no tray
595                                        if !has_windows && !has_tray && self.exit_on_close {
596                                            active_event_loop.exit();
597                                        }
598                                    }
599                                    NativeWindowErasedEventAction::RendererCallback(cb) => {
600                                        let window_id = app.window.id();
601                                        let mut renderer_context = RendererContext {
602                                            fallback_fonts: &mut self.fallback_fonts,
603                                            active_event_loop,
604                                            windows: &mut self.windows,
605                                            proxy: &mut self.proxy,
606                                            plugins: &mut self.plugins,
607                                            screen_reader: &mut self.screen_reader,
608                                            font_manager: &mut self.font_manager,
609                                            font_collection: &mut self.font_collection,
610                                        };
611                                        (cb)(window_id, &mut renderer_context);
612                                    }
613                                }
614                            }
615                        },
616                        NativeWindowEventAction::PlatformEvent(platform_event) => {
617                            let mut events_measurer_adapter = EventsMeasurerAdapter {
618                                tree: &mut app.tree,
619                                scale_factor: app.window.scale_factor(),
620                            };
621                            let processed_events = events_measurer_adapter.run(
622                                &mut vec![platform_event],
623                                &mut app.nodes_state,
624                                app.accessibility.focused_node_id(),
625                            );
626                            app.events_sender
627                                .unbounded_send(EventsChunk::Processed(processed_events))
628                                .unwrap();
629                        }
630                    }
631                }
632            }
633        }
634    }
635
636    fn window_event(
637        &mut self,
638        event_loop: &winit::event_loop::ActiveEventLoop,
639        window_id: winit::window::WindowId,
640        event: winit::event::WindowEvent,
641    ) {
642        if let Some(app) = &mut self.windows.get_mut(&window_id) {
643            app.accessibility_adapter.process_event(&app.window, &event);
644            match event {
645                WindowEvent::ThemeChanged(theme) => {
646                    app.platform.preferred_theme.set(match theme {
647                        Theme::Light => PreferredTheme::Light,
648                        Theme::Dark => PreferredTheme::Dark,
649                    });
650                }
651                WindowEvent::ScaleFactorChanged { .. } => {
652                    app.window.request_redraw();
653                    app.process_layout_on_next_render = true;
654                    app.tree.layout.reset();
655                    app.tree.text_cache.reset();
656                }
657                WindowEvent::CloseRequested => {
658                    let mut on_close_hook = self
659                        .windows
660                        .get_mut(&window_id)
661                        .and_then(|app| app.on_close.take());
662
663                    let decision = if let Some(ref mut on_close) = on_close_hook {
664                        let renderer_context = RendererContext {
665                            fallback_fonts: &mut self.fallback_fonts,
666                            active_event_loop: event_loop,
667                            windows: &mut self.windows,
668                            proxy: &mut self.proxy,
669                            plugins: &mut self.plugins,
670                            screen_reader: &mut self.screen_reader,
671                            font_manager: &mut self.font_manager,
672                            font_collection: &mut self.font_collection,
673                        };
674                        on_close(renderer_context, window_id)
675                    } else {
676                        CloseDecision::Close
677                    };
678
679                    if matches!(decision, CloseDecision::KeepOpen)
680                        && let Some(app) = self.windows.get_mut(&window_id)
681                    {
682                        app.on_close = on_close_hook;
683                    }
684
685                    if matches!(decision, CloseDecision::Close) {
686                        self.windows.remove(&window_id);
687                        let has_windows = !self.windows.is_empty();
688
689                        let has_tray = {
690                            #[cfg(feature = "tray")]
691                            {
692                                self.tray.1.is_some()
693                            }
694                            #[cfg(not(feature = "tray"))]
695                            {
696                                false
697                            }
698                        };
699
700                        // Only exit when there is no windows and no tray
701                        if !has_windows && !has_tray && self.exit_on_close {
702                            event_loop.exit();
703                        }
704                    }
705                }
706                WindowEvent::ModifiersChanged(modifiers) => {
707                    app.modifiers_state = modifiers.state();
708                }
709                WindowEvent::Focused(is_focused) => {
710                    if cfg!(not(target_os = "android")) {
711                        // The focused workaround is only for desktop targets
712                        app.just_focused = is_focused;
713                    }
714                }
715                WindowEvent::RedrawRequested => {
716                    hotpath::measure_block!("RedrawRequested", {
717                        if app.process_layout_on_next_render {
718                            self.plugins.send(
719                                PluginEvent::StartedMeasuringLayout {
720                                    window: &app.window,
721                                    tree: &app.tree,
722                                },
723                                PluginHandle::new(&self.proxy),
724                            );
725                            let size: Size2D = (
726                                app.window.inner_size().width as f32,
727                                app.window.inner_size().height as f32,
728                            )
729                                .into();
730
731                            app.tree.measure_layout(
732                                size,
733                                &mut self.font_collection,
734                                &self.font_manager,
735                                &app.events_sender,
736                                app.window.scale_factor(),
737                                &self.fallback_fonts,
738                            );
739                            app.platform.root_size.set_if_modified(size);
740                            app.process_layout_on_next_render = false;
741                            self.plugins.send(
742                                PluginEvent::FinishedMeasuringLayout {
743                                    window: &app.window,
744                                    tree: &app.tree,
745                                },
746                                PluginHandle::new(&self.proxy),
747                            );
748                        }
749
750                        app.driver.present(
751                            app.window.inner_size().cast(),
752                            &app.window,
753                            |surface| {
754                                self.plugins.send(
755                                    PluginEvent::BeforeRender {
756                                        window: &app.window,
757                                        canvas: surface.canvas(),
758                                        font_collection: &self.font_collection,
759                                        tree: &app.tree,
760                                    },
761                                    PluginHandle::new(&self.proxy),
762                                );
763
764                                let render_pipeline = RenderPipeline {
765                                    font_collection: &mut self.font_collection,
766                                    font_manager: &self.font_manager,
767                                    tree: &app.tree,
768                                    canvas: surface.canvas(),
769                                    scale_factor: app.window.scale_factor(),
770                                    background: app.background,
771                                };
772
773                                render_pipeline.render();
774
775                                self.plugins.send(
776                                    PluginEvent::AfterRender {
777                                        window: &app.window,
778                                        canvas: surface.canvas(),
779                                        font_collection: &self.font_collection,
780                                        tree: &app.tree,
781                                        animation_clock: &app.animation_clock,
782                                    },
783                                    PluginHandle::new(&self.proxy),
784                                );
785                                self.plugins.send(
786                                    PluginEvent::BeforePresenting {
787                                        window: &app.window,
788                                        font_collection: &self.font_collection,
789                                        tree: &app.tree,
790                                    },
791                                    PluginHandle::new(&self.proxy),
792                                );
793                            },
794                        );
795                        self.plugins.send(
796                            PluginEvent::AfterPresenting {
797                                window: &app.window,
798                                font_collection: &self.font_collection,
799                                tree: &app.tree,
800                            },
801                            PluginHandle::new(&self.proxy),
802                        );
803
804                        self.plugins.send(
805                            PluginEvent::BeforeAccessibility {
806                                window: &app.window,
807                                font_collection: &self.font_collection,
808                                tree: &app.tree,
809                            },
810                            PluginHandle::new(&self.proxy),
811                        );
812
813                        match app.accessibility_tasks_for_next_render.take() {
814                            AccessibilityTask::ProcessUpdate { mode } => {
815                                let update = app
816                                    .accessibility
817                                    .process_updates(&mut app.tree, &app.events_sender);
818                                app.platform
819                                    .focused_accessibility_id
820                                    .set_if_modified(update.focus);
821                                let node_id = app.accessibility.focused_node_id().unwrap();
822                                let layout_node = app.tree.layout.get(&node_id).unwrap();
823                                let focused_node =
824                                    AccessibilityTree::create_node(node_id, layout_node, &app.tree);
825                                app.window.set_ime_allowed(is_ime_role(focused_node.role()));
826                                app.platform
827                                    .focused_accessibility_node
828                                    .set_if_modified(focused_node);
829                                if let Some(mode) = mode {
830                                    app.platform.navigation_mode.set(mode);
831                                }
832
833                                let area = layout_node.visible_area();
834                                app.window.set_ime_cursor_area(
835                                    LogicalPosition::new(area.min_x(), area.min_y()),
836                                    LogicalSize::new(area.width(), area.height()),
837                                );
838
839                                app.accessibility_adapter.update_if_active(|| update);
840                            }
841                            AccessibilityTask::Init => {
842                                let update = app.accessibility.init(&mut app.tree);
843                                app.platform
844                                    .focused_accessibility_id
845                                    .set_if_modified(update.focus);
846                                let node_id = app.accessibility.focused_node_id().unwrap();
847                                let layout_node = app.tree.layout.get(&node_id).unwrap();
848                                let focused_node =
849                                    AccessibilityTree::create_node(node_id, layout_node, &app.tree);
850                                app.window.set_ime_allowed(is_ime_role(focused_node.role()));
851                                app.platform
852                                    .focused_accessibility_node
853                                    .set_if_modified(focused_node);
854
855                                let area = layout_node.visible_area();
856                                app.window.set_ime_cursor_area(
857                                    LogicalPosition::new(area.min_x(), area.min_y()),
858                                    LogicalSize::new(area.width(), area.height()),
859                                );
860
861                                app.accessibility_adapter.update_if_active(|| update);
862                            }
863                            AccessibilityTask::None => {}
864                        }
865
866                        self.plugins.send(
867                            PluginEvent::AfterAccessibility {
868                                window: &app.window,
869                                font_collection: &self.font_collection,
870                                tree: &app.tree,
871                            },
872                            PluginHandle::new(&self.proxy),
873                        );
874
875                        if app.ticker_sender.receiver_count() > 0 {
876                            app.ticker_sender.broadcast_blocking(()).unwrap();
877                        }
878
879                        self.plugins.send(
880                            PluginEvent::AfterRedraw {
881                                window: &app.window,
882                                font_collection: &self.font_collection,
883                                tree: &app.tree,
884                            },
885                            PluginHandle::new(&self.proxy),
886                        );
887                    });
888                }
889                WindowEvent::Resized(size) => {
890                    app.driver.resize(size);
891
892                    app.window.request_redraw();
893
894                    app.process_layout_on_next_render = true;
895                    app.tree.layout.clear_dirty();
896                    app.tree.layout.invalidate(NodeId::ROOT);
897                }
898
899                WindowEvent::MouseInput { state, button, .. } => {
900                    app.just_focused = false;
901                    app.mouse_state = state;
902                    app.platform
903                        .navigation_mode
904                        .set(NavigationMode::NotKeyboard);
905
906                    let name = if state == ElementState::Pressed {
907                        MouseEventName::MouseDown
908                    } else {
909                        MouseEventName::MouseUp
910                    };
911                    let platform_event = PlatformEvent::Mouse {
912                        name,
913                        cursor: (app.position.x, app.position.y).into(),
914                        button: Some(map_winit_mouse_button(button)),
915                    };
916                    let mut events_measurer_adapter = EventsMeasurerAdapter {
917                        tree: &mut app.tree,
918                        scale_factor: app.window.scale_factor(),
919                    };
920                    let processed_events = events_measurer_adapter.run(
921                        &mut vec![platform_event],
922                        &mut app.nodes_state,
923                        app.accessibility.focused_node_id(),
924                    );
925                    app.events_sender
926                        .unbounded_send(EventsChunk::Processed(processed_events))
927                        .unwrap();
928                }
929
930                WindowEvent::KeyboardInput { event, .. } => {
931                    // Workaround for winit sending a Tab event when alt-tabbing
932                    if app.just_focused {
933                        app.just_focused = false;
934                        return;
935                    }
936
937                    let name = match event.state {
938                        ElementState::Pressed => KeyboardEventName::KeyDown,
939                        ElementState::Released => KeyboardEventName::KeyUp,
940                    };
941                    let key = winit_mappings::map_winit_key(&event.logical_key);
942                    let code = winit_mappings::map_winit_physical_key(&event.physical_key);
943                    let modifiers = winit_mappings::map_winit_modifiers(app.modifiers_state);
944
945                    self.plugins.send(
946                        PluginEvent::KeyboardInput {
947                            window: &app.window,
948                            key: key.clone(),
949                            code,
950                            modifiers,
951                            is_pressed: event.state.is_pressed(),
952                        },
953                        PluginHandle::new(&self.proxy),
954                    );
955
956                    let platform_event = PlatformEvent::Keyboard {
957                        name,
958                        key,
959                        code,
960                        modifiers,
961                    };
962                    let mut events_measurer_adapter = EventsMeasurerAdapter {
963                        tree: &mut app.tree,
964                        scale_factor: app.window.scale_factor(),
965                    };
966                    let processed_events = events_measurer_adapter.run(
967                        &mut vec![platform_event],
968                        &mut app.nodes_state,
969                        app.accessibility.focused_node_id(),
970                    );
971                    app.events_sender
972                        .unbounded_send(EventsChunk::Processed(processed_events))
973                        .unwrap();
974                }
975
976                WindowEvent::MouseWheel { delta, phase, .. } => {
977                    const WHEEL_SPEED_MODIFIER: f64 = 53.0;
978                    const TOUCHPAD_SPEED_MODIFIER: f64 = 2.0;
979
980                    if TouchPhase::Moved == phase {
981                        let scroll_data = {
982                            match delta {
983                                MouseScrollDelta::LineDelta(x, y) => (
984                                    (x as f64 * WHEEL_SPEED_MODIFIER),
985                                    (y as f64 * WHEEL_SPEED_MODIFIER),
986                                ),
987                                MouseScrollDelta::PixelDelta(pos) => (
988                                    (pos.x * TOUCHPAD_SPEED_MODIFIER),
989                                    (pos.y * TOUCHPAD_SPEED_MODIFIER),
990                                ),
991                            }
992                        };
993
994                        let platform_event = PlatformEvent::Wheel {
995                            name: WheelEventName::Wheel,
996                            scroll: scroll_data.into(),
997                            cursor: app.position,
998                            source: WheelSource::Device,
999                        };
1000                        let mut events_measurer_adapter = EventsMeasurerAdapter {
1001                            tree: &mut app.tree,
1002                            scale_factor: app.window.scale_factor(),
1003                        };
1004                        let processed_events = events_measurer_adapter.run(
1005                            &mut vec![platform_event],
1006                            &mut app.nodes_state,
1007                            app.accessibility.focused_node_id(),
1008                        );
1009                        app.events_sender
1010                            .unbounded_send(EventsChunk::Processed(processed_events))
1011                            .unwrap();
1012                    }
1013                }
1014
1015                WindowEvent::CursorLeft { .. } => {
1016                    if app.mouse_state == ElementState::Released {
1017                        app.position = CursorPoint::from((-1., -1.));
1018                        let platform_event = PlatformEvent::Mouse {
1019                            name: MouseEventName::MouseMove,
1020                            cursor: app.position,
1021                            button: None,
1022                        };
1023                        let mut events_measurer_adapter = EventsMeasurerAdapter {
1024                            tree: &mut app.tree,
1025                            scale_factor: app.window.scale_factor(),
1026                        };
1027                        let processed_events = events_measurer_adapter.run(
1028                            &mut vec![platform_event],
1029                            &mut app.nodes_state,
1030                            app.accessibility.focused_node_id(),
1031                        );
1032                        app.events_sender
1033                            .unbounded_send(EventsChunk::Processed(processed_events))
1034                            .unwrap();
1035                    }
1036                }
1037                WindowEvent::CursorMoved { position, .. } => {
1038                    app.just_focused = false;
1039                    app.position = CursorPoint::from((position.x, position.y));
1040
1041                    let mut platform_event = vec![PlatformEvent::Mouse {
1042                        name: MouseEventName::MouseMove,
1043                        cursor: app.position,
1044                        button: None,
1045                    }];
1046
1047                    for dropped_file_path in app.dropped_file_paths.drain(..) {
1048                        platform_event.push(PlatformEvent::File {
1049                            name: FileEventName::FileDrop,
1050                            file_path: Some(dropped_file_path),
1051                            cursor: app.position,
1052                        });
1053                    }
1054
1055                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1056                        tree: &mut app.tree,
1057                        scale_factor: app.window.scale_factor(),
1058                    };
1059                    let processed_events = events_measurer_adapter.run(
1060                        &mut platform_event,
1061                        &mut app.nodes_state,
1062                        app.accessibility.focused_node_id(),
1063                    );
1064                    app.events_sender
1065                        .unbounded_send(EventsChunk::Processed(processed_events))
1066                        .unwrap();
1067                }
1068
1069                WindowEvent::Touch(Touch {
1070                    location,
1071                    phase,
1072                    id,
1073                    force,
1074                    ..
1075                }) => {
1076                    app.position = CursorPoint::from((location.x, location.y));
1077
1078                    let name = match phase {
1079                        TouchPhase::Cancelled => TouchEventName::TouchCancel,
1080                        TouchPhase::Ended => TouchEventName::TouchEnd,
1081                        TouchPhase::Moved => TouchEventName::TouchMove,
1082                        TouchPhase::Started => TouchEventName::TouchStart,
1083                    };
1084
1085                    let platform_event = PlatformEvent::Touch {
1086                        name,
1087                        location: app.position,
1088                        finger_id: id,
1089                        phase: map_winit_touch_phase(phase),
1090                        force: force.map(map_winit_touch_force),
1091                    };
1092                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1093                        tree: &mut app.tree,
1094                        scale_factor: app.window.scale_factor(),
1095                    };
1096                    let processed_events = events_measurer_adapter.run(
1097                        &mut vec![platform_event],
1098                        &mut app.nodes_state,
1099                        app.accessibility.focused_node_id(),
1100                    );
1101                    app.events_sender
1102                        .unbounded_send(EventsChunk::Processed(processed_events))
1103                        .unwrap();
1104                    app.position = CursorPoint::from((location.x, location.y));
1105                }
1106                WindowEvent::Ime(Ime::Commit(text)) => {
1107                    let platform_event = PlatformEvent::Keyboard {
1108                        name: KeyboardEventName::KeyDown,
1109                        key: keyboard_types::Key::Character(text),
1110                        code: keyboard_types::Code::Unidentified,
1111                        modifiers: winit_mappings::map_winit_modifiers(app.modifiers_state),
1112                    };
1113                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1114                        tree: &mut app.tree,
1115                        scale_factor: app.window.scale_factor(),
1116                    };
1117                    let processed_events = events_measurer_adapter.run(
1118                        &mut vec![platform_event],
1119                        &mut app.nodes_state,
1120                        app.accessibility.focused_node_id(),
1121                    );
1122                    app.events_sender
1123                        .unbounded_send(EventsChunk::Processed(processed_events))
1124                        .unwrap();
1125                }
1126                WindowEvent::Ime(Ime::Preedit(text, pos)) => {
1127                    let platform_event = PlatformEvent::ImePreedit {
1128                        name: ImeEventName::Preedit,
1129                        text,
1130                        cursor: pos,
1131                    };
1132                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1133                        tree: &mut app.tree,
1134                        scale_factor: app.window.scale_factor(),
1135                    };
1136                    let processed_events = events_measurer_adapter.run(
1137                        &mut vec![platform_event],
1138                        &mut app.nodes_state,
1139                        app.accessibility.focused_node_id(),
1140                    );
1141                    app.events_sender
1142                        .unbounded_send(EventsChunk::Processed(processed_events))
1143                        .unwrap();
1144                }
1145                WindowEvent::DroppedFile(file_path) => {
1146                    app.dropped_file_paths.push(file_path);
1147                }
1148                WindowEvent::HoveredFile(file_path) => {
1149                    let platform_event = PlatformEvent::File {
1150                        name: FileEventName::FileHover,
1151                        file_path: Some(file_path),
1152                        cursor: app.position,
1153                    };
1154                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1155                        tree: &mut app.tree,
1156                        scale_factor: app.window.scale_factor(),
1157                    };
1158                    let processed_events = events_measurer_adapter.run(
1159                        &mut vec![platform_event],
1160                        &mut app.nodes_state,
1161                        app.accessibility.focused_node_id(),
1162                    );
1163                    app.events_sender
1164                        .unbounded_send(EventsChunk::Processed(processed_events))
1165                        .unwrap();
1166                }
1167                WindowEvent::HoveredFileCancelled => {
1168                    let platform_event = PlatformEvent::File {
1169                        name: FileEventName::FileHoverCancelled,
1170                        file_path: None,
1171                        cursor: app.position,
1172                    };
1173                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1174                        tree: &mut app.tree,
1175                        scale_factor: app.window.scale_factor(),
1176                    };
1177                    let processed_events = events_measurer_adapter.run(
1178                        &mut vec![platform_event],
1179                        &mut app.nodes_state,
1180                        app.accessibility.focused_node_id(),
1181                    );
1182                    app.events_sender
1183                        .unbounded_send(EventsChunk::Processed(processed_events))
1184                        .unwrap();
1185                }
1186                _ => {}
1187            }
1188        }
1189    }
1190}
1191
1192fn subscribe_preferences(proxy: EventLoopProxy<NativeEvent>) {
1193    let subscription = mundy::Preferences::subscribe(mundy::Interest::AccentColor, move |prefs| {
1194        let _ = proxy.send_event(NativeEvent::Preferences(prefs));
1195    });
1196    std::mem::forget(subscription);
1197}