1
0
Fork 0
forked from wry/wry
wry/src/ifs/jay_compositor.rs
2024-09-01 20:25:22 +02:00

429 lines
13 KiB
Rust

use {
crate::{
cli::CliLogLevel,
client::{Client, ClientCaps, ClientError, CAP_JAY_COMPOSITOR},
globals::{Global, GlobalName},
ifs::{
jay_ei_session_builder::JayEiSessionBuilder,
jay_idle::JayIdle,
jay_input::JayInput,
jay_log_file::JayLogFile,
jay_output::JayOutput,
jay_pointer::JayPointer,
jay_randr::JayRandr,
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_workspace_watcher::JayWorkspaceWatcher,
},
leaks::Tracker,
object::{Object, Version},
screenshoter::take_screenshot,
utils::errorfmt::ErrorFmt,
wire::{jay_compositor::*, JayCompositorId, JayScreenshotId},
},
bstr::ByteSlice,
log::Level,
std::{cell::Cell, ops::Deref, rc::Rc},
thiserror::Error,
};
pub const CREATE_EI_SESSION_SINCE: Version = Version(5);
pub const SCREENSHOT_SPLITUP_SINCE: Version = Version(6);
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 singleton(&self) -> bool {
true
}
fn version(&self) -> u32 {
6
}
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> {
log::info!("Quitting");
self.client.state.ring.stop();
Ok(())
}
fn set_log_level(&self, req: SetLogLevel, _slf: &Rc<Self>) -> Result<(), Self::Error> {
const ERROR: u32 = CliLogLevel::Error as u32;
const WARN: u32 = CliLogLevel::Warn as u32;
const INFO: u32 = CliLogLevel::Info as u32;
const DEBUG: u32 = CliLogLevel::Debug as u32;
const TRACE: u32 = CliLogLevel::Trace as u32;
let level = match req.level {
ERROR => Level::Error,
WARN => Level::Warn,
INFO => Level::Info,
DEBUG => Level::Debug,
TRACE => Level::Trace,
_ => return Err(JayCompositorError::UnknownLogLevel(req.level)),
};
if let Some(logger) = &self.client.state.logger {
logger.set_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(),
});
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(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(),
});
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));
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 seat = self.client.lookup(req.seat)?;
let obj = Rc::new(JaySelectToplevel {
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 = JayToplevelSelector {
tl: Default::default(),
jst: obj.clone(),
};
seat.global.select_toplevel(selector);
Ok(())
}
fn select_workspace(&self, req: SelectWorkspace, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let seat = self.client.lookup(req.seat)?;
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(),
};
seat.global.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(())
}
}
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);