1
0
Fork 0
forked from wry/wry
wry/src/ifs/jay_screencast.rs

499 lines
16 KiB
Rust

use {
crate::{
client::{Client, ClientError},
format::XRGB8888,
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
ifs::jay_output::JayOutput,
leaks::Tracker,
object::Object,
tree::{OutputNode, WorkspaceNodeId},
utils::{
buffd::{MsgParser, MsgParserError},
clonecell::CloneCell,
errorfmt::ErrorFmt,
numcell::NumCell,
option_ext::OptionExt,
},
video::{
dmabuf::DmaBuf,
gbm::{GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
Modifier, INVALID_MODIFIER, LINEAR_MODIFIER,
},
wire::{jay_screencast::*, JayScreencastId},
},
ahash::AHashSet,
indexmap::{indexset, IndexSet},
once_cell::sync::Lazy,
std::{
cell::{Cell, RefCell},
ops::{Deref, DerefMut},
rc::Rc,
},
thiserror::Error,
};
pub struct JayScreencast {
pub id: JayScreencastId,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
config_serial: NumCell<u32>,
config_acked: Cell<bool>,
buffers_serial: NumCell<u32>,
buffers_acked: Cell<bool>,
buffers: RefCell<Vec<ScreencastBuffer>>,
missed_frame: Cell<bool>,
output: CloneCell<Option<Rc<OutputNode>>>,
destroyed: Cell<bool>,
running: Cell<bool>,
show_all: Cell<bool>,
show_workspaces: RefCell<AHashSet<WorkspaceNodeId>>,
linear: Cell<bool>,
pending: Pending,
}
#[derive(Default)]
struct Pending {
linear: Cell<Option<bool>>,
running: Cell<Option<bool>>,
output: Cell<Option<Option<Rc<JayOutput>>>>,
show_all: Cell<Option<bool>>,
show_workspaces: RefCell<Option<AHashSet<WorkspaceNodeId>>>,
}
struct ScreencastBuffer {
dmabuf: DmaBuf,
fb: Rc<dyn GfxFramebuffer>,
free: bool,
}
impl JayScreencast {
pub fn new(id: JayScreencastId, client: &Rc<Client>) -> Self {
Self {
id,
client: client.clone(),
tracker: Default::default(),
config_serial: Default::default(),
config_acked: Cell::new(true),
buffers_serial: Default::default(),
buffers_acked: Cell::new(false),
buffers: Default::default(),
missed_frame: Cell::new(false),
output: Default::default(),
destroyed: Cell::new(false),
running: Cell::new(false),
show_all: Cell::new(false),
show_workspaces: Default::default(),
linear: Cell::new(false),
pending: Default::default(),
}
}
fn send_buffers(&self) {
self.buffers_acked.set(false);
let serial = self.buffers_serial.fetch_add(1) + 1;
let buffers = self.buffers.borrow_mut();
for buffer in buffers.iter() {
for plane in &buffer.dmabuf.planes {
self.client.event(Plane {
self_id: self.id,
fd: plane.fd.clone(),
offset: plane.offset,
stride: plane.stride,
});
}
self.client.event(Buffer {
self_id: self.id,
format: buffer.dmabuf.format.drm,
modifier: buffer.dmabuf.modifier,
width: buffer.dmabuf.width,
height: buffer.dmabuf.height,
});
}
self.client.event(BuffersDone {
self_id: self.id,
serial,
});
}
#[allow(dead_code)]
fn send_config(&self) {
self.config_acked.set(false);
let serial = self.config_serial.fetch_add(1) + 1;
if let Some(output) = self.output.get() {
self.client.event(ConfigOutput {
self_id: self.id,
linear_id: output.id.raw(),
});
}
self.client.event(ConfigAllowAllWorkspaces {
self_id: self.id,
allow_all: self.show_all.get() as _,
});
for &ws in self.show_workspaces.borrow_mut().iter() {
self.client.event(ConfigAllowWorkspace {
self_id: self.id,
linear_id: ws.raw(),
});
}
self.client.event(ConfigUseLinearBuffers {
self_id: self.id,
use_linear: self.linear.get() as _,
});
self.client.event(ConfigRunning {
self_id: self.id,
running: self.running.get() as _,
});
self.client.event(ConfigDone {
self_id: self.id,
serial,
});
}
pub fn copy_texture(
&self,
on: &OutputNode,
texture: &Rc<dyn GfxTexture>,
render_hardware_cursors: bool,
x_off: i32,
y_off: i32,
size: Option<(i32, i32)>,
) {
if !self.running.get() {
return;
}
if !self.show_all.get() {
let ws = match on.workspace.get() {
Some(ws) => ws,
_ => return,
};
if !self.show_workspaces.borrow_mut().contains(&ws.id) {
return;
}
}
let mut buffer = self.buffers.borrow_mut();
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
if buffer.free {
self.client.state.perform_screencopy(
texture,
&buffer.fb,
on.global.pos.get(),
render_hardware_cursors,
x_off,
y_off,
size,
on.global.persistent.transform.get(),
);
self.client.event(Ready {
self_id: self.id,
idx: idx as _,
});
buffer.free = false;
return;
}
}
self.missed_frame.set(true);
self.client.event(MissedFrame { self_id: self.id })
}
fn detach(&self) {
if let Some(output) = self.output.take() {
output.screencasts.remove(&(self.client.id, self.id));
if output.screencasts.is_empty() {
output.state.damage();
}
}
}
pub fn do_destroy(&self) {
self.detach();
self.destroyed.set(true);
self.client.event(Destroyed { self_id: self.id });
}
pub fn realloc(&self, ctx: &Rc<dyn GfxContext>) -> Result<(), JayScreencastError> {
let mut buffers = vec![];
let formats = ctx.formats();
let format = match formats.get(&XRGB8888.drm) {
Some(f) => f,
_ => return Err(JayScreencastError::XRGB8888),
};
if let Some(output) = self.output.get() {
let (width, height) = output.global.pixel_size();
let num = 3;
for _ in 0..num {
let mut usage = GBM_BO_USE_RENDERING;
let modifiers = match self.linear.get() {
true if format.write_modifiers.contains(&LINEAR_MODIFIER) => {
static MODS: Lazy<IndexSet<Modifier>> =
Lazy::new(|| indexset![LINEAR_MODIFIER]);
&MODS
}
true if format.write_modifiers.contains(&INVALID_MODIFIER) => {
usage |= GBM_BO_USE_LINEAR;
static MODS: Lazy<IndexSet<Modifier>> =
Lazy::new(|| indexset![INVALID_MODIFIER]);
&MODS
}
true => return Err(JayScreencastError::Modifier),
false if format.write_modifiers.is_empty() => {
return Err(JayScreencastError::XRGB8888Writing)
}
false => &format.write_modifiers,
};
let buffer = ctx.gbm().create_bo(
&self.client.state.dma_buf_ids,
width,
height,
XRGB8888,
modifiers,
usage,
)?;
let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
buffers.push(ScreencastBuffer {
dmabuf: buffer.dmabuf().clone(),
fb,
free: true,
});
}
}
*self.buffers.borrow_mut() = buffers;
self.send_buffers();
self.damage();
Ok(())
}
fn damage(&self) {
if let Some(output) = self.output.get() {
output.global.connector.connector.damage();
}
}
}
impl JayScreencast {
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let _req: Destroy = self.client.parse(self, parser)?;
self.detach();
self.client.remove_obj(self)?;
Ok(())
}
fn set_output(&self, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let req: SetOutput = self.client.parse(self, parser)?;
let output = if req.output.is_some() {
Some(self.client.lookup(req.output)?)
} else {
None
};
if self.destroyed.get() || !self.config_acked.get() {
return Ok(());
}
self.pending.output.set(Some(output));
Ok(())
}
fn set_allow_all_workspaces(
&self,
parser: MsgParser<'_, '_>,
) -> Result<(), JayScreencastError> {
let req: SetAllowAllWorkspaces = self.client.parse(self, parser)?;
if self.destroyed.get() || !self.config_acked.get() {
return Ok(());
}
self.pending.show_all.set(Some(req.allow_all != 0));
Ok(())
}
fn allow_workspace(&self, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let req: AllowWorkspace = self.client.parse(self, parser)?;
let ws = self.client.lookup(req.workspace)?;
if self.destroyed.get() || !self.config_acked.get() {
return Ok(());
}
let mut sw = self.pending.show_workspaces.borrow_mut();
let sw = sw.get_or_insert_default_ext();
if let Some(ws) = ws.workspace.get() {
sw.insert(ws.id);
}
Ok(())
}
fn touch_allowed_workspaces(
&self,
parser: MsgParser<'_, '_>,
) -> Result<(), JayScreencastError> {
let _req: TouchAllowedWorkspaces = self.client.parse(self, parser)?;
if self.destroyed.get() || !self.config_acked.get() {
return Ok(());
}
self.pending
.show_workspaces
.borrow_mut()
.get_or_insert_default_ext();
Ok(())
}
fn set_use_linear_buffers(&self, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let req: SetUseLinearBuffers = self.client.parse(self, parser)?;
if self.destroyed.get() || !self.config_acked.get() {
return Ok(());
}
self.pending.linear.set(Some(req.use_linear != 0));
Ok(())
}
fn set_running(&self, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let req: SetRunning = self.client.parse(self, parser)?;
if self.destroyed.get() || !self.config_acked.get() {
return Ok(());
}
self.pending.running.set(Some(req.running != 0));
Ok(())
}
fn configure(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let _req: Configure = self.client.parse(self.deref(), parser)?;
if self.destroyed.get() || !self.config_acked.get() {
return Ok(());
}
let mut need_realloc = false;
if let Some(output) = self.pending.output.take() {
let output = output.and_then(|o| o.output.get());
if output_size(&output) != output_size(&self.output.get()) {
need_realloc = true;
}
self.detach();
if let Some(new) = &output {
if new.screencasts.is_empty() {
new.state.damage();
}
new.screencasts.set((self.client.id, self.id), self.clone());
}
self.output.set(output);
}
if let Some(linear) = self.pending.linear.take() {
if self.linear.replace(linear) != linear {
need_realloc = true;
}
}
if let Some(show_all) = self.pending.show_all.take() {
self.show_all.set(show_all);
}
if let Some(new_workspaces) = self.pending.show_workspaces.borrow_mut().take() {
*self.show_workspaces.borrow_mut() = new_workspaces;
}
if let Some(running) = self.pending.running.take() {
self.running.set(running);
}
if need_realloc {
let ctx = match self.client.state.render_ctx.get() {
Some(ctx) => ctx,
_ => {
self.do_destroy();
return Ok(());
}
};
if let Err(e) = self.realloc(&ctx) {
log::error!("Could not allocate buffers: {}", ErrorFmt(e));
self.do_destroy();
return Ok(());
}
}
Ok(())
}
fn ack_buffers(&self, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let req: AckBuffers = self.client.parse(self, parser)?;
if self.destroyed.get() {
return Ok(());
}
if req.serial == self.buffers_serial.get() {
self.buffers_acked.set(true);
}
Ok(())
}
fn ack_config(&self, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let req: AckConfig = self.client.parse(self, parser)?;
if self.destroyed.get() {
return Ok(());
}
if req.serial == self.config_serial.get() {
self.config_acked.set(true);
}
Ok(())
}
fn release_buffer(&self, parser: MsgParser<'_, '_>) -> Result<(), JayScreencastError> {
let req: ReleaseBuffer = self.client.parse(self, parser)?;
if self.destroyed.get() || !self.buffers_acked.get() {
return Ok(());
}
let idx = req.idx as usize;
if idx > self.buffers.borrow_mut().len() {
return Err(JayScreencastError::OutOfBounds(req.idx));
}
self.buffers.borrow_mut()[idx].free = true;
if self.missed_frame.replace(false) {
self.damage();
}
Ok(())
}
}
object_base! {
self = JayScreencast;
DESTROY => destroy,
SET_OUTPUT => set_output,
SET_ALLOW_ALL_WORKSPACES => set_allow_all_workspaces,
ALLOW_WORKSPACE => allow_workspace,
TOUCH_ALLOWED_WORKSPACES => touch_allowed_workspaces,
SET_USE_LINEAR_BUFFERS => set_use_linear_buffers,
SET_RUNNING => set_running,
CONFIGURE => configure,
ACK_CONFIG => ack_config,
ACK_BUFFERS => ack_buffers,
RELEASE_BUFFER => release_buffer,
}
impl Object for JayScreencast {
fn break_loops(&self) {
self.detach();
}
}
dedicated_add_obj!(JayScreencast, JayScreencastId, screencasts);
#[derive(Debug, Error)]
pub enum JayScreencastError {
#[error("Parsing failed")]
MsgParserError(Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("Buffer index {0} is out-of-bounds")]
OutOfBounds(u32),
#[error(transparent)]
GbmError(#[from] GbmError),
#[error(transparent)]
GfxError(#[from] GfxError),
#[error("Render context does not support XRGB8888 format")]
XRGB8888,
#[error("Render context does not support XRGB8888 format for rendering")]
XRGB8888Writing,
#[error("Render context supports neither linear or invalid modifier")]
Modifier,
}
efrom!(JayScreencastError, MsgParserError);
efrom!(JayScreencastError, ClientError);
fn output_size(output: &Option<Rc<OutputNode>>) -> (i32, i32) {
match output {
Some(o) => o.global.pixel_size(),
_ => (0, 0),
}
}