Skip to content

Commit c109e6c

Browse files
committed
add absolute axis information to capabilities()
1 parent 2a3f289 commit c109e6c

4 files changed

Lines changed: 85 additions & 19 deletions

File tree

evdev/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
# Gather everything into a convenient namespace
44

5-
from evdev.device import DeviceInfo, InputDevice
5+
from evdev.device import DeviceInfo, InputDevice, AbsInfo
66
from evdev.events import InputEvent, KeyEvent, RelEvent, SynEvent, AbsEvent, event_factory
77
from evdev.uinput import UInput, UInputError
8-
from evdev.util import list_devices, categorize
8+
from evdev.util import list_devices, categorize, resolve_ecodes
99
from evdev import ecodes
10-

evdev/device.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# encoding: utf-8
22

33
import os
4+
from collections import namedtuple
45

56
from evdev import _input, ecodes, util
67
from evdev.events import InputEvent
@@ -30,12 +31,20 @@ def __eq__(self, o):
3031
and self.version == o.version
3132

3233

34+
35+
_AbsInfo = namedtuple('AbsInfo',
36+
['min', 'max', 'fuzz', 'flat'])
37+
38+
class AbsInfo(_AbsInfo):
39+
pass
40+
41+
3342
class InputDevice(object):
3443
'''
3544
A linux input device from which input events can be read.
3645
'''
3746

38-
__slots__ = 'fn', 'nophys', 'fd', 'info', 'name', 'phys', '_capabilities'
47+
__slots__ = 'fn', 'nophys', 'fd', 'info', 'name', 'phys', '_rawcapabilities'
3948

4049
def __init__(self, dev, nophys=False):
4150
'''
@@ -61,29 +70,56 @@ def __init__(self, dev, nophys=False):
6170

6271
#: The physical topology of the device
6372
self.phys = info_res[5] if not nophys else ''
64-
self._capabilities = info_res[6]
65-
66-
def capabilities(self, verbose=False):
73+
self._rawcapabilities = info_res[6]
74+
75+
def _capabilities(self, absinfo=True):
76+
res = {}
77+
for etype, ecodes in self._rawcapabilities.items():
78+
for code in ecodes:
79+
l = res.setdefault(etype, [])
80+
if isinstance(code, tuple):
81+
a = code[1] # (0, 0, 255, 0)
82+
i = AbsInfo(min=a[1], max=a[2], fuzz=a[3], flat=a[4])
83+
l.append((code[0], i))
84+
else:
85+
l.append(code)
86+
87+
return res
88+
89+
def capabilities(self, verbose=False, absinfo=True):
6790
'''
6891
Returns the event types that this device supports as a a mapping of
6992
supported event types to lists of handled event codes. Example::
7093
7194
{ 1: [272, 273, 274],
7295
2: [0, 1, 6, 8] }
7396
74-
If verbose is `True`, event codes and types will be resolved to their
75-
names. Example::
97+
If ``verbose`` is ``True``, event codes and types will be resolved
98+
to their names. Example::
7699
77100
{ ('EV_KEY', 1) : [('BTN_MOUSE', 272), ('BTN_RIGHT', 273), ('BTN_MIDDLE', 273)],
78101
('EV_REL', 2) : [('REL_X', 0), ('REL_Y', 0), ('REL_HWHEEL', 6), ('REL_WHEEL', 8)] }
79102
80103
Unknown codes or types will be resolved to '?'.
104+
105+
If ``absinfo`` is ``True``, the list of capabilities will also
106+
include absolute axis information (``absmin``, ``absmax``,
107+
``absfuzz``, ``absflat``) in the following form::
108+
109+
{ 3 : [ (0, AbsInfo(min=0, max=255, fuzz=0, flat=0)),
110+
(1, AbsInfo(min=0, max=255, fuzz=0, flat=0)) ]}
111+
112+
Combined with ``verbose`` the above becomes::
113+
114+
{ ('EV_ABS', 3) : [ (('ABS_X', 0), AbsInfo(min=0, max=255, fuzz=0, flat=0)),
115+
(('ABS_Y', 1), AbsInfo(min=0, max=255, fuzz=0, flat=0)) ]}
116+
81117
'''
82118

83119
if verbose:
84-
return dict(util.resolve_ecodes(self._capabilities))
120+
return dict(util.resolve_ecodes(self._capabilities(absinfo)))
85121
else:
86-
return self._capabilities
122+
return self._capabilities(absinfo)
87123

88124
def __eq__(self, o):
89125
''' Two devices are considered equal if their :data:`info` attributes are equal. '''

evdev/input.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ event_unpack(PyObject *self, PyObject *args)
130130
static PyObject *
131131
ioctl_capabilities(PyObject *self, PyObject *args)
132132
{
133-
int fd, ev_type, ev_code;
133+
int abs_bits[6] = {0};
134+
int fd, ev_type, ev_code, i;
134135
char ev_bits[EV_MAX/8], code_bits[KEY_MAX/8];
135136

136137
int ret = PyArg_ParseTuple(args, "i", &fd);
@@ -145,14 +146,16 @@ ioctl_capabilities(PyObject *self, PyObject *args)
145146
PyObject* capabilities = PyDict_New();
146147
PyObject* eventcodes = NULL;
147148
PyObject* capability = NULL;
149+
PyObject* absdata = NULL;
150+
PyObject* absitem = NULL;
148151

149152
memset(&ev_bits, 0, sizeof(ev_bits));
150153

151154
if (ioctl(_fd, EVIOCGBIT(0, EV_MAX), ev_bits) < 0)
152155
goto on_err;
153156

154157
// Build a dictionary of the device's capabilities
155-
for (ev_type = 0 ; ev_type < EV_MAX ; ev_type++) {
158+
for (ev_type=0 ; ev_type<EV_MAX ; ev_type++) {
156159
if (test_bit(ev_bits, ev_type)) {
157160
capability = PyLong_FromLong(ev_type);
158161
eventcodes = PyList_New(0);
@@ -161,7 +164,20 @@ ioctl_capabilities(PyObject *self, PyObject *args)
161164
ioctl(_fd, EVIOCGBIT(ev_type, KEY_MAX), code_bits);
162165
for (ev_code = 0; ev_code < KEY_MAX; ev_code++) {
163166
if (test_bit(code_bits, ev_code)) {
164-
PyList_Append(eventcodes, PyLong_FromLong(ev_code));
167+
if (ev_type == EV_ABS) {
168+
memset(&abs_bits, 0, sizeof(abs_bits));
169+
ioctl(_fd, EVIOCGABS(ev_code), abs_bits);
170+
171+
absdata = Py_BuildValue("(iiiii)", abs_bits[0], abs_bits[1],
172+
abs_bits[2], abs_bits[3], abs_bits[4],
173+
abs_bits[5]);
174+
175+
absitem = Py_BuildValue("(OO)", PyLong_FromLong(ev_code), absdata);
176+
PyList_Append(eventcodes, absitem);
177+
} else {
178+
PyList_Append(eventcodes, PyLong_FromLong(ev_code));
179+
}
180+
165181
}
166182
}
167183

evdev/util.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,38 @@ def resolve_ecodes(typecodemap, unknown='?'):
5959
6060
resolve_ecodes({ 1 : [272, 273, 274] })
6161
{ ('EV_KEY', 1) : [('BTN_MOUSE', 272), ('BTN_RIGHT', 273), ('BTN_MIDDLE', 274)] }
62+
63+
If the typecodemap contains absolute axis info (wrapped in
64+
instances of `AbsInfo <evdev.device.AbsInfo>`) the result would
65+
look like::
66+
67+
resove_ecodes({ 3 : [(0, AbsInfo(...))] })
68+
{ ('EV_ABS', 3L): [(('ABS_X', 0L), AbsInfo(...))] }
6269
'''
6370

64-
for type, codes in typecodemap.items():
65-
type_name = ecodes.EV[type]
71+
for etype, codes in typecodemap.items():
72+
type_name = ecodes.EV[etype]
6673

6774
# ecodes.keys are a combination of KEY_ and BTN_ codes
68-
if type == ecodes.EV_KEY:
75+
if etype == ecodes.EV_KEY:
6976
code_names = ecodes.keys
7077
else:
7178
code_names = getattr(ecodes, type_name.split('_')[-1])
7279

7380
res = []
7481
for i in codes:
75-
l = (code_names[i], i) if i in code_names else (unknown, i)
82+
# elements with AbsInfo(), eg { 3 : [(0, AbsInfo(...)), (1, AbsInfo(...))] }
83+
if isinstance(i, tuple):
84+
l = ((code_names[i[0]], i[0]), i[1]) if i[0] in code_names \
85+
else ((unknown, i[0]), i[1])
86+
87+
# just ecodes { 0 : [0, 1, 3], 1 : [30, 48] }
88+
else:
89+
l = (code_names[i], i) if i in code_names else (unknown, i)
90+
7691
res.append(l)
7792

78-
yield (type_name, type), res
93+
yield (type_name, etype), res
7994

8095

8196
__all__ = list_devices, is_device, categorize, resolve_ecodes

0 commit comments

Comments
 (0)