From 284f4c055828c280a2c7413122c3dc001eb20f5f Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sun, 4 Feb 2024 15:28:25 -0800 Subject: [PATCH] feat: Move Event to winit-core This requires a lot of restructuring on the backend side, as we need to accommodate for backends not being able to construct these types implicitly. In addition, we also need to expose some new APIs on these types to make them usable. Several breaking changes occurred in this commit. Signed-off-by: John Nunley --- winit-core/Cargo.toml | 4 + winit-core/build.rs | 24 + winit-core/src/event.rs | 1316 +++++++++++++++++ winit-core/src/event_loop.rs | 28 + winit-core/src/keyboard.rs | 23 + winit-core/src/lib.rs | 1 + winit-core/src/window.rs | 22 + winit/src/event.rs | 1298 +--------------- winit/src/event_loop.rs | 31 +- winit/src/keyboard.rs | 24 - winit/src/platform/modifier_supplement.rs | 5 +- winit/src/platform/startup_notify.rs | 6 +- winit/src/platform_impl/android/mod.rs | 4 +- winit/src/platform_impl/ios/app_state.rs | 26 +- .../platform_impl/linux/common/xkb_state.rs | 4 +- .../linux/wayland/event_loop/sink.rs | 6 +- .../linux/wayland/seat/touch/mod.rs | 2 +- .../linux/wayland/types/xdg_activation.rs | 2 +- .../platform_impl/linux/wayland/window/mod.rs | 2 +- .../linux/x11/event_processor.rs | 27 +- winit/src/platform_impl/linux/x11/mod.rs | 2 +- winit/src/platform_impl/linux/x11/window.rs | 2 +- winit/src/platform_impl/macos/event.rs | 13 +- winit/src/platform_impl/macos/view.rs | 2 +- winit/src/platform_impl/orbital/event_loop.rs | 13 +- .../platform_impl/web/event_loop/runner.rs | 12 +- .../web/event_loop/window_target.rs | 15 +- winit/src/platform_impl/web/web_sys/event.rs | 18 +- .../src/platform_impl/windows/drop_handler.rs | 6 +- winit/src/platform_impl/windows/event_loop.rs | 42 +- .../windows/event_loop/runner.rs | 13 +- winit/src/platform_impl/windows/keyboard.rs | 10 +- winit/src/platform_impl/windows/mod.rs | 14 +- winit/src/window.rs | 14 - 34 files changed, 1555 insertions(+), 1476 deletions(-) create mode 100644 winit-core/build.rs create mode 100644 winit-core/src/event_loop.rs diff --git a/winit-core/Cargo.toml b/winit-core/Cargo.toml index 1fe2249ff..da345d173 100644 --- a/winit-core/Cargo.toml +++ b/winit-core/Cargo.toml @@ -23,3 +23,7 @@ bitflags.workspace = true cursor-icon.workspace = true serde = { workspace = true, optional = true } smol_str.workspace = true +web-time = "1" + +[build-dependencies] +cfg_aliases = "0.2.0" diff --git a/winit-core/build.rs b/winit-core/build.rs new file mode 100644 index 000000000..371e4978c --- /dev/null +++ b/winit-core/build.rs @@ -0,0 +1,24 @@ +use cfg_aliases::cfg_aliases; + +fn main() { + // The script doesn't depend on our code + println!("cargo:rerun-if-changed=build.rs"); + + // Setup cfg aliases + cfg_aliases! { + // Systems. + android_platform: { target_os = "android" }, + web_platform: { all(target_family = "wasm", target_os = "unknown") }, + macos_platform: { target_os = "macos" }, + ios_platform: { target_os = "ios" }, + windows_platform: { target_os = "windows" }, + apple: { any(target_os = "ios", target_os = "macos") }, + free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) }, + redox: { target_os = "redox" }, + + // Native displays. + x11_platform: { all(feature = "x11", free_unix, not(redox)) }, + wayland_platform: { all(feature = "wayland", free_unix, not(redox)) }, + orbital_platform: { redox }, + } +} diff --git a/winit-core/src/event.rs b/winit-core/src/event.rs index 05f9f4260..ba6444002 100644 --- a/winit-core/src/event.rs +++ b/winit-core/src/event.rs @@ -1,5 +1,21 @@ //! Incoming notifications from the GUI system. +use crate::dpi::{PhysicalPosition, PhysicalSize}; +use crate::event_loop::AsyncRequestSerial; +use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState}; +use crate::window::{ActivationToken, Theme, WindowId}; + +use std::fmt; +use std::path::PathBuf; +use std::sync::{Mutex, Weak}; + +#[cfg(not(web_platform))] +use std::time::Instant; +#[cfg(web_platform)] +use web_time::Instant; + +use smol_str::SmolStr; + /// Identifier of an input device. /// /// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which @@ -34,3 +50,1303 @@ impl From for u64 { value.0 } } + +/// Describes a generic event. +/// +/// See the module-level docs for more information on the event loop manages each event. +#[derive(Debug, Clone, PartialEq)] +pub enum Event { + /// Emitted when new events arrive from the OS to be processed. + /// + /// This event type is useful as a place to put code that should be done before you start + /// processing events, such as updating frame timing information for benchmarking or checking + /// the [`StartCause`] to see if a timer set by + /// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed. + NewEvents(StartCause), + + /// Emitted when the OS sends an event to a winit window. + WindowEvent { + window_id: WindowId, + event: WindowEvent, + }, + + /// Emitted when the OS sends an event to a device. + DeviceEvent { + device_id: DeviceId, + event: DeviceEvent, + }, + + /// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event) + UserEvent(T), + + /// Emitted when the application has been suspended. + /// + /// # Portability + /// + /// Not all platforms support the notion of suspending applications, and there may be no + /// technical way to guarantee being able to emit a `Suspended` event if the OS has + /// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason, + /// Winit does not currently try to emit pseudo `Suspended` events before the application + /// quits on platforms without an application lifecycle. + /// + /// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally + /// driven by multiple platform-specific events, and that there may be subtle differences across + /// platforms with how these internal events are delivered, it's recommended that applications + /// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events. + /// + /// Also see [`Resumed`] notes. + /// + /// ## Android + /// + /// On Android, the `Suspended` event is only sent when the application's associated + /// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`] + /// lifecycle event but there may technically be a discrepancy. + /// + /// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause() + /// + /// Applications that need to run on Android should assume their [`SurfaceView`] has been + /// destroyed, which indirectly invalidates any existing render surfaces that may have been + /// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]). + /// + /// After being `Suspended` on Android applications must drop all render surfaces before + /// the event callback completes, which may be re-created when the application is next [`Resumed`]. + /// + /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView + /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle + /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html + /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html + /// + /// ## iOS + /// + /// On iOS, the `Suspended` event is currently emitted in response to an + /// [`applicationWillResignActive`] callback which means that the application is + /// about to transition from the active to inactive state (according to the + /// [iOS application lifecycle]). + /// + /// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive + /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle + /// + /// ## Web + /// + /// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event + /// with the property [`persisted`] being true, which means that the page is being + /// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a + /// complete snapshot of a page (including the JavaScript heap) as the user is + /// navigating away. + /// + /// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event + /// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted + /// [`bfcache`]: https://web.dev/bfcache/ + /// + /// [`Resumed`]: Self::Resumed + Suspended, + + /// Emitted when the application has been resumed. + /// + /// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a + /// formal suspend/resume lifecycle. For systems without a standard suspend/resume lifecycle + /// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init] + /// event. + /// + /// # Portability + /// + /// It's recommended that applications should only initialize their graphics context and create + /// a window after they have received their first `Resumed` event. Some systems + /// (specifically Android) won't allow applications to create a render surface until they are + /// resumed. + /// + /// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally + /// driven by multiple platform-specific events, and that there may be subtle differences across + /// platforms with how these internal events are delivered, it's recommended that applications + /// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events. + /// + /// Also see [`Suspended`] notes. + /// + /// ## Android + /// + /// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is + /// expected to closely correlate with the [`onResume`] lifecycle event but there may technically + /// be a discrepancy. + /// + /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume() + /// + /// Applications that need to run on Android must wait until they have been `Resumed` + /// before they will be able to create a render surface (such as an `EGLSurface`, + /// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a + /// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their + /// render surfaces are invalid and should be dropped. + /// + /// Also see [`Suspended`] notes. + /// + /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView + /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle + /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html + /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html + /// + /// ## iOS + /// + /// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`] + /// callback which means the application is "active" (according to the + /// [iOS application lifecycle]). + /// + /// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive + /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle + /// + /// ## Web + /// + /// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event + /// with the property [`persisted`] being true, which means that the page is being + /// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that + /// stores a complete snapshot of a page (including the JavaScript heap) as the + /// user is navigating away. + /// + /// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event + /// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted + /// [`bfcache`]: https://web.dev/bfcache/ + /// + /// [`Suspended`]: Self::Suspended + Resumed, + + /// Emitted when the event loop is about to block and wait for new events. + /// + /// Most applications shouldn't need to hook into this event since there is no real relationship + /// between how often the event loop needs to wake up and the dispatching of any specific events. + /// + /// High frequency event sources, such as input devices could potentially lead to lots of wake + /// ups and also lots of corresponding `AboutToWait` events. + /// + /// This is not an ideal event to drive application rendering from and instead applications + /// should render in response to [`WindowEvent::RedrawRequested`] events. + AboutToWait, + + /// Emitted when the event loop is being shut down. + /// + /// This is irreversible - if this event is emitted, it is guaranteed to be the last event that + /// gets emitted. You generally want to treat this as a "do on quit" event. + LoopExiting, + + /// Emitted when the application has received a memory warning. + /// + /// ## Platform-specific + /// + /// ### Android + /// + /// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application + /// must [release memory] or risk being killed. + /// + /// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory() + /// [release memory]: https://developer.android.com/topic/performance/memory#release + /// + /// ### iOS + /// + /// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`] + /// callback. The application must free as much memory as possible or risk being terminated, see + /// [how to respond to memory warnings]. + /// + /// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni + /// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings + /// + /// ### Others + /// + /// - **macOS / Wayland / Windows / Orbital:** Unsupported. + MemoryWarning, +} + +impl Event { + #[allow(clippy::result_large_err)] + pub fn map_nonuser_event(self) -> Result, Event> { + use self::Event::*; + match self { + UserEvent(_) => Err(self), + WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }), + DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }), + NewEvents(cause) => Ok(NewEvents(cause)), + AboutToWait => Ok(AboutToWait), + LoopExiting => Ok(LoopExiting), + Suspended => Ok(Suspended), + Resumed => Ok(Resumed), + MemoryWarning => Ok(MemoryWarning), + } + } +} + +/// Describes the reason the event loop is resuming. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum StartCause { + /// Sent if the time specified by [`ControlFlow::WaitUntil`] has been reached. Contains the + /// moment the timeout was requested and the requested resume time. The actual resume time is + /// guaranteed to be equal to or after the requested resume time. + /// + /// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil + ResumeTimeReached { + start: Instant, + requested_resume: Instant, + }, + + /// Sent if the OS has new events to send to the window, after a wait was requested. Contains + /// the moment the wait was requested and the resume time, if requested. + WaitCancelled { + start: Instant, + requested_resume: Option, + }, + + /// Sent if the event loop is being resumed after the loop's control flow was set to + /// [`ControlFlow::Poll`]. + /// + /// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll + Poll, + + /// Sent once, immediately after `run` is called. Indicates that the loop was just initialized. + Init, +} + +/// Describes an event from a [`Window`]. +#[derive(Debug, Clone, PartialEq)] +pub enum WindowEvent { + /// The activation token was delivered back and now could be used. + /// + #[cfg_attr( + not(any(x11_platform, wayland_platfrom)), + allow(rustdoc::broken_intra_doc_links) + )] + /// Delivered in response to [`request_activation_token`]. + /// + /// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token + ActivationTokenDone { + serial: AsyncRequestSerial, + token: ActivationToken, + }, + + /// The size of the window has changed. Contains the client area's new dimensions. + Resized(PhysicalSize), + + /// The position of the window has changed. Contains the window's new position. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Web / Wayland:** Unsupported. + Moved(PhysicalPosition), + + /// The window has been requested to close. + CloseRequested, + + /// The window has been destroyed. + Destroyed, + + /// A file has been dropped into the window. + /// + /// When the user drops multiple files at once, this event will be emitted for each file + /// separately. + DroppedFile(PathBuf), + + /// A file is being hovered over the window. + /// + /// When the user hovers multiple files at once, this event will be emitted for each file + /// separately. + HoveredFile(PathBuf), + + /// A file was hovered, but has exited the window. + /// + /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were + /// hovered. + HoveredFileCancelled, + + /// The window gained or lost focus. + /// + /// The parameter is true if the window has gained focus, and false if it has lost focus. + Focused(bool), + + /// An event from the keyboard has been received. + /// + /// ## Platform-specific + /// - **Windows:** The shift key overrides NumLock. In other words, while shift is held down, + /// numpad keys act as if NumLock wasn't active. When this is used, the OS sends fake key + /// events which are not marked as `is_synthetic`. + KeyboardInput { + device_id: DeviceId, + event: KeyEvent, + + /// If `true`, the event was generated synthetically by winit + /// in one of the following circumstances: + /// + /// * Synthetic key press events are generated for all keys pressed + /// when a window gains focus. Likewise, synthetic key release events + /// are generated for all keys pressed when a window goes out of focus. + /// ***Currently, this is only functional on X11 and Windows*** + /// + /// Otherwise, this value is always `false`. + is_synthetic: bool, + }, + + /// The keyboard modifiers have changed. + ModifiersChanged(Modifiers), + + /// An event from an input method. + /// + /// **Note:** You have to explicitly enable this event using [`Window::set_ime_allowed`]. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Web / Orbital:** Unsupported. + Ime(Ime), + + /// The cursor has moved on the window. + /// + /// ## Platform-specific + /// + /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. + /// + /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border + /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding + /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform + CursorMoved { + device_id: DeviceId, + + /// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is + /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor + /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. + position: PhysicalPosition, + }, + + /// The cursor has entered the window. + /// + /// ## Platform-specific + /// + /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. + /// + /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border + /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding + /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform + CursorEntered { device_id: DeviceId }, + + /// The cursor has left the window. + /// + /// ## Platform-specific + /// + /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. + /// + /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border + /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding + /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform + CursorLeft { device_id: DeviceId }, + + /// A mouse wheel movement or touchpad scroll occurred. + MouseWheel { + device_id: DeviceId, + delta: MouseScrollDelta, + phase: TouchPhase, + }, + + /// An mouse button press has been received. + MouseInput { + device_id: DeviceId, + state: ElementState, + button: MouseButton, + }, + + /// Two-finger pinch gesture, often used for magnification. + /// + /// ## Platform-specific + /// + /// - Only available on **macOS** and **iOS**. + /// - On iOS, not recognized by default. It must be enabled when needed. + PinchGesture { + device_id: DeviceId, + /// Positive values indicate magnification (zooming in) and negative + /// values indicate shrinking (zooming out). + /// + /// This value may be NaN. + delta: f64, + phase: TouchPhase, + }, + + /// Double tap gesture. + /// + /// On a Mac, smart magnification is triggered by a double tap with two fingers + /// on the trackpad and is commonly used to zoom on a certain object + /// (e.g. a paragraph of a PDF) or (sort of like a toggle) to reset any zoom. + /// The gesture is also supported in Safari, Pages, etc. + /// + /// The event is general enough that its generating gesture is allowed to vary + /// across platforms. It could also be generated by another device. + /// + /// Unfortunatly, neither [Windows](https://support.microsoft.com/en-us/windows/touch-gestures-for-windows-a9d28305-4818-a5df-4e2b-e5590f850741) + /// nor [Wayland](https://wayland.freedesktop.org/libinput/doc/latest/gestures.html) + /// support this gesture or any other gesture with the same effect. + /// + /// ## Platform-specific + /// + /// - Only available on **macOS 10.8** and later, and **iOS**. + /// - On iOS, not recognized by default. It must be enabled when needed. + DoubleTapGesture { device_id: DeviceId }, + + /// Two-finger rotation gesture. + /// + /// Positive delta values indicate rotation counterclockwise and + /// negative delta values indicate rotation clockwise. + /// + /// ## Platform-specific + /// + /// - Only available on **macOS** and **iOS**. + /// - On iOS, not recognized by default. It must be enabled when needed. + RotationGesture { + device_id: DeviceId, + delta: f32, + phase: TouchPhase, + }, + + /// Touchpad pressure event. + /// + /// At the moment, only supported on Apple forcetouch-capable macbooks. + /// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad + /// is being pressed) and stage (integer representing the click level). + TouchpadPressure { + device_id: DeviceId, + pressure: f32, + stage: i64, + }, + + /// Motion on some analog axis. May report data redundant to other, more specific events. + AxisMotion { + device_id: DeviceId, + axis: AxisId, + value: f64, + }, + + /// Touch event has been received + /// + /// ## Platform-specific + /// + /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. + /// - **macOS:** Unsupported. + /// + /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border + /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding + /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform + Touch(Touch), + + /// The window's scale factor has changed. + /// + /// The following user actions can cause DPI changes: + /// + /// * Changing the display's resolution. + /// * Changing the display's scale factor (e.g. in Control Panel on Windows). + /// * Moving the window to a display with a different scale factor. + /// + /// After this event callback has been processed, the window will be resized to whatever value + /// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested + /// by the OS, but it can be changed to any value. + /// + /// For more information about DPI in general, see the [`dpi`](crate::dpi) module. + ScaleFactorChanged { + scale_factor: f64, + /// Handle to update inner size during scale changes. + /// + /// See [`InnerSizeWriter`] docs for more details. + inner_size_writer: InnerSizeWriter, + }, + + /// The system window theme has changed. + /// + /// Applications might wish to react to this to change the theme of the content of the window + /// when the system changes the window theme. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported. + ThemeChanged(Theme), + + /// The window has been occluded (completely hidden from view). + /// + /// This is different to window visibility as it depends on whether the window is closed, + /// minimised, set invisible, or fully occluded by another window. + /// + /// ## Platform-specific + /// + /// ### iOS + /// + /// On iOS, the `Occluded(false)` event is emitted in response to an [`applicationWillEnterForeground`] + /// callback which means the application should start preparing its data. The `Occluded(true)` event is + /// emitted in response to an [`applicationDidEnterBackground`] callback which means the application + /// should free resources (according to the [iOS application lifecycle]). + /// + /// [`applicationWillEnterForeground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground + /// [`applicationDidEnterBackground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground + /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle + /// + /// ### Others + /// + /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. + /// - **Android / Wayland / Windows / Orbital:** Unsupported. + /// + /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border + /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding + /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform + Occluded(bool), + + /// Emitted when a window should be redrawn. + /// + /// This gets triggered in two scenarios: + /// - The OS has performed an operation that's invalidated the window's contents (such as + /// resizing the window). + /// - The application has explicitly requested a redraw via [`Window::request_redraw`]. + /// + /// Winit will aggregate duplicate redraw requests into a single event, to + /// help avoid duplicating rendering work. + RedrawRequested, +} + +/// Represents raw hardware events that are not associated with any particular window. +/// +/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person +/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because +/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs +/// may not match. +/// +/// Note that these events are delivered regardless of input focus. +#[derive(Clone, Debug, PartialEq)] +pub enum DeviceEvent { + Added, + Removed, + + /// Change in physical position of a pointing device. + /// + /// This represents raw, unfiltered physical motion. Not to be confused with [`WindowEvent::CursorMoved`]. + MouseMotion { + /// (x, y) change in position in unspecified units. + /// + /// Different devices may use different units. + delta: (f64, f64), + }, + + /// Physical scroll event + MouseWheel { + delta: MouseScrollDelta, + }, + + /// Motion on some analog axis. This event will be reported for all arbitrary input devices + /// that winit supports on this platform, including mouse devices. If the device is a mouse + /// device then this will be reported alongside the MouseMotion event. + Motion { + axis: AxisId, + value: f64, + }, + + Button { + button: ButtonId, + state: ElementState, + }, + + Key(RawKeyEvent), +} + +/// Describes a keyboard input as a raw device event. +/// +/// Note that holding down a key may produce repeated `RawKeyEvent`s. The +/// operating system doesn't provide information whether such an event is a +/// repeat or the initial keypress. An application may emulate this by, for +/// example keeping a Map/Set of pressed keys and determining whether a keypress +/// corresponds to an already pressed key. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct RawKeyEvent { + pub physical_key: keyboard::PhysicalKey, + pub state: ElementState, +} + +/// Describes a keyboard input targeting a window. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEvent { + /// Represents the position of a key independent of the currently active layout. + /// + /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). + /// The most prevalent use case for this is games. For example the default keys for the player + /// to move around might be the W, A, S, and D keys on a US layout. The position of these keys + /// is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY" + /// layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.) + /// + /// ## Caveats + /// + /// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that + /// implements DVORAK in hardware (or firmware) + /// - Your application will likely have to handle keyboards which are missing keys that your + /// own keyboard has. + /// - Certain `KeyCode`s will move between a couple of different positions depending on what + /// layout the keyboard was manufactured to support. + /// + /// **Because of these caveats, it is important that you provide users with a way to configure + /// most (if not all) keybinds in your application.** + /// + /// ## `Fn` and `FnLock` + /// + /// `Fn` and `FnLock` key events are *exceedingly unlikely* to be emitted by Winit. These keys + /// are usually handled at the hardware or OS level, and aren't surfaced to applications. If + /// you somehow see this in the wild, we'd like to know :) + pub physical_key: keyboard::PhysicalKey, + + // Allowing `broken_intra_doc_links` for `logical_key`, because + // `key_without_modifiers` is not available on all platforms + #[cfg_attr( + not(any(windows_platform, macos_platform, x11_platform, wayland_platform)), + allow(rustdoc::broken_intra_doc_links) + )] + /// This value is affected by all modifiers except Ctrl. + /// + /// This has two use cases: + /// - Allows querying whether the current input is a Dead key. + /// - Allows handling key-bindings on platforms which don't + /// support [`key_without_modifiers`]. + /// + /// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard + /// shortcuts, **it is important that you provide users with a way to configure your + /// application's shortcuts so you don't render your application unusable for users with an + /// incompatible keyboard layout.** + /// + /// ## Platform-specific + /// - **Web:** Dead keys might be reported as the real key instead + /// of `Dead` depending on the browser/OS. + /// + /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers + pub logical_key: keyboard::Key, + + /// Contains the text produced by this keypress. + /// + /// In most cases this is identical to the content + /// of the `Character` variant of `logical_key`. + /// However, on Windows when a dead key was pressed earlier + /// but cannot be combined with the character from this + /// keypress, the produced text will consist of two characters: + /// the dead-key-character followed by the character resulting + /// from this keypress. + /// + /// An additional difference from `logical_key` is that + /// this field stores the text representation of any key + /// that has such a representation. For example when + /// `logical_key` is `Key::Named(NamedKey::Enter)`, this field is `Some("\r")`. + /// + /// This is `None` if the current keypress cannot + /// be interpreted as text. + /// + /// See also: `text_with_all_modifiers()` + pub text: Option, + + /// Contains the location of this key on the keyboard. + /// + /// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" key + /// appears on the left side of the QWERTY keyboard as well as the right side. However, both keys + /// have the same symbolic value. Another example of this phenomenon is the "1" key, which appears + /// both above the "Q" key and as the "Keypad 1" key. + /// + /// This field allows the user to differentiate between keys like this that have the same symbolic + /// value but different locations on the keyboard. + /// + /// See the [`KeyLocation`] type for more details. + /// + /// [`KeyLocation`]: crate::keyboard::KeyLocation + pub location: keyboard::KeyLocation, + + /// Whether the key is being pressed or released. + /// + /// See the [`ElementState`] type for more details. + pub state: ElementState, + + /// Whether or not this key is a key repeat event. + /// + /// On some systems, holding down a key for some period of time causes that key to be repeated + /// as though it were being pressed and released repeatedly. This field is `true` if and only if + /// this event is the result of one of those repeats. + pub repeat: bool, + + /// Platform-specific key event information. + /// + /// On Windows, Linux and macOS, this type contains the key without modifiers and the text with all + /// modifiers applied. + /// + /// On Android, iOS, Redox and Web, this type is a no-op. + pub extra: Extra, +} + +/// Describes keyboard modifiers event. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct Modifiers { + pub(crate) state: ModifiersState, + + // NOTE: Currently pressed modifiers keys. + // + // The field providing a metadata, it shouldn't be used as a source of truth. + pub(crate) pressed_mods: ModifiersKeys, +} + +impl Modifiers { + #[doc(hidden)] + pub fn new(state: ModifiersState, pressed_mods: ModifiersKeys) -> Self { + Self { + state, + pressed_mods, + } + } + + /// The state of the modifiers. + pub fn state(&self) -> ModifiersState { + self.state + } + + /// The state of the left shift key. + pub fn lshift_state(&self) -> ModifiersKeyState { + self.mod_state(ModifiersKeys::LSHIFT) + } + + /// The state of the right shift key. + pub fn rshift_state(&self) -> ModifiersKeyState { + self.mod_state(ModifiersKeys::RSHIFT) + } + + /// The state of the left alt key. + pub fn lalt_state(&self) -> ModifiersKeyState { + self.mod_state(ModifiersKeys::LALT) + } + + /// The state of the right alt key. + pub fn ralt_state(&self) -> ModifiersKeyState { + self.mod_state(ModifiersKeys::RALT) + } + + /// The state of the left control key. + pub fn lcontrol_state(&self) -> ModifiersKeyState { + self.mod_state(ModifiersKeys::LCONTROL) + } + + /// The state of the right control key. + pub fn rcontrol_state(&self) -> ModifiersKeyState { + self.mod_state(ModifiersKeys::RCONTROL) + } + + /// The state of the left super key. + pub fn lsuper_state(&self) -> ModifiersKeyState { + self.mod_state(ModifiersKeys::LSUPER) + } + + /// The state of the right super key. + pub fn rsuper_state(&self) -> ModifiersKeyState { + self.mod_state(ModifiersKeys::RSUPER) + } + + fn mod_state(&self, modifier: ModifiersKeys) -> ModifiersKeyState { + if self.pressed_mods.contains(modifier) { + ModifiersKeyState::Pressed + } else { + ModifiersKeyState::Unknown + } + } +} + +impl From for Modifiers { + fn from(value: ModifiersState) -> Self { + Self { + state: value, + pressed_mods: Default::default(), + } + } +} + +/// Describes [input method](https://en.wikipedia.org/wiki/Input_method) events. +/// +/// This is also called a "composition event". +/// +/// Most keypresses using a latin-like keyboard layout simply generate a [`WindowEvent::KeyboardInput`]. +/// However, one couldn't possibly have a key for every single unicode character that the user might want to type +/// - so the solution operating systems employ is to allow the user to type these using _a sequence of keypresses_ instead. +/// +/// A prominent example of this is accents - many keyboard layouts allow you to first click the "accent key", and then +/// the character you want to apply the accent to. In this case, some platforms will generate the following event sequence: +/// ```ignore +/// // Press "`" key +/// Ime::Preedit("`", Some((0, 0))) +/// // Press "E" key +/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. +/// Ime::Commit("é") +/// ``` +/// +/// Additionally, certain input devices are configured to display a candidate box that allow the user to select the +/// desired character interactively. (To properly position this box, you must use [`Window::set_ime_cursor_area`].) +/// +/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the following event +/// sequence could be obtained: +/// ```ignore +/// // Press "A" key +/// Ime::Preedit("a", Some((1, 1))) +/// // Press "B" key +/// Ime::Preedit("a b", Some((3, 3))) +/// // Press left arrow key +/// Ime::Preedit("a b", Some((1, 1))) +/// // Press space key +/// Ime::Preedit("啊b", Some((3, 3))) +/// // Press space key +/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. +/// Ime::Commit("啊不") +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Ime { + /// Notifies when the IME was enabled. + /// + /// After getting this event you could receive [`Preedit`](Self::Preedit) and + /// [`Commit`](Self::Commit) events. You should also start performing IME related requests + /// like [`Window::set_ime_cursor_area`]. + Enabled, + + /// Notifies when a new composing text should be set at the cursor position. + /// + /// The value represents a pair of the preedit string and the cursor begin position and end + /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string + /// this indicates that preedit was cleared. + /// + /// The cursor position is byte-wise indexed. + Preedit(String, Option<(usize, usize)>), + + /// Notifies when text should be inserted into the editor widget. + /// + /// Right before this event winit will send empty [`Self::Preedit`] event. + Commit(String), + + /// Notifies when the IME was disabled. + /// + /// After receiving this event you won't get any more [`Preedit`](Self::Preedit) or + /// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You should + /// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending + /// preedit text. + Disabled, +} + +/// Describes touch-screen input state. +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum TouchPhase { + Started, + Moved, + Ended, + Cancelled, +} + +/// Represents a touch event +/// +/// Every time the user touches the screen, a new [`TouchPhase::Started`] event with an unique +/// identifier for the finger is generated. When the finger is lifted, an [`TouchPhase::Ended`] +/// event is generated with the same finger id. +/// +/// After a `Started` event has been emitted, there may be zero or more `Move` +/// events when the finger is moved or the touch pressure changes. +/// +/// The finger id may be reused by the system after an `Ended` event. The user +/// should assume that a new `Started` event received with the same id has nothing +/// to do with the old finger and is a new finger. +/// +/// A [`TouchPhase::Cancelled`] event is emitted when the system has canceled tracking this +/// touch, such as when the window loses focus, or on iOS if the user moves the +/// device against their face. +/// +/// ## Platform-specific +/// +/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. +/// - **macOS:** Unsupported. +/// +/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border +/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding +/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Touch { + pub device_id: DeviceId, + pub phase: TouchPhase, + pub location: PhysicalPosition, + /// Describes how hard the screen was pressed. May be `None` if the platform + /// does not support pressure sensitivity. + /// + /// ## Platform-specific + /// + /// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**. + /// - **Android**: This will never be [None]. If the device doesn't support pressure + /// sensitivity, force will either be 0.0 or 1.0. Also see the + /// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE). + pub force: Option, + /// Unique identifier of a finger. + pub id: u64, +} + +/// Describes the force of a touch event +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Force { + /// On iOS, the force is calibrated so that the same number corresponds to + /// roughly the same amount of pressure on the screen regardless of the + /// device. + Calibrated { + /// The force of the touch, where a value of 1.0 represents the force of + /// an average touch (predetermined by the system, not user-specific). + /// + /// The force reported by Apple Pencil is measured along the axis of the + /// pencil. If you want a force perpendicular to the device, you need to + /// calculate this value using the `altitude_angle` value. + force: f64, + /// The maximum possible force for a touch. + /// + /// The value of this field is sufficiently high to provide a wide + /// dynamic range for values of the `force` field. + max_possible_force: f64, + /// The altitude (in radians) of the stylus. + /// + /// A value of 0 radians indicates that the stylus is parallel to the + /// surface. The value of this property is Pi/2 when the stylus is + /// perpendicular to the surface. + altitude_angle: Option, + }, + /// If the platform reports the force as normalized, we have no way of + /// knowing how much pressure 1.0 corresponds to – we know it's the maximum + /// amount of force, but as to how much force, you might either have to + /// press really really hard, or not hard at all, depending on the device. + Normalized(f64), +} + +impl Force { + /// Returns the force normalized to the range between 0.0 and 1.0 inclusive. + /// + /// Instead of normalizing the force, you should prefer to handle + /// [`Force::Calibrated`] so that the amount of force the user has to apply is + /// consistent across devices. + pub fn normalized(&self) -> f64 { + match self { + Force::Calibrated { + force, + max_possible_force, + altitude_angle, + } => { + let force = match altitude_angle { + Some(altitude_angle) => force / altitude_angle.sin(), + None => *force, + }; + force / max_possible_force + } + Force::Normalized(force) => *force, + } + } +} + +/// Identifier for a specific analog axis on some device. +pub type AxisId = u32; + +/// Identifier for a specific button on some device. +pub type ButtonId = u32; + +/// Describes the input state of a key. +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ElementState { + Pressed, + Released, +} + +impl ElementState { + /// True if `self == Pressed`. + pub fn is_pressed(self) -> bool { + self == ElementState::Pressed + } +} + +/// Describes a button of a mouse controller. +/// +/// ## Platform-specific +/// +/// **macOS:** `Back` and `Forward` might not work with all hardware. +/// **Orbital:** `Back` and `Forward` are unsupported due to orbital not supporting them. +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum MouseButton { + Left, + Right, + Middle, + Back, + Forward, + Other(u16), +} + +/// Describes a difference in the mouse scroll wheel state. +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum MouseScrollDelta { + /// Amount in lines or rows to scroll in the horizontal + /// and vertical directions. + /// + /// Positive values indicate that the content that is being scrolled should move + /// right and down (revealing more content left and up). + LineDelta(f32, f32), + + /// Amount in pixels to scroll in the horizontal and + /// vertical direction. + /// + /// Scroll events are expressed as a `PixelDelta` if + /// supported by the device (eg. a touchpad) and + /// platform. + /// + /// Positive values indicate that the content being scrolled should + /// move right/down. + /// + /// For a 'natural scrolling' touch pad (that acts like a touch screen) + /// this means moving your fingers right and down should give positive values, + /// and move the content right and down (to reveal more things left and up). + PixelDelta(PhysicalPosition), +} + +/// Handle to synchroniously change the size of the window from the +/// [`WindowEvent`]. +#[derive(Debug, Clone)] +pub struct InnerSizeWriter { + pub(crate) new_inner_size: Weak>>, +} + +impl InnerSizeWriter { + #[cfg(not(orbital_platform))] + pub fn new(new_inner_size: Weak>>) -> Self { + Self { new_inner_size } + } + + /// Try to request inner size which will be set synchroniously on the window. + pub fn request_inner_size( + &mut self, + new_inner_size: PhysicalSize, + ) -> Result<(), InnerSizeIgnored> { + if let Some(inner) = self.new_inner_size.upgrade() { + *inner.lock().unwrap() = new_inner_size; + Ok(()) + } else { + Err(InnerSizeIgnored { _private: () }) + } + } + + pub fn get(&self) -> PhysicalSize { + *self.new_inner_size.upgrade().unwrap().lock().unwrap() + } +} + +impl PartialEq for InnerSizeWriter { + fn eq(&self, other: &Self) -> bool { + self.new_inner_size.as_ptr() == other.new_inner_size.as_ptr() + } +} + +/// Could not write the inner size. +pub struct InnerSizeIgnored { + _private: (), +} + +impl fmt::Debug for InnerSizeIgnored { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("InnerSizeIgnored").finish_non_exhaustive() + } +} + +impl fmt::Display for InnerSizeIgnored { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("tried to write inner size after") + } +} + +impl std::error::Error for InnerSizeIgnored {} + +#[cfg(test)] +mod tests { + use crate::event; + use std::collections::{BTreeSet, HashSet}; + + macro_rules! foreach_event { + ($closure:expr) => {{ + #[allow(unused_mut)] + let mut x = $closure; + let did = unsafe { event::DeviceId::dummy() }; + + #[allow(deprecated)] + { + use crate::event::{Event::*, Ime::Enabled, WindowEvent::*}; + use crate::window::WindowId; + + // Mainline events. + let wid = unsafe { WindowId::dummy() }; + x(UserEvent(())); + x(NewEvents(event::StartCause::Init)); + x(AboutToWait); + x(LoopExiting); + x(Suspended); + x(Resumed); + + // Window events. + let with_window_event = |wev| { + x(WindowEvent { + window_id: wid, + event: wev, + }) + }; + + with_window_event(CloseRequested); + with_window_event(Destroyed); + with_window_event(Focused(true)); + with_window_event(Moved((0, 0).into())); + with_window_event(Resized((0, 0).into())); + with_window_event(DroppedFile("x.txt".into())); + with_window_event(HoveredFile("x.txt".into())); + with_window_event(HoveredFileCancelled); + with_window_event(Ime(Enabled)); + with_window_event(CursorMoved { + device_id: did, + position: (0, 0).into(), + }); + with_window_event(ModifiersChanged(event::Modifiers::default())); + with_window_event(CursorEntered { device_id: did }); + with_window_event(CursorLeft { device_id: did }); + with_window_event(MouseWheel { + device_id: did, + delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), + phase: event::TouchPhase::Started, + }); + with_window_event(MouseInput { + device_id: did, + state: event::ElementState::Pressed, + button: event::MouseButton::Other(0), + }); + with_window_event(PinchGesture { + device_id: did, + delta: 0.0, + phase: event::TouchPhase::Started, + }); + with_window_event(DoubleTapGesture { device_id: did }); + with_window_event(RotationGesture { + device_id: did, + delta: 0.0, + phase: event::TouchPhase::Started, + }); + with_window_event(TouchpadPressure { + device_id: did, + pressure: 0.0, + stage: 0, + }); + with_window_event(AxisMotion { + device_id: did, + axis: 0, + value: 0.0, + }); + with_window_event(Touch(event::Touch { + device_id: did, + phase: event::TouchPhase::Started, + location: (0.0, 0.0).into(), + id: 0, + force: Some(event::Force::Normalized(0.0)), + })); + with_window_event(ThemeChanged(crate::window::Theme::Light)); + with_window_event(Occluded(true)); + } + + #[allow(deprecated)] + { + use event::DeviceEvent::*; + + let with_device_event = |dev_ev| { + x(event::Event::DeviceEvent { + device_id: did, + event: dev_ev, + }) + }; + + with_device_event(Added); + with_device_event(Removed); + with_device_event(MouseMotion { + delta: (0.0, 0.0).into(), + }); + with_device_event(MouseWheel { + delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), + }); + with_device_event(Motion { + axis: 0, + value: 0.0, + }); + with_device_event(Button { + button: 0, + state: event::ElementState::Pressed, + }); + } + }}; + } + + #[allow(clippy::redundant_clone)] + #[test] + fn test_event_clone() { + foreach_event!(|event: event::Event<(), ()>| { + let event2 = event.clone(); + assert_eq!(event, event2); + }) + } + + #[test] + fn test_map_nonuser_event() { + foreach_event!(|event: event::Event<(), ()>| { + let is_user = matches!(event, event::Event::UserEvent(())); + let event2 = event.map_nonuser_event::<()>(); + if is_user { + assert_eq!(event2, Err(event::Event::UserEvent(()))); + } else { + assert!(event2.is_ok()); + } + }) + } + + #[test] + fn test_force_normalize() { + let force = event::Force::Normalized(0.0); + assert_eq!(force.normalized(), 0.0); + + let force2 = event::Force::Calibrated { + force: 5.0, + max_possible_force: 2.5, + altitude_angle: None, + }; + assert_eq!(force2.normalized(), 2.0); + + let force3 = event::Force::Calibrated { + force: 5.0, + max_possible_force: 2.5, + altitude_angle: Some(std::f64::consts::PI / 2.0), + }; + assert_eq!(force3.normalized(), 2.0); + } + + #[allow(clippy::clone_on_copy)] + #[test] + fn ensure_attrs_do_not_panic() { + foreach_event!(|event: event::Event<(), ()>| { + let _ = format!("{:?}", event); + }); + let _ = event::StartCause::Init.clone(); + + let did = unsafe { crate::event::DeviceId::dummy() }.clone(); + HashSet::new().insert(did); + let mut set = [did, did, did]; + set.sort_unstable(); + let mut set2 = BTreeSet::new(); + set2.insert(did); + set2.insert(did); + + HashSet::new().insert(event::TouchPhase::Started.clone()); + HashSet::new().insert(event::MouseButton::Left.clone()); + HashSet::new().insert(event::Ime::Enabled); + + let _ = event::Touch { + device_id: did, + phase: event::TouchPhase::Started, + location: (0.0, 0.0).into(), + id: 0, + force: Some(event::Force::Normalized(0.0)), + } + .clone(); + let _ = event::Force::Calibrated { + force: 0.0, + max_possible_force: 0.0, + altitude_angle: None, + } + .clone(); + } +} diff --git a/winit-core/src/event_loop.rs b/winit-core/src/event_loop.rs new file mode 100644 index 000000000..0541c33af --- /dev/null +++ b/winit-core/src/event_loop.rs @@ -0,0 +1,28 @@ +//! Types needed to define the event loop. + +use std::sync::atomic::{AtomicU64, Ordering}; + +/// A unique identifier of the winit's async request. +/// +/// This could be used to identify the async request once it's done +/// and a specific action must be taken. +/// +/// One of the handling scenarious could be to maintain a working list +/// containing [`AsyncRequestSerial`] and some closure associated with it. +/// Then once event is arriving the working list is being traversed and a job +/// executed and removed from the list. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AsyncRequestSerial { + serial: u64, +} + +impl AsyncRequestSerial { + /// Get the next serial in the sequence. + pub fn get() -> Self { + static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0); + // NOTE: we rely on wrap around here, while the user may just request + // in the loop u64::MAX times that's issue is considered on them. + let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed); + Self { serial } + } +} diff --git a/winit-core/src/keyboard.rs b/winit-core/src/keyboard.rs index 2475530ea..74f9f6c10 100644 --- a/winit-core/src/keyboard.rs +++ b/winit-core/src/keyboard.rs @@ -1454,6 +1454,29 @@ pub enum NamedKey { F35, } +// NOTE: the exact modifier key is not used to represent modifiers state in the +// first place due to a fact that modifiers state could be changed without any +// key being pressed and on some platforms like Wayland/X11 which key resulted +// in modifiers change is hidden, also, not that it really matters. +// +// The reason this API is even exposed is mostly to provide a way for users +// to treat modifiers differently based on their position, which is required +// on macOS due to their AltGr/Option situation. +bitflags! { + #[doc(hidden)] + #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct ModifiersKeys: u8 { + const LSHIFT = 0b0000_0001; + const RSHIFT = 0b0000_0010; + const LCONTROL = 0b0000_0100; + const RCONTROL = 0b0000_1000; + const LALT = 0b0001_0000; + const RALT = 0b0010_0000; + const LSUPER = 0b0100_0000; + const RSUPER = 0b1000_0000; + } +} + /// Key represents the meaning of a keypress. /// /// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with diff --git a/winit-core/src/lib.rs b/winit-core/src/lib.rs index 837054dcf..a0ff281c2 100644 --- a/winit-core/src/lib.rs +++ b/winit-core/src/lib.rs @@ -13,5 +13,6 @@ compile_error! { "no-std and no-alloc usage are not yet supported" } pub mod dpi; pub mod error; pub mod event; +pub mod event_loop; pub mod keyboard; pub mod window; diff --git a/winit-core/src/window.rs b/winit-core/src/window.rs index 1a26b883f..5f5e7627d 100644 --- a/winit-core/src/window.rs +++ b/winit-core/src/window.rs @@ -191,3 +191,25 @@ impl Default for ImePurpose { Self::Normal } } + +/// An opaque token used to activate the [`Window`]. +/// +/// [`Window`]: crate::window::Window +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ActivationToken { + pub(crate) token: String, +} + +impl ActivationToken { + pub fn new(_token: String) -> Self { + Self { token: _token } + } + + pub fn token(&self) -> &str { + &self.token + } + + pub fn into_token(self) -> String { + self.token + } +} diff --git a/winit/src/event.rs b/winit/src/event.rs index 818202ee8..d63beb73f 100644 --- a/winit/src/event.rs +++ b/winit/src/event.rs @@ -32,1296 +32,22 @@ //! //! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run //! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil -use std::path::PathBuf; -use std::sync::{Mutex, Weak}; -#[cfg(not(web_platform))] -use std::time::Instant; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use smol_str::SmolStr; -#[cfg(web_platform)] -use web_time::Instant; - -use crate::error::ExternalError; -#[cfg(doc)] -use crate::window::Window; -use crate::{ - dpi::{PhysicalPosition, PhysicalSize}, - event_loop::AsyncRequestSerial, - keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState}, - platform_impl, - window::{ActivationToken, Theme, WindowId}, -}; +use crate::platform_impl; #[doc(inline)] -pub use winit_core::event::DeviceId; +pub use winit_core::event::{ + DeviceEvent, DeviceId, ElementState, Force, Ime, InnerSizeIgnored, InnerSizeWriter, Modifiers, + MouseButton, MouseScrollDelta, RawKeyEvent, StartCause, Touch, TouchPhase, +}; -/// Describes a generic event. -/// -/// See the module-level docs for more information on the event loop manages each event. -#[derive(Debug, Clone, PartialEq)] -pub enum Event { - /// Emitted when new events arrive from the OS to be processed. - /// - /// This event type is useful as a place to put code that should be done before you start - /// processing events, such as updating frame timing information for benchmarking or checking - /// the [`StartCause`] to see if a timer set by - /// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed. - NewEvents(StartCause), +pub type Event = winit_core::event::Event; +pub type WindowEvent = winit_core::event::WindowEvent; +pub type KeyEvent = winit_core::event::KeyEvent; - /// Emitted when the OS sends an event to a winit window. - WindowEvent { - window_id: WindowId, - event: WindowEvent, - }, - - /// Emitted when the OS sends an event to a device. - DeviceEvent { - device_id: DeviceId, - event: DeviceEvent, - }, - - /// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event) - UserEvent(T), - - /// Emitted when the application has been suspended. - /// - /// # Portability - /// - /// Not all platforms support the notion of suspending applications, and there may be no - /// technical way to guarantee being able to emit a `Suspended` event if the OS has - /// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason, - /// Winit does not currently try to emit pseudo `Suspended` events before the application - /// quits on platforms without an application lifecycle. - /// - /// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally - /// driven by multiple platform-specific events, and that there may be subtle differences across - /// platforms with how these internal events are delivered, it's recommended that applications - /// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events. - /// - /// Also see [`Resumed`] notes. - /// - /// ## Android - /// - /// On Android, the `Suspended` event is only sent when the application's associated - /// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`] - /// lifecycle event but there may technically be a discrepancy. - /// - /// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause() - /// - /// Applications that need to run on Android should assume their [`SurfaceView`] has been - /// destroyed, which indirectly invalidates any existing render surfaces that may have been - /// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]). - /// - /// After being `Suspended` on Android applications must drop all render surfaces before - /// the event callback completes, which may be re-created when the application is next [`Resumed`]. - /// - /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView - /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle - /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html - /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html - /// - /// ## iOS - /// - /// On iOS, the `Suspended` event is currently emitted in response to an - /// [`applicationWillResignActive`] callback which means that the application is - /// about to transition from the active to inactive state (according to the - /// [iOS application lifecycle]). - /// - /// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive - /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle - /// - /// ## Web - /// - /// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event - /// with the property [`persisted`] being true, which means that the page is being - /// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a - /// complete snapshot of a page (including the JavaScript heap) as the user is - /// navigating away. - /// - /// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event - /// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted - /// [`bfcache`]: https://web.dev/bfcache/ - /// - /// [`Resumed`]: Self::Resumed - Suspended, - - /// Emitted when the application has been resumed. - /// - /// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a - /// formal suspend/resume lifecycle. For systems without a standard suspend/resume lifecycle - /// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init] - /// event. - /// - /// # Portability - /// - /// It's recommended that applications should only initialize their graphics context and create - /// a window after they have received their first `Resumed` event. Some systems - /// (specifically Android) won't allow applications to create a render surface until they are - /// resumed. - /// - /// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally - /// driven by multiple platform-specific events, and that there may be subtle differences across - /// platforms with how these internal events are delivered, it's recommended that applications - /// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events. - /// - /// Also see [`Suspended`] notes. - /// - /// ## Android - /// - /// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is - /// expected to closely correlate with the [`onResume`] lifecycle event but there may technically - /// be a discrepancy. - /// - /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume() - /// - /// Applications that need to run on Android must wait until they have been `Resumed` - /// before they will be able to create a render surface (such as an `EGLSurface`, - /// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a - /// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their - /// render surfaces are invalid and should be dropped. - /// - /// Also see [`Suspended`] notes. - /// - /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView - /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle - /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html - /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html - /// - /// ## iOS - /// - /// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`] - /// callback which means the application is "active" (according to the - /// [iOS application lifecycle]). - /// - /// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive - /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle - /// - /// ## Web - /// - /// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event - /// with the property [`persisted`] being true, which means that the page is being - /// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that - /// stores a complete snapshot of a page (including the JavaScript heap) as the - /// user is navigating away. - /// - /// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event - /// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted - /// [`bfcache`]: https://web.dev/bfcache/ - /// - /// [`Suspended`]: Self::Suspended - Resumed, - - /// Emitted when the event loop is about to block and wait for new events. - /// - /// Most applications shouldn't need to hook into this event since there is no real relationship - /// between how often the event loop needs to wake up and the dispatching of any specific events. - /// - /// High frequency event sources, such as input devices could potentially lead to lots of wake - /// ups and also lots of corresponding `AboutToWait` events. - /// - /// This is not an ideal event to drive application rendering from and instead applications - /// should render in response to [`WindowEvent::RedrawRequested`] events. - AboutToWait, - - /// Emitted when the event loop is being shut down. - /// - /// This is irreversible - if this event is emitted, it is guaranteed to be the last event that - /// gets emitted. You generally want to treat this as a "do on quit" event. - LoopExiting, - - /// Emitted when the application has received a memory warning. - /// - /// ## Platform-specific - /// - /// ### Android - /// - /// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application - /// must [release memory] or risk being killed. - /// - /// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory() - /// [release memory]: https://developer.android.com/topic/performance/memory#release - /// - /// ### iOS - /// - /// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`] - /// callback. The application must free as much memory as possible or risk being terminated, see - /// [how to respond to memory warnings]. - /// - /// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni - /// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings - /// - /// ### Others - /// - /// - **macOS / Wayland / Windows / Orbital:** Unsupported. - MemoryWarning, -} - -impl Event { - #[allow(clippy::result_large_err)] - pub fn map_nonuser_event(self) -> Result, Event> { - use self::Event::*; - match self { - UserEvent(_) => Err(self), - WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }), - DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }), - NewEvents(cause) => Ok(NewEvents(cause)), - AboutToWait => Ok(AboutToWait), - LoopExiting => Ok(LoopExiting), - Suspended => Ok(Suspended), - Resumed => Ok(Resumed), - MemoryWarning => Ok(MemoryWarning), - } - } -} - -/// Describes the reason the event loop is resuming. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum StartCause { - /// Sent if the time specified by [`ControlFlow::WaitUntil`] has been reached. Contains the - /// moment the timeout was requested and the requested resume time. The actual resume time is - /// guaranteed to be equal to or after the requested resume time. - /// - /// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil - ResumeTimeReached { - start: Instant, - requested_resume: Instant, - }, - - /// Sent if the OS has new events to send to the window, after a wait was requested. Contains - /// the moment the wait was requested and the resume time, if requested. - WaitCancelled { - start: Instant, - requested_resume: Option, - }, - - /// Sent if the event loop is being resumed after the loop's control flow was set to - /// [`ControlFlow::Poll`]. - /// - /// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll - Poll, - - /// Sent once, immediately after `run` is called. Indicates that the loop was just initialized. - Init, -} - -/// Describes an event from a [`Window`]. -#[derive(Debug, Clone, PartialEq)] -pub enum WindowEvent { - /// The activation token was delivered back and now could be used. - /// - #[cfg_attr( - not(any(x11_platform, wayland_platfrom)), - allow(rustdoc::broken_intra_doc_links) - )] - /// Delivered in response to [`request_activation_token`]. - /// - /// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token - ActivationTokenDone { - serial: AsyncRequestSerial, - token: ActivationToken, - }, - - /// The size of the window has changed. Contains the client area's new dimensions. - Resized(PhysicalSize), - - /// The position of the window has changed. Contains the window's new position. - /// - /// ## Platform-specific - /// - /// - **iOS / Android / Web / Wayland:** Unsupported. - Moved(PhysicalPosition), - - /// The window has been requested to close. - CloseRequested, - - /// The window has been destroyed. - Destroyed, - - /// A file has been dropped into the window. - /// - /// When the user drops multiple files at once, this event will be emitted for each file - /// separately. - DroppedFile(PathBuf), - - /// A file is being hovered over the window. - /// - /// When the user hovers multiple files at once, this event will be emitted for each file - /// separately. - HoveredFile(PathBuf), - - /// A file was hovered, but has exited the window. - /// - /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were - /// hovered. - HoveredFileCancelled, - - /// The window gained or lost focus. - /// - /// The parameter is true if the window has gained focus, and false if it has lost focus. - Focused(bool), - - /// An event from the keyboard has been received. - /// - /// ## Platform-specific - /// - **Windows:** The shift key overrides NumLock. In other words, while shift is held down, - /// numpad keys act as if NumLock wasn't active. When this is used, the OS sends fake key - /// events which are not marked as `is_synthetic`. - KeyboardInput { - device_id: DeviceId, - event: KeyEvent, - - /// If `true`, the event was generated synthetically by winit - /// in one of the following circumstances: - /// - /// * Synthetic key press events are generated for all keys pressed - /// when a window gains focus. Likewise, synthetic key release events - /// are generated for all keys pressed when a window goes out of focus. - /// ***Currently, this is only functional on X11 and Windows*** - /// - /// Otherwise, this value is always `false`. - is_synthetic: bool, - }, - - /// The keyboard modifiers have changed. - ModifiersChanged(Modifiers), - - /// An event from an input method. - /// - /// **Note:** You have to explicitly enable this event using [`Window::set_ime_allowed`]. - /// - /// ## Platform-specific - /// - /// - **iOS / Android / Web / Orbital:** Unsupported. - Ime(Ime), - - /// The cursor has moved on the window. - /// - /// ## Platform-specific - /// - /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. - /// - /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border - /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding - /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform - CursorMoved { - device_id: DeviceId, - - /// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is - /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor - /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. - position: PhysicalPosition, - }, - - /// The cursor has entered the window. - /// - /// ## Platform-specific - /// - /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. - /// - /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border - /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding - /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform - CursorEntered { device_id: DeviceId }, - - /// The cursor has left the window. - /// - /// ## Platform-specific - /// - /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. - /// - /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border - /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding - /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform - CursorLeft { device_id: DeviceId }, - - /// A mouse wheel movement or touchpad scroll occurred. - MouseWheel { - device_id: DeviceId, - delta: MouseScrollDelta, - phase: TouchPhase, - }, - - /// An mouse button press has been received. - MouseInput { - device_id: DeviceId, - state: ElementState, - button: MouseButton, - }, - - /// Two-finger pinch gesture, often used for magnification. - /// - /// ## Platform-specific - /// - /// - Only available on **macOS** and **iOS**. - /// - On iOS, not recognized by default. It must be enabled when needed. - PinchGesture { - device_id: DeviceId, - /// Positive values indicate magnification (zooming in) and negative - /// values indicate shrinking (zooming out). - /// - /// This value may be NaN. - delta: f64, - phase: TouchPhase, - }, - - /// Double tap gesture. - /// - /// On a Mac, smart magnification is triggered by a double tap with two fingers - /// on the trackpad and is commonly used to zoom on a certain object - /// (e.g. a paragraph of a PDF) or (sort of like a toggle) to reset any zoom. - /// The gesture is also supported in Safari, Pages, etc. - /// - /// The event is general enough that its generating gesture is allowed to vary - /// across platforms. It could also be generated by another device. - /// - /// Unfortunatly, neither [Windows](https://support.microsoft.com/en-us/windows/touch-gestures-for-windows-a9d28305-4818-a5df-4e2b-e5590f850741) - /// nor [Wayland](https://wayland.freedesktop.org/libinput/doc/latest/gestures.html) - /// support this gesture or any other gesture with the same effect. - /// - /// ## Platform-specific - /// - /// - Only available on **macOS 10.8** and later, and **iOS**. - /// - On iOS, not recognized by default. It must be enabled when needed. - DoubleTapGesture { device_id: DeviceId }, - - /// Two-finger rotation gesture. - /// - /// Positive delta values indicate rotation counterclockwise and - /// negative delta values indicate rotation clockwise. - /// - /// ## Platform-specific - /// - /// - Only available on **macOS** and **iOS**. - /// - On iOS, not recognized by default. It must be enabled when needed. - RotationGesture { - device_id: DeviceId, - delta: f32, - phase: TouchPhase, - }, - - /// Touchpad pressure event. - /// - /// At the moment, only supported on Apple forcetouch-capable macbooks. - /// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad - /// is being pressed) and stage (integer representing the click level). - TouchpadPressure { - device_id: DeviceId, - pressure: f32, - stage: i64, - }, - - /// Motion on some analog axis. May report data redundant to other, more specific events. - AxisMotion { - device_id: DeviceId, - axis: AxisId, - value: f64, - }, - - /// Touch event has been received - /// - /// ## Platform-specific - /// - /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. - /// - **macOS:** Unsupported. - /// - /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border - /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding - /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform - Touch(Touch), - - /// The window's scale factor has changed. - /// - /// The following user actions can cause DPI changes: - /// - /// * Changing the display's resolution. - /// * Changing the display's scale factor (e.g. in Control Panel on Windows). - /// * Moving the window to a display with a different scale factor. - /// - /// After this event callback has been processed, the window will be resized to whatever value - /// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested - /// by the OS, but it can be changed to any value. - /// - /// For more information about DPI in general, see the [`dpi`](crate::dpi) module. - ScaleFactorChanged { - scale_factor: f64, - /// Handle to update inner size during scale changes. - /// - /// See [`InnerSizeWriter`] docs for more details. - inner_size_writer: InnerSizeWriter, - }, - - /// The system window theme has changed. - /// - /// Applications might wish to react to this to change the theme of the content of the window - /// when the system changes the window theme. - /// - /// ## Platform-specific - /// - /// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported. - ThemeChanged(Theme), - - /// The window has been occluded (completely hidden from view). - /// - /// This is different to window visibility as it depends on whether the window is closed, - /// minimised, set invisible, or fully occluded by another window. - /// - /// ## Platform-specific - /// - /// ### iOS - /// - /// On iOS, the `Occluded(false)` event is emitted in response to an [`applicationWillEnterForeground`] - /// callback which means the application should start preparing its data. The `Occluded(true)` event is - /// emitted in response to an [`applicationDidEnterBackground`] callback which means the application - /// should free resources (according to the [iOS application lifecycle]). - /// - /// [`applicationWillEnterForeground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground - /// [`applicationDidEnterBackground`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground - /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle - /// - /// ### Others - /// - /// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. - /// - **Android / Wayland / Windows / Orbital:** Unsupported. - /// - /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border - /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding - /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform - Occluded(bool), - - /// Emitted when a window should be redrawn. - /// - /// This gets triggered in two scenarios: - /// - The OS has performed an operation that's invalidated the window's contents (such as - /// resizing the window). - /// - The application has explicitly requested a redraw via [`Window::request_redraw`]. - /// - /// Winit will aggregate duplicate redraw requests into a single event, to - /// help avoid duplicating rendering work. - RedrawRequested, -} - -/// Represents raw hardware events that are not associated with any particular window. -/// -/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person -/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because -/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs -/// may not match. -/// -/// Note that these events are delivered regardless of input focus. -#[derive(Clone, Debug, PartialEq)] -pub enum DeviceEvent { - Added, - Removed, - - /// Change in physical position of a pointing device. - /// - /// This represents raw, unfiltered physical motion. Not to be confused with [`WindowEvent::CursorMoved`]. - MouseMotion { - /// (x, y) change in position in unspecified units. - /// - /// Different devices may use different units. - delta: (f64, f64), - }, - - /// Physical scroll event - MouseWheel { - delta: MouseScrollDelta, - }, - - /// Motion on some analog axis. This event will be reported for all arbitrary input devices - /// that winit supports on this platform, including mouse devices. If the device is a mouse - /// device then this will be reported alongside the MouseMotion event. - Motion { - axis: AxisId, - value: f64, - }, - - Button { - button: ButtonId, - state: ElementState, - }, - - Key(RawKeyEvent), -} - -/// Describes a keyboard input as a raw device event. -/// -/// Note that holding down a key may produce repeated `RawKeyEvent`s. The -/// operating system doesn't provide information whether such an event is a -/// repeat or the initial keypress. An application may emulate this by, for -/// example keeping a Map/Set of pressed keys and determining whether a keypress -/// corresponds to an already pressed key. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct RawKeyEvent { - pub physical_key: keyboard::PhysicalKey, - pub state: ElementState, -} - -/// Describes a keyboard input targeting a window. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct KeyEvent { - /// Represents the position of a key independent of the currently active layout. - /// - /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). - /// The most prevalent use case for this is games. For example the default keys for the player - /// to move around might be the W, A, S, and D keys on a US layout. The position of these keys - /// is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY" - /// layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.) - /// - /// ## Caveats - /// - /// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that - /// implements DVORAK in hardware (or firmware) - /// - Your application will likely have to handle keyboards which are missing keys that your - /// own keyboard has. - /// - Certain `KeyCode`s will move between a couple of different positions depending on what - /// layout the keyboard was manufactured to support. - /// - /// **Because of these caveats, it is important that you provide users with a way to configure - /// most (if not all) keybinds in your application.** - /// - /// ## `Fn` and `FnLock` - /// - /// `Fn` and `FnLock` key events are *exceedingly unlikely* to be emitted by Winit. These keys - /// are usually handled at the hardware or OS level, and aren't surfaced to applications. If - /// you somehow see this in the wild, we'd like to know :) - pub physical_key: keyboard::PhysicalKey, - - // Allowing `broken_intra_doc_links` for `logical_key`, because - // `key_without_modifiers` is not available on all platforms - #[cfg_attr( - not(any(windows_platform, macos_platform, x11_platform, wayland_platform)), - allow(rustdoc::broken_intra_doc_links) - )] - /// This value is affected by all modifiers except Ctrl. - /// - /// This has two use cases: - /// - Allows querying whether the current input is a Dead key. - /// - Allows handling key-bindings on platforms which don't - /// support [`key_without_modifiers`]. - /// - /// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard - /// shortcuts, **it is important that you provide users with a way to configure your - /// application's shortcuts so you don't render your application unusable for users with an - /// incompatible keyboard layout.** - /// - /// ## Platform-specific - /// - **Web:** Dead keys might be reported as the real key instead - /// of `Dead` depending on the browser/OS. - /// - /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers - pub logical_key: keyboard::Key, - - /// Contains the text produced by this keypress. - /// - /// In most cases this is identical to the content - /// of the `Character` variant of `logical_key`. - /// However, on Windows when a dead key was pressed earlier - /// but cannot be combined with the character from this - /// keypress, the produced text will consist of two characters: - /// the dead-key-character followed by the character resulting - /// from this keypress. - /// - /// An additional difference from `logical_key` is that - /// this field stores the text representation of any key - /// that has such a representation. For example when - /// `logical_key` is `Key::Named(NamedKey::Enter)`, this field is `Some("\r")`. - /// - /// This is `None` if the current keypress cannot - /// be interpreted as text. - /// - /// See also: `text_with_all_modifiers()` - pub text: Option, - - /// Contains the location of this key on the keyboard. - /// - /// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" key - /// appears on the left side of the QWERTY keyboard as well as the right side. However, both keys - /// have the same symbolic value. Another example of this phenomenon is the "1" key, which appears - /// both above the "Q" key and as the "Keypad 1" key. - /// - /// This field allows the user to differentiate between keys like this that have the same symbolic - /// value but different locations on the keyboard. - /// - /// See the [`KeyLocation`] type for more details. - /// - /// [`KeyLocation`]: crate::keyboard::KeyLocation - pub location: keyboard::KeyLocation, - - /// Whether the key is being pressed or released. - /// - /// See the [`ElementState`] type for more details. - pub state: ElementState, - - /// Whether or not this key is a key repeat event. - /// - /// On some systems, holding down a key for some period of time causes that key to be repeated - /// as though it were being pressed and released repeatedly. This field is `true` if and only if - /// this event is the result of one of those repeats. - pub repeat: bool, - - /// Platform-specific key event information. - /// - /// On Windows, Linux and macOS, this type contains the key without modifiers and the text with all - /// modifiers applied. - /// - /// On Android, iOS, Redox and Web, this type is a no-op. - pub(crate) platform_specific: platform_impl::KeyEventExtra, -} - -/// Describes keyboard modifiers event. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub struct Modifiers { - pub(crate) state: ModifiersState, - - // NOTE: Currently pressed modifiers keys. - // - // The field providing a metadata, it shouldn't be used as a source of truth. - pub(crate) pressed_mods: ModifiersKeys, -} - -impl Modifiers { - /// The state of the modifiers. - pub fn state(&self) -> ModifiersState { - self.state - } - - /// The state of the left shift key. - pub fn lshift_state(&self) -> ModifiersKeyState { - self.mod_state(ModifiersKeys::LSHIFT) - } - - /// The state of the right shift key. - pub fn rshift_state(&self) -> ModifiersKeyState { - self.mod_state(ModifiersKeys::RSHIFT) - } - - /// The state of the left alt key. - pub fn lalt_state(&self) -> ModifiersKeyState { - self.mod_state(ModifiersKeys::LALT) - } - - /// The state of the right alt key. - pub fn ralt_state(&self) -> ModifiersKeyState { - self.mod_state(ModifiersKeys::RALT) - } - - /// The state of the left control key. - pub fn lcontrol_state(&self) -> ModifiersKeyState { - self.mod_state(ModifiersKeys::LCONTROL) - } - - /// The state of the right control key. - pub fn rcontrol_state(&self) -> ModifiersKeyState { - self.mod_state(ModifiersKeys::RCONTROL) - } - - /// The state of the left super key. - pub fn lsuper_state(&self) -> ModifiersKeyState { - self.mod_state(ModifiersKeys::LSUPER) - } - - /// The state of the right super key. - pub fn rsuper_state(&self) -> ModifiersKeyState { - self.mod_state(ModifiersKeys::RSUPER) - } - - fn mod_state(&self, modifier: ModifiersKeys) -> ModifiersKeyState { - if self.pressed_mods.contains(modifier) { - ModifiersKeyState::Pressed - } else { - ModifiersKeyState::Unknown - } - } -} - -impl From for Modifiers { - fn from(value: ModifiersState) -> Self { - Self { - state: value, - pressed_mods: Default::default(), - } - } -} - -/// Describes [input method](https://en.wikipedia.org/wiki/Input_method) events. -/// -/// This is also called a "composition event". -/// -/// Most keypresses using a latin-like keyboard layout simply generate a [`WindowEvent::KeyboardInput`]. -/// However, one couldn't possibly have a key for every single unicode character that the user might want to type -/// - so the solution operating systems employ is to allow the user to type these using _a sequence of keypresses_ instead. -/// -/// A prominent example of this is accents - many keyboard layouts allow you to first click the "accent key", and then -/// the character you want to apply the accent to. In this case, some platforms will generate the following event sequence: -/// ```ignore -/// // Press "`" key -/// Ime::Preedit("`", Some((0, 0))) -/// // Press "E" key -/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. -/// Ime::Commit("é") -/// ``` -/// -/// Additionally, certain input devices are configured to display a candidate box that allow the user to select the -/// desired character interactively. (To properly position this box, you must use [`Window::set_ime_cursor_area`].) -/// -/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the following event -/// sequence could be obtained: -/// ```ignore -/// // Press "A" key -/// Ime::Preedit("a", Some((1, 1))) -/// // Press "B" key -/// Ime::Preedit("a b", Some((3, 3))) -/// // Press left arrow key -/// Ime::Preedit("a b", Some((1, 1))) -/// // Press space key -/// Ime::Preedit("啊b", Some((3, 3))) -/// // Press space key -/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit. -/// Ime::Commit("啊不") -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Ime { - /// Notifies when the IME was enabled. - /// - /// After getting this event you could receive [`Preedit`](Self::Preedit) and - /// [`Commit`](Self::Commit) events. You should also start performing IME related requests - /// like [`Window::set_ime_cursor_area`]. - Enabled, - - /// Notifies when a new composing text should be set at the cursor position. - /// - /// The value represents a pair of the preedit string and the cursor begin position and end - /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string - /// this indicates that preedit was cleared. - /// - /// The cursor position is byte-wise indexed. - Preedit(String, Option<(usize, usize)>), - - /// Notifies when text should be inserted into the editor widget. - /// - /// Right before this event winit will send empty [`Self::Preedit`] event. - Commit(String), - - /// Notifies when the IME was disabled. - /// - /// After receiving this event you won't get any more [`Preedit`](Self::Preedit) or - /// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You should - /// also stop issuing IME related requests like [`Window::set_ime_cursor_area`] and clear pending - /// preedit text. - Disabled, -} - -/// Describes touch-screen input state. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum TouchPhase { - Started, - Moved, - Ended, - Cancelled, -} - -/// Represents a touch event -/// -/// Every time the user touches the screen, a new [`TouchPhase::Started`] event with an unique -/// identifier for the finger is generated. When the finger is lifted, an [`TouchPhase::Ended`] -/// event is generated with the same finger id. -/// -/// After a `Started` event has been emitted, there may be zero or more `Move` -/// events when the finger is moved or the touch pressure changes. -/// -/// The finger id may be reused by the system after an `Ended` event. The user -/// should assume that a new `Started` event received with the same id has nothing -/// to do with the old finger and is a new finger. -/// -/// A [`TouchPhase::Cancelled`] event is emitted when the system has canceled tracking this -/// touch, such as when the window loses focus, or on iOS if the user moves the -/// device against their face. -/// -/// ## Platform-specific -/// -/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. -/// - **macOS:** Unsupported. -/// -/// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border -/// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding -/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Touch { - pub device_id: DeviceId, - pub phase: TouchPhase, - pub location: PhysicalPosition, - /// Describes how hard the screen was pressed. May be `None` if the platform - /// does not support pressure sensitivity. - /// - /// ## Platform-specific - /// - /// - Only available on **iOS** 9.0+, **Windows** 8+, **Web**, and **Android**. - /// - **Android**: This will never be [None]. If the device doesn't support pressure - /// sensitivity, force will either be 0.0 or 1.0. Also see the - /// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE). - pub force: Option, - /// Unique identifier of a finger. - pub id: u64, -} - -/// Describes the force of a touch event -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Force { - /// On iOS, the force is calibrated so that the same number corresponds to - /// roughly the same amount of pressure on the screen regardless of the - /// device. - Calibrated { - /// The force of the touch, where a value of 1.0 represents the force of - /// an average touch (predetermined by the system, not user-specific). - /// - /// The force reported by Apple Pencil is measured along the axis of the - /// pencil. If you want a force perpendicular to the device, you need to - /// calculate this value using the `altitude_angle` value. - force: f64, - /// The maximum possible force for a touch. - /// - /// The value of this field is sufficiently high to provide a wide - /// dynamic range for values of the `force` field. - max_possible_force: f64, - /// The altitude (in radians) of the stylus. - /// - /// A value of 0 radians indicates that the stylus is parallel to the - /// surface. The value of this property is Pi/2 when the stylus is - /// perpendicular to the surface. - altitude_angle: Option, - }, - /// If the platform reports the force as normalized, we have no way of - /// knowing how much pressure 1.0 corresponds to – we know it's the maximum - /// amount of force, but as to how much force, you might either have to - /// press really really hard, or not hard at all, depending on the device. - Normalized(f64), -} - -impl Force { - /// Returns the force normalized to the range between 0.0 and 1.0 inclusive. - /// - /// Instead of normalizing the force, you should prefer to handle - /// [`Force::Calibrated`] so that the amount of force the user has to apply is - /// consistent across devices. - pub fn normalized(&self) -> f64 { - match self { - Force::Calibrated { - force, - max_possible_force, - altitude_angle, - } => { - let force = match altitude_angle { - Some(altitude_angle) => force / altitude_angle.sin(), - None => *force, - }; - force / max_possible_force - } - Force::Normalized(force) => *force, - } - } -} - -/// Identifier for a specific analog axis on some device. -pub type AxisId = u32; - -/// Identifier for a specific button on some device. -pub type ButtonId = u32; - -/// Describes the input state of a key. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum ElementState { - Pressed, - Released, -} - -impl ElementState { - /// True if `self == Pressed`. - pub fn is_pressed(self) -> bool { - self == ElementState::Pressed - } -} - -/// Describes a button of a mouse controller. -/// -/// ## Platform-specific -/// -/// **macOS:** `Back` and `Forward` might not work with all hardware. -/// **Orbital:** `Back` and `Forward` are unsupported due to orbital not supporting them. -#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MouseButton { - Left, - Right, - Middle, - Back, - Forward, - Other(u16), -} - -/// Describes a difference in the mouse scroll wheel state. -#[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MouseScrollDelta { - /// Amount in lines or rows to scroll in the horizontal - /// and vertical directions. - /// - /// Positive values indicate that the content that is being scrolled should move - /// right and down (revealing more content left and up). - LineDelta(f32, f32), - - /// Amount in pixels to scroll in the horizontal and - /// vertical direction. - /// - /// Scroll events are expressed as a `PixelDelta` if - /// supported by the device (eg. a touchpad) and - /// platform. - /// - /// Positive values indicate that the content being scrolled should - /// move right/down. - /// - /// For a 'natural scrolling' touch pad (that acts like a touch screen) - /// this means moving your fingers right and down should give positive values, - /// and move the content right and down (to reveal more things left and up). - PixelDelta(PhysicalPosition), -} - -/// Handle to synchroniously change the size of the window from the -/// [`WindowEvent`]. +/// Extra keyboard information. #[derive(Debug, Clone)] -pub struct InnerSizeWriter { - pub(crate) new_inner_size: Weak>>, -} - -impl InnerSizeWriter { - #[cfg(not(orbital_platform))] - pub(crate) fn new(new_inner_size: Weak>>) -> Self { - Self { new_inner_size } - } - - /// Try to request inner size which will be set synchroniously on the window. - pub fn request_inner_size( - &mut self, - new_inner_size: PhysicalSize, - ) -> Result<(), ExternalError> { - if let Some(inner) = self.new_inner_size.upgrade() { - *inner.lock().unwrap() = new_inner_size; - Ok(()) - } else { - Err(ExternalError::Ignored) - } - } -} - -impl PartialEq for InnerSizeWriter { - fn eq(&self, other: &Self) -> bool { - self.new_inner_size.as_ptr() == other.new_inner_size.as_ptr() - } -} - -#[cfg(test)] -mod tests { - use crate::event; - use std::collections::{BTreeSet, HashSet}; - - macro_rules! foreach_event { - ($closure:expr) => {{ - #[allow(unused_mut)] - let mut x = $closure; - let did = unsafe { event::DeviceId::dummy() }; - - #[allow(deprecated)] - { - use crate::event::{Event::*, Ime::Enabled, WindowEvent::*}; - use crate::window::WindowId; - - // Mainline events. - let wid = unsafe { WindowId::dummy() }; - x(UserEvent(())); - x(NewEvents(event::StartCause::Init)); - x(AboutToWait); - x(LoopExiting); - x(Suspended); - x(Resumed); - - // Window events. - let with_window_event = |wev| { - x(WindowEvent { - window_id: wid, - event: wev, - }) - }; - - with_window_event(CloseRequested); - with_window_event(Destroyed); - with_window_event(Focused(true)); - with_window_event(Moved((0, 0).into())); - with_window_event(Resized((0, 0).into())); - with_window_event(DroppedFile("x.txt".into())); - with_window_event(HoveredFile("x.txt".into())); - with_window_event(HoveredFileCancelled); - with_window_event(Ime(Enabled)); - with_window_event(CursorMoved { - device_id: did, - position: (0, 0).into(), - }); - with_window_event(ModifiersChanged(event::Modifiers::default())); - with_window_event(CursorEntered { device_id: did }); - with_window_event(CursorLeft { device_id: did }); - with_window_event(MouseWheel { - device_id: did, - delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), - phase: event::TouchPhase::Started, - }); - with_window_event(MouseInput { - device_id: did, - state: event::ElementState::Pressed, - button: event::MouseButton::Other(0), - }); - with_window_event(PinchGesture { - device_id: did, - delta: 0.0, - phase: event::TouchPhase::Started, - }); - with_window_event(DoubleTapGesture { device_id: did }); - with_window_event(RotationGesture { - device_id: did, - delta: 0.0, - phase: event::TouchPhase::Started, - }); - with_window_event(TouchpadPressure { - device_id: did, - pressure: 0.0, - stage: 0, - }); - with_window_event(AxisMotion { - device_id: did, - axis: 0, - value: 0.0, - }); - with_window_event(Touch(event::Touch { - device_id: did, - phase: event::TouchPhase::Started, - location: (0.0, 0.0).into(), - id: 0, - force: Some(event::Force::Normalized(0.0)), - })); - with_window_event(ThemeChanged(crate::window::Theme::Light)); - with_window_event(Occluded(true)); - } - - #[allow(deprecated)] - { - use event::DeviceEvent::*; - - let with_device_event = |dev_ev| { - x(event::Event::DeviceEvent { - device_id: did, - event: dev_ev, - }) - }; - - with_device_event(Added); - with_device_event(Removed); - with_device_event(MouseMotion { - delta: (0.0, 0.0).into(), - }); - with_device_event(MouseWheel { - delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), - }); - with_device_event(Motion { - axis: 0, - value: 0.0, - }); - with_device_event(Button { - button: 0, - state: event::ElementState::Pressed, - }); - } - }}; - } - - #[allow(clippy::redundant_clone)] - #[test] - fn test_event_clone() { - foreach_event!(|event: event::Event<()>| { - let event2 = event.clone(); - assert_eq!(event, event2); - }) - } - - #[test] - fn test_map_nonuser_event() { - foreach_event!(|event: event::Event<()>| { - let is_user = matches!(event, event::Event::UserEvent(())); - let event2 = event.map_nonuser_event::<()>(); - if is_user { - assert_eq!(event2, Err(event::Event::UserEvent(()))); - } else { - assert!(event2.is_ok()); - } - }) - } - - #[test] - fn test_force_normalize() { - let force = event::Force::Normalized(0.0); - assert_eq!(force.normalized(), 0.0); - - let force2 = event::Force::Calibrated { - force: 5.0, - max_possible_force: 2.5, - altitude_angle: None, - }; - assert_eq!(force2.normalized(), 2.0); - - let force3 = event::Force::Calibrated { - force: 5.0, - max_possible_force: 2.5, - altitude_angle: Some(std::f64::consts::PI / 2.0), - }; - assert_eq!(force3.normalized(), 2.0); - } - - #[allow(clippy::clone_on_copy)] - #[test] - fn ensure_attrs_do_not_panic() { - foreach_event!(|event: event::Event<()>| { - let _ = format!("{:?}", event); - }); - let _ = event::StartCause::Init.clone(); - - let did = unsafe { crate::event::DeviceId::dummy() }.clone(); - HashSet::new().insert(did); - let mut set = [did, did, did]; - set.sort_unstable(); - let mut set2 = BTreeSet::new(); - set2.insert(did); - set2.insert(did); - - HashSet::new().insert(event::TouchPhase::Started.clone()); - HashSet::new().insert(event::MouseButton::Left.clone()); - HashSet::new().insert(event::Ime::Enabled); - - let _ = event::Touch { - device_id: did, - phase: event::TouchPhase::Started, - location: (0.0, 0.0).into(), - id: 0, - force: Some(event::Force::Normalized(0.0)), - } - .clone(); - let _ = event::Force::Calibrated { - force: 0.0, - max_possible_force: 0.0, - altitude_angle: None, - } - .clone(); - } +pub struct KeyExtra { + #[allow(dead_code)] + pub(crate) extra: platform_impl::KeyEventExtra, } diff --git a/winit/src/event_loop.rs b/winit/src/event_loop.rs index 7f16dbbab..7cb6a5a24 100644 --- a/winit/src/event_loop.rs +++ b/winit/src/event_loop.rs @@ -11,7 +11,7 @@ use std::marker::PhantomData; use std::ops::Deref; #[cfg(any(x11_platform, wayland_platform))] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; -use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::{error, fmt}; #[cfg(not(web_platform))] @@ -22,6 +22,9 @@ use web_time::{Duration, Instant}; use crate::error::EventLoopError; use crate::{event::Event, monitor::MonitorHandle, platform_impl}; +#[doc(inline)] +pub use winit_core::event_loop::AsyncRequestSerial; + /// Provides a way to retrieve events from the system and from the windows that were registered to /// the events loop. /// @@ -506,29 +509,3 @@ pub enum DeviceEvents { /// Never capture device events. Never, } - -/// A unique identifier of the winit's async request. -/// -/// This could be used to identify the async request once it's done -/// and a specific action must be taken. -/// -/// One of the handling scenarious could be to maintain a working list -/// containing [`AsyncRequestSerial`] and some closure associated with it. -/// Then once event is arriving the working list is being traversed and a job -/// executed and removed from the list. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct AsyncRequestSerial { - serial: u64, -} - -impl AsyncRequestSerial { - // TODO(kchibisov) remove `cfg` when the clipboard will be added. - #[allow(dead_code)] - pub(crate) fn get() -> Self { - static CURRENT_SERIAL: AtomicU64 = AtomicU64::new(0); - // NOTE: we rely on wrap around here, while the user may just request - // in the loop u64::MAX times that's issue is considered on them. - let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed); - Self { serial } - } -} diff --git a/winit/src/keyboard.rs b/winit/src/keyboard.rs index 369a84750..7a743b2a9 100644 --- a/winit/src/keyboard.rs +++ b/winit/src/keyboard.rs @@ -2,27 +2,3 @@ #[doc(inline)] pub use winit_core::keyboard::*; - -use bitflags::bitflags; - -// NOTE: the exact modifier key is not used to represent modifiers state in the -// first place due to a fact that modifiers state could be changed without any -// key being pressed and on some platforms like Wayland/X11 which key resulted -// in modifiers change is hidden, also, not that it really matters. -// -// The reason this API is even exposed is mostly to provide a way for users -// to treat modifiers differently based on their position, which is required -// on macOS due to their AltGr/Option situation. -bitflags! { - #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub(crate) struct ModifiersKeys: u8 { - const LSHIFT = 0b0000_0001; - const RSHIFT = 0b0000_0010; - const LCONTROL = 0b0000_0100; - const RCONTROL = 0b0000_1000; - const LALT = 0b0001_0000; - const RALT = 0b0010_0000; - const LSUPER = 0b0100_0000; - const RSUPER = 0b1000_0000; - } -} diff --git a/winit/src/platform/modifier_supplement.rs b/winit/src/platform/modifier_supplement.rs index 651ba7f3a..664db7c39 100644 --- a/winit/src/platform/modifier_supplement.rs +++ b/winit/src/platform/modifier_supplement.rs @@ -25,7 +25,8 @@ pub trait KeyEventExtModifierSupplement { impl KeyEventExtModifierSupplement for KeyEvent { #[inline] fn text_with_all_modifiers(&self) -> Option<&str> { - self.platform_specific + self.extra + .extra .text_with_all_modifiers .as_ref() .map(|s| s.as_str()) @@ -33,6 +34,6 @@ impl KeyEventExtModifierSupplement for KeyEvent { #[inline] fn key_without_modifiers(&self) -> Key { - self.platform_specific.key_without_modifiers.clone() + self.extra.extra.key_without_modifiers.clone() } } diff --git a/winit/src/platform/startup_notify.rs b/winit/src/platform/startup_notify.rs index 0fc713475..a412ffb41 100644 --- a/winit/src/platform/startup_notify.rs +++ b/winit/src/platform/startup_notify.rs @@ -64,7 +64,7 @@ impl EventLoopExtStartupNotify for EventLoopWindowTarget { crate::platform_impl::EventLoopWindowTarget::X(_) => env::var(X11_VAR), } .ok() - .map(ActivationToken::_new) + .map(ActivationToken::new) } } @@ -94,6 +94,6 @@ pub fn reset_activation_token_env() { /// /// This could be used before running daemon processes. pub fn set_activation_token_env(token: ActivationToken) { - env::set_var(X11_VAR, &token._token); - env::set_var(WAYLAND_VAR, token._token); + env::set_var(X11_VAR, token.token()); + env::set_var(WAYLAND_VAR, token.token()); } diff --git a/winit/src/platform_impl/android/mod.rs b/winit/src/platform_impl/android/mod.rs index 4d270ab30..cbb2965c1 100644 --- a/winit/src/platform_impl/android/mod.rs +++ b/winit/src/platform_impl/android/mod.rs @@ -465,7 +465,9 @@ impl EventLoop { location: keycodes::to_location(keycode), repeat: key.repeat_count() > 0, text: None, - platform_specific: KeyEventExtra {}, + extra: event::KeyExtra { + extra: KeyEventExtra {}, + }, }, is_synthetic: false, }, diff --git a/winit/src/platform_impl/ios/app_state.rs b/winit/src/platform_impl/ios/app_state.rs index 26bbf85d5..d874304d9 100644 --- a/winit/src/platform_impl/ios/app_state.rs +++ b/winit/src/platform_impl/ios/app_state.rs @@ -92,16 +92,14 @@ enum UserCallbackTransitionResult<'a> { }, } -impl Event { - fn is_redraw(&self) -> bool { - matches!( - self, - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } - ) - } +fn is_redraw(event: &Event) -> bool { + matches!( + event, + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } + ) } // this is the state machine for the app lifecycle @@ -621,9 +619,9 @@ pub(crate) fn handle_nonuser_events>( for wrapper in events { match wrapper { EventWrapper::StaticEvent(event) => { - if !processing_redraws && event.is_redraw() { + if !processing_redraws && is_redraw(&event) { log::info!("processing `RedrawRequested` during the main event loop"); - } else if processing_redraws && !event.is_redraw() { + } else if processing_redraws && !is_redraw(&event) { log::warn!( "processing non `RedrawRequested` event after the main event loop: {:#?}", event @@ -675,9 +673,9 @@ pub(crate) fn handle_nonuser_events>( for wrapper in queued_events { match wrapper { EventWrapper::StaticEvent(event) => { - if !processing_redraws && event.is_redraw() { + if !processing_redraws && is_redraw(&event) { log::info!("processing `RedrawRequested` during the main event loop"); - } else if processing_redraws && !event.is_redraw() { + } else if processing_redraws && !is_redraw(&event) { log::warn!( "processing non-`RedrawRequested` event after the main event loop: {:#?}", event diff --git a/winit/src/platform_impl/linux/common/xkb_state.rs b/winit/src/platform_impl/linux/common/xkb_state.rs index 1f926b3ce..dcc9d848e 100644 --- a/winit/src/platform_impl/linux/common/xkb_state.rs +++ b/winit/src/platform_impl/linux/common/xkb_state.rs @@ -409,7 +409,9 @@ impl KbdState { location, state, repeat, - platform_specific, + extra: crate::event::KeyExtra { + extra: platform_specific, + }, } } diff --git a/winit/src/platform_impl/linux/wayland/event_loop/sink.rs b/winit/src/platform_impl/linux/wayland/event_loop/sink.rs index 14eb475d2..f24e66c38 100644 --- a/winit/src/platform_impl/linux/wayland/event_loop/sink.rs +++ b/winit/src/platform_impl/linux/wayland/event_loop/sink.rs @@ -26,10 +26,8 @@ impl EventSink { /// Add new device event to a queue. #[inline] pub fn push_device_event(&mut self, event: DeviceEvent, device_id: DeviceId) { - self.window_events.push(Event::DeviceEvent { - event, - device_id, - }); + self.window_events + .push(Event::DeviceEvent { event, device_id }); } /// Add new window event to a queue. diff --git a/winit/src/platform_impl/linux/wayland/seat/touch/mod.rs b/winit/src/platform_impl/linux/wayland/seat/touch/mod.rs index e55753b8f..fbbde55d8 100644 --- a/winit/src/platform_impl/linux/wayland/seat/touch/mod.rs +++ b/winit/src/platform_impl/linux/wayland/seat/touch/mod.rs @@ -11,8 +11,8 @@ use winit_core::event::DeviceId; use crate::dpi::LogicalPosition; use crate::event::{Touch, TouchPhase, WindowEvent}; -use crate::platform_impl::wayland::state::WinitState; use crate::platform_impl::wayland; +use crate::platform_impl::wayland::state::WinitState; impl TouchHandler for WinitState { fn down( diff --git a/winit/src/platform_impl/linux/wayland/types/xdg_activation.rs b/winit/src/platform_impl/linux/wayland/types/xdg_activation.rs index 852df84b8..fbfa33266 100644 --- a/winit/src/platform_impl/linux/wayland/types/xdg_activation.rs +++ b/winit/src/platform_impl/linux/wayland/types/xdg_activation.rs @@ -82,7 +82,7 @@ impl Dispatch for XdgA state.events_sink.push_window_event( crate::event::WindowEvent::ActivationTokenDone { serial: *serial, - token: ActivationToken::_new(token), + token: ActivationToken::new(token), }, *window_id, ); diff --git a/winit/src/platform_impl/linux/wayland/window/mod.rs b/winit/src/platform_impl/linux/wayland/window/mod.rs index e6472efd5..58900d623 100644 --- a/winit/src/platform_impl/linux/wayland/window/mod.rs +++ b/winit/src/platform_impl/linux/wayland/window/mod.rs @@ -177,7 +177,7 @@ impl Window { xdg_activation.as_ref(), attributes.platform_specific.activation_token, ) { - xdg_activation.activate(token._token, &surface); + xdg_activation.activate(token.into_token(), &surface); } // XXX Do initial commit. diff --git a/winit/src/platform_impl/linux/x11/event_processor.rs b/winit/src/platform_impl/linux/x11/event_processor.rs index a4288a5f5..564a7591f 100644 --- a/winit/src/platform_impl/linux/x11/event_processor.rs +++ b/winit/src/platform_impl/linux/x11/event_processor.rs @@ -17,13 +17,15 @@ use x11rb::{ }; use super::{ - atoms::*, ffi, get_xtarget, mkdid, mkwid, util, CookieResultExt, Device, DeviceInfo, - Dnd, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, + atoms::*, ffi, get_xtarget, mkdid, mkwid, util, CookieResultExt, Device, DeviceInfo, Dnd, + DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, }; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, ElementState, Event, Ime, RawKeyEvent, TouchPhase, WindowEvent, DeviceId}, + event::{ + DeviceEvent, DeviceId, ElementState, Event, Ime, RawKeyEvent, TouchPhase, WindowEvent, + }, event_loop::EventLoopWindowTarget as RootELW, keyboard::ModifiersState, platform_impl::platform::common::{keymap, xkb_state::KbdState}, @@ -719,10 +721,10 @@ impl EventProcessor { MouseButton::{Back, Forward, Left, Middle, Other, Right}, MouseScrollDelta::LineDelta, Touch, - WindowEvent::{ - AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput, - MouseWheel, - }, + }; + use winit_core::event::WindowEvent::{ + AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput, + MouseWheel, }; match xev.evtype { @@ -858,12 +860,11 @@ impl EventProcessor { ) }; let mut devices = self.devices.borrow_mut(); - let physical_device = match devices - .get_mut(&mkdid(xev.sourceid as xinput::DeviceId)) - { - Some(device) => device, - None => return, - }; + let physical_device = + match devices.get_mut(&mkdid(xev.sourceid as xinput::DeviceId)) { + Some(device) => device, + None => return, + }; let mut value = xev.valuators.values; for i in 0..xev.valuators.mask_len * 8 { diff --git a/winit/src/platform_impl/linux/x11/mod.rs b/winit/src/platform_impl/linux/x11/mod.rs index 76a2a6b7a..3da91e8fd 100644 --- a/winit/src/platform_impl/linux/x11/mod.rs +++ b/winit/src/platform_impl/linux/x11/mod.rs @@ -569,7 +569,7 @@ impl EventLoop { window_id, event: crate::event::WindowEvent::ActivationTokenDone { serial, - token: crate::window::ActivationToken::_new(token), + token: crate::window::ActivationToken::new(token), }, }, &self.target, diff --git a/winit/src/platform_impl/linux/x11/window.rs b/winit/src/platform_impl/linux/x11/window.rs index c0d0ae340..3c23006ce 100644 --- a/winit/src/platform_impl/linux/x11/window.rs +++ b/winit/src/platform_impl/linux/x11/window.rs @@ -588,7 +588,7 @@ impl UnownedWindow { // Remove the startup notification if we have one. if let Some(startup) = window_attrs.platform_specific.activation_token.as_ref() { - leap!(xconn.remove_activation_token(xwindow, &startup._token)); + leap!(xconn.remove_activation_token(xwindow, startup.token())); } // We never want to give the user a broken window, since by then, it's too late to handle. diff --git a/winit/src/platform_impl/macos/event.rs b/winit/src/platform_impl/macos/event.rs index ff863f38f..c4a421d2e 100644 --- a/winit/src/platform_impl/macos/event.rs +++ b/winit/src/platform_impl/macos/event.rs @@ -188,9 +188,11 @@ pub(crate) fn create_key_event( repeat: is_repeat, state, text, - platform_specific: KeyEventExtra { - text_with_all_modifiers, - key_without_modifiers, + extra: crate::event::KeyExtra { + extra: KeyEventExtra { + text_with_all_modifiers, + key_without_modifiers, + }, }, } } @@ -393,10 +395,7 @@ pub(super) fn event_mods(event: &NSEvent) -> Modifiers { flags_contains(flags, NX_DEVICERCMDKEYMASK), ); - Modifiers { - state, - pressed_mods, - } + Modifiers::new(state, pressed_mods) } pub(super) fn dummy_event() -> Option> { diff --git a/winit/src/platform_impl/macos/view.rs b/winit/src/platform_impl/macos/view.rs index a202e44f6..fdb17c9d5 100644 --- a/winit/src/platform_impl/macos/view.rs +++ b/winit/src/platform_impl/macos/view.rs @@ -1076,7 +1076,7 @@ fn mouse_button(event: &NSEvent) -> MouseButton { // we're getting from the operating system, which makes it // impossible to provide such events as extra in `KeyEvent`. fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Id { - let ev_mods = event_mods(event).state; + let ev_mods = event_mods(event).state(); let ignore_alt_characters = match option_as_alt { OptionAsAlt::OnlyLeft if lalt_pressed(event) => true, OptionAsAlt::OnlyRight if ralt_pressed(event) => true, diff --git a/winit/src/platform_impl/orbital/event_loop.rs b/winit/src/platform_impl/orbital/event_loop.rs index 094b978b1..49aee7a38 100644 --- a/winit/src/platform_impl/orbital/event_loop.rs +++ b/winit/src/platform_impl/orbital/event_loop.rs @@ -25,8 +25,8 @@ use crate::{ }; use super::{ - KeyEventExtra, MonitorHandle, OsError, PlatformSpecificEventLoopAttributes, - RedoxSocket, TimeSocket, WindowProperties, + KeyEventExtra, MonitorHandle, OsError, PlatformSpecificEventLoopAttributes, RedoxSocket, + TimeSocket, WindowProperties, }; fn convert_scancode(scancode: u8) -> PhysicalKey { @@ -265,10 +265,7 @@ impl EventState { self.keyboard.contains(KeyboardModifierState::RSUPER), ); - Modifiers { - state, - pressed_mods, - } + Modifiers::new(state, pressed_mods) } } @@ -353,7 +350,9 @@ impl EventLoop { repeat: false, text: None, - platform_specific: KeyEventExtra {}, + extra: event::KeyExtra { + extra: KeyEventExtra {}, + }, }, is_synthetic: false, }, diff --git a/winit/src/platform_impl/web/event_loop/runner.rs b/winit/src/platform_impl/web/event_loop/runner.rs index c22016838..65581ea64 100644 --- a/winit/src/platform_impl/web/event_loop/runner.rs +++ b/winit/src/platform_impl/web/event_loop/runner.rs @@ -1,9 +1,9 @@ +use super::super::backend::event::mouse_button_to_id; use super::super::main_thread::MainThreadMarker; use super::{backend, state::State}; use crate::dpi::PhysicalSize; use crate::event::{ - DeviceEvent, DeviceId, ElementState, Event, RawKeyEvent, StartCause, - WindowEvent, + DeviceEvent, DeviceId, ElementState, Event, RawKeyEvent, StartCause, WindowEvent, }; use crate::event_loop::{ControlFlow, DeviceEvents}; use crate::platform::web::PollStrategy; @@ -281,7 +281,7 @@ impl Shared { runner.send_event(Event::DeviceEvent { device_id, event: DeviceEvent::Button { - button: button.to_id(), + button: mouse_button_to_id(button), state, }, }); @@ -359,7 +359,7 @@ impl Shared { runner.send_event(Event::DeviceEvent { device_id: DeviceId::from(event.pointer_id() as u64), event: DeviceEvent::Button { - button: button.to_id(), + button: mouse_button_to_id(button), state: ElementState::Pressed, }, }); @@ -382,7 +382,7 @@ impl Shared { runner.send_event(Event::DeviceEvent { device_id: DeviceId::from(event.pointer_id() as u64), event: DeviceEvent::Button { - button: button.to_id(), + button: mouse_button_to_id(button), state: ElementState::Released, }, }); @@ -398,7 +398,7 @@ impl Shared { } runner.send_event(Event::DeviceEvent { - device_id: unsafe { DeviceId::dummy() }, + device_id: unsafe { DeviceId::dummy() }, event: DeviceEvent::Key(RawKeyEvent { physical_key: backend::event::key_code(&event), state: ElementState::Pressed, diff --git a/winit/src/platform_impl/web/event_loop/window_target.rs b/winit/src/platform_impl/web/event_loop/window_target.rs index 24f3c3e35..acd0e9d58 100644 --- a/winit/src/platform_impl/web/event_loop/window_target.rs +++ b/winit/src/platform_impl/web/event_loop/window_target.rs @@ -9,12 +9,9 @@ use web_sys::Element; use super::runner::{EventWrapper, Execution}; use super::{ super::{monitor::MonitorHandle, KeyEventExtra}, - backend, - runner, -}; -use crate::event::{ - DeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, WindowEvent, + backend, runner, }; +use crate::event::{DeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, WindowEvent}; use crate::event_loop::{ControlFlow, DeviceEvents}; use crate::keyboard::ModifiersState; use crate::platform::web::PollStrategy; @@ -151,7 +148,9 @@ impl EventLoopWindowTarget { location, state: ElementState::Pressed, repeat, - platform_specific: KeyEventExtra, + extra: crate::event::KeyExtra { + extra: KeyEventExtra, + }, }, is_synthetic: false, }, @@ -187,7 +186,9 @@ impl EventLoopWindowTarget { location, state: ElementState::Released, repeat, - platform_specific: KeyEventExtra, + extra: crate::event::KeyExtra { + extra: KeyEventExtra, + }, }, is_synthetic: false, }, diff --git a/winit/src/platform_impl/web/web_sys/event.rs b/winit/src/platform_impl/web/web_sys/event.rs index e848c280f..9910369bf 100644 --- a/winit/src/platform_impl/web/web_sys/event.rs +++ b/winit/src/platform_impl/web/web_sys/event.rs @@ -67,16 +67,14 @@ pub fn mouse_button(event: &MouseEvent) -> Option { } } -impl MouseButton { - pub fn to_id(self) -> u32 { - match self { - MouseButton::Left => 0, - MouseButton::Right => 1, - MouseButton::Middle => 2, - MouseButton::Back => 3, - MouseButton::Forward => 4, - MouseButton::Other(value) => value.into(), - } +pub fn mouse_button_to_id(mb: MouseButton) -> u32 { + match mb { + MouseButton::Left => 0, + MouseButton::Right => 1, + MouseButton::Middle => 2, + MouseButton::Back => 3, + MouseButton::Forward => 4, + MouseButton::Other(value) => value.into(), } } diff --git a/winit/src/platform_impl/windows/drop_handler.rs b/winit/src/platform_impl/windows/drop_handler.rs index d665976d3..0ffc81f06 100644 --- a/winit/src/platform_impl/windows/drop_handler.rs +++ b/winit/src/platform_impl/windows/drop_handler.rs @@ -92,7 +92,7 @@ impl FileDropHandler { _pt: *const POINTL, pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::HoveredFile; + use winit_core::event::WindowEvent::HoveredFile; let drop_handler = unsafe { Self::from_interface(this) }; let hdrop = unsafe { Self::iterate_filenames(pDataObj, |filename| { @@ -130,7 +130,7 @@ impl FileDropHandler { } pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { - use crate::event::WindowEvent::HoveredFileCancelled; + use winit_core::event::WindowEvent::HoveredFileCancelled; let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.hovered_is_valid { drop_handler.send_event(Event::WindowEvent { @@ -149,7 +149,7 @@ impl FileDropHandler { _pt: *const POINTL, _pdwEffect: *mut u32, ) -> HRESULT { - use crate::event::WindowEvent::DroppedFile; + use winit_core::event::WindowEvent::DroppedFile; let drop_handler = unsafe { Self::from_interface(this) }; let hdrop = unsafe { Self::iterate_filenames(pDataObj, |filename| { diff --git a/winit/src/platform_impl/windows/event_loop.rs b/winit/src/platform_impl/windows/event_loop.rs index a0eca6c6e..281f3f387 100644 --- a/winit/src/platform_impl/windows/event_loop.rs +++ b/winit/src/platform_impl/windows/event_loop.rs @@ -944,7 +944,7 @@ fn normalize_pointer_pressure(pressure: u32) -> Option { /// Emit a `ModifiersChanged` event whenever modifiers have changed. /// Returns the current modifier state fn update_modifiers(window: HWND, userdata: &WindowData) { - use crate::event::WindowEvent::ModifiersChanged; + use winit_core::event::WindowEvent::ModifiersChanged; let modifiers = { let mut layouts = LAYOUT_CACHE.lock().unwrap(); @@ -966,7 +966,7 @@ fn update_modifiers(window: HWND, userdata: &WindowData) { } unsafe fn gain_active_focus(window: HWND, userdata: &WindowData) { - use crate::event::WindowEvent::Focused; + use winit_core::event::WindowEvent::Focused; update_modifiers(window, userdata); @@ -977,7 +977,7 @@ unsafe fn gain_active_focus(window: HWND, userdata: &WindowData) { } unsafe fn lose_active_focus(window: HWND, userdata: &WindowData) { - use crate::event::WindowEvent::{Focused, ModifiersChanged}; + use winit_core::event::WindowEvent::{Focused, ModifiersChanged}; userdata.window_state_lock().modifiers_state = ModifiersState::empty(); userdata.send_event(Event::WindowEvent { @@ -1080,7 +1080,7 @@ unsafe fn public_window_callback_inner( .unwrap_or_else(|| result = ProcResult::Value(-1)); let keyboard_callback = || { - use crate::event::WindowEvent::KeyboardInput; + use winit_core::event::WindowEvent::KeyboardInput; let events = userdata .key_event_builder @@ -1170,7 +1170,7 @@ unsafe fn public_window_callback_inner( } WM_CLOSE => { - use crate::event::WindowEvent::CloseRequested; + use winit_core::event::WindowEvent::CloseRequested; userdata.send_event(Event::WindowEvent { window_id: WindowId::from(window as u64), event: CloseRequested, @@ -1179,7 +1179,7 @@ unsafe fn public_window_callback_inner( } WM_DESTROY => { - use crate::event::WindowEvent::Destroyed; + use winit_core::event::WindowEvent::Destroyed; unsafe { RevokeDragDrop(window) }; userdata.send_event(Event::WindowEvent { window_id: WindowId::from(window as u64), @@ -1297,7 +1297,7 @@ unsafe fn public_window_callback_inner( // WM_MOVE supplies client area positions, so we send Moved here instead. WM_WINDOWPOSCHANGED => { - use crate::event::WindowEvent::Moved; + use winit_core::event::WindowEvent::Moved; let windowpos = lparam as *const WINDOWPOS; if unsafe { (*windowpos).flags & SWP_NOMOVE != SWP_NOMOVE } { @@ -1314,7 +1314,7 @@ unsafe fn public_window_callback_inner( } WM_SIZE => { - use crate::event::WindowEvent::Resized; + use winit_core::event::WindowEvent::Resized; let w = super::loword(lparam as u32) as u32; let h = super::hiword(lparam as u32) as u32; @@ -1474,7 +1474,7 @@ unsafe fn public_window_callback_inner( } WM_MOUSEMOVE => { - use crate::event::WindowEvent::{CursorEntered, CursorLeft, CursorMoved}; + use winit_core::event::WindowEvent::{CursorEntered, CursorLeft, CursorMoved}; let x = super::get_x_lparam(lparam as u32) as i32; let y = super::get_y_lparam(lparam as u32) as i32; @@ -1550,7 +1550,7 @@ unsafe fn public_window_callback_inner( } WM_MOUSELEAVE => { - use crate::event::WindowEvent::CursorLeft; + use winit_core::event::WindowEvent::CursorLeft; { let mut w = userdata.window_state_lock(); w.mouse @@ -1623,7 +1623,9 @@ unsafe fn public_window_callback_inner( } WM_LBUTTONDOWN => { - use crate::event::{ElementState::Pressed, MouseButton::Left, WindowEvent::MouseInput}; + use winit_core::event::{ + ElementState::Pressed, MouseButton::Left, WindowEvent::MouseInput, + }; unsafe { capture_mouse(window, &mut userdata.window_state_lock()) }; @@ -1641,7 +1643,7 @@ unsafe fn public_window_callback_inner( } WM_LBUTTONUP => { - use crate::event::{ + use winit_core::event::{ ElementState::Released, MouseButton::Left, WindowEvent::MouseInput, }; @@ -1661,7 +1663,7 @@ unsafe fn public_window_callback_inner( } WM_RBUTTONDOWN => { - use crate::event::{ + use winit_core::event::{ ElementState::Pressed, MouseButton::Right, WindowEvent::MouseInput, }; @@ -1681,7 +1683,7 @@ unsafe fn public_window_callback_inner( } WM_RBUTTONUP => { - use crate::event::{ + use winit_core::event::{ ElementState::Released, MouseButton::Right, WindowEvent::MouseInput, }; @@ -1701,7 +1703,7 @@ unsafe fn public_window_callback_inner( } WM_MBUTTONDOWN => { - use crate::event::{ + use winit_core::event::{ ElementState::Pressed, MouseButton::Middle, WindowEvent::MouseInput, }; @@ -1721,7 +1723,7 @@ unsafe fn public_window_callback_inner( } WM_MBUTTONUP => { - use crate::event::{ + use winit_core::event::{ ElementState::Released, MouseButton::Middle, WindowEvent::MouseInput, }; @@ -1741,7 +1743,7 @@ unsafe fn public_window_callback_inner( } WM_XBUTTONDOWN => { - use crate::event::{ + use winit_core::event::{ ElementState::Pressed, MouseButton::Back, MouseButton::Forward, MouseButton::Other, WindowEvent::MouseInput, }; @@ -1767,7 +1769,7 @@ unsafe fn public_window_callback_inner( } WM_XBUTTONUP => { - use crate::event::{ + use winit_core::event::{ ElementState::Released, MouseButton::Back, MouseButton::Forward, MouseButton::Other, WindowEvent::MouseInput, }; @@ -2097,7 +2099,7 @@ unsafe fn public_window_callback_inner( // Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change // DPI, therefore all applications are closed while DPI is changing. WM_DPICHANGED => { - use crate::event::WindowEvent::ScaleFactorChanged; + use winit_core::event::WindowEvent::ScaleFactorChanged; // This message actually provides two DPI values - x and y. However MSDN says that // "you only need to use either the X-axis or the Y-axis value when scaling your @@ -2308,7 +2310,7 @@ unsafe fn public_window_callback_inner( } WM_SETTINGCHANGE => { - use crate::event::WindowEvent::ThemeChanged; + use winit_core::event::WindowEvent::ThemeChanged; let preferred_theme = userdata.window_state_lock().preferred_theme; diff --git a/winit/src/platform_impl/windows/event_loop/runner.rs b/winit/src/platform_impl/windows/event_loop/runner.rs index 504f90cad..05259b97f 100644 --- a/winit/src/platform_impl/windows/event_loop/runner.rs +++ b/winit/src/platform_impl/windows/event_loop/runner.rs @@ -381,16 +381,9 @@ impl BufferedEvent { inner_size_writer, }, window_id, - } => BufferedEvent::ScaleFactorChanged( - window_id, - scale_factor, - *inner_size_writer - .new_inner_size - .upgrade() - .unwrap() - .lock() - .unwrap(), - ), + } => { + BufferedEvent::ScaleFactorChanged(window_id, scale_factor, inner_size_writer.get()) + } event => BufferedEvent::Event(event), } } diff --git a/winit/src/platform_impl/windows/keyboard.rs b/winit/src/platform_impl/windows/keyboard.rs index 05d108ead..789b462ee 100644 --- a/winit/src/platform_impl/windows/keyboard.rs +++ b/winit/src/platform_impl/windows/keyboard.rs @@ -483,7 +483,7 @@ impl KeyEventBuilder { let mut event = event_info.finalize(); event.logical_key = logical_key; - event.platform_specific.text_with_all_modifiers = text; + event.extra.extra.text_with_all_modifiers = text; Some(MessageAsKeyEvent { event, is_synthetic: true, @@ -664,9 +664,11 @@ impl PartialKeyEventInfo { location: self.location, state: self.key_state, repeat: self.is_repeat, - platform_specific: KeyEventExtra { - text_with_all_modifiers: char_with_all_modifiers, - key_without_modifiers: self.key_without_modifiers, + extra: crate::event::KeyExtra { + extra: KeyEventExtra { + text_with_all_modifiers: char_with_all_modifiers, + key_without_modifiers: self.key_without_modifiers, + }, }, } } diff --git a/winit/src/platform_impl/windows/mod.rs b/winit/src/platform_impl/windows/mod.rs index 931a67205..eb792c46f 100644 --- a/winit/src/platform_impl/windows/mod.rs +++ b/winit/src/platform_impl/windows/mod.rs @@ -75,14 +75,14 @@ pub struct Cursor(pub *const u16); unsafe impl Send for Cursor {} unsafe impl Sync for Cursor {} - pub fn persistent_identifier(id: DeviceId) -> Option { - let val: u64 = id.into(); - if val != 0 { - raw_input::get_raw_input_device_name(val as HANDLE) - } else { - None - } +pub fn persistent_identifier(id: DeviceId) -> Option { + let val: u64 = id.into(); + if val != 0 { + raw_input::get_raw_input_device_name(val as HANDLE) + } else { + None } +} // Constant device ID, to be removed when this backend is updated to report real device IDs. const DEVICE_ID: DeviceId = unsafe { DeviceId::dummy() }; diff --git a/winit/src/window.rs b/winit/src/window.rs index bb4e46bf4..7a8d7b763 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -1596,17 +1596,3 @@ pub enum Fullscreen { /// Providing `None` to `Borderless` will fullscreen on the current monitor. Borderless(Option), } - -/// An opaque token used to activate the [`Window`]. -/// -/// [`Window`]: crate::window::Window -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct ActivationToken { - pub(crate) _token: String, -} - -impl ActivationToken { - pub(crate) fn _new(_token: String) -> Self { - Self { _token } - } -}