use { crate::{ client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError, ClientId}, compositor::LogLevel, globals::{Global, GlobalName}, ifs::{ jay_acceptor_request::JayAcceptorRequest, jay_client_query::JayClientQuery, jay_color_management::JayColorManagement, jay_ei_session_builder::JayEiSessionBuilder, jay_idle::JayIdle, jay_input::JayInput, jay_log_file::JayLogFile, jay_open_control_center_request::JayOpenControlCenterRequest, jay_output::JayOutput, jay_pointer::JayPointer, jay_randr::JayRandr, jay_reexec::JayReexec, jay_render_ctx::JayRenderCtx, jay_screencast::JayScreencast, jay_screenshot::JayScreenshot, jay_seat_events::JaySeatEvents, jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector}, jay_select_workspace::{JaySelectWorkspace, JayWorkspaceSelector}, jay_tree_query::JayTreeQuery, jay_workspace_watcher::JayWorkspaceWatcher, jay_xwayland::JayXwayland, wl_surface::jay_sync_file_surface::JaySyncFileSurface, }, leaks::Tracker, object::{Object, Version}, screenshoter::take_screenshot, utils::{errorfmt::ErrorFmt, toplevel_identifier::ToplevelIdentifier}, wire::{ JayCompositorId, JayScreenshotId, jay_compositor::{self, *}, }, }, bstr::ByteSlice, linearize::LinearizeExt, std::{cell::Cell, ops::Deref, rc::Rc, str::FromStr}, thiserror::Error, }; pub const CREATE_EI_SESSION_SINCE: Version = Version(5); pub const SCREENSHOT_SPLITUP_SINCE: Version = Version(6); pub const GET_TOPLEVEL_SINCE: Version = Version(12); pub struct JayCompositorGlobal { name: GlobalName, } impl JayCompositorGlobal { pub fn new(name: GlobalName) -> Self { Self { name } } fn bind_( self: Rc, id: JayCompositorId, client: &Rc, version: Version, ) -> Result<(), JayCompositorError> { let obj = Rc::new(JayCompositor { id, client: client.clone(), tracker: Default::default(), version, }); track!(client, obj); client.add_client_obj(&obj)?; obj.send_capabilities(); Ok(()) } } global_base!(JayCompositorGlobal, JayCompositor, JayCompositorError); impl Global for JayCompositorGlobal { fn version(&self) -> u32 { 28 } fn required_caps(&self) -> ClientCaps { CAP_JAY_COMPOSITOR } } simple_add_global!(JayCompositorGlobal); pub struct JayCompositor { id: JayCompositorId, client: Rc, tracker: Tracker, version: Version, } pub struct Cap; impl Cap { pub const NONE: u16 = 0; pub const WINDOW_CAPTURE: u16 = 1; pub const SELECT_WORKSPACE: u16 = 2; } impl JayCompositor { fn send_capabilities(&self) { self.client.event(Capabilities { self_id: self.id, cap: &[Cap::NONE, Cap::WINDOW_CAPTURE, Cap::SELECT_WORKSPACE], }); } fn take_screenshot_impl( &self, id: JayScreenshotId, include_cursor: bool, ) -> Result<(), JayCompositorError> { let ss = Rc::new(JayScreenshot { id, client: self.client.clone(), tracker: Default::default(), }); track!(self.client, ss); self.client.add_client_obj(&ss)?; match take_screenshot(&self.client.state, include_cursor) { Ok(s) => { let dmabuf = s.bo.dmabuf(); if self.version < SCREENSHOT_SPLITUP_SINCE { if let Some(drm) = &s.drm { let plane = &dmabuf.planes[0]; ss.send_dmabuf( drm, &plane.fd, dmabuf.width, dmabuf.height, plane.offset, plane.stride, dmabuf.modifier, ); } else { ss.send_error("Buffer has no associated DRM device"); } } else { if let Some(drm) = &s.drm { ss.send_drm_dev(drm); } for plane in &dmabuf.planes { ss.send_plane(plane); } ss.send_dmabuf2(dmabuf); } } Err(e) => { let msg = ErrorFmt(e).to_string(); ss.send_error(&msg); } } self.client.remove_obj(ss.deref())?; Ok(()) } } impl JayCompositorRequestHandler for JayCompositor { type Error = JayCompositorError; fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { self.client.remove_obj(self)?; Ok(()) } fn get_log_file(&self, req: GetLogFile, _slf: &Rc) -> Result<(), Self::Error> { let log_file = Rc::new(JayLogFile::new(req.id, &self.client)); track!(self.client, log_file); self.client.add_client_obj(&log_file)?; match &self.client.state.logger { Some(logger) => log_file.send_path(logger.path().as_bstr()), _ => log_file.send_path(b"".as_bstr()), }; Ok(()) } fn quit(&self, _req: Quit, _slf: &Rc) -> Result<(), Self::Error> { self.client.state.quit(); Ok(()) } fn set_log_level(&self, req: SetLogLevel, _slf: &Rc) -> Result<(), Self::Error> { let Some(level) = LogLevel::from_linear(req.level as usize) else { return Err(JayCompositorError::UnknownLogLevel(req.level)); }; self.client.state.set_log_level(level); Ok(()) } fn take_screenshot(&self, req: TakeScreenshot, _slf: &Rc) -> Result<(), Self::Error> { self.take_screenshot_impl(req.id, false) } fn take_screenshot2(&self, req: TakeScreenshot2, _slf: &Rc) -> Result<(), Self::Error> { self.take_screenshot_impl(req.id, req.include_cursor != 0) } fn get_idle(&self, req: GetIdle, _slf: &Rc) -> Result<(), Self::Error> { let idle = Rc::new(JayIdle { id: req.id, client: self.client.clone(), tracker: Default::default(), version: self.version, }); track!(self.client, idle); self.client.add_client_obj(&idle)?; Ok(()) } fn get_client_id(&self, _req: GetClientId, _slf: &Rc) -> Result<(), Self::Error> { self.client.event(jay_compositor::ClientId { self_id: self.id, client_id: self.client.id.raw(), }); Ok(()) } fn enable_symmetric_delete( &self, _req: EnableSymmetricDelete, _slf: &Rc, ) -> Result<(), Self::Error> { self.client.symmetric_delete.set(true); Ok(()) } fn unlock(&self, _req: Unlock, _slf: &Rc) -> Result<(), Self::Error> { let state = &self.client.state; if state.lock.locked.get() { if let Some(lock) = state.lock.lock.get() { lock.finish(); } state.do_unlock(); } Ok(()) } fn get_seats(&self, _req: GetSeats, _slf: &Rc) -> Result<(), Self::Error> { for seat in self.client.state.globals.seats.lock().values() { self.client.event(Seat { self_id: self.id, id: seat.id().raw(), name: seat.seat_name(), }) } Ok(()) } fn seat_events(&self, req: SeatEvents, _slf: &Rc) -> Result<(), Self::Error> { let se = Rc::new(JaySeatEvents { id: req.id, client: self.client.clone(), tracker: Default::default(), }); track!(self.client, se); self.client.add_client_obj(&se)?; self.client .state .testers .borrow_mut() .insert((self.client.id, req.id), se); Ok(()) } fn get_output(&self, req: GetOutput, _slf: &Rc) -> Result<(), Self::Error> { let output = self.client.lookup(req.output)?; let jo = Rc::new(JayOutput { id: req.id, client: self.client.clone(), output: output.global.clone(), tracker: Default::default(), }); track!(self.client, jo); self.client.add_client_obj(&jo)?; if let Some(node) = jo.output.node() { node.jay_outputs.set((self.client.id, req.id), jo.clone()); jo.send_linear_id(); } else { jo.send_destroyed(); } Ok(()) } fn get_pointer(&self, req: GetPointer, _slf: &Rc) -> Result<(), Self::Error> { let seat = self.client.lookup(req.seat)?; let ctx = Rc::new(JayPointer { id: req.id, client: self.client.clone(), seat: seat.global.clone(), tracker: Default::default(), }); track!(self.client, ctx); self.client.add_client_obj(&ctx)?; Ok(()) } fn get_render_ctx(&self, req: GetRenderCtx, _slf: &Rc) -> Result<(), Self::Error> { let ctx = Rc::new(JayRenderCtx { id: req.id, client: self.client.clone(), tracker: Default::default(), version: self.version, }); track!(self.client, ctx); self.client.add_client_obj(&ctx)?; self.client .state .render_ctx_watchers .set((self.client.id, req.id), ctx.clone()); let rctx = self.client.state.render_ctx.get(); ctx.send_render_ctx(rctx); Ok(()) } fn watch_workspaces(&self, req: WatchWorkspaces, _slf: &Rc) -> Result<(), Self::Error> { let watcher = Rc::new(JayWorkspaceWatcher { id: req.id, client: self.client.clone(), tracker: Default::default(), }); track!(self.client, watcher); self.client.add_client_obj(&watcher)?; self.client .state .workspace_watchers .set((self.client.id, req.id), watcher.clone()); for ws in self.client.state.workspaces.lock().values() { watcher.send_workspace(ws)?; } Ok(()) } fn create_screencast(&self, req: CreateScreencast, _slf: &Rc) -> Result<(), Self::Error> { let sc = Rc::new_cyclic(|slf| JayScreencast::new(req.id, &self.client, slf, self.version)); track!(self.client, sc); self.client.add_client_obj(&sc)?; Ok(()) } fn get_randr(&self, req: GetRandr, _slf: &Rc) -> Result<(), Self::Error> { let sc = Rc::new(JayRandr::new(req.id, &self.client, self.version)); track!(self.client, sc); self.client.add_client_obj(&sc)?; Ok(()) } fn get_input(&self, req: GetInput, _slf: &Rc) -> Result<(), Self::Error> { let sc = Rc::new(JayInput::new(req.id, &self.client, self.version)); track!(self.client, sc); self.client.add_client_obj(&sc)?; Ok(()) } fn select_toplevel(&self, req: SelectToplevel, _slf: &Rc) -> Result<(), Self::Error> { let obj = JaySelectToplevel::new(&self.client, req.id, self.version); track!(self.client, obj); self.client.add_client_obj(&obj)?; let selector = JayToplevelSelector { tl: Default::default(), jst: obj.clone(), }; let seat = if req.seat.is_none() { match self.client.state.seat_queue.last() { Some(s) => s.deref().clone(), None => { obj.done(None); return Ok(()); } } } else { self.client.lookup(req.seat)?.global.clone() }; seat.select_toplevel(selector); Ok(()) } fn select_workspace(&self, req: SelectWorkspace, _slf: &Rc) -> Result<(), Self::Error> { let obj = Rc::new(JaySelectWorkspace { id: req.id, client: self.client.clone(), tracker: Default::default(), destroyed: Cell::new(false), }); track!(self.client, obj); self.client.add_client_obj(&obj)?; let selector = JayWorkspaceSelector { ws: Default::default(), jsw: obj.clone(), }; let seat = if req.seat.is_none() { match self.client.state.seat_queue.last() { Some(s) => s.deref().clone(), None => return Ok(()), } } else { self.client.lookup(req.seat)?.global.clone() }; seat.select_workspace(selector); Ok(()) } fn create_ei_session(&self, req: CreateEiSession, _slf: &Rc) -> Result<(), Self::Error> { let obj = Rc::new(JayEiSessionBuilder { id: req.id, client: self.client.clone(), tracker: Default::default(), version: self.version, app_id: Default::default(), }); track!(self.client, obj); self.client.add_client_obj(&obj)?; Ok(()) } fn get_xwayland(&self, req: GetXwayland, _slf: &Rc) -> Result<(), Self::Error> { let obj = Rc::new(JayXwayland { id: req.id, client: self.client.clone(), tracker: Default::default(), version: self.version, }); track!(self.client, obj); self.client.add_client_obj(&obj)?; Ok(()) } fn get_toplevel(&self, req: GetToplevel<'_>, _slf: &Rc) -> Result<(), Self::Error> { let obj = JaySelectToplevel::new(&self.client, req.id, self.version); track!(self.client, obj); self.client.add_client_obj(&obj)?; let tl = match ToplevelIdentifier::from_str(req.toplevel_id) { Ok(id) => self .client .state .toplevels .get(&id) .and_then(|w| w.upgrade()), Err(e) => { log::error!("Could not parse toplevel id: {}", ErrorFmt(e)); None } }; obj.done(tl); Ok(()) } fn get_color_management( &self, req: GetColorManagement, _slf: &Rc, ) -> Result<(), Self::Error> { let obj = Rc::new(JayColorManagement { id: req.id, client: self.client.clone(), tracker: Default::default(), version: self.version, }); track!(self.client, obj); self.client.add_client_obj(&obj)?; Ok(()) } fn reexec(&self, req: Reexec, _slf: &Rc) -> Result<(), Self::Error> { let obj = Rc::new(JayReexec { id: req.id, client: self.client.clone(), tracker: Default::default(), version: self.version, args: Default::default(), }); track!(self.client, obj); self.client.add_client_obj(&obj)?; Ok(()) } fn create_client_query( &self, req: CreateClientQuery, _slf: &Rc, ) -> Result<(), Self::Error> { let obj = Rc::new(JayClientQuery::new(&self.client, req.id, self.version)); track!(self.client, obj); self.client.add_client_obj(&obj)?; Ok(()) } fn kill_client(&self, req: KillClient, _slf: &Rc) -> Result<(), Self::Error> { self.client.state.clients.kill(ClientId::from_raw(req.id)); Ok(()) } fn create_tree_query(&self, req: CreateTreeQuery, _slf: &Rc) -> Result<(), Self::Error> { let obj = Rc::new(JayTreeQuery::new(&self.client, req.id, self.version)); track!(self.client, obj); self.client.add_client_obj(&obj)?; Ok(()) } fn get_tagged_acceptor( &self, req: GetTaggedAcceptor<'_>, _slf: &Rc, ) -> Result<(), Self::Error> { let obj = Rc::new(JayAcceptorRequest { id: req.id, client: self.client.clone(), tracker: Default::default(), version: self.version, }); track!(self.client, obj); self.client.add_client_obj(&obj)?; let state = &self.client.state; match state.tagged_acceptors.get(state, req.tag) { Ok(d) => obj.send_done(&d), Err(e) => obj.send_failed(e), } Ok(()) } fn get_sync_file_surface( &self, req: GetSyncFileSurface, _slf: &Rc, ) -> Result<(), Self::Error> { let surface = self.client.lookup(req.surface)?; let obj = Rc::new(JaySyncFileSurface::new(req.id, self.version, &surface)); track!(self.client, obj); self.client.add_client_obj(&obj)?; Ok(()) } fn get_pid(&self, _req: GetPid, _slf: &Rc) -> Result<(), Self::Error> { self.client.event(Pid { self_id: self.id, pid: self.client.state.pid, }); Ok(()) } fn open_control_center( &self, req: OpenControlCenter, _slf: &Rc, ) -> Result<(), Self::Error> { let obj = Rc::new(JayOpenControlCenterRequest { id: req.id, client: self.client.clone(), tracker: Default::default(), version: self.version, }); track!(self.client, obj); self.client.add_client_obj(&obj)?; if let Err(e) = self.client.state.open_control_center() { obj.send_failed(e); } Ok(()) } } object_base! { self = JayCompositor; version = self.version; } impl Object for JayCompositor {} simple_add_obj!(JayCompositor); #[derive(Debug, Error)] pub enum JayCompositorError { #[error(transparent)] ClientError(Box), #[error("Unknown log level {0}")] UnknownLogLevel(u32), } efrom!(JayCompositorError, ClientError);