update ashpd

This commit is contained in:
Ferdinand Schober
2026-04-08 13:11:18 +02:00
parent 1075a90c5b
commit 38920917cd
6 changed files with 122 additions and 89 deletions

10
Cargo.lock generated
View File

@@ -125,18 +125,16 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]] [[package]]
name = "ashpd" name = "ashpd"
version = "0.11.1" version = "0.13.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" checksum = "13bdf0fd848239dcd5e64eeeee35dbc00378ebcc6f3aa4ead0a305eec83d0cfb"
dependencies = [ dependencies = [
"enumflags2", "enumflags2",
"futures-channel",
"futures-util", "futures-util",
"rand 0.9.2", "getrandom 0.4.2",
"serde", "serde",
"serde_repr", "serde_repr",
"tokio", "tokio",
"url",
"zbus", "zbus",
] ]
@@ -3310,7 +3308,6 @@ dependencies = [
"idna", "idna",
"percent-encoding", "percent-encoding",
"serde", "serde",
"serde_derive",
] ]
[[package]] [[package]]
@@ -4226,7 +4223,6 @@ dependencies = [
"endi", "endi",
"enumflags2", "enumflags2",
"serde", "serde",
"url",
"winnow 0.7.15", "winnow 0.7.15",
"zvariant_derive", "zvariant_derive",
"zvariant_utils", "zvariant_utils",

View File

@@ -12,7 +12,7 @@ futures-core = "0.3.30"
log = "0.4.22" log = "0.4.22"
input-event = { path = "../input-event", version = "0.3.0" } input-event = { path = "../input-event", version = "0.3.0" }
memmap = "0.7" memmap = "0.7"
tempfile = "3.8" tempfile = "3.25.0"
thiserror = "2.0.0" thiserror = "2.0.0"
tokio = { version = "1.32.0", features = [ tokio = { version = "1.32.0", features = [
"io-util", "io-util",
@@ -41,7 +41,8 @@ wayland-protocols-wlr = { version = "0.3.1", features = [
"client", "client",
], optional = true } ], optional = true }
x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true } x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true }
ashpd = { version = "0.11.0", default-features = false, features = [ ashpd = { version = "0.13.9", default-features = false, features = [
"input_capture",
"tokio", "tokio",
], optional = true } ], optional = true }
reis = { version = "0.5.0", features = ["tokio"], optional = true } reis = { version = "0.5.0", features = ["tokio"], optional = true }

View File

@@ -2,8 +2,8 @@ use ashpd::{
desktop::{ desktop::{
Session, Session,
input_capture::{ input_capture::{
Activated, ActivatedBarrier, Barrier, BarrierID, Capabilities, InputCapture, Region, Activated, ActivatedBarrier, Barrier, BarrierID, Capabilities, CreateSessionOptions,
Zones, InputCapture, Region, ReleaseOptions, Zones,
}, },
}, },
enumflags2::BitFlags, enumflags2::BitFlags,
@@ -58,8 +58,8 @@ enum LibeiNotifyEvent {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub struct LibeiInputCapture<'a> { pub struct LibeiInputCapture {
input_capture: Pin<Box<InputCapture<'a>>>, input_capture: Pin<Box<InputCapture>>,
capture_task: JoinHandle<Result<(), CaptureError>>, capture_task: JoinHandle<Result<(), CaptureError>>,
event_rx: Receiver<(Position, CaptureEvent)>, event_rx: Receiver<(Position, CaptureEvent)>,
notify_capture: Sender<LibeiNotifyEvent>, notify_capture: Sender<LibeiNotifyEvent>,
@@ -130,12 +130,15 @@ fn select_barriers(
} }
async fn update_barriers( async fn update_barriers(
input_capture: &InputCapture<'_>, input_capture: &InputCapture,
session: &Session<'_, InputCapture<'_>>, session: &Session<InputCapture>,
active_clients: &[Position], active_clients: &[Position],
next_barrier_id: &mut NonZeroU32, next_barrier_id: &mut NonZeroU32,
) -> Result<(Vec<ICBarrier>, HashMap<BarrierID, Position>), ashpd::Error> { ) -> Result<(Vec<ICBarrier>, HashMap<BarrierID, Position>), ashpd::Error> {
let zones = input_capture.zones(session).await?.response()?; let zones = input_capture
.zones(session, Default::default())
.await?
.response()?;
log::debug!("zones: {zones:?}"); log::debug!("zones: {zones:?}");
let (barriers, id_map) = select_barriers(&zones, active_clients, next_barrier_id); let (barriers, id_map) = select_barriers(&zones, active_clients, next_barrier_id);
@@ -144,31 +147,38 @@ async fn update_barriers(
let ashpd_barriers: Vec<Barrier> = barriers.iter().copied().map(|b| b.into()).collect(); let ashpd_barriers: Vec<Barrier> = barriers.iter().copied().map(|b| b.into()).collect();
let response = input_capture let response = input_capture
.set_pointer_barriers(session, &ashpd_barriers, zones.zone_set()) .set_pointer_barriers(
session,
&ashpd_barriers,
zones.zone_set(),
Default::default(),
)
.await?; .await?;
let response = response.response()?; let response = response.response()?;
log::debug!("{response:?}"); log::debug!("{response:?}");
Ok((barriers, id_map)) Ok((barriers, id_map))
} }
async fn create_session<'a>( async fn create_session(
input_capture: &'a InputCapture<'a>, input_capture: &InputCapture,
) -> std::result::Result<(Session<'a, InputCapture<'a>>, BitFlags<Capabilities>), ashpd::Error> { ) -> std::result::Result<(Session<InputCapture>, BitFlags<Capabilities>), ashpd::Error> {
log::debug!("creating input capture session"); log::debug!("creating input capture session");
input_capture let create_session_options = CreateSessionOptions::default().set_capabilities(
.create_session(
None,
Capabilities::Keyboard | Capabilities::Pointer | Capabilities::Touchscreen, Capabilities::Keyboard | Capabilities::Pointer | Capabilities::Touchscreen,
) );
input_capture
.create_session(None, create_session_options)
.await .await
} }
async fn connect_to_eis( async fn connect_to_eis(
input_capture: &InputCapture<'_>, input_capture: &InputCapture,
session: &Session<'_, InputCapture<'_>>, session: &Session<InputCapture>,
) -> Result<(ei::Context, Connection, EiConvertEventStream), CaptureError> { ) -> Result<(ei::Context, Connection, EiConvertEventStream), CaptureError> {
log::debug!("connect_to_eis"); log::debug!("connect_to_eis");
let fd = input_capture.connect_to_eis(session).await?; let fd = input_capture
.connect_to_eis(session, Default::default())
.await?;
// create unix stream from fd // create unix stream from fd
let stream = UnixStream::from(fd); let stream = UnixStream::from(fd);
@@ -201,10 +211,10 @@ async fn libei_event_handler(
} }
} }
impl LibeiInputCapture<'_> { impl LibeiInputCapture {
pub async fn new() -> std::result::Result<Self, LibeiCaptureCreationError> { pub async fn new() -> std::result::Result<Self, LibeiCaptureCreationError> {
let input_capture = Box::pin(InputCapture::new().await?); let input_capture = Box::pin(InputCapture::new().await?);
let input_capture_ptr = input_capture.as_ref().get_ref() as *const InputCapture<'static>; let input_capture_ptr = input_capture.as_ref().get_ref() as *const InputCapture;
let first_session = Some(create_session(unsafe { &*input_capture_ptr }).await?); let first_session = Some(create_session(unsafe { &*input_capture_ptr }).await?);
let (event_tx, event_rx) = mpsc::channel(1); let (event_tx, event_rx) = mpsc::channel(1);
@@ -238,10 +248,10 @@ impl LibeiInputCapture<'_> {
} }
async fn do_capture( async fn do_capture(
input_capture: *const InputCapture<'static>, input_capture: *const InputCapture,
mut capture_event: Receiver<LibeiNotifyEvent>, mut capture_event: Receiver<LibeiNotifyEvent>,
notify_release: Arc<Notify>, notify_release: Arc<Notify>,
session: Option<(Session<'_, InputCapture<'_>>, BitFlags<Capabilities>)>, session: Option<(Session<InputCapture>, BitFlags<Capabilities>)>,
event_tx: Sender<(Position, CaptureEvent)>, event_tx: Sender<(Position, CaptureEvent)>,
cancellation_token: CancellationToken, cancellation_token: CancellationToken,
) -> Result<(), CaptureError> { ) -> Result<(), CaptureError> {
@@ -307,7 +317,7 @@ async fn do_capture(
// disable capture // disable capture
log::debug!("disabling input capture"); log::debug!("disabling input capture");
if let Err(e) = input_capture.disable(&session).await { if let Err(e) = input_capture.disable(&session, Default::default()).await {
log::warn!("input_capture.disable(&session) {e}"); log::warn!("input_capture.disable(&session) {e}");
} }
if let Err(e) = session.close().await { if let Err(e) = session.close().await {
@@ -336,8 +346,8 @@ async fn do_capture(
} }
async fn do_capture_session( async fn do_capture_session(
input_capture: &InputCapture<'_>, input_capture: &InputCapture,
session: &mut Session<'_, InputCapture<'_>>, session: &mut Session<InputCapture>,
event_tx: &Sender<(Position, CaptureEvent)>, event_tx: &Sender<(Position, CaptureEvent)>,
active_clients: &[Position], active_clients: &[Position],
next_barrier_id: &mut NonZeroU32, next_barrier_id: &mut NonZeroU32,
@@ -356,7 +366,7 @@ async fn do_capture_session(
update_barriers(input_capture, session, active_clients, next_barrier_id).await?; update_barriers(input_capture, session, active_clients, next_barrier_id).await?;
log::debug!("enabling session"); log::debug!("enabling session");
input_capture.enable(session).await?; input_capture.enable(session, Default::default()).await?;
// cancellation token to release session // cancellation token to release session
let release_session = Arc::new(Notify::new()); let release_session = Arc::new(Notify::new());
@@ -462,9 +472,9 @@ async fn do_capture_session(
Ok(()) Ok(())
} }
async fn release_capture<'a>( async fn release_capture(
input_capture: &InputCapture<'a>, input_capture: &InputCapture,
session: &Session<'a, InputCapture<'a>>, session: &Session<InputCapture>,
activated: Activated, activated: Activated,
current_pos: Position, current_pos: Position,
) -> Result<(), CaptureError> { ) -> Result<(), CaptureError> {
@@ -484,9 +494,10 @@ async fn release_capture<'a>(
}; };
// release 1px to the right of the entered zone // release 1px to the right of the entered zone
let cursor_position = (x as f64 + dx, y as f64 + dy); let cursor_position = (x as f64 + dx, y as f64 + dy);
input_capture let release_options = ReleaseOptions::default()
.release(session, activated.activation_id(), Some(cursor_position)) .set_activation_id(activated.activation_id())
.await?; .set_cursor_position(Some(cursor_position));
input_capture.release(session, release_options).await?;
Ok(()) Ok(())
} }
@@ -561,7 +572,7 @@ async fn handle_ei_event(
} }
#[async_trait] #[async_trait]
impl LanMouseInputCapture for LibeiInputCapture<'_> { impl LanMouseInputCapture for LibeiInputCapture {
async fn create(&mut self, pos: Position) -> Result<(), CaptureError> { async fn create(&mut self, pos: Position) -> Result<(), CaptureError> {
let _ = self let _ = self
.notify_capture .notify_capture
@@ -598,7 +609,7 @@ impl LanMouseInputCapture for LibeiInputCapture<'_> {
} }
} }
impl Drop for LibeiInputCapture<'_> { impl Drop for LibeiInputCapture {
fn drop(&mut self) { fn drop(&mut self) {
if !self.terminated { if !self.terminated {
/* this workaround is needed until async drop is stabilized */ /* this workaround is needed until async drop is stabilized */
@@ -607,10 +618,10 @@ impl Drop for LibeiInputCapture<'_> {
} }
} }
impl Stream for LibeiInputCapture<'_> { impl Stream for LibeiInputCapture {
type Item = Result<(Position, CaptureEvent), CaptureError>; type Item = Result<(Position, CaptureEvent), CaptureError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
match self.capture_task.poll_unpin(cx) { match self.capture_task.poll_unpin(cx) {
Poll::Ready(r) => match r.expect("failed to join") { Poll::Ready(r) => match r.expect("failed to join") {
Ok(()) => Poll::Ready(None), Ok(()) => Poll::Ready(None),

View File

@@ -40,7 +40,9 @@ wayland-protocols-misc = { version = "0.3.1", features = [
"client", "client",
], optional = true } ], optional = true }
x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true } x11 = { version = "2.21.0", features = ["xlib", "xtest"], optional = true }
ashpd = { version = "0.11.0", default-features = false, features = [ ashpd = { version = "0.13.9", default-features = false, features = [
"remote_desktop",
"screencast",
"tokio", "tokio",
], optional = true } ], optional = true }
reis = { version = "0.5.0", features = ["tokio"], optional = true } reis = { version = "0.5.0", features = ["tokio"], optional = true }

View File

@@ -13,7 +13,7 @@ use tokio::task::JoinHandle;
use ashpd::desktop::{ use ashpd::desktop::{
PersistMode, Session, PersistMode, Session,
remote_desktop::{DeviceType, RemoteDesktop}, remote_desktop::{DeviceType, RemoteDesktop, SelectDevicesOptions},
}; };
use async_trait::async_trait; use async_trait::async_trait;
@@ -40,15 +40,15 @@ struct Devices {
keyboard: Arc<RwLock<Option<(ei::Device, ei::Keyboard)>>>, keyboard: Arc<RwLock<Option<(ei::Device, ei::Keyboard)>>>,
} }
pub(crate) struct LibeiEmulation<'a> { pub(crate) struct LibeiEmulation {
context: ei::Context, context: ei::Context,
conn: event::Connection, conn: event::Connection,
devices: Devices, devices: Devices,
ei_task: JoinHandle<()>, ei_task: JoinHandle<()>,
error: Arc<Mutex<Option<EmulationError>>>, error: Arc<Mutex<Option<EmulationError>>>,
libei_error: Arc<AtomicBool>, libei_error: Arc<AtomicBool>,
_remote_desktop: RemoteDesktop<'a>, _remote_desktop: RemoteDesktop,
session: Session<'a, RemoteDesktop<'a>>, session: Session<RemoteDesktop>,
} }
/// Get the path to the RemoteDesktop token file /// Get the path to the RemoteDesktop token file
@@ -84,27 +84,26 @@ fn write_token(token: &str) -> io::Result<()> {
Ok(()) Ok(())
} }
async fn get_ei_fd<'a>() async fn get_ei_fd() -> Result<(RemoteDesktop, Session<RemoteDesktop>, OwnedFd), ashpd::Error> {
-> Result<(RemoteDesktop<'a>, Session<'a, RemoteDesktop<'a>>, OwnedFd), ashpd::Error> {
let remote_desktop = RemoteDesktop::new().await?; let remote_desktop = RemoteDesktop::new().await?;
let restore_token = read_token(); let restore_token = read_token();
log::debug!("creating session ..."); log::debug!("creating session ...");
let session = remote_desktop.create_session().await?; let session = remote_desktop.create_session(Default::default()).await?;
log::debug!("selecting devices ..."); log::debug!("selecting devices ...");
remote_desktop let options = SelectDevicesOptions::default()
.select_devices( .set_devices(DeviceType::Keyboard | DeviceType::Pointer)
&session, .set_persist_mode(PersistMode::ExplicitlyRevoked)
DeviceType::Keyboard | DeviceType::Pointer, .set_restore_token(restore_token.as_deref());
restore_token.as_deref(), remote_desktop.select_devices(&session, options).await?;
PersistMode::ExplicitlyRevoked,
)
.await?;
log::info!("requesting permission for input emulation"); log::info!("requesting permission for input emulation");
let start_response = remote_desktop.start(&session, None).await?.response()?; let start_response = remote_desktop
.start(&session, None, Default::default())
.await?
.response()?;
// The restore token is only valid once, we need to re-save it each time // The restore token is only valid once, we need to re-save it each time
if let Some(token_str) = start_response.restore_token() { if let Some(token_str) = start_response.restore_token() {
@@ -113,11 +112,13 @@ async fn get_ei_fd<'a>()
} }
} }
let fd = remote_desktop.connect_to_eis(&session).await?; let fd = remote_desktop
.connect_to_eis(&session, Default::default())
.await?;
Ok((remote_desktop, session, fd)) Ok((remote_desktop, session, fd))
} }
impl LibeiEmulation<'_> { impl LibeiEmulation {
pub(crate) async fn new() -> Result<Self, LibeiEmulationCreationError> { pub(crate) async fn new() -> Result<Self, LibeiEmulationCreationError> {
let (_remote_desktop, session, eifd) = get_ei_fd().await?; let (_remote_desktop, session, eifd) = get_ei_fd().await?;
let stream = UnixStream::from(eifd); let stream = UnixStream::from(eifd);
@@ -152,14 +153,14 @@ impl LibeiEmulation<'_> {
} }
} }
impl Drop for LibeiEmulation<'_> { impl Drop for LibeiEmulation {
fn drop(&mut self) { fn drop(&mut self) {
self.ei_task.abort(); self.ei_task.abort();
} }
} }
#[async_trait] #[async_trait]
impl Emulation for LibeiEmulation<'_> { impl Emulation for LibeiEmulation {
async fn consume( async fn consume(
&mut self, &mut self,
event: Event, event: Event,

View File

@@ -1,7 +1,10 @@
use ashpd::{ use ashpd::{
desktop::{ desktop::{
PersistMode, Session, PersistMode, Session,
remote_desktop::{Axis, DeviceType, KeyState, RemoteDesktop}, remote_desktop::{
Axis, DeviceType, KeyState, NotifyPointerAxisOptions, RemoteDesktop,
SelectDevicesOptions,
},
}, },
zbus::AsyncDrop, zbus::AsyncDrop,
}; };
@@ -17,32 +20,31 @@ use crate::error::EmulationError;
use super::{Emulation, EmulationHandle, error::XdpEmulationCreationError}; use super::{Emulation, EmulationHandle, error::XdpEmulationCreationError};
pub(crate) struct DesktopPortalEmulation<'a> { pub(crate) struct DesktopPortalEmulation {
proxy: RemoteDesktop<'a>, proxy: RemoteDesktop,
session: Session<'a, RemoteDesktop<'a>>, session: Session<RemoteDesktop>,
} }
impl<'a> DesktopPortalEmulation<'a> { impl DesktopPortalEmulation {
pub(crate) async fn new() -> Result<DesktopPortalEmulation<'a>, XdpEmulationCreationError> { pub(crate) async fn new() -> Result<DesktopPortalEmulation, XdpEmulationCreationError> {
log::debug!("connecting to org.freedesktop.portal.RemoteDesktop portal ..."); log::debug!("connecting to org.freedesktop.portal.RemoteDesktop portal ...");
let proxy = RemoteDesktop::new().await?; let proxy = RemoteDesktop::new().await?;
// retry when user presses the cancel button // retry when user presses the cancel button
log::debug!("creating session ..."); log::debug!("creating session ...");
let session = proxy.create_session().await?; let session = proxy.create_session(Default::default()).await?;
log::debug!("selecting devices ..."); log::debug!("selecting devices ...");
proxy let options = SelectDevicesOptions::default()
.select_devices( .set_devices(DeviceType::Keyboard | DeviceType::Pointer)
&session, .set_persist_mode(PersistMode::ExplicitlyRevoked);
DeviceType::Keyboard | DeviceType::Pointer, proxy.select_devices(&session, options).await?;
None,
PersistMode::ExplicitlyRevoked,
)
.await?;
log::info!("requesting permission for input emulation"); log::info!("requesting permission for input emulation");
let _devices = proxy.start(&session, None).await?.response()?; let _devices = proxy
.start(&session, None, Default::default())
.await?
.response()?;
log::debug!("started session"); log::debug!("started session");
let session = session; let session = session;
@@ -52,7 +54,7 @@ impl<'a> DesktopPortalEmulation<'a> {
} }
#[async_trait] #[async_trait]
impl Emulation for DesktopPortalEmulation<'_> { impl Emulation for DesktopPortalEmulation {
async fn consume( async fn consume(
&mut self, &mut self,
event: input_event::Event, event: input_event::Event,
@@ -62,7 +64,7 @@ impl Emulation for DesktopPortalEmulation<'_> {
Pointer(p) => match p { Pointer(p) => match p {
PointerEvent::Motion { time: _, dx, dy } => { PointerEvent::Motion { time: _, dx, dy } => {
self.proxy self.proxy
.notify_pointer_motion(&self.session, dx, dy) .notify_pointer_motion(&self.session, dx, dy, Default::default())
.await?; .await?;
} }
PointerEvent::Button { PointerEvent::Button {
@@ -75,7 +77,12 @@ impl Emulation for DesktopPortalEmulation<'_> {
_ => KeyState::Pressed, _ => KeyState::Pressed,
}; };
self.proxy self.proxy
.notify_pointer_button(&self.session, button as i32, state) .notify_pointer_button(
&self.session,
button as i32,
state,
Default::default(),
)
.await?; .await?;
} }
PointerEvent::AxisDiscrete120 { axis, value } => { PointerEvent::AxisDiscrete120 { axis, value } => {
@@ -84,7 +91,12 @@ impl Emulation for DesktopPortalEmulation<'_> {
_ => Axis::Horizontal, _ => Axis::Horizontal,
}; };
self.proxy self.proxy
.notify_pointer_axis_discrete(&self.session, axis, value / 120) .notify_pointer_axis_discrete(
&self.session,
axis,
value / 120,
Default::default(),
)
.await?; .await?;
} }
PointerEvent::Axis { PointerEvent::Axis {
@@ -101,7 +113,12 @@ impl Emulation for DesktopPortalEmulation<'_> {
Axis::Horizontal => (value, 0.), Axis::Horizontal => (value, 0.),
}; };
self.proxy self.proxy
.notify_pointer_axis(&self.session, dx, dy, true) .notify_pointer_axis(
&self.session,
dx,
dy,
NotifyPointerAxisOptions::default().set_finish(true),
)
.await?; .await?;
} }
}, },
@@ -117,7 +134,12 @@ impl Emulation for DesktopPortalEmulation<'_> {
_ => KeyState::Pressed, _ => KeyState::Pressed,
}; };
self.proxy self.proxy
.notify_keyboard_keycode(&self.session, key as i32, state) .notify_keyboard_keycode(
&self.session,
key as i32,
state,
Default::default(),
)
.await?; .await?;
} }
KeyboardEvent::Modifiers { .. } => { KeyboardEvent::Modifiers { .. } => {
@@ -141,7 +163,7 @@ impl Emulation for DesktopPortalEmulation<'_> {
} }
} }
impl AsyncDrop for DesktopPortalEmulation<'_> { impl AsyncDrop for DesktopPortalEmulation {
#[doc = r" Perform the async cleanup."] #[doc = r" Perform the async cleanup."]
#[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)] #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
fn async_drop<'async_trait>( fn async_drop<'async_trait>(