device_input 0.0.3.1 → 0.1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +30 -23
- data/bin/devsniff +16 -1
- data/lib/device_input/codes.rb +4 -1
- data/lib/device_input.rb +53 -18
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 803a063ac3069383c1d2f3cff0dc5ff5c7caf43a
|
4
|
+
data.tar.gz: 97282e042e693fda103f18ccaf10d19eee397308
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 970a6aab1d8d674cb3b8db5c2509c313ed195600dca3aefd4ed80ae826cc140ab20d86b7d50e166f10b7b7a5b37136c8ab8fd94b30406768df53892498e5fe50
|
7
|
+
data.tar.gz: a83de5605ceec1982206ab8f9f072e40e65b0fd84eb00e1b28edbfc8a1b2beb8768972839c83dc7ec67783192f4cbaa557a65bc1e368b00e5008524e071a52a9
|
data/README.md
CHANGED
@@ -28,14 +28,14 @@ To determine the message size, we need to know the data structure. For a
|
|
28
28
|
long time, it was pretty simple: events are 16 bytes:
|
29
29
|
|
30
30
|
* timestamp - 8 bytes
|
31
|
-
* type -
|
32
|
-
* code -
|
33
|
-
* value -
|
31
|
+
* type - 2 bytes
|
32
|
+
* code - 2 bytes
|
33
|
+
* value - 4 bytes
|
34
34
|
|
35
35
|
However, this is only true for 32-bit platforms. On 64-bit platforms, event
|
36
|
-
timestamps became 16 bytes, increasing events from 16 to 24 bytes.
|
37
|
-
because a timestamp is defined as two `long`s, and
|
38
|
-
64-bit platforms. It's easy to remember:
|
36
|
+
timestamps became 16 bytes, increasing the size of events from 16 to 24 bytes.
|
37
|
+
This is because a timestamp is defined (ultimately) as two `long`s, and
|
38
|
+
`long`s are bigger on 64-bit platforms. It's easy to remember:
|
39
39
|
|
40
40
|
* 32-bit platform: 32-bit `long` (4 bytes)
|
41
41
|
* 64-bit platform: 64-bit `long` (8 bytes)
|
@@ -50,14 +50,16 @@ executable code to assist in examining kernel input events.
|
|
50
50
|
|
51
51
|
# Installation
|
52
52
|
|
53
|
-
|
53
|
+
Requirements: Ruby >= 2.0
|
54
|
+
|
55
|
+
Dependencies: none
|
54
56
|
|
57
|
+
Install the gem:
|
55
58
|
```
|
56
59
|
$ gem install device_input # sudo as necessary
|
57
60
|
```
|
58
61
|
|
59
62
|
Or, if using [Bundler](http://bundler.io/), add to your `Gemfile`:
|
60
|
-
|
61
63
|
```
|
62
64
|
gem 'device_input', '~> 0.0'
|
63
65
|
```
|
@@ -71,7 +73,6 @@ $ sudo devsniff /dev/input/event0
|
|
71
73
|
```
|
72
74
|
|
73
75
|
When the `f` key is pressed:
|
74
|
-
|
75
76
|
```
|
76
77
|
Misc:ScanCode:33
|
77
78
|
Key:F:1
|
@@ -81,6 +82,9 @@ Sync:Sync:0
|
|
81
82
|
And released:
|
82
83
|
```
|
83
84
|
Misc:ScanCode:33
|
85
|
+
Key:F:1
|
86
|
+
Sync:Sync:0
|
87
|
+
Misc:ScanCode:33
|
84
88
|
Key:F:0
|
85
89
|
Sync:Sync:0
|
86
90
|
```
|
@@ -90,18 +94,25 @@ Sync:Sync:0
|
|
90
94
|
```
|
91
95
|
require 'device_input'
|
92
96
|
|
97
|
+
# this loops forever and blocks waiting for input
|
93
98
|
DeviceInput.read_from('/dev/input/event0') do |event|
|
94
99
|
puts event
|
100
|
+
# break if event.time > start + 30
|
95
101
|
end
|
96
102
|
```
|
97
103
|
|
98
104
|
An event has:
|
99
105
|
|
100
|
-
* `#data
|
101
|
-
* `#time
|
102
|
-
* `#type
|
103
|
-
* `#code
|
104
|
-
* `#value
|
106
|
+
* `#data`: Struct of ints (class name Data)
|
107
|
+
* `#time`: Time, accurate to usecs
|
108
|
+
* `#type`: String label, possibly `UNK-X` where X is the integer from `#data`
|
109
|
+
* `#code`: String label, possibly `UNK-X-Y` where X and Y are from `#data`
|
110
|
+
* `#value`: Fixnum (signed) from `#data`
|
111
|
+
|
112
|
+
You will probably want to write your own read loop for your own project.
|
113
|
+
[`DeviceInput.read_from`](lib/device_input.rb#L111) is very simple and can
|
114
|
+
easily be rewritten outside of this project's namespace and adapted for your
|
115
|
+
needs.
|
105
116
|
|
106
117
|
# Research
|
107
118
|
|
@@ -116,7 +127,6 @@ about these structs towards the end of this document.
|
|
116
127
|
## Kernel structs
|
117
128
|
|
118
129
|
from https://www.kernel.org/doc/Documentation/input/input.txt
|
119
|
-
|
120
130
|
```
|
121
131
|
struct input_event {
|
122
132
|
struct timeval time;
|
@@ -128,7 +138,6 @@ struct input_event {
|
|
128
138
|
|
129
139
|
from
|
130
140
|
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/input.h#n25
|
131
|
-
|
132
141
|
```
|
133
142
|
struct input_event {
|
134
143
|
struct timeval time;
|
@@ -139,7 +148,6 @@ struct input_event {
|
|
139
148
|
```
|
140
149
|
|
141
150
|
What's a [`timeval`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/time.h#n15)?
|
142
|
-
|
143
151
|
```
|
144
152
|
struct timeval {
|
145
153
|
__kernel_time_t tv_sec; /* seconds */
|
@@ -148,7 +156,6 @@ struct timeval {
|
|
148
156
|
```
|
149
157
|
|
150
158
|
What's a [`__kernel_time_t`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/posix_types.h#n88)?
|
151
|
-
|
152
159
|
```
|
153
160
|
typedef long __kernel_long_t;
|
154
161
|
# ...
|
@@ -160,7 +167,6 @@ typedef __kernel_long_t __kernel_time_t;
|
|
160
167
|
What's a [`__u16`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/int-l64.h#n23)?
|
161
168
|
We're pretty sure it's an unsigned 16 bit integer. Likewise `__s32` should
|
162
169
|
be a signed 32-bit integer:
|
163
|
-
|
164
170
|
```
|
165
171
|
typedef unsigned short __u16;
|
166
172
|
|
@@ -190,9 +196,9 @@ platform, you get 32 bits (4 bytes). On a 64-bit platform you get 64 bits
|
|
190
196
|
|
191
197
|
## Ruby tools
|
192
198
|
|
193
|
-
We can use
|
194
|
-
|
195
|
-
|
199
|
+
We can use
|
200
|
+
[`RbConfig::SIZEOF`](http://idiosyncratic-ruby.com/42-ruby-config.html#rbconfigsizeof)
|
201
|
+
and `Array#pack`/`String#unpack` to help us read these binary structs:
|
196
202
|
```
|
197
203
|
FIELD C RbConfig Pack
|
198
204
|
--- --- --- ---
|
@@ -206,5 +212,6 @@ value __s32 int32_t l
|
|
206
212
|
# Acknowledgments
|
207
213
|
|
208
214
|
* Inspired by https://github.com/prullmann/libdevinput (don't use it)
|
209
|
-
- also the source of
|
215
|
+
- also the source of an early version of the
|
216
|
+
[event code labels](lib/device_input/codes.rb)
|
210
217
|
* Thanks to al2o3-cr from #ruby on Freenode for feedback
|
data/bin/devsniff
CHANGED
@@ -3,7 +3,22 @@
|
|
3
3
|
require 'device_input'
|
4
4
|
|
5
5
|
device = ARGV.shift || '/dev/input/event0'
|
6
|
+
mode = (ARGV.shift || 'normal').downcase
|
7
|
+
|
8
|
+
if !File.readable?(device)
|
9
|
+
puts "#{device} cannot be read. Perhaps you need to sudo?"
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
|
13
|
+
case mode
|
14
|
+
when 'normal'
|
15
|
+
mode = 'to_s'
|
16
|
+
when 'pretty', 'raw', 'bytes'
|
17
|
+
# ok
|
18
|
+
else
|
19
|
+
raise "unsupported mode: #{mode}"
|
20
|
+
end
|
6
21
|
|
7
22
|
DeviceInput.read_from(device) { |event|
|
8
|
-
puts event
|
23
|
+
puts event.send(mode)
|
9
24
|
}
|
data/lib/device_input/codes.rb
CHANGED
data/lib/device_input.rb
CHANGED
@@ -19,17 +19,18 @@ module DeviceInput
|
|
19
19
|
|
20
20
|
# these are just labels, not used internally
|
21
21
|
TYPES = {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
22
|
+
0x00 => ['EV_SYN', 'Sync'],
|
23
|
+
0x01 => ['EV_KEY', 'Key'],
|
24
|
+
0x02 => ['EV_REL', 'Relative'],
|
25
|
+
0x03 => ['EV_ABS', 'Absolute'],
|
26
|
+
0x04 => ['EV_MSC', 'Misc'],
|
27
|
+
0x05 => ['EV_SW', 'ToggleSwitch'],
|
28
|
+
0x17 => ['EV_LED', 'LED'],
|
29
|
+
0x18 => ['EV_SND', 'Sound'],
|
30
|
+
0x20 => ['EV_REP', 'Repeat'],
|
31
|
+
0x21 => ['EV_FF', 'ForceFeedback'],
|
32
|
+
0x22 => ['EV_PWR', 'Power'],
|
33
|
+
0x23 => ['EV_FF_STATUS', 'ForceFeedbackStatus'],
|
33
34
|
}
|
34
35
|
|
35
36
|
# convert Event::Data to a string
|
@@ -42,14 +43,21 @@ module DeviceInput
|
|
42
43
|
Data.new *binstr.unpack(PACK)
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
-
|
46
|
+
# return an array from [raw ... pretty]
|
47
|
+
def self.type_labels(type_code)
|
48
|
+
TYPES[type_code] || ["UNK-#{type_code}"]
|
47
49
|
end
|
48
50
|
|
49
|
-
|
51
|
+
# return an array from [raw ... pretty]
|
52
|
+
def self.code_labels(type_code, code_code)
|
50
53
|
require 'device_input/codes'
|
51
|
-
DeviceInput::CODES.dig(type_code, code_code)
|
52
|
-
|
54
|
+
labels = DeviceInput::CODES.dig(type_code, code_code)
|
55
|
+
if labels
|
56
|
+
# not all labels have been converted to arrays yet
|
57
|
+
labels.kind_of?(Enumerable) ? labels : [labels]
|
58
|
+
else
|
59
|
+
["UNK-#{type_code}-#{code_code}"]
|
60
|
+
end
|
53
61
|
end
|
54
62
|
|
55
63
|
NULL_DATA = Data.new(0, 0, 0, 0, 0)
|
@@ -61,8 +69,9 @@ module DeviceInput
|
|
61
69
|
def initialize(data)
|
62
70
|
@data = data
|
63
71
|
@time = Time.at(data.tv_sec, data.tv_usec)
|
64
|
-
|
65
|
-
@
|
72
|
+
# take the raw label, closest to the metal
|
73
|
+
@type = self.class.type_labels(data.type).first
|
74
|
+
@code = self.class.code_labels(data.type, data.code).first
|
66
75
|
end
|
67
76
|
|
68
77
|
def value
|
@@ -72,8 +81,34 @@ module DeviceInput
|
|
72
81
|
def to_s
|
73
82
|
[@type, @code, @data.value].join(':')
|
74
83
|
end
|
84
|
+
|
85
|
+
# show timestamp and use the last of the labels
|
86
|
+
def pretty
|
87
|
+
[@time.strftime("%Y-%m-%d %H:%M:%S.%L"),
|
88
|
+
[self.class.type_labels(@data.type).last,
|
89
|
+
self.class.code_labels(@data.type, @data.code).last,
|
90
|
+
@data.value].join(':'),
|
91
|
+
].join(" ")
|
92
|
+
end
|
93
|
+
|
94
|
+
# don't use any labels
|
95
|
+
def raw
|
96
|
+
[@data.type, @data.code, @data.value].join(':')
|
97
|
+
end
|
98
|
+
|
99
|
+
# display fields in hex
|
100
|
+
def bytes
|
101
|
+
require 'rbconfig/sizeof'
|
102
|
+
DEFINITION.inject('') { |memo, (field, type)|
|
103
|
+
int = @data.send(field)
|
104
|
+
width = RbConfig::SIZEOF.fetch(type)
|
105
|
+
# memo + ("%#0.#{width * 2}x" % int) + " "
|
106
|
+
memo + ("%0.#{width * 2}x" % int) + " "
|
107
|
+
}
|
108
|
+
end
|
75
109
|
end
|
76
110
|
|
111
|
+
# never gonna give you up
|
77
112
|
def self.read_from(filename)
|
78
113
|
File.open(filename, 'r') { |f|
|
79
114
|
loop {
|