Merge pull request #475 from Smithay/fix/popup_output_events

Send popups and layers output enter/leave events as well
This commit is contained in:
Victoria Brekenfeld 2022-01-20 12:57:50 +01:00 committed by GitHub
commit e019b4fa9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 188 additions and 100 deletions

View File

@ -3,7 +3,7 @@ use crate::{
desktop::{utils::*, PopupManager, Space}, desktop::{utils::*, PopupManager, Space},
utils::{user_data::UserDataMap, Logical, Point, Rectangle}, utils::{user_data::UserDataMap, Logical, Point, Rectangle},
wayland::{ wayland::{
compositor::with_states, compositor::{with_states, with_surface_tree_downward, TraversalAction},
output::{Inner as OutputInner, Output}, output::{Inner as OutputInner, Output},
shell::wlr_layer::{ shell::wlr_layer::{
Anchor, ExclusiveZone, KeyboardInteractivity, Layer as WlrLayer, LayerSurface as WlrLayerSurface, Anchor, ExclusiveZone, KeyboardInteractivity, Layer as WlrLayer, LayerSurface as WlrLayerSurface,
@ -29,6 +29,9 @@ pub struct LayerMap {
layers: IndexSet<LayerSurface>, layers: IndexSet<LayerSurface>,
output: Weak<(Mutex<OutputInner>, wayland_server::UserDataMap)>, output: Weak<(Mutex<OutputInner>, wayland_server::UserDataMap)>,
zone: Rectangle<i32, Logical>, zone: Rectangle<i32, Logical>,
// surfaces for tracking enter and leave events
surfaces: Vec<WlSurface>,
logger: ::slog::Logger,
} }
/// Retrieve a [`LayerMap`] for a given [`Output`]. /// Retrieve a [`LayerMap`] for a given [`Output`].
@ -53,6 +56,10 @@ pub fn layer_map_for_output(o: &Output) -> RefMut<'_, LayerMap> {
.map(|mode| mode.size.to_logical(o.current_scale())) .map(|mode| mode.size.to_logical(o.current_scale()))
.unwrap_or_else(|| (0, 0).into()), .unwrap_or_else(|| (0, 0).into()),
), ),
surfaces: Vec::new(),
logger: (*o.inner.0.lock().unwrap())
.log
.new(slog::o!("smithay_module" => "layer_map")),
}) })
}); });
userdata.get::<RefCell<LayerMap>>().unwrap().borrow_mut() userdata.get::<RefCell<LayerMap>>().unwrap().borrow_mut()
@ -90,6 +97,34 @@ impl LayerMap {
let _ = layer.user_data().get::<LayerUserdata>().take(); let _ = layer.user_data().get::<LayerUserdata>().take();
self.arrange(); self.arrange();
} }
if let (Some(output), Some(surface)) = (self.output(), layer.get_surface()) {
with_surface_tree_downward(
surface,
(),
|_, _, _| TraversalAction::DoChildren(()),
|wl_surface, _, _| {
output_leave(&output, &mut self.surfaces, wl_surface, &self.logger);
},
|_, _, _| true,
);
for (popup, _) in PopupManager::popups_for_surface(surface)
.ok()
.into_iter()
.flatten()
{
if let Some(surface) = popup.get_surface() {
with_surface_tree_downward(
surface,
(),
|_, _, _| TraversalAction::DoChildren(()),
|wl_surface, _, _| {
output_leave(&output, &mut self.surfaces, wl_surface, &self.logger);
},
|_, _, _| true,
)
}
}
}
} }
/// Return the area of this output, that is not exclusive to any [`LayerSurface`]s. /// Return the area of this output, that is not exclusive to any [`LayerSurface`]s.
@ -160,11 +195,7 @@ impl LayerMap {
.unwrap_or_else(|| (0, 0).into()), .unwrap_or_else(|| (0, 0).into()),
); );
let mut zone = output_rect; let mut zone = output_rect;
slog::debug!( slog::trace!(self.logger, "Arranging layers into {:?}", output_rect.size);
crate::slog_or_fallback(None),
"Arranging layers into {:?}",
output_rect.size
);
for layer in self.layers.iter() { for layer in self.layers.iter() {
let surface = if let Some(surface) = layer.get_surface() { let surface = if let Some(surface) = layer.get_surface() {
@ -173,6 +204,35 @@ impl LayerMap {
continue; continue;
}; };
let logger_ref = &self.logger;
let surfaces_ref = &mut self.surfaces;
with_surface_tree_downward(
surface,
(),
|_, _, _| TraversalAction::DoChildren(()),
|wl_surface, _, _| {
output_enter(&output, surfaces_ref, wl_surface, logger_ref);
},
|_, _, _| true,
);
for (popup, _) in PopupManager::popups_for_surface(surface)
.ok()
.into_iter()
.flatten()
{
if let Some(surface) = popup.get_surface() {
with_surface_tree_downward(
surface,
(),
|_, _, _| TraversalAction::DoChildren(()),
|wl_surface, _, _| {
output_enter(&output, surfaces_ref, wl_surface, logger_ref);
},
|_, _, _| true,
)
}
}
let data = with_states(surface, |states| { let data = with_states(surface, |states| {
*states.cached_state.current::<LayerSurfaceCachedState>() *states.cached_state.current::<LayerSurfaceCachedState>()
}) })
@ -233,8 +293,8 @@ impl LayerMap {
} }
} }
slog::debug!( slog::trace!(
crate::slog_or_fallback(None), self.logger,
"Setting layer to pos {:?} and size {:?}", "Setting layer to pos {:?} and size {:?}",
location, location,
size size
@ -253,7 +313,7 @@ impl LayerMap {
layer_state(layer).location = location; layer_state(layer).location = location;
} }
slog::debug!(crate::slog_or_fallback(None), "Remaining zone {:?}", zone); slog::trace!(self.logger, "Remaining zone {:?}", zone);
self.zone = zone; self.zone = zone;
} }
} }
@ -267,7 +327,8 @@ impl LayerMap {
/// This function needs to be called periodically (though not necessarily frequently) /// This function needs to be called periodically (though not necessarily frequently)
/// to be able cleanup internally used resources. /// to be able cleanup internally used resources.
pub fn cleanup(&mut self) { pub fn cleanup(&mut self) {
self.layers.retain(|layer| layer.alive()) self.layers.retain(|layer| layer.alive());
self.surfaces.retain(|s| s.as_ref().is_alive());
} }
} }

View File

@ -2,23 +2,22 @@
//! rendering helpers to add custom elements or different clients to a space. //! rendering helpers to add custom elements or different clients to a space.
use crate::{ use crate::{
backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer}, backend::renderer::{Frame, ImportAll, Renderer},
desktop::{ desktop::{
layer::{layer_map_for_output, LayerSurface}, layer::{layer_map_for_output, LayerSurface},
popup::PopupManager,
utils::{output_leave, output_update},
window::Window, window::Window,
}, },
utils::{Logical, Point, Rectangle, Transform}, utils::{Logical, Point, Rectangle, Transform},
wayland::{ wayland::{
compositor::{ compositor::{get_parent, is_sync_subsurface},
get_parent, is_sync_subsurface, with_surface_tree_downward, SubsurfaceCachedState,
TraversalAction,
},
output::Output, output::Output,
shell::wlr_layer::Layer as WlrLayer, shell::wlr_layer::Layer as WlrLayer,
}, },
}; };
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
use std::{cell::RefCell, collections::VecDeque, fmt}; use std::{collections::VecDeque, fmt};
use wayland_server::protocol::wl_surface::WlSurface; use wayland_server::protocol::wl_surface::WlSurface;
mod element; mod element;
@ -326,97 +325,39 @@ impl Space {
// the output. // the output.
if !output_geometry.overlaps(bbox) { if !output_geometry.overlaps(bbox) {
if let Some(surface) = kind.get_surface() { if let Some(surface) = kind.get_surface() {
with_surface_tree_downward( output_leave(output, &mut output_state.surfaces, surface, &self.logger);
surface,
(),
|_, _, _| TraversalAction::DoChildren(()),
|wl_surface, _, _| {
if output_state.surfaces.contains(wl_surface) {
slog::trace!(
self.logger,
"surface ({:?}) leaving output {:?}",
wl_surface,
output.name()
);
output.leave(wl_surface);
output_state.surfaces.retain(|s| s != wl_surface);
}
},
|_, _, _| true,
)
} }
continue; continue;
} }
if let Some(surface) = kind.get_surface() { if let Some(surface) = kind.get_surface() {
with_surface_tree_downward( output_update(
output,
output_geometry,
&mut output_state.surfaces,
surface, surface,
window_loc(window, &self.id), window_loc(window, &self.id),
|_, states, location| { &self.logger,
let mut location = *location; );
let data = states.data_map.get::<RefCell<SurfaceState>>();
if data.is_some() { for (popup, location) in PopupManager::popups_for_surface(surface)
if states.role == Some("subsurface") { .ok()
let current = states.cached_state.current::<SubsurfaceCachedState>(); .into_iter()
location += current.location; .flatten()
} {
if let Some(surface) = popup.get_surface() {
TraversalAction::DoChildren(location) let location = window_loc(window, &self.id) + window.geometry().loc + location
} else { - popup.geometry().loc;
// If the parent surface is unmapped, then the child surfaces are hidden as output_update(
// well, no need to consider them here. output,
TraversalAction::SkipChildren output_geometry,
} &mut output_state.surfaces,
}, surface,
|wl_surface, states, &loc| { location,
let data = states.data_map.get::<RefCell<SurfaceState>>(); &self.logger,
);
if let Some(size) = data.and_then(|d| d.borrow().surface_size()) { }
let surface_rectangle = Rectangle { loc, size }; }
if output_geometry.overlaps(surface_rectangle) {
// We found a matching output, check if we already sent enter
if !output_state.surfaces.contains(wl_surface) {
slog::trace!(
self.logger,
"surface ({:?}) entering output {:?}",
wl_surface,
output.name()
);
output.enter(wl_surface);
output_state.surfaces.push(wl_surface.clone());
}
} else {
// Surface does not match output, if we sent enter earlier
// we should now send leave
if output_state.surfaces.contains(wl_surface) {
slog::trace!(
self.logger,
"surface ({:?}) leaving output {:?}",
wl_surface,
output.name()
);
output.leave(wl_surface);
output_state.surfaces.retain(|s| s != wl_surface);
}
}
} else {
// Maybe the the surface got unmapped, send leave on output
if output_state.surfaces.contains(wl_surface) {
slog::trace!(
self.logger,
"surface ({:?}) leaving output {:?}",
wl_surface,
output.name()
);
output.leave(wl_surface);
output_state.surfaces.retain(|s| s != wl_surface);
}
}
},
|_, _, _| true,
)
} }
} }
} }

View File

@ -231,3 +231,89 @@ pub fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) {
|_, _, &()| true, |_, _, &()| true,
); );
} }
pub(crate) fn output_update(
output: &Output,
output_geometry: Rectangle<i32, Logical>,
surface_list: &mut Vec<wl_surface::WlSurface>,
surface: &wl_surface::WlSurface,
location: Point<i32, Logical>,
logger: &slog::Logger,
) {
with_surface_tree_downward(
surface,
location,
|_, states, location| {
let mut location = *location;
let data = states.data_map.get::<RefCell<SurfaceState>>();
if data.is_some() {
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
location += current.location;
}
TraversalAction::DoChildren(location)
} else {
// If the parent surface is unmapped, then the child surfaces are hidden as
// well, no need to consider them here.
TraversalAction::SkipChildren
}
},
|wl_surface, states, &loc| {
let data = states.data_map.get::<RefCell<SurfaceState>>();
if let Some(size) = data.and_then(|d| d.borrow().surface_size()) {
let surface_rectangle = Rectangle { loc, size };
if output_geometry.overlaps(surface_rectangle) {
// We found a matching output, check if we already sent enter
output_enter(output, surface_list, wl_surface, logger);
} else {
// Surface does not match output, if we sent enter earlier
// we should now send leave
output_leave(output, surface_list, wl_surface, logger);
}
} else {
// Maybe the the surface got unmapped, send leave on output
output_leave(output, surface_list, wl_surface, logger);
}
},
|_, _, _| true,
);
}
pub(crate) fn output_enter(
output: &Output,
surface_list: &mut Vec<wl_surface::WlSurface>,
surface: &wl_surface::WlSurface,
logger: &slog::Logger,
) {
if !surface_list.contains(surface) {
slog::debug!(
logger,
"surface ({:?}) entering output {:?}",
surface,
output.name()
);
output.enter(surface);
surface_list.push(surface.clone());
}
}
pub(crate) fn output_leave(
output: &Output,
surface_list: &mut Vec<wl_surface::WlSurface>,
surface: &wl_surface::WlSurface,
logger: &slog::Logger,
) {
if surface_list.contains(surface) {
slog::debug!(
logger,
"surface ({:?}) leaving output {:?}",
surface,
output.name()
);
output.leave(surface);
surface_list.retain(|s| s != surface);
}
}

View File

@ -102,7 +102,7 @@ pub struct PhysicalProperties {
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Inner { pub(crate) struct Inner {
name: String, name: String,
log: ::slog::Logger, pub(crate) log: ::slog::Logger,
instances: Vec<WlOutput>, instances: Vec<WlOutput>,
physical: PhysicalProperties, physical: PhysicalProperties,
location: Point<i32, Logical>, location: Point<i32, Logical>,