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.
269 lines
8.5 KiB
Lua
269 lines
8.5 KiB
Lua
local base = string.match(..., "(.-)[^%.]+$")
|
|
local bf = require(base.."helper.bitfield")
|
|
local decls = require(base.."ev_api.decls")
|
|
local errno = require(base.."helper.errno")
|
|
local eh = require(base.."helper.enum")
|
|
local ffi = require("ffi")
|
|
local mth = require(base.."helper.mt")
|
|
local pio = require(base.."helper.posix_io")
|
|
local ioh = require(base.."helper.ioctl_helper")
|
|
|
|
local bf_new_for_ioctl = bf.new_for_ioctl
|
|
local ffi_cast, ffi_errno = ffi.cast, ffi.errno
|
|
local enums, ioctls = decls.enums, decls.ioctls
|
|
local enum_to_int, enum_to_str = eh.to_int, eh.to_str
|
|
local e_assert, e_error = errno.assert, errno.error
|
|
|
|
-- ev_api modules
|
|
local absinfo = require(base.."ev_api.absinfo").new
|
|
local event = require(base.."ev_api.event")
|
|
local ff_effect = require(base.."ev_api.ff_effect").new
|
|
local id = require(base.."ev_api.id").new
|
|
local rs_new = require(base.."ev_api.repeat_settings").new
|
|
|
|
local setmetatable = setmetatable
|
|
|
|
local mt, index = {}, {}
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- IOCTL wrappers
|
|
|
|
index.get_driver_version = ioh.check_1(ioctls.get_version)
|
|
index.get_device_id = ioh.check_1_reswrap(ioctls.get_id, id)
|
|
|
|
-- repeat settings. need EV.REP in bits
|
|
local ioctl_get_rep = ioctls.get_rep
|
|
function index:get_repeat()
|
|
ffi_errno(0)
|
|
local res, delay, period = ioctl_get_rep(self.fd)
|
|
e_assert("ioctl", res == 0, 2)
|
|
return rs_new(delay, period)
|
|
end
|
|
index.get_rep = index.get_repeat -- repeat is a keyword in lua
|
|
|
|
local ioctl_set_rep = ioctls.set_rep
|
|
function index:set_repeat(...)
|
|
local r = rs_new(...)
|
|
ffi_errno(0)
|
|
e_assert("ioctl", ioctl_set_rep(self.fd, r.raw.delay, r.raw.period) == 0, 2)
|
|
end
|
|
index.set_rep = index.set_repeat
|
|
|
|
-- keycode
|
|
local KEY = enums.KEY
|
|
local ioctl_get_keycode = ioctls.get_keycode
|
|
local function get_keycode(proxy, scancode)
|
|
ffi_errno(0)
|
|
local res, kc = ioctl_get_keycode(proxy._instance.fd, scancode, 0)
|
|
e_assert("ioctl", res == 0, 2)
|
|
return enum_to_str(KEY, kc)
|
|
end
|
|
local ioctl_set_keycode = ioctls.set_keycode
|
|
local function set_keycode(proxy, scancode, keycode)
|
|
keycode = enum_to_int(KEY, keycode)
|
|
ffi_errno(0)
|
|
local res = ioctl_set_keycode(proxy._instance.fd, scancode, keycode)
|
|
e_assert("ioctl", res == 0, 2)
|
|
end
|
|
local keycode_mt = { __index = get_keycode, __newindex = set_keycode }
|
|
|
|
-- TODO: keycode V2
|
|
|
|
-- these can result in ENOENT when the specified property is not supported
|
|
index.get_name = ioh.string_prop(ioctls.get_name)
|
|
index.get_phys = ioh.string_prop(ioctls.get_phys)
|
|
index.get_uniq = ioh.string_prop(ioctls.get_uniq)
|
|
|
|
local function bitfield_prop(ioctl, enum, len)
|
|
len = len or enum.MAX + 1
|
|
return function(self)
|
|
local bf, buf, bytes = bf_new_for_ioctl(enum, len)
|
|
ffi_errno(0)
|
|
local res = ioctl(self.fd, buf, bytes)
|
|
e_assert("ioctl", res > 0 and res <= bytes, 2)
|
|
return bf
|
|
end
|
|
end
|
|
index.get_prop = bitfield_prop(ioctls.get_prop, enums.INPUT_PROP)
|
|
-- consistent naming with other bitfield getters
|
|
index.get_prop_bits = index.get_prop
|
|
|
|
-- mt slots
|
|
-- whoever came up with this API should be impaled on stick with AIDS.
|
|
-- You pass in the size of the struct in bytes, then the code will fill in the
|
|
-- first few values, then return 0. How many values did it fill? Looking at the
|
|
-- source, you'll neet to get the absinfo for ABS_MT_SLOT, take the maximum and
|
|
-- add one. Of course, whoever wrote this shit would surely have died suddenly
|
|
-- if his cockteaser wouldn't be so fucking lazy and would have returned the
|
|
-- fucking count already used in that dumbass function you nigger cocksucker
|
|
-- retard. Fuck you.
|
|
ffi.cdef[[
|
|
struct input_mt_request_layout {
|
|
__u32 code;
|
|
__s32 values[?];
|
|
};
|
|
]]
|
|
local mt_req_ct = ffi.typeof("struct input_mt_request_layout")
|
|
local ioctl_get_mtslots = ioctls.get_mtslots
|
|
local ABS = enums.ABS
|
|
function index:get_mt_slots(code, num)
|
|
num = num or self:get_mt_slot_count()
|
|
local req = mt_req_ct(num)
|
|
req.code = enum_to_int(ABS, code)
|
|
ffi_errno(0)
|
|
local res = ioctl_get_mtslots(self.fd, req)
|
|
e_assert("ioctl", res == 0, 2)
|
|
local tbl = {}
|
|
for i=1,num do tbl[i] = req.values[i-1] end
|
|
return tbl
|
|
end
|
|
|
|
-- state
|
|
index.get_key_state = bitfield_prop(ioctls.get_key, KEY, KEY.KEY_MAX+1)
|
|
index.get_led_state = bitfield_prop(ioctls.get_led, enums.LED)
|
|
index.get_snd_state = bitfield_prop(ioctls.get_snd, enums.SND)
|
|
index.get_sw_state = bitfield_prop(ioctls.get_sw, enums.SW)
|
|
|
|
local ioctl_get_bit = ioctls.get_bit
|
|
local inpmask = ffi.typeof("struct input_mask")
|
|
local ct_u64 = ffi.typeof("__u64")
|
|
local ioctl_get_mask, ioctl_set_mask = ioctls.get_mask, ioctls.set_mask
|
|
for _,x in ipairs{"EV", "KEY", "REL", "ABS", "MSC", "LED", "SND", "FF", "SW"} do
|
|
local xl = x:lower()
|
|
local enum = assert(enums[x])
|
|
-- 0 maps to EV_SYN, but you use that to get EV bits, because fuck logic
|
|
local id = assert(x == "EV" and 0 or enums.EV[x], x)
|
|
local len = (enum.MAX or enum.KEY_MAX) + 1
|
|
index["get_"..xl.."_bits"] = function(self)
|
|
local bf, buf, bytes = bf_new_for_ioctl(enum, len)
|
|
ffi_errno(0)
|
|
local res = ioctl_get_bit(self.fd, id, buf, bytes)
|
|
e_assert("ioctl", res > 0 and res <= bytes, 2)
|
|
return bf
|
|
end
|
|
|
|
-- also event mask here because it uses the same set of enums
|
|
index["get_"..xl.."_mask"] = function(self)
|
|
local bf, buf, bytes = bf_new_for_ioctl(enum, len)
|
|
local mask = inpmask(id, bytes, ffi_cast(ct_u64, buf))
|
|
ffi_errno(0)
|
|
e_assert("ioctl", ioctl_get_mask(self.fd, mask) == 0, 2)
|
|
return bf
|
|
end
|
|
|
|
index["set_"..xl.."_mask"] = function(self, bf)
|
|
local mask = inpmask(id, bf.bytes, ffi_cast(ct_u64, bf._buf))
|
|
ffi_errno(0)
|
|
e_assert("ioctl", ioctl_set_mask(self.fd, mask) == 0, 2)
|
|
end
|
|
end
|
|
|
|
-- absinfo
|
|
local ioctl_get_abs = ioctls.get_abs
|
|
local function get_absinfo(proxy, abs)
|
|
ffi_errno(0)
|
|
local res, info = ioctl_get_abs(proxy._instance.fd, enum_to_int(ABS, abs))
|
|
e_assert("ioctl", res == 0, 2)
|
|
return absinfo(info)
|
|
end
|
|
|
|
local ioctl_set_abs = ioctls.set_abs
|
|
local function set_absinfo(proxy, abs, ...)
|
|
local info = absinfo(...)
|
|
ffi_errno(0)
|
|
local res = ioctl_set_abs(proxy._instance.fd, enum_to_int(ABS, abs), info.raw)
|
|
e_assert("ioctl", res == 0, 2)
|
|
end
|
|
local absinfo_mt = { __index = get_absinfo, __newindex = set_absinfo }
|
|
|
|
local ABS_MT_SLOT = ABS.MT_SLOT
|
|
function index:get_mt_slot_count()
|
|
if not self.abs_bits["MT_SLOT"] then return 0 end
|
|
local res, info = ioctl_get_abs(self.fd, ABS_MT_SLOT)
|
|
e_assert("ioctl", res == 0, 2)
|
|
return info.maximum + 1
|
|
end
|
|
|
|
-- FF
|
|
local ioctl_set_ff = ioctls.set_ff
|
|
function index:add_ff(...)
|
|
local ff = ff_effect(...)
|
|
ffi_errno(0)
|
|
e_assert("ioctl", ioctl_set_ff(self.fd, ff.raw) == 0, 2)
|
|
return ff.id
|
|
end
|
|
|
|
index.remove_ff = ioh.check_0(ioctls.rm_ff)
|
|
index.get_ff_count = ioh.check_1(ioctls.get_effects)
|
|
|
|
-- misc
|
|
local ioctl_grab = ioctls.grab
|
|
local function set_grab(self, grab)
|
|
ffi_errno(0)
|
|
e_assert("ioctl", ioctl_grab(self.fd, grab and 1 or 0) == 0, 2)
|
|
end
|
|
index.set_grab = set_grab
|
|
function index:grab() set_grab(self, true) end
|
|
function index:ungrab() set_grab(self, false) end
|
|
|
|
local ioctl_revoke = ioctls.revoke
|
|
function index:revoke()
|
|
ffi_errno(0)
|
|
-- int parameter must be 0, otherwise kernel throws EINVAL
|
|
e_assert("ioctl", ioctl_revoke(self.fd, 0) == 0, 2)
|
|
end
|
|
|
|
local CLOCK = eh.make_enum{ {"REALTIME", 0}, {"MONOTONIC", 1}, {"BOOTTIME", 7} }
|
|
index.CLOCK = CLOCK
|
|
local ioctl_set_clockid = ioctls.set_clockid
|
|
function index:set_clockid(id)
|
|
ffi_errno(0)
|
|
e_assert("ioctl", ioctl_set_clockid(self.fd, CLOCK[id] or id) == 0, 2)
|
|
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
|
|
|
|
function index:play_ff(id, count)
|
|
self.io:write(event{type="FF", code=id, value=count or 1}.raw, ev_size)
|
|
end
|
|
|
|
local GAIN = enums.FF.GAIN
|
|
function index:set_ff_gain(gain)
|
|
if gain < 0 or gain > 0xffff then error("Invalid gain value "..gain, 2) end
|
|
self.io:write(event{type="FF", code=GAIN, value=gain}.raw, ev_size)
|
|
end
|
|
|
|
local AUTOCENTER = enums.FF.AUTOCENTER
|
|
function index:set_ff_autocenter(level)
|
|
if level < 0 or level > 0xffff then error("Invalid level value "..level, 2) end
|
|
self.io:write(event{type="FF", code=AUTOCENTER, value=level}.raw, ev_size)
|
|
end
|
|
|
|
|
|
local function new(fname)
|
|
local io = pio(fname)
|
|
local inst = {io=io, fd=io.fd}
|
|
inst.keycode = setmetatable({_instance=inst}, keycode_mt)
|
|
inst.absinfo = setmetatable({_instance=inst}, absinfo_mt)
|
|
return setmetatable(inst, mt)
|
|
end
|
|
index.new = new
|
|
|
|
mth.add_index_methods(mt, index)
|
|
return setmetatable(index, { __call = function(_, ...) return new(...) end })
|