portal: implement RemoteDesktop portal
This commit is contained in:
parent
40e87f8f91
commit
665127e6c0
22 changed files with 994 additions and 35 deletions
|
|
@ -1,3 +1,4 @@
|
|||
[preferred]
|
||||
default=gtk
|
||||
org.freedesktop.impl.portal.ScreenCast=jay
|
||||
org.freedesktop.impl.portal.RemoteDesktop=jay
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.desktop.jay
|
||||
Interfaces=org.freedesktop.impl.portal.ScreenCast;
|
||||
Interfaces=org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
- Add support for tearing.
|
||||
- Add support for touch input.
|
||||
- Add support for libei.
|
||||
- Add support for RemoteDesktop portal.
|
||||
|
||||
# 1.4.0 (2024-07-07)
|
||||
|
||||
|
|
|
|||
|
|
@ -146,8 +146,7 @@ async fn accept(fd: Rc<OwnedFd>, state: Rc<State>) {
|
|||
break;
|
||||
}
|
||||
};
|
||||
let id = state.clients.id();
|
||||
if let Err(e) = state.ei_clients.spawn(id, &state, fd) {
|
||||
if let Err(e) = state.ei_clients.spawn(&state, fd) {
|
||||
log::error!("Could not spawn a client: {}", ErrorFmt(e));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use {
|
|||
asyncevent::AsyncEvent,
|
||||
buffd::{EiMsgFormatter, EiMsgParser, EiMsgParserError, OutBufferSwapchain},
|
||||
clonecell::CloneCell,
|
||||
debug_fn::debug_fn,
|
||||
errorfmt::ErrorFmt,
|
||||
numcell::NumCell,
|
||||
pid_info::{get_pid_info, get_socket_creds, PidInfo},
|
||||
|
|
@ -31,7 +32,7 @@ use {
|
|||
ops::DerefMut,
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::{c, OwnedFd},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
mod ei_error;
|
||||
|
|
@ -64,26 +65,21 @@ impl EiClients {
|
|||
mem::take(self.shutdown_clients.borrow_mut().deref_mut());
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
&self,
|
||||
id: ClientId,
|
||||
global: &Rc<State>,
|
||||
socket: Rc<OwnedFd>,
|
||||
) -> Result<(), EiClientError> {
|
||||
pub fn spawn(&self, global: &Rc<State>, socket: Rc<OwnedFd>) -> Result<(), EiClientError> {
|
||||
let Some((uid, pid)) = get_socket_creds(&socket) else {
|
||||
return Ok(());
|
||||
};
|
||||
self.spawn2(id, global, socket, uid, pid)?;
|
||||
let pid_info = get_pid_info(uid, pid);
|
||||
self.spawn2(global, socket, Some(pid_info), None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn spawn2(
|
||||
&self,
|
||||
id: ClientId,
|
||||
global: &Rc<State>,
|
||||
socket: Rc<OwnedFd>,
|
||||
uid: c::uid_t,
|
||||
pid: c::pid_t,
|
||||
pid_info: Option<PidInfo>,
|
||||
app_id: Option<String>,
|
||||
) -> Result<Rc<EiClient>, EiClientError> {
|
||||
let versions = EiInterfaceVersions {
|
||||
ei_button: EiInterfaceVersion::new(1),
|
||||
|
|
@ -100,7 +96,7 @@ impl EiClients {
|
|||
ei_touchscreen: EiInterfaceVersion::new(1),
|
||||
};
|
||||
let data = Rc::new(EiClient {
|
||||
id,
|
||||
id: global.clients.id(),
|
||||
state: global.clone(),
|
||||
context: Cell::new(EiContext::Receiver),
|
||||
connection: Default::default(),
|
||||
|
|
@ -111,10 +107,11 @@ impl EiClients {
|
|||
flush_request: Default::default(),
|
||||
shutdown: Default::default(),
|
||||
tracker: Default::default(),
|
||||
pid_info: get_pid_info(uid, pid),
|
||||
pid_info,
|
||||
disconnect_announced: Cell::new(false),
|
||||
versions,
|
||||
name: Default::default(),
|
||||
app_id,
|
||||
last_serial: Default::default(),
|
||||
});
|
||||
track!(data, data);
|
||||
|
|
@ -127,12 +124,17 @@ impl EiClients {
|
|||
data: data.clone(),
|
||||
};
|
||||
log::info!(
|
||||
"Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}",
|
||||
id,
|
||||
pid,
|
||||
uid,
|
||||
client.data.socket.raw(),
|
||||
data.pid_info.comm,
|
||||
"Client {} connected{:?}",
|
||||
data.id,
|
||||
debug_fn(|fmt| {
|
||||
if let Some(p) = &data.pid_info {
|
||||
write!(fmt, ", pid: {}, uid: {}, comm: {:?}", p.pid, p.uid, p.comm)?;
|
||||
}
|
||||
if let Some(app_id) = &data.app_id {
|
||||
write!(fmt, ", app-id: {app_id:?}")?;
|
||||
}
|
||||
Ok(())
|
||||
}),
|
||||
);
|
||||
self.clients.borrow_mut().insert(client.data.id, client);
|
||||
Ok(data)
|
||||
|
|
@ -199,10 +201,11 @@ pub struct EiClient {
|
|||
flush_request: AsyncEvent,
|
||||
shutdown: AsyncEvent,
|
||||
pub tracker: Tracker<EiClient>,
|
||||
pub pid_info: PidInfo,
|
||||
pub pid_info: Option<PidInfo>,
|
||||
pub disconnect_announced: Cell<bool>,
|
||||
pub versions: EiInterfaceVersions,
|
||||
pub name: RefCell<Option<String>>,
|
||||
pub app_id: Option<String>,
|
||||
pub last_serial: NumCell<u64>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ pub mod ext_session_lock_v1;
|
|||
pub mod ipc;
|
||||
pub mod jay_compositor;
|
||||
pub mod jay_damage_tracking;
|
||||
pub mod jay_ei_session;
|
||||
pub mod jay_ei_session_builder;
|
||||
pub mod jay_idle;
|
||||
pub mod jay_input;
|
||||
pub mod jay_log_file;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use {
|
|||
client::{Client, ClientCaps, ClientError, CAP_JAY_COMPOSITOR},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
jay_ei_session_builder::JayEiSessionBuilder,
|
||||
jay_idle::JayIdle,
|
||||
jay_input::JayInput,
|
||||
jay_log_file::JayLogFile,
|
||||
|
|
@ -30,6 +31,8 @@ use {
|
|||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub const CREATE_EI_SESSION_SINCE: Version = Version(5);
|
||||
|
||||
pub struct JayCompositorGlobal {
|
||||
name: GlobalName,
|
||||
}
|
||||
|
|
@ -66,7 +69,7 @@ impl Global for JayCompositorGlobal {
|
|||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
4
|
||||
5
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
@ -377,6 +380,19 @@ impl JayCompositorRequestHandler for JayCompositor {
|
|||
seat.global.select_workspace(selector);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_ei_session(&self, req: CreateEiSession, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let obj = Rc::new(JayEiSessionBuilder {
|
||||
id: req.id,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
app_id: Default::default(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
|
|||
81
src/ifs/jay_ei_session.rs
Normal file
81
src/ifs/jay_ei_session.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError, ClientId},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{
|
||||
jay_ei_session::{Created, Destroyed, Failed, JayEiSessionRequestHandler, Release},
|
||||
JayEiSessionId,
|
||||
},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct JayEiSession {
|
||||
pub id: JayEiSessionId,
|
||||
pub client: Rc<Client>,
|
||||
pub ei_client_id: Option<ClientId>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
impl JayEiSession {
|
||||
pub fn send_created(&self, fd: &Rc<OwnedFd>) {
|
||||
self.client.event(Created {
|
||||
self_id: self.id,
|
||||
fd: fd.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_failed(&self, reason: &str) {
|
||||
self.client.event(Failed {
|
||||
self_id: self.id,
|
||||
reason,
|
||||
});
|
||||
}
|
||||
|
||||
fn send_destroyed(&self) {
|
||||
self.client.event(Destroyed { self_id: self.id });
|
||||
}
|
||||
|
||||
fn kill(&self, send_destroyed: bool) {
|
||||
if let Some(id) = self.ei_client_id {
|
||||
self.client.state.ei_clients.shutdown(id);
|
||||
}
|
||||
if send_destroyed {
|
||||
self.send_destroyed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JayEiSessionRequestHandler for JayEiSession {
|
||||
type Error = JayEiSessionError;
|
||||
|
||||
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.kill(false);
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JayEiSession;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for JayEiSession {
|
||||
fn break_loops(&self) {
|
||||
self.kill(false);
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(JayEiSession);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayEiSessionError {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(JayEiSessionError, ClientError);
|
||||
97
src/ifs/jay_ei_session_builder.rs
Normal file
97
src/ifs/jay_ei_session_builder.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ei::ei_client::EiClientError,
|
||||
ifs::jay_ei_session::JayEiSession,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
utils::{errorfmt::ErrorFmt, oserror::OsError},
|
||||
wire::{
|
||||
jay_ei_session_builder::{Commit, JayEiSessionBuilderRequestHandler, SetAppId},
|
||||
JayEiSessionBuilderId,
|
||||
},
|
||||
},
|
||||
std::{cell::RefCell, rc::Rc},
|
||||
thiserror::Error,
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
pub struct JayEiSessionBuilder {
|
||||
pub id: JayEiSessionBuilderId,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub app_id: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
impl JayEiSessionBuilderRequestHandler for JayEiSessionBuilder {
|
||||
type Error = JayEiSessionBuilderError;
|
||||
|
||||
fn commit(&self, req: Commit, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
let app_id = self.app_id.borrow().clone();
|
||||
if app_id.is_none() {
|
||||
return Err(JayEiSessionBuilderError::NoAppId);
|
||||
}
|
||||
let res = (move || {
|
||||
let con = uapi::socketpair(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0);
|
||||
let (server, client) = match con {
|
||||
Ok(w) => w,
|
||||
Err(e) => return Err(JayEiSessionBuilderError::SocketPair(e.into())),
|
||||
};
|
||||
let ei_client_id = self
|
||||
.client
|
||||
.state
|
||||
.ei_clients
|
||||
.spawn2(&self.client.state, Rc::new(server), None, app_id)
|
||||
.map_err(JayEiSessionBuilderError::SpawnClient)?
|
||||
.id;
|
||||
Ok((ei_client_id, Rc::new(client)))
|
||||
})();
|
||||
let obj = Rc::new(JayEiSession {
|
||||
id: req.id,
|
||||
client: self.client.clone(),
|
||||
ei_client_id: res.as_ref().ok().map(|v| v.0),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
match res {
|
||||
Ok((_, fd)) => obj.send_created(&fd),
|
||||
Err(e) => {
|
||||
let e = format!("Could not spawn client: {}", ErrorFmt(e));
|
||||
log::error!("{}", e);
|
||||
obj.send_failed(&e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_app_id(&self, req: SetAppId<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
*self.app_id.borrow_mut() = Some(req.app_id.to_string());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JayEiSessionBuilder;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for JayEiSessionBuilder {}
|
||||
|
||||
simple_add_obj!(JayEiSessionBuilder);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayEiSessionBuilderError {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("Could not create a socketpair")]
|
||||
SocketPair(#[source] OsError),
|
||||
#[error("Could not spawn a new client")]
|
||||
SpawnClient(#[source] EiClientError),
|
||||
#[error("Commit called without app-id")]
|
||||
NoAppId,
|
||||
}
|
||||
efrom!(JayEiSessionBuilderError, ClientError);
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
mod ptl_display;
|
||||
mod ptl_remote_desktop;
|
||||
mod ptl_render_ctx;
|
||||
mod ptl_screencast;
|
||||
mod ptr_gui;
|
||||
|
|
@ -17,6 +18,7 @@ use {
|
|||
pipewire::pw_con::{PwConHolder, PwConOwner},
|
||||
portal::{
|
||||
ptl_display::{watch_displays, PortalDisplay, PortalDisplayId},
|
||||
ptl_remote_desktop::{add_remote_desktop_dbus_members, RemoteDesktopSession},
|
||||
ptl_render_ctx::PortalRenderCtx,
|
||||
ptl_screencast::{add_screencast_dbus_members, ScreencastSession},
|
||||
},
|
||||
|
|
@ -195,6 +197,7 @@ async fn run_async(
|
|||
displays: Default::default(),
|
||||
dbus,
|
||||
screencasts: Default::default(),
|
||||
remote_desktop_sessions: Default::default(),
|
||||
next_id: NumCell::new(1),
|
||||
render_ctxs: Default::default(),
|
||||
dma_buf_ids: Default::default(),
|
||||
|
|
@ -210,6 +213,7 @@ async fn run_async(
|
|||
if let Some(pw_con) = &pw_con {
|
||||
add_screencast_dbus_members(&state, &pw_con.con, &obj);
|
||||
}
|
||||
add_remote_desktop_dbus_members(&state, &obj);
|
||||
obj
|
||||
};
|
||||
watch_displays(state.clone()).await;
|
||||
|
|
@ -288,6 +292,7 @@ struct PortalState {
|
|||
displays: CopyHashMap<PortalDisplayId, Rc<PortalDisplay>>,
|
||||
dbus: Rc<DbusSocket>,
|
||||
screencasts: CopyHashMap<String, Rc<ScreencastSession>>,
|
||||
remote_desktop_sessions: CopyHashMap<String, Rc<RemoteDesktopSession>>,
|
||||
next_id: NumCell<u32>,
|
||||
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
|
||||
dma_buf_ids: Rc<DmaBufIds>,
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use {
|
|||
ifs::wl_seat::POINTER,
|
||||
object::Version,
|
||||
portal::{
|
||||
ptl_render_ctx::PortalRenderCtx, ptl_screencast::ScreencastSession,
|
||||
ptr_gui::WindowData, PortalState,
|
||||
ptl_remote_desktop::RemoteDesktopSession, ptl_render_ctx::PortalRenderCtx,
|
||||
ptl_screencast::ScreencastSession, ptr_gui::WindowData, PortalState,
|
||||
},
|
||||
utils::{
|
||||
bitflags::BitflagsExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
|
|
@ -74,6 +74,7 @@ pub struct PortalDisplay {
|
|||
|
||||
pub windows: CopyHashMap<WlSurfaceId, Rc<WindowData>>,
|
||||
pub screencasts: CopyHashMap<String, Rc<ScreencastSession>>,
|
||||
pub remote_desktop_sessions: CopyHashMap<String, Rc<RemoteDesktopSession>>,
|
||||
}
|
||||
|
||||
pub struct PortalOutput {
|
||||
|
|
@ -303,7 +304,7 @@ fn finish_display_connect(dpy: Rc<PortalDisplayPrelude>) {
|
|||
con: dpy.con.clone(),
|
||||
owner: Default::default(),
|
||||
caps: Default::default(),
|
||||
version: Version(version.min(4)),
|
||||
version: Version(version.min(5)),
|
||||
});
|
||||
dpy.con.add_object(jc.clone());
|
||||
dpy.registry.request_bind(name, version, jc.deref());
|
||||
|
|
@ -395,6 +396,7 @@ fn finish_display_connect(dpy: Rc<PortalDisplayPrelude>) {
|
|||
vp,
|
||||
windows: Default::default(),
|
||||
screencasts: Default::default(),
|
||||
remote_desktop_sessions: Default::default(),
|
||||
});
|
||||
|
||||
dpy.state.displays.set(dpy.id, dpy.clone());
|
||||
|
|
|
|||
384
src/portal/ptl_remote_desktop.rs
Normal file
384
src/portal/ptl_remote_desktop.rs
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
mod remote_desktop_gui;
|
||||
|
||||
use {
|
||||
crate::{
|
||||
dbus::{prelude::Variant, DbusObject, DictEntry, DynamicType, PendingReply, FALSE},
|
||||
ifs::jay_compositor::CREATE_EI_SESSION_SINCE,
|
||||
portal::{
|
||||
ptl_display::{PortalDisplay, PortalDisplayId},
|
||||
ptl_remote_desktop::remote_desktop_gui::SelectionGui,
|
||||
PortalState, PORTAL_SUCCESS,
|
||||
},
|
||||
utils::{
|
||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||
copyhashmap::CopyHashMap,
|
||||
hash_map_ext::HashMapExt,
|
||||
},
|
||||
wire_dbus::{
|
||||
org,
|
||||
org::freedesktop::impl_::portal::{
|
||||
remote_desktop::{
|
||||
ConnectToEIS, ConnectToEISReply, CreateSession, CreateSessionReply,
|
||||
SelectDevices, SelectDevicesReply, Start, StartReply,
|
||||
},
|
||||
session::{CloseReply as SessionCloseReply, Closed},
|
||||
},
|
||||
},
|
||||
wl_usr::usr_ifs::usr_jay_ei_session::{UsrJayEiSession, UsrJayEiSessionOwner},
|
||||
},
|
||||
std::{borrow::Cow, 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,
|
||||
DevicesSelected,
|
||||
Selecting(Rc<SelectingDisplay>),
|
||||
Starting(Rc<StartingRemoteDesktop>),
|
||||
Started(Rc<StartedRemoteDesktop>),
|
||||
Terminated,
|
||||
}
|
||||
|
||||
unsafe impl UnsafeCellCloneSafe for RemoteDesktopPhase {}
|
||||
|
||||
pub struct SelectingDisplay {
|
||||
pub session: Rc<RemoteDesktopSession>,
|
||||
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 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>>>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
DeviceTypes: u32;
|
||||
|
||||
KEYBOARD = 1,
|
||||
POINTER = 2,
|
||||
TOUCHSCREEN = 4,
|
||||
}
|
||||
|
||||
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(),
|
||||
ei_session: self.ei_session.clone(),
|
||||
ei_fd: Cell::new(Some(fd.clone())),
|
||||
});
|
||||
self.session
|
||||
.phase
|
||||
.set(RemoteDesktopPhase::Started(started.clone()));
|
||||
started.ei_session.owner.set(Some(started.clone()));
|
||||
}
|
||||
|
||||
fn failed(&self, reason: &str) {
|
||||
log::error!("Could not create session: {}", reason);
|
||||
self.reply.err(reason);
|
||||
self.session.kill();
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectingDisplay {
|
||||
pub fn starting(&self, dpy: &Rc<PortalDisplay>) {
|
||||
let builder = dpy.jc.create_ei_session();
|
||||
builder.set_app_id(&self.session.app);
|
||||
let ei_session = builder.commit();
|
||||
let starting = Rc::new(StartingRemoteDesktop {
|
||||
session: self.session.clone(),
|
||||
_request_obj: self.request_obj.clone(),
|
||||
reply: self.reply.clone(),
|
||||
dpy: dpy.clone(),
|
||||
ei_session,
|
||||
});
|
||||
self.session
|
||||
.phase
|
||||
.set(RemoteDesktopPhase::Starting(starting.clone()));
|
||||
starting.ei_session.owner.set(Some(starting.clone()));
|
||||
dpy.remote_desktop_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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dbus_select_devices(
|
||||
self: &Rc<Self>,
|
||||
_req: SelectDevices,
|
||||
reply: PendingReply<SelectDevicesReply<'static>>,
|
||||
) {
|
||||
match self.phase.get() {
|
||||
RemoteDesktopPhase::Init => {}
|
||||
_ => {
|
||||
self.kill();
|
||||
reply.err("Devices have already been selected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.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() {
|
||||
RemoteDesktopPhase::DevicesSelected => {}
|
||||
_ => {
|
||||
self.kill();
|
||||
reply.err("Session is not in the correct phase for starting");
|
||||
return;
|
||||
}
|
||||
}
|
||||
let request_obj = match self.state.dbus.add_object(req.handle.to_string()) {
|
||||
Ok(r) => r,
|
||||
Err(_) => {
|
||||
self.kill();
|
||||
reply.err("Request handle is not unique");
|
||||
return;
|
||||
}
|
||||
};
|
||||
{
|
||||
use org::freedesktop::impl_::portal::request::*;
|
||||
request_obj.add_method::<Close, _>({
|
||||
let slf = self.clone();
|
||||
move |_, pr| {
|
||||
slf.kill();
|
||||
pr.ok(&CloseReply);
|
||||
}
|
||||
});
|
||||
}
|
||||
let guis = CopyHashMap::new();
|
||||
for dpy in self.state.displays.lock().values() {
|
||||
if dpy.outputs.len() > 0 && dpy.jc.version >= CREATE_EI_SESSION_SINCE {
|
||||
guis.set(dpy.id, SelectionGui::new(self, dpy));
|
||||
}
|
||||
}
|
||||
if guis.is_empty() {
|
||||
self.kill();
|
||||
reply.err("There are no running displays");
|
||||
return;
|
||||
}
|
||||
self.phase
|
||||
.set(RemoteDesktopPhase::Selecting(Rc::new(SelectingDisplay {
|
||||
session: self.clone(),
|
||||
request_obj: Rc::new(request_obj),
|
||||
reply: Rc::new(reply),
|
||||
guis,
|
||||
})));
|
||||
}
|
||||
|
||||
fn dbus_connect_to_eis(
|
||||
self: &Rc<Self>,
|
||||
_req: ConnectToEIS,
|
||||
reply: PendingReply<ConnectToEISReply>,
|
||||
) {
|
||||
let RemoteDesktopPhase::Started(started) = self.phase.get() else {
|
||||
self.kill();
|
||||
reply.err("Sources have already been selected");
|
||||
return;
|
||||
};
|
||||
let Some(fd) = started.ei_fd.take() else {
|
||||
self.kill();
|
||||
reply.err("EI file descriptor has already been consumed");
|
||||
return;
|
||||
};
|
||||
reply.ok(&ConnectToEISReply { fd });
|
||||
}
|
||||
}
|
||||
|
||||
impl UsrJayEiSessionOwner for StartedRemoteDesktop {
|
||||
fn destroyed(&self) {
|
||||
self.session.kill();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_remote_desktop_dbus_members(state_: &Rc<PortalState>, object: &DbusObject) {
|
||||
use org::freedesktop::impl_::portal::remote_desktop::*;
|
||||
let state = state_.clone();
|
||||
object.add_method::<CreateSession, _>(move |req, pr| {
|
||||
dbus_create_session(&state, req, pr);
|
||||
});
|
||||
let state = state_.clone();
|
||||
object.add_method::<SelectDevices, _>(move |req, pr| {
|
||||
dbus_select_devices(&state, req, pr);
|
||||
});
|
||||
let state = state_.clone();
|
||||
object.add_method::<Start, _>(move |req, pr| {
|
||||
dbus_start(&state, req, pr);
|
||||
});
|
||||
let state = state_.clone();
|
||||
object.add_method::<ConnectToEIS, _>(move |req, pr| {
|
||||
dbus_connect_to_eis(&state, req, pr);
|
||||
});
|
||||
object.set_property::<AvailableDeviceTypes>(Variant::U32(DeviceTypes::all().0));
|
||||
object.set_property::<version>(Variant::U32(2));
|
||||
}
|
||||
|
||||
fn dbus_create_session(
|
||||
state: &Rc<PortalState>,
|
||||
req: CreateSession,
|
||||
reply: PendingReply<CreateSessionReply<'static>>,
|
||||
) {
|
||||
log::info!("Create remote desktop session {:#?}", req);
|
||||
if state
|
||||
.remote_desktop_sessions
|
||||
.contains(req.session_handle.0.deref())
|
||||
{
|
||||
reply.err("Session already exists");
|
||||
return;
|
||||
}
|
||||
let obj = match state.dbus.add_object(req.session_handle.0.to_string()) {
|
||||
Ok(obj) => obj,
|
||||
Err(_) => {
|
||||
reply.err("Session path is not unique");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let session = Rc::new(RemoteDesktopSession {
|
||||
_id: state.id(),
|
||||
state: state.clone(),
|
||||
app: req.app_id.to_string(),
|
||||
session_obj: obj,
|
||||
phase: CloneCell::new(RemoteDesktopPhase::Init),
|
||||
});
|
||||
{
|
||||
use org::freedesktop::impl_::portal::session::*;
|
||||
let ses = session.clone();
|
||||
session.session_obj.add_method::<Close, _>(move |_, pr| {
|
||||
ses.kill();
|
||||
pr.ok(&SessionCloseReply);
|
||||
});
|
||||
session.session_obj.set_property::<version>(Variant::U32(2));
|
||||
}
|
||||
state
|
||||
.remote_desktop_sessions
|
||||
.set(req.session_handle.0.to_string(), session);
|
||||
reply.ok(&CreateSessionReply {
|
||||
response: PORTAL_SUCCESS,
|
||||
results: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
fn dbus_select_devices(
|
||||
state: &Rc<PortalState>,
|
||||
req: SelectDevices,
|
||||
reply: PendingReply<SelectDevicesReply<'static>>,
|
||||
) {
|
||||
if let Some(s) = get_session(state, &reply, &req.session_handle.0) {
|
||||
s.dbus_select_devices(req, reply);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
fn dbus_connect_to_eis(
|
||||
state: &Rc<PortalState>,
|
||||
req: ConnectToEIS,
|
||||
reply: PendingReply<ConnectToEISReply>,
|
||||
) {
|
||||
if let Some(s) = get_session(state, &reply, &req.session_handle.0) {
|
||||
s.dbus_connect_to_eis(req, reply);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_session<T>(
|
||||
state: &Rc<PortalState>,
|
||||
reply: &PendingReply<T>,
|
||||
handle: &str,
|
||||
) -> Option<Rc<RemoteDesktopSession>> {
|
||||
let res = state.remote_desktop_sessions.get(handle);
|
||||
if res.is_none() {
|
||||
let msg = format!("Remote desktop session `{}` does not exist", handle);
|
||||
reply.err(&msg);
|
||||
}
|
||||
res
|
||||
}
|
||||
159
src/portal/ptl_remote_desktop/remote_desktop_gui.rs
Normal file
159
src/portal/ptl_remote_desktop/remote_desktop_gui.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::wl_seat::{wl_pointer::PRESSED, BTN_LEFT},
|
||||
portal::{
|
||||
ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
||||
ptl_remote_desktop::{RemoteDesktopPhase, RemoteDesktopSession},
|
||||
ptr_gui::{
|
||||
Align, Button, ButtonOwner, Flow, GuiElement, Label, Orientation, OverlayWindow,
|
||||
OverlayWindowOwner,
|
||||
},
|
||||
},
|
||||
theme::Color,
|
||||
utils::{copyhashmap::CopyHashMap, hash_map_ext::HashMapExt},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
const H_MARGIN: f32 = 30.0;
|
||||
const V_MARGIN: f32 = 20.0;
|
||||
|
||||
pub struct SelectionGui {
|
||||
remote_desktop_session: Rc<RemoteDesktopSession>,
|
||||
dpy: Rc<PortalDisplay>,
|
||||
surfaces: CopyHashMap<u32, Rc<SelectionGuiSurface>>,
|
||||
}
|
||||
|
||||
pub struct SelectionGuiSurface {
|
||||
gui: Rc<SelectionGui>,
|
||||
output: Rc<PortalOutput>,
|
||||
overlay: Rc<OverlayWindow>,
|
||||
}
|
||||
|
||||
struct StaticButton {
|
||||
surface: Rc<SelectionGuiSurface>,
|
||||
role: ButtonRole,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
enum ButtonRole {
|
||||
Accept,
|
||||
Reject,
|
||||
}
|
||||
|
||||
impl SelectionGui {
|
||||
pub fn kill(&self, upwards: bool) {
|
||||
for surface in self.surfaces.lock().drain_values() {
|
||||
surface.overlay.data.kill(false);
|
||||
}
|
||||
if let RemoteDesktopPhase::Selecting(s) = self.remote_desktop_session.phase.get() {
|
||||
s.guis.remove(&self.dpy.id);
|
||||
if upwards && s.guis.is_empty() {
|
||||
self.remote_desktop_session.kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_accept_gui(surface: &Rc<SelectionGuiSurface>) -> Rc<dyn GuiElement> {
|
||||
let app = &surface.gui.remote_desktop_session.app;
|
||||
let text = if app.is_empty() {
|
||||
format!("An application wants to generate/monitor input")
|
||||
} else {
|
||||
format!("`{}` wants to generate/monitor input", app)
|
||||
};
|
||||
let label = Rc::new(Label::default());
|
||||
*label.text.borrow_mut() = text;
|
||||
let accept_button = static_button(surface, ButtonRole::Accept, "Allow");
|
||||
let reject_button = static_button(surface, ButtonRole::Reject, "Reject");
|
||||
for button in [&accept_button, &reject_button] {
|
||||
button.border_color.set(Color::from_gray(100));
|
||||
button.border.set(2.0);
|
||||
button.padding.set(5.0);
|
||||
}
|
||||
accept_button.bg_color.set(Color::from_rgb(170, 200, 170));
|
||||
accept_button
|
||||
.bg_hover_color
|
||||
.set(Color::from_rgb(170, 255, 170));
|
||||
reject_button.bg_color.set(Color::from_rgb(200, 170, 170));
|
||||
reject_button
|
||||
.bg_hover_color
|
||||
.set(Color::from_rgb(255, 170, 170));
|
||||
let flow = Rc::new(Flow::default());
|
||||
flow.orientation.set(Orientation::Vertical);
|
||||
flow.cross_align.set(Align::Center);
|
||||
flow.in_margin.set(V_MARGIN);
|
||||
flow.cross_margin.set(H_MARGIN);
|
||||
*flow.elements.borrow_mut() = vec![label, accept_button, reject_button];
|
||||
flow
|
||||
}
|
||||
|
||||
impl OverlayWindowOwner for SelectionGuiSurface {
|
||||
fn kill(&self, upwards: bool) {
|
||||
self.gui.dpy.windows.remove(&self.overlay.data.surface.id);
|
||||
self.gui.surfaces.remove(&self.output.global_id);
|
||||
if upwards && self.gui.surfaces.is_empty() {
|
||||
self.gui.kill(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectionGui {
|
||||
pub fn new(ss: &Rc<RemoteDesktopSession>, dpy: &Rc<PortalDisplay>) -> Rc<Self> {
|
||||
let gui = Rc::new(SelectionGui {
|
||||
remote_desktop_session: ss.clone(),
|
||||
dpy: dpy.clone(),
|
||||
surfaces: Default::default(),
|
||||
});
|
||||
for output in dpy.outputs.lock().values() {
|
||||
let sgs = Rc::new(SelectionGuiSurface {
|
||||
gui: gui.clone(),
|
||||
output: output.clone(),
|
||||
overlay: OverlayWindow::new(output),
|
||||
});
|
||||
let element = create_accept_gui(&sgs);
|
||||
sgs.overlay.data.content.set(Some(element));
|
||||
gui.dpy
|
||||
.windows
|
||||
.set(sgs.overlay.data.surface.id, sgs.overlay.data.clone());
|
||||
gui.surfaces.set(output.global_id, sgs);
|
||||
}
|
||||
gui
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonOwner for StaticButton {
|
||||
fn button(&self, _seat: &PortalSeat, button: u32, state: u32) {
|
||||
if button != BTN_LEFT || state != PRESSED {
|
||||
return;
|
||||
}
|
||||
match self.role {
|
||||
ButtonRole::Accept => {
|
||||
log::info!("User has accepted the request");
|
||||
let selecting = match self.surface.gui.remote_desktop_session.phase.get() {
|
||||
RemoteDesktopPhase::Selecting(selecting) => selecting,
|
||||
_ => return,
|
||||
};
|
||||
for gui in selecting.guis.lock().drain_values() {
|
||||
gui.kill(false);
|
||||
}
|
||||
selecting.starting(&self.surface.output.dpy);
|
||||
}
|
||||
ButtonRole::Reject => {
|
||||
log::info!("User has rejected the remote desktop request");
|
||||
self.surface.gui.remote_desktop_session.kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn static_button(surface: &Rc<SelectionGuiSurface>, role: ButtonRole, text: &str) -> Rc<Button> {
|
||||
let button = Rc::new(Button::default());
|
||||
let slf = Rc::new(StaticButton {
|
||||
surface: surface.clone(),
|
||||
role,
|
||||
});
|
||||
button.owner.set(Some(slf));
|
||||
*button.text.borrow_mut() = text.to_string();
|
||||
button
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ use {
|
|||
};
|
||||
|
||||
pub struct PidInfo {
|
||||
pub _uid: c::uid_t,
|
||||
pub uid: c::uid_t,
|
||||
pub pid: c::pid_t,
|
||||
pub comm: String,
|
||||
}
|
||||
|
|
@ -18,11 +18,7 @@ pub fn get_pid_info(uid: c::uid_t, pid: c::pid_t) -> PidInfo {
|
|||
"Unknown".to_string()
|
||||
}
|
||||
};
|
||||
PidInfo {
|
||||
_uid: uid,
|
||||
pid,
|
||||
comm,
|
||||
}
|
||||
PidInfo { uid, pid, comm }
|
||||
}
|
||||
|
||||
pub fn get_socket_creds(socket: &OwnedFd) -> Option<(c::uid_t, c::pid_t)> {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
pub mod usr_jay_compositor;
|
||||
pub mod usr_jay_ei_session;
|
||||
pub mod usr_jay_ei_session_builder;
|
||||
pub mod usr_jay_output;
|
||||
pub mod usr_jay_pointer;
|
||||
pub mod usr_jay_render_ctx;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ use {
|
|||
wire::{jay_compositor::*, JayCompositorId},
|
||||
wl_usr::{
|
||||
usr_ifs::{
|
||||
usr_jay_output::UsrJayOutput, usr_jay_pointer::UsrJayPointer,
|
||||
usr_jay_render_ctx::UsrJayRenderCtx, usr_jay_screencast::UsrJayScreencast,
|
||||
usr_jay_ei_session_builder::UsrJayEiSessionBuilder, usr_jay_output::UsrJayOutput,
|
||||
usr_jay_pointer::UsrJayPointer, usr_jay_render_ctx::UsrJayRenderCtx,
|
||||
usr_jay_screencast::UsrJayScreencast,
|
||||
usr_jay_select_toplevel::UsrJaySelectToplevel,
|
||||
usr_jay_select_workspace::UsrJaySelectWorkspace,
|
||||
usr_jay_workspace_watcher::UsrJayWorkspaceWatcher, usr_wl_output::UsrWlOutput,
|
||||
|
|
@ -156,6 +157,20 @@ impl UsrJayCompositor {
|
|||
self.con.add_object(sc.clone());
|
||||
sc
|
||||
}
|
||||
|
||||
pub fn create_ei_session(&self) -> Rc<UsrJayEiSessionBuilder> {
|
||||
let obj = Rc::new(UsrJayEiSessionBuilder {
|
||||
id: self.con.id(),
|
||||
con: self.con.clone(),
|
||||
version: self.version,
|
||||
});
|
||||
self.con.request(CreateEiSession {
|
||||
self_id: self.id,
|
||||
id: obj.id,
|
||||
});
|
||||
self.con.add_object(obj.clone());
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
impl JayCompositorEventHandler for UsrJayCompositor {
|
||||
|
|
|
|||
72
src/wl_usr/usr_ifs/usr_jay_ei_session.rs
Normal file
72
src/wl_usr/usr_ifs/usr_jay_ei_session.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use {
|
||||
crate::{
|
||||
object::Version,
|
||||
utils::clonecell::CloneCell,
|
||||
wire::{
|
||||
jay_ei_session::{Created, Destroyed, Failed, JayEiSessionEventHandler, Release},
|
||||
JayEiSessionId,
|
||||
},
|
||||
wl_usr::{usr_object::UsrObject, UsrCon},
|
||||
},
|
||||
std::{convert::Infallible, rc::Rc},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct UsrJayEiSession {
|
||||
pub id: JayEiSessionId,
|
||||
pub con: Rc<UsrCon>,
|
||||
pub owner: CloneCell<Option<Rc<dyn UsrJayEiSessionOwner>>>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
pub trait UsrJayEiSessionOwner {
|
||||
fn destroyed(&self) {}
|
||||
|
||||
fn created(&self, fd: &Rc<OwnedFd>) {
|
||||
let _ = fd;
|
||||
}
|
||||
|
||||
fn failed(&self, reason: &str) {
|
||||
let _ = reason;
|
||||
}
|
||||
}
|
||||
|
||||
impl JayEiSessionEventHandler for UsrJayEiSession {
|
||||
type Error = Infallible;
|
||||
|
||||
fn destroyed(&self, _ev: Destroyed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if let Some(owner) = self.owner.get() {
|
||||
owner.destroyed();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn created(&self, ev: Created, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if let Some(owner) = self.owner.get() {
|
||||
owner.created(&ev.fd);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn failed(&self, ev: Failed<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if let Some(owner) = self.owner.get() {
|
||||
owner.failed(ev.reason);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
usr_object_base! {
|
||||
self = UsrJayEiSession = JayEiSession;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl UsrObject for UsrJayEiSession {
|
||||
fn destroy(&self) {
|
||||
self.con.request(Release { self_id: self.id });
|
||||
}
|
||||
|
||||
fn break_loops(&self) {
|
||||
self.owner.take();
|
||||
}
|
||||
}
|
||||
57
src/wl_usr/usr_ifs/usr_jay_ei_session_builder.rs
Normal file
57
src/wl_usr/usr_ifs/usr_jay_ei_session_builder.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
use {
|
||||
crate::{
|
||||
object::Version,
|
||||
wire::{
|
||||
jay_ei_session_builder::{Commit, JayEiSessionBuilderEventHandler, SetAppId},
|
||||
JayEiSessionBuilderId,
|
||||
},
|
||||
wl_usr::{usr_ifs::usr_jay_ei_session::UsrJayEiSession, usr_object::UsrObject, UsrCon},
|
||||
},
|
||||
std::{convert::Infallible, rc::Rc},
|
||||
};
|
||||
|
||||
pub struct UsrJayEiSessionBuilder {
|
||||
pub id: JayEiSessionBuilderId,
|
||||
pub con: Rc<UsrCon>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
impl UsrJayEiSessionBuilder {
|
||||
pub fn set_app_id(&self, app_id: &str) {
|
||||
self.con.request(SetAppId {
|
||||
self_id: self.id,
|
||||
app_id,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn commit(&self) -> Rc<UsrJayEiSession> {
|
||||
let obj = Rc::new(UsrJayEiSession {
|
||||
id: self.con.id(),
|
||||
con: self.con.clone(),
|
||||
owner: Default::default(),
|
||||
version: self.version,
|
||||
});
|
||||
self.con.add_object(obj.clone());
|
||||
self.con.request(Commit {
|
||||
self_id: self.id,
|
||||
id: obj.id,
|
||||
});
|
||||
self.con.remove_obj(self);
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
impl JayEiSessionBuilderEventHandler for UsrJayEiSessionBuilder {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
usr_object_base! {
|
||||
self = UsrJayEiSessionBuilder = JayEiSessionBuilder;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl UsrObject for UsrJayEiSessionBuilder {
|
||||
fn destroy(&self) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
41
wire-dbus/org.freedesktop.impl.portal.RemoteDesktop.txt
Normal file
41
wire-dbus/org.freedesktop.impl.portal.RemoteDesktop.txt
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
fn CreateSession(
|
||||
handle: object_path,
|
||||
session_handle: object_path,
|
||||
app_id: string,
|
||||
options: array(dict(string, variant)),
|
||||
) {
|
||||
response: u32,
|
||||
results: array(dict(string, variant)),
|
||||
}
|
||||
|
||||
fn SelectDevices(
|
||||
handle: object_path,
|
||||
session_handle: object_path,
|
||||
app_id: string,
|
||||
options: array(dict(string, variant)),
|
||||
) {
|
||||
response: u32,
|
||||
results: array(dict(string, variant)),
|
||||
}
|
||||
|
||||
fn Start(
|
||||
handle: object_path,
|
||||
session_handle: object_path,
|
||||
app_id: string,
|
||||
parent_window: string,
|
||||
options: array(dict(string, variant)),
|
||||
) {
|
||||
response: u32,
|
||||
results: array(dict(string, variant)),
|
||||
}
|
||||
|
||||
fn ConnectToEIS(
|
||||
session_handle: object_path,
|
||||
app_id: string,
|
||||
options: array(dict(string, variant)),
|
||||
) {
|
||||
fd: fd,
|
||||
}
|
||||
|
||||
prop AvailableDeviceTypes = u32
|
||||
prop version = u32
|
||||
|
|
@ -88,6 +88,10 @@ request select_workspace {
|
|||
seat: id(wl_seat),
|
||||
}
|
||||
|
||||
request create_ei_session (since = 5) {
|
||||
id: id(jay_ei_session_builder),
|
||||
}
|
||||
|
||||
# events
|
||||
|
||||
event client_id {
|
||||
|
|
|
|||
15
wire/jay_ei_session.txt
Normal file
15
wire/jay_ei_session.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
request release (since = 5) {
|
||||
|
||||
}
|
||||
|
||||
event destroyed (since = 5) {
|
||||
|
||||
}
|
||||
|
||||
event created (since = 5) {
|
||||
fd: fd,
|
||||
}
|
||||
|
||||
event failed (since = 5) {
|
||||
reason: str,
|
||||
}
|
||||
7
wire/jay_ei_session_builder.txt
Normal file
7
wire/jay_ei_session_builder.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
request commit (since = 5) {
|
||||
id: id(jay_ei_session),
|
||||
}
|
||||
|
||||
request set_app_id (since = 5) {
|
||||
app_id: str,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue