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