|
1 | | -# -*- coding: utf-8 -*- |
2 | | -# uinput - Simple Python API to the Linux uinput-system |
3 | | -# Copyright (C) 2009 Tuomas Räsänen <tuos@codegrove.org> |
4 | | - |
5 | | -# This library is free software; you can redistribute it and/or |
6 | | -# modify it under the terms of the GNU Lesser General Public |
7 | | -# License as published by the Free Software Foundation; either |
8 | | -# version 3 of the License, or (at your option) any later version. |
9 | | - |
10 | | -# This library is distributed in the hope that it will be useful, |
11 | | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | -# Lesser General Public License for more details. |
14 | | - |
15 | | -# You should have received a copy of the GNU Lesser General Public |
16 | | -# License along with this library; if not, write to the Free Software |
17 | | -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
18 | | - |
19 | | -"""Simple Python API to the Linux uinput-system |
20 | | -
|
21 | | -A high-level API to programmatically generate Linux input events. |
22 | | -
|
23 | | -Example usage: |
24 | | ->>> import uinput |
25 | | ->>> driver = uinput.Driver() |
26 | | ->>> driver.move_pointer(100, 100) |
27 | | ->>> driver.click(uinput.keycodes.BTN_LEFT) |
28 | | -""" |
29 | | - |
30 | | -import _suinput |
31 | | -from _suinput import BUS_PCI |
32 | | -from _suinput import BUS_ISAPNP |
33 | | -from _suinput import BUS_USB |
34 | | -from _suinput import BUS_HIL |
35 | | -from _suinput import BUS_BLUETOOTH |
36 | | -from _suinput import BUS_VIRTUAL |
37 | | -from _suinput import BUS_ISA |
38 | | -from _suinput import BUS_I8042 |
39 | | -from _suinput import BUS_XTKBD |
40 | | -from _suinput import BUS_RS232 |
41 | | -from _suinput import BUS_GAMEPORT |
42 | | -from _suinput import BUS_PARPORT |
43 | | -from _suinput import BUS_AMIGA |
44 | | -from _suinput import BUS_ADB |
45 | | -from _suinput import BUS_I2C |
46 | | -from _suinput import BUS_HOST |
47 | | -from _suinput import BUS_GSC |
48 | | -from _suinput import BUS_ATARI |
49 | | - |
50 | | -import keycodes |
51 | | - |
52 | | -__all__ = [ |
53 | | - "Driver", |
54 | | - ] |
55 | | - |
56 | | -class Driver(object): |
57 | | - """Device driver for the Linux uinput-system. |
58 | | -
|
59 | | - keycodes are defined in uinput.keycodes -module. |
60 | | -
|
61 | | - For the documentation of the constructor arguments, see the |
62 | | - documentation of the corresponding properties. |
63 | | - """ |
64 | | - |
65 | | - def __init__(self, name="python-uinput", bustype=_suinput.BUS_VIRTUAL, |
66 | | - vendor=0, product=0, version=0): |
67 | | - self._context = _suinput.open(name, bustype, vendor, product, version) |
| 1 | +from __future__ import absolute_import |
| 2 | + |
| 3 | +from uinput import suinput |
| 4 | + |
| 5 | +from .evtypes import EV_KEY |
| 6 | +from .evtypes import EV_ABS |
| 7 | +from .evtypes import EV_REL |
| 8 | + |
| 9 | +from .bustypes import * |
| 10 | +from .keycodes import * |
| 11 | +from .abscodes import * |
| 12 | +from .relcodes import * |
| 13 | + |
| 14 | +class DeviceError(Exception): |
| 15 | + pass |
| 16 | + |
| 17 | +class Device(object): |
| 18 | + def __init__(self, name="python-uinput", |
| 19 | + bustype=BUS_VIRTUAL, vendor=0, product=0, version=3, |
| 20 | + ff_effects_max=0): |
| 21 | + self._uinput_fd = None |
| 22 | + self._capability_catalogue = {} |
68 | 23 | self._name = name |
69 | 24 | self._bustype = bustype |
70 | 25 | self._vendor = vendor |
71 | 26 | self._product = product |
72 | 27 | self._version = version |
| 28 | + self._ff_effects_max = ff_effects_max |
| 29 | + self._absmin = [0] * (ABS_CNT) |
| 30 | + self._absmax = [0] * (ABS_CNT) |
| 31 | + self._absfuzz = [0] * (ABS_CNT) |
| 32 | + self._absflat = [0] * (ABS_CNT) |
| 33 | + |
| 34 | + def set_abs_parameters(self, abs_code, abs_min=0, abs_max=0, abs_fuzz=0, |
| 35 | + abs_flat=0): |
| 36 | + self._absmin[abs_code] = abs_min |
| 37 | + self._absmax[abs_code] = abs_max |
| 38 | + self._absfuzz[abs_code] = abs_fuzz |
| 39 | + self._absflat[abs_code] = abs_flat |
| 40 | + |
| 41 | + def get_abs_parameters(self, abs_code): |
| 42 | + return (self._absmin[abs_code], self._absmax[abs_code], |
| 43 | + self._absfuzz[abs_code], self._absflat[abs_code]) |
| 44 | + |
| 45 | + def send(self, ev_type, ev_code, ev_value): |
| 46 | + if self._uinput_fd is None: |
| 47 | + self.activate() |
| 48 | + try: |
| 49 | + capability_set = self._capability_catalogue[ev_type] |
| 50 | + if ev_code not in capability_set: |
| 51 | + raise KeyError() |
| 52 | + except KeyError: |
| 53 | + raise DeviceError("Device is not capable of handling event.", |
| 54 | + ev_type, ev_code) |
| 55 | + suinput.uinput_write(self._uinput_fd, ev_type, ev_code, ev_value) |
| 56 | + |
| 57 | + def activate(self): |
| 58 | + uinput_fd = suinput.uinput_open() |
| 59 | + try: |
| 60 | + for ev_type, capabilities in self._capability_catalogue.items(): |
| 61 | + suinput.uinput_set_capabilities(uinput_fd, ev_type, |
| 62 | + capabilities) |
| 63 | + suinput.uinput_create(uinput_fd, self._name, |
| 64 | + self._bustype, self._vendor, |
| 65 | + self._product, self._version, |
| 66 | + self._ff_effects_max, self._absmin, |
| 67 | + self._absmax, self._absfuzz, |
| 68 | + self._absflat) |
| 69 | + except Exception: |
| 70 | + suinput.uinput_destroy(uinput_fd) |
| 71 | + return |
| 72 | + self._uinput_fd = uinput_fd |
| 73 | + |
| 74 | + def is_active(self): |
| 75 | + return self._uinput_fd is not None |
| 76 | + |
| 77 | + def syn(self): |
| 78 | + suinput.uinput_syn(self._uinput_fd) |
| 79 | + |
| 80 | + def add_capability(self, ev_type, ev_code): |
| 81 | + if self._uinput_fd is not None: |
| 82 | + raise DeviceError("Device is already active.") |
| 83 | + capabilities = self._capability_catalogue.setdefault(ev_type, set()) |
| 84 | + capabilities.add(ev_code) |
| 85 | + |
| 86 | + def remove_capability(self, ev_type, ev_code): |
| 87 | + if self._uinput_fd is not None: |
| 88 | + raise DeviceError("Device is already active.") |
| 89 | + capability_set = self._capability_catalogue[ev_type] |
| 90 | + capability_set.remove(ev_code) |
| 91 | + if len(capability_set) == 0: |
| 92 | + self._capability_catalogue.pop(ev_type) |
73 | 93 |
|
74 | 94 | @property |
75 | | - def name(self): |
76 | | - "Name of the device." |
77 | | - return self._name |
| 95 | + def capabilities(self): |
| 96 | + return dict(self._capability_catalogue) |
78 | 97 |
|
79 | | - @property |
80 | | - def bustype(self): |
81 | | - """One of the BUS_ -prefixed constant values. |
82 | | - """ |
83 | | - return self._bustype |
| 98 | + def __del__(self): |
| 99 | + if self._uinput_fd is not None: |
| 100 | + suinput.uinput_destroy(self._uinput_fd) |
84 | 101 |
|
85 | | - @property |
86 | | - def vendor(self): |
87 | | - """Arbitrary 16 bit unsigned integer vendor id.""" |
88 | | - return self._vendor |
| 102 | +class Capabilities(object): |
| 103 | + """Abstract class representing a set of input capabilities. |
| 104 | + Descendants must define _EV_TYPE.""" |
89 | 105 |
|
90 | | - @property |
91 | | - def product(self): |
92 | | - """Arbitrary 16 bit unsigned integer product id.""" |
93 | | - return self._product |
| 106 | + def __init__(self, device): |
| 107 | + self._device = device |
| 108 | + |
| 109 | + def __iter__(self): |
| 110 | + try: |
| 111 | + return iter(self.device.capabilities[self._EV_TYPE]) |
| 112 | + except KeyError: |
| 113 | + return iter(()) |
| 114 | + |
| 115 | + def _send_to_device(self, ev_code, ev_value, syn): |
| 116 | + self.device.send(self._EV_TYPE, ev_code, ev_value) |
| 117 | + if syn: |
| 118 | + self.device.syn() |
94 | 119 |
|
95 | 120 | @property |
96 | | - def version(self): |
97 | | - """Arbitrary 16 bit unsigned integer version number.""" |
98 | | - return self._version |
| 121 | + def device(self): |
| 122 | + return self._device |
99 | 123 |
|
100 | | - def move_pointer(self, x, y): |
101 | | - "Move pointer towards bottom-right." |
102 | | - _suinput.move_pointer(self._context, x, y) |
| 124 | + def add(self, ev_code): |
| 125 | + self.device.add_capability(self._EV_TYPE, ev_code) |
103 | 126 |
|
104 | | - def press(self, keycode): |
105 | | - """Send a press event. |
106 | | - Event is repeated after a short delay until a release event is sent.""" |
107 | | - _suinput.press(self._context, keycode) |
| 127 | + def remove(self, ev_code): |
| 128 | + self.device.remove_capability(self._EV_TYPE, ev_code) |
108 | 129 |
|
109 | | - def release(self, keycode): |
110 | | - "Send a release event." |
111 | | - _suinput.release(self._context, keycode) |
| 130 | +class KeyCapabilities(Capabilities): |
112 | 131 |
|
113 | | - def click(self, keycode): |
114 | | - "Send a press and a release event." |
115 | | - _suinput.click(self._context, keycode) |
| 132 | + _EV_TYPE = EV_KEY |
116 | 133 |
|
117 | | - def press_release(self, signed_keycode): |
118 | | - """Send a press event if signed_keycode > 0, otherwise send |
119 | | - a release event.""" |
120 | | - _suinput.press_release(self._context, signed_keycode) |
| 134 | + def press(self, key_code, syn=True): |
| 135 | + self._send_to_device(key_code, 1, syn) |
121 | 136 |
|
122 | | - def toggle(self, keycode): |
123 | | - "Press button if it is not pressed currently, release it otherwise." |
124 | | - _suinput.toggle(self._context, keycode) |
| 137 | + def release(self, key_code, syn=True): |
| 138 | + self._send_to_device(key_code, 0, syn) |
125 | 139 |
|
126 | | - def is_pressed(self, keycode): |
127 | | - "Return True if button is pressed, otherwise return False." |
128 | | - return _suinput.is_pressed(self._context, keycode) |
| 140 | + def click(self, key_code): |
| 141 | + self.press(key_code) |
| 142 | + self.release(key_code) |
129 | 143 |
|
130 | | - def __del__(self): |
131 | | - _suinput.close(self._context) |
| 144 | +class RelativeAxisCapabilities(Capabilities): |
| 145 | + |
| 146 | + _EV_TYPE = EV_REL |
| 147 | + |
| 148 | + def move_by(self, rel_code, rel_value, syn=True): |
| 149 | + self._send_to_device(rel_code, rel_value, syn) |
| 150 | + |
| 151 | +class AbsoluteAxisCapabilities(Capabilities): |
| 152 | + |
| 153 | + _EV_TYPE = EV_ABS |
| 154 | + |
| 155 | + def add(self, abs_code, abs_min=0, abs_max=0, abs_fuzz=0, abs_flat=0): |
| 156 | + Capabilities.add(self, abs_code) |
| 157 | + self.device.set_abs_parameters(abs_code, abs_min, abs_max, abs_fuzz, |
| 158 | + abs_flat) |
| 159 | + |
| 160 | + def move_to(self, abs_code, abs_value, syn=True): |
| 161 | + self._send_to_device(abs_code, abs_value, syn) |
0 commit comments