Skip to main content

servoshell/desktop/
event_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 gilrs::Event;
11use log::warn;
12use servo::EventLoopWaker;
13#[cfg(feature = "gamepad")]
14use servo::GamepadIndex;
15use winit::event_loop::{EventLoop, EventLoop as WinitEventLoop, EventLoopProxy};
16
17use super::app::App;
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    #[cfg(feature = "gamepad")]
25    Gamepad(Event, String, GamepadIndex),
26}
27
28impl From<egui_winit::accesskit_winit::Event> for AppEvent {
29    fn from(event: egui_winit::accesskit_winit::Event) -> AppEvent {
30        AppEvent::Accessibility(event)
31    }
32}
33
34/// A headed or headless event loop. Headless event loops are necessary for environments without a
35/// display server. Ideally, we could use the headed winit event loop in both modes, but on Linux,
36/// the event loop requires a display server, which prevents running servoshell in a console.
37#[allow(clippy::large_enum_variant)]
38pub(crate) enum ServoShellEventLoop {
39    /// A real Winit windowing event loop.
40    Winit(EventLoop<AppEvent>),
41    /// A fake event loop which contains a signalling flag used to ensure
42    /// that pending events get processed in a timely fashion, and a condition
43    /// variable to allow waiting on that flag changing state.
44    Headless(Arc<HeadlessEventLoop>),
45}
46
47impl ServoShellEventLoop {
48    pub(crate) fn headless() -> ServoShellEventLoop {
49        ServoShellEventLoop::Headless(Default::default())
50    }
51
52    pub(crate) fn headed() -> ServoShellEventLoop {
53        ServoShellEventLoop::Winit(
54            WinitEventLoop::with_user_event()
55                .build()
56                .expect("Could not start winit event loop"),
57        )
58    }
59}
60
61impl ServoShellEventLoop {
62    pub(crate) fn event_loop_proxy(&self) -> Option<EventLoopProxy<AppEvent>> {
63        match self {
64            ServoShellEventLoop::Winit(event_loop) => Some(event_loop.create_proxy()),
65            ServoShellEventLoop::Headless(..) => None,
66        }
67    }
68
69    pub fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
70        match self {
71            ServoShellEventLoop::Winit(event_loop) => {
72                Box::new(HeadedEventLoopWaker::new(event_loop))
73            },
74            ServoShellEventLoop::Headless(data) => Box::new(HeadlessEventLoopWaker(data.clone())),
75        }
76    }
77
78    pub fn run_app(self, app: &mut App) {
79        match self {
80            ServoShellEventLoop::Winit(event_loop) => {
81                event_loop
82                    .run_app(app)
83                    .expect("Failed while running events loop");
84            },
85            ServoShellEventLoop::Headless(event_loop) => event_loop.run_app(app),
86        }
87    }
88}
89
90#[derive(Clone)]
91struct HeadedEventLoopWaker {
92    proxy: Arc<Mutex<EventLoopProxy<AppEvent>>>,
93}
94
95impl HeadedEventLoopWaker {
96    fn new(event_loop: &EventLoop<AppEvent>) -> HeadedEventLoopWaker {
97        let proxy = Arc::new(Mutex::new(event_loop.create_proxy()));
98        HeadedEventLoopWaker { proxy }
99    }
100}
101
102impl EventLoopWaker for HeadedEventLoopWaker {
103    fn wake(&self) {
104        // Kick the OS event loop awake.
105        if let Err(err) = self.proxy.lock().unwrap().send_event(AppEvent::Waker) {
106            warn!("Failed to wake up event loop ({}).", err);
107        }
108    }
109
110    fn clone_box(&self) -> Box<dyn EventLoopWaker> {
111        Box::new(self.clone())
112    }
113}
114
115/// The [`HeadlessEventLoop`] is used when running in headless mode. The event
116/// loop just loops over a condvar to simulate a real windowing system event loop.
117#[derive(Default)]
118pub(crate) struct HeadlessEventLoop {
119    guard: Arc<Mutex<bool>>,
120    condvar: Condvar,
121}
122
123impl HeadlessEventLoop {
124    fn run_app(&self, app: &mut App) {
125        app.init(None);
126
127        loop {
128            self.sleep();
129            if !app.pump_servo_event_loop(None) {
130                break;
131            }
132            *self.guard.lock().unwrap() = false;
133        }
134    }
135
136    fn sleep(&self) {
137        // To avoid sleeping when we should be processing events, do two things:
138        // * before sleeping, check whether our signalling flag has been set
139        // * wait on a condition variable with a maximum timeout, to allow
140        //   being woken up by any signals that occur while sleeping.
141        let guard = self.guard.lock().unwrap();
142        if *guard {
143            return;
144        }
145        let _ = self
146            .condvar
147            .wait_timeout(guard, time::Duration::from_millis(5))
148            .unwrap();
149    }
150}
151
152#[derive(Clone)]
153struct HeadlessEventLoopWaker(Arc<HeadlessEventLoop>);
154
155impl EventLoopWaker for HeadlessEventLoopWaker {
156    fn wake(&self) {
157        // Set the signalling flag and notify the condition variable.
158        // This ensures that any sleep operation is interrupted,
159        // and any non-sleeping operation will have a change to check
160        // the flag before going to sleep.
161        let mut flag = self.0.guard.lock().unwrap();
162        *flag = true;
163        self.0.condvar.notify_all();
164    }
165
166    fn clone_box(&self) -> Box<dyn EventLoopWaker> {
167        Box::new(self.clone())
168    }
169}