From d3e0424122fbd7ef2aa900751c1bbb2cb836f5e0 Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Sat, 17 Nov 2012 13:41:26 -0500 Subject: [PATCH] add setTimeOut method in inputevent.lua adapt all the state machine to use setTimeOut. Now supported gestures includes tap, double tap and hold. --- frontend/ui/gesturedetector.lua | 64 +++++++++++++++++++++++++-------- frontend/ui/inputevent.lua | 55 +++++++++++++++++----------- frontend/ui/time.lua | 3 ++ frontend/ui/ui.lua | 1 + input.c | 3 +- 5 files changed, 89 insertions(+), 37 deletions(-) diff --git a/frontend/ui/gesturedetector.lua b/frontend/ui/gesturedetector.lua index e21d82e4b..c405c0236 100644 --- a/frontend/ui/gesturedetector.lua +++ b/frontend/ui/gesturedetector.lua @@ -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 diff --git a/frontend/ui/inputevent.lua b/frontend/ui/inputevent.lua index 61a8ab735..5531fbca0 100644 --- a/frontend/ui/inputevent.lua +++ b/frontend/ui/inputevent.lua @@ -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 diff --git a/frontend/ui/time.lua b/frontend/ui/time.lua index 52e47021a..9189cc614 100644 --- a/frontend/ui/time.lua +++ b/frontend/ui/time.lua @@ -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 diff --git a/frontend/ui/ui.lua b/frontend/ui/ui.lua index be876c099..23b14eaa2 100644 --- a/frontend/ui/ui.lua +++ b/frontend/ui/ui.lua @@ -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 diff --git a/input.c b/input.c index 245abd483..baa77d7a3 100644 --- a/input.c +++ b/input.c @@ -299,8 +299,9 @@ static int waitForInput(lua_State *L) { } int ticks = SDL_GetTicks(); - if (usecs < 0) + if (usecs < 0) { SDL_WaitEvent(&event); + } else { while (SDL_GetTicks()-ticks <= usecs/1000) { if (SDL_PollEvent(&event)) break;