git.lucas.co / cce-compositor
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 }