Commit 2888d5881262
tarball5 files changed+186-35
@@ -172,6 +172,9 @@ fn focus_under_pointer(state: &mut State) {
172172
}173173
174174
fn window_at(state: &State, pos: Point<f64, Logical>) -> Option<WindowId> {175
+ if let Some((root, ..)) = state.window_popup_under(pos) {176
+ return state.window_id(&root);177
+ }175178
let (w, _) = state.wm.space.element_under(pos)?;176179
state.window_id(&crate::wm::root(w)?)177180
}@@ -6,7 +6,10 @@ use crate::state::State;
66
use monolith_layout::{Insets, OutputId};77
use monolith_render_vk::VulkanRenderer;88
use smithay::backend::renderer::element::AsRenderElements;9
-use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;9
+use smithay::backend::renderer::element::Kind;10
+use smithay::backend::renderer::element::surface::{11
+ WaylandSurfaceRenderElement, render_elements_from_surface_tree,12
+};1013
use smithay::desktop::{LayerSurface, PopupKind, WindowSurfaceType, layer_map_for_output};1114
use smithay::output::Output;1215
use smithay::reexports::wayland_server::Resource;@@ -213,6 +216,24 @@ impl State {
213216
})214217
}215218
219
+ /// The popup of a layer surface below the windows under `pos`.220
+ pub fn lower_popup_under(221
+ &self,222
+ pos: Point<f64, Logical>,223
+ ) -> Option<(WlSurface, Point<f64, Logical>)> {224
+ let output = self.wm.space.output_under(pos).next()?;225
+ let origin = self.wm.space.output_geometry(output)?.loc;226
+ let map = layer_map_for_output(output);227
+ [Layer::Bottom, Layer::Background]228
+ .into_iter()229
+ .find_map(|layer| {230
+ map.layers_on(layer).rev().find_map(|l| {231
+ let at = origin + map.layer_geometry(l)?.loc;232
+ crate::popups::hit(crate::popups::placed(l.wl_surface(), at), pos)233
+ })234
+ })235
+ }236
+216237
/// The layer surface under `pos` on `layers`, with its surface origin.217238
pub fn layer_under(218239
&self,@@ -239,12 +260,37 @@ pub fn elements(out: &mut Vec<Draw>, renderer: &mut VulkanRenderer, output: &Out
239260
let Some(geo) = map.layer_geometry(l) else {240261
continue;241262
};263
+ let at = geo.loc.to_physical(1);242264
let elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>> =243
- l.render_elements(renderer, geo.loc.to_physical(1), Scale::from(1.0), 1.0);265
+ if matches!(layer, Layer::Bottom | Layer::Background) {266
+ render_elements_from_surface_tree(267
+ renderer,268
+ l.wl_surface(),269
+ at,270
+ 1.0,271
+ 1.0,272
+ Kind::Unspecified,273
+ )274
+ } else {275
+ l.render_elements(renderer, at, Scale::from(1.0), 1.0)276
+ };244277
out.extend(elems.into_iter().map(Draw::Surface));245278
}246279
}247280
281
+/// The popups of the layer surfaces below the windows. Those surfaces draw282
+/// without them, so a menu is not hidden by the window it opens over.283
+pub fn lower_popups(out: &mut Vec<Draw>, renderer: &mut VulkanRenderer, output: &Output) {284
+ let map = layer_map_for_output(output);285
+ for layer in [Layer::Bottom, Layer::Background] {286
+ for l in map.layers_on(layer).rev() {287
+ if let Some(geo) = map.layer_geometry(l) {288
+ crate::popups::elements(out, renderer, l.wl_surface(), geo.loc, 1.0);289
+ }290
+ }291
+ }292
+}293
+248294
/// With `covered`, a fullscreen window hides everything below the overlay249295
/// layer, so only overlay surfaces are asked to draw.250296
pub fn send_frames(output: &Output, elapsed: Duration, covered: bool) {@@ -1,17 +1,22 @@
11
//! xdg popups, from their grabs to where they are placed.22
33
use crate::focus::Focus;4
+use crate::scene::Draw;45
use crate::state::State;5
-use monolith_layout::Rect;6
+use monolith_layout::{Rect, WindowId};7
+use monolith_render_vk::VulkanRenderer;8
+use smithay::backend::renderer::element::Kind;9
+use smithay::backend::renderer::element::surface::render_elements_from_surface_tree;10
+use smithay::desktop::utils::under_from_surface_tree;611
use smithay::desktop::{7
- PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, WindowSurfaceType,8
- find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output,12
+ PopupKeyboardGrab, PopupKind, PopupManager, PopupPointerGrab, PopupUngrabStrategy,13
+ WindowSurfaceType, find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output,914
};1015
use smithay::input::Seat;1116
use smithay::input::pointer::Focus as GrabFocus;1217
use smithay::reexports::wayland_server::protocol::wl_seat::WlSeat;1318
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;14
-use smithay::utils::{Logical, Point, Rectangle, Serial};19
+use smithay::utils::{Logical, Physical, Point, Rectangle, Serial};1520
use smithay::wayland::shell::xdg::PopupSurface;1621
1722
impl State {@@ -115,3 +120,82 @@ impl State {
115120
popup.with_pending_state(|s| s.geometry = s.positioner.get_unconstrained_geometry(target));116121
}117122
}123
+124
+/// The popup surfaces of `root` with where their origins are, newest first.125
+/// `geometry_at` is where root's window geometry sits, which for a layer126
+/// surface is where the surface itself sits.127
+pub fn placed(128
+ root: &WlSurface,129
+ geometry_at: Point<i32, Logical>,130
+) -> Vec<(WlSurface, Point<i32, Logical>)> {131
+ PopupManager::popups_for_surface(root)132
+ .map(|(popup, at)| {133
+ (134
+ popup.wl_surface().clone(),135
+ geometry_at + at - popup.geometry().loc,136
+ )137
+ })138
+ .collect()139
+}140
+141
+/// The surface of `popups` under `pos`, with its origin.142
+pub fn hit(143
+ popups: Vec<(WlSurface, Point<i32, Logical>)>,144
+ pos: Point<f64, Logical>,145
+) -> Option<(WlSurface, Point<f64, Logical>)> {146
+ popups.into_iter().find_map(|(popup, at)| {147
+ let (s, p) =148
+ under_from_surface_tree(&popup, pos - at.to_f64(), (0, 0), WindowSurfaceType::ALL)?;149
+ Some((s, (p + at).to_f64()))150
+ })151
+}152
+153
+/// Draws `root`'s popups with its window geometry at `geometry_at`.154
+pub fn elements(155
+ out: &mut Vec<Draw>,156
+ renderer: &mut VulkanRenderer,157
+ root: &WlSurface,158
+ geometry_at: Point<i32, Logical>,159
+ alpha: f32,160
+) {161
+ for (popup, at) in placed(root, geometry_at) {162
+ let at = Point::<i32, Physical>::from((at.x, at.y));163
+ out.extend(164
+ render_elements_from_surface_tree(renderer, &popup, at, 1.0, alpha, Kind::Unspecified)165
+ .into_iter()166
+ .map(Draw::Surface),167
+ );168
+ }169
+}170
+171
+impl State {172
+ /// The window covering the output under `pos` while it is fullscreen.173
+ pub fn fullscreen_at(&self, pos: Point<f64, Logical>) -> Option<WindowId> {174
+ self.wm.layout.outputs().find_map(|(o, out)| {175
+ let a = out.area;176
+ a.contains(pos.x, pos.y)177
+ .then(|| self.wm.layout.arrange(o).fullscreen)178
+ .flatten()179
+ })180
+ }181
+182
+ /// The popup of a window under `pos`, frontmost window first, with the183
+ /// window's root surface. A fullscreen window hides the popups of the184
+ /// windows below it.185
+ pub fn window_popup_under(186
+ &self,187
+ pos: Point<f64, Logical>,188
+ ) -> Option<(WlSurface, WlSurface, Point<f64, Logical>)> {189
+ let fullscreen = self.fullscreen_at(pos);190
+ self.wm.space.elements().rev().find_map(|window| {191
+ let root = crate::wm::root(window).filter(|_| window.toplevel().is_some())?;192
+ let id = self.window_id(&root)?;193
+ if fullscreen.is_some_and(|f| f != id) {194
+ return None;195
+ }196
+ let (_, rect, _) = self.wm.get(id)?.placed?;197
+ let (surface, at) = hit(placed(&root, whole(rect).loc), pos)?;198
+ Some((root, surface, at))199
+ })200
+ }201
+}@@ -15,11 +15,10 @@ use smithay::backend::renderer::element::{AsRenderElements, Element, Id, Kind, R
1515
use smithay::backend::renderer::utils::with_renderer_surface_state;1616
use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};1717
use smithay::backend::renderer::{ContextId, Renderer, Texture};18
-use smithay::desktop::{PopupManager, Window};18
+use smithay::desktop::Window;1919
use smithay::output::Output;2020
use smithay::utils::user_data::UserDataMap;2121
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};22
-use smithay::wayland::compositor::{TraversalAction, with_surface_tree_downward};2322
use smithay::wayland::shell::wlr_layer::Layer;2423
use smithay::xwayland::X11Surface;2524
@@ -121,6 +120,25 @@ pub fn build(
121120
crate::layers::elements(&mut out, renderer, output, Layer::Top);122121
}123122
}123
+ let covering = arr.fullscreen.filter(|_| fullscreen_at_rest);124
+ for &(id, r, _, _) in tiled.iter().chain(&above).rev() {125
+ if covering.is_some_and(|f| f != id) {126
+ continue;127
+ }128
+ let Some(root) = windows.get(id).and_then(|s| s.window.toplevel()) else {129
+ continue;130
+ };131
+ let Some(((x, y), _)) = pixels(r) else {132
+ continue;133
+ };134
+ let alpha = anim.effect(id, now).map_or(1.0, |e| e.progress);135
+ crate::popups::elements(&mut out, renderer, root.wl_surface(), (x, y).into(), alpha);136
+ }137
+ if let Some(output) = output138
+ && !fullscreen_at_rest139
+ {140
+ crate::layers::lower_popups(&mut out, renderer, output);141
+ }124142
for (g, frame, r) in fading.into_iter().rev() {125143
out.extend(ghost_element(frame, r, anim.ghost_effect(g, now), deco));126144
}@@ -133,7 +151,7 @@ pub fn build(
133151
window_elements(&mut out, renderer, &state.window, r, effect, gliding);134152
let clip = framed.then(|| corner_clip(r, deco)).flatten();135153
if let Some(clip) = clip {136
- clip_window(&mut out, first, &state.window, clip);154
+ clip_window(&mut out, first, clip);137155
}138156
if framed && deco.border > 0 {139157
let color = if focused == Some(id) {@@ -180,8 +198,17 @@ fn window_elements(
180198
let geo = window.geometry();181199
let loc = Point::<i32, Physical>::from((x - geo.loc.x, y - geo.loc.y));182200
let alpha = effect.map_or(1.0, |e| e.progress);183
- let elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>> =184
- window.render_elements(renderer, loc, Scale::from(1.0), alpha);201
+ let elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>> = match window.toplevel() {202
+ Some(t) => render_elements_from_surface_tree(203
+ renderer,204
+ t.wl_surface(),205
+ loc,206
+ 1.0,207
+ alpha,208
+ Kind::Unspecified,209
+ ),210
+ None => window.render_elements(renderer, loc, Scale::from(1.0), alpha),211
+ };185212
if let Some(effect) = effect {186213
effect_elements(out, renderer, window, elems, ((x, y), (w, h)), effect);187214
return;@@ -320,7 +347,7 @@ fn texture(
320347
}321348
322349
/// A window in a spawn transition. Its root surface goes through the effect323
-/// shader, clamped to fill its rect, and its subsurfaces and popups fade with it.350
+/// shader, clamped to fill its rect, and its subsurfaces fade with it.324351
fn effect_elements(325352
out: &mut Vec<Draw>,326353
renderer: &mut VulkanRenderer,@@ -471,26 +498,10 @@ fn corner_clip(r: Rect, deco: &Decoration) -> Option<Clip> {
471498
})472499
}473500
474
-/// Clips what a window drew from `first` on, leaving its popups whole.475
-fn clip_window(out: &mut [Draw], first: usize, window: &Window, clip: Clip) {476
- let mut popups = std::collections::HashSet::new();477
- if let Some(root) = crate::wm::root(window) {478
- for (popup, _) in PopupManager::popups_for_surface(&root) {479
- with_surface_tree_downward(480
- popup.wl_surface(),481
- (),482
- |_, _, _| TraversalAction::DoChildren(()),483
- |s, _, _| {484
- popups.insert(Id::from_wayland_resource(s));485
- },486
- |_, _, _| true,487
- );488
- }489
- }501
+/// Clips what a window drew from `first` on.502
+fn clip_window(out: &mut [Draw], first: usize, clip: Clip) {490503
for draw in &mut out[first..] {491
- if !popups.contains(draw.id()) {492
- wrap(draw, clip);493
- }504
+ wrap(draw, clip);494505
}495506
}496507
@@ -472,10 +472,7 @@ impl State {
472472
return Some((s, (p + geo.loc).to_f64()));473473
}474474
}475
- let fullscreen = self.wm.layout.outputs().any(|(o, out)| {476
- let a = out.area;477
- a.contains(pos.x, pos.y) && self.wm.layout.arrange(o).fullscreen.is_some()478
- });475
+ let fullscreen = self.fullscreen_at(pos).is_some();479476
let above: &[Layer] = if fullscreen {480477
&[Layer::Overlay]481478
} else {@@ -484,6 +481,16 @@ impl State {
484481
let layer = |layers| self.layer_under(pos, layers).map(|(_, s, p)| (s, p));485482
layer(above)486483
.or_else(|| {484
+ self.window_popup_under(pos)485
+ .map(|(_, surface, origin)| (surface, origin))486
+ })487
+ .or_else(|| {488
+ if fullscreen {489
+ return None;490
+ }491
+ self.lower_popup_under(pos)492
+ })493
+ .or_else(|| {487494
self.wm488495
.space489496
.element_under(pos)