Mercurial > louis > kiibohd-kll
changeset 2:ce7428de7787
Initial source dump.
- Not quite complete.
- Most of the parser is done (excluding analog) for 0.3 of the KLL spec
- Transformation and correlation isn't complete yet.
- Backend generation for Kiibohd capabilties is complete.
author | Jacob Alexander <haata@kiibohd.com> |
---|---|
date | Tue, 02 Sep 2014 10:03:50 -0700 |
parents | 32730e9c05f2 |
children | 409ddf71b86b |
files | .gitignore README backends/__init__.py backends/kiibohd.py funcparserlib/__init__.py funcparserlib/lexer.py funcparserlib/parser.py funcparserlib/util.py kll.py kll_lib/__init__.py kll_lib/containers.py kll_lib/hid_dict.py templateKeymap.h |
diffstat | 10 files changed, 1973 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gitignore Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,63 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so +*.elf +*.bin +*.hex +*.lss +*.sym +*.map + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Editor generated files # +########################## +*.swp + +# Source browsing files # +######################### +tags + +# CMake Generated Files # +######################### +CMakeFiles +CMakeCache.txt +cmake_install.cmake + +# Python Generated Files # +########################## +__pycache__/ +*.py[cod] +
--- a/README Tue Sep 02 09:55:21 2014 -0700 +++ b/README Tue Sep 02 10:03:50 2014 -0700 @@ -6,6 +6,8 @@ Most current version of the KLL spec: https://www.writelatex.com/read/zzqbdwqjfwwf Or visit http://kiibohd.com +Uses funcparserlib: https://code.google.com/p/funcparserlib/ + Usage -----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backends/kiibohd.py Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# KLL Compiler - Kiibohd Backend +# +# Backend code generator for the Kiibohd Controller firmware. +# +# Copyright (C) 2014 by Jacob Alexander +# +# This file is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this file. If not, see <http://www.gnu.org/licenses/>. + +### Imports ### + +import os +import sys +import re + +# Modifying Python Path, which is dumb, but the only way to import up one directory... +sys.path.append( os.path.expanduser('..') ) + +from kll_lib.containers import * + + +### Decorators ### + + ## Print Decorator Variables +ERROR = '\033[5;1;31mERROR\033[0m:' + + + +### Classes ### + +class Backend: + # Initializes backend + # Looks for template file and builds list of fill tags + def __init__( self, templatePath ): + # Does template exist? + if not os.path.isfile( templatePath ): + print ( "{0} '{1}' does not exist...".format( ERROR, templatePath ) ) + sys.exit( 1 ) + + self.templatePath = templatePath + self.fill_dict = dict() + + # Generate list of fill tags + self.tagList = [] + with open( templatePath, 'r' ) as openFile: + for line in openFile: + match = re.findall( '<\|([^|>]+)\|>', line ) + for item in match: + self.tagList.append( item ) + + + # Processes content for fill tags and does any needed dataset calculations + def process( self, capabilities ): + ## Capabilities ## + self.fill_dict['CapabilitiesList'] = "const Capability CapabilitiesList[] = {\n" + + # Keys are pre-sorted + for key in capabilities.keys(): + funcName = capabilities.funcName( key ) + argByteWidth = capabilities.totalArgBytes( key ) + self.fill_dict['CapabilitiesList'] += "\t{{ {0}, {1} }},\n".format( funcName, argByteWidth ) + + self.fill_dict['CapabilitiesList'] += "};" + + print( self.fill_dict['CapabilitiesList'] ) + + + # Generates the output keymap with fill tags filled + def generate( self, filepath ): + print("My path: {0}".format( filepath) ) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/funcparserlib/lexer.py Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2008/2013 Andrey Vlasovskikh +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__all__ = ['make_tokenizer', 'Token', 'LexerError'] + +import re + + +class LexerError(Exception): + def __init__(self, place, msg): + self.place = place + self.msg = msg + + def __str__(self): + s = u'cannot tokenize data' + line, pos = self.place + return u'%s: %d,%d: "%s"' % (s, line, pos, self.msg) + + +class Token(object): + def __init__(self, type, value, start=None, end=None): + self.type = type + self.value = value + self.start = start + self.end = end + + def __repr__(self): + return u'Token(%r, %r)' % (self.type, self.value) + + def __eq__(self, other): + # FIXME: Case sensitivity is assumed here + return self.type == other.type and self.value == other.value + + def _pos_str(self): + if self.start is None or self.end is None: + return '' + else: + sl, sp = self.start + el, ep = self.end + return u'%d,%d-%d,%d:' % (sl, sp, el, ep) + + def __str__(self): + s = u"%s %s '%s'" % (self._pos_str(), self.type, self.value) + return s.strip() + + @property + def name(self): + return self.value + + def pformat(self): + return u"%s %s '%s'" % (self._pos_str().ljust(20), + self.type.ljust(14), + self.value) + + +def make_tokenizer(specs): + """[(str, (str, int?))] -> (str -> Iterable(Token))""" + + def compile_spec(spec): + name, args = spec + return name, re.compile(*args) + + compiled = [compile_spec(s) for s in specs] + + def match_specs(specs, str, i, position): + line, pos = position + for type, regexp in specs: + m = regexp.match(str, i) + if m is not None: + value = m.group() + nls = value.count(u'\n') + n_line = line + nls + if nls == 0: + n_pos = pos + len(value) + else: + n_pos = len(value) - value.rfind(u'\n') - 1 + return Token(type, value, (line, pos + 1), (n_line, n_pos)) + else: + errline = str.splitlines()[line - 1] + raise LexerError((line, pos + 1), errline) + + def f(str): + length = len(str) + line, pos = 1, 0 + i = 0 + while i < length: + t = match_specs(compiled, str, i, (line, pos)) + yield t + line, pos = t.end + i += len(t.value) + + return f + +# This is an example of a token spec. See also [this article][1] for a +# discussion of searching for multiline comments using regexps (including `*?`). +# +# [1]: http://ostermiller.org/findcomment.html +_example_token_specs = [ + ('COMMENT', (r'\(\*(.|[\r\n])*?\*\)', re.MULTILINE)), + ('COMMENT', (r'\{(.|[\r\n])*?\}', re.MULTILINE)), + ('COMMENT', (r'//.*',)), + ('NL', (r'[\r\n]+',)), + ('SPACE', (r'[ \t\r\n]+',)), + ('NAME', (r'[A-Za-z_][A-Za-z_0-9]*',)), + ('REAL', (r'[0-9]+\.[0-9]*([Ee][+\-]?[0-9]+)*',)), + ('INT', (r'[0-9]+',)), + ('INT', (r'\$[0-9A-Fa-f]+',)), + ('OP', (r'(\.\.)|(<>)|(<=)|(>=)|(:=)|[;,=\(\):\[\]\.+\-<>\*/@\^]',)), + ('STRING', (r"'([^']|(''))*'",)), + ('CHAR', (r'#[0-9]+',)), + ('CHAR', (r'#\$[0-9A-Fa-f]+',)), +] +#tokenize = make_tokenizer(_example_token_specs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/funcparserlib/parser.py Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,409 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2008/2013 Andrey Vlasovskikh +# Small Python 3 modifications by Jacob Alexander 2014 +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""A recurisve descent parser library based on functional combinators. + +Basic combinators are taken from Harrison's book ["Introduction to Functional +Programming"][1] and translated from ML into Python. See also [a Russian +translation of the book][2]. + + [1]: http://www.cl.cam.ac.uk/teaching/Lectures/funprog-jrh-1996/ + [2]: http://code.google.com/p/funprog-ru/ + +A parser `p` is represented by a function of type: + + p :: Sequence(a), State -> (b, State) + +that takes as its input a sequence of tokens of arbitrary type `a` and a +current parsing state and return a pair of a parsed token of arbitrary type +`b` and the new parsing state. + +The parsing state includes the current position in the sequence being parsed and +the position of the rightmost token that has been consumed while parsing. + +Parser functions are wrapped into an object of the class `Parser`. This class +implements custom operators `+` for sequential composition of parsers, `|` for +choice composition, `>>` for transforming the result of parsing. The method +`Parser.parse` provides an easier way for invoking a parser hiding details +related to a parser state: + + Parser.parse :: Parser(a, b), Sequence(a) -> b + +Altough this module is able to deal with a sequences of any kind of objects, the +recommended way of using it is applying a parser to a `Sequence(Token)`. +`Token` objects are produced by a regexp-based tokenizer defined in +`funcparserlib.lexer`. By using it this way you get more readable parsing error +messages (as `Token` objects contain their position in the source file) and good +separation of lexical and syntactic levels of the grammar. See examples for more +info. + +Debug messages are emitted via a `logging.Logger` object named +`"funcparserlib"`. +""" + +__all__ = [ + 'some', 'a', 'many', 'pure', 'finished', 'maybe', 'skip', 'oneplus', + 'forward_decl', 'NoParseError', +] + +import logging + +log = logging.getLogger('funcparserlib') + +debug = False + + +class Parser(object): + """A wrapper around a parser function that defines some operators for parser + composition. + """ + + def __init__(self, p): + """Wraps a parser function p into an object.""" + self.define(p) + + def named(self, name): + """Specifies the name of the parser for more readable parsing log.""" + self.name = name + return self + + def define(self, p): + """Defines a parser wrapped into this object.""" + f = getattr(p, 'run', p) + if debug: + setattr(self, '_run', f) + else: + setattr(self, 'run', f) + self.named(getattr(p, 'name', p.__doc__)) + + def run(self, tokens, s): + """Sequence(a), State -> (b, State) + + Runs a parser wrapped into this object. + """ + if debug: + log.debug(u'trying %s' % self.name) + return self._run(tokens, s) + + def _run(self, tokens, s): + raise NotImplementedError(u'you must define() a parser') + + def parse(self, tokens): + """Sequence(a) -> b + + Applies the parser to a sequence of tokens producing a parsing result. + + It provides a way to invoke a parser hiding details related to the + parser state. Also it makes error messages more readable by specifying + the position of the rightmost token that has been reached. + """ + try: + (tree, _) = self.run(tokens, State()) + return tree + except NoParseError as e: + max = e.state.max + if len(tokens) > max: + tok = tokens[max] + else: + tok = u'<EOF>' + raise NoParseError(u'%s: %s' % (e.msg, tok), e.state) + + def __add__(self, other): + """Parser(a, b), Parser(a, c) -> Parser(a, _Tuple(b, c)) + + A sequential composition of parsers. + + NOTE: The real type of the parsed value isn't always such as specified. + Here we use dynamic typing for ignoring the tokens that are of no + interest to the user. Also we merge parsing results into a single _Tuple + unless the user explicitely prevents it. See also skip and >> + combinators. + """ + + def magic(v1, v2): + vs = [v for v in [v1, v2] if not isinstance(v, _Ignored)] + if len(vs) == 1: + return vs[0] + elif len(vs) == 2: + if isinstance(vs[0], _Tuple): + return _Tuple(v1 + (v2,)) + else: + return _Tuple(vs) + else: + return _Ignored(()) + + @Parser + def _add(tokens, s): + (v1, s2) = self.run(tokens, s) + (v2, s3) = other.run(tokens, s2) + return magic(v1, v2), s3 + + # or in terms of bind and pure: + # _add = self.bind(lambda x: other.bind(lambda y: pure(magic(x, y)))) + _add.name = u'(%s , %s)' % (self.name, other.name) + return _add + + def __or__(self, other): + """Parser(a, b), Parser(a, c) -> Parser(a, b or c) + + A choice composition of two parsers. + + NOTE: Here we are not providing the exact type of the result. In a + statically typed langage something like Either b c could be used. See + also + combinator. + """ + + @Parser + def _or(tokens, s): + try: + return self.run(tokens, s) + except NoParseError as e: + return other.run(tokens, State(s.pos, e.state.max)) + + _or.name = u'(%s | %s)' % (self.name, other.name) + return _or + + def __rshift__(self, f): + """Parser(a, b), (b -> c) -> Parser(a, c) + + Given a function from b to c, transforms a parser of b into a parser of + c. It is useful for transorming a parser value into another value for + making it a part of a parse tree or an AST. + + This combinator may be thought of as a functor from b -> c to Parser(a, + b) -> Parser(a, c). + """ + + @Parser + def _shift(tokens, s): + (v, s2) = self.run(tokens, s) + return f(v), s2 + + # or in terms of bind and pure: + # _shift = self.bind(lambda x: pure(f(x))) + _shift.name = u'(%s)' % (self.name,) + return _shift + + def bind(self, f): + """Parser(a, b), (b -> Parser(a, c)) -> Parser(a, c) + + NOTE: A monadic bind function. It is used internally to implement other + combinators. Functions bind and pure make the Parser a Monad. + """ + + @Parser + def _bind(tokens, s): + (v, s2) = self.run(tokens, s) + return f(v).run(tokens, s2) + + _bind.name = u'(%s >>=)' % (self.name,) + return _bind + + +class State(object): + """A parsing state that is maintained basically for error reporting. + + It consists of the current position pos in the sequence being parsed and + the position max of the rightmost token that has been consumed while + parsing. + """ + + def __init__(self, pos=0, max=0): + self.pos = pos + self.max = max + + def __str__(self): + return unicode((self.pos, self.max)) + + def __repr__(self): + return u'State(%r, %r)' % (self.pos, self.max) + + +class NoParseError(Exception): + def __init__(self, msg=u'', state=None): + self.msg = msg + self.state = state + + def __str__(self): + return self.msg + + +class _Tuple(tuple): + pass + + +class _Ignored(object): + def __init__(self, value): + self.value = value + + def __repr__(self): + return u'_Ignored(%s)' % repr(self.value) + + +@Parser +def finished(tokens, s): + """Parser(a, None) + + Throws an exception if any tokens are left in the input unparsed. + """ + if s.pos >= len(tokens): + return None, s + else: + raise NoParseError(u'should have reached <EOF>', s) + + +finished.name = u'finished' + + +def many(p): + """Parser(a, b) -> Parser(a, [b]) + + Returns a parser that infinitely applies the parser p to the input sequence + of tokens while it successfully parsers them. The resulting parser returns a + list of parsed values. + """ + + @Parser + def _many(tokens, s): + """Iterative implementation preventing the stack overflow.""" + res = [] + try: + while True: + (v, s) = p.run(tokens, s) + res.append(v) + except NoParseError as e: + return res, State(s.pos, e.state.max) + + _many.name = u'{ %s }' % p.name + return _many + + +def some(pred): + """(a -> bool) -> Parser(a, a) + + Returns a parser that parses a token if it satisfies a predicate pred. + """ + + @Parser + def _some(tokens, s): + if s.pos >= len(tokens): + raise NoParseError(u'no tokens left in the stream', s) + else: + t = tokens[s.pos] + if pred(t): + pos = s.pos + 1 + s2 = State(pos, max(pos, s.max)) + if debug: + log.debug(u'*matched* "%s", new state = %s' % (t, s2)) + return t, s2 + else: + if debug: + log.debug(u'failed "%s", state = %s' % (t, s)) + raise NoParseError(u'got unexpected token', s) + + _some.name = u'(some)' + return _some + + +def a(value): + """Eq(a) -> Parser(a, a) + + Returns a parser that parses a token that is equal to the value value. + """ + name = getattr(value, 'name', value) + return some(lambda t: t == value).named(u'(a "%s")' % (name,)) + + +def pure(x): + @Parser + def _pure(_, s): + return x, s + + _pure.name = u'(pure %r)' % (x,) + return _pure + + +def maybe(p): + """Parser(a, b) -> Parser(a, b or None) + + Returns a parser that retuns None if parsing fails. + + NOTE: In a statically typed language, the type Maybe b could be more + approprieate. + """ + return (p | pure(None)).named(u'[ %s ]' % (p.name,)) + + +def skip(p): + """Parser(a, b) -> Parser(a, _Ignored(b)) + + Returns a parser which results are ignored by the combinator +. It is useful + for throwing away elements of concrete syntax (e. g. ",", ";"). + """ + return p >> _Ignored + + +def oneplus(p): + """Parser(a, b) -> Parser(a, [b]) + + Returns a parser that applies the parser p one or more times. + """ + q = p + many(p) >> (lambda x: [x[0]] + x[1]) + return q.named(u'(%s , { %s })' % (p.name, p.name)) + + +def with_forward_decls(suspension): + """(None -> Parser(a, b)) -> Parser(a, b) + + Returns a parser that computes itself lazily as a result of the suspension + provided. It is needed when some parsers contain forward references to + parsers defined later and such references are cyclic. See examples for more + details. + """ + + @Parser + def f(tokens, s): + return suspension().run(tokens, s) + + return f + + +def forward_decl(): + """None -> Parser(?, ?) + + Returns an undefined parser that can be used as a forward declaration. You + will be able to define() it when all the parsers it depends on are + available. + """ + + @Parser + def f(tokens, s): + raise NotImplementedError(u'you must define() a forward_decl somewhere') + + return f + + +if __name__ == '__main__': + import doctest + doctest.testmod()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/funcparserlib/util.py Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2008/2013 Andrey Vlasovskikh +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +def pretty_tree(x, kids, show): + """(a, (a -> list(a)), (a -> str)) -> str + + Returns a pseudographic tree representation of x similar to the tree command + in Unix. + """ + (MID, END, CONT, LAST, ROOT) = (u'|-- ', u'`-- ', u'| ', u' ', u'') + + def rec(x, indent, sym): + line = indent + sym + show(x) + xs = kids(x) + if len(xs) == 0: + return line + else: + if sym == MID: + next_indent = indent + CONT + elif sym == ROOT: + next_indent = indent + ROOT + else: + next_indent = indent + LAST + syms = [MID] * (len(xs) - 1) + [END] + lines = [rec(x, next_indent, sym) for x, sym in zip(xs, syms)] + return u'\n'.join([line] + lines) + + return rec(x, u'', ROOT)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kll.py Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,505 @@ +#!/usr/bin/env python3 +# KLL Compiler +# Keyboard Layout Langauge +# +# Copyright (C) 2014 by Jacob Alexander +# +# This file is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this file. If not, see <http://www.gnu.org/licenses/>. + +### Imports ### + +import argparse +import io +import os +import re +import sys +import token +import importlib + +from tokenize import generate_tokens +from re import VERBOSE +from pprint import pformat + +from kll_lib.hid_dict import * +from kll_lib.containers import * + +from funcparserlib.lexer import make_tokenizer, Token, LexerError +from funcparserlib.parser import (some, a, many, oneplus, skip, finished, maybe, skip, forward_decl, NoParseError) + + + +### Decorators ### + + ## Print Decorator Variables +ERROR = '\033[5;1;31mERROR\033[0m:' + + + ## Python Text Formatting Fixer... + ## Because the creators of Python are averse to proper capitalization. +textFormatter_lookup = { + "usage: " : "Usage: ", + "optional arguments" : "Optional Arguments", +} + +def textFormatter_gettext( s ): + return textFormatter_lookup.get( s, s ) + +argparse._ = textFormatter_gettext + + + +### Argument Parsing ### + +def processCommandLineArgs(): + # Setup argument processor + pArgs = argparse.ArgumentParser( + usage="%(prog)s [options] <file1>...", + description="Generates .h file state tables and pointer indices from KLL .kll files.", + epilog="Example: {0} TODO".format( os.path.basename( sys.argv[0] ) ), + formatter_class=argparse.RawTextHelpFormatter, + add_help=False, +) + + # Positional Arguments + pArgs.add_argument( 'files', type=str, nargs='+', + help=argparse.SUPPRESS ) # Suppressed help output, because Python output is verbosely ugly + + # Optional Arguments + pArgs.add_argument( '-b', '--backend', type=str, default="kiibohd", + help="Specify target backend for the KLL compiler.\n" + "Default: kiibohd" ) + pArgs.add_argument( '-p', '--partial', type=str, nargs='+', action='append', + help="Specify .kll files to generate partial map, multiple files per flag.\n" + "Each -p defines another partial map.\n" + "Base .kll files (that define the scan code maps) must be defined for each partial map." ) + pArgs.add_argument( '-t', '--template', type=str, default="templateKeymap.h", + help="Specify template used to generate the keymap.\n" + "Default: templateKeymap.h" ) + pArgs.add_argument( '-o', '--output', type=str, default="templateKeymap.h", + help="Specify output file. Writes to current working directory by default.\n" + "Default: generatedKeymap.h" ) + pArgs.add_argument( '-h', '--help', action="help", + help="This message." ) + + # Process Arguments + args = pArgs.parse_args() + + # Parameters + defaultFiles = args.files + partialFileSets = args.partial + if partialFileSets is None: + partialFileSets = [[]] + + # Check file existance + for filename in defaultFiles: + if not os.path.isfile( filename ): + print ( "{0} {1} does not exist...".format( ERROR, filename ) ) + sys.exit( 1 ) + + for partial in partialFileSets: + for filename in partial: + if not os.path.isfile( filename ): + print ( "{0} {1} does not exist...".format( ERROR, filename ) ) + sys.exit( 1 ) + + return (defaultFiles, partialFileSets, args.backend, args.template, args.output) + + + +### Tokenizer ### + +def tokenize( string ): + """str -> Sequence(Token)""" + + # Basic Tokens Spec + specs = [ + ( 'Comment', ( r' *#.*', ) ), + ( 'Space', ( r'[ \t\r\n]+', ) ), + ( 'USBCode', ( r'U(("[^"]+")|(0x[0-9a-fA-F]+)|([0-9]+))', ) ), + ( 'USBCodeStart', ( r'U\[', ) ), + ( 'ScanCode', ( r'S((0x[0-9a-fA-F]+)|([0-9]+))', ) ), + ( 'ScanCodeStart', ( r'S\[', ) ), + ( 'CodeEnd', ( r'\]', ) ), + ( 'String', ( r'"[^"]*"', VERBOSE ) ), + ( 'SequenceString', ( r"'[^']*'", ) ), + ( 'Comma', ( r',', ) ), + ( 'Dash', ( r'-', ) ), + ( 'Plus', ( r'\+', ) ), + ( 'Operator', ( r'=>|:|=', ) ), + ( 'Parenthesis', ( r'\(|\)', ) ), + ( 'Number', ( r'-?(0x[0-9a-fA-F]+)|(0|([1-9][0-9]*))', VERBOSE ) ), + ( 'Name', ( r'[A-Za-z_][A-Za-z_0-9]*', ) ), + ( 'VariableContents', ( r'''[^"' ;:=>()]+''', ) ), + ( 'EndOfLine', ( r';', ) ), + ] + + # Tokens to filter out of the token stream + useless = ['Space', 'Comment'] + + tokens = make_tokenizer( specs ) + return [x for x in tokens( string ) if x.type not in useless] + + + +### Parsing ### + + ## Map Arrays +scanCode_map = [ None ] * 0xFF # Define 8 bit address width +usbCode_map = [ None ] * 0xFF # Define 8 bit address width +variable_dict = dict() +capabilities_dict = Capabilities() + + + ## Parsing Functions + +def make_scanCode( token ): + scanCode = int( token[1:], 0 ) + # Check size, to make sure it's valid + if scanCode > 0xFF: + print ( "{0} ScanCode value {1} is larger than 255".format( ERROR, scanCode ) ) + raise + return scanCode + +def make_usbCode( token ): + # If first character is a U, strip + if token[0] == "U": + token = token[1:] + + # If using string representation of USB Code, do lookup, case-insensitive + if '"' in token: + try: + usbCode = kll_hid_lookup_dictionary[ token[1:-1].upper() ] + except LookupError as err: + print ( "{0} {1} is an invalid USB Code Lookup...".format( ERROR, err ) ) + raise + else: + usbCode = int( token, 0 ) + + # Check size, to make sure it's valid + if usbCode > 0xFF: + print ( "{0} USBCode value {1} is larger than 255".format( ERROR, usbCode ) ) + raise + return usbCode + +def make_seqString( token ): + # Shifted Characters, and amount to move by to get non-shifted version + # US ANSI + shiftCharacters = ( + ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0x20 ), + ( "+", 0x12 ), + ( "&(", 0x11 ), + ( "!#$%<>", 0x10 ), + ( "*", 0x0E ), + ( ")", 0x07 ), + ( '"', 0x05 ), + ( ":", 0x01 ), + ( "^", -0x10 ), + ( "_", -0x18 ), + ( "{}|", -0x1E ), + ( "~", -0x20 ), + ( "@", -0x32 ), + ( "?", -0x38 ), + ) + + listOfLists = [] + shiftKey = kll_hid_lookup_dictionary["SHIFT"] + + # Creates a list of USB codes from the string: sequence (list) of combos (lists) + for char in token[1:-1]: + processedChar = char + + # Whether or not to create a combo for this sequence with a shift + shiftCombo = False + + # Depending on the ASCII character, convert to single character or Shift + character + for pair in shiftCharacters: + if char in pair[0]: + shiftCombo = True + processedChar = chr( ord( char ) + pair[1] ) + break + + # Do KLL HID Lookup on non-shifted character + # NOTE: Case-insensitive, which is why the shift must be pre-computed + usbCode = kll_hid_lookup_dictionary[ processedChar.upper() ] + + # Create Combo for this character, add shift key if shifted + charCombo = [] + if shiftCombo: + charCombo = [ [ shiftKey ] ] + charCombo.append( [ usbCode ] ) + + # Add to list of lists + listOfLists.append( charCombo ) + + return listOfLists + +def make_string( token ): + return token[1:-1] + +def make_number( token ): + return int( token, 0 ) + + # Range can go from high to low or low to high +def make_scanCode_range( rangeVals ): + start = rangeVals[0] + end = rangeVals[1] + + # Swap start, end if start is greater than end + if start > end: + start, end = end, start + + # Iterate from start to end, and generate the range + return list( range( start, end + 1 ) ) + + # Range can go from high to low or low to high + # Warn on 0-9 (as this does not do what one would expect) TODO + # Lookup USB HID tags and convert to a number +def make_usbCode_range( rangeVals ): + # Check if already integers + if isinstance( rangeVals[0], int ): + start = rangeVals[0] + else: + start = make_usbCode( rangeVals[0] ) + + if isinstance( rangeVals[1], int ): + end = rangeVals[1] + else: + end = make_usbCode( rangeVals[1] ) + + # Swap start, end if start is greater than end + if start > end: + start, end = end, start + + # Iterate from start to end, and generate the range + return list( range( start, end + 1 ) ) + pass + + + ## Base Rules + +const = lambda x: lambda _: x +unarg = lambda f: lambda x: f(*x) +flatten = lambda list: sum( list, [] ) + +tokenValue = lambda x: x.value +tokenType = lambda t: some( lambda x: x.type == t ) >> tokenValue +operator = lambda s: a( Token( 'Operator', s ) ) >> tokenValue +parenthesis = lambda s: a( Token( 'Parenthesis', s ) ) >> tokenValue +eol = a( Token( 'EndOfLine', ';' ) ) + +def listElem( item ): + return [ item ] + + # Flatten only the top layer (list of lists of ...) +def oneLayerFlatten( items ): + mainList = [] + for sublist in items: + for item in sublist: + mainList.append( item ) + + return mainList + + # Expand ranges of values in the 3rd dimension of the list, to a list of 2nd lists + # i.e. [ sequence, [ combo, [ range ] ] ] --> [ [ sequence, [ combo ] ], <option 2>, <option 3> ] +def optionExpansion( sequences ): + expandedSequences = [] + + # Total number of combinations of the sequence of combos that needs to be generated + totalCombinations = 1 + + # List of leaf lists, with number of leaves + maxLeafList = [] + + # Traverse to the leaf nodes, and count the items in each leaf list + for sequence in sequences: + for combo in sequence: + rangeLen = len( combo ) + totalCombinations *= rangeLen + maxLeafList.append( rangeLen ) + + # Counter list to keep track of which combination is being generated + curLeafList = [0] * len( maxLeafList ) + + # Generate a list of permuations of the sequence of combos + for count in range( 0, totalCombinations ): + expandedSequences.append( [] ) # Prepare list for adding the new combination + position = 0 + + # Traverse sequence of combos to generate permuation + for sequence in sequences: + expandedSequences[ -1 ].append( [] ) + for combo in sequence: + expandedSequences[ -1 ][ -1 ].append( combo[ curLeafList[ position ] ] ) + position += 1 + + # Increment combination tracker + for leaf in range( 0, len( curLeafList ) ): + curLeafList[ leaf ] += 1 + + # Reset this position, increment next position (if it exists), then stop + if curLeafList[ leaf ] >= maxLeafList[ leaf ]: + curLeafList[ leaf ] = 0 + if leaf + 1 < len( curLeafList ): + curLeafList[ leaf + 1 ] += 1 + break + + return expandedSequences + + + ## Evaluation Rules + +def eval_scanCode( trigger, result ): + # Convert to lists of lists of lists to tuples of tuples of tuples + trigger = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in trigger ) + result = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in result ) + + # Add to the base scanCode map, overwrite if already defined +# if scanCode_map[ trigger ] != None: +# print ( "ScanCodeMap - Replacing '{0}' with '{1}' -> {2}".format( scanCode_map[ trigger ], result, trigger ) ) +# scanCode_map[ trigger ] = result + +def eval_usbCode( trigger, result ): + # Check if trigger is list + + # Add to the base usbCode map, overwrite if already defined + if usbCode_map[ trigger ] != None: + print ( "USBCodeMap - Replacing '{0}' with '{1}' -> {2}".format( usbCode_map[ trigger ], result, trigger ) ) + usbCode_map[ trigger ] = result + print ( trigger ) + +def eval_variable( name, content ): + # Content might be a concatenation of multiple data types, convert everything into a single string + assigned_content = "" + for item in content: + assigned_content += str( item ) + + variable_dict[ name ] = assigned_content + +def eval_capability( name, function, args ): + capabilities_dict[ name ] = [ function, args ] + +map_scanCode = unarg( eval_scanCode ) +map_usbCode = unarg( eval_usbCode ) + +set_variable = unarg( eval_variable ) +set_capability = unarg( eval_capability ) + + + ## Sub Rules + +usbCode = tokenType('USBCode') >> make_usbCode +scanCode = tokenType('ScanCode') >> make_scanCode +name = tokenType('Name') +number = tokenType('Number') >> make_number +comma = tokenType('Comma') +dash = tokenType('Dash') +plus = tokenType('Plus') +content = tokenType('VariableContents') +string = tokenType('String') >> make_string +unString = tokenType('String') # When the double quotes are still needed for internal processing +seqString = tokenType('SequenceString') >> make_seqString + + # Code variants +code_end = tokenType('CodeEnd') + + # Scan Codes +scanCode_start = tokenType('ScanCodeStart') +scanCode_range = number + skip( dash ) + number >> make_scanCode_range +scanCode_listElem = number >> listElem +scanCode_innerList = oneplus( ( scanCode_range | scanCode_listElem ) + skip( maybe( comma ) ) ) >> flatten +scanCode_expanded = skip( scanCode_start ) + scanCode_innerList + skip( code_end ) +scanCode_elem = scanCode >> listElem +scanCode_combo = oneplus( ( scanCode_expanded | scanCode_elem ) + skip( maybe( plus ) ) ) +scanCode_sequence = oneplus( scanCode_combo + skip( maybe( comma ) ) ) + + # USB Codes +usbCode_start = tokenType('USBCodeStart') +usbCode_range = ( number | unString ) + skip( dash ) + ( number | unString ) >> make_usbCode_range +usbCode_listElemTag = unString >> make_usbCode +usbCode_listElem = ( number | usbCode_listElemTag ) >> listElem +usbCode_innerList = oneplus( ( usbCode_range | usbCode_listElem ) + skip( maybe( comma ) ) ) >> flatten +usbCode_expanded = skip( usbCode_start ) + usbCode_innerList + skip( code_end ) +usbCode_elem = usbCode >> listElem +usbCode_combo = oneplus( ( usbCode_expanded | usbCode_elem ) + skip( maybe( plus ) ) ) >> listElem +usbCode_sequence = oneplus( ( usbCode_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten + + # Capabilities +capFunc_arguments = number + skip( maybe( comma ) ) +capFunc_elem = name + skip( parenthesis('(') ) + many( capFunc_arguments ) + skip( parenthesis(')') ) >> listElem +capFunc_combo = oneplus( ( usbCode_expanded | usbCode_elem | capFunc_elem ) + skip( maybe( plus ) ) ) >> listElem +capFunc_sequence = oneplus( ( capFunc_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten + + # Trigger / Result Codes +triggerCode_outerList = scanCode_sequence >> optionExpansion +triggerUSBCode_outerList = usbCode_sequence >> optionExpansion +resultCode_outerList = capFunc_sequence >> optionExpansion + + + ## Main Rules + +#| <variable> = <variable contents>; +variable_contents = name | content | string | number | comma | dash +variable_expression = name + skip( operator('=') ) + oneplus( variable_contents ) + skip( eol ) >> set_variable + +#| <capability name> => <c function>; +capability_arguments = name + skip( operator(':') ) + number + skip( maybe( comma ) ) +capability_expression = name + skip( operator('=>') ) + name + skip( parenthesis('(') ) + many( capability_arguments ) + skip( parenthesis(')') ) + skip( eol ) >> set_capability + +#| <trigger> : <result>; +scanCode_expression = triggerCode_outerList + skip( operator(':') ) + resultCode_outerList + skip( eol ) >> map_scanCode +usbCode_expression = triggerUSBCode_outerList + skip( operator(':') ) + resultCode_outerList + skip( eol ) #>> map_usbCode + +def parse( tokenSequence ): + """Sequence(Token) -> object""" + + # Top-level Parser + expression = scanCode_expression | usbCode_expression | variable_expression | capability_expression + + kll_text = many( expression ) + kll_file = maybe( kll_text ) + skip( finished ) + + return kll_file.parse( tokenSequence ) + + + +### Main Entry Point ### + +if __name__ == '__main__': + (defaultFiles, partialFileSets, backend_name, template, output) = processCommandLineArgs() + + # Load backend module + global backend + backend_import = importlib.import_module( "backends.{0}".format( backend_name ) ) + backend = backend_import.Backend( template ) + + #TODO Move elsewhere + for filename in defaultFiles: + with open( filename ) as file: + data = file.read() + + tokenSequence = tokenize( data ) + print ( pformat( tokenSequence ) ) + tree = parse( tokenSequence ) + #print ( tree ) + #print ( scanCode_map ) + #print ( usbCode_map ) + print ( variable_dict ) + print ( capabilities_dict ) + + # TODO Move + backend.process( capabilities_dict ) + + # Successful Execution + sys.exit( 0 ) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kll_lib/containers.py Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# KLL Compiler Containers +# +# Copyright (C) 2014 by Jacob Alexander +# +# This file is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this file. If not, see <http://www.gnu.org/licenses/>. + +### Imports ### + + + +### Decorators ### + + ## Print Decorator Variables +ERROR = '\033[5;1;31mERROR\033[0m:' + + + +### Parsing ### + + ## Containers +class Capabilities: + # Container for capabilities dictionary and convenience functions + def __init__( self ): + self.capabilities = dict() + + def __getitem__( self, name ): + return self.capabilities[ name ] + + def __setitem__( self, name, contents ): + self.capabilities[ name ] = contents + + def __repr__( self ): + return "Capabilities => {0}\nIndexed Capabilities => {1}".format( self.capabilities, sorted( self.capabilities, key = self.capabilities.get ) ) + + + # Total bytes needed to store arguments + def totalArgBytes( self, name ): + totalBytes = 0 + + # Iterate over the arguments, summing the total bytes + for arg in self.capabilities[ name ][1]: + totalBytes += int( arg[1] ) + + return totalBytes + + # Name of the capability function + def funcName( self, name ): + return self.capabilities[ name ][0] + + + # Only valid while dictionary keys are not added/removed + def getIndex( self, name ): + return sorted( self.capabilities, key = self.capabilities.get ).index( name ) + + def getName( self, index ): + return sorted( self.capabilities, key = self.capabilities.get )[ index ] + + def keys( self ): + return sorted( self.capabilities, key = self.capabilities.get ) + + +class Macros: + # Container for Trigger Macro : Result Macro correlation + # Layer selection for generating TriggerLists + # + # Only convert USB Code list once all the ResultMacros have been accumulated (does a macro reduction; not reversible) + # Two staged list for ResultMacros: + # 1) USB Code/Non-converted (may contain capabilities) + # 2) Capabilities + def __init__( self ): + # Default layer (0) + self.layer = 0 + + # Macro Storage + self.macros = [ [] ] + + def setLayer( self, layer ): + self.layer = layer + + # Use for ScanCode trigger macros + def appendScanCode( self, trigger, result ): + self.macros[ self.layer ][ trigger ] = result + + # Use for USBCode trigger macros + # An extra lookup is required + def appendUSBCode( self, trigger, result ): + noSuccess = True + + for macro in self.macros[ self.layer ].keys(): + # USB Code Found + if trigger == self.macros[ self.layer ][ macro ]: + print ( "USBCode - Replacing '{0}' with '{1}' -> '{2}'".format( trigger, macro, result ) ) + self.macros[ self.layer ][ macro ] = result + noSuccess = False + + # Only show warning if no replacements were done + if noSuccess: + print ( "Warning: '{1}' USB Code not found in layer {1}".format( trigger, self.layer ) ) + return False + + return True +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kll_lib/hid_dict.py Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,502 @@ +#!/usr/bin/env python3 +# KLL Compiler - HID Dictionary Lookup +# +# USB Code Lookup Dictionary +# +# Copyright (C) 2014 by Jacob Alexander +# +# This file is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this file. If not, see <http://www.gnu.org/licenses/>. + +# Rather than generating tables of hex USB codes for the keymapping tables, readable defines are used (which correspond to usb_hid.h) +hid_lookup_dictionary = dict([ + ( 0x00, 'KEY_NOEVENT' ), # Event, not a physical key + ( 0x01, 'KEY_ERRORROLLOVER' ), # Event, not a physical key + ( 0x02, 'KEY_POSTFAIL' ), # Event, not a physical key + ( 0x03, 'KEY_ERRORUNDEFINED' ), # Event, not a physical key + ( 0x04, 'KEY_A' ), + ( 0x05, 'KEY_B' ), + ( 0x06, 'KEY_C' ), + ( 0x07, 'KEY_D' ), + ( 0x08, 'KEY_E' ), + ( 0x09, 'KEY_F' ), + ( 0x0A, 'KEY_G' ), + ( 0x0B, 'KEY_H' ), + ( 0x0C, 'KEY_I' ), + ( 0x0D, 'KEY_J' ), + ( 0x0E, 'KEY_K' ), + ( 0x0F, 'KEY_L' ), + ( 0x10, 'KEY_M' ), + ( 0x11, 'KEY_N' ), + ( 0x12, 'KEY_O' ), + ( 0x13, 'KEY_P' ), + ( 0x14, 'KEY_Q' ), + ( 0x15, 'KEY_R' ), + ( 0x16, 'KEY_S' ), + ( 0x17, 'KEY_T' ), + ( 0x18, 'KEY_U' ), + ( 0x19, 'KEY_V' ), + ( 0x1A, 'KEY_W' ), + ( 0x1B, 'KEY_X' ), + ( 0x1C, 'KEY_Y' ), + ( 0x1D, 'KEY_Z' ), + ( 0x1E, 'KEY_1' ), + ( 0x1F, 'KEY_2' ), + ( 0x20, 'KEY_3' ), + ( 0x21, 'KEY_4' ), + ( 0x22, 'KEY_5' ), + ( 0x23, 'KEY_6' ), + ( 0x24, 'KEY_7' ), + ( 0x25, 'KEY_8' ), + ( 0x26, 'KEY_9' ), + ( 0x27, 'KEY_0' ), + ( 0x28, 'KEY_ENTER' ), + ( 0x29, 'KEY_ESC' ), + ( 0x2A, 'KEY_BACKSPACE' ), + ( 0x2B, 'KEY_TAB' ), + ( 0x2C, 'KEY_SPACE' ), + ( 0x2D, 'KEY_MINUS' ), + ( 0x2E, 'KEY_EQUAL' ), + ( 0x2F, 'KEY_LEFT_BRACE' ), + ( 0x30, 'KEY_RIGHT_BRACE' ), + ( 0x31, 'KEY_BACKSLASH' ), + ( 0x32, 'KEY_NUMBER' ), + ( 0x33, 'KEY_SEMICOLON' ), + ( 0x34, 'KEY_QUOTE' ), + ( 0x35, 'KEY_BACKTICK' ), + ( 0x36, 'KEY_COMMA' ), + ( 0x37, 'KEY_PERIOD' ), + ( 0x38, 'KEY_SLASH' ), + ( 0x39, 'KEY_CAPS_LOCK' ), + ( 0x3A, 'KEY_F1' ), + ( 0x3B, 'KEY_F2' ), + ( 0x3C, 'KEY_F3' ), + ( 0x3D, 'KEY_F4' ), + ( 0x3E, 'KEY_F5' ), + ( 0x3F, 'KEY_F6' ), + ( 0x40, 'KEY_F7' ), + ( 0x41, 'KEY_F8' ), + ( 0x42, 'KEY_F9' ), + ( 0x43, 'KEY_F10' ), + ( 0x44, 'KEY_F11' ), + ( 0x45, 'KEY_F12' ), + ( 0x46, 'KEY_PRINTSCREEN' ), + ( 0x47, 'KEY_SCROLL_LOCK' ), + ( 0x48, 'KEY_PAUSE' ), + ( 0x49, 'KEY_INSERT' ), + ( 0x4A, 'KEY_HOME' ), + ( 0x4B, 'KEY_PAGE_UP' ), + ( 0x4C, 'KEY_DELETE' ), + ( 0x4D, 'KEY_END' ), + ( 0x4E, 'KEY_PAGE_DOWN' ), + ( 0x4F, 'KEY_RIGHT' ), + ( 0x50, 'KEY_LEFT' ), + ( 0x51, 'KEY_DOWN' ), + ( 0x52, 'KEY_UP' ), + ( 0x53, 'KEY_NUM_LOCK' ), + ( 0x54, 'KEYPAD_SLASH' ), + ( 0x55, 'KEYPAD_ASTERIX' ), + ( 0x56, 'KEYPAD_MINUS' ), + ( 0x57, 'KEYPAD_PLUS' ), + ( 0x58, 'KEYPAD_ENTER' ), + ( 0x59, 'KEYPAD_1' ), + ( 0x5A, 'KEYPAD_2' ), + ( 0x5B, 'KEYPAD_3' ), + ( 0x5C, 'KEYPAD_4' ), + ( 0x5D, 'KEYPAD_5' ), + ( 0x5E, 'KEYPAD_6' ), + ( 0x5F, 'KEYPAD_7' ), + ( 0x60, 'KEYPAD_8' ), + ( 0x61, 'KEYPAD_9' ), + ( 0x62, 'KEYPAD_0' ), + ( 0x63, 'KEYPAD_PERIOD' ), + ( 0x64, 'KEY_ISO_SLASH' ), + ( 0x65, 'KEY_APP' ), + ( 0x66, 'KEYBOARD_STATUS' ), # Used for indicating status or errors, not a key + ( 0x67, 'KEYPAD_EQUAL' ), + ( 0x68, 'KEY_F13' ), + ( 0x69, 'KEY_F14' ), + ( 0x6A, 'KEY_F15' ), + ( 0x6B, 'KEY_F16' ), + ( 0x6C, 'KEY_F17' ), + ( 0x6D, 'KEY_F18' ), + ( 0x6E, 'KEY_F19' ), + ( 0x6F, 'KEY_F20' ), + ( 0x70, 'KEY_F21' ), + ( 0x71, 'KEY_F22' ), + ( 0x72, 'KEY_F23' ), + ( 0x73, 'KEY_F24' ), + ( 0x74, 'KEY_EXEC' ), + ( 0x75, 'KEY_HELP' ), + ( 0x76, 'KEY_MENU' ), + ( 0x77, 'KEY_SELECT' ), + ( 0x78, 'KEY_STOP' ), + ( 0x79, 'KEY_AGAIN' ), + ( 0x7A, 'KEY_UNDO' ), + ( 0x7B, 'KEY_CUT' ), + ( 0x7C, 'KEY_COPY' ), + ( 0x7D, 'KEY_PASTE' ), + ( 0x7E, 'KEY_FIND' ), + ( 0x7F, 'KEY_MUTE' ), + ( 0x80, 'KEY_VOL_UP' ), + ( 0x81, 'KEY_VOL_DOWN' ), + ( 0x82, 'KEY_CAPS_TLOCK' ), # Toggle "Locking" Scroll Lock (Old keyboards with Locking Caps Lock) + ( 0x83, 'KEY_NUM_TLOCK' ), + ( 0x84, 'KEY_SCROLL_TLOCK' ), + ( 0x85, 'KEYPAD_COMMA' ), # Brazillian (See spec) + ( 0x86, 'KEYPAD_EQUAL_AS' ), # AS/400 Keyboard (See spec) + ( 0x87, 'KEY_INTER1' ), # KANJI1 - Brazillian and Japanese "Ru" and "-" + ( 0x88, 'KEY_INTER2' ), # KANJI2 - Japanese Katakana/Hiragana + ( 0x89, 'KEY_INTER3' ), # KANJI3 - Japanese Yen + ( 0x8A, 'KEY_INTER4' ), # KANJI4 - Japanese Henkan + ( 0x8B, 'KEY_INTER5' ), # KANJI5 - Japanese Muhenkan + ( 0x8C, 'KEY_INTER6' ), # KANJI6 - PC0x62 Comma (Ka-m-ma) + ( 0x8D, 'KEY_INTER7' ), # KANJI7 - Double-Byte/Single-Byte Toggle + ( 0x8E, 'KEY_INTER8' ), # KANJI8 - Undefined + ( 0x8F, 'KEY_INTER9' ), # KANJI9 - Undefined + ( 0x90, 'KEY_LANG1' ), # Korean Hangul/English Toggle + ( 0x91, 'KEY_LANG2' ), # Korean Hanja Conversion - Japanese Eisu + ( 0x92, 'KEY_LANG3' ), # Japanese Katakana Key (USB) + ( 0x93, 'KEY_LANG4' ), # Japanese Hiragana Key (USB) + ( 0x94, 'KEY_LANG5' ), # Japanese Zenkaku/Hankaku Key (USB) + ( 0x95, 'KEY_LANG6' ), # Reserved (Application Specific) + ( 0x96, 'KEY_LANG7' ), # Reserved (Application Specific) + ( 0x97, 'KEY_LANG8' ), # Reserved (Application Specific) + ( 0x98, 'KEY_LANG9' ), # Reserved (Application Specific) + ( 0x99, 'KEY_ALT_ERASE' ), # Special Erase (See Spec) + ( 0x9A, 'KEY_SYSREQ_ATT' ), # Modifier Type + ( 0x9B, 'KEY_CANCEL' ), + ( 0x9C, 'KEY_CLEAR' ), + ( 0x9D, 'KEY_PRIOR' ), + ( 0x9E, 'KEY_RETURN' ), + ( 0x9F, 'KEY_SEPARATOR' ), + ( 0xA0, 'KEY_OUT' ), + ( 0xA1, 'KEY_OPER' ), + ( 0xA2, 'KEY_CLEAR_AGAIN' ), + ( 0xA3, 'KEY_CRSEL_PROPS' ), + ( 0xA4, 'KEY_EXSEL' ), +# 0xA5 - 0xAF Reserved + ( 0xB0, 'KEYPAD_00' ), + ( 0xB1, 'KEYPAD_000' ), + ( 0xB2, 'KEY_1000_SEP' ), + ( 0xB3, 'KEY_DECIMAL_SEP' ), + ( 0xB4, 'KEY_CURRENCY_MAIN' ), + ( 0xB5, 'KEY_CURRENCY_SUB' ), + ( 0xB6, 'KEYPAD_LPAREN' ), + ( 0xB7, 'KEYPAD_RPAREN' ), + ( 0xB8, 'KEYPAD_LBRACE' ), + ( 0xB9, 'KEYPAD_RBRACE' ), + ( 0xBA, 'KEYPAD_TAB' ), + ( 0xBB, 'KEYPAD_BACKSPACE' ), + ( 0xBC, 'KEYPAD_A' ), + ( 0xBD, 'KEYPAD_B' ), + ( 0xBE, 'KEYPAD_C' ), + ( 0xBF, 'KEYPAD_D' ), + ( 0xC0, 'KEYPAD_E' ), + ( 0xC1, 'KEYPAD_F' ), + ( 0xC2, 'KEYPAD_XOR' ), + ( 0xC3, 'KEYPAD_CHEVRON' ), + ( 0xC4, 'KEYPAD_PERCENT' ), + ( 0xC5, 'KEYPAD_LTHAN' ), + ( 0xC6, 'KEYPAD_GTHAN' ), + ( 0xC7, 'KEYPAD_BITAND' ), + ( 0xC8, 'KEYPAD_AND' ), + ( 0xC9, 'KEYPAD_BITOR' ), + ( 0xCA, 'KEYPAD_OR' ), + ( 0xCB, 'KEYPAD_COLON' ), + ( 0xCC, 'KEYPAD_POUND' ), + ( 0xCD, 'KEYPAD_SPACE' ), + ( 0xCE, 'KEYPAD_AT' ), + ( 0xCF, 'KEYPAD_EXCLAIM' ), + ( 0xD0, 'KEYPAD_MEM_STORE' ), + ( 0xD1, 'KEYPAD_MEM_RECALL' ), + ( 0xD2, 'KEYPAD_MEM_CLEAR' ), + ( 0xD3, 'KEYPAD_MEM_ADD' ), + ( 0xD4, 'KEYPAD_MEM_SUB' ), + ( 0xD5, 'KEYPAD_MEM_MULT' ), + ( 0xD6, 'KEYPAD_MEM_DIV' ), + ( 0xD7, 'KEYPAD_PLUS_MINUS' ), + ( 0xD8, 'KEYPAD_CLEAR' ), + ( 0xD9, 'KEYPAD_CLEAR_ENTRY' ), + ( 0xDA, 'KEYPAD_BINARY' ), + ( 0xDB, 'KEYPAD_OCTAL' ), + ( 0xDC, 'KEYPAD_DECIMAL' ), + ( 0xDD, 'KEYPAD_HEX' ), +# 0xDE - 0xDF Reserved + ( 0xE0, 'KEY_LCTRL' ), + ( 0xE1, 'KEY_LSHIFT' ), + ( 0xE2, 'KEY_LALT' ), + ( 0xE3, 'KEY_LGUI' ), + ( 0xE4, 'KEY_RCTRL' ), + ( 0xE5, 'KEY_RSHIFT' ), + ( 0xE6, 'KEY_RALT' ), + ( 0xE7, 'KEY_RGUI' ), +# 0xE8 - 0xFFFF Reserved, using 0xF0 to 0xFF for function key placeholders + ( 0xF0, 'KEY_FUN1' ), + ( 0xF1, 'KEY_FUN2' ), + ( 0xF2, 'KEY_FUN3' ), + ( 0xF3, 'KEY_FUN4' ), + ( 0xF4, 'KEY_FUN5' ), + ( 0xF5, 'KEY_FUN6' ), + ( 0xF6, 'KEY_FUN7' ), + ( 0xF7, 'KEY_FUN8' ), + ( 0xF8, 'KEY_FUN9' ), + ( 0xF9, 'KEY_FUN10' ), + ( 0xFA, 'KEY_FUN11' ), + ( 0xFB, 'KEY_FUN12' ), + ( 0xFC, 'KEY_FUN13' ), + ( 0xFD, 'KEY_FUN14' ), + ( 0xFE, 'KEY_FUN15' ), + ( 0xFF, 'KEY_FUN16' ), +]) + + + +# Lookup for KLL defined HID values, internally the compiler uses numbers to combine the keymaps +kll_hid_lookup_dictionary = dict([ + ( 'A', 0x04 ), + ( 'B', 0x05 ), + ( 'C', 0x06 ), + ( 'D', 0x07 ), + ( 'E', 0x08 ), + ( 'F', 0x09 ), + ( 'G', 0x0A ), + ( 'H', 0x0B ), + ( 'I', 0x0C ), + ( 'J', 0x0D ), + ( 'K', 0x0E ), + ( 'L', 0x0F ), + ( 'M', 0x10 ), + ( 'N', 0x11 ), + ( 'O', 0x12 ), + ( 'P', 0x13 ), + ( 'Q', 0x14 ), + ( 'R', 0x15 ), + ( 'S', 0x16 ), + ( 'T', 0x17 ), + ( 'U', 0x18 ), + ( 'V', 0x19 ), + ( 'W', 0x1A ), + ( 'X', 0x1B ), + ( 'Y', 0x1C ), + ( 'Z', 0x1D ), + ( '1', 0x1E ), + ( '2', 0x1F ), + ( '3', 0x20 ), + ( '4', 0x21 ), + ( '5', 0x22 ), + ( '6', 0x23 ), + ( '7', 0x24 ), + ( '8', 0x25 ), + ( '9', 0x26 ), + ( '0', 0x27 ), + ( 'ENTER', 0x28 ), + ( 'ESC', 0x29 ), + ( 'BACKSPACE', 0x2A ), + ( 'TAB', 0x2B ), + ( 'SPACE', 0x2C ), + ( '-', 0x2D ), ( 'MINUS', 0x2D ), + ( '=', 0x2E ), ( 'EQUALS', 0x2E ), ( 'EQUAL', 0x2E ), + ( '{', 0x2F ), ( 'LEFT BRACE', 0x2F ), ( 'LBRACE', 0x2F ), + ( '}', 0x30 ), ( 'RIGHT BRACE', 0x30 ), ( 'RBRACE', 0x30 ), + ( '\\', 0x31 ), ( 'BACKSLASH', 0x31 ), + ( '#', 0x32 ), ( 'NUMBER', 0x32 ), ( 'HASH', 0x32 ), + ( ';', 0x33 ), ( 'SEMICOLON', 0x33 ), + ( "'", 0x34 ), ( 'QUOTE', 0x34 ), + ( '`', 0x35 ), ( 'BACKTICK', 0x35 ), + ( ',', 0x36 ), ( 'COMMA', 0x36 ), + ( '.', 0x37 ), ( 'PERIOD', 0x37 ), + ( '/', 0x38 ), ( 'SLASH', 0x38 ), + ( 'CAPSLOCK', 0x39 ), + ( 'F1', 0x3A ), + ( 'F2', 0x3B ), + ( 'F3', 0x3C ), + ( 'F4', 0x3D ), + ( 'F5', 0x3E ), + ( 'F6', 0x3F ), + ( 'F7', 0x40 ), + ( 'F8', 0x41 ), + ( 'F9', 0x42 ), + ( 'F10', 0x43 ), + ( 'F11', 0x44 ), + ( 'F12', 0x45 ), + ( 'PRINTSCREEN', 0x46 ), + ( 'SCROLLLOCK', 0x47 ), + ( 'PAUSE', 0x48 ), + ( 'INSERT', 0x49 ), + ( 'HOME', 0x4A ), + ( 'PAGEUP', 0x4B ), + ( 'DELETE', 0x4C ), + ( 'END', 0x4D ), + ( 'PAGEDOWN', 0x4E ), + ( 'RIGHT', 0x4F ), + ( 'LEFT', 0x50 ), + ( 'DOWN', 0x51 ), + ( 'UP', 0x52 ), + ( 'NUMLOCK', 0x53 ), + ( 'P/', 0x54 ), ( 'KEYPAD SLASH', 0x54 ), + ( 'P*', 0x55 ), ( 'KEYPAD ASTERIX', 0x55 ), + ( 'P-', 0x56 ), ( 'KEYPAD MINUS', 0x56 ), + ( 'P+', 0x57 ), ( 'KEYPAD PLUS', 0x57 ), + ( 'PENTER', 0x58 ), ( 'KEYPAD ENTER', 0x58 ), + ( 'P1', 0x59 ), ( 'KEYPAD 1', 0x59 ), + ( 'P2', 0x5A ), ( 'KEYPAD 2', 0x5A ), + ( 'P3', 0x5B ), ( 'KEYPAD 3', 0x5B ), + ( 'P4', 0x5C ), ( 'KEYPAD 4', 0x5C ), + ( 'P5', 0x5D ), ( 'KEYPAD 5', 0x5D ), + ( 'P6', 0x5E ), ( 'KEYPAD 6', 0x5E ), + ( 'P7', 0x5F ), ( 'KEYPAD 7', 0x5F ), + ( 'P8', 0x60 ), ( 'KEYPAD 8', 0x60 ), + ( 'P9', 0x61 ), ( 'KEYPAD 9', 0x61 ), + ( 'P0', 0x62 ), ( 'KEYPAD 0', 0x62 ), + ( 'P.', 0x63 ), ( 'KEYPAD PERIOD', 0x63 ), + ( 'ISO/', 0x64 ), ( 'ISO SLASH', 0x64 ), + ( 'APP', 0x65 ), + + ( 'P=', 0x67 ), ( 'KEYPAD EQUAL', 0x67 ), + ( 'F13', 0x68 ), + ( 'F14', 0x69 ), + ( 'F15', 0x6A ), + ( 'F16', 0x6B ), + ( 'F17', 0x6C ), + ( 'F18', 0x6D ), + ( 'F19', 0x6E ), + ( 'F20', 0x6F ), + ( 'F21', 0x70 ), + ( 'F22', 0x71 ), + ( 'F23', 0x72 ), + ( 'F24', 0x73 ), + ( 'EXEC', 0x74 ), + ( 'HELP', 0x75 ), + ( 'MENU', 0x76 ), + ( 'SELECT', 0x77 ), + ( 'STOP', 0x78 ), + ( 'AGAIN', 0x79 ), + ( 'UNDO', 0x7A ), + ( 'CUT', 0x7B ), + ( 'COPY', 0x7C ), + ( 'PASTE', 0x7D ), + ( 'FIND', 0x7E ), + ( 'MUTE', 0x7F ), + ( 'VOLUMEUP', 0x80 ), + ( 'VOLUMEDOWN', 0x81 ), + ( 'CAPSTOGGLELOCK', 0x82 ), + ( 'NUMTOGGLELOCK', 0x83 ), + ( 'SCROLLTOGGLELOCK', 0x84 ), + ( 'P,', 0x85 ), + ( 'KEYPAD AS400 EQUAL', 0x86 ), + ( 'INTER1', 0x87 ), ( 'KANJI1', 0x87 ), + ( 'INTER2', 0x88 ), ( 'KANJI2', 0x88 ), ( 'KANA', 0x88 ), + ( 'INTER3', 0x89 ), ( 'KANJI3', 0x89 ), ( 'YEN', 0x89 ), + ( 'INTER4', 0x8A ), ( 'KANJI4', 0x8A ), ( 'HENKAN', 0x8A ), + ( 'INTER5', 0x8B ), ( 'KANJI5', 0x8B ), ( 'MUHENKAN', 0x8B ), + ( 'INTER6', 0x8C ), ( 'KANJI6', 0x8C ), + ( 'INTER7', 0x8D ), ( 'KANJI7', 0x8D ), ( 'BYTETOGGLE', 0x8D ), + ( 'INTER8', 0x8E ), ( 'KANJI8', 0x8E ), + ( 'INTER9', 0x8F ), ( 'KANJI9', 0x8F ), + ( 'LANG1', 0x90 ), ( 'HANGULENGLISH', 0x90 ), + ( 'LANG2', 0x91 ), ( 'HANJA', 0x91 ), ( 'EISU', 0x91 ), + ( 'LANG3', 0x92 ), ( 'KATAKANA', 0x92 ), + ( 'LANG4', 0x93 ), ( 'HIRAGANA', 0x93 ), + ( 'LANG5', 0x94 ), ( 'ZENKAKUHANKAKU', 0x94 ), + ( 'LANG6', 0x95 ), + ( 'LANG7', 0x96 ), + ( 'LANG8', 0x97 ), + ( 'LANG9', 0x98 ), + ( 'ALTERASE', 0x99 ), + ( 'SYSREQATT', 0x9A ), + ( 'CANCEL', 0x9B ), + ( 'CLEAR', 0x9C ), + ( 'PRIOR', 0x9D ), + ( 'RETURN', 0x9E ), + ( 'SEP', 0x9F ), ( 'SEPARATOR', 0x9F ), + ( 'OUT', 0xA0 ), + ( 'OPER', 0xA1 ), + ( 'CLEAR_AGAIN', 0xA2 ), + ( 'CRSEL_PROPS', 0xA3 ), + ( 'EXSEL', 0xA4 ), + + ( 'P00', 0xB0 ), ( 'KEYPAD 00', 0xB0 ), + ( 'P000', 0xB1 ), ( 'KEYPAD 000', 0xB1 ), + ( '1000SEP', 0xB2 ), ( 'THOUSANDSEPARATOR', 0xB2 ), + ( 'DECIMALSEP', 0xB3 ), ( 'DECIMALSEPARATOR', 0xB3 ), + ( 'CURRENCY', 0xB4 ), ( 'CURRENCYUNIT', 0xB4 ), + ( 'CURRENCYSUB', 0xB5 ), ( 'CURRENCYSUBUNIT', 0xB5 ), + ( 'P(', 0xB6 ), ( 'KEYPAD LEFT PARENTHESES', 0xB6 ), + ( 'P)', 0xB7 ), ( 'KEYPAD RIGHT PARENTHESES', 0xB7 ), + ( 'P{', 0xB8 ), ( 'KEYPAD LEFT BRACE', 0xB8 ), + ( 'P}', 0xB9 ), ( 'KEYPAD RIGHT BRACE', 0xB9 ), + ( 'PTAB', 0xBA ), ( 'KEYPAD TAB', 0xBA ), + ( 'PBACKSPACE', 0xBB ), ( 'KEYPAD BACKSPACE', 0xBB ), + ( 'PA', 0xBC ), ( 'KEYPAD A', 0xBC ), + ( 'PB', 0xBD ), ( 'KEYPAD B', 0xBD ), + ( 'PC', 0xBE ), ( 'KEYPAD C', 0xBE ), + ( 'PD', 0xBF ), ( 'KEYPAD D', 0xBF ), + ( 'PE', 0xC0 ), ( 'KEYPAD E', 0xC0 ), + ( 'PF', 0xC1 ), ( 'KEYPAD F', 0xC1 ), + ( 'PXOR', 0xC2 ), ( 'KEYPAD XOR', 0xC2 ), + ( 'P^', 0xC3 ), ( 'KEYPAD CHEVRON', 0xC3 ), + ( 'P%', 0xC4 ), ( 'KEYPAD PERCENT', 0xC4 ), + ( 'P<', 0xC5 ), ( 'KEYPAD LESSTHAN', 0xC5 ), + ( 'P>', 0xC6 ), ( 'KEYPAD GREATERTHAN', 0xC6 ), + ( 'P&', 0xC7 ), ( 'KEYPAD BITAND', 0xC7 ), + ( 'P&&', 0xC8 ), ( 'KEYPAD AND', 0xC8 ), + ( 'P|', 0xC9 ), ( 'KEYPAD BITOR', 0xC9 ), + ( 'P||', 0xCA ), ( 'KEYPAD OR', 0xCA ), + ( 'P:', 0xCB ), ( 'KEYPAD COLON', 0xCB ), + ( 'P#', 0xCC ), ( 'KEYPAD NUMBER', 0xCC ), ( 'KEYPAD HASH', 0xCC ), + ( 'PSPACE', 0xCD ), ( 'KEYPAD SPACE', 0xCD ), + ( 'P@', 0xCE ), ( 'KEYPAD AT', 0xCE ), + ( 'P!', 0xCF ), ( 'KEYPAD EXCLAIM', 0xCF ), + ( 'PMEMSTORE', 0xD0 ), ( 'KEYPAD MEMSTORE', 0xD0 ), + ( 'PMEMRECALL', 0xD1 ), ( 'KEYPAD MEMRECALL', 0xD1 ), + ( 'PMEMCLEAR', 0xD2 ), ( 'KEYPAD MEMCLEAR', 0xD2 ), + ( 'PMEMADD', 0xD3 ), ( 'KEYPAD MEMADD', 0xD3 ), + ( 'PMEMSUB', 0xD4 ), ( 'KEYPAD MEMSUB', 0xD4 ), + ( 'PMEMMULT', 0xD5 ), ( 'KEYPAD MEMMULT', 0xD5 ), + ( 'PMEMDIV', 0xD6 ), ( 'KEYPAD MEMDIV', 0xD6 ), + ( 'P+/-', 0xD7 ), ( 'KEYPAD PLUSMINUS', 0xD7 ), + ( 'PCLEAR', 0xD8 ), ( 'KEYPAD CLEAR', 0xD8 ), + ( 'PCLEARENTRY', 0xD9 ), ( 'KEYPAD CLEARENTRY', 0xD9 ), + ( 'PBINARY', 0xDA ), ( 'KEYPAD BINARY', 0xDA ), + ( 'POCTAL', 0xDB ), ( 'KEYPAD OCTAL', 0xDB ), + ( 'PDECIMAL', 0xDC ), ( 'KEYPAD DECIMAL', 0xDC ), + ( 'PHEX', 0xDD ), ( 'KEYPAD HEX', 0xDD ), + + ( 'LCTRL', 0xE0 ), ( 'LEFT CTRL', 0xE0 ), ( 'CTRL', 0xE0 ), + ( 'LSHIFT', 0xE1 ), ( 'LEFT SHIFT', 0xE1 ), ( 'SHIFT', 0xE1 ), + ( 'LALT', 0xE2 ), ( 'LEFT ALT', 0xE2 ), ( 'ALT', 0xE2 ), + ( 'LGUI', 0xE3 ), ( 'LEFT GUI', 0xE3 ), ( 'GUI', 0xE3 ), + ( 'RCTRL', 0xE4 ), ( 'RIGHT CTRL', 0xE4 ), + ( 'RSHIFT', 0xE5 ), ( 'RIGHT SHIFT', 0xE5 ), + ( 'RALT', 0xE6 ), ( 'RIGHT ALT', 0xE6 ), + ( 'RGUI', 0xE7 ), ( 'RIGHT GUI', 0xE7 ), + + ( 'FUN1', 0xF0 ), ( 'FUNCTION1', 0xF0 ), ( 'FUN', 0xF0 ), + ( 'FUN2', 0xF1 ), ( 'FUNCTION2', 0xF1 ), + ( 'FUN3', 0xF2 ), ( 'FUNCTION3', 0xF2 ), + ( 'FUN4', 0xF3 ), ( 'FUNCTION4', 0xF3 ), + ( 'FUN5', 0xF4 ), ( 'FUNCTION5', 0xF4 ), + ( 'FUN6', 0xF5 ), ( 'FUNCTION6', 0xF5 ), + ( 'FUN7', 0xF6 ), ( 'FUNCTION7', 0xF6 ), + ( 'FUN8', 0xF7 ), ( 'FUNCTION8', 0xF7 ), + ( 'FUN9', 0xF8 ), ( 'FUNCTION9', 0xF8 ), + ( 'FUN10', 0xF9 ), ( 'FUNCTION10', 0xF9 ), + ( 'FUN11', 0xFA ), ( 'FUNCTION11', 0xFA ), + ( 'FUN12', 0xFB ), ( 'FUNCTION12', 0xFB ), + ( 'FUN13', 0xFC ), ( 'FUNCTION13', 0xFC ), + ( 'FUN14', 0xFD ), ( 'FUNCTION14', 0xFD ), + ( 'FUN15', 0xFE ), ( 'FUNCTION15', 0xFE ), + ( 'FUN16', 0xFF ), ( 'FUNCTION16', 0xFF ), +]) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templateKeymap.h Tue Sep 02 10:03:50 2014 -0700 @@ -0,0 +1,114 @@ +/* Copyright (C) 2014 by Jacob Alexander + * + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this file. If not, see <http://www.gnu.org/licenses/>. + */ + +// Generated MSG /w timestamp and compiler information + +#ifndef __generatedKeymap_h +#define __generatedKeymap_h + +// ----- Includes ----- + +// KLL Include +#include <kll.h> + + + +// ----- Capabilities ----- + +// Indexed Capabilities Table +<|CapabilitiesList|> + + +// -- Result Macros + +// Result Macro Guides +<|ResultMacros|> + + +// -- Result Macro List + +// Indexed Table of Result Macros +<|ResultMacroList|> + + +// -- Trigger Macros + +// Trigger Macro Guides +<|TriggerMacros|> + + +// -- Trigger Macro List + +// Indexed Table of Trigger Macros +<|TriggerMacroList|> + + + +// ----- Trigger Maps ----- + +// MaxScanCode +// - This is retrieved from the KLL configuration +// - Should be corollated with the max scan code in the scan module +// - Maximum value is 0x100 (0x0 to 0xFF) +// - Increasing it beyond the keyboard's capabilities is just a waste of ram... +#define MaxScanCode <MaxScanCode> + +// -- Trigger Lists +// +// Index 0: # of triggers in list +// Index n: pointer to trigger macro - use tm() macro + +// - Default Layer - +<|DefaultLayerTriggerList|> + + +// - Partial Layers - +<|PartialLayerTriggerLists|> + + +// -- ScanCode Indexed Maps +// Maps to a trigger list of macro pointers +// _ +// <scan code> -> |T| +// |r| -> <trigger macro pointer 1> +// |i| +// |g| -> <trigger macro pointer 2> +// |g| +// |e| -> <trigger macro pointer 3> +// |r| +// |s| -> <trigger macro pointer n> +// - + +// - Default Map for ScanCode Lookup - +<|DefaultLayerScanMap|> + +// - Partial Layer ScanCode Lookup Maps - +<|PartialLayerScanMaps|> + + + +// ----- Layer Index ----- + +// -- Layer Index List +// +// Index 0: Default map +// Index n: Additional layers +<|LayerIndexList|> + + + +#endif // __generatedKeymap_h +