Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
src/server/scene.rs (14.8K)
1 // SPDX-FileCopyrightText: © 2024 The River Developers
2 // SPDX-License-Identifier: GPL-3.0-only
3
4 use crate::ffi;
5 use crate::scene_node_data::{SceneNodeData, SceneNodeDataVal};
6
7 pub struct SceneLayers {
8 pub background: *mut ffi::wlr_scene_tree,
9 /// Client-provided backgrounds — wlr-layer-shell Background surfaces and
10 /// the `cce-wallpaper` window — inside `background`, ABOVE each output's
11 /// base rect and grid backdrop and BELOW its grid cells (`Output::draw_grid`
12 /// keeps the backdrop under this tree and the cell tree above it). A client
13 /// wallpaper therefore replaces the flat backdrop colour and keeps the cell
14 /// lattice; before this tree the grid tree, re-raised on every redraw, buried
15 /// every client background under its opaque backdrop.
16 pub background_clients: *mut ffi::wlr_scene_tree,
17 pub bottom: *mut ffi::wlr_scene_tree,
18 pub wm: *mut ffi::wlr_scene_tree,
19 pub top: *mut ffi::wlr_scene_tree,
20 pub fullscreen: *mut ffi::wlr_scene_tree,
21 pub overlay: *mut ffi::wlr_scene_tree,
22 pub popups: *mut ffi::wlr_scene_tree,
23 pub override_redirect: *mut ffi::wlr_scene_tree,
24 /// Hover-revealed window borders. Borders draw outside the content box, so
25 /// with the content filling its grid cell they overhang into the gap and
26 /// over the neighbouring window. Hosting them above every other layer
27 /// keeps a revealed edge visible instead of letting the neighbour occlude
28 /// it. Each window parents its own `border_tree` here.
29 pub border_overlay: *mut ffi::wlr_scene_tree,
30 }
31
32 pub struct Scene {
33 pub wlr_scene: *mut ffi::wlr_scene,
34 pub interactive_tree: *mut ffi::wlr_scene_tree,
35 pub drag_icons: *mut ffi::wlr_scene_tree,
36 pub hidden_tree: *mut ffi::wlr_scene_tree,
37 pub normal_tree: *mut ffi::wlr_scene_tree,
38 pub locked_tree: *mut ffi::wlr_scene_tree,
39 pub layers: SceneLayers,
40 }
41
42 impl Scene {
43 pub fn new() -> Self {
44 Self {
45 wlr_scene: std::ptr::null_mut(),
46 interactive_tree: std::ptr::null_mut(),
47 drag_icons: std::ptr::null_mut(),
48 hidden_tree: std::ptr::null_mut(),
49 normal_tree: std::ptr::null_mut(),
50 locked_tree: std::ptr::null_mut(),
51 layers: SceneLayers {
52 background: std::ptr::null_mut(),
53 background_clients: std::ptr::null_mut(),
54 bottom: std::ptr::null_mut(),
55 wm: std::ptr::null_mut(),
56 top: std::ptr::null_mut(),
57 fullscreen: std::ptr::null_mut(),
58 overlay: std::ptr::null_mut(),
59 popups: std::ptr::null_mut(),
60 override_redirect: std::ptr::null_mut(),
61 border_overlay: std::ptr::null_mut(),
62 },
63 }
64 }
65
66 pub unsafe fn init(
67 &mut self,
68 linux_dmabuf: *mut ffi::wlr_linux_dmabuf_v1,
69 _color_manager: *mut ffi::wlr_color_manager_v1,
70 ) -> Result<(), &'static str> {
71 let wlr_scene = ffi::wlr_scene_create();
72 if wlr_scene.is_null() {
73 return Err("Failed to create wlr_scene");
74 }
75 self.wlr_scene = wlr_scene;
76
77 if !linux_dmabuf.is_null() {
78 ffi::wlr_scene_set_linux_dmabuf_v1(wlr_scene, linux_dmabuf);
79 }
80 // SceneFX 0.4 does not support set_color_manager_v1
81 // if !color_manager.is_null() {
82 // ffi::wlr_scene_set_color_manager_v1(wlr_scene, color_manager);
83 // }
84
85 ffi::wlr_scene_set_blur_data(wlr_scene, 3, 5, 0.0, 1.0, 1.0, 1.0);
86
87 let interactive_tree = ffi::wlr_scene_tree_create(&mut (*wlr_scene).tree);
88 let drag_icons = ffi::wlr_scene_tree_create(&mut (*wlr_scene).tree);
89 let hidden_tree = ffi::wlr_scene_tree_create(&mut (*wlr_scene).tree);
90 if interactive_tree.is_null() || drag_icons.is_null() || hidden_tree.is_null() {
91 return Err("Failed to create root scene trees");
92 }
93 self.interactive_tree = interactive_tree;
94 self.drag_icons = drag_icons;
95 self.hidden_tree = hidden_tree;
96
97 ffi::wlr_scene_node_set_enabled(hidden_tree as *mut ffi::wlr_scene_node, false);
98
99 let normal_tree = ffi::wlr_scene_tree_create(interactive_tree);
100 let locked_tree = ffi::wlr_scene_tree_create(interactive_tree);
101 if normal_tree.is_null() || locked_tree.is_null() {
102 return Err("Failed to create normal/locked scene trees");
103 }
104 self.normal_tree = normal_tree;
105 self.locked_tree = locked_tree;
106
107 ffi::wlr_scene_node_set_enabled(locked_tree as *mut ffi::wlr_scene_node, false);
108
109 self.layers.background = ffi::wlr_scene_tree_create(normal_tree);
110 self.layers.background_clients = ffi::wlr_scene_tree_create(self.layers.background);
111 self.layers.bottom = ffi::wlr_scene_tree_create(normal_tree);
112 self.layers.wm = ffi::wlr_scene_tree_create(normal_tree);
113 self.layers.top = ffi::wlr_scene_tree_create(normal_tree);
114 self.layers.fullscreen = ffi::wlr_scene_tree_create(normal_tree);
115 self.layers.overlay = ffi::wlr_scene_tree_create(normal_tree);
116 // Window decorations (the resize ring) sit above every window but
117 // BELOW popups: a cce-ui dropdown is a separate Popup-mode window in
118 // layers.popups, and a menu must never be drawn under the chrome of
119 // the window that opened it. Creation order is stacking order.
120 self.layers.border_overlay = ffi::wlr_scene_tree_create(normal_tree);
121 self.layers.popups = ffi::wlr_scene_tree_create(normal_tree);
122 self.layers.override_redirect = ffi::wlr_scene_tree_create(normal_tree);
123
124 // Desk content renders with the camera's sub-pixel offset
125 // (`WindowManager::layout_camera`): windows, their borders, their
126 // popups and X11 menus, and the grid (flagged where it is built).
127 // Layer shells, fullscreen and the lock screen stay put.
128 for tree in [self.layers.wm, self.layers.border_overlay, self.layers.popups, self.layers.override_redirect] {
129 if !tree.is_null() {
130 ffi::river_scene_tree_set_desk_offset(tree, true);
131 }
132 }
133
134 if self.layers.border_overlay.is_null()
135 || self.layers.background.is_null()
136 || self.layers.background_clients.is_null()
137 || self.layers.bottom.is_null()
138 || self.layers.wm.is_null()
139 || self.layers.top.is_null()
140 || self.layers.fullscreen.is_null()
141 || self.layers.overlay.is_null()
142 || self.layers.popups.is_null()
143 || self.layers.override_redirect.is_null()
144 {
145 return Err("Failed to create scene layer trees");
146 }
147
148 Ok(())
149 }
150
151 pub unsafe fn deinit(&mut self) {}
152
153 pub unsafe fn at(&self, lx: f64, ly: f64) -> Option<AtResult> {
154 self.at_impl(lx, ly, true)
155 }
156
157 unsafe fn at_impl(&self, lx: f64, ly: f64, include_grid: bool) -> Option<AtResult> {
158 let mut disabled_nodes = Vec::new();
159 let mut result = None;
160
161 loop {
162 let mut sx: f64 = 0.0;
163 let mut sy: f64 = 0.0;
164 let node = ffi::wlr_scene_node_at(
165 self.interactive_tree as *mut ffi::wlr_scene_node,
166 lx,
167 ly,
168 &mut sx,
169 &mut sy,
170 );
171
172 if node.is_null() {
173 break;
174 }
175
176 if let Some(scene_node_data) = SceneNodeData::from_node(node) {
177 if let SceneNodeDataVal::Window(window) = scene_node_data.data {
178 // The grid layer used to be skipped here to keep it
179 // input-transparent. It now advertises an input region
180 // covering exactly its desktop items, so wlr_scene_node_at
181 // already misses it over bare canvas and every other input
182 // path (clicks, hover, overview background-exit) still sees
183 // through it — while a click ON an item reaches the client
184 // that owns it. The parameter stays for the callers that
185 // must never see the grid at all.
186 if !include_grid && (*window).is_grid() {
187 let tree_node = (*window).tree as *mut ffi::wlr_scene_node;
188 ffi::wlr_scene_node_set_enabled(tree_node, false);
189 disabled_nodes.push(tree_node);
190 continue;
191 }
192 if (*window).rendering_requested.circular {
193 // Check if outside the circle
194 let w = (*window).box_geom.width as f64 * (*window).scale;
195 let h = (*window).box_geom.height as f64 * (*window).scale;
196 let cx = (*window).box_geom.x as f64 + w / 2.0;
197 let cy = (*window).box_geom.y as f64 + h / 2.0;
198 let r = w.min(h) / 2.0;
199 let dx = lx - cx;
200 let dy = ly - cy;
201 if dx * dx + dy * dy > r * r {
202 // Outside the circle! Disable the window tree node temporarily and try again.
203 let tree_node = (*window).tree as *mut ffi::wlr_scene_node;
204 ffi::wlr_scene_node_set_enabled(tree_node, false);
205 disabled_nodes.push(tree_node);
206 continue;
207 }
208 }
209 }
210
211 let surface = ffi::river_scene_node_get_surface(node);
212 // An X11 surface under xwayland_hidpi is a physical-pixel
213 // buffer drawn at 1/scale, and wlr_scene_node_at maps the
214 // point through the buffer's CURRENT dest size. That size
215 // is reset to natural on every commit and restored by the
216 // commit hook — but any other writer of dest sizes (a
217 // transaction's frozen copy, a fullscreen tick) opens the
218 // same gap, in which a pointer event reaches the client at
219 // half its coordinates: Houdini's hover jumping up-left for
220 // a frame. Derive the surface point from what is INVARIANT
221 // instead — the node's layout origin and the scale the
222 // buffer is meant to be shown at — so input never depends
223 // on the dest state.
224 let (mut sx, mut sy) = (sx, sy);
225 if !surface.is_null() {
226 let ratio: Option<f64> = match scene_node_data.data {
227 SceneNodeDataVal::Window(window)
228 if !window.is_null()
229 && matches!((*window).impl_type, crate::window::WindowImpl::Xwayland(_)) =>
230 {
231 let xsurface = match (*window).impl_type {
232 crate::window::WindowImpl::Xwayland(xw) if !xw.is_null() => (*xw).xsurface as *const _,
233 _ => std::ptr::null(),
234 };
235 let s = crate::xwayland_window::x11_scale_for((*window).server, xsurface) as f64;
236 let zoom = if (*window).scale > 0.0 { (*window).scale } else { 1.0 };
237 (s != 1.0).then_some(s / zoom)
238 }
239 SceneNodeDataVal::OverrideRedirect(or) if !or.is_null() => {
240 let s = crate::xwayland_window::x11_scale_for((*or).server, (*or).xsurface) as f64;
241 (s != 1.0).then_some(s)
242 }
243 _ => None,
244 };
245 if let Some(ratio) = ratio {
246 let (mut nx, mut ny) = (0, 0);
247 if ffi::wlr_scene_node_coords(node, &mut nx, &mut ny) {
248 sx = (lx - nx as f64) * ratio;
249 sy = (ly - ny as f64) * ratio;
250 }
251 }
252 }
253 result = Some(AtResult {
254 node,
255 surface,
256 sx,
257 sy,
258 data: scene_node_data.data,
259 });
260 break;
261 } else {
262 break;
263 }
264 }
265
266 // Re-enable all disabled nodes
267 for node in disabled_nodes {
268 ffi::wlr_scene_node_set_enabled(node, true);
269 }
270
271 result
272 }
273
274 pub unsafe fn layer_surface_tree(&self, layer: u32) -> *mut ffi::wlr_scene_tree {
275 // layer is zwlr_layer_shell_v1_layer enum values
276 match layer {
277 ffi::zwlr_layer_shell_v1_layer_ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND => self.layers.background_clients,
278 ffi::zwlr_layer_shell_v1_layer_ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM => self.layers.bottom,
279 ffi::zwlr_layer_shell_v1_layer_ZWLR_LAYER_SHELL_V1_LAYER_TOP => self.layers.top,
280 ffi::zwlr_layer_shell_v1_layer_ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY => self.layers.overlay,
281 _ => std::ptr::null_mut(),
282 }
283 }
284 }
285
286 pub struct AtResult {
287 pub node: *mut ffi::wlr_scene_node,
288 pub surface: *mut ffi::wlr_surface,
289 pub sx: f64,
290 pub sy: f64,
291 pub data: SceneNodeDataVal,
292 }
293
294 pub struct SaveableSurfaces {
295 pub enabled: bool,
296 pub saved: bool,
297 pub tree: *mut ffi::wlr_scene_tree,
298 pub saved_tree: *mut ffi::wlr_scene_tree,
299 }
300
301 impl SaveableSurfaces {
302 pub unsafe fn init(parent: *mut ffi::wlr_scene_tree) -> Result<Self, &'static str> {
303 let tree = ffi::wlr_scene_tree_create(parent);
304 let saved_tree = ffi::wlr_scene_tree_create(parent);
305 if tree.is_null() || saved_tree.is_null() {
306 return Err("Failed to create saveable surfaces trees");
307 }
308 let surfaces = SaveableSurfaces {
309 enabled: true,
310 saved: false,
311 tree,
312 saved_tree,
313 };
314 surfaces.sync_enabled();
315 Ok(surfaces)
316 }
317
318 pub unsafe fn sync_enabled(&self) {
319 ffi::wlr_scene_node_set_enabled(self.tree as *mut ffi::wlr_scene_node, self.enabled && !self.saved);
320 ffi::wlr_scene_node_set_enabled(self.saved_tree as *mut ffi::wlr_scene_node, self.enabled && self.saved);
321 }
322
323 pub unsafe fn set_enabled(&mut self, enabled: bool) {
324 if self.enabled == enabled {
325 return;
326 }
327 self.enabled = enabled;
328 self.sync_enabled();
329 }
330
331 pub unsafe fn save(&mut self) {
332 if self.saved {
333 return;
334 }
335 ffi::river_scene_tree_save_buffers(self.tree, self.saved_tree);
336 self.saved = true;
337 self.sync_enabled();
338 }
339
340 pub unsafe fn drop_saved(&mut self) {
341 if !self.saved {
342 return;
343 }
344 ffi::river_scene_tree_clear_children(self.saved_tree);
345 self.saved = false;
346 self.sync_enabled();
347 }
348 }