diff --git a/.travis.yml b/.travis.yml index 26e07a2..65c23e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ addons: apt: packages: - libwayland-dev + - libudev-dev branches: only: @@ -59,4 +60,3 @@ notifications: on_success: change on_failure: always on_start: never - diff --git a/Cargo.toml b/Cargo.toml index 433ba3f..3527288 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,14 @@ slog = { version = "2.0.0" } slog-stdlog = "2.0.0-0.2" glutin = { version = "~0.7.4", optional = true } glium = { version = "~0.16.0", optional = true } +input = { version = "~0.1.1", optional = true } clippy = { version = "*", optional = true } [dev-dependencies] slog-term = "~1.5" [features] -default = ["backend_glutin", "renderer_glium"] +default = ["backend_glutin", "backend_libinput", "renderer_glium"] backend_glutin = ["glutin", "wayland-server/dlopen"] renderer_glium = ["glium"] +backend_libinput = ["input"] diff --git a/src/backend/glutin.rs b/src/backend/glutin.rs index c545d52..d09401a 100644 --- a/src/backend/glutin.rs +++ b/src/backend/glutin.rs @@ -2,13 +2,18 @@ use backend::{SeatInternal, TouchSlotInternal}; +use backend::graphics::GraphicsBackend; use backend::graphics::opengl::{Api, OpenglGraphicsBackend, PixelFormat, SwapBuffersError}; -use backend::input::{Axis, AxisSource, InputBackend, InputHandler, KeyState, MouseButton, MouseButtonState, - Seat, SeatCapabilities, TouchEvent, TouchSlot}; -use glutin::{Api as GlutinApi, MouseButton as GlutinMouseButton, PixelFormat as GlutinPixelFormat}; +use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, + KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, + PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent, + TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent}; +use glutin::{Api as GlutinApi, MouseButton as GlutinMouseButton, MouseCursor, + PixelFormat as GlutinPixelFormat}; use glutin::{ContextError, CreationError, ElementState, Event, GlContext, HeadlessContext, HeadlessRendererBuilder, MouseScrollDelta, Touch, TouchPhase, Window, WindowBuilder}; use nix::c_void; +use std::cmp; use std::error::Error; use std::fmt; use std::rc::Rc; @@ -79,6 +84,17 @@ impl GlutinHeadlessRenderer { } } +impl GraphicsBackend for GlutinHeadlessRenderer { + type CursorFormat = (); + + fn set_cursor_position(&mut self, _x: u32, _y: u32) -> Result<(), ()> { + // FIXME: Maybe save position? Is it of any use? + Ok(()) + } + + fn set_cursor_representation(&mut self, _cursor: ()) {} +} + impl OpenglGraphicsBackend for GlutinHeadlessRenderer { #[inline] fn swap_buffers(&self) -> Result<(), SwapBuffersError> { @@ -130,6 +146,23 @@ impl GlutinWindowedRenderer { } } +impl GraphicsBackend for GlutinWindowedRenderer { + type CursorFormat = MouseCursor; + + fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()> { + if let Some((win_x, win_y)) = self.window.get_position() { + self.window + .set_cursor_position(win_x + x as i32, win_y + y as i32) + } else { + Err(()) + } + } + + fn set_cursor_representation(&mut self, cursor: MouseCursor) { + self.window.set_cursor(cursor); + } +} + impl OpenglGraphicsBackend for GlutinWindowedRenderer { #[inline] fn swap_buffers(&self) -> Result<(), SwapBuffersError> { @@ -200,15 +233,289 @@ impl fmt::Display for GlutinInputError { pub struct GlutinInputBackend { window: Rc, time_counter: u32, + key_counter: u32, seat: Seat, input_config: (), handler: Option + 'static>>, } +#[derive(Clone)] +/// Glutin-Backend internal event wrapping glutin's types into a `KeyboardKeyEvent` +pub struct GlutinKeyboardInputEvent { + time: u32, + key: u8, + count: u32, + state: ElementState, +} + +impl BackendEvent for GlutinKeyboardInputEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl KeyboardKeyEvent for GlutinKeyboardInputEvent { + fn key_code(&self) -> u32 { + self.key as u32 + } + + fn state(&self) -> KeyState { + self.state.into() + } + + fn count(&self) -> u32 { + self.count + } +} + +#[derive(Clone)] +/// Glutin-Backend internal event wrapping glutin's types into a `PointerMotionAbsoluteEvent` +pub struct GlutinMouseMovedEvent { + window: Rc, + time: u32, + x: i32, + y: i32, +} + +impl BackendEvent for GlutinMouseMovedEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl PointerMotionAbsoluteEvent for GlutinMouseMovedEvent { + fn x(&self) -> f64 { + self.x as f64 + } + + fn y(&self) -> f64 { + self.y as f64 + } + + fn x_transformed(&self, width: u32) -> u32 { + cmp::min(self.x * width as i32 / + self.window + .get_inner_size_points() + .unwrap_or((width, 0)) + .0 as i32, + 0) as u32 + } + + fn y_transformed(&self, height: u32) -> u32 { + cmp::min(self.y * height as i32 / + self.window + .get_inner_size_points() + .unwrap_or((0, height)) + .1 as i32, + 0) as u32 + } +} + +#[derive(Clone)] +/// Glutin-Backend internal event wrapping glutin's types into a `PointerAxisEvent` +pub struct GlutinMouseWheelEvent { + axis: Axis, + time: u32, + delta: MouseScrollDelta, +} + +impl BackendEvent for GlutinMouseWheelEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl PointerAxisEvent for GlutinMouseWheelEvent { + fn axis(&self) -> Axis { + self.axis + } + + fn source(&self) -> AxisSource { + match self.delta { + MouseScrollDelta::LineDelta(_, _) => AxisSource::Wheel, + MouseScrollDelta::PixelDelta(_, _) => AxisSource::Continuous, + } + } + + fn amount(&self) -> f64 { + match (self.axis, self.delta) { + (Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) | + (Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => x as f64, + (Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) | + (Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => y as f64, + } + } +} + +#[derive(Clone)] +/// Glutin-Backend internal event wrapping glutin's types into a `PointerButtonEvent` +pub struct GlutinMouseInputEvent { + time: u32, + button: GlutinMouseButton, + state: ElementState, +} + +impl BackendEvent for GlutinMouseInputEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl PointerButtonEvent for GlutinMouseInputEvent { + fn button(&self) -> MouseButton { + self.button.into() + } + + fn state(&self) -> MouseButtonState { + self.state.into() + } +} + +#[derive(Clone)] +/// Glutin-Backend internal event wrapping glutin's types into a `TouchDownEvent` +pub struct GlutinTouchStartedEvent { + window: Rc, + time: u32, + location: (f64, f64), + id: u64, +} + +impl BackendEvent for GlutinTouchStartedEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl TouchDownEvent for GlutinTouchStartedEvent { + fn slot(&self) -> Option { + Some(TouchSlot::new(self.id)) + } + + fn x(&self) -> f64 { + self.location.0 + } + + fn y(&self) -> f64 { + self.location.1 + } + + fn x_transformed(&self, width: u32) -> u32 { + cmp::min(self.location.0 as i32 * width as i32 / + self.window + .get_inner_size_points() + .unwrap_or((width, 0)) + .0 as i32, + 0) as u32 + } + + fn y_transformed(&self, height: u32) -> u32 { + cmp::min(self.location.1 as i32 * height as i32 / + self.window + .get_inner_size_points() + .unwrap_or((0, height)) + .1 as i32, + 0) as u32 + } +} + +#[derive(Clone)] +/// Glutin-Backend internal event wrapping glutin's types into a `TouchMotionEvent` +pub struct GlutinTouchMovedEvent { + window: Rc, + time: u32, + location: (f64, f64), + id: u64, +} + +impl BackendEvent for GlutinTouchMovedEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl TouchMotionEvent for GlutinTouchMovedEvent { + fn slot(&self) -> Option { + Some(TouchSlot::new(self.id)) + } + + fn x(&self) -> f64 { + self.location.0 + } + + fn y(&self) -> f64 { + self.location.1 + } + + fn x_transformed(&self, width: u32) -> u32 { + self.location.0 as u32 * width / + self.window + .get_inner_size_points() + .unwrap_or((width, 0)) + .0 + } + + fn y_transformed(&self, height: u32) -> u32 { + self.location.1 as u32 * height / + self.window + .get_inner_size_points() + .unwrap_or((0, height)) + .1 + } +} + +#[derive(Clone)] +/// Glutin-Backend internal event wrapping glutin's types into a `TouchUpEvent` +pub struct GlutinTouchEndedEvent { + time: u32, + id: u64, +} + +impl BackendEvent for GlutinTouchEndedEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl TouchUpEvent for GlutinTouchEndedEvent { + fn slot(&self) -> Option { + Some(TouchSlot::new(self.id)) + } +} + +#[derive(Clone)] +/// Glutin-Backend internal event wrapping glutin's types into a `TouchCancelEvent` +pub struct GlutinTouchCancelledEvent { + time: u32, + id: u64, +} + +impl BackendEvent for GlutinTouchCancelledEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl TouchCancelEvent for GlutinTouchCancelledEvent { + fn slot(&self) -> Option { + Some(TouchSlot::new(self.id)) + } +} + impl InputBackend for GlutinInputBackend { type InputConfig = (); type EventError = GlutinInputError; + type KeyboardKeyEvent = GlutinKeyboardInputEvent; + type PointerAxisEvent = GlutinMouseWheelEvent; + type PointerButtonEvent = GlutinMouseInputEvent; + type PointerMotionEvent = UnusedEvent; + type PointerMotionAbsoluteEvent = GlutinMouseMovedEvent; + type TouchDownEvent = GlutinTouchStartedEvent; + type TouchUpEvent = GlutinTouchEndedEvent; + type TouchMotionEvent = GlutinTouchMovedEvent; + type TouchCancelEvent = GlutinTouchCancelledEvent; + type TouchFrameEvent = UnusedEvent; + fn set_handler + 'static>(&mut self, mut handler: H) { if self.handler.is_some() { self.clear_handler(); @@ -233,15 +540,6 @@ impl InputBackend for GlutinInputBackend { &mut self.input_config } - fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()> { - if let Some((win_x, win_y)) = self.window.get_position() { - self.window - .set_cursor_position(win_x + x as i32, win_y + y as i32) - } else { - Err(()) - } - } - /// Processes new events of the underlying event loop to drive the set `InputHandler`. /// /// You need to periodically call this function to keep the underlying event loop and @@ -259,104 +557,109 @@ impl InputBackend for GlutinInputBackend { if let Some(ref mut handler) = self.handler { match event { Event::KeyboardInput(state, key_code, _) => { + match state { + ElementState::Pressed => self.key_counter += 1, + ElementState::Released => { + self.key_counter = self.key_counter.checked_sub(1).unwrap_or(0) + } + }; handler.on_keyboard_key(&self.seat, - self.time_counter, - key_code as u32, - state.into(), - 1) + GlutinKeyboardInputEvent { + time: self.time_counter, + key: key_code, + count: self.key_counter, + state: state, + }) } Event::MouseMoved(x, y) => { - handler.on_pointer_move(&self.seat, self.time_counter, (x as u32, y as u32)) + handler.on_pointer_move_absolute(&self.seat, + GlutinMouseMovedEvent { + window: self.window.clone(), + time: self.time_counter, + x: x, + y: y, + }) } Event::MouseWheel(delta, _) => { + let event = GlutinMouseWheelEvent { + axis: Axis::Horizontal, + time: self.time_counter, + delta: delta, + }; match delta { - MouseScrollDelta::LineDelta(x, y) => { - if x != 0.0 { - handler.on_pointer_scroll(&self.seat, - self.time_counter, - Axis::Horizontal, - AxisSource::Wheel, - x as f64); - } - if y != 0.0 { - handler.on_pointer_scroll(&self.seat, - self.time_counter, - Axis::Vertical, - AxisSource::Wheel, - y as f64); - } - } + MouseScrollDelta::LineDelta(x, y) | MouseScrollDelta::PixelDelta(x, y) => { if x != 0.0 { - handler.on_pointer_scroll(&self.seat, - self.time_counter, - Axis::Vertical, - AxisSource::Continous, - x as f64); + handler.on_pointer_axis(&self.seat, event.clone()); } if y != 0.0 { - handler.on_pointer_scroll(&self.seat, - self.time_counter, - Axis::Horizontal, - AxisSource::Continous, - y as f64); + handler.on_pointer_axis(&self.seat, event); } } } } Event::MouseInput(state, button) => { - handler.on_pointer_button(&self.seat, self.time_counter, button.into(), state.into()) + handler.on_pointer_button(&self.seat, + GlutinMouseInputEvent { + time: self.time_counter, + button: button, + state: state, + }) } Event::Touch(Touch { phase: TouchPhase::Started, location: (x, y), id, }) => { - handler.on_touch(&self.seat, - self.time_counter, - TouchEvent::Down { - slot: Some(TouchSlot::new(id as u32)), - x: x, - y: y, - }) + handler.on_touch_down(&self.seat, + GlutinTouchStartedEvent { + window: self.window.clone(), + time: self.time_counter, + location: (x, y), + id: id, + }) } Event::Touch(Touch { phase: TouchPhase::Moved, location: (x, y), id, }) => { - handler.on_touch(&self.seat, - self.time_counter, - TouchEvent::Motion { - slot: Some(TouchSlot::new(id as u32)), - x: x, - y: y, - }) + handler.on_touch_motion(&self.seat, + GlutinTouchMovedEvent { + window: self.window.clone(), + time: self.time_counter, + location: (x, y), + id: id, + }) } Event::Touch(Touch { phase: TouchPhase::Ended, location: (x, y), id, }) => { - handler.on_touch(&self.seat, - self.time_counter, - TouchEvent::Motion { - slot: Some(TouchSlot::new(id as u32)), - x: x, - y: y, - }); - handler.on_touch(&self.seat, - self.time_counter, - TouchEvent::Up { slot: Some(TouchSlot::new(id as u32)) }); + handler.on_touch_motion(&self.seat, + GlutinTouchMovedEvent { + window: self.window.clone(), + time: self.time_counter, + location: (x, y), + id: id, + }); + handler.on_touch_up(&self.seat, + GlutinTouchEndedEvent { + time: self.time_counter, + id: id, + }); } Event::Touch(Touch { phase: TouchPhase::Cancelled, id, .. }) => { - handler.on_touch(&self.seat, - self.time_counter, - TouchEvent::Cancel { slot: Some(TouchSlot::new(id as u32)) }) + handler.on_touch_cancel(&self.seat, + GlutinTouchCancelledEvent { + time: self.time_counter, + id: id, + }) } Event::Closed => return Err(GlutinInputError::WindowClosed), _ => {} @@ -373,6 +676,7 @@ impl GlutinInputBackend { GlutinInputBackend { window: window, time_counter: 0, + key_counter: 0, seat: Seat::new(0, SeatCapabilities { pointer: true, diff --git a/src/backend/graphics/mod.rs b/src/backend/graphics/mod.rs index 75b2ee6..4ad08f8 100644 --- a/src/backend/graphics/mod.rs +++ b/src/backend/graphics/mod.rs @@ -2,5 +2,32 @@ //! //! Note: Not every api may be supported by every backend +/// General functions any graphics backend should support independently from it's rendering +/// techique. +pub trait GraphicsBackend { + /// Format representing the image drawn for the cursor. + type CursorFormat; + + /// Sets the cursor position and therefor updates the drawn cursors position. + /// Useful as well for e.g. pointer wrapping. + /// + /// Not guaranteed to be supported on every backend. The result usually + /// depends on the backend, the cursor might be "owned" by another more priviledged + /// compositor (running nested). + /// + /// In these cases setting the position is actually not required, as movement is done + /// by the higher compositor and not by the backend. It is still good practice to update + /// the position after every recieved event, but don't rely on pointer wrapping working. + /// + fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()>; + + /// Set the cursor drawn on the `GraphicsBackend`. + /// + /// The format is entirely dictated by the concrete implementation and might range + /// from raw image buffers over a fixed list of possible cursor types to simply the + /// void type () to represent no possible customization of the cursor itself. + fn set_cursor_representation(&mut self, cursor: Self::CursorFormat); +} + pub mod software; pub mod opengl; diff --git a/src/backend/graphics/opengl.rs b/src/backend/graphics/opengl.rs index a03f93b..e5884f5 100644 --- a/src/backend/graphics/opengl.rs +++ b/src/backend/graphics/opengl.rs @@ -1,5 +1,7 @@ //! Common traits and types for opengl rendering on graphics backends + +use super::GraphicsBackend; use nix::c_void; /// Error that can happen when swapping buffers. @@ -61,7 +63,7 @@ pub struct PixelFormat { /// Trait that describes objects that have an OpenGl context /// and can be used to render upon -pub trait OpenglGraphicsBackend { +pub trait OpenglGraphicsBackend: GraphicsBackend { /// Swaps buffers at the end of a frame. fn swap_buffers(&self) -> Result<(), SwapBuffersError>; diff --git a/src/backend/graphics/software.rs b/src/backend/graphics/software.rs index 278885a..e053c77 100644 --- a/src/backend/graphics/software.rs +++ b/src/backend/graphics/software.rs @@ -1,10 +1,12 @@ //! Common traits and types used for software rendering on graphics backends + +use super::GraphicsBackend; use std::error::Error; use wayland_server::protocol::wl_shm::Format; /// Trait that describes objects providing a software rendering implementation -pub trait CpuGraphicsBackend { +pub trait CpuGraphicsBackend: GraphicsBackend { /// Render a given buffer of a given format at a specified place in the framebuffer /// /// # Error diff --git a/src/backend/input.rs b/src/backend/input.rs index 03d0ca5..c1e7874 100644 --- a/src/backend/input.rs +++ b/src/backend/input.rs @@ -11,20 +11,27 @@ use std::error::Error; /// however multiseat configurations are possible and should be treated as /// separated users, all with their own focus, input and cursor available. /// -/// Seats can be checked for equality and hashed for differentiation. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// Seats referring to the same internal id will always be equal and result in the same +/// hash, but capabilities of cloned and copied `Seat`s will not be updated by smithay. +/// Always referr to the `Seat` given by a callback for up-to-date information. You may +/// use this to calculate the differences since the last callback. +#[derive(Debug, Clone, Copy, Eq)] pub struct Seat { - id: u32, + id: u64, capabilities: SeatCapabilities, } impl SeatInternal for Seat { - fn new(id: u32, capabilities: SeatCapabilities) -> Seat { + fn new(id: u64, capabilities: SeatCapabilities) -> Seat { Seat { id: id, capabilities: capabilities, } } + + fn capabilities_mut(&mut self) -> &mut SeatCapabilities { + &mut self.capabilities + } } impl Seat { @@ -34,6 +41,20 @@ impl Seat { } } +impl ::std::cmp::PartialEq for Seat { + fn eq(&self, other: &Seat) -> bool { + self.id == other.id + } +} + +impl ::std::hash::Hash for Seat { + fn hash(&self, state: &mut H) + where H: ::std::hash::Hasher + { + self.id.hash(state); + } +} + /// Describes capabilities a `Seat` has. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SeatCapabilities { @@ -45,6 +66,31 @@ pub struct SeatCapabilities { pub touch: bool, } +/// Trait for generic functions every input event does provide/ +pub trait Event { + /// Returns an upward counting variable useful for event ordering. + /// + /// Makes no gurantees about actual time passed between events. + // # TODO: + // - check if events can even arrive out of order. + // - Make stronger time guarantees, if possible + fn time(&self) -> u32; +} + +/// Used to mark events never emitted by an `InputBackend` implementation. +/// +/// Implements all event types and can be used in place for any `Event` type, +/// that is not used by an `InputBackend` implementation. Initialization is not +/// possible, making accidential use impossible and enabling a lot of possible +/// compiler optimizations. +pub enum UnusedEvent {} + +impl Event for UnusedEvent { + fn time(&self) -> u32 { + match *self {} + } +} + /// State of key on a keyboard. Either pressed or released #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum KeyState { @@ -54,6 +100,30 @@ pub enum KeyState { Pressed, } +/// Trait for keyboard event +pub trait KeyboardKeyEvent: Event { + /// Code of the pressed key. See linux/input-event-codes.h + fn key_code(&self) -> u32; + /// State of the key + fn state(&self) -> KeyState; + /// Total number of keys pressed on all devices on the associated `Seat` + fn count(&self) -> u32; +} + +impl KeyboardKeyEvent for UnusedEvent { + fn key_code(&self) -> u32 { + match *self {} + } + + fn state(&self) -> KeyState { + match *self {} + } + + fn count(&self) -> u32 { + match *self {} + } +} + /// A particular mouse button #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum MouseButton { @@ -76,6 +146,24 @@ pub enum MouseButtonState { Pressed, } +/// Common methods pointer event generated by pressed buttons do implement +pub trait PointerButtonEvent: Event { + /// Pressed button of the event + fn button(&self) -> MouseButton; + /// State of the button + fn state(&self) -> MouseButtonState; +} + +impl PointerButtonEvent for UnusedEvent { + fn button(&self) -> MouseButton { + match *self {} + } + + fn state(&self) -> MouseButtonState { + match *self {} + } +} + /// Axis when scrolling #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Axis { @@ -105,7 +193,7 @@ pub enum AxisSource { /// The coordinate system is identical to /// the cursor movement, i.e. a scroll value of 1 represents the equivalent relative /// motion of 1. - Continous, + Continuous, /// Scroll wheel. /// /// No terminating event is guaranteed (though it may happen). Scrolling is in @@ -118,6 +206,105 @@ pub enum AxisSource { WheelTilt, } +/// Trait for pointer events generated by scrolling on an axis. +pub trait PointerAxisEvent: Event { + /// `Axis` this event was generated for. + fn axis(&self) -> Axis; + /// Source of the scroll event. Important for interpretation of `amount`. + fn source(&self) -> AxisSource; + /// Amount of scrolling on the given `Axis`. See `source` for interpretation. + fn amount(&self) -> f64; +} + +impl PointerAxisEvent for UnusedEvent { + fn axis(&self) -> Axis { + match *self {} + } + + fn source(&self) -> AxisSource { + match *self {} + } + + fn amount(&self) -> f64 { + match *self {} + } +} + +/// Trait for pointer events generated by relative device movement. +pub trait PointerMotionEvent: Event { + /// Delta between the last and new pointer device position interpreted as pixel movement + fn delta(&self) -> (u32, u32) { + (self.delta_x(), self.delta_y()) + } + + /// Delta on the x axis between the last and new pointer device position interpreted as pixel movement + fn delta_x(&self) -> u32; + /// Delta on the y axis between the last and new pointer device position interpreted as pixel movement + fn delta_y(&self) -> u32; +} + +impl PointerMotionEvent for UnusedEvent { + fn delta_x(&self) -> u32 { + match *self {} + } + + fn delta_y(&self) -> u32 { + match *self {} + } +} + +/// Trait for pointer events generated by absolute device positioning. +pub trait PointerMotionAbsoluteEvent: Event { + /// Device position in it's original coordinate space. + /// + /// The format is defined by the backend implementation. + fn position(&self) -> (f64, f64) { + (self.x(), self.y()) + } + + /// Device x position in it's original coordinate space. + /// + /// The format is defined by the backend implementation. + fn x(&self) -> f64; + + /// Device y position in it's original coordinate space. + /// + /// The format is defined by the backend implementation. + fn y(&self) -> f64; + + /// Device position converted to the targets coordinate space. + /// E.g. the focused output's resolution. + fn position_transformed(&self, coordinate_space: (u32, u32)) -> (u32, u32) { + (self.x_transformed(coordinate_space.0), self.y_transformed(coordinate_space.1)) + } + + /// Device x position converted to the targets coordinate space's width. + /// E.g. the focused output's width. + fn x_transformed(&self, width: u32) -> u32; + + /// Device y position converted to the targets coordinate space's height. + /// E.g. the focused output's height. + fn y_transformed(&self, height: u32) -> u32; +} + +impl PointerMotionAbsoluteEvent for UnusedEvent { + fn x(&self) -> f64 { + match *self {} + } + + fn y(&self) -> f64 { + match *self {} + } + + fn x_transformed(&self, _width: u32) -> u32 { + match *self {} + } + + fn y_transformed(&self, _height: u32) -> u32 { + match *self {} + } +} + /// Slot of a different touch event. /// /// Touch events are groubed by slots, usually to identify different @@ -125,68 +312,193 @@ pub enum AxisSource { /// be interpreted in the context of other events on the same slot. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TouchSlot { - id: u32, + id: u64, } impl TouchSlotInternal for TouchSlot { - fn new(id: u32) -> Self { + fn new(id: u64) -> Self { TouchSlot { id: id } } } -/// Touch event -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum TouchEvent { - /// The start of an event at a given position (x, y). +/// Trait for touch events starting at a given position. +pub trait TouchDownEvent: Event { + /// `TouchSlot`, if the device has multi-touch capabilities + fn slot(&self) -> Option; + + /// Touch position in the device's native coordinate space /// - /// If the device has multi-touch capabilities a slot is given. - Down { - /// `TouchSlot`, if the device has multi-touch capabilities - slot: Option, - /// Absolute x-coordinate of the touch position. - x: f64, - /// Absolute y-coordinate of the touch position. - y: f64, - }, - /// Movement of a touch on the device surface to a given position (x, y). + /// The actual format is defined by the implementation. + fn position(&self) -> (f64, f64) { + (self.x(), self.y()) + } + + /// Touch position converted into the target coordinate space. + /// E.g. the focused output's resolution. + fn position_transformed(&self, coordinate_space: (u32, u32)) -> (u32, u32) { + (self.x_transformed(coordinate_space.0), self.y_transformed(coordinate_space.1)) + } + + /// Touch event's x-coordinate in the device's native coordinate space /// - /// If the device has multi-touch capabilities a slot is given. - Motion { - /// `TouchSlot`, if the device has multi-touch capabilities - slot: Option, - /// Absolute x-coordinate of the final touch position after the motion. - x: f64, - /// Absolute y-coordinate of the final touch position after the motion. - y: f64, - }, - /// Stop of an event chain. + /// The actual format is defined by the implementation. + fn x(&self) -> f64; + + /// Touch event's x-coordinate in the device's native coordinate space /// - /// If the device has multi-touch capabilities a slot is given. - Up { - /// `TouchSlot`, if the device has multi-touch capabilities - slot: Option, - }, - /// Cancel of an event chain. All previous events in the chain should be ignored. - /// - /// If the device has multi-touch capabilities a slot is given. - Cancel { - /// `TouchSlot`, if the device has multi-touch capabilities - slot: Option, - }, - /// Signals the end of a set of touchpoints at one device sample time. - Frame, + /// The actual format is defined by the implementation. + fn y(&self) -> f64; + + /// Touch event's x position converted to the targets coordinate space's width. + /// E.g. the focused output's width. + fn x_transformed(&self, width: u32) -> u32; + + /// Touch event's y position converted to the targets coordinate space's width. + /// E.g. the focused output's width. + fn y_transformed(&self, height: u32) -> u32; } +impl TouchDownEvent for UnusedEvent { + fn slot(&self) -> Option { + match *self {} + } + + fn x(&self) -> f64 { + match *self {} + } + + fn y(&self) -> f64 { + match *self {} + } + + fn x_transformed(&self, _width: u32) -> u32 { + match *self {} + } + + fn y_transformed(&self, _height: u32) -> u32 { + match *self {} + } +} + +/// Trait for touch events regarding movement on the screen +pub trait TouchMotionEvent: Event { + /// `TouchSlot`, if the device has multi-touch capabilities + fn slot(&self) -> Option; + + /// Touch position in the device's native coordinate space + /// + /// The actual format is defined by the implementation. + fn position(&self) -> (f64, f64) { + (self.x(), self.y()) + } + + /// Touch position converted into the target coordinate space. + /// E.g. the focused output's resolution. + fn position_transformed(&self, coordinate_space: (u32, u32)) -> (u32, u32) { + (self.x_transformed(coordinate_space.0), self.y_transformed(coordinate_space.1)) + } + + /// Touch event's x-coordinate in the device's native coordinate space + /// + /// The actual format is defined by the implementation. + fn x(&self) -> f64; + + /// Touch event's x-coordinate in the device's native coordinate space + /// + /// The actual format is defined by the implementation. + fn y(&self) -> f64; + + /// Touch event's x position converted to the targets coordinate space's width. + /// E.g. the focused output's width. + fn x_transformed(&self, width: u32) -> u32; + + /// Touch event's y position converted to the targets coordinate space's width. + /// E.g. the focused output's width. + fn y_transformed(&self, height: u32) -> u32; +} + +impl TouchMotionEvent for UnusedEvent { + fn slot(&self) -> Option { + match *self {} + } + + fn x(&self) -> f64 { + match *self {} + } + + fn y(&self) -> f64 { + match *self {} + } + + fn x_transformed(&self, _width: u32) -> u32 { + match *self {} + } + + fn y_transformed(&self, _height: u32) -> u32 { + match *self {} + } +} + +/// Trait for touch events finishing. +pub trait TouchUpEvent: Event { + /// `TouchSlot`, if the device has multi-touch capabilities + fn slot(&self) -> Option; +} + +impl TouchUpEvent for UnusedEvent { + fn slot(&self) -> Option { + match *self {} + } +} + +/// Trait for touch events cancelling the chain +pub trait TouchCancelEvent: Event { + /// `TouchSlot`, if the device has multi-touch capabilities + fn slot(&self) -> Option; +} + +impl TouchCancelEvent for UnusedEvent { + fn slot(&self) -> Option { + match *self {} + } +} + +/// Trait for touch frame events +pub trait TouchFrameEvent: Event {} + +impl TouchFrameEvent for UnusedEvent {} + /// Trait that describes objects providing a source of input events. All input backends /// need to implemenent this and provide the same base gurantees about the presicion of /// given events. pub trait InputBackend: Sized { /// Type of input device associated with the backend - type InputConfig; + type InputConfig: ?Sized; /// Type representing errors that may be returned when processing events type EventError: Error; + /// Type representing keyboard events + type KeyboardKeyEvent: KeyboardKeyEvent; + /// Type representing axis events on pointer devices + type PointerAxisEvent: PointerAxisEvent; + /// Type representing button events on pointer devices + type PointerButtonEvent: PointerButtonEvent; + /// Type representing motion events of pointer devices + type PointerMotionEvent: PointerMotionEvent; + /// Type representing motion events of pointer devices + type PointerMotionAbsoluteEvent: PointerMotionAbsoluteEvent; + /// Type representing touch events starting + type TouchDownEvent: TouchDownEvent; + /// Type representing touch events ending + type TouchUpEvent: TouchUpEvent; + /// Type representing touch events from moving + type TouchMotionEvent: TouchMotionEvent; + /// Type representing cancelling of touch events + type TouchCancelEvent: TouchCancelEvent; + /// Type representing touch frame events + type TouchFrameEvent: TouchFrameEvent; + /// Sets a new handler for this `InputBackend` fn set_handler + 'static>(&mut self, handler: H); /// Get a reference to the currently set handler, if any @@ -199,13 +511,6 @@ pub trait InputBackend: Sized { /// Processes new events of the underlying backend and drives the `InputHandler`. fn dispatch_new_events(&mut self) -> Result<(), Self::EventError>; - - /// Sets the cursor position, useful for e.g. pointer wrapping. - /// - /// Not guaranteed to be supported on every backend. The result usually - /// depends on the capability of the backend, but may also fail for certain - /// specific actions. See the backends documentation. - fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()>; } /// Implement to receive input events from any `InputBackend`. @@ -215,6 +520,10 @@ pub trait InputHandler { /// Called when an existing `Seat` has been destroyed. fn on_seat_destroyed(&mut self, seat: &Seat); /// Called when a `Seat`'s properties have changed. + /// + /// ## Note: + /// + /// It is not guaranteed that any change has actually happened. fn on_seat_changed(&mut self, seat: &Seat); /// Called when a new keyboard event was received. @@ -222,66 +531,74 @@ pub trait InputHandler { /// # Arguments /// /// - `seat` - The `Seat` the event belongs to - /// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events. - /// - `key_code` - Code of the pressed key. See linux/input-event-codes.h - /// - `state` - `KeyState` of the event - /// - `count` - Total number of keys pressed on all devices on the associated `Seat` + /// - `event` - The keyboard event /// - /// # TODO: - /// - check if events can arrive out of order. - /// - Make stronger time guarantees - fn on_keyboard_key(&mut self, seat: &Seat, time: u32, key_code: u32, state: KeyState, count: u32); + fn on_keyboard_key(&mut self, seat: &Seat, event: B::KeyboardKeyEvent); + /// Called when a new pointer movement event was received. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to - /// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events. - /// - `to` - Absolute screen coordinates of the pointer moved to. + /// - `event` - The pointer movement event + fn on_pointer_move(&mut self, seat: &Seat, event: B::PointerMotionEvent); + /// Called when a new pointer absolute movement event was received. /// - /// # TODO: - /// - check if events can arrive out of order. - /// - Make stronger time guarantees - fn on_pointer_move(&mut self, seat: &Seat, time: u32, to: (u32, u32)); + /// # Arguments + /// + /// - `seat` - The `Seat` the event belongs to + /// - `event` - The pointer absolute movement event + fn on_pointer_move_absolute(&mut self, seat: &Seat, event: B::PointerMotionAbsoluteEvent); /// Called when a new pointer button event was received. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to - /// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events. - /// - `button` - Which button was pressed.. - /// - `state` - `MouseButtonState` of the event - /// - /// # TODO: - /// - check if events can arrive out of order. - /// - Make stronger time guarantees - fn on_pointer_button(&mut self, seat: &Seat, time: u32, button: MouseButton, state: MouseButtonState); + /// - `event` - The pointer button event + fn on_pointer_button(&mut self, seat: &Seat, event: B::PointerButtonEvent); /// Called when a new pointer scroll event was received. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to - /// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events. - /// - `axis` - `Axis` this event was generated for. - /// - `source` - Source of the scroll event. Important for interpretation of `amount`. - /// - `amount` - Amount of scrolling on the given `Axis`. See `source` for interpretation. - /// - /// # TODO: - /// - check if events can arrive out of order. - /// - Make stronger time guarantees - fn on_pointer_scroll(&mut self, seat: &Seat, time: u32, axis: Axis, source: AxisSource, amount: f64); - /// Called when a new touch event was received. + /// - `event` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events. + fn on_pointer_axis(&mut self, seat: &Seat, event: B::PointerAxisEvent); + + /// Called when a new touch down event was received. /// /// # Arguments /// /// - `seat` - The `Seat` the event belongs to - /// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events. - /// - `event` - Touch event recieved. See `TouchEvent`. + /// - `event` - The touch down event + fn on_touch_down(&mut self, seat: &Seat, event: B::TouchDownEvent); + /// Called when a new touch motion event was received. /// - /// # TODO: - /// - check if events can arrive out of order. - /// - Make stronger time guarantees - fn on_touch(&mut self, seat: &Seat, time: u32, event: TouchEvent); + /// # Arguments + /// + /// - `seat` - The `Seat` the event belongs to + /// - `event` - The touch motion event. + fn on_touch_motion(&mut self, seat: &Seat, event: B::TouchMotionEvent); + /// Called when a new touch up event was received. + /// + /// # Arguments + /// + /// - `seat` - The `Seat` the event belongs to + /// - `event` - The touch up event. + fn on_touch_up(&mut self, seat: &Seat, event: B::TouchUpEvent); + /// Called when a new touch cancel event was received. + /// + /// # Arguments + /// + /// - `seat` - The `Seat` the event belongs to + /// - `event` - The touch cancel event. + fn on_touch_cancel(&mut self, seat: &Seat, event: B::TouchCancelEvent); + /// Called when a new touch frame event was received. + /// + /// # Arguments + /// + /// - `seat` - The `Seat` the event belongs to + /// - `event` - The touch frame event. + fn on_touch_frame(&mut self, seat: &Seat, event: B::TouchFrameEvent); /// Called when the `InputConfig` was changed through an external event. /// @@ -303,24 +620,44 @@ impl InputHandler for Box> { (**self).on_seat_changed(seat) } - fn on_keyboard_key(&mut self, seat: &Seat, time: u32, key_code: u32, state: KeyState, count: u32) { - (**self).on_keyboard_key(seat, time, key_code, state, count) + fn on_keyboard_key(&mut self, seat: &Seat, event: B::KeyboardKeyEvent) { + (**self).on_keyboard_key(seat, event) } - fn on_pointer_move(&mut self, seat: &Seat, time: u32, to: (u32, u32)) { - (**self).on_pointer_move(seat, time, to) + fn on_pointer_move(&mut self, seat: &Seat, event: B::PointerMotionEvent) { + (**self).on_pointer_move(seat, event) } - fn on_pointer_button(&mut self, seat: &Seat, time: u32, button: MouseButton, state: MouseButtonState) { - (**self).on_pointer_button(seat, time, button, state) + fn on_pointer_move_absolute(&mut self, seat: &Seat, event: B::PointerMotionAbsoluteEvent) { + (**self).on_pointer_move_absolute(seat, event) } - fn on_pointer_scroll(&mut self, seat: &Seat, time: u32, axis: Axis, source: AxisSource, amount: f64) { - (**self).on_pointer_scroll(seat, time, axis, source, amount) + fn on_pointer_button(&mut self, seat: &Seat, event: B::PointerButtonEvent) { + (**self).on_pointer_button(seat, event) } - fn on_touch(&mut self, seat: &Seat, time: u32, event: TouchEvent) { - (**self).on_touch(seat, time, event) + fn on_pointer_axis(&mut self, seat: &Seat, event: B::PointerAxisEvent) { + (**self).on_pointer_axis(seat, event) + } + + fn on_touch_down(&mut self, seat: &Seat, event: B::TouchDownEvent) { + (**self).on_touch_down(seat, event) + } + + fn on_touch_motion(&mut self, seat: &Seat, event: B::TouchMotionEvent) { + (**self).on_touch_motion(seat, event) + } + + fn on_touch_up(&mut self, seat: &Seat, event: B::TouchUpEvent) { + (**self).on_touch_up(seat, event) + } + + fn on_touch_cancel(&mut self, seat: &Seat, event: B::TouchCancelEvent) { + (**self).on_touch_cancel(seat, event) + } + + fn on_touch_frame(&mut self, seat: &Seat, event: B::TouchFrameEvent) { + (**self).on_touch_frame(seat, event) } fn on_input_config_changed(&mut self, config: &mut B::InputConfig) { diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs new file mode 100644 index 0000000..62d46f5 --- /dev/null +++ b/src/backend/libinput.rs @@ -0,0 +1,538 @@ +//! Implementation of input backend trait for types provided by `libinput` + +use backend::{SeatInternal, TouchSlotInternal}; +use backend::input as backend; +use input as libinput; +use input::event; +use std::collections::hash_map::{DefaultHasher, Entry, HashMap}; +use std::hash::{Hash, Hasher}; + +use std::io::Error as IoError; +use std::rc::Rc; + +/// 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. + pub fn new(context: libinput::Libinput, logger: L) -> Self + where L: Into> + { + let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_libinput")); + info!(log, "Initializing a libinput backend"); + LibinputInputBackend { + context: 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() + } +} + +/// Wrapper for libinput pointer axis events to implement `backend::input::PointerAxisEvent` +pub struct PointerAxisEvent { + axis: event::pointer::Axis, + event: Rc, +} + +impl<'a> backend::Event for PointerAxisEvent { + fn time(&self) -> u32 { + use input::event::pointer::PointerEventTrait; + self.event.time() + } +} + +impl<'a> backend::PointerAxisEvent for PointerAxisEvent { + fn axis(&self) -> backend::Axis { + self.axis.into() + } + + fn source(&self) -> backend::AxisSource { + self.event.axis_source().into() + } + + fn amount(&self) -> f64 { + match self.source() { + backend::AxisSource::Finger | + backend::AxisSource::Continuous => self.event.axis_value(self.axis), + backend::AxisSource::Wheel | + backend::AxisSource::WheelTilt => self.event.axis_value_discrete(self.axis).unwrap(), + } + } +} + +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) -> u32 { + self.dx() as u32 + } + fn delta_y(&self) -> u32 { + self.dy() as u32 + } +} + +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 backend::InputBackend for LibinputInputBackend { + type InputConfig = [libinput::Device]; + type EventError = IoError; + + type KeyboardKeyEvent = event::keyboard::KeyboardKeyEvent; + type PointerAxisEvent = 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 backend::InputHandler> { + self.handler + .as_mut() + .map(|handler| handler as &mut 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(); + self.devices.push(added); + + match self.seats.entry(device_seat) { + 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(), + 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(); + + // 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 { + panic!("Seat changed that was never created") + } + + // 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) { + if let Some(ref mut handler) = self.handler { + trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat); + handler.on_seat_destroyed(&seat); + } + } else { + panic!("Seat destroyed that was never created"); + } + // it has, notify about updates + } else if let Some(ref mut handler) = self.handler { + let seat = self.seats[&device_seat]; + trace!(self.logger, "Calling on_seat_changed with {:?}", seat); + handler.on_seat_changed(&seat); + } + } + } + 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(); + let seat = &self.seats + .get(&device_seat) + .expect("Recieved key event of non existing 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) + } + } + } + } + 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(); + let seat = &self.seats + .get(&device_seat) + .expect("Recieved key event of non existing Seat"); + trace!(self.logger, "Calling on_keyboard_key with {:?}", key_event); + handler.on_keyboard_key(seat, key_event); + } + } + } + } + libinput::Event::Pointer(pointer_event) => { + use input::event::pointer::*; + if let Some(ref mut handler) = self.handler { + let device_seat = pointer_event.device().seat(); + let seat = &self.seats + .get(&device_seat) + .expect("Recieved key event of non existing 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) => { + let rc_axis_event = Rc::new(axis_event); + if rc_axis_event.has_axis(Axis::Vertical) { + trace!(self.logger, + "Calling on_pointer_axis for Axis::Vertical with {:?}", + *rc_axis_event); + handler.on_pointer_axis(seat, + self::PointerAxisEvent { + axis: Axis::Vertical, + event: rc_axis_event.clone(), + }); + } + if rc_axis_event.has_axis(Axis::Horizontal) { + trace!(self.logger, + "Calling on_pointer_axis for Axis::Horizontal with {:?}", + *rc_axis_event); + handler.on_pointer_axis(seat, + self::PointerAxisEvent { + axis: Axis::Horizontal, + event: rc_axis_event.clone(), + }); + } + } + PointerEvent::Button(button_event) => { + trace!(self.logger, + "Calling on_pointer_button with {:?}", + button_event); + handler.on_pointer_button(seat, button_event); + } + } + } + } + _ => {} //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 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, + } + } +} diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 6e01f97..c91fc91 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -17,6 +17,8 @@ pub mod graphics; #[cfg(feature = "backend_glutin")] pub mod glutin; +#[cfg(feature = "backend_libinput")] +pub mod libinput; #[cfg(feature = "renderer_glium")] mod glium; @@ -26,9 +28,10 @@ pub use glium::*; /// Internal functions that need to be accessible by the different backend implementations trait SeatInternal { - fn new(id: u32, capabilities: input::SeatCapabilities) -> Self; + fn new(id: u64, capabilities: input::SeatCapabilities) -> Self; + fn capabilities_mut(&mut self) -> &mut input::SeatCapabilities; } trait TouchSlotInternal { - fn new(id: u32) -> Self; + fn new(id: u64) -> Self; } diff --git a/src/lib.rs b/src/lib.rs index d266b43..7ac03cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,8 @@ extern crate tempfile; #[cfg(feature = "backend_glutin")] extern crate glutin; +#[cfg(feature = "backend_libinput")] +extern crate input; #[cfg(feature = "renderer_glium")] extern crate glium;