Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Side by Side Diff: ui/events/keycodes/dom3/generate_dom_values.py

Issue 641753003: DOM Level 3 .code and .key value enumerations. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix macro case KeyX -> KEY_X Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Generate C++ header files for DOM keyboard event values.
7
8 Run this script with --help for a description of arguments.
9 """
10
11 import argparse
12 import csv
13 import sys
14
15 def CamelCaseToCMacro(s):
16 """Convert CamelCase as used by W3C to C_MACRO_STYLE as used by Chromium."""
17 r = ''
18 n = len(s)
19 for i in range(0, n):
20 if s[i].isupper() and i > 0 and (i + 1 == n or not s[i + 1].isupper()):
21 r += '_'
22 r += s[i].upper()
23 return r
24
25 def StringToCString(s):
26 """Converts to a C-style string (assuming no escapes required), or nullptr."""
27 if s is None:
28 return 'nullptr'
29 return '"' + s + '"'
30
31 class NameFields:
32 """Implements 'DomString[-EnumId[-Comment]]' syntax for DOM value columns."""
33 def __init__(self, name, delimiter='-'):
34 self.fields = name.split(delimiter)
35 def _field(self, index):
36 return self.fields[min(index, len(self.fields) - 1)]
37 def GetDomString(self):
38 s = self._field(0)
39 return s if (s or self._field(1)) else None
40 def GetEnumId(self, prefix=''):
41 s = self._field(1)
42 return (prefix + CamelCaseToCMacro(s)) if s else None
43
44 def IntOrNone(s):
45 if s == '-':
46 return None
47 return int(s, base=0)
48
49 def IsNotComment(line):
50 """Implements the input file comment line convention."""
51 return not line[0].isspace()
52
53 def SplitLine(line):
54 """Split the input line into a list of fields separated by white space."""
55 # Using csv.reader to split the line allows for escaping and quoting.
56 return csv.reader([line], delimiter=' ', skipinitialspace=True).next()
57
58 class Header:
59 """Wrap output in a C header file."""
60 def __init__(self, filename, comments, wrappers):
61 self.out = sys.stdout if filename == '-' else open(filename, 'wb')
62 for c in comments:
63 self.out.write('// ' + c + '\n')
64 guard = (''.join([c.upper() if c.isalpha() else '_' for c in filename]) +
65 '_').lstrip('_')
66 self.wrappers = [('#ifndef {0}\n#define {0}'.format(guard),
67 '#endif // {}'.format(guard))] + wrappers
68 def __enter__(self):
69 for begin, _ in self.wrappers:
70 self.out.write(begin + '\n')
71 return self.out
72 def __exit__(self, typ, value, traceback):
73 for _, end in reversed(self.wrappers):
74 self.out.write(end + '\n')
75 if self.out != sys.stdout:
76 self.out.close()
77
78 class CK:
79 """Things we do for both .code and .key."""
80 def __init__(self, name):
81 # Names of things.
82 self.name = name
83 self.enum = 'Dom' + name.capitalize()
84 self.macro = CamelCaseToCMacro(self.enum)
85 self.var = self.macro.lower()
86 self.basename = ''.join(filter(str.isalpha, list(self.var)))
87 # Accumulated data.
88 self.name_by_value = {}
89 self.id_by_value = {}
90 self.string_by_value = {}
91 self.id_by_string = {}
92 self.value_by_id = {}
93 # Generated files.
94 self.headers = {
95 'declaration': {
96 'argument': '--output_{}_declaration'.format(self.var),
97 'file': '{}.h'.format(self.basename),
98 'help': 'Enumeration of DOM Level 3 .{} values.'.format(name),
99 'wrap': [('namespace ui {', '} // namespace ui'),
100 ('enum class {} {{'.format(self.enum), '};')],
101 'generator': self.GenerateEnumValues
102 },
103 'table': {
104 'argument': '--output_{}_table'.format(self.var),
105 'file': '{}_table.h'.format(self.basename),
106 'help':
107 'Table of DOM Level 3 .{} strings and enum values.'.format(name),
108 'wrap': [('const DomKeyCodeEntry<{}> {}_table[] = {{'
109 .format(self.enum, self.var),
110 '};')],
111 'generator': self.GenerateTable
112 },
113 'strings': {
114 'argument': '--output_{}_string'.format(self.var),
115 'file': '{}_string.h'.format(self.basename),
116 'help':
117 'DOM .{} strings indexed by {} integer value.'.format(name, self.enum),
118 'wrap': [('const char* const {}_string[] = {{'.format(self.var), '};')],
119 'generator': self.GenerateStrings
120 }
121 }
122
123 def AddArguments(self, parser):
124 for h in self.headers.values():
125 h['arg'] = parser.add_argument(h['argument'],
126 default=h['file'],
127 help=h['help'])
128
129 def GenerateFiles(self, args, danger):
130 for h in self.headers.values():
131 with Header(vars(args)[h['arg'].dest],
132 [danger, h['help']],
133 h['wrap']) as out:
134 h['generator'](out)
135
136 def GenerateEnumDeclaration(self, out):
137 out.write('enum class {};\n'.format(self.enum))
138
139 def GenerateEnumValues(self, out):
140 for v, i in sorted(self.id_by_value.iteritems()):
141 out.write(' {} = 0x{:03X},'.format(i, v))
142 s = self.string_by_value.get(v)
143 if s:
144 out.write(' // ' + s)
145 out.write('\n')
146
147 def GenerateTable(self, out):
148 for s, i in sorted(self.id_by_string.iteritems()):
149 out.write(' {{{}, {}::{}}},\n'.format(StringToCString(s), self.enum, i))
150
151 def GenerateStrings(self, out):
152 for v in range(0, 1 + max(self.string_by_value)):
153 out.write(' /* 0x{:03X} */ {},\n'
154 .format(v, StringToCString(self.string_by_value.get(v))))
155 # out.write('#define MAX_{}_STRING_FOR_TEST 0x{:03X}\n'
156 # .format(self.macro, max(self.string_by_value)))
157 # out.write('#define NUM_{}_STRINGS_FOR_TEST 0x{:03X}\n'
158 # .format(self.macro, len(self.string_by_value)))
159
160 def AddEntry(self, value, name):
161 value = int(value, base=0)
162 identifier = NameFields(name).GetEnumId()
163 if identifier:
164 self.value_by_id[identifier] = value
165 self.name_by_value[value] = name
166 self.id_by_value[value] = identifier
167 string = NameFields(name).GetDomString()
168 if string is not None:
169 self.string_by_value[value] = string
170 self.id_by_string[string] = identifier
171
172 def Some(a, b):
173 return a if a is not None else b
174
175 def main(argv):
176 fail = 0
177 codes = CK('code')
178 keys = CK('key')
179
180 parser = argparse.ArgumentParser()
181 parser.add_argument('--input_dom', default='dom_values.txt')
182 parser.add_argument('--input_native', default='native_codes.txt')
183 codes.AddArguments(parser)
184 keys.AddArguments(parser)
185 parser.add_argument('--output_native', default='native_data.h')
186 args = parser.parse_args(argv[1:])
187
188 # Read the --input_dom file.
189 with open(args.input_dom, 'rb') as input_dom:
190 for line in filter(IsNotComment, input_dom):
191 column = SplitLine(line)
192 codes.AddEntry(column[0], column[1])
193 keys.AddEntry(column[0], column[2])
194
195 # Read the --input_native file into various dictionaries,
196 # checking it against --input_dom.
197 xkb_by_usb = {}
198 win_by_usb = {}
199 mac_by_usb = {}
200 code_by_usb = {}
201 usb_name_by_usb = {}
202 with open(args.input_native, 'rb') as input_native:
203 for line in filter(IsNotComment, input_native):
204 column = SplitLine(line)
205 usb = int(column[0], base=0)
206 xkb_by_usb[usb] = IntOrNone(column[1])
207 win_by_usb[usb] = IntOrNone(column[2])
208 mac_by_usb[usb] = IntOrNone(column[3])
209 if len(column) > 5:
210 usb_name_by_usb[usb] = column[5]
211 code_id = NameFields(column[4]).GetEnumId()
212 if code_id is None:
213 code_value = None
214 else:
215 code_value = codes.value_by_id.get(code_id, None)
216 if code_value is None:
217 fail = 1
218 sys.stderr.write('error: code {} in {} but not in {}\n'
219 .format(column[4],
220 args.input_native,
221 args.input_dom))
222 code_by_usb[usb] = code_value
223
224 # Write output files.
225 danger = ('DO NOT MODIFY. GENERATED BY ' + sys.argv[0] +
226 ' FROM ' + args.input_dom + '.')
227
228 codes.GenerateFiles(args, danger)
229 keys.GenerateFiles(args, danger)
230
231 with Header(args.output_native,
232 [danger, 'Table of native key codes.'],
233 [('const KeycodeMapEntry usb_keycode_map[] = {', '};')]) as out:
234 for usb, code in code_by_usb.iteritems():
235 out.write(' USB_KEYMAP(0x{:06X}, 0x{:04X}, 0x{:04X}, 0x{:04X}, {}::{}),'
236 .format(usb,
237 Some(xkb_by_usb[usb], xkb_by_usb[0]),
238 Some(win_by_usb[usb], win_by_usb[0]),
239 Some(mac_by_usb[usb], mac_by_usb[0]),
240 codes.enum,
241 codes.id_by_value.get(code, codes.id_by_value[0])))
242 if usb in usb_name_by_usb:
243 out.write(' // {}'.format(usb_name_by_usb[usb]))
244 out.write('\n')
245
246
247 return fail
248
249 if __name__ == '__main__':
250 sys.exit(main(sys.argv))
251
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698