| 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))
|
| +
|
|
|