1
0
Fork 0
forked from wry/wry
wry/src/ifs/jay_compositor.rs
2026-03-13 16:27:39 +01:00

582 lines
18 KiB
Rust

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<Self>,
id: JayCompositorId,
client: &Rc<Client>,
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<Client>,
tracker: Tracker<Self>,
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<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
fn get_log_file(&self, req: GetLogFile, _slf: &Rc<Self>) -> 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<Self>) -> Result<(), Self::Error> {
self.client.state.quit();
Ok(())
}
fn set_log_level(&self, req: SetLogLevel, _slf: &Rc<Self>) -> 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<Self>) -> Result<(), Self::Error> {
self.take_screenshot_impl(req.id, false)
}
fn take_screenshot2(&self, req: TakeScreenshot2, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.take_screenshot_impl(req.id, req.include_cursor != 0)
}
fn get_idle(&self, req: GetIdle, _slf: &Rc<Self>) -> 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<Self>) -> 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<Self>,
) -> Result<(), Self::Error> {
self.client.symmetric_delete.set(true);
Ok(())
}
fn unlock(&self, _req: Unlock, _slf: &Rc<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>,
) -> 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<Self>) -> 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<Self>,
) -> 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<Self>) -> Result<(), Self::Error> {
self.client.state.clients.kill(ClientId::from_raw(req.id));
Ok(())
}
fn create_tree_query(&self, req: CreateTreeQuery, _slf: &Rc<Self>) -> 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<Self>,
) -> 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<Self>,
) -> 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<Self>) -> 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<Self>,
) -> 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<ClientError>),
#[error("Unknown log level {0}")]
UnknownLogLevel(u32),
}
efrom!(JayCompositorError, ClientError);