Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
feat(borders): slower handle fade, and the ring fades OUT on release
`BORDER_FADE_STEP` 0.25 → 0.15 per 16 ms tick: the ring now eases in
over roughly half a second instead of a fifth.
Releasing Super (or leaving overview, or losing focus) cut the ring
away in one frame: `draw_borders` disabled every handle node outright
the moment the mode was off, while `step_border_fade` was still
easing the reveal toward zero on a ring nobody drew. The ring now
stays drawn while any reveal is above zero — a fading ring is
decoration — and only the invisible catcher rects are gated on the
mode being live, so a ring on its way out is never a grab. Measured
in a shadow through a virtual-keyboard Super hold: the ring pixel
ramps up over ~500 ms after the press and back down over ~500 ms
after the release.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
src/server/window.rs | 27 +++++++++++++++++----------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/src/server/window.rs b/src/server/window.rs
index 20445f1..64eafb9 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -173,7 +173,7 @@ impl BorderElement {
/// Per-frame step of the hover fade, as a fraction of the remaining distance
/// to the target (the same exponential-approach shape the viewport pan uses).
-pub const BORDER_FADE_STEP: f32 = 0.25;
+pub const BORDER_FADE_STEP: f32 = 0.15;
/// Below this the fade is treated as finished and snapped to its target.
pub const BORDER_FADE_EPSILON: f32 = 0.004;
@@ -4033,12 +4033,19 @@ impl Window {
let (cw, ch) = (content.width, content.height);
// A window thinner than two bands has no interior left for a
// ring; drawing one would be a solid block over the whole window.
- let handles_on = in_overview
+ // Live handles: the mode is on and this is the focused window.
+ // Focused-only, like the reveal in step_border_fade: without
+ // this the invisible catcher rects would keep intercepting
+ // scene hits on windows whose ring is not even drawn.
+ let handles_live = in_overview && self.is_seat_focused();
+ // Drawn handles: live, OR still fading out — releasing Super (or
+ // leaving overview, or losing focus) eases the ring away instead
+ // of cutting it, so the ring stays drawn while any reveal is
+ // above zero. The catchers below are gated on `handles_live`
+ // alone: a fading ring is decoration, never a grab.
+ let fading_out = !handles_live && self.border_reveal.iter().any(|&a| a > 0.0);
+ let handles_on = (handles_live || fading_out)
&& window_takes_handles(self_ptr)
- // Focused-only, like the reveal in step_border_fade: without
- // this the invisible catcher rects would keep intercepting
- // scene hits on windows whose ring is not even drawn.
- && self.is_seat_focused()
&& !is_virtual_border
&& bw > 0
&& (cw as f64 * sc) >= 12.0
@@ -4084,13 +4091,13 @@ impl Window {
// to split a gap SHARED with a neighbouring window, and an inside
// ring shares nothing.
let b = ffi::wlr_box { x: 0, y: 0, width: bw_u, height: ch };
- apply(self.border.left, b, &transparent, true);
+ apply(self.border.left, b, &transparent, handles_live);
let b = ffi::wlr_box { x: cw - bw_u, y: 0, width: bw_u, height: ch };
- apply(self.border.right, b, &transparent, true);
+ apply(self.border.right, b, &transparent, handles_live);
let b = ffi::wlr_box { x: bw_u, y: 0, width: cw - 2 * bw_u, height: bw_u };
- apply(self.border.top, b, &transparent, true);
+ apply(self.border.top, b, &transparent, handles_live);
let b = ffi::wlr_box { x: bw_u, y: ch - bw_u, width: cw - 2 * bw_u, height: bw_u };
- apply(self.border.bottom, b, &transparent, true);
+ apply(self.border.bottom, b, &transparent, handles_live);
let layout = &(*self.server).wm.layout;
// The ring hugs the window's own silhouette, so its outer arc IS