Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
src/server/inspector.rs (6.8K)
1 use crate::ffi;
2 use crate::server::Server;
3 use std::collections::HashMap;
4
5 pub struct Inspector {
6 pub server: *mut Server,
7 pub global: *mut ffi::wl_global,
8 pub client_states: HashMap<*mut ffi::wlr_surface, String>,
9 pub resources: Vec<*mut ffi::wl_resource>,
10 }
11
12 impl Inspector {
13 pub fn new() -> Self {
14 Self {
15 server: std::ptr::null_mut(),
16 global: std::ptr::null_mut(),
17 client_states: HashMap::new(),
18 resources: Vec::new(),
19 }
20 }
21
22 pub unsafe fn init(&mut self, server: *mut Server) -> Result<(), &'static str> {
23 self.server = server;
24 self.client_states = HashMap::new();
25 self.resources = Vec::new();
26
27 self.global = ffi::wl_global_create(
28 (*server).wl_server,
29 &ffi::zcce_inspector_v1_interface,
30 1,
31 self as *mut Inspector as *mut _,
32 Some(bind),
33 );
34
35 if self.global.is_null() {
36 return Err("Failed to create zcce_inspector_v1 global");
37 }
38
39 log::info!("zcce_inspector_v1 protocol global initialized successfully");
40 Ok(())
41 }
42
43 pub unsafe fn deinit(&mut self) {
44 if !self.global.is_null() {
45 ffi::wl_global_destroy(self.global);
46 self.global = std::ptr::null_mut();
47 }
48 }
49 }
50
51 unsafe extern "C" fn handle_destroy_resource(resource: *mut ffi::wl_resource) {
52 let inspector = ffi::wl_resource_get_user_data(resource) as *mut Inspector;
53 if !inspector.is_null() {
54 (*inspector).resources.retain(|&r| r != resource);
55 }
56 }
57
58 unsafe extern "C" fn bind(
59 client: *mut ffi::wl_client,
60 data: *mut std::ffi::c_void,
61 version: u32,
62 id: u32,
63 ) {
64 let inspector = data as *mut Inspector;
65 if inspector.is_null() {
66 return;
67 }
68
69 let resource = ffi::wl_resource_create(client, &ffi::zcce_inspector_v1_interface, version as i32, id);
70 if resource.is_null() {
71 ffi::wl_client_post_no_memory(client);
72 return;
73 }
74
75 ffi::wl_resource_set_implementation(
76 resource,
77 &INSPECTOR_INTERFACE as *const _ as *const _,
78 inspector as *mut _,
79 Some(handle_destroy_resource),
80 );
81
82 (*inspector).resources.push(resource);
83 }
84
85 unsafe extern "C" fn inspector_destroy(_client: *mut ffi::wl_client, resource: *mut ffi::wl_resource) {
86 ffi::wl_resource_destroy(resource);
87 }
88
89 unsafe extern "C" fn inspector_register_client(
90 _client: *mut ffi::wl_client,
91 resource: *mut ffi::wl_resource,
92 surface_resource: *mut ffi::wl_resource,
93 ) {
94 let inspector = ffi::wl_resource_get_user_data(resource) as *mut Inspector;
95 if inspector.is_null() {
96 return;
97 }
98 let surface = ffi::wlr_surface_from_resource(surface_resource);
99 if surface.is_null() {
100 return;
101 }
102 (*inspector).client_states.entry(surface).or_insert_with(String::new);
103 }
104
105 unsafe fn create_memfd_with_data(name: &str, data: &[u8]) -> Option<(std::os::raw::c_int, u32)> {
106 let c_name = std::ffi::CString::new(name).ok()?;
107 let fd = libc::memfd_create(c_name.as_ptr(), libc::MFD_CLOEXEC);
108 if fd < 0 {
109 return None;
110 }
111 let mut written = 0;
112 while written < data.len() {
113 let res = libc::write(
114 fd,
115 data.as_ptr().add(written) as *const _,
116 data.len() - written,
117 );
118 if res < 0 {
119 let err = *libc::__errno_location();
120 if err == libc::EINTR {
121 continue;
122 }
123 libc::close(fd);
124 return None;
125 }
126 written += res as usize;
127 }
128 libc::lseek(fd, 0, libc::SEEK_SET);
129 Some((fd, data.len() as u32))
130 }
131
132 unsafe extern "C" fn inspector_update_state(
133 _client: *mut ffi::wl_client,
134 resource: *mut ffi::wl_resource,
135 surface_resource: *mut ffi::wl_resource,
136 fd: std::os::raw::c_int,
137 len: u32,
138 ) {
139 let inspector = ffi::wl_resource_get_user_data(resource) as *mut Inspector;
140 if inspector.is_null() {
141 if fd >= 0 {
142 libc::close(fd);
143 }
144 return;
145 }
146 let surface = ffi::wlr_surface_from_resource(surface_resource);
147 if surface.is_null() {
148 if fd >= 0 {
149 libc::close(fd);
150 }
151 return;
152 }
153 if fd >= 0 {
154 use std::os::unix::io::FromRawFd;
155 let file = unsafe { std::fs::File::from_raw_fd(fd) };
156 let mut state_str = String::with_capacity(len as usize);
157 use std::io::Read;
158 if file.take(len as u64).read_to_string(&mut state_str).is_ok() {
159 (*inspector).client_states.insert(surface, state_str);
160 }
161 }
162 }
163
164 unsafe extern "C" fn inspector_get_inspected_surfaces(
165 _client: *mut ffi::wl_client,
166 resource: *mut ffi::wl_resource,
167 ) {
168 let inspector = ffi::wl_resource_get_user_data(resource) as *mut Inspector;
169 if inspector.is_null() {
170 return;
171 }
172 let server = (*inspector).server;
173 let windows = &(*server).wm.windows;
174
175 for &window in windows.iter() {
176 if window.is_null() {
177 continue;
178 }
179 let root_surface = (*window).root_surface();
180 if root_surface.is_null() {
181 continue;
182 }
183
184 // Retrieve registered UI state JSON or default to empty
185 let state = (*inspector)
186 .client_states
187 .get(&root_surface)
188 .cloned()
189 .unwrap_or_else(|| "{}".to_string());
190
191 let title_ptr = (*window).get_title();
192 let app_id_ptr = (*window).get_app_id();
193
194 let title = if title_ptr.is_null() {
195 std::ffi::CString::new("").unwrap()
196 } else {
197 std::ffi::CStr::from_ptr(title_ptr).to_owned()
198 };
199
200 let app_id = if app_id_ptr.is_null() {
201 std::ffi::CString::new("").unwrap()
202 } else {
203 std::ffi::CStr::from_ptr(app_id_ptr).to_owned()
204 };
205
206 if let Some((fd, len)) = create_memfd_with_data("cce_ui_inspected_state", state.as_bytes()) {
207 // Send: inspected_surface(title, app_id, x, y, width, height, fd, len)
208 // Event inspected_surface has index 0
209 ffi::wl_resource_post_event(
210 resource,
211 0,
212 title.as_ptr(),
213 app_id.as_ptr(),
214 (*window).box_geom.x,
215 (*window).box_geom.y,
216 (*window).box_geom.width,
217 (*window).box_geom.height,
218 fd,
219 len,
220 );
221 libc::close(fd);
222 }
223 }
224
225 // Send: inspected_surface_done()
226 // Event inspected_surface_done has index 1
227 ffi::wl_resource_post_event(resource, 1);
228 }
229
230 static INSPECTOR_INTERFACE: ffi::zcce_inspector_v1_interface = ffi::zcce_inspector_v1_interface {
231 destroy: Some(inspector_destroy),
232 register_client: Some(inspector_register_client),
233 update_state: Some(inspector_update_state),
234 get_inspected_surfaces: Some(inspector_get_inspected_surfaces),
235 };