use std::cell::LazyCell;
use std::cmp::Ordering;
use std::f64;
use std::path::PathBuf;
use std::sync::{Mutex, Weak};
use dpi::{PhysicalPosition, PhysicalSize};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use crate::Instant;
use crate::error::RequestError;
use crate::event_loop::AsyncRequestSerial;
use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState};
#[cfg(doc)]
use crate::window::Window;
use crate::window::{ActivationToken, Theme};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StartCause {
ResumeTimeReached { start: Instant, requested_resume: Instant },
WaitCancelled { start: Instant, requested_resume: Option<Instant> },
Poll,
Init,
}
#[derive(Debug, Clone, PartialEq)]
pub enum WindowEvent {
ActivationTokenDone { serial: AsyncRequestSerial, token: ActivationToken },
SurfaceResized(PhysicalSize<u32>),
Moved(PhysicalPosition<i32>),
CloseRequested,
Destroyed,
DragEntered {
paths: Vec<PathBuf>,
position: PhysicalPosition<f64>,
},
DragMoved {
position: PhysicalPosition<f64>,
},
DragDropped {
paths: Vec<PathBuf>,
position: PhysicalPosition<f64>,
},
DragLeft {
position: Option<PhysicalPosition<f64>>,
},
Focused(bool),
KeyboardInput {
device_id: Option<DeviceId>,
event: KeyEvent,
is_synthetic: bool,
},
ModifiersChanged(Modifiers),
Ime(Ime),
PointerMoved {
device_id: Option<DeviceId>,
position: PhysicalPosition<f64>,
primary: bool,
source: PointerSource,
},
PointerEntered {
device_id: Option<DeviceId>,
position: PhysicalPosition<f64>,
primary: bool,
kind: PointerKind,
},
PointerLeft {
device_id: Option<DeviceId>,
position: Option<PhysicalPosition<f64>>,
primary: bool,
kind: PointerKind,
},
MouseWheel { device_id: Option<DeviceId>, delta: MouseScrollDelta, phase: TouchPhase },
PointerButton {
device_id: Option<DeviceId>,
state: ElementState,
position: PhysicalPosition<f64>,
primary: bool,
button: ButtonSource,
},
PinchGesture {
device_id: Option<DeviceId>,
delta: f64,
phase: TouchPhase,
},
PanGesture {
device_id: Option<DeviceId>,
delta: PhysicalPosition<f32>,
phase: TouchPhase,
},
DoubleTapGesture { device_id: Option<DeviceId> },
RotationGesture {
device_id: Option<DeviceId>,
delta: f32,
phase: TouchPhase,
},
TouchpadPressure {
device_id: Option<DeviceId>,
pressure: f32,
stage: i64,
},
ScaleFactorChanged {
scale_factor: f64,
surface_size_writer: SurfaceSizeWriter,
},
ThemeChanged(Theme),
Occluded(bool),
RedrawRequested,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerKind {
Mouse,
Touch(FingerId),
TabletTool(TabletToolKind),
Unknown,
}
#[derive(Clone, Debug, PartialEq)]
pub enum PointerSource {
Mouse,
Touch {
finger_id: FingerId,
force: Option<Force>,
},
TabletTool {
kind: TabletToolKind,
data: TabletToolData,
},
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::TabletTool { kind, .. } => Self::TabletTool(kind),
PointerSource::Unknown => Self::Unknown,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ButtonSource {
Mouse(MouseButton),
Touch {
finger_id: FingerId,
force: Option<Force>,
},
TabletTool {
kind: TabletToolKind,
button: TabletToolButton,
data: TabletToolData,
},
Unknown(u16),
}
impl ButtonSource {
pub fn mouse_button(self) -> Option<MouseButton> {
match self {
ButtonSource::Mouse(mouse) => Some(mouse),
ButtonSource::Touch { .. } => Some(MouseButton::Left),
ButtonSource::TabletTool { button, .. } => button.into(),
ButtonSource::Unknown(_) => None,
}
}
}
impl From<MouseButton> for ButtonSource {
fn from(mouse: MouseButton) -> Self {
Self::Mouse(mouse)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(i64);
impl DeviceId {
pub const fn into_raw(self) -> i64 {
self.0
}
pub const fn from_raw(id: i64) -> Self {
Self(id)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FingerId(pub(crate) usize);
impl FingerId {
pub const fn into_raw(self) -> usize {
self.0
}
pub const fn from_raw(id: usize) -> Self {
Self(id)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DeviceEvent {
PointerMotion {
delta: (f64, f64),
},
MouseWheel {
delta: MouseScrollDelta,
},
Button {
button: ButtonId,
state: ElementState,
},
Key(RawKeyEvent),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RawKeyEvent {
pub physical_key: keyboard::PhysicalKey,
pub state: ElementState,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEvent {
pub physical_key: keyboard::PhysicalKey,
pub logical_key: keyboard::Key,
pub text: Option<SmolStr>,
pub location: keyboard::KeyLocation,
pub state: ElementState,
pub repeat: bool,
pub text_with_all_modifiers: Option<SmolStr>,
pub key_without_modifiers: keyboard::Key,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Modifiers {
pub(crate) state: ModifiersState,
pub(crate) pressed_mods: ModifiersKeys,
}
impl Modifiers {
pub fn new(state: ModifiersState, pressed_mods: ModifiersKeys) -> Self {
Self { state, pressed_mods }
}
pub fn state(&self) -> ModifiersState {
self.state
}
pub fn lshift_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::LSHIFT)
}
pub fn rshift_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::RSHIFT)
}
pub fn lalt_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::LALT)
}
pub fn ralt_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::RALT)
}
pub fn lcontrol_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::LCONTROL)
}
pub fn rcontrol_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::RCONTROL)
}
pub fn lsuper_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::LMETA)
}
pub fn rsuper_state(&self) -> ModifiersKeyState {
self.mod_state(ModifiersKeys::RMETA)
}
fn mod_state(&self, modifier: ModifiersKeys) -> ModifiersKeyState {
if self.pressed_mods.contains(modifier) {
ModifiersKeyState::Pressed
} else {
ModifiersKeyState::Unknown
}
}
}
impl From<ModifiersState> for Modifiers {
fn from(value: ModifiersState) -> Self {
Self { state: value, pressed_mods: Default::default() }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Ime {
Enabled,
Preedit(String, Option<(usize, usize)>),
Commit(String),
DeleteSurrounding {
before_bytes: usize,
after_bytes: usize,
},
Disabled,
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TouchPhase {
Started,
Moved,
Ended,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[doc(alias = "Pressure")]
pub enum Force {
Calibrated {
force: f64,
max_possible_force: f64,
},
Normalized(f64),
}
impl Force {
pub fn normalized(&self, angle: Option<TabletToolAngle>) -> f64 {
match self {
Force::Calibrated { force, max_possible_force } => {
let force = match angle {
Some(TabletToolAngle { altitude, .. }) => force / altitude.sin(),
None => *force,
};
force / max_possible_force
},
Force::Normalized(force) => *force,
}
}
}
pub type AxisId = u32;
pub type ButtonId = u32;
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum TabletToolKind {
#[default]
Pen,
Eraser,
Brush,
Pencil,
Airbrush,
Finger,
Mouse,
Lens,
}
#[derive(Default, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct TabletToolData {
pub force: Option<Force>,
pub tangential_force: Option<f32>,
pub twist: Option<u16>,
pub tilt: Option<TabletToolTilt>,
pub angle: Option<TabletToolAngle>,
}
impl TabletToolData {
pub fn tilt(self) -> Option<TabletToolTilt> {
if let Some(tilt) = self.tilt { Some(tilt) } else { self.angle.map(TabletToolAngle::tilt) }
}
pub fn angle(self) -> Option<TabletToolAngle> {
if let Some(angle) = self.angle {
Some(angle)
} else {
self.tilt.map(TabletToolTilt::angle)
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct TabletToolTilt {
pub x: i8,
pub y: i8,
}
impl TabletToolTilt {
pub fn angle(self) -> TabletToolAngle {
use std::f64::consts::*;
const PI_0_5: f64 = FRAC_PI_2;
const PI_1_5: f64 = 3. * FRAC_PI_2;
const PI_2: f64 = 2. * PI;
let x = LazyCell::new(|| f64::from(self.x).to_radians());
let y = LazyCell::new(|| f64::from(self.y).to_radians());
let mut azimuth = 0.;
if self.x == 0 {
match self.y.cmp(&0) {
Ordering::Greater => azimuth = PI_0_5,
Ordering::Less => azimuth = PI_1_5,
Ordering::Equal => (),
}
} else if self.y == 0 {
if self.x < 0 {
azimuth = PI;
}
} else if self.x.abs() == 90 || self.y.abs() == 90 {
azimuth = 0.;
} else {
azimuth = f64::atan2(y.tan(), x.tan());
if azimuth < 0. {
azimuth += PI_2;
}
}
let altitude;
if self.x.abs() == 90 || self.y.abs() == 90 {
altitude = 0.;
} else if self.x == 0 {
altitude = PI_0_5 - y.abs();
} else if self.y == 0 {
altitude = PI_0_5 - x.abs();
} else {
altitude = f64::atan(1. / f64::sqrt(x.tan().powi(2) + y.tan().powi(2)));
}
TabletToolAngle { altitude, azimuth }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct TabletToolAngle {
pub altitude: f64,
pub azimuth: f64,
}
impl Default for TabletToolAngle {
fn default() -> Self {
Self { altitude: f64::consts::FRAC_2_PI, azimuth: 0. }
}
}
impl TabletToolAngle {
pub fn tilt(self) -> TabletToolTilt {
use std::f64::consts::*;
const PI_0_5: f64 = FRAC_PI_2;
const PI_1_5: f64 = 3. * FRAC_PI_2;
const PI_2: f64 = 2. * PI;
let mut x = 0.;
let mut y = 0.;
if self.altitude == 0. {
if self.azimuth == 0. || self.azimuth == PI_2 {
x = FRAC_PI_2;
} else if self.azimuth == PI_0_5 {
y = FRAC_PI_2;
} else if self.azimuth == PI {
x = -FRAC_PI_2;
} else if self.azimuth == PI_1_5 {
y = -FRAC_PI_2;
} else if self.azimuth > 0. && self.azimuth < PI_0_5 {
x = FRAC_PI_2;
y = FRAC_PI_2;
} else if self.azimuth > PI_0_5 && self.azimuth < PI {
x = -FRAC_PI_2;
y = FRAC_PI_2;
} else if self.azimuth > PI && self.azimuth < PI_1_5 {
x = -FRAC_PI_2;
y = -FRAC_PI_2;
} else if self.azimuth > PI_1_5 && self.azimuth < PI_2 {
x = FRAC_PI_2;
y = -FRAC_PI_2;
}
}
if self.altitude != 0. {
let altitude = self.altitude.tan();
x = f64::atan(f64::cos(self.azimuth) / altitude);
y = f64::atan(f64::sin(self.azimuth) / altitude);
}
TabletToolTilt { x: x.to_degrees().round() as i8, y: y.to_degrees().round() as i8 }
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ElementState {
Pressed,
Released,
}
impl ElementState {
pub fn is_pressed(self) -> bool {
self == ElementState::Pressed
}
}
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum MouseButton {
Left = 0,
Right = 1,
Middle = 2,
Back = 3,
Forward = 4,
Button6 = 5,
Button7 = 6,
Button8 = 7,
Button9 = 8,
Button10 = 9,
Button11 = 10,
Button12 = 11,
Button13 = 12,
Button14 = 13,
Button15 = 14,
Button16 = 15,
Button17 = 16,
Button18 = 17,
Button19 = 18,
Button20 = 19,
Button21 = 20,
Button22 = 21,
Button23 = 22,
Button24 = 23,
Button25 = 24,
Button26 = 25,
Button27 = 26,
Button28 = 27,
Button29 = 28,
Button30 = 29,
Button31 = 30,
Button32 = 31,
}
impl MouseButton {
pub fn try_from_u8(b: u8) -> Option<MouseButton> {
Some(match b {
0 => MouseButton::Left,
1 => MouseButton::Right,
2 => MouseButton::Middle,
3 => MouseButton::Back,
4 => MouseButton::Forward,
5 => MouseButton::Button6,
6 => MouseButton::Button7,
7 => MouseButton::Button8,
8 => MouseButton::Button9,
9 => MouseButton::Button10,
10 => MouseButton::Button11,
11 => MouseButton::Button12,
12 => MouseButton::Button13,
13 => MouseButton::Button14,
14 => MouseButton::Button15,
15 => MouseButton::Button16,
16 => MouseButton::Button17,
17 => MouseButton::Button18,
18 => MouseButton::Button19,
19 => MouseButton::Button20,
20 => MouseButton::Button21,
21 => MouseButton::Button22,
22 => MouseButton::Button23,
23 => MouseButton::Button24,
24 => MouseButton::Button25,
25 => MouseButton::Button26,
26 => MouseButton::Button27,
27 => MouseButton::Button28,
28 => MouseButton::Button29,
29 => MouseButton::Button30,
30 => MouseButton::Button31,
31 => MouseButton::Button32,
_ => return None,
})
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum TabletToolButton {
Contact,
Barrel,
Other(u16),
}
impl From<TabletToolButton> for Option<MouseButton> {
fn from(tool: TabletToolButton) -> Self {
Some(match tool {
TabletToolButton::Contact => MouseButton::Left,
TabletToolButton::Barrel => MouseButton::Right,
TabletToolButton::Other(1) => MouseButton::Middle,
TabletToolButton::Other(3) => MouseButton::Back,
TabletToolButton::Other(4) => MouseButton::Forward,
TabletToolButton::Other(_) => return None,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseScrollDelta {
LineDelta(f32, f32),
PixelDelta(PhysicalPosition<f64>),
}
#[derive(Debug, Clone)]
pub struct SurfaceSizeWriter {
pub(crate) new_surface_size: Weak<Mutex<PhysicalSize<u32>>>,
}
impl SurfaceSizeWriter {
pub fn new(new_surface_size: Weak<Mutex<PhysicalSize<u32>>>) -> Self {
Self { new_surface_size }
}
pub fn request_surface_size(
&mut self,
new_surface_size: PhysicalSize<u32>,
) -> Result<(), RequestError> {
if let Some(inner) = self.new_surface_size.upgrade() {
*inner.lock().unwrap() = new_surface_size;
Ok(())
} else {
Err(RequestError::Ignored)
}
}
pub fn surface_size(&self) -> Result<PhysicalSize<u32>, RequestError> {
if let Some(inner) = self.new_surface_size.upgrade() {
Ok(*inner.lock().unwrap())
} else {
Err(RequestError::Ignored)
}
}
}
impl PartialEq for SurfaceSizeWriter {
fn eq(&self, other: &Self) -> bool {
self.new_surface_size.as_ptr() == other.new_surface_size.as_ptr()
}
}
impl Eq for SurfaceSizeWriter {}
#[cfg(test)]
mod tests {
use std::collections::{BTreeSet, HashSet};
use dpi::PhysicalPosition;
use crate::event;
macro_rules! foreach_event {
($closure:expr) => {{
foreach_event!(window: $closure);
foreach_event!(device: $closure);
}};
(window: $closure:expr) => {{
#[allow(unused_mut)]
let mut with_window_event: &mut dyn FnMut(event::WindowEvent) = &mut $closure;
let fid = event::FingerId::from_raw(0);
use crate::event::Ime::Enabled;
use crate::event::WindowEvent::*;
use crate::event::{PointerKind, PointerSource};
with_window_event(CloseRequested);
with_window_event(Destroyed);
with_window_event(Focused(true));
with_window_event(Moved((0, 0).into()));
with_window_event(SurfaceResized((0, 0).into()));
with_window_event(DragEntered { paths: vec!["x.txt".into()], position: (0, 0).into() });
with_window_event(DragMoved { position: (0, 0).into() });
with_window_event(DragDropped { paths: vec!["x.txt".into()], position: (0, 0).into() });
with_window_event(DragLeft { position: Some((0, 0).into()) });
with_window_event(Ime(Enabled));
with_window_event(PointerMoved {
device_id: None,
primary: true,
position: (0, 0).into(),
source: PointerSource::Mouse,
});
with_window_event(ModifiersChanged(event::Modifiers::default()));
with_window_event(PointerEntered {
device_id: None,
primary: true,
position: (0, 0).into(),
kind: PointerKind::Mouse,
});
with_window_event(PointerLeft {
primary: true,
device_id: None,
position: Some((0, 0).into()),
kind: PointerKind::Mouse,
});
with_window_event(MouseWheel {
device_id: None,
delta: event::MouseScrollDelta::LineDelta(0.0, 0.0),
phase: event::TouchPhase::Started,
});
with_window_event(PointerButton {
device_id: None,
primary: true,
state: event::ElementState::Pressed,
position: (0, 0).into(),
button: event::ButtonSource::Unknown(0),
});
with_window_event(PointerButton {
device_id: None,
primary: true,
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 {
device_id: None,
delta: 0.0,
phase: event::TouchPhase::Started,
});
with_window_event(DoubleTapGesture { device_id: None });
with_window_event(RotationGesture {
device_id: None,
delta: 0.0,
phase: event::TouchPhase::Started,
});
with_window_event(PanGesture {
device_id: None,
delta: PhysicalPosition::<f32>::new(0.0, 0.0),
phase: event::TouchPhase::Started,
});
with_window_event(TouchpadPressure { device_id: None, pressure: 0.0, stage: 0 });
with_window_event(ThemeChanged(crate::window::Theme::Light));
with_window_event(Occluded(true));
}};
(device: $closure:expr) => {{
use event::DeviceEvent::*;
#[allow(unused_mut)]
let mut with_device_event: &mut dyn FnMut(event::DeviceEvent) = &mut $closure;
with_device_event(PointerMotion { delta: (0.0, 0.0).into() });
with_device_event(MouseWheel { delta: event::MouseScrollDelta::LineDelta(0.0, 0.0) });
with_device_event(Button { button: 0, state: event::ElementState::Pressed });
}};
}
#[allow(clippy::clone_on_copy)]
#[test]
fn test_event_clone() {
foreach_event!(|event| {
let event2 = event.clone();
assert_eq!(event, event2);
});
}
#[test]
fn test_tilt_angle_conversions() {
use std::f64::consts::*;
use event::{TabletToolAngle, TabletToolTilt};
const TILT_TO_ANGLE: &[(TabletToolTilt, TabletToolAngle)] = &[
(TabletToolTilt { x: 0, y: 0 }, TabletToolAngle { altitude: FRAC_PI_2, azimuth: 0. }),
(TabletToolTilt { x: 0, y: 90 }, TabletToolAngle { altitude: 0., azimuth: FRAC_PI_2 }),
(TabletToolTilt { x: 0, y: -90 }, TabletToolAngle {
altitude: 0.,
azimuth: 3. * FRAC_PI_2,
}),
(TabletToolTilt { x: 90, y: 0 }, TabletToolAngle { altitude: 0., azimuth: 0. }),
(TabletToolTilt { x: 90, y: 90 }, TabletToolAngle { altitude: 0., azimuth: 0. }),
(TabletToolTilt { x: 90, y: -90 }, TabletToolAngle { altitude: 0., azimuth: 0. }),
(TabletToolTilt { x: -90, y: 0 }, TabletToolAngle { altitude: 0., azimuth: PI }),
(TabletToolTilt { x: -90, y: 90 }, TabletToolAngle { altitude: 0., azimuth: 0. }),
(TabletToolTilt { x: -90, y: -90 }, TabletToolAngle { altitude: 0., azimuth: 0. }),
(TabletToolTilt { x: 0, y: 45 }, TabletToolAngle {
altitude: FRAC_PI_4,
azimuth: FRAC_PI_2,
}),
(TabletToolTilt { x: 0, y: -45 }, TabletToolAngle {
altitude: FRAC_PI_4,
azimuth: 3. * FRAC_PI_2,
}),
(TabletToolTilt { x: 45, y: 0 }, TabletToolAngle { altitude: FRAC_PI_4, azimuth: 0. }),
(TabletToolTilt { x: -45, y: 0 }, TabletToolAngle { altitude: FRAC_PI_4, azimuth: PI }),
];
for (tilt, angle) in TILT_TO_ANGLE {
assert_eq!(tilt.angle(), *angle, "{tilt:?}");
}
const ANGLE_TO_TILT: &[(TabletToolAngle, TabletToolTilt)] = &[
(TabletToolAngle { altitude: 0., azimuth: 0. }, TabletToolTilt { x: 90, y: 0 }),
(TabletToolAngle { altitude: FRAC_PI_4, azimuth: 0. }, TabletToolTilt { x: 45, y: 0 }),
(TabletToolAngle { altitude: FRAC_PI_2, azimuth: 0. }, TabletToolTilt { x: 0, y: 0 }),
(TabletToolAngle { altitude: 0., azimuth: FRAC_PI_2 }, TabletToolTilt { x: 0, y: 90 }),
(TabletToolAngle { altitude: FRAC_PI_4, azimuth: FRAC_PI_2 }, TabletToolTilt {
x: 0,
y: 45,
}),
(TabletToolAngle { altitude: 0., azimuth: PI }, TabletToolTilt { x: -90, y: 0 }),
(TabletToolAngle { altitude: FRAC_PI_4, azimuth: PI }, TabletToolTilt { x: -45, y: 0 }),
(TabletToolAngle { altitude: 0., azimuth: 3. * FRAC_PI_2 }, TabletToolTilt {
x: 0,
y: -90,
}),
(TabletToolAngle { altitude: FRAC_PI_4, azimuth: 3. * FRAC_PI_2 }, TabletToolTilt {
x: 0,
y: -45,
}),
];
for (angle, tilt) in ANGLE_TO_TILT {
assert_eq!(angle.tilt(), *tilt, "{angle:?}");
}
}
#[test]
fn test_force_normalize() {
let force = event::Force::Normalized(0.0);
assert_eq!(force.normalized(None), 0.0);
let force2 = event::Force::Calibrated { force: 5.0, max_possible_force: 2.5 };
assert_eq!(force2.normalized(None), 2.0);
let force3 = event::Force::Calibrated { force: 5.0, max_possible_force: 2.5 };
assert_eq!(
force3.normalized(Some(event::TabletToolAngle {
altitude: std::f64::consts::PI / 2.0,
azimuth: 0.
})),
2.0
);
}
#[allow(clippy::clone_on_copy)]
#[test]
fn ensure_attrs_do_not_panic() {
foreach_event!(|event| {
let _ = format!("{event:?}");
});
let _ = event::StartCause::Init.clone();
let fid = crate::event::FingerId::from_raw(0).clone();
HashSet::new().insert(fid);
let mut set = [fid, fid, fid];
set.sort_unstable();
let mut set2 = BTreeSet::new();
set2.insert(fid);
set2.insert(fid);
HashSet::new().insert(event::TouchPhase::Started.clone());
HashSet::new().insert(event::MouseButton::Left.clone());
HashSet::new().insert(event::Ime::Enabled);
let _ = event::Force::Calibrated { force: 0.0, max_possible_force: 0.0 }.clone();
}
}