examples: introduce window_map

This commit is contained in:
Victor Berger 2017-09-22 14:56:59 +02:00
parent e3cb52d06b
commit e6eb03c184
5 changed files with 340 additions and 91 deletions

View File

@ -17,7 +17,7 @@ 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;
@ -25,8 +25,10 @@ use smithay::compositor::{compositor_init, CompositorToken, SubsurfaceRole, Trav
use smithay::compositor::roles::Role; use smithay::compositor::roles::Role;
use smithay::shell::{shell_init, ShellState}; use smithay::shell::{shell_init, 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();
} }

View File

@ -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)
}

View File

@ -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;

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() {
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>, mut 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() {
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,7 +12,7 @@ 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::InputBackend;
@ -40,16 +40,8 @@ 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
@ -71,37 +63,45 @@ fn main() {
{ {
let screen_dimensions = drawer.get_framebuffer_dimensions(); let screen_dimensions = drawer.get_framebuffer_dimensions();
let state = event_loop.state(); let state = event_loop.state();
for toplevel_surface in state.get(&shell_state_token).toplevel_surfaces() { 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 = 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
compositor_token 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();
event_loop.dispatch(Some(16)).unwrap(); event_loop.dispatch(Some(16)).unwrap();
display.flush_clients(); display.flush_clients();
window_map.borrow_mut().refresh();
} }
} }