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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
local ffi = require "ffi"
local C = ffi.C
cdef_include "evdev.h"
cdef_safe[[
/*
* These definitions are copied from input-event-codes.h
*/
enum applause_evdev_type {
EV_KEY = 0x01,
EV_REL = 0x02,
EV_ABS = 0x03
};
enum applause_evdev_rel {
REL_X = 0x00,
REL_Y = 0x01,
REL_Z = 0x02,
REL_RX = 0x03,
REL_RY = 0x04,
REL_RZ = 0x05,
REL_HWHEEL = 0x06,
REL_DIAL = 0x07,
REL_WHEEL = 0x08,
REL_MISC = 0x09,
REL_WHEEL_HI_RES = 0x0b,
REL_HWHEEL_HI_RES = 0x0c
};
enum applause_evdev_abs {
ABS_X = 0x00,
ABS_Y = 0x01,
ABS_Z = 0x02
};
]]
EvdevStream = DeriveClass(Stream)
function EvdevStream:ctor(id, grab)
local grab = grab == nil or grab
local node
if type(id) == "number" then
node = "/dev/input/event"..id
else
assert(type(id) == "string")
-- id is assumed to be a Lua pattern to match against the device name
local name
local i = 0
repeat
node = "/dev/input/event"..i
local buffer = ffi.gc(C.applause_evdev_getname(node), C.free)
if buffer == nil then error("Evdev device not found!") end
name = ffi.string(buffer)
i = i + 1
until name:match(id)
end
-- Creating only one object has the advantage that the device can be
-- grabbed.
-- NOTE: To reliably ungrab the device, the entire object needs to be niled
-- and you have to drive the garbage collector manually.
self.evdev = ffi.gc(C.applause_evdev_new(node, grab), C.applause_evdev_free)
if self.evdev == nil then error("Evdev device not found!") end
end
function EvdevStream:gtick()
local evdev = self.evdev
local sample = ffi.new("applause_evdev_sample[1]")
return function()
-- EvdevStreams only have a single event queue no matter how often they are
-- gticked. That's why it must always be cached.
local cached_sample = sampleCache[self]
if not cached_sample then
C.applause_evdev_pull(evdev, sample)
sampleCache[self] = sample[0]
cached_sample = sample[0]
end
return cached_sample
end
end
-- Relative devices like some mouses and the trackpoint.
-- The coordinate is returned in `resolution` steps between [-1, 1]
-- NOTE: `code` is optional (default: REL_X) and you can specify a number (0 or 1)
-- or string ('REL_X', 'REL_Y') as well.
function Stream:evrel(code, resolution)
code = ffi.cast("enum applause_evdev_rel", code)
resolution = resolution or 1000
local min, max = math.min, math.max
return self:scan(function(last, sample)
last = last or 0
return sample.type == C.EV_REL and sample.code == code and
min(max(last+sample.value, 0), resolution) or last
end) / (resolution/2) - 1
end
-- FIXME: min and max can be read from the properties of the corresponding code.
-- This would however require us to do all postprocessing already in EvdevStream:gtick()
-- or even in applause_evdev_new().
function Stream:evabs(code, min, max)
code = ffi.cast("enum applause_evdev_abs", code)
return self:scan(function(last, sample)
last = last or 0
return sample.type == C.EV_ABS and sample.code == code and
(sample.value - min)/((max-min)/2) - 1 or last
end)
end
-- FIXME: We haven't got constants for all KEY_X constants,#
-- so you will have to look up the key code in input-event-codes.h.
function Stream:evkey(key)
return self:scan(function(last, sample)
last = last or 0
return sample.type == C.EV_KEY and sample.code == key and
sample.value or last
end)
end
|