Skip to content

Commit 2f0a0f9

Browse files
Major refactoring, much more granular and flexible interface.
1 parent c56acf2 commit 2f0a0f9

12 files changed

Lines changed: 1163 additions & 834 deletions

examples/basic.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#! /usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
import time
5+
6+
import uinput
7+
8+
def main():
9+
device = uinput.Device()
10+
11+
keys = uinput.KeyCapabilities(device)
12+
keys.add(uinput.KEY_H)
13+
keys.add(uinput.KEY_E)
14+
keys.add(uinput.KEY_L)
15+
keys.add(uinput.KEY_O)
16+
keys.add(uinput.BTN_LEFT)
17+
keys.add(uinput.BTN_RIGHT)
18+
keys.add(uinput.BTN_MIDDLE)
19+
20+
rel_axes = uinput.RelativeAxisCapabilities(device)
21+
rel_axes.add(uinput.REL_X)
22+
rel_axes.add(uinput.REL_Y)
23+
rel_axes.add(uinput.REL_WHEEL)
24+
25+
device.activate()
26+
time.sleep(1)
27+
28+
keys.click(uinput.KEY_H)
29+
keys.click(uinput.KEY_E)
30+
keys.click(uinput.KEY_L)
31+
keys.click(uinput.KEY_L)
32+
keys.click(uinput.KEY_O)
33+
34+
for i in range(20):
35+
rel_axes.move_by(uinput.REL_X, 5, False)
36+
rel_axes.move_by(uinput.REL_Y, 5)
37+
time.sleep(0.01)
38+
39+
if __name__ == "__main__":
40+
main()

setup.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,42 @@
55
reload(sys).setdefaultencoding('utf-8')
66
from distutils.core import setup, Extension
77

8-
pysuinput_module = Extension('uinput._suinput',
9-
sources=['src/pysuinput.c'],
10-
libraries=['suinput'],
11-
)
8+
suinput_module = Extension('uinput.suinput',
9+
sources=['src/suinputmodule.c'],
10+
libraries=['suinput', 'udev'],
11+
)
1212

1313
keycodes_module = Extension('uinput.keycodes',
14-
sources=['src/keycodes.c'],
15-
libraries=['suinput'],
14+
sources=['src/keycodesmodule.c'],
1615
)
1716

17+
bustypes_module = Extension('uinput.bustypes',
18+
sources=['src/bustypesmodule.c'],
19+
)
20+
21+
relcodes_module = Extension('uinput.relcodes',
22+
sources=['src/relcodesmodule.c'],
23+
)
24+
25+
abscodes_module = Extension('uinput.abscodes',
26+
sources=['src/abscodesmodule.c'],
27+
)
28+
29+
evtypes_module = Extension('uinput.evtypes',
30+
sources=['src/evtypesmodule.c'],
31+
)
32+
1833
setup(name='python-uinput',
19-
version='0.2',
34+
version='0.3',
2035
description='Simple Python API to the Linux uinput-system.',
2136
author='Tuomas Räsänen',
2237
author_email='tuos@codegrove.org',
2338
url='http://codegrove.org/python-uinput/',
24-
download_url='http://codegrove.org/python-uinput/0.2/python-uinput-0.2.tar.gz',
39+
download_url='http://codegrove.org/python-uinput/0.3/python-uinput-0.3.tar.gz',
2540
package_dir={'uinput': 'src'},
2641
packages=['uinput'],
27-
ext_modules=[pysuinput_module, keycodes_module],
42+
ext_modules=[suinput_module, keycodes_module, bustypes_module,
43+
relcodes_module, abscodes_module, evtypes_module],
2844
license='LGPLv3+',
2945
platforms=['Linux'],
3046
classifiers=[

src/__init__.py

Lines changed: 141 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,161 @@
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 = {}
6823
self._name = name
6924
self._bustype = bustype
7025
self._vendor = vendor
7126
self._product = product
7227
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)
7393

7494
@property
75-
def name(self):
76-
"Name of the device."
77-
return self._name
95+
def capabilities(self):
96+
return dict(self._capability_catalogue)
7897

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)
84101

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."""
89105

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()
94119

95120
@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
99123

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)
103126

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)
108129

109-
def release(self, keycode):
110-
"Send a release event."
111-
_suinput.release(self._context, keycode)
130+
class KeyCapabilities(Capabilities):
112131

113-
def click(self, keycode):
114-
"Send a press and a release event."
115-
_suinput.click(self._context, keycode)
132+
_EV_TYPE = EV_KEY
116133

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)
121136

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)
125139

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)
129143

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

Comments
 (0)