//! Implementation of input backend trait for types provided by `libinput` use crate::backend::input::{self as backend, Axis, InputBackend}; #[cfg(feature = "backend_session")] use crate::backend::session::{AsErrno, Session, SessionObserver}; use input as libinput; use input::event; #[cfg(feature = "backend_session")] use std::path::Path; use std::{ collections::hash_map::{DefaultHasher, Entry, HashMap}, hash::{Hash, Hasher}, io::Error as IoError, os::unix::io::{AsRawFd, RawFd}, }; use calloop::{ generic::{EventedFd, Generic}, mio::Ready, InsertError, LoopHandle, Source, }; // No idea if this is the same across unix platforms // Lets make this linux exclusive for now, once someone tries to build it for // any BSD-like system, they can verify if this is right and make a PR to change this. #[cfg(all(any(target_os = "linux", target_os = "android"), feature = "backend_session"))] const INPUT_MAJOR: u32 = 13; /// Libinput based [`InputBackend`]. /// /// Tracks input of all devices given manually or via a udev seat to a provided libinput /// context. pub struct LibinputInputBackend { context: libinput::Libinput, devices: Vec, seats: HashMap, handler: Option + 'static>>, logger: ::slog::Logger, } impl LibinputInputBackend { /// Initialize a new [`LibinputInputBackend`] from a given already initialized /// [libinput context](libinput::Libinput). pub fn new(context: libinput::Libinput, logger: L) -> Self where L: Into>, { let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_libinput")); info!(log, "Initializing a libinput backend"); LibinputInputBackend { context, devices: Vec::new(), seats: HashMap::new(), handler: None, logger: log, } } } impl backend::Event for event::keyboard::KeyboardKeyEvent { fn time(&self) -> u32 { event::keyboard::KeyboardEventTrait::time(self) } } impl backend::KeyboardKeyEvent for event::keyboard::KeyboardKeyEvent { fn key_code(&self) -> u32 { use input::event::keyboard::KeyboardEventTrait; self.key() } fn state(&self) -> backend::KeyState { use input::event::keyboard::KeyboardEventTrait; self.key_state().into() } fn count(&self) -> u32 { self.seat_key_count() } } impl<'a> backend::Event for event::pointer::PointerAxisEvent { fn time(&self) -> u32 { event::pointer::PointerEventTrait::time(self) } } impl backend::PointerAxisEvent for event::pointer::PointerAxisEvent { fn amount(&self, axis: Axis) -> Option { Some(self.axis_value(axis.into())) } fn amount_discrete(&self, axis: Axis) -> Option { self.axis_value_discrete(axis.into()) } fn source(&self) -> backend::AxisSource { self.axis_source().into() } } impl backend::Event for event::pointer::PointerButtonEvent { fn time(&self) -> u32 { event::pointer::PointerEventTrait::time(self) } } impl backend::PointerButtonEvent for event::pointer::PointerButtonEvent { fn button(&self) -> backend::MouseButton { match self.button() { 0x110 => backend::MouseButton::Left, 0x111 => backend::MouseButton::Right, 0x112 => backend::MouseButton::Middle, x => backend::MouseButton::Other(x as u8), } } fn state(&self) -> backend::MouseButtonState { self.button_state().into() } } impl backend::Event for event::pointer::PointerMotionEvent { fn time(&self) -> u32 { event::pointer::PointerEventTrait::time(self) } } impl backend::PointerMotionEvent for event::pointer::PointerMotionEvent { fn delta_x(&self) -> i32 { self.dx() as i32 } fn delta_y(&self) -> i32 { self.dy() as i32 } } impl backend::Event for event::pointer::PointerMotionAbsoluteEvent { fn time(&self) -> u32 { event::pointer::PointerEventTrait::time(self) } } impl backend::PointerMotionAbsoluteEvent for event::pointer::PointerMotionAbsoluteEvent { fn x(&self) -> f64 { self.absolute_x() } fn y(&self) -> f64 { self.absolute_y() } fn x_transformed(&self, width: u32) -> u32 { self.absolute_x_transformed(width) as u32 } fn y_transformed(&self, height: u32) -> u32 { self.absolute_y_transformed(height) as u32 } } impl backend::Event for event::touch::TouchDownEvent { fn time(&self) -> u32 { event::touch::TouchEventTrait::time(self) } } impl backend::TouchDownEvent for event::touch::TouchDownEvent { fn slot(&self) -> Option { event::touch::TouchEventSlot::slot(self).map(|x| backend::TouchSlot::new(x as u64)) } fn x(&self) -> f64 { event::touch::TouchEventPosition::x(self) } fn y(&self) -> f64 { event::touch::TouchEventPosition::y(self) } fn x_transformed(&self, width: u32) -> u32 { event::touch::TouchEventPosition::x_transformed(self, width) as u32 } fn y_transformed(&self, height: u32) -> u32 { event::touch::TouchEventPosition::y_transformed(self, height) as u32 } } impl backend::Event for event::touch::TouchMotionEvent { fn time(&self) -> u32 { event::touch::TouchEventTrait::time(self) } } impl backend::TouchMotionEvent for event::touch::TouchMotionEvent { fn slot(&self) -> Option { event::touch::TouchEventSlot::slot(self).map(|x| backend::TouchSlot::new(x as u64)) } fn x(&self) -> f64 { event::touch::TouchEventPosition::x(self) } fn y(&self) -> f64 { event::touch::TouchEventPosition::y(self) } fn x_transformed(&self, width: u32) -> u32 { event::touch::TouchEventPosition::x_transformed(self, width) as u32 } fn y_transformed(&self, height: u32) -> u32 { event::touch::TouchEventPosition::y_transformed(self, height) as u32 } } impl backend::Event for event::touch::TouchUpEvent { fn time(&self) -> u32 { event::touch::TouchEventTrait::time(self) } } impl backend::TouchUpEvent for event::touch::TouchUpEvent { fn slot(&self) -> Option { event::touch::TouchEventSlot::slot(self).map(|x| backend::TouchSlot::new(x as u64)) } } impl backend::Event for event::touch::TouchCancelEvent { fn time(&self) -> u32 { event::touch::TouchEventTrait::time(self) } } impl backend::TouchCancelEvent for event::touch::TouchCancelEvent { fn slot(&self) -> Option { event::touch::TouchEventSlot::slot(self).map(|x| backend::TouchSlot::new(x as u64)) } } impl backend::Event for event::touch::TouchFrameEvent { fn time(&self) -> u32 { event::touch::TouchEventTrait::time(self) } } impl backend::TouchFrameEvent for event::touch::TouchFrameEvent {} impl InputBackend for LibinputInputBackend { type InputConfig = [libinput::Device]; type EventError = IoError; type KeyboardKeyEvent = event::keyboard::KeyboardKeyEvent; type PointerAxisEvent = event::pointer::PointerAxisEvent; type PointerButtonEvent = event::pointer::PointerButtonEvent; type PointerMotionEvent = event::pointer::PointerMotionEvent; type PointerMotionAbsoluteEvent = event::pointer::PointerMotionAbsoluteEvent; type TouchDownEvent = event::touch::TouchDownEvent; type TouchUpEvent = event::touch::TouchUpEvent; type TouchMotionEvent = event::touch::TouchMotionEvent; type TouchCancelEvent = event::touch::TouchCancelEvent; type TouchFrameEvent = event::touch::TouchFrameEvent; fn set_handler + 'static>(&mut self, mut handler: H) { if self.handler.is_some() { self.clear_handler(); } info!(self.logger, "New input handler set"); for seat in self.seats.values() { trace!(self.logger, "Calling on_seat_created with {:?}", seat); handler.on_seat_created(seat); } self.handler = Some(Box::new(handler)); } fn get_handler(&mut self) -> Option<&mut dyn backend::InputHandler> { self.handler .as_mut() .map(|handler| handler as &mut dyn backend::InputHandler) } fn clear_handler(&mut self) { if let Some(mut handler) = self.handler.take() { for seat in self.seats.values() { trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat); handler.on_seat_destroyed(seat); } info!(self.logger, "Removing input handler"); } } fn input_config(&mut self) -> &mut Self::InputConfig { &mut self.devices } fn dispatch_new_events(&mut self) -> Result<(), IoError> { use input::event::EventTrait; self.context.dispatch()?; for event in &mut self.context { match event { libinput::Event::Device(device_event) => { use input::event::device::*; match device_event { DeviceEvent::Added(device_added_event) => { let added = device_added_event.device(); let new_caps = backend::SeatCapabilities { pointer: added.has_capability(libinput::DeviceCapability::Pointer), keyboard: added.has_capability(libinput::DeviceCapability::Keyboard), touch: added.has_capability(libinput::DeviceCapability::Touch), }; let device_seat = added.seat(); info!(self.logger, "New device {:?} on seat {:?}", added.sysname(), device_seat.logical_name()); self.devices.push(added); match self.seats.entry(device_seat.clone()) { Entry::Occupied(mut seat_entry) => { let old_seat = seat_entry.get_mut(); { let caps = old_seat.capabilities_mut(); caps.pointer = new_caps.pointer || caps.pointer; caps.keyboard = new_caps.keyboard || caps.keyboard; caps.touch = new_caps.touch || caps.touch; } if let Some(ref mut handler) = self.handler { trace!(self.logger, "Calling on_seat_changed with {:?}", old_seat); handler.on_seat_changed(old_seat); } } Entry::Vacant(seat_entry) => { let mut hasher = DefaultHasher::default(); seat_entry.key().hash(&mut hasher); let seat = seat_entry.insert(backend::Seat::new( hasher.finish(), format!( "{}:{}", device_seat.physical_name(), device_seat.logical_name() ), new_caps, )); if let Some(ref mut handler) = self.handler { trace!(self.logger, "Calling on_seat_created with {:?}", seat); handler.on_seat_created(seat); } } } } DeviceEvent::Removed(device_removed_event) => { let removed = device_removed_event.device(); // remove device self.devices.retain(|dev| *dev != removed); let device_seat = removed.seat(); info!(self.logger, "Removed device {:?} on seat {:?}", removed.sysname(), device_seat.logical_name()); // update capabilities, so they appear correctly on `on_seat_changed` and `on_seat_destroyed`. if let Some(seat) = self.seats.get_mut(&device_seat) { let caps = seat.capabilities_mut(); caps.pointer = self .devices .iter() .filter(|x| x.seat() == device_seat) .any(|x| x.has_capability(libinput::DeviceCapability::Pointer)); caps.keyboard = self .devices .iter() .filter(|x| x.seat() == device_seat) .any(|x| x.has_capability(libinput::DeviceCapability::Keyboard)); caps.touch = self .devices .iter() .filter(|x| x.seat() == device_seat) .any(|x| x.has_capability(libinput::DeviceCapability::Touch)); } else { warn!(self.logger, "Seat changed that was never created"); continue; } // check if the seat has any other devices if !self.devices.iter().any(|x| x.seat() == device_seat) { // it has not, lets destroy it if let Some(seat) = self.seats.remove(&device_seat) { info!(self.logger, "Removing seat {} which no longer has any device", device_seat.logical_name()); if let Some(ref mut handler) = self.handler { trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat); handler.on_seat_destroyed(&seat); } } else { warn!(self.logger, "Seat destroyed that was never created"); continue; } // it has, notify about updates } else if let Some(ref mut handler) = self.handler { if let Some(seat) = self.seats.get(&device_seat) { trace!(self.logger, "Calling on_seat_changed with {:?}", seat); handler.on_seat_changed(&seat); } else { warn!(self.logger, "Seat changed that was never created"); continue; } } } } if let Some(ref mut handler) = self.handler { handler.on_input_config_changed(&mut self.devices); } } libinput::Event::Touch(touch_event) => { use input::event::touch::*; if let Some(ref mut handler) = self.handler { let device_seat = touch_event.device().seat(); if let Some(ref seat) = self.seats.get(&device_seat) { match touch_event { TouchEvent::Down(down_event) => { trace!(self.logger, "Calling on_touch_down with {:?}", down_event); handler.on_touch_down(seat, down_event) } TouchEvent::Motion(motion_event) => { trace!(self.logger, "Calling on_touch_motion with {:?}", motion_event); handler.on_touch_motion(seat, motion_event) } TouchEvent::Up(up_event) => { trace!(self.logger, "Calling on_touch_up with {:?}", up_event); handler.on_touch_up(seat, up_event) } TouchEvent::Cancel(cancel_event) => { trace!(self.logger, "Calling on_touch_cancel with {:?}", cancel_event); handler.on_touch_cancel(seat, cancel_event) } TouchEvent::Frame(frame_event) => { trace!(self.logger, "Calling on_touch_frame with {:?}", frame_event); handler.on_touch_frame(seat, frame_event) } } } else { warn!(self.logger, "Received touch event of non existing Seat"); continue; } } } libinput::Event::Keyboard(keyboard_event) => { use input::event::keyboard::*; match keyboard_event { KeyboardEvent::Key(key_event) => { if let Some(ref mut handler) = self.handler { let device_seat = key_event.device().seat(); if let Some(ref seat) = self.seats.get(&device_seat) { trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event); handler.on_keyboard_key(seat, key_event); } else { warn!(self.logger, "Received key event of non existing Seat"); continue; } } } } } libinput::Event::Pointer(pointer_event) => { use input::event::pointer::*; if let Some(ref mut handler) = self.handler { let device_seat = pointer_event.device().seat(); if let Some(ref seat) = self.seats.get(&device_seat) { match pointer_event { PointerEvent::Motion(motion_event) => { trace!(self.logger, "Calling on_pointer_move with {:?}", motion_event); handler.on_pointer_move(seat, motion_event); } PointerEvent::MotionAbsolute(motion_abs_event) => { trace!( self.logger, "Calling on_pointer_move_absolute with {:?}", motion_abs_event ); handler.on_pointer_move_absolute(seat, motion_abs_event); } PointerEvent::Axis(axis_event) => { trace!(self.logger, "Calling on_pointer_axis with {:?}", axis_event); handler.on_pointer_axis(seat, axis_event); } PointerEvent::Button(button_event) => { trace!(self.logger, "Calling on_pointer_button with {:?}", button_event); handler.on_pointer_button(seat, button_event); } } } else { warn!(self.logger, "Received pointer event of non existing Seat"); continue; } } } _ => {} //FIXME: What to do with the rest. } } Ok(()) } } impl From for backend::KeyState { fn from(libinput: event::keyboard::KeyState) -> Self { match libinput { event::keyboard::KeyState::Pressed => backend::KeyState::Pressed, event::keyboard::KeyState::Released => backend::KeyState::Released, } } } impl From for backend::Axis { fn from(libinput: event::pointer::Axis) -> Self { match libinput { event::pointer::Axis::Vertical => backend::Axis::Vertical, event::pointer::Axis::Horizontal => backend::Axis::Horizontal, } } } impl From for event::pointer::Axis { fn from(axis: backend::Axis) -> Self { match axis { backend::Axis::Vertical => event::pointer::Axis::Vertical, backend::Axis::Horizontal => event::pointer::Axis::Horizontal, } } } impl From for backend::AxisSource { fn from(libinput: event::pointer::AxisSource) -> Self { match libinput { event::pointer::AxisSource::Finger => backend::AxisSource::Finger, event::pointer::AxisSource::Continuous => backend::AxisSource::Continuous, event::pointer::AxisSource::Wheel => backend::AxisSource::Wheel, event::pointer::AxisSource::WheelTilt => backend::AxisSource::WheelTilt, } } } impl From for backend::MouseButtonState { fn from(libinput: event::pointer::ButtonState) -> Self { match libinput { event::pointer::ButtonState::Pressed => backend::MouseButtonState::Pressed, event::pointer::ButtonState::Released => backend::MouseButtonState::Released, } } } #[cfg(feature = "backend_session")] impl SessionObserver for libinput::Libinput { fn pause(&mut self, device: Option<(u32, u32)>) { if let Some((major, _)) = device { if major != INPUT_MAJOR { return; } } // lets hope multiple suspends are okay in case of logind? self.suspend() } fn activate(&mut self, _device: Option<(u32, u32, Option)>) { // libinput closes the devices on suspend, so we should not get any INPUT_MAJOR calls // also lets hope multiple resumes are okay in case of logind self.resume().expect("Unable to resume libinput context"); } } /// Wrapper for types implementing the [`Session`] trait to provide /// a [`libinput::LibinputInterface`] implementation. #[cfg(feature = "backend_session")] pub struct LibinputSessionInterface(S); #[cfg(feature = "backend_session")] impl From for LibinputSessionInterface { fn from(session: S) -> LibinputSessionInterface { LibinputSessionInterface(session) } } #[cfg(feature = "backend_session")] impl libinput::LibinputInterface for LibinputSessionInterface { fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { use nix::fcntl::OFlag; self.0 .open(path, OFlag::from_bits_truncate(flags)) .map_err(|err| err.as_errno().unwrap_or(1 /*Use EPERM by default*/)) } fn close_restricted(&mut self, fd: RawFd) { let _ = self.0.close(fd); } } impl AsRawFd for LibinputInputBackend { fn as_raw_fd(&self) -> RawFd { self.context.as_raw_fd() } } /// Binds a [`LibinputInputBackend`] to a given [`LoopHandle`]. /// /// Automatically feeds the backend with incoming events without any manual calls to /// [`dispatch_new_events`](InputBackend::dispatch_new_events). Should be used to achieve the smallest possible latency. pub fn libinput_bind( backend: LibinputInputBackend, handle: LoopHandle, ) -> ::std::result::Result< Source>>, InsertError>>, > { let mut source = Generic::from_fd_source(backend); source.set_interest(Ready::readable()); handle.insert_source(source, move |evt, _| { let mut backend = evt.source.borrow_mut(); if let Err(error) = backend.0.dispatch_new_events() { warn!(backend.0.logger, "Libinput errored: {}", error); } }) }