use std::{ any::Any, env, error::Error, sync::{ Arc, mpsc::{self}, }, }; use rxscreen::monitor::Monitor; use crate::{ WlxCapture, frame::{DRM_FORMAT_XRGB8888, DrmFormat, FrameFormat, MemPtrFrame, MouseMeta, WlxFrame}, }; pub struct XshmScreen { pub name: Arc, pub monitor: Monitor, } pub struct XshmCapture where U: Any + Send, R: Any + Send, { pub screen: Arc, sender: Option>, receiver: Option>, _dummy: Option>, } impl XshmCapture where U: Any + Send, R: Any + Send, { pub fn new(screen: Arc) -> Self { Self { screen, sender: None, receiver: None, _dummy: None, } } } pub fn xshm_get_monitors() -> Result>, Box> { let display = env::var("DISPLAY").inspect_err(|e| log::info!("DISPLAY: {e:?}"))?; let Ok(d) = rxscreen::Display::new(display) else { return Err("X11: Failed to open display".into()); }; Ok(d.monitors() .into_iter() .enumerate() .map(|x| { Arc::new(XshmScreen { name: x.1.name().replace("DisplayPort", "DP").into(), monitor: x.1, }) }) .collect()) } impl WlxCapture for XshmCapture where U: Any + Send, R: Any + Send, { fn init( &mut self, _: &[DrmFormat], user_data: U, receive_callback: fn(&U, WlxFrame) -> Option, ) { let (tx_frame, rx_frame) = std::sync::mpsc::sync_channel(4); let (tx_cmd, rx_cmd) = std::sync::mpsc::sync_channel(2); self.sender = Some(tx_cmd); self.receiver = Some(rx_frame); std::thread::spawn({ let monitor = self.screen.monitor.clone(); move || { let display = env::var("DISPLAY").expect("DISPLAY not set"); let Ok(d) = rxscreen::Display::new(display) else { log::error!("{}: failed to open display", monitor.name()); return; }; let Ok(shm) = d.shm().monitor(&monitor).build() else { log::error!("{}: failed to create shm", monitor.name()); return; }; loop { match rx_cmd.recv() { Ok(_) => { if let Ok(image) = shm.capture() { let size = unsafe { image.as_bytes().len() }; let memptr_frame = MemPtrFrame { format: FrameFormat { width: image.width() as _, height: image.height() as _, fourcc: DRM_FORMAT_XRGB8888.into(), ..Default::default() }, ptr: unsafe { image.as_ptr() as _ }, size, mouse: d.root_mouse_position().and_then(|root_pos| { monitor.mouse_to_local(root_pos).map(|(x, y)| MouseMeta { x: (x as f32) / (image.width() as f32), y: (y as f32) / (image.height() as f32), }) }), }; log::trace!("{}: captured frame", &monitor.name()); let frame = WlxFrame::MemPtr(memptr_frame); if let Some(r) = receive_callback(&user_data, frame) { match tx_frame.try_send(r) { Ok(_) => (), Err(mpsc::TrySendError::Full(_)) => { log::debug!("{}: channel full", &monitor.name()); } Err(mpsc::TrySendError::Disconnected(_)) => { log::warn!( "{}: capture thread channel closed (send)", &monitor.name(), ); break; } } } } else { log::debug!("{}: XShmGetImage failed", &monitor.name()); } } Err(_) => { log::warn!("{}: capture thread channel closed (recv)", monitor.name()); break; } } } log::warn!("{}: capture thread stopped", monitor.name()); } }); } fn is_ready(&self) -> bool { self.receiver.is_some() } fn supports_dmbuf(&self) -> bool { false } fn receive(&mut self) -> Option { if let Some(rx) = self.receiver.as_ref() { return rx.try_iter().last(); } None } fn pause(&mut self) {} fn resume(&mut self) { if let Some(rx) = self.receiver.as_ref() { log::debug!( "{}: dropped {} old frames before resuming", &self.screen.name, rx.try_iter().count() ); } self.request_new_frame(); } fn request_new_frame(&mut self) { if let Some(sender) = &self.sender && let Err(e) = sender.send(()) { log::debug!("Failed to send frame request: {}", e); } } }