mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
add setTimeOut method in inputevent.lua
adapt all the state machine to use setTimeOut. Now supported gestures includes tap, double tap and hold.
This commit is contained in:
@@ -51,7 +51,8 @@ SYN REPORT
|
||||
|
||||
GestureDetector = {
|
||||
-- all the time parameters are in us
|
||||
DOUBLE_TAP_TIME = 500 * 1000,
|
||||
DOUBLE_TAP_INTERVAL = 300 * 1000,
|
||||
HOLD_INTERVAL = 1000 * 1000,
|
||||
-- distance parameters
|
||||
DOUBLE_TAP_DISTANCE = 50,
|
||||
PAN_THRESHOLD = 50,
|
||||
@@ -103,13 +104,17 @@ function GestureDetector:isDoubleTap(tap1, tap2)
|
||||
return (
|
||||
math.abs(tap1.x - tap2.x) < self.DOUBLE_TAP_DISTANCE and
|
||||
math.abs(tap1.y - tap2.y) < self.DOUBLE_TAP_DISTANCE and
|
||||
(tv_diff.sec == 0 and (tv_diff.usec) < self.DOUBLE_TAP_TIME)
|
||||
(tv_diff.sec == 0 and (tv_diff.usec) < self.DOUBLE_TAP_INTERVAL)
|
||||
)
|
||||
end
|
||||
|
||||
function GestureDetector:switchState(state, ev)
|
||||
--[[
|
||||
Warning! this method won't update self.state, you need to do it
|
||||
in each state method!
|
||||
--]]
|
||||
function GestureDetector:switchState(state_new, ev)
|
||||
--@TODO do we need to check whether state is valid? (houqp)
|
||||
return self[state](self, ev)
|
||||
return self[state_new](self, ev)
|
||||
end
|
||||
|
||||
function GestureDetector:clearState()
|
||||
@@ -130,7 +135,7 @@ function GestureDetector:initialState(ev)
|
||||
end
|
||||
end
|
||||
if ev.x and ev.y then
|
||||
-- a new event has just started
|
||||
-- user starts a new touch motion
|
||||
if not self.ev_start then
|
||||
self.ev_start = true
|
||||
-- default to tap state
|
||||
@@ -141,8 +146,9 @@ end
|
||||
|
||||
--[[
|
||||
this method handles both single and double tap
|
||||
]]
|
||||
--]]
|
||||
function GestureDetector:tapState(ev)
|
||||
DEBUG("in tap state...", ev)
|
||||
if ev.id == -1 then
|
||||
-- end of tap event
|
||||
local ges_ev = {
|
||||
@@ -161,8 +167,10 @@ function GestureDetector:tapState(ev)
|
||||
timev = ev.timev,
|
||||
}
|
||||
|
||||
if self.last_tap and
|
||||
if self.last_tap ~= nil and
|
||||
self:isDoubleTap(self.last_tap, cur_tap) then
|
||||
-- it is a double tap
|
||||
self:clearState()
|
||||
ges_ev.ges = "double_tap"
|
||||
self.last_tap = nil
|
||||
return ges_ev
|
||||
@@ -170,31 +178,42 @@ function GestureDetector:tapState(ev)
|
||||
|
||||
-- set current tap to last tap
|
||||
self.last_tap = cur_tap
|
||||
|
||||
local dead_line = self.cur_ev.timev + TimeVal:new{
|
||||
sec = 0, usec = self.DOUBLE_TAP_INTERVAL,
|
||||
}
|
||||
DEBUG("set up tap timer")
|
||||
Input:setTimeOut(function()
|
||||
print("in tap timer", self.last_tap ~= nil)
|
||||
-- double tap will set last_tap to nil
|
||||
-- so if it is not, then user must only
|
||||
-- tapped once
|
||||
if self.last_tap then
|
||||
if self.last_tap ~= nil then
|
||||
self.last_tap = nil
|
||||
self:clearState()
|
||||
-- we are using closure here
|
||||
return ges_ev
|
||||
end
|
||||
end, self.cur_ev.timev+TimeVal:new{sec=0, usec=DOUBLE_TAP_TIME})
|
||||
|
||||
end, dead_line)
|
||||
-- we are already at the end of touch event
|
||||
-- so reset the state
|
||||
self:clearState()
|
||||
elseif self.state ~= self.tapState then
|
||||
-- switched from other state, probably from initialState
|
||||
-- we return nil in this case
|
||||
self.state = self.tapState
|
||||
self.cur_x = ev.x
|
||||
self.cur_y = ev.y
|
||||
--@TODO set up hold timer (houqp)
|
||||
DEBUG("set up hold timer")
|
||||
local dead_line = self.cur_ev.timev + TimeVal:new{
|
||||
sec = 0, usec = self.HOLD_INTERVAL
|
||||
}
|
||||
Input:setTimeOut(function()
|
||||
print("hold timer", self.state == self.tapState)
|
||||
if self.state == self.tapState then
|
||||
-- timer set in tapState, so we switch to hold
|
||||
return self:switchState("holdState")
|
||||
end
|
||||
end,
|
||||
self.cur_ev.timev + TimeVal:new{sec = 0, usec = HOLD_TIME})
|
||||
end, dead_line)
|
||||
else
|
||||
-- it is not end of touch event, see if we need to switch to
|
||||
-- other states
|
||||
@@ -208,8 +227,10 @@ function GestureDetector:tapState(ev)
|
||||
end
|
||||
|
||||
function GestureDetector:panState(ev)
|
||||
DEBUG("in pan state...")
|
||||
if ev.id == -1 then
|
||||
-- end of pan, signal swipe gesture
|
||||
self:clearState()
|
||||
elseif self.state ~= self.panState then
|
||||
self.state = self.panState
|
||||
--@TODO calculate direction here (houqp)
|
||||
@@ -220,7 +241,11 @@ function GestureDetector:panState(ev)
|
||||
end
|
||||
|
||||
function GestureDetector:holdState(ev)
|
||||
DEBUG("in hold state...")
|
||||
-- when we switch to hold state, we pass no ev
|
||||
-- so ev = nil
|
||||
if not ev and self.cur_x and self.cur_y then
|
||||
self.state = self.holdState
|
||||
return {
|
||||
ges = "hold",
|
||||
pos = Geom:new{
|
||||
@@ -231,7 +256,16 @@ function GestureDetector:holdState(ev)
|
||||
}
|
||||
end
|
||||
if ev.id == -1 then
|
||||
-- end of hold, signal hold release?
|
||||
-- end of hold, signal hold release
|
||||
self:clearState()
|
||||
return {
|
||||
ges = "hold_release",
|
||||
pos = Geom:new{
|
||||
x = self.cur_x,
|
||||
y = self.cur_y,
|
||||
w = 0, h = 0,
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -269,10 +269,19 @@ function Input:adjustKindle4EventMap()
|
||||
end
|
||||
|
||||
function Input:setTimeOut(cb, tv_out)
|
||||
table.insert(self.timer_callbacks, {
|
||||
local item = {
|
||||
callback = cb,
|
||||
dead_line = tv_out,
|
||||
})
|
||||
}
|
||||
for k,v in ipairs(self.timer_callbacks) do
|
||||
if v.dead_line > 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:waitEvent(timeout_us, timeout_s)
|
||||
@@ -283,9 +292,26 @@ function Input:waitEvent(timeout_us, timeout_s)
|
||||
usec = timeout_us
|
||||
}
|
||||
while true do
|
||||
if #self.timer_callbacks then
|
||||
if #self.timer_callbacks > 0 then
|
||||
-- we don't block if there is any timer, set wait to 10us
|
||||
ok, ev = pcall(input.waitForEvent, 10)
|
||||
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].dead_line then
|
||||
local ges = self.timer_callbacks[1].callback()
|
||||
table.remove(self.timer_callbacks, 1)
|
||||
if ges then
|
||||
-- Do we really need to clear all setTimeOut after
|
||||
-- decided a gesture? FIXME
|
||||
Input.timer_callbacks = {}
|
||||
return Event:new("Gesture", ges)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
ok, ev = pcall(input.waitForEvent, timeout_us)
|
||||
end
|
||||
@@ -293,25 +319,13 @@ function Input:waitEvent(timeout_us, timeout_s)
|
||||
break
|
||||
end
|
||||
if ev == "Waiting for input failed: timeout\n" then
|
||||
local tv_now = TimeVal:now()
|
||||
if #self.timer_thread and tv_now < wait_deadline then
|
||||
-- check whether timer is up
|
||||
if tv_now >= timer_thread[1].dead_line then
|
||||
local ges = self.timer_callbacks[1].callback()
|
||||
table.remove(self.timer_callbacks, 1)
|
||||
if ges then
|
||||
return Event:new("Gesture", ges)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- don't report an error on timeout
|
||||
ev = nil
|
||||
break
|
||||
end
|
||||
-- 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)
|
||||
--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
|
||||
@@ -363,7 +377,6 @@ function Input:waitEvent(timeout_us, timeout_s)
|
||||
end
|
||||
elseif ev.type == EV_ABS or ev.type == EV_SYN then
|
||||
local touch_ges = GestureDetector:feedEvent(ev)
|
||||
DEBUG(touch_ges)
|
||||
if touch_ges then
|
||||
return Event:new("Gesture", touch_ges)
|
||||
end
|
||||
|
||||
@@ -10,6 +10,9 @@ function TimeVal:new(o)
|
||||
end
|
||||
if o.usec == nil then
|
||||
o.usec = 0
|
||||
elseif o.usec > 1000000 then
|
||||
o.sec = o.sec + maht.floor(o.usec/1000000)
|
||||
o.usec = o.usec % 1000000
|
||||
end
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
||||
@@ -200,6 +200,7 @@ function UIManager:run()
|
||||
|
||||
-- delegate input_event to handler
|
||||
if input_event then
|
||||
DEBUG(input_event)
|
||||
self:sendEvent(input_event)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user