2
0
mirror of https://github.com/koreader/koreader synced 2024-11-13 19:11:25 +00:00
koreader/frontend/ui/uimanager.lua

1187 lines
44 KiB
Lua
Raw Normal View History

--[[--
This module manages widgets.
]]
local Device = require("device")
2013-10-18 20:38:07 +00:00
local Event = require("ui/event")
local Geom = require("ui/geometry")
local dbg = require("dbg")
local logger = require("logger")
local util = require("ffi/util")
local _ = require("gettext")
local Input = Device.input
local Screen = Device.screen
2012-04-22 19:29:48 +00:00
local MILLION = 1000000
2012-04-22 19:29:48 +00:00
-- there is only one instance of this
2013-10-18 20:38:07 +00:00
local UIManager = {
2014-03-13 13:52:43 +00:00
-- trigger a full refresh when counter reaches FULL_REFRESH_COUNT
FULL_REFRESH_COUNT =
G_reader_settings:readSetting("full_refresh_count") or DRCOUNTMAX,
2014-03-13 13:52:43 +00:00
refresh_count = 0,
2013-03-12 17:18:53 +00:00
-- How long to wait between ZMQ wakeups: 50ms.
ZMQ_TIMEOUT = 50 * 1000,
event_handlers = nil,
2014-03-13 13:52:43 +00:00
_running = true,
_window_stack = {},
_task_queue = {},
_task_queue_dirty = false,
_dirty = {},
_zeromqs = {},
_refresh_stack = {},
_refresh_func_stack = {},
_entered_poweroff_stage = false,
_exit_code = nil,
event_hook = require("ui/hook_container"):new()
2012-04-22 19:29:48 +00:00
}
function UIManager:init()
self.event_handlers = {
__default__ = function(input_event)
self:sendEvent(input_event)
end,
SaveState = function()
self:flushSettings()
end,
Power = function(input_event)
Device:onPowerEvent(input_event)
end,
}
self.poweroff_action = function()
self._entered_poweroff_stage = true;
Screen:setRotationMode(0)
require("ui/screensaver"):show("poweroff", _("Powered off"))
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
if Device:needsScreenRefreshAfterResume() then
Screen:refreshFull()
end
UIManager:nextTick(function()
Device:saveSettings()
self:broadcastEvent(Event:new("Close"))
Device:powerOff()
end)
end
self.reboot_action = function()
self._entered_poweroff_stage = true;
Screen:setRotationMode(0)
require("ui/screensaver"):show("reboot", _("Rebooting..."))
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
if Device:needsScreenRefreshAfterResume() then
Screen:refreshFull()
end
UIManager:nextTick(function()
Device:saveSettings()
self:broadcastEvent(Event:new("Close"))
Device:reboot()
end)
end
if Device:isPocketBook() then
self.event_handlers["Suspend"] = function()
self:_beforeSuspend()
Device:onPowerEvent("Power")
end
self.event_handlers["Resume"] = function()
Device:onPowerEvent("Power")
self:_afterResume()
end
end
if Device:isKobo() then
-- We do not want auto suspend procedure to waste battery during
-- suspend. So let's unschedule it when suspending, and restart it after
-- resume. Done via the plugin's onSuspend/onResume handlers.
self.event_handlers["Suspend"] = function()
-- Ignore the accelerometer (if that's not already the case) while we're alseep
if G_reader_settings:nilOrFalse("input_ignore_gsensor") then
Device:toggleGSensor(false)
end
self:_beforeSuspend()
Device:onPowerEvent("Suspend")
end
self.event_handlers["Resume"] = function()
Device:onPowerEvent("Resume")
self:_afterResume()
-- Stop ignoring the accelerometer (unless requested) when we wakeup
if G_reader_settings:nilOrFalse("input_ignore_gsensor") then
Device:toggleGSensor(true)
end
end
self.event_handlers["PowerPress"] = function()
-- Always schedule power off.
-- Press the power button for 2+ seconds to shutdown directly from suspend.
UIManager:scheduleIn(2, self.poweroff_action)
end
self.event_handlers["PowerRelease"] = function()
if not self._entered_poweroff_stage then
UIManager:unschedule(self.poweroff_action)
-- resume if we were suspended
if Device.screen_saver_mode then
self:resume()
else
self:suspend()
end
end
end
-- Sleep Cover handling
if G_reader_settings:readSetting("ignore_power_sleepcover") then
-- NOTE: The hardware event itself will wake the kernel up if it's in suspend (:/).
-- Let the unexpected wakeup guard handle that.
self.event_handlers["SleepCoverClosed"] = nil
self.event_handlers["SleepCoverOpened"] = nil
elseif G_reader_settings:readSetting("ignore_open_sleepcover") then
-- Just ignore wakeup events, and do NOT set is_cover_closed,
-- so device/generic/device will let us use the power button to wake ;).
self.event_handlers["SleepCoverClosed"] = function()
self:suspend()
end
self.event_handlers["SleepCoverOpened"] = function()
Device.is_cover_closed = false
end
else
self.event_handlers["SleepCoverClosed"] = function()
Device.is_cover_closed = true
self:suspend()
end
self.event_handlers["SleepCoverOpened"] = function()
Device.is_cover_closed = false
self:resume()
end
end
self.event_handlers["Light"] = function()
Device:getPowerDevice():toggleFrontlight()
end
2016-10-22 07:04:37 +00:00
self.event_handlers["Charging"] = function()
self:_beforeCharging()
if Device.screen_saver_mode then
self:suspend()
end
end
self.event_handlers["NotCharging"] = function()
-- We need to put the device into suspension, other things need to be done before it.
self:_afterNotCharging()
if Device.screen_saver_mode then
self:suspend()
end
end
self.event_handlers["__default__"] = function(input_event)
-- Suspension in Kobo can be interrupted by screen updates. We ignore user touch input
-- in screen_saver_mode so screen updates won't be triggered in suspend mode.
-- We should not call self:suspend() in screen_saver_mode lest we stay on forever
-- trying to reschedule suspend. Other systems take care of unintended wake-up.
if not Device.screen_saver_mode then
self:sendEvent(input_event)
end
end
elseif Device:isKindle() then
self.event_handlers["IntoSS"] = function()
self:_beforeSuspend()
Device:intoScreenSaver()
end
self.event_handlers["OutOfSS"] = function()
Device:outofScreenSaver()
self:_afterResume();
end
self.event_handlers["Charging"] = function()
self:_beforeCharging()
Device:usbPlugIn()
end
self.event_handlers["NotCharging"] = function()
Device:usbPlugOut()
self:_afterNotCharging()
end
elseif Device:isSonyPRSTUX() then
self.event_handlers["PowerPress"] = function()
UIManager:scheduleIn(2, self.poweroff_action)
end
self.event_handlers["PowerRelease"] = function()
if not self._entered_poweroff_stage then
UIManager:unschedule(self.poweroff_action)
-- resume if we were suspended
if Device.screen_saver_mode then
self:resume()
else
self:suspend()
end
end
end
self.event_handlers["Suspend"] = function()
self:_beforeSuspend()
Device:intoScreenSaver()
Device:suspend()
end
self.event_handlers["Resume"] = function()
Device:resume()
Device:outofScreenSaver()
self:_afterResume()
end
self.event_handlers["__default__"] = function(input_event)
-- Same as in Kobo: we want to ignore keys during suspension
if not Device.screen_saver_mode then
self:sendEvent(input_event)
end
end
elseif Device:isCervantes() then
self.event_handlers["Suspend"] = function()
self:_beforeSuspend()
Device:onPowerEvent("Suspend")
end
self.event_handlers["Resume"] = function()
Device:onPowerEvent("Resume")
self:_afterResume()
end
self.event_handlers["PowerPress"] = function()
UIManager:scheduleIn(2, self.poweroff_action)
end
self.event_handlers["PowerRelease"] = function()
if not self._entered_poweroff_stage then
UIManager:unschedule(self.poweroff_action)
-- resume if we were suspended
if Device.screen_saver_mode then
self:resume()
else
self:suspend()
end
end
end
self.event_handlers["Charging"] = function()
self:_beforeCharging()
if Device.screen_saver_mode then
self:suspend()
end
end
self.event_handlers["NotCharging"] = function()
self:_afterNotCharging()
if Device.screen_saver_mode then
self:suspend()
end
end
self.event_handlers["UsbPlugIn"] = function()
self:_beforeCharging()
if Device.screen_saver_mode then
self:suspend()
end
end
self.event_handlers["USbPlugOut"] = function()
self:_afterNotCharging()
if Device.screen_saver_mode then
self:suspend()
end
end
self.event_handlers["__default__"] = function(input_event)
-- Same as in Kobo: we want to ignore keys during suspension
if not Device.screen_saver_mode then
self:sendEvent(input_event)
end
end
elseif Device:isSDL() then
self.event_handlers["Suspend"] = function()
self:_beforeSuspend()
Device:simulateSuspend()
end
self.event_handlers["Resume"] = function()
Device:simulateResume()
self:_afterResume()
end
end
end
--[[--
Registers and shows a widget.
Modal widget should be always on top.
For refreshtype & refreshregion see description of setDirty().
]]
---- @param widget a widget object
---- @param refreshtype "full", "flashpartial", "flashui", "partial", "ui", "fast"
---- @param refreshregion a Geom object
---- @int x
---- @int y
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
---- @param refreshdither an optional bool
---- @see setDirty
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
function UIManager:show(widget, refreshtype, refreshregion, x, y, refreshdither)
if not widget then
logger.dbg("widget not exist to be shown")
return
end
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
logger.dbg("show widget:", widget.id or widget.name or tostring(widget))
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 widget.modal or not top_window or not top_window.widget.modal then
table.insert(self._window_stack, i + 1, window)
break
end
end
2014-03-13 13:52:43 +00:00
-- and schedule it to be painted
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
self:setDirty(widget, refreshtype, refreshregion, refreshdither)
2014-03-13 13:52:43 +00:00
-- 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 == false then
Input.disable_double_tap = false
else
2014-03-13 13:52:43 +00:00
Input.disable_double_tap = true
end
2012-04-22 19:29:48 +00:00
end
--[[--
Unregisters a widget.
For refreshtype & refreshregion see description of setDirty().
]]
---- @param widget a widget object
---- @param refreshtype "full", "flashpartial", "flashui", "partial", "ui", "fast"
---- @param refreshregion a Geom object
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
---- @param refreshdither an optional bool
---- @see setDirty
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
function UIManager:close(widget, refreshtype, refreshregion, refreshdither)
if not widget then
logger.dbg("widget to be closed does not exist")
return
end
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
logger.dbg("close widget:", widget.name or widget.id or tostring(widget))
2014-03-13 13:52:43 +00:00
local dirty = false
-- Ensure all the widgets can get onFlushSettings event.
widget:handleEvent(Event:new("FlushSettings"))
-- first send close event to widget
widget:handleEvent(Event:new("CloseWidget"))
-- make it disabled by default and check if any widget wants it disabled or enabled
Input.disable_double_tap = true
local requested_disable_double_tap = nil
-- then remove all references to that widget on stack and refresh
2014-03-13 13:52:43 +00:00
for i = #self._window_stack, 1, -1 do
if self._window_stack[i].widget == widget then
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
self._dirty[self._window_stack[i].widget] = nil
2014-03-13 13:52:43 +00:00
table.remove(self._window_stack, i)
dirty = true
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
else
-- If anything else on the stack was dithered, honor the hint
if self._window_stack[i].widget.dithered then
refreshdither = true
logger.dbg("Lower widget", self._window_stack[i].widget.name or self._window_stack[i].widget.id or tostring(self._window_stack[i].widget), "was dithered, honoring the dithering hint")
end
-- Set double tap to how the topmost specifying widget wants it
if requested_disable_double_tap == nil and self._window_stack[i].widget.disable_double_tap ~= nil then
requested_disable_double_tap = self._window_stack[i].widget.disable_double_tap
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
end
2014-03-13 13:52:43 +00:00
end
end
if requested_disable_double_tap ~= nil then
Input.disable_double_tap = requested_disable_double_tap
end
if dirty and not widget.invisible then
2014-03-13 13:52:43 +00:00
-- schedule remaining widgets to be painted
for i = 1, #self._window_stack do
self:setDirty(self._window_stack[i].widget)
2014-03-13 13:52:43 +00:00
end
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
self:_refresh(refreshtype, refreshregion, refreshdither)
2014-03-13 13:52:43 +00:00
end
2012-04-22 19:29:48 +00:00
end
-- schedule an execution task, task queue is in ascendant order
function UIManager:schedule(time, action, ...)
local p, s, e = 1, 1, #self._task_queue
if e ~= 0 then
local us = time[1] * MILLION + time[2]
-- do a binary insert
repeat
p = math.floor(s + (e - s) / 2)
local ptime = self._task_queue[p].time
local ptus = ptime[1] * MILLION + ptime[2]
if us > ptus then
if s == e then
p = e + 1
break
elseif s + 1 == e then
s = e
else
s = p
end
elseif us < ptus then
e = p
if s == e then
break
end
else
2016-04-24 04:16:37 +00:00
-- for fairness, it's better to make p+1 is strictly less than
-- p might want to revisit here in the future
break
end
until e < s
end
table.insert(self._task_queue, p, {
time = time,
action = action,
args = {...},
})
self._task_queue_dirty = true
2012-04-22 19:29:48 +00:00
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)
2012-04-22 19:29:48 +00:00
--- Schedules task in a certain amount of seconds (fractions allowed) from now.
function UIManager:scheduleIn(seconds, action, ...)
2014-03-13 13:52:43 +00:00
local when = { util.gettime() }
local s = math.floor(seconds)
local usecs = (seconds - s) * MILLION
2014-03-13 13:52:43 +00:00
when[1] = when[1] + s
when[2] = when[2] + usecs
if when[2] >= MILLION then
2014-03-13 13:52:43 +00:00
when[1] = when[1] + 1
when[2] = when[2] - MILLION
2014-03-13 13:52:43 +00:00
end
self:schedule(when, action, ...)
2012-04-22 19:29:48 +00:00
end
dbg:guard(UIManager, 'scheduleIn',
function(self, seconds, action)
assert(seconds >= 0, "Only positive seconds allowed")
end)
2012-04-22 19:29:48 +00:00
function UIManager:nextTick(action)
return self:scheduleIn(0, action)
end
-- Useful to run UI callbacks ASAP without skipping repaints
function UIManager:tickAfterNext(action)
return self:nextTick(function() self:nextTick(action) end)
end
--[[
-- NOTE: This appears to work *nearly* just as well, but does sometimes go too fast (might depend on kernel HZ & NO_HZ settings?)
function UIManager:tickAfterNext(action)
return self:scheduleIn(0.001, action)
end
--]]
--[[-- Unschedules an execution task.
In order to unschedule anonymous functions, store a reference.
@usage
self.anonymousFunction = function() self:regularFunction() end
UIManager:scheduleIn(10, self.anonymousFunction)
UIManager:unschedule(self.anonymousFunction)
]]
function UIManager:unschedule(action)
for i = #self._task_queue, 1, -1 do
2016-03-08 06:52:52 +00:00
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)
--[[--
Registers a widget to be repainted and enqueues a refresh.
the second parameter (refreshtype) can either specify a refreshtype
(optionally in combination with a refreshregion - which is suggested)
or a function that returns refreshtype AND refreshregion and is called
after painting the widget.
Here's a quick rundown of what each refreshtype should be used for:
full: high-fidelity flashing refresh (f.g., large images).
Highest quality, but highest latency.
Don't abuse if you only want a flash (in this case, prefer flashpartial or flashui).
partial: medium fidelity refresh (f.g., text on a white background).
Can be promoted to flashing after FULL_REFRESH_COUNT refreshes.
Don't abuse to avoid spurious flashes.
ui: medium fidelity refresh (f.g., mixed content).
Should apply to most UI elements.
fast: low fidelity refresh (f.g., monochrome content).
Should apply to most highlighting effects achieved through inversion.
Note that if your highlighted element contains text,
you might want to keep the unhighlight refresh as "ui" instead, for crisper text.
(Or optimize that refresh away entirely, if you can get away with it).
flashui: like ui, but flashing.
Can be used when showing a UI element for the first time, to avoid ghosting.
flashpartial: like partial, but flashing (and not counting towards flashing promotions).
Can be used when closing an UI element, to avoid ghosting.
You can even drop the region in these cases, to ensure a fullscreen flash.
NOTE: On REAGL devices, "flashpartial" will NOT actually flash (by design).
As such, even onClose, you might prefer "flashui" in some rare instances.
NOTE: You'll notice a trend on UI elements that are usually shown *over* some kind of text
of using "ui" onShow & onUpdate, but "partial" onClose.
This is by design: "partial" is what the reader uses, as it's tailor-made for pure text
over a white background, so this ensures we resume the usual flow of the reader.
The same dynamic is true for their flashing counterparts, in the rare instances we enforce flashes.
Any kind of "partial" refresh *will* count towards a flashing promotion after FULL_REFRESH_COUNT refreshes,
so making sure your stuff only applies to the proper region is key to avoiding spurious large black flashes.
That said, depending on your use case, using "ui" onClose can be a perfectly valid decision, and will ensure
never seeing a flash because of that widget.
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
The final parameter (refreshdither) is an optional hint for devices with hardware dithering support that this repaint
could benefit from dithering (i.e., it contains an image).
@usage
UIManager:setDirty(self.widget, "partial")
UIManager:setDirty(self.widget, "partial", Geom:new{x=10,y=10,w=100,h=50})
UIManager:setDirty(self.widget, function() return "ui", self.someelement.dimen end)
--]]
---- @param widget a widget object
---- @param refreshtype "full", "flashpartial", "flashui", "partial", "ui", "fast"
---- @param refreshregion a Geom object
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
---- @param refreshdither an optional bool
function UIManager:setDirty(widget, refreshtype, refreshregion, refreshdither)
if widget then
if widget == "all" then
-- special case: set all top-level widgets as being "dirty".
for i = 1, #self._window_stack do
self._dirty[self._window_stack[i].widget] = true
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- If any of 'em were dithered, honor their dithering hint
if self._window_stack[i].widget.dithered then
-- NOTE: That works when refreshtype is NOT a function,
-- which is why _repaint does another pass of this check ;).
refreshdither = true
end
end
elseif not widget.invisible then
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- We only ever check the dirty flag on top-level widgets, so only set it there!
-- NOTE: Enable verbose debug to catch misbehaving widgets via our post-guard.
for i = 1, #self._window_stack do
if self._window_stack[i].widget == widget then
self._dirty[widget] = true
end
end
-- Again, if it's flagged as dithered, honor that
if widget.dithered then
refreshdither = true
end
end
end
-- handle refresh information
if type(refreshtype) == "function" then
-- callback, will be issued after painting
table.insert(self._refresh_func_stack, refreshtype)
if dbg.is_on then
--- @fixme We can't consume the return values of refreshtype by running it, because for a reason that is beyond me (scoping? gc?), that renders it useless later, meaning we then enqueue refreshes with bogus arguments...
-- Thankfully, we can track them in _refresh()'s logging very soon after that...
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
logger.dbg("setDirty via a func from widget", widget and (widget.name or widget.id or tostring(widget)) or "nil")
end
else
-- otherwise, enqueue refresh
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
self:_refresh(refreshtype, refreshregion, refreshdither)
if dbg.is_on then
if refreshregion then
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
logger.dbg("setDirty", refreshtype and refreshtype or "nil", "from widget", widget and (widget.name or widget.id or tostring(widget)) or "nil", "w/ region", refreshregion.x, refreshregion.y, refreshregion.w, refreshregion.h, refreshdither and "AND w/ HW dithering" or "")
else
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
logger.dbg("setDirty", refreshtype and refreshtype or "nil", "from widget", widget and (widget.name or widget.id or tostring(widget)) or "nil", "w/ NO region", refreshdither and "AND w/ HW dithering" or "")
end
end
end
2012-04-22 19:29:48 +00:00
end
dbg:guard(UIManager, 'setDirty',
nil,
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
function(self, widget, refreshtype, refreshregion, refreshdither)
if not widget or widget == "all" 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:v("INFO: invalid widget for setDirty()", debug.traceback())
end
end)
2012-04-22 19:29:48 +00:00
-- Clear the full repaint & refreshes queues.
-- NOTE: Beware! This doesn't take any prisonners!
-- You shouldn't have to resort to this unless in very specific circumstances!
-- plugins/coverbrowser.koplugin/covermenu.lua building a franken-menu out of buttondialogtitle & buttondialog
-- and wanting to avoid inheriting their original paint/refresh cycle being a prime example.
function UIManager:clearRenderStack()
logger.dbg("clearRenderStack: Clearing the full render stack!")
self._dirty = {}
self._refresh_func_stack = {}
self._refresh_stack = {}
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
--- Sets full refresh rate for e-ink screen.
--
-- Also makes the refresh rate persistent in global reader settings.
function UIManager:setRefreshRate(rate)
logger.dbg("set screen full refresh rate", rate)
self.FULL_REFRESH_COUNT = rate
G_reader_settings:saveSetting("full_refresh_count", rate)
end
--- Gets full refresh rate for e-ink screen.
function UIManager:getRefreshRate(rate)
return self.FULL_REFRESH_COUNT
end
--- Get top widget.
function UIManager:getTopWidget()
return ((self._window_stack[#self._window_stack] or {}).widget or {}).name
end
--- Signals to quit.
2012-04-22 19:29:48 +00:00
function UIManager:quit()
if not self._running then return end
logger.info("quitting uimanager")
self._task_queue_dirty = false
2014-03-13 13:52:43 +00:00
self._running = false
self._run_forever = nil
for i = #self._window_stack, 1, -1 do
table.remove(self._window_stack, i)
end
for i = #self._task_queue, 1, -1 do
table.remove(self._task_queue, i)
end
for i = #self._zeromqs, 1, -1 do
self._zeromqs[i]:stop()
table.remove(self._zeromqs, i)
end
if self.looper then
self.looper:close()
self.looper = nil
end
2012-04-22 19:29:48 +00:00
end
--- Transmits an event to an active widget.
2012-04-22 19:29:48 +00:00
function UIManager:sendEvent(event)
if #self._window_stack == 0 then return end
local top_widget = self._window_stack[#self._window_stack]
2014-03-13 13:52:43 +00:00
-- top level widget has first access to the event
if top_widget.widget:handleEvent(event) then
2014-03-13 13:52:43 +00:00
return
end
if top_widget.widget.active_widgets then
for _, active_widget in ipairs(top_widget.widget.active_widgets) do
if active_widget:handleEvent(event) then return end
2014-03-13 13:52:43 +00:00
end
end
-- if the event is not consumed, active widgets (from top to bottom) can
-- access it. NOTE: _window_stack can shrink on close event
local checked_widgets = {top_widget}
for i = #self._window_stack, 1, -1 do
local widget = self._window_stack[i]
if checked_widgets[widget] == nil then
-- active widgets has precedence to handle this event
-- Note: ReaderUI currently only has one active_widget: readerscreenshot
if widget.widget.active_widgets then
checked_widgets[widget] = true
for _, active_widget in ipairs(widget.widget.active_widgets) do
if active_widget:handleEvent(event) then return end
end
2014-03-13 13:52:43 +00:00
end
if widget.widget.is_always_active or widget.widget.modal then
-- active widgets will handle this event
-- Note: is_always_active widgets currently are widgets that want to show a keyboard
-- and readerconfig
-- By default modal widgets are always on top but if there is more than one modal
-- widget, only last one will be top_widget. e.g. keyboard and confirmbox.
checked_widgets[widget] = true
if widget.widget:handleEvent(event) then return end
end
2014-03-13 13:52:43 +00:00
end
end
2012-04-22 19:29:48 +00:00
end
--- Transmits an event to all registered widgets.
function UIManager:broadcastEvent(event)
-- the widget's event handler might close widgets in which case
-- a simple iterator like ipairs would skip over some entries
local i = 1
2016-06-27 16:43:23 +00:00
while i <= #self._window_stack do
local prev_widget = self._window_stack[i].widget
self._window_stack[i].widget:handleEvent(event)
local top_widget = self._window_stack[i]
if top_widget == nil then
-- top widget closed itself
break
2016-06-27 16:43:23 +00:00
elseif top_widget.widget == prev_widget then
i = i + 1
end
end
end
function UIManager:_checkTasks()
2014-03-13 13:52:43 +00:00
local now = { util.gettime() }
local now_us = now[1] * MILLION + now[2]
2014-03-13 13:52:43 +00:00
local wait_until = nil
-- task.action may schedule other events
self._task_queue_dirty = false
while true do
2016-01-03 09:18:54 +00:00
local nu_task = #self._task_queue
if nu_task == 0 then
-- all tasks checked
break
end
local task = self._task_queue[1]
local task_us = 0
if task.time ~= nil then
task_us = task.time[1] * MILLION + task.time[2]
2014-03-13 13:52:43 +00:00
end
if task_us <= now_us then
-- remove from table
table.remove(self._task_queue, 1)
-- task is pending to be executed right now. do it.
-- NOTE: be careful that task.action() might modify
-- _task_queue here. So need to avoid race condition
task.action(unpack(task.args or {}))
else
-- queue is sorted in ascendant order, safe to assume all items
-- are future tasks for now
wait_until = task.time
break
end
end
return wait_until, now
end
-- precedence of refresh modes:
local refresh_modes = { fast = 1, ui = 2, partial = 3, flashui = 4, flashpartial = 5, full = 6 }
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- NOTE: We might want to introduce a "force_fast" that points to fast, but has the highest priority,
-- for the few cases where we might *really* want to enforce fast (for stuff like panning or skimming?).
-- refresh methods in framebuffer implementation
local refresh_methods = {
fast = "refreshFast",
ui = "refreshUI",
partial = "refreshPartial",
flashui = "refreshFlashUI",
flashpartial = "refreshFlashPartial",
full = "refreshFull",
}
--[[
Compares refresh mode.
Will return the mode that takes precedence.
--]]
local function update_mode(mode1, mode2)
if refresh_modes[mode1] > refresh_modes[mode2] then
logger.dbg("update_mode: Update refresh mode", mode2, "to", mode1)
return mode1
else
return mode2
end
end
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
--[[
Compares dither hints.
Dither always wins.
--]]
local function update_dither(dither1, dither2)
if dither1 and not dither2 then
logger.dbg("update_dither: Update dither hint", dither2, "to", dither1)
return dither1
else
return dither2
end
end
--[[--
Enqueues a refresh.
Widgets call this in their paintTo() method in order to notify
UIManager that a certain part of the screen is to be refreshed.
@param mode
refresh mode ("full", "flashpartial", "flashui", "partial", "ui", "fast")
@param region
Rect() that specifies the region to be updated
optional, update will affect whole screen if not specified.
Note that this should be the exception.
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
@param dither
Bool, a hint to request hardware dithering (if supported)
optional, no dithering requested if not specified or not supported.
--]]
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
function UIManager:_refresh(mode, region, dither)
if not mode then
-- If we're trying to float a dither hint up from a lower widget after a close, mode might be nil...
-- So use the lowest priority refresh mode (short of fast, because that'd do half-toning).
if dither then
mode = "ui"
else
return
end
end
if not region and mode == "full" then
self.refresh_count = 0 -- reset counter on explicit full refresh
end
-- Handle downgrading flashing modes to non-flashing modes, according to user settings.
-- NOTE: Do it before "full" promotion and collision checks/update_mode.
if G_reader_settings:isTrue("avoid_flashing_ui") then
if mode == "flashui" then
mode = "ui"
logger.dbg("_refresh: downgraded flashui refresh to", mode)
elseif mode == "flashpartial" then
mode = "partial"
logger.dbg("_refresh: downgraded flashpartial refresh to", mode)
elseif mode == "partial" and region then
mode = "ui"
logger.dbg("_refresh: downgraded regional partial refresh to", mode)
end
end
-- special case: "partial" refreshes
-- will get promoted every self.FULL_REFRESH_COUNT refreshes
-- since _refresh can be called mutiple times via setDirty called in
-- different widgets before a real screen repaint, we should make sure
-- refresh_count is incremented by only once at most for each repaint
-- NOTE: Ideally, we'd only check for "partial"" w/ no region set (that neatly narrows it down to just the reader).
-- In practice, we also want to promote refreshes in a few other places, except purely text-poor UI elements.
-- (Putting "ui" in that list is problematic with a number of UI elements, most notably, ReaderHighlight,
-- because it is implemented as "ui" over the full viewport, since we can't devise a proper bounding box).
-- So we settle for only "partial", but treating full-screen ones slightly differently.
if 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
-- NOTE: Promote to "full" if no region (reader), to "flashui" otherwise (UI)
if region then
mode = "flashui"
else
mode = "full"
end
logger.dbg("_refresh: promote refresh to", mode)
end
self.refresh_counted = true
end
-- if no region is specified, define default region
region = region or Geom:new{w=Screen:getWidth(), h=Screen:getHeight()}
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- if no dithering hint was specified, don't request dithering
dither = dither or false
-- NOTE: While, ideally, we shouldn't merge refreshes w/ different waveform modes,
-- this allows us to optimize away a number of quirks of our rendering stack
-- (f.g., multiple setDirty calls queued when showing/closing a widget because of update mechanisms),
-- as well as a few actually effective merges
-- (f.g., the disappearance of a selection HL with the following menu update).
for i = 1, #self._refresh_stack do
-- check for collision with refreshes that are already enqueued
if region:intersectWith(self._refresh_stack[i].region) then
-- combine both refreshes' regions
local combined = region:combine(self._refresh_stack[i].region)
-- update the mode, if needed
2016-02-16 07:10:07 +00:00
mode = update_mode(mode, self._refresh_stack[i].mode)
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- dithering hints are viral, one is enough to infect the whole queue
dither = update_dither(dither, self._refresh_stack[i].dither)
-- remove colliding refresh
table.remove(self._refresh_stack, i)
-- and try again with combined data
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
return self:_refresh(mode, combined, dither)
end
end
-- if we've stopped hitting collisions, enqueue the refresh
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
logger.dbg("_refresh: Enqueued", mode, "update for region", region.x, region.y, region.w, region.h, dither and "w/ HW dithering" or "")
table.insert(self._refresh_stack, {mode = mode, region = region, dither = dither})
end
--- Repaints dirty widgets.
function UIManager:_repaint()
-- flag in which we will record if we did any repaints at all
-- will trigger a refresh if set.
local dirty = false
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- remember if any of our repaints were dithered
local dithered = false
-- We don't need to call paintTo() on widgets that are under
-- a widget that covers the full screen
local start_idx = 1
for i = #self._window_stack, 1, -1 do
if self._window_stack[i].widget.covers_fullscreen then
start_idx = i
if i > 1 then
logger.dbg("not painting", i-1, "covered widget(s)")
end
break
end
end
for i = start_idx, #self._window_stack do
local widget = self._window_stack[i]
-- paint if current widget or any widget underneath is dirty
if dirty or self._dirty[widget.widget] then
-- pass hint to widget that we got when setting widget dirty
-- the widget can use this to decide which parts should be refreshed
logger.dbg("painting widget:", widget.widget.name or widget.widget.id or tostring(widget))
widget.widget:paintTo(Screen.bb, widget.x, widget.y, self._dirty[widget.widget])
-- and remove from list after painting
self._dirty[widget.widget] = nil
-- trigger repaint
dirty = true
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- if any of 'em were dithered, we'll want to dither the final refresh
if widget.widget.dithered then
logger.dbg("_repaint: it was dithered, infecting the refresh queue")
dithered = true
end
end
end
-- execute pending refresh functions
for _, refreshfunc in ipairs(self._refresh_func_stack) do
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
local refreshtype, region, dither = refreshfunc()
-- honor dithering hints from *anywhere* in the dirty stack
dither = update_dither(dither, dithered)
if refreshtype then self:_refresh(refreshtype, region, dither) end
end
self._refresh_func_stack = {}
-- we should have at least one refresh if we did repaint. If we don't, we
-- add one now and log a warning if we are debugging
if dirty and #self._refresh_stack == 0 then
logger.dbg("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
-- Honor dithering hints from *anywhere* in the dirty stack
refresh.dither = update_dither(refresh.dither, dithered)
-- If HW dithering is disabled, unconditionally drop the dither flag
if not Screen.hw_dithering then
refresh.dither = nil
end
dbg:v("triggering refresh", refresh)
-- NOTE: We overshoot by 1px to account for potential off-by-ones.
-- This may not strictly be needed anymore, and is blatantly unneeded for full-screen updates,
-- but checkBounds & getPhysicalRect will sanitize that in mxc_update @ ffi/framebuffer_mxcfb ;).
Screen[refresh_methods[refresh.mode]](Screen,
refresh.region.x - 1, refresh.region.y - 1,
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
refresh.region.w + 2, refresh.region.h + 2,
refresh.dither)
end
self._refresh_stack = {}
self.refresh_counted = false
end
function UIManager:forceRePaint()
self:_repaint()
end
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- Used to repaint a specific sub-widget that isn't on the _window_stack itself
-- Useful to avoid repainting a complex widget when we just want to invert an icon, for instance.
-- No safety checks on x & y *by design*. I want this to blow up if used wrong.
function UIManager:widgetRepaint(widget, x, y)
if not widget then return end
logger.dbg("Explicit widgetRepaint:", widget.name or widget.id or tostring(widget), "@ (", x, ",", y, ")")
widget:paintTo(Screen.bb, x, y)
end
function UIManager:setInputTimeout(timeout)
self.INPUT_TIMEOUT = timeout or 200*1000
end
function UIManager:resetInputTimeout()
self.INPUT_TIMEOUT = nil
end
function UIManager:handleInputEvent(input_event)
if input_event.handler ~= "onInputError" then
self.event_hook:execute("InputEvent", input_event)
end
local handler = self.event_handlers[input_event]
if handler then
handler(input_event)
else
self.event_handlers["__default__"](input_event)
end
end
-- Process all pending events on all registered ZMQs.
function UIManager:processZMQs()
for _, zeromq in ipairs(self._zeromqs) do
for input_event in zeromq.waitEvent,zeromq do
self:handleInputEvent(input_event)
end
end
end
function UIManager:handleInput()
local wait_until, now
-- run this in a loop, so that paints can trigger events
-- that will be honored when calculating the time to wait
-- for input events:
repeat
wait_until, now = self:_checkTasks()
--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
logger.info("no dialog left to show")
self:quit()
return nil
end
2012-04-22 19:29:48 +00:00
self:_repaint()
until not self._task_queue_dirty
-- run ZMQs if any
self:processZMQs()
-- Figure out how long to wait.
-- Default to INPUT_TIMEOUT (which may be nil, i.e. block until an event happens).
local wait_us = self.INPUT_TIMEOUT
-- If there's a timed event pending, that puts an upper bound on how long to wait.
if wait_until then
wait_us = math.min(
wait_us or math.huge,
(wait_until[1] - now[1]) * MILLION
+ (wait_until[2] - now[2]))
end
-- If we have any ZMQs registered, ZMQ_TIMEOUT is another upper bound.
if #self._zeromqs > 0 then
wait_us = math.min(wait_us or math.huge, self.ZMQ_TIMEOUT)
end
2012-04-22 19:29:48 +00:00
-- wait for next event
local input_event = Input:waitEvent(wait_us)
-- delegate input_event to handler
if input_event then
self:handleInputEvent(input_event)
2014-03-13 13:52:43 +00:00
end
if self.looper then
logger.info("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?
xpcall(function() self:handleInput() end, function(err)
io.stderr:write(err .. "\n")
io.stderr:write(debug.traceback() .. "\n")
io.stderr:flush()
self.looper:close()
os.exit(1)
end)
end)
end
end
2016-08-02 09:21:27 +00:00
function UIManager:onRotation()
self:setDirty('all', 'full')
self:forceRePaint()
end
function UIManager:initLooper()
2015-06-02 05:07:17 +00:00
if DUSE_TURBO_LIB and not self.looper then
2016-02-16 17:58:24 +00:00
TURBO_SSL = true -- luacheck: ignore
__TURBO_USE_LUASOCKET__ = true -- luacheck: ignore
local turbo = require("turbo")
self.looper = turbo.ioloop.instance()
end
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
self:initLooper()
2015-04-28 03:06:00 +00:00
-- currently there is no Turbo support for Windows
-- use our own main loop
if not self.looper then
while self._running do
self:handleInput()
end
else
self.looper:add_callback(function() self:handleInput() end)
self.looper:start()
end
return self._exit_code
end
-- run uimanager forever for testing purpose
function UIManager:runForever()
self._run_forever = true
return self:run()
2012-04-22 19:29:48 +00:00
end
2013-10-18 20:38:07 +00:00
-- The common operations should be performed before suspending the device. Ditto.
function UIManager:_beforeSuspend()
self:flushSettings()
self:broadcastEvent(Event:new("Suspend"))
end
-- The common operations should be performed after resuming the device. Ditto.
function UIManager:_afterResume()
self:broadcastEvent(Event:new("Resume"))
end
function UIManager:_beforeCharging()
self:broadcastEvent(Event:new("Charging"))
end
function UIManager:_afterNotCharging()
self:broadcastEvent(Event:new("NotCharging"))
end
-- Executes all the operations of a suspending request. This function usually puts the device into
-- suspension.
function UIManager:suspend()
if Device:isCervantes() or Device:isKobo() or Device:isSDL() or Device:isSonyPRSTUX() then
self.event_handlers["Suspend"]()
elseif Device:isKindle() then
Device.powerd:toggleSuspend()
end
end
-- Executes all the operations of a resume request. This function usually wakes up the device.
function UIManager:resume()
if Device:isCervantes() or Device:isKobo() or Device:isSDL() or Device:isSonyPRSTUX() then
self.event_handlers["Resume"]()
elseif Device:isKindle() then
self.event_handlers["OutOfSS"]()
end
end
function UIManager:flushSettings()
self:broadcastEvent(Event:new("FlushSettings"))
end
function UIManager:restartKOReader()
self:quit()
-- This is just a magic number to indicate the restart request for shell scripts.
self._exit_code = 85
end
UIManager:init()
2013-10-18 20:38:07 +00:00
return UIManager