Index: ui/events/keycodes/dom4/generate_dom_values.py |
diff --git a/ui/events/keycodes/dom4/generate_dom_values.py b/ui/events/keycodes/dom4/generate_dom_values.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..2c7dd64079c639947d0219e9245d26cb8d237595 |
--- /dev/null |
+++ b/ui/events/keycodes/dom4/generate_dom_values.py |
@@ -0,0 +1,308 @@ |
+#!/usr/bin/env python |
+# Copyright 2014 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""Generate C++ header files for DOM keyboard event values. |
+ |
+Run it like this: |
+ gen_key_codes.py \ |
+ --input_dom=dom_values.txt \ |
+ --input_native=native_codes.txt \ |
+ --output_dom_code_declaration=domcode.h \ |
+ --output_dom_code_definition=domcode_definition.h \ |
+ --output_dom_code_string=domcode_string.h \ |
+ --output_dom_code_table=domcode_table.h \ |
+ --output_dom_key_declaration=domkey.h \ |
+ --output_dom_key_string=domkey_string.h \ |
+ --output_dom_key_table=domkey_table.h \ |
+ --output_dom_key_definition=domkey_definition.h \ |
+ --output_native=native_data.h |
+ |
+Inputs: |
+ |
+ --input_dom |
+ Table of DOM Level 3 .code and .key values. |
+ |
+ --input_native |
+ Table of platform-native scan codes and corresponding DOM .code values. |
+ |
+Output: |
+ |
+ --output_dom_{code|key}_declaration |
+ Header containing 'enum class' forward declaration. |
+ |
+ --output_dom_{code|key}_definition |
+ Header containing enumeration values. |
+ |
+ --output_dom_{code|key}_string |
+ Table of DOM string values indexed by enumeration value. |
+ |
+ --output_dom_{code|key}_table |
+ Table of DOM string values and corresponding enumerators. |
+ |
+ --output_native |
+ Table of platform-native scan codes and corresponding |DomKey|s. |
+ |
+""" |
+ |
+import argparse |
+import csv |
+import sys |
+ |
+def CamelCaseToCMacro(s): |
+ """Convert CamelCase as used by W3C to C_MACRO_STYLE as used by Chromium.""" |
+ r = '' |
+ n = len(s) |
+ for i in range(0, n): |
+ if s[i].isupper() and i > 0 and i + 1 < n and not s[i + 1].isupper(): |
+ r += '_' |
+ r += s[i].upper() |
+ return r |
+ |
+def StringToCString(s): |
+ """Converts to a C-style string (assuming no escapes required), or nullptr.""" |
+ if s is None: |
+ return 'nullptr' |
+ return '"' + s + '"' |
+ |
+class NameFields: |
+ """Implements 'DomString[-EnumId[-Comment]]' syntax for DOM value columns.""" |
+ def __init__(self, name, delimiter='-'): |
+ self.fields = name.split(delimiter) |
+ def _field(self, index): |
+ return self.fields[min(index, len(self.fields) - 1)] |
+ def GetDomString(self): |
+ s = self._field(0) |
+ return s if (s or self._field(1)) else None |
+ def GetEnumId(self, prefix=''): |
+ s = self._field(1) |
+ return (prefix + CamelCaseToCMacro(s)) if s else None |
+ |
+def IntOrNone(s): |
+ if s == '-': |
+ return None |
+ return int(s, base=0) |
+ |
+def IsNotComment(line): |
+ """Implements the input file comment line convention.""" |
+ return not line[0].isspace() |
+ |
+def SplitLine(line): |
+ """Split the input line into a list of fields separated by white space.""" |
+ # Using csv.reader to split the line allows for escaping and quoting. |
+ return csv.reader([line], delimiter=' ', skipinitialspace=True).next() |
+ |
+class Header: |
+ """Wrap output in a C header file.""" |
+ def __init__(self, filename, comments, wrappers): |
+ self.out = sys.stdout if filename == '-' else open(filename, 'wb') |
+ for c in comments: |
+ self.out.write('// ' + c + '\n') |
+ guard = (''.join([c.upper() if c.isalpha() else '_' for c in filename]) + |
+ '_').lstrip('_') |
+ self.wrappers = [('#ifndef {0}\n#define {0}'.format(guard), |
+ '#endif // {}'.format(guard))] + wrappers |
+ def __enter__(self): |
+ for begin, _ in self.wrappers: |
+ self.out.write(begin + '\n') |
+ return self.out |
+ def __exit__(self, typ, value, traceback): |
+ for _, end in reversed(self.wrappers): |
+ self.out.write(end + '\n') |
+ if self.out != sys.stdout: |
+ self.out.close() |
+ |
+class CK: |
+ """Things we do for both .code and .key.""" |
+ def __init__(self, name): |
+ # Names of things. |
+ self.name = name |
+ self.enum = 'Dom' + name.capitalize() |
+ self.macro = CamelCaseToCMacro(self.enum) |
+ self.var = self.macro.lower() |
+ self.basename = ''.join(filter(str.isalpha, list(self.var))) |
+ # Accumulated data. |
+ self.name_by_value = {} |
+ self.id_by_value = {} |
+ self.string_by_value = {} |
+ self.id_by_string = {} |
+ self.value_by_id = {} |
+ # Generated files. |
+ self.headers = { |
+ 'declaration': { |
+ 'argument': '--output_{}_declaration'.format(self.var), |
+ 'file': '{}.h'.format(self.basename), |
+ 'help': 'Enumeration type for DOM Level 3 .{}.'.format(name), |
+ 'wrap': [('namespace ui {', '} // namespace ui'), |
+ ('enum {} {{'.format(self.enum), '};')], |
+ 'generator': self.GenerateEnumValues |
+# For C++11 enum class: |
+# 'wrap': [('namespace ui {', '} // namespace ui')], |
+# 'generator': self.GenerateEnumDeclaration |
+ }, |
+# For C++11 enum class: |
+# 'definition': { |
+# 'argument': '--output_{}_definition'.format(self.var), |
+# 'file': '{}_definition.h'.format(self.basename), |
+# 'help': 'Enumeration of DOM Level 3 .{} values.'.format(name), |
+# 'wrap': [('namespace ui {', '} // namespace ui'), |
+# ('enum class {} {{'.format(self.enum), '};')], |
+# 'generator': self.GenerateEnumValues |
+# }, |
+ 'table': { |
+ 'argument': '--output_{}_table'.format(self.var), |
+ 'file': '{}_table.h'.format(self.basename), |
+ 'help': |
+ 'Table of DOM Level 3 .{} strings and enum values.'.format(name), |
+ 'wrap': [('const DomKeyCodeEntry<{}> {}_table[] = {{' |
+ .format(self.enum, self.var), |
+ '};')], |
+ 'generator': self.GenerateTable |
+ }, |
+ 'strings': { |
+ 'argument': '--output_{}_string'.format(self.var), |
+ 'file': '{}_string.h'.format(self.basename), |
+ 'help': |
+ 'DOM .{} strings indexed by {} integer value.'.format(name, self.enum), |
+ 'wrap': [('const char* const {}_string[] = {{'.format(self.var), '};')], |
+ 'generator': self.GenerateStrings |
+ } |
+ } |
+ |
+ def AddArguments(self, parser): |
+ for h in self.headers.values(): |
+ h['arg'] = parser.add_argument(h['argument'], |
+ default=h['file'], |
+ help=h['help']) |
+ |
+ def GenerateFiles(self, args, danger): |
+ for h in self.headers.values(): |
+ with Header(vars(args)[h['arg'].dest], |
+ [danger, h['help']], |
+ h['wrap']) as out: |
+ h['generator'](out) |
+ |
+ def GenerateEnumDeclaration(self, out): |
+ out.write('enum class {};\n'.format(self.enum)) |
+ |
+ def GenerateEnumValues(self, out): |
+ for v, i in sorted(self.id_by_value.iteritems()): |
+ out.write(' {}_{} = 0x{:03X},'.format(self.macro, i, v)) |
+ # For C++11 enum class: |
+ # out.write(' {} = 0x{:03X},'.format(i, v)) |
+ s = self.string_by_value.get(v) |
+ if s: |
+ out.write(' // ' + s) |
+ out.write('\n') |
+ |
+ def GenerateTable(self, out): |
+ for s, i in sorted(self.id_by_string.iteritems()): |
+ out.write(' {{{}, {}_{}}},\n'.format(StringToCString(s), self.macro, i)) |
+ # For C++11 enum class: |
+ #out.write(' {{{}, {}::{}}},\n'.format(StringToCString(s), self.enum, i)) |
+ |
+ def GenerateStrings(self, out): |
+ for v in range(0, 1 + max(self.string_by_value)): |
+ out.write(' /* 0x{:03X} */ {},\n' |
+ .format(v, StringToCString(self.string_by_value.get(v)))) |
+# out.write('#define MAX_{}_STRING_FOR_TEST 0x{:03X}\n' |
+# .format(self.macro, max(self.string_by_value))) |
+# out.write('#define NUM_{}_STRINGS_FOR_TEST 0x{:03X}\n' |
+# .format(self.macro, len(self.string_by_value))) |
+ |
+ def AddEntry(self, value, name): |
+ value = int(value, base=0) |
+ identifier = NameFields(name).GetEnumId() |
+ if identifier: |
+ self.value_by_id[identifier] = value |
+ self.name_by_value[value] = name |
+ self.id_by_value[value] = identifier |
+ string = NameFields(name).GetDomString() |
+ if string is not None: |
+ self.string_by_value[value] = string |
+ self.id_by_string[string] = identifier |
+ |
+def Some(a, b): |
+ return a if a is not None else b |
+ |
+def main(argv): |
+ fail = 0 |
+ codes = CK('code') |
+ keys = CK('key') |
+ |
+ parser = argparse.ArgumentParser() |
+ parser.add_argument('--input_dom', default='dom_values.txt') |
+ parser.add_argument('--input_native', default='native_codes.txt') |
+ codes.AddArguments(parser) |
+ keys.AddArguments(parser) |
+ parser.add_argument('--output_native', default='native_data.h') |
+ args = parser.parse_args(argv[1:]) |
+ |
+ # Read the --input_dom file. |
+ with open(args.input_dom, 'rb') as input_dom: |
+ for line in filter(IsNotComment, input_dom): |
+ column = SplitLine(line) |
+ codes.AddEntry(column[0], column[1]) |
+ keys.AddEntry(column[0], column[2]) |
+ |
+ # Read the --input_native file into various dictionaries, |
+ # checking it against --input_dom. |
+ xkb_by_usb = {} |
+ win_by_usb = {} |
+ mac_by_usb = {} |
+ code_by_usb = {} |
+ usb_name_by_usb = {} |
+ with open(args.input_native, 'rb') as input_native: |
+ for line in filter(IsNotComment, input_native): |
+ column = SplitLine(line) |
+ usb = int(column[0], base=0) |
+ xkb_by_usb[usb] = IntOrNone(column[1]) |
+ win_by_usb[usb] = IntOrNone(column[2]) |
+ mac_by_usb[usb] = IntOrNone(column[3]) |
+ usb_name_by_usb[usb] = column[5] |
+ code_id = NameFields(column[4]).GetEnumId() |
+ if code_id is None: |
+ code_value = None |
+ else: |
+ code_value = codes.value_by_id.get(code_id, None) |
+ if code_value is None: |
+ fail = 1 |
+ sys.stderr.write('error: code {} in {} but not in {}\n' |
+ .format(column[4], |
+ args.input_native, |
+ args.input_dom)) |
+ code_by_usb[usb] = code_value |
+ |
+ # Write output files. |
+ danger = ('DO NOT MODIFY. GENERATED BY ' + sys.argv[0] + |
+ ' FROM ' + args.input_dom + '.') |
+ |
+ codes.GenerateFiles(args, danger) |
+ keys.GenerateFiles(args, danger) |
+ |
+ with Header(args.output_native, |
+ [danger, 'Table of native key codes.'], |
+ [('const KeycodeMapEntry usb_keycode_map[] = {', '};')]) as out: |
+ for usb, code in code_by_usb.iteritems(): |
+ # For C++11 enum class: |
+ #out.write(' USB_KEYMAP(0x{:06X}, 0x{:04X}, 0x{:04X}, 0x{:04X}, {}::{}),' |
+ out.write(' USB_KEYMAP(0x{:06X}, 0x{:04X}, 0x{:04X}, 0x{:04X}, {}_{}),' |
+ .format(usb, |
+ Some(xkb_by_usb[usb], xkb_by_usb[0]), |
+ Some(win_by_usb[usb], win_by_usb[0]), |
+ Some(mac_by_usb[usb], mac_by_usb[0]), |
+ codes.macro, |
+ # For C++11 enum class: |
+ # codes.enum, |
+ codes.id_by_value.get(code, codes.id_by_value[0]))) |
+ if usb_name_by_usb[usb]: |
+ out.write(' // {}'.format(usb_name_by_usb[usb])) |
+ out.write('\n') |
+ |
+ |
+ return fail |
+ |
+if __name__ == '__main__': |
+ sys.exit(main(sys.argv)) |
+ |