From 19634f30ede9b9de029b182fb17f6b24c751d323 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 13 Jun 2017 16:52:43 +0200 Subject: [PATCH] example: actually draw some windows! --- examples/helpers/glium.rs | 116 +++++++++++++++++++++++++++++++++++ examples/helpers/mod.rs | 5 ++ examples/helpers/shell.rs | 97 +++++++++++++++++++++++++++++ examples/simple.rs | 126 ++++++++++++++++++++++++++++++++------ 4 files changed, 326 insertions(+), 18 deletions(-) create mode 100644 examples/helpers/glium.rs create mode 100644 examples/helpers/mod.rs create mode 100644 examples/helpers/shell.rs diff --git a/examples/helpers/glium.rs b/examples/helpers/glium.rs new file mode 100644 index 0000000..4af2b4c --- /dev/null +++ b/examples/helpers/glium.rs @@ -0,0 +1,116 @@ +use glium; +use glium::Surface; +use glium::index::PrimitiveType; + +#[derive(Copy, Clone)] +struct Vertex { + position: [f32; 2], + tex_coords: [f32; 2], +} + +implement_vertex!(Vertex, position, tex_coords); + +pub struct GliumDrawer<'a, F: 'a> { + display: &'a F, + vertex_buffer: glium::VertexBuffer, + index_buffer: glium::IndexBuffer, + program: glium::Program, +} + +impl<'a, F: glium::backend::Facade + 'a> GliumDrawer<'a, F> { + pub fn new(display: &'a F) -> GliumDrawer<'a, F> + { + + // building the vertex buffer, which contains all the vertices that we will draw + let vertex_buffer = glium::VertexBuffer::new(display, + &[Vertex { + position: [0.0, 0.0], + tex_coords: [0.0, 0.0], + }, + Vertex { + position: [0.0, 1.0], + tex_coords: [0.0, 1.0], + }, + Vertex { + position: [1.0, 1.0], + tex_coords: [1.0, 1.0], + }, + Vertex { + position: [1.0, 0.0], + tex_coords: [1.0, 0.0], + }]) + .unwrap(); + + // building the index buffer + let index_buffer = + glium::IndexBuffer::new(display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap(); + + // compiling shaders and linking them together + let program = program!(display, + 100 => { + vertex: " + #version 100 + uniform lowp mat4 matrix; + attribute lowp vec2 position; + attribute lowp vec2 tex_coords; + varying lowp vec2 v_tex_coords; + void main() { + gl_Position = matrix * vec4(position, 0.0, 1.0); + v_tex_coords = tex_coords; + } + ", + + fragment: " + #version 100 + uniform lowp sampler2D tex; + varying lowp vec2 v_tex_coords; + void main() { + lowp vec4 color = texture2D(tex, v_tex_coords); + gl_FragColor.r = color.z; + gl_FragColor.g = color.y; + gl_FragColor.b = color.x; + gl_FragColor.a = color.w; + } + ", + }, + ) + .unwrap(); + + GliumDrawer { + display, + vertex_buffer, + index_buffer, + program, + } + } + + pub fn draw(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32), surface_location: (i32,i32), screen_size: (u32,u32)) { + + let image = glium::texture::RawImage2d { + data: contents.into(), + width: surface_dimensions.0, + height: surface_dimensions.1, + format: glium::texture::ClientFormat::U8U8U8U8 + }; + let opengl_texture = glium::texture::CompressedSrgbTexture2d::new(self.display, image).unwrap(); + + let xscale = 2.0*(surface_dimensions.0 as f32) / (screen_size.0 as f32); + let yscale = -2.0*(surface_dimensions.1 as f32) / (screen_size.1 as f32); + + let x = 2.0*(surface_location.0 as f32) / (screen_size.0 as f32) - 1.0; + let y = 1.0 - 2.0*(surface_location.1 as f32) / (screen_size.1 as f32); + + let uniforms = uniform! { + matrix: [ + [xscale, 0.0 , 0.0, 0.0], + [ 0.0 , yscale , 0.0, 0.0], + [ 0.0 , 0.0 , 1.0, 0.0], + [ x , y , 0.0, 1.0] + ], + tex: &opengl_texture + }; + + target.draw(&self.vertex_buffer, &self.index_buffer, &self.program, &uniforms, &Default::default()).unwrap(); + + } +} diff --git a/examples/helpers/mod.rs b/examples/helpers/mod.rs new file mode 100644 index 0000000..e913100 --- /dev/null +++ b/examples/helpers/mod.rs @@ -0,0 +1,5 @@ +mod shell; +mod glium; + +pub use self::glium::GliumDrawer; +pub use self::shell::WlShellStubHandler; diff --git a/examples/helpers/shell.rs b/examples/helpers/shell.rs new file mode 100644 index 0000000..de73b2c --- /dev/null +++ b/examples/helpers/shell.rs @@ -0,0 +1,97 @@ + + +use smithay::compositor::{CompositorToken, Handler as CompositorHandler}; +use wayland_server::{Client, EventLoopHandle, GlobalHandler, Init, Resource}; +use wayland_server::protocol::{wl_shell, wl_shell_surface, wl_surface}; + +/// A very basic handler for wl_shell +/// +/// All it does is track which wl_shell_surface exist and which do not, +/// as well as the roles associated to them. +/// +/// That's it. +pub struct WlShellStubHandler { + my_id: Option, + token: CompositorToken, + surfaces: Vec<(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)>, +} + +impl WlShellStubHandler { + pub fn new(compositor_token: CompositorToken) -> WlShellStubHandler { + WlShellStubHandler { + my_id: None, + token: compositor_token, + surfaces: Vec::new(), + } + } + + pub fn surfaces(&self) -> &[(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)] { + &self.surfaces + } +} + +impl Init for WlShellStubHandler { + fn init(&mut self, evqh: &mut EventLoopHandle, index: usize) { + self.my_id = Some(index) + } +} + + +impl GlobalHandler for WlShellStubHandler + where U: Send + 'static, + H: CompositorHandler + Send + 'static +{ + fn bind(&mut self, evqh: &mut EventLoopHandle, client: &Client, global: wl_shell::WlShell) { + evqh.register::<_, Self>(&global, + self.my_id + .expect("WlShellStubHandler was not properly initialized.")); + } +} + +impl wl_shell::Handler for WlShellStubHandler + where U: Send + 'static, + H: CompositorHandler + Send + 'static +{ + fn get_shell_surface(&mut self, evqh: &mut EventLoopHandle, client: &Client, + resource: &wl_shell::WlShell, id: wl_shell_surface::WlShellSurface, + surface: &wl_surface::WlSurface) { + let surface = surface.clone().expect("WlShellStubHandler can only manage surfaces managed by Smithay's CompositorHandler."); + if self.token.give_role(&surface).is_err() { + // This surface already has a role, and thus cannot be given one! + resource.post_error(wl_shell::Error::Role as u32, + "Surface already has a role.".into()); + return; + } + evqh.register::<_, Self>(&id, self.my_id.unwrap()); + self.surfaces.push((id, surface)) + } +} + +unsafe impl ::wayland_server::Handler for WlShellStubHandler + where U: Send + 'static, + H: CompositorHandler + Send + 'static +{ + unsafe fn message(&mut self, evq: &mut EventLoopHandle, client: &Client, resource: &wl_shell::WlShell, + opcode: u32, args: *const ::wayland_server::sys::wl_argument) + -> Result<(), ()> { + as ::wayland_server::protocol::wl_shell::Handler>::__message(self, evq, client, resource, opcode, args) + } +} + +impl wl_shell_surface::Handler for WlShellStubHandler + where U: Send + 'static, + H: CompositorHandler + Send + 'static +{ +} + +unsafe impl ::wayland_server::Handler for WlShellStubHandler + where U: Send + 'static, + H: CompositorHandler + Send + 'static +{ + unsafe fn message(&mut self, evq: &mut EventLoopHandle, client: &Client, + resource: &wl_shell_surface::WlShellSurface, opcode: u32, + args: *const ::wayland_server::sys::wl_argument) + -> Result<(), ()> { + as ::wayland_server::protocol::wl_shell_surface::Handler>::__message(self, evq, client, resource, opcode, args) + } +} diff --git a/examples/simple.rs b/examples/simple.rs index 7463fc0..1cdbb86 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,5 +1,6 @@ extern crate wayland_server; extern crate smithay; +#[macro_use] extern crate glium; #[macro_use] @@ -7,21 +8,59 @@ extern crate slog; extern crate slog_async; extern crate slog_term; +mod helpers; use glium::Surface; + +use helpers::{WlShellStubHandler, GliumDrawer}; use slog::*; use smithay::backend::graphics::glium::IntoGlium; use smithay::backend::input::InputBackend; use smithay::backend::winit; -use smithay::compositor::{self, CompositorHandler}; -use smithay::shm::ShmGlobal; +use smithay::compositor::{self, CompositorHandler, CompositorToken}; +use smithay::shm::{ShmGlobal, ShmToken, BufferData}; -use wayland_server::protocol::{wl_compositor, wl_shm, wl_subcompositor}; +use wayland_server::protocol::{wl_compositor, wl_shell, wl_shm, wl_subcompositor, wl_surface}; +use wayland_server::{EventLoopHandle,Client,Liveness, Resource}; -struct SurfaceHandler; +struct SurfaceHandler { + shm_token: ShmToken +} -impl compositor::Handler for SurfaceHandler {} +#[derive(Default)] +struct SurfaceData { + buffer: Option<(Vec, (u32, u32))> +} + +impl compositor::Handler for SurfaceHandler { + fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, token: CompositorToken) { + // we retrieve the contents of the associated buffer and copy it + token.with_surface_data(surface, |attributes| { + match attributes.buffer.take() { + Some(Some((buffer, (x,y)))) => { + self.shm_token.with_buffer_contents(&buffer, |slice, data| { + let offset = data.offset as usize; + let stride = data.stride as usize; + let width = data.width as usize; + let height = data.height as usize; + let mut new_vec = Vec::with_capacity(width*height*4); + for i in 0..height { + new_vec.extend(&slice[(offset+i*stride)..(offset+i*stride+width*4)]); + } + attributes.user_data.buffer = Some((new_vec, (data.width as u32, data.height as u32))); + }); + + } + Some(None) => { + // erase the contents + attributes.user_data.buffer = None; + } + None => {} + } + }); + } +} fn main() { // A logger facility, here we use the terminal for this example @@ -31,7 +70,7 @@ fn main() { // Initialize a simple backend for testing let (renderer, mut input) = winit::init(log.clone()).unwrap(); - let (_display, mut event_loop) = wayland_server::create_display(); + let (mut display, mut event_loop) = wayland_server::create_display(); /* * Initialize wl_shm global @@ -41,40 +80,91 @@ fn main() { let shm_handler_id = event_loop.add_handler_with_init(ShmGlobal::new(vec![], log.clone())); // Register this handler to advertise a wl_shm global of version 1 event_loop.register_global::(shm_handler_id, 1); + // retreive the token + let shm_token = { + let state = event_loop.state(); + state + .get_handler::(shm_handler_id) + .get_token() + }; + /* * Initialize the compositor global */ let compositor_handler_id = - event_loop.add_handler_with_init(CompositorHandler::<(), _>::new(SurfaceHandler, log.clone())); + event_loop.add_handler_with_init(CompositorHandler::::new(SurfaceHandler { shm_token: shm_token.clone() }, log.clone())); // register it to handle wl_compositor and wl_subcompositor - event_loop.register_global::>(compositor_handler_id, 4); - event_loop.register_global::>(compositor_handler_id, 1); + event_loop.register_global::>(compositor_handler_id, 4); + event_loop.register_global::>(compositor_handler_id, 1); + // retrieve the tokens + let compositor_token = { + let state = event_loop.state(); + state + .get_handler::>(compositor_handler_id) + .get_token() + }; /* - * retrieve the tokens + * Initialize the shell stub global */ - let (shm_token, compositor_token) = { - let state = event_loop.state(); - (state.get_handler::(shm_handler_id).get_token(), - state - .get_handler::>(compositor_handler_id) - .get_token()) - }; + let shell_handler_id = + event_loop.add_handler_with_init(WlShellStubHandler::new(compositor_token.clone())); + event_loop.register_global::>(shell_handler_id, + 1); /* * Initialize glium */ let context = renderer.into_glium(); + let drawer = GliumDrawer::new(&context); + + /* + * Add a listening socket: + */ + let name = display + .add_socket_auto() + .unwrap() + .into_string() + .unwrap(); + println!("Listening on socket: {}", name); loop { input.dispatch_new_events().unwrap(); let mut frame = context.draw(); - frame.clear(None, Some((0.0, 0.0, 0.0, 1.0)), false, None, None); + frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, None, None); + // redraw the frame, in a simple but inneficient way + { + let screen_dimensions = context.get_framebuffer_dimensions(); + let state = event_loop.state(); + for &(_, ref surface) in + state + .get_handler::>(shell_handler_id) + .surfaces() { + if surface.status() != Liveness::Alive { + continue; + } + // this surface is a root of a subsurface tree that needs to be drawn + compositor_token.with_surface_tree(surface, |surface, attributes| { + if let Some((ref contents, (w, h))) = attributes.user_data.buffer { + // there is actually something to draw ! + let mut x = 100; + let mut y = 100; + if let Some(ref subdata) = attributes.subsurface_attributes { + x += subdata.x; + y += subdata.y; + } + drawer.draw(&mut frame, contents, (w, h), (x, y), screen_dimensions); + } + true + }); + } + } frame.finish().unwrap(); event_loop.dispatch(Some(16)).unwrap(); + display.flush_clients(); } }