Skip to main content

winit_core/event_loop/
mod.rs

1pub mod never_return;
2pub mod pump_events;
3pub mod register;
4pub mod run_on_demand;
5
6use std::any::Any;
7use std::fmt::{self, Debug};
8use std::sync::Arc;
9use std::sync::atomic::{AtomicUsize, Ordering};
10use std::time::Duration;
11
12use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle};
13
14use crate::Instant;
15use crate::application::ApplicationHandler;
16use crate::cursor::{CustomCursor, CustomCursorSource};
17use crate::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType};
18use crate::error::{EventLoopError, NotSupportedError, RequestError};
19use crate::icon::Icon;
20use crate::monitor::MonitorHandle;
21use crate::window::{Theme, Window, WindowAttributes, WindowId};
22
23/// Common methods to implement for the platform event loop.
24pub trait EventLoopProvider: fmt::Debug {
25    /// Run the event loop with the given application on the calling thread.
26    ///
27    /// The `app` is dropped when the event loop is shut down.
28    ///
29    /// ## Event loop flow
30    ///
31    /// This function internally handles the different parts of a traditional event-handling loop.
32    /// You can imagine this method as being implemented like this:
33    ///
34    /// ```rust,ignore
35    /// let mut start_cause = StartCause::Init;
36    ///
37    /// // Run the event loop.
38    /// while !event_loop.exiting() {
39    ///     // Wake up.
40    ///     app.new_events(event_loop, start_cause);
41    ///
42    ///     // Indicate that surfaces can now safely be created.
43    ///     if start_cause == StartCause::Init {
44    ///         app.can_create_surfaces(event_loop);
45    ///     }
46    ///
47    ///     // Handle proxy wake-up event.
48    ///     if event_loop.proxy_wake_up_set() {
49    ///         event_loop.proxy_wake_up_clear();
50    ///         app.proxy_wake_up(event_loop);
51    ///     }
52    ///
53    ///     // Handle actions done by the user / system such as moving the cursor, resizing the
54    ///     // window, changing the window theme, etc.
55    ///     for event in event_loop.events() {
56    ///         match event {
57    ///             window event => app.window_event(event_loop, window_id, event),
58    ///             device event => app.device_event(event_loop, device_id, event),
59    ///         }
60    ///     }
61    ///
62    ///     // Handle redraws.
63    ///     for window_id in event_loop.pending_redraws() {
64    ///         app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
65    ///     }
66    ///
67    ///     // Done handling events, wait until we're woken up again.
68    ///     app.about_to_wait(event_loop);
69    ///     start_cause = event_loop.wait_if_necessary();
70    /// }
71    ///
72    /// // Finished running, drop application state.
73    /// drop(app);
74    /// ```
75    ///
76    /// This is of course a very coarse-grained overview, and leaves out timing details like
77    /// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`], but
78    /// it should give you an idea of how things fit together.
79    ///
80    /// ## Returns
81    ///
82    /// The semantics of this function is defined by the target platform. Consult the implementor
83    /// docs for details.
84    fn run_app<A: ApplicationHandler + 'static>(self, app: A) -> Result<(), EventLoopError>;
85
86    /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
87    /// to the main event loop, possibly from another thread.
88    fn create_proxy(&self) -> EventLoopProxy;
89
90    /// Gets a persistent reference to the underlying platform display.
91    ///
92    /// See the [`OwnedDisplayHandle`] type for more information.
93    fn owned_display_handle(&self) -> OwnedDisplayHandle;
94
95    /// Change if or when [`DeviceEvent`]s are captured.
96    ///
97    /// See [`ActiveEventLoop::listen_device_events`] for details.
98    ///
99    /// [`DeviceEvent`]: crate::event::DeviceEvent
100    fn listen_device_events(&self, allowed: DeviceEvents);
101
102    /// Sets the [`ControlFlow`].
103    fn set_control_flow(&self, control_flow: ControlFlow);
104
105    /// Create custom cursor.
106    fn create_custom_cursor(
107        &self,
108        custom_cursor: CustomCursorSource,
109    ) -> Result<CustomCursor, RequestError>;
110}
111
112pub trait ActiveEventLoop: Any + fmt::Debug {
113    /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
114    /// to the main event loop, possibly from another thread.
115    fn create_proxy(&self) -> EventLoopProxy;
116
117    /// Create the window.
118    ///
119    /// Possible causes of error include denied permission, incompatible system, and lack of memory.
120    ///
121    /// ## Platform-specific
122    ///
123    /// - **Web:** The window is created but not inserted into the Web page automatically. Please
124    ///   see the Web platform module for more information.
125    fn create_window(
126        &self,
127        window_attributes: WindowAttributes,
128    ) -> Result<Box<dyn Window>, RequestError>;
129
130    /// Create custom cursor.
131    ///
132    /// ## Platform-specific
133    ///
134    /// **iOS / Android / Orbital:** Unsupported.
135    fn create_custom_cursor(
136        &self,
137        custom_cursor: CustomCursorSource,
138    ) -> Result<CustomCursor, RequestError>;
139
140    /// Returns the list of all the monitors available on the system.
141    ///
142    /// ## Platform-specific
143    ///
144    /// **Web:** Only returns the current monitor without `detailed monitor permissions`.
145    fn available_monitors(&self) -> Box<dyn Iterator<Item = MonitorHandle>>;
146
147    /// Returns the primary monitor of the system.
148    ///
149    /// Returns `None` if it can't identify any monitor as a primary one.
150    ///
151    /// ## Platform-specific
152    ///
153    /// - **Wayland:** Always returns `None`.
154    /// - **Web:** Always returns `None` without `detailed monitor permissions`.
155    fn primary_monitor(&self) -> Option<MonitorHandle>;
156
157    /// Change if or when [`DeviceEvent`]s are captured.
158    ///
159    /// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit
160    /// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing
161    /// this at runtime to explicitly capture them again.
162    ///
163    /// ## Platform-specific
164    ///
165    /// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
166    ///
167    /// [`DeviceEvent`]: crate::event::DeviceEvent
168    fn listen_device_events(&self, allowed: DeviceEvents);
169
170    /// Returns the current system theme.
171    ///
172    /// Returns `None` if it cannot be determined on the current platform.
173    ///
174    /// ## Platform-specific
175    ///
176    /// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
177    fn system_theme(&self) -> Option<Theme>;
178
179    /// Sets the [`ControlFlow`].
180    fn set_control_flow(&self, control_flow: ControlFlow);
181
182    /// Gets the current [`ControlFlow`].
183    fn control_flow(&self) -> ControlFlow;
184
185    /// Stop the event loop.
186    ///
187    /// ## Platform-specific
188    ///
189    /// ### iOS
190    ///
191    /// It is not possible to programmatically exit/quit an application on iOS, so this function is
192    /// a no-op there. See also [this technical Q&A][qa1561].
193    ///
194    /// [qa1561]: https://developer.apple.com/library/archive/qa/qa1561/_index.html
195    fn exit(&self);
196
197    /// Returns whether the [`ActiveEventLoop`] is about to stop.
198    ///
199    /// Set by [`exit()`][Self::exit].
200    fn exiting(&self) -> bool;
201
202    /// Gets a persistent reference to the underlying platform display.
203    ///
204    /// See the [`OwnedDisplayHandle`] type for more information.
205    fn owned_display_handle(&self) -> OwnedDisplayHandle;
206
207    /// Get the raw-window-handle handle.
208    fn rwh_06_handle(&self) -> &dyn HasDisplayHandle;
209
210    /// Request to fetch a type from a [data transfer](crate::data_transfer::DataTransfer).
211    ///
212    /// This may be called multiple times on the same [`DataTransferId`] with different types,
213    /// and may be called at any point during the drag operation, including during handling the
214    /// [`DragDropped`](crate::event::WindowEvent::DragDropped) event. After that event has been
215    /// received, though, the data transfer is not guaranteed to be available. The data is
216    /// _not_ guaranteed to be available during (or after) handling of
217    /// [`DragLeft](crate::event::WindowEvent::DragLeft).
218    ///
219    /// Once available, the data will be supplied to the application with the
220    /// [`DataTransferReceived`](crate::event::WindowEvent::DataTransferReceived) event.
221    fn fetch_data_transfer(
222        &self,
223        id: DataTransferId,
224        type_: &dyn TransferType,
225    ) -> Result<AsyncRequestSerial, RequestError> {
226        let _ = id;
227        let _ = type_;
228        Err(RequestError::NotSupported(NotSupportedError::new(
229            DATA_TRANSFER_UNSUPPORTED_ERROR_MESSAGE,
230        )))
231    }
232
233    /// Get a [data transfer](DataTransfer) by its ID.
234    ///
235    /// If the ID is invalid (e.g. if the lifetime of the data transfer has expired), this will
236    /// return an error.
237    fn data_transfer(&self, id: DataTransferId) -> Result<Box<dyn DataTransfer>, RequestError> {
238        let _ = id;
239        Err(RequestError::NotSupported(NotSupportedError::new(
240            DATA_TRANSFER_UNSUPPORTED_ERROR_MESSAGE,
241        )))
242    }
243
244    /// Set a given set of `DndAction`s as the valid actions for the given [`DataTransferId`],
245    /// if the transfer ID is from an incoming drag-and-drop operation.
246    ///
247    /// This allows the OS/compositor to display the correct UI, indicating that the dragged data
248    /// can be dropped. If the data transfer does not exist or is not from a drag-and-drop
249    /// operation, will return an error.
250    ///
251    /// The operating system will consider the drag either accepted or rejected based on the
252    /// set of valid actions supplied using this method, combined with the set of valid actions
253    /// on the drag source. If the drag is rejected at the point that the user finalizes the drop,
254    /// the application will receive [`DragLeft`](crate::event::WindowEvent::DragLeft) instead
255    /// of [`DragDropped`](crate::event::WindowEvent::DragDropped).
256    ///
257    /// Note that _rejecting_ the drag is not the same as _canceling_ the drag. A rejected drag can
258    /// be accepted later and the user can continue dragging it over other potential targets. On
259    /// most platforms, there is no way for an application to explicitly cancel a drag
260    /// operation.
261    ///
262    /// The set of actions is expected to be ordered by preference.
263    fn set_valid_dnd_actions(
264        &self,
265        id: DataTransferId,
266        actions: &[DndAction],
267    ) -> Result<(), RequestError> {
268        let _ = id;
269        let _ = actions;
270        Err(RequestError::NotSupported(NotSupportedError::new(
271            DATA_TRANSFER_UNSUPPORTED_ERROR_MESSAGE,
272        )))
273    }
274
275    /// Initiate a new drag-and-drop operation.
276    ///
277    /// See [`DataTransferSendBuilder`](crate::data_transfer::DataTransferSendBuilder) for how to
278    /// create a new cross-platform data transfer, or [`DataTransferSend`] for a generic trait
279    /// which can be implemented manually.
280    ///
281    /// The [`DataTransferId`] returned from this method, identifying the outgoing drag, is
282    /// currently only used for identifying the drag in the
283    /// [`OutgoingDragDropped`](crate::event::WindowEvent::OutgoingDragDropped) event. In most
284    /// cases, a drag will be started while the mouse is over the window which started it. This
285    /// means that, directly after this method is called, the window will then receive a
286    /// [`DragEntered`](crate::event::WindowEvent::DragEntered) event. However, the ID identifying
287    /// the incoming drag is not guaranteed to be the same as the ID returned from this method.
288    ///
289    /// For most cases, applications can treat all `DragEntered` events the same, whether they were
290    /// initiated by the same application or a different application. However, if the user wants to
291    /// have some kind of special handling for internal drag-and-drop, they will currently need
292    /// to implement it via workaround. On all systems where drag-and-drop is implemented in
293    /// Winit, the application can make the assumption that only a single drag operation can
294    /// occur at one time. Therefore, if `DragEntered` is received between calling this method
295    /// and receiving `OutgoingDragDropped`, then you can assume that it's the same drag.
296    /// In theory, Wayland allows multiple simultaneous drag operations at a time, but Winit does
297    /// not currently guarantee that this is supported correctly for either internal or external
298    /// drag.
299    ///
300    /// ### Arguments
301    ///
302    /// - `source` - The ID of the window that initiated the drag operation.
303    /// - `send_data` - The data provided by this drag operation. See
304    ///   [`DataTransferSendBuilder`](crate::data_transfer::DataTransferSendBuilder).
305    /// - `actions` - The set of valid actions for this drag operation. See [`DndAction`]. On
306    ///   Wayland, this is expected to be ordered by preference.
307    /// - `icon` - The icon to show while dragging.
308    ///
309    /// Some platforms have a more-expressive way of setting the visual component of a drag
310    /// operation. For those platforms, consider using the platform-specific implementation of
311    /// [`DataTransferSend`] for `send_data` and set this field to `None`.
312    ///
313    /// ### Returns
314    ///
315    /// A unique identifier for this drag operation, which will be later supplied by
316    /// [`OutgoingDragDropped`](crate::event::WindowEvent::OutgoingDragDropped).
317    fn start_drag(
318        &self,
319        source: WindowId,
320        send_data: Box<dyn DataTransferSend>,
321        actions: &[DndAction],
322        icon: Option<DragIcon>,
323    ) -> Result<DataTransferId, RequestError> {
324        let _ = source;
325        let _ = send_data;
326        let _ = actions;
327        let _ = icon;
328        Err(RequestError::NotSupported(NotSupportedError::new(
329            DATA_TRANSFER_UNSUPPORTED_ERROR_MESSAGE,
330        )))
331    }
332}
333
334const DATA_TRANSFER_UNSUPPORTED_ERROR_MESSAGE: &str = {
335    "Cross-application data transfer (e.g. drag-and-drop, clipboard) is unsupported on this \
336     platform"
337};
338
339impl HasDisplayHandle for dyn ActiveEventLoop + '_ {
340    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
341        self.rwh_06_handle().display_handle()
342    }
343}
344
345impl_dyn_casting!(ActiveEventLoop);
346
347/// Information needed to initiate a new drag operation.
348pub struct DragIcon {
349    /// The icon to apply to the cursor.
350    pub icon: Icon,
351    /// An x offset applied to the dragged icon.
352    ///
353    /// This is specified in image pixels. 0 means that the left side of the icon will be at
354    /// the cursor.
355    pub offset_x: i32,
356    /// A y offset applied to the dragged icon.
357    ///
358    /// This is specified in image pixels. 0 means that the top of the icon will be at the
359    /// cursor.
360    pub offset_y: i32,
361}
362
363impl From<Icon> for DragIcon {
364    fn from(value: Icon) -> Self {
365        Self { icon: value, offset_x: 0, offset_y: 0 }
366    }
367}
368
369/// The set of available actions for a drag operation.
370///
371/// This is _not_ a bitset, as on some platforms (e.g. Wayland, macOS) the source and/or destination
372/// are expected to provide some kind of order of preference.
373#[repr(u8)]
374#[derive(Debug, Clone, Copy, PartialEq)]
375#[non_exhaustive]
376pub enum DndAction {
377    /// Move the dragged item from the source to the destination.
378    ///
379    /// # Platforms
380    ///
381    /// - Wayland
382    /// - macOS
383    /// - Windows
384    Move,
385    /// Copy the dragged item from the source to the destination.
386    ///
387    /// # Platforms
388    ///
389    /// - X11
390    /// - Wayland
391    /// - macOS
392    /// - Windows
393    Copy,
394    /// A link is established between the source and the destination.
395    ///
396    /// # Platforms
397    ///
398    /// - macOS
399    /// - Windows
400    Link,
401    /// The user will be prompted for what should be done
402    ///
403    /// # Platforms
404    ///
405    /// - Wayland
406    Ask,
407    /// The source and destination will negotiate the drag operation privately
408    ///
409    /// # Platforms
410    ///
411    /// - macOS
412    Private,
413}
414
415/// Control the [`ActiveEventLoop`], possibly from a different thread, without referencing it
416/// directly.
417#[derive(Clone, Debug)]
418pub struct EventLoopProxy {
419    pub(crate) proxy: Arc<dyn EventLoopProxyProvider>,
420}
421
422impl EventLoopProxy {
423    /// Wake up the [`ActiveEventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being
424    /// called.
425    ///
426    /// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the
427    /// documentation on that for details.
428    ///
429    /// If the event loop is no longer running, this is a no-op.
430    ///
431    /// [`proxy_wake_up`]: crate::application::ApplicationHandler::proxy_wake_up
432    /// [`ApplicationHandler::proxy_wake_up()`]: crate::application::ApplicationHandler::proxy_wake_up
433    ///
434    /// # Platform-specific
435    ///
436    /// - **Windows**: The wake-up may be ignored under high contention, see [#3687].
437    ///
438    /// [#3687]: https://github.com/rust-windowing/winit/pull/3687
439    pub fn wake_up(&self) {
440        self.proxy.wake_up();
441    }
442
443    pub fn new(proxy: Arc<dyn EventLoopProxyProvider>) -> Self {
444        Self { proxy }
445    }
446}
447
448pub trait EventLoopProxyProvider: Send + Sync + Debug {
449    /// See [`EventLoopProxy::wake_up`] for details.
450    fn wake_up(&self);
451}
452
453/// A proxy for the underlying display handle.
454///
455/// The purpose of this type is to provide a cheaply cloneable handle to the underlying
456/// display handle. This is often used by graphics APIs to connect to the underlying APIs.
457/// It is difficult to keep a handle to the underlying event loop type or the [`ActiveEventLoop`]
458/// type. In contrast, this type involves no lifetimes and can be persisted for as long as
459/// needed.
460///
461/// For all platforms, this is one of the following:
462///
463/// - A zero-sized type that is likely optimized out.
464/// - A reference-counted pointer to the underlying type.
465#[derive(Clone)]
466pub struct OwnedDisplayHandle {
467    pub(crate) handle: Arc<dyn HasDisplayHandle + Send + Sync>,
468}
469
470impl OwnedDisplayHandle {
471    pub fn new(handle: Arc<dyn HasDisplayHandle + Send + Sync>) -> Self {
472        Self { handle }
473    }
474}
475
476impl HasDisplayHandle for OwnedDisplayHandle {
477    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
478        self.handle.display_handle()
479    }
480}
481
482impl fmt::Debug for OwnedDisplayHandle {
483    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484        f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive()
485    }
486}
487
488impl PartialEq for OwnedDisplayHandle {
489    fn eq(&self, other: &Self) -> bool {
490        match (self.display_handle(), other.display_handle()) {
491            (Ok(lhs), Ok(rhs)) => lhs == rhs,
492            _ => false,
493        }
494    }
495}
496
497impl Eq for OwnedDisplayHandle {}
498
499/// Set through [`ActiveEventLoop::set_control_flow()`].
500///
501/// Indicates the desired behavior of the event loop after [`about_to_wait`] is called.
502///
503/// Defaults to [`Wait`].
504///
505/// [`Wait`]: Self::Wait
506/// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait
507#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
508#[allow(clippy::exhaustive_enums)]
509pub enum ControlFlow {
510    /// When the current loop iteration finishes, immediately begin a new iteration regardless of
511    /// whether or not new events are available to process.
512    Poll,
513
514    /// When the current loop iteration finishes, suspend the thread until another event arrives.
515    #[default]
516    Wait,
517
518    /// When the current loop iteration finishes, suspend the thread until either another event
519    /// arrives or the given time is reached.
520    ///
521    /// Useful for implementing efficient timers. Applications which want to render at the
522    /// display's native refresh rate should instead use [`Poll`] and the VSync functionality
523    /// of a graphics API to reduce odds of missed frames.
524    ///
525    /// [`Poll`]: Self::Poll
526    WaitUntil(Instant),
527}
528
529impl ControlFlow {
530    /// Creates a [`ControlFlow`] that waits until a timeout has expired.
531    ///
532    /// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
533    /// instead set to [`Wait`].
534    ///
535    /// [`WaitUntil`]: Self::WaitUntil
536    /// [`Wait`]: Self::Wait
537    pub fn wait_duration(timeout: Duration) -> Self {
538        match Instant::now().checked_add(timeout) {
539            Some(instant) => Self::WaitUntil(instant),
540            None => Self::Wait,
541        }
542    }
543}
544
545/// Control when device events are captured.
546#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
547#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
548#[allow(clippy::exhaustive_enums)]
549pub enum DeviceEvents {
550    /// Report device events regardless of window focus.
551    Always,
552    /// Only capture device events while the window is focused.
553    #[default]
554    WhenFocused,
555    /// Never capture device events.
556    Never,
557}
558
559/// A unique identifier of the winit's async request.
560///
561/// This could be used to identify the async request once it's done
562/// and a specific action must be taken.
563///
564/// One of the handling scenarios could be to maintain a working list
565/// containing [`AsyncRequestSerial`] and some closure associated with it.
566/// Then once event is arriving the working list is being traversed and a job
567/// executed and removed from the list.
568#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
569pub struct AsyncRequestSerial {
570    serial: usize,
571}
572
573impl AsyncRequestSerial {
574    pub fn get() -> Self {
575        static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0);
576        // NOTE: We rely on wrap around here, while the user may just request
577        // in the loop usize::MAX times that's issue is considered on them.
578        let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
579        Self { serial }
580    }
581}