diff --git a/frontend/ui/device.lua b/frontend/ui/device.lua index 7a67b73bc..2001d6c2a 100644 --- a/frontend/ui/device.lua +++ b/frontend/ui/device.lua @@ -83,11 +83,14 @@ 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... "} + msg = InfoMessage:new{ + text = "Going into screensaver... ", + timeout = 2, + } --UIManager:show(msg) - Screen.kpv_rotation_mode = Screen.cur_rotation_mode - Screen.fb:setOrientation(Screen.native_rotation_mode) + --Screen.kpv_rotation_mode = Screen.cur_rotation_mode + --Screen.fb:setOrientation(Screen.native_rotation_mode) --util.sleep(1) --os.execute("killall -cont cvm") self.screen_saver_mode = true @@ -99,11 +102,11 @@ 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 - util.usleep(1500000) + --util.usleep(1500000) --os.execute("killall -stop cvm") - Screen.fb:setOrientation(Screen.kpv_rotation_mode) + --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 +115,14 @@ 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") + --Screen.kpv_rotation_mode = Screen.cur_rotation_mode + --Screen.fb:setOrientation(Screen.native_rotation_mode) + msg = InfoMessage:new{ + text = "Going into USB mode... ", + timeout = 2, + } + --util.sleep(1) + --os.execute("killall -cont cvm") end self.charging_mode = true end @@ -126,11 +130,11 @@ 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.fb:setOrientation(Screen.kpv_rotation_mode) Screen:restoreFromSavedBB() - Screen.fb:refresh(0) + Screen:refresh(0) end --@TODO signal filemanager for file changes 13.06 2012 (houqp) diff --git a/frontend/ui/gesturedetector.lua b/frontend/ui/gesturedetector.lua index 11b963dca..bc703168c 100644 --- a/frontend/ui/gesturedetector.lua +++ b/frontend/ui/gesturedetector.lua @@ -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, } } diff --git a/frontend/ui/infomessage.lua b/frontend/ui/infomessage.lua index 85efe3ee6..7c9eb408c 100644 --- a/frontend/ui/infomessage.lua +++ b/frontend/ui/infomessage.lua @@ -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(), diff --git a/frontend/ui/inputevent.lua b/frontend/ui/inputevent.lua index 87a941e7d..d60caebd9 100644 --- a/frontend/ui/inputevent.lua +++ b/frontend/ui/inputevent.lua @@ -122,7 +122,13 @@ end an interface to get input events ]] Input = { - event_map = { + event_map = {}, + 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 +163,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 +193,19 @@ Input = { [116] = "Down", -- arrow down [117] = "RPgFwd", -- normal PageDown [119] = "Del", -- Delete - }, - rotation = 0, - rotation_map = { + } + self.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.fb:getOrientation() + + 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) diff --git a/frontend/ui/notification.lua b/frontend/ui/notification.lua index 7a63842d8..7ae326bbc 100644 --- a/frontend/ui/notification.lua +++ b/frontend/ui/notification.lua @@ -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{ diff --git a/frontend/ui/reader/readerbookmark.lua b/frontend/ui/reader/readerbookmark.lua index 015dcd0d0..5c17e4e7e 100644 --- a/frontend/ui/reader/readerbookmark.lua +++ b/frontend/ui/reader/readerbookmark.lua @@ -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() diff --git a/frontend/ui/reader/readerconfig.lua b/frontend/ui/reader/readerconfig.lua index 2eed6226f..842028b1c 100644 --- a/frontend/ui/reader/readerconfig.lua +++ b/frontend/ui/reader/readerconfig.lua @@ -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:copy(), + } + } + } end function ReaderConfig:onShowConfigMenu() @@ -107,8 +112,15 @@ function ReaderConfig:onTapShowConfigMenu() end function ReaderConfig:onSetDimensions(dimen) - -- update gesture listenning range according to new screen orientation - self:init() + -- update listening according to new screen dimen + --@TODO do we really need to new a Geom everytime? 02.02 2013 (houqp) + self.dimen = Geom:new{ + x = 0, + y = 7*Screen:getHeight()/8, + w = Screen:getWidth(), + h = Screen:getHeight()/8, + } + self:initGesListener() end function ReaderConfig:onReadSettings(config) diff --git a/frontend/ui/reader/readermenu.lua b/frontend/ui/reader/readermenu.lua index 810fb4b0d..4b014968d 100644 --- a/frontend/ui/reader/readermenu.lua +++ b/frontend/ui/reader/readermenu.lua @@ -1,4 +1,5 @@ ReaderMenu = InputContainer:new{ + _name = "ReaderMenu", item_table = {}, registered_widgets = {}, } @@ -7,42 +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") + Screen:setViewMode("landscape") self.ui:handleEvent( Event:new("SetDimensions", Screen:getSize())) end }, { - text = "rotate 90 degree anticlockwise", + text = "portrait", callback = function() - --Screen:screenRotate("anticlockwise") + Screen:setViewMode("portrait") self.ui:handleEvent( Event:new("SetDimensions", Screen:getSize())) end @@ -101,8 +104,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() diff --git a/frontend/ui/reader/readerpaging.lua b/frontend/ui/reader/readerpaging.lua index e84dca234..ff73d7603 100644 --- a/frontend/ui/reader/readerpaging.lua +++ b/frontend/ui/reader/readerpaging.lua @@ -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 + + diff --git a/frontend/ui/reader/readerrolling.lua b/frontend/ui/reader/readerrolling.lua index 5e2fccb75..8c9875d13 100644 --- a/frontend/ui/reader/readerrolling.lua +++ b/frontend/ui/reader/readerrolling.lua @@ -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. --]] diff --git a/frontend/ui/reader/readerview.lua b/frontend/ui/reader/readerview.lua index 82d0e6d30..b5e85f038 100644 --- a/frontend/ui/reader/readerview.lua +++ b/frontend/ui/reader/readerview.lua @@ -1,4 +1,5 @@ ReaderView = WidgetContainer:new{ + _name = "ReaderView", document = nil, state = { diff --git a/frontend/ui/reader/readerzooming.lua b/frontend/ui/reader/readerzooming.lua index ecfb7cf6d..a95bf4ae2 100644 --- a/frontend/ui/reader/readerzooming.lua +++ b/frontend/ui/reader/readerzooming.lua @@ -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 diff --git a/frontend/ui/screen.lua b/frontend/ui/screen.lua index 34349a943..601fc2efe 100644 --- a/frontend/ui/screen.lua +++ b/frontend/ui/screen.lua @@ -40,18 +40,42 @@ 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"), } +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.width = self.fb:getPitch()*2 + self.pitch = self:getPitch() + self.bb = Blitbuffer.new(self.width, self.height, self.pitch) + self.native_rotation_mode = self.fb:getOrientation() + self.cur_rotation_mode = self.native_rotation_mode +end + +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) + end + self.fb:refresh(refesh_type) +end + -- @orien: 1 for clockwise rotate, -1 for anti-clockwise -- Remember to reread screen resolution after this function call +-- WARNING: this method is deprecated!!! use setRotationMode() or +-- setViewMode() instead. function Screen:screenRotate(orien) if orien == "clockwise" then orien = -1 @@ -70,22 +94,19 @@ function Screen:screenRotate(orien) 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() @@ -94,7 +115,46 @@ function Screen:updateRotationMode() end function Screen:setRotationMode(mode) - self.fb:setOrientation(Screen.native_rotation_mode) + -- 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) +end + +function Screen:setViewMode(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) + -- we do nothing is screen is not rotated + if self.native_rotation_mode == self.cur_rotation_mode then + return ges + end + + if self.native_rotation_mode == 0 and self.cur_rotation_mode == 1 then + ges.pos.x, ges.pos.y = (self.width - ges.pos.y), (ges.pos.x) + end + + return ges end function Screen:saveCurrentBB() diff --git a/frontend/ui/ui.lua b/frontend/ui/ui.lua index cc258fc96..aac25681e 100644 --- a/frontend/ui/ui.lua +++ b/frontend/ui/ui.lua @@ -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