2
0
mirror of https://github.com/koreader/koreader synced 2024-11-13 19:11:25 +00:00

input: drop use of ffi/input

Get rid of the weird interaction between device implementations,
`ffi.util` (`isSDL`, `noSDL`) and the device input code: each
device knows if it's using SDL or not, so rely on that to load
the right underlying input implementation.
This commit is contained in:
Benoit Pierre 2024-09-22 20:23:28 +02:00 committed by Frans de Jonge
parent 283c764351
commit 0c0d2ab9f1
13 changed files with 79 additions and 83 deletions

2
base

@ -1 +1 @@
Subproject commit e65c7dd5f867cb026e6058a680f14da6adb77dd3
Subproject commit be7404777ba0b45b551084c708b460e5a623007d

View File

@ -4,54 +4,46 @@ local util = require("ffi/util")
local function probeDevice()
if isAndroid then
util.noSDL()
return require("device/android/device")
end
local kindle_test_stat = lfs.attributes("/proc/usid")
if kindle_test_stat then
util.noSDL()
return require("device/kindle/device")
end
local kobo_test_stat = lfs.attributes("/bin/kobo_config.sh")
if kobo_test_stat then
util.noSDL()
return require("device/kobo/device")
end
local pbook_test_stat = lfs.attributes("/ebrmain")
if pbook_test_stat then
util.noSDL()
return require("device/pocketbook/device")
end
local remarkable_test_stat = lfs.attributes("/usr/bin/xochitl")
if remarkable_test_stat then
util.noSDL()
return require("device/remarkable/device")
end
local sony_prstux_test_stat = lfs.attributes("/etc/PRSTUX")
if sony_prstux_test_stat then
util.noSDL()
return require("device/sony-prstux/device")
end
local cervantes_test_stat = lfs.attributes("/usr/bin/ntxinfo")
if cervantes_test_stat then
util.noSDL()
return require("device/cervantes/device")
end
-- add new ports here:
--
-- if --[[ implement a proper test instead --]] false then
-- util.noSDL()
-- return require("device/newport/device")
-- end
if util.isSDL() then
if util.loadSDL2() then
return require("device/sdl/device")
end

View File

@ -123,9 +123,9 @@ function Cervantes:init()
[116] = "Power",
}
}
self.input.open("/dev/input/event0") -- Keys
self.input.open("/dev/input/event1") -- touchscreen
self.input.open("fake_events") -- usb events
self.input:open("/dev/input/event0") -- Keys
self.input:open("/dev/input/event1") -- touchscreen
self.input:open("fake_events") -- usb events
self:initEventAdjustHooks()
Generic.init(self)
end

View File

@ -608,7 +608,7 @@ function Device:exit()
G_reader_settings:close()
-- I/O teardown
self.input.teardown()
self.input:teardown()
end
-- Lifted from busybox's libbb/inet_cksum.c

View File

@ -9,7 +9,6 @@ local GestureDetector = require("device/gesturedetector")
local Key = require("device/key")
local UIManager
local framebuffer = require("ffi/framebuffer")
local input = require("ffi/input")
local logger = require("logger")
local time = require("ui/time")
local _ = require("gettext")
@ -211,6 +210,31 @@ function Input:new(o)
end
function Input:init()
-- Setup underlying input implementation.
if self.input then -- luacheck: ignore 542
-- Already setup (e.g. stubbed by the testsuite).
elseif self.device:isSDL() then
self.input = require("ffi/input_SDL2_0")
self.hasClipboardText = function()
return self.input.hasClipboardText()
end
self.getClipboardText = function()
return self.input.getClipboardText()
end
self.setClipboardText = function(text)
return self.input.setClipboardText(text)
end
self.gameControllerRumble = function(left_intensity, right_intensity, duration)
return self.input.gameControllerRumble(left_intensity, right_intensity, duration)
end
elseif self.device:isAndroid() then
self.input = require("ffi/input_android")
elseif self.device:isPocketBook() then
self.input = require("ffi/input_pocketbook")
else
self.input = require("libs/libkoreader-input")
end
-- Initialize instance-specific tables
-- NOTE: All of these arrays may be destroyed & recreated at runtime, so we don't want a parent/class object for those.
self.timer_callbacks = {}
@ -306,10 +330,13 @@ Note that we adhere to the "." syntax here for compatibility.
The `name` argument is optional, and used for logging purposes only.
--]]
function Input.open(path, name)
function Input:open(path, name)
if self.input.is_ffi then
return self.input.open(path, name)
end
-- Make sure we don't open the same device twice.
if not Input.opened_devices[path] then
local fd = input.open(path)
local fd = self.input.open(path)
if fd then
Input.opened_devices[path] = fd
if name then
@ -332,10 +359,10 @@ Note that we adhere to the "." syntax here for compatibility.
The `name` argument is optional, and used for logging purposes only.
`path` is mandatory, though!
--]]
function Input.fdopen(fd, path, name)
function Input:fdopen(fd, path, name)
-- Make sure we don't open the same device twice.
if not Input.opened_devices[path] then
input.fdopen(fd)
self.input.fdopen(fd)
-- As with input.open, it will throw on error (closing the fd first)
Input.opened_devices[path] = fd
if name then
@ -352,11 +379,14 @@ Wrapper for our Lua/C input module's close.
Note that we adhere to the "." syntax here for compatibility.
--]]
function Input.close(path)
function Input:close(path)
if self.input.is_ffi then
return self.input.close(path)
end
-- Make sure we actually know about this device
local fd = Input.opened_devices[path]
if fd then
local ok, err = input.close(fd)
local ok, err = self.input.close(fd)
if ok or err == C.ENODEV then
-- Either the call succeeded,
-- or the backend had already caught an ENODEV in waitForInput and closed the fd internally.
@ -374,25 +404,11 @@ Wrapper for our Lua/C input module's closeAll.
Note that we adhere to the "." syntax here for compatibility.
--]]
function Input.teardown()
input.closeAll()
function Input:teardown()
self.input.closeAll()
Input.opened_devices = {}
end
-- Wrappers for the custom FFI implementations with no concept of paths or fd
if input.is_ffi then
-- Pass args as-is. None of 'em actually *take* arguments, but some may be invoked as methods...
function Input.open(...)
return input.open(...)
end
function Input.close(...)
return input.close(...)
end
function Input.teardown(...)
return input.closeAll(...)
end
end
--[[--
Different device models can implement their own hooks and register them.
--]]
@ -536,7 +552,7 @@ function Input:setTimeout(slot, ges, cb, origin, delay)
-- If we're on a platform with the timerfd backend, handle that
local timerfd
if input.setTimer then
if self.input.setTimer then
-- If GestureDetector's clock source probing was inconclusive, do this on the UI timescale instead.
if clock_id == -1 then
deadline = time.now() + delay
@ -548,7 +564,7 @@ function Input:setTimeout(slot, ges, cb, origin, delay)
-- instead of ensuring that ourselves via a polling timeout.
-- This ensures perfect accuracy, and allows it to be computed in the event's own timescale.
local sec, usec = time.split_s_us(deadline)
timerfd = input.setTimer(clock_id, sec, usec)
timerfd = self.input.setTimer(clock_id, sec, usec)
end
if timerfd then
-- It worked, tweak the table a bit to make it clear the deadline will be handled by the kernel
@ -582,7 +598,7 @@ function Input:clearTimeout(slot, ges)
if item.slot == slot and (not ges or item.gesture == ges) then
-- If the timerfd backend is in use, close the fd and free the list's node, too.
if item.timerfd then
input.clearTimer(item.timerfd)
self.input.clearTimer(item.timerfd)
end
table.remove(self.timer_callbacks, i)
end
@ -591,10 +607,10 @@ end
function Input:clearTimeouts()
-- If the timerfd backend is in use, close the fds, too
if input.setTimer then
if self.input.setTimer then
for _, item in ipairs(self.timer_callbacks) do
if item.timerfd then
input.clearTimer(item.timerfd)
self.input.clearTimer(item.timerfd)
end
end
end
@ -1354,7 +1370,7 @@ function Input:waitEvent(now, deadline)
local timerfd
local sec, usec = time.split_s_us(poll_timeout)
ok, ev, timerfd = input.waitForEvent(sec, usec)
ok, ev, timerfd = self.input.waitForEvent(sec, usec)
-- We got an actual input event, go and process it
if ok then break end
@ -1404,7 +1420,7 @@ function Input:waitEvent(now, deadline)
-- GestureDetector has guards in place to avoid double frees in case the callback itself
-- affected the timerfd or timer_callbacks list (e.g., by dropping a contact).
if timerfd then
input.clearTimer(timerfd)
self.input.clearTimer(timerfd)
end
table.remove(self.timer_callbacks, timer_idx)
@ -1441,7 +1457,7 @@ function Input:waitEvent(now, deadline)
end
local sec, usec = time.split_s_us(poll_timeout)
ok, ev = input.waitForEvent(sec, usec)
ok, ev = self.input.waitForEvent(sec, usec)
end -- if #timer_callbacks > 0
-- Handle errors

View File

@ -489,7 +489,7 @@ function Kindle:openInputDevices()
for i = 0, tonumber(dev_count[0]) - 1 do
local dev = devices[i]
if dev.matched then
self.input.fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
self.input:fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
end
end
C.free(devices)
@ -498,11 +498,11 @@ function Kindle:openInputDevices()
logger.warn("We failed to auto-detect the proper input devices, input handling may be inconsistent!")
if self.touch_dev then
-- We've got a preferred path specified for the touch panel
self.input.open(self.touch_dev)
self.input:open(self.touch_dev)
else
-- That generally works out well enough on legacy devices...
self.input.open("/dev/input/event0")
self.input.open("/dev/input/event1")
self.input:open("/dev/input/event0")
self.input:open("/dev/input/event1")
end
end
@ -516,14 +516,14 @@ function Kindle:openInputDevices()
for i = 0, tonumber(dev_count[0]) - 1 do
local dev = devices[i]
if dev.matched then
self.input.fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
self.input:fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
end
end
C.free(devices)
end
end
self.input.open("fake_events")
self.input:open("fake_events")
end
function Kindle:otaModel()

View File

@ -880,9 +880,9 @@ function Kobo:init()
-- We need to single out whichever device provides pagination buttons or sleep cover events, as we'll want to tweak key repeat there...
-- The first one will do, as it's extremely likely to be event0, and that's pretty fairly set in stone on NTX boards.
if (bit.band(dev.type, C.INPUT_PAGINATION_BUTTONS) ~= 0 or bit.band(dev.type, C.INPUT_SLEEP_COVER) ~= 0) and not self.ntx_fd then
self.ntx_fd = self.input.fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
self.ntx_fd = self.input:fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
else
self.input.fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
self.input:fdopen(tonumber(dev.fd), ffi.string(dev.path), ffi.string(dev.name))
end
end
end
@ -891,9 +891,9 @@ function Kobo:init()
-- Auto-detection failed, warn and fall back to defaults
logger.warn("We failed to auto-detect the proper input devices, input handling may be inconsistent!")
-- Various HW Buttons, Switches & Synthetic NTX events
self.ntx_fd = self.input.open(self.ntx_dev)
self.ntx_fd = self.input:open(self.ntx_dev)
-- Touch panel
self.input.open(self.touch_dev)
self.input:open(self.touch_dev)
end
-- NOTE: On devices with a gyro, there may be a dedicated input device outputting the raw accelerometer data

View File

@ -243,7 +243,7 @@ function PocketBook:init()
self._model_init()
-- NOTE: This is the odd one out actually calling input.open as a *method*,
-- which the imp supports to get access to self.input.raw_input
if (not self.input.raw_input) or (not pcall(self.input.open, self.input)) then
if (not self.input.raw_input) or (not pcall(self.input.open, self.input, self.input)) then
inkview.OpenScreen()
-- Raw mode open failed (no permissions?), so we'll run the usual way.
-- Disable touch coordinate translation as inkview will do that.

View File

@ -167,9 +167,9 @@ function Remarkable:init()
self.input_ts = "/dev/input/touchscreen0"
end
self.input.open(self.input_wacom) -- Wacom
self.input.open(self.input_ts) -- Touchscreen
self.input.open(self.input_buttons) -- Buttons
self.input:open(self.input_wacom) -- Wacom
self.input:open(self.input_ts) -- Touchscreen
self.input:open(self.input_buttons) -- Buttons
local scalex = screen_width / self.mt_width
local scaley = screen_height / self.mt_height
@ -202,7 +202,7 @@ function Remarkable:init()
end
-- USB plug/unplug, battery charge/not charging are generated as fake events
self.input.open("fake_events")
self.input:open("fake_events")
local rotation_mode = self.screen.DEVICE_ROTATED_UPRIGHT
self.screen.native_rotation_mode = rotation_mode

View File

@ -193,7 +193,6 @@ function Device:init()
local ok, re = pcall(self.screen.setWindowIcon, self.screen, "resources/koreader.png")
if not ok then logger.warn(re) end
local input = require("ffi/input")
self.input = require("device/input"):new{
device = self,
event_map = dofile("frontend/device/sdl/event_map_sdl2.lua"),
@ -296,18 +295,6 @@ function Device:init()
UIManager:sendEvent(Event:new("TextInput", tostring(ev.value)))
end
end,
hasClipboardText = function()
return input.hasClipboardText()
end,
getClipboardText = function()
return input.getClipboardText()
end,
setClipboardText = function(text)
return input.setClipboardText(text)
end,
gameControllerRumble = function(left_intensity, right_intensity, duration)
return input.gameControllerRumble(left_intensity, right_intensity, duration)
end,
}
self.keyboard_layout = dofile("frontend/device/sdl/keyboard_layout.lua")

View File

@ -59,10 +59,10 @@ function SonyPRSTUX:init()
event_map = dofile("frontend/device/sony-prstux/event_map.lua"),
}
self.input.open("/dev/input/event0") -- Keys
self.input.open("/dev/input/event1") -- touchscreen
self.input.open("/dev/input/event2") -- power button
self.input.open("fake_events") -- usb plug-in/out and charging/not-charging
self.input:open("/dev/input/event0") -- Keys
self.input:open("/dev/input/event1") -- touchscreen
self.input:open("/dev/input/event2") -- power button
self.input:open("fake_events") -- usb plug-in/out and charging/not-charging
self.input:registerEventAdjustHook(adjustTouchEvt)
local rotation_mode = self.screen.DEVICE_ROTATED_COUNTER_CLOCKWISE

View File

@ -247,7 +247,7 @@ function ExternalKeyboard:_onEvdevInputRemove(event_path)
end
-- Close our Input handle on it
Device.input.close(event_path)
Device.input:close(event_path)
ExternalKeyboard.keyboard_fds[event_path] = nil
ExternalKeyboard.connected_keyboards = ExternalKeyboard.connected_keyboards - 1
@ -363,7 +363,7 @@ function ExternalKeyboard:setupKeyboard(data)
logger.dbg("ExternalKeyboard:setupKeyboard", keyboard_info.name, "@", keyboard_info.event_path, "- has_dpad:", keyboard_info.has_dpad)
-- Check if we already know about this event file.
if ExternalKeyboard.keyboard_fds[keyboard_info.event_path] == nil then
local ok, fd = pcall(Device.input.fdopen, keyboard_info.event_fd, keyboard_info.event_path, keyboard_info.name)
local ok, fd = pcall(Device.input.fdopen, Device.input, keyboard_info.event_fd, keyboard_info.event_path, keyboard_info.name)
if not ok then
UIManager:show(InfoMessage:new{
text = "Error opening keyboard:\n" .. tostring(fd),

View File

@ -41,6 +41,8 @@ describe("device module", function()
before_each(function()
package.loaded["ffi/framebuffer_mxcfb"] = mock_fb
mock_input = require("device/input")
mock_input.input = {}
mock_input.gameControllerRumble = function() return false end
stub(mock_input, "open")
stub(os, "getenv")
stub(os, "execute")
@ -308,9 +310,8 @@ describe("device module", function()
package.unload("device/kindle/device")
io.open = make_io_open_kindle_model_override("G0B0GCXXX")
mock_ffi_input = require("ffi/input")
stub(mock_ffi_input, "waitForEvent")
mock_ffi_input.waitForEvent.returns(true, {
stub(mock_input.input, "waitForEvent")
mock_input.input.waitForEvent.returns(true, {
{
type = C.EV_ABS,
time = {
@ -333,7 +334,7 @@ describe("device module", function()
kindle_dev.input:waitEvent()
assert.stub(UIManager.onRotation).was_called()
mock_ffi_input.waitForEvent:revert()
mock_input.input.waitForEvent:revert()
UIManager.onRotation:revert()
end)
end)