tao/event_loop.rs
1// Copyright 2014-2021 The winit contributors
2// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
3// SPDX-License-Identifier: Apache-2.0
4
5//! The `EventLoop` struct and assorted supporting types, including `ControlFlow`.
6//!
7//! If you want to send custom events to the event loop, use [`EventLoop::create_proxy()`][create_proxy]
8//! to acquire an [`EventLoopProxy`][event_loop_proxy] and call its [`send_event`][send_event] method.
9//!
10//! See the root-level documentation for information on how to create and use an event loop to
11//! handle events.
12//!
13//! [create_proxy]: crate::event_loop::EventLoop::create_proxy
14//! [event_loop_proxy]: crate::event_loop::EventLoopProxy
15//! [send_event]: crate::event_loop::EventLoopProxy::send_event
16use std::time::Instant;
17use std::{error, fmt, marker::PhantomData, ops::Deref};
18
19use crate::{
20 dpi::PhysicalPosition,
21 error::ExternalError,
22 event::Event,
23 monitor::MonitorHandle,
24 platform_impl,
25 window::{ProgressBarState, Theme},
26};
27
28/// Provides a way to retrieve events from the system and from the windows that were registered to
29/// the events loop.
30///
31/// An `EventLoop` can be seen more or less as a "context". Calling `EventLoop::new()`
32/// initializes everything that will be required to create windows.
33///
34/// To wake up an `EventLoop` from a another thread, see the `EventLoopProxy` docs.
35///
36/// Note that the `EventLoop` cannot be shared across threads (due to platform-dependant logic
37/// forbidding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the
38/// `Window` created from this `EventLoop` _can_ be sent to an other thread, and the
39/// `EventLoopProxy` allows you to wake up an `EventLoop` from another thread.
40///
41pub struct EventLoop<T: 'static> {
42 pub(crate) event_loop: platform_impl::EventLoop<T>,
43 pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync
44}
45
46/// Target that associates windows with an `EventLoop`.
47///
48/// This type exists to allow you to create new windows while Tao executes
49/// your callback. `EventLoop` will coerce into this type (`impl<T> Deref for
50/// EventLoop<T>`), so functions that take this as a parameter can also take
51/// `&EventLoop`.
52#[derive(Clone)]
53pub struct EventLoopWindowTarget<T: 'static> {
54 pub(crate) p: platform_impl::EventLoopWindowTarget<T>,
55 pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync
56}
57
58impl<T> fmt::Debug for EventLoop<T> {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 f.pad("EventLoop { .. }")
61 }
62}
63
64impl<T> fmt::Debug for EventLoopWindowTarget<T> {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.pad("EventLoopWindowTarget { .. }")
67 }
68}
69
70/// Object that allows building the event loop.
71///
72/// This is used to make specifying options that affect the whole application
73/// easier. But note that constructing multiple event loops is not supported.
74#[derive(Default)]
75pub struct EventLoopBuilder<T: 'static> {
76 pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
77 _p: PhantomData<T>,
78}
79impl EventLoopBuilder<()> {
80 /// Start building a new event loop.
81 #[inline]
82 pub fn new() -> Self {
83 Self::with_user_event()
84 }
85}
86impl<T> EventLoopBuilder<T> {
87 /// Start building a new event loop, with the given type as the user event
88 /// type.
89 #[inline]
90 pub fn with_user_event() -> Self {
91 Self {
92 platform_specific: Default::default(),
93 _p: PhantomData,
94 }
95 }
96 /// Builds a new event loop.
97 ///
98 /// ***For cross-platform compatibility, the `EventLoop` must be created on the main thread.***
99 /// Attempting to create the event loop on a different thread will panic. This restriction isn't
100 /// strictly necessary on all platforms, but is imposed to eliminate any nasty surprises when
101 /// porting to platforms that require it. `EventLoopBuilderExt::any_thread` functions are exposed
102 /// in the relevant `platform` module if the target platform supports creating an event loop on
103 /// any thread.
104 ///
105 /// Usage will result in display backend initialisation, this can be controlled on linux
106 /// using an environment variable `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`.
107 /// If it is not set, winit will try to connect to a wayland connection, and if it fails will
108 /// fallback on x11. If this variable is set with any other value, winit will panic.
109 ///
110 /// ## Platform-specific
111 ///
112 /// - **iOS:** Can only be called on the main thread.
113 #[inline]
114 pub fn build(&mut self) -> EventLoop<T> {
115 EventLoop {
116 event_loop: platform_impl::EventLoop::new(&mut self.platform_specific),
117 _marker: PhantomData,
118 }
119 }
120}
121
122/// Set by the user callback given to the `EventLoop::run` method.
123///
124/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`][events_cleared]
125/// is emitted. Defaults to `Poll`.
126///
127/// ## Persistency
128/// Almost every change is persistent between multiple calls to the event loop closure within a
129/// given run loop. The only exception to this is `ExitWithCode` which, once set, cannot be unset.
130/// Changes are **not** persistent between multiple calls to `run_return` - issuing a new call will
131/// reset the control flow to `Poll`.
132///
133/// [events_cleared]: crate::event::Event::RedrawEventsCleared
134#[non_exhaustive]
135#[derive(Copy, Clone, Debug, PartialEq, Eq)]
136pub enum ControlFlow {
137 /// When the current loop iteration finishes, immediately begin a new iteration regardless of
138 /// whether or not new events are available to process.
139 Poll,
140 /// When the current loop iteration finishes, suspend the thread until another event arrives.
141 Wait,
142 /// When the current loop iteration finishes, suspend the thread until either another event
143 /// arrives or the given time is reached.
144 WaitUntil(Instant),
145 /// Send a `LoopDestroyed` event and stop the event loop. This variant is *sticky* - once set,
146 /// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will
147 /// result in the `control_flow` parameter being reset to `ExitWithCode`.
148 ///
149 /// The contained number will be used as exit code. The [`Exit`] constant is a shortcut for this
150 /// with exit code 0.
151 ///
152 /// ## Platform-specific
153 ///
154 /// - **Android / iOS / WASM**: The supplied exit code is unused.
155 /// - **Unix**: On most Unix-like platforms, only the 8 least significant bits will be used,
156 /// which can cause surprises with negative exit values (`-42` would end up as `214`). See
157 /// [`std::process::exit`].
158 ///
159 /// [`Exit`]: ControlFlow::Exit
160 ExitWithCode(i32),
161}
162
163impl ControlFlow {
164 /// Alias for [`ExitWithCode`]`(0)`.
165 ///
166 /// [`ExitWithCode`]: ControlFlow::ExitWithCode
167 #[allow(non_upper_case_globals)]
168 pub const Exit: Self = Self::ExitWithCode(0);
169}
170
171impl Default for ControlFlow {
172 #[inline(always)]
173 fn default() -> ControlFlow {
174 ControlFlow::Poll
175 }
176}
177
178impl EventLoop<()> {
179 /// Alias for [`EventLoopBuilder::new().build()`].
180 ///
181 /// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build
182 #[inline]
183 pub fn new() -> EventLoop<()> {
184 EventLoopBuilder::new().build()
185 }
186}
187
188impl Default for EventLoop<()> {
189 fn default() -> Self {
190 Self::new()
191 }
192}
193
194impl<T> EventLoop<T> {
195 /// Hijacks the calling thread and initializes the tao event loop with the provided
196 /// closure. Since the closure is `'static`, it must be a `move` closure if it needs to
197 /// access any data from the calling context.
198 ///
199 /// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
200 /// event loop's behavior.
201 ///
202 /// Any values not passed to this function will *not* be dropped.
203 ///
204 /// ## Platform-specific
205 ///
206 /// - **Unix**: The program terminates with exit code 1 if the display server
207 /// disconnects.
208 ///
209 /// [`ControlFlow`]: crate::event_loop::ControlFlow
210 #[inline]
211 pub fn run<F>(self, event_handler: F) -> !
212 where
213 F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
214 {
215 self.event_loop.run(event_handler)
216 }
217
218 /// Creates an `EventLoopProxy` that can be used to dispatch user events to the main event loop.
219 pub fn create_proxy(&self) -> EventLoopProxy<T> {
220 EventLoopProxy {
221 event_loop_proxy: self.event_loop.create_proxy(),
222 }
223 }
224}
225
226impl<T> Deref for EventLoop<T> {
227 type Target = EventLoopWindowTarget<T>;
228 fn deref(&self) -> &EventLoopWindowTarget<T> {
229 self.event_loop.window_target()
230 }
231}
232
233impl<T> EventLoopWindowTarget<T> {
234 /// Returns the list of all the monitors available on the system.
235 #[inline]
236 pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
237 self
238 .p
239 .available_monitors()
240 .into_iter()
241 .map(|inner| MonitorHandle { inner })
242 }
243
244 /// Returns the primary monitor of the system.
245 ///
246 /// Returns `None` if it can't identify any monitor as a primary one.
247 #[inline]
248 pub fn primary_monitor(&self) -> Option<MonitorHandle> {
249 self.p.primary_monitor()
250 }
251
252 /// Returns the monitor that contains the given point.
253 ///
254 /// ## Platform-specific:
255 ///
256 /// - **Android / iOS:** Unsupported.
257 #[inline]
258 pub fn monitor_from_point(&self, x: f64, y: f64) -> Option<MonitorHandle> {
259 self
260 .p
261 .monitor_from_point(x, y)
262 .map(|inner| MonitorHandle { inner })
263 }
264
265 /// Change [`DeviceEvent`] filter mode.
266 ///
267 /// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, tao
268 /// will ignore them by default for unfocused windows. This method allows changing
269 /// this filter at runtime to explicitly capture them again.
270 ///
271 /// ## Platform-specific
272 ///
273 /// - **Linux / macOS / iOS / Android:** Unsupported.
274 ///
275 /// [`DeviceEvent`]: crate::event::DeviceEvent
276 pub fn set_device_event_filter(&self, _filter: DeviceEventFilter) {
277 #[cfg(target_os = "windows")]
278 self.p.set_device_event_filter(_filter);
279 }
280
281 /// Returns the current cursor position
282 ///
283 /// ## Platform-specific
284 ///
285 /// - **iOS / Android / Linux(Wayland)**: Unsupported, returns `0,0`.
286 #[inline]
287 pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
288 self.p.cursor_position()
289 }
290
291 /// Sets the progress bar state
292 ///
293 /// ## Platform-specific
294 ///
295 /// - **Windows:** Unsupported. Use the Progress Bar Function Available in Window (Windows can have different progress bars for different window)
296 /// - **Linux:** Only supported desktop environments with `libunity` (e.g. GNOME).
297 /// - **iOS / Android:** Unsupported.
298 #[inline]
299 pub fn set_progress_bar(&self, _progress: ProgressBarState) {
300 #[cfg(any(target_os = "linux", target_os = "macos"))]
301 self.p.set_progress_bar(_progress)
302 }
303
304 /// Sets the theme for the application.
305 ///
306 /// ## Platform-specific
307 ///
308 /// - **iOS / Android:** Unsupported.
309 #[inline]
310 pub fn set_theme(&self, _theme: Option<Theme>) {
311 #[cfg(any(
312 windows,
313 target_os = "linux",
314 target_os = "dragonfly",
315 target_os = "freebsd",
316 target_os = "netbsd",
317 target_os = "openbsd",
318 target_os = "macos",
319 ))]
320 self.p.set_theme(_theme)
321 }
322}
323
324#[cfg(feature = "rwh_05")]
325unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
326 fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
327 rwh_05::HasRawDisplayHandle::raw_display_handle(&**self)
328 }
329}
330
331#[cfg(feature = "rwh_06")]
332impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
333 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
334 rwh_06::HasDisplayHandle::display_handle(&**self)
335 }
336}
337
338#[cfg(feature = "rwh_05")]
339unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoopWindowTarget<T> {
340 fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
341 self.p.raw_display_handle_rwh_05()
342 }
343}
344
345#[cfg(feature = "rwh_06")]
346impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> {
347 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
348 let raw = self.p.raw_display_handle_rwh_06()?;
349 // SAFETY: The display will never be deallocated while the event loop is alive.
350 Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) })
351 }
352}
353
354/// Used to send custom events to `EventLoop`.
355pub struct EventLoopProxy<T: 'static> {
356 event_loop_proxy: platform_impl::EventLoopProxy<T>,
357}
358
359impl<T: 'static> Clone for EventLoopProxy<T> {
360 fn clone(&self) -> Self {
361 Self {
362 event_loop_proxy: self.event_loop_proxy.clone(),
363 }
364 }
365}
366
367impl<T: 'static> EventLoopProxy<T> {
368 /// Send an event to the `EventLoop` from which this proxy was created. This emits a
369 /// `UserEvent(event)` event in the event loop, where `event` is the value passed to this
370 /// function.
371 ///
372 /// Returns an `Err` if the associated `EventLoop` no longer exists.
373 pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
374 self.event_loop_proxy.send_event(event)
375 }
376}
377
378impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 f.pad("EventLoopProxy { .. }")
381 }
382}
383
384/// The error that is returned when an `EventLoopProxy` attempts to wake up an `EventLoop` that
385/// no longer exists. Contains the original event given to `send_event`.
386#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
387pub struct EventLoopClosed<T>(pub T);
388
389impl<T> fmt::Display for EventLoopClosed<T> {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 f.write_str("Tried to wake up a closed `EventLoop`")
392 }
393}
394
395impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {}
396
397/// Fiter controlling the propagation of device events.
398#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
399pub enum DeviceEventFilter {
400 /// Always filter out device events.
401 Always,
402 /// Filter out device events while the window is not focused.
403 Unfocused,
404 /// Report all device events regardless of window focus.
405 Never,
406}
407
408impl Default for DeviceEventFilter {
409 fn default() -> Self {
410 Self::Unfocused
411 }
412}