Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions bin/rumbletest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''
Example of a device listening to rumble events.

Test using any program that sends rumble events to the uinput device (e.g. fftest)
'''

import time

from evdev import UInput, UInputError, ecodes

joystick = UInput()

had_event = True

while True:
event = joystick.read()

if event == None:
if had_event:
had_event = False
print('Waiting for events', end='', flush=True)
else:
print('.', end='', flush=True)
else:
had_event = True
print()
print()

if event == ecodes.FF_STATUS_PLAYING:
print("Rumble playing!")
elif event == ecodes.FF_STATUS_STOPPED:
print("Rumble stopped!")
else:
print("Received an unknown event!")

time.sleep(0.2)
65 changes: 64 additions & 1 deletion evdev/uinput.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ uinput_open(PyObject *self, PyObject *args)
int ret = PyArg_ParseTuple(args, "s", &devnode);
if (!ret) return NULL;

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

uidev.ff_effects_max = 1;

if (ioctl(fd, UI_SET_EVBIT, EV_FF) < 0)
goto on_err;

if (ioctl(fd, UI_SET_FFBIT, FF_RUMBLE) < 0)
goto on_err;

if (write(fd, &uidev, sizeof(uidev)) != sizeof(uidev))
goto on_err;

Expand Down Expand Up @@ -142,6 +150,58 @@ uinput_write(PyObject *self, PyObject *args)
}


static PyObject *
uinput_read(PyObject *self, PyObject *args)
{
int fd;

int ret = PyArg_ParseTuple(args, "i", &fd);
if (!ret) return NULL;

size_t len;
struct input_event event;

struct uinput_ff_upload upload;
struct uinput_ff_erase erase;

len = read(fd, &event, sizeof(event));
if (len == -1) {
if (errno == EAGAIN) {
// No events available
Py_RETURN_NONE;
} else {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
} else if (len != sizeof(event)) {
return NULL;
}

switch (event.type) {
case EV_UINPUT:
switch (event.code) {
case UI_FF_UPLOAD:
upload.request_id = event.value;
if (ioctl(fd, UI_BEGIN_FF_UPLOAD, &upload) < 0) return NULL;
if (ioctl(fd, UI_END_FF_UPLOAD, &upload) < 0) return NULL;
return Py_BuildValue("i", UI_FF_UPLOAD);

case UI_FF_ERASE:
erase.request_id = event.value;
if (ioctl(fd, UI_BEGIN_FF_ERASE, &erase) < 0) return NULL;
if (ioctl(fd, UI_END_FF_ERASE, &erase) < 0) return NULL;
return Py_BuildValue("i", UI_FF_ERASE);

default: break;
}

default: break;
}

Py_RETURN_NONE;
}


static PyObject *
uinput_enable_event(PyObject *self, PyObject *args)
{
Expand Down Expand Up @@ -197,6 +257,9 @@ static PyMethodDef MethodTable[] = {
{ "write", uinput_write, METH_VARARGS,
"Write event to uinput device."},

{ "read", uinput_read, METH_VARARGS,
"Read event from uinput device."},

{ "enable", uinput_enable_event, METH_VARARGS,
"Enable a type of event."},

Expand Down
18 changes: 18 additions & 0 deletions evdev/uinput.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,24 @@ def write(self, etype, code, value):

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

def read(self):
'''
Read a queued event from the uinput device. Returns None if no events
are available.
'''
event = _uinput.read(self.fd)

# Return values from uinput.h
UI_FF_UPLOAD = 1 # start rumble
UI_FF_ERASE = 2 # stop rumble

if event == UI_FF_UPLOAD:
return ecodes.FF_STATUS_PLAYING
elif event == UI_FF_ERASE:
return ecodes.FF_STATUS_STOPPED

# No supported events available

def syn(self):
'''
Inject a ``SYN_REPORT`` event into the input subsystem. Events
Expand Down