879 lines
26 KiB
Python
879 lines
26 KiB
Python
# Copyright 2007 Jeff Epler <jepler@unpythonic.net>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
import struct, fcntl, array, os, select, glob, fnmatch, time, re
|
|
|
|
size_shift = 16
|
|
def SZ(a,b): return a | (b<<size_shift)
|
|
|
|
def get_name(f):
|
|
r = fcntl.ioctl(f, SZ(EVIOCGNAME, 1024), '\0' * 1024)
|
|
return r.decode('utf-8', errors='replace').rstrip('\0')
|
|
|
|
def get_phys(f):
|
|
r = fcntl.ioctl(f, SZ(EVIOCGPHYS, 1024), '\0' * 1024)
|
|
return r.decode('utf-8', errors='replace').rstrip('\0')
|
|
|
|
# get_uniq seems to return -ENOENT on all my devices
|
|
def get_uniq(f):
|
|
r = fcntl.ioctl(f, SZ(EVIOCGUNIQ, 1024), '\0' * 1024)
|
|
return r.decode('utf-8', errors='replace').rstrip('\0')
|
|
|
|
def get_bits(f, o=0):
|
|
o = EV_invert.get(o, o)
|
|
if o == 'EV_SYN': fmap, rmap, prefix = EV_invert, EV, "SYN"
|
|
elif o == 'EV_KEY': fmap, rmap, prefix = KEYBTN_invert, KEY, "KEY"
|
|
elif o == 'EV_LED': fmap, rmap, prefix = LED_invert, LED, "LED"
|
|
elif o == 'EV_ABS': fmap, rmap, prefix = ABS_invert, ABS, "ABS"
|
|
elif o == 'EV_REL': fmap, rmap, prefix = REL_invert, REL, "REL"
|
|
else: raise ValueError("get_bits: unexpected map %s" % o)
|
|
|
|
sz = max(fmap) + 1
|
|
a = fcntl.ioctl(f, SZ(EVIOCGBIT+EV[o], sz), '\0' * ((sz+7)//8))
|
|
ret = set()
|
|
for j, ch in enumerate(a):
|
|
for i in range(8):
|
|
b = 1<<i
|
|
if ch & b:
|
|
k = j*8+i
|
|
if k not in fmap:
|
|
name = "%s_%d" % (prefix, k)
|
|
fmap[k] = name
|
|
rmap[name] = k
|
|
ret.add(fmap[k])
|
|
return ret
|
|
|
|
def get_keys(f):
|
|
ret = set()
|
|
|
|
class InputId:
|
|
format = "HHHH"
|
|
size = struct.calcsize(format)
|
|
def __init__(self, buf):
|
|
self.bustype, self.vendor, self.product, self.version = \
|
|
struct.unpack(self.format, buf)
|
|
self.bustype = BUS_invert.get(self.bustype, self.bustype)
|
|
@classmethod
|
|
def get(cls, fd):
|
|
buf = fcntl.ioctl(fd, EVIOCGID, '\0' * cls.size)
|
|
return cls(buf)
|
|
|
|
def __repr__(self):
|
|
return "<InputId: %s %04x:%04x version=%d>" % (
|
|
self.bustype, self.vendor, self.product, self.version)
|
|
class AbsInfo:
|
|
format = "iiiii"
|
|
size = struct.calcsize(format)
|
|
def __init__(self, *args):
|
|
if len(args) == 1:
|
|
self.value, self.minimum, self.maximum, self.fuzz, self.flat = \
|
|
struct.unpack(self.format, args[0])
|
|
else:
|
|
self.value, self.minimum, self.maximum, self.fuzz, self.flat = args
|
|
def __repr__(self):
|
|
return "<AbsInfo: %8d %8d %8d %8d %8d>" % (
|
|
self.value, self.minimum, self.maximum, self.fuzz, self.flat)
|
|
|
|
@classmethod
|
|
def get(cls, fd, idx):
|
|
idx = ABS.get(idx, idx)
|
|
buf = fcntl.ioctl(fd, EVIOCGABS + idx, '\0' * cls.size)
|
|
return cls(buf)
|
|
|
|
def set(self, fd, idx):
|
|
idx = ABS.get(idx, idx)
|
|
buf = struct.pack(format, self.value, self.minimum, self.maximum,
|
|
self.fuzz, self.flat)
|
|
fcntl.ioctl(fd, EVIOCSABS + idx, buf)
|
|
|
|
def mapcode(type, code):
|
|
if type == EV_REL: return REL_invert.get(code, code)
|
|
if type == EV_ABS: return ABS_invert.get(code, code)
|
|
if type == EV_KEY:
|
|
if code in KEY_invert:
|
|
return KEY_invert[code]
|
|
return BTN_invert.get(code, code)
|
|
if type == EV_REL: return REL_invert.get(code, code)
|
|
|
|
class Event:
|
|
format = "llHHi"
|
|
size = struct.calcsize(format)
|
|
|
|
def __init__(self, buf):
|
|
data = struct.unpack(self.format, buf)
|
|
self.time = data[0] + data[1] * 1e-9
|
|
self.type = EV_invert[data[2]]
|
|
self.code = data[3]
|
|
self.value = data[4]
|
|
|
|
@classmethod
|
|
def read(cls, f):
|
|
if isinstance(f, int):
|
|
buf = os.read(f, cls.size)
|
|
else:
|
|
buf = f.read(size)
|
|
return cls(buf)
|
|
|
|
@classmethod
|
|
def write(cls, f, *args):
|
|
if len(args) == 3:
|
|
event_time = time.time()
|
|
time_seconds = int(event_time)
|
|
time_ns = int((event_time - time_seconds) * 1e9)
|
|
type, code, value = args
|
|
elif len(args) == 4:
|
|
event_time, type, code, value = args
|
|
time_seconds = int(event_time)
|
|
time_ns = int((event_time - time_seconds) * 1e9)
|
|
else:
|
|
time_seconds, time_ns, type, code, value = args
|
|
if type == 'EV_LED' and isinstance(code, str): code = LED[code]
|
|
type = EV.get(type, type)
|
|
buf = struct.pack(cls.format, time_seconds, time_ns, type, code, value)
|
|
os.write(f, buf)
|
|
|
|
def __repr__(self):
|
|
code = self.code
|
|
if isinstance(code, int): code = "%04x" % code
|
|
return "<Event %s %s %8d>" % (self.type, code, self.value)
|
|
|
|
EV = {
|
|
'EV_SYN': 0x00,
|
|
'EV_KEY': 0x01,
|
|
'EV_REL': 0x02,
|
|
'EV_ABS': 0x03,
|
|
'EV_MSC': 0x04,
|
|
'EV_SW': 0x05,
|
|
'EV_LED': 0x11,
|
|
'EV_SND': 0x12,
|
|
'EV_REP': 0x14,
|
|
'EV_FF': 0x15,
|
|
'EV_PWR': 0x16,
|
|
'EV_FF_STATUS': 0x17,
|
|
}
|
|
REL = {
|
|
'REL_X': 0x00,
|
|
'REL_Y': 0x01,
|
|
'REL_Z': 0x02,
|
|
'REL_RX': 0x03,
|
|
'REL_RY': 0x04,
|
|
'REL_RZ': 0x05,
|
|
'REL_HWHEEL': 0x06,
|
|
'REL_DIAL': 0x07,
|
|
'REL_WHEEL': 0x08,
|
|
'REL_MISC': 0x09,
|
|
}
|
|
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,
|
|
'KEY_KPEQUAL': 117,
|
|
'KEY_KPPLUSMINUS': 118,
|
|
'KEY_PAUSE': 119,
|
|
'KEY_KPCOMMA': 121,
|
|
'KEY_HANGUEL': 122,
|
|
'KEY_HANJA': 123,
|
|
'KEY_YEN': 124,
|
|
'KEY_LEFTMETA': 125,
|
|
'KEY_RIGHTMETA': 126,
|
|
'KEY_COMPOSE': 127,
|
|
'KEY_STOP': 128,
|
|
'KEY_AGAIN': 129,
|
|
'KEY_PROPS': 130,
|
|
'KEY_UNDO': 131,
|
|
'KEY_FRONT': 132,
|
|
'KEY_COPY': 133,
|
|
'KEY_OPEN': 134,
|
|
'KEY_PASTE': 135,
|
|
'KEY_FIND': 136,
|
|
'KEY_CUT': 137,
|
|
'KEY_HELP': 138,
|
|
'KEY_MENU': 139,
|
|
'KEY_CALC': 140,
|
|
'KEY_SETUP': 141,
|
|
'KEY_SLEEP': 142,
|
|
'KEY_WAKEUP': 143,
|
|
'KEY_FILE': 144,
|
|
'KEY_SENDFILE': 145,
|
|
'KEY_DELETEFILE': 146,
|
|
'KEY_XFER': 147,
|
|
'KEY_PROG1': 148,
|
|
'KEY_PROG2': 149,
|
|
'KEY_WWW': 150,
|
|
'KEY_MSDOS': 151,
|
|
'KEY_COFFEE': 152,
|
|
'KEY_DIRECTION': 153,
|
|
'KEY_CYCLEWINDOWS': 154,
|
|
'KEY_MAIL': 155,
|
|
'KEY_BOOKMARKS': 156,
|
|
'KEY_COMPUTER': 157,
|
|
'KEY_BACK': 158,
|
|
'KEY_FORWARD': 159,
|
|
'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,
|
|
'KEY_ISO': 170,
|
|
'KEY_CONFIG': 171,
|
|
'KEY_HOMEPAGE': 172,
|
|
'KEY_REFRESH': 173,
|
|
'KEY_EXIT': 174,
|
|
'KEY_MOVE': 175,
|
|
'KEY_EDIT': 176,
|
|
'KEY_SCROLLUP': 177,
|
|
'KEY_SCROLLDOWN': 178,
|
|
'KEY_KPLEFTPAREN': 179,
|
|
'KEY_KPRIGHTPAREN': 180,
|
|
'KEY_NEW': 181,
|
|
'KEY_REDO': 182,
|
|
'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_SUSPEND': 205,
|
|
'KEY_CLOSE': 206,
|
|
'KEY_PLAY': 207,
|
|
'KEY_FASTFORWARD': 208,
|
|
'KEY_BASSBOOST': 209,
|
|
'KEY_PRINT': 210,
|
|
'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_UNKNOWN': 240,
|
|
'KEY_VIDEO_NEXT': 241,
|
|
'KEY_VIDEO_PREV': 242,
|
|
'KEY_BRIGHTNESS_CYCLE': 243,
|
|
'KEY_BRIGHTNESS_ZERO': 244,
|
|
'KEY_DISPLAY_OFF': 245,
|
|
'KEY_WIMAX': 246,
|
|
'KEY_RFKILL': 247,
|
|
|
|
'KEY_OK': 0x160,
|
|
'KEY_SELECT': 0x161,
|
|
'KEY_GOTO': 0x162,
|
|
'KEY_CLEAR': 0x163,
|
|
'KEY_POWER2': 0x164,
|
|
'KEY_OPTION': 0x165,
|
|
'KEY_INFO': 0x166,
|
|
'KEY_TIME': 0x167,
|
|
'KEY_VENDOR': 0x168,
|
|
'KEY_ARCHIVE': 0x169,
|
|
'KEY_PROGRAM': 0x16a,
|
|
'KEY_CHANNEL': 0x16b,
|
|
'KEY_FAVORITES': 0x16c,
|
|
'KEY_EPG': 0x16d,
|
|
'KEY_PVR': 0x16e,
|
|
'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,
|
|
'KEY_TV': 0x179,
|
|
'KEY_TV2': 0x17a,
|
|
'KEY_VCR': 0x17b,
|
|
'KEY_VCR2': 0x17c,
|
|
'KEY_SAT': 0x17d,
|
|
'KEY_SAT2': 0x17e,
|
|
'KEY_CD': 0x17f,
|
|
'KEY_TAPE': 0x180,
|
|
'KEY_RADIO': 0x181,
|
|
'KEY_TUNER': 0x182,
|
|
'KEY_PLAYER': 0x183,
|
|
'KEY_TEXT': 0x184,
|
|
'KEY_DVD': 0x185,
|
|
'KEY_AUX': 0x186,
|
|
'KEY_MP3': 0x187,
|
|
'KEY_AUDIO': 0x188,
|
|
'KEY_VIDEO': 0x189,
|
|
'KEY_DIRECTORY': 0x18a,
|
|
'KEY_LIST': 0x18b,
|
|
'KEY_MEMO': 0x18c,
|
|
'KEY_CALENDAR': 0x18d,
|
|
'KEY_RED': 0x18e,
|
|
'KEY_GREEN': 0x18f,
|
|
'KEY_YELLOW': 0x190,
|
|
'KEY_BLUE': 0x191,
|
|
'KEY_CHANNELUP': 0x192,
|
|
'KEY_CHANNELDOWN': 0x193,
|
|
'KEY_FIRST': 0x194,
|
|
'KEY_LAST': 0x195,
|
|
'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_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,
|
|
'KEY_NUMERIC_1': 0x201,
|
|
'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,
|
|
'KEY_TOUCHPAD_TOGGLE': 0x212,
|
|
'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,
|
|
}
|
|
BTN = {
|
|
'BTN_MISC': 0x100,
|
|
'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_10': 0x10a,
|
|
'BTN_11': 0x10b,
|
|
'BTN_12': 0x10c,
|
|
'BTN_13': 0x10d,
|
|
'BTN_14': 0x10e,
|
|
'BTN_15': 0x10f,
|
|
'BTN_MOUSE': 0x110,
|
|
'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_JOYSTICK': 0x120,
|
|
'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_GAMEPAD': 0x130,
|
|
'BTN_A': 0x130,
|
|
'BTN_B': 0x131,
|
|
'BTN_C': 0x132,
|
|
'BTN_X': 0x133,
|
|
'BTN_Y': 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_DIGI': 0x140,
|
|
'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_TOUCH': 0x14a,
|
|
'BTN_STYLUS': 0x14b,
|
|
'BTN_STYLUS2': 0x14c,
|
|
'BTN_TOOL_DOUBLETAP': 0x14d,
|
|
'BTN_TOOL_TRIPLETAP': 0x14e,
|
|
'BTN_TOOL_QUADTAP': 0x14f,
|
|
'BTN_WHEEL': 0x150,
|
|
'BTN_GEAR_DOWN': 0x150,
|
|
'BTN_GEAR_UP': 0x151,
|
|
|
|
'BTN_TRIGGER_HAPPY': 0x2c0,
|
|
'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,
|
|
}
|
|
BUS = {
|
|
'BUS_PCI': 0x01,
|
|
'BUS_ISAPNP': 0x02,
|
|
'BUS_USB': 0x03,
|
|
'BUS_HIL': 0x04,
|
|
'BUS_BLUETOOTH': 0x05,
|
|
'BUS_ISA': 0x10,
|
|
'BUS_I8042': 0x11,
|
|
'BUS_XTKBD': 0x12,
|
|
'BUS_RS232': 0x13,
|
|
'BUS_GAMEPORT': 0x14,
|
|
'BUS_PARPORT': 0x15,
|
|
'BUS_AMIGA': 0x16,
|
|
'BUS_ADB': 0x17,
|
|
'BUS_I2C': 0x18,
|
|
'BUS_HOST': 0x19,
|
|
'BUS_GSC': 0x1A,
|
|
'BUS_ATARI': 0x1B,
|
|
'BUS_SPI': 0x1C,
|
|
}
|
|
ABS = {
|
|
'ABS_X': 0x00,
|
|
'ABS_Y': 0x01,
|
|
'ABS_Z': 0x02,
|
|
'ABS_RX': 0x03,
|
|
'ABS_RY': 0x04,
|
|
'ABS_RZ': 0x05,
|
|
'ABS_THROTTLE': 0x06,
|
|
'ABS_RUDDER': 0x07,
|
|
'ABS_WHEEL': 0x08,
|
|
'ABS_GAS': 0x09,
|
|
'ABS_BRAKE': 0x0a,
|
|
'ABS_HAT0X': 0x10,
|
|
'ABS_HAT0Y': 0x11,
|
|
'ABS_HAT1X': 0x12,
|
|
'ABS_HAT1Y': 0x13,
|
|
'ABS_HAT2X': 0x14,
|
|
'ABS_HAT2Y': 0x15,
|
|
'ABS_HAT3X': 0x16,
|
|
'ABS_HAT3Y': 0x17,
|
|
'ABS_PRESSURE': 0x18,
|
|
'ABS_DISTANCE': 0x19,
|
|
'ABS_TILT_X': 0x1a,
|
|
'ABS_TILT_Y': 0x1b,
|
|
'ABS_TOOL_WIDTH': 0x1c,
|
|
'ABS_VOLUME': 0x20,
|
|
'ABS_MISC': 0x28,
|
|
'ABS_MT_SLOT': 0x2f,
|
|
'ABS_MT_TOUCH_MAJOR': 0x30,
|
|
'ABS_MT_TOUCH_MINOR': 0x31,
|
|
'ABS_MT_WIDTH_MAJOR': 0x32,
|
|
'ABS_MT_WIDTH_MINOR': 0x33,
|
|
'ABS_MT_ORIENTATION': 0x34,
|
|
'ABS_MT_POSITION_X': 0x35,
|
|
'ABS_MT_POSITION_Y': 0x36,
|
|
'ABS_MT_TOOL_TYPE': 0x37,
|
|
'ABS_MT_BLOB_ID': 0x38,
|
|
'ABS_MT_TRACKING_ID': 0x39,
|
|
'ABS_MT_PRESSURE': 0x3a,
|
|
'ABS_MT_DISTANCE': 0x3b,
|
|
}
|
|
LED = {
|
|
'LED_NUML': 0x00,
|
|
'LED_CAPSL': 0x01,
|
|
'LED_SCROLLL': 0x02,
|
|
'LED_COMPOSE': 0x03,
|
|
'LED_KANA': 0x04,
|
|
'LED_SLEEP': 0x05,
|
|
'LED_SUSPEND': 0x06,
|
|
'LED_MUTE': 0x07,
|
|
'LED_MISC': 0x08,
|
|
'LED_MAIL': 0x09,
|
|
'LED_CHARGING': 0x0a,
|
|
}
|
|
|
|
def i(x):
|
|
return x - 2**32
|
|
EVIOCGVERSION = i(0x80044501)
|
|
EVIOCGID = i(0x80084502)
|
|
EVIOCGREP = i(0x80084503)
|
|
EVIOCSREP = 0x40084503
|
|
EVIOCGKEYCODE = i(0x80084504)
|
|
EVIOCSKEYCODE = 0x40084504
|
|
EVIOCGNAME = i(0x80004506)
|
|
EVIOCGPHYS = i(0x80004507)
|
|
EVIOCGUNIQ = i(0x80004508)
|
|
EVIOCGKEY = i(0x80004518)
|
|
EVIOCGLED = i(0x80004519)
|
|
EVIOCGSND = i(0x8000451a)
|
|
EVIOCGBIT = i(0x80004520)
|
|
EVIOCGABS = i(0x80144540)
|
|
EVIOCSABS = 0x401445c0
|
|
EVIOCSFF = 0x402c4580
|
|
EVIOCRMFF = 0x40044581
|
|
EVIOCGEFFECTS = i(0x80044584)
|
|
EVIOCGRAB = 0x40044590
|
|
|
|
def invert(d):
|
|
return dict((v,k) for k,v in d.items())
|
|
ABS_invert = invert(ABS)
|
|
BTN_invert = invert(BTN)
|
|
BUS_invert = invert(BUS)
|
|
EV_invert = invert(EV)
|
|
KEY_invert = invert(KEY)
|
|
LED_invert = invert(LED)
|
|
REL_invert = invert(REL)
|
|
del invert
|
|
KEYBTN_invert = KEY_invert.copy()
|
|
KEYBTN_invert.update(BTN_invert)
|
|
|
|
def humanize(s):
|
|
s = re.split("(\d+)", s)
|
|
def maybe_int(ss):
|
|
if ss and ss[0] in "0123456789": return int(ss)
|
|
return ss
|
|
return list(map(maybe_int, s))
|
|
|
|
def find(pattern):
|
|
if os.path.exists(pattern):
|
|
return os.open(pattern, os.O_RDWR)
|
|
if os.path.exists("/dev/input/event%s" % pattern):
|
|
return os.open("/dev/input/event%s" % pattern, os.O_RDWR)
|
|
if ":" in pattern:
|
|
pattern, idx = pattern.rsplit(":", 1)
|
|
idx = int(idx)
|
|
else:
|
|
idx = 0
|
|
|
|
candidates = glob.glob("/dev/input/event*")
|
|
candidates.sort(key=humanize)
|
|
successful_opens = 0
|
|
for c in candidates:
|
|
try:
|
|
f = os.open(c, os.O_RDWR)
|
|
except os.error:
|
|
continue
|
|
successful_opens += 1
|
|
|
|
name = get_name(f)
|
|
print(f"note: name is {repr(name)}")
|
|
if name.find(pattern) != -1 or fnmatch.fnmatch(name, pattern):
|
|
if idx == 0:
|
|
return f
|
|
else:
|
|
idx -= 1
|
|
continue
|
|
|
|
try:
|
|
phys = get_phys(f)
|
|
except IOError:
|
|
pass
|
|
else:
|
|
if phys.find(pattern) != -1 or fnmatch.fnmatch(phys, pattern):
|
|
if idx == 0:
|
|
return f
|
|
else:
|
|
idx -= 1
|
|
continue
|
|
|
|
id = InputId.get(f)
|
|
sid = "Bus=%s Vendor=%04x Product=%04x Version=%04x" % (\
|
|
id.bustype, id.vendor, id.product, id.version)
|
|
if sid.find(pattern) != -1 or fnmatch.fnmatch(sid, pattern):
|
|
if idx == 0:
|
|
return f
|
|
else:
|
|
idx -= 1
|
|
continue
|
|
|
|
sid = "%04x:%04x" % (id.vendor, id.product)
|
|
if sid.find(pattern) != -1 or fnmatch.fnmatch(sid, pattern):
|
|
if idx == 0:
|
|
return f
|
|
else:
|
|
idx -= 1
|
|
continue
|
|
|
|
os.close(f)
|
|
if not successful_opens:
|
|
raise LookupError("""\
|
|
No input devices could be opened. This usually indicates a misconfigured
|
|
system. Please read the section 'PERMISSIONS AND UDEV' in the hal_input
|
|
manpage""")
|
|
raise LookupError(
|
|
"No input device matching %r was found (%d devices checked)"
|
|
% (pattern, successful_opens))
|
|
|
|
def decode(map, mapname, code):
|
|
if isinstance(code, str): return code
|
|
if code in map: return map[code]
|
|
return "%s_%s" % (mapname, code)
|
|
|
|
class InputDevice:
|
|
def __init__(self, pattern):
|
|
if pattern.startswith("+"):
|
|
exclusive = 1
|
|
pattern = pattern[1:]
|
|
else:
|
|
exclusive = 0
|
|
|
|
self.f = find(pattern)
|
|
|
|
if exclusive:
|
|
fcntl.ioctl(self.f, EVIOCGRAB, 1)
|
|
|
|
def fileno(self): return self.f
|
|
def readable(self):
|
|
r, w, x = select.select([self.f], [], [], 0)
|
|
return self.f in r
|
|
|
|
def get_bits(self, arg): return get_bits(self.f, arg)
|
|
def get_absinfo(self, arg): return AbsInfo.get(self.f, arg)
|
|
def read_event(self):
|
|
e = Event.read(self.f)
|
|
if e.type == 'EV_KEY': e.code = decode(KEYBTN_invert, 'KEY', e.code)
|
|
elif e.type == 'EV_ABS': e.code = decode(ABS_invert, 'ABS', e.code)
|
|
elif e.type == 'EV_REL': e.code = decode(REL_invert, 'REL', e.code)
|
|
elif e.type == 'EV_LED': e.code = decode(LED_invert, 'LED', e.code)
|
|
return e
|
|
|
|
def write_event(self, *args):
|
|
Event.write(self.f, *args)
|