#![allow(clippy::result_unit_err)]
use crate::{ffi, AsRaw, Device, Event, FromRaw};
use std::{
ffi::{CStr, CString},
io::{Error as IoError, Result as IoResult},
iter::Iterator,
os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd},
path::Path,
rc::Rc,
};
#[cfg(feature = "udev")]
use udev::ffi as udev;
pub trait LibinputInterface {
fn open_restricted(&mut self, path: &Path, flags: i32) -> Result<OwnedFd, i32>;
fn close_restricted(&mut self, fd: OwnedFd);
}
unsafe extern "C" fn open_restricted<I: LibinputInterface + 'static>(
path: *const libc::c_char,
flags: libc::c_int,
user_data: *mut libc::c_void,
) -> libc::c_int {
use std::borrow::Cow;
if let Some(interface) = (user_data as *mut I).as_mut() {
let path_str = CStr::from_ptr(path).to_string_lossy();
let res = match path_str {
Cow::Borrowed(string) => interface.open_restricted(Path::new(string), flags),
Cow::Owned(string) => interface.open_restricted(Path::new(&string), flags),
};
match res {
Ok(fd) => fd.into_raw_fd(),
Err(errno) => {
if errno > 0 {
-errno
} else {
errno
}
}
}
} else {
-1
}
}
unsafe extern "C" fn close_restricted<I: LibinputInterface + 'static>(
fd: libc::c_int,
user_data: *mut libc::c_void,
) {
if let Some(interface) = (user_data as *mut I).as_mut() {
interface.close_restricted(unsafe { OwnedFd::from_raw_fd(fd) })
}
}
pub struct Libinput {
ffi: *mut ffi::libinput,
_interface: Option<Rc<dyn LibinputInterface + 'static>>,
}
impl ::std::fmt::Debug for Libinput {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Libinput @{:p}", self.as_raw())
}
}
impl AsRaw<ffi::libinput> for Libinput {
fn as_raw(&self) -> *const ffi::libinput {
self.ffi as *const _
}
}
impl Clone for Libinput {
fn clone(&self) -> Self {
Libinput {
ffi: unsafe { ffi::libinput_ref(self.as_raw_mut()) },
_interface: self._interface.clone(),
}
}
}
impl Drop for Libinput {
fn drop(&mut self) {
unsafe {
ffi::libinput_unref(self.ffi);
}
}
}
impl PartialEq for Libinput {
fn eq(&self, other: &Self) -> bool {
self.as_raw() == other.as_raw()
}
}
impl Eq for Libinput {}
impl ::std::hash::Hash for Libinput {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.as_raw().hash(state);
}
}
impl Iterator for Libinput {
type Item = Event;
fn next(&mut self) -> Option<Self::Item> {
loop {
let ptr = unsafe { ffi::libinput_get_event(self.as_raw_mut()) };
if ptr.is_null() {
return None;
} else {
match unsafe { Event::try_from_raw(ptr, self) } {
Some(x) => return Some(x),
None => {
#[cfg(feature = "log")]
log::warn!("Skipping unknown event: {}", unsafe {
ffi::libinput_event_get_type(ptr)
});
continue;
}
}
}
}
}
}
impl Libinput {
#[cfg(feature = "udev")]
pub fn new_with_udev<I: LibinputInterface + 'static>(interface: I) -> Libinput {
let boxed_userdata = Rc::new(interface);
let boxed_interface = Box::new(ffi::libinput_interface {
open_restricted: Some(open_restricted::<I>),
close_restricted: Some(close_restricted::<I>),
});
unsafe {
let udev = udev::udev_new();
let libinput = ffi::libinput_udev_create_context(
Box::into_raw(boxed_interface),
Rc::as_ptr(&boxed_userdata) as *mut _,
udev as *mut input_sys::udev,
);
udev::udev_unref(udev);
Libinput {
ffi: libinput,
_interface: Some(boxed_userdata as Rc<dyn LibinputInterface>),
}
}
}
pub fn new_from_path<I: 'static + LibinputInterface>(interface: I) -> Libinput {
let boxed_userdata = Rc::new(interface);
let boxed_interface = Box::new(ffi::libinput_interface {
open_restricted: Some(open_restricted::<I>),
close_restricted: Some(close_restricted::<I>),
});
Libinput {
ffi: unsafe {
ffi::libinput_path_create_context(
Box::into_raw(boxed_interface),
Rc::as_ptr(&boxed_userdata) as *mut _,
)
},
_interface: Some(boxed_userdata as Rc<dyn LibinputInterface>),
}
}
pub fn path_add_device(&mut self, path: &str) -> Option<Device> {
let path = CString::new(path).expect("Device Path contained a null-byte");
unsafe {
let ptr = ffi::libinput_path_add_device(self.as_raw_mut(), path.as_ptr());
if ptr.is_null() {
None
} else {
Some(Device::from_raw(ptr, self))
}
}
}
pub fn path_remove_device(&mut self, device: Device) {
unsafe { ffi::libinput_path_remove_device(device.as_raw_mut()) }
}
#[cfg(feature = "udev")]
pub fn udev_assign_seat(&mut self, seat_id: &str) -> Result<(), ()> {
let id = CString::new(seat_id).expect("Seat Id contained a null-byte");
unsafe {
match ffi::libinput_udev_assign_seat(self.as_raw_mut(), id.as_ptr()) {
0 => Ok(()),
-1 => Err(()),
_ => unreachable!(),
}
}
}
ffi_func!(
pub fn suspend, ffi::libinput_suspend, ());
pub fn resume(&mut self) -> Result<(), ()> {
unsafe {
match ffi::libinput_resume(self.as_raw_mut()) {
0 => Ok(()),
-1 => Err(()),
_ => unreachable!(),
}
}
}
pub fn dispatch(&mut self) -> IoResult<()> {
unsafe {
match ffi::libinput_dispatch(self.as_raw_mut()) {
0 => Ok(()),
x if x < 0 => Err(IoError::from_raw_os_error(-x)),
_ => unreachable!(),
}
}
}
#[deprecated(since = "0.4.1", note = "Use the provided AsRawFd implementation")]
pub unsafe fn fd(&self) -> RawFd {
ffi::libinput_get_fd(self.as_raw_mut())
}
pub unsafe fn from_raw(ffi: *mut ffi::libinput) -> Self {
Libinput {
ffi: ffi::libinput_ref(ffi),
_interface: None,
}
}
}
#[cfg(feature = "libinput_1_30")]
bitflags::bitflags! {
#[doc(alias = "libinput_plugin_system_flags")]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct PluginSystemFlags: u32 {}
}
#[cfg(feature = "libinput_1_30")]
impl Libinput {
#[doc(alias = "libinput_plugin_system_append_default_paths")]
pub fn plugin_system_append_default_paths(&self) {
unsafe { ffi::libinput_plugin_system_append_default_paths(self.as_raw_mut()) };
}
#[doc(alias = "libinput_plugin_system_append_path")]
pub fn plugin_system_append_path(&self, path: &str) {
let path = CString::new(path).expect("Plugin path contained a null-byte");
unsafe { ffi::libinput_plugin_system_append_path(self.as_raw_mut(), path.as_ptr()) };
}
#[doc(alias = "libinput_plugin_system_load_plugins")]
pub fn plugin_system_load_plugins(&self, flags: PluginSystemFlags) -> IoResult<()> {
unsafe {
match ffi::libinput_plugin_system_load_plugins(self.as_raw_mut(), flags.bits()) {
0 => Ok(()),
x if x < 0 => Err(IoError::from_raw_os_error(-x)),
_ => unreachable!(),
}
}
}
}
impl AsRawFd for Libinput {
fn as_raw_fd(&self) -> RawFd {
unsafe { ffi::libinput_get_fd(self.as_raw_mut()) }
}
}
impl AsFd for Libinput {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
}