Merge pull request #732 from houqp/rotate

Screen rotate support for new ui
pull/2/merge
Huang Xin 11 years ago
commit 756785e171

@ -30,7 +30,18 @@
#define ASSERT_BLITBUFFER_BOUNDARIES(bb,bb_ptr) {}
#endif // DEBUG
inline int setPixel(BlitBuffer *bb, int x, int y, int c) {
inline int getPixel(BlitBuffer *bb, int x, int y) {
uint8_t *dstptr = (uint8_t*)(bb->data) + (y * bb->pitch) + (x / 2);
ASSERT_BLITBUFFER_BOUNDARIES(bb, dstptr);
if(x % 2 == 0) {
return (*dstptr & 0xF0) >> 4;
} else {
return *dstptr & 0x0F;
}
}
inline void setPixel(BlitBuffer *bb, int x, int y, int c) {
uint8_t *dstptr = (uint8_t*)(bb->data) + (y * bb->pitch) + (x / 2);
ASSERT_BLITBUFFER_BOUNDARIES(bb, dstptr);
@ -41,7 +52,6 @@ inline int setPixel(BlitBuffer *bb, int x, int y, int c) {
*dstptr &= 0xF0;
*dstptr |= c;
}
return 0;
}
/*
@ -252,6 +262,64 @@ static int blitToBuffer(lua_State *L) {
return 0;
}
static int rotate_table[3][2] = {
/* cos, sin */
{0, 1}, /* 90 degree */
{-1, 0}, /* 180 degree */
{0, -1}, /* 270 degree */
};
/** @brief rotate and blit to buffer
*
* Currently, only support rotation of 90, 180, 270 degree.
* */
static int blitToBufferRotate(lua_State *L) {
BlitBuffer *dst = (BlitBuffer*) luaL_checkudata(L, 1, "blitbuffer");
BlitBuffer *src = (BlitBuffer*) luaL_checkudata(L, 2, "blitbuffer");
int degree = luaL_checkint(L, 3);
int i, j, x, y, /* src and dst coordinate */
x_adj = 0, y_adj = 0; /* value for translation after rotatation */
int cosT = rotate_table[degree/90-1][0],
sinT = rotate_table[degree/90-1][1],
u, v;
switch (degree) {
case 180:
y_adj = dst->h;
case 90:
x_adj = dst->w;
break;
case 270:
y_adj = dst->h;
break;
}
u = x_adj;
v = y_adj;
for (j = 0; j < src->h; j++) {
/*
* x = -sinT * j + x_adj;
* y = cosT * j + y_adj;
*/
x = u;
y = v;
for (i = 0; i < src->w; i++) {
/*
* each (i, j) maps to (x, y)
* x = cosT * i - sinT * j + x_adj;
* y = cosT * j + sinT * i + y_adj;
*/
setPixel(dst, x, y, getPixel(src, i, j));
x += cosT;
y += sinT;
}
u -= sinT;
v += cosT;
}
return 0;
}
static int addblitToBuffer(lua_State *L) {
BlitBuffer *dst = (BlitBuffer*) luaL_checkudata(L, 1, "blitbuffer");
BlitBuffer *src = (BlitBuffer*) luaL_checkudata(L, 2, "blitbuffer");
@ -772,6 +840,7 @@ static const struct luaL_Reg blitbuffer_meth[] = {
{"getWidth", getWidth},
{"getHeight", getHeight},
{"blitFrom", blitToBuffer},
{"blitFromRotate", blitToBufferRotate},
{"addblitFrom", addblitToBuffer},
{"blitFullFrom", blitFullToBuffer},
{"paintRect", paintRect},

@ -4,6 +4,19 @@ CreOptions = {
prefix = 'copt',
default_options = {
},
{
icon = "resources/icons/appbar.transform.rotate.right.large.png",
options = {
{
name = "screen_mode",
name_text = "Screen Mode",
toggle = {"portrait", "landscape"},
args = {"portrait", "landscape"},
default_arg = Screen:getScreenMode(),
event = "SetScreenMode",
}
}
},
{
icon = "resources/icons/appbar.column.two.large.png",
options = {

@ -23,12 +23,12 @@ KoptOptions = {
icon = "resources/icons/appbar.transform.rotate.right.large.png",
options = {
{
name="screen_mode",
name = "screen_mode",
name_text = "Screen Mode",
toggle = {"portrait", "landscape"},
values = {1, 0},
default_value = 1,
-- TODO: add screen mode changing command
args = {"portrait", "landscape"},
default_arg = Screen:getScreenMode(),
event = "SetScreenMode",
}
}
},
@ -36,7 +36,7 @@ KoptOptions = {
icon = "resources/icons/appbar.crop.large.png",
options = {
{
name="trim_page",
name = "trim_page",
name_text = "Page Crop",
toggle = {"auto", "manual"},
alternate = false,

@ -83,27 +83,25 @@ 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
Screen:saveCurrentBB()
--msg = InfoMessage:new{"Going into screensaver... "}
--UIManager:show(msg)
Screen.kpv_rotation_mode = Screen.cur_rotation_mode
Screen.fb:setOrientation(Screen.native_rotation_mode)
--UIManager:show(InfoMessage:new{
--text = "Going into screensaver... ",
--timeout = 2,
--})
--util.sleep(1)
--os.execute("killall -cont cvm")
self.screen_saver_mode = true
--UIManager:close(msg)
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")
Screen.fb:setOrientation(Screen.kpv_rotation_mode)
Screen:restoreFromSavedBB()
Screen.fb:refresh(0)
Screen:refresh(0)
end
self.screen_saver_mode = false
end
@ -112,13 +110,12 @@ 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
Screen:saveCurrentBB()
Screen.kpv_rotation_mode = Screen.cur_rotation_mode
Screen.fb:setOrientation(Screen.native_rotation_mode)
msg = InfoMessage:new{"Going into USB mode... "}
UIManager:show(msg)
util.sleep(1)
UIManager:close(msg)
os.execute("killall -cont cvm")
--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
@ -126,16 +123,12 @@ 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")
Screen.fb:setOrientation(Screen.kpv_rotation_mode)
--util.usleep(1500000)
--os.execute("killall -stop cvm")
Screen:restoreFromSavedBB()
Screen.fb:refresh(0)
Screen:refresh(0)
end
--@TODO signal filemanager for file changes 13.06 2012 (houqp)
--FileChooser:setPath(FileChooser.path)
--FileChooser.pagedirty = true
self.charging_mode = false
end

@ -33,14 +33,22 @@ Currently supported gestures:
* pan
* swipe
Single tap event from kernel example:
You change the state machine by feeding it touch events, i.e. calling
GestureDetector:feedEvent(tev).
MT_TRACK_ID: 0
MT_X: 222
MT_Y: 207
SYN REPORT
MT_TRACK_ID: -1
SYN REPORT
a touch event should have following format:
tev = {
slot = 1,
id = 46,
x = 0,
y = 1,
timev = TimeVal:new{...},
}
Don't confuse tev with raw evs from kernel, tev is build according to ev.
GestureDetector:feedEvent(tev) will return a detection result when you
feed a touch release event to it.
--]]
GestureDetector = {
@ -54,41 +62,22 @@ GestureDetector = {
track_id = {},
ev_stack = {},
cur_ev = {},
-- latest feeded touch event
last_ev = {},
is_ev_start = false,
first_ev = nil,
state = function(self, ev)
self:switchState("initialState", ev)
end,
last_ev_timev = nil,
last_tap = nil, -- for single/double tap
}
function GestureDetector:feedEvent(ev)
--DEBUG(ev.type, ev.code, ev.value, ev.time)
if ev.type == EV_SYN then
if ev.code == SYN_REPORT then
self.cur_ev.timev = TimeVal:new(ev.time)
local re = self.state(self, self.cur_ev)
self.last_ev_timev = self.cur_ev.timev
if re ~= nil then
return re
end
self.cur_ev = {}
end
elseif ev.type == EV_ABS then
if ev.code == ABS_MT_SLOT then
self.cur_ev.slot = ev.value
elseif ev.code == ABS_MT_TRACKING_ID then
self.cur_ev.id = ev.value
elseif ev.code == ABS_MT_POSITION_X then
self.cur_ev.x = ev.value
elseif ev.code == ABS_MT_POSITION_Y then
self.cur_ev.y = ev.value
end
function GestureDetector:feedEvent(tev)
if tev.id ~= -1 then
self.last_ev = tev
end
return self.state(self, tev)
end
function GestureDetector:deepCopyEv(ev)
@ -120,11 +109,11 @@ end
compare last_pan with self.first_ev
if it is a swipe, return direction of swipe gesture.
--]]
function GestureDetector:isSwipe(last_pan_ev)
local tv_diff = self.first_ev.timev - last_pan_ev.timev
function GestureDetector:isSwipe()
local tv_diff = self.first_ev.timev - self.last_ev.timev
if (tv_diff.sec == 0) and (tv_diff.usec < self.SWIPE_INTERVAL) then
x_diff = last_pan_ev.x - self.first_ev.x
y_diff = last_pan_ev.y - self.first_ev.y
x_diff = self.last_ev.x - self.first_ev.x
y_diff = self.last_ev.y - self.first_ev.y
if x_diff == 0 and y_diff == 0 then
return nil
end
@ -157,10 +146,8 @@ function GestureDetector:switchState(state_new, ev)
end
function GestureDetector:clearState()
self.cur_x = nil
self.cur_y = nil
self.state = self.initialState
self.cur_ev = {}
self.last_ev = {}
self.is_ev_start = false
self.first_ev = nil
end
@ -196,15 +183,15 @@ function GestureDetector:tapState(ev)
-- default to single tap
ges = "tap",
pos = Geom:new{
x = self.cur_x,
y = self.cur_y,
x = self.last_ev.x,
y = self.last_ev.y,
w = 0, h = 0,
}
}
-- cur_tap is used for double tap detection
local cur_tap = {
x = self.cur_x,
y = self.cur_y,
x = self.last_ev.x,
y = self.last_ev.y,
timev = ev.timev,
}
@ -221,7 +208,7 @@ function GestureDetector:tapState(ev)
self.last_tap = cur_tap
DEBUG("set up tap timer")
local deadline = self.cur_ev.timev + TimeVal:new{
local deadline = self.last_ev.timev + TimeVal:new{
sec = 0, usec = self.DOUBLE_TAP_INTERVAL,
}
Input:setTimeout(function()
@ -241,10 +228,8 @@ function GestureDetector:tapState(ev)
-- switched from other state, probably from initialState
-- we return nil in this case
self.state = self.tapState
self.cur_x = ev.x
self.cur_y = ev.y
DEBUG("set up hold timer")
local deadline = self.cur_ev.timev + TimeVal:new{
local deadline = self.last_ev.timev + TimeVal:new{
sec = 0, usec = self.HOLD_INTERVAL
}
Input:setTimeout(function()
@ -269,16 +254,7 @@ function GestureDetector:panState(ev)
DEBUG("in pan state...")
if ev.id == -1 then
-- end of pan, signal swipe gesture if necessary
-- we need to construct a complete_last_ev because
-- the x or y of ev might be nil.
local complete_last_ev = self:deepCopyEv(ev)
if not complete_last_ev.x then
complete_last_ev.x = self.cur_x
end
if not complete_last_ev.y then
complete_last_ev.y = self.cur_y
end
swipe_direct = self:isSwipe(complete_last_ev)
swipe_direct = self:isSwipe()
if swipe_direct then
local start_pos = Geom:new{
x = self.first_ev.x,
@ -309,19 +285,14 @@ function GestureDetector:panState(ev)
},
pos = nil,
}
if ev.x then
pan_ev.relative.x = ev.x - self.cur_x
self.cur_x = ev.x
end
if ev.y then
pan_ev.relative.y = ev.y - self.cur_y
self.cur_y = ev.y
end
pan_ev.relative.x = ev.x - self.last_ev.x
pan_ev.relative.y = ev.y - self.last_ev.y
pan_ev.pos = Geom:new{
x = self.cur_x,
y = self.cur_y,
x = self.last_ev.x,
y = self.last_ev.y,
w = 0, h = 0,
}
self.last_ev = ev
return pan_ev
end
end
@ -330,13 +301,13 @@ function GestureDetector:holdState(ev)
DEBUG("in hold state...")
-- when we switch to hold state, we pass no ev
-- so ev = nil
if not ev and self.cur_x and self.cur_y then
if not ev and self.last_ev.x and self.last_ev.y then
self.state = self.holdState
return {
ges = "hold",
pos = Geom:new{
x = self.cur_x,
y = self.cur_y,
x = self.last_ev.x,
y = self.last_ev.y,
w = 0, h = 0,
}
}
@ -347,8 +318,8 @@ function GestureDetector:holdState(ev)
return {
ges = "hold_release",
pos = Geom:new{
x = self.cur_x,
y = self.cur_y,
x = self.last_ev.x,
y = self.last_ev.y,
w = 0, h = 0,
}
}

@ -9,14 +9,15 @@ it vanishes on key press or after a given timeout
InfoMessage = InputContainer:new{
face = Font:getFace("infofont", 25),
text = "",
timeout = nil,
key_events = {
AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" }
}
timeout = nil, -- in seconds
}
function InfoMessage:init()
if Device:hasKeyboard() then
key_events = {
AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" }
}
end
-- we construct the actual content here because self.text is only available now
self[1] = CenterContainer:new{
dimen = Screen:getSize(),

@ -122,7 +122,19 @@ end
an interface to get input events
]]
Input = {
event_map = {
event_map = {},
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" }
},
rotation = 0,
timer_callbacks = {},
}
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",
@ -157,13 +169,8 @@ Input = {
[191] = "RPgFwd", -- K[3] & k[4]
[193] = "LPgFwd", -- K[3] only
[194] = "Press", -- K[3] & k[4]
[10000] = "IntoSS", -- go into screen saver
[10001] = "OutOfSS", -- go out of screen saver
[10020] = "Charging",
[10021] = "NotCharging",
},
sdl_event_map = {
}
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",
@ -192,21 +199,13 @@ Input = {
[116] = "Down", -- arrow down
[117] = "RPgFwd", -- normal PageDown
[119] = "Del", -- Delete
},
rotation = 0,
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" }
},
modifiers = {
}
self.modifiers = {
Alt = false,
Shift = false
},
}
-- these groups are just helpers:
group = {
self.group = {
Cursor = { "Up", "Down", "Left", "Right" },
PgFwd = { "RPgFwd", "LPgFwd" },
PgBack = { "RPgBack", "LPgBack" },
@ -237,13 +236,31 @@ Input = {
"Back", "Enter", "Sym", "AA", "Menu", "Home", "Del",
"LPgBack", "RPgBack", "LPgFwd", "RPgFwd"
}
},
}
end
timer_callbacks = {},
}
function Input:initTouchState()
self.cur_ev = {}
end
function Input:init()
-- Screen module must have been initilized by now.
self.rotation = Screen:getRotationMode()
if Device:hasKeyboard() then
self:initKeyMap()
end
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()==1 then
self:initKeyMap()
os.remove("emu_event")
os.execute("mkfifo emu_event")
input.open("emu_event")
@ -327,6 +344,86 @@ function Input:setTimeout(cb, tv_out)
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.rotation][keycode] then
keycode = self.rotation_map[self.rotation][keycode]
end
if keycode == "IntoSS" or keycode == "OutOfSS"
or keycode == "Charging" or keycode == "NotCharging" 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
--[[
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.
--]]
function Input:handleTouchEv(ev)
if ev.type == EV_SYN then
if ev.code == SYN_REPORT then
self.cur_ev.timev = TimeVal:new(ev.time)
--self.cur_x = self.cur_ev.x or self.cur_x
--self.cur_y = self.cur_ev.y or self.cur_y
-- send ev to state machine
local touch_ges = GestureDetector:feedEvent(self.cur_ev)
--self.last_ev_timev = self.cur_ev.timev
--self.cur_ev = {}
if touch_ges then
return Event:new("Gesture",
Screen:adjustGesCoordinate(touch_ges)
)
end
end
elseif ev.type == EV_ABS then
if ev.code == ABS_MT_SLOT then
self.cur_ev.slot = ev.value
elseif ev.code == ABS_MT_TRACKING_ID then
self.cur_ev.id = ev.value
elseif ev.code == ABS_MT_POSITION_X then
self.cur_ev.x = ev.value
elseif ev.code == ABS_MT_POSITION_Y then
self.cur_ev.y = ev.value
end
end
end
function Input:waitEvent(timeout_us, timeout_s)
-- wrapper for input.waitForEvents that will retry for some cases
local ok, ev
@ -344,14 +441,16 @@ function Input:waitEvent(timeout_us, timeout_s)
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 ges = self.timer_callbacks[1].callback()
local touch_ges = self.timer_callbacks[1].callback()
table.remove(self.timer_callbacks, 1)
if ges then
if touch_ges then
-- Do we really need to clear all setTimeout after
-- decided a gesture? FIXME
Input.timer_callbacks = {}
return Event:new("Gesture", ges)
end -- EOF if ges
return Event:new("Gesture",
Screen:adjustGesCoordinate(touch_ges)
)
end -- EOF if touch_ges
end -- EOF if deadline reached
else
break
@ -376,47 +475,13 @@ function Input:waitEvent(timeout_us, timeout_s)
break
end
end
if ok and ev then
ev = self:eventAdjustHook(ev)
if ev.type == EV_KEY then
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.rotation][keycode] then
keycode = self.rotation_map[self.rotation][keycode]
end
if keycode == "IntoSS" or keycode == "OutOfSS"
or keycode == "Charging" or keycode == "NotCharging" 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
return self:handleKeyBoardEv(ev)
elseif ev.type == EV_ABS or ev.type == EV_SYN then
local touch_ges = GestureDetector:feedEvent(ev)
if touch_ges then
return Event:new("Gesture", touch_ges)
end
return self:handleTouchEv(ev)
else
-- some other kind of event that we do not know yet
return Event:new("GenericInput", ev)

@ -8,13 +8,14 @@ Notification = InputContainer:new{
face = Font:getFace("infofont", 20),
text = "Null Message",
timeout = nil,
key_events = {
AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" }
}
}
function Notification:init()
if Device:hasKeyboard() then
key_events = {
AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" }
}
end
-- we construct the actual content here because self.text is only available now
self[1] = CenterContainer:new{
dimen = Geom:new{

@ -12,23 +12,25 @@ function ReaderBookmark:init()
{ "B" },
doc = "show bookmarks" },
}
elseif Device:isTouchDevice() then
self.ges_events = {
AddBookmark = {
GestureRange:new{
ges = "double_tap",
range = Geom:new{
x = Screen:getWidth()/2, y = 0,
w = Screen:getWidth()/2,
h = Screen:getHeight()/2
}
}
},
}
end
self.ui.menu:registerToMainMenu(self)
end
function ReaderBookmark:initGesListener()
self.ges_events = {
AddBookmark = {
GestureRange:new{
ges = "double_tap",
range = Geom:new{
x = Screen:getWidth()/2, y = 0,
w = Screen:getWidth()/2,
h = Screen:getHeight()/2
}
}
},
}
end
function ReaderBookmark:onReadSettings(config)
self.bookmarks = config:readSetting("bookmarks") or {}
end
@ -38,7 +40,10 @@ function ReaderBookmark:onCloseDocument()
end
function ReaderBookmark:onSetDimensions(dimen)
self.dimen = dimen
-- update listening according to new screen dimen
if Device:isTouchDevice() then
self:initGesListener()
end
end
function ReaderBookmark:onAddBookmark()

@ -55,20 +55,25 @@ ReaderConfig = InputContainer:new{
}
function ReaderConfig:init()
if Device:isTouchDevice() then
self.ges_events = {
TapShowConfigMenu = {
GestureRange:new{
ges = "tap",
range = self.dimen:copy(),
}
}
}
else
if Device:hasKeyboard() then
self.key_events = {
ShowConfigMenu = { { "AA" }, doc = "show config dialog" },
}
end
if Device:isTouchDevice() then
self:initGesListener()
end
end
function ReaderConfig:initGesListener()
self.ges_events = {
TapShowConfigMenu = {
GestureRange:new{
ges = "tap",
range = self.dimen,
}
}
}
end
function ReaderConfig:onShowConfigMenu()
@ -90,8 +95,15 @@ function ReaderConfig:onTapShowConfigMenu()
end
function ReaderConfig:onSetDimensions(dimen)
-- update gesture listenning range according to new screen orientation
self:init()
self.dimen.x = 0
self.dimen.y = 7 * Screen:getHeight() / 8
self.dimen.w = Screen:getWidth()
self.dimen.h = Screen:getHeight() / 8
-- since we cannot redraw config_dialog with new size, we close
-- the old one on screen size change
if self.config_dialog then
self.config_dialog:closeDialog()
end
end
function ReaderConfig:onCloseConfig()

@ -1,4 +1,5 @@
ReaderMenu = InputContainer:new{
_name = "ReaderMenu",
item_table = {},
registered_widgets = {},
}
@ -7,44 +8,44 @@ function ReaderMenu:init()
self.item_table = {}
self.registered_widgets = {}
if Device:isTouchDevice() then
self.ges_events = {
TapShowMenu = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = 0, y = 0,
w = Screen:getWidth(),
h = Screen:getHeight()/4,
}
}
},
}
else
if Device:hasKeyboard() then
self.key_events = {
ShowMenu = { { "Menu" }, doc = "show menu" },
}
end
end
function ReaderMenu:initGesListener()
self.ges_events = {
TapShowMenu = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = 0, y = 0,
w = Screen:getWidth(),
h = Screen:getHeight()/4,
}
}
},
}
end
function ReaderMenu:setUpdateItemTable()
table.insert(self.item_table, {
text = "Screen rotate",
sub_item_table = {
{
text = "rotate 90 degree clockwise",
text = "landscape",
callback = function()
--Screen:screenRotate("clockwise")
self.ui:handleEvent(
Event:new("SetDimensions", Screen:getSize()))
Event:new("SetScreenMode", "landscape"))
end
},
{
text = "rotate 90 degree anticlockwise",
text = "portrait",
callback = function()
--Screen:screenRotate("anticlockwise")
self.ui:handleEvent(
Event:new("SetDimensions", Screen:getSize()))
Event:new("SetScreenMode", "portrait"))
end
},
}
@ -96,8 +97,10 @@ function ReaderMenu:onTapShowMenu()
end
function ReaderMenu:onSetDimensions(dimen)
-- @TODO update gesture listenning range according to new screen
-- orientation 15.12 2012 (houqp)
-- update listening according to new screen dimen
if Device:isTouchDevice() then
self:initGesListener()
end
end
function ReaderMenu:onCloseDocument()

@ -8,32 +8,7 @@ ReaderPaging = InputContainer:new{
}
function ReaderPaging:init()
if Device:isTouchDevice() then
self.ges_events = {
TapForward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = Screen:getWidth()/4,
y = Screen:getHeight()/4,
w = 3*Screen:getWidth()/4,
h = 5*Screen:getHeight()/8,
}
}
},
TapBackward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = 0,
y = Screen:getHeight()/4,
w = Screen:getWidth()/4,
h = 5*Screen:getHeight()/8,
}
}
}
}
else
if Device:hasKeyboard() then
self.key_events = {
GotoNextPage = {
{Input.group.PgFwd}, doc = "go to next page",
@ -67,6 +42,34 @@ function ReaderPaging:init()
self.number_of_pages = self.ui.document.info.number_of_pages
end
-- This method will be called in onSetDimensions handler
function ReaderPaging:initGesListener()
self.ges_events = {
TapForward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = Screen:getWidth()/4,
y = Screen:getHeight()/4,
w = 3*Screen:getWidth()/4,
h = 5*Screen:getHeight()/8,
}
}
},
TapBackward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = 0,
y = Screen:getHeight()/4,
w = Screen:getWidth()/4,
h = 5*Screen:getHeight()/8,
}
}
}
}
end
function ReaderPaging:onReadSettings(config)
self:gotoPage(config:readSetting("last_page") or 1)
local soe = config:readSetting("show_overlap_enable")
@ -90,24 +93,6 @@ function ReaderPaging:onTapBackward()
return true
end
-- wrapper for bounds checking
function ReaderPaging:gotoPage(number)
if number == self.current_page then
return true
end
if number > self.number_of_pages
or number < 1 then
DEBUG("wrong page number: "..number.."!")
return false
end
DEBUG("going to page number", number)
-- this is an event to allow other controllers to be aware of this change
self.ui:handleEvent(Event:new("PageUpdate", number))
return true
end
function ReaderPaging:onZoomModeUpdate(new_mode)
-- we need to remember zoom mode to handle page turn event
self.zoom_mode = new_mode
@ -232,3 +217,30 @@ function ReaderPaging:onRedrawCurrentPage()
self.ui:handleEvent(Event:new("PageUpdate", self.current_page))
return true
end
function ReaderPaging:onSetDimensions()
-- update listening according to new screen dimen
if Device:isTouchDevice() then
self:initGesListener()
end
end
-- wrapper for bounds checking
function ReaderPaging:gotoPage(number)
if number == self.current_page then
return true
end
if number > self.number_of_pages
or number < 1 then
DEBUG("wrong page number: "..number.."!")
return false
end
DEBUG("going to page number", number)
-- this is an event to allow other controllers to be aware of this change
self.ui:handleEvent(Event:new("PageUpdate", number))
return true
end

@ -14,32 +14,7 @@ ReaderRolling = InputContainer:new{
}
function ReaderRolling:init()
if Device:isTouchDevice() then
self.ges_events = {
TapForward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = Screen:getWidth()/4,
y = Screen:getHeight()/4,
w = 3*Screen:getWidth()/4,
h = 5*Screen:getHeight()/8,
}
}
},
TapBackward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = 0,
y = Screen:getHeight()/4,
w = Screen:getWidth()/4,
h = 5*Screen:getHeight()/8,
}
}
}
}
else
if Device:hasKeyboard() then
self.key_events = {
GotoNextView = {
{ Input.group.PgFwd },
@ -89,6 +64,34 @@ function ReaderRolling:init()
self.old_page = self.ui.document.info.number_of_pages
end
-- This method will be called in onSetDimensions handler
function ReaderRolling:initGesListener()
self.ges_events = {
TapForward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = Screen:getWidth()/4,
y = Screen:getHeight()/4,
w = 3*Screen:getWidth()/4,
h = 5*Screen:getHeight()/8,
}
}
},
TapBackward = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = 0,
y = Screen:getHeight()/4,
w = Screen:getWidth()/4,
h = 5*Screen:getHeight()/8,
}
}
}
}
end
function ReaderRolling:onReadSettings(config)
local soe = config:readSetting("show_overlap_enable")
if not soe then
@ -223,6 +226,13 @@ function ReaderRolling:onRedrawCurrentView()
return true
end
function ReaderRolling:onSetDimensions()
-- update listening according to new screen dimen
if Device:isTouchDevice() then
self:initGesListener()
end
end
--[[
PosUpdate event is used to signal other widgets that pos has been changed.
--]]

@ -1,4 +1,5 @@
ReaderView = WidgetContainer:new{
_name = "ReaderView",
document = nil,
state = {
@ -129,6 +130,14 @@ function ReaderView:PanningUpdate(dx, dy)
return true
end
function ReaderView:onSetScreenMode(new_mode)
if new_mode == "landscape" or new_mode == "portrait" then
Screen:setScreenMode(new_mode)
self.ui:handleEvent(Event:new("SetDimensions", Screen:getSize()))
end
return true
end
function ReaderView:onSetDimensions(dimensions)
self.dimen = dimensions
-- recalculate view

@ -1,46 +1,4 @@
ReaderZooming = InputContainer:new{
key_events = {
ZoomIn = {
{ "Shift", Input.group.PgFwd },
doc = "zoom in",
event = "Zoom", args = "in"
},
ZoomOut = {
{ "Shift", Input.group.PgBack },
doc = "zoom out",
event = "Zoom", args = "out"
},
ZoomToFitPage = {
{ "A" },
doc = "zoom to fit page",
event = "SetZoomMode", args = "page"
},
ZoomToFitContent = {
{ "Shift", "A" },
doc = "zoom to fit content",
event = "SetZoomMode", args = "content"
},
ZoomToFitPageWidth = {
{ "S" },
doc = "zoom to fit page width",
event = "SetZoomMode", args = "pagewidth"
},
ZoomToFitContentWidth = {
{ "Shift", "S" },
doc = "zoom to fit content width",
event = "SetZoomMode", args = "contentwidth"
},
ZoomToFitPageHeight = {
{ "D" },
doc = "zoom to fit page height",
event = "SetZoomMode", args = "pageheight"
},
ZoomToFitContentHeight = {
{ "Shift", "D" },
doc = "zoom to fit content height",
event = "SetZoomMode", args = "contentheight"
},
},
zoom = 1.0,
-- default to nil so we can trigger ZoomModeUpdate events on start up
zoom_mode = nil,
@ -50,6 +8,50 @@ ReaderZooming = InputContainer:new{
}
function ReaderZooming:init()
if Device:hasKeyboard() then
self.key_events = {
ZoomIn = {
{ "Shift", Input.group.PgFwd },
doc = "zoom in",
event = "Zoom", args = "in"
},
ZoomOut = {
{ "Shift", Input.group.PgBack },
doc = "zoom out",
event = "Zoom", args = "out"
},
ZoomToFitPage = {
{ "A" },
doc = "zoom to fit page",
event = "SetZoomMode", args = "page"
},
ZoomToFitContent = {
{ "Shift", "A" },
doc = "zoom to fit content",
event = "SetZoomMode", args = "content"
},
ZoomToFitPageWidth = {
{ "S" },
doc = "zoom to fit page width",
event = "SetZoomMode", args = "pagewidth"
},
ZoomToFitContentWidth = {
{ "Shift", "S" },
doc = "zoom to fit content width",
event = "SetZoomMode", args = "contentwidth"
},
ZoomToFitPageHeight = {
{ "D" },
doc = "zoom to fit page height",
event = "SetZoomMode", args = "pageheight"
},
ZoomToFitContentHeight = {
{ "Shift", "D" },
doc = "zoom to fit content height",
event = "SetZoomMode", args = "contentheight"
},
}
end
self.ui.menu:registerToMainMenu(self)
end

@ -40,61 +40,126 @@ Codes for rotation modes:
Screen = {
cur_rotation_mode = 0,
-- these two variabls are used to help switching from framework to reader
width = 0,
height = 0,
pitch = 0,
native_rotation_mode = nil,
kpv_rotation_mode = nil,
cur_rotation_mode = 0,
bb = nil,
saved_bb = nil,
fb = einkfb.open("/dev/fb0")
fb = einkfb.open("/dev/fb0"),
}
-- @orien: 1 for clockwise rotate, -1 for anti-clockwise
-- Remember to reread screen resolution after this function call
function Screen:screenRotate(orien)
if orien == "clockwise" then
orien = -1
elseif orien == "anticlockwise" then
orien = 1
function Screen:init()
_, self.height = self.fb:getSize()
-- for unknown strange reason, pitch*2 is less than screen width in KPW
-- so we need to calculate width by pitch here
self.pitch = self.fb:getPitch()
self.width = self.pitch * 2
self.bb = Blitbuffer.new(self.width, self.height, self.pitch)
if self.width > self.height then
-- For another unknown strange reason, self.fb:getOrientation always
-- return 0 in KPW, even though we are in landscape mode.
-- Seems like the native framework change framebuffer on the fly when
-- starting booklet. Starting KPV from ssh and KPVBooklet will get
-- different framebuffer height and width.
--
--self.native_rotation_mode = self.fb:getOrientation()
self.native_rotation_mode = 1
else
return
self.native_rotation_mode = 0
end
self.cur_rotation_mode = self.native_rotation_mode
end
self.cur_rotation_mode = (self.cur_rotation_mode + orien) % 4
-- you have to reopen framebuffer after rotate
self.fb:setOrientation(self.cur_rotation_mode)
self.fb:close()
self.fb = einkfb.open("/dev/fb0")
Input.rotation = self.cur_rotation_mode
function Screen:refresh(refesh_type)
if self.native_rotation_mode == self.cur_rotation_mode then
self.fb.bb:blitFrom(self.bb, 0, 0, 0, 0, self.width, self.height)
elseif self.native_rotation_mode == 0 and self.cur_rotation_mode == 1 then
self.fb.bb:blitFromRotate(self.bb, 270)
elseif self.native_rotation_mode == 1 and self.cur_rotation_mode == 0 then
self.fb.bb:blitFromRotate(self.bb, 90)
end
self.fb:refresh(refesh_type)
end
function Screen:getSize()
local w, h = self.fb:getSize()
return Geom:new{w = w, h = h}
return Geom:new{w = self.width, h = self.height}
end
function Screen:getWidth()
local w, _ = self.fb:getSize()
return w
return self.width
end
function Screen:getHeight()
local _, h = self.fb:getSize()
return h
return self.height
end
function Screen:getPitch()
return self.fb:getPitch()
return self.ptich
end
function Screen:updateRotationMode()
function Screen:getNativeRotationMode()
-- in EMU mode, you will always get 0 from getOrientation()
self.cur_rotation_mode = self.fb:getOrientation()
return self.fb:getOrientation()
end
function Screen:getRotationMode()
return self.cur_rotation_mode
end
function Screen:getScreenMode()
if self.width > self.height then
return "landscape"
else
return "portrait"
end
end
function Screen:setRotationMode(mode)
self.fb:setOrientation(Screen.native_rotation_mode)
if mode > 3 or mode < 0 then
return
end
-- mode 0 and mode 2 has the same width and height, so do mode 1 and 3
if (self.cur_rotation_mode % 2) ~= (mode % 2) then
self.width, self.height = self.height, self.width
end
self.cur_rotation_mode = mode
self.bb:free()
self.pitch = self.width/2
self.bb = Blitbuffer.new(self.width, self.height, self.pitch)
-- update mode for input module
Input.rotation = 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 ~= 1 then
self:setRotationMode(1)
end
end
end
--[[
@brief change gesture's x and y coordinates according to screen view mode
@param ges gesture that you want to adjust
@return adjusted gesture.
--]]
function Screen:adjustGesCoordinate(ges)
if self.cur_rotation_mode == 1 then
--@TODO fix wipe direction 03.02 2013 (houqp)
ges.pos.x, ges.pos.y = (self.width - ges.pos.y), (ges.pos.x)
end
return ges
end
function Screen:saveCurrentBB()
@ -107,7 +172,7 @@ function Screen:saveCurrentBB()
self.saved_bb:free()
self.saved_bb = Blitbuffer.new(width, height, self:getPitch())
end
self.saved_bb:blitFullFrom(self.fb.bb)
self.saved_bb:blitFullFrom(self.bb)
end
function Screen:restoreFromSavedBB()
@ -116,13 +181,13 @@ end
function Screen:getCurrentScreenBB()
local bb = Blitbuffer.new(self:getWidth(), self:getHeight())
bb:blitFullFrom(self.fb.bb)
bb:blitFullFrom(self.bb)
return bb
end
function Screen:restoreFromBB(bb)
if bb then
self.fb.bb:blitFullFrom(bb)
self.bb:blitFullFrom(bb)
else
DEBUG("Got nil bb in restoreFromSavedBB!")
end

@ -5,9 +5,9 @@ require "ui/widget"
require "ui/screen"
require "settings" -- for DEBUG(), TODO: put DEBUG() somewhere else
-- initialize output module, this must be initialized before Input
Screen:init()
-- initialize the input handling
Input:init()
@ -164,7 +164,7 @@ function UIManager:run()
local dirty = false
for _, widget in ipairs(self._window_stack) do
if self.repaint_all or self._dirty[widget.widget] then
widget.widget:paintTo(Screen.fb.bb, widget.x, widget.y)
widget.widget:paintTo(Screen.bb, widget.x, widget.y)
if self._dirty[widget.widget] == "full" then
self.refresh_type = 0
end
@ -178,7 +178,7 @@ function UIManager:run()
if dirty then
-- refresh FB
Screen.fb:refresh(self.refresh_type) -- TODO: refresh explicitly only repainted area
Screen:refresh(self.refresh_type) -- TODO: refresh explicitly only repainted area
-- reset refresh_type
self.refresh_type = 1
end
@ -204,7 +204,7 @@ function UIManager:run()
-- delegate input_event to handler
if input_event then
DEBUG(input_event)
DEBUG("in ui.lua:", input_event)
if input_event == "IntoSS" then
Device:intoScreenSaver()
elseif input_event == "OutOfSS" then

@ -159,8 +159,6 @@ if fontmap ~= nil then
end
local last_file = G_reader_settings:readSetting("lastfile")
Screen:updateRotationMode()
Screen.native_rotation_mode = Screen.cur_rotation_mode
--@TODO we can read version here, refer to commit in master tree: (houqp)
--87712cf0e43fed624f8a9f610be42b1fe174b9fe
@ -180,14 +178,9 @@ else
return showusage()
end
-- @TODO dirty workaround, find a way to force native system poll
-- screen orientation and upside down mode 09.03 2012
Screen:setRotationMode(Screen.native_rotation_mode)
input.closeAll()
if util.isEmulated()==0 then
os.execute("killall -cont cvm")
if Device:isKindle3() or (Device:getModel() == "KindleDXG") then
-- send double menu key press events to trigger screen refresh
os.execute("echo 'send 139' > /proc/keypad;echo 'send 139' > /proc/keypad")

Loading…
Cancel
Save