T21: on X11 the window names its pane in _KDE_NET_WM_BLUR_BEHIND_REGION and drops it when Blurred goes; Ely's new ResizeBorder::glass leaves a client-drawn window's pane to the content, without ground or shadow, so a Linux window shows the desktop at all; T20's region and this property cover the pane alone, worked out each frame and sent only on a change; the Ely pin moves to 5dc4dd1; the X11 test runs its own Xvfb
CI / platforms (windows-latest) (push) Failing after 8s
CI / macos (push) Failing after 8m43s
CI / platforms (ubuntu-24.04) (push) Canceled after 0s

This commit is contained in:
ZacharyZhang-NY committed 2026-10-15 01:09:47 -04:00
1 parent 8eeeedf12d
commit b5e4967071
11 files changed
+339 -45

No files matched your search

+95 -6
View File
@@ -1,9 +1,13 @@
#[cfg(target_os = "linux")]
use gpui::{Bounds, Decorations, Pixels, Size, point};
use gpui::{Window, WindowBackgroundAppearance};
use crate::settings::Settings;
#[cfg(target_os = "linux")]
mod wayland;
#[cfg(target_os = "linux")]
mod x11;
/// What the window shows of the desktop: blurred or clear while the background lets it through.
pub fn appearance(settings: &Settings) -> WindowBackgroundAppearance {
@@ -16,26 +20,34 @@ pub fn appearance(settings: &Settings) -> WindowBackgroundAppearance {
}
}
/// What the main window shows of the desktop, as last set, and on Wayland the blur behind its surface.
/// What the main window shows of the desktop, as last set, and on Linux the compositor's blur behind its pane.
pub struct Blur {
shown: WindowBackgroundAppearance,
#[cfg(target_os = "linux")]
wayland: Option<wayland::Effect>,
#[cfg(target_os = "linux")]
x11: Option<x11::Effect>,
}
impl Blur {
/// The window as `WindowOptions` opened it from these settings.
pub fn new(window: &Window, settings: &Settings) -> Self {
let shown = appearance(settings);
#[cfg(not(target_os = "linux"))]
let _ = window;
Self {
shown,
shown: appearance(settings),
#[cfg(target_os = "linux")]
wayland: wayland::Effect::on(window, shown == WindowBackgroundAppearance::Blurred),
wayland: wayland::Effect::on(window),
#[cfg(target_os = "linux")]
x11: x11::Effect::on(window),
}
}
/// Whether the desktop shows through the window.
pub fn see_through(&self) -> bool {
self.shown != WindowBackgroundAppearance::Opaque
}
/// Sets the window to what the settings ask when it differs from what it shows; logs the way taken.
pub fn follow(&mut self, window: &Window, settings: &Settings) {
let wanted = appearance(settings);
@@ -45,13 +57,65 @@ impl Blur {
self.shown = wanted;
window.set_background_appearance(wanted);
log::info!("blur: the window is {wanted:?} on {}", std::env::consts::OS);
}
/// Keeps a Linux compositor's blur on the window's pane while Blurred; each frame, sending only a change.
pub fn fit(&mut self, window: &Window) {
#[cfg(not(target_os = "linux"))]
let _ = window;
#[cfg(target_os = "linux")]
if let Some(effect) = &self.wayland {
effect.blur(wanted == WindowBackgroundAppearance::Blurred);
{
let blurred = self.shown == WindowBackgroundAppearance::Blurred;
let size = window.viewport_size();
let pane =
blurred.then(|| pane(size, window.client_inset(), window.window_decorations()));
if let Some(effect) = &mut self.wayland {
effect.blur(pane.map(|pane| rect(pane, 1.0)));
}
if let Some(effect) = &mut self.x11 {
effect.blur(pane.map(|pane| rect(pane, window.scale_factor())));
}
}
}
}
/// The window's pane in its surface: all of it, less the inset on each free edge of a client-drawn window, as Ely's `ResizeBorder` pads it.
#[cfg(target_os = "linux")]
fn pane(size: Size<Pixels>, inset: Option<Pixels>, decorations: Decorations) -> Bounds<Pixels> {
let mut pane = Bounds::new(point(Pixels::ZERO, Pixels::ZERO), size);
let (Decorations::Client { tiling }, Some(inset)) = (decorations, inset) else {
return pane;
};
if !tiling.top {
pane.origin.y += inset;
pane.size.height -= inset;
}
if !tiling.bottom {
pane.size.height -= inset;
}
if !tiling.left {
pane.origin.x += inset;
pane.size.width -= inset;
}
if !tiling.right {
pane.size.width -= inset;
}
pane.size = pane.size.max(&Size::default());
pane
}
/// `pane` in whole pixels at `scale`: x, y, width, height.
#[cfg(target_os = "linux")]
fn rect(pane: Bounds<Pixels>, scale: f32) -> [i32; 4] {
let whole = |length: Pixels| (f32::from(length) * scale).round() as i32;
[
whole(pane.origin.x),
whole(pane.origin.y),
whole(pane.size.width),
whole(pane.size.height),
]
}
#[cfg(test)]
mod tests {
use gpui::WindowBackgroundAppearance::{Blurred, Opaque, Transparent};
@@ -72,4 +136,29 @@ mod tests {
assert_eq!(at(0.88, true), Blurred);
assert_eq!(at(0.3, false), Transparent);
}
#[cfg(target_os = "linux")]
#[test]
fn the_pane_leaves_the_margin_on_each_free_edge() {
use gpui::{Tiling, px, size};
let surface = size(px(1220.), px(780.));
let inset = Some(px(10.));
let at = |decorations| rect(pane(surface, inset, decorations), 2.0);
assert_eq!(at(Decorations::Server), [0, 0, 2440, 1560]);
let floating = Decorations::Client {
tiling: Tiling::default(),
};
assert_eq!(at(floating), [20, 20, 2400, 1520]);
let left = Tiling {
left: true,
top: true,
bottom: true,
right: false,
};
let half = Decorations::Client { tiling: left };
assert_eq!(at(half), [0, 0, 2420, 1560]);
assert_eq!(rect(pane(surface, None, floating), 1.0), [0, 0, 1220, 780]);
let tiny = size(px(15.), px(15.));
assert_eq!(rect(pane(tiny, inset, floating), 1.0), [10, 10, 0, 0]);
}
}