portal: implement window capture
This commit is contained in:
parent
f0600917ff
commit
4e10415e5c
27 changed files with 840 additions and 136 deletions
|
|
@ -4,10 +4,17 @@ use {
|
|||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
jay_idle::JayIdle, jay_input::JayInput, jay_log_file::JayLogFile,
|
||||
jay_output::JayOutput, jay_pointer::JayPointer, jay_randr::JayRandr,
|
||||
jay_render_ctx::JayRenderCtx, jay_screencast::JayScreencast,
|
||||
jay_screenshot::JayScreenshot, jay_seat_events::JaySeatEvents,
|
||||
jay_idle::JayIdle,
|
||||
jay_input::JayInput,
|
||||
jay_log_file::JayLogFile,
|
||||
jay_output::JayOutput,
|
||||
jay_pointer::JayPointer,
|
||||
jay_randr::JayRandr,
|
||||
jay_render_ctx::JayRenderCtx,
|
||||
jay_screencast::JayScreencast,
|
||||
jay_screenshot::JayScreenshot,
|
||||
jay_seat_events::JaySeatEvents,
|
||||
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
|
||||
jay_workspace_watcher::JayWorkspaceWatcher,
|
||||
},
|
||||
leaks::Tracker,
|
||||
|
|
@ -18,7 +25,7 @@ use {
|
|||
},
|
||||
bstr::ByteSlice,
|
||||
log::Level,
|
||||
std::{ops::Deref, rc::Rc},
|
||||
std::{cell::Cell, ops::Deref, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
|
|
@ -77,13 +84,14 @@ pub struct Cap;
|
|||
|
||||
impl Cap {
|
||||
pub const NONE: u16 = 0;
|
||||
pub const WINDOW_CAPTURE: u16 = 1;
|
||||
}
|
||||
|
||||
impl JayCompositor {
|
||||
fn send_capabilities(&self) {
|
||||
self.client.event(Capabilities {
|
||||
self_id: self.id,
|
||||
cap: &[Cap::NONE],
|
||||
cap: &[Cap::NONE, Cap::WINDOW_CAPTURE],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -336,6 +344,24 @@ impl JayCompositorRequestHandler for JayCompositor {
|
|||
self.client.add_client_obj(&sc)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn select_toplevel(&self, req: SelectToplevel, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let seat = self.client.lookup(req.seat)?;
|
||||
let obj = Rc::new(JaySelectToplevel {
|
||||
id: req.id,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
destroyed: Cell::new(false),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
let selector = JayToplevelSelector {
|
||||
tl: Default::default(),
|
||||
jst: obj.clone(),
|
||||
};
|
||||
seat.global.select_toplevel(selector);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,17 @@ use {
|
|||
client::{Client, ClientError},
|
||||
format::XRGB8888,
|
||||
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
|
||||
ifs::jay_output::JayOutput,
|
||||
ifs::{jay_output::JayOutput, jay_toplevel::JayToplevel},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
tree::{OutputNode, WorkspaceNodeId},
|
||||
scale::Scale,
|
||||
state::State,
|
||||
tree::{OutputNode, ToplevelNode, WorkspaceNodeId},
|
||||
utils::{
|
||||
clonecell::CloneCell, errorfmt::ErrorFmt, numcell::NumCell, option_ext::OptionExt,
|
||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||
errorfmt::ErrorFmt,
|
||||
numcell::NumCell,
|
||||
option_ext::OptionExt,
|
||||
},
|
||||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
|
|
@ -19,6 +24,7 @@ use {
|
|||
},
|
||||
ahash::AHashSet,
|
||||
indexmap::{indexset, IndexSet},
|
||||
jay_config::video::Transform,
|
||||
once_cell::sync::Lazy,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -28,6 +34,28 @@ use {
|
|||
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_toplevel_screencast_reallocs.pop().await;
|
||||
screencast.realloc_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 struct JayScreencast {
|
||||
pub id: JayScreencastId,
|
||||
pub client: Rc<Client>,
|
||||
|
|
@ -38,20 +66,35 @@ pub struct JayScreencast {
|
|||
buffers_acked: Cell<bool>,
|
||||
buffers: RefCell<Vec<ScreencastBuffer>>,
|
||||
missed_frame: Cell<bool>,
|
||||
output: CloneCell<Option<Rc<OutputNode>>>,
|
||||
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: Cell<bool>,
|
||||
realloc_scheduled: Cell<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Target {
|
||||
Output(Rc<OutputNode>),
|
||||
Toplevel(Rc<dyn ToplevelNode>),
|
||||
}
|
||||
|
||||
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>>,
|
||||
output: Cell<Option<Option<Rc<JayOutput>>>>,
|
||||
target: Cell<Option<Option<PendingTarget>>>,
|
||||
show_all: Cell<Option<bool>>,
|
||||
show_workspaces: RefCell<Option<AHashSet<WorkspaceNodeId>>>,
|
||||
}
|
||||
|
|
@ -71,19 +114,80 @@ impl JayScreencast {
|
|||
config_serial: Default::default(),
|
||||
config_acked: Cell::new(true),
|
||||
buffers_serial: Default::default(),
|
||||
buffers_acked: Cell::new(false),
|
||||
buffers_acked: Cell::new(true),
|
||||
buffers: Default::default(),
|
||||
missed_frame: Cell::new(false),
|
||||
output: Default::default(),
|
||||
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: Cell::new(false),
|
||||
realloc_scheduled: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub 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(
|
||||
tl.tl_as_node(),
|
||||
&self.client.state,
|
||||
Some(tl.node_absolute_position()),
|
||||
None,
|
||||
scale,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
Transform::None,
|
||||
);
|
||||
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;
|
||||
|
|
@ -115,11 +219,13 @@ impl JayScreencast {
|
|||
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(),
|
||||
});
|
||||
if let Some(target) = self.target.get() {
|
||||
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,
|
||||
|
|
@ -200,10 +306,21 @@ impl JayScreencast {
|
|||
}
|
||||
|
||||
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();
|
||||
if let Some(target) = self.target.take() {
|
||||
match target {
|
||||
Target::Output(output) => {
|
||||
output.screencasts.remove(&(self.client.id, self.id));
|
||||
if output.screencasts.is_empty() {
|
||||
output.state.damage();
|
||||
}
|
||||
}
|
||||
Target::Toplevel(tl) => {
|
||||
let data = tl.tl_data();
|
||||
data.jay_screencasts.remove(&(self.client.id, self.id));
|
||||
if data.jay_screencasts.is_empty() {
|
||||
self.client.state.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -214,17 +331,39 @@ impl JayScreencast {
|
|||
self.client.event(Destroyed { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn realloc(&self, ctx: &Rc<dyn GfxContext>) -> Result<(), JayScreencastError> {
|
||||
pub fn schedule_realloc(self: &Rc<Self>) {
|
||||
self.need_realloc.set(true);
|
||||
if !self.realloc_scheduled.replace(true) {
|
||||
self.client
|
||||
.state
|
||||
.pending_toplevel_screencast_reallocs
|
||||
.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(())
|
||||
}
|
||||
}
|
||||
|
||||
fn do_realloc(&self, ctx: &Rc<dyn GfxContext>) -> Result<(), JayScreencastError> {
|
||||
self.need_realloc.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(output) = self.output.get() {
|
||||
let (width, height) = output.global.pixel_size();
|
||||
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 = GBM_BO_USE_RENDERING;
|
||||
let modifiers = match self.linear.get() {
|
||||
true if format.write_modifiers.contains(&LINEAR_MODIFIER) => {
|
||||
|
|
@ -267,8 +406,11 @@ impl JayScreencast {
|
|||
}
|
||||
|
||||
fn damage(&self) {
|
||||
if let Some(output) = self.output.get() {
|
||||
output.global.connector.connector.damage();
|
||||
if let Some(target) = self.target.get() {
|
||||
match target {
|
||||
Target::Output(o) => o.global.connector.connector.damage(),
|
||||
Target::Toplevel(_) => self.client.state.damage(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -284,14 +426,14 @@ impl JayScreencastRequestHandler for JayScreencast {
|
|||
|
||||
fn set_output(&self, req: SetOutput, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let output = if req.output.is_some() {
|
||||
Some(self.client.lookup(req.output)?)
|
||||
Some(PendingTarget::Output(self.client.lookup(req.output)?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if self.destroyed.get() || !self.config_acked.get() {
|
||||
return Ok(());
|
||||
}
|
||||
self.pending.output.set(Some(output));
|
||||
self.pending.target.set(Some(output));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -362,19 +504,42 @@ impl JayScreencastRequestHandler for JayScreencast {
|
|||
|
||||
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()) {
|
||||
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.get() else {
|
||||
self.do_destroy();
|
||||
return Ok(());
|
||||
};
|
||||
if o.screencasts.is_empty() {
|
||||
o.state.damage();
|
||||
}
|
||||
o.screencasts.set((self.client.id, self.id), slf.clone());
|
||||
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();
|
||||
if data.jay_screencasts.is_empty() {
|
||||
data.state.damage();
|
||||
}
|
||||
data.jay_screencasts
|
||||
.set((self.client.id, self.id), slf.clone());
|
||||
new_target = Some(Target::Toplevel(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
if target_size(new_target.as_ref()) != target_size(self.target.get().as_ref()) {
|
||||
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), slf.clone());
|
||||
}
|
||||
self.output.set(output);
|
||||
self.target.set(new_target);
|
||||
}
|
||||
if let Some(linear) = self.pending.linear.take() {
|
||||
if self.linear.replace(linear) != linear {
|
||||
|
|
@ -392,29 +557,21 @@ impl JayScreencastRequestHandler for JayScreencast {
|
|||
}
|
||||
|
||||
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(());
|
||||
}
|
||||
slf.schedule_realloc();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ack_buffers(&self, req: AckBuffers, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
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.get() {
|
||||
slf.schedule_realloc();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -443,6 +600,19 @@ impl JayScreencastRequestHandler for JayScreencast {
|
|||
}
|
||||
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() || !self.config_acked.get() {
|
||||
return Ok(());
|
||||
}
|
||||
self.pending.target.set(Some(toplevel));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
@ -477,9 +647,19 @@ pub enum JayScreencastError {
|
|||
}
|
||||
efrom!(JayScreencastError, ClientError);
|
||||
|
||||
fn output_size(output: &Option<Rc<OutputNode>>) -> (i32, i32) {
|
||||
match output {
|
||||
Some(o) => o.global.pixel_size(),
|
||||
_ => (0, 0),
|
||||
fn target_size(target: Option<&Target>) -> (i32, i32) {
|
||||
if let Some(target) = target {
|
||||
match target {
|
||||
Target::Output(o) => return o.global.pixel_size(),
|
||||
Target::Toplevel(t) => {
|
||||
let data = t.tl_data();
|
||||
let (dw, dh) = data.desired_extents.get().size();
|
||||
if let Some(ws) = data.workspace.get() {
|
||||
let scale = ws.output.get().global.persistent.scale.get();
|
||||
return scale.pixel_size(dw, dh);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
(0, 0)
|
||||
}
|
||||
|
|
|
|||
100
src/ifs/jay_select_toplevel.rs
Normal file
100
src/ifs/jay_select_toplevel.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::{jay_toplevel::JayToplevel, wl_seat::ToplevelSelector},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
tree::ToplevelNode,
|
||||
utils::clonecell::CloneCell,
|
||||
wire::{jay_select_toplevel::*, JaySelectToplevelId, JayToplevelId},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct JaySelectToplevel {
|
||||
pub id: JaySelectToplevelId,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub destroyed: Cell<bool>,
|
||||
}
|
||||
|
||||
pub struct JayToplevelSelector {
|
||||
pub tl: CloneCell<Option<Rc<dyn ToplevelNode>>>,
|
||||
pub jst: Rc<JaySelectToplevel>,
|
||||
}
|
||||
|
||||
impl ToplevelSelector for JayToplevelSelector {
|
||||
fn set(&self, toplevel: Rc<dyn ToplevelNode>) {
|
||||
self.tl.set(Some(toplevel));
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for JayToplevelSelector {
|
||||
fn drop(&mut self) {
|
||||
if self.jst.destroyed.get() {
|
||||
return;
|
||||
}
|
||||
let id = match self.tl.take() {
|
||||
None => JayToplevelId::NONE,
|
||||
Some(toplevel) => {
|
||||
let id = match self.jst.client.new_id() {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
self.jst.client.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let jtl = Rc::new(JayToplevel {
|
||||
id,
|
||||
client: self.jst.client.clone(),
|
||||
tracker: Default::default(),
|
||||
toplevel,
|
||||
destroyed: Cell::new(false),
|
||||
});
|
||||
track!(self.jst.client, jtl);
|
||||
self.jst.client.add_server_obj(&jtl);
|
||||
jtl.toplevel
|
||||
.tl_data()
|
||||
.jay_toplevels
|
||||
.set((jtl.client.id, jtl.id), jtl.clone());
|
||||
jtl.id
|
||||
}
|
||||
};
|
||||
self.jst.send_done(id);
|
||||
let _ = self.jst.client.remove_obj(&*self.jst);
|
||||
}
|
||||
}
|
||||
|
||||
impl JaySelectToplevel {
|
||||
fn send_done(&self, id: JayToplevelId) {
|
||||
self.client.event(Done {
|
||||
self_id: self.id,
|
||||
id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl JaySelectToplevelRequestHandler for JaySelectToplevel {
|
||||
type Error = JaySelectToplevelError;
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JaySelectToplevel;
|
||||
version = Version(1);
|
||||
}
|
||||
|
||||
impl Object for JaySelectToplevel {
|
||||
fn break_loops(&self) {
|
||||
self.destroyed.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(JaySelectToplevel);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JaySelectToplevelError {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(JaySelectToplevelError, ClientError);
|
||||
68
src/ifs/jay_toplevel.rs
Normal file
68
src/ifs/jay_toplevel.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
tree::ToplevelNode,
|
||||
wire::{jay_toplevel::*, JayToplevelId},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct JayToplevel {
|
||||
pub id: JayToplevelId,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub toplevel: Rc<dyn ToplevelNode>,
|
||||
pub destroyed: Cell<bool>,
|
||||
}
|
||||
|
||||
impl JayToplevel {
|
||||
fn detach(&self) {
|
||||
self.destroyed.set(true);
|
||||
self.toplevel
|
||||
.tl_data()
|
||||
.jay_toplevels
|
||||
.remove(&(self.client.id, self.id));
|
||||
}
|
||||
|
||||
pub fn destroy(&self) {
|
||||
self.destroyed.set(true);
|
||||
self.send_destroyed();
|
||||
}
|
||||
|
||||
fn send_destroyed(&self) {
|
||||
self.client.event(Destroyed { self_id: self.id });
|
||||
}
|
||||
}
|
||||
|
||||
impl JayToplevelRequestHandler for JayToplevel {
|
||||
type Error = JayToplevelError;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JayToplevel;
|
||||
version = Version(1);
|
||||
}
|
||||
|
||||
impl Object for JayToplevel {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
dedicated_add_obj!(JayToplevel, JayToplevelId, jay_toplevels);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayToplevelError {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(JayToplevelError, ClientError);
|
||||
|
|
@ -38,8 +38,8 @@ use {
|
|||
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
||||
wp_tearing_control_v1::WpTearingControlV1,
|
||||
wp_viewport::WpViewport,
|
||||
x_surface::XSurface,
|
||||
xdg_surface::{PendingXdgSurfaceData, XdgSurfaceError},
|
||||
x_surface::{xwindow::Xwindow, XSurface},
|
||||
xdg_surface::{xdg_toplevel::XdgToplevel, PendingXdgSurfaceData, XdgSurfaceError},
|
||||
zwlr_layer_surface_v1::{PendingLayerSurfaceData, ZwlrLayerSurfaceV1Error},
|
||||
},
|
||||
wp_content_type_v1::ContentType,
|
||||
|
|
@ -51,8 +51,8 @@ use {
|
|||
rect::{Rect, Region},
|
||||
renderer::Renderer,
|
||||
tree::{
|
||||
FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase, OutputNode,
|
||||
ToplevelNode,
|
||||
ContainerNode, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase,
|
||||
OutputNode, PlaceholderNode, ToplevelNode,
|
||||
},
|
||||
utils::{
|
||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||
|
|
@ -126,11 +126,40 @@ impl SurfaceRole {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeVisitorBase for SurfaceSendPreferredScaleVisitor {
|
||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||
node.on_scale_change();
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
|
||||
fn visit_toplevel(&mut self, node: &Rc<XdgToplevel>) {
|
||||
self.schedule_realloc(&**node);
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
|
||||
fn visit_xwindow(&mut self, node: &Rc<Xwindow>) {
|
||||
self.schedule_realloc(&**node);
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
|
||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||
self.schedule_realloc(&**node);
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
|
||||
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||
self.schedule_realloc(&**node);
|
||||
node.node_visit_children(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SurfaceSendPreferredTransformVisitor;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue