example: actually draw some windows!

This commit is contained in:
Victor Berger 2017-06-13 16:52:43 +02:00
parent bffc02c5f1
commit 19634f30ed
4 changed files with 326 additions and 18 deletions

116
examples/helpers/glium.rs Normal file
View File

@ -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<Vertex>,
index_buffer: glium::IndexBuffer<u16>,
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();
}
}

5
examples/helpers/mod.rs Normal file
View File

@ -0,0 +1,5 @@
mod shell;
mod glium;
pub use self::glium::GliumDrawer;
pub use self::shell::WlShellStubHandler;

97
examples/helpers/shell.rs Normal file
View File

@ -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<U, H> {
my_id: Option<usize>,
token: CompositorToken<U, H>,
surfaces: Vec<(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)>,
}
impl<U, H> WlShellStubHandler<U, H> {
pub fn new(compositor_token: CompositorToken<U, H>) -> WlShellStubHandler<U, H> {
WlShellStubHandler {
my_id: None,
token: compositor_token,
surfaces: Vec::new(),
}
}
pub fn surfaces(&self) -> &[(wl_shell_surface::WlShellSurface, wl_surface::WlSurface)] {
&self.surfaces
}
}
impl<U, H> Init for WlShellStubHandler<U, H> {
fn init(&mut self, evqh: &mut EventLoopHandle, index: usize) {
self.my_id = Some(index)
}
}
impl<U, H> GlobalHandler<wl_shell::WlShell> for WlShellStubHandler<U, H>
where U: Send + 'static,
H: CompositorHandler<U> + 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<U, H> wl_shell::Handler for WlShellStubHandler<U, H>
where U: Send + 'static,
H: CompositorHandler<U> + 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<U, H> ::wayland_server::Handler<wl_shell::WlShell> for WlShellStubHandler<U, H>
where U: Send + 'static,
H: CompositorHandler<U> + 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<(), ()> {
<WlShellStubHandler<U,H> as ::wayland_server::protocol::wl_shell::Handler>::__message(self, evq, client, resource, opcode, args)
}
}
impl<U, H> wl_shell_surface::Handler for WlShellStubHandler<U, H>
where U: Send + 'static,
H: CompositorHandler<U> + Send + 'static
{
}
unsafe impl<U, H> ::wayland_server::Handler<wl_shell_surface::WlShellSurface> for WlShellStubHandler<U, H>
where U: Send + 'static,
H: CompositorHandler<U> + 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<(), ()> {
<WlShellStubHandler<U,H> as ::wayland_server::protocol::wl_shell_surface::Handler>::__message(self, evq, client, resource, opcode, args)
}
}

View File

@ -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<u8>, (u32, u32))>
}
impl compositor::Handler<SurfaceData> for SurfaceHandler {
fn commit(&mut self, evlh: &mut EventLoopHandle, client: &Client, surface: &wl_surface::WlSurface, token: CompositorToken<SurfaceData, SurfaceHandler>) {
// 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::<wl_shm::WlShm, ShmGlobal>(shm_handler_id, 1);
// retreive the token
let shm_token = {
let state = event_loop.state();
state
.get_handler::<ShmGlobal>(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::<SurfaceData, _>::new(SurfaceHandler { shm_token: shm_token.clone() }, log.clone()));
// register it to handle wl_compositor and wl_subcompositor
event_loop.register_global::<wl_compositor::WlCompositor, CompositorHandler<(),SurfaceHandler>>(compositor_handler_id, 4);
event_loop.register_global::<wl_subcompositor::WlSubcompositor, CompositorHandler<(),SurfaceHandler>>(compositor_handler_id, 1);
event_loop.register_global::<wl_compositor::WlCompositor, CompositorHandler<SurfaceData,SurfaceHandler>>(compositor_handler_id, 4);
event_loop.register_global::<wl_subcompositor::WlSubcompositor, CompositorHandler<SurfaceData,SurfaceHandler>>(compositor_handler_id, 1);
// retrieve the tokens
let compositor_token = {
let state = event_loop.state();
state
.get_handler::<CompositorHandler<SurfaceData, SurfaceHandler>>(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::<ShmGlobal>(shm_handler_id).get_token(),
state
.get_handler::<CompositorHandler<(), SurfaceHandler>>(compositor_handler_id)
.get_token())
};
let shell_handler_id =
event_loop.add_handler_with_init(WlShellStubHandler::new(compositor_token.clone()));
event_loop.register_global::<wl_shell::WlShell, WlShellStubHandler<SurfaceData, SurfaceHandler>>(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::<WlShellStubHandler<SurfaceData, SurfaceHandler>>(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();
}
}