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