Working with /dev/input/eventX from Go

9 September 2017 · 4 minute read

Today I would like to show how you can work with stream of bytes from /dev/input/eventX.

First of all I found which files in /dev/input/ bind to my keyboard and mouse. To do it I used command:

$ cat /proc/bus/input/devices

And I have got that my mouse bindind is /dev/input/event10 and my keyboard binding is /dev/input/event3.

...
H: Handlers=sysrq kbd event3 leds # keyboard
...
...
H: Handlers=mouse1 event10 # mouse
...

I found in documentation Linux Input drivers that steam of bytes in device eventX is structured this way:

You can use blocking and nonblocking reads, also select() on the
/dev/input/eventX devices, and you'll always get a whole number of input
events on a read. Their layout is:
struct input_event {
	struct timeval time;
	unsigned short type;
	unsigned short code;
	unsigned int value;
};

I used simple program in C to figure out size of input_event and timeval:

    printf("%ld\n", sizeof(struct input_event));
    printf("%ld\n", sizeof(struct timeval));

It shows that input_event has 24 bytes and timeval has 16.

+                       16                      +  2  +  2  +     4     +
|          sec          +         µsec          |     |     |           |
+-----------------------------------------------------------------------+
| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|
+-----------------------------------------------------------------------+
|                                               |     |     |           |
+                    timeval                    +type + code+    value  +

This means that each of 24 bytes has information about time, type of event, code of event and value of this event.

After that I wrote a simple program on Go for reading 24 bytes in loop from /dev/input/event10 (reading from /dev/input/* needs root permisions):

package main

import (
	"fmt"
	"os"
)

func main() {
	f, err := os.Open("/dev/input/event10")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	b := make([]byte, 24)
	for {
		f.Read(b)
 		fmt.Printf("%b\n", b)
	}
}

After compiling and running this program I have got some data:

[1001000 11001110 10110011 1011001 0 0 0 0 11100110 110000 1100 0 0 0 0 0 0 0 0 0 0 0 0 0]
[1001000 11001110 10110011 1011001 0 0 0 0 1101100 1010000 1100 0 0 0 0 0 10 0 1 0 11111111 11111111 11111111 11111111]
[1001000 11001110 10110011 1011001 0 0 0 0 1101100 1010000 1100 0 0 0 0 0 0 0 0 0 0 0 0 0]
[1001000 11001110 10110011 1011001 0 0 0 0 1111111 1101111 1100 0 0 0 0 0 10 0 1 0 11111111 11111111 11111111 11111111]
[1001000 11001110 10110011 1011001 0 0 0 0 1111111 1101111 1100 0 0 0 0 0 0 0 0 0 0 0 0 0]

First 16 bytes shows time. First 8 bytes stand for seconds and next 8 bytes stand for microseconds. We can use packages time and encoding/binary in Go to encode it.

	sec := binary.LittleEndian.Uint64(b[0:8])
	usec := binary.LittleEndian.Uint64(b[8:16])
	t := time.Unix(int64(sec), int64(usec)*1000)
	fmt.Println(t)

You can see how it works:

[1000000 11010010 10110011 1011001 0 0 0 0 110010 100001 101 0 0 0 0 0 0 0 0 0 0 0 0 0]
2017-09-09 13:36:32.000336178 +0200 CEST
[1000000 11010010 10110011 1011001 0 0 0 0 11000111 1011111 101 0 0 0 0 0 10 0 1 0 11111111 11111111 11111111 11111111]
2017-09-09 13:36:32.000352199 +0200 CEST
[1000000 11010010 10110011 1011001 0 0 0 0 11000111 1011111 101 0 0 0 0 0 0 0 0 0 0 0 0 0]
2017-09-09 13:36:32.000352199 +0200 CEST

I decoded type, code and value the same way:

	var value int32
	typ := binary.LittleEndian.Uint16(b[16:18])
	code := binary.LittleEndian.Uint16(b[18:20])
	binary.Read(bytes.NewReader(b[20:]), binary.LittleEndian, &value)
	fmt.Printf("type: %x\ncode: %d\nvalue: %d\n", typ, code, value)

After running it I can see decoded data of events:

....
2017-09-09 13:42:01.000336178 +0200 CEST
type: 2
code: 0
value: 2
....
....
....
2017-09-09 13:42:02.000312149 +0200 CEST
type: 2
code: 1
value: -1
....
....
....
2017-09-09 13:42:10.000351198 +0200 CEST
type: 1
code: 272
value: 0
....

In this file we can see that type 0x02 equals EV_REL, code 0x00 means REL_X and value means change of mouse position +2 OX.

Type 0x01 equals EV_KEY, code 0x110(272) means BTN_LEFT.

You can see that reading and parsing data from /dev/input/* is quite simple.

All necessary code is bellow here:

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"os"
	"time"
)

func main() {
	f, err := os.Open("/dev/input/event10")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	b := make([]byte, 24)
	for {
		f.Read(b)
		sec := binary.LittleEndian.Uint64(b[0:8])
		usec := binary.LittleEndian.Uint64(b[8:16])
		t := time.Unix(int64(sec), int64(usec))
		fmt.Println(t)
		var value int32
		typ := binary.LittleEndian.Uint16(b[16:18])
		code := binary.LittleEndian.Uint16(b[18:20])
		binary.Read(bytes.NewReader(b[20:]), binary.LittleEndian, &value)
		fmt.Printf("type: %x\ncode: %d\nvalue: %d\n", typ, code, value)
	}
}
comments powered by Disqus
github