diff options
| author | srdusr <[email protected]> | 2026-05-11 16:44:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2026-05-11 16:44:00 +0200 |
| commit | caec1e7c355bbe6437afe87cd3dab6b64fb91e0a (patch) | |
| tree | 5ecab25d2ebc36949ac4129b35e35334a575d563 /crates/wayland/src/decoration | |
| parent | baabd91411dacd179c690f724b6cbfe6270cb131 (diff) | |
| download | srdwm-caec1e7c355bbe6437afe87cd3dab6b64fb91e0a.tar.gz srdwm-caec1e7c355bbe6437afe87cd3dab6b64fb91e0a.zip | |
Three live bug reports after a restart: icon drag, lock cursor, lock box
All three reported directly after the owner restarted into today's build.
Desktop icons could not be dragged at all in single-click mode. The press
handler opened the icon immediately when general.desktop_icon_single_click
was on, so the branch that starts a drag was unreachable and an icon could
never be moved. Deciding activation on press cannot distinguish a click from
the first instant of a drag. Every press on an icon now starts a potential
drag and release decides which it was, using a 4px movement threshold that
latches once exceeded. Double-click mode goes through the same path, so both
modes now drag identically.
The lock screen drew no cursor. The cursor push in the udev render loop sits
inside `if !locked`, and a locked head renders only the lock element list, so
nothing drew a pointer - and on a bare TTY nothing else does. The on-screen
keyboard's clicks were being handled correctly the whole time
(native_lock_click); they simply could not be aimed. The pointer is now
prepended to the lock element list, above the UI it is used to click.
The password field's opaque panel is gone. New LockConfig::box_opacity,
default 0.0: no fill, no border, no rounded rectangle, just the dots and
status text over the blurred background. Raising it restores the panel at
that opacity for anyone who wants a solid field. Drawing text on a
transparent surface needed a new blit_glyph_over: the existing blit_glyph
blends against one flat opaque colour and writes alpha 255, which would have
turned every glyph into a block of the assumed background - the same box
with its middle removed.
VERIFICATION STATUS, stated plainly: all three are code-complete and the
suite passes, but none is confirmed on screen. The nested backend's capture
pass does not draw the desktop icon grid (a gap already recorded in
winit/capture.rs), so the icon drag cannot be checked by screenshot there,
and aiming blind is what this project's own rules forbid. The two lock
changes were not visually checked either.
515 tests pass, clippy clean.
Diffstat (limited to 'crates/wayland/src/decoration')
| -rw-r--r-- | crates/wayland/src/decoration/font.rs | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/crates/wayland/src/decoration/font.rs b/crates/wayland/src/decoration/font.rs index 6a2a312..74d5047 100644 --- a/crates/wayland/src/decoration/font.rs +++ b/crates/wayland/src/decoration/font.rs @@ -121,6 +121,60 @@ fn find_best_font(dir: &std::path::Path, best: &mut Option<(std::path::PathBuf, } } +/// Composites a glyph over whatever is already in `buf`, honouring the +/// destination's own alpha - straight (non-premultiplied) BGRA, the same +/// convention `rgb_to_bgra` writes everywhere else in this module. +/// +/// [`blit_glyph`] below blends the glyph against one flat opaque colour and +/// writes alpha 255, which is right for a surface that has an opaque +/// background of its own (a titlebar, a menu row). It is wrong for text +/// drawn straight onto a transparent surface: every glyph pixel would +/// become an opaque block of the assumed background colour, so the text +/// would sit in a solid rectangle of exactly the box this was meant to +/// remove. Used by the lock screen's password field, which draws over the +/// blurred wallpaper with no panel behind it. +#[allow(clippy::too_many_arguments)] +pub(crate) fn blit_glyph_over( + buf: &mut [u8], + width: usize, + height: usize, + glyph_x: i32, + glyph_y: i32, + metrics: &fontdue::Metrics, + coverage: &[u8], + foreground: (u8, u8, u8), +) { + for row in 0..metrics.height { + let y = glyph_y + row as i32; + if y < 0 || y as usize >= height { + continue; + } + for col in 0..metrics.width { + let x = glyph_x + col as i32; + if x < 0 || x as usize >= width { + continue; + } + let sa = coverage[row * metrics.width + col] as f32 / 255.0; + if sa <= 0.0 { + continue; + } + let idx = (y as usize * width + x as usize) * 4; + let (db, dg, dr, da) = (buf[idx] as f32, buf[idx + 1] as f32, buf[idx + 2] as f32, buf[idx + 3] as f32 / 255.0); + let out_a = sa + da * (1.0 - sa); + if out_a <= 0.0 { + continue; + } + // Un-premultiplied source-over: each channel weighted by its + // own coverage, then divided back out by the result's alpha. + let mix = |fg: f32, dst: f32| -> u8 { (((fg * sa) + (dst * da * (1.0 - sa))) / out_a).round().clamp(0.0, 255.0) as u8 }; + buf[idx] = mix(foreground.2 as f32, db); + buf[idx + 1] = mix(foreground.1 as f32, dg); + buf[idx + 2] = mix(foreground.0 as f32, dr); + buf[idx + 3] = (out_a * 255.0).round().clamp(0.0, 255.0) as u8; + } + } +} + #[allow(clippy::too_many_arguments)] pub(crate) fn blit_glyph( buf: &mut [u8], |