inputor

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

uinput.lua (5435B)


      1 local base = string.match(..., "(.-)[^%.]+$")
      2 local eh = require(base.."helper.enum")
      3 local errno = require(base.."helper.errno")
      4 local ffi = require("ffi")
      5 local decls = require(base.."ev_api.decls")
      6 local ioh = require(base.."helper.ioctl_helper")
      7 local mth = require(base.."helper.mt")
      8 local pio = require(base.."helper.posix_io")
      9 
     10 local ffi_errno = ffi.errno
     11 local enum_to_int = eh.to_int
     12 local enums, ioctls = decls.enums, decls.ioctls
     13 local e_assert = errno.assert
     14 
     15 -- ev_api modules
     16 local absinfo = require(base.."ev_api.absinfo").new
     17 local event = require(base.."ev_api.event")
     18 local ff_erase = require(base.."ev_api.ff_erase").new
     19 local ff_upload = require(base.."ev_api.ff_upload").new
     20 local setup = require(base.."ev_api.setup").new
     21 
     22 local select, type = select, type
     23 local getmetatable, setmetatable = getmetatable, setmetatable
     24 
     25 local mt, index = {}, {}
     26 
     27 --------------------------------------------------------------------------------
     28 -- IOCTL wrappers
     29 
     30 index.create = ioh.check_0(ioctls.ui_dev_create)
     31 index.destroy = ioh.check_0(ioctls.ui_dev_destroy)
     32 
     33 -- general setup
     34 index.setup = ioh.check_0_argwrap(ioctls.ui_dev_setup, setup)
     35 
     36 local ioctl_abs_setup = ioctls.ui_abs_setup
     37 local ct = ffi.typeof("struct uinput_abs_setup")
     38 local ABS = enums.ABS
     39 function index:abs_setup(abs, ...)
     40   self:set_evbit("ABS")
     41 
     42   local info = absinfo(...)
     43   local arg = ct(enum_to_int(ABS, abs), info.raw)
     44   ffi_errno(0)
     45   e_assert("ioctl", ioctl_abs_setup(self.fd, arg) == 0, 2)
     46 end
     47 
     48 -- bits setup
     49 for _,x in ipairs{"EV", "KEY", "REL", "ABS", "MSC", "LED", "SND", "FF", "SW", "PROP"} do
     50   local ioctl = ioctls["ui_set_"..x:lower().."bit"]
     51   local enum = assert(x == "PROP" and enums.INPUT_PROP or enums[x], x)
     52   index["set_"..x:lower().."bit"] = function(self, bit)
     53     bit = enum_to_int(enum, bit)
     54     ffi_errno(0)
     55     e_assert("ioctl", ioctl(self.fd, bit) == 0, 2)
     56   end
     57 end
     58 
     59 -- phys
     60 index.set_phys = ioh.check_0(ioctls.ui_set_phys)
     61 
     62 -- FF
     63 local function begin_func(ioctl, wrap)
     64   return function(self, ...)
     65     local arg
     66     if select('#', ...) == 1 then
     67       local x = ...
     68       local t = type(x)
     69       if t == "number" then
     70         arg = wrap{request_id=x}
     71       -- TODO something better
     72       elseif t == "table" and x.struct_name == "input_event" then
     73         arg = wrap{request_id=x.value}
     74       end
     75     end
     76     if not arg then arg = wrap(...) end
     77 
     78     ffi_errno(0)
     79     e_assert("ioctl", ioctl(self.fd, arg.raw) == 0, 2)
     80     return arg
     81   end
     82 end
     83 
     84 index.begin_ff_upload = begin_func(ioctls.ui_begin_ff_upload, ff_upload)
     85 index.end_ff_upload = ioh.check_0_argwrap(ioctls.ui_end_ff_upload, ff_upload)
     86 
     87 index.begin_ff_erase = begin_func(ioctls.ui_begin_ff_erase, ff_erase)
     88 index.end_ff_erase = ioh.check_0_argwrap(ioctls.ui_end_ff_erase, ff_erase)
     89 
     90 -- misc
     91 index.get_sysname = ioh.string_prop(ioctls.ui_get_sysname)
     92 index.get_version = ioh.check_1(ioctls.ui_get_version)
     93 
     94 --------------------------------------------------------------------------------
     95 -- helpers
     96 
     97 function index:copy_caps(inp)
     98   local absinfo = inp.absinfo
     99   for i in inp.abs_bits:i_members() do
    100     self:abs_setup(i, absinfo[i])
    101   end
    102 
    103   -- skip abs, abs_setup did it automatically
    104   for _,x in ipairs{"ev", "key", "rel", "msc", "led", "snd", "ff", "sw", "prop"} do
    105     local setter = self["set_"..x.."bit"]
    106     for i in inp[x.."_bits"]:i_members() do
    107       setter(self, i)
    108     end
    109   end
    110 end
    111 
    112 -- ???: rep, scancode map
    113 function index:add_keyboard_caps(skip_leds)
    114   self:set_evbit("KEY")
    115   for k,v in pairs(enums.KEY) do
    116     if type(k) == "number" and v:sub(1, 4) == "KEY_" then
    117       self:set_keybit(k)
    118     end
    119   end
    120 
    121   if not skip_leds then
    122     self:set_evbit("LED")
    123     self:set_ledbit("NUML")
    124     self:set_ledbit("CAPSL")
    125     self:set_ledbit("SCROLLL")
    126   end
    127 end
    128 
    129 function index:gen_ff_forwarder(inp)
    130   local last_map = {}
    131   local function write_if(ev)
    132     local t = ev.type
    133     local m = last_map[t]
    134     if not m then m = {} last_map[t] = m end
    135 
    136     local code = ev.code
    137     local val = ev.value
    138     if m[code] == val then return end
    139 
    140     inp:write(ev)
    141     m[code] = val
    142   end
    143 
    144   local ff_map = {}
    145   return function()
    146     local ev = self:read()
    147     if ev.type == "UINPUT" then
    148       local code = ev.code
    149       if code == "FF_UPLOAD" then
    150         local x = self:begin_ff_upload(ev)
    151         -- TODO error handling
    152         local old_id = x.effect.id
    153         x.effect.id = ff_map[old_id] or -1
    154         inp:add_ff(x.effect)
    155         ff_map[old_id] = x.effect.id
    156         x.retval = 0
    157         self:end_ff_upload(x)
    158       elseif code == "FF_ERASE" then
    159         local x = self:begin_ff_erase(ev)
    160         inp:remove_ff(ff_map[x.effect_id])
    161         ff_map[x.effect_id] = nil
    162         x.retval = 0
    163         self:end_ff_erase(x)
    164       end
    165     elseif ev.type == "FF" then
    166       local m = ff_map[ev.code]
    167       if m then ev.code = m end
    168       write_if(ev)
    169     else
    170       write_if(ev)
    171     end
    172   end
    173 end
    174 
    175 --------------------------------------------------------------------------------
    176 -- misc
    177 
    178 function index:close()
    179   self.io:close()
    180 end
    181 
    182 local ev_size = event.raw_struct_size
    183 function index:read()
    184   return event.new(self.io:read(ev_size))
    185 end
    186 
    187 function index:write(...)
    188   local ev = event(...)
    189   self.io:write(ev.raw, ev_size)
    190 end
    191 
    192 local function new(fname)
    193   local io = pio(fname or "/dev/uinput")
    194   return setmetatable({io=io, fd=io.fd}, mt)
    195 end
    196 index.new = new
    197 
    198 mth.add_index_methods(mt, index)
    199 return setmetatable(index, { __call = function(_, ...) return new(...) end })