espanso/espanso-inject/src/evdev/uinput.rs

113 lines
3.0 KiB
Rust

/*
* This file is part of espanso.
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::ffi::CString;
use libc::{c_uint, close, ioctl, open, O_NONBLOCK, O_WRONLY};
use scopeguard::ScopeGuard;
use anyhow::Result;
use log::error;
use thiserror::Error;
use super::ffi::{
setup_uinput_device, ui_dev_create, ui_dev_destroy, ui_set_evbit, ui_set_keybit, uinput_emit,
EV_KEY,
};
pub struct UInputDevice {
fd: i32,
}
impl UInputDevice {
pub fn new() -> Result<UInputDevice> {
let uinput_path = CString::new("/dev/uinput").expect("unable to generate /dev/uinput path");
let raw_fd = unsafe { open(uinput_path.as_ptr(), O_WRONLY | O_NONBLOCK) };
if raw_fd < 0 {
error!("Error: could not open uinput device");
error!("This might be due to a recent kernel update, please restart your PC so that the uinput module can be loaded correctly.");
return Err(UInputDeviceError::Open().into());
}
let fd = scopeguard::guard(raw_fd, |raw_fd| unsafe {
close(raw_fd);
});
// Enable keyboard events
if unsafe { ioctl(*fd, ui_set_evbit(), EV_KEY as c_uint) } != 0 {
return Err(UInputDeviceError::KeyEVBit().into());
}
// Register all keycodes
for key_code in 0..256 {
if unsafe { ioctl(*fd, ui_set_keybit(), key_code) } != 0 {
return Err(UInputDeviceError::KeyBit().into());
}
}
// Register the virtual device
if unsafe { setup_uinput_device(*fd) } != 0 {
return Err(UInputDeviceError::DeviceSetup().into());
}
// Create the device
if unsafe { ioctl(*fd, ui_dev_create()) } != 0 {
return Err(UInputDeviceError::DeviceCreate().into());
}
Ok(Self {
fd: ScopeGuard::into_inner(fd),
})
}
pub fn emit(&self, key_code: u32, pressed: bool) {
let pressed = if pressed { 1 } else { 0 };
unsafe {
uinput_emit(self.fd, key_code, pressed);
}
}
}
impl Drop for UInputDevice {
fn drop(&mut self) {
unsafe {
ioctl(self.fd, ui_dev_destroy());
close(self.fd);
}
}
}
#[derive(Error, Debug)]
pub enum UInputDeviceError {
#[error("could not open uinput device")]
Open(),
#[error("could not set keyboard evbit")]
KeyEVBit(),
#[error("could not set keyboard keybit")]
KeyBit(),
#[error("could not register virtual device")]
DeviceSetup(),
#[error("could not create uinput device")]
DeviceCreate(),
}