mirror of
https://github.com/koreader/koreader
synced 2024-11-02 15:40:16 +00:00
9863a9c0bb
This required some... creative thinking to avoid complexifying common Input/UIManager codepaths ;p.
111 lines
3.3 KiB
Lua
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
|