From ad63b6ba202d3fea8e6349f1299bc782099944d5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 11 Feb 2026 22:27:32 +1000 Subject: [PATCH] Handle the RemoteDesktop portal restore token correctly (#383) For a session to actually persist, we need to request a persistence mode which we already do. The portal then returns a restore-token (in the form of an uuid) to us as part of the response to Start. This token must then be passed into the *next* session during SelectDevices to restore the previous session. The token is officially a single-use token, so we need to overwrite it every time. In practise the current XDP implementation may re-use the token but we cannot rely on that. Reading and writing the token is not async since we expect them to be uuid-length. Closes #74. --- input-emulation/src/libei.rs | 49 +++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/input-emulation/src/libei.rs b/input-emulation/src/libei.rs index 674a498..4e60c98 100644 --- a/input-emulation/src/libei.rs +++ b/input-emulation/src/libei.rs @@ -1,7 +1,8 @@ use futures::{StreamExt, future}; use std::{ - io, + env, fs, io, os::{fd::OwnedFd, unix::net::UnixStream}, + path::PathBuf, sync::{ Arc, Mutex, RwLock, atomic::{AtomicBool, Ordering}, @@ -50,10 +51,45 @@ pub(crate) struct LibeiEmulation<'a> { session: Session<'a, RemoteDesktop<'a>>, } +/// Get the path to the RemoteDesktop token file +fn get_token_file_path() -> PathBuf { + let cache_dir = env::var("XDG_CACHE_HOME") + .ok() + .map(PathBuf::from) + .unwrap_or_else(|| { + let home = env::var("HOME").expect("HOME not set"); + PathBuf::from(home).join(".cache") + }); + + cache_dir.join("lan-mouse").join("remote-desktop.token") +} + +/// Read the RemoteDesktop token from file +fn read_token() -> Option { + let token_path = get_token_file_path(); + match fs::read_to_string(&token_path) { + Ok(token) => Some(token.trim().to_string()), + Err(_) => None, + } +} + +/// Write the RemoteDesktop token to file +fn write_token(token: &str) -> io::Result<()> { + let token_path = get_token_file_path(); + if let Some(parent) = token_path.parent() { + fs::create_dir_all(parent)?; + } + + fs::write(&token_path, token)?; + Ok(()) +} + async fn get_ei_fd<'a>() -> Result<(RemoteDesktop<'a>, Session<'a, RemoteDesktop<'a>>, OwnedFd), ashpd::Error> { let remote_desktop = RemoteDesktop::new().await?; + let restore_token = read_token(); + log::debug!("creating session ..."); let session = remote_desktop.create_session().await?; @@ -62,13 +98,20 @@ async fn get_ei_fd<'a>() .select_devices( &session, DeviceType::Keyboard | DeviceType::Pointer, - None, + restore_token.as_deref(), PersistMode::ExplicitlyRevoked, ) .await?; log::info!("requesting permission for input emulation"); - let _devices = remote_desktop.start(&session, None).await?.response()?; + let start_response = remote_desktop.start(&session, None).await?.response()?; + + // 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 Err(e) = write_token(token_str) { + log::warn!("failed to save RemoteDesktop token: {}", e); + } + } let fd = remote_desktop.connect_to_eis(&session).await?; Ok((remote_desktop, session, fd))