use std::time::Duration;
use inset_embedder::{
PointerChange, PointerData, PointerDataPacket, PointerDeviceKind, PointerSignalKind, ViewId,
};
use winit::event::{MouseButton, MouseScrollDelta};
use winit::window::WindowId;
const PRIMARY_MOUSE_BUTTON: i64 = 1 << 0;
const SECONDARY_MOUSE_BUTTON: i64 = 1 << 1;
const MIDDLE_MOUSE_BUTTON: i64 = 1 << 2;
const BACK_MOUSE_BUTTON: i64 = 1 << 3;
const FORWARD_MOUSE_BUTTON: i64 = 1 << 4;
const KNOWN_BUTTONS: [i64; 5] = [
PRIMARY_MOUSE_BUTTON,
SECONDARY_MOUSE_BUTTON,
MIDDLE_MOUSE_BUTTON,
BACK_MOUSE_BUTTON,
FORWARD_MOUSE_BUTTON,
];
pub(crate) struct Pointer {
pub(crate) window: Option<WindowId>,
position: [f64; 2],
last_position: [f64; 2],
buttons: i64,
added: bool,
pointer_id: i64,
embedder_id: i64,
}
impl Pointer {
pub(crate) fn new() -> Pointer {
Pointer {
window: None,
position: [0.0, 0.0],
last_position: [0.0, 0.0],
buttons: 0,
added: false,
pointer_id: 0,
embedder_id: 0,
}
}
pub(crate) fn move_to(&mut self, position: [f64; 2]) {
self.position = position;
}
pub(crate) fn motion(&self) -> PointerChange {
if self.buttons != 0 {
PointerChange::Move
} else {
PointerChange::Hover
}
}
pub(crate) fn add(&mut self) -> bool {
!std::mem::replace(&mut self.added, true)
}
pub(crate) fn remove(&mut self) -> bool {
if self.added && self.buttons == 0 {
self.added = false;
return true;
}
false
}
pub(crate) fn set_button(
&mut self,
button: MouseButton,
pressed: bool,
) -> Option<PointerChange> {
self.set_button_bit(flutter_mouse_button(button)?, pressed)
}
fn set_button_bit(&mut self, bit: i64, pressed: bool) -> Option<PointerChange> {
let previous = self.buttons;
if pressed {
self.buttons |= bit;
} else {
self.buttons &= !bit;
}
pointer_change_for_buttons(previous, self.buttons)
}
pub(crate) fn reconcile(&mut self, actual: i64) -> Vec<PointerChange> {
let mut changes = Vec::new();
for bit in KNOWN_BUTTONS {
let held = actual & bit != 0;
if held != (self.buttons & bit != 0)
&& let Some(change) = self.set_button_bit(bit, held)
{
changes.push(change);
}
}
changes
}
pub(crate) fn packet(
&mut self,
view_id: ViewId,
change: PointerChange,
scroll: Option<[f64; 2]>,
time_stamp: Duration,
) -> PointerDataPacket {
if change == PointerChange::Down {
self.pointer_id += 1;
}
self.embedder_id += 1;
let [x, y] = self.position;
let [last_x, last_y] = self.last_position;
let pointer_identifier = if self.buttons == 0
&& matches!(
change,
PointerChange::Hover | PointerChange::Add | PointerChange::Remove
) {
0
} else {
self.pointer_id
};
let (signal_kind, [scroll_delta_x, scroll_delta_y]) = match scroll {
Some(delta) => (Some(PointerSignalKind::Scroll), delta),
None => (None, [0.0, 0.0]),
};
let data = PointerData {
view_id,
embedder_id: self.embedder_id,
time_stamp,
change,
kind: PointerDeviceKind::Mouse,
signal_kind,
pointer_identifier,
physical_x: x,
physical_y: y,
physical_delta_x: x - last_x,
physical_delta_y: y - last_y,
buttons: self.buttons,
pressure: 1.0,
pressure_min: 1.0,
pressure_max: 1.0,
scroll_delta_x,
scroll_delta_y,
..PointerData::default()
};
self.last_position = self.position;
PointerDataPacket::new(vec![data])
}
}
fn flutter_mouse_button(button: MouseButton) -> Option<i64> {
match button {
MouseButton::Left => Some(PRIMARY_MOUSE_BUTTON),
MouseButton::Right => Some(SECONDARY_MOUSE_BUTTON),
MouseButton::Middle => Some(MIDDLE_MOUSE_BUTTON),
MouseButton::Back => Some(BACK_MOUSE_BUTTON),
MouseButton::Forward => Some(FORWARD_MOUSE_BUTTON),
MouseButton::Other(n) => {
let shift = u32::from(n);
(shift < i64::BITS).then_some(1_i64 << shift)
}
}
}
fn pointer_change_for_buttons(previous: i64, next: i64) -> Option<PointerChange> {
if previous == next {
return None;
}
Some(if next == 0 {
PointerChange::Up
} else if previous == 0 {
PointerChange::Down
} else {
PointerChange::Move
})
}
pub(crate) fn wheel_to_physical(delta: MouseScrollDelta, scale: f64) -> [f64; 2] {
const LOGICAL_PIXELS_PER_LINE: f64 = 40.0;
match delta {
MouseScrollDelta::LineDelta(dx, dy) => [
-f64::from(dx) * LOGICAL_PIXELS_PER_LINE * scale,
-f64::from(dy) * LOGICAL_PIXELS_PER_LINE * scale,
],
MouseScrollDelta::PixelDelta(physical) => [-physical.x, -physical.y],
}
}
#[cfg(test)]
mod tests {
use winit::dpi::PhysicalPosition;
use super::*;
#[test]
fn line_deltas_scale_to_physical_pixels_with_flutter_sign() {
assert_eq!(
wheel_to_physical(MouseScrollDelta::LineDelta(0.0, 1.0), 2.0),
[0.0, -80.0]
);
assert_eq!(
wheel_to_physical(MouseScrollDelta::LineDelta(0.0, -3.0), 1.0),
[0.0, 120.0]
);
}
#[test]
fn pixel_deltas_keep_physical_pixels_and_flip_sign() {
assert_eq!(
wheel_to_physical(
MouseScrollDelta::PixelDelta(PhysicalPosition::new(0.0, -100.0)),
2.0
),
[0.0, 100.0]
);
}
#[test]
fn winit_mouse_buttons_map_to_flutter_bits() {
assert_eq!(
flutter_mouse_button(MouseButton::Left),
Some(PRIMARY_MOUSE_BUTTON)
);
assert_eq!(
flutter_mouse_button(MouseButton::Right),
Some(SECONDARY_MOUSE_BUTTON)
);
assert_eq!(
flutter_mouse_button(MouseButton::Middle),
Some(MIDDLE_MOUSE_BUTTON)
);
assert_eq!(
flutter_mouse_button(MouseButton::Back),
Some(BACK_MOUSE_BUTTON)
);
assert_eq!(
flutter_mouse_button(MouseButton::Forward),
Some(FORWARD_MOUSE_BUTTON)
);
assert_eq!(flutter_mouse_button(MouseButton::Other(5)), Some(1 << 5));
}
#[test]
fn mouse_button_chords_are_down_move_up() {
assert_eq!(
pointer_change_for_buttons(0, SECONDARY_MOUSE_BUTTON),
Some(PointerChange::Down)
);
assert_eq!(
pointer_change_for_buttons(SECONDARY_MOUSE_BUTTON, 0),
Some(PointerChange::Up)
);
assert_eq!(
pointer_change_for_buttons(
PRIMARY_MOUSE_BUTTON,
PRIMARY_MOUSE_BUTTON | SECONDARY_MOUSE_BUTTON
),
Some(PointerChange::Move)
);
assert_eq!(
pointer_change_for_buttons(PRIMARY_MOUSE_BUTTON, PRIMARY_MOUSE_BUTTON),
None
);
}
}