seat: doc & warnings & fmt & cleanup methods

This commit is contained in:
Victor Berger 2017-09-22 18:42:52 +02:00
parent 4b01b55f75
commit 532cb6b78e
3 changed files with 196 additions and 21 deletions

View File

@ -174,7 +174,7 @@ pub enum Error {
pub(crate) fn create_keyboard_handler(rules: &str, model: &str, layout: &str, variant: &str, pub(crate) fn create_keyboard_handler(rules: &str, model: &str, layout: &str, variant: &str,
options: Option<String>, repeat_delay: i32, repeat_rate: i32, options: Option<String>, repeat_delay: i32, repeat_rate: i32,
logger: ::slog::Logger) logger: ::slog::Logger)
-> Result<KbdHandle, Error> { -> Result<KeyboardHandle, Error> {
let log = logger.new(o!("smithay_module" => "xkbcommon_handler")); let log = logger.new(o!("smithay_module" => "xkbcommon_handler"));
info!(log, "Initializing a xkbcommon handler with keymap query"; info!(log, "Initializing a xkbcommon handler with keymap query";
"rules" => rules, "model" => model, "layout" => layout, "variant" => variant, "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() "fd" => keymap_file.as_raw_fd(), "len" => keymap_data.as_bytes().len()
); );
Ok(KbdHandle { Ok(KeyboardHandle {
arc: Arc::new(KbdArc { arc: Arc::new(KbdArc {
internal: Mutex::new(internal), internal: Mutex::new(internal),
keymap_file: keymap_file, keymap_file: keymap_file,
@ -230,20 +230,19 @@ struct KbdArc {
/// It can be cloned and all clones manipulate the same internal state. Clones /// It can be cloned and all clones manipulate the same internal state. Clones
/// can also be sent across threads. /// 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 /// - 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 /// - 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. /// details.
#[derive(Clone)] #[derive(Clone)]
pub struct KbdHandle { pub struct KeyboardHandle {
arc: Arc<KbdArc>, arc: Arc<KbdArc>,
} }
impl KbdHandle { impl KeyboardHandle {
/// Handle a keystroke /// Handle a keystroke
/// ///
/// All keystrokes from the input backend should be fed _in order_ to this method of the /// All keystrokes from the input backend should be fed _in order_ to this method of the

View File

@ -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 keyboard;
mod pointer; mod pointer;
pub use self::keyboard::{Error as KbdError, KbdHandle}; pub use self::keyboard::{Error as KeyboardError, KeyboardHandle};
pub use self::pointer::PointerHandle; 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}; 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 { pub struct Seat {
log: ::slog::Logger, log: ::slog::Logger,
name: String, name: String,
pointer: Option<PointerHandle>, pointer: Option<PointerHandle>,
keyboard: Option<KbdHandle>, keyboard: Option<KeyboardHandle>,
known_seats: Vec<wl_seat::WlSeat>, known_seats: Vec<wl_seat::WlSeat>,
} }
impl Seat { 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<L>(evl: &mut EventLoop, name: String, logger: L) pub fn new<L>(evl: &mut EventLoop, name: String, logger: L)
-> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>) -> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>)
where where
@ -34,8 +106,25 @@ impl Seat {
(token, global) (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 { pub fn add_pointer(&mut self) -> PointerHandle {
let pointer = self::pointer::create_pointer_handler(); 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()); self.pointer = Some(pointer.clone());
let caps = self.compute_caps(); let caps = self.compute_caps();
for seat in &self.known_seats { for seat in &self.known_seats {
@ -44,9 +133,34 @@ impl Seat {
pointer 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<String>, pub fn add_keyboard(&mut self, model: &str, layout: &str, variant: &str, options: Option<String>,
repeat_delay: i32, repeat_rate: i32) repeat_delay: i32, repeat_rate: i32)
-> Result<KbdHandle, KbdError> { -> Result<KeyboardHandle, KeyboardError> {
let keyboard = self::keyboard::create_keyboard_handler( let keyboard = self::keyboard::create_keyboard_handler(
"evdev", // we need this one "evdev", // we need this one
model, model,
@ -57,6 +171,15 @@ impl Seat {
repeat_rate, repeat_rate,
self.log.clone(), 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()); self.keyboard = Some(keyboard.clone());
let caps = self.compute_caps(); let caps = self.compute_caps();
for seat in &self.known_seats { for seat in &self.known_seats {
@ -65,6 +188,35 @@ impl Seat {
Ok(keyboard) 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 { fn compute_caps(&self) -> wl_seat::Capability {
let mut caps = wl_seat::Capability::empty(); let mut caps = wl_seat::Capability::empty();
if self.pointer.is_some() { if self.pointer.is_some() {
@ -80,7 +232,7 @@ impl Seat {
fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _: &Client, fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _: &Client,
seat: wl_seat::WlSeat) { seat: wl_seat::WlSeat) {
evlh.register(&seat, seat_implementation(), token.clone(), None); 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.name(seat_mgr.name.clone());
seat.capabilities(seat_mgr.compute_caps()); seat.capabilities(seat_mgr.compute_caps());
seat_mgr.known_seats.push(seat); seat_mgr.known_seats.push(seat);
@ -88,21 +240,24 @@ fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _:
fn seat_implementation() -> wl_seat::Implementation<StateToken<Seat>> { fn seat_implementation() -> wl_seat::Implementation<StateToken<Seat>> {
wl_seat::Implementation { wl_seat::Implementation {
get_pointer: |evlh, token, _, seat, pointer| { get_pointer: |evlh, token, _, _, pointer| {
evlh.register(&pointer, pointer_implementation(), (), None); evlh.register(&pointer, pointer_implementation(), (), None);
if let Some(ref ptr_handle) = evlh.state().get(token).pointer { if let Some(ref ptr_handle) = evlh.state().get(token).pointer {
ptr_handle.new_pointer(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); evlh.register(&keyboard, keyboard_implementation(), (), None);
if let Some(ref kbd_handle) = evlh.state().get(token).keyboard { if let Some(ref kbd_handle) = evlh.state().get(token).keyboard {
kbd_handle.new_kbd(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 // TODO
}, },
release: |_, _, _, _| {}, release: |_, _, _, _| {},

View File

@ -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)] #[derive(Clone)]
pub struct PointerHandle { pub struct PointerHandle {
inner: Arc<Mutex<PointerInternal>>, inner: Arc<Mutex<PointerInternal>>,
@ -42,6 +49,16 @@ impl PointerHandle {
guard.known_pointers.push(pointer); 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) { pub fn motion(&self, location: Option<(&wl_surface::WlSurface, f64, f64)>, serial: u32, time: u32) {
let mut guard = self.inner.lock().unwrap(); let mut guard = self.inner.lock().unwrap();
// do we leave a surface ? // do we leave a surface ?
@ -69,15 +86,19 @@ impl PointerHandle {
}) })
} else { } else {
// we were on top of a surface and remained on it // 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); 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) { 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, _| { guard.with_focused_pointers(|pointer, _| {
pointer.button(serial, time, button, state); pointer.button(serial, time, button, state);
}) })