use { crate::{ dbus::{DbusObject, DictEntry, DynamicType, FALSE, PendingReply, prelude::Variant}, pipewire::pw_con::PwCon, portal::{ PORTAL_SUCCESS, PortalState, ptl_remote_desktop::{DeviceTypes, RemoteDesktopPhase}, ptl_screencast::{ScreencastPhase, ScreencastTarget}, }, 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, pub pw_con: Option>, pub app: String, pub session_obj: DbusObject, pub sc_phase: CloneCell, pub rd_phase: CloneCell, pub start_reply: Cell>, } pub enum PortalSessionReply { RemoteDesktop(PendingReply>), ScreenCast(PendingReply>), } 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, restore_data: Option>, mapping_id: Option<&str>, ) { 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(mapping_id) = mapping_id { variants.push(DictEntry { key: "mapping_id".into(), value: Variant::String(mapping_id.into()), }); } 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), } } } }