From 6ae2af4e68919937c645fbbe1ae464cdd4073ffa Mon Sep 17 00:00:00 2001 From: Linus Johansen Date: Mon, 11 Oct 2021 23:20:33 +0200 Subject: [PATCH 1/3] added wlr shell abstractions --- src/shell/mod.rs | 1 + src/shell/wlr.rs | 150 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 src/shell/wlr.rs diff --git a/src/shell/mod.rs b/src/shell/mod.rs index fdc0e73ce..086ad025f 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -23,6 +23,7 @@ use wayland_protocols::{ use crate::environment::{Environment, GlobalHandler}; mod wl; +pub mod wlr; mod xdg; mod zxdg; diff --git a/src/shell/wlr.rs b/src/shell/wlr.rs new file mode 100644 index 000000000..69f5b0cdd --- /dev/null +++ b/src/shell/wlr.rs @@ -0,0 +1,150 @@ +/// Wlr shell abstraction +use std::cell::Cell; +use std::rc::Rc; + +use wayland_client::{ + protocol::{wl_output, wl_surface}, + Attached, Main, +}; + +use wayland_protocols::wlr::unstable::layer_shell::v1::client::{ + zwlr_layer_shell_v1, zwlr_layer_surface_v1, +}; + +#[derive(PartialEq, Copy, Clone)] +enum RenderEvent { + Configure { width: u32, height: u32 }, + Closed, +} + +/// Just a tiny abstraction making layers easier. A layer specifies a z depth for where something is drawn. A normal xdg shell is normally drawn somewhere in between Bottom and Top +#[derive(PartialEq, Copy, Clone)] +pub enum Layer { + /// All the way in the back, a example usecase is for backgrounds + Background, + + /// In front of background but behind normal xdg shells + Bottom, + + /// Behind overlay but in front normal xdg shells + Top, + + /// All the way in the front, overiding what other applications are trying to draw. + Overlay, +} + +/// Just a tiny abstraction making anchors easier. Anchor says something about where on the screen the shell is rendered + +#[derive(PartialEq, Copy, Clone)] +pub enum Anchor { + /// Top of the screen + Top, + + /// Bottom of the screen + Bottom, + + /// Left side of the screen + Left, + + /// Right side of the screen + Right, +} + +impl Anchor { + pub(crate) fn too_raw(&self) -> zwlr_layer_surface_v1::Anchor { + match *self { + Anchor::Top => zwlr_layer_surface_v1::Anchor::Top, + Anchor::Bottom => zwlr_layer_surface_v1::Anchor::Bottom, + Anchor::Left => zwlr_layer_surface_v1::Anchor::Left, + Anchor::Right => zwlr_layer_surface_v1::Anchor::Right, + } + } +} + +impl Layer { + pub(crate) fn too_raw(&self) -> zwlr_layer_shell_v1::Layer { + match *self { + Layer::Background => zwlr_layer_shell_v1::Layer::Background, + Layer::Bottom => zwlr_layer_shell_v1::Layer::Bottom, + Layer::Top => zwlr_layer_shell_v1::Layer::Top, + Layer::Overlay => zwlr_layer_shell_v1::Layer::Overlay, + } + } +} + +/// A struct representing the wlr shell + +pub struct WlrShell { + /// The raw wl_surface + pub surface: wl_surface::WlSurface, + + pub(crate) layer_surface: Main, + + /// On what layer it should be positioned + pub layer: Layer, + + /// Where on the screen it is positioned + pub anchor: Anchor, + + /// The dimensions of the wlr surface + pub dimensions: (u32, u32), +} + +impl WlrShell { + /// Create a new wlr shell + + pub fn new( + output: &wl_output::WlOutput, + surface: wl_surface::WlSurface, + layer_shell: &Attached, + layer: Layer, + anchor: Anchor, + dimensions: (u32, u32), + ) -> Self { + let layer_surface = layer_shell.get_layer_surface( + &surface, + Some(output), + layer.too_raw(), + "example".to_owned(), + ); + + layer_surface.set_size(dimensions.0, dimensions.1); + // Anchor to the top left corner of the output + layer_surface.set_anchor(anchor.too_raw()); + + 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, + layer, + anchor, + dimensions: (dimensions.0 as u32, dimensions.1 as u32), + } + } +} + +impl Drop for WlrShell { + fn drop(&mut self) { + self.layer_surface.destroy(); + self.surface.destroy(); + } +} From aae0049ba4c238b386b6c50b8e0d3e2202888c17 Mon Sep 17 00:00:00 2001 From: Linus Johansen Date: Mon, 11 Oct 2021 23:47:50 +0200 Subject: [PATCH 2/3] added a tiny abstraction for creating wlr_shells --- examples/layer_shell.rs | 2 + examples/layer_shell_wlr_module.rs | 150 +++++++++++++++++++++++++++++ src/shell/wlr.rs | 6 +- 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 examples/layer_shell_wlr_module.rs diff --git a/examples/layer_shell.rs b/examples/layer_shell.rs index b8e8bf636..ba3bef8cc 100644 --- a/examples/layer_shell.rs +++ b/examples/layer_shell.rs @@ -1,3 +1,5 @@ +// Abstraction in later_shell_wlr_module + use smithay_client_toolkit::{ default_environment, environment::SimpleGlobal, diff --git a/examples/layer_shell_wlr_module.rs b/examples/layer_shell_wlr_module.rs new file mode 100644 index 000000000..de18fe3d0 --- /dev/null +++ b/examples/layer_shell_wlr_module.rs @@ -0,0 +1,150 @@ +use smithay_client_toolkit::{ + default_environment, + environment::SimpleGlobal, + new_default_environment, + output::{with_output_info, OutputInfo}, + reexports::{ + calloop, + client::protocol::{wl_output, wl_shm}, + protocols::wlr::unstable::layer_shell::v1::client::zwlr_layer_shell_v1, + }, + shell::wlr, + shm::AutoMemPool, + WaylandSource, +}; + +use std::cell::RefCell; +use std::rc::Rc; + +default_environment!(Env, + fields = [ + layer_shell: SimpleGlobal, + ], + singles = [ + zwlr_layer_shell_v1::ZwlrLayerShellV1 => layer_shell + ], +); + +struct Surface { + shell: wlr::WlrShell, + pool: AutoMemPool, +} + +impl Surface { + fn new(shell: wlr::WlrShell, pool: AutoMemPool) -> Self { + shell.surface.commit(); + + Self { pool, shell } + } + + /// 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.shell.render_event.take() { + Some(wlr::RenderEvent::Closed) => true, + Some(wlr::RenderEvent::Configure { width, height }) => { + self.shell.dimensions = (width, height); + self.draw(); + false + } + None => false, + } + } + + fn draw(&mut self) { + let stride = 4 * self.shell.dimensions.0 as i32; + let width = self.shell.dimensions.0 as i32; + let height = self.shell.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 = 0xaa2246u32.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.shell.surface.attach(Some(&buffer), 0, 0); + self.shell.surface.damage_buffer(0, 0, width as i32, height as i32); + + // Finally, commit the surface + self.shell.surface.commit(); + } +} + +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!"); + + let wlr_shell = wlr::WlrShell::new( + &output, + surface, + &layer_shell.clone(), + wlr::Layer::Background, + wlr::Anchor::Top, + (info.modes[0].dimensions.0 as u32, info.modes[0].dimensions.1 as u32), + ); + + (*surfaces_handle.borrow_mut()).push((info.id, Surface::new(wlr_shell, 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 { + // This is ugly, let's hope that some version of drain_filter() gets stabilized soon + // https://github.com/rust-lang/rust/issues/43244 + { + let mut surfaces = surfaces.borrow_mut(); + let mut i = 0; + while i != surfaces.len() { + if surfaces[i].1.handle_events() { + surfaces.remove(i); + } else { + i += 1; + } + } + } + + display.flush().unwrap(); + event_loop.dispatch(None, &mut ()).unwrap(); + } +} diff --git a/src/shell/wlr.rs b/src/shell/wlr.rs index 69f5b0cdd..7ec981cc5 100644 --- a/src/shell/wlr.rs +++ b/src/shell/wlr.rs @@ -12,7 +12,7 @@ use wayland_protocols::wlr::unstable::layer_shell::v1::client::{ }; #[derive(PartialEq, Copy, Clone)] -enum RenderEvent { +pub enum RenderEvent { Configure { width: u32, height: u32 }, Closed, } @@ -88,6 +88,9 @@ pub struct WlrShell { /// The dimensions of the wlr surface pub dimensions: (u32, u32), + + /// The next render event + pub render_event: Rc>>, } impl WlrShell { @@ -137,6 +140,7 @@ impl WlrShell { layer_surface, layer, anchor, + render_event: next_render_event, dimensions: (dimensions.0 as u32, dimensions.1 as u32), } } From befb7182d39aba8d62404ba8297c5422c64d1902 Mon Sep 17 00:00:00 2001 From: Linus Johansen Date: Tue, 12 Oct 2021 00:46:26 +0200 Subject: [PATCH 3/3] satisfied the requests in the PR --- examples/layer_shell_wlr_module.rs | 38 ++++++------- src/shell/{wlr.rs => layer.rs} | 88 ++++++++---------------------- src/shell/mod.rs | 2 +- 3 files changed, 42 insertions(+), 86 deletions(-) rename src/shell/{wlr.rs => layer.rs} (53%) diff --git a/examples/layer_shell_wlr_module.rs b/examples/layer_shell_wlr_module.rs index de18fe3d0..04c404470 100644 --- a/examples/layer_shell_wlr_module.rs +++ b/examples/layer_shell_wlr_module.rs @@ -8,7 +8,7 @@ use smithay_client_toolkit::{ client::protocol::{wl_output, wl_shm}, protocols::wlr::unstable::layer_shell::v1::client::zwlr_layer_shell_v1, }, - shell::wlr, + shell::layer, shm::AutoMemPool, WaylandSource, }; @@ -26,24 +26,24 @@ default_environment!(Env, ); struct Surface { - shell: wlr::WlrShell, + surface: layer::LayerSurface, pool: AutoMemPool, } impl Surface { - fn new(shell: wlr::WlrShell, pool: AutoMemPool) -> Self { - shell.surface.commit(); + fn new(surface: layer::LayerSurface, pool: AutoMemPool) -> Self { + surface.surface.commit(); - Self { pool, shell } + Self { pool, surface } } /// 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.shell.render_event.take() { - Some(wlr::RenderEvent::Closed) => true, - Some(wlr::RenderEvent::Configure { width, height }) => { - self.shell.dimensions = (width, height); + match self.surface.render_event.take() { + Some(layer::RenderEvent::Closed) => true, + Some(layer::RenderEvent::Configure { width, height }) => { + self.surface.dimensions = (width, height); self.draw(); false } @@ -52,9 +52,9 @@ impl Surface { } fn draw(&mut self) { - let stride = 4 * self.shell.dimensions.0 as i32; - let width = self.shell.dimensions.0 as i32; - let height = self.shell.dimensions.1 as i32; + let stride = 4 * self.surface.dimensions.0 as i32; + let width = self.surface.dimensions.0 as i32; + let height = self.surface.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 @@ -63,7 +63,7 @@ impl Surface { self.pool.buffer(width, height, stride, wl_shm::Format::Argb8888).unwrap(); for dst_pixel in canvas.chunks_exact_mut(4) { - let pixel = 0xaa2246u32.to_ne_bytes(); + let pixel = 0x24021bu32.to_ne_bytes(); dst_pixel[0] = pixel[0]; dst_pixel[1] = pixel[1]; dst_pixel[2] = pixel[2]; @@ -71,11 +71,11 @@ impl Surface { } // Attach the buffer to the surface and mark the entire surface as damaged - self.shell.surface.attach(Some(&buffer), 0, 0); - self.shell.surface.damage_buffer(0, 0, width as i32, height as i32); + self.surface.surface.attach(Some(&buffer), 0, 0); + self.surface.surface.damage_buffer(0, 0, width as i32, height as i32); // Finally, commit the surface - self.shell.surface.commit(); + self.surface.surface.commit(); } } @@ -100,12 +100,12 @@ fn main() { let surface = env_handle.create_surface().detach(); let pool = env_handle.create_auto_pool().expect("Failed to create a memory pool!"); - let wlr_shell = wlr::WlrShell::new( + let wlr_shell = layer::LayerSurface::new( &output, surface, &layer_shell.clone(), - wlr::Layer::Background, - wlr::Anchor::Top, + layer::Layer::Background, + layer::Anchor::Top, (info.modes[0].dimensions.0 as u32, info.modes[0].dimensions.1 as u32), ); diff --git a/src/shell/wlr.rs b/src/shell/layer.rs similarity index 53% rename from src/shell/wlr.rs rename to src/shell/layer.rs index 7ec981cc5..3d97ce2d3 100644 --- a/src/shell/wlr.rs +++ b/src/shell/layer.rs @@ -1,4 +1,4 @@ -/// Wlr shell abstraction +/// WLR layer abstraction use std::cell::Cell; use std::rc::Rc; @@ -11,70 +11,30 @@ use wayland_protocols::wlr::unstable::layer_shell::v1::client::{ zwlr_layer_shell_v1, zwlr_layer_surface_v1, }; -#[derive(PartialEq, Copy, Clone)] -pub enum RenderEvent { - Configure { width: u32, height: u32 }, - Closed, -} - -/// Just a tiny abstraction making layers easier. A layer specifies a z depth for where something is drawn. A normal xdg shell is normally drawn somewhere in between Bottom and Top -#[derive(PartialEq, Copy, Clone)] -pub enum Layer { - /// All the way in the back, a example usecase is for backgrounds - Background, - - /// In front of background but behind normal xdg shells - Bottom, - - /// Behind overlay but in front normal xdg shells - Top, - - /// All the way in the front, overiding what other applications are trying to draw. - Overlay, -} +pub use wayland_protocols::wlr::unstable::layer_shell::v1::client::{ + zwlr_layer_shell_v1::Layer, zwlr_layer_surface_v1::Anchor, +}; -/// Just a tiny abstraction making anchors easier. Anchor says something about where on the screen the shell is rendered +/// Render event #[derive(PartialEq, Copy, Clone)] -pub enum Anchor { - /// Top of the screen - Top, - - /// Bottom of the screen - Bottom, - - /// Left side of the screen - Left, +pub enum RenderEvent { + /// Surface wants a reconfiguration/configuration + Configure { + /// The new width of the surface + width: u32, - /// Right side of the screen - Right, -} + /// The new height of the surface + height: u32, + }, -impl Anchor { - pub(crate) fn too_raw(&self) -> zwlr_layer_surface_v1::Anchor { - match *self { - Anchor::Top => zwlr_layer_surface_v1::Anchor::Top, - Anchor::Bottom => zwlr_layer_surface_v1::Anchor::Bottom, - Anchor::Left => zwlr_layer_surface_v1::Anchor::Left, - Anchor::Right => zwlr_layer_surface_v1::Anchor::Right, - } - } -} - -impl Layer { - pub(crate) fn too_raw(&self) -> zwlr_layer_shell_v1::Layer { - match *self { - Layer::Background => zwlr_layer_shell_v1::Layer::Background, - Layer::Bottom => zwlr_layer_shell_v1::Layer::Bottom, - Layer::Top => zwlr_layer_shell_v1::Layer::Top, - Layer::Overlay => zwlr_layer_shell_v1::Layer::Overlay, - } - } + /// Surface has closed and wants to be closed here also. + Closed, } -/// A struct representing the wlr shell +/// A struct representing the layer surface (wlr) -pub struct WlrShell { +pub struct LayerSurface { /// The raw wl_surface pub surface: wl_surface::WlSurface, @@ -93,7 +53,7 @@ pub struct WlrShell { pub render_event: Rc>>, } -impl WlrShell { +impl LayerSurface { /// Create a new wlr shell pub fn new( @@ -104,16 +64,12 @@ impl WlrShell { anchor: Anchor, dimensions: (u32, u32), ) -> Self { - let layer_surface = layer_shell.get_layer_surface( - &surface, - Some(output), - layer.too_raw(), - "example".to_owned(), - ); + let layer_surface = + layer_shell.get_layer_surface(&surface, Some(output), layer, "example".to_owned()); layer_surface.set_size(dimensions.0, dimensions.1); // Anchor to the top left corner of the output - layer_surface.set_anchor(anchor.too_raw()); + layer_surface.set_anchor(anchor); let next_render_event = Rc::new(Cell::new(None::)); let next_render_event_handle = Rc::clone(&next_render_event); @@ -146,7 +102,7 @@ impl WlrShell { } } -impl Drop for WlrShell { +impl Drop for LayerSurface { fn drop(&mut self) { self.layer_surface.destroy(); self.surface.destroy(); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 086ad025f..73a86ec9e 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -22,8 +22,8 @@ use wayland_protocols::{ use crate::environment::{Environment, GlobalHandler}; +pub mod layer; mod wl; -pub mod wlr; mod xdg; mod zxdg;