pátek 27. září 2013

Using twistedinput

Recently I described basics of asynchronous file reading with Twisted framework. Using this approach is build my twistedinput library. Now I would like describe more this library and show you basic usage.

InputEvent objects

Before we can start using twistedinput, you have to understand of basic building block of this library.

If you read previous article, you should remember, that we simply printed out length of received data. Let me quickly describe these data.

When you press button on your input device, protocol from previous article actually received couple of input_event structures. This structure is defined in linux/input.h header file.

struct input_event {
    struct timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};

Time is another structure. It is composed of two fields – seconds and microseconds. Both are 64-bit long. Type and code are unsigned 16-bit values. Value field is signed 32-bit value.

Twistedinput knows this structure and has wrapper class InputEvent for it. See the basic usage.

>>> from twistedinput.event import InputEvent 
>>> event = InputEvent.buildInputEvent(1, 2, 3) 
>>> print unicode(event) 
time: 1379719268.6574370, type: 0x0001, code: 0x0002, value: 0x00000003 
>>> event.time.seconds 
1379719268 
>>> event.type 
1 
>>> event.code 
2 
>>> event.value 
3 

Receiving events

Now you are familiar with InputEvent class. Lets write small script, which receives event from your input device. I'm using Genius gamepad with device file located at /dev/input/event15.

from twistedinput.device import EventDevice
from twistedinput.protocol import EventProtocol
from twistedinput.factory import InputEventFactory
from twisted.internet import reactor
import sys

class GamepadProtocol(EventProtocol):

    def eventReceived(self, event):
        print unicode(event)

gamepad = EventDevice(
    GamepadProtocol(InputEventFactory()),
    sys.argv[1])
gamepad.startReading()
reactor.run()

Twistedinput has protocol for receiving events. Is simply receive data from input device and save them in buffer. When it has enough data for creating an event, it use factory (given as first argument) for building it and calls eventReceived method.

When I run this script and press and release buttons 1, 2, 3 and 4 respectively, I get following output

buben@debian:~$ python gamepad.py /dev/input/event15 
time: 1379976614.640730, type: 0x0004, code: 0x0004, value: 0x00090001 
time: 1379976614.640733, type: 0x0001, code: 0x0120, value: 0x00000001 
time: 1379976614.640781, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976614.712725, type: 0x0004, code: 0x0004, value: 0x00090001 
time: 1379976614.712730, type: 0x0001, code: 0x0120, value: 0x00000000 
time: 1379976614.712774, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976614.888736, type: 0x0004, code: 0x0004, value: 0x00090002 
time: 1379976614.888741, type: 0x0001, code: 0x0121, value: 0x00000001 
time: 1379976614.888786, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976614.944742, type: 0x0004, code: 0x0004, value: 0x00090002 
time: 1379976614.944747, type: 0x0001, code: 0x0121, value: 0x00000000 
time: 1379976614.944790, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976615.192746, type: 0x0004, code: 0x0004, value: 0x00090003 
time: 1379976615.192752, type: 0x0001, code: 0x0122, value: 0x00000001 
time: 1379976615.192792, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976615.272742, type: 0x0004, code: 0x0004, value: 0x00090003 
time: 1379976615.272747, type: 0x0001, code: 0x0122, value: 0x00000000 
time: 1379976615.272788, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976615.632773, type: 0x0004, code: 0x0004, value: 0x00090004 
time: 1379976615.632778, type: 0x0001, code: 0x0123, value: 0x00000001 
time: 1379976615.632815, type: 0x0000, code: 0x0000, value: 0x00000000 
time: 1379976615.712743, type: 0x0004, code: 0x0004, value: 0x00090004 
time: 1379976615.712746, type: 0x0001, code: 0x0123, value: 0x00000000 
time: 1379976615.712768, type: 0x0000, code: 0x0000, value: 0x00000000 

There are 24 events in total. Four buttons has been pressed and released. There are three events for each physical event. In other words, when you press a button, three events is generated. Not all buttons generate three events, some generate only two.

Let's despite first three events which belongs to button 1.

time: 1379976614.640730, type: 0x0004, code: 0x0004, value: 0x00090001 
time: 1379976614.640733, type: 0x0001, code: 0x0120, value: 0x00000001 
time: 1379976614.640781, type: 0x0000, code: 0x0000, value: 0x00000000 

First event has type 0x4 and code 0x4. These values corresponds to symbolic names EV_MSC and MSC_SCAN in linux/input.h header file. This sort of events are not important to us for now.

The second one has type 0x1 and code 0x120. Symbolic names are EV_KEY and BTN_TRIGGER. So type telling us that some button has been pressed. Code describing which button has been pressed. And finally value is value of this button. This is the most important event for us.

The last one is synchronization event. It is used as event separator. We can ignore it too.

Using Mapping

Now we can handle events in one eventReceived method. It's fine if you are interested in for few event types. But what if you are writing driver for keyboard. Your eventReceived should be composed from very long list of elif statemets for handling all different key. That's not good idea. In this case event mapping comes for your rescue.

Event mapping maps event into their handle routines. Everything what you have to do is implement only those routines, which you are interested in.

Instance of mapping is second optional argument for EventProtocol. If omnited, you have to override eventReceived method. If you provide mapping, simply implement methods defined by mapping and only those, which are interesting for you. Let's look at following example.

from twistedinput.device import EventDevice
from twistedinput.protocol import EventProtocol
from twistedinput.factory import InputEventFactory
from twistedinput.mapping import GamepadEventMapping
from twisted.internet import reactor
import sys

class GamepadProtocol(EventProtocol):

    def button1(self, event):
        print "button 1:", event.value

    def button2(self, event):
        print "button 2:", event.value

    def button3(self, event):
        print "button 3:", event.value

    def button4(self, event):
        print "button 4:", event.value

    def nonMappedEvent(self, event):
        print "not supported event:", unicode(event)

gamepad = EventDevice(
    GamepadProtocol(
        InputEventFactory(),
        GamepadEventMapping()),
    sys.argv[1])
gamepad.startReading()
reactor.run()

Lets run this script and see its output

buben@debian:~$ python gamepad.py /dev/input/event15 
button 1: 1 
button 1: 0 
button 2: 1 
button 2: 0 
button 3: 1 
button 3: 0 
button 4: 1 
button 4: 0 

This is much better. If you don't implement method defined by mapping, nothing happened. If mapping doesn't map some event, nonMappedEvent method is called with this event as an argument.

Žádné komentáře:

Okomentovat