From 3a2badbfd9b1ab6d5ffc4263d6794ab376fd6534 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 4 Feb 2023 14:42:29 +0100 Subject: [PATCH] wlr-layer-shell support --- Cargo.lock | 14 ++++++ Cargo.toml | 1 + src/main.rs | 9 ++-- src/state.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 131 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95766ba..ea685d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1489,6 +1489,19 @@ dependencies = [ "wayland-scanner", ] +[[package]] +name = "wayland-protocols-wlr" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce991093320e4a6a525876e6b629ab24da25f9baef0c2e0080ad173ec89588a" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + [[package]] name = "wayland-scanner" version = "0.30.0" @@ -1630,6 +1643,7 @@ dependencies = [ "walkdir", "wayland-client", "wayland-protocols", + "wayland-protocols-wlr", "xkbcommon", "zbus", ] diff --git a/Cargo.toml b/Cargo.toml index 8bf8b4b..c218727 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ fontloading = ["fontconfig"] [dependencies] wayland-client = { version = "0.30", features = ["calloop", "log"] } wayland-protocols = { version = "0.30", features = ["client", "unstable", "staging"] } +wayland-protocols-wlr = { version = "0.1", features = ["client"] } nix = "0.26" memmap = "0.7" fontconfig = { version = "0.6.0", optional = true } diff --git a/src/main.rs b/src/main.rs index 09a8851..cb53f4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use chrono::{Duration, Local, Timelike}; use buffer::BufferView; use color::Color; use fonts::{FontMap, MaybeFontMap}; -use state::State; +use state::{State, OperationMode}; use event::{Event, Events}; use widgets::{ Audio, Backlight, Battery, Calendar, Clock, Date, Geometry, HorizontalLayout, IndexedLayout, @@ -157,7 +157,7 @@ fn main() { fm }).unwrap(); - let mut state = State::new(widgets, layout, MaybeFontMap::Waiting(font_thread), events); + let mut state = State::new(OperationMode::XdgToplevel, widgets, layout, MaybeFontMap::Waiting(font_thread), events); let mut damage = Vec::new(); for _ in 0..state.widgets.len() { @@ -199,8 +199,9 @@ fn main() { let surface = state.base_surface.as_ref().unwrap().clone(); if force { - // Other compositors probably need this. - //bufview.memset(Color::BLACK); + if state.needs_memset { + bufview.memset(Color::BLACK); + } for (idx, widget) in state.widgets.iter_mut().enumerate() { if force || widget.get_dirty() { let geo = widget.geometry(); diff --git a/src/state.rs b/src/state.rs index b6c1eb2..aa50038 100644 --- a/src/state.rs +++ b/src/state.rs @@ -18,6 +18,10 @@ use wayland_protocols::xdg::{ shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base}, }; +use wayland_protocols_wlr::layer_shell::v1::client::{ + zwlr_layer_shell_v1, zwlr_layer_surface_v1, +}; + use crate::{ buffer::BufferManager, fonts::{FontMap, MaybeFontMap}, @@ -26,11 +30,20 @@ use crate::{ event::{Event, Events, PointerEvent, PointerButton}, }; +pub enum OperationMode { + LayerSurface((u32, u32)), + XdgToplevel, +} + pub struct State { + mode: OperationMode, pub running: bool, pub dirty: bool, activated: bool, + pub needs_memset: bool, pub base_surface: Option, + layer_shell: Option, + layer_surface: Option, wm_base: Option, xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>, wl_shm: Option, @@ -48,6 +61,7 @@ pub struct State { impl State { pub fn new( + mode: OperationMode, widgets: Vec>, layout: Rc>, fonts: MaybeFontMap, @@ -57,7 +71,10 @@ impl State { running: true, dirty: true, activated: false, + needs_memset: true, base_surface: None, + layer_shell: None, + layer_surface: None, wm_base: None, xdg_surface: None, wl_shm: None, @@ -67,6 +84,7 @@ impl State { bufmgr: BufferManager::new(), keyboard: Keyboard::new(), pointer: None, + mode, widgets, fonts, events, @@ -97,6 +115,31 @@ impl State { _ => (), } } + + fn init_xdg_surface(&mut self, qh: &QueueHandle) { + let wm_base = self.wm_base.as_ref().unwrap(); + let base_surface = self.base_surface.as_ref().unwrap(); + + let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ()); + let toplevel = xdg_surface.get_toplevel(qh, ()); + toplevel.set_title("wldash".into()); + + base_surface.commit(); + + self.xdg_surface = Some((xdg_surface, toplevel)); + } + + fn init_layer_surface(&mut self, qh: &QueueHandle, size: (u32, u32)) { + let layer_shell = self.layer_shell.as_ref().unwrap(); + let base_surface = self.base_surface.as_ref().unwrap(); + + let layer_surface = layer_shell.get_layer_surface(base_surface, None, zwlr_layer_shell_v1::Layer::Top, "launcher".to_string(), qh, ()); + layer_surface.set_keyboard_interactivity(zwlr_layer_surface_v1::KeyboardInteractivity::Exclusive); + layer_surface.set_size(size.0, size.1); + + base_surface.commit(); + self.layer_surface = Some(layer_surface); + } } impl WidgetUpdater for State { @@ -126,23 +169,26 @@ impl Dispatch for State { match &interface[..] { "wl_compositor" => { let compositor = - registry.bind::(name, 4, qh, ()); + registry.bind::(name, 5, qh, ()); let surface = compositor.create_surface(qh, ()); state.base_surface = Some(surface); state.activate(qh); - if state.wm_base.is_some() && state.xdg_surface.is_none() { - state.init_xdg_surface(qh); + if state.wm_base.is_some() && state.xdg_surface.is_none() && state.layer_surface.is_none() { + match state.mode { + OperationMode::XdgToplevel => state.init_xdg_surface(qh), + OperationMode::LayerSurface(size) => state.init_layer_surface(qh, size), + } } } "wl_shm" => { state.wl_shm = Some(registry.bind::(name, 1, qh, ())); } "wl_seat" => { - registry.bind::(name, 1, qh, ()); + registry.bind::(name, 8, qh, ()); } - "xdg_wm_base" => { - let wm_base = registry.bind::(name, 1, qh, ()); + "xdg_wm_base" => if let OperationMode::XdgToplevel = state.mode { + let wm_base = registry.bind::(name, 2, qh, ()); state.wm_base = Some(wm_base); if state.base_surface.is_some() && state.xdg_surface.is_none() { @@ -155,6 +201,21 @@ impl Dispatch for State { ); state.activate(qh); } + "zwlr_layer_shell_v1" => if let OperationMode::LayerSurface(size) = state.mode { + state.layer_shell = Some( + registry.bind::(name, 4, qh, ()), + ); + if state.base_surface.is_some() && state.layer_surface.is_none() { + state.init_layer_surface(qh, size); + } + }, + "clay_control_v1" => { + // Buffers are zeroed by default, which is equivalent to zero alpha black. This + // is not a particularly good background, but memsetting is slow. On clay, + // toplevels that adhere to their requested dimensions only have black behind + // them, so we can save the memset in this case, speeding things up. + state.needs_memset = false; + } _ => {} } } @@ -226,18 +287,52 @@ impl Dispatch for State { } } -impl State { - fn init_xdg_surface(&mut self, qh: &QueueHandle) { - let wm_base = self.wm_base.as_ref().unwrap(); - let base_surface = self.base_surface.as_ref().unwrap(); +impl Dispatch for State { + fn event( + _: &mut Self, + _: &zwlr_layer_shell_v1::ZwlrLayerShellV1, + _: zwlr_layer_shell_v1::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) {} +} - let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ()); - let toplevel = xdg_surface.get_toplevel(qh, ()); - toplevel.set_title("wldash".into()); +impl Dispatch for State { + fn event( + state: &mut Self, + layer_surface: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, + event: zwlr_layer_surface_v1::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let zwlr_layer_surface_v1::Event::Configure { serial, width, height } = event { + if (width, height) == (0, 0) { + return; + } + layer_surface.ack_configure(serial); + state.configured = true; + state.dirty = true; - base_surface.commit(); - - self.xdg_surface = Some((xdg_surface, toplevel)); + if state.dimensions != (width as i32, height as i32) { + state.bufmgr.clear_buffers(); + state.dimensions = (width as i32, height as i32); + state.fonts.resolve(); + let fonts = state.fonts.unwrap(); + let layout = state.layout.clone(); + layout.geometry_update( + &mut fonts.borrow_mut(), + &Geometry { + x: 0, + y: 0, + width: width as u32, + height: height as u32, + }, + state, + ); + } + } } }