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}