Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
src/server/shell_surface.rs (9.7K)
1 // SPDX-FileCopyrightText: © 2024 The River Developers
2 // SPDX-License-Identifier: GPL-3.0-only
3
4 use crate::ffi;
5 use crate::server::{Server, WlList, wl_list_insert};
6 use crate::wm_node::WmNode;
7
8 pub struct ShellSurfaceRenderingRequested {
9 pub x: i32,
10 pub y: i32,
11 pub sync_next_commit: bool,
12 }
13
14 pub struct ShellSurface {
15 pub server: *mut Server,
16 pub object: *mut ffi::wl_resource, // zcce_shell_surface_v1
17 pub surface: *mut ffi::wlr_surface,
18 pub tree: *mut ffi::wlr_scene_tree,
19 pub surfaces: crate::scene::SaveableSurfaces,
20 pub popup_tree: *mut ffi::wlr_scene_tree,
21 pub node: WmNode,
22 pub rendering_requested: ShellSurfaceRenderingRequested,
23 }
24
25 impl ShellSurface {
26 pub unsafe fn create(
27 client: *mut ffi::wl_client,
28 version: u32,
29 id: u32,
30 surface: *mut ffi::wlr_surface,
31 server: *mut Server,
32 ) -> Result<(), &'static str> {
33 log::debug!("new zcce_shell_surface_v1");
34
35 let shell_surface_v1 = ffi::wl_resource_create(client, &ffi::zcce_shell_surface_v1_interface, version as i32, id);
36 if shell_surface_v1.is_null() {
37 ffi::wl_client_post_no_memory(client);
38 return Err("wl_resource_create failed");
39 }
40
41 if !ffi::wlr_surface_set_role(
42 surface,
43 &raw const SHELL_SURFACE_ROLE,
44 shell_surface_v1,
45 ffi::zcce_window_manager_v1_error_ZCCE_WINDOW_MANAGER_V1_ERROR_ROLE,
46 ) {
47 return Err("wlr_surface_set_role failed");
48 }
49 ffi::river_wlr_surface_set_role_object(surface, shell_surface_v1);
50
51 let shell_surface = Box::new(ShellSurface {
52 server,
53 object: shell_surface_v1,
54 surface,
55 tree: std::ptr::null_mut(),
56 surfaces: std::mem::zeroed(),
57 popup_tree: std::ptr::null_mut(),
58 node: std::mem::zeroed(),
59 rendering_requested: ShellSurfaceRenderingRequested {
60 x: 0,
61 y: 0,
62 sync_next_commit: false,
63 },
64 });
65 let raw = Box::into_raw(shell_surface);
66
67 ffi::wl_resource_set_implementation(
68 shell_surface_v1,
69 &SHELL_SURFACE_INTERFACE as *const _ as *const _,
70 raw as *mut _,
71 Some(handle_shell_surface_destroy_resource),
72 );
73
74 let hidden_tree = (*server).scene.hidden_tree;
75 let tree = ffi::wlr_scene_tree_create(hidden_tree);
76 if tree.is_null() {
77 let _ = Box::from_raw(raw);
78 return Err("Failed to create scene tree");
79 }
80 let popup_tree = ffi::wlr_scene_tree_create(hidden_tree);
81 if popup_tree.is_null() {
82 ffi::wlr_scene_node_destroy(tree as *mut ffi::wlr_scene_node);
83 let _ = Box::from_raw(raw);
84 return Err("Failed to create popup tree");
85 }
86
87 let surfaces = match crate::scene::SaveableSurfaces::init(tree) {
88 Ok(s) => s,
89 Err(e) => {
90 ffi::wlr_scene_node_destroy(tree as *mut ffi::wlr_scene_node);
91 ffi::wlr_scene_node_destroy(popup_tree as *mut ffi::wlr_scene_node);
92 let _ = Box::from_raw(raw);
93 return Err(e);
94 }
95 };
96
97 let subsurface_tree = ffi::wlr_scene_subsurface_tree_create(surfaces.tree, surface);
98 if subsurface_tree.is_null() {
99 ffi::wlr_scene_node_destroy(tree as *mut ffi::wlr_scene_node);
100 ffi::wlr_scene_node_destroy(popup_tree as *mut ffi::wlr_scene_node);
101 let _ = Box::from_raw(raw);
102 return Err("Failed to create scene subsurface tree");
103 }
104
105 (*raw).tree = tree;
106 (*raw).popup_tree = popup_tree;
107 (*raw).surfaces = surfaces;
108 (*raw).node.init(crate::wm_node::WmNodeTag::ShellSurface);
109
110 let list_head = &mut (*server).wm.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
111 wl_list_insert((*list_head).prev, &mut (*raw).node.link as *mut ffi::wl_list as *mut WlList);
112
113 crate::scene_node_data::SceneNodeData::attach(
114 tree as *mut ffi::wlr_scene_node,
115 crate::scene_node_data::SceneNodeDataVal::ShellSurface(raw),
116 );
117 crate::scene_node_data::SceneNodeData::attach(
118 popup_tree as *mut ffi::wlr_scene_node,
119 crate::scene_node_data::SceneNodeDataVal::ShellSurface(raw),
120 );
121
122 Ok(())
123 }
124
125 pub unsafe fn render_finish(&mut self) {
126 if self.rendering_requested.sync_next_commit {
127 self.rendering_requested.sync_next_commit = false;
128
129 if !self.surfaces.saved {
130 ffi::wl_resource_post_error(
131 self.object,
132 ffi::zcce_shell_surface_v1_error_ZCCE_SHELL_SURFACE_V1_ERROR_NO_COMMIT,
133 b"no wl_surface.commit after sync_next_commit and before update_rendering_finish\0".as_ptr() as *const _,
134 );
135 }
136 }
137
138 self.surfaces.drop_saved();
139
140 ffi::wlr_scene_node_set_position(
141 self.tree as *mut ffi::wlr_scene_node,
142 self.rendering_requested.x,
143 self.rendering_requested.y,
144 );
145 ffi::wlr_scene_node_set_position(
146 self.popup_tree as *mut ffi::wlr_scene_node,
147 self.rendering_requested.x,
148 self.rendering_requested.y,
149 );
150 }
151 }
152
153 pub unsafe fn from_wlr_surface(surface: *mut ffi::wlr_surface) -> *mut ShellSurface {
154 if surface.is_null() {
155 return std::ptr::null_mut();
156 }
157 let role_ptr = ffi::river_wlr_surface_get_role(surface);
158 if role_ptr != &raw const SHELL_SURFACE_ROLE {
159 return std::ptr::null_mut();
160 }
161 let resource = ffi::river_wlr_surface_get_role_resource(surface);
162 if resource.is_null() {
163 return std::ptr::null_mut();
164 }
165 ffi::wl_resource_get_user_data(resource) as *mut ShellSurface
166 }
167
168 unsafe extern "C" fn client_commit(surface: *mut ffi::wlr_surface) {
169 let shell_surface = from_wlr_surface(surface);
170 if shell_surface.is_null() {
171 return;
172 }
173 if (*shell_surface).rendering_requested.sync_next_commit {
174 (*shell_surface).surfaces.save();
175 }
176 }
177
178 unsafe extern "C" fn commit(surface: *mut ffi::wlr_surface) {
179 if ffi::wlr_surface_has_buffer(surface) {
180 ffi::wlr_surface_map(surface);
181 }
182 }
183
184 unsafe extern "C" fn handle_shell_surface_destroy_resource(resource: *mut ffi::wl_resource) {
185 let shell_surface = ffi::wl_resource_get_user_data(resource) as *mut ShellSurface;
186 if !shell_surface.is_null() {
187 ffi::river_wlr_surface_set_role_object((*shell_surface).surface, std::ptr::null_mut());
188 (*shell_surface).object = std::ptr::null_mut();
189
190 ffi::wlr_surface_unmap((*shell_surface).surface);
191 (*shell_surface).node.make_inert();
192 (*shell_surface).node.deinit();
193 ffi::wlr_scene_node_destroy((*shell_surface).tree as *mut ffi::wlr_scene_node);
194 ffi::wlr_scene_node_destroy((*shell_surface).popup_tree as *mut ffi::wlr_scene_node);
195
196 let _ = Box::from_raw(shell_surface);
197 }
198 }
199
200 unsafe extern "C" fn role_destroy(surface: *mut ffi::wlr_surface) {
201 let shell_surface = from_wlr_surface(surface);
202 if shell_surface.is_null() {
203 return;
204 }
205
206 ffi::river_wlr_surface_set_role_object(surface, std::ptr::null_mut());
207 if !(*shell_surface).object.is_null() {
208 ffi::wl_resource_set_user_data((*shell_surface).object, std::ptr::null_mut());
209 ffi::wl_resource_destroy((*shell_surface).object);
210 (*shell_surface).object = std::ptr::null_mut();
211 }
212
213 ffi::wlr_surface_unmap((*shell_surface).surface);
214
215 (*shell_surface).node.make_inert();
216 (*shell_surface).node.deinit();
217
218 ffi::wlr_scene_node_destroy((*shell_surface).tree as *mut ffi::wlr_scene_node);
219 ffi::wlr_scene_node_destroy((*shell_surface).popup_tree as *mut ffi::wlr_scene_node);
220
221 let _ = Box::from_raw(shell_surface);
222 }
223
224 unsafe extern "C" fn shell_surface_destroy(
225 _client: *mut ffi::wl_client,
226 resource: *mut ffi::wl_resource,
227 ) {
228 ffi::wl_resource_destroy(resource);
229 }
230
231 unsafe extern "C" fn shell_surface_get_node(
232 client: *mut ffi::wl_client,
233 resource: *mut ffi::wl_resource,
234 id: u32,
235 ) {
236 let shell_surface = ffi::wl_resource_get_user_data(resource) as *mut ShellSurface;
237 if shell_surface.is_null() {
238 return;
239 }
240
241 if !(*shell_surface).node.object.is_null() {
242 ffi::wl_resource_post_error(
243 resource,
244 ffi::zcce_shell_surface_v1_error_ZCCE_SHELL_SURFACE_V1_ERROR_NODE_EXISTS,
245 b"shell surface already has a node object\0".as_ptr() as *const _,
246 );
247 return;
248 }
249
250 (*shell_surface).node.create_object(
251 client,
252 ffi::wl_resource_get_version(resource) as u32,
253 id,
254 );
255 }
256
257 unsafe extern "C" fn shell_surface_sync_next_commit(
258 _client: *mut ffi::wl_client,
259 resource: *mut ffi::wl_resource,
260 ) {
261 let shell_surface = ffi::wl_resource_get_user_data(resource) as *mut ShellSurface;
262 if shell_surface.is_null() {
263 return;
264 }
265
266 let server = (*shell_surface).server;
267 if !(*server).wm.ensure_rendering() {
268 return;
269 }
270
271 (*shell_surface).rendering_requested.sync_next_commit = true;
272 }
273
274 static SHELL_SURFACE_INTERFACE: ffi::zcce_shell_surface_v1_interface = ffi::zcce_shell_surface_v1_interface {
275 destroy: Some(shell_surface_destroy),
276 get_node: Some(shell_surface_get_node),
277 sync_next_commit: Some(shell_surface_sync_next_commit),
278 };
279
280 #[no_mangle]
281 pub static mut SHELL_SURFACE_ROLE: ffi::wlr_surface_role = ffi::wlr_surface_role {
282 name: b"zcce_shell_surface_v1\0".as_ptr() as *const _,
283 no_object: false,
284 client_commit: Some(client_commit),
285 commit: Some(commit),
286 map: None,
287 unmap: None,
288 destroy: Some(role_destroy),
289 };