inputor

Evdev remapping utility in lua
git clone https://git.neptards.moe/u3shit/inputor.git
Log | Files | Refs

input.lua (8748B)


      1 local base = string.match(..., "(.-)[^%.]+$")
      2 local bf = require(base.."helper.bitfield")
      3 local decls = require(base.."ev_api.decls")
      4 local errno = require(base.."helper.errno")
      5 local eh = require(base.."helper.enum")
      6 local ffi = require("ffi")
      7 local mth = require(base.."helper.mt")
      8 local pio = require(base.."helper.posix_io")
      9 local ioh = require(base.."helper.ioctl_helper")
     10 
     11 local bf_new_for_ioctl = bf.new_for_ioctl
     12 local ffi_cast, ffi_errno = ffi.cast, ffi.errno
     13 local enums, ioctls = decls.enums, decls.ioctls
     14 local enum_to_int, enum_to_str = eh.to_int, eh.to_str
     15 local e_assert, e_error = errno.assert, errno.error
     16 
     17 -- ev_api modules
     18 local absinfo = require(base.."ev_api.absinfo").new
     19 local event = require(base.."ev_api.event")
     20 local ff_effect = require(base.."ev_api.ff_effect").new
     21 local id = require(base.."ev_api.id").new
     22 local rs_new = require(base.."ev_api.repeat_settings").new
     23 
     24 local setmetatable = setmetatable
     25 
     26 local mt, index = {}, {}
     27 
     28 --------------------------------------------------------------------------------
     29 -- IOCTL wrappers
     30 
     31 index.get_driver_version = ioh.check_1(ioctls.get_version)
     32 index.get_device_id = ioh.check_1_reswrap(ioctls.get_id, id)
     33 
     34 -- repeat settings. need EV.REP in bits
     35 local ioctl_get_rep = ioctls.get_rep
     36 function index:get_repeat()
     37   ffi_errno(0)
     38   local res, delay, period = ioctl_get_rep(self.fd)
     39   e_assert("ioctl", res == 0, 2)
     40   return rs_new(delay, period)
     41 end
     42 index.get_rep = index.get_repeat -- repeat is a keyword in lua
     43 
     44 local ioctl_set_rep = ioctls.set_rep
     45 function index:set_repeat(...)
     46   local r = rs_new(...)
     47   ffi_errno(0)
     48   e_assert("ioctl", ioctl_set_rep(self.fd, r.raw.delay, r.raw.period) == 0, 2)
     49 end
     50 index.set_rep = index.set_repeat
     51 
     52 -- keycode
     53 local KEY = enums.KEY
     54 local ioctl_get_keycode = ioctls.get_keycode
     55 local function get_keycode(proxy, scancode)
     56   ffi_errno(0)
     57   local res, kc = ioctl_get_keycode(proxy._instance.fd, scancode, 0)
     58   e_assert("ioctl", res == 0, 2)
     59   return enum_to_str(KEY, kc)
     60 end
     61 local ioctl_set_keycode = ioctls.set_keycode
     62 local function set_keycode(proxy, scancode, keycode)
     63   keycode = enum_to_int(KEY, keycode)
     64   ffi_errno(0)
     65   local res = ioctl_set_keycode(proxy._instance.fd, scancode, keycode)
     66   e_assert("ioctl", res == 0, 2)
     67 end
     68 local keycode_mt = { __index = get_keycode, __newindex = set_keycode }
     69 
     70 -- TODO: keycode V2
     71 
     72 -- these can result in ENOENT when the specified property is not supported
     73 index.get_name = ioh.string_prop(ioctls.get_name)
     74 index.get_phys = ioh.string_prop(ioctls.get_phys)
     75 index.get_uniq = ioh.string_prop(ioctls.get_uniq)
     76 
     77 local function bitfield_prop(ioctl, enum, len)
     78   len = len or enum.MAX + 1
     79   return function(self)
     80     local bf, buf, bytes = bf_new_for_ioctl(enum, len)
     81     ffi_errno(0)
     82     local res = ioctl(self.fd, buf, bytes)
     83     e_assert("ioctl", res > 0 and res <= bytes, 2)
     84     return bf
     85   end
     86 end
     87 index.get_prop = bitfield_prop(ioctls.get_prop, enums.INPUT_PROP)
     88 -- consistent naming with other bitfield getters
     89 index.get_prop_bits = index.get_prop
     90 
     91 -- mt slots
     92 -- whoever came up with this API should be impaled on stick with AIDS.
     93 -- You pass in the size of the struct in bytes, then the code will fill in the
     94 -- first few values, then return 0. How many values did it fill? Looking at the
     95 -- source, you'll neet to get the absinfo for ABS_MT_SLOT, take the maximum and
     96 -- add one. Of course, whoever wrote this shit would surely have died suddenly
     97 -- if his cockteaser wouldn't be so fucking lazy and would have returned the
     98 -- fucking count already used in that dumbass function you nigger cocksucker
     99 -- retard. Fuck you.
    100 ffi.cdef[[
    101 struct input_mt_request_layout {
    102   __u32 code;
    103   __s32 values[?];
    104 };
    105 ]]
    106 local mt_req_ct = ffi.typeof("struct input_mt_request_layout")
    107 local ioctl_get_mtslots = ioctls.get_mtslots
    108 local ABS = enums.ABS
    109 function index:get_mt_slots(code, num)
    110   num = num or self:get_mt_slot_count()
    111   local req = mt_req_ct(num)
    112   req.code = enum_to_int(ABS, code)
    113   ffi_errno(0)
    114   local res = ioctl_get_mtslots(self.fd, req)
    115   e_assert("ioctl", res == 0, 2)
    116   local tbl = {}
    117   for i=1,num do tbl[i] = req.values[i-1] end
    118   return tbl
    119 end
    120 
    121 -- state
    122 index.get_key_state = bitfield_prop(ioctls.get_key, KEY, KEY.KEY_MAX+1)
    123 index.get_led_state = bitfield_prop(ioctls.get_led, enums.LED)
    124 index.get_snd_state = bitfield_prop(ioctls.get_snd, enums.SND)
    125 index.get_sw_state = bitfield_prop(ioctls.get_sw, enums.SW)
    126 
    127 local ioctl_get_bit = ioctls.get_bit
    128 local inpmask = ffi.typeof("struct input_mask")
    129 local ct_u64 = ffi.typeof("__u64")
    130 local ioctl_get_mask, ioctl_set_mask = ioctls.get_mask, ioctls.set_mask
    131 for _,x in ipairs{"EV", "KEY", "REL", "ABS", "MSC", "LED", "SND", "FF", "SW"} do
    132   local xl = x:lower()
    133   local enum = assert(enums[x])
    134   -- 0 maps to EV_SYN, but you use that to get EV bits, because fuck logic
    135   local id = assert(x == "EV" and 0 or enums.EV[x], x)
    136   local len = (enum.MAX or enum.KEY_MAX) + 1
    137   index["get_"..xl.."_bits"] = function(self)
    138     local bf, buf, bytes = bf_new_for_ioctl(enum, len)
    139     ffi_errno(0)
    140     local res = ioctl_get_bit(self.fd, id, buf, bytes)
    141     e_assert("ioctl", res > 0 and res <= bytes, 2)
    142     return bf
    143   end
    144 
    145   -- also event mask here because it uses the same set of enums
    146   index["get_"..xl.."_mask"] = function(self)
    147     local bf, buf, bytes = bf_new_for_ioctl(enum, len)
    148     local mask = inpmask(id, bytes, ffi_cast(ct_u64, buf))
    149     ffi_errno(0)
    150     e_assert("ioctl", ioctl_get_mask(self.fd, mask) == 0, 2)
    151     return bf
    152   end
    153 
    154   index["set_"..xl.."_mask"] = function(self, bf)
    155     local mask = inpmask(id, bf.bytes, ffi_cast(ct_u64, bf._buf))
    156     ffi_errno(0)
    157     e_assert("ioctl", ioctl_set_mask(self.fd, mask) == 0, 2)
    158   end
    159 end
    160 
    161 -- absinfo
    162 local ioctl_get_abs = ioctls.get_abs
    163 local function get_absinfo(proxy, abs)
    164   ffi_errno(0)
    165   local res, info = ioctl_get_abs(proxy._instance.fd, enum_to_int(ABS, abs))
    166   e_assert("ioctl", res == 0, 2)
    167   return absinfo(info)
    168 end
    169 
    170 local ioctl_set_abs = ioctls.set_abs
    171 local function set_absinfo(proxy, abs, ...)
    172   local info = absinfo(...)
    173   ffi_errno(0)
    174   local res = ioctl_set_abs(proxy._instance.fd, enum_to_int(ABS, abs), info.raw)
    175   e_assert("ioctl", res == 0, 2)
    176 end
    177 local absinfo_mt = { __index = get_absinfo, __newindex = set_absinfo }
    178 
    179 local ABS_MT_SLOT = ABS.MT_SLOT
    180 function index:get_mt_slot_count()
    181   if not self.abs_bits["MT_SLOT"] then return 0 end
    182   local res, info = ioctl_get_abs(self.fd, ABS_MT_SLOT)
    183   e_assert("ioctl", res == 0, 2)
    184   return info.maximum + 1
    185 end
    186 
    187 -- FF
    188 local ioctl_set_ff = ioctls.set_ff
    189 function index:add_ff(...)
    190   local ff = ff_effect(...)
    191   ffi_errno(0)
    192   e_assert("ioctl", ioctl_set_ff(self.fd, ff.raw) == 0, 2)
    193   return ff.id
    194 end
    195 
    196 index.remove_ff = ioh.check_0(ioctls.rm_ff)
    197 index.get_ff_count = ioh.check_1(ioctls.get_effects)
    198 
    199 -- misc
    200 local ioctl_grab = ioctls.grab
    201 local function set_grab(self, grab)
    202   ffi_errno(0)
    203   e_assert("ioctl", ioctl_grab(self.fd, grab and 1 or 0) == 0, 2)
    204 end
    205 index.set_grab = set_grab
    206 function index:grab() set_grab(self, true) end
    207 function index:ungrab() set_grab(self, false) end
    208 
    209 local ioctl_revoke = ioctls.revoke
    210 function index:revoke()
    211   ffi_errno(0)
    212   -- int parameter must be 0, otherwise kernel throws EINVAL
    213   e_assert("ioctl", ioctl_revoke(self.fd, 0) == 0, 2)
    214 end
    215 
    216 local CLOCK = eh.make_enum{ {"REALTIME", 0}, {"MONOTONIC", 1}, {"BOOTTIME", 7} }
    217 index.CLOCK = CLOCK
    218 local ioctl_set_clockid = ioctls.set_clockid
    219 function index:set_clockid(id)
    220   ffi_errno(0)
    221   e_assert("ioctl", ioctl_set_clockid(self.fd, CLOCK[id] or id) == 0, 2)
    222 end
    223 
    224 --------------------------------------------------------------------------------
    225 -- MISC
    226 
    227 function index:close()
    228   self.io:close()
    229 end
    230 
    231 local ev_size = event.raw_struct_size
    232 function index:read()
    233   return event.new(self.io:read(ev_size))
    234 end
    235 
    236 function index:write(...)
    237   local ev = event(...)
    238   self.io:write(ev.raw, ev_size)
    239 end
    240 
    241 function index:play_ff(id, count)
    242   self.io:write(event{type="FF", code=id, value=count or 1}.raw, ev_size)
    243 end
    244 
    245 local GAIN = enums.FF.GAIN
    246 function index:set_ff_gain(gain)
    247   if gain < 0 or gain > 0xffff then error("Invalid gain value "..gain, 2) end
    248   self.io:write(event{type="FF", code=GAIN, value=gain}.raw, ev_size)
    249 end
    250 
    251 local AUTOCENTER = enums.FF.AUTOCENTER
    252 function index:set_ff_autocenter(level)
    253   if level < 0 or level > 0xffff then error("Invalid level value "..level, 2) end
    254   self.io:write(event{type="FF", code=AUTOCENTER, value=level}.raw, ev_size)
    255 end
    256 
    257 
    258 local function new(fname)
    259   local io = pio(fname)
    260   local inst = {io=io, fd=io.fd}
    261   inst.keycode = setmetatable({_instance=inst}, keycode_mt)
    262   inst.absinfo = setmetatable({_instance=inst}, absinfo_mt)
    263   return setmetatable(inst, mt)
    264 end
    265 index.new = new
    266 
    267 mth.add_index_methods(mt, index)
    268 return setmetatable(index, { __call = function(_, ...) return new(...) end })