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

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