Kobo: Fix input on Mk. 3 (i.e., Kobo Touch A/B). (#9474)

* Kobo: Discriminate between the Touch A/B and the Touch C properly, and implement actual support for the A/B input quirks. This means the clunky touchscreen probe widget shown on fresh installs on those devices is now gone :}.
* Input: Fix an off-by-one in most adjustTouchMirrorX/Y callers (only rM was doing it right), and adjust their documentation to avoid similar mistakes in the future.
* GestureDetector: Unify logging to always display transformed coordinates for simple gestures.
* GestureDetector: Fix two-contact hold lifts to be computed at the midpoint between the two contacts, like their holds counterpart already did.
pull/9478/head
NiLuJe 2 years ago committed by GitHub
parent 0967098a0d
commit 1b14ee36b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -113,7 +113,7 @@ function Cervantes:initEventAdjustHooks()
if self.touch_mirrored_x then
self.input:registerEventAdjustHook(
self.input.adjustTouchMirrorX,
self.screen:getWidth()
(self.screen:getWidth() - 1)
)
end

@ -498,17 +498,18 @@ function Contact:tapState(new_tap)
h = 0,
}
local tap_span = pos0:distance(pos1)
logger.dbg("two_finger_tap detected with span", tap_span)
local tap_pos = pos0:midpoint(pos1)
logger.dbg("two_finger_tap detected @", tap_pos.x, tap_pos.y, "with span", tap_span)
-- Don't drop buddy, voidState will handle it
gesture_detector:dropContact(self)
return {
ges = "two_finger_tap",
pos = pos0:midpoint(pos1),
pos = tap_pos,
span = tap_span,
time = tev.timev,
}
else
logger.dbg("Two-contact tap failed to pass the two_finger_tap constraints")
logger.dbg("Contact:tapState: Two-contact tap failed to pass the two_finger_tap constraints -> single tap @", tev.x, tev.y)
-- We blew the gesture position/time constraints,
-- neuter buddy and send a single tap on this slot.
buddy_contact.state = Contact.voidState
@ -661,7 +662,7 @@ function Contact:handleNonTap(new_tap)
-- NOTE: If we happened to have moved enough, holdState will generate a hold_pan on the *next* event,
-- but for now, the initial hold is mandatory.
-- On the other hand, if we're *already* in pan state, we stay there and *never* switch to hold.
logger.dbg("hold timer detected a hold gesture in slot", slot)
logger.dbg("hold timer tripped a switch to hold state in slot", slot)
return self:switchState(Contact.holdState, true)
end
end
@ -1167,14 +1168,16 @@ function Contact:holdState(new_hold)
h = 0,
}
local tap_span = pos0:distance(pos1)
logger.dbg("two_finger_hold detected with span", tap_span)
local tap_pos = pos0:midpoint(pos1)
logger.dbg("two_finger_hold detected @", tap_pos.x, tap_pos.y, "with span", tap_span)
return {
ges = "two_finger_hold",
pos = pos0:midpoint(pos1),
pos = tap_pos,
span = tap_span,
time = tev.timev,
}
elseif self.down then
logger.dbg("hold detected @", tev.x, tev.y)
return {
ges = "hold",
pos = Geom:new{
@ -1212,30 +1215,41 @@ function Contact:holdState(new_hold)
elseif self.mt_gesture == "hold_pan" or self.mt_gesture == "pan" then
self.mt_gesture = "hold_pan_release"
buddy_contact.mt_gesture = "hold_pan_release"
logger.dbg("two_finger_hold_pan_release detected")
else
self.mt_gesture = "hold_release"
buddy_contact.mt_gesture = "hold_release"
logger.dbg("two_finger_hold_release detected")
end
-- Neuter its buddy
buddy_contact.state = Contact.voidState
-- Don't drop buddy, voidState will handle it
gesture_detector:dropContact(self)
local pos0 = Geom:new{
x = tev.x,
y = tev.y,
w = 0,
h = 0,
}
local pos1 = Geom:new{
x = buddy_contact.current_tev.x,
y = buddy_contact.current_tev.y,
w = 0,
h = 0,
}
local ges_type = self.mt_gesture == "hold_pan_release" and "two_finger_hold_pan_release" or "two_finger_hold_release"
local tap_span = pos0:distance(pos1)
local tap_pos = pos0:midpoint(pos1)
logger.dbg(ges_type, "detected @", tap_pos.x, tap_pos.y, "with span", tap_span)
return {
ges = self.mt_gesture == "hold_pan_release" and "two_finger_hold_pan_release" or "two_finger_hold_release",
pos = Geom:new{
x = tev.x,
y = tev.y,
w = 0,
h = 0,
},
ges = ges_type,
pos = tap_pos,
span = tap_span,
time = tev.timev,
}
elseif self.down then
-- Contact lift, emit a hold_release
logger.dbg("hold_release detected")
logger.dbg("hold_release detected @", tev.x, tev.y)
gesture_detector:dropContact(self)
return {
ges = "hold_release",

@ -341,17 +341,17 @@ function Input:adjustTouchScale(ev, by)
end
end
function Input:adjustTouchMirrorX(ev, width)
function Input:adjustTouchMirrorX(ev, max_x)
if ev.type == C.EV_ABS
and (ev.code == C.ABS_X or ev.code == C.ABS_MT_POSITION_X) then
ev.value = width - ev.value
ev.value = max_x - ev.value
end
end
function Input:adjustTouchMirrorY(ev, height)
function Input:adjustTouchMirrorY(ev, max_y)
if ev.type == C.EV_ABS
and (ev.code == C.ABS_Y or ev.code == C.ABS_MT_POSITION_Y) then
ev.value = height - ev.value
ev.value = max_y - ev.value
end
end
@ -847,6 +847,20 @@ function Input:handleTouchEvLegacy(ev)
self:setMtSlot(MTSlot.slot, "timev", time.timeval(ev.time))
end
-- On Kobo Mk. 3 devices, the frame that reports a contact lift *actually* does the coordinates transform for us...
-- Unfortunately, our own transforms are not stateful, so, just revert 'em here,
-- since we can't simply avoid not doing 'em for that frame...
-- c.f., https://github.com/koreader/koreader/issues/2128#issuecomment-1236289909 for logs on a Touch B
if self.touch_kobo_mk3_protocol then
if self:getCurrentMtSlotData("id") == -1 then
-- Technically, it's the frame where ABS_PRESSURE is set to 0 ;).
local y = 599 - self:getCurrentMtSlotData("x") -- Mk. 3 devices are all 600x800, so just hard-code it here.
local x = self:getCurrentMtSlotData("y")
self:setCurrentMtSlot("x", x)
self:setCurrentMtSlot("y", y)
end
end
-- feed ev in all slots to state machine
local touch_gestures = self.gesture_detector:feedEvent(self.MTSlots)
self:newFrame()

@ -122,11 +122,16 @@ local Kobo = Generic:new{
unexpected_wakeup_count = 0
}
-- Kobo Touch:
local KoboTrilogy = Kobo:new{
model = "Kobo_trilogy",
needsTouchScreenProbe = yes,
touch_switch_xy = false,
-- Kobo Touch A/B:
local KoboTrilogyAB = Kobo:new{
model = "Kobo_trilogy_AB",
touch_kobo_mk3_protocol = true,
hasKeys = yes,
hasMultitouch = no,
}
-- Kobo Touch C:
local KoboTrilogyC = Kobo:new{
model = "Kobo_trilogy_C",
hasKeys = yes,
hasMultitouch = no,
}
@ -646,24 +651,7 @@ function Kobo:init()
-- Input handling on Kobo is a thing of nightmares, start by setting up the actual evdev handler...
self:setTouchEventHandler()
-- And then handle the extra shenanigans if necessary.
if not self.needsTouchScreenProbe() then
self:initEventAdjustHooks()
else
-- If touch probe is required, we postpone EventAdjustHook to *after* it has run,
-- because some of it depends on its results...
self.touchScreenProbe = function()
-- Only run the probe once ;).
if G_reader_settings:hasNot("kobo_touch_switch_xy") then
local TouchProbe = require("tools/kobo_touch_probe")
local UIManager = require("ui/uimanager")
UIManager:show(TouchProbe:new{})
UIManager:run()
-- If all goes well, we should now have a kobo_touch_switch_xy setting.
end
self.touch_switch_xy = G_reader_settings:readSetting("kobo_touch_switch_xy")
self:initEventAdjustHooks()
end
end
self:initEventAdjustHooks()
-- See if the device supports key repeat
self:getKeyRepeat()
@ -778,6 +766,9 @@ function Kobo:setTouchEventHandler()
self.input.handleTouchEv = self.input.handleTouchEvPhoenix
elseif not self:hasMultitouch() then
self.input.handleTouchEv = self.input.handleTouchEvLegacy
if self.touch_kobo_mk3_protocol then
self.input.touch_kobo_mk3_protocol = true
end
end
-- Accelerometer
@ -792,7 +783,7 @@ function Kobo:setTouchEventHandler()
end
function Kobo:initEventAdjustHooks()
-- NOTE: On trilogy, adjustTouchSwitchXY needs to be called before adjustTouchMirrorX
-- NOTE: adjustTouchSwitchXY needs to be called before adjustTouchMirrorX
if self.touch_switch_xy then
self.input:registerEventAdjustHook(self.input.adjustTouchSwitchXY)
end
@ -801,7 +792,7 @@ function Kobo:initEventAdjustHooks()
self.input:registerEventAdjustHook(
self.input.adjustTouchMirrorX,
--- NOTE: This is safe, we enforce the canonical portrait rotation on startup.
self.screen:getWidth()
(self.screen:getWidth() - 1)
)
end
end
@ -1252,8 +1243,10 @@ elseif codename == "kraken" then
return KoboKraken
elseif codename == "phoenix" then
return KoboPhoenix
elseif codename == "trilogy" then
return KoboTrilogy
elseif codename == "trilogy" and product_id == "310" then
return KoboTrilogyAB
elseif codename == "trilogy" and product_id == "320" then
return KoboTrilogyC
elseif codename == "pixie" then
return KoboPixie
elseif codename == "alyssum" then
@ -1285,5 +1278,5 @@ elseif codename == "cadmus" then
elseif codename == "io" then
return KoboIo
else
error("unrecognized Kobo model "..codename)
error("unrecognized Kobo model ".. codename .. " with device id " .. product_id)
end

@ -304,7 +304,7 @@ function Device:init()
self.input:registerEventAdjustHook(self.input.adjustTouchSwitchXY)
self.input:registerEventAdjustHook(
self.input.adjustTouchMirrorX,
self.screen:getScreenWidth()
(self.screen:getScreenWidth() - 1)
)
end

@ -72,10 +72,12 @@ describe("device module", function()
assert.is.same("Kobo_dahlia", kobo_dev.model)
end)
it("should setup eventAdjustHooks properly for input in trilogy", function()
it("should setup eventAdjustHooks properly for input on trilogy C", function()
os.getenv.invokes(function(key)
if key == "PRODUCT" then
return "trilogy"
elseif key == "MODEL_NUMBER" then
return "320"
else
return osgetenv(key)
end
@ -86,10 +88,8 @@ describe("device module", function()
kobo_dev:init()
local Screen = kobo_dev.screen
assert.is.same("Kobo_trilogy", kobo_dev.model)
assert.truthy(kobo_dev:needsTouchScreenProbe())
G_reader_settings:saveSetting("kobo_touch_switch_xy", true)
kobo_dev:touchScreenProbe()
assert.is.same("Kobo_trilogy_C", kobo_dev.model)
assert.falsy(kobo_dev:needsTouchScreenProbe())
local x, y = Screen:getWidth()-5, 10
-- mirror x, then switch_xy
local ev_x = {
@ -101,7 +101,7 @@ describe("device module", function()
local ev_y = {
type = C.EV_ABS,
code = C.ABS_Y,
value = Screen:getWidth()-x,
value = Screen:getWidth() - 1 - x,
time = TimeVal:realtime(),
}
@ -125,6 +125,8 @@ describe("device module", function()
os.getenv.invokes(function(key)
if key == "PRODUCT" then
return "trilogy"
elseif key == "MODEL_NUMBER" then
return "320"
else
return osgetenv(key)
end
@ -135,9 +137,8 @@ describe("device module", function()
kobo_dev:init()
local Screen = kobo_dev.screen
assert.is.same("Kobo_trilogy", kobo_dev.model)
assert.truthy(kobo_dev:needsTouchScreenProbe())
kobo_dev:touchScreenProbe()
assert.is.same("Kobo_trilogy_C", kobo_dev.model)
assert.falsy(kobo_dev:needsTouchScreenProbe())
local x, y = Screen:getWidth()-5, 10
local ev_x = {
type = C.EV_ABS,
@ -148,7 +149,7 @@ describe("device module", function()
local ev_y = {
type = C.EV_ABS,
code = C.ABS_Y,
value = Screen:getWidth()-x,
value = Screen:getWidth() - 1 - x,
time = {sec = 1000}
}

Loading…
Cancel
Save