From 532cb6b78e325bcd3ee076d334ff9ab129dd3410 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 22 Sep 2017 18:42:52 +0200 Subject: [PATCH] seat: doc & warnings & fmt & cleanup methods --- src/seat/keyboard.rs | 17 ++--- src/seat/mod.rs | 175 ++++++++++++++++++++++++++++++++++++++++--- src/seat/pointer.rs | 25 ++++++- 3 files changed, 196 insertions(+), 21 deletions(-) diff --git a/src/seat/keyboard.rs b/src/seat/keyboard.rs index 4855e08..12cb235 100644 --- a/src/seat/keyboard.rs +++ b/src/seat/keyboard.rs @@ -174,7 +174,7 @@ pub enum Error { pub(crate) fn create_keyboard_handler(rules: &str, model: &str, layout: &str, variant: &str, options: Option, repeat_delay: i32, repeat_rate: i32, logger: ::slog::Logger) - -> Result { + -> Result { let log = logger.new(o!("smithay_module" => "xkbcommon_handler")); info!(log, "Initializing a xkbcommon handler with keymap query"; "rules" => rules, "model" => model, "layout" => layout, "variant" => variant, @@ -208,7 +208,7 @@ pub(crate) fn create_keyboard_handler(rules: &str, model: &str, layout: &str, va "fd" => keymap_file.as_raw_fd(), "len" => keymap_data.as_bytes().len() ); - Ok(KbdHandle { + Ok(KeyboardHandle { arc: Arc::new(KbdArc { internal: Mutex::new(internal), keymap_file: keymap_file, @@ -230,20 +230,19 @@ struct KbdArc { /// It can be cloned and all clones manipulate the same internal state. Clones /// can also be sent across threads. /// -/// This handle gives you 3 main ways to interact with the keymap handling: +/// This handle gives you 2 main ways to interact with the keyboard handling: /// -/// - register new `wl_keyboard` instances to it with the `new_kbd` method. /// - set the current focus for this keyboard: designing the surface that will receive the key inputs -/// using the `KbdHandle::set_focus` method. +/// using the `KeyboardHandle::set_focus` method. /// - process key inputs from the input backend, allowing them to be catched at the compositor-level -/// or forwarded to the client. See the documentation of the `KbdHandle::input` method for +/// or forwarded to the client. See the documentation of the `KeyboardHandle::input` method for /// details. #[derive(Clone)] -pub struct KbdHandle { +pub struct KeyboardHandle { arc: Arc, } -impl KbdHandle { +impl KeyboardHandle { /// Handle a keystroke /// /// All keystrokes from the input backend should be fed _in order_ to this method of the @@ -265,7 +264,7 @@ impl KbdHandle { // Offset the keycode by 8, as the evdev XKB rules reflect X's // broken keycode system, which starts at 8. - let sym = guard.state.key_get_one_sym(keycode+8); + let sym = guard.state.key_get_one_sym(keycode + 8); let mods_changed = guard.key_input(keycode, state); diff --git a/src/seat/mod.rs b/src/seat/mod.rs index c0057ad..ef04cc4 100644 --- a/src/seat/mod.rs +++ b/src/seat/mod.rs @@ -1,20 +1,92 @@ +//! Seat global utilities +//! +//! This module provides you with utilities for handling the seat globals +//! and the associated input wayland objects. +//! +//! ## How to use it +//! +//! ### Initialization +//! +//! ``` +//! # extern crate wayland_server; +//! # #[macro_use] extern crate smithay; +//! +//! use smithay::seat::Seat; +//! +//! # fn main(){ +//! # let (_display, mut event_loop) = wayland_server::create_display(); +//! // insert the seat: +//! let (seat_state_token, seat_global) = Seat::new( +//! &mut event_loop, +//! "seat-0".into(), // the name of the seat, will be advertize to clients +//! None /* insert a logger here*/ +//! ); +//! # } +//! ``` +//! +//! ### Run usage +//! +//! Once the seat is initialized, you can add capabilities to it. +//! +//! Currently, only pointer and keyboard capabilities are supported by +//! smithay. +//! +//! You can add these capabilities via methods of the `Seat` struct that was +//! inserted in the event loop, that you can retreive via its token: +//! +//! ``` +//! # extern crate wayland_server; +//! # #[macro_use] extern crate smithay; +//! # +//! # use smithay::seat::Seat; +//! # +//! # fn main(){ +//! # let (_display, mut event_loop) = wayland_server::create_display(); +//! # let (seat_state_token, seat_global) = Seat::new( +//! # &mut event_loop, +//! # "seat-0".into(), // the name of the seat, will be advertize to clients +//! # None /* insert a logger here*/ +//! # ); +//! let pointer_handle = event_loop.state().get_mut(&seat_state_token).add_pointer(); +//! # } +//! ``` +//! +//! These handles can be cloned and sent accross thread, so you can keep one around +//! in your event-handling code to forward inputs to your clients. + mod keyboard; mod pointer; -pub use self::keyboard::{Error as KbdError, KbdHandle}; +pub use self::keyboard::{Error as KeyboardError, KeyboardHandle}; pub use self::pointer::PointerHandle; -use wayland_server::{Client, EventLoop, EventLoopHandle, Global, StateToken}; +use wayland_server::{Client, EventLoop, EventLoopHandle, Global, Liveness, Resource, StateToken}; use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat}; +/// Internal data of a seat global +/// +/// This struct gives you access to the control of the +/// capabilities of the associated seat. +/// +/// It is directly inserted in the event loop by its `new` method. +/// +/// See module-level documentation for details of use. pub struct Seat { log: ::slog::Logger, name: String, pointer: Option, - keyboard: Option, + keyboard: Option, known_seats: Vec, } impl Seat { + /// Create a new seat global + /// + /// A new seat global is created with given name and inserted + /// into this event loop. + /// + /// You are provided with the state token to retrieve it (allowing + /// you to add or remove capabilities from it), and the global handle, + /// in case you want to remove it. pub fn new(evl: &mut EventLoop, name: String, logger: L) -> (StateToken, Global>) where @@ -34,8 +106,25 @@ impl Seat { (token, global) } + /// Adds the pointer capability to this seat + /// + /// You are provided a `PointerHandle`, which allows you to send input events + /// to this keyboard. This handle can be cloned and sent accross threads. + /// + /// Calling this method on a seat that already has a pointer capability + /// will overwrite it, and will be seen by the clients as if the + /// mouse was unplugged and a new one was plugged. pub fn add_pointer(&mut self) -> PointerHandle { let pointer = self::pointer::create_pointer_handler(); + if self.pointer.is_some() { + // there is already a pointer, remove it and notify the clients + // of the change + self.pointer = None; + let caps = self.compute_caps(); + for seat in &self.known_seats { + seat.capabilities(caps); + } + } self.pointer = Some(pointer.clone()); let caps = self.compute_caps(); for seat in &self.known_seats { @@ -44,9 +133,34 @@ impl Seat { pointer } + /// Remove the pointer capability from this seat + /// + /// Clients will be appropriately notified. + pub fn remove_pointer(&mut self) { + if self.pointer.is_some() { + self.pointer = None; + let caps = self.compute_caps(); + for seat in &self.known_seats { + seat.capabilities(caps); + } + } + } + + /// Adds the keyboard capability to this seat + /// + /// You are provided a `KbdHandle`, which allows you to send input events + /// to this keyboard. This handle can be cloned and sent accross threads. + /// + /// You also provide a Model/Layout/Variant/Options specification of the + /// keymap to be used for this keyboard, as well as any repeat-info that + /// will be forwarded to the clients. + /// + /// Calling this method on a seat that already has a keyboard capability + /// will overwrite it, and will be seen by the clients as if the + /// keyboard was unplugged and a new one was plugged. pub fn add_keyboard(&mut self, model: &str, layout: &str, variant: &str, options: Option, repeat_delay: i32, repeat_rate: i32) - -> Result { + -> Result { let keyboard = self::keyboard::create_keyboard_handler( "evdev", // we need this one model, @@ -57,6 +171,15 @@ impl Seat { repeat_rate, self.log.clone(), )?; + if self.keyboard.is_some() { + // there is already a keyboard, remove it and notify the clients + // of the change + self.keyboard = None; + let caps = self.compute_caps(); + for seat in &self.known_seats { + seat.capabilities(caps); + } + } self.keyboard = Some(keyboard.clone()); let caps = self.compute_caps(); for seat in &self.known_seats { @@ -65,6 +188,35 @@ impl Seat { Ok(keyboard) } + /// Remove the keyboard capability from this seat + /// + /// Clients will be appropriately notified. + pub fn remove_keyboard(&mut self) { + if self.keyboard.is_some() { + self.keyboard = None; + let caps = self.compute_caps(); + for seat in &self.known_seats { + seat.capabilities(caps); + } + } + } + + /// Cleanup internal states from old resources + /// + /// Deletes all remnnant of ressources from clients that + /// are now disconnected. + /// + /// It can be wise to run this from time to time. + pub fn cleanup(&mut self) { + if let Some(ref pointer) = self.pointer { + pointer.cleanup_old_pointers(); + } + if let Some(ref kbd) = self.keyboard { + kbd.cleanup_old_kbds(); + } + self.known_seats.retain(|s| s.status() == Liveness::Alive); + } + fn compute_caps(&self) -> wl_seat::Capability { let mut caps = wl_seat::Capability::empty(); if self.pointer.is_some() { @@ -80,7 +232,7 @@ impl Seat { fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken, _: &Client, seat: wl_seat::WlSeat) { evlh.register(&seat, seat_implementation(), token.clone(), None); - let mut seat_mgr = evlh.state().get_mut(token); + let seat_mgr = evlh.state().get_mut(token); seat.name(seat_mgr.name.clone()); seat.capabilities(seat_mgr.compute_caps()); seat_mgr.known_seats.push(seat); @@ -88,21 +240,24 @@ fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken, _: fn seat_implementation() -> wl_seat::Implementation> { wl_seat::Implementation { - get_pointer: |evlh, token, _, seat, pointer| { + get_pointer: |evlh, token, _, _, pointer| { evlh.register(&pointer, pointer_implementation(), (), None); if let Some(ref ptr_handle) = evlh.state().get(token).pointer { ptr_handle.new_pointer(pointer); + } else { + // we should send a protocol error... but the protocol does not allow + // us, so this pointer will just remain inactive ¯\_(ツ)_/¯ } - // TODO: protocol error ? }, - get_keyboard: |evlh, token, _, seat, keyboard| { + get_keyboard: |evlh, token, _, _, keyboard| { evlh.register(&keyboard, keyboard_implementation(), (), None); if let Some(ref kbd_handle) = evlh.state().get(token).keyboard { kbd_handle.new_kbd(keyboard); + } else { + // same, should error but cant } - // TODO: protocol error ? }, - get_touch: |evlh, token, _, seat, touch| { + get_touch: |_evlh, _token, _, _, _touch| { // TODO }, release: |_, _, _, _| {}, diff --git a/src/seat/pointer.rs b/src/seat/pointer.rs index 7c3c5be..43b2599 100644 --- a/src/seat/pointer.rs +++ b/src/seat/pointer.rs @@ -31,6 +31,13 @@ impl PointerInternal { } } +/// An handle to a keyboard handler +/// +/// It can be cloned and all clones manipulate the same internal state. Clones +/// can also be sent across threads. +/// +/// This handle gives you access to an interface to send pointer events to your +/// clients. #[derive(Clone)] pub struct PointerHandle { inner: Arc>, @@ -42,6 +49,16 @@ impl PointerHandle { guard.known_pointers.push(pointer); } + /// Notify that the pointer moved + /// + /// You provide the new location of the pointer, in the form of: + /// + /// - `None` if the pointer is not on top of a client surface + /// - `Some(surface, x, y)` if the pointer is focusing surface `surface`, + /// at location `(x, y)` relative to this surface + /// + /// This will internally take care of notifying the appropriate client objects + /// of enter/motion/leave events. pub fn motion(&self, location: Option<(&wl_surface::WlSurface, f64, f64)>, serial: u32, time: u32) { let mut guard = self.inner.lock().unwrap(); // do we leave a surface ? @@ -69,15 +86,19 @@ impl PointerHandle { }) } else { // we were on top of a surface and remained on it - guard.with_focused_pointers(|pointer, surface| { + guard.with_focused_pointers(|pointer, _| { pointer.motion(time, x, y); }) } } } + /// Notify that a button was pressed + /// + /// This will internally send the appropriate button event to the client + /// objects matching with the currently focused surface. pub fn button(&self, button: u32, state: wl_pointer::ButtonState, serial: u32, time: u32) { - let mut guard = self.inner.lock().unwrap(); + let guard = self.inner.lock().unwrap(); guard.with_focused_pointers(|pointer, _| { pointer.button(serial, time, button, state); })