1""" 2The evemu module provides the Python interface to the kernel-level input device 3raw events. 4""" 5 6# Copyright 2011-2012 Canonical Ltd. 7# Copyright 2014 Red Hat, Inc. 8# 9# This library is free software: you can redistribute it and/or modify it 10# under the terms of the GNU Lesser General Public License version 3 11# as published by the Free Software Foundation. 12# 13# This library is distributed in the hope that it will be useful, but 14# WITHOUT ANY WARRANTY; without even the implied warranties of 15# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 16# PURPOSE. See the GNU General Public License for more details. 17# 18# You should have received a copy of the GNU Lesser General Public License 19# along with this program. If not, see <http://www.gnu.org/licenses/>. 20 21import ctypes 22import glob 23import os 24import re 25import stat 26import tempfile 27 28import evemu.base 29 30__all__ = ["Device", 31 "InputEvent", 32 "event_get_value", 33 "event_get_name", 34 "input_prop_get_value", 35 "input_prop_get_name"] 36 37_libevdev = evemu.base.LibEvdev() 38 39def event_get_value(event_type, event_code = None): 40 """ 41 Return the integer-value for the given event type and/or code string 42 e.g. "EV_ABS" returns 0x03, ("EV_ABS", "ABS_Y") returns 0x01. 43 Unknown event types or type/code combinations return None. 44 45 If an event code is passed, the event type may be given as integer or 46 string. 47 """ 48 t = -1 49 c = -1 50 51 if isinstance(event_type, int): 52 event_type = _libevdev.libevdev_event_type_get_name(event_type) 53 if event_type is None: 54 return None 55 56 event_type = event_type.decode("iso8859-1") 57 58 event_type = str(event_type).encode("iso8859-1") 59 t = _libevdev.libevdev_event_type_from_name(event_type) 60 61 if event_code is None: 62 return None if t < 0 else t 63 64 if isinstance(event_code, int): 65 event_code = _libevdev.libevdev_event_code_get_name(t, event_code) 66 if event_code is None: 67 return None 68 69 event_code = event_code.decode("iso8859-1") 70 71 event_code = str(event_code).encode("iso8859-1") 72 c = _libevdev.libevdev_event_code_from_name(t, event_code) 73 74 return None if c < 0 else c 75 76def event_get_name(event_type, event_code = None): 77 """ 78 Return the string-value for the given event type and/or code value 79 e.g. 0x03 returns "EV_ABS", ("EV_ABS", 0x01) returns "ABS_Y" 80 Unknown event types or type/code combinations return None. 81 82 If an event code is passed, the event type may be given as integer or 83 string. 84 """ 85 if not isinstance(event_type, int): 86 event_type = event_get_value(event_type) 87 88 if event_type is None: 89 return None 90 91 if event_code is None: 92 type_name = _libevdev.libevdev_event_type_get_name(event_type) 93 94 if type_name is None: 95 return None 96 97 return type_name.decode("iso8859-1") 98 99 if not isinstance(event_code, int): 100 event_code = event_get_value(event_type, event_code) 101 102 if event_code is None: 103 return None 104 105 code_name = _libevdev.libevdev_event_code_get_name(event_type, event_code) 106 if code_name is None: 107 return None 108 109 return code_name.decode("iso8859-1") 110 111def input_prop_get_name(prop): 112 """ 113 Return the name of the input property, or None if undefined. 114 """ 115 if not isinstance(prop, int): 116 prop = input_prop_get_value(prop) 117 118 if prop is None: 119 return None 120 121 prop = _libevdev.libevdev_property_get_name(prop) 122 if prop is None: 123 return None 124 125 return prop.decode("iso8859-1") 126 127def input_prop_get_value(prop): 128 """ 129 Return the value of the input property, or None if undefined. 130 """ 131 if isinstance(prop, int): 132 prop = input_prop_get_name(prop) 133 134 if prop is None: 135 return None 136 137 prop = str(prop).encode("iso8859-1") 138 prop = _libevdev.libevdev_property_from_name(prop) 139 return None if prop < 0 else prop 140 141class InputEvent(object): 142 __slots__ = 'sec', 'usec', 'type', 'code', 'value' 143 144 def __init__(self, sec, usec, type, code, value): 145 self.sec = sec 146 self.usec = usec 147 self.type = type 148 self.code = code 149 self.value = value 150 151 def matches(self, type, code = None): 152 """ 153 If code is None, return True if the event matches the given event 154 type. If code is not None, return True if the event matches the 155 given type/code pair. 156 157 type and code may be ints or string-like ("EV_ABS", "ABS_X"). 158 """ 159 if event_get_value(type) != self.type: 160 return False 161 162 if code != None and event_get_value(self.type, code) != self.code: 163 return False 164 165 return True 166 167 def __str__(self): 168 f = tempfile.TemporaryFile() 169 libc = evemu.base.LibC() 170 fp = libc.fdopen(f.fileno(), b"w+") 171 172 event = evemu.base.InputEvent() 173 event.sec = self.sec 174 event.usec = self.usec 175 event.type = self.type 176 event.code = self.code 177 event.value = self.value 178 179 libevemu = evemu.base.LibEvemu() 180 libevemu.evemu_write_event(fp, ctypes.byref(event)) 181 libc.fflush(fp) 182 f.seek(0) 183 return f.readline().rstrip() 184 185class Device(object): 186 """ 187 Encapsulates a raw kernel input event device, either an existing one as 188 reported by the kernel or a pseudodevice as created through a .prop file. 189 """ 190 191 def __init__(self, f, create=True): 192 """ 193 Initialize an evemu Device. 194 195 args: 196 f -- a file object or filename string for either an existing input 197 device node (/dev/input/eventNN) or an evemu prop file that can be used 198 to create a pseudo-device node. 199 create -- If f points to an evemu prop file, 'create' specifies if a 200 uinput device should be created 201 """ 202 203 if type(f) == str: 204 self._file = open(f) 205 elif hasattr(f, "read"): 206 self._file = f 207 else: 208 raise TypeError("expected file or file name") 209 210 self._is_propfile = self._check_is_propfile(self._file) 211 self._libc = evemu.base.LibC() 212 self._libevemu = evemu.base.LibEvemu() 213 214 self._evemu_device = self._libevemu.evemu_new(b"") 215 216 if self._is_propfile: 217 fs = self._libc.fdopen(self._file.fileno(), b"r") 218 self._libevemu.evemu_read(self._evemu_device, fs) 219 if create: 220 self._file = self._create_devnode() 221 else: 222 self._libevemu.evemu_extract(self._evemu_device, 223 self._file.fileno()) 224 225 def __del__(self): 226 if hasattr(self, "_is_propfile") and self._is_propfile: 227 self._file.close() 228 self._libevemu.evemu_destroy(self._evemu_device) 229 230 def _create_devnode(self): 231 self._libevemu.evemu_create_managed(self._evemu_device) 232 return open(self._find_newest_devnode(self.name), 'r+b', buffering=0) 233 234 def _find_newest_devnode(self, target_name): 235 newest_node = (None, float(0)) 236 for sysname in glob.glob("/sys/class/input/event*/device/name"): 237 with open(sysname) as f: 238 name = f.read().rstrip() 239 if name == target_name: 240 ev = re.search("(event\d+)", sysname) 241 if ev: 242 devname = os.path.join("/dev/input", ev.group(1)) 243 ctime = os.stat(devname).st_ctime 244 if ctime > newest_node[1]: 245 newest_node = (devname, ctime) 246 return newest_node[0] 247 248 def _check_is_propfile(self, f): 249 if stat.S_ISCHR(os.fstat(f.fileno()).st_mode): 250 return False 251 252 result = False 253 for line in f.readlines(): 254 if line.startswith("N:"): 255 result = True 256 break 257 elif line.startswith("# EVEMU"): 258 result = True 259 break 260 elif line[0] != "#": 261 raise TypeError("file must be a device special or prop file") 262 263 f.seek(0) 264 return result 265 266 def describe(self, prop_file): 267 """ 268 Gathers information about the input device and prints it 269 to prop_file. This information can be parsed later when constructing 270 a Device to create a virtual input device with the same properties. 271 272 You need the required permissions to access the device file to 273 succeed (usually root). 274 275 prop_file must be a real file with fileno(), not file-like. 276 """ 277 if not hasattr(prop_file, "fileno"): 278 raise TypeError("expected file") 279 280 fs = self._libc.fdopen(prop_file.fileno(), b"w") 281 self._libevemu.evemu_write(self._evemu_device, fs) 282 self._libc.fflush(fs) 283 284 def events(self, events_file=None): 285 """ 286 Reads the events from the given file and returns them as a list of 287 dicts. 288 289 If not None, events_file must be a real file with fileno(), not 290 file-like. If None, the file used for creating this device is used. 291 """ 292 if events_file: 293 if not hasattr(events_file, "fileno"): 294 raise TypeError("expected file") 295 else: 296 events_file = self._file 297 298 fs = self._libc.fdopen(events_file.fileno(), b"r") 299 event = evemu.base.InputEvent() 300 while self._libevemu.evemu_read_event(fs, ctypes.byref(event)) > 0: 301 yield InputEvent(event.sec, event.usec, event.type, event.code, event.value) 302 303 self._libc.rewind(fs) 304 305 def play(self, events_file): 306 """ 307 Replays an event sequence, as provided by the events_file, 308 through the input device. The event sequence must be in 309 the form created by the record method. 310 311 You need the required permissions to access the device file to 312 succeed (usually root). 313 314 events_file must be a real file with fileno(), not file-like. 315 """ 316 if not hasattr(events_file, "fileno"): 317 raise TypeError("expected file") 318 319 fs = self._libc.fdopen(events_file.fileno(), b"r") 320 self._libevemu.evemu_play(fs, self._file.fileno()) 321 322 def record(self, events_file, timeout=10000): 323 """ 324 Captures events from the input device and prints them to the 325 events_file. The events can be parsed by the play method, 326 allowing a virtual input device to emit the exact same event 327 sequence. 328 329 You need the required permissions to access the device file to 330 succeed (usually root). 331 332 events_file must be a real file with fileno(), not file-like. 333 """ 334 if not hasattr(events_file, "fileno"): 335 raise TypeError("expected file") 336 337 fs = self._libc.fdopen(events_file.fileno(), b"w") 338 self._libevemu.evemu_record(fs, self._file.fileno(), timeout) 339 self._libc.fflush(fs) 340 341 @property 342 def version(self): 343 """ 344 Gets the version of the evemu library used to create the Device. 345 """ 346 return self._libevemu.evemu_get_version(self._evemu_device) 347 348 @property 349 def devnode(self): 350 """ 351 Gets the name of the /dev node of the input device. 352 """ 353 return self._file.name 354 355 @property 356 def name(self): 357 """ 358 Gets the name of the input device (as reported by the device). 359 """ 360 result = self._libevemu.evemu_get_name(self._evemu_device) 361 return result.decode("iso8859-1") 362 363 @property 364 def id_bustype(self): 365 """ 366 Identifies the kernel device bustype. 367 """ 368 return self._libevemu.evemu_get_id_bustype(self._evemu_device) 369 370 @property 371 def id_vendor(self): 372 """ 373 Identifies the kernel device vendor. 374 """ 375 return self._libevemu.evemu_get_id_vendor(self._evemu_device) 376 377 @property 378 def id_product(self): 379 """ 380 Identifies the kernel device product. 381 """ 382 return self._libevemu.evemu_get_id_product(self._evemu_device) 383 384 @property 385 def id_version(self): 386 """ 387 Identifies the kernel device version. 388 """ 389 return self._libevemu.evemu_get_id_version(self._evemu_device) 390 391 def get_abs_current_value(self, event_code): 392 """ 393 Return the current value for the given EV_ABS value. 394 395 event_code may be an int or string-like ("ABS_X"). 396 """ 397 if not isinstance(event_code, int): 398 event_code = event_get_value("EV_ABS", event_code) 399 return self._libevemu.evemu_get_abs_current_value(self._evemu_device, 400 event_code) 401 402 def get_abs_minimum(self, event_code): 403 """ 404 Return the axis minimum for the given EV_ABS value. 405 406 event_code may be an int or string-like ("ABS_X"). 407 """ 408 if not isinstance(event_code, int): 409 event_code = evemu.event_get_value("EV_ABS", event_code) 410 return self._libevemu.evemu_get_abs_minimum(self._evemu_device, 411 event_code) 412 413 def get_abs_maximum(self, event_code): 414 """ 415 Return the axis maximum for the given EV_ABS value. 416 417 event_code may be an int or string-like ("ABS_X"). 418 """ 419 if not isinstance(event_code, int): 420 event_code = evemu.event_get_value("EV_ABS", event_code) 421 return self._libevemu.evemu_get_abs_maximum(self._evemu_device, 422 event_code) 423 424 def get_abs_fuzz(self, event_code): 425 """ 426 Return the abs fuzz for the given EV_ABS value. 427 428 event_code may be an int or string-like ("ABS_X"). 429 """ 430 if not isinstance(event_code, int): 431 event_code = evemu.event_get_value("EV_ABS", event_code) 432 return self._libevemu.evemu_get_abs_fuzz(self._evemu_device, 433 event_code) 434 435 def get_abs_flat(self, event_code): 436 """ 437 Return the abs flat for the given EV_ABS value. 438 439 event_code may be an int or string-like ("ABS_X"). 440 """ 441 if not isinstance(event_code, int): 442 event_code = evemu.event_get_value("EV_ABS", event_code) 443 return self._libevemu.evemu_get_abs_flat(self._evemu_device, 444 event_code) 445 446 def get_abs_resolution(self, event_code): 447 """ 448 Return the resolution for the given EV_ABS value. 449 450 event_code may be an int or string-like ("ABS_X"). 451 """ 452 if not isinstance(event_code, int): 453 event_code = evemu.event_get_value("EV_ABS", event_code) 454 return self._libevemu.evemu_get_abs_resolution(self._evemu_device, 455 event_code) 456 457 # don't change 'event_code' to prop, it breaks API 458 def has_prop(self, event_code): 459 """ 460 Return True if the device supports the given input property, 461 or False otherwise. 462 463 event_code may be an int or string-like ("INPUT_PROP_DIRECT"). 464 """ 465 if not isinstance(event_code, int): 466 event_code = evemu.input_prop_get_value(event_code) 467 result = self._libevemu.evemu_has_prop(self._evemu_device, event_code) 468 return bool(result) 469 470 def has_event(self, event_type, event_code): 471 """ 472 Return True if the device supports the given event type/code 473 pair, or False otherwise. 474 475 event_type and event_code may be ints or string-like ("EV_REL", 476 "REL_X"). 477 """ 478 if not isinstance(event_type, int): 479 event_type = evemu.event_get_value(event_type) 480 if not isinstance(event_code, int): 481 event_code = evemu.event_get_value(event_type, event_code) 482 result = self._libevemu.evemu_has_event(self._evemu_device, 483 event_type, 484 event_code) 485 return bool(result) 486 487