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 } - } -}