api: overhaul pointer API

- Rename `CursorMoved` to `PointerMoved`.
- Rename `CursorEntered` to `PointerEntered`.
- Rename `CursorLeft` to `PointerLeft`.
- Rename `MouseInput` to `PointerButton`.
- Add `position` to every `PointerEvent`.
- Remove `Touch`, which is folded into the `Pointer*` events.
- New `PointerType` added to `PointerEntered` and `PointerLeft`,
  signifying which pointer type is the source of this event.
- New `PointerSource` added to `PointerMoved`, similar to `PointerType`
  but holding additional data.
- New `ButtonSource` added to `PointerButton`, similar to `PointerType`
  but holding pointer type specific buttons. Use
  `ButtonSource::mouse_button()` to easily normalize any pointer button
  type to a generic mouse button.
- In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`.
- Remove `Force::Calibrated::altitude_angle`.

Fixes #3833.
Fixes #883.
Fixes #336.

Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
daxpedda 2024-10-08 14:19:00 +02:00 committed by GitHub
parent 32cd1ad9a7
commit eccd9e415d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 1236 additions and 869 deletions

View file

@ -44,7 +44,7 @@ fn main() -> Result<(), impl std::error::Error> {
self.windows.clear(); self.windows.clear();
event_loop.exit(); event_loop.exit();
}, },
WindowEvent::CursorEntered { device_id: _ } => { WindowEvent::PointerEntered { device_id: _, .. } => {
// On x11, println when the cursor entered in a window even if the child window // On x11, println when the cursor entered in a window even if the child window
// is created by some key inputs. // is created by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the // the child windows are always placed at (0, 0) with size (200, 200) in the

View file

@ -446,20 +446,23 @@ impl ApplicationHandler for Application {
} }
} }
}, },
WindowEvent::MouseInput { button, state, .. } => { WindowEvent::PointerButton { button, state, .. } => {
info!("Pointer button {button:?} {state:?}");
let mods = window.modifiers; let mods = window.modifiers;
if let Some(action) = if let Some(action) = state
state.is_pressed().then(|| Self::process_mouse_binding(button, &mods)).flatten() .is_pressed()
.then(|| Self::process_mouse_binding(button.mouse_button(), &mods))
.flatten()
{ {
self.handle_action_with_window(event_loop, window_id, action); self.handle_action_with_window(event_loop, window_id, action);
} }
}, },
WindowEvent::CursorLeft { .. } => { WindowEvent::PointerLeft { .. } => {
info!("Cursor left Window={window_id:?}"); info!("Pointer left Window={window_id:?}");
window.cursor_left(); window.cursor_left();
}, },
WindowEvent::CursorMoved { position, .. } => { WindowEvent::PointerMoved { position, .. } => {
info!("Moved cursor to {position:?}"); info!("Moved pointer to {position:?}");
window.cursor_moved(position); window.cursor_moved(position);
}, },
WindowEvent::ActivationTokenDone { token: _token, .. } => { WindowEvent::ActivationTokenDone { token: _token, .. } => {
@ -510,11 +513,10 @@ impl ApplicationHandler for Application {
WindowEvent::TouchpadPressure { .. } WindowEvent::TouchpadPressure { .. }
| WindowEvent::HoveredFileCancelled | WindowEvent::HoveredFileCancelled
| WindowEvent::KeyboardInput { .. } | WindowEvent::KeyboardInput { .. }
| WindowEvent::CursorEntered { .. } | WindowEvent::PointerEntered { .. }
| WindowEvent::DroppedFile(_) | WindowEvent::DroppedFile(_)
| WindowEvent::HoveredFile(_) | WindowEvent::HoveredFile(_)
| WindowEvent::Destroyed | WindowEvent::Destroyed
| WindowEvent::Touch(_)
| WindowEvent::Moved(_) => (), | WindowEvent::Moved(_) => (),
} }
} }

View file

@ -56,9 +56,6 @@ changelog entry.
Keep in mind that handles do not auto-upgrade after permissions are granted and have to be Keep in mind that handles do not auto-upgrade after permissions are granted and have to be
re-created to make full use of this feature. re-created to make full use of this feature.
- Add `Touch::finger_id` with a new type `FingerId`.
- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine
the primary finger in a multi-touch interaction.
- Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd` - Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd`
and `Serialize` on many types. and `Serialize` on many types.
- Add `MonitorHandle::current_video_mode()`. - Add `MonitorHandle::current_video_mode()`.
@ -66,6 +63,8 @@ changelog entry.
- On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game` - On macOS, add `WindowExtMacOS::set_borderless_game` and `WindowAttributesExtMacOS::with_borderless_game`
to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games. to fully disable the menu bar and dock in Borderless Fullscreen as commonly done in games.
- Add `WindowId::into_raw()` and `from_raw()`. - Add `WindowId::into_raw()` and `from_raw()`.
- Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId` and `position` to all pointer
events as part of the pointer event overhaul.
### Changed ### Changed
@ -127,6 +126,29 @@ changelog entry.
To migrate, you can probably just replace all instances of `inner_size` with `surface_size` in your codebase. To migrate, you can probably just replace all instances of `inner_size` with `surface_size` in your codebase.
- Every event carrying a `DeviceId` now uses `Option<DeviceId>` instead. A `None` value signifies that the - Every event carrying a `DeviceId` now uses `Option<DeviceId>` instead. A `None` value signifies that the
device can't be uniquely identified. device can't be uniquely identified.
- Pointer `WindowEvent`s were overhauled. The new events can handle any type of pointer, serving as
a single pointer input source. Now your application can handle any pointer type without having to
explicitly handle e.g. `Touch`:
- Rename `CursorMoved` to `PointerMoved`.
- Rename `CursorEntered` to `PointerEntered`.
- Rename `CursorLeft` to `PointerLeft`.
- Rename `MouseInput` to `PointerButton`.
- Add `position` to every `PointerEvent`.
- `PointerMoved` is **not sent** after `PointerEntered` anymore.
- Remove `Touch`, which is folded into the `Pointer*` events.
- New `PointerKind` added to `PointerEntered` and `PointerLeft`, signifying which pointer type is
the source of this event.
- New `PointerSource` added to `PointerMoved`, similar to `PointerKind` but holding additional
data.
- New `ButtonSource` added to `PointerButton`, similar to `PointerKind` but holding pointer type
specific buttons. Use `ButtonSource::mouse_button()` to easily normalize any pointer button
type to a generic mouse button.
- New `FingerId` added to `PointerKind::Touch` and `PointerSource::Touch` able to uniquely
identify a finger in a multi-touch interaction. Replaces the old `Touch::id`.
- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine
the primary finger in a multi-touch interaction.
- In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`.
- Remove `Force::Calibrated::altitude_angle`.
### Removed ### Removed
@ -149,13 +171,15 @@ changelog entry.
v0.5 support. v0.6 remains in place and is enabled by default. v0.5 support. v0.6 remains in place and is enabled by default.
- Remove `DeviceEvent::Added` and `DeviceEvent::Removed`. - Remove `DeviceEvent::Added` and `DeviceEvent::Removed`.
- Remove `DeviceEvent::Motion` and `WindowEvent::AxisMotion`. - Remove `DeviceEvent::Motion` and `WindowEvent::AxisMotion`.
- Remove `Touch::id` in favor of `Touch::finger_id`.
- Remove `MonitorHandle::size()` and `refresh_rate_millihertz()` in favor of - Remove `MonitorHandle::size()` and `refresh_rate_millihertz()` in favor of
`MonitorHandle::current_video_mode()`. `MonitorHandle::current_video_mode()`.
- On Android, remove all `MonitorHandle` support instead of emitting false data. - On Android, remove all `MonitorHandle` support instead of emitting false data.
- Remove `impl From<u64> for WindowId` and `impl From<WindowId> for u64`. Replaced with - Remove `impl From<u64> for WindowId` and `impl From<WindowId> for u64`. Replaced with
`WindowId::into_raw()` and `from_raw()`. `WindowId::into_raw()` and `from_raw()`.
- Remove `dummy()` from `WindowId` and `DeviceId`. - Remove `dummy()` from `WindowId` and `DeviceId`.
- Remove `WindowEvent::Touch` and `Touch` in favor of the new `PointerKind`, `PointerSource` and
`ButtonSource` as part of the new pointer event overhaul.
- Remove `Force::altitude_angle`.
### Fixed ### Fixed

View file

@ -226,53 +226,89 @@ pub enum WindowEvent {
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
Ime(Ime), Ime(Ime),
/// The cursor has moved on the window. /// The pointer has moved on the window.
/// PointerMoved {
/// ## 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: Option<DeviceId>, device_id: Option<DeviceId>,
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range /// (x,y) coordinates in pixels relative to the top-left corner of the window. Because the
/// of this data is limited by the display area and it may have been transformed by /// range of this data is limited by the display area and it may have been
/// the OS to implement effects such as cursor acceleration, it should not be used /// transformed by the OS to implement effects such as pointer acceleration, it
/// to implement non-cursor-like interactions such as 3D camera control. For that, /// should not be used to implement non-pointer-like interactions such as 3D camera
/// consider [`DeviceEvent::MouseMotion`]. /// control. For that, consider [`DeviceEvent::PointerMotion`].
///
/// ## 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
position: PhysicalPosition<f64>, position: PhysicalPosition<f64>,
source: PointerSource,
}, },
/// The cursor has entered the window. /// The pointer has entered the window.
/// PointerEntered {
/// ## Platform-specific device_id: Option<DeviceId>,
///
/// - **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: Option<DeviceId> },
/// The cursor has left the window. /// The position of the pointer when it entered the window.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Web:** Doesn't take into account CSS [`border`], [`padding`], or [`transform`]. /// - **Orbital: Always emits `(0., 0.)`.
/// /// - **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 /// [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform /// [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
CursorLeft { device_id: Option<DeviceId> }, /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
position: PhysicalPosition<f64>,
kind: PointerKind,
},
/// The pointer has left the window.
PointerLeft {
device_id: Option<DeviceId>,
/// The position of the pointer when it left the window. The position reported can be
/// outside the bounds of the window.
///
/// ## Platform-specific
///
/// - **Orbital/Windows:** Always emits [`None`].
/// - **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
position: Option<PhysicalPosition<f64>>,
kind: PointerKind,
},
/// A mouse wheel movement or touchpad scroll occurred. /// A mouse wheel movement or touchpad scroll occurred.
MouseWheel { device_id: Option<DeviceId>, delta: MouseScrollDelta, phase: TouchPhase }, MouseWheel { device_id: Option<DeviceId>, delta: MouseScrollDelta, phase: TouchPhase },
/// An mouse button press has been received. /// An mouse button press has been received.
MouseInput { device_id: Option<DeviceId>, state: ElementState, button: MouseButton }, PointerButton {
device_id: Option<DeviceId>,
state: ElementState,
/// The position of the pointer when the button was pressed.
///
/// ## Platform-specific
///
/// - **Orbital: Always emits `(0., 0.)`.
/// - **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
position: PhysicalPosition<f64>,
button: ButtonSource,
},
/// Two-finger pinch gesture, often used for magnification. /// Two-finger pinch gesture, often used for magnification.
/// ///
@ -346,18 +382,6 @@ pub enum WindowEvent {
/// touchpad is being pressed) and stage (integer representing the click level). /// touchpad is being pressed) and stage (integer representing the click level).
TouchpadPressure { device_id: Option<DeviceId>, pressure: f32, stage: i64 }, TouchpadPressure { device_id: Option<DeviceId>, pressure: f32, stage: i64 },
/// 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 window's scale factor has changed.
/// ///
/// The following user actions can cause DPI changes: /// The following user actions can cause DPI changes:
@ -431,6 +455,129 @@ pub enum WindowEvent {
RedrawRequested, RedrawRequested,
} }
/// Represents the kind type of a pointer event.
///
/// ## Platform-specific
///
/// **Wayland/X11:** [`Unknown`](Self::Unknown) device types are converted to known variants by the
/// system.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerKind {
Mouse,
/// See [`PointerSource::Touch`] for more details.
///
/// ## Platform-specific
///
/// **macOS:** Unsupported.
Touch(FingerId),
Unknown,
}
/// Represents the pointer type and its data for a pointer event.
///
/// **Wayland/X11:** [`Unknown`](Self::Unknown) device types are converted to known variants by the
/// system.
#[derive(Clone, Debug, PartialEq)]
pub enum PointerSource {
Mouse,
/// Represents a touch event.
///
/// Every time the user touches the screen, a [`WindowEvent::PointerEntered`] and a
/// [`WindowEvent::PointerButton`] with [`ElementState::Pressed`] event with an unique
/// identifier for the finger is emitted. When a finger is lifted, a
/// [`WindowEvent::PointerButton`] with [`ElementState::Released`] and a
/// [`WindowEvent::PointerLeft`] event is generated with the same [`FingerId`].
///
/// After a [`WindowEvent::PointerEntered`] event has been emitted, there may be zero or more
/// [`WindowEvent::PointerMoved`] events when the finger is moved or the touch pressure
/// changes.
///
/// A [`WindowEvent::PointerLeft`] without a [`WindowEvent::PointerButton`] with
/// [`ElementState::Released`] event is emitted when the system has canceled tracking this
/// touch, such as when the window loses focus, or on mobile devices if the user moves the
/// device against their face.
///
/// The [`FingerId`] may be reused by the system after a [`WindowEvent::PointerLeft`] event.
/// The user should assume that a new [`WindowEvent::PointerEntered`] event received with the
/// same ID has nothing to do with the old finger and is a new finger.
///
/// ## Platform-specific
///
/// **macOS:** Unsupported.
Touch {
finger_id: FingerId,
/// Describes how hard the screen was pressed. May be [`None`] if the hardware does not
/// support pressure sensitivity.
///
/// ## Platform-specific
///
/// - **MacOS / Orbital / Wayland / X11:** Always emits [`None`].
/// - **Android:** 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).#[derive(Debug, Clone, Copy, PartialEq)]
/// - **Web:** Will never be [`None`]. If the device doesn't support pressure sensitivity,
/// force will be 0.5 when a button is pressed or 0.0 otherwise.
force: Option<Force>,
},
Unknown,
}
impl From<PointerSource> for PointerKind {
fn from(source: PointerSource) -> Self {
match source {
PointerSource::Mouse => Self::Mouse,
PointerSource::Touch { finger_id, .. } => Self::Touch(finger_id),
PointerSource::Unknown => Self::Unknown,
}
}
}
/// Represents the pointer type of a [`WindowEvent::PointerButton`].
///
/// **Wayland/X11:** [`Unknown`](Self::Unknown) device types are converted to known variants by the
/// system.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ButtonSource {
Mouse(MouseButton),
/// See [`PointerSource::Touch`] for more details.
///
/// ## Platform-specific
///
/// **macOS:** Unsupported.
Touch {
finger_id: FingerId,
force: Option<Force>,
},
Unknown(u16),
}
impl ButtonSource {
/// Convert any [`ButtonSource`] to an equivalent [`MouseButton`]. If a pointer type has no
/// special handling in an application, this method can be used to handle it like any generic
/// mouse input.
pub fn mouse_button(self) -> MouseButton {
match self {
ButtonSource::Mouse(mouse) => mouse,
ButtonSource::Touch { .. } => MouseButton::Left,
ButtonSource::Unknown(button) => match button {
0 => MouseButton::Left,
1 => MouseButton::Middle,
2 => MouseButton::Right,
3 => MouseButton::Back,
4 => MouseButton::Forward,
_ => MouseButton::Other(button),
},
}
}
}
impl From<MouseButton> for ButtonSource {
fn from(mouse: MouseButton) -> Self {
Self::Mouse(mouse)
}
}
/// Identifier of an input device. /// Identifier of an input device.
/// ///
/// Whenever you receive an event arising from a particular input device, this event contains a /// Whenever you receive an event arising from a particular input device, this event contains a
@ -459,7 +606,7 @@ impl FingerId {
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera /// 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 /// 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 /// 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. /// (corresponding to GUI pointers and keyboard focus) the device IDs may not match.
/// ///
/// Note that these events are delivered regardless of input focus. /// Note that these events are delivered regardless of input focus.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -467,7 +614,7 @@ pub enum DeviceEvent {
/// Change in physical position of a pointing device. /// Change in physical position of a pointing device.
/// ///
/// This represents raw, unfiltered physical motion. Not to be confused with /// This represents raw, unfiltered physical motion. Not to be confused with
/// [`WindowEvent::CursorMoved`]. /// [`WindowEvent::PointerMoved`].
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
@ -484,7 +631,7 @@ pub enum DeviceEvent {
/// ///
#[rustfmt::skip] #[rustfmt::skip]
/// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked /// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked
MouseMotion { PointerMotion {
/// (x, y) change in position in unspecified units. /// (x, y) change in position in unspecified units.
/// ///
/// Different devices may use different units. /// Different devices may use different units.
@ -813,50 +960,6 @@ pub enum TouchPhase {
Cancelled, 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: Option<DeviceId>,
pub phase: TouchPhase,
pub location: PhysicalPosition<f64>,
/// 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<Force>,
/// Unique identifier of a finger.
pub finger_id: FingerId,
}
/// Describes the force of a touch event /// Describes the force of a touch event
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -877,12 +980,6 @@ pub enum Force {
/// The value of this field is sufficiently high to provide a wide /// The value of this field is sufficiently high to provide a wide
/// dynamic range for values of the `force` field. /// dynamic range for values of the `force` field.
max_possible_force: f64, 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<f64>,
}, },
/// If the platform reports the force as normalized, we have no way of /// 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 /// knowing how much pressure 1.0 corresponds to we know it's the maximum
@ -899,13 +996,7 @@ impl Force {
/// consistent across devices. /// consistent across devices.
pub fn normalized(&self) -> f64 { pub fn normalized(&self) -> f64 {
match self { match self {
Force::Calibrated { force, max_possible_force, altitude_angle } => { Force::Calibrated { force, max_possible_force } => force / max_possible_force,
let force = match altitude_angle {
Some(altitude_angle) => force / altitude_angle.sin(),
None => *force,
};
force / max_possible_force
},
Force::Normalized(force) => *force, Force::Normalized(force) => *force,
} }
} }
@ -1028,6 +1119,7 @@ mod tests {
use crate::event::Event::*; use crate::event::Event::*;
use crate::event::Ime::Enabled; use crate::event::Ime::Enabled;
use crate::event::WindowEvent::*; use crate::event::WindowEvent::*;
use crate::event::{PointerKind, PointerSource};
use crate::window::WindowId; use crate::window::WindowId;
// Mainline events. // Mainline events.
@ -1050,19 +1142,41 @@ mod tests {
with_window_event(HoveredFile("x.txt".into())); with_window_event(HoveredFile("x.txt".into()));
with_window_event(HoveredFileCancelled); with_window_event(HoveredFileCancelled);
with_window_event(Ime(Enabled)); with_window_event(Ime(Enabled));
with_window_event(CursorMoved { device_id: None, position: (0, 0).into() }); with_window_event(PointerMoved {
device_id: None,
position: (0, 0).into(),
source: PointerSource::Mouse,
});
with_window_event(ModifiersChanged(event::Modifiers::default())); with_window_event(ModifiersChanged(event::Modifiers::default()));
with_window_event(CursorEntered { device_id: None }); with_window_event(PointerEntered {
with_window_event(CursorLeft { device_id: None }); device_id: None,
position: (0, 0).into(),
kind: PointerKind::Mouse,
});
with_window_event(PointerLeft {
device_id: None,
position: Some((0, 0).into()),
kind: PointerKind::Mouse,
});
with_window_event(MouseWheel { with_window_event(MouseWheel {
device_id: None, device_id: None,
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
}); });
with_window_event(MouseInput { with_window_event(PointerButton {
device_id: None, device_id: None,
state: event::ElementState::Pressed, state: event::ElementState::Pressed,
button: event::MouseButton::Other(0), position: (0, 0).into(),
button: event::MouseButton::Other(0).into(),
});
with_window_event(PointerButton {
device_id: None,
state: event::ElementState::Released,
position: (0, 0).into(),
button: event::ButtonSource::Touch {
finger_id: fid,
force: Some(event::Force::Normalized(0.0)),
},
}); });
with_window_event(PinchGesture { with_window_event(PinchGesture {
device_id: None, device_id: None,
@ -1081,13 +1195,6 @@ mod tests {
phase: event::TouchPhase::Started, phase: event::TouchPhase::Started,
}); });
with_window_event(TouchpadPressure { device_id: None, pressure: 0.0, stage: 0 }); with_window_event(TouchpadPressure { device_id: None, pressure: 0.0, stage: 0 });
with_window_event(Touch(event::Touch {
device_id: None,
phase: event::TouchPhase::Started,
location: (0.0, 0.0).into(),
finger_id: fid,
force: Some(event::Force::Normalized(0.0)),
}));
with_window_event(ThemeChanged(crate::window::Theme::Light)); with_window_event(ThemeChanged(crate::window::Theme::Light));
with_window_event(Occluded(true)); with_window_event(Occluded(true));
} }
@ -1099,7 +1206,7 @@ mod tests {
let with_device_event = let with_device_event =
|dev_ev| x(event::Event::DeviceEvent { device_id: None, event: dev_ev }); |dev_ev| x(event::Event::DeviceEvent { device_id: None, event: dev_ev });
with_device_event(MouseMotion { delta: (0.0, 0.0).into() }); with_device_event(PointerMotion { delta: (0.0, 0.0).into() });
with_device_event(MouseWheel { with_device_event(MouseWheel {
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
}); });
@ -1122,15 +1229,10 @@ mod tests {
let force = event::Force::Normalized(0.0); let force = event::Force::Normalized(0.0);
assert_eq!(force.normalized(), 0.0); assert_eq!(force.normalized(), 0.0);
let force2 = let force2 = event::Force::Calibrated { force: 5.0, max_possible_force: 2.5 };
event::Force::Calibrated { force: 5.0, max_possible_force: 2.5, altitude_angle: None };
assert_eq!(force2.normalized(), 2.0); assert_eq!(force2.normalized(), 2.0);
let force3 = event::Force::Calibrated { let force3 = event::Force::Calibrated { force: 5.0, max_possible_force: 2.5 };
force: 5.0,
max_possible_force: 2.5,
altitude_angle: Some(std::f64::consts::PI / 2.0),
};
assert_eq!(force3.normalized(), 2.0); assert_eq!(force3.normalized(), 2.0);
} }
@ -1154,16 +1256,6 @@ mod tests {
HashSet::new().insert(event::MouseButton::Left.clone()); HashSet::new().insert(event::MouseButton::Left.clone());
HashSet::new().insert(event::Ime::Enabled); HashSet::new().insert(event::Ime::Enabled);
let _ = event::Touch { let _ = event::Force::Calibrated { force: 0.0, max_possible_force: 0.0 }.clone();
device_id: None,
phase: event::TouchPhase::Started,
location: (0.0, 0.0).into(),
finger_id: fid,
force: Some(event::Force::Normalized(0.0)),
}
.clone();
let _ =
event::Force::Calibrated { force: 0.0, max_possible_force: 0.0, altitude_angle: None }
.clone();
} }
} }

View file

@ -420,7 +420,7 @@ impl rwh_06::HasDisplayHandle for dyn ActiveEventLoop + '_ {
/// A proxy for the underlying display handle. /// A proxy for the underlying display handle.
/// ///
/// The purpose of this type is to provide a cheaply clonable handle to the underlying /// The purpose of this type is to provide a cheaply cloneable handle to the underlying
/// display handle. This is often used by graphics APIs to connect to the underlying APIs. /// display handle. This is often used by graphics APIs to connect to the underlying APIs.
/// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`] /// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`]
/// type. In contrast, this type involves no lifetimes and can be persisted for as long as /// type. In contrast, this type involves no lifetimes and can be persisted for as long as

View file

@ -29,17 +29,16 @@
//! The following APIs can't take them into account and will therefore provide inaccurate results: //! The following APIs can't take them into account and will therefore provide inaccurate results:
//! - [`WindowEvent::SurfaceResized`] and [`Window::(set_)surface_size()`] //! - [`WindowEvent::SurfaceResized`] and [`Window::(set_)surface_size()`]
//! - [`WindowEvent::Occluded`] //! - [`WindowEvent::Occluded`]
//! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`], and //! - [`WindowEvent::PointerMoved`], [`WindowEvent::PointerEntered`] and
//! [`WindowEvent::Touch`]. //! [`WindowEvent::PointerLeft`].
//! - [`Window::set_outer_position()`] //! - [`Window::set_outer_position()`]
//! //!
//! [`WindowEvent::SurfaceResized`]: crate::event::WindowEvent::SurfaceResized //! [`WindowEvent::SurfaceResized`]: crate::event::WindowEvent::SurfaceResized
//! [`Window::(set_)surface_size()`]: crate::window::Window::surface_size //! [`Window::(set_)surface_size()`]: crate::window::Window::surface_size
//! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded //! [`WindowEvent::Occluded`]: crate::event::WindowEvent::Occluded
//! [`WindowEvent::CursorMoved`]: crate::event::WindowEvent::CursorMoved //! [`WindowEvent::PointerMoved`]: crate::event::WindowEvent::PointerMoved
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered //! [`WindowEvent::PointerEntered`]: crate::event::WindowEvent::PointerEntered
//! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft //! [`WindowEvent::PointerLeft`]: crate::event::WindowEvent::PointerLeft
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch
//! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position //! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position
use std::cell::Ref; use std::cell::Ref;

View file

@ -317,48 +317,115 @@ impl EventLoop {
InputEvent::MotionEvent(motion_event) => { InputEvent::MotionEvent(motion_event) => {
let window_id = window::WindowId(WindowId); let window_id = window::WindowId(WindowId);
let device_id = Some(event::DeviceId(DeviceId(motion_event.device_id()))); let device_id = Some(event::DeviceId(DeviceId(motion_event.device_id())));
let action = motion_event.action();
let phase = match motion_event.action() { let pointers: Option<
MotionAction::Down | MotionAction::PointerDown => { Box<dyn Iterator<Item = android_activity::input::Pointer<'_>>>,
Some(event::TouchPhase::Started) > = match action {
}, MotionAction::Down
MotionAction::Up | MotionAction::PointerUp => Some(event::TouchPhase::Ended), | MotionAction::PointerDown
MotionAction::Move => Some(event::TouchPhase::Moved), | MotionAction::Up
MotionAction::Cancel => Some(event::TouchPhase::Cancelled), | MotionAction::PointerUp => Some(Box::new(std::iter::once(
_ => { motion_event.pointer_at_index(motion_event.pointer_index()),
None // TODO mouse events ))),
MotionAction::Move | MotionAction::Cancel => {
Some(Box::new(motion_event.pointers()))
}, },
// TODO mouse events
_ => None,
}; };
if let Some(phase) = phase {
let pointers: Box<dyn Iterator<Item = android_activity::input::Pointer<'_>>> =
match phase {
event::TouchPhase::Started | event::TouchPhase::Ended => {
Box::new(std::iter::once(
motion_event.pointer_at_index(motion_event.pointer_index()),
))
},
event::TouchPhase::Moved | event::TouchPhase::Cancelled => {
Box::new(motion_event.pointers())
},
};
if let Some(pointers) = pointers {
for pointer in pointers { for pointer in pointers {
let location = let tool_type = pointer.tool_type();
let position =
PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ }; PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ };
trace!( trace!(
"Input event {device_id:?}, {phase:?}, loc={location:?}, \ "Input event {device_id:?}, {action:?}, loc={position:?}, \
pointer={pointer:?}" pointer={pointer:?}, tool_type={tool_type:?}"
); );
let finger_id = event::FingerId(FingerId(pointer.pointer_id()));
let force = Some(Force::Normalized(pointer.pressure() as f64));
let event = event::WindowEvent::Touch(event::Touch { match action {
device_id, MotionAction::Down | MotionAction::PointerDown => {
phase, let event = event::WindowEvent::PointerEntered {
location, device_id,
finger_id: event::FingerId(FingerId(pointer.pointer_id())), position,
force: Some(Force::Normalized(pointer.pressure() as f64)), kind: match tool_type {
}); android_activity::input::ToolType::Finger => {
event::PointerKind::Touch(finger_id)
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
},
};
app.window_event(&self.window_target, window_id, event);
let event = event::WindowEvent::PointerButton {
device_id,
state: event::ElementState::Pressed,
position,
button: match tool_type {
android_activity::input::ToolType::Finger => {
event::ButtonSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::ButtonSource::Unknown(0),
},
};
app.window_event(&self.window_target, window_id, event);
},
MotionAction::Move => {
let event = event::WindowEvent::PointerMoved {
device_id,
position,
source: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerSource::Unknown,
},
};
app.window_event(&self.window_target, window_id, event);
},
MotionAction::Up | MotionAction::PointerUp | MotionAction::Cancel => {
if let MotionAction::Up | MotionAction::PointerUp = action {
let event = event::WindowEvent::PointerButton {
device_id,
state: event::ElementState::Released,
position,
button: match tool_type {
android_activity::input::ToolType::Finger => {
event::ButtonSource::Touch { finger_id, force }
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::ButtonSource::Unknown(0),
},
};
app.window_event(&self.window_target, window_id, event);
}
app.window_event(&self.window_target, window_id, event); let event = event::WindowEvent::PointerLeft {
device_id,
position: Some(position),
kind: match tool_type {
android_activity::input::ToolType::Finger => {
event::PointerKind::Touch(finger_id)
},
// TODO mouse events
android_activity::input::ToolType::Mouse => continue,
_ => event::PointerKind::Unknown,
},
};
app.window_event(&self.window_target, window_id, event);
},
_ => unreachable!(),
}
} }
} }
}, },

View file

@ -60,7 +60,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
if delta_x != 0.0 || delta_y != 0.0 { if delta_x != 0.0 || delta_y != 0.0 {
app_state.maybe_queue_with_handler(move |app, event_loop| { app_state.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, None, DeviceEvent::MouseMotion { app.device_event(event_loop, None, DeviceEvent::PointerMotion {
delta: (delta_x, delta_y), delta: (delta_x, delta_y),
}); });
}); });

View file

@ -26,8 +26,8 @@ use super::event::{
use super::window::WinitWindow; use super::window::WinitWindow;
use crate::dpi::{LogicalPosition, LogicalSize}; use crate::dpi::{LogicalPosition, LogicalSize};
use crate::event::{ use crate::event::{
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, TouchPhase, DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, PointerKind,
WindowEvent, PointerSource, TouchPhase, WindowEvent,
}; };
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey}; use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
use crate::platform::macos::OptionAsAlt; use crate::platform::macos::OptionAsAlt;
@ -638,19 +638,28 @@ declare_class!(
} }
#[method(mouseEntered:)] #[method(mouseEntered:)]
fn mouse_entered(&self, _event: &NSEvent) { fn mouse_entered(&self, event: &NSEvent) {
trace_scope!("mouseEntered:"); trace_scope!("mouseEntered:");
self.queue_event(WindowEvent::CursorEntered {
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
self.queue_event(WindowEvent::PointerEntered {
device_id: None, device_id: None,
position,
kind: PointerKind::Mouse,
}); });
} }
#[method(mouseExited:)] #[method(mouseExited:)]
fn mouse_exited(&self, _event: &NSEvent) { fn mouse_exited(&self, event: &NSEvent) {
trace_scope!("mouseExited:"); trace_scope!("mouseExited:");
self.queue_event(WindowEvent::CursorLeft { let position = self.mouse_view_point(event).to_physical(self.scale_factor());
self.queue_event(WindowEvent::PointerLeft {
device_id: None, device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
}); });
} }
@ -1033,16 +1042,21 @@ impl WinitView {
} }
fn mouse_click(&self, event: &NSEvent, button_state: ElementState) { fn mouse_click(&self, event: &NSEvent, button_state: ElementState) {
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
let button = mouse_button(event); let button = mouse_button(event);
self.update_modifiers(event, false); self.update_modifiers(event, false);
self.queue_event(WindowEvent::MouseInput { device_id: None, state: button_state, button }); self.queue_event(WindowEvent::PointerButton {
device_id: None,
state: button_state,
position,
button: button.into(),
});
} }
fn mouse_motion(&self, event: &NSEvent) { fn mouse_motion(&self, event: &NSEvent) {
let window_point = unsafe { event.locationInWindow() }; let view_point = self.mouse_view_point(event);
let view_point = self.convertPoint_fromView(window_point, None);
let frame = self.frame(); let frame = self.frame();
if view_point.x.is_sign_negative() if view_point.x.is_sign_negative()
@ -1057,15 +1071,21 @@ impl WinitView {
} }
} }
let view_point = LogicalPosition::new(view_point.x, view_point.y);
self.update_modifiers(event, false); self.update_modifiers(event, false);
self.queue_event(WindowEvent::CursorMoved { self.queue_event(WindowEvent::PointerMoved {
device_id: None, device_id: None,
position: view_point.to_physical(self.scale_factor()), position: view_point.to_physical(self.scale_factor()),
source: PointerSource::Mouse,
}); });
} }
fn mouse_view_point(&self, event: &NSEvent) -> LogicalPosition<f64> {
let window_point = unsafe { event.locationInWindow() };
let view_point = self.convertPoint_fromView(window_point, None);
LogicalPosition::new(view_point.x, view_point.y)
}
} }
/// Get the mouse button from the NSEvent. /// Get the mouse button from the NSEvent.

View file

@ -17,7 +17,8 @@ use super::window::WinitUIWindow;
use super::FingerId; use super::FingerId;
use crate::dpi::PhysicalPosition; use crate::dpi::PhysicalPosition;
use crate::event::{ use crate::event::{
ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, Touch, TouchPhase, WindowEvent, ButtonSource, ElementState, Event, FingerId as RootFingerId, Force, KeyEvent, PointerKind,
PointerSource, TouchPhase, WindowEvent,
}; };
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey}; use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
use crate::platform_impl::KeyEventExtra; use crate::platform_impl::KeyEventExtra;
@ -483,25 +484,18 @@ impl WinitView {
for touch in touches { for touch in touches {
let logical_location = touch.locationInView(None); let logical_location = touch.locationInView(None);
let touch_type = touch.r#type(); let touch_type = touch.r#type();
let force = if os_supports_force { let force = if let UITouchType::Pencil = touch_type {
None
} else if os_supports_force {
let trait_collection = self.traitCollection(); let trait_collection = self.traitCollection();
let touch_capability = trait_collection.forceTouchCapability(); let touch_capability = trait_collection.forceTouchCapability();
// Both the OS _and_ the device need to be checked for force touch support. // Both the OS _and_ the device need to be checked for force touch support.
if touch_capability == UIForceTouchCapability::Available if touch_capability == UIForceTouchCapability::Available {
|| touch_type == UITouchType::Pencil
{
let force = touch.force(); let force = touch.force();
let max_possible_force = touch.maximumPossibleForce(); let max_possible_force = touch.maximumPossibleForce();
let altitude_angle: Option<f64> = if touch_type == UITouchType::Pencil {
let angle = touch.altitudeAngle();
Some(angle as _)
} else {
None
};
Some(Force::Calibrated { Some(Force::Calibrated {
force: force as _, force: force as _,
max_possible_force: max_possible_force as _, max_possible_force: max_possible_force as _,
altitude_angle,
}) })
} else { } else {
None None
@ -511,32 +505,91 @@ impl WinitView {
}; };
let touch_id = touch as *const UITouch as usize; let touch_id = touch as *const UITouch as usize;
let phase = touch.phase(); let phase = touch.phase();
let phase = match phase { let position = {
UITouchPhase::Began => TouchPhase::Started,
UITouchPhase::Moved => TouchPhase::Moved,
// 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended => TouchPhase::Ended,
UITouchPhase::Cancelled => TouchPhase::Cancelled,
_ => panic!("unexpected touch phase: {phase:?}"),
};
let physical_location = {
let scale_factor = self.contentScaleFactor(); let scale_factor = self.contentScaleFactor();
PhysicalPosition::from_logical::<(f64, f64), f64>( PhysicalPosition::from_logical::<(f64, f64), f64>(
(logical_location.x as _, logical_location.y as _), (logical_location.x as _, logical_location.y as _),
scale_factor as f64, scale_factor as f64,
) )
}; };
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { let window_id = RootWindowId(window.id());
window_id: RootWindowId(window.id()), let finger_id = RootFingerId(FingerId(touch_id));
event: WindowEvent::Touch(Touch {
device_id: None, match phase {
finger_id: RootFingerId(FingerId(touch_id)), UITouchPhase::Began => {
location: physical_location, touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
force, window_id,
phase, event: WindowEvent::PointerEntered {
}), device_id: None,
})); position,
kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown
} else {
PointerKind::Touch(finger_id)
},
},
}));
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: ElementState::Pressed,
position,
button: if let UITouchType::Pencil = touch_type {
ButtonSource::Unknown(0)
} else {
ButtonSource::Touch { finger_id, force }
},
},
}));
},
UITouchPhase::Moved => {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
position,
source: if let UITouchType::Pencil = touch_type {
PointerSource::Unknown
} else {
PointerSource::Touch { finger_id, force }
},
},
}));
},
// 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended | UITouchPhase::Cancelled => {
if let UITouchPhase::Ended = phase {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: ElementState::Released,
position,
button: if let UITouchType::Pencil = touch_type {
ButtonSource::Unknown(0)
} else {
ButtonSource::Touch { finger_id, force }
},
},
}));
}
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: if let UITouchType::Pencil = touch_type {
PointerKind::Unknown
} else {
PointerKind::Touch(finger_id)
},
},
}));
},
_ => panic!("unexpected touch phase: {phase:?}"),
}
} }
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_events(mtm, touch_events); app_state::handle_nonuser_events(mtm, touch_events);

View file

@ -638,7 +638,7 @@ pub fn keysym_to_key(keysym: u32) -> Key {
// keysyms::ISO_Release_Margin_Left => NamedKey::IsoReleaseMarginLeft, // keysyms::ISO_Release_Margin_Left => NamedKey::IsoReleaseMarginLeft,
// keysyms::ISO_Release_Margin_Right => NamedKey::IsoReleaseMarginRight, // keysyms::ISO_Release_Margin_Right => NamedKey::IsoReleaseMarginRight,
// keysyms::ISO_Release_Both_Margins => NamedKey::IsoReleaseBothMargins, // keysyms::ISO_Release_Both_Margins => NamedKey::IsoReleaseBothMargins,
// keysyms::ISO_Fast_Cursor_Left => NamedKey::IsoFastCursorLeft, // keysyms::ISO_Fast_Cursor_Left => NamedKey::IsoFastPointerLeft,
// keysyms::ISO_Fast_Cursor_Right => NamedKey::IsoFastCursorRight, // keysyms::ISO_Fast_Cursor_Right => NamedKey::IsoFastCursorRight,
// keysyms::ISO_Fast_Cursor_Up => NamedKey::IsoFastCursorUp, // keysyms::ISO_Fast_Cursor_Up => NamedKey::IsoFastCursorUp,
// keysyms::ISO_Fast_Cursor_Down => NamedKey::IsoFastCursorDown, // keysyms::ISO_Fast_Cursor_Down => NamedKey::IsoFastCursorDown,

View file

@ -27,7 +27,7 @@ use sctk::seat::pointer::{
use sctk::seat::SeatState; use sctk::seat::SeatState;
use crate::dpi::{LogicalPosition, PhysicalPosition}; use crate::dpi::{LogicalPosition, PhysicalPosition};
use crate::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent}; use crate::event::{ElementState, MouseButton, MouseScrollDelta, PointerSource, PointerKind, TouchPhase, WindowEvent};
use crate::platform_impl::wayland::state::WinitState; use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, WindowId}; use crate::platform_impl::wayland::{self, WindowId};
@ -123,7 +123,11 @@ impl PointerHandler for WinitState {
// Regular events on the main surface. // Regular events on the main surface.
PointerEventKind::Enter { .. } => { PointerEventKind::Enter { .. } => {
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::CursorEntered { device_id: None }, WindowEvent::PointerEntered {
device_id: None,
position,
kind: PointerKind::Mouse,
},
window_id, window_id,
); );
@ -131,11 +135,6 @@ impl PointerHandler for WinitState {
// Set the currently focused surface. // Set the currently focused surface.
pointer.winit_data().inner.lock().unwrap().surface = Some(window_id); pointer.winit_data().inner.lock().unwrap().surface = Some(window_id);
self.events_sink.push_window_event(
WindowEvent::CursorMoved { device_id: None, position },
window_id,
);
}, },
PointerEventKind::Leave { .. } => { PointerEventKind::Leave { .. } => {
window.pointer_left(Arc::downgrade(themed_pointer)); window.pointer_left(Arc::downgrade(themed_pointer));
@ -143,12 +142,22 @@ impl PointerHandler for WinitState {
// Remove the active surface. // Remove the active surface.
pointer.winit_data().inner.lock().unwrap().surface = None; pointer.winit_data().inner.lock().unwrap().surface = None;
self.events_sink self.events_sink.push_window_event(
.push_window_event(WindowEvent::CursorLeft { device_id: None }, window_id); WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
},
window_id,
);
}, },
PointerEventKind::Motion { .. } => { PointerEventKind::Motion { .. } => {
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::CursorMoved { device_id: None, position }, WindowEvent::PointerMoved {
device_id: None,
position,
source: PointerSource::Mouse,
},
window_id, window_id,
); );
}, },
@ -164,7 +173,12 @@ impl PointerHandler for WinitState {
ElementState::Released ElementState::Released
}; };
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::MouseInput { device_id: None, state, button }, WindowEvent::PointerButton {
device_id: None,
state,
position,
button: button.into(),
},
window_id, window_id,
); );
}, },

View file

@ -68,7 +68,7 @@ impl Dispatch<ZwpRelativePointerV1, GlobalData, WinitState> for RelativePointerS
}; };
state state
.events_sink .events_sink
.push_device_event(DeviceEvent::MouseMotion { delta: (dx_unaccel, dy_unaccel) }); .push_device_event(DeviceEvent::PointerMotion { delta: (dx_unaccel, dy_unaccel) });
} }
} }

View file

@ -8,7 +8,7 @@ use sctk::seat::touch::{TouchData, TouchHandler};
use tracing::warn; use tracing::warn;
use crate::dpi::LogicalPosition; use crate::dpi::LogicalPosition;
use crate::event::{Touch, TouchPhase, WindowEvent}; use crate::event::{ButtonSource, ElementState, PointerKind, PointerSource, WindowEvent};
use crate::platform_impl::wayland::state::WinitState; use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, FingerId}; use crate::platform_impl::wayland::{self, FingerId};
@ -42,16 +42,25 @@ impl TouchHandler for WinitState {
let location = LogicalPosition::<f64>::from(position); let location = LogicalPosition::<f64>::from(position);
seat_state.touch_map.insert(id, TouchPoint { surface, location }); seat_state.touch_map.insert(id, TouchPoint { surface, location });
let position = location.to_physical(scale_factor);
let finger_id =
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::Touch(Touch { WindowEvent::PointerEntered {
device_id: None, device_id: None,
phase: TouchPhase::Started, position,
location: location.to_physical(scale_factor), kind: PointerKind::Touch(finger_id),
force: None, },
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland( window_id,
FingerId(id), );
)), self.events_sink.push_window_event(
}), WindowEvent::PointerButton {
device_id: None,
state: ElementState::Pressed,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
window_id, window_id,
); );
} }
@ -85,16 +94,25 @@ impl TouchHandler for WinitState {
None => return, None => return,
}; };
let position = touch_point.location.to_physical(scale_factor);
let finger_id =
crate::event::FingerId(crate::platform_impl::FingerId::Wayland(FingerId(id)));
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::Touch(Touch { WindowEvent::PointerButton {
device_id: None, device_id: None,
phase: TouchPhase::Ended, state: ElementState::Released,
location: touch_point.location.to_physical(scale_factor), position,
force: None, button: ButtonSource::Touch { finger_id, force: None },
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland( },
FingerId(id), window_id,
)), );
}), self.events_sink.push_window_event(
WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
window_id, window_id,
); );
} }
@ -131,15 +149,16 @@ impl TouchHandler for WinitState {
touch_point.location = LogicalPosition::<f64>::from(position); touch_point.location = LogicalPosition::<f64>::from(position);
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::Touch(Touch { WindowEvent::PointerMoved {
device_id: None, device_id: None,
phase: TouchPhase::Moved, position: touch_point.location.to_physical(scale_factor),
location: touch_point.location.to_physical(scale_factor), source: PointerSource::Touch {
force: None, finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland( FingerId(id),
FingerId(id), )),
)), force: None,
}), },
},
window_id, window_id,
); );
} }
@ -160,18 +179,16 @@ impl TouchHandler for WinitState {
None => return, None => return,
}; };
let location = touch_point.location.to_physical(scale_factor); let position = touch_point.location.to_physical(scale_factor);
self.events_sink.push_window_event( self.events_sink.push_window_event(
WindowEvent::Touch(Touch { WindowEvent::PointerLeft {
device_id: None, device_id: None,
phase: TouchPhase::Cancelled, position: Some(position),
location, kind: PointerKind::Touch(crate::event::FingerId(
force: None, crate::platform_impl::FingerId::Wayland(FingerId(id)),
finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland(
FingerId(id),
)), )),
}), },
window_id, window_id,
); );
} }

View file

@ -22,8 +22,8 @@ use xkbcommon_dl::xkb_mod_mask_t;
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::event::{ use crate::event::{
DeviceEvent, ElementState, Event, Ime, MouseButton, MouseScrollDelta, RawKeyEvent, ButtonSource, DeviceEvent, ElementState, Event, Ime, MouseButton, MouseScrollDelta,
SurfaceSizeWriter, Touch, TouchPhase, WindowEvent, PointerKind, PointerSource, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent,
}; };
use crate::keyboard::ModifiersState; use crate::keyboard::ModifiersState;
use crate::platform_impl::common::xkb::{self, XkbState}; use crate::platform_impl::common::xkb::{self, XkbState};
@ -238,15 +238,8 @@ impl EventProcessor {
self.xinput2_unfocused(xev, &mut callback); self.xinput2_unfocused(xev, &mut callback);
}, },
xinput2::XI_TouchBegin | xinput2::XI_TouchUpdate | xinput2::XI_TouchEnd => { xinput2::XI_TouchBegin | xinput2::XI_TouchUpdate | xinput2::XI_TouchEnd => {
let phase = match evtype {
xinput2::XI_TouchBegin => TouchPhase::Started,
xinput2::XI_TouchUpdate => TouchPhase::Moved,
xinput2::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!(),
};
let xev: &XIDeviceEvent = unsafe { xev.as_event() }; let xev: &XIDeviceEvent = unsafe { xev.as_event() };
self.xinput2_touch(xev, phase, &mut callback); self.xinput2_touch(xev, evtype, &mut callback);
}, },
xinput2::XI_RawButtonPress | xinput2::XI_RawButtonRelease => { xinput2::XI_RawButtonPress | xinput2::XI_RawButtonRelease => {
let state = match evtype { let state = match evtype {
@ -1025,16 +1018,27 @@ impl EventProcessor {
return; return;
} }
let position = PhysicalPosition::new(event.event_x, event.event_y);
let event = match event.detail as u32 { let event = match event.detail as u32 {
xlib::Button1 => { xlib::Button1 => WindowEvent::PointerButton {
WindowEvent::MouseInput { device_id, state, button: MouseButton::Left } device_id,
state,
position,
button: MouseButton::Left.into(),
}, },
xlib::Button2 => { xlib::Button2 => WindowEvent::PointerButton {
WindowEvent::MouseInput { device_id, state, button: MouseButton::Middle } device_id,
state,
position,
button: MouseButton::Middle.into(),
}, },
xlib::Button3 => { xlib::Button3 => WindowEvent::PointerButton {
WindowEvent::MouseInput { device_id, state, button: MouseButton::Right } device_id,
state,
position,
button: MouseButton::Right.into(),
}, },
// Suppress emulated scroll wheel clicks, since we handle the real motion events for // Suppress emulated scroll wheel clicks, since we handle the real motion events for
@ -1052,10 +1056,25 @@ impl EventProcessor {
}, },
phase: TouchPhase::Moved, phase: TouchPhase::Moved,
}, },
8 => WindowEvent::MouseInput { device_id, state, button: MouseButton::Back }, 8 => WindowEvent::PointerButton {
device_id,
state,
position,
button: MouseButton::Back.into(),
},
9 => WindowEvent::MouseInput { device_id, state, button: MouseButton::Forward }, 9 => WindowEvent::PointerButton {
x => WindowEvent::MouseInput { device_id, state, button: MouseButton::Other(x as u16) }, device_id,
state,
position,
button: MouseButton::Forward.into(),
},
x => WindowEvent::PointerButton {
device_id,
state,
position,
button: MouseButton::Other(x as u16).into(),
},
}; };
let event = Event::WindowEvent { window_id, event }; let event = Event::WindowEvent { window_id, event };
@ -1084,7 +1103,11 @@ impl EventProcessor {
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id, window_id,
event: WindowEvent::CursorMoved { device_id, position }, event: WindowEvent::PointerMoved {
device_id,
position,
source: PointerSource::Mouse,
},
}; };
callback(&self.target, event); callback(&self.target, event);
} else if cursor_moved.is_none() { } else if cursor_moved.is_none() {
@ -1167,13 +1190,13 @@ impl EventProcessor {
let device_id = Some(device_id); let device_id = Some(device_id);
let position = PhysicalPosition::new(event.event_x, event.event_y); let position = PhysicalPosition::new(event.event_x, event.event_y);
let event =
Event::WindowEvent { window_id, event: WindowEvent::CursorEntered { device_id } };
callback(&self.target, event);
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id, window_id,
event: WindowEvent::CursorMoved { device_id, position }, event: WindowEvent::PointerEntered {
device_id,
position,
kind: PointerKind::Mouse,
},
}; };
callback(&self.target, event); callback(&self.target, event);
} }
@ -1193,8 +1216,10 @@ impl EventProcessor {
if self.window_exists(window) { if self.window_exists(window) {
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id: mkwid(window), window_id: mkwid(window),
event: WindowEvent::CursorLeft { event: WindowEvent::PointerLeft {
device_id: Some(mkdid(event.deviceid as xinput::DeviceId)), device_id: Some(mkdid(event.deviceid as xinput::DeviceId)),
position: Some(PhysicalPosition::new(event.event_x, event.event_y)),
kind: PointerKind::Mouse,
}, },
}; };
callback(&self.target, event); callback(&self.target, event);
@ -1253,7 +1278,7 @@ impl EventProcessor {
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id, window_id,
event: WindowEvent::CursorMoved { device_id, position }, event: WindowEvent::PointerMoved { device_id, position, source: PointerSource::Mouse },
}; };
callback(&self.target, event); callback(&self.target, event);
} }
@ -1309,7 +1334,7 @@ impl EventProcessor {
} }
} }
fn xinput2_touch<F>(&mut self, xev: &XIDeviceEvent, phase: TouchPhase, mut callback: F) fn xinput2_touch<F>(&mut self, xev: &XIDeviceEvent, phase: i32, mut callback: F)
where where
F: FnMut(&ActiveEventLoop, Event), F: FnMut(&ActiveEventLoop, Event),
{ {
@ -1320,29 +1345,81 @@ impl EventProcessor {
if self.window_exists(window) { if self.window_exists(window) {
let window_id = mkwid(window); let window_id = mkwid(window);
let id = xev.detail as u32; let id = xev.detail as u32;
let location = PhysicalPosition::new(xev.event_x, xev.event_y); let position = PhysicalPosition::new(xev.event_x, xev.event_y);
// Mouse cursor position changes when touch events are received. // Mouse cursor position changes when touch events are received.
// Only the first concurrently active touch ID moves the mouse cursor. // Only the first concurrently active touch ID moves the mouse cursor.
if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase) { if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase) {
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id, window_id,
event: WindowEvent::CursorMoved { device_id: None, position: location.cast() }, event: WindowEvent::PointerMoved {
device_id: None,
position: position.cast(),
source: PointerSource::Mouse,
},
}; };
callback(&self.target, event); callback(&self.target, event);
} }
let event = Event::WindowEvent { let device_id = Some(mkdid(xev.deviceid as xinput::DeviceId));
window_id, let finger_id = mkfid(id);
event: WindowEvent::Touch(Touch {
device_id: Some(mkdid(xev.deviceid as xinput::DeviceId)), match phase {
phase, xinput2::XI_TouchBegin => {
location, let event = Event::WindowEvent {
force: None, // TODO window_id,
finger_id: mkfid(id), event: WindowEvent::PointerEntered {
}), device_id,
}; position,
callback(&self.target, event) kind: PointerKind::Touch(finger_id),
},
};
callback(&self.target, event);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id,
state: ElementState::Pressed,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
};
callback(&self.target, event);
},
xinput2::XI_TouchUpdate => {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id,
position,
source: PointerSource::Touch { finger_id, force: None },
},
};
callback(&self.target, event);
},
xinput2::XI_TouchEnd => {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id,
state: ElementState::Released,
position,
button: ButtonSource::Touch { finger_id, force: None },
},
};
callback(&self.target, event);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
};
callback(&self.target, event);
},
_ => unreachable!(),
}
} }
} }
@ -1398,7 +1475,7 @@ impl EventProcessor {
if let Some(mouse_delta) = mouse_delta.consume() { if let Some(mouse_delta) = mouse_delta.consume() {
let event = Event::DeviceEvent { let event = Event::DeviceEvent {
device_id: did, device_id: did,
event: DeviceEvent::MouseMotion { delta: mouse_delta }, event: DeviceEvent::PointerMotion { delta: mouse_delta },
}; };
callback(&self.target, event); callback(&self.target, event);
} }
@ -1762,15 +1839,15 @@ impl EventProcessor {
} }
} }
fn is_first_touch(first: &mut Option<u32>, num: &mut u32, id: u32, phase: TouchPhase) -> bool { fn is_first_touch(first: &mut Option<u32>, num: &mut u32, id: u32, phase: i32) -> bool {
match phase { match phase {
TouchPhase::Started => { xinput2::XI_TouchBegin => {
if *num == 0 { if *num == 0 {
*first = Some(id); *first = Some(id);
} }
*num += 1; *num += 1;
}, },
TouchPhase::Cancelled | TouchPhase::Ended => { xinput2::XI_TouchEnd => {
if *first == Some(id) { if *first == Some(id) {
*first = None; *first = None;
} }

View file

@ -407,11 +407,15 @@ impl EventLoop {
app.window_event( app.window_event(
window_target, window_target,
RootWindowId(window_id), RootWindowId(window_id),
event::WindowEvent::CursorMoved { device_id: None, position: (x, y).into() }, event::WindowEvent::PointerMoved {
device_id: None,
position: (x, y).into(),
source: event::PointerSource::Mouse,
},
); );
}, },
EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => { EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => {
app.device_event(window_target, None, event::DeviceEvent::MouseMotion { app.device_event(window_target, None, event::DeviceEvent::PointerMotion {
delta: (dx as f64, dy as f64), delta: (dx as f64, dy as f64),
}); });
}, },
@ -420,7 +424,12 @@ impl EventLoop {
app.window_event( app.window_event(
window_target, window_target,
RootWindowId(window_id), RootWindowId(window_id),
event::WindowEvent::MouseInput { device_id: None, state, button }, event::WindowEvent::PointerButton {
device_id: None,
state,
position: dpi::PhysicalPosition::default(),
button: button.into(),
},
); );
} }
}, },
@ -469,9 +478,17 @@ impl EventLoop {
// TODO: Screen, Clipboard, Drop // TODO: Screen, Clipboard, Drop
EventOption::Hover(HoverEvent { entered }) => { EventOption::Hover(HoverEvent { entered }) => {
let event = if entered { let event = if entered {
event::WindowEvent::CursorEntered { device_id: None } event::WindowEvent::PointerEntered {
device_id: None,
position: dpi::PhysicalPosition::default(),
kind: event::PointerKind::Mouse,
}
} else { } else {
event::WindowEvent::CursorLeft { device_id: None } event::WindowEvent::PointerLeft {
device_id: None,
position: None,
kind: event::PointerKind::Mouse,
}
}; };
app.window_event(window_target, RootWindowId(window_id), event); app.window_event(window_target, RootWindowId(window_id), event);

View file

@ -1,5 +1,7 @@
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] use crate::event::FingerId as RootFingerId;
pub struct DeviceId(u32);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(pub(crate) u32);
impl DeviceId { impl DeviceId {
pub fn new(pointer_id: i32) -> Option<Self> { pub fn new(pointer_id: i32) -> Option<Self> {
@ -34,3 +36,9 @@ impl FingerId {
self.primary self.primary
} }
} }
impl From<FingerId> for RootFingerId {
fn from(id: FingerId) -> Self {
Self(id)
}
}

View file

@ -297,7 +297,7 @@ impl Shared {
runner.send_event(Event::DeviceEvent { runner.send_event(Event::DeviceEvent {
device_id, device_id,
event: DeviceEvent::Button { button: button.to_id(), state }, event: DeviceEvent::Button { button: button.to_id().into(), state },
}); });
return; return;
@ -310,7 +310,7 @@ impl Shared {
Event::DeviceEvent { Event::DeviceEvent {
device_id, device_id,
event: DeviceEvent::MouseMotion { delta: (delta.x, delta.y) }, event: DeviceEvent::PointerMotion { delta: (delta.x, delta.y) },
} }
})); }));
}), }),
@ -346,7 +346,7 @@ impl Shared {
runner.send_event(Event::DeviceEvent { runner.send_event(Event::DeviceEvent {
device_id: DeviceId::new(event.pointer_id()).map(RootDeviceId), device_id: DeviceId::new(event.pointer_id()).map(RootDeviceId),
event: DeviceEvent::Button { event: DeviceEvent::Button {
button: button.to_id(), button: button.to_id().into(),
state: ElementState::Pressed, state: ElementState::Pressed,
}, },
}); });
@ -365,7 +365,7 @@ impl Shared {
runner.send_event(Event::DeviceEvent { runner.send_event(Event::DeviceEvent {
device_id: DeviceId::new(event.pointer_id()).map(RootDeviceId), device_id: DeviceId::new(event.pointer_id()).map(RootDeviceId),
event: DeviceEvent::Button { event: DeviceEvent::Button {
button: button.to_id(), button: button.to_id().into(),
state: ElementState::Released, state: ElementState::Released,
}, },
}); });

View file

@ -12,8 +12,7 @@ use super::window::WindowId;
use super::{backend, runner, EventLoopProxy}; use super::{backend, runner, EventLoopProxy};
use crate::error::{NotSupportedError, RequestError}; use crate::error::{NotSupportedError, RequestError};
use crate::event::{ use crate::event::{
DeviceId as RootDeviceId, ElementState, Event, FingerId as RootFingerId, KeyEvent, Touch, DeviceId as RootDeviceId, ElementState, Event, KeyEvent, TouchPhase, WindowEvent,
TouchPhase, WindowEvent,
}; };
use crate::event_loop::{ use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
@ -200,12 +199,12 @@ impl ActiveEventLoop {
); );
let has_focus = canvas.has_focus.clone(); let has_focus = canvas.has_focus.clone();
canvas.on_cursor_leave({ canvas.on_pointer_leave({
let runner = self.runner.clone(); let runner = self.runner.clone();
let has_focus = has_focus.clone(); let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone(); let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id| { move |active_modifiers, device_id, position, kind| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers); modifiers.set(active_modifiers);
Event::WindowEvent { Event::WindowEvent {
@ -214,23 +213,23 @@ impl ActiveEventLoop {
} }
}); });
let pointer = pointer_id.map(|device_id| Event::WindowEvent { runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::CursorLeft { device_id: device_id.map(RootDeviceId) }, event: WindowEvent::PointerLeft {
}); device_id: device_id.map(RootDeviceId),
position: Some(position),
if focus.is_some() || pointer.is_some() { kind,
runner.send_events(focus.into_iter().chain(pointer)) },
} })))
} }
}); });
canvas.on_cursor_enter({ canvas.on_pointer_enter({
let runner = self.runner.clone(); let runner = self.runner.clone();
let has_focus = has_focus.clone(); let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone(); let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id| { move |active_modifiers, device_id, position, kind| {
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers); modifiers.set(active_modifiers);
Event::WindowEvent { Event::WindowEvent {
@ -239,41 +238,41 @@ impl ActiveEventLoop {
} }
}); });
let pointer = pointer_id.map(|device_id| Event::WindowEvent { runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::CursorEntered { device_id: device_id.map(RootDeviceId) }, event: WindowEvent::PointerEntered {
}); device_id: device_id.map(RootDeviceId),
position,
if focus.is_some() || pointer.is_some() { kind,
runner.send_events(focus.into_iter().chain(pointer)) },
} })))
} }
}); });
canvas.on_cursor_move( canvas.on_pointer_move(
{ {
let runner = self.runner.clone(); let runner = self.runner.clone();
let has_focus = has_focus.clone(); let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone(); let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id, events| { move |pointer_id, events| {
let modifiers = runner.send_events(events.flat_map(|(active_modifiers, position, source)| {
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers.into_iter().chain(events.flat_map(|position| {
let device_id = pointer_id.map(RootDeviceId); let device_id = pointer_id.map(RootDeviceId);
iter::once(Event::WindowEvent { let modifiers = (has_focus.get() && modifiers.get() != active_modifiers)
.then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { device_id, position }, event: WindowEvent::PointerMoved { device_id, position, source },
}) }))
}))); }));
} }
}, },
{ {
@ -281,40 +280,7 @@ impl ActiveEventLoop {
let has_focus = has_focus.clone(); let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone(); let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, finger_id, events| { move |active_modifiers, device_id, position, state, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers.into_iter().chain(events.map(
|(location, force)| Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Touch(Touch {
finger_id: RootFingerId(finger_id),
device_id: device_id.map(RootDeviceId),
phase: TouchPhase::Moved,
force: Some(force),
location,
}),
},
)));
}
},
{
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers,
device_id,
position: crate::dpi::PhysicalPosition<f64>,
buttons,
button| {
let modifiers = let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| { (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers); modifiers.set(active_modifiers);
@ -326,36 +292,48 @@ impl ActiveEventLoop {
let device_id = device_id.map(RootDeviceId); let device_id = device_id.map(RootDeviceId);
let state = if buttons.contains(button.into()) { runner.send_events(modifiers.into_iter().chain([Event::WindowEvent {
ElementState::Pressed window_id: RootWindowId(id),
} else { event: WindowEvent::PointerButton { device_id, state, position, button },
ElementState::Released }]));
};
// A chorded button event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
runner.send_events(modifiers.into_iter().chain([
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { device_id, position },
},
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput { device_id, state, button },
},
]));
} }
}, },
); );
canvas.on_mouse_press( canvas.on_pointer_press({
{ let runner = self.runner.clone();
let runner = self.runner.clone(); let modifiers = self.modifiers.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id, position, button| { move |active_modifiers, pointer_id, position, button| {
let modifiers = (modifiers.get() != active_modifiers).then(|| { let modifiers = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let device_id = pointer_id.map(RootDeviceId);
runner.send_events(modifiers.into_iter().chain(iter::once(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::PointerButton {
device_id,
state: ElementState::Pressed,
position,
button,
},
})));
}
});
canvas.on_pointer_release({
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id, position, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers); modifiers.set(active_modifiers);
Event::WindowEvent { Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
@ -363,123 +341,19 @@ impl ActiveEventLoop {
} }
}); });
let device_id = pointer_id.map(RootDeviceId); let device_id = pointer_id.map(RootDeviceId);
// A mouse down event may come in without any prior CursorMoved events, runner.send_events(modifiers.into_iter().chain(iter::once(Event::WindowEvent {
// therefore we should send a CursorMoved event to make sure that the window_id: RootWindowId(id),
// user code has the correct cursor position. event: WindowEvent::PointerButton {
runner.send_events(modifiers.into_iter().chain([ device_id,
Event::WindowEvent { state: ElementState::Released,
window_id: RootWindowId(id), position,
event: WindowEvent::CursorMoved { device_id, position }, button,
}, },
Event::WindowEvent { })));
window_id: RootWindowId(id), }
event: WindowEvent::MouseInput { });
device_id,
state: ElementState::Pressed,
button,
},
},
]));
}
},
{
let runner = self.runner.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, finger_id, location, force| {
let modifiers = (modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers.into_iter().chain(iter::once(
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Touch(Touch {
finger_id: RootFingerId(finger_id),
device_id: device_id.map(RootDeviceId),
phase: TouchPhase::Started,
force: Some(force),
location,
}),
},
)))
}
},
);
canvas.on_mouse_release(
{
let runner = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id, position, button| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let device_id = pointer_id.map(RootDeviceId);
// A mouse up event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
runner.send_events(modifiers.into_iter().chain([
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved { device_id, position },
},
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id,
state: ElementState::Released,
button,
},
},
]));
}
},
{
let runner_touch = self.runner.clone();
let has_focus = has_focus.clone();
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, finger_id, location, force| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner_touch.send_events(modifiers.into_iter().chain(iter::once(
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Touch(Touch {
finger_id: RootFingerId(finger_id),
device_id: device_id.map(RootDeviceId),
phase: TouchPhase::Ended,
force: Some(force),
location,
}),
},
)));
}
},
);
let runner = self.runner.clone(); let runner = self.runner.clone();
let modifiers = self.modifiers.clone(); let modifiers = self.modifiers.clone();
@ -505,20 +379,6 @@ impl ActiveEventLoop {
))); )));
}); });
let runner = self.runner.clone();
canvas.on_touch_cancel(move |device_id, finger_id, location, force| {
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Touch(Touch {
finger_id: RootFingerId(finger_id),
device_id: device_id.map(RootDeviceId),
phase: TouchPhase::Cancelled,
force: Some(force),
location,
}),
});
});
let runner = self.runner.clone(); let runner = self.runner.clone();
canvas.on_dark_mode(move |is_dark_mode| { canvas.on_dark_mode(move |is_dark_mode| {
let theme = if is_dark_mode { Theme::Dark } else { Theme::Light }; let theme = if is_dark_mode { Theme::Dark } else { Theme::Light };

View file

@ -12,7 +12,7 @@ use web_sys::{
}; };
use super::super::cursor::CursorHandler; use super::super::cursor::CursorHandler;
use super::super::event::{DeviceId, FingerId}; use super::super::event::DeviceId;
use super::super::main_thread::MainThreadMarker; use super::super::main_thread::MainThreadMarker;
use super::super::WindowId; use super::super::WindowId;
use super::animation_frame::AnimationFrameHandler; use super::animation_frame::AnimationFrameHandler;
@ -20,10 +20,12 @@ use super::event_handle::EventListenerHandle;
use super::intersection_handle::IntersectionObserverHandle; use super::intersection_handle::IntersectionObserverHandle;
use super::media_query_handle::MediaQueryListHandle; use super::media_query_handle::MediaQueryListHandle;
use super::pointer::PointerHandler; use super::pointer::PointerHandler;
use super::{event, fullscreen, ButtonsState, ResizeScaleHandle}; use super::{event, fullscreen, ResizeScaleHandle};
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::error::RequestError; use crate::error::RequestError;
use crate::event::{Force, MouseButton, MouseScrollDelta, SurfaceSizeWriter}; use crate::event::{
ButtonSource, ElementState, MouseScrollDelta, PointerKind, PointerSource, SurfaceSizeWriter,
};
use crate::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey}; use crate::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey};
use crate::platform_impl::Fullscreen; use crate::platform_impl::Fullscreen;
use crate::window::{WindowAttributes, WindowId as RootWindowId}; use crate::window::{WindowAttributes, WindowId as RootWindowId};
@ -328,83 +330,62 @@ impl Canvas {
})); }));
} }
pub fn on_cursor_leave<F>(&self, handler: F) pub fn on_pointer_leave<F>(&self, handler: F)
where where
F: 'static + FnMut(ModifiersState, Option<Option<DeviceId>>), F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{ {
self.handlers.borrow_mut().pointer_handler.on_cursor_leave(&self.common, handler) self.handlers.borrow_mut().pointer_handler.on_pointer_leave(&self.common, handler)
} }
pub fn on_cursor_enter<F>(&self, handler: F) pub fn on_pointer_enter<F>(&self, handler: F)
where where
F: 'static + FnMut(ModifiersState, Option<Option<DeviceId>>), F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{ {
self.handlers.borrow_mut().pointer_handler.on_cursor_enter(&self.common, handler) self.handlers.borrow_mut().pointer_handler.on_pointer_enter(&self.common, handler)
} }
pub fn on_mouse_release<M, T>(&self, mouse_handler: M, touch_handler: T) pub fn on_pointer_release<C>(&self, handler: C)
where where
M: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, MouseButton), C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
T: 'static
+ FnMut(ModifiersState, Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
{ {
self.handlers.borrow_mut().pointer_handler.on_mouse_release( self.handlers.borrow_mut().pointer_handler.on_pointer_release(&self.common, handler)
}
pub fn on_pointer_press<C>(&self, handler: C)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_press(
&self.common, &self.common,
mouse_handler, handler,
touch_handler,
)
}
pub fn on_mouse_press<M, T>(&self, mouse_handler: M, touch_handler: T)
where
M: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, MouseButton),
T: 'static
+ FnMut(ModifiersState, Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
{
self.handlers.borrow_mut().pointer_handler.on_mouse_press(
&self.common,
mouse_handler,
touch_handler,
Rc::clone(&self.prevent_default), Rc::clone(&self.prevent_default),
) )
} }
pub fn on_cursor_move<M, T, B>(&self, mouse_handler: M, touch_handler: T, button_handler: B) pub fn on_pointer_move<C, B>(&self, cursor_handler: C, button_handler: B)
where where
M: 'static C: 'static
+ FnMut(ModifiersState, Option<DeviceId>, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
T: 'static
+ FnMut( + FnMut(
ModifiersState,
Option<DeviceId>, Option<DeviceId>,
FingerId, &mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
&mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>,
), ),
B: 'static B: 'static
+ FnMut( + FnMut(
ModifiersState, ModifiersState,
Option<DeviceId>, Option<DeviceId>,
PhysicalPosition<f64>, PhysicalPosition<f64>,
ButtonsState, ElementState,
MouseButton, ButtonSource,
), ),
{ {
self.handlers.borrow_mut().pointer_handler.on_cursor_move( self.handlers.borrow_mut().pointer_handler.on_pointer_move(
&self.common, &self.common,
mouse_handler, cursor_handler,
touch_handler,
button_handler, button_handler,
Rc::clone(&self.prevent_default), Rc::clone(&self.prevent_default),
) )
} }
pub fn on_touch_cancel<F>(&self, handler: F)
where
F: 'static + FnMut(Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
{
self.handlers.borrow_mut().pointer_handler.on_touch_cancel(&self.common, handler)
}
pub fn on_mouse_wheel<F>(&self, mut handler: F) pub fn on_mouse_wheel<F>(&self, mut handler: F)
where where
F: 'static + FnMut(MouseScrollDelta, ModifiersState), F: 'static + FnMut(MouseScrollDelta, ModifiersState),

View file

@ -6,8 +6,9 @@ use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen::{JsCast, JsValue};
use web_sys::{KeyboardEvent, MouseEvent, Navigator, PointerEvent, WheelEvent}; use web_sys::{KeyboardEvent, MouseEvent, Navigator, PointerEvent, WheelEvent};
use super::super::FingerId;
use super::Engine; use super::Engine;
use crate::event::{MouseButton, MouseScrollDelta}; use crate::event::{MouseButton, MouseScrollDelta, PointerKind};
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey}; use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
bitflags::bitflags! { bitflags::bitflags! {
@ -68,14 +69,14 @@ pub fn mouse_button(event: &MouseEvent) -> Option<MouseButton> {
} }
impl MouseButton { impl MouseButton {
pub fn to_id(self) -> u32 { pub fn to_id(self) -> u16 {
match self { match self {
MouseButton::Left => 0, MouseButton::Left => 0,
MouseButton::Right => 1, MouseButton::Right => 1,
MouseButton::Middle => 2, MouseButton::Middle => 2,
MouseButton::Back => 3, MouseButton::Back => 3,
MouseButton::Forward => 4, MouseButton::Forward => 4,
MouseButton::Other(value) => value.into(), MouseButton::Other(value) => value,
} }
} }
} }
@ -160,6 +161,14 @@ pub fn mouse_scroll_delta(
} }
} }
pub fn pointer_type(event: &PointerEvent, pointer_id: i32) -> PointerKind {
match event.pointer_type().as_str() {
"mouse" => PointerKind::Mouse,
"touch" => PointerKind::Touch(FingerId::new(pointer_id, event.is_primary()).into()),
_ => PointerKind::Unknown,
}
}
pub fn key_code(event: &KeyboardEvent) -> PhysicalKey { pub fn key_code(event: &KeyboardEvent) -> PhysicalKey {
let code = event.code(); let code = event.code();
PhysicalKey::from_key_code_attribute_value(&code) PhysicalKey::from_key_code_attribute_value(&code)

View file

@ -18,7 +18,6 @@ use wasm_bindgen::JsCast;
use web_sys::{Document, HtmlCanvasElement, Navigator, PageTransitionEvent, VisibilityState}; use web_sys::{Document, HtmlCanvasElement, Navigator, PageTransitionEvent, VisibilityState};
pub use self::canvas::{Canvas, Style}; pub use self::canvas::{Canvas, Style};
pub use self::event::ButtonsState;
pub use self::event_handle::EventListenerHandle; pub use self::event_handle::EventListenerHandle;
pub use self::resize_scaling::ResizeScaleHandle; pub use self::resize_scaling::ResizeScaleHandle;
pub use self::schedule::Schedule; pub use self::schedule::Schedule;

View file

@ -1,15 +1,14 @@
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
use event::ButtonsState;
use web_sys::PointerEvent; use web_sys::PointerEvent;
use super::super::event::{DeviceId, FingerId}; use super::super::event::DeviceId;
use super::canvas::Common; use super::canvas::Common;
use super::event; use super::event;
use super::event_handle::EventListenerHandle; use super::event_handle::EventListenerHandle;
use crate::dpi::PhysicalPosition; use crate::dpi::PhysicalPosition;
use crate::event::{Force, MouseButton}; use crate::event::{ButtonSource, ElementState, Force, PointerKind, PointerSource};
use crate::keyboard::ModifiersState; use crate::keyboard::ModifiersState;
#[allow(dead_code)] #[allow(dead_code)]
@ -34,88 +33,78 @@ impl PointerHandler {
} }
} }
pub fn on_cursor_leave<F>(&mut self, canvas_common: &Common, mut handler: F) pub fn on_pointer_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
where where
F: 'static + FnMut(ModifiersState, Option<Option<DeviceId>>), F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{ {
let window = canvas_common.window.clone();
self.on_cursor_leave = self.on_cursor_leave =
Some(canvas_common.add_event("pointerout", move |event: PointerEvent| { Some(canvas_common.add_event("pointerout", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event); let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
// touch events are handled separately let device_id = DeviceId::new(pointer_id);
// handling them here would produce duplicate mouse events, inconsistent with let position =
// other platforms. event::mouse_position(&event).to_physical(super::scale_factor(&window));
let device_id = let kind = event::pointer_type(&event, pointer_id);
(event.pointer_type() != "touch").then(|| DeviceId::new(event.pointer_id())); handler(modifiers, device_id, position, kind);
handler(modifiers, device_id);
})); }));
} }
pub fn on_cursor_enter<F>(&mut self, canvas_common: &Common, mut handler: F) pub fn on_pointer_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
where where
F: 'static + FnMut(ModifiersState, Option<Option<DeviceId>>), F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{ {
let window = canvas_common.window.clone();
self.on_cursor_enter = self.on_cursor_enter =
Some(canvas_common.add_event("pointerover", move |event: PointerEvent| { Some(canvas_common.add_event("pointerover", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event); let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
// touch events are handled separately let device_id = DeviceId::new(pointer_id);
// handling them here would produce duplicate mouse events, inconsistent with let position =
// other platforms. event::mouse_position(&event).to_physical(super::scale_factor(&window));
let device_id = let kind = event::pointer_type(&event, pointer_id);
(event.pointer_type() != "touch").then(|| DeviceId::new(event.pointer_id())); handler(modifiers, device_id, position, kind);
handler(modifiers, device_id);
})); }));
} }
pub fn on_mouse_release<M, T>( pub fn on_pointer_release<C>(&mut self, canvas_common: &Common, mut handler: C)
&mut self, where
canvas_common: &Common, C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
mut mouse_handler: M,
mut touch_handler: T,
) where
M: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, MouseButton),
T: 'static
+ FnMut(ModifiersState, Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
{ {
let window = canvas_common.window.clone(); let window = canvas_common.window.clone();
self.on_pointer_release = self.on_pointer_release =
Some(canvas_common.add_event("pointerup", move |event: PointerEvent| { Some(canvas_common.add_event("pointerup", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event); let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
let kind = event::pointer_type(&event, pointer_id);
match event.pointer_type().as_str() { let button = event::mouse_button(&event).expect("no mouse button pressed");
"touch" => {
let pointer_id = event.pointer_id(); let source = match kind {
touch_handler( PointerKind::Mouse => ButtonSource::Mouse(button),
modifiers, PointerKind::Touch(finger_id) => ButtonSource::Touch {
DeviceId::new(pointer_id), finger_id,
FingerId::new(pointer_id, event.is_primary()), force: Some(Force::Normalized(event.pressure().into())),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
Force::Normalized(event.pressure() as f64),
)
}, },
_ => mouse_handler( PointerKind::Unknown => ButtonSource::Unknown(button.to_id()),
modifiers, };
DeviceId::new(event.pointer_id()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)), handler(
event::mouse_button(&event).expect("no mouse button released"), modifiers,
), DeviceId::new(pointer_id),
} event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
})); }));
} }
pub fn on_mouse_press<M, T>( pub fn on_pointer_press<C>(
&mut self, &mut self,
canvas_common: &Common, canvas_common: &Common,
mut mouse_handler: M, mut handler: C,
mut touch_handler: T,
prevent_default: Rc<Cell<bool>>, prevent_default: Rc<Cell<bool>>,
) where ) where
M: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, MouseButton), C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
T: 'static
+ FnMut(ModifiersState, Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
{ {
let window = canvas_common.window.clone(); let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone(); let canvas = canvas_common.raw().clone();
@ -129,75 +118,65 @@ impl PointerHandler {
} }
let modifiers = event::mouse_modifiers(&event); let modifiers = event::mouse_modifiers(&event);
let pointer_type = &event.pointer_type(); let pointer_id = event.pointer_id();
let kind = event::pointer_type(&event, pointer_id);
let button = event::mouse_button(&event).expect("no mouse button pressed");
match pointer_type.as_str() { let source = match kind {
"touch" => { PointerKind::Mouse => {
let pointer_id = event.pointer_id(); // Error is swallowed here since the error would occur every time the
touch_handler( // mouse is clicked when the cursor is
modifiers, // grabbed, and there is probably not a
DeviceId::new(pointer_id), // situation where this could fail, that we
FingerId::new(pointer_id, event.is_primary()), // care if it fails.
event::mouse_position(&event).to_physical(super::scale_factor(&window)), let _e = canvas.set_pointer_capture(pointer_id);
Force::Normalized(event.pressure() as f64),
); ButtonSource::Mouse(button)
}, },
_ => { PointerKind::Touch(finger_id) => ButtonSource::Touch {
let pointer_id = event.pointer_id(); finger_id,
force: Some(Force::Normalized(event.pressure().into())),
mouse_handler(
modifiers,
DeviceId::new(pointer_id),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
event::mouse_button(&event).expect("no mouse button pressed"),
);
if pointer_type == "mouse" {
// Error is swallowed here since the error would occur every time the
// mouse is clicked when the cursor is
// grabbed, and there is probably not a
// situation where this could fail, that we
// care if it fails.
let _e = canvas.set_pointer_capture(pointer_id);
}
}, },
} PointerKind::Unknown => ButtonSource::Unknown(button.to_id()),
};
handler(
modifiers,
DeviceId::new(pointer_id),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
})); }));
} }
pub fn on_cursor_move<M, T, B>( pub fn on_pointer_move<C, B>(
&mut self, &mut self,
canvas_common: &Common, canvas_common: &Common,
mut mouse_handler: M, mut cursor_handler: C,
mut touch_handler: T,
mut button_handler: B, mut button_handler: B,
prevent_default: Rc<Cell<bool>>, prevent_default: Rc<Cell<bool>>,
) where ) where
M: 'static C: 'static
+ FnMut(ModifiersState, Option<DeviceId>, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
T: 'static
+ FnMut( + FnMut(
ModifiersState,
Option<DeviceId>, Option<DeviceId>,
FingerId, &mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
&mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>,
), ),
B: 'static B: 'static
+ FnMut( + FnMut(
ModifiersState, ModifiersState,
Option<DeviceId>, Option<DeviceId>,
PhysicalPosition<f64>, PhysicalPosition<f64>,
ButtonsState, ElementState,
MouseButton, ButtonSource,
), ),
{ {
let window = canvas_common.window.clone(); let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone(); let canvas = canvas_common.raw().clone();
self.on_cursor_move = self.on_cursor_move =
Some(canvas_common.add_event("pointermove", move |event: PointerEvent| { Some(canvas_common.add_event("pointermove", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id(); let pointer_id = event.pointer_id();
let device_id = DeviceId::new(pointer_id); let device_id = DeviceId::new(pointer_id);
let kind = event::pointer_type(&event, pointer_id);
// chorded button event // chorded button event
if let Some(button) = event::mouse_button(&event) { if let Some(button) = event::mouse_button(&event) {
@ -208,11 +187,34 @@ impl PointerHandler {
let _ = canvas.focus(); let _ = canvas.focus();
} }
let state = if event::mouse_buttons(&event).contains(button.into()) {
ElementState::Pressed
} else {
ElementState::Released
};
let button = match kind {
PointerKind::Mouse => ButtonSource::Mouse(button),
PointerKind::Touch(finger_id) => {
let button_id = button.to_id();
if button_id != 1 {
tracing::error!("unexpected touch button id: {button_id}");
}
ButtonSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
}
},
PointerKind::Unknown => todo!(),
};
button_handler( button_handler(
modifiers, event::mouse_modifiers(&event),
device_id, device_id,
event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_position(&event).to_physical(super::scale_factor(&window)),
event::mouse_buttons(&event), state,
button, button,
); );
@ -221,44 +223,24 @@ impl PointerHandler {
// pointer move event // pointer move event
let scale = super::scale_factor(&window); let scale = super::scale_factor(&window);
match event.pointer_type().as_str() {
"touch" => touch_handler(
modifiers,
device_id,
FingerId::new(pointer_id, event.is_primary()),
&mut event::pointer_move_event(event).map(|event| {
(
event::mouse_position(&event).to_physical(scale),
Force::Normalized(event.pressure() as f64),
)
}),
),
_ => mouse_handler(
modifiers,
device_id,
&mut event::pointer_move_event(event)
.map(|event| event::mouse_position(&event).to_physical(scale)),
),
};
}));
}
pub fn on_touch_cancel<F>(&mut self, canvas_common: &Common, mut handler: F) cursor_handler(
where device_id,
F: 'static + FnMut(Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force), &mut event::pointer_move_event(event).map(|event| {
{ (
let window = canvas_common.window.clone(); event::mouse_modifiers(&event),
self.on_touch_cancel = event::mouse_position(&event).to_physical(scale),
Some(canvas_common.add_event("pointercancel", move |event: PointerEvent| { match kind {
if event.pointer_type() == "touch" { PointerKind::Mouse => PointerSource::Mouse,
let pointer_id = event.pointer_id(); PointerKind::Touch(finger_id) => PointerSource::Touch {
handler( finger_id,
DeviceId::new(pointer_id), force: Some(Force::Normalized(event.pressure().into())),
FingerId::new(pointer_id, event.is_primary()), },
event::mouse_position(&event).to_physical(super::scale_factor(&window)), PointerKind::Unknown => PointerSource::Unknown,
Force::Normalized(event.pressure() as f64), },
); )
} }),
);
})); }));
} }

View file

@ -37,9 +37,9 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW, GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos, TranslateMessage, CREATESTRUCTW, RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos, TranslateMessage, CREATESTRUCTW,
GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS,
PM_REMOVE, PT_PEN, PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, PM_REMOVE, PT_TOUCH, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM,
WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT,
WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS,
@ -58,7 +58,7 @@ use crate::application::ApplicationHandler;
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::{EventLoopError, RequestError}; use crate::error::{EventLoopError, RequestError};
use crate::event::{ use crate::event::{
Event, FingerId as RootFingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, Touch, TouchPhase, Event, FingerId as RootFingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase,
WindowEvent, WindowEvent,
}; };
use crate::event_loop::{ use crate::event_loop::{
@ -1520,7 +1520,8 @@ unsafe fn public_window_callback_inner(
}, },
WM_MOUSEMOVE => { WM_MOUSEMOVE => {
use crate::event::WindowEvent::{CursorEntered, CursorLeft, CursorMoved}; use crate::event::WindowEvent::{PointerEntered, PointerLeft, PointerMoved};
use crate::event::{PointerKind, PointerSource};
let x = super::get_x_lparam(lparam as u32) as i32; let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32; let y = super::get_y_lparam(lparam as u32) as i32;
@ -1541,7 +1542,11 @@ unsafe fn public_window_callback_inner(
drop(w); drop(w);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: CursorEntered { device_id: None }, event: PointerEntered {
device_id: None,
position,
kind: PointerKind::Mouse,
},
}); });
// Calling TrackMouseEvent in order to receive mouse leave events. // Calling TrackMouseEvent in order to receive mouse leave events.
@ -1562,7 +1567,11 @@ unsafe fn public_window_callback_inner(
drop(w); drop(w);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: CursorLeft { device_id: None }, event: PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Mouse,
},
}); });
}, },
PointerMoveKind::None => drop(w), PointerMoveKind::None => drop(w),
@ -1581,7 +1590,7 @@ unsafe fn public_window_callback_inner(
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: CursorMoved { device_id: None, position }, event: PointerMoved { device_id: None, position, source: PointerSource::Mouse },
}); });
} }
@ -1589,7 +1598,9 @@ unsafe fn public_window_callback_inner(
}, },
WM_MOUSELEAVE => { WM_MOUSELEAVE => {
use crate::event::WindowEvent::CursorLeft; use crate::event::PointerKind::Mouse;
use crate::event::WindowEvent::PointerLeft;
{ {
let mut w = userdata.window_state_lock(); let mut w = userdata.window_state_lock();
w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)).ok(); w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)).ok();
@ -1597,7 +1608,7 @@ unsafe fn public_window_callback_inner(
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: CursorLeft { device_id: None }, event: PointerLeft { device_id: None, position: None, kind: Mouse },
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
@ -1660,15 +1671,24 @@ unsafe fn public_window_callback_inner(
WM_LBUTTONDOWN => { WM_LBUTTONDOWN => {
use crate::event::ElementState::Pressed; use crate::event::ElementState::Pressed;
use crate::event::MouseButton::Left; use crate::event::MouseButton::Left;
use crate::event::WindowEvent::MouseInput; use crate::event::WindowEvent::PointerButton;
unsafe { capture_mouse(window, &mut userdata.window_state_lock()) }; unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
update_modifiers(window, userdata); update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: MouseInput { device_id: None, state: Pressed, button: Left }, event: PointerButton {
device_id: None,
state: Pressed,
position,
button: Left.into(),
},
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
}, },
@ -1676,15 +1696,24 @@ unsafe fn public_window_callback_inner(
WM_LBUTTONUP => { WM_LBUTTONUP => {
use crate::event::ElementState::Released; use crate::event::ElementState::Released;
use crate::event::MouseButton::Left; use crate::event::MouseButton::Left;
use crate::event::WindowEvent::MouseInput; use crate::event::WindowEvent::PointerButton;
unsafe { release_mouse(userdata.window_state_lock()) }; unsafe { release_mouse(userdata.window_state_lock()) };
update_modifiers(window, userdata); update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: MouseInput { device_id: None, state: Released, button: Left }, event: PointerButton {
device_id: None,
state: Released,
position,
button: Left.into(),
},
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
}, },
@ -1692,15 +1721,24 @@ unsafe fn public_window_callback_inner(
WM_RBUTTONDOWN => { WM_RBUTTONDOWN => {
use crate::event::ElementState::Pressed; use crate::event::ElementState::Pressed;
use crate::event::MouseButton::Right; use crate::event::MouseButton::Right;
use crate::event::WindowEvent::MouseInput; use crate::event::WindowEvent::PointerButton;
unsafe { capture_mouse(window, &mut userdata.window_state_lock()) }; unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
update_modifiers(window, userdata); update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: MouseInput { device_id: None, state: Pressed, button: Right }, event: PointerButton {
device_id: None,
state: Pressed,
position,
button: Right.into(),
},
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
}, },
@ -1708,15 +1746,24 @@ unsafe fn public_window_callback_inner(
WM_RBUTTONUP => { WM_RBUTTONUP => {
use crate::event::ElementState::Released; use crate::event::ElementState::Released;
use crate::event::MouseButton::Right; use crate::event::MouseButton::Right;
use crate::event::WindowEvent::MouseInput; use crate::event::WindowEvent::PointerButton;
unsafe { release_mouse(userdata.window_state_lock()) }; unsafe { release_mouse(userdata.window_state_lock()) };
update_modifiers(window, userdata); update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: MouseInput { device_id: None, state: Released, button: Right }, event: PointerButton {
device_id: None,
state: Released,
position,
button: Right.into(),
},
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
}, },
@ -1724,15 +1771,24 @@ unsafe fn public_window_callback_inner(
WM_MBUTTONDOWN => { WM_MBUTTONDOWN => {
use crate::event::ElementState::Pressed; use crate::event::ElementState::Pressed;
use crate::event::MouseButton::Middle; use crate::event::MouseButton::Middle;
use crate::event::WindowEvent::MouseInput; use crate::event::WindowEvent::PointerButton;
unsafe { capture_mouse(window, &mut userdata.window_state_lock()) }; unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
update_modifiers(window, userdata); update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: MouseInput { device_id: None, state: Pressed, button: Middle }, event: PointerButton {
device_id: None,
state: Pressed,
position,
button: Middle.into(),
},
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
}, },
@ -1740,15 +1796,24 @@ unsafe fn public_window_callback_inner(
WM_MBUTTONUP => { WM_MBUTTONUP => {
use crate::event::ElementState::Released; use crate::event::ElementState::Released;
use crate::event::MouseButton::Middle; use crate::event::MouseButton::Middle;
use crate::event::WindowEvent::MouseInput; use crate::event::WindowEvent::PointerButton;
unsafe { release_mouse(userdata.window_state_lock()) }; unsafe { release_mouse(userdata.window_state_lock()) };
update_modifiers(window, userdata); update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: MouseInput { device_id: None, state: Released, button: Middle }, event: PointerButton {
device_id: None,
state: Released,
position,
button: Middle.into(),
},
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
}, },
@ -1756,23 +1821,29 @@ unsafe fn public_window_callback_inner(
WM_XBUTTONDOWN => { WM_XBUTTONDOWN => {
use crate::event::ElementState::Pressed; use crate::event::ElementState::Pressed;
use crate::event::MouseButton::{Back, Forward, Other}; use crate::event::MouseButton::{Back, Forward, Other};
use crate::event::WindowEvent::MouseInput; use crate::event::WindowEvent::PointerButton;
let xbutton = super::get_xbutton_wparam(wparam as u32); let xbutton = super::get_xbutton_wparam(wparam as u32);
unsafe { capture_mouse(window, &mut userdata.window_state_lock()) }; unsafe { capture_mouse(window, &mut userdata.window_state_lock()) };
update_modifiers(window, userdata); update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: MouseInput { event: PointerButton {
device_id: None, device_id: None,
state: Pressed, state: Pressed,
position,
button: match xbutton { button: match xbutton {
1 => Back, 1 => Back,
2 => Forward, 2 => Forward,
_ => Other(xbutton), _ => Other(xbutton),
}, }
.into(),
}, },
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
@ -1781,23 +1852,29 @@ unsafe fn public_window_callback_inner(
WM_XBUTTONUP => { WM_XBUTTONUP => {
use crate::event::ElementState::Released; use crate::event::ElementState::Released;
use crate::event::MouseButton::{Back, Forward, Other}; use crate::event::MouseButton::{Back, Forward, Other};
use crate::event::WindowEvent::MouseInput; use crate::event::WindowEvent::PointerButton;
let xbutton = super::get_xbutton_wparam(wparam as u32); let xbutton = super::get_xbutton_wparam(wparam as u32);
unsafe { release_mouse(userdata.window_state_lock()) }; unsafe { release_mouse(userdata.window_state_lock()) };
update_modifiers(window, userdata); update_modifiers(window, userdata);
let x = super::get_x_lparam(lparam as u32) as i32;
let y = super::get_y_lparam(lparam as u32) as i32;
let position = PhysicalPosition::new(x as f64, y as f64);
userdata.send_event(Event::WindowEvent { userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), window_id: CoreWindowId(WindowId(window)),
event: MouseInput { event: PointerButton {
device_id: None, device_id: None,
state: Released, state: Released,
position,
button: match xbutton { button: match xbutton {
1 => Back, 1 => Back,
2 => Forward, 2 => Forward,
_ => Other(xbutton), _ => Other(xbutton),
}, }
.into(),
}, },
}); });
result = ProcResult::Value(0); result = ProcResult::Value(0);
@ -1815,6 +1892,10 @@ unsafe fn public_window_callback_inner(
}, },
WM_TOUCH => { WM_TOUCH => {
use crate::event::ButtonSource::Touch;
use crate::event::ElementState::{Pressed, Released};
use crate::event::{PointerKind, PointerSource};
let pcount = super::loword(wparam as u32) as usize; let pcount = super::loword(wparam as u32) as usize;
let mut inputs = Vec::with_capacity(pcount); let mut inputs = Vec::with_capacity(pcount);
let htouch = lparam; let htouch = lparam;
@ -1828,36 +1909,70 @@ unsafe fn public_window_callback_inner(
} { } {
unsafe { inputs.set_len(pcount) }; unsafe { inputs.set_len(pcount) };
for input in &inputs { for input in &inputs {
let mut location = POINT { x: input.x / 100, y: input.y / 100 }; let mut position = POINT { x: input.x / 100, y: input.y / 100 };
if unsafe { ScreenToClient(window, &mut location) } == false.into() { if unsafe { ScreenToClient(window, &mut position) } == false.into() {
continue; continue;
} }
let x = location.x as f64 + (input.x % 100) as f64 / 100f64; let x = position.x as f64 + (input.x % 100) as f64 / 100f64;
let y = location.y as f64 + (input.y % 100) as f64 / 100f64; let y = position.y as f64 + (input.y % 100) as f64 / 100f64;
let location = PhysicalPosition::new(x, y); let position = PhysicalPosition::new(x, y);
userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), let window_id = CoreWindowId(WindowId(window));
event: WindowEvent::Touch(Touch { let finger_id = RootFingerId(FingerId {
phase: if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) { id: input.dwID,
TouchPhase::Started primary: util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY),
} else if util::has_flag(input.dwFlags, TOUCHEVENTF_UP) {
TouchPhase::Ended
} else if util::has_flag(input.dwFlags, TOUCHEVENTF_MOVE) {
TouchPhase::Moved
} else {
continue;
},
location,
force: None, // WM_TOUCH doesn't support pressure information
finger_id: RootFingerId(FingerId {
id: input.dwID,
primary: util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY),
}),
device_id: None,
}),
}); });
if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id: None,
position,
kind: PointerKind::Touch(finger_id),
},
});
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: Pressed,
position,
button: Touch { finger_id, force: None },
},
});
} else if util::has_flag(input.dwFlags, TOUCHEVENTF_UP) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: Released,
position,
button: Touch { finger_id, force: None },
},
});
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: PointerKind::Touch(finger_id),
},
});
} else if util::has_flag(input.dwFlags, TOUCHEVENTF_MOVE) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
position,
source: PointerSource::Touch { finger_id, force: None },
},
});
} else {
continue;
}
} }
} }
unsafe { CloseTouchInputHandle(htouch) }; unsafe { CloseTouchInputHandle(htouch) };
@ -1865,6 +1980,9 @@ unsafe fn public_window_callback_inner(
}, },
WM_POINTERDOWN | WM_POINTERUPDATE | WM_POINTERUP => { WM_POINTERDOWN | WM_POINTERUPDATE | WM_POINTERUP => {
use crate::event::ElementState::{Pressed, Released};
use crate::event::{ButtonSource, PointerKind, PointerSource};
if let ( if let (
Some(GetPointerFrameInfoHistory), Some(GetPointerFrameInfoHistory),
Some(SkipPointerFrameMessages), Some(SkipPointerFrameMessages),
@ -1949,67 +2067,100 @@ unsafe fn public_window_callback_inner(
continue; continue;
} }
let force = match pointer_info.pointerType { let force = if let PT_TOUCH = pointer_info.pointerType {
PT_TOUCH => { let mut touch_info = mem::MaybeUninit::uninit();
let mut touch_info = mem::MaybeUninit::uninit(); util::GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
util::GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| { match unsafe {
match unsafe { GetPointerTouchInfo(pointer_info.pointerId, touch_info.as_mut_ptr())
GetPointerTouchInfo( } {
pointer_info.pointerId, 0 => None,
touch_info.as_mut_ptr(), _ => normalize_pointer_pressure(unsafe {
) touch_info.assume_init().pressure
} { }),
0 => None, }
_ => normalize_pointer_pressure(unsafe { })
touch_info.assume_init().pressure } else {
}), None
}
})
},
PT_PEN => {
let mut pen_info = mem::MaybeUninit::uninit();
util::GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| {
match unsafe {
GetPointerPenInfo(pointer_info.pointerId, pen_info.as_mut_ptr())
} {
0 => None,
_ => normalize_pointer_pressure(unsafe {
pen_info.assume_init().pressure
}),
}
})
},
_ => None,
}; };
let x = location.x as f64 + x.fract(); let x = location.x as f64 + x.fract();
let y = location.y as f64 + y.fract(); let y = location.y as f64 + y.fract();
let location = PhysicalPosition::new(x, y); let position = PhysicalPosition::new(x, y);
userdata.send_event(Event::WindowEvent {
window_id: CoreWindowId(WindowId(window)), let window_id = CoreWindowId(WindowId(window));
event: WindowEvent::Touch(Touch { let finger_id = RootFingerId(FingerId {
phase: if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) { id: pointer_info.pointerId,
TouchPhase::Started primary: util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY),
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) {
TouchPhase::Ended
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UPDATE)
{
TouchPhase::Moved
} else {
continue;
},
location,
force,
finger_id: RootFingerId(FingerId {
id: pointer_info.pointerId,
primary: util::has_flag(
pointer_info.pointerFlags,
POINTER_FLAG_PRIMARY,
),
}),
device_id: None,
}),
}); });
if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerEntered {
device_id: None,
position,
kind: if let PT_TOUCH = pointer_info.pointerType {
PointerKind::Touch(finger_id)
} else {
PointerKind::Unknown
},
},
});
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: Pressed,
position,
button: if let PT_TOUCH = pointer_info.pointerType {
ButtonSource::Touch { finger_id, force }
} else {
ButtonSource::Unknown(0)
},
},
});
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerButton {
device_id: None,
state: Released,
position,
button: if let PT_TOUCH = pointer_info.pointerType {
ButtonSource::Touch { finger_id, force }
} else {
ButtonSource::Unknown(0)
},
},
});
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerLeft {
device_id: None,
position: Some(position),
kind: if let PT_TOUCH = pointer_info.pointerType {
PointerKind::Touch(finger_id)
} else {
PointerKind::Unknown
},
},
});
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UPDATE) {
userdata.send_event(Event::WindowEvent {
window_id,
event: WindowEvent::PointerMoved {
device_id: None,
position,
source: if let PT_TOUCH = pointer_info.pointerType {
PointerSource::Touch { finger_id, force }
} else {
PointerSource::Unknown
},
},
});
} else {
continue;
}
} }
unsafe { SkipPointerFrameMessages(pointer_id) }; unsafe { SkipPointerFrameMessages(pointer_id) };
@ -2431,7 +2582,7 @@ unsafe extern "system" fn thread_event_target_callback(
} }
unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) { unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) {
use crate::event::DeviceEvent::{Button, Key, MouseMotion, MouseWheel}; use crate::event::DeviceEvent::{Button, Key, MouseWheel, PointerMotion};
use crate::event::ElementState::{Pressed, Released}; use crate::event::ElementState::{Pressed, Released};
use crate::event::MouseScrollDelta::LineDelta; use crate::event::MouseScrollDelta::LineDelta;
@ -2447,7 +2598,7 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) {
if x != 0.0 || y != 0.0 { if x != 0.0 || y != 0.0 {
userdata.send_event(Event::DeviceEvent { userdata.send_event(Event::DeviceEvent {
device_id, device_id,
event: MouseMotion { delta: (x, y) }, event: PointerMotion { delta: (x, y) },
}); });
} }
} }

View file

@ -14,7 +14,7 @@ use windows_sys::Win32::UI::HiDpi::{
DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS, DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
}; };
use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow; use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow;
use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_PEN_INFO, POINTER_TOUCH_INFO}; use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_TOUCH_INFO};
use windows_sys::Win32::UI::WindowsAndMessaging::{ use windows_sys::Win32::UI::WindowsAndMessaging::{
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowPlacement, GetWindowRect, ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowPlacement, GetWindowRect,
IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM,
@ -244,9 +244,6 @@ pub type GetPointerDeviceRects = unsafe extern "system" fn(
pub type GetPointerTouchInfo = pub type GetPointerTouchInfo =
unsafe extern "system" fn(pointerId: u32, touchInfo: *mut POINTER_TOUCH_INFO) -> BOOL; unsafe extern "system" fn(pointerId: u32, touchInfo: *mut POINTER_TOUCH_INFO) -> BOOL;
pub type GetPointerPenInfo =
unsafe extern "system" fn(pointId: u32, penInfo: *mut POINTER_PEN_INFO) -> BOOL;
pub(crate) static GET_DPI_FOR_WINDOW: Lazy<Option<GetDpiForWindow>> = pub(crate) static GET_DPI_FOR_WINDOW: Lazy<Option<GetDpiForWindow>> =
Lazy::new(|| get_function!("user32.dll", GetDpiForWindow)); Lazy::new(|| get_function!("user32.dll", GetDpiForWindow));
pub(crate) static ADJUST_WINDOW_RECT_EX_FOR_DPI: Lazy<Option<AdjustWindowRectExForDpi>> = pub(crate) static ADJUST_WINDOW_RECT_EX_FOR_DPI: Lazy<Option<AdjustWindowRectExForDpi>> =
@ -269,5 +266,3 @@ pub(crate) static GET_POINTER_DEVICE_RECTS: Lazy<Option<GetPointerDeviceRects>>
Lazy::new(|| get_function!("user32.dll", GetPointerDeviceRects)); Lazy::new(|| get_function!("user32.dll", GetPointerDeviceRects));
pub(crate) static GET_POINTER_TOUCH_INFO: Lazy<Option<GetPointerTouchInfo>> = pub(crate) static GET_POINTER_TOUCH_INFO: Lazy<Option<GetPointerTouchInfo>> =
Lazy::new(|| get_function!("user32.dll", GetPointerTouchInfo)); Lazy::new(|| get_function!("user32.dll", GetPointerTouchInfo));
pub(crate) static GET_POINTER_PEN_INFO: Lazy<Option<GetPointerPenInfo>> =
Lazy::new(|| get_function!("user32.dll", GetPointerPenInfo));