Red Bear OS — microkernel OS in Rust, based on Redox
Derivative of Redox OS (https://www.redox-os.org) adding: - AMD GPU driver (amdgpu) via LinuxKPI compat layer - ext4 filesystem support (ext4d scheme daemon) - ACPI fixes for AMD bare metal (x2APIC, DMAR, IVRS, MCFG) - Custom branding (hostname, os-release, boot identity) Build system is full upstream Redox with RBOS overlay in local/. Patches for kernel, base, and relibc are symlinked from local/patches/ and protected from make clean/distclean. Custom recipes live in local/recipes/ with symlinks into the recipes/ search path. Build: make all CONFIG_NAME=redbear-full Sync: ./local/scripts/sync-upstream.sh
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::types::{InputEvent, InputId, BUS_VIRTUAL};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum DeviceKind {
|
||||
Keyboard,
|
||||
Mouse,
|
||||
Touchpad,
|
||||
}
|
||||
|
||||
pub struct InputDevice {
|
||||
pub id: usize,
|
||||
pub kind: DeviceKind,
|
||||
pub name: String,
|
||||
pub input_id: InputId,
|
||||
pub event_buf: VecDeque<InputEvent>,
|
||||
}
|
||||
|
||||
impl InputDevice {
|
||||
pub fn new_keyboard(id: usize) -> Self {
|
||||
InputDevice {
|
||||
id,
|
||||
kind: DeviceKind::Keyboard,
|
||||
name: format!("Redox Keyboard {}", id),
|
||||
input_id: InputId {
|
||||
bustype: BUS_VIRTUAL,
|
||||
vendor: 0,
|
||||
product: id as u16,
|
||||
version: 1,
|
||||
},
|
||||
event_buf: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_mouse(id: usize) -> Self {
|
||||
InputDevice {
|
||||
id,
|
||||
kind: DeviceKind::Mouse,
|
||||
name: format!("Redox Mouse {}", id),
|
||||
input_id: InputId {
|
||||
bustype: BUS_VIRTUAL,
|
||||
vendor: 0,
|
||||
product: (id + 0x10) as u16,
|
||||
version: 1,
|
||||
},
|
||||
event_buf: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_touchpad(id: usize) -> Self {
|
||||
InputDevice {
|
||||
id,
|
||||
kind: DeviceKind::Touchpad,
|
||||
name: format!("Redox Touchpad {}", id),
|
||||
input_id: InputId {
|
||||
bustype: BUS_VIRTUAL,
|
||||
vendor: 0,
|
||||
product: (id + 0x20) as u16,
|
||||
version: 1,
|
||||
},
|
||||
event_buf: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_event(&mut self, event: InputEvent) {
|
||||
self.event_buf.push_back(event);
|
||||
}
|
||||
|
||||
pub fn push_events(&mut self, events: &[InputEvent]) {
|
||||
for &ev in events {
|
||||
self.event_buf.push_back(ev);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_bytes(&mut self, buf: &mut [u8]) -> usize {
|
||||
let event_count = buf.len() / InputEvent::SIZE;
|
||||
let mut written = 0;
|
||||
for _ in 0..event_count {
|
||||
match self.event_buf.pop_front() {
|
||||
Some(ev) => {
|
||||
let bytes = ev.to_bytes();
|
||||
buf[written..written + InputEvent::SIZE].copy_from_slice(&bytes);
|
||||
written += InputEvent::SIZE;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
written
|
||||
}
|
||||
|
||||
pub fn has_events(&self) -> bool {
|
||||
!self.event_buf.is_empty()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
mod device;
|
||||
mod scheme;
|
||||
mod translate;
|
||||
mod types;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::process;
|
||||
|
||||
use log::{error, info, LevelFilter, Metadata, Record};
|
||||
use redox_scheme::{SignalBehavior, Socket};
|
||||
|
||||
use scheme::EvdevScheme;
|
||||
|
||||
struct StderrLogger {
|
||||
level: LevelFilter,
|
||||
}
|
||||
|
||||
impl log::Log for StderrLogger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
metadata.level() <= self.level
|
||||
}
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
eprintln!("[{}] {}", record.level(), record.args());
|
||||
}
|
||||
}
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn read_input_events(scheme: &mut EvdevScheme) -> Result<(), String> {
|
||||
let mut input_file =
|
||||
File::open("/scheme/input").map_err(|e| format!("failed to open /scheme/input: {}", e))?;
|
||||
|
||||
let mut buf = [0u8; 256];
|
||||
match input_file.read(&mut buf) {
|
||||
Ok(n) if n > 0 => {
|
||||
let data = &buf[..n];
|
||||
for &byte in data {
|
||||
let pressed = (byte & 0x80) == 0;
|
||||
let key = byte & 0x7F;
|
||||
scheme.feed_keyboard_event(key, pressed);
|
||||
}
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("evdevd: failed to read input: {}", e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
let mut scheme = EvdevScheme::new();
|
||||
|
||||
let socket =
|
||||
Socket::create("evdev").map_err(|e| format!("failed to register evdev scheme: {}", e))?;
|
||||
info!("evdevd: registered scheme:evdev");
|
||||
|
||||
loop {
|
||||
let request = match socket.next_request(SignalBehavior::Restart) {
|
||||
Ok(Some(r)) => r,
|
||||
Ok(None) => {
|
||||
info!("evdevd: scheme unmounted, exiting");
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("evdevd: failed to read scheme request: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let response = match request.handle_scheme_block_mut(&mut scheme) {
|
||||
Ok(r) => r,
|
||||
Err(_req) => {
|
||||
error!("evdevd: failed to handle request");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = socket.write_response(response, SignalBehavior::Restart) {
|
||||
error!("evdevd: failed to write response: {}", e);
|
||||
}
|
||||
|
||||
let _ = read_input_events(&mut scheme);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let log_level = match env::var("EVDEVD_LOG").as_deref() {
|
||||
Ok("debug") => LevelFilter::Debug,
|
||||
Ok("trace") => LevelFilter::Trace,
|
||||
_ => LevelFilter::Info,
|
||||
};
|
||||
let _ = log::set_boxed_logger(Box::new(StderrLogger { level: log_level }));
|
||||
log::set_max_level(log_level);
|
||||
|
||||
if let Err(e) = run() {
|
||||
error!("evdevd: fatal error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT, EROFS};
|
||||
use syscall::flag::{EventFlags, MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
|
||||
use crate::device::{DeviceKind, InputDevice};
|
||||
use crate::translate;
|
||||
|
||||
struct Handle {
|
||||
kind: HandleKind,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
enum HandleKind {
|
||||
Root,
|
||||
Device(usize),
|
||||
}
|
||||
|
||||
pub struct EvdevScheme {
|
||||
next_id: usize,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
devices: Vec<InputDevice>,
|
||||
}
|
||||
|
||||
impl EvdevScheme {
|
||||
pub fn new() -> Self {
|
||||
let mut scheme = EvdevScheme {
|
||||
next_id: 0,
|
||||
handles: BTreeMap::new(),
|
||||
devices: Vec::new(),
|
||||
};
|
||||
scheme.devices.push(InputDevice::new_keyboard(0));
|
||||
scheme.devices.push(InputDevice::new_mouse(0));
|
||||
scheme
|
||||
}
|
||||
|
||||
pub fn feed_keyboard_event(&mut self, key: u8, pressed: bool) {
|
||||
let events = translate::translate_keyboard(key, pressed);
|
||||
if !events.is_empty() {
|
||||
if let Some(dev) = self
|
||||
.devices
|
||||
.iter_mut()
|
||||
.find(|d| d.kind == DeviceKind::Keyboard)
|
||||
{
|
||||
dev.push_events(&events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn feed_mouse_move(&mut self, dx: i32, dy: i32) {
|
||||
if let Some(dev) = self
|
||||
.devices
|
||||
.iter_mut()
|
||||
.find(|d| d.kind == DeviceKind::Mouse)
|
||||
{
|
||||
dev.push_events(&translate::translate_mouse_dx(dx));
|
||||
dev.push_events(&translate::translate_mouse_dy(dy));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn feed_mouse_scroll(&mut self, y: i32) {
|
||||
if let Some(dev) = self
|
||||
.devices
|
||||
.iter_mut()
|
||||
.find(|d| d.kind == DeviceKind::Mouse)
|
||||
{
|
||||
dev.push_events(&translate::translate_mouse_scroll(y));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn feed_mouse_button(&mut self, button: usize, pressed: bool) {
|
||||
if let Some(dev) = self
|
||||
.devices
|
||||
.iter_mut()
|
||||
.find(|d| d.kind == DeviceKind::Mouse)
|
||||
{
|
||||
dev.push_events(&translate::translate_mouse_button(button, pressed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl redox_scheme::SchemeBlockMut for EvdevScheme {
|
||||
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result<Option<usize>> {
|
||||
let cleaned = path.trim_matches('/');
|
||||
|
||||
let kind = if cleaned.is_empty() {
|
||||
HandleKind::Root
|
||||
} else if let Some(rest) = cleaned.strip_prefix("event") {
|
||||
let idx: usize = rest
|
||||
.trim_end_matches('/')
|
||||
.parse()
|
||||
.map_err(|_| Error::new(ENOENT))?;
|
||||
if idx >= self.devices.len() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
HandleKind::Device(idx)
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
};
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle { kind, offset: 0 });
|
||||
Ok(Some(id))
|
||||
}
|
||||
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match &handle.kind {
|
||||
HandleKind::Root => {
|
||||
let mut listing = String::new();
|
||||
for (i, _dev) in self.devices.iter().enumerate() {
|
||||
listing.push_str(&format!("event{}\n", i));
|
||||
}
|
||||
let bytes = listing.as_bytes();
|
||||
let remaining = &bytes[handle.offset..];
|
||||
let to_copy = remaining.len().min(buf.len());
|
||||
buf[..to_copy].copy_from_slice(&remaining[..to_copy]);
|
||||
handle.offset += to_copy;
|
||||
Ok(Some(to_copy))
|
||||
}
|
||||
HandleKind::Device(idx) => {
|
||||
let dev = &mut self.devices[*idx];
|
||||
let written = dev.pop_bytes(buf);
|
||||
handle.offset += written;
|
||||
Ok(if written == 0 { None } else { Some(written) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, _buf: &[u8]) -> Result<Option<usize>> {
|
||||
let _ = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
Err(Error::new(EROFS))
|
||||
}
|
||||
|
||||
fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result<Option<isize>> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let new_offset = match whence {
|
||||
SEEK_SET => pos as isize,
|
||||
SEEK_CUR => handle.offset as isize + pos,
|
||||
SEEK_END => pos,
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
if new_offset < 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
handle.offset = new_offset as usize;
|
||||
Ok(Some(new_offset))
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
match &handle.kind {
|
||||
HandleKind::Root => {
|
||||
stat.st_mode = MODE_DIR | 0o555;
|
||||
}
|
||||
HandleKind::Device(_) => {
|
||||
stat.st_mode = MODE_FILE | 0o444;
|
||||
}
|
||||
}
|
||||
Ok(Some(0))
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles.remove(&id);
|
||||
Ok(Some(0))
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
let path = match &handle.kind {
|
||||
HandleKind::Root => "evdev:".to_string(),
|
||||
HandleKind::Device(idx) => format!("evdev:event{}", idx),
|
||||
};
|
||||
let bytes = path.as_bytes();
|
||||
let to_copy = bytes.len().min(buf.len());
|
||||
buf[..to_copy].copy_from_slice(&bytes[..to_copy]);
|
||||
Ok(Some(to_copy))
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result<Option<usize>> {
|
||||
let _ = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
Ok(Some(0))
|
||||
}
|
||||
|
||||
fn fevent(&mut self, id: usize, flags: EventFlags) -> Result<Option<EventFlags>> {
|
||||
let _ = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
Ok(Some(flags))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::types::*;
|
||||
|
||||
fn orb_key_to_evdev(orb_key: u8) -> Option<u16> {
|
||||
let mapped = match orb_key {
|
||||
b'1'..=b'9' => KEY_1 + (orb_key - b'1') as u16,
|
||||
b'0' => KEY_0,
|
||||
b'a'..=b'z' => KEY_A + (orb_key - b'a') as u16,
|
||||
b'\n' | b'\r' => KEY_ENTER,
|
||||
b'\t' => KEY_TAB,
|
||||
b' ' => KEY_SPACE,
|
||||
b'\x08' => KEY_BACKSPACE,
|
||||
b'\x1b' => KEY_ESC,
|
||||
b'-' => KEY_MINUS,
|
||||
b'=' => KEY_EQUAL,
|
||||
b'[' => KEY_LEFTBRACE,
|
||||
b']' => KEY_RIGHTBRACE,
|
||||
b'\\' => KEY_BACKSLASH,
|
||||
b';' => KEY_SEMICOLON,
|
||||
b'\'' => KEY_APOSTROPHE,
|
||||
b'`' => KEY_GRAVE,
|
||||
b',' => KEY_COMMA,
|
||||
b'.' => KEY_DOT,
|
||||
b'/' => KEY_SLASH,
|
||||
_ => return None,
|
||||
};
|
||||
Some(mapped)
|
||||
}
|
||||
|
||||
pub fn translate_keyboard(orb_key: u8, pressed: bool) -> Vec<InputEvent> {
|
||||
let value = if pressed { 1 } else { 0 };
|
||||
match orb_key_to_evdev(orb_key) {
|
||||
Some(code) => vec![
|
||||
InputEvent::new(EV_KEY, code, value),
|
||||
InputEvent::syn_report(),
|
||||
],
|
||||
None => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_mouse_dx(dx: i32) -> Vec<InputEvent> {
|
||||
vec![InputEvent::new(EV_REL, REL_X, dx), InputEvent::syn_report()]
|
||||
}
|
||||
|
||||
pub fn translate_mouse_dy(dy: i32) -> Vec<InputEvent> {
|
||||
vec![InputEvent::new(EV_REL, REL_Y, dy), InputEvent::syn_report()]
|
||||
}
|
||||
|
||||
pub fn translate_mouse_scroll(y: i32) -> Vec<InputEvent> {
|
||||
vec![
|
||||
InputEvent::new(EV_REL, REL_WHEEL, y),
|
||||
InputEvent::syn_report(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn translate_mouse_button(button: usize, pressed: bool) -> Vec<InputEvent> {
|
||||
let code = match button {
|
||||
0 => BTN_LEFT,
|
||||
1 => BTN_MIDDLE,
|
||||
2 => BTN_RIGHT,
|
||||
3 => BTN_SIDE,
|
||||
4 => BTN_EXTRA,
|
||||
_ => return vec![],
|
||||
};
|
||||
let value = if pressed { 1 } else { 0 };
|
||||
vec![
|
||||
InputEvent::new(EV_KEY, code, value),
|
||||
InputEvent::syn_report(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn translate_touch(x: i32, y: i32, touching: bool) -> Vec<InputEvent> {
|
||||
let btn = InputEvent::new(EV_KEY, BTN_TOUCH, if touching { 1 } else { 0 });
|
||||
let abs_x = InputEvent::new(EV_ABS, ABS_X, x);
|
||||
let abs_y = InputEvent::new(EV_ABS, ABS_Y, y);
|
||||
let syn = InputEvent::syn_report();
|
||||
vec![btn, abs_x, abs_y, syn]
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/// Linux-compatible evdev event types and constants.
|
||||
///
|
||||
/// These mirror the Linux kernel's `include/uapi/linux/input.h` definitions
|
||||
/// so that clients expecting evdev semantics can work on Redox.
|
||||
|
||||
// Event types
|
||||
pub const EV_SYN: u16 = 0x00;
|
||||
pub const EV_KEY: u16 = 0x01;
|
||||
pub const EV_REL: u16 = 0x02;
|
||||
pub const EV_ABS: u16 = 0x03;
|
||||
pub const EV_MSC: u16 = 0x04;
|
||||
pub const EV_LED: u16 = 0x11;
|
||||
pub const EV_SND: u16 = 0x12;
|
||||
pub const EV_REP: u16 = 0x14;
|
||||
|
||||
// Synchronization events
|
||||
pub const SYN_REPORT: u16 = 0;
|
||||
pub const SYN_CONFIG: u16 = 1;
|
||||
|
||||
// Relative axes
|
||||
pub const REL_X: u16 = 0x00;
|
||||
pub const REL_Y: u16 = 0x01;
|
||||
pub const REL_Z: u16 = 0x02;
|
||||
pub const REL_WHEEL: u16 = 0x08;
|
||||
pub const REL_HWHEEL: u16 = 0x06;
|
||||
|
||||
// Absolute axes
|
||||
pub const ABS_X: u16 = 0x00;
|
||||
pub const ABS_Y: u16 = 0x01;
|
||||
pub const ABS_PRESSURE: u16 = 0x18;
|
||||
pub const ABS_DISTANCE: u16 = 0x19;
|
||||
pub const ABS_MT_SLOT: u16 = 0x2f;
|
||||
pub const ABS_MT_TOUCH_MAJOR: u16 = 0x30;
|
||||
pub const ABS_MT_POSITION_X: u16 = 0x35;
|
||||
pub const ABS_MT_POSITION_Y: u16 = 0x36;
|
||||
pub const ABS_MT_TRACKING_ID: u16 = 0x39;
|
||||
|
||||
// Keys and buttons
|
||||
pub const KEY_RESERVED: u16 = 0;
|
||||
pub const KEY_ESC: u16 = 1;
|
||||
pub const KEY_1: u16 = 2;
|
||||
pub const KEY_2: u16 = 3;
|
||||
pub const KEY_3: u16 = 4;
|
||||
pub const KEY_4: u16 = 5;
|
||||
pub const KEY_5: u16 = 6;
|
||||
pub const KEY_6: u16 = 7;
|
||||
pub const KEY_7: u16 = 8;
|
||||
pub const KEY_8: u16 = 9;
|
||||
pub const KEY_9: u16 = 10;
|
||||
pub const KEY_0: u16 = 11;
|
||||
pub const KEY_MINUS: u16 = 12;
|
||||
pub const KEY_EQUAL: u16 = 13;
|
||||
pub const KEY_BACKSPACE: u16 = 14;
|
||||
pub const KEY_TAB: u16 = 15;
|
||||
pub const KEY_Q: u16 = 16;
|
||||
pub const KEY_W: u16 = 17;
|
||||
pub const KEY_E: u16 = 18;
|
||||
pub const KEY_R: u16 = 19;
|
||||
pub const KEY_T: u16 = 20;
|
||||
pub const KEY_Y: u16 = 21;
|
||||
pub const KEY_U: u16 = 22;
|
||||
pub const KEY_I: u16 = 23;
|
||||
pub const KEY_O: u16 = 24;
|
||||
pub const KEY_P: u16 = 25;
|
||||
pub const KEY_LEFTBRACE: u16 = 26;
|
||||
pub const KEY_RIGHTBRACE: u16 = 27;
|
||||
pub const KEY_ENTER: u16 = 28;
|
||||
pub const KEY_LEFTCTRL: u16 = 29;
|
||||
pub const KEY_A: u16 = 30;
|
||||
pub const KEY_S: u16 = 31;
|
||||
pub const KEY_D: u16 = 32;
|
||||
pub const KEY_F: u16 = 33;
|
||||
pub const KEY_G: u16 = 34;
|
||||
pub const KEY_H: u16 = 35;
|
||||
pub const KEY_J: u16 = 36;
|
||||
pub const KEY_K: u16 = 37;
|
||||
pub const KEY_L: u16 = 38;
|
||||
pub const KEY_SEMICOLON: u16 = 39;
|
||||
pub const KEY_APOSTROPHE: u16 = 40;
|
||||
pub const KEY_GRAVE: u16 = 41;
|
||||
pub const KEY_LEFTSHIFT: u16 = 42;
|
||||
pub const KEY_BACKSLASH: u16 = 43;
|
||||
pub const KEY_Z: u16 = 44;
|
||||
pub const KEY_X: u16 = 45;
|
||||
pub const KEY_C: u16 = 46;
|
||||
pub const KEY_V: u16 = 47;
|
||||
pub const KEY_B: u16 = 48;
|
||||
pub const KEY_N: u16 = 49;
|
||||
pub const KEY_M: u16 = 50;
|
||||
pub const KEY_COMMA: u16 = 51;
|
||||
pub const KEY_DOT: u16 = 52;
|
||||
pub const KEY_SLASH: u16 = 53;
|
||||
pub const KEY_RIGHTSHIFT: u16 = 54;
|
||||
pub const KEY_KPASTERISK: u16 = 55;
|
||||
pub const KEY_LEFTALT: u16 = 56;
|
||||
pub const KEY_SPACE: u16 = 57;
|
||||
pub const KEY_CAPSLOCK: u16 = 58;
|
||||
pub const KEY_F1: u16 = 59;
|
||||
pub const KEY_F2: u16 = 60;
|
||||
pub const KEY_F3: u16 = 61;
|
||||
pub const KEY_F4: u16 = 62;
|
||||
pub const KEY_F5: u16 = 63;
|
||||
pub const KEY_F6: u16 = 64;
|
||||
pub const KEY_F7: u16 = 65;
|
||||
pub const KEY_F8: u16 = 66;
|
||||
pub const KEY_F9: u16 = 67;
|
||||
pub const KEY_F10: u16 = 68;
|
||||
pub const KEY_NUMLOCK: u16 = 69;
|
||||
pub const KEY_SCROLLLOCK: u16 = 70;
|
||||
pub const KEY_F11: u16 = 87;
|
||||
pub const KEY_F12: u16 = 88;
|
||||
|
||||
pub const KEY_HOME: u16 = 102;
|
||||
pub const KEY_UP: u16 = 103;
|
||||
pub const KEY_PAGEUP: u16 = 104;
|
||||
pub const KEY_LEFT: u16 = 105;
|
||||
pub const KEY_RIGHT: u16 = 106;
|
||||
pub const KEY_END: u16 = 107;
|
||||
pub const KEY_DOWN: u16 = 108;
|
||||
pub const KEY_PAGEDOWN: u16 = 109;
|
||||
pub const KEY_INSERT: u16 = 110;
|
||||
pub const KEY_DELETE: u16 = 111;
|
||||
|
||||
pub const KEY_LEFTMETA: u16 = 125;
|
||||
pub const KEY_RIGHTMETA: u16 = 126;
|
||||
pub const KEY_RIGHTCTRL: u16 = 97;
|
||||
pub const KEY_RIGHTALT: u16 = 100;
|
||||
|
||||
// Mouse buttons
|
||||
pub const BTN_LEFT: u16 = 0x110;
|
||||
pub const BTN_RIGHT: u16 = 0x111;
|
||||
pub const BTN_MIDDLE: u16 = 0x112;
|
||||
pub const BTN_SIDE: u16 = 0x113;
|
||||
pub const BTN_EXTRA: u16 = 0x114;
|
||||
|
||||
// Touch
|
||||
pub const BTN_TOUCH: u16 = 0x14a;
|
||||
pub const BTN_TOOL_FINGER: u16 = 0x145;
|
||||
|
||||
// Bus types
|
||||
pub const BUS_PCI: u16 = 0x01;
|
||||
pub const BUS_USB: u16 = 0x03;
|
||||
pub const BUS_VIRTUAL: u16 = 0x06;
|
||||
|
||||
// Evdev version
|
||||
pub const EV_VERSION: i32 = 0x010001;
|
||||
|
||||
/// Linux `struct input_event` layout (24 bytes).
|
||||
///
|
||||
/// Matches the kernel binary layout:
|
||||
/// struct input_event {
|
||||
/// struct timeval time; // 8 + 8 bytes (sec + usec, 64-bit each on x86_64)
|
||||
/// __u16 type;
|
||||
/// __u16 code;
|
||||
/// __s32 value;
|
||||
/// };
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct InputEvent {
|
||||
pub time_sec: u64,
|
||||
pub time_usec: u64,
|
||||
pub event_type: u16,
|
||||
pub code: u16,
|
||||
pub value: i32,
|
||||
}
|
||||
|
||||
impl InputEvent {
|
||||
pub const SIZE: usize = 24;
|
||||
|
||||
pub fn new(event_type: u16, code: u16, value: i32) -> Self {
|
||||
let (sec, usec) = now_timestamp();
|
||||
InputEvent {
|
||||
time_sec: sec,
|
||||
time_usec: usec,
|
||||
event_type,
|
||||
code,
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
|
||||
let mut buf = [0u8; Self::SIZE];
|
||||
buf[0..8].copy_from_slice(&self.time_sec.to_le_bytes());
|
||||
buf[8..16].copy_from_slice(&self.time_usec.to_le_bytes());
|
||||
buf[16..18].copy_from_slice(&self.event_type.to_le_bytes());
|
||||
buf[18..20].copy_from_slice(&self.code.to_le_bytes());
|
||||
buf[20..24].copy_from_slice(&self.value.to_le_bytes());
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn syn_report() -> Self {
|
||||
Self::new(EV_SYN, SYN_REPORT, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Linux `struct input_id` layout (8 bytes).
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct InputId {
|
||||
pub bustype: u16,
|
||||
pub vendor: u16,
|
||||
pub product: u16,
|
||||
pub version: u16,
|
||||
}
|
||||
|
||||
fn now_timestamp() -> (u64, u64) {
|
||||
use std::time::SystemTime;
|
||||
let dur = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap_or_default();
|
||||
(dur.as_secs(), dur.subsec_micros() as u64)
|
||||
}
|
||||
Reference in New Issue
Block a user