1
0
Fork 0
forked from wry/wry

portal: unify remote desktop and screencast sessions

This commit is contained in:
Julian Orth 2024-10-10 21:14:05 +02:00
parent 3e3532574b
commit 260d241f79
8 changed files with 304 additions and 279 deletions

View file

@ -8,6 +8,7 @@
- Implement ext-image-capture-source-v1. - Implement ext-image-capture-source-v1.
- Implement ext-image-copy-capture-v1. - Implement ext-image-copy-capture-v1.
- Implement screencast session restoration. - Implement screencast session restoration.
- Fix screen sharing in zoom.
# 1.6.0 (2024-09-25) # 1.6.0 (2024-09-25)

View file

@ -2,6 +2,7 @@ mod ptl_display;
mod ptl_remote_desktop; mod ptl_remote_desktop;
mod ptl_render_ctx; mod ptl_render_ctx;
mod ptl_screencast; mod ptl_screencast;
mod ptl_session;
mod ptl_text; mod ptl_text;
mod ptr_gui; mod ptr_gui;
@ -16,12 +17,13 @@ use {
forker::ForkerError, forker::ForkerError,
io_uring::IoUring, io_uring::IoUring,
logger::Logger, logger::Logger,
pipewire::pw_con::{PwConHolder, PwConOwner}, pipewire::pw_con::{PwCon, PwConHolder, PwConOwner},
portal::{ portal::{
ptl_display::{watch_displays, PortalDisplay, PortalDisplayId}, ptl_display::{watch_displays, PortalDisplay, PortalDisplayId},
ptl_remote_desktop::{add_remote_desktop_dbus_members, RemoteDesktopSession}, ptl_remote_desktop::add_remote_desktop_dbus_members,
ptl_render_ctx::PortalRenderCtx, ptl_render_ctx::PortalRenderCtx,
ptl_screencast::{add_screencast_dbus_members, ScreencastSession}, ptl_screencast::add_screencast_dbus_members,
ptl_session::PortalSession,
}, },
utils::{ utils::{
clone3::{fork_with_pidfd, Forked}, clone3::{fork_with_pidfd, Forked},
@ -200,11 +202,11 @@ async fn run_async(
wheel, wheel,
displays: Default::default(), displays: Default::default(),
dbus, dbus,
screencasts: Default::default(), sessions: Default::default(),
remote_desktop_sessions: Default::default(),
next_id: NumCell::new(1), next_id: NumCell::new(1),
render_ctxs: Default::default(), render_ctxs: Default::default(),
dma_buf_ids: Default::default(), dma_buf_ids: Default::default(),
pw_con: pw_con.as_ref().map(|c| c.con.clone()),
}); });
if let Some(pw_con) = &pw_con { if let Some(pw_con) = &pw_con {
pw_con.con.owner.set(Some(state.clone())); pw_con.con.owner.set(Some(state.clone()));
@ -295,11 +297,11 @@ struct PortalState {
wheel: Rc<Wheel>, wheel: Rc<Wheel>,
displays: CopyHashMap<PortalDisplayId, Rc<PortalDisplay>>, displays: CopyHashMap<PortalDisplayId, Rc<PortalDisplay>>,
dbus: Rc<DbusSocket>, dbus: Rc<DbusSocket>,
screencasts: CopyHashMap<String, Rc<ScreencastSession>>, sessions: CopyHashMap<String, Rc<PortalSession>>,
remote_desktop_sessions: CopyHashMap<String, Rc<RemoteDesktopSession>>,
next_id: NumCell<u32>, next_id: NumCell<u32>,
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>, render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
dma_buf_ids: Rc<DmaBufIds>, dma_buf_ids: Rc<DmaBufIds>,
pw_con: Option<Rc<PwCon>>,
} }
impl PortalState { impl PortalState {

View file

@ -5,9 +5,8 @@ use {
ifs::wl_seat::POINTER, ifs::wl_seat::POINTER,
object::Version, object::Version,
portal::{ portal::{
ptl_remote_desktop::RemoteDesktopSession,
ptl_render_ctx::{PortalRenderCtx, PortalServerRenderCtx}, ptl_render_ctx::{PortalRenderCtx, PortalServerRenderCtx},
ptl_screencast::ScreencastSession, ptl_session::PortalSession,
ptr_gui::WindowData, ptr_gui::WindowData,
PortalState, PortalState,
}, },
@ -87,8 +86,7 @@ pub struct PortalDisplay {
pub workspaces: CopyHashMap<u32, Rc<UsrJayWorkspace>>, pub workspaces: CopyHashMap<u32, Rc<UsrJayWorkspace>>,
pub windows: CopyHashMap<WlSurfaceId, Rc<WindowData>>, pub windows: CopyHashMap<WlSurfaceId, Rc<WindowData>>,
pub screencasts: CopyHashMap<String, Rc<ScreencastSession>>, pub sessions: CopyHashMap<String, Rc<PortalSession>>,
pub remote_desktop_sessions: CopyHashMap<String, Rc<RemoteDesktopSession>>,
} }
pub struct PortalOutput { pub struct PortalOutput {
@ -225,7 +223,7 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
impl UsrConOwner for PortalDisplay { impl UsrConOwner for PortalDisplay {
fn killed(&self) { fn killed(&self) {
log::info!("Removing display {}", self.id); log::info!("Removing display {}", self.id);
for sc in self.screencasts.lock().drain_values() { for sc in self.sessions.lock().drain_values() {
sc.kill(); sc.kill();
} }
self.windows.clear(); self.windows.clear();
@ -441,8 +439,7 @@ fn finish_display_connect(dpy: Rc<PortalDisplayPrelude>) {
fsm, fsm,
vp, vp,
windows: Default::default(), windows: Default::default(),
screencasts: Default::default(), sessions: Default::default(),
remote_desktop_sessions: Default::default(),
workspaces: Default::default(), workspaces: Default::default(),
}); });

View file

@ -2,17 +2,18 @@ mod remote_desktop_gui;
use { use {
crate::{ crate::{
dbus::{prelude::Variant, DbusObject, DictEntry, DynamicType, PendingReply, FALSE}, dbus::{prelude::Variant, DbusObject, PendingReply},
ifs::jay_compositor::CREATE_EI_SESSION_SINCE, ifs::jay_compositor::CREATE_EI_SESSION_SINCE,
portal::{ portal::{
ptl_display::{PortalDisplay, PortalDisplayId}, ptl_display::{PortalDisplay, PortalDisplayId},
ptl_remote_desktop::remote_desktop_gui::SelectionGui, ptl_remote_desktop::remote_desktop_gui::SelectionGui,
ptl_screencast::ScreencastPhase,
ptl_session::{PortalSession, PortalSessionReply},
PortalState, PORTAL_SUCCESS, PortalState, PORTAL_SUCCESS,
}, },
utils::{ utils::{
clonecell::{CloneCell, UnsafeCellCloneSafe}, clonecell::{CloneCell, UnsafeCellCloneSafe},
copyhashmap::CopyHashMap, copyhashmap::CopyHashMap,
hash_map_ext::HashMapExt,
}, },
wire_dbus::{ wire_dbus::{
org, org,
@ -21,24 +22,15 @@ use {
ConnectToEIS, ConnectToEISReply, CreateSession, CreateSessionReply, ConnectToEIS, ConnectToEISReply, CreateSession, CreateSessionReply,
SelectDevices, SelectDevicesReply, Start, StartReply, SelectDevices, SelectDevicesReply, Start, StartReply,
}, },
session::{CloseReply as SessionCloseReply, Closed}, session::CloseReply as SessionCloseReply,
}, },
}, },
wl_usr::usr_ifs::usr_jay_ei_session::{UsrJayEiSession, UsrJayEiSessionOwner}, wl_usr::usr_ifs::usr_jay_ei_session::{UsrJayEiSession, UsrJayEiSessionOwner},
}, },
std::{borrow::Cow, cell::Cell, ops::Deref, rc::Rc}, std::{cell::Cell, ops::Deref, rc::Rc},
uapi::OwnedFd, uapi::OwnedFd,
}; };
shared_ids!(ScreencastSessionId);
pub struct RemoteDesktopSession {
_id: ScreencastSessionId,
state: Rc<PortalState>,
pub app: String,
session_obj: DbusObject,
pub phase: CloneCell<RemoteDesktopPhase>,
}
#[derive(Clone)] #[derive(Clone)]
pub enum RemoteDesktopPhase { pub enum RemoteDesktopPhase {
Init, Init,
@ -52,25 +44,23 @@ pub enum RemoteDesktopPhase {
unsafe impl UnsafeCellCloneSafe for RemoteDesktopPhase {} unsafe impl UnsafeCellCloneSafe for RemoteDesktopPhase {}
pub struct SelectingDisplay { pub struct SelectingDisplay {
pub session: Rc<RemoteDesktopSession>, pub session: Rc<PortalSession>,
pub request_obj: Rc<DbusObject>, pub request_obj: Rc<DbusObject>,
pub reply: Rc<PendingReply<StartReply<'static>>>,
pub guis: CopyHashMap<PortalDisplayId, Rc<SelectionGui>>, pub guis: CopyHashMap<PortalDisplayId, Rc<SelectionGui>>,
} }
pub struct StartingRemoteDesktop { pub struct StartingRemoteDesktop {
pub session: Rc<RemoteDesktopSession>, pub session: Rc<PortalSession>,
pub _request_obj: Rc<DbusObject>, pub request_obj: Rc<DbusObject>,
pub reply: Rc<PendingReply<StartReply<'static>>>,
pub dpy: Rc<PortalDisplay>, pub dpy: Rc<PortalDisplay>,
pub ei_session: Rc<UsrJayEiSession>, pub ei_session: Rc<UsrJayEiSession>,
} }
pub struct StartedRemoteDesktop { pub struct StartedRemoteDesktop {
session: Rc<RemoteDesktopSession>, pub session: Rc<PortalSession>,
dpy: Rc<PortalDisplay>, pub dpy: Rc<PortalDisplay>,
ei_session: Rc<UsrJayEiSession>, pub ei_session: Rc<UsrJayEiSession>,
ei_fd: Cell<Option<Rc<OwnedFd>>>, pub ei_fd: Cell<Option<Rc<OwnedFd>>>,
} }
bitflags! { bitflags! {
@ -83,34 +73,6 @@ bitflags! {
impl UsrJayEiSessionOwner for StartingRemoteDesktop { impl UsrJayEiSessionOwner for StartingRemoteDesktop {
fn created(&self, fd: &Rc<OwnedFd>) { fn created(&self, fd: &Rc<OwnedFd>) {
{
let inner_type = DynamicType::DictEntry(
Box::new(DynamicType::String),
Box::new(DynamicType::Variant),
);
let kt = DynamicType::Struct(vec![
DynamicType::U32,
DynamicType::Array(Box::new(inner_type.clone())),
]);
let variants = [
DictEntry {
key: "devices".into(),
value: Variant::U32(DeviceTypes::all().0),
},
DictEntry {
key: "clipboard_enabled".into(),
value: Variant::Bool(FALSE),
},
DictEntry {
key: "streams".into(),
value: Variant::Array(kt, vec![]),
},
];
self.reply.ok(&StartReply {
response: PORTAL_SUCCESS,
results: Cow::Borrowed(&variants[..]),
});
}
let started = Rc::new(StartedRemoteDesktop { let started = Rc::new(StartedRemoteDesktop {
session: self.session.clone(), session: self.session.clone(),
dpy: self.dpy.clone(), dpy: self.dpy.clone(),
@ -118,14 +80,23 @@ impl UsrJayEiSessionOwner for StartingRemoteDesktop {
ei_fd: Cell::new(Some(fd.clone())), ei_fd: Cell::new(Some(fd.clone())),
}); });
self.session self.session
.phase .rd_phase
.set(RemoteDesktopPhase::Started(started.clone())); .set(RemoteDesktopPhase::Started(started.clone()));
started.ei_session.owner.set(Some(started.clone())); started.ei_session.owner.set(Some(started.clone()));
if let ScreencastPhase::SourcesSelected(s) = self.session.sc_phase.get() {
self.session.screencast_restore(
&self.request_obj,
s.restore_data.take(),
Some(self.dpy.clone()),
);
} else {
self.session.send_start_reply(None, None);
}
} }
fn failed(&self, reason: &str) { fn failed(&self, reason: &str) {
log::error!("Could not create session: {}", reason); log::error!("Could not create session: {}", reason);
self.reply.err(reason); self.session.reply_err(reason);
self.session.kill(); self.session.kill();
} }
} }
@ -137,60 +108,28 @@ impl SelectingDisplay {
let ei_session = builder.commit(); let ei_session = builder.commit();
let starting = Rc::new(StartingRemoteDesktop { let starting = Rc::new(StartingRemoteDesktop {
session: self.session.clone(), session: self.session.clone(),
_request_obj: self.request_obj.clone(), request_obj: self.request_obj.clone(),
reply: self.reply.clone(),
dpy: dpy.clone(), dpy: dpy.clone(),
ei_session, ei_session,
}); });
self.session self.session
.phase .rd_phase
.set(RemoteDesktopPhase::Starting(starting.clone())); .set(RemoteDesktopPhase::Starting(starting.clone()));
starting.ei_session.owner.set(Some(starting.clone())); starting.ei_session.owner.set(Some(starting.clone()));
dpy.remote_desktop_sessions.set( dpy.sessions.set(
self.session.session_obj.path().to_owned(), self.session.session_obj.path().to_owned(),
self.session.clone(), self.session.clone(),
); );
} }
} }
impl RemoteDesktopSession { impl PortalSession {
pub(super) fn kill(&self) {
self.session_obj.emit_signal(&Closed);
self.state
.remote_desktop_sessions
.remove(self.session_obj.path());
match self.phase.set(RemoteDesktopPhase::Terminated) {
RemoteDesktopPhase::Init => {}
RemoteDesktopPhase::DevicesSelected => {}
RemoteDesktopPhase::Terminated => {}
RemoteDesktopPhase::Selecting(s) => {
s.reply.err("Session has been terminated");
for gui in s.guis.lock().drain_values() {
gui.kill(false);
}
}
RemoteDesktopPhase::Starting(s) => {
s.reply.err("Session has been terminated");
s.ei_session.con.remove_obj(s.ei_session.deref());
s.dpy
.remote_desktop_sessions
.remove(self.session_obj.path());
}
RemoteDesktopPhase::Started(s) => {
s.ei_session.con.remove_obj(s.ei_session.deref());
s.dpy
.remote_desktop_sessions
.remove(self.session_obj.path());
}
}
}
fn dbus_select_devices( fn dbus_select_devices(
self: &Rc<Self>, self: &Rc<Self>,
_req: SelectDevices, _req: SelectDevices,
reply: PendingReply<SelectDevicesReply<'static>>, reply: PendingReply<SelectDevicesReply<'static>>,
) { ) {
match self.phase.get() { match self.rd_phase.get() {
RemoteDesktopPhase::Init => {} RemoteDesktopPhase::Init => {}
_ => { _ => {
self.kill(); self.kill();
@ -198,15 +137,19 @@ impl RemoteDesktopSession {
return; return;
} }
} }
self.phase.set(RemoteDesktopPhase::DevicesSelected); self.rd_phase.set(RemoteDesktopPhase::DevicesSelected);
reply.ok(&SelectDevicesReply { reply.ok(&SelectDevicesReply {
response: PORTAL_SUCCESS, response: PORTAL_SUCCESS,
results: Default::default(), results: Default::default(),
}); });
} }
fn dbus_start(self: &Rc<Self>, req: Start<'_>, reply: PendingReply<StartReply<'static>>) { fn dbus_start_remote_desktop(
match self.phase.get() { self: &Rc<Self>,
req: Start<'_>,
reply: PendingReply<StartReply<'static>>,
) {
match self.rd_phase.get() {
RemoteDesktopPhase::DevicesSelected => {} RemoteDesktopPhase::DevicesSelected => {}
_ => { _ => {
self.kill(); self.kill();
@ -243,11 +186,12 @@ impl RemoteDesktopSession {
reply.err("There are no running displays"); reply.err("There are no running displays");
return; return;
} }
self.phase self.start_reply
.set(Some(PortalSessionReply::RemoteDesktop(reply)));
self.rd_phase
.set(RemoteDesktopPhase::Selecting(Rc::new(SelectingDisplay { .set(RemoteDesktopPhase::Selecting(Rc::new(SelectingDisplay {
session: self.clone(), session: self.clone(),
request_obj: Rc::new(request_obj), request_obj: Rc::new(request_obj),
reply: Rc::new(reply),
guis, guis,
}))); })));
} }
@ -257,7 +201,7 @@ impl RemoteDesktopSession {
_req: ConnectToEIS, _req: ConnectToEIS,
reply: PendingReply<ConnectToEISReply>, reply: PendingReply<ConnectToEISReply>,
) { ) {
let RemoteDesktopPhase::Started(started) = self.phase.get() else { let RemoteDesktopPhase::Started(started) = self.rd_phase.get() else {
self.kill(); self.kill();
reply.err("Sources have already been selected"); reply.err("Sources have already been selected");
return; return;
@ -305,10 +249,7 @@ fn dbus_create_session(
reply: PendingReply<CreateSessionReply<'static>>, reply: PendingReply<CreateSessionReply<'static>>,
) { ) {
log::info!("Create remote desktop session {:#?}", req); log::info!("Create remote desktop session {:#?}", req);
if state if state.sessions.contains(req.session_handle.0.deref()) {
.remote_desktop_sessions
.contains(req.session_handle.0.deref())
{
reply.err("Session already exists"); reply.err("Session already exists");
return; return;
} }
@ -319,12 +260,15 @@ fn dbus_create_session(
return; return;
} }
}; };
let session = Rc::new(RemoteDesktopSession { let session = Rc::new(PortalSession {
_id: state.id(), _id: state.id(),
state: state.clone(), state: state.clone(),
pw_con: state.pw_con.clone(),
app: req.app_id.to_string(), app: req.app_id.to_string(),
session_obj: obj, session_obj: obj,
phase: CloneCell::new(RemoteDesktopPhase::Init), sc_phase: CloneCell::new(ScreencastPhase::Init),
rd_phase: CloneCell::new(RemoteDesktopPhase::Init),
start_reply: Default::default(),
}); });
{ {
use org::freedesktop::impl_::portal::session::*; use org::freedesktop::impl_::portal::session::*;
@ -336,7 +280,7 @@ fn dbus_create_session(
session.session_obj.set_property::<version>(Variant::U32(2)); session.session_obj.set_property::<version>(Variant::U32(2));
} }
state state
.remote_desktop_sessions .sessions
.set(req.session_handle.0.to_string(), session); .set(req.session_handle.0.to_string(), session);
reply.ok(&CreateSessionReply { reply.ok(&CreateSessionReply {
response: PORTAL_SUCCESS, response: PORTAL_SUCCESS,
@ -356,7 +300,7 @@ fn dbus_select_devices(
fn dbus_start(state: &Rc<PortalState>, req: Start, reply: PendingReply<StartReply<'static>>) { fn dbus_start(state: &Rc<PortalState>, req: Start, reply: PendingReply<StartReply<'static>>) {
if let Some(s) = get_session(state, &reply, &req.session_handle.0) { if let Some(s) = get_session(state, &reply, &req.session_handle.0) {
s.dbus_start(req, reply); s.dbus_start_remote_desktop(req, reply);
} }
} }
@ -374,8 +318,8 @@ fn get_session<T>(
state: &Rc<PortalState>, state: &Rc<PortalState>,
reply: &PendingReply<T>, reply: &PendingReply<T>,
handle: &str, handle: &str,
) -> Option<Rc<RemoteDesktopSession>> { ) -> Option<Rc<PortalSession>> {
let res = state.remote_desktop_sessions.get(handle); let res = state.sessions.get(handle);
if res.is_none() { if res.is_none() {
let msg = format!("Remote desktop session `{}` does not exist", handle); let msg = format!("Remote desktop session `{}` does not exist", handle);
reply.err(&msg); reply.err(&msg);

View file

@ -3,7 +3,7 @@ use {
ifs::wl_seat::{wl_pointer::PRESSED, BTN_LEFT}, ifs::wl_seat::{wl_pointer::PRESSED, BTN_LEFT},
portal::{ portal::{
ptl_display::{PortalDisplay, PortalOutput, PortalSeat}, ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
ptl_remote_desktop::{RemoteDesktopPhase, RemoteDesktopSession}, ptl_remote_desktop::{PortalSession, RemoteDesktopPhase},
ptr_gui::{ ptr_gui::{
Align, Button, ButtonOwner, Flow, GuiElement, Label, Orientation, OverlayWindow, Align, Button, ButtonOwner, Flow, GuiElement, Label, Orientation, OverlayWindow,
OverlayWindowOwner, OverlayWindowOwner,
@ -19,7 +19,7 @@ const H_MARGIN: f32 = 30.0;
const V_MARGIN: f32 = 20.0; const V_MARGIN: f32 = 20.0;
pub struct SelectionGui { pub struct SelectionGui {
remote_desktop_session: Rc<RemoteDesktopSession>, remote_desktop_session: Rc<PortalSession>,
dpy: Rc<PortalDisplay>, dpy: Rc<PortalDisplay>,
surfaces: CopyHashMap<u32, Rc<SelectionGuiSurface>>, surfaces: CopyHashMap<u32, Rc<SelectionGuiSurface>>,
} }
@ -46,7 +46,7 @@ impl SelectionGui {
for surface in self.surfaces.lock().drain_values() { for surface in self.surfaces.lock().drain_values() {
surface.overlay.data.kill(false); surface.overlay.data.kill(false);
} }
if let RemoteDesktopPhase::Selecting(s) = self.remote_desktop_session.phase.get() { if let RemoteDesktopPhase::Selecting(s) = self.remote_desktop_session.rd_phase.get() {
s.guis.remove(&self.dpy.id); s.guis.remove(&self.dpy.id);
if upwards && s.guis.is_empty() { if upwards && s.guis.is_empty() {
self.remote_desktop_session.kill(); self.remote_desktop_session.kill();
@ -99,7 +99,7 @@ impl OverlayWindowOwner for SelectionGuiSurface {
} }
impl SelectionGui { impl SelectionGui {
pub fn new(ss: &Rc<RemoteDesktopSession>, dpy: &Rc<PortalDisplay>) -> Rc<Self> { pub fn new(ss: &Rc<PortalSession>, dpy: &Rc<PortalDisplay>) -> Rc<Self> {
let gui = Rc::new(SelectionGui { let gui = Rc::new(SelectionGui {
remote_desktop_session: ss.clone(), remote_desktop_session: ss.clone(),
dpy: dpy.clone(), dpy: dpy.clone(),
@ -130,7 +130,7 @@ impl ButtonOwner for StaticButton {
match self.role { match self.role {
ButtonRole::Accept => { ButtonRole::Accept => {
log::info!("User has accepted the request"); log::info!("User has accepted the request");
let selecting = match self.surface.gui.remote_desktop_session.phase.get() { let selecting = match self.surface.gui.remote_desktop_session.rd_phase.get() {
RemoteDesktopPhase::Selecting(selecting) => selecting, RemoteDesktopPhase::Selecting(selecting) => selecting,
_ => return, _ => return,
}; };

View file

@ -3,7 +3,7 @@ mod screencast_gui;
use { use {
crate::{ crate::{
allocator::{AllocatorError, BufferObject, BufferUsage, BO_USE_RENDERING}, allocator::{AllocatorError, BufferObject, BufferUsage, BO_USE_RENDERING},
dbus::{prelude::Variant, DbusObject, DictEntry, DynamicType, PendingReply}, dbus::{prelude::Variant, DbusObject, DictEntry, PendingReply},
format::{Format, XRGB8888}, format::{Format, XRGB8888},
ifs::{jay_compositor::GET_TOPLEVEL_SINCE, jay_screencast::CLIENT_BUFFERS_SINCE}, ifs::{jay_compositor::GET_TOPLEVEL_SINCE, jay_screencast::CLIENT_BUFFERS_SINCE},
pipewire::{ pipewire::{
@ -21,14 +21,15 @@ use {
}, },
portal::{ portal::{
ptl_display::{PortalDisplay, PortalDisplayId, PortalOutput}, ptl_display::{PortalDisplay, PortalDisplayId, PortalOutput},
ptl_remote_desktop::RemoteDesktopPhase,
ptl_screencast::screencast_gui::SelectionGui, ptl_screencast::screencast_gui::SelectionGui,
ptl_session::{PortalSession, PortalSessionReply},
PortalState, PORTAL_SUCCESS, PortalState, PORTAL_SUCCESS,
}, },
utils::{ utils::{
clonecell::{CloneCell, UnsafeCellCloneSafe}, clonecell::{CloneCell, UnsafeCellCloneSafe},
copyhashmap::CopyHashMap, copyhashmap::CopyHashMap,
errorfmt::ErrorFmt, errorfmt::ErrorFmt,
hash_map_ext::HashMapExt,
opaque::Opaque, opaque::Opaque,
}, },
video::{dmabuf::DmaBuf, Modifier, LINEAR_MODIFIER}, video::{dmabuf::DmaBuf, Modifier, LINEAR_MODIFIER},
@ -40,7 +41,7 @@ use {
CreateSession, CreateSessionReply, SelectSources, SelectSourcesReply, Start, CreateSession, CreateSessionReply, SelectSources, SelectSourcesReply, Start,
StartReply, StartReply,
}, },
session::{CloseReply as SessionCloseReply, Closed}, session::CloseReply as SessionCloseReply,
}, },
}, },
wl_usr::usr_ifs::{ wl_usr::usr_ifs::{
@ -66,16 +67,6 @@ use {
thiserror::Error, thiserror::Error,
}; };
shared_ids!(ScreencastSessionId);
pub struct ScreencastSession {
_id: ScreencastSessionId,
state: Rc<PortalState>,
pw_con: Rc<PwCon>,
pub app: String,
session_obj: DbusObject,
pub phase: CloneCell<ScreencastPhase>,
}
#[derive(Clone)] #[derive(Clone)]
pub enum ScreencastPhase { pub enum ScreencastPhase {
Init, Init,
@ -96,9 +87,8 @@ pub struct SourcesSelectedScreencast {
#[derive(Clone)] #[derive(Clone)]
pub struct SelectingScreencastCore { pub struct SelectingScreencastCore {
pub session: Rc<ScreencastSession>, pub session: Rc<PortalSession>,
pub request_obj: Rc<DbusObject>, pub request_obj: Rc<DbusObject>,
pub reply: Rc<PendingReply<StartReply<'static>>>,
} }
pub struct SelectingScreencast { pub struct SelectingScreencast {
@ -121,9 +111,8 @@ pub struct SelectingWorkspaceScreencast {
} }
pub struct StartingScreencast { pub struct StartingScreencast {
pub session: Rc<ScreencastSession>, pub session: Rc<PortalSession>,
pub _request_obj: Rc<DbusObject>, pub _request_obj: Rc<DbusObject>,
pub reply: Rc<PendingReply<StartReply<'static>>>,
pub node: Rc<PwClientNode>, pub node: Rc<PwClientNode>,
pub dpy: Rc<PortalDisplay>, pub dpy: Rc<PortalDisplay>,
pub target: ScreencastTarget, pub target: ScreencastTarget,
@ -136,21 +125,21 @@ pub enum ScreencastTarget {
} }
pub struct StartedScreencast { pub struct StartedScreencast {
session: Rc<ScreencastSession>, pub session: Rc<PortalSession>,
node: Rc<PwClientNode>, pub node: Rc<PwClientNode>,
port: Rc<PwClientNodePort>, pub port: Rc<PwClientNodePort>,
buffer_objects: RefCell<Vec<Rc<dyn BufferObject>>>, pub buffer_objects: RefCell<Vec<Rc<dyn BufferObject>>>,
buffers: RefCell<Vec<DmaBuf>>, pub buffers: RefCell<Vec<DmaBuf>>,
pending_buffers: RefCell<Vec<Rc<UsrLinuxBufferParams>>>, pub pending_buffers: RefCell<Vec<Rc<UsrLinuxBufferParams>>>,
buffers_valid: Cell<bool>, pub buffers_valid: Cell<bool>,
dpy: Rc<PortalDisplay>, pub dpy: Rc<PortalDisplay>,
jay_screencast: Rc<UsrJayScreencast>, pub jay_screencast: Rc<UsrJayScreencast>,
port_buffer_valid: Cell<bool>, pub port_buffer_valid: Cell<bool>,
fixated: Cell<bool>, pub fixated: Cell<bool>,
format: Cell<&'static Format>, pub format: Cell<&'static Format>,
modifier: Cell<Modifier>, pub modifier: Cell<Modifier>,
width: Cell<i32>, pub width: Cell<i32>,
height: Cell<i32>, pub height: Cell<i32>,
} }
bitflags! { bitflags! {
@ -170,33 +159,8 @@ bitflags! {
impl PwClientNodeOwner for StartingScreencast { impl PwClientNodeOwner for StartingScreencast {
fn bound_id(&self, node_id: u32) { fn bound_id(&self, node_id: u32) {
{ self.session
let inner_type = DynamicType::DictEntry( .send_start_reply(Some(node_id), create_restore_data(&self.dpy, &self.target));
Box::new(DynamicType::String),
Box::new(DynamicType::Variant),
);
let kt = DynamicType::Struct(vec![
DynamicType::U32,
DynamicType::Array(Box::new(inner_type.clone())),
]);
let mut variants = vec![DictEntry {
key: "streams".into(),
value: Variant::Array(
kt,
vec![Variant::U32(node_id), Variant::Array(inner_type, vec![])],
),
}];
if let Some(rd) = create_restore_data(&self.dpy, &self.target) {
variants.push(DictEntry {
key: "restore_data".into(),
value: rd,
});
}
self.reply.ok(&StartReply {
response: PORTAL_SUCCESS,
results: Cow::Owned(variants),
});
}
let mut supported_formats = PwClientNodePortSupportedFormats { let mut supported_formats = PwClientNodePortSupportedFormats {
media_type: Some(SPA_MEDIA_TYPE_video), media_type: Some(SPA_MEDIA_TYPE_video),
media_sub_type: Some(SPA_MEDIA_SUBTYPE_raw), media_sub_type: Some(SPA_MEDIA_SUBTYPE_raw),
@ -271,7 +235,7 @@ impl PwClientNodeOwner for StartingScreencast {
height: Cell::new(1), height: Cell::new(1),
}); });
self.session self.session
.phase .sc_phase
.set(ScreencastPhase::Started(started.clone())); .set(ScreencastPhase::Started(started.clone()));
started.jay_screencast.owner.set(Some(started.clone())); started.jay_screencast.owner.set(Some(started.clone()));
self.node.owner.set(Some(started.clone())); self.node.owner.set(Some(started.clone()));
@ -424,7 +388,11 @@ impl StartedScreencast {
impl SelectingScreencastCore { impl SelectingScreencastCore {
pub fn starting(&self, dpy: &Rc<PortalDisplay>, target: ScreencastTarget) { pub fn starting(&self, dpy: &Rc<PortalDisplay>, target: ScreencastTarget) {
let node = self.session.pw_con.create_client_node(&[ let Some(pw_con) = &self.session.pw_con else {
self.session.kill();
return;
};
let node = pw_con.create_client_node(&[
("media.class".to_string(), "Video/Source".to_string()), ("media.class".to_string(), "Video/Source".to_string()),
("node.name".to_string(), "jay-desktop-portal".to_string()), ("node.name".to_string(), "jay-desktop-portal".to_string()),
("node.driver".to_string(), "true".to_string()), ("node.driver".to_string(), "true".to_string()),
@ -432,76 +400,28 @@ impl SelectingScreencastCore {
let starting = Rc::new(StartingScreencast { let starting = Rc::new(StartingScreencast {
session: self.session.clone(), session: self.session.clone(),
_request_obj: self.request_obj.clone(), _request_obj: self.request_obj.clone(),
reply: self.reply.clone(),
node, node,
dpy: dpy.clone(), dpy: dpy.clone(),
target, target,
}); });
self.session self.session
.phase .sc_phase
.set(ScreencastPhase::Starting(starting.clone())); .set(ScreencastPhase::Starting(starting.clone()));
starting.node.owner.set(Some(starting.clone())); starting.node.owner.set(Some(starting.clone()));
dpy.screencasts.set( dpy.sessions.set(
self.session.session_obj.path().to_owned(), self.session.session_obj.path().to_owned(),
self.session.clone(), self.session.clone(),
); );
} }
} }
impl ScreencastSession { impl PortalSession {
pub(super) fn kill(&self) {
self.session_obj.emit_signal(&Closed);
self.state.screencasts.remove(self.session_obj.path());
match self.phase.set(ScreencastPhase::Terminated) {
ScreencastPhase::Init => {}
ScreencastPhase::SourcesSelected(_) => {}
ScreencastPhase::Terminated => {}
ScreencastPhase::Selecting(s) => {
s.core.reply.err("Session has been terminated");
for gui in s.guis.lock().drain_values() {
gui.kill(false);
}
}
ScreencastPhase::SelectingWindow(s) => {
s.dpy.con.remove_obj(&*s.selector);
s.core.reply.err("Session has been terminated");
}
ScreencastPhase::SelectingWorkspace(s) => {
s.dpy.con.remove_obj(&*s.selector);
s.core.reply.err("Session has been terminated");
}
ScreencastPhase::Starting(s) => {
s.reply.err("Session has been terminated");
s.node.con.destroy_obj(s.node.deref());
s.dpy.screencasts.remove(self.session_obj.path());
match &s.target {
ScreencastTarget::Output(_) => {}
ScreencastTarget::Workspace(_, w, true) => {
s.dpy.con.remove_obj(&**w);
}
ScreencastTarget::Workspace(_, _, false) => {}
ScreencastTarget::Toplevel(t) => {
s.dpy.con.remove_obj(&**t);
}
}
}
ScreencastPhase::Started(s) => {
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);
}
}
}
}
fn dbus_select_sources( fn dbus_select_sources(
self: &Rc<Self>, self: &Rc<Self>,
req: SelectSources, req: SelectSources,
reply: PendingReply<SelectSourcesReply<'static>>, reply: PendingReply<SelectSourcesReply<'static>>,
) { ) {
match self.phase.get() { match self.sc_phase.get() {
ScreencastPhase::Init => {} ScreencastPhase::Init => {}
_ => { _ => {
self.kill(); self.kill();
@ -509,7 +429,7 @@ impl ScreencastSession {
return; return;
} }
} }
self.phase.set(ScreencastPhase::SourcesSelected(Rc::new( self.sc_phase.set(ScreencastPhase::SourcesSelected(Rc::new(
SourcesSelectedScreencast { SourcesSelectedScreencast {
restore_data: Cell::new(get_restore_data(&req)), restore_data: Cell::new(get_restore_data(&req)),
}, },
@ -520,8 +440,12 @@ impl ScreencastSession {
}); });
} }
fn dbus_start(self: &Rc<Self>, req: Start<'_>, reply: PendingReply<StartReply<'static>>) { fn dbus_start_screencast(
let restore_data = match self.phase.get() { self: &Rc<Self>,
req: Start<'_>,
reply: PendingReply<StartReply<'static>>,
) {
let restore_data = match self.sc_phase.get() {
ScreencastPhase::SourcesSelected(s) => s.restore_data.take(), ScreencastPhase::SourcesSelected(s) => s.restore_data.take(),
_ => { _ => {
self.kill(); self.kill();
@ -547,14 +471,14 @@ impl ScreencastSession {
} }
}); });
} }
let reply = Rc::new(reply); self.start_reply
self.restore(&request_obj, &reply, restore_data, None); .set(Some(PortalSessionReply::ScreenCast(reply)));
self.screencast_restore(&request_obj, restore_data, None);
} }
fn start_interactive_selection( fn start_interactive_selection(
self: &Rc<Self>, self: &Rc<Self>,
request_obj: &Rc<DbusObject>, request_obj: &Rc<DbusObject>,
reply: &Rc<PendingReply<StartReply<'static>>>,
restore_data: Option<RestoreData>, restore_data: Option<RestoreData>,
) { ) {
let guis = CopyHashMap::new(); let guis = CopyHashMap::new();
@ -565,42 +489,39 @@ impl ScreencastSession {
} }
if guis.is_empty() { if guis.is_empty() {
self.kill(); self.kill();
reply.err("There are no running displays"); self.reply_err("There are no running displays");
return; return;
} }
self.phase self.sc_phase
.set(ScreencastPhase::Selecting(Rc::new(SelectingScreencast { .set(ScreencastPhase::Selecting(Rc::new(SelectingScreencast {
core: SelectingScreencastCore { core: SelectingScreencastCore {
session: self.clone(), session: self.clone(),
request_obj: request_obj.clone(), request_obj: request_obj.clone(),
reply: reply.clone(),
}, },
guis, guis,
restore_data: Cell::new(restore_data), restore_data: Cell::new(restore_data),
}))); })));
} }
fn restore( pub fn screencast_restore(
self: &Rc<Self>, self: &Rc<Self>,
request_obj: &Rc<DbusObject>, request_obj: &Rc<DbusObject>,
reply: &Rc<PendingReply<StartReply<'static>>>,
restore_data: Option<Result<RestoreData, RestoreError>>, restore_data: Option<Result<RestoreData, RestoreError>>,
display: Option<Rc<PortalDisplay>>, display: Option<Rc<PortalDisplay>>,
) { ) {
if let Some(rd) = restore_data { if let Some(rd) = restore_data {
if let Err(e) = self.try_restore(&request_obj, &reply, rd, display) { if let Err(e) = self.try_restore(&request_obj, rd, display) {
log::error!("Could not restore session: {}", ErrorFmt(e)); log::error!("Could not restore session: {}", ErrorFmt(e));
} else { } else {
return; return;
} }
} }
self.start_interactive_selection(&request_obj, &reply, None); self.start_interactive_selection(&request_obj, None);
} }
fn try_restore( fn try_restore(
self: &Rc<Self>, self: &Rc<Self>,
request_obj: &Rc<DbusObject>, request_obj: &Rc<DbusObject>,
reply: &Rc<PendingReply<StartReply<'static>>>,
restore_data: Result<RestoreData, RestoreError>, restore_data: Result<RestoreData, RestoreError>,
display: Option<Rc<PortalDisplay>>, display: Option<Rc<PortalDisplay>>,
) -> Result<(), RestoreError> { ) -> Result<(), RestoreError> {
@ -623,7 +544,7 @@ impl ScreencastSession {
} else if self.state.displays.len() == 1 { } else if self.state.displays.len() == 1 {
self.state.displays.lock().values().next().unwrap().clone() self.state.displays.lock().values().next().unwrap().clone()
} else { } else {
self.start_interactive_selection(&request_obj, &reply, Some(rd)); self.start_interactive_selection(&request_obj, Some(rd));
return Ok(()); return Ok(());
} }
} }
@ -633,7 +554,6 @@ impl ScreencastSession {
SelectingScreencastCore { SelectingScreencastCore {
session: self.clone(), session: self.clone(),
request_obj: request_obj.clone(), request_obj: request_obj.clone(),
reply: reply.clone(),
} }
.starting(&dpy, target); .starting(&dpy, target);
}; };
@ -674,14 +594,14 @@ impl ScreencastSession {
core: SelectingScreencastCore { core: SelectingScreencastCore {
session: self.clone(), session: self.clone(),
request_obj: request_obj.clone(), request_obj: request_obj.clone(),
reply: reply.clone(),
}, },
dpy: dpy.clone(), dpy: dpy.clone(),
selector: selector.clone(), selector: selector.clone(),
restoring: true, restoring: true,
}); });
selector.owner.set(Some(selecting.clone())); selector.owner.set(Some(selecting.clone()));
self.phase.set(ScreencastPhase::SelectingWindow(selecting)); self.sc_phase
.set(ScreencastPhase::SelectingWindow(selecting));
} }
} }
Ok(()) Ok(())
@ -833,7 +753,7 @@ fn dbus_create_session(
reply: PendingReply<CreateSessionReply<'static>>, reply: PendingReply<CreateSessionReply<'static>>,
) { ) {
log::info!("Create Session {:#?}", req); log::info!("Create Session {:#?}", req);
if state.screencasts.contains(req.session_handle.0.deref()) { if state.sessions.contains(req.session_handle.0.deref()) {
reply.err("Session already exists"); reply.err("Session already exists");
return; return;
} }
@ -844,13 +764,15 @@ fn dbus_create_session(
return; return;
} }
}; };
let session = Rc::new(ScreencastSession { let session = Rc::new(PortalSession {
_id: state.id(), _id: state.id(),
state: state.clone(), state: state.clone(),
pw_con: pw_con.clone(), pw_con: Some(pw_con.clone()),
app: req.app_id.to_string(), app: req.app_id.to_string(),
session_obj: obj, session_obj: obj,
phase: CloneCell::new(ScreencastPhase::Init), sc_phase: CloneCell::new(ScreencastPhase::Init),
rd_phase: CloneCell::new(RemoteDesktopPhase::Init),
start_reply: Default::default(),
}); });
{ {
use org::freedesktop::impl_::portal::session::*; use org::freedesktop::impl_::portal::session::*;
@ -862,7 +784,7 @@ fn dbus_create_session(
session.session_obj.set_property::<version>(Variant::U32(4)); session.session_obj.set_property::<version>(Variant::U32(4));
} }
state state
.screencasts .sessions
.set(req.session_handle.0.to_string(), session); .set(req.session_handle.0.to_string(), session);
reply.ok(&CreateSessionReply { reply.ok(&CreateSessionReply {
response: PORTAL_SUCCESS, response: PORTAL_SUCCESS,
@ -882,7 +804,7 @@ fn dbus_select_sources(
fn dbus_start(state: &Rc<PortalState>, req: Start, reply: PendingReply<StartReply<'static>>) { fn dbus_start(state: &Rc<PortalState>, req: Start, reply: PendingReply<StartReply<'static>>) {
if let Some(s) = get_session(state, &reply, &req.session_handle.0) { if let Some(s) = get_session(state, &reply, &req.session_handle.0) {
s.dbus_start(req, reply); s.dbus_start_screencast(req, reply);
} }
} }
@ -890,8 +812,8 @@ fn get_session<T>(
state: &Rc<PortalState>, state: &Rc<PortalState>,
reply: &PendingReply<T>, reply: &PendingReply<T>,
handle: &str, handle: &str,
) -> Option<Rc<ScreencastSession>> { ) -> Option<Rc<PortalSession>> {
let res = state.screencasts.get(handle); let res = state.sessions.get(handle);
if res.is_none() { if res.is_none() {
let msg = format!("Screencast session `{}` does not exist", handle); let msg = format!("Screencast session `{}` does not exist", handle);
reply.err(&msg); reply.err(&msg);

View file

@ -4,7 +4,7 @@ use {
portal::{ portal::{
ptl_display::{PortalDisplay, PortalOutput, PortalSeat}, ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
ptl_screencast::{ ptl_screencast::{
ScreencastPhase, ScreencastSession, ScreencastTarget, SelectingWindowScreencast, PortalSession, ScreencastPhase, ScreencastTarget, SelectingWindowScreencast,
SelectingWorkspaceScreencast, SelectingWorkspaceScreencast,
}, },
ptr_gui::{ ptr_gui::{
@ -27,7 +27,7 @@ const H_MARGIN: f32 = 30.0;
const V_MARGIN: f32 = 20.0; const V_MARGIN: f32 = 20.0;
pub struct SelectionGui { pub struct SelectionGui {
screencast_session: Rc<ScreencastSession>, screencast_session: Rc<PortalSession>,
dpy: Rc<PortalDisplay>, dpy: Rc<PortalDisplay>,
surfaces: CopyHashMap<u32, Rc<SelectionGuiSurface>>, surfaces: CopyHashMap<u32, Rc<SelectionGuiSurface>>,
} }
@ -57,7 +57,7 @@ impl SelectionGui {
for surface in self.surfaces.lock().drain_values() { for surface in self.surfaces.lock().drain_values() {
surface.overlay.data.kill(false); surface.overlay.data.kill(false);
} }
if let ScreencastPhase::Selecting(s) = self.screencast_session.phase.get() { if let ScreencastPhase::Selecting(s) = self.screencast_session.sc_phase.get() {
s.guis.remove(&self.dpy.id); s.guis.remove(&self.dpy.id);
if upwards && s.guis.is_empty() { if upwards && s.guis.is_empty() {
self.screencast_session.kill(); self.screencast_session.kill();
@ -135,7 +135,7 @@ impl OverlayWindowOwner for SelectionGuiSurface {
} }
impl SelectionGui { impl SelectionGui {
pub fn new(ss: &Rc<ScreencastSession>, dpy: &Rc<PortalDisplay>, for_restore: bool) -> Rc<Self> { pub fn new(ss: &Rc<PortalSession>, dpy: &Rc<PortalDisplay>, for_restore: bool) -> Rc<Self> {
let gui = Rc::new(SelectionGui { let gui = Rc::new(SelectionGui {
screencast_session: ss.clone(), screencast_session: ss.clone(),
dpy: dpy.clone(), dpy: dpy.clone(),
@ -169,7 +169,7 @@ impl ButtonOwner for StaticButton {
| ButtonRole::SelectWorkspace | ButtonRole::SelectWorkspace
| ButtonRole::SelectWindow => { | ButtonRole::SelectWindow => {
log::info!("User has accepted the request"); log::info!("User has accepted the request");
let selecting = match self.surface.gui.screencast_session.phase.get() { let selecting = match self.surface.gui.screencast_session.sc_phase.get() {
ScreencastPhase::Selecting(selecting) => selecting, ScreencastPhase::Selecting(selecting) => selecting,
_ => return, _ => return,
}; };
@ -178,9 +178,8 @@ impl ButtonOwner for StaticButton {
} }
let dpy = &self.surface.output.dpy; let dpy = &self.surface.output.dpy;
if self.role == ButtonRole::Restore { if self.role == ButtonRole::Restore {
selecting.core.session.restore( selecting.core.session.screencast_restore(
&selecting.core.request_obj, &selecting.core.request_obj,
&selecting.core.reply,
selecting.restore_data.take().map(Ok), selecting.restore_data.take().map(Ok),
Some(self.surface.gui.dpy.clone()), Some(self.surface.gui.dpy.clone()),
); );
@ -199,7 +198,7 @@ impl ButtonOwner for StaticButton {
self.surface self.surface
.gui .gui
.screencast_session .screencast_session
.phase .sc_phase
.set(ScreencastPhase::SelectingWorkspace(selecting)); .set(ScreencastPhase::SelectingWorkspace(selecting));
} else { } else {
let selector = dpy.jc.select_toplevel(&seat.wl); let selector = dpy.jc.select_toplevel(&seat.wl);
@ -213,7 +212,7 @@ impl ButtonOwner for StaticButton {
self.surface self.surface
.gui .gui
.screencast_session .screencast_session
.phase .sc_phase
.set(ScreencastPhase::SelectingWindow(selecting)); .set(ScreencastPhase::SelectingWindow(selecting));
} }
} }
@ -230,18 +229,16 @@ impl UsrJaySelectToplevelOwner for SelectingWindowScreencast {
let Some(tl) = tl else { let Some(tl) = tl else {
if self.restoring { if self.restoring {
log::warn!("Could not restore session because toplevel no longer exists"); log::warn!("Could not restore session because toplevel no longer exists");
self.core.session.start_interactive_selection( self.core
&self.core.request_obj, .session
&self.core.reply, .start_interactive_selection(&self.core.request_obj, None);
None,
);
return; return;
} }
log::info!("User has aborted the selection"); log::info!("User has aborted the selection");
self.core.session.kill(); self.core.session.kill();
return; return;
}; };
match self.core.session.phase.get() { match self.core.session.sc_phase.get() {
ScreencastPhase::SelectingWindow(s) => { ScreencastPhase::SelectingWindow(s) => {
self.dpy.con.remove_obj(&*s.selector); self.dpy.con.remove_obj(&*s.selector);
} }
@ -263,7 +260,7 @@ impl UsrJaySelectWorkspaceOwner for SelectingWorkspaceScreencast {
self.core.session.kill(); self.core.session.kill();
return; return;
}; };
match self.core.session.phase.get() { match self.core.session.sc_phase.get() {
ScreencastPhase::SelectingWorkspace(s) => { ScreencastPhase::SelectingWorkspace(s) => {
self.dpy.con.remove_obj(&*s.selector); self.dpy.con.remove_obj(&*s.selector);
} }

162
src/portal/ptl_session.rs Normal file
View file

@ -0,0 +1,162 @@
use {
crate::{
dbus::{prelude::Variant, DbusObject, DictEntry, DynamicType, PendingReply, FALSE},
pipewire::pw_con::PwCon,
portal::{
ptl_remote_desktop::{DeviceTypes, RemoteDesktopPhase},
ptl_screencast::{ScreencastPhase, ScreencastTarget},
PortalState, PORTAL_SUCCESS,
},
utils::{clonecell::CloneCell, hash_map_ext::HashMapExt},
wire_dbus::org::freedesktop::impl_::portal::{
remote_desktop::StartReply as RdStartReply, screen_cast::StartReply as ScStartReply,
session::Closed,
},
},
std::{borrow::Cow, cell::Cell, ops::Deref, rc::Rc},
};
shared_ids!(SessionId);
pub struct PortalSession {
pub _id: SessionId,
pub state: Rc<PortalState>,
pub pw_con: Option<Rc<PwCon>>,
pub app: String,
pub session_obj: DbusObject,
pub sc_phase: CloneCell<ScreencastPhase>,
pub rd_phase: CloneCell<RemoteDesktopPhase>,
pub start_reply: Cell<Option<PortalSessionReply>>,
}
pub enum PortalSessionReply {
RemoteDesktop(PendingReply<RdStartReply<'static>>),
ScreenCast(PendingReply<ScStartReply<'static>>),
}
impl PortalSession {
pub(super) fn kill(&self) {
self.session_obj.emit_signal(&Closed);
self.state.sessions.remove(self.session_obj.path());
self.reply_err("Session has been terminated");
match self.rd_phase.set(RemoteDesktopPhase::Terminated) {
RemoteDesktopPhase::Init => {}
RemoteDesktopPhase::DevicesSelected => {}
RemoteDesktopPhase::Terminated => {}
RemoteDesktopPhase::Selecting(s) => {
for gui in s.guis.lock().drain_values() {
gui.kill(false);
}
}
RemoteDesktopPhase::Starting(s) => {
s.ei_session.con.remove_obj(s.ei_session.deref());
s.dpy.sessions.remove(self.session_obj.path());
}
RemoteDesktopPhase::Started(s) => {
s.ei_session.con.remove_obj(s.ei_session.deref());
s.dpy.sessions.remove(self.session_obj.path());
}
}
match self.sc_phase.set(ScreencastPhase::Terminated) {
ScreencastPhase::Init => {}
ScreencastPhase::SourcesSelected(_) => {}
ScreencastPhase::Terminated => {}
ScreencastPhase::Selecting(s) => {
for gui in s.guis.lock().drain_values() {
gui.kill(false);
}
}
ScreencastPhase::SelectingWindow(s) => {
s.dpy.con.remove_obj(&*s.selector);
}
ScreencastPhase::SelectingWorkspace(s) => {
s.dpy.con.remove_obj(&*s.selector);
}
ScreencastPhase::Starting(s) => {
s.node.con.destroy_obj(s.node.deref());
s.dpy.sessions.remove(self.session_obj.path());
match &s.target {
ScreencastTarget::Output(_) => {}
ScreencastTarget::Workspace(_, w, true) => {
s.dpy.con.remove_obj(&**w);
}
ScreencastTarget::Workspace(_, _, false) => {}
ScreencastTarget::Toplevel(t) => {
s.dpy.con.remove_obj(&**t);
}
}
}
ScreencastPhase::Started(s) => {
s.jay_screencast.con.remove_obj(s.jay_screencast.deref());
s.node.con.destroy_obj(s.node.deref());
s.dpy.sessions.remove(self.session_obj.path());
for buffer in s.pending_buffers.borrow_mut().drain(..) {
s.dpy.con.remove_obj(&*buffer);
}
}
}
}
pub(super) fn send_start_reply(
&self,
pw_node_id: Option<u32>,
restore_data: Option<Variant<'static>>,
) {
let inner_type = DynamicType::DictEntry(
Box::new(DynamicType::String),
Box::new(DynamicType::Variant),
);
let kt = DynamicType::Struct(vec![
DynamicType::U32,
DynamicType::Array(Box::new(inner_type.clone())),
]);
let mut streams = vec![];
if let Some(node_id) = pw_node_id {
streams = vec![Variant::U32(node_id), Variant::Array(inner_type, vec![])];
}
let mut variants = vec![
DictEntry {
key: "devices".into(),
value: Variant::U32(DeviceTypes::all().0),
},
DictEntry {
key: "clipboard_enabled".into(),
value: Variant::Bool(FALSE),
},
DictEntry {
key: "streams".into(),
value: Variant::Array(kt, streams),
},
];
if let Some(rd) = restore_data {
variants.push(DictEntry {
key: "restore_data".into(),
value: rd,
});
}
if let Some(reply) = self.start_reply.take() {
match reply {
PortalSessionReply::RemoteDesktop(reply) => {
reply.ok(&RdStartReply {
response: PORTAL_SUCCESS,
results: Cow::Borrowed(&variants),
});
}
PortalSessionReply::ScreenCast(reply) => {
reply.ok(&ScStartReply {
response: PORTAL_SUCCESS,
results: Cow::Borrowed(&variants),
});
}
}
}
}
pub(super) fn reply_err(&self, err: &str) {
if let Some(reply) = self.start_reply.take() {
match reply {
PortalSessionReply::RemoteDesktop(r) => r.err(err),
PortalSessionReply::ScreenCast(r) => r.err(err),
}
}
}
}