-
Notifications
You must be signed in to change notification settings - Fork 1
/
device_input.rb
101 lines (86 loc) · 2.81 KB
/
device_input.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
require 'device_input/labels'
require 'rbconfig/sizeof'
module DeviceInput
class Event
DEFINITION = {
:tv_sec => 'long', # 64 bits on 64-bit platforms
:tv_usec => 'long', # 32 bits on 32-bit platforms
:type => 'uint16_t',
:code => 'uint16_t',
:value => 'int32_t',
}
PACK_MAP = {
'long' => 'l!', # platform-dependent
'uint16_t' => 'S',
'int32_t' => 'l',
}
PACK = DEFINITION.values.map { |v| PACK_MAP.fetch(v) }.join
# This defines a class, i.e. class Data ... with keys/ivars matching
# DEFINITION. Data instances will hold a value for each of the keys.
# Sorry for the name. It refers literally to the struct-of-integers
# representation we're creating here, every time we use the term `data`
Data = Struct.new(*DEFINITION.keys)
# convert Event::Data to a string
def self.encode(data)
data.values.pack(PACK)
end
# convert string to Event::Data
def self.decode(binstr)
Data.new(*binstr.unpack(PACK))
end
# return an array of equivalent labels, prettier toward the end
def self.type_labels(type_val)
TYPES[type_val] || ["UNK-#{type_val}"]
end
# return an array of equivalent labels, prettier toward the end
def self.code_labels(type_val, code_val)
CODES.dig(type_val, code_val) || ["UNK-#{type_val}-#{code_val}"]
end
NULL_DATA = Data.new(0, 0, 0, 0, 0)
NULL_MSG = self.encode(NULL_DATA)
BYTE_LENGTH = NULL_MSG.length
attr_reader :data, :time, :type, :code
def initialize(data)
@data = data # sorry for the name. it's a Data. data everywhere
@time = Time.at(data.tv_sec, data.tv_usec)
# take the raw label, closest to the metal
@type = Event.type_labels(data.type).first
@code = Event.code_labels(data.type, data.code).first
end
def value
@data.value
end
def to_s
[@type, @code, @data.value].join(':')
end
# show timestamp and use the last of the labels
def pretty
[@time.strftime("%Y-%m-%d %H:%M:%S.%L"),
[Event.type_labels(@data.type).last,
Event.code_labels(@data.type, @data.code).last,
@data.value].join(':'),
].join(" ")
end
# don't use any labels
def raw
[@data.type, @data.code, @data.value].join(':')
end
# display fields in hex
def hex
DEFINITION.inject('') { |memo, (field, type)|
int = @data.send(field)
width = RbConfig::SIZEOF.fetch(type)
# memo + ("%#0.#{width * 2}x" % int) + " "
memo + ("%0.#{width * 2}x" % int) + " "
}
end
end
def self.read_loop(io)
loop {
bytes = io.read(Event::BYTE_LENGTH)
break unless (bytes and bytes.length == Event::BYTE_LENGTH)
data = Event.decode(bytes)
yield Event.new(data)
}
end
end