Skip to main content

wingy_core/event_loop/
mod.rs

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