Files
lan-mouse/input-emulation/src/xdg_desktop_portal.rs
2024-08-11 16:51:47 +02:00

166 lines
5.1 KiB
Rust

use ashpd::{
desktop::{
remote_desktop::{Axis, DeviceType, KeyState, RemoteDesktop},
PersistMode, Session,
},
zbus::AsyncDrop,
WindowIdentifier,
};
use async_trait::async_trait;
use futures::FutureExt;
use input_event::{
Event::{Keyboard, Pointer},
KeyboardEvent, PointerEvent,
};
use crate::error::EmulationError;
use super::{error::XdpEmulationCreationError, Emulation, EmulationHandle};
pub(crate) struct DesktopPortalEmulation<'a> {
proxy: RemoteDesktop<'a>,
session: Session<'a, RemoteDesktop<'a>>,
}
impl<'a> DesktopPortalEmulation<'a> {
pub(crate) async fn new() -> Result<DesktopPortalEmulation<'a>, XdpEmulationCreationError> {
log::debug!("connecting to org.freedesktop.portal.RemoteDesktop portal ...");
let proxy = RemoteDesktop::new().await?;
// retry when user presses the cancel button
log::debug!("creating session ...");
let session = proxy.create_session().await?;
log::debug!("selecting devices ...");
proxy
.select_devices(
&session,
DeviceType::Keyboard | DeviceType::Pointer,
None,
PersistMode::ExplicitlyRevoked,
)
.await?;
log::info!("requesting permission for input emulation");
let _devices = proxy
.start(&session, &WindowIdentifier::default())
.await?
.response()?;
log::debug!("started session");
let session = session;
Ok(Self { proxy, session })
}
}
#[async_trait]
impl<'a> Emulation for DesktopPortalEmulation<'a> {
async fn consume(
&mut self,
event: input_event::Event,
_client: EmulationHandle,
) -> Result<(), EmulationError> {
match event {
Pointer(p) => match p {
PointerEvent::Motion { time: _, dx, dy } => {
self.proxy
.notify_pointer_motion(&self.session, dx, dy)
.await?;
}
PointerEvent::Button {
time: _,
button,
state,
} => {
let state = match state {
0 => KeyState::Released,
_ => KeyState::Pressed,
};
self.proxy
.notify_pointer_button(&self.session, button as i32, state)
.await?;
}
PointerEvent::AxisDiscrete120 { axis, value } => {
let axis = match axis {
0 => Axis::Vertical,
_ => Axis::Horizontal,
};
self.proxy
.notify_pointer_axis_discrete(&self.session, axis, value / 120)
.await?;
}
PointerEvent::Axis {
time: _,
axis,
value,
} => {
let axis = match axis {
0 => Axis::Vertical,
_ => Axis::Horizontal,
};
let (dx, dy) = match axis {
Axis::Vertical => (0., value),
Axis::Horizontal => (value, 0.),
};
self.proxy
.notify_pointer_axis(&self.session, dx, dy, true)
.await?;
}
},
Keyboard(k) => {
match k {
KeyboardEvent::Key {
time: _,
key,
state,
} => {
let state = match state {
0 => KeyState::Released,
_ => KeyState::Pressed,
};
self.proxy
.notify_keyboard_keycode(&self.session, key as i32, state)
.await?;
}
KeyboardEvent::Modifiers { .. } => {
// ignore
}
}
}
}
Ok(())
}
async fn create(&mut self, _client: EmulationHandle) {}
async fn destroy(&mut self, _client: EmulationHandle) {}
async fn terminate(&mut self) {
if let Err(e) = self.session.close().await {
log::warn!("session.close(): {e}");
};
if let Err(e) = self.session.receive_closed().await {
log::warn!("session.receive_closed(): {e}");
};
}
}
impl<'a> AsyncDrop for DesktopPortalEmulation<'a> {
#[doc = r" Perform the async cleanup."]
#[must_use]
#[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
fn async_drop<'async_trait>(
self,
) -> ::core::pin::Pin<
Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
>
where
Self: 'async_trait,
{
async move {
let _ = self.session.close().await;
}
.boxed()
}
}