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,
options: Option<String>, repeat_delay: i32, repeat_rate: i32,
logger: ::slog::Logger)
-> Result<KbdHandle, Error> {
-> Result<KeyboardHandle, Error> {
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<KbdArc>,
}
impl KbdHandle {
impl KeyboardHandle {
/// Handle a keystroke
///
/// 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 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<PointerHandle>,
keyboard: Option<KbdHandle>,
keyboard: Option<KeyboardHandle>,
known_seats: Vec<wl_seat::WlSeat>,
}
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)
-> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>)
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<String>,
repeat_delay: i32, repeat_rate: i32)
-> Result<KbdHandle, KbdError> {
-> Result<KeyboardHandle, KeyboardError> {
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<Seat>, _: &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<Seat>, _:
fn seat_implementation() -> wl_seat::Implementation<StateToken<Seat>> {
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: |_, _, _, _| {},

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)]
pub struct PointerHandle {
inner: Arc<Mutex<PointerInternal>>,
@ -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);
})