diff --git a/frontend/apps/filemanager/filemanager.lua b/frontend/apps/filemanager/filemanager.lua index 16a54c828..7abadad6d 100644 --- a/frontend/apps/filemanager/filemanager.lua +++ b/frontend/apps/filemanager/filemanager.lua @@ -73,9 +73,10 @@ end -- init should be set to True when starting the FM for the first time -- (not coming from the reader). This allows the default to be properly set. function FileManager:setRotationMode(init) - local locked = G_reader_settings:readSetting("lock_rotation") + local locked = G_reader_settings:isTrue("lock_rotation") local rotation_mode = G_reader_settings:readSetting("fm_rotation_mode") or Screen.ORIENTATION_PORTRAIT - if locked or init then + -- Only enforce the default rotation on first FM open, or when switching to the FM when sticky rota is disabled. + if init or not locked then self:onSetRotationMode(rotation_mode) end end diff --git a/frontend/apps/reader/modules/readerview.lua b/frontend/apps/reader/modules/readerview.lua index 43944e40e..19688d614 100644 --- a/frontend/apps/reader/modules/readerview.lua +++ b/frontend/apps/reader/modules/readerview.lua @@ -724,13 +724,19 @@ end function ReaderView:onReadSettings(config) self.render_mode = config:readSetting("render_mode") or 0 - local locked = G_reader_settings:readSetting("lock_rotation") - local rotation_mode = config:readSetting("rotation_mode") - if not rotation_mode and locked then - if self.ui.document.info.has_pages then - rotation_mode = G_reader_settings:readSetting("kopt_rotation_mode") or 0 - else - rotation_mode = G_reader_settings:readSetting("copt_rotation_mode") or 0 + local rotation_mode = nil + local locked = G_reader_settings:isTrue("lock_rotation") + -- Keep current rotation by doing nothing when sticky rota is enabled. + if not locked then + -- Honor docsettings's rotation + rotation_mode = config:readSetting("rotation_mode") -- Doc's + if not rotation_mode then + -- No doc specific rotation, pickup global defaults for the doc type + if self.ui.document.info.has_pages then + rotation_mode = G_reader_settings:readSetting("kopt_rotation_mode") or Screen.ORIENTATION_PORTRAIT + else + rotation_mode = G_reader_settings:readSetting("copt_rotation_mode") or Screen.ORIENTATION_PORTRAIT + end end end if rotation_mode then diff --git a/frontend/device/generic/device.lua b/frontend/device/generic/device.lua index e45f91160..61d47aa31 100644 --- a/frontend/device/generic/device.lua +++ b/frontend/device/generic/device.lua @@ -51,7 +51,9 @@ local Device = { hasBGRFrameBuffer = no, canImportFiles = no, canShareText = no, + hasGSensor = no, canToggleGSensor = no, + isGSensorLocked = no, canToggleMassStorage = no, canUseWAL = yes, -- requires mmap'ed I/O on the target FS canRestart = yes, @@ -176,6 +178,13 @@ function Device:init() self:invertButtons() end end + + -- Honor the gyro lock + if self:hasGSensor() then + if G_reader_settings:isTrue("input_lock_gsensor") then + self:lockGSensor(true) + end + end end function Device:setScreenDPI(dpi_override) @@ -246,7 +255,7 @@ function Device:onPowerEvent(ev) -- Leave Portrait & Inverted Portrait alone, that works just fine. if bit.band(self.orig_rotation_mode, 1) == 1 then -- i.e., only switch to Portrait if we're currently in *any* Landscape orientation (odd number) - self.screen:setRotationMode(0) + self.screen:setRotationMode(self.screen.ORIENTATION_PORTRAIT) else self.orig_rotation_mode = nil end @@ -332,6 +341,28 @@ function Device:setIgnoreInput(enable) return true end -- Device specific method for toggling the GSensor function Device:toggleGSensor(toggle) end +-- Whether or not the GSensor should be locked to the current orientation (i.e. Portrait <-> Inverted Portrait or Landscape <-> Inverted Landscape only) +function Device:lockGSensor(toggle) + if not self:hasGSensor() then + return + end + + if toggle == true then + -- Lock GSensor to current roientation + self.isGSensorLocked = yes + elseif toggle == false then + -- Unlock GSensor + self.isGSensorLocked = no + else + -- Toggle it + if self:isGSensorLocked() then + self.isGSensorLocked = no + else + self.isGSensorLocked = yes + end + end +end + -- Device specific method for set custom light levels function Device:setScreenBrightness(level) end diff --git a/frontend/device/gesturedetector.lua b/frontend/device/gesturedetector.lua index 5c6bc19f6..757fd17d4 100644 --- a/frontend/device/gesturedetector.lua +++ b/frontend/device/gesturedetector.lua @@ -601,7 +601,7 @@ function GestureDetector:handlePan(tev) local pan_ev_multiswipe = pan_ev -- store a copy of pan_ev without rotation adjustment -- for multiswipe calculations when rotated - if self.screen.cur_rotation_mode > 0 then + if self.screen.cur_rotation_mode > self.screen.ORIENTATION_PORTRAIT then pan_ev_multiswipe = util.tableDeepCopy(pan_ev) end if msd_direction ~= msd_direction_prev then @@ -743,7 +743,7 @@ function GestureDetector:holdState(tev, hold) end end -local ges_coordinate_translation_90 = { +local ges_coordinate_translation_270 = { north = "west", south = "east", east = "north", @@ -763,7 +763,7 @@ local ges_coordinate_translation_180 = { southeast = "northwest", southwest = "northeast", } -local ges_coordinate_translation_270 = { +local ges_coordinate_translation_90 = { north = "east", south = "west", east = "south", @@ -787,8 +787,8 @@ end @return adjusted gesture. --]] function GestureDetector:adjustGesCoordinate(ges) - if self.screen.cur_rotation_mode == 1 then - -- in landscape mode rotated 270 + if self.screen.cur_rotation_mode == self.screen.ORIENTATION_LANDSCAPE then + -- in landscape mode rotated 90 if ges.pos then ges.pos.x, ges.pos.y = (self.screen:getWidth() - ges.pos.y), (ges.pos.x) end @@ -797,9 +797,9 @@ function GestureDetector:adjustGesCoordinate(ges) or ges.ges == "two_finger_swipe" or ges.ges == "two_finger_pan" then - ges.direction = translateGesDirCoordinate(ges.direction, ges_coordinate_translation_270) + ges.direction = translateGesDirCoordinate(ges.direction, ges_coordinate_translation_90) if ges.ges == "multiswipe" then - ges.multiswipe_directions = translateMultiswipeGesDirCoordinate(ges.multiswipe_directions, ges_coordinate_translation_270) + ges.multiswipe_directions = translateMultiswipeGesDirCoordinate(ges.multiswipe_directions, ges_coordinate_translation_90) end if ges.relative then ges.relative.x, ges.relative.y = -ges.relative.y, ges.relative.x @@ -814,8 +814,8 @@ function GestureDetector:adjustGesCoordinate(ges) ges.direction = "horizontal" end end - elseif self.screen.cur_rotation_mode == 3 then - -- in landscape mode rotated 90 + elseif self.screen.cur_rotation_mode == self.screen.ORIENTATION_LANDSCAPE_ROTATED then + -- in landscape mode rotated 270 if ges.pos then ges.pos.x, ges.pos.y = (ges.pos.y), (self.screen:getHeight() - ges.pos.x) end @@ -824,9 +824,9 @@ function GestureDetector:adjustGesCoordinate(ges) or ges.ges == "two_finger_swipe" or ges.ges == "two_finger_pan" then - ges.direction = translateGesDirCoordinate(ges.direction, ges_coordinate_translation_90) + ges.direction = translateGesDirCoordinate(ges.direction, ges_coordinate_translation_270) if ges.ges == "multiswipe" then - ges.multiswipe_directions = translateMultiswipeGesDirCoordinate(ges.multiswipe_directions, ges_coordinate_translation_90) + ges.multiswipe_directions = translateMultiswipeGesDirCoordinate(ges.multiswipe_directions, ges_coordinate_translation_270) end if ges.relative then ges.relative.x, ges.relative.y = ges.relative.y, -ges.relative.x @@ -841,7 +841,7 @@ function GestureDetector:adjustGesCoordinate(ges) ges.direction = "horizontal" end end - elseif self.screen.cur_rotation_mode == 2 then + elseif self.screen.cur_rotation_mode == self.screen.ORIENTATION_PORTRAIT_ROTATED then -- in portrait mode rotated 180 if ges.pos then ges.pos.x, ges.pos.y = (self.screen:getWidth() - ges.pos.x), (self.screen:getHeight() - ges.pos.y) diff --git a/frontend/device/input.lua b/frontend/device/input.lua index c7103764d..f0eab58bd 100644 --- a/frontend/device/input.lua +++ b/frontend/device/input.lua @@ -657,30 +657,41 @@ function Input:handleOasisOrientationEv(ev) end local old_rotation_mode = self.device.screen:getRotationMode() - local old_screen_mode = self.device.screen:getScreenMode() - if rotation_mode ~= old_rotation_mode and screen_mode == old_screen_mode then - self.device.screen:setRotationMode(rotation_mode) - local UIManager = require("ui/uimanager") - UIManager:onRotation() + if self.device:isGSensorLocked() then + local old_screen_mode = self.device.screen:getScreenMode() + if rotation_mode ~= old_rotation_mode and screen_mode == old_screen_mode then + -- Cheaper than a full SetRotationMode event, as we don't need to re-layout anything. + self.device.screen:setRotationMode(rotation_mode) + local UIManager = require("ui/uimanager") + UIManager:onRotation() + end + else + if rotation_mode ~= old_rotation_mode then + return Event:new("SetRotationMode", rotation_mode) + end end end --- Accelerometer on the Forma, c.f., drivers/hwmon/mma8x5x.c function Input:handleMiscEvNTX(ev) - local rotation_mode + local rotation_mode, screen_mode if ev.code == MSC_RAW then if ev.value == MSC_RAW_GSENSOR_PORTRAIT_UP then -- i.e., UR rotation_mode = framebuffer.ORIENTATION_PORTRAIT + screen_mode = 'portrait' elseif ev.value == MSC_RAW_GSENSOR_LANDSCAPE_RIGHT then -- i.e., CW rotation_mode = framebuffer.ORIENTATION_LANDSCAPE + screen_mode = 'landscape' elseif ev.value == MSC_RAW_GSENSOR_PORTRAIT_DOWN then -- i.e., UD rotation_mode = framebuffer.ORIENTATION_PORTRAIT_ROTATED + screen_mode = 'portrait' elseif ev.value == MSC_RAW_GSENSOR_LANDSCAPE_LEFT then -- i.e., CCW rotation_mode = framebuffer.ORIENTATION_LANDSCAPE_ROTATED + screen_mode = 'landscape' else -- Discard FRONT/BACK return @@ -691,22 +702,30 @@ function Input:handleMiscEvNTX(ev) end local old_rotation_mode = self.device.screen:getRotationMode() - -- NOTE: See the Oasis version just above us for a variant that's locked to the current ScreenMode. - -- Might be nice to expose the two behaviors to the user, somehow? - if rotation_mode and rotation_mode ~= old_rotation_mode then - return Event:new("SetRotationMode", rotation_mode) + if self.device:isGSensorLocked() then + local old_screen_mode = self.device.screen:getScreenMode() + if rotation_mode and rotation_mode ~= old_rotation_mode and screen_mode == old_screen_mode then + -- Cheaper than a full SetRotationMode event, as we don't need to re-layout anything. + self.device.screen:setRotationMode(rotation_mode) + local UIManager = require("ui/uimanager") + UIManager:onRotation() + end + else + if rotation_mode and rotation_mode ~= old_rotation_mode then + return Event:new("SetRotationMode", rotation_mode) + end end end --- Allow toggling the accelerometer at runtime. function Input:toggleMiscEvNTX(toggle) - if toggle and toggle == true then + if toggle == true then -- Honor Gyro events if not self.isNTXAccelHooked then self.handleMiscEv = self.handleMiscEvNTX self.isNTXAccelHooked = true end - elseif toggle and toggle == false then + elseif toggle == false then -- Ignore Gyro events if self.isNTXAccelHooked then self.handleMiscEv = function() end diff --git a/frontend/device/kindle/device.lua b/frontend/device/kindle/device.lua index e52f273aa..4f016583a 100644 --- a/frontend/device/kindle/device.lua +++ b/frontend/device/kindle/device.lua @@ -179,7 +179,7 @@ function Kindle:intoScreenSaver() -- Leave Portrait & Inverted Portrait alone, that works just fine. if bit.band(self.orig_rotation_mode, 1) == 1 then -- i.e., only switch to Portrait if we're currently in *any* Landscape orientation (odd number) - self.screen:setRotationMode(0) + self.screen:setRotationMode(self.screen.ORIENTATION_PORTRAIT) else self.orig_rotation_mode = nil end @@ -371,6 +371,7 @@ local KindleOasis = Kindle:new{ isTouchDevice = yes, hasFrontlight = yes, hasKeys = yes, + hasGSensor = yes, display_dpi = 300, --[[ -- NOTE: Points to event3 on WiFi devices, event4 on 3G devices... @@ -386,6 +387,7 @@ local KindleOasis2 = Kindle:new{ isTouchDevice = yes, hasFrontlight = yes, hasKeys = yes, + hasGSensor = yes, display_dpi = 300, touch_dev = "/dev/input/by-path/platform-30a30000.i2c-event", } diff --git a/frontend/device/kobo/device.lua b/frontend/device/kobo/device.lua index 30dbbbbed..38f32e4cc 100644 --- a/frontend/device/kobo/device.lua +++ b/frontend/device/kobo/device.lua @@ -222,6 +222,7 @@ local KoboFrost = Kobo:new{ model = "Kobo_frost", hasFrontlight = yes, hasKeys = yes, + hasGSensor = yes, canToggleGSensor = yes, touch_snow_protocol = true, misc_ntx_gsensor_protocol = true, @@ -244,6 +245,7 @@ local KoboStorm = Kobo:new{ model = "Kobo_storm", hasFrontlight = yes, hasKeys = yes, + hasGSensor = yes, canToggleGSensor = yes, touch_snow_protocol = true, misc_ntx_gsensor_protocol = true, diff --git a/frontend/device/pocketbook/device.lua b/frontend/device/pocketbook/device.lua index 5f416157b..fcc45119b 100644 --- a/frontend/device/pocketbook/device.lua +++ b/frontend/device/pocketbook/device.lua @@ -148,8 +148,8 @@ function PocketBook:init() -- fix rotation for Color Lux device if PocketBook:getDeviceModel() == "PocketBook Color Lux" then - self.screen.blitbuffer_rotation_mode = 0 - self.screen.native_rotation_mode = 0 + self.screen.blitbuffer_rotation_mode = self.screen.ORIENTATION_PORTRAIT + self.screen.native_rotation_mode = self.screen.ORIENTATION_PORTRAIT end os.remove(self.emu_events_dev) diff --git a/frontend/ui/elements/screen_rotation_menu_table.lua b/frontend/ui/elements/screen_rotation_menu_table.lua index 65bb9e61f..364676853 100644 --- a/frontend/ui/elements/screen_rotation_menu_table.lua +++ b/frontend/ui/elements/screen_rotation_menu_table.lua @@ -11,9 +11,10 @@ return { sub_item_table_func = function() local rotation_table = {} - if Device:canToggleGSensor() then + if Device:hasGSensor() and Device:canToggleGSensor() then table.insert(rotation_table, { text = _("Ignore accelerometer rotation events"), + help_text = _("This will inhibit automatic rotations triggered by your device's gyro."), checked_func = function() return G_reader_settings:isTrue("input_ignore_gsensor") end, @@ -24,9 +25,33 @@ return { }) end + if Device:hasGSensor() then + table.insert(rotation_table, { + text = _("Lock auto rotation to current orientation"), + help_text = _([[ +When checked, the gyro will only be honored when switching between the two inverse variants of your current rotation, +i.e., Portrait <-> Inverted Portrait OR Landscape <-> Inverted Landscape. +Switching between (Inverted) Portrait and (Inverted) Landscape will be inhibited. +If you need to do so, you'll have to use the UI toggles.]]), + enabled_func = function() + return G_reader_settings:nilOrFalse("input_ignore_gsensor") + end, + checked_func = function() + return G_reader_settings:isTrue("input_lock_gsensor") + end, + callback = function() + G_reader_settings:flipNilOrFalse("input_lock_gsensor") + Device:lockGSensor(G_reader_settings:isTrue("input_lock_gsensor")) + end, + }) + end + table.insert(rotation_table, { - text = _("Keep file browser rotation"), - help_text = _("When checked the rotation of the file browser and the reader will not affect each other"), + text = _("Keep current rotation across views"), + help_text = _([[ +When checked, the current rotation will be kept when switching between the file browser and the reader, in both directions, and that no matter what the document's saved rotation or the default reader or file browser rotation may be. +This means that nothing will ever sneak a rotation behind your back, you choose your device's rotation, and it stays that way. +When unchecked, the default rotation of the file browser and the default/saved reader rotation will not affect each other (i.e., they will be honored), and may very well be different.]]), checked_func = function() return G_reader_settings:isTrue("lock_rotation") end, diff --git a/frontend/ui/uimanager.lua b/frontend/ui/uimanager.lua index 555472b0a..03a23bb3e 100644 --- a/frontend/ui/uimanager.lua +++ b/frontend/ui/uimanager.lua @@ -55,7 +55,7 @@ function UIManager:init() } self.poweroff_action = function() self._entered_poweroff_stage = true; - Screen:setRotationMode(0) + Screen:setRotationMode(Screen.ORIENTATION_PORTRAIT) require("ui/screensaver"):show("poweroff", _("Powered off")) if Device:needsScreenRefreshAfterResume() then Screen:refreshFull() @@ -68,7 +68,7 @@ function UIManager:init() end self.reboot_action = function() self._entered_poweroff_stage = true; - Screen:setRotationMode(0) + Screen:setRotationMode(Screen.ORIENTATION_PORTRAIT) require("ui/screensaver"):show("reboot", _("Rebooting…")) if Device:needsScreenRefreshAfterResume() then Screen:refreshFull() diff --git a/spec/unit/device_spec.lua b/spec/unit/device_spec.lua index 64d44a2ec..d49117235 100644 --- a/spec/unit/device_spec.lua +++ b/spec/unit/device_spec.lua @@ -287,6 +287,7 @@ describe("device module", function() local kindle_dev = require('device/kindle/device') assert.is.same("KindleOasis", kindle_dev.model) kindle_dev:init() + kindle_dev:lockGSensor(true) kindle_dev.input:waitEvent() assert.stub(UIManager.onRotation).was_called() diff --git a/spec/unit/gesturedetector_spec.lua b/spec/unit/gesturedetector_spec.lua index 0bb096bda..548a113a7 100644 --- a/spec/unit/gesturedetector_spec.lua +++ b/spec/unit/gesturedetector_spec.lua @@ -12,7 +12,12 @@ describe("gesturedetector module", function() direction = direction, multiswipe_directions = direction, } - GestureDetector.screen = {} + GestureDetector.screen = { + ORIENTATION_PORTRAIT = 0, + ORIENTATION_LANDSCAPE = 1, + ORIENTATION_PORTRAIT_ROTATED = 2, + ORIENTATION_LANDSCAPE_ROTATED = 3, + } GestureDetector.screen.cur_rotation_mode = rotation_mode return GestureDetector:adjustGesCoordinate(ges).direction @@ -25,7 +30,7 @@ describe("gesturedetector module", function() assert.are.equal("north", adjustTest("two_finger_swipe", "north", 0)) assert.are.equal("north", adjustTest("two_finger_pan", "north", 0)) end) - it("should translate rotation 90", function() + it("should translate rotation 270", function() assert.are.equal("west", adjustTest("swipe", "north", 3)) assert.are.equal("west", adjustTest("multiswipe", "north", 3)) assert.are.equal("west", adjustTest("pan", "north", 3)) @@ -39,7 +44,7 @@ describe("gesturedetector module", function() assert.are.equal("south", adjustTest("two_finger_swipe", "north", 2)) assert.are.equal("south", adjustTest("two_finger_pan", "north", 2)) end) - it("should translate rotation 270", function() + it("should translate rotation 90", function() assert.are.equal("east", adjustTest("swipe", "north", 1)) assert.are.equal("east", adjustTest("multiswipe", "north", 1)) assert.are.equal("east", adjustTest("pan", "north", 1))