2
0
mirror of https://github.com/koreader/koreader synced 2024-11-13 19:11:25 +00:00
koreader/frontend/ui/input.lua
Markismus 730b1880de Update input.lua
When the the power_off switch is pulled a empty SYN_REPORT is generated and koreader crashed on a DEBUG line.

The DEBUG lines now generate identically spaced output, which makes it easier to read by eye.
2013-12-24 19:29:00 +01:00

775 lines
22 KiB
Lua

local Device = require("ui/device")
local GestureDetector = require("ui/gesturedetector")
local Event = require("ui/event")
local TimeVal = require("ui/timeval")
local Screen = require("ui/screen")
local Math = require("optmath")
local Dbg = require("dbg")
local DEBUG = require("dbg")
local _ = require("gettext")
-- constants from <linux/input.h>
local EV_SYN = 0
local EV_KEY = 1
local EV_ABS = 3
-- key press event values (KEY.value)
local EVENT_VALUE_KEY_PRESS = 1
local EVENT_VALUE_KEY_REPEAT = 2
local EVENT_VALUE_KEY_RELEASE = 0
-- Synchronization events (SYN.code).
local SYN_REPORT = 0
local SYN_CONFIG = 1
local SYN_MT_REPORT = 2
-- For single-touch events (ABS.code).
local ABS_X = 00
local ABS_Y = 01
local ABS_PRESSURE = 24
-- For multi-touch events (ABS.code).
local ABS_MT_SLOT = 47
local ABS_MT_TOUCH_MAJOR = 48
local ABS_MT_WIDTH_MAJOR = 50
local ABS_MT_POSITION_X = 53
local ABS_MT_POSITION_Y = 54
local ABS_MT_TRACKING_ID = 57
local ABS_MT_PRESSURE = 58
--[[
an interface for key presses
]]
local Key = {}
function Key:new(key, modifiers)
local o = { key = key, modifiers = modifiers }
-- we're a hash map, too
o[key] = true
for mod, pressed in pairs(modifiers) do
if pressed then
o[mod] = true
end
end
setmetatable(o, self)
self.__index = self
return o
end
function Key:__tostring()
return table.concat(self:getSequence(), "-")
end
--[[
get a sequence that can be matched against later
use this to let the user press a sequence and then
store this as configuration data (configurable
shortcuts)
]]
function Key:getSequence()
local seq = {}
for mod, pressed in pairs(self.modifiers) do
if pressed then
table.insert(seq, mod)
end
end
table.insert(seq, self.key)
end
--[[
this will match a key against a sequence
the sequence should be a table of key names that
must be pressed together to match.
if an entry in this table is itself a table, at
least one key in this table must match.
E.g.:
Key:match({ "Alt", "K" }) -- match Alt-K
Key:match({ "Alt", { "K", "L" }}) -- match Alt-K _or_ Alt-L
]]
function Key:match(sequence)
local mod_keys = {} -- a hash table for checked modifiers
for _, key in ipairs(sequence) do
if type(key) == "table" then
local found = false
for _, variant in ipairs(key) do
if self[variant] then
found = true
break
end
end
if not found then
-- one of the needed keys is not pressed
return false
end
elseif not self[key] then
-- needed key not pressed
return false
elseif self.modifiers[key] ~= nil then
-- checked key is a modifier key
mod_keys[key] = true
end
end
for mod, pressed in pairs(self.modifiers) do
if pressed and not mod_keys[mod] then
-- additional modifier keys are pressed, don't match
return false
end
end
return true
end
--[[
an interface to get input events
]]
local Input = {
event_map = {},
modifiers = {},
rotation_map = {
[0] = {},
[1] = { Up = "Right", Right = "Down", Down = "Left", Left = "Up" },
[2] = { Up = "Down", Right = "Left", Down = "Up", Left = "Right" },
[3] = { Up = "Left", Right = "Up", Down = "Right", Left = "Down" }
},
timer_callbacks = {},
disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP,
}
function Input:initKeyMap()
self.event_map = {
[2] = "1", [3] = "2", [4] = "3", [5] = "4", [6] = "5", [7] = "6", [8] = "7", [9] = "8", [10] = "9", [11] = "0",
[16] = "Q", [17] = "W", [18] = "E", [19] = "R", [20] = "T", [21] = "Y", [22] = "U", [23] = "I", [24] = "O", [25] = "P",
[30] = "A", [31] = "S", [32] = "D", [33] = "F", [34] = "G", [35] = "H", [36] = "J", [37] = "K", [38] = "L", [14] = "Del",
[44] = "Z", [45] = "X", [46] = "C", [47] = "V", [48] = "B", [49] = "N", [50] = "M", [52] = ".", [53] = "/", -- only KDX
[28] = "Enter",
[29] = "ScreenKB", -- K[4]
[42] = "Shift",
[56] = "Alt",
[57] = " ",
[90] = "AA", -- KDX
[91] = "Back", -- KDX
[92] = "Press", -- KDX
[94] = "Sym", -- KDX
[98] = "Home", -- KDX
[102] = "Home", -- K[3] & k[4]
[104] = "LPgBack", -- K[3] only
[103] = "Up", -- K[3] & k[4]
[105] = "Left",
[106] = "Right",
[108] = "Down", -- K[3] & k[4]
[109] = "RPgBack",
[114] = "VMinus",
[115] = "VPlus",
[122] = "Up", -- KDX
[123] = "Down", -- KDX
[124] = "RPgFwd", -- KDX
[126] = "Sym", -- K[3]
[139] = "Menu",
[158] = "Back", -- K[3] & K[4]
[190] = "AA", -- K[3]
[191] = "RPgFwd", -- K[3] & k[4]
[193] = "LPgFwd", -- K[3] only
[194] = "Press", -- K[3] & k[4]
}
self.sdl_event_map = {
[10] = "1", [11] = "2", [12] = "3", [13] = "4", [14] = "5", [15] = "6", [16] = "7", [17] = "8", [18] = "9", [19] = "0",
[24] = "Q", [25] = "W", [26] = "E", [27] = "R", [28] = "T", [29] = "Y", [30] = "U", [31] = "I", [32] = "O", [33] = "P",
[38] = "A", [39] = "S", [40] = "D", [41] = "F", [42] = "G", [43] = "H", [44] = "J", [45] = "K", [46] = "L",
[52] = "Z", [53] = "X", [54] = "C", [55] = "V", [56] = "B", [57] = "N", [58] = "M",
[22] = "Back", -- Backspace
[36] = "Enter", -- Enter
[50] = "Shift", -- left shift
[60] = ".",
[61] = "/",
[62] = "Sym", -- right shift key
[64] = "Alt", -- left alt
[65] = " ", -- Spacebar
[67] = "Menu", -- F[1]
[72] = "LPgBack", -- F[6]
[73] = "LPgFwd", -- F[7]
[95] = "VPlus", -- F[11]
[96] = "VMinus", -- F[12]
[105] = "AA", -- right alt key
[110] = "Home", -- Home
[111] = "Up", -- arrow up
[112] = "RPgBack", -- normal PageUp
[113] = "Left", -- arrow left
[114] = "Right", -- arrow right
[115] = "Press", -- End (above arrows)
[116] = "Down", -- arrow down
[117] = "RPgFwd", -- normal PageDown
[119] = "Del", -- Delete
}
self.modifiers = {
Alt = false,
Shift = false
}
-- these groups are just helpers:
self.group = {
Cursor = { "Up", "Down", "Left", "Right" },
PgFwd = { "RPgFwd", "LPgFwd" },
PgBack = { "RPgBack", "LPgBack" },
Alphabet = {
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
},
AlphaNumeric = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
},
Numeric = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
},
Text = {
" ", ".", "/",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
},
Any = {
" ", ".", "/",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"Up", "Down", "Left", "Right", "Press",
"Back", "Enter", "Sym", "AA", "Menu", "Home", "Del",
"LPgBack", "RPgBack", "LPgFwd", "RPgFwd"
}
}
end
function Input:initTouchState()
self.cur_slot = 0
self.MTSlots = {}
self.ev_slots = {
[0] = {
slot = 0,
}
}
end
function Input:init()
if Device:hasKeyboard() then
self:initKeyMap()
end
if Device:isTouchDevice() then
self:initTouchState()
end
-- set up fake event map
self.event_map[10000] = "IntoSS" -- go into screen saver
self.event_map[10001] = "OutOfSS" -- go out of screen saver
self.event_map[10020] = "Charging"
self.event_map[10021] = "NotCharging"
if util.isEmulated() then
self:initKeyMap()
os.remove("/tmp/emu_event")
os.execute("mkfifo /tmp/emu_event")
input.open("/tmp/emu_event")
-- SDL key codes
self.event_map = self.sdl_event_map
else
local dev_mod = Device:getModel()
if not Device:isKobo() then
input.open("fake_events")
end
if dev_mod == "KindlePaperWhite" then
print(_("Auto-detected Kindle PaperWhite"))
Device:setTouchInputDev("/dev/input/event0")
input.open("/dev/input/event0")
elseif dev_mod == "KindlePaperWhite2" then
print(_("Auto-detected Kindle PaperWhite"))
Device:setTouchInputDev("/dev/input/event1")
input.open("/dev/input/event1")
elseif dev_mod == "KindleTouch" then
-- event0 in KindleTouch is "WM8962 Beep Generator" (useless)
-- event1 in KindleTouch is "imx-yoshi Headset" (useless)
Device:setTouchInputDev("/dev/input/event3")
input.open("/dev/input/event2") -- Home button
input.open("/dev/input/event3") -- touchscreen
-- KT does have one key!
self.event_map[102] = "Home"
-- update event hook
function Input:eventAdjustHook(ev)
if ev.type == EV_ABS then
--@TODO handle coordinates properly after
--screen rotate. (houqp)
if ev.code == ABS_MT_POSITION_X then
ev.value = Math.round(ev.value * (600/4095))
elseif ev.code == ABS_MT_POSITION_Y then
ev.value = Math.round(ev.value * (800/4095))
end
end
return ev
end
print(_("Auto-detected Kindle Touch"))
elseif Device:isKobo() then
local firm_rev = Device:getFirmVer()
input.open("/dev/input/event1")
Device:setTouchInputDev("/dev/input/event1")
input.open("/dev/input/event0") -- Light button and sleep slider
print(_("Auto-detected Kobo"))
print(_("Device model="))
print(_(dev_mod))
print(_("Firmware revision"))
print(_(firm_rev))
print(_("Screen height ="))
print(_(Screen:getHeight()))
print(_("Screen width ="))
print(_(Screen:getWidth()))
self:adjustKoboEventMap()
if dev_mod ~= 'Kobo_trilogy' then
function Input:eventAdjustHook(ev)
if ev.type == EV_ABS then
if ev.code == ABS_X then
ev.code = ABS_Y
elseif ev.code == ABS_Y then
ev.code = ABS_X
-- We always have to substract from the physical x,
-- regardless of the orientation
if (Screen:getWidth()<Screen:getHeight()) then
ev.value = Screen:getWidth() - ev.value
else
ev.value = Screen:getHeight() - ev.value
end
end
-- same thing for multitouch events (phoenix)
if ev.code == ABS_MT_POSITION_X then
ev.code = ABS_MT_POSITION_Y
elseif ev.code == ABS_MT_POSITION_Y then
ev.code = ABS_MT_POSITION_X
-- We always have to substract from the physical x,
-- regardless of the orientation
if (Screen:getWidth()<Screen:getHeight()) then
ev.value = Screen:getWidth() - ev.value
else
ev.value = Screen:getHeight() - ev.value
end
end
end
return ev
end
else -- kobo touch (trilogy)
-- FIXME some touch models should be treated as the other models,
-- depending on board revision
function Input:eventAdjustHook(ev)
if ev.code == ABS_X then
-- We always have to substract from the physical x,
-- regardless of the orientation
if (Screen:getWidth()<Screen:getHeight()) then
ev.value = Screen:getWidth() - ev.value
else
ev.value = Screen:getHeight() - ev.value
end
end
return ev
end
end
elseif dev_mod == "Kindle4" then
print(_("Auto-detected Kindle 4"))
input.open("/dev/input/event1")
self:adjustKindle4EventMap()
elseif dev_mod == "Kindle3" then
print(_("Auto-detected Kindle 3"))
input.open("/dev/input/event1")
input.open("/dev/input/event2")
elseif dev_mod == "KindleDXG" then
print(_("Auto-detected Kindle DXG"))
input.open("/dev/input/event1")
elseif dev_mod == "Kindle2" then
print(_("Auto-detected Kindle 2"))
input.open("/dev/input/event1")
else
print(_("Not supported device model!"))
os.exit(-1)
end
end
if Device:getModel() ~= 'Kobo_phoenix' then
function Input:handleTouchEv(ev)
return Input:handleTypeBTouchEv(ev)
end
else
function Input:handleTouchEv(ev)
return Input:handlePhoenixTouchEv(ev)
end
end
end
--[[
different device models shoudl overload this method if
necessary to make event compatible to KPV.
--]]
function Input:eventAdjustHook(ev)
-- do nothing by default
return ev
end
function Input:adjustKindle4EventMap()
self.event_map[193] = "LPgBack"
self.event_map[104] = "LPgFwd"
end
function Input:adjustKoboEventMap()
self.event_map[59] = "Power_SleepCover"
self.event_map[90] = "Light"
self.event_map[116] = "Power"
end
function Input:setTimeout(cb, tv_out)
local item = {
callback = cb,
deadline = tv_out,
}
for k,v in ipairs(self.timer_callbacks) do
if v.deadline > tv_out then
table.insert(self.timer_callbacks, k, item)
break
end
end
if #self.timer_callbacks <= 0 then
self.timer_callbacks[1] = item
end
end
function Input:handleKeyBoardEv(ev)
local keycode = self.event_map[ev.code]
if not keycode then
-- do not handle keypress for keys we don't know
return
end
-- take device rotation into account
if self.rotation_map[Screen:getRotationMode()][keycode] then
keycode = self.rotation_map[Screen:getRotationMode()][keycode]
end
if keycode == "IntoSS" or keycode == "OutOfSS"
or keycode == "Charging" or keycode == "NotCharging" then
return keycode
end
-- Kobo sleep
if keycode == "Power_SleepCover" then
if ev.value == EVENT_VALUE_KEY_PRESS then
return "Suspend"
else
return "Resume"
end
end
if ev.value == EVENT_VALUE_KEY_RELEASE
and (keycode == "Light" or keycode == "Power") then
return keycode
end
-- handle modifier keys
if self.modifiers[keycode] ~= nil then
if ev.value == EVENT_VALUE_KEY_PRESS then
self.modifiers[keycode] = true
elseif ev.value == EVENT_VALUE_KEY_RELEASE then
self.modifiers[keycode] = false
end
return
end
local key = Key:new(keycode, self.modifiers)
if ev.value == EVENT_VALUE_KEY_PRESS then
return Event:new("KeyPress", key)
elseif ev.value == EVENT_VALUE_KEY_RELEASE then
return Event:new("KeyRelease", key)
end
end
function Input:setMtSlot(slot, key, val)
if not self.ev_slots[slot] then
self.ev_slots[slot] = {
slot = slot
}
end
self.ev_slots[slot][key] = val
end
function Input:setCurrentMtSlot(key, val)
self:setMtSlot(self.cur_slot, key, val)
end
function Input:getMtSlot(slot)
return self.ev_slots[slot]
end
function Input:getCurrentMtSlot()
return self:getMtSlot(self.cur_slot)
end
function Input:confirmAbsxy()
self:setCurrentMtSlot("x", self.ev_slots[self.cur_slot]["abs_x"])
self:setCurrentMtSlot("y", self.ev_slots[self.cur_slot]["abs_y"])
end
function Input:cleanAbsxy()
self:setCurrentMtSlot("abs_x", nil)
self:setCurrentMtSlot("abs_y", nil)
end
--[[
parse each touch ev from kernel and build up tev.
tev will be sent to GestureDetector:feedEvent
Events for a single tap motion from Linux kernel (MT protocol B):
MT_TRACK_ID: 0
MT_X: 222
MT_Y: 207
SYN REPORT
MT_TRACK_ID: -1
SYN REPORT
Notice that each line is a single event.
From kernel document:
For type B devices, the kernel driver should associate a slot with each
identified contact, and use that slot to propagate changes for the contact.
Creation, replacement and destruction of contacts is achieved by modifying
the ABS_MT_TRACKING_ID of the associated slot. A non-negative tracking id
is interpreted as a contact, and the value -1 denotes an unused slot. A
tracking id not previously present is considered new, and a tracking id no
longer present is considered removed. Since only changes are propagated,
the full state of each initiated contact has to reside in the receiving
end. Upon receiving an MT event, one simply updates the appropriate
attribute of the current slot.
--]]
function Input:handleTypeBTouchEv(ev)
if ev.type == EV_SYN then
if ev.code == SYN_REPORT then
for _, MTSlot in pairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
end
-- feed ev in all slots to state machine
local touch_ges = GestureDetector:feedEvent(self.MTSlots)
self.MTSlots = {}
if touch_ges then
return Event:new("Gesture",
GestureDetector:adjustGesCoordinate(touch_ges)
)
end
end
elseif ev.type == EV_ABS then
if #self.MTSlots == 0 then
table.insert(self.MTSlots, self:getMtSlot(self.cur_slot))
end
if ev.code == ABS_MT_SLOT then
if self.cur_slot ~= ev.value then
table.insert(self.MTSlots, self:getMtSlot(ev.value))
end
self.cur_slot = ev.value
elseif ev.code == ABS_MT_TRACKING_ID then
self:setCurrentMtSlot("id", ev.value)
elseif ev.code == ABS_MT_POSITION_X then
self:setCurrentMtSlot("x", ev.value)
elseif ev.code == ABS_MT_POSITION_Y then
self:setCurrentMtSlot("y", ev.value)
-- code to emulate mt protocol on kobos
-- we "confirm" abs_x, abs_y only when pressure ~= 0
elseif ev.code == ABS_X then
self:setCurrentMtSlot("abs_x", ev.value)
elseif ev.code == ABS_Y then
self:setCurrentMtSlot("abs_y", ev.value)
elseif ev.code == ABS_PRESSURE then
if ev.value ~= 0 then
self:setCurrentMtSlot("id", 1)
self:confirmAbsxy()
else
self:cleanAbsxy()
self:setCurrentMtSlot("id", -1)
end
end
end
end
local is_TwoSlotTrack = false
function Input:handlePhoenixTouchEv(ev)
-- Hack on handleTouchEV for the Kobo Aura
-- DEBUG("is_TwoSlotTrack = ", is_TwoSlotTrack)
if ev.type == EV_SYN and ev.code == SYN_REPORT then
DEBUG("Event =|",ev.time.sec,"|",ev.time.usec,"|SYN_REPORT")
for _, MTSlot in pairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
end
-- feed ev in all slots to state machine
if Dbg.is_on then
if #self.MTSlots > 0 then
DEBUG("Evt", self.MTSlots[1].slot,"=|", self.MTSlots[1].timev.sec,"|", self.MTSlots[1].timev.usec,"|id=", self.MTSlots[1].id,"|x=", self.MTSlots[1].x,"|y=", self.MTSlots[1].y)
end -- if slot 0 exists
if #self.MTSlots == 2 then
DEBUG("Evt", self.MTSlots[2].slot,"=|", self.MTSlots[2].timev.sec,"|", self.MTSlots[2].timev.usec,"|id=", self.MTSlots[2].id,"|x=", self.MTSlots[2].x,"|y=", self.MTSlots[2].y)
end -- if slot 1 exists
end -- if Dbg.is_on
local touch_ges = GestureDetector:feedEvent(self.MTSlots)
self.MTSlots = {}
if touch_ges then
return Event:new("Gesture",
GestureDetector:adjustGesCoordinate(touch_ges)
)
end
elseif ev.type == EV_ABS and ( ev.code == ABS_PRESSURE or ev.code > ABS_MT_WIDTH_MAJOR ) then
if #self.MTSlots == 0 then
self.cur_slot = 0
table.insert(self.MTSlots, self:getMtSlot(self.cur_slot))
-- I have to add id's without events for the AURA.
self:setMtSlot(self.cur_slot, "id", 0)
end
-- I have changed ABS_MT_SLOT to ABS_MT_TRACKING_ID
if ev.code == ABS_MT_TRACKING_ID then
DEBUG("Event =|",ev.time.sec,"|",ev.time.usec,"|ABS_MT_TRACKING_ID|",ev.value)
if self.cur_slot ~= ev.value and #self.MTSlots == 1 then
table.insert(self.MTSlots, self:getMtSlot(ev.value))
-- I have to add id's without events for the AURA.
-- Since there are only two
-- ID's 0 and 1 and it's not 0,
self:setMtSlot(ev.value, "id", 1)
is_TwoSlotTrack = true
end
self.cur_slot = ev.value
elseif ev.code == ABS_MT_POSITION_X then
DEBUG("Event =|",ev.time.sec,"|",ev.time.usec,"|ABS_MT_POSITION_X |",ev.value)
self:setCurrentMtSlot("x", ev.value)
elseif ev.code == ABS_MT_POSITION_Y then
DEBUG("Event =|",ev.time.sec,"|",ev.time.usec,"|ABS_MT_POSITION_Y |",ev.value)
self:setCurrentMtSlot("y", ev.value)
elseif ev.code == ABS_PRESSURE and ev.value == 0 then
DEBUG("Event =|",ev.time.sec,"|",ev.time.usec,"|ABS_PRESSURE |",ev.value)
-- Pressure 0 events invalidate slot 0.
self:setMtSlot(0, "id", -1)
-- If there are 2 slots active, invalidates slot 1.
if is_TwoSlotTrack then
if #self.MTSlots == 1 then
DEBUG("Extra table inserted for Track ID -1 in slot 1")
table.insert(self.MTSlots, self:getMtSlot(1))
end
self:setMtSlot(1, "id", -1)
is_TwoSlotTrack = false
end
end
end
end
function Input:waitEvent(timeout_us, timeout_s)
-- wrapper for input.waitForEvents that will retry for some cases
local ok, ev
local wait_deadline = TimeVal:now() + TimeVal:new{
sec = timeout_s,
usec = timeout_us
}
while true do
if #self.timer_callbacks > 0 then
-- we don't block if there is any timer, set wait to 10us
while #self.timer_callbacks > 0 do
ok, ev = pcall(input.waitForEvent, 100)
if ok then break end
local tv_now = TimeVal:now()
if ((not timeout_us and not timeout_s) or tv_now < wait_deadline) then
-- check whether timer is up
if tv_now >= self.timer_callbacks[1].deadline then
local touch_ges = self.timer_callbacks[1].callback()
table.remove(self.timer_callbacks, 1)
if touch_ges then
-- Do we really need to clear all setTimeout after
-- decided a gesture? FIXME
Input.timer_callbacks = {}
return Event:new("Gesture",
GestureDetector:adjustGesCoordinate(touch_ges)
)
end -- EOF if touch_ges
end -- EOF if deadline reached
else
break
end -- EOF if not exceed wait timeout
end -- while #timer_callbacks > 0
else
ok, ev = pcall(input.waitForEvent, timeout_us)
end -- EOF if #timer_callbacks > 0
if ok then
break
end
if ev == "Waiting for input failed: timeout\n" then
-- don't report an error on timeout
ev = nil
break
elseif ev == "application forced to quit" then
os.exit(0)
end
--DEBUG("got error waiting for events:", ev)
if ev ~= "Waiting for input failed: 4\n" then
-- we only abort if the error is not EINTR
break
end
end
if ok and ev then
if Dbg.is_on and ev then
Dbg:logEv(ev)
end
ev = self:eventAdjustHook(ev)
if ev.type == EV_KEY then
return self:handleKeyBoardEv(ev)
elseif ev.type == EV_ABS or ev.type == EV_SYN then
return self:handleTouchEv(ev)
else
-- some other kind of event that we do not know yet
return Event:new("GenericInput", ev)
end
elseif not ok and ev then
return Event:new("InputError", ev)
end
end
--[[
helper function for formatting sequence definitions for output
]]
function Input:sequenceToString(sequence)
local modifiers = {}
local keystring = {"",""} -- first entries reserved for modifier specification
for _, key in ipairs(sequence) do
if type(key) == "table" then
local alternatives = {}
for _, alternative in ipairs(key) do
table.insert(alternatives, alternative)
end
table.insert(keystring, "{")
table.insert(keystring, table.concat(alternatives, "|"))
table.insert(keystring, "}")
elseif self.modifiers[key] ~= nil then
table.insert(modifiers, key)
else
table.insert(keystring, key)
end
end
if #modifiers then
keystring[1] = table.concat(modifiers, "-")
keystring[2] = "-"
end
return table.concat(keystring)
end
-- initialize the GestureDectector
-- so it can modify our (Input) state
GestureDetector.input = Input
return Input