feat(teipe): initial commit
This commit is contained in:
16
libeirs/Cargo.toml
Normal file
16
libeirs/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "libeirs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen.workspace = true
|
||||
anyhow.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true, features = ["rt", "net"] }
|
||||
tokio-stream = { workspace = true }
|
||||
log = { workspace = true, features = ["kv"] }
|
||||
memmap2 = "0.9.4"
|
||||
nix = { version = "0.29", features = ["poll"] }
|
||||
anyhow.workspace = true
|
||||
25
libeirs/build.rs
Normal file
25
libeirs/build.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let bindings = bindgen::builder()
|
||||
.header("/usr/include/libei-1.0/libei.h")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.allowlist_item("ei_.*")
|
||||
.default_enum_style(bindgen::EnumVariation::NewType {
|
||||
is_bitfield: false,
|
||||
is_global: false,
|
||||
})
|
||||
.no_debug("ei_event_type") // Implemented manually to use Display
|
||||
.bitfield_enum("ei_device_capability")
|
||||
.generate()?;
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let out_file = out_path.join("libei.rs");
|
||||
bindings
|
||||
.write_to_file(out_file)
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
println!("cargo:rustc-link-lib=ei");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
129
libeirs/src/device.rs
Normal file
129
libeirs/src/device.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use std::{borrow::Borrow, ffi::CStr, fmt::Display, ops::Deref, ptr::NonNull};
|
||||
|
||||
use crate::{eirc::Ei, ffi::ei_device};
|
||||
|
||||
use super::{ffi, keymap::Keymap};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct Device {
|
||||
device: DeviceRef,
|
||||
}
|
||||
|
||||
impl Drop for Device {
|
||||
fn drop(&mut self) {
|
||||
log::trace!("Device dropped");
|
||||
let device = self.as_raw();
|
||||
unsafe {
|
||||
ffi::ei_device_close(device);
|
||||
ffi::ei_device_unref(device)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Device {
|
||||
type Target = DeviceRef;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.device
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Device {
|
||||
fn clone(&self) -> Self {
|
||||
self.device.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<DeviceRef> for Device {
|
||||
fn borrow(&self) -> &DeviceRef {
|
||||
&self.device
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<DeviceRef> for Device {
|
||||
fn as_ref(&self) -> &DeviceRef {
|
||||
&self.device
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Device {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.device)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<DeviceRef> for Device {
|
||||
fn eq(&self, other: &DeviceRef) -> bool {
|
||||
self.device == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct DeviceRef {
|
||||
device: NonNull<ffi::ei_device>,
|
||||
}
|
||||
|
||||
impl DeviceRef {
|
||||
pub(crate) fn new(device: *mut ei_device) -> Option<Self> {
|
||||
NonNull::new(device).map(|device| Self { device })
|
||||
}
|
||||
pub fn start_emulating(&self, sequence: u32) {
|
||||
unsafe { ffi::ei_device_start_emulating(self.as_raw(), sequence) }
|
||||
}
|
||||
|
||||
pub fn keyboard_key(&self, key: u32, pressed: bool) {
|
||||
unsafe {
|
||||
ffi::ei_device_keyboard_key(self.as_raw(), key, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn frame(&self, time: Option<u64>) {
|
||||
let time = time.unwrap_or_else(|| unsafe {
|
||||
let ei = ffi::ei_device_get_context(self.as_raw());
|
||||
ffi::ei_now(ei)
|
||||
});
|
||||
unsafe {
|
||||
ffi::ei_device_frame(self.as_raw(), time);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_raw(&self) -> *mut ffi::ei_device {
|
||||
self.device.as_ptr()
|
||||
}
|
||||
|
||||
pub fn get_keymap(&self) -> Option<Keymap> {
|
||||
Keymap::from_device(self)
|
||||
}
|
||||
|
||||
pub fn get_context(&self) -> Ei {
|
||||
unsafe { Ei::from_raw(ffi::ei_device_get_context(self.as_raw())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DeviceRef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = unsafe { CStr::from_ptr(ffi::ei_device_get_name(self.as_raw())) }
|
||||
.to_str()
|
||||
.unwrap();
|
||||
write!(f, "{str}",)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for DeviceRef {
|
||||
type Owned = Device;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
let ptr = unsafe { ffi::ei_device_ref(self.as_raw()) };
|
||||
Device {
|
||||
device: DeviceRef {
|
||||
device: NonNull::new(ptr).unwrap(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Device> for DeviceRef {
|
||||
fn eq(&self, other: &Device) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
107
libeirs/src/eirc.rs
Normal file
107
libeirs/src/eirc.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use std::{
|
||||
os::fd::{BorrowedFd, IntoRawFd, OwnedFd},
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use super::{events::Event, ffi};
|
||||
|
||||
const CLIENT_NAME: &std::ffi::CStr = c"teipe";
|
||||
|
||||
pub struct Ei {
|
||||
ei: *mut ffi::ei,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn null_handler(
|
||||
_ei: *mut ffi::ei,
|
||||
_priority: ffi::ei_log_priority,
|
||||
_message: *const ::std::os::raw::c_char,
|
||||
_context: *mut ffi::ei_log_context,
|
||||
) {
|
||||
// no op
|
||||
}
|
||||
|
||||
unsafe extern "C" fn log_handler(
|
||||
_ei: *mut ffi::ei,
|
||||
priority: ffi::ei_log_priority,
|
||||
message: *const ::std::os::raw::c_char,
|
||||
ctx: *mut ffi::ei_log_context,
|
||||
) {
|
||||
let (file, func, line, msg) = unsafe {
|
||||
(
|
||||
std::ffi::CStr::from_ptr(ffi::ei_log_context_get_file(ctx)),
|
||||
std::ffi::CStr::from_ptr(ffi::ei_log_context_get_func(ctx)),
|
||||
ffi::ei_log_context_get_line(ctx),
|
||||
std::ffi::CStr::from_ptr(message),
|
||||
)
|
||||
};
|
||||
log::log!(
|
||||
target:"libei",
|
||||
priority.into(),
|
||||
file:?, func:?, line:?;
|
||||
"{msg:?}"
|
||||
);
|
||||
}
|
||||
|
||||
impl Ei {
|
||||
pub(crate) fn new_sender(fd: OwnedFd) -> Self {
|
||||
// FIX: handle errors
|
||||
log::info!("Creating ei");
|
||||
let ei = unsafe { ffi::ei_new_sender(std::ptr::null_mut()) };
|
||||
|
||||
unsafe {
|
||||
if let Some(level) = log::max_level().to_level() {
|
||||
ffi::ei_log_set_handler(ei, Some(log_handler));
|
||||
ffi::ei_log_set_priority(ei, level.into());
|
||||
} else {
|
||||
ffi::ei_log_set_handler(ei, Some(null_handler));
|
||||
}
|
||||
ffi::ei_configure_name(ei, CLIENT_NAME.as_ptr());
|
||||
ffi::ei_setup_backend_fd(ei, fd.into_raw_fd());
|
||||
}
|
||||
Self { ei }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn from_raw(ei: *mut ffi::ei) -> Self {
|
||||
unsafe {
|
||||
ffi::ei_ref(ei);
|
||||
}
|
||||
Self { ei }
|
||||
}
|
||||
|
||||
pub fn get_fd(&self) -> BorrowedFd {
|
||||
unsafe {
|
||||
let fd = ffi::ei_get_fd(self.ei);
|
||||
BorrowedFd::borrow_raw(fd)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch(&self) {
|
||||
log::trace!("Dispatch");
|
||||
unsafe {
|
||||
ffi::ei_dispatch(self.ei);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_event(&self) -> Option<Event> {
|
||||
let e = unsafe { ffi::ei_get_event(self.ei) };
|
||||
NonNull::new(e).map(Event::new)
|
||||
}
|
||||
|
||||
pub fn now(&self) -> u64 {
|
||||
unsafe { ffi::ei_now(self.ei) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Ei {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
ei: unsafe { ffi::ei_ref(self.ei) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Ei {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::ei_unref(self.ei) };
|
||||
}
|
||||
}
|
||||
47
libeirs/src/events.rs
Normal file
47
libeirs/src/events.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use std::{fmt::Display, ptr::NonNull};
|
||||
|
||||
use crate::device::DeviceRef;
|
||||
|
||||
use super::{
|
||||
ffi::{self, ei_event},
|
||||
seat::Seat,
|
||||
};
|
||||
|
||||
pub use ffi::ei_event_type as EventType;
|
||||
pub struct Event(NonNull<ei_event>);
|
||||
|
||||
impl Drop for Event {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::ei_event_unref(self.as_raw()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn new(event: std::ptr::NonNull<ffi::ei_event>) -> Self {
|
||||
Self(event)
|
||||
}
|
||||
|
||||
pub fn as_raw(&self) -> *mut ei_event {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
|
||||
pub fn get_type(&self) -> EventType {
|
||||
unsafe { ffi::ei_event_get_type(self.as_raw()) }
|
||||
}
|
||||
|
||||
pub fn get_seat(&self) -> Option<Seat> {
|
||||
Seat::from_event(self)
|
||||
}
|
||||
|
||||
pub fn get_device(&self) -> Option<DeviceRef> {
|
||||
DeviceRef::new(unsafe { ffi::ei_event_get_device(self.as_raw()) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Event {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Event")
|
||||
.field("Type", &self.get_type())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
49
libeirs/src/ffi.rs
Normal file
49
libeirs/src/ffi.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/libei.rs"));
|
||||
|
||||
impl From<ei_log_priority> for log::Level {
|
||||
fn from(value: ei_log_priority) -> Self {
|
||||
match value {
|
||||
ei_log_priority::EI_LOG_PRIORITY_DEBUG => log::Level::Debug,
|
||||
ei_log_priority::EI_LOG_PRIORITY_INFO => log::Level::Info,
|
||||
ei_log_priority::EI_LOG_PRIORITY_WARNING => log::Level::Warn,
|
||||
ei_log_priority::EI_LOG_PRIORITY_ERROR => log::Level::Error,
|
||||
_ => log::Level::Debug,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<log::Level> for ei_log_priority {
|
||||
fn from(value: log::Level) -> Self {
|
||||
match value {
|
||||
log::Level::Trace | log::Level::Debug => ei_log_priority::EI_LOG_PRIORITY_DEBUG,
|
||||
log::Level::Info => ei_log_priority::EI_LOG_PRIORITY_INFO,
|
||||
log::Level::Warn => ei_log_priority::EI_LOG_PRIORITY_WARNING,
|
||||
log::Level::Error => ei_log_priority::EI_LOG_PRIORITY_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ei_event_type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ei_event_type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = unsafe { CStr::from_ptr(ei_event_type_to_string(*self)) }
|
||||
.to_str()
|
||||
.unwrap();
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
49
libeirs/src/keymap.rs
Normal file
49
libeirs/src/keymap.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use std::{error::Error, fmt::Debug, ptr::NonNull};
|
||||
|
||||
use super::{device::DeviceRef, ffi};
|
||||
use ffi::ei_keymap_type as KeymapType;
|
||||
|
||||
pub struct Keymap(NonNull<ffi::ei_keymap>);
|
||||
|
||||
impl Keymap {
|
||||
pub(crate) fn from_device(device: &DeviceRef) -> Option<Self> {
|
||||
let keymap = unsafe {
|
||||
let keymap = ffi::ei_device_keyboard_get_keymap(device.as_raw());
|
||||
ffi::ei_keymap_ref(keymap)
|
||||
};
|
||||
NonNull::new(keymap).map(Self)
|
||||
}
|
||||
|
||||
pub fn as_raw(&self) -> *mut ffi::ei_keymap {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
|
||||
pub fn get_type(&self) -> KeymapType {
|
||||
unsafe { ffi::ei_keymap_get_type(self.as_raw()) }
|
||||
}
|
||||
|
||||
pub fn mmap_keymap(&self) -> Result<impl AsRef<[u8]>, impl Error> {
|
||||
unsafe {
|
||||
let fd = ffi::ei_keymap_get_fd(self.as_raw());
|
||||
memmap2::Mmap::map(fd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Keymap {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("Keymap").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Keymap {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::ei_keymap_unref(self.as_raw()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Keymap {
|
||||
fn clone(&self) -> Self {
|
||||
Self(unsafe { NonNull::new_unchecked(ffi::ei_keymap_ref(self.as_raw())) })
|
||||
}
|
||||
}
|
||||
13
libeirs/src/lib.rs
Normal file
13
libeirs/src/lib.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
mod device;
|
||||
mod eirc;
|
||||
pub mod events;
|
||||
mod ffi;
|
||||
mod keymap;
|
||||
pub mod seat;
|
||||
mod sender;
|
||||
|
||||
pub use device::*;
|
||||
pub use events::*;
|
||||
pub use keymap::Keymap;
|
||||
pub use seat::Seat;
|
||||
pub use sender::Sender;
|
||||
41
libeirs/src/seat.rs
Normal file
41
libeirs/src/seat.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use std::{ffi::CStr, fmt::Display, ptr::NonNull};
|
||||
|
||||
use super::{events::Event, ffi};
|
||||
|
||||
pub use ffi::ei_device_capability as DeviceCapability;
|
||||
|
||||
pub struct Seat {
|
||||
seat: NonNull<ffi::ei_seat>,
|
||||
}
|
||||
|
||||
impl Seat {
|
||||
pub fn from_event(event: &Event) -> Option<Self> {
|
||||
let seat = unsafe {
|
||||
let seat = ffi::ei_event_get_seat(event.as_raw());
|
||||
ffi::ei_seat_ref(seat)
|
||||
};
|
||||
NonNull::new(seat).map(|seat| Self { seat })
|
||||
}
|
||||
|
||||
pub fn bind_capabilities(&self, capabilities: DeviceCapability) {
|
||||
unsafe {
|
||||
ffi::ei_seat_bind_capabilities(self.seat.as_ptr(), capabilities, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Seat {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = unsafe { CStr::from_ptr(ffi::ei_seat_get_name(self.seat.as_ptr())) }
|
||||
.to_str()
|
||||
.unwrap();
|
||||
write!(f, "{str}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Seat {
|
||||
fn drop(&mut self) {
|
||||
log::trace!("Dropping seat {self}");
|
||||
unsafe { ffi::ei_seat_unref(self.seat.as_ptr()) };
|
||||
}
|
||||
}
|
||||
23
libeirs/src/sender.rs
Normal file
23
libeirs/src/sender.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use std::{ops::Deref, os::fd::OwnedFd};
|
||||
|
||||
use super::eirc::Ei;
|
||||
|
||||
pub struct Sender {
|
||||
ei: Ei,
|
||||
}
|
||||
|
||||
impl Sender {
|
||||
pub fn new(fd: OwnedFd) -> Self {
|
||||
Self {
|
||||
ei: Ei::new_sender(fd),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Sender {
|
||||
type Target = Ei;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.ei
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user