From 3a0c631edddfdbc32a4b47a95cfa5c1ed7f00c2b Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 29 Jun 2021 17:30:48 +0200 Subject: [PATCH] Rudimentary xdg_popup support --- anvil/src/drawing.rs | 10 +++++ anvil/src/shell.rs | 22 ++++++++++- anvil/src/window_map.rs | 57 ++++++++++++++++++++++++++- anvil/src/winit.rs | 19 ++++++++- src/wayland/shell/xdg/mod.rs | 2 +- src/wayland/shell/xdg/xdg_handlers.rs | 2 +- 6 files changed, 105 insertions(+), 7 deletions(-) diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs index 6e61fd4..6b0c693 100644 --- a/anvil/src/drawing.rs +++ b/anvil/src/drawing.rs @@ -210,6 +210,16 @@ where if let Err(err) = draw_surface_tree(renderer, frame, &wl_surface, initial_place, log) { result = Err(err); } + // furthermore, draw its popups + window_map.with_child_popups(&wl_surface, |popup| { + let location = popup.location(); + let draw_location = (initial_place.0 + location.0, initial_place.1 + location.1); + if let Some(wl_surface) = popup.get_surface() { + if let Err(err) = draw_surface_tree(renderer, frame, &wl_surface, draw_location, log) { + result = Err(err); + } + } + }); } }); diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 4a2d76e..e0c6c99 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -23,8 +23,8 @@ use smithay::{ shell::{ legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind}, xdg::{ - xdg_shell_init, Configure, ShellState as XdgShellState, SurfaceCachedState, XdgRequest, - XdgToplevelSurfaceRoleAttributes, + xdg_shell_init, Configure, ShellState as XdgShellState, SurfaceCachedState, + XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRoleAttributes, }, }, Serial, @@ -859,4 +859,22 @@ fn surface_commit(surface: &wl_surface::WlSurface, window_map: &RefCell>() + .unwrap() + .lock() + .unwrap() + .initial_configure_sent + }) + .unwrap(); + if !initial_configure_sent { + // TODO: properly recompute the geometry with the whole of positioner state + popup.send_configure(); + } + } } diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index 3e2fa7f..0be8d84 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::sync::Mutex; use smithay::{ reexports::{wayland_protocols::xdg_shell::server::xdg_toplevel, wayland_server::protocol::wl_surface}, @@ -7,7 +8,7 @@ use smithay::{ compositor::{with_states, with_surface_tree_downward, SubsurfaceCachedState, TraversalAction}, shell::{ legacy::ShellSurface, - xdg::{PopupSurface, SurfaceCachedState, ToplevelSurface}, + xdg::{PopupSurface, SurfaceCachedState, ToplevelSurface, XdgPopupSurfaceRoleAttributes}, }, }, }; @@ -77,7 +78,7 @@ pub enum PopupKind { } impl PopupKind { - pub fn alive(&self) -> bool { + fn alive(&self) -> bool { match *self { PopupKind::Xdg(ref t) => t.alive(), } @@ -88,6 +89,44 @@ impl PopupKind { PopupKind::Xdg(ref t) => t.get_surface(), } } + + fn parent(&self) -> Option { + let wl_surface = match self.get_surface() { + Some(s) => s, + None => return None, + }; + with_states(wl_surface, |states| { + states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap() + .parent + .clone() + }) + .ok() + .flatten() + } + + pub fn location(&self) -> (i32, i32) { + let wl_surface = match self.get_surface() { + Some(s) => s, + None => return (0, 0), + }; + let geometry = with_states(wl_surface, |states| { + states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap() + .current + .geometry + }) + .unwrap_or_default(); + (geometry.x, geometry.y) + } } struct Window { @@ -294,6 +333,19 @@ impl WindowMap { f(&w.toplevel, w.location, &w.bbox) } } + pub fn with_child_popups(&self, base: &wl_surface::WlSurface, mut f: Func) + where + Func: FnMut(&PopupKind), + { + for w in self + .popups + .iter() + .rev() + .filter(move |w| w.popup.parent().as_ref() == Some(base)) + { + f(&w.popup) + } + } pub fn refresh(&mut self) { self.windows.retain(|w| w.toplevel.alive()); @@ -329,6 +381,7 @@ impl WindowMap { }) } + /// Finds the popup corresponding to the given `WlSurface`. pub fn find_popup(&self, surface: &wl_surface::WlSurface) -> Option { self.popups.iter().find_map(|p| { if p.popup diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 16cfeb9..44c929b 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -11,6 +11,7 @@ use smithay::{ calloop::EventLoop, wayland_server::{protocol::wl_output, Display}, }, + utils::Rectangle, wayland::{ output::{Mode, Output, PhysicalProperties}, seat::CursorImageStatus, @@ -120,12 +121,28 @@ pub fn run_winit( { let mut renderer = renderer.borrow_mut(); + let output_rect = { + let (width, height) = renderer.window_size().physical_size.into(); + Rectangle { + x: 0, + y: 0, + width, + height, + } + }; + let result = renderer .render(|renderer, frame| { frame.clear([0.8, 0.8, 0.9, 1.0])?; // draw the windows - draw_windows(renderer, frame, &*state.window_map.borrow(), None, &log)?; + draw_windows( + renderer, + frame, + &*state.window_map.borrow(), + Some(output_rect), + &log, + )?; let (x, y) = state.pointer_location; // draw the dnd icon if any diff --git a/src/wayland/shell/xdg/mod.rs b/src/wayland/shell/xdg/mod.rs index b8ea983..79b99a2 100644 --- a/src/wayland/shell/xdg/mod.rs +++ b/src/wayland/shell/xdg/mod.rs @@ -1368,7 +1368,7 @@ impl PopupSurface { /// Access the underlying `wl_surface` of this toplevel surface /// - /// Returns `None` if the toplevel surface actually no longer exists. + /// Returns `None` if the popup surface actually no longer exists. pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> { if self.alive() { Some(&self.wl_surface) diff --git a/src/wayland/shell/xdg/xdg_handlers.rs b/src/wayland/shell/xdg/xdg_handlers.rs index 3663321..2741678 100644 --- a/src/wayland/shell/xdg/xdg_handlers.rs +++ b/src/wayland/shell/xdg/xdg_handlers.rs @@ -18,7 +18,7 @@ use super::{ }; static XDG_TOPLEVEL_ROLE: &str = "xdg_toplevel"; -static XDG_POPUP_ROLE: &str = "xdg_toplevel"; +static XDG_POPUP_ROLE: &str = "xdg_popup"; pub(crate) fn implement_wm_base( shell: Main,