#![allow(clippy::derive_partial_eq_without_eq)]
use enumflags2::{BitFlag, BitFlags};
use crate::{
ei::connection::DisconnectReason, eis, handshake::EisHandshakeResp, wire::Interface, Error,
Object,
};
use std::{
collections::{HashMap, HashSet, VecDeque},
fmt,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex, Weak,
},
};
pub use crate::event::DeviceCapability;
const EIS_MAX_TOUCHES: usize = 16;
#[derive(Debug)]
pub enum RequestError {
InvalidCapabilities,
DuplicatedTouchDown,
TooManyTouches,
}
impl fmt::Display for RequestError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidCapabilities => write!(f, "Invalid capabilities"),
Self::DuplicatedTouchDown => write!(f, "Touch down event for duplicated touch ID"),
Self::TooManyTouches => write!(f, "Too many simultaneous touch events"),
}
}
}
#[derive(Debug)]
struct ConnectionInner {
context: eis::Context,
handshake_resp: EisHandshakeResp,
seats: Mutex<HashMap<eis::Seat, Seat>>,
devices: Mutex<HashMap<eis::Device, Device>>,
device_for_interface: Mutex<HashMap<Object, Device>>,
last_serial: Mutex<u32>,
disconnected: AtomicBool,
}
#[derive(Clone, Debug)]
pub struct Connection(Arc<ConnectionInner>);
impl Connection {
#[must_use]
pub fn connection(&self) -> &eis::Connection {
&self.0.handshake_resp.connection
}
pub fn disconnected(&self, reason: DisconnectReason, explanation: Option<&str>) {
let seats = self
.0
.seats
.lock()
.unwrap()
.values()
.cloned()
.collect::<Vec<_>>();
for seat in seats {
seat.remove();
}
self.connection()
.disconnected(self.last_serial(), reason, explanation);
let _ = self.flush();
self.0.disconnected.store(true, Ordering::SeqCst);
self.0.context.0.shutdown_read();
}
#[cfg(feature = "calloop")]
pub(crate) fn has_sent_disconnected(&self) -> bool {
self.0.disconnected.load(Ordering::SeqCst)
}
pub fn flush(&self) -> rustix::io::Result<()> {
self.0.context.flush()
}
#[must_use]
pub fn context_type(&self) -> eis::handshake::ContextType {
self.0.handshake_resp.context_type
}
#[must_use]
pub fn name(&self) -> Option<&str> {
self.0.handshake_resp.name.as_deref()
}
#[must_use]
pub fn has_interface(&self, interface: &str) -> bool {
self.0
.handshake_resp
.negotiated_interfaces
.contains_key(interface)
}
#[must_use]
pub fn interface_version(&self, interface: &str) -> Option<u32> {
self.0
.handshake_resp
.negotiated_interfaces
.get(interface)
.copied()
}
#[must_use]
pub fn last_serial(&self) -> u32 {
*self.0.last_serial.lock().unwrap()
}
pub fn with_next_serial<T, F: FnOnce(u32) -> T>(&self, cb: F) -> T {
let mut last_serial = self.0.last_serial.lock().unwrap();
let serial = last_serial.wrapping_add(1);
let res = cb(serial);
*last_serial = serial;
res
}
fn device_for_interface<T: DeviceInterface>(&mut self, interface: &T) -> Option<Device> {
self.0
.device_for_interface
.lock()
.unwrap()
.get(interface.as_object())
.cloned()
}
#[must_use]
pub fn add_seat(&self, name: Option<&str>, capabilities: BitFlags<DeviceCapability>) -> Seat {
let seat_version = self.interface_version(eis::Seat::NAME).unwrap_or(1);
let seat = self.connection().seat(seat_version);
if let Some(name) = name {
seat.name(name);
}
for capability in capabilities {
let interface_name = capability.interface_name();
if !self.has_interface(interface_name) {
continue;
}
seat.capability(capability as u64, interface_name);
}
seat.done();
let seat = Seat(Arc::new(SeatInner {
seat,
name: name.map(std::borrow::ToOwned::to_owned),
handle: Arc::downgrade(&self.0),
advertised_capabilities: capabilities,
}));
self.0
.seats
.lock()
.unwrap()
.insert(seat.0.seat.clone(), seat.clone());
seat
}
}
#[allow(clippy::cast_sign_loss)] fn eis_now() -> u64 {
let time = rustix::time::clock_gettime(rustix::time::ClockId::Monotonic);
time.tv_sec as u64 * 1_000_000 + time.tv_nsec as u64 / 1_000
}
#[derive(Debug)]
pub struct EisRequestConverter {
requests: VecDeque<EisRequest>,
pending_requests: VecDeque<EisRequest>,
connection: Connection,
}
impl EisRequestConverter {
#[must_use]
pub fn new(
context: &eis::Context,
handshake_resp: EisHandshakeResp,
initial_serial: u32,
) -> Self {
Self {
requests: VecDeque::new(),
pending_requests: VecDeque::new(),
connection: Connection(Arc::new(ConnectionInner {
context: context.clone(),
handshake_resp,
seats: Mutex::default(),
devices: Mutex::default(),
device_for_interface: Mutex::default(),
last_serial: Mutex::new(initial_serial),
disconnected: AtomicBool::new(false),
})),
}
}
#[must_use]
pub fn handle(&self) -> &Connection {
&self.connection
}
fn queue_frame_event(&mut self, device: &Device) {
self.queue_request(EisRequest::Frame(Frame {
time: eis_now(),
device: device.clone(),
last_serial: self.connection.last_serial(),
}));
}
fn queue_request(&mut self, mut request: EisRequest) {
if request.time_mut().is_some() {
self.pending_requests.push_back(request);
} else if let EisRequest::Frame(Frame { time, .. }) = &request {
if self.pending_requests.is_empty() {
return;
}
for mut pending_request in self.pending_requests.drain(..) {
*pending_request.time_mut().unwrap() = *time;
self.requests.push_back(pending_request);
}
self.requests.push_back(request);
} else {
if let Some(device) = request.device() {
if !self.pending_requests.is_empty() {
self.queue_frame_event(device);
}
}
self.requests.push_back(request);
}
}
pub fn next_request(&mut self) -> Option<EisRequest> {
self.requests.pop_front()
}
pub fn handle_request(&mut self, request: eis::Request) -> Result<(), Error> {
match request {
eis::Request::Handshake(_handshake, _request) => {
return Err(Error::UnexpectedHandshakeEvent);
}
eis::Request::Connection(_connection, request) => {
self.handle_connection_request(request)?;
}
eis::Request::Callback(_callback, request) => match request {},
eis::Request::Pingpong(_ping_pong, request) => match request {
eis::pingpong::Request::Done { callback_data: _ } => {
}
},
eis::Request::Seat(seat, request) => self.handle_seat_request(&seat, &request)?,
eis::Request::Device(device, request) => self.handle_device_request(device, request),
eis::Request::Keyboard(keyboard, request) => {
let Some(device) = self.connection.device_for_interface(&keyboard) else {
return Ok(());
};
match request {
eis::keyboard::Request::Release => {}
eis::keyboard::Request::Key { key, state } => {
self.queue_request(EisRequest::KeyboardKey(KeyboardKey {
device,
key,
state,
time: 0,
}));
}
}
}
eis::Request::Pointer(pointer, request) => {
let Some(device) = self.connection.device_for_interface(&pointer) else {
return Ok(());
};
match request {
eis::pointer::Request::Release => {}
eis::pointer::Request::MotionRelative { x, y } => {
self.queue_request(EisRequest::PointerMotion(PointerMotion {
device,
dx: x,
dy: y,
time: 0,
}));
}
}
}
eis::Request::PointerAbsolute(pointer_absolute, request) => {
let Some(device) = self.connection.device_for_interface(&pointer_absolute) else {
return Ok(());
};
match request {
eis::pointer_absolute::Request::Release => {}
eis::pointer_absolute::Request::MotionAbsolute { x, y } => {
self.queue_request(EisRequest::PointerMotionAbsolute(
PointerMotionAbsolute {
device,
dx_absolute: x,
dy_absolute: y,
time: 0,
},
));
}
}
}
eis::Request::Scroll(scroll, request) => {
self.handle_scroll_request(scroll, request);
}
eis::Request::Button(button, request) => {
let Some(device) = self.connection.device_for_interface(&button) else {
return Ok(());
};
match request {
eis::button::Request::Release => {}
eis::button::Request::Button { button, state } => {
self.queue_request(EisRequest::Button(Button {
device,
button,
state,
time: 0,
}));
}
}
}
eis::Request::Touchscreen(touchscreen, request) => {
self.handle_touchscreen_request(touchscreen, request)?;
}
}
Ok(())
}
fn handle_connection_request(
&mut self,
request: eis::connection::Request,
) -> Result<(), Error> {
match request {
eis::connection::Request::Sync { callback } => {
if callback.version() != 1 {
return Err(Error::InvalidInterfaceVersion(
"ei_callback",
callback.version(),
));
}
callback.done(0);
if let Some(backend) = callback.0.backend() {
let _ = backend.flush();
}
}
eis::connection::Request::Disconnect => {
self.queue_request(EisRequest::Disconnect);
}
}
Ok(())
}
fn handle_seat_request(
&mut self,
seat: &eis::Seat,
request: &eis::seat::Request,
) -> Result<(), Error> {
match request {
eis::seat::Request::Release => {
self.connection
.with_next_serial(|serial| seat.destroyed(serial));
}
eis::seat::Request::Bind { capabilities } => {
let Some(seat) = self.connection.0.seats.lock().unwrap().get(seat).cloned() else {
return Ok(());
};
let capabilities = DeviceCapability::from_bits(*capabilities)
.map_err(|_err| RequestError::InvalidCapabilities)?;
if !seat.0.advertised_capabilities.contains(capabilities) {
return Err(RequestError::InvalidCapabilities.into());
}
self.queue_request(EisRequest::Bind(Bind { seat, capabilities }));
return Ok(());
}
}
Ok(())
}
#[allow(clippy::needless_pass_by_value)] fn handle_device_request(&mut self, device: eis::Device, request: eis::device::Request) {
let Some(device) = self
.connection
.0
.devices
.lock()
.unwrap()
.get(&device)
.cloned()
else {
return;
};
match request {
eis::device::Request::Release => {}
eis::device::Request::StartEmulating {
last_serial,
sequence,
} => {
self.queue_request(EisRequest::DeviceStartEmulating(DeviceStartEmulating {
device,
last_serial,
sequence,
}));
}
eis::device::Request::StopEmulating { last_serial } => {
self.queue_request(EisRequest::DeviceStopEmulating(DeviceStopEmulating {
device,
last_serial,
}));
}
eis::device::Request::Frame {
last_serial,
timestamp,
} => {
self.queue_request(EisRequest::Frame(Frame {
device,
last_serial,
time: timestamp,
}));
}
}
}
#[allow(clippy::needless_pass_by_value)] fn handle_scroll_request(&mut self, scroll: eis::Scroll, request: eis::scroll::Request) {
let Some(device) = self.connection.device_for_interface(&scroll) else {
return;
};
match request {
eis::scroll::Request::Release => {}
eis::scroll::Request::Scroll { x, y } => {
self.queue_request(EisRequest::ScrollDelta(ScrollDelta {
device,
dx: x,
dy: y,
time: 0,
}));
}
eis::scroll::Request::ScrollDiscrete { x, y } => {
self.queue_request(EisRequest::ScrollDiscrete(ScrollDiscrete {
device,
discrete_dx: x,
discrete_dy: y,
time: 0,
}));
}
eis::scroll::Request::ScrollStop { x, y, is_cancel } => {
if is_cancel != 0 {
self.queue_request(EisRequest::ScrollCancel(ScrollCancel {
device,
time: 0,
x: x != 0,
y: y != 0,
}));
} else {
self.queue_request(EisRequest::ScrollStop(ScrollStop {
device,
time: 0,
x: x != 0,
y: y != 0,
}));
}
}
}
}
#[allow(clippy::needless_pass_by_value)] fn handle_touchscreen_request(
&mut self,
touchscreen: eis::Touchscreen,
request: eis::touchscreen::Request,
) -> Result<(), Error> {
let Some(device) = self.connection.device_for_interface(&touchscreen) else {
return Ok(());
};
match request {
eis::touchscreen::Request::Release => {}
eis::touchscreen::Request::Down { touchid, x, y } => {
let mut down_touch_ids = device.0.down_touch_ids.lock().unwrap();
if down_touch_ids.len() == EIS_MAX_TOUCHES {
return Err(RequestError::TooManyTouches.into());
}
if !down_touch_ids.insert(touchid) {
return Err(RequestError::DuplicatedTouchDown.into());
}
drop(down_touch_ids);
self.queue_request(EisRequest::TouchDown(TouchDown {
device,
touch_id: touchid,
x,
y,
time: 0,
}));
}
eis::touchscreen::Request::Motion { touchid, x, y } => {
if device.0.down_touch_ids.lock().unwrap().contains(&touchid) {
self.queue_request(EisRequest::TouchMotion(TouchMotion {
device,
touch_id: touchid,
x,
y,
time: 0,
}));
}
}
eis::touchscreen::Request::Up { touchid } => {
if device.0.down_touch_ids.lock().unwrap().remove(&touchid) {
self.queue_request(EisRequest::TouchUp(TouchUp {
device,
touch_id: touchid,
time: 0,
}));
}
}
eis::touchscreen::Request::Cancel { touchid } => {
if touchscreen.version() < 2 {
return Err(Error::InvalidInterfaceVersion(
"ei_touchscreen",
touchscreen.version(),
));
}
if device.0.down_touch_ids.lock().unwrap().remove(&touchid) {
self.queue_request(EisRequest::TouchCancel(TouchCancel {
device,
touch_id: touchid,
time: 0,
}));
}
}
}
Ok(())
}
}
struct SeatInner {
seat: eis::Seat,
name: Option<String>,
handle: Weak<ConnectionInner>,
advertised_capabilities: BitFlags<DeviceCapability>,
}
#[derive(Clone)]
pub struct Seat(Arc<SeatInner>);
fn add_interface<I: eis::Interface>(
device: &eis::Device,
connection: Option<&Connection>,
) -> Object {
let version = connection
.as_ref()
.and_then(|c| c.interface_version(I::NAME))
.unwrap_or(1);
device.interface::<I>(version).as_object().clone()
}
impl Seat {
#[must_use]
pub fn eis_seat(&self) -> &eis::Seat {
&self.0.seat
}
pub fn add_device(
&self,
name: Option<&str>,
device_type: eis::device::DeviceType,
capabilities: BitFlags<DeviceCapability>,
before_done_cb: impl for<'a> FnOnce(&'a Device),
) -> Device {
let connection = self.0.handle.upgrade().map(Connection);
let device_version = connection
.as_ref()
.and_then(|c| c.interface_version(eis::Device::NAME))
.unwrap_or(1);
let device = self.0.seat.device(device_version);
if let Some(name) = name {
device.name(name);
}
device.device_type(device_type);
let mut interfaces = HashMap::new();
for capability in capabilities {
if !self.0.advertised_capabilities.contains(capability) {
continue;
}
let object = match capability {
DeviceCapability::Pointer => {
add_interface::<eis::Pointer>(&device, connection.as_ref())
}
DeviceCapability::PointerAbsolute => {
add_interface::<eis::PointerAbsolute>(&device, connection.as_ref())
}
DeviceCapability::Keyboard => {
add_interface::<eis::Keyboard>(&device, connection.as_ref())
}
DeviceCapability::Touch => {
add_interface::<eis::Touchscreen>(&device, connection.as_ref())
}
DeviceCapability::Scroll => {
add_interface::<eis::Scroll>(&device, connection.as_ref())
}
DeviceCapability::Button => {
add_interface::<eis::Button>(&device, connection.as_ref())
}
};
interfaces.insert(object.interface().to_owned(), object);
}
let device = Device(Arc::new(DeviceInner {
device,
seat: self.clone(),
name: name.map(std::string::ToString::to_string),
interfaces,
handle: self.0.handle.clone(),
down_touch_ids: Mutex::new(HashSet::new()),
}));
if let Some(handle) = connection {
for interface in device.0.interfaces.values() {
handle
.0
.device_for_interface
.lock()
.unwrap()
.insert(interface.clone(), device.clone());
}
handle
.0
.devices
.lock()
.unwrap()
.insert(device.0.device.clone(), device.clone());
}
before_done_cb(&device);
device.device().done();
device
}
pub fn remove(&self) {
if let Some(handle) = self.0.handle.upgrade().map(Connection) {
let devices = handle
.0
.devices
.lock()
.unwrap()
.values()
.filter(|device| &device.0.seat == self)
.cloned()
.collect::<Vec<_>>();
for device in devices {
device.remove();
}
handle.with_next_serial(|serial| self.0.seat.destroyed(serial));
handle.0.seats.lock().unwrap().remove(&self.0.seat);
}
}
}
impl fmt::Debug for Seat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(name) = &self.0.name {
write!(f, "Seat(\"{name}\")")
} else {
write!(f, "Seat(None)")
}
}
}
impl PartialEq for Seat {
fn eq(&self, rhs: &Seat) -> bool {
Arc::ptr_eq(&self.0, &rhs.0)
}
}
impl Eq for Seat {}
impl std::hash::Hash for Seat {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.seat.0.id().hash(state);
}
}
pub trait DeviceInterface: eis::Interface {}
macro_rules! impl_device_interface {
($ty:ty) => {
impl DeviceInterface for $ty {}
};
}
impl_device_interface!(eis::Pointer);
impl_device_interface!(eis::PointerAbsolute);
impl_device_interface!(eis::Scroll);
impl_device_interface!(eis::Button);
impl_device_interface!(eis::Keyboard);
impl_device_interface!(eis::Touchscreen);
fn destroy_interface(object: crate::Object, serial: u32) {
match object.interface() {
eis::Pointer::NAME => object
.downcast_unchecked::<eis::Pointer>()
.destroyed(serial),
eis::PointerAbsolute::NAME => object
.downcast_unchecked::<eis::PointerAbsolute>()
.destroyed(serial),
eis::Scroll::NAME => object.downcast_unchecked::<eis::Scroll>().destroyed(serial),
eis::Button::NAME => object.downcast_unchecked::<eis::Button>().destroyed(serial),
eis::Keyboard::NAME => object
.downcast_unchecked::<eis::Keyboard>()
.destroyed(serial),
eis::Touchscreen::NAME => object
.downcast_unchecked::<eis::Touchscreen>()
.destroyed(serial),
_ => unreachable!(),
}
}
struct DeviceInner {
device: eis::Device,
seat: Seat,
name: Option<String>,
interfaces: HashMap<String, crate::Object>,
handle: Weak<ConnectionInner>,
down_touch_ids: Mutex<HashSet<u32>>,
}
#[derive(Clone)]
pub struct Device(Arc<DeviceInner>);
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(name) = self.name() {
write!(f, "Device(\"{name}\")")
} else {
write!(f, "Device(None)")
}
}
}
impl Device {
#[must_use]
pub fn seat(&self) -> &Seat {
&self.0.seat
}
#[must_use]
pub fn device(&self) -> &eis::Device {
&self.0.device
}
#[must_use]
pub fn name(&self) -> Option<&str> {
self.0.name.as_deref()
}
#[must_use]
pub fn interface<T: DeviceInterface>(&self) -> Option<T> {
self.0.interfaces.get(T::NAME)?.clone().downcast()
}
#[must_use]
pub fn has_capability(&self, capability: DeviceCapability) -> bool {
self.0.interfaces.contains_key(capability.interface_name())
}
pub fn remove(&self) {
if let Some(handle) = self.0.handle.upgrade().map(Connection) {
for interface in self.0.interfaces.values() {
handle
.0
.device_for_interface
.lock()
.unwrap()
.remove(interface);
handle.with_next_serial(|serial| destroy_interface(interface.clone(), serial));
}
handle.with_next_serial(|serial| self.0.device.destroyed(serial));
handle.0.devices.lock().unwrap().remove(&self.0.device);
}
}
pub fn resumed(&self) {
if let Some(handle) = self.0.handle.upgrade().map(Connection) {
handle.with_next_serial(|serial| self.device().resumed(serial));
}
}
pub fn paused(&self) {
if let Some(handle) = self.0.handle.upgrade().map(Connection) {
handle.with_next_serial(|serial| self.device().paused(serial));
}
self.0.down_touch_ids.lock().unwrap().clear();
}
pub fn start_emulating(&self, sequence: u32) {
if let Some(handle) = self.0.handle.upgrade().map(Connection) {
handle.with_next_serial(|serial| self.device().start_emulating(serial, sequence));
}
}
pub fn stop_emulating(&self) {
if let Some(handle) = self.0.handle.upgrade().map(Connection) {
handle.with_next_serial(|serial| self.device().stop_emulating(serial));
}
}
pub fn frame(&self, time: u64) {
if let Some(handle) = self.0.handle.upgrade().map(Connection) {
handle.with_next_serial(|serial| self.device().frame(serial, time));
}
}
}
impl PartialEq for Device {
fn eq(&self, rhs: &Device) -> bool {
Arc::ptr_eq(&self.0, &rhs.0)
}
}
impl Eq for Device {}
impl std::hash::Hash for Device {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.device.0.id().hash(state);
}
}
#[derive(Clone, Debug, PartialEq)]
#[allow(missing_docs)] pub enum EisRequest {
Disconnect,
Bind(Bind),
Frame(Frame),
DeviceStartEmulating(DeviceStartEmulating),
DeviceStopEmulating(DeviceStopEmulating),
PointerMotion(PointerMotion),
PointerMotionAbsolute(PointerMotionAbsolute),
Button(Button),
ScrollDelta(ScrollDelta),
ScrollStop(ScrollStop),
ScrollCancel(ScrollCancel),
ScrollDiscrete(ScrollDiscrete),
KeyboardKey(KeyboardKey),
TouchDown(TouchDown),
TouchUp(TouchUp),
TouchMotion(TouchMotion),
TouchCancel(TouchCancel),
}
impl EisRequest {
fn time_mut(&mut self) -> Option<&mut u64> {
match self {
Self::PointerMotion(evt) => Some(&mut evt.time),
Self::PointerMotionAbsolute(evt) => Some(&mut evt.time),
Self::Button(evt) => Some(&mut evt.time),
Self::ScrollDelta(evt) => Some(&mut evt.time),
Self::ScrollStop(evt) => Some(&mut evt.time),
Self::ScrollCancel(evt) => Some(&mut evt.time),
Self::ScrollDiscrete(evt) => Some(&mut evt.time),
Self::KeyboardKey(evt) => Some(&mut evt.time),
Self::TouchDown(evt) => Some(&mut evt.time),
Self::TouchUp(evt) => Some(&mut evt.time),
Self::TouchMotion(evt) => Some(&mut evt.time),
Self::TouchCancel(evt) => Some(&mut evt.time),
Self::Disconnect
| Self::Bind(_)
| Self::Frame(_)
| Self::DeviceStartEmulating(_)
| Self::DeviceStopEmulating(_) => None,
}
}
#[must_use]
pub fn device(&self) -> Option<&Device> {
match self {
Self::Frame(evt) => Some(&evt.device),
Self::DeviceStartEmulating(evt) => Some(&evt.device),
Self::DeviceStopEmulating(evt) => Some(&evt.device),
Self::PointerMotion(evt) => Some(&evt.device),
Self::PointerMotionAbsolute(evt) => Some(&evt.device),
Self::Button(evt) => Some(&evt.device),
Self::ScrollDelta(evt) => Some(&evt.device),
Self::ScrollStop(evt) => Some(&evt.device),
Self::ScrollCancel(evt) => Some(&evt.device),
Self::ScrollDiscrete(evt) => Some(&evt.device),
Self::KeyboardKey(evt) => Some(&evt.device),
Self::TouchDown(evt) => Some(&evt.device),
Self::TouchUp(evt) => Some(&evt.device),
Self::TouchMotion(evt) => Some(&evt.device),
Self::TouchCancel(evt) => Some(&evt.device),
Self::Disconnect | Self::Bind(_) => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Bind {
pub seat: Seat,
pub capabilities: BitFlags<DeviceCapability>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Frame {
pub device: Device,
pub last_serial: u32,
pub time: u64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct DeviceStartEmulating {
pub device: Device,
pub last_serial: u32,
pub sequence: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct DeviceStopEmulating {
pub device: Device,
pub last_serial: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PointerMotion {
pub device: Device,
pub time: u64,
pub dx: f32,
pub dy: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PointerMotionAbsolute {
pub device: Device,
pub time: u64,
pub dx_absolute: f32,
pub dy_absolute: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Button {
pub device: Device,
pub time: u64,
pub button: u32,
pub state: eis::button::ButtonState,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ScrollDelta {
pub device: Device,
pub time: u64,
pub dx: f32,
pub dy: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ScrollStop {
pub device: Device,
pub time: u64,
pub x: bool,
pub y: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ScrollCancel {
pub device: Device,
pub time: u64,
pub x: bool,
pub y: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ScrollDiscrete {
pub device: Device,
pub time: u64,
pub discrete_dx: i32,
pub discrete_dy: i32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct KeyboardKey {
pub device: Device,
pub time: u64,
pub key: u32,
pub state: eis::keyboard::KeyState,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TouchDown {
pub device: Device,
pub time: u64,
pub touch_id: u32,
pub x: f32,
pub y: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TouchMotion {
pub device: Device,
pub time: u64,
pub touch_id: u32,
pub x: f32,
pub y: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TouchUp {
pub device: Device,
pub time: u64,
pub touch_id: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TouchCancel {
pub device: Device,
pub time: u64,
pub touch_id: u32,
}
pub trait SeatEvent {
fn seat(&self) -> &Seat;
}
pub trait DeviceEvent: SeatEvent {
fn device(&self) -> &Device;
}
pub trait EventTime: DeviceEvent {
fn time(&self) -> u64;
}
impl SeatEvent for Bind {
fn seat(&self) -> &Seat {
&self.seat
}
}
impl<T: DeviceEvent> SeatEvent for T {
fn seat(&self) -> &Seat {
&self.device().0.seat
}
}
macro_rules! impl_device_trait {
($ty:ty) => {
impl DeviceEvent for $ty {
fn device(&self) -> &Device {
&self.device
}
}
};
($ty:ty; time) => {
impl_device_trait!($ty);
impl EventTime for $ty {
fn time(&self) -> u64 {
self.time
}
}
};
}
impl_device_trait!(Frame; time);
impl_device_trait!(DeviceStartEmulating);
impl_device_trait!(DeviceStopEmulating);
impl_device_trait!(PointerMotion; time);
impl_device_trait!(PointerMotionAbsolute; time);
impl_device_trait!(Button; time);
impl_device_trait!(ScrollDelta; time);
impl_device_trait!(ScrollStop; time);
impl_device_trait!(ScrollCancel; time);
impl_device_trait!(ScrollDiscrete; time);
impl_device_trait!(KeyboardKey; time);
impl_device_trait!(TouchDown; time);
impl_device_trait!(TouchUp; time);
impl_device_trait!(TouchMotion; time);
impl_device_trait!(TouchCancel; time);