2
0
mirror of https://github.com/koreader/koreader synced 2024-11-16 06:12:56 +00:00
koreader/frontend/ui/screensaver.lua
NiLuJe 812e595608
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 💯 commits! :D)
2019-02-07 01:14:37 +01:00

472 lines
17 KiB
Lua

local Blitbuffer = require("ffi/blitbuffer")
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
local BookStatusWidget = require("ui/widget/bookstatuswidget")
local DataStorage = require("datastorage")
local Device = require("device")
local DocSettings = require("docsettings")
local DocumentRegistry = require("document/documentregistry")
local InfoMessage = require("ui/widget/infomessage")
local ImageWidget = require("ui/widget/imagewidget")
local ScreenSaverWidget = require("ui/widget/screensaverwidget")
local UIManager = require("ui/uimanager")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local _ = require("gettext")
local Screen = Device.screen
local T = require("ffi/util").template
local screensaver_provider = {
["jpg"] = true,
["jpeg"] = true,
["png"] = true,
["gif"] = true,
["tif"] = true,
["tiff"] = true,
}
local default_screensaver_message = _("Sleeping")
local Screensaver = {}
local function getRandomImage(dir)
if string.sub(dir, string.len(dir)) ~= "/" then
dir = dir .. "/"
end
local pics = {}
local i = 0
math.randomseed(os.time())
local ok, iter, dir_obj = pcall(lfs.dir, dir)
if ok then
for entry in iter, dir_obj do
if lfs.attributes(dir .. entry, "mode") == "file" then
local extension = string.lower(string.match(entry, ".+%.([^.]+)") or "")
if screensaver_provider[extension] then
i = i + 1
pics[i] = entry
end
end
end
if i == 0 then
return nil
end
else
return nil
end
return dir .. pics[math.random(i)]
end
function Screensaver:chooseFolder()
local buttons = {}
table.insert(buttons, {
{
text = _("Choose screensaver directory"),
callback = function()
UIManager:close(self.choose_dialog)
require("ui/downloadmgr"):new{
onConfirm = function(path)
logger.dbg("set screensaver directory to", path)
G_reader_settings:saveSetting("screensaver_dir", path)
UIManager:show(InfoMessage:new{
text = T(_("Screensaver directory set to:\n%1"), path),
timeout = 3,
})
end,
}:chooseDir()
end,
}
})
table.insert(buttons, {
{
text = _("Close"),
callback = function()
UIManager:close(self.choose_dialog)
end,
}
})
local screensaver_dir = G_reader_settings:readSetting("screensaver_dir")
if screensaver_dir == nil then
screensaver_dir = DataStorage:getDataDir() .. "/screenshots/"
end
self.choose_dialog = ButtonDialogTitle:new{
title = T(_("Current screensaver image directory:\n %1"), screensaver_dir),
buttons = buttons
}
UIManager:show(self.choose_dialog)
end
function Screensaver:chooseFile()
local buttons = {}
table.insert(buttons, {
{
text = _("Choose screensaver image"),
callback = function()
UIManager:close(self.choose_dialog)
local FileChooser = require("ui/widget/filechooser")
local image_chooser = FileChooser:new{
title = _("Choose screensaver image"),
no_title = false,
path = self.root_path,
show_hidden = G_reader_settings:readSetting("show_hidden"),
focused_path = self.focused_file,
collate = G_reader_settings:readSetting("collate") or "strcoll",
reverse_collate = G_reader_settings:isTrue("reverse_collate"),
show_parent = self.show_parent,
width = Screen:getWidth(),
height = Screen:getHeight(),
is_popout = false,
is_borderless = true,
has_close_button = true,
perpage = G_reader_settings:readSetting("items_per_page"),
file_filter = function(filename)
local util = require("util")
local suffix = util.getFileNameSuffix(filename)
if screensaver_provider[suffix] then
return true
end
end,
}
function image_chooser:onFileSelect(file) -- luacheck: ignore
local ConfirmBox = require("ui/widget/confirmbox")
UIManager:show(ConfirmBox:new{
text = T(_("Set screensaver image to:\n%1"), require("ffi/util").basename(file)),
ok_callback = function()
G_reader_settings:saveSetting("screensaver_image", file)
UIManager:close(image_chooser)
end
})
return true
end
UIManager:show(image_chooser)
end,
}
})
table.insert(buttons, {
{
text = _("Close"),
callback = function()
UIManager:close(self.choose_dialog)
end,
}
})
local screensaver_image = G_reader_settings:readSetting("screensaver_image")
if screensaver_image == nil then
screensaver_image = DataStorage:getDataDir() .. "/resources/koreader.png"
end
self.choose_dialog = ButtonDialogTitle:new{
title = T(_("Current screensaver image:\n %1"), screensaver_image),
buttons = buttons
}
UIManager:show(self.choose_dialog)
end
function Screensaver:stretchImages()
return G_reader_settings:isTrue("screensaver_stretch_images")
end
function Screensaver:whiteBackground()
return G_reader_settings:isTrue("screensaver_white_background")
end
function Screensaver:excluded()
local lastfile = G_reader_settings:readSetting("lastfile")
local exclude_ss = false -- consider it not excluded if there's no docsetting
if DocSettings:hasSidecarFile(lastfile) then
local doc_settings = DocSettings:open(lastfile)
exclude_ss = doc_settings:readSetting("exclude_screensaver")
doc_settings:close()
end
return exclude_ss or false
end
function Screensaver:setMessage()
local InputDialog = require("ui/widget/inputdialog")
local screensaver_message = G_reader_settings:readSetting("screensaver_message")
if screensaver_message == nil then
screensaver_message = default_screensaver_message
end
self.input_dialog = InputDialog:new{
title = "Screensaver message",
input = screensaver_message,
buttons = {
{
{
text = _("Cancel"),
callback = function()
UIManager:close(self.input_dialog)
end,
},
{
text = _("Set message"),
is_enter_default = true,
callback = function()
G_reader_settings:saveSetting("screensaver_message", self.input_dialog:getInputText())
UIManager:close(self.input_dialog)
end,
},
},
},
}
UIManager:show(self.input_dialog)
self.input_dialog:onShowKeyboard()
end
function Screensaver:show(event, fallback_message)
-- These 2 (optional) parameters are to support poweroff and reboot actions
-- on Kobo (see uimanager.lua)
if self.left_msg then
UIManager:close(self.left_msg)
self.left_msg = nil
end
local covers_fullscreen = true -- hint for UIManager:_repaint()
local overlay_message
local prefix = event and event.."_" or "" -- "", "poweroff_" or "reboot_"
local screensaver_type = G_reader_settings:readSetting(prefix.."screensaver_type")
if prefix and not screensaver_type then
-- No manually added setting for poweroff/reboot, fallback to using the
-- same settings as for suspend that could be set via menus
screensaver_type = G_reader_settings:readSetting("screensaver_type")
prefix = ""
-- And display fallback_message over the common screensaver,
-- so user can distinguish between suspend (no message) and
-- poweroff (overlay message)
overlay_message = fallback_message
end
if screensaver_type == nil then
screensaver_type = "message"
end
if screensaver_type == "disable" then
return
end
local widget = nil
local background = Blitbuffer.COLOR_WHITE
if screensaver_type == "cover" then
local lastfile = G_reader_settings:readSetting("lastfile")
local exclude = false -- consider it not excluded if there's no docsetting
if DocSettings:hasSidecarFile(lastfile) then
local doc_settings = DocSettings:open(lastfile)
exclude = doc_settings:readSetting("exclude_screensaver")
doc_settings:close()
end
if exclude ~= true then
if lfs.attributes(lastfile, "mode") == "file" then
local doc = DocumentRegistry:openDocument(lastfile)
if doc.loadDocument then -- CreDocument
doc:loadDocument(false) -- load only metadata
end
local image = doc:getCoverPageImage()
doc:close()
if image ~= nil then
widget = ImageWidget:new{
image = image,
image_disposable = true,
alpha = true,
height = Screen:getHeight(),
width = Screen:getWidth(),
scale_factor = not self:stretchImages() and 0 or nil,
}
if not self:whiteBackground() then
background = Blitbuffer.COLOR_BLACK
end
else
screensaver_type = "random_image"
end
else
screensaver_type = "random_image"
end
else --fallback to random images if this book cover is excluded
screensaver_type = "random_image"
end
end
if screensaver_type == "bookstatus" then
local lastfile = G_reader_settings:readSetting("lastfile")
if lfs.attributes(lastfile, "mode") == "file" then
local doc = DocumentRegistry:openDocument(lastfile)
local doc_settings = DocSettings:open(lastfile)
local instance = require("apps/reader/readerui"):_getRunningInstance()
if instance ~= nil then
widget = BookStatusWidget:new {
thumbnail = doc:getCoverPageImage(),
props = doc:getProps(),
document = doc,
settings = doc_settings,
view = instance.view,
readonly = true,
}
else
screensaver_type = "message"
end
doc:close()
doc_settings:close()
else
screensaver_type = "message"
end
end
if screensaver_type == "random_image" then
local screensaver_dir = G_reader_settings:readSetting(prefix.."screensaver_dir")
if screensaver_dir == nil and prefix ~= "" then
screensaver_dir = G_reader_settings:readSetting("screensaver_dir")
end
if screensaver_dir == nil then
screensaver_dir = DataStorage:getDataDir() .. "/screenshots/"
end
local image_file = getRandomImage(screensaver_dir)
if image_file == nil then
screensaver_type = "message"
else
widget = ImageWidget:new{
file = image_file,
file_do_cache = false,
alpha = true,
height = Screen:getHeight(),
width = Screen:getWidth(),
scale_factor = not self:stretchImages() and 0 or nil,
}
if not self:whiteBackground() then
background = Blitbuffer.COLOR_BLACK
end
end
end
if screensaver_type == "image_file" then
local screensaver_image = G_reader_settings:readSetting(prefix.."screensaver_image")
if screensaver_image == nil and prefix ~= "" then
screensaver_image = G_reader_settings:readSetting("screensaver_image")
end
if screensaver_image == nil then
screensaver_image = DataStorage:getDataDir() .. "/resources/koreader.png"
end
if lfs.attributes(screensaver_image, "mode") ~= "file" then
screensaver_type = "message"
else
widget = ImageWidget:new{
file = screensaver_image,
file_do_cache = false,
alpha = true,
height = Screen:getHeight(),
width = Screen:getWidth(),
scale_factor = not self:stretchImages() and 0 or nil,
}
if not self:whiteBackground() then
background = Blitbuffer.COLOR_BLACK
end
end
end
if screensaver_type == "readingprogress" then
if Screensaver.getReaderProgress ~= nil then
widget = Screensaver.getReaderProgress()
else
screensaver_type = "message"
end
end
if screensaver_type == "message" then
local screensaver_message = G_reader_settings:readSetting(prefix.."screensaver_message")
if not self:whiteBackground() then
background = nil -- no background filling, let book text visible
covers_fullscreen = false
end
if screensaver_message == nil and prefix ~= "" then
screensaver_message = G_reader_settings:readSetting("screensaver_message")
end
if screensaver_message == nil then
screensaver_message = fallback_message or default_screensaver_message
end
widget = InfoMessage:new{
text = screensaver_message,
readonly = true,
}
-- No overlay needed as we just displayed the message
overlay_message = nil
end
if overlay_message then
widget = self:addOverlayMessage(widget, overlay_message)
end
if widget then
self.left_msg = ScreenSaverWidget:new{
widget = widget,
background = background,
covers_fullscreen = covers_fullscreen,
}
self.left_msg.modal = true
-- Refresh whole screen for other types
self.left_msg.dithered = true
UIManager:show(self.left_msg, "full")
end
end
function Screensaver:close()
if self.left_msg == nil then return end
local screensaver_delay = G_reader_settings:readSetting("screensaver_delay")
local screensaver_delay_number = tonumber(screensaver_delay)
if screensaver_delay_number then
UIManager:scheduleIn(screensaver_delay_number, function()
logger.dbg("close screensaver")
if self.left_msg then
UIManager:close(self.left_msg, "full")
self.left_msg = nil
end
end)
elseif screensaver_delay == "disable" or screensaver_delay == nil then
logger.dbg("close screensaver")
if self.left_msg then
UIManager:close(self.left_msg)
self.left_msg = nil
end
else
logger.dbg("tap to exit from screensaver")
end
end
function Screensaver:addOverlayMessage(widget, text)
local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer")
local OverlapGroup = require("ui/widget/overlapgroup")
local RenderText = require("ui/rendertext")
local RightContainer = require("ui/widget/container/rightcontainer")
local Size = require("ui/size")
local TextBoxWidget = require("ui/widget/textboxwidget")
local TextWidget = require("ui/widget/textwidget")
local face = Font:getFace("infofont")
local screen_w, screen_h = Screen:getWidth(), Screen:getHeight()
local textw
-- Don't make our message reach full screen width
local tsize = RenderText:sizeUtf8Text(0, screen_w, face, text)
if tsize.x < screen_w * 0.9 then
textw = TextWidget:new{
text = text,
face = face,
}
else -- if text too wide, use TextBoxWidget for multi lines display
textw = TextBoxWidget:new{
text = text,
face = face,
width = math.floor(screen_w * 0.9)
}
end
textw = FrameContainer:new{
background = Blitbuffer.COLOR_WHITE,
bordersize = Size.border.default,
margin = 0,
textw,
}
textw = RightContainer:new{
dimen = {
w = screen_w,
h = textw:getSize().h,
},
textw,
}
widget = OverlapGroup:new{
dimen = {
h = screen_w,
w = screen_h,
},
widget,
textw,
}
return widget
end
return Screensaver