mirror of
https://github.com/koreader/koreader
synced 2024-10-31 21:20:20 +00:00
591 lines
25 KiB
Lua
591 lines
25 KiB
Lua
local Device = require("device")
|
|
local Screen = Device.screen
|
|
local Input = require("device").input
|
|
local Event = require("ui/event")
|
|
local util = require("ffi/util")
|
|
local DEBUG = require("dbg")
|
|
local _ = require("gettext")
|
|
|
|
-- cf. koreader-base/ffi-cdecl/include/mxcfb-kindle.h (UPDATE_MODE_* applies to Kobo, too)
|
|
local UPDATE_MODE_PARTIAL = 0x0
|
|
local UPDATE_MODE_FULL = 0x1
|
|
|
|
-- Kindle waveform update modes
|
|
local WAVEFORM_MODE_INIT = 0x0 -- Screen goes to white (clears)
|
|
local WAVEFORM_MODE_DU = 0x1 -- Grey->white/grey->black
|
|
local WAVEFORM_MODE_GC16 = 0x2 -- High fidelity (flashing)
|
|
local WAVEFORM_MODE_GC4 = WAVEFORM_MODE_GC16 -- For compatibility
|
|
local WAVEFORM_MODE_GC16_FAST = 0x3 -- Medium fidelity
|
|
local WAVEFORM_MODE_A2 = 0x4 -- Faster but even lower fidelity
|
|
local WAVEFORM_MODE_GL16 = 0x5 -- High fidelity from white transition
|
|
local WAVEFORM_MODE_GL16_FAST = 0x6 -- Medium fidelity from white transition
|
|
-- Kindle FW >= 5.3
|
|
local WAVEFORM_MODE_DU4 = 0x7 -- Medium fidelity 4 level of gray direct update
|
|
-- Kindle PW2
|
|
local WAVEFORM_MODE_REAGL = 0x8 -- Ghost compensation waveform
|
|
local WAVEFORM_MODE_REAGLD = 0x9 -- Ghost compensation waveform with dithering
|
|
-- Kindle Basic/Kindle Voyage
|
|
local WAVEFORM_MODE_GL4 = 0xA -- 2-bit from white transition
|
|
-- TODO: Use me in night mode on those devices?
|
|
local WAVEFORM_MODE_GL16_INV = 0xB -- High fidelity for black transition
|
|
|
|
-- Kobo waveform update modes
|
|
local NTX_WFM_MODE_INIT = 0x0 -- WAVEFORM_MODE_INIT
|
|
local NTX_WFM_MODE_DU = 0x1 -- WAVEFORM_MODE_DU
|
|
local NTX_WFM_MODE_GC16 = 0x2 -- WAVEFORM_MODE_GC16
|
|
local NTX_WFM_MODE_GC4 = 0x3 -- WAVEFORM_MODE_GC4
|
|
local NTX_WFM_MODE_A2 = 0x4 -- WAVEFORM_MODE_A2
|
|
local NTX_WFM_MODE_GL16 = 0x5 -- WAVEFORM_MODE_GL16
|
|
local NTX_WFM_MODE_GLR16 = 0x6 -- WAVEFORM_MODE_REAGL
|
|
local NTX_WFM_MODE_GLD16 = 0x7 -- WAVEFORM_MODE_REAGLD
|
|
|
|
-- Common
|
|
local WAVEFORM_MODE_AUTO = 0x101
|
|
|
|
|
|
-- there is only one instance of this
|
|
local UIManager = {
|
|
default_refresh_type = UPDATE_MODE_PARTIAL,
|
|
default_waveform_mode = WAVEFORM_MODE_GC16, -- high fidelity waveform
|
|
fast_waveform_mode = WAVEFORM_MODE_A2,
|
|
full_refresh_waveform_mode = WAVEFORM_MODE_GC16,
|
|
partial_refresh_waveform_mode = WAVEFORM_MODE_GC16,
|
|
wait_for_every_marker = false,
|
|
regal_always_full = true,
|
|
-- force to repaint all the widget is stack, will be reset to false
|
|
-- after each ui loop
|
|
repaint_all = false,
|
|
-- force to do full refresh, will be reset to false
|
|
-- after each ui loop
|
|
full_refresh = false,
|
|
-- force to do partial refresh, will be reset to false
|
|
-- after each ui loop
|
|
partial_refresh = false,
|
|
-- trigger a full refresh when counter reaches FULL_REFRESH_COUNT
|
|
FULL_REFRESH_COUNT = G_reader_settings:readSetting("full_refresh_count") or DRCOUNTMAX,
|
|
refresh_count = 0,
|
|
|
|
event_handlers = nil,
|
|
|
|
_running = true,
|
|
_window_stack = {},
|
|
_execution_stack = {},
|
|
_dirty = {},
|
|
_zeromqs = {},
|
|
}
|
|
|
|
function UIManager:init()
|
|
self.event_handlers = {
|
|
__default__ = function(input_event)
|
|
self:sendEvent(input_event)
|
|
end,
|
|
SaveState = function()
|
|
self:sendEvent(Event:new("FlushSettings"))
|
|
end,
|
|
Power = function(input_event)
|
|
Device:onPowerEvent(input_event)
|
|
end,
|
|
}
|
|
if Device:isKobo() then
|
|
self.event_handlers["Suspend"] = function(input_event)
|
|
self:sendEvent(Event:new("FlushSettings"))
|
|
Device:onPowerEvent(input_event)
|
|
end
|
|
self.event_handlers["Resume"] = function(input_event)
|
|
Device:onPowerEvent(input_event)
|
|
self:sendEvent(Event:new("Resume"))
|
|
end
|
|
self.event_handlers["Light"] = function()
|
|
Device:getPowerDevice():toggleFrontlight()
|
|
end
|
|
self.event_handlers["__default__"] = function(input_event)
|
|
if Device.screen_saver_mode then
|
|
-- Suspension in Kobo can be interrupted by screen updates. We
|
|
-- ignore user touch input here so screen udpate won't be
|
|
-- triggered in suspend mode
|
|
return
|
|
else
|
|
self:sendEvent(input_event)
|
|
end
|
|
end
|
|
if KOBO_LIGHT_ON_START and tonumber(KOBO_LIGHT_ON_START) > -1 then
|
|
Device:getPowerDevice():setIntensity( math.max( math.min(KOBO_LIGHT_ON_START,100) ,0) )
|
|
end
|
|
-- Emulate the stock reader's refresh behavior...
|
|
self.full_refresh_waveform_mode = NTX_WFM_MODE_GC16
|
|
-- Request REAGLD waveform mode on devices that support it (Aura & H2O)
|
|
if Device.model == "Kobo_phoenix" or Device.model == "Kobo_dahlia" then
|
|
self.partial_refresh_waveform_mode = NTX_WFM_MODE_GLD16
|
|
-- Since Kobo doesn't have MXCFB_WAIT_FOR_UPDATE_SUBMISSION, enabling this currently has no effect :).
|
|
self.wait_for_every_marker = true
|
|
-- The H2O appears to be the odd duck... Nickel uses AUTO for PARTIAL updates instead of asking for REAGLD specifically...
|
|
-- For now, try asking for REAGLD, but don't switch them to FULL, since that triggers a black flash on the H2O...
|
|
-- Strangely enough, if I follow the kernel sources correctly, and they aren't using dirty tricks (like enabling some switches at compile time...),
|
|
-- PARTIAL, AUTO updates should default to NTX_WFM_MODE_GL16 on the H2O, which isn't REAGL at all, so, err, WTF?
|
|
-- FIXME: Either live like this, or try switchng to NTX_WFM_MODE_GLR16, which appears to be the right thing for PARTIAL REAGL,
|
|
-- or simply stop trying to figure it out and go AUTO...
|
|
if Device.model == "Kobo_dahlia" then
|
|
self.regal_always_full = false
|
|
--self.partial_refresh_waveform_mode = WAVEFORM_MODE_AUTO
|
|
end
|
|
else
|
|
-- See the note in the Kindle code path later, the stock reader might be using AUTO
|
|
self.partial_refresh_waveform_mode = NTX_WFM_MODE_GL16
|
|
self.wait_for_every_marker = false
|
|
end
|
|
-- Let the driver decide what to do with PARTIAL, regional updates...
|
|
self.default_waveform_mode = WAVEFORM_MODE_AUTO
|
|
elseif Device:isKindle() then
|
|
self.event_handlers["IntoSS"] = function()
|
|
self:sendEvent(Event:new("FlushSettings"))
|
|
Device:intoScreenSaver()
|
|
end
|
|
self.event_handlers["OutOfSS"] = function()
|
|
Device:outofScreenSaver()
|
|
self:sendEvent(Event:new("Resume"))
|
|
end
|
|
self.event_handlers["Charging"] = function()
|
|
Device:usbPlugIn()
|
|
end
|
|
self.event_handlers["NotCharging"] = function()
|
|
Device:usbPlugOut()
|
|
self:sendEvent(Event:new("NotCharging"))
|
|
end
|
|
-- Emulate the stock reader's refresh behavior...
|
|
--[[
|
|
NOTE: For ref, on a Touch (debugPaint & a patched strace are your friend!):
|
|
UI: flash: GC16_FAST, non-flash: AUTO (prefers GC16_FAST)
|
|
Reader: When flash: if to/from img: GC16, else GC16_FAST; when non-flash: AUTO (seems to prefer GL16_FAST); Waiting for marker only on flash
|
|
On a PW2:
|
|
UI: flash: GC16_FAST, non-flash: AUTO (prefers GC16_FAST)
|
|
Reader: When flash: if to/from img: GC16, else GC16_FAST; when non-flash: REAGL (w/ UPDATE_MODE_FULL!); Always waits for marker
|
|
Note that the bottom status bar region is refreshed separately, right after the screen, as a PARTIAL, AUTO (GC16_FAST) update, and it's this marker that's waited after...
|
|
Non flash lasts longer (dual timeout: 14pgs / 7mins).
|
|
--]]
|
|
-- We don't really have an easy way to know if we're refreshing the UI, or a page, or if said page contains an image, so go with the highest fidelity option
|
|
-- We spend much more time in the reader than the UI, and our UI isn't very graphic anyway, so we'll follow the reader's behaviour above all
|
|
self.full_refresh_waveform_mode = WAVEFORM_MODE_GC16
|
|
-- Request REAGL waveform mode on devices that support it (PW2, KT2, KV) [FIXME: Is that actually true of the KT2?]
|
|
if Device.model == "KindlePaperWhite2" or Device.model == "KindleBasic" or Device.model == "KindleVoyage" then
|
|
self.partial_refresh_waveform_mode = WAVEFORM_MODE_REAGL
|
|
-- We need to wait for every update marker when using REAGL waveform modes. That mostly means we always use MXCFB_WAIT_FOR_UPDATE_SUBMISSION.
|
|
self.wait_for_every_marker = true
|
|
else
|
|
self.partial_refresh_waveform_mode = WAVEFORM_MODE_GL16_FAST
|
|
-- NOTE: Or we could go back to what KOReader did before fa55acc in koreader-base, which was also to use AUTO ;).
|
|
-- That said, we *should* be making more or less the same decisions as AUTO on our own, if I followed things correctly...
|
|
--self.partial_refresh_waveform_mode = WAVEFORM_MODE_AUTO
|
|
-- Only wait for update markers on FULL updates
|
|
self.wait_for_every_marker = false
|
|
end
|
|
-- Default to GC16_FAST (will be used for PARTIAL, regional updates)
|
|
self.default_waveform_mode = WAVEFORM_MODE_GC16_FAST
|
|
end
|
|
end
|
|
|
|
-- register & show a widget
|
|
-- modal widget should be always on the top
|
|
function UIManager:show(widget, x, y)
|
|
DEBUG("show widget", widget.id)
|
|
self._running = true
|
|
local window = {x = x or 0, y = y or 0, widget = widget}
|
|
-- put this window on top of the toppest non-modal window
|
|
for i = #self._window_stack, 0, -1 do
|
|
local top_window = self._window_stack[i]
|
|
-- skip modal window
|
|
if not top_window or not top_window.widget.modal then
|
|
table.insert(self._window_stack, i + 1, window)
|
|
break
|
|
end
|
|
end
|
|
-- and schedule it to be painted
|
|
self:setDirty(widget)
|
|
-- tell the widget that it is shown now
|
|
widget:handleEvent(Event:new("Show"))
|
|
-- check if this widget disables double tap gesture
|
|
if widget.disable_double_tap then
|
|
Input.disable_double_tap = true
|
|
end
|
|
end
|
|
|
|
-- unregister a widget
|
|
function UIManager:close(widget)
|
|
if not widget then
|
|
DEBUG("widget not exist to be closed")
|
|
return
|
|
end
|
|
DEBUG("close widget", widget.id)
|
|
Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP
|
|
local dirty = false
|
|
for i = #self._window_stack, 1, -1 do
|
|
if self._window_stack[i].widget == widget then
|
|
table.remove(self._window_stack, i)
|
|
dirty = true
|
|
elseif self._window_stack[i].widget.disable_double_tap then
|
|
Input.disable_double_tap = true
|
|
end
|
|
end
|
|
if dirty then
|
|
-- schedule remaining widgets to be painted
|
|
for i = 1, #self._window_stack do
|
|
self:setDirty(self._window_stack[i].widget)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- schedule an execution task
|
|
function UIManager:schedule(time, action)
|
|
table.insert(self._execution_stack, { time = time, action = action })
|
|
end
|
|
|
|
-- schedule task in a certain amount of seconds (fractions allowed) from now
|
|
function UIManager:scheduleIn(seconds, action)
|
|
local when = { util.gettime() }
|
|
local s = math.floor(seconds)
|
|
local usecs = (seconds - s) * 1000000
|
|
when[1] = when[1] + s
|
|
when[2] = when[2] + usecs
|
|
if when[2] > 1000000 then
|
|
when[1] = when[1] + 1
|
|
when[2] = when[2] - 1000000
|
|
end
|
|
self:schedule(when, action)
|
|
end
|
|
|
|
-- unschedule an execution task
|
|
-- in order to unschedule anonymous functions, store a reference
|
|
-- for example:
|
|
-- self.anonymousFunction = function() self:regularFunction() end
|
|
-- UIManager:scheduleIn(10, self.anonymousFunction)
|
|
-- UIManager:unschedule(self.anonymousFunction)
|
|
function UIManager:unschedule(action)
|
|
for i = #self._execution_stack, 1, -1 do
|
|
local task = self._execution_stack[i]
|
|
if task.action == action then
|
|
-- remove from table
|
|
table.remove(self._execution_stack, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- register a widget to be repainted
|
|
function UIManager:setDirty(widget, refresh_type)
|
|
-- "auto": request full refresh
|
|
-- "full": force full refresh
|
|
-- "partial": partial refresh
|
|
if not refresh_type then
|
|
refresh_type = "auto"
|
|
end
|
|
if widget then
|
|
self._dirty[widget] = refresh_type
|
|
end
|
|
end
|
|
|
|
function UIManager:insertZMQ(zeromq)
|
|
table.insert(self._zeromqs, zeromq)
|
|
return zeromq
|
|
end
|
|
|
|
function UIManager:removeZMQ(zeromq)
|
|
for i = #self._zeromqs, 1, -1 do
|
|
if self._zeromqs[i] == zeromq then
|
|
table.remove(self._zeromqs, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- set full refresh rate for e-ink screen
|
|
-- and make the refresh rate persistant in global reader settings
|
|
function UIManager:setRefreshRate(rate)
|
|
DEBUG("set screen full refresh rate", rate)
|
|
self.FULL_REFRESH_COUNT = rate
|
|
G_reader_settings:saveSetting("full_refresh_count", rate)
|
|
end
|
|
|
|
-- get full refresh rate for e-ink screen
|
|
function UIManager:getRefreshRate(rate)
|
|
return self.FULL_REFRESH_COUNT
|
|
end
|
|
|
|
-- signal to quit
|
|
function UIManager:quit()
|
|
DEBUG("quit uimanager")
|
|
self._running = false
|
|
for i = #self._window_stack, 1, -1 do
|
|
table.remove(self._window_stack, i)
|
|
end
|
|
for i = #self._execution_stack, 1, -1 do
|
|
table.remove(self._execution_stack, i)
|
|
end
|
|
for i = #self._zeromqs, 1, -1 do
|
|
self._zeromqs[i]:stop()
|
|
table.remove(self._zeromqs, i)
|
|
end
|
|
end
|
|
|
|
-- transmit an event to registered widgets
|
|
function UIManager:sendEvent(event)
|
|
if #self._window_stack == 0 then return end
|
|
-- top level widget has first access to the event
|
|
if self._window_stack[#self._window_stack].widget:handleEvent(event) then
|
|
return
|
|
end
|
|
|
|
-- if the event is not consumed, active widgets can access it
|
|
for _, widget in ipairs(self._window_stack) do
|
|
if widget.widget.is_always_active then
|
|
if widget.widget:handleEvent(event) then return end
|
|
end
|
|
if widget.widget.active_widgets then
|
|
for _, active_widget in ipairs(widget.widget.active_widgets) do
|
|
if active_widget:handleEvent(event) then return end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function UIManager:checkTasks()
|
|
local now = { util.gettime() }
|
|
|
|
-- check if we have timed events in our queue and search next one
|
|
local wait_until = nil
|
|
local all_tasks_checked
|
|
repeat
|
|
all_tasks_checked = true
|
|
for i = #self._execution_stack, 1, -1 do
|
|
local task = self._execution_stack[i]
|
|
if not task.time
|
|
or task.time[1] < now[1]
|
|
or task.time[1] == now[1] and task.time[2] < now[2] then
|
|
-- task is pending to be executed right now. do it.
|
|
task.action()
|
|
-- and remove from table
|
|
table.remove(self._execution_stack, i)
|
|
-- start loop again, since new tasks might be on the
|
|
-- queue now
|
|
all_tasks_checked = false
|
|
elseif not wait_until
|
|
or wait_until[1] > task.time[1]
|
|
or wait_until[1] == task.time[1] and wait_until[2] > task.time[2] then
|
|
-- task is to be run in the future _and_ is scheduled
|
|
-- earlier than the tasks we looked at already
|
|
-- so adjust to the currently examined task instead.
|
|
wait_until = task.time
|
|
end
|
|
end
|
|
until all_tasks_checked
|
|
return wait_until
|
|
end
|
|
|
|
-- this is the main loop of the UI controller
|
|
-- it is intended to manage input events and delegate
|
|
-- them to dialogs
|
|
function UIManager:run()
|
|
self._running = true
|
|
while self._running do
|
|
local now = { util.gettime() }
|
|
local wait_until = self:checkTasks()
|
|
|
|
--DEBUG("---------------------------------------------------")
|
|
--DEBUG("exec stack", self._execution_stack)
|
|
--DEBUG("window stack", self._window_stack)
|
|
--DEBUG("dirty stack", self._dirty)
|
|
--DEBUG("---------------------------------------------------")
|
|
|
|
-- stop when we have no window to show
|
|
if #self._window_stack == 0 then
|
|
DEBUG("no dialog left to show")
|
|
self:quit()
|
|
return nil
|
|
end
|
|
|
|
-- repaint dirty widgets
|
|
local dirty = false
|
|
local request_full_refresh = false
|
|
local force_full_refresh = false
|
|
local force_partial_refresh = false
|
|
local force_fast_refresh = false
|
|
for _, widget in ipairs(self._window_stack) do
|
|
-- paint if repaint_all is request
|
|
-- paint also if current widget or any widget underneath is dirty
|
|
if self.repaint_all or dirty or self._dirty[widget.widget] then
|
|
widget.widget:paintTo(Screen.bb, widget.x, widget.y)
|
|
|
|
if self._dirty[widget.widget] == "auto" then
|
|
request_full_refresh = true
|
|
end
|
|
if self._dirty[widget.widget] == "full" then
|
|
force_full_refresh = true
|
|
end
|
|
if self._dirty[widget.widget] == "partial" then
|
|
force_partial_refresh = true
|
|
end
|
|
if self._dirty[widget.widget] == "fast" then
|
|
force_fast_refresh = true
|
|
end
|
|
-- and remove from list after painting
|
|
self._dirty[widget.widget] = nil
|
|
-- trigger repaint
|
|
dirty = true
|
|
end
|
|
end
|
|
|
|
if self.full_refresh then
|
|
dirty = true
|
|
force_full_refresh = true
|
|
end
|
|
|
|
if self.partial_refresh then
|
|
dirty = true
|
|
force_partial_refresh = true
|
|
end
|
|
|
|
self.repaint_all = false
|
|
self.full_refresh = false
|
|
self.partial_refresh = false
|
|
|
|
local refresh_type = self.default_refresh_type
|
|
local waveform_mode = self.default_waveform_mode
|
|
local wait_for_marker = self.wait_for_every_marker
|
|
if dirty then
|
|
if force_partial_refresh or force_fast_refresh then
|
|
refresh_type = UPDATE_MODE_PARTIAL
|
|
elseif force_full_refresh or self.refresh_count == self.FULL_REFRESH_COUNT - 1 then
|
|
refresh_type = UPDATE_MODE_FULL
|
|
end
|
|
-- Handle the waveform mode selection...
|
|
if refresh_type == UPDATE_MODE_FULL then
|
|
waveform_mode = self.full_refresh_waveform_mode
|
|
else
|
|
waveform_mode = self.partial_refresh_waveform_mode
|
|
end
|
|
if force_fast_refresh then
|
|
waveform_mode = self.fast_waveform_mode
|
|
end
|
|
-- If the device is REAGL-aware, and we're doing a reader refresh (i.e., PARTIAL, but not regional), apply some trickery to match the stock reader's behavior if needed (On *some* devices, REAGL updates are always FULL, but there's no black flash)
|
|
if self.regal_always_full and not self.update_regions_func and refresh_type == UPDATE_MODE_PARTIAL and (waveform_mode == WAVEFORM_MODE_REAGL or waveform_mode == NTX_WFM_MODE_GLD16) then
|
|
refresh_type = UPDATE_MODE_FULL
|
|
end
|
|
-- If we truly asked for a PARTIAL, regional update, it's likely for an UI element, so fall back to the default waveform mode, which is tailored per-device to hopefully be more appropriate than the one we use in the reader
|
|
if self.update_regions_func and refresh_type == UPDATE_MODE_PARTIAL and not force_fast_refresh then
|
|
-- NOTE: Using default_waveform_mode might seem counter-intuitive when we have partial_refresh_waveform_mode, but partial_refresh_waveform_mode is mostly there as a means to flag REAGL-aware devices ;).
|
|
-- Here, we're actually interested in handling regional updates (which happen to be PARTIAL by definition), and not 'PARTIAL' updates that actually refresh the whole screen.
|
|
waveform_mode = self.default_waveform_mode
|
|
end
|
|
if self.update_regions_func then
|
|
local update_regions = self.update_regions_func()
|
|
for _, update_region in ipairs(update_regions) do
|
|
-- in some rare cases update region has 1 pixel offset
|
|
Screen:refresh(refresh_type, waveform_mode, wait_for_marker,
|
|
update_region.x-1, update_region.y-1,
|
|
update_region.w+2, update_region.h+2)
|
|
end
|
|
else
|
|
Screen:refresh(refresh_type, waveform_mode, wait_for_marker)
|
|
end
|
|
-- REAGL refreshes are always FULL (but without a black flash), but we want to keep our black flash timeout working, so don't reset the counter on FULL REAGL refreshes...
|
|
if refresh_type == UPDATE_MODE_FULL and waveform_mode ~= WAVEFORM_MODE_REAGL and waveform_mode ~= NTX_WFM_MODE_GLD16 then
|
|
self.refresh_count = 0
|
|
elseif not force_partial_refresh and not force_full_refresh then
|
|
self.refresh_count = (self.refresh_count + 1)%self.FULL_REFRESH_COUNT
|
|
end
|
|
self.update_regions_func = nil
|
|
end
|
|
|
|
self:checkTasks()
|
|
|
|
-- wait for next event
|
|
-- note that we will skip that if in the meantime we have tasks that are ready to run
|
|
local input_event = nil
|
|
if not wait_until then
|
|
if #self._zeromqs > 0 then
|
|
-- pending message queue, wait 100ms for input
|
|
input_event = Input:waitEvent(1000*100)
|
|
if not input_event or input_event.handler == "onInputError" then
|
|
for _, zeromq in ipairs(self._zeromqs) do
|
|
input_event = zeromq:waitEvent()
|
|
if input_event then break end
|
|
end
|
|
end
|
|
else
|
|
-- no pending task, wait endlessly
|
|
input_event = Input:waitEvent()
|
|
end
|
|
elseif wait_until[1] > now[1]
|
|
or wait_until[1] == now[1] and wait_until[2] > now[2] then
|
|
local wait_for = { s = wait_until[1] - now[1], us = wait_until[2] - now[2] }
|
|
if wait_for.us < 0 then
|
|
wait_for.s = wait_for.s - 1
|
|
wait_for.us = 1000000 + wait_for.us
|
|
end
|
|
-- wait until next task is pending
|
|
input_event = Input:waitEvent(wait_for.us, wait_for.s)
|
|
end
|
|
|
|
-- delegate input_event to handler
|
|
if input_event then
|
|
local handler = self.event_handlers[input_event]
|
|
if handler then
|
|
handler(input_event)
|
|
else
|
|
self.event_handlers["__default__"](input_event)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function UIManager:getRefreshMenuTable()
|
|
local function custom_1() return G_reader_settings:readSetting("refresh_rate_1") or 12 end
|
|
local function custom_2() return G_reader_settings:readSetting("refresh_rate_2") or 22 end
|
|
local function custom_3() return G_reader_settings:readSetting("refresh_rate_3") or 99 end
|
|
local function custom_input(name)
|
|
return {
|
|
title = _("Input page number for a full refresh"),
|
|
type = "number",
|
|
hint = "(1 - 99)",
|
|
callback = function(input)
|
|
local rate = tonumber(input)
|
|
G_reader_settings:saveSetting(name, rate)
|
|
UIManager:setRefreshRate(rate)
|
|
end,
|
|
}
|
|
end
|
|
return {
|
|
text = _("E-ink full refresh rate"),
|
|
sub_item_table = {
|
|
{
|
|
text = _("Every page"),
|
|
checked_func = function() return UIManager:getRefreshRate() == 1 end,
|
|
callback = function() UIManager:setRefreshRate(1) end,
|
|
},
|
|
{
|
|
text = _("Every 6 pages"),
|
|
checked_func = function() return UIManager:getRefreshRate() == 6 end,
|
|
callback = function() UIManager:setRefreshRate(6) end,
|
|
},
|
|
{
|
|
text_func = function() return _("Custom ") .. "1: " .. custom_1() .. _(" pages") end,
|
|
checked_func = function() return UIManager:getRefreshRate() == custom_1() end,
|
|
callback = function() UIManager:setRefreshRate(custom_1()) end,
|
|
hold_input = custom_input("refresh_rate_1")
|
|
},
|
|
{
|
|
text_func = function() return _("Custom ") .. "2: " .. custom_2() .. _(" pages") end,
|
|
checked_func = function() return UIManager:getRefreshRate() == custom_2() end,
|
|
callback = function() UIManager:setRefreshRate(custom_2()) end,
|
|
hold_input = custom_input("refresh_rate_2")
|
|
},
|
|
{
|
|
text_func = function() return _("Custom ") .. "3: " .. custom_3() .. _(" pages") end,
|
|
checked_func = function() return UIManager:getRefreshRate() == custom_3() end,
|
|
callback = function() UIManager:setRefreshRate(custom_3()) end,
|
|
hold_input = custom_input("refresh_rate_3")
|
|
},
|
|
}
|
|
}
|
|
end
|
|
|
|
UIManager:init()
|
|
return UIManager
|
|
|