use glium; use glium::{Frame, GlObject, Surface}; use glium::index::PrimitiveType; use glium::texture::{MipmapsOption, Texture2d, UncompressedFloatFormat}; use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::graphics::egl::error::Result as EGLResult; use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages, EGLWaylandExtensions, Format}; use smithay::backend::graphics::glium::GliumGraphicsBackend; use smithay::wayland::compositor::{SubsurfaceRole, TraversalAction}; use smithay::wayland::compositor::roles::Role; use smithay::wayland_server::Display; use std::cell::Ref; use slog::Logger; use shell::{Buffer, MyCompositorToken, MyWindowMap}; #[derive(Copy, Clone)] struct Vertex { position: [f32; 2], tex_coords: [f32; 2], } implement_vertex!(Vertex, position, tex_coords); pub struct GliumDrawer { display: GliumGraphicsBackend, vertex_buffer: glium::VertexBuffer, index_buffer: glium::IndexBuffer, program: glium::Program, } impl GliumDrawer { pub fn borrow(&self) -> Ref { self.display.borrow() } } impl> + EGLGraphicsBackend + 'static> From for GliumDrawer { fn from(backend: T) -> GliumDrawer { let display = backend.into(); // 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, } } } impl GliumDrawer { pub fn texture_from_mem(&self, contents: &[u8], surface_dimensions: (u32, u32)) -> Texture2d { let image = glium::texture::RawImage2d { data: contents.into(), width: surface_dimensions.0, height: surface_dimensions.1, format: glium::texture::ClientFormat::U8U8U8U8, }; Texture2d::new(&self.display, image).unwrap() } pub fn texture_from_egl(&self, images: &EGLImages) -> Option { let format = match images.format { Format::RGB => UncompressedFloatFormat::U8U8U8, Format::RGBA => UncompressedFloatFormat::U8U8U8U8, _ => return None, }; let opengl_texture = Texture2d::empty_with_format( &self.display, format, MipmapsOption::NoMipmap, images.width, images.height, ).unwrap(); unsafe { images .bind_to_texture(0, opengl_texture.get_id()) .expect("Failed to bind to texture"); } Some(opengl_texture) } pub fn render_texture( &self, target: &mut glium::Frame, texture: &Texture2d, y_inverted: bool, surface_dimensions: (u32, u32), surface_location: (i32, i32), screen_size: (u32, u32), blending: glium::Blend, ) { let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32); let mut 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 mut y = 1.0 - 2.0 * (surface_location.1 as f32) / (screen_size.1 as f32); if y_inverted { yscale = -yscale; y -= surface_dimensions.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: texture, }; target .draw( &self.vertex_buffer, &self.index_buffer, &self.program, &uniforms, &glium::DrawParameters { blend: blending, ..Default::default() }, ) .unwrap(); } #[inline] pub fn draw(&self) -> Frame { self.display.draw() } } impl EGLWaylandExtensions for GliumDrawer { fn bind_wl_display(&self, display: &Display) -> EGLResult { self.display.bind_wl_display(display) } } impl GliumDrawer { pub fn draw_windows(&self, window_map: &MyWindowMap, compositor_token: MyCompositorToken, log: &Logger) { let mut frame = self.draw(); frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); // redraw the frame, in a simple but inneficient way { let screen_dimensions = self.borrow().get_framebuffer_dimensions(); window_map.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)| { // there is actually something to draw ! if attributes.user_data.texture.is_none() { let mut remove = false; match attributes.user_data.buffer { Some(Buffer::Egl { ref images }) => { match images.format { Format::RGB | Format::RGBA => { attributes.user_data.texture = self.texture_from_egl(&images); } _ => { // we don't handle the more complex formats here. attributes.user_data.texture = None; remove = true; } }; } Some(Buffer::Shm { ref data, ref size }) => { attributes.user_data.texture = Some(self.texture_from_mem(data, *size)); } _ => {} } if remove { attributes.user_data.buffer = None; } } if let Some(ref texture) = attributes.user_data.texture { if let Ok(subdata) = Role::::data(role) { x += subdata.location.0; y += subdata.location.1; } self.render_texture( &mut frame, texture, match *attributes.user_data.buffer.as_ref().unwrap() { Buffer::Egl { ref images } => images.y_inverted, Buffer::Shm { .. } => false, }, match *attributes.user_data.buffer.as_ref().unwrap() { Buffer::Egl { ref images } => (images.width, images.height), Buffer::Shm { ref size, .. } => *size, }, (x, y), screen_dimensions, ::glium::Blend { color: ::glium::BlendingFunction::Addition { source: ::glium::LinearBlendingFactor::One, destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha, }, alpha: ::glium::BlendingFunction::Addition { source: ::glium::LinearBlendingFactor::One, destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha, }, ..Default::default() }, ); TraversalAction::DoChildren((x, y)) } else { // we are not display, so our children are neither TraversalAction::SkipChildren } }, ) .unwrap(); } }); } if let Err(err) = frame.finish() { error!(log, "Error during rendering: {:?}", err); } } }