diff options
| author | srdusr <[email protected]> | 2024-12-24 20:44:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-12-24 20:44:00 +0200 |
| commit | 3d73ed0f057a555ded67ad58dbe02b36cd4d2d1f (patch) | |
| tree | 71e9bab462b9073444183e2a969ba512cb94559f /crates/wayland/src/elements.rs | |
| parent | 331a1a1d3a0d8dfd4c285b755c2e13785c95ee2a (diff) | |
| download | srdwm-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.rs | 56 |
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. |