Blade window transparency (#10973)

Release Notes:

- N/A

Following up to #10880
TODO:
- [x] create window as transparent
  - [x] X11
  - [x] Wayland
  - [ ] Windows
  - [x] MacOS (when used with Blade)  
- [x] enable GPU surface transparency
- [x] adjust the pipeline blend modes
- [x] adjust shader outputs


![transparency2](https://github.com/zed-industries/zed/assets/107301/d554a41b-5d3f-4420-a857-c64c1747c2d5)

Blurred results from @jansol (on Wayland), who contributed to this work:


![zed-blur](https://github.com/zed-industries/zed/assets/107301/a6822171-2dcf-4109-be55-b75557c586de)

---------

Co-authored-by: Jan Solanti <jhs@psonet.com>
This commit is contained in:
Dzmitry Malyshau
2024-05-06 09:53:08 -07:00
committed by GitHub
co-authored by Jan Solanti
parent 056c785f4e
commit e4f13dd561
11 changed files with 343 additions and 127 deletions
@@ -24,7 +24,7 @@ use wayland_client::protocol::wl_callback::{self, WlCallback};
use wayland_client::protocol::wl_data_device_manager::DndAction;
use wayland_client::protocol::wl_pointer::{AxisRelativeDirection, AxisSource};
use wayland_client::protocol::{
wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source, wl_output,
wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source, wl_output, wl_region,
};
use wayland_client::{
delegate_noop,
@@ -47,6 +47,7 @@ use wayland_protocols::xdg::decoration::zv1::client::{
zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
};
use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};
use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
@@ -82,6 +83,7 @@ pub struct Globals {
pub fractional_scale_manager:
Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
pub blur_manager: Option<org_kde_kwin_blur_manager::OrgKdeKwinBlurManager>,
pub executor: ForegroundExecutor,
}
@@ -114,6 +116,7 @@ impl Globals {
viewporter: globals.bind(&qh, 1..=1, ()).ok(),
fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
blur_manager: globals.bind(&qh, 1..=1, ()).ok(),
executor,
qh,
}
@@ -557,8 +560,11 @@ delegate_noop!(WaylandClientStatePtr: ignore wl_data_device_manager::WlDataDevic
delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm);
delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool);
delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
delegate_noop!(WaylandClientStatePtr: ignore wl_region::WlRegion);
delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur_manager::OrgKdeKwinBlurManager);
delegate_noop!(WaylandClientStatePtr: ignore org_kde_kwin_blur::OrgKdeKwinBlur);
delegate_noop!(WaylandClientStatePtr: ignore wp_viewporter::WpViewporter);
delegate_noop!(WaylandClientStatePtr: ignore wp_viewport::WpViewport);
@@ -11,6 +11,7 @@ use collections::{HashMap, HashSet};
use futures::channel::oneshot::Receiver;
use raw_window_handle as rwh;
use wayland_backend::client::ObjectId;
use wayland_client::protocol::wl_region::WlRegion;
use wayland_client::WEnum;
use wayland_client::{protocol::wl_surface, Proxy};
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1;
@@ -18,8 +19,9 @@ use wayland_protocols::wp::viewporter::client::wp_viewport;
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
use wayland_protocols::xdg::shell::client::xdg_surface;
use wayland_protocols::xdg::shell::client::xdg_toplevel::{self, WmCapabilities};
use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};
use crate::platform::blade::BladeRenderer;
use crate::platform::blade::{BladeRenderer, BladeSurfaceConfig};
use crate::platform::linux::wayland::display::WaylandDisplay;
use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
use crate::scene::Scene;
@@ -67,6 +69,7 @@ pub struct WaylandWindowState {
acknowledged_first_configure: bool,
pub surface: wl_surface::WlSurface,
decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
toplevel: xdg_toplevel::XdgToplevel,
viewport: Option<wp_viewport::WpViewport>,
outputs: HashSet<ObjectId>,
@@ -124,10 +127,13 @@ impl WaylandWindowState {
}
.unwrap(),
);
let extent = gpu::Extent {
width: bounds.size.width,
height: bounds.size.height,
depth: 1,
let config = BladeSurfaceConfig {
size: gpu::Extent {
width: bounds.size.width,
height: bounds.size.height,
depth: 1,
},
transparent: options.window_background != WindowBackgroundAppearance::Opaque,
};
Self {
@@ -135,13 +141,12 @@ impl WaylandWindowState {
acknowledged_first_configure: false,
surface,
decoration,
blur: None,
toplevel,
viewport,
globals,
outputs: HashSet::default(),
renderer: BladeRenderer::new(gpu, extent),
renderer: BladeRenderer::new(gpu, config),
bounds,
scale: 1.0,
input_handler: None,
@@ -166,6 +171,9 @@ impl Drop for WaylandWindow {
if let Some(decoration) = &state.decoration {
decoration.destroy();
}
if let Some(blur) = &state.blur {
blur.release();
}
state.toplevel.destroy();
if let Some(viewport) = &state.viewport {
viewport.destroy();
@@ -615,8 +623,44 @@ impl PlatformWindow for WaylandWindow {
self.borrow_mut().toplevel.set_app_id(app_id.to_owned());
}
fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
// todo(linux)
fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
let opaque = background_appearance == WindowBackgroundAppearance::Opaque;
let mut state = self.borrow_mut();
state.renderer.update_transparency(!opaque);
let region = state
.globals
.compositor
.create_region(&state.globals.qh, ());
region.add(0, 0, i32::MAX, i32::MAX);
if opaque {
// Promise the compositor that this region of the window surface
// contains no transparent pixels. This allows the compositor to
// do skip whatever is behind the surface for better performance.
state.surface.set_opaque_region(Some(&region));
} else {
state.surface.set_opaque_region(None);
}
if let Some(ref blur_manager) = state.globals.blur_manager {
if (background_appearance == WindowBackgroundAppearance::Blurred) {
if (state.blur.is_none()) {
let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
blur.set_region(Some(&region));
state.blur = Some(blur);
}
state.blur.as_ref().unwrap().commit();
} else {
// It probably doesn't hurt to clear the blur for opaque windows
blur_manager.unset(&state.surface);
if let Some(b) = state.blur.take() {
b.release()
}
}
}
region.destroy();
}
fn set_edited(&mut self, edited: bool) {
+151 -36
View File
@@ -2,20 +2,22 @@
#![allow(unused)]
use crate::{
platform::blade::BladeRenderer, size, Bounds, DevicePixels, ForegroundExecutor, Modifiers,
Pixels, Platform, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
PlatformWindow, Point, PromptLevel, Scene, Size, WindowAppearance, WindowBackgroundAppearance,
WindowOptions, WindowParams, X11Client, X11ClientState, X11ClientStatePtr,
platform::blade::{BladeRenderer, BladeSurfaceConfig},
size, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels, Platform, PlatformAtlas,
PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel,
Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowOptions, WindowParams,
X11Client, X11ClientState, X11ClientStatePtr,
};
use blade_graphics as gpu;
use parking_lot::Mutex;
use raw_window_handle as rwh;
use util::ResultExt;
use x11rb::{
connection::Connection,
connection::{Connection as _, RequestConnection as _},
protocol::{
render::{self, ConnectionExt as _},
xinput,
xproto::{self, ConnectionExt as _, CreateWindowAux},
xproto::{self, ConnectionExt as _},
},
resource_manager::Database,
wrapper::ConnectionExt,
@@ -24,6 +26,7 @@ use x11rb::{
use std::{
cell::{Ref, RefCell, RefMut},
collections::HashMap,
ffi::c_void,
iter::Zip,
mem,
@@ -61,6 +64,76 @@ fn query_render_extent(xcb_connection: &XCBConnection, x_window: xproto::Window)
}
}
#[derive(Debug)]
struct Visual {
id: xproto::Visualid,
colormap: u32,
depth: u8,
}
struct VisualSet {
inherit: Visual,
opaque: Option<Visual>,
transparent: Option<Visual>,
root: u32,
black_pixel: u32,
}
fn find_visuals(xcb_connection: &XCBConnection, screen_index: usize) -> VisualSet {
let screen = &xcb_connection.setup().roots[screen_index];
let mut set = VisualSet {
inherit: Visual {
id: screen.root_visual,
colormap: screen.default_colormap,
depth: screen.root_depth,
},
opaque: None,
transparent: None,
root: screen.root,
black_pixel: screen.black_pixel,
};
for depth_info in screen.allowed_depths.iter() {
for visual_type in depth_info.visuals.iter() {
let visual = Visual {
id: visual_type.visual_id,
colormap: 0,
depth: depth_info.depth,
};
log::debug!("Visual id: {}, class: {:?}, depth: {}, bits_per_value: {}, masks: 0x{:x} 0x{:x} 0x{:x}",
visual_type.visual_id,
visual_type.class,
depth_info.depth,
visual_type.bits_per_rgb_value,
visual_type.red_mask, visual_type.green_mask, visual_type.blue_mask,
);
if (
visual_type.red_mask,
visual_type.green_mask,
visual_type.blue_mask,
) != (0xFF0000, 0xFF00, 0xFF)
{
continue;
}
let color_mask = visual_type.red_mask | visual_type.green_mask | visual_type.blue_mask;
let alpha_mask = color_mask as usize ^ ((1usize << depth_info.depth) - 1);
if alpha_mask == 0 {
if set.opaque.is_none() {
set.opaque = Some(visual);
}
} else {
if set.transparent.is_none() {
set.transparent = Some(visual);
}
}
}
}
set
}
struct RawWindow {
connection: *mut c_void,
screen_id: usize,
@@ -90,7 +163,6 @@ pub(crate) struct X11WindowState {
scale_factor: f32,
renderer: BladeRenderer,
display: Rc<dyn PlatformDisplay>,
input_handler: Option<PlatformInputHandler>,
}
@@ -106,7 +178,8 @@ pub(crate) struct X11WindowStatePtr {
impl rwh::HasWindowHandle for RawWindow {
fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
let non_zero = NonZeroU32::new(self.window_id).unwrap();
let handle = rwh::XcbWindowHandle::new(non_zero);
let mut handle = rwh::XcbWindowHandle::new(non_zero);
handle.visual_id = NonZeroU32::new(self.visual_id);
Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
}
}
@@ -144,39 +217,77 @@ impl X11WindowState {
let x_screen_index = params
.display_id
.map_or(x_main_screen_index, |did| did.0 as usize);
let screen = xcb_connection.setup().roots.get(x_screen_index).unwrap();
let win_aux = xproto::CreateWindowAux::new().event_mask(
xproto::EventMask::EXPOSURE
| xproto::EventMask::STRUCTURE_NOTIFY
| xproto::EventMask::ENTER_WINDOW
| xproto::EventMask::LEAVE_WINDOW
| xproto::EventMask::FOCUS_CHANGE
| xproto::EventMask::KEY_PRESS
| xproto::EventMask::KEY_RELEASE
| xproto::EventMask::BUTTON_PRESS
| xproto::EventMask::BUTTON_RELEASE
| xproto::EventMask::POINTER_MOTION
| xproto::EventMask::BUTTON1_MOTION
| xproto::EventMask::BUTTON2_MOTION
| xproto::EventMask::BUTTON3_MOTION
| xproto::EventMask::BUTTON_MOTION,
);
let visual_set = find_visuals(&xcb_connection, x_screen_index);
let visual_maybe = match params.window_background {
WindowBackgroundAppearance::Opaque => visual_set.opaque,
WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred => {
visual_set.transparent
}
};
let visual = match visual_maybe {
Some(visual) => visual,
None => {
log::warn!(
"Unable to find a matching visual for {:?}",
params.window_background
);
visual_set.inherit
}
};
log::info!("Using {:?}", visual);
let colormap = if visual.colormap != 0 {
visual.colormap
} else {
let id = xcb_connection.generate_id().unwrap();
log::info!("Creating colormap {}", id);
xcb_connection
.create_colormap(xproto::ColormapAlloc::NONE, id, visual_set.root, visual.id)
.unwrap()
.check()
.unwrap();
id
};
let win_aux = xproto::CreateWindowAux::new()
.background_pixel(x11rb::NONE)
// https://stackoverflow.com/questions/43218127/x11-xlib-xcb-creating-a-window-requires-border-pixel-if-specifying-colormap-wh
.border_pixel(visual_set.black_pixel)
.colormap(colormap)
.event_mask(
xproto::EventMask::EXPOSURE
| xproto::EventMask::STRUCTURE_NOTIFY
| xproto::EventMask::ENTER_WINDOW
| xproto::EventMask::LEAVE_WINDOW
| xproto::EventMask::FOCUS_CHANGE
| xproto::EventMask::KEY_PRESS
| xproto::EventMask::KEY_RELEASE
| xproto::EventMask::BUTTON_PRESS
| xproto::EventMask::BUTTON_RELEASE
| xproto::EventMask::POINTER_MOTION
| xproto::EventMask::BUTTON1_MOTION
| xproto::EventMask::BUTTON2_MOTION
| xproto::EventMask::BUTTON3_MOTION
| xproto::EventMask::BUTTON_MOTION,
);
xcb_connection
.create_window(
x11rb::COPY_FROM_PARENT as _,
visual.depth,
x_window,
screen.root,
visual_set.root,
params.bounds.origin.x.0 as i16,
params.bounds.origin.y.0 as i16,
params.bounds.size.width.0 as u16,
params.bounds.size.height.0 as u16,
0,
xproto::WindowClass::INPUT_OUTPUT,
screen.root_visual,
visual.id,
&win_aux,
)
.unwrap()
.check()
.unwrap();
xinput::ConnectionExt::xinput_xi_select_events(
@@ -224,7 +335,7 @@ impl X11WindowState {
) as *mut _,
screen_id: x_screen_index,
window_id: x_window,
visual_id: screen.root_visual,
visual_id: visual.id,
};
let gpu = Arc::new(
unsafe {
@@ -240,9 +351,12 @@ impl X11WindowState {
.unwrap(),
);
// Note: this has to be done after the GPU init, or otherwise
// the sizes are immediately invalidated.
let gpu_extent = query_render_extent(xcb_connection, x_window);
let config = BladeSurfaceConfig {
// Note: this has to be done after the GPU init, or otherwise
// the sizes are immediately invalidated.
size: query_render_extent(xcb_connection, x_window),
transparent: params.window_background != WindowBackgroundAppearance::Opaque,
};
Self {
client,
@@ -251,9 +365,8 @@ impl X11WindowState {
raw,
bounds: params.bounds.map(|v| v.0),
scale_factor,
renderer: BladeRenderer::new(gpu, gpu_extent),
renderer: BladeRenderer::new(gpu, config),
atoms: *atoms,
input_handler: None,
}
}
@@ -533,8 +646,10 @@ impl PlatformWindow for X11Window {
// todo(linux)
fn set_edited(&mut self, edited: bool) {}
fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
// todo(linux)
fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
let mut inner = self.0.state.borrow_mut();
let transparent = background_appearance != WindowBackgroundAppearance::Opaque;
inner.renderer.update_transparency(transparent);
}
// todo(linux), this corresponds to `orderFrontCharacterPalette` on macOS,