From ed4245c380a0e301ef9e8652c954d6ffd08be424 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 20 Jul 2021 17:40:49 +0100 Subject: [PATCH] Support autorepeats and getting keymap entries (#46) * Support getting and setting auto repeat settings Signed-off-by: Sean Young * Retrieving and updating keymap entries Signed-off-by: Sean Young * Add missing keycodes and missing aliases The aliases are #defines in input-event-codes.h. Signed-off-by: Sean Young --- src/attribute_set.rs | 1 + src/lib.rs | 1 + src/raw_stream.rs | 133 +++++++++++++++++++++++++++++++++++++++++-- src/scancodes.rs | 45 ++++++++++++--- src/sync_stream.rs | 44 ++++++++++++-- src/sys.rs | 7 ++- 6 files changed, 210 insertions(+), 21 deletions(-) diff --git a/src/attribute_set.rs b/src/attribute_set.rs index 52afc98..ab43865 100644 --- a/src/attribute_set.rs +++ b/src/attribute_set.rs @@ -198,6 +198,7 @@ macro_rules! evdev_enum { } impl std::fmt::Debug for $t { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + #[allow(unreachable_patterns)] match *self { $(Self::$c => f.pad(stringify!($c)),)* _ => write!(f, "unknown key: {}", self.0), diff --git a/src/lib.rs b/src/lib.rs index c8bcafd..9a63a4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,6 +94,7 @@ pub use attribute_set::{AttributeSet, AttributeSetRef}; pub use constants::*; pub use device_state::DeviceState; pub use inputid::*; +pub use raw_stream::AutoRepeat; pub use scancodes::*; pub use sync_stream::*; diff --git a/src/raw_stream.rs b/src/raw_stream.rs index 58e2a9d..5744bc7 100644 --- a/src/raw_stream.rs +++ b/src/raw_stream.rs @@ -38,6 +38,8 @@ const ABSINFO_ZERO: libc::input_absinfo = libc::input_absinfo { pub(crate) const ABS_VALS_INIT: [libc::input_absinfo; AbsoluteAxisType::COUNT] = [ABSINFO_ZERO; AbsoluteAxisType::COUNT]; +const INPUT_KEYMAP_BY_INDEX: u8 = 1; + /// A physical or virtual device supported by evdev. /// /// Each device corresponds to a path typically found in `/dev/input`, and supports access via @@ -59,13 +61,20 @@ pub struct RawDevice { supported_switch: Option>, supported_led: Option>, supported_misc: Option>, + auto_repeat: Option, // ff: Option>, // ff_stat: Option, - // rep: Option, supported_snd: Option>, pub(crate) event_buf: Vec, } +#[derive(Debug, Clone)] +#[repr(C)] +pub struct AutoRepeat { + pub delay: u32, + pub period: u32, +} + impl RawDevice { /// Opens a device, given its system path. /// @@ -198,6 +207,25 @@ impl RawDevice { None }; + let auto_repeat = if ty.contains(EventType::REPEAT) { + let mut auto_repeat: AutoRepeat = AutoRepeat { + delay: 0, + period: 0, + }; + + unsafe { + sys::eviocgrep( + file.as_raw_fd(), + &mut auto_repeat as *mut AutoRepeat as *mut [u32; 2], + ) + .map_err(nix_err)? + }; + + Some(auto_repeat) + } else { + None + }; + Ok(RawDevice { file, ty, @@ -214,6 +242,7 @@ impl RawDevice { supported_led, supported_misc, supported_snd, + auto_repeat, event_buf: Vec::new(), }) } @@ -238,6 +267,11 @@ impl RawDevice { InputId::from(self.id) } + /// Returns the current auto repeat settings + pub fn get_auto_repeat(&self) -> Option { + self.auto_repeat.clone() + } + /// Returns the set of supported "properties" for the device (see `INPUT_PROP_*` in kernel headers) pub fn properties(&self) -> &AttributeSetRef { &self.props @@ -360,10 +394,6 @@ impl RawDevice { self.supported_misc.as_deref() } - // pub fn supported_repeats(&self) -> Option { - // self.rep - // } - /// Returns the set of supported simple sounds supported by a device. /// /// You can use these to make really annoying beep sounds come from an internal self-test @@ -493,6 +523,99 @@ impl RawDevice { .map_err(nix_err) } + /// Update the auto repeat delays + #[inline] + pub fn update_auto_repeat(&mut self, repeat: &AutoRepeat) -> io::Result<()> { + unsafe { + sys::eviocsrep( + self.as_raw_fd(), + repeat as *const AutoRepeat as *const [u32; 2], + ) + } + .map(|_| { + self.auto_repeat = Some(repeat.clone()); + }) + .map_err(nix_err) + } + + /// Retrieve the scancode for a keycode, if any + pub fn get_scancode_by_keycode(&self, keycode: u32) -> io::Result> { + let mut keymap = libc::input_keymap_entry { + flags: 0, + len: 0, + index: 0, + keycode, + scancode: [0u8; 32], + }; + + unsafe { sys::eviocgkeycode_v2(self.as_raw_fd(), &mut keymap) } + .map(|_| keymap.scancode[..keymap.len as usize].to_vec()) + .map_err(nix_err) + } + + /// Retrieve the keycode and scancode by index, starting at 0 + pub fn get_scancode_by_index(&self, index: u16) -> io::Result<(u32, Vec)> { + let mut keymap = libc::input_keymap_entry { + flags: INPUT_KEYMAP_BY_INDEX, + len: 0, + index, + keycode: 0, + scancode: [0u8; 32], + }; + + unsafe { sys::eviocgkeycode_v2(self.as_raw_fd(), &mut keymap) } + .map(|_| { + ( + keymap.keycode, + keymap.scancode[..keymap.len as usize].to_vec(), + ) + }) + .map_err(nix_err) + } + + /// Update a scancode by index. The return value is the previous keycode + pub fn update_scancode_by_index( + &self, + index: u16, + keycode: u32, + scancode: &[u8], + ) -> io::Result { + let len = scancode.len(); + + let mut keymap = libc::input_keymap_entry { + flags: INPUT_KEYMAP_BY_INDEX, + len: len as u8, + index, + keycode, + scancode: [0u8; 32], + }; + + keymap.scancode[..len].copy_from_slice(scancode); + + unsafe { sys::eviocskeycode_v2(self.as_raw_fd(), &keymap) } + .map(|keycode| keycode as u32) + .map_err(nix_err) + } + + /// Update a scancode. The return value is the previous keycode + pub fn update_scancode(&self, keycode: u32, scancode: &[u8]) -> io::Result { + let len = scancode.len(); + + let mut keymap = libc::input_keymap_entry { + flags: 0, + len: len as u8, + index: 0, + keycode, + scancode: [0u8; 32], + }; + + keymap.scancode[..len].copy_from_slice(scancode); + + unsafe { sys::eviocskeycode_v2(self.as_raw_fd(), &keymap) } + .map(|keycode| keycode as u32) + .map_err(nix_err) + } + #[cfg(feature = "tokio")] #[inline] pub fn into_event_stream(self) -> io::Result { diff --git a/src/scancodes.rs b/src/scancodes.rs index f6e4fea..254a37f 100644 --- a/src/scancodes.rs +++ b/src/scancodes.rs @@ -188,6 +188,7 @@ evdev_enum!( KEY_MSDOS = 151, KEY_COFFEE = 152, /* AL Terminal Lock/Screensaver */ KEY_DIRECTION = 153, + KEY_ROTATE_DISPLAY = 153, KEY_CYCLEWINDOWS = 154, KEY_MAIL = 155, KEY_BOOKMARKS = 156, /* AC Bookmarks */ @@ -362,6 +363,7 @@ evdev_enum!( KEY_SUBTITLE = 0x172, KEY_ANGLE = 0x173, KEY_ZOOM = 0x174, + KEY_FULL_SCREEN = 0x174, KEY_MODE = 0x175, KEY_KEYBOARD = 0x176, KEY_SCREEN = 0x177, @@ -479,6 +481,10 @@ evdev_enum!( KEY_NUMERIC_9 = 0x209, KEY_NUMERIC_STAR = 0x20a, KEY_NUMERIC_POUND = 0x20b, + KEY_NUMERIC_A = 0x20c, /* Phone key A - HUT Telephony 0xb9 */ + KEY_NUMERIC_B = 0x20d, + KEY_NUMERIC_C = 0x20e, + KEY_NUMERIC_D = 0x20f, KEY_CAMERA_FOCUS = 0x210, KEY_WPS_BUTTON = 0x211, /* WiFi Protected Setup key */ KEY_TOUCHPAD_TOGGLE = 0x212, /* Request switch touchpad on or off */ @@ -498,14 +504,16 @@ evdev_enum!( BTN_DPAD_DOWN = 0x221, BTN_DPAD_LEFT = 0x222, BTN_DPAD_RIGHT = 0x223, - KEY_ALS_TOGGLE = 0x230, /* Ambient light sensor */ - KEY_BUTTONCONFIG = 0x240, /* AL Button Configuration */ - KEY_TASKMANAGER = 0x241, /* AL Task/Project Manager */ - KEY_JOURNAL = 0x242, /* AL Log/Journal/Timecard */ - KEY_CONTROLPANEL = 0x243, /* AL Control Panel */ - KEY_APPSELECT = 0x244, /* AL Select Task/Application */ - KEY_SCREENSAVER = 0x245, /* AL Screen Saver */ - KEY_VOICECOMMAND = 0x246, /* Listening Voice Command */ + KEY_ALS_TOGGLE = 0x230, /* Ambient light sensor */ + KEY_BUTTONCONFIG = 0x240, /* AL Button Configuration */ + KEY_TASKMANAGER = 0x241, /* AL Task/Project Manager */ + KEY_JOURNAL = 0x242, /* AL Log/Journal/Timecard */ + KEY_CONTROLPANEL = 0x243, /* AL Control Panel */ + KEY_APPSELECT = 0x244, /* AL Select Task/Application */ + KEY_SCREENSAVER = 0x245, /* AL Screen Saver */ + KEY_VOICECOMMAND = 0x246, /* Listening Voice Command */ + KEY_ASSISTANT = 0x247, + KEY_KBD_LAYOUT_NEXT = 0x248, KEY_BRIGHTNESS_MIN = 0x250, /* Set Brightness to Minimum */ KEY_BRIGHTNESS_MAX = 0x251, /* Set Brightness to Maximum */ KEY_KBDINPUTASSIST_PREV = 0x260, @@ -514,6 +522,27 @@ evdev_enum!( KEY_KBDINPUTASSIST_NEXTGROUP = 0x263, KEY_KBDINPUTASSIST_ACCEPT = 0x264, KEY_KBDINPUTASSIST_CANCEL = 0x265, + KEY_RIGHT_UP = 0x266, + KEY_RIGHT_DOWN = 0x267, + KEY_LEFT_UP = 0x268, + KEY_LEFT_DOWN = 0x269, + KEY_ROOT_MENU = 0x26a, + KEY_MEDIA_TOP_MENU = 0x26b, + KEY_NUMERIC_11 = 0x26c, + KEY_NUMERIC_12 = 0x26d, + KEY_AUDIO_DESC = 0x26e, + KEY_3D_MODE = 0x26f, + KEY_NEXT_FAVORITE = 0x270, + KEY_STOP_RECORD = 0x271, + KEY_PAUSE_RECORD = 0x272, + KEY_VOD = 0x273, /* Video on Demand */ + KEY_UNMUTE = 0x274, + KEY_FASTREVERSE = 0x275, + KEY_SLOWREVERSE = 0x276, + KEY_DATA = 0x277, + KEY_ONSCREEN_KEYBOARD = 0x278, + KEY_PRIVACY_SCREEN_TOGGLE = 0x279, + KEY_SELECTIVE_SCREENSHOT = 0x27a, BTN_TRIGGER_HAPPY1 = 0x2c0, BTN_TRIGGER_HAPPY2 = 0x2c1, BTN_TRIGGER_HAPPY3 = 0x2c2, diff --git a/src/sync_stream.rs b/src/sync_stream.rs index 355b500..950da92 100644 --- a/src/sync_stream.rs +++ b/src/sync_stream.rs @@ -1,7 +1,7 @@ use crate::constants::*; use crate::device_state::DeviceState; use crate::raw_stream::RawDevice; -use crate::{AttributeSet, AttributeSetRef, InputEvent, InputEventKind, InputId, Key}; +use crate::{AttributeSet, AttributeSetRef, AutoRepeat, InputEvent, InputEventKind, InputId, Key}; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use std::time::SystemTime; @@ -85,6 +85,44 @@ impl Device { self.raw.input_id() } + /// Returns a struct containing the delay and period for auto repeat + pub fn get_auto_repeat(&self) -> Option { + self.raw.get_auto_repeat() + } + + /// Update the delay and period for autorepeat + pub fn update_auto_repeat(&mut self, repeat: &AutoRepeat) -> io::Result<()> { + self.raw.update_auto_repeat(repeat) + } + + /// Retrieve the scancode for a keycode, if any + pub fn get_scancode_by_keycode(&self, keycode: Key) -> io::Result> { + self.raw.get_scancode_by_keycode(keycode.code() as u32) + } + + /// Retrieve the keycode and scancode by index, starting at 0 + pub fn get_scancode_by_index(&self, index: u16) -> io::Result<(u32, Vec)> { + self.raw.get_scancode_by_index(index) + } + + /// Update a scancode. The return value is the previous keycode + pub fn update_scancode(&self, keycode: Key, scancode: &[u8]) -> io::Result { + self.raw + .update_scancode(keycode.code() as u32, scancode) + .map(|keycode| Key::new(keycode as u16)) + } + + /// Update a scancode by index. The return value is the previous keycode + pub fn update_scancode_by_index( + &self, + index: u16, + keycode: Key, + scancode: &[u8], + ) -> io::Result { + self.raw + .update_scancode_by_index(index, keycode.code() as u32, scancode) + } + /// Returns the set of supported "properties" for the device (see `INPUT_PROP_*` in kernel headers) pub fn properties(&self) -> &AttributeSetRef { self.raw.properties() @@ -207,10 +245,6 @@ impl Device { self.raw.misc_properties() } - // pub fn supported_repeats(&self) -> Option { - // self.rep - // } - /// Returns the set of supported simple sounds supported by a device. /// /// You can use these to make really annoying beep sounds come from an internal self-test diff --git a/src/sys.rs b/src/sys.rs index a5ea30e..a9608f6 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,5 +1,5 @@ use libc::c_int; -use libc::{ff_effect, input_absinfo, input_id, uinput_setup}; +use libc::{ff_effect, input_absinfo, input_id, input_keymap_entry, uinput_setup}; // use libc::{ // ff_condition_effect, ff_constant_effect, ff_envelope, ff_periodic_effect, ff_ramp_effect, // ff_replay, ff_rumble_effect, ff_trigger, input_event, input_keymap_entry, @@ -15,10 +15,11 @@ ioctl_read!(eviocgkeycode, b'E', 0x04, [::libc::c_uint; 2]); ioctl_read!(eviocgrep, b'E', 0x03, [::libc::c_uint; 2]); ioctl_read!(eviocgversion, b'E', 0x01, ::libc::c_int); ioctl_write_int!(eviocrmff, b'E', 0x81); -// ioctl!(read eviocgkeycode_v2 with b'E', 0x04; /*struct*/ input_keymap_entry); + +ioctl_read!(eviocgkeycode_v2, b'E', 0x04, input_keymap_entry); // TODO #define EVIOCSFF _IOC ( _IOC_WRITE , 'E' , 0x80 , sizeof ( struct ff_effect ) ) ioctl_write_ptr!(eviocskeycode, b'E', 0x04, [::libc::c_uint; 2]); -// ioctl!(write_int eviocskeycode_v2 with b'E', 0x04; /*struct*/ input_keymap_entry); +ioctl_write_ptr!(eviocskeycode_v2, b'E', 0x04, input_keymap_entry); ioctl_write_ptr!(eviocsrep, b'E', 0x03, [::libc::c_uint; 2]); ioctl_read_buf!(eviocgname, b'E', 0x06, u8);