aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-04-25 08:53:16 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-04-25 02:00:18 -0700
commit9571d149b2cecd3df8ba3f90f0af296e9f69af6e (patch)
tree185e7a30f094f5e0eb42400e7709e233e86c9bff
parent99358e36b38c1aa5cc0c0fb36a91ae7cb4a2e3ba (diff)
downloadniri-9571d149b2cecd3df8ba3f90f0af296e9f69af6e.tar.gz
niri-9571d149b2cecd3df8ba3f90f0af296e9f69af6e.tar.bz2
niri-9571d149b2cecd3df8ba3f90f0af296e9f69af6e.zip
Render workspaces separately with gaps between
This design makes more sense spatially, and is required for the Overview. Gaps also make it clear how clipping windows to workspace bounds works. Background and bottom layer-shell surfaces get duplicated for each workspace, while top and overlay stay "on top".
-rw-r--r--niri-config/src/lib.rs12
-rw-r--r--niri-visual-tests/src/cases/layout.rs1
-rw-r--r--src/layout/mod.rs2
-rw-r--r--src/layout/monitor.rs147
-rw-r--r--src/niri.rs156
5 files changed, 232 insertions, 86 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs
index d622bfcd..34a3b6fa 100644
--- a/niri-config/src/lib.rs
+++ b/niri-config/src/lib.rs
@@ -23,7 +23,8 @@ use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE};
use smithay::input::keyboard::{Keysym, XkbConfig};
use smithay::reexports::input;
-pub const DEFAULT_BACKGROUND_COLOR: Color = Color::from_array_unpremul([0.2, 0.2, 0.2, 1.]);
+pub const DEFAULT_BACKGROUND_COLOR: Color = Color::from_array_unpremul([0.25, 0.25, 0.25, 1.]);
+pub const DEFAULT_BACKDROP_COLOR: Color = Color::from_array_unpremul([0.15, 0.15, 0.15, 1.]);
pub mod layer_rule;
@@ -444,6 +445,8 @@ pub struct Output {
pub focus_at_startup: bool,
#[knuffel(child, default = DEFAULT_BACKGROUND_COLOR)]
pub background_color: Color,
+ #[knuffel(child, default = DEFAULT_BACKDROP_COLOR)]
+ pub backdrop_color: Color,
}
impl Output {
@@ -472,6 +475,7 @@ impl Default for Output {
mode: None,
variable_refresh_rate: None,
background_color: DEFAULT_BACKGROUND_COLOR,
+ backdrop_color: DEFAULT_BACKDROP_COLOR,
}
}
}
@@ -4127,6 +4131,12 @@ mod tests {
b: 0.4,
a: 1.0,
},
+ backdrop_color: Color {
+ r: 0.15,
+ g: 0.15,
+ b: 0.15,
+ a: 1.0,
+ },
},
],
),
diff --git a/niri-visual-tests/src/cases/layout.rs b/niri-visual-tests/src/cases/layout.rs
index 23f2bc8d..81748e8c 100644
--- a/niri-visual-tests/src/cases/layout.rs
+++ b/niri-visual-tests/src/cases/layout.rs
@@ -266,6 +266,7 @@ impl TestCase for Layout {
.monitor_for_output(&self.output)
.unwrap()
.render_elements(renderer, RenderTarget::Output, true)
+ .flat_map(|(_, iter)| iter)
.map(|elem| Box::new(elem) as _)
.collect()
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 040a5cb4..cdb96113 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -2279,7 +2279,7 @@ impl<W: LayoutElement> Layout<W> {
) -> Option<(&W, HitType)> {
if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {
if move_.output == *output {
- let tile_pos = move_.tile_render_location(1.);
+ let tile_pos = move_.tile_render_location();
HitType::hit_tile(&move_.tile, tile_pos, pos_within_output)
} else {
None
diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs
index 4c20d533..ab5384a8 100644
--- a/src/layout/monitor.rs
+++ b/src/layout/monitor.rs
@@ -25,7 +25,9 @@ use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::RenderTarget;
use crate::rubber_band::RubberBand;
use crate::utils::transaction::Transaction;
-use crate::utils::{output_size, ResizeEdge};
+use crate::utils::{
+ output_size, round_logical_in_physical, round_logical_in_physical_max1, ResizeEdge,
+};
/// Amount of touchpad movement to scroll the height of one workspace.
const WORKSPACE_GESTURE_MOVEMENT: f64 = 300.;
@@ -943,6 +945,23 @@ impl<W: LayoutElement> Monitor<W> {
self.active_workspace_ref().active_tile_visual_rectangle()
}
+ fn workspace_size(&self, zoom: f64) -> Size<f64, Logical> {
+ let ws_size = self.view_size.upscale(zoom);
+ let scale = self.scale.fractional_scale();
+ ws_size.to_physical_precise_ceil(scale).to_logical(scale)
+ }
+
+ fn workspace_gap(&self, zoom: f64) -> f64 {
+ let scale = self.scale.fractional_scale();
+ let gap = self.view_size.h * 0.1 * zoom;
+ round_logical_in_physical_max1(scale, gap)
+ }
+
+ fn workspace_size_with_gap(&self, zoom: f64) -> Size<f64, Logical> {
+ let gap = self.workspace_gap(zoom);
+ self.workspace_size(zoom) + Size::from((0., gap))
+ }
+
pub fn workspace_render_idx(&self) -> f64 {
if let Some(switch) = &self.workspace_switch {
switch.current_idx()
@@ -953,17 +972,19 @@ impl<W: LayoutElement> Monitor<W> {
pub fn workspaces_render_geo(&self) -> impl Iterator<Item = Rectangle<f64, Logical>> {
let scale = self.scale.fractional_scale();
- let size = self.view_size;
+ let zoom = 1.;
- // Ceil the workspace size in physical pixels.
- let ws_size = size.to_physical_precise_ceil(scale).to_logical(scale);
+ let ws_size = self.workspace_size(zoom);
+ let gap = self.workspace_gap(zoom);
+ let ws_height_with_gap = ws_size.h + gap;
- let first_ws_y = -self.workspace_render_idx() * ws_size.h;
+ let first_ws_y = -self.workspace_render_idx() * ws_height_with_gap;
+ let first_ws_y = round_logical_in_physical(scale, first_ws_y);
- (0..self.workspaces.len()).map(move |idx| {
- let y = first_ws_y + idx as f64 * ws_size.h;
+ // Return position for one-past-last workspace too.
+ (0..=self.workspaces.len()).map(move |idx| {
+ let y = first_ws_y + idx as f64 * ws_height_with_gap;
let loc = Point::from((0., y));
- let loc = loc.to_physical_precise_round(scale).to_logical(scale);
Rectangle::new(loc, ws_size)
})
}
@@ -1031,7 +1052,12 @@ impl<W: LayoutElement> Monitor<W> {
renderer: &'a mut R,
target: RenderTarget,
focus_ring: bool,
- ) -> impl Iterator<Item = MonitorRenderElement<R>> + 'a {
+ ) -> impl Iterator<
+ Item = (
+ Rectangle<f64, Logical>,
+ impl Iterator<Item = MonitorRenderElement<R>> + 'a,
+ ),
+ > {
let _span = tracy_client::span!("Monitor::render_elements");
let scale = self.scale.fractional_scale();
@@ -1072,45 +1098,45 @@ impl<W: LayoutElement> Monitor<W> {
}
}
- self.workspaces_with_render_geo()
- .flat_map(move |(ws, geo)| {
- let map_ws_contents = move |elem: WorkspaceRenderElement<R>| {
+ self.workspaces_with_render_geo().map(move |(ws, geo)| {
+ let map_ws_contents = move |elem: WorkspaceRenderElement<R>| {
+ let elem = CropRenderElement::from_element(elem, scale, crop_bounds)?;
+ let elem = MonitorInnerRenderElement::Workspace(elem);
+ Some(elem)
+ };
+
+ let (floating, scrolling) = ws.render_elements(renderer, target, focus_ring);
+ let floating = floating.filter_map(map_ws_contents);
+ let scrolling = scrolling.filter_map(map_ws_contents);
+
+ let hint = if matches!(insert_hint, Some((hint_ws_id, _)) if hint_ws_id == ws.id()) {
+ let iter = insert_hint.take().unwrap().1;
+ let iter = iter.filter_map(move |elem| {
let elem = CropRenderElement::from_element(elem, scale, crop_bounds)?;
- let elem = MonitorInnerRenderElement::Workspace(elem);
+ let elem = MonitorInnerRenderElement::InsertHint(elem);
Some(elem)
- };
-
- let (floating, scrolling) = ws.render_elements(renderer, target, focus_ring);
- let floating = floating.filter_map(map_ws_contents);
- let scrolling = scrolling.filter_map(map_ws_contents);
-
- let hint = if matches!(insert_hint, Some((hint_ws_id, _)) if hint_ws_id == ws.id())
- {
- let iter = insert_hint.take().unwrap().1;
- let iter = iter.filter_map(move |elem| {
- let elem = CropRenderElement::from_element(elem, scale, crop_bounds)?;
- let elem = MonitorInnerRenderElement::InsertHint(elem);
- Some(elem)
- });
- Some(iter)
- } else {
- None
- };
- let hint = hint.into_iter().flatten();
-
- let iter = floating.chain(hint).chain(scrolling);
-
- iter.map(move |elem| {
- RelocateRenderElement::from_element(
- elem,
- // The offset we get from workspaces_with_render_positions() is already
- // rounded to physical pixels, but it's in the logical coordinate
- // space, so we need to convert it to physical.
- geo.loc.to_physical_precise_round(scale),
- Relocate::Relative,
- )
- })
- })
+ });
+ Some(iter)
+ } else {
+ None
+ };
+ let hint = hint.into_iter().flatten();
+
+ let iter = floating.chain(hint).chain(scrolling);
+
+ let iter = iter.map(move |elem| {
+ RelocateRenderElement::from_element(
+ elem,
+ // The offset we get from workspaces_with_render_positions() is already
+ // rounded to physical pixels, but it's in the logical coordinate
+ // space, so we need to convert it to physical.
+ geo.loc.to_physical_precise_round(scale),
+ Relocate::Relative,
+ )
+ });
+
+ (geo, iter)
+ })
}
pub fn workspace_switch_gesture_begin(&mut self, is_touchpad: bool) {
@@ -1133,7 +1159,7 @@ impl<W: LayoutElement> Monitor<W> {
timestamp: Duration,
is_touchpad: bool,
) -> Option<bool> {
- let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else {
+ let Some(WorkspaceSwitch::Gesture(gesture)) = &self.workspace_switch else {
return None;
};
@@ -1141,13 +1167,18 @@ impl<W: LayoutElement> Monitor<W> {
return None;
}
- gesture.tracker.push(delta_y, timestamp);
-
let total_height = if gesture.is_touchpad {
WORKSPACE_GESTURE_MOVEMENT
} else {
- self.workspaces[0].view_size().h
+ self.workspace_size_with_gap(1.).h
+ };
+
+ let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else {
+ return None;
};
+
+ gesture.tracker.push(delta_y, timestamp);
+
let pos = gesture.tracker.pos() / total_height;
let (min, max) = gesture.min_max(self.workspaces.len());
@@ -1163,7 +1194,7 @@ impl<W: LayoutElement> Monitor<W> {
}
pub fn workspace_switch_gesture_end(&mut self, is_touchpad: Option<bool>) -> bool {
- let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else {
+ let Some(WorkspaceSwitch::Gesture(gesture)) = &self.workspace_switch else {
return false;
};
@@ -1171,16 +1202,20 @@ impl<W: LayoutElement> Monitor<W> {
return false;
}
- // Take into account any idle time between the last event and now.
- let now = self.clock.now_unadjusted();
- gesture.tracker.push(0., now);
-
let total_height = if gesture.is_touchpad {
WORKSPACE_GESTURE_MOVEMENT
} else {
- self.workspaces[0].view_size().h
+ self.workspace_size_with_gap(1.).h
+ };
+
+ let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else {
+ return false;
};
+ // Take into account any idle time between the last event and now.
+ let now = self.clock.now_unadjusted();
+ gesture.tracker.push(0., now);
+
let mut velocity = gesture.tracker.velocity() / total_height;
let current_pos = gesture.tracker.pos() / total_height;
let pos = gesture.tracker.projected_end_pos() / total_height;
diff --git a/src/niri.rs b/src/niri.rs
index 2543ddb3..de88da71 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -15,7 +15,7 @@ use anyhow::{bail, ensure, Context};
use calloop::futures::Scheduler;
use niri_config::{
Config, FloatOrInt, Key, Modifiers, OutputName, PreviewRender, TrackLayout,
- WarpMouseToFocusMode, WorkspaceReference, DEFAULT_BACKGROUND_COLOR,
+ WarpMouseToFocusMode, WorkspaceReference, DEFAULT_BACKDROP_COLOR, DEFAULT_BACKGROUND_COLOR,
};
use smithay::backend::allocator::Fourcc;
use smithay::backend::input::Keycode;
@@ -26,10 +26,12 @@ use smithay::backend::renderer::element::surface::{
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
};
use smithay::backend::renderer::element::utils::{
- select_dmabuf_feedback, Relocate, RelocateRenderElement,
+ select_dmabuf_feedback, CropRenderElement, Relocate, RelocateRenderElement,
+ RescaleRenderElement,
};
use smithay::backend::renderer::element::{
- default_primary_scanout_output_compare, Id, Kind, PrimaryScanoutOutput, RenderElementStates,
+ default_primary_scanout_output_compare, Element, Id, Kind, PrimaryScanoutOutput,
+ RenderElementStates,
};
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::backend::renderer::sync::SyncPoint;
@@ -428,6 +430,7 @@ pub struct OutputState {
/// Solid color buffer for the background that we use instead of clearing to avoid damage
/// tracking issues and make screenshots easier.
pub background_buffer: SolidColorBuffer,
+ pub backdrop_buffer: SolidColorBuffer,
pub lock_render_state: LockRenderState,
pub lock_surface: Option<LockSurface>,
pub lock_color_buffer: SolidColorBuffer,
@@ -1465,11 +1468,22 @@ impl State {
background_color[3] = 1.;
let background_color = Color32F::from(background_color);
+ let mut backdrop_color = config
+ .map(|c| c.backdrop_color)
+ .unwrap_or(DEFAULT_BACKDROP_COLOR)
+ .to_array_unpremul();
+ backdrop_color[3] = 1.;
+ let backdrop_color = Color32F::from(backdrop_color);
+
if let Some(state) = self.niri.output_state.get_mut(output) {
if state.background_buffer.color() != background_color {
state.background_buffer.set_color(background_color);
recolored_outputs.push(output.clone());
}
+ if state.backdrop_buffer.color() != backdrop_color {
+ state.backdrop_buffer.set_color(backdrop_color);
+ recolored_outputs.push(output.clone());
+ }
}
}
@@ -2640,6 +2654,12 @@ impl Niri {
.to_array_unpremul();
background_color[3] = 1.;
+ let mut backdrop_color = c
+ .map(|c| c.backdrop_color)
+ .unwrap_or(DEFAULT_BACKDROP_COLOR)
+ .to_array_unpremul();
+ backdrop_color[3] = 1.;
+
// FIXME: fix winit damage on other transforms.
if name.connector == "winit" {
transform = Transform::Flipped180;
@@ -2673,6 +2693,7 @@ impl Niri {
last_drm_sequence: None,
frame_callback_sequence: 0,
background_buffer: SolidColorBuffer::new(size, background_color),
+ backdrop_buffer: SolidColorBuffer::new(size, backdrop_color),
lock_render_state,
lock_surface: None,
lock_color_buffer: SolidColorBuffer::new(size, CLEAR_COLOR_LOCKED),
@@ -2777,6 +2798,7 @@ impl Niri {
if let Some(state) = self.output_state.get_mut(output) {
state.background_buffer.resize(output_size);
+ state.backdrop_buffer.resize(output_size);
state.lock_color_buffer.resize(output_size);
if let Some(lock_surface) = &state.lock_surface {
@@ -2894,11 +2916,18 @@ impl Niri {
layers
.layers_on(layer)
.rev()
- .find_map(|layer| {
- let layer_pos_within_output =
- layers.layer_geometry(layer).unwrap().loc.to_f64();
+ .find_map(|layer_surface| {
+ let mut layer_pos_within_output =
+ layers.layer_geometry(layer_surface).unwrap().loc.to_f64();
+
+ // Background and bottom layers move together with the workspaces.
+ let mon = self.layout.monitor_for_output(output)?;
+ let (_, geo) = mon.workspace_under(pos_within_output)?;
+ layer_pos_within_output += geo.loc;
+
let surface_type = WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE;
- layer.surface_under(pos_within_output - layer_pos_within_output, surface_type)
+ layer_surface
+ .surface_under(pos_within_output - layer_pos_within_output, surface_type)
})
.is_some()
};
@@ -3001,20 +3030,29 @@ impl Niri {
layers
.layers_on(layer)
.rev()
- .find_map(|layer| {
- let layer_pos_within_output =
- layers.layer_geometry(layer).unwrap().loc.to_f64();
+ .find_map(|layer_surface| {
+ let mut layer_pos_within_output =
+ layers.layer_geometry(layer_surface).unwrap().loc.to_f64();
+
+ // Background and bottom layers move together with the workspaces.
+ if matches!(layer, Layer::Background | Layer::Bottom) {
+ let mon = self.layout.monitor_for_output(output)?;
+ let (_, geo) = mon.workspace_under(pos_within_output)?;
+ layer_pos_within_output += geo.loc;
+ }
+
let surface_type = if popup {
WindowSurfaceType::POPUP
} else {
WindowSurfaceType::TOPLEVEL
} | WindowSurfaceType::SUBSURFACE;
- layer
+
+ layer_surface
.surface_under(pos_within_output - layer_pos_within_output, surface_type)
.map(|(surface, pos_within_layer)| {
(
(surface, pos_within_layer.to_f64() + layer_pos_within_output),
- layer,
+ layer_surface,
)
})
})
@@ -3791,10 +3829,18 @@ impl Niri {
return elements;
}
- // Prepare the background element.
+ // Prepare the background elements.
let state = self.output_state.get(output).unwrap();
+ let background_buffer = state.background_buffer.clone();
let background = SolidColorRenderElement::from_buffer(
- &state.background_buffer,
+ &background_buffer,
+ (0, 0),
+ output_scale,
+ 1.,
+ Kind::Unspecified,
+ );
+ let backdrop = SolidColorRenderElement::from_buffer(
+ &state.backdrop_buffer,
(0, 0),
output_scale,
1.,
@@ -3811,8 +3857,8 @@ impl Niri {
.map(OutputRenderElements::from),
);
- // Add the background for outputs that were connected while the screenshot UI was open.
- elements.push(background);
+ // Add the backdrop for outputs that were connected while the screenshot UI was open.
+ elements.push(backdrop);
if self.debug_draw_opaque_regions {
draw_opaque_regions(&mut elements, output_scale);
@@ -3831,7 +3877,11 @@ impl Niri {
// Get monitor elements.
let mon = self.layout.monitor_for_output(output).unwrap();
- let monitor_elements: Vec<_> = mon.render_elements(renderer, target, focus_ring).collect();
+ let zoom = 1.;
+ let monitor_elements = Vec::from_iter(
+ mon.render_elements(renderer, target, focus_ring)
+ .map(|(geo, iter)| (geo, Vec::from_iter(iter))),
+ );
let int_move_elements: Vec<_> = self
.layout
.render_interactive_move_for_output(renderer, output, target)
@@ -3854,24 +3904,31 @@ impl Niri {
extend_from_layer(&mut layer_elems, Layer::Top);
let top_layer = layer_elems;
- // Collect all other layer-shell elements.
- let mut layer_elems = SplitElements::default();
- extend_from_layer(&mut layer_elems, Layer::Bottom);
- extend_from_layer(&mut layer_elems, Layer::Background);
-
// When rendering above the top layer, we put the regular monitor elements first.
// Otherwise, we will render all layer-shell pop-ups and the top layer on top.
if mon.render_above_top_layer() {
+ // Collect all other layer-shell elements.
+ let mut layer_elems = SplitElements::default();
+ extend_from_layer(&mut layer_elems, Layer::Bottom);
+ extend_from_layer(&mut layer_elems, Layer::Background);
+
elements.extend(
int_move_elements
.into_iter()
.map(OutputRenderElements::from),
);
- elements.extend(monitor_elements.into_iter().map(OutputRenderElements::from));
+ elements.extend(
+ monitor_elements
+ .into_iter()
+ .flat_map(|(_ws_geo, iter)| iter)
+ .map(OutputRenderElements::from),
+ );
elements.extend(top_layer.into_iter().map(OutputRenderElements::from));
elements.extend(layer_elems.popups.drain(..).map(OutputRenderElements::from));
elements.extend(layer_elems.normal.drain(..).map(OutputRenderElements::from));
+
+ elements.push(OutputRenderElements::from(background));
} else {
elements.extend(top_layer.into_iter().map(OutputRenderElements::from));
@@ -3881,15 +3938,40 @@ impl Niri {
.map(OutputRenderElements::from),
);
- elements.extend(layer_elems.popups.drain(..).map(OutputRenderElements::from));
+ for (ws_geo, ws_elements) in monitor_elements {
+ // Collect all other layer-shell elements.
+ let mut layer_elems = SplitElements::default();
+ extend_from_layer(&mut layer_elems, Layer::Bottom);
+ extend_from_layer(&mut layer_elems, Layer::Background);
+
+ elements.extend(
+ layer_elems
+ .popups
+ .into_iter()
+ .filter_map(|elem| scale_relocate_crop(elem, output_scale, zoom, ws_geo))
+ .map(OutputRenderElements::from),
+ );
- elements.extend(monitor_elements.into_iter().map(OutputRenderElements::from));
+ elements.extend(ws_elements.into_iter().map(OutputRenderElements::from));
- elements.extend(layer_elems.normal.drain(..).map(OutputRenderElements::from));
+ elements.extend(
+ layer_elems
+ .normal
+ .into_iter()
+ .filter_map(|elem| scale_relocate_crop(elem, output_scale, zoom, ws_geo))
+ .map(OutputRenderElements::from),
+ );
+
+ if let Some(elem) =
+ scale_relocate_crop(background.clone(), output_scale, zoom, ws_geo)
+ {
+ elements.push(OutputRenderElements::from(elem));
+ }
+ }
}
- // Then the background.
- elements.push(background);
+ // Then the backdrop.
+ elements.push(backdrop);
if self.debug_draw_opaque_regions {
draw_opaque_regions(&mut elements, output_scale);
@@ -5712,14 +5794,32 @@ impl ClientData for ClientState {
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}
}
+fn scale_relocate_crop<E: Element>(
+ elem: E,
+ output_scale: Scale<f64>,
+ zoom: f64,
+ ws_geo: Rectangle<f64, Logical>,
+) -> Option<CropRenderElement<RelocateRenderElement<RescaleRenderElement<E>>>> {
+ let ws_geo = ws_geo.to_physical_precise_round(output_scale);
+ let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), zoom);
+ let elem = RelocateRenderElement::from_element(elem, ws_geo.loc, Relocate::Relative);
+ CropRenderElement::from_element(elem, output_scale, ws_geo)
+}
+
niri_render_elements! {
OutputRenderElements<R> => {
Monitor = MonitorRenderElement<R>,
Tile = TileRenderElement<R>,
LayerSurface = LayerSurfaceRenderElement<R>,
+ RelocatedLayerSurface = CropRenderElement<RelocateRenderElement<RescaleRenderElement<
+ LayerSurfaceRenderElement<R>
+ >>>,
Wayland = WaylandSurfaceRenderElement<R>,
NamedPointer = MemoryRenderBufferRenderElement<R>,
SolidColor = SolidColorRenderElement,
+ RelocatedSolidColor = CropRenderElement<RelocateRenderElement<RescaleRenderElement<
+ SolidColorRenderElement
+ >>>,
ScreenshotUi = ScreenshotUiRenderElement,
Texture = PrimaryGpuTextureRenderElement,
// Used for the CPU-rendered panels.