Compare commits

..

1 Commits

Author SHA1 Message Date
Ferdinand Schober
eb8c2d091b automatically update config when changed 2026-03-26 15:23:14 +01:00
7 changed files with 977 additions and 943 deletions

1717
Cargo.lock generated

File diff suppressed because it is too large Load Diff

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.25.0" tempfile = "3.8"
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,8 +41,7 @@ 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.13.9", default-features = false, features = [ ashpd = { version = "0.11.0", 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, CreateSessionOptions, Activated, ActivatedBarrier, Barrier, BarrierID, Capabilities, InputCapture, Region,
InputCapture, Region, ReleaseOptions, Zones, Zones,
}, },
}, },
enumflags2::BitFlags, enumflags2::BitFlags,
@@ -58,8 +58,8 @@ enum LibeiNotifyEvent {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub struct LibeiInputCapture { pub struct LibeiInputCapture<'a> {
input_capture: Pin<Box<InputCapture>>, input_capture: Pin<Box<InputCapture<'a>>>,
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,15 +130,12 @@ 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 let zones = input_capture.zones(session).await?.response()?;
.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);
@@ -147,38 +144,31 @@ 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( .set_pointer_barriers(session, &ashpd_barriers, zones.zone_set())
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( async fn create_session<'a>(
input_capture: &InputCapture, input_capture: &'a InputCapture<'a>,
) -> std::result::Result<(Session<InputCapture>, BitFlags<Capabilities>), ashpd::Error> { ) -> std::result::Result<(Session<'a, InputCapture<'a>>, BitFlags<Capabilities>), ashpd::Error> {
log::debug!("creating input capture session"); log::debug!("creating input capture session");
let create_session_options = CreateSessionOptions::default().set_capabilities(
Capabilities::Keyboard | Capabilities::Pointer | Capabilities::Touchscreen,
);
input_capture input_capture
.create_session(None, create_session_options) .create_session(
None,
Capabilities::Keyboard | Capabilities::Pointer | Capabilities::Touchscreen,
)
.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 let fd = input_capture.connect_to_eis(session).await?;
.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);
@@ -211,10 +201,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; let input_capture_ptr = input_capture.as_ref().get_ref() as *const InputCapture<'static>;
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);
@@ -248,10 +238,10 @@ impl LibeiInputCapture {
} }
async fn do_capture( async fn do_capture(
input_capture: *const InputCapture, input_capture: *const InputCapture<'static>,
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> {
@@ -317,7 +307,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, Default::default()).await { if let Err(e) = input_capture.disable(&session).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 {
@@ -346,8 +336,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,
@@ -366,7 +356,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, Default::default()).await?; input_capture.enable(session).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());
@@ -472,9 +462,9 @@ async fn do_capture_session(
Ok(()) Ok(())
} }
async fn release_capture( async fn release_capture<'a>(
input_capture: &InputCapture, input_capture: &InputCapture<'a>,
session: &Session<InputCapture>, session: &Session<'a, InputCapture<'a>>,
activated: Activated, activated: Activated,
current_pos: Position, current_pos: Position,
) -> Result<(), CaptureError> { ) -> Result<(), CaptureError> {
@@ -494,10 +484,9 @@ async fn release_capture(
}; };
// 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);
let release_options = ReleaseOptions::default() input_capture
.set_activation_id(activated.activation_id()) .release(session, activated.activation_id(), Some(cursor_position))
.set_cursor_position(Some(cursor_position)); .await?;
input_capture.release(session, release_options).await?;
Ok(()) Ok(())
} }
@@ -572,7 +561,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
@@ -609,7 +598,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 */
@@ -618,10 +607,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,9 +40,7 @@ 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.13.9", default-features = false, features = [ ashpd = { version = "0.11.0", 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, SelectDevicesOptions}, remote_desktop::{DeviceType, RemoteDesktop},
}; };
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 { pub(crate) struct LibeiEmulation<'a> {
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, _remote_desktop: RemoteDesktop<'a>,
session: Session<RemoteDesktop>, session: Session<'a, RemoteDesktop<'a>>,
} }
/// Get the path to the RemoteDesktop token file /// Get the path to the RemoteDesktop token file
@@ -84,26 +84,27 @@ fn write_token(token: &str) -> io::Result<()> {
Ok(()) Ok(())
} }
async fn get_ei_fd() -> Result<(RemoteDesktop, Session<RemoteDesktop>, OwnedFd), ashpd::Error> { async fn get_ei_fd<'a>()
-> 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(Default::default()).await?; let session = remote_desktop.create_session().await?;
log::debug!("selecting devices ..."); log::debug!("selecting devices ...");
let options = SelectDevicesOptions::default() remote_desktop
.set_devices(DeviceType::Keyboard | DeviceType::Pointer) .select_devices(
.set_persist_mode(PersistMode::ExplicitlyRevoked) &session,
.set_restore_token(restore_token.as_deref()); DeviceType::Keyboard | DeviceType::Pointer,
remote_desktop.select_devices(&session, options).await?; restore_token.as_deref(),
PersistMode::ExplicitlyRevoked,
)
.await?;
log::info!("requesting permission for input emulation"); log::info!("requesting permission for input emulation");
let start_response = remote_desktop let start_response = remote_desktop.start(&session, None).await?.response()?;
.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() {
@@ -112,13 +113,11 @@ async fn get_ei_fd() -> Result<(RemoteDesktop, Session<RemoteDesktop>, OwnedFd),
} }
} }
let fd = remote_desktop let fd = remote_desktop.connect_to_eis(&session).await?;
.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);
@@ -153,14 +152,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,10 +1,7 @@
use ashpd::{ use ashpd::{
desktop::{ desktop::{
PersistMode, Session, PersistMode, Session,
remote_desktop::{ remote_desktop::{Axis, DeviceType, KeyState, RemoteDesktop},
Axis, DeviceType, KeyState, NotifyPointerAxisOptions, RemoteDesktop,
SelectDevicesOptions,
},
}, },
zbus::AsyncDrop, zbus::AsyncDrop,
}; };
@@ -20,31 +17,32 @@ use crate::error::EmulationError;
use super::{Emulation, EmulationHandle, error::XdpEmulationCreationError}; use super::{Emulation, EmulationHandle, error::XdpEmulationCreationError};
pub(crate) struct DesktopPortalEmulation { pub(crate) struct DesktopPortalEmulation<'a> {
proxy: RemoteDesktop, proxy: RemoteDesktop<'a>,
session: Session<RemoteDesktop>, session: Session<'a, RemoteDesktop<'a>>,
} }
impl DesktopPortalEmulation { impl<'a> DesktopPortalEmulation<'a> {
pub(crate) async fn new() -> Result<DesktopPortalEmulation, XdpEmulationCreationError> { pub(crate) async fn new() -> Result<DesktopPortalEmulation<'a>, 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(Default::default()).await?; let session = proxy.create_session().await?;
log::debug!("selecting devices ..."); log::debug!("selecting devices ...");
let options = SelectDevicesOptions::default() proxy
.set_devices(DeviceType::Keyboard | DeviceType::Pointer) .select_devices(
.set_persist_mode(PersistMode::ExplicitlyRevoked); &session,
proxy.select_devices(&session, options).await?; DeviceType::Keyboard | DeviceType::Pointer,
None,
PersistMode::ExplicitlyRevoked,
)
.await?;
log::info!("requesting permission for input emulation"); log::info!("requesting permission for input emulation");
let _devices = proxy let _devices = proxy.start(&session, None).await?.response()?;
.start(&session, None, Default::default())
.await?
.response()?;
log::debug!("started session"); log::debug!("started session");
let session = session; let session = session;
@@ -54,7 +52,7 @@ impl DesktopPortalEmulation {
} }
#[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,
@@ -64,7 +62,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, Default::default()) .notify_pointer_motion(&self.session, dx, dy)
.await?; .await?;
} }
PointerEvent::Button { PointerEvent::Button {
@@ -77,12 +75,7 @@ impl Emulation for DesktopPortalEmulation {
_ => KeyState::Pressed, _ => KeyState::Pressed,
}; };
self.proxy self.proxy
.notify_pointer_button( .notify_pointer_button(&self.session, button as i32, state)
&self.session,
button as i32,
state,
Default::default(),
)
.await?; .await?;
} }
PointerEvent::AxisDiscrete120 { axis, value } => { PointerEvent::AxisDiscrete120 { axis, value } => {
@@ -91,12 +84,7 @@ impl Emulation for DesktopPortalEmulation {
_ => Axis::Horizontal, _ => Axis::Horizontal,
}; };
self.proxy self.proxy
.notify_pointer_axis_discrete( .notify_pointer_axis_discrete(&self.session, axis, value / 120)
&self.session,
axis,
value / 120,
Default::default(),
)
.await?; .await?;
} }
PointerEvent::Axis { PointerEvent::Axis {
@@ -113,12 +101,7 @@ impl Emulation for DesktopPortalEmulation {
Axis::Horizontal => (value, 0.), Axis::Horizontal => (value, 0.),
}; };
self.proxy self.proxy
.notify_pointer_axis( .notify_pointer_axis(&self.session, dx, dy, true)
&self.session,
dx,
dy,
NotifyPointerAxisOptions::default().set_finish(true),
)
.await?; .await?;
} }
}, },
@@ -134,12 +117,7 @@ impl Emulation for DesktopPortalEmulation {
_ => KeyState::Pressed, _ => KeyState::Pressed,
}; };
self.proxy self.proxy
.notify_keyboard_keycode( .notify_keyboard_keycode(&self.session, key as i32, state)
&self.session,
key as i32,
state,
Default::default(),
)
.await?; .await?;
} }
KeyboardEvent::Modifiers { .. } => { KeyboardEvent::Modifiers { .. } => {
@@ -163,7 +141,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>(

View File

@@ -481,7 +481,7 @@ impl Config {
/// set authorized keys /// set authorized keys
pub fn set_authorized_keys(&mut self, fingerprints: HashMap<String, String>) { pub fn set_authorized_keys(&mut self, fingerprints: HashMap<String, String>) {
if self.config_toml.is_none() { if self.config_toml.is_none() {
self.config_toml = Some(Default::default()); self.config_toml = Default::default();
} }
self.config_toml self.config_toml
.as_mut() .as_mut()