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