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