use std::time::Duration;
use ashpd::desktop::input_capture::{
Barrier, BarrierID, Capabilities, CreateSessionOptions, InputCapture, ReleaseOptions,
StartOptions,
};
use futures_util::StreamExt;
#[tokio::main]
async fn main() -> ashpd::Result<()> {
let input_capture = InputCapture::new().await?;
let capabilities = Capabilities::Keyboard | Capabilities::Pointer;
let session = match input_capture.create_session2(Default::default()).await {
Ok(session) => {
let start_request = input_capture
.start(
&session,
None,
StartOptions::default().set_capabilities(capabilities),
)
.await?;
let _start_response = start_request.response()?;
session
}
Err(ashpd::Error::RequiresVersion(_want, _have)) => {
let (session, _caps) = input_capture
.create_session(
None,
CreateSessionOptions::default().set_capabilities(capabilities),
)
.await?;
session
}
Err(err) => {
return Err(err);
}
};
let zones_response = input_capture.zones(&session, Default::default()).await?;
let zones = zones_response.response()?;
println!("Available Zones: {:?}", zones);
let barriers: Vec<Barrier> =
zones
.regions()
.iter()
.enumerate()
.flat_map(|(n, region)| {
let x = region.x_offset();
let y = region.y_offset();
let width = region.width() as i32;
let height = region.height() as i32;
let edges = [
("left", (x, y, x, y + height - 1)),
("right", (x + width - 1, y, x + width - 1, y + height - 1)),
("top", (x, y, x + width - 1, y)),
("bottom", (x, y + height - 1, x + width - 1, y + height - 1)),
];
edges.into_iter().enumerate().map(move |(edge_idx, (edge_name, position))| {
let barrier_id = (n * 4 + edge_idx + 1) as u32;
let id = BarrierID::new(barrier_id).expect("barrier-id must be non-zero");
println!(
"Creating barrier {} ({} edge) at position {:?} for zone ({}, {}, {}x{})",
id,
edge_name,
position,
region.x_offset(),
region.y_offset(),
region.width(),
region.height()
);
Barrier::new(id, position)
})
})
.collect();
let barriers_response = input_capture
.set_pointer_barriers(&session, &barriers, zones.zone_set(), Default::default())
.await?;
let result = barriers_response.response()?;
let failed = result.failed_barriers();
if !failed.is_empty() {
println!("Failed barriers: {:?}", failed);
} else {
println!("All barriers set successfully");
}
let eifd = input_capture
.connect_to_eis(&session, Default::default())
.await?;
println!("Connected to EIS, fd: {:?}", eifd);
input_capture.enable(&session, Default::default()).await?;
println!("Input capture enabled - move cursor to any edge to trigger");
let mut activated_stream = input_capture.receive_activated().await?;
let mut deactivated_stream = input_capture.receive_deactivated().await?;
println!("\nWaiting for input capture to be triggered...");
println!("Press Ctrl+C to exit\n");
loop {
tokio::select! {
Some(activated) = activated_stream.next() => {
println!("Input capture ACTIVATED!");
println!(" Activation ID: {:?}", activated.activation_id());
println!(" Cursor position: {:?}", activated.cursor_position());
println!(" Barrier ID: {:?}", activated.barrier_id());
println!(" (Input events would now be captured via libei)");
println!(" Waiting 5 seconds before releasing...");
tokio::time::sleep(Duration::from_secs(5)).await;
input_capture.release(&session, ReleaseOptions::default().set_activation_id(activated.activation_id())).await?;
println!(" Released input capture");
tokio::time::sleep(Duration::from_secs(1)).await;
input_capture.enable(&session, Default::default()).await?;
}
Some(deactivated) = deactivated_stream.next() => {
println!("Input capture DEACTIVATED");
println!(" Activation ID: {:?}", deactivated.activation_id());
}
}
}
}