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) { if let Err(err) = draw_surface_tree(renderer, frame, &wl_surface, initial_place, log) {
result = Err(err); 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::{ shell::{
legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind}, legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState, ShellSurfaceKind},
xdg::{ xdg::{
xdg_shell_init, Configure, ShellState as XdgShellState, SurfaceCachedState, XdgRequest, xdg_shell_init, Configure, ShellState as XdgShellState, SurfaceCachedState,
XdgToplevelSurfaceRoleAttributes, XdgPopupSurfaceRoleAttributes, XdgRequest, XdgToplevelSurfaceRoleAttributes,
}, },
}, },
Serial, Serial,
@ -859,4 +859,22 @@ fn surface_commit(surface: &wl_surface::WlSurface, window_map: &RefCell<WindowMa
window_map.set_location(&toplevel, location); 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::cell::RefCell;
use std::sync::Mutex;
use smithay::{ use smithay::{
reexports::{wayland_protocols::xdg_shell::server::xdg_toplevel, wayland_server::protocol::wl_surface}, 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}, compositor::{with_states, with_surface_tree_downward, SubsurfaceCachedState, TraversalAction},
shell::{ shell::{
legacy::ShellSurface, legacy::ShellSurface,
xdg::{PopupSurface, SurfaceCachedState, ToplevelSurface}, xdg::{PopupSurface, SurfaceCachedState, ToplevelSurface, XdgPopupSurfaceRoleAttributes},
}, },
}, },
}; };
@ -77,7 +78,7 @@ pub enum PopupKind {
} }
impl PopupKind { impl PopupKind {
pub fn alive(&self) -> bool { fn alive(&self) -> bool {
match *self { match *self {
PopupKind::Xdg(ref t) => t.alive(), PopupKind::Xdg(ref t) => t.alive(),
} }
@ -88,6 +89,44 @@ impl PopupKind {
PopupKind::Xdg(ref t) => t.get_surface(), 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 { struct Window {
@ -294,6 +333,19 @@ impl WindowMap {
f(&w.toplevel, w.location, &w.bbox) 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) { pub fn refresh(&mut self) {
self.windows.retain(|w| w.toplevel.alive()); 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> { pub fn find_popup(&self, surface: &wl_surface::WlSurface) -> Option<PopupKind> {
self.popups.iter().find_map(|p| { self.popups.iter().find_map(|p| {
if p.popup if p.popup

View File

@ -11,6 +11,7 @@ use smithay::{
calloop::EventLoop, calloop::EventLoop,
wayland_server::{protocol::wl_output, Display}, wayland_server::{protocol::wl_output, Display},
}, },
utils::Rectangle,
wayland::{ wayland::{
output::{Mode, Output, PhysicalProperties}, output::{Mode, Output, PhysicalProperties},
seat::CursorImageStatus, seat::CursorImageStatus,
@ -120,12 +121,28 @@ pub fn run_winit(
{ {
let mut renderer = renderer.borrow_mut(); 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 let result = renderer
.render(|renderer, frame| { .render(|renderer, frame| {
frame.clear([0.8, 0.8, 0.9, 1.0])?; frame.clear([0.8, 0.8, 0.9, 1.0])?;
// draw the windows // 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; let (x, y) = state.pointer_location;
// draw the dnd icon if any // draw the dnd icon if any

View File

@ -1368,7 +1368,7 @@ impl PopupSurface {
/// Access the underlying `wl_surface` of this toplevel surface /// 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> { pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
if self.alive() { if self.alive() {
Some(&self.wl_surface) Some(&self.wl_surface)

View File

@ -18,7 +18,7 @@ use super::{
}; };
static XDG_TOPLEVEL_ROLE: &str = "xdg_toplevel"; 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( pub(crate) fn implement_wm_base(
shell: Main<xdg_wm_base::XdgWmBase>, shell: Main<xdg_wm_base::XdgWmBase>,