1
0
Fork 0
forked from wry/wry

libinput: move bindings into workspace crate

This commit is contained in:
kossLAN 2026-05-29 11:32:34 -04:00
parent 4562a34890
commit f5bcfaf73c
No known key found for this signature in database
12 changed files with 442 additions and 316 deletions

16
Cargo.lock generated
View file

@ -712,6 +712,7 @@ dependencies = [
"jay-geometry",
"jay-io-uring",
"jay-layout-animation",
"jay-libinput",
"jay-logger",
"jay-pango",
"jay-pr-caps",
@ -869,6 +870,21 @@ dependencies = [
"jay-geometry",
]
[[package]]
name = "jay-libinput"
version = "0.1.0"
dependencies = [
"anyhow",
"bstr",
"isnt 0.2.0",
"jay-utils",
"libloading",
"log",
"repc",
"thiserror",
"uapi",
]
[[package]]
name = "jay-logger"
version = "0.1.0"

View file

@ -44,6 +44,7 @@ members = [
"logger",
"video-types",
"pango",
"libinput",
"toml-config",
"algorithms",
"toml-spec",
@ -89,6 +90,7 @@ jay-bugs = { version = "0.1.0", path = "bugs" }
jay-logger = { version = "0.1.0", path = "logger" }
jay-video-types = { version = "0.1.0", path = "video-types" }
jay-pango = { version = "0.1.0", path = "pango" }
jay-libinput = { version = "0.1.0", path = "libinput" }
uapi = "0.2.13"
thiserror = "2.0.11"

View file

@ -4,13 +4,25 @@ use {
std::{env, io::Write},
};
#[expect(unused_macros)]
#[macro_use]
#[path = "../src/macros.rs"]
mod macros;
#[allow(unused_macros)]
macro_rules! cenum {
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct $name(pub i32);
#[path = "../src/libinput/consts.rs"]
mod libinput;
impl $name {
pub fn raw(self) -> i32 {
self.0
}
}
pub const $uc: &[i32] = &[$($val,)*];
$(
pub const $name2: $name = $name($val);
)*
}
}
#[path = "../src/fontconfig/consts.rs"]
mod fontconfig;
@ -46,102 +58,6 @@ fn write_ty<W: Write>(f: &mut W, vals: &[i32], ty: &str) -> anyhow::Result<()> {
}
pub fn main() -> anyhow::Result<()> {
let mut f = open("libinput_tys.rs")?;
write_ty(
&mut f,
libinput::LIBINPUT_LOG_PRIORITY,
"libinput_log_priority",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_DEVICE_CAPABILITY,
"libinput_device_capability",
)?;
write_ty(&mut f, libinput::LIBINPUT_KEY_STATE, "libinput_key_state")?;
write_ty(&mut f, libinput::LIBINPUT_LED, "libinput_led")?;
write_ty(
&mut f,
libinput::LIBINPUT_BUTTON_STATE,
"libinput_button_state",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_POINTER_AXIS,
"libinput_pointer_axis",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_POINTER_AXIS_SOURCE,
"libinput_pointer_axis_source",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_TABLET_PAD_RING_AXIS_SOURCE,
"libinput_tablet_pad_ring_axis_source",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_TABLET_PAD_STRIP_AXIS_SOURCE,
"libinput_tablet_pad_strip_axis_source",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_TABLET_TOOL_TYPE,
"libinput_tablet_tool_type",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_TABLET_TOOL_PROXIMITY_STATE,
"libinput_tablet_tool_proximity_state",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_TABLET_TOOL_TIP_STATE,
"libinput_tablet_tool_tip_state",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_SWITCH_STATE,
"libinput_switch_state",
)?;
write_ty(&mut f, libinput::LIBINPUT_SWITCH, "libinput_switch")?;
write_ty(&mut f, libinput::LIBINPUT_EVENT_TYPE, "libinput_event_type")?;
write_ty(
&mut f,
libinput::LIBINPUT_CONFIG_STATUS,
"libinput_config_status",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_CONFIG_ACCEL_PROFILE,
"libinput_config_accel_profile",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_CONFIG_TAP_STATE,
"libinput_config_tap_state",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_CONFIG_DRAG_STATE,
"libinput_config_drag_state",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_CONFIG_DRAG_LOCK_STATE,
"libinput_config_drag_lock_state",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_CONFIG_CLICK_METHOD,
"libinput_config_click_method",
)?;
write_ty(
&mut f,
libinput::LIBINPUT_CONFIG_MIDDLE_EMULATION_STATE,
"libinput_config_middle_emulation_state",
)?;
let mut f = open("fontconfig_tys.rs")?;
write_ty(&mut f, fontconfig::FC_MATCH_KINDS, "FcMatchKind")?;
write_ty(&mut f, fontconfig::FC_RESULTS, "FcResult")?;

20
libinput/Cargo.toml Normal file
View file

@ -0,0 +1,20 @@
[package]
name = "jay-libinput"
version = "0.1.0"
edition = "2024"
license = "GPL-3.0-only"
build = "build.rs"
[dependencies]
jay-utils = { version = "0.1.0", path = "../utils" }
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
isnt = "0.2.0"
libloading = "0.9.0"
log = { version = "0.4.20", features = ["std"] }
thiserror = "2.0.11"
uapi = "0.2.13"
[build-dependencies]
anyhow = "1.0.79"
repc = "0.1.1"

175
libinput/build.rs Normal file
View file

@ -0,0 +1,175 @@
use {
repc::layout::{Type, TypeVariant},
std::{
env,
fs::{File, OpenOptions},
io::{self, BufWriter, Write},
path::PathBuf,
},
};
#[allow(unused_macros)]
macro_rules! cenum {
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct $name(pub i32);
impl $name {
pub fn raw(self) -> i32 {
self.0
}
}
$(
pub const $name2: $name = $name($val);
)*
pub const $uc: &[i32] = &[$($val,)*];
};
}
#[path = "src/consts.rs"]
mod consts;
fn open(s: &str) -> io::Result<BufWriter<File>> {
let mut path = PathBuf::from(env::var("OUT_DIR").unwrap());
path.push(s);
Ok(BufWriter::new(
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)?,
))
}
fn get_target() -> repc::Target {
let rustc_target = env::var("TARGET").unwrap();
repc::TARGET_MAP
.iter()
.cloned()
.find(|t| t.0 == rustc_target)
.unwrap()
.1
}
fn get_enum_ty(variants: Vec<i128>) -> anyhow::Result<u64> {
let target = get_target();
let ty = Type {
layout: (),
annotations: vec![],
variant: TypeVariant::Enum(variants),
};
let ty = repc::compute_layout(target, &ty)?;
assert!(ty.layout.pointer_alignment_bits <= ty.layout.size_bits);
Ok(ty.layout.size_bits)
}
fn write_ty<W: Write>(f: &mut W, vals: &[i32], ty: &str) -> anyhow::Result<()> {
let variants: Vec<_> = vals.iter().cloned().map(|v| v as i128).collect();
let size = get_enum_ty(variants)?;
writeln!(f, "#[allow(clippy::allow_attributes, dead_code)]")?;
writeln!(f, "pub type {} = i{};", ty, size)?;
Ok(())
}
fn main() -> anyhow::Result<()> {
let mut f = open("libinput_tys.rs")?;
write_ty(
&mut f,
consts::LIBINPUT_LOG_PRIORITY,
"libinput_log_priority",
)?;
write_ty(
&mut f,
consts::LIBINPUT_DEVICE_CAPABILITY,
"libinput_device_capability",
)?;
write_ty(&mut f, consts::LIBINPUT_KEY_STATE, "libinput_key_state")?;
write_ty(&mut f, consts::LIBINPUT_LED, "libinput_led")?;
write_ty(
&mut f,
consts::LIBINPUT_BUTTON_STATE,
"libinput_button_state",
)?;
write_ty(
&mut f,
consts::LIBINPUT_POINTER_AXIS,
"libinput_pointer_axis",
)?;
write_ty(
&mut f,
consts::LIBINPUT_POINTER_AXIS_SOURCE,
"libinput_pointer_axis_source",
)?;
write_ty(
&mut f,
consts::LIBINPUT_TABLET_PAD_RING_AXIS_SOURCE,
"libinput_tablet_pad_ring_axis_source",
)?;
write_ty(
&mut f,
consts::LIBINPUT_TABLET_PAD_STRIP_AXIS_SOURCE,
"libinput_tablet_pad_strip_axis_source",
)?;
write_ty(
&mut f,
consts::LIBINPUT_TABLET_TOOL_TYPE,
"libinput_tablet_tool_type",
)?;
write_ty(
&mut f,
consts::LIBINPUT_TABLET_TOOL_PROXIMITY_STATE,
"libinput_tablet_tool_proximity_state",
)?;
write_ty(
&mut f,
consts::LIBINPUT_TABLET_TOOL_TIP_STATE,
"libinput_tablet_tool_tip_state",
)?;
write_ty(
&mut f,
consts::LIBINPUT_SWITCH_STATE,
"libinput_switch_state",
)?;
write_ty(&mut f, consts::LIBINPUT_SWITCH, "libinput_switch")?;
write_ty(&mut f, consts::LIBINPUT_EVENT_TYPE, "libinput_event_type")?;
write_ty(
&mut f,
consts::LIBINPUT_CONFIG_STATUS,
"libinput_config_status",
)?;
write_ty(
&mut f,
consts::LIBINPUT_CONFIG_ACCEL_PROFILE,
"libinput_config_accel_profile",
)?;
write_ty(
&mut f,
consts::LIBINPUT_CONFIG_TAP_STATE,
"libinput_config_tap_state",
)?;
write_ty(
&mut f,
consts::LIBINPUT_CONFIG_DRAG_STATE,
"libinput_config_drag_state",
)?;
write_ty(
&mut f,
consts::LIBINPUT_CONFIG_DRAG_LOCK_STATE,
"libinput_config_drag_lock_state",
)?;
write_ty(
&mut f,
consts::LIBINPUT_CONFIG_CLICK_METHOD,
"libinput_config_click_method",
)?;
write_ty(
&mut f,
consts::LIBINPUT_CONFIG_MIDDLE_EMULATION_STATE,
"libinput_config_middle_emulation_state",
)?;
println!("cargo:rerun-if-changed=src/consts.rs");
Ok(())
}

View file

@ -1,5 +1,5 @@
use {
crate::libinput::{
crate::{
LibInput,
consts::{
AccelProfile, ConfigClickMethod, ConfigDragLockState, ConfigDragState,

View file

@ -1,5 +1,5 @@
use {
crate::libinput::{
crate::{
consts::{
ButtonState, EventType, KeyState, PointerAxis, Switch, SwitchState,
TabletPadRingAxisSource, TabletPadStripAxisSource, TabletToolProximityState,
@ -292,7 +292,7 @@ impl<'a> LibInputEventSwitch<'a> {
macro_rules! has_changed {
($name:ident, $f:ident) => {
pub fn $name(&self) -> bool {
unsafe { crate::libinput::sys::$f(self.event) != 0 }
unsafe { crate::sys::$f(self.event) != 0 }
}
};
}
@ -300,7 +300,7 @@ macro_rules! has_changed {
macro_rules! get_double {
($name:ident, $f:ident) => {
pub fn $name(&self) -> f64 {
unsafe { crate::libinput::sys::$f(self.event) }
unsafe { crate::sys::$f(self.event) }
}
};
}
@ -308,7 +308,7 @@ macro_rules! get_double {
macro_rules! has_capability {
($name:ident, $f:ident) => {
pub fn $name(&self) -> bool {
unsafe { crate::libinput::sys::$f(self.tool) != 0 }
unsafe { crate::sys::$f(self.tool) != 0 }
}
};
}

205
libinput/src/lib.rs Normal file
View file

@ -0,0 +1,205 @@
#![allow(non_camel_case_types)]
macro_rules! cenum {
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct $name(pub i32);
impl $name {
#[allow(dead_code)]
pub fn raw(self) -> i32 {
self.0
}
}
$(
pub const $name2: $name = $name($val);
)*
pub const $uc: &[i32] = &[$($val,)*];
};
}
pub mod consts;
pub mod device;
pub mod event;
mod sys;
use {
crate::{
consts::{
LIBINPUT_LOG_PRIORITY_DEBUG, LIBINPUT_LOG_PRIORITY_ERROR, LIBINPUT_LOG_PRIORITY_INFO,
LogPriority,
},
device::RegisteredDevice,
event::LibInputEvent,
sys::{
libinput, libinput_device_ref, libinput_dispatch, libinput_get_event, libinput_get_fd,
libinput_interface, libinput_log_priority, libinput_log_set_handler,
libinput_log_set_priority, libinput_path_add_device, libinput_path_create_context,
libinput_unref,
},
},
bstr::ByteSlice,
isnt::std_1::primitive::IsntConstPtrExt,
jay_utils::{errorfmt::ErrorFmt, oserror::OsError, ptr_ext::PtrExt},
std::{ffi::CStr, rc::Rc},
thiserror::Error,
uapi::{IntoUstr, OwnedFd, c},
};
static INTERFACE: libinput_interface = libinput_interface {
open_restricted,
close_restricted,
};
unsafe extern "C" fn open_restricted(
path: *const c::c_char,
_flags: c::c_int,
user_data: *mut c::c_void,
) -> c::c_int {
unsafe {
let ud = (user_data as *const UserData).deref();
match ud.adapter.open(CStr::from_ptr(path)) {
Ok(f) => f.unwrap(),
Err(e) => {
log::error!("Could not open device for libinput: {}", ErrorFmt(e));
-1
}
}
}
}
unsafe extern "C" fn close_restricted(fd: c::c_int, _user_data: *mut c::c_void) {
drop(OwnedFd::new(fd));
}
struct UserData {
adapter: Rc<dyn LibInputAdapter>,
}
pub trait LibInputAdapter {
fn open(&self, path: &CStr) -> Result<OwnedFd, LibInputError>;
}
#[derive(Debug, Error)]
pub enum LibInputError {
#[error("Could not create a libinput instance")]
New,
#[error("Could not open a libinput device")]
Open,
#[error("Could not dispatch libinput events")]
Dispatch(#[source] OsError),
#[error("The requested device is not available")]
DeviceUnavailable,
#[error("Dupfd failed")]
DupFd(#[source] OsError),
#[error("Stat failed")]
Stat(#[source] OsError),
}
pub struct LibInput {
_data: Box<UserData>,
li: *mut libinput,
}
unsafe extern "C" {
fn jay_libinput_log_handler_bridge();
}
impl LibInput {
pub fn new(adapter: Rc<dyn LibInputAdapter>) -> Result<Self, LibInputError> {
let mut ud = Box::new(UserData { adapter });
let li = unsafe {
libinput_path_create_context(&INTERFACE, &mut *ud as *mut _ as *mut c::c_void)
};
if li.is_null() {
return Err(LibInputError::New);
}
unsafe {
libinput_log_set_handler(li, jay_libinput_log_handler_bridge);
let priority = if log::log_enabled!(log::Level::Debug) {
LIBINPUT_LOG_PRIORITY_DEBUG
} else if log::log_enabled!(log::Level::Info) {
LIBINPUT_LOG_PRIORITY_INFO
} else {
LIBINPUT_LOG_PRIORITY_ERROR
};
libinput_log_set_priority(li, priority.raw() as _);
}
Ok(Self { _data: ud, li })
}
pub fn fd(&self) -> c::c_int {
unsafe { libinput_get_fd(self.li) }
}
pub fn open<'a>(
self: &Rc<Self>,
path: impl IntoUstr<'a>,
) -> Result<RegisteredDevice, LibInputError> {
let path = path.into_ustr();
let res = unsafe { libinput_path_add_device(self.li, path.as_ptr()) };
if res.is_null() {
Err(LibInputError::Open)
} else {
unsafe {
libinput_device_ref(res);
}
Ok(RegisteredDevice {
_li: self.clone(),
dev: res,
})
}
}
pub fn dispatch(&self) -> Result<(), LibInputError> {
let res = unsafe { libinput_dispatch(self.li) };
if res < 0 {
Err(LibInputError::Dispatch(OsError(-res)))
} else {
Ok(())
}
}
pub fn event(&self) -> Option<LibInputEvent<'_>> {
let res = unsafe { libinput_get_event(self.li) };
if res.is_null() {
None
} else {
Some(LibInputEvent {
event: res,
_phantom: Default::default(),
})
}
}
}
impl Drop for LibInput {
fn drop(&mut self) {
unsafe {
libinput_unref(self.li);
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn jay_libinput_log_handler(
_libinput: *mut libinput,
priority: libinput_log_priority,
line: *const c::c_char,
) {
assert!(line.is_not_null());
let str = unsafe { CStr::from_ptr(line) };
let priority = match LogPriority(priority as _) {
LIBINPUT_LOG_PRIORITY_DEBUG => log::Level::Debug,
LIBINPUT_LOG_PRIORITY_INFO => log::Level::Info,
LIBINPUT_LOG_PRIORITY_ERROR => log::Level::Error,
_ => log::Level::Error,
};
log::log!(
priority,
"libinput: {}",
str.to_bytes().trim_ascii().as_bstr()
);
}

View file

@ -1,190 +1 @@
#![allow(non_camel_case_types)]
pub mod consts;
pub mod device;
pub mod event;
mod sys;
use {
crate::{
libinput::{
consts::{
LIBINPUT_LOG_PRIORITY_DEBUG, LIBINPUT_LOG_PRIORITY_ERROR,
LIBINPUT_LOG_PRIORITY_INFO, LogPriority,
},
device::RegisteredDevice,
event::LibInputEvent,
sys::{
libinput, libinput_device_ref, libinput_dispatch, libinput_get_event,
libinput_get_fd, libinput_interface, libinput_log_priority,
libinput_log_set_handler, libinput_log_set_priority, libinput_path_add_device,
libinput_path_create_context, libinput_unref,
},
},
udev::UdevError,
utils::{errorfmt::ErrorFmt, oserror::OsError, ptr_ext::PtrExt},
},
bstr::ByteSlice,
isnt::std_1::primitive::IsntConstPtrExt,
std::{ffi::CStr, rc::Rc},
thiserror::Error,
uapi::{IntoUstr, OwnedFd, c},
};
static INTERFACE: libinput_interface = libinput_interface {
open_restricted,
close_restricted,
};
unsafe extern "C" fn open_restricted(
path: *const c::c_char,
_flags: c::c_int,
user_data: *mut c::c_void,
) -> c::c_int {
unsafe {
let ud = (user_data as *const UserData).deref();
match ud.adapter.open(CStr::from_ptr(path)) {
Ok(f) => f.unwrap(),
Err(e) => {
log::error!("Could not open device for libinput: {}", ErrorFmt(e));
-1
}
}
}
}
unsafe extern "C" fn close_restricted(fd: c::c_int, _user_data: *mut c::c_void) {
drop(OwnedFd::new(fd));
}
struct UserData {
adapter: Rc<dyn LibInputAdapter>,
}
pub trait LibInputAdapter {
fn open(&self, path: &CStr) -> Result<OwnedFd, LibInputError>;
}
#[derive(Debug, Error)]
pub enum LibInputError {
#[error("Could not create a libinput instance")]
New,
#[error("Could not open a libinput device")]
Open,
#[error("Could not dispatch libinput events")]
Dispatch(#[source] OsError),
#[error("The requested device is not available")]
DeviceUnavailable,
#[error("Dupfd failed")]
DupFd(#[source] OsError),
#[error("The udev subsystem produced an error")]
Udev(#[from] UdevError),
#[error("Stat failed")]
Stat(#[source] OsError),
}
pub struct LibInput {
_data: Box<UserData>,
li: *mut libinput,
}
unsafe extern "C" {
fn jay_libinput_log_handler_bridge();
}
impl LibInput {
pub fn new(adapter: Rc<dyn LibInputAdapter>) -> Result<Self, LibInputError> {
let mut ud = Box::new(UserData { adapter });
let li = unsafe {
libinput_path_create_context(&INTERFACE, &mut *ud as *mut _ as *mut c::c_void)
};
if li.is_null() {
return Err(LibInputError::New);
}
unsafe {
libinput_log_set_handler(li, jay_libinput_log_handler_bridge);
let priority = if log::log_enabled!(log::Level::Debug) {
LIBINPUT_LOG_PRIORITY_DEBUG
} else if log::log_enabled!(log::Level::Info) {
LIBINPUT_LOG_PRIORITY_INFO
} else {
LIBINPUT_LOG_PRIORITY_ERROR
};
libinput_log_set_priority(li, priority.raw() as _);
}
Ok(Self { _data: ud, li })
}
pub fn fd(&self) -> c::c_int {
unsafe { libinput_get_fd(self.li) }
}
pub fn open<'a>(
self: &Rc<Self>,
path: impl IntoUstr<'a>,
) -> Result<RegisteredDevice, LibInputError> {
let path = path.into_ustr();
let res = unsafe { libinput_path_add_device(self.li, path.as_ptr()) };
if res.is_null() {
Err(LibInputError::Open)
} else {
unsafe {
libinput_device_ref(res);
}
Ok(RegisteredDevice {
_li: self.clone(),
dev: res,
})
}
}
pub fn dispatch(&self) -> Result<(), LibInputError> {
let res = unsafe { libinput_dispatch(self.li) };
if res < 0 {
Err(LibInputError::Dispatch(OsError(-res)))
} else {
Ok(())
}
}
pub fn event(&self) -> Option<LibInputEvent<'_>> {
let res = unsafe { libinput_get_event(self.li) };
if res.is_null() {
None
} else {
Some(LibInputEvent {
event: res,
_phantom: Default::default(),
})
}
}
}
impl Drop for LibInput {
fn drop(&mut self) {
unsafe {
libinput_unref(self.li);
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn jay_libinput_log_handler(
_libinput: *mut libinput,
priority: libinput_log_priority,
line: *const c::c_char,
) {
assert!(line.is_not_null());
let str = unsafe { CStr::from_ptr(line) };
let priority = match LogPriority(priority as _) {
LIBINPUT_LOG_PRIORITY_DEBUG => log::Level::Debug,
LIBINPUT_LOG_PRIORITY_INFO => log::Level::Info,
LIBINPUT_LOG_PRIORITY_ERROR => log::Level::Error,
_ => log::Level::Error,
};
log::log!(
priority,
"libinput: {}",
str.to_bytes().trim_ascii().as_bstr()
);
}
pub use jay_libinput::*;

View file

@ -232,25 +232,6 @@ macro_rules! linear_ids {
};
}
macro_rules! cenum {
($name:ident, $uc:ident; $($name2:ident = $val:expr,)*) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct $name(pub i32);
impl $name {
pub fn raw(self) -> i32 {
self.0
}
}
pub const $uc: &[i32] = &[$($val,)*];
$(
pub const $name2: $name = $name($val);
)*
}
}
#[expect(unused_macros)]
macro_rules! bitor {
($name:ident) => {