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}
251
252impl From<accesskit_winit::Event> for NativeEvent {
253    fn from(event: accesskit_winit::Event) -> Self {
254        NativeEvent::Window(NativeWindowEvent {
255            window_id: event.window_id,
256            action: NativeWindowEventAction::Accessibility(event.window_event),
257        })
258    }
259}
260
261impl ApplicationHandler<NativeEvent> for WinitRenderer {
262    fn resumed(&mut self, active_event_loop: &winit::event_loop::ActiveEventLoop) {
263        if !self.resumed {
264            #[cfg(feature = "tray")]
265            {
266                #[cfg(not(target_os = "linux"))]
267                if let Some(tray_icon) = self.tray.0.take() {
268                    self.tray_icon = Some((tray_icon)());
269                }
270
271                #[cfg(target_os = "macos")]
272                {
273                    use objc2_core_foundation::CFRunLoop;
274
275                    let rl = CFRunLoop::main().expect("Failed to run CFRunLoop");
276                    CFRunLoop::wake_up(&rl);
277                }
278            }
279
280            for window_config in self.windows_configs.drain(..) {
281                let app_window = AppWindow::new(
282                    window_config,
283                    active_event_loop,
284                    &self.proxy,
285                    &mut self.plugins,
286                    &mut self.font_collection,
287                    &self.font_manager,
288                    &self.fallback_fonts,
289                    self.screen_reader.clone(),
290                );
291
292                self.proxy
293                    .send_event(NativeEvent::Window(NativeWindowEvent {
294                        window_id: app_window.window.id(),
295                        action: NativeWindowEventAction::PollRunner,
296                    }))
297                    .ok();
298
299                self.windows.insert(app_window.window.id(), app_window);
300            }
301            self.resumed = true;
302
303            let _ = self
304                .proxy
305                .send_event(NativeEvent::Generic(NativeGenericEvent::PollFutures));
306        } else {
307            // [Android] Recreate the GraphicsDriver when the app gets brought into the foreground after being suspended,
308            // so we don't end up with a completely black surface with broken rendering.
309            let old_windows: Vec<_> = self.windows.drain().collect();
310            for (_, mut app_window) in old_windows {
311                let (new_driver, new_window) =
312                    GraphicsDriver::new(active_event_loop, app_window.window_attributes.clone());
313
314                let new_id = new_window.id();
315                app_window.driver = new_driver;
316                app_window.window = new_window;
317                app_window.process_layout_on_next_render = true;
318                app_window.tree.layout.reset();
319
320                self.windows.insert(new_id, app_window);
321
322                self.proxy
323                    .send_event(NativeEvent::Window(NativeWindowEvent {
324                        window_id: new_id,
325                        action: NativeWindowEventAction::PollRunner,
326                    }))
327                    .ok();
328            }
329        }
330    }
331
332    fn user_event(
333        &mut self,
334        active_event_loop: &winit::event_loop::ActiveEventLoop,
335        event: NativeEvent,
336    ) {
337        match event {
338            NativeEvent::Generic(NativeGenericEvent::RendererCallback(cb)) => {
339                let mut renderer_context = RendererContext {
340                    fallback_fonts: &mut self.fallback_fonts,
341                    active_event_loop,
342                    windows: &mut self.windows,
343                    proxy: &mut self.proxy,
344                    plugins: &mut self.plugins,
345                    screen_reader: &mut self.screen_reader,
346                    font_manager: &mut self.font_manager,
347                    font_collection: &mut self.font_collection,
348                };
349                (cb)(&mut renderer_context);
350            }
351            NativeEvent::Generic(NativeGenericEvent::PollFutures) => {
352                let mut cx = std::task::Context::from_waker(&self.waker);
353                self.futures
354                    .retain_mut(|fut| fut.poll(&mut cx).is_pending());
355            }
356            #[cfg(feature = "tray")]
357            NativeEvent::Tray(NativeTrayEvent { action }) => {
358                let renderer_context = RendererContext {
359                    fallback_fonts: &mut self.fallback_fonts,
360                    active_event_loop,
361                    windows: &mut self.windows,
362                    proxy: &mut self.proxy,
363                    plugins: &mut self.plugins,
364                    screen_reader: &mut self.screen_reader,
365                    font_manager: &mut self.font_manager,
366                    font_collection: &mut self.font_collection,
367                };
368                match action {
369                    NativeTrayEventAction::TrayEvent(icon_event) => {
370                        use crate::tray::TrayEvent;
371                        if let Some(tray_handler) = &mut self.tray.1 {
372                            (tray_handler)(TrayEvent::Icon(icon_event), renderer_context)
373                        }
374                    }
375                    NativeTrayEventAction::MenuEvent(menu_event) => {
376                        use crate::tray::TrayEvent;
377                        if let Some(tray_handler) = &mut self.tray.1 {
378                            (tray_handler)(TrayEvent::Menu(menu_event), renderer_context)
379                        }
380                    }
381                    NativeTrayEventAction::LaunchWindow(data) => {
382                        let window_config = data
383                            .0
384                            .downcast::<WindowConfig>()
385                            .expect("Expected WindowConfig");
386                        let app_window = AppWindow::new(
387                            *window_config,
388                            active_event_loop,
389                            &self.proxy,
390                            &mut self.plugins,
391                            &mut self.font_collection,
392                            &self.font_manager,
393                            &self.fallback_fonts,
394                            self.screen_reader.clone(),
395                        );
396
397                        self.proxy
398                            .send_event(NativeEvent::Window(NativeWindowEvent {
399                                window_id: app_window.window.id(),
400                                action: NativeWindowEventAction::PollRunner,
401                            }))
402                            .ok();
403
404                        self.windows.insert(app_window.window.id(), app_window);
405                    }
406                }
407            }
408            NativeEvent::Window(NativeWindowEvent { action, window_id }) => {
409                if let Some(app) = &mut self.windows.get_mut(&window_id) {
410                    match action {
411                        NativeWindowEventAction::PollRunner => {
412                            let mut cx = std::task::Context::from_waker(&app.waker);
413
414                            {
415                                let fut = std::pin::pin!(async {
416                                    select! {
417                                        events_chunk = app.events_receiver.next() => {
418                                            match events_chunk {
419                                                Some(EventsChunk::Processed(processed_events)) => {
420                                                    let events_executor_adapter = EventsExecutorAdapter {
421                                                        runner: &mut app.runner,
422                                                    };
423                                                    events_executor_adapter.run(&mut app.nodes_state, processed_events);
424                                                }
425                                                Some(EventsChunk::Batch(events)) => {
426                                                    for event in events {
427                                                        app.runner.handle_event(event.node_id, event.name, event.data, event.bubbles);
428                                                    }
429                                                }
430                                                _ => {}
431                                            }
432
433                                        },
434                                         _ = app.runner.handle_events().fuse() => {},
435                                    }
436                                });
437
438                                match fut.poll(&mut cx) {
439                                    std::task::Poll::Ready(_) => {
440                                        self.proxy
441                                            .send_event(NativeEvent::Window(NativeWindowEvent {
442                                                window_id: app.window.id(),
443                                                action: NativeWindowEventAction::PollRunner,
444                                            }))
445                                            .ok();
446                                    }
447                                    std::task::Poll::Pending => {}
448                                }
449                            }
450
451                            self.plugins.send(
452                                PluginEvent::StartedUpdatingTree {
453                                    window: &app.window,
454                                    tree: &app.tree,
455                                },
456                                PluginHandle::new(&self.proxy),
457                            );
458                            let mutations = app.runner.sync_and_update();
459                            let result = app.runner.run_in(|| app.tree.apply_mutations(mutations));
460                            if result.needs_render {
461                                app.process_layout_on_next_render = true;
462                                app.window.request_redraw();
463                            }
464                            if result.needs_accessibility {
465                                app.accessibility_tasks_for_next_render |=
466                                    AccessibilityTask::ProcessUpdate { mode: None };
467                                app.window.request_redraw();
468                            }
469                            self.plugins.send(
470                                PluginEvent::FinishedUpdatingTree {
471                                    window: &app.window,
472                                    tree: &app.tree,
473                                },
474                                PluginHandle::new(&self.proxy),
475                            );
476                            #[cfg(debug_assertions)]
477                            {
478                                tracing::info!("Updated app tree.");
479                                tracing::info!("{:#?}", app.tree);
480                                tracing::info!("{:#?}", app.runner);
481                            }
482                        }
483                        NativeWindowEventAction::Accessibility(
484                            accesskit_winit::WindowEvent::AccessibilityDeactivated,
485                        ) => {
486                            self.screen_reader.set(false);
487                        }
488                        NativeWindowEventAction::Accessibility(
489                            accesskit_winit::WindowEvent::ActionRequested(_),
490                        ) => {}
491                        NativeWindowEventAction::Accessibility(
492                            accesskit_winit::WindowEvent::InitialTreeRequested,
493                        ) => {
494                            app.accessibility_tasks_for_next_render = AccessibilityTask::Init;
495                            app.window.request_redraw();
496                            self.screen_reader.set(true);
497                        }
498                        NativeWindowEventAction::User(user_event) => match user_event {
499                            UserEvent::RequestRedraw => {
500                                app.window.request_redraw();
501                            }
502                            UserEvent::FocusAccessibilityNode(strategy) => {
503                                let task = match strategy {
504                                    AccessibilityFocusStrategy::Backward(_)
505                                    | AccessibilityFocusStrategy::Forward(_) => {
506                                        AccessibilityTask::ProcessUpdate {
507                                            mode: Some(NavigationMode::Keyboard),
508                                        }
509                                    }
510                                    _ => AccessibilityTask::ProcessUpdate { mode: None },
511                                };
512                                app.tree.accessibility_diff.request_focus(strategy);
513                                app.accessibility_tasks_for_next_render = task;
514                                app.window.request_redraw();
515                            }
516                            UserEvent::SetCursorIcon(cursor_icon) => {
517                                app.window.set_cursor(cursor_icon);
518                            }
519                            UserEvent::Erased(data) => {
520                                let action = data
521                                    .0
522                                    .downcast::<NativeWindowErasedEventAction>()
523                                    .expect("Expected NativeWindowErasedEventAction");
524                                match *action {
525                                    NativeWindowErasedEventAction::LaunchWindow {
526                                        window_config,
527                                        ack,
528                                    } => {
529                                        let app_window = AppWindow::new(
530                                            window_config,
531                                            active_event_loop,
532                                            &self.proxy,
533                                            &mut self.plugins,
534                                            &mut self.font_collection,
535                                            &self.font_manager,
536                                            &self.fallback_fonts,
537                                            self.screen_reader.clone(),
538                                        );
539
540                                        let window_id = app_window.window.id();
541
542                                        let _ = self.proxy.send_event(NativeEvent::Window(
543                                            NativeWindowEvent {
544                                                window_id,
545                                                action: NativeWindowEventAction::PollRunner,
546                                            },
547                                        ));
548
549                                        self.windows.insert(window_id, app_window);
550                                        let _ = ack.send(window_id);
551                                    }
552                                    NativeWindowErasedEventAction::CloseWindow(window_id) => {
553                                        // Its fine to ignore if the window doesnt exist anymore
554                                        let _ = self.windows.remove(&window_id);
555                                        let has_windows = !self.windows.is_empty();
556
557                                        let has_tray = {
558                                            #[cfg(feature = "tray")]
559                                            {
560                                                self.tray.1.is_some()
561                                            }
562                                            #[cfg(not(feature = "tray"))]
563                                            {
564                                                false
565                                            }
566                                        };
567
568                                        // Only exit when there is no window and no tray
569                                        if !has_windows && !has_tray && self.exit_on_close {
570                                            active_event_loop.exit();
571                                        }
572                                    }
573                                    NativeWindowErasedEventAction::RendererCallback(cb) => {
574                                        let window_id = app.window.id();
575                                        let mut renderer_context = RendererContext {
576                                            fallback_fonts: &mut self.fallback_fonts,
577                                            active_event_loop,
578                                            windows: &mut self.windows,
579                                            proxy: &mut self.proxy,
580                                            plugins: &mut self.plugins,
581                                            screen_reader: &mut self.screen_reader,
582                                            font_manager: &mut self.font_manager,
583                                            font_collection: &mut self.font_collection,
584                                        };
585                                        (cb)(window_id, &mut renderer_context);
586                                    }
587                                }
588                            }
589                        },
590                        NativeWindowEventAction::PlatformEvent(platform_event) => {
591                            let mut events_measurer_adapter = EventsMeasurerAdapter {
592                                tree: &mut app.tree,
593                                scale_factor: app.window.scale_factor(),
594                            };
595                            let processed_events = events_measurer_adapter.run(
596                                &mut vec![platform_event],
597                                &mut app.nodes_state,
598                                app.accessibility.focused_node_id(),
599                            );
600                            app.events_sender
601                                .unbounded_send(EventsChunk::Processed(processed_events))
602                                .unwrap();
603                        }
604                    }
605                }
606            }
607        }
608    }
609
610    fn window_event(
611        &mut self,
612        event_loop: &winit::event_loop::ActiveEventLoop,
613        window_id: winit::window::WindowId,
614        event: winit::event::WindowEvent,
615    ) {
616        if let Some(app) = &mut self.windows.get_mut(&window_id) {
617            app.accessibility_adapter.process_event(&app.window, &event);
618            match event {
619                WindowEvent::ThemeChanged(theme) => {
620                    app.platform.preferred_theme.set(match theme {
621                        Theme::Light => PreferredTheme::Light,
622                        Theme::Dark => PreferredTheme::Dark,
623                    });
624                }
625                WindowEvent::ScaleFactorChanged { .. } => {
626                    app.window.request_redraw();
627                    app.process_layout_on_next_render = true;
628                    app.tree.layout.reset();
629                    app.tree.text_cache.reset();
630                }
631                WindowEvent::CloseRequested => {
632                    let mut on_close_hook = self
633                        .windows
634                        .get_mut(&window_id)
635                        .and_then(|app| app.on_close.take());
636
637                    let decision = if let Some(ref mut on_close) = on_close_hook {
638                        let renderer_context = RendererContext {
639                            fallback_fonts: &mut self.fallback_fonts,
640                            active_event_loop: event_loop,
641                            windows: &mut self.windows,
642                            proxy: &mut self.proxy,
643                            plugins: &mut self.plugins,
644                            screen_reader: &mut self.screen_reader,
645                            font_manager: &mut self.font_manager,
646                            font_collection: &mut self.font_collection,
647                        };
648                        on_close(renderer_context, window_id)
649                    } else {
650                        CloseDecision::Close
651                    };
652
653                    if matches!(decision, CloseDecision::KeepOpen)
654                        && let Some(app) = self.windows.get_mut(&window_id)
655                    {
656                        app.on_close = on_close_hook;
657                    }
658
659                    if matches!(decision, CloseDecision::Close) {
660                        self.windows.remove(&window_id);
661                        let has_windows = !self.windows.is_empty();
662
663                        let has_tray = {
664                            #[cfg(feature = "tray")]
665                            {
666                                self.tray.1.is_some()
667                            }
668                            #[cfg(not(feature = "tray"))]
669                            {
670                                false
671                            }
672                        };
673
674                        // Only exit when there is no windows and no tray
675                        if !has_windows && !has_tray && self.exit_on_close {
676                            event_loop.exit();
677                        }
678                    }
679                }
680                WindowEvent::ModifiersChanged(modifiers) => {
681                    app.modifiers_state = modifiers.state();
682                }
683                WindowEvent::Focused(is_focused) => {
684                    if cfg!(not(target_os = "android")) {
685                        // The focused workaround is only for desktop targets
686                        app.just_focused = is_focused;
687                    }
688                }
689                WindowEvent::RedrawRequested => {
690                    hotpath::measure_block!("RedrawRequested", {
691                        if app.process_layout_on_next_render {
692                            self.plugins.send(
693                                PluginEvent::StartedMeasuringLayout {
694                                    window: &app.window,
695                                    tree: &app.tree,
696                                },
697                                PluginHandle::new(&self.proxy),
698                            );
699                            let size: Size2D = (
700                                app.window.inner_size().width as f32,
701                                app.window.inner_size().height as f32,
702                            )
703                                .into();
704
705                            app.tree.measure_layout(
706                                size,
707                                &mut self.font_collection,
708                                &self.font_manager,
709                                &app.events_sender,
710                                app.window.scale_factor(),
711                                &self.fallback_fonts,
712                            );
713                            app.platform.root_size.set_if_modified(size);
714                            app.process_layout_on_next_render = false;
715                            self.plugins.send(
716                                PluginEvent::FinishedMeasuringLayout {
717                                    window: &app.window,
718                                    tree: &app.tree,
719                                },
720                                PluginHandle::new(&self.proxy),
721                            );
722                        }
723
724                        app.driver.present(
725                            app.window.inner_size().cast(),
726                            &app.window,
727                            |surface| {
728                                self.plugins.send(
729                                    PluginEvent::BeforeRender {
730                                        window: &app.window,
731                                        canvas: surface.canvas(),
732                                        font_collection: &self.font_collection,
733                                        tree: &app.tree,
734                                    },
735                                    PluginHandle::new(&self.proxy),
736                                );
737
738                                let render_pipeline = RenderPipeline {
739                                    font_collection: &mut self.font_collection,
740                                    font_manager: &self.font_manager,
741                                    tree: &app.tree,
742                                    canvas: surface.canvas(),
743                                    scale_factor: app.window.scale_factor(),
744                                    background: app.background,
745                                };
746
747                                render_pipeline.render();
748
749                                self.plugins.send(
750                                    PluginEvent::AfterRender {
751                                        window: &app.window,
752                                        canvas: surface.canvas(),
753                                        font_collection: &self.font_collection,
754                                        tree: &app.tree,
755                                        animation_clock: &app.animation_clock,
756                                    },
757                                    PluginHandle::new(&self.proxy),
758                                );
759                                self.plugins.send(
760                                    PluginEvent::BeforePresenting {
761                                        window: &app.window,
762                                        font_collection: &self.font_collection,
763                                        tree: &app.tree,
764                                    },
765                                    PluginHandle::new(&self.proxy),
766                                );
767                            },
768                        );
769                        self.plugins.send(
770                            PluginEvent::AfterPresenting {
771                                window: &app.window,
772                                font_collection: &self.font_collection,
773                                tree: &app.tree,
774                            },
775                            PluginHandle::new(&self.proxy),
776                        );
777
778                        self.plugins.send(
779                            PluginEvent::BeforeAccessibility {
780                                window: &app.window,
781                                font_collection: &self.font_collection,
782                                tree: &app.tree,
783                            },
784                            PluginHandle::new(&self.proxy),
785                        );
786
787                        match app.accessibility_tasks_for_next_render.take() {
788                            AccessibilityTask::ProcessUpdate { mode } => {
789                                let update = app
790                                    .accessibility
791                                    .process_updates(&mut app.tree, &app.events_sender);
792                                app.platform
793                                    .focused_accessibility_id
794                                    .set_if_modified(update.focus);
795                                let node_id = app.accessibility.focused_node_id().unwrap();
796                                let layout_node = app.tree.layout.get(&node_id).unwrap();
797                                let focused_node =
798                                    AccessibilityTree::create_node(node_id, layout_node, &app.tree);
799                                app.window.set_ime_allowed(is_ime_role(focused_node.role()));
800                                app.platform
801                                    .focused_accessibility_node
802                                    .set_if_modified(focused_node);
803                                if let Some(mode) = mode {
804                                    app.platform.navigation_mode.set(mode);
805                                }
806
807                                let area = layout_node.visible_area();
808                                app.window.set_ime_cursor_area(
809                                    LogicalPosition::new(area.min_x(), area.min_y()),
810                                    LogicalSize::new(area.width(), area.height()),
811                                );
812
813                                app.accessibility_adapter.update_if_active(|| update);
814                            }
815                            AccessibilityTask::Init => {
816                                let update = app.accessibility.init(&mut app.tree);
817                                app.platform
818                                    .focused_accessibility_id
819                                    .set_if_modified(update.focus);
820                                let node_id = app.accessibility.focused_node_id().unwrap();
821                                let layout_node = app.tree.layout.get(&node_id).unwrap();
822                                let focused_node =
823                                    AccessibilityTree::create_node(node_id, layout_node, &app.tree);
824                                app.window.set_ime_allowed(is_ime_role(focused_node.role()));
825                                app.platform
826                                    .focused_accessibility_node
827                                    .set_if_modified(focused_node);
828
829                                let area = layout_node.visible_area();
830                                app.window.set_ime_cursor_area(
831                                    LogicalPosition::new(area.min_x(), area.min_y()),
832                                    LogicalSize::new(area.width(), area.height()),
833                                );
834
835                                app.accessibility_adapter.update_if_active(|| update);
836                            }
837                            AccessibilityTask::None => {}
838                        }
839
840                        self.plugins.send(
841                            PluginEvent::AfterAccessibility {
842                                window: &app.window,
843                                font_collection: &self.font_collection,
844                                tree: &app.tree,
845                            },
846                            PluginHandle::new(&self.proxy),
847                        );
848
849                        if app.ticker_sender.receiver_count() > 0 {
850                            app.ticker_sender.broadcast_blocking(()).unwrap();
851                        }
852
853                        self.plugins.send(
854                            PluginEvent::AfterRedraw {
855                                window: &app.window,
856                                font_collection: &self.font_collection,
857                                tree: &app.tree,
858                            },
859                            PluginHandle::new(&self.proxy),
860                        );
861                    });
862                }
863                WindowEvent::Resized(size) => {
864                    app.driver.resize(size);
865
866                    app.window.request_redraw();
867
868                    app.process_layout_on_next_render = true;
869                    app.tree.layout.clear_dirty();
870                    app.tree.layout.invalidate(NodeId::ROOT);
871                }
872
873                WindowEvent::MouseInput { state, button, .. } => {
874                    app.just_focused = false;
875                    app.mouse_state = state;
876                    app.platform
877                        .navigation_mode
878                        .set(NavigationMode::NotKeyboard);
879
880                    let name = if state == ElementState::Pressed {
881                        MouseEventName::MouseDown
882                    } else {
883                        MouseEventName::MouseUp
884                    };
885                    let platform_event = PlatformEvent::Mouse {
886                        name,
887                        cursor: (app.position.x, app.position.y).into(),
888                        button: Some(map_winit_mouse_button(button)),
889                    };
890                    let mut events_measurer_adapter = EventsMeasurerAdapter {
891                        tree: &mut app.tree,
892                        scale_factor: app.window.scale_factor(),
893                    };
894                    let processed_events = events_measurer_adapter.run(
895                        &mut vec![platform_event],
896                        &mut app.nodes_state,
897                        app.accessibility.focused_node_id(),
898                    );
899                    app.events_sender
900                        .unbounded_send(EventsChunk::Processed(processed_events))
901                        .unwrap();
902                }
903
904                WindowEvent::KeyboardInput { event, .. } => {
905                    // Workaround for winit sending a Tab event when alt-tabbing
906                    if app.just_focused {
907                        app.just_focused = false;
908                        return;
909                    }
910
911                    let name = match event.state {
912                        ElementState::Pressed => KeyboardEventName::KeyDown,
913                        ElementState::Released => KeyboardEventName::KeyUp,
914                    };
915                    let key = winit_mappings::map_winit_key(&event.logical_key);
916                    let code = winit_mappings::map_winit_physical_key(&event.physical_key);
917                    let modifiers = winit_mappings::map_winit_modifiers(app.modifiers_state);
918
919                    self.plugins.send(
920                        PluginEvent::KeyboardInput {
921                            window: &app.window,
922                            key: key.clone(),
923                            code,
924                            modifiers,
925                            is_pressed: event.state.is_pressed(),
926                        },
927                        PluginHandle::new(&self.proxy),
928                    );
929
930                    let platform_event = PlatformEvent::Keyboard {
931                        name,
932                        key,
933                        code,
934                        modifiers,
935                    };
936                    let mut events_measurer_adapter = EventsMeasurerAdapter {
937                        tree: &mut app.tree,
938                        scale_factor: app.window.scale_factor(),
939                    };
940                    let processed_events = events_measurer_adapter.run(
941                        &mut vec![platform_event],
942                        &mut app.nodes_state,
943                        app.accessibility.focused_node_id(),
944                    );
945                    app.events_sender
946                        .unbounded_send(EventsChunk::Processed(processed_events))
947                        .unwrap();
948                }
949
950                WindowEvent::MouseWheel { delta, phase, .. } => {
951                    const WHEEL_SPEED_MODIFIER: f64 = 53.0;
952                    const TOUCHPAD_SPEED_MODIFIER: f64 = 2.0;
953
954                    if TouchPhase::Moved == phase {
955                        let scroll_data = {
956                            match delta {
957                                MouseScrollDelta::LineDelta(x, y) => (
958                                    (x as f64 * WHEEL_SPEED_MODIFIER),
959                                    (y as f64 * WHEEL_SPEED_MODIFIER),
960                                ),
961                                MouseScrollDelta::PixelDelta(pos) => (
962                                    (pos.x * TOUCHPAD_SPEED_MODIFIER),
963                                    (pos.y * TOUCHPAD_SPEED_MODIFIER),
964                                ),
965                            }
966                        };
967
968                        let platform_event = PlatformEvent::Wheel {
969                            name: WheelEventName::Wheel,
970                            scroll: scroll_data.into(),
971                            cursor: app.position,
972                            source: WheelSource::Device,
973                        };
974                        let mut events_measurer_adapter = EventsMeasurerAdapter {
975                            tree: &mut app.tree,
976                            scale_factor: app.window.scale_factor(),
977                        };
978                        let processed_events = events_measurer_adapter.run(
979                            &mut vec![platform_event],
980                            &mut app.nodes_state,
981                            app.accessibility.focused_node_id(),
982                        );
983                        app.events_sender
984                            .unbounded_send(EventsChunk::Processed(processed_events))
985                            .unwrap();
986                    }
987                }
988
989                WindowEvent::CursorLeft { .. } => {
990                    if app.mouse_state == ElementState::Released {
991                        app.position = CursorPoint::from((-1., -1.));
992                        let platform_event = PlatformEvent::Mouse {
993                            name: MouseEventName::MouseMove,
994                            cursor: app.position,
995                            button: None,
996                        };
997                        let mut events_measurer_adapter = EventsMeasurerAdapter {
998                            tree: &mut app.tree,
999                            scale_factor: app.window.scale_factor(),
1000                        };
1001                        let processed_events = events_measurer_adapter.run(
1002                            &mut vec![platform_event],
1003                            &mut app.nodes_state,
1004                            app.accessibility.focused_node_id(),
1005                        );
1006                        app.events_sender
1007                            .unbounded_send(EventsChunk::Processed(processed_events))
1008                            .unwrap();
1009                    }
1010                }
1011                WindowEvent::CursorMoved { position, .. } => {
1012                    app.just_focused = false;
1013                    app.position = CursorPoint::from((position.x, position.y));
1014
1015                    let mut platform_event = vec![PlatformEvent::Mouse {
1016                        name: MouseEventName::MouseMove,
1017                        cursor: app.position,
1018                        button: None,
1019                    }];
1020
1021                    for dropped_file_path in app.dropped_file_paths.drain(..) {
1022                        platform_event.push(PlatformEvent::File {
1023                            name: FileEventName::FileDrop,
1024                            file_path: Some(dropped_file_path),
1025                            cursor: app.position,
1026                        });
1027                    }
1028
1029                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1030                        tree: &mut app.tree,
1031                        scale_factor: app.window.scale_factor(),
1032                    };
1033                    let processed_events = events_measurer_adapter.run(
1034                        &mut platform_event,
1035                        &mut app.nodes_state,
1036                        app.accessibility.focused_node_id(),
1037                    );
1038                    app.events_sender
1039                        .unbounded_send(EventsChunk::Processed(processed_events))
1040                        .unwrap();
1041                }
1042
1043                WindowEvent::Touch(Touch {
1044                    location,
1045                    phase,
1046                    id,
1047                    force,
1048                    ..
1049                }) => {
1050                    app.position = CursorPoint::from((location.x, location.y));
1051
1052                    let name = match phase {
1053                        TouchPhase::Cancelled => TouchEventName::TouchCancel,
1054                        TouchPhase::Ended => TouchEventName::TouchEnd,
1055                        TouchPhase::Moved => TouchEventName::TouchMove,
1056                        TouchPhase::Started => TouchEventName::TouchStart,
1057                    };
1058
1059                    let platform_event = PlatformEvent::Touch {
1060                        name,
1061                        location: app.position,
1062                        finger_id: id,
1063                        phase: map_winit_touch_phase(phase),
1064                        force: force.map(map_winit_touch_force),
1065                    };
1066                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1067                        tree: &mut app.tree,
1068                        scale_factor: app.window.scale_factor(),
1069                    };
1070                    let processed_events = events_measurer_adapter.run(
1071                        &mut vec![platform_event],
1072                        &mut app.nodes_state,
1073                        app.accessibility.focused_node_id(),
1074                    );
1075                    app.events_sender
1076                        .unbounded_send(EventsChunk::Processed(processed_events))
1077                        .unwrap();
1078                    app.position = CursorPoint::from((location.x, location.y));
1079                }
1080                WindowEvent::Ime(Ime::Commit(text)) => {
1081                    let platform_event = PlatformEvent::Keyboard {
1082                        name: KeyboardEventName::KeyDown,
1083                        key: keyboard_types::Key::Character(text),
1084                        code: keyboard_types::Code::Unidentified,
1085                        modifiers: winit_mappings::map_winit_modifiers(app.modifiers_state),
1086                    };
1087                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1088                        tree: &mut app.tree,
1089                        scale_factor: app.window.scale_factor(),
1090                    };
1091                    let processed_events = events_measurer_adapter.run(
1092                        &mut vec![platform_event],
1093                        &mut app.nodes_state,
1094                        app.accessibility.focused_node_id(),
1095                    );
1096                    app.events_sender
1097                        .unbounded_send(EventsChunk::Processed(processed_events))
1098                        .unwrap();
1099                }
1100                WindowEvent::Ime(Ime::Preedit(text, pos)) => {
1101                    let platform_event = PlatformEvent::ImePreedit {
1102                        name: ImeEventName::Preedit,
1103                        text,
1104                        cursor: pos,
1105                    };
1106                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1107                        tree: &mut app.tree,
1108                        scale_factor: app.window.scale_factor(),
1109                    };
1110                    let processed_events = events_measurer_adapter.run(
1111                        &mut vec![platform_event],
1112                        &mut app.nodes_state,
1113                        app.accessibility.focused_node_id(),
1114                    );
1115                    app.events_sender
1116                        .unbounded_send(EventsChunk::Processed(processed_events))
1117                        .unwrap();
1118                }
1119                WindowEvent::DroppedFile(file_path) => {
1120                    app.dropped_file_paths.push(file_path);
1121                }
1122                WindowEvent::HoveredFile(file_path) => {
1123                    let platform_event = PlatformEvent::File {
1124                        name: FileEventName::FileHover,
1125                        file_path: Some(file_path),
1126                        cursor: app.position,
1127                    };
1128                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1129                        tree: &mut app.tree,
1130                        scale_factor: app.window.scale_factor(),
1131                    };
1132                    let processed_events = events_measurer_adapter.run(
1133                        &mut vec![platform_event],
1134                        &mut app.nodes_state,
1135                        app.accessibility.focused_node_id(),
1136                    );
1137                    app.events_sender
1138                        .unbounded_send(EventsChunk::Processed(processed_events))
1139                        .unwrap();
1140                }
1141                WindowEvent::HoveredFileCancelled => {
1142                    let platform_event = PlatformEvent::File {
1143                        name: FileEventName::FileHoverCancelled,
1144                        file_path: None,
1145                        cursor: app.position,
1146                    };
1147                    let mut events_measurer_adapter = EventsMeasurerAdapter {
1148                        tree: &mut app.tree,
1149                        scale_factor: app.window.scale_factor(),
1150                    };
1151                    let processed_events = events_measurer_adapter.run(
1152                        &mut vec![platform_event],
1153                        &mut app.nodes_state,
1154                        app.accessibility.focused_node_id(),
1155                    );
1156                    app.events_sender
1157                        .unbounded_send(EventsChunk::Processed(processed_events))
1158                        .unwrap();
1159                }
1160                _ => {}
1161            }
1162        }
1163    }
1164}