diff --git a/Cargo.toml b/Cargo.toml index 2935d66..5f1a332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,11 @@ repository = "https://github.com/cmr/evdev" documentation = "https://cmr.github.io/evdev" [dependencies] -ioctl = "*" +ioctl = { path = "/home/cmr/ioctl" } +bitflags = { git = "https://github.com/cmr/bitflags" } +errno = "*" +libc = "*" +fixedbitset = "*" [features] unstable = [] diff --git a/examples/enumerate.rs b/examples/enumerate.rs new file mode 100644 index 0000000..063e0e3 --- /dev/null +++ b/examples/enumerate.rs @@ -0,0 +1,7 @@ +extern crate evdev; + +fn main() { + for d in evdev::enumerate() { + println!("{:?}", d); + } +} diff --git a/src/lib.rs b/src/lib.rs index a93251b..d00416d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,886 @@ -#[test] -fn it_works() { +#[macro_use] +extern crate bitflags; +extern crate ioctl; +extern crate libc; +extern crate errno; +extern crate fixedbitset; + +use std::os::unix::io::*; +use std::os::unix::ffi::*; +use std::path::Path; +use std::ffi::CString; +use fixedbitset::FixedBitSet; + +pub enum Error { + NulError(std::ffi::NulError), + LibcError(errno::Errno), + IoctlError(&'static str, errno::Errno), +} + +impl From for Error { + fn from(e: std::ffi::NulError) -> Error { + Error::NulError(e) + } +} + +impl From for Error { + fn from(e: errno::Errno) -> Error { + Error::LibcError(e) + } +} + +macro_rules! do_ioctl { + ($name:ident($($arg:expr),+)) => {{ + let rc = unsafe { ::ioctl::$name($($arg,)+) }; + if rc < 0 { + return Err(Error::IoctlError(stringify!($name), errno::errno())) + } + rc + }} +} + +struct Fd(libc::c_int); +impl Drop for Fd { + fn drop(&mut self) { + unsafe { libc::close(self.0); } + } +} +impl std::ops::Deref for Fd { + type Target = libc::c_int; + fn deref(&self) -> &libc::c_int { + &self.0 + } +} + + +bitflags! { + flags Types: u32 { + const SYNCHRONIZATION = 1 << 0x00, + const KEY = 1 << 0x01, + const RELATIVE = 1 << 0x02, + const ABSOLUTE = 1 << 0x03, + const MISC = 1 << 0x04, + const SWITCH = 1 << 0x05, + const LED = 1 << 0x11, + const SOUND = 1 << 0x12, + const REPEAT = 1 << 0x14, + const FORCEFEEDBACK = 1 << 0x15, + const POWER = 1 << 0x16, + const FORCEFEEDBACKSTATUS = 1 << 0x17, + } +} +bitflags! { + flags Props: u32 { + const POINTER = 1 << 0x00, + const DIRECT = 1 << 0x01, + const BUTTONPAD = 1 << 0x02, + const SEMI_MT = 1 << 0x03, + const TOPBUTTONPAD = 1 << 0x04, + const POINTING_STICK = 1 << 0x05, + const ACCELEROETER = 1 << 0x06 + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +enum Key { + KEY_RESERVED = 0, + KEY_ESC = 1, + KEY_1 = 2, + KEY_2 = 3, + KEY_3 = 4, + KEY_4 = 5, + KEY_5 = 6, + KEY_6 = 7, + KEY_7 = 8, + KEY_8 = 9, + KEY_9 = 10, + KEY_0 = 11, + KEY_MINUS = 12, + KEY_EQUAL = 13, + KEY_BACKSPACE = 14, + KEY_TAB = 15, + KEY_Q = 16, + KEY_W = 17, + KEY_E = 18, + KEY_R = 19, + KEY_T = 20, + KEY_Y = 21, + KEY_U = 22, + KEY_I = 23, + KEY_O = 24, + KEY_P = 25, + KEY_LEFTBRACE = 26, + KEY_RIGHTBRACE = 27, + KEY_ENTER = 28, + KEY_LEFTCTRL = 29, + KEY_A = 30, + KEY_S = 31, + KEY_D = 32, + KEY_F = 33, + KEY_G = 34, + KEY_H = 35, + KEY_J = 36, + KEY_K = 37, + KEY_L = 38, + KEY_SEMICOLON = 39, + KEY_APOSTROPHE = 40, + KEY_GRAVE = 41, + KEY_LEFTSHIFT = 42, + KEY_BACKSLASH = 43, + KEY_Z = 44, + KEY_X = 45, + KEY_C = 46, + KEY_V = 47, + KEY_B = 48, + KEY_N = 49, + KEY_M = 50, + KEY_COMMA = 51, + KEY_DOT = 52, + KEY_SLASH = 53, + KEY_RIGHTSHIFT = 54, + KEY_KPASTERISK = 55, + KEY_LEFTALT = 56, + KEY_SPACE = 57, + KEY_CAPSLOCK = 58, + KEY_F1 = 59, + KEY_F2 = 60, + KEY_F3 = 61, + KEY_F4 = 62, + KEY_F5 = 63, + KEY_F6 = 64, + KEY_F7 = 65, + KEY_F8 = 66, + KEY_F9 = 67, + KEY_F10 = 68, + KEY_NUMLOCK = 69, + KEY_SCROLLLOCK = 70, + KEY_KP7 = 71, + KEY_KP8 = 72, + KEY_KP9 = 73, + KEY_KPMINUS = 74, + KEY_KP4 = 75, + KEY_KP5 = 76, + KEY_KP6 = 77, + KEY_KPPLUS = 78, + KEY_KP1 = 79, + KEY_KP2 = 80, + KEY_KP3 = 81, + KEY_KP0 = 82, + KEY_KPDOT = 83, + KEY_ZENKAKUHANKAKU =85, + KEY_102ND = 86, + KEY_F11 = 87, + KEY_F12 = 88, + KEY_RO = 89, + KEY_KATAKANA = 90, + KEY_HIRAGANA = 91, + KEY_HENKAN = 92, + KEY_KATAKANAHIRAGANA =93, + KEY_MUHENKAN = 94, + KEY_KPJPCOMMA = 95, + KEY_KPENTER = 96, + KEY_RIGHTCTRL = 97, + KEY_KPSLASH = 98, + KEY_SYSRQ = 99, + KEY_RIGHTALT = 100, + KEY_LINEFEED = 101, + KEY_HOME = 102, + KEY_UP = 103, + KEY_PAGEUP = 104, + KEY_LEFT = 105, + KEY_RIGHT = 106, + KEY_END = 107, + KEY_DOWN = 108, + KEY_PAGEDOWN = 109, + KEY_INSERT = 110, + KEY_DELETE = 111, + KEY_MACRO = 112, + KEY_MUTE = 113, + KEY_VOLUMEDOWN = 114, + KEY_VOLUMEUP = 115, + KEY_POWER = 116 /* SC System Power Down */, + KEY_KPEQUAL = 117, + KEY_KPPLUSMINUS = 118, + KEY_PAUSE = 119, + KEY_SCALE = 120 /* AL Compiz Scale (Expose) */, + KEY_KPCOMMA = 121, + KEY_HANGEUL = 122, + KEY_HANJA = 123, + KEY_YEN = 124, + KEY_LEFTMETA = 125, + KEY_RIGHTMETA = 126, + KEY_COMPOSE = 127, + KEY_STOP = 128 /* AC Stop */, + KEY_AGAIN = 129, + KEY_PROPS = 130 /* AC Properties */, + KEY_UNDO = 131 /* AC Undo */, + KEY_FRONT = 132, + KEY_COPY = 133 /* AC Copy */, + KEY_OPEN = 134 /* AC Open */, + KEY_PASTE = 135 /* AC Paste */, + KEY_FIND = 136 /* AC Search */, + KEY_CUT = 137 /* AC Cut */, + KEY_HELP = 138 /* AL Integrated Help Center */, + KEY_MENU = 139 /* Menu (show menu) */, + KEY_CALC = 140 /* AL Calculator */, + KEY_SETUP = 141, + KEY_SLEEP = 142 /* SC System Sleep */, + KEY_WAKEUP = 143 /* System Wake Up */, + KEY_FILE = 144 /* AL Local Machine Browser */, + KEY_SENDFILE = 145, + KEY_DELETEFILE = 146, + KEY_XFER = 147, + KEY_PROG1 = 148, + KEY_PROG2 = 149, + KEY_WWW = 150 /* AL Internet Browser */, + KEY_MSDOS = 151, + KEY_COFFEE = 152 /* AL Terminal Lock/Screensaver */, + KEY_DIRECTION = 153, + KEY_CYCLEWINDOWS =154, + KEY_MAIL = 155, + KEY_BOOKMARKS = 156 /* AC Bookmarks */, + KEY_COMPUTER = 157, + KEY_BACK = 158 /* AC Back */, + KEY_FORWARD = 159 /* AC Forward */, + KEY_CLOSECD = 160, + KEY_EJECTCD = 161, + KEY_EJECTCLOSECD =162, + KEY_NEXTSONG = 163, + KEY_PLAYPAUSE = 164, + KEY_PREVIOUSSONG =165, + KEY_STOPCD = 166, + KEY_RECORD = 167, + KEY_REWIND = 168, + KEY_PHONE = 169 /* Media Select Telephone */, + KEY_ISO = 170, + KEY_CONFIG = 171 /* AL Consumer Control Configuration */, + KEY_HOMEPAGE = 172 /* AC Home */, + KEY_REFRESH = 173 /* AC Refresh */, + KEY_EXIT = 174 /* AC Exit */, + KEY_MOVE = 175, + KEY_EDIT = 176, + KEY_SCROLLUP = 177, + KEY_SCROLLDOWN = 178, + KEY_KPLEFTPAREN = 179, + KEY_KPRIGHTPAREN =180, + KEY_NEW = 181 /* AC New */, + KEY_REDO = 182 /* AC Redo/Repeat */, + KEY_F13 = 183, + KEY_F14 = 184, + KEY_F15 = 185, + KEY_F16 = 186, + KEY_F17 = 187, + KEY_F18 = 188, + KEY_F19 = 189, + KEY_F20 = 190, + KEY_F21 = 191, + KEY_F22 = 192, + KEY_F23 = 193, + KEY_F24 = 194, + KEY_PLAYCD = 200, + KEY_PAUSECD = 201, + KEY_PROG3 = 202, + KEY_PROG4 = 203, + KEY_DASHBOARD = 204 /* AL Dashboard */, + KEY_SUSPEND = 205, + KEY_CLOSE = 206 /* AC Close */, + KEY_PLAY = 207, + KEY_FASTFORWARD = 208, + KEY_BASSBOOST = 209, + KEY_PRINT = 210 /* AC Print */, + KEY_HP = 211, + KEY_CAMERA = 212, + KEY_SOUND = 213, + KEY_QUESTION = 214, + KEY_EMAIL = 215, + KEY_CHAT = 216, + KEY_SEARCH = 217, + KEY_CONNECT = 218, + KEY_FINANCE = 219, + KEY_SPORT = 220, + KEY_SHOP = 221, + KEY_ALTERASE = 222, + KEY_CANCEL = 223, + KEY_BRIGHTNESSDOWN =224, + KEY_BRIGHTNESSUP =225, + KEY_MEDIA = 226, + KEY_SWITCHVIDEOMODE = 227, + KEY_KBDILLUMTOGGLE = 228, + KEY_KBDILLUMDOWN = 229, + KEY_KBDILLUMUP = 230, + KEY_SEND = 231, + KEY_REPLY = 232, + KEY_FORWARDMAIL = 233, + KEY_SAVE = 234, + KEY_DOCUMENTS = 235, + KEY_BATTERY = 236, + KEY_BLUETOOTH = 237, + KEY_WLAN = 238, + KEY_UWB = 239, + KEY_UNKNOWN = 240, + KEY_VIDEO_NEXT = 241, + KEY_VIDEO_PREV = 242, + KEY_BRIGHTNESS_CYCLE =243, + KEY_BRIGHTNESS_AUTO =244, + KEY_DISPLAY_OFF = 245, + KEY_WWAN = 246, + KEY_RFKILL = 247, + KEY_MICMUTE = 248, + BTN_0 = 0x100, + BTN_1 = 0x101, + BTN_2 = 0x102, + BTN_3 = 0x103, + BTN_4 = 0x104, + BTN_5 = 0x105, + BTN_6 = 0x106, + BTN_7 = 0x107, + BTN_8 = 0x108, + BTN_9 = 0x109, + BTN_LEFT = 0x110, + BTN_RIGHT = 0x111, + BTN_MIDDLE = 0x112, + BTN_SIDE = 0x113, + BTN_EXTRA = 0x114, + BTN_FORWARD = 0x115, + BTN_BACK = 0x116, + BTN_TASK = 0x117, + BTN_TRIGGER = 0x120, + BTN_THUMB = 0x121, + BTN_THUMB2 = 0x122, + BTN_TOP = 0x123, + BTN_TOP2 = 0x124, + BTN_PINKIE = 0x125, + BTN_BASE = 0x126, + BTN_BASE2 = 0x127, + BTN_BASE3 = 0x128, + BTN_BASE4 = 0x129, + BTN_BASE5 = 0x12a, + BTN_BASE6 = 0x12b, + BTN_DEAD = 0x12f, + BTN_SOUTH = 0x130, + BTN_EAST = 0x131, + BTN_C = 0x132, + BTN_NORTH = 0x133, + BTN_WEST = 0x134, + BTN_Z = 0x135, + BTN_TL = 0x136, + BTN_TR = 0x137, + BTN_TL2 = 0x138, + BTN_TR2 = 0x139, + BTN_SELECT = 0x13a, + BTN_START = 0x13b, + BTN_MODE = 0x13c, + BTN_THUMBL = 0x13d, + BTN_THUMBR = 0x13e, + BTN_TOOL_PEN = 0x140, + BTN_TOOL_RUBBER = 0x141, + BTN_TOOL_BRUSH = 0x142, + BTN_TOOL_PENCIL = 0x143, + BTN_TOOL_AIRBRUSH =0x144, + BTN_TOOL_FINGER = 0x145, + BTN_TOOL_MOUSE = 0x146, + BTN_TOOL_LENS = 0x147, + BTN_TOOL_QUINTTAP =0x148 /* Five fingers on trackpad */, + BTN_TOUCH = 0x14a, + BTN_STYLUS = 0x14b, + BTN_STYLUS2 = 0x14c, + BTN_TOOL_DOUBLETAP =0x14d, + BTN_TOOL_TRIPLETAP =0x14e, + BTN_TOOL_QUADTAP =0x14f /* Four fingers on trackpad */, + BTN_GEAR_DOWN = 0x150, + BTN_GEAR_UP = 0x151, + KEY_OK = 0x160, + KEY_SELECT = 0x161, + KEY_GOTO = 0x162, + KEY_CLEAR = 0x163, + KEY_POWER2 = 0x164, + KEY_OPTION = 0x165, + KEY_INFO = 0x166 /* AL OEM Features/Tips/Tutorial */, + KEY_TIME = 0x167, + KEY_VENDOR = 0x168, + KEY_ARCHIVE = 0x169, + KEY_PROGRAM = 0x16a /* Media Select Program Guide */, + KEY_CHANNEL = 0x16b, + KEY_FAVORITES = 0x16c, + KEY_EPG = 0x16d, + KEY_PVR = 0x16e /* Media Select Home */, + KEY_MHP = 0x16f, + KEY_LANGUAGE = 0x170, + KEY_TITLE = 0x171, + KEY_SUBTITLE = 0x172, + KEY_ANGLE = 0x173, + KEY_ZOOM = 0x174, + KEY_MODE = 0x175, + KEY_KEYBOARD = 0x176, + KEY_SCREEN = 0x177, + KEY_PC = 0x178 /* Media Select Computer */, + KEY_TV = 0x179 /* Media Select TV */, + KEY_TV2 = 0x17a /* Media Select Cable */, + KEY_VCR = 0x17b /* Media Select VCR */, + KEY_VCR2 = 0x17c /* VCR Plus */, + KEY_SAT = 0x17d /* Media Select Satellite */, + KEY_SAT2 = 0x17e, + KEY_CD = 0x17f /* Media Select CD */, + KEY_TAPE = 0x180 /* Media Select Tape */, + KEY_RADIO = 0x181, + KEY_TUNER = 0x182 /* Media Select Tuner */, + KEY_PLAYER = 0x183, + KEY_TEXT = 0x184, + KEY_DVD = 0x185 /* Media Select DVD */, + KEY_AUX = 0x186, + KEY_MP3 = 0x187, + KEY_AUDIO = 0x188 /* AL Audio Browser */, + KEY_VIDEO = 0x189 /* AL Movie Browser */, + KEY_DIRECTORY = 0x18a, + KEY_LIST = 0x18b, + KEY_MEMO = 0x18c /* Media Select Messages */, + KEY_CALENDAR = 0x18d, + KEY_RED = 0x18e, + KEY_GREEN = 0x18f, + KEY_YELLOW = 0x190, + KEY_BLUE = 0x191, + KEY_CHANNELUP = 0x192 /* Channel Increment */, + KEY_CHANNELDOWN = 0x193 /* Channel Decrement */, + KEY_FIRST = 0x194, + KEY_LAST = 0x195 /* Recall Last */, + KEY_AB = 0x196, + KEY_NEXT = 0x197, + KEY_RESTART = 0x198, + KEY_SLOW = 0x199, + KEY_SHUFFLE = 0x19a, + KEY_BREAK = 0x19b, + KEY_PREVIOUS = 0x19c, + KEY_DIGITS = 0x19d, + KEY_TEEN = 0x19e, + KEY_TWEN = 0x19f, + KEY_VIDEOPHONE = 0x1a0 /* Media Select Video Phone */, + KEY_GAMES = 0x1a1 /* Media Select Games */, + KEY_ZOOMIN = 0x1a2 /* AC Zoom In */, + KEY_ZOOMOUT = 0x1a3 /* AC Zoom Out */, + KEY_ZOOMRESET = 0x1a4 /* AC Zoom */, + KEY_WORDPROCESSOR =0x1a5 /* AL Word Processor */, + KEY_EDITOR = 0x1a6 /* AL Text Editor */, + KEY_SPREADSHEET = 0x1a7 /* AL Spreadsheet */, + KEY_GRAPHICSEDITOR =0x1a8 /* AL Graphics Editor */, + KEY_PRESENTATION =0x1a9 /* AL Presentation App */, + KEY_DATABASE = 0x1aa /* AL Database App */, + KEY_NEWS = 0x1ab /* AL Newsreader */, + KEY_VOICEMAIL = 0x1ac /* AL Voicemail */, + KEY_ADDRESSBOOK = 0x1ad /* AL Contacts/Address Book */, + KEY_MESSENGER = 0x1ae /* AL Instant Messaging */, + KEY_DISPLAYTOGGLE =0x1af /* Turn display (LCD) on and off */, + KEY_SPELLCHECK = 0x1b0 /* AL Spell Check */, + KEY_LOGOFF = 0x1b1 /* AL Logoff */, + KEY_DOLLAR = 0x1b2, + KEY_EURO = 0x1b3, + KEY_FRAMEBACK = 0x1b4 /* Consumer - transport controls */, + KEY_FRAMEFORWARD =0x1b5, + KEY_CONTEXT_MENU =0x1b6 /* GenDesc - system context menu */, + KEY_MEDIA_REPEAT =0x1b7 /* Consumer - transport control */, + KEY_10CHANNELSUP =0x1b8 /* 10 channels up (10+) */, + KEY_10CHANNELSDOWN =0x1b9 /* 10 channels down (10-) */, + KEY_IMAGES = 0x1ba /* AL Image Browser */, + KEY_DEL_EOL = 0x1c0, + KEY_DEL_EOS = 0x1c1, + KEY_INS_LINE = 0x1c2, + KEY_DEL_LINE = 0x1c3, + KEY_FN = 0x1d0, + KEY_FN_ESC = 0x1d1, + KEY_FN_F1 = 0x1d2, + KEY_FN_F2 = 0x1d3, + KEY_FN_F3 = 0x1d4, + KEY_FN_F4 = 0x1d5, + KEY_FN_F5 = 0x1d6, + KEY_FN_F6 = 0x1d7, + KEY_FN_F7 = 0x1d8, + KEY_FN_F8 = 0x1d9, + KEY_FN_F9 = 0x1da, + KEY_FN_F10 = 0x1db, + KEY_FN_F11 = 0x1dc, + KEY_FN_F12 = 0x1dd, + KEY_FN_1 = 0x1de, + KEY_FN_2 = 0x1df, + KEY_FN_D = 0x1e0, + KEY_FN_E = 0x1e1, + KEY_FN_F = 0x1e2, + KEY_FN_S = 0x1e3, + KEY_FN_B = 0x1e4, + KEY_BRL_DOT1 = 0x1f1, + KEY_BRL_DOT2 = 0x1f2, + KEY_BRL_DOT3 = 0x1f3, + KEY_BRL_DOT4 = 0x1f4, + KEY_BRL_DOT5 = 0x1f5, + KEY_BRL_DOT6 = 0x1f6, + KEY_BRL_DOT7 = 0x1f7, + KEY_BRL_DOT8 = 0x1f8, + KEY_BRL_DOT9 = 0x1f9, + KEY_BRL_DOT10 = 0x1fa, + KEY_NUMERIC_0 = 0x200 /* used by phones, remote controls, */, + KEY_NUMERIC_1 = 0x201 /* and other keypads */, + KEY_NUMERIC_2 = 0x202, + KEY_NUMERIC_3 = 0x203, + KEY_NUMERIC_4 = 0x204, + KEY_NUMERIC_5 = 0x205, + KEY_NUMERIC_6 = 0x206, + KEY_NUMERIC_7 = 0x207, + KEY_NUMERIC_8 = 0x208, + KEY_NUMERIC_9 = 0x209, + KEY_NUMERIC_STAR =0x20a, + KEY_NUMERIC_POUND =0x20b, + KEY_CAMERA_FOCUS =0x210, + KEY_WPS_BUTTON = 0x211 /* WiFi Protected Setup key */, + KEY_TOUCHPAD_TOGGLE =0x212 /* Request switch touchpad on or off */, + KEY_TOUCHPAD_ON = 0x213, + KEY_TOUCHPAD_OFF =0x214, + KEY_CAMERA_ZOOMIN =0x215, + KEY_CAMERA_ZOOMOUT =0x216, + KEY_CAMERA_UP = 0x217, + KEY_CAMERA_DOWN = 0x218, + KEY_CAMERA_LEFT = 0x219, + KEY_CAMERA_RIGHT =0x21a, + KEY_ATTENDANT_ON =0x21b, + KEY_ATTENDANT_OFF =0x21c, + KEY_ATTENDANT_TOGGLE =0x21d /* Attendant call on or off */, + KEY_LIGHTS_TOGGLE =0x21e /* Reading light on or off */, + BTN_DPAD_UP = 0x220, + 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_BRIGHTNESS_MIN = 0x250 /* Set Brightness to Minimum */, + KEY_BRIGHTNESS_MAX = 0x251 /* Set Brightness to Maximum */, + KEY_KBDINPUTASSIST_PREV = 0x260, + KEY_KBDINPUTASSIST_NEXT = 0x261, + KEY_KBDINPUTASSIST_PREVGROUP = 0x262, + KEY_KBDINPUTASSIST_NEXTGROUP = 0x263, + KEY_KBDINPUTASSIST_ACCEPT = 0x264, + KEY_KBDINPUTASSIST_CANCEL = 0x265, + BTN_TRIGGER_HAPPY1 = 0x2c0, + BTN_TRIGGER_HAPPY2 = 0x2c1, + BTN_TRIGGER_HAPPY3 = 0x2c2, + BTN_TRIGGER_HAPPY4 = 0x2c3, + BTN_TRIGGER_HAPPY5 = 0x2c4, + BTN_TRIGGER_HAPPY6 = 0x2c5, + BTN_TRIGGER_HAPPY7 = 0x2c6, + BTN_TRIGGER_HAPPY8 = 0x2c7, + BTN_TRIGGER_HAPPY9 = 0x2c8, + BTN_TRIGGER_HAPPY10 = 0x2c9, + BTN_TRIGGER_HAPPY11 = 0x2ca, + BTN_TRIGGER_HAPPY12 = 0x2cb, + BTN_TRIGGER_HAPPY13 = 0x2cc, + BTN_TRIGGER_HAPPY14 = 0x2cd, + BTN_TRIGGER_HAPPY15 = 0x2ce, + BTN_TRIGGER_HAPPY16 = 0x2cf, + BTN_TRIGGER_HAPPY17 = 0x2d0, + BTN_TRIGGER_HAPPY18 = 0x2d1, + BTN_TRIGGER_HAPPY19 = 0x2d2, + BTN_TRIGGER_HAPPY20 = 0x2d3, + BTN_TRIGGER_HAPPY21 = 0x2d4, + BTN_TRIGGER_HAPPY22 = 0x2d5, + BTN_TRIGGER_HAPPY23 = 0x2d6, + BTN_TRIGGER_HAPPY24 = 0x2d7, + BTN_TRIGGER_HAPPY25 = 0x2d8, + BTN_TRIGGER_HAPPY26 = 0x2d9, + BTN_TRIGGER_HAPPY27 = 0x2da, + BTN_TRIGGER_HAPPY28 = 0x2db, + BTN_TRIGGER_HAPPY29 = 0x2dc, + BTN_TRIGGER_HAPPY30 = 0x2dd, + BTN_TRIGGER_HAPPY31 = 0x2de, + BTN_TRIGGER_HAPPY32 = 0x2df, + BTN_TRIGGER_HAPPY33 = 0x2e0, + BTN_TRIGGER_HAPPY34 = 0x2e1, + BTN_TRIGGER_HAPPY35 = 0x2e2, + BTN_TRIGGER_HAPPY36 = 0x2e3, + BTN_TRIGGER_HAPPY37 = 0x2e4, + BTN_TRIGGER_HAPPY38 = 0x2e5, + BTN_TRIGGER_HAPPY39 = 0x2e6, + BTN_TRIGGER_HAPPY40 = 0x2e7, + KEY_MAX = 0x2ff, +} + +bitflags! { + flags RelativeAxis: u32 { + const REL_X = 1 << 0x00, + const REL_Y = 1 << 0x01, + const REL_Z = 1 << 0x02, + const REL_RX = 1 << 0x03, + const REL_RY = 1 << 0x04, + const REL_RZ = 1 << 0x05, + const REL_HWHEEL = 1 << 0x06, + const REL_DIAL = 1 << 0x07, + const REL_WHEEL = 1 << 0x08, + const REL_MISC = 1 << 0x09, + } +} + +bitflags! { + flags AbsoluteAxis: u64 { + const ABS_X = 1 << 0x00, + const ABS_Y = 1 << 0x01, + const ABS_Z = 1 << 0x02, + const ABS_RX = 1 << 0x03, + const ABS_RY = 1 << 0x04, + const ABS_RZ = 1 << 0x05, + const ABS_THROTTLE = 1 << 0x06, + const ABS_RUDDER = 1 << 0x07, + const ABS_WHEEL = 1 << 0x08, + const ABS_GAS = 1 << 0x09, + const ABS_BRAKE = 1 << 0x0a, + const ABS_HAT0X = 1 << 0x10, + const ABS_HAT0Y = 1 << 0x11, + const ABS_HAT1X = 1 << 0x12, + const ABS_HAT1Y = 1 << 0x13, + const ABS_HAT2X = 1 << 0x14, + const ABS_HAT2Y = 1 << 0x15, + const ABS_HAT3X = 1 << 0x16, + const ABS_HAT3Y = 1 << 0x17, + const ABS_PRESSURE = 1 << 0x18, + const ABS_DISTANCE = 1 << 0x19, + const ABS_TILT_X = 1 << 0x1a, + const ABS_TILT_Y = 1 << 0x1b, + const ABS_TOOL_WIDTH = 1 << 0x1c, + const ABS_VOLUME = 1 << 0x20, + const ABS_MISC = 1 << 0x28, + const ABS_MT_SLOT = 1 << 0x2f/* MT slot being modified */, + const ABS_MT_TOUCH_MAJOR = 1 << 0x30/* Major axis of touching ellipse */, + const ABS_MT_TOUCH_MINOR = 1 << 0x31/* Minor axis (omit if circular) */, + const ABS_MT_WIDTH_MAJOR = 1 << 0x32/* Major axis of approaching ellipse */, + const ABS_MT_WIDTH_MINOR = 1 << 0x33/* Minor axis (omit if circular) */, + const ABS_MT_ORIENTATION = 1 << 0x34/* Ellipse orientation */, + const ABS_MT_POSITION_X = 1 << 0x35/* Center X touch position */, + const ABS_MT_POSITION_Y = 1 << 0x36/* Center Y touch position */, + const ABS_MT_TOOL_TYPE = 1 << 0x37/* Type of touching device */, + const ABS_MT_BLOB_ID = 1 << 0x38/* Group a set of packets as a blob */, + const ABS_MT_TRACKING_ID = 1 << 0x39/* Unique ID of initiated contact */, + const ABS_MT_PRESSURE = 1 << 0x3a/* Pressure on contact area */, + const ABS_MT_DISTANCE = 1 << 0x3b/* Contact hover distance */, + const ABS_MT_TOOL_X = 1 << 0x3c/* Center X tool position */, + const ABS_MT_TOOL_Y = 1 << 0x3d/* Center Y tool position */, + const ABS_MAX = 1 << 0x3f, + } +} + +bitflags! { + flags Switch: u32 { + const SW_LID = 1 << 0x00 /* set = lid shut */, + const SW_TABLET_MODE = 1 << 0x01 /* set = tablet mode */, + const SW_HEADPHONE_INSERT = 1 << 0x02 /* set = inserted */, + const SW_RFKILL_ALL = 1 << 0x03 /* rfkill master switch, type "any" */, + const SW_MICROPHONE_INSERT = 1 << 0x04 /* set = inserted */, + const SW_DOCK = 1 << 0x05 /* set = plugged into dock */, + const SW_LINEOUT_INSERT = 1 << 0x06 /* set = inserted */, + const SW_JACK_PHYSICAL_INSERT = 1 << 0x07 /* set = mechanical switch set */, + const SW_VIDEOOUT_INSERT = 1 << 0x08 /* set = inserted */, + const SW_CAMERA_LENS_COVER = 1 << 0x09 /* set = lens covered */, + const SW_KEYPAD_SLIDE = 1 << 0x0a /* set = keypad slide out */, + const SW_FRONT_PROXIMITY = 1 << 0x0b /* set = front proximity sensor active */, + const SW_ROTATE_LOCK = 1 << 0x0c /* set = rotate locked/disabled */, + const SW_LINEIN_INSERT = 1 << 0x0d /* set = inserted */, + const SW_MUTE_DEVICE = 1 << 0x0e /* set = device disabled */, + const SW_MAX = 1 << 0x0f, + } +} + +bitflags! { + flags Led: u32 { + const LED_NUML = 1 << 0x00, + const LED_CAPSL = 1 << 0x01, + const LED_SCROLLL = 1 << 0x02, + const LED_COMPOSE = 1 << 0x03, + const LED_KANA = 1 << 0x04, + const LED_SLEEP = 1 << 0x05, + const LED_SUSPEND = 1 << 0x06, + const LED_MUTE = 1 << 0x07, + const LED_MISC = 1 << 0x08, + const LED_MAIL = 1 << 0x09, + const LED_CHARGING = 1 << 0x0a, + const LED_MAX = 1 << 0x0f, + } +} + +bitflags! { + flags Misc: u32 { + const MSC_SERIAL = 1 << 0x00, + const MSC_PULSELED = 1 << 0x01, + const MSC_GESTURE = 1 << 0x02, + const MSC_RAW = 1 << 0x03, + const MSC_SCAN = 1 << 0x04, + const MSC_TIMESTAMP = 1 << 0x05, + const MSC_MAX = 1 << 0x07, + } +} + +bitflags! { + flags FFStatus: u32 { + const FF_STATUS_STOPPED = 1 << 0x00, + const FF_STATUS_PLAYING = 1 << 0x01, + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum FFEffect { + FF_RUMBLE = 0x50, + FF_PERIODIC = 0x51, + FF_CONSTANT = 0x52, + FF_SPRING = 0x53, + FF_FRICTION = 0x54, + FF_DAMPER = 0x55, + FF_INERTIA = 0x56, + FF_RAMP = 0x57, + FF_SQUARE = 0x58, + FF_TRIANGLE = 0x59, + FF_SINE = 0x5a, + FF_SAW_UP = 0x5b, + FF_SAW_DOWN = 0x5c, + FF_CUSTOM = 0x5d, + FF_GAIN = 0x60, + FF_AUTOCENTER = 0x61, + FF_MAX = 0x7f, +} + +bitflags! { + flags Repeat: u32 { + const REP_DELAY = 1 << 0x00, + const REP_PERIOD = 1 << 0x01, + } +} + +bitflags! { + flags Sound: u32 { + const SND_CLICK = 1 << 0x00, + const SND_BELL = 1 << 0x01, + const SND_TONE = 1 << 0x02, + } +} + +#[derive(Debug)] +pub struct Device { + fd: RawFd, + ty: Types, + name: CString, + phys: CString, + uniq: CString, + id: ioctl::input_id, + props: Props, + driver_version: libc::c_int, + key_bits: FixedBitSet, + key_vals: FixedBitSet, + rel: RelativeAxis, + abs: AbsoluteAxis, + switch: Switch, + switch_vals: FixedBitSet, + led: Led, + led_vals: FixedBitSet, + misc: Misc, + ff: FixedBitSet, + ff_stat: FFStatus, + rep: Repeat, + snd: Sound, +} + +impl Device { + pub fn open>(path: P) -> Result { + let cstr = match CString::new(path.as_ref().as_os_str().as_bytes()) { + Ok(s) => s, + Err(e) => return Err(Error::NulError(e)) + }; + // FIXME: only need for writing is for setting LED values. re-evaluate always using RDWR + // later. + let fd = Fd(unsafe { libc::open(cstr.as_ptr(), libc::O_NONBLOCK | libc::O_RDWR, 0) }); + if *fd == -1 { + return Err(Error::LibcError(errno::errno())) + } + do_ioctl!(fioclex(*fd)); + let mut dev = Device { + fd: *fd, + ty: Types::empty(), + name: unsafe { CString::from_vec_unchecked(Vec::new()) }, + phys: unsafe { CString::from_vec_unchecked(Vec::new()) }, + uniq: unsafe { CString::from_vec_unchecked(Vec::new()) }, + id: unsafe { std::mem::zeroed() }, + props: Props::empty(), + driver_version: 0, + key_bits: FixedBitSet::with_capacity(Key::KEY_MAX as usize + 1), + key_vals: FixedBitSet::with_capacity(Key::KEY_MAX as usize + 1), + rel: RelativeAxis::empty(), + abs: AbsoluteAxis::empty(), + switch: Switch::empty(), + switch_vals: FixedBitSet::with_capacity(0x10), + led: Led::empty(), + led_vals: FixedBitSet::with_capacity(0x10), + misc: Misc::empty(), + ff: FixedBitSet::with_capacity(FFEffect::FF_MAX as usize + 1), + ff_stat: FFStatus::empty(), + rep: Repeat::empty(), + snd: Sound::empty(), + }; + let mut bits: u32 = 0; + let mut vec = Vec::with_capacity(256); + do_ioctl!(eviocgbit(*fd, 0, 4, &mut bits as *mut _ as *mut u8)); + dev.ty = Types::from_bits(bits).expect("evdev: unexpected type bits! report a bug"); + let dev_len = do_ioctl!(eviocgname(*fd, vec.as_mut_ptr(), 255)); + unsafe { vec.set_len(dev_len as usize - 1) }; + dev.name = CString::new(vec.clone()).unwrap(); + let phys_len = unsafe { ioctl::eviocgphys(*fd, vec.as_mut_ptr(), 255) }; + if phys_len > 0 { + unsafe { vec.set_len(phys_len as usize - 1) }; + dev.phys = CString::new(vec.clone()).unwrap(); + } + let uniq_len = unsafe { ioctl::eviocguniq(*fd, vec.as_mut_ptr(), 255) }; + if uniq_len > 0 { + unsafe { vec.set_len(uniq_len as usize - 1) }; + dev.uniq = CString::new(vec.clone()).unwrap(); + } + do_ioctl!(eviocgid(*fd, &mut dev.id)); + do_ioctl!(eviocgversion(*fd, &mut dev.driver_version)); + do_ioctl!(eviocgprop(*fd, &mut bits as *mut _ as *mut u8, 4)); // todo: handle old kernel + dev.props = Props::from_bits(bits).expect("evdev: unexpected prop bits! report a bug"); + do_ioctl!(eviocgbit(*fd, 31 - KEY.bits().leading_zeros(), (dev.key_bits.len() / 8) as libc::c_int, &mut dev.key_bits.as_mut_slice().as_mut_ptr() as *mut _ as *mut u8)); + do_ioctl!(eviocgbit(*fd, 31 - RELATIVE.bits().leading_zeros(), 4, &mut bits as *mut _ as *mut u8)); + dev.rel = RelativeAxis::from_bits(bits).expect("evdev: unexpected rel bits! report a bug"); + let mut bits64: u64 = 0; + do_ioctl!(eviocgbit(*fd, 31 - ABSOLUTE.bits().leading_zeros(), 8, &mut bits64 as *mut _ as *mut u8)); + dev.abs = AbsoluteAxis::from_bits(bits64).expect("evdev: unexpected abs bits! report a bug"); + do_ioctl!(eviocgbit(*fd, 31 - SWITCH.bits().leading_zeros(), 4, &mut bits as *mut _ as *mut u8)); + dev.switch = Switch::from_bits(bits).expect("evdev: unexpected switch bits! report a bug"); + do_ioctl!(eviocgbit(*fd, 31 - LED.bits().leading_zeros(), 4, &mut bits as *mut _ as *mut u8)); + dev.led = Led::from_bits(bits).expect("evdev: unexpected led bits! report a bug"); + do_ioctl!(eviocgbit(*fd, 31 - MISC.bits().leading_zeros(), 4, &mut bits as *mut _ as *mut u8)); + dev.misc = Misc::from_bits(bits).expect("evdev: unexpected misc bits! report a bug"); + do_ioctl!(eviocgbit(*fd, 31 - FORCEFEEDBACK.bits().leading_zeros(), FFEffect::FF_MAX as libc::c_int + 1, &mut bits as *mut _ as *mut u8)); + do_ioctl!(eviocgbit(*fd, 31 - SOUND.bits().leading_zeros(), 4, &mut bits as *mut _ as *mut u8)); + dev.snd = Sound::from_bits(bits).expect("evdev: unexpected sound bits! report a bug"); + do_ioctl!(eviocgkey(*fd, &mut dev.key_vals.as_mut_slice().as_mut_ptr() as *mut _ as *mut u8, dev.key_vals.len() / 8)); + do_ioctl!(eviocgled(*fd, &mut dev.led_vals.as_mut_slice().as_mut_ptr() as *mut _ as *mut u8, dev.led_vals.len() / 8)); + do_ioctl!(eviocgsw(*fd, &mut dev.switch_vals.as_mut_slice().as_mut_ptr() as *mut _ as *mut u8, dev.switch_vals.len() / 8)); + unsafe { std::mem::forget(fd) }; + Ok(dev) + } +} + +/// Crawls `/dev/input` for evdev devices. +/// +/// Will not bubble up any errors in opening devices or traversing the directory. Instead returns +/// an empty vector or omits the devices that could not be opened. +pub fn enumerate() -> Vec { + let mut res = Vec::new(); + if let Ok(dir) = std::fs::read_dir("/dev/input") { + for entry in dir { + if let Ok(entry) = entry { + if let Ok(dev) = Device::open(entry.path()) { + res.push(dev) + } + } + } + } + res }