diff options
| author | srdusr <[email protected]> | 2024-07-30 22:10:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-07-30 22:10:00 +0200 |
| commit | 38683f069a039f9da6d7ddc64167850560fd07ed (patch) | |
| tree | c9c240dfb91639c59e27779cd654c96202b25688 /crates/wayland/src/state/layers.rs | |
| parent | 5fe599437ab9d1d9cfa749e861ce2f3acc02aa3c (diff) | |
| download | srdwm-38683f069a039f9da6d7ddc64167850560fd07ed.tar.gz srdwm-38683f069a039f9da6d7ddc64167850560fd07ed.zip | |
Split crates/wayland/src/state.rs (1276 lines) into state/
Pure reorganization, no behavior change - verified by diffing the
function-name and struct-name sets before/after (both identical) plus
a full cargo test pass. mod.rs keeps ClientState/OutputEntry/CompState/
WindowAnim/RepeatState's definitions, the key-repeat impl, and the
output-lookup impl (all small and tightly coupled to the type
definitions), plus mod declarations. The one large impl CompState
block (previously ~600 lines) splits by concern:
- lifecycle.rs: new_managed_window, set_decorated_from_mode,
redraw_decoration_buffer, remove_window.
- layers.rs: ensure_layer_initial_configure.
- focus.rs: set_keyboard_focus, set_window_activated.
- menu.rs: open/close/run_context_menu_action, is_double_click.
- geometry.rs: raise_pinned, sync_geometry.
- tick.rs: tick_dirty_broadcasts, tick_animations,
resync_stacking_order.
- toplevel.rs: the with_toplevel_title/app_id/sync_toplevel_metadata
free functions.
- tests.rs: unsplit, same reasoning as every other split this pass.
CompState's fields were already pub(crate) (this crate's existing
convention, unlike core's/config's plain-private), so no field-
visibility changes were needed - only resync_stacking_order (called
from geometry.rs, defined in tick.rs) needed bumping from private to
pub(crate), matching that same convention.
Diffstat (limited to 'crates/wayland/src/state/layers.rs')
| -rw-r--r-- | crates/wayland/src/state/layers.rs | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/crates/wayland/src/state/layers.rs b/crates/wayland/src/state/layers.rs new file mode 100644 index 0000000..1a02858 --- /dev/null +++ b/crates/wayland/src/state/layers.rs @@ -0,0 +1,105 @@ +use super::*; + +impl CompState { + + /// Layer surfaces need a configure sent in direct response to their + /// first commit (sending it any earlier violates the protocol - see + /// `smithay::desktop::LayerMap::arrange`'s doc comment on why `arrange` + /// itself deliberately won't send one). Also the point at which an + /// `Exclusive`-interactivity layer (e.g. a lock screen, or a launcher + /// configured to grab all keyboard input) claims keyboard focus, since + /// its `keyboard_interactivity` isn't reliably known until the client's + /// state has actually committed. + pub(crate) fn ensure_layer_initial_configure(&mut self, surface: &WlSurface) { + // Called unconditionally from `commit()` for every surface in the + // whole desktop, on every single commit - so before doing anything + // that scales with output/layer count, a cheap O(1) check: has this + // surface ever gone through `get_layer_surface` at all? Only that + // request ever inserts `LayerSurfaceData` into a surface's + // `data_map` (smithay's own `handlers.rs`), so this is `None` for + // every ordinary xdg-toplevel/subsurface commit - the overwhelming + // majority of commits on any real desktop. Skipping straight past + // the per-output `layer_for_surface` surface-tree walk for all of + // those is the difference between this function costing something + // on every single frame any window renders versus only on commits + // from the handful of surfaces that were ever layer surfaces. + if with_states(surface, |states| states.data_map.get::<LayerSurfaceData>().is_none()) { + return; + } + // A layer surface lives in exactly one output's `LayerMap` (whichever + // one `new_layer_surface` mapped it into), so find that output rather + // than assuming a single global one. + let found = self.outputs().find_map(|output| { + let layer = layer_map_for_output(output).layer_for_surface(surface, WindowSurfaceType::TOPLEVEL).cloned(); + layer.map(|l| (output.clone(), l)) + }); + // Not a layer surface (or a destroyed one - see `new_surface`'s + // pre-commit-hook workaround, which is what stops this from being + // a protocol error). Every ordinary commit from every window in + // the desktop passes through here and takes this branch, so this + // used to log unconditionally during the P0 investigation + // (docs/PANEL_SUPPORT_TODO.md) - diagnostic purpose long since + // served, and left running it logged upwards of 15k lines in a few + // minutes of normal use (every Firefox frame, every terminal + // redraw, ...), which is real wasted I/O, not just noise. + let Some((output, layer)) = found else { + return; + }; + + // Recompute geometry from whatever the client just committed + // (`set_size`/`set_anchor`/`set_margin`/`set_exclusive_zone` are all + // double-buffered, applied on this commit) *before* looking at + // `initial_configure_sent` - `map_layer`'s own `arrange()` call ran + // before the client had sent any of that, so without this, the + // first configure would carry stale, pre-request-processed geometry + // (verified live: wofi's `set_size(420, 550)` was otherwise ignored + // and it got stuck at the half-output fallback size instead). Every + // later commit needs the same treatment for live resizes/anchor + // changes; `arrange()` only actually sends a configure when + // something changed, so this is a no-op on a commit that didn't + // touch layer-shell state. + let zone_before = layer_map_for_output(&output).non_exclusive_zone(); + layer_map_for_output(&output).arrange(); + let zone_after = layer_map_for_output(&output).non_exclusive_zone(); + if zone_after != zone_before { + // A bar/dock claiming (or releasing) an exclusive zone changes + // the area core's placement/tiling should actually use -- + // without this, `WindowManager`'s notion of the monitor rect + // is whatever `Platform::monitors()` returned once at startup + // (before any layer-shell client had connected and set a real + // exclusive zone), so every window keeps being placed across + // the *whole* output including the strip a bar now occupies: + // new windows spawn with their titlebar directly under the bar, + // rendered beneath it and unreachable to drag. Reusing + // `MonitorAdded` here rather than a new event type: main.rs's + // handler for it already re-queries the full monitor list from + // the platform rather than trusting the event's payload (see + // its own comment on why), which is exactly "go recompute the + // usable area" - the placeholder `Monitor` below is discarded + // unread on that path. + self.pending.borrow_mut().push(CoreEvent::MonitorAdded(srdwm_core::Monitor::new(0, "", srdwm_core::Rect::new(0, 0, 0, 0)))); + } + + let initial_configure_sent = with_states(surface, |states| { + states + .data_map + .get::<LayerSurfaceData>() + .map(|d| d.lock().unwrap().initial_configure_sent) + .unwrap_or(false) + }); + if !initial_configure_sent { + layer.layer_surface().send_configure(); + log::debug!("layer-shell: sent initial configure for surface {:?}", surface.id()); + } + + // Checked on every commit, not just the first: a client can flip + // `keyboard_interactivity` to `Exclusive` after already being + // mapped (and this is also, in practice, where a freshly-mapped + // `Exclusive` surface - e.g. wofi, which requests it from the very + // first commit - actually gets focus, since `set_keyboard_focus` + // is idempotent against a surface that's already focused). + if layer.cached_state().keyboard_interactivity == KeyboardInteractivity::Exclusive { + self.set_keyboard_focus(Some(surface.clone())); + } + } +} |