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 }