feat: add libinput support

Signed-off-by: sisungo <[email protected]>
This commit is contained in:
2026-09-30 00:23:36 +00:00
parent cb4c459175
commit 5700a89954
17 changed files with 675 additions and 201 deletions
+2
View File
@@ -10,4 +10,6 @@ crate-type = ["cdylib"]
ipc = { path = "../../libs/ipc" }
libc = "0.2"
rustc-hash = "2"
db = { path = "../../libs/db" }
fnmatch-regex = "0.3"
rule = { path = "../../libs/rule" }
+228 -135
View File
@@ -1,26 +1,28 @@
mod util;
use crate::util::FfiStringPool;
use db::Db;
use ipc::client::Client;
use rule::parser::{Str, When};
use rule::{
engine::Engine,
parser::{Str, When},
};
use rustc_hash::FxHashMap;
use std::{
ffi::{CStr, CString},
str::FromStr,
ffi::CStr,
os::fd::AsRawFd,
sync::atomic::{self, AtomicUsize},
};
#[derive(Debug)]
pub struct Udev {
client: Client,
userdata: *mut u8,
refcount: AtomicUsize,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_new() -> *mut Udev {
let Ok(client) = Client::connect() else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(Udev {
client,
userdata: std::ptr::null_mut(),
refcount: AtomicUsize::new(1),
}))
@@ -61,8 +63,8 @@ pub unsafe extern "C" fn udev_set_userdata(udev: *mut Udev, userdata: *mut u8) {
#[derive(Debug)]
pub struct UdevListEntry {
key: CString,
value: CString,
key: *const libc::c_char,
value: *const libc::c_char,
next: Option<Box<UdevListEntry>>,
}
@@ -87,7 +89,7 @@ pub unsafe extern "C" fn udev_list_entry_get_by_name(
unsafe {
let name = CStr::from_ptr(name);
while !entry.is_null() {
if (*entry).key == name {
if CStr::from_ptr((*entry).key) == name {
return entry;
}
entry = udev_list_entry_get_next(entry);
@@ -100,14 +102,14 @@ pub unsafe extern "C" fn udev_list_entry_get_by_name(
pub unsafe extern "C" fn udev_list_entry_get_name(
entry: *const UdevListEntry,
) -> *const libc::c_char {
unsafe { (*entry).key.as_ptr() }
unsafe { (*entry).key }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_list_entry_get_value(
entry: *const UdevListEntry,
) -> *const libc::c_char {
unsafe { (*entry).value.as_ptr() }
unsafe { (*entry).value }
}
#[derive(Debug)]
@@ -115,40 +117,33 @@ pub struct UdevDevice {
refcount: AtomicUsize,
udev: *mut Udev,
properties: UdevListEntry,
syspath: Option<CString>,
syspath: Option<*const libc::c_char>,
devlinks: UdevListEntry,
tags: UdevListEntry,
sysattr: UdevListEntry,
action: Option<CString>,
seqnum: libc::c_ulonglong,
sysattr_cache: FxHashMap<CString, CString>,
strings: FfiStringPool,
}
impl UdevDevice {
fn new(udev: *mut Udev, properties: FxHashMap<String, String>) -> Self {
let syspath_str = properties.get("DEVPATH").map(|x| format!("/sys/{x}"));
let syspath = syspath_str
.as_deref()
.map(|x| CString::from_str(x).unwrap_or_default());
let devlinks = properties
.get("SYMLINKS")
.map(|x| x.as_str())
.unwrap_or_default()
.split('\0')
.filter(|x| !x.is_empty())
.map(|x| x.to_string())
.collect();
let devlinks = vec_to_list(&devlinks);
let tags = properties
.get("TAGS")
.map(|x| x.as_str())
.unwrap_or_default()
.split('\0')
.filter(|x| !x.is_empty())
.map(|x| x.to_string())
.collect();
let tags = vec_to_list(&tags);
let mut strings = FfiStringPool::new();
let syspath = properties.get("DEVPATH").map(|x| format!("/sys/{x}"));
let devlinks = util::parse_zero_separated_list(
properties
.get("SYMLINKS")
.map(|x| x.as_str())
.unwrap_or_default(),
);
let devlinks = vec_to_list(&mut strings, &devlinks);
let tags = util::parse_zero_separated_list(
properties
.get("TAGS")
.map(|x| x.as_str())
.unwrap_or_default(),
);
let tags = vec_to_list(&mut strings, &tags);
let mut sysattr = Vec::new();
if let Some(syspath_str) = &syspath_str
if let Some(syspath_str) = &syspath
&& let Ok(read_dir) = std::fs::read_dir(syspath_str)
{
for i in read_dir {
@@ -158,18 +153,18 @@ impl UdevDevice {
sysattr.push(i.file_name().to_string_lossy().to_string());
}
}
let sysattr = vec_to_list(&sysattr);
let sysattr = vec_to_list(&mut strings, &sysattr);
let properties = *hashmap_to_list(&mut strings, &properties);
Self {
refcount: AtomicUsize::new(1),
udev,
properties: *hashmap_to_list(&properties),
syspath,
udev: unsafe { udev_ref(udev) },
properties,
syspath: syspath.map(|x| strings.insert_rust(x).as_ptr()),
devlinks: *devlinks,
tags: *tags,
sysattr: *sysattr,
action: None,
seqnum: 0,
sysattr_cache: Default::default(),
strings,
}
}
}
@@ -183,19 +178,58 @@ impl Drop for UdevDevice {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_device_new_from_syspath(
_udev: *mut Udev,
_syspath: *const libc::c_char,
udev: *mut Udev,
syspath: *const libc::c_char,
) -> *mut UdevDevice {
std::ptr::null_mut()
unsafe {
let Ok(syspath) = CStr::from_ptr(syspath).to_str() else {
return std::ptr::null_mut();
};
let Some(devpath) = syspath.strip_prefix("/sys") else {
return std::ptr::null_mut();
};
udev_device_new_from_lxdeviced_name(udev, devpath.into())
}
}
fn udev_device_new_from_lxdeviced_name(udev: *mut Udev, name: String) -> *mut UdevDevice {
let Ok(mut client) = Client::connect() else {
return std::ptr::null_mut();
};
let Ok(properties) = client.get_device(name) else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(UdevDevice::new(udev, properties)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_device_new_from_devnum(
_udev: *mut Udev,
_type: libc::c_char,
_devnum: libc::dev_t,
udev: *mut Udev,
ty: libc::c_char,
devnum: libc::dev_t,
) -> *mut UdevDevice {
std::ptr::null_mut()
let block = When::Match(Str::Direct("SUBSYSTEM".into()), Str::Direct("block".into()));
let ty = match ty {
b'b' => block,
b'c' => When::Not(Box::new(block)),
_ => return std::ptr::null_mut(),
};
let major = libc::major(devnum);
let minor = libc::minor(devnum);
let major = When::Match(Str::Direct("MAJOR".into()), Str::Direct(major.to_string()));
let minor = When::Match(Str::Direct("MINOR".into()), Str::Direct(minor.to_string()));
let devnum = When::All(vec![major, minor]);
let when = When::All(vec![ty, devnum]);
let Ok(mut client) = Client::connect() else {
return std::ptr::null_mut();
};
let Ok(result) = client.list_devices(when) else {
return std::ptr::null_mut();
};
let Some(devname) = result.first() else {
return std::ptr::null_mut();
};
udev_device_new_from_lxdeviced_name(udev, devname.into())
}
#[unsafe(no_mangle)]
@@ -277,13 +311,7 @@ pub unsafe extern "C" fn udev_device_get_devtype(dev: *const UdevDevice) -> *con
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_device_get_syspath(dev: *const UdevDevice) -> *const libc::c_char {
unsafe {
(*dev)
.syspath
.as_ref()
.map(|x| x.as_ptr())
.unwrap_or_default()
}
unsafe { (*dev).syspath.as_ref().copied().unwrap_or_default() }
}
#[unsafe(no_mangle)]
@@ -292,12 +320,15 @@ pub unsafe extern "C" fn udev_device_get_sysname(dev: *const UdevDevice) -> *con
if dev.is_null() {
return std::ptr::null();
}
let Some(syspath) = (*dev).syspath.as_ref() else {
let Some(syspath) = (*dev).syspath else {
return std::ptr::null();
};
let split_pos = syspath.as_bytes().iter().rposition(|x| *x == b'/');
let split_pos = CStr::from_ptr(syspath)
.to_bytes()
.iter()
.rposition(|x| *x == b'/');
let offset = split_pos.map(|x| x + 1).unwrap_or_default();
syspath.as_ptr().add(offset)
syspath.add(offset)
}
}
@@ -372,7 +403,13 @@ pub unsafe extern "C" fn udev_device_get_property_value(
dev: *const UdevDevice,
key: *const libc::c_char,
) -> *const libc::c_char {
unsafe { udev_list_entry_get_value(udev_list_entry_get_by_name(&(*dev).properties, key)) }
unsafe {
let entry = udev_list_entry_get_by_name(&(*dev).properties, key);
if entry.is_null() {
return std::ptr::null_mut();
}
udev_list_entry_get_value(entry)
}
}
#[unsafe(no_mangle)]
@@ -398,13 +435,7 @@ pub unsafe extern "C" fn udev_device_get_devnum(dev: *const UdevDevice) -> libc:
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_device_get_action(dev: *const UdevDevice) -> *const libc::c_char {
unsafe {
(*dev)
.action
.as_ref()
.map(|x| x.as_ptr())
.unwrap_or_default()
}
unsafe { udev_device_get_property_value(dev, c"ACTION".as_ptr()) }
}
#[unsafe(no_mangle)]
@@ -429,26 +460,21 @@ pub unsafe extern "C" fn udev_device_get_sysattr_value(
let Ok(sysattr_str) = sysattr.to_str() else {
return std::ptr::null();
};
if let Some(val) = (*dev).sysattr_cache.get(sysattr) {
return val.as_ptr();
}
let Some(syspath) = (*dev).syspath.as_ref() else {
return std::ptr::null();
};
let Ok(mut path) = String::from_utf8(syspath.as_bytes().to_vec()) else {
let Ok(mut path) = String::from_utf8(CStr::from_ptr(*syspath).to_bytes().to_vec()) else {
return std::ptr::null();
};
path.push('/');
path.push_str(sysattr_str);
let Ok(data) = std::fs::read_to_string(path) else {
let Ok(mut data) = std::fs::read_to_string(path) else {
return std::ptr::null();
};
let Ok(data) = CString::from_str(&data) else {
return std::ptr::null();
};
let ptr = data.as_ptr();
(*dev).sysattr_cache.insert(CString::from(sysattr), data);
ptr
if data.ends_with("\n") {
data.pop();
}
(*dev).strings.insert_rust(data).as_ptr()
}
}
@@ -472,21 +498,12 @@ pub unsafe extern "C" fn udev_device_set_sysattr_value(
Some(p) => p,
None => return -libc::ENOENT,
};
let path = match syspath.to_str() {
let path = match CStr::from_ptr(*syspath).to_str() {
Ok(p) => format!("{}/{}", p, sysattr),
Err(_) => return -libc::ENOENT,
};
match std::fs::write(&path, value) {
Ok(_) => {
if let Ok(cstr) = CString::new(value) {
let key = match CString::new(sysattr) {
Ok(k) => k,
Err(_) => return 0,
};
dev.sysattr_cache.insert(key, cstr);
}
0
}
Ok(_) => 0,
Err(e) => match e.raw_os_error() {
Some(code) => -code,
None => -libc::EIO,
@@ -519,6 +536,8 @@ pub unsafe extern "C" fn udev_device_has_current_tag(
pub struct UdevMonitor {
udev: *mut Udev,
refcount: AtomicUsize,
client: Client,
conditions: Vec<When>,
}
impl Drop for UdevMonitor {
fn drop(&mut self) {
@@ -560,20 +579,30 @@ pub unsafe extern "C" fn udev_monitor_new_from_netlink(
name: *const libc::c_char,
) -> *mut UdevMonitor {
unsafe {
let name = CStr::from_ptr(name);
if name != c"udev" {
if CStr::from_ptr(name) != c"udev" {
return std::ptr::null_mut();
}
let Ok(client) = Client::connect() else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(UdevMonitor {
udev,
udev: udev_ref(udev),
refcount: AtomicUsize::new(1),
client,
conditions: Vec::new(),
}))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_monitor_enable_receiving(_: *const UdevMonitor) -> libc::c_int {
-libc::EPERM
pub unsafe extern "C" fn udev_monitor_enable_receiving(monitor: *mut UdevMonitor) -> libc::c_int {
unsafe {
let monitor = &mut *monitor;
match monitor.client.send(&ipc::repr::Request::WaitForEvent) {
Ok(()) => 0,
Err(err) => -err.raw_os_error().unwrap_or(libc::EPERM),
}
}
}
#[unsafe(no_mangle)]
@@ -585,40 +614,94 @@ pub unsafe extern "C" fn udev_monitor_set_receive_buffer_size(
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_monitor_get_fd(_: *const UdevMonitor) -> libc::c_int {
-1
pub unsafe extern "C" fn udev_monitor_get_fd(monitor: *const UdevMonitor) -> libc::c_int {
unsafe { (*monitor).client.inner().as_raw_fd() }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_monitor_receive_device(_: *const UdevMonitor) -> *mut UdevDevice {
std::ptr::null_mut()
pub unsafe extern "C" fn udev_monitor_receive_device(monitor: *mut UdevMonitor) -> *mut UdevDevice {
unsafe {
let monitor = &mut *monitor;
let db = Db::open_in_memory();
loop {
let result = monitor.client.recv::<String>();
udev_monitor_enable_receiving(monitor);
let Ok(Ok(result)) = result else {
return std::ptr::null_mut();
};
let Ok(device) = monitor.client.get_device(result) else {
return std::ptr::null_mut();
};
let section = db.section("default");
section.restore(device.clone());
let engine = Engine::with_properties(section);
if !engine
.exec_condition(&When::All(monitor.conditions.clone()))
.unwrap_or_default()
{
return std::ptr::null_mut();
}
return Box::into_raw(Box::new(UdevDevice::new(monitor.udev, device)));
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_monitor_filter_add_match_subsystem_devtype(
_mon: *mut UdevMonitor,
_subsystem: *const libc::c_char,
_devtype: *const libc::c_char,
mon: *mut UdevMonitor,
subsystem: *const libc::c_char,
devtype: *const libc::c_char,
) -> libc::c_int {
-libc::EPERM
unsafe {
udev_monitor_match(mon, "SUBSYSTEM", subsystem, false);
udev_monitor_match(mon, "DEVTYPE", devtype, false);
0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_monitor_filter_add_match_tag(
_mon: *mut UdevMonitor,
_tag: *const libc::c_char,
mon: *mut UdevMonitor,
tag: *const libc::c_char,
) -> libc::c_int {
-libc::EPERM
unsafe { udev_monitor_match(mon, "TAGS", tag, false) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_monitor_filter_update(_mon: *mut UdevMonitor) -> libc::c_int {
-libc::EPERM
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_monitor_filter_remove(_mon: *mut UdevMonitor) -> libc::c_int {
-libc::EPERM
pub unsafe extern "C" fn udev_monitor_filter_remove(mon: *mut UdevMonitor) -> libc::c_int {
unsafe {
(*mon).conditions.clear();
0
}
}
unsafe fn udev_monitor_match(
this: *mut UdevMonitor,
key: &str,
data: *const libc::c_char,
not: bool,
) -> libc::c_int {
unsafe {
if data.is_null() {
return 0;
}
let Ok(data) = CStr::from_ptr(data).to_str() else {
return -libc::EINVAL;
};
let mut when = When::Match(Str::Direct(key.into()), Str::Direct(data.into()));
if not {
when = When::Not(Box::new(when));
}
(*this).conditions.push(when);
0
}
}
#[derive(Debug)]
@@ -627,6 +710,7 @@ pub struct UdevEnumerate {
refcount: AtomicUsize,
filters: Vec<When>,
result: Option<UdevListEntry>,
strings: FfiStringPool,
}
impl Drop for UdevEnumerate {
fn drop(&mut self) {
@@ -664,12 +748,15 @@ pub unsafe extern "C" fn udev_enumerate_get_udev(p: *mut UdevEnumerate) -> *mut
#[unsafe(no_mangle)]
pub unsafe extern "C" fn udev_enumerate_new(udev: *mut Udev) -> *mut UdevEnumerate {
Box::into_raw(Box::new(UdevEnumerate {
udev,
refcount: AtomicUsize::new(1),
filters: Vec::with_capacity(16),
result: None,
}))
unsafe {
Box::into_raw(Box::new(UdevEnumerate {
udev: udev_ref(udev),
refcount: AtomicUsize::new(1),
filters: Vec::with_capacity(16),
result: None,
strings: FfiStringPool::new(),
}))
}
}
#[unsafe(no_mangle)]
@@ -735,7 +822,7 @@ pub unsafe extern "C" fn udev_enumerate_add_match_sysname(
udev_enumerate: *mut UdevEnumerate,
sysname: *const libc::c_char,
) -> libc::c_int {
unsafe { udev_enumerate_match(udev_enumerate, "SYSNAME", sysname, false) }
unsafe { udev_enumerate_match(udev_enumerate, "DEVNAME", sysname, false) }
}
#[unsafe(no_mangle)]
@@ -774,13 +861,18 @@ pub unsafe extern "C" fn udev_enumerate_scan_devices(
udev_enumerate: *mut UdevEnumerate,
) -> libc::c_int {
unsafe {
let udev = (*udev_enumerate).udev;
let when = When::All((*udev_enumerate).filters.clone());
let result = match (*udev).client.list_devices(when) {
let Ok(mut client) = Client::connect() else {
return -libc::EPERM;
};
let mut result = match client.list_devices(when) {
Ok(x) => x,
Err(_) => return -libc::EPERM,
};
(*udev_enumerate).result = Some(*vec_to_list(&result));
for i in &mut result {
i.insert_str(0, "/sys");
}
(*udev_enumerate).result = Some(*vec_to_list(&mut (*udev_enumerate).strings, &result));
0
}
}
@@ -815,7 +907,8 @@ unsafe fn udev_enumerate_match(
let Ok(data) = CStr::from_ptr(data).to_str() else {
return -libc::EINVAL;
};
let mut when = When::Match(Str::Direct(key.into()), Str::Direct(data.into()));
let regex = fnmatch_regex::glob_to_regex_pattern(data).unwrap_or_default();
let mut when = When::PartMatch(Str::Direct(key.into()), Str::Direct((&regex[1..]).into()));
if not {
when = When::Not(Box::new(when));
}
@@ -906,16 +999,17 @@ pub unsafe extern "C" fn udev_util_encode_string(
len as _
}
fn hashmap_to_list(hashmap: &FxHashMap<String, String>) -> Box<UdevListEntry> {
fn hashmap_to_list(
strings: &mut FfiStringPool,
hashmap: &FxHashMap<String, String>,
) -> Box<UdevListEntry> {
let mut head = None;
let mut tail = &mut head;
for (key, value) in hashmap {
let key_cstr = CString::new(key.as_str()).unwrap();
let value_cstr = CString::new(value.as_str()).unwrap();
let entry = Box::new(UdevListEntry {
key: key_cstr,
value: value_cstr,
key: strings.insert_rust(key.clone()).as_ptr(),
value: strings.insert_rust(value.clone()).as_ptr(),
next: None,
});
*tail = Some(entry);
@@ -923,21 +1017,20 @@ fn hashmap_to_list(hashmap: &FxHashMap<String, String>) -> Box<UdevListEntry> {
}
head.unwrap_or(Box::new(UdevListEntry {
key: CString::new("").unwrap(),
value: CString::new("").unwrap(),
key: std::ptr::null(),
value: std::ptr::null(),
next: None,
}))
}
fn vec_to_list(vec: &Vec<String>) -> Box<UdevListEntry> {
fn vec_to_list(strings: &mut FfiStringPool, vec: &Vec<String>) -> Box<UdevListEntry> {
let mut head = None;
let mut tail = &mut head;
for s in vec {
let cstr = CString::new(s.as_str()).unwrap();
let entry = Box::new(UdevListEntry {
key: cstr,
value: CString::new("").unwrap(),
key: strings.insert_rust(s.clone()).as_ptr(),
value: strings.insert_rust("".into()).as_ptr(),
next: None,
});
*tail = Some(entry);
@@ -946,8 +1039,8 @@ fn vec_to_list(vec: &Vec<String>) -> Box<UdevListEntry> {
head.unwrap_or_else(|| {
Box::new(UdevListEntry {
key: CString::new("").unwrap(),
value: CString::new("").unwrap(),
key: std::ptr::null(),
value: std::ptr::null(),
next: None,
})
})
+28
View File
@@ -0,0 +1,28 @@
use rustc_hash::FxHashMap;
use std::ffi::{CStr, CString};
#[derive(Debug)]
pub struct FfiStringPool {
strings: FxHashMap<String, CString>,
}
impl FfiStringPool {
pub fn new() -> Self {
Self {
strings: FxHashMap::default(),
}
}
pub fn insert_rust(&mut self, s: String) -> &CStr {
let s = s.replace("\0", "");
let entry = self.strings.entry(s);
let value = entry.or_insert_with_key(|key| CString::new(key.clone()).unwrap());
unsafe { &*(value.as_c_str() as *const CStr) }
}
}
pub fn parse_zero_separated_list(s: &str) -> Vec<String> {
s.split('\0')
.filter(|x| !x.is_empty())
.map(|x| x.to_string())
.collect()
}