Skip to main content

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