[feat] Replace TimeVal (RIP) with time, fixed point time seconds (#8999)

reviewable/pr9032/r1
zwim 2 years ago committed by GitHub
parent 54ead5fc88
commit 9b9cfe29a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,7 +16,7 @@ memory_confirm_box = nil,
function ReaderDeviceStatus:init()
if Device:hasBattery() then
self.battery_interval = G_reader_settings:readSetting("device_status_battery_interval", 10)
self.battery_interval_m = G_reader_settings:readSetting("device_status_battery_interval_minutes", 10)
self.battery_threshold = G_reader_settings:readSetting("device_status_battery_threshold", 20)
self.battery_threshold_high = G_reader_settings:readSetting("device_status_battery_threshold_high", 100)
self.checkLowBatteryLevel = function()
@ -45,13 +45,13 @@ function ReaderDeviceStatus:init()
UIManager:show(self.battery_confirm_box)
end
end
UIManager:scheduleIn(self.battery_interval * 60, self.checkLowBatteryLevel)
UIManager:scheduleIn(self.battery_interval_m * 60, self.checkLowBatteryLevel)
end
self:startBatteryChecker()
end
if not Device:isAndroid() then
self.memory_interval = G_reader_settings:readSetting("device_status_memory_interval", 5)
self.memory_interval_m = G_reader_settings:readSetting("device_status_memory_interval_minutes", 5)
self.memory_threshold = G_reader_settings:readSetting("device_status_memory_threshold", 100)
self.checkHighMemoryUsage = function()
local statm = io.open("/proc/self/statm", "r")
@ -103,7 +103,7 @@ function ReaderDeviceStatus:init()
end
end
end
UIManager:scheduleIn(self.memory_interval * 60, self.checkHighMemoryUsage)
UIManager:scheduleIn(self.memory_interval_m * 60, self.checkHighMemoryUsage)
end
self:startMemoryChecker()
end
@ -136,7 +136,7 @@ function ReaderDeviceStatus:addToMainMenu(menu_items)
table.insert(menu_items.device_status_alarm.sub_item_table,
{
text_func = function()
return T(_("Check interval: %1 min"), self.battery_interval)
return T(_("Check interval: %1 min"), self.battery_interval_m)
end,
enabled_func = function()
return G_reader_settings:isTrue("device_status_battery_alarm")
@ -144,18 +144,18 @@ function ReaderDeviceStatus:addToMainMenu(menu_items)
keep_menu_open = true,
callback = function(touchmenu_instance)
UIManager:show(SpinWidget:new{
value = self.battery_interval,
value = self.battery_interval_m,
value_min = 1,
value_max = 60,
default_value = 10,
value_hold_step = 5,
title_text = _("Battery check interval"),
callback = function(spin)
self.battery_interval = spin.value
G_reader_settings:saveSetting("device_status_battery_interval", self.battery_interval)
self.battery_interval_m = spin.value
G_reader_settings:saveSetting("device_status_battery_interval_minutes", self.battery_interval_m)
touchmenu_instance:updateItems()
powerd:setDismissBatteryStatus(false)
UIManager:scheduleIn(self.battery_interval * 60, self.checkLowBatteryLevel)
UIManager:scheduleIn(self.battery_interval_m * 60, self.checkLowBatteryLevel)
end,
})
end,
@ -228,7 +228,7 @@ High level threshold is checked when the device is charging.]]),
table.insert(menu_items.device_status_alarm.sub_item_table,
{
text_func = function()
return T(_("Check interval: %1 min"), self.memory_interval)
return T(_("Check interval: %1 min"), self.memory_interval_m)
end,
enabled_func = function()
return G_reader_settings:isTrue("device_status_memory_alarm")
@ -236,17 +236,17 @@ High level threshold is checked when the device is charging.]]),
keep_menu_open = true,
callback = function(touchmenu_instance)
UIManager:show(SpinWidget:new{
value = self.memory_interval,
value = self.memory_interval_m,
value_min = 1,
value_max = 60,
default_value = 5,
value_hold_step = 5,
title_text = _("Memory check interval"),
callback = function(spin)
self.memory_interval = spin.value
G_reader_settings:saveSetting("device_status_memory_interval", self.memory_interval)
self.memory_interval_m = spin.value
G_reader_settings:saveSetting("device_status_memory_interval_minutes", self.memory_interval_m)
touchmenu_instance:updateItems()
UIManager:scheduleIn(self.memory_interval * 60, self.checkHighMemoryUsage)
UIManager:scheduleIn(self.memory_interval_m * 60, self.checkHighMemoryUsage)
end,
})
end,

@ -13,11 +13,11 @@ local LuaData = require("luadata")
local MultiConfirmBox = require("ui/widget/multiconfirmbox")
local NetworkMgr = require("ui/network/manager")
local SortWidget = require("ui/widget/sortwidget")
local TimeVal = require("ui/timeval")
local Trapper = require("ui/trapper")
local UIManager = require("ui/uimanager")
local ffiUtil = require("ffi/util")
local logger = require("logger")
local time = require("ui/time")
local util = require("util")
local _ = require("gettext")
local T = ffiUtil.template
@ -111,7 +111,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, usec = 0 }
self.quick_dismiss_before_delay = time.s(3)
-- Gather info about available dictionaries
if not available_ifos then
@ -931,9 +931,10 @@ function ReaderDictionary:stardictLookup(word, dict_names, fuzzy_search, boxes,
self:showLookupInfo(word, self.lookup_msg_delay)
self._lookup_start_tv = UIManager:getTime()
self._lookup_start_time = UIManager:getTime()
local results = self:startSdcv(word, dict_names, fuzzy_search)
if results and results.lookup_cancelled and TimeVal:now() - self._lookup_start_tv <= self.quick_dismiss_before_delay then
if results and results.lookup_cancelled
and (time.now() - self._lookup_start_time) <= self.quick_dismiss_before_delay then
-- If interrupted quickly just after launch, don't display anything
-- (this might help avoiding refreshes and the need to dismiss
-- after accidental long-press when holding a device).
@ -991,7 +992,8 @@ function ReaderDictionary:showDict(word, results, boxes, link)
self:dismissLookupInfo()
if results and results[1] then
UIManager:show(self.dict_window)
if not results.lookup_cancelled and self._lookup_start_tv and TimeVal:now() - self._lookup_start_tv > self.quick_dismiss_before_delay then
if not results.lookup_cancelled and self._lookup_start_time
and (time.now() - self._lookup_start_time) > self.quick_dismiss_before_delay then
-- If the search took more than a few seconds to be done, discard
-- queued and coming up events to avoid a voluntary dismissal
-- (because the user felt the result would not come) to kill the

@ -7,7 +7,6 @@ local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local Notification = require("ui/widget/notification")
local TextViewer = require("ui/widget/textviewer")
local TimeVal = require("ui/timeval")
local Translator = require("ui/translator")
local UIManager = require("ui/uimanager")
local dbg = require("dbg")
@ -15,6 +14,7 @@ local logger = require("logger")
local util = require("util")
local Size = require("ui/size")
local ffiUtil = require("ffi/util")
local time = require("ui/time")
local _ = require("gettext")
local C_ = _.pgettext
local T = require("ffi/util").template
@ -51,7 +51,7 @@ function ReaderHighlight:init()
self._start_indicator_highlight = false
self._current_indicator_pos = nil
self._previous_indicator_pos = nil
self._last_indicator_move_args = {dx = 0, dy = 0, distance = 0, time = TimeVal:now()}
self._last_indicator_move_args = {dx = 0, dy = 0, distance = 0, time = time:now()}
if Device:hasDPad() then
-- Used for text selection with dpad/keys
@ -897,9 +897,9 @@ dbg:guard(ReaderHighlight, "onShowHighlightMenu",
function ReaderHighlight:_resetHoldTimer(clear)
if clear then
self.hold_last_tv = nil
self.hold_last_time = nil
else
self.hold_last_tv = UIManager:getTime()
self.hold_last_time = UIManager:getTime()
end
end
@ -1423,14 +1423,14 @@ function ReaderHighlight:onHoldRelease()
end
local long_final_hold = false
if self.hold_last_tv then
local hold_duration = TimeVal:now() - self.hold_last_tv
local long_hold_threshold = G_reader_settings:readSetting("highlight_long_hold_threshold", 3)
if hold_duration > TimeVal:new{ sec = long_hold_threshold, usec = 0 } then
if self.hold_last_time then
local hold_duration = time.now() - self.hold_last_time
local long_hold_threshold_s = G_reader_settings:readSetting("highlight_long_hold_threshold", 3) -- seconds
if hold_duration > time.s(long_hold_threshold_s) then
-- We stayed 3 seconds before release without updating selection
long_final_hold = true
end
self.hold_last_tv = nil
self.hold_last_time = nil
end
if self.is_word_selection then -- single-word selection
if long_final_hold or G_reader_settings:isTrue("highlight_action_on_single_word") then
@ -1947,14 +1947,14 @@ function ReaderHighlight:onMoveHighlightIndicator(args)
rect.x = rect.x + quick_move_distance_dx * dx
rect.y = rect.y + quick_move_distance_dy * dy
else
local now = TimeVal:now()
local now = time:now()
if dx == self._last_indicator_move_args.dx and dy == self._last_indicator_move_args.dy then
local diff = now - self._last_indicator_move_args.time
-- if press same arrow key in 1 second, speed up
-- double press: 4 single move distances, usually move to next word or line
-- triple press: 16 single distances, usually skip several words or lines
-- quadruple press: 54 single distances, almost move to screen edge
if diff < TimeVal:new{ sec = 1, usec = 0 } then
if diff < time.s(1) then
move_distance = self._last_indicator_move_args.distance * 4
end
end
@ -1998,7 +1998,7 @@ function ReaderHighlight:_createHighlightGesture(gesture)
return {
ges = gesture,
pos = point,
time = TimeVal:realtime(),
time = time.realtime(),
}
end

@ -5,16 +5,15 @@ local Geom = require("ui/geometry")
local InputContainer = require("ui/widget/container/inputcontainer")
local Math = require("optmath")
local ReaderZooming = require("apps/reader/modules/readerzooming")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local bit = require("bit")
local logger = require("logger")
local util = require("util")
local time = require("ui/time")
local _ = require("gettext")
local Input = Device.input
local Screen = Device.screen
local function copyPageState(page_state)
return {
page = page_state.page,
@ -98,7 +97,7 @@ function ReaderPaging:init()
{"0"}, doc = "go to end", event = "GotoPercent", args = 100,
}
end
self.pan_interval = TimeVal:new{ usec = 1000000 / self.pan_rate }
self.pan_interval = time.s(1 / self.pan_rate)
self.number_of_pages = self.ui.document.info.number_of_pages
end
@ -321,9 +320,9 @@ function ReaderPaging:bookmarkFlipping(flipping_page, flipping_ges)
UIManager:setDirty(self.view.dialog, "partial")
end
function ReaderPaging:onScrollSettingsUpdated(scroll_method, inertial_scroll_enabled, scroll_activation_delay)
function ReaderPaging:onScrollSettingsUpdated(scroll_method, inertial_scroll_enabled, scroll_activation_delay_ms)
self.scroll_method = scroll_method
self.scroll_activation_delay = TimeVal:new{ usec = scroll_activation_delay * 1000 }
self.scroll_activation_delay = time.ms(scroll_activation_delay_ms)
if inertial_scroll_enabled then
self.ui.scrolling:setInertialScrollCallbacks(
function(distance) -- do_scroll_callback
@ -408,7 +407,7 @@ function ReaderPaging:onPan(_, ges)
self._pan_has_scrolled = false
self._pan_prev_relative_y = 0
self._pan_to_scroll_later = 0
self._pan_real_last_time = TimeVal.zero
self._pan_real_last_time = 0
if ges.mousewheel_direction then
self._pan_activation_time = false
else

@ -7,10 +7,10 @@ local InputContainer = require("ui/widget/container/inputcontainer")
local ProgressWidget = require("ui/widget/progresswidget")
local ReaderPanning = require("apps/reader/modules/readerpanning")
local Size = require("ui/size")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local bit = require("bit")
local logger = require("logger")
local time = require("ui/time")
local _ = require("gettext")
local Screen = Device.screen
local T = require("ffi/util").template
@ -112,7 +112,7 @@ function ReaderRolling:init()
{"0"}, doc = "go to end", event = "GotoPercent", args = 100,
}
end
self.pan_interval = TimeVal:new{ usec = 1000000 / self.pan_rate }
self.pan_interval = time.s(1 / self.pan_rate)
table.insert(self.ui.postInitCallback, function()
self.rendering_hash = self.ui.document:getDocumentRenderingHash()
@ -403,9 +403,9 @@ function ReaderRolling:getLastPercent()
end
end
function ReaderRolling:onScrollSettingsUpdated(scroll_method, inertial_scroll_enabled, scroll_activation_delay)
function ReaderRolling:onScrollSettingsUpdated(scroll_method, inertial_scroll_enabled, scroll_activation_delay_ms)
self.scroll_method = scroll_method
self.scroll_activation_delay = TimeVal:new{ usec = scroll_activation_delay * 1000 }
self.scroll_activation_delay = time.ms(scroll_activation_delay_ms)
if inertial_scroll_enabled then
self.ui.scrolling:setInertialScrollCallbacks(
function(distance) -- do_scroll_callback
@ -478,7 +478,7 @@ function ReaderRolling:onPan(_, ges)
self._pan_has_scrolled = false
self._pan_prev_relative_y = 0
self._pan_to_scroll_later = 0
self._pan_real_last_time = TimeVal.zero
self._pan_real_last_time = 0
if ges.mousewheel_direction then
self._pan_activation_time = false
else
@ -1173,8 +1173,8 @@ function ReaderRolling:handleEngineCallback(ev, ...)
-- ignore other events
end
local ENGINE_PROGRESS_INITIAL_DELAY = TimeVal:new{ sec = 2, usec = 0 }
local ENGINE_PROGRESS_UPDATE_DELAY = TimeVal:new{ sec = 0, usec = 500000 }
local ENGINE_PROGRESS_INITIAL_DELAY = time.s(2)
local ENGINE_PROGRESS_UPDATE_DELAY = time.ms(500)
function ReaderRolling:showEngineProgress(percent)
if G_reader_settings and G_reader_settings:isFalse("cre_show_progress") then
@ -1186,7 +1186,7 @@ function ReaderRolling:showEngineProgress(percent)
end
if percent then
local now = TimeVal:now()
local now = time.now()
if self.engine_progress_update_not_before and now < self.engine_progress_update_not_before then
return
end

@ -1,9 +1,9 @@
local Device = require("device")
local Event = require("ui/event")
local InputContainer = require("ui/widget/container/inputcontainer")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local logger = require("logger")
local time = require("ui/time")
local _ = require("gettext")
local T = require("ffi/util").template
local Screen = Device.screen
@ -22,7 +22,7 @@ local ReaderScrolling = InputContainer:new{
SCROLL_METHOD_ON_RELEASE = SCROLL_METHOD_ON_RELEASE,
scroll_method = SCROLL_METHOD_CLASSIC,
scroll_activation_delay = 0, -- 0 ms
scroll_activation_delay_ms = 0, -- 0 ms
inertial_scroll = false,
pan_rate = 30, -- default 30 ops, will be adjusted in readerui
@ -30,7 +30,7 @@ local ReaderScrolling = InputContainer:new{
-- go at ending scrolling soon when we reach steps smaller than this
end_scroll_dist = Screen:scaleBySize(10),
-- no inertial scrolling if 300ms pause without any movement before release
pause_before_release_cancel_duration = TimeVal:new{ sec = 0, usec = 300000 },
pause_before_release_cancel_duration = time.ms(300),
-- Callbacks to be updated by readerrolling or readerpaging
_do_scroll_callback = function(distance) return false end,
@ -66,14 +66,14 @@ function ReaderScrolling:init()
-- we miss a first touch event.
-- We can keep it obsolete, which will result in a long
-- duration and a small/zero velocity that won't hurt.
self._last_manual_scroll_timev = TimeVal.zero
self._last_manual_scroll_timev = 0
self:_setupAction()
end
self.ui.menu:registerToMainMenu(self)
end
function ReaderScrolling:getDefaultScrollActivationDelay()
function ReaderScrolling:getDefaultScrollActivationDelay_ms()
if (self.ui.gestures and self.ui.gestures.multiswipes_enabled)
or G_reader_settings:readSetting("activate_menu") ~= "tap" then
-- If swipes to show menu or multiswipes are enabled, higher default
@ -143,11 +143,11 @@ This is interesting on eInk if you only pan to better adjust page vertical posit
},
{
text_func = function()
return T(_("Activation delay: %1 ms"), self.scroll_activation_delay)
return T(_("Activation delay: %1 ms"), self.scroll_activation_delay_ms)
end,
keep_menu_open = true,
callback = function(touchmenu_instance)
local scroll_activation_delay_default = self:getDefaultScrollActivationDelay()
local scroll_activation_delay_default_ms = self:getDefaultScrollActivationDelay_ms()
local SpinWidget = require("ui/widget/spinwidget")
local widget = SpinWidget:new{
title_text = _("Scroll activation delay"),
@ -155,17 +155,17 @@ This is interesting on eInk if you only pan to better adjust page vertical posit
A delay can be used to avoid scrolling when swipes or multiswipes are intended.
The delay value is in milliseconds and can range from 0 to 2000 (2 seconds).
Default value: %1 ms]]), scroll_activation_delay_default),
Default value: %1 ms]]), scroll_activation_delay_default_ms),
width = math.floor(Screen:getWidth() * 0.75),
value = self.scroll_activation_delay,
value = self.scroll_activation_delay_ms,
value_min = 0,
value_max = 2000,
value_step = 100,
value_hold_step = 500,
ok_text = _("Set delay"),
default_value = scroll_activation_delay_default,
default_value = scroll_activation_delay_default_ms,
callback = function(spin)
self.scroll_activation_delay = spin.value
self.scroll_activation_delay_ms = spin.value
self:applyScrollSettings()
if touchmenu_instance then touchmenu_instance:updateItems() end
end
@ -195,18 +195,18 @@ end
function ReaderScrolling:onReaderReady()
-- We don't know if the gestures plugin is loaded in :init(), but we know it here
self.scroll_activation_delay = G_reader_settings:readSetting("scroll_activation_delay")
or self:getDefaultScrollActivationDelay()
self.scroll_activation_delay_ms = G_reader_settings:readSetting("scroll_activation_delay")
or self:getDefaultScrollActivationDelay_ms()
self:applyScrollSettings()
end
function ReaderScrolling:applyScrollSettings()
G_reader_settings:saveSetting("scroll_method", self.scroll_method)
G_reader_settings:saveSetting("inertial_scroll", self.inertial_scroll)
if self.scroll_activation_delay == self:getDefaultScrollActivationDelay() then
if self.scroll_activation_delay_ms == self:getDefaultScrollActivationDelay_ms() then
G_reader_settings:delSetting("scroll_activation_delay")
else
G_reader_settings:saveSetting("scroll_activation_delay", self.scroll_activation_delay)
G_reader_settings:saveSetting("scroll_activation_delay", self.scroll_activation_delay_ms)
end
if self.scroll_method == self.SCROLL_METHOD_CLASSIC then
self._inertial_scroll_enabled = self.inertial_scroll
@ -215,7 +215,7 @@ function ReaderScrolling:applyScrollSettings()
end
self:setupTouchZones()
self.ui:handleEvent(Event:new("ScrollSettingsUpdated", self.scroll_method,
self._inertial_scroll_enabled, self.scroll_activation_delay))
self._inertial_scroll_enabled, self.scroll_activation_delay_ms))
end
function ReaderScrolling:setupTouchZones()
@ -339,14 +339,14 @@ function ReaderScrolling:_setupAction()
self._last_manual_scroll_dy = 0
return false
end
if self._last_manual_scroll_duration:isZero() or self._last_manual_scroll_dy == 0 then
if self._last_manual_scroll_duration == 0 or self._last_manual_scroll_dy == 0 then
return false
end
-- Initial velocity is the one of the last pan scroll given to accountManualScroll()
local delay = self._last_manual_scroll_duration:tousecs()
if delay < 1 then delay = 1 end -- safety check
self._velocity = self._last_manual_scroll_dy * 1000000 / delay
local delay_us = time.to_us(self._last_manual_scroll_duration)
if delay_us < 1 then delay_us = 1 end -- safety check
self._velocity = self._last_manual_scroll_dy * time.s(1 / delay_us)
self._last_manual_scroll_dy = 0
self._inertial_scroll_action_scheduled = true

@ -314,15 +314,15 @@ function ReaderThumbnail:startTileGeneration(request)
local scale_factor = math.min(request.width / bb:getWidth(), request.height / bb:getHeight())
local target_w = math.floor(bb:getWidth() * scale_factor)
local target_h = math.floor(bb:getHeight() * scale_factor)
-- local TimeVal = require("ui/timeval")
-- local start_tv = TimeVal:now()
-- local time = require("ui/time")
-- local start_time = time.now()
local tile = TileCacheItem:new{
bb = RenderImage:scaleBlitBuffer(bb, target_w, target_h, true),
pageno = request.page,
}
tile.size = tonumber(tile.bb.stride) * tile.bb.h
-- logger.info("tile size", tile.bb.w, tile.bb.h, "=>", tile.size)
-- logger.info(string.format(" scaling took %.3f seconds, %d bpp", TimeVal:getDuration(start_tv), tile.bb:getBpp()))
-- logger.info(string.format(" scaling took %.3f seconds, %d bpp", time.to_s(time.since(start_time)), tile.bb:getBpp()))
-- bb:free() -- no need to spend time freeing, we're dying soon anyway!
ffiutil.writeToFD(child_write_fd, self.codec.serialize(tile:totable()), true)
@ -343,8 +343,8 @@ function ReaderThumbnail:checkTileGeneration(request)
local subprocess_done = ffiutil.isSubProcessDone(pid)
logger.dbg("subprocess_done:", subprocess_done, " stuff_to_read:", stuff_to_read)
if stuff_to_read then
-- local TimeVal = require("ui/timeval")
-- local start_tv = TimeVal:now()
-- local time = require("ui/time")
-- local start_time = time.now()
local result, err = self.codec.deserialize(ffiutil.readAllFromFD(parent_read_fd))
if result then
local tile = TileCacheItem:new{}
@ -361,7 +361,7 @@ function ReaderThumbnail:checkTileGeneration(request)
request.when_generated_callback(nil, request.batch_id, true)
end
end
-- logger.info(string.format(" parsing result from subprocess took %.3f seconds", TimeVal:getDuration(start_tv)))
-- logger.info(string.format(" parsing result from subprocess took %.3f seconds", time.to_s(time.since(start_time))))
if not subprocess_done then
table.insert(pids_to_collect, pid)
return false, true

@ -14,12 +14,12 @@ local OverlapGroup = require("ui/widget/overlapgroup")
local ReaderDogear = require("apps/reader/modules/readerdogear")
local ReaderFlipping = require("apps/reader/modules/readerflipping")
local ReaderFooter = require("apps/reader/modules/readerfooter")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local dbg = require("dbg")
local logger = require("logger")
local optionsutil = require("ui/data/optionsutil")
local Size = require("ui/size")
local time = require("ui/time")
local _ = require("gettext")
local Screen = Device.screen
local T = require("ffi/util").template
@ -76,7 +76,7 @@ local ReaderView = OverlapGroup:extend{
-- in flipping state
flipping_visible = false,
-- to ensure periodic flush of settings
settings_last_save_btv = nil,
settings_last_save_time = nil,
-- might be directly updated by readerpaging/readerrolling when
-- they handle some panning/scrolling, to request "fast" refreshes
currently_scrolling = false,
@ -1038,17 +1038,17 @@ end
function ReaderView:onReaderReady()
self.ui.doc_settings:delSetting("docsettings_reset_done")
self.settings_last_save_btv = UIManager:getElapsedTimeSinceBoot()
self.settings_last_save_time = UIManager:getElapsedTimeSinceBoot()
end
function ReaderView:onResume()
-- As settings were saved on suspend, reset this on resume,
-- as there's no need for a possibly immediate save.
self.settings_last_save_btv = UIManager:getElapsedTimeSinceBoot()
self.settings_last_save_time = UIManager:getElapsedTimeSinceBoot()
end
function ReaderView:checkAutoSaveSettings()
if not self.settings_last_save_btv then -- reader not yet ready
if not self.settings_last_save_time then -- reader not yet ready
return
end
if G_reader_settings:nilOrFalse("auto_save_settings_interval_minutes") then
@ -1056,11 +1056,11 @@ function ReaderView:checkAutoSaveSettings()
return
end
local interval = G_reader_settings:readSetting("auto_save_settings_interval_minutes")
interval = TimeVal:new{ sec = interval*60, usec = 0 }
local now_btv = UIManager:getElapsedTimeSinceBoot()
if now_btv - self.settings_last_save_btv >= interval then
self.settings_last_save_btv = now_btv
local interval_m = G_reader_settings:readSetting("auto_save_settings_interval_minutes")
local interval = time.s(interval_m * 60)
local now = UIManager:getElapsedTimeSinceBoot()
if now - self.settings_last_save_time >= interval then
self.settings_last_save_time = now
-- I/O, delay until after the pageturn
UIManager:tickAfterNext(function()
self.ui:saveSettings()

@ -56,11 +56,11 @@ local ReaderWikipedia = require("apps/reader/modules/readerwikipedia")
local ReaderZooming = require("apps/reader/modules/readerzooming")
local Screenshoter = require("ui/widget/screenshoter")
local SettingsMigration = require("ui/data/settings_migration")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local ffiUtil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local time = require("ui/time")
local util = require("util")
local _ = require("gettext")
local Screen = require("device").screen
@ -290,19 +290,19 @@ function ReaderUI:init()
end
-- make sure we render document first before calling any callback
self:registerPostInitCallback(function()
local start_tv = TimeVal:now()
local start_time = time.now()
if not self.document:loadDocument() then
self:dealWithLoadDocumentFailure()
end
logger.dbg(string.format(" loading took %.3f seconds", TimeVal:getDuration(start_tv)))
logger.dbg(string.format(" loading took %.3f seconds", time.to_s(time.since(start_time))))
-- used to read additional settings after the document has been
-- loaded (but not rendered yet)
self:handleEvent(Event:new("PreRenderDocument", self.doc_settings))
start_tv = TimeVal:now()
start_time = time.now()
self.document:render()
logger.dbg(string.format(" rendering took %.3f seconds", TimeVal:getDuration(start_tv)))
logger.dbg(string.format(" rendering took %.3f seconds", time.to_s(time.since(start_time))))
-- Uncomment to output the built DOM (for debugging)
-- logger.dbg(self.document:getHTMLFromXPointer(".0", 0x6830))

@ -6,7 +6,6 @@ This module defines stubs for common methods.
local DataStorage = require("datastorage")
local Geom = require("ui/geometry")
local TimeVal = require("ui/timeval")
local logger = require("logger")
local util = require("util")
local _ = require("gettext")
@ -71,10 +70,10 @@ local Device = {
canSuspend = yes,
canStandby = no,
canPowerSaveWhileCharging = no,
total_standby_tv = TimeVal.zero, -- total time spent in standby
last_standby_tv = TimeVal.zero,
total_suspend_tv = TimeVal.zero, -- total time spent in suspend
last_suspend_tv = TimeVal.zero,
total_standby_time = 0, -- total time spent in standby
last_standby_time = 0,
total_suspend_time = 0, -- total time spent in suspend
last_suspend_time = 0,
canReboot = no,
canPowerOff = no,
canAssociateFileExtensions = no,

@ -1,7 +1,7 @@
local UIManager -- will be updated when available
local Math = require("optmath")
local TimeVal = require("ui/timeval")
local logger = require("logger")
local time = require("ui/time")
local BasePowerD = {
fl_min = 0, -- min frontlight intensity
fl_max = 10, -- max frontlight intensity
@ -13,8 +13,8 @@ local BasePowerD = {
aux_batt_capacity = 0, -- auxiliary battery capacity
device = nil, -- device object
last_capacity_pull_time = TimeVal:new{ sec = -61, usec = 0}, -- timestamp of last pull
last_aux_capacity_pull_time = TimeVal:new{ sec = -61, usec = 0}, -- timestamp of last pull
last_capacity_pull_time = time.s(-61), -- timestamp of last pull
last_aux_capacity_pull_time = time.s(-61), -- timestamp of last pull
is_fl_on = false, -- whether the frontlight is on
}
@ -216,17 +216,17 @@ end
function BasePowerD:getCapacity()
-- BasePowerD is loaded before UIManager.
-- Nothing *currently* calls this before UIManager is actually loaded, but future-proof this anyway.
local now_btv
local now
if UIManager then
now_btv = UIManager:getElapsedTimeSinceBoot()
now = UIManager:getElapsedTimeSinceBoot()
else
-- Add time the device was in standby and suspend
now_btv = TimeVal:now() + self.device.total_standby_tv + self.device.total_suspend_tv
now = time.now() + self.device.total_standby_time + self.device.total_suspend_time
end
if (now_btv - self.last_capacity_pull_time):tonumber() >= 60 then
if now - self.last_capacity_pull_time >= time.s(60) then
self.batt_capacity = self:getCapacityHW()
self.last_capacity_pull_time = now_btv
self.last_capacity_pull_time = now
end
return self.batt_capacity
end
@ -240,29 +240,29 @@ function BasePowerD:isCharged()
end
function BasePowerD:getAuxCapacity()
local now_btv
local now
if UIManager then
now_btv = UIManager:getElapsedTimeSinceBoot()
now = UIManager:getElapsedTimeSinceBoot()
else
-- Add time the device was in standby and suspend
now_btv = TimeVal:now() + self.device.total_standby_tv + self.device.total_suspend_tv
now = time.now() + self.device.total_standby_time + self.device.total_suspend_time
end
if (now_btv - self.last_aux_capacity_pull_time):tonumber() >= 60 then
if now - self.last_aux_capacity_pull_time >= time.s(60) then
local aux_batt_capa = self:getAuxCapacityHW()
-- If the read failed, don't update our cache, and retry next time.
if aux_batt_capa then
self.aux_batt_capacity = aux_batt_capa
self.last_aux_capacity_pull_time = now_btv
self.last_aux_capacity_pull_time = now
end
end
return self.aux_batt_capacity
end
function BasePowerD:invalidateCapacityCache()
self.last_capacity_pull_time = TimeVal:new{ sec = -61, usec = 0}
self.last_aux_capacity_pull_time = TimeVal:new{ sec = -61, usec = 0}
self.last_capacity_pull_time = time.s(-61)
self.last_aux_capacity_pull_time = self.last_capacity_pull_time
end
function BasePowerD:isAuxCharging()

@ -33,7 +33,7 @@ a touch event should have following format:
id = 46,
x = 0,
y = 1,
timev = TimeVal:new{...},
timev = time.s(123.23),
}
Don't confuse `tev` with raw evs from kernel, `tev` is built according to ev.
@ -43,8 +43,8 @@ detection result when you feed a touch release event to it.
--]]
local Geom = require("ui/geometry")
local TimeVal = require("ui/timeval")
local logger = require("logger")
local time = require("ui/time")
local util = require("util")
-- We're going to need some clockid_t constants
@ -52,33 +52,22 @@ local ffi = require("ffi")
local C = ffi.C
require("ffi/posix_h")
-- default values (all the time parameters are in microseconds)
local TAP_INTERVAL = 0 * 1000
local DOUBLE_TAP_INTERVAL = 300 * 1000
local TWO_FINGER_TAP_DURATION = 300 * 1000
local HOLD_INTERVAL = 500 * 1000
local SWIPE_INTERVAL = 900 * 1000
-- current values
local ges_tap_interval = G_reader_settings:readSetting("ges_tap_interval") or TAP_INTERVAL
ges_tap_interval = TimeVal:new{ usec = ges_tap_interval }
local ges_double_tap_interval = G_reader_settings:readSetting("ges_double_tap_interval") or DOUBLE_TAP_INTERVAL
ges_double_tap_interval = TimeVal:new{ usec = ges_double_tap_interval }
local ges_two_finger_tap_duration = G_reader_settings:readSetting("ges_two_finger_tap_duration") or TWO_FINGER_TAP_DURATION
ges_two_finger_tap_duration = TimeVal:new{ usec = ges_two_finger_tap_duration }
local ges_hold_interval = G_reader_settings:readSetting("ges_hold_interval") or HOLD_INTERVAL
ges_hold_interval = TimeVal:new{ usec = ges_hold_interval }
local ges_swipe_interval = G_reader_settings:readSetting("ges_swipe_interval") or SWIPE_INTERVAL
ges_swipe_interval = TimeVal:new{ usec = ges_swipe_interval }
-- default values (time parameters are in milliseconds (ms))
local TAP_INTERVAL_MS = 0
local DOUBLE_TAP_INTERVAL_MS = 300
local TWO_FINGER_TAP_DURATION_MS = 300
local HOLD_INTERVAL_MS = 500
local SWIPE_INTERVAL_MS = 900
local GestureDetector = {
-- must be initialized with the Input singleton class
input = nil,
-- default values (accessed for display by plugins/gestures.koplugin)
TAP_INTERVAL = TAP_INTERVAL,
DOUBLE_TAP_INTERVAL = DOUBLE_TAP_INTERVAL,
TWO_FINGER_TAP_DURATION = TWO_FINGER_TAP_DURATION,
HOLD_INTERVAL = HOLD_INTERVAL,
SWIPE_INTERVAL = SWIPE_INTERVAL,
TAP_INTERVAL_MS = TAP_INTERVAL_MS,
DOUBLE_TAP_INTERVAL_MS = DOUBLE_TAP_INTERVAL_MS,
TWO_FINGER_TAP_DURATION_MS = TWO_FINGER_TAP_DURATION_MS,
HOLD_INTERVAL_MS = HOLD_INTERVAL_MS,
SWIPE_INTERVAL_MS = SWIPE_INTERVAL_MS,
-- pinch/spread direction table
DIRECTION_TABLE = {
east = "horizontal",
@ -106,6 +95,14 @@ local GestureDetector = {
last_taps = {},
-- for timestamp clocksource detection
clock_id = nil,
-- current values
ges_tap_interval = time.ms(G_reader_settings:readSetting("ges_tap_interval_ms") or TAP_INTERVAL_MS),
ges_double_tap_interval = time.ms(G_reader_settings:readSetting("ges_double_tap_interval_ms")
or DOUBLE_TAP_INTERVAL_MS),
ges_two_finger_tap_duration = time.ms(G_reader_settings:readSetting("ges_two_finger_tap_duration_ms")
or TWO_FINGER_TAP_DURATION_MS),
ges_hold_interval = time.ms(G_reader_settings:readSetting("ges_hold_interval_ms") or HOLD_INTERVAL_MS),
ges_swipe_interval = time.ms(G_reader_settings:readSetting("ges_swipe_interval_ms") or SWIPE_INTERVAL_MS),
}
function GestureDetector:new(o)
@ -167,38 +164,38 @@ tap2 is the later tap
function GestureDetector:isTapBounce(tap1, tap2, interval)
-- NOTE: If time went backwards, make the delta infinite to avoid misdetections,
-- 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.huge
local time_diff = tap2.timev - tap1.timev
if time_diff < 0 then
time_diff = time.huge
end
return (
math.abs(tap1.x - tap2.x) < self.SINGLE_TAP_BOUNCE_DISTANCE and
math.abs(tap1.y - tap2.y) < self.SINGLE_TAP_BOUNCE_DISTANCE and
tv_diff < interval
time_diff < interval
)
end
function GestureDetector:isDoubleTap(tap1, tap2)
local tv_diff = tap2.timev - tap1.timev
if not tv_diff:isPositive() then
tv_diff = TimeVal.huge
local time_diff = tap2.timev - tap1.timev
if time_diff < 0 then
time_diff = time.huge
end
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 < ges_double_tap_interval
time_diff < self.ges_double_tap_interval
)
end
-- Takes TimeVals as input, not a tev
function GestureDetector:isHold(t1, t2)
local tv_diff = t2 - t1
if not tv_diff:isPositive() then
tv_diff = TimeVal.zero
-- Takes times as input, not a tev
function GestureDetector:isHold(time1, time2)
local time_diff = time2 - time1
if time_diff < 0 then
time_diff = 0
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 ;).
return tv_diff >= ges_hold_interval
return time_diff >= self.ges_hold_interval
end
function GestureDetector:isTwoFingerTap()
@ -212,21 +209,21 @@ function GestureDetector:isTwoFingerTap()
local x_diff1 = math.abs(self.last_tevs[s2].x - self.first_tevs[s2].x)
local y_diff0 = math.abs(self.last_tevs[s1].y - self.first_tevs[s1].y)
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.huge
local time_diff0 = self.last_tevs[s1].timev - self.first_tevs[s1].timev
if time_diff0 < 0 then
time_diff0 = time.huge
end
local tv_diff1 = self.last_tevs[s2].timev - self.first_tevs[s2].timev
if not tv_diff1:isPositive() then
tv_diff1 = TimeVal.huge
local time_diff1 = self.last_tevs[s2].timev - self.first_tevs[s2].timev
if time_diff1 < 0 then
time_diff1 = time.huge
end
return (
x_diff0 < self.TWO_FINGER_TAP_REGION and
x_diff1 < self.TWO_FINGER_TAP_REGION and
y_diff0 < self.TWO_FINGER_TAP_REGION and
y_diff1 < self.TWO_FINGER_TAP_REGION and
tv_diff0 < ges_two_finger_tap_duration and
tv_diff1 < ges_two_finger_tap_duration
time_diff0 < self.ges_two_finger_tap_duration and
time_diff1 < self.ges_two_finger_tap_duration
)
end
@ -264,11 +261,11 @@ end
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.huge
local time_diff = self.last_tevs[slot].timev - self.first_tevs[slot].timev
if time_diff < 0 then
time_diff = time.huge
end
if tv_diff < ges_swipe_interval then
if time_diff < self.ges_swipe_interval then
local x_diff = self.last_tevs[slot].x - self.first_tevs[slot].x
local y_diff = self.last_tevs[slot].y - self.first_tevs[slot].y
if x_diff ~= 0 or y_diff ~= 0 then
@ -307,34 +304,6 @@ function GestureDetector:clearState(slot)
self.input:clearTimeout(slot, "hold")
end
function GestureDetector:setNewInterval(type, interval)
if type == "ges_tap_interval" then
ges_tap_interval = TimeVal:new{ usec = interval }
elseif type == "ges_double_tap_interval" then
ges_double_tap_interval = TimeVal:new{ usec = interval }
elseif type == "ges_two_finger_tap_duration" then
ges_two_finger_tap_duration = TimeVal:new{ usec = interval }
elseif type == "ges_hold_interval" then
ges_hold_interval = TimeVal:new{ usec = interval }
elseif type == "ges_swipe_interval" then
ges_swipe_interval = TimeVal:new{ usec = interval }
end
end
function GestureDetector:getInterval(type)
if type == "ges_tap_interval" then
return ges_tap_interval:tousecs()
elseif type == "ges_double_tap_interval" then
return ges_double_tap_interval:tousecs()
elseif type == "ges_two_finger_tap_duration" then
return ges_two_finger_tap_duration:tousecs()
elseif type == "ges_hold_interval" then
return ges_hold_interval:tousecs()
elseif type == "ges_swipe_interval" then
return ges_swipe_interval:tousecs()
end
end
function GestureDetector:clearStates()
for k, _ in pairs(self.states) do
self:clearState(k)
@ -371,10 +340,10 @@ Attempts to figure out which clock source tap events are using...
function GestureDetector:probeClockSource(timev)
-- We'll check if that timestamp is +/- 2.5s away from the three potential clock sources supported by evdev.
-- We have bigger issues than this if we're parsing events more than 3s late ;).
local threshold = TimeVal:new{ sec = 2, usec = 500000 }
local threshold = time.s(2) + time.ms(500)
-- Start w/ REALTIME, because it's the easiest to detect ;).
local realtime = TimeVal:realtime_coarse()
local realtime = time.realtime_coarse()
-- clock-threshold <= timev <= clock+threshold
if timev >= realtime - threshold and timev <= realtime + threshold then
self.clock_id = C.CLOCK_REALTIME
@ -383,7 +352,7 @@ function GestureDetector:probeClockSource(timev)
end
-- Then MONOTONIC, as it's (hopefully) more common than BOOTTIME (and also guaranteed to be an usable clock source)
local monotonic = TimeVal:monotonic_coarse()
local monotonic = time.monotonic_coarse()
if timev >= monotonic - threshold and timev <= monotonic + threshold then
self.clock_id = C.CLOCK_MONOTONIC
logger.info("GestureDetector:probeClockSource: Touch event timestamps appear to use CLOCK_MONOTONIC")
@ -391,9 +360,9 @@ function GestureDetector:probeClockSource(timev)
end
-- Finally, BOOTTIME
local boottime = TimeVal:boottime()
local boottime = time.boottime()
-- NOTE: It was implemented in Linux 2.6.39, so, reject 0, which would mean it's unsupported...
if not boottime:isZero() and timev >= boottime - threshold and timev <= boottime + threshold then
if not boottime == 0 and timev >= boottime - threshold and timev <= boottime + threshold then
self.clock_id = C.CLOCK_BOOTTIME
logger.info("GestureDetector:probeClockSource: Touch event timestamps appear to use CLOCK_BOOTTIME")
return
@ -403,10 +372,10 @@ function GestureDetector:probeClockSource(timev)
self.clock_id = -1
logger.info("GestureDetector:probeClockSource: Touch event clock source detection was inconclusive")
-- Print all all the gory details in debug mode when this happens...
logger.dbg("Input frame :", timev:tonumber())
logger.dbg("CLOCK_REALTIME :", realtime:tonumber())
logger.dbg("CLOCK_MONOTONIC:", monotonic:tonumber())
logger.dbg("CLOCK_BOOTTIME :", boottime:tonumber())
logger.dbg("Input frame :", time.format_time(timev))
logger.dbg("CLOCK_REALTIME :", time.format_time(realtime))
logger.dbg("CLOCK_MONOTONIC:", time.format_time(monotonic))
logger.dbg("CLOCK_BOOTTIME :", time.format_time(boottime))
end
function GestureDetector:getClockSource()
@ -498,10 +467,10 @@ function GestureDetector:handleDoubleTap(tev)
}
-- Tap interval / bounce detection may be tweaked by widget (i.e. VirtualKeyboard)
local tap_interval = self.input.tap_interval_override or ges_tap_interval
local tap_interval = self.input.tap_interval_override or self.ges_tap_interval
-- We do tap bounce detection even when double tap is enabled (so, double tap
-- is triggered when: ges_tap_interval <= delay < ges_double_tap_interval)
if not tap_interval:isZero() and self.last_taps[slot] ~= nil and self:isTapBounce(self.last_taps[slot], cur_tap, tap_interval) then
if tap_interval ~= 0 and self.last_taps[slot] ~= nil and self:isTapBounce(self.last_taps[slot], cur_tap, tap_interval) then
logger.dbg("tap bounce detected in slot", slot, ": ignored")
-- Simply ignore it, and clear state as this is the end of a touch event
-- (this doesn't clear self.last_taps[slot], so a 3rd tap can be detected
@ -547,7 +516,7 @@ function GestureDetector:handleDoubleTap(tev)
logger.dbg("single tap detected in slot", slot, ges_ev.pos)
return ges_ev
end
end, tev.timev, ges_double_tap_interval)
end, tev.timev, self.ges_double_tap_interval)
-- we are already at the end of touch event
-- so reset the state
self:clearState(slot)
@ -573,7 +542,7 @@ function GestureDetector:handleNonTap(tev)
self.pending_hold_timer[slot] = nil
return self:switchState("holdState", tev, true)
end
end, tev.timev, ges_hold_interval)
end, tev.timev, self.ges_hold_interval)
return {
ges = "touch",
pos = Geom:new{

@ -7,10 +7,10 @@ local DEBUG = require("dbg")
local Event = require("ui/event")
local GestureDetector = require("device/gesturedetector")
local Key = require("device/key")
local TimeVal = require("ui/timeval")
local framebuffer = require("ffi/framebuffer")
local input = require("ffi/input")
local logger = require("logger")
local time = require("ui/time")
local _ = require("gettext")
-- We're going to need a few <linux/input.h> constants...
@ -372,7 +372,7 @@ function Input:setTimeout(slot, ges, cb, origin, delay)
if input.setTimer then
-- If GestureDetector's clock source probing was inconclusive, do this on the UI timescale instead.
if clock_id == -1 then
deadline = TimeVal:now() + delay
deadline = time.now() + delay
clock_id = C.CLOCK_MONOTONIC
else
deadline = origin + delay
@ -380,7 +380,8 @@ function Input:setTimeout(slot, ges, cb, origin, delay)
-- What this does is essentially to ask the kernel to wake us up when the timer expires,
-- instead of ensuring that ourselves via a polling timeout.
-- This ensures perfect accuracy, and allows it to be computed in the event's own timescale.
timerfd = input.setTimer(clock_id, deadline.sec, deadline.usec)
local sec, usec = time.split_s_us(deadline)
timerfd = input.setTimer(clock_id, sec, usec)
end
if timerfd then
-- It worked, tweak the table a bit to make it clear the deadline will be handled by the kernel
@ -395,7 +396,7 @@ function Input:setTimeout(slot, ges, cb, origin, delay)
else
-- Otherwise, fudge it by using a current timestamp in the UI's timescale (MONOTONIC).
-- This isn't the end of the world in practice (c.f., #7415).
deadline = TimeVal:now() + delay
deadline = time.now() + delay
end
item.deadline = deadline
end
@ -709,10 +710,8 @@ 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 ipairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", ev.time)
self:setMtSlot(MTSlot.slot, "timev", time.timeval(ev.time))
end
-- feed ev in all slots to state machine
local touch_ges = self.gesture_detector:feedEvent(self.MTSlots)
@ -773,9 +772,8 @@ 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 ipairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", ev.time)
self:setMtSlot(MTSlot.slot, "timev", time.timeval(ev.time))
end
-- feed ev in all slots to state machine
local touch_ges = self.gesture_detector:feedEvent(self.MTSlots)
@ -809,9 +807,8 @@ 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 ipairs(self.MTSlots) do
self:setMtSlot(MTSlot.slot, "timev", ev.time)
self:setMtSlot(MTSlot.slot, "timev", time.timeval(ev.time))
end
-- feed ev in all slots to state machine
@ -1022,8 +1019,8 @@ end
--- Main event handling.
-- `now` corresponds to UIManager:getTime() (a TimeVal), and it's just been updated by UIManager.
-- `deadline` (a TimeVal) is the absolute deadline imposed by UIManager:handleInput() (a.k.a., our main event loop ^^):
-- `now` corresponds to UIManager:getTime() (an fts time), and it's just been updated by UIManager.
-- `deadline` (an fts time) is the absolute deadline imposed by UIManager:handleInput() (a.k.a., our main event loop ^^):
-- it's either nil (meaning block forever waiting for input), or the earliest UIManager deadline (in most cases, that's the next scheduled task,
-- in much less common cases, that's the earliest of UIManager.INPUT_TIMEOUT (currently, only KOSync ever sets it) or UIManager.ZMQ_TIMEOUT if there are pending ZMQs).
function Input:waitEvent(now, deadline)
@ -1073,18 +1070,19 @@ function Input:waitEvent(now, deadline)
if poll_deadline then
-- If we haven't hit that deadline yet, poll until it expires, otherwise,
-- have select return immediately so that we trip a timeout.
now = now or TimeVal:now()
now = now or time.now()
if poll_deadline > now then
-- Deadline hasn't been blown yet, honor it.
poll_timeout = poll_deadline - now
else
-- We've already blown the deadline: make select return immediately (most likely straight to timeout)
poll_timeout = TimeVal.zero
poll_timeout = 0
end
end
local timerfd
ok, ev, timerfd = input.waitForEvent(poll_timeout and poll_timeout.sec, poll_timeout and poll_timeout.usec)
local sec, usec = time.split_s_us(poll_timeout)
ok, ev, timerfd = input.waitForEvent(sec, usec)
-- We got an actual input event, go and process it
if ok then break end
@ -1102,7 +1100,7 @@ function Input:waitEvent(now, deadline)
-- We're only guaranteed to have blown the timer's deadline
-- when our actual select deadline *was* the timer's!
consume_callback = true
elseif TimeVal:now() >= self.timer_callbacks[1].deadline then
elseif time.now() >= self.timer_callbacks[1].deadline then
-- But if it was a task deadline instead, we to have to check the timer's against the current time,
-- to double-check whether we blew it or not.
consume_callback = true
@ -1144,17 +1142,18 @@ function Input:waitEvent(now, deadline)
-- If UIManager put us on deadline, enforce it, otherwise, block forever.
if deadline then
-- Convert that absolute deadline to value relative to *now*, as we may loop multiple times between UI ticks.
now = now or TimeVal:now()
now = now or time.now()
if deadline > now then
-- Deadline hasn't been blown yet, honor it.
poll_timeout = deadline - now
else
-- Deadline has been blown: make select return immediately.
poll_timeout = TimeVal.zero
poll_timeout = 0
end
end
ok, ev = input.waitForEvent(poll_timeout and poll_timeout.sec, poll_timeout and poll_timeout.usec)
local sec, usec = time.split_s_us(poll_timeout)
ok, ev = input.waitForEvent(sec, usec)
end -- if #timer_callbacks > 0
-- Handle errors

@ -824,14 +824,14 @@ function Kobo:standby(max_duration)
self.wakeup_mgr:addTask(max_duration, standby_alarm)
end
local TimeVal = require("ui/timeval")
local time = require("ui/time")
logger.info("Kobo standby: asking to enter standby . . .")
local standby_time_tv = TimeVal:boottime_or_realtime_coarse()
local standby_time = time.boottime_or_realtime_coarse()
local ret = writeToSys("standby", "/sys/power/state")
self.last_standby_tv = TimeVal:boottime_or_realtime_coarse() - standby_time_tv
self.total_standby_tv = self.total_standby_tv + self.last_standby_tv
self.last_standby_time = time.boottime_or_realtime_coarse() - standby_time
self.total_standby_time = self.total_standby_time + self.last_standby_time
if ret then
logger.info("Kobo standby: zZz zZz zZz zZz... And woke up!")
@ -925,16 +925,16 @@ function Kobo:suspend()
end
--]]
local TimeVal = require("ui/timeval")
local time = require("ui/time")
logger.info("Kobo suspend: asking for a suspend to RAM . . .")
local suspend_time_tv = TimeVal:boottime_or_realtime_coarse()
local suspend_time = time.boottime_or_realtime_coarse()
ret = writeToSys("mem", "/sys/power/state")
-- NOTE: At this point, we *should* be in suspend to RAM, as such,
-- execution should only resume on wakeup...
self.last_suspend_tv = TimeVal:boottime_or_realtime_coarse() - suspend_time_tv
self.total_suspend_tv = self.total_suspend_tv + self.last_suspend_tv
self.last_suspend_time = time.boottime_or_realtime_coarse() - suspend_time
self.total_suspend_time = self.total_suspend_time + self.last_suspend_time
if ret then
logger.info("Kobo suspend: ZzZ ZzZ ZzZ... And woke up!")

@ -1,7 +1,7 @@
local Generic = require("device/generic/device") -- <= look at this file!
local TimeVal = require("ui/timeval")
local PluginShare = require("pluginshare")
local logger = require("logger")
local time = require("ui/time")
local ffi = require("ffi")
local C = ffi.C
require("ffi/linux_input_h")
@ -95,7 +95,7 @@ function Remarkable2:adjustTouchEvent(ev, by)
-- Inject CLOCK_MONOTONIC timestamps at the end of every input frame in order to have consistent gesture detection across input devices.
-- c.f., #7536
if ev.type == C.EV_SYN and ev.code == C.SYN_REPORT then
ev.time = TimeVal:now()
ev.time = time.now()
end
end

@ -168,7 +168,6 @@ function Device:init()
event_map = require("device/sdl/event_map_sdl2"),
handleSdlEv = function(device_input, ev)
local Geom = require("ui/geometry")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
-- SDL events can remain cdata but are almost completely transparent
@ -192,8 +191,6 @@ function Device:init()
w = 0, h = 0,
}
setmetatable(ev.time, TimeVal)
local fake_ges = {
ges = "pan",
distance = 200,

@ -6,13 +6,13 @@ local FontList = require("fontlist")
local Geom = require("ui/geometry")
local RenderImage = require("ui/renderimage")
local Screen = require("device").screen
local TimeVal = require("ui/timeval")
local buffer = require("string.buffer")
local ffi = require("ffi")
local C = ffi.C
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local lru = require("ffi/lru")
local time = require("ui/time")
-- engine can be initialized only once, on first document opened
local engine_initialized = false
@ -694,16 +694,16 @@ function CreDocument:drawCurrentView(target, x, y, rect, pos)
-- We also honor the current smooth scaling setting,
-- as well as the global SW dithering setting.
--local start_tv = TimeVal:now()
--local start_time = time.now()
self._drawn_images_count, self._drawn_images_surface_ratio =
self._document:drawCurrentPage(self.buffer, self.render_color, Screen.night_mode and self._nightmode_images, self._smooth_scaling, Screen.sw_dithering)
--local end_tv = TimeVal:now()
--print(string.format("CreDocument:drawCurrentView: Rendering took %9.3f ms", (end_tv - start_tv):tomsecs()))
--local end_time = time.now()
--print(string.format("CreDocument:drawCurrentView: Rendering took %9.3f ms", time.to_ms(end_time - start_time))
--start_tv = TimeVal:now()
--start = time.now()
target:blitFrom(self.buffer, x, y, 0, 0, rect.w, rect.h)
--end_tv = TimeVal:now()
--print(string.format("CreDocument:drawCurrentView: Blitting took %9.3f ms", (end_tv - start_tv):tomsecs()))
--end_time = time.now()
--print(string.format("CreDocument:drawCurrentView: Blitting took %9.3f ms", time.to_ms(end_time - start_time))
end
function CreDocument:drawCurrentViewByPos(target, x, y, rect, pos)
@ -1457,10 +1457,10 @@ function CreDocument:setupCallCache()
-- cache statistics
self._call_cache_stats = {}
now = function()
return TimeVal:now()
return time.now()
end
addStatMiss = function(name, starttime, not_cached)
local duration = TimeVal:getDuration(starttime)
local duration = time.since(starttime)
if not self._call_cache_stats[name] then
self._call_cache_stats[name] = {0, 0.0, 1, duration, not_cached}
else
@ -1470,7 +1470,7 @@ function CreDocument:setupCallCache()
end
end
addStatHit = function(name, starttime)
local duration = TimeVal:getDuration(starttime)
local duration = time.since(starttime)
if not self._call_cache_stats[name] then
self._call_cache_stats[name] = {1, duration, 0, 0.0}
else

@ -10,16 +10,16 @@
-- able to get there easily.
--------
local TimeVal = require("ui/timeval")
local logger = require("logger")
local util = require("util")
local time = require("ui/time")
local _ = require("gettext")
local N_ = _.ngettext
local T = require("ffi/util").template
local K = require("frontend/ui/data/keyboardlayouts/ja_keyboard_keys")
local DEFAULT_KEITAI_TAP_INTERVAL = 2
local DEFAULT_KEITAI_TAP_INTERVAL_S = 2
-- "Keitai input" is an input mode similar to T9 mobile input, where you tap a
-- key to cycle through several candidate characters. The tap interval is how
@ -28,16 +28,16 @@ local DEFAULT_KEITAI_TAP_INTERVAL = 2
-- information.
local function getKeitaiTapInterval()
return G_reader_settings:readSetting("keyboard_japanese_keitai_tap_interval") or DEFAULT_KEITAI_TAP_INTERVAL
return time.s(G_reader_settings:readSetting("keyboard_japanese_keitai_tap_interval", DEFAULT_KEITAI_TAP_INTERVAL_S))
end
local function setKeitaiTapInterval(interval)
G_reader_settings:saveSetting("keyboard_japanese_keitai_tap_interval", interval)
G_reader_settings:saveSetting("keyboard_japanese_keitai_tap_interval", time.to_s(interval))
end
local function exitKeitaiMode(inputbox)
logger.dbg("ja_kbd: clearing keitai window last tap tv")
inputbox._ja_last_tap_tv = nil
inputbox._ja_last_tap_time = nil
end
local function wrappedAddChars(inputbox, char)
@ -48,10 +48,10 @@ local function wrappedAddChars(inputbox, char)
-- For keitai buttons, are we still in the tap interval?
local within_tap_window
if keitai_cycle then
if inputbox._ja_last_tap_tv then
within_tap_window = TimeVal:getDuration(inputbox._ja_last_tap_tv) < getKeitaiTapInterval()
if inputbox._ja_last_tap_time then
within_tap_window = time.since(inputbox._ja_last_tap_time) < getKeitaiTapInterval()
end
inputbox._ja_last_tap_tv = TimeVal:now()
inputbox._ja_last_tap_time = time.now()
else
-- This is a non-keitai or non-tap key, so break out of keitai window.
exitKeitaiMode(inputbox)
@ -113,7 +113,7 @@ local function wrapInputBox(inputbox)
for _, wrapper in ipairs(wrappers) do
wrapper:revert()
end
inputbox._ja_last_tap_tv = nil
inputbox._ja_last_tap_time = nil
inputbox._ja_wrapped = nil
end
end
@ -127,7 +127,7 @@ local function genMenuItems(self)
local interval = getKeitaiTapInterval()
if interval ~= 0 then
-- @translators Keitai input is a kind of Japanese keyboard input mode (similar to T9 keypad input). See <https://en.wikipedia.org/wiki/Japanese_input_method#Mobile_phones> for more information.
return T(N_("Keitai tap interval: %1 second", "Keitai tap interval: %1 seconds", interval), interval)
return T(N_("Keitai tap interval: %1 second", "Keitai tap interval: %1 seconds", time.to_s(interval)), time.to_s(interval))
else
-- @translators Flick and keitai are kinds of Japanese keyboard input modes. See <https://en.wikipedia.org/wiki/Japanese_input_method#Mobile_phones> for more information.
return _("Keitai input: disabled (flick-only input)")
@ -146,14 +146,14 @@ How long to wait (in seconds) for the next tap when in keitai input mode before
If set to 0, keitai input is disabled entirely and only flick input can be used.]]),
width = math.floor(Screen:getWidth() * 0.75),
value = getKeitaiTapInterval(),
value = time.to_s(getKeitaiTapInterval()),
value_min = 0,
value_max = 10,
value_step = 1,
ok_text = _("Set interval"),
default_value = DEFAULT_KEITAI_TAP_INTERVAL,
default_value = DEFAULT_KEITAI_TAP_INTERVAL_S,
callback = function(spin)
setKeitaiTapInterval(spin.value)
setKeitaiTapInterval(time.s(spin.value))
if touchmenu_instance then touchmenu_instance:updateItems() end
end,
}

@ -7,7 +7,7 @@ local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
-- Date at which the last migration snippet was added
local CURRENT_MIGRATION_DATE = 20220205
local CURRENT_MIGRATION_DATE = 20220426
-- Retrieve the date of the previous migration, if any
local last_migration_date = G_reader_settings:readSetting("last_migration_date", 0)
@ -366,5 +366,27 @@ if last_migration_date < 20220205 then
end
end
-- Rename several time storing settings and shift their value to the new meaning
if last_migration_date < 20220426 then
local function migrateSettingsName(old, new, factor)
factor = factor or 1
if G_reader_settings:readSetting(old) then
local value = math.floor(G_reader_settings:readSetting(old) * factor)
G_reader_settings:saveSetting(new, value)
G_reader_settings:delSetting(old)
end
end
migrateSettingsName("ges_tap_interval", "ges_tap_interval_ms", 1e-3)
migrateSettingsName("ges_double_tap_interval", "ges_double_tap_interval_ms", 1e-3)
migrateSettingsName("ges_two_finger_tap_duration", "ges_two_finger_tap_duration_ms", 1e-3)
migrateSettingsName("ges_hold_interval", "ges_hold_interval_ms", 1e-3)
migrateSettingsName("ges_swipe_interval", "ges_swipe_interval_ms", 1e-3)
migrateSettingsName("ges_tap_interval_on_keyboard", "ges_tap_interval_on_keyboard_ms", 1e-3)
migrateSettingsName("device_status_battery_interval", "device_status_battery_interval_minutes")
migrateSettingsName("device_status_memory_interval", "device_status_memory_interval_minutes")
end
-- We're done, store the current migration date
G_reader_settings:saveSetting("last_migration_date", CURRENT_MIGRATION_DATE)

@ -1,4 +1,4 @@
local TimeVal = require("ui/timeval")
local time = require("ui/time")
local GestureRange = {
-- gesture matching type
@ -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.zero
if gs.time - last_time > TimeVal:new{ usec = 1000000 / self.rate } then
local last_time = self.last_time or 0
if gs.time - last_time > time.s(1 / self.rate) then
self.last_time = gs.time
else
return false

@ -123,8 +123,8 @@ function NetworkListener:_unscheduleActivityCheck()
if self._last_tx_packets then
self._last_tx_packets = nil
end
if self._activity_check_delay then
self._activity_check_delay = nil
if self._activity_check_delay_seconds then
self._activity_check_delay_seconds = nil
end
end
@ -135,8 +135,8 @@ function NetworkListener:_scheduleActivityCheck()
local tx_packets = NetworkListener:_getTxPackets()
if self._last_tx_packets and tx_packets then
-- Compute noise threshold based on the current delay
local delay = self._activity_check_delay or default_network_timeout_seconds
local noise_threshold = delay / default_network_timeout_seconds * network_activity_noise_margin
local delay_seconds = self._activity_check_delay_seconds or default_network_timeout_seconds
local noise_threshold = delay_seconds / default_network_timeout_seconds * network_activity_noise_margin
local delta = tx_packets - self._last_tx_packets
-- If there was no meaningful activity (+/- a couple packets), kill the Wi-Fi
if delta <= noise_threshold then
@ -161,19 +161,19 @@ function NetworkListener:_scheduleActivityCheck()
self._last_tx_packets = tx_packets
-- If it's already been scheduled, increase the delay until we hit the ceiling
if self._activity_check_delay then
self._activity_check_delay = self._activity_check_delay + default_network_timeout_seconds
if self._activity_check_delay_seconds then
self._activity_check_delay_seconds = self._activity_check_delay_seconds + default_network_timeout_seconds
if self._activity_check_delay > max_network_timeout_seconds then
self._activity_check_delay = max_network_timeout_seconds
if self._activity_check_delay_seconds > max_network_timeout_seconds then
self._activity_check_delay_seconds = max_network_timeout_seconds
end
else
self._activity_check_delay = default_network_timeout_seconds
self._activity_check_delay_seconds = default_network_timeout_seconds
end
UIManager:scheduleIn(self._activity_check_delay, self._scheduleActivityCheck, self)
UIManager:scheduleIn(self._activity_check_delay_seconds, self._scheduleActivityCheck, self)
self._activity_check_scheduled = true
logger.dbg("NetworkListener: network activity check scheduled in", self._activity_check_delay, "seconds")
logger.dbg("NetworkListener: network activity check scheduled in", self._activity_check_delay_seconds, "seconds")
end
function NetworkListener:onNetworkConnected()
@ -211,5 +211,4 @@ function NetworkListener:onSuspend()
self:onNetworkDisconnected()
end
return NetworkListener

@ -0,0 +1,307 @@
--[[--
A runtime optimized module to compare and do simple arithmetics with fixed point time values (which are called fts in here).
Also implements functions to retrieve time from various system clocks (monotonic, monotonic_coarse, realtime, realtime_coarse, boottime ...).
**Encode:**
Don't store a numerical constant in an fts encoded time. Use the functions provided here!
To convert real world units to an fts, you can use the following functions: time.s(seconds), time.ms(milliseconds), time.us(microseconds).
You can calculate an fts encoded time of 3 s with `time.s(3)`.
Special values: `0` can be used for a zero time and `time.huge` can be used for the longest possible time.
Beware of float encoding precision, though. For instance, take 2.1s: 2.1 cannot be encoded with full precision, so time.s(2.1) would be slightly inaccurate.
(For small values (under 10 secs) the error will be ±1µs, for values below a minute the error will be below ±2µs, for values below an hour the error will be ±100µs.)
When full precision is necessary, use `time.s(2) + time.ms(100)` or `time.s(2) + time.us(100000)` instead.
(For more information about floating-point-representation see: https://stackoverflow.com/questions/3448777/how-to-represent-0-1-in-floating-point-arithmetic-and-decimal)
**Decode:**
You can get the number of seconds in an fts encoded time with `time.to_s(time_fts)`.
You can get the number of milliseconds in an fts encoded time with `time.to_ms(time_fts)`.
You can get the number of microseconds in an fts encoded time with `time.to_us(time_fts)`.
Please be aware, that `time.to_number` is the same as a `time.to_s` with a precision of four decimal places.
**Supported calculations:**
You can add and subtract all fts encoded times, without any problems.
You can multiply or divide fts encoded times by numerical constants. So if you need the half of a time, `time_fts/2` is correct.
A division of two fts encoded times would give you a number. (e.g., `time.s(2.5)/time.s(0.5)` equals `5`).
The functions `math.abs()`, `math.min()`, `math.max()` and `math.huge` will work as expected.
Comparisons (`>`, `>=`, `==`, `<`, `<=` and `~=`) of two fts encoded times work as expected.
If you want a duration form a given time_fts to *now*, `time.since(time_fts)` as a shortcut (or simply use `fts.now - time_fts`) will return an fts encoded time. If you need milliseconds use `time.to_ms(time.since(time_fts))`.
**Unsupported calculations:**
Don't add a numerical constant to an fts time (in the best case, the numerical constant is interpreted as µs).
Don't multiply two fts_encoded times (the position of the comma is wrong).
But please be aware that _all other not explicitly supported_ math on fts encoded times (`math.xxx()`) won't work as expected. (If you really, really need that, you have to shift the position of the comma yourself!)
**Background:**
Numbers in Lua are double float which have a mantissa (precision) of 53 bit (plus sign + exponent)
We won't use the exponent here.
So we can store 2^53 = 9.0072E15 different values. If we use the lower 6 digits for µs, we can store
up to 9.0072E9 seconds.
A year has 365.25*24*3600 = 3.15576E7 s, so we can store up to 285 years (9.0072E9/3.15576E7) with µs precision.
The module has been tested with the fixed point comma at 10^6 (other values might work, but are not really tested).
**Recommendations:**
If the name of a variable implies a time (now, when, until, xxxdeadline, xxxtime, getElapsedTimeSinceBoot, lastxxxtimexxx, ...) we assume this value to be a time (fts encoded).
Other objects which are times (like `last_tap`, `tap_interval_override`, ...) shall be renamed to something like `last_tap_time` (so to make it clear that they are fts encoded).
All other time variables (a handful) get the appropriate suffix `_ms`, `_us`, `_s` (`_m`, `_h`, `_d`) denoting their status as plain Lua numbers and their resolution.
@module time
@usage
local time = require("ui/time")
local start_time = time.now()
-- Do some stuff.
-- You can add and subtract `fts times` objects.
local duration = time.now() - start.fts
-- And convert that object to various more human-readable formats, e.g.,
print(string.format("Stuff took %.3fms", time.to_ms(duration)))
local offset = time.s(100)
print(string.format("Stuff plus 100s took %.3fms", time.to_ms(duration + offset)))
]]
local ffi = require("ffi")
require("ffi/posix_h")
local logger = require("logger")
local C = ffi.C
-- An FTS_PRECISION of 1e6 will give us a µs precision.
local FTS_PRECISION = 1e6
local S2FTS = FTS_PRECISION
local MS2FTS = FTS_PRECISION / 1e3
local US2FTS = FTS_PRECISION / 1e6
local NS2FTS = FTS_PRECISION / 1e9
local FTS2S = 1 / S2FTS
local FTS2MS = 1 / MS2FTS
local FTS2US = 1 / US2FTS
-- Fixed point time
local time = {}
--- Sometimes we need a very large time
time.huge = math.huge
--- Creates a time (fts) from a number in seconds
function time.s(seconds)
return math.floor(seconds * S2FTS)
end
--- Creates a time (fts) from a number in milliseconds
function time.ms(msec)
return math.floor(msec * MS2FTS)
end
--- Creates a time (fts) from a number in microseconds
function time.us(usec)
return math.floor(usec * US2FTS)
end
--- Creates a time (fts) from a structure similar to timeval
function time.timeval(tv)
return tv.sec * S2FTS + tv.usec * US2FTS
end
--- Converts an fts time to a Lua (decimal) number (sec.usecs) (accurate to the ms, rounded to 4 decimal places)
function time.to_number(time_fts)
-- Round to 4 decimal places
return math.floor(time.to_s(time_fts) * 10000 + 0.5) / 10000
end
--- Converts an fts to a Lua (int) number (resolution: 1µs)
function time.to_s(time_fts)
-- Time in seconds with µs precision (without decimal places)
return time_fts * FTS2S
end
--[[-- Converts a fts to a Lua (int) number (resolution: 1ms, rounded).
(Mainly useful when computing a time lapse for benchmarking purposes).
]]
function time.to_ms(time_fts)
-- Time in milliseconds ms (without decimal places)
return math.floor(time_fts * FTS2MS + 0.5)
end
--- Converts an fts to a Lua (int) number (resolution: 1µs, rounded)
function time.to_us(time_fts)
-- Time in microseconds µs (without decimal places)
return math.floor(time_fts * FTS2US + 0.5)
end
--[[-- Compare a past *MONOTONIC* fts time to *now*, returning the elapsed time between the two. (sec.usecs variant)
Returns a Lua (decimal) number (sec.usecs, with decimal places) (accurate to the µs)
]]
function time.since(start_time)
-- Time difference
return time.now() - start_time
end
--- Splits an fts to seconds and microseconds.
-- If argument is nil, returns nil,nil.
function time.split_s_us(time_fts)
if not time_fts then return nil, nil end
local sec = math.floor(time_fts * FTS2S)
local usec = math.floor(time_fts - sec * S2FTS) * FTS2US
-- Seconds and µs
return sec, usec
end
-- ffi object for C.clock_gettime calls
local timespec = ffi.new("struct timespec")
-- We prefer CLOCK_MONOTONIC_COARSE if it's available and has a decent resolution,
-- as we generally don't need nano/micro second precision,
-- and it can be more than twice as fast as CLOCK_MONOTONIC/CLOCK_REALTIME/gettimeofday...
local PREFERRED_MONOTONIC_CLOCKID = C.CLOCK_MONOTONIC
-- Ditto for REALTIME (for :realtime_coarse only, :realtime uses gettimeofday ;)).
local PREFERRED_REALTIME_CLOCKID = C.CLOCK_REALTIME
-- CLOCK_BOOTTIME is only available on Linux 2.6.39+...
local HAVE_BOOTTIME = false
if ffi.os == "Linux" then
-- Unfortunately, it was only implemented in Linux 2.6.32, and we may run on older kernels than that...
-- So, just probe it to see if we can rely on it.
local probe_ts = ffi.new("struct timespec")
if C.clock_getres(C.CLOCK_MONOTONIC_COARSE, probe_ts) == 0 then
-- Now, it usually has a 1ms resolution on modern x86_64 systems,
-- but it only provides a 10ms resolution on all my armv7 devices :/.
if probe_ts.tv_sec == 0 and probe_ts.tv_nsec <= 1000000 then
PREFERRED_MONOTONIC_CLOCKID = C.CLOCK_MONOTONIC_COARSE
end
end
logger.dbg("fts: Preferred MONOTONIC clock source is", PREFERRED_MONOTONIC_CLOCKID == C.CLOCK_MONOTONIC_COARSE and "CLOCK_MONOTONIC_COARSE" or "CLOCK_MONOTONIC")
if C.clock_getres(C.CLOCK_REALTIME_COARSE, probe_ts) == 0 then
if probe_ts.tv_sec == 0 and probe_ts.tv_nsec <= 1000000 then
PREFERRED_REALTIME_CLOCKID = C.CLOCK_REALTIME_COARSE
end
end
logger.dbg("fts: Preferred REALTIME clock source is", PREFERRED_REALTIME_CLOCKID == C.CLOCK_REALTIME_COARSE and "CLOCK_REALTIME_COARSE" or "CLOCK_REALTIME")
if C.clock_getres(C.CLOCK_BOOTTIME, probe_ts) == 0 then
HAVE_BOOTTIME = true
end
logger.dbg("fts: BOOTTIME clock source is", HAVE_BOOTTIME and "supported" or "NOT supported")
probe_ts = nil --luacheck: ignore
end
--[[--
Returns an fts time based on the current wall clock time.
(e.g., gettimeofday / clock_gettime(CLOCK_REALTIME).
This is a simple wrapper around clock_gettime(CLOCK_REALTIME) to get all the niceties of a time.
If you don't need sub-second precision, prefer os.time().
Which means that, yes, this is a fancier POSIX Epoch ;).
@usage
local time = require("ui/time")
local fts_start = time.realtime()
-- Do some stuff.
-- You can add and substract fts times
local fts_duration = time.realtime() - fts_start
@treturn fts fixed point time
]]
function time.realtime()
C.clock_gettime(C.CLOCK_REALTIME, timespec)
-- TIMESPEC_TO_FTS
return tonumber(timespec.tv_sec) * S2FTS + math.floor(tonumber(timespec.tv_nsec) * NS2FTS)
end
--[[--
Returns an fts time based on the current value from the system's MONOTONIC clock source.
(e.g., clock_gettime(CLOCK_MONOTONIC).)
POSIX guarantees that this clock source will *never* go backwards (but it *may* return the same value multiple times).
On Linux, this will not account for time spent with the device in suspend (unlike CLOCK_BOOTTIME).
@treturn fts fixed point time
]]
function time.monotonic()
C.clock_gettime(C.CLOCK_MONOTONIC, timespec)
-- TIMESPEC_TO_FTS
return tonumber(timespec.tv_sec) * S2FTS + math.floor(tonumber(timespec.tv_nsec) * NS2FTS)
end
--- Ditto, but w/ CLOCK_MONOTONIC_COARSE if it's available and has a 1ms resolution or better (uses CLOCK_MONOTONIC otherwise).
function time.monotonic_coarse()
C.clock_gettime(PREFERRED_MONOTONIC_CLOCKID, timespec)
-- TIMESPEC_TO_FTS
return tonumber(timespec.tv_sec) * S2FTS + math.floor(tonumber(timespec.tv_nsec) * NS2FTS)
end
-- Ditto, but w/ CLOCK_REALTIME_COARSE if it's available and has a 1ms resolution or better (uses CLOCK_REALTIME otherwise).
function time.realtime_coarse()
C.clock_gettime(PREFERRED_REALTIME_CLOCKID, timespec)
-- TIMESPEC_TO_FTS
return tonumber(timespec.tv_sec) * S2FTS + math.floor(tonumber(timespec.tv_nsec) * NS2FTS)
end
--- Since CLOCK_BOOTIME may not be supported, we offer a few aliases with automatic fallbacks to MONOTONIC or REALTIME
if HAVE_BOOTTIME then
--- Ditto, but w/ CLOCK_BOOTTIME (will return an fts time set to 0, 0 if the clock source is unsupported, as it's 2.6.39+)
--- Only use it if you *know* it's going to be supported, otherwise, prefer the four following aliases.
function time.boottime()
C.clock_gettime(C.CLOCK_BOOTTIME, timespec)
-- TIMESPEC_TO_FTS
return tonumber(timespec.tv_sec) * S2FTS + math.floor(tonumber(timespec.tv_nsec) * NS2FTS)
end
time.boottime_or_monotonic = time.boottime
time.boottime_or_monotonic_coarse = time.boottime
time.boottime_or_realtime = time.boottime
time.boottime_or_realtime_coarse = time.boottime
else
function time.boottime()
logger.warn("fts: Attemped to call boottime on a platform where it's unsupported!")
return 0
end
time.boottime_or_monotonic = time.monotonic
time.boottime_or_monotonic_coarse = time.monotonic_coarse
time.boottime_or_realtime = time.realtime
time.boottime_or_realtime_coarse = time.realtime_coarse
end
--[[-- Alias for `monotonic_coarse`.
The assumption being anything that requires accurate timestamps expects a monotonic clock source.
This is certainly true for KOReader's UI scheduling.
]]
time.now = time.monotonic_coarse
--- Converts an fts time to a string (seconds with 6 decimal places)
function time.format_time(time_fts)
return string.format("%.06f", time_fts * FTS2S)
end
return time

@ -5,11 +5,11 @@ This module manages widgets.
local Device = require("device")
local Event = require("ui/event")
local Geom = require("ui/geometry")
local TimeVal = require("ui/timeval")
local dbg = require("dbg")
local logger = require("logger")
local ffiUtil = require("ffi/util")
local util = require("util")
local time = require("ui/time")
local _ = require("gettext")
local Input = Device.input
local Screen = Device.screen
@ -30,7 +30,7 @@ local UIManager = {
event_handlers = nil,
_running = true,
_now = TimeVal:now(),
_now = time.now(),
_window_stack = {},
_task_queue = {},
_task_queue_dirty = false,
@ -523,14 +523,14 @@ function UIManager:close(widget, refreshtype, refreshregion, refreshdither)
end
-- schedule an execution task, task queue is in ascendant order
function UIManager:schedule(time, action, ...)
function UIManager:schedule(sched_time, action, ...)
local p, s, e = 1, 1, #self._task_queue
if e ~= 0 then
-- do a binary insert
repeat
p = math.floor(s + (e - s) / 2)
p = math.floor((e + s) / 2) -- Not necessary to use (s + (e -s) / 2) here!
local p_time = self._task_queue[p].time
if time > p_time then
if sched_time > p_time then
if s == e then
p = e + 1
break
@ -539,11 +539,11 @@ function UIManager:schedule(time, action, ...)
else
s = p
end
elseif time < p_time then
e = p
if s == e then
elseif sched_time < p_time then
if s == p then
break
end
e = p
else
-- for fairness, it's better to make p+1 is strictly less than
-- p might want to revisit here in the future
@ -552,7 +552,7 @@ function UIManager:schedule(time, action, ...)
until e < s
end
table.insert(self._task_queue, p, {
time = time,
time = sched_time,
action = action,
argc = select('#', ...),
args = {...},
@ -560,8 +560,8 @@ function UIManager:schedule(time, action, ...)
self._task_queue_dirty = true
end
dbg:guard(UIManager, 'schedule',
function(self, time, action)
assert(time.sec >= 0, "Only positive time allowed")
function(self, sched_time, action)
assert(sched_time >= 0, "Only positive time allowed")
assert(action ~= nil, "No action")
end)
@ -577,7 +577,7 @@ Schedules a task to be run a certain amount of seconds from now.
function UIManager:scheduleIn(seconds, action, ...)
-- We might run significantly late inside an UI frame, so we can't use the cached value here.
-- It would also cause some bad interactions with the way nextTick & co behave.
local when = TimeVal:now() + TimeVal:fromnumber(seconds)
local when = time.now() + time.s(seconds)
self:schedule(when, action, ...)
end
dbg:guard(UIManager, 'scheduleIn',
@ -1065,15 +1065,15 @@ 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{ sec = 0, usec = 800000 }
delay = time.ms(800)
else
-- On non-eInk screen, display is usually instantaneous
delay = TimeVal:new{ sec = 0, usec = 400000 }
delay = time.ms(400)
end
else -- we expect a number
delay = TimeVal:new{ sec = set_or_seconds, usec = 0 }
delay = time.s(set_or_seconds)
end
self._discard_events_till = TimeVal:now() + delay
self._discard_events_till = time.now() + delay
end
--[[--
@ -1089,7 +1089,7 @@ function UIManager:sendEvent(event)
-- Ensure discardEvents
if self._discard_events_till then
if TimeVal:now() < self._discard_events_till then
if time.now() < self._discard_events_till then
return
else
self._discard_events_till = nil
@ -1173,10 +1173,10 @@ end
--[[
function UIManager:getNextTaskTimes(count)
count = count or 1
count = math.min(count or 1, #self._task_queue)
local times = {}
for i = 1, math.min(count, #self._task_queue) do
times[i] = self._task_queue[i].time - TimeVal:now()
for i = 1, count do
times[i] = self._task_queue[i].time - time.now()
end
return times
end
@ -1184,14 +1184,14 @@ end
function UIManager:getNextTaskTime()
if #self._task_queue > 0 then
return self._task_queue[1].time - TimeVal:now()
return self._task_queue[1].time - time:now()
else
return nil
end
end
function UIManager:_checkTasks()
self._now = TimeVal:now()
self._now = time.now()
local wait_until = nil
-- task.action may schedule other events
@ -1202,8 +1202,8 @@ function UIManager:_checkTasks()
break
end
local next_task = self._task_queue[1]
local task_tv = next_task.time or TimeVal.zero
if task_tv <= self._now then
local task_time = next_task.time or 0
if task_time <= self._now then
-- remove from table
local task = table.remove(self._task_queue, 1)
-- task is pending to be executed right now. do it.
@ -1213,7 +1213,7 @@ function UIManager:_checkTasks()
else
-- queue is sorted in ascendant order, safe to assume all items
-- are future tasks for now
wait_until = next_task.time
wait_until = task_time
break
end
end
@ -1222,9 +1222,9 @@ function UIManager:_checkTasks()
end
--[[--
Returns a TimeVal object corresponding to the last UI tick.
Returns a time (fts) corresponding to the last tick.
This is essentially a cached TimeVal:now(), computed at the top of every iteration of the main UI loop,
This is essentially a cached time.now(), computed at the top of every iteration of the main UI loop,
(right before checking/running scheduled tasks).
This is mainly useful to compute/schedule stuff in the same time scale as the UI loop (i.e., MONOTONIC),
without having to resort to a syscall.
@ -1233,7 +1233,7 @@ unless you're blocking the UI for a significant amount of time in a single UI fr
That is to say, its granularity is an UI frame.
Prefer the appropriate TimeVal method for your needs if you require perfect accuracy or better granularity
Prefer the appropriate time function for your needs if you require perfect accuracy or better granularity
(e.g., when you're actually working on the event loop *itself* (UIManager, Input, GestureDetector),
or if you're dealing with intra-frame timers).
@ -1244,10 +1244,10 @@ function UIManager:getTime()
end
--[[--
Returns a TimeVal object corresponding to the last UI tick plus the time in standby and suspend.
Returns a time (fts) corresponding to the last UI tick plus the time in standby.
]]
function UIManager:getElapsedTimeSinceBoot()
return self:getTime() + Device.total_standby_tv + Device.total_suspend_tv
return self:getTime() + Device.total_standby_time + Device.total_suspend_time
end
-- precedence of refresh modes:
@ -1676,7 +1676,7 @@ function UIManager:handleInput()
-- We pass that on as an absolute deadline, not a relative wait time.
if wait_us then
deadline = now + TimeVal:new{ usec = wait_us }
deadline = now + time.us(wait_us)
end
-- If there's a scheduled task pending, that puts an upper bound on how long to wait.
@ -1721,7 +1721,6 @@ function UIManager:handleInput()
end
end
function UIManager:onRotation()
self:setDirty("all", "full")
self:forceRePaint()

@ -18,7 +18,6 @@ local ScrollHtmlWidget = require("ui/widget/scrollhtmlwidget")
local ScrollTextWidget = require("ui/widget/scrolltextwidget")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
local TimeVal = require("ui/timeval")
local TitleBar = require("ui/widget/titlebar")
local Translator = require("ui/translator")
local UIManager = require("ui/uimanager")
@ -32,6 +31,7 @@ local C_ = _.pgettext
local Input = Device.input
local Screen = Device.screen
local T = require("ffi/util").template
local time = require("ui/time")
--[[
Display quick lookup word definition
@ -154,7 +154,7 @@ function DictQuickLookup:init()
args = function(text, hold_duration)
-- do this lookup in the same domain (dict/wikipedia)
local lookup_wikipedia = self.is_wiki
if hold_duration >= TimeVal:new{ sec = 3, usec = 0 } then
if hold_duration >= time.s(3) then
-- but allow switching domain with a long hold
lookup_wikipedia = not lookup_wikipedia
end

@ -13,13 +13,13 @@ local InputContainer = require("ui/widget/container/inputcontainer")
local LineWidget = require("ui/widget/linewidget")
local ScrollHtmlWidget = require("ui/widget/scrollhtmlwidget")
local Size = require("ui/size")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local _ = require("gettext")
local Screen = Device.screen
local T = require("ffi/util").template
local time = require("ui/time")
-- If we wanted to use the default font set for the book,
-- we'd need to add a few functions to crengine and cre.cpp
@ -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, usec = 0 } and "LookupWord" or "LookupWikipedia"
local lookup_target = hold_duration < time.s(3) and "LookupWord" or "LookupWikipedia"
self.dialog:handleEvent(
Event:new(lookup_target, text)
)

@ -15,12 +15,12 @@ local NaturalLight = require("ui/widget/naturallightwidget")
local ProgressWidget = require("ui/widget/progresswidget")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
local TimeVal = require("ui/timeval")
local TitleBar = require("ui/widget/titlebar")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local time = require("ui/time")
local _ = require("gettext")
local Screen = Device.screen
@ -30,7 +30,7 @@ local FrontLightWidget = FocusManager: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.zero, -- Tracks last update time to prevent update spamming.
last_time = 0, -- Tracks last update time to prevent update spamming.
}
function FrontLightWidget:init()
@ -569,9 +569,9 @@ 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.zero
if current_time - last_time > TimeVal:new{ usec = 1000000 / self.rate } then
local current_time = time.now()
local last_time = self.last_time or 0
if current_time - last_time > time.s(1 / self.rate) then
self.last_time = current_time
else
-- Schedule a final update after we stop panning.

@ -9,9 +9,9 @@ local GestureRange = require("ui/gesturerange")
local InputContainer = require("ui/widget/container/inputcontainer")
local Mupdf = require("ffi/mupdf")
local Screen = Device.screen
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local logger = require("logger")
local time = require("ui/time")
local util = require("util")
local HtmlBoxWidget = InputContainer:new{
@ -21,7 +21,7 @@ local HtmlBoxWidget = InputContainer:new{
page_count = 0,
page_number = 1,
hold_start_pos = nil,
hold_start_tv = nil,
hold_start_time = nil,
html_link_tapped_callback = nil,
}
@ -166,7 +166,7 @@ function HtmlBoxWidget:onHoldStartText(_, ges)
return false -- let event be processed by other widgets
end
self.hold_start_tv = UIManager:getTime()
self.hold_start_time = UIManager:getTime()
return true
end
@ -230,7 +230,7 @@ function HtmlBoxWidget:onHoldReleaseText(callback, ges)
return false
end
local hold_duration = TimeVal.now() - self.hold_start_tv
local hold_duration = time.now() - self.hold_start_time
local page = self.document:openPage(self.page_number)
local lines = page:getPageText()

@ -13,10 +13,10 @@ local InputContainer = require("ui/widget/container/inputcontainer")
local RectSpan = require("ui/widget/rectspan")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local Input = Device.input
local time = require("ui/time")
local Screen = Device.screen
local band = bit.band
@ -168,9 +168,9 @@ 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, usec = 0 }
local expire_time = UIManager:getTime() - time.s(30)
for i=#Notification._nums_shown, 1, -1 do
if Notification._nums_shown[i] and Notification._nums_shown[i] > expire_tv then
if Notification._nums_shown[i] and Notification._nums_shown[i] > expire_time then
break -- still shown (or not yet expired)
end
table.remove(Notification._nums_shown, i)

@ -24,11 +24,11 @@ local RenderText = require("ui/rendertext")
local RightContainer = require("ui/widget/container/rightcontainer")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local Math = require("optmath")
local logger = require("logger")
local dbg = require("dbg")
local time = require("ui/time")
local util = require("util")
local Screen = require("device").screen
@ -1852,18 +1852,18 @@ function TextBoxWidget:onHoldStartText(_, ges)
-- check coordinates are actually inside our area
if self.hold_start_x < 0 or self.hold_start_x > self.dimen.w or
self.hold_start_y < 0 or self.hold_start_y > self.dimen.h then
self.hold_start_tv = nil -- don't process coming HoldRelease event
self.hold_start_time = nil -- don't process coming HoldRelease event
return false -- let event be processed by other widgets
end
self.hold_start_tv = UIManager:getTime()
self.hold_start_time = UIManager:getTime()
return true
end
function TextBoxWidget:onHoldPanText(_, ges)
-- We don't highlight the currently selected text, but just let this
-- event pop up if we are not currently selecting text
if not self.hold_start_tv then
if not self.hold_start_time then
return false
end
-- Don't let that event be processed by other widget
@ -1877,7 +1877,7 @@ function TextBoxWidget:onHoldReleaseText(callback, ges)
local hold_end_y = ges.pos.y - self.dimen.y
-- check we have seen a HoldStart event
if not self.hold_start_tv then
if not self.hold_start_time then
return false
end
-- check start and end coordinates are actually inside our area
@ -1888,7 +1888,7 @@ function TextBoxWidget:onHoldReleaseText(callback, ges)
return false
end
local hold_duration = TimeVal.now() - self.hold_start_tv
local hold_duration = time.now() - self.hold_start_time
-- If page contains an image, check if Hold is on this image and deal
-- with it directly
@ -1950,7 +1950,7 @@ function TextBoxWidget:onHoldReleaseText(callback, ges)
-- a missed start event
self.hold_start_x = nil
self.hold_start_y = nil
self.hold_start_tv = nil
self.hold_start_time = nil
if self.use_xtext then
-- With xtext and fribidi, words may not be laid out in logical order,
@ -1982,7 +1982,7 @@ function TextBoxWidget:onHoldReleaseText(callback, ges)
-- to consider when looking for word boundaries)
local selected_text = self._xtext:getSelectedWords(sel_start_idx, sel_end_idx, 50)
logger.dbg("onHoldReleaseText (duration:", hold_duration:tonumber(), ") :",
logger.dbg("onHoldReleaseText (duration:", time.format_time(hold_duration), ") :",
sel_start_idx, ">", sel_end_idx, "=", selected_text)
callback(selected_text, hold_duration)
return true
@ -2000,7 +2000,7 @@ function TextBoxWidget:onHoldReleaseText(callback, ges)
end
local selected_text = table.concat(self.charlist, "", sel_start_idx, sel_end_idx)
logger.dbg("onHoldReleaseText (duration:", hold_duration:tonumber(), ") :", sel_start_idx, ">", sel_end_idx, "=", selected_text)
logger.dbg("onHoldReleaseText (duration:", time.format_time(hold_duration), ") :", sel_start_idx, ">", sel_end_idx, "=", selected_text)
callback(selected_text, hold_duration)
return true
end

@ -14,12 +14,12 @@ local InputContainer = require("ui/widget/container/inputcontainer")
local KeyboardLayoutDialog = require("ui/widget/keyboardlayoutdialog")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local time = require("ui/time")
local util = require("util")
local Screen = Device.screen
@ -665,8 +665,7 @@ function VirtualKeyPopup:init()
}
},
}
self.tap_interval_override = G_reader_settings:readSetting("ges_tap_interval_on_keyboard", 0)
self.tap_interval_override = TimeVal:new{ usec = self.tap_interval_override }
self.tap_interval_override = time.ms(G_reader_settings:readSetting("ges_tap_interval_on_keyboard_ms", 0))
if Device:hasKeys() then
self.key_events.Close = { {Device.input.group.Back}, doc = "close keyboard" }
@ -787,8 +786,7 @@ function VirtualKeyboard:init()
self.min_layer = keyboard.min_layer
self.max_layer = keyboard.max_layer
self:initLayer(self.keyboard_layer)
self.tap_interval_override = G_reader_settings:readSetting("ges_tap_interval_on_keyboard", 0)
self.tap_interval_override = TimeVal:new{ usec = self.tap_interval_override }
self.tap_interval_override = time.ms(G_reader_settings:readSetting("ges_tap_interval_on_keyboard_ms", 0))
if Device:hasKeys() then
self.key_events.Close = { {"Back"}, doc = "close keyboard" }
end

@ -13,11 +13,11 @@ end
local Event = require("ui/event")
local NetworkMgr = require("ui/network/manager")
local PluginShare = require("pluginshare")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local util = require("util")
local time = require("ui/time")
local _ = require("gettext")
local Math = require("optmath")
local T = require("ffi/util").template
@ -32,7 +32,7 @@ local AutoSuspend = WidgetContainer:new{
autoshutdown_timeout_seconds = default_autoshutdown_timeout_seconds,
auto_suspend_timeout_seconds = default_auto_suspend_timeout_seconds,
auto_standby_timeout_seconds = default_auto_standby_timeout_seconds,
last_action_tv = TimeVal.zero,
last_action_time = 0,
is_standby_scheduled = false,
task = nil,
standby_task = nil,
@ -58,7 +58,7 @@ function AutoSuspend:_schedule(shutdown_only)
return
end
local suspend_delay, shutdown_delay
local suspend_delay_seconds, shutdown_delay_seconds
local is_charging
-- On devices with an auxiliary battery, we only care about the auxiliary battery being charged...
local powerd = Device:getPowerDevice()
@ -68,29 +68,29 @@ function AutoSuspend:_schedule(shutdown_only)
is_charging = powerd:isCharging()
end
if PluginShare.pause_auto_suspend or is_charging then
suspend_delay = self.auto_suspend_timeout_seconds
shutdown_delay = self.autoshutdown_timeout_seconds
suspend_delay_seconds = self.auto_suspend_timeout_seconds
shutdown_delay_seconds = self.autoshutdown_timeout_seconds
else
local now_tv = UIManager:getElapsedTimeSinceBoot()
suspend_delay = self.auto_suspend_timeout_seconds - (now_tv - self.last_action_tv):tonumber()
shutdown_delay = self.autoshutdown_timeout_seconds - (now_tv - self.last_action_tv):tonumber()
local now = UIManager:getElapsedTimeSinceBoot()
suspend_delay_seconds = self.auto_suspend_timeout_seconds - time.to_number(now - self.last_action_time)
shutdown_delay_seconds = self.autoshutdown_timeout_seconds - time.to_number(now - self.last_action_time)
end
-- Try to shutdown first, as we may have been woken up from suspend just for the sole purpose of doing that.
if self:_enabledShutdown() and shutdown_delay <= 0 then
if self:_enabledShutdown() and shutdown_delay_seconds <= 0 then
logger.dbg("AutoSuspend: initiating shutdown")
UIManager:poweroff_action()
elseif self:_enabled() and suspend_delay <= 0 and not shutdown_only then
elseif self:_enabled() and suspend_delay_seconds <= 0 and not shutdown_only then
logger.dbg("AutoSuspend: will suspend the device")
UIManager:suspend()
else
if self:_enabled() and not shutdown_only then
logger.dbg("AutoSuspend: scheduling next suspend check in", suspend_delay)
UIManager:scheduleIn(suspend_delay, self.task)
logger.dbg("AutoSuspend: scheduling next suspend check in", suspend_delay_seconds)
UIManager:scheduleIn(suspend_delay_seconds, self.task)
end
if self:_enabledShutdown() then
logger.dbg("AutoSuspend: scheduling next shutdown check in", shutdown_delay)
UIManager:scheduleIn(shutdown_delay, self.task)
logger.dbg("AutoSuspend: scheduling next shutdown check in", shutdown_delay_seconds)
UIManager:scheduleIn(shutdown_delay_seconds, self.task)
end
end
end
@ -104,14 +104,14 @@ end
function AutoSuspend:_start()
if self:_enabled() or self:_enabledShutdown() then
logger.dbg("AutoSuspend: start suspend/shutdown timer at", self.last_action_tv:tonumber())
logger.dbg("AutoSuspend: start suspend/shutdown timer at", time.format_time(self.last_action_time))
self:_schedule()
end
end
function AutoSuspend:_start_standby()
if self:_enabledStandby() then
logger.dbg("AutoSuspend: start standby timer at", self.last_action_tv:tonumber())
logger.dbg("AutoSuspend: start standby timer at", time.format_time(self.last_action_time))
self:_schedule_standby()
end
end
@ -119,7 +119,7 @@ end
-- Variant that only re-engages the shutdown timer for onUnexpectedWakeupLimit
function AutoSuspend:_restart()
if self:_enabledShutdown() then
logger.dbg("AutoSuspend: restart shutdown timer at", self.last_action_tv:tonumber())
logger.dbg("AutoSuspend: restart shutdown timer at", time.format_time(self.last_action_time))
self:_schedule(true)
end
end
@ -161,7 +161,7 @@ function AutoSuspend:init()
-- Make sure we only have an AllowStandby handler when we actually want one...
self:toggleStandbyHandler(self:_enabledStandby())
self.last_action_tv = UIManager:getElapsedTimeSinceBoot()
self.last_action_time = UIManager:getElapsedTimeSinceBoot()
self:_start()
self:_start_standby()
@ -185,7 +185,7 @@ end
function AutoSuspend:onInputEvent()
logger.dbg("AutoSuspend: onInputEvent")
self.last_action_tv = UIManager:getElapsedTimeSinceBoot()
self.last_action_time = UIManager:getElapsedTimeSinceBoot()
end
function AutoSuspend:_unschedule_standby()
@ -219,41 +219,41 @@ function AutoSuspend:_schedule_standby()
end
-- When we're in a state where entering suspend is undesirable, we simply postpone the check by the full delay.
local standby_delay
local standby_delay_seconds
if NetworkMgr:isWifiOn() then
-- Don't enter standby if wifi is on, as this will break in fun and interesting ways (from Wi-Fi issues to kernel deadlocks).
--logger.dbg("AutoSuspend: WiFi is on, delaying standby")
standby_delay = self.auto_standby_timeout_seconds
standby_delay_seconds = self.auto_standby_timeout_seconds
elseif Device.powerd:isCharging() and not Device:canPowerSaveWhileCharging() then
-- Don't enter standby when charging on devices where charging prevents entering low power states.
-- NOTE: Minor simplification here, we currently don't do the hasAuxBattery dance like in _schedule,
-- because all the hasAuxBattery devices can currently enter PM states while charging ;).
--logger.dbg("AutoSuspend: charging, delaying standby")
standby_delay = self.auto_standby_timeout_seconds
standby_delay_seconds = self.auto_standby_timeout_seconds
else
local now_tv = UIManager:getElapsedTimeSinceBoot()
standby_delay = self.auto_standby_timeout_seconds - (now_tv - self.last_action_tv):tonumber()
local now = UIManager:getElapsedTimeSinceBoot()
standby_delay_seconds = self.auto_standby_timeout_seconds - time.to_number(now - self.last_action_time)
-- If we blow past the deadline on the first call of a scheduling cycle,
-- make sure we don't go straight to allowStandby, as we haven't called preventStandby yet...
if not self.is_standby_scheduled and standby_delay <= 0 then
if not self.is_standby_scheduled and standby_delay_seconds <= 0 then
-- If this happens, it means we hit LeaveStandby or Resume *before* consuming new input events,
-- e.g., if there weren't any input events at all (woken up by an alarm),
-- or if the only input events we consumed did not trigger an InputEvent event (woken up by gyro events),
-- meaning self.last_action_tv is further in the past than it ought to.
-- meaning self.last_action_time is further in the past than it ought to.
-- Delay by the full amount to avoid further bad scheduling interactions.
standby_delay = self.auto_standby_timeout_seconds
standby_delay_seconds = self.auto_standby_timeout_seconds
end
end
if standby_delay <= 0 then
if standby_delay_seconds <= 0 then
-- We blew the deadline, tell UIManager we're ready to enter standby
self:allowStandby()
else
-- Reschedule standby for the full or remaining delay
-- NOTE: This is fairly chatty, given the low delays, but really helpful nonetheless... :/
logger.dbg("AutoSuspend: scheduling next standby check in", standby_delay)
UIManager:scheduleIn(standby_delay, self.standby_task)
logger.dbg("AutoSuspend: scheduling next standby check in", standby_delay_seconds)
UIManager:scheduleIn(standby_delay_seconds, self.standby_task)
-- Prevent standby until we actually blow the deadline
if not self.is_standby_scheduled then
@ -318,7 +318,7 @@ function AutoSuspend:onResume()
end
-- Unschedule in case we tripped onUnexpectedWakeupLimit first...
self:_unschedule()
-- We should always follow an InputEvent, so last_action_tv is already up to date :).
-- We should always follow an InputEvent, so last_action_time is already up to date :).
self:_start()
self:_unschedule_standby()
self:_start_standby()
@ -391,13 +391,13 @@ function AutoSuspend:pickTimeoutValue(touchmenu_instance, title, info, setting,
ok_text = _("Set timeout"),
title_text = title,
info_text = info,
callback = function(time)
callback = function(spinner)
if time_scale == 2 then
self[setting] = (time.hour * 24 + time.min) * 3600
self[setting] = (spinner.hour * 24 + spinner.min) * 3600
elseif time_scale == 1 then
self[setting] = time.hour * 3600 + time.min * 60
self[setting] = spinner.hour * 3600 + spinner.min * 60
else
self[setting] = time.hour * 60 + time.min
self[setting] = spinner.hour * 60 + spinner.min
end
self[setting] = Math.clamp(self[setting], range[1], range[2])
G_reader_settings:saveSetting(setting, self[setting])
@ -571,7 +571,7 @@ function AutoSuspend:AllowStandbyHandler()
if next_task_time then
-- Wake up slightly after the formerly scheduled event,
-- to avoid resheduling the same function after a fraction of a second again (e.g. don't draw footer twice).
wake_in = math.floor(next_task_time:tonumber()) + 1
wake_in = math.floor(time.to_number(next_task_time)) + 1
else
wake_in = math.huge
end
@ -583,12 +583,12 @@ function AutoSuspend:AllowStandbyHandler()
-- This obviously needs a matching implementation in Device, the canonical one being Kobo.
Device:standby(wake_in)
logger.dbg("AutoSuspend: left standby after", Device.last_standby_tv:tonumber(), "s")
logger.dbg("AutoSuspend: left standby after", time.format_time(Device.last_standby_time), "s")
-- We delay the LeaveStandby event (our onLeaveStandby handler is responsible for rescheduling everything properly),
-- to make sure UIManager will consume the input events that woke us up first
-- (in case we were woken up by user input, as opposed to an rtc wake alarm)!
-- (This ensures we'll use an up to date last_action_tv, and that it only ever gets updated from *user* input).
-- (This ensures we'll use an up to date last_action_time, and that it only ever gets updated from *user* input).
-- NOTE: UIManager consumes scheduled tasks before input events, which is why we can't use nextTick.
UIManager:tickAfterNext(self.leave_standby_task)
end

@ -1,10 +1,10 @@
local Device = require("device")
local Event = require("ui/event")
local PluginShare = require("pluginshare")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local time = require("ui/time")
local _ = require("gettext")
local T = require("ffi/util").template
@ -14,7 +14,7 @@ local AutoTurn = WidgetContainer:new{
autoturn_sec = 0,
autoturn_distance = 1,
enabled = false,
last_action_tv = TimeVal.zero,
last_action_time = 0,
task = nil,
}
@ -28,21 +28,21 @@ function AutoTurn:_schedule()
return
end
local delay = self.last_action_tv + TimeVal:new{ sec = self.autoturn_sec, usec = 0 } - UIManager:getTime()
delay = delay:tonumber()
local delay = self.last_action_time + time.s(self.autoturn_sec) - UIManager:getTime()
if delay <= 0 then
if UIManager:getTopWidget() == "ReaderUI" then
logger.dbg("AutoTurn: go to next page")
self.ui:handleEvent(Event:new("GotoViewRel", self.autoturn_distance))
self.last_action_tv = UIManager:getTime()
self.last_action_time = UIManager:getTime()
end
logger.dbg("AutoTurn: schedule in", self.autoturn_sec)
UIManager:scheduleIn(self.autoturn_sec, self.task)
self.scheduled = true
else
logger.dbg("AutoTurn: schedule in", delay)
UIManager:scheduleIn(delay, self.task)
local delay_s = time.to_number(delay)
logger.dbg("AutoTurn: schedule in", delay_s, "s")
UIManager:scheduleIn(delay_s, self.task)
self.scheduled = true
end
end
@ -58,10 +58,10 @@ end
function AutoTurn:_start()
if self:_enabled() then
local now_tv = UIManager:getTime()
logger.dbg("AutoTurn: start at", now_tv:tonumber())
local now = UIManager:getTime()
logger.dbg("AutoTurn: start at", time.format_time(now))
PluginShare.pause_auto_suspend = true
self.last_action_tv = now_tv
self.last_action_time = now
self:_schedule()
local text
@ -107,7 +107,7 @@ end
function AutoTurn:onInputEvent()
logger.dbg("AutoTurn: onInputEvent")
self.last_action_tv = UIManager:getTime()
self.last_action_time = UIManager:getTime()
end
function AutoTurn:onEnterStandby()
@ -122,9 +122,9 @@ function AutoTurn:onSuspend()
end
function AutoTurn:_onLeaveStandby()
self.last_action_tv = self.last_action_tv - Device.last_standby_tv
self.last_action_time = self.last_action_time - Device.last_standby_time
-- We messed with last_action_tv, so a complete reschedule has to be done.
-- We messed with last_action_time, so a complete reschedule has to be done.
if self:_enabled() then
self:_unschedule()
self:_schedule()

@ -1,6 +1,6 @@
local logger = require("logger")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local time = require("ui/time")
local CommandRunner = {
pio = nil,
@ -37,7 +37,7 @@ function CommandRunner:start(job)
assert(self.pio == nil)
assert(self.job == nil)
self.job = job
self.job.start_tv = UIManager:getTime()
self.job.start_time = UIManager:getTime()
assert(type(self.job.executable) == "string")
local command = self:createEnvironment() .. " " ..
"sh plugins/backgroundrunner.koplugin/luawrapper.sh " ..
@ -77,7 +77,7 @@ function CommandRunner:poll()
UIManager:allowStandby()
self.pio:close()
self.pio = nil
self.job.end_tv = TimeVal:now()
self.job.end_time = time.now()
local job = self.job
self.job = nil
return job

@ -9,10 +9,10 @@ end
local CommandRunner = require("commandrunner")
local PluginShare = require("pluginshare")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local time = require("ui/time")
local _ = require("gettext")
-- BackgroundRunner is an experimental feature to execute non-critical jobs in
@ -72,9 +72,9 @@ local _ = require("gettext")
-- bad_command: boolean, whether the command is not found. Not available for
-- function executable.
-- blocked: boolean, whether the job is blocked.
-- start_tv: number, the TimeVal when the job was started.
-- end_tv: number, the TimeVal when the job was stopped.
-- insert_tv: number, the TimeVal when the job was inserted into queue.
-- start_time: number, the time (fts) when the job was started.
-- end_time: number, the time (fts) when the job was stopped.
-- insert_time: number, the time (fts) when the job was inserted into queue.
-- (All of them in the monotonic time scale, like the main event loop & task queue).
local BackgroundRunner = {
@ -117,9 +117,9 @@ end
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, usec = 0 }
job.timeout = (tv_diff > threshold)
local time_diff = job.end_time - job.start_time
local threshold = time.s(1)
job.timeout = (time_diff > threshold)
end
job.blocked = job.timeout
if not job.blocked and self:_shouldRepeat(job) then
@ -141,7 +141,7 @@ function BackgroundRunner:_executeJob(job)
CommandRunner:start(job)
return true
elseif type(job.executable) == "function" then
job.start_tv = UIManager:getTime()
job.start_time = UIManager:getTime()
local status, err = pcall(job.executable)
if status then
job.result = 0
@ -149,7 +149,7 @@ function BackgroundRunner:_executeJob(job)
job.result = 1
job.exception = err
end
job.end_tv = TimeVal:now()
job.end_time = time.now()
self:_finishJob(job)
return true
else
@ -176,10 +176,10 @@ function BackgroundRunner:_execute()
local round = 0
while #self.jobs > 0 do
local job = table.remove(self.jobs, 1)
if job.insert_tv == nil then
if job.insert_time == nil then
-- Jobs are first inserted to jobs table from external users.
-- So they may not have an insert field.
job.insert_tv = UIManager:getTime()
job.insert_time = UIManager:getTime()
end
local should_execute = false
local should_ignore = false
@ -192,7 +192,7 @@ function BackgroundRunner:_execute()
end
elseif type(job.when) == "number" then
if job.when >= 0 then
should_execute = ((UIManager:getTime() - job.insert_tv) >= TimeVal:fromnumber(job.when))
should_execute = (UIManager:getTime() - job.insert_time >= time.s(job.when))
else
should_ignore = true
end
@ -253,7 +253,7 @@ end
function BackgroundRunner:_insert(job)
assert(self ~= nil)
job.insert_tv = UIManager:getTime()
job.insert_time = UIManager:getTime()
table.insert(self.jobs, job)
end

@ -8,12 +8,12 @@ of storing it.
@module koplugin.calibre.metadata
--]]--
local TimeVal = require("ui/timeval")
local lfs = require("libs/libkoreader-lfs")
local rapidjson = require("rapidjson")
local logger = require("logger")
local parser = require("parser")
local util = require("util")
local time = require("ui/time")
local used_metadata = {
"uuid",
@ -242,7 +242,7 @@ end
function CalibreMetadata:init(dir, is_search)
if not dir then return end
local start = TimeVal:now()
local start_time = time.now()
self.path = dir
local ok_meta, ok_drive, file_meta, file_drive = findCalibreFiles(dir)
self.driveinfo = file_drive
@ -261,12 +261,12 @@ function CalibreMetadata:init(dir, is_search)
if is_search then
self:cleanUnused(is_search)
msg = string.format("(search) in %.3f milliseconds: %d books",
TimeVal:getDurationMs(start), #self.books)
time.to_ms(time.since(start_time())), #self.books)
else
local deleted_count = self:prune()
self:cleanUnused()
msg = string.format("in %.3f milliseconds: %d books. %d pruned",
TimeVal:getDurationMs(start), #self.books, deleted_count)
time.to_ms(time.since(start_time())), #self.books, deleted_count)
end
logger.info(string.format("calibre info loaded from disk %s", msg))
return true

@ -13,12 +13,12 @@ local InputContainer = require("ui/widget/container/inputcontainer")
local Menu = require("ui/widget/menu")
local Persist = require("persist")
local Screen = require("device").screen
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local rapidjson = require("rapidjson")
local util = require("util")
local time = require("ui/time")
local _ = require("gettext")
local T = require("ffi/util").template
@ -325,10 +325,10 @@ function CalibreSearch:find(option)
end
-- measure time elapsed searching
local start = TimeVal:now()
local start_time = time.now()
self:browse(option)
logger.info(string.format("search done in %.3f milliseconds (%s, %s, %s, %s, %s)",
TimeVal:getDurationMs(start),
time.to_ms(time.since(start_time)),
option == "find" and "books" or option,
"case sensitive: " .. tostring(not self.case_insensitive),
"title: " .. tostring(self.find_by_title),
@ -552,7 +552,7 @@ end
-- get metadata from cache or calibre files
function CalibreSearch:getMetadata()
local start = TimeVal:now()
local start_time = time.now()
local template = "metadata: %d books imported from %s in %.3f milliseconds"
-- try to load metadata from cache
@ -594,7 +594,7 @@ function CalibreSearch:getMetadata()
end
end
if is_newer then
logger.info(string.format(template, #cache, "cache", TimeVal:getDurationMs(start)))
logger.info(string.format(template, #cache, "cache", time.to_ms(time.since(start_time))))
return cache
else
logger.warn("cache is older than metadata, ignoring it")
@ -623,7 +623,7 @@ function CalibreSearch:getMetadata()
logger.info("Failed to serialize calibre metadata cache:", err)
end
end
logger.info(string.format(template, #books, "calibre", TimeVal:getDurationMs(start)))
logger.info(string.format(template, #books, "calibre", time.to_ms(time.since(start_time))))
return books
end

@ -7,12 +7,12 @@ local FFIUtil = require("ffi/util")
local InfoMessage = require("ui/widget/infomessage")
local RenderImage = require("ui/renderimage")
local SQ3 = require("lua-ljsqlite3/init")
local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local util = require("util")
local zstd = require("ffi/zstd")
local time = require("ui/time")
local _ = require("gettext")
local N_ = _.ngettext
local T = FFIUtil.template
@ -137,8 +137,8 @@ function BookInfoManager:init()
self.subprocesses_collector = nil
self.subprocesses_collect_interval = 10 -- do that every 10 seconds
self.subprocesses_pids = {}
self.subprocesses_last_added_tv = TimeVal.zero
self.subprocesses_killall_timeout_tv = TimeVal:new{ sec = 300, usec = 0 } -- cleanup timeout for stuck subprocesses
self.subprocesses_last_added_time = 0
self.subprocesses_killall_timeout_time = time.s(300) -- cleanup timeout for stuck subprocesses
-- 300 seconds should be more than enough to open and get info from 9-10 books
-- Whether to use former blitbuffer:scale() (default to using MuPDF)
self.use_legacy_image_scaling = G_reader_settings:isTrue("legacy_image_scaling")
@ -657,7 +657,7 @@ function BookInfoManager:collectSubprocesses()
-- the user has not left FileManager or changed page - that would
-- have caused a terminateBackgroundJobs() - if we're here, it's
-- that user has left reader in FileBrower and went away)
if TimeVal:now() > self.subprocesses_last_added_tv + self.subprocesses_killall_timeout_tv then
if time.now() > self.subprocesses_last_added_time + self.subprocesses_killall_timeout_time then
logger.warn("Some subprocesses were running for too long, killing them")
self:terminateBackgroundJobs()
-- we'll collect them next time we're run
@ -730,7 +730,7 @@ function BookInfoManager:extractInBackground(files)
-- counter on each task, and undo that inside collectSubprocesses() zombie reaper.
UIManager:preventStandby()
table.insert(self.subprocesses_pids, task_pid)
self.subprocesses_last_added_tv = TimeVal:now()
self.subprocesses_last_added_time = time.now()
-- We need to collect terminated jobs pids (so they do not stay "zombies"
-- and fill linux processes table)

@ -17,6 +17,7 @@ local SpinWidget = require("ui/widget/spinwidget")
local UIManager = require("ui/uimanager")
local util = require("util")
local T = FFIUtil.template
local time = require("ui/time")
local _ = require("gettext")
local logger = require("logger")
@ -478,16 +479,16 @@ Any other taps made within this interval after a first tap will be considered ac
The interval value is in milliseconds and can range from 0 (0 seconds) to 2000 (2 seconds).]]),
width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_tap_interval")/1000,
value = time.to_ms(GestureDetector.ges_tap_interval),
value_min = 0,
value_max = 2000,
value_step = 50,
value_hold_step = 200,
ok_text = _("Set interval"),
default_value = GestureDetector.TAP_INTERVAL/1000,
default_value = GestureDetector.TAP_INTERVAL_MS,
callback = function(spin)
G_reader_settings:saveSetting("ges_tap_interval", spin.value*1000)
GestureDetector:setNewInterval("ges_tap_interval", spin.value*1000)
G_reader_settings:saveSetting("ges_tap_interval_ms", spin.value)
GestureDetector.ges_tap_interval = time.ms(spin.value)
end
}
UIManager:show(items)
@ -504,7 +505,7 @@ Any other taps made within this interval after a first tap will be considered ac
The interval value is in milliseconds and can range from 0 (0 seconds) to 2000 (2 seconds).]]),
width = math.floor(Screen:getWidth() * 0.75),
value = G_reader_settings:readSetting("ges_tap_interval_on_keyboard", 0)/1000,
value = time.to_ms(G_reader_settings:readSetting("ges_tap_interval_on_keyboard_ms", 0)),
value_min = 0,
value_max = 2000,
value_step = 50,
@ -512,7 +513,7 @@ The interval value is in milliseconds and can range from 0 (0 seconds) to 2000 (
ok_text = _("Set interval"),
default_value = 0,
callback = function(spin)
G_reader_settings:saveSetting("ges_tap_interval_on_keyboard", spin.value*1000)
G_reader_settings:saveSetting("ges_tap_interval_on_keyboard_ms", spin.value)
end
}
UIManager:show(items)
@ -529,16 +530,16 @@ When double tap is enabled, this sets the time to wait for the second tap. A sin
The interval value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).]]),
width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_double_tap_interval")/1000,
value = time.to_ms(GestureDetector.ges_double_tap_interval),
value_min = 100,
value_max = 2000,
value_step = 100,
value_hold_step = 500,
ok_text = _("Set interval"),
default_value = GestureDetector.DOUBLE_TAP_INTERVAL/1000,
default_value = GestureDetector.DOUBLE_TAP_INTERVAL_MS,
callback = function(spin)
G_reader_settings:saveSetting("ges_double_tap_interval", spin.value*1000)
GestureDetector:setNewInterval("ges_double_tap_interval", spin.value*1000)
G_reader_settings:saveSetting("ges_double_tap_interval_ms", spin.value)
GestureDetector.ges_double_tap_interval = time.ms(spin.value)
end
}
UIManager:show(items)
@ -555,16 +556,16 @@ This sets the allowed duration of any of the two fingers touch/release for the c
The duration value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).]]),
width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_two_finger_tap_duration")/1000,
value = time.to_ms(GestureDetector.ges_two_finger_tap_duration),
value_min = 100,
value_max = 2000,
value_step = 100,
value_hold_step = 500,
ok_text = _("Set duration"),
default_value = GestureDetector.TWO_FINGER_TAP_DURATION/1000,
default_value = GestureDetector.TWO_FINGER_TAP_DURATION_MS,
callback = function(spin)
G_reader_settings:saveSetting("ges_two_finger_tap_duration", spin.value*1000)
GestureDetector:setNewInterval("ges_two_finger_tap_duration", spin.value*1000)
G_reader_settings:saveSetting("ges_two_finger_tap_duration_ms", spin.value)
GestureDetector.ges_two_finger_tap_duration = time.ms(spin.value)
end
}
UIManager:show(items)
@ -581,16 +582,16 @@ If a touch is not released in this interval, it is considered a long-press. On d
The interval value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).]]),
width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_hold_interval")/1000,
value = time.to_ms(GestureDetector.ges_hold_interval),
value_min = 100,
value_max = 2000,
value_step = 100,
value_hold_step = 500,
ok_text = _("Set interval"),
default_value = GestureDetector.HOLD_INTERVAL/1000,
default_value = GestureDetector.HOLD_INTERVAL_MS,
callback = function(spin)
G_reader_settings:saveSetting("ges_hold_interval", spin.value*1000)
GestureDetector:setNewInterval("ges_hold_interval", spin.value*1000)
G_reader_settings:saveSetting("ges_hold_interval_ms", spin.value)
GestureDetector.ges_hold_interval = time.ms(spin.value)
end
}
UIManager:show(items)
@ -607,16 +608,16 @@ This sets the maximum delay between the start and the end of a swipe for it to b
The interval value is in milliseconds and can range from 100 (0.1 seconds) to 2000 (2 seconds).]]),
width = math.floor(Screen:getWidth() * 0.75),
value = GestureDetector:getInterval("ges_swipe_interval")/1000,
value = time.to_ms(GestureDetector.ges_swipe_interval),
value_min = 100,
value_max = 2000,
value_step = 100,
value_hold_step = 500,
ok_text = _("Set interval"),
default_value = GestureDetector.SWIPE_INTERVAL/1000,
default_value = GestureDetector.SWIPE_INTERVAL_MS,
callback = function(spin)
G_reader_settings:saveSetting("ges_swipe_interval", spin.value*1000)
GestureDetector:setNewInterval("ges_swipe_interval", spin.value*1000)
G_reader_settings:saveSetting("ges_swipe_interval_ms", spin.value)
GestureDetector.ges_swipe_interval = time.ms(spin.value)
end
}
UIManager:show(items)

@ -3,6 +3,7 @@ local Dispatcher = require("dispatcher")
local KeyValuePage = require("ui/widget/keyvaluepage")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local time = require("ui/time")
local util = require("util")
local _ = require("gettext")
@ -48,11 +49,11 @@ function SystemStat:appendCounters()
util.secondsToClockDuration("", os.difftime(os.time(), self.start_sec), false, true, true)})
if Device:canSuspend() then
self:put({" " .. _("Time in suspend"),
util.secondsToClockDuration("", Device.total_suspend_tv:tonumber(), false, true, true)})
util.secondsToClockDuration("", time.to_number(Device.total_suspend_time), false, true, true)})
end
if Device:canStandby() then
self:put({" " .. _("Time in standby"),
util.secondsToClockDuration("", Device.total_standby_tv:tonumber(), false, true, true)})
util.secondsToClockDuration("", time.to_number(Device.total_standby_time), false, true, true)})
end
self:put({_("Counters"), ""})
self:put({_(" wake-ups"), self.wakeup_count})

@ -39,6 +39,7 @@ describe("BackgroundRunner widget tests", function()
MockTime:increase(2)
UIManager:handleInput()
assert.is_false(executed)
MockTime:increase(9)
UIManager:handleInput()
assert.is_false(executed)
@ -132,7 +133,7 @@ describe("BackgroundRunner widget tests", function()
table.insert(PluginShare.backgroundJobs, job)
notifyBackgroundJobsUpdated()
while job.end_tv == nil do
while job.end_time == nil do
MockTime:increase(2)
UIManager:handleInput()
end
@ -157,7 +158,7 @@ describe("BackgroundRunner widget tests", function()
table.insert(PluginShare.backgroundJobs, job)
notifyBackgroundJobsUpdated()
while job.end_tv == nil do
while job.end_time == nil do
MockTime:increase(2)
UIManager:handleInput()
end
@ -171,11 +172,11 @@ describe("BackgroundRunner widget tests", function()
ENV1 = "yes",
ENV2 = "no",
}
job.end_tv = nil
job.end_time = nil
table.insert(PluginShare.backgroundJobs, job)
notifyBackgroundJobsUpdated()
while job.end_tv == nil do
while job.end_time == nil do
MockTime:increase(2)
UIManager:handleInput()
end
@ -206,7 +207,7 @@ describe("BackgroundRunner widget tests", function()
table.insert(PluginShare.backgroundJobs, job)
notifyBackgroundJobsUpdated()
while job.end_tv == nil do
while job.end_time == nil do
MockTime:increase(2)
UIManager:handleInput()
end
@ -216,12 +217,12 @@ describe("BackgroundRunner widget tests", function()
assert.is_false(job.timeout)
assert.is_false(job.bad_command)
job.end_tv = nil
job.end_time = nil
env2 = "no"
table.insert(PluginShare.backgroundJobs, job)
notifyBackgroundJobsUpdated()
while job.end_tv == nil do
while job.end_time == nil do
MockTime:increase(2)
UIManager:handleInput()
end
@ -244,7 +245,7 @@ describe("BackgroundRunner widget tests", function()
table.insert(PluginShare.backgroundJobs, job)
notifyBackgroundJobsUpdated()
while job.end_tv == nil do
while job.end_time == nil do
MockTime:increase(2)
UIManager:handleInput()
end

@ -1,5 +1,6 @@
require("commonrequire")
local TimeVal = require("ui/timeval")
local time = require("ui/time")
local ffi = require("ffi")
local dummy = require("ffi/posix_h")
local logger = require("logger")
@ -21,6 +22,10 @@ local MockTime = {
realtime = 0,
boottime = 0,
boottime_or_realtime_coarse = 0,
monotonic_time = 0,
realtime_time = 0,
boottime_time = 0,
boottime_or_realtime_coarse_time = 0,
}
function MockTime:install()
@ -100,7 +105,72 @@ function MockTime:install()
logger.dbg("MockTime:TimeVal.now: ", self.monotonic)
return TimeVal:new{ sec = self.monotonic }
end
end
if self.original_tv_realtime_time == nil then
self.original_tv_realtime_time = time.realtime
assert(self.original_tv_realtime_time ~= nil)
end
if self.original_tv_realtime_coarse_time == nil then
self.original_tv_realtime_coarse_time = time.realtime_coarse
assert(self.original_tv_realtime_coarse_time ~= nil)
end
if self.original_tv_monotonic_time == nil then
self.original_tv_monotonic_time = time.monotonic
assert(self.original_tv_monotonic_time ~= nil)
end
if self.original_tv_monotonic_coarse_time == nil then
self.original_tv_monotonic_coarse_time = time.monotonic_coarse
assert(self.original_tv_monotonic_coarse_time ~= nil)
end
if self.original_tv_boottime_time == nil then
self.original_tv_boottime_time = time.boottime
assert(self.original_tv_boottime_time ~= nil)
end
if self.original_tv_boottime_or_realtime_coarse_time == nil then
self.original_tv_boottime_or_realtime_coarse_time = time.boottime_or_realtime_coarse
assert(self.original_tv_boottime_or_realtime_coarse_time ~= nil)
end
if self.original_tv_now == nil then
self.original_tv_now = time.now
assert(self.original_tv_now ~= nil)
end
-- Store both REALTIME & MONOTONIC clocks for fts
self.realtime_time = os.time() * 1e6
local timespec_time = ffi.new("struct timespec")
C.clock_gettime(C.CLOCK_MONOTONIC_COARSE, timespec_time)
self.monotonic = tonumber(timespec.tv_sec) * 1e6
time.realtime = function()
logger.dbg("MockTime:TimeVal.realtime: ", self.realtime_time)
return self.realtime_time
end
time.realtime_coarse = function()
logger.dbg("MockTime:TimeVal.realtime_coarse: ", self.realtime_coarse_time)
return self.realtime_coarse_time
end
time.monotonic = function()
logger.dbg("MockTime:TimeVal.monotonic: ", self.monotonic)
return self.monotonic_time
end
time.monotonic_coarse = function()
logger.dbg("MockTime:TimeVal.monotonic_coarse: ", self.monotonic)
return self.monotonic_time
end
time.boottime = function()
logger.dbg("MockTime:TimeVal.boottime: ", self.boottime_time)
return self.boottime_time
end
time.boottime_or_realtime_coarse = function()
logger.dbg("MockTime:TimeVal.boottime: ", self.boottime_or_realtime_coarse_time)
return self.boottime_or_realtime_coarse_time
end
time.now = function()
logger.dbg("MockTime:TimeVal.now: ", self.monotonic)
return self.monotonic_time
end
end
function MockTime:uninstall()
assert(self ~= nil)
@ -240,6 +310,17 @@ function MockTime:increase(value)
logger.dbg("MockTime:increase (boottime) ", self.boottime)
self.boottime_or_realtime_coarse = math.floor(self.boottime_or_realtime_coarse + value)
logger.dbg("MockTime:increase (boottime) ", self.boottime_or_realtime_coarse)
local value_time = value * 1e6
self.realtime_time = math.floor(self.realtime_time + value_time)
logger.dbg("MockTime:increase (realtime) ", self.realtime_time)
self.monotonic_time = math.floor(self.monotonic_time + value_time)
logger.dbg("MockTime:increase (monotonic) ", self.monotonic)
self.boottime_time = math.floor(self.boottime_time + value_time)
logger.dbg("MockTime:increase (boottime) ", self.boottime_time)
self.boottime_or_realtime_coarse_time = math.floor(self.boottime_or_realtime_coarse_time + value_time)
logger.dbg("MockTime:increase (boottime) ", self.boottime_or_realtime_coarse_time)
return true
end

@ -0,0 +1,135 @@
describe("Time module", function()
local time, dbg, dbg_on
setup(function()
require("commonrequire")
time = require("ui/time")
dbg = require("dbg")
dbg_on = dbg.is_on
end)
after_each(function()
if dbg_on then
dbg:turnOn()
else
dbg:turnOff()
end
end)
it("should set", function()
local time1 = time.s(12)
local time2 = time.ms(12)
local time3 = time.us(12)
assert.is.same(12000000, time1)
assert.is.same(12000, time2)
assert.is.same(12, time3)
end)
it("should convert", function()
local time1 = 12000000
local time2 = 12000
local time3 = 12
local time4 = time.s(12) + time.us(40)
local time5 = time.s(12) + time.us(60)
assert.is.same(12, time.to_s(time1))
assert.is.same(12000, time.to_ms(time1))
assert.is.same(12000000, time.to_us(time1))
assert.is.same(0.012, time.to_s(time2))
assert.is.same(12, time.to_ms(time2))
assert.is.same(12000, time.to_us(time2))
assert.is.same(0.000012, time.to_s(time3))
assert.is.same(math.floor(0.012), time.to_ms(time3))
assert.is.same(12, time.to_us(time3))
assert.is.same(12.0000, time.to_number(time4))
assert.is.same(12.0001, time.to_number(time5))
end)
it("should add", function()
local time1 = time.s(5) + time.us(5000)
local time2 = time.s(10) + time.us(6000)
local time3 = time.s(10) + time.us(50000000)
assert.is.same(time.s(15) + time.us(11000), time1 + time2)
assert.is.same(time.s(65) + time.us(5000), time1 + time3)
end)
it("should subtract", function()
local time1 = time.s(5.005)
local time2 = time.s(10.006)
assert.is.same(time.s(5.001), time2 - time1)
local backwards_sub = time1 - time2
assert.is.same(time.s(-5.001), backwards_sub)
-- Check that to/from float conversions behave, even for negative values.
assert.is.same(-5.001, time.to_number(backwards_sub))
assert.is.same(time.s(-6) + time.us(999000), time.s(-5.001))
local tv = time.s(-6) + time.us(1000)
assert.is.same(-5.999, time.to_number(tv))
assert.is.same(time.s(-6) + time.us(1000), time.s(-5.999))
-- We lose precision because of rounding if we go higher resolution than a ms...
tv = time.s(-6) + time.us(101)
assert.is.same(-5.9999, time.to_number(tv))
assert.is.same(time.s(-6) + time.us(100), time.s(-5.9999))
-- ^ precision loss
tv = time.s(-6) + time.us(11)
assert.is.same(-6, time.to_number(tv))
-- ^ precision loss
assert.is.same(time.s(-6) + time.us(10), time.s(-5.99999))
-- ^ precision loss
tv = time.s(-6) + time.us(1)
assert.is.same(-6, time.to_number(tv))
-- ^ precision loss
assert.is.same(time.s(-6) + time.us(1), time.s(-5.999999))
end)
it("should derive sec and usec from more than 1 sec worth of usec", function()
local time1 = time.s(5) + time.us(5000000)
assert.is.same(time.s(10), time1)
end)
it("should compare", function()
local time1 = time.s(5) + time.us(5000)
local time2 = time.s(10) + time.us(6000)
local time3 = time.s(5) + time.us(5000)
local time4 = time.s(5) + time.us(6000)
assert.is_true(time2 > time1)
assert.is_false(time2 < time1)
assert.is_true(time2 >= time1)
assert.is_true(time4 > time1)
assert.is_false(time4 < time1)
assert.is_true(time4 >= time1)
assert.is_true(time1 < time2)
assert.is_false(time1 > time2)
assert.is_true(time1 <= time2)
assert.is_true(time1 == time3)
assert.is_false(time1 == time2)
assert.is_true(time1 >= time3)
assert.is_true(time1 <= time3)
end)
it("should calculate durations", function()
local time1 = time.s(5) + time.us(500000)
local function now() return time.s(10) end
local now_save = time.now
time.now = now
assert.is.equal(time.to_s(time.s(4) + time.us(500000)), time.to_s(time.since(time1)))
assert.is.equal(time.to_ms(time.s(4) + time.us(500000)), time.to_ms(time.since(time1)))
assert.is.equal(time.to_us(time.s(4) + time.us(500000)), time.to_us(time.since(time1)))
time.now = now_save
end)
end)

@ -1,50 +1,54 @@
require("commonrequire")
local util = require("ffi/util")
local UIManager = require("ui/uimanager")
local time = require("ui/time")
local NB_TESTS = 40000
local noop = function() end
describe("UIManager checkTasks benchmark", function()
local now = { util.gettime() }
local now = time.now()
local wait_until -- luacheck: no unused
UIManager:quit()
UIManager._task_queue = {}
for i=1,1000000 do
for i=1, NB_TESTS do
table.insert(
UIManager._task_queue,
{ time = { now[1] + 10000+i, now[2] }, action = noop }
{ time = now + i, action = noop, argc = 0, args = {} }
)
end
-- for i=1,1000 do
for i=1, NB_TESTS do
wait_until, now = UIManager:_checkTasks() -- luacheck: no unused
-- end
end
end)
describe("UIManager schedule benchmark", function()
local now = { util.gettime() }
local now = time.now()
UIManager:quit()
UIManager._task_queue = {}
for i=1,100000 do
UIManager:schedule({ now[1] + i, now[2] }, noop)
for i=1, NB_TESTS do
UIManager:schedule(now + i, noop)
end
end)
describe("UIManager unschedule benchmark", function()
local now = { util.gettime() }
local now = time.now()
UIManager:quit()
UIManager._task_queue = {}
for i=1,1000 do
for i=1, NB_TESTS do
table.insert(
UIManager._task_queue,
{ time = { now[1] + 10000+i, now[2] }, action = 'a' }
{ time = now + i, action = 'a', argc=0, args={} }
)
end
for i=1,1000 do
UIManager:schedule(now, noop)
for i=1, NB_TESTS do
UIManager:schedule(now + i, noop)
UIManager:unschedule(noop)
end
end)

@ -1,22 +1,22 @@
describe("UIManager spec", function()
local TimeVal, UIManager
local time, UIManager
local now, wait_until
local noop = function() end
setup(function()
require("commonrequire")
TimeVal = require("ui/timeval")
time = require("ui/time")
UIManager = require("ui/uimanager")
end)
it("should consume due tasks", function()
now = TimeVal:now()
local future = TimeVal:new{ sec = now.sec + 60000, usec = now.usec }
local future2 = TimeVal:new{ sec = future.sec + 5, usec = future.usec}
now = time.now()
local future = now + time.s(60000)
local future2 = future + time.s(5)
UIManager:quit()
UIManager._task_queue = {
{ time = TimeVal:new{ sec = now.sec - 10, usec = now.usec }, action = noop, args = {}, argc = 0 },
{ time = TimeVal:new{ sec = now.sec, usec = now.usec - 5 }, action = noop, args = {}, argc = 0 },
{ time = now - time.s(10), action = noop, args = {}, argc = 0 },
{ time = now - time.us(5), action = noop, args = {}, argc = 0 },
{ time = now, action = noop, args = {}, argc = 0 },
{ time = future, action = noop, args = {}, argc = 0 },
{ time = future2, action = noop, args = {}, argc = 0 },
@ -28,26 +28,25 @@ describe("UIManager spec", function()
end)
it("should calcualte wait_until properly in checkTasks routine", function()
now = TimeVal:now()
local future = TimeVal:new{ sec = now.sec + 60000, usec = now.usec }
now = time.now()
local future_time = now + time.s(60000)
UIManager:quit()
UIManager._task_queue = {
{ time = TimeVal:new{ sec = now.sec - 10, usec = now.usec }, action = noop, args = {}, argc = 0 },
{ time = TimeVal:new{ sec = now.sec, usec = now.usec - 5 }, action = noop, args = {}, argc = 0 },
{ time = now - time.s(10), action = noop, args = {}, argc = 0 },
{ time = now - time.us(5), action = noop, args = {}, argc = 0 },
{ time = now, action = noop, args = {}, argc = 0 },
{ time = future, action = noop, args = {}, argc = 0 },
{ time = TimeVal:new{ sec = future.sec + 5, usec = future.usec }, action = noop, args = {}, argc = 0 },
{ time = future_time, action = noop, args = {}, argc = 0 },
}
wait_until, now = UIManager:_checkTasks()
assert.are.same(future, wait_until)
assert.are.same(future_time, wait_until)
end)
it("should return nil wait_until properly in checkTasks routine", function()
now = TimeVal:now()
now = time.now()
UIManager:quit()
UIManager._task_queue = {
{ time = TimeVal:new{ sec = now.sec - 10, usec = now.usec }, action = noop, args = {}, argc = 0 },
{ time = TimeVal:new{ sec = now.sec, usec = now.usec - 5 }, action = noop, args = {}, argc = 0 },
{ time = now - time.s(10), action = noop, args = {}, argc = 0 },
{ time = now - time.us(5), action = noop, args = {}, argc = 0 },
{ time = now, action = noop, args = {}, argc = 0 },
}
wait_until, now = UIManager:_checkTasks()
@ -55,7 +54,7 @@ describe("UIManager spec", function()
end)
it("should insert new task properly in empty task queue", function()
now = TimeVal:now()
now = time.now()
UIManager:quit()
UIManager._task_queue = {}
assert.are.same(0, #UIManager._task_queue)
@ -65,11 +64,11 @@ describe("UIManager spec", function()
end)
it("should insert new task properly in single task queue", function()
now = TimeVal:now()
local future = TimeVal:new{ sec = now.sec + 10000, usec = now.usec }
now = time.now()
local future_time = now + time.s(10000)
UIManager:quit()
UIManager._task_queue = {
{ time = future, action = '1', args = {}, argc = 0 },
{ time = future_time, action = '1', args = {}, argc = 0 },
}
assert.are.same(1, #UIManager._task_queue)
UIManager:scheduleIn(150, 'quz')
@ -90,59 +89,59 @@ describe("UIManager spec", function()
end)
it("should insert new task in ascendant order", function()
now = TimeVal:now()
now = time.now()
UIManager:quit()
UIManager._task_queue = {
{ time = TimeVal:new{ sec = now.sec - 10, usec = now.usec }, action = '1', args = {}, argc = 0 },
{ time = TimeVal:new{ sec = now.sec, usec = now.usec - 5 }, action = '2', args = {}, argc = 0 },
{ time = now - time.s(10), action = '1', args = {}, argc = 0 },
{ time = now - time.us(5), action = '2', args = {}, argc = 0 },
{ time = now, action = '3', args = {}, argc = 0 },
}
-- insert into the tail slot
UIManager:scheduleIn(10, 'foo')
assert.are.same('foo', UIManager._task_queue[4].action)
-- insert into the second slot
UIManager:schedule(TimeVal:new{ sec = now.sec - 5, usec = now.usec }, 'bar')
UIManager:schedule(now - time.s(5), 'bar')
assert.are.same('bar', UIManager._task_queue[2].action)
-- insert into the head slot
UIManager:schedule(TimeVal:new{ sec = now.sec - 15, usec = now.usec }, 'baz')
UIManager:schedule(now - time.s(15), 'baz')
assert.are.same('baz', UIManager._task_queue[1].action)
-- insert into the last second slot
UIManager:scheduleIn(5, 'qux')
assert.are.same('qux', UIManager._task_queue[6].action)
-- insert into the middle slot
UIManager:schedule(TimeVal:new{ sec = now.sec, usec = now.usec - 1 }, 'quux')
UIManager:schedule(now - time.us(1), 'quux')
assert.are.same('quux', UIManager._task_queue[5].action)
end)
it("should unschedule all the tasks with the same action", function()
now = TimeVal:now()
now = time.now()
UIManager:quit()
UIManager._task_queue = {
{ time = TimeVal:new{ sec = now.sec - 15, usec = now.usec }, action = '3', args = {}, argc = 0 },
{ time = TimeVal:new{ sec = now.sec - 10, usec = now.usec }, action = '1', args = {}, argc = 0 },
{ time = TimeVal:new{ sec = now.sec, usec = now.usec - 6 }, action = '3', args = {}, argc = 0 },
{ time = TimeVal:new{ sec = now.sec, usec = now.usec - 5 }, action = '2', args = {}, argc = 0 },
{ time = now - time.s(15), action = '3', args = {}, argc = 0 },
{ time = now - time.s(10), action = '1', args = {}, argc = 0 },
{ time = now - time.us(6), action = '3', args = {}, argc = 0 },
{ time = now - time.us(5), action = '2', args = {}, argc = 0 },
{ time = now, action = '3', args = {}, argc = 0 },
}
-- insert into the tail slot
UIManager:unschedule('3')
assert.are.same({
{ time = TimeVal:new{ sec = now.sec - 10, usec = now.usec }, action = '1', args = {}, argc = 0 },
{ time = TimeVal:new{ sec = now.sec, usec = now.usec - 5 }, action = '2', args = {}, argc = 0 },
{ time = now - time.s(10), action = '1', args = {}, argc = 0 },
{ time = now - time.us(5), action = '2', args = {}, argc = 0 },
}, UIManager._task_queue)
end)
it("should not have race between unschedule and _checkTasks", function()
now = TimeVal:now()
now = time.now()
local run_count = 0
local task_to_remove = function()
run_count = run_count + 1
end
UIManager:quit()
UIManager._task_queue = {
{ time = TimeVal:new{ sec = now.sec, usec = now.usec - 5 }, action = task_to_remove, args = {}, argc = 0 },
{ time = now - time.us(5), action = task_to_remove, args = {}, argc = 0 },
{
time = TimeVal:new{ sec = now.sec - 10, usec = now.usec },
time = now - time.s(10),
action = function()
run_count = run_count + 1
UIManager:unschedule(task_to_remove)

Loading…
Cancel
Save