Skip to content

Instantly share code, notes, and snippets.

@ezdiy

ezdiy/device.lua Secret

Last active August 27, 2020 20:47
Show Gist options
  • Save ezdiy/a1e300bc05160032ea8390781f52abdd to your computer and use it in GitHub Desktop.
Save ezdiy/a1e300bc05160032ea8390781f52abdd to your computer and use it in GitHub Desktop.
-- Put this code into /applications/koreader/frontend/device/pocketbook/device.lua (replace file)
-- If you have older device, you'll also need to:
-- * https://github.com/ezdiy/inkview-compat/releases/download/v3/libinkview-compat.so
-- * Put the .so file into /applications/koreader/libs/libinkview-compat.so
local logger = require("logger")
local ffi = require("ffi")
local inkview = ffi.load("inkview")
local band = require("bit").band
local TimeVal = require("ui/timeval")
local util = require("util")
local Generic = require("device/generic/device")
local function yes() return true end
local function no() return false end
local PocketBook = Generic:new {
model = "PocketBook",
isPocketBook = yes,
isInBackGround = false,
hasOTAUpdates = yes,
hasWifiToggle = yes,
isTouchDevice = yes,
hasKeys = yes,
hasFrontlight = no,
canSuspend = no,
supportsScreensaver = no,
home_dir = "/mnt/ext1",
hasNaturalLightApi = no,
}
local EV_SYN = 0
local EV_KEY = 1
local EV_ABS = 3
local EV_MSC = 4
local SYN_REPORT = 0
local SYN_CONFIG = 1
local SYN_MT_REPORT = 2
local ABS_X = 00
local ABS_Y = 01
local ABS_PRESSURE = 24
local ABS_MT_SLOT = 47
local ABS_MT_TOUCH_MAJOR = 48
local ABS_MT_WIDTH_MAJOR = 50
local ABS_MT_POSITION_X = 53
local ABS_MT_POSITION_Y = 54
local ABS_MT_TRACKING_ID = 57
local ABS_MT_PRESSURE = 58
local EVT_INIT = 21
local EVT_EXIT = 22
local EVT_SHOW = 23
local EVT_REPAINT = 23
local EVT_HIDE = 24
local EVT_KEYDOWN = 25
local EVT_KEYPRESS = 25
local EVT_KEYUP = 26
local EVT_KEYRELEASE = 26
local EVT_KEYREPEAT = 28
local EVT_POINTERUP = 29
local EVT_POINTERDOWN = 30
local EVT_POINTERMOVE = 31
local EVT_ORIENTATION = 32
local EVT_MTSYNC = 39
local EVT_FOREGROUND = 151
local EVT_BACKGROUND = 152
local KEY_POWER = 0x01
local KEY_DELETE = 0x08
local KEY_OK = 0x0a
local KEY_UP = 0x11
local KEY_DOWN = 0x12
local KEY_LEFT = 0x13
local KEY_RIGHT = 0x14
local KEY_MINUS = 0x15
local KEY_PLUS = 0x16
local KEY_MENU = 0x17
local KEY_PREV = 0x18
local KEY_NEXT = 0x19
local KEY_HOME = 0x1a
local KEY_BACK = 0x1b
local KEY_PREV2 = 0x1c
local KEY_NEXT2 = 0x1d
local KEY_COVEROPEN = 0x02
local KEY_COVERCLOSE = 0x03
local CONNECTED = 0xf00
local NET_OK = 0
local PANEL_DISABLED = 0
ffi.cdef [[
typedef int (*iv_handler)(int type, int par1, int par2);
void PrepareForLoop(iv_handler handler);
void ProcessEventLoop();
void ClearOnExit();
void OpenScreen();
void iv_sleepmode(int on);
char *GetSoftwareVersion(void);
char *GetDeviceModel(void);
int GetFrontlightColor(void);
int QueryNetwork();
int NetConnect(const char *name);
int NetDisconnect();
typedef struct iv_mtinfo_s {
int active;
int x;
int y;
int pressure;
int rsv[4];
} iv_mtinfo;
iv_mtinfo *GetTouchInfoI(int i);
void SetPanelType(int type);
int GetBatteryPower();
]]
local compat, compat2 = inkview, inkview
if not pcall(function() _ = inkview.PrepareForLoop end) then
compat = ffi.load("inkview-compat")
compat2 = compat
logger.dbg("Switchin on compat, compat2")
elseif not pcall(function() _ = inkview.GetTouchInfoI end) then
compat2 = ffi.load("inkview-compat")
logger.dbg("Switchin on compat")
end
--------------------------------------------------------------------------
-- FIXME: perhaps this should be made into a plugin and get proper UI at some point
local lastEvent = 0
local suspendDelay
local function adaptiveSuspend()
local settings = G_reader_settings:readSetting("pocketbook_suspend") or {
enabled = true, -- if suspend is enabled at all
win = 1, -- count only events further apart than 1 second
ban = 2, -- initially, ban suspends for 2 seconds.
mul = 1.5, -- ban is multiplied by this each subsequent event
max = 30, -- events that happen further than 30 seconds since last one reset ban back to 1
bat = 60, -- do adaptive suspend only if battery is above this. otherwise do aggressive suspend.
}
if not settings.enabled then
inkview.iv_sleepmode(0)
suspendDelay = 0
return
end
if inkview.GetBatteryPower() < settings.bat then
inkview.iv_sleepmode(1)
suspendDelay = nil
return
end
local t = os.time()
-- too close event, just ignore it
if t < lastEvent + settings.win then
return
end
if t > lastEvent + settings.max then
logger.dbg("adaptive suspend event far into future, reset delay")
-- too far apart, so reset ban delay
suspendDelay = settings.ban
else
-- otherwise widen the delay ("adaptive"), but no further than max
suspendDelay = math.min(suspendDelay * settings.mul, settings.max)
end
lastEvent = t
logger.dbg("adaptive suspend new delay",suspendDelay)
-- disable suspend. will be re-enabled once suspendDelay expires
inkview.iv_sleepmode(0)
end
local eventq
local ts
local function genEmuEvent(t,c,v)
assert(ts, "need ts")
table.insert(eventq, {
type = tonumber(t),
code = tonumber(c),
value = tonumber(v) or v,
time = ts,
})
end
-- this is more or less direct translation of pb_event_handler()
local nTouch = 0
local function translateEvent(t, par1, par2)
if eventq == nil then
return 0
end
logger.dbg("received event",t,par1,par2)
adaptiveSuspend()
ts = TimeVal:now() -- shared to lessen gc spam
if t == EVT_INIT then
logger.dbg("EVT_INIT")
inkview.SetPanelType(PANEL_DISABLED);
inkview.iv_sleepmode(0)
logger.dbg("SetPanelType(PANEL_DISABLED) done")
elseif t == EVT_POINTERDOWN then
nTouch = 1
genEmuEvent(EV_ABS, ABS_MT_TRACKING_ID, 0)
genEmuEvent(EV_ABS, ABS_MT_POSITION_X, par1)
genEmuEvent(EV_ABS, ABS_MT_POSITION_Y, par2)
elseif t == EVT_MTSYNC then
if nTouch > 0 and par2 == 2 then
if ok then
nTouch = 2
for i = 0, 1 do
genEmuEvent(EV_ABS, ABS_MT_SLOT, i);
genEmuEvent(EV_ABS, ABS_MT_TRACKING_ID, i);
local mt = compat2.GetTouchInfoI(i)
genEmuEvent(EV_ABS, ABS_MT_POSITION_X, mt.x)
genEmuEvent(EV_ABS, ABS_MT_POSITION_Y, mt.y)
genEmuEvent(EV_SYN, SYN_REPORT, 0)
end
end
elseif par2 == 0 then
for i = 0, 1 do
genEmuEvent(EV_ABS, ABS_MT_SLOT, i);
genEmuEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
genEmuEvent(EV_SYN, SYN_REPORT, 0)
end
else
genEmuEvent(EV_SYN, SYN_REPORT, 0)
end
elseif t == EVT_POINTERMOVE then
if nTouch == 1 then
genEmuEvent(EV_ABS, ABS_MT_POSITION_X, par1)
genEmuEvent(EV_ABS, ABS_MT_POSITION_Y, par2)
end
elseif t == EVT_POINTERUP then
if nTouch == 1 then
genEmuEvent(EV_ABS, ABS_MT_TRACKING_ID, -1)
end
nTouch = 0
else
genEmuEvent(t, par1, par2)
end
return 1
end
-- FIXME: Base device class shouldn't hardcode where input comes from - that's for the frontend, ie us, to decide.
-- Currently, generics brings forth ffi/input which awkwardly decides on its own under our nodes. This breaks owner
-- hierarchy resulting hacks like this whenever there's more than one driver for a given device.
local lastlog = 0
package.loaded["ffi/input"] = { waitForEvent = function(t)
assert(eventq, "waitForEvent() invoked after device shutdown")
if #eventq > 0 then return table.remove(eventq, 1) end
local expire = TimeVal:new { usec = t } + TimeVal:now()
while expire > TimeVal:now() do
local canlog = false
if lastlog < os.time() then
lastlog = os.time() + 5
canlog = true
end
if canlog then logger.dbg("ProcessEventLoop()") end
compat.ProcessEventLoop()
if canlog or #eventq > 0 then logger.dbg("ProcessEventLoop() end, eventq = ", #eventq) end
if #eventq > 0 then return table.remove(eventq, 1) end
end
if suspendDelay and suspendDelay > 0 and (lastEvent + suspendDelay < os.time()) then
-- suspend ban expired, so re-enable it
logger.dbg("iv_sleepmode(1)")
inkview.iv_sleepmode(1)
end
-- FIXME: This is a workaround for messy gesture detection timers in generic input and gesture module.
-- Really it should just accept nil result and perform exponential back off each time it sees subsequent nil.
-- Currently we're doing error() 1000 times every touch, which revs up the CPU scaling freq needlessly.
if t == 100 then
error("timed out")
end
return nil
end }
function PocketBook:exit()
self.screen:close()
-- no further waitForEvent() should be called by now
eventq = nil
logger.dbg("ClearOnExit()")
compat.ClearOnExit()
end
function PocketBook:init()
if not self:canUseCBB() or not self:canHWInvert() then
local dummy = require("ffi/posix_h")
local C = ffi.C
logger.info("Blacklisting the C BB on this device")
C.setenv("KO_NO_CBB", "true", 1)
G_reader_settings:saveSetting("dev_no_c_blitter", true)
end
logger.dbg("OpenScreen()")
inkview.OpenScreen()
logger.dbg("OpenScreen() done")
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
local pd = require("device/pocketbook/powerd")
function pd:init()
-- powerd is broken trying to touch backlight APIs even if there's no device support
local color = self.device:hasNaturalLight() and inkview.GetFrontlightColor() or 0
self.fl_warmth = color >= 0 and color or 0
end
self.powerd = pd:new{device = self}
logger.dbg("powerd init done")
self.input = require("device/input"):new {
device = self,
event_map = {
[KEY_MENU] = "Menu",
[KEY_PREV] = "LPgBack",
[KEY_NEXT] = "LPgFwd",
[KEY_UP] = "Up",
[KEY_DOWN] = "Down",
[KEY_LEFT] = "Left",
[KEY_RIGHT] = "Right",
[KEY_OK] = "Press",
},
handleMiscEv = function(this, ev)
if ev.code == EVT_HIDE or ev.code == EVT_BACKGROUND then
return "SaveState"
elseif ev.code == EVT_FOREGROUND then
require("ui/uimanager"):setDirty('all', 'partial')
return "Foreground"
end
end,
}
logger.dbg("PrepareForLoop()")
compat.PrepareForLoop(translateEvent)
logger.dbg("PrepareForLoop() done")
eventq = {}
self.input:registerEventAdjustHook(function(_input, ev)
if ev.type == EVT_KEYDOWN or ev.type == EVT_KEYUP then
ev.value = ev.type == EVT_KEYDOWN and 1 or 0
ev.type = EV_KEY -- linux/input.h Key-Event
end
if ev.type == EVT_FOREGROUND or ev.type == EVT_BACKGROUND or ev.type == EVT_HIDE then
ev.code = ev.type
ev.type = EV_MSC
end
if ev.type == EVT_EXIT then
require("ui/uimanager"):broadcastEvent(
require("ui/event"):new("Close"))
end
end)
-- fix rotation for Color Lux device
if PocketBook:getDeviceModel() == "PocketBook Color Lux" then
self.screen.blitbuffer_rotation_mode = self.screen.ORIENTATION_PORTRAIT
self.screen.native_rotation_mode = self.screen.ORIENTATION_PORTRAIT
end
Generic.init(self)
end
function PocketBook:setDateTime(year, month, day, hour, min, sec)
if hour == nil or min == nil then return true end
local su = "/mnt/secure/su"
su = util.pathExists(su) and (su .. " ") or ""
local command
if year and month and day then
command = string.format(su .. "/bin/date -s '%d-%d-%d %d:%d:%d'", year, month, day, hour, min, sec)
else
command = string.format(su .. "/bin/date -s '%d:%d'",hour, min)
end
if os.execute(command) == 0 then
os.execute(su .. '/sbin/hwclock -u -w')
return true
else
return false
end
end
function PocketBook:initNetworkManager(NetworkMgr)
function NetworkMgr:turnOnWifi(complete_callback)
if inkview.NetConnect(nil) ~= NET_OK then
logger.info('NetConnect failed')
end
if complete_callback then
complete_callback()
end
end
function NetworkMgr:turnOffWifi(complete_callback)
inkview.NetDisconnect()
if complete_callback then
complete_callback()
end
end
function NetworkMgr:isWifiOn()
return band(inkview.QueryNetwork(), CONNECTED) ~= 0
end
end
function PocketBook:getSoftwareVersion()
return ffi.string(inkview.GetSoftwareVersion())
end
function PocketBook:getDeviceModel()
return ffi.string(inkview.GetDeviceModel())
end
--------------------------------------------------------------------------
-- PocketBook Mini (515)
local PocketBook515 = PocketBook:new{
model = "PB515",
display_dpi = 200,
isTouchDevice = no,
hasFrontlight = no,
hasDPad = yes,
hasFewKeys = yes,
}
-- PocketBook 606 (606)
local PocketBook606 = PocketBook:new{
model = "PB606",
display_dpi = 212,
isTouchDevice = no,
hasFrontlight = no,
hasDPad = yes,
hasFewKeys = yes,
}
-- PocketBook Basic (611)
local PocketBook611 = PocketBook:new{
model = "PB611",
display_dpi = 167,
isTouchDevice = no,
hasFrontlight = no,
hasDPad = yes,
hasFewKeys = yes,
}
-- PocketBook Basic (613)
local PocketBook613 = PocketBook:new{
model = "PB613B",
display_dpi = 167,
isTouchDevice = no,
hasWifiToggle = no,
hasFrontlight = no,
hasDPad = yes,
hasFewKeys = yes,
}
-- PocketBook Basic 2 / Basic 3 (614/614W)
local PocketBook614W = PocketBook:new{
model = "PB614W",
display_dpi = 167,
isTouchDevice = no,
hasFrontlight = no,
hasDPad = yes,
hasFewKeys = yes,
}
-- PocketBook Basic Lux / 615 Plus (615/615W)
local PocketBook615 = PocketBook:new{
model = "PBBLux",
display_dpi = 212,
isTouchDevice = no,
hasDPad = yes,
hasFewKeys = yes,
}
-- PocketBook Basic Lux 2 (616/616W)
local PocketBook616 = PocketBook:new{
model = "PBBLux2",
display_dpi = 212,
isTouchDevice = no,
hasDPad = yes,
hasFewKeys = yes,
}
-- PocketBook Touch (622)
local PocketBook622 = PocketBook:new{
model = "PBTouch",
display_dpi = 167,
hasFrontlight = no,
}
-- PocketBook Touch Lux (623)
local PocketBook623 = PocketBook:new{
model = "PBTouchLux",
display_dpi = 212,
}
-- PocketBook Basic Touch (624)
local PocketBook624 = PocketBook:new{
model = "PBBasicTouch",
display_dpi = 167,
hasFrontlight = no,
}
-- PocketBook Basic Touch 2 (625)
local PocketBook625 = PocketBook:new{
model = "PBBasicTouch2",
display_dpi = 167,
hasFrontlight = no,
}
-- PocketBook Touch Lux 2 / Touch Lux 3 (626)
local PocketBook626 = PocketBook:new{
model = "PBLux3",
display_dpi = 212,
}
-- PocketBook Touch Lux 4 (627)
local PocketBook627 = PocketBook:new{
model = "PBLux4",
display_dpi = 212,
}
-- PocketBook Touch Lux 5 (628)
local PocketBook628 = PocketBook:new{
model = "PBTouchLux5",
display_dpi = 212,
isAlwaysPortrait = yes,
hasNaturalLight = yes,
}
-- PocketBook Sense / Sense 2 (630)
local PocketBook630 = PocketBook:new{
model = "PBSense",
display_dpi = 212,
}
-- PocketBook Touch HD / Touch HD 2 (631)
local PocketBook631 = PocketBook:new{
model = "PBTouchHD",
display_dpi = 300,
-- see https://github.com/koreader/koreader/pull/6531#issuecomment-676629182
hasNaturalLight = function() return inkview.GetFrontlightColor() >= 0 end,
}
-- PocketBook Touch HD Plus / Touch HD 3 (632)
local PocketBook632 = PocketBook:new{
model = "PBTouchHDPlus",
display_dpi = 300,
isAlwaysPortrait = yes,
hasNaturalLight = yes,
}
-- PocketBook Color (633)
local PocketBook633 = PocketBook:new{
model = "PBColor",
display_dpi = 300,
hasColorScreen = yes,
canUseCBB = no, -- 24bpp
isAlwaysPortrait = yes,
}
-- PocketBook Aqua (640)
local PocketBook640 = PocketBook:new{
model = "PBAqua",
display_dpi = 167,
}
-- PocketBook Aqua 2 (641)
local PocketBook641 = PocketBook:new{
model = "PBAqua2",
display_dpi = 212,
}
-- PocketBook Ultra (650)
local PocketBook650 = PocketBook:new{
model = "PBUltra",
display_dpi = 212,
}
-- PocketBook InkPad 3 (740)
local PocketBook740 = PocketBook:new{
model = "PBInkPad3",
display_dpi = 300,
isAlwaysPortrait = yes,
hasFrontLight = no,
}
-- PocketBook InkPad 3 Pro (740_2)
local PocketBook740_2 = PocketBook:new{
model = "PBInkPad3Pro",
display_dpi = 300,
isAlwaysPortrait = yes,
hasFrontLight = no,
}
-- PocketBook Color Lux (801)
local PocketBookColorLux = PocketBook:new{
model = "PBColorLux",
display_dpi = 125,
hasColorScreen = yes,
has3BytesWideFrameBuffer = yes,
canUseCBB = no, -- 24bpp
}
-- PocketBook InkPad / InkPad 2 (840)
local PocketBook840 = PocketBook:new{
model = "PBInkPad",
display_dpi = 250,
}
-- PocketBook InkPad X (1040)
local PocketBook1040 = PocketBook:new{
model = "PB1040",
display_dpi = 227,
isAlwaysPortrait = yes,
hasNaturalLight = yes,
}
logger.info('SoftwareVersion: ', PocketBook:getSoftwareVersion())
local codename = PocketBook:getDeviceModel()
if codename == "PocketBook 515" then
return PocketBook515
elseif codename == "PB606" or codename == "PocketBook 606" then
return PocketBook606
elseif codename == "PocketBook 611" then
return PocketBook611
elseif codename == "PocketBook 613" then
return PocketBook613
elseif codename == "PocketBook 614" or codename == "PocketBook 614W" then
return PocketBook614W
elseif codename == "PB615" or codename == "PB615W" or
codename == "PocketBook 615" or codename == "PocketBook 615W" then
return PocketBook615
elseif codename == "PB616" or codename == "PB616W" or
codename == "PocketBook 616" or codename == "PocketBook 616W" then
return PocketBook616
elseif codename == "PocketBook 622" then
return PocketBook622
elseif codename == "PocketBook 623" then
return PocketBook623
elseif codename == "PocketBook 624" then
return PocketBook624
elseif codename == "PB625" then
return PocketBook625
elseif codename == "PB626" or codename == "PB626(2)-TL3" or
codename == "PocketBook 626" then
return PocketBook626
elseif codename == "PB627" then
return PocketBook627
elseif codename == "PB628" then
return PocketBook628
elseif codename == "PocketBook 630" then
return PocketBook630
elseif codename == "PB631" or codename == "PocketBook 631" then
return PocketBook631
elseif codename == "PB632" then
return PocketBook632
elseif codename == "PB633" then
return PocketBook633
elseif codename == "PB640" or codename == "PocketBook 640" then
return PocketBook640
elseif codename == "PB641" then
return PocketBook641
elseif codename == "PB650" or codename == "PocketBook 650" then
return PocketBook650
elseif codename == "PB740" then
return PocketBook740
elseif codename == "PB740-2" then
return PocketBook740_2
elseif codename == "PocketBook 840" then
return PocketBook840
elseif codename == "PB1040" then
return PocketBook1040
elseif codename == "PocketBook Color Lux" then
return PocketBookColorLux
else
error("unrecognized PocketBook model " .. codename)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment