Colpetto is a modern Rust library that provides asynchronous bindings for libinput, enabling efficient handling of input device events on Linux systems. Built on tokio, it offers a stream-based API that seamlessly integrates with async applications while maintaining low latency and efficient resource usage for real-time operations.
Colpetto transforms libinput's traditional callback-based interface into an ergonomic Rust API with:
- Stream-based event handling powered by tokio
- Low-overhead event polling optimized for performance
- Flexible logging system with built-in basic loggers
Here's a basic example demonstrating event handling using rustix:
use colpetto::{event::AsRawEvent, Libinput, Result};
use rustix::{
fd::{FromRawFd, IntoRawFd, OwnedFd},
fs::{open, Mode, OFlags},
io::Errno,
};
use tokio_stream::StreamExt;
#[tokio::main]
async fn main() -> Result<()> {
let mut libinput = Libinput::new(
|path, flags| {
open(path, OFlags::from_bits_retain(flags as u32), Mode::empty())
.map(IntoRawFd::into_raw_fd)
.map_err(Errno::raw_os_error)
},
|fd| drop(unsafe { OwnedFd::from_raw_fd(fd) }),
)?;
libinput.assign_seat(c"seat0")?;
let mut stream = libinput.event_stream()?;
while let Some(event) = stream.try_next().await? {
println!(
"Got \"{}\" event from \"{}\"",
event.event_type(),
event.device().name().to_string_lossy()
)
}
Ok(())
}
For more examples, check out the examples directory:
- Simple: Basic event handling demonstration
- Print Keys: Advanced example with custom logging and keyboard event handling
Colpetto requires libinput 1.22 or higher (current version in Debian stable).
Contributions to support older versions are welcome provided they:
- Don't compromise the existing async functionality
- Maintain API consistency
- Include appropriate version-specific tests if applicable
If you're using an older version of libinput, you may need to upgrade your system packages or consider using input-rs which supports earlier versions.
While input-rs is an established alternative, Colpetto takes a different approach in several key areas:
Colpetto uses direct function passing for device management, while input-rs employs a trait-based approach:
// Colpetto: Function-based approach with closure support
let mut libinput = Libinput::new(
|path, flags| { // Open function
open(path, OFlags::from_bits_retain(flags as u32), Mode::empty())
.map(IntoRawFd::into_raw_fd)
.map_err(Errno::raw_os_error)
},
|fd| drop(unsafe { OwnedFd::from_raw_fd(fd) }) // Close function
)?;
// input-rs: Trait-based approach
struct Interface;
impl LibinputInterface for Interface {
fn open_restricted(&mut self, path: &Path, flags: i32) -> Result<OwnedFd, i32> {
open(path, OFlags::from_bits_retain(flags as u32), Mode::empty())
.map_err(Errno::raw_os_error)
}
fn close_restricted(&mut self, fd: OwnedFd) {
drop(fd);
}
}
let mut input = Libinput::new_with_udev(Interface);
While input-rs supports a wider range of libinput versions (1.9+), Colpetto focuses on modern versions (1.22+) to provide comprehensive bindings for newer libinput features and async capabilities.
- Native tokio integration for async/await support with stream-based event
handling via
event_stream()
- Safe handling of non-UTF8 strings using
CStr
instead of implicit panics - Comprehensive event type safety through more exhaustive enum matching
- More robust context lifetime management
This project is licensed under the Apache-2.0 License. For more information, please see the LICENSE file.