Merge pull request #39 from vberger/seat_global

Global handler for seat
This commit is contained in:
Victor Berger 2017-09-23 19:15:58 +02:00 committed by GitHub
commit ec4a9a0578
18 changed files with 1085 additions and 207 deletions

View File

@ -39,7 +39,7 @@ branches:
before_script:
- export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH
- pip install 'travis-cargo<0.2' --user
- which rustfmt || travis-cargo --only nightly install rustfmt-nightly
- which rustfmt || travis-cargo --only nightly install rustfmt-nightly -- --force
- which cargo-install-update || cargo install cargo-update
- cargo install-update -a
- mkdir $(pwd)/socket

View File

@ -5,7 +5,7 @@ authors = ["Victor Berger <victor.berger@thalesgroup.com>"]
license = "MIT"
[dependencies]
wayland-server = "0.10.1"
wayland-server = "0.10.2"
nix = "0.7.0"
xkbcommon = "0.2.1"
tempfile = "2.1.5"
@ -13,14 +13,14 @@ slog = { version = "2.0.0" }
slog-stdlog = "2.0.0-0.2"
libloading = "0.4.0"
wayland-client = { version = "0.9.9", optional = true }
winit = { version = "0.7.5", optional = true }
winit = { version = "0.8.0", optional = true }
drm = { version = "0.2.1", optional = true }
gbm = { version = "0.2.1", optional = true }
glium = { version = "0.17.1", optional = true, default-features = false }
input = { version = "0.2.0", optional = true }
clippy = { version = "*", optional = true }
rental = "0.4.11"
wayland-protocols = { version = "0.10.1", features = ["unstable_protocols", "server"] }
wayland-protocols = { version = "0.10.2", features = ["unstable_protocols", "server"] }
image = "0.15.0"
error-chain = "0.11.0"

View File

@ -17,16 +17,18 @@ use drm::control::{Device as ControlDevice, ResourceInfo};
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
use drm::control::encoder::Info as EncoderInfo;
use glium::Surface;
use helpers::{shell_implementation, surface_implementation, GliumDrawer, Roles, SurfaceData};
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData};
use slog::{Drain, Logger};
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::compositor::{compositor_init, CompositorToken, SubsurfaceRole, TraversalAction};
use smithay::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
use smithay::compositor::roles::Role;
use smithay::shell::{shell_init, ShellState};
use smithay::shell::ShellState;
use smithay::shm::init_shm_global;
use std::cell::RefCell;
use std::fs::OpenOptions;
use std::io::Error as IoError;
use std::rc::Rc;
use std::time::Duration;
use wayland_server::{EventLoopHandle, StateToken};
@ -89,16 +91,7 @@ fn main() {
init_shm_global(&mut event_loop, vec![], log.clone());
let (compositor_token, _, _) =
compositor_init(&mut event_loop, surface_implementation(), (), log.clone());
let (shell_state_token, _, _) = shell_init(
&mut event_loop,
compositor_token,
shell_implementation(),
compositor_token,
log.clone(),
);
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
/*
* Initialize glium
@ -125,16 +118,23 @@ fn main() {
DrmHandlerImpl {
shell_state_token,
compositor_token,
window_map: window_map.clone(),
logger: log,
},
).unwrap();
event_loop.run().unwrap();
loop {
event_loop.dispatch(Some(16)).unwrap();
display.flush_clients();
window_map.borrow_mut().refresh();
}
}
pub struct DrmHandlerImpl {
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
window_map: Rc<RefCell<MyWindowMap>>,
logger: ::slog::Logger,
}
@ -148,33 +148,39 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
// redraw the frame, in a simple but inneficient way
{
let screen_dimensions = drawer.get_framebuffer_dimensions();
for toplevel_surface in state.get(&self.shell_state_token).toplevel_surfaces() {
if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn
let initial_place = self.compositor_token
.with_surface_data(wl_surface, |data| data.user_data.location.unwrap_or((0, 0)));
self.compositor_token
.with_surface_tree(
wl_surface,
initial_place,
|_surface, attributes, role, &(mut x, mut y)| {
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
// there is actually something to draw !
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
self.window_map
.borrow()
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn
self.compositor_token
.with_surface_tree_upward(
wl_surface,
initial_place,
|_surface, attributes, role, &(mut x, mut y)| {
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
// there is actually something to draw !
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
}
drawer.render(
&mut frame,
contents,
(w, h),
(x, y),
screen_dimensions,
);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
drawer.render(&mut frame, contents, (w, h), (x, y), screen_dimensions);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
},
)
.unwrap();
}
}
},
)
.unwrap();
}
});
}
frame.finish().unwrap();
}

View File

@ -1,14 +1,18 @@
use super::WindowMap;
use rand;
use smithay::compositor::{CompositorToken, SurfaceUserImplementation};
use smithay::shell::{PopupConfigure, ShellSurfaceRole, ShellSurfaceUserImplementation, ToplevelConfigure};
use smithay::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceUserImplementation};
use smithay::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole,
ShellSurfaceUserImplementation, ToplevelConfigure};
use smithay::shm::with_buffer_contents;
use std::cell::RefCell;
use std::rc::Rc;
use wayland_server::{EventLoop, StateToken};
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
#[derive(Default)]
pub struct SurfaceData {
pub buffer: Option<(Vec<u8>, (u32, u32))>,
pub location: Option<(i32, i32)>,
}
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, ()> {
@ -48,24 +52,26 @@ pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles,
}
}
pub fn shell_implementation(
)
-> ShellSurfaceUserImplementation<SurfaceData, Roles, (), CompositorToken<SurfaceData, Roles, ()>, ()>
pub struct ShellIData<F> {
pub token: CompositorToken<SurfaceData, Roles, ()>,
pub window_map: Rc<RefCell<super::WindowMap<SurfaceData, Roles, (), (), F>>>,
}
pub fn shell_implementation<F>() -> ShellSurfaceUserImplementation<SurfaceData, Roles, (), ShellIData<F>, ()>
where
F: Fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
{
ShellSurfaceUserImplementation {
new_client: |_, _, _| {},
client_pong: |_, _, _| {},
new_toplevel: |_, token, toplevel| {
let wl_surface = toplevel.get_surface().unwrap();
token.with_surface_data(wl_surface, |data| {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
data.user_data.location = Some((x, y))
});
new_toplevel: |_, idata, toplevel| {
// place the window at a random location in the [0;300]x[0;300] square
use rand::distributions::{IndependentSample, Range};
let range = Range::new(0, 300);
let mut rng = rand::thread_rng();
let x = range.ind_sample(&mut rng);
let y = range.ind_sample(&mut rng);
idata.window_map.borrow_mut().insert(toplevel, (x, y));
ToplevelConfigure {
size: None,
states: vec![],
@ -92,3 +98,47 @@ pub fn shell_implementation(
show_window_menu: |_, _, _, _, _, _, _| {},
}
}
fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
attrs
.user_data
.buffer
.as_ref()
.map(|&(_, (w, h))| (w as i32, h as i32))
}
pub type MyWindowMap = WindowMap<
SurfaceData,
Roles,
(),
(),
fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
>;
pub fn init_shell(
evl: &mut EventLoop, log: ::slog::Logger)
-> (
CompositorToken<SurfaceData, Roles, ()>,
StateToken<ShellState<SurfaceData, Roles, (), ()>>,
Rc<RefCell<MyWindowMap>>,
) {
let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), (), log.clone());
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, _, (), _>::new(
compositor_token,
get_size as _,
)));
let (shell_state_token, _, _) = shell_init(
evl,
compositor_token,
shell_implementation(),
ShellIData {
token: compositor_token,
window_map: window_map.clone(),
},
log.clone(),
);
(compositor_token, shell_state_token, window_map)
}

View File

@ -1,5 +1,7 @@
mod glium;
mod implementations;
mod window_map;
pub use self::glium::GliumDrawer;
pub use self::implementations::*;
pub use self::window_map::WindowMap;

View File

@ -0,0 +1,191 @@
use smithay::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttributes, TraversalAction};
use smithay::compositor::roles::Role;
use smithay::shell::{ShellSurfaceRole, ToplevelSurface};
use smithay::utils::Rectangle;
use wayland_server::Resource;
use wayland_server::protocol::wl_surface;
struct Window<U, R, CID, SD> {
location: (i32, i32),
surface: Rectangle,
toplevel: ToplevelSurface<U, R, CID, SD>,
}
impl<U, R, CID, SD> Window<U, R, CID, SD>
where
U: 'static,
R: Role<SubsurfaceRole> + Role<ShellSurfaceRole> + 'static,
CID: 'static,
SD: 'static,
{
// Find the topmost surface under this point if any and the location of this point in the surface
fn matching<F>(&self, point: (f64, f64), ctoken: CompositorToken<U, R, CID>, get_size: F)
-> Option<(wl_surface::WlSurface, (f64, f64))>
where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
{
if !self.surface.contains((point.0 as i32, point.1 as i32)) {
return None;
}
// need to check more carefully
let mut found = None;
if let Some(wl_surface) = self.toplevel.get_surface() {
let _ = ctoken.with_surface_tree_downward(
wl_surface,
self.location,
|wl_surface, attributes, role, &(mut x, mut y)| if let Some((w, h)) = get_size(attributes) {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
}
let my_rect = Rectangle {
x,
y,
width: w,
height: h,
};
if my_rect.contains((point.0 as i32, point.1 as i32)) {
found = wl_surface.clone().map(|s| {
(s, (point.0 - my_rect.x as f64, point.1 - my_rect.y as f64))
});
TraversalAction::Break
} else {
TraversalAction::DoChildren((x, y))
}
} else {
TraversalAction::SkipChildren
},
);
}
found
}
fn self_update<F>(&mut self, ctoken: CompositorToken<U, R, CID>, get_size: F)
where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
{
let (base_x, base_y) = self.location;
let (mut min_x, mut min_y, mut max_x, mut max_y) = (base_x, base_y, base_x, base_y);
if let Some(wl_surface) = self.toplevel.get_surface() {
let _ = ctoken.with_surface_tree_downward(
wl_surface,
(base_x, base_y),
|_, attributes, role, &(mut x, mut y)| {
if let Some((w, h)) = get_size(attributes) {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
}
// update the bounding box
if x < min_x {
min_x = x;
}
if y < min_y {
min_y = y;
}
if x + w > max_x {
max_x = x + w;
}
if y + h > max_y {
max_y = y + w;
}
TraversalAction::DoChildren((x, y))
} else {
TraversalAction::SkipChildren
}
},
);
}
self.surface = Rectangle {
x: min_x,
y: min_y,
width: max_x - min_x,
height: max_y - min_y,
};
}
}
pub struct WindowMap<U, R, CID, SD, F> {
ctoken: CompositorToken<U, R, CID>,
windows: Vec<Window<U, R, CID, SD>>,
get_size: F,
}
impl<U, R, CID, SD, F> WindowMap<U, R, CID, SD, F>
where
F: Fn(&SurfaceAttributes<U>) -> Option<(i32, i32)>,
U: 'static,
R: Role<SubsurfaceRole> + Role<ShellSurfaceRole> + 'static,
CID: 'static,
SD: 'static,
{
pub fn new(ctoken: CompositorToken<U, R, CID>, get_size: F) -> WindowMap<U, R, CID, SD, F> {
WindowMap {
ctoken: ctoken,
windows: Vec::new(),
get_size: get_size,
}
}
pub fn insert(&mut self, toplevel: ToplevelSurface<U, R, CID, SD>, location: (i32, i32)) {
let mut window = Window {
location: location,
surface: Rectangle {
x: 0,
y: 0,
width: 0,
height: 0,
},
toplevel: toplevel,
};
window.self_update(self.ctoken, &self.get_size);
self.windows.insert(0, window);
}
pub fn get_surface_under(&self, point: (f64, f64)) -> Option<(wl_surface::WlSurface, (f64, f64))> {
for w in &self.windows {
if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) {
return Some(surface);
}
}
None
}
pub fn get_surface_and_bring_to_top(&mut self, point: (f64, f64))
-> Option<(wl_surface::WlSurface, (f64, f64))> {
let mut found = None;
for (i, w) in self.windows.iter().enumerate() {
if let Some(surface) = w.matching(point, self.ctoken, &self.get_size) {
found = Some((i, surface));
break;
}
}
if let Some((i, surface)) = found {
let winner = self.windows.remove(i);
self.windows.insert(0, winner);
Some(surface)
} else {
None
}
}
pub fn with_windows_from_bottom_to_top<Func>(&self, mut f: Func)
where
Func: FnMut(&ToplevelSurface<U, R, CID, SD>, (i32, i32)),
{
for w in self.windows.iter().rev() {
f(&w.toplevel, w.location)
}
}
pub fn refresh(&mut self) {
self.windows.retain(|w| w.toplevel.alive());
for w in self.windows.iter_mut() {
w.self_update(self.ctoken, &self.get_size);
}
}
pub fn clear(&mut self) {
self.windows.clear();
}
}

View File

@ -12,15 +12,112 @@ extern crate wayland_server;
mod helpers;
use glium::Surface;
use helpers::{shell_implementation, surface_implementation, GliumDrawer};
use helpers::{init_shell, GliumDrawer, MyWindowMap};
use slog::{Drain, Logger};
use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::backend::input::InputBackend;
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerButtonEvent,
PointerMotionAbsoluteEvent};
use smithay::backend::winit;
use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction};
use smithay::compositor::{SubsurfaceRole, TraversalAction};
use smithay::compositor::roles::Role;
use smithay::shell::shell_init;
use smithay::seat::{KeyboardHandle, PointerHandle, Seat};
use smithay::shm::init_shm_global;
use std::cell::RefCell;
use std::rc::Rc;
use wayland_server::protocol::wl_pointer;
struct WinitInputHandler {
log: Logger,
pointer: PointerHandle,
keyboard: KeyboardHandle,
window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: (f64, f64),
serial: u32,
}
impl WinitInputHandler {
fn next_serial(&mut self) -> u32 {
self.serial += 1;
self.serial
}
}
impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
fn on_seat_created(&mut self, _: &input::Seat) {
/* never happens with winit */
}
fn on_seat_destroyed(&mut self, _: &input::Seat) {
/* never happens with winit */
}
fn on_seat_changed(&mut self, _: &input::Seat) {
/* never happens with winit */
}
fn on_keyboard_key(&mut self, _: &input::Seat, evt: winit::WinitKeyboardInputEvent) {
let keycode = evt.key_code() - 8;
let state = evt.state();
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
let serial = self.next_serial();
self.keyboard.input(keycode, state, serial, |_, _| true);
}
fn on_pointer_move(&mut self, _: &input::Seat, _: input::UnusedEvent) {
/* never happens with winit */
}
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: winit::WinitMouseMovedEvent) {
// on winit, mouse events are already in pixel coordinates
let (x, y) = evt.position();
self.pointer_location = (x, y);
let serial = self.next_serial();
let under = self.window_map.borrow().get_surface_under((x, y));
self.pointer.motion(
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
serial,
evt.time(),
);
}
fn on_pointer_button(&mut self, _: &input::Seat, evt: winit::WinitMouseInputEvent) {
let serial = self.next_serial();
let button = match evt.button() {
input::MouseButton::Left => 0x110,
input::MouseButton::Right => 0x111,
input::MouseButton::Middle => 0x112,
input::MouseButton::Other(b) => b as u32,
};
let state = match evt.state() {
input::MouseButtonState::Pressed => {
// change the keyboard focus
let under = self.window_map
.borrow_mut()
.get_surface_and_bring_to_top(self.pointer_location);
self.keyboard
.set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
wl_pointer::ButtonState::Pressed
}
input::MouseButtonState::Released => wl_pointer::ButtonState::Released,
};
self.pointer.button(button, state, serial, evt.time());
}
fn on_pointer_axis(&mut self, _: &input::Seat, _: winit::WinitMouseWheelEvent) {
/* not done in this example */
}
fn on_touch_down(&mut self, _: &input::Seat, _: winit::WinitTouchStartedEvent) {
/* not done in this example */
}
fn on_touch_motion(&mut self, _: &input::Seat, _: winit::WinitTouchMovedEvent) {
/* not done in this example */
}
fn on_touch_up(&mut self, _: &input::Seat, _: winit::WinitTouchEndedEvent) {
/* not done in this example */
}
fn on_touch_cancel(&mut self, _: &input::Seat, _: winit::WinitTouchCancelledEvent) {
/* not done in this example */
}
fn on_touch_frame(&mut self, _: &input::Seat, _: input::UnusedEvent) {
/* never happens with winit */
}
fn on_input_config_changed(&mut self, _: &mut ()) {
/* never happens with winit */
}
}
fn main() {
// A logger facility, here we use the terminal for this example
@ -40,22 +137,31 @@ fn main() {
init_shm_global(&mut event_loop, vec![], log.clone());
let (compositor_token, _, _) =
compositor_init(&mut event_loop, surface_implementation(), (), log.clone());
let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
let (shell_state_token, _, _) = shell_init(
&mut event_loop,
compositor_token,
shell_implementation(),
compositor_token,
log.clone(),
);
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone());
let pointer = event_loop.state().get_mut(&seat_token).add_pointer();
let keyboard = event_loop
.state()
.get_mut(&seat_token)
.add_keyboard("", "fr", "oss", None, 1000, 500)
.expect("Failed to initialize the keyboard");
/*
* Initialize glium
*/
let drawer = GliumDrawer::from(renderer);
input.set_handler(WinitInputHandler {
log: log.clone(),
pointer,
keyboard,
window_map: window_map.clone(),
pointer_location: (0.0, 0.0),
serial: 0,
});
/*
* Add a listening socket:
*/
@ -70,38 +176,45 @@ fn main() {
// redraw the frame, in a simple but inneficient way
{
let screen_dimensions = drawer.get_framebuffer_dimensions();
let state = event_loop.state();
for toplevel_surface in state.get(&shell_state_token).toplevel_surfaces() {
if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn
let initial_place = compositor_token
.with_surface_data(wl_surface, |data| data.user_data.location.unwrap_or((0, 0)));
compositor_token
.with_surface_tree(
wl_surface,
initial_place,
|_surface, attributes, role, &(mut x, mut y)| {
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
// there is actually something to draw !
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
window_map
.borrow()
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn
compositor_token
.with_surface_tree_upward(
wl_surface,
initial_place,
|_surface, attributes, role, &(mut x, mut y)| {
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
// there is actually something to draw !
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
}
drawer.render(
&mut frame,
contents,
(w, h),
(x, y),
screen_dimensions,
);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
drawer.render(&mut frame, contents, (w, h), (x, y), screen_dimensions);
TraversalAction::DoChildren((x, y))
} else {
// we are not display, so our children are neither
TraversalAction::SkipChildren
}
},
)
.unwrap();
}
}
},
)
.unwrap();
}
});
}
frame.finish().unwrap();
event_loop.dispatch(Some(16)).unwrap();
display.flush_clients();
window_map.borrow_mut().refresh();
}
}

View File

@ -94,6 +94,7 @@ use self::region::RegionData;
use self::roles::{Role, RoleType, WrongRole};
use self::tree::SurfaceData;
pub use self::tree::TraversalAction;
use utils::Rectangle;
use wayland_server::{resource_is_registered, EventLoop, EventLoopHandle, Global};
use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region,
wl_subcompositor, wl_surface};
@ -217,19 +218,6 @@ pub enum RectangleKind {
Subtract,
}
/// A rectangle defined by its top-left corner and dimensions
#[derive(Copy, Clone, Debug)]
pub struct Rectangle {
/// horizontal position of the top-leftcorner of the rectangle, in surface coordinates
pub x: i32,
/// vertical position of the top-leftcorner of the rectangle, in surface coordinates
pub y: i32,
/// width of the rectangle
pub width: i32,
/// height of the rectangle
pub height: i32,
}
/// Description of the contents of a region
///
/// A region is defined as an union and difference of rectangle.
@ -310,7 +298,7 @@ where
R: RoleType + Role<SubsurfaceRole> + 'static,
ID: 'static,
{
/// Access the data of a surface tree
/// Access the data of a surface tree from bottom to top
///
/// The provided closure is called successively on the surface and all its child subsurfaces,
/// in a depth-first order. This matches the order in which the surfaces are supposed to be
@ -326,7 +314,8 @@ where
///
/// If the surface not managed by the CompositorGlobal that provided this token, this
/// will panic (having more than one compositor is not supported).
pub fn with_surface_tree<F, T>(&self, surface: &wl_surface::WlSurface, initial: T, f: F) -> Result<(), ()>
pub fn with_surface_tree_upward<F, T>(&self, surface: &wl_surface::WlSurface, initial: T, f: F)
-> Result<(), ()>
where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
-> TraversalAction<T>,
@ -339,7 +328,33 @@ where
"Accessing the data of foreign surfaces is not supported."
);
unsafe {
SurfaceData::<U, R>::map_tree(surface, initial, f);
SurfaceData::<U, R>::map_tree(surface, initial, f, false);
}
Ok(())
}
/// Access the data of a surface tree from top to bottom
///
/// The provided closure is called successively on the surface and all its child subsurfaces,
/// in a depth-first order. This matches the reverse of the order in which the surfaces are
/// supposed to be drawn: top-most first.
///
/// Behavior is the same as `with_surface_tree_upward`.
pub fn with_surface_tree_downward<F, T>(&self, surface: &wl_surface::WlSurface, initial: T, f: F)
-> Result<(), ()>
where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
-> TraversalAction<T>,
{
assert!(
resource_is_registered(
surface,
&self::handlers::surface_implementation::<U, R, ID>()
),
"Accessing the data of foreign surfaces is not supported."
);
unsafe {
SurfaceData::<U, R>::map_tree(surface, initial, f, true);
}
Ok(())
}

View File

@ -307,21 +307,22 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
}
/// Access sequentially the attributes associated with a surface tree,
/// in a depth-first order
/// in a depth-first order.
///
/// Note that an internal lock is taken during access of this data,
/// so the tree cannot be manipulated at the same time.
///
/// The callback returns wether the traversal should continue or not. Returning
/// false will cause an early-stopping.
pub unsafe fn map_tree<F, T>(root: &wl_surface::WlSurface, initial: T, mut f: F)
pub unsafe fn map_tree<F, T>(root: &wl_surface::WlSurface, initial: T, mut f: F, reverse: bool)
where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
-> TraversalAction<T>,
{
// helper function for recursion
unsafe fn map<U: 'static, R: 'static, F, T>(surface: &wl_surface::WlSurface,
root: &wl_surface::WlSurface, initial: &T, f: &mut F)
root: &wl_surface::WlSurface, initial: &T, f: &mut F,
reverse: bool)
-> bool
where
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
@ -344,9 +345,17 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
) {
TraversalAction::DoChildren(t) => {
// loop over children
for c in &data_guard.children {
if !map::<U, R, _, _>(c, root, &t, f) {
return false;
if reverse {
for c in data_guard.children.iter().rev() {
if !map::<U, R, _, _>(c, root, &t, f, true) {
return false;
}
}
} else {
for c in &data_guard.children {
if !map::<U, R, _, _>(c, root, &t, f, false) {
return false;
}
}
}
true
@ -368,9 +377,17 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
) {
TraversalAction::DoChildren(t) => {
// loop over children
for c in &data_guard.children {
if !map::<U, R, _, _>(c, root, &t, &mut f) {
break;
if reverse {
for c in data_guard.children.iter().rev() {
if !map::<U, R, _, _>(c, root, &t, &mut f, true) {
break;
}
}
} else {
for c in &data_guard.children {
if !map::<U, R, _, _>(c, root, &t, &mut f, false) {
break;
}
}
}
}

View File

@ -44,8 +44,9 @@ extern crate error_chain;
pub mod backend;
pub mod compositor;
pub mod shm;
pub mod keyboard;
pub mod seat;
pub mod shell;
pub mod utils;
fn slog_or_stdlog<L>(logger: L) -> ::slog::Logger
where

View File

@ -1,22 +1,3 @@
//! Utilities for keyboard handling
//!
//! This module provides utilities for keyboardand keymap handling: keymap interpretation
//! and forwarding keystrokes to clients using xkbcommon.
//!
//! You can first create a `KbdHandle` using the `create_keyboard_handler` function in this module.
//! The handle you obtained can be cloned to access this keyboard state from different places. It is
//! expected that such a context is created for each keyboard the compositor has access to.
//!
//! This handle gives you 3 main ways to interact with the keymap handling:
//!
//! - send the keymap information to a client using the `KbdHandle::send_keymap` method.
//! - set the current focus for this keyboard: designing the client that will receive the key inputs
//! using the `KbdHandle::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
//! details.
use backend::input::KeyState;
use std::io::{Error as IoError, Write};
use std::os::unix::io::AsRawFd;
@ -74,15 +55,19 @@ impl ModifiersState {
}
struct KbdInternal {
focus: Option<(wl_surface::WlSurface, wl_keyboard::WlKeyboard)>,
known_kbds: Vec<wl_keyboard::WlKeyboard>,
focus: Option<wl_surface::WlSurface>,
pressed_keys: Vec<u32>,
mods_state: ModifiersState,
keymap: xkb::Keymap,
state: xkb::State,
repeat_rate: i32,
repeat_delay: i32,
}
impl KbdInternal {
fn new(rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>)
fn new(rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>,
repeat_rate: i32, repeat_delay: i32)
-> Result<KbdInternal, ()> {
// we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe
// so confining it inside the KbdInternal allows us to use Rusts mutability rules to make
@ -102,11 +87,14 @@ impl KbdInternal {
).ok_or(())?;
let state = xkb::State::new(&keymap);
Ok(KbdInternal {
known_kbds: Vec::new(),
focus: None,
pressed_keys: Vec::new(),
mods_state: ModifiersState::new(),
keymap: keymap,
state: state,
repeat_rate: repeat_rate,
repeat_delay: repeat_delay,
})
}
@ -125,7 +113,13 @@ impl KbdInternal {
};
// update state
let state_components = self.state.update_key(keycode, direction);
// Offset the keycode by 8, as the evdev XKB rules reflect X's
// broken keycode system, which starts at 8.
let state_components = self.state.update_key(keycode + 8, direction);
println!("KEYCODE: {}", keycode);
println!("MODS: {:b}", state_components);
if state_components != 0 {
self.mods_state.update_with(&self.state);
true
@ -152,9 +146,23 @@ impl KbdInternal {
};
serialized.into()
}
fn with_focused_kbds<F>(&self, mut f: F)
where
F: FnMut(&wl_keyboard::WlKeyboard, &wl_surface::WlSurface),
{
if let Some(ref surface) = self.focus {
for kbd in &self.known_kbds {
if kbd.same_client_as(surface) {
f(kbd, surface);
}
}
}
}
}
/// Errors that can be encountered when creating a keyboard handler
#[derive(Debug)]
pub enum Error {
/// libxkbcommon could not load the specified keymap
BadKeymap,
@ -163,22 +171,30 @@ pub enum Error {
}
/// Create a keyboard handler from a set of RMLVO rules
pub fn create_keyboard_handler<L>(rules: &str, model: &str, layout: &str, variant: &str,
options: Option<String>, logger: L)
-> Result<KbdHandle, Error>
where
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "xkbcommon_handler"));
info!(log, "Initializing a xkbcommon handler with keymap";
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<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,
"options" => &options
);
let internal = KbdInternal::new(rules, model, layout, variant, options).map_err(|_| {
let internal = KbdInternal::new(
rules,
model,
layout,
variant,
options,
repeat_rate,
repeat_delay,
).map_err(|_| {
debug!(log, "Loading keymap failed");
Error::BadKeymap
})?;
info!(log, "Loaded Keymap"; "name" => internal.keymap.layouts().next());
// prepare a tempfile with the keymap, to send it to clients
let mut keymap_file = tempfile().map_err(Error::IoError)?;
@ -192,7 +208,7 @@ where
"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,
@ -214,20 +230,26 @@ struct KbdArc {
/// It can be cloned and all clones manipulate the same internal state. Clones
/// can also be sent across threads.
///
/// See module-level documentation for details of its use.
/// This handle gives you 2 main ways to interact with the keyboard handling:
///
/// - set the current focus for this keyboard: designing the surface that will receive the key inputs
/// 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 `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
/// keyboard handler. It will internally track the state of the keymap.
///
/// The `filter` argument is expected to be a closure which will peek at the generated input
/// as interpreted by the keymap befor it is forwarded to the focused client. If this closure
/// as interpreted by the keymap before it is forwarded to the focused client. If this closure
/// returns false, the input will not be sent to the client. This mechanism can be used to
/// implement compositor-level key bindings for example.
///
@ -239,9 +261,12 @@ impl KbdHandle {
{
trace!(self.arc.logger, "Handling keystroke"; "keycode" => keycode, "state" => format_args!("{:?}", state));
let mut guard = self.arc.internal.lock().unwrap();
let mods_changed = guard.key_input(keycode, state);
let sym = guard.state.key_get_one_sym(keycode);
// 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 mods_changed = guard.key_input(keycode, state);
trace!(self.arc.logger, "Calling input filter";
"mods_state" => format_args!("{:?}", guard.mods_state), "sym" => xkb::keysym_get_name(sym)
@ -254,16 +279,22 @@ impl KbdHandle {
}
// forward to client if no keybinding is triggered
if let Some((_, ref kbd)) = guard.focus {
if mods_changed {
let (dep, la, lo, gr) = guard.serialize_modifiers();
let modifiers = if mods_changed {
Some(guard.serialize_modifiers())
} else {
None
};
let wl_state = match state {
KeyState::Pressed => wl_keyboard::KeyState::Pressed,
KeyState::Released => wl_keyboard::KeyState::Released,
};
guard.with_focused_kbds(|kbd, _| {
if let Some((dep, la, lo, gr)) = modifiers {
kbd.modifiers(serial, dep, la, lo, gr);
}
let wl_state = match state {
KeyState::Pressed => wl_keyboard::KeyState::Pressed,
KeyState::Released => wl_keyboard::KeyState::Released,
};
kbd.key(serial, 0, keycode, wl_state);
});
if guard.focus.is_some() {
trace!(self.arc.logger, "Input forwarded to client");
} else {
trace!(self.arc.logger, "No client currently focused");
@ -272,53 +303,78 @@ impl KbdHandle {
/// Set the current focus of this keyboard
///
/// Any previous focus will be sent a `wl_keyboard::leave` event, and if the new focus
/// is not `None`, a `wl_keyboard::enter` event will be sent.
pub fn set_focus(&self, focus: Option<(wl_surface::WlSurface, wl_keyboard::WlKeyboard)>, serial: u32) {
// TODO: check surface and keyboard are from the same client
/// If the ne focus is different from the previous one, any previous focus
/// will be sent a `wl_keyboard::leave` event, and if the new focus is not `None`,
/// a `wl_keyboard::enter` event will be sent.
pub fn set_focus(&self, focus: Option<&wl_surface::WlSurface>, serial: u32) {
let mut guard = self.arc.internal.lock().unwrap();
// remove current focus
let old_kbd = if let Some((old_surface, old_kbd)) = guard.focus.take() {
if old_surface.status() != Liveness::Dead {
old_kbd.leave(serial, &old_surface);
}
Some(old_kbd)
} else {
None
};
let same = guard
.focus
.as_ref()
.and_then(|f| focus.map(|s| s.equals(f)))
.unwrap_or(false);
// set new focus
if let Some((surface, kbd)) = focus {
if surface.status() != Liveness::Dead {
// send new mods status if client instance changed
match old_kbd {
Some(ref okbd) if okbd.equals(&kbd) => {}
_ => {
let (dep, la, lo, gr) = guard.serialize_modifiers();
kbd.modifiers(serial, dep, la, lo, gr);
}
}
// send enter event
kbd.enter(serial, &surface, guard.serialize_pressed_keys());
if !same {
// unset old focus
guard.with_focused_kbds(|kbd, s| {
kbd.leave(serial, s);
});
// set new focus
guard.focus = focus.and_then(|s| s.clone());
let (dep, la, lo, gr) = guard.serialize_modifiers();
let keys = guard.serialize_pressed_keys();
guard.with_focused_kbds(|kbd, s| {
kbd.modifiers(serial, dep, la, lo, gr);
kbd.enter(serial, s, keys.clone());
});
if guard.focus.is_some() {
trace!(self.arc.logger, "Focus set to new surface");
} else {
trace!(self.arc.logger, "Focus unset");
}
guard.focus = Some((surface, kbd));
trace!(self.arc.logger, "Focus set to new surface");
} else {
trace!(self.arc.logger, "Focus unset");
trace!(self.arc.logger, "Focus unchanged");
}
}
/// Send the keymap to this keyboard
/// Register a new keyboard to this handler
///
/// The keymap will automatically be sent to it
///
/// This should be done first, before anything else is done with this keyboard.
pub fn send_keymap(&self, kbd: &wl_keyboard::WlKeyboard) {
pub(crate) fn new_kbd(&self, kbd: wl_keyboard::WlKeyboard) {
trace!(self.arc.logger, "Sending keymap to client");
kbd.keymap(
wl_keyboard::KeymapFormat::XkbV1,
self.arc.keymap_file.as_raw_fd(),
self.arc.keymap_len,
);
let mut guard = self.arc.internal.lock().unwrap();
if kbd.version() >= 4 {
kbd.repeat_info(guard.repeat_rate, guard.repeat_delay);
}
guard.known_kbds.push(kbd);
}
/// Change the repeat info configured for this keyboard
pub fn change_repeat_info(&self, rate: i32, delay: i32) {
let mut guard = self.arc.internal.lock().unwrap();
guard.repeat_delay = delay;
guard.repeat_rate = rate;
for kbd in &guard.known_kbds {
kbd.repeat_info(rate, delay);
}
}
/// Performs an internal cleanup of known kbds
///
/// Drops any wl_keyboard that is no longer alive
pub(crate) fn cleanup_old_kbds(&self) {
let mut guard = self.arc.internal.lock().unwrap();
guard
.known_kbds
.retain(|kbd| kbd.status() != Liveness::Dead);
}
}

278
src/seat/mod.rs Normal file
View File

@ -0,0 +1,278 @@
//! 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 KeyboardError, KeyboardHandle};
pub use self::pointer::PointerHandle;
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<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
L: Into<Option<::slog::Logger>>,
{
let log = ::slog_or_stdlog(logger);
let seat = Seat {
log: log.new(o!("smithay_module" => "seat_handler", "seat_name" => name.clone())),
name: name,
pointer: None,
keyboard: None,
known_seats: Vec::new(),
};
let token = evl.state().insert(seat);
// TODO: support version 5 (axis)
let global = evl.register_global(4, seat_global_bind, token.clone());
(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 {
seat.capabilities(caps);
}
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<KeyboardHandle, KeyboardError> {
let keyboard = self::keyboard::create_keyboard_handler(
"evdev", // we need this one
model,
layout,
variant,
options,
repeat_delay,
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 {
seat.capabilities(caps);
}
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() {
caps |= wl_seat::Pointer;
}
if self.keyboard.is_some() {
caps |= wl_seat::Keyboard;
}
caps
}
}
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 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);
}
fn seat_implementation() -> wl_seat::Implementation<StateToken<Seat>> {
wl_seat::Implementation {
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 ¯\_(ツ)_/¯
}
},
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
}
},
get_touch: |_evlh, _token, _, _, _touch| {
// TODO
},
release: |_, _, _, _| {},
}
}
fn pointer_implementation() -> wl_pointer::Implementation<()> {
wl_pointer::Implementation {
set_cursor: |_, _, _, _, _, _, _, _| {},
release: |_, _, _, _| {},
}
}
fn keyboard_implementation() -> wl_keyboard::Implementation<()> {
wl_keyboard::Implementation {
release: |_, _, _, _| {},
}
}

121
src/seat/pointer.rs Normal file
View File

@ -0,0 +1,121 @@
use std::sync::{Arc, Mutex};
use wayland_server::{Liveness, Resource};
use wayland_server::protocol::{wl_pointer, wl_surface};
// TODO: handle pointer surface role
struct PointerInternal {
known_pointers: Vec<wl_pointer::WlPointer>,
focus: Option<wl_surface::WlSurface>,
}
impl PointerInternal {
fn new() -> PointerInternal {
PointerInternal {
known_pointers: Vec::new(),
focus: None,
}
}
fn with_focused_pointers<F>(&self, mut f: F)
where
F: FnMut(&wl_pointer::WlPointer, &wl_surface::WlSurface),
{
if let Some(ref focus) = self.focus {
for ptr in &self.known_pointers {
if ptr.same_client_as(focus) {
f(ptr, focus)
}
}
}
}
}
/// 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>>,
}
impl PointerHandle {
pub(crate) fn new_pointer(&self, pointer: wl_pointer::WlPointer) {
let mut guard = self.inner.lock().unwrap();
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 ?
let mut leave = true;
if let Some(ref focus) = guard.focus {
if let Some((ref surface, _, _)) = location {
if focus.equals(surface) {
leave = false;
}
}
}
if leave {
guard.with_focused_pointers(|pointer, surface| {
pointer.leave(serial, surface);
});
guard.focus = None;
}
// do we enter one ?
if let Some((surface, x, y)) = location {
if guard.focus.is_none() {
guard.focus = surface.clone();
guard.with_focused_pointers(|pointer, surface| {
pointer.enter(serial, surface, x, y);
})
} else {
// we were on top of a surface and remained on it
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 guard = self.inner.lock().unwrap();
guard.with_focused_pointers(|pointer, _| {
pointer.button(serial, time, button, state);
})
}
// TODO: handle axis
pub(crate) fn cleanup_old_pointers(&self) {
let mut guard = self.inner.lock().unwrap();
guard
.known_pointers
.retain(|p| p.status() != Liveness::Dead);
}
}
pub(crate) fn create_pointer_handler() -> PointerHandle {
PointerHandle {
inner: Arc::new(Mutex::new(PointerInternal::new())),
}
}

View File

@ -105,10 +105,11 @@
//! access from the `state()` of the event loop and the token returned by the init
//! function.
use compositor::{CompositorToken, Rectangle};
use compositor::CompositorToken;
use compositor::roles::Role;
use std::cell::RefCell;
use std::rc::Rc;
use utils::Rectangle;
use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6 as xdg_positioner,
zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{EventLoop, EventLoopHandle, EventResult, Global, Liveness, Resource, StateToken};

View File

@ -1,9 +1,10 @@
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
ToplevelConfigure, ToplevelState};
use compositor::{CompositorToken, Rectangle};
use compositor::CompositorToken;
use compositor::roles::*;
use std::sync::Mutex;
use utils::Rectangle;
use wayland_protocols::unstable::xdg_shell::server::{zxdg_positioner_v6 as xdg_positioner, zxdg_toplevel_v6};
use wayland_server::{Client, EventLoopHandle, Resource};
use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface};

View File

@ -1,9 +1,10 @@
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
ToplevelConfigure, ToplevelState};
use compositor::{CompositorToken, Rectangle};
use compositor::CompositorToken;
use compositor::roles::*;
use std::sync::Mutex;
use utils::Rectangle;
use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6,
zxdg_surface_v6, zxdg_toplevel_v6};
use wayland_server::{Client, EventLoopHandle, Resource};

5
src/utils/mod.rs Normal file
View File

@ -0,0 +1,5 @@
//! Various utilities functions and types
mod rectangle;
pub use self::rectangle::Rectangle;

20
src/utils/rectangle.rs Normal file
View File

@ -0,0 +1,20 @@
/// A rectangle defined by its top-left corner and dimensions
#[derive(Copy, Clone, Debug)]
pub struct Rectangle {
/// horizontal position of the top-leftcorner of the rectangle, in surface coordinates
pub x: i32,
/// vertical position of the top-leftcorner of the rectangle, in surface coordinates
pub y: i32,
/// width of the rectangle
pub width: i32,
/// height of the rectangle
pub height: i32,
}
impl Rectangle {
/// Checks wether given point is inside a rectangle
pub fn contains(&self, point: (i32, i32)) -> bool {
let (x, y) = point;
(x >= self.x) && (x < self.x + self.width) && (y >= self.y) && (y < self.y + self.height)
}
}