mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
* Kindle: Implement a NetworkMgr backend loosely based on WpaClient in order to allow feature-parity with hasWifiManager platforms. This involves dealing with the native wifid over lipc (the native IPC system, based on DBus), through custom Lua bindings (https://github.com/notmarek/openlipclua), since the stock ones lack support for the needed hasharray data type. * NetworkMgr: Clear up leftover hallucinations from #10669, making `enableWifi` much simpler (and much more similar to `turnOnWifiAndWaitForConnection`). * NetworkMgr: Made it clearer that `turnOnWifi` implementations *must* deal with `complete_callback`, as part of the aforementioned changes mean that it's *always* wrapped in a connectivity check, and we need that for proper event signaling. * Android, Emu: Run `complete_callback` properly in `turnOnWifi`. * Kindle: Support `powerd:isCharged()` on the PW2 (yes, this is random, it just happened to be my test device :D). * NetworkMgr:disableWifi: Properly tear down any potential ongoing connection attempt (e.g., connectivity check). * NetworkMgr:promptWifi: Make the "wifi enabled but not connected" popup clearer if there's an ongoing connection attempt, and gray out the "Connect" button in this case (as it would only lead to another "connection already in progress" popup anyway). * NetworkMgr:reconnectOrShowNetworkMenu: Make *total* scanning failures fatal (they will lead to an immediate wifi teardown). * NetworkMgr:reconnectOrShowNetworkMenu: Clear up the long-press behavior (which *always* shows the network list popup) so that it doesn't weirdly break all the things (technical term!). * NetworkMgr:reconnectOrShowNetworkMenu: When we manage to connect to a preferred network on our own *before* showing the network list, make sure it is flagged as "connected" in said list. * NetworkMgr:reconnectOrShowNetworkMenu: Make connection failures fatal in non-interactive workflows (they'll lead to a wifi teardown). * NetworkSetting (the aforementioned network list widget): Clear NetworkMgr's "connection pending" flag on dismiss when nothing else will (i.e., when there's no connectivity check ticking).
1747 lines
66 KiB
Lua
1747 lines
66 KiB
Lua
local Generic = require("device/generic/device")
|
|
local UIManager
|
|
local time = require("ui/time")
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
local logger = require("logger")
|
|
|
|
-- We're going to need a few <linux/fb.h> & <linux/input.h> constants...
|
|
local ffi = require("ffi")
|
|
local C = ffi.C
|
|
require("ffi/linux_fb_h")
|
|
require("ffi/linux_input_h")
|
|
require("ffi/posix_h")
|
|
require("ffi/fbink_input_h")
|
|
|
|
local function yes() return true end
|
|
local function no() return false end -- luacheck: ignore
|
|
|
|
local function kindleGetSavedNetworks()
|
|
local haslipc, lipc = pcall(require, "libopenlipclua") -- use our lua lipc library with access to hasharray properties
|
|
local lipc_handle
|
|
if haslipc then
|
|
lipc_handle = lipc.open_no_name()
|
|
end
|
|
if lipc_handle then
|
|
local ha_input = lipc_handle:new_hasharray() -- an empty hash array since we only want to read
|
|
local ha_result = lipc_handle:access_hash_property("com.lab126.wifid", "profileData", ha_input)
|
|
local profiles = ha_result:to_table()
|
|
ha_result:destroy()
|
|
ha_input:destroy()
|
|
lipc_handle:close()
|
|
return profiles
|
|
end
|
|
end
|
|
|
|
local function kindleGetCurrentProfile()
|
|
local haslipc, lipc = pcall(require, "libopenlipclua") -- use our lua lipc library with access to hasharray properties
|
|
local lipc_handle
|
|
if haslipc then
|
|
lipc_handle = lipc.open_no_name()
|
|
end
|
|
if lipc_handle then
|
|
local ha_input = lipc_handle:new_hasharray() -- an empty hash array since we only want to read
|
|
local ha_result = lipc_handle:access_hash_property("com.lab126.wifid", "currentEssid", ha_input)
|
|
local profile = ha_result:to_table()[1] -- theres only a single element
|
|
ha_input:destroy()
|
|
ha_result:destroy()
|
|
lipc_handle:close()
|
|
return profile
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
local function kindleAuthenticateNetwork(essid)
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
local lipc_handle
|
|
if haslipc then
|
|
lipc_handle = lipc.init("com.github.koreader.networkmgr")
|
|
end
|
|
if lipc_handle then
|
|
lipc_handle:set_string_property("com.lab126.cmd", "ensureConnection", "wifi:" .. essid)
|
|
lipc_handle:close()
|
|
end
|
|
end
|
|
|
|
local function kindleSaveNetwork(data)
|
|
local haslipc, lipc = pcall(require, "libopenlipclua") -- use our lua lipc library with access to hasharray properties
|
|
local lipc_handle
|
|
if haslipc then
|
|
lipc_handle = lipc.open_no_name()
|
|
end
|
|
if lipc_handle then
|
|
local profile = lipc_handle:new_hasharray()
|
|
profile:add_hash()
|
|
profile:put_string(0, "essid", data.ssid)
|
|
if string.find(data.flags, "WPA") then
|
|
profile:put_string(0, "secured", "yes")
|
|
profile:put_string(0, "psk", data.password)
|
|
profile:put_int(0, "store_nw_user_pref", 0) -- tells amazon we don't want them to have our password
|
|
else
|
|
profile:put_string(0, "secured", "no")
|
|
end
|
|
lipc_handle:access_hash_property("com.lab126.wifid", "createProfile", profile):destroy() -- destroy the returned empty ha
|
|
profile:destroy()
|
|
lipc_handle:close()
|
|
end
|
|
end
|
|
|
|
local function kindleGetScanList()
|
|
local _ = require("gettext")
|
|
local haslipc, lipc = pcall(require, "libopenlipclua") -- use our lua lipc library with access to hasharray properties
|
|
local lipc_handle
|
|
if haslipc then
|
|
lipc_handle = lipc.open_no_name()
|
|
end
|
|
if lipc_handle then
|
|
if lipc_handle:get_string_property("com.lab126.wifid", "cmState") ~= "CONNECTED" then
|
|
local ha_input = lipc_handle:new_hasharray()
|
|
local ha_results = lipc_handle:access_hash_property("com.lab126.wifid", "scanList", ha_input)
|
|
if ha_results == nil then
|
|
-- Shouldn't really happen, access_hash_property will throw if LipcAccessHasharrayProperty failed
|
|
ha_input:destroy()
|
|
lipc_handle:close()
|
|
-- NetworkMgr will ask for a re-scan on seeing an empty table, the second attempt *should* work ;).
|
|
return {}, nil
|
|
end
|
|
local scan_result = ha_results:to_table()
|
|
ha_results:destroy()
|
|
ha_input:destroy()
|
|
lipc_handle:close()
|
|
if not scan_result then
|
|
-- e.g., to_table hit lha->ha == NULL
|
|
return {}, nil
|
|
else
|
|
return scan_result, nil
|
|
end
|
|
end
|
|
lipc_handle:close()
|
|
-- return a fake scan list containing only the currently connected profile :)
|
|
local profile = kindleGetCurrentProfile()
|
|
return { profile }, nil
|
|
else
|
|
logger.dbg("kindleGetScanList: Failed to acquire an anonymous lipc handle")
|
|
return nil, _("Unable to communicate with the Wi-Fi backend")
|
|
end
|
|
end
|
|
|
|
local function kindleScanThenGetResults()
|
|
local _ = require("gettext")
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
local lipc_handle
|
|
if haslipc then
|
|
lipc_handle = lipc.init("com.github.koreader.networkmgr")
|
|
end
|
|
if not lipc_handle then
|
|
logger.dbg("kindleScanThenGetResults: Failed to acquire a lipc handle for NetworkMgr")
|
|
return nil, _("Unable to communicate with the Wi-Fi backend")
|
|
end
|
|
|
|
lipc_handle:set_string_property("com.lab126.wifid", "scan", "") -- trigger a scan
|
|
|
|
-- Mimic WpaClient:scanThenGetResults: block while waiting for the scan to finish.
|
|
-- Ideally, we'd do this via a poll/event workflow, but, eh', this is going to be good enough for now ;p.
|
|
-- For future reference, see `lipc-wait-event -m -s 0 -t com.lab126.wifid '*'`
|
|
--[[
|
|
-- For a connection:
|
|
[00:00:04.675699] cmStateChange "PENDING"
|
|
[00:00:04.677402] scanning
|
|
[00:00:05.488043] scanComplete
|
|
[00:00:05.973188] cmConnected
|
|
[00:00:05.977862] cmStateChange "CONNECTED"
|
|
[00:00:05.980698] signalStrength "1/5"
|
|
[00:00:06.417549] cmConnected
|
|
|
|
-- And a disconnection:
|
|
[00:01:34.094652] cmDisconnected
|
|
[00:01:34.096088] cmStateChange "NA"
|
|
[00:01:34.219802] signalStrength "0/5"
|
|
[00:01:34.221802] cmStateChange "READY"
|
|
[00:01:35.656375] cmIntfNotAvailable
|
|
[00:01:35.658710] cmStateChange "NA"
|
|
--]]
|
|
local done_scanning = false
|
|
local wait_cnt = 80 -- 20s in chunks on 250ms
|
|
while wait_cnt > 0 do
|
|
local scan_state = lipc_handle:get_string_property("com.lab126.wifid", "scanState")
|
|
|
|
if scan_state == "idle" then
|
|
done_scanning = true
|
|
logger.dbg("kindleScanThenGetResults: Wi-Fi scan took", (80 - wait_cnt) * 0.25, "seconds")
|
|
break
|
|
end
|
|
|
|
-- Whether it's still "scanning" or in whatever other state we don't know about,
|
|
-- try again until it says it's done.
|
|
wait_cnt = wait_cnt - 1
|
|
C.usleep(250 * 1000)
|
|
end
|
|
lipc_handle:close()
|
|
|
|
if done_scanning then
|
|
return kindleGetScanList()
|
|
else
|
|
logger.warn("kindleScanThenGetResults: Timed-out scanning for Wi-Fi networks")
|
|
return nil, _("Scanning for Wi-Fi networks timed out")
|
|
end
|
|
end
|
|
|
|
local function kindleEnableWifi(toggle)
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
local lipc_handle
|
|
if haslipc then
|
|
lipc_handle = lipc.init("com.github.koreader.networkmgr")
|
|
end
|
|
if lipc_handle then
|
|
-- Be extremely thorough... c.f., #6019
|
|
-- NOTE: I *assume* this'll also ensure we prefer Wi-Fi over 3G/4G, which is a plus in my book...
|
|
if toggle == 1 then
|
|
lipc_handle:set_int_property("com.lab126.cmd", "wirelessEnable", 1)
|
|
lipc_handle:set_int_property("com.lab126.wifid", "enable", 1)
|
|
else
|
|
lipc_handle:set_int_property("com.lab126.wifid", "enable", 0)
|
|
lipc_handle:set_int_property("com.lab126.cmd", "wirelessEnable", 0)
|
|
end
|
|
lipc_handle:close()
|
|
else
|
|
-- No liblipclua on FW < 5.x ;)
|
|
-- Always kill 3G first...
|
|
os.execute("lipc-set-prop -i com.lab126.wan enable 0")
|
|
os.execute("lipc-set-prop -i com.lab126.wifid enable " .. toggle)
|
|
end
|
|
end
|
|
|
|
-- Check if wifid thinks that the WiFi is enabled
|
|
--[[
|
|
local function isWifiUp()
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
local lipc_handle
|
|
if haslipc then
|
|
lipc_handle = lipc.init("com.github.koreader.networkmgr")
|
|
end
|
|
if lipc_handle then
|
|
local status = lipc_handle:get_int_property("com.lab126.wifid", "enable") or 0
|
|
lipc_handle:close()
|
|
|
|
return status == 1
|
|
else
|
|
local std_out = io.popen("lipc-get-prop -i com.lab126.wifid enable", "r")
|
|
if std_out then
|
|
local result = std_out:read("*number")
|
|
std_out:close()
|
|
|
|
if not result then
|
|
return false
|
|
end
|
|
|
|
return result == 1
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
--]]
|
|
|
|
--[[
|
|
Test if a kindle device is flagged as a Special Offers device (i.e., ad supported) (FW >= 5.x)
|
|
--]]
|
|
local function isSpecialOffers()
|
|
-- Look at the current blanket modules to see if the SO screensavers are enabled...
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
if not haslipc then
|
|
logger.warn("could not load liblipclua:", lipc)
|
|
return true
|
|
end
|
|
local lipc_handle = lipc.init("com.github.koreader.device")
|
|
if not lipc_handle then
|
|
logger.warn("could not get lipc handle")
|
|
return true
|
|
end
|
|
local is_so
|
|
local loaded_blanket_modules = lipc_handle:get_string_property("com.lab126.blanket", "load")
|
|
if not loaded_blanket_modules then
|
|
logger.warn("could not get lipc property")
|
|
return true
|
|
end
|
|
if string.find(loaded_blanket_modules, "ad_screensaver") then
|
|
is_so = true
|
|
else
|
|
is_so = false
|
|
end
|
|
lipc_handle:close()
|
|
return is_so
|
|
end
|
|
|
|
--[[
|
|
Test if a kindle device has *received* Special Offers (FW < 5.x)
|
|
--]]
|
|
local function hasSpecialOffers()
|
|
if lfs.attributes("/mnt/us/system/.assets", "mode") == "directory" then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function frameworkStopped()
|
|
if os.getenv("STOP_FRAMEWORK") == "yes" then
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
if not haslipc then
|
|
logger.warn("could not load liblibclua")
|
|
return
|
|
end
|
|
local lipc_handle = lipc.init("com.lab126.kaf")
|
|
if not lipc_handle then
|
|
logger.warn("could not get lipc handle")
|
|
return
|
|
end
|
|
local frameworkStarted = lipc_handle:register_int_property("frameworkStarted", "r")
|
|
frameworkStarted.value = 1
|
|
lipc_handle:set_string_property("com.lab126.blanket", "unload", "splash")
|
|
lipc_handle:set_string_property("com.lab126.blanket", "unload", "screensaver")
|
|
return lipc_handle
|
|
end
|
|
end
|
|
|
|
local Kindle = Generic:extend{
|
|
model = "Kindle",
|
|
isKindle = yes,
|
|
-- NOTE: We can cheat by adding a platform-specific entry here, because the only code that will check for this is here.
|
|
isSpecialOffers = isSpecialOffers(),
|
|
hasOTAUpdates = yes,
|
|
hasFastWifiStatusQuery = yes,
|
|
hasWifiRestore = yes,
|
|
-- NOTE: HW inversion is generally safe on mxcfb Kindles
|
|
canHWInvert = yes,
|
|
-- NOTE: And the fb driver is generally sane on those, too
|
|
canModifyFBInfo = yes,
|
|
-- NOTE: Newer devices will turn the frontlight off at 0
|
|
canTurnFrontlightOff = yes,
|
|
-- NOTE: Via powerd.toggleSuspend
|
|
canSuspend = yes,
|
|
home_dir = "/mnt/us",
|
|
-- New devices are REAGL-aware, default to REAGL
|
|
isREAGL = yes,
|
|
-- Rex & Zelda devices sport an updated driver.
|
|
isZelda = no,
|
|
isRex = no,
|
|
-- So do devices running on a MediaTek SoC
|
|
isMTK = no,
|
|
-- But of course, some devices don't actually support all the features the kernel exposes...
|
|
isNightModeChallenged = no,
|
|
-- NOTE: While this ought to behave on Zelda/Rex, turns out, nope, it really doesn't work on *any* of 'em :/ (c.f., ko#5884).
|
|
canHWDither = no,
|
|
-- Device has an Ambient Light Sensor
|
|
hasLightSensor = no,
|
|
-- The time the device went into suspend
|
|
suspend_time = 0,
|
|
framework_lipc_handle = frameworkStopped(),
|
|
}
|
|
|
|
function Kindle:initNetworkManager(NetworkMgr)
|
|
local haslipc, _ = pcall(require, "liblipclua")
|
|
if haslipc then
|
|
function NetworkMgr:turnOnWifi(complete_callback, interactive)
|
|
kindleEnableWifi(1)
|
|
return self:reconnectOrShowNetworkMenu(complete_callback, interactive)
|
|
end
|
|
else
|
|
-- If we can't use the lipc Lua bindings, we can't support any kind of interactive Wi-Fi UI...
|
|
function NetworkMgr:turnOnWifi(complete_callback, interactive)
|
|
kindleEnableWifi(1)
|
|
if complete_callback then
|
|
complete_callback()
|
|
end
|
|
end
|
|
end
|
|
|
|
function NetworkMgr:turnOffWifi(complete_callback)
|
|
kindleEnableWifi(0)
|
|
-- NOTE: Same here, except disconnect is simpler, so a dumb delay will do...
|
|
if complete_callback then
|
|
UIManager:scheduleIn(2, complete_callback)
|
|
end
|
|
end
|
|
|
|
function NetworkMgr:getNetworkInterfaceName()
|
|
return "wlan0" -- so far, all Kindles appear to use wlan0
|
|
end
|
|
|
|
function NetworkMgr:restoreWifiAsync()
|
|
kindleEnableWifi(1)
|
|
end
|
|
|
|
function NetworkMgr:authenticateNetwork(network)
|
|
kindleAuthenticateNetwork(network.ssid)
|
|
return true, nil
|
|
end
|
|
|
|
-- NOTE: We don't have a disconnectNetwork & releaseIP implementation,
|
|
-- which means the "disconnect" button in NetworkSetting kind of does nothing ;p.
|
|
|
|
function NetworkMgr:saveNetwork(setting)
|
|
kindleSaveNetwork(setting)
|
|
end
|
|
|
|
function NetworkMgr:getNetworkList()
|
|
local scan_list, err = kindleScanThenGetResults()
|
|
if not scan_list then
|
|
return nil, err
|
|
end
|
|
|
|
-- trick ui/widget/networksetting into displaying the correct signal strength icon
|
|
local qualities = {
|
|
[1] = 0,
|
|
[2] = 6,
|
|
[3] = 31,
|
|
[4] = 56,
|
|
[5] = 81
|
|
}
|
|
|
|
local network_list = {}
|
|
local saved_profiles = kindleGetSavedNetworks()
|
|
local current_profile = kindleGetCurrentProfile()
|
|
for _, network in ipairs(scan_list) do
|
|
local password = nil
|
|
if network.known == "yes" then
|
|
for _, p in ipairs(saved_profiles) do
|
|
-- Earlier FW do not have a netid field at all, fall back to essid as that's the best we'll get (we don't get bssid either)...
|
|
if (p.netid and p.netid == network.netid) or (p.netid == nil and p.essid == network.essid) then
|
|
password = p.psk
|
|
break
|
|
end
|
|
end
|
|
end
|
|
table.insert(network_list, {
|
|
-- signal_level is purely for fun, the widget doesn't do anything with it. The WpaClient backend stores the raw dBa attenuation in it.
|
|
signal_level = string.format("%d/%d", network.signal, network.signal_max),
|
|
signal_quality = qualities[network.signal],
|
|
-- See comment above about netid being unfortunately optional...
|
|
connected = (current_profile.netid and current_profile.netid ~= -1 and current_profile.netid == network.netid)
|
|
or (current_profile.netid == nil and current_profile.essid ~= "" and current_profile.essid == network.essid),
|
|
flags = network.key_mgmt,
|
|
ssid = network.essid ~= "" and network.essid,
|
|
password = password,
|
|
})
|
|
end
|
|
return network_list, nil
|
|
end
|
|
|
|
function NetworkMgr:getCurrentNetwork()
|
|
return { ssid = kindleGetCurrentProfile().essid }
|
|
end
|
|
|
|
NetworkMgr.isWifiOn = NetworkMgr.sysfsWifiOn
|
|
NetworkMgr.isConnected = NetworkMgr.ifHasAnAddress
|
|
end
|
|
|
|
function Kindle:supportsScreensaver()
|
|
if self.isSpecialOffers then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
function Kindle:openInputDevices()
|
|
-- Auto-detect input devices (via FBInk's fbink_input_scan)
|
|
local ok, FBInkInput = pcall(ffi.load, "fbink_input")
|
|
if not ok then
|
|
-- NOP fallback for the testsuite...
|
|
FBInkInput = { fbink_input_scan = function() end }
|
|
end
|
|
local dev_count = ffi.new("size_t[1]")
|
|
-- We care about: the touchscreen, a properly scaled stylus, pagination buttons, a home button and a fiveway.
|
|
local match_mask = bit.bor(C.INPUT_TOUCHSCREEN, C.INPUT_SCALED_TABLET, C.INPUT_PAGINATION_BUTTONS, C.INPUT_HOME_BUTTON, C.INPUT_DPAD)
|
|
local devices = FBInkInput.fbink_input_scan(match_mask, 0, 0, dev_count)
|
|
if devices ~= nil then
|
|
for i = 0, tonumber(dev_count[0]) - 1 do
|
|
local dev = devices[i]
|
|
if dev.matched then
|
|
self.input.fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
|
|
end
|
|
end
|
|
C.free(devices)
|
|
else
|
|
-- Auto-detection failed, warn and fall back to defaults
|
|
logger.warn("We failed to auto-detect the proper input devices, input handling may be inconsistent!")
|
|
if self.touch_dev then
|
|
-- We've got a preferred path specified for the touch panel
|
|
self.input.open(self.touch_dev)
|
|
else
|
|
-- That generally works out well enough on legacy devices...
|
|
self.input.open("/dev/input/event0")
|
|
self.input.open("/dev/input/event1")
|
|
end
|
|
end
|
|
|
|
-- Getting the device where rotation events end up without catching a bunch of false-positives is... trickier,
|
|
-- thanks to the inane event code being used...
|
|
if self:hasGSensor() then
|
|
-- i.e., we want something that reports EV_ABS:ABS_PRESSURE that isn't *also* a pen (because those are pretty much guaranteed to report pressure...).
|
|
-- And let's add that isn't also a touchscreen to the mix, because while not true at time of writing, that's an event touchscreens sure can support...
|
|
devices = FBInkInput.fbink_input_scan(C.INPUT_ROTATION_EVENT, bit.bor(C.INPUT_TABLET, C.INPUT_TOUCHSCREEN), C.NO_RECAP, dev_count)
|
|
if devices ~= nil then
|
|
for i = 0, tonumber(dev_count[0]) - 1 do
|
|
local dev = devices[i]
|
|
if dev.matched then
|
|
self.input.fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
|
|
end
|
|
end
|
|
C.free(devices)
|
|
end
|
|
end
|
|
|
|
self.input.open("fake_events")
|
|
end
|
|
|
|
function Kindle:init()
|
|
-- Check if the device supports deep sleep/quick boot
|
|
if lfs.attributes("/sys/devices/platform/falconblk/uevent", "mode") == "file" then
|
|
-- Now, poke the appreg db to see if it's actually *enabled*...
|
|
-- NOTE: The setting is only available on registered devices, as such, it *can* be missing,
|
|
-- which is why we check for it existing and being *disabled*, as that ensures user interaction.
|
|
local SQ3 = require("lua-ljsqlite3/init")
|
|
local appreg = SQ3.open("/var/local/appreg.db", "ro")
|
|
local hibernation_disabled = tonumber(appreg:rowexec(
|
|
"SELECT EXISTS(SELECT value FROM properties WHERE handlerId = 'dcc' AND name = 'hibernate.enabled' AND value = 0);"
|
|
))
|
|
-- Check the actual delay while we're there...
|
|
local hibernation_delay =
|
|
appreg:rowexec("SELECT value FROM properties WHERE handlerId = 'dcc' AND name = 'hibernate.s2h.rtc.secs'") or
|
|
appreg:rowexec("SELECT value FROM properties WHERE handlerId = 'dcd' AND name = 'hibernate.s2h.rtc.secs'") or
|
|
3600
|
|
appreg:close()
|
|
if hibernation_disabled == 1 then
|
|
self.canDeepSleep = false
|
|
else
|
|
self.canDeepSleep = true
|
|
self.hibernationDelay = tonumber(hibernation_delay)
|
|
logger.dbg("Kindle: Device supports hibernation, enters hibernation after", self.hibernationDelay, "seconds in suspend")
|
|
end
|
|
else
|
|
self.canDeepSleep = false
|
|
end
|
|
|
|
-- If the device-specific init hasn't done so already (devices without keys don't), instantiate Input.
|
|
if not self.input then
|
|
self.input = require("device/input"):new{ device = self }
|
|
end
|
|
|
|
-- Auto-detect & open input devices
|
|
self:openInputDevices()
|
|
|
|
Generic.init(self)
|
|
end
|
|
|
|
function Kindle:setDateTime(year, month, day, hour, min, sec)
|
|
if hour == nil or min == nil then return true end
|
|
|
|
-- Prefer using the setdate wrapper if possible, as it will poke the native UI, too.
|
|
if lfs.attributes("/usr/sbin/setdate", "mode") == "file" then
|
|
local t = os.date("*t") -- Start with now to make sure we have a full table
|
|
t.year = year or t.year
|
|
t.month = month or t.month
|
|
t.day = day or t.day
|
|
t.hour = hour
|
|
t.min = min
|
|
t.sec = sec or t.sec
|
|
local epoch = os.time(t)
|
|
|
|
local command = string.format("/usr/sbin/setdate '%d'", epoch)
|
|
return os.execute(command) == 0
|
|
else
|
|
local command
|
|
if year and month and day then
|
|
command = string.format("date -s '%d-%d-%d %d:%d:%d' '+%%Y-%%m-%%d %%H:%%M:%%S'", year, month, day, hour, min, sec)
|
|
else
|
|
command = string.format("date -s '%d:%d' '+%%H:%%M'", hour, min)
|
|
end
|
|
if os.execute(command) == 0 then
|
|
os.execute("hwclock -u -w")
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
function Kindle:usbPlugIn()
|
|
-- NOTE: We cannot support running in USBMS mode (we cannot, we live on the partition being exported!).
|
|
-- But since that's the default state of the Kindle system, we have to try to make nice...
|
|
-- To that end, we're currently SIGSTOPping volumd to inhibit the system's USBMS mode handling.
|
|
-- It's not perfect (e.g., if the system is setup for USBMS and not USBNet,
|
|
-- the frontlight will be turned off when plugged in), but it at least prevents users from completely
|
|
-- shooting themselves in the foot (c.f., https://github.com/koreader/koreader/issues/3220)!
|
|
-- On the upside, we don't have to bother waking up the WM to show us the USBMS screen :D.
|
|
-- NOTE: If the device is put in USBNet mode before we even start, everything's peachy, though :).
|
|
end
|
|
|
|
-- Hopefully, the event sources are fairly portable...
|
|
-- c.f., https://github.com/koreader/koreader/pull/11174#issuecomment-1830064445
|
|
-- NOTE: There's no distinction between real button presses and powerd_test -p or lipc-set-prop -i com.lab126.powerd powerButton 1
|
|
local POWERD_EVENT_SOURCES = {
|
|
[1] = "BUTTON_WAKEUP", -- outOfScreenSaver 1
|
|
[2] = "BUTTON_SUSPEND", -- goingToScreenSaver 2
|
|
[4] = "HALL_SUSPEND", -- goingToScreenSaver 4
|
|
[6] = "HALL_WAKEUP", -- outOfScreenSaver 6
|
|
}
|
|
|
|
function Kindle:intoScreenSaver(source)
|
|
logger.dbg("Kindle:intoScreenSaver via", POWERD_EVENT_SOURCES[source] or string.format("UNKNOWN_SUSPEND (%d)", source or -1))
|
|
if not self.screen_saver_mode then
|
|
if self:supportsScreensaver() then
|
|
-- NOTE: Meaning this is not a SO device ;)
|
|
local Screensaver = require("ui/screensaver")
|
|
Screensaver:setup()
|
|
Screensaver:show()
|
|
else
|
|
-- Let the native system handle screensavers on SO devices...
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -CONT awesome")
|
|
elseif os.getenv("CVM_STOPPED") == "yes" then
|
|
os.execute("killall -CONT cvm")
|
|
end
|
|
|
|
-- Don't forget to flag ourselves in ScreenSaver mode like Screensaver:show would,
|
|
-- so that we do the right thing on resume ;).
|
|
self.screen_saver_mode = true
|
|
end
|
|
end
|
|
|
|
self.powerd:beforeSuspend()
|
|
end
|
|
|
|
function Kindle:outofScreenSaver(source)
|
|
logger.dbg("Kindle:outofScreenSaver via", POWERD_EVENT_SOURCES[source] or string.format("UNKNOWN_WAKEUP (%d)", source or -1))
|
|
if self.screen_saver_mode then
|
|
if self:supportsScreensaver() then
|
|
local Screensaver = require("ui/screensaver")
|
|
local widget_was_closed = Screensaver:close()
|
|
if widget_was_closed then
|
|
-- And redraw everything in case the framework managed to screw us over...
|
|
UIManager:nextTick(function() UIManager:setDirty("all", "full") end)
|
|
end
|
|
|
|
-- If the device supports deep sleep, and we woke up from hibernation (which kicks in at the 1H mark),
|
|
-- chuck an extra tiny refresh to get rid of the "waking up" banner if the above refresh was too early...
|
|
if self.canDeepSleep and self.last_suspend_time > time.s(self.hibernationDelay) then
|
|
if lfs.attributes("/var/local/system/powerd/hibernate_session_tracker", "mode") == "file" then
|
|
local mtime = lfs.attributes("/var/local/system/powerd/hibernate_session_tracker", "modification")
|
|
local now = os.time()
|
|
if math.abs(now - mtime) <= 60 then
|
|
-- That was less than a minute ago, assume we're golden.
|
|
logger.dbg("Kindle: Woke up from hibernation")
|
|
-- The banner on a 1236x1648 PW5 is 1235x125; we refresh the bottom 10% of the screen to be safe.
|
|
local Geom = require("ui/geometry")
|
|
local screen_height = self.screen:getHeight()
|
|
local refresh_height = math.ceil(screen_height * (1/10))
|
|
local refresh_region = Geom:new{
|
|
x = 0,
|
|
y = screen_height - 1 - refresh_height,
|
|
w = self.screen:getWidth(),
|
|
h = refresh_height
|
|
}
|
|
UIManager:scheduleIn(1.5, function()
|
|
UIManager:setDirty("all", "ui", refresh_region)
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- Stop awesome again if need be...
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -STOP awesome")
|
|
elseif os.getenv("CVM_STOPPED") == "yes" then
|
|
os.execute("killall -STOP cvm")
|
|
end
|
|
-- 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.
|
|
UIManager:scheduleIn(3, function() UIManager:setDirty("all", "full") end)
|
|
-- Flip the switch again
|
|
self.screen_saver_mode = false
|
|
end
|
|
end
|
|
|
|
self.powerd:afterResume()
|
|
end
|
|
|
|
-- On stock, there's a distinction between OutOfSS (which *requests* closing the SS) and ExitingSS, which fires once they're *actually* closed...
|
|
function Kindle:exitingScreenSaver() end
|
|
|
|
function Kindle:usbPlugOut()
|
|
-- NOTE: See usbPlugIn(), we don't have anything fancy to do here either.
|
|
end
|
|
|
|
function Kindle:wakeupFromSuspend(ts)
|
|
logger.dbg("Kindle:wakeupFromSuspend", ts)
|
|
self.powerd:wakeupFromSuspend(ts)
|
|
self.last_suspend_time = time.boottime_or_realtime_coarse() - self.suspend_time
|
|
self.total_suspend_time = self.total_suspend_time + self.last_suspend_time
|
|
end
|
|
|
|
function Kindle:readyToSuspend(delay)
|
|
logger.dbg("Kindle:readyToSuspend", delay)
|
|
self.powerd:readyToSuspend(delay)
|
|
self.suspend_time = time.boottime_or_realtime_coarse()
|
|
end
|
|
|
|
-- We add --no-same-permissions --no-same-owner to make the userstore fuse proxy happy...
|
|
function Kindle:untar(archive, extract_to)
|
|
return os.execute(("./tar --no-same-permissions --no-same-owner -xf %q -C %q"):format(archive, extract_to))
|
|
end
|
|
|
|
function Kindle:UIManagerReady(uimgr)
|
|
UIManager = uimgr
|
|
end
|
|
|
|
function Kindle:setEventHandlers(uimgr)
|
|
-- These custom fake events *will* pass an argument...
|
|
self.input.fake_event_args.IntoSS = {}
|
|
self.input.fake_event_args.OutOfSS = {}
|
|
self.input.fake_event_args.WakeupFromSuspend = {}
|
|
self.input.fake_event_args.ReadyToSuspend = {}
|
|
|
|
UIManager.event_handlers.Suspend = function()
|
|
self.powerd:toggleSuspend()
|
|
end
|
|
UIManager.event_handlers.IntoSS = function(input_event)
|
|
-- Retrieve the argument set by Input:handleKeyBoardEv
|
|
local arg = table.remove(self.input.fake_event_args[input_event])
|
|
self:intoScreenSaver(arg)
|
|
end
|
|
UIManager.event_handlers.OutOfSS = function(input_event)
|
|
local arg = table.remove(self.input.fake_event_args[input_event])
|
|
self:outofScreenSaver(arg)
|
|
end
|
|
UIManager.event_handlers.ExitingSS = function()
|
|
self:exitingScreenSaver()
|
|
end
|
|
UIManager.event_handlers.Charging = function()
|
|
self:_beforeCharging()
|
|
self:usbPlugIn()
|
|
end
|
|
UIManager.event_handlers.NotCharging = function()
|
|
self:usbPlugOut()
|
|
self:_afterNotCharging()
|
|
end
|
|
UIManager.event_handlers.WakeupFromSuspend = function(input_event)
|
|
local arg = table.remove(self.input.fake_event_args[input_event])
|
|
self:wakeupFromSuspend(arg)
|
|
end
|
|
UIManager.event_handlers.ReadyToSuspend = function(input_event)
|
|
local arg = table.remove(self.input.fake_event_args[input_event])
|
|
self:readyToSuspend(arg)
|
|
end
|
|
end
|
|
|
|
function Kindle:ambientBrightnessLevel()
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
if not haslipc or lipc == nil then return 0 end
|
|
local lipc_handle = lipc.init("com.github.koreader.ambientbrightness")
|
|
if not lipc_handle then return 0 end
|
|
local value = lipc_handle:get_int_property("com.lab126.powerd", "alsLux")
|
|
lipc_handle:close()
|
|
if type(value) ~= "number" then return 0 end
|
|
if value < 10 then return 0 end
|
|
if value < 96 then return 1 end
|
|
if value < 192 then return 2 end
|
|
if value < 32768 then return 3 end
|
|
return 4
|
|
end
|
|
|
|
local Kindle2 = Kindle:extend{
|
|
model = "Kindle2",
|
|
isREAGL = no,
|
|
hasKeyboard = yes,
|
|
hasKeys = yes,
|
|
hasSymKey = yes,
|
|
hasDPad = yes,
|
|
useDPadAsActionKeys = yes,
|
|
canHWInvert = no,
|
|
canModifyFBInfo = no,
|
|
canUseCBB = no, -- 4bpp
|
|
canUseWAL = no, -- Kernel too old to support mmap'ed I/O on /mnt/us
|
|
supportsScreensaver = yes, -- The first ad-supported device was the K3
|
|
}
|
|
|
|
local KindleDXG = Kindle:extend{
|
|
model = "KindleDXG",
|
|
isREAGL = no,
|
|
hasKeyboard = yes,
|
|
hasKeys = yes,
|
|
hasSymKey = yes,
|
|
hasDPad = yes,
|
|
useDPadAsActionKeys = yes,
|
|
canHWInvert = no,
|
|
canModifyFBInfo = no,
|
|
canUseCBB = no, -- 4bpp
|
|
canUseWAL = no, -- Kernel too old to support mmap'ed I/O on /mnt/us
|
|
supportsScreensaver = yes, -- The first ad-supported device was the K3
|
|
}
|
|
|
|
local Kindle3 = Kindle:extend{
|
|
model = "Kindle3",
|
|
isREAGL = no,
|
|
hasKeyboard = yes,
|
|
hasKeys = yes,
|
|
hasSymKey = yes,
|
|
hasDPad = yes,
|
|
useDPadAsActionKeys = yes,
|
|
canHWInvert = no,
|
|
canModifyFBInfo = no,
|
|
canUseCBB = no, -- 4bpp
|
|
isSpecialOffers = hasSpecialOffers(),
|
|
}
|
|
|
|
local Kindle4 = Kindle:extend{
|
|
model = "Kindle4",
|
|
isREAGL = no,
|
|
hasKeys = yes,
|
|
hasScreenKB = yes,
|
|
hasDPad = yes,
|
|
useDPadAsActionKeys = yes,
|
|
canHWInvert = no,
|
|
canModifyFBInfo = no,
|
|
-- NOTE: It could *technically* use the C BB, as it's running @ 8bpp, but it's expecting an inverted palette...
|
|
canUseCBB = no,
|
|
isSpecialOffers = hasSpecialOffers(),
|
|
}
|
|
|
|
local KindleTouch = Kindle:extend{
|
|
model = "KindleTouch",
|
|
isREAGL = no,
|
|
isTouchDevice = yes,
|
|
hasKeys = yes,
|
|
touch_dev = "/dev/input/event3",
|
|
}
|
|
|
|
local KindlePaperWhite = Kindle:extend{
|
|
model = "KindlePaperWhite",
|
|
isREAGL = no,
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
canTurnFrontlightOff = no,
|
|
display_dpi = 212,
|
|
touch_dev = "/dev/input/event0",
|
|
}
|
|
|
|
local KindlePaperWhite2 = Kindle:extend{
|
|
model = "KindlePaperWhite2",
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
canTurnFrontlightOff = no,
|
|
display_dpi = 212,
|
|
touch_dev = "/dev/input/event1",
|
|
}
|
|
|
|
local KindleBasic = Kindle:extend{
|
|
model = "KindleBasic",
|
|
isTouchDevice = yes,
|
|
touch_dev = "/dev/input/event1",
|
|
}
|
|
|
|
local KindleVoyage = Kindle:extend{
|
|
model = "KindleVoyage",
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
canTurnFrontlightOff = no,
|
|
hasLightSensor = yes,
|
|
hasKeys = yes,
|
|
display_dpi = 300,
|
|
touch_dev = "/dev/input/event1",
|
|
}
|
|
|
|
local KindlePaperWhite3 = Kindle:extend{
|
|
model = "KindlePaperWhite3",
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
canTurnFrontlightOff = no,
|
|
display_dpi = 300,
|
|
touch_dev = "/dev/input/event1",
|
|
}
|
|
|
|
local KindleOasis = Kindle:extend{
|
|
model = "KindleOasis",
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
hasKeys = yes,
|
|
hasGSensor = yes,
|
|
display_dpi = 300,
|
|
--[[
|
|
-- NOTE: Points to event3 on Wi-Fi devices, event4 on 3G devices...
|
|
-- 3G devices apparently have an extra SX9500 Proximity/Capacitive controller for mysterious purposes...
|
|
-- This evidently screws with the ordering, so, use the udev by-path path instead to avoid hackier workarounds.
|
|
-- cf. #2181
|
|
--]]
|
|
touch_dev = "/dev/input/by-path/platform-imx-i2c.1-event",
|
|
}
|
|
|
|
local KindleOasis2 = Kindle:extend{
|
|
model = "KindleOasis2",
|
|
isZelda = yes,
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
hasLightSensor = yes,
|
|
hasKeys = yes,
|
|
hasGSensor = yes,
|
|
display_dpi = 300,
|
|
touch_dev = "/dev/input/by-path/platform-30a30000.i2c-event",
|
|
}
|
|
|
|
local KindleOasis3 = Kindle:extend{
|
|
model = "KindleOasis3",
|
|
isZelda = yes,
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
hasNaturalLight = yes,
|
|
hasNaturalLightMixer = yes,
|
|
hasLightSensor = yes,
|
|
hasKeys = yes,
|
|
hasGSensor = yes,
|
|
display_dpi = 300,
|
|
touch_dev = "/dev/input/by-path/platform-30a30000.i2c-event",
|
|
}
|
|
|
|
local KindleBasic2 = Kindle:extend{
|
|
model = "KindleBasic2",
|
|
isTouchDevice = yes,
|
|
touch_dev = "/dev/input/event0",
|
|
}
|
|
|
|
local KindlePaperWhite4 = Kindle:extend{
|
|
model = "KindlePaperWhite4",
|
|
isRex = yes,
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
display_dpi = 300,
|
|
-- NOTE: LTE devices once again have a mysterious extra SX9310 proximity sensor...
|
|
-- Except this time, we can't rely on by-path, because there's no entry for the TS :/.
|
|
-- Should be event2 on Wi-Fi, event3 on LTE, we'll fix it in init.
|
|
touch_dev = "/dev/input/event2",
|
|
}
|
|
|
|
local KindleBasic3 = Kindle:extend{
|
|
model = "KindleBasic3",
|
|
isRex = yes,
|
|
-- NOTE: Apparently, the KT4 doesn't actually support the fancy nightmode waveforms, c.f., ko/#5076
|
|
-- It also doesn't handle HW dithering, c.f., base/#1039
|
|
isNightModeChallenged = yes,
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
touch_dev = "/dev/input/event2",
|
|
}
|
|
|
|
local KindlePaperWhite5 = Kindle:extend{
|
|
model = "KindlePaperWhite5",
|
|
isMTK = yes,
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
hasNaturalLight = yes,
|
|
-- NOTE: We *can* technically control both LEDs independently,
|
|
-- but the mix is device-specific, we don't have access to the LUT for the mix powerd is using,
|
|
-- and the widget is designed for the Kobo Aura One anyway, so, hahaha, nope.
|
|
hasNaturalLightMixer = yes,
|
|
display_dpi = 300,
|
|
-- NOTE: While hardware dithering (via MDP) should be a thing, it doesn't appear to do anything right now :/.
|
|
canHWDither = no,
|
|
canDoSwipeAnimation = yes,
|
|
-- NOTE: Input device path is variable, see findInputDevices
|
|
}
|
|
|
|
local KindleBasic4 = Kindle:extend{
|
|
model = "KindleBasic4",
|
|
isMTK = yes,
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
display_dpi = 300,
|
|
canHWDither = no,
|
|
canDoSwipeAnimation = yes,
|
|
-- NOTE: Like the PW5, input device path is variable, see findInputDevices
|
|
}
|
|
|
|
local KindleScribe = Kindle:extend{
|
|
model = "KindleScribe",
|
|
isMTK = yes,
|
|
isTouchDevice = yes,
|
|
hasFrontlight = yes,
|
|
hasNaturalLight = yes,
|
|
-- NOTE: We *can* technically control both LEDs independently,
|
|
-- but the mix is device-specific, we don't have access to the LUT for the mix powerd is using,
|
|
-- and the widget is designed for the Kobo Aura One anyway, so, hahaha, nope.
|
|
hasNaturalLightMixer = yes,
|
|
hasLightSensor = yes,
|
|
hasGSensor = yes,
|
|
display_dpi = 300,
|
|
touch_dev = "/dev/input/touch",
|
|
canHWDither = yes,
|
|
canDoSwipeAnimation = yes,
|
|
}
|
|
|
|
function Kindle2:init()
|
|
self.screen = require("ffi/framebuffer_einkfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
is_charging_file = "/sys/devices/platform/charger/charging",
|
|
}
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
event_map = require("device/kindle/event_map_keyboard"),
|
|
}
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindleDXG:init()
|
|
self.screen = require("ffi/framebuffer_einkfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
is_charging_file = "/sys/devices/platform/charger/charging",
|
|
}
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
event_map = require("device/kindle/event_map_keyboard"),
|
|
}
|
|
self.keyboard_layout = require("device/kindle/keyboard_layout")
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function Kindle3:init()
|
|
self.screen = require("ffi/framebuffer_einkfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
batt_capacity_file = "/sys/devices/system/luigi_battery/luigi_battery0/battery_capacity",
|
|
is_charging_file = "/sys/devices/platform/fsl-usb2-udc/charging",
|
|
}
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
event_map = require("device/kindle/event_map_kindle4"),
|
|
}
|
|
self.keyboard_layout = require("device/kindle/keyboard_layout")
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function Kindle4:init()
|
|
self.screen = require("ffi/framebuffer_einkfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
batt_capacity_file = "/sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity",
|
|
is_charging_file = "/sys/devices/platform/fsl-usb2-udc/charging",
|
|
}
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
event_map = require("device/kindle/event_map_kindle4"),
|
|
}
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindleTouch:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
batt_capacity_file = "/sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity",
|
|
is_charging_file = "/sys/devices/platform/fsl-usb2-udc/charging",
|
|
}
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
-- Kindle Touch has a single button
|
|
event_map = { [102] = "Home" },
|
|
}
|
|
|
|
-- Kindle Touch needs event modification for proper coordinates
|
|
self.input:registerEventAdjustHook(self.input.adjustTouchScale, {x=600/4095, y=800/4095})
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindlePaperWhite:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/devices/system/fl_tps6116x/fl_tps6116x0/fl_intensity",
|
|
batt_capacity_file = "/sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity",
|
|
is_charging_file = "/sys/devices/platform/aplite_charger.0/charging",
|
|
}
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindlePaperWhite2:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/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",
|
|
batt_status_file = "/sys/class/power_supply/max77696-battery/status",
|
|
hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable",
|
|
}
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindleBasic:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
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",
|
|
hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable",
|
|
}
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindleVoyage:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/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",
|
|
hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable",
|
|
}
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
event_map = {
|
|
[104] = "LPgBack",
|
|
[109] = "LPgFwd",
|
|
},
|
|
}
|
|
-- touch gestures fall into these cold spots defined by (x, y, r)
|
|
-- will be rewritten to 'none' ges thus being ignored
|
|
-- x, y is the absolute position disregard of screen mode, r is spot radius
|
|
self.cold_spots = {
|
|
{
|
|
x = 1080 + 50, y = 485, r = 80
|
|
},
|
|
{
|
|
x = 1080 + 70, y = 910, r = 120
|
|
},
|
|
{
|
|
x = -50, y = 485, r = 80
|
|
},
|
|
{
|
|
x = -70, y = 910, r = 120
|
|
},
|
|
}
|
|
|
|
self.input:registerGestureAdjustHook(function(_, ges)
|
|
if ges then
|
|
local pos = ges.pos
|
|
for _, spot in ipairs(self.cold_spots) do
|
|
if (spot.x - pos.x) * (spot.x - pos.x) +
|
|
(spot.y - pos.y) * (spot.y - pos.y) < spot.r * spot.r then
|
|
ges.ges = "none"
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
Kindle.init(self)
|
|
|
|
-- Re-enable WhisperTouch keys when started without framework
|
|
if self.framework_lipc_handle then
|
|
self.framework_lipc_handle:set_int_property("com.lab126.deviced", "fsrkeypadEnable", 1)
|
|
self.framework_lipc_handle:set_int_property("com.lab126.deviced", "fsrkeypadPrevEnable", 1)
|
|
self.framework_lipc_handle:set_int_property("com.lab126.deviced", "fsrkeypadNextEnable", 1)
|
|
end
|
|
end
|
|
|
|
function KindlePaperWhite3:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/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",
|
|
hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable",
|
|
}
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
-- HAL for gyro orientation switches (EV_ABS:ABS_PRESSURE (?!) w/ custom values to EV_MSC:MSC_GYRO w/ our own custom values)
|
|
local function OasisGyroTranslation(this, ev)
|
|
local DEVICE_ORIENTATION_PORTRAIT_LEFT = 15
|
|
local DEVICE_ORIENTATION_PORTRAIT_RIGHT = 17
|
|
local DEVICE_ORIENTATION_PORTRAIT = 19
|
|
local DEVICE_ORIENTATION_PORTRAIT_ROTATED_LEFT = 16
|
|
local DEVICE_ORIENTATION_PORTRAIT_ROTATED_RIGHT = 18
|
|
local DEVICE_ORIENTATION_PORTRAIT_ROTATED = 20
|
|
local DEVICE_ORIENTATION_LANDSCAPE = 21
|
|
local DEVICE_ORIENTATION_LANDSCAPE_ROTATED = 22
|
|
|
|
if ev.type == C.EV_ABS and ev.code == C.ABS_PRESSURE then
|
|
if ev.value == DEVICE_ORIENTATION_PORTRAIT
|
|
or ev.value == DEVICE_ORIENTATION_PORTRAIT_LEFT
|
|
or ev.value == DEVICE_ORIENTATION_PORTRAIT_RIGHT then
|
|
-- i.e., UR
|
|
ev.type = C.EV_MSC
|
|
ev.code = C.MSC_GYRO
|
|
ev.value = C.DEVICE_ROTATED_UPRIGHT
|
|
elseif ev.value == DEVICE_ORIENTATION_LANDSCAPE then
|
|
-- i.e., CW
|
|
ev.type = C.EV_MSC
|
|
ev.code = C.MSC_GYRO
|
|
ev.value = C.DEVICE_ROTATED_CLOCKWISE
|
|
elseif ev.value == DEVICE_ORIENTATION_PORTRAIT_ROTATED
|
|
or ev.value == DEVICE_ORIENTATION_PORTRAIT_ROTATED_LEFT
|
|
or ev.value == DEVICE_ORIENTATION_PORTRAIT_ROTATED_RIGHT then
|
|
-- i.e., UD
|
|
ev.type = C.EV_MSC
|
|
ev.code = C.MSC_GYRO
|
|
ev.value = C.DEVICE_ROTATED_UPSIDE_DOWN
|
|
elseif ev.value == DEVICE_ORIENTATION_LANDSCAPE_ROTATED then
|
|
-- i.e., CCW
|
|
ev.type = C.EV_MSC
|
|
ev.code = C.MSC_GYRO
|
|
ev.value = C.DEVICE_ROTATED_COUNTER_CLOCKWISE
|
|
end
|
|
end
|
|
end
|
|
|
|
function KindleOasis:init()
|
|
-- temporarily wake up awesome
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -CONT awesome")
|
|
end
|
|
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/max77696-bl/brightness",
|
|
-- NOTE: Points to the embedded battery. The one in the cover is codenamed "soda".
|
|
batt_capacity_file = "/sys/devices/system/wario_battery/wario_battery0/battery_capacity",
|
|
is_charging_file = "/sys/devices/system/wario_charger/wario_charger0/charging",
|
|
hall_file = "/sys/devices/system/wario_hall/wario_hall0/hall_enable",
|
|
}
|
|
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
|
|
event_map = {
|
|
[104] = "RPgFwd",
|
|
[109] = "RPgBack",
|
|
}
|
|
}
|
|
|
|
Kindle.init(self)
|
|
|
|
--- @note See comments in KindleOasis2:init() for details.
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
if haslipc then
|
|
local lipc_handle = lipc.init("com.github.koreader.screen")
|
|
if lipc_handle then
|
|
local orientation_code = lipc_handle:get_string_property(
|
|
"com.lab126.winmgr", "accelerometer")
|
|
logger.dbg("orientation_code =", orientation_code)
|
|
local rotation_mode = 0
|
|
if orientation_code then
|
|
if orientation_code == "U" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_UPRIGHT
|
|
elseif orientation_code == "R" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_CLOCKWISE
|
|
elseif orientation_code == "D" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_UPSIDE_DOWN
|
|
elseif orientation_code == "L" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_COUNTER_CLOCKWISE
|
|
end
|
|
end
|
|
if rotation_mode > 0 then
|
|
self.screen.native_rotation_mode = rotation_mode
|
|
end
|
|
self.screen:setRotationMode(rotation_mode)
|
|
lipc_handle:close()
|
|
end
|
|
end
|
|
-- put awesome back to sleep
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -STOP awesome")
|
|
end
|
|
|
|
self.input:registerEventAdjustHook(OasisGyroTranslation)
|
|
self.input.handleMiscEv = function(this, ev)
|
|
if ev.code == C.MSC_GYRO then
|
|
return this:handleGyroEv(ev)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- HAL for gyro orientation switches (EV_ABS:ABS_PRESSURE (?!) w/ custom values to EV_MSC:MSC_GYRO w/ our own custom values)
|
|
local function KindleGyroTransform(this, ev)
|
|
-- See source code:
|
|
-- c.f., drivers/input/misc/accel/bma2x2.c for KOA2/KOA3
|
|
-- c.f., drivers/input/misc/kx132/kx132.h for KS
|
|
local UPWARD_PORTRAIT_UP_INTERRUPT_HAPPENED = 15
|
|
local UPWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED = 16
|
|
local UPWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED = 17
|
|
local UPWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED = 18
|
|
|
|
if ev.type == C.EV_ABS and ev.code == C.ABS_PRESSURE then
|
|
if ev.value == UPWARD_PORTRAIT_UP_INTERRUPT_HAPPENED then
|
|
-- i.e., UR
|
|
ev.type = C.EV_MSC
|
|
ev.code = C.MSC_GYRO
|
|
ev.value = C.DEVICE_ROTATED_UPRIGHT
|
|
elseif ev.value == UPWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED then
|
|
-- i.e., CW
|
|
ev.type = C.EV_MSC
|
|
ev.code = C.MSC_GYRO
|
|
ev.value = C.DEVICE_ROTATED_CLOCKWISE
|
|
elseif ev.value == UPWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED then
|
|
-- i.e., UD
|
|
ev.type = C.EV_MSC
|
|
ev.code = C.MSC_GYRO
|
|
ev.value = C.DEVICE_ROTATED_UPSIDE_DOWN
|
|
elseif ev.value == UPWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED then
|
|
-- i.e., CCW
|
|
ev.type = C.EV_MSC
|
|
ev.code = C.MSC_GYRO
|
|
ev.value = C.DEVICE_ROTATED_COUNTER_CLOCKWISE
|
|
end
|
|
end
|
|
end
|
|
|
|
function KindleOasis2:init()
|
|
-- temporarily wake up awesome
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -CONT awesome")
|
|
end
|
|
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/max77796-bl/brightness",
|
|
batt_capacity_file = "/sys/class/power_supply/max77796-battery/capacity",
|
|
is_charging_file = "/sys/class/power_supply/max77796-charger/charging",
|
|
batt_status_file = "/sys/class/power_supply/max77796-charger/status",
|
|
}
|
|
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
|
|
-- Top, Bottom (yes, it's the reverse than on non-Oasis devices)
|
|
event_map = {
|
|
[104] = "RPgFwd",
|
|
[109] = "RPgBack",
|
|
}
|
|
}
|
|
|
|
Kindle.init(self)
|
|
|
|
--- @note When starting KOReader with the device upside down ("D"), touch input is registered wrong
|
|
-- (i.e., probably upside down).
|
|
-- If it's started upright ("U"), everything's okay, and turning it upside down after that works just fine.
|
|
-- See #2206 & #2209 for the original KOA implementation, which obviously doesn't quite cut it here...
|
|
-- See also <https://www.mobileread.com/forums/showthread.php?t=298302&page=5>
|
|
-- See also #11159 for details about the solution (Kindle Scribe as an example)
|
|
-- In regular mode, awesome is woken up for a brief moment for lipc calls.
|
|
-- In no-framework mode, this works as is.
|
|
-- NOTE: It'd take some effort to actually start KOReader while in a LANDSCAPE orientation,
|
|
-- since they're only exposed inside the stock reader, and not the Home/KUAL Booklets.
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
if haslipc then
|
|
local lipc_handle = lipc.init("com.github.koreader.screen")
|
|
if lipc_handle then
|
|
local orientation_code = lipc_handle:get_string_property(
|
|
"com.lab126.winmgr", "accelerometer")
|
|
logger.dbg("orientation_code =", orientation_code)
|
|
local rotation_mode = 0
|
|
if orientation_code then
|
|
if orientation_code == "U" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_UPRIGHT
|
|
elseif orientation_code == "R" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_CLOCKWISE
|
|
elseif orientation_code == "D" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_UPSIDE_DOWN
|
|
elseif orientation_code == "L" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_COUNTER_CLOCKWISE
|
|
end
|
|
end
|
|
if rotation_mode > 0 then
|
|
self.screen.native_rotation_mode = rotation_mode
|
|
end
|
|
self.screen:setRotationMode(rotation_mode)
|
|
lipc_handle:close()
|
|
end
|
|
end
|
|
-- put awesome back to sleep
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -STOP awesome")
|
|
end
|
|
|
|
self.input:registerEventAdjustHook(KindleGyroTransform)
|
|
self.input.handleMiscEv = function(this, ev)
|
|
if ev.code == C.MSC_GYRO then
|
|
return this:handleGyroEv(ev)
|
|
end
|
|
end
|
|
end
|
|
|
|
function KindleOasis3:init()
|
|
-- temporarily wake up awesome
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -CONT awesome")
|
|
end
|
|
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/lm3697-bl1/brightness",
|
|
warmth_intensity_file = "/sys/class/backlight/lm3697-bl0/brightness",
|
|
batt_capacity_file = "/sys/class/power_supply/max77796-battery/capacity",
|
|
is_charging_file = "/sys/class/power_supply/max77796-charger/charging",
|
|
batt_status_file = "/sys/class/power_supply/max77796-charger/status",
|
|
}
|
|
|
|
self.input = require("device/input"):new{
|
|
device = self,
|
|
|
|
-- Top, Bottom (yes, it's the reverse than on non-Oasis devices)
|
|
event_map = {
|
|
[104] = "RPgFwd",
|
|
[109] = "RPgBack",
|
|
}
|
|
}
|
|
|
|
Kindle.init(self)
|
|
|
|
--- @note The same quirks as on the Oasis 2 apply ;).
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
if haslipc then
|
|
local lipc_handle = lipc.init("com.github.koreader.screen")
|
|
if lipc_handle then
|
|
local orientation_code = lipc_handle:get_string_property(
|
|
"com.lab126.winmgr", "accelerometer")
|
|
logger.dbg("orientation_code =", orientation_code)
|
|
local rotation_mode = 0
|
|
if orientation_code then
|
|
if orientation_code == "U" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_UPRIGHT
|
|
elseif orientation_code == "R" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_CLOCKWISE
|
|
elseif orientation_code == "D" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_UPSIDE_DOWN
|
|
elseif orientation_code == "L" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_COUNTER_CLOCKWISE
|
|
end
|
|
end
|
|
if rotation_mode > 0 then
|
|
self.screen.native_rotation_mode = rotation_mode
|
|
end
|
|
self.screen:setRotationMode(rotation_mode)
|
|
lipc_handle:close()
|
|
end
|
|
end
|
|
-- put awesome back to sleep
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -STOP awesome")
|
|
end
|
|
|
|
self.input:registerEventAdjustHook(KindleGyroTransform)
|
|
self.input.handleMiscEv = function(this, ev)
|
|
if ev.code == C.MSC_GYRO then
|
|
return this:handleGyroEv(ev)
|
|
end
|
|
end
|
|
end
|
|
|
|
function KindleBasic2:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
batt_capacity_file = "/sys/class/power_supply/bd7181x_bat/capacity",
|
|
is_charging_file = "/sys/class/power_supply/bd7181x_bat/charging",
|
|
batt_status_file = "/sys/class/power_supply/bd7181x_bat/status",
|
|
hall_file = "/sys/devices/system/heisenberg_hall/heisenberg_hall0/hall_enable",
|
|
}
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindlePaperWhite4:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/bl/brightness",
|
|
batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity",
|
|
is_charging_file = "/sys/class/power_supply/bd71827_bat/charging",
|
|
batt_status_file = "/sys/class/power_supply/bd71827_bat/status",
|
|
hall_file = "/sys/bus/platform/drivers/hall_sensor/rex_hall/hall_enable",
|
|
}
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindleBasic3:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/bl/brightness",
|
|
batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity",
|
|
is_charging_file = "/sys/class/power_supply/bd71827_bat/charging",
|
|
batt_status_file = "/sys/class/power_supply/bd71827_bat/status",
|
|
hall_file = "/sys/bus/platform/drivers/hall_sensor/rex_hall/hall_enable",
|
|
}
|
|
|
|
Kindle.init(self)
|
|
|
|
-- This device doesn't emit ABS_MT_TRACKING_ID:-1 events on contact lift,
|
|
-- so we have to rely on contact lift detection via BTN_TOUCH:0,
|
|
-- c.f., https://github.com/koreader/koreader/issues/5070
|
|
self.input.snow_protocol = true
|
|
end
|
|
|
|
function KindlePaperWhite5:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/fp9966-bl1/brightness",
|
|
warmth_intensity_file = "/sys/class/backlight/fp9966-bl0/brightness",
|
|
batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity",
|
|
is_charging_file = "/sys/class/power_supply/bd71827_bat/charging",
|
|
batt_status_file = "/sys/class/power_supply/bd71827_bat/status",
|
|
hall_file = "/sys/devices/platform/eink_hall/hall_enable",
|
|
}
|
|
|
|
-- Enable the so-called "fast" mode, so as to prevent the driver from silently promoting refreshes to REAGL.
|
|
self.screen:_MTK_ToggleFastMode(true)
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindleBasic4:init()
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/fp9966-bl1/brightness",
|
|
warmth_intensity_file = "/sys/class/backlight/fp9966-bl0/brightness",
|
|
batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity",
|
|
is_charging_file = "/sys/class/power_supply/bd71827_bat/charging",
|
|
batt_status_file = "/sys/class/power_supply/bd71827_bat/status",
|
|
}
|
|
|
|
-- Enable the so-called "fast" mode, so as to prevent the driver from silently promoting refreshes to REAGL.
|
|
self.screen:_MTK_ToggleFastMode(true)
|
|
|
|
Kindle.init(self)
|
|
end
|
|
|
|
function KindleScribe:init()
|
|
-- temporarily wake up awesome
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -CONT awesome")
|
|
end
|
|
|
|
self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg}
|
|
self.powerd = require("device/kindle/powerd"):new{
|
|
device = self,
|
|
fl_intensity_file = "/sys/class/backlight/fp9966-bl1/brightness",
|
|
warmth_intensity_file = "/sys/class/backlight/fp9966-bl0/brightness",
|
|
batt_capacity_file = "/sys/class/power_supply/bd71827_bat/capacity",
|
|
is_charging_file = "/sys/class/power_supply/bd71827_bat/charging",
|
|
batt_status_file = "/sys/class/power_supply/bd71827_bat/status",
|
|
hall_file = "/sys/devices/platform/eink_hall/hall_enable",
|
|
}
|
|
|
|
-- Enable the so-called "fast" mode, so as to prevent the driver from silently promoting refreshes to REAGL.
|
|
self.screen:_MTK_ToggleFastMode(true)
|
|
|
|
Kindle.init(self)
|
|
|
|
--- @note The same quirks as on the Oasis 2 and 3 apply ;).
|
|
local haslipc, lipc = pcall(require, "liblipclua")
|
|
if haslipc then
|
|
local lipc_handle = lipc.init("com.github.koreader.screen")
|
|
if lipc_handle then
|
|
local orientation_code = lipc_handle:get_string_property(
|
|
"com.lab126.winmgr", "accelerometer")
|
|
logger.dbg("orientation_code =", orientation_code)
|
|
local rotation_mode = 0
|
|
if orientation_code then
|
|
if orientation_code == "U" or "L" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_UPRIGHT
|
|
elseif orientation_code == "D" or "R" then
|
|
rotation_mode = self.screen.DEVICE_ROTATED_UPSIDE_DOWN
|
|
end
|
|
end
|
|
if rotation_mode > 0 then
|
|
self.screen.native_rotation_mode = rotation_mode
|
|
end
|
|
self.screen:setRotationMode(rotation_mode)
|
|
lipc_handle:close()
|
|
end
|
|
end
|
|
-- put awesome back to sleep
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -STOP awesome")
|
|
end
|
|
|
|
-- Setup accelerometer rotation input
|
|
self.input:registerEventAdjustHook(KindleGyroTransform)
|
|
self.input.handleMiscEv = function(this, ev)
|
|
if ev.code == C.MSC_GYRO then
|
|
return this:handleGyroEv(ev)
|
|
end
|
|
end
|
|
|
|
-- Setup pen input
|
|
self.input.wacom_protocol = true
|
|
end
|
|
|
|
function KindleTouch:exit()
|
|
if self:isMTK() then
|
|
-- Disable the so-called "fast" mode
|
|
self.screen:_MTK_ToggleFastMode(false)
|
|
end
|
|
|
|
if self.framework_lipc_handle then
|
|
-- Fixes missing *stock Amazon UI* screensavers on exiting out of "no framework" started KOReader
|
|
-- module was unloaded in frameworkStopped() function but wasn't (re)loaded on KOReader exit
|
|
self.framework_lipc_handle:set_string_property("com.lab126.blanket", "load", "screensaver")
|
|
self.framework_lipc_handle:close()
|
|
end
|
|
|
|
self.powerd:__gc()
|
|
|
|
Generic.exit(self)
|
|
|
|
if self.isSpecialOffers then
|
|
-- Wakey wakey...
|
|
if os.getenv("AWESOME_STOPPED") == "yes" then
|
|
os.execute("killall -CONT awesome")
|
|
end
|
|
-- fake a touch event
|
|
if self.touch_dev then
|
|
local width, height = self.screen:getScreenWidth(), self.screen:getScreenHeight()
|
|
require("ffi/input").fakeTapInput(self.touch_dev,
|
|
math.min(width, height)/2,
|
|
math.max(width, height)-30
|
|
)
|
|
end
|
|
end
|
|
end
|
|
KindlePaperWhite.exit = KindleTouch.exit
|
|
KindlePaperWhite2.exit = KindleTouch.exit
|
|
KindleBasic.exit = KindleTouch.exit
|
|
KindleVoyage.exit = KindleTouch.exit
|
|
KindlePaperWhite3.exit = KindleTouch.exit
|
|
KindleOasis.exit = KindleTouch.exit
|
|
KindleOasis2.exit = KindleTouch.exit
|
|
KindleBasic2.exit = KindleTouch.exit
|
|
KindlePaperWhite4.exit = KindleTouch.exit
|
|
KindleBasic3.exit = KindleTouch.exit
|
|
KindleOasis3.exit = KindleTouch.exit
|
|
KindlePaperWhite5.exit = KindleTouch.exit
|
|
KindleBasic4.exit = KindleTouch.exit
|
|
KindleScribe.exit = KindleTouch.exit
|
|
|
|
function Kindle3:exit()
|
|
-- send double menu key press events to trigger screen refresh
|
|
os.execute("echo 'send 139' > /proc/keypad;echo 'send 139' > /proc/keypad")
|
|
|
|
Generic.exit(self)
|
|
end
|
|
|
|
KindleDXG.exit = Kindle3.exit
|
|
|
|
|
|
----------------- device recognition: -------------------
|
|
|
|
local function Set(list)
|
|
local set = {}
|
|
for _, l in ipairs(list) do set[l] = true end
|
|
return set
|
|
end
|
|
|
|
|
|
local kindle_sn_fd = io.open("/proc/usid", "r")
|
|
if not kindle_sn_fd then return end
|
|
local kindle_sn = kindle_sn_fd:read("*line")
|
|
kindle_sn_fd:close()
|
|
-- NOTE: Attempt to sanely differentiate v1 from v2,
|
|
-- c.f., https://github.com/NiLuJe/FBInk/commit/8a1161734b3f5b4461247af461d26987f6f1632e
|
|
local kindle_sn_lead = string.sub(kindle_sn, 1, 1)
|
|
|
|
-- NOTE: Update me when new devices come out :)
|
|
-- c.f., https://wiki.mobileread.com/wiki/Kindle_Serial_Numbers for identified variants
|
|
-- c.f., https://github.com/NiLuJe/KindleTool/blob/master/KindleTool/kindle_tool.h#L174 for all variants
|
|
local k2_set = Set { "02", "03" }
|
|
local dx_set = Set { "04", "05" }
|
|
local dxg_set = Set { "09" }
|
|
local k3_set = Set { "08", "06", "0A" }
|
|
local k4_set = Set { "0E", "23" }
|
|
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", "DD" }
|
|
local kv_set = Set { "13", "54", "2A", "4F", "52", "53" }
|
|
local pw3_set = Set { "0G1", "0G2", "0G4", "0G5", "0G6", "0G7",
|
|
"0KB", "0KC", "0KD", "0KE", "0KF", "0KG", "0LK", "0LL" }
|
|
local koa_set = Set { "0GC", "0GD", "0GR", "0GS", "0GT", "0GU" }
|
|
local koa2_set = Set { "0LM", "0LN", "0LP", "0LQ", "0P1", "0P2", "0P6",
|
|
"0P7", "0P8", "0S1", "0S2", "0S3", "0S4", "0S7", "0SA" }
|
|
local kt3_set = Set { "0DU", "0K9", "0KA" }
|
|
local pw4_set = Set { "0PP", "0T1", "0T2", "0T3", "0T4", "0T5", "0T6",
|
|
"0T7", "0TJ", "0TK", "0TL", "0TM", "0TN", "102", "103",
|
|
"16Q", "16R", "16S", "16T", "16U", "16V" }
|
|
local kt4_set = Set { "10L", "0WF", "0WG", "0WH", "0WJ", "0VB" }
|
|
local koa3_set = Set { "11L", "0WQ", "0WP", "0WN", "0WM", "0WL" }
|
|
local pw5_set = Set { "1LG", "1Q0", "1PX", "1VD", "219", "21A", "2BH", "2BJ", "2DK" }
|
|
local kt5_set = Set { "22D", "25T", "23A", "2AQ", "2AP", "1XH", "22C" }
|
|
local ks_set = Set { "27J", "2BL", "263", "227", "2BM", "23L", "23M", "270" }
|
|
|
|
if kindle_sn_lead == "B" or kindle_sn_lead == "9" then
|
|
local kindle_devcode = string.sub(kindle_sn, 3, 4)
|
|
|
|
if k2_set[kindle_devcode] then
|
|
return Kindle2
|
|
elseif dx_set[kindle_devcode] then
|
|
return Kindle2
|
|
elseif dxg_set[kindle_devcode] then
|
|
return KindleDXG
|
|
elseif k3_set[kindle_devcode] then
|
|
return Kindle3
|
|
elseif k4_set[kindle_devcode] then
|
|
return Kindle4
|
|
elseif touch_set[kindle_devcode] then
|
|
return KindleTouch
|
|
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
|
|
else
|
|
local kindle_devcode_v2 = string.sub(kindle_sn, 4, 6)
|
|
|
|
if pw3_set[kindle_devcode_v2] then
|
|
return KindlePaperWhite3
|
|
elseif koa_set[kindle_devcode_v2] then
|
|
return KindleOasis
|
|
elseif koa2_set[kindle_devcode_v2] then
|
|
return KindleOasis2
|
|
elseif kt3_set[kindle_devcode_v2] then
|
|
return KindleBasic2
|
|
elseif pw4_set[kindle_devcode_v2] then
|
|
return KindlePaperWhite4
|
|
elseif kt4_set[kindle_devcode_v2] then
|
|
return KindleBasic3
|
|
elseif koa3_set[kindle_devcode_v2] then
|
|
return KindleOasis3
|
|
elseif pw5_set[kindle_devcode_v2] then
|
|
return KindlePaperWhite5
|
|
elseif kt5_set[kindle_devcode_v2] then
|
|
return KindleBasic4
|
|
elseif ks_set[kindle_devcode_v2] then
|
|
return KindleScribe
|
|
end
|
|
end
|
|
|
|
local kindle_sn_prefix = string.sub(kindle_sn, 1, 6)
|
|
error("unknown Kindle model: " .. kindle_sn_prefix)
|