use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use tokio_util::sync::CancellationToken;
use waydriver::backend::cancellable_tail;
use waydriver::{Error, InputBackend, PointerAxis, PointerButton, Result};
use waydriver_compositor_mutter::MutterState;
pub struct MutterInput {
state: Arc<MutterState>,
}
impl MutterInput {
pub fn new(state: Arc<MutterState>) -> Self {
Self { state }
}
async fn call_rd_session<Args>(
&self,
method: &'static str,
args: &Args,
op: &'static str,
) -> Result<zbus::Message>
where
Args: serde::Serialize + zbus::zvariant::DynamicType,
{
self.state
.conn()
.call_method(
Some("org.gnome.Mutter.RemoteDesktop"),
self.state.rd_session_path(),
Some("org.gnome.Mutter.RemoteDesktop.Session"),
method,
args,
)
.await
.map_err(|e| Error::process_with(op, e))
}
}
#[async_trait]
impl InputBackend for MutterInput {
async fn press_keysym(&self, keysym: u32, cancel: &CancellationToken) -> Result<()> {
self.key_down(keysym, cancel).await?;
tokio::time::sleep(Duration::from_millis(20)).await;
self.key_up(keysym, cancel).await?;
cancellable_tail(Duration::from_millis(30), cancel).await;
Ok(())
}
async fn key_down(&self, keysym: u32, _cancel: &CancellationToken) -> Result<()> {
self.call_rd_session(
"NotifyKeyboardKeysym",
&(keysym, true),
"NotifyKeyboardKeysym press",
)
.await?;
Ok(())
}
async fn key_up(&self, keysym: u32, _cancel: &CancellationToken) -> Result<()> {
self.call_rd_session(
"NotifyKeyboardKeysym",
&(keysym, false),
"NotifyKeyboardKeysym release",
)
.await?;
Ok(())
}
async fn pointer_motion_relative(
&self,
dx: f64,
dy: f64,
_cancel: &CancellationToken,
) -> Result<()> {
self.call_rd_session(
"NotifyPointerMotionRelative",
&(dx, dy),
"NotifyPointerMotionRelative",
)
.await?;
Ok(())
}
async fn pointer_motion_absolute(
&self,
x: f64,
y: f64,
_cancel: &CancellationToken,
) -> Result<()> {
let stream = self
.state
.active_stream_path_lock()?
.clone()
.ok_or_else(|| {
Error::process("no active ScreenCast stream; absolute pointer motion needs one")
})?;
self.call_rd_session(
"NotifyPointerMotionAbsolute",
&(stream.as_str(), x, y),
"NotifyPointerMotionAbsolute",
)
.await?;
Ok(())
}
async fn pointer_button_down(
&self,
button: PointerButton,
_cancel: &CancellationToken,
) -> Result<()> {
let button = i32::try_from(button.evdev_code())
.map_err(|e| Error::process_with("NotifyPointerButton press", e))?;
self.call_rd_session(
"NotifyPointerButton",
&(button, true),
"NotifyPointerButton press",
)
.await?;
Ok(())
}
async fn pointer_button_up(
&self,
button: PointerButton,
cancel: &CancellationToken,
) -> Result<()> {
let button = i32::try_from(button.evdev_code())
.map_err(|e| Error::process_with("NotifyPointerButton release", e))?;
self.call_rd_session(
"NotifyPointerButton",
&(button, false),
"NotifyPointerButton release",
)
.await?;
cancellable_tail(Duration::from_millis(30), cancel).await;
Ok(())
}
async fn pointer_axis_discrete(
&self,
axis: PointerAxis,
steps: i32,
cancel: &CancellationToken,
) -> Result<()> {
let axis_code: u32 = match axis {
PointerAxis::Vertical => 0,
PointerAxis::Horizontal => 1,
};
self.call_rd_session(
"NotifyPointerAxisDiscrete",
&(axis_code, steps),
"NotifyPointerAxisDiscrete",
)
.await?;
cancellable_tail(Duration::from_millis(30), cancel).await;
Ok(())
}
}