Skip to main content

waydriver_input_mutter/
lib.rs

1//! Mutter implementation of [`waydriver::InputBackend`].
2//!
3//! Wraps an `Arc<MutterState>` obtained from [`waydriver_compositor_mutter::MutterCompositor::state`]
4//! and sends keyboard / pointer events via
5//! `org.gnome.Mutter.RemoteDesktop.Session.{NotifyKeyboardKeysym, NotifyPointerMotionRelative}`.
6
7use std::sync::Arc;
8
9use async_trait::async_trait;
10
11use waydriver::{Error, InputBackend, Result};
12use waydriver_compositor_mutter::MutterState;
13
14/// Mutter RemoteDesktop input backend.
15pub struct MutterInput {
16    state: Arc<MutterState>,
17}
18
19impl MutterInput {
20    /// Create a new input backend from shared compositor state.
21    pub fn new(state: Arc<MutterState>) -> Self {
22        Self { state }
23    }
24}
25
26#[async_trait]
27impl InputBackend for MutterInput {
28    async fn press_keysym(&self, keysym: u32) -> Result<()> {
29        self.key_down(keysym).await?;
30        // Mutter's RemoteDesktop needs a short gap between press and
31        // release or the app sees a 0ms keystroke that some handlers drop.
32        tokio::time::sleep(std::time::Duration::from_millis(20)).await;
33        self.key_up(keysym).await?;
34        // Tail delay so back-to-back calls from a test loop don't stack up
35        // faster than GTK can process them.
36        tokio::time::sleep(std::time::Duration::from_millis(30)).await;
37        Ok(())
38    }
39
40    async fn key_down(&self, keysym: u32) -> Result<()> {
41        self.state
42            .conn
43            .call_method(
44                Some("org.gnome.Mutter.RemoteDesktop"),
45                self.state.rd_session_path.as_str(),
46                Some("org.gnome.Mutter.RemoteDesktop.Session"),
47                "NotifyKeyboardKeysym",
48                &(keysym, true),
49            )
50            .await
51            .map_err(|e| Error::Process(format!("NotifyKeyboardKeysym press: {e}")))?;
52        Ok(())
53    }
54
55    async fn key_up(&self, keysym: u32) -> Result<()> {
56        self.state
57            .conn
58            .call_method(
59                Some("org.gnome.Mutter.RemoteDesktop"),
60                self.state.rd_session_path.as_str(),
61                Some("org.gnome.Mutter.RemoteDesktop.Session"),
62                "NotifyKeyboardKeysym",
63                &(keysym, false),
64            )
65            .await
66            .map_err(|e| Error::Process(format!("NotifyKeyboardKeysym release: {e}")))?;
67        Ok(())
68    }
69
70    async fn pointer_motion_relative(&self, dx: f64, dy: f64) -> Result<()> {
71        self.state
72            .conn
73            .call_method(
74                Some("org.gnome.Mutter.RemoteDesktop"),
75                self.state.rd_session_path.as_str(),
76                Some("org.gnome.Mutter.RemoteDesktop.Session"),
77                "NotifyPointerMotionRelative",
78                &(dx, dy),
79            )
80            .await
81            .map_err(|e| Error::Process(format!("NotifyPointerMotionRelative: {e}")))?;
82        Ok(())
83    }
84
85    async fn pointer_button(&self, button: u32) -> Result<()> {
86        let button: i32 = button
87            .try_into()
88            .map_err(|_| Error::Process(format!("button code {button} exceeds i32::MAX")))?;
89        self.state
90            .conn
91            .call_method(
92                Some("org.gnome.Mutter.RemoteDesktop"),
93                self.state.rd_session_path.as_str(),
94                Some("org.gnome.Mutter.RemoteDesktop.Session"),
95                "NotifyPointerButton",
96                &(button, true),
97            )
98            .await
99            .map_err(|e| Error::Process(format!("NotifyPointerButton press: {e}")))?;
100        tokio::time::sleep(std::time::Duration::from_millis(20)).await;
101        self.state
102            .conn
103            .call_method(
104                Some("org.gnome.Mutter.RemoteDesktop"),
105                self.state.rd_session_path.as_str(),
106                Some("org.gnome.Mutter.RemoteDesktop.Session"),
107                "NotifyPointerButton",
108                &(button, false),
109            )
110            .await
111            .map_err(|e| Error::Process(format!("NotifyPointerButton release: {e}")))?;
112        tokio::time::sleep(std::time::Duration::from_millis(30)).await;
113        Ok(())
114    }
115}