|
|
|
@ -4,7 +4,7 @@ local Input = require("device").input
|
|
|
|
|
local Event = require("ui/event")
|
|
|
|
|
local Geom = require("ui/geometry")
|
|
|
|
|
local util = require("ffi/util")
|
|
|
|
|
local DEBUG = require("dbg")
|
|
|
|
|
local dbg = require("dbg")
|
|
|
|
|
local _ = require("gettext")
|
|
|
|
|
|
|
|
|
|
local MILLION = 1000000
|
|
|
|
@ -124,7 +124,7 @@ end
|
|
|
|
|
-- modal widget should be always on the top
|
|
|
|
|
-- for refreshtype & refreshregion see description of setDirty()
|
|
|
|
|
function UIManager:show(widget, refreshtype, refreshregion, x, y)
|
|
|
|
|
DEBUG("show widget", widget._name)
|
|
|
|
|
dbg("show widget", widget._name)
|
|
|
|
|
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
|
|
|
|
@ -150,10 +150,10 @@ end
|
|
|
|
|
-- for refreshtype & refreshregion see description of setDirty()
|
|
|
|
|
function UIManager:close(widget, refreshtype, refreshregion)
|
|
|
|
|
if not widget then
|
|
|
|
|
DEBUG("widget not exist to be closed")
|
|
|
|
|
dbg("widget not exist to be closed")
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
DEBUG("close widget", widget.id)
|
|
|
|
|
dbg("close widget", widget.id)
|
|
|
|
|
-- TODO: Why do we the following?
|
|
|
|
|
Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP
|
|
|
|
|
local dirty = false
|
|
|
|
@ -178,8 +178,6 @@ end
|
|
|
|
|
|
|
|
|
|
-- schedule an execution task, task queue is in ascendant order
|
|
|
|
|
function UIManager:schedule(time, action)
|
|
|
|
|
assert(time[1] >= 0 and time[2] >= 0, "Only positive time allowed")
|
|
|
|
|
assert(action ~= nil)
|
|
|
|
|
local p, s, e = 1, 1, #self._task_queue
|
|
|
|
|
if e ~= 0 then
|
|
|
|
|
local us = time[1] * MILLION + time[2]
|
|
|
|
@ -212,10 +210,14 @@ function UIManager:schedule(time, action)
|
|
|
|
|
table.insert(self._task_queue, p, { time = time, action = action })
|
|
|
|
|
self._task_queue_dirty = true
|
|
|
|
|
end
|
|
|
|
|
dbg:guard(UIManager, 'schedule',
|
|
|
|
|
function(self, time, action)
|
|
|
|
|
assert(time[1] >= 0 and time[2] >= 0, "Only positive time allowed")
|
|
|
|
|
assert(action ~= nil)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
-- schedule task in a certain amount of seconds (fractions allowed) from now
|
|
|
|
|
function UIManager:scheduleIn(seconds, action)
|
|
|
|
|
assert(seconds >= 0, "Only positive seconds allowed")
|
|
|
|
|
local when = { util.gettime() }
|
|
|
|
|
local s = math.floor(seconds)
|
|
|
|
|
local usecs = (seconds - s) * MILLION
|
|
|
|
@ -227,6 +229,10 @@ function UIManager:scheduleIn(seconds, action)
|
|
|
|
|
end
|
|
|
|
|
self:schedule(when, action)
|
|
|
|
|
end
|
|
|
|
|
dbg:guard(UIManager, 'scheduleIn',
|
|
|
|
|
function(self, seconds, action)
|
|
|
|
|
assert(seconds >= 0, "Only positive seconds allowed")
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
function UIManager:nextTick(action)
|
|
|
|
|
return self:scheduleIn(0, action)
|
|
|
|
@ -239,13 +245,14 @@ end
|
|
|
|
|
-- UIManager:scheduleIn(10, self.anonymousFunction)
|
|
|
|
|
-- UIManager:unschedule(self.anonymousFunction)
|
|
|
|
|
function UIManager:unschedule(action)
|
|
|
|
|
assert(action ~= nil)
|
|
|
|
|
for i = #self._task_queue, 1, -1 do
|
|
|
|
|
if self._task_queue[i].action == action then
|
|
|
|
|
table.remove(self._task_queue, i)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
dbg:guard(UIManager, 'unschedule',
|
|
|
|
|
function(self, action) assert(action ~= nil) end)
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
register a widget to be repainted and enqueue a refresh
|
|
|
|
@ -269,17 +276,6 @@ function UIManager:setDirty(widget, refreshtype, refreshregion)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
self._dirty[widget] = true
|
|
|
|
|
if DEBUG.is_on then
|
|
|
|
|
-- when debugging, we check if we get handed a valid widget,
|
|
|
|
|
-- which would be a dialog that was previously passed via show()
|
|
|
|
|
local found = false
|
|
|
|
|
for i = 1, #self._window_stack do
|
|
|
|
|
if self._window_stack[i].widget == widget then found = true end
|
|
|
|
|
end
|
|
|
|
|
if not found then
|
|
|
|
|
DEBUG("INFO: invalid widget for setDirty()", debug.traceback())
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
-- handle refresh information
|
|
|
|
@ -291,6 +287,20 @@ function UIManager:setDirty(widget, refreshtype, refreshregion)
|
|
|
|
|
self:_refresh(refreshtype, refreshregion)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
dbg:guard(UIManager, 'setDirty',
|
|
|
|
|
nil,
|
|
|
|
|
function(self, widget, refreshtype, refreshregion)
|
|
|
|
|
if not widget then return end
|
|
|
|
|
-- when debugging, we check if we get handed a valid widget,
|
|
|
|
|
-- which would be a dialog that was previously passed via show()
|
|
|
|
|
local found = false
|
|
|
|
|
for i = 1, #self._window_stack do
|
|
|
|
|
if self._window_stack[i].widget == widget then found = true end
|
|
|
|
|
end
|
|
|
|
|
if not found then
|
|
|
|
|
dbg("INFO: invalid widget for setDirty()", debug.traceback())
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
function UIManager:insertZMQ(zeromq)
|
|
|
|
|
table.insert(self._zeromqs, zeromq)
|
|
|
|
@ -308,7 +318,7 @@ 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)
|
|
|
|
|
dbg("set screen full refresh rate", rate)
|
|
|
|
|
self.FULL_REFRESH_COUNT = rate
|
|
|
|
|
G_reader_settings:saveSetting("full_refresh_count", rate)
|
|
|
|
|
end
|
|
|
|
@ -320,7 +330,7 @@ end
|
|
|
|
|
|
|
|
|
|
-- signal to quit
|
|
|
|
|
function UIManager:quit()
|
|
|
|
|
DEBUG("quiting uimanager")
|
|
|
|
|
dbg("quiting uimanager")
|
|
|
|
|
self._task_queue_dirty = false
|
|
|
|
|
self._running = false
|
|
|
|
|
self._run_forever = nil
|
|
|
|
@ -441,7 +451,7 @@ function UIManager:_refresh(mode, region)
|
|
|
|
|
if not region and mode == "partial" and not self.refresh_counted then
|
|
|
|
|
self.refresh_count = (self.refresh_count + 1) % self.FULL_REFRESH_COUNT
|
|
|
|
|
if self.refresh_count == self.FULL_REFRESH_COUNT - 1 then
|
|
|
|
|
DEBUG("promote refresh to full refresh")
|
|
|
|
|
dbg("promote refresh to full refresh")
|
|
|
|
|
mode = "full"
|
|
|
|
|
end
|
|
|
|
|
self.refresh_counted = true
|
|
|
|
@ -499,13 +509,13 @@ function UIManager:_repaint()
|
|
|
|
|
-- If we don't, we add one now and print a warning if we
|
|
|
|
|
-- are debugging
|
|
|
|
|
if dirty and #self._refresh_stack == 0 then
|
|
|
|
|
DEBUG("WARNING: no refresh got enqueued. Will do a partial full screen refresh, which might be inefficient")
|
|
|
|
|
dbg("WARNING: no refresh got enqueued. Will do a partial full screen refresh, which might be inefficient")
|
|
|
|
|
self:_refresh("partial")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- execute refreshes:
|
|
|
|
|
for _, refresh in ipairs(self._refresh_stack) do
|
|
|
|
|
DEBUG("triggering refresh", refresh)
|
|
|
|
|
dbg("triggering refresh", refresh)
|
|
|
|
|
Screen[refresh_methods[refresh.mode]](Screen,
|
|
|
|
|
refresh.region.x - 1, refresh.region.y - 1,
|
|
|
|
|
refresh.region.w + 2, refresh.region.h + 2)
|
|
|
|
@ -529,15 +539,15 @@ function UIManager:handleInput()
|
|
|
|
|
-- for input events:
|
|
|
|
|
repeat
|
|
|
|
|
wait_until, now = self:_checkTasks()
|
|
|
|
|
--DEBUG("---------------------------------------------------")
|
|
|
|
|
--DEBUG("exec stack", self._task_queue)
|
|
|
|
|
--DEBUG("window stack", self._window_stack)
|
|
|
|
|
--DEBUG("dirty stack", self._dirty)
|
|
|
|
|
--DEBUG("---------------------------------------------------")
|
|
|
|
|
--dbg("---------------------------------------------------")
|
|
|
|
|
--dbg("exec stack", self._task_queue)
|
|
|
|
|
--dbg("window stack", self._window_stack)
|
|
|
|
|
--dbg("dirty stack", self._dirty)
|
|
|
|
|
--dbg("---------------------------------------------------")
|
|
|
|
|
|
|
|
|
|
-- stop when we have no window to show
|
|
|
|
|
if #self._window_stack == 0 and not self._run_forever then
|
|
|
|
|
DEBUG("no dialog left to show")
|
|
|
|
|
dbg("no dialog left to show")
|
|
|
|
|
self:quit()
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
@ -587,7 +597,7 @@ function UIManager:handleInput()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if self.looper then
|
|
|
|
|
DEBUG("handle input in turbo I/O looper")
|
|
|
|
|
dbg("handle input in turbo I/O looper")
|
|
|
|
|
self.looper:add_callback(function()
|
|
|
|
|
-- FIXME: force close looper when there is unhandled error,
|
|
|
|
|
-- otherwise the looper will hang. Any better solution?
|
|
|
|
@ -661,10 +671,13 @@ function UIManager:_initAutoSuspend()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function UIManager:_startAutoSuspend()
|
|
|
|
|
assert(self:_autoSuspendEnabled())
|
|
|
|
|
self.last_action_sec = util.gettime()
|
|
|
|
|
self:nextTick(self.auto_suspend_action)
|
|
|
|
|
end
|
|
|
|
|
dbg:guard(UIManager, '_startAutoSuspend',
|
|
|
|
|
function(self)
|
|
|
|
|
assert(self:_autoSuspendEnabled())
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
function UIManager:_autoSuspendEnabled()
|
|
|
|
|
return Device:isKobo() and self.auto_suspend_sec > 0
|
|
|
|
|