mirror of https://github.com/koreader/koreader
commit
a300f1e981
@ -1 +1 @@
|
|||||||
Subproject commit 3260f0494f7558f234806e68adb92606af391f8f
|
Subproject commit af763e714395a9bf1a2356f7fb99a020c0387684
|
@ -0,0 +1,29 @@
|
|||||||
|
local isAndroid, android = pcall(require, "android")
|
||||||
|
local util = require("ffi/util")
|
||||||
|
|
||||||
|
local function probeDevice()
|
||||||
|
if util.isEmulated() then
|
||||||
|
return require("device/emulator/device")
|
||||||
|
end
|
||||||
|
|
||||||
|
if isAndroid then
|
||||||
|
return require("device/android/device")
|
||||||
|
end
|
||||||
|
|
||||||
|
local kindle_sn = io.open("/proc/usid", "r")
|
||||||
|
if kindle_sn then
|
||||||
|
kindle_sn:close()
|
||||||
|
return require("device/kindle/device")
|
||||||
|
end
|
||||||
|
|
||||||
|
local kg_test_fd = lfs.attributes("/bin/kobo_config.sh")
|
||||||
|
if kg_test_fd then
|
||||||
|
return require("device/kobo/device")
|
||||||
|
end
|
||||||
|
|
||||||
|
error("did not find a hardware abstraction for this platform")
|
||||||
|
end
|
||||||
|
|
||||||
|
local dev = probeDevice()
|
||||||
|
dev:init()
|
||||||
|
return dev
|
@ -0,0 +1,43 @@
|
|||||||
|
local Generic = require("device/generic/device")
|
||||||
|
local isAndroid, android = pcall(require, "android")
|
||||||
|
local ffi = require("ffi")
|
||||||
|
|
||||||
|
local function yes() return true end
|
||||||
|
|
||||||
|
local Device = Generic:new{
|
||||||
|
model = "Android",
|
||||||
|
isAndroid = yes,
|
||||||
|
firmware_rev = "none",
|
||||||
|
display_dpi = ffi.C.AConfiguration_getDensity(android.app.config),
|
||||||
|
}
|
||||||
|
|
||||||
|
function Device:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
self.powerd = require("device/android/powerd"):new{device = self}
|
||||||
|
self.input = require("device/input"):new{
|
||||||
|
device = self,
|
||||||
|
event_map = require("device/android/event_map"),
|
||||||
|
handleMiscEv = function(self, ev)
|
||||||
|
if ev.code == ffi.C.APP_CMD_SAVE_STATE then
|
||||||
|
return "SaveState"
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- check if we have a keyboard
|
||||||
|
if ffi.C.AConfiguration_getKeyboard(android.app.config)
|
||||||
|
== ffi.C.ACONFIGURATION_KEYBOARD_QWERTY
|
||||||
|
then
|
||||||
|
self.hasKeyboard = yes
|
||||||
|
end
|
||||||
|
-- check if we have a touchscreen
|
||||||
|
if ffi.C.AConfiguration_getTouchscreen(android.app.config)
|
||||||
|
~= ffi.C.ACONFIGURATION_TOUCHSCREEN_NOTOUCH
|
||||||
|
then
|
||||||
|
self.isTouchDevice = yes
|
||||||
|
end
|
||||||
|
|
||||||
|
Generic:init()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Device
|
@ -0,0 +1,33 @@
|
|||||||
|
return {
|
||||||
|
[29] = "A", [30] = "B", [31] = "C", [32] = "D", [33] = "E", [34] = "F",
|
||||||
|
[35] = "G", [36] = "H", [37] = "I", [38] = "J", [39] = "K", [40] = "L",
|
||||||
|
[41] = "M", [42] = "N", [43] = "O", [44] = "P", [45] = "Q", [46] = "R",
|
||||||
|
[47] = "S", [48] = "T", [49] = "U", [50] = "V", [51] = "W", [52] = "X",
|
||||||
|
[53] = "Y", [54] = "Z", [ 7] = "0", [ 8] = "1", [ 9] = "2", [10] = "3",
|
||||||
|
[11] = "4", [12] = "5", [13] = "6", [14] = "7", [15] = "8", [16] = "9",
|
||||||
|
|
||||||
|
[4] = "Back", -- BACK
|
||||||
|
[19] = "Up", -- DPAD_UP
|
||||||
|
[20] = "Down", -- DPAD_UP
|
||||||
|
[21] = "Left", -- DPAD_LEFT
|
||||||
|
[22] = "Right", -- DPAD_RIGHT
|
||||||
|
[23] = "Press", -- DPAD_CENTER
|
||||||
|
[24] = "LPgBack", -- VOLUME_UP
|
||||||
|
[25] = "LPgFwd", -- VOLUME_DOWN
|
||||||
|
[56] = ".", -- PERIOD
|
||||||
|
[59] = "Shift", -- SHIFT_LEFT
|
||||||
|
[60] = "Shift", -- SHIFT_RIGHT
|
||||||
|
[62] = " ", -- SPACE
|
||||||
|
[63] = "Sym", -- SYM
|
||||||
|
[66] = "Enter", -- ENTER
|
||||||
|
[67] = "Del", -- DEL
|
||||||
|
[76] = "/", -- SLASH
|
||||||
|
[82] = "Menu", -- MENU
|
||||||
|
[84] = "Search",--SEARCH
|
||||||
|
[92] = "LPgBack", -- PAGE_UP
|
||||||
|
[93] = "LPgFwd", -- PAGE_DOWN
|
||||||
|
|
||||||
|
[104] = "LPgBack", -- T68 PageUp
|
||||||
|
[109] = "LPgFwd", -- T68 PageDown
|
||||||
|
[139] = "Menu", -- T68 Menu
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
local BasePowerD = require("ui/device/basepowerd")
|
local BasePowerD = require("device/generic/powerd")
|
||||||
|
|
||||||
local AndroidPowerD = BasePowerD:new{
|
local AndroidPowerD = BasePowerD:new{
|
||||||
batt_capacity_file = "/sys/class/power_supply/battery/capacity",
|
batt_capacity_file = "/sys/class/power_supply/battery/capacity",
|
@ -0,0 +1,27 @@
|
|||||||
|
local Generic = require("device/generic/device")
|
||||||
|
local util = require("ffi/util")
|
||||||
|
|
||||||
|
local function yes() return true end
|
||||||
|
|
||||||
|
local Device = Generic:new{
|
||||||
|
model = "Emulator",
|
||||||
|
isEmulator = yes,
|
||||||
|
hasKeyboard = yes,
|
||||||
|
hasKeys = yes,
|
||||||
|
hasFrontlight = yes,
|
||||||
|
isTouchDevice = yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Device:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
self.input = require("device/input"):new{
|
||||||
|
device = self,
|
||||||
|
event_map = util.haveSDL2()
|
||||||
|
and require("device/emulator/event_map_sdl2")
|
||||||
|
or require("device/emulator/event_map_sdl"),
|
||||||
|
}
|
||||||
|
|
||||||
|
Generic.init(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Device
|
@ -0,0 +1,31 @@
|
|||||||
|
return {
|
||||||
|
[10] = "1", [11] = "2", [12] = "3", [13] = "4", [14] = "5", [15] = "6", [16] = "7", [17] = "8", [18] = "9", [19] = "0",
|
||||||
|
[24] = "Q", [25] = "W", [26] = "E", [27] = "R", [28] = "T", [29] = "Y", [30] = "U", [31] = "I", [32] = "O", [33] = "P",
|
||||||
|
[38] = "A", [39] = "S", [40] = "D", [41] = "F", [42] = "G", [43] = "H", [44] = "J", [45] = "K", [46] = "L",
|
||||||
|
[52] = "Z", [53] = "X", [54] = "C", [55] = "V", [56] = "B", [57] = "N", [58] = "M",
|
||||||
|
|
||||||
|
[22] = "Back", -- Backspace
|
||||||
|
[36] = "Enter", -- Enter
|
||||||
|
[50] = "Shift", -- left shift
|
||||||
|
[60] = ".",
|
||||||
|
[61] = "/",
|
||||||
|
[62] = "Sym", -- right shift key
|
||||||
|
[64] = "Alt", -- left alt
|
||||||
|
[65] = " ", -- Spacebar
|
||||||
|
[67] = "Menu", -- F[1]
|
||||||
|
[68] = "Power", -- F[2]
|
||||||
|
[72] = "LPgBack", -- F[6]
|
||||||
|
[73] = "LPgFwd", -- F[7]
|
||||||
|
[95] = "VPlus", -- F[11]
|
||||||
|
[96] = "VMinus", -- F[12]
|
||||||
|
[105] = "AA", -- right alt key
|
||||||
|
[110] = "Home", -- Home
|
||||||
|
[111] = "Up", -- arrow up
|
||||||
|
[112] = "RPgBack", -- normal PageUp
|
||||||
|
[113] = "Left", -- arrow left
|
||||||
|
[114] = "Right", -- arrow right
|
||||||
|
[115] = "Press", -- End (above arrows)
|
||||||
|
[116] = "Down", -- arrow down
|
||||||
|
[117] = "RPgFwd", -- normal PageDown
|
||||||
|
[119] = "Del", -- Delete
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
return {
|
||||||
|
[ 4] = "A", [ 5] = "B", [ 6] = "C", [ 7] = "D", [ 8] = "E", [ 9] = "F",
|
||||||
|
[10] = "G", [11] = "H", [12] = "I", [13] = "J", [14] = "K", [15] = "L",
|
||||||
|
[16] = "M", [17] = "N", [18] = "O", [19] = "P", [20] = "Q", [21] = "R",
|
||||||
|
[22] = "S", [23] = "T", [24] = "U", [25] = "V", [26] = "W", [27] = "X",
|
||||||
|
[28] = "Y", [29] = "Z", [30] = "1", [31] = "2", [32] = "3", [33] = "4",
|
||||||
|
[34] = "5", [35] = "6", [36] = "7", [37] = "8", [38] = "9", [39] = "0",
|
||||||
|
|
||||||
|
[42] = "Back", -- Backspace
|
||||||
|
[40] = "Enter", -- Enter
|
||||||
|
[225] = "Shift", -- left shift
|
||||||
|
[55] = ".",
|
||||||
|
[56] = "/",
|
||||||
|
[229] = "Sym", -- right shift key
|
||||||
|
[226] = "Alt", -- left alt
|
||||||
|
[44] = " ", -- Spacebar
|
||||||
|
[58] = "Menu", -- F[1]
|
||||||
|
[59] = "Power", -- F[2]
|
||||||
|
[63] = "LPgBack", -- F[6]
|
||||||
|
[64] = "LPgFwd", -- F[7]
|
||||||
|
[68] = "VPlus", -- F[11]
|
||||||
|
[69] = "VMinus", -- F[12]
|
||||||
|
[230] = "AA", -- right alt key
|
||||||
|
[74] = "Home", -- Home
|
||||||
|
[82] = "Up", -- arrow up
|
||||||
|
[75] = "RPgBack", -- normal PageUp
|
||||||
|
[80] = "Left", -- arrow left
|
||||||
|
[79] = "Right", -- arrow right
|
||||||
|
[77] = "Press", -- End (above arrows)
|
||||||
|
[81] = "Down", -- arrow down
|
||||||
|
[78] = "RPgFwd", -- normal PageDown
|
||||||
|
[76] = "Del", -- Delete
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
local util = require("ffi/util")
|
||||||
|
local DEBUG = require("dbg")
|
||||||
|
|
||||||
|
local function no() return false end
|
||||||
|
|
||||||
|
local Device = {
|
||||||
|
screen_saver_mode = false,
|
||||||
|
charging_mode = false,
|
||||||
|
survive_screen_saver = false,
|
||||||
|
model = nil,
|
||||||
|
powerd = nil,
|
||||||
|
screen = nil,
|
||||||
|
input = nil,
|
||||||
|
|
||||||
|
-- hardware feature tests: (these are functions!)
|
||||||
|
hasKeyboard = no,
|
||||||
|
hasKeys = no,
|
||||||
|
isTouchDevice = no,
|
||||||
|
hasFrontlight = no,
|
||||||
|
|
||||||
|
-- use these only as a last resort. We should abstract the functionality
|
||||||
|
-- and have device dependent implementations in the corresponting
|
||||||
|
-- device/<devicetype>/device.lua file
|
||||||
|
-- (these are functions!)
|
||||||
|
isKindle = no,
|
||||||
|
isKobo = no,
|
||||||
|
isAndroid = no,
|
||||||
|
isEmulator = no,
|
||||||
|
|
||||||
|
-- some devices have part of their screen covered by the bezel
|
||||||
|
viewport = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Device:new(o)
|
||||||
|
local o = o or {}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:init()
|
||||||
|
if not self.screen then
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
end
|
||||||
|
if not self.input then
|
||||||
|
self.input = require("device/input"):new{device = self}
|
||||||
|
end
|
||||||
|
if not self.powerd then
|
||||||
|
self.powerd = require("device/generic/powerd"):new{device = self}
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.viewport then
|
||||||
|
self.screen:setViewport(self.viewport)
|
||||||
|
self.input:registerEventAdjustHook(
|
||||||
|
self.input.adjustTouchTranslate,
|
||||||
|
{x = 0 - self.viewport.x, y = 0 - self.viewport.y})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:getPowerDevice()
|
||||||
|
return self.powerd
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:intoScreenSaver()
|
||||||
|
if self.charging_mode == false and self.screen_saver_mode == false then
|
||||||
|
self.screen:saveCurrentBB()
|
||||||
|
self.screen_saver_mode = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:outofScreenSaver()
|
||||||
|
if self.screen_saver_mode == true and self.charging_mode == false then
|
||||||
|
-- wait for native system update screen before we recover saved
|
||||||
|
-- Blitbuffer.
|
||||||
|
util.usleep(1500000)
|
||||||
|
self.screen:restoreFromSavedBB()
|
||||||
|
self.screen:refresh(0)
|
||||||
|
self.survive_screen_saver = true
|
||||||
|
end
|
||||||
|
self.screen_saver_mode = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:onPowerEvent(ev)
|
||||||
|
local Screensaver = require("ui/screensaver")
|
||||||
|
if (ev == "Power" or ev == "Suspend") and not self.screen_saver_mode then
|
||||||
|
local UIManager = require("ui/uimanager")
|
||||||
|
DEBUG("Suspending...")
|
||||||
|
-- always suspend in portrait mode
|
||||||
|
self.orig_rotation_mode = self.screen:getRotationMode()
|
||||||
|
self.screen:setRotationMode(0)
|
||||||
|
Screensaver:show()
|
||||||
|
self:prepareSuspend()
|
||||||
|
UIManager:scheduleIn(2, function() self:Suspend() end)
|
||||||
|
elseif (ev == "Power" or ev == "Resume") and self.screen_saver_mode then
|
||||||
|
DEBUG("Resuming...")
|
||||||
|
-- restore to previous rotation mode
|
||||||
|
self.screen:setRotationMode(self.orig_rotation_mode)
|
||||||
|
self:Resume()
|
||||||
|
Screensaver:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:prepareSuspend()
|
||||||
|
if self.powerd and self.powerd.fl ~= nil then
|
||||||
|
-- in no case should the frontlight be turned on in suspend mode
|
||||||
|
self.powerd.fl:sleep()
|
||||||
|
end
|
||||||
|
self.screen:refresh(0)
|
||||||
|
self.screen_saver_mode = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:Suspend()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:Resume()
|
||||||
|
self.screen:refresh(1)
|
||||||
|
self.screen_saver_mode = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:usbPlugIn()
|
||||||
|
if self.charging_mode == false and self.screen_saver_mode == false then
|
||||||
|
self.screen:saveCurrentBB()
|
||||||
|
end
|
||||||
|
self.charging_mode = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function Device:usbPlugOut()
|
||||||
|
if self.charging_mode == true and self.screen_saver_mode == false then
|
||||||
|
self.screen:restoreFromSavedBB()
|
||||||
|
self.screen:refresh(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
--@TODO signal filemanager for file changes 13.06 2012 (houqp)
|
||||||
|
self.charging_mode = false
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
prepare for application shutdown
|
||||||
|
--]]
|
||||||
|
function Device:exit()
|
||||||
|
require("ffi/input"):closeAll()
|
||||||
|
self.screen:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Device
|
@ -0,0 +1,518 @@
|
|||||||
|
local Event = require("ui/event")
|
||||||
|
local TimeVal = require("ui/timeval")
|
||||||
|
local input = require("ffi/input")
|
||||||
|
local util = require("ffi/util")
|
||||||
|
local Math = require("optmath")
|
||||||
|
local DEBUG = require("dbg")
|
||||||
|
local ffi = require("ffi")
|
||||||
|
local _ = require("gettext")
|
||||||
|
local Key = require("device/key")
|
||||||
|
local GestureDetector = require("device/gesturedetector")
|
||||||
|
|
||||||
|
-- constants from <linux/input.h>
|
||||||
|
local EV_SYN = 0
|
||||||
|
local EV_KEY = 1
|
||||||
|
local EV_ABS = 3
|
||||||
|
local EV_MSC = 4
|
||||||
|
|
||||||
|
-- key press event values (KEY.value)
|
||||||
|
local EVENT_VALUE_KEY_PRESS = 1
|
||||||
|
local EVENT_VALUE_KEY_REPEAT = 2
|
||||||
|
local EVENT_VALUE_KEY_RELEASE = 0
|
||||||
|
|
||||||
|
-- Synchronization events (SYN.code).
|
||||||
|
local SYN_REPORT = 0
|
||||||
|
local SYN_CONFIG = 1
|
||||||
|
local SYN_MT_REPORT = 2
|
||||||
|
|
||||||
|
-- For single-touch events (ABS.code).
|
||||||
|
local ABS_X = 00
|
||||||
|
local ABS_Y = 01
|
||||||
|
local ABS_PRESSURE = 24
|
||||||
|
|
||||||
|
-- For multi-touch events (ABS.code).
|
||||||
|
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
|
||||||
|
|
||||||
|
--[[
|
||||||
|
an interface to get input events
|
||||||
|
]]
|
||||||
|
local Input = {
|
||||||
|
-- this depends on keyboard layout and should be overridden:
|
||||||
|
event_map = {},
|
||||||
|
|
||||||
|
group = {
|
||||||
|
Cursor = { "Up", "Down", "Left", "Right" },
|
||||||
|
PgFwd = { "RPgFwd", "LPgFwd" },
|
||||||
|
PgBack = { "RPgBack", "LPgBack" },
|
||||||
|
Alphabet = {
|
||||||
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
||||||
|
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
||||||
|
},
|
||||||
|
AlphaNumeric = {
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
||||||
|
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
||||||
|
},
|
||||||
|
Numeric = {
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
|
||||||
|
},
|
||||||
|
Text = {
|
||||||
|
" ", ".", "/",
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
||||||
|
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
||||||
|
},
|
||||||
|
Any = {
|
||||||
|
" ", ".", "/",
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
||||||
|
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
||||||
|
"Up", "Down", "Left", "Right", "Press",
|
||||||
|
"Back", "Enter", "Sym", "AA", "Menu", "Home", "Del",
|
||||||
|
"LPgBack", "RPgBack", "LPgFwd", "RPgFwd"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
rotation_map = {
|
||||||
|
[0] = {},
|
||||||
|
[1] = { Up = "Right", Right = "Down", Down = "Left", Left = "Up" },
|
||||||
|
[2] = { Up = "Down", Right = "Left", Down = "Up", Left = "Right" },
|
||||||
|
[3] = { Up = "Left", Right = "Up", Down = "Right", Left = "Down" }
|
||||||
|
},
|
||||||
|
|
||||||
|
timer_callbacks = {},
|
||||||
|
disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP,
|
||||||
|
|
||||||
|
-- keyboard state:
|
||||||
|
modifiers = {
|
||||||
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- touch state:
|
||||||
|
cur_slot = 0,
|
||||||
|
MTSlots = {},
|
||||||
|
ev_slots = {
|
||||||
|
[0] = {
|
||||||
|
slot = 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gesture_detector = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Input:new(o)
|
||||||
|
local o = o or {}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
if o.init then o:init() end
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:init()
|
||||||
|
self.gesture_detector = GestureDetector:new{
|
||||||
|
screen = self.device.screen,
|
||||||
|
input = self,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- set up fake event map
|
||||||
|
self.event_map[10000] = "IntoSS" -- go into screen saver
|
||||||
|
self.event_map[10001] = "OutOfSS" -- go out of screen saver
|
||||||
|
self.event_map[10020] = "Charging"
|
||||||
|
self.event_map[10021] = "NotCharging"
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
wrapper for FFI input open
|
||||||
|
|
||||||
|
Note that we adhere to the "." syntax here for compatibility.
|
||||||
|
TODO: clean up separation FFI/this
|
||||||
|
--]]
|
||||||
|
function Input.open(device)
|
||||||
|
input.open(device)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Different device models can implement their own hooks
|
||||||
|
and register them.
|
||||||
|
--]]
|
||||||
|
function Input:registerEventAdjustHook(hook, hook_params)
|
||||||
|
local old = self.eventAdjustHook
|
||||||
|
self.eventAdjustHook = function(self, ev)
|
||||||
|
old(self, ev)
|
||||||
|
hook(self, ev, hook_params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function Input:eventAdjustHook(ev)
|
||||||
|
-- do nothing by default
|
||||||
|
end
|
||||||
|
-- catalogue of predefined hooks:
|
||||||
|
function Input:adjustTouchSwitchXY(ev)
|
||||||
|
if ev.type == EV_ABS then
|
||||||
|
if ev.code == ABS_X then ev.code = ABS_Y end
|
||||||
|
if ev.code == ABS_Y then ev.code = ABS_X end
|
||||||
|
if ev.code == ABS_MT_POSITION_X then ev.code = ABS_MT_POSITION_Y end
|
||||||
|
if ev.code == ABS_MT_POSITION_Y then ev.code = ABS_MT_POSITION_X end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function Input:adjustTouchScale(ev, by)
|
||||||
|
if ev.type == EV_ABS then
|
||||||
|
if ev.code == ABS_X or ev.code == ABS_MT_POSITION_X then
|
||||||
|
ev.value = by.x * ev.value
|
||||||
|
end
|
||||||
|
if ev.code == ABS_Y or ev.code == ABS_MT_POSITION_Y then
|
||||||
|
ev.value = by.y * ev.value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function Input:adjustTouchMirrorX(ev, width)
|
||||||
|
if ev.type == EV_ABS then
|
||||||
|
if ev.code == ABS_X or ev.code == ABS_MT_POSITION_X then
|
||||||
|
ev.value = width - ev.value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function Input:adjustTouchMirrorY(ev, height)
|
||||||
|
if ev.type == EV_ABS then
|
||||||
|
if ev.code == ABS_Y or ev.code == ABS_MT_POSITION_Y then
|
||||||
|
ev.value = height - ev.value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function Input:adjustTouchTranslate(ev, by)
|
||||||
|
if ev.type == EV_ABS then
|
||||||
|
if ev.code == ABS_X or ev.code == ABS_MT_POSITION_X then
|
||||||
|
ev.value = by.x + ev.value
|
||||||
|
end
|
||||||
|
if ev.code == ABS_Y or ev.code == ABS_MT_POSITION_Y then
|
||||||
|
ev.value = by.y + ev.value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:setTimeout(cb, tv_out)
|
||||||
|
local item = {
|
||||||
|
callback = cb,
|
||||||
|
deadline = tv_out,
|
||||||
|
}
|
||||||
|
table.insert(self.timer_callbacks, item)
|
||||||
|
table.sort(self.timer_callbacks, function(v1,v2)
|
||||||
|
return v1.deadline < v2.deadline
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:handleKeyBoardEv(ev)
|
||||||
|
local keycode = self.event_map[ev.code]
|
||||||
|
if not keycode then
|
||||||
|
-- do not handle keypress for keys we don't know
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- take device rotation into account
|
||||||
|
if self.rotation_map[self.device.screen:getRotationMode()][keycode] then
|
||||||
|
keycode = self.rotation_map[self.device.screen:getRotationMode()][keycode]
|
||||||
|
end
|
||||||
|
|
||||||
|
if keycode == "IntoSS" or keycode == "OutOfSS"
|
||||||
|
or keycode == "Charging" or keycode == "NotCharging" then
|
||||||
|
return keycode
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Kobo sleep
|
||||||
|
if keycode == "Power_SleepCover" then
|
||||||
|
if ev.value == EVENT_VALUE_KEY_PRESS then
|
||||||
|
return "Suspend"
|
||||||
|
else
|
||||||
|
return "Resume"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ev.value == EVENT_VALUE_KEY_RELEASE
|
||||||
|
and (keycode == "Light" or keycode == "Power") then
|
||||||
|
return keycode
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle modifier keys
|
||||||
|
if self.modifiers[keycode] ~= nil then
|
||||||
|
if ev.value == EVENT_VALUE_KEY_PRESS then
|
||||||
|
self.modifiers[keycode] = true
|
||||||
|
elseif ev.value == EVENT_VALUE_KEY_RELEASE then
|
||||||
|
self.modifiers[keycode] = false
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local key = Key:new(keycode, self.modifiers)
|
||||||
|
|
||||||
|
if ev.value == EVENT_VALUE_KEY_PRESS then
|
||||||
|
return Event:new("KeyPress", key)
|
||||||
|
elseif ev.value == EVENT_VALUE_KEY_RELEASE then
|
||||||
|
return Event:new("KeyRelease", key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:handleMiscEv(ev)
|
||||||
|
-- should be handled by a misc event protocol plugin
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
parse each touch ev from kernel and build up tev.
|
||||||
|
tev will be sent to GestureDetector:feedEvent
|
||||||
|
|
||||||
|
Events for a single tap motion from Linux kernel (MT protocol B):
|
||||||
|
|
||||||
|
MT_TRACK_ID: 0
|
||||||
|
MT_X: 222
|
||||||
|
MT_Y: 207
|
||||||
|
SYN REPORT
|
||||||
|
MT_TRACK_ID: -1
|
||||||
|
SYN REPORT
|
||||||
|
|
||||||
|
Notice that each line is a single event.
|
||||||
|
|
||||||
|
From kernel document:
|
||||||
|
For type B devices, the kernel driver should associate a slot with each
|
||||||
|
identified contact, and use that slot to propagate changes for the contact.
|
||||||
|
Creation, replacement and destruction of contacts is achieved by modifying
|
||||||
|
the ABS_MT_TRACKING_ID of the associated slot. A non-negative tracking id
|
||||||
|
is interpreted as a contact, and the value -1 denotes an unused slot. A
|
||||||
|
tracking id not previously present is considered new, and a tracking id no
|
||||||
|
longer present is considered removed. Since only changes are propagated,
|
||||||
|
the full state of each initiated contact has to reside in the receiving
|
||||||
|
end. Upon receiving an MT event, one simply updates the appropriate
|
||||||
|
attribute of the current slot.
|
||||||
|
--]]
|
||||||
|
function Input:handleTouchEv(ev)
|
||||||
|
if ev.type == EV_ABS then
|
||||||
|
if #self.MTSlots == 0 then
|
||||||
|
table.insert(self.MTSlots, self:getMtSlot(self.cur_slot))
|
||||||
|
end
|
||||||
|
if ev.code == ABS_MT_SLOT then
|
||||||
|
if self.cur_slot ~= ev.value then
|
||||||
|
table.insert(self.MTSlots, self:getMtSlot(ev.value))
|
||||||
|
end
|
||||||
|
self.cur_slot = ev.value
|
||||||
|
elseif ev.code == ABS_MT_TRACKING_ID then
|
||||||
|
self:setCurrentMtSlot("id", ev.value)
|
||||||
|
elseif ev.code == ABS_MT_POSITION_X then
|
||||||
|
self:setCurrentMtSlot("x", ev.value)
|
||||||
|
elseif ev.code == ABS_MT_POSITION_Y then
|
||||||
|
self:setCurrentMtSlot("y", ev.value)
|
||||||
|
|
||||||
|
-- code to emulate mt protocol on kobos
|
||||||
|
-- we "confirm" abs_x, abs_y only when pressure ~= 0
|
||||||
|
elseif ev.code == ABS_X then
|
||||||
|
self:setCurrentMtSlot("abs_x", ev.value)
|
||||||
|
elseif ev.code == ABS_Y then
|
||||||
|
self:setCurrentMtSlot("abs_y", ev.value)
|
||||||
|
elseif ev.code == ABS_PRESSURE then
|
||||||
|
if ev.value ~= 0 then
|
||||||
|
self:setCurrentMtSlot("id", 1)
|
||||||
|
self:confirmAbsxy()
|
||||||
|
else
|
||||||
|
self:cleanAbsxy()
|
||||||
|
self:setCurrentMtSlot("id", -1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif ev.type == EV_SYN then
|
||||||
|
if ev.code == SYN_REPORT then
|
||||||
|
for _, MTSlot in pairs(self.MTSlots) do
|
||||||
|
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
|
||||||
|
end
|
||||||
|
-- feed ev in all slots to state machine
|
||||||
|
local touch_ges = self.gesture_detector:feedEvent(self.MTSlots)
|
||||||
|
self.MTSlots = {}
|
||||||
|
if touch_ges then
|
||||||
|
return Event:new("Gesture",
|
||||||
|
self.gesture_detector:adjustGesCoordinate(touch_ges)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function Input:handleTouchEvPhoenix(ev)
|
||||||
|
-- Hack on handleTouchEV for the Kobo Aura
|
||||||
|
-- It seems to be using a custom protocol:
|
||||||
|
-- finger 0 down:
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_TRACKING_ID, 0);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_TOUCH_MAJOR, 1);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_WIDTH_MAJOR, 1);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_X, x1);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_Y, y1);
|
||||||
|
-- input_mt_sync (elan_touch_data.input);
|
||||||
|
-- finger 1 down:
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_TRACKING_ID, 1);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_TOUCH_MAJOR, 1);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_WIDTH_MAJOR, 1);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_X, x2);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_Y, y2);
|
||||||
|
-- input_mt_sync (elan_touch_data.input);
|
||||||
|
-- finger 0 up:
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_TRACKING_ID, 0);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_TOUCH_MAJOR, 0);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_WIDTH_MAJOR, 0);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_X, last_x);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_Y, last_y);
|
||||||
|
-- input_mt_sync (elan_touch_data.input);
|
||||||
|
-- finger 1 up:
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_TRACKING_ID, 1);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_TOUCH_MAJOR, 0);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_WIDTH_MAJOR, 0);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_X, last_x2);
|
||||||
|
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_Y, last_y2);
|
||||||
|
-- input_mt_sync (elan_touch_data.input);
|
||||||
|
if ev.type == EV_ABS then
|
||||||
|
if #self.MTSlots == 0 then
|
||||||
|
table.insert(self.MTSlots, self:getMtSlot(self.cur_slot))
|
||||||
|
end
|
||||||
|
if ev.code == ABS_MT_TRACKING_ID then
|
||||||
|
if self.cur_slot ~= ev.value then
|
||||||
|
table.insert(self.MTSlots, self:getMtSlot(ev.value))
|
||||||
|
end
|
||||||
|
self.cur_slot = ev.value
|
||||||
|
self:setCurrentMtSlot("id", ev.value)
|
||||||
|
elseif ev.code == ABS_MT_TOUCH_MAJOR and ev.value == 0 then
|
||||||
|
self:setCurrentMtSlot("id", -1)
|
||||||
|
elseif ev.code == ABS_MT_POSITION_X then
|
||||||
|
self:setCurrentMtSlot("x", ev.value)
|
||||||
|
elseif ev.code == ABS_MT_POSITION_Y then
|
||||||
|
self:setCurrentMtSlot("y", ev.value)
|
||||||
|
end
|
||||||
|
elseif ev.type == EV_SYN then
|
||||||
|
if ev.code == SYN_REPORT then
|
||||||
|
for _, MTSlot in pairs(self.MTSlots) do
|
||||||
|
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
|
||||||
|
end
|
||||||
|
-- feed ev in all slots to state machine
|
||||||
|
local touch_ges = self.gesture_detector:feedEvent(self.MTSlots)
|
||||||
|
self.MTSlots = {}
|
||||||
|
if touch_ges then
|
||||||
|
return Event:new("Gesture",
|
||||||
|
self.gesture_detector:adjustGesCoordinate(touch_ges)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- helpers for touch event data management:
|
||||||
|
|
||||||
|
function Input:setMtSlot(slot, key, val)
|
||||||
|
if not self.ev_slots[slot] then
|
||||||
|
self.ev_slots[slot] = {
|
||||||
|
slot = slot
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.ev_slots[slot][key] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:setCurrentMtSlot(key, val)
|
||||||
|
self:setMtSlot(self.cur_slot, key, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:getMtSlot(slot)
|
||||||
|
return self.ev_slots[slot]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:getCurrentMtSlot()
|
||||||
|
return self:getMtSlot(self.cur_slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:confirmAbsxy()
|
||||||
|
self:setCurrentMtSlot("x", self.ev_slots[self.cur_slot]["abs_x"])
|
||||||
|
self:setCurrentMtSlot("y", self.ev_slots[self.cur_slot]["abs_y"])
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:cleanAbsxy()
|
||||||
|
self:setCurrentMtSlot("abs_x", nil)
|
||||||
|
self:setCurrentMtSlot("abs_y", nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- main event handling:
|
||||||
|
|
||||||
|
function Input:waitEvent(timeout_us, timeout_s)
|
||||||
|
-- wrapper for input.waitForEvents that will retry for some cases
|
||||||
|
local ok, ev
|
||||||
|
local wait_deadline = TimeVal:now() + TimeVal:new{
|
||||||
|
sec = timeout_s,
|
||||||
|
usec = timeout_us
|
||||||
|
}
|
||||||
|
while true do
|
||||||
|
if #self.timer_callbacks > 0 then
|
||||||
|
-- we don't block if there is any timer, set wait to 10us
|
||||||
|
while #self.timer_callbacks > 0 do
|
||||||
|
ok, ev = pcall(input.waitForEvent, 100)
|
||||||
|
if ok then break end
|
||||||
|
local tv_now = TimeVal:now()
|
||||||
|
if ((not timeout_us and not timeout_s) or tv_now < wait_deadline) then
|
||||||
|
-- check whether timer is up
|
||||||
|
if tv_now >= self.timer_callbacks[1].deadline then
|
||||||
|
local touch_ges = self.timer_callbacks[1].callback()
|
||||||
|
table.remove(self.timer_callbacks, 1)
|
||||||
|
if touch_ges then
|
||||||
|
-- Do we really need to clear all setTimeout after
|
||||||
|
-- decided a gesture? FIXME
|
||||||
|
self.timer_callbacks = {}
|
||||||
|
return Event:new("Gesture",
|
||||||
|
self.gesture_detector:adjustGesCoordinate(touch_ges)
|
||||||
|
)
|
||||||
|
end -- EOF if touch_ges
|
||||||
|
end -- EOF if deadline reached
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end -- EOF if not exceed wait timeout
|
||||||
|
end -- while #timer_callbacks > 0
|
||||||
|
else
|
||||||
|
ok, ev = pcall(input.waitForEvent, timeout_us)
|
||||||
|
end -- EOF if #timer_callbacks > 0
|
||||||
|
if ok then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ev does contain an error message:
|
||||||
|
if ev == "Waiting for input failed: timeout\n" then
|
||||||
|
-- don't report an error on timeout
|
||||||
|
ev = nil
|
||||||
|
break
|
||||||
|
elseif ev == "application forced to quit" then
|
||||||
|
-- TODO: return an event that can be handled
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
--DEBUG("got error waiting for events:", ev)
|
||||||
|
if ev ~= "Waiting for input failed: 4\n" then
|
||||||
|
-- we only abort if the error is not EINTR
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ok and ev then
|
||||||
|
if DEBUG.is_on and ev then
|
||||||
|
DEBUG:logEv(ev)
|
||||||
|
end
|
||||||
|
self:eventAdjustHook(ev)
|
||||||
|
if ev.type == EV_KEY then
|
||||||
|
DEBUG("key ev", ev)
|
||||||
|
return self:handleKeyBoardEv(ev)
|
||||||
|
elseif ev.type == EV_ABS or ev.type == EV_SYN then
|
||||||
|
return self:handleTouchEv(ev)
|
||||||
|
elseif ev.type == EV_MSC then
|
||||||
|
return self:handleMiscEv(ev)
|
||||||
|
else
|
||||||
|
-- some other kind of event that we do not know yet
|
||||||
|
return Event:new("GenericInput", ev)
|
||||||
|
end
|
||||||
|
elseif not ok and ev then
|
||||||
|
return Event:new("InputError", ev)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Input
|
@ -0,0 +1,91 @@
|
|||||||
|
--[[
|
||||||
|
an interface for key presses
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Key = {}
|
||||||
|
|
||||||
|
function Key:new(key, modifiers)
|
||||||
|
local o = { key = key, modifiers = modifiers }
|
||||||
|
|
||||||
|
-- we're a hash map, too
|
||||||
|
o[key] = true
|
||||||
|
for mod, pressed in pairs(modifiers) do
|
||||||
|
if pressed then
|
||||||
|
o[mod] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Key:__tostring()
|
||||||
|
return table.concat(self:getSequence(), "-")
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
get a sequence that can be matched against later
|
||||||
|
|
||||||
|
use this to let the user press a sequence and then
|
||||||
|
store this as configuration data (configurable
|
||||||
|
shortcuts)
|
||||||
|
]]
|
||||||
|
function Key:getSequence()
|
||||||
|
local seq = {}
|
||||||
|
for mod, pressed in pairs(self.modifiers) do
|
||||||
|
if pressed then
|
||||||
|
table.insert(seq, mod)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(seq, self.key)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
this will match a key against a sequence
|
||||||
|
|
||||||
|
the sequence should be a table of key names that
|
||||||
|
must be pressed together to match.
|
||||||
|
if an entry in this table is itself a table, at
|
||||||
|
least one key in this table must match.
|
||||||
|
|
||||||
|
E.g.:
|
||||||
|
|
||||||
|
Key:match({ "Alt", "K" }) -- match Alt-K
|
||||||
|
Key:match({ "Alt", { "K", "L" }}) -- match Alt-K _or_ Alt-L
|
||||||
|
]]
|
||||||
|
function Key:match(sequence)
|
||||||
|
local mod_keys = {} -- a hash table for checked modifiers
|
||||||
|
for _, key in ipairs(sequence) do
|
||||||
|
if type(key) == "table" then
|
||||||
|
local found = false
|
||||||
|
for _, variant in ipairs(key) do
|
||||||
|
if self[variant] then
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not found then
|
||||||
|
-- one of the needed keys is not pressed
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
elseif not self[key] then
|
||||||
|
-- needed key not pressed
|
||||||
|
return false
|
||||||
|
elseif self.modifiers[key] ~= nil then
|
||||||
|
-- checked key is a modifier key
|
||||||
|
mod_keys[key] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for mod, pressed in pairs(self.modifiers) do
|
||||||
|
if pressed and not mod_keys[mod] then
|
||||||
|
-- additional modifier keys are pressed, don't match
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return Key
|
@ -0,0 +1,245 @@
|
|||||||
|
local Generic = require("device/generic/device")
|
||||||
|
local DEBUG = require("dbg")
|
||||||
|
|
||||||
|
local function yes() return true end
|
||||||
|
|
||||||
|
local Kindle = Generic:new{
|
||||||
|
model = "Kindle",
|
||||||
|
isKindle = yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
local Kindle2 = Kindle:new{
|
||||||
|
model = "Kindle2",
|
||||||
|
hasKeyboard = yes,
|
||||||
|
hasKeys = yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
local KindleDXG = Kindle:new{
|
||||||
|
model = "KindleDXG",
|
||||||
|
hasKeyboard = yes,
|
||||||
|
hasKeys = yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
local Kindle3 = Kindle2:new{
|
||||||
|
model = "Kindle3",
|
||||||
|
hasKeyboard = yes,
|
||||||
|
hasKeys = yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
local Kindle4 = Kindle:new{
|
||||||
|
model = "Kindle4",
|
||||||
|
hasKeys = yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
local KindleTouch = Kindle:new{
|
||||||
|
model = "KindleTouch",
|
||||||
|
isTouchDevice = yes,
|
||||||
|
touch_dev = "/dev/input/event3",
|
||||||
|
}
|
||||||
|
|
||||||
|
local KindlePaperWhite = Kindle:new{
|
||||||
|
model = "KindlePaperWhite",
|
||||||
|
isTouchDevice = yes,
|
||||||
|
hasFrontlight = yes,
|
||||||
|
display_dpi = 212,
|
||||||
|
touch_dev = "/dev/input/event0",
|
||||||
|
}
|
||||||
|
|
||||||
|
local KindlePaperWhite2 = Kindle:new{
|
||||||
|
model = "KindlePaperWhite2",
|
||||||
|
isTouchDevice = yes,
|
||||||
|
hasFrontlight = yes,
|
||||||
|
display_dpi = 212,
|
||||||
|
touch_dev = "/dev/input/event1",
|
||||||
|
}
|
||||||
|
|
||||||
|
function Kindle2:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
self.input = require("device/input"):new{
|
||||||
|
device = self,
|
||||||
|
event_map = require("device/kindle/event_map_keyboard"),
|
||||||
|
}
|
||||||
|
self.input.open("/dev/input/event1")
|
||||||
|
Kindle.init(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function KindleDXG:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
self.input = require("device/input"):new{
|
||||||
|
device = self,
|
||||||
|
event_map = require("device/kindle/event_map_keyboard"),
|
||||||
|
}
|
||||||
|
self.input.open("/dev/input/event0")
|
||||||
|
self.input.open("/dev/input/event1")
|
||||||
|
Kindle.init(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Kindle3:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
self.input = require("device/input"):new{
|
||||||
|
device = self,
|
||||||
|
event_map = require("device/kindle/event_map_keyboard"),
|
||||||
|
}
|
||||||
|
self.input.open("/dev/input/event1")
|
||||||
|
self.input.open("/dev/input/event2")
|
||||||
|
Kindle.init(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Kindle4:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
self.input = require("device/input"):new{
|
||||||
|
device = self,
|
||||||
|
event_map = require("device/kindle/event_map_kindle4"),
|
||||||
|
}
|
||||||
|
self.input.event_map = require("device/kindle/event_map_kindle4")
|
||||||
|
self.input.open("/dev/input/event1")
|
||||||
|
Kindle.init(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ABS_MT_POSITION_X = 53
|
||||||
|
local ABS_MT_POSITION_Y = 54
|
||||||
|
function KindleTouch:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
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})
|
||||||
|
|
||||||
|
-- event0 in KindleTouch is "WM8962 Beep Generator" (useless)
|
||||||
|
-- event1 in KindleTouch is "imx-yoshi Headset" (useless)
|
||||||
|
self.input.open("/dev/input/event2") -- Home button
|
||||||
|
self.input.open("/dev/input/event3") -- touchscreen
|
||||||
|
Kindle.init(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function KindlePaperWhite:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
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)
|
||||||
|
|
||||||
|
self.input.open("/dev/input/event0")
|
||||||
|
end
|
||||||
|
|
||||||
|
function KindlePaperWhite2: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/yoshi_battery/yoshi_battery0/battery_capacity",
|
||||||
|
is_charging_file = "/sys/devices/platform/aplite_charger.0/charging",
|
||||||
|
}
|
||||||
|
|
||||||
|
Kindle.init(self)
|
||||||
|
|
||||||
|
self.input.open("/dev/input/event1")
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Test if a kindle device has Special Offers
|
||||||
|
--]]
|
||||||
|
local function isSpecialOffers()
|
||||||
|
-- Look at the current blanket modules to see if the SO screensavers are enabled...
|
||||||
|
local lipc = require("liblipclua")
|
||||||
|
if not lipc then
|
||||||
|
DEBUG("could not load liblibclua")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local lipc_handle = lipc.init("com.github.koreader.device")
|
||||||
|
if not lipc_handle then
|
||||||
|
DEBUG("could not get lipc handle")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local so = false
|
||||||
|
local loaded_blanket_modules = lipc_handle:get_string_property("com.lab126.blanket", "load")
|
||||||
|
if string.find(loaded_blanket_modules, "ad_screensaver") then
|
||||||
|
so = true
|
||||||
|
end
|
||||||
|
lipc_handle:close()
|
||||||
|
return so
|
||||||
|
end
|
||||||
|
|
||||||
|
function KindleTouch:exit()
|
||||||
|
if isSpecialOffers() then
|
||||||
|
-- fake a touch event
|
||||||
|
if self.touch_dev then
|
||||||
|
local width, height = Screen:getScreenWidth(), Screen:getScreenHeight()
|
||||||
|
require("ffi/input").fakeTapInput(self.touch_dev,
|
||||||
|
math.min(width, height)/2,
|
||||||
|
math.max(width, height)-30
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Generic.exit(self)
|
||||||
|
end
|
||||||
|
KindlePaperWhite.exit = KindleTouch.exit
|
||||||
|
KindlePaperWhite2.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 = io.open("/proc/usid", "r")
|
||||||
|
if not kindle_sn then return end
|
||||||
|
local kindle_devcode = string.sub(kindle_sn:read(),3,4)
|
||||||
|
kindle_sn:close()
|
||||||
|
|
||||||
|
-- NOTE: Update me when new devices come out :)
|
||||||
|
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" }
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
error("unknown Kindle model "..kindle_devcode)
|
@ -0,0 +1,39 @@
|
|||||||
|
--[[
|
||||||
|
event map for Kindle devices with an alphabetic and/or alphanumeric keyboard
|
||||||
|
--]]
|
||||||
|
|
||||||
|
return {
|
||||||
|
[2] = "1", [3] = "2", [4] = "3", [5] = "4", [6] = "5", [7] = "6", [8] = "7", [9] = "8", [10] = "9", [11] = "0",
|
||||||
|
[16] = "Q", [17] = "W", [18] = "E", [19] = "R", [20] = "T", [21] = "Y", [22] = "U", [23] = "I", [24] = "O", [25] = "P",
|
||||||
|
[30] = "A", [31] = "S", [32] = "D", [33] = "F", [34] = "G", [35] = "H", [36] = "J", [37] = "K", [38] = "L", [14] = "Del",
|
||||||
|
[44] = "Z", [45] = "X", [46] = "C", [47] = "V", [48] = "B", [49] = "N", [50] = "M", [52] = ".", [53] = "/", -- only KDX
|
||||||
|
|
||||||
|
[28] = "Enter",
|
||||||
|
[42] = "Shift",
|
||||||
|
[56] = "Alt",
|
||||||
|
[57] = " ",
|
||||||
|
[90] = "AA", -- KDX
|
||||||
|
[91] = "Back", -- KDX
|
||||||
|
[92] = "Press", -- KDX
|
||||||
|
[94] = "Sym", -- KDX
|
||||||
|
[98] = "Home", -- KDX
|
||||||
|
[102] = "Home", -- K[3] & k[4]
|
||||||
|
[104] = "LPgBack", -- K[3] only
|
||||||
|
[103] = "Up", -- K[3] & k[4]
|
||||||
|
[105] = "Left",
|
||||||
|
[106] = "Right",
|
||||||
|
[108] = "Down", -- K[3] & k[4]
|
||||||
|
[109] = "RPgBack",
|
||||||
|
[114] = "VMinus",
|
||||||
|
[115] = "VPlus",
|
||||||
|
[122] = "Up", -- KDX
|
||||||
|
[123] = "Down", -- KDX
|
||||||
|
[124] = "RPgFwd", -- KDX
|
||||||
|
[126] = "Sym", -- K[3]
|
||||||
|
[139] = "Menu",
|
||||||
|
[158] = "Back", -- K[3] & K[4]
|
||||||
|
[190] = "AA", -- K[3]
|
||||||
|
[191] = "RPgFwd", -- K[3] & k[4]
|
||||||
|
[193] = "LPgFwd", -- K[3] only
|
||||||
|
[194] = "Press", -- K[3] & k[4]
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
--[[
|
||||||
|
event map for Kindle devices with control buttons & DPad
|
||||||
|
--]]
|
||||||
|
|
||||||
|
return {
|
||||||
|
[29] = "ScreenKB",
|
||||||
|
[102] = "Home",
|
||||||
|
[103] = "Up",
|
||||||
|
[104] = "LPgFwd",
|
||||||
|
[108] = "Down",
|
||||||
|
[158] = "Back",
|
||||||
|
[191] = "RPgFwd",
|
||||||
|
[193] = "LPgBack",
|
||||||
|
[194] = "Press",
|
||||||
|
}
|
@ -0,0 +1,151 @@
|
|||||||
|
local Generic = require("device/generic/device")
|
||||||
|
local lfs = require("libs/libkoreader-lfs")
|
||||||
|
local Geom = require("ui/geometry")
|
||||||
|
|
||||||
|
local function yes() return true end
|
||||||
|
|
||||||
|
local Kobo = Generic:new{
|
||||||
|
model = "Kobo",
|
||||||
|
isKobo = yes,
|
||||||
|
isTouchDevice = yes, -- all of them are
|
||||||
|
|
||||||
|
-- most Kobos have X/Y switched for the touch screen
|
||||||
|
touch_switch_xy = true,
|
||||||
|
-- most Kobos have also mirrored X coordinates
|
||||||
|
touch_mirrored_x = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- TODO: hasKeys for some devices?
|
||||||
|
|
||||||
|
local KoboTrilogy = Kobo:new{
|
||||||
|
model = "Kobo_trilogy",
|
||||||
|
touch_switch_xy = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
local KoboPixie = Kobo:new{
|
||||||
|
model = "Kobo_pixie",
|
||||||
|
display_dpi = 200,
|
||||||
|
}
|
||||||
|
|
||||||
|
local KoboDahlia = Kobo:new{
|
||||||
|
model = "Kobo_dahlia",
|
||||||
|
hasFrontlight = yes,
|
||||||
|
touch_phoenix_protocol = true,
|
||||||
|
display_dpi = 265,
|
||||||
|
-- bezel:
|
||||||
|
viewport = Geom:new{x=0, y=10, w=1080, h=1430},
|
||||||
|
}
|
||||||
|
|
||||||
|
local KoboDragon = Kobo:new{
|
||||||
|
model = "Kobo_dragon",
|
||||||
|
hasFrontlight = yes,
|
||||||
|
display_dpi = 265,
|
||||||
|
}
|
||||||
|
|
||||||
|
local KoboKraken = Kobo:new{
|
||||||
|
model = "Kobo_kraken",
|
||||||
|
hasFrontlight = yes,
|
||||||
|
display_dpi = 212,
|
||||||
|
}
|
||||||
|
|
||||||
|
local KoboPhoenix = Kobo:new{
|
||||||
|
model = "Kobo_phoenix",
|
||||||
|
hasFrontlight = yes,
|
||||||
|
touch_phoenix_protocol = true,
|
||||||
|
display_dpi = 212.8,
|
||||||
|
-- bezel:
|
||||||
|
viewport = Geom:new{x=6, y=12, w=752, h=1012},
|
||||||
|
}
|
||||||
|
|
||||||
|
function Kobo:init()
|
||||||
|
self.screen = require("device/screen"):new{device = self}
|
||||||
|
self.powerd = require("device/kobo/powerd"):new{device = self}
|
||||||
|
self.input = require("device/input"):new{
|
||||||
|
device = self,
|
||||||
|
event_map = {
|
||||||
|
[59] = "Power_SleepCover",
|
||||||
|
[90] = "Light",
|
||||||
|
[116] = "Power",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Generic.init(self)
|
||||||
|
|
||||||
|
self.input.open("/dev/input/event0") -- Light button and sleep slider
|
||||||
|
self.input.open("/dev/input/event1")
|
||||||
|
|
||||||
|
-- it's called KOBO_TOUCH_MIRRORED in defaults.lua, but what it
|
||||||
|
-- actually did in its original implementation was to switch X/Y.
|
||||||
|
if self.touch_switch_xy and not KOBO_TOUCH_MIRRORED
|
||||||
|
or not self.touch_switch_xy and KOBO_TOUCH_MIRRORED
|
||||||
|
then
|
||||||
|
self.input:registerEventAdjustHook(self.input.adjustTouchSwitchXY)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.touch_mirrored_x then
|
||||||
|
self.input:registerEventAdjustHook(
|
||||||
|
self.input.adjustTouchMirrorX,
|
||||||
|
self.screen:getScreenWidth()
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.touch_phoenix_protocol then
|
||||||
|
self.input.handleTouchEv = self.input.handleTouchEvPhoenix
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Kobo:getCodeName()
|
||||||
|
local std_out = io.popen("/bin/kobo_config.sh 2>/dev/null", "r")
|
||||||
|
local codename = std_out:read()
|
||||||
|
std_out:close()
|
||||||
|
return codename
|
||||||
|
end
|
||||||
|
|
||||||
|
function Kobo:getFirmwareVersion()
|
||||||
|
local version_file = io.open("/mnt/onboard/.kobo/version", "r")
|
||||||
|
self.firmware_rev = string.sub(version_file:read(),24,28)
|
||||||
|
version_file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Kobo:Suspend()
|
||||||
|
if KOBO_LIGHT_OFF_ON_SUSPEND then
|
||||||
|
self.powerd:setIntensity(0)
|
||||||
|
end
|
||||||
|
os.execute("./suspend.sh")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Kobo:Resume()
|
||||||
|
os.execute("echo 0 > /sys/power/state-extended")
|
||||||
|
if self.powerd then
|
||||||
|
if KOBO_LIGHT_ON_START and tonumber(KOBO_LIGHT_ON_START) > -1 then
|
||||||
|
self.powerd:setIntensity(math.max(math.min(KOBO_LIGHT_ON_START,100),0))
|
||||||
|
elseif powerd.fl ~= nil then
|
||||||
|
self.powerd.fl:restore()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Generic.Resume(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------- device probe ------------
|
||||||
|
|
||||||
|
local codename = Kobo:getCodeName()
|
||||||
|
|
||||||
|
if codename == "dahlia" then
|
||||||
|
return KoboDahlia
|
||||||
|
elseif codename == "dragon" then
|
||||||
|
return KoboDragon
|
||||||
|
elseif codename == "kraken" then
|
||||||
|
return KoboKraken
|
||||||
|
elseif codename == "phoenix" then
|
||||||
|
return KoboPhoenix
|
||||||
|
elseif codename == "trilogy" then
|
||||||
|
return KoboTrilogy
|
||||||
|
elseif codename == "pixie" then
|
||||||
|
return KoboPixie
|
||||||
|
else
|
||||||
|
error("unrecognized Kobo model "..codename)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
local BasePowerD = require("ui/device/basepowerd")
|
local BasePowerD = require("device/generic/powerd")
|
||||||
|
|
||||||
local KoboPowerD = BasePowerD:new{
|
local KoboPowerD = BasePowerD:new{
|
||||||
fl_min = 0, fl_max = 100,
|
fl_min = 0, fl_max = 100,
|
@ -0,0 +1,183 @@
|
|||||||
|
local Blitbuffer = require("ffi/blitbuffer")
|
||||||
|
local einkfb = require("ffi/framebuffer")
|
||||||
|
local Geom = require("ui/geometry")
|
||||||
|
local util = require("ffi/util")
|
||||||
|
local DEBUG = require("dbg")
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Codes for rotation modes:
|
||||||
|
|
||||||
|
1 for no rotation,
|
||||||
|
2 for landscape with bottom on the right side of screen, etc.
|
||||||
|
|
||||||
|
2
|
||||||
|
+--------------+
|
||||||
|
| +----------+ |
|
||||||
|
| | | |
|
||||||
|
| | Freedom! | |
|
||||||
|
| | | |
|
||||||
|
| | | |
|
||||||
|
3 | | | | 1
|
||||||
|
| | | |
|
||||||
|
| | | |
|
||||||
|
| +----------+ |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
+--------------+
|
||||||
|
0
|
||||||
|
--]]
|
||||||
|
|
||||||
|
|
||||||
|
local Screen = {
|
||||||
|
cur_rotation_mode = 0,
|
||||||
|
native_rotation_mode = nil,
|
||||||
|
blitbuffer_rotation_mode = 0,
|
||||||
|
|
||||||
|
bb = nil,
|
||||||
|
saved_bb = nil,
|
||||||
|
|
||||||
|
screen_size = Geom:new(),
|
||||||
|
viewport = nil,
|
||||||
|
|
||||||
|
fb = einkfb.open("/dev/fb0"),
|
||||||
|
-- will be set upon loading by Device class:
|
||||||
|
device = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Screen:new(o)
|
||||||
|
local o = o or {}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
if o.init then o:init() end
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:init()
|
||||||
|
self.bb = self.fb.bb
|
||||||
|
self.blitbuffer_rotation_mode = self.bb:getRotation()
|
||||||
|
-- asking the framebuffer for orientation is error prone,
|
||||||
|
-- so we do this simple heuristic (for now)
|
||||||
|
self.screen_size.w = self.bb:getWidth()
|
||||||
|
self.screen_size.h = self.bb:getHeight()
|
||||||
|
if self.screen_size.w > self.screen_size.h then
|
||||||
|
self.native_rotation_mode = 1
|
||||||
|
self.screen_size.w, self.screen_size.h = self.screen_size.h, self.screen_size.w
|
||||||
|
else
|
||||||
|
self.native_rotation_mode = 0
|
||||||
|
end
|
||||||
|
self.cur_rotation_mode = self.native_rotation_mode
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
set a rectangle that represents the area of the screen we are working on
|
||||||
|
--]]
|
||||||
|
function Screen:setViewport(viewport)
|
||||||
|
self.viewport = self.screen_size:intersect(viewport)
|
||||||
|
self.bb = self.fb.bb:viewport(
|
||||||
|
self.viewport.x, self.viewport.y,
|
||||||
|
self.viewport.w, self.viewport.h)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:refresh(refresh_type, waveform_mode, 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)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:getSize()
|
||||||
|
return Geom:new{w = self.bb:getWidth(), h = self.bb:getHeight()}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:getWidth()
|
||||||
|
return self.bb:getWidth()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:getScreenWidth()
|
||||||
|
return self.screen_size.w
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:getScreenHeight()
|
||||||
|
return self.screen_size.h
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:getHeight()
|
||||||
|
return self.bb:getHeight()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:getDPI()
|
||||||
|
if self.dpi == nil then
|
||||||
|
self.dpi = G_reader_settings:readSetting("screen_dpi")
|
||||||
|
end
|
||||||
|
if self.dpi == nil then
|
||||||
|
self.dpi = self.device.display_dpi
|
||||||
|
end
|
||||||
|
if self.dpi == nil then
|
||||||
|
self.dpi = 160
|
||||||
|
end
|
||||||
|
return self.dpi
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:setDPI(dpi)
|
||||||
|
G_reader_settings:saveSetting("screen_dpi", dpi)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:scaleByDPI(px)
|
||||||
|
-- scaled positive px should also be positive
|
||||||
|
return math.ceil(px * self:getDPI()/167)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:getRotationMode()
|
||||||
|
return self.cur_rotation_mode
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:getScreenMode()
|
||||||
|
if self:getWidth() > self:getHeight() then
|
||||||
|
return "landscape"
|
||||||
|
else
|
||||||
|
return "portrait"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:setRotationMode(mode)
|
||||||
|
self.fb.bb:rotateAbsolute(-90 * (mode - self.native_rotation_mode - self.blitbuffer_rotation_mode))
|
||||||
|
self.cur_rotation_mode = mode
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:setScreenMode(mode)
|
||||||
|
if mode == "portrait" then
|
||||||
|
if self.cur_rotation_mode ~= 0 then
|
||||||
|
self:setRotationMode(0)
|
||||||
|
end
|
||||||
|
elseif mode == "landscape" then
|
||||||
|
if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then
|
||||||
|
self:setRotationMode(DLANDSCAPE_CLOCKWISE_ROTATION and 1 or 3)
|
||||||
|
elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then
|
||||||
|
self:setRotationMode((self.cur_rotation_mode + 2) % 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:saveCurrentBB()
|
||||||
|
if self.saved_bb then self.saved_bb:free() end
|
||||||
|
self.saved_bb = self.bb:copy()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:restoreFromSavedBB()
|
||||||
|
if self.saved_bb then
|
||||||
|
self.bb:blitFullFrom(self.saved_bb)
|
||||||
|
-- free data
|
||||||
|
self.saved_bb:free()
|
||||||
|
self.saved_bb = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Screen:close()
|
||||||
|
DEBUG("close screen framebuffer")
|
||||||
|
self.fb:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return Screen
|
@ -1,326 +0,0 @@
|
|||||||
local isAndroid, android = pcall(require, "android")
|
|
||||||
local lfs = require("libs/libkoreader-lfs")
|
|
||||||
local Screen = require("ui/device/screen")
|
|
||||||
local util = require("ffi/util")
|
|
||||||
local DEBUG = require("dbg")
|
|
||||||
local ffi = require("ffi")
|
|
||||||
|
|
||||||
local Device = {
|
|
||||||
screen_saver_mode = false,
|
|
||||||
charging_mode = false,
|
|
||||||
survive_screen_saver = false,
|
|
||||||
is_special_offers = nil,
|
|
||||||
touch_dev = nil,
|
|
||||||
model = nil,
|
|
||||||
firmware_rev = nil,
|
|
||||||
powerd = nil,
|
|
||||||
has_no_keyboard = nil,
|
|
||||||
is_touch_device = nil,
|
|
||||||
has_front_light = nil,
|
|
||||||
screen = Screen
|
|
||||||
}
|
|
||||||
|
|
||||||
Screen.device = Device
|
|
||||||
|
|
||||||
function Set(list)
|
|
||||||
local set = {}
|
|
||||||
for _, l in ipairs(list) do set[l] = true end
|
|
||||||
return set
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:getModel()
|
|
||||||
if self.model then return self.model end
|
|
||||||
if util.isEmulated() then
|
|
||||||
self.model = "Emulator"
|
|
||||||
return self.model
|
|
||||||
end
|
|
||||||
self.model = ""
|
|
||||||
local kindle_sn = io.open("/proc/usid", "r")
|
|
||||||
if kindle_sn then
|
|
||||||
local kindle_devcode = string.sub(kindle_sn:read(),3,4)
|
|
||||||
kindle_sn:close()
|
|
||||||
-- NOTE: Update me when new devices come out :)
|
|
||||||
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" }
|
|
||||||
|
|
||||||
if k2_set[kindle_devcode] then
|
|
||||||
self.model = "Kindle2"
|
|
||||||
elseif dx_set[kindle_devcode] then
|
|
||||||
self.model = "Kindle2"
|
|
||||||
elseif dxg_set[kindle_devcode] then
|
|
||||||
self.model = "KindleDXG"
|
|
||||||
elseif k3_set[kindle_devcode] then
|
|
||||||
self.model = "Kindle3"
|
|
||||||
elseif k4_set[kindle_devcode] then
|
|
||||||
self.model = "Kindle4"
|
|
||||||
elseif touch_set[kindle_devcode] then
|
|
||||||
self.model = "KindleTouch"
|
|
||||||
elseif pw_set[kindle_devcode] then
|
|
||||||
self.model = "KindlePaperWhite"
|
|
||||||
elseif pw2_set[kindle_devcode] then
|
|
||||||
self.model = "KindlePaperWhite2"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local kg_test_fd = lfs.attributes("/bin/kobo_config.sh")
|
|
||||||
if kg_test_fd then
|
|
||||||
local std_out = io.popen("/bin/kobo_config.sh 2>/dev/null", "r")
|
|
||||||
local codename = std_out:read()
|
|
||||||
self.model = "Kobo_" .. codename
|
|
||||||
local version_file = io.open("/mnt/onboard/.kobo/version", "r")
|
|
||||||
self.firmware_rev = string.sub(version_file:read(),24,28)
|
|
||||||
version_file:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self.model
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:getFirmVer()
|
|
||||||
if not self.model then self:getModel() end
|
|
||||||
return self.firmware_rev
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:isKindle4()
|
|
||||||
return (self:getModel() == "Kindle4")
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:isKindle3()
|
|
||||||
return (self:getModel() == "Kindle3")
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:isKindle2()
|
|
||||||
return (self:getModel() == "Kindle2")
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:isKindle()
|
|
||||||
local is_kindle = false
|
|
||||||
local kindle_sn = io.open("/proc/usid", "r")
|
|
||||||
if kindle_sn then
|
|
||||||
is_kindle = true
|
|
||||||
kindle_sn:close()
|
|
||||||
end
|
|
||||||
return is_kindle
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:isKobo()
|
|
||||||
return string.find(self:getModel() or "", "Kobo_") == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
Device.isAndroid = util.isAndroid
|
|
||||||
|
|
||||||
-- device has qwerty keyboard
|
|
||||||
function Device:hasKeyboard()
|
|
||||||
if self.has_keyboard ~= nil then return self.has_keyboard end
|
|
||||||
if not isAndroid then
|
|
||||||
local model = self:getModel()
|
|
||||||
self.has_keyboard = (model == "Kindle2") or (model == "Kindle3")
|
|
||||||
or (model == "KindleDXG") or util.isEmulated()
|
|
||||||
else
|
|
||||||
self.has_keyboard = ffi.C.AConfiguration_getKeyboard(android.app.config)
|
|
||||||
== ffi.C.ACONFIGURATION_KEYBOARD_QWERTY
|
|
||||||
end
|
|
||||||
return self.has_keyboard
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:hasNoKeyboard()
|
|
||||||
return not self:hasKeyboard()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- device has hardware keys for pagedown/pageup
|
|
||||||
function Device:hasKeys()
|
|
||||||
if self.has_keys ~= nil then return self.has_keys end
|
|
||||||
local model = self:getModel()
|
|
||||||
self.has_keys = (model ~= "KindlePaperWhite") and (model ~= "KindlePaperWhite2")
|
|
||||||
and (model ~= "KindleTouch") and not self:isKobo()
|
|
||||||
return self.has_keys
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:isTouchDevice()
|
|
||||||
if self.is_touch_device ~= nil then return self.is_touch_device end
|
|
||||||
if not isAndroid then
|
|
||||||
local model = self:getModel()
|
|
||||||
self.is_touch_device = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2")
|
|
||||||
or (model == "KindleTouch") or self:isKobo() or util.isEmulated()
|
|
||||||
else
|
|
||||||
self.is_touch_device = ffi.C.AConfiguration_getTouchscreen(android.app.config)
|
|
||||||
~= ffi.C.ACONFIGURATION_TOUCHSCREEN_NOTOUCH
|
|
||||||
end
|
|
||||||
return self.is_touch_device
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:hasFrontlight()
|
|
||||||
if self.has_front_light ~= nil then return self.has_front_light end
|
|
||||||
local model = self:getModel()
|
|
||||||
self.has_front_light = (model == "KindlePaperWhite") or (model == "KindlePaperWhite2")
|
|
||||||
or (model == "Kobo_dahlia") or (model == "Kobo_dragon") or (model == "Kobo_kraken") or (model == "Kobo_phoenix")
|
|
||||||
or util.isEmulated()
|
|
||||||
return self.has_front_light
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:setTouchInputDev(dev)
|
|
||||||
self.touch_dev = dev
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:getTouchInputDev()
|
|
||||||
return self.touch_dev
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:intoScreenSaver()
|
|
||||||
--os.execute("echo 'screensaver in' >> /mnt/us/event_test.txt")
|
|
||||||
if self.charging_mode == false and self.screen_saver_mode == false then
|
|
||||||
self.screen:saveCurrentBB()
|
|
||||||
--UIManager:show(InfoMessage:new{
|
|
||||||
--text = "Going into screensaver... ",
|
|
||||||
--timeout = 2,
|
|
||||||
--})
|
|
||||||
--util.sleep(1)
|
|
||||||
--os.execute("killall -cont cvm")
|
|
||||||
self.screen_saver_mode = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:outofScreenSaver()
|
|
||||||
--os.execute("echo 'screensaver out' >> /mnt/us/event_test.txt")
|
|
||||||
if self.screen_saver_mode == true and self.charging_mode == false then
|
|
||||||
-- wait for native system update screen before we recover saved
|
|
||||||
-- Blitbuffer.
|
|
||||||
util.usleep(1500000)
|
|
||||||
--os.execute("killall -stop cvm")
|
|
||||||
self.screen:restoreFromSavedBB()
|
|
||||||
self.screen:refresh(0)
|
|
||||||
self.survive_screen_saver = true
|
|
||||||
end
|
|
||||||
self.screen_saver_mode = false
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:onPowerEvent(ev)
|
|
||||||
local Screensaver = require("ui/screensaver")
|
|
||||||
if (ev == "Power" or ev == "Suspend") and not self.screen_saver_mode then
|
|
||||||
local UIManager = require("ui/uimanager")
|
|
||||||
DEBUG("Suspending...")
|
|
||||||
-- always suspend in portrait mode
|
|
||||||
self.orig_rotation_mode = Screen:getRotationMode()
|
|
||||||
Screen:setRotationMode(0)
|
|
||||||
Screensaver:show()
|
|
||||||
self:prepareSuspend()
|
|
||||||
UIManager:scheduleIn(2, function() self:Suspend() end)
|
|
||||||
elseif (ev == "Power" or ev == "Resume") and self.screen_saver_mode then
|
|
||||||
DEBUG("Resuming...")
|
|
||||||
-- restore to previous rotation mode
|
|
||||||
Screen:setRotationMode(self.orig_rotation_mode)
|
|
||||||
self:Resume()
|
|
||||||
Screensaver:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:prepareSuspend()
|
|
||||||
local powerd = self:getPowerDevice()
|
|
||||||
if powerd.fl ~= nil then
|
|
||||||
-- in no case should the frontlight be turned on in suspend mode
|
|
||||||
powerd.fl:sleep()
|
|
||||||
end
|
|
||||||
self.screen:refresh(0)
|
|
||||||
self.screen_saver_mode = true
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:Suspend()
|
|
||||||
if self:isKobo() then
|
|
||||||
if KOBO_LIGHT_OFF_ON_SUSPEND then self:getPowerDevice():setIntensity(0) end
|
|
||||||
os.execute("./suspend.sh")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:Resume()
|
|
||||||
if self:isKobo() then
|
|
||||||
os.execute("echo 0 > /sys/power/state-extended")
|
|
||||||
local powerd = self:getPowerDevice()
|
|
||||||
if powerd then
|
|
||||||
if KOBO_LIGHT_ON_START and tonumber(KOBO_LIGHT_ON_START) > -1 then
|
|
||||||
powerd:setIntensity(math.max(math.min(KOBO_LIGHT_ON_START,100),0))
|
|
||||||
elseif powerd.fl ~= nil then
|
|
||||||
powerd.fl:restore()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.screen:refresh(1)
|
|
||||||
self.screen_saver_mode = false
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:usbPlugIn()
|
|
||||||
--os.execute("echo 'usb in' >> /mnt/us/event_test.txt")
|
|
||||||
if self.charging_mode == false and self.screen_saver_mode == false then
|
|
||||||
self.screen:saveCurrentBB()
|
|
||||||
--UIManager:show(InfoMessage:new{
|
|
||||||
--text = "Going into USB mode... ",
|
|
||||||
--timeout = 2,
|
|
||||||
--})
|
|
||||||
--util.sleep(1)
|
|
||||||
--os.execute("killall -cont cvm")
|
|
||||||
end
|
|
||||||
self.charging_mode = true
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:usbPlugOut()
|
|
||||||
--os.execute("echo 'usb out' >> /mnt/us/event_test.txt")
|
|
||||||
if self.charging_mode == true and self.screen_saver_mode == false then
|
|
||||||
--util.usleep(1500000)
|
|
||||||
--os.execute("killall -stop cvm")
|
|
||||||
self.screen:restoreFromSavedBB()
|
|
||||||
self.screen:refresh(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
--@TODO signal filemanager for file changes 13.06 2012 (houqp)
|
|
||||||
self.charging_mode = false
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:getPowerDevice()
|
|
||||||
if self.powerd ~= nil then
|
|
||||||
return self.powerd
|
|
||||||
else
|
|
||||||
local model = self:getModel()
|
|
||||||
if model == "KindleTouch" or model == "KindlePaperWhite" or model == "KindlePaperWhite2" then
|
|
||||||
local KindlePowerD = require("ui/device/kindlepowerd")
|
|
||||||
self.powerd = KindlePowerD:new{model = model}
|
|
||||||
elseif self:isKobo() then
|
|
||||||
local KoboPowerD = require("ui/device/kobopowerd")
|
|
||||||
self.powerd = KoboPowerD:new()
|
|
||||||
elseif self.isAndroid then
|
|
||||||
local AndroidPowerd = require("ui/device/androidpowerd")
|
|
||||||
self.powerd = AndroidPowerd:new()
|
|
||||||
else -- emulated FrontLight
|
|
||||||
local BasePowerD = require("ui/device/basepowerd")
|
|
||||||
self.powerd = BasePowerD:new()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self.powerd
|
|
||||||
end
|
|
||||||
|
|
||||||
function Device:isSpecialOffers()
|
|
||||||
if self.is_special_offers ~= nil then return self.is_special_offers end
|
|
||||||
-- K5 only
|
|
||||||
if self:isTouchDevice() and self:isKindle() then
|
|
||||||
-- Look at the current blanket modules to see if the SO screensavers are enabled...
|
|
||||||
local lipc = require("liblipclua")
|
|
||||||
local lipc_handle = nil
|
|
||||||
if lipc then
|
|
||||||
lipc_handle = lipc.init("com.github.koreader.device")
|
|
||||||
end
|
|
||||||
if lipc_handle then
|
|
||||||
local loaded_blanket_modules = lipc_handle:get_string_property("com.lab126.blanket", "load")
|
|
||||||
if string.find(loaded_blanket_modules, "ad_screensaver") then
|
|
||||||
self.is_special_offers = true
|
|
||||||
end
|
|
||||||
lipc_handle:close()
|
|
||||||
else
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self.is_special_offers
|
|
||||||
end
|
|
||||||
|
|
||||||
return Device
|
|
@ -1,352 +0,0 @@
|
|||||||
local Blitbuffer = require("ffi/blitbuffer")
|
|
||||||
local einkfb = require("ffi/framebuffer")
|
|
||||||
local Geom = require("ui/geometry")
|
|
||||||
local util = require("ffi/util")
|
|
||||||
local DEBUG = require("dbg")
|
|
||||||
local _ = require("gettext")
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Codes for rotation modes:
|
|
||||||
|
|
||||||
1 for no rotation,
|
|
||||||
2 for landscape with bottom on the right side of screen, etc.
|
|
||||||
|
|
||||||
2
|
|
||||||
+--------------+
|
|
||||||
| +----------+ |
|
|
||||||
| | | |
|
|
||||||
| | Freedom! | |
|
|
||||||
| | | |
|
|
||||||
| | | |
|
|
||||||
3 | | | | 1
|
|
||||||
| | | |
|
|
||||||
| | | |
|
|
||||||
| +----------+ |
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
+--------------+
|
|
||||||
0
|
|
||||||
--]]
|
|
||||||
|
|
||||||
|
|
||||||
local Screen = {
|
|
||||||
cur_rotation_mode = 0,
|
|
||||||
native_rotation_mode = nil,
|
|
||||||
blitbuffer_rotation_mode = 0,
|
|
||||||
|
|
||||||
bb = nil,
|
|
||||||
saved_bb = nil,
|
|
||||||
|
|
||||||
fb = einkfb.open("/dev/fb0"),
|
|
||||||
-- will be set upon loading by Device class:
|
|
||||||
device = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
function Screen:init()
|
|
||||||
self.bb = self.fb.bb
|
|
||||||
if self.device:getModel() == 'Kobo_phoenix' then
|
|
||||||
function Screen:getSize()
|
|
||||||
return Screen:getSizePhoenix()
|
|
||||||
end
|
|
||||||
function Screen:getWidth()
|
|
||||||
return Screen:getWidthPhoenix()
|
|
||||||
end
|
|
||||||
function Screen:getHeight()
|
|
||||||
return Screen:getHeightPhoenix()
|
|
||||||
end
|
|
||||||
function self:offsetX()
|
|
||||||
if Screen.cur_rotation_mode == 0 then
|
|
||||||
return 6
|
|
||||||
elseif Screen.cur_rotation_mode == 1 then
|
|
||||||
return 12
|
|
||||||
elseif Screen.cur_rotation_mode == 2 then
|
|
||||||
return 12
|
|
||||||
elseif Screen.cur_rotation_mode == 3 then
|
|
||||||
return 6
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function self:offsetY()
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
elseif self.device:getModel() == 'Kobo_dahlia' then
|
|
||||||
function Screen:getSize()
|
|
||||||
return Screen:getSizePhoenix()
|
|
||||||
end
|
|
||||||
function Screen:getWidth()
|
|
||||||
return Screen:getWidthDahlia()
|
|
||||||
end
|
|
||||||
function Screen:getHeight()
|
|
||||||
return Screen:getHeightDahlia()
|
|
||||||
end
|
|
||||||
function self:offsetX()
|
|
||||||
if Screen.cur_rotation_mode == 3 then
|
|
||||||
return 10
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function self:offsetY()
|
|
||||||
if Screen.cur_rotation_mode == 0 then
|
|
||||||
return 10
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
function Screen:getSize()
|
|
||||||
return Screen:getSizeBB()
|
|
||||||
end
|
|
||||||
function Screen:getWidth()
|
|
||||||
return Screen:getWidthBB()
|
|
||||||
end
|
|
||||||
function Screen:getHeight()
|
|
||||||
return Screen:getHeightBB()
|
|
||||||
end
|
|
||||||
function self:offsetX() return 0 end
|
|
||||||
function self:offsetY() return 0 end
|
|
||||||
end
|
|
||||||
self.blitbuffer_rotation_mode = self.bb:getRotation()
|
|
||||||
-- asking the framebuffer for orientation is error prone,
|
|
||||||
-- so we do this simple heuristic (for now)
|
|
||||||
if self:getWidth() > self:getHeight() then
|
|
||||||
self.native_rotation_mode = 1
|
|
||||||
else
|
|
||||||
self.native_rotation_mode = 0
|
|
||||||
end
|
|
||||||
self.cur_rotation_mode = self.native_rotation_mode
|
|
||||||
end
|
|
||||||
|
|
||||||
-- For the Kobo Aura an offset is needed, because the bezel make the
|
|
||||||
-- visible screen smaller.
|
|
||||||
function Screen:PhoenixBezelCleaner()
|
|
||||||
self.bb:paintRect(0,0, Screen:getWidth(), Screen:offsetY(), Blitbuffer.COLOR_WHITE)
|
|
||||||
self.bb:paintRect(0,0, Screen:offsetX(), Screen:getHeight(), Blitbuffer.COLOR_WHITE)
|
|
||||||
self.bb:paintRect(Screen:getWidth() + Screen:offsetX(), 0,
|
|
||||||
Screen:getWidth() - Screen:getWidth() - Screen:offsetX(), Screen:getHeight(),
|
|
||||||
Blitbuffer.COLOR_WHITE)
|
|
||||||
self.bb:paintRect(0, Screen:getHeight() + Screen:offsetY(),
|
|
||||||
Screen:offsetX(), Screen:getWidth(),
|
|
||||||
Blitbuffer.COLOR_WHITE)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:refresh(refresh_type, waveform_mode, x, y, w, h)
|
|
||||||
self.fb:refresh(refresh_type, waveform_mode, x, y, w, h)
|
|
||||||
if self.device:getModel() == 'Kobo_phoenix' and refresh_type == 1 then
|
|
||||||
Screen:PhoenixBezelCleaner()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getSizeBB()
|
|
||||||
return Geom:new{w = self.bb:getWidth(), h = self.bb:getHeight()}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getSizePhoenix()
|
|
||||||
return Geom:new{w = self.getWidth(), h = self.getHeight()}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getWidthBB()
|
|
||||||
return self.bb:getWidth()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getWidthDahlia()
|
|
||||||
if self.cur_rotation_mode == 0 then return 1080
|
|
||||||
else return 1430
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getWidthPhoenix()
|
|
||||||
if self.cur_rotation_mode == 0 then return 752
|
|
||||||
else return 1012
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getHeightBB()
|
|
||||||
return self.bb:getHeight()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getHeightDahlia()
|
|
||||||
if self.cur_rotation_mode == 0 then return 1430
|
|
||||||
else return 1080
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getHeightPhoenix()
|
|
||||||
if self.cur_rotation_mode == 0 then return 1012
|
|
||||||
else return 752
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getDPI()
|
|
||||||
if self.dpi == nil then
|
|
||||||
self.dpi = G_reader_settings:readSetting("screen_dpi")
|
|
||||||
end
|
|
||||||
if self.dpi ~= nil then return self.dpi end
|
|
||||||
local model = self.device:getModel()
|
|
||||||
if model == "KindlePaperWhite" or model == "KindlePaperWhite2"
|
|
||||||
or model == "Kobo_kraken" then
|
|
||||||
self.dpi = 212
|
|
||||||
elseif model == "Kobo_phoenix" then
|
|
||||||
self.dpi = 212.8
|
|
||||||
elseif model == "Kobo_dragon" or model == "Kobo_dahlia" then
|
|
||||||
self.dpi = 265
|
|
||||||
elseif model == "Kobo_pixie" then
|
|
||||||
self.dpi = 200
|
|
||||||
elseif util.isAndroid() then
|
|
||||||
local android = require("android")
|
|
||||||
local ffi = require("ffi")
|
|
||||||
self.dpi = ffi.C.AConfiguration_getDensity(android.app.config)
|
|
||||||
else
|
|
||||||
self.dpi = 160
|
|
||||||
end
|
|
||||||
return self.dpi
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:setDPI(dpi)
|
|
||||||
G_reader_settings:saveSetting("screen_dpi", dpi)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:scaleByDPI(px)
|
|
||||||
-- scaled positive px should also be positive
|
|
||||||
return math.ceil(px * self:getDPI()/167)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getRotationMode()
|
|
||||||
return self.cur_rotation_mode
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getScreenMode()
|
|
||||||
if self:getWidth() > self:getHeight() then
|
|
||||||
return "landscape"
|
|
||||||
else
|
|
||||||
return "portrait"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:setRotationMode(mode)
|
|
||||||
self.fb.bb:rotateAbsolute(-90 * (mode - self.native_rotation_mode - self.blitbuffer_rotation_mode))
|
|
||||||
self.cur_rotation_mode = mode
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:setScreenMode(mode)
|
|
||||||
if mode == "portrait" then
|
|
||||||
if self.cur_rotation_mode ~= 0 then
|
|
||||||
self:setRotationMode(0)
|
|
||||||
end
|
|
||||||
elseif mode == "landscape" then
|
|
||||||
if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then
|
|
||||||
self:setRotationMode(DLANDSCAPE_CLOCKWISE_ROTATION and 1 or 3)
|
|
||||||
elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then
|
|
||||||
self:setRotationMode((self.cur_rotation_mode + 2) % 4)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:saveCurrentBB()
|
|
||||||
local width, height = self:getWidth(), self:getHeight()
|
|
||||||
|
|
||||||
if not self.saved_bb then
|
|
||||||
self.saved_bb = Blitbuffer.new(width, height)
|
|
||||||
end
|
|
||||||
if self.saved_bb:getWidth() ~= width then
|
|
||||||
self.saved_bb:free()
|
|
||||||
self.saved_bb = Blitbuffer.new(width, height)
|
|
||||||
end
|
|
||||||
self.saved_bb:blitFullFrom(self.bb)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:restoreFromSavedBB()
|
|
||||||
self:restoreFromBB(self.saved_bb)
|
|
||||||
-- free data
|
|
||||||
self.saved_bb = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getCurrentScreenBB()
|
|
||||||
local bb = Blitbuffer.new(self:getWidth(), self:getHeight())
|
|
||||||
bb:blitFullFrom(self.bb)
|
|
||||||
return bb
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:restoreFromBB(bb)
|
|
||||||
if bb then
|
|
||||||
self.bb:blitFullFrom(bb)
|
|
||||||
else
|
|
||||||
DEBUG("Got nil bb in restoreFromSavedBB!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:close()
|
|
||||||
DEBUG("close screen framebuffer")
|
|
||||||
self.fb:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Screen:getDPIMenuTable()
|
|
||||||
local function dpi() return G_reader_settings:readSetting("screen_dpi") end
|
|
||||||
local function custom() return G_reader_settings:readSetting("custom_screen_dpi") end
|
|
||||||
local function setDPI(dpi)
|
|
||||||
local InfoMessage = require("ui/widget/infomessage")
|
|
||||||
local UIManager = require("ui/uimanager")
|
|
||||||
UIManager:show(InfoMessage:new{
|
|
||||||
text = _("This will take effect on next restart."),
|
|
||||||
})
|
|
||||||
Screen:setDPI(dpi)
|
|
||||||
end
|
|
||||||
return {
|
|
||||||
text = _("Screen DPI"),
|
|
||||||
sub_item_table = {
|
|
||||||
{
|
|
||||||
text = _("Auto"),
|
|
||||||
checked_func = function()
|
|
||||||
return dpi() == nil
|
|
||||||
end,
|
|
||||||
callback = function() setDPI() end
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text = _("Small"),
|
|
||||||
checked_func = function()
|
|
||||||
local dpi, custom = dpi(), custom()
|
|
||||||
return dpi and dpi <= 140 and dpi ~= custom
|
|
||||||
end,
|
|
||||||
callback = function() setDPI(120) end
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text = _("Medium"),
|
|
||||||
checked_func = function()
|
|
||||||
local dpi, custom = dpi(), custom()
|
|
||||||
return dpi and dpi > 140 and dpi <= 200 and dpi ~= custom
|
|
||||||
end,
|
|
||||||
callback = function() setDPI(160) end
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text = _("Large"),
|
|
||||||
checked_func = function()
|
|
||||||
local dpi, custom = dpi(), custom()
|
|
||||||
return dpi and dpi > 200 and dpi ~= custom
|
|
||||||
end,
|
|
||||||
callback = function() setDPI(240) end
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text = _("Custom DPI") .. ": " .. (custom() or 160),
|
|
||||||
checked_func = function()
|
|
||||||
local dpi, custom = dpi(), custom()
|
|
||||||
return custom and dpi == custom
|
|
||||||
end,
|
|
||||||
callback = function() setDPI(custom() or 160) end,
|
|
||||||
hold_input = {
|
|
||||||
title = _("Input screen DPI"),
|
|
||||||
type = "number",
|
|
||||||
hint = "(90 - 330)",
|
|
||||||
callback = function(input)
|
|
||||||
local dpi = tonumber(input)
|
|
||||||
dpi = dpi < 90 and 90 or dpi
|
|
||||||
dpi = dpi > 330 and 330 or dpi
|
|
||||||
G_reader_settings:saveSetting("custom_screen_dpi", dpi)
|
|
||||||
setDPI(dpi)
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return Screen
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
|||||||
|
local _ = require("gettext")
|
||||||
|
local Screen = require("device").screen
|
||||||
|
|
||||||
|
|
||||||
|
local function dpi() return G_reader_settings:readSetting("screen_dpi") end
|
||||||
|
|
||||||
|
local function custom() return G_reader_settings:readSetting("custom_screen_dpi") end
|
||||||
|
|
||||||
|
local function setDPI(dpi)
|
||||||
|
local InfoMessage = require("ui/widget/infomessage")
|
||||||
|
local UIManager = require("ui/uimanager")
|
||||||
|
UIManager:show(InfoMessage:new{
|
||||||
|
text = _("This will take effect on next restart."),
|
||||||
|
})
|
||||||
|
Screen:setDPI(dpi)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
text = _("Screen DPI"),
|
||||||
|
sub_item_table = {
|
||||||
|
{
|
||||||
|
text = _("Auto"),
|
||||||
|
checked_func = function()
|
||||||
|
return dpi() == nil
|
||||||
|
end,
|
||||||
|
callback = function() setDPI() end
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text = _("Small"),
|
||||||
|
checked_func = function()
|
||||||
|
local dpi, custom = dpi(), custom()
|
||||||
|
return dpi and dpi <= 140 and dpi ~= custom
|
||||||
|
end,
|
||||||
|
callback = function() setDPI(120) end
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text = _("Medium"),
|
||||||
|
checked_func = function()
|
||||||
|
local dpi, custom = dpi(), custom()
|
||||||
|
return dpi and dpi > 140 and dpi <= 200 and dpi ~= custom
|
||||||
|
end,
|
||||||
|
callback = function() setDPI(160) end
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text = _("Large"),
|
||||||
|
checked_func = function()
|
||||||
|
local dpi, custom = dpi(), custom()
|
||||||
|
return dpi and dpi > 200 and dpi ~= custom
|
||||||
|
end,
|
||||||
|
callback = function() setDPI(240) end
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text = _("Custom DPI") .. ": " .. (custom() or 160),
|
||||||
|
checked_func = function()
|
||||||
|
local dpi, custom = dpi(), custom()
|
||||||
|
return custom and dpi == custom
|
||||||
|
end,
|
||||||
|
callback = function() setDPI(custom() or 160) end,
|
||||||
|
hold_input = {
|
||||||
|
title = _("Input screen DPI"),
|
||||||
|
type = "number",
|
||||||
|
hint = "(90 - 330)",
|
||||||
|
callback = function(input)
|
||||||
|
local dpi = tonumber(input)
|
||||||
|
dpi = dpi < 90 and 90 or dpi
|
||||||
|
dpi = dpi > 330 and 330 or dpi
|
||||||
|
G_reader_settings:saveSetting("custom_screen_dpi", dpi)
|
||||||
|
setDPI(dpi)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -1,861 +0,0 @@
|
|||||||
local Device = require("ui/device")
|
|
||||||
local GestureDetector = require("ui/gesturedetector")
|
|
||||||
local Event = require("ui/event")
|
|
||||||
local TimeVal = require("ui/timeval")
|
|
||||||
local Screen = require("ui/screen")
|
|
||||||
local input = require("ffi/input")
|
|
||||||
local util = require("ffi/util")
|
|
||||||
local Math = require("optmath")
|
|
||||||
local DEBUG = require("dbg")
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local _ = require("gettext")
|
|
||||||
|
|
||||||
-- constants from <linux/input.h>
|
|
||||||
local EV_SYN = 0
|
|
||||||
local EV_KEY = 1
|
|
||||||
local EV_ABS = 3
|
|
||||||
local EV_MSC = 4
|
|
||||||
|
|
||||||
-- key press event values (KEY.value)
|
|
||||||
local EVENT_VALUE_KEY_PRESS = 1
|
|
||||||
local EVENT_VALUE_KEY_REPEAT = 2
|
|
||||||
local EVENT_VALUE_KEY_RELEASE = 0
|
|
||||||
|
|
||||||
-- Synchronization events (SYN.code).
|
|
||||||
local SYN_REPORT = 0
|
|
||||||
local SYN_CONFIG = 1
|
|
||||||
local SYN_MT_REPORT = 2
|
|
||||||
|
|
||||||
-- For single-touch events (ABS.code).
|
|
||||||
local ABS_X = 00
|
|
||||||
local ABS_Y = 01
|
|
||||||
local ABS_PRESSURE = 24
|
|
||||||
|
|
||||||
-- For multi-touch events (ABS.code).
|
|
||||||
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
|
|
||||||
if KOBO_TOUCH_MIRRORED then
|
|
||||||
ABS_X = 01
|
|
||||||
ABS_Y = 00
|
|
||||||
ABS_MT_POSITION_X = 54
|
|
||||||
ABS_MT_POSITION_Y = 53
|
|
||||||
end
|
|
||||||
local ABS_MT_TRACKING_ID = 57
|
|
||||||
local ABS_MT_PRESSURE = 58
|
|
||||||
|
|
||||||
--[[
|
|
||||||
an interface for key presses
|
|
||||||
]]
|
|
||||||
|
|
||||||
local Key = {}
|
|
||||||
|
|
||||||
function Key:new(key, modifiers)
|
|
||||||
local o = { key = key, modifiers = modifiers }
|
|
||||||
|
|
||||||
-- we're a hash map, too
|
|
||||||
o[key] = true
|
|
||||||
for mod, pressed in pairs(modifiers) do
|
|
||||||
if pressed then
|
|
||||||
o[mod] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
function Key:__tostring()
|
|
||||||
return table.concat(self:getSequence(), "-")
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
get a sequence that can be matched against later
|
|
||||||
|
|
||||||
use this to let the user press a sequence and then
|
|
||||||
store this as configuration data (configurable
|
|
||||||
shortcuts)
|
|
||||||
]]
|
|
||||||
function Key:getSequence()
|
|
||||||
local seq = {}
|
|
||||||
for mod, pressed in pairs(self.modifiers) do
|
|
||||||
if pressed then
|
|
||||||
table.insert(seq, mod)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(seq, self.key)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
this will match a key against a sequence
|
|
||||||
|
|
||||||
the sequence should be a table of key names that
|
|
||||||
must be pressed together to match.
|
|
||||||
if an entry in this table is itself a table, at
|
|
||||||
least one key in this table must match.
|
|
||||||
|
|
||||||
E.g.:
|
|
||||||
|
|
||||||
Key:match({ "Alt", "K" }) -- match Alt-K
|
|
||||||
Key:match({ "Alt", { "K", "L" }}) -- match Alt-K _or_ Alt-L
|
|
||||||
]]
|
|
||||||
function Key:match(sequence)
|
|
||||||
local mod_keys = {} -- a hash table for checked modifiers
|
|
||||||
for _, key in ipairs(sequence) do
|
|
||||||
if type(key) == "table" then
|
|
||||||
local found = false
|
|
||||||
for _, variant in ipairs(key) do
|
|
||||||
if self[variant] then
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not found then
|
|
||||||
-- one of the needed keys is not pressed
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
elseif not self[key] then
|
|
||||||
-- needed key not pressed
|
|
||||||
return false
|
|
||||||
elseif self.modifiers[key] ~= nil then
|
|
||||||
-- checked key is a modifier key
|
|
||||||
mod_keys[key] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for mod, pressed in pairs(self.modifiers) do
|
|
||||||
if pressed and not mod_keys[mod] then
|
|
||||||
-- additional modifier keys are pressed, don't match
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
an interface to get input events
|
|
||||||
]]
|
|
||||||
local Input = {
|
|
||||||
event_map = {},
|
|
||||||
modifiers = {},
|
|
||||||
rotation_map = {
|
|
||||||
[0] = {},
|
|
||||||
[1] = { Up = "Right", Right = "Down", Down = "Left", Left = "Up" },
|
|
||||||
[2] = { Up = "Down", Right = "Left", Down = "Up", Left = "Right" },
|
|
||||||
[3] = { Up = "Left", Right = "Up", Down = "Right", Left = "Down" }
|
|
||||||
},
|
|
||||||
timer_callbacks = {},
|
|
||||||
disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP,
|
|
||||||
}
|
|
||||||
|
|
||||||
function Input:initKeyMap()
|
|
||||||
self.event_map = {
|
|
||||||
[2] = "1", [3] = "2", [4] = "3", [5] = "4", [6] = "5", [7] = "6", [8] = "7", [9] = "8", [10] = "9", [11] = "0",
|
|
||||||
[16] = "Q", [17] = "W", [18] = "E", [19] = "R", [20] = "T", [21] = "Y", [22] = "U", [23] = "I", [24] = "O", [25] = "P",
|
|
||||||
[30] = "A", [31] = "S", [32] = "D", [33] = "F", [34] = "G", [35] = "H", [36] = "J", [37] = "K", [38] = "L", [14] = "Del",
|
|
||||||
[44] = "Z", [45] = "X", [46] = "C", [47] = "V", [48] = "B", [49] = "N", [50] = "M", [52] = ".", [53] = "/", -- only KDX
|
|
||||||
|
|
||||||
[28] = "Enter",
|
|
||||||
[29] = "ScreenKB", -- K[4]
|
|
||||||
[42] = "Shift",
|
|
||||||
[56] = "Alt",
|
|
||||||
[57] = " ",
|
|
||||||
[90] = "AA", -- KDX
|
|
||||||
[91] = "Back", -- KDX
|
|
||||||
[92] = "Press", -- KDX
|
|
||||||
[94] = "Sym", -- KDX
|
|
||||||
[98] = "Home", -- KDX
|
|
||||||
[102] = "Home", -- K[3] & k[4]
|
|
||||||
[104] = "LPgBack", -- K[3] only
|
|
||||||
[103] = "Up", -- K[3] & k[4]
|
|
||||||
[105] = "Left",
|
|
||||||
[106] = "Right",
|
|
||||||
[108] = "Down", -- K[3] & k[4]
|
|
||||||
[109] = "RPgBack",
|
|
||||||
[114] = "VMinus",
|
|
||||||
[115] = "VPlus",
|
|
||||||
[122] = "Up", -- KDX
|
|
||||||
[123] = "Down", -- KDX
|
|
||||||
[124] = "RPgFwd", -- KDX
|
|
||||||
[126] = "Sym", -- K[3]
|
|
||||||
[139] = "Menu",
|
|
||||||
[158] = "Back", -- K[3] & K[4]
|
|
||||||
[190] = "AA", -- K[3]
|
|
||||||
[191] = "RPgFwd", -- K[3] & k[4]
|
|
||||||
[193] = "LPgFwd", -- K[3] only
|
|
||||||
[194] = "Press", -- K[3] & k[4]
|
|
||||||
}
|
|
||||||
self.sdl_event_map = {
|
|
||||||
[10] = "1", [11] = "2", [12] = "3", [13] = "4", [14] = "5", [15] = "6", [16] = "7", [17] = "8", [18] = "9", [19] = "0",
|
|
||||||
[24] = "Q", [25] = "W", [26] = "E", [27] = "R", [28] = "T", [29] = "Y", [30] = "U", [31] = "I", [32] = "O", [33] = "P",
|
|
||||||
[38] = "A", [39] = "S", [40] = "D", [41] = "F", [42] = "G", [43] = "H", [44] = "J", [45] = "K", [46] = "L",
|
|
||||||
[52] = "Z", [53] = "X", [54] = "C", [55] = "V", [56] = "B", [57] = "N", [58] = "M",
|
|
||||||
|
|
||||||
[22] = "Back", -- Backspace
|
|
||||||
[36] = "Enter", -- Enter
|
|
||||||
[50] = "Shift", -- left shift
|
|
||||||
[60] = ".",
|
|
||||||
[61] = "/",
|
|
||||||
[62] = "Sym", -- right shift key
|
|
||||||
[64] = "Alt", -- left alt
|
|
||||||
[65] = " ", -- Spacebar
|
|
||||||
[67] = "Menu", -- F[1]
|
|
||||||
[68] = "Power", -- F[2]
|
|
||||||
[72] = "LPgBack", -- F[6]
|
|
||||||
[73] = "LPgFwd", -- F[7]
|
|
||||||
[95] = "VPlus", -- F[11]
|
|
||||||
[96] = "VMinus", -- F[12]
|
|
||||||
[105] = "AA", -- right alt key
|
|
||||||
[110] = "Home", -- Home
|
|
||||||
[111] = "Up", -- arrow up
|
|
||||||
[112] = "RPgBack", -- normal PageUp
|
|
||||||
[113] = "Left", -- arrow left
|
|
||||||
[114] = "Right", -- arrow right
|
|
||||||
[115] = "Press", -- End (above arrows)
|
|
||||||
[116] = "Down", -- arrow down
|
|
||||||
[117] = "RPgFwd", -- normal PageDown
|
|
||||||
[119] = "Del", -- Delete
|
|
||||||
}
|
|
||||||
self.sdl2_event_map = {
|
|
||||||
[ 4] = "A", [ 5] = "B", [ 6] = "C", [ 7] = "D", [ 8] = "E", [ 9] = "F",
|
|
||||||
[10] = "G", [11] = "H", [12] = "I", [13] = "J", [14] = "K", [15] = "L",
|
|
||||||
[16] = "M", [17] = "N", [18] = "O", [19] = "P", [20] = "Q", [21] = "R",
|
|
||||||
[22] = "S", [23] = "T", [24] = "U", [25] = "V", [26] = "W", [27] = "X",
|
|
||||||
[28] = "Y", [29] = "Z", [30] = "1", [31] = "2", [32] = "3", [33] = "4",
|
|
||||||
[34] = "5", [35] = "6", [36] = "7", [37] = "8", [38] = "9", [39] = "0",
|
|
||||||
|
|
||||||
[42] = "Back", -- Backspace
|
|
||||||
[40] = "Enter", -- Enter
|
|
||||||
[225] = "Shift", -- left shift
|
|
||||||
[55] = ".",
|
|
||||||
[56] = "/",
|
|
||||||
[229] = "Sym", -- right shift key
|
|
||||||
[226] = "Alt", -- left alt
|
|
||||||
[44] = " ", -- Spacebar
|
|
||||||
[58] = "Menu", -- F[1]
|
|
||||||
[59] = "Power", -- F[2]
|
|
||||||
[63] = "LPgBack", -- F[6]
|
|
||||||
[64] = "LPgFwd", -- F[7]
|
|
||||||
[68] = "VPlus", -- F[11]
|
|
||||||
[69] = "VMinus", -- F[12]
|
|
||||||
[230] = "AA", -- right alt key
|
|
||||||
[74] = "Home", -- Home
|
|
||||||
[82] = "Up", -- arrow up
|
|
||||||
[75] = "RPgBack", -- normal PageUp
|
|
||||||
[80] = "Left", -- arrow left
|
|
||||||
[79] = "Right", -- arrow right
|
|
||||||
[77] = "Press", -- End (above arrows)
|
|
||||||
[81] = "Down", -- arrow down
|
|
||||||
[78] = "RPgFwd", -- normal PageDown
|
|
||||||
[76] = "Del", -- Delete
|
|
||||||
}
|
|
||||||
self.android_event_map = {
|
|
||||||
[29] = "A", [30] = "B", [31] = "C", [32] = "D", [33] = "E", [34] = "F",
|
|
||||||
[35] = "G", [36] = "H", [37] = "I", [38] = "J", [39] = "K", [40] = "L",
|
|
||||||
[41] = "M", [42] = "N", [43] = "O", [44] = "P", [45] = "Q", [46] = "R",
|
|
||||||
[47] = "S", [48] = "T", [49] = "U", [50] = "V", [51] = "W", [52] = "X",
|
|
||||||
[53] = "Y", [54] = "Z", [ 7] = "0", [ 8] = "1", [ 9] = "2", [10] = "3",
|
|
||||||
[11] = "4", [12] = "5", [13] = "6", [14] = "7", [15] = "8", [16] = "9",
|
|
||||||
|
|
||||||
[4] = "Back", -- BACK
|
|
||||||
[19] = "Up", -- DPAD_UP
|
|
||||||
[20] = "Down", -- DPAD_UP
|
|
||||||
[21] = "Left", -- DPAD_LEFT
|
|
||||||
[22] = "Right", -- DPAD_RIGHT
|
|
||||||
[23] = "Press", -- DPAD_CENTER
|
|
||||||
[24] = "LPgBack", -- VOLUME_UP
|
|
||||||
[25] = "LPgFwd", -- VOLUME_DOWN
|
|
||||||
[56] = ".", -- PERIOD
|
|
||||||
[59] = "Shift", -- SHIFT_LEFT
|
|
||||||
[60] = "Shift", -- SHIFT_RIGHT
|
|
||||||
[62] = " ", -- SPACE
|
|
||||||
[63] = "Sym", -- SYM
|
|
||||||
[66] = "Enter", -- ENTER
|
|
||||||
[67] = "Del", -- DEL
|
|
||||||
[76] = "/", -- SLASH
|
|
||||||
[82] = "Menu", -- MENU
|
|
||||||
[84] = "Search",--SEARCH
|
|
||||||
[92] = "LPgBack", -- PAGE_UP
|
|
||||||
[93] = "LPgFwd", -- PAGE_DOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
self.modifiers = {
|
|
||||||
Alt = false,
|
|
||||||
Shift = false
|
|
||||||
}
|
|
||||||
-- these groups are just helpers:
|
|
||||||
self.group = {
|
|
||||||
Cursor = { "Up", "Down", "Left", "Right" },
|
|
||||||
PgFwd = { "RPgFwd", "LPgFwd" },
|
|
||||||
PgBack = { "RPgBack", "LPgBack" },
|
|
||||||
Alphabet = {
|
|
||||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
|
||||||
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
|
||||||
},
|
|
||||||
AlphaNumeric = {
|
|
||||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
|
||||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
|
||||||
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
|
||||||
},
|
|
||||||
Numeric = {
|
|
||||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
|
|
||||||
},
|
|
||||||
Text = {
|
|
||||||
" ", ".", "/",
|
|
||||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
|
||||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
|
||||||
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
|
||||||
},
|
|
||||||
Any = {
|
|
||||||
" ", ".", "/",
|
|
||||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
|
||||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
|
||||||
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
|
||||||
"Up", "Down", "Left", "Right", "Press",
|
|
||||||
"Back", "Enter", "Sym", "AA", "Menu", "Home", "Del",
|
|
||||||
"LPgBack", "RPgBack", "LPgFwd", "RPgFwd"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:initTouchState()
|
|
||||||
self.cur_slot = 0
|
|
||||||
self.MTSlots = {}
|
|
||||||
self.ev_slots = {
|
|
||||||
[0] = {
|
|
||||||
slot = 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:init()
|
|
||||||
self:initKeyMap()
|
|
||||||
if Device:isTouchDevice() then
|
|
||||||
self:initTouchState()
|
|
||||||
end
|
|
||||||
-- set up fake event map
|
|
||||||
self.event_map[10000] = "IntoSS" -- go into screen saver
|
|
||||||
self.event_map[10001] = "OutOfSS" -- go out of screen saver
|
|
||||||
self.event_map[10020] = "Charging"
|
|
||||||
self.event_map[10021] = "NotCharging"
|
|
||||||
|
|
||||||
if util.isEmulated() then
|
|
||||||
-- open SDL if it's not dummy input device
|
|
||||||
if not self.dummy then input.open() end
|
|
||||||
-- SDL key codes
|
|
||||||
if not util.haveSDL2() then
|
|
||||||
self.event_map = self.sdl_event_map
|
|
||||||
else
|
|
||||||
self.event_map = self.sdl2_event_map
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local dev_mod = Device:getModel()
|
|
||||||
if not Device:isKobo() then
|
|
||||||
input.open("fake_events")
|
|
||||||
end
|
|
||||||
if dev_mod == "KindlePaperWhite" then
|
|
||||||
DEBUG("Auto-detected Kindle PaperWhite")
|
|
||||||
Device:setTouchInputDev("/dev/input/event0")
|
|
||||||
input.open("/dev/input/event0")
|
|
||||||
elseif dev_mod == "KindlePaperWhite2" then
|
|
||||||
DEBUG("Auto-detected Kindle PaperWhite")
|
|
||||||
Device:setTouchInputDev("/dev/input/event1")
|
|
||||||
input.open("/dev/input/event1")
|
|
||||||
elseif dev_mod == "KindleTouch" then
|
|
||||||
-- event0 in KindleTouch is "WM8962 Beep Generator" (useless)
|
|
||||||
-- event1 in KindleTouch is "imx-yoshi Headset" (useless)
|
|
||||||
Device:setTouchInputDev("/dev/input/event3")
|
|
||||||
input.open("/dev/input/event2") -- Home button
|
|
||||||
input.open("/dev/input/event3") -- touchscreen
|
|
||||||
-- KT does have one key!
|
|
||||||
self.event_map[102] = "Home"
|
|
||||||
-- update event hook
|
|
||||||
function Input:eventAdjustHook(ev)
|
|
||||||
if ev.type == EV_ABS then
|
|
||||||
--@TODO handle coordinates properly after
|
|
||||||
--screen rotate. (houqp)
|
|
||||||
if ev.code == ABS_MT_POSITION_X then
|
|
||||||
ev.value = Math.round(ev.value * (600/4095))
|
|
||||||
elseif ev.code == ABS_MT_POSITION_Y then
|
|
||||||
ev.value = Math.round(ev.value * (800/4095))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ev
|
|
||||||
end
|
|
||||||
DEBUG("Auto-detected Kindle Touch")
|
|
||||||
elseif Device:isKobo() then
|
|
||||||
local firm_rev = Device:getFirmVer()
|
|
||||||
input.open("/dev/input/event1")
|
|
||||||
Device:setTouchInputDev("/dev/input/event1")
|
|
||||||
input.open("/dev/input/event0") -- Light button and sleep slider
|
|
||||||
DEBUG("Auto-detected Kobo")
|
|
||||||
DEBUG("Device model=", dev_mod)
|
|
||||||
DEBUG("Firmware revision", firm_rev)
|
|
||||||
DEBUG("Screen width =", Screen:getWidth())
|
|
||||||
DEBUG("Screen height =", Screen:getHeight())
|
|
||||||
self:adjustKoboEventMap()
|
|
||||||
if dev_mod ~= 'Kobo_trilogy' then
|
|
||||||
function Input:eventAdjustHook(ev)
|
|
||||||
if ev.type == EV_ABS then
|
|
||||||
if ev.code == ABS_X then
|
|
||||||
ev.code = ABS_Y
|
|
||||||
elseif ev.code == ABS_Y then
|
|
||||||
ev.code = ABS_X
|
|
||||||
-- We always have to substract from the physical x,
|
|
||||||
-- regardless of the orientation
|
|
||||||
if (Screen:getWidth()<Screen:getHeight()) then
|
|
||||||
ev.value = Screen:getWidth() - ev.value
|
|
||||||
else
|
|
||||||
ev.value = Screen:getHeight() - ev.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- same thing for multitouch events (phoenix)
|
|
||||||
if ev.code == ABS_MT_POSITION_X then
|
|
||||||
ev.code = ABS_MT_POSITION_Y
|
|
||||||
elseif ev.code == ABS_MT_POSITION_Y then
|
|
||||||
ev.code = ABS_MT_POSITION_X
|
|
||||||
-- We always have to substract from the physical x,
|
|
||||||
-- regardless of the orientation
|
|
||||||
if (Screen:getWidth()<Screen:getHeight()) then
|
|
||||||
ev.value = Screen:getWidth() - ev.value
|
|
||||||
else
|
|
||||||
ev.value = Screen:getHeight() - ev.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ev
|
|
||||||
end
|
|
||||||
else -- kobo touch (trilogy)
|
|
||||||
-- FIXME some touch models should be treated as the other models,
|
|
||||||
-- depending on board revision
|
|
||||||
function Input:eventAdjustHook(ev)
|
|
||||||
if ev.code == ABS_X then
|
|
||||||
-- We always have to substract from the physical x,
|
|
||||||
-- regardless of the orientation
|
|
||||||
if (Screen:getWidth()<Screen:getHeight()) then
|
|
||||||
ev.value = Screen:getWidth() - ev.value
|
|
||||||
else
|
|
||||||
ev.value = Screen:getHeight() - ev.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ev
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif dev_mod == "Kindle4" then
|
|
||||||
DEBUG("Auto-detected Kindle 4")
|
|
||||||
input.open("/dev/input/event1")
|
|
||||||
self:adjustKindle4EventMap()
|
|
||||||
elseif dev_mod == "Kindle3" then
|
|
||||||
DEBUG("Auto-detected Kindle 3")
|
|
||||||
input.open("/dev/input/event1")
|
|
||||||
input.open("/dev/input/event2")
|
|
||||||
elseif dev_mod == "KindleDXG" then
|
|
||||||
DEBUG("Auto-detected Kindle DXG")
|
|
||||||
input.open("/dev/input/event0")
|
|
||||||
input.open("/dev/input/event1")
|
|
||||||
elseif dev_mod == "Kindle2" then
|
|
||||||
DEBUG("Auto-detected Kindle 2")
|
|
||||||
input.open("/dev/input/event1")
|
|
||||||
elseif util.isAndroid() then
|
|
||||||
DEBUG("Auto-detected Android")
|
|
||||||
self.event_map = self.android_event_map
|
|
||||||
self:adjustAndroidEventMap()
|
|
||||||
function Input:handleMiscEv(ev)
|
|
||||||
return Input:handleAndroidMiscEvent(ev)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
DEBUG("Not supported device model!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if Device:getModel() == 'Kobo_phoenix' or Device:getModel() == 'Kobo_dahlia' then
|
|
||||||
function Input:handleTouchEv(ev)
|
|
||||||
return Input:handlePhoenixTouchEv(ev)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
function Input:handleTouchEv(ev)
|
|
||||||
return Input:handleTypeBTouchEv(ev)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
different device models shoudl overload this method if
|
|
||||||
necessary to make event compatible to KPV.
|
|
||||||
--]]
|
|
||||||
function Input:eventAdjustHook(ev)
|
|
||||||
-- do nothing by default
|
|
||||||
return ev
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:adjustKindle4EventMap()
|
|
||||||
self.event_map[193] = "LPgBack"
|
|
||||||
self.event_map[104] = "LPgFwd"
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:adjustKoboEventMap()
|
|
||||||
self.event_map[59] = "Power_SleepCover"
|
|
||||||
self.event_map[90] = "Light"
|
|
||||||
self.event_map[116] = "Power"
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:adjustAndroidEventMap()
|
|
||||||
self.event_map[104] = "LPgBack" -- T68 PageUp
|
|
||||||
self.event_map[109] = "LPgFwd" -- T68 PageDown
|
|
||||||
self.event_map[139] = "Menu" -- T68 Menu
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:setTimeout(cb, tv_out)
|
|
||||||
local item = {
|
|
||||||
callback = cb,
|
|
||||||
deadline = tv_out,
|
|
||||||
}
|
|
||||||
table.insert(self.timer_callbacks, item)
|
|
||||||
table.sort(self.timer_callbacks, function(v1,v2)
|
|
||||||
return v1.deadline < v2.deadline
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:handleKeyBoardEv(ev)
|
|
||||||
local keycode = self.event_map[ev.code]
|
|
||||||
if not keycode then
|
|
||||||
-- do not handle keypress for keys we don't know
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- take device rotation into account
|
|
||||||
if self.rotation_map[Screen:getRotationMode()][keycode] then
|
|
||||||
keycode = self.rotation_map[Screen:getRotationMode()][keycode]
|
|
||||||
end
|
|
||||||
|
|
||||||
if keycode == "IntoSS" or keycode == "OutOfSS"
|
|
||||||
or keycode == "Charging" or keycode == "NotCharging" then
|
|
||||||
return keycode
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Kobo sleep
|
|
||||||
if keycode == "Power_SleepCover" then
|
|
||||||
if ev.value == EVENT_VALUE_KEY_PRESS then
|
|
||||||
return "Suspend"
|
|
||||||
else
|
|
||||||
return "Resume"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if ev.value == EVENT_VALUE_KEY_RELEASE
|
|
||||||
and (keycode == "Light" or keycode == "Power") then
|
|
||||||
return keycode
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle modifier keys
|
|
||||||
if self.modifiers[keycode] ~= nil then
|
|
||||||
if ev.value == EVENT_VALUE_KEY_PRESS then
|
|
||||||
self.modifiers[keycode] = true
|
|
||||||
elseif ev.value == EVENT_VALUE_KEY_RELEASE then
|
|
||||||
self.modifiers[keycode] = false
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local key = Key:new(keycode, self.modifiers)
|
|
||||||
|
|
||||||
if ev.value == EVENT_VALUE_KEY_PRESS then
|
|
||||||
return Event:new("KeyPress", key)
|
|
||||||
elseif ev.value == EVENT_VALUE_KEY_RELEASE then
|
|
||||||
return Event:new("KeyRelease", key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:handleMiscEv(ev)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:handleAndroidMiscEvent(ev)
|
|
||||||
if ev.code == ffi.C.APP_CMD_SAVE_STATE then
|
|
||||||
return "SaveState"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:setMtSlot(slot, key, val)
|
|
||||||
if not self.ev_slots[slot] then
|
|
||||||
self.ev_slots[slot] = {
|
|
||||||
slot = slot
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
self.ev_slots[slot][key] = val
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:setCurrentMtSlot(key, val)
|
|
||||||
self:setMtSlot(self.cur_slot, key, val)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:getMtSlot(slot)
|
|
||||||
return self.ev_slots[slot]
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:getCurrentMtSlot()
|
|
||||||
return self:getMtSlot(self.cur_slot)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:confirmAbsxy()
|
|
||||||
self:setCurrentMtSlot("x", self.ev_slots[self.cur_slot]["abs_x"])
|
|
||||||
self:setCurrentMtSlot("y", self.ev_slots[self.cur_slot]["abs_y"])
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:cleanAbsxy()
|
|
||||||
self:setCurrentMtSlot("abs_x", nil)
|
|
||||||
self:setCurrentMtSlot("abs_y", nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
parse each touch ev from kernel and build up tev.
|
|
||||||
tev will be sent to GestureDetector:feedEvent
|
|
||||||
|
|
||||||
Events for a single tap motion from Linux kernel (MT protocol B):
|
|
||||||
|
|
||||||
MT_TRACK_ID: 0
|
|
||||||
MT_X: 222
|
|
||||||
MT_Y: 207
|
|
||||||
SYN REPORT
|
|
||||||
MT_TRACK_ID: -1
|
|
||||||
SYN REPORT
|
|
||||||
|
|
||||||
Notice that each line is a single event.
|
|
||||||
|
|
||||||
From kernel document:
|
|
||||||
For type B devices, the kernel driver should associate a slot with each
|
|
||||||
identified contact, and use that slot to propagate changes for the contact.
|
|
||||||
Creation, replacement and destruction of contacts is achieved by modifying
|
|
||||||
the ABS_MT_TRACKING_ID of the associated slot. A non-negative tracking id
|
|
||||||
is interpreted as a contact, and the value -1 denotes an unused slot. A
|
|
||||||
tracking id not previously present is considered new, and a tracking id no
|
|
||||||
longer present is considered removed. Since only changes are propagated,
|
|
||||||
the full state of each initiated contact has to reside in the receiving
|
|
||||||
end. Upon receiving an MT event, one simply updates the appropriate
|
|
||||||
attribute of the current slot.
|
|
||||||
--]]
|
|
||||||
function Input:handleTypeBTouchEv(ev)
|
|
||||||
if ev.type == EV_ABS then
|
|
||||||
if #self.MTSlots == 0 then
|
|
||||||
table.insert(self.MTSlots, self:getMtSlot(self.cur_slot))
|
|
||||||
end
|
|
||||||
if ev.code == ABS_MT_SLOT then
|
|
||||||
if self.cur_slot ~= ev.value then
|
|
||||||
table.insert(self.MTSlots, self:getMtSlot(ev.value))
|
|
||||||
end
|
|
||||||
self.cur_slot = ev.value
|
|
||||||
elseif ev.code == ABS_MT_TRACKING_ID then
|
|
||||||
self:setCurrentMtSlot("id", ev.value)
|
|
||||||
elseif ev.code == ABS_MT_POSITION_X then
|
|
||||||
self:setCurrentMtSlot("x", ev.value)
|
|
||||||
elseif ev.code == ABS_MT_POSITION_Y then
|
|
||||||
self:setCurrentMtSlot("y", ev.value)
|
|
||||||
|
|
||||||
-- code to emulate mt protocol on kobos
|
|
||||||
-- we "confirm" abs_x, abs_y only when pressure ~= 0
|
|
||||||
elseif ev.code == ABS_X then
|
|
||||||
self:setCurrentMtSlot("abs_x", ev.value)
|
|
||||||
elseif ev.code == ABS_Y then
|
|
||||||
self:setCurrentMtSlot("abs_y", ev.value)
|
|
||||||
elseif ev.code == ABS_PRESSURE then
|
|
||||||
if ev.value ~= 0 then
|
|
||||||
self:setCurrentMtSlot("id", 1)
|
|
||||||
self:confirmAbsxy()
|
|
||||||
else
|
|
||||||
self:cleanAbsxy()
|
|
||||||
self:setCurrentMtSlot("id", -1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif ev.type == EV_SYN then
|
|
||||||
if ev.code == SYN_REPORT then
|
|
||||||
for _, MTSlot in pairs(self.MTSlots) do
|
|
||||||
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
|
|
||||||
end
|
|
||||||
-- feed ev in all slots to state machine
|
|
||||||
local touch_ges = GestureDetector:feedEvent(self.MTSlots)
|
|
||||||
self.MTSlots = {}
|
|
||||||
if touch_ges then
|
|
||||||
return Event:new("Gesture",
|
|
||||||
GestureDetector:adjustGesCoordinate(touch_ges)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:handlePhoenixTouchEv(ev)
|
|
||||||
-- Hack on handleTouchEV for the Kobo Aura
|
|
||||||
-- It seems to be using a custom protocol:
|
|
||||||
-- finger 0 down:
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_TRACKING_ID, 0);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_TOUCH_MAJOR, 1);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_WIDTH_MAJOR, 1);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_X, x1);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_Y, y1);
|
|
||||||
-- input_mt_sync (elan_touch_data.input);
|
|
||||||
-- finger 1 down:
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_TRACKING_ID, 1);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_TOUCH_MAJOR, 1);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_WIDTH_MAJOR, 1);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_X, x2);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_Y, y2);
|
|
||||||
-- input_mt_sync (elan_touch_data.input);
|
|
||||||
-- finger 0 up:
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_TRACKING_ID, 0);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_TOUCH_MAJOR, 0);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_WIDTH_MAJOR, 0);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_X, last_x);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_Y, last_y);
|
|
||||||
-- input_mt_sync (elan_touch_data.input);
|
|
||||||
-- finger 1 up:
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_TRACKING_ID, 1);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_TOUCH_MAJOR, 0);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_WIDTH_MAJOR, 0);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_X, last_x2);
|
|
||||||
-- input_report_abs(elan_touch_data.input, ABS_MT_POSITION_Y, last_y2);
|
|
||||||
-- input_mt_sync (elan_touch_data.input);
|
|
||||||
if ev.type == EV_ABS then
|
|
||||||
if #self.MTSlots == 0 then
|
|
||||||
table.insert(self.MTSlots, self:getMtSlot(self.cur_slot))
|
|
||||||
end
|
|
||||||
if ev.code == ABS_MT_TRACKING_ID then
|
|
||||||
if self.cur_slot ~= ev.value then
|
|
||||||
table.insert(self.MTSlots, self:getMtSlot(ev.value))
|
|
||||||
end
|
|
||||||
self.cur_slot = ev.value
|
|
||||||
self:setCurrentMtSlot("id", ev.value)
|
|
||||||
elseif ev.code == ABS_MT_TOUCH_MAJOR and ev.value == 0 then
|
|
||||||
self:setCurrentMtSlot("id", -1)
|
|
||||||
elseif ev.code == ABS_MT_POSITION_X then
|
|
||||||
self:setCurrentMtSlot("x", ev.value)
|
|
||||||
elseif ev.code == ABS_MT_POSITION_Y then
|
|
||||||
self:setCurrentMtSlot("y", ev.value)
|
|
||||||
end
|
|
||||||
elseif ev.type == EV_SYN then
|
|
||||||
if ev.code == SYN_REPORT then
|
|
||||||
for _, MTSlot in pairs(self.MTSlots) do
|
|
||||||
self:setMtSlot(MTSlot.slot, "timev", TimeVal:new(ev.time))
|
|
||||||
end
|
|
||||||
-- feed ev in all slots to state machine
|
|
||||||
local touch_ges = GestureDetector:feedEvent(self.MTSlots)
|
|
||||||
self.MTSlots = {}
|
|
||||||
if touch_ges then
|
|
||||||
return Event:new("Gesture",
|
|
||||||
GestureDetector:adjustGesCoordinate(touch_ges)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:waitEvent(timeout_us, timeout_s)
|
|
||||||
-- wrapper for input.waitForEvents that will retry for some cases
|
|
||||||
local ok, ev
|
|
||||||
local wait_deadline = TimeVal:now() + TimeVal:new{
|
|
||||||
sec = timeout_s,
|
|
||||||
usec = timeout_us
|
|
||||||
}
|
|
||||||
while true do
|
|
||||||
if #self.timer_callbacks > 0 then
|
|
||||||
-- we don't block if there is any timer, set wait to 10us
|
|
||||||
while #self.timer_callbacks > 0 do
|
|
||||||
ok, ev = pcall(input.waitForEvent, 100)
|
|
||||||
if ok then break end
|
|
||||||
local tv_now = TimeVal:now()
|
|
||||||
if ((not timeout_us and not timeout_s) or tv_now < wait_deadline) then
|
|
||||||
-- check whether timer is up
|
|
||||||
if tv_now >= self.timer_callbacks[1].deadline then
|
|
||||||
local touch_ges = self.timer_callbacks[1].callback()
|
|
||||||
table.remove(self.timer_callbacks, 1)
|
|
||||||
if touch_ges then
|
|
||||||
-- Do we really need to clear all setTimeout after
|
|
||||||
-- decided a gesture? FIXME
|
|
||||||
Input.timer_callbacks = {}
|
|
||||||
return Event:new("Gesture",
|
|
||||||
GestureDetector:adjustGesCoordinate(touch_ges)
|
|
||||||
)
|
|
||||||
end -- EOF if touch_ges
|
|
||||||
end -- EOF if deadline reached
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end -- EOF if not exceed wait timeout
|
|
||||||
end -- while #timer_callbacks > 0
|
|
||||||
else
|
|
||||||
ok, ev = pcall(input.waitForEvent, timeout_us)
|
|
||||||
end -- EOF if #timer_callbacks > 0
|
|
||||||
if ok then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
if ev == "Waiting for input failed: timeout\n" then
|
|
||||||
-- don't report an error on timeout
|
|
||||||
ev = nil
|
|
||||||
break
|
|
||||||
elseif ev == "application forced to quit" then
|
|
||||||
os.exit(0)
|
|
||||||
end
|
|
||||||
--DEBUG("got error waiting for events:", ev)
|
|
||||||
if ev ~= "Waiting for input failed: 4\n" then
|
|
||||||
-- we only abort if the error is not EINTR
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if ok and ev then
|
|
||||||
if DEBUG.is_on and ev then
|
|
||||||
DEBUG:logEv(ev)
|
|
||||||
end
|
|
||||||
ev = self:eventAdjustHook(ev)
|
|
||||||
if ev.type == EV_KEY then
|
|
||||||
DEBUG("key ev", ev)
|
|
||||||
return self:handleKeyBoardEv(ev)
|
|
||||||
elseif ev.type == EV_ABS or ev.type == EV_SYN then
|
|
||||||
return self:handleTouchEv(ev)
|
|
||||||
elseif ev.type == EV_MSC then
|
|
||||||
return self:handleMiscEv(ev)
|
|
||||||
else
|
|
||||||
-- some other kind of event that we do not know yet
|
|
||||||
return Event:new("GenericInput", ev)
|
|
||||||
end
|
|
||||||
elseif not ok and ev then
|
|
||||||
return Event:new("InputError", ev)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
helper function for formatting sequence definitions for output
|
|
||||||
]]
|
|
||||||
function Input:sequenceToString(sequence)
|
|
||||||
local modifiers = {}
|
|
||||||
local keystring = {"",""} -- first entries reserved for modifier specification
|
|
||||||
for _, key in ipairs(sequence) do
|
|
||||||
if type(key) == "table" then
|
|
||||||
local alternatives = {}
|
|
||||||
for _, alternative in ipairs(key) do
|
|
||||||
table.insert(alternatives, alternative)
|
|
||||||
end
|
|
||||||
table.insert(keystring, "{")
|
|
||||||
table.insert(keystring, table.concat(alternatives, "|"))
|
|
||||||
table.insert(keystring, "}")
|
|
||||||
elseif self.modifiers[key] ~= nil then
|
|
||||||
table.insert(modifiers, key)
|
|
||||||
else
|
|
||||||
table.insert(keystring, key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #modifiers then
|
|
||||||
keystring[1] = table.concat(modifiers, "-")
|
|
||||||
keystring[2] = "-"
|
|
||||||
end
|
|
||||||
return table.concat(keystring)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- initialize the GestureDectector
|
|
||||||
-- so it can modify our (Input) state
|
|
||||||
GestureDetector.input = Input
|
|
||||||
|
|
||||||
return Input
|
|
@ -1,2 +1,2 @@
|
|||||||
-- compatibility wrapper
|
-- compatibility wrapper
|
||||||
return require("ui/device").screen
|
return require("device").screen
|
||||||
|
Loading…
Reference in New Issue