diff --git a/frontend/apps/reader/modules/readeractivityindicator.lua b/frontend/apps/reader/modules/readeractivityindicator.lua index f8ccdd3a8..14db8aefc 100644 --- a/frontend/apps/reader/modules/readeractivityindicator.lua +++ b/frontend/apps/reader/modules/readeractivityindicator.lua @@ -7,7 +7,7 @@ local ReaderActivityIndicator = EventListener:new{} function ReaderActivityIndicator:init() local dev_mod = Device.model - if dev_mod == "KindlePaperWhite" or dev_mod == "KindlePaperWhite2" or dev_mod == "KindleTouch" then + if dev_mod == "KindlePaperWhite" or dev_mod == "KindlePaperWhite2" or dev_mod == "KindleVoyage" or dev_mod == "KindleBasic" or dev_mod == "KindleTouch" then require "liblipclua" self.lipc_handle = lipc.init("com.github.koreader.activityindicator") end diff --git a/frontend/device/kindle/device.lua b/frontend/device/kindle/device.lua index 8de7d86b2..af9bfdbd5 100644 --- a/frontend/device/kindle/device.lua +++ b/frontend/device/kindle/device.lua @@ -53,6 +53,21 @@ local KindlePaperWhite2 = Kindle:new{ touch_dev = "/dev/input/event1", } +local KindleBasic = Kindle:new{ + model = "KindleBasic", + isTouchDevice = yes, + -- FIXME! + touch_dev = "/dev/input/event0", +} + +local KindleVoyage = Kindle:new{ + model = "KindleVoyage", + isTouchDevice = yes, + hasFrontlight = yes, + display_dpi = 300, + touch_dev = "/dev/input/event1", +} + function Kindle2:init() self.screen = require("device/screen"):new{device = self} self.input = require("device/input"):new{ @@ -140,8 +155,8 @@ function KindlePaperWhite2:init() self.powerd = require("device/kindle/powerd"):new{ device = self, fl_intensity_file = "/sys/class/backlight/max77696-bl/brightness", - batt_capacity_file = "/sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity", - is_charging_file = "/sys/devices/platform/aplite_charger.0/charging", + batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", + is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", } Kindle.init(self) @@ -149,6 +164,35 @@ function KindlePaperWhite2:init() self.input.open("/dev/input/event1") end +function KindleBasic:init() + self.screen = require("device/screen"):new{device = self} + self.powerd = require("device/kindle/powerd"):new{ + device = self, + batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", + is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", + } + + Kindle.init(self) + + -- FIXME! + self.input.open("/dev/input/event0") +end + +function KindleVoyage:init() + self.screen = require("device/screen"):new{device = self} + self.powerd = require("device/kindle/powerd"):new{ + device = self, + fl_intensity_file = "/sys/class/backlight/max77696-bl/brightness", + batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity", + is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging", + } + + Kindle.init(self) + + self.input.open("/dev/input/event1") + -- TODO: Handle the page turn 'buttons'! (/dev/input/event2) +end + --[[ Test if a kindle device has Special Offers --]] @@ -188,6 +232,8 @@ function KindleTouch:exit() end KindlePaperWhite.exit = KindleTouch.exit KindlePaperWhite2.exit = KindleTouch.exit +KindleBasic.exit = KindleTouch.exit +KindleVoyage.exit = KindleTouch.exit function Kindle3:exit() -- send double menu key press events to trigger screen refresh @@ -223,6 +269,8 @@ local touch_set = Set { "0F", "11", "10", "12" } local pw_set = Set { "24", "1B", "1D", "1F", "1C", "20" } local pw2_set = Set { "D4", "5A", "D5", "D6", "D7", "D8", "F2", "17", "60", "F4", "F9", "62", "61", "5F" } +local kt2_set = Set { "C6" } +local kv_set = Set { "13", "54", "2A", "4F", "52", "53" } if k2_set[kindle_devcode] then return Kindle2 @@ -240,6 +288,10 @@ elseif pw_set[kindle_devcode] then return KindlePaperWhite elseif pw2_set[kindle_devcode] then return KindlePaperWhite2 +elseif kt2_set[kindle_devcode] then + return KindleBasic +elseif kv_set[kindle_devcode] then + return KindleVoyage end error("unknown Kindle model "..kindle_devcode) diff --git a/frontend/device/screen.lua b/frontend/device/screen.lua index d755bb3cc..1f6f40ec8 100644 --- a/frontend/device/screen.lua +++ b/frontend/device/screen.lua @@ -78,13 +78,13 @@ function Screen:setViewport(viewport) self.viewport.w, self.viewport.h) end -function Screen:refresh(refresh_type, waveform_mode, x, y, w, h) +function Screen:refresh(refresh_type, waveform_mode, wait_for_marker, x, y, w, h) if self.viewport and x and y then -- adapt to viewport x = x + self.viewport.x y = y + self.viewport.y end - self.fb:refresh(refresh_type, waveform_mode, x, y, w, h) + self.fb:refresh(refresh_type, waveform_mode, wait_for_marker, x, y, w, h) end function Screen:getSize() diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index f1607ad0d..1a3552fb4 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -6,6 +6,10 @@ local util = require("ffi/util") local DEBUG = require("dbg") local _ = require("gettext") +-- cf. koreader-base/ffi-cdecl/include/mxcfb-kindle.h (UPDATE_MODE_* applies to Kobo, too) +local UPDATE_MODE_PARTIAL = 0x0 +local UPDATE_MODE_FULL = 0x1 + -- NOTE: Those have been confirmed on Kindle devices. Might be completely different on Kobo (except for AUTO)! local WAVEFORM_MODE_INIT = 0x0 -- Screen goes to white (clears) local WAVEFORM_MODE_DU = 0x1 -- Grey->white/grey->black @@ -20,16 +24,25 @@ local WAVEFORM_MODE_DU4 = 0x7 -- Medium fidelity 4 level of gray dire -- Kindle PW2 local WAVEFORM_MODE_REAGL = 0x8 -- Ghost compensation waveform local WAVEFORM_MODE_REAGLD = 0x9 -- Ghost compensation waveform with dithering +-- Kindle Basic/Kindle Voyage +local WAVEFORM_MODE_GL4 = 0xA -- 2-bit from white transition +-- TODO: Use me in night mode on those devices? +local WAVEFORM_MODE_GL16_INV = 0xB -- High fidelity for black transition local WAVEFORM_MODE_AUTO = 0x101 +-- Kobo's headers suck, so invent something to avoid magic numbers... +local WAVEFORM_MODE_KOBO_REGAL = 0x7 + + -- there is only one instance of this local UIManager = { - default_refresh_type = 0, -- 0 for partial refresh, 1 for full refresh + default_refresh_type = UPDATE_MODE_PARTIAL, default_waveform_mode = WAVEFORM_MODE_GC16, -- high fidelity waveform - fast_waveform_mode = WAVEFORM_MODE_A2, + fast_waveform_mode = WAVEFORM_MODE_A2, -- FIXME: Is this OK on Kobo? full_refresh_waveform_mode = WAVEFORM_MODE_GC16, partial_refresh_waveform_mode = WAVEFORM_MODE_GC16, + wait_for_every_marker = false, -- force to repaint all the widget is stack, will be reset to false -- after each ui loop repaint_all = false, @@ -89,6 +102,20 @@ function UIManager:init() if KOBO_LIGHT_ON_START and tonumber(KOBO_LIGHT_ON_START) > -1 then Device:getPowerDevice():setIntensity( math.max( math.min(KOBO_LIGHT_ON_START,100) ,0) ) end + -- Emulate the stock reader's refresh behavior... + self.full_refresh_waveform_mode = WAVEFORM_MODE_GC16 + -- Request REGAL waveform mode on devices that support it (Aura & H2O) + if Device.model == "Kobo_phoenix" or Device.model == "Kobo_dahlia" then + self.partial_refresh_waveform_mode = WAVEFORM_MODE_KOBO_REGAL + -- Since Kobo doesn't have MXCFB_WAIT_FOR_UPDATE_SUBMISSION, enabling this currently has no effect :). + self.wait_for_every_marker = true + else + -- See the note in the Kindle code path later, the stock reader might be using AUTO + self.partial_refresh_waveform_mode = WAVEFORM_MODE_GC16 + self.wait_for_every_marker = false + end + -- Let the driver decide what to do with PARTIAL, regional updates... + self.default_waveform_mode = WAVEFORM_MODE_AUTO elseif Device:isKindle() then self.event_handlers["IntoSS"] = function() self:sendEvent(Event:new("FlushSettings")) @@ -105,24 +132,34 @@ function UIManager:init() Device:usbPlugOut() self:sendEvent(Event:new("NotCharging")) end - -- Emulate the stock reader refresh behavior... + -- Emulate the stock reader's refresh behavior... --[[ - NOTE: For ref, on a Touch (debugPaint is my new best friend): - UI: gc16_fast - Reader: When flash: if to/from img: gc16, else gc16_fast; when non-flash: auto (seems to prefer gl16_fast); Waiting for marker only on flash + NOTE: For ref, on a Touch (debugPaint & a patched strace are your friend!): + UI: flash: GC16_FAST, non-flash: AUTO (prefers GC16_FAST) + Reader: When flash: if to/from img: GC16, else GC16_FAST; when non-flash: AUTO (seems to prefer GL16_FAST); Waiting for marker only on flash On a PW2: - Same as Touch, except reader uses reagl on non-flash, non-flash lasts longer (12 pgs); Always waits for marker + UI: flash: GC16_FAST, non-flash: AUTO (prefers GC16_FAST) + Reader: When flash: if to/from img: GC16, else GC16_FAST; when non-flash: REAGL (w/ UPDATE_MODE_FULL!); Always waits for marker + Note that the bottom status bar region is refreshed separately, right after the screen, as a PARTIAL, AUTO (GC16_FAST) update, and it's this marker that's waited after... + Non flash lasts longer (dual timeout: 14pgs / 7mins). --]] -- We don't really have an easy way to know if we're refreshing the UI, or a page, or if said page contains an image, so go with the highest fidelity option + -- We spend much more time in the reader than the UI, and our UI isn't very graphic anyway, so we'll follow the reader's behaviour above all self.full_refresh_waveform_mode = WAVEFORM_MODE_GC16 - -- We spend much more time in the reader than the UI, and our UI isn't very graphic anyway, so go with the reader behavior - if Device.model == "KindlePaperWhite2" then + -- Request REAGL waveform mode on devices that support it (PW2, KT2, KV) [FIXME: Is that actually true of the KT2?] + if Device.model == "KindlePaperWhite2" or Device.model == "KindleBasic" or Device.model == "KindleVoyage" then self.partial_refresh_waveform_mode = WAVEFORM_MODE_REAGL + -- We need to wait for every update marker when using REAGL waveform modes. That mostly means we always use MXCFB_WAIT_FOR_UPDATE_SUBMISSION. + self.wait_for_every_marker = true else self.partial_refresh_waveform_mode = WAVEFORM_MODE_GL16_FAST - -- NOTE: Or we could go back to what KOReader did before fa55acc in koreader-base, which was also use WAVEFORM_MODE_AUTO ;). I have *no* idea how the driver makes its choice though... + -- NOTE: Or we could go back to what KOReader did before fa55acc in koreader-base, which was also to use AUTO ;). I have *no* idea how the driver makes its choice though... --self.partial_refresh_waveform_mode = WAVEFORM_MODE_AUTO + -- Only wait for update markers on FULL updates + self.wait_for_every_marker = false end + -- Default to GC16_FAST (will be used for PARTIAL, regional updates) + self.default_waveform_mode = WAVEFORM_MODE_GC16_FAST end end @@ -383,14 +420,15 @@ function UIManager:run() local refresh_type = self.default_refresh_type local waveform_mode = self.default_waveform_mode + local wait_for_marker = self.wait_for_every_marker if dirty then if force_partial_refresh or force_fast_refresh then - refresh_type = 0 + refresh_type = UPDATE_MODE_PARTIAL elseif force_full_refresh or self.refresh_count == self.FULL_REFRESH_COUNT - 1 then - refresh_type = 1 + refresh_type = UPDATE_MODE_FULL end -- Handle the waveform mode selection... - if refresh_type == 1 then + if refresh_type == UPDATE_MODE_FULL then waveform_mode = self.full_refresh_waveform_mode else waveform_mode = self.partial_refresh_waveform_mode @@ -398,18 +436,29 @@ function UIManager:run() if force_fast_refresh then waveform_mode = self.fast_waveform_mode end + -- If the device is REAGL-aware, and we're doing a reader refresh (i.e., PARTIAL, but not regional), apply some trickery to match the stock reader's behavior (REAGL updates are always FULL, but there's no black flash) + if not self.update_regions_func and refresh_type == UPDATE_MODE_PARTIAL and (waveform_mode == WAVEFORM_MODE_REAGL or waveform_mode == WAVEFORM_MODE_KOBO_REGAL) then + refresh_type = UPDATE_MODE_FULL + end + -- If we truly asked for a PARTIAL, regional update, it's likely for an UI element, so fall back to the default waveform mode, which is tailored per-device to hopefully be more appropriate than the one we use in the reader + if self.update_regions_func and refresh_type == UPDATE_MODE_PARTIAL and not force_fast_refresh then + -- NOTE: Using default_waveform_mode might seem counter-intuitive when we have partial_refresh_waveform_mode, but partial_refresh_waveform_mode is mostly there as a means to flag REAGL-aware devices ;). + -- Here, we're actually interested in handling regional updates (which happen to be PARTIAL by definition), and not 'PARTIAL' updates that actually refresh the whole screen. + waveform_mode = self.default_waveform_mode + end if self.update_regions_func then local update_regions = self.update_regions_func() for _, update_region in ipairs(update_regions) do -- in some rare cases update region has 1 pixel offset - Screen:refresh(refresh_type, waveform_mode, + Screen:refresh(refresh_type, waveform_mode, wait_for_marker, update_region.x-1, update_region.y-1, update_region.w+2, update_region.h+2) end else - Screen:refresh(refresh_type, waveform_mode) + Screen:refresh(refresh_type, waveform_mode, wait_for_marker) end - if self.refresh_type == 1 then + -- REAGL refreshes are always FULL (but without a black flash), but we want to keep our black flash timeout working, so don't reset the counter on FULL REAGL refreshes... + if refresh_type == UPDATE_MODE_FULL and waveform_mode ~= WAVEFORM_MODE_REAGL and waveform_mode ~= WAVEFORM_MODE_KOBO_REGAL then self.refresh_count = 0 elseif not force_partial_refresh and not force_full_refresh then self.refresh_count = (self.refresh_count + 1)%self.FULL_REFRESH_COUNT