PM: Minor refactor to suspend/resume code flow (#10426)

Make sure we only send Suspend/Resume events when we *actually* suspend/resume. This is done via the Device `_beforeSuspend`/`_afterResume` methods, and those were called by the *input handlers*, not the PM logic; which means they would fire, while the PM logic could actually take a smarter decision and *not* do what the event just sent implied ;).

(i.e., sleep with a cover -> suspend + actual suspend, OK; but if you then resume with a button -> input assumes resume, but PM will actually suspend again!).

Existing design issue made more apparent by #9448 ;).

Also fixes/generalizes a few corner-cases related to screen_saver_lock handling (e.g., don't allow USBMS during a lock).

And deal with the fallout of the main change to the Kobo frontlight ramp behavior ;).
reviewable/pr10462/r1
NiLuJe 12 months ago committed by GitHub
parent 7ab832081b
commit 7e98b9de4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -95,7 +95,7 @@ else
cp -f $(KOR_BASE)/ev_replay.py $(INSTALL_DIR)/koreader/ cp -f $(KOR_BASE)/ev_replay.py $(INSTALL_DIR)/koreader/
@echo "[*] create symlink instead of copying files in development mode" @echo "[*] create symlink instead of copying files in development mode"
cd $(INSTALL_DIR)/koreader && \ cd $(INSTALL_DIR)/koreader && \
bash -O extglob -c "ln -sf ../../$(KOR_BASE)/$(OUTPUT_DIR)/!(cache) ." bash -O extglob -c "ln -sf ../../$(KOR_BASE)/$(OUTPUT_DIR)/!(cache|history) ."
@echo "[*] install front spec only for the emulator" @echo "[*] install front spec only for the emulator"
cd $(INSTALL_DIR)/koreader/spec && test -e front || \ cd $(INSTALL_DIR)/koreader/spec && test -e front || \
ln -sf ../../../../spec ./front ln -sf ../../../../spec ./front

@ -1,9 +1,11 @@
local FFIUtil = require("ffi/util")
local Generic = require("device/generic/device")
local A, android = pcall(require, "android") -- luacheck: ignore local A, android = pcall(require, "android") -- luacheck: ignore
local Event = require("ui/event")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local Generic = require("device/generic/device")
local UIManager
local ffi = require("ffi") local ffi = require("ffi")
local C = ffi.C local C = ffi.C
local FFIUtil = require("ffi/util")
local lfs = require("libs/libkoreader-lfs") local lfs = require("libs/libkoreader-lfs")
local logger = require("logger") local logger = require("logger")
local util = require("util") local util = require("util")
@ -129,8 +131,6 @@ function Device:init()
device = self, device = self,
event_map = event_map, event_map = event_map,
handleMiscEv = function(this, ev) handleMiscEv = function(this, ev)
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
logger.dbg("Android application event", ev.code) logger.dbg("Android application event", ev.code)
if ev.code == C.APP_CMD_SAVE_STATE then if ev.code == C.APP_CMD_SAVE_STATE then
UIManager:broadcastEvent(Event:new("FlushSettings")) UIManager:broadcastEvent(Event:new("FlushSettings"))
@ -280,6 +280,10 @@ function Device:init()
Generic.init(self) Generic.init(self)
end end
function Device:UIManagerReady(uimgr)
UIManager = uimgr
end
function Device:initNetworkManager(NetworkMgr) function Device:initNetworkManager(NetworkMgr)
function NetworkMgr:turnOnWifi(complete_callback) function NetworkMgr:turnOnWifi(complete_callback)
android.openWifiSettings() android.openWifiSettings()
@ -483,7 +487,6 @@ function Device:showLightDialog()
-- Delay it until next tick so that the event loop gets a chance to drain the input queue, -- Delay it until next tick so that the event loop gets a chance to drain the input queue,
-- and consume the APP_CMD_LOST_FOCUS event. -- and consume the APP_CMD_LOST_FOCUS event.
-- This helps prevent ANRs on Tolino (c.f., #6583 & #7552). -- This helps prevent ANRs on Tolino (c.f., #6583 & #7552).
local UIManager = require("ui/uimanager")
UIManager:nextTick(function() self:_showLightDialog() end) UIManager:nextTick(function() self:_showLightDialog() end)
end end
@ -499,8 +502,6 @@ function Device:_showLightDialog()
self.powerd.fl_warmth = self.powerd:frontlightWarmthHW() self.powerd.fl_warmth = self.powerd:frontlightWarmthHW()
logger.dbg("Dialog OK, warmth: " .. self.powerd.fl_warmth) logger.dbg("Dialog OK, warmth: " .. self.powerd.fl_warmth)
end end
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
UIManager:broadcastEvent(Event:new("FrontlightStateChanged")) UIManager:broadcastEvent(Event:new("FrontlightStateChanged"))
elseif action == C.ALIGHTS_DIALOG_CANCEL then elseif action == C.ALIGHTS_DIALOG_CANCEL then
logger.dbg("Dialog Cancel, brightness: " .. self.powerd.fl_intensity) logger.dbg("Dialog Cancel, brightness: " .. self.powerd.fl_intensity)
@ -519,7 +520,6 @@ end
function Device:download(link, name, ok_text) function Device:download(link, name, ok_text)
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local InfoMessage = require("ui/widget/infomessage") local InfoMessage = require("ui/widget/infomessage")
local UIManager = require("ui/uimanager")
local ok = android.download(link, name) local ok = android.download(link, name)
if ok == C.ADOWNLOAD_EXISTS then if ok == C.ADOWNLOAD_EXISTS then
self:install() self:install()
@ -540,8 +540,6 @@ end
function Device:install() function Device:install()
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
UIManager:show(ConfirmBox:new{ UIManager:show(ConfirmBox:new{
text = _("Update is ready. Install it now?"), text = _("Update is ready. Install it now?"),
ok_text = _("Install"), ok_text = _("Install"),

@ -1,4 +1,6 @@
local BasePowerD = require("device/generic/powerd") local BasePowerD = require("device/generic/powerd")
local Event = require("ui/event")
local UIManager
local _, android = pcall(require, "android") local _, android = pcall(require, "android")
local AndroidPowerD = BasePowerD:new{ local AndroidPowerD = BasePowerD:new{
@ -8,9 +10,7 @@ local AndroidPowerD = BasePowerD:new{
-- Let the footer know of the change -- Let the footer know of the change
local function broadcastLightChanges() local function broadcastLightChanges()
if package.loaded["ui/uimanager"] ~= nil then if UIManager then
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
UIManager:broadcastEvent(Event:new("FrontlightStateChanged")) UIManager:broadcastEvent(Event:new("FrontlightStateChanged"))
end end
end end
@ -83,4 +83,8 @@ function AndroidPowerD:turnOnFrontlightHW()
broadcastLightChanges() broadcastLightChanges()
end end
function AndroidPowerD:UIManagerReadyHW(uimgr)
UIManager = uimgr
end
return AndroidPowerD return AndroidPowerD

@ -200,16 +200,10 @@ function Cervantes:setEventHandlers(UIManager)
-- suspend. So let's unschedule it when suspending, and restart it after -- suspend. So let's unschedule it when suspending, and restart it after
-- resume. Done via the plugin's onSuspend/onResume handlers. -- resume. Done via the plugin's onSuspend/onResume handlers.
UIManager.event_handlers.Suspend = function() UIManager.event_handlers.Suspend = function()
self:_beforeSuspend()
self:onPowerEvent("Suspend") self:onPowerEvent("Suspend")
end end
UIManager.event_handlers.Resume = function() UIManager.event_handlers.Resume = function()
-- MONOTONIC doesn't tick during suspend,
-- invalidate the last battery capacity pull time so that we get up to date data immediately.
self:getPowerDevice():invalidateCapacityCache()
self:onPowerEvent("Resume") self:onPowerEvent("Resume")
self:_afterResume()
end end
UIManager.event_handlers.PowerPress = function() UIManager.event_handlers.PowerPress = function()
-- Always schedule power off. -- Always schedule power off.
@ -222,10 +216,7 @@ function Cervantes:setEventHandlers(UIManager)
-- resume if we were suspended -- resume if we were suspended
if self.screen_saver_mode then if self.screen_saver_mode then
if self.screen_saver_lock then if self.screen_saver_lock then
logger.dbg("Pressed power while awake in screen saver mode, going back to suspend...") UIManager.event_handlers.Suspend()
self:_beforeSuspend()
self.powerd:beforeSuspend() -- this won't be run by onPowerEvent because we're in screen_saver_mode
self:onPowerEvent("Suspend")
else else
UIManager.event_handlers.Resume() UIManager.event_handlers.Resume()
end end
@ -241,7 +232,7 @@ function Cervantes:setEventHandlers(UIManager)
UIManager.event_handlers.Charging = function() UIManager.event_handlers.Charging = function()
self:_beforeCharging() self:_beforeCharging()
-- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep. -- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep.
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
end end
end end
@ -249,7 +240,7 @@ function Cervantes:setEventHandlers(UIManager)
-- We need to put the device into suspension, other things need to be done before it. -- We need to put the device into suspension, other things need to be done before it.
self:usbPlugOut() self:usbPlugOut()
self:_afterNotCharging() self:_afterNotCharging()
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
end end
end end
@ -257,9 +248,9 @@ function Cervantes:setEventHandlers(UIManager)
UIManager.event_handlers.UsbPlugIn = function() UIManager.event_handlers.UsbPlugIn = function()
self:_beforeCharging() self:_beforeCharging()
-- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep. -- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep.
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
else elseif not self.screen_saver_lock then
-- Potentially start an USBMS session -- Potentially start an USBMS session
local MassStorage = require("ui/elements/mass_storage") local MassStorage = require("ui/elements/mass_storage")
MassStorage:start() MassStorage:start()
@ -269,9 +260,9 @@ function Cervantes:setEventHandlers(UIManager)
-- We need to put the device into suspension, other things need to be done before it. -- We need to put the device into suspension, other things need to be done before it.
self:usbPlugOut() self:usbPlugOut()
self:_afterNotCharging() self:_afterNotCharging()
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
else elseif not self.screen_saver_lock then
-- Potentially dismiss the USBMS ConfirmBox -- Potentially dismiss the USBMS ConfirmBox
local MassStorage = require("ui/elements/mass_storage") local MassStorage = require("ui/elements/mass_storage")
MassStorage:dismiss() MassStorage:dismiss()

@ -132,19 +132,29 @@ function CervantesPowerD:isChargingHW()
end end
function CervantesPowerD:beforeSuspend() function CervantesPowerD:beforeSuspend()
if self.fl == nil then return end -- Inhibit user input and emit the Suspend event.
-- just turn off frontlight without remembering its state self.device:_beforeSuspend()
self.fl:setBrightness(0)
if self.fl then
-- just turn off frontlight without remembering its state
self.fl:setBrightness(0)
end
end end
function CervantesPowerD:afterResume() function CervantesPowerD:afterResume()
if self.fl == nil then return end if self.fl then
-- just re-set it to self.hw_intensity that we haven't change on Suspend -- just re-set it to self.hw_intensity that we haven't changed on Suspend
if not self.device:hasNaturalLight() then if not self.device:hasNaturalLight() then
self.fl:setBrightness(self.hw_intensity) self.fl:setBrightness(self.hw_intensity)
else else
self.fl:setNaturalBrightness(self.hw_intensity, self.fl_warmth) self.fl:setNaturalBrightness(self.hw_intensity, self.fl_warmth)
end
end end
self:invalidateCapacityCache()
-- Restore user input and emit the Resume event.
self.device:_afterResume()
end end
return CervantesPowerD return CervantesPowerD

@ -5,7 +5,9 @@ This module defines stubs for common methods.
--]] --]]
local DataStorage = require("datastorage") local DataStorage = require("datastorage")
local Event = require("ui/event")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local UIManager -- Updated on UIManager init
local logger = require("logger") local logger = require("logger")
local ffi = require("ffi") local ffi = require("ffi")
local time = require("ui/time") local time = require("ui/time")
@ -259,7 +261,6 @@ function Device:getPowerDevice()
end end
function Device:rescheduleSuspend() function Device:rescheduleSuspend()
local UIManager = require("ui/uimanager")
UIManager:unschedule(self.suspend) UIManager:unschedule(self.suspend)
UIManager:scheduleIn(self.suspend_wait_timeout, self.suspend, self) UIManager:scheduleIn(self.suspend_wait_timeout, self.suspend, self)
end end
@ -270,11 +271,11 @@ function Device:onPowerEvent(ev)
if self.screen_saver_mode then if self.screen_saver_mode then
if ev == "Power" or ev == "Resume" then if ev == "Power" or ev == "Resume" then
if self.is_cover_closed then if self.is_cover_closed then
-- don't let power key press wake up device when the cover is in closed state. -- Don't let power key press wake up device when the cover is in closed state.
logger.dbg("Pressed power while asleep in screen saver mode with a closed sleepcover, going back to suspend...")
self:rescheduleSuspend() self:rescheduleSuspend()
else else
logger.dbg("Resuming...") logger.dbg("Resuming...")
local UIManager = require("ui/uimanager")
UIManager:unschedule(self.suspend) UIManager:unschedule(self.suspend)
if self:hasWifiManager() then if self:hasWifiManager() then
local network_manager = require("ui/network/manager") local network_manager = require("ui/network/manager")
@ -291,10 +292,21 @@ function Device:onPowerEvent(ev)
self.powerd:afterResume() self.powerd:afterResume()
end end
elseif ev == "Suspend" then elseif ev == "Suspend" then
-- Already in screen saver mode, no need to update UI/state before -- Already in screen saver mode, no need to update the UI (and state, usually) before suspending again.
-- suspending the hardware. This usually happens when sleep cover -- This usually happens when the sleep cover is closed on an already sleeping device,
-- is closed after the device was sent to suspend state. -- (e.g., it was previously suspended via the Power button).
logger.dbg("Already in screen saver mode, going back to suspend...") if self.screen_saver_lock then
-- This can only happen when some sort of screensaver_delay is set,
-- and the user presses the Power button *after* already having woken up the device.
-- In this case, we want to go back to suspend *without* affecting the screensaver,
-- so we simply mimic our own behavior when *not* in screen_saver_mode ;).
logger.dbg("Pressed power while awake in screen saver mode, going back to suspend...")
-- Basically, this is the only difference.
-- We need it because we're actually in a sane post-Resume event state right now.
self.powerd:beforeSuspend()
else
logger.dbg("Already in screen saver mode, going back to suspend...")
end
-- Much like the real suspend codepath below, in case we got here via screen_saver_lock, -- Much like the real suspend codepath below, in case we got here via screen_saver_lock,
-- make sure we murder WiFi again (because restore WiFi on resume could have kicked in). -- make sure we murder WiFi again (because restore WiFi on resume could have kicked in).
if self:hasWifiToggle() then if self:hasWifiToggle() then
@ -308,7 +320,6 @@ function Device:onPowerEvent(ev)
end end
-- else we were not in screensaver mode -- else we were not in screensaver mode
elseif ev == "Power" or ev == "Suspend" then elseif ev == "Power" or ev == "Suspend" then
local UIManager = require("ui/uimanager")
logger.dbg("Suspending...") logger.dbg("Suspending...")
-- Add the current state of the SleepCover flag... -- Add the current state of the SleepCover flag...
logger.dbg("Sleep cover is", self.is_cover_closed and "closed" or "open") logger.dbg("Sleep cover is", self.is_cover_closed and "closed" or "open")
@ -348,7 +359,6 @@ end
function Device:showLightDialog() function Device:showLightDialog()
local FrontLightWidget = require("ui/widget/frontlightwidget") local FrontLightWidget = require("ui/widget/frontlightwidget")
local UIManager = require("ui/uimanager")
UIManager:show(FrontLightWidget:new{}) UIManager:show(FrontLightWidget:new{})
end end
@ -357,8 +367,6 @@ function Device:info()
end end
function Device:install() function Device:install()
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
UIManager:show(ConfirmBox:new{ UIManager:show(ConfirmBox:new{
text = _("Update is ready. Install it now?"), text = _("Update is ready. Install it now?"),
@ -925,8 +933,28 @@ function Device:untar(archive, extract_to, with_stripped_root)
return os.execute(cmd:format(archive, extract_to)) return os.execute(cmd:format(archive, extract_to))
end end
-- Update our UIManager reference once it's ready
function Device:_UIManagerReady(uimgr)
-- Our own ref
UIManager = uimgr
-- Let implementations do the same thing
self:UIManagerReady(uimgr)
-- Forward that to PowerD
self.powerd:UIManagerReady(uimgr)
-- And to Input
self.input:UIManagerReady(uimgr)
-- Setup PM event handlers
-- NOTE: We keep forwarding the uimgr reference because some implementations don't actually have a module-local UIManager ref to update
self:_setEventHandlers(uimgr)
end
-- In case implementations *also* need a reference to UIManager, *this* is the one to implement!
function Device:UIManagerReady(uimgr) end
-- Set device event handlers common to all devices -- Set device event handlers common to all devices
function Device:_setEventHandlers(UIManager) function Device:_setEventHandlers(uimgr)
if self:canReboot() then if self:canReboot() then
UIManager.event_handlers.Reboot = function(message_text) UIManager.event_handlers.Reboot = function(message_text)
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
@ -934,7 +962,6 @@ function Device:_setEventHandlers(UIManager)
text = message_text or _("Are you sure you want to reboot the device?"), text = message_text or _("Are you sure you want to reboot the device?"),
ok_text = _("Reboot"), ok_text = _("Reboot"),
ok_callback = function() ok_callback = function()
local Event = require("ui/event")
UIManager:broadcastEvent(Event:new("Reboot")) UIManager:broadcastEvent(Event:new("Reboot"))
UIManager:nextTick(UIManager.reboot_action) UIManager:nextTick(UIManager.reboot_action)
end, end,
@ -951,7 +978,6 @@ function Device:_setEventHandlers(UIManager)
text = message_text or _("Are you sure you want to power off the device?"), text = message_text or _("Are you sure you want to power off the device?"),
ok_text = _("Power off"), ok_text = _("Power off"),
ok_callback = function() ok_callback = function()
local Event = require("ui/event")
UIManager:broadcastEvent(Event:new("PowerOff")) UIManager:broadcastEvent(Event:new("PowerOff"))
UIManager:nextTick(UIManager.poweroff_action) UIManager:nextTick(UIManager.poweroff_action)
end, end,
@ -968,7 +994,6 @@ function Device:_setEventHandlers(UIManager)
text = message_text or _("This will take effect on next restart."), text = message_text or _("This will take effect on next restart."),
ok_text = _("Restart now"), ok_text = _("Restart now"),
ok_callback = function() ok_callback = function()
local Event = require("ui/event")
UIManager:broadcastEvent(Event:new("Restart")) UIManager:broadcastEvent(Event:new("Restart"))
end, end,
cancel_text = _("Restart later"), cancel_text = _("Restart later"),
@ -983,24 +1008,23 @@ function Device:_setEventHandlers(UIManager)
end end
end end
self:setEventHandlers(UIManager) -- Let implementations expand on that
self:setEventHandlers(uimgr)
end end
-- Devices can add additional event handlers by overwriting this method. -- Devices can add additional event handlers by implementing this method.
function Device:setEventHandlers(UIManager) function Device:setEventHandlers(uimgr)
-- These will be most probably overwritten in the device specific `setEventHandlers` -- These will most probably be overwritten by device-specific `setEventHandlers` implementations
UIManager.event_handlers.Suspend = function() UIManager.event_handlers.Suspend = function()
self:_beforeSuspend(false) self.powerd:beforeSuspend()
end end
UIManager.event_handlers.Resume = function() UIManager.event_handlers.Resume = function()
self:_afterResume(false) self.powerd:afterResume()
end end
end end
-- The common operations that should be performed before suspending the device. -- The common operations that should be performed before suspending the device.
function Device:_beforeSuspend(inhibit) function Device:_beforeSuspend(inhibit)
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
UIManager:flushSettings() UIManager:flushSettings()
UIManager:broadcastEvent(Event:new("Suspend")) UIManager:broadcastEvent(Event:new("Suspend"))
@ -1023,16 +1047,12 @@ function Device:_afterResume(inhibit)
self.input:inhibitInput(false) self.input:inhibitInput(false)
end end
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
UIManager:broadcastEvent(Event:new("Resume")) UIManager:broadcastEvent(Event:new("Resume"))
end end
-- The common operations that should be performed when the device is plugged to a power source. -- The common operations that should be performed when the device is plugged to a power source.
function Device:_beforeCharging() function Device:_beforeCharging()
-- Leave the kernel some time to figure it out ;o). -- Leave the kernel some time to figure it out ;o).
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
UIManager:scheduleIn(1, function() self:setupChargingLED() end) UIManager:scheduleIn(1, function() self:setupChargingLED() end)
UIManager:broadcastEvent(Event:new("Charging")) UIManager:broadcastEvent(Event:new("Charging"))
end end
@ -1040,8 +1060,6 @@ end
-- The common operations that should be performed when the device is unplugged from a power source. -- The common operations that should be performed when the device is unplugged from a power source.
function Device:_afterNotCharging() function Device:_afterNotCharging()
-- Leave the kernel some time to figure it out ;o). -- Leave the kernel some time to figure it out ;o).
local Event = require("ui/event")
local UIManager = require("ui/uimanager")
UIManager:scheduleIn(1, function() self:setupChargingLED() end) UIManager:scheduleIn(1, function() self:setupChargingLED() end)
UIManager:broadcastEvent(Event:new("NotCharging")) UIManager:broadcastEvent(Event:new("NotCharging"))
end end

@ -1,5 +1,6 @@
local UIManager = nil -- will be updated when available local Event = require("ui/event")
local Math = require("optmath") local Math = require("optmath")
local UIManager
local logger = require("logger") local logger = require("logger")
local time = require("ui/time") local time = require("ui/time")
local BasePowerD = { local BasePowerD = {
@ -41,11 +42,6 @@ function BasePowerD:new(o)
return o return o
end end
function BasePowerD:readyUI()
UIManager = require("ui/uimanager")
self:readyUIHW(UIManager)
end
function BasePowerD:init() end function BasePowerD:init() end
function BasePowerD:setIntensityHW(intensity) end function BasePowerD:setIntensityHW(intensity) end
--- @note: Unlike the "public" setWarmth, this one takes a value in the *native* scale! --- @note: Unlike the "public" setWarmth, this one takes a value in the *native* scale!
@ -66,13 +62,30 @@ function BasePowerD:isFrontlightOnHW() return self.fl_intensity > self.fl_min en
function BasePowerD:turnOffFrontlightHW(done_callback) self:setIntensityHW(self.fl_min) end function BasePowerD:turnOffFrontlightHW(done_callback) self:setIntensityHW(self.fl_min) end
function BasePowerD:turnOnFrontlightHW(done_callback) self:setIntensityHW(self.fl_intensity) end --- @fixme: what if fl_intensity == fl_min (c.f., kindle)? function BasePowerD:turnOnFrontlightHW(done_callback) self:setIntensityHW(self.fl_intensity) end --- @fixme: what if fl_intensity == fl_min (c.f., kindle)?
function BasePowerD:frontlightWarmthHW() return 0 end function BasePowerD:frontlightWarmthHW() return 0 end
function BasePowerD:readyUIHW(uimgr) end
-- Anything that needs to be done before doing a real hardware suspend. -- Anything that needs to be done before doing a real hardware suspend.
-- (Such as turning the front light off). -- (Such as turning the front light off).
function BasePowerD:beforeSuspend() end -- Do *not* omit calling Device's _beforeSuspend method! This default implementation passes `false` so as *not* to disable input events during PM.
function BasePowerD:beforeSuspend() self.device:_beforeSuspend(false) end
-- Anything that needs to be done after doing a real hardware resume. -- Anything that needs to be done after doing a real hardware resume.
-- (Such as restoring front light state). -- (Such as restoring front light state).
function BasePowerD:afterResume() end -- Do *not* omit calling Device's _afterResume method!
function BasePowerD:afterResume()
-- MONOTONIC doesn't tick during suspend,
-- invalidate the last battery capacity pull time so that we get up to date data immediately.
self:invalidateCapacityCache()
self.device:_afterResume(false)
end
-- Update our UIManager reference once it's ready
function BasePowerD:UIManagerReady(uimgr)
-- Our own ref
UIManager = uimgr
-- Let implementations do the same thing, too
self:UIManagerReadyHW(uimgr)
end
-- Ditto, but for implementations
function BasePowerD:UIManagerReadyHW(uimgr) end
function BasePowerD:isFrontlightOn() function BasePowerD:isFrontlightOn()
return self.is_fl_on return self.is_fl_on
@ -275,7 +288,6 @@ end
function BasePowerD:stateChanged() function BasePowerD:stateChanged()
-- BasePowerD is loaded before UIManager. So we cannot broadcast events before UIManager has been loaded. -- BasePowerD is loaded before UIManager. So we cannot broadcast events before UIManager has been loaded.
if UIManager then if UIManager then
local Event = require("ui/event")
UIManager:broadcastEvent(Event:new("FrontlightStateChanged")) UIManager:broadcastEvent(Event:new("FrontlightStateChanged"))
end end
end end

@ -7,6 +7,7 @@ local DEBUG = require("dbg")
local Event = require("ui/event") local Event = require("ui/event")
local GestureDetector = require("device/gesturedetector") local GestureDetector = require("device/gesturedetector")
local Key = require("device/key") local Key = require("device/key")
local UIManager
local framebuffer = require("ffi/framebuffer") local framebuffer = require("ffi/framebuffer")
local input = require("ffi/input") local input = require("ffi/input")
local logger = require("logger") local logger = require("logger")
@ -275,6 +276,10 @@ function Input:init()
self._inhibitInputUntil_func = function() self:inhibitInputUntil() end self._inhibitInputUntil_func = function() self:inhibitInputUntil() end
end end
function Input:UIManagerReady(uimgr)
UIManager = uimgr
end
--[[-- --[[--
Setup a rotation_map that does nothing (for platforms where the events we get are already translated). Setup a rotation_map that does nothing (for platforms where the events we get are already translated).
--]] --]]
@ -606,14 +611,12 @@ function Input:handleKeyBoardEv(ev)
-- toggle fullscreen on F11 -- toggle fullscreen on F11
if self:isEvKeyPress(ev) and keycode == "F11" and not self.device:isAlwaysFullscreen() then if self:isEvKeyPress(ev) and keycode == "F11" and not self.device:isAlwaysFullscreen() then
local UIManager = require("ui/uimanager")
UIManager:broadcastEvent(Event:new("ToggleFullscreen")) UIManager:broadcastEvent(Event:new("ToggleFullscreen"))
end end
-- quit on Alt + F4 -- quit on Alt + F4
-- this is also emitted by the close event in SDL -- this is also emitted by the close event in SDL
if self:isEvKeyPress(ev) and self.modifiers["Alt"] and keycode == "F4" then if self:isEvKeyPress(ev) and self.modifiers["Alt"] and keycode == "F4" then
local UIManager = require("ui/uimanager")
UIManager:broadcastEvent(Event:new("Close")) -- Tell all widgets to close. UIManager:broadcastEvent(Event:new("Close")) -- Tell all widgets to close.
UIManager:nextTick(function() UIManager:quit() end) -- Ensure the program closes in case of some lingering dialog. UIManager:nextTick(function() UIManager:quit() end) -- Ensure the program closes in case of some lingering dialog.
end end
@ -986,7 +989,6 @@ function Input:handleMiscGyroEv(ev)
if rotation_mode and rotation_mode ~= old_rotation_mode and screen_mode == old_screen_mode then if rotation_mode and rotation_mode ~= old_rotation_mode and screen_mode == old_screen_mode then
-- Cheaper than a full SetRotationMode event, as we don't need to re-layout anything. -- Cheaper than a full SetRotationMode event, as we don't need to re-layout anything.
self.device.screen:setRotationMode(rotation_mode) self.device.screen:setRotationMode(rotation_mode)
local UIManager = require("ui/uimanager")
UIManager:onRotation() UIManager:onRotation()
end end
else else
@ -1284,7 +1286,6 @@ function Input:waitEvent(now, deadline)
elseif ok == nil then elseif ok == nil then
-- Something went horribly wrong, abort. -- Something went horribly wrong, abort.
logger.err("Polling for input events failed catastrophically") logger.err("Polling for input events failed catastrophically")
local UIManager = require("ui/uimanager")
UIManager:abort() UIManager:abort()
break break
end end
@ -1453,7 +1454,6 @@ Request all input events to be ignored for some duration.
@param set_or_seconds either `true`, in which case a platform-specific delay is chosen, or a duration in seconds (***int***). @param set_or_seconds either `true`, in which case a platform-specific delay is chosen, or a duration in seconds (***int***).
]] ]]
function Input:inhibitInputUntil(set_or_seconds) function Input:inhibitInputUntil(set_or_seconds)
local UIManager = require("ui/uimanager")
UIManager:unschedule(self._inhibitInputUntil_func) UIManager:unschedule(self._inhibitInputUntil_func)
if not set_or_seconds then -- remove any previously set if not set_or_seconds then -- remove any previously set
self:inhibitInput(false) self:inhibitInput(false)

@ -1,4 +1,5 @@
local Generic = require("device/generic/device") local Generic = require("device/generic/device")
local UIManager
local time = require("ui/time") local time = require("ui/time")
local lfs = require("libs/libkoreader-lfs") local lfs = require("libs/libkoreader-lfs")
local logger = require("logger") local logger = require("logger")
@ -177,7 +178,6 @@ function Kindle:initNetworkManager(NetworkMgr)
kindleEnableWifi(0) kindleEnableWifi(0)
-- NOTE: Same here, except disconnect is simpler, so a dumb delay will do... -- NOTE: Same here, except disconnect is simpler, so a dumb delay will do...
if complete_callback then if complete_callback then
local UIManager = require("ui/uimanager")
UIManager:scheduleIn(2, complete_callback) UIManager:scheduleIn(2, complete_callback)
end end
end end
@ -291,8 +291,9 @@ function Kindle:intoScreenSaver()
-- so that we do the right thing on resume ;). -- so that we do the right thing on resume ;).
self.screen_saver_mode = true self.screen_saver_mode = true
end end
self.powerd:beforeSuspend()
end end
self.powerd:beforeSuspend()
end end
function Kindle:outofScreenSaver() function Kindle:outofScreenSaver()
@ -300,7 +301,6 @@ function Kindle:outofScreenSaver()
if self:supportsScreensaver() then if self:supportsScreensaver() then
local Screensaver = require("ui/screensaver") local Screensaver = require("ui/screensaver")
local widget_was_closed = Screensaver:close() local widget_was_closed = Screensaver:close()
local UIManager = require("ui/uimanager")
if widget_was_closed then if widget_was_closed then
-- And redraw everything in case the framework managed to screw us over... -- And redraw everything in case the framework managed to screw us over...
UIManager:nextTick(function() UIManager:setDirty("all", "full") end) UIManager:nextTick(function() UIManager:setDirty("all", "full") end)
@ -338,15 +338,15 @@ function Kindle:outofScreenSaver()
elseif os.getenv("CVM_STOPPED") == "yes" then elseif os.getenv("CVM_STOPPED") == "yes" then
os.execute("killall -STOP cvm") os.execute("killall -STOP cvm")
end end
local UIManager = require("ui/uimanager")
-- NOTE: We redraw after a slightly longer delay to take care of the potentially dynamic ad screen... -- NOTE: We redraw after a slightly longer delay to take care of the potentially dynamic ad screen...
-- This is obviously brittle as all hell. Tested on a slow-ass PW1. -- This is obviously brittle as all hell. Tested on a slow-ass PW1.
UIManager:scheduleIn(3, function() UIManager:setDirty("all", "full") end) UIManager:scheduleIn(3, function() UIManager:setDirty("all", "full") end)
-- Flip the switch again -- Flip the switch again
self.screen_saver_mode = false self.screen_saver_mode = false
end end
self.powerd:afterResume()
end end
self.powerd:afterResume()
end end
function Kindle:usbPlugOut() function Kindle:usbPlugOut()
@ -369,17 +369,19 @@ function Kindle:untar(archive, extract_to)
return os.execute(("./tar --no-same-permissions --no-same-owner -xf %q -C %q"):format(archive, extract_to)) return os.execute(("./tar --no-same-permissions --no-same-owner -xf %q -C %q"):format(archive, extract_to))
end end
function Kindle:setEventHandlers(UIManager) function Kindle:UIManagerReady(uimgr)
UIManager = uimgr
end
function Kindle:setEventHandlers(uimgr)
UIManager.event_handlers.Suspend = function() UIManager.event_handlers.Suspend = function()
self.powerd:toggleSuspend() self.powerd:toggleSuspend()
end end
UIManager.event_handlers.IntoSS = function() UIManager.event_handlers.IntoSS = function()
self:_beforeSuspend()
self:intoScreenSaver() self:intoScreenSaver()
end end
UIManager.event_handlers.OutOfSS = function() UIManager.event_handlers.OutOfSS = function()
self:outofScreenSaver() self:outofScreenSaver()
self:_afterResume()
end end
UIManager.event_handlers.Charging = function() UIManager.event_handlers.Charging = function()
self:_beforeCharging() self:_beforeCharging()

@ -1,4 +1,5 @@
local BasePowerD = require("device/generic/powerd") local BasePowerD = require("device/generic/powerd")
local UIManager
local WakeupMgr = require("device/wakeupmgr") local WakeupMgr = require("device/wakeupmgr")
local logger = require("logger") local logger = require("logger")
local util = require("util") local util = require("util")
@ -185,7 +186,6 @@ function KindlePowerD:afterResume()
if not self.device:hasFrontlight() then if not self.device:hasFrontlight() then
return return
end end
local UIManager = require("ui/uimanager")
if self:isFrontlightOn() then if self:isFrontlightOn() then
-- The Kindle framework should turn the front light back on automatically. -- The Kindle framework should turn the front light back on automatically.
-- The following statement ensures consistency of intensity, but should basically always be redundant, -- The following statement ensures consistency of intensity, but should basically always be redundant,
@ -249,7 +249,6 @@ function KindlePowerD:initWakeupMgr()
-- This filters out user input resumes -> device will resume to active -- This filters out user input resumes -> device will resume to active
-- Also the Kindle stays in Ready to suspend for 10 seconds -- Also the Kindle stays in Ready to suspend for 10 seconds
-- so the alarm may fire 10 seconds early -- so the alarm may fire 10 seconds early
local UIManager = require("ui/uimanager")
UIManager:scheduleIn(15, self.checkUnexpectedWakeup, self) UIManager:scheduleIn(15, self.checkUnexpectedWakeup, self)
end end
@ -284,6 +283,22 @@ function KindlePowerD:resetT1Timeout()
end end
end end
function KindlePowerD:beforeSuspend()
-- Inhibit user input and emit the Suspend event.
self.device:_beforeSuspend()
end
function KindlePowerD:afterResume()
self:invalidateCapacityCache()
-- Restore user input and emit the Resume event.
self.device:_afterResume()
end
function KindlePowerD:UIManagerReadyHW(uimgr)
UIManager = uimgr
end
--- @fixme: This won't ever fire on its own, as KindlePowerD is already a metatable on a plain table. --- @fixme: This won't ever fire on its own, as KindlePowerD is already a metatable on a plain table.
function KindlePowerD:__gc() function KindlePowerD:__gc()
if self.lipc_handle then if self.lipc_handle then

@ -1,6 +1,6 @@
local Generic = require("device/generic/device") local Generic = require("device/generic/device")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local UIManager -- Updated on UIManager init local UIManager
local WakeupMgr = require("device/wakeupmgr") local WakeupMgr = require("device/wakeupmgr")
local time = require("ui/time") local time = require("ui/time")
local ffiUtil = require("ffi/util") local ffiUtil = require("ffi/util")
@ -1369,24 +1369,19 @@ function Kobo:isStartupScriptUpToDate()
return md5.sumFile(current_script) == md5.sumFile(new_script) return md5.sumFile(current_script) == md5.sumFile(new_script)
end end
function Kobo:setEventHandlers(uimgr) function Kobo:UIManagerReady(uimgr)
-- Update our module-local
UIManager = uimgr UIManager = uimgr
end
function Kobo:setEventHandlers(uimgr)
-- We do not want auto suspend procedure to waste battery during -- We do not want auto suspend procedure to waste battery during
-- suspend. So let's unschedule it when suspending, and restart it after -- suspend. So let's unschedule it when suspending, and restart it after
-- resume. Done via the plugin's onSuspend/onResume handlers. -- resume. Done via the plugin's onSuspend/onResume handlers.
UIManager.event_handlers.Suspend = function() UIManager.event_handlers.Suspend = function()
self:_beforeSuspend()
self:onPowerEvent("Suspend") self:onPowerEvent("Suspend")
end end
UIManager.event_handlers.Resume = function() UIManager.event_handlers.Resume = function()
-- MONOTONIC doesn't tick during suspend,
-- invalidate the last battery capacity pull time so that we get up to date data immediately.
self:getPowerDevice():invalidateCapacityCache()
self:onPowerEvent("Resume") self:onPowerEvent("Resume")
self:_afterResume()
end end
UIManager.event_handlers.PowerPress = function() UIManager.event_handlers.PowerPress = function()
-- Always schedule power off. -- Always schedule power off.
@ -1399,14 +1394,7 @@ function Kobo:setEventHandlers(uimgr)
-- resume if we were suspended -- resume if we were suspended
if self.screen_saver_mode then if self.screen_saver_mode then
if self.screen_saver_lock then if self.screen_saver_lock then
-- This can only happen when some sort of screensaver_delay is set, UIManager.event_handlers.Suspend()
-- and the user presses the Power button *after* already having woken up the device.
-- In this case, we want to go back to suspend *without* affecting the screensaver,
-- so we mimic UIManager.event_handlers.Suspend's behavior when *not* in screen_saver_mode ;).
logger.dbg("Pressed power while awake in screen saver mode, going back to suspend...")
self:_beforeSuspend()
self.powerd:beforeSuspend() -- this won't be run by onPowerEvent because we're in screen_saver_mode
self:onPowerEvent("Suspend")
else else
UIManager.event_handlers.Resume() UIManager.event_handlers.Resume()
end end
@ -1422,7 +1410,7 @@ function Kobo:setEventHandlers(uimgr)
UIManager.event_handlers.Charging = function() UIManager.event_handlers.Charging = function()
self:_beforeCharging() self:_beforeCharging()
-- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep. -- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep.
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
end end
end end
@ -1430,7 +1418,7 @@ function Kobo:setEventHandlers(uimgr)
-- We need to put the device into suspension, other things need to be done before it. -- We need to put the device into suspension, other things need to be done before it.
self:usbPlugOut() self:usbPlugOut()
self:_afterNotCharging() self:_afterNotCharging()
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
end end
end end
@ -1438,9 +1426,9 @@ function Kobo:setEventHandlers(uimgr)
UIManager.event_handlers.UsbPlugIn = function() UIManager.event_handlers.UsbPlugIn = function()
self:_beforeCharging() self:_beforeCharging()
-- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep. -- NOTE: Plug/unplug events will wake the device up, which is why we put it back to sleep.
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
else elseif not self.screen_saver_lock then
-- Potentially start an USBMS session -- Potentially start an USBMS session
local MassStorage = require("ui/elements/mass_storage") local MassStorage = require("ui/elements/mass_storage")
MassStorage:start() MassStorage:start()
@ -1450,9 +1438,9 @@ function Kobo:setEventHandlers(uimgr)
-- We need to put the device into suspension, other things need to be done before it. -- We need to put the device into suspension, other things need to be done before it.
self:usbPlugOut() self:usbPlugOut()
self:_afterNotCharging() self:_afterNotCharging()
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
else elseif not self.screen_saver_lock then
-- Potentially dismiss the USBMS ConfirmBox -- Potentially dismiss the USBMS ConfirmBox
local MassStorage = require("ui/elements/mass_storage") local MassStorage = require("ui/elements/mass_storage")
MassStorage:dismiss() MassStorage:dismiss()

@ -1,8 +1,8 @@
local UIManager = nil -- will be updated when available
local BasePowerD = require("device/generic/powerd") local BasePowerD = require("device/generic/powerd")
local Math = require("optmath") local Math = require("optmath")
local NickelConf = require("device/kobo/nickel_conf") local NickelConf = require("device/kobo/nickel_conf")
local SysfsLight = require ("device/sysfs_light") local SysfsLight = require ("device/sysfs_light")
local UIManager
local RTC = require("ffi/rtc") local RTC = require("ffi/rtc")
-- Here, we only deal with the real hw intensity. -- Here, we only deal with the real hw intensity.
@ -415,26 +415,44 @@ end
-- Turn off front light before suspend. -- Turn off front light before suspend.
function KoboPowerD:beforeSuspend() function KoboPowerD:beforeSuspend()
if self.fl == nil then return end -- Inhibit user input and emit the Suspend event.
-- Remember the current frontlight state self.device:_beforeSuspend()
self.fl_was_on = self.is_fl_on
-- Turn off the frontlight -- Handle the frontlight last,
self:turnOffFrontlight() -- to prevent as many things as we can from interfering with the smoothness of the ramp
if self.fl then
-- Remember the current frontlight state
self.fl_was_on = self.is_fl_on
-- Turn off the frontlight
-- NOTE: Funky delay mainly to yield to the EPDC's refresh on UP systems.
-- (Neither yieldToEPDC nor nextTick & friends quite cut it here)...
UIManager:scheduleIn(0.001, self.turnOffFrontlight, self)
end
end end
-- Restore front light state after resume. -- Restore front light state after resume.
function KoboPowerD:afterResume() function KoboPowerD:afterResume()
if self.fl == nil then return end
-- Don't bother if the light was already off on suspend
if not self.fl_was_on then return end
-- Turn the frontlight back on
self:turnOnFrontlight()
-- Set the system clock to the hardware clock's time. -- Set the system clock to the hardware clock's time.
RTC:HCToSys() RTC:HCToSys()
self:invalidateCapacityCache()
-- Restore user input and emit the Resume event.
self.device:_afterResume()
-- There's a whole bunch of stuff happening before us in Generic:onPowerEvent,
-- so we'll delay this ever so slightly so as to appear as smooth as possible...
if self.fl then
-- Don't bother if the light was already off on suspend
if self.fl_was_on then
-- Turn the frontlight back on
-- NOTE: There's quite likely *more* resource contention than on suspend here :/.
UIManager:scheduleIn(0.001, self.turnOnFrontlight, self)
end
end
end end
function KoboPowerD:readyUIHW(uimgr) function KoboPowerD:UIManagerReadyHW(uimgr)
UIManager = uimgr UIManager = uimgr
end end

@ -1,4 +1,5 @@
local Generic = require("device/generic/device") -- <= look at this file! local Generic = require("device/generic/device") -- <= look at this file!
local UIManager
local logger = require("logger") local logger = require("logger")
local ffi = require("ffi") local ffi = require("ffi")
local C = ffi.C local C = ffi.C
@ -333,8 +334,6 @@ function PocketBook:reboot()
end end
function PocketBook:initNetworkManager(NetworkMgr) function PocketBook:initNetworkManager(NetworkMgr)
local UIManager = require("ui/uimanager")
local function keepWifiAlive() local function keepWifiAlive()
-- Make sure only one wifiKeepAlive is scheduled -- Make sure only one wifiKeepAlive is scheduled
UIManager:unschedule(keepWifiAlive) UIManager:unschedule(keepWifiAlive)
@ -386,13 +385,17 @@ function PocketBook:getDefaultCoverPath()
return "/mnt/ext1/system/logo/offlogo/cover.bmp" return "/mnt/ext1/system/logo/offlogo/cover.bmp"
end end
function PocketBook:setEventHandlers(UIManager) function PocketBook:UIManagerReady(uimgr)
UIManager = uimgr
end
function PocketBook:setEventHandlers(uimgr)
-- Only fg/bg state plugin notifiers, not real power event. -- Only fg/bg state plugin notifiers, not real power event.
UIManager.event_handlers.Suspend = function() UIManager.event_handlers.Suspend = function()
self:_beforeSuspend() self.powerd:beforeSuspend()
end end
UIManager.event_handlers.Resume = function() UIManager.event_handlers.Resume = function()
self:_afterResume() self.powerd:afterResume()
end end
UIManager.event_handlers.Exit = function() UIManager.event_handlers.Exit = function()
local Event = require("ui/event") local Event = require("ui/event")

@ -73,4 +73,16 @@ function PocketBookPowerD:isChargingHW()
end end
end end
function PocketBookPowerD:beforeSuspend()
-- Inhibit user input and emit the Suspend event.
self.device:_beforeSuspend()
end
function PocketBookPowerD:afterResume()
self:invalidateCapacityCache()
-- Restore user input and emit the Resume event.
self.device:_afterResume()
end
return PocketBookPowerD return PocketBookPowerD

@ -268,12 +268,10 @@ end
function Remarkable:setEventHandlers(UIManager) function Remarkable:setEventHandlers(UIManager)
UIManager.event_handlers.Suspend = function() UIManager.event_handlers.Suspend = function()
self:_beforeSuspend()
self:onPowerEvent("Suspend") self:onPowerEvent("Suspend")
end end
UIManager.event_handlers.Resume = function() UIManager.event_handlers.Resume = function()
self:onPowerEvent("Resume") self:onPowerEvent("Resume")
self:_afterResume()
end end
UIManager.event_handlers.PowerPress = function() UIManager.event_handlers.PowerPress = function()
UIManager:scheduleIn(2, UIManager.poweroff_action) UIManager:scheduleIn(2, UIManager.poweroff_action)
@ -284,10 +282,7 @@ function Remarkable:setEventHandlers(UIManager)
-- resume if we were suspended -- resume if we were suspended
if self.screen_saver_mode then if self.screen_saver_mode then
if self.screen_saver_lock then if self.screen_saver_lock then
logger.dbg("Pressed power while awake in screen saver mode, going back to suspend...") UIManager.event_handlers.Suspend()
self:_beforeSuspend()
self.powerd:beforeSuspend() -- this won't be run by onPowerEvent because we're in screen_saver_mode
self:onPowerEvent("Suspend")
else else
UIManager.event_handlers.Resume() UIManager.event_handlers.Resume()
end end

@ -22,4 +22,16 @@ function Remarkable_PowerD:isChargingHW()
return self:read_str_file(self.status_file) == "Charging" return self:read_str_file(self.status_file) == "Charging"
end end
function Remarkable_PowerD:beforeSuspend()
-- Inhibit user input and emit the Suspend event.
self.device:_beforeSuspend()
end
function Remarkable_PowerD:afterResume()
self:invalidateCapacityCache()
-- Restore user input and emit the Resume event.
self.device:_afterResume()
end
return Remarkable_PowerD return Remarkable_PowerD

@ -1,5 +1,7 @@
local Event = require("ui/event") local Event = require("ui/event")
local Geom = require("ui/geometry")
local Generic = require("device/generic/device") local Generic = require("device/generic/device")
local UIManager
local SDL = require("ffi/SDL2_0") local SDL = require("ffi/SDL2_0")
local ffi = require("ffi") local ffi = require("ffi")
local logger = require("logger") local logger = require("logger")
@ -180,8 +182,7 @@ function Device:init()
device = self, device = self,
event_map = require("device/sdl/event_map_sdl2"), event_map = require("device/sdl/event_map_sdl2"),
handleSdlEv = function(device_input, ev) handleSdlEv = function(device_input, ev)
local Geom = require("ui/geometry")
local UIManager = require("ui/uimanager")
-- SDL events can remain cdata but are almost completely transparent -- SDL events can remain cdata but are almost completely transparent
local SDL_TEXTINPUT = 771 local SDL_TEXTINPUT = 771
@ -342,7 +343,11 @@ function Device:toggleFullscreen()
end end
end end
function Device:setEventHandlers(UIManager) function Device:UIManagerReady(uimgr)
UIManager = uimgr
end
function Device:setEventHandlers(uimgr)
if not self:canSuspend() then if not self:canSuspend() then
-- If we can't suspend, we have no business even trying to, as we may not have overloaded `Device:simulateResume`. -- If we can't suspend, we have no business even trying to, as we may not have overloaded `Device:simulateResume`.
-- Instead, rely on the Generic Suspend/Resume handlers. -- Instead, rely on the Generic Suspend/Resume handlers.
@ -350,17 +355,19 @@ function Device:setEventHandlers(UIManager)
end end
UIManager.event_handlers.Suspend = function() UIManager.event_handlers.Suspend = function()
self:_beforeSuspend()
self:simulateSuspend() self:simulateSuspend()
end end
UIManager.event_handlers.Resume = function() UIManager.event_handlers.Resume = function()
self:simulateResume() self:simulateResume()
self:_afterResume()
end end
UIManager.event_handlers.PowerRelease = function() UIManager.event_handlers.PowerRelease = function()
-- Resume if we were suspended -- Resume if we were suspended
if self.screen_saver_mode then if self.screen_saver_mode then
UIManager.event_handlers.Resume() if self.screen_saver_lock then
UIManager.event_handlers.Suspend()
else
UIManager.event_handlers.Resume()
end
else else
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
end end
@ -385,16 +392,19 @@ function Emulator:simulateSuspend()
local Screensaver = require("ui/screensaver") local Screensaver = require("ui/screensaver")
Screensaver:setup() Screensaver:setup()
Screensaver:show() Screensaver:show()
self.powerd:beforeSuspend()
end end
function Emulator:simulateResume() function Emulator:simulateResume()
local Screensaver = require("ui/screensaver") local Screensaver = require("ui/screensaver")
Screensaver:close() Screensaver:close()
self.powerd:afterResume()
end end
-- fake network manager for the emulator -- fake network manager for the emulator
function Emulator:initNetworkManager(NetworkMgr) function Emulator:initNetworkManager(NetworkMgr)
local UIManager = require("ui/uimanager")
local connectionChangedEvent = function() local connectionChangedEvent = function()
if G_reader_settings:nilOrTrue("emulator_fake_wifi_connected") then if G_reader_settings:nilOrTrue("emulator_fake_wifi_connected") then
UIManager:broadcastEvent(Event:new("NetworkConnected")) UIManager:broadcastEvent(Event:new("NetworkConnected"))

@ -37,4 +37,16 @@ function SDLPowerD:isChargingHW()
return false return false
end end
function SDLPowerD:beforeSuspend()
-- Inhibit user input and emit the Suspend event.
self.device:_beforeSuspend()
end
function SDLPowerD:afterResume()
self:invalidateCapacityCache()
-- Restore user input and emit the Resume event.
self.device:_afterResume()
end
return SDLPowerD return SDLPowerD

@ -1,5 +1,6 @@
local Generic = require("device/generic/device") -- <= look at this file! local Generic = require("device/generic/device") -- <= look at this file!
local PluginShare = require("pluginshare") local PluginShare = require("pluginshare")
local UIManager
local ffi = require("ffi") local ffi = require("ffi")
local logger = require("logger") local logger = require("logger")
@ -102,7 +103,6 @@ function SonyPRSTUX:outofScreenSaver()
if self.screen_saver_mode then if self.screen_saver_mode then
local Screensaver = require("ui/screensaver") local Screensaver = require("ui/screensaver")
Screensaver:close() Screensaver:close()
local UIManager = require("ui/uimanager")
UIManager:nextTick(function() UIManager:setDirty("all", "full") end) UIManager:nextTick(function() UIManager:setDirty("all", "full") end)
end end
self.powerd:afterResume() self.powerd:afterResume()
@ -190,16 +190,18 @@ function SonyPRSTUX:getDeviceModel()
return ffi.string("PRS-T2") return ffi.string("PRS-T2")
end end
function SonyPRSTUX:setEventHandlers(UIManager) function SonyPRSTUX:UIManagerReady(uimgr)
UIManager = uimgr
end
function SonyPRSTUX:setEventHandlers(uimgr)
UIManager.event_handlers.Suspend = function() UIManager.event_handlers.Suspend = function()
self:_beforeSuspend()
self:intoScreenSaver() self:intoScreenSaver()
self:suspend() self:suspend()
end end
UIManager.event_handlers.Resume = function() UIManager.event_handlers.Resume = function()
self:resume() self:resume()
self:outofScreenSaver() self:outofScreenSaver()
self:_afterResume()
end end
UIManager.event_handlers.PowerPress = function() UIManager.event_handlers.PowerPress = function()
UIManager:scheduleIn(2, UIManager.poweroff_action) UIManager:scheduleIn(2, UIManager.poweroff_action)
@ -209,7 +211,11 @@ function SonyPRSTUX:setEventHandlers(UIManager)
UIManager:unschedule(UIManager.poweroff_action) UIManager:unschedule(UIManager.poweroff_action)
-- resume if we were suspended -- resume if we were suspended
if self.screen_saver_mode then if self.screen_saver_mode then
UIManager.event_handlers.Resume() if self.screen_saver_lock then
UIManager.event_handlers.Suspend()
else
UIManager.event_handlers.Resume()
end
else else
UIManager.event_handlers.Suspend() UIManager.event_handlers.Suspend()
end end
@ -222,22 +228,15 @@ function SonyPRSTUX:setEventHandlers(UIManager)
self:_afterNotCharging() self:_afterNotCharging()
end end
UIManager.event_handlers.UsbPlugIn = function() UIManager.event_handlers.UsbPlugIn = function()
if self.screen_saver_mode then if self.screen_saver_mode and not self.screen_saver_lock then
self:resume() self:resume()
self:outofScreenSaver() self:outofScreenSaver()
self:_afterResume()
end end
self:usbPlugIn() self:usbPlugIn()
end end
UIManager.event_handlers.UsbPlugOut = function() UIManager.event_handlers.UsbPlugOut = function()
self:usbPlugOut() self:usbPlugOut()
end end
UIManager.event_handlers.__default__ = function(input_event)
-- Same as in Kobo: we want to ignore keys during suspension
if not self.screen_saver_mode then
UIManager:sendEvent(input_event)
end
end
end end
-- For Sony PRS-T2 -- For Sony PRS-T2

@ -28,4 +28,16 @@ function SonyPRSTUX_PowerD:isChargingHW()
return self:read_str_file(self.status_file) == "Charging" return self:read_str_file(self.status_file) == "Charging"
end end
function SonyPRSTUX_PowerD:beforeSuspend()
-- Inhibit user input and emit the Suspend event.
self.device:_beforeSuspend()
end
function SonyPRSTUX_PowerD:afterResume()
self:invalidateCapacityCache()
-- Restore user input and emit the Resume event.
self.device:_afterResume()
end
return SonyPRSTUX_PowerD return SonyPRSTUX_PowerD

@ -102,7 +102,8 @@ function UIManager:init()
end) end)
end end
Device:_setEventHandlers(self) -- Tell Device that we're now available, so that it can setup PM event handlers
Device:_UIManagerReady(self)
-- A simple wrapper for UIManager:quit() -- A simple wrapper for UIManager:quit()
-- This may be overwritten by setRunForeverMode(); for testing purposes -- This may be overwritten by setRunForeverMode(); for testing purposes
@ -1548,9 +1549,6 @@ This is the main loop of the UI controller.
It is intended to manage input events and delegate them to dialogs. It is intended to manage input events and delegate them to dialogs.
--]] --]]
function UIManager:run() function UIManager:run()
-- Tell PowerD that we're ready
Device:getPowerDevice():readyUI()
self:initLooper() self:initLooper()
-- currently there is no Turbo support for Windows -- currently there is no Turbo support for Windows
-- use our own main loop -- use our own main loop

@ -7,17 +7,24 @@ describe("device module", function()
local ffi, C local ffi, C
setup(function() setup(function()
local fb = require("ffi/framebuffer")
mock_fb = { mock_fb = {
new = function() new = function()
return { return {
device = package.loaded.device,
bb = require("ffi/blitbuffer").new(600, 800, 1),
getRawSize = function() return {w = 600, h = 800} end, getRawSize = function() return {w = 600, h = 800} end,
getWidth = function() return 600 end, getWidth = function() return 600 end,
getHeight = function() return 800 end,
getDPI = function() return 72 end, getDPI = function() return 72 end,
setViewport = function() end, setViewport = function() end,
getRotationMode = function() return 0 end, getRotationMode = function() return 0 end,
getScreenMode = function() return "portrait" end, getScreenMode = function() return "portrait" end,
setRotationMode = function() end, setRotationMode = function() end,
scaleByDPI = function(this, dp) return math.ceil(dp * this:getDPI() / 160) end, scaleByDPI = fb.scaleByDPI,
scaleBySize = fb.scaleBySize,
setWindowTitle = function() end,
refreshFull = function() end,
} }
end end
} }
@ -38,8 +45,15 @@ describe("device module", function()
end) end)
after_each(function() after_each(function()
-- Don't let UIManager hang on to a stale Device reference, and vice-versa...
package.unload("device")
package.unload("device/generic/device")
package.unload("device/generic/powerd")
package.unload("ui/uimanager")
package.unload("apps/reader/readerui")
mock_input.open:revert() mock_input.open:revert()
os.getenv:revert() os.getenv:revert()
os.execute:revert()
os.getenv = osgetenv os.getenv = osgetenv
io.open = iopen io.open = iopen
@ -327,24 +341,31 @@ describe("device module", function()
os.getenv.invokes(function(key) os.getenv.invokes(function(key)
if key == "PRODUCT" then if key == "PRODUCT" then
return "trilogy" return "trilogy"
elseif key == "MODEL_NUMBER" then
return "320"
else else
return osgetenv(key) return osgetenv(key)
end end
end) end)
local sample_pdf = "spec/front/unit/data/tall.pdf" -- Bypass frontend/device probeDevice, while making sure that it points to the right implementation
local ReaderUI = require("apps/reader/readerui") local Device = require("device/kobo/device")
local device_to_test = require("device/kobo/device") -- Apparently common isn't setup properly in the testsuite, so we can't have nice things
local Device = require("device") stub(Device, "initNetworkManager")
Device.setEventHandlers = device_to_test.setEventHandlers
local UIManager = require("ui/uimanager")
stub(Device, "suspend") stub(Device, "suspend")
stub(Device.powerd, "beforeSuspend") Device:init()
stub(Device, "isKobo") -- Don't poke the RTC
Device.wakeup_mgr = require("device/wakeupmgr"):new{rtc = require("device/kindle/mockrtc")}
-- Don't poke the fl
Device.powerd.fl = nil
package.loaded.device = Device
Device.isKobo.returns(true) local UIManager = require("ui/uimanager")
-- Generic's onPowerEvent may request a repaint, but we can't do that
stub(UIManager, "forceRePaint")
UIManager:init() UIManager:init()
local sample_pdf = "spec/front/unit/data/tall.pdf"
local ReaderUI = require("apps/reader/readerui")
ReaderUI:doShowReader(sample_pdf) ReaderUI:doShowReader(sample_pdf)
local readerui = ReaderUI._getRunningInstance() local readerui = ReaderUI._getRunningInstance()
stub(readerui, "onFlushSettings") stub(readerui, "onFlushSettings")
@ -352,9 +373,9 @@ describe("device module", function()
UIManager.event_handlers.PowerRelease() UIManager.event_handlers.PowerRelease()
assert.stub(readerui.onFlushSettings).was_called() assert.stub(readerui.onFlushSettings).was_called()
UIManager.forceRePaint:revert()
Device.initNetworkManager:revert()
Device.suspend:revert() Device.suspend:revert()
Device.powerd.beforeSuspend:revert()
Device.isKobo:revert()
readerui.onFlushSettings:revert() readerui.onFlushSettings:revert()
Device.screen_saver_mode = false Device.screen_saver_mode = false
readerui:onClose() readerui:onClose()
@ -374,52 +395,19 @@ describe("device module", function()
end end
end end
local sample_pdf = "spec/front/unit/data/tall.pdf" local Device = require("device/cervantes/device")
local ReaderUI = require("apps/reader/readerui") stub(Device, "initNetworkManager")
local Device = require("device")
local device_to_test = require("device/cervantes/device")
Device.setEventHandlers = device_to_test.setEventHandlers
local UIManager = require("ui/uimanager")
stub(Device, "suspend") stub(Device, "suspend")
stub(Device.powerd, "beforeSuspend") Device:init()
stub(Device, "isCervantes") Device.powerd.fl = nil
package.loaded.device = Device
Device.isCervantes.returns(true) local UIManager = require("ui/uimanager")
stub(UIManager, "forceRePaint")
UIManager:init() UIManager:init()
ReaderUI:doShowReader(sample_pdf)
local readerui = ReaderUI._getRunningInstance()
stub(readerui, "onFlushSettings")
UIManager.event_handlers.PowerPress()
UIManager.event_handlers.PowerRelease()
assert.stub(readerui.onFlushSettings).was_called()
Device.suspend:revert()
Device.powerd.beforeSuspend:revert()
Device.isCervantes:revert()
readerui.onFlushSettings:revert()
Device.screen_saver_mode = false
readerui:onClose()
end)
it("SDL", function()
local sample_pdf = "spec/front/unit/data/tall.pdf" local sample_pdf = "spec/front/unit/data/tall.pdf"
local ReaderUI = require("apps/reader/readerui") local ReaderUI = require("apps/reader/readerui")
local Device = require("device")
local device_to_test = require("device/sdl/device")
Device.setEventHandlers = device_to_test.setEventHandlers
local UIManager = require("ui/uimanager")
stub(Device, "suspend")
stub(Device.powerd, "beforeSuspend")
stub(Device, "isSDL")
Device.isSDL.returns(true)
UIManager:init()
ReaderUI:doShowReader(sample_pdf) ReaderUI:doShowReader(sample_pdf)
local readerui = ReaderUI._getRunningInstance() local readerui = ReaderUI._getRunningInstance()
stub(readerui, "onFlushSettings") stub(readerui, "onFlushSettings")
@ -427,9 +415,9 @@ describe("device module", function()
UIManager.event_handlers.PowerRelease() UIManager.event_handlers.PowerRelease()
assert.stub(readerui.onFlushSettings).was_called() assert.stub(readerui.onFlushSettings).was_called()
UIManager.forceRePaint:revert()
Device.initNetworkManager:revert()
Device.suspend:revert() Device.suspend:revert()
Device.powerd.beforeSuspend:revert()
Device.isSDL:revert()
readerui.onFlushSettings:revert() readerui.onFlushSettings:revert()
Device.screen_saver_mode = false Device.screen_saver_mode = false
readerui:onClose() readerui:onClose()
@ -455,31 +443,55 @@ describe("device module", function()
return iopen(filename, mode) return iopen(filename, mode)
end end
end end
local Device = require("device/remarkable/device")
stub(Device, "initNetworkManager")
stub(Device, "suspend")
Device:init()
Device.powerd.fl = nil
package.loaded.device = Device
local UIManager = require("ui/uimanager")
stub(UIManager, "forceRePaint")
UIManager:init()
local sample_pdf = "spec/front/unit/data/tall.pdf" local sample_pdf = "spec/front/unit/data/tall.pdf"
local ReaderUI = require("apps/reader/readerui") local ReaderUI = require("apps/reader/readerui")
local Device = require("device") ReaderUI:doShowReader(sample_pdf)
local device_to_test = require("device/remarkable/device") local readerui = ReaderUI._getRunningInstance()
Device.setEventHandlers = device_to_test.setEventHandlers stub(readerui, "onFlushSettings")
UIManager.event_handlers.PowerPress()
UIManager.event_handlers.PowerRelease()
assert.stub(readerui.onFlushSettings).was_called()
local UIManager = require("ui/uimanager") UIManager.forceRePaint:revert()
Device.initNetworkManager:revert()
Device.suspend:revert()
readerui.onFlushSettings:revert()
Device.screen_saver_mode = false
readerui:onClose()
end)
it("SDL", function()
local Device = require("device/sdl/device")
stub(Device, "initNetworkManager")
stub(Device, "suspend") stub(Device, "suspend")
stub(Device.powerd, "beforeSuspend") Device:init()
stub(Device, "isRemarkable") package.loaded.device = Device
Device.isRemarkable.returns(true) local UIManager = require("ui/uimanager")
UIManager:init() UIManager:init()
local sample_pdf = "spec/front/unit/data/tall.pdf"
local ReaderUI = require("apps/reader/readerui")
ReaderUI:doShowReader(sample_pdf) ReaderUI:doShowReader(sample_pdf)
local readerui = ReaderUI._getRunningInstance() local readerui = ReaderUI._getRunningInstance()
stub(readerui, "onFlushSettings") stub(readerui, "onFlushSettings")
UIManager.event_handlers.PowerPress() -- UIManager.event_handlers.PowerPress() -- We only fake a Release event on the Emu
UIManager.event_handlers.PowerRelease() UIManager.event_handlers.PowerRelease()
assert.stub(readerui.onFlushSettings).was_called() assert.stub(readerui.onFlushSettings).was_called()
Device.initNetworkManager:revert()
Device.suspend:revert() Device.suspend:revert()
Device.powerd.beforeSuspend:revert()
Device.isRemarkable:revert()
readerui.onFlushSettings:revert() readerui.onFlushSettings:revert()
Device.screen_saver_mode = false Device.screen_saver_mode = false
readerui:onClose() readerui:onClose()

Loading…
Cancel
Save