1
0
Fork 0
forked from wry/wry

screencast: implement format negotation

This commit is contained in:
Julian Orth 2024-09-04 12:03:42 +02:00
parent 8d2bd6f660
commit e7c63fd09a
19 changed files with 649 additions and 201 deletions

View file

@ -180,7 +180,7 @@ fn start_compositor2(
pending_float_titles: Default::default(),
pending_input_popup_positioning: Default::default(),
pending_toplevel_screencasts: Default::default(),
pending_toplevel_screencast_reallocs: Default::default(),
pending_screencast_reallocs_or_reconfigures: Default::default(),
dbus: Dbus::new(&engine, &ring, &run_toplevel),
fdcloser: FdCloser::new(),
logger: logger.clone(),

View file

@ -586,3 +586,39 @@ impl Debug for GfxError {
Debug::fmt(&self.0, f)
}
}
impl GfxFormat {
pub fn cross_intersect(&self, other: &GfxFormat) -> GfxFormat {
assert_eq!(self.format, other.format);
GfxFormat {
format: self.format,
read_modifiers: self
.read_modifiers
.intersection(&other.write_modifiers)
.copied()
.collect(),
write_modifiers: self
.write_modifiers
.intersection(&other.read_modifiers)
.copied()
.collect(),
}
}
}
pub fn cross_intersect_formats(
local: &AHashMap<u32, GfxFormat>,
remote: &AHashMap<u32, GfxFormat>,
) -> AHashMap<u32, GfxFormat> {
let mut res = AHashMap::new();
for lf in local.values() {
if let Some(rf) = remote.get(&lf.format.drm) {
let f = lf.cross_intersect(rf);
if f.read_modifiers.is_empty() && f.write_modifiers.is_empty() {
continue;
}
res.insert(f.format.drm, f);
}
}
res
}

View file

@ -70,7 +70,7 @@ impl Global for JayCompositorGlobal {
}
fn version(&self) -> u32 {
6
7
}
fn required_caps(&self) -> ClientCaps {
@ -309,6 +309,7 @@ impl JayCompositorRequestHandler for JayCompositor {
id: req.id,
client: self.client.clone(),
tracker: Default::default(),
version: self.version,
});
track!(self.client, ctx);
self.client.add_client_obj(&ctx)?;
@ -340,7 +341,7 @@ impl JayCompositorRequestHandler for JayCompositor {
}
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));
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(())

View file

@ -11,16 +11,41 @@ use {
thiserror::Error,
};
pub const FORMATS_SINCE: Version = Version(7);
pub struct JayRenderCtx {
pub id: JayRenderCtxId,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl JayRenderCtx {
pub fn send_render_ctx(&self, ctx: Option<Rc<dyn GfxContext>>) {
let mut fd = None;
if let Some(ctx) = ctx {
if self.version >= FORMATS_SINCE {
for format in ctx.formats().values() {
self.client.event(Format {
self_id: self.id,
format: format.format.drm,
});
for modifier in &format.write_modifiers {
self.client.event(WriteModifier {
self_id: self.id,
format: format.format.drm,
modifier: *modifier,
});
}
for modifier in &format.read_modifiers {
self.client.event(ReadModifier {
self_id: self.id,
format: format.format.drm,
modifier: *modifier,
});
}
}
}
let allocator = ctx.allocator();
match allocator.drm() {
Some(drm) => match drm.dup_render() {
@ -33,8 +58,6 @@ impl JayRenderCtx {
log::error!("Allocator does not have a DRM device");
}
}
} else {
self.client.event(NoDevice { self_id: self.id });
}
match fd {
Some(fd) => self.client.event(Device {

View file

@ -4,7 +4,7 @@ use {
client::{Client, ClientError},
format::XRGB8888,
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
ifs::{jay_output::JayOutput, jay_toplevel::JayToplevel},
ifs::{jay_output::JayOutput, jay_toplevel::JayToplevel, wl_buffer::WlBufferStorage},
leaks::Tracker,
object::{Object, Version},
scale::Scale,
@ -39,8 +39,11 @@ pub async fn perform_toplevel_screencasts(state: Rc<State>) {
pub async fn perform_screencast_realloc(state: Rc<State>) {
loop {
let screencast = state.pending_toplevel_screencast_reallocs.pop().await;
screencast.realloc_scheduled.set(false);
let screencast = state
.pending_screencast_reallocs_or_reconfigures
.pop()
.await;
screencast.realloc_or_reconfigure_scheduled.set(false);
match state.render_ctx.get() {
None => screencast.do_destroy(),
Some(ctx) => {
@ -52,8 +55,11 @@ pub async fn perform_screencast_realloc(state: Rc<State>) {
}
}
pub const CLIENT_BUFFERS_SINCE: Version = Version(7);
pub struct JayScreencast {
pub id: JayScreencastId,
pub version: Version,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
config_serial: NumCell<u32>,
@ -69,8 +75,8 @@ pub struct JayScreencast {
show_workspaces: RefCell<AHashSet<WorkspaceNodeId>>,
linear: Cell<bool>,
pending: Pending,
need_realloc: Cell<bool>,
realloc_scheduled: Cell<bool>,
need_realloc_or_reconfigure: Cell<bool>,
realloc_or_reconfigure_scheduled: Cell<bool>,
latch_listener: EventListener<dyn LatchListener>,
}
@ -100,11 +106,13 @@ struct Pending {
target: Cell<Option<Option<PendingTarget>>>,
show_all: Cell<Option<bool>>,
show_workspaces: RefCell<Option<AHashSet<WorkspaceNodeId>>>,
clear_buffers: Cell<bool>,
buffers: RefCell<Vec<Rc<dyn GfxFramebuffer>>>,
}
struct ScreencastBuffer {
_bo: Rc<dyn BufferObject>,
dmabuf: DmaBuf,
_bo: Option<Rc<dyn BufferObject>>,
dmabuf: Option<DmaBuf>,
fb: Rc<dyn GfxFramebuffer>,
free: bool,
}
@ -122,9 +130,15 @@ impl JayScreencast {
false
}
pub fn new(id: JayScreencastId, client: &Rc<Client>, slf: &Weak<Self>) -> Self {
pub fn new(
id: JayScreencastId,
client: &Rc<Client>,
slf: &Weak<Self>,
version: Version,
) -> Self {
Self {
id,
version,
client: client.clone(),
tracker: Default::default(),
config_serial: Default::default(),
@ -140,8 +154,8 @@ impl JayScreencast {
show_workspaces: Default::default(),
linear: Cell::new(false),
pending: Default::default(),
need_realloc: Cell::new(false),
realloc_scheduled: Cell::new(false),
need_realloc_or_reconfigure: Cell::new(false),
realloc_or_reconfigure_scheduled: Cell::new(false),
latch_listener: EventListener::new(slf.clone()),
}
}
@ -210,7 +224,12 @@ impl JayScreencast {
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 {
let Some(dmabuf) = &buffer.dmabuf else {
log::error!("Trying to send buffers but buffers are client allocated");
self.do_destroy();
return;
};
for plane in &dmabuf.planes {
self.client.event(Plane {
self_id: self.id,
fd: plane.fd.clone(),
@ -220,10 +239,10 @@ impl JayScreencast {
}
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,
format: dmabuf.format.drm,
modifier: dmabuf.modifier,
width: dmabuf.width,
height: dmabuf.height,
});
}
self.client.event(BuffersDone {
@ -232,11 +251,19 @@ impl JayScreencast {
});
}
#[allow(dead_code)]
fn send_config(&self) {
self.need_realloc_or_reconfigure.set(false);
self.config_acked.set(false);
let serial = self.config_serial.fetch_add(1) + 1;
if let Some(target) = self.target.get() {
let (width, height) = target_size(Some(&target));
if self.version >= CLIENT_BUFFERS_SINCE {
self.client.event(ConfigSize {
self_id: self.id,
width,
height,
});
}
if let Target::Output(output) = target {
self.client.event(ConfigOutput {
self_id: self.id,
@ -339,30 +366,39 @@ impl JayScreencast {
pub fn do_destroy(&self) {
self.detach();
self.buffers.borrow_mut().clear();
self.destroyed.set(true);
self.client.event(Destroyed { self_id: self.id });
}
pub fn schedule_realloc(self: &Rc<Self>) {
self.need_realloc.set(true);
if !self.realloc_scheduled.replace(true) {
pub fn schedule_realloc_or_reconfigure(self: &Rc<Self>) {
self.need_realloc_or_reconfigure.set(true);
if !self.realloc_or_reconfigure_scheduled.replace(true) {
self.client
.state
.pending_toplevel_screencast_reallocs
.pending_screencast_reallocs_or_reconfigures
.push(self.clone());
}
}
fn realloc(&self, ctx: &Rc<dyn GfxContext>) -> Result<(), JayScreencastError> {
if !self.destroyed.get() && self.buffers_acked.get() {
self.do_realloc(ctx)
} else {
Ok(())
if self.destroyed.get() {
return Ok(());
}
if self.version < CLIENT_BUFFERS_SINCE {
if self.buffers_acked.get() {
return self.do_realloc(ctx);
}
} else {
if self.config_acked.get() {
self.send_config();
}
}
Ok(())
}
fn do_realloc(&self, ctx: &Rc<dyn GfxContext>) -> Result<(), JayScreencastError> {
self.need_realloc.set(false);
self.need_realloc_or_reconfigure.set(false);
let mut buffers = vec![];
let formats = ctx.formats();
let format = match formats.get(&XRGB8888.drm) {
@ -395,14 +431,14 @@ impl JayScreencast {
&self.client.state.dma_buf_ids,
width,
height,
XRGB8888,
format.format,
&modifiers,
usage,
)?;
let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
buffers.push(ScreencastBuffer {
dmabuf: buffer.dmabuf().clone(),
_bo: buffer,
dmabuf: Some(buffer.dmabuf().clone()),
_bo: Some(buffer),
fb,
free: true,
});
@ -457,7 +493,7 @@ impl JayScreencastRequestHandler for JayScreencast {
} else {
None
};
if self.destroyed.get() || !self.config_acked.get() {
if self.destroyed.get() {
return Ok(());
}
self.pending.target.set(Some(output));
@ -469,7 +505,7 @@ impl JayScreencastRequestHandler for JayScreencast {
req: SetAllowAllWorkspaces,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if self.destroyed.get() || !self.config_acked.get() {
if self.destroyed.get() {
return Ok(());
}
self.pending.show_all.set(Some(req.allow_all != 0));
@ -478,7 +514,7 @@ impl JayScreencastRequestHandler for JayScreencast {
fn allow_workspace(&self, req: AllowWorkspace, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let ws = self.client.lookup(req.workspace)?;
if self.destroyed.get() || !self.config_acked.get() {
if self.destroyed.get() {
return Ok(());
}
let mut sw = self.pending.show_workspaces.borrow_mut();
@ -494,7 +530,7 @@ impl JayScreencastRequestHandler for JayScreencast {
_req: TouchAllowedWorkspaces,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if self.destroyed.get() || !self.config_acked.get() {
if self.destroyed.get() {
return Ok(());
}
self.pending
@ -509,7 +545,7 @@ impl JayScreencastRequestHandler for JayScreencast {
req: SetUseLinearBuffers,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if self.destroyed.get() || !self.config_acked.get() {
if self.destroyed.get() {
return Ok(());
}
self.pending.linear.set(Some(req.use_linear != 0));
@ -517,7 +553,7 @@ impl JayScreencastRequestHandler for JayScreencast {
}
fn set_running(&self, req: SetRunning, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.destroyed.get() || !self.config_acked.get() {
if self.destroyed.get() {
return Ok(());
}
self.pending.running.set(Some(req.running != 0));
@ -525,11 +561,11 @@ impl JayScreencastRequestHandler for JayScreencast {
}
fn configure(&self, _req: Configure, slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.destroyed.get() || !self.config_acked.get() {
if self.destroyed.get() {
return Ok(());
}
let mut need_realloc = false;
let mut need_realloc_or_reconfigure = false;
if let Some(target) = self.pending.target.take() {
self.detach();
@ -561,15 +597,26 @@ impl JayScreencastRequestHandler for JayScreencast {
}
}
if target_size(new_target.as_ref()) != target_size(self.target.get().as_ref()) {
need_realloc = true;
need_realloc_or_reconfigure = true;
}
self.target.set(new_target);
}
if let Some(linear) = self.pending.linear.take() {
if self.linear.replace(linear) != linear {
need_realloc = true;
need_realloc_or_reconfigure = true;
}
}
if self.pending.clear_buffers.take() {
self.buffers.borrow_mut().clear();
}
for buffer in self.pending.buffers.borrow_mut().drain(..) {
self.buffers.borrow_mut().push(ScreencastBuffer {
_bo: None,
dmabuf: None,
fb: buffer,
free: true,
});
}
let mut capture_rules_changed = false;
if let Some(show_all) = self.pending.show_all.take() {
self.show_all.set(show_all);
@ -583,8 +630,8 @@ impl JayScreencastRequestHandler for JayScreencast {
self.running.set(running);
}
if need_realloc {
slf.schedule_realloc();
if need_realloc_or_reconfigure {
slf.schedule_realloc_or_reconfigure();
}
if capture_rules_changed {
@ -606,19 +653,22 @@ impl JayScreencastRequestHandler for JayScreencast {
}
if req.serial == self.buffers_serial.get() {
self.buffers_acked.set(true);
if self.need_realloc.get() {
slf.schedule_realloc();
if self.need_realloc_or_reconfigure.get() {
slf.schedule_realloc_or_reconfigure();
}
}
Ok(())
}
fn ack_config(&self, req: AckConfig, _slf: &Rc<Self>) -> Result<(), Self::Error> {
fn ack_config(&self, req: AckConfig, slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
if req.serial == self.config_serial.get() {
self.config_acked.set(true);
if self.need_realloc_or_reconfigure.get() {
slf.schedule_realloc_or_reconfigure();
}
}
Ok(())
}
@ -628,7 +678,7 @@ impl JayScreencastRequestHandler for JayScreencast {
return Ok(());
}
let idx = req.idx as usize;
if idx > self.buffers.borrow_mut().len() {
if idx >= self.buffers.borrow_mut().len() {
return Err(JayScreencastError::OutOfBounds(req.idx));
}
self.buffers.borrow_mut()[idx].free = true;
@ -644,17 +694,46 @@ impl JayScreencastRequestHandler for JayScreencast {
} else {
None
};
if self.destroyed.get() || !self.config_acked.get() {
if self.destroyed.get() {
return Ok(());
}
self.pending.target.set(Some(toplevel));
Ok(())
}
fn clear_buffers(&self, _req: ClearBuffers, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
self.pending.clear_buffers.set(true);
Ok(())
}
fn add_buffer(&self, req: AddBuffer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
let buffer = self.client.lookup(req.buffer)?;
if let Some(WlBufferStorage::Dmabuf { img, .. }) = &*buffer.storage.borrow() {
match img.clone().to_framebuffer() {
Ok(fb) => self.pending.buffers.borrow_mut().push(fb),
Err(e) => {
log::warn!(
"Could not turn GfxImage into GfxFramebuffer: {}",
ErrorFmt(e)
);
self.do_destroy();
}
}
return Ok(());
}
Err(JayScreencastError::NotDmabuf)
}
}
object_base! {
self = JayScreencast;
version = Version(1);
version = self.version;
}
impl Object for JayScreencast {
@ -681,6 +760,8 @@ pub enum JayScreencastError {
XRGB8888Writing,
#[error("Render context supports neither linear or invalid modifier")]
Modifier,
#[error("Buffer is not a dmabuf")]
NotDmabuf,
}
efrom!(JayScreencastError, ClientError);

View file

@ -141,7 +141,7 @@ pub struct SurfaceSendPreferredScaleVisitor;
impl SurfaceSendPreferredScaleVisitor {
fn schedule_realloc(&self, tl: &impl ToplevelNode) {
for sc in tl.tl_data().jay_screencasts.lock().values() {
sc.schedule_realloc();
sc.schedule_realloc_or_reconfigure();
}
}
}

View file

@ -12,9 +12,9 @@ use {
pw_pod::{
pw_node_activation, spa_chunk, spa_io_buffers, spa_meta_bitmap, spa_meta_busy,
spa_meta_cursor, spa_meta_header, spa_meta_region, PW_CHOICE_Enum, PW_CHOICE_Flags,
PW_OBJECT_Format, PW_OBJECT_ParamBuffers, PW_OBJECT_ParamMeta, PwIoType,
PwPodFraction, PwPodObject, PwPodRectangle, PwPropFlag, SPA_DATA_DmaBuf,
SPA_DATA_MemFd, SPA_DATA_MemPtr, SPA_FORMAT_VIDEO_format,
PW_OBJECT_Format, PW_OBJECT_ParamBuffers, PW_OBJECT_ParamMeta, PW_TYPE_Long,
PwIoType, PwPod, PwPodFraction, PwPodObject, PwPodRectangle, PwPropFlag,
SPA_DATA_DmaBuf, SPA_DATA_MemFd, SPA_DATA_MemPtr, SPA_FORMAT_VIDEO_format,
SPA_FORMAT_VIDEO_framerate, SPA_FORMAT_VIDEO_modifier, SPA_FORMAT_VIDEO_size,
SPA_FORMAT_mediaSubtype, SPA_FORMAT_mediaType, SPA_IO_Buffers, SPA_META_Bitmap,
SPA_META_Busy, SPA_META_Control, SPA_META_Cursor, SPA_META_Header,
@ -26,21 +26,21 @@ use {
SpaMediaSubtype, SpaMediaType, SpaMetaType, SpaNodeBuffersFlags, SpaNodeCommand,
SpaParamType, SpaVideoFormat, PW_NODE_ACTIVATION_FINISHED,
PW_NODE_ACTIVATION_NOT_TRIGGERED, PW_NODE_ACTIVATION_TRIGGERED,
SPA_DATA_FLAG_READABLE, SPA_DIRECTION_INPUT, SPA_DIRECTION_OUTPUT,
SPA_NODE_BUFFERS_FLAG_ALLOC, SPA_PARAM_INFO, SPA_PARAM_INFO_READ,
SPA_PARAM_INFO_SERIAL, SPA_PORT_FLAG, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS,
PW_PROP_DONT_FIXATE, SPA_DATA_FLAG_READABLE, SPA_DIRECTION_INPUT,
SPA_DIRECTION_OUTPUT, SPA_NODE_BUFFERS_FLAG_ALLOC, SPA_PARAM_INFO,
SPA_PARAM_INFO_READ, SPA_PARAM_INFO_SERIAL, SPA_PORT_FLAG,
SPA_PORT_FLAG_CAN_ALLOC_BUFFERS,
},
},
utils::{
bitfield::Bitfield, buf::TypedBuf, clonecell::CloneCell, copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
errorfmt::ErrorFmt, option_ext::OptionExt,
},
video::dmabuf::DmaBuf,
video::{dmabuf::DmaBuf, Modifier},
},
std::{
cell::{Cell, RefCell},
mem,
ops::Deref,
rc::Rc,
sync::atomic::Ordering::{Relaxed, Release},
},
@ -80,7 +80,7 @@ pub trait PwClientNodeOwner {
fn port_format_changed(&self, port: &Rc<PwClientNodePort>) {
let _ = port;
}
fn use_buffers(&self, port: &Rc<PwClientNodePort>) {
fn use_buffers(self: Rc<Self>, port: &Rc<PwClientNodePort>) {
let _ = port;
}
fn start(self: Rc<Self>) {}
@ -113,14 +113,14 @@ pub struct PwClientNodePort {
pub _destroyed: Cell<bool>,
pub effective_format: Cell<PwClientNodePortFormat>,
pub supported_formats: RefCell<Option<PwClientNodePortSupportedFormats>>,
pub negotiated_format: RefCell<PwClientNodePortFormat>,
pub supported_formats: RefCell<PwClientNodePortSupportedFormats>,
pub supported_metas: Cell<PwClientNodePortSupportedMetas>,
pub can_alloc_buffers: Cell<bool>,
pub buffers: RefCell<Vec<Rc<PwClientNodeBuffer>>>,
pub buffer_config: Cell<Option<PwClientNodeBufferConfig>>,
pub buffer_config: RefCell<PwClientNodeBufferConfig>,
pub io_buffers: CloneCell<Option<Rc<PwMemTyped<spa_io_buffers>>>>,
@ -129,11 +129,8 @@ pub struct PwClientNodePort {
#[derive(Copy, Clone, Debug, Default)]
pub struct PwClientNodeBufferConfig {
pub num_buffers: usize,
pub planes: usize,
pub _size: Option<u32>,
pub _stride: Option<u32>,
pub _align: usize,
pub num_buffers: Option<usize>,
pub planes: Option<usize>,
pub data_type: SpaDataType,
}
@ -145,21 +142,27 @@ pub struct PwClientNodeBuffer {
pub _slices: Vec<Rc<PwMemSlice>>,
}
#[derive(Clone, Debug)]
pub struct PwClientNodePortSupportedFormat {
pub format: &'static Format,
pub modifiers: Vec<u64>,
}
#[derive(Clone, Debug, Default)]
pub struct PwClientNodePortSupportedFormats {
pub media_type: Option<SpaMediaType>,
pub media_sub_type: Option<SpaMediaSubtype>,
pub video_size: Option<PwPodRectangle>,
pub formats: Vec<&'static Format>,
pub modifiers: Vec<u64>,
pub formats: Vec<PwClientNodePortSupportedFormat>,
}
#[derive(Copy, Clone, Debug, Default)]
#[derive(Clone, Debug, Default)]
pub struct PwClientNodePortFormat {
pub media_type: Option<SpaMediaType>,
pub media_sub_type: Option<SpaMediaSubtype>,
pub video_size: Option<PwPodRectangle>,
pub format: Option<&'static Format>,
pub modifiers: Option<Vec<Modifier>>,
pub framerate: Option<PwPodFraction>,
}
@ -243,7 +246,12 @@ impl PwClientNode {
});
}
pub fn create_port(self: &Rc<Self>, output: bool) -> Rc<PwClientNodePort> {
pub fn create_port(
self: &Rc<Self>,
output: bool,
supported_formats: PwClientNodePortSupportedFormats,
num_buffers: Option<usize>,
) -> Rc<PwClientNodePort> {
let (ids, direction) = match output {
true => (&self.port_out_free, SPA_DIRECTION_OUTPUT),
false => (&self.port_in_free, SPA_DIRECTION_INPUT),
@ -253,12 +261,16 @@ impl PwClientNode {
direction,
id: ids.borrow_mut().acquire(),
_destroyed: Cell::new(false),
effective_format: Cell::new(Default::default()),
supported_formats: RefCell::new(None),
negotiated_format: Default::default(),
supported_formats: RefCell::new(supported_formats),
supported_metas: Cell::new(PwClientNodePortSupportedMetas::none()),
can_alloc_buffers: Cell::new(false),
buffers: RefCell::new(vec![]),
buffer_config: Cell::new(None),
buffer_config: RefCell::new(PwClientNodeBufferConfig {
num_buffers,
planes: None,
data_type: SPA_DATA_DmaBuf,
}),
io_buffers: Default::default(),
serial: Cell::new(false),
});
@ -297,10 +309,8 @@ impl PwClientNode {
});
}
pub fn send_port_update(&self, port: &PwClientNodePort, re_init: bool) {
if re_init {
port.serial.set(!port.serial.get());
}
pub fn send_port_update(&self, port: &PwClientNodePort, fixate: bool) {
port.serial.set(!port.serial.get());
let serial = match port.serial.get() {
true => SPA_PARAM_INFO_SERIAL,
false => SPA_PARAM_INFO::none(),
@ -324,18 +334,14 @@ impl PwClientNode {
if sm.contains(SUPPORTED_META_VIDEO_CROP) {
metas.push((SPA_META_VideoCrop, mem::size_of::<spa_meta_region>()));
}
let sf = port.supported_formats.borrow_mut();
let bc = port.buffer_config.get();
let mut num_params = metas.len() as u32;
if sf.is_some() {
num_params += 1;
}
if bc.is_some() {
num_params += 1;
}
let sf = &*port.supported_formats.borrow();
let num_formats = sf.formats.len() as u32;
let bc = &*port.buffer_config.borrow();
let num_params = metas.len() as u32 + num_formats + 1;
// num params
f.write_uint(num_params);
if let Some(sf) = sf.deref() {
for format in &sf.formats {
f.write_object(PW_OBJECT_Format, SPA_PARAM_EnumFormat.0, |f| {
if let Some(mt) = sf.media_type {
f.write_property(SPA_FORMAT_mediaType.0, PwPropFlag::none(), |f| {
@ -347,30 +353,28 @@ impl PwClientNode {
f.write_id(mst.0);
});
}
if sf.formats.len() > 0 {
f.write_property(SPA_FORMAT_VIDEO_format.0, PwPropFlag::none(), |f| {
f.write_property(SPA_FORMAT_VIDEO_format.0, PwPropFlag::none(), |f| {
f.write_choice(PW_CHOICE_Enum, 0, |f| {
f.write_id(format.format.pipewire.0);
f.write_id(format.format.pipewire.0);
});
});
f.write_property(
SPA_FORMAT_VIDEO_modifier.0,
if fixate {
PwPropFlag::none()
} else {
PW_PROP_DONT_FIXATE
},
|f| {
f.write_choice(PW_CHOICE_Enum, 0, |f| {
f.write_id(sf.formats[0].pipewire.0);
for format in &sf.formats {
f.write_id(format.pipewire.0);
f.write_ulong(format.modifiers[0]);
for modifier in &format.modifiers {
f.write_ulong(*modifier);
}
});
});
}
if sf.modifiers.len() > 0 {
f.write_property(
SPA_FORMAT_VIDEO_modifier.0,
PwPropFlag::none(),
|f| {
f.write_choice(PW_CHOICE_Enum, 0, |f| {
f.write_ulong(sf.modifiers[0]);
for modifier in &sf.modifiers {
f.write_ulong(*modifier);
}
});
},
);
}
},
);
if let Some(vs) = sf.video_size {
f.write_property(SPA_FORMAT_VIDEO_size.0, PwPropFlag::none(), |f| {
f.write_choice(PW_CHOICE_Enum, 0, |f| {
@ -381,34 +385,23 @@ impl PwClientNode {
}
});
}
if let Some(bc) = &bc {
f.write_object(PW_OBJECT_ParamBuffers, SPA_PARAM_Buffers.0, |f| {
f.write_object(PW_OBJECT_ParamBuffers, SPA_PARAM_Buffers.0, |f| {
if let Some(num_buffers) = bc.num_buffers {
f.write_property(SPA_PARAM_BUFFERS_buffers.0, PwPropFlag::none(), |f| {
f.write_uint(bc.num_buffers as _);
f.write_uint(num_buffers as _);
});
}
if let Some(planes) = bc.planes {
f.write_property(SPA_PARAM_BUFFERS_blocks.0, PwPropFlag::none(), |f| {
f.write_uint(bc.planes as _);
f.write_uint(planes as _);
});
// if let Some(size) = bc.size {
// f.write_property(SPA_PARAM_BUFFERS_size.0, PwPropFlag::none(), |f| {
// f.write_uint(size as _);
// });
// }
// if let Some(stride) = bc.stride {
// f.write_property(SPA_PARAM_BUFFERS_stride.0, PwPropFlag::none(), |f| {
// f.write_uint(stride as _);
// });
// }
// f.write_property(SPA_PARAM_BUFFERS_align.0, PwPropFlag::none(), |f| {
// f.write_uint(bc.align as _);
// });
f.write_property(SPA_PARAM_BUFFERS_dataType.0, PwPropFlag::none(), |f| {
f.write_choice(PW_CHOICE_Flags, 0, |f| {
f.write_uint(1 << bc.data_type.0);
});
}
f.write_property(SPA_PARAM_BUFFERS_dataType.0, PwPropFlag::none(), |f| {
f.write_choice(PW_CHOICE_Flags, 0, |f| {
f.write_uint(1 << bc.data_type.0);
});
});
}
});
for (key, size) in metas {
f.write_object(PW_OBJECT_ParamMeta, SPA_PARAM_Meta.0, |f| {
f.write_property(SPA_PARAM_META_type.0, PwPropFlag::none(), |f| {
@ -439,23 +432,13 @@ impl PwClientNode {
f.write_int(1);
// num props
f.write_int(0);
let mut num_params = 1;
if sf.is_some() {
num_params += 1;
}
if bc.is_some() {
num_params += 1;
}
let num_params = 3;
// num params
f.write_uint(num_params);
if sf.is_some() {
f.write_id(SPA_PARAM_EnumFormat.0);
f.write_uint((SPA_PARAM_INFO_READ | serial).0);
}
if bc.is_some() {
f.write_id(SPA_PARAM_Buffers.0);
f.write_uint((SPA_PARAM_INFO_READ | serial).0);
}
f.write_id(SPA_PARAM_EnumFormat.0);
f.write_uint((SPA_PARAM_INFO_READ | serial).0);
f.write_id(SPA_PARAM_Buffers.0);
f.write_uint((SPA_PARAM_INFO_READ | serial).0);
f.write_id(SPA_PARAM_Meta.0);
f.write_uint(SPA_PARAM_INFO_READ.0);
});
@ -537,7 +520,7 @@ impl PwClientNode {
let mut obj = match obj {
Some(obj) => obj,
_ => {
port.effective_format.take();
port.negotiated_format.take();
return Ok(());
}
};
@ -556,10 +539,25 @@ impl PwClientNode {
format.format = Some(*fmt);
}
}
if let Some(mt) = obj.get_param(SPA_FORMAT_VIDEO_modifier.0)? {
if let PwPod::Choice(mods) = mt.pod {
let mut p1 = mods.elements.elements;
p1.read_pod_body_packed(PW_TYPE_Long, 8)?;
while p1.len() > 0 {
let modifier = p1.read_pod_body_packed(PW_TYPE_Long, 8)?;
if let PwPod::Long(modifier) = modifier {
format
.modifiers
.get_or_insert_default_ext()
.push(modifier as u64);
}
}
}
}
if let Some(mt) = obj.get_param(SPA_FORMAT_VIDEO_framerate.0)? {
format.framerate = Some(mt.pod.get_fraction()?);
}
port.effective_format.set(format);
*port.negotiated_format.borrow_mut() = format;
Ok(())
}
@ -712,8 +710,16 @@ impl PwClientNode {
if mem_id == !0 {
port.io_buffers.take();
} else {
port.io_buffers
.set(Some(self.con.mem.map(mem_id, offset, size)?.typed()));
let io_buffers = self
.con
.mem
.map(mem_id, offset, size)?
.typed::<spa_io_buffers>();
unsafe {
io_buffers.read().buffer_id.store(!0, Relaxed);
io_buffers.read().status.store(0, Relaxed);
}
port.io_buffers.set(Some(io_buffers));
}
}
_ => {}

View file

@ -1,11 +1,15 @@
use {
crate::{
gfx_api::{cross_intersect_formats, GfxFormat},
gfx_apis::create_gfx_context,
ifs::wl_seat::POINTER,
object::Version,
portal::{
ptl_remote_desktop::RemoteDesktopSession, ptl_render_ctx::PortalRenderCtx,
ptl_screencast::ScreencastSession, ptr_gui::WindowData, PortalState,
ptl_remote_desktop::RemoteDesktopSession,
ptl_render_ctx::{PortalRenderCtx, PortalServerRenderCtx},
ptl_screencast::ScreencastSession,
ptr_gui::WindowData,
PortalState,
},
utils::{
bitflags::BitflagsExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
@ -67,7 +71,7 @@ pub struct PortalDisplay {
pub comp: Rc<UsrWlCompositor>,
pub fsm: Rc<UsrWpFractionalScaleManager>,
pub vp: Rc<UsrWpViewporter>,
pub render_ctx: CloneCell<Option<Rc<PortalRenderCtx>>>,
pub render_ctx: CloneCell<Option<Rc<PortalServerRenderCtx>>>,
pub outputs: CopyHashMap<u32, Rc<PortalOutput>>,
pub seats: CopyHashMap<u32, Rc<PortalSeat>>,
@ -156,7 +160,7 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
self.render_ctx.take();
}
fn device(&self, fd: Rc<OwnedFd>) {
fn device(&self, fd: Rc<OwnedFd>, server_formats: Option<AHashMap<u32, GfxFormat>>) {
self.render_ctx.take();
let dev_id = match uapi::fstat(fd.raw()) {
Ok(s) => s.st_rdev,
@ -165,12 +169,13 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
return;
}
};
let mut render_ctx = None;
if let Some(ctx) = self.state.render_ctxs.get(&dev_id) {
if let Some(ctx) = ctx.upgrade() {
self.render_ctx.set(Some(ctx));
render_ctx = Some(ctx);
}
}
if self.render_ctx.is_none() {
if render_ctx.is_none() {
let drm = Drm::open_existing(fd);
let ctx =
match create_gfx_context(&self.state.eng, &self.state.ring, &drm, GfxApi::OpenGl) {
@ -187,8 +192,22 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
_dev_id: dev_id,
ctx,
});
self.render_ctx.set(Some(ctx.clone()));
self.state.render_ctxs.set(dev_id, Rc::downgrade(&ctx));
render_ctx = Some(ctx);
}
if let Some(ctx) = render_ctx {
let client_formats = ctx.ctx.formats();
let usable_formats = match &server_formats {
None => client_formats,
Some(server_formats) => {
Rc::new(cross_intersect_formats(&client_formats, server_formats))
}
};
self.render_ctx.set(Some(Rc::new(PortalServerRenderCtx {
ctx,
usable_formats,
server_formats,
})));
}
}
}
@ -304,7 +323,7 @@ fn finish_display_connect(dpy: Rc<PortalDisplayPrelude>) {
con: dpy.con.clone(),
owner: Default::default(),
caps: Default::default(),
version: Version(version.min(5)),
version: Version(version.min(7)),
});
dpy.con.add_object(jc.clone());
dpy.registry.request_bind(name, jc.version.0, jc.deref());

View file

@ -1,6 +1,17 @@
use {crate::gfx_api::GfxContext, std::rc::Rc, uapi::c};
use {
crate::gfx_api::{GfxContext, GfxFormat},
ahash::AHashMap,
std::rc::Rc,
uapi::c,
};
pub struct PortalRenderCtx {
pub _dev_id: c::dev_t,
pub ctx: Rc<dyn GfxContext>,
}
pub struct PortalServerRenderCtx {
pub ctx: Rc<PortalRenderCtx>,
pub usable_formats: Rc<AHashMap<u32, GfxFormat>>,
pub server_formats: Option<AHashMap<u32, GfxFormat>>,
}

View file

@ -2,16 +2,21 @@ mod screencast_gui;
use {
crate::{
allocator::{AllocatorError, BufferObject, BO_USE_RENDERING},
dbus::{prelude::Variant, DbusObject, DictEntry, DynamicType, PendingReply},
format::{Format, XRGB8888},
ifs::jay_screencast::CLIENT_BUFFERS_SINCE,
pipewire::{
pw_con::PwCon,
pw_ifs::pw_client_node::{
PwClientNode, PwClientNodeBufferConfig, PwClientNodeOwner, PwClientNodePort,
PwClientNodePortSupportedFormats, SUPPORTED_META_VIDEO_CROP,
PwClientNodePortSupportedFormat, PwClientNodePortSupportedFormats,
SUPPORTED_META_VIDEO_CROP,
},
pw_pod::{
spa_point, spa_rectangle, spa_region, PwPodRectangle, SPA_DATA_DmaBuf,
SPA_MEDIA_SUBTYPE_raw, SPA_MEDIA_TYPE_video, SpaChunkFlags, SPA_STATUS_HAVE_DATA,
SPA_VIDEO_FORMAT_UNKNOWN,
},
},
portal::{
@ -22,9 +27,10 @@ use {
utils::{
clonecell::{CloneCell, UnsafeCellCloneSafe},
copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
hash_map_ext::HashMapExt,
},
video::dmabuf::{DmaBuf, PlaneVec},
video::{dmabuf::DmaBuf, Modifier, LINEAR_MODIFIER},
wire::jay_screencast::Ready,
wire_dbus::{
org,
@ -37,11 +43,15 @@ use {
},
},
wl_usr::usr_ifs::{
usr_jay_screencast::{UsrJayScreencast, UsrJayScreencastOwner},
usr_jay_screencast::{
UsrJayScreencast, UsrJayScreencastOwner, UsrJayScreencastServerConfig,
},
usr_jay_select_toplevel::UsrJaySelectToplevel,
usr_jay_select_workspace::UsrJaySelectWorkspace,
usr_jay_toplevel::UsrJayToplevel,
usr_jay_workspace::UsrJayWorkspace,
usr_linux_buffer_params::{UsrLinuxBufferParams, UsrLinuxBufferParamsOwner},
usr_wl_buffer::UsrWlBuffer,
},
},
std::{
@ -51,6 +61,7 @@ use {
rc::Rc,
sync::atomic::Ordering::{Acquire, Relaxed, Release},
},
thiserror::Error,
};
shared_ids!(ScreencastSessionId);
@ -120,10 +131,18 @@ pub struct StartedScreencast {
session: Rc<ScreencastSession>,
node: Rc<PwClientNode>,
port: Rc<PwClientNodePort>,
buffers: RefCell<PlaneVec<DmaBuf>>,
buffer_objects: RefCell<Vec<Rc<dyn BufferObject>>>,
buffers: RefCell<Vec<DmaBuf>>,
pending_buffers: RefCell<Vec<Rc<UsrLinuxBufferParams>>>,
buffers_valid: Cell<bool>,
dpy: Rc<PortalDisplay>,
jay_screencast: Rc<UsrJayScreencast>,
port_buffer_valid: Cell<bool>,
fixated: Cell<bool>,
format: Cell<&'static Format>,
modifier: Cell<Modifier>,
width: Cell<i32>,
height: Cell<i32>,
}
bitflags! {
@ -164,7 +183,36 @@ impl PwClientNodeOwner for StartingScreencast {
results: Cow::Borrowed(variants),
});
}
let port = self.node.create_port(true);
let mut supported_formats = PwClientNodePortSupportedFormats {
media_type: Some(SPA_MEDIA_TYPE_video),
media_sub_type: Some(SPA_MEDIA_SUBTYPE_raw),
video_size: None,
formats: vec![PwClientNodePortSupportedFormat {
format: XRGB8888,
modifiers: vec![LINEAR_MODIFIER],
}],
};
if let Some(ctx) = self.dpy.render_ctx.get() {
if let Some(server_formats) = &ctx.server_formats {
supported_formats.formats.clear();
for format in server_formats.values() {
if format.write_modifiers.is_empty() {
continue;
}
if format.format.pipewire == SPA_VIDEO_FORMAT_UNKNOWN {
continue;
}
let ptl_format = PwClientNodePortSupportedFormat {
format: format.format,
modifiers: format.write_modifiers.iter().copied().collect(),
};
supported_formats.formats.push(ptl_format);
}
}
}
let jsc_version = self.dpy.jc.version;
let num_buffers = (jsc_version >= CLIENT_BUFFERS_SINCE).then_some(3);
let port = self.node.create_port(true, supported_formats, num_buffers);
port.can_alloc_buffers.set(true);
port.supported_metas.set(SUPPORTED_META_VIDEO_CROP);
let jsc = self.dpy.jc.create_screencast();
@ -194,10 +242,18 @@ impl PwClientNodeOwner for StartingScreencast {
session: self.session.clone(),
node: self.node.clone(),
port,
buffer_objects: Default::default(),
buffers: Default::default(),
pending_buffers: Default::default(),
buffers_valid: Cell::new(false),
dpy: self.dpy.clone(),
jay_screencast: jsc,
port_buffer_valid: Cell::new(false),
fixated: Cell::new(jsc_version < CLIENT_BUFFERS_SINCE),
format: Cell::new(XRGB8888),
modifier: Cell::new(LINEAR_MODIFIER),
width: Cell::new(1),
height: Cell::new(1),
});
self.session
.phase
@ -209,13 +265,84 @@ impl PwClientNodeOwner for StartingScreencast {
impl PwClientNodeOwner for StartedScreencast {
fn port_format_changed(&self, port: &Rc<PwClientNodePort>) {
self.node.send_port_update(port, false);
let format = &*port.negotiated_format.borrow();
if self.fixated.get() {
return;
}
let (Some(fmt), Some(modifiers)) = (format.format, &format.modifiers) else {
return;
};
let modifier;
let planes;
match self.allocate_buffer(fmt, modifiers, 1, 1) {
Ok(bo) => {
let dmabuf = bo.dmabuf();
modifier = dmabuf.modifier;
planes = dmabuf.planes.len();
}
Err(e) => {
log::error!("Could not allocate buffer: {}", ErrorFmt(e));
self.session.kill();
return;
}
};
self.port.supported_formats.borrow_mut().formats = vec![PwClientNodePortSupportedFormat {
format: fmt,
modifiers: vec![modifier],
}];
self.port.buffer_config.borrow_mut().planes = Some(planes);
self.node.send_port_update(&self.port, true);
self.format.set(fmt);
self.modifier.set(modifier);
self.fixated.set(true);
}
fn use_buffers(&self, port: &Rc<PwClientNodePort>) {
fn use_buffers(self: Rc<Self>, port: &Rc<PwClientNodePort>) {
if self.jay_screencast.version < CLIENT_BUFFERS_SINCE {
self.node
.send_port_output_buffers(port, &self.buffers.borrow_mut());
self.buffers_valid.set(true);
return;
}
self.buffers_valid.set(false);
self.port_buffer_valid.set(false);
let Some(dmabuf) = self.dpy.dmabuf.get() else {
log::error!("Display does not support dmabuf");
self.session.kill();
return;
};
self.jay_screencast.clear_buffers();
self.jay_screencast.configure();
self.buffer_objects.borrow_mut().clear();
self.buffers.borrow_mut().clear();
for buffer in self.pending_buffers.borrow_mut().drain(..) {
self.dpy.con.remove_obj(&*buffer);
}
for _ in 0..self.port.buffers.borrow().len() {
let res = self.allocate_buffer(
self.format.get(),
&[self.modifier.get()],
self.width.get(),
self.height.get(),
);
match res {
Ok(b) => {
let params = dmabuf.create_params();
params.create(&b.dmabuf());
params.owner.set(Some(self.clone()));
self.buffers.borrow_mut().push(b.dmabuf().clone());
self.buffer_objects.borrow_mut().push(b);
self.pending_buffers.borrow_mut().push(params);
}
Err(e) => {
log::error!("Could not allocate buffer: {}", ErrorFmt(e));
self.session.kill();
return;
}
}
}
self.node
.send_port_output_buffers(port, &self.buffers.borrow_mut());
self.buffers_valid.set(true);
.send_port_output_buffers(&self.port, &self.buffers.borrow());
}
fn start(self: Rc<Self>) {
@ -234,6 +361,37 @@ impl PwClientNodeOwner for StartedScreencast {
}
}
#[derive(Debug, Error)]
enum BufferAllocationError {
#[error("Display has no render context")]
NoRenderContext,
#[error(transparent)]
Allocator(#[from] AllocatorError),
}
impl StartedScreencast {
fn allocate_buffer(
&self,
format: &'static Format,
modifiers: &[Modifier],
width: i32,
height: i32,
) -> Result<Rc<dyn BufferObject>, BufferAllocationError> {
let Some(ctx) = self.dpy.render_ctx.get() else {
return Err(BufferAllocationError::NoRenderContext);
};
let buffer = ctx.ctx.ctx.allocator().create_bo(
&self.dpy.state.dma_buf_ids,
width,
height,
format,
modifiers,
BO_USE_RENDERING,
)?;
Ok(buffer)
}
}
impl SelectingScreencastCore {
pub fn starting(&self, dpy: &Rc<PortalDisplay>, target: ScreencastTarget) {
let node = self.session.pw_con.create_client_node(&[
@ -300,6 +458,9 @@ impl ScreencastSession {
s.jay_screencast.con.remove_obj(s.jay_screencast.deref());
s.node.con.destroy_obj(s.node.deref());
s.dpy.screencasts.remove(self.session_obj.path());
for buffer in s.pending_buffers.borrow_mut().drain(..) {
s.dpy.con.remove_obj(&*buffer);
}
}
}
}
@ -375,32 +536,31 @@ impl ScreencastSession {
}
impl UsrJayScreencastOwner for StartedScreencast {
fn buffers(&self, buffers: PlaneVec<DmaBuf>) {
fn buffers(&self, buffers: Vec<DmaBuf>) {
if buffers.len() == 0 {
return;
}
let buffer = &buffers[0];
*self.port.supported_formats.borrow_mut() = Some(PwClientNodePortSupportedFormats {
*self.port.supported_formats.borrow_mut() = PwClientNodePortSupportedFormats {
media_type: Some(SPA_MEDIA_TYPE_video),
media_sub_type: Some(SPA_MEDIA_SUBTYPE_raw),
video_size: Some(PwPodRectangle {
width: buffer.width as _,
height: buffer.height as _,
}),
formats: vec![buffer.format],
modifiers: vec![buffer.modifier],
});
let bc = PwClientNodeBufferConfig {
num_buffers: buffers.len(),
planes: buffer.planes.len(),
_stride: Some(buffer.planes[0].stride),
_size: Some(buffer.planes[0].stride * buffer.height as u32),
_align: 16,
formats: vec![PwClientNodePortSupportedFormat {
format: buffer.format,
modifiers: vec![buffer.modifier],
}],
};
*self.port.buffer_config.borrow_mut() = PwClientNodeBufferConfig {
num_buffers: Some(buffers.len()),
planes: Some(buffer.planes.len()),
data_type: SPA_DATA_DmaBuf,
};
self.port.buffer_config.set(Some(bc));
self.node.send_port_update(&self.port, true);
self.node.send_active(true);
self.fixated.set(true);
*self.buffers.borrow_mut() = buffers;
self.buffers_valid.set(false);
}
@ -414,7 +574,6 @@ impl UsrJayScreencastOwner for StartedScreencast {
self.jay_screencast.release_buffer(idx);
};
if !self.buffers_valid.get() {
discard_buffer();
return;
}
let Some(io) = self.port.io_buffers.get() else {
@ -447,19 +606,47 @@ impl UsrJayScreencastOwner for StartedScreencast {
};
}
let buffer_id = io.buffer_id.load(Relaxed) as usize;
if buffer_id != idx {
if buffer_id < buffers.len() {
self.jay_screencast.release_buffer(buffer_id);
if self.port_buffer_valid.get() {
if buffer_id != idx {
if buffer_id < buffers.len() {
self.jay_screencast.release_buffer(buffer_id);
}
}
}
io.buffer_id.store(ev.idx, Relaxed);
io.status.store(SPA_STATUS_HAVE_DATA.0, Release);
self.port_buffer_valid.set(true);
self.port.node.drive();
}
fn destroyed(&self) {
self.session.kill();
}
fn config(&self, config: UsrJayScreencastServerConfig) {
self.width.set(config.width.max(1));
self.height.set(config.height.max(1));
self.port.supported_formats.borrow_mut().video_size = Some(PwPodRectangle {
width: self.width.get() as _,
height: self.height.get() as _,
});
self.node.send_port_update(&self.port, self.fixated.get());
self.node.send_active(true);
}
}
impl UsrLinuxBufferParamsOwner for StartedScreencast {
fn created(&self, buffer: Rc<UsrWlBuffer>) {
self.buffers_valid.set(true);
self.jay_screencast.add_buffer(&buffer);
self.jay_screencast.configure();
self.dpy.con.remove_obj(&*buffer);
}
fn failed(&self) {
log::error!("Buffer import failed");
self.session.kill();
}
}
pub(super) fn add_screencast_dbus_members(

View file

@ -599,7 +599,8 @@ impl WindowData {
Some(c) => c,
_ => return,
};
let (mut width, mut height) = content.layout(&ctx.ctx, scale, f32::INFINITY, f32::INFINITY);
let (mut width, mut height) =
content.layout(&ctx.ctx.ctx, scale, f32::INFINITY, f32::INFINITY);
content.data().width.set(width);
content.data().height.set(height);
width = width.max(1.0);
@ -710,7 +711,7 @@ impl WindowData {
self.frame_missed.set(true);
let width = (self.width.get() as f64 * self.scale.get().to_f64()).round() as i32;
let height = (self.height.get() as f64 * self.scale.get().to_f64()).round() as i32;
let formats = ctx.ctx.formats();
let formats = &ctx.usable_formats;
let format = match formats.get(&ARGB8888.drm) {
None => {
log::error!("Render context does not support ARGB8888 format");
@ -724,7 +725,7 @@ impl WindowData {
}
let modifiers: Vec<_> = format.write_modifiers.iter().copied().collect();
for _ in 0..NUM_BUFFERS {
let bo = match ctx.ctx.allocator().create_bo(
let bo = match ctx.ctx.ctx.allocator().create_bo(
&self.dpy.state.dma_buf_ids,
width,
height,
@ -738,7 +739,7 @@ impl WindowData {
return;
}
};
let img = match ctx.ctx.clone().dmabuf_img(bo.dmabuf()) {
let img = match ctx.ctx.ctx.clone().dmabuf_img(bo.dmabuf()) {
Ok(b) => b,
Err(e) => {
log::error!("Could not import dmabuf into EGL: {}", ErrorFmt(e));

View file

@ -153,7 +153,7 @@ pub struct State {
pub pending_float_titles: AsyncQueue<Rc<FloatNode>>,
pub pending_input_popup_positioning: AsyncQueue<Rc<ZwpInputPopupSurfaceV2>>,
pub pending_toplevel_screencasts: AsyncQueue<Rc<JayScreencast>>,
pub pending_toplevel_screencast_reallocs: AsyncQueue<Rc<JayScreencast>>,
pub pending_screencast_reallocs_or_reconfigures: AsyncQueue<Rc<JayScreencast>>,
pub dbus: Dbus,
pub fdcloser: Arc<FdCloser>,
pub logger: Option<Arc<Logger>>,
@ -814,7 +814,7 @@ impl State {
self.pending_float_titles.clear();
self.pending_input_popup_positioning.clear();
self.pending_toplevel_screencasts.clear();
self.pending_toplevel_screencast_reallocs.clear();
self.pending_screencast_reallocs_or_reconfigures.clear();
self.render_ctx_watchers.clear();
self.workspace_watchers.clear();
self.toplevel_lists.clear();

View file

@ -591,7 +591,7 @@ impl OutputNode {
if (old_width, old_height) != (new_width, new_height) {
for sc in self.screencasts.lock().values() {
sc.schedule_realloc();
sc.schedule_realloc_or_reconfigure();
}
}

View file

@ -134,7 +134,7 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
let prev = data.desired_extents.replace(*rect);
if prev.size() != rect.size() {
for sc in data.jay_screencasts.lock().values() {
sc.schedule_realloc();
sc.schedule_realloc_or_reconfigure();
}
}
if data.is_floating.get() {

View file

@ -52,6 +52,7 @@ impl UsrJayCompositor {
con: self.con.clone(),
owner: Default::default(),
version: self.version,
formats: Default::default(),
});
self.con.request(GetRenderCtx {
self_id: self.id,

View file

@ -1,11 +1,15 @@
use {
crate::{
format::formats,
gfx_api::GfxFormat,
ifs::jay_render_ctx::FORMATS_SINCE,
object::Version,
utils::clonecell::CloneCell,
wire::{jay_render_ctx::*, JayRenderCtxId},
wl_usr::{usr_object::UsrObject, UsrCon},
},
std::{convert::Infallible, rc::Rc},
ahash::AHashMap,
std::{cell::RefCell, convert::Infallible, rc::Rc},
uapi::OwnedFd,
};
@ -14,12 +18,14 @@ pub struct UsrJayRenderCtx {
pub con: Rc<UsrCon>,
pub owner: CloneCell<Option<Rc<dyn UsrJayRenderCtxOwner>>>,
pub version: Version,
pub formats: RefCell<AHashMap<u32, GfxFormat>>,
}
pub trait UsrJayRenderCtxOwner {
fn no_device(&self) {}
fn device(&self, fd: Rc<OwnedFd>) {
fn device(&self, fd: Rc<OwnedFd>, server_formats: Option<AHashMap<u32, GfxFormat>>) {
let _ = fd;
let _ = server_formats;
}
}
@ -27,6 +33,7 @@ impl JayRenderCtxEventHandler for UsrJayRenderCtx {
type Error = Infallible;
fn no_device(&self, _ev: NoDevice, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.formats.take();
if let Some(owner) = self.owner.get() {
owner.no_device();
}
@ -34,8 +41,38 @@ impl JayRenderCtxEventHandler for UsrJayRenderCtx {
}
fn device(&self, ev: Device, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let formats = self.formats.take();
let formats = (self.version >= FORMATS_SINCE).then_some(formats);
if let Some(owner) = self.owner.get() {
owner.device(ev.fd);
owner.device(ev.fd, formats);
}
Ok(())
}
fn read_modifier(&self, ev: ReadModifier, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if let Some(format) = self.formats.borrow_mut().get_mut(&ev.format) {
format.read_modifiers.insert(ev.modifier);
}
Ok(())
}
fn write_modifier(&self, ev: WriteModifier, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if let Some(format) = self.formats.borrow_mut().get_mut(&ev.format) {
format.write_modifiers.insert(ev.modifier);
}
Ok(())
}
fn format(&self, ev: Format, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if let Some(format) = formats().get(&ev.format) {
self.formats.borrow_mut().insert(
ev.format,
GfxFormat {
format,
read_modifiers: Default::default(),
write_modifiers: Default::default(),
},
);
}
Ok(())
}

View file

@ -8,7 +8,7 @@ use {
wl_usr::{
usr_ifs::{
usr_jay_output::UsrJayOutput, usr_jay_toplevel::UsrJayToplevel,
usr_jay_workspace::UsrJayWorkspace,
usr_jay_workspace::UsrJayWorkspace, usr_wl_buffer::UsrWlBuffer,
},
usr_object::UsrObject,
UsrCon,
@ -24,7 +24,7 @@ pub struct UsrJayScreencast {
pub owner: CloneCell<Option<Rc<dyn UsrJayScreencastOwner>>>,
pub version: Version,
pub pending_buffers: RefCell<PlaneVec<DmaBuf>>,
pub pending_buffers: RefCell<Vec<DmaBuf>>,
pub pending_planes: RefCell<PlaneVec<DmaBufPlane>>,
pub pending_config: RefCell<UsrJayScreencastServerConfig>,
@ -37,10 +37,12 @@ pub struct UsrJayScreencastServerConfig {
pub running: bool,
pub use_linear_buffers: bool,
pub allowed_workspaces: Vec<u32>,
pub width: i32,
pub height: i32,
}
pub trait UsrJayScreencastOwner {
fn buffers(&self, buffers: PlaneVec<DmaBuf>) {
fn buffers(&self, buffers: Vec<DmaBuf>) {
let _ = buffers;
}
@ -116,6 +118,17 @@ impl UsrJayScreencast {
idx: idx as _,
});
}
pub fn clear_buffers(&self) {
self.con.request(ClearBuffers { self_id: self.id });
}
pub fn add_buffer(&self, buffer: &UsrWlBuffer) {
self.con.request(AddBuffer {
self_id: self.id,
buffer: buffer.id,
});
}
}
impl JayScreencastEventHandler for UsrJayScreencast {
@ -228,6 +241,12 @@ impl JayScreencastEventHandler for UsrJayScreencast {
});
Ok(())
}
fn config_size(&self, ev: ConfigSize, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.pending_config.borrow_mut().width = ev.width;
self.pending_config.borrow_mut().height = ev.height;
Ok(())
}
}
usr_object_base! {

View file

@ -13,3 +13,17 @@ event no_device {
event device {
fd: fd,
}
event read_modifier (since = 7) {
format: u32,
modifier: pod(u64),
}
event write_modifier (since = 7) {
format: u32,
modifier: pod(u64),
}
event format (since = 7) {
format: u32,
}

View file

@ -47,6 +47,13 @@ request set_toplevel {
id: id(jay_toplevel),
}
request clear_buffers (since = 7) {
}
request add_buffer (since = 7) {
buffer: id(wl_buffer),
}
# events
event plane {
@ -101,3 +108,8 @@ event config_running {
event config_done {
serial: u32,
}
event config_size (since = 7) {
width: i32,
height: i32,
}