use smithay_client_toolkit::{ default_environment, environment::SimpleGlobal, new_default_environment, output::{with_output_info, OutputInfo}, reexports::{ calloop, client::protocol::{wl_output, wl_shm, wl_surface}, client::{Attached, Main}, protocols::wlr::unstable::layer_shell::v1::client::{ zwlr_layer_shell_v1, zwlr_layer_surface_v1, }, }, shm::AutoMemPool, WaylandSource, }; use std::cell::{Cell, RefCell}; use std::rc::Rc; default_environment!(Env, fields = [ layer_shell: SimpleGlobal, ], singles = [ zwlr_layer_shell_v1::ZwlrLayerShellV1 => layer_shell ], ); #[derive(PartialEq, Copy, Clone)] enum RenderEvent { Configure { width: u32, height: u32 }, Closed, } struct Surface { surface: wl_surface::WlSurface, layer_surface: Main, next_render_event: Rc>>, pool: AutoMemPool, dimensions: (u32, u32), } impl Surface { fn new( output: &wl_output::WlOutput, surface: wl_surface::WlSurface, layer_shell: &Attached, pool: AutoMemPool, ) -> Self { let layer_surface = layer_shell.get_layer_surface( &surface, Some(output), zwlr_layer_shell_v1::Layer::Overlay, "example".to_owned(), ); layer_surface.set_size(32, 32); // Anchor to the top left corner of the output layer_surface .set_anchor(zwlr_layer_surface_v1::Anchor::Top | zwlr_layer_surface_v1::Anchor::Left); let next_render_event = Rc::new(Cell::new(None::)); let next_render_event_handle = Rc::clone(&next_render_event); layer_surface.quick_assign(move |layer_surface, event, _| { match (event, next_render_event_handle.get()) { (zwlr_layer_surface_v1::Event::Closed, _) => { next_render_event_handle.set(Some(RenderEvent::Closed)); } (zwlr_layer_surface_v1::Event::Configure { serial, width, height }, next) if next != Some(RenderEvent::Closed) => { layer_surface.ack_configure(serial); next_render_event_handle.set(Some(RenderEvent::Configure { width, height })); } (_, _) => {} } }); // Commit so that the server will send a configure event surface.commit(); Self { surface, layer_surface, next_render_event, pool, dimensions: (0, 0) } } /// Handles any events that have occurred since the last call, redrawing if needed. /// Returns true if the surface should be dropped. fn handle_events(&mut self) -> bool { match self.next_render_event.take() { Some(RenderEvent::Closed) => true, Some(RenderEvent::Configure { width, height }) => { if self.dimensions != (width, height) { self.dimensions = (width, height); self.draw(); } false } None => false, } } fn draw(&mut self) { let stride = 4 * self.dimensions.0 as i32; let width = self.dimensions.0 as i32; let height = self.dimensions.1 as i32; // Note: unwrap() is only used here in the interest of simplicity of the example. // A "real" application should handle the case where both pools are still in use by the // compositor. let (canvas, buffer) = self.pool.buffer(width, height, stride, wl_shm::Format::Argb8888).unwrap(); for dst_pixel in canvas.chunks_exact_mut(4) { let pixel = 0xff00ff00u32.to_ne_bytes(); dst_pixel[0] = pixel[0]; dst_pixel[1] = pixel[1]; dst_pixel[2] = pixel[2]; dst_pixel[3] = pixel[3]; } // Attach the buffer to the surface and mark the entire surface as damaged self.surface.attach(Some(&buffer), 0, 0); self.surface.damage_buffer(0, 0, width as i32, height as i32); // Finally, commit the surface self.surface.commit(); } } impl Drop for Surface { fn drop(&mut self) { self.layer_surface.destroy(); self.surface.destroy(); } } fn main() { let (env, display, queue) = new_default_environment!(Env, fields = [layer_shell: SimpleGlobal::new(),]) .expect("Initial roundtrip failed!"); let surfaces = Rc::new(RefCell::new(Vec::new())); let layer_shell = env.require_global::(); let env_handle = env.clone(); let surfaces_handle = Rc::clone(&surfaces); let output_handler = move |output: wl_output::WlOutput, info: &OutputInfo| { if info.obsolete { // an output has been removed, release it surfaces_handle.borrow_mut().retain(|(i, _)| *i != info.id); output.release(); } else { // an output has been created, construct a surface for it let surface = env_handle.create_surface().detach(); let pool = env_handle.create_auto_pool().expect("Failed to create a memory pool!"); (*surfaces_handle.borrow_mut()) .push((info.id, Surface::new(&output, surface, &layer_shell.clone(), pool))); } }; // Process currently existing outputs for output in env.get_all_outputs() { if let Some(info) = with_output_info(&output, Clone::clone) { output_handler(output, &info); } } // Setup a listener for changes // The listener will live for as long as we keep this handle alive let _listner_handle = env.listen_for_outputs(move |output, info, _| output_handler(output, info)); let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap(); WaylandSource::new(queue).quick_insert(event_loop.handle()).unwrap(); loop { { // Using a new scope so that `surfaces` reference gets dropped surfaces.borrow_mut().retain_mut(|surface| !surface.1.handle_events()); } display.flush().unwrap(); event_loop.dispatch(None, &mut ()).unwrap(); } }