mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
* Added a new plugin external-keyboard. It listens to USB events. When keyboard is plugged in or plugged out, it updates device and input configuration accordingly. * Added new fake events UsbDevicePlugIn and UsbDevicePlugOut that are emitted when a device is connected to a book reader that plays the role of USB host. The usage of the existing events UsbPlugIn and UsbPlugOut has not changed - they are used when a reader is connected to a host. The koreader-base has a related PR for those events. * Did a small refactoring of initialization for the modules FocusManager and InputText. They check device keyboard capabilities on their when the module is first loaded and store it. Some of the initialization code has been extracted into functions, so that we can re-initialize them when keyboard is (dis)connected. * Initial implementation centered around text input, and tested with USB keyboards on devices with OTG support. * Said OTG shenanigans are so far supported on devices with debugfs & the chipidea driver, or sunxi devices.
103 lines
3.2 KiB
Lua
103 lines
3.2 KiB
Lua
local bit = require("bit")
|
|
local ffi = require("ffi")
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
|
|
-- Constants from the linux kernel input-event-codes.h
|
|
local KEY_UP = 103
|
|
local BTN_DPAD_UP = 0x220
|
|
|
|
local FindKeyboard = {}
|
|
|
|
local function count_set_bits(n)
|
|
-- Brian Kernighan's algorithm
|
|
local count = 0
|
|
while n ~= 0 do
|
|
count = count + 1
|
|
n = bit.band(n, n - 1)
|
|
end
|
|
return count
|
|
end
|
|
|
|
local function capabilities_str_to_long_bitmap_array(str)
|
|
-- The format for capabilities is at include/linux/mod_devicetable.h.
|
|
-- They are long's split by spaces. See linux/drivers/input/input.c::input_print_bitmap.
|
|
local long_bitmap_arr = {}
|
|
for c in str:gmatch "([0-9a-fA-F]+)" do
|
|
local long_bitmap = tonumber(c, 16)
|
|
table.insert(long_bitmap_arr, 1, long_bitmap)
|
|
end
|
|
return long_bitmap_arr
|
|
end
|
|
|
|
local function count_set_bits_in_array(arr)
|
|
local count = 0
|
|
for __, number in ipairs(arr) do
|
|
local count_in_number = count_set_bits(number)
|
|
count = count + count_in_number
|
|
end
|
|
|
|
return count
|
|
end
|
|
|
|
local function is_capabilities_bit_set(long_bitmap_arr, bit_offset)
|
|
local long_bitsize = ffi.sizeof("long") * 8
|
|
local arr_index = math.floor(bit_offset / long_bitsize)
|
|
local long_mask = bit.lshift(1, bit_offset % long_bitsize)
|
|
local long_bitmap = long_bitmap_arr[arr_index + 1] -- Array index starts from 1 in Lua
|
|
|
|
if long_bitmap then
|
|
return bit.band(long_bitmap, long_mask) ~= 0
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function read_key_capabilities(sys_event_path)
|
|
local key_path = sys_event_path .. "/device/capabilities/key"
|
|
local file = io.open(key_path, "r")
|
|
if not file then
|
|
-- This should not happen - the kernel creates key capabilities file for all devices.
|
|
return nil
|
|
end
|
|
local keys_bitmap_str = file:read("l")
|
|
file:close()
|
|
|
|
return capabilities_str_to_long_bitmap_array(keys_bitmap_str)
|
|
end
|
|
|
|
local function analyze_key_capabilities(long_bitmap_arr)
|
|
-- The heuristic is that a keyboard has at least as many keys as there are alphabet letters and some more.
|
|
local keyboard_min_number_keys = 64
|
|
local keys_count = count_set_bits_in_array(long_bitmap_arr)
|
|
|
|
local is_keyboard = keys_count >= keyboard_min_number_keys
|
|
local has_dpad = is_capabilities_bit_set(long_bitmap_arr, KEY_UP) or
|
|
is_capabilities_bit_set(long_bitmap_arr, BTN_DPAD_UP)
|
|
|
|
return {
|
|
is_keyboard = is_keyboard,
|
|
has_dpad = has_dpad,
|
|
}
|
|
end
|
|
|
|
function FindKeyboard:find()
|
|
local keyboards = {}
|
|
for event_file_name in lfs.dir("/sys/class/input/") do
|
|
if event_file_name:match("event.*") then
|
|
local capabilities_long_bitmap_arr = read_key_capabilities("/sys/class/input/" .. event_file_name)
|
|
if capabilities_long_bitmap_arr then
|
|
local keyboard_info = analyze_key_capabilities(capabilities_long_bitmap_arr)
|
|
if keyboard_info.is_keyboard then
|
|
table.insert(keyboards, {
|
|
event_path = "/dev/input/" .. event_file_name,
|
|
has_dpad = keyboard_info.has_dpad
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return keyboards
|
|
end
|
|
|
|
return FindKeyboard
|