Initial Kindle PW5 support (#8856)

* Rejig frontlight warmth API to more closely match the existing API, and, hopefully, clarify some of its quirks, and reduce boilerplate and duplicate code in platform implementations.
* Tweak Kindle:setDateTime to prefer using the platform's custom script, as in interacts better with the stock UI. And make the fallbacks handle old busybox versions better.
* Add Kindle PW5 support ;).
* Add warmth support to the Kindle platform.
* Random TextBoxWidget cleanups: make sure we immediately free destroyed instances.
* FrontLightWidget: Refactor to make it slightly less obnoxious to grok and update; i.e., separate layout from update, and properly separate brightness from warmth handling. Move to simpler widgets instead of reinventing the wheel.
* TextBoxWidgets: Implement `setText` to match TextWidget's API, as some callers may be using the two interchangeably (i.e., Button).
* NaturalLightWidget: Make sure we pass a string to InputText
* InputText: Add debug guards to catch bad callers not passing strings ;).
reviewable/pr8907/r1
NiLuJe 2 years ago committed by GitHub
parent f709cc48e6
commit 217a73f3c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1 +1 @@
Subproject commit 5ac0b049192ae68fc8809de44ed7c2741c64836f
Subproject commit c4c46e57854975eb7593b6f0d204721f4a6732f0

@ -160,7 +160,7 @@ local footerTextGeneratorMap = {
local prefix = symbol_prefix[symbol_type].frontlight_warmth
local powerd = Device:getPowerDevice()
if powerd:isFrontlightOn() then
local warmth = powerd:getWarmth()
local warmth = powerd:frontlightWarmth()
if warmth then
return (prefix .. " %d%%"):format(warmth)
end

@ -438,7 +438,7 @@ function Device:_showLightDialog()
self.powerd.fl_intensity = self.powerd:frontlightIntensityHW()
logger.dbg("Dialog OK, brightness: " .. self.powerd.fl_intensity)
if android.isWarmthDevice() then
self.powerd.fl_warmth = self.powerd:getWarmth()
self.powerd.fl_warmth = self.powerd:frontlightWarmthHW()
logger.dbg("Dialog OK, warmth: " .. self.powerd.fl_warmth)
end
local Event = require("ui/event")

@ -42,18 +42,14 @@ function AndroidPowerD:init()
self.fl_warmth_min = android.getScreenMinWarmth()
self.fl_warmth_max = android.getScreenMaxWarmth()
self.warm_diff = self.fl_warmth_max - self.fl_warmth_min
self.fl_warmth = self:getWarmth()
end
end
function AndroidPowerD:setWarmth(warmth)
self.fl_warmth = warmth
local new_warmth = math.floor(warmth * self.fl_warmth_max / 100)
android.setScreenWarmth(new_warmth)
self:stateChanged()
function AndroidPowerD:setWarmthHW(warmth)
android.setScreenWarmth(warmth)
end
function AndroidPowerD:getWarmth()
function AndroidPowerD:frontlightWarmthHW()
return android.getScreenWarmth() * self.warm_diff
end

@ -5,7 +5,6 @@ local battery_sysfs = "/sys/devices/platform/pmic_battery.1/power_supply/mc13892
local CervantesPowerD = BasePowerD:new{
fl = nil,
fl_warmth = nil,
fl_min = 0,
fl_max = 100,
@ -15,16 +14,16 @@ local CervantesPowerD = BasePowerD:new{
status_file = battery_sysfs .. 'status'
}
-- We can't read back the current state from the OS or hardware.
-- Use the last value stored in KOReader settings instead.
function CervantesPowerD:frontlightWarmthHW()
return G_reader_settings:readSetting("frontlight_warmth") or 0
end
function CervantesPowerD:_syncLightOnStart()
-- We can't read value from the OS or hardware.
-- Use last values stored in koreader settings.
local new_intensity = G_reader_settings:readSetting("frontlight_intensity") or nil
local is_frontlight_on = G_reader_settings:readSetting("is_frontlight_on") or nil
local new_warmth = nil
if self.fl_warmth ~= nil then
new_warmth = G_reader_settings:readSetting("frontlight_warmth") or nil
end
if new_intensity ~= nil then
self.hw_intensity = new_intensity
@ -34,9 +33,7 @@ function CervantesPowerD:_syncLightOnStart()
self.initial_is_fl_on = is_frontlight_on
end
if new_warmth ~= nil then
self.fl_warmth = new_warmth
end
self.fl_warmth = self:frontlightWarmthHW()
if self.initial_is_fl_on == false and self.hw_intensity == 0 then
self.hw_intensity = 1
@ -67,7 +64,6 @@ function CervantesPowerD:init()
end
end
self.fl = SysfsLight:new(self.device.frontlight_settings)
self.fl_warmth = 0
self:_syncLightOnStart()
else
local kobolight = require("ffi/kobolight")
@ -122,16 +118,9 @@ function CervantesPowerD:setIntensityHW(intensity)
self:_decideFrontlightState()
end
function CervantesPowerD:setWarmth(warmth)
if self.fl == nil then return end
self.fl_warmth = warmth
self.fl:setWarmth(self.fl_warmth)
self:stateChanged()
end
function CervantesPowerD:getWarmth()
function CervantesPowerD:setWarmthHW(warmth)
if self.fl == nil then return end
return self.fl_warmth
self.fl:setWarmth(warmth)
end
function CervantesPowerD:getCapacityHW()
@ -151,7 +140,7 @@ end
function CervantesPowerD:afterResume()
if self.fl == nil then return end
-- just re-set it to self.hw_intensity that we haven't change on Suspend
if self.fl_warmth == nil then
if not self.device:hasNaturalLight() then
self.fl:setBrightness(self.hw_intensity)
else
self.fl:setNaturalBrightness(self.hw_intensity, self.fl_warmth)

@ -1,10 +1,14 @@
local UIManager -- will be updated when available
local Math = require("optmath")
local TimeVal = require("ui/timeval")
local logger = require("logger")
local BasePowerD = {
fl_min = 0, -- min frontlight intensity
fl_max = 10, -- max frontlight intensity
fl_intensity = nil, -- frontlight intensity
fl_warmth_min = 0, -- min warmth level
fl_warmth_max = 100, -- max warmth level
fl_warmth = nil, -- warmth level
batt_capacity = 0, -- battery capacity
aux_batt_capacity = 0, -- auxiliary battery capacity
device = nil, -- device object
@ -25,6 +29,15 @@ function BasePowerD:new(o)
o.fl_intensity = o:frontlightIntensityHW()
o:_decideFrontlightState()
end
--- @note: Post-init, as the min/max values may be computed at runtime on some platforms
assert(o.fl_warmth_min < o.fl_warmth_max)
-- For historical reasons, the *public* PowerD warmth API always expects warmth to be in the [0...100] range...
self.warmth_scale = 100 / o.fl_warmth_max
--- @note: Some platforms cannot actually read fl/warmth level from the HW,
-- in which case the implementation should just return self.fl_warmth (c.f., kobo).
if o.device and o.device:hasNaturalLight() then
o.fl_warmth = o:frontlightWarmthHW()
end
return o
end
@ -34,6 +47,8 @@ end
function BasePowerD:init() end
function BasePowerD:setIntensityHW(intensity) end
--- @note: Unlike the "public" setWarmth, this one takes a value in the *native* scale!
function BasePowerD:setWarmthHW(warmth) end
function BasePowerD:getCapacityHW() return 0 end
function BasePowerD:getAuxCapacityHW() return 0 end
function BasePowerD:isAuxBatteryConnectedHW() return false end
@ -45,11 +60,12 @@ function BasePowerD:frontlightIntensityHW() return 0 end
function BasePowerD:isFrontlightOnHW() return self.fl_intensity > self.fl_min end
function BasePowerD:turnOffFrontlightHW() self:setIntensityHW(self.fl_min) end
function BasePowerD:turnOnFrontlightHW() self:setIntensityHW(self.fl_intensity) end --- @fixme: what if fl_intensity == fl_min (c.f., kindle)?
-- Anything needs to be done before do a real hardware suspend. Such as turn off
-- front light.
function BasePowerD:frontlightWarmthHW() return 0 end
-- Anything that needs to be done before doing a real hardware suspend.
-- (Such as turning the front light off).
function BasePowerD:beforeSuspend() end
-- Anything needs to be done after do a real hardware resume. Such as resume
-- front light state.
-- Anything that needs to be done after doing a real hardware resume.
-- (Such as restoring front light state).
function BasePowerD:afterResume() end
function BasePowerD:isFrontlightOn()
@ -71,6 +87,9 @@ function BasePowerD:frontlightIntensity()
assert(self ~= nil)
if not self.device:hasFrontlight() then return 0 end
if self:isFrontlightOff() then return 0 end
--- @note: We assume that nothing other than us will set the frontlight level,
--- so we only actually query the HW during initialization.
--- (Also, some platforms do not actually have any way of querying the HW).
return self.fl_intensity
end
@ -105,6 +124,15 @@ function BasePowerD:turnOnFrontlight()
return true
end
function BasePowerD:frontlightWarmth()
assert(self ~= nil)
if not self.device:hasNaturalLight() then
return 0
end
--- @note: No live query, much like frontlightIntensity
return self.fl_warmth
end
function BasePowerD:read_int_file(file)
local fd = io.open(file, "r")
if fd then
@ -143,6 +171,7 @@ function BasePowerD:normalizeIntensity(intensity)
return intensity > self.fl_max and self.fl_max or intensity
end
--- @note: Takes an intensity in the native scale (i.e., [self.fl_min, self.fl_max])
function BasePowerD:setIntensity(intensity)
if not self.device:hasFrontlight() then return false end
if intensity == self:frontlightIntensity() then return false end
@ -154,6 +183,32 @@ function BasePowerD:setIntensity(intensity)
return true
end
function BasePowerD:normalizeWarmth(warmth)
warmth = warmth < 0 and 0 or warmth
return warmth > 100 and 100 or warmth
end
function BasePowerD:toNativeWarmth(ko_warmth)
return Math.round(ko_warmth / self.warmth_scale)
end
function BasePowerD:fromNativeWarmth(nat_warmth)
return Math.round(nat_warmth * self.warmth_scale)
end
--- @note: Takes a warmth in the *KOReader* scale (i.e., [0, 100], *sic*)
function BasePowerD:setWarmth(warmth)
if not self.device:hasNaturalLight() then return false end
if warmth == self:frontlightWarmth() then return false end
-- Which means that fl_warmth is *also* in the KOReader scale (unlike fl_intensity)
self.fl_warmth = self:normalizeWarmth(warmth)
local nat_warmth = self:toNativeWarmth(self.fl_warmth)
logger.dbg("set light warmth", self.fl_warmth, "->", nat_warmth)
self:setWarmthHW(nat_warmth)
self:stateChanged()
return true
end
function BasePowerD:getCapacity()
-- BasePowerD is loaded before UIManager.
-- Nothing *currently* calls this before UIManager is actually loaded, but future-proof this anyway.

@ -29,31 +29,53 @@ local function kindleEnableWifi(toggle)
end
end
-- Check if wifid thinks that the WiFi is enabled
--[[
local function isWifiUp()
local status
local haslipc, lipc = pcall(require, "liblipclua")
local lipc_handle = nil
if haslipc and lipc then
lipc_handle = lipc.init("com.github.koreader.networkmgr")
end
if lipc_handle then
status = lipc_handle:get_int_property("com.lab126.wifid", "enable") or 0
local status = lipc_handle:get_int_property("com.lab126.wifid", "enable") or 0
lipc_handle:close()
return status == 1
else
local std_out = io.popen("lipc-get-prop -i com.lab126.wifid enable", "r")
if std_out then
local result = std_out:read("*all")
local result = std_out:read("*number")
std_out:close()
if result then
return tonumber(result)
else
return 0
if not result then
return false
end
return result == 1
else
return 0
return false
end
end
return status
end
--]]
-- Faster lipc-less variant ;).
local function isWifiUp()
-- Read carrier state from sysfs (so far, all Kindles appear to use wlan0)
-- NOTE: We can afford to use CLOEXEC, as devices too old for it don't support Wi-Fi anyway ;).
local file = io.open("/sys/class/net/wlan0/carrier", "re")
-- File only exists while Wi-Fi module is loaded.
if not file then
return false
end
-- 0 means not connected, 1 connected
local out = file:read("*number")
file:close()
return out == 1
end
--[[
@ -104,6 +126,7 @@ local Kindle = Generic:new{
-- NOTE: We can cheat by adding a platform-specific entry here, because the only code that will check for this is here.
isSpecialOffers = isSpecialOffers(),
hasOTAUpdates = yes,
hasFastWifiStatusQuery = yes,
-- NOTE: HW inversion is generally safe on mxcfb Kindles
canHWInvert = yes,
-- NOTE: Newer devices will turn the frontlight off at 0
@ -114,6 +137,8 @@ local Kindle = Generic:new{
-- Rex & Zelda devices sport an updated driver.
isZelda = no,
isRex = no,
-- So do devices running on a MediaTek SoC
isMTK = no,
-- But of course, some devices don't actually support all the features the kernel exposes...
isNightModeChallenged = no,
-- NOTE: While this ought to behave on Zelda/Rex, turns out, nope, it really doesn't work on *any* of 'em :/ (c.f., ko#5884).
@ -140,9 +165,7 @@ function Kindle:initNetworkManager(NetworkMgr)
end
end
NetworkMgr.isWifiOn = function()
return 1 == isWifiUp()
end
NetworkMgr.isWifiOn = isWifiUp
end
function Kindle:supportsScreensaver()
@ -155,23 +178,35 @@ end
function Kindle:setDateTime(year, month, day, hour, min, sec)
if hour == nil or min == nil then return true end
local commands = {}
if year and month and day then
table.insert(commands, string.format("date -s '%d-%d-%d %d:%d:%d'", year, month, day, hour, min, sec))
--Kindle DX
--BusyBox v1.7.2 (2011-01-13 18:01:58 PST) multi-call binary
--Usage: date [OPTION]... [MMDDhhmm[[CC]YY][.ss]] [+FORMAT]
table.insert(commands, string.format("date -s '%02d%02d%02d%02d%04d.%02d'", month, day, hour, min, year, sec))
local lfs = require("libs/libkoreader-lfs")
-- Prefer using the setdate wrapper if possible, as it will poke the native UI, too.
if lfs.attributes("/usr/sbin/setdate", "mode") == "file" then
local t = os.date("*t") -- Start with now to make sure we have a full table
t.year = year or t.year
t.month = month or t.month
t.day = day or t.day
t.hour = hour
t.min = min
t.sec = sec or t.sec
local epoch = os.time(t)
local command = string.format("/usr/sbin/setdate '%d'", epoch)
return os.execute(command) == 0
else
table.insert(commands,string.format("date -s '%d:%d'",hour, min))
end
for _, command in ipairs(commands) do
local command
if year and month and day then
command = string.format("date -s '%d-%d-%d %d:%d:%d' '+%Y-%m-%d %H:%M:%S'", year, month, day, hour, min, sec)
else
command = string.format("date -s '%d:%d' '+%H:%M'", hour, min)
end
if os.execute(command) == 0 then
os.execute("hwclock -u -w")
return true
else
return false
end
end
return false
end
function Kindle:usbPlugIn()
@ -266,8 +301,6 @@ end
function Kindle:usbPlugOut()
-- NOTE: See usbPlugIn(), we don't have anything fancy to do here either.
--- @todo signal filemanager for file changes 13.06 2012 (houqp)
self.charging_mode = false
end
@ -416,6 +449,11 @@ local KindleOasis3 = Kindle:new{
isZelda = yes,
isTouchDevice = yes,
hasFrontlight = yes,
--- @fixme: Requires a proper KindleOasis3.init, notably with the right warmth_intensity_file entry.
--[[
hasNaturalLight = yes,
hasNaturalLightMixer = yes,
--]]
hasKeys = yes,
hasGSensor = yes,
display_dpi = 300,
@ -451,6 +489,22 @@ local KindleBasic3 = Kindle:new{
touch_dev = "/dev/input/event2",
}
local KindlePaperWhite5 = Kindle:new{
model = "KindlePaperWhite5",
isMTK = yes,
isTouchDevice = yes,
hasFrontlight = yes,
hasNaturalLight = yes,
-- NOTE: We *can* technically control both LEDs independently,
-- but the mix is device-specific, we don't have access to the LUT for the mix powerd is using,
-- and the widget is designed for the Kobo Aura One anyway, so, hahaha, nope.
hasNaturalLightMixer = yes,
display_dpi = 300,
touch_dev = "/dev/input/by-path/platform-1001e000.i2c-event",
-- NOTE: While hardware dithering (via MDP) should be a thing, it doesn't appear to do anything right now :/.
canHWDither = no,
}
function Kindle2:init()
self.screen = require("ffi/framebuffer_einkfb"):new{device = self, debug = logger.dbg}
self.powerd = require("device/kindle/powerd"):new{
@ -859,8 +913,33 @@ function KindleBasic3:init()
self.input.open("fake_events")
end
function KindlePaperWhite5:init()
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
self.powerd = require("device/kindle/powerd"):new{
device = self,
fl_intensity_file = "/sys/class/backlight/fp9966-bl1/brightness",
warmth_intensity_file = "/sys/class/backlight/fp9966-bl0/brightness",
batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity",
is_charging_file = "/sys/class/power_supply/bd71827_bat/charging",
}
-- Enable the so-called "fast" mode, so as to prevent the driver from silently promoting refreshes to REAGL.
self.screen:_MTK_ToggleFastMode(true)
Kindle.init(self)
self.input.open(self.touch_dev)
self.input.open("fake_events")
end
function KindleTouch:exit()
if self:isMTK() then
-- Disable the so-called "fast" mode
self.screen:_MTK_ToggleFastMode(false)
end
Generic.exit(self)
if self.isSpecialOffers then
-- Wakey wakey...
if os.getenv("AWESOME_STOPPED") == "yes" then
@ -887,6 +966,7 @@ KindleBasic2.exit = KindleTouch.exit
KindlePaperWhite4.exit = KindleTouch.exit
KindleBasic3.exit = KindleTouch.exit
KindleOasis3.exit = KindleTouch.exit
KindlePaperWhite5.exit = KindleTouch.exit
function Kindle3:exit()
-- send double menu key press events to trigger screen refresh
@ -940,9 +1020,10 @@ local pw4_set = Set { "0PP", "0T1", "0T2", "0T3", "0T4", "0T5", "0T6",
"16Q", "16R", "16S", "16T", "16U", "16V" }
local kt4_set = Set { "10L", "0WF", "0WG", "0WH", "0WJ", "0VB" }
local koa3_set = Set { "11L", "0WQ", "0WP", "0WN", "0WM", "0WL" }
local pw5_set = Set { "1LG", "1Q0", "1PX", "1VD", "219", "21A", "2BH", "2BJ" }
if kindle_sn_lead == "B" or kindle_sn_lead == "9" then
local kindle_devcode = string.sub(kindle_sn,3,4)
local kindle_devcode = string.sub(kindle_sn, 3, 4)
if k2_set[kindle_devcode] then
return Kindle2
@ -966,7 +1047,7 @@ if kindle_sn_lead == "B" or kindle_sn_lead == "9" then
return KindleVoyage
end
else
local kindle_devcode_v2 = string.sub(kindle_sn,4,6)
local kindle_devcode_v2 = string.sub(kindle_sn, 4, 6)
if pw3_set[kindle_devcode_v2] then
return KindlePaperWhite3
@ -982,6 +1063,8 @@ else
return KindleBasic3
elseif koa3_set[kindle_devcode_v2] then
return KindleOasis3
elseif pw5_set[kindle_devcode_v2] then
return KindlePaperWhite5
end
end

@ -3,6 +3,7 @@ local BasePowerD = require("device/generic/powerd")
local KindlePowerD = BasePowerD:new{
fl_min = 0, fl_max = 24,
fl_warmth_min = 0, fl_warmth_max = 24,
lipc_handle = nil,
}
@ -87,14 +88,36 @@ function KindlePowerD:setIntensityHW(intensity)
-- it knows what the UI values should map to for the specific hardware much better than us.
if self.lipc_handle ~= nil then
-- NOTE: We want to bypass setIntensity's shenanigans and simply restore the light as-is
self.lipc_handle:set_int_property(
"com.lab126.powerd", "flIntensity", intensity)
self.lipc_handle:set_int_property("com.lab126.powerd", "flIntensity", intensity)
end
if turn_it_off then
-- NOTE: when intensity is 0, we want to *really* kill the light, so do it manually
-- (asking lipc to set it to 0 would in fact set it to > 0 on ! canTurnFrontlightOff Kindles).
-- We do *both* to make the fl restore on resume less jarring on devices where lipc 0 != off.
os.execute("printf '%s' ".. intensity .." > " .. self.fl_intensity_file)
-- And in case there are two LED groups...
if self.warmth_intensity_file then
os.execute("printf '%s' ".. intensity .." > " .. self.warmth_intensity_file)
end
end
end
function KindlePowerD:frontlightWarmthHW()
if self.lipc_handle ~= nil then
local nat_warmth = self.lipc_handle:get_int_property("com.lab126.powerd", "currentAmberLevel")
if nat_warmth then
-- [0...24] -> [0...100]
return self:fromNativeWarmth(nat_warmth)
else
return 0
end
end
end
function KindlePowerD:setWarmthHW(warmth)
if self.lipc_handle ~= nil then
self.lipc_handle:set_int_property("com.lab126.powerd", "currentAmberLevel", warmth)
end
end

@ -1,4 +1,5 @@
local BasePowerD = require("device/generic/powerd")
local Math = require("optmath")
local NickelConf = require("device/kobo/nickel_conf")
local SysfsLight = require ("device/sysfs_light")
local ffiUtil = require("ffi/util")
@ -16,7 +17,6 @@ local KoboPowerD = BasePowerD:new{
battery_sysfs = nil,
aux_battery_sysfs = nil,
fl_warmth_min = 0, fl_warmth_max = 100,
fl_warmth = nil,
fl_was_on = nil,
}
@ -41,8 +41,8 @@ function KoboPowerD:_syncKoboLightOnStart()
if new_color ~= nil then
-- ColorSetting is stored as a color temperature scale in Kelvin,
-- from 1500 to 6400
-- so normalize this to [0,100] on our end.
new_warmth = (100 - math.floor((new_color - 1500) / 49))
-- so normalize this to [0, 100] on our end.
new_warmth = (100 - Math.round((new_color - 1500) / 49))
end
end
if is_frontlight_on == nil then
@ -132,7 +132,7 @@ function KoboPowerD:init()
if self.device:hasNaturalLight() then
local nl_config = G_reader_settings:readSetting("natural_light_config")
if nl_config then
for key,val in pairs(nl_config) do
for key, val in pairs(nl_config) do
self.device.frontlight_settings[key] = val
end
end
@ -250,22 +250,19 @@ function KoboPowerD:setIntensityHW(intensity)
self:_decideFrontlightState()
end
function KoboPowerD:setWarmth(warmth)
-- NOTE: We *can* actually read this from the system (as well as frontlight level, since Mk. 7),
-- but this is already a huge mess, so, keep ignoring it...
function KoboPowerD:frontlightWarmthHW()
return self.fl_warmth
end
function KoboPowerD:setWarmthHW(warmth)
if self.fl == nil then return end
self.fl_warmth = warmth or self.fl_warmth
-- Don't turn the light back on on legacy NaturalLight devices just for the sake of setting the warmth!
-- That's because we can only set warmth independently of brightness on devices with a mixer.
-- On older ones, calling setWarmth *will* actually set the brightness, too!
if self.device:hasNaturalLightMixer() or self:isFrontlightOnHW() then
self.fl:setWarmth(self.fl_warmth)
self:stateChanged()
end
end
function KoboPowerD:getWarmth()
if self.fl == nil then return end
if self.device:hasNaturalLight() then
return self.fl_warmth
self.fl:setWarmth(warmth)
end
end

@ -4,7 +4,6 @@ local inkview = ffi.load("inkview")
local PocketBookPowerD = BasePowerD:new{
is_charging = nil,
fl_warmth = nil,
fl_min = 0,
fl_max = 100,
@ -12,19 +11,11 @@ local PocketBookPowerD = BasePowerD:new{
fl_warmth_max = 100,
}
function PocketBookPowerD:init()
-- needed for SetFrontlightState / GetFrontlightState
if self.device:hasNaturalLight() then
local color = inkview.GetFrontlightColor()
self.fl_warmth = color >= 0 and color or 0
end
end
function PocketBookPowerD:frontlightIntensityHW()
-- Always update fl_intensity (and perhaps fl_warmth) from the OS value whenever queried (its fast).
-- This way koreader setting can stay in sync even if the value is changed behind its back.
-- Always update fl_intensity (and perhaps fl_warmth) from the OS value whenever queried (it's fast).
-- This way koreader settings can stay in sync even if the value is changed behind its back.
self.fl_intensity = math.max(0, inkview.GetFrontlightState())
if self.fl_warmth then
if self.device:hasNaturalLight() then
self.fl_warmth = math.max(0, inkview.GetFrontlightColor())
end
return self.fl_intensity
@ -33,6 +24,8 @@ end
function PocketBookPowerD:frontlightIntensity()
if not self.device:hasFrontlight() then return 0 end
if self:isFrontlightOff() then return 0 end
--- @note: We actually have a working frontlightIntensityHW implementation,
--- use it instead of returning a cached self.fl_intensity like BasePowerD.
return self:frontlightIntensityHW()
end
@ -60,18 +53,12 @@ function PocketBookPowerD:isFrontlightOn()
return enabled
end
function PocketBookPowerD:setWarmth(level)
if self.fl_warmth then
self.fl_warmth = level or self.fl_warmth
inkview.SetFrontlightColor(self.fl_warmth)
self:stateChanged()
end
function PocketBookPowerD:setWarmthHW(level)
return inkview.SetFrontlightColor(level)
end
function PocketBookPowerD:getWarmth()
if self.fl_warmth then
return self.fl_warmth
end
function PocketBookPowerD:frontlightWarmthHW()
return inkview.GetFrontlightColor()
end
function PocketBookPowerD:getCapacityHW()

@ -20,15 +20,7 @@ function SDLPowerD:setIntensityHW(intensity)
self.hw_intensity = intensity or self.hw_intensity
end
function SDLPowerD:setWarmth(level)
require("logger").info("set warmth to", level)
self.fl_warmth = level or self.fl_warmth
self:stateChanged()
end
function SDLPowerD:getWarmth()
if self.hw_intensity == 0 then return end
function SDLPowerD:frontlightWarmthHW()
return self.fl_warmth
end

@ -53,6 +53,7 @@ dbg:guard(SysfsLight, 'setWarmth',
"Wrong warmth value given!")
end)
--- @note: warmth is already in the *native* scale!
function SysfsLight:setNaturalBrightness(brightness, warmth)
local set_brightness = true
local set_warmth = true
@ -67,8 +68,6 @@ function SysfsLight:setNaturalBrightness(brightness, warmth)
-- Newer devices use a mixer instead of writting values per color.
if self.frontlight_mixer then
-- Honor the device's scale, which may not be [0...100] (e.g., it's [0...10] on the Forma) ;).
warmth = math.floor(warmth / self.nl_max)
if set_brightness then
-- Prefer the ioctl, as it's much lower latency.
if self.frontlight_ioctl then
@ -77,7 +76,7 @@ function SysfsLight:setNaturalBrightness(brightness, warmth)
self:_write_value(self.frontlight_white, brightness)
end
end
-- And it may be inverted... (cold is nl_max, warm is nl_min)
-- The mixer might be using inverted values... (cold is nl_max, warm is nl_min)
if set_warmth then
if self.nl_inverted then
self:_write_value(self.frontlight_mixer, self.nl_max - warmth)

@ -655,7 +655,7 @@ end
function BookStatusWidget:closeInputDialog()
UIManager:close(self.note_dialog)
self.input_note:onUnfocus();
self.input_note:onUnfocus()
end
return BookStatusWidget

@ -96,7 +96,7 @@ function Button:init()
local new_size = self.label_widget.face.orig_size - 1
if new_size < font_size_2_lines then
-- Switch to a 2-lines TextBoxWidget
self.label_widget:free()
self.label_widget:free(true)
self.label_widget = TextBoxWidget:new{
text = self.text,
line_height = 0,
@ -118,7 +118,7 @@ function Button:init()
if new_size < 8 then -- don't go too small
break
end
self.label_widget:free()
self.label_widget:free(true)
self.label_widget = TextWidget:new{
text = self.text,
max_width = max_width,

@ -254,7 +254,7 @@ function ButtonProgressWidget:update()
end
self:refocusWidget()
UIManager:setDirty(self.show_parrent, function()
UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen
end)
end

@ -637,7 +637,7 @@ function ConfigOption:init()
self.options[c].labels or self.options[c].args, arg)
end
end,
show_parrent = self.config,
show_parent = self.config,
enabled = enabled,
fine_tune = self.options[c].fine_tune,
fine_tune_param = self.options[c].fine_tune_param,

@ -118,11 +118,12 @@ function WidgetContainer:handleEvent(event)
end
end
function WidgetContainer:free()
-- Honor full for TextBoxWidget's benefit...
function WidgetContainer:free(full)
for _, widget in ipairs(self) do
if widget.free then
--print("WidgetContainer: Calling free for widget", debug.getinfo(widget.free, "S").short_src, widget, "from", debug.getinfo(self.free, "S").short_src, self)
widget:free()
widget:free(full)
end
end
end

@ -559,7 +559,7 @@ function DictQuickLookup:init()
for_measurement_only = true, -- flag it as a dummy, so it won't trigger any bogus repaint/refresh...
}
self.definition_line_height = test_widget:getLineHeight()
test_widget:free()
test_widget:free(true)
end
if is_large_window then

@ -1,5 +1,6 @@
local Blitbuffer = require("ffi/blitbuffer")
local Button = require("ui/widget/button")
local ButtonProgressWidget = require("ui/widget/buttonprogresswidget")
local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
@ -13,7 +14,7 @@ local Math = require("optmath")
local NaturalLight = require("ui/widget/naturallightwidget")
local ProgressWidget = require("ui/widget/progresswidget")
local Size = require("ui/size")
local TextBoxWidget = require("ui/widget/textboxwidget")
local TextWidget = require("ui/widget/textwidget")
local TimeVal = require("ui/timeval")
local TitleBar = require("ui/widget/titlebar")
local UIManager = require("ui/uimanager")
@ -33,50 +34,49 @@ local FrontLightWidget = FocusManager:new{
}
function FrontLightWidget:init()
-- Layout constants
self.medium_font_face = Font:getFace("ffont")
self.screen_width = Screen:getWidth()
self.screen_height = Screen:getHeight()
self.span = math.ceil(self.screen_height * 0.01)
self.span = Math.round(self.screen_height * 0.01)
self.width = math.floor(self.screen_width * 0.95)
-- State constants
self.powerd = Device:getPowerDevice()
self.fl_min = self.powerd.fl_min
self.fl_max = self.powerd.fl_max
self.fl_cur = self.powerd:frontlightIntensity()
local steps_fl = self.fl_max - self.fl_min + 1
self.one_step = math.ceil(steps_fl / 25)
self.steps = math.ceil(steps_fl / self.one_step)
if (self.steps - 1) * self.one_step < self.fl_max - self.fl_min then
self.steps = self.steps + 1
-- Frontlight
self.fl = {}
self.fl.min = self.powerd.fl_min
self.fl.max = self.powerd.fl_max
self.fl.cur = self.powerd:frontlightIntensity()
local fl_steps = self.fl.max - self.fl.min + 1
self.fl.stride = math.ceil(fl_steps / 25)
self.fl.steps = math.ceil(fl_steps / self.fl.stride)
if (self.fl.steps - 1) * self.fl.stride < self.fl.max - self.fl.min then
self.fl.steps = self.fl.steps + 1
end
self.steps = math.min(self.steps, steps_fl)
self.natural_light = Device:hasNaturalLight()
self.fl.steps = math.min(self.fl.steps, fl_steps)
-- Warmth
self.has_nl = Device:hasNaturalLight()
self.has_nl_mixer = Device:hasNaturalLightMixer()
self.has_nl_api = Device:hasNaturalLightApi()
-- Handle Warmth separately, because it may use a different scale
if self.natural_light then
self.nl_min = self.powerd.fl_warmth_min
self.nl_max = self.powerd.fl_warmth_max
-- NOTE: fl_warmth is always [0...100] even when internal scale is [0...10]
self.nl_scale = (100 / self.nl_max)
if self.has_nl then
self.nl = {}
self.nl.min = self.powerd.fl_warmth_min
self.nl.max = self.powerd.fl_warmth_max
self.nl.cur = self.powerd:toNativeWarmth(self.powerd:frontlightWarmth())
local nl_steps = self.nl.max - self.nl.min + 1
self.nl.stride = math.ceil(nl_steps / 25)
self.nl.steps = math.ceil(nl_steps / self.nl.stride)
if (self.nl.steps - 1) * self.nl.stride < self.nl.max - self.nl.min then
self.nl.steps = self.nl.steps + 1
end
self.nl.steps = math.min(self.nl.steps, nl_steps)
end
-- button width to fit screen size
local button_margin = Size.margin.tiny
local button_padding = Size.padding.button
local button_bordersize = Size.border.button
self.button_width = math.floor(self.screen_width * 0.9 / self.steps) -
2 * (button_margin + button_padding + button_bordersize)
self.fl_prog_button = Button:new{
text = "",
radius = 0,
margin = button_margin,
padding = button_padding,
bordersize = button_bordersize,
enabled = true,
width = self.button_width,
show_parent = self,
}
-- Input
if Device:hasKeys() then
self.key_events.Close = { {Device.input.group.Back}, doc = "close frontlight" }
end
@ -104,102 +104,104 @@ function FrontLightWidget:init()
},
}
end
self:update()
end
function FrontLightWidget:generateProgressGroup(width, height, fl_level, step)
self.fl_container = CenterContainer:new{
dimen = Geom:new{ w = width, h = height },
}
self:setProgress(fl_level, step)
return self.fl_container
-- Widget layout
self:layout()
end
function FrontLightWidget:setProgress(num, step, num_warmth)
self.fl_container:clear()
local padding_span = VerticalSpan:new{ width = self.span }
local button_group_down = HorizontalGroup:new{ align = "center" }
local button_group_up = HorizontalGroup:new{ align = "center" }
local vertical_group = VerticalGroup:new{ align = "center" }
local enable_button_plus = true
local enable_button_minus = true
if self.natural_light then
num_warmth = num_warmth or self.powerd.fl_warmth
end
if num then
--- @note Don't set the same value twice, to play nice with the update() sent by the swipe handler on the FL bar
-- Except for fl_min, as that's how setFrontLightIntensity detects a toggle...
if num == self.fl_min or num ~= self.fl_cur then
self:setFrontLightIntensity(num)
end
function FrontLightWidget:layout()
self.layout = {}
if self.fl_cur == self.fl_max then enable_button_plus = false end
if self.fl_cur == self.fl_min then enable_button_minus = false end
end
local main_container = CenterContainer:new{
dimen = Geom:new{
w = self.width,
h = math.floor(self.screen_height * 0.2),
},
}
-- Frontlight
-- Bigger spans, as ProgressWidget appears to be ever so slightly smaller than ButtonProgressWidget ;).
local fl_padding_span = VerticalSpan:new{ width = Math.round(self.span * 1.5) }
local fl_group_above = HorizontalGroup:new{ align = "center" }
local fl_group_below = HorizontalGroup:new{ align = "center" }
local main_group = VerticalGroup:new{ align = "center" }
local ticks = {}
for i = 1, self.steps-2 do
table.insert(ticks, i*self.one_step)
for i = 1, self.fl.steps - 2 do
table.insert(ticks, i * self.fl.stride)
end
self.fl_group = ProgressWidget:new{
self.fl_progress = ProgressWidget:new{
width = math.floor(self.screen_width * 0.9),
height = Size.item.height_big,
percentage = self.fl_cur / self.fl_max,
percentage = self.fl.cur / self.fl.max,
ticks = ticks,
tick_width = Screen:scaleBySize(0.5),
last = self.fl_max,
last = self.fl.max,
}
local text_br = TextBoxWidget:new{
local fl_header = TextWidget:new{
text = _("Brightness"),
face = self.medium_font_face,
bold = true,
alignment = "center",
width = math.floor(self.screen_width * 0.95),
max_width = math.floor(self.screen_width * 0.95),
}
local button_minus = Button:new{
text = "-1",
self.fl_minus = Button:new{
text = "",
margin = Size.margin.small,
radius = 0,
enabled = enable_button_minus,
enabled = self.fl.cur ~= self.fl.min,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function() self:setProgress(self.fl_cur - 1, step) end,
callback = function()
self:setBrightness(self.fl.cur - 1)
end,
}
local button_plus = Button:new{
text = "+1",
self.fl_plus = Button:new{
text = "",
margin = Size.margin.small,
radius = 0,
enabled = enable_button_plus,
enabled = self.fl.cur ~= self.fl.max,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function() self:setProgress(self.fl_cur + 1, step) end,
callback = function()
self:setBrightness(self.fl.cur + 1)
end,
}
local item_level = TextBoxWidget:new{
text = self.fl_cur,
self.fl_level = TextWidget:new{
text = tostring(self.fl.cur),
face = self.medium_font_face,
alignment = "center",
width = math.floor(self.screen_width * 0.95 - 1.275 * button_minus.width - 1.275 * button_plus.width),
max_width = math.floor(self.screen_width * 0.95 - 1.275 * self.fl_minus.width - 1.275 * self.fl_plus.width),
}
local button_min = Button:new{
local fl_level_container = CenterContainer:new{
dimen = Geom:new{
w = self.fl_level.max_width,
h = self.fl_level:getSize().h
},
self.fl_level,
}
local fl_min = Button:new{
text = _("Min"),
margin = Size.margin.small,
radius = 0,
enabled = true,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function() self:setProgress(self.fl_min+1, step) end, -- min is 1 (use toggle for 0)
callback = function()
self:setBrightness(self.fl.min + 1)
end, -- min is 1 (We use 0 to mean "toggle")
}
local button_max = Button:new{
local fl_max = Button:new{
text = _("Max"),
margin = Size.margin.small,
radius = 0,
enabled = true,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function() self:setProgress(self.fl_max, step) end,
callback = function()
self:setBrightness(self.fl.max)
end,
}
local button_toggle = Button:new{
local fl_toggle = Button:new{
text = _("Toggle"),
margin = Size.margin.small,
radius = 0,
@ -207,48 +209,161 @@ function FrontLightWidget:setProgress(num, step, num_warmth)
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function()
self:setProgress(self.fl_min, step)
self:setBrightness(self.fl.min)
end,
}
local empty_space = HorizontalSpan:new{
width = math.floor((self.screen_width * 0.95 - 1.2 * button_minus.width - 1.2 * button_plus.width - 1.2 * button_toggle.width) / 2),
local fl_spacer = HorizontalSpan:new{
width = math.floor((self.screen_width * 0.95 - 1.2 * self.fl_minus.width - 1.2 * self.fl_plus.width - 1.2 * fl_toggle.width) / 2),
}
local button_table_up = HorizontalGroup:new{
local fl_buttons_above = HorizontalGroup:new{
align = "center",
button_minus,
item_level,
button_plus,
self.fl_minus,
fl_level_container,
self.fl_plus,
}
self.layout[1] = {button_minus, button_plus}
local button_table_down = HorizontalGroup:new{
self.layout[1] = {self.fl_minus, self.fl_plus}
local fl_buttons_below = HorizontalGroup:new{
align = "center",
button_min,
empty_space,
button_toggle,
empty_space,
button_max,
fl_min,
fl_spacer,
fl_toggle,
fl_spacer,
fl_max,
}
self.layout[2] = {button_min, button_toggle, button_max}
if self.natural_light then
-- Only insert 'brightness' caption if we also add 'warmth'
-- widgets below.
table.insert(vertical_group, text_br)
self.layout[2] = {fl_min, fl_toggle, fl_max}
if self.has_nl then
-- Only insert a "Brightness" caption if we also add the full set of warmth widgets below,
-- otherwise, it's implied by the title bar ;).
table.insert(main_group, fl_header)
end
table.insert(button_group_up, button_table_up)
table.insert(button_group_down, button_table_down)
table.insert(vertical_group, padding_span)
table.insert(vertical_group, button_group_up)
table.insert(vertical_group, padding_span)
table.insert(vertical_group, self.fl_group)
table.insert(vertical_group, padding_span)
table.insert(vertical_group, button_group_down)
table.insert(vertical_group, padding_span)
if self.natural_light then
-- If the device supports natural light, add the widgets for 'warmth',
-- as well as a 'Configure' button for devices *without* a mixer
self:addWarmthWidgets(num_warmth, step, vertical_group)
table.insert(fl_group_above, fl_buttons_above)
table.insert(fl_group_below, fl_buttons_below)
table.insert(main_group, fl_padding_span)
table.insert(main_group, fl_group_above)
table.insert(main_group, fl_padding_span)
table.insert(main_group, self.fl_progress)
table.insert(main_group, fl_padding_span)
table.insert(main_group, fl_group_below)
table.insert(main_group, fl_padding_span)
-- Warmth
if self.has_nl then
-- Smaller spans, as ButtonProgressWidget appears to be ever so slightly taller than ProgressWidget ;).
local nl_padding_span = VerticalSpan:new{ width = self.span }
local nl_group_above = HorizontalGroup:new{ align = "center" }
local nl_group_below = HorizontalGroup:new{ align = "center" }
self.nl_progress = ButtonProgressWidget:new{
width = math.floor(self.screen_width * 0.9),
font_size = 20, -- match Button's default
padding = 0,
thin_grey_style = false,
num_buttons = self.nl.steps - 1, -- no button for step 0
position = math.floor(self.nl.cur / self.nl.stride),
default_position = math.floor(self.nl.cur / self.nl.stride),
callback = function(i)
self:setWarmth(i, false)
end,
show_parent = self,
enabled = true,
}
-- We want a wider gap between the two sets of widgets
local nl_span = VerticalSpan:new{ width = Size.span.vertical_large * 4 }
local nl_header = TextWidget:new{
text = _("Warmth"),
face = self.medium_font_face,
bold = true,
max_width = math.floor(self.screen_width * 0.95),
}
self.nl_minus = Button:new{
text = "",
margin = Size.margin.small,
radius = 0,
enabled = self.nl.cur ~= self.nl.min,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function()
self:setWarmth(self.nl.cur - 1, true) end,
}
self.nl_plus = Button:new{
text = "",
margin = Size.margin.small,
radius = 0,
enabled = self.nl.cur ~= self.nl.max,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function()
self:setWarmth(self.nl.cur + 1, true) end,
}
self.nl_level = TextWidget:new{
text = tostring(self.nl.cur),
face = self.medium_font_face,
max_width = math.floor(self.screen_width * 0.95 - 1.275 * self.nl_minus.width - 1.275 * self.nl_plus.width),
}
local nl_level_container = CenterContainer:new{
dimen = Geom:new{
w = self.nl_level.max_width,
h = self.nl_level:getSize().h
},
self.nl_level,
}
local nl_min = Button:new{
text = _("Min"),
margin = Size.margin.small,
radius = 0,
enabled = true,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function()
self:setWarmth(self.nl.min, true)
end,
}
local nl_max = Button:new{
text = _("Max"),
margin = Size.margin.small,
radius = 0,
enabled = true,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function()
self:setWarmth(self.nl.max, true)
end,
}
local nl_spacer = HorizontalSpan:new{
width = math.floor((self.screen_width * 0.95 - 1.2 * self.nl_minus.width - 1.2 * self.nl_plus.width) / 2),
}
local nl_buttons_above = HorizontalGroup:new{
align = "center",
self.nl_minus,
nl_level_container,
self.nl_plus,
}
self.layout[3] = {self.nl_minus, self.nl_plus}
local nl_buttons_below = HorizontalGroup:new{
align = "center",
nl_min,
nl_spacer,
nl_max,
}
self.layout[4] = {nl_min, nl_max}
table.insert(main_group, nl_span)
table.insert(main_group, nl_header)
table.insert(nl_group_above, nl_buttons_above)
table.insert(nl_group_below, nl_buttons_below)
table.insert(main_group, nl_padding_span)
table.insert(main_group, nl_group_above)
table.insert(main_group, nl_padding_span)
table.insert(main_group, self.nl_progress)
table.insert(main_group, nl_padding_span)
table.insert(main_group, nl_group_below)
table.insert(main_group, nl_padding_span)
-- Aura One R/G/B widget
if not self.has_nl_mixer and not self.has_nl_api then
self.configure_button = Button:new{
local nl_setup = Button:new{
text = _("Configure"),
margin = Size.margin.small,
radius = 0,
@ -258,177 +373,41 @@ function FrontLightWidget:setProgress(num, step, num_warmth)
UIManager:show(NaturalLight:new{fl_widget = self})
end,
}
table.insert(vertical_group, self.configure_button)
self.layout[5] = {self.configure_button}
end
end
table.insert(self.fl_container, vertical_group)
-- Reset container height to what it actually contains
self.fl_container.dimen.h = vertical_group:getSize().h
self:refocusWidget()
UIManager:setDirty(self, function()
return "ui", self.light_frame.dimen
end)
return true
end
-- Currently, we are assuming the 'warmth' has the same min/max limits as 'brightness'.
function FrontLightWidget:addWarmthWidgets(num_warmth, step, vertical_group)
local button_group_down = HorizontalGroup:new{ align = "center" }
local button_group_up = HorizontalGroup:new{ align = "center" }
local warmth_group = HorizontalGroup:new{ align = "center" }
local padding_span = VerticalSpan:new{ width = self.span }
local enable_button_plus = true
local enable_button_minus = true
if self[1] then
--- @note Don't set the same value twice, to play nice with the update() sent by the swipe handler on the FL bar
if num_warmth ~= self.powerd.fl_warmth then
self.powerd:setWarmth(num_warmth)
table.insert(main_group, nl_setup)
self.layout[5] = {nl_setup}
end
end
if self.natural_light and num_warmth then
local curr_warmth_step = math.floor(num_warmth / step)
for i = 0, curr_warmth_step do
table.insert(warmth_group, self.fl_prog_button:new{
text = "",
preselect = curr_warmth_step > 0 and true or false,
callback = function()
self:setProgress(self.fl_cur, step, i * step)
end
})
end
for i = curr_warmth_step + 1, self.steps - 1 do
table.insert(warmth_group, self.fl_prog_button:new{
text = "",
callback = function()
self:setProgress(self.fl_cur, step, i * step)
end
})
end
end
if math.floor(num_warmth / self.nl_scale) <= self.nl_min then enable_button_minus = false end
if math.ceil(num_warmth / self.nl_scale) >= self.nl_max then enable_button_plus = false end
local text_warmth = TextBoxWidget:new{
text = "\n" .. _("Warmth"),
face = self.medium_font_face,
bold = true,
alignment = "center",
width = math.floor(self.screen_width * 0.95),
}
local button_minus = Button:new{
text = "-" .. (1 * self.nl_scale),
margin = Size.margin.small,
radius = 0,
enabled = enable_button_minus,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function() self:setProgress(self.fl_cur, step, (num_warmth - (1 * self.nl_scale))) end,
}
local button_plus = Button:new{
text = "+" .. (1 * self.nl_scale),
margin = Size.margin.small,
radius = 0,
enabled = enable_button_plus,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function() self:setProgress(self.fl_cur, step, (num_warmth + (1 * self.nl_scale))) end,
}
local item_level = TextBoxWidget:new{
text = num_warmth,
face = self.medium_font_face,
alignment = "center",
width = math.floor(self.screen_width * 0.95 - 1.275 * button_minus.width - 1.275 * button_plus.width),
}
local button_min = Button:new{
text = _("Min"),
margin = Size.margin.small,
radius = 0,
enabled = true,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function() self:setProgress(self.fl_cur, step, self.nl_min) end,
}
local button_max = Button:new{
text = _("Max"),
margin = Size.margin.small,
radius = 0,
enabled = true,
width = math.floor(self.screen_width * 0.2),
show_parent = self,
callback = function() self:setProgress(self.fl_cur, step, (self.nl_max * self.nl_scale)) end,
}
local empty_space = HorizontalSpan:new{
width = math.floor((self.screen_width * 0.95 - 1.2 * button_minus.width - 1.2 * button_plus.width) / 2),
}
local button_table_up = HorizontalGroup:new{
align = "center",
button_minus,
item_level,
button_plus,
}
self.layout[3] = {button_minus, button_plus}
local button_table_down = HorizontalGroup:new{
align = "center",
button_min,
empty_space,
button_max,
}
self.layout[4] = {button_min, button_max}
table.insert(vertical_group, text_warmth)
table.insert(button_group_up, button_table_up)
table.insert(button_group_down, button_table_down)
table.insert(vertical_group, padding_span)
table.insert(vertical_group, button_group_up)
table.insert(vertical_group, padding_span)
table.insert(vertical_group, warmth_group)
table.insert(vertical_group, padding_span)
table.insert(vertical_group, button_group_down)
table.insert(vertical_group, padding_span)
end
function FrontLightWidget:setFrontLightIntensity(num)
self.fl_cur = num
local set_fl = math.min(self.fl_cur, self.fl_max)
-- Don't touch frontlight on first call (no self[1] means not yet out of update()),
-- so that we don't untoggle light.
if self[1] then
if set_fl == self.fl_min then -- fl_min (which is always 0) means toggle
self.powerd:toggleFrontlight()
else
self.powerd:setIntensity(set_fl)
end
-- get back the real level (different from set_fl if untoggle)
self.fl_cur = self.powerd:frontlightIntensity()
end
end
table.insert(main_container, main_group)
-- Reset container height to what it actually contains
main_container.dimen.h = main_group:getSize().h
function FrontLightWidget:update()
self.layout = {}
-- Common
local title_bar = TitleBar:new{
title = _("Frontlight"),
width = self.width,
align = "left",
with_bottom_line = true,
bottom_v_padding = 0,
close_callback = function() self:onClose() end,
close_callback = function()
self:onClose()
end,
show_parent = self,
}
local light_level = FrameContainer:new{
local inner_frame = FrameContainer:new{
padding = Size.padding.button,
margin = Size.margin.small,
bordersize = 0,
self:generateProgressGroup(self.width, math.floor(self.screen_height * 0.2),
self.fl_cur, self.one_step)
main_container,
}
self.light_frame = FrameContainer:new{
local center_container = CenterContainer:new{
dimen = Geom:new{
w = self.width,
h = inner_frame:getSize().h,
},
inner_frame,
}
self.frame = FrameContainer:new{
radius = Size.radius.window,
bordersize = Size.border.window,
padding = 0,
@ -437,39 +416,123 @@ function FrontLightWidget:update()
VerticalGroup:new{
align = "left",
title_bar,
CenterContainer:new{
dimen = Geom:new{
w = self.width,
h = light_level:getSize().h,
},
light_level,
},
center_container,
}
}
self[1] = WidgetContainer:new{
align = "center",
dimen =Geom:new{
dimen = Geom:new{
x = 0, y = 0,
w = self.screen_width,
h = self.screen_height,
},
FrameContainer:new{
bordersize = 0,
self.light_frame,
self.frame,
},
}
end
function FrontLightWidget:update()
self:refocusWidget()
UIManager:setDirty(self, function()
return "ui", self.frame.dimen
end)
return true
end
function FrontLightWidget:updateBrightnessWidgets()
self.fl_progress:setPercentage(self.fl.cur / self.fl.max)
self.fl_level:setText(tostring(self.fl.cur))
if self.fl.cur == self.fl.min then
self.fl_minus:disable()
else
self.fl_minus:enable()
end
if self.fl.cur == self.fl.max then
self.fl_plus:disable()
else
self.fl_plus:enable()
end
end
function FrontLightWidget:refreshBrightnessWidgets()
self:updateBrightnessWidgets()
self:update()
end
function FrontLightWidget:setBrightness(intensity)
-- Let fl.min through, as that's what we use for the Toggle button ;).
if intensity ~= self.fl.min and intensity == self.fl.cur then
return
end
-- Set brightness
self:setFrontLightIntensity(intensity)
-- Update the progress bar
self:updateBrightnessWidgets()
-- Refresh widget
self:update()
end
function FrontLightWidget:setWarmth(warmth, update_position)
if warmth == self.nl.cur then
return
end
-- Set warmth
self.nl.cur = warmth
self.powerd:setWarmth(self.powerd:fromNativeWarmth(self.nl.cur))
-- Update the progress bar, if we were called from outside ButtonProgressWidget
-- (as it already handles that internally ;)).
if update_position then
self.nl_progress:setPosition(warmth, self.nl_progress.default_position)
end
self.nl_level:setText(tostring(self.nl.cur))
if self.nl.cur == self.nl.min then
self.nl_minus:disable()
else
self.nl_minus:enable()
end
if self.nl.cur == self.nl.max then
self.nl_plus:disable()
else
self.nl_plus:enable()
end
-- Refresh widget
self:update()
end
function FrontLightWidget:setFrontLightIntensity(intensity)
self.fl.cur = intensity
-- min (which is always 0) means toggle
if self.fl.cur == self.fl.min then
self.powerd:toggleFrontlight()
else
self.powerd:setIntensity(self.fl.cur)
end
-- Retrieve the real level (different from intensity on toggle)
self.fl.cur = self.powerd:frontlightIntensity()
end
function FrontLightWidget:onCloseWidget()
UIManager:setDirty(nil, function()
return "flashui", self.light_frame.dimen
return "flashui", self.frame.dimen
end)
end
function FrontLightWidget:onShow()
-- NOTE: Keep this one as UI, it'll get coalesced...
UIManager:setDirty(self, function()
return "ui", self.light_frame.dimen
return "ui", self.frame.dimen
end)
return true
end
@ -487,19 +550,19 @@ end
function FrontLightWidget:onTapProgress(arg, ges_ev)
-- The throttling has a tendency to wreak a bit of a havoc,
-- so, if the widget hasn't been repainted yet, go away.
if not self.fl_group.dimen or not self.light_frame.dimen then
if not self.fl_progress.dimen or not self.frame.dimen then
return true
end
if ges_ev.pos:intersectWith(self.fl_group.dimen) then
if ges_ev.pos:intersectWith(self.fl_progress.dimen) then
-- Unschedule any pending updates.
UIManager:unschedule(self.update)
UIManager:unschedule(self.refreshBrightnessWidgets)
local perc = self.fl_group:getPercentageFromPosition(ges_ev.pos)
local perc = self.fl_progress:getPercentageFromPosition(ges_ev.pos)
if not perc then
return true
end
local num = Math.round(perc * self.fl_max)
local num = Math.round(perc * self.fl.max)
-- Always set the frontlight intensity.
self:setFrontLightIntensity(num)
@ -512,17 +575,17 @@ function FrontLightWidget:onTapProgress(arg, ges_ev)
self.last_time = current_time
else
-- Schedule a final update after we stop panning.
UIManager:scheduleIn(0.075, self.update, self)
UIManager:scheduleIn(0.075, self.refreshBrightnessWidgets, self)
return true
end
end
self:update()
elseif not ges_ev.pos:intersectWith(self.light_frame.dimen) and ges_ev.ges == "tap" then
-- close if tap outside
self:refreshBrightnessWidgets()
elseif not ges_ev.pos:intersectWith(self.frame.dimen) and ges_ev.ges == "tap" then
-- Close when tapping outside.
self:onClose()
end
-- otherwise, do nothing (it's easy missing taping a button)
-- Otherwise, do nothing (it's easy to miss a button).
return true
end

@ -360,7 +360,7 @@ end
-- lines than before
function InputText:initTextBox(text, char_added)
if self.text_widget then
self.text_widget:free()
self.text_widget:free(true)
end
self.text = text
local fgcolor
@ -438,7 +438,7 @@ function InputText:initTextBox(text, char_added)
}
self.height = text_widget:getTextHeight()
self.scroll = true
text_widget:free()
text_widget:free(true)
end
if self.scroll then
self.text_widget = ScrollTextWidget:new{
@ -517,6 +517,11 @@ function InputText:initTextBox(text, char_added)
self.edit_callback(self.is_text_edited)
end
end
dbg:guard(InputText, "initTextBox",
function(self, text, char_added)
assert(type(text) == "string",
"Wrong text type (expected string)")
end)
function InputText:initKeyboard()
local keyboard_layer = 2
@ -634,6 +639,11 @@ function InputText:onTextInput(text)
end
return false
end
dbg:guard(InputText, "onTextInput",
function(self, text)
assert(type(text) == "string",
"Wrong text type (expected string)")
end)
function InputText:onShowKeyboard(ignore_first_hold_release)
Device:startTextInput()
@ -932,5 +942,10 @@ function InputText:setText(text, keep_edited_state)
self:checkTextEditability()
end
end
dbg:guard(InputText, "setText",
function(self, text, keep_edited_state)
assert(type(text) == "string",
"Wrong text type (expected string)")
end)
return InputText

@ -347,7 +347,7 @@ end
function NaturalLightWidget:setValueTextBox(widget, val)
widget:focus()
widget:setText(val)
widget:setText(tostring(val))
widget:unfocus()
end
@ -360,7 +360,7 @@ end
function NaturalLightWidget:onShow()
UIManager:setDirty(self, function()
return "ui", self.nl_frame.dimen
return "ui", self.nl_frame.dimen
end)
-- Store values in case user cancels
self.old_values = self:getCurrentValues()

@ -28,6 +28,7 @@ local TimeVal = require("ui/timeval")
local UIManager = require("ui/uimanager")
local Math = require("optmath")
local logger = require("logger")
local dbg = require("dbg")
local util = require("util")
local Screen = require("device").screen
@ -151,6 +152,26 @@ function TextBoxWidget:init()
self.text_height = self.lines_per_page * self.line_height_px
end
self:_computeTextDimensions()
self:_updateLayout()
if self.editable then
self:moveCursorToCharPos(self.charpos or 1)
end
self.dimen = Geom:new(self:getSize())
if Device:isTouchDevice() then
self.ges_events = {
TapImage = {
GestureRange:new{
ges = "tap",
range = function() return self.dimen end,
},
},
}
end
end
function TextBoxWidget:_computeTextDimensions()
if self.use_xtext then
self:_measureWithXText()
else
@ -185,21 +206,6 @@ function TextBoxWidget:init()
self:scrollViewToCharPos()
end
end
self:_updateLayout()
if self.editable then
self:moveCursorToCharPos(self.charpos or 1)
end
self.dimen = Geom:new(self:getSize())
if Device:isTouchDevice() then
self.ges_events = {
TapImage = {
GestureRange:new{
ges = "tap",
range = function() return self.dimen end,
},
},
}
end
end
function TextBoxWidget:unfocus()
@ -1168,6 +1174,26 @@ function TextBoxWidget:update(scheduled_update)
self.scheduled_update = nil
end
function TextBoxWidget:setText(text)
if text == self.text then
return
end
self.text = text
self:_computeTextDimensions()
self:update()
-- Don't break the reference
local new_size = self:getSize()
self.dimen.w = new_size.w
self.dimen.h = new_size.h
end
dbg:guard(TextBoxWidget, "setText",
function(self, text)
assert(type(text) == "string",
"Wrong text type (expected string)")
end)
function TextBoxWidget:onTapImage(arg, ges)
if self.line_num_to_image and self.line_num_to_image[self.virtual_line_num] then
local image = self.line_num_to_image[self.virtual_line_num]

@ -312,10 +312,12 @@ function TextWidget:getBaseline()
end
function TextWidget:setText(text)
if text ~= self.text then
self.text = text
self:free()
if text == self.text then
return
end
self.text = text
self:free()
end
dbg:guard(TextWidget, "setText",
function(self, text)

@ -162,7 +162,7 @@ function TitleBar:init()
-- gathered, we'll re :init() ourselves with the original title,
-- using the metrics we're computing now (self._initial*).
self._initial_re_init_needed = true
self.title_widget:free()
self.title_widget:free(true)
self.title_widget = TextWidget:new{
text = "",
face = title_face,
@ -171,7 +171,7 @@ function TitleBar:init()
break
end
-- otherwise, loop and do the same with a smaller font size
self.title_widget:free()
self.title_widget:free(true)
title_face = Font:getFace(title_face.orig_font, title_face.orig_size - 1)
end
end

@ -581,10 +581,10 @@ function ListMenuItem:update()
while true do
-- Free previously made widgets to avoid memory leaks
if wtitle then
wtitle:free()
wtitle:free(true)
end
if wauthors then
wauthors:free()
wauthors:free(true)
wauthors = nil
end
-- BookInfoManager:extractBookInfo() made sure
@ -730,7 +730,7 @@ function ListMenuItem:update()
local fontsize_no_bookinfo = _fontSize(18, 22)
repeat
if text_widget then
text_widget:free()
text_widget:free(true)
end
text_widget = TextBoxWidget:new{
text = text .. hint,

@ -211,15 +211,15 @@ function FakeCover:init()
while true do
-- Free previously made widgets to avoid memory leaks
if authors_wg then
authors_wg:free()
authors_wg:free(true)
authors_wg = nil
end
if title_wg then
title_wg:free()
title_wg:free(true)
title_wg = nil
end
if filename_wg then
filename_wg:free()
filename_wg:free(true)
filename_wg = nil
end
-- Build new widgets
@ -485,7 +485,7 @@ function MosaicMenuItem:update()
local directory
while true do
if directory then
directory:free()
directory:free(true)
end
directory = TextBoxWidget:new{
text = text,

@ -146,6 +146,7 @@ if dpi_override ~= nil then
Device:setScreenDPI(dpi_override)
end
-- Night mode
local hw_nightmode = Device.screen:getHWNightmode()
if G_reader_settings:isTrue("night_mode") then
Device.screen:toggleNightMode()
end
@ -347,6 +348,9 @@ local function exitReader()
-- Close lipc handles
ReaderActivityIndicator:coda()
-- Restore initial inversion state
Device.screen:setHWNightmode(hw_nightmode)
-- shutdown hardware abstraction
Device:exit()

Loading…
Cancel
Save