portal: unify remote desktop and screencast sessions
This commit is contained in:
parent
3e3532574b
commit
260d241f79
8 changed files with 304 additions and 279 deletions
|
|
@ -8,6 +8,7 @@
|
|||
- Implement ext-image-capture-source-v1.
|
||||
- Implement ext-image-copy-capture-v1.
|
||||
- Implement screencast session restoration.
|
||||
- Fix screen sharing in zoom.
|
||||
|
||||
# 1.6.0 (2024-09-25)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ mod ptl_display;
|
|||
mod ptl_remote_desktop;
|
||||
mod ptl_render_ctx;
|
||||
mod ptl_screencast;
|
||||
mod ptl_session;
|
||||
mod ptl_text;
|
||||
mod ptr_gui;
|
||||
|
||||
|
|
@ -16,12 +17,13 @@ use {
|
|||
forker::ForkerError,
|
||||
io_uring::IoUring,
|
||||
logger::Logger,
|
||||
pipewire::pw_con::{PwConHolder, PwConOwner},
|
||||
pipewire::pw_con::{PwCon, PwConHolder, PwConOwner},
|
||||
portal::{
|
||||
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_screencast::{add_screencast_dbus_members, ScreencastSession},
|
||||
ptl_screencast::add_screencast_dbus_members,
|
||||
ptl_session::PortalSession,
|
||||
},
|
||||
utils::{
|
||||
clone3::{fork_with_pidfd, Forked},
|
||||
|
|
@ -200,11 +202,11 @@ async fn run_async(
|
|||
wheel,
|
||||
displays: Default::default(),
|
||||
dbus,
|
||||
screencasts: Default::default(),
|
||||
remote_desktop_sessions: Default::default(),
|
||||
sessions: Default::default(),
|
||||
next_id: NumCell::new(1),
|
||||
render_ctxs: 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 {
|
||||
pw_con.con.owner.set(Some(state.clone()));
|
||||
|
|
@ -295,11 +297,11 @@ struct PortalState {
|
|||
wheel: Rc<Wheel>,
|
||||
displays: CopyHashMap<PortalDisplayId, Rc<PortalDisplay>>,
|
||||
dbus: Rc<DbusSocket>,
|
||||
screencasts: CopyHashMap<String, Rc<ScreencastSession>>,
|
||||
remote_desktop_sessions: CopyHashMap<String, Rc<RemoteDesktopSession>>,
|
||||
sessions: CopyHashMap<String, Rc<PortalSession>>,
|
||||
next_id: NumCell<u32>,
|
||||
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
|
||||
dma_buf_ids: Rc<DmaBufIds>,
|
||||
pw_con: Option<Rc<PwCon>>,
|
||||
}
|
||||
|
||||
impl PortalState {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ use {
|
|||
ifs::wl_seat::POINTER,
|
||||
object::Version,
|
||||
portal::{
|
||||
ptl_remote_desktop::RemoteDesktopSession,
|
||||
ptl_render_ctx::{PortalRenderCtx, PortalServerRenderCtx},
|
||||
ptl_screencast::ScreencastSession,
|
||||
ptl_session::PortalSession,
|
||||
ptr_gui::WindowData,
|
||||
PortalState,
|
||||
},
|
||||
|
|
@ -87,8 +86,7 @@ pub struct PortalDisplay {
|
|||
pub workspaces: CopyHashMap<u32, Rc<UsrJayWorkspace>>,
|
||||
|
||||
pub windows: CopyHashMap<WlSurfaceId, Rc<WindowData>>,
|
||||
pub screencasts: CopyHashMap<String, Rc<ScreencastSession>>,
|
||||
pub remote_desktop_sessions: CopyHashMap<String, Rc<RemoteDesktopSession>>,
|
||||
pub sessions: CopyHashMap<String, Rc<PortalSession>>,
|
||||
}
|
||||
|
||||
pub struct PortalOutput {
|
||||
|
|
@ -225,7 +223,7 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
|
|||
impl UsrConOwner for PortalDisplay {
|
||||
fn killed(&self) {
|
||||
log::info!("Removing display {}", self.id);
|
||||
for sc in self.screencasts.lock().drain_values() {
|
||||
for sc in self.sessions.lock().drain_values() {
|
||||
sc.kill();
|
||||
}
|
||||
self.windows.clear();
|
||||
|
|
@ -441,8 +439,7 @@ fn finish_display_connect(dpy: Rc<PortalDisplayPrelude>) {
|
|||
fsm,
|
||||
vp,
|
||||
windows: Default::default(),
|
||||
screencasts: Default::default(),
|
||||
remote_desktop_sessions: Default::default(),
|
||||
sessions: Default::default(),
|
||||
workspaces: Default::default(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,17 +2,18 @@ mod remote_desktop_gui;
|
|||
|
||||
use {
|
||||
crate::{
|
||||
dbus::{prelude::Variant, DbusObject, DictEntry, DynamicType, PendingReply, FALSE},
|
||||
dbus::{prelude::Variant, DbusObject, PendingReply},
|
||||
ifs::jay_compositor::CREATE_EI_SESSION_SINCE,
|
||||
portal::{
|
||||
ptl_display::{PortalDisplay, PortalDisplayId},
|
||||
ptl_remote_desktop::remote_desktop_gui::SelectionGui,
|
||||
ptl_screencast::ScreencastPhase,
|
||||
ptl_session::{PortalSession, PortalSessionReply},
|
||||
PortalState, PORTAL_SUCCESS,
|
||||
},
|
||||
utils::{
|
||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||
copyhashmap::CopyHashMap,
|
||||
hash_map_ext::HashMapExt,
|
||||
},
|
||||
wire_dbus::{
|
||||
org,
|
||||
|
|
@ -21,24 +22,15 @@ use {
|
|||
ConnectToEIS, ConnectToEISReply, CreateSession, CreateSessionReply,
|
||||
SelectDevices, SelectDevicesReply, Start, StartReply,
|
||||
},
|
||||
session::{CloseReply as SessionCloseReply, Closed},
|
||||
session::CloseReply as SessionCloseReply,
|
||||
},
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
shared_ids!(ScreencastSessionId);
|
||||
pub struct RemoteDesktopSession {
|
||||
_id: ScreencastSessionId,
|
||||
state: Rc<PortalState>,
|
||||
pub app: String,
|
||||
session_obj: DbusObject,
|
||||
pub phase: CloneCell<RemoteDesktopPhase>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum RemoteDesktopPhase {
|
||||
Init,
|
||||
|
|
@ -52,25 +44,23 @@ pub enum RemoteDesktopPhase {
|
|||
unsafe impl UnsafeCellCloneSafe for RemoteDesktopPhase {}
|
||||
|
||||
pub struct SelectingDisplay {
|
||||
pub session: Rc<RemoteDesktopSession>,
|
||||
pub session: Rc<PortalSession>,
|
||||
pub request_obj: Rc<DbusObject>,
|
||||
pub reply: Rc<PendingReply<StartReply<'static>>>,
|
||||
pub guis: CopyHashMap<PortalDisplayId, Rc<SelectionGui>>,
|
||||
}
|
||||
|
||||
pub struct StartingRemoteDesktop {
|
||||
pub session: Rc<RemoteDesktopSession>,
|
||||
pub _request_obj: Rc<DbusObject>,
|
||||
pub reply: Rc<PendingReply<StartReply<'static>>>,
|
||||
pub session: Rc<PortalSession>,
|
||||
pub request_obj: Rc<DbusObject>,
|
||||
pub dpy: Rc<PortalDisplay>,
|
||||
pub ei_session: Rc<UsrJayEiSession>,
|
||||
}
|
||||
|
||||
pub struct StartedRemoteDesktop {
|
||||
session: Rc<RemoteDesktopSession>,
|
||||
dpy: Rc<PortalDisplay>,
|
||||
ei_session: Rc<UsrJayEiSession>,
|
||||
ei_fd: Cell<Option<Rc<OwnedFd>>>,
|
||||
pub session: Rc<PortalSession>,
|
||||
pub dpy: Rc<PortalDisplay>,
|
||||
pub ei_session: Rc<UsrJayEiSession>,
|
||||
pub ei_fd: Cell<Option<Rc<OwnedFd>>>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
|
@ -83,34 +73,6 @@ bitflags! {
|
|||
|
||||
impl UsrJayEiSessionOwner for StartingRemoteDesktop {
|
||||
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 {
|
||||
session: self.session.clone(),
|
||||
dpy: self.dpy.clone(),
|
||||
|
|
@ -118,14 +80,23 @@ impl UsrJayEiSessionOwner for StartingRemoteDesktop {
|
|||
ei_fd: Cell::new(Some(fd.clone())),
|
||||
});
|
||||
self.session
|
||||
.phase
|
||||
.rd_phase
|
||||
.set(RemoteDesktopPhase::Started(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) {
|
||||
log::error!("Could not create session: {}", reason);
|
||||
self.reply.err(reason);
|
||||
self.session.reply_err(reason);
|
||||
self.session.kill();
|
||||
}
|
||||
}
|
||||
|
|
@ -137,60 +108,28 @@ impl SelectingDisplay {
|
|||
let ei_session = builder.commit();
|
||||
let starting = Rc::new(StartingRemoteDesktop {
|
||||
session: self.session.clone(),
|
||||
_request_obj: self.request_obj.clone(),
|
||||
reply: self.reply.clone(),
|
||||
request_obj: self.request_obj.clone(),
|
||||
dpy: dpy.clone(),
|
||||
ei_session,
|
||||
});
|
||||
self.session
|
||||
.phase
|
||||
.rd_phase
|
||||
.set(RemoteDesktopPhase::Starting(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.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl RemoteDesktopSession {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PortalSession {
|
||||
fn dbus_select_devices(
|
||||
self: &Rc<Self>,
|
||||
_req: SelectDevices,
|
||||
reply: PendingReply<SelectDevicesReply<'static>>,
|
||||
) {
|
||||
match self.phase.get() {
|
||||
match self.rd_phase.get() {
|
||||
RemoteDesktopPhase::Init => {}
|
||||
_ => {
|
||||
self.kill();
|
||||
|
|
@ -198,15 +137,19 @@ impl RemoteDesktopSession {
|
|||
return;
|
||||
}
|
||||
}
|
||||
self.phase.set(RemoteDesktopPhase::DevicesSelected);
|
||||
self.rd_phase.set(RemoteDesktopPhase::DevicesSelected);
|
||||
reply.ok(&SelectDevicesReply {
|
||||
response: PORTAL_SUCCESS,
|
||||
results: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
fn dbus_start(self: &Rc<Self>, req: Start<'_>, reply: PendingReply<StartReply<'static>>) {
|
||||
match self.phase.get() {
|
||||
fn dbus_start_remote_desktop(
|
||||
self: &Rc<Self>,
|
||||
req: Start<'_>,
|
||||
reply: PendingReply<StartReply<'static>>,
|
||||
) {
|
||||
match self.rd_phase.get() {
|
||||
RemoteDesktopPhase::DevicesSelected => {}
|
||||
_ => {
|
||||
self.kill();
|
||||
|
|
@ -243,11 +186,12 @@ impl RemoteDesktopSession {
|
|||
reply.err("There are no running displays");
|
||||
return;
|
||||
}
|
||||
self.phase
|
||||
self.start_reply
|
||||
.set(Some(PortalSessionReply::RemoteDesktop(reply)));
|
||||
self.rd_phase
|
||||
.set(RemoteDesktopPhase::Selecting(Rc::new(SelectingDisplay {
|
||||
session: self.clone(),
|
||||
request_obj: Rc::new(request_obj),
|
||||
reply: Rc::new(reply),
|
||||
guis,
|
||||
})));
|
||||
}
|
||||
|
|
@ -257,7 +201,7 @@ impl RemoteDesktopSession {
|
|||
_req: ConnectToEIS,
|
||||
reply: PendingReply<ConnectToEISReply>,
|
||||
) {
|
||||
let RemoteDesktopPhase::Started(started) = self.phase.get() else {
|
||||
let RemoteDesktopPhase::Started(started) = self.rd_phase.get() else {
|
||||
self.kill();
|
||||
reply.err("Sources have already been selected");
|
||||
return;
|
||||
|
|
@ -305,10 +249,7 @@ fn dbus_create_session(
|
|||
reply: PendingReply<CreateSessionReply<'static>>,
|
||||
) {
|
||||
log::info!("Create remote desktop session {:#?}", req);
|
||||
if state
|
||||
.remote_desktop_sessions
|
||||
.contains(req.session_handle.0.deref())
|
||||
{
|
||||
if state.sessions.contains(req.session_handle.0.deref()) {
|
||||
reply.err("Session already exists");
|
||||
return;
|
||||
}
|
||||
|
|
@ -319,12 +260,15 @@ fn dbus_create_session(
|
|||
return;
|
||||
}
|
||||
};
|
||||
let session = Rc::new(RemoteDesktopSession {
|
||||
let session = Rc::new(PortalSession {
|
||||
_id: state.id(),
|
||||
state: state.clone(),
|
||||
pw_con: state.pw_con.clone(),
|
||||
app: req.app_id.to_string(),
|
||||
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::*;
|
||||
|
|
@ -336,7 +280,7 @@ fn dbus_create_session(
|
|||
session.session_obj.set_property::<version>(Variant::U32(2));
|
||||
}
|
||||
state
|
||||
.remote_desktop_sessions
|
||||
.sessions
|
||||
.set(req.session_handle.0.to_string(), session);
|
||||
reply.ok(&CreateSessionReply {
|
||||
response: PORTAL_SUCCESS,
|
||||
|
|
@ -356,7 +300,7 @@ fn dbus_select_devices(
|
|||
|
||||
fn dbus_start(state: &Rc<PortalState>, req: Start, reply: PendingReply<StartReply<'static>>) {
|
||||
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>,
|
||||
reply: &PendingReply<T>,
|
||||
handle: &str,
|
||||
) -> Option<Rc<RemoteDesktopSession>> {
|
||||
let res = state.remote_desktop_sessions.get(handle);
|
||||
) -> Option<Rc<PortalSession>> {
|
||||
let res = state.sessions.get(handle);
|
||||
if res.is_none() {
|
||||
let msg = format!("Remote desktop session `{}` does not exist", handle);
|
||||
reply.err(&msg);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use {
|
|||
ifs::wl_seat::{wl_pointer::PRESSED, BTN_LEFT},
|
||||
portal::{
|
||||
ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
||||
ptl_remote_desktop::{RemoteDesktopPhase, RemoteDesktopSession},
|
||||
ptl_remote_desktop::{PortalSession, RemoteDesktopPhase},
|
||||
ptr_gui::{
|
||||
Align, Button, ButtonOwner, Flow, GuiElement, Label, Orientation, OverlayWindow,
|
||||
OverlayWindowOwner,
|
||||
|
|
@ -19,7 +19,7 @@ const H_MARGIN: f32 = 30.0;
|
|||
const V_MARGIN: f32 = 20.0;
|
||||
|
||||
pub struct SelectionGui {
|
||||
remote_desktop_session: Rc<RemoteDesktopSession>,
|
||||
remote_desktop_session: Rc<PortalSession>,
|
||||
dpy: Rc<PortalDisplay>,
|
||||
surfaces: CopyHashMap<u32, Rc<SelectionGuiSurface>>,
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ impl SelectionGui {
|
|||
for surface in self.surfaces.lock().drain_values() {
|
||||
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);
|
||||
if upwards && s.guis.is_empty() {
|
||||
self.remote_desktop_session.kill();
|
||||
|
|
@ -99,7 +99,7 @@ impl OverlayWindowOwner for SelectionGuiSurface {
|
|||
}
|
||||
|
||||
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 {
|
||||
remote_desktop_session: ss.clone(),
|
||||
dpy: dpy.clone(),
|
||||
|
|
@ -130,7 +130,7 @@ impl ButtonOwner for StaticButton {
|
|||
match self.role {
|
||||
ButtonRole::Accept => {
|
||||
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,
|
||||
_ => return,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ mod screencast_gui;
|
|||
use {
|
||||
crate::{
|
||||
allocator::{AllocatorError, BufferObject, BufferUsage, BO_USE_RENDERING},
|
||||
dbus::{prelude::Variant, DbusObject, DictEntry, DynamicType, PendingReply},
|
||||
dbus::{prelude::Variant, DbusObject, DictEntry, PendingReply},
|
||||
format::{Format, XRGB8888},
|
||||
ifs::{jay_compositor::GET_TOPLEVEL_SINCE, jay_screencast::CLIENT_BUFFERS_SINCE},
|
||||
pipewire::{
|
||||
|
|
@ -21,14 +21,15 @@ use {
|
|||
},
|
||||
portal::{
|
||||
ptl_display::{PortalDisplay, PortalDisplayId, PortalOutput},
|
||||
ptl_remote_desktop::RemoteDesktopPhase,
|
||||
ptl_screencast::screencast_gui::SelectionGui,
|
||||
ptl_session::{PortalSession, PortalSessionReply},
|
||||
PortalState, PORTAL_SUCCESS,
|
||||
},
|
||||
utils::{
|
||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
hash_map_ext::HashMapExt,
|
||||
opaque::Opaque,
|
||||
},
|
||||
video::{dmabuf::DmaBuf, Modifier, LINEAR_MODIFIER},
|
||||
|
|
@ -40,7 +41,7 @@ use {
|
|||
CreateSession, CreateSessionReply, SelectSources, SelectSourcesReply, Start,
|
||||
StartReply,
|
||||
},
|
||||
session::{CloseReply as SessionCloseReply, Closed},
|
||||
session::CloseReply as SessionCloseReply,
|
||||
},
|
||||
},
|
||||
wl_usr::usr_ifs::{
|
||||
|
|
@ -66,16 +67,6 @@ use {
|
|||
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)]
|
||||
pub enum ScreencastPhase {
|
||||
Init,
|
||||
|
|
@ -96,9 +87,8 @@ pub struct SourcesSelectedScreencast {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct SelectingScreencastCore {
|
||||
pub session: Rc<ScreencastSession>,
|
||||
pub session: Rc<PortalSession>,
|
||||
pub request_obj: Rc<DbusObject>,
|
||||
pub reply: Rc<PendingReply<StartReply<'static>>>,
|
||||
}
|
||||
|
||||
pub struct SelectingScreencast {
|
||||
|
|
@ -121,9 +111,8 @@ pub struct SelectingWorkspaceScreencast {
|
|||
}
|
||||
|
||||
pub struct StartingScreencast {
|
||||
pub session: Rc<ScreencastSession>,
|
||||
pub session: Rc<PortalSession>,
|
||||
pub _request_obj: Rc<DbusObject>,
|
||||
pub reply: Rc<PendingReply<StartReply<'static>>>,
|
||||
pub node: Rc<PwClientNode>,
|
||||
pub dpy: Rc<PortalDisplay>,
|
||||
pub target: ScreencastTarget,
|
||||
|
|
@ -136,21 +125,21 @@ pub enum ScreencastTarget {
|
|||
}
|
||||
|
||||
pub struct StartedScreencast {
|
||||
session: Rc<ScreencastSession>,
|
||||
node: Rc<PwClientNode>,
|
||||
port: Rc<PwClientNodePort>,
|
||||
buffer_objects: RefCell<Vec<Rc<dyn BufferObject>>>,
|
||||
buffers: RefCell<Vec<DmaBuf>>,
|
||||
pending_buffers: RefCell<Vec<Rc<UsrLinuxBufferParams>>>,
|
||||
buffers_valid: Cell<bool>,
|
||||
dpy: Rc<PortalDisplay>,
|
||||
jay_screencast: Rc<UsrJayScreencast>,
|
||||
port_buffer_valid: Cell<bool>,
|
||||
fixated: Cell<bool>,
|
||||
format: Cell<&'static Format>,
|
||||
modifier: Cell<Modifier>,
|
||||
width: Cell<i32>,
|
||||
height: Cell<i32>,
|
||||
pub session: Rc<PortalSession>,
|
||||
pub node: Rc<PwClientNode>,
|
||||
pub port: Rc<PwClientNodePort>,
|
||||
pub buffer_objects: RefCell<Vec<Rc<dyn BufferObject>>>,
|
||||
pub buffers: RefCell<Vec<DmaBuf>>,
|
||||
pub pending_buffers: RefCell<Vec<Rc<UsrLinuxBufferParams>>>,
|
||||
pub buffers_valid: Cell<bool>,
|
||||
pub dpy: Rc<PortalDisplay>,
|
||||
pub jay_screencast: Rc<UsrJayScreencast>,
|
||||
pub port_buffer_valid: Cell<bool>,
|
||||
pub fixated: Cell<bool>,
|
||||
pub format: Cell<&'static Format>,
|
||||
pub modifier: Cell<Modifier>,
|
||||
pub width: Cell<i32>,
|
||||
pub height: Cell<i32>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
|
@ -170,33 +159,8 @@ bitflags! {
|
|||
|
||||
impl PwClientNodeOwner for StartingScreencast {
|
||||
fn bound_id(&self, node_id: u32) {
|
||||
{
|
||||
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 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),
|
||||
});
|
||||
}
|
||||
self.session
|
||||
.send_start_reply(Some(node_id), create_restore_data(&self.dpy, &self.target));
|
||||
let mut supported_formats = PwClientNodePortSupportedFormats {
|
||||
media_type: Some(SPA_MEDIA_TYPE_video),
|
||||
media_sub_type: Some(SPA_MEDIA_SUBTYPE_raw),
|
||||
|
|
@ -271,7 +235,7 @@ impl PwClientNodeOwner for StartingScreencast {
|
|||
height: Cell::new(1),
|
||||
});
|
||||
self.session
|
||||
.phase
|
||||
.sc_phase
|
||||
.set(ScreencastPhase::Started(started.clone()));
|
||||
started.jay_screencast.owner.set(Some(started.clone()));
|
||||
self.node.owner.set(Some(started.clone()));
|
||||
|
|
@ -424,7 +388,11 @@ impl StartedScreencast {
|
|||
|
||||
impl SelectingScreencastCore {
|
||||
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()),
|
||||
("node.name".to_string(), "jay-desktop-portal".to_string()),
|
||||
("node.driver".to_string(), "true".to_string()),
|
||||
|
|
@ -432,76 +400,28 @@ impl SelectingScreencastCore {
|
|||
let starting = Rc::new(StartingScreencast {
|
||||
session: self.session.clone(),
|
||||
_request_obj: self.request_obj.clone(),
|
||||
reply: self.reply.clone(),
|
||||
node,
|
||||
dpy: dpy.clone(),
|
||||
target,
|
||||
});
|
||||
self.session
|
||||
.phase
|
||||
.sc_phase
|
||||
.set(ScreencastPhase::Starting(starting.clone()));
|
||||
starting.node.owner.set(Some(starting.clone()));
|
||||
dpy.screencasts.set(
|
||||
dpy.sessions.set(
|
||||
self.session.session_obj.path().to_owned(),
|
||||
self.session.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl ScreencastSession {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PortalSession {
|
||||
fn dbus_select_sources(
|
||||
self: &Rc<Self>,
|
||||
req: SelectSources,
|
||||
reply: PendingReply<SelectSourcesReply<'static>>,
|
||||
) {
|
||||
match self.phase.get() {
|
||||
match self.sc_phase.get() {
|
||||
ScreencastPhase::Init => {}
|
||||
_ => {
|
||||
self.kill();
|
||||
|
|
@ -509,7 +429,7 @@ impl ScreencastSession {
|
|||
return;
|
||||
}
|
||||
}
|
||||
self.phase.set(ScreencastPhase::SourcesSelected(Rc::new(
|
||||
self.sc_phase.set(ScreencastPhase::SourcesSelected(Rc::new(
|
||||
SourcesSelectedScreencast {
|
||||
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>>) {
|
||||
let restore_data = match self.phase.get() {
|
||||
fn dbus_start_screencast(
|
||||
self: &Rc<Self>,
|
||||
req: Start<'_>,
|
||||
reply: PendingReply<StartReply<'static>>,
|
||||
) {
|
||||
let restore_data = match self.sc_phase.get() {
|
||||
ScreencastPhase::SourcesSelected(s) => s.restore_data.take(),
|
||||
_ => {
|
||||
self.kill();
|
||||
|
|
@ -547,14 +471,14 @@ impl ScreencastSession {
|
|||
}
|
||||
});
|
||||
}
|
||||
let reply = Rc::new(reply);
|
||||
self.restore(&request_obj, &reply, restore_data, None);
|
||||
self.start_reply
|
||||
.set(Some(PortalSessionReply::ScreenCast(reply)));
|
||||
self.screencast_restore(&request_obj, restore_data, None);
|
||||
}
|
||||
|
||||
fn start_interactive_selection(
|
||||
self: &Rc<Self>,
|
||||
request_obj: &Rc<DbusObject>,
|
||||
reply: &Rc<PendingReply<StartReply<'static>>>,
|
||||
restore_data: Option<RestoreData>,
|
||||
) {
|
||||
let guis = CopyHashMap::new();
|
||||
|
|
@ -565,42 +489,39 @@ impl ScreencastSession {
|
|||
}
|
||||
if guis.is_empty() {
|
||||
self.kill();
|
||||
reply.err("There are no running displays");
|
||||
self.reply_err("There are no running displays");
|
||||
return;
|
||||
}
|
||||
self.phase
|
||||
self.sc_phase
|
||||
.set(ScreencastPhase::Selecting(Rc::new(SelectingScreencast {
|
||||
core: SelectingScreencastCore {
|
||||
session: self.clone(),
|
||||
request_obj: request_obj.clone(),
|
||||
reply: reply.clone(),
|
||||
},
|
||||
guis,
|
||||
restore_data: Cell::new(restore_data),
|
||||
})));
|
||||
}
|
||||
|
||||
fn restore(
|
||||
pub fn screencast_restore(
|
||||
self: &Rc<Self>,
|
||||
request_obj: &Rc<DbusObject>,
|
||||
reply: &Rc<PendingReply<StartReply<'static>>>,
|
||||
restore_data: Option<Result<RestoreData, RestoreError>>,
|
||||
display: Option<Rc<PortalDisplay>>,
|
||||
) {
|
||||
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));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.start_interactive_selection(&request_obj, &reply, None);
|
||||
self.start_interactive_selection(&request_obj, None);
|
||||
}
|
||||
|
||||
fn try_restore(
|
||||
self: &Rc<Self>,
|
||||
request_obj: &Rc<DbusObject>,
|
||||
reply: &Rc<PendingReply<StartReply<'static>>>,
|
||||
restore_data: Result<RestoreData, RestoreError>,
|
||||
display: Option<Rc<PortalDisplay>>,
|
||||
) -> Result<(), RestoreError> {
|
||||
|
|
@ -623,7 +544,7 @@ impl ScreencastSession {
|
|||
} else if self.state.displays.len() == 1 {
|
||||
self.state.displays.lock().values().next().unwrap().clone()
|
||||
} else {
|
||||
self.start_interactive_selection(&request_obj, &reply, Some(rd));
|
||||
self.start_interactive_selection(&request_obj, Some(rd));
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
|
@ -633,7 +554,6 @@ impl ScreencastSession {
|
|||
SelectingScreencastCore {
|
||||
session: self.clone(),
|
||||
request_obj: request_obj.clone(),
|
||||
reply: reply.clone(),
|
||||
}
|
||||
.starting(&dpy, target);
|
||||
};
|
||||
|
|
@ -674,14 +594,14 @@ impl ScreencastSession {
|
|||
core: SelectingScreencastCore {
|
||||
session: self.clone(),
|
||||
request_obj: request_obj.clone(),
|
||||
reply: reply.clone(),
|
||||
},
|
||||
dpy: dpy.clone(),
|
||||
selector: selector.clone(),
|
||||
restoring: true,
|
||||
});
|
||||
selector.owner.set(Some(selecting.clone()));
|
||||
self.phase.set(ScreencastPhase::SelectingWindow(selecting));
|
||||
self.sc_phase
|
||||
.set(ScreencastPhase::SelectingWindow(selecting));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -833,7 +753,7 @@ fn dbus_create_session(
|
|||
reply: PendingReply<CreateSessionReply<'static>>,
|
||||
) {
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
|
@ -844,13 +764,15 @@ fn dbus_create_session(
|
|||
return;
|
||||
}
|
||||
};
|
||||
let session = Rc::new(ScreencastSession {
|
||||
let session = Rc::new(PortalSession {
|
||||
_id: state.id(),
|
||||
state: state.clone(),
|
||||
pw_con: pw_con.clone(),
|
||||
pw_con: Some(pw_con.clone()),
|
||||
app: req.app_id.to_string(),
|
||||
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::*;
|
||||
|
|
@ -862,7 +784,7 @@ fn dbus_create_session(
|
|||
session.session_obj.set_property::<version>(Variant::U32(4));
|
||||
}
|
||||
state
|
||||
.screencasts
|
||||
.sessions
|
||||
.set(req.session_handle.0.to_string(), session);
|
||||
reply.ok(&CreateSessionReply {
|
||||
response: PORTAL_SUCCESS,
|
||||
|
|
@ -882,7 +804,7 @@ fn dbus_select_sources(
|
|||
|
||||
fn dbus_start(state: &Rc<PortalState>, req: Start, reply: PendingReply<StartReply<'static>>) {
|
||||
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>,
|
||||
reply: &PendingReply<T>,
|
||||
handle: &str,
|
||||
) -> Option<Rc<ScreencastSession>> {
|
||||
let res = state.screencasts.get(handle);
|
||||
) -> Option<Rc<PortalSession>> {
|
||||
let res = state.sessions.get(handle);
|
||||
if res.is_none() {
|
||||
let msg = format!("Screencast session `{}` does not exist", handle);
|
||||
reply.err(&msg);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use {
|
|||
portal::{
|
||||
ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
||||
ptl_screencast::{
|
||||
ScreencastPhase, ScreencastSession, ScreencastTarget, SelectingWindowScreencast,
|
||||
PortalSession, ScreencastPhase, ScreencastTarget, SelectingWindowScreencast,
|
||||
SelectingWorkspaceScreencast,
|
||||
},
|
||||
ptr_gui::{
|
||||
|
|
@ -27,7 +27,7 @@ const H_MARGIN: f32 = 30.0;
|
|||
const V_MARGIN: f32 = 20.0;
|
||||
|
||||
pub struct SelectionGui {
|
||||
screencast_session: Rc<ScreencastSession>,
|
||||
screencast_session: Rc<PortalSession>,
|
||||
dpy: Rc<PortalDisplay>,
|
||||
surfaces: CopyHashMap<u32, Rc<SelectionGuiSurface>>,
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ impl SelectionGui {
|
|||
for surface in self.surfaces.lock().drain_values() {
|
||||
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);
|
||||
if upwards && s.guis.is_empty() {
|
||||
self.screencast_session.kill();
|
||||
|
|
@ -135,7 +135,7 @@ impl OverlayWindowOwner for SelectionGuiSurface {
|
|||
}
|
||||
|
||||
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 {
|
||||
screencast_session: ss.clone(),
|
||||
dpy: dpy.clone(),
|
||||
|
|
@ -169,7 +169,7 @@ impl ButtonOwner for StaticButton {
|
|||
| ButtonRole::SelectWorkspace
|
||||
| ButtonRole::SelectWindow => {
|
||||
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,
|
||||
_ => return,
|
||||
};
|
||||
|
|
@ -178,9 +178,8 @@ impl ButtonOwner for StaticButton {
|
|||
}
|
||||
let dpy = &self.surface.output.dpy;
|
||||
if self.role == ButtonRole::Restore {
|
||||
selecting.core.session.restore(
|
||||
selecting.core.session.screencast_restore(
|
||||
&selecting.core.request_obj,
|
||||
&selecting.core.reply,
|
||||
selecting.restore_data.take().map(Ok),
|
||||
Some(self.surface.gui.dpy.clone()),
|
||||
);
|
||||
|
|
@ -199,7 +198,7 @@ impl ButtonOwner for StaticButton {
|
|||
self.surface
|
||||
.gui
|
||||
.screencast_session
|
||||
.phase
|
||||
.sc_phase
|
||||
.set(ScreencastPhase::SelectingWorkspace(selecting));
|
||||
} else {
|
||||
let selector = dpy.jc.select_toplevel(&seat.wl);
|
||||
|
|
@ -213,7 +212,7 @@ impl ButtonOwner for StaticButton {
|
|||
self.surface
|
||||
.gui
|
||||
.screencast_session
|
||||
.phase
|
||||
.sc_phase
|
||||
.set(ScreencastPhase::SelectingWindow(selecting));
|
||||
}
|
||||
}
|
||||
|
|
@ -230,18 +229,16 @@ impl UsrJaySelectToplevelOwner for SelectingWindowScreencast {
|
|||
let Some(tl) = tl else {
|
||||
if self.restoring {
|
||||
log::warn!("Could not restore session because toplevel no longer exists");
|
||||
self.core.session.start_interactive_selection(
|
||||
&self.core.request_obj,
|
||||
&self.core.reply,
|
||||
None,
|
||||
);
|
||||
self.core
|
||||
.session
|
||||
.start_interactive_selection(&self.core.request_obj, None);
|
||||
return;
|
||||
}
|
||||
log::info!("User has aborted the selection");
|
||||
self.core.session.kill();
|
||||
return;
|
||||
};
|
||||
match self.core.session.phase.get() {
|
||||
match self.core.session.sc_phase.get() {
|
||||
ScreencastPhase::SelectingWindow(s) => {
|
||||
self.dpy.con.remove_obj(&*s.selector);
|
||||
}
|
||||
|
|
@ -263,7 +260,7 @@ impl UsrJaySelectWorkspaceOwner for SelectingWorkspaceScreencast {
|
|||
self.core.session.kill();
|
||||
return;
|
||||
};
|
||||
match self.core.session.phase.get() {
|
||||
match self.core.session.sc_phase.get() {
|
||||
ScreencastPhase::SelectingWorkspace(s) => {
|
||||
self.dpy.con.remove_obj(&*s.selector);
|
||||
}
|
||||
|
|
|
|||
162
src/portal/ptl_session.rs
Normal file
162
src/portal/ptl_session.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue