Rudimentary xdg_popup support

This commit is contained in:
Victor Berger 2021-06-29 17:30:48 +02:00 committed by Victor Berger
parent 47bc37c67b
commit 3a0c631edd
6 changed files with 105 additions and 7 deletions

View File

@ -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);
}
}
});
}
});

View File

@ -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<WindowMa
window_map.set_location(&toplevel, location);
}
}
if let Some(popup) = window_map.find_popup(surface) {
let PopupKind::Xdg(ref popup) = popup;
let initial_configure_sent = with_states(surface, |states| {
states
.data_map
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
.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();
}
}
}

View File

@ -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<wl_surface::WlSurface> {
let wl_surface = match self.get_surface() {
Some(s) => s,
None => return None,
};
with_states(wl_surface, |states| {
states
.data_map
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
.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::<Mutex<XdgPopupSurfaceRoleAttributes>>()
.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<Func>(&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<PopupKind> {
self.popups.iter().find_map(|p| {
if p.popup

View File

@ -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

View File

@ -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)

View File

@ -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<xdg_wm_base::XdgWmBase>,