device_input 0.0.3.1 → 0.1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 {
|