You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
5.3 KiB
Lua

local base = string.match(..., "(.-)[^%.]+$")
local eh = require(base.."helper.enum")
local errno = require(base.."helper.errno")
local ffi = require("ffi")
local decls = require(base.."ev_api.decls")
local ioh = require(base.."helper.ioctl_helper")
local mth = require(base.."helper.mt")
local pio = require(base.."helper.posix_io")
local ffi_errno = ffi.errno
local enum_to_int = eh.to_int
local enums, ioctls = decls.enums, decls.ioctls
local e_assert = errno.assert
-- ev_api modules
local absinfo = require(base.."ev_api.absinfo").new
local event = require(base.."ev_api.event")
local ff_erase = require(base.."ev_api.ff_erase").new
local ff_upload = require(base.."ev_api.ff_upload").new
local setup = require(base.."ev_api.setup").new
local select, type = select, type
local getmetatable, setmetatable = getmetatable, setmetatable
local mt, index = {}, {}
--------------------------------------------------------------------------------
-- IOCTL wrappers
index.create = ioh.check_0(ioctls.ui_dev_create)
index.destroy = ioh.check_0(ioctls.ui_dev_destroy)
-- general setup
index.setup = ioh.check_0_argwrap(ioctls.ui_dev_setup, setup)
local ioctl_abs_setup = ioctls.ui_abs_setup
local ct = ffi.typeof("struct uinput_abs_setup")
local ABS = enums.ABS
function index:abs_setup(abs, ...)
self:set_evbit("ABS")
local info = absinfo(...)
local arg = ct(enum_to_int(ABS, abs), info.raw)
ffi_errno(0)
e_assert("ioctl", ioctl_abs_setup(self.fd, arg) == 0, 2)
end
-- bits setup
for _,x in ipairs{"EV", "KEY", "REL", "ABS", "MSC", "LED", "SND", "FF", "SW", "PROP"} do
local ioctl = ioctls["ui_set_"..x:lower().."bit"]
local enum = assert(x == "PROP" and enums.INPUT_PROP or enums[x], x)
index["set_"..x:lower().."bit"] = function(self, bit)
bit = enum_to_int(enum, bit)
ffi_errno(0)
e_assert("ioctl", ioctl(self.fd, bit) == 0, 2)
end
end
-- phys
index.set_phys = ioh.check_0(ioctls.ui_set_phys)
-- FF
local function begin_func(ioctl, wrap)
return function(self, ...)
local arg
if select('#', ...) == 1 then
local x = ...
local t = type(x)
if t == "number" then
arg = wrap{request_id=x}
-- TODO something better
elseif t == "table" and x.struct_name == "input_event" then
arg = wrap{request_id=x.value}
end
end
if not arg then arg = wrap(...) end
ffi_errno(0)
e_assert("ioctl", ioctl(self.fd, arg.raw) == 0, 2)
return arg
end
end
index.begin_ff_upload = begin_func(ioctls.ui_begin_ff_upload, ff_upload)
index.end_ff_upload = ioh.check_0_argwrap(ioctls.ui_end_ff_upload, ff_upload)
index.begin_ff_erase = begin_func(ioctls.ui_begin_ff_erase, ff_erase)
index.end_ff_erase = ioh.check_0_argwrap(ioctls.ui_end_ff_erase, ff_erase)
-- misc
index.get_sysname = ioh.string_prop(ioctls.ui_get_sysname)
index.get_version = ioh.check_1(ioctls.ui_get_version)
--------------------------------------------------------------------------------
-- helpers
function index:copy_caps(inp)
local absinfo = inp.absinfo
for i in inp.abs_bits:i_members() do
self:abs_setup(i, absinfo[i])
end
-- skip abs, abs_setup did it automatically
for _,x in ipairs{"ev", "key", "rel", "msc", "led", "snd", "ff", "sw", "prop"} do
local setter = self["set_"..x.."bit"]
for i in inp[x.."_bits"]:i_members() do
setter(self, i)
end
end
end
-- ???: rep, scancode map
function index:add_keyboard_caps(skip_leds)
self:set_evbit("KEY")
for k,v in pairs(enums.KEY) do
if type(k) == "number" and v:sub(1, 4) == "KEY_" then
self:set_keybit(k)
end
end
if not skip_leds then
self:set_evbit("LED")
self:set_ledbit("NUML")
self:set_ledbit("CAPSL")
self:set_ledbit("SCROLLL")
end
end
function index:gen_ff_forwarder(inp)
local last_map = {}
local function write_if(ev)
local t = ev.type
local m = last_map[t]
if not m then m = {} last_map[t] = m end
local code = ev.code
local val = ev.value
if m[code] == val then return end
inp:write(ev)
m[code] = val
end
local ff_map = {}
return function()
local ev = self:read()
if ev.type == "UINPUT" then
local code = ev.code
if code == "FF_UPLOAD" then
local x = self:begin_ff_upload(ev)
-- TODO error handling
local old_id = x.effect.id
x.effect.id = ff_map[old_id] or -1
inp:add_ff(x.effect)
ff_map[old_id] = x.effect.id
x.retval = 0
self:end_ff_upload(x)
elseif code == "FF_ERASE" then
local x = self:begin_ff_erase(ev)
inp:remove_ff(ff_map[x.effect_id])
ff_map[x.effect_id] = nil
x.retval = 0
self:end_ff_erase(x)
end
elseif ev.type == "FF" then
local m = ff_map[ev.code]
if m then ev.code = m end
write_if(ev)
else
write_if(ev)
end
end
end
--------------------------------------------------------------------------------
-- misc
function index:close()
self.io:close()
end
local ev_size = event.raw_struct_size
function index:read()
return event.new(self.io:read(ev_size))
end
function index:write(...)
local ev = event(...)
self.io:write(ev.raw, ev_size)
end
local function new(fname)
local io = pio(fname or "/dev/uinput")
return setmetatable({io=io, fd=io.fd}, mt)
end
index.new = new
mth.add_index_methods(mt, index)
return setmetatable(index, { __call = function(_, ...) return new(...) end })