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
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 })
|