diff --git a/base b/base index 5ac0b0491..c4c46e578 160000 --- a/base +++ b/base @@ -1 +1 @@ -Subproject commit 5ac0b049192ae68fc8809de44ed7c2741c64836f +Subproject commit c4c46e57854975eb7593b6f0d204721f4a6732f0 diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index cf987246d..bb26a7e75 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -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 diff --git a/frontend/device/android/device.lua b/frontend/device/android/device.lua index 35e3cf723..3bd181b20 100644 --- a/frontend/device/android/device.lua +++ b/frontend/device/android/device.lua @@ -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") diff --git a/frontend/device/android/powerd.lua b/frontend/device/android/powerd.lua index f81a5b028..25e93cd1a 100644 --- a/frontend/device/android/powerd.lua +++ b/frontend/device/android/powerd.lua @@ -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 diff --git a/frontend/device/cervantes/powerd.lua b/frontend/device/cervantes/powerd.lua index 3201fb81a..c8aad9d76 100644 --- a/frontend/device/cervantes/powerd.lua +++ b/frontend/device/cervantes/powerd.lua @@ -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) diff --git a/frontend/device/generic/powerd.lua b/frontend/device/generic/powerd.lua index 83dab07b2..e81b6548b 100644 --- a/frontend/device/generic/powerd.lua +++ b/frontend/device/generic/powerd.lua @@ -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. diff --git a/frontend/device/kindle/device.lua b/frontend/device/kindle/device.lua index 125526149..7672c100b 100644 --- a/frontend/device/kindle/device.lua +++ b/frontend/device/kindle/device.lua @@ -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 diff --git a/frontend/device/kindle/powerd.lua b/frontend/device/kindle/powerd.lua index 3de9c9c68..37c223465 100644 --- a/frontend/device/kindle/powerd.lua +++ b/frontend/device/kindle/powerd.lua @@ -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 diff --git a/frontend/device/kobo/powerd.lua b/frontend/device/kobo/powerd.lua index 2a63d4c57..118c5e7fc 100644 --- a/frontend/device/kobo/powerd.lua +++ b/frontend/device/kobo/powerd.lua @@ -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 diff --git a/frontend/device/pocketbook/powerd.lua b/frontend/device/pocketbook/powerd.lua index ad054e1ae..ce12e18ca 100644 --- a/frontend/device/pocketbook/powerd.lua +++ b/frontend/device/pocketbook/powerd.lua @@ -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() diff --git a/frontend/device/sdl/powerd.lua b/frontend/device/sdl/powerd.lua index fc56a0430..fcb734f50 100644 --- a/frontend/device/sdl/powerd.lua +++ b/frontend/device/sdl/powerd.lua @@ -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 diff --git a/frontend/device/sysfs_light.lua b/frontend/device/sysfs_light.lua index 00e509b5b..a113f588d 100644 --- a/frontend/device/sysfs_light.lua +++ b/frontend/device/sysfs_light.lua @@ -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) diff --git a/frontend/ui/widget/bookstatuswidget.lua b/frontend/ui/widget/bookstatuswidget.lua index dc4b9911c..6aa7603d2 100644 --- a/frontend/ui/widget/bookstatuswidget.lua +++ b/frontend/ui/widget/bookstatuswidget.lua @@ -655,7 +655,7 @@ end function BookStatusWidget:closeInputDialog() UIManager:close(self.note_dialog) - self.input_note:onUnfocus(); + self.input_note:onUnfocus() end return BookStatusWidget diff --git a/frontend/ui/widget/button.lua b/frontend/ui/widget/button.lua index c49711b44..1d62233e9 100644 --- a/frontend/ui/widget/button.lua +++ b/frontend/ui/widget/button.lua @@ -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, diff --git a/frontend/ui/widget/buttonprogresswidget.lua b/frontend/ui/widget/buttonprogresswidget.lua index fecdb9172..fa9ee86cf 100644 --- a/frontend/ui/widget/buttonprogresswidget.lua +++ b/frontend/ui/widget/buttonprogresswidget.lua @@ -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 diff --git a/frontend/ui/widget/configdialog.lua b/frontend/ui/widget/configdialog.lua index f83401e48..87b0122bb 100644 --- a/frontend/ui/widget/configdialog.lua +++ b/frontend/ui/widget/configdialog.lua @@ -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, diff --git a/frontend/ui/widget/container/widgetcontainer.lua b/frontend/ui/widget/container/widgetcontainer.lua index d8e5d383d..5ab7b06fe 100644 --- a/frontend/ui/widget/container/widgetcontainer.lua +++ b/frontend/ui/widget/container/widgetcontainer.lua @@ -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 diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index bfd633509..9fc83800c 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -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 diff --git a/frontend/ui/widget/frontlightwidget.lua b/frontend/ui/widget/frontlightwidget.lua index 802fc6c5a..56efacc09 100644 --- a/frontend/ui/widget/frontlightwidget.lua +++ b/frontend/ui/widget/frontlightwidget.lua @@ -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 diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index c1269516a..76ad655ca 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -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 diff --git a/frontend/ui/widget/naturallightwidget.lua b/frontend/ui/widget/naturallightwidget.lua index 81db7f844..e6f7d38b8 100644 --- a/frontend/ui/widget/naturallightwidget.lua +++ b/frontend/ui/widget/naturallightwidget.lua @@ -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() diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index c464efaa3..65bbc684f 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -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] diff --git a/frontend/ui/widget/textwidget.lua b/frontend/ui/widget/textwidget.lua index 2837352cc..39c942456 100644 --- a/frontend/ui/widget/textwidget.lua +++ b/frontend/ui/widget/textwidget.lua @@ -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) diff --git a/frontend/ui/widget/titlebar.lua b/frontend/ui/widget/titlebar.lua index fea4c51dc..53f944957 100644 --- a/frontend/ui/widget/titlebar.lua +++ b/frontend/ui/widget/titlebar.lua @@ -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 diff --git a/plugins/coverbrowser.koplugin/listmenu.lua b/plugins/coverbrowser.koplugin/listmenu.lua index b124e01a7..78d45e1de 100644 --- a/plugins/coverbrowser.koplugin/listmenu.lua +++ b/plugins/coverbrowser.koplugin/listmenu.lua @@ -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, diff --git a/plugins/coverbrowser.koplugin/mosaicmenu.lua b/plugins/coverbrowser.koplugin/mosaicmenu.lua index 9a2b3942a..9c8779698 100644 --- a/plugins/coverbrowser.koplugin/mosaicmenu.lua +++ b/plugins/coverbrowser.koplugin/mosaicmenu.lua @@ -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, diff --git a/reader.lua b/reader.lua index 54d1793b0..07120700a 100755 --- a/reader.lua +++ b/reader.lua @@ -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()