servoshell/desktop/
events_loop.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! An event loop implementation that works in headless mode.
6
7use std::sync::{Arc, Condvar, Mutex};
8use std::time;
9
10use log::warn;
11use servo::EventLoopWaker;
12use winit::error::EventLoopError;
13use winit::event_loop::EventLoop as WinitEventLoop;
14
15use super::app::App;
16
17pub type EventLoopProxy = winit::event_loop::EventLoopProxy<AppEvent>;
18
19#[derive(Debug)]
20pub enum AppEvent {
21    /// Another process or thread has kicked the OS event loop with EventLoopWaker.
22    Waker,
23    Accessibility(egui_winit::accesskit_winit::Event),
24}
25
26impl From<egui_winit::accesskit_winit::Event> for AppEvent {
27    fn from(event: egui_winit::accesskit_winit::Event) -> AppEvent {
28        AppEvent::Accessibility(event)
29    }
30}
31
32/// The real or fake OS event loop.
33#[allow(dead_code)]
34#[allow(clippy::large_enum_variant)]
35enum EventLoop {
36    /// A real Winit windowing event loop.
37    Winit(winit::event_loop::EventLoop<AppEvent>),
38    /// A fake event loop which contains a signalling flag used to ensure
39    /// that pending events get processed in a timely fashion, and a condition
40    /// variable to allow waiting on that flag changing state.
41    Headless(Arc<(Mutex<bool>, Condvar)>),
42}
43
44pub struct EventsLoop(EventLoop);
45
46impl EventsLoop {
47    // Ideally, we could use the winit event loop in both modes,
48    // but on Linux, the event loop requires a X11 server.
49    #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
50    pub fn new(_headless: bool) -> Result<EventsLoop, EventLoopError> {
51        Ok(EventsLoop(EventLoop::Winit(
52            WinitEventLoop::with_user_event().build()?,
53        )))
54    }
55    #[cfg(any(target_os = "linux", target_os = "windows"))]
56    pub fn new(headless: bool) -> Result<EventsLoop, EventLoopError> {
57        Ok(EventsLoop(if headless {
58            EventLoop::Headless(Arc::new((Mutex::new(false), Condvar::new())))
59        } else {
60            EventLoop::Winit(WinitEventLoop::with_user_event().build()?)
61        }))
62    }
63    #[cfg(target_os = "macos")]
64    pub fn new(headless: bool) -> Result<EventsLoop, EventLoopError> {
65        Ok(EventsLoop(if headless {
66            EventLoop::Headless(Arc::new((Mutex::new(false), Condvar::new())))
67        } else {
68            EventLoop::Winit(WinitEventLoop::with_user_event().build()?)
69        }))
70    }
71}
72
73impl EventsLoop {
74    pub(crate) fn event_loop_proxy(&self) -> Option<EventLoopProxy> {
75        match self.0 {
76            EventLoop::Winit(ref events_loop) => Some(events_loop.create_proxy()),
77            EventLoop::Headless(..) => None,
78        }
79    }
80
81    pub fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
82        match self.0 {
83            EventLoop::Winit(ref events_loop) => Box::new(HeadedEventLoopWaker::new(events_loop)),
84            EventLoop::Headless(ref data) => Box::new(HeadlessEventLoopWaker(data.clone())),
85        }
86    }
87
88    pub fn run_app(self, app: &mut App) {
89        match self.0 {
90            EventLoop::Winit(events_loop) => {
91                events_loop
92                    .run_app(app)
93                    .expect("Failed while running events loop");
94            },
95            EventLoop::Headless(ref data) => {
96                let (flag, condvar) = &**data;
97
98                app.init(None);
99                loop {
100                    self.sleep(flag, condvar);
101                    app.handle_webdriver_messages();
102                    if !app.handle_events_with_headless() {
103                        break;
104                    }
105                    if !app.animating() {
106                        *flag.lock().unwrap() = false;
107                    }
108                }
109            },
110        }
111    }
112
113    fn sleep(&self, lock: &Mutex<bool>, condvar: &Condvar) {
114        // To avoid sleeping when we should be processing events, do two things:
115        // * before sleeping, check whether our signalling flag has been set
116        // * wait on a condition variable with a maximum timeout, to allow
117        //   being woken up by any signals that occur while sleeping.
118        let guard = lock.lock().unwrap();
119        if *guard {
120            return;
121        }
122        let _ = condvar
123            .wait_timeout(guard, time::Duration::from_millis(5))
124            .unwrap();
125    }
126}
127
128struct HeadedEventLoopWaker {
129    proxy: Arc<Mutex<winit::event_loop::EventLoopProxy<AppEvent>>>,
130}
131impl HeadedEventLoopWaker {
132    fn new(events_loop: &winit::event_loop::EventLoop<AppEvent>) -> HeadedEventLoopWaker {
133        let proxy = Arc::new(Mutex::new(events_loop.create_proxy()));
134        HeadedEventLoopWaker { proxy }
135    }
136}
137impl EventLoopWaker for HeadedEventLoopWaker {
138    fn wake(&self) {
139        // Kick the OS event loop awake.
140        if let Err(err) = self.proxy.lock().unwrap().send_event(AppEvent::Waker) {
141            warn!("Failed to wake up event loop ({}).", err);
142        }
143    }
144    fn clone_box(&self) -> Box<dyn EventLoopWaker> {
145        Box::new(HeadedEventLoopWaker {
146            proxy: self.proxy.clone(),
147        })
148    }
149}
150
151struct HeadlessEventLoopWaker(Arc<(Mutex<bool>, Condvar)>);
152impl EventLoopWaker for HeadlessEventLoopWaker {
153    fn wake(&self) {
154        // Set the signalling flag and notify the condition variable.
155        // This ensures that any sleep operation is interrupted,
156        // and any non-sleeping operation will have a change to check
157        // the flag before going to sleep.
158        let (ref flag, ref condvar) = *self.0;
159        let mut flag = flag.lock().unwrap();
160        *flag = true;
161        condvar.notify_all();
162    }
163    fn clone_box(&self) -> Box<dyn EventLoopWaker> {
164        Box::new(HeadlessEventLoopWaker(self.0.clone()))
165    }
166}