diff --git a/frontend/apps/reader/modules/readerdictionary.lua b/frontend/apps/reader/modules/readerdictionary.lua index db755208f..651bf3119 100644 --- a/frontend/apps/reader/modules/readerdictionary.lua +++ b/frontend/apps/reader/modules/readerdictionary.lua @@ -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 diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index 4ad664bd1..349b9b97b 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -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 diff --git a/frontend/apps/reader/modules/readerrolling.lua b/frontend/apps/reader/modules/readerrolling.lua index d7a02317a..dd36ba2c3 100644 --- a/frontend/apps/reader/modules/readerrolling.lua +++ b/frontend/apps/reader/modules/readerrolling.lua @@ -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 diff --git a/frontend/apps/reader/modules/readerview.lua b/frontend/apps/reader/modules/readerview.lua index 0a6fc31ea..68f1c9966 100644 --- a/frontend/apps/reader/modules/readerview.lua +++ b/frontend/apps/reader/modules/readerview.lua @@ -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 diff --git a/frontend/device/gesturedetector.lua b/frontend/device/gesturedetector.lua index d64c0bf4b..c8a8490f1 100644 --- a/frontend/device/gesturedetector.lua +++ b/frontend/device/gesturedetector.lua @@ -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 = { diff --git a/frontend/device/input.lua b/frontend/device/input.lua index 45b2219b5..5620d55b1 100644 --- a/frontend/device/input.lua +++ b/frontend/device/input.lua @@ -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 diff --git a/frontend/device/sdl/device.lua b/frontend/device/sdl/device.lua index b11b2f013..43581056a 100644 --- a/frontend/device/sdl/device.lua +++ b/frontend/device/sdl/device.lua @@ -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) diff --git a/frontend/ui/gesturerange.lua b/frontend/ui/gesturerange.lua index c39917297..881b1d6eb 100644 --- a/frontend/ui/gesturerange.lua +++ b/frontend/ui/gesturerange.lua @@ -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 diff --git a/frontend/ui/timeval.lua b/frontend/ui/timeval.lua index b4c4f1f53..3948f721e 100644 --- a/frontend/ui/timeval.lua +++ b/frontend/ui/timeval.lua @@ -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 diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index 32b3941ab..57fc71217 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -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) diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 420ecf0b5..9712c45d6 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -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 diff --git a/frontend/ui/widget/footnotewidget.lua b/frontend/ui/widget/footnotewidget.lua index 7f26eb78a..efcdce39d 100644 --- a/frontend/ui/widget/footnotewidget.lua +++ b/frontend/ui/widget/footnotewidget.lua @@ -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) ) diff --git a/frontend/ui/widget/frontlightwidget.lua b/frontend/ui/widget/frontlightwidget.lua index a54afbe25..39764fec0 100644 --- a/frontend/ui/widget/frontlightwidget.lua +++ b/frontend/ui/widget/frontlightwidget.lua @@ -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. diff --git a/frontend/ui/widget/notification.lua b/frontend/ui/widget/notification.lua index 17db5862e..65ecee04a 100644 --- a/frontend/ui/widget/notification.lua +++ b/frontend/ui/widget/notification.lua @@ -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) diff --git a/plugins/autosuspend.koplugin/main.lua b/plugins/autosuspend.koplugin/main.lua index 87bec3d5b..ae285ce65 100644 --- a/plugins/autosuspend.koplugin/main.lua +++ b/plugins/autosuspend.koplugin/main.lua @@ -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 diff --git a/plugins/autoturn.koplugin/main.lua b/plugins/autoturn.koplugin/main.lua index 2e7431606..d165bce13 100644 --- a/plugins/autoturn.koplugin/main.lua +++ b/plugins/autoturn.koplugin/main.lua @@ -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 diff --git a/plugins/backgroundrunner.koplugin/main.lua b/plugins/backgroundrunner.koplugin/main.lua index a9fa3de08..451e5e22b 100644 --- a/plugins/backgroundrunner.koplugin/main.lua +++ b/plugins/backgroundrunner.koplugin/main.lua @@ -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