inputor

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

gen_events.rb (5994B)


      1 #! /usr/bin/env ruby
      2 
      3 IF_VALS = {
      4   '(__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)' => false,
      5   'defined(__sparc__) && defined(__arch64__)' => false,
      6 }
      7 
      8 def replace defs, l
      9   l.gsub /[a-zA-Z_][a-zA-Z0-9_]*/ do |m|
     10     d = defs[m]
     11     d && !d[0] ? replace(defs, d[1]) : m
     12   end
     13 end
     14 
     15 def preproc defs, code, fn
     16   if_queue = [true]
     17   File.read(File.join '/usr/include', fn).gsub(%r{/\*.*?\*/}m, '').
     18     each_line do |l|
     19     l.gsub! /\s+\z/, ''
     20 
     21     if l =~ /\G\s*#else\s*\z/
     22       if_queue[-1] = !if_queue[-1]
     23       next
     24     elsif l =~ /\G\s*#endif\s*\z/
     25       if_queue.pop
     26       next
     27     elsif !if_queue.last && l =~ /\G\s*#if/
     28       if_queue << false
     29     end
     30     next unless if_queue.last
     31 
     32     if l =~ /\G\s*#ifndef\s+([a-zA-Z0-9_]+)\s*\z/
     33       if_queue << (if_queue.last && !defs[$1])
     34     elsif l =~ /\G\s*#if\s+(.*?)\s*\z/
     35       # TODO proper preprocessor
     36       fail "Unhandled if #{$1.inspect}" if IF_VALS[$1].nil?
     37       if_queue << IF_VALS[$1]
     38     elsif l =~ /\G\s*#define\s+([a-zA-Z0-9_]+)(?:\s*\(([a-zA-Z0-9_,\s]*)\))?(?:\s+(.+?))?\s*\z/
     39       defs[$1] = [$2, $3]
     40     elsif l =~ /\G\s*#include/
     41       next # ignore for now
     42     elsif l =~ /\G\s*#/
     43       fail "Unhandled preproc directive #{l}"
     44     elsif !l.empty?
     45       code << replace(defs, l)
     46       code << "\n"
     47     end
     48   end
     49 end
     50 
     51 defs = {}
     52 code = String.new
     53 preproc defs, code, 'linux/input-event-codes.h'
     54 preproc defs, code, 'linux/input.h'
     55 preproc defs, code, 'linux/uinput.h'
     56 
     57 enums = { 'KEY' => [], 'UINPUT' => [] }
     58 ENUM_BLACKLIST = %w(EV_VERSION FF_MAX_EFFECTS INPUT_KEYMAP_BY_INDEX).to_set
     59 defs.each do |name, (vars, val)|
     60   next if vars || val.nil? || name.match?(/[a-z]/) ||
     61           name.match?(/_(CNT)\z/) || val.match?(/\G_IO/) ||
     62           name.start_with?('UINPUT_') || ENUM_BLACKLIST.include?(name)
     63   val = replace defs, val
     64   val = Integer val
     65 
     66   if name =~ /\G(?:KEY|BTN)_/
     67     enums['KEY'] << [name, val]
     68   elsif name.start_with? 'UI_FF_'
     69     enums['UINPUT'] << [name[3..], val]
     70   else
     71     name =~ /\G(INPUT_PROP|MT_TOOL|FF_STATUS|[A-Z]+)_(.+)\z/ or
     72       fail "Bad define name #{name}"
     73     n = $2
     74     n.insert 0, '_' if n.match? /\G[0-9]/
     75     (enums[$1] ||= []) << [n, val]
     76   end
     77 end
     78 
     79 ioctls = String.new
     80 ctypes = {}
     81 def to_ctype ctypes, str
     82   mangled = 'ctype_' + str.gsub(/[^a-zA-Z0-9]/) {|c| '_%02x' % c.ord }
     83   ctypes[mangled] ||= "local #{mangled} = ffi.typeof(\"#{str}\")"
     84   mangled
     85 end
     86 
     87 def to_num str
     88   if str =~ /\G'(.)'\z/
     89     $1.ord
     90   else
     91     Integer str
     92   end
     93 end
     94 defs.each do |name, (vars, val)|
     95   next unless val&.match? /\G_IO/
     96   vars &&= vars.split(/\s*,\s*/).to_set
     97   val.gsub! /\G_IOC\(_IOC_READ,\s*/, '_IOR('
     98   name = name.gsub /\GEVIOC/, ''
     99   # try to do something with evdev's moronic creat() style naming convention
    100   name.gsub! /\GS/, 'SET_'
    101   name.gsub! /\GG(?!RAB)/, 'GET_'
    102   name.gsub! /\GRMFF/, 'RM_FF'
    103   name.downcase!
    104 
    105   has_len = vars&.include? 'len'
    106   nr_var = vars&.-(['len'])&.first
    107 
    108   val = replace defs, val
    109   m = val.match /\G_IO([RW]*)\((.*)\)\z/
    110   rd = m[1].include? 'R'
    111   wr = m[1].include? 'W'
    112   params = m[2].split(/\s*,\s*/)
    113   params[1] =~ /\G([0-9a-fx]+)(?:\s*\+\s*\(\s*#{Regexp.escape nr_var || ''}\s*\))?\s*\z/ or fail
    114   base_nr = Integer $1
    115   base_len = has_len || !params[2] ? 0 : "ffi.sizeof(\"#{params[2]}\")"
    116   ioctls << <<EOS
    117 local #{name}_req = ioctl_mod.ioctl_req(#{rd}, #{wr}, #{to_num params[0]}, #{base_nr}, #{base_len})
    118 EOS
    119 
    120   # random workarounds
    121   wr = true if name.start_with?('get_keycode') || name == 'get_mask'
    122   # this fucker needs a pointer
    123   params[2] = 'unsigned int[1]' if name == 'set_clockid'
    124 
    125   func_params = %w(fd)
    126   func_pre = nil
    127   req_num = "#{name}_req"
    128   req_param = nil
    129   res = %w(res)
    130   if nr_var
    131     func_params << nr_var
    132     req_num = "#{req_num} + lshift(#{nr_var}, nrshift)"
    133   end
    134   if has_len
    135     fail unless rd && !wr
    136     func_params += %w(arg len)
    137     func_pre = 'len = len or ffi_sizeof(arg)'
    138     req_param = "ffi_cast(#{to_ctype ctypes, 'void*'}, arg)"
    139     req_num = "#{req_num} + lshift(len, sizeshift)"
    140   elsif params[2] == 'char*'
    141     fail unless !rd && wr
    142     func_params << 'str'
    143     req_param = 'tostring(str)'
    144   elsif params[2]
    145     param_ioctl = params[2].dup
    146     unless param_ioctl.end_with?(']')
    147       if wr && param_ioctl.match?(/\Gstruct/)
    148         param_ioctl << '*'
    149       elsif rd
    150         param_ioctl << '[1]'
    151       end
    152     end
    153     ary_cnt = Integer param_ioctl.match(/\[([0-9]+)\]\z/)&.[](1) || 1
    154     req_param = 'arg'
    155 
    156     ctype = to_ctype ctypes, param_ioctl
    157     if wr
    158       ary_cnt.times {|i| func_params << "x#{i}" }
    159       func_pre = "local arg = #{ctype}(#{ary_cnt.times.map {|i| "x#{i}" }.join ', '})"
    160     else
    161       func_pre = "local arg = #{ctype}()"
    162       func_pre << "ffi.fill(arg, ffi.sizeof(#{ctype}), 42)" if ENV['DEBUG']
    163     end
    164     ary_cnt.times {|i| res << "arg[#{i}]" } if rd
    165   end
    166 
    167   ioctls << "function ioctls.#{name}(#{func_params.join ', '})\n"
    168   ioctls << "  #{func_pre}\n" if func_pre
    169   req_param &&= ", #{req_param}"
    170   ioctls << "  local res = ioctl(fd, #{req_num}#{req_param})\n"
    171   ioctls << "  return #{res.join ', '}\n"
    172   ioctls << "end\n"
    173 end
    174 
    175 puts <<EOS
    176 -- Auto generated file by gen_events.rb, do not edit!
    177 
    178 local base = string.match(..., "(.-)[^%.]+%.[^%.]+$")
    179 local bit = require("bit")
    180 local eh = require(base.."helper.enum")
    181 local ffi = require("ffi")
    182 local ioctl_mod = require(base.."helper.ioctl")
    183 
    184 local ioctl, nrshift, sizeshift = ioctl_mod.ioctl, ioctl_mod.nrshift, ioctl_mod.sizeshift
    185 local lshift = bit.lshift
    186 local ffi_cast, ffi_sizeof = ffi.cast, ffi.sizeof
    187 local tostring = tostring
    188 
    189 ffi.cdef[[
    190 typedef unsigned long __kernel_ulong_t;
    191 #{[8, 16, 32, 64].map do |b|
    192   "typedef int#{b}_t __s#{b}; typedef uint#{b}_t __u#{b};"
    193 end.join "\n"}
    194 #{code}]]
    195 local enums = {}
    196 EOS
    197 enums.each do |e, v|
    198   puts "enums.#{e} = eh.make_enum{"
    199   v.each {|n,v| puts "  {\"#{n}\", #{v}}," }
    200   puts '}'
    201 end
    202 puts <<EOS
    203 enums.EV_VERSION = #{defs['EV_VERSION'][1]}
    204 
    205 #{ctypes.values.join "\n"}
    206 local ioctls = {}
    207 #{ioctls}
    208 return {enums=enums, ioctls=ioctls}
    209 EOS