Skip to content

Commit d22ef95

Browse files
Apegvalkov
authored andcommitted
Support rumble events
1 parent 5d61fd9 commit d22ef95

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

bin/rumbletest.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
'''
5+
Example of a device listening to rumble events.
6+
7+
Test using any program that sends rumble events to the uinput device (e.g. fftest)
8+
'''
9+
10+
import time
11+
12+
from evdev import UInput, UInputError, ecodes
13+
14+
joystick = UInput()
15+
16+
had_event = True
17+
18+
while True:
19+
event = joystick.read()
20+
21+
if event == None:
22+
if had_event:
23+
had_event = False
24+
print('Waiting for events', end='', flush=True)
25+
else:
26+
print('.', end='', flush=True)
27+
else:
28+
had_event = True
29+
print()
30+
print()
31+
32+
if event == ecodes.FF_STATUS_PLAYING:
33+
print("Rumble playing!")
34+
elif event == ecodes.FF_STATUS_STOPPED:
35+
print("Rumble stopped!")
36+
else:
37+
print("Received an unknown event!")
38+
39+
time.sleep(0.2)

evdev/uinput.c

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ uinput_open(PyObject *self, PyObject *args)
3333
int ret = PyArg_ParseTuple(args, "s", &devnode);
3434
if (!ret) return NULL;
3535

36-
int fd = open(devnode, O_WRONLY | O_NONBLOCK);
36+
int fd = open(devnode, O_RDWR | O_NONBLOCK);
3737
if (fd < 0) {
3838
PyErr_SetString(PyExc_IOError, "could not open uinput device in write mode");
3939
return NULL;
@@ -76,6 +76,14 @@ uinput_create(PyObject *self, PyObject *args) {
7676
uidev.absflat[abscode] = PyLong_AsLong(PyList_GetItem(item, 4));
7777
}
7878

79+
uidev.ff_effects_max = 1;
80+
81+
if (ioctl(fd, UI_SET_EVBIT, EV_FF) < 0)
82+
goto on_err;
83+
84+
if (ioctl(fd, UI_SET_FFBIT, FF_RUMBLE) < 0)
85+
goto on_err;
86+
7987
if (write(fd, &uidev, sizeof(uidev)) != sizeof(uidev))
8088
goto on_err;
8189

@@ -142,6 +150,58 @@ uinput_write(PyObject *self, PyObject *args)
142150
}
143151

144152

153+
static PyObject *
154+
uinput_read(PyObject *self, PyObject *args)
155+
{
156+
int fd;
157+
158+
int ret = PyArg_ParseTuple(args, "i", &fd);
159+
if (!ret) return NULL;
160+
161+
size_t len;
162+
struct input_event event;
163+
164+
struct uinput_ff_upload upload;
165+
struct uinput_ff_erase erase;
166+
167+
len = read(fd, &event, sizeof(event));
168+
if (len == -1) {
169+
if (errno == EAGAIN) {
170+
// No events available
171+
Py_RETURN_NONE;
172+
} else {
173+
PyErr_SetFromErrno(PyExc_IOError);
174+
return NULL;
175+
}
176+
} else if (len != sizeof(event)) {
177+
return NULL;
178+
}
179+
180+
switch (event.type) {
181+
case EV_UINPUT:
182+
switch (event.code) {
183+
case UI_FF_UPLOAD:
184+
upload.request_id = event.value;
185+
if (ioctl(fd, UI_BEGIN_FF_UPLOAD, &upload) < 0) return NULL;
186+
if (ioctl(fd, UI_END_FF_UPLOAD, &upload) < 0) return NULL;
187+
return Py_BuildValue("i", UI_FF_UPLOAD);
188+
189+
case UI_FF_ERASE:
190+
erase.request_id = event.value;
191+
if (ioctl(fd, UI_BEGIN_FF_ERASE, &erase) < 0) return NULL;
192+
if (ioctl(fd, UI_END_FF_ERASE, &erase) < 0) return NULL;
193+
return Py_BuildValue("i", UI_FF_ERASE);
194+
195+
default: break;
196+
}
197+
198+
default: break;
199+
}
200+
201+
Py_RETURN_NONE;
202+
}
203+
204+
145205
static PyObject *
146206
uinput_enable_event(PyObject *self, PyObject *args)
147207
{
@@ -197,6 +257,9 @@ static PyMethodDef MethodTable[] = {
197257
{ "write", uinput_write, METH_VARARGS,
198258
"Write event to uinput device."},
199259

260+
{ "read", uinput_read, METH_VARARGS,
261+
"Read event from uinput device."},
262+
200263
{ "enable", uinput_enable_event, METH_VARARGS,
201264
"Enable a type of event."},
202265

evdev/uinput.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,24 @@ def write(self, etype, code, value):
159159

160160
_uinput.write(self.fd, etype, code, value)
161161

162+
def read(self):
163+
'''
164+
Read a queued event from the uinput device. Returns None if no events
165+
are available.
166+
'''
167+
event = _uinput.read(self.fd)
168+
169+
# Return values from uinput.h
170+
UI_FF_UPLOAD = 1 # start rumble
171+
UI_FF_ERASE = 2 # stop rumble
172+
173+
if event == UI_FF_UPLOAD:
174+
return ecodes.FF_STATUS_PLAYING
175+
elif event == UI_FF_ERASE:
176+
return ecodes.FF_STATUS_STOPPED
177+
178+
# No supported events available
179+
162180
def syn(self):
163181
'''
164182
Inject a ``SYN_REPORT`` event into the input subsystem. Events

0 commit comments

Comments
 (0)