Merge pull request #39 from vberger/seat_global
Global handler for seat
This commit is contained in:
commit
ec4a9a0578
|
@ -39,7 +39,7 @@ branches:
|
||||||
before_script:
|
before_script:
|
||||||
- export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH
|
- export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH
|
||||||
- pip install 'travis-cargo<0.2' --user
|
- 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
|
- which cargo-install-update || cargo install cargo-update
|
||||||
- cargo install-update -a
|
- cargo install-update -a
|
||||||
- mkdir $(pwd)/socket
|
- mkdir $(pwd)/socket
|
||||||
|
|
|
@ -5,7 +5,7 @@ authors = ["Victor Berger <victor.berger@thalesgroup.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wayland-server = "0.10.1"
|
wayland-server = "0.10.2"
|
||||||
nix = "0.7.0"
|
nix = "0.7.0"
|
||||||
xkbcommon = "0.2.1"
|
xkbcommon = "0.2.1"
|
||||||
tempfile = "2.1.5"
|
tempfile = "2.1.5"
|
||||||
|
@ -13,14 +13,14 @@ slog = { version = "2.0.0" }
|
||||||
slog-stdlog = "2.0.0-0.2"
|
slog-stdlog = "2.0.0-0.2"
|
||||||
libloading = "0.4.0"
|
libloading = "0.4.0"
|
||||||
wayland-client = { version = "0.9.9", optional = true }
|
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 }
|
drm = { version = "0.2.1", optional = true }
|
||||||
gbm = { version = "0.2.1", optional = true }
|
gbm = { version = "0.2.1", optional = true }
|
||||||
glium = { version = "0.17.1", optional = true, default-features = false }
|
glium = { version = "0.17.1", optional = true, default-features = false }
|
||||||
input = { version = "0.2.0", optional = true }
|
input = { version = "0.2.0", optional = true }
|
||||||
clippy = { version = "*", optional = true }
|
clippy = { version = "*", optional = true }
|
||||||
rental = "0.4.11"
|
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"
|
image = "0.15.0"
|
||||||
error-chain = "0.11.0"
|
error-chain = "0.11.0"
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,18 @@ use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
use drm::control::encoder::Info as EncoderInfo;
|
use drm::control::encoder::Info as EncoderInfo;
|
||||||
use glium::Surface;
|
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 slog::{Drain, Logger};
|
||||||
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
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::compositor::roles::Role;
|
||||||
use smithay::shell::{shell_init, ShellState};
|
use smithay::shell::ShellState;
|
||||||
use smithay::shm::init_shm_global;
|
use smithay::shm::init_shm_global;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wayland_server::{EventLoopHandle, StateToken};
|
use wayland_server::{EventLoopHandle, StateToken};
|
||||||
|
|
||||||
|
@ -89,16 +91,7 @@ fn main() {
|
||||||
|
|
||||||
init_shm_global(&mut event_loop, vec![], log.clone());
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
let (compositor_token, _, _) =
|
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
||||||
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(),
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize glium
|
* Initialize glium
|
||||||
|
@ -125,16 +118,23 @@ fn main() {
|
||||||
DrmHandlerImpl {
|
DrmHandlerImpl {
|
||||||
shell_state_token,
|
shell_state_token,
|
||||||
compositor_token,
|
compositor_token,
|
||||||
|
window_map: window_map.clone(),
|
||||||
logger: log,
|
logger: log,
|
||||||
},
|
},
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
event_loop.run().unwrap();
|
loop {
|
||||||
|
event_loop.dispatch(Some(16)).unwrap();
|
||||||
|
display.flush_clients();
|
||||||
|
|
||||||
|
window_map.borrow_mut().refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrmHandlerImpl {
|
pub struct DrmHandlerImpl {
|
||||||
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
||||||
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
||||||
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,33 +148,39 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
// redraw the frame, in a simple but inneficient way
|
// redraw the frame, in a simple but inneficient way
|
||||||
{
|
{
|
||||||
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
||||||
for toplevel_surface in state.get(&self.shell_state_token).toplevel_surfaces() {
|
self.window_map
|
||||||
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
.borrow()
|
||||||
// this surface is a root of a subsurface tree that needs to be drawn
|
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
||||||
let initial_place = self.compositor_token
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
.with_surface_data(wl_surface, |data| data.user_data.location.unwrap_or((0, 0)));
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
self.compositor_token
|
self.compositor_token
|
||||||
.with_surface_tree(
|
.with_surface_tree_upward(
|
||||||
wl_surface,
|
wl_surface,
|
||||||
initial_place,
|
initial_place,
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
||||||
// there is actually something to draw !
|
// there is actually something to draw !
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
x += subdata.x;
|
x += subdata.x;
|
||||||
y += subdata.y;
|
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 {
|
.unwrap();
|
||||||
// we are not display, so our children are neither
|
}
|
||||||
TraversalAction::SkipChildren
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
|
use super::WindowMap;
|
||||||
use rand;
|
use rand;
|
||||||
use smithay::compositor::{CompositorToken, SurfaceUserImplementation};
|
use smithay::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceUserImplementation};
|
||||||
use smithay::shell::{PopupConfigure, ShellSurfaceRole, ShellSurfaceUserImplementation, ToplevelConfigure};
|
use smithay::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole,
|
||||||
|
ShellSurfaceUserImplementation, ToplevelConfigure};
|
||||||
use smithay::shm::with_buffer_contents;
|
use smithay::shm::with_buffer_contents;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wayland_server::{EventLoop, StateToken};
|
||||||
|
|
||||||
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
|
define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SurfaceData {
|
pub struct SurfaceData {
|
||||||
pub buffer: Option<(Vec<u8>, (u32, u32))>,
|
pub buffer: Option<(Vec<u8>, (u32, u32))>,
|
||||||
pub location: Option<(i32, i32)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, ()> {
|
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, ()> {
|
||||||
|
@ -48,24 +52,26 @@ pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shell_implementation(
|
pub struct ShellIData<F> {
|
||||||
)
|
pub token: CompositorToken<SurfaceData, Roles, ()>,
|
||||||
-> ShellSurfaceUserImplementation<SurfaceData, Roles, (), 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 {
|
ShellSurfaceUserImplementation {
|
||||||
new_client: |_, _, _| {},
|
new_client: |_, _, _| {},
|
||||||
client_pong: |_, _, _| {},
|
client_pong: |_, _, _| {},
|
||||||
new_toplevel: |_, token, toplevel| {
|
new_toplevel: |_, idata, toplevel| {
|
||||||
let wl_surface = toplevel.get_surface().unwrap();
|
// place the window at a random location in the [0;300]x[0;300] square
|
||||||
token.with_surface_data(wl_surface, |data| {
|
use rand::distributions::{IndependentSample, Range};
|
||||||
// place the window at a random location in the [0;300]x[0;300] square
|
let range = Range::new(0, 300);
|
||||||
use rand::distributions::{IndependentSample, Range};
|
let mut rng = rand::thread_rng();
|
||||||
let range = Range::new(0, 300);
|
let x = range.ind_sample(&mut rng);
|
||||||
let mut rng = rand::thread_rng();
|
let y = range.ind_sample(&mut rng);
|
||||||
let x = range.ind_sample(&mut rng);
|
idata.window_map.borrow_mut().insert(toplevel, (x, y));
|
||||||
let y = range.ind_sample(&mut rng);
|
|
||||||
data.user_data.location = Some((x, y))
|
|
||||||
});
|
|
||||||
ToplevelConfigure {
|
ToplevelConfigure {
|
||||||
size: None,
|
size: None,
|
||||||
states: vec![],
|
states: vec![],
|
||||||
|
@ -92,3 +98,47 @@ pub fn shell_implementation(
|
||||||
show_window_menu: |_, _, _, _, _, _, _| {},
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mod glium;
|
mod glium;
|
||||||
mod implementations;
|
mod implementations;
|
||||||
|
mod window_map;
|
||||||
|
|
||||||
pub use self::glium::GliumDrawer;
|
pub use self::glium::GliumDrawer;
|
||||||
pub use self::implementations::*;
|
pub use self::implementations::*;
|
||||||
|
pub use self::window_map::WindowMap;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,15 +12,112 @@ extern crate wayland_server;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
use glium::Surface;
|
use glium::Surface;
|
||||||
use helpers::{shell_implementation, surface_implementation, GliumDrawer};
|
use helpers::{init_shell, GliumDrawer, MyWindowMap};
|
||||||
use slog::{Drain, Logger};
|
use slog::{Drain, Logger};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
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::backend::winit;
|
||||||
use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction};
|
use smithay::compositor::{SubsurfaceRole, TraversalAction};
|
||||||
use smithay::compositor::roles::Role;
|
use smithay::compositor::roles::Role;
|
||||||
use smithay::shell::shell_init;
|
use smithay::seat::{KeyboardHandle, PointerHandle, Seat};
|
||||||
use smithay::shm::init_shm_global;
|
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() {
|
fn main() {
|
||||||
// A logger facility, here we use the terminal for this example
|
// 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());
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
let (compositor_token, _, _) =
|
let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
||||||
compositor_init(&mut event_loop, surface_implementation(), (), log.clone());
|
|
||||||
|
|
||||||
let (shell_state_token, _, _) = shell_init(
|
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone());
|
||||||
&mut event_loop,
|
|
||||||
compositor_token,
|
let pointer = event_loop.state().get_mut(&seat_token).add_pointer();
|
||||||
shell_implementation(),
|
let keyboard = event_loop
|
||||||
compositor_token,
|
.state()
|
||||||
log.clone(),
|
.get_mut(&seat_token)
|
||||||
);
|
.add_keyboard("", "fr", "oss", None, 1000, 500)
|
||||||
|
.expect("Failed to initialize the keyboard");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize glium
|
* Initialize glium
|
||||||
*/
|
*/
|
||||||
let drawer = GliumDrawer::from(renderer);
|
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:
|
* Add a listening socket:
|
||||||
*/
|
*/
|
||||||
|
@ -70,38 +176,45 @@ fn main() {
|
||||||
// redraw the frame, in a simple but inneficient way
|
// redraw the frame, in a simple but inneficient way
|
||||||
{
|
{
|
||||||
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
||||||
let state = event_loop.state();
|
window_map
|
||||||
for toplevel_surface in state.get(&shell_state_token).toplevel_surfaces() {
|
.borrow()
|
||||||
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
||||||
// this surface is a root of a subsurface tree that needs to be drawn
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
let initial_place = compositor_token
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
.with_surface_data(wl_surface, |data| data.user_data.location.unwrap_or((0, 0)));
|
compositor_token
|
||||||
compositor_token
|
.with_surface_tree_upward(
|
||||||
.with_surface_tree(
|
wl_surface,
|
||||||
wl_surface,
|
initial_place,
|
||||||
initial_place,
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
||||||
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
// there is actually something to draw !
|
||||||
// there is actually something to draw !
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
x += subdata.x;
|
||||||
x += subdata.x;
|
y += subdata.y;
|
||||||
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 {
|
.unwrap();
|
||||||
// we are not display, so our children are neither
|
}
|
||||||
TraversalAction::SkipChildren
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
|
|
||||||
event_loop.dispatch(Some(16)).unwrap();
|
event_loop.dispatch(Some(16)).unwrap();
|
||||||
display.flush_clients();
|
display.flush_clients();
|
||||||
|
|
||||||
|
window_map.borrow_mut().refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ use self::region::RegionData;
|
||||||
use self::roles::{Role, RoleType, WrongRole};
|
use self::roles::{Role, RoleType, WrongRole};
|
||||||
use self::tree::SurfaceData;
|
use self::tree::SurfaceData;
|
||||||
pub use self::tree::TraversalAction;
|
pub use self::tree::TraversalAction;
|
||||||
|
use utils::Rectangle;
|
||||||
use wayland_server::{resource_is_registered, EventLoop, EventLoopHandle, Global};
|
use wayland_server::{resource_is_registered, EventLoop, EventLoopHandle, Global};
|
||||||
use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region,
|
use wayland_server::protocol::{wl_buffer, wl_callback, wl_compositor, wl_output, wl_region,
|
||||||
wl_subcompositor, wl_surface};
|
wl_subcompositor, wl_surface};
|
||||||
|
@ -217,19 +218,6 @@ pub enum RectangleKind {
|
||||||
Subtract,
|
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
|
/// Description of the contents of a region
|
||||||
///
|
///
|
||||||
/// A region is defined as an union and difference of rectangle.
|
/// A region is defined as an union and difference of rectangle.
|
||||||
|
@ -310,7 +298,7 @@ where
|
||||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
R: RoleType + Role<SubsurfaceRole> + 'static,
|
||||||
ID: '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,
|
/// 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
|
/// 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
|
/// If the surface not managed by the CompositorGlobal that provided this token, this
|
||||||
/// will panic (having more than one compositor is not supported).
|
/// 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
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
||||||
-> TraversalAction<T>,
|
-> TraversalAction<T>,
|
||||||
|
@ -339,7 +328,33 @@ where
|
||||||
"Accessing the data of foreign surfaces is not supported."
|
"Accessing the data of foreign surfaces is not supported."
|
||||||
);
|
);
|
||||||
unsafe {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,21 +307,22 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access sequentially the attributes associated with a surface tree,
|
/// 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,
|
/// Note that an internal lock is taken during access of this data,
|
||||||
/// so the tree cannot be manipulated at the same time.
|
/// so the tree cannot be manipulated at the same time.
|
||||||
///
|
///
|
||||||
/// The callback returns wether the traversal should continue or not. Returning
|
/// The callback returns wether the traversal should continue or not. Returning
|
||||||
/// false will cause an early-stopping.
|
/// 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
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
||||||
-> TraversalAction<T>,
|
-> TraversalAction<T>,
|
||||||
{
|
{
|
||||||
// helper function for recursion
|
// helper function for recursion
|
||||||
unsafe fn map<U: 'static, R: 'static, F, T>(surface: &wl_surface::WlSurface,
|
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
|
-> bool
|
||||||
where
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
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) => {
|
TraversalAction::DoChildren(t) => {
|
||||||
// loop over children
|
// loop over children
|
||||||
for c in &data_guard.children {
|
if reverse {
|
||||||
if !map::<U, R, _, _>(c, root, &t, f) {
|
for c in data_guard.children.iter().rev() {
|
||||||
return false;
|
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
|
true
|
||||||
|
@ -368,9 +377,17 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
|
||||||
) {
|
) {
|
||||||
TraversalAction::DoChildren(t) => {
|
TraversalAction::DoChildren(t) => {
|
||||||
// loop over children
|
// loop over children
|
||||||
for c in &data_guard.children {
|
if reverse {
|
||||||
if !map::<U, R, _, _>(c, root, &t, &mut f) {
|
for c in data_guard.children.iter().rev() {
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,9 @@ extern crate error_chain;
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod shm;
|
pub mod shm;
|
||||||
pub mod keyboard;
|
pub mod seat;
|
||||||
pub mod shell;
|
pub mod shell;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
fn slog_or_stdlog<L>(logger: L) -> ::slog::Logger
|
fn slog_or_stdlog<L>(logger: L) -> ::slog::Logger
|
||||||
where
|
where
|
||||||
|
|
|
@ -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 backend::input::KeyState;
|
||||||
use std::io::{Error as IoError, Write};
|
use std::io::{Error as IoError, Write};
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
@ -74,15 +55,19 @@ impl ModifiersState {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KbdInternal {
|
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>,
|
pressed_keys: Vec<u32>,
|
||||||
mods_state: ModifiersState,
|
mods_state: ModifiersState,
|
||||||
keymap: xkb::Keymap,
|
keymap: xkb::Keymap,
|
||||||
state: xkb::State,
|
state: xkb::State,
|
||||||
|
repeat_rate: i32,
|
||||||
|
repeat_delay: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbdInternal {
|
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, ()> {
|
-> Result<KbdInternal, ()> {
|
||||||
// we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe
|
// 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
|
// so confining it inside the KbdInternal allows us to use Rusts mutability rules to make
|
||||||
|
@ -102,11 +87,14 @@ impl KbdInternal {
|
||||||
).ok_or(())?;
|
).ok_or(())?;
|
||||||
let state = xkb::State::new(&keymap);
|
let state = xkb::State::new(&keymap);
|
||||||
Ok(KbdInternal {
|
Ok(KbdInternal {
|
||||||
|
known_kbds: Vec::new(),
|
||||||
focus: None,
|
focus: None,
|
||||||
pressed_keys: Vec::new(),
|
pressed_keys: Vec::new(),
|
||||||
mods_state: ModifiersState::new(),
|
mods_state: ModifiersState::new(),
|
||||||
keymap: keymap,
|
keymap: keymap,
|
||||||
state: state,
|
state: state,
|
||||||
|
repeat_rate: repeat_rate,
|
||||||
|
repeat_delay: repeat_delay,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +113,13 @@ impl KbdInternal {
|
||||||
};
|
};
|
||||||
|
|
||||||
// update state
|
// 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 {
|
if state_components != 0 {
|
||||||
self.mods_state.update_with(&self.state);
|
self.mods_state.update_with(&self.state);
|
||||||
true
|
true
|
||||||
|
@ -152,9 +146,23 @@ impl KbdInternal {
|
||||||
};
|
};
|
||||||
serialized.into()
|
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
|
/// Errors that can be encountered when creating a keyboard handler
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// libxkbcommon could not load the specified keymap
|
/// libxkbcommon could not load the specified keymap
|
||||||
BadKeymap,
|
BadKeymap,
|
||||||
|
@ -163,22 +171,30 @@ pub enum Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a keyboard handler from a set of RMLVO rules
|
/// Create a keyboard handler from a set of RMLVO rules
|
||||||
pub fn create_keyboard_handler<L>(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>, logger: L)
|
options: Option<String>, repeat_delay: i32, repeat_rate: i32,
|
||||||
-> Result<KbdHandle, Error>
|
logger: ::slog::Logger)
|
||||||
where
|
-> Result<KeyboardHandle, Error> {
|
||||||
L: Into<Option<::slog::Logger>>,
|
let log = logger.new(o!("smithay_module" => "xkbcommon_handler"));
|
||||||
{
|
info!(log, "Initializing a xkbcommon handler with keymap query";
|
||||||
let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "xkbcommon_handler"));
|
|
||||||
info!(log, "Initializing a xkbcommon handler with keymap";
|
|
||||||
"rules" => rules, "model" => model, "layout" => layout, "variant" => variant,
|
"rules" => rules, "model" => model, "layout" => layout, "variant" => variant,
|
||||||
"options" => &options
|
"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");
|
debug!(log, "Loading keymap failed");
|
||||||
Error::BadKeymap
|
Error::BadKeymap
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
info!(log, "Loaded Keymap"; "name" => internal.keymap.layouts().next());
|
||||||
|
|
||||||
|
|
||||||
// prepare a tempfile with the keymap, to send it to clients
|
// prepare a tempfile with the keymap, to send it to clients
|
||||||
let mut keymap_file = tempfile().map_err(Error::IoError)?;
|
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()
|
"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,
|
||||||
|
@ -214,20 +230,26 @@ 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.
|
||||||
///
|
///
|
||||||
/// 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)]
|
#[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
|
||||||
/// keyboard handler. It will internally track the state of the keymap.
|
/// 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
|
/// 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
|
/// returns false, the input will not be sent to the client. This mechanism can be used to
|
||||||
/// implement compositor-level key bindings for example.
|
/// 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));
|
trace!(self.arc.logger, "Handling keystroke"; "keycode" => keycode, "state" => format_args!("{:?}", state));
|
||||||
let mut guard = self.arc.internal.lock().unwrap();
|
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";
|
trace!(self.arc.logger, "Calling input filter";
|
||||||
"mods_state" => format_args!("{:?}", guard.mods_state), "sym" => xkb::keysym_get_name(sym)
|
"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
|
// forward to client if no keybinding is triggered
|
||||||
if let Some((_, ref kbd)) = guard.focus {
|
let modifiers = if mods_changed {
|
||||||
if mods_changed {
|
Some(guard.serialize_modifiers())
|
||||||
let (dep, la, lo, gr) = 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);
|
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);
|
kbd.key(serial, 0, keycode, wl_state);
|
||||||
|
});
|
||||||
|
if guard.focus.is_some() {
|
||||||
trace!(self.arc.logger, "Input forwarded to client");
|
trace!(self.arc.logger, "Input forwarded to client");
|
||||||
} else {
|
} else {
|
||||||
trace!(self.arc.logger, "No client currently focused");
|
trace!(self.arc.logger, "No client currently focused");
|
||||||
|
@ -272,53 +303,78 @@ impl KbdHandle {
|
||||||
|
|
||||||
/// Set the current focus of this keyboard
|
/// Set the current focus of this keyboard
|
||||||
///
|
///
|
||||||
/// Any previous focus will be sent a `wl_keyboard::leave` event, and if the new focus
|
/// If the ne focus is different from the previous one, any previous focus
|
||||||
/// is not `None`, a `wl_keyboard::enter` event will be sent.
|
/// will be sent a `wl_keyboard::leave` event, and if the new focus is not `None`,
|
||||||
pub fn set_focus(&self, focus: Option<(wl_surface::WlSurface, wl_keyboard::WlKeyboard)>, serial: u32) {
|
/// a `wl_keyboard::enter` event will be sent.
|
||||||
// TODO: check surface and keyboard are from the same client
|
pub fn set_focus(&self, focus: Option<&wl_surface::WlSurface>, serial: u32) {
|
||||||
|
|
||||||
let mut guard = self.arc.internal.lock().unwrap();
|
let mut guard = self.arc.internal.lock().unwrap();
|
||||||
|
|
||||||
// remove current focus
|
let same = guard
|
||||||
let old_kbd = if let Some((old_surface, old_kbd)) = guard.focus.take() {
|
.focus
|
||||||
if old_surface.status() != Liveness::Dead {
|
.as_ref()
|
||||||
old_kbd.leave(serial, &old_surface);
|
.and_then(|f| focus.map(|s| s.equals(f)))
|
||||||
}
|
.unwrap_or(false);
|
||||||
Some(old_kbd)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// set new focus
|
if !same {
|
||||||
if let Some((surface, kbd)) = focus {
|
// unset old focus
|
||||||
if surface.status() != Liveness::Dead {
|
guard.with_focused_kbds(|kbd, s| {
|
||||||
// send new mods status if client instance changed
|
kbd.leave(serial, s);
|
||||||
match old_kbd {
|
});
|
||||||
Some(ref okbd) if okbd.equals(&kbd) => {}
|
|
||||||
_ => {
|
// set new focus
|
||||||
let (dep, la, lo, gr) = guard.serialize_modifiers();
|
guard.focus = focus.and_then(|s| s.clone());
|
||||||
kbd.modifiers(serial, dep, la, lo, gr);
|
let (dep, la, lo, gr) = guard.serialize_modifiers();
|
||||||
}
|
let keys = guard.serialize_pressed_keys();
|
||||||
}
|
guard.with_focused_kbds(|kbd, s| {
|
||||||
// send enter event
|
kbd.modifiers(serial, dep, la, lo, gr);
|
||||||
kbd.enter(serial, &surface, guard.serialize_pressed_keys());
|
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 {
|
} 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.
|
/// 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");
|
trace!(self.arc.logger, "Sending keymap to client");
|
||||||
kbd.keymap(
|
kbd.keymap(
|
||||||
wl_keyboard::KeymapFormat::XkbV1,
|
wl_keyboard::KeymapFormat::XkbV1,
|
||||||
self.arc.keymap_file.as_raw_fd(),
|
self.arc.keymap_file.as_raw_fd(),
|
||||||
self.arc.keymap_len,
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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: |_, _, _, _| {},
|
||||||
|
}
|
||||||
|
}
|
|
@ -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())),
|
||||||
|
}
|
||||||
|
}
|
|
@ -105,10 +105,11 @@
|
||||||
//! access from the `state()` of the event loop and the token returned by the init
|
//! access from the `state()` of the event loop and the token returned by the init
|
||||||
//! function.
|
//! function.
|
||||||
|
|
||||||
use compositor::{CompositorToken, Rectangle};
|
use compositor::CompositorToken;
|
||||||
use compositor::roles::Role;
|
use compositor::roles::Role;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use utils::Rectangle;
|
||||||
use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6 as xdg_positioner,
|
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};
|
zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6};
|
||||||
use wayland_server::{EventLoop, EventLoopHandle, EventResult, Global, Liveness, Resource, StateToken};
|
use wayland_server::{EventLoop, EventLoopHandle, EventResult, Global, Liveness, Resource, StateToken};
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
|
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
|
||||||
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
|
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
|
||||||
ToplevelConfigure, ToplevelState};
|
ToplevelConfigure, ToplevelState};
|
||||||
use compositor::{CompositorToken, Rectangle};
|
use compositor::CompositorToken;
|
||||||
use compositor::roles::*;
|
use compositor::roles::*;
|
||||||
use std::sync::Mutex;
|
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_protocols::unstable::xdg_shell::server::{zxdg_positioner_v6 as xdg_positioner, zxdg_toplevel_v6};
|
||||||
use wayland_server::{Client, EventLoopHandle, Resource};
|
use wayland_server::{Client, EventLoopHandle, Resource};
|
||||||
use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface};
|
use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface};
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
|
use super::{make_shell_client_data, PopupConfigure, PopupState, PositionerState, ShellClient,
|
||||||
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
|
ShellClientData, ShellSurfaceIData, ShellSurfacePendingState, ShellSurfaceRole,
|
||||||
ToplevelConfigure, ToplevelState};
|
ToplevelConfigure, ToplevelState};
|
||||||
use compositor::{CompositorToken, Rectangle};
|
use compositor::CompositorToken;
|
||||||
use compositor::roles::*;
|
use compositor::roles::*;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use utils::Rectangle;
|
||||||
use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6,
|
use wayland_protocols::unstable::xdg_shell::server::{zxdg_popup_v6, zxdg_positioner_v6, zxdg_shell_v6,
|
||||||
zxdg_surface_v6, zxdg_toplevel_v6};
|
zxdg_surface_v6, zxdg_toplevel_v6};
|
||||||
use wayland_server::{Client, EventLoopHandle, Resource};
|
use wayland_server::{Client, EventLoopHandle, Resource};
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
//! Various utilities functions and types
|
||||||
|
|
||||||
|
mod rectangle;
|
||||||
|
|
||||||
|
pub use self::rectangle::Rectangle;
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue