auv-driver-linux 0.0.22

Linux driver for AUV
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

use auv_driver_common::{Driver, DriverDescriptor, DriverResult, DriverSession};

use crate::descriptor::{LinuxDriverDescriptor, linux_driver_descriptor};
use crate::native::portal::{ClipboardSession, RestoreTokenStore, ScreenCastSession};

/// Select the input mechanism explicitly; delivery failures never switch backends.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum InputBackend {
  #[default]
  Portal,
  Uinput,
}

#[derive(Clone, Debug, Default)]
pub struct LinuxDriver {
  input_backend: InputBackend,
  portal_state_root: Option<PathBuf>,
  #[cfg(target_os = "linux")]
  portal_app_id: Option<ashpd::AppID>,
  #[cfg(not(target_os = "linux"))]
  portal_app_id: Option<String>,
}

impl LinuxDriver {
  pub fn new() -> Self {
    Self::default()
  }

  /// Selects the foreground input mechanism for newly opened sessions.
  pub fn with_input_backend(mut self, backend: InputBackend) -> Self {
    self.input_backend = backend;
    self
  }

  /// Persists opaque portal restore tokens below a daemon-owned state root.
  pub fn with_portal_state_root(mut self, root: PathBuf) -> Self {
    self.portal_state_root = Some(root);
    self
  }

  /// Associates every Portal D-Bus connection with the caller's desktop ID.
  /// The caller must install a matching desktop entry before opening a Portal.
  pub fn with_portal_app_id(mut self, app_id: String) -> auv_driver_common::DriverResult<Self> {
    #[cfg(target_os = "linux")]
    let app_id = app_id.parse::<ashpd::AppID>().map_err(|error| crate::error::invalid_input(error.to_string()))?;
    self.portal_app_id = Some(app_id);
    Ok(self)
  }

  pub fn linux_descriptor(&self) -> LinuxDriverDescriptor {
    linux_driver_descriptor()
  }
}

#[derive(Clone, Debug)]
pub struct LinuxDriverSession {
  pub(crate) state: Arc<Mutex<LinuxDriverSessionState>>,
}

#[derive(Debug, Default)]
pub(crate) struct LinuxDriverSessionState {
  // TODO(linux-portal-remote-desktop-shared-session): input and clipboard use
  // separate RemoteDesktop sessions, so live validation still requests those
  // permissions separately. Merge only after an owner-approved slice defines
  // combined RequestClipboard/SelectDevices/Start and clipboard transfer
  // thread ownership.
  // TODO(linux-portal-clipboard-restore): persistent clipboard authorization
  // is deferred with that shared-session slice because GNOME currently hangs
  // when the standalone clipboard session calls SelectDevices with no devices.
  pub(crate) clipboard_session: Option<ClipboardSession>,
  pub(crate) input_session: Option<Arc<Mutex<crate::input::InputSession>>>,
  pub(crate) input_backend: InputBackend,
  pub(crate) screencast_session: Option<ScreenCastSession>,
  pub(crate) restore_tokens: Option<RestoreTokenStore>,
  #[cfg(target_os = "linux")]
  pub(crate) portal_app_id: Option<ashpd::AppID>,
  #[cfg(not(target_os = "linux"))]
  pub(crate) portal_app_id: Option<String>,
}

impl LinuxDriverSession {
  pub fn linux_descriptor(&self) -> LinuxDriverDescriptor {
    linux_driver_descriptor()
  }
}

impl Driver for LinuxDriver {
  type Session = LinuxDriverSession;

  fn descriptor(&self) -> DriverDescriptor {
    self.linux_descriptor().as_driver_descriptor()
  }

  fn open_local(&self) -> DriverResult<Self::Session> {
    Ok(LinuxDriverSession {
      state: Arc::new(Mutex::new(LinuxDriverSessionState {
        input_backend: self.input_backend,
        restore_tokens: self.portal_state_root.clone().map(RestoreTokenStore::new),
        portal_app_id: self.portal_app_id.clone(),
        ..Default::default()
      })),
    })
  }
}

impl DriverSession for LinuxDriverSession {
  fn descriptor(&self) -> DriverDescriptor {
    self.linux_descriptor().as_driver_descriptor()
  }
}

#[cfg(test)]
#[path = "driver_test.rs"]
mod tests;