smithay/src/desktop/popup.rs

263 lines
7.5 KiB
Rust

use crate::{
utils::{DeadResource, Logical, Point},
wayland::{
compositor::{get_role, with_states},
shell::xdg::{PopupSurface, XdgPopupSurfaceRoleAttributes, XDG_POPUP_ROLE},
},
};
use std::sync::{Arc, Mutex};
use wayland_server::protocol::wl_surface::WlSurface;
#[derive(Debug)]
pub struct PopupManager {
unmapped_popups: Vec<PopupKind>,
popup_trees: Vec<PopupTree>,
logger: ::slog::Logger,
}
impl PopupManager {
pub fn new<L: Into<Option<::slog::Logger>>>(logger: L) -> Self {
PopupManager {
unmapped_popups: Vec::new(),
popup_trees: Vec::new(),
logger: crate::slog_or_fallback(logger),
}
}
pub fn track_popup(&mut self, kind: PopupKind) -> Result<(), DeadResource> {
if kind.parent().is_some() {
self.add_popup(kind)
} else {
slog::trace!(self.logger, "Adding unmapped popups: {:?}", kind);
self.unmapped_popups.push(kind);
Ok(())
}
}
pub fn commit(&mut self, surface: &WlSurface) {
if get_role(surface) == Some(XDG_POPUP_ROLE) {
if let Some(i) = self
.unmapped_popups
.iter()
.enumerate()
.find(|(_, p)| p.get_surface() == Some(surface))
.map(|(i, _)| i)
{
slog::trace!(self.logger, "Popup got mapped");
let popup = self.unmapped_popups.swap_remove(i);
// at this point the popup must have a parent,
// or it would have raised a protocol error
let _ = self.add_popup(popup);
}
}
}
fn add_popup(&mut self, popup: PopupKind) -> Result<(), DeadResource> {
let mut parent = popup.parent().unwrap();
while get_role(&parent) == Some(XDG_POPUP_ROLE) {
parent = with_states(&parent, |states| {
states
.data_map
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap()
.parent
.as_ref()
.cloned()
.unwrap()
})?;
}
with_states(&parent, |states| {
let tree = PopupTree::default();
if states.data_map.insert_if_missing(|| tree.clone()) {
self.popup_trees.push(tree);
};
let tree = states.data_map.get::<PopupTree>().unwrap();
if !tree.alive() {
// if it previously had no popups, we likely removed it from our list already
self.popup_trees.push(tree.clone());
}
slog::trace!(self.logger, "Adding popup {:?} to parent {:?}", popup, parent);
tree.insert(popup);
})
}
pub fn find_popup(&self, surface: &WlSurface) -> Option<PopupKind> {
self.unmapped_popups
.iter()
.find(|p| p.get_surface() == Some(surface))
.cloned()
.or_else(|| {
self.popup_trees
.iter()
.map(|tree| tree.iter_popups())
.flatten()
.find(|(p, _)| p.get_surface() == Some(surface))
.map(|(p, _)| p)
})
}
pub fn popups_for_surface(
surface: &WlSurface,
) -> Result<impl Iterator<Item = (PopupKind, Point<i32, Logical>)>, DeadResource> {
with_states(surface, |states| {
states
.data_map
.get::<PopupTree>()
.map(|x| x.iter_popups())
.into_iter()
.flatten()
})
}
pub fn cleanup(&mut self) {
// retain_mut is sadly still unstable
self.popup_trees.iter_mut().for_each(|tree| tree.cleanup());
self.popup_trees.retain(|tree| tree.alive());
self.unmapped_popups.retain(|surf| surf.alive());
}
}
#[derive(Debug, Default, Clone)]
struct PopupTree(Arc<Mutex<Vec<PopupNode>>>);
#[derive(Debug, Clone)]
struct PopupNode {
surface: PopupKind,
children: Vec<PopupNode>,
}
impl PopupTree {
fn iter_popups(&self) -> impl Iterator<Item = (PopupKind, Point<i32, Logical>)> {
self.0
.lock()
.unwrap()
.iter()
.map(|n| n.iter_popups_relative_to((0, 0)).map(|(p, l)| (p.clone(), l)))
.flatten()
.collect::<Vec<_>>()
.into_iter()
}
fn insert(&self, popup: PopupKind) {
let children = &mut *self.0.lock().unwrap();
for child in children.iter_mut() {
if child.insert(popup.clone()) {
return;
}
}
children.push(PopupNode::new(popup));
}
fn cleanup(&mut self) {
let mut children = self.0.lock().unwrap();
for child in children.iter_mut() {
child.cleanup();
}
children.retain(|n| n.surface.alive());
}
fn alive(&self) -> bool {
!self.0.lock().unwrap().is_empty()
}
}
impl PopupNode {
fn new(surface: PopupKind) -> Self {
PopupNode {
surface,
children: Vec::new(),
}
}
fn iter_popups_relative_to<P: Into<Point<i32, Logical>>>(
&self,
loc: P,
) -> impl Iterator<Item = (&PopupKind, Point<i32, Logical>)> {
let relative_to = loc.into() + self.surface.location();
std::iter::once((&self.surface, relative_to)).chain(
self.children
.iter()
.map(move |x| {
Box::new(x.iter_popups_relative_to(relative_to))
as Box<dyn Iterator<Item = (&PopupKind, Point<i32, Logical>)>>
})
.flatten(),
)
}
fn insert(&mut self, popup: PopupKind) -> bool {
let parent = popup.parent().unwrap();
if self.surface.get_surface() == Some(&parent) {
self.children.push(PopupNode::new(popup));
true
} else {
for child in &mut self.children {
if child.insert(popup.clone()) {
return true;
}
}
false
}
}
fn cleanup(&mut self) {
for child in &mut self.children {
child.cleanup();
}
self.children.retain(|n| n.surface.alive());
}
}
#[derive(Debug, Clone)]
pub enum PopupKind {
Xdg(PopupSurface),
}
impl PopupKind {
fn alive(&self) -> bool {
match *self {
PopupKind::Xdg(ref t) => t.alive(),
}
}
pub fn get_surface(&self) -> Option<&WlSurface> {
match *self {
PopupKind::Xdg(ref t) => t.get_surface(),
}
}
fn parent(&self) -> Option<WlSurface> {
match *self {
PopupKind::Xdg(ref t) => t.get_parent_surface(),
}
}
fn location(&self) -> Point<i32, Logical> {
let wl_surface = match self.get_surface() {
Some(s) => s,
None => return (0, 0).into(),
};
with_states(wl_surface, |states| {
states
.data_map
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap()
.current
.geometry
})
.unwrap_or_default()
.loc
}
}
impl From<PopupSurface> for PopupKind {
fn from(p: PopupSurface) -> PopupKind {
PopupKind::Xdg(p)
}
}