srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/elements.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-12-24 20:44:00 +0200
committersrdusr <[email protected]>2024-12-24 20:44:00 +0200
commit3d73ed0f057a555ded67ad58dbe02b36cd4d2d1f (patch)
tree71e9bab462b9073444183e2a969ba512cb94559f /crates/wayland/src/elements.rs
parent331a1a1d3a0d8dfd4c285b755c2e13785c95ee2a (diff)
downloadsrdwm-3d73ed0f057a555ded67ad58dbe02b36cd4d2d1f.tar.gz
srdwm-3d73ed0f057a555ded67ad58dbe02b36cd4d2d1f.zip
Accumulate wayland-crate additions: capture rendering, focus/Space sync, VT resume, plumbing
Bundles the remaining wayland-crate changes built up here, touching both backends (udev and winit) and the shared input/rendering code: - udev/capture.rs: off-screen Pixman render of an arbitrary (not necessarily on-screen) workspace's window content to a PPM file -- what crates/core's capture-request queue drives, for a workspace switcher's thumbnail previews. wlr-screencopy structurally can't do this (it can only see what an output is presenting), which is why this exists as a separate render path rather than reusing it. - input::focus_window now also raises the window in smithay's own Space, not just core's stacking order - Space is what actually renders on top and what pointer hit-testing reads, so any focus path that skipped this (an IPC "focus" dispatch, concretely) left a window genuinely focused while still rendering, and receiving clicks, underneath whatever was already topmost. Both backends' poll loops now re-sync this after any IPC mutation. - udev/session.rs's VT-switch resume fix (drains a stale pending page flip before reasserting CRTCs) already has its own earlier, cleanly isolated commit - not duplicated here. - Assorted decoration/cursor/rounded-corners/output-management/ screencopy/XWayland changes and their cross-backend wiring. Coarser than the repo's usual one-purpose-per-commit convention, deliberately - see the core-crate sweep commit's own message for why.
Diffstat (limited to 'crates/wayland/src/elements.rs')
-rw-r--r--crates/wayland/src/elements.rs56
1 files changed, 50 insertions, 6 deletions
diff --git a/crates/wayland/src/elements.rs b/crates/wayland/src/elements.rs
index f7f7484..4c20ee6 100644
--- a/crates/wayland/src/elements.rs
+++ b/crates/wayland/src/elements.rs
@@ -12,10 +12,10 @@ use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRen
use smithay::backend::renderer::element::surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement};
use smithay::backend::renderer::element::Kind;
use smithay::backend::renderer::{Color32F, ImportAll, ImportMem, Renderer};
-use smithay::desktop::{layer_map_for_output, PopupManager, Space, Window as DWindow};
+use smithay::desktop::{layer_map_for_output, utils::under_from_surface_tree, PopupManager, Space, Window as DWindow, WindowSurfaceType};
use smithay::output::Output;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
-use smithay::utils::{Physical, Point, Rectangle, Scale};
+use smithay::utils::{Logical, Physical, Point, Rectangle, Scale};
use smithay::wayland::shell::wlr_layer::Layer;
use srdwm_core::TITLEBAR_HEIGHT;
@@ -167,25 +167,26 @@ where
/// caller's fallback is the same: render `surface`'s content unrounded via
/// [`surface_content_elements`].
pub(crate) fn rounded_content_buffer<'a>(
- cache: &'a mut std::collections::HashMap<srdwm_core::WindowId, (u64, smithay::backend::renderer::element::memory::MemoryRenderBuffer)>,
+ cache: &'a mut std::collections::HashMap<srdwm_core::WindowId, (u64, u32, smithay::backend::renderer::element::memory::MemoryRenderBuffer)>,
epoch: u64,
id: srdwm_core::WindowId,
surface: &WlSurface,
radius: f32,
corners: crate::rounded_corners::RoundedCorners,
) -> Option<&'a smithay::backend::renderer::element::memory::MemoryRenderBuffer> {
- let stale = cache.get(&id).map(|(built, _)| *built != epoch).unwrap_or(true);
+ let radius_bits = radius.to_bits();
+ let stale = cache.get(&id).map(|(built, r, _)| *built != epoch || *r != radius_bits).unwrap_or(true);
if stale {
match crate::rounded_corners_pixman::masked_content_buffer(surface, radius, corners) {
Some(buf) => {
- cache.insert(id, (epoch, buf));
+ cache.insert(id, (epoch, radius_bits, buf));
}
None => {
cache.remove(&id);
}
}
}
- cache.get(&id).map(|(_, b)| b)
+ cache.get(&id).map(|(_, _, b)| b)
}
/// Every mapped layer-shell surface on `output` whose [`Layer`] `include`
@@ -304,6 +305,49 @@ where
elements
}
+/// Topmost currently-mapped popup (tooltip, dropdown, right-click menu)
+/// under `pos`, if any - checked before every other kind of surface, the
+/// same "draw on top of literally everything else" priority
+/// `popup_render_elements` above already gives every popup (pushed into
+/// `custom_elements` ahead of even `Overlay`/`Top` layer-shell surfaces in
+/// both backends' render loops).
+///
+/// Without this, pointer hit-testing (`input::refresh_pointer_focus`) never
+/// checked popups at all: `state.popups: PopupManager` is entirely separate
+/// from `state.space` (a popup is never `space.map_element`'d - see this
+/// module's own `popup_render_elements` doc comment) and from layer-shell's
+/// `LayerMap`, so hit-testing that only walked those two structures was
+/// blind to every open popup. `xdg_popup`'s implicit pointer grab
+/// (`PopupPointerGrab`, smithay's own - see `protocols.rs`'s `grab`
+/// handler) only checks that the *focus* handed to it by `pointer.motion()`
+/// belongs to the same client as the grabbed popup; it never substitutes in
+/// the popup's own surface itself. A focus computed with no popup check at
+/// all resolved to whatever was visually *underneath* the popup instead --
+/// typically the same client's own parent window, so the grab's same-client
+/// check passed and let the motion straight through - meaning every click
+/// or scroll over an open popup actually landed on the parent surface, at
+/// coordinates that correspond to nothing real drawn there. Reads exactly
+/// as "menus are hard to click or scroll in", even though neither the grab
+/// itself nor the popup's own widgets were ever broken.
+///
+/// Real per-pixel hit-testing via `under_from_surface_tree`, not a
+/// bounding-rect check against `PopupKind::geometry()` - respects each
+/// surface's actual input region and walks subsurfaces, the same
+/// precision `layer_surface_under`/`Window::surface_under` already give
+/// every other surface kind.
+pub(crate) fn popup_surface_under(state: &CompState, pos: Point<f64, Logical>) -> Option<(WlSurface, Point<i32, Logical>)> {
+ for target in popup_targets(state).iter().rev() {
+ let popups: Vec<_> = PopupManager::popups_for_surface(&target.surface).collect();
+ for (popup, offset) in popups.into_iter().rev() {
+ let origin = Point::<i32, Logical>::from(target.window_pos) + offset;
+ if let Some(hit) = under_from_surface_tree(popup.wl_surface(), pos, origin, WindowSurfaceType::ALL) {
+ return Some(hit);
+ }
+ }
+ }
+ None
+}
+
/// Which of `space`'s mapped windows actually need a frame callback this
/// pass - the ones whose on-screen bounds overlap `damage`, in physical
/// output-space.