waydriver-input-mutter 0.2.0

Mutter RemoteDesktop keyboard and pointer input backend for waydriver
Documentation
//! Mutter implementation of [`waydriver::InputBackend`].
//!
//! Wraps an `Arc<MutterState>` obtained from [`waydriver_compositor_mutter::MutterCompositor::state`]
//! and sends keyboard / pointer events via
//! `org.gnome.Mutter.RemoteDesktop.Session.{NotifyKeyboardKeysym, NotifyPointerMotionRelative}`.

use std::sync::Arc;

use async_trait::async_trait;

use waydriver::{Error, InputBackend, Result};
use waydriver_compositor_mutter::MutterState;

/// Mutter RemoteDesktop input backend.
pub struct MutterInput {
    state: Arc<MutterState>,
}

impl MutterInput {
    /// Create a new input backend from shared compositor state.
    pub fn new(state: Arc<MutterState>) -> Self {
        Self { state }
    }
}

#[async_trait]
impl InputBackend for MutterInput {
    async fn press_keysym(&self, keysym: u32) -> Result<()> {
        self.key_down(keysym).await?;
        // Mutter's RemoteDesktop needs a short gap between press and
        // release or the app sees a 0ms keystroke that some handlers drop.
        tokio::time::sleep(std::time::Duration::from_millis(20)).await;
        self.key_up(keysym).await?;
        // Tail delay so back-to-back calls from a test loop don't stack up
        // faster than GTK can process them.
        tokio::time::sleep(std::time::Duration::from_millis(30)).await;
        Ok(())
    }

    async fn key_down(&self, keysym: u32) -> Result<()> {
        self.state
            .conn
            .call_method(
                Some("org.gnome.Mutter.RemoteDesktop"),
                self.state.rd_session_path.as_str(),
                Some("org.gnome.Mutter.RemoteDesktop.Session"),
                "NotifyKeyboardKeysym",
                &(keysym, true),
            )
            .await
            .map_err(|e| Error::Process(format!("NotifyKeyboardKeysym press: {e}")))?;
        Ok(())
    }

    async fn key_up(&self, keysym: u32) -> Result<()> {
        self.state
            .conn
            .call_method(
                Some("org.gnome.Mutter.RemoteDesktop"),
                self.state.rd_session_path.as_str(),
                Some("org.gnome.Mutter.RemoteDesktop.Session"),
                "NotifyKeyboardKeysym",
                &(keysym, false),
            )
            .await
            .map_err(|e| Error::Process(format!("NotifyKeyboardKeysym release: {e}")))?;
        Ok(())
    }

    async fn pointer_motion_relative(&self, dx: f64, dy: f64) -> Result<()> {
        self.state
            .conn
            .call_method(
                Some("org.gnome.Mutter.RemoteDesktop"),
                self.state.rd_session_path.as_str(),
                Some("org.gnome.Mutter.RemoteDesktop.Session"),
                "NotifyPointerMotionRelative",
                &(dx, dy),
            )
            .await
            .map_err(|e| Error::Process(format!("NotifyPointerMotionRelative: {e}")))?;
        Ok(())
    }

    async fn pointer_button(&self, button: u32) -> Result<()> {
        let button: i32 = button
            .try_into()
            .map_err(|_| Error::Process(format!("button code {button} exceeds i32::MAX")))?;
        self.state
            .conn
            .call_method(
                Some("org.gnome.Mutter.RemoteDesktop"),
                self.state.rd_session_path.as_str(),
                Some("org.gnome.Mutter.RemoteDesktop.Session"),
                "NotifyPointerButton",
                &(button, true),
            )
            .await
            .map_err(|e| Error::Process(format!("NotifyPointerButton press: {e}")))?;
        tokio::time::sleep(std::time::Duration::from_millis(20)).await;
        self.state
            .conn
            .call_method(
                Some("org.gnome.Mutter.RemoteDesktop"),
                self.state.rd_session_path.as_str(),
                Some("org.gnome.Mutter.RemoteDesktop.Session"),
                "NotifyPointerButton",
                &(button, false),
            )
            .await
            .map_err(|e| Error::Process(format!("NotifyPointerButton release: {e}")))?;
        tokio::time::sleep(std::time::Duration::from_millis(30)).await;
        Ok(())
    }
}