2
0
mirror of https://github.com/koreader/koreader synced 2024-11-08 07:10:27 +00:00
koreader/plugins/externalkeyboard.koplugin/find-keyboard.lua
NiLuJe 9863a9c0bb ExternalKeyboard: Use the evdev number passed along by base to avoid sweeping the full list of input devices
This required some... creative thinking to avoid complexifying common
Input/UIManager codepaths ;p.
2022-11-02 03:48:39 +01:00

111 lines
3.3 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:check(event_file_name)
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
return {
event_path = "/dev/input/" .. event_file_name,
has_dpad = keyboard_info.has_dpad
}
end
end
return nil
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 kb = self:check(event_file_name)
if kb then
table.insert(keyboards, kb)
end
end
end
return keyboards
end
return FindKeyboard