1
0
Fork 0
forked from wry/wry

ifs: remove private screencast interface

This commit is contained in:
kossLAN 2026-05-29 18:20:10 -04:00
parent 698110c265
commit ce03990ea4
No known key found for this signature in database
16 changed files with 7 additions and 1315 deletions

View file

@ -19,7 +19,6 @@ use {
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
},
jay_output::JayOutput,
jay_screencast::JayScreencast,
jay_toplevel::JayToplevel,
jay_workspace::JayWorkspace,
wl_buffer::WlBuffer,
@ -49,9 +48,9 @@ use {
wire::{
ExtDataControlSourceV1Id, ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id,
ExtImageCopyCaptureSessionV1Id, ExtWorkspaceGroupHandleV1Id, JayHeadErrorV1Id,
JayOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId,
WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId,
WlSurfaceId, WpDrmLeaseConnectorV1Id, WpImageDescriptionReferenceV1Id,
JayOutputId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId,
WpDrmLeaseConnectorV1Id, WpImageDescriptionReferenceV1Id,
WpImageDescriptionV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, XdgPositionerId,
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
ZwlrOutputHeadV1Id, ZwlrOutputModeV1Id, ZwpPrimarySelectionSourceV1Id,
@ -80,7 +79,6 @@ pub struct Objects {
pub pointers: CopyHashMap<WlPointerId, Rc<WlPointer>>,
pub xdg_wm_bases: CopyHashMap<XdgWmBaseId, Rc<XdgWmBase>>,
pub seats: CopyHashMap<WlSeatId, Rc<WlSeat>>,
pub screencasts: CopyHashMap<JayScreencastId, Rc<JayScreencast>>,
pub timelines: CopyHashMap<WpLinuxDrmSyncobjTimelineV1Id, Rc<WpLinuxDrmSyncobjTimelineV1>>,
pub zwlr_data_sources: CopyHashMap<ZwlrDataControlSourceV1Id, Rc<ZwlrDataControlSourceV1>>,
pub zwlr_output_heads: CopyHashMap<ZwlrOutputHeadV1Id, Rc<ZwlrOutputHeadV1>>,
@ -127,7 +125,6 @@ impl Objects {
pointers: Default::default(),
xdg_wm_bases: Default::default(),
seats: Default::default(),
screencasts: Default::default(),
timelines: Default::default(),
zwlr_data_sources: Default::default(),
zwlr_output_heads: Default::default(),
@ -176,7 +173,6 @@ impl Objects {
self.xdg_wm_bases.clear();
self.seats.clear();
self.pointers.clear();
self.screencasts.clear();
self.timelines.clear();
self.zwlr_data_sources.clear();
self.jay_toplevels.clear();

View file

@ -35,7 +35,6 @@ use {
head_management::{
HeadManagers, HeadState, jay_head_manager_session_v1::handle_jay_head_manager_done,
},
jay_screencast::{perform_screencast_realloc, perform_toplevel_screencasts},
wl_output::{BlendSpace, OutputId, PersistentOutputState, WlOutputGlobal},
wl_seat::{handle_position_hint_requests, handle_warp_mouse_to_focus},
wl_surface::{
@ -247,8 +246,6 @@ fn start_compositor2(
pending_output_render_data: Default::default(),
pending_float_layout: Default::default(),
pending_input_popup_positioning: Default::default(),
pending_toplevel_screencasts: Default::default(),
pending_screencast_reallocs_or_reconfigures: Default::default(),
pending_placeholder_render_textures: Default::default(),
pending_container_tab_render_textures: Default::default(),
dbus: Dbus::new(&engine, &ring, &run_toplevel),
@ -514,16 +511,6 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
Phase::PostLayout,
input_popup_positioning(state.clone()),
),
eng.spawn2(
"toplevel screencast present",
Phase::Present,
perform_toplevel_screencasts(state.clone()),
),
eng.spawn2(
"screencast realloc",
Phase::PostLayout,
perform_screencast_realloc(state.clone()),
),
eng.spawn2(
"visualize damage",
Phase::PostLayout,
@ -759,7 +746,6 @@ fn create_dummy_output(state: &Rc<State>) {
lock_surface: Default::default(),
hardware_cursor: Default::default(),
update_render_data_scheduled: Cell::new(false),
screencasts: Default::default(),
hardware_cursor_needs_render: Cell::new(false),
screencopies: Default::default(),
title_visible: Cell::new(false),

View file

@ -28,7 +28,6 @@ pub mod jay_popup_ext_manager_v1;
pub mod jay_randr;
pub mod jay_reexec;
pub mod jay_render_ctx;
pub mod jay_screencast;
pub mod jay_screenshot;
pub mod jay_seat_events;
pub mod jay_select_toplevel;

View file

@ -15,7 +15,6 @@ use {
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},
@ -95,7 +94,6 @@ pub struct Cap;
impl Cap {
pub const NONE: u16 = 0;
pub const WINDOW_CAPTURE: u16 = 1;
pub const SELECT_WORKSPACE: u16 = 2;
}
@ -103,7 +101,7 @@ impl JayCompositor {
fn send_capabilities(&self) {
self.client.event(Capabilities {
self_id: self.id,
cap: &[Cap::NONE, Cap::WINDOW_CAPTURE, Cap::SELECT_WORKSPACE],
cap: &[Cap::NONE, Cap::SELECT_WORKSPACE],
});
}
@ -332,13 +330,6 @@ impl JayCompositorRequestHandler for JayCompositor {
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);

View file

@ -1,796 +0,0 @@
use {
crate::{
allocator::{AllocatorError, BO_USE_LINEAR, BO_USE_RENDERING, BufferObject},
client::{Client, ClientError},
cmm::cmm_description::ColorDescription,
format::XRGB8888,
gfx_api::{
AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync,
},
ifs::{jay_output::JayOutput, jay_toplevel::JayToplevel, wl_buffer::WlBufferStorage},
leaks::Tracker,
object::{Object, Version},
scale::Scale,
state::State,
tree::{
LatchListener, OutputNode, ToplevelNode, Transform, WorkspaceNode, WorkspaceNodeId,
},
utils::{
clonecell::{CloneCell, UnsafeCellCloneSafe},
errorfmt::ErrorFmt,
event_listener::EventListener,
numcell::NumCell,
option_ext::OptionExt,
},
video::{INVALID_MODIFIER, LINEAR_MODIFIER, dmabuf::DmaBuf},
wire::{JayScreencastId, jay_screencast::*},
},
ahash::AHashSet,
std::{
cell::{Cell, RefCell},
ops::DerefMut,
rc::{Rc, Weak},
},
thiserror::Error,
};
pub async fn perform_toplevel_screencasts(state: Rc<State>) {
loop {
let screencast = state.pending_toplevel_screencasts.pop().await;
screencast.perform_toplevel_screencast();
}
}
pub async fn perform_screencast_realloc(state: Rc<State>) {
loop {
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) => {
if let Err(e) = screencast.realloc(&ctx) {
screencast.client.error(e);
}
}
}
}
}
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>,
config_acked: Cell<bool>,
buffers_serial: NumCell<u32>,
buffers_acked: Cell<bool>,
buffers: RefCell<Vec<ScreencastBuffer>>,
missed_frame: Cell<bool>,
target: CloneCell<Option<Target>>,
destroyed: Cell<bool>,
running: Cell<bool>,
show_all: Cell<bool>,
show_workspaces: RefCell<AHashSet<WorkspaceNodeId>>,
linear: Cell<bool>,
pending: Pending,
need_realloc_or_reconfigure: Cell<bool>,
realloc_or_reconfigure_scheduled: Cell<bool>,
latch_listener: EventListener<dyn LatchListener>,
}
#[derive(Clone)]
enum Target {
Output(Rc<OutputNode>),
Toplevel(Rc<dyn ToplevelNode>),
}
impl LatchListener for JayScreencast {
fn after_latch(self: Rc<Self>, _on: &OutputNode, _tearing: bool) {
self.schedule_toplevel_screencast();
}
}
unsafe impl UnsafeCellCloneSafe for Target {}
enum PendingTarget {
Output(Rc<JayOutput>),
Toplevel(Rc<JayToplevel>),
}
#[derive(Default)]
struct Pending {
linear: Cell<Option<bool>>,
running: Cell<Option<bool>>,
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: Option<Rc<dyn BufferObject>>,
dmabuf: Option<DmaBuf>,
fb: Rc<dyn GfxFramebuffer>,
free: bool,
}
impl JayScreencast {
pub fn shows_ws(&self, ws: &WorkspaceNode) -> bool {
if self.show_all.get() {
return true;
}
for &id in &*self.show_workspaces.borrow() {
if id == ws.id {
return true;
}
}
false
}
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(),
config_acked: Cell::new(true),
buffers_serial: Default::default(),
buffers_acked: Cell::new(true),
buffers: Default::default(),
missed_frame: Cell::new(false),
target: 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(),
need_realloc_or_reconfigure: Cell::new(false),
realloc_or_reconfigure_scheduled: Cell::new(false),
latch_listener: EventListener::new(slf.clone()),
}
}
fn schedule_toplevel_screencast(self: &Rc<Self>) {
if !self.running.get() {
return;
}
self.client
.state
.pending_toplevel_screencasts
.push(self.clone());
}
fn perform_toplevel_screencast(&self) {
if self.destroyed.get() || !self.running.get() {
return;
}
let Some(target) = self.target.get() else {
return;
};
let Target::Toplevel(tl) = target else {
log::warn!("Tried to perform window screencast for output screencast");
return;
};
let scale = match tl.tl_data().workspace.get() {
None => Scale::default(),
Some(w) => w.output.get().global.persistent.scale.get(),
};
let mut buffer = self.buffers.borrow_mut();
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
if buffer.free {
let res = buffer.fb.render_node(
AcquireSync::Implicit,
ReleaseSync::Implicit,
self.client.state.color_manager.srgb_gamma22(),
&*tl,
&self.client.state,
Some(tl.node_absolute_position()),
scale,
true,
true,
false,
false,
Transform::None,
None,
self.client.state.color_manager.srgb_linear(),
);
match res {
Ok(_) => {
self.client.event(Ready {
self_id: self.id,
idx: idx as _,
});
buffer.free = false;
return;
}
Err(e) => {
log::error!("Could not perform window copy: {}", ErrorFmt(e));
break;
}
}
}
}
self.missed_frame.set(true);
self.client.event(MissedFrame { self_id: self.id })
}
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() {
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(),
offset: plane.offset,
stride: plane.stride,
});
}
self.client.event(Buffer {
self_id: self.id,
format: dmabuf.format.drm,
modifier: dmabuf.modifier,
width: dmabuf.width,
height: dmabuf.height,
});
}
self.client.event(BuffersDone {
self_id: self.id,
serial,
});
}
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,
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>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
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 {
let res = self.client.state.perform_screencopy(
texture,
resv,
acquire_sync,
release_sync,
cd,
&buffer.fb,
AcquireSync::Implicit,
ReleaseSync::Implicit,
Transform::None,
self.client.state.color_manager.srgb_gamma22(),
on.global.pos.get(),
render_hardware_cursors,
x_off,
y_off,
size,
on.global.persistent.transform.get(),
on.global.persistent.scale.get(),
);
match res {
Ok(_) => {
self.client.event(Ready {
self_id: self.id,
idx: idx as _,
});
buffer.free = false;
return;
}
Err(e) => {
log::error!("Could not perform screencopy: {}", ErrorFmt(e));
break;
}
}
}
}
self.missed_frame.set(true);
self.client.event(MissedFrame { self_id: self.id })
}
fn detach(&self) {
self.latch_listener.detach();
if let Some(target) = self.target.take() {
match target {
Target::Output(output) => {
output.remove_screencast(self);
}
Target::Toplevel(tl) => {
let data = tl.tl_data();
data.jay_screencasts.remove(&(self.client.id, self.id));
}
}
}
}
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_or_reconfigure(self: &Rc<Self>) {
self.need_realloc_or_reconfigure.set(true);
if !self.realloc_or_reconfigure_scheduled.replace(true) {
self.client
.state
.pending_screencast_reallocs_or_reconfigures
.push(self.clone());
}
}
fn realloc(&self, ctx: &Rc<dyn GfxContext>) -> Result<(), JayScreencastError> {
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_or_reconfigure.set(false);
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(target) = self.target.get() {
let (width, height) = target_size(Some(&target));
let num = 3;
for _ in 0..num {
if width == 0 || height == 0 {
continue;
}
let mut usage = BO_USE_RENDERING;
let modifiers = match self.linear.get() {
true if format.write_modifiers.contains_key(&LINEAR_MODIFIER) => {
vec![LINEAR_MODIFIER]
}
true if format.write_modifiers.contains_key(&INVALID_MODIFIER) => {
usage |= BO_USE_LINEAR;
vec![INVALID_MODIFIER]
}
true => return Err(JayScreencastError::Modifier),
false if format.write_modifiers.is_empty() => {
return Err(JayScreencastError::XRGB8888Writing);
}
false => format.write_modifiers.keys().copied().collect(),
};
let buffer = ctx.allocator().create_bo(
&self.client.state.dma_buf_ids,
width,
height,
format.format,
&modifiers,
usage,
)?;
let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
buffers.push(ScreencastBuffer {
dmabuf: Some(buffer.dmabuf().clone()),
_bo: Some(buffer),
fb,
free: true,
});
}
}
*self.buffers.borrow_mut() = buffers;
self.send_buffers();
self.damage();
Ok(())
}
fn damage(&self) {
if let Some(target) = self.target.get() {
let rect = match target {
Target::Output(o) => o.global.pos.get(),
Target::Toplevel(t) => {
if !t.node_visible() {
return;
}
t.node_absolute_position()
}
};
self.client.state.damage(rect);
}
}
pub fn update_latch_listener(&self) {
let Some(Target::Toplevel(tl)) = self.target.get() else {
return;
};
let data = tl.tl_data();
if data.visible.get() {
self.latch_listener.attach(&data.output().latch_event);
} else {
self.latch_listener.detach();
}
}
}
impl JayScreencastRequestHandler for JayScreencast {
type Error = JayScreencastError;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.detach();
self.client.remove_obj(self)?;
Ok(())
}
fn set_output(&self, req: SetOutput, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let output = if req.output.is_some() {
Some(PendingTarget::Output(self.client.lookup(req.output)?))
} else {
None
};
if self.destroyed.get() {
return Ok(());
}
self.pending.target.set(Some(output));
Ok(())
}
fn set_allow_all_workspaces(
&self,
req: SetAllowAllWorkspaces,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
self.pending.show_all.set(Some(req.allow_all != 0));
Ok(())
}
fn allow_workspace(&self, req: AllowWorkspace, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let ws = self.client.lookup(req.workspace)?;
if self.destroyed.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,
_req: TouchAllowedWorkspaces,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
self.pending
.show_workspaces
.borrow_mut()
.get_or_insert_default_ext();
Ok(())
}
fn set_use_linear_buffers(
&self,
req: SetUseLinearBuffers,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
self.pending.linear.set(Some(req.use_linear != 0));
Ok(())
}
fn set_running(&self, req: SetRunning, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
self.pending.running.set(Some(req.running != 0));
Ok(())
}
fn configure(&self, _req: Configure, slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
let mut need_realloc_or_reconfigure = false;
if let Some(target) = self.pending.target.take() {
self.detach();
let mut new_target = None;
if let Some(new) = target {
match new {
PendingTarget::Output(o) => {
let Some(o) = o.output.node() else {
self.do_destroy();
return Ok(());
};
o.add_screencast(slf);
new_target = Some(Target::Output(o));
}
PendingTarget::Toplevel(t) => {
if t.destroyed.get() {
self.do_destroy();
return Ok(());
}
let t = t.toplevel.clone();
let data = t.tl_data();
data.jay_screencasts
.set((self.client.id, self.id), slf.clone());
if data.visible.get() {
self.latch_listener.attach(&data.output().latch_event);
}
new_target = Some(Target::Toplevel(t));
}
}
}
if target_size(new_target.as_ref()) != target_size(self.target.get().as_ref()) {
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_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);
capture_rules_changed = true;
}
if let Some(new_workspaces) = self.pending.show_workspaces.borrow_mut().take() {
*self.show_workspaces.borrow_mut() = new_workspaces;
capture_rules_changed = true;
}
if let Some(running) = self.pending.running.take() {
self.running.set(running);
}
if need_realloc_or_reconfigure {
slf.schedule_realloc_or_reconfigure();
}
if capture_rules_changed && let Some(Target::Output(o)) = self.target.get() {
o.screencast_changed();
}
if self.running.get() {
self.damage();
}
Ok(())
}
fn ack_buffers(&self, req: AckBuffers, slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.destroyed.get() {
return Ok(());
}
if req.serial == self.buffers_serial.get() {
self.buffers_acked.set(true);
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> {
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(())
}
fn release_buffer(&self, req: ReleaseBuffer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
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(())
}
fn set_toplevel(&self, req: SetToplevel, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let toplevel = if req.id.is_some() {
Some(PendingTarget::Toplevel(self.client.lookup(req.id)?))
} else {
None
};
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 = self.version;
}
impl Object for JayScreencast {
fn break_loops(&self) {
self.detach();
}
}
dedicated_add_obj!(JayScreencast, JayScreencastId, screencasts);
#[derive(Debug, Error)]
pub enum JayScreencastError {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("Buffer index {0} is out-of-bounds")]
OutOfBounds(u32),
#[error(transparent)]
AllocatorError(#[from] AllocatorError),
#[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,
#[error("Buffer is not a dmabuf")]
NotDmabuf,
}
efrom!(JayScreencastError, ClientError);
fn target_size(target: Option<&Target>) -> (i32, i32) {
if let Some(target) = target {
return match target {
Target::Output(o) => o.global.pixel_size(),
Target::Toplevel(t) => t.tl_data().desired_pixel_size(),
};
}
(0, 0)
}

View file

@ -164,9 +164,6 @@ pub struct SurfaceSendPreferredScaleVisitor;
impl SurfaceSendPreferredScaleVisitor {
fn schedule_realloc(&self, tl: &impl ToplevelNode) {
let data = tl.tl_data();
for sc in data.jay_screencasts.lock().values() {
sc.schedule_realloc_or_reconfigure();
}
for sc in data.ext_copy_sessions.lock().values() {
sc.buffer_size_changed();
}

View file

@ -68,7 +68,6 @@ use {
x_data_device::XTransferDeviceIds,
},
jay_render_ctx::JayRenderCtx,
jay_screencast::JayScreencast,
jay_seat_events::JaySeatEvents,
jay_workspace_watcher::JayWorkspaceWatcher,
wl_buffer::WlBuffer,
@ -294,8 +293,6 @@ pub struct State {
pub pending_output_render_data: AsyncQueue<Rc<OutputNode>>,
pub pending_float_layout: AsyncQueue<Rc<FloatNode>>,
pub pending_input_popup_positioning: AsyncQueue<Rc<ZwpInputPopupSurfaceV2>>,
pub pending_toplevel_screencasts: AsyncQueue<Rc<JayScreencast>>,
pub pending_screencast_reallocs_or_reconfigures: AsyncQueue<Rc<JayScreencast>>,
pub pending_placeholder_render_textures: AsyncQueue<Rc<PlaceholderNode>>,
pub pending_container_tab_render_textures: AsyncQueue<Rc<ContainerNode>>,
pub dbus: Dbus,
@ -854,18 +851,11 @@ impl State {
watcher.send_render_ctx(ctx.clone());
}
let mut scs = vec![];
for client in self.clients.clients.borrow_mut().values() {
for sc in client.data.objects.screencasts.lock().values() {
scs.push(sc.clone());
}
for sc in client.data.objects.ext_copy_sessions.lock().values() {
sc.stop();
}
}
for sc in scs {
sc.do_destroy();
}
self.expose_new_singletons();
}
@ -1234,8 +1224,6 @@ impl State {
self.pending_output_render_data.clear();
self.pending_float_layout.clear();
self.pending_input_popup_positioning.clear();
self.pending_toplevel_screencasts.clear();
self.pending_screencast_reallocs_or_reconfigures.clear();
self.pending_placeholder_render_textures.clear();
self.pending_container_tab_render_textures.clear();
self.animations.clear();

View file

@ -218,7 +218,6 @@ impl ConnectorHandler {
lock_surface: Default::default(),
hardware_cursor: Default::default(),
jay_outputs: Default::default(),
screencasts: Default::default(),
update_render_data_scheduled: Cell::new(false),
hardware_cursor_needs_render: Cell::new(false),
screencopies: Default::default(),
@ -341,10 +340,6 @@ impl ConnectorHandler {
for jo in on.jay_outputs.lock().drain_values() {
jo.send_destroyed();
}
let screencasts: Vec<_> = on.screencasts.lock().values().cloned().collect();
for sc in screencasts {
sc.do_destroy();
}
for sc in on.screencopies.lock().drain_values() {
sc.send_failed();
}

View file

@ -12,7 +12,6 @@ use {
ifs::{
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
jay_output::JayOutput,
jay_screencast::JayScreencast,
wl_buffer::WlBufferStorage,
wl_output::{BlendSpace, WlOutputGlobal},
wl_seat::{
@ -63,7 +62,7 @@ use {
scroller::Scroller,
},
wire::{
ExtImageCopyCaptureSessionV1Id, JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id,
ExtImageCopyCaptureSessionV1Id, JayOutputId, ZwlrScreencopyFrameV1Id,
},
},
ahash::AHashMap,
@ -109,7 +108,6 @@ pub struct OutputNode {
pub hardware_cursor: CloneCell<Option<Rc<dyn HardwareCursor>>>,
pub hardware_cursor_needs_render: Cell<bool>,
pub update_render_data_scheduled: Cell<bool>,
pub screencasts: CopyHashMap<(ClientId, JayScreencastId), Rc<JayScreencast>>,
pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc<ZwlrScreencopyFrameV1>>,
pub title_visible: Cell<bool>,
pub schedule: Rc<OutputSchedule>,
@ -271,16 +269,6 @@ impl OutputNode {
}
}
pub fn add_screencast(&self, sc: &Rc<JayScreencast>) {
self.screencasts.set((sc.client.id, sc.id), sc.clone());
self.screencast_changed();
}
pub fn remove_screencast(&self, sc: &JayScreencast) {
self.screencasts.remove(&(sc.client.id, sc.id));
self.screencast_changed();
}
pub fn screencast_changed(&self) {
for ws in self.workspaces.iter() {
ws.update_has_captures();
@ -315,20 +303,6 @@ impl OutputNode {
y_off,
size,
);
for sc in self.screencasts.lock().values() {
sc.copy_texture(
self,
tex,
cd,
resv,
acquire_sync,
release_sync,
render_hardware_cursor,
x_off,
y_off,
size,
);
}
for sc in self.ext_copy_sessions.lock().values() {
sc.copy_texture(
self,
@ -460,7 +434,6 @@ impl OutputNode {
}
self.lock_surface.take();
self.jay_outputs.clear();
self.screencasts.clear();
self.screencopies.clear();
self.ext_copy_sessions.clear();
self.ext_workspace_groups.clear();
@ -885,9 +858,6 @@ impl OutputNode {
self.change_extents_(&self.calculate_extents());
if (old_width, old_height) != (new_width, new_height) {
for sc in self.screencasts.lock().values() {
sc.schedule_realloc_or_reconfigure();
}
for sc in self.ext_copy_sessions.lock().values() {
sc.buffer_size_changed();
}

View file

@ -20,7 +20,6 @@ use {
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
jay_screencast::JayScreencast,
jay_toplevel::JayToplevel,
wl_seat::{NodeSeatState, SeatId, collect_kb_foci, collect_kb_foci2},
wl_surface::{
@ -44,8 +43,8 @@ use {
rc_eq::rc_eq, threshold_counter::ThresholdCounter,
},
wire::{
ExtForeignToplevelHandleV1Id, ExtImageCopyCaptureSessionV1Id, JayScreencastId,
JayToplevelId, XxForeignToplevelGeometryTrackerV1Id, ZwlrForeignToplevelHandleV1Id,
ExtForeignToplevelHandleV1Id, ExtImageCopyCaptureSessionV1Id, JayToplevelId,
XxForeignToplevelGeometryTrackerV1Id, ZwlrForeignToplevelHandleV1Id,
},
},
jay_config::{window, window::WindowType},
@ -174,9 +173,6 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
fn tl_workspace_output_changed(&self, prev: &Rc<OutputNode>, new: &Rc<OutputNode>) {
let data = self.tl_data();
for sc in data.jay_screencasts.lock().values() {
sc.update_latch_listener();
}
for sc in data.ext_copy_sessions.lock().values() {
sc.update_latch_listener();
}
@ -244,9 +240,6 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
data.spawn_in_pending.set(false);
}
if prev.size() != rect.size() {
for sc in data.jay_screencasts.lock().values() {
sc.schedule_realloc_or_reconfigure();
}
for sc in data.ext_copy_sessions.lock().values() {
sc.buffer_size_changed();
}
@ -543,7 +536,6 @@ pub struct ToplevelData {
CopyHashMap<(ClientId, ZwlrForeignToplevelHandleV1Id), Rc<ZwlrForeignToplevelHandleV1>>,
pub render_highlight: NumCell<u32>,
pub jay_toplevels: CopyHashMap<(ClientId, JayToplevelId), Rc<JayToplevel>>,
pub jay_screencasts: CopyHashMap<(ClientId, JayScreencastId), Rc<JayScreencast>>,
pub ext_copy_sessions:
CopyHashMap<(ClientId, ExtImageCopyCaptureSessionV1Id), Rc<ExtImageCopyCaptureSessionV1>>,
pub slf: Weak<dyn ToplevelNode>,
@ -603,7 +595,6 @@ impl ToplevelData {
manager_handles: Default::default(),
render_highlight: Default::default(),
jay_toplevels: Default::default(),
jay_screencasts: Default::default(),
ext_copy_sessions: Default::default(),
slf: slf.clone(),
destroyed: Default::default(),
@ -680,9 +671,6 @@ impl ToplevelData {
for jay_tl in self.jay_toplevels.lock().drain_values() {
jay_tl.destroy();
}
for screencast in self.jay_screencasts.lock().drain_values() {
screencast.do_destroy();
}
for screencast in self.ext_copy_sessions.lock().drain_values() {
screencast.stop();
}
@ -1003,9 +991,6 @@ impl ToplevelData {
self.property_changed(TL_CHANGED_VISIBLE);
}
self.seat_state.set_visible(node, visible);
for sc in self.jay_screencasts.lock().values() {
sc.update_latch_listener();
}
for sc in self.ext_copy_sessions.lock().values() {
sc.update_latch_listener();
}

View file

@ -119,12 +119,6 @@ impl WorkspaceNode {
if !self.may_capture.get() {
break 'update;
}
for sc in output.screencasts.lock().values() {
if sc.shows_ws(self) {
has_capture = true;
break 'update;
}
}
if output.screencopies.is_not_empty() {
has_capture = true;
}

View file

@ -4,7 +4,6 @@ pub mod usr_jay_ei_session_builder;
pub mod usr_jay_output;
pub mod usr_jay_pointer;
pub mod usr_jay_render_ctx;
pub mod usr_jay_screencast;
pub mod usr_jay_select_toplevel;
pub mod usr_jay_select_workspace;
pub mod usr_jay_sync_file_release;

View file

@ -9,7 +9,6 @@ use {
usr_ifs::{
usr_jay_ei_session_builder::UsrJayEiSessionBuilder, usr_jay_output::UsrJayOutput,
usr_jay_pointer::UsrJayPointer, usr_jay_render_ctx::UsrJayRenderCtx,
usr_jay_screencast::UsrJayScreencast,
usr_jay_select_toplevel::UsrJaySelectToplevel,
usr_jay_select_workspace::UsrJaySelectWorkspace,
usr_jay_workspace_watcher::UsrJayWorkspaceWatcher, usr_wl_output::UsrWlOutput,
@ -31,7 +30,6 @@ pub struct UsrJayCompositor {
#[derive(Default)]
pub struct UsrJayCompositorCaps {
pub window_capture: Cell<bool>,
pub select_workspace: Cell<bool>,
}
@ -62,24 +60,6 @@ impl UsrJayCompositor {
rc
}
pub fn create_screencast(&self) -> Rc<UsrJayScreencast> {
let sc = Rc::new(UsrJayScreencast {
id: self.con.id(),
con: self.con.clone(),
owner: Default::default(),
version: self.version,
pending_buffers: Default::default(),
pending_planes: Default::default(),
pending_config: Default::default(),
});
self.con.request(CreateScreencast {
self_id: self.id,
id: sc.id,
});
self.con.add_object(sc.clone());
sc
}
pub fn get_output(&self, output: &UsrWlOutput) -> Rc<UsrJayOutput> {
let jo = Rc::new(UsrJayOutput {
id: self.con.id(),
@ -210,7 +190,6 @@ impl JayCompositorEventHandler for UsrJayCompositor {
for &cap in ev.cap {
match cap {
Cap::NONE => {}
Cap::WINDOW_CAPTURE => self.caps.window_capture.set(true),
Cap::SELECT_WORKSPACE => self.caps.select_workspace.set(true),
_ => {}
}

View file

@ -1,272 +0,0 @@
use {
crate::{
format::formats,
object::Version,
utils::clonecell::CloneCell,
video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
wire::{JayScreencastId, jay_screencast::*},
wl_usr::{
UsrCon,
usr_ifs::{
usr_jay_output::UsrJayOutput, usr_jay_toplevel::UsrJayToplevel,
usr_jay_workspace::UsrJayWorkspace, usr_wl_buffer::UsrWlBuffer,
},
usr_object::UsrObject,
},
},
std::{cell::RefCell, mem, ops::DerefMut, rc::Rc},
thiserror::Error,
};
pub struct UsrJayScreencast {
pub id: JayScreencastId,
pub con: Rc<UsrCon>,
pub owner: CloneCell<Option<Rc<dyn UsrJayScreencastOwner>>>,
pub version: Version,
pub pending_buffers: RefCell<Vec<DmaBuf>>,
pub pending_planes: RefCell<PlaneVec<DmaBufPlane>>,
pub pending_config: RefCell<UsrJayScreencastServerConfig>,
}
#[derive(Default)]
pub struct UsrJayScreencastServerConfig {
pub output: Option<u32>,
pub show_all: bool,
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: Vec<DmaBuf>) {
let _ = buffers;
}
fn ready(&self, ev: &Ready) {
let _ = ev;
}
fn destroyed(&self) {}
fn missed_frame(&self) {}
fn config(&self, config: UsrJayScreencastServerConfig) {
let _ = config;
}
}
impl UsrJayScreencast {
pub fn set_output(&self, output: &UsrJayOutput) {
self.con.request(SetOutput {
self_id: self.id,
output: output.id,
});
}
pub fn set_toplevel(&self, tl: &UsrJayToplevel) {
self.con.request(SetToplevel {
self_id: self.id,
id: tl.id,
});
}
pub fn set_allow_all_workspaces(&self, allow_all: bool) {
self.con.request(SetAllowAllWorkspaces {
self_id: self.id,
allow_all: allow_all as _,
});
}
pub fn allow_workspace(&self, ws: &UsrJayWorkspace) {
self.con.request(AllowWorkspace {
self_id: self.id,
workspace: ws.id,
});
}
#[expect(dead_code)]
pub fn touch_allowed_workspaces(&self) {
self.con
.request(TouchAllowedWorkspaces { self_id: self.id });
}
pub fn set_use_linear_buffers(&self, linear: bool) {
self.con.request(SetUseLinearBuffers {
self_id: self.id,
use_linear: linear as _,
});
}
pub fn set_running(&self, running: bool) {
self.con.request(SetRunning {
self_id: self.id,
running: running as _,
});
}
pub fn configure(&self) {
self.con.request(Configure { self_id: self.id });
}
pub fn release_buffer(&self, idx: usize) {
self.con.request(ReleaseBuffer {
self_id: self.id,
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 {
type Error = UsrJayScreencastError;
fn plane(&self, ev: Plane, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.pending_planes.borrow_mut().push(DmaBufPlane {
offset: ev.offset,
stride: ev.stride,
fd: ev.fd,
});
Ok(())
}
fn buffer(&self, ev: Buffer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let format = match formats().get(&ev.format) {
Some(f) => f,
_ => return Err(UsrJayScreencastError::UnknownFormat(ev.format)),
};
self.pending_buffers.borrow_mut().push(DmaBuf {
id: self.con.dma_buf_ids.next(),
width: ev.width,
height: ev.height,
format,
modifier: ev.modifier,
planes: mem::take(self.pending_planes.borrow_mut().deref_mut()),
is_disjoint: Default::default(),
});
Ok(())
}
fn buffers_done(&self, ev: BuffersDone, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if let Some(owner) = self.owner.get() {
owner.buffers(mem::take(self.pending_buffers.borrow_mut().deref_mut()));
}
self.con.request(AckBuffers {
self_id: self.id,
serial: ev.serial,
});
Ok(())
}
fn ready(&self, ev: Ready, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if let Some(owner) = self.owner.get() {
owner.ready(&ev);
}
Ok(())
}
fn destroyed(&self, _ev: Destroyed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if let Some(owner) = self.owner.get() {
owner.destroyed();
}
Ok(())
}
fn missed_frame(&self, _ev: MissedFrame, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if let Some(owner) = self.owner.get() {
owner.missed_frame();
}
Ok(())
}
fn config_output(&self, ev: ConfigOutput, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.pending_config.borrow_mut().output = Some(ev.linear_id);
Ok(())
}
fn config_allow_all_workspaces(
&self,
ev: ConfigAllowAllWorkspaces,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.pending_config.borrow_mut().show_all = ev.allow_all != 0;
Ok(())
}
fn config_allow_workspace(
&self,
ev: ConfigAllowWorkspace,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.pending_config
.borrow_mut()
.allowed_workspaces
.push(ev.linear_id);
Ok(())
}
fn config_use_linear_buffers(
&self,
ev: ConfigUseLinearBuffers,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.pending_config.borrow_mut().use_linear_buffers = ev.use_linear != 0;
Ok(())
}
fn config_running(&self, ev: ConfigRunning, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.pending_config.borrow_mut().running = ev.running != 0;
Ok(())
}
fn config_done(&self, ev: ConfigDone, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if let Some(owner) = self.owner.get() {
owner.config(mem::take(self.pending_config.borrow_mut().deref_mut()));
}
self.con.request(AckConfig {
self_id: self.id,
serial: ev.serial,
});
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! {
self = UsrJayScreencast = JayScreencast;
version = self.version;
}
impl UsrObject for UsrJayScreencast {
fn destroy(&self) {
self.con.request(Destroy { self_id: self.id });
}
fn break_loops(&self) {
self.owner.take();
}
}
#[derive(Debug, Error)]
pub enum UsrJayScreencastError {
#[error("The server sent an unknown format {0}")]
UnknownFormat(u32),
}

View file

@ -61,10 +61,6 @@ request watch_workspaces {
id: id(jay_workspace_watcher),
}
request create_screencast {
id: id(jay_screencast),
}
request get_randr {
id: id(jay_randr),
}

View file

@ -1,115 +0,0 @@
# requests
request destroy {
}
request set_output {
output: id(jay_output),
}
request set_allow_all_workspaces {
allow_all: u32,
}
request allow_workspace {
workspace: id(jay_workspace),
}
request touch_allowed_workspaces {
}
request set_use_linear_buffers {
use_linear: u32,
}
request set_running {
running: u32,
}
request configure {
}
request ack_buffers {
serial: u32,
}
request ack_config {
serial: u32,
}
request release_buffer {
idx: u32,
}
request set_toplevel {
id: id(jay_toplevel),
}
request clear_buffers (since = 7) {
}
request add_buffer (since = 7) {
buffer: id(wl_buffer),
}
# events
event plane {
fd: fd,
offset: u32,
stride: u32,
}
event buffer {
format: u32,
modifier: pod(u64),
width: i32,
height: i32,
}
event buffers_done {
serial: u32,
}
event ready {
idx: u32,
}
event destroyed {
}
event missed_frame {
}
event config_output {
linear_id: u32,
}
event config_allow_all_workspaces {
allow_all: u32,
}
event config_allow_workspace {
linear_id: u32,
}
event config_use_linear_buffers {
use_linear: u32,
}
event config_running {
running: u32,
}
event config_done {
serial: u32,
}
event config_size (since = 7) {
width: i32,
height: i32,
}