diff --git a/frontend/apps/reader/modules/readergesture.lua b/frontend/apps/reader/modules/readergesture.lua index ce4514d4d..8730de936 100644 --- a/frontend/apps/reader/modules/readergesture.lua +++ b/frontend/apps/reader/modules/readergesture.lua @@ -8,7 +8,7 @@ local _ = require("gettext") local default_gesture = { tap_right_bottom_corner = "nothing", - tap_left_bottom_corner = Device.hasFrontlight() and "toggle_frontlight" or "nothing", + tap_left_bottom_corner = Device:hasFrontlight() and "toggle_frontlight" or "nothing", short_diagonal_swipe = "full_refresh", } @@ -70,7 +70,8 @@ function ReaderGesture:buildMenu(ges, default) {_("Reading progress"), "reading_progress", ReaderGesture.getReaderProgress ~= nil}, {_("Full screen refresh"), "full_refresh", true}, {_("Night mode"), "night_mode", true}, - {_("Toggle frontlight"), "toggle_frontlight", Device.hasFrontlight()}, + {_("Toggle frontlight"), "toggle_frontlight", Device:hasFrontlight()}, + {_("Toggle accelerometer"), "toggle_gsensor", Device:canToggleGSensor()}, } local return_menu = {} -- add default action to the top of the submenu @@ -190,15 +191,19 @@ function ReaderGesture:gestureAction(action) UIManager:setDirty("all", "full") elseif action == "bookmarks" then self.ui:handleEvent(Event:new("ShowBookmark")) - elseif action =="page_update_up10" then + elseif action == "page_update_up10" then self:pageUpdate(10) - elseif action =="page_update_down10" then + elseif action == "page_update_down10" then self:pageUpdate(-10) - elseif action =="folder_up" then + elseif action == "folder_up" then self.ui.file_chooser:changeToPath(string.format("%s/..", self.ui.file_chooser.path)) - elseif action =="toggle_frontlight" then + elseif action == "toggle_frontlight" then Device:getPowerDevice():toggleFrontlight() self:onShowFLOnOff() + elseif action == "toggle_gsensor" then + G_reader_settings:flipNilOrFalse("input_ignore_gsensor") + Device:toggleGSensor() + self:onGSensorToggle() end return true end @@ -233,4 +238,19 @@ function ReaderGesture:onShowFLOnOff() return true end +function ReaderGesture:onGSensorToggle() + local Notification = require("ui/widget/notification") + local new_text + if G_reader_settings:isTrue("input_ignore_gsensor") then + new_text = _("Accelerometer rotation events will now be ignored.") + else + new_text = _("Accelerometer rotation events will now be honored.") + end + UIManager:show(Notification:new{ + text = new_text, + timeout = 1.0, + }) + return true +end + return ReaderGesture diff --git a/frontend/device/generic/device.lua b/frontend/device/generic/device.lua index 15f7bdcae..c592b39e8 100644 --- a/frontend/device/generic/device.lua +++ b/frontend/device/generic/device.lua @@ -27,6 +27,7 @@ local Device = { hasClipboard = no, hasColorScreen = no, hasBGRFrameBuffer = no, + canToggleGSensor = no, -- use these only as a last resort. We should abstract the functionality -- and have device dependent implementations in the corresponting @@ -65,6 +66,27 @@ function Device:new(o) return o end +-- Inverts PageTurn button mappings +-- NOTE: For ref. on Kobo, stored by Nickel in the [Reading] section as invertPageTurnButtons=true +function Device:invertButtons() + if self:hasKeys() and self.input and self.input.event_map then + for key, value in pairs(self.input.event_map) do + if value == "LPgFwd" then + self.input.event_map[key] = "LPgBack" + elseif value == "LPgBack" then + self.input.event_map[key] = "LPgFwd" + elseif value == "RPgFwd" then + self.input.event_map[key] = "RPgBack" + elseif value == "RPgBack" then + self.input.event_map[key] = "RPgFwd" + end + end + + -- NOTE: We currently leave self.input.rotation_map alone, + -- which will definitely yield fairly stupid mappings in Landscape... + end +end + function Device:init() assert(self ~= nil) if not self.screen then @@ -99,6 +121,13 @@ function Device:init() self.input.adjustTouchTranslate, {x = 0 - self.viewport.x, y = 0 - self.viewport.y}) end + + -- Handle button mappings shenanigans + if self:hasKeys() then + if G_reader_settings:isTrue("input_invert_page_turn_keys") then + self:invertButtons() + end + end end function Device:getPowerDevice() @@ -207,6 +236,9 @@ function Device:setDateTime(year, month, day, hour, min, sec) end -- Device specific method if any setting needs being saved function Device:saveSettings() end +-- Device specific method for toggling the GSensor +function Device:toggleGSensor() end + --[[ prepare for application shutdown --]] diff --git a/frontend/device/input.lua b/frontend/device/input.lua index 19c175f2d..b87a7a6a0 100755 --- a/frontend/device/input.lua +++ b/frontend/device/input.lua @@ -659,6 +659,16 @@ function Input:handleMiscEvNTX(ev) end end +-- Allow toggling it at runtime +function Input:toggleMiscEvNTX() + if self.isNTXAccelHooked then + self.handleMiscEv = function() end + else + self.handleMiscEv = self.handleMiscEvNTX + end + + self.isNTXAccelHooked = not self.isNTXAccelHooked +end -- helpers for touch event data management: @@ -705,6 +715,10 @@ function Input:isEvKeyPress(ev) return ev.value == EVENT_VALUE_KEY_PRESS end +function Input:isEvKeyRepeat(ev) + return ev.value == EVENT_VALUE_KEY_REPEAT +end + function Input:isEvKeyRelease(ev) return ev.value == EVENT_VALUE_KEY_RELEASE end diff --git a/frontend/device/kobo/device.lua b/frontend/device/kobo/device.lua index 4dbea2617..d4fdb8b02 100644 --- a/frontend/device/kobo/device.lua +++ b/frontend/device/kobo/device.lua @@ -207,6 +207,7 @@ local KoboFrost = Kobo:new{ model = "Kobo_frost", hasFrontlight = yes, hasKeys = yes, + canToggleGSensor = yes, touch_snow_protocol = true, misc_ntx_gsensor_protocol = true, display_dpi = 300, @@ -222,6 +223,24 @@ local KoboFrost = Kobo:new{ }, } +-- This function will update itself after the first touch event +local probeEvEpochTime +probeEvEpochTime = function(self, ev) + local now = TimeVal:now() + -- This check should work as long as main UI loop is not blocked for more + -- than 10 minute before handling the first touch event. + if ev.time.sec <= now.sec - 600 then + -- time is seconds since boot, force it to epoch + probeEvEpochTime = function(_, _ev) + _ev.time = TimeVal:now() + end + ev.time = now + else + -- time is already epoch time, no need to do anything + probeEvEpochTime = function(_, _) end + end +end + function Kobo:init() self.screen = require("ffi/framebuffer_mxcfb"):new{device = self, debug = logger.dbg} -- NOTE: Something about the extra work needed to handle RGB565 conversions is making the JIT optimizer crazy when doing @@ -252,7 +271,7 @@ function Kobo:init() SleepCover = function(ev) if self.input:isEvKeyPress(ev) then return "SleepCoverClosed" - else + elseif self.input:isEvKeyRelease(ev) then return "SleepCoverOpened" end end, @@ -278,6 +297,15 @@ function Kobo:init() else -- if touch probe is required, we postpone EventAdjustHook -- initialization to when self:touchScreenProbe is called + -- Except we may need bits of EventAdjustHook for stuff to be functional, so, + -- re-order things in the most horrible way possible! + if self.touch_probe_ev_epoch_time then + self.input:registerEventAdjustHook(function(_, ev) + probeEvEpochTime(_, ev) + end) + -- And don't do it again during the real initEventAdjustHooks ;) + self.touch_probe_ev_epoch_time = nil + end self.touchScreenProbe = function() -- if user has not set KOBO_TOUCH_MIRRORED yet if KOBO_TOUCH_MIRRORED == nil then @@ -349,30 +377,12 @@ function Kobo:initNetworkManager(NetworkMgr) -- NOTE: Cheap-ass way of checking if WiFi seems to be enabled... -- Since the crux of the issues lies in race-y module unloading, this is perfectly fine for our usage. function NetworkMgr:isWifiOn() - return 0 == os.execute("lsmod | grep -q sdio_wifi_pwr") + return 0 == os.execute("lsmod | grep -q " .. os.getenv("WIFI_MODULE") or "sdio_wifi_pwr") end end function Kobo:supportsScreensaver() return true end -local probeEvEpochTime --- this function will update itself after the first touch event -probeEvEpochTime = function(self, ev) - local now = TimeVal:now() - -- This check should work as long as main UI loop is not blocked for more - -- than 10 minute before handling the first touch event. - if ev.time.sec <= now.sec - 600 then - -- time is seconds since boot, force it to epoch - probeEvEpochTime = function(_, _ev) - _ev.time = TimeVal:now() - end - ev.time = now - else - -- time is already epoch time, no need to do anything - probeEvEpochTime = function(_, _) end - end -end - local ABS_MT_TRACKING_ID = 57 local EV_ABS = 3 local adjustTouchAlyssum = function(self, ev) @@ -421,7 +431,12 @@ function Kobo:initEventAdjustHooks() -- Accelerometer on the Forma if self.misc_ntx_gsensor_protocol then - self.input.handleMiscEv = self.input.handleMiscEvNTX + if G_reader_settings:isTrue("input_ignore_gsensor") then + self.input.isNTXAccelHooked = false + else + self.input.handleMiscEv = self.input.handleMiscEvNTX + self.input.isNTXAccelHooked = true + end end end @@ -672,6 +687,15 @@ function Kobo:reboot() os.execute("reboot") end +function Kobo:toggleGSensor() + if self:canToggleGSensor() and self.input then + -- Currently only supported on the Forma + if self.misc_ntx_gsensor_protocol then + self.input:toggleMiscEvNTX() + end + end +end + -------------- device probe ------------ local codename = Kobo:getCodeName() diff --git a/frontend/ui/elements/common_settings_menu_table.lua b/frontend/ui/elements/common_settings_menu_table.lua index 289353af3..a923ae483 100644 --- a/frontend/ui/elements/common_settings_menu_table.lua +++ b/frontend/ui/elements/common_settings_menu_table.lua @@ -112,10 +112,11 @@ common_settings.screen = { require("ui/elements/screen_eink_opt_menu_table"), require("ui/elements/menu_activate"), require("ui/elements/screen_disable_double_tap_table"), - require("ui/elements/flash_ui"), - require("ui/elements/flash_keyboard"), }, } +if Device:canToggleGSensor() then + table.insert(common_settings.screen.sub_item_table, require("ui/elements/screen_toggle_gsensor")) +end if Screen.isColorScreen() then table.insert(common_settings.screen.sub_item_table, 3, require("ui/elements/screen_color_menu_table")) common_settings.screen.sub_item_table[3].separator = true @@ -199,6 +200,16 @@ if Device:hasKeys() then G_reader_settings:flipNilOrTrue("enable_back_history") end, }, + { + text = _("Invert page turn buttons"), + checked_func = function() + return G_reader_settings:isTrue("input_invert_page_turn_keys") + end, + callback = function() + G_reader_settings:flipNilOrFalse("input_invert_page_turn_keys") + Device:invertButtons() + end, + }, } } end diff --git a/frontend/ui/elements/screen_eink_opt_menu_table.lua b/frontend/ui/elements/screen_eink_opt_menu_table.lua index fc40e2db3..9cd8611b7 100644 --- a/frontend/ui/elements/screen_eink_opt_menu_table.lua +++ b/frontend/ui/elements/screen_eink_opt_menu_table.lua @@ -13,6 +13,8 @@ local eink_settings_table = { G_reader_settings:saveSetting("low_pan_rate", Screen.low_pan_rate) end, }, + require("ui/elements/flash_ui"), + require("ui/elements/flash_keyboard"), { text = _("Avoid mandatory black flashes in UI"), checked_func = function() return G_reader_settings:isTrue("avoid_flashing_ui") end, @@ -20,7 +22,6 @@ local eink_settings_table = { G_reader_settings:flipNilOrFalse("avoid_flashing_ui") end, }, - }, } diff --git a/frontend/ui/elements/screen_toggle_gsensor.lua b/frontend/ui/elements/screen_toggle_gsensor.lua new file mode 100644 index 000000000..b9f236086 --- /dev/null +++ b/frontend/ui/elements/screen_toggle_gsensor.lua @@ -0,0 +1,13 @@ +local Device = require("device") +local _ = require("gettext") + +return { + text = _("Ignore accelerometer rotation events"), + checked_func = function() + return G_reader_settings:isTrue("input_ignore_gsensor") + end, + callback = function() + G_reader_settings:flipNilOrFalse("input_ignore_gsensor") + Device:toggleGSensor() + end, +} diff --git a/spec/unit/device_spec.lua b/spec/unit/device_spec.lua index 1270eaf1c..c1f169cca 100644 --- a/spec/unit/device_spec.lua +++ b/spec/unit/device_spec.lua @@ -80,9 +80,10 @@ describe("device module", function() kobo_dev:init() local Screen = kobo_dev.screen - kobo_dev.touch_probe_ev_epoch_time = false assert.is.same("Kobo_trilogy", kobo_dev.model) assert.truthy(kobo_dev:needsTouchScreenProbe()) + -- This gets reset to nil during Kobo:init() since #4450 + assert.falsy(kobo_dev.touch_probe_ev_epoch_time) G_reader_settings:saveSetting("kobo_touch_switch_xy", true) kobo_dev:touchScreenProbe() local x, y = Screen:getWidth()-5, 10 @@ -94,11 +95,13 @@ describe("device module", function() type = EV_ABS, code = ABS_X, value = y, + time = TimeVal:now(), } local ev_y = { type = EV_ABS, code = ABS_Y, value = Screen:getWidth()-x, + time = TimeVal:now(), } kobo_dev.input:eventAdjustHook(ev_x) @@ -110,7 +113,6 @@ describe("device module", function() -- reset eventAdjustHook kobo_dev.input.eventAdjustHook = function() end - kobo_dev.touch_probe_ev_epoch_time = true end) it("should setup eventAdjustHooks properly for trilogy with non-epoch ev time", function() @@ -121,16 +123,21 @@ describe("device module", function() return osgetenv(key) end end) + + package.loaded['device/kobo/device'] = nil local kobo_dev = require("device/kobo/device") kobo_dev:init() local Screen = kobo_dev.screen assert.is.same("Kobo_trilogy", kobo_dev.model) + assert.truthy(kobo_dev:needsTouchScreenProbe()) + -- This gets reset to nil during Kobo:init() since #4450 + assert.falsy(kobo_dev.touch_probe_ev_epoch_time) + kobo_dev:touchScreenProbe() local x, y = Screen:getWidth()-5, 10 local EV_ABS = 3 local ABS_X = 00 local ABS_Y = 01 - -- mirror x, then switch_xy local ev_x = { type = EV_ABS, code = ABS_X, @@ -144,16 +151,13 @@ describe("device module", function() time = {sec = 1000} } - assert.truthy(kobo_dev.touch_probe_ev_epoch_time) - G_reader_settings:saveSetting("kobo_touch_switch_xy", true) - kobo_dev:touchScreenProbe() - kobo_dev.input:eventAdjustHook(ev_x) kobo_dev.input:eventAdjustHook(ev_y) local cur_sec = TimeVal:now().sec assert.truthy(cur_sec - ev_x.time.sec < 10) assert.truthy(cur_sec - ev_y.time.sec < 10) + -- reset eventAdjustHook kobo_dev.input.eventAdjustHook = function() end end)