From e6eb03c184281bcff0d94a1bffc2f7ce312e6a75 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 22 Sep 2017 14:56:59 +0200 Subject: [PATCH] examples: introduce window_map --- examples/drm.rs | 82 ++++++------ examples/helpers/implementations.rs | 84 +++++++++--- examples/helpers/mod.rs | 2 + examples/helpers/window_map.rs | 191 ++++++++++++++++++++++++++++ examples/winit.rs | 72 +++++------ 5 files changed, 340 insertions(+), 91 deletions(-) create mode 100644 examples/helpers/window_map.rs diff --git a/examples/drm.rs b/examples/drm.rs index 3c1d4eb..f181c6b 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -17,7 +17,7 @@ use drm::control::{Device as ControlDevice, ResourceInfo}; use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState}; use drm::control::encoder::Info as EncoderInfo; use glium::Surface; -use helpers::{shell_implementation, surface_implementation, GliumDrawer, Roles, SurfaceData}; +use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData}; use slog::{Drain, Logger}; use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler}; use smithay::backend::graphics::egl::EGLGraphicsBackend; @@ -25,8 +25,10 @@ use smithay::compositor::{compositor_init, CompositorToken, SubsurfaceRole, Trav use smithay::compositor::roles::Role; use smithay::shell::{shell_init, ShellState}; use smithay::shm::init_shm_global; +use std::cell::RefCell; use std::fs::OpenOptions; use std::io::Error as IoError; +use std::rc::Rc; use std::time::Duration; use wayland_server::{EventLoopHandle, StateToken}; @@ -89,16 +91,7 @@ fn main() { init_shm_global(&mut event_loop, vec![], log.clone()); - let (compositor_token, _, _) = - compositor_init(&mut event_loop, surface_implementation(), (), log.clone()); - - let (shell_state_token, _, _) = shell_init( - &mut event_loop, - compositor_token, - shell_implementation(), - compositor_token, - log.clone(), - ); + let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone()); /* * Initialize glium @@ -125,16 +118,23 @@ fn main() { DrmHandlerImpl { shell_state_token, compositor_token, + window_map: window_map.clone(), logger: log, }, ).unwrap(); - event_loop.run().unwrap(); + loop { + event_loop.dispatch(Some(16)).unwrap(); + display.flush_clients(); + + window_map.borrow_mut().refresh(); + } } pub struct DrmHandlerImpl { shell_state_token: StateToken>, compositor_token: CompositorToken, + window_map: Rc>, logger: ::slog::Logger, } @@ -148,33 +148,39 @@ impl DrmHandler> for DrmHandlerImpl { // redraw the frame, in a simple but inneficient way { let screen_dimensions = drawer.get_framebuffer_dimensions(); - for toplevel_surface in state.get(&self.shell_state_token).toplevel_surfaces() { - if let Some(wl_surface) = toplevel_surface.get_surface() { - // this surface is a root of a subsurface tree that needs to be drawn - let initial_place = self.compositor_token - .with_surface_data(wl_surface, |data| data.user_data.location.unwrap_or((0, 0))); - self.compositor_token - .with_surface_tree( - wl_surface, - initial_place, - |_surface, attributes, role, &(mut x, mut y)| { - if let Some((ref contents, (w, h))) = attributes.user_data.buffer { - // there is actually something to draw ! - if let Ok(subdata) = Role::::data(role) { - x += subdata.x; - y += subdata.y; + self.window_map + .borrow() + .with_windows_from_bottom_to_top(|toplevel_surface, initial_place| { + if let Some(wl_surface) = toplevel_surface.get_surface() { + // this surface is a root of a subsurface tree that needs to be drawn + self.compositor_token + .with_surface_tree_upward( + wl_surface, + initial_place, + |_surface, attributes, role, &(mut x, mut y)| { + if let Some((ref contents, (w, h))) = attributes.user_data.buffer { + // there is actually something to draw ! + if let Ok(subdata) = Role::::data(role) { + x += subdata.x; + y += subdata.y; + } + drawer.render( + &mut frame, + contents, + (w, h), + (x, y), + screen_dimensions, + ); + TraversalAction::DoChildren((x, y)) + } else { + // we are not display, so our children are neither + TraversalAction::SkipChildren } - drawer.render(&mut frame, contents, (w, h), (x, y), screen_dimensions); - TraversalAction::DoChildren((x, y)) - } else { - // we are not display, so our children are neither - TraversalAction::SkipChildren - } - }, - ) - .unwrap(); - } - } + }, + ) + .unwrap(); + } + }); } frame.finish().unwrap(); } diff --git a/examples/helpers/implementations.rs b/examples/helpers/implementations.rs index e89874d..4fc3ff5 100644 --- a/examples/helpers/implementations.rs +++ b/examples/helpers/implementations.rs @@ -1,14 +1,18 @@ +use super::WindowMap; use rand; -use smithay::compositor::{CompositorToken, SurfaceUserImplementation}; -use smithay::shell::{PopupConfigure, ShellSurfaceRole, ShellSurfaceUserImplementation, ToplevelConfigure}; +use smithay::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceUserImplementation}; +use smithay::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole, + ShellSurfaceUserImplementation, ToplevelConfigure}; use smithay::shm::with_buffer_contents; +use std::cell::RefCell; +use std::rc::Rc; +use wayland_server::{EventLoop, StateToken}; define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] ); #[derive(Default)] pub struct SurfaceData { pub buffer: Option<(Vec, (u32, u32))>, - pub location: Option<(i32, i32)>, } pub fn surface_implementation() -> SurfaceUserImplementation { @@ -48,24 +52,26 @@ pub fn surface_implementation() -> SurfaceUserImplementation ShellSurfaceUserImplementation, ()> +pub struct ShellIData { + pub token: CompositorToken, + pub window_map: Rc>>, +} + +pub fn shell_implementation() -> ShellSurfaceUserImplementation, ()> +where + F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, { ShellSurfaceUserImplementation { new_client: |_, _, _| {}, client_pong: |_, _, _| {}, - new_toplevel: |_, token, toplevel| { - let wl_surface = toplevel.get_surface().unwrap(); - token.with_surface_data(wl_surface, |data| { - // place the window at a random location in the [0;300]x[0;300] square - use rand::distributions::{IndependentSample, Range}; - let range = Range::new(0, 300); - let mut rng = rand::thread_rng(); - let x = range.ind_sample(&mut rng); - let y = range.ind_sample(&mut rng); - data.user_data.location = Some((x, y)) - }); + new_toplevel: |_, idata, toplevel| { + // place the window at a random location in the [0;300]x[0;300] square + use rand::distributions::{IndependentSample, Range}; + let range = Range::new(0, 300); + let mut rng = rand::thread_rng(); + let x = range.ind_sample(&mut rng); + let y = range.ind_sample(&mut rng); + idata.window_map.borrow_mut().insert(toplevel, (x, y)); ToplevelConfigure { size: None, states: vec![], @@ -92,3 +98,47 @@ pub fn shell_implementation( show_window_menu: |_, _, _, _, _, _, _| {}, } } + +fn get_size(attrs: &SurfaceAttributes) -> 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) -> Option<(i32, i32)>, +>; + +pub fn init_shell( + evl: &mut EventLoop, log: ::slog::Logger) + -> ( + CompositorToken, + StateToken>, + Rc>, + ) { + 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) +} diff --git a/examples/helpers/mod.rs b/examples/helpers/mod.rs index 36e6945..6659931 100644 --- a/examples/helpers/mod.rs +++ b/examples/helpers/mod.rs @@ -1,5 +1,7 @@ mod glium; mod implementations; +mod window_map; pub use self::glium::GliumDrawer; pub use self::implementations::*; +pub use self::window_map::WindowMap; diff --git a/examples/helpers/window_map.rs b/examples/helpers/window_map.rs new file mode 100644 index 0000000..e05cc7c --- /dev/null +++ b/examples/helpers/window_map.rs @@ -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 { + location: (i32, i32), + surface: Rectangle, + toplevel: ToplevelSurface, +} + +impl Window +where + U: 'static, + R: Role + Role + '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(&self, point: (f64, f64), ctoken: CompositorToken, get_size: F) + -> Option<(wl_surface::WlSurface, (f64, f64))> + where + F: Fn(&SurfaceAttributes) -> 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::::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(&mut self, ctoken: CompositorToken, mut get_size: F) + where + F: Fn(&SurfaceAttributes) -> 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::::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 { + ctoken: CompositorToken, + windows: Vec>, + get_size: F, +} + +impl WindowMap +where + F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, + U: 'static, + R: Role + Role + 'static, + CID: 'static, + SD: 'static, +{ + pub fn new(ctoken: CompositorToken, get_size: F) -> WindowMap { + WindowMap { + ctoken: ctoken, + windows: Vec::new(), + get_size: get_size, + } + } + + pub fn insert(&mut self, toplevel: ToplevelSurface, 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(&self, mut f: Func) + where + Func: FnMut(&ToplevelSurface, (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(); + } +} diff --git a/examples/winit.rs b/examples/winit.rs index 037ece2..03a9783 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -12,7 +12,7 @@ extern crate wayland_server; mod helpers; use glium::Surface; -use helpers::{shell_implementation, surface_implementation, GliumDrawer}; +use helpers::{init_shell, GliumDrawer, MyWindowMap}; use slog::{Drain, Logger}; use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::input::InputBackend; @@ -40,16 +40,8 @@ fn main() { init_shm_global(&mut event_loop, vec![], log.clone()); - let (compositor_token, _, _) = - compositor_init(&mut event_loop, surface_implementation(), (), log.clone()); + let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone()); - let (shell_state_token, _, _) = shell_init( - &mut event_loop, - compositor_token, - shell_implementation(), - compositor_token, - log.clone(), - ); /* * Initialize glium @@ -71,37 +63,45 @@ fn main() { { let screen_dimensions = drawer.get_framebuffer_dimensions(); let state = event_loop.state(); - for toplevel_surface in state.get(&shell_state_token).toplevel_surfaces() { - if let Some(wl_surface) = toplevel_surface.get_surface() { - // this surface is a root of a subsurface tree that needs to be drawn - let initial_place = compositor_token - .with_surface_data(wl_surface, |data| data.user_data.location.unwrap_or((0, 0))); - compositor_token - .with_surface_tree( - wl_surface, - initial_place, - |_surface, attributes, role, &(mut x, mut y)| { - if let Some((ref contents, (w, h))) = attributes.user_data.buffer { - // there is actually something to draw ! - if let Ok(subdata) = Role::::data(role) { - x += subdata.x; - y += subdata.y; + window_map + .borrow() + .with_windows_from_bottom_to_top(|toplevel_surface, initial_place| { + if let Some(wl_surface) = toplevel_surface.get_surface() { + // this surface is a root of a subsurface tree that needs to be drawn + compositor_token + .with_surface_tree_upward( + wl_surface, + initial_place, + |_surface, attributes, role, &(mut x, mut y)| { + if let Some((ref contents, (w, h))) = attributes.user_data.buffer { + // there is actually something to draw ! + if let Ok(subdata) = Role::::data(role) { + x += subdata.x; + y += subdata.y; + } + drawer.render( + &mut frame, + contents, + (w, h), + (x, y), + screen_dimensions, + ); + TraversalAction::DoChildren((x, y)) + } else { + // we are not display, so our children are neither + TraversalAction::SkipChildren } - drawer.render(&mut frame, contents, (w, h), (x, y), screen_dimensions); - TraversalAction::DoChildren((x, y)) - } else { - // we are not display, so our children are neither - TraversalAction::SkipChildren - } - }, - ) - .unwrap(); - } - } + }, + ) + .unwrap(); + } + }); } frame.finish().unwrap(); event_loop.dispatch(Some(16)).unwrap(); display.flush_clients(); + + window_map.borrow_mut().refresh(); } }