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