Browse Source

Button and relative axes information.

Beoran 4 years ago
parent
commit
0b1a200ba5

+ 0 - 2
os/linux/event_codes.go

@@ -775,7 +775,6 @@ const LED_CNT = (LED_MAX+1)
 /*
  * Autorepeat values
  */
-
 const REP_DELAY = 0x00
 const REP_PERIOD = 0x01
 const REP_MAX = 0x01
@@ -784,7 +783,6 @@ const REP_CNT = (REP_MAX+1)
 /*
  * Sounds
  */
-
 const SND_CLICK = 0x00
 const SND_BELL = 0x01
 const SND_TONE = 0x02

+ 732 - 2
os/linux/event_to_string_linux.go

@@ -68,9 +68,739 @@ func AbsToString(e uint) string {
         case ABS_MT_TOOL_Y:      return "ABS_MT_TOOL_Y" 
         case ABS_MAX:            return "ABS_MAX" 
         case ABS_CNT:            return "ABS_CNT" 
-        default: return fmt.Sprintf("Unknown absolute axis %d", e)
-    
+        default: return fmt.Sprintf("Unknown absolute axis %d", e)    
     }
 }
  
 
+/*
+ * Input event codes to string
+ */
+
+/*
+ * Device properties and quirks
+ */
+func InputPropString(e uint) string {
+    switch e {
+        case INPUT_PROP_POINTER: return "INPUT_PROP_POINTER"
+        case INPUT_PROP_DIRECT: return "INPUT_PROP_DIRECT"
+        case INPUT_PROP_BUTTONPAD: return "INPUT_PROP_BUTTONPAD"
+        case INPUT_PROP_SEMI_MT: return "INPUT_PROP_SEMI_MT"
+        case INPUT_PROP_TOPBUTTONPAD: return "INPUT_PROP_TOPBUTTONPAD"
+        case INPUT_PROP_POINTING_STICK: return "INPUT_PROP_POINTING_STICK"
+        case INPUT_PROP_ACCELEROMETER: return "INPUT_PROP_ACCELEROMETER"
+        case INPUT_PROP_MAX: return "INPUT_PROP_MAX"
+        case INPUT_PROP_CNT: return "INPUT_PROP_CNT"
+        default: return fmt.Sprintf("Unknown INPUT_PROP %d", e)    
+    }
+}
+
+
+/*
+ * Synchronization events.
+ */
+
+func SynString(e uint) string {
+    switch e {
+        case SYN_REPORT: return "SYN_REPORT"
+        case SYN_CONFIG: return "SYN_CONFIG"
+        case SYN_MT_REPORT: return "SYN_MT_REPORT"
+        case SYN_DROPPED: return "SYN_DROPPED"
+        case SYN_MAX: return "SYN_MAX"
+        case SYN_CNT: return "SYN_CNT"
+        default: return fmt.Sprintf("Unknown SYN %d", e)    
+    }
+}
+
+
+/*
+ * Keys and buttons
+ *
+ */
+func KeyString(e uint) string {
+    switch e {
+        case KEY_RESERVED: return "KEY_RESERVED"
+        case KEY_ESC: return "KEY_ESC"
+        case KEY_1: return "KEY_1"
+        case KEY_2: return "KEY_2"
+        case KEY_3: return "KEY_3"
+        case KEY_4: return "KEY_4"
+        case KEY_5: return "KEY_5"
+        case KEY_6: return "KEY_6"
+        case KEY_7: return "KEY_7"
+        case KEY_8: return "KEY_8"
+        case KEY_9: return "KEY_9"
+        case KEY_0: return "KEY_0"
+        case KEY_MINUS: return "KEY_MINUS"
+        case KEY_EQUAL: return "KEY_EQUAL"
+        case KEY_BACKSPACE: return "KEY_BACKSPACE"
+        case KEY_TAB: return "KEY_TAB"
+        case KEY_Q: return "KEY_Q"
+        case KEY_W: return "KEY_W"
+        case KEY_E: return "KEY_E"
+        case KEY_R: return "KEY_R"
+        case KEY_T: return "KEY_T"
+        case KEY_Y: return "KEY_Y"
+        case KEY_U: return "KEY_U"
+        case KEY_I: return "KEY_I"
+        case KEY_O: return "KEY_O"
+        case KEY_P: return "KEY_P"
+        case KEY_LEFTBRACE: return "KEY_LEFTBRACE"
+        case KEY_RIGHTBRACE: return "KEY_RIGHTBRACE"
+        case KEY_ENTER: return "KEY_ENTER"
+        case KEY_LEFTCTRL: return "KEY_LEFTCTRL"
+        case KEY_A: return "KEY_A"
+        case KEY_S: return "KEY_S"
+        case KEY_D: return "KEY_D"
+        case KEY_F: return "KEY_F"
+        case KEY_G: return "KEY_G"
+        case KEY_H: return "KEY_H"
+        case KEY_J: return "KEY_J"
+        case KEY_K: return "KEY_K"
+        case KEY_L: return "KEY_L"
+        case KEY_SEMICOLON: return "KEY_SEMICOLON"
+        case KEY_APOSTROPHE: return "KEY_APOSTROPHE"
+        case KEY_GRAVE: return "KEY_GRAVE"
+        case KEY_LEFTSHIFT: return "KEY_LEFTSHIFT"
+        case KEY_BACKSLASH: return "KEY_BACKSLASH"
+        case KEY_Z: return "KEY_Z"
+        case KEY_X: return "KEY_X"
+        case KEY_C: return "KEY_C"
+        case KEY_V: return "KEY_V"
+        case KEY_B: return "KEY_B"
+        case KEY_N: return "KEY_N"
+        case KEY_M: return "KEY_M"
+        case KEY_COMMA: return "KEY_COMMA"
+        case KEY_DOT: return "KEY_DOT"
+        case KEY_SLASH: return "KEY_SLASH"
+        case KEY_RIGHTSHIFT: return "KEY_RIGHTSHIFT"
+        case KEY_KPASTERISK: return "KEY_KPASTERISK"
+        case KEY_LEFTALT: return "KEY_LEFTALT"
+        case KEY_SPACE: return "KEY_SPACE"
+        case KEY_CAPSLOCK: return "KEY_CAPSLOCK"
+        case KEY_F1: return "KEY_F1"
+        case KEY_F2: return "KEY_F2"
+        case KEY_F3: return "KEY_F3"
+        case KEY_F4: return "KEY_F4"
+        case KEY_F5: return "KEY_F5"
+        case KEY_F6: return "KEY_F6"
+        case KEY_F7: return "KEY_F7"
+        case KEY_F8: return "KEY_F8"
+        case KEY_F9: return "KEY_F9"
+        case KEY_F10: return "KEY_F10"
+        case KEY_NUMLOCK: return "KEY_NUMLOCK"
+        case KEY_SCROLLLOCK: return "KEY_SCROLLLOCK"
+        case KEY_KP7: return "KEY_KP7"
+        case KEY_KP8: return "KEY_KP8"
+        case KEY_KP9: return "KEY_KP9"
+        case KEY_KPMINUS: return "KEY_KPMINUS"
+        case KEY_KP4: return "KEY_KP4"
+        case KEY_KP5: return "KEY_KP5"
+        case KEY_KP6: return "KEY_KP6"
+        case KEY_KPPLUS: return "KEY_KPPLUS"
+        case KEY_KP1: return "KEY_KP1"
+        case KEY_KP2: return "KEY_KP2"
+        case KEY_KP3: return "KEY_KP3"
+        case KEY_KP0: return "KEY_KP0"
+        case KEY_KPDOT: return "KEY_KPDOT"
+
+        case KEY_ZENKAKUHANKAKU: return "KEY_ZENKAKUHANKAKU"
+        case KEY_102ND: return "KEY_102ND"
+        case KEY_F11: return "KEY_F11"
+        case KEY_F12: return "KEY_F12"
+        case KEY_RO: return "KEY_RO"
+        case KEY_KATAKANA: return "KEY_KATAKANA"
+        case KEY_HIRAGANA: return "KEY_HIRAGANA"
+        case KEY_HENKAN: return "KEY_HENKAN"
+        case KEY_KATAKANAHIRAGANA: return "KEY_KATAKANAHIRAGANA"
+        case KEY_MUHENKAN: return "KEY_MUHENKAN"
+        case KEY_KPJPCOMMA: return "KEY_KPJPCOMMA"
+        case KEY_KPENTER: return "KEY_KPENTER"
+        case KEY_RIGHTCTRL: return "KEY_RIGHTCTRL"
+        case KEY_KPSLASH: return "KEY_KPSLASH"
+        case KEY_SYSRQ: return "KEY_SYSRQ"
+        case KEY_RIGHTALT: return "KEY_RIGHTALT"
+        case KEY_LINEFEED: return "KEY_LINEFEED"
+        case KEY_HOME: return "KEY_HOME"
+        case KEY_UP: return "KEY_UP"
+        case KEY_PAGEUP: return "KEY_PAGEUP"
+        case KEY_LEFT: return "KEY_LEFT"
+        case KEY_RIGHT: return "KEY_RIGHT"
+        case KEY_END: return "KEY_END"
+        case KEY_DOWN: return "KEY_DOWN"
+        case KEY_PAGEDOWN: return "KEY_PAGEDOWN"
+        case KEY_INSERT: return "KEY_INSERT"
+        case KEY_DELETE: return "KEY_DELETE"
+        case KEY_MACRO: return "KEY_MACRO"
+        case KEY_MUTE: return "KEY_MUTE"
+        case KEY_VOLUMEDOWN: return "KEY_VOLUMEDOWN"
+        case KEY_VOLUMEUP: return "KEY_VOLUMEUP"
+        case KEY_POWER: return "KEY_POWER"
+        case KEY_KPEQUAL: return "KEY_KPEQUAL"
+        case KEY_KPPLUSMINUS: return "KEY_KPPLUSMINUS"
+        case KEY_PAUSE: return "KEY_PAUSE"
+        case KEY_SCALE: return "KEY_SCALE"
+
+        case KEY_KPCOMMA: return "KEY_KPCOMMA"
+        case KEY_HANGEUL: return "KEY_HANGEUL"
+        case KEY_HANJA: return "KEY_HANJA"
+        case KEY_YEN: return "KEY_YEN"
+        case KEY_LEFTMETA: return "KEY_LEFTMETA"
+        case KEY_RIGHTMETA: return "KEY_RIGHTMETA"
+        case KEY_COMPOSE: return "KEY_COMPOSE"
+
+        case KEY_STOP: return "KEY_STOP"
+        case KEY_AGAIN: return "KEY_AGAIN"
+        case KEY_PROPS: return "KEY_PROPS"
+        case KEY_UNDO: return "KEY_UNDO"
+        case KEY_FRONT: return "KEY_FRONT"
+        case KEY_COPY: return "KEY_COPY"
+        case KEY_OPEN: return "KEY_OPEN"
+        case KEY_PASTE: return "KEY_PASTE"
+        case KEY_FIND: return "KEY_FIND"
+        case KEY_CUT: return "KEY_CUT"
+        case KEY_HELP: return "KEY_HELP"
+        case KEY_MENU: return "KEY_MENU"
+        case KEY_CALC: return "KEY_CALC"
+        case KEY_SETUP: return "KEY_SETUP"
+        case KEY_SLEEP: return "KEY_SLEEP"
+        case KEY_WAKEUP: return "KEY_WAKEUP"
+        case KEY_FILE: return "KEY_FILE"
+        case KEY_SENDFILE: return "KEY_SENDFILE"
+        case KEY_DELETEFILE: return "KEY_DELETEFILE"
+        case KEY_XFER: return "KEY_XFER"
+        case KEY_PROG1: return "KEY_PROG1"
+        case KEY_PROG2: return "KEY_PROG2"
+        case KEY_WWW: return "KEY_WWW"
+        case KEY_MSDOS: return "KEY_MSDOS"
+        case KEY_SCREENLOCK: return "KEY_SCREENLOCK"
+        case KEY_ROTATE_DISPLAY: return "KEY_ROTATE_DISPLAY"
+        case KEY_CYCLEWINDOWS: return "KEY_CYCLEWINDOWS"
+        case KEY_MAIL: return "KEY_MAIL"
+        case KEY_BOOKMARKS: return "KEY_BOOKMARKS"
+        case KEY_COMPUTER: return "KEY_COMPUTER"
+        case KEY_BACK: return "KEY_BACK"
+        case KEY_FORWARD: return "KEY_FORWARD"
+        case KEY_CLOSECD: return "KEY_CLOSECD"
+        case KEY_EJECTCD: return "KEY_EJECTCD"
+        case KEY_EJECTCLOSECD: return "KEY_EJECTCLOSECD"
+        case KEY_NEXTSONG: return "KEY_NEXTSONG"
+        case KEY_PLAYPAUSE: return "KEY_PLAYPAUSE"
+        case KEY_PREVIOUSSONG: return "KEY_PREVIOUSSONG"
+        case KEY_STOPCD: return "KEY_STOPCD"
+        case KEY_RECORD: return "KEY_RECORD"
+        case KEY_REWIND: return "KEY_REWIND"
+        case KEY_PHONE: return "KEY_PHONE"
+        case KEY_ISO: return "KEY_ISO"
+        case KEY_CONFIG: return "KEY_CONFIG"
+        case KEY_HOMEPAGE: return "KEY_HOMEPAGE"
+        case KEY_REFRESH: return "KEY_REFRESH"
+        case KEY_EXIT: return "KEY_EXIT"
+        case KEY_MOVE: return "KEY_MOVE"
+        case KEY_EDIT: return "KEY_EDIT"
+        case KEY_SCROLLUP: return "KEY_SCROLLUP"
+        case KEY_SCROLLDOWN: return "KEY_SCROLLDOWN"
+        case KEY_KPLEFTPAREN: return "KEY_KPLEFTPAREN"
+        case KEY_KPRIGHTPAREN: return "KEY_KPRIGHTPAREN"
+        case KEY_NEW: return "KEY_NEW"
+        case KEY_REDO: return "KEY_REDO"
+
+        case KEY_F13: return "KEY_F13"
+        case KEY_F14: return "KEY_F14"
+        case KEY_F15: return "KEY_F15"
+        case KEY_F16: return "KEY_F16"
+        case KEY_F17: return "KEY_F17"
+        case KEY_F18: return "KEY_F18"
+        case KEY_F19: return "KEY_F19"
+        case KEY_F20: return "KEY_F20"
+        case KEY_F21: return "KEY_F21"
+        case KEY_F22: return "KEY_F22"
+        case KEY_F23: return "KEY_F23"
+        case KEY_F24: return "KEY_F24"
+
+        case KEY_PLAYCD: return "KEY_PLAYCD"
+        case KEY_PAUSECD: return "KEY_PAUSECD"
+        case KEY_PROG3: return "KEY_PROG3"
+        case KEY_PROG4: return "KEY_PROG4"
+        case KEY_DASHBOARD: return "KEY_DASHBOARD"
+        case KEY_SUSPEND: return "KEY_SUSPEND"
+        case KEY_CLOSE: return "KEY_CLOSE"
+        case KEY_PLAY: return "KEY_PLAY"
+        case KEY_FASTFORWARD: return "KEY_FASTFORWARD"
+        case KEY_BASSBOOST: return "KEY_BASSBOOST"
+        case KEY_PRINT: return "KEY_PRINT"
+        case KEY_HP: return "KEY_HP"
+        case KEY_CAMERA: return "KEY_CAMERA"
+        case KEY_SOUND: return "KEY_SOUND"
+        case KEY_QUESTION: return "KEY_QUESTION"
+        case KEY_EMAIL: return "KEY_EMAIL"
+        case KEY_CHAT: return "KEY_CHAT"
+        case KEY_SEARCH: return "KEY_SEARCH"
+        case KEY_CONNECT: return "KEY_CONNECT"
+        case KEY_FINANCE: return "KEY_FINANCE"
+        case KEY_SPORT: return "KEY_SPORT"
+        case KEY_SHOP: return "KEY_SHOP"
+        case KEY_ALTERASE: return "KEY_ALTERASE"
+        case KEY_CANCEL: return "KEY_CANCEL"
+        case KEY_BRIGHTNESSDOWN: return "KEY_BRIGHTNESSDOWN"
+        case KEY_BRIGHTNESSUP: return "KEY_BRIGHTNESSUP"
+        case KEY_MEDIA: return "KEY_MEDIA"
+
+        case KEY_SWITCHVIDEOMODE: return "KEY_SWITCHVIDEOMODE"
+        case KEY_KBDILLUMTOGGLE: return "KEY_KBDILLUMTOGGLE"
+        case KEY_KBDILLUMDOWN: return "KEY_KBDILLUMDOWN"
+        case KEY_KBDILLUMUP: return "KEY_KBDILLUMUP"
+
+        case KEY_SEND: return "KEY_SEND"
+        case KEY_REPLY: return "KEY_REPLY"
+        case KEY_FORWARDMAIL: return "KEY_FORWARDMAIL"
+        case KEY_SAVE: return "KEY_SAVE"
+        case KEY_DOCUMENTS: return "KEY_DOCUMENTS"
+
+        case KEY_BATTERY: return "KEY_BATTERY"
+
+        case KEY_BLUETOOTH: return "KEY_BLUETOOTH"
+        case KEY_WLAN: return "KEY_WLAN"
+        case KEY_UWB: return "KEY_UWB"
+
+        case KEY_UNKNOWN: return "KEY_UNKNOWN"
+
+        case KEY_VIDEO_NEXT: return "KEY_VIDEO_NEXT"
+        case KEY_VIDEO_PREV: return "KEY_VIDEO_PREV"
+        case KEY_BRIGHTNESS_CYCLE: return "KEY_BRIGHTNESS_CYCLE"
+        case KEY_BRIGHTNESS_AUTO: return "KEY_BRIGHTNESS_AUTO"
+        case KEY_DISPLAY_OFF: return "KEY_DISPLAY_OFF"
+
+        case KEY_WWAN: return "KEY_WWAN"
+        case KEY_RFKILL: return "KEY_RFKILL"
+
+        case KEY_MICMUTE: return "KEY_MICMUTE"
+
+        /* Code 255 is reserved for special needs of AT keyboard driver */
+
+        case BTN_0: return "BTN_0"
+        case BTN_1: return "BTN_1"
+        case BTN_2: return "BTN_2"
+        case BTN_3: return "BTN_3"
+        case BTN_4: return "BTN_4"
+        case BTN_5: return "BTN_5"
+        case BTN_6: return "BTN_6"
+        case BTN_7: return "BTN_7"
+        case BTN_8: return "BTN_8"
+        case BTN_9: return "BTN_9"
+
+        case BTN_LEFT: return "BTN_LEFT"
+        case BTN_RIGHT: return "BTN_RIGHT"
+        case BTN_MIDDLE: return "BTN_MIDDLE"
+        case BTN_SIDE: return "BTN_SIDE"
+        case BTN_EXTRA: return "BTN_EXTRA"
+        case BTN_FORWARD: return "BTN_FORWARD"
+        case BTN_BACK: return "BTN_BACK"
+        case BTN_TASK: return "BTN_TASK"
+
+        case BTN_JOYSTICK: return "BTN_JOYSTICK"
+        case BTN_THUMB: return "BTN_THUMB"
+        case BTN_THUMB2: return "BTN_THUMB2"
+        case BTN_TOP: return "BTN_TOP"
+        case BTN_TOP2: return "BTN_TOP2"
+        case BTN_PINKIE: return "BTN_PINKIE"
+        case BTN_BASE: return "BTN_BASE"
+        case BTN_BASE2: return "BTN_BASE2"
+        case BTN_BASE3: return "BTN_BASE3"
+        case BTN_BASE4: return "BTN_BASE4"
+        case BTN_BASE5: return "BTN_BASE5"
+        case BTN_BASE6: return "BTN_BASE6"
+        case BTN_DEAD: return "BTN_DEAD"
+
+        case BTN_A: return "BTN_A"
+        case BTN_B: return "BTN_B"
+        case BTN_C: return "BTN_C"
+        case BTN_X: return "BTN_X"
+        case BTN_Y: return "BTN_Y"
+        case BTN_Z: return "BTN_Z"
+        case BTN_TL: return "BTN_TL"
+        case BTN_TR: return "BTN_TR"
+        case BTN_TL2: return "BTN_TL2"
+        case BTN_TR2: return "BTN_TR2"
+        case BTN_SELECT: return "BTN_SELECT"
+        case BTN_START: return "BTN_START"
+        case BTN_MODE: return "BTN_MODE"
+        case BTN_THUMBL: return "BTN_THUMBL"
+        case BTN_THUMBR: return "BTN_THUMBR"
+
+        case BTN_TOOL_PEN: return "BTN_TOOL_PEN"
+        case BTN_TOOL_RUBBER: return "BTN_TOOL_RUBBER"
+        case BTN_TOOL_BRUSH: return "BTN_TOOL_BRUSH"
+        case BTN_TOOL_PENCIL: return "BTN_TOOL_PENCIL"
+        case BTN_TOOL_AIRBRUSH: return "BTN_TOOL_AIRBRUSH"
+        case BTN_TOOL_FINGER: return "BTN_TOOL_FINGER"
+        case BTN_TOOL_MOUSE: return "BTN_TOOL_MOUSE"
+        case BTN_TOOL_LENS: return "BTN_TOOL_LENS"
+        case BTN_TOOL_QUINTTAP: return "BTN_TOOL_QUINTTAP"
+        case BTN_TOUCH: return "BTN_TOUCH"
+        case BTN_STYLUS: return "BTN_STYLUS"
+        case BTN_STYLUS2: return "BTN_STYLUS2"
+        case BTN_TOOL_DOUBLETAP: return "BTN_TOOL_DOUBLETAP"
+        case BTN_TOOL_TRIPLETAP: return "BTN_TOOL_TRIPLETAP"
+        case BTN_TOOL_QUADTAP: return "BTN_TOOL_QUADTAP"
+
+        case BTN_GEAR_DOWN: return "BTN_GEAR_DOWN"
+        case BTN_GEAR_UP: return "BTN_GEAR_UP"
+
+        case KEY_OK: return "KEY_OK"
+        case KEY_SELECT: return "KEY_SELECT"
+        case KEY_GOTO: return "KEY_GOTO"
+        case KEY_CLEAR: return "KEY_CLEAR"
+        case KEY_POWER2: return "KEY_POWER2"
+        case KEY_OPTION: return "KEY_OPTION"
+        case KEY_INFO: return "KEY_INFO"
+        case KEY_TIME: return "KEY_TIME"
+        case KEY_VENDOR: return "KEY_VENDOR"
+        case KEY_ARCHIVE: return "KEY_ARCHIVE"
+        case KEY_PROGRAM: return "KEY_PROGRAM"
+        case KEY_CHANNEL: return "KEY_CHANNEL"
+        case KEY_FAVORITES: return "KEY_FAVORITES"
+        case KEY_EPG: return "KEY_EPG"
+        case KEY_PVR: return "KEY_PVR"
+        case KEY_MHP: return "KEY_MHP"
+        case KEY_LANGUAGE: return "KEY_LANGUAGE"
+        case KEY_TITLE: return "KEY_TITLE"
+        case KEY_SUBTITLE: return "KEY_SUBTITLE"
+        case KEY_ANGLE: return "KEY_ANGLE"
+        case KEY_ZOOM: return "KEY_ZOOM"
+        case KEY_MODE: return "KEY_MODE"
+        case KEY_KEYBOARD: return "KEY_KEYBOARD"
+        case KEY_SCREEN: return "KEY_SCREEN"
+        case KEY_PC: return "KEY_PC"
+        case KEY_TV: return "KEY_TV"
+        case KEY_TV2: return "KEY_TV2"
+        case KEY_VCR: return "KEY_VCR"
+        case KEY_VCR2: return "KEY_VCR2"
+        case KEY_SAT: return "KEY_SAT"
+        case KEY_SAT2: return "KEY_SAT2"
+        case KEY_CD: return "KEY_CD"
+        case KEY_TAPE: return "KEY_TAPE"
+        case KEY_RADIO: return "KEY_RADIO"
+        case KEY_TUNER: return "KEY_TUNER"
+        case KEY_PLAYER: return "KEY_PLAYER"
+        case KEY_TEXT: return "KEY_TEXT"
+        case KEY_DVD: return "KEY_DVD"
+        case KEY_AUX: return "KEY_AUX"
+        case KEY_MP3: return "KEY_MP3"
+        case KEY_AUDIO: return "KEY_AUDIO"
+        case KEY_VIDEO: return "KEY_VIDEO"
+        case KEY_DIRECTORY: return "KEY_DIRECTORY"
+        case KEY_LIST: return "KEY_LIST"
+        case KEY_MEMO: return "KEY_MEMO"
+        case KEY_CALENDAR: return "KEY_CALENDAR"
+        case KEY_RED: return "KEY_RED"
+        case KEY_GREEN: return "KEY_GREEN"
+        case KEY_YELLOW: return "KEY_YELLOW"
+        case KEY_BLUE: return "KEY_BLUE"
+        case KEY_CHANNELUP: return "KEY_CHANNELUP"
+        case KEY_CHANNELDOWN: return "KEY_CHANNELDOWN"
+        case KEY_FIRST: return "KEY_FIRST"
+        case KEY_LAST: return "KEY_LAST"
+        case KEY_AB: return "KEY_AB"
+        case KEY_NEXT: return "KEY_NEXT"
+        case KEY_RESTART: return "KEY_RESTART"
+        case KEY_SLOW: return "KEY_SLOW"
+        case KEY_SHUFFLE: return "KEY_SHUFFLE"
+        case KEY_BREAK: return "KEY_BREAK"
+        case KEY_PREVIOUS: return "KEY_PREVIOUS"
+        case KEY_DIGITS: return "KEY_DIGITS"
+        case KEY_TEEN: return "KEY_TEEN"
+        case KEY_TWEN: return "KEY_TWEN"
+        case KEY_VIDEOPHONE: return "KEY_VIDEOPHONE"
+        case KEY_GAMES: return "KEY_GAMES"
+        case KEY_ZOOMIN: return "KEY_ZOOMIN"
+        case KEY_ZOOMOUT: return "KEY_ZOOMOUT"
+        case KEY_ZOOMRESET: return "KEY_ZOOMRESET"
+        case KEY_WORDPROCESSOR: return "KEY_WORDPROCESSOR"
+        case KEY_EDITOR: return "KEY_EDITOR"
+        case KEY_SPREADSHEET: return "KEY_SPREADSHEET"
+        case KEY_GRAPHICSEDITOR: return "KEY_GRAPHICSEDITOR"
+        case KEY_PRESENTATION: return "KEY_PRESENTATION"
+        case KEY_DATABASE: return "KEY_DATABASE"
+        case KEY_NEWS: return "KEY_NEWS"
+        case KEY_VOICEMAIL: return "KEY_VOICEMAIL"
+        case KEY_ADDRESSBOOK: return "KEY_ADDRESSBOOK"
+        case KEY_MESSENGER: return "KEY_MESSENGER"
+        case KEY_DISPLAYTOGGLE: return "KEY_DISPLAYTOGGLE"
+        case KEY_SPELLCHECK: return "KEY_SPELLCHECK"
+        case KEY_LOGOFF: return "KEY_LOGOFF"
+
+        case KEY_DOLLAR: return "KEY_DOLLAR"
+        case KEY_EURO: return "KEY_EURO"
+
+        case KEY_FRAMEBACK: return "KEY_FRAMEBACK"
+        case KEY_FRAMEFORWARD: return "KEY_FRAMEFORWARD"
+        case KEY_CONTEXT_MENU: return "KEY_CONTEXT_MENU"
+        case KEY_MEDIA_REPEAT: return "KEY_MEDIA_REPEAT"
+        case KEY_10CHANNELSUP: return "KEY_10CHANNELSUP"
+        case KEY_10CHANNELSDOWN: return "KEY_10CHANNELSDOWN"
+        case KEY_IMAGES: return "KEY_IMAGES"
+
+        case KEY_DEL_EOL: return "KEY_DEL_EOL"
+        case KEY_DEL_EOS: return "KEY_DEL_EOS"
+        case KEY_INS_LINE: return "KEY_INS_LINE"
+        case KEY_DEL_LINE: return "KEY_DEL_LINE"
+
+        case KEY_FN: return "KEY_FN"
+        case KEY_FN_ESC: return "KEY_FN_ESC"
+        case KEY_FN_F1: return "KEY_FN_F1"
+        case KEY_FN_F2: return "KEY_FN_F2"
+        case KEY_FN_F3: return "KEY_FN_F3"
+        case KEY_FN_F4: return "KEY_FN_F4"
+        case KEY_FN_F5: return "KEY_FN_F5"
+        case KEY_FN_F6: return "KEY_FN_F6"
+        case KEY_FN_F7: return "KEY_FN_F7"
+        case KEY_FN_F8: return "KEY_FN_F8"
+        case KEY_FN_F9: return "KEY_FN_F9"
+        case KEY_FN_F10: return "KEY_FN_F10"
+        case KEY_FN_F11: return "KEY_FN_F11"
+        case KEY_FN_F12: return "KEY_FN_F12"
+        case KEY_FN_1: return "KEY_FN_1"
+        case KEY_FN_2: return "KEY_FN_2"
+        case KEY_FN_D: return "KEY_FN_D"
+        case KEY_FN_E: return "KEY_FN_E"
+        case KEY_FN_F: return "KEY_FN_F"
+        case KEY_FN_S: return "KEY_FN_S"
+        case KEY_FN_B: return "KEY_FN_B"
+
+        case KEY_BRL_DOT1: return "KEY_BRL_DOT1"
+        case KEY_BRL_DOT2: return "KEY_BRL_DOT2"
+        case KEY_BRL_DOT3: return "KEY_BRL_DOT3"
+        case KEY_BRL_DOT4: return "KEY_BRL_DOT4"
+        case KEY_BRL_DOT5: return "KEY_BRL_DOT5"
+        case KEY_BRL_DOT6: return "KEY_BRL_DOT6"
+        case KEY_BRL_DOT7: return "KEY_BRL_DOT7"
+        case KEY_BRL_DOT8: return "KEY_BRL_DOT8"
+        case KEY_BRL_DOT9: return "KEY_BRL_DOT9"
+        case KEY_BRL_DOT10: return "KEY_BRL_DOT10"
+
+        case KEY_NUMERIC_0: return "KEY_NUMERIC_0"
+        case KEY_NUMERIC_1: return "KEY_NUMERIC_1"
+        case KEY_NUMERIC_2: return "KEY_NUMERIC_2"
+        case KEY_NUMERIC_3: return "KEY_NUMERIC_3"
+        case KEY_NUMERIC_4: return "KEY_NUMERIC_4"
+        case KEY_NUMERIC_5: return "KEY_NUMERIC_5"
+        case KEY_NUMERIC_6: return "KEY_NUMERIC_6"
+        case KEY_NUMERIC_7: return "KEY_NUMERIC_7"
+        case KEY_NUMERIC_8: return "KEY_NUMERIC_8"
+        case KEY_NUMERIC_9: return "KEY_NUMERIC_9"
+        case KEY_NUMERIC_STAR: return "KEY_NUMERIC_STAR"
+        case KEY_NUMERIC_POUND: return "KEY_NUMERIC_POUND"
+        case KEY_NUMERIC_A: return "KEY_NUMERIC_A"
+        case KEY_NUMERIC_B: return "KEY_NUMERIC_B"
+        case KEY_NUMERIC_C: return "KEY_NUMERIC_C"
+        case KEY_NUMERIC_D: return "KEY_NUMERIC_D"
+
+        case KEY_CAMERA_FOCUS: return "KEY_CAMERA_FOCUS"
+        case KEY_WPS_BUTTON: return "KEY_WPS_BUTTON"
+
+        case KEY_TOUCHPAD_TOGGLE: return "KEY_TOUCHPAD_TOGGLE"
+        case KEY_TOUCHPAD_ON: return "KEY_TOUCHPAD_ON"
+        case KEY_TOUCHPAD_OFF: return "KEY_TOUCHPAD_OFF"
+
+        case KEY_CAMERA_ZOOMIN: return "KEY_CAMERA_ZOOMIN"
+        case KEY_CAMERA_ZOOMOUT: return "KEY_CAMERA_ZOOMOUT"
+        case KEY_CAMERA_UP: return "KEY_CAMERA_UP"
+        case KEY_CAMERA_DOWN: return "KEY_CAMERA_DOWN"
+        case KEY_CAMERA_LEFT: return "KEY_CAMERA_LEFT"
+        case KEY_CAMERA_RIGHT: return "KEY_CAMERA_RIGHT"
+
+        case KEY_ATTENDANT_ON: return "KEY_ATTENDANT_ON"
+        case KEY_ATTENDANT_OFF: return "KEY_ATTENDANT_OFF"
+        case KEY_ATTENDANT_TOGGLE: return "KEY_ATTENDANT_TOGGLE"
+        case KEY_LIGHTS_TOGGLE: return "KEY_LIGHTS_TOGGLE"
+
+        case BTN_DPAD_UP: return "BTN_DPAD_UP"
+        case BTN_DPAD_DOWN: return "BTN_DPAD_DOWN"
+        case BTN_DPAD_LEFT: return "BTN_DPAD_LEFT"
+        case BTN_DPAD_RIGHT: return "BTN_DPAD_RIGHT"
+
+        case KEY_ALS_TOGGLE: return "KEY_ALS_TOGGLE"
+
+        case KEY_BUTTONCONFIG: return "KEY_BUTTONCONFIG"
+        case KEY_TASKMANAGER: return "KEY_TASKMANAGER"
+        case KEY_JOURNAL: return "KEY_JOURNAL"
+        case KEY_CONTROLPANEL: return "KEY_CONTROLPANEL"
+        case KEY_APPSELECT: return "KEY_APPSELECT"
+        case KEY_SCREENSAVER: return "KEY_SCREENSAVER"
+        case KEY_VOICECOMMAND: return "KEY_VOICECOMMAND"
+
+        case KEY_BRIGHTNESS_MIN: return "KEY_BRIGHTNESS_MIN"
+        case KEY_BRIGHTNESS_MAX: return "KEY_BRIGHTNESS_MAX"
+
+        case KEY_KBDINPUTASSIST_PREV: return "KEY_KBDINPUTASSIST_PREV"
+        case KEY_KBDINPUTASSIST_NEXT: return "KEY_KBDINPUTASSIST_NEXT"
+        case KEY_KBDINPUTASSIST_PREVGROUP: return "KEY_KBDINPUTASSIST_PREVGROUP"
+        case KEY_KBDINPUTASSIST_NEXTGROUP: return "KEY_KBDINPUTASSIST_NEXTGROUP"
+        case KEY_KBDINPUTASSIST_ACCEPT: return "KEY_KBDINPUTASSIST_ACCEPT"
+        case KEY_KBDINPUTASSIST_CANCEL: return "KEY_KBDINPUTASSIST_CANCEL"
+
+        case BTN_TRIGGER_HAPPY1: return "BTN_TRIGGER_HAPPY1"
+        case BTN_TRIGGER_HAPPY2: return "BTN_TRIGGER_HAPPY2"
+        case BTN_TRIGGER_HAPPY3: return "BTN_TRIGGER_HAPPY3"
+        case BTN_TRIGGER_HAPPY4: return "BTN_TRIGGER_HAPPY4"
+        case BTN_TRIGGER_HAPPY5: return "BTN_TRIGGER_HAPPY5"
+        case BTN_TRIGGER_HAPPY6: return "BTN_TRIGGER_HAPPY6"
+        case BTN_TRIGGER_HAPPY7: return "BTN_TRIGGER_HAPPY7"
+        case BTN_TRIGGER_HAPPY8: return "BTN_TRIGGER_HAPPY8"
+        case BTN_TRIGGER_HAPPY9: return "BTN_TRIGGER_HAPPY9"
+        case BTN_TRIGGER_HAPPY10: return "BTN_TRIGGER_HAPPY10"
+        case BTN_TRIGGER_HAPPY11: return "BTN_TRIGGER_HAPPY11"
+        case BTN_TRIGGER_HAPPY12: return "BTN_TRIGGER_HAPPY12"
+        case BTN_TRIGGER_HAPPY13: return "BTN_TRIGGER_HAPPY13"
+        case BTN_TRIGGER_HAPPY14: return "BTN_TRIGGER_HAPPY14"
+        case BTN_TRIGGER_HAPPY15: return "BTN_TRIGGER_HAPPY15"
+        case BTN_TRIGGER_HAPPY16: return "BTN_TRIGGER_HAPPY16"
+        case BTN_TRIGGER_HAPPY17: return "BTN_TRIGGER_HAPPY17"
+        case BTN_TRIGGER_HAPPY18: return "BTN_TRIGGER_HAPPY18"
+        case BTN_TRIGGER_HAPPY19: return "BTN_TRIGGER_HAPPY19"
+        case BTN_TRIGGER_HAPPY20: return "BTN_TRIGGER_HAPPY20"
+        case BTN_TRIGGER_HAPPY21: return "BTN_TRIGGER_HAPPY21"
+        case BTN_TRIGGER_HAPPY22: return "BTN_TRIGGER_HAPPY22"
+        case BTN_TRIGGER_HAPPY23: return "BTN_TRIGGER_HAPPY23"
+        case BTN_TRIGGER_HAPPY24: return "BTN_TRIGGER_HAPPY24"
+        case BTN_TRIGGER_HAPPY25: return "BTN_TRIGGER_HAPPY25"
+        case BTN_TRIGGER_HAPPY26: return "BTN_TRIGGER_HAPPY26"
+        case BTN_TRIGGER_HAPPY27: return "BTN_TRIGGER_HAPPY27"
+        case BTN_TRIGGER_HAPPY28: return "BTN_TRIGGER_HAPPY28"
+        case BTN_TRIGGER_HAPPY29: return "BTN_TRIGGER_HAPPY29"
+        case BTN_TRIGGER_HAPPY30: return "BTN_TRIGGER_HAPPY30"
+        case BTN_TRIGGER_HAPPY31: return "BTN_TRIGGER_HAPPY31"
+        case BTN_TRIGGER_HAPPY32: return "BTN_TRIGGER_HAPPY32"
+        case BTN_TRIGGER_HAPPY33: return "BTN_TRIGGER_HAPPY33"
+        case BTN_TRIGGER_HAPPY34: return "BTN_TRIGGER_HAPPY34"
+        case BTN_TRIGGER_HAPPY35: return "BTN_TRIGGER_HAPPY35"
+        case BTN_TRIGGER_HAPPY36: return "BTN_TRIGGER_HAPPY36"
+        case BTN_TRIGGER_HAPPY37: return "BTN_TRIGGER_HAPPY37"
+        case BTN_TRIGGER_HAPPY38: return "BTN_TRIGGER_HAPPY38"
+        case BTN_TRIGGER_HAPPY39: return "BTN_TRIGGER_HAPPY39"
+        case BTN_TRIGGER_HAPPY40: return "BTN_TRIGGER_HAPPY40"
+
+        /* We avoid low common keys in module aliases so they don't get huge. */
+        case KEY_MAX: return "KEY_MAX"
+        case KEY_CNT: return "KEY_CNT"
+        default: return fmt.Sprintf("Unknown KEY or BUTTON %d", e)    
+    }
+}
+
+/*
+ * Relative axes
+ */
+func RelString(e uint) string {
+    switch e {
+        case REL_X: return "REL_X"
+        case REL_Y: return "REL_Y"
+        case REL_Z: return "REL_Z"
+        case REL_RX: return "REL_RX"
+        case REL_RY: return "REL_RY"
+        case REL_RZ: return "REL_RZ"
+        case REL_HWHEEL: return "REL_HWHEEL"
+        case REL_DIAL: return "REL_DIAL"
+        case REL_WHEEL: return "REL_WHEEL"
+        case REL_MISC: return "REL_MISC"
+        case REL_MAX: return "REL_MAX"
+        case REL_CNT: return "REL_CNT"
+        default: return fmt.Sprintf("Unknown REL %d", e)    
+    }
+}
+
+
+
+/*
+ * Switch events
+ */
+func SwitchString(e uint) string {
+    switch e {
+        case SW_LID: return "SW_LID"
+        case SW_TABLET_MODE: return "SW_TABLET_MODE"
+        case SW_HEADPHONE_INSERT: return "SW_HEADPHONE_INSERT"
+        case SW_RADIO: return "SW_RADIO"
+        case SW_MICROPHONE_INSERT: return "SW_MICROPHONE_INSERT"
+        case SW_DOCK: return "SW_DOCK"
+        case SW_LINEOUT_INSERT: return "SW_LINEOUT_INSERT"
+        case SW_JACK_PHYSICAL_INSERT: return "SW_JACK_PHYSICAL_INSERT"
+        case SW_VIDEOOUT_INSERT: return "SW_VIDEOOUT_INSERT"
+        case SW_CAMERA_LENS_COVER: return "SW_CAMERA_LENS_COVER"
+        case SW_KEYPAD_SLIDE: return "SW_KEYPAD_SLIDE"
+        case SW_FRONT_PROXIMITY: return "SW_FRONT_PROXIMITY"
+        case SW_ROTATE_LOCK: return "SW_ROTATE_LOCK"
+        case SW_LINEIN_INSERT: return "SW_LINEIN_INSERT"
+        case SW_MUTE_DEVICE: return "SW_MUTE_DEVICE"
+        case SW_MAX: return "SW_MAX"
+        case SW_CNT: return "SW_CNT"
+        default: return fmt.Sprintf("Unknown SW %d", e)    
+    }
+}
+
+/*
+ * Misc events
+ */
+func MscString(e uint) string {
+    switch e {
+        case MSC_SERIAL: return "MSC_SERIAL"
+        case MSC_PULSELED: return "MSC_PULSELED"
+        case MSC_GESTURE: return "MSC_GESTURE"
+        case MSC_RAW: return "MSC_RAW"
+        case MSC_SCAN: return "MSC_SCAN"
+        case MSC_TIMESTAMP: return "MSC_TIMESTAMP"
+        case MSC_MAX: return "MSC_MAX"
+        case MSC_CNT: return "MSC_CNT"
+        default: return fmt.Sprintf("Unknown MSC %d", e)    
+    }
+}
+
+/*
+ * LEDs
+ */
+func LedString(e uint) string {
+    switch e {
+        case LED_NUML: return "LED_NUML"
+        case LED_CAPSL: return "LED_CAPSL"
+        case LED_SCROLLL: return "LED_SCROLLL"
+        case LED_COMPOSE: return "LED_COMPOSE"
+        case LED_KANA: return "LED_KANA"
+        case LED_SLEEP: return "LED_SLEEP"
+        case LED_SUSPEND: return "LED_SUSPEND"
+        case LED_MUTE: return "LED_MUTE"
+        case LED_MISC: return "LED_MISC"
+        case LED_MAIL: return "LED_MAIL"
+        case LED_CHARGING: return "LED_CHARGING"
+        case LED_MAX: return "LED_MAX"
+        case LED_CNT: return "LED_CNT"
+        default: return fmt.Sprintf("Unknown LED %d", e)    
+    }
+}
+
+/*
+ * Autorepeat values
+ */
+func RepString(e uint) string {
+    switch e {
+        case REP_DELAY: return "REP_DELAY"
+        case REP_PERIOD: return "REP_PERIOD"
+        case REP_CNT: return "REP_CNT"
+        default: return fmt.Sprintf("Unknown REP %d", e)    
+    }
+}
+
+
+/*
+ * Sounds
+ */
+func SndString(e uint) string {
+    switch e {
+        case SND_CLICK: return "SND_CLICK"
+        case SND_BELL: return "SND_BELL"
+        case SND_TONE: return "SND_TONE"
+        case SND_MAX: return "SND_MAX"
+        case SND_CNT: return "SND_CNT"
+        default: return fmt.Sprintf("Unknown SND %d", e)    
+    }
+}
+
+

+ 124 - 8
os/linux/input/input_linux.go

@@ -3,11 +3,14 @@
 package input
 
 import "gitlab.com/beoran/galago/os/linux"
+import "strings"
 import "os"
 import "unsafe"
 import syscall "golang.org/x/sys/unix"
 import "fmt"
 import "path/filepath"
+import "time"
+import "runtime"
 
 
 // Device models an input device
@@ -17,12 +20,19 @@ type Device struct {
 
 const Directory = "/dev/input"
 
+func (d * Device) KeepAlive() {
+    runtime.KeepAlive(d.File)
+}
+
 func Open(name string) (*Device, error) {
     f, err := os.OpenFile(filepath.Join(Directory, name), 
         syscall.O_ASYNC|syscall.O_NONBLOCK|os.O_RDWR, 0666)
     if err != nil {
         return nil, err
     }
+    if err = syscall.SetNonblock(int(f.Fd()), true); err != nil {
+        return nil, err
+    }
     return &Device{File: f}, nil
 } 
 
@@ -31,7 +41,18 @@ func List() ([]string, error) {
     if err != nil {
         return nil, err
     }
-    return dir.Readdirnames(-1)
+    
+    names, err := dir.Readdirnames(-1)
+    if err != nil {
+        return names, err
+    }
+    results := names[:0]
+    for _, name := range names {
+        if strings.HasPrefix(name, "event") {
+            results = append(results, name)
+        }
+    }
+    return results, nil
 }
 
 
@@ -46,6 +67,7 @@ func (d * Device) Ioctl(code uint32, pointer unsafe.Pointer) error {
     if (errno != 0) {
         return errno
     }
+    d.KeepAlive()
     return nil
 }
 
@@ -57,8 +79,10 @@ func (d * Device) DriverVersion() (int32, error) {
     return res, err
 }
 
+const NAME_MAX = 256
+
 func (d * Device) Name() (string, error) {
-    buffer := [256]byte{}
+    buffer := [NAME_MAX]byte{}
     err := d.Ioctl(linux.EVIOCGNAME(uintptr(len(buffer))), unsafe.Pointer(&buffer))
     return string(buffer[0:len(buffer)]), err
 }
@@ -73,7 +97,9 @@ func (d * Device) Id() (linux.INPUT_id, error) {
 // The Linux developers thought it was a great idea a to use an array of 
 // unsigned longs for the bit flags of input devices. Of course, the length 
 // of an unsigned long is platform dependent which then entails all sorts of
-// gymnastics to extract the bits from the array...
+// gymnastics to extract the bits from the array.
+// To avoid this, we use a byte array instead, which might lessen performance,
+// but is much easier to use.
 
 const SIZEOF_LONG = uint(unsafe.Sizeof(*((*linux.UnsignedLong)(nil))))      
 const BITS_PER_LONG = SIZEOF_LONG * 8
@@ -82,8 +108,10 @@ func BitsToLong(bits uint) uint {
     return ((bits) + (8 * SIZEOF_LONG) - 1) / (8 * SIZEOF_LONG)
 }
 
+const TOPOLOGY_MAX = 256
+
 func (d * Device) Topology() (string, error) {
-    buffer := [256]byte{}
+    buffer := [TOPOLOGY_MAX]byte{}
     err := d.Ioctl(linux.EVIOCGPHYS(uintptr(len(buffer))), unsafe.Pointer(&buffer))
     return string(buffer[0:len(buffer)]), err
 }
@@ -104,7 +132,6 @@ func (se SupportedEvent) String() string {
     return se.Name()
 }
 
-
 func (d * Device) SupportedEvents() ([]SupportedEvent, error) {
     var bits [linux.EV_MAX / 8 + 1]uint8
     size := unsafe.Sizeof(bits)
@@ -129,12 +156,12 @@ func (d * Device) SupportedEvents() ([]SupportedEvent, error) {
 type InputAbsinfo linux.INPUT_absinfo
 type InputEvent linux.INPUT_event
 
-
 type AbsoluteAxis struct {
     Index uint
     InputAbsinfo
 }
 
+
 func (ax AbsoluteAxis) Name() string {
     return linux.AbsToString(ax.Index)
 }
@@ -149,9 +176,14 @@ func (ax AbsoluteAxis) String() string {
                 ax.Fuzz)
 }
 
+func (ev InputEvent) Time() time.Time {
+    return time.Unix(ev.Timeval.Tv_sec, ev.Timeval.Tv_usec)
+}
+
+
 func (ev InputEvent) String() string {
-    return fmt.Sprintf("Event: Time: %d Type: %s, Code: %d, Value:%d",
-        ev.Time, 
+    return fmt.Sprintf("Event: Time: %s Type: %s, Code: %d, Value:%d",
+        ev.Time().Format("2006-01-02 15:04:05.000000"), 
         linux.EvToString(uint(ev.Type)),
         ev.Code,
         ev.Value)
@@ -166,12 +198,30 @@ func (d * Device) Read(pointer unsafe.Pointer, size uintptr) (uintptr, error) {
         uintptr(d.Fd()),
         uintptr(pointer),
         uintptr(size))
+    d.KeepAlive()
+
     if (errno != 0) {
         return read, errno
     }
     return read, nil
 }
 
+// Write performs a write syscall on the given device
+func (d * Device) Write(pointer unsafe.Pointer, size uintptr) (uintptr, error) {
+    fmt.Printf("write: %d %d %d\n", uintptr(d.Fd()), uintptr(pointer), uintptr(size))
+    wrote, _, errno := syscall.Syscall(
+        syscall.SYS_WRITE,
+        uintptr(d.Fd()),
+        uintptr(pointer),
+        uintptr(size))
+    d.KeepAlive()
+
+    if (errno != 0) {
+        return wrote, errno
+    }
+    return wrote, nil
+}
+
 
 const READ_EVENTS_MAX = 64
 
@@ -186,6 +236,16 @@ func (d * Device) ReadEvents() ([]InputEvent, error) {
     return events[0:len], nil
 }
 
+const WRITE_EVENTS_MAX = 64
+
+func (d * Device) WriteEvents(events []InputEvent) (uint, error) {
+    size := unsafe.Sizeof(events[0]) * uintptr(len(events)) 
+    wrote, err := d.Write(unsafe.Pointer(&events[0]), size) 
+    len := uint(wrote / size) 
+    return len, err
+}
+
+
 func (d * Device) SupportedAxes() ([]AbsoluteAxis, error) { 
     var abs_feat InputAbsinfo;
     var abs_b [linux.ABS_MAX/8 + 1]uint8;
@@ -211,6 +271,62 @@ func (d * Device) SupportedAxes() ([]AbsoluteAxis, error) {
 }
 
 
+type SupportedKey uint
+
+func (se SupportedKey) Name() string {
+    return linux.KeyString(uint(se))
+}
+
+func (se SupportedKey) String() string {
+    return se.Name()
+}
+
+func (d * Device) SupportedKeys() ([]SupportedKey, error) { 
+    var bits [linux.KEY_MAX / 8 + 1]uint8
+    size := unsafe.Sizeof(bits)
+	err := d.Ioctl(linux.EVIOCGBIT(linux.EV_KEY, uintptr(size)), unsafe.Pointer(&bits));
+    if err != nil {
+        return nil, err
+    }
+    fmt.Printf("size %d, bits: %v\n", size, bits)
+    result := []SupportedKey{}
+    for i := uint(0); i < uint(linux.KEY_MAX); i++ {
+        if (TestBit(bits[0:len(bits)],i)) {
+            result = append(result, SupportedKey(uint(i)))
+		}
+    }
+    return result, nil
+}
+
+type RelativeAxis uint
+
+func (se RelativeAxis) Name() string {
+    return linux.RelString(uint(se))
+}
+
+func (se RelativeAxis) String() string {
+    return se.Name()
+}
+
+func (d * Device) RelativeAxes() ([]RelativeAxis, error) { 
+    var bits [linux.REL_MAX / 8 + 1]uint8
+    size := unsafe.Sizeof(bits)
+	err := d.Ioctl(linux.EVIOCGBIT(linux.EV_REL, uintptr(size)), unsafe.Pointer(&bits));
+    if err != nil {
+        return nil, err
+    }
+    fmt.Printf("size %d, bits: %v\n", size, bits)
+    result := []RelativeAxis{}
+    for i := uint(0); i < uint(linux.REL_MAX); i++ {
+        if (TestBit(bits[0:len(bits)],i)) {
+            result = append(result, RelativeAxis(uint(i)))
+		}
+    }
+    return result, nil
+}
+
+
+
 
 /*    
 	printf("Supported events:\n");

+ 46 - 0
os/linux/input/input_linux_test.go

@@ -9,6 +9,19 @@ import "gitlab.com/beoran/galago/os/linux"
 
 
 const IN = "by-id/usb-0583_USB_2-axis_8-button_gamepad-event-joystick"
+const IN2 = "by-id/usb-PixArt_USB_Optical_Mouse-event-mouse"
+
+func TestList(t * testing.T) {
+    names, err := List()
+    if err != nil {
+        t.Errorf("Error List: %s\n", err)
+        return
+    }
+    for _, name := range names {
+        t.Logf("Device: %s\n", name)
+    }
+}
+
 
 func TestGetDriverVersion(t * testing.T) {
     device , err := Open(IN)
@@ -94,6 +107,23 @@ func TestSupportedEvents(t * testing.T) {
 		
 }
 
+func TestSupportedKeys(t * testing.T) {
+    device , err := Open(IN)
+    if err != nil {
+        t.Errorf("Error Open: %s\n", err)
+        return
+    }
+    defer device.Close()
+    keys, err := device.SupportedKeys()
+
+	t.Logf("Supported keys:\n")
+	for i, key := range keys {
+        t.Logf("Supported: %d: %d %s", i, int(key), key.Name())
+    }
+		
+}
+
+
 func TestSupportedAxes(t * testing.T) {
     device , err := Open(IN)
     if err != nil {
@@ -110,6 +140,22 @@ func TestSupportedAxes(t * testing.T) {
 }
 
 
+func TestRelativeAxes(t * testing.T) {
+    device , err := Open(IN2)
+    if err != nil {
+        t.Errorf("Error Open: %s\n", err)
+        return
+    }
+    defer device.Close()
+    events, err := device.RelativeAxes()
+
+	t.Logf("Supported relative axes:\n")
+	for i, ev := range events {
+        t.Logf("Supported: %d: %d %s", i, int(ev), ev.Name())
+    }
+		
+}
+
 func TestReadEvents(t * testing.T) {
     device , err := Open(IN)
     if err != nil {

+ 4 - 4
os/linux/input_linux.go

@@ -9,10 +9,10 @@ import "unsafe"
 // so it is exported. 
 
 type INPUT_event struct {
-	Time    Timeval
-	Type    uint16
-	Code    uint16
-	Value   int32
+	Timeval    Timeval
+	Type        uint16
+	Code        uint16
+	Value       int32
 }
 
 /*