Feat: Wayland flatpak input support | Remote desktop portal (#6675)
* autogen portal code Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use remote desktop portal Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove clipboard portal in favour of #6586 Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove clipboard portal Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use select_devices for input capture Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove embedded cursor code as not being used | return session path for input capture Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * setup rdp input Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove simulate example Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * setup rdp input raw key events + mouse movements Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix rdp raw key input Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * refact rdp raw key inpuy & fix right meta key Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * refact and support rdp layout mode key input Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * support rdp mouse clicks Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * support rdp mouse scroll Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * support rdp key sequence input Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use rdp input only when uinput is not available Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * combine rdp input and get_capturables into a single rdp request Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * rdp fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * rdp fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix rdp caps lock Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * format pipewire.rs Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * format rdp_input.rs Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * revert #6628 as rdp request state is now managed (better solution) Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix rdp crash on arch kde Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * rdp_input.rs improvements Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * refact request_remote_desktop Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * improve unwraps Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove unwraps Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use session references instead of clones Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> --------- Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
This commit is contained in:
parent
dc8a70bb26
commit
445fe6e714
@ -151,7 +151,7 @@ pub fn is_cursor_embedded() -> bool {
|
||||
if is_x11() {
|
||||
x11::IS_CURSOR_EMBEDDED
|
||||
} else {
|
||||
wayland::is_cursor_embedded()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,34 +4,11 @@ use std::{io, sync::RwLock, time::Duration};
|
||||
|
||||
pub struct Capturer(Display, Box<dyn Recorder>, Vec<u8>);
|
||||
|
||||
static mut IS_CURSOR_EMBEDDED: Option<bool> = None;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref MAP_ERR: RwLock<Option<fn(err: String)-> io::Error>> = Default::default();
|
||||
}
|
||||
|
||||
pub fn is_cursor_embedded() -> bool {
|
||||
unsafe {
|
||||
if IS_CURSOR_EMBEDDED.is_none() {
|
||||
init_cursor_embedded();
|
||||
}
|
||||
IS_CURSOR_EMBEDDED.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn init_cursor_embedded() {
|
||||
use crate::common::wayland::pipewire::get_available_cursor_modes;
|
||||
match get_available_cursor_modes() {
|
||||
Ok(_modes) => {
|
||||
// IS_CURSOR_EMBEDDED = Some((_modes & 0x02) > 0);
|
||||
IS_CURSOR_EMBEDDED = Some(false)
|
||||
}
|
||||
Err(..) => {
|
||||
IS_CURSOR_EMBEDDED = Some(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_map_err(f: fn(err: String) -> io::Error) {
|
||||
*MAP_ERR.write().unwrap() = Some(f);
|
||||
}
|
||||
@ -82,7 +59,7 @@ impl Display {
|
||||
}
|
||||
|
||||
pub fn all() -> io::Result<Vec<Display>> {
|
||||
Ok(pipewire::get_capturables(is_cursor_embedded())
|
||||
Ok(pipewire::get_capturables()
|
||||
.map_err(map_err)?
|
||||
.drain(..)
|
||||
.map(|x| Display(x))
|
||||
|
@ -1,3 +1,5 @@
|
||||
pub mod capturable;
|
||||
pub mod pipewire;
|
||||
mod pipewire_dbus;
|
||||
mod screencast_portal;
|
||||
mod request_portal;
|
||||
pub mod remote_desktop_portal;
|
||||
|
@ -20,11 +20,24 @@ use hbb_common::config;
|
||||
|
||||
use super::capturable::PixelProvider;
|
||||
use super::capturable::{Capturable, Recorder};
|
||||
use super::pipewire_dbus::{OrgFreedesktopPortalRequestResponse, OrgFreedesktopPortalScreenCast};
|
||||
use super::remote_desktop_portal::OrgFreedesktopPortalRemoteDesktop as remote_desktop_portal;
|
||||
use super::request_portal::OrgFreedesktopPortalRequestResponse;
|
||||
use super::screencast_portal::OrgFreedesktopPortalScreenCast as screencast_portal;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref RDP_RESPONSE: Mutex<Option<RdpResponse>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
pub struct RdpResponse {
|
||||
pub conn: Arc<SyncConnection>,
|
||||
pub streams: Vec<PwStreamInfo>,
|
||||
pub fd: OwnedFd,
|
||||
pub session: dbus::Path<'static>,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct PwStreamInfo {
|
||||
path: u64,
|
||||
pub struct PwStreamInfo {
|
||||
pub path: u64,
|
||||
source_type: u64,
|
||||
position: (i32, i32),
|
||||
size: (usize, usize),
|
||||
@ -124,24 +137,26 @@ impl Capturable for PipeWireCapturable {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_res(capturable:PipeWireCapturable) -> Result<(usize, usize), Box<dyn Error>> {
|
||||
fn get_res(capturable: PipeWireCapturable) -> Result<(usize, usize), Box<dyn Error>> {
|
||||
let rec = PipeWireRecorder::new(capturable)?;
|
||||
if let Some(sample) = rec.appsink
|
||||
.try_pull_sample(gst::ClockTime::from_mseconds(300))
|
||||
{
|
||||
let cap = sample
|
||||
.get_caps()
|
||||
.ok_or("Failed get caps")?
|
||||
.get_structure(0)
|
||||
.ok_or("Failed to get structure")?;
|
||||
let w: i32 = cap.get_value("width")?.get_some()?;
|
||||
let h: i32 = cap.get_value("height")?.get_some()?;
|
||||
let w = w as usize;
|
||||
let h = h as usize;
|
||||
Ok((w,h))
|
||||
}
|
||||
else {
|
||||
Err(Box::new(GStreamerError("Error getting screen resolution".into())))
|
||||
if let Some(sample) = rec
|
||||
.appsink
|
||||
.try_pull_sample(gst::ClockTime::from_mseconds(300))
|
||||
{
|
||||
let cap = sample
|
||||
.get_caps()
|
||||
.ok_or("Failed get caps")?
|
||||
.get_structure(0)
|
||||
.ok_or("Failed to get structure")?;
|
||||
let w: i32 = cap.get_value("width")?.get_some()?;
|
||||
let h: i32 = cap.get_value("height")?.get_some()?;
|
||||
let w = w as usize;
|
||||
let h = h as usize;
|
||||
Ok((w, h))
|
||||
} else {
|
||||
Err(Box::new(GStreamerError(
|
||||
"Error getting screen resolution".into(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,7 +372,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn get_portal(conn: &SyncConnection) -> Proxy<&SyncConnection> {
|
||||
pub fn get_portal(conn: &SyncConnection) -> Proxy<&SyncConnection> {
|
||||
conn.with_proxy(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
@ -447,22 +462,22 @@ static mut INIT: bool = false;
|
||||
const RESTORE_TOKEN: &str = "restore_token";
|
||||
const RESTORE_TOKEN_CONF_KEY: &str = "wayland-restore-token";
|
||||
|
||||
const PORTAL_CURSOR_MODE_HIDDEN: u32 = 1;
|
||||
#[allow(dead_code)]
|
||||
const PORTAL_CURSOR_MODE_EMBEDDED: u32 = 2;
|
||||
#[allow(dead_code)]
|
||||
const PORTAL_CURSOR_MODE_METADATA: u32 = 4;
|
||||
|
||||
pub fn get_available_cursor_modes() -> Result<u32, dbus::Error> {
|
||||
let conn = SyncConnection::new_session()?;
|
||||
let portal = get_portal(&conn);
|
||||
portal.available_cursor_modes()
|
||||
}
|
||||
|
||||
// mostly inspired by https://gitlab.gnome.org/snippets/19
|
||||
fn request_screen_cast(
|
||||
capture_cursor: bool,
|
||||
) -> Result<(SyncConnection, OwnedFd, Vec<PwStreamInfo>), Box<dyn Error>> {
|
||||
// mostly inspired by https://gitlab.gnome.org/-/snippets/39
|
||||
pub fn request_remote_desktop() -> Result<
|
||||
(
|
||||
SyncConnection,
|
||||
OwnedFd,
|
||||
Vec<PwStreamInfo>,
|
||||
dbus::Path<'static>,
|
||||
),
|
||||
Box<dyn Error>,
|
||||
> {
|
||||
unsafe {
|
||||
if !INIT {
|
||||
gstreamer::init()?;
|
||||
@ -478,6 +493,8 @@ fn request_screen_cast(
|
||||
let streams_res = streams.clone();
|
||||
let failure = Arc::new(AtomicBool::new(false));
|
||||
let failure_res = failure.clone();
|
||||
let session: Arc<Mutex<Option<dbus::Path>>> = Arc::new(Mutex::new(None));
|
||||
let session_res = session.clone();
|
||||
args.insert(
|
||||
"session_handle_token".to_string(),
|
||||
Variant(Box::new("u1".to_string())),
|
||||
@ -492,125 +509,19 @@ fn request_screen_cast(
|
||||
// between the caller subscribing to the signal after receiving the reply for the method call and the signal getting emitted,
|
||||
// a convention for Request object paths has been established that allows
|
||||
// the caller to subscribe to the signal before making the method call.
|
||||
let path = portal.create_session(args)?;
|
||||
let path = remote_desktop_portal::create_session(&portal, args)?;
|
||||
handle_response(
|
||||
&conn,
|
||||
path,
|
||||
move |r: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
let mut args: PropMap = HashMap::new();
|
||||
if let Ok(version) = portal.version() {
|
||||
if version >= 4 {
|
||||
let restore_token = config::LocalConfig::get_option(RESTORE_TOKEN_CONF_KEY);
|
||||
if !restore_token.is_empty() {
|
||||
args.insert(RESTORE_TOKEN.to_string(), Variant(Box::new(restore_token)));
|
||||
}
|
||||
// persist_mode may be configured by the user.
|
||||
args.insert("persist_mode".to_string(), Variant(Box::new(2u32)));
|
||||
}
|
||||
}
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u2".to_string())),
|
||||
);
|
||||
// https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-method-org-freedesktop-portal-ScreenCast.SelectSources
|
||||
args.insert("multiple".into(), Variant(Box::new(true)));
|
||||
args.insert("types".into(), Variant(Box::new(1u32))); //| 2u32)));
|
||||
|
||||
let mut cursor_mode = 0u32;
|
||||
let mut available_cursor_modes = 0u32;
|
||||
if let Ok(modes) = portal.available_cursor_modes() {
|
||||
available_cursor_modes = modes;
|
||||
}
|
||||
// if capture_cursor {
|
||||
// cursor_mode = PORTAL_CURSOR_MODE_METADATA & available_cursor_modes;
|
||||
// }
|
||||
if cursor_mode == 0 {
|
||||
cursor_mode = PORTAL_CURSOR_MODE_HIDDEN & available_cursor_modes;
|
||||
}
|
||||
let plasma = std::env::var("DESKTOP_SESSION").map_or(false, |s| s.contains("plasma"));
|
||||
if plasma && capture_cursor {
|
||||
// Warn the user if capturing the cursor is tried on kde as this can crash
|
||||
// kwin_wayland and tear down the plasma desktop, see:
|
||||
// https://bugs.kde.org/show_bug.cgi?id=435042
|
||||
warn!("You are attempting to capture the cursor under KDE Plasma, this may crash your \
|
||||
desktop, see https://bugs.kde.org/show_bug.cgi?id=435042 for details! \
|
||||
You have been warned.");
|
||||
}
|
||||
if cursor_mode > 0 {
|
||||
args.insert("cursor_mode".into(), Variant(Box::new(cursor_mode)));
|
||||
}
|
||||
let session: dbus::Path = r
|
||||
.results
|
||||
.get("session_handle")
|
||||
.ok_or_else(|| {
|
||||
DBusError(format!(
|
||||
"Failed to obtain session_handle from response: {:?}",
|
||||
r
|
||||
))
|
||||
})?
|
||||
.as_str()
|
||||
.ok_or_else(|| DBusError("Failed to convert session_handle to string.".into()))?
|
||||
.to_string()
|
||||
.into();
|
||||
let path = portal.select_sources(session.clone(), args)?;
|
||||
let fd = fd.clone();
|
||||
let streams = streams.clone();
|
||||
let failure = failure.clone();
|
||||
let failure_out = failure.clone();
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
let mut args: PropMap = HashMap::new();
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u3".to_string())),
|
||||
);
|
||||
let path = portal.start(session.clone(), "", args)?;
|
||||
let session = session.clone();
|
||||
let fd = fd.clone();
|
||||
let streams = streams.clone();
|
||||
let failure = failure.clone();
|
||||
let failure_out = failure.clone();
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
move |r: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
if let Ok(version) = portal.version() {
|
||||
if version >= 4 {
|
||||
if let Some(restore_token) = r.results.get(RESTORE_TOKEN) {
|
||||
if let Some(restore_token) = restore_token.as_str() {
|
||||
config::LocalConfig::set_option(
|
||||
RESTORE_TOKEN_CONF_KEY.to_owned(),
|
||||
restore_token.to_owned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
streams
|
||||
.clone()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.append(&mut streams_from_response(r));
|
||||
fd.clone().lock().unwrap().replace(
|
||||
portal.open_pipe_wire_remote(session.clone(), HashMap::new())?,
|
||||
);
|
||||
Ok(())
|
||||
},
|
||||
failure_out,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
failure_out,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
on_create_session_response(
|
||||
fd.clone(),
|
||||
streams.clone(),
|
||||
session.clone(),
|
||||
failure.clone(),
|
||||
),
|
||||
failure_res.clone(),
|
||||
)?;
|
||||
|
||||
// wait 3 minutes for user interaction
|
||||
for _ in 0..1800 {
|
||||
conn.process(Duration::from_millis(100))?;
|
||||
@ -625,9 +536,13 @@ fn request_screen_cast(
|
||||
}
|
||||
let fd_res = fd_res.lock().unwrap();
|
||||
let streams_res = streams_res.lock().unwrap();
|
||||
let session_res = session_res.lock().unwrap();
|
||||
|
||||
if let Some(fd_res) = fd_res.clone() {
|
||||
if !streams_res.is_empty() {
|
||||
return Ok((conn, fd_res, streams_res.clone()));
|
||||
if let Some(session) = session_res.clone() {
|
||||
if !streams_res.is_empty() {
|
||||
return Ok((conn, fd_res, streams_res.clone(), session));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Box::new(DBusError(
|
||||
@ -635,11 +550,209 @@ fn request_screen_cast(
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn get_capturables(capture_cursor: bool) -> Result<Vec<PipeWireCapturable>, Box<dyn Error>> {
|
||||
let (conn, fd, streams) = request_screen_cast(capture_cursor)?;
|
||||
let conn = Arc::new(conn);
|
||||
Ok(streams
|
||||
fn on_create_session_response(
|
||||
fd: Arc<Mutex<Option<OwnedFd>>>,
|
||||
streams: Arc<Mutex<Vec<PwStreamInfo>>>,
|
||||
session: Arc<Mutex<Option<dbus::Path<'static>>>>,
|
||||
failure: Arc<AtomicBool>,
|
||||
) -> impl Fn(
|
||||
OrgFreedesktopPortalRequestResponse,
|
||||
&SyncConnection,
|
||||
&dbus::Message,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
move |r: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
let mut args: PropMap = HashMap::new();
|
||||
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u2".to_string())),
|
||||
);
|
||||
args.insert("types".to_string(), Variant(Box::new(7u32)));
|
||||
|
||||
let ses: dbus::Path = r
|
||||
.results
|
||||
.get("session_handle")
|
||||
.ok_or_else(|| {
|
||||
DBusError(format!(
|
||||
"Failed to obtain session_handle from response: {:?}",
|
||||
r
|
||||
))
|
||||
})?
|
||||
.as_str()
|
||||
.ok_or_else(|| DBusError("Failed to convert session_handle to string.".into()))?
|
||||
.to_string()
|
||||
.into();
|
||||
|
||||
let mut session = match session.lock() {
|
||||
Ok(session) => session,
|
||||
Err(_) => {
|
||||
return Err(Box::new(DBusError(
|
||||
"Failed to lock session.".into(),
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
session.replace(ses.clone());
|
||||
|
||||
let path = portal.select_devices(ses.clone(), args)?;
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
on_select_devices_response(fd.clone(), streams.clone(), failure.clone(), ses),
|
||||
failure.clone(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn on_select_devices_response(
|
||||
fd: Arc<Mutex<Option<OwnedFd>>>,
|
||||
streams: Arc<Mutex<Vec<PwStreamInfo>>>,
|
||||
failure: Arc<AtomicBool>,
|
||||
session: dbus::Path<'static>,
|
||||
) -> impl Fn(
|
||||
OrgFreedesktopPortalRequestResponse,
|
||||
&SyncConnection,
|
||||
&dbus::Message,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
let mut args: PropMap = HashMap::new();
|
||||
if let Ok(version) = remote_desktop_portal::version(&portal) {
|
||||
if version >= 4 {
|
||||
let restore_token = config::LocalConfig::get_option(RESTORE_TOKEN_CONF_KEY);
|
||||
if !restore_token.is_empty() {
|
||||
args.insert(RESTORE_TOKEN.to_string(), Variant(Box::new(restore_token)));
|
||||
}
|
||||
// persist_mode may be configured by the user.
|
||||
args.insert("persist_mode".to_string(), Variant(Box::new(2u32)));
|
||||
}
|
||||
}
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u3".to_string())),
|
||||
);
|
||||
// https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-method-org-freedesktop-portal-ScreenCast.SelectSources
|
||||
args.insert("multiple".into(), Variant(Box::new(true)));
|
||||
args.insert("types".into(), Variant(Box::new(1u32))); //| 2u32)));
|
||||
|
||||
let session = session.clone();
|
||||
let path = portal.select_sources(session.clone(), args)?;
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
on_select_sources_response(
|
||||
fd.clone(),
|
||||
streams.clone(),
|
||||
failure.clone(),
|
||||
session.clone(),
|
||||
),
|
||||
failure.clone(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn on_select_sources_response(
|
||||
fd: Arc<Mutex<Option<OwnedFd>>>,
|
||||
streams: Arc<Mutex<Vec<PwStreamInfo>>>,
|
||||
failure: Arc<AtomicBool>,
|
||||
session: dbus::Path<'static>,
|
||||
) -> impl Fn(
|
||||
OrgFreedesktopPortalRequestResponse,
|
||||
&SyncConnection,
|
||||
&dbus::Message,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
move |_: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
let mut args: PropMap = HashMap::new();
|
||||
args.insert(
|
||||
"handle_token".to_string(),
|
||||
Variant(Box::new("u4".to_string())),
|
||||
);
|
||||
let path = remote_desktop_portal::start(&portal, session.clone(), "", args)?;
|
||||
handle_response(
|
||||
c,
|
||||
path,
|
||||
on_start_response(fd.clone(), streams.clone(), session.clone()),
|
||||
failure.clone(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn on_start_response(
|
||||
fd: Arc<Mutex<Option<OwnedFd>>>,
|
||||
streams: Arc<Mutex<Vec<PwStreamInfo>>>,
|
||||
session: dbus::Path<'static>,
|
||||
) -> impl Fn(
|
||||
OrgFreedesktopPortalRequestResponse,
|
||||
&SyncConnection,
|
||||
&dbus::Message,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
move |r: OrgFreedesktopPortalRequestResponse, c, _| {
|
||||
let portal = get_portal(c);
|
||||
if let Ok(version) = remote_desktop_portal::version(&portal) {
|
||||
if version >= 4 {
|
||||
if let Some(restore_token) = r.results.get(RESTORE_TOKEN) {
|
||||
if let Some(restore_token) = restore_token.as_str() {
|
||||
config::LocalConfig::set_option(
|
||||
RESTORE_TOKEN_CONF_KEY.to_owned(),
|
||||
restore_token.to_owned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
streams
|
||||
.clone()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.append(&mut streams_from_response(r));
|
||||
fd.clone()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.replace(portal.open_pipe_wire_remote(session.clone(), HashMap::new())?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_capturables() -> Result<Vec<PipeWireCapturable>, Box<dyn Error>> {
|
||||
let mut rdp_connection = match RDP_RESPONSE.lock() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return Err(Box::new(err)),
|
||||
};
|
||||
|
||||
if rdp_connection.is_none() {
|
||||
let (conn, fd, streams, session) = request_remote_desktop()?;
|
||||
let conn = Arc::new(conn);
|
||||
|
||||
let rdp_res = RdpResponse {
|
||||
conn,
|
||||
streams,
|
||||
fd,
|
||||
session,
|
||||
};
|
||||
*rdp_connection = Some(rdp_res);
|
||||
}
|
||||
|
||||
let rdp_res = match rdp_connection.as_ref() {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
return Err(Box::new(DBusError("RDP response is None.".into())));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(rdp_res
|
||||
.streams
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|s| PipeWireCapturable::new(conn.clone(), fd.clone(), s))
|
||||
.map(|s| PipeWireCapturable::new(rdp_res.conn.clone(), rdp_res.fd.clone(), s))
|
||||
.collect())
|
||||
}
|
||||
|
315
libs/scrap/src/wayland/remote_desktop_portal.rs
Normal file
315
libs/scrap/src/wayland/remote_desktop_portal.rs
Normal file
@ -0,0 +1,315 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -c blocking -m None`, see https://github.com/diwic/dbus-rs
|
||||
// https://github.com/flatpak/xdg-desktop-portal/blob/main/data/org.freedesktop.portal.RemoteDesktop.xml
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgFreedesktopPortalRemoteDesktop {
|
||||
fn create_session(&self, options: arg::PropMap) -> Result<dbus::Path<'static>, dbus::Error>;
|
||||
fn select_devices(
|
||||
&self,
|
||||
session_handle: dbus::Path,
|
||||
options: arg::PropMap,
|
||||
) -> Result<dbus::Path<'static>, dbus::Error>;
|
||||
fn start(
|
||||
&self,
|
||||
session_handle: dbus::Path,
|
||||
parent_window: &str,
|
||||
options: arg::PropMap,
|
||||
) -> Result<dbus::Path<'static>, dbus::Error>;
|
||||
fn notify_pointer_motion(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_pointer_motion_absolute(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
stream: u32,
|
||||
x_: f64,
|
||||
y_: f64,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_pointer_button(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
button: i32,
|
||||
state: u32,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_pointer_axis(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_pointer_axis_discrete(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
axis: u32,
|
||||
steps: i32,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_keyboard_keycode(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
keycode: i32,
|
||||
state: u32,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_keyboard_keysym(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
keysym: i32,
|
||||
state: u32,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_touch_down(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
stream: u32,
|
||||
slot: u32,
|
||||
x_: f64,
|
||||
y_: f64,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_touch_motion(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
stream: u32,
|
||||
slot: u32,
|
||||
x_: f64,
|
||||
y_: f64,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn notify_touch_up(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
slot: u32,
|
||||
) -> Result<(), dbus::Error>;
|
||||
fn connect_to_eis(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
) -> Result<arg::OwnedFd, dbus::Error>;
|
||||
fn available_device_types(&self) -> Result<u32, dbus::Error>;
|
||||
fn version(&self) -> Result<u32, dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>>
|
||||
OrgFreedesktopPortalRemoteDesktop for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn create_session(&self, options: arg::PropMap) -> Result<dbus::Path<'static>, dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"CreateSession",
|
||||
(options,),
|
||||
)
|
||||
.and_then(|r: (dbus::Path<'static>,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn select_devices(
|
||||
&self,
|
||||
session_handle: dbus::Path,
|
||||
options: arg::PropMap,
|
||||
) -> Result<dbus::Path<'static>, dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"SelectDevices",
|
||||
(session_handle, options),
|
||||
)
|
||||
.and_then(|r: (dbus::Path<'static>,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn start(
|
||||
&self,
|
||||
session_handle: dbus::Path,
|
||||
parent_window: &str,
|
||||
options: arg::PropMap,
|
||||
) -> Result<dbus::Path<'static>, dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"Start",
|
||||
(session_handle, parent_window, options),
|
||||
)
|
||||
.and_then(|r: (dbus::Path<'static>,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn notify_pointer_motion(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyPointerMotion",
|
||||
(session_handle, options, dx, dy),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_pointer_motion_absolute(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
stream: u32,
|
||||
x_: f64,
|
||||
y_: f64,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyPointerMotionAbsolute",
|
||||
(session_handle, options, stream, x_, y_),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_pointer_button(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
button: i32,
|
||||
state: u32,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyPointerButton",
|
||||
(session_handle, options, button, state),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_pointer_axis(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyPointerAxis",
|
||||
(session_handle, options, dx, dy),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_pointer_axis_discrete(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
axis: u32,
|
||||
steps: i32,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyPointerAxisDiscrete",
|
||||
(session_handle, options, axis, steps),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_keyboard_keycode(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
keycode: i32,
|
||||
state: u32,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyKeyboardKeycode",
|
||||
(session_handle, options, keycode, state),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_keyboard_keysym(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
keysym: i32,
|
||||
state: u32,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyKeyboardKeysym",
|
||||
(session_handle, options, keysym, state),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_touch_down(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
stream: u32,
|
||||
slot: u32,
|
||||
x_: f64,
|
||||
y_: f64,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyTouchDown",
|
||||
(session_handle, options, stream, slot, x_, y_),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_touch_motion(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
stream: u32,
|
||||
slot: u32,
|
||||
x_: f64,
|
||||
y_: f64,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyTouchMotion",
|
||||
(session_handle, options, stream, slot, x_, y_),
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_touch_up(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
slot: u32,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"NotifyTouchUp",
|
||||
(session_handle, options, slot),
|
||||
)
|
||||
}
|
||||
|
||||
fn connect_to_eis(
|
||||
&self,
|
||||
session_handle: &dbus::Path,
|
||||
options: arg::PropMap,
|
||||
) -> Result<arg::OwnedFd, dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"ConnectToEIS",
|
||||
(session_handle, options),
|
||||
)
|
||||
.and_then(|r: (arg::OwnedFd,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn available_device_types(&self) -> Result<u32, dbus::Error> {
|
||||
<Self as blocking::stdintf::org_freedesktop_dbus::Properties>::get(
|
||||
&self,
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"AvailableDeviceTypes",
|
||||
)
|
||||
}
|
||||
|
||||
fn version(&self) -> Result<u32, dbus::Error> {
|
||||
<Self as blocking::stdintf::org_freedesktop_dbus::Properties>::get(
|
||||
&self,
|
||||
"org.freedesktop.portal.RemoteDesktop",
|
||||
"version",
|
||||
)
|
||||
}
|
||||
}
|
45
libs/scrap/src/wayland/request_portal.rs
Normal file
45
libs/scrap/src/wayland/request_portal.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -c blocking -m None`, see https://github.com/diwic/dbus-rs
|
||||
// https://github.com/flatpak/xdg-desktop-portal/blob/main/data/org.freedesktop.portal.Request.xml
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgFreedesktopPortalRequest {
|
||||
fn close(&self) -> Result<(), dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgFreedesktopPortalRequest
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn close(&self) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.portal.Request", "Close", ())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrgFreedesktopPortalRequestResponse {
|
||||
pub response: u32,
|
||||
pub results: arg::PropMap,
|
||||
}
|
||||
|
||||
impl arg::AppendAll for OrgFreedesktopPortalRequestResponse {
|
||||
fn append(&self, i: &mut arg::IterAppend) {
|
||||
arg::RefArg::append(&self.response, i);
|
||||
arg::RefArg::append(&self.results, i);
|
||||
}
|
||||
}
|
||||
|
||||
impl arg::ReadAll for OrgFreedesktopPortalRequestResponse {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgFreedesktopPortalRequestResponse {
|
||||
response: i.read()?,
|
||||
results: i.read()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl dbus::message::SignalArgs for OrgFreedesktopPortalRequestResponse {
|
||||
const NAME: &'static str = "Response";
|
||||
const INTERFACE: &'static str = "org.freedesktop.portal.Request";
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -c blocking -m None`, see https://github.com/diwic/dbus-rs
|
||||
// https://github.com/flatpak/xdg-desktop-portal/blob/main/data/org.freedesktop.portal.ScreenCast.xml
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
@ -103,42 +104,3 @@ impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OrgFreedesktopPortalRequest {
|
||||
fn close(&self) -> Result<(), dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgFreedesktopPortalRequest
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn close(&self) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.portal.Request", "Close", ())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrgFreedesktopPortalRequestResponse {
|
||||
pub response: u32,
|
||||
pub results: arg::PropMap,
|
||||
}
|
||||
|
||||
impl arg::AppendAll for OrgFreedesktopPortalRequestResponse {
|
||||
fn append(&self, i: &mut arg::IterAppend) {
|
||||
arg::RefArg::append(&self.response, i);
|
||||
arg::RefArg::append(&self.results, i);
|
||||
}
|
||||
}
|
||||
|
||||
impl arg::ReadAll for OrgFreedesktopPortalRequestResponse {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgFreedesktopPortalRequestResponse {
|
||||
response: i.read()?,
|
||||
results: i.read()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl dbus::message::SignalArgs for OrgFreedesktopPortalRequestResponse {
|
||||
const NAME: &'static str = "Response";
|
||||
const INTERFACE: &'static str = "org.freedesktop.portal.Request";
|
||||
}
|
@ -39,6 +39,8 @@ pub(crate) mod wayland;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod uinput;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod rdp_input;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod dbus;
|
||||
pub mod input_service;
|
||||
} else {
|
||||
|
@ -5,6 +5,8 @@ use crate::clipboard_file::*;
|
||||
use crate::common::update_clipboard;
|
||||
#[cfg(target_os = "android")]
|
||||
use crate::keyboard::client::map_key_to_control_key;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::platform::linux::is_x11;
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
use crate::platform::linux_desktop_manager;
|
||||
@ -12,8 +14,6 @@ use crate::platform::linux_desktop_manager;
|
||||
use crate::platform::WallPaperRemover;
|
||||
#[cfg(windows)]
|
||||
use crate::portable_service::client as portable_client;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::platform::linux::is_x11;
|
||||
use crate::{
|
||||
client::{
|
||||
new_voice_call_request, new_voice_call_response, start_audio_thread, MediaData, MediaSender,
|
||||
@ -1172,7 +1172,21 @@ impl Connection {
|
||||
..Default::default()
|
||||
})
|
||||
.into();
|
||||
pi.resolutions = Self::get_supported_resolutions(self.display_idx).into();
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
pi.resolutions = Some(SupportedResolutions {
|
||||
resolutions: display_service::try_get_displays()
|
||||
.map(|displays| {
|
||||
displays
|
||||
.get(self.display_idx)
|
||||
.map(|d| crate::platform::resolutions(&d.name()))
|
||||
.unwrap_or(vec![])
|
||||
})
|
||||
.unwrap_or(vec![]),
|
||||
..Default::default()
|
||||
})
|
||||
.into();
|
||||
}
|
||||
|
||||
let mut sub_service = false;
|
||||
if self.file_transfer.is_some() {
|
||||
@ -1194,6 +1208,14 @@ impl Connection {
|
||||
pi.current_display = self.display_idx as _;
|
||||
res.set_peer_info(pi);
|
||||
sub_service = true;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// use rdp_input when uinput is not available in wayland. Ex: flatpak
|
||||
if !is_x11() && !crate::is_server() {
|
||||
let _ = setup_rdp_input().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.on_remote_authorized();
|
||||
@ -1236,31 +1258,6 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_supported_resolutions(display_idx: usize) -> Option<SupportedResolutions> {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
return None;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !is_x11() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(SupportedResolutions {
|
||||
resolutions: display_service::try_get_displays()
|
||||
.map(|displays| {
|
||||
displays
|
||||
.get(display_idx)
|
||||
.map(|d| crate::platform::resolutions(&d.name()))
|
||||
.unwrap_or(vec![])
|
||||
})
|
||||
.unwrap_or(vec![]),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn on_remote_authorized(&self) {
|
||||
self.update_codec_on_login();
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
|
@ -5,6 +5,8 @@ use crate::input::*;
|
||||
#[cfg(target_os = "macos")]
|
||||
use dispatch::Queue;
|
||||
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||
#[cfg(target_os = "linux")]
|
||||
use super::rdp_input::client::{RdpInputKeyboard, RdpInputMouse};
|
||||
use hbb_common::{
|
||||
get_time,
|
||||
message_proto::{pointer_device_event::Union::TouchEvent, touch_event::Union::ScaleUpdate},
|
||||
@ -13,6 +15,8 @@ use hbb_common::{
|
||||
use rdev::{self, EventType, Key as RdevKey, KeyCode, RawKey};
|
||||
#[cfg(target_os = "macos")]
|
||||
use rdev::{CGEventSourceStateID, CGEventTapLocation, VirtualInput};
|
||||
#[cfg(target_os = "linux")]
|
||||
use scrap::wayland::pipewire::RDP_RESPONSE;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
ops::{Deref, DerefMut, Sub},
|
||||
@ -461,6 +465,25 @@ pub async fn setup_uinput(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultT
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn setup_rdp_input() -> ResultType<(), Box<dyn std::error::Error>> {
|
||||
let mut en = ENIGO.lock()?;
|
||||
let rdp_res_lock = RDP_RESPONSE.lock()?;
|
||||
let rdp_res = rdp_res_lock.as_ref().ok_or("RDP response is None")?;
|
||||
|
||||
let keyboard = RdpInputKeyboard::new(rdp_res.conn.clone(), rdp_res.session.clone())?;
|
||||
en.set_custom_keyboard(Box::new(keyboard));
|
||||
log::info!("RdpInput keyboard created");
|
||||
|
||||
if let Some(stream) = rdp_res.streams.clone().into_iter().next() {
|
||||
let mouse = RdpInputMouse::new(rdp_res.conn.clone(), rdp_res.session.clone(), stream)?;
|
||||
en.set_custom_mouse(Box::new(mouse));
|
||||
log::info!("RdpInput mouse created");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub async fn update_mouse_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
|
||||
set_uinput_resolution(minx, maxx, miny, maxy).await?;
|
||||
|
234
src/server/rdp_input.rs
Normal file
234
src/server/rdp_input.rs
Normal file
@ -0,0 +1,234 @@
|
||||
use crate::uinput::service::map_key;
|
||||
use dbus::{blocking::SyncConnection, Path};
|
||||
use enigo::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||
use hbb_common::ResultType;
|
||||
use scrap::wayland::pipewire::{get_portal, PwStreamInfo};
|
||||
use scrap::wayland::remote_desktop_portal::OrgFreedesktopPortalRemoteDesktop as remote_desktop_portal;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub mod client {
|
||||
use super::*;
|
||||
|
||||
const EVDEV_MOUSE_LEFT: i32 = 272;
|
||||
const EVDEV_MOUSE_RIGHT: i32 = 273;
|
||||
const EVDEV_MOUSE_MIDDLE: i32 = 274;
|
||||
|
||||
const PRESSED_DOWN_STATE: u32 = 1;
|
||||
const PRESSED_UP_STATE: u32 = 0;
|
||||
|
||||
pub struct RdpInputKeyboard {
|
||||
conn: Arc<SyncConnection>,
|
||||
session: Path<'static>,
|
||||
}
|
||||
|
||||
impl RdpInputKeyboard {
|
||||
pub fn new(conn: Arc<SyncConnection>, session: Path<'static>) -> ResultType<Self> {
|
||||
Ok(Self { conn, session })
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardControllable for RdpInputKeyboard {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn get_key_state(&mut self, _: Key) -> bool {
|
||||
// no api for this
|
||||
false
|
||||
}
|
||||
|
||||
fn key_sequence(&mut self, s: &str) {
|
||||
for c in s.chars() {
|
||||
let key = Key::Layout(c);
|
||||
let _ = handle_key(true, key, self.conn.clone(), &self.session);
|
||||
let _ = handle_key(false, key, self.conn.clone(), &self.session);
|
||||
}
|
||||
}
|
||||
|
||||
fn key_down(&mut self, key: Key) -> enigo::ResultType {
|
||||
handle_key(true, key, self.conn.clone(), &self.session)?;
|
||||
Ok(())
|
||||
}
|
||||
fn key_up(&mut self, key: Key) {
|
||||
let _ = handle_key(false, key, self.conn.clone(), &self.session);
|
||||
}
|
||||
fn key_click(&mut self, key: Key) {
|
||||
let _ = handle_key(true, key, self.conn.clone(), &self.session);
|
||||
let _ = handle_key(false, key, self.conn.clone(), &self.session);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RdpInputMouse {
|
||||
conn: Arc<SyncConnection>,
|
||||
session: Path<'static>,
|
||||
stream: PwStreamInfo,
|
||||
}
|
||||
|
||||
impl RdpInputMouse {
|
||||
pub fn new(
|
||||
conn: Arc<SyncConnection>,
|
||||
session: Path<'static>,
|
||||
stream: PwStreamInfo,
|
||||
) -> ResultType<Self> {
|
||||
Ok(Self {
|
||||
conn,
|
||||
session,
|
||||
stream,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseControllable for RdpInputMouse {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn mouse_move_to(&mut self, x: i32, y: i32) {
|
||||
let portal = get_portal(&self.conn);
|
||||
let _ = remote_desktop_portal::notify_pointer_motion_absolute(
|
||||
&portal,
|
||||
&self.session,
|
||||
HashMap::new(),
|
||||
self.stream.path as u32,
|
||||
x as f64,
|
||||
y as f64,
|
||||
);
|
||||
}
|
||||
fn mouse_move_relative(&mut self, x: i32, y: i32) {
|
||||
let portal = get_portal(&self.conn);
|
||||
let _ = remote_desktop_portal::notify_pointer_motion(
|
||||
&portal,
|
||||
&self.session,
|
||||
HashMap::new(),
|
||||
x as f64,
|
||||
y as f64,
|
||||
);
|
||||
}
|
||||
fn mouse_down(&mut self, button: MouseButton) -> enigo::ResultType {
|
||||
handle_mouse(true, button, self.conn.clone(), &self.session);
|
||||
Ok(())
|
||||
}
|
||||
fn mouse_up(&mut self, button: MouseButton) {
|
||||
handle_mouse(false, button, self.conn.clone(), &self.session);
|
||||
}
|
||||
fn mouse_click(&mut self, button: MouseButton) {
|
||||
handle_mouse(true, button, self.conn.clone(), &self.session);
|
||||
handle_mouse(false, button, self.conn.clone(), &self.session);
|
||||
}
|
||||
fn mouse_scroll_x(&mut self, length: i32) {
|
||||
let portal = get_portal(&self.conn);
|
||||
let _ = remote_desktop_portal::notify_pointer_axis(
|
||||
&portal,
|
||||
&self.session,
|
||||
HashMap::new(),
|
||||
length as f64,
|
||||
0 as f64,
|
||||
);
|
||||
}
|
||||
fn mouse_scroll_y(&mut self, length: i32) {
|
||||
let portal = get_portal(&self.conn);
|
||||
let _ = remote_desktop_portal::notify_pointer_axis(
|
||||
&portal,
|
||||
&self.session,
|
||||
HashMap::new(),
|
||||
0 as f64,
|
||||
length as f64,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw_evdev_keycode(key: u16) -> i32 {
|
||||
// 8 is the offset between xkb and evdev
|
||||
let mut key = key as i32 - 8;
|
||||
// fix for right_meta key
|
||||
if key == 126 {
|
||||
key = 125;
|
||||
}
|
||||
key
|
||||
}
|
||||
|
||||
fn handle_key(
|
||||
down: bool,
|
||||
key: Key,
|
||||
conn: Arc<SyncConnection>,
|
||||
session: &Path<'static>,
|
||||
) -> ResultType<()> {
|
||||
let state: u32 = if down {
|
||||
PRESSED_DOWN_STATE
|
||||
} else {
|
||||
PRESSED_UP_STATE
|
||||
};
|
||||
let portal = get_portal(&conn);
|
||||
match key {
|
||||
Key::Raw(key) => {
|
||||
let key = get_raw_evdev_keycode(key);
|
||||
remote_desktop_portal::notify_keyboard_keycode(
|
||||
&portal,
|
||||
&session,
|
||||
HashMap::new(),
|
||||
key,
|
||||
state,
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
if let Ok((key, is_shift)) = map_key(&key) {
|
||||
if is_shift {
|
||||
remote_desktop_portal::notify_keyboard_keycode(
|
||||
&portal,
|
||||
&session,
|
||||
HashMap::new(),
|
||||
evdev::Key::KEY_LEFTSHIFT.code() as i32,
|
||||
state,
|
||||
)?;
|
||||
}
|
||||
remote_desktop_portal::notify_keyboard_keycode(
|
||||
&portal,
|
||||
&session,
|
||||
HashMap::new(),
|
||||
key.code() as i32,
|
||||
state,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_mouse(
|
||||
down: bool,
|
||||
button: MouseButton,
|
||||
conn: Arc<SyncConnection>,
|
||||
session: &Path<'static>,
|
||||
) {
|
||||
let portal = get_portal(&conn);
|
||||
let but_key = match button {
|
||||
MouseButton::Left => EVDEV_MOUSE_LEFT,
|
||||
MouseButton::Right => EVDEV_MOUSE_RIGHT,
|
||||
MouseButton::Middle => EVDEV_MOUSE_MIDDLE,
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let state: u32 = if down {
|
||||
PRESSED_DOWN_STATE
|
||||
} else {
|
||||
PRESSED_UP_STATE
|
||||
};
|
||||
let _ = remote_desktop_portal::notify_pointer_button(
|
||||
&portal,
|
||||
&session,
|
||||
HashMap::new(),
|
||||
but_key,
|
||||
state,
|
||||
);
|
||||
}
|
||||
}
|
@ -382,7 +382,7 @@ pub mod service {
|
||||
Ok(keyboard)
|
||||
}
|
||||
|
||||
fn map_key(key: &enigo::Key) -> ResultType<(evdev::Key, bool)> {
|
||||
pub fn map_key(key: &enigo::Key) -> ResultType<(evdev::Key, bool)> {
|
||||
if let Some(k) = KEY_MAP.get(&key) {
|
||||
log::trace!("mapkey {:?}, get {:?}", &key, &k);
|
||||
return Ok((k.clone(), false));
|
||||
|
Loading…
x
Reference in New Issue
Block a user