use std::cell::RefCell;
use std::rc::Rc;
use sctk::reexports::client::protocol::wl_pointer::{self, Event as PointerEvent};
use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::Event as RelativePointerEvent;
use sctk::seat::pointer::ThemedPointer;
use crate::dpi::LogicalPosition;
use crate::event::{
DeviceEvent, ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent,
};
use crate::platform_impl::wayland::event_loop::WinitState;
use crate::platform_impl::wayland::{self, DeviceId};
use super::{PointerData, WinitPointer};
const BTN_LEFT: u32 = 0x110;
const BTN_RIGHT: u32 = 0x111;
const BTN_MIDDLE: u32 = 0x112;
#[inline]
pub(super) fn handle_pointer(
pointer: ThemedPointer,
event: PointerEvent,
pointer_data: &Rc<RefCell<PointerData>>,
winit_state: &mut WinitState,
) {
let event_sink = &mut winit_state.event_sink;
let mut pointer_data = pointer_data.borrow_mut();
match event {
PointerEvent::Enter {
surface,
surface_x,
surface_y,
serial,
..
} => {
pointer_data.latest_serial.replace(serial);
let window_id = wayland::make_wid(&surface);
if !winit_state.window_map.contains_key(&window_id) {
return;
}
let window_handle = match winit_state.window_map.get_mut(&window_id) {
Some(window_handle) => window_handle,
None => return,
};
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
pointer_data.surface = Some(surface);
let winit_pointer = WinitPointer {
pointer,
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
pointer_constraints: pointer_data.pointer_constraints.clone(),
latest_serial: pointer_data.latest_serial.clone(),
};
window_handle.pointer_entered(winit_pointer);
event_sink.push_window_event(
WindowEvent::CursorEntered {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
},
window_id,
);
let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor);
event_sink.push_window_event(
WindowEvent::CursorMoved {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
position,
modifiers: *pointer_data.modifiers_state.borrow(),
},
window_id,
);
}
PointerEvent::Leave { surface, serial } => {
pointer_data.surface = None;
pointer_data.latest_serial.replace(serial);
let window_id = wayland::make_wid(&surface);
let window_handle = match winit_state.window_map.get_mut(&window_id) {
Some(window_handle) => window_handle,
None => return,
};
let winit_pointer = WinitPointer {
pointer,
confined_pointer: Rc::downgrade(&pointer_data.confined_pointer),
pointer_constraints: pointer_data.pointer_constraints.clone(),
latest_serial: pointer_data.latest_serial.clone(),
};
window_handle.pointer_left(winit_pointer);
event_sink.push_window_event(
WindowEvent::CursorLeft {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
},
window_id,
);
}
PointerEvent::Motion {
surface_x,
surface_y,
..
} => {
let surface = match pointer_data.surface.as_ref() {
Some(surface) => surface,
None => return,
};
let window_id = wayland::make_wid(surface);
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor);
event_sink.push_window_event(
WindowEvent::CursorMoved {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
position,
modifiers: *pointer_data.modifiers_state.borrow(),
},
window_id,
);
}
PointerEvent::Button {
button,
state,
serial,
..
} => {
pointer_data.latest_serial.replace(serial);
let window_id = match pointer_data.surface.as_ref().map(wayland::make_wid) {
Some(window_id) => window_id,
None => return,
};
let state = match state {
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
wl_pointer::ButtonState::Released => ElementState::Released,
_ => unreachable!(),
};
let button = match button {
BTN_LEFT => MouseButton::Left,
BTN_RIGHT => MouseButton::Right,
BTN_MIDDLE => MouseButton::Middle,
button => MouseButton::Other(button as u16),
};
event_sink.push_window_event(
WindowEvent::MouseInput {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
state,
button,
modifiers: *pointer_data.modifiers_state.borrow(),
},
window_id,
);
}
PointerEvent::Axis { axis, value, .. } => {
let surface = match pointer_data.surface.as_ref() {
Some(surface) => surface,
None => return,
};
let window_id = wayland::make_wid(&surface);
if pointer.as_ref().version() < 5 {
let (mut x, mut y) = (0.0, 0.0);
match axis {
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
_ => unreachable!(),
}
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
let delta = LogicalPosition::new(x as f64, y as f64).to_physical(scale_factor);
event_sink.push_window_event(
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
delta: MouseScrollDelta::PixelDelta(delta),
phase: TouchPhase::Moved,
modifiers: *pointer_data.modifiers_state.borrow(),
},
window_id,
);
} else {
let (mut x, mut y) = pointer_data.axis_data.axis_buffer.unwrap_or((0.0, 0.0));
match axis {
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
_ => unreachable!(),
}
pointer_data.axis_data.axis_buffer = Some((x, y));
pointer_data.axis_data.axis_state = match pointer_data.axis_data.axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
}
PointerEvent::AxisDiscrete { axis, discrete } => {
let (mut x, mut y) = pointer_data
.axis_data
.axis_discrete_buffer
.unwrap_or((0., 0.));
match axis {
wl_pointer::Axis::VerticalScroll => y -= discrete as f32,
wl_pointer::Axis::HorizontalScroll => x += discrete as f32,
_ => unreachable!(),
}
pointer_data.axis_data.axis_discrete_buffer = Some((x, y));
pointer_data.axis_data.axis_state = match pointer_data.axis_data.axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
PointerEvent::AxisSource { .. } => (),
PointerEvent::AxisStop { .. } => {
pointer_data.axis_data.axis_state = TouchPhase::Ended;
}
PointerEvent::Frame => {
let axis_buffer = pointer_data.axis_data.axis_buffer.take();
let axis_discrete_buffer = pointer_data.axis_data.axis_discrete_buffer.take();
let surface = match pointer_data.surface.as_ref() {
Some(surface) => surface,
None => return,
};
let window_id = wayland::make_wid(&surface);
let window_event = if let Some((x, y)) = axis_discrete_buffer {
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
delta: MouseScrollDelta::LineDelta(x, y),
phase: pointer_data.axis_data.axis_state,
modifiers: *pointer_data.modifiers_state.borrow(),
}
} else if let Some((x, y)) = axis_buffer {
let scale_factor = sctk::get_surface_scale_factor(&surface) as f64;
let delta = LogicalPosition::new(x, y).to_physical(scale_factor);
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
DeviceId,
)),
delta: MouseScrollDelta::PixelDelta(delta),
phase: pointer_data.axis_data.axis_state,
modifiers: *pointer_data.modifiers_state.borrow(),
}
} else {
return;
};
event_sink.push_window_event(window_event, window_id);
}
_ => (),
}
}
#[inline]
pub(super) fn handle_relative_pointer(event: RelativePointerEvent, winit_state: &mut WinitState) {
if let RelativePointerEvent::RelativeMotion { dx, dy, .. } = event {
winit_state
.event_sink
.push_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId)
}
}