git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

src/server/xwayland_override_redirect.rs (13.7K)

  1 // SPDX-FileCopyrightText: © 2020 The River Developers
  2 // SPDX-License-Identifier: GPL-3.0-only
  3 
  4 use crate::ffi;
  5 use crate::server::{Server, WlListener, wl_signal_add};
  6 use crate::xwayland_window::XwaylandWindow;
  7 
  8 #[repr(C)]
  9 pub struct XwaylandOverrideRedirect {
 10     pub server: *mut Server,
 11     pub xsurface: *mut ffi::wlr_xwayland_surface,
 12     pub surface_tree: *mut ffi::wlr_scene_tree,
 13 
 14     pub request_configure: ffi::wl_listener,
 15     pub destroy: ffi::wl_listener,
 16     pub set_override_redirect: ffi::wl_listener,
 17     pub associate: ffi::wl_listener,
 18     pub dissociate: ffi::wl_listener,
 19 
 20     pub map: ffi::wl_listener,
 21     pub unmap: ffi::wl_listener,
 22 
 23     pub set_geometry: ffi::wl_listener,
 24     /// Re-applies the 1/scale dest size after every commit (the scene's
 25     /// commit handler resets it) — see `Window`'s commit handler for why a
 26     /// per-frame pass alone leaves a hit-testing gap.
 27     pub commit: ffi::wl_listener,
 28 }
 29 
 30 unsafe fn connect_listener(
 31     signal: *mut ffi::wl_signal,
 32     listener: *mut ffi::wl_listener,
 33     callback: unsafe extern "C" fn(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void),
 34 ) {
 35     let wl_lis = listener as *mut WlListener;
 36     (*wl_lis).notify = Some(callback);
 37     wl_signal_add(signal, listener);
 38 }
 39 
 40 unsafe fn wl_listener_remove_safe(listener: *mut ffi::wl_listener) {
 41     let prev = (*listener).link.prev;
 42     let next = (*listener).link.next;
 43     if !prev.is_null() && !next.is_null() && prev != listener as *mut ffi::wl_list && next != listener as *mut ffi::wl_list {
 44         ffi::wl_list_remove(&mut (*listener).link);
 45         (*listener).link.prev = std::ptr::null_mut();
 46         (*listener).link.next = std::ptr::null_mut();
 47     }
 48 }
 49 
 50 impl XwaylandOverrideRedirect {
 51     pub unsafe fn create(
 52         xsurface: *mut ffi::wlr_xwayland_surface,
 53         server: *mut Server,
 54     ) -> Result<(), &'static str> {
 55         let title_ptr = (*xsurface).title;
 56         let class_ptr = (*xsurface).class;
 57         log::debug!(
 58             "new xwayland override redirect: title='{:?}', class='{:?}'",
 59             if title_ptr.is_null() { "" } else { std::ffi::CStr::from_ptr(title_ptr).to_str().unwrap_or("") },
 60             if class_ptr.is_null() { "" } else { std::ffi::CStr::from_ptr(class_ptr).to_str().unwrap_or("") }
 61         );
 62 
 63         let override_redirect = Box::new(XwaylandOverrideRedirect {
 64             server,
 65             xsurface,
 66             surface_tree: std::ptr::null_mut(),
 67             request_configure: std::mem::zeroed(),
 68             destroy: std::mem::zeroed(),
 69             set_override_redirect: std::mem::zeroed(),
 70             associate: std::mem::zeroed(),
 71             dissociate: std::mem::zeroed(),
 72             map: std::mem::zeroed(),
 73             unmap: std::mem::zeroed(),
 74             set_geometry: std::mem::zeroed(),
 75             commit: std::mem::zeroed(),
 76         });
 77 
 78         let raw = Box::into_raw(override_redirect);
 79         (*server).wm.override_redirects.push(raw);
 80 
 81         connect_listener(&mut (*xsurface).events.request_configure, &mut (*raw).request_configure, handle_request_configure);
 82         connect_listener(&mut (*xsurface).events.destroy, &mut (*raw).destroy, handle_destroy);
 83         connect_listener(&mut (*xsurface).events.set_override_redirect, &mut (*raw).set_override_redirect, handle_set_override_redirect);
 84         connect_listener(&mut (*xsurface).events.associate, &mut (*raw).associate, handle_associate);
 85         connect_listener(&mut (*xsurface).events.dissociate, &mut (*raw).dissociate, handle_dissociate);
 86 
 87         if !(*xsurface).surface.is_null() {
 88             handle_associate_impl(raw);
 89             if ffi::river_wlr_surface_is_mapped((*xsurface).surface) {
 90                 handle_map_impl(raw);
 91             }
 92         }
 93 
 94         Ok(())
 95     }
 96 
 97     /// Put the surface tree where X11 says the window is, in logical
 98     /// pixels (`xwayland_window::x11_scale`).
 99     pub unsafe fn place(&mut self) {
100         if self.surface_tree.is_null() {
101             return;
102         }
103         let s = crate::xwayland_window::x11_scale_for(self.server, self.xsurface);
104         ffi::wlr_scene_node_set_position(
105             self.surface_tree as *mut ffi::wlr_scene_node,
106             crate::xwayland_window::from_x11((*self.xsurface).x as i32, s),
107             crate::xwayland_window::from_x11((*self.xsurface).y as i32, s),
108         );
109     }
110 
111     /// Draw the physical-pixel X11 buffer at 1/scale. Runs from the
112     /// per-frame pass (output.rs) because the scene's commit listener
113     /// resets a committed buffer's dest size; every setter is change-checked.
114     pub unsafe fn apply_x11_scale(&mut self) {
115         if self.surface_tree.is_null() {
116             return;
117         }
118         let s = crate::xwayland_window::x11_scale_for(self.server, self.xsurface) as f64;
119         if s == 1.0 {
120             return;
121         }
122         unsafe extern "C" fn iter(
123             buffer: *mut ffi::wlr_scene_buffer,
124             _sx: i32,
125             _sy: i32,
126             user_data: *mut std::ffi::c_void,
127         ) {
128             let inv = *(user_data as *const f64);
129             let node = buffer as *mut ffi::wlr_scene_node;
130             let surface = ffi::river_scene_node_get_surface(node);
131             if surface.is_null() {
132                 return;
133             }
134             let w = ffi::river_wlr_surface_get_width(surface);
135             let h = ffi::river_wlr_surface_get_height(surface);
136             ffi::river_scene_buffer_set_dest_size_if_changed(
137                 buffer,
138                 (w as f64 * inv).round() as i32,
139                 (h as f64 * inv).round() as i32,
140             );
141             ffi::river_scene_buffer_set_scaled_opaque_region(buffer, surface, inv);
142         }
143         let inv = 1.0 / s;
144         ffi::wlr_scene_node_for_each_buffer(
145             self.surface_tree as *mut ffi::wlr_scene_node,
146             Some(iter),
147             &inv as *const f64 as *mut std::ffi::c_void,
148         );
149     }
150 
151     pub unsafe fn focus_if_desired(&self) {
152         if (*self.server).lock_manager.state != crate::lock_manager::LockState::Unlocked {
153             return;
154         }
155 
156         if ffi::wlr_xwayland_surface_override_redirect_wants_focus(self.xsurface)
157             && ffi::wlr_xwayland_surface_icccm_input_model(self.xsurface)
158                 != ffi::wlr_xwayland_icccm_input_model_WLR_ICCCM_INPUT_MODEL_NONE
159         {
160             let seat = (*self.server).input_manager.default_seat;
161             if !seat.is_null() {
162                 if let crate::seat::Focus::Window(window) = (*seat).focused {
163                     if !window.is_null() && matches!((*window).impl_type, crate::window::WindowImpl::Xwayland(_)) {
164                         let parent_xwindow = (*window).impl_type;
165                         if let crate::window::WindowImpl::Xwayland(xwindow) = parent_xwindow {
166                             if !xwindow.is_null() && (*(*xwindow).xsurface).pid == (*self.xsurface).pid {
167                                 (*seat).keyboard_enter_or_leave((*self.xsurface).surface);
168                                 return;
169                             }
170                         }
171                     }
172                 }
173                 (*seat).focus(crate::seat::Focus::OverrideRedirect(self as *const _ as *mut _));
174             }
175         }
176     }
177 }
178 
179 unsafe extern "C" fn handle_request_configure(_listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
180     let event = data as *mut ffi::wlr_xwayland_surface_configure_event;
181     ffi::wlr_xwayland_surface_configure((*event).surface, (*event).x, (*event).y, (*event).width, (*event).height);
182 }
183 
184 unsafe extern "C" fn handle_destroy(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
185     let or = crate::container_of!(listener, XwaylandOverrideRedirect, destroy);
186     (*(*or).server).wm.override_redirects.retain(|&p| p != or);
187 
188     wl_listener_remove_safe(&mut (*or).request_configure);
189     wl_listener_remove_safe(&mut (*or).destroy);
190     wl_listener_remove_safe(&mut (*or).associate);
191     wl_listener_remove_safe(&mut (*or).dissociate);
192     wl_listener_remove_safe(&mut (*or).set_override_redirect);
193 
194     let _ = Box::from_raw(or);
195 }
196 
197 unsafe extern "C" fn handle_associate(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
198     let or = crate::container_of!(listener, XwaylandOverrideRedirect, associate);
199     handle_associate_impl(or);
200 }
201 
202 unsafe fn handle_associate_impl(or: *mut XwaylandOverrideRedirect) {
203     let surface = (*(*or).xsurface).surface;
204     if !surface.is_null() {
205         connect_listener(
206             ffi::river_wlr_surface_get_map_signal(surface),
207             &mut (*or).map,
208             handle_map,
209         );
210         connect_listener(
211             ffi::river_wlr_surface_get_unmap_signal(surface),
212             &mut (*or).unmap,
213             handle_unmap,
214         );
215     }
216 }
217 
218 unsafe extern "C" fn handle_dissociate(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
219     let or = crate::container_of!(listener, XwaylandOverrideRedirect, dissociate);
220     wl_listener_remove_safe(&mut (*or).map);
221     wl_listener_remove_safe(&mut (*or).unmap);
222 }
223 
224 unsafe extern "C" fn handle_map(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
225     let or = crate::container_of!(listener, XwaylandOverrideRedirect, map);
226     handle_map_impl(or);
227 }
228 
229 unsafe fn handle_map_impl(or: *mut XwaylandOverrideRedirect) {
230     let surface = (*(*or).xsurface).surface;
231     let override_redirect_tree = (*(*or).server).scene.layers.override_redirect;
232 
233     let surface_tree = ffi::wlr_scene_subsurface_tree_create(override_redirect_tree, surface);
234     if surface_tree.is_null() {
235         log::error!("out of memory creating subsurface tree for override redirect");
236         let surface_resource = ffi::river_wlr_surface_get_resource(surface);
237         let client = ffi::wl_resource_get_client(surface_resource);
238         ffi::wl_client_post_no_memory(client);
239         return;
240     }
241     (*or).surface_tree = surface_tree;
242 
243     crate::scene_node_data::SceneNodeData::attach(
244         surface_tree as *mut ffi::wlr_scene_node,
245         crate::scene_node_data::SceneNodeDataVal::OverrideRedirect(or as *mut _),
246     );
247 
248     ffi::river_wlr_surface_set_data(surface, surface_tree as *mut ffi::wlr_scene_node as *mut _);
249 
250     (*or).place();
251     (*or).apply_x11_scale();
252 
253     connect_listener(&mut (*(*or).xsurface).events.set_geometry, &mut (*or).set_geometry, handle_set_geometry);
254     // After the scene's subsurface tree, so this runs after its reset.
255     connect_listener(ffi::river_wlr_surface_get_commit_signal(surface), &mut (*or).commit, handle_commit);
256 
257     (*or).focus_if_desired();
258 }
259 
260 unsafe extern "C" fn handle_unmap(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
261     let or = crate::container_of!(listener, XwaylandOverrideRedirect, unmap);
262 
263     wl_listener_remove_safe(&mut (*or).set_geometry);
264     wl_listener_remove_safe(&mut (*or).commit);
265 
266     let surface = (*(*or).xsurface).surface;
267     if !surface.is_null() {
268         ffi::river_wlr_surface_set_data(surface, std::ptr::null_mut());
269     }
270 
271     if !(*or).surface_tree.is_null() {
272         ffi::wlr_scene_node_destroy((*or).surface_tree as *mut ffi::wlr_scene_node);
273         (*or).surface_tree = std::ptr::null_mut();
274     }
275 
276     let default_seat = (*(*or).server).input_manager.default_seat;
277     if !default_seat.is_null() {
278         if let crate::seat::Focus::Window(window) = (*default_seat).focused {
279             if !window.is_null() && matches!((*window).impl_type, crate::window::WindowImpl::Xwayland(_)) {
280                 let parent_xwindow = (*window).impl_type;
281                 if let crate::window::WindowImpl::Xwayland(xwindow) = parent_xwindow {
282                     if !xwindow.is_null()
283                         && (*(*xwindow).xsurface).pid == (*(*or).xsurface).pid
284                         && ffi::river_wlr_seat_get_keyboard_focused_surface((*default_seat).wlr_seat) == surface
285                     {
286                         (*default_seat).keyboard_enter_or_leave((*window).root_surface());
287                     }
288                 }
289             }
290         }
291     }
292 
293     (*(*or).server).wm.dirty_windowing();
294 }
295 
296 unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
297     let or = crate::container_of!(listener, XwaylandOverrideRedirect, commit);
298     (*or).apply_x11_scale();
299 }
300 
301 unsafe extern "C" fn handle_set_geometry(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
302     let or = crate::container_of!(listener, XwaylandOverrideRedirect, set_geometry);
303     (*or).place();
304 }
305 
306 unsafe extern "C" fn handle_set_override_redirect(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
307     let or = crate::container_of!(listener, XwaylandOverrideRedirect, set_override_redirect);
308     let xsurface = (*or).xsurface;
309     log::debug!("xwayland surface unset override redirect");
310     assert!(!(*xsurface).override_redirect);
311 
312     let surface = (*xsurface).surface;
313     if !surface.is_null() {
314         if ffi::river_wlr_surface_is_mapped(surface) {
315             // handle unmap inline
316             wl_listener_remove_safe(&mut (*or).set_geometry);
317     wl_listener_remove_safe(&mut (*or).commit);
318             ffi::river_wlr_surface_set_data(surface, std::ptr::null_mut());
319             if !(*or).surface_tree.is_null() {
320                 ffi::wlr_scene_node_destroy((*or).surface_tree as *mut ffi::wlr_scene_node);
321                 (*or).surface_tree = std::ptr::null_mut();
322             }
323         }
324         wl_listener_remove_safe(&mut (*or).map);
325         wl_listener_remove_safe(&mut (*or).unmap);
326     }
327 
328     let server = (*or).server;
329 
330     // Destroy this OR instance
331     wl_listener_remove_safe(&mut (*or).request_configure);
332     wl_listener_remove_safe(&mut (*or).destroy);
333     wl_listener_remove_safe(&mut (*or).associate);
334     wl_listener_remove_safe(&mut (*or).dissociate);
335     wl_listener_remove_safe(&mut (*or).set_override_redirect);
336     let _ = Box::from_raw(or);
337 
338     if let Err(e) = XwaylandWindow::create(xsurface, server) {
339         log::error!("Failed to transition OR to XwaylandWindow: {}", e);
340     }
341 }