use std::path::{Path, PathBuf};
use async_trait::async_trait;
use tokio_util::sync::CancellationToken;
use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PointerAxis {
Vertical,
Horizontal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PointerButton {
Left,
Middle,
Right,
Other(u32),
}
impl PointerButton {
pub fn evdev_code(self) -> u32 {
match self {
PointerButton::Left => 0x110,
PointerButton::Middle => 0x112,
PointerButton::Right => 0x111,
PointerButton::Other(code) => code,
}
}
pub fn from_evdev_code(code: u32) -> Self {
match code {
0x110 => PointerButton::Left,
0x112 => PointerButton::Middle,
0x111 => PointerButton::Right,
other => PointerButton::Other(other),
}
}
}
#[async_trait]
pub trait CompositorRuntime: Send + Sync {
async fn start(&mut self, resolution: Option<&str>) -> Result<()>;
async fn stop(&mut self) -> Result<()>;
fn id(&self) -> &str;
fn wayland_display(&self) -> &str;
fn runtime_dir(&self) -> &Path;
}
#[async_trait]
pub trait InputBackend: Send + Sync {
async fn press_keysym(&self, keysym: u32, cancel: &CancellationToken) -> Result<()>;
async fn key_down(&self, keysym: u32, cancel: &CancellationToken) -> Result<()>;
async fn key_up(&self, keysym: u32, cancel: &CancellationToken) -> Result<()>;
async fn pointer_motion_relative(
&self,
dx: f64,
dy: f64,
cancel: &CancellationToken,
) -> Result<()>;
async fn pointer_motion_absolute(
&self,
x: f64,
y: f64,
cancel: &CancellationToken,
) -> Result<()>;
async fn pointer_button_down(
&self,
button: PointerButton,
cancel: &CancellationToken,
) -> Result<()>;
async fn pointer_button_up(
&self,
button: PointerButton,
cancel: &CancellationToken,
) -> Result<()>;
async fn pointer_button(
&self,
button: PointerButton,
cancel: &CancellationToken,
) -> Result<()> {
self.pointer_button_down(button, cancel).await?;
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
self.pointer_button_up(button, cancel).await
}
async fn pointer_axis_discrete(
&self,
axis: PointerAxis,
steps: i32,
cancel: &CancellationToken,
) -> Result<()>;
}
pub async fn cancellable_tail(dur: std::time::Duration, cancel: &CancellationToken) {
tokio::select! {
_ = cancel.cancelled() => {}
_ = tokio::time::sleep(dur) => {}
}
}
pub struct StreamToken {
inner: Box<dyn std::any::Any + Send + Sync + 'static>,
stored_type: &'static str,
}
impl StreamToken {
pub fn new<T: std::any::Any + Send + Sync + 'static>(value: T) -> Self {
Self {
inner: Box::new(value),
stored_type: std::any::type_name::<T>(),
}
}
pub fn downcast<T: std::any::Any>(self) -> crate::error::Result<Box<T>> {
let stored = self.stored_type;
self.inner.downcast::<T>().map_err(|_| {
crate::error::Error::screenshot(format!(
"stream token type mismatch: expected {}, found {stored}",
std::any::type_name::<T>(),
))
})
}
}
pub struct PipeWireStream {
pub node_id: u32,
pub token: StreamToken,
}
#[async_trait]
pub trait CaptureBackend: Send + Sync {
async fn start_stream(&self) -> Result<PipeWireStream>;
async fn stop_stream(&self, stream: PipeWireStream) -> Result<()>;
fn pipewire_socket(&self) -> PathBuf;
async fn grab_screenshot(&self, stream: &PipeWireStream) -> Result<Vec<u8>> {
crate::capture::grab_png(stream.node_id, &self.pipewire_socket()).await
}
async fn start_recording(
&self,
stream: &PipeWireStream,
output_path: &Path,
bitrate: u32,
fps: u32,
) -> Result<crate::capture::VideoRecorder> {
crate::capture::VideoRecorder::start(
stream.node_id,
&self.pipewire_socket(),
output_path,
bitrate,
fps,
)
.await
}
async fn stop_recording(&self, recorder: crate::capture::VideoRecorder) -> Result<()> {
recorder.stop().await
}
}