Minor Input & TimeVal cleanups

* Input: Don't create a new TimeVal object for input frame timestamps, just promote our existing table by assigning it the `TimeVal` metatable.
* TimeVal: Export (const) `zero` & `huge` TimeVal objects, because they're common enough in our codebase. (NOTE: not actually const, that's a Lua 5.4 feature ;p).
* GestureDetector: Explain the behavior of the `last_tevs` & `first_tevs` tables, and why one needs a new object and not the other.
* Speaking of, simplify the copy method for `first_tevs`, because it doesn't need to create a new TimeVal object, we can just reference the original, it's unique and re-assigned for each frame.
pull/7557/head
NiLuJe 3 years ago
parent fce63d3c0f
commit 3274183466

@ -110,7 +110,7 @@ function ReaderDictionary:init()
-- Allow quick interruption or dismiss of search result window
-- with tap if done before this delay. After this delay, the
-- result window is shown and dismiss prevented for a few 100ms
self.quick_dismiss_before_delay = TimeVal:new{ sec = 3 }
self.quick_dismiss_before_delay = TimeVal:new{ sec = 3, usec = 0 }
-- Gather info about available dictionaries
if not available_ifos then

@ -1156,7 +1156,7 @@ function ReaderHighlight:onHoldRelease()
local long_final_hold = false
if self.hold_last_tv then
local hold_duration = UIManager:getTime() - self.hold_last_tv
if hold_duration > TimeVal:new{ sec = 3 } then
if hold_duration > TimeVal:new{ sec = 3, usec = 0 } then
-- We stayed 3 seconds before release without updating selection
long_final_hold = true
end

@ -1101,8 +1101,8 @@ function ReaderRolling:handleEngineCallback(ev, ...)
-- ignore other events
end
local ENGINE_PROGRESS_INITIAL_DELAY = TimeVal:new{ sec = 2 }
local ENGINE_PROGRESS_UPDATE_DELAY = TimeVal:new{ usec = 500000 }
local ENGINE_PROGRESS_INITIAL_DELAY = TimeVal:new{ sec = 2, usec = 0 }
local ENGINE_PROGRESS_UPDATE_DELAY = TimeVal:new{ sec = 0, usec = 500000 }
function ReaderRolling:showEngineProgress(percent)
if G_reader_settings and G_reader_settings:isFalse("cre_show_progress") then

@ -975,7 +975,7 @@ function ReaderView:checkAutoSaveSettings()
end
local interval = G_reader_settings:readSetting("auto_save_settings_interval_minutes")
interval = TimeVal:new{ sec = interval*60 }
interval = TimeVal:new{ sec = interval*60, usec = 0 }
local now_tv = UIManager:getTime()
if now_tv - self.settings_last_save_tv >= interval then
self.settings_last_save_tv = now_tv

@ -36,7 +36,7 @@ a touch event should have following format:
timev = TimeVal:new{...},
}
Don't confuse `tev` with raw evs from kernel, `tev` is build according to ev.
Don't confuse `tev` with raw evs from kernel, `tev` is built according to ev.
@{GestureDetector:feedEvent|GestureDetector:feedEvent(tev)} will return a
detection result when you feed a touch release event to it.
@ -143,6 +143,10 @@ function GestureDetector:feedEvent(tevs)
end
local ges = self.states[slot](self, tev)
if tev.id ~= -1 then
-- NOTE: tev is actually a simple reference to Input's self.ev_slots[slot],
-- which means self.last_tevs[slot] doesn't actually point to the *previous*
-- input frame for a given slot, but always points to the *current* input frame for that slot!
-- Compare to self.first_tevs below, which does create a copy...
self.last_tevs[slot] = tev
end
-- return no more than one gesture
@ -157,10 +161,7 @@ function GestureDetector:deepCopyEv(tev)
y = tev.y,
id = tev.id,
slot = tev.slot,
timev = TimeVal:new{
sec = tev.timev.sec,
usec = tev.timev.usec,
}
timev = tev.timev, -- No need to make a copy of this one, tev.timev is re-assigned to a new object on every SYN_REPORT
}
end
@ -172,7 +173,7 @@ function GestureDetector:isTapBounce(tap1, tap2, interval)
-- as we can no longer compute a sensible value...
local tv_diff = tap2.timev - tap1.timev
if not tv_diff:isPositive() then
tv_diff = TimeVal:new{ sec = math.huge }
tv_diff = TimeVal.huge
end
return (
math.abs(tap1.x - tap2.x) < self.SINGLE_TAP_BOUNCE_DISTANCE and
@ -184,7 +185,7 @@ end
function GestureDetector:isDoubleTap(tap1, tap2)
local tv_diff = tap2.timev - tap1.timev
if not tv_diff:isPositive() then
tv_diff = TimeVal:new{ sec = math.huge }
tv_diff = TimeVal.huge
end
return (
math.abs(tap1.x - tap2.x) < self.DOUBLE_TAP_DISTANCE and
@ -197,7 +198,7 @@ end
function GestureDetector:isHold(t1, t2)
local tv_diff = t2 - t1
if not tv_diff:isPositive() then
tv_diff = TimeVal:new{ sec = 0 }
tv_diff = TimeVal.zero
end
-- NOTE: We cheat by not checking a distance because we're only checking that in tapState,
-- which already ensures a stationary finger, by elimination ;).
@ -217,11 +218,11 @@ function GestureDetector:isTwoFingerTap()
local y_diff1 = math.abs(self.last_tevs[s2].y - self.first_tevs[s2].y)
local tv_diff0 = self.last_tevs[s1].timev - self.first_tevs[s1].timev
if not tv_diff0:isPositive() then
tv_diff0 = TimeVal:new{ sec = math.huge }
tv_diff0 = TimeVal.huge
end
local tv_diff1 = self.last_tevs[s2].timev - self.first_tevs[s2].timev
if not tv_diff1:isPositive() then
tv_diff1 = TimeVal:new{ sec = math.huge }
tv_diff1 = TimeVal.huge
end
return (
x_diff0 < self.TWO_FINGER_TAP_REGION and
@ -269,7 +270,7 @@ function GestureDetector:isSwipe(slot)
if not self.first_tevs[slot] or not self.last_tevs[slot] then return end
local tv_diff = self.last_tevs[slot].timev - self.first_tevs[slot].timev
if not tv_diff:isPositive() then
tv_diff = TimeVal:new{ sec = math.huge }
tv_diff = TimeVal.huge
end
if tv_diff < ges_swipe_interval then
local x_diff = self.last_tevs[slot].x - self.first_tevs[slot].x
@ -360,6 +361,9 @@ function GestureDetector:initialState(tev)
-- user starts a new touch motion
if not self.detectings[slot] then
self.detectings[slot] = true
-- NOTE: We can't use a simple reference, because tev is actually Input's self.ev_slots[slot],
-- and *that* is a fixed reference for a given slot!
-- Here, we really want to rememver the *first* tev, so, make a copy of it.
self.first_tevs[slot] = self:deepCopyEv(tev)
-- default to tap state
return self:switchState("tapState", tev)
@ -686,7 +690,7 @@ function GestureDetector:handlePan(tev)
local pan_direction, pan_distance = self:getPath(slot)
local tv_diff = self.last_tevs[slot].timev - self.first_tevs[slot].timev
if not tv_diff:isPositive() then
tv_diff = TimeVal:new{ sec = math.huge }
tv_diff = TimeVal.huge
end
local pan_ev = {

@ -607,8 +607,10 @@ function Input:handleTouchEv(ev)
end
elseif ev.type == C.EV_SYN then
if ev.code == C.SYN_REPORT then
-- Promote our event's time table to a real TimeVal
setmetatable(ev.time, TimeVal)
for _, MTSlot in pairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
self:setMtSlot(MTSlot.slot, "timev", ev.time)
if self.snow_protocol then
-- if a slot appears in the current touch event, set "used"
self:setMtSlot(MTSlot.slot, "used", true)
@ -622,7 +624,7 @@ function Input:handleTouchEv(ev)
table.insert(self.MTSlots, slot)
if not slot.used then
slot.id = -1
slot.timev = TimeVal:new(ev.time)
slot.timev = ev.time
end
end
end
@ -691,8 +693,9 @@ function Input:handleTouchEvPhoenix(ev)
end
elseif ev.type == C.EV_SYN then
if ev.code == C.SYN_REPORT then
setmetatable(ev.time, TimeVal)
for _, MTSlot in pairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
self:setMtSlot(MTSlot.slot, "timev", ev.time)
end
-- feed ev in all slots to state machine
local touch_ges = self.gesture_detector:feedEvent(self.MTSlots)
@ -726,8 +729,9 @@ function Input:handleTouchEvLegacy(ev)
end
elseif ev.type == C.EV_SYN then
if ev.code == C.SYN_REPORT then
setmetatable(ev.time, TimeVal)
for _, MTSlot in pairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
self:setMtSlot(MTSlot.slot, "timev", ev.time)
end
-- feed ev in all slots to state machine
@ -986,7 +990,7 @@ function Input:waitEvent(now, deadline)
poll_timeout = poll_deadline - now
else
-- We've already blown the deadline: make select return immediately (most likely straight to timeout)
poll_timeout = TimeVal:new{ sec = 0 }
poll_timeout = TimeVal.zero
end
end
@ -1057,7 +1061,7 @@ function Input:waitEvent(now, deadline)
poll_timeout = deadline - now
else
-- Deadline has been blown: make select return immediately.
poll_timeout = TimeVal:new{ sec = 0 }
poll_timeout = TimeVal.zero
end
end

@ -190,7 +190,7 @@ function Device:init()
w = 0, h = 0,
}
local timev = TimeVal:new(ev.time)
setmetatable(ev.time, TimeVal)
local fake_ges = {
ges = "pan",
@ -205,7 +205,7 @@ function Device:init()
y = 100*scrolled_y,
},
pos = pos,
time = timev,
time = ev.time,
mousewheel_direction = scrolled_y,
}
local fake_ges_release = {
@ -215,7 +215,7 @@ function Device:init()
relative = fake_ges.relative,
relative_delayed = fake_ges.relative_delayed,
pos = pos,
time = timev,
time = ev.time,
}
local fake_pan_ev = Event:new("Pan", nil, fake_ges)
local fake_release_ev = Event:new("Gesture", fake_ges_release)

@ -43,8 +43,8 @@ function GestureRange:match(gs)
-- This field sets up rate-limiting (in matches per second).
-- It's mostly useful for e-Ink devices with less powerful CPUs
-- and screens that cannot handle the amount of gesture events that would otherwise be generated.
local last_time = self.last_time or TimeVal:new{}
if gs.time - last_time > TimeVal:new{usec = 1000000 / self.rate} then
local last_time = self.last_time or TimeVal.zero
if gs.time - last_time > TimeVal:new{ usec = 1000000 / self.rate } then
self.last_time = gs.time
else
return false

@ -112,7 +112,7 @@ end
-- If sec is negative, time went backwards!
function TimeVal:__sub(time_b)
local diff = TimeVal:new{}
local diff = TimeVal:new{ sec = 0, usec = 0 }
diff.sec = self.sec - time_b.sec
diff.usec = self.usec - time_b.usec
@ -126,7 +126,7 @@ function TimeVal:__sub(time_b)
end
function TimeVal:__add(time_b)
local sum = TimeVal:new{}
local sum = TimeVal:new{ sec = 0, usec = 0 }
sum.sec = self.sec + time_b.sec
sum.usec = self.usec + time_b.usec
@ -158,7 +158,7 @@ Which means that, yes, this is a fancier POSIX Epoch ;).
]]
function TimeVal:realtime()
local sec, usec = util.gettime()
return TimeVal:new{sec = sec, usec = usec}
return TimeVal:new{ sec = sec, usec = usec }
end
--[[--
@ -175,7 +175,7 @@ function TimeVal:monotonic()
C.clock_gettime(C.CLOCK_MONOTONIC, timespec)
-- TIMESPEC_TO_TIMEVAL
return TimeVal:new{sec = tonumber(timespec.tv_sec), usec = math.floor(tonumber(timespec.tv_nsec / 1000))}
return TimeVal:new{ sec = tonumber(timespec.tv_sec), usec = math.floor(tonumber(timespec.tv_nsec / 1000)) }
end
--- Ditto, but w/ CLOCK_MONOTONIC_COARSE if it's available and has a 1ms resolution or better (uses CLOCK_MONOTONIC otherwise).
@ -184,7 +184,7 @@ function TimeVal:monotonic_coarse()
C.clock_gettime(PREFERRED_MONOTONIC_CLOCKID, timespec)
-- TIMESPEC_TO_TIMEVAL
return TimeVal:new{sec = tonumber(timespec.tv_sec), usec = math.floor(tonumber(timespec.tv_nsec / 1000))}
return TimeVal:new{ sec = tonumber(timespec.tv_sec), usec = math.floor(tonumber(timespec.tv_nsec / 1000)) }
end
--- Ditto, but w/ CLOCK_REALTIME_COARSE if it's available and has a 1ms resolution or better (uses CLOCK_REALTIME otherwise).
@ -193,7 +193,7 @@ function TimeVal:realtime_coarse()
C.clock_gettime(PREFERRED_REALTIME_CLOCKID, timespec)
-- TIMESPEC_TO_TIMEVAL
return TimeVal:new{sec = tonumber(timespec.tv_sec), usec = math.floor(tonumber(timespec.tv_nsec / 1000))}
return TimeVal:new{ sec = tonumber(timespec.tv_sec), usec = math.floor(tonumber(timespec.tv_nsec / 1000)) }
end
--- Ditto, but w/ CLOCK_BOOTTIME (will return a TimeVal set to 0, 0 if the clock source is unsupported, as it's 2.6.39+)
@ -202,7 +202,7 @@ function TimeVal:boottime()
C.clock_gettime(C.CLOCK_BOOTTIME, timespec)
-- TIMESPEC_TO_TIMEVAL
return TimeVal:new{sec = tonumber(timespec.tv_sec), usec = math.floor(tonumber(timespec.tv_nsec / 1000))}
return TimeVal:new{ sec = tonumber(timespec.tv_sec), usec = math.floor(tonumber(timespec.tv_nsec / 1000)) }
end
--[[-- Alias for `monotonic_coarse`.
@ -235,7 +235,7 @@ end
function TimeVal:fromnumber(seconds)
local sec = math.floor(seconds)
local usec = math.floor((seconds - sec) * 1000000 + 0.5)
return TimeVal:new{sec = sec, usec = usec}
return TimeVal:new{ sec = sec, usec = usec }
end
--- Checks if a TimeVal object is positive
@ -248,4 +248,11 @@ function TimeVal:isZero()
return self.sec == 0 and self.usec == 0
end
--- We often need a const TimeVal set to zero...
--- LuaJIT doesn't actually support const values (Lua 5.4+): Do *NOT* modify it.
TimeVal.zero = TimeVal:new{ sec = 0, usec = 0 }
--- Ditto for one set to math.huge
TimeVal.huge = TimeVal:new{ sec = math.huge, usec = 0 }
return TimeVal

@ -1052,13 +1052,13 @@ function UIManager:discardEvents(set_or_seconds)
-- sometimes > 500ms on some devices/temperatures.
-- So, block for 400ms (to have it displayed) + 400ms
-- for user reaction to it
delay = TimeVal:new{ usec = 800000 }
delay = TimeVal:new{ sec = 0, usec = 800000 }
else
-- On non-eInk screen, display is usually instantaneous
delay = TimeVal:new{ usec = 400000 }
delay = TimeVal:new{ sec = 0, usec = 400000 }
end
else -- we expect a number
delay = TimeVal:new{ sec = set_or_seconds }
delay = TimeVal:new{ sec = set_or_seconds, usec = 0 }
end
self._discard_events_till = self._now + delay
end
@ -1160,7 +1160,7 @@ function UIManager:_checkTasks()
break
end
local task = self._task_queue[1]
local task_tv = task.time or TimeVal:new{}
local task_tv = task.time or TimeVal.zero
if task_tv <= self._now then
-- remove from table
table.remove(self._task_queue, 1)

@ -155,7 +155,7 @@ function DictQuickLookup:init()
-- callback function when HoldReleaseText is handled as args
args = function(text, hold_duration)
local lookup_target
if hold_duration < TimeVal:new{ sec = 3 } then
if hold_duration < TimeVal:new{ sec = 3, usec = 0 } then
-- do this lookup in the same domain (dict/wikipedia)
lookup_target = self.is_wiki and "LookupWikipedia" or "LookupWord"
else

@ -195,7 +195,7 @@ function FootnoteWidget:init()
-- callback function when HoldReleaseText is handled as args
args = function(text, hold_duration)
if self.dialog then
local lookup_target = hold_duration < TimeVal:new{ sec = 3 } and "LookupWord" or "LookupWikipedia"
local lookup_target = hold_duration < TimeVal:new{ sec = 3, usec = 0 } and "LookupWord" or "LookupWikipedia"
self.dialog:handleEvent(
Event:new(lookup_target, text)
)

@ -34,7 +34,7 @@ local FrontLightWidget = InputContainer:new{
-- This should stay active during natural light configuration
is_always_active = true,
rate = Screen.low_pan_rate and 3 or 30, -- Widget update rate.
last_time = TimeVal:new{}, -- Tracks last update time to prevent update spamming.
last_time = TimeVal.zero, -- Tracks last update time to prevent update spamming.
}
function FrontLightWidget:init()
@ -643,8 +643,8 @@ function FrontLightWidget:onTapProgress(arg, ges_ev)
-- But limit the widget update frequency on E Ink.
if Screen.low_pan_rate then
local current_time = TimeVal:now()
local last_time = self.last_time or TimeVal:new{}
if current_time - last_time > TimeVal:new{usec = 1000000 / self.rate} then
local last_time = self.last_time or TimeVal.zero
if current_time - last_time > TimeVal:new{ usec = 1000000 / self.rate } then
self.last_time = current_time
else
-- Schedule a final update after we stop panning.

@ -105,7 +105,7 @@ function Notification:_cleanShownStack(num)
-- to follow what is happening).
-- As a sanity check, we also forget those shown for
-- more than 30s in case no close event was received.
local expire_tv = UIManager:getTime() - TimeVal:new{ sec = 30 }
local expire_tv = UIManager:getTime() - TimeVal:new{ sec = 30, usec = 0 }
for i=#Notification._nums_shown, 1, -1 do
if Notification._nums_shown[i] and Notification._nums_shown[i] > expire_tv then
break -- still shown (or not yet expired)

@ -50,9 +50,9 @@ function AutoSuspend:_schedule(shutdown_only)
delay_shutdown = self.autoshutdown_timeout_seconds
else
local now_tv = UIManager:getTime()
delay_suspend = self.last_action_tv + TimeVal:new{ sec = self.auto_suspend_timeout_seconds } - now_tv
delay_suspend = self.last_action_tv + TimeVal:new{ sec = self.auto_suspend_timeout_seconds, usec = 0 } - now_tv
delay_suspend = delay_suspend:tonumber()
delay_shutdown = self.last_action_tv + TimeVal:new{ sec = self.autoshutdown_timeout_seconds } - now_tv
delay_shutdown = self.last_action_tv + TimeVal:new{ sec = self.autoshutdown_timeout_seconds, usec = 0 } - now_tv
delay_shutdown = delay_shutdown:tonumber()
end

@ -35,7 +35,7 @@ function AutoTurn:_schedule(settings_id)
return
end
local delay = self.last_action_tv + TimeVal:new{ sec = self.autoturn_sec } - UIManager:getTime()
local delay = self.last_action_tv + TimeVal:new{ sec = self.autoturn_sec, usec = 0 } - UIManager:getTime()
delay = delay:tonumber()
if delay <= 0 then

@ -118,7 +118,7 @@ function BackgroundRunner:_finishJob(job)
assert(self ~= nil)
if type(job.executable) == "function" then
local tv_diff = job.end_tv - job.start_tv
local threshold = TimeVal:new{ sec = 1 }
local threshold = TimeVal:new{ sec = 1, usec = 0 }
job.timeout = (tv_diff > threshold)
end
job.blocked = job.timeout

Loading…
Cancel
Save