diff --git a/common/__init__.py b/common/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/common/channel.py b/common/channel.py
new file mode 100644
index 0000000..45e014d
--- /dev/null
+++ b/common/channel.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+'''
+KLL Channel Containers
+'''
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+
+
+### Classes ###
+
+class Channel:
+ '''
+ Pixel Channel Container
+ '''
+ def __init__( self, uid, width ):
+ self.uid = uid
+ self.width = width
+
+ def __repr__( self ):
+ return "{0}:{1}".format( self.uid, self.width )
+
+
+class ChannelList:
+ '''
+ Pixel Channel List Container
+ '''
+ def __init__( self ):
+ self.channels = []
+
+ def setChannels( self, channel_list ):
+ '''
+ Apply channels to Pixel
+ '''
+ for channel in channel_list:
+ self.channels.append( Channel( channel[0], channel[1] ) )
+
+ def strChannels( self ):
+ '''
+ __repr__ of Channel when multiple inheritance is used
+ '''
+ output = ""
+ for index, channel in enumerate( self.channels ):
+ if index > 0:
+ output += ","
+ output += "{0}".format( channel )
+
+ return output
+
+ def __repr__( self ):
+ return self.strChannels()
+
diff --git a/common/context.py b/common/context.py
new file mode 100644
index 0000000..f86dbc1
--- /dev/null
+++ b/common/context.py
@@ -0,0 +1,256 @@
+#!/usr/bin/env python3
+'''
+KLL Context Definitions
+* Generic (auto-detected)
+* Configuration
+* BaseMap
+* DefaultMap
+* PartialMap
+'''
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+import copy
+import os
+
+import common.organization as organization
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+
+
+### Classes ###
+
+class Context:
+ '''
+ Base KLL Context Class
+ '''
+ def __init__( self ):
+ '''
+ Context initialization
+ '''
+ # Each context may have one or more included kll files
+ # And each of these files will have at least 1 Context
+ self.kll_files = []
+
+ # File data assigned to each context
+ # This info is populated during the PreprocessorStage
+ self.lines = []
+ self.data = ""
+ self.parent = None
+
+ # Tokenized data sets
+ self.classification_token_data = []
+ self.expressions = []
+
+ # Organized Expression Datastructure
+ self.organization = organization.Organization()
+
+ def __repr__( self ):
+ # Build list of all the info
+ return "(kll_files={0}, lines={1}, data='''{2}''')".format(
+ self.kll_files,
+ self.lines,
+ self.data,
+ )
+
+ def initial_context( self, lines, data, parent ):
+ '''
+ Used in the PreprocessorStage to update the initial line and kll file data
+
+ @param lines: Data split per line
+ @param data: Entire context in a single string
+ @param parent: Parent node, always a KLLFile
+ '''
+ self.lines = lines
+ self.data = data
+ self.parent = parent
+
+ def query( self, kll_expression, kll_type ):
+ '''
+ Query
+
+ Returns a dictionary of the specified property.
+ Most queries should use this.
+
+ See organization.py:Organization for property_type details.
+
+ @param kll_expression: String name of expression type
+ @param kll_type: String name of the expression sub-type
+
+ @return: context_name: (dictionary)
+ '''
+ return self.organization.data_mapping[ kll_expression ][ kll_type ]
+
+
+class GenericContext( Context ):
+ '''
+ Generic KLL Context Class
+ '''
+
+
+class ConfigurationContext( Context ):
+ '''
+ Configuration KLL Context Class
+ '''
+
+
+class BaseMapContext( Context ):
+ '''
+ Base Map KLL Context Class
+ '''
+
+
+class DefaultMapContext( Context ):
+ '''
+ Default Map KLL Context Class
+ '''
+
+
+class PartialMapContext( Context ):
+ '''
+ Partial Map KLL Context Class
+ '''
+ def __init__( self, layer ):
+ '''
+ Partial Map Layer Context Initialization
+
+ @param: Layer associated with Partial Map
+ '''
+ super().__init__()
+
+ self.layer = layer
+
+
+class MergeContext( Context ):
+ '''
+ Container class for a merged Context
+
+ Has references to the original contexts merged in
+ '''
+ def __init__( self, base_context ):
+ '''
+ Initialize the MergeContext with the base context
+ Another MergeContext can be used as the base_context
+
+ @param base_context: Context used to seed the MergeContext
+ '''
+ super().__init__()
+
+ # List of context, in the order of merging, starting from the base
+ self.contexts = [ base_context ]
+
+ # Copy the base context Organization into the MergeContext
+ self.organization = copy.copy( base_context.organization )
+
+ # Set the layer if the base is a PartialMapContext
+ if base_context.__class__.__name__ == 'PartialMapContext':
+ self.layer = base_context.layer
+
+ def merge( self, merge_in, debug ):
+ '''
+ Merge in context
+
+ Another MergeContext can be merged into a MergeContext
+
+ @param merge_in: Context to merge in to this one
+ @param debug: Enable debug out
+ '''
+ # Append to context list
+ self.contexts.append( merge_in )
+
+ # Merge context
+ self.organization.merge(
+ merge_in.organization,
+ debug
+ )
+
+ # Set the layer if the base is a PartialMapContext
+ if merge_in.__class__.__name__ == 'PartialMapContext':
+ self.layer = merge_in.layer
+
+ def reduction( self ):
+ '''
+ Simplifies datastructure
+
+ NOTE: This will remove data, therefore, context is lost
+ '''
+ self.organization.reduction()
+
+ def paths( self ):
+ '''
+ Returns list of file paths used to generate this context
+ '''
+ file_paths = []
+
+ for kll_context in self.contexts:
+ # If context is a MergeContext then we have to recursively search
+ if kll_context.__class__.__name__ is 'MergeContext':
+ file_paths.extend( kll_context.paths() )
+ else:
+ file_paths.append( kll_context.parent.path )
+
+ return file_paths
+
+ def files( self ):
+ '''
+ Short form list of file paths used to generate this context
+ '''
+ file_paths = []
+ for file_path in self.paths():
+ file_paths.append( os.path.basename( file_path ) )
+
+ return file_paths
+
+ def __repr__( self ):
+ return "(kll_files={0}, organization={1})".format(
+ self.files(),
+ self.organization,
+ )
+
+ def query_contexts( self, kll_expression, kll_type ):
+ '''
+ Context Query
+
+ Returns a list of tuples (dictionary, kll_context) doing a deep search to the context leaf nodes.
+ This results in pre-merge data and is useful for querying information about files used during compilation.
+
+ See organization.py:Organization for property_type details.
+
+ @param kll_expression: String name of expression type
+ @param kll_type: String name of the expression sub-type
+
+ @return: context_name: (dictionary, kll_context)
+ '''
+ # Build list of leaf contexts
+ leaf_contexts = []
+ for kll_context in self.contexts:
+ # Recursively search if necessary
+ if kll_context.__class__.__name__ == 'MergeContext':
+ leaf_contexts.extend( kll_context.query_contexts( kll_expression, kll_type ) )
+ else:
+ leaf_contexts.append( ( kll_context.query( kll_expression, kll_type ), kll_context ) )
+
+ return leaf_contexts
+
diff --git a/common/emitter.py b/common/emitter.py
new file mode 100644
index 0000000..37bfcf4
--- /dev/null
+++ b/common/emitter.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python3
+'''
+KLL Emitter Base Classes
+'''
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+import re
+import os
+import sys
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+
+
+### Classes ###
+
+class Emitter:
+ '''
+ KLL Emitter Base Class
+
+ NOTE: Emitter should do as little as possible in the __init__ function.
+ '''
+ def __init__( self, control ):
+ '''
+ Emitter initialization
+
+ @param control: ControlStage object, used to access data from other stages
+ '''
+ self.control = control
+ self.color = False
+
+ def command_line_args( self, args ):
+ '''
+ Group parser for command line arguments
+
+ @param args: Name space of processed arguments
+ '''
+ print( "{0} '{1}' '{2}' has not been implemented yet"
+ .format(
+ WARNING,
+ self.command_line_args.__name__,
+ type( self ).__name__
+ )
+ )
+
+ def command_line_flags( self, parser ):
+ '''
+ Group parser for command line options
+
+ @param parser: argparse setup object
+ '''
+ print( "{0} '{1}' '{2}' has not been implemented yet"
+ .format(
+ WARNING,
+ self.command_line_flags.__name__,
+ type( self ).__name__
+ )
+ )
+
+ def process( self ):
+ '''
+ Emitter Processing
+ '''
+ print( "{0} '{1}' '{2}' has not been implemented yet"
+ .format(
+ WARNING,
+ self.process.__name__,
+ type( self ).__name__
+ )
+ )
+
+ def output( self ):
+ '''
+ Final Stage of Emitter
+
+ Generate desired outputs
+ '''
+ print( "{0} '{1}' '{2}' has not been implemented yet"
+ .format(
+ WARNING,
+ self.output.__name__,
+ type( self ).__name__
+ )
+ )
+
+
+class TextEmitter:
+ '''
+ KLL Text Emitter Class
+
+ Base class for any text emitter that wants to use the templating functionality
+
+ If multiple files need to be generated, call load_template and generate multiple times.
+ e.g.
+ load_template('_myfile.h')
+ generate('/tmp/myfile.h')
+
+ load_template('_myfile2.h')
+ generate('/tmp/myfile2.h')
+
+ TODO
+ - Generate list of unused tags
+ '''
+ def __init__( self ):
+ '''
+ TextEmitter Initialization
+ '''
+ # Dictionary used to do template replacements
+ self.fill_dict = {}
+ self.tag_list = []
+
+ self.template = None
+
+ def load_template( self, template ):
+ '''
+ Loads template file
+
+ Looks for <|tags|> to replace in the template
+
+ @param template: Path to template
+ '''
+
+ # Does template exist?
+ if not os.path.isfile( template ):
+ print ( "{0} '{1}' does not exist...".format( ERROR, template ) )
+ sys.exit( 1 )
+
+ self.template = template
+
+ # Generate list of fill tags
+ with open( template, 'r' ) as openFile:
+ for line in openFile:
+ match = re.findall( r'<\|([^|>]+)\|>', line )
+ for item in match:
+ self.tag_list.append( item )
+
+ def generate( self, output_path ):
+ '''
+ Generates the output file from the template file
+
+ @param output_path: Path to the generated file
+ '''
+ # Make sure we've called load_template at least once
+ if self.template is None:
+ print ( "{0} TextEmitter template (load_template) has not been called.".format( ERROR ) )
+ sys.exit( 1 )
+
+ # Process each line of the template, outputting to the target path
+ with open( output_path, 'w' ) as outputFile:
+ with open( self.template, 'r' ) as templateFile:
+ for line in templateFile:
+ # TODO Support multiple replacements per line
+ # TODO Support replacement with other text inline
+ match = re.findall( r'<\|([^|>]+)\|>', line )
+
+ # If match, replace with processed variable
+ if match:
+ try:
+ outputFile.write( self.fill_dict[ match[0] ] )
+ except KeyError as err:
+ print( "{0} '{1}' not found, skipping...".format(
+ WARNING, match[0]
+ ) )
+ outputFile.write("\n")
+
+ # Otherwise, just append template to output file
+ else:
+ outputFile.write( line )
+
diff --git a/common/expression.py b/common/expression.py
new file mode 100644
index 0000000..02da8e8
--- /dev/null
+++ b/common/expression.py
@@ -0,0 +1,630 @@
+#!/usr/bin/env python3
+'''
+KLL Expression Container
+'''
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+import copy
+
+from common.id import CapId
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+
+
+### Classes ###
+
+class Expression:
+ '''
+ Container class for KLL expressions
+ '''
+ def __init__( self, lparam, operator, rparam, context ):
+ '''
+ Initialize expression container
+
+ @param lparam: LOperatorData token
+ @param operator: Operator token
+ @param rparam: ROperatorData token
+ @param context: Parent context of expression
+ '''
+ # First stage/init
+ self.lparam_token = lparam
+ self.operator_token = operator
+ self.rparam_token = rparam
+ self.context = context # TODO, set multiple contexts for later stages
+
+ # Second stage
+ self.lparam_sub_tokens = []
+ self.rparam_sub_tokens = []
+
+ # Mutate class into the desired type
+ self.__class__ = {
+ '=>' : NameAssociationExpression,
+ '<=' : DataAssociationExpression,
+ '=' : AssignmentExpression,
+ ':' : MapExpression,
+ }[ self.operator_type() ]
+
+ def operator_type( self ):
+ '''
+ Determine which base operator this operator is of
+
+ All : (map) expressions are tokenized/parsed the same way
+
+ @return Base string representation of the operator
+ '''
+ if ':' in self.operator_token.value:
+ return ':'
+
+ return self.operator_token.value
+
+
+ def final_tokens( self, no_filter=False ):
+ '''
+ Return the final list of tokens, must complete the second stage first
+
+ @param no_filter: If true, do not filter out Space tokens
+
+ @return Finalized list of tokens
+ '''
+ ret = self.lparam_sub_tokens + [ self.operator_token ] + self.rparam_sub_tokens
+
+ if not no_filter:
+ ret = [ x for x in ret if x.type != 'Space' ]
+ return ret
+
+ def regen_str( self ):
+ '''
+ Re-construct the string based off the original set of tokens
+
+ ;
+ '''
+ return "{0}{1}{2};".format(
+ self.lparam_token.value,
+ self.operator_token.value,
+ self.rparam_token.value,
+ )
+
+ def point_chars( self, pos_list ):
+ '''
+ Using the regenerated string, point to a given list of characters
+ Used to indicate where a possible issue/syntax error is
+
+ @param pos_list: List of character indices
+
+ i.e.
+ > U"A" : : U"1";
+ > ^
+ '''
+ out = "\t{0}\n\t".format( self.regen_str() )
+
+ # Place a ^ character at the given locations
+ curpos = 1
+ for pos in sorted( pos_list ):
+ # Pad spaces, then add a ^
+ out += ' ' * (pos - curpos)
+ out += '^'
+ curpos += pos
+
+ return out
+
+ def rparam_start( self ):
+ '''
+ Starting positing char of rparam_token in a regen_str
+ '''
+ return len( self.lparam_token.value ) + len( self.operator_token.value )
+
+ def __repr__( self ):
+ # Build string representation based off of what has been set
+ # lparam, operator and rparam are always set
+ out = "Expression: {0}{1}{2}".format(
+ self.lparam_token.value,
+ self.operator_token.value,
+ self.rparam_token.value,
+ )
+
+ # TODO - Add more depending on what has been set
+ return out
+
+ def unique_keys( self ):
+ '''
+ Generates a list of unique identifiers for the expression that is mergeable
+ with other functional equivalent expressions.
+
+ This method should never get called directly as a generic Expression
+ '''
+ return [ ('UNKNOWN KEY', 'UNKNOWN EXPRESSION') ]
+
+
+class AssignmentExpression( Expression ):
+ '''
+ Container class for assignment KLL expressions
+ '''
+ type = None
+ name = None
+ pos = None
+ value = None
+
+ ## Setters ##
+ def array( self, name, pos, value ):
+ '''
+ Assign array assignment parameters to expression
+
+ @param name: Name of variable
+ @param pos: Array position of the value (if None, overwrite the entire array)
+ @param value: Value of the array, if pos is specified, this is the value of an element
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'Array'
+ self.name = name
+ self.pos = pos
+ self.value = value
+
+ # If pos is not none, flatten
+ if pos is not None:
+ self.value = "".join( str( x ) for x in self.value )
+
+ return True
+
+ def variable( self, name, value ):
+ '''
+ Assign variable assignment parameters to expression
+
+ @param name: Name of variable
+ @param value: Value of variable
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'Variable'
+ self.name = name
+ self.value = value
+
+ # Flatten value, often a list of various token types
+ self.value = "".join( str( x ) for x in self.value )
+
+ return True
+
+ def __repr__( self ):
+ if self.type == 'Variable':
+ return "{0} = {1};".format( self.name, self.value )
+ elif self.type == 'Array':
+ return "{0}[{1}] = {2};".format( self.name, self.pos, self.value )
+
+ return "ASSIGNMENT UNKNOWN"
+
+ def unique_keys( self ):
+ '''
+ Generates a list of unique identifiers for the expression that is mergeable
+ with other functional equivalent expressions.
+ '''
+ return [ ( self.name, self ) ]
+
+
+class NameAssociationExpression( Expression ):
+ '''
+ Container class for name association KLL expressions
+ '''
+ type = None
+ name = None
+ association = None
+
+ ## Setters ##
+ def capability( self, name, association, parameters ):
+ '''
+ Assign a capability C function name association
+
+ @param name: Name of capability
+ @param association: Name of capability in target backend output
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'Capability'
+ self.name = name
+ self.association = CapId( association, 'Definition', parameters )
+
+ return True
+
+ def define( self, name, association ):
+ '''
+ Assign a define C define name association
+
+ @param name: Name of variable
+ @param association: Name of association in target backend output
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'Define'
+ self.name = name
+ self.association = association
+
+ return True
+
+ def __repr__( self ):
+ return "{0} <= {1};".format( self.name, self.association )
+
+ def unique_keys( self ):
+ '''
+ Generates a list of unique identifiers for the expression that is mergeable
+ with other functional equivalent expressions.
+ '''
+ return [ ( self.name, self ) ]
+
+
+class DataAssociationExpression( Expression ):
+ '''
+ Container class for data association KLL expressions
+ '''
+ type = None
+ association = None
+ value = None
+
+ ## Setters ##
+ def animation( self, animations, animation_modifiers ):
+ '''
+ Animation definition and configuration
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'Animation'
+ self.association = animations
+ self.value = animation_modifiers
+
+ return True
+
+ def animationFrame( self, animation_frames, pixel_modifiers ):
+ '''
+ Pixel composition of an Animation Frame
+
+ @return: True if parsing was successful
+ '''
+
+ self.type = 'AnimationFrame'
+ self.association = animation_frames
+ self.value = pixel_modifiers
+
+ return True
+
+ def pixelPosition( self, pixels, position ):
+ '''
+ Pixel Positioning
+
+ @return: True if parsing was successful
+ '''
+ for pixel in pixels:
+ pixel.setPosition( position )
+
+ self.type = 'PixelPosition'
+ self.association = pixels
+
+ return True
+
+ def scanCodePosition( self, scancodes, position ):
+ '''
+ Scan Code to Position Mapping
+
+ Note: Accepts lists of scan codes
+ Alone this isn't useful, but you can assign rows and columns using ranges instead of individually
+
+ @return: True if parsing was successful
+ '''
+ for scancode in scancodes:
+ scancode.setPosition( position )
+
+ self.type = 'ScanCodePosition'
+ self.association = scancodes
+
+ return True
+
+ def __repr__( self ):
+ if self.type in ['PixelPosition', 'ScanCodePosition']:
+ output = ""
+ for index, association in enumerate( self.association ):
+ if index > 0:
+ output += "; "
+ output += "{0}".format( association )
+ return "{0};".format( output )
+ return "{0} <= {1};".format( self.association, self.value )
+
+ def unique_keys( self ):
+ '''
+ Generates a list of unique identifiers for the expression that is mergeable
+ with other functional equivalent expressions.
+ '''
+ keys = []
+
+ # Positions require a bit more introspection to get the unique keys
+ if self.type in ['PixelPosition', 'ScanCodePosition']:
+ for index, key in enumerate( self.association ):
+ uniq_expr = self
+
+ # If there is more than one key, copy the expression
+ # and remove the non-related variants
+ if len( self.association ) > 1:
+ uniq_expr = copy.copy( self )
+
+ # Isolate variant by index
+ uniq_expr.association = [ uniq_expr.association[ index ] ]
+
+ keys.append( ( "{0}".format( key.unique_key() ), uniq_expr ) )
+
+ # AnimationFrames are already list of keys
+ # TODO Reorder frame assignments to dedup function equivalent mappings
+ elif self.type in ['AnimationFrame']:
+ for index, key in enumerate( self.association ):
+ uniq_expr = self
+
+ # If there is more than one key, copy the expression
+ # and remove the non-related variants
+ if len( self.association ) > 1:
+ uniq_expr = copy.copy( self )
+
+ # Isolate variant by index
+ uniq_expr.association = [ uniq_expr.association[ index ] ]
+
+ keys.append( ( "{0}".format( key ), uniq_expr ) )
+
+ # Otherwise treat as a single element
+ else:
+ keys = [ ( "{0}".format( self.association ), self ) ]
+
+ # Remove any duplicate keys
+ # TODO Stat? Might be at neat report about how many duplicates were squashed
+ keys = list( set( keys ) )
+
+ return keys
+
+
+class MapExpression( Expression ):
+ '''
+ Container class for KLL map expressions
+ '''
+ type = None
+ triggers = None
+ operator = None
+ results = None
+ animation = None
+ animation_frame = None
+ pixels = None
+ position = None
+
+ ## Setters ##
+ def scanCode( self, triggers, operator, results ):
+ '''
+ Scan Code mapping
+
+ @param triggers: Sequence of combos of ranges of namedtuples
+ @param operator: Type of map operation
+ @param results: Sequence of combos of ranges of namedtuples
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'ScanCode'
+ self.triggers = triggers
+ self.operator = operator
+ self.results = results
+
+ return True
+
+ def usbCode( self, triggers, operator, results ):
+ '''
+ USB Code mapping
+
+ @param triggers: Sequence of combos of ranges of namedtuples
+ @param operator: Type of map operation
+ @param results: Sequence of combos of ranges of namedtuples
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'USBCode'
+ self.triggers = triggers
+ self.operator = operator
+ self.results = results
+
+ return True
+
+ def animationTrigger( self, animation, operator, results ):
+ '''
+ Animation Trigger mapping
+
+ @param animation: Animation trigger of result
+ @param operator: Type of map operation
+ @param results: Sequence of combos of ranges of namedtuples
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'Animation'
+ self.animation = animation
+ self.triggers = animation
+ self.operator = operator
+ self.results = results
+
+ return True
+
+ def pixelChannels( self, pixelmap, trigger ):
+ '''
+ Pixel Channel Composition
+
+ @return: True if parsing was successful
+ '''
+ self.type = 'PixelChannel'
+ self.pixel = pixelmap
+ self.position = trigger
+
+ return True
+
+ def sequencesOfCombosOfIds( self, expression_param ):
+ '''
+ Prettified Sequence of Combos of Identifiers
+
+ @param expression_param: Trigger or Result parameter of an expression
+
+ Scan Code Example
+ [[[S10, S16], [S42]], [[S11, S16], [S42]]] -> (S10 + S16, S42)|(S11 + S16, S42)
+ '''
+ output = ""
+
+ # Sometimes during error cases, might be None
+ if expression_param is None:
+ return output
+
+ # Iterate over each trigger/result variants (expanded from ranges), each one is a sequence
+ for index, sequence in enumerate( expression_param ):
+ if index > 0:
+ output += "|"
+ output += "("
+
+ # Iterate over each combo (element of the sequence)
+ for index, combo in enumerate( sequence ):
+ if index > 0:
+ output += ", "
+
+ # Iterate over each trigger identifier
+ for index, identifier in enumerate( combo ):
+ if index > 0:
+ output += " + "
+ output += "{0}".format( identifier )
+
+ output += ")"
+
+ return output
+
+ def elems( self ):
+ '''
+ Return number of trigger and result elements
+
+ Useful for determining if this is a trigger macro (2+)
+ Should always return at least (1,1) unless it's an invalid calculation
+
+ @return: ( triggers, results )
+ '''
+ elems = [ 0, 0 ]
+
+ # XXX Needed?
+ if self.type == 'PixelChannel':
+ return tuple( elems )
+
+ # Iterate over each trigger variant (expanded from ranges), each one is a sequence
+ for sequence in self.triggers:
+ # Iterate over each combo (element of the sequence)
+ for combo in sequence:
+ # Just measure the size of the combo
+ elems[0] += len( combo )
+
+ # Iterate over each result variant (expanded from ranges), each one is a sequence
+ for sequence in self.results:
+ # Iterate over each combo (element of the sequence)
+ for combo in sequence:
+ # Just measure the size of the combo
+ elems[1] += len( combo )
+
+ return tuple( elems )
+
+ def trigger_str( self ):
+ '''
+ String version of the trigger
+ Used for sorting
+ '''
+ # Pixel Channel Mapping doesn't follow the same pattern
+ if self.type == 'PixelChannel':
+ return "{0}".format( self.pixel )
+
+ return "{0}".format(
+ self.sequencesOfCombosOfIds( self.triggers ),
+ )
+
+ def result_str( self ):
+ '''
+ String version of the result
+ Used for sorting
+ '''
+ # Pixel Channel Mapping doesn't follow the same pattern
+ if self.type == 'PixelChannel':
+ return "{0}".format( self.position )
+
+ return "{0}".format(
+ self.sequencesOfCombosOfIds( self.results ),
+ )
+
+ def __repr__( self ):
+ # Pixel Channel Mapping doesn't follow the same pattern
+ if self.type == 'PixelChannel':
+ return "{0} : {1};".format( self.pixel, self.position )
+
+ return "{0} {1} {2};".format(
+ self.sequencesOfCombosOfIds( self.triggers ),
+ self.operator,
+ self.sequencesOfCombosOfIds( self.results ),
+ )
+
+ def unique_keys( self ):
+ '''
+ Generates a list of unique identifiers for the expression that is mergeable
+ with other functional equivalent expressions.
+
+ TODO: This function should re-order combinations to generate the key.
+ The final generated combo will be in the original order.
+ '''
+ keys = []
+
+ # Pixel Channel only has key per mapping
+ if self.type == 'PixelChannel':
+ keys = [ ( "{0}".format( self.pixel ), self ) ]
+
+ # Split up each of the keys
+ else:
+ # Iterate over each trigger/result variants (expanded from ranges), each one is a sequence
+ for index, sequence in enumerate( self.triggers ):
+ key = ""
+ uniq_expr = self
+
+ # If there is more than one key, copy the expression
+ # and remove the non-related variants
+ if len( self.triggers ) > 1:
+ uniq_expr = copy.copy( self )
+
+ # Isolate variant by index
+ uniq_expr.triggers = [ uniq_expr.triggers[ index ] ]
+
+ # Iterate over each combo (element of the sequence)
+ for index, combo in enumerate( sequence ):
+ if index > 0:
+ key += ", "
+
+ # Iterate over each trigger identifier
+ for index, identifier in enumerate( combo ):
+ if index > 0:
+ key += " + "
+ key += "{0}".format( identifier )
+
+ # Add key to list
+ keys.append( ( key, uniq_expr ) )
+
+ # Remove any duplicate keys
+ # TODO Stat? Might be at neat report about how many duplicates were squashed
+ keys = list( set( keys ) )
+
+ return keys
+
diff --git a/common/file.py b/common/file.py
new file mode 100644
index 0000000..8450f90
--- /dev/null
+++ b/common/file.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+'''
+KLL File Container
+'''
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+import os
+
+import common.context as context
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+
+
+### Classes ###
+
+class KLLFile:
+ '''
+ Container class for imported KLL files
+ '''
+ def __init__( self, path, file_context ):
+ '''
+ Initialize file container
+
+ @param path: Path to filename, if relative, relative to the execution environment
+ @param context: KLL Context object
+ '''
+ self.path = path
+ self.context = file_context
+ self.lines = []
+ self.data = ""
+
+ def __repr__( self ):
+ context_str = type( self.context ).__name__
+
+ # Show layer info if this is a PartialMap
+ if isinstance( self.context, context.PartialMapContext ):
+ context_str = "{0}({1})".format( context_str, self.context.layer )
+
+ return "({0}, {1})".format( self.path, context_str )
+
+ def check( self ):
+ '''
+ Make sure that the file exists at the initialized path
+ '''
+ exists = os.path.isfile( self.path )
+
+ # Display error message, will exit later
+ if not exists:
+ print( "{0} {1} does not exist...".format( ERROR, self.path ) )
+
+ return exists
+
+ def read( self ):
+ '''
+ Read the contents of the file path into memory
+ Reads both per line and complete copies
+ '''
+ try:
+ # Read file into memory, removing newlines
+ with open( self.path ) as f:
+ self.data = f.read()
+ self.lines = self.data.splitlines()
+
+ except:
+ print( "{0} Failed to read '{1}' into memory...".format( ERROR, self.path ) )
+ return False
+
+ return True
+
+
+
diff --git a/common/hid_dict.py b/common/hid_dict.py
new file mode 100644
index 0000000..d9e5b7f
--- /dev/null
+++ b/common/hid_dict.py
@@ -0,0 +1,1583 @@
+#!/usr/bin/env python3
+'''
+KLL Compiler - HID Dictionary Lookup
+
+USB Code Lookup Dictionary
+'''
+
+# Copyright (C) 2014-2016 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 .
+
+# 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([
+ # Fall-through block
+ ( ('NONE', 0), '' ), # Special case, there are no arguments
+
+ # USB HID Keyboard Codes
+ ( ('USB', 0x00), 'KEY_NOEVENT' ), # Event, not a physical key
+ ( ('USB', 0x01), 'KEY_ERRORROLLOVER' ), # Event, not a physical key
+ ( ('USB', 0x02), 'KEY_POSTFAIL' ), # Event, not a physical key
+ ( ('USB', 0x03), 'KEY_ERRORUNDEFINED' ), # Event, not a physical key
+ ( ('USB', 0x04), 'KEY_A' ),
+ ( ('USB', 0x05), 'KEY_B' ),
+ ( ('USB', 0x06), 'KEY_C' ),
+ ( ('USB', 0x07), 'KEY_D' ),
+ ( ('USB', 0x08), 'KEY_E' ),
+ ( ('USB', 0x09), 'KEY_F' ),
+ ( ('USB', 0x0A), 'KEY_G' ),
+ ( ('USB', 0x0B), 'KEY_H' ),
+ ( ('USB', 0x0C), 'KEY_I' ),
+ ( ('USB', 0x0D), 'KEY_J' ),
+ ( ('USB', 0x0E), 'KEY_K' ),
+ ( ('USB', 0x0F), 'KEY_L' ),
+ ( ('USB', 0x10), 'KEY_M' ),
+ ( ('USB', 0x11), 'KEY_N' ),
+ ( ('USB', 0x12), 'KEY_O' ),
+ ( ('USB', 0x13), 'KEY_P' ),
+ ( ('USB', 0x14), 'KEY_Q' ),
+ ( ('USB', 0x15), 'KEY_R' ),
+ ( ('USB', 0x16), 'KEY_S' ),
+ ( ('USB', 0x17), 'KEY_T' ),
+ ( ('USB', 0x18), 'KEY_U' ),
+ ( ('USB', 0x19), 'KEY_V' ),
+ ( ('USB', 0x1A), 'KEY_W' ),
+ ( ('USB', 0x1B), 'KEY_X' ),
+ ( ('USB', 0x1C), 'KEY_Y' ),
+ ( ('USB', 0x1D), 'KEY_Z' ),
+ ( ('USB', 0x1E), 'KEY_1' ),
+ ( ('USB', 0x1F), 'KEY_2' ),
+ ( ('USB', 0x20), 'KEY_3' ),
+ ( ('USB', 0x21), 'KEY_4' ),
+ ( ('USB', 0x22), 'KEY_5' ),
+ ( ('USB', 0x23), 'KEY_6' ),
+ ( ('USB', 0x24), 'KEY_7' ),
+ ( ('USB', 0x25), 'KEY_8' ),
+ ( ('USB', 0x26), 'KEY_9' ),
+ ( ('USB', 0x27), 'KEY_0' ),
+ ( ('USB', 0x28), 'KEY_ENTER' ),
+ ( ('USB', 0x29), 'KEY_ESC' ),
+ ( ('USB', 0x2A), 'KEY_BACKSPACE' ),
+ ( ('USB', 0x2B), 'KEY_TAB' ),
+ ( ('USB', 0x2C), 'KEY_SPACE' ),
+ ( ('USB', 0x2D), 'KEY_MINUS' ),
+ ( ('USB', 0x2E), 'KEY_EQUAL' ),
+ ( ('USB', 0x2F), 'KEY_LEFT_BRACKET' ),
+ ( ('USB', 0x30), 'KEY_RIGHT_BRACKET' ),
+ ( ('USB', 0x31), 'KEY_BACKSLASH' ),
+ ( ('USB', 0x32), 'KEY_NUMBER' ),
+ ( ('USB', 0x33), 'KEY_SEMICOLON' ),
+ ( ('USB', 0x34), 'KEY_QUOTE' ),
+ ( ('USB', 0x35), 'KEY_BACKTICK' ),
+ ( ('USB', 0x36), 'KEY_COMMA' ),
+ ( ('USB', 0x37), 'KEY_PERIOD' ),
+ ( ('USB', 0x38), 'KEY_SLASH' ),
+ ( ('USB', 0x39), 'KEY_CAPS_LOCK' ),
+ ( ('USB', 0x3A), 'KEY_F1' ),
+ ( ('USB', 0x3B), 'KEY_F2' ),
+ ( ('USB', 0x3C), 'KEY_F3' ),
+ ( ('USB', 0x3D), 'KEY_F4' ),
+ ( ('USB', 0x3E), 'KEY_F5' ),
+ ( ('USB', 0x3F), 'KEY_F6' ),
+ ( ('USB', 0x40), 'KEY_F7' ),
+ ( ('USB', 0x41), 'KEY_F8' ),
+ ( ('USB', 0x42), 'KEY_F9' ),
+ ( ('USB', 0x43), 'KEY_F10' ),
+ ( ('USB', 0x44), 'KEY_F11' ),
+ ( ('USB', 0x45), 'KEY_F12' ),
+ ( ('USB', 0x46), 'KEY_PRINTSCREEN' ),
+ ( ('USB', 0x47), 'KEY_SCROLL_LOCK' ),
+ ( ('USB', 0x48), 'KEY_PAUSE' ),
+ ( ('USB', 0x49), 'KEY_INSERT' ),
+ ( ('USB', 0x4A), 'KEY_HOME' ),
+ ( ('USB', 0x4B), 'KEY_PAGE_UP' ),
+ ( ('USB', 0x4C), 'KEY_DELETE' ),
+ ( ('USB', 0x4D), 'KEY_END' ),
+ ( ('USB', 0x4E), 'KEY_PAGE_DOWN' ),
+ ( ('USB', 0x4F), 'KEY_RIGHT' ),
+ ( ('USB', 0x50), 'KEY_LEFT' ),
+ ( ('USB', 0x51), 'KEY_DOWN' ),
+ ( ('USB', 0x52), 'KEY_UP' ),
+ ( ('USB', 0x53), 'KEY_NUM_LOCK' ),
+ ( ('USB', 0x54), 'KEYPAD_SLASH' ),
+ ( ('USB', 0x55), 'KEYPAD_ASTERISK' ),
+ ( ('USB', 0x56), 'KEYPAD_MINUS' ),
+ ( ('USB', 0x57), 'KEYPAD_PLUS' ),
+ ( ('USB', 0x58), 'KEYPAD_ENTER' ),
+ ( ('USB', 0x59), 'KEYPAD_1' ),
+ ( ('USB', 0x5A), 'KEYPAD_2' ),
+ ( ('USB', 0x5B), 'KEYPAD_3' ),
+ ( ('USB', 0x5C), 'KEYPAD_4' ),
+ ( ('USB', 0x5D), 'KEYPAD_5' ),
+ ( ('USB', 0x5E), 'KEYPAD_6' ),
+ ( ('USB', 0x5F), 'KEYPAD_7' ),
+ ( ('USB', 0x60), 'KEYPAD_8' ),
+ ( ('USB', 0x61), 'KEYPAD_9' ),
+ ( ('USB', 0x62), 'KEYPAD_0' ),
+ ( ('USB', 0x63), 'KEYPAD_PERIOD' ),
+ ( ('USB', 0x64), 'KEY_ISO_SLASH' ),
+ ( ('USB', 0x65), 'KEY_APP' ),
+ ( ('USB', 0x66), 'KEYBOARD_STATUS' ), # Used for indicating status or errors, not a key
+ ( ('USB', 0x67), 'KEYPAD_EQUAL' ),
+ ( ('USB', 0x68), 'KEY_F13' ),
+ ( ('USB', 0x69), 'KEY_F14' ),
+ ( ('USB', 0x6A), 'KEY_F15' ),
+ ( ('USB', 0x6B), 'KEY_F16' ),
+ ( ('USB', 0x6C), 'KEY_F17' ),
+ ( ('USB', 0x6D), 'KEY_F18' ),
+ ( ('USB', 0x6E), 'KEY_F19' ),
+ ( ('USB', 0x6F), 'KEY_F20' ),
+ ( ('USB', 0x70), 'KEY_F21' ),
+ ( ('USB', 0x71), 'KEY_F22' ),
+ ( ('USB', 0x72), 'KEY_F23' ),
+ ( ('USB', 0x73), 'KEY_F24' ),
+ ( ('USB', 0x74), 'KEY_EXEC' ),
+ ( ('USB', 0x75), 'KEY_HELP' ),
+ ( ('USB', 0x76), 'KEY_MENU' ),
+ ( ('USB', 0x77), 'KEY_SELECT' ),
+ ( ('USB', 0x78), 'KEY_STOP' ),
+ ( ('USB', 0x79), 'KEY_AGAIN' ),
+ ( ('USB', 0x7A), 'KEY_UNDO' ),
+ ( ('USB', 0x7B), 'KEY_CUT' ),
+ ( ('USB', 0x7C), 'KEY_COPY' ),
+ ( ('USB', 0x7D), 'KEY_PASTE' ),
+ ( ('USB', 0x7E), 'KEY_FIND' ),
+ ( ('USB', 0x7F), 'KEY_MUTE' ),
+ ( ('USB', 0x80), 'KEY_VOL_UP' ),
+ ( ('USB', 0x81), 'KEY_VOL_DOWN' ),
+ ( ('USB', 0x82), 'KEY_CAPS_TLOCK' ), # Toggle "Locking" Scroll Lock (Old keyboards with Locking Caps Lock)
+ ( ('USB', 0x83), 'KEY_NUM_TLOCK' ),
+ ( ('USB', 0x84), 'KEY_SCROLL_TLOCK' ),
+ ( ('USB', 0x85), 'KEYPAD_COMMA' ), # Brazillian (See spec)
+ ( ('USB', 0x86), 'KEYPAD_EQUAL_AS' ), # AS/400 Keyboard (See spec)
+ ( ('USB', 0x87), 'KEY_INTER1' ), # KANJI1 - Brazillian and Japanese "Ru" and "-"
+ ( ('USB', 0x88), 'KEY_INTER2' ), # KANJI2 - Japanese Katakana/Hiragana
+ ( ('USB', 0x89), 'KEY_INTER3' ), # KANJI3 - Japanese Yen
+ ( ('USB', 0x8A), 'KEY_INTER4' ), # KANJI4 - Japanese Henkan
+ ( ('USB', 0x8B), 'KEY_INTER5' ), # KANJI5 - Japanese Muhenkan
+ ( ('USB', 0x8C), 'KEY_INTER6' ), # KANJI6 - PC('USB', 0x62) Comma (Ka-m-ma)
+ ( ('USB', 0x8D), 'KEY_INTER7' ), # KANJI7 - Double-Byte/Single-Byte Toggle
+ ( ('USB', 0x8E), 'KEY_INTER8' ), # KANJI8 - Undefined
+ ( ('USB', 0x8F), 'KEY_INTER9' ), # KANJI9 - Undefined
+ ( ('USB', 0x90), 'KEY_LANG1' ), # Korean Hangul/English Toggle
+ ( ('USB', 0x91), 'KEY_LANG2' ), # Korean Hanja Conversion - Japanese Eisu
+ ( ('USB', 0x92), 'KEY_LANG3' ), # Japanese Katakana Key (USB)
+ ( ('USB', 0x93), 'KEY_LANG4' ), # Japanese Hiragana Key (USB)
+ ( ('USB', 0x94), 'KEY_LANG5' ), # Japanese Zenkaku/Hankaku Key (USB)
+ ( ('USB', 0x95), 'KEY_LANG6' ), # Reserved (Application Specific)
+ ( ('USB', 0x96), 'KEY_LANG7' ), # Reserved (Application Specific)
+ ( ('USB', 0x97), 'KEY_LANG8' ), # Reserved (Application Specific)
+ ( ('USB', 0x98), 'KEY_LANG9' ), # Reserved (Application Specific)
+ ( ('USB', 0x99), 'KEY_ALT_ERASE' ), # Special Erase (See Spec)
+ ( ('USB', 0x9A), 'KEY_SYSREQ_ATT' ), # Modifier Type
+ ( ('USB', 0x9B), 'KEY_CANCEL' ),
+ ( ('USB', 0x9C), 'KEY_CLEAR' ),
+ ( ('USB', 0x9D), 'KEY_PRIOR' ),
+ ( ('USB', 0x9E), 'KEY_RETURN' ),
+ ( ('USB', 0x9F), 'KEY_SEPARATOR' ),
+ ( ('USB', 0xA0), 'KEY_OUT' ),
+ ( ('USB', 0xA1), 'KEY_OPER' ),
+ ( ('USB', 0xA2), 'KEY_CLEAR_AGAIN' ),
+ ( ('USB', 0xA3), 'KEY_CRSEL_PROPS' ),
+ ( ('USB', 0xA4), 'KEY_EXSEL' ),
+# ('USB', 0xA5) - ('USB', 0xAF) Reserved
+ ( ('USB', 0xB0), 'KEYPAD_00' ),
+ ( ('USB', 0xB1), 'KEYPAD_000' ),
+ ( ('USB', 0xB2), 'KEY_1000_SEP' ),
+ ( ('USB', 0xB3), 'KEY_DECIMAL_SEP' ),
+ ( ('USB', 0xB4), 'KEY_CURRENCY_MAIN' ),
+ ( ('USB', 0xB5), 'KEY_CURRENCY_SUB' ),
+ ( ('USB', 0xB6), 'KEYPAD_LPAREN' ),
+ ( ('USB', 0xB7), 'KEYPAD_RPAREN' ),
+ ( ('USB', 0xB8), 'KEYPAD_LBRACKET' ),
+ ( ('USB', 0xB9), 'KEYPAD_RBRACKET' ),
+ ( ('USB', 0xBA), 'KEYPAD_TAB' ),
+ ( ('USB', 0xBB), 'KEYPAD_BACKSPACE' ),
+ ( ('USB', 0xBC), 'KEYPAD_A' ),
+ ( ('USB', 0xBD), 'KEYPAD_B' ),
+ ( ('USB', 0xBE), 'KEYPAD_C' ),
+ ( ('USB', 0xBF), 'KEYPAD_D' ),
+ ( ('USB', 0xC0), 'KEYPAD_E' ),
+ ( ('USB', 0xC1), 'KEYPAD_F' ),
+ ( ('USB', 0xC2), 'KEYPAD_XOR' ),
+ ( ('USB', 0xC3), 'KEYPAD_CHEVRON' ),
+ ( ('USB', 0xC4), 'KEYPAD_PERCENT' ),
+ ( ('USB', 0xC5), 'KEYPAD_LTHAN' ),
+ ( ('USB', 0xC6), 'KEYPAD_GTHAN' ),
+ ( ('USB', 0xC7), 'KEYPAD_BITAND' ),
+ ( ('USB', 0xC8), 'KEYPAD_AND' ),
+ ( ('USB', 0xC9), 'KEYPAD_BITOR' ),
+ ( ('USB', 0xCA), 'KEYPAD_OR' ),
+ ( ('USB', 0xCB), 'KEYPAD_COLON' ),
+ ( ('USB', 0xCC), 'KEYPAD_POUND' ),
+ ( ('USB', 0xCD), 'KEYPAD_SPACE' ),
+ ( ('USB', 0xCE), 'KEYPAD_AT' ),
+ ( ('USB', 0xCF), 'KEYPAD_EXCLAIM' ),
+ ( ('USB', 0xD0), 'KEYPAD_MEM_STORE' ),
+ ( ('USB', 0xD1), 'KEYPAD_MEM_RECALL' ),
+ ( ('USB', 0xD2), 'KEYPAD_MEM_CLEAR' ),
+ ( ('USB', 0xD3), 'KEYPAD_MEM_ADD' ),
+ ( ('USB', 0xD4), 'KEYPAD_MEM_SUB' ),
+ ( ('USB', 0xD5), 'KEYPAD_MEM_MULT' ),
+ ( ('USB', 0xD6), 'KEYPAD_MEM_DIV' ),
+ ( ('USB', 0xD7), 'KEYPAD_PLUS_MINUS' ),
+ ( ('USB', 0xD8), 'KEYPAD_CLEAR' ),
+ ( ('USB', 0xD9), 'KEYPAD_CLEAR_ENTRY' ),
+ ( ('USB', 0xDA), 'KEYPAD_BINARY' ),
+ ( ('USB', 0xDB), 'KEYPAD_OCTAL' ),
+ ( ('USB', 0xDC), 'KEYPAD_DECIMAL' ),
+ ( ('USB', 0xDD), 'KEYPAD_HEX' ),
+# ('USB', 0xDE) - ('USB', 0xDF) Reserved
+ ( ('USB', 0xE0), 'KEY_LCTRL' ),
+ ( ('USB', 0xE1), 'KEY_LSHIFT' ),
+ ( ('USB', 0xE2), 'KEY_LALT' ),
+ ( ('USB', 0xE3), 'KEY_LGUI' ),
+ ( ('USB', 0xE4), 'KEY_RCTRL' ),
+ ( ('USB', 0xE5), 'KEY_RSHIFT' ),
+ ( ('USB', 0xE6), 'KEY_RALT' ),
+ ( ('USB', 0xE7), 'KEY_RGUI' ),
+# ('USB', 0xE8) - ('USB', 0xFF)FF Reserved, using ('USB', 0xF0) to ('USB', 0xFF) for function key placeholders
+ ( ('USB', 0xF0), 'KEY_FUN1' ),
+ ( ('USB', 0xF1), 'KEY_FUN2' ),
+ ( ('USB', 0xF2), 'KEY_FUN3' ),
+ ( ('USB', 0xF3), 'KEY_FUN4' ),
+ ( ('USB', 0xF4), 'KEY_FUN5' ),
+ ( ('USB', 0xF5), 'KEY_FUN6' ),
+ ( ('USB', 0xF6), 'KEY_FUN7' ),
+ ( ('USB', 0xF7), 'KEY_FUN8' ),
+ ( ('USB', 0xF8), 'KEY_FUN9' ),
+ ( ('USB', 0xF9), 'KEY_FUN10' ),
+ ( ('USB', 0xFA), 'KEY_FUN11' ),
+ ( ('USB', 0xFB), 'KEY_FUN12' ),
+ ( ('USB', 0xFC), 'KEY_FUN13' ),
+ ( ('USB', 0xFD), 'KEY_FUN14' ),
+ ( ('USB', 0xFE), 'KEY_FUN15' ),
+ ( ('USB', 0xFF), 'KEY_FUN16' ),
+# ('USB', 0x100) to ('USB', 0x121) for function key placeholders, not valid usb codes (must use a translation .kll file before firmware compilation)
+ ( ('USB', 0x100), 'KEY_LCK1' ),
+ ( ('USB', 0x101), 'KEY_LCK2' ),
+ ( ('USB', 0x102), 'KEY_LCK3' ),
+ ( ('USB', 0x103), 'KEY_LCK4' ),
+ ( ('USB', 0x104), 'KEY_LCK5' ),
+ ( ('USB', 0x105), 'KEY_LCK6' ),
+ ( ('USB', 0x106), 'KEY_LCK7' ),
+ ( ('USB', 0x107), 'KEY_LCK8' ),
+ ( ('USB', 0x108), 'KEY_LCK9' ),
+ ( ('USB', 0x109), 'KEY_LCK10' ),
+ ( ('USB', 0x10A), 'KEY_LCK11' ),
+ ( ('USB', 0x10B), 'KEY_LCK12' ),
+ ( ('USB', 0x10C), 'KEY_LCK13' ),
+ ( ('USB', 0x10D), 'KEY_LCK14' ),
+ ( ('USB', 0x10E), 'KEY_LCK15' ),
+ ( ('USB', 0x10F), 'KEY_LCK16' ),
+ ( ('USB', 0x110), 'KEY_LAT1' ),
+ ( ('USB', 0x111), 'KEY_LAT2' ),
+ ( ('USB', 0x112), 'KEY_LAT3' ),
+ ( ('USB', 0x113), 'KEY_LAT4' ),
+ ( ('USB', 0x114), 'KEY_LAT5' ),
+ ( ('USB', 0x115), 'KEY_LAT6' ),
+ ( ('USB', 0x116), 'KEY_LAT7' ),
+ ( ('USB', 0x117), 'KEY_LAT8' ),
+ ( ('USB', 0x118), 'KEY_LAT9' ),
+ ( ('USB', 0x119), 'KEY_LAT10' ),
+ ( ('USB', 0x11A), 'KEY_LAT11' ),
+ ( ('USB', 0x11B), 'KEY_LAT12' ),
+ ( ('USB', 0x11C), 'KEY_LAT13' ),
+ ( ('USB', 0x11D), 'KEY_LAT14' ),
+ ( ('USB', 0x11E), 'KEY_LAT15' ),
+ ( ('USB', 0x11F), 'KEY_LAT16' ),
+ ( ('USB', 0x120), 'KEY_NEXT_LAYER' ),
+ ( ('USB', 0x121), 'KEY_PREV_LAYER' ),
+
+ # USB HID Consumer Control Codes
+# List of Consumer Codes - USB HID 1.12v2
+# Only listing relevant ones, let me know if you need more -HaaTa
+# NKRO HID Supports 0x020 - 0x29C
+ ( ('CONS', 0x020), 'CONSUMER_10' ),
+ ( ('CONS', 0x021), 'CONSUMER_100' ),
+ ( ('CONS', 0x022), 'CONSUMER_AM_PM' ),
+# 0x023 - 0x03F Reserved
+ ( ('CONS', 0x030), 'CONSUMER_POWER' ),
+ ( ('CONS', 0x031), 'CONSUMER_RESET' ),
+ ( ('CONS', 0x032), 'CONSUMER_SLEEP' ),
+ ( ('CONS', 0x033), 'CONSUMER_SLEEP_AFTER' ),
+ ( ('CONS', 0x034), 'CONSUMER_SLEEP_MODE' ),
+ ( ('CONS', 0x035), 'CONSUMER_ILLUMINATION' ),
+
+# 0x037 - 0x03F Reserved
+ ( ('CONS', 0x040), 'CONSUMER_MENU' ),
+ ( ('CONS', 0x041), 'CONSUMER_MENU_PICK' ),
+ ( ('CONS', 0x042), 'CONSUMER_MENU_UP' ),
+ ( ('CONS', 0x043), 'CONSUMER_MENU_DOWN' ),
+ ( ('CONS', 0x044), 'CONSUMER_MENU_LEFT' ),
+ ( ('CONS', 0x045), 'CONSUMER_MENU_RIGHT' ),
+ ( ('CONS', 0x046), 'CONSUMER_MENU_ESCAPE' ),
+ ( ('CONS', 0x047), 'CONSUMER_MENU_VALUE_INCREASE' ),
+ ( ('CONS', 0x048), 'CONSUMER_MENU_VALUE_DECREASE' ),
+# 0x049 - 0x05F Reserved
+ ( ('CONS', 0x060), 'CONSUMER_DATA_ON_SCREEN' ),
+ ( ('CONS', 0x061), 'CONSUMER_CLOSED_CAPTION' ),
+ ( ('CONS', 0x062), 'CONSUMER_CLOSED_CAPTION_SELECT' ),
+ ( ('CONS', 0x063), 'CONSUMER_VCR_TV' ),
+ ( ('CONS', 0x064), 'CONSUMER_BROADCAST_MODE' ),
+ ( ('CONS', 0x065), 'CONSUMER_SNAPSHOT' ),
+ ( ('CONS', 0x066), 'CONSUMER_STILL' ),
+# 0x067 - 0x06E Reserved?
+ ( ('CONS', 0x06F), 'CONSUMER_BRIGHTNESS_INCREMENT' ),
+ ( ('CONS', 0x070), 'CONSUMER_BRIGHTNESS_DECREMENT' ),
+
+ ( ('CONS', 0x072), 'CONSUMER_BACKLIGHT_TOGGLE' ),
+ ( ('CONS', 0x073), 'CONSUMER_BRIGHTNESS_MIN' ),
+ ( ('CONS', 0x074), 'CONSUMER_BRIGHTNESS_MAX' ),
+ ( ('CONS', 0x075), 'CONSUMER_BRIGHTNESS_AUTO' ),
+# 0x076 - 0x07F Reserved
+
+ ( ('CONS', 0x081), 'CONSUMER_ASSIGN_SELECTION' ),
+ ( ('CONS', 0x082), 'CONSUMER_MODE_STEP' ),
+ ( ('CONS', 0x083), 'CONSUMER_RECALL_LAST' ),
+ ( ('CONS', 0x084), 'CONSUMER_ENTER_CHANNEL' ),
+ ( ('CONS', 0x085), 'CONSUMER_ORDER_MOVIE' ),
+
+ ( ('CONS', 0x088), 'CONSUMER_MEDIA_COMPUTER' ),
+ ( ('CONS', 0x089), 'CONSUMER_MEDIA_TV' ),
+ ( ('CONS', 0x08A), 'CONSUMER_MEDIA_WWW' ),
+ ( ('CONS', 0x08B), 'CONSUMER_MEDIA_DVD' ),
+ ( ('CONS', 0x08C), 'CONSUMER_MEDIA_TELEPHONE' ),
+ ( ('CONS', 0x08D), 'CONSUMER_MEDIA_PROGRAM_GUIDE' ),
+ ( ('CONS', 0x08E), 'CONSUMER_MEDIA_VIDEO_PHONE' ),
+ ( ('CONS', 0x08F), 'CONSUMER_MEDIA_SELECT_GAMES' ),
+ ( ('CONS', 0x090), 'CONSUMER_MEDIA_SELECT_MESSAGES' ),
+ ( ('CONS', 0x091), 'CONSUMER_MEDIA_SELECT_CD' ),
+ ( ('CONS', 0x092), 'CONSUMER_MEDIA_SELECT_VCR' ),
+ ( ('CONS', 0x093), 'CONSUMER_MEDIA_SELECT_TUNER' ),
+ ( ('CONS', 0x094), 'CONSUMER_QUIT' ),
+ ( ('CONS', 0x095), 'CONSUMER_HELP' ),
+ ( ('CONS', 0x096), 'CONSUMER_MEDIA_SELECT_TAPE' ),
+ ( ('CONS', 0x097), 'CONSUMER_MEDIA_SELECT_CABLE' ),
+ ( ('CONS', 0x098), 'CONSUMER_MEDIA_SELECT_SATELLITE' ),
+ ( ('CONS', 0x099), 'CONSUMER_MEDIA_SELECT_SECURITY' ),
+ ( ('CONS', 0x09A), 'CONSUMER_MEDIA_SELECT_HOME' ),
+ ( ('CONS', 0x09B), 'CONSUMER_MEDIA_SELECT_CALL' ),
+ ( ('CONS', 0x09C), 'CONSUMER_CHANNEL_INCREMENT' ),
+ ( ('CONS', 0x09D), 'CONSUMER_CAHNNEL_DECREMENT' ),
+ ( ('CONS', 0x09E), 'CONSUMER_MEDIA_SELECT_SAP' ),
+# 0x09F Reserved
+ ( ('CONS', 0x0A0), 'CONSUMER_VCR_PLUS' ),
+ ( ('CONS', 0x0A1), 'CONSUMER_ONCE' ),
+ ( ('CONS', 0x0A2), 'CONSUMER_DAILY' ),
+ ( ('CONS', 0x0A3), 'CONSUMER_WEEKLY' ),
+ ( ('CONS', 0x0A4), 'CONSUMER_MONTHLY' ),
+# 0x0A5 - 0x0AF Reserved
+ ( ('CONS', 0x0B0), 'CONSUMER_PLAY' ),
+ ( ('CONS', 0x0B1), 'CONSUMER_PAUSE' ),
+ ( ('CONS', 0x0B2), 'CONSUMER_RECORD' ),
+ ( ('CONS', 0x0B3), 'CONSUMER_FAST_FORWARD' ),
+ ( ('CONS', 0x0B4), 'CONSUMER_REWIND' ),
+ ( ('CONS', 0x0B5), 'CONSUMER_SCAN_NEXT_TRACK' ),
+ ( ('CONS', 0x0B6), 'CONSUMER_SCAN_PREVIOUS_TRACK' ),
+ ( ('CONS', 0x0B7), 'CONSUMER_STOP' ),
+ ( ('CONS', 0x0B8), 'CONSUMER_EJECT' ),
+ ( ('CONS', 0x0B9), 'CONSUMER_RANDOM_PLAY' ),
+
+ ( ('CONS', 0x0BC), 'CONSUMER_REPEAT' ),
+
+ ( ('CONS', 0x0BE), 'CONSUMER_TRACK_NORMAL' ),
+
+ ( ('CONS', 0x0C0), 'CONSUMER_FRAME_FORWARD' ),
+ ( ('CONS', 0x0C1), 'CONSUMER_FRAME_BACK' ),
+ ( ('CONS', 0x0C2), 'CONSUMER_MARK' ),
+ ( ('CONS', 0x0C3), 'CONSUMER_CLEAR_MARK' ),
+ ( ('CONS', 0x0C4), 'CONSUMER_REPEAT_FROM_MARK' ),
+ ( ('CONS', 0x0C5), 'CONSUMER_RETURN_TO_MARK' ),
+ ( ('CONS', 0x0C6), 'CONSUMER_SEARCH_MARK_FORWARDS' ),
+ ( ('CONS', 0x0C7), 'CONSUMER_SEARCH_MARK_BACKWARDS' ),
+ ( ('CONS', 0x0C8), 'CONSUMER_COUNTER_RESET' ),
+ ( ('CONS', 0x0C9), 'CONSUMER_SHOW_COUNTER' ),
+ ( ('CONS', 0x0CA), 'CONSUMER_TRACKING_INCREMENT' ),
+ ( ('CONS', 0x0CB), 'CONSUMER_TRACKING_DECREMENT' ),
+ ( ('CONS', 0x0CC), 'CONSUMER_STOP_EJECT' ),
+ ( ('CONS', 0x0CD), 'CONSUMER_PAUSE_PLAY' ),
+ ( ('CONS', 0x0CE), 'CONSUMER_PLAY_SKIP' ),
+# 0x0CF - 0x0DF Reserved
+
+ ( ('CONS', 0x0E2), 'CONSUMER_MUTE' ),
+
+ ( ('CONS', 0x0E5), 'CONSUMER_BASS_BOOST' ),
+ ( ('CONS', 0x0E6), 'CONSUMER_SURROUND_MODE' ),
+ ( ('CONS', 0x0E7), 'CONSUMER_LOUDNESS' ),
+ ( ('CONS', 0x0E8), 'CONSUMER_MPX' ),
+ ( ('CONS', 0x0E9), 'CONSUMER_VOLUME_UP' ),
+ ( ('CONS', 0x0EA), 'CONSUMER_VOLUME_DOWN' ),
+# 0x0EB - 0x0EF Reserved
+ ( ('CONS', 0x0F0), 'CONSUMER_SPEED_SELECT' ),
+ ( ('CONS', 0x0F2), 'CONSUMER_STANDARD_PLAY' ),
+ ( ('CONS', 0x0F3), 'CONSUMER_LONG_PLAY' ),
+ ( ('CONS', 0x0F4), 'CONSUMER_EXTENDED_PLAY' ),
+ ( ('CONS', 0x0F5), 'CONSUMER_SLOW' ),
+# 0x0F6 - 0x0FF
+ ( ('CONS', 0x100), 'CONSUMER_FAN_ENABLE' ),
+
+ ( ('CONS', 0x102), 'CONSUMER_LIGHT_ENABLE' ),
+
+ ( ('CONS', 0x104), 'CONSUMER_CLIMATE_CONTROL_ENABLE' ),
+
+ ( ('CONS', 0x106), 'CONSUMER_SECURITY_ENABLE' ),
+ ( ('CONS', 0x107), 'CONSUMER_FIRE_ALARM' ),
+
+ ( ('CONS', 0x10A), 'CONSUMER_MOTION' ),
+ ( ('CONS', 0x10B), 'CONSUMER_DURESS_ALARM' ),
+ ( ('CONS', 0x10C), 'CONSUMER_HOLDUP_ALARM' ),
+ ( ('CONS', 0x10D), 'CONSUMER_MEDICAL_ALARM' ),
+# 0x10E - 0x14F Reserved
+ ( ('CONS', 0x150), 'CONSUMER_BALANCE_RIGHT' ),
+ ( ('CONS', 0x151), 'CONSUMER_BALANCE_LEFT' ),
+ ( ('CONS', 0x152), 'CONSUMER_BASS_INCR' ),
+ ( ('CONS', 0x153), 'CONSUMER_BASS_DECR' ),
+ ( ('CONS', 0x154), 'CONSUMER_TREBLE_INCR' ),
+ ( ('CONS', 0x155), 'CONSUMER_TREBLE_DECR' ),
+# 0x156 - 0x15F Reserved
+
+ ( ('CONS', 0x171), 'CONSUMER_SUB_CHANNEL_INCREMENT' ),
+ ( ('CONS', 0x172), 'CONSUMER_SUB_CHANNEL_DECREMENT' ),
+ ( ('CONS', 0x173), 'CONSUMER_ALT_AUDIO_INCREMENT' ),
+ ( ('CONS', 0x174), 'CONSUMER_ALT_AUDIO_DECREMENT' ),
+
+# List of Consumer Codes - USB HID 1.12v2
+# Application Launch Buttons pg 79
+ ( ('CONS', 0x181), 'AL_LAUNCH_BUTTON_CONFIG_TOOL' ),
+ ( ('CONS', 0x182), 'AL_PROGRAMMABLE_BUTTON_CONFIG' ),
+ ( ('CONS', 0x183), 'AL_CONSUMER_CONTROL_CONFIG' ),
+ ( ('CONS', 0x184), 'AL_WORD_PROCESSOR' ),
+ ( ('CONS', 0x185), 'AL_TEXT_EDITOR' ),
+ ( ('CONS', 0x186), 'AL_SPREADSHEET' ),
+ ( ('CONS', 0x187), 'AL_GRAPHICS_EDITOR' ),
+ ( ('CONS', 0x188), 'AL_PRESENTATION_APP' ),
+ ( ('CONS', 0x189), 'AL_DATABASE_APP' ),
+ ( ('CONS', 0x18A), 'AL_EMAIL_READER' ),
+ ( ('CONS', 0x18B), 'AL_NEWSREADER' ),
+ ( ('CONS', 0x18C), 'AL_VOICEMAIL' ),
+ ( ('CONS', 0x18D), 'AL_CONTACTS_ADDRESS_BOOK' ),
+ ( ('CONS', 0x18E), 'AL_CALENDAR_SCHEDULE' ),
+ ( ('CONS', 0x18F), 'AL_TASK_PROJECT_MANAGER' ),
+ ( ('CONS', 0x190), 'AL_LOG_JOURNAL_TIMECARD' ),
+ ( ('CONS', 0x191), 'AL_CHECKBOOK_FINANCE' ),
+ ( ('CONS', 0x192), 'AL_CALCULATOR' ),
+ ( ('CONS', 0x193), 'AL_A_V_CAPTURE_PLAYBACK' ),
+ ( ('CONS', 0x194), 'AL_LOCAL_MACHINE_BROWSER' ),
+ ( ('CONS', 0x195), 'AL_LAN_WAN_BROWSER' ),
+ ( ('CONS', 0x196), 'AL_INTERNET_BROWSER' ),
+ ( ('CONS', 0x197), 'AL_REMOTE_NETWORKING_ISP_CONNECT' ),
+ ( ('CONS', 0x198), 'AL_NETWORK_CONFERENCE' ),
+ ( ('CONS', 0x199), 'AL_NETWORK_CHAT' ),
+ ( ('CONS', 0x19A), 'AL_TELEPHONY_DIALER' ),
+ ( ('CONS', 0x19B), 'AL_LOGON' ),
+ ( ('CONS', 0x19C), 'AL_LOGOFF' ),
+ ( ('CONS', 0x19D), 'AL_LOGON_LOGOFF' ),
+ ( ('CONS', 0x19E), 'AL_TERMINAL_LOCK_SCREENSAVER' ),
+ ( ('CONS', 0x19F), 'AL_CONTROL_PANEL' ),
+ ( ('CONS', 0x1A0), 'AL_COMMAND_LINE_PROCESSOR_RUN' ),
+ ( ('CONS', 0x1A1), 'AL_PROCESS_TASK_MANAGER' ),
+ ( ('CONS', 0x1A2), 'AL_SELECT_TAST_APP' ),
+ ( ('CONS', 0x1A3), 'AL_NEXT_TASK_APP' ),
+ ( ('CONS', 0x1A4), 'AL_PREVIOUS_TASK_APP' ),
+ ( ('CONS', 0x1A5), 'AL_PREEMPTIVE_HALT_TASK_APP' ),
+ ( ('CONS', 0x1A6), 'AL_INTEGRATED_HELP_CENTER' ),
+ ( ('CONS', 0x1A7), 'AL_DOCUMENTS' ),
+ ( ('CONS', 0x1A8), 'AL_THESAURUS' ),
+ ( ('CONS', 0x1A9), 'AL_DICTIONARY' ),
+ ( ('CONS', 0x1AA), 'AL_DESKTOP' ),
+ ( ('CONS', 0x1AB), 'AL_SPELL_CHECK' ),
+ ( ('CONS', 0x1AC), 'AL_GRAMMAR_CHECK' ),
+ ( ('CONS', 0x1AD), 'AL_WIRELESS_STATUS' ),
+ ( ('CONS', 0x1AE), 'AL_KEYBOARD_LAYOUT' ),
+ ( ('CONS', 0x1AF), 'AL_VIRUS_PROTECTION' ),
+ ( ('CONS', 0x1B0), 'AL_ENCRYPTION' ),
+ ( ('CONS', 0x1B1), 'AL_SCREEN_SAVER' ),
+ ( ('CONS', 0x1B2), 'AL_ALARMS' ),
+ ( ('CONS', 0x1B3), 'AL_CLOCK' ),
+ ( ('CONS', 0x1B4), 'AL_FILE_BROWSER' ),
+ ( ('CONS', 0x1B5), 'AL_POWER_STATUS' ),
+ ( ('CONS', 0x1B6), 'AL_IMAGE_BROWSER' ),
+ ( ('CONS', 0x1B7), 'AL_AUDIO_BROWSER' ),
+ ( ('CONS', 0x1B8), 'AL_MOVIE_BROWSER' ),
+ ( ('CONS', 0x1B9), 'AL_DIGITAL_RIGHTS_MANAGER' ),
+ ( ('CONS', 0x1BA), 'AL_DIGITAL_WALLET' ),
+# 0x1BB Reserved
+ ( ('CONS', 0x1BC), 'AL_INSTANT_MESSAGING' ),
+ ( ('CONS', 0x1BD), 'AL_OEM_FEATURES_TIPS_TUTORIAL' ),
+ ( ('CONS', 0x1BE), 'AL_OEM_HELP' ),
+ ( ('CONS', 0x1BF), 'AL_ONLINE_COMMUNITY' ),
+ ( ('CONS', 0x1C0), 'AL_ENTERTAINMENT_CONTENT' ),
+ ( ('CONS', 0x1C1), 'AL_ONLINE_SHOPPING' ),
+ ( ('CONS', 0x1C2), 'AL_SMARTCARD_INFO_HELP' ),
+ ( ('CONS', 0x1C3), 'AL_MARKET_MONITOR' ),
+ ( ('CONS', 0x1C4), 'AL_CUSTOMIZED_CORP_NEWS' ),
+ ( ('CONS', 0x1C5), 'AL_ONLINE_ACTIVITY' ),
+ ( ('CONS', 0x1C6), 'AL_SEARCH_BROWSER' ),
+ ( ('CONS', 0x1C7), 'AL_AUDIO_PLAYER' ),
+
+# List of Consumer Codes - USB HID 1.12v2
+# Generic GUI Application Controls pg 82
+ ( ('CONS', 0x201), 'AC_NEW' ),
+ ( ('CONS', 0x202), 'AC_OPEN' ),
+ ( ('CONS', 0x203), 'AC_CLOSE' ),
+ ( ('CONS', 0x204), 'AC_EXIT' ),
+ ( ('CONS', 0x205), 'AC_MAXIMIZE' ),
+ ( ('CONS', 0x206), 'AC_MINIMIZE' ),
+ ( ('CONS', 0x207), 'AC_SAVE' ),
+ ( ('CONS', 0x208), 'AC_PRINT' ),
+ ( ('CONS', 0x209), 'AC_PROPERTIES' ),
+ ( ('CONS', 0x21A), 'AC_UNDO' ),
+ ( ('CONS', 0x21B), 'AC_COPY' ),
+ ( ('CONS', 0x21C), 'AC_CUT' ),
+ ( ('CONS', 0x21D), 'AC_PASTE' ),
+ ( ('CONS', 0x21E), 'AC_SELECT_ALL' ),
+ ( ('CONS', 0x21F), 'AC_FIND' ),
+ ( ('CONS', 0x220), 'AC_FIND_AND_REPLACE' ),
+ ( ('CONS', 0x221), 'AC_SEARCH' ),
+ ( ('CONS', 0x222), 'AC_GO_TO' ),
+ ( ('CONS', 0x223), 'AC_HOME' ),
+ ( ('CONS', 0x224), 'AC_BACK' ),
+ ( ('CONS', 0x225), 'AC_FORWARD' ),
+ ( ('CONS', 0x226), 'AC_STOP' ),
+ ( ('CONS', 0x227), 'AC_REFRESH' ),
+ ( ('CONS', 0x228), 'AC_PREVIOUS_LINK' ),
+ ( ('CONS', 0x229), 'AC_NEXT_LINK' ),
+ ( ('CONS', 0x22A), 'AC_BOOKMARKS' ),
+ ( ('CONS', 0x22B), 'AC_HISTORY' ),
+ ( ('CONS', 0x22C), 'AC_SUBSCRIPTIONS' ),
+ ( ('CONS', 0x22D), 'AC_ZOOM_IN' ),
+ ( ('CONS', 0x22E), 'AC_ZOOM_OUT' ),
+ ( ('CONS', 0x22F), 'AC_ZOOM' ),
+ ( ('CONS', 0x230), 'AC_FULL_SCREEN_VIEW' ),
+ ( ('CONS', 0x231), 'AC_NORMAL_VIEW' ),
+ ( ('CONS', 0x232), 'AC_VIEW_TOGGLE' ),
+ ( ('CONS', 0x233), 'AC_SCROLL_UP' ),
+ ( ('CONS', 0x234), 'AC_SCROLL_DOWN' ),
+ ( ('CONS', 0x235), 'AC_SCROLL' ),
+ ( ('CONS', 0x236), 'AC_PAN_LEFT' ),
+ ( ('CONS', 0x237), 'AC_PAN_RIGHT' ),
+ ( ('CONS', 0x238), 'AC_PAN' ),
+ ( ('CONS', 0x239), 'AC_NEW_WINDOW' ),
+ ( ('CONS', 0x23A), 'AC_TILE_HORIZONTALLY' ),
+ ( ('CONS', 0x23B), 'AC_TILE_VERTICALLY' ),
+ ( ('CONS', 0x23C), 'AC_FORMAT' ),
+ ( ('CONS', 0x23D), 'AC_EDIT' ),
+ ( ('CONS', 0x23E), 'AC_BOLD' ),
+ ( ('CONS', 0x23F), 'AC_ITALICS' ),
+ ( ('CONS', 0x240), 'AC_UNDERLINE' ),
+ ( ('CONS', 0x241), 'AC_STRIKETHROUGH' ),
+ ( ('CONS', 0x242), 'AC_SUBSCRIPT' ),
+ ( ('CONS', 0x243), 'AC_SUPERSCRIPT' ),
+ ( ('CONS', 0x244), 'AC_ALL_CAPS' ),
+ ( ('CONS', 0x245), 'AC_ROTATE' ),
+ ( ('CONS', 0x246), 'AC_RESIZE' ),
+ ( ('CONS', 0x247), 'AC_FILP_HORIZONTAL' ),
+ ( ('CONS', 0x248), 'AC_FILP_VERTICAL' ),
+ ( ('CONS', 0x249), 'AC_MIRROR_HORIZONTAL' ),
+ ( ('CONS', 0x24A), 'AC_MIRROR_VERTICAL' ),
+ ( ('CONS', 0x24B), 'AC_FONT_SELECT' ),
+ ( ('CONS', 0x24C), 'AC_FONT_COLOR' ),
+ ( ('CONS', 0x24D), 'AC_FONT_SIZE' ),
+ ( ('CONS', 0x24E), 'AC_JUSTIFY_LEFT' ),
+ ( ('CONS', 0x24F), 'AC_JUSTIFY_CENTER_H' ),
+ ( ('CONS', 0x250), 'AC_JUSTIFY_RIGHT' ),
+ ( ('CONS', 0x251), 'AC_JUSTIFY_BLOCK_H' ),
+ ( ('CONS', 0x252), 'AC_JUSTIFY_TOP' ),
+ ( ('CONS', 0x253), 'AC_JUSTIFY_CENTER_V' ),
+ ( ('CONS', 0x254), 'AC_JUSTIFY_BOTTOM' ),
+ ( ('CONS', 0x255), 'AC_JUSTIFY_BLOCK_V' ),
+ ( ('CONS', 0x256), 'AC_INDENT_DECREASE' ),
+ ( ('CONS', 0x257), 'AC_INDENT_INCREASE' ),
+ ( ('CONS', 0x258), 'AC_NUMBERED_LIST' ),
+ ( ('CONS', 0x259), 'AC_RESTART_NUMBERING' ),
+ ( ('CONS', 0x25A), 'AC_BULLETED_LIST' ),
+ ( ('CONS', 0x25B), 'AC_PROMOTE' ),
+ ( ('CONS', 0x25C), 'AC_DEMOTE' ),
+ ( ('CONS', 0x25D), 'AC_YES' ),
+ ( ('CONS', 0x25E), 'AC_NO' ),
+ ( ('CONS', 0x25F), 'AC_CANCEL' ),
+ ( ('CONS', 0x260), 'AC_CATALOG' ),
+ ( ('CONS', 0x261), 'AC_BUY_CHECKOUT' ),
+ ( ('CONS', 0x262), 'AC_ADD_TO_CART' ),
+ ( ('CONS', 0x263), 'AC_EXPAND' ),
+ ( ('CONS', 0x264), 'AC_EXPAND_ALL' ),
+ ( ('CONS', 0x265), 'AC_COLLAPSE' ),
+ ( ('CONS', 0x266), 'AC_COLLAPSE_ALL' ),
+ ( ('CONS', 0x267), 'AC_PRINT_PREVIEW' ),
+ ( ('CONS', 0x268), 'AC_PASTE_SPECIAL' ),
+ ( ('CONS', 0x269), 'AC_INSERT_MODE' ),
+ ( ('CONS', 0x26A), 'AC_DELETE' ),
+ ( ('CONS', 0x26B), 'AC_LOCK' ),
+ ( ('CONS', 0x26C), 'AC_UNLOCK' ),
+ ( ('CONS', 0x26D), 'AC_PROTECT' ),
+ ( ('CONS', 0x26E), 'AC_UNPROTECT' ),
+ ( ('CONS', 0x26F), 'AC_ATTACH_COMMENT' ),
+ ( ('CONS', 0x270), 'AC_DELETE_COMMENT' ),
+ ( ('CONS', 0x271), 'AC_VIEW_COMMENT' ),
+ ( ('CONS', 0x272), 'AC_SELECT_WORD' ),
+ ( ('CONS', 0x273), 'AC_SELECT_SENTENCE' ),
+ ( ('CONS', 0x274), 'AC_SELECT_PARAGRAPH' ),
+ ( ('CONS', 0x275), 'AC_SELECT_COLUMN' ),
+ ( ('CONS', 0x276), 'AC_SELECT_ROW' ),
+ ( ('CONS', 0x277), 'AC_SELECT_TABLE' ),
+ ( ('CONS', 0x278), 'AC_SELECT_OBJECT' ),
+ ( ('CONS', 0x279), 'AC_REDO_REPEAT' ),
+ ( ('CONS', 0x27A), 'AC_SORT' ),
+ ( ('CONS', 0x27B), 'AC_SORT_ASCENDING' ),
+ ( ('CONS', 0x27C), 'AC_SORT_DESCENDING' ),
+ ( ('CONS', 0x27D), 'AC_FILTER' ),
+ ( ('CONS', 0x27E), 'AC_SET_CLOCK' ),
+ ( ('CONS', 0x27F), 'AC_VIEW_CLOCK' ),
+ ( ('CONS', 0x280), 'AC_SELECT_TIME_ZONE' ),
+ ( ('CONS', 0x281), 'AC_EDIT_TIME_ZONE' ),
+ ( ('CONS', 0x282), 'AC_SET_ALARM' ),
+ ( ('CONS', 0x283), 'AC_CLEAR_ALARM' ),
+ ( ('CONS', 0x284), 'AC_SNOOZE_ALARM' ),
+ ( ('CONS', 0x285), 'AC_RESET_ALARM' ),
+ ( ('CONS', 0x286), 'AC_SYNCHRONIZE' ),
+ ( ('CONS', 0x287), 'AC_SEND_RECEIVE' ),
+ ( ('CONS', 0x288), 'AC_SEND_TO' ),
+ ( ('CONS', 0x289), 'AC_REPLY' ),
+ ( ('CONS', 0x28A), 'AC_REPLY_ALL' ),
+ ( ('CONS', 0x28B), 'AC_FORWARD_MSG' ),
+ ( ('CONS', 0x28C), 'AC_SEND' ),
+ ( ('CONS', 0x28D), 'AC_ATTACH_FILE' ),
+ ( ('CONS', 0x28E), 'AC_UPLOAD' ),
+ ( ('CONS', 0x28F), 'AC_DOWNLOAD' ),
+ ( ('CONS', 0x290), 'AC_SET_BORDERS' ),
+ ( ('CONS', 0x291), 'AC_INSERT_ROW' ),
+ ( ('CONS', 0x292), 'AC_INSERT_COLUMN' ),
+ ( ('CONS', 0x293), 'AC_INSERT_FILE' ),
+ ( ('CONS', 0x294), 'AC_INSERT_PICTURE' ),
+ ( ('CONS', 0x295), 'AC_INSERT_OBJECT' ),
+ ( ('CONS', 0x296), 'AC_INSERT_SYMBOL' ),
+ ( ('CONS', 0x297), 'AC_SAVE_AND_CLOSE' ),
+ ( ('CONS', 0x298), 'AC_RENAME' ),
+ ( ('CONS', 0x299), 'AC_MERGE' ),
+ ( ('CONS', 0x29A), 'AC_SPLIT' ),
+ ( ('CONS', 0x29B), 'AC_DISTRIBUTE_HORIZONTALLY' ),
+ ( ('CONS', 0x29C), 'AC_DISTRIBUTE_VERTICALLY' ),
+ ( ('CONS', 0x29D), 'AC_NEXT_KEYBOARD_LAYOUT_SEL' ),
+# 0x29E-0xFFFF Reserved
+
+ # USB HID LED Codes
+ ( ('IND', 0x00), 'LED_UNDEFINED' ),
+ ( ('IND', 0x01), 'LED_NUM_LOCK' ),
+ ( ('IND', 0x02), 'LED_CAPS_LOCK' ),
+ ( ('IND', 0x03), 'LED_SCROLL_LOCK' ),
+ ( ('IND', 0x04), 'LED_COMPOSE' ),
+ ( ('IND', 0x05), 'LED_KANA' ),
+ ( ('IND', 0x06), 'LED_POWER' ),
+ ( ('IND', 0x07), 'LED_SHIFT' ),
+ ( ('IND', 0x08), 'LED_DO_NOT_DISTURB' ),
+ ( ('IND', 0x09), 'LED_MUTE' ),
+ ( ('IND', 0x0A), 'LED_TONE_ENABLE' ),
+ ( ('IND', 0x0B), 'LED_HIGHCUT_FILTER' ),
+ ( ('IND', 0x0C), 'LED_LOWCUT_FILTER' ),
+ ( ('IND', 0x0D), 'LED_EQL_ENABLE' ),
+ ( ('IND', 0x0E), 'LED_SND_FLD_ON' ),
+ ( ('IND', 0x0F), 'LED_SURROUND_ON' ),
+ ( ('IND', 0x10), 'LED_REPEAT' ),
+ ( ('IND', 0x11), 'LED_STEREO' ),
+ ( ('IND', 0x12), 'LED_SAMPLE_RT_DET' ),
+ ( ('IND', 0x13), 'LED_SPINNING' ),
+ ( ('IND', 0x14), 'LED_CAV' ),
+ ( ('IND', 0x15), 'LED_CLV' ),
+ ( ('IND', 0x16), 'LED_REC_FMT_DET' ),
+ ( ('IND', 0x17), 'LED_OFF_HOOK' ),
+ ( ('IND', 0x18), 'LED_RING' ),
+ ( ('IND', 0x19), 'LED_MSG_WAITING' ),
+ ( ('IND', 0x1A), 'LED_DATA_MODE' ),
+ ( ('IND', 0x1B), 'LED_BAT_OPERATION' ),
+ ( ('IND', 0x1C), 'LED_BAT_OK' ),
+ ( ('IND', 0x1D), 'LED_BAT_LOW' ),
+ ( ('IND', 0x1E), 'LED_SPEAKER' ),
+ ( ('IND', 0x1F), 'LED_HEAD_SET' ),
+ ( ('IND', 0x20), 'LED_HOLD' ),
+ ( ('IND', 0x21), 'LED_MICROPHONE' ),
+ ( ('IND', 0x22), 'LED_COVERAGE' ),
+ ( ('IND', 0x23), 'LED_NIGHT_MODE' ),
+ ( ('IND', 0x24), 'LED_SEND_CALLS' ),
+ ( ('IND', 0x25), 'LED_CALL_PICKUP' ),
+ ( ('IND', 0x26), 'LED_CONFERENCE' ),
+ ( ('IND', 0x27), 'LED_STAND_BY' ),
+ ( ('IND', 0x28), 'LED_CAMERA_ON' ),
+ ( ('IND', 0x29), 'LED_CAMERA_OFF' ),
+ ( ('IND', 0x2A), 'LED_ON_LINE' ),
+ ( ('IND', 0x2B), 'LED_OFF_LINE' ),
+ ( ('IND', 0x2C), 'LED_BUSY' ),
+ ( ('IND', 0x2D), 'LED_READY' ),
+ ( ('IND', 0x2E), 'LED_PAPER_OUT' ),
+ ( ('IND', 0x2F), 'LED_PAPER_JAM' ),
+ ( ('IND', 0x30), 'LED_REMOTE' ),
+ ( ('IND', 0x31), 'LED_FORWARD' ),
+ ( ('IND', 0x32), 'LED_REVERSE' ),
+ ( ('IND', 0x33), 'LED_STOP' ),
+ ( ('IND', 0x34), 'LED_REWIND' ),
+ ( ('IND', 0x35), 'LED_FAST_FORWARD' ),
+ ( ('IND', 0x36), 'LED_PLAY' ),
+ ( ('IND', 0x37), 'LED_PAUSE' ),
+ ( ('IND', 0x38), 'LED_RECORD' ),
+ ( ('IND', 0x39), 'LED_ERROR' ),
+ ( ('IND', 0x3A), 'LED_USI' ),
+ ( ('IND', 0x3B), 'LED_UIUI' ),
+ ( ('IND', 0x3C), 'LED_UMMI' ),
+ ( ('IND', 0x3D), 'LED_IND_ON' ),
+ ( ('IND', 0x3E), 'LED_IND_FLASH' ),
+ ( ('IND', 0x3F), 'LED_IND_SLOW_BLNK' ),
+ ( ('IND', 0x40), 'LED_IND_FAST_BLNK' ),
+ ( ('IND', 0x41), 'LED_IND_OFF' ),
+ ( ('IND', 0x42), 'LED_FLASH_ON_TIME' ),
+ ( ('IND', 0x43), 'LED_SLW_B_ON_TIME' ),
+ ( ('IND', 0x44), 'LED_SLW_B_OFF_TIME' ),
+ ( ('IND', 0x45), 'LED_FST_B_ON_TIME' ),
+ ( ('IND', 0x46), 'LED_FST_B_OFF_TIME' ),
+ ( ('IND', 0x47), 'LED_UIC' ),
+ ( ('IND', 0x48), 'LED_IND_RED' ),
+ ( ('IND', 0x49), 'LED_IND_GREEN' ),
+ ( ('IND', 0x4A), 'LED_IND_AMBER' ),
+ ( ('IND', 0x4B), 'LED_GENERIC_IND' ),
+ ( ('IND', 0x4C), 'LED_SYS_SUSPEND' ),
+ ( ('IND', 0x4D), 'LED_EXT_PWR_CONN' ),
+# 0x4E - 0xFFFF Reserved
+
+ # USB HID System Control Codes
+# List of System Controls - USB HID 1.12v2 pg 32
+# NKRO HID Supports 0x81 - 0xB7
+ ( ('SYS', 0x81), 'SYS_POWER_DOWN' ),
+ ( ('SYS', 0x82), 'SYS_SLEEP' ),
+ ( ('SYS', 0x83), 'SYS_WAKE_UP' ),
+ ( ('SYS', 0x84), 'SYS_CONTEXT_MENU' ),
+ ( ('SYS', 0x85), 'SYS_MAIN_MENU' ),
+ ( ('SYS', 0x86), 'SYS_APP_MENU' ),
+ ( ('SYS', 0x87), 'SYS_MENU_HELP' ),
+ ( ('SYS', 0x88), 'SYS_MENU_EXIT' ),
+ ( ('SYS', 0x89), 'SYS_MENU_SELECT' ),
+ ( ('SYS', 0x8A), 'SYS_MENU_RIGHT' ),
+ ( ('SYS', 0x8B), 'SYS_MENU_LEFT' ),
+ ( ('SYS', 0x8C), 'SYS_MENU_UP' ),
+ ( ('SYS', 0x8D), 'SYS_MENU_DOWN' ),
+ ( ('SYS', 0x8E), 'SYS_COLD_RESTART' ),
+ ( ('SYS', 0x8F), 'SYS_WARM_RESTART' ),
+ ( ('SYS', 0x90), 'SYS_DPAD_UP' ),
+ ( ('SYS', 0x91), 'SYS_DPAD_DOWN' ),
+ ( ('SYS', 0x92), 'SYS_DPAD_RIGHT' ),
+ ( ('SYS', 0x93), 'SYS_DPAD_LEFT' ),
+# 0x94 - 0x9F Reserved
+ ( ('SYS', 0xA0), 'SYS_DOCK' ),
+ ( ('SYS', 0xA1), 'SYS_UNDOCK' ),
+ ( ('SYS', 0xA2), 'SYS_SETUP' ),
+ ( ('SYS', 0xA3), 'SYS_BREAK' ),
+ ( ('SYS', 0xA4), 'SYS_DEBUGGER_BREAK' ),
+ ( ('SYS', 0xA5), 'SYS_APP_BREAK' ),
+ ( ('SYS', 0xA6), 'SYS_APP_DEBUGGER_BREAK' ),
+ ( ('SYS', 0xA7), 'SYS_SPEAKER_MUTE' ),
+ ( ('SYS', 0xA8), 'SYS_HIBERNATE' ),
+# 0xA9 - 0xAF Reserved
+ ( ('SYS', 0xB0), 'SYS_DISP_INVERT' ),
+ ( ('SYS', 0xB1), 'SYS_DISP_INTERNAL' ),
+ ( ('SYS', 0xB2), 'SYS_DISP_EXTERNAL' ),
+ ( ('SYS', 0xB3), 'SYS_DISP_BOTH' ),
+ ( ('SYS', 0xB4), 'SYS_DISP_DUAL' ),
+ ( ('SYS', 0xB5), 'SYS_DISP_TOGGLE_INT_EXT' ),
+ ( ('SYS', 0xB6), 'SYS_DISP_SWAP_PRI_SEC' ),
+ ( ('SYS', 0xB7), 'SYS_DISP_LCD_AUTOSCALE' ),
+# 0xB8 - 0xFFFF Reserved
+])
+
+
+
+# Lookup for KLL defined HID values, internally the compiler uses numbers to combine the keymaps
+kll_hid_lookup_dictionary = dict()
+kll_hid_lookup_dictionary['USBCode'] = dict([
+ # USB HID Keyboard Codes
+ ( 'A', ('USB', 0x04) ),
+ ( 'B', ('USB', 0x05) ),
+ ( 'C', ('USB', 0x06) ),
+ ( 'D', ('USB', 0x07) ),
+ ( 'E', ('USB', 0x08) ),
+ ( 'F', ('USB', 0x09) ),
+ ( 'G', ('USB', 0x0A) ),
+ ( 'H', ('USB', 0x0B) ),
+ ( 'I', ('USB', 0x0C) ),
+ ( 'J', ('USB', 0x0D) ),
+ ( 'K', ('USB', 0x0E) ),
+ ( 'L', ('USB', 0x0F) ),
+ ( 'M', ('USB', 0x10) ),
+ ( 'N', ('USB', 0x11) ),
+ ( 'O', ('USB', 0x12) ),
+ ( 'P', ('USB', 0x13) ),
+ ( 'Q', ('USB', 0x14) ),
+ ( 'R', ('USB', 0x15) ),
+ ( 'S', ('USB', 0x16) ),
+ ( 'T', ('USB', 0x17) ),
+ ( 'U', ('USB', 0x18) ),
+ ( 'V', ('USB', 0x19) ),
+ ( 'W', ('USB', 0x1A) ),
+ ( 'X', ('USB', 0x1B) ),
+ ( 'Y', ('USB', 0x1C) ),
+ ( 'Z', ('USB', 0x1D) ),
+ ( '1', ('USB', 0x1E) ),
+ ( '2', ('USB', 0x1F) ),
+ ( '3', ('USB', 0x20) ),
+ ( '4', ('USB', 0x21) ),
+ ( '5', ('USB', 0x22) ),
+ ( '6', ('USB', 0x23) ),
+ ( '7', ('USB', 0x24) ),
+ ( '8', ('USB', 0x25) ),
+ ( '9', ('USB', 0x26) ),
+ ( '0', ('USB', 0x27) ),
+ ( 'ENTER', ('USB', 0x28) ),
+ ( 'ESC', ('USB', 0x29) ), ( 'ESCAPE', ('USB', 0x29) ),
+ ( 'BACKSPACE', ('USB', 0x2A) ),
+ ( 'TAB', ('USB', 0x2B) ),
+ ( 'SPACE', ('USB', 0x2C) ), ( 'SPACEBAR', ('USB', 0x2C) ),
+ ( '-', ('USB', 0x2D) ), ( 'MINUS', ('USB', 0x2D) ),
+ ( '=', ('USB', 0x2E) ), ( 'EQUALS', ('USB', 0x2E) ), ( 'EQUAL', ('USB', 0x2E) ),
+ ( '[', ('USB', 0x2F) ), ( 'LEFT BRACKET', ('USB', 0x2F) ), ( 'LBRACKET', ('USB', 0x2F) ), ( 'LEFT BRACE', ('USB', 0x2F) ), ( 'LBRACE', ('USB', 0x2F) ),
+ ( ']', ('USB', 0x30) ), ( 'RIGHT BRACKET', ('USB', 0x30) ), ( 'RBRACKET', ('USB', 0x30) ), ( 'RIGHT BRACE', ('USB', 0x30) ), ( 'RBRACE', ('USB', 0x30) ),
+ ( '\\', ('USB', 0x31) ), ( 'BACKSLASH', ('USB', 0x31) ),
+ ( '#', ('USB', 0x32) ), ( 'NUMBER', ('USB', 0x32) ), ( 'HASH', ('USB', 0x32) ),
+ ( ';', ('USB', 0x33) ), ( 'SEMICOLON', ('USB', 0x33) ),
+ ( "'", ('USB', 0x34) ), ( 'QUOTE', ('USB', 0x34) ), ( 'SINGLE QUOTE', ('USB', 0x34) ),
+ ( '`', ('USB', 0x35) ), ( 'BACKTICK', ('USB', 0x35) ),
+ ( ',', ('USB', 0x36) ), ( 'COMMA', ('USB', 0x36) ),
+ ( '.', ('USB', 0x37) ), ( 'PERIOD', ('USB', 0x37) ),
+ ( '/', ('USB', 0x38) ), ( 'SLASH', ('USB', 0x38) ),
+ ( 'CAPSLOCK', ('USB', 0x39) ), { 'CAPS LOCK', ('USB', 0x39) },
+ ( 'F1', ('USB', 0x3A) ),
+ ( 'F2', ('USB', 0x3B) ),
+ ( 'F3', ('USB', 0x3C) ),
+ ( 'F4', ('USB', 0x3D) ),
+ ( 'F5', ('USB', 0x3E) ),
+ ( 'F6', ('USB', 0x3F) ),
+ ( 'F7', ('USB', 0x40) ),
+ ( 'F8', ('USB', 0x41) ),
+ ( 'F9', ('USB', 0x42) ),
+ ( 'F10', ('USB', 0x43) ),
+ ( 'F11', ('USB', 0x44) ),
+ ( 'F12', ('USB', 0x45) ),
+ ( 'PRINTSCREEN', ('USB', 0x46) ), ( 'PRINT SCREEN', ('USB', 0x46) ),
+ ( 'SCROLLLOCK', ('USB', 0x47) ), ( 'SCROLL LOCK', ('USB', 0x47) ),
+ ( 'PAUSE', ('USB', 0x48) ),
+ ( 'INSERT', ('USB', 0x49) ),
+ ( 'HOME', ('USB', 0x4A) ),
+ ( 'PAGEUP', ('USB', 0x4B) ), ( 'PAGE UP', ('USB', 0x4B) ),
+ ( 'DELETE', ('USB', 0x4C) ),
+ ( 'END', ('USB', 0x4D) ),
+ ( 'PAGEDOWN', ('USB', 0x4E) ), ( 'PAGE DOWN', ('USB', 0x4E) ),
+ ( 'RIGHT', ('USB', 0x4F) ),
+ ( 'LEFT', ('USB', 0x50) ),
+ ( 'DOWN', ('USB', 0x51) ),
+ ( 'UP', ('USB', 0x52) ),
+ ( 'NUMLOCK', ('USB', 0x53) ), ( 'NUM LOCK', ('USB', 0x53) ),
+ ( 'P/', ('USB', 0x54) ), ( 'KEYPAD SLASH', ('USB', 0x54) ),
+ ( 'P*', ('USB', 0x55) ), ( 'KEYPAD ASTERIX', ('USB', 0x55) ), ( 'KEYPAD ASTERISK', ('USB', 0x55) ),
+ ( 'P-', ('USB', 0x56) ), ( 'KEYPAD MINUS', ('USB', 0x56) ),
+ ( 'P+', ('USB', 0x57) ), ( 'KEYPAD PLUS', ('USB', 0x57) ),
+ ( 'PENTER', ('USB', 0x58) ), ( 'KEYPAD ENTER', ('USB', 0x58) ),
+ ( 'P1', ('USB', 0x59) ), ( 'KEYPAD 1', ('USB', 0x59) ),
+ ( 'P2', ('USB', 0x5A) ), ( 'KEYPAD 2', ('USB', 0x5A) ),
+ ( 'P3', ('USB', 0x5B) ), ( 'KEYPAD 3', ('USB', 0x5B) ),
+ ( 'P4', ('USB', 0x5C) ), ( 'KEYPAD 4', ('USB', 0x5C) ),
+ ( 'P5', ('USB', 0x5D) ), ( 'KEYPAD 5', ('USB', 0x5D) ),
+ ( 'P6', ('USB', 0x5E) ), ( 'KEYPAD 6', ('USB', 0x5E) ),
+ ( 'P7', ('USB', 0x5F) ), ( 'KEYPAD 7', ('USB', 0x5F) ),
+ ( 'P8', ('USB', 0x60) ), ( 'KEYPAD 8', ('USB', 0x60) ),
+ ( 'P9', ('USB', 0x61) ), ( 'KEYPAD 9', ('USB', 0x61) ),
+ ( 'P0', ('USB', 0x62) ), ( 'KEYPAD 0', ('USB', 0x62) ),
+ ( 'P.', ('USB', 0x63) ), ( 'KEYPAD PERIOD', ('USB', 0x63) ),
+ ( 'ISO/', ('USB', 0x64) ), ( 'ISO SLASH', ('USB', 0x64) ),
+ ( 'APP', ('USB', 0x65) ),
+
+ ( 'P=', ('USB', 0x67) ), ( 'KEYPAD EQUAL', ('USB', 0x67) ),
+ ( 'F13', ('USB', 0x68) ),
+ ( 'F14', ('USB', 0x69) ),
+ ( 'F15', ('USB', 0x6A) ),
+ ( 'F16', ('USB', 0x6B) ),
+ ( 'F17', ('USB', 0x6C) ),
+ ( 'F18', ('USB', 0x6D) ),
+ ( 'F19', ('USB', 0x6E) ),
+ ( 'F20', ('USB', 0x6F) ),
+ ( 'F21', ('USB', 0x70) ),
+ ( 'F22', ('USB', 0x71) ),
+ ( 'F23', ('USB', 0x72) ),
+ ( 'F24', ('USB', 0x73) ),
+ ( 'EXEC', ('USB', 0x74) ),
+ ( 'HELP', ('USB', 0x75) ),
+ ( 'MENU', ('USB', 0x76) ),
+ ( 'SELECT', ('USB', 0x77) ),
+ ( 'STOP', ('USB', 0x78) ),
+ ( 'AGAIN', ('USB', 0x79) ),
+ ( 'UNDO', ('USB', 0x7A) ),
+ ( 'CUT', ('USB', 0x7B) ),
+ ( 'COPY', ('USB', 0x7C) ),
+ ( 'PASTE', ('USB', 0x7D) ),
+ ( 'FIND', ('USB', 0x7E) ),
+ ( 'MUTE', ('USB', 0x7F) ),
+ ( 'VOLUMEUP', ('USB', 0x80) ), ( 'VOLUME UP', ('USB', 0x80) ),
+ ( 'VOLUMEDOWN', ('USB', 0x81) ), ( 'VOLUME DOWN', ('USB', 0x81) ),
+ ( 'CAPSTOGGLELOCK', ('USB', 0x82) ), ( 'CAPS TOGGLE LOCK', ('USB', 0x82) ),
+ ( 'NUMTOGGLELOCK', ('USB', 0x83) ), ( 'NUM TOGGLE LOCK', ('USB', 0x83) ),
+ ( 'SCROLLTOGGLELOCK', ('USB', 0x84) ), ( 'SCROLL TOGGLE LOCK', ('USB', 0x84) ),
+ ( 'P,', ('USB', 0x85) ),
+ ( 'KEYPAD AS400 EQUAL', ('USB', 0x86) ),
+ ( 'INTER1', ('USB', 0x87) ), ( 'KANJI1', ('USB', 0x87) ),
+ ( 'INTER2', ('USB', 0x88) ), ( 'KANJI2', ('USB', 0x88) ), ( 'KANA', ('USB', 0x88) ), ( 'カナ', ('USB', 0x88) ),
+ ( 'INTER3', ('USB', 0x89) ), ( 'KANJI3', ('USB', 0x89) ), ( 'YEN', ('USB', 0x89) ), ( '¥', ('USB', 0x89) ),
+ ( 'INTER4', ('USB', 0x8A) ), ( 'KANJI4', ('USB', 0x8A) ), ( 'HENKAN', ('USB', 0x8A) ), ( '変換', ('USB', 0x8A) ),
+ ( 'INTER5', ('USB', 0x8B) ), ( 'KANJI5', ('USB', 0x8B) ), ( 'MUHENKAN', ('USB', 0x8B) ), ( '無変換', ('USB', 0x8B) ),
+ ( 'INTER6', ('USB', 0x8C) ), ( 'KANJI6', ('USB', 0x8C) ),
+ ( 'INTER7', ('USB', 0x8D) ), ( 'KANJI7', ('USB', 0x8D) ), ( 'BYTETOGGLE', ('USB', 0x8D) ),
+ ( 'INTER8', ('USB', 0x8E) ), ( 'KANJI8', ('USB', 0x8E) ),
+ ( 'INTER9', ('USB', 0x8F) ), ( 'KANJI9', ('USB', 0x8F) ),
+ ( 'LANG1', ('USB', 0x90) ), ( 'HANGULENGLISH', ('USB', 0x90) ), ( 'HANGUL ENGLISH', ('USB', 0x90) ), ( '한/영', ('USB', 0x90) ),
+ ( 'LANG2', ('USB', 0x91) ), ( 'HANJA', ('USB', 0x91) ), ( 'EISU', ('USB', 0x91) ), ( '英数/한자', ('USB', 0x91) ),
+ ( 'LANG3', ('USB', 0x92) ), ( 'KATAKANA', ('USB', 0x92) ), ( 'カタカナ', ('USB', 0x92) ),
+ ( 'LANG4', ('USB', 0x93) ), ( 'HIRAGANA', ('USB', 0x93) ), ( 'ひらがな', ('USB', 0x92) ),
+ ( 'LANG5', ('USB', 0x94) ), ( 'ZENKAKUHANKAKU', ('USB', 0x94) ), ( 'ZENKAKU HANKAKU', ('USB', 0x94) ), ( '半角/全角', ('USB', 0x94) ),
+ ( 'LANG6', ('USB', 0x95) ),
+ ( 'LANG7', ('USB', 0x96) ),
+ ( 'LANG8', ('USB', 0x97) ),
+ ( 'LANG9', ('USB', 0x98) ),
+ ( 'ALTERASE', ('USB', 0x99) ), ( 'ALT ERASE', ('USB', 0x99) ),
+ ( 'SYSREQATT', ('USB', 0x9A) ), ( 'SYSREQ', ('USB', 0x9A) ), ( 'SYSTEM REQUEST', ('USB', 0x9A) ),
+ ( 'CANCEL', ('USB', 0x9B) ),
+ ( 'CLEAR', ('USB', 0x9C) ),
+ ( 'PRIOR', ('USB', 0x9D) ),
+ ( 'RETURN', ('USB', 0x9E) ),
+ ( 'SEP', ('USB', 0x9F) ), ( 'SEPARATOR', ('USB', 0x9F) ),
+ ( 'OUT', ('USB', 0xA0) ),
+ ( 'OPER', ('USB', 0xA1) ),
+ ( 'CLEAR AGAIN', ('USB', 0xA2) ),
+ ( 'CRSEL PROPS', ('USB', 0xA3) ),
+ ( 'EXSEL', ('USB', 0xA4) ),
+
+ ( 'P00', ('USB', 0xB0) ), ( 'KEYPAD 00', ('USB', 0xB0) ),
+ ( 'P000', ('USB', 0xB1) ), ( 'KEYPAD 000', ('USB', 0xB1) ),
+ ( '1000SEP', ('USB', 0xB2) ), ( 'THOUSANDSEPARATOR', ('USB', 0xB2) ), ( 'THOUSAND SEPARATOR', ('USB', 0xB2) ),
+ ( 'DECIMALSEP', ('USB', 0xB3) ), ( 'DECIMALSEPARATOR', ('USB', 0xB3) ), ( 'DECIMAL SEPARATOR', ('USB', 0xB3) ),
+ ( 'CURRENCY', ('USB', 0xB4) ), ( 'CURRENCYUNIT', ('USB', 0xB4) ), ( 'CURRENCY UNIT', ('USB', 0xB4) ),
+ ( 'CURRENCYSUB', ('USB', 0xB5) ), ( 'CURRENCYSUBUNIT', ('USB', 0xB5) ), ( 'CURRENCY SUB UNIT', ('USB', 0xB5) ),
+ ( 'P(', ('USB', 0xB6) ), ( 'KEYPAD LEFT PARENTHESES', ('USB', 0xB6) ),
+ ( 'P)', ('USB', 0xB7) ), ( 'KEYPAD RIGHT PARENTHESES', ('USB', 0xB7) ),
+ ( 'P{', ('USB', 0xB8) ), ( 'KEYPAD LEFT BRACE', ('USB', 0xB8) ),
+ ( 'P}', ('USB', 0xB9) ), ( 'KEYPAD RIGHT BRACE', ('USB', 0xB9) ),
+ ( 'PTAB', ('USB', 0xBA) ), ( 'KEYPAD TAB', ('USB', 0xBA) ),
+ ( 'PBACKSPACE', ('USB', 0xBB) ), ( 'KEYPAD BACKSPACE', ('USB', 0xBB) ),
+ ( 'PA', ('USB', 0xBC) ), ( 'KEYPAD A', ('USB', 0xBC) ),
+ ( 'PB', ('USB', 0xBD) ), ( 'KEYPAD B', ('USB', 0xBD) ),
+ ( 'PC', ('USB', 0xBE) ), ( 'KEYPAD C', ('USB', 0xBE) ),
+ ( 'PD', ('USB', 0xBF) ), ( 'KEYPAD D', ('USB', 0xBF) ),
+ ( 'PE', ('USB', 0xC0) ), ( 'KEYPAD E', ('USB', 0xC0) ),
+ ( 'PF', ('USB', 0xC1) ), ( 'KEYPAD F', ('USB', 0xC1) ),
+ ( 'PXOR', ('USB', 0xC2) ), ( 'KEYPAD XOR', ('USB', 0xC2) ),
+ ( 'P^', ('USB', 0xC3) ), ( 'KEYPAD CHEVRON', ('USB', 0xC3) ),
+ ( 'P%', ('USB', 0xC4) ), ( 'KEYPAD PERCENT', ('USB', 0xC4) ),
+ ( 'P<', ('USB', 0xC5) ), ( 'KEYPAD LESSTHAN', ('USB', 0xC5) ), ( 'KEYPAD LESS THAN', ('USB', 0xC5) ),
+ ( 'P>', ('USB', 0xC6) ), ( 'KEYPAD GREATERTHAN', ('USB', 0xC6) ), ( 'KEYPAD GREATER THAN', ('USB', 0xC6) ),
+ ( 'P&', ('USB', 0xC7) ), ( 'KEYPAD BITAND', ('USB', 0xC7) ), ( 'KEYPAD BIT AND', ('USB', 0xC7) ),
+ ( 'P&&', ('USB', 0xC8) ), ( 'KEYPAD AND', ('USB', 0xC8) ),
+ ( 'P|', ('USB', 0xC9) ), ( 'KEYPAD BITOR', ('USB', 0xC9) ), ( 'KEYPAD BIT OR', ('USB', 0xC9) ),
+ ( 'P||', ('USB', 0xCA) ), ( 'KEYPAD OR', ('USB', 0xCA) ),
+ ( 'P:', ('USB', 0xCB) ), ( 'KEYPAD COLON', ('USB', 0xCB) ),
+ ( 'P#', ('USB', 0xCC) ), ( 'KEYPAD NUMBER', ('USB', 0xCC) ), ( 'KEYPAD HASH', ('USB', 0xCC) ),
+ ( 'PSPACE', ('USB', 0xCD) ), ( 'KEYPAD SPACE', ('USB', 0xCD) ),
+ ( 'P@', ('USB', 0xCE) ), ( 'KEYPAD AT', ('USB', 0xCE) ),
+ ( 'P!', ('USB', 0xCF) ), ( 'KEYPAD EXCLAIM', ('USB', 0xCF) ),
+ ( 'PMEMSTORE', ('USB', 0xD0) ), ( 'KEYPAD MEMSTORE', ('USB', 0xD0) ), ( 'KEYPAD MEMORY STORE', ('USB', 0xD0) ),
+ ( 'PMEMRECALL', ('USB', 0xD1) ), ( 'KEYPAD MEMRECALL', ('USB', 0xD1) ), ( 'KEYPAD MEMORY RECALL', ('USB', 0xD1) ),
+ ( 'PMEMCLEAR', ('USB', 0xD2) ), ( 'KEYPAD MEMCLEAR', ('USB', 0xD2) ), ( 'KEYPAD MEMORY CLEAR', ('USB', 0xD2) ),
+ ( 'PMEMADD', ('USB', 0xD3) ), ( 'KEYPAD MEMADD', ('USB', 0xD3) ), ( 'KEYPAD MEMORY ADD', ('USB', 0xD3) ),
+ ( 'PMEMSUB', ('USB', 0xD4) ), ( 'KEYPAD MEMSUB', ('USB', 0xD4) ), ( 'KEYPAD MEMORY SUB', ('USB', 0xD4) ),
+ ( 'PMEMMULT', ('USB', 0xD5) ), ( 'KEYPAD MEMMULT', ('USB', 0xD5) ), ( 'KEYPAD MEMORY MULTIPLY', ('USB', 0xD5) ),
+ ( 'PMEMDIV', ('USB', 0xD6) ), ( 'KEYPAD MEMDIV', ('USB', 0xD6) ), ( 'KEYPAD MEMORY DIVIDE', ('USB', 0xD6) ),
+ ( 'P+/-', ('USB', 0xD7) ), ( 'KEYPAD PLUSMINUS', ('USB', 0xD7) ), ( 'KEYPAD PLUS MINUS', ('USB', 0xD7) ),
+ ( 'PCLEAR', ('USB', 0xD8) ), ( 'KEYPAD CLEAR', ('USB', 0xD8) ),
+ ( 'PCLEARENTRY', ('USB', 0xD9) ), ( 'KEYPAD CLEARENTRY', ('USB', 0xD9) ), ( 'KEYPAD CLEAR ENTRY', ('USB', 0xD9) ),
+ ( 'PBINARY', ('USB', 0xDA) ), ( 'KEYPAD BINARY', ('USB', 0xDA) ),
+ ( 'POCTAL', ('USB', 0xDB) ), ( 'KEYPAD OCTAL', ('USB', 0xDB) ),
+ ( 'PDECIMAL', ('USB', 0xDC) ), ( 'KEYPAD DECIMAL', ('USB', 0xDC) ),
+ ( 'PHEX', ('USB', 0xDD) ), ( 'KEYPAD HEX', ('USB', 0xDD) ),
+
+ ( 'LCTRL', ('USB', 0xE0) ), ( 'LEFT CTRL', ('USB', 0xE0) ), ( 'CTRL', ('USB', 0xE0) ), ( 'CONTROL', ('USB', 0xE0) ), ( 'LEFT CONTROL', ('USB', 0xE0) ),
+ ( 'LSHIFT', ('USB', 0xE1) ), ( 'LEFT SHIFT', ('USB', 0xE1) ), ( 'SHIFT', ('USB', 0xE1) ),
+ ( 'LALT', ('USB', 0xE2) ), ( 'LEFT ALT', ('USB', 0xE2) ), ( 'ALT', ('USB', 0xE2) ), ( 'ALTERNATE', ('USB', 0xE2) ), ( 'LEFT ALTERNATE', ('USB', 0xE2) ),
+ ( 'LGUI', ('USB', 0xE3) ), ( 'LEFT GUI', ('USB', 0xE3) ), ( 'GUI', ('USB', 0xE3) ), ( 'SUPER', ('USB', 0xE3) ), ( 'LEFT SUPER', ('USB', 0xE3) ), ( 'WINDOWS', ('USB', 0xE3) ), ( 'LEFT WINDOWS', ('USB', 0xE3) ), ( 'WIN', ('USB', 0xE3) ), ( 'LEFT WIN', ('USB', 0xE3) ),
+ ( 'RCTRL', ('USB', 0xE4) ), ( 'RIGHT CTRL', ('USB', 0xE4) ), ( 'RIGHT CONTROL', ('USB', 0xE4) ),
+ ( 'RSHIFT', ('USB', 0xE5) ), ( 'RIGHT SHIFT', ('USB', 0xE5) ),
+ ( 'RALT', ('USB', 0xE6) ), ( 'RIGHT ALT', ('USB', 0xE6) ), ( 'RIGHT ALTERNATE', ('USB', 0xE6) ),
+ ( 'RGUI', ('USB', 0xE7) ), ( 'RIGHT GUI', ('USB', 0xE7) ), ( 'RIGHT SUPER', ('USB', 0xE7) ), ( 'RIGHT WINDOWS', ('USB', 0xE7) ), ( 'RIGHT WIN', ('USB', 0xE7) ),
+
+# Special Function Shift/Lock/Latch symbolic names (not part of the USB HID spec)
+ ( 'FUN1', ('USB', 0xF0) ), ( 'FUNCTION1', ('USB', 0xF0) ), ( 'FUN', ('USB', 0xF0) ),
+ ( 'FUN2', ('USB', 0xF1) ), ( 'FUNCTION2', ('USB', 0xF1) ),
+ ( 'FUN3', ('USB', 0xF2) ), ( 'FUNCTION3', ('USB', 0xF2) ),
+ ( 'FUN4', ('USB', 0xF3) ), ( 'FUNCTION4', ('USB', 0xF3) ),
+ ( 'FUN5', ('USB', 0xF4) ), ( 'FUNCTION5', ('USB', 0xF4) ),
+ ( 'FUN6', ('USB', 0xF5) ), ( 'FUNCTION6', ('USB', 0xF5) ),
+ ( 'FUN7', ('USB', 0xF6) ), ( 'FUNCTION7', ('USB', 0xF6) ),
+ ( 'FUN8', ('USB', 0xF7) ), ( 'FUNCTION8', ('USB', 0xF7) ),
+ ( 'FUN9', ('USB', 0xF8) ), ( 'FUNCTION9', ('USB', 0xF8) ),
+ ( 'FUN10', ('USB', 0xF9) ), ( 'FUNCTION10', ('USB', 0xF9) ),
+ ( 'FUN11', ('USB', 0xFA) ), ( 'FUNCTION11', ('USB', 0xFA) ),
+ ( 'FUN12', ('USB', 0xFB) ), ( 'FUNCTION12', ('USB', 0xFB) ),
+ ( 'FUN13', ('USB', 0xFC) ), ( 'FUNCTION13', ('USB', 0xFC) ),
+ ( 'FUN14', ('USB', 0xFD) ), ( 'FUNCTION14', ('USB', 0xFD) ),
+ ( 'FUN15', ('USB', 0xFE) ), ( 'FUNCTION15', ('USB', 0xFE) ),
+ ( 'FUN16', ('USB', 0xFF) ), ( 'FUNCTION16', ('USB', 0xFF) ),
+ ( 'LCK1', ('USB', 0x100) ), ( 'LOCK1', ('USB', 0x100) ), ( 'LCK', ('USB', 0x100) ),
+ ( 'LCK2', ('USB', 0x101) ), ( 'LOCK2', ('USB', 0x101) ),
+ ( 'LCK3', ('USB', 0x102) ), ( 'LOCK3', ('USB', 0x102) ),
+ ( 'LCK4', ('USB', 0x103) ), ( 'LOCK4', ('USB', 0x103) ),
+ ( 'LCK5', ('USB', 0x104) ), ( 'LOCK5', ('USB', 0x104) ),
+ ( 'LCK6', ('USB', 0x105) ), ( 'LOCK6', ('USB', 0x105) ),
+ ( 'LCK7', ('USB', 0x106) ), ( 'LOCK7', ('USB', 0x106) ),
+ ( 'LCK8', ('USB', 0x107) ), ( 'LOCK8', ('USB', 0x107) ),
+ ( 'LCK9', ('USB', 0x108) ), ( 'LOCK9', ('USB', 0x108) ),
+ ( 'LCK10', ('USB', 0x109) ), ( 'LOCK10', ('USB', 0x109) ),
+ ( 'LCK11', ('USB', 0x10A) ), ( 'LOCK11', ('USB', 0x10A) ),
+ ( 'LCK12', ('USB', 0x10B) ), ( 'LOCK12', ('USB', 0x10B) ),
+ ( 'LCK13', ('USB', 0x10C) ), ( 'LOCK13', ('USB', 0x10C) ),
+ ( 'LCK14', ('USB', 0x10D) ), ( 'LOCK14', ('USB', 0x10D) ),
+ ( 'LCK15', ('USB', 0x10E) ), ( 'LOCK15', ('USB', 0x10E) ),
+ ( 'LCK16', ('USB', 0x10F) ), ( 'LOCK16', ('USB', 0x10F) ),
+ ( 'LAT1', ('USB', 0x110) ), ( 'LATCH1', ('USB', 0x110) ), ( 'LAT', ('USB', 0x110) ),
+ ( 'LAT2', ('USB', 0x111) ), ( 'LATCH2', ('USB', 0x111) ),
+ ( 'LAT3', ('USB', 0x112) ), ( 'LATCH3', ('USB', 0x112) ),
+ ( 'LAT4', ('USB', 0x113) ), ( 'LATCH4', ('USB', 0x113) ),
+ ( 'LAT5', ('USB', 0x114) ), ( 'LATCH5', ('USB', 0x114) ),
+ ( 'LAT6', ('USB', 0x115) ), ( 'LATCH6', ('USB', 0x115) ),
+ ( 'LAT7', ('USB', 0x116) ), ( 'LATCH7', ('USB', 0x116) ),
+ ( 'LAT8', ('USB', 0x117) ), ( 'LATCH8', ('USB', 0x117) ),
+ ( 'LAT9', ('USB', 0x118) ), ( 'LATCH9', ('USB', 0x118) ),
+ ( 'LAT10', ('USB', 0x119) ), ( 'LATCH10', ('USB', 0x119) ),
+ ( 'LAT11', ('USB', 0x11A) ), ( 'LATCH11', ('USB', 0x11A) ),
+ ( 'LAT12', ('USB', 0x11B) ), ( 'LATCH12', ('USB', 0x11B) ),
+ ( 'LAT13', ('USB', 0x11C) ), ( 'LATCH13', ('USB', 0x11C) ),
+ ( 'LAT14', ('USB', 0x11D) ), ( 'LATCH14', ('USB', 0x11D) ),
+ ( 'LAT15', ('USB', 0x11E) ), ( 'LATCH15', ('USB', 0x11E) ),
+ ( 'LAT16', ('USB', 0x11F) ), ( 'LATCH16', ('USB', 0x11F) ),
+ ( 'NLAYER', ('USB', 0x120) ), ( 'NEXT LAYER', ('USB', 0x120) ),
+ ( 'PLAYER', ('USB', 0x121) ), ( 'PREV LAYER', ('USB', 0x121) ),
+])
+
+
+ # USB HID LED Codes
+kll_hid_lookup_dictionary['IndCode'] = dict([
+ ( 'UNDEFINED', ('IND', 0x00) ),
+ ( 'NUMLOCK', ('IND', 0x01) ),
+ ( 'CAPSLOCK', ('IND', 0x02) ),
+ ( 'SCROLLLOCK', ('IND', 0x03) ),
+ ( 'COMPOSE', ('IND', 0x04) ),
+ ( 'KANA', ('IND', 0x05) ),
+ ( 'POWER', ('IND', 0x06) ),
+ ( 'SHIFT', ('IND', 0x07) ),
+ ( 'DONOT_DISTURB', ('IND', 0x08) ),
+ ( 'MUTE', ('IND', 0x09) ),
+ ( 'TONEENABLE', ('IND', 0x0A) ),
+ ( 'HIGHCUTFILTER', ('IND', 0x0B) ),
+ ( 'LOWCUTFILTER', ('IND', 0x0C) ),
+ ( 'EQLENABLE', ('IND', 0x0D) ),
+ ( 'SNDFLD_ON', ('IND', 0x0E) ),
+ ( 'SURROUNDON', ('IND', 0x0F) ),
+ ( 'REPEAT', ('IND', 0x10) ),
+ ( 'STEREO', ('IND', 0x11) ),
+ ( 'SAMPLERT_DET', ('IND', 0x12) ),
+ ( 'SPINNING', ('IND', 0x13) ),
+ ( 'CAV', ('IND', 0x14) ),
+ ( 'CLV', ('IND', 0x15) ),
+ ( 'RECFMT_DET', ('IND', 0x16) ),
+ ( 'OFFHOOK', ('IND', 0x17) ),
+ ( 'RING', ('IND', 0x18) ),
+ ( 'MSGWAITING', ('IND', 0x19) ),
+ ( 'DATAMODE', ('IND', 0x1A) ),
+ ( 'BATOPERATION', ('IND', 0x1B) ),
+ ( 'BATOK', ('IND', 0x1C) ),
+ ( 'BATLOW', ('IND', 0x1D) ),
+ ( 'SPEAKER', ('IND', 0x1E) ),
+ ( 'HEADSET', ('IND', 0x1F) ),
+ ( 'HOLD', ('IND', 0x20) ),
+ ( 'MICROPHONE', ('IND', 0x21) ),
+ ( 'COVERAGE', ('IND', 0x22) ),
+ ( 'NIGHTMODE', ('IND', 0x23) ),
+ ( 'SENDCALLS', ('IND', 0x24) ),
+ ( 'CALLPICKUP', ('IND', 0x25) ),
+ ( 'CONFERENCE', ('IND', 0x26) ),
+ ( 'STANDBY', ('IND', 0x27) ),
+ ( 'CAMERAON', ('IND', 0x28) ),
+ ( 'CAMERAOFF', ('IND', 0x29) ),
+ ( 'ONLINE', ('IND', 0x2A) ),
+ ( 'OFFLINE', ('IND', 0x2B) ),
+ ( 'BUSY', ('IND', 0x2C) ),
+ ( 'READY', ('IND', 0x2D) ),
+ ( 'PAPEROUT', ('IND', 0x2E) ),
+ ( 'PAPERJAM', ('IND', 0x2F) ),
+ ( 'REMOTE', ('IND', 0x30) ),
+ ( 'FORWARD', ('IND', 0x31) ),
+ ( 'REVERSE', ('IND', 0x32) ),
+ ( 'STOP', ('IND', 0x33) ),
+ ( 'REWIND', ('IND', 0x34) ),
+ ( 'FASTFORWARD', ('IND', 0x35) ),
+ ( 'PLAY', ('IND', 0x36) ),
+ ( 'PAUSE', ('IND', 0x37) ),
+ ( 'RECORD', ('IND', 0x38) ),
+ ( 'ERROR', ('IND', 0x39) ),
+ ( 'USI', ('IND', 0x3A) ),
+ ( 'UIUI', ('IND', 0x3B) ),
+ ( 'UMMI', ('IND', 0x3C) ),
+ ( 'INDON', ('IND', 0x3D) ),
+ ( 'INDFLASH', ('IND', 0x3E) ),
+ ( 'INDSLOW_BLNK', ('IND', 0x3F) ),
+ ( 'INDFAST_BLNK', ('IND', 0x40) ),
+ ( 'INDOFF', ('IND', 0x41) ),
+ ( 'FLASHON_TIME', ('IND', 0x42) ),
+ ( 'SLWB_ON_TIME', ('IND', 0x43) ),
+ ( 'SLWB_OFF_TIME', ('IND', 0x44) ),
+ ( 'FSTB_ON_TIME', ('IND', 0x45) ),
+ ( 'FSTB_OFF_TIME', ('IND', 0x46) ),
+ ( 'UIC', ('IND', 0x47) ),
+ ( 'INDRED', ('IND', 0x48) ),
+ ( 'INDGREEN', ('IND', 0x49) ),
+ ( 'INDAMBER', ('IND', 0x4A) ),
+ ( 'GENERICIND', ('IND', 0x4B) ),
+ ( 'SYSSUSPEND', ('IND', 0x4C) ),
+ ( 'EXTPWR_CONN', ('IND', 0x4D) ),
+# 0x4E - 0xFFFF Reserved
+])
+
+
+# List of System Controls - USB HID 1.12v2 pg 32
+# NKRO HID Supports 0x81 - 0xB7
+kll_hid_lookup_dictionary['SysCode'] = dict([
+ ( 'POWERDOWN', ('SYS', 0x81) ),
+ ( 'SLEEP', ('SYS', 0x82) ),
+ ( 'WAKEUP', ('SYS', 0x83) ),
+ ( 'CONTEXTMENU', ('SYS', 0x84) ),
+ ( 'MAINMENU', ('SYS', 0x85) ),
+ ( 'APPMENU', ('SYS', 0x86) ),
+ ( 'MENUHELP', ('SYS', 0x87) ),
+ ( 'MENUEXIT', ('SYS', 0x88) ),
+ ( 'MENUSELECT', ('SYS', 0x89) ),
+ ( 'MENURIGHT', ('SYS', 0x8A) ),
+ ( 'MENULEFT', ('SYS', 0x8B) ),
+ ( 'MENUUP', ('SYS', 0x8C) ),
+ ( 'MENUDOWN', ('SYS', 0x8D) ),
+ ( 'COLDRESTART', ('SYS', 0x8E) ),
+ ( 'WARMRESTART', ('SYS', 0x8F) ),
+ ( 'DPADUP', ('SYS', 0x90) ),
+ ( 'DPADDOWN', ('SYS', 0x91) ),
+ ( 'DPADRIGHT', ('SYS', 0x92) ),
+ ( 'DPADLEFT', ('SYS', 0x93) ),
+# 0x94 - 0x9F Reserved
+ ( 'DOCK', ('SYS', 0xA0) ),
+ ( 'UNDOCK', ('SYS', 0xA1) ),
+ ( 'SETUP', ('SYS', 0xA2) ),
+ ( 'BREAK', ('SYS', 0xA3) ),
+ ( 'DEBUGGERBREAK', ('SYS', 0xA4) ),
+ ( 'APPBREAK', ('SYS', 0xA5) ),
+ ( 'APPDEBUGGER_BREAK', ('SYS', 0xA6) ),
+ ( 'SPEAKERMUTE', ('SYS', 0xA7) ),
+ ( 'HIBERNATE', ('SYS', 0xA8) ),
+# 0xA9 - 0xAF Reserved
+ ( 'DISPINVERT', ('SYS', 0xB0) ),
+ ( 'DISPINTERNAL', ('SYS', 0xB1) ),
+ ( 'DISPEXTERNAL', ('SYS', 0xB2) ),
+ ( 'DISPBOTH', ('SYS', 0xB3) ),
+ ( 'DISPDUAL', ('SYS', 0xB4) ),
+ ( 'DISPTOGGLE_INT_EXT', ('SYS', 0xB5) ),
+ ( 'DISPSWAP_PRI_SEC', ('SYS', 0xB6) ),
+ ( 'DISPLCD_AUTOSCALE', ('SYS', 0xB7) ),
+# 0xB8 - 0xFFFF Reserved
+])
+
+
+ # USB HID Consumer Control Codes
+# List of Consumer Codes - USB HID 1.12v2
+# Only listing relevant ones, let me know if you need more -HaaTa
+# NKRO HID Supports 0x020 - 0x29C
+kll_hid_lookup_dictionary['ConsCode'] = dict([
+ ( '10', ('CONS', 0x020) ),
+ ( '100', ('CONS', 0x021) ),
+ ( 'AMPM', ('CONS', 0x022) ),
+# 0x023 - 0x03F Reserved
+ ( 'POWER', ('CONS', 0x030) ),
+ ( 'RESET', ('CONS', 0x031) ),
+ ( 'SLEEP', ('CONS', 0x032) ),
+ ( 'SLEEPAFTER', ('CONS', 0x033) ),
+ ( 'SLEEPMODE', ('CONS', 0x034) ),
+ ( 'ILLUMINATION', ('CONS', 0x035) ),
+
+# 0x037 - 0x03F Reserved
+ ( 'MENU', ('CONS', 0x040) ),
+ ( 'MENUPICK', ('CONS', 0x041) ),
+ ( 'MENUUP', ('CONS', 0x042) ),
+ ( 'MENUDOWN', ('CONS', 0x043) ),
+ ( 'MENULEFT', ('CONS', 0x044) ),
+ ( 'MENURIGHT', ('CONS', 0x045) ),
+ ( 'MENUESCAPE', ('CONS', 0x046) ),
+ ( 'MENUVALUE_INCREASE', ('CONS', 0x047) ),
+ ( 'MENUVALUE_DECREASE', ('CONS', 0x048) ),
+# 0x049 - 0x05F Reserved
+ ( 'DATAON_SCREEN', ('CONS', 0x060) ),
+ ( 'CLOSEDCAPTION', ('CONS', 0x061) ),
+ ( 'CLOSEDCAPTION_SELECT', ('CONS', 0x062) ),
+ ( 'VCRTV', ('CONS', 0x063) ),
+ ( 'BROADCASTMODE', ('CONS', 0x064) ),
+ ( 'SNAPSHOT', ('CONS', 0x065) ),
+ ( 'STILL', ('CONS', 0x066) ),
+# 0x067 - 0x06E Reserved?
+ ( 'BRIGHTNESSINCREMENT', ('CONS', 0x06F) ),
+ ( 'BRIGHTNESSDECREMENT', ('CONS', 0x070) ),
+
+ ( 'BACKLIGHTTOGGLE', ('CONS', 0x072) ),
+ ( 'BRIGHTNESSMIN', ('CONS', 0x073) ),
+ ( 'BRIGHTNESSMAX', ('CONS', 0x074) ),
+ ( 'BRIGHTNESSAUTO', ('CONS', 0x075) ),
+# 0x076 - 0x07F Reserved
+
+ ( 'ASSIGNSELECTION', ('CONS', 0x081) ),
+ ( 'MODESTEP', ('CONS', 0x082) ),
+ ( 'RECALLLAST', ('CONS', 0x083) ),
+ ( 'ENTERCHANNEL', ('CONS', 0x084) ),
+ ( 'ORDERMOVIE', ('CONS', 0x085) ),
+
+ ( 'MEDIACOMPUTER', ('CONS', 0x088) ),
+ ( 'MEDIATV', ('CONS', 0x089) ),
+ ( 'MEDIAWWW', ('CONS', 0x08A) ),
+ ( 'MEDIADVD', ('CONS', 0x08B) ),
+ ( 'MEDIATELEPHONE', ('CONS', 0x08C) ),
+ ( 'MEDIAPROGRAM_GUIDE', ('CONS', 0x08D) ),
+ ( 'MEDIAVIDEO_PHONE', ('CONS', 0x08E) ),
+ ( 'MEDIASELECTGAMES', ('CONS', 0x08F) ),
+ ( 'MEDIASELECTMESSAGES', ('CONS', 0x090) ),
+ ( 'MEDIASELECTCD', ('CONS', 0x091) ),
+ ( 'MEDIASELECTVCR', ('CONS', 0x092) ),
+ ( 'MEDIASELECTTUNER', ('CONS', 0x093) ),
+ ( 'QUIT', ('CONS', 0x094) ),
+ ( 'HELP', ('CONS', 0x095) ),
+ ( 'MEDIASELECT_TAPE', ('CONS', 0x096) ),
+ ( 'MEDIASELECT_CABLE', ('CONS', 0x097) ),
+ ( 'MEDIASELECT_SATELLITE', ('CONS', 0x098) ),
+ ( 'MEDIASELECT_SECURITY', ('CONS', 0x099) ),
+ ( 'MEDIASELECT_HOME', ('CONS', 0x09A) ),
+ ( 'MEDIASELECT_CALL', ('CONS', 0x09B) ),
+ ( 'CHANNELINCREMENT', ('CONS', 0x09C) ),
+ ( 'CAHNNELDECREMENT', ('CONS', 0x09D) ),
+ ( 'MEDIASELECT_SAP', ('CONS', 0x09E) ),
+# 0x09F Reserved
+ ( 'VCRPLUS', ('CONS', 0x0A0) ),
+ ( 'ONCE', ('CONS', 0x0A1) ),
+ ( 'DAILY', ('CONS', 0x0A2) ),
+ ( 'WEEKLY', ('CONS', 0x0A3) ),
+ ( 'MONTHLY', ('CONS', 0x0A4) ),
+# 0x0A5 - 0x0AF Reserved
+ ( 'PLAY', ('CONS', 0x0B0) ),
+ ( 'PAUSE', ('CONS', 0x0B1) ),
+ ( 'RECORD', ('CONS', 0x0B2) ),
+ ( 'FASTFORWARD', ('CONS', 0x0B3) ),
+ ( 'REWIND', ('CONS', 0x0B4) ),
+ ( 'SCANNEXTTRACK', ('CONS', 0x0B5) ),
+ ( 'SCANPREVIOUSTRACK', ('CONS', 0x0B6) ),
+ ( 'STOP', ('CONS', 0x0B7) ),
+ ( 'EJECT', ('CONS', 0x0B8) ),
+ ( 'RANDOMPLAY', ('CONS', 0x0B9) ),
+
+ ( 'REPEAT', ('CONS', 0x0BC) ),
+
+ ( 'TRACKNORMAL', ('CONS', 0x0BE) ),
+
+ ( 'FRAMEFORWARD', ('CONS', 0x0C0) ),
+ ( 'FRAMEBACK', ('CONS', 0x0C1) ),
+ ( 'MARK', ('CONS', 0x0C2) ),
+ ( 'CLEARMARK', ('CONS', 0x0C3) ),
+ ( 'REPEATFROM_MARK', ('CONS', 0x0C4) ),
+ ( 'RETURNTO_MARK', ('CONS', 0x0C5) ),
+ ( 'SEARCHMARK_FORWARDS', ('CONS', 0x0C6) ),
+ ( 'SEARCHMARK_BACKWARDS', ('CONS', 0x0C7) ),
+ ( 'COUNTERRESET', ('CONS', 0x0C8) ),
+ ( 'SHOWCOUNTER', ('CONS', 0x0C9) ),
+ ( 'TRACKINGINCREMENT', ('CONS', 0x0CA) ),
+ ( 'TRACKINGDECREMENT', ('CONS', 0x0CB) ),
+ ( 'STOPEJECT', ('CONS', 0x0CC) ),
+ ( 'PAUSEPLAY', ('CONS', 0x0CD) ),
+ ( 'PLAYSKIP', ('CONS', 0x0CE) ),
+# 0x0CF - 0x0DF Reserved
+
+ ( 'MUTE', ('CONS', 0x0E2) ),
+
+ ( 'BASSBOOST', ('CONS', 0x0E5) ),
+ ( 'SURROUNDMODE', ('CONS', 0x0E6) ),
+ ( 'LOUDNESS', ('CONS', 0x0E7) ),
+ ( 'MPX', ('CONS', 0x0E8) ),
+ ( 'VOLUMEUP', ('CONS', 0x0E9) ),
+ ( 'VOLUMEDOWN', ('CONS', 0x0EA) ),
+# 0x0EB - 0x0EF Reserved
+ ( 'SPEEDSELECT', ('CONS', 0x0F0) ),
+ ( 'STANDARDPLAY', ('CONS', 0x0F2) ),
+ ( 'LONGPLAY', ('CONS', 0x0F3) ),
+ ( 'EXTENDEDPLAY', ('CONS', 0x0F4) ),
+ ( 'SLOW', ('CONS', 0x0F5) ),
+# 0x0F6 - 0x0FF
+ ( 'FANENABLE', ('CONS', 0x100) ),
+
+ ( 'LIGHTENABLE', ('CONS', 0x102) ),
+
+ ( 'CLIMATECONTROL_ENABLE', ('CONS', 0x104) ),
+
+ ( 'SECURITYENABLE', ('CONS', 0x106) ),
+ ( 'FIREALARM', ('CONS', 0x107) ),
+
+ ( 'MOTION', ('CONS', 0x10A) ),
+ ( 'DURESSALARM', ('CONS', 0x10B) ),
+ ( 'HOLDUPALARM', ('CONS', 0x10C) ),
+ ( 'MEDICALALARM', ('CONS', 0x10D) ),
+# 0x10E - 0x14F Reserved
+ ( 'BALANCERIGHT', ('CONS', 0x150) ),
+ ( 'BALANCELEFT', ('CONS', 0x151) ),
+ ( 'BASSINCR', ('CONS', 0x152) ),
+ ( 'BASSDECR', ('CONS', 0x153) ),
+ ( 'TREBLEINCR', ('CONS', 0x154) ),
+ ( 'TREBLEDECR', ('CONS', 0x155) ),
+# 0x156 - 0x15F Reserved
+
+ ( 'SUBCHANNEL_INCREMENT', ('CONS', 0x171) ),
+ ( 'SUBCHANNEL_DECREMENT', ('CONS', 0x172) ),
+ ( 'ALTAUDIO_INCREMENT', ('CONS', 0x173) ),
+ ( 'ALTAUDIO_DECREMENT', ('CONS', 0x174) ),
+
+
+# List of Consumer Codes - USB HID 1.12v2
+# Application Launch Buttons pg 79
+ ( 'LAUNCHBUTTON_CONFIG_TOOL', ('CONS', 0x181) ),
+ ( 'PROGRAMMABLEBUTTON_CONFIG', ('CONS', 0x182) ),
+ ( 'CONSUMERCONTROL_CONFIG', ('CONS', 0x183) ),
+ ( 'WORDPROCESSOR', ('CONS', 0x184) ),
+ ( 'TEXTEDITOR', ('CONS', 0x185) ),
+ ( 'SPREADSHEET', ('CONS', 0x186) ),
+ ( 'GRAPHICSEDITOR', ('CONS', 0x187) ),
+ ( 'PRESENTATIONAPP', ('CONS', 0x188) ),
+ ( 'DATABASEAPP', ('CONS', 0x189) ),
+ ( 'EMAILREADER', ('CONS', 0x18A) ),
+ ( 'NEWSREADER', ('CONS', 0x18B) ),
+ ( 'VOICEMAIL', ('CONS', 0x18C) ),
+ ( 'CONTACTSADDRESS_BOOK', ('CONS', 0x18D) ),
+ ( 'CALENDARSCHEDULE', ('CONS', 0x18E) ),
+ ( 'TASKPROJECT_MANAGER', ('CONS', 0x18F) ),
+ ( 'LOGJOURNAL_TIMECARD', ('CONS', 0x190) ),
+ ( 'CHECKBOOKFINANCE', ('CONS', 0x191) ),
+ ( 'CALCULATOR', ('CONS', 0x192) ),
+ ( 'AV_CAPTURE_PLAYBACK', ('CONS', 0x193) ),
+ ( 'LOCALMACHINE_BROWSER', ('CONS', 0x194) ),
+ ( 'LANWAN_BROWSER', ('CONS', 0x195) ),
+ ( 'INTERNETBROWSER', ('CONS', 0x196) ),
+ ( 'REMOTENETWORKING_ISP_CONNECT', ('CONS', 0x197) ),
+ ( 'NETWORKCONFERENCE', ('CONS', 0x198) ),
+ ( 'NETWORKCHAT', ('CONS', 0x199) ),
+ ( 'TELEPHONYDIALER', ('CONS', 0x19A) ),
+ ( 'LOGON', ('CONS', 0x19B) ),
+ ( 'LOGOFF', ('CONS', 0x19C) ),
+ ( 'LOGONLOGOFF', ('CONS', 0x19D) ),
+ ( 'TERMINALLOCK_SCREENSAVER', ('CONS', 0x19E) ),
+ ( 'CONTROLPANEL', ('CONS', 0x19F) ),
+ ( 'COMMANDLINE_PROCESSOR_RUN', ('CONS', 0x1A0) ),
+ ( 'PROCESSTASK_MANAGER', ('CONS', 0x1A1) ),
+ ( 'SELECTTAST_APP', ('CONS', 0x1A2) ),
+ ( 'NEXTTASK_APP', ('CONS', 0x1A3) ),
+ ( 'PREVIOUSTASK_APP', ('CONS', 0x1A4) ),
+ ( 'PREEMPTIVEHALT_TASK_APP', ('CONS', 0x1A5) ),
+ ( 'INTEGRATEDHELP_CENTER', ('CONS', 0x1A6) ),
+ ( 'DOCUMENTS', ('CONS', 0x1A7) ),
+ ( 'THESAURUS', ('CONS', 0x1A8) ),
+ ( 'DICTIONARY', ('CONS', 0x1A9) ),
+ ( 'DESKTOP', ('CONS', 0x1AA) ),
+ ( 'SPELLCHECK', ('CONS', 0x1AB) ),
+ ( 'GRAMMARCHECK', ('CONS', 0x1AC) ),
+ ( 'WIRELESSSTATUS', ('CONS', 0x1AD) ),
+ ( 'KEYBOARDLAYOUT', ('CONS', 0x1AE) ),
+ ( 'VIRUSPROTECTION', ('CONS', 0x1AF) ),
+ ( 'ENCRYPTION', ('CONS', 0x1B0) ),
+ ( 'SCREENSAVER', ('CONS', 0x1B1) ),
+ ( 'ALARMS', ('CONS', 0x1B2) ),
+ ( 'CLOCK', ('CONS', 0x1B3) ),
+ ( 'FILEBROWSER', ('CONS', 0x1B4) ),
+ ( 'POWERSTATUS', ('CONS', 0x1B5) ),
+ ( 'IMAGEBROWSER', ('CONS', 0x1B6) ),
+ ( 'AUDIOBROWSER', ('CONS', 0x1B7) ),
+ ( 'MOVIEBROWSER', ('CONS', 0x1B8) ),
+ ( 'DIGITALRIGHTS_MANAGER', ('CONS', 0x1B9) ),
+ ( 'DIGITALWALLET', ('CONS', 0x1BA) ),
+# 0x1BB Reserved
+ ( 'INSTANTMESSAGING', ('CONS', 0x1BC) ),
+ ( 'OEMFEATURES_TIPS_TUTORIAL', ('CONS', 0x1BD) ),
+ ( 'OEMHELP', ('CONS', 0x1BE) ),
+ ( 'ONLINECOMMUNITY', ('CONS', 0x1BF) ),
+ ( 'ENTERTAINMENTCONTENT', ('CONS', 0x1C0) ),
+ ( 'ONLINESHOPPING', ('CONS', 0x1C1) ),
+ ( 'SMARTCARDINFO_HELP', ('CONS', 0x1C2) ),
+ ( 'MARKETMONITOR', ('CONS', 0x1C3) ),
+ ( 'CUSTOMIZEDCORP_NEWS', ('CONS', 0x1C4) ),
+ ( 'ONLINEACTIVITY', ('CONS', 0x1C5) ),
+ ( 'SEARCHBROWSER', ('CONS', 0x1C6) ),
+ ( 'AUDIOPLAYER', ('CONS', 0x1C7) ),
+
+
+# List of Consumer Codes - USB HID 1.12v2
+# Generic GUI Application Controls pg 82
+ ( 'NEW', ('CONS', 0x201) ),
+ ( 'OPEN', ('CONS', 0x202) ),
+ ( 'CLOSE', ('CONS', 0x203) ),
+ ( 'EXIT', ('CONS', 0x204) ),
+ ( 'MAXIMIZE', ('CONS', 0x205) ),
+ ( 'MINIMIZE', ('CONS', 0x206) ),
+ ( 'SAVE', ('CONS', 0x207) ),
+ ( 'PRINT', ('CONS', 0x208) ),
+ ( 'PROPERTIES', ('CONS', 0x209) ),
+ ( 'UNDO', ('CONS', 0x21A) ),
+ ( 'COPY', ('CONS', 0x21B) ),
+ ( 'CUT', ('CONS', 0x21C) ),
+ ( 'PASTE', ('CONS', 0x21D) ),
+ ( 'SELECTALL', ('CONS', 0x21E) ),
+ ( 'FIND', ('CONS', 0x21F) ),
+ ( 'FINDANDREPLACE', ('CONS', 0x220) ),
+ ( 'SEARCH', ('CONS', 0x221) ),
+ ( 'GOTO', ('CONS', 0x222) ),
+ ( 'HOME', ('CONS', 0x223) ),
+ ( 'BACK', ('CONS', 0x224) ),
+ ( 'FORWARD', ('CONS', 0x225) ),
+ ( 'STOP', ('CONS', 0x226) ),
+ ( 'REFRESH', ('CONS', 0x227) ),
+ ( 'PREVIOUSLINK', ('CONS', 0x228) ),
+ ( 'NEXTLINK', ('CONS', 0x229) ),
+ ( 'BOOKMARKS', ('CONS', 0x22A) ),
+ ( 'HISTORY', ('CONS', 0x22B) ),
+ ( 'SUBSCRIPTIONS', ('CONS', 0x22C) ),
+ ( 'ZOOMIN', ('CONS', 0x22D) ),
+ ( 'ZOOMOUT', ('CONS', 0x22E) ),
+ ( 'ZOOM', ('CONS', 0x22F) ),
+ ( 'FULLSCREEN_VIEW', ('CONS', 0x230) ),
+ ( 'NORMALVIEW', ('CONS', 0x231) ),
+ ( 'VIEWTOGGLE', ('CONS', 0x232) ),
+ ( 'SCROLLUP', ('CONS', 0x233) ),
+ ( 'SCROLLDOWN', ('CONS', 0x234) ),
+ ( 'SCROLL', ('CONS', 0x235) ),
+ ( 'PANLEFT', ('CONS', 0x236) ),
+ ( 'PANRIGHT', ('CONS', 0x237) ),
+ ( 'PAN', ('CONS', 0x238) ),
+ ( 'NEWWINDOW', ('CONS', 0x239) ),
+ ( 'TILEHORIZONTALLY', ('CONS', 0x23A) ),
+ ( 'TILEVERTICALLY', ('CONS', 0x23B) ),
+ ( 'FORMAT', ('CONS', 0x23C) ),
+ ( 'EDIT', ('CONS', 0x23D) ),
+ ( 'BOLD', ('CONS', 0x23E) ),
+ ( 'ITALICS', ('CONS', 0x23F) ),
+ ( 'UNDERLINE', ('CONS', 0x240) ),
+ ( 'STRIKETHROUGH', ('CONS', 0x241) ),
+ ( 'SUBSCRIPT', ('CONS', 0x242) ),
+ ( 'SUPERSCRIPT', ('CONS', 0x243) ),
+ ( 'ALLCAPS', ('CONS', 0x244) ),
+ ( 'ROTATE', ('CONS', 0x245) ),
+ ( 'RESIZE', ('CONS', 0x246) ),
+ ( 'FILPHORIZONTAL', ('CONS', 0x247) ),
+ ( 'FILPVERTICAL', ('CONS', 0x248) ),
+ ( 'MIRRORHORIZONTAL', ('CONS', 0x249) ),
+ ( 'MIRRORVERTICAL', ('CONS', 0x24A) ),
+ ( 'FONTSELECT', ('CONS', 0x24B) ),
+ ( 'FONTCOLOR', ('CONS', 0x24C) ),
+ ( 'FONTSIZE', ('CONS', 0x24D) ),
+ ( 'JUSTIFYLEFT', ('CONS', 0x24E) ),
+ ( 'JUSTIFYCENTER_H', ('CONS', 0x24F) ),
+ ( 'JUSTIFYRIGHT', ('CONS', 0x250) ),
+ ( 'JUSTIFYBLOCK_H', ('CONS', 0x251) ),
+ ( 'JUSTIFYTOP', ('CONS', 0x252) ),
+ ( 'JUSTIFYCENTER_V', ('CONS', 0x253) ),
+ ( 'JUSTIFYBOTTOM', ('CONS', 0x254) ),
+ ( 'JUSTIFYBLOCK_V', ('CONS', 0x255) ),
+ ( 'INDENTDECREASE', ('CONS', 0x256) ),
+ ( 'INDENTINCREASE', ('CONS', 0x257) ),
+ ( 'NUMBEREDLIST', ('CONS', 0x258) ),
+ ( 'RESTARTNUMBERING', ('CONS', 0x259) ),
+ ( 'BULLETEDLIST', ('CONS', 0x25A) ),
+ ( 'PROMOTE', ('CONS', 0x25B) ),
+ ( 'DEMOTE', ('CONS', 0x25C) ),
+ ( 'YES', ('CONS', 0x25D) ),
+ ( 'NO', ('CONS', 0x25E) ),
+ ( 'CANCEL', ('CONS', 0x25F) ),
+ ( 'CATALOG', ('CONS', 0x260) ),
+ ( 'BUYCHECKOUT', ('CONS', 0x261) ),
+ ( 'ADDTO_CART', ('CONS', 0x262) ),
+ ( 'EXPAND', ('CONS', 0x263) ),
+ ( 'EXPANDALL', ('CONS', 0x264) ),
+ ( 'COLLAPSE', ('CONS', 0x265) ),
+ ( 'COLLAPSEALL', ('CONS', 0x266) ),
+ ( 'PRINTPREVIEW', ('CONS', 0x267) ),
+ ( 'PASTESPECIAL', ('CONS', 0x268) ),
+ ( 'INSERTMODE', ('CONS', 0x269) ),
+ ( 'DELETE', ('CONS', 0x26A) ),
+ ( 'LOCK', ('CONS', 0x26B) ),
+ ( 'UNLOCK', ('CONS', 0x26C) ),
+ ( 'PROTECT', ('CONS', 0x26D) ),
+ ( 'UNPROTECT', ('CONS', 0x26E) ),
+ ( 'ATTACHCOMMENT', ('CONS', 0x26F) ),
+ ( 'DELETECOMMENT', ('CONS', 0x270) ),
+ ( 'VIEWCOMMENT', ('CONS', 0x271) ),
+ ( 'SELECTWORD', ('CONS', 0x272) ),
+ ( 'SELECTSENTENCE', ('CONS', 0x273) ),
+ ( 'SELECTPARAGRAPH', ('CONS', 0x274) ),
+ ( 'SELECTCOLUMN', ('CONS', 0x275) ),
+ ( 'SELECTROW', ('CONS', 0x276) ),
+ ( 'SELECTTABLE', ('CONS', 0x277) ),
+ ( 'SELECTOBJECT', ('CONS', 0x278) ),
+ ( 'REDOREPEAT', ('CONS', 0x279) ),
+ ( 'SORT', ('CONS', 0x27A) ),
+ ( 'SORTASCENDING', ('CONS', 0x27B) ),
+ ( 'SORTDESCENDING', ('CONS', 0x27C) ),
+ ( 'FILTER', ('CONS', 0x27D) ),
+ ( 'SETCLOCK', ('CONS', 0x27E) ),
+ ( 'VIEWCLOCK', ('CONS', 0x27F) ),
+ ( 'SELECTTIME_ZONE', ('CONS', 0x280) ),
+ ( 'EDITTIME_ZONE', ('CONS', 0x281) ),
+ ( 'SETALARM', ('CONS', 0x282) ),
+ ( 'CLEARALARM', ('CONS', 0x283) ),
+ ( 'SNOOZEALARM', ('CONS', 0x284) ),
+ ( 'RESETALARM', ('CONS', 0x285) ),
+ ( 'SYNCHRONIZE', ('CONS', 0x286) ),
+ ( 'SENDRECEIVE', ('CONS', 0x287) ),
+ ( 'SENDTO', ('CONS', 0x288) ),
+ ( 'REPLY', ('CONS', 0x289) ),
+ ( 'REPLYALL', ('CONS', 0x28A) ),
+ ( 'FORWARDMSG', ('CONS', 0x28B) ),
+ ( 'SEND', ('CONS', 0x28C) ),
+ ( 'ATTACHFILE', ('CONS', 0x28D) ),
+ ( 'UPLOAD', ('CONS', 0x28E) ),
+ ( 'DOWNLOAD', ('CONS', 0x28F) ),
+ ( 'SETBORDERS', ('CONS', 0x290) ),
+ ( 'INSERTROW', ('CONS', 0x291) ),
+ ( 'INSERTCOLUMN', ('CONS', 0x292) ),
+ ( 'INSERTFILE', ('CONS', 0x293) ),
+ ( 'INSERTPICTURE', ('CONS', 0x294) ),
+ ( 'INSERTOBJECT', ('CONS', 0x295) ),
+ ( 'INSERTSYMBOL', ('CONS', 0x296) ),
+ ( 'SAVEANDCLOSE', ('CONS', 0x297) ),
+ ( 'RENAME', ('CONS', 0x298) ),
+ ( 'MERGE', ('CONS', 0x299) ),
+ ( 'SPLIT', ('CONS', 0x29A) ),
+ ( 'DISTRIBUTEHORIZONTALLY', ('CONS', 0x29B) ),
+ ( 'DISTRIBUTEVERTICALLY', ('CONS', 0x29C) ),
+ ( 'NEXTKEYBOARDLAYOUTSELECT', ('CONS', 0x29D) ),
+# 0x29E-0xFFFF Reserved
+])
+
diff --git a/common/id.py b/common/id.py
new file mode 100644
index 0000000..3337068
--- /dev/null
+++ b/common/id.py
@@ -0,0 +1,280 @@
+#!/usr/bin/env python3
+'''
+KLL Id Containers
+'''
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+from common.hid_dict import hid_lookup_dictionary
+
+from common.channel import ChannelList
+from common.modifier import AnimationModifierList, PixelModifierList
+from common.position import Position
+from common.schedule import Schedule
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+
+
+### Classes ###
+
+class Id:
+ '''
+ Base container class for various KLL types
+ '''
+ def __init__( self ):
+ self.type = None
+ self.uid = None
+
+
+class HIDId( Id, Schedule ):
+ '''
+ HID/USB identifier container class
+ '''
+ secondary_types = {
+ 'USBCode' : 'USB',
+ 'SysCode' : 'SYS',
+ 'ConsCode' : 'CONS',
+ 'IndCode' : 'IND',
+ }
+
+ def __init__( self, type, uid ):
+ '''
+ @param type: String type of the Id
+ @param uid: Unique integer identifier for the Id
+ '''
+ Id.__init__( self )
+ Schedule.__init__( self )
+ self.type = type
+ self.uid = uid
+
+ # Set secondary type
+ self.second_type = self.secondary_types[ self.type ]
+
+ # TODO Validate uid to make sure it's in the lookup dictionary
+ # TODO Validate HID specifier
+ #print ( "{0} Unknown HID Specifier '{1}'".format( ERROR, type ) )
+ #raise
+
+ def __repr__( self ):
+ '''
+ Use string name instead of integer, easier to debug
+ '''
+ uid = hid_lookup_dictionary[ ( self.second_type, self.uid ) ]
+ schedule = self.strSchedule()
+ if len( schedule ) > 0:
+ schedule = "({0})".format( schedule )
+
+ output = 'HID({0})"{1}"{2}'.format( self.type, uid, schedule )
+ return output
+
+
+class ScanCodeId( Id, Schedule, Position ):
+ '''
+ Scan Code identifier container class
+ '''
+
+ def __init__( self, uid ):
+ Id.__init__( self )
+ Schedule.__init__( self )
+ Position.__init__( self )
+ self.type = 'ScanCode'
+ self.uid = uid
+
+ # By default, interconnect_id of 0
+ # Will be set during the merge process if it needs to change
+ self.interconnect_id = 0
+
+ def unique_key( self ):
+ '''
+ Returns the key string used for datastructure sorting
+ '''
+ # Positions are a special case
+ if self.positionSet():
+ return "P{0}".format( self.uid )
+
+ def __repr__( self ):
+ # Positions are a special case
+ if self.positionSet():
+ return "{0} <= {1}".format( self.unique_key(), self.strPosition() )
+
+ schedule = self.strSchedule()
+ if len( schedule ) > 0:
+ return "S{0}({1})".format( self.uid, schedule )
+ else:
+ return "S{0}".format( self.uid )
+
+
+class AnimationId( Id, AnimationModifierList ):
+ '''
+ Animation identifier container class
+ '''
+ name = None
+
+ def __init__( self, name ):
+ Id.__init__( self )
+ AnimationModifierList.__init__( self )
+ self.name = name
+ self.type = 'Animation'
+
+ def __repr__( self ):
+ if len( self.modifiers ) > 0:
+ return "A[{0}]({1})".format( self.name, self.strModifiers() )
+ return "A[{0}]".format( self.name )
+
+
+class AnimationFrameId( Id, AnimationModifierList ):
+ '''
+ Animation Frame identifier container class
+ '''
+ def __init__( self, name, index ):
+ Id.__init__( self )
+ AnimationModifierList.__init__( self )
+ self.name = name
+ self.index = index
+ self.type = 'AnimationFrame'
+
+ def __repr__( self ):
+ return "AF[{0}, {1}]".format( self.name, self.index )
+
+
+class PixelId( Id, Position, PixelModifierList, ChannelList ):
+ '''
+ Pixel identifier container class
+ '''
+ def __init__( self, uid ):
+ Id.__init__( self )
+ Position.__init__( self )
+ PixelModifierList.__init__( self )
+ ChannelList.__init__( self )
+ self.uid = uid
+ self.type = 'Pixel'
+
+ def unique_key( self ):
+ '''
+ Returns the key string used for datastructure sorting
+ '''
+ return "P{0}".format( self.uid )
+
+ def __repr__( self ):
+ # Positions are a special case
+ if self.positionSet():
+ return "{0} <= {1}".format( self.unique_key(), self.strPosition() )
+
+ extra = ""
+ if len( self.modifiers ) > 0:
+ extra += "({0})".format( self.strModifiers() )
+ if len( self.channels ) > 0:
+ extra += "({0})".format( self.strChannels() )
+ return "{0}{1}".format( self.unique_key(), extra )
+
+
+class PixelLayerId( Id, PixelModifierList ):
+ '''
+ Pixel Layer identifier container class
+ '''
+ def __init__( self, uid ):
+ Id.__init__( self )
+ PixelModifierList.__init__( self )
+ self.uid = uid
+ self.type = 'PixelLayer'
+
+ def __repr__( self ):
+ if len( self.modifiers ) > 0:
+ return "PL{0}({1})".format( self.uid, self.strModifiers() )
+ return "PL{0}".format( self.uid )
+
+
+class CapId( Id ):
+ '''
+ Capability identifier
+ '''
+ def __init__( self, name, type, arg_list=[] ):
+ '''
+ @param name: Name of capability
+ @param type: Type of capability definition, string
+ @param arg_list: List of CapArgIds, empty list if there are none
+ '''
+ Id.__init__( self )
+ self.name = name
+ self.type = type
+ self.arg_list = arg_list
+
+ def __repr__( self ):
+ # Generate prettified argument list
+ arg_string = ""
+ for arg in self.arg_list:
+ arg_string += "{0},".format( arg )
+ if len( arg_string ) > 0:
+ arg_string = arg_string[:-1]
+
+ return "{0}({1})".format( self.name, arg_string )
+
+ def total_arg_bytes( self ):
+ '''
+ Calculate the total number of bytes needed for the args
+
+ return: Number of bytes
+ '''
+ # Zero if no args
+ total_bytes = 0
+ for arg in self.arg_list:
+ total_bytes += arg.width
+
+ return total_bytes
+
+
+class NoneId( CapId ):
+ '''
+ None identifier
+
+ It's just a capability...that does nothing (instead of infering to do something else)
+ '''
+ def __init__( self ):
+ super().__init__( 'None', 'None' )
+
+ def __repr__( self ):
+ return "None"
+
+
+class CapArgId( Id ):
+ '''
+ Capability Argument identifier
+ '''
+ def __init__( self, name, width=None ):
+ '''
+ @param name: Name of argument
+ @param width: Byte-width of the argument, if None, this is not port of a capability definition
+ '''
+ Id.__init__( self )
+ self.name = name
+ self.width = width
+ self.type = 'CapArg'
+
+ def __repr__( self ):
+ if self.width is None:
+ return "{0}".format( self.name )
+ else:
+ return "{0}:{1}".format( self.name, self.width )
+
diff --git a/common/modifier.py b/common/modifier.py
new file mode 100644
index 0000000..4420d2a
--- /dev/null
+++ b/common/modifier.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+'''
+KLL Modifier Containers
+'''
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+
+
+### Classes ###
+
+class AnimationModifier:
+ '''
+ Animation modification container class
+ '''
+ def __init__( self, name, value=None ):
+ self.name = name
+ self.value = value
+
+ def __repr__( self ):
+ if self.value is None:
+ return "{0}".format( self.name )
+ return "{0}:{1}".format( self.name, self.value )
+
+
+class AnimationModifierList:
+ '''
+ Animation modification container list class
+
+ Contains a list of modifiers, the order does not matter
+ '''
+ def __init__( self ):
+ self.modifiers = []
+
+ def setModifiers( self, modifier_list ):
+ '''
+ Apply modifiers to Animation
+ '''
+ for modifier in modifier_list:
+ self.modifiers.append( AnimationModifier( modifier[0], modifier[1] ) )
+
+ def strModifiers( self ):
+ '''
+ __repr__ of Position when multiple inheritance is used
+ '''
+ output = ""
+ for index, modifier in enumerate( self.modifiers ):
+ if index > 0:
+ output += ","
+ output += "{0}".format( modifier )
+
+ return output
+
+ def __repr__( self ):
+ return self.strModifiers()
+
+
+class PixelModifier:
+ '''
+ Pixel modification container class
+ '''
+ def __init__( self, operator, value ):
+ self.operator = operator
+ self.value = value
+
+ def __repr__( self ):
+ if self.operator is None:
+ return "{0}".format( self.value )
+ return "{0}{1}".format( self.operator, self.value )
+
+
+class PixelModifierList:
+ '''
+ Pixel modification container list class
+
+ Contains a list of modifiers
+ Index 0, corresponds to pixel 0
+ '''
+ def __init__( self ):
+ self.modifiers = []
+
+ def setModifiers( self, modifier_list ):
+ '''
+ Apply modifier to each pixel channel
+ '''
+ for modifier in modifier_list:
+ self.modifiers.append( PixelModifier( modifier[0], modifier[1] ) )
+
+ def strModifiers( self ):
+ '''
+ __repr__ of Position when multiple inheritance is used
+ '''
+ output = ""
+ for index, modifier in enumerate( self.modifiers ):
+ if index > 0:
+ output += ","
+ output += "{0}".format( modifier )
+
+ return output
+
+ def __repr__( self ):
+ return self.strModifiers()
+
diff --git a/common/organization.py b/common/organization.py
new file mode 100644
index 0000000..2d189a3
--- /dev/null
+++ b/common/organization.py
@@ -0,0 +1,614 @@
+#!/usr/bin/env python3
+'''
+KLL Data Organization
+'''
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+import re
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+ansi_escape = re.compile(r'\x1b[^m]*m')
+
+
+
+### Classes ###
+
+class Data:
+ '''
+ Base class for KLL datastructures
+ '''
+ # Debug output formatters
+ debug_output = {
+ 'add' : "\t\033[1;42;37m++\033[0m\033[1mADD KEY\033[1;42;37m++\033[0m \033[1m<==\033[0m {0}",
+ 'app' : "\t\033[1;45;37m**\033[0m\033[1mAPP KEY\033[1;45;37m**\033[0m \033[1m<==\033[0m {0}",
+ 'mod' : "\t\033[1;44;37m##\033[0m\033[1mMOD KEY\033[1;44;37m##\033[0m \033[1m<==\033[0m {0}",
+ 'rem' : "\t\033[1;41;37m--\033[0m\033[1mREM KEY\033[1;41;37m--\033[0m \033[1m<==\033[0m {0}",
+ 'drp' : "\t\033[1;43;37m@@\033[0m\033[1mDRP KEY\033[1;43;37m@@\033[0m \033[1m<==\033[0m {0}",
+ 'dup' : "\t\033[1;46;37m!!\033[0m\033[1mDUP KEY\033[1;46;37m!!\033[0m \033[1m<==\033[0m {0}",
+ }
+
+ def __init__( self, parent ):
+ '''
+ Initialize datastructure
+
+ @param parent: Parent organization, used to query data from other datastructures
+ '''
+ self.data = {}
+ self.parent = parent
+
+ def add_expression( self, expression, debug ):
+ '''
+ Add expression to data structure
+
+ May have multiple keys to add for a given expression
+
+ @param expression: KLL Expression (fully tokenized and parsed)
+ @param debug: Enable debug output
+ '''
+ # Lookup unique keys for expression
+ keys = expression.unique_keys()
+
+ # Add/Modify expressions in datastructure
+ for key, uniq_expr in keys:
+ # Check which operation we are trying to do, add or modify
+ if debug[0]:
+ if key in self.data.keys():
+ output = self.debug_output['mod'].format( key )
+ else:
+ output = self.debug_output['add'].format( key )
+ print( debug[1] and output or ansi_escape.sub( '', output ) )
+
+ self.data[ key ] = uniq_expr
+
+ def merge( self, merge_in, debug ):
+ '''
+ Merge in the given datastructure to this datastructure
+
+ This datastructure serves as the base.
+
+ @param merge_in: Data structure from another organization to merge into this one
+ @param debug: Enable debug out
+ '''
+ # The default case is just to add the expression in directly
+ for key, kll_expression in merge_in.data.items():
+ # Display key:expression being merged in
+ if debug[0]:
+ output = merge_in.elem_str( key, True )
+ print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
+
+ self.add_expression( kll_expression, debug )
+
+ def reduction( self ):
+ '''
+ Simplifies datastructure
+
+ Most of the datastructures don't have a reduction. Just do nothing in this case.
+ '''
+ pass
+
+ def elem_str( self, key, single=False ):
+ '''
+ Debug output for a single element
+
+ @param key: Index to datastructure
+ @param single: Setting to True will bold the key
+ '''
+ if single:
+ return "\033[1;33m{0: <20}\033[0m \033[1;36;41m>\033[0m {1}\n".format( key, self.data[ key ] )
+ else:
+ return "{0: <20} \033[1;36;41m>\033[0m {1}\n".format( key, self.data[ key ] )
+
+ def __repr__( self ):
+ output = ""
+
+ # Display sorted list of keys, along with the internal value
+ for key in sorted( self.data ):
+ output += self.elem_str( key )
+
+ return output
+
+
+class MappingData( Data ):
+ '''
+ KLL datastructure for data mapping
+
+ ScanCode trigger -> result
+ USBCode trigger -> result
+ Animation trigger -> result
+ '''
+ def add_expression( self, expression, debug ):
+ '''
+ Add expression to data structure
+
+ May have multiple keys to add for a given expression
+
+ Map expressions insert into the datastructure according to their operator.
+
+ +Operators+
+ : Add/Modify
+ :+ Append
+ :- Remove
+ :: Lazy Add/Modify
+
+ i: Add/Modify
+ i:+ Append
+ i:- Remove
+ i:: Lazy Add/Modify
+
+ The i or isolation operators are stored separately from the main ones.
+ Each key is pre-pended with an i
+
+ The :: or lazy operators act just like : operators, except that they will be ignore if the evaluation
+ merge cannot resolve a ScanCode.
+
+ @param expression: KLL Expression (fully tokenized and parsed)
+ @param debug: Enable debug output
+ '''
+ # Lookup unique keys for expression
+ keys = expression.unique_keys()
+
+ # Add/Modify expressions in datastructure
+ for key, uniq_expr in keys:
+ # Determine which the expression operator
+ operator = expression.operator
+
+ # Except for the : operator, all others have delayed action
+ # Meaning, they change behaviour depending on how Contexts are merged
+ # This means we can't simplify yet
+ # In addition, :+ and :- are stackable, which means each key has a list of expressions
+ # We append the operator to differentiate between the different types of delayed operations
+ key = "{0}{1}".format( operator, key )
+
+ # Determine if key exists already
+ exists = key in self.data.keys()
+
+ # Add/Modify
+ if operator in [':', '::', 'i:', 'i::']:
+ debug_tag = exists and 'mod' or 'add'
+
+ # Append/Remove
+ else:
+ # Check to make sure we haven't already appended expression
+ # Use the string representation to do the comparison (general purpose)
+ if exists and "{0}".format( uniq_expr ) in [ "{0}".format( elem ) for elem in self.data[ key ] ]:
+ debug_tag = 'dup'
+
+ # Append
+ elif operator in [':+', 'i:+']:
+ debug_tag = 'app'
+
+ # Remove
+ else:
+ debug_tag = 'rem'
+
+ # Debug output
+ if debug[0]:
+ output = self.debug_output[ debug_tag ].format( key )
+ print( debug[1] and output or ansi_escape.sub( '', output ) )
+
+ # Don't append if a duplicate
+ if debug_tag == 'dup':
+ continue
+
+ # Append, rather than replace
+ if operator in [':+', ':-', 'i:+', 'i:-']:
+ if exists:
+ self.data[ key ].append( uniq_expr )
+
+ # Create initial list
+ else:
+ self.data[ key ] = [ uniq_expr ]
+ else:
+ self.data[ key ] = [ uniq_expr ]
+
+ def set_interconnect_id( self, interconnect_id, triggers ):
+ '''
+ Traverses the sequence of combo of identifiers to set the interconnect_id
+ '''
+ for sequence in triggers:
+ for combo in sequence:
+ for identifier in combo:
+ identifier.interconnect_id = interconnect_id
+
+ def merge( self, merge_in, debug ):
+ '''
+ Merge in the given datastructure to this datastructure
+
+ This datastructure serves as the base.
+
+ Map expressions merge differently than insertions.
+
+ +Operators+
+ : Add/Modify - Replace
+ :+ Append - Add
+ :- Remove - Remove
+ :: Lazy Add/Modify - Replace if found, otherwise drop
+
+ i: Add/Modify - Replace
+ i:+ Append - Add
+ i:- Remove - Remove
+ i:: Lazy Add/Modify - Replace if found, otherwise drop
+
+ @param merge_in: Data structure from another organization to merge into this one
+ @param debug: Enable debug out
+ '''
+ # Check what the current interconnectId is
+ # If not set, we set to 0 (default)
+ # We use this to calculate the scancode during the DataAnalysisStage
+ interconnect_id = 0
+ if 'interconnectId' in self.parent.variable_data.data.keys():
+ interconnect_id = self.parent.variable_data.data['interconnectId']
+
+ # Sort different types of keys
+ cur_keys = merge_in.data.keys()
+
+ # Lazy Set ::
+ lazy_keys = [ key for key in cur_keys if key[0:2] == '::' or key[0:3] == 'i::' ]
+ cur_keys = list( set( cur_keys ) - set( lazy_keys ) )
+
+ # Append :+
+ append_keys = [ key for key in cur_keys if key[0:2] == ':+' or key[0:3] == 'i:+' ]
+ cur_keys = list( set( cur_keys ) - set( append_keys ) )
+
+ # Remove :-
+ remove_keys = [ key for key in cur_keys if key[0:2] == ':-' or key[0:3] == 'i:-' ]
+ cur_keys = list( set( cur_keys ) - set( remove_keys ) )
+
+ # Set :
+ # Everything left is just a set
+ set_keys = cur_keys
+
+
+ # First process the :: (or lazy) operators
+ # We need to read into this datastructure and apply those first
+ # Otherwise we may get undesired behaviour
+ for key in lazy_keys:
+ # Display key:expression being merged in
+ if debug[0]:
+ output = merge_in.elem_str( key, True )
+ print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
+
+ # Construct target key
+ target_key = key[0] == 'i' and "i{0}".format( key[2:] ) or key[1:]
+
+ # If target key exists, replace
+ if target_key in self.data.keys():
+ debug_tag = 'mod'
+ else:
+ debug_tag = 'drp'
+
+ # Debug output
+ if debug[0]:
+ output = self.debug_output[ debug_tag ].format( key )
+ print( debug[1] and output or ansi_escape.sub( '', output ) )
+
+ # Only replace
+ if debug_tag == 'mod':
+ self.data[ target_key ] = merge_in.data[ key ]
+
+ # Then apply : assignment operators
+ for key in set_keys:
+ # Display key:expression being merged in
+ if debug[0]:
+ output = merge_in.elem_str( key, True )
+ print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
+
+ # Construct target key
+ target_key = key
+
+ # Indicate if add or modify
+ if target_key in self.data.keys():
+ debug_tag = 'mod'
+ else:
+ debug_tag = 'add'
+
+ # Debug output
+ if debug[0]:
+ output = self.debug_output[ debug_tag ].format( key )
+ print( debug[1] and output or ansi_escape.sub( '', output ) )
+
+ # Set into new datastructure regardless
+ self.data[ target_key ] = merge_in.data[ key ]
+
+ # Only the : is used to set ScanCodes
+ # We need to set the interconnect_id just in case the base context has it set
+ # and in turn influence the new context as well
+ # This must be done during the merge
+ for elem in self.data[ target_key ]:
+ if elem.type == 'ScanCode':
+ self.set_interconnect_id( interconnect_id, elem.triggers )
+
+ # Now apply append operations
+ for key in append_keys:
+ # Display key:expression being merged in
+ if debug[0]:
+ output = merge_in.elem_str( key, True )
+ print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
+
+ # Construct target key
+ target_key = key[0] == 'i' and "i:{0}".format( key[3:] ) or ":{0}".format( key[2:] )
+
+ # Alwyays appending
+ debug_tag = 'app'
+
+ # Debug output
+ if debug[0]:
+ output = self.debug_output[ debug_tag ].format( key )
+ print( debug[1] and output or ansi_escape.sub( '', output ) )
+
+ # Extend list if it exists
+ if target_key in self.data.keys():
+ self.data[ target_key ].extend( merge_in.data[ key ] )
+ else:
+ self.data[ target_key ] = merge_in.data[ key ]
+
+ # Finally apply removal operations to this datastructure
+ # If the target removal doesn't exist, ignore silently (show debug message)
+ for key in remove_keys:
+ # Display key:expression being merged in
+ if debug[0]:
+ output = merge_in.elem_str( key, True )
+ print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
+
+ # Construct target key
+ target_key = key[0] == 'i' and "i:{0}".format( key[3:] ) or ":{0}".format( key[2:] )
+
+ # Drop right away if target datastructure doesn't have target key
+ if target_key not in self.data.keys():
+ debug_tag = 'drp'
+
+ # Debug output
+ if debug[0]:
+ output = self.debug_output[ debug_tag ].format( key )
+ print( debug[1] and output or ansi_escape.sub( '', output ) )
+
+ continue
+
+ # Compare expressions to be removed with the current set
+ # Use strings to compare
+ remove_expressions = [ "{0}".format( expr ) for expr in merge_in.data[ key ] ]
+ current_expressions = [ ( "{0}".format( expr ), expr ) for expr in self.data[ target_key ] ]
+ for string, expr in current_expressions:
+ debug_tag = 'drp'
+
+ # Check if an expression matches
+ if string in remove_expressions:
+ debug_tag = 'rem'
+
+ # Debug output
+ if debug[0]:
+ output = self.debug_output[ debug_tag ].format( key )
+ print( debug[1] and output or ansi_escape.sub( '', output ) )
+
+ # Remove if found
+ if debug_tag == 'rem':
+ self.data[ target_key ] = [ value for value in self.data.values() if value != expr ]
+
+ def reduction( self ):
+ '''
+ Simplifies datastructure
+
+ Used to replace all trigger HIDCode(USBCode)s with ScanCodes
+
+ NOTE: Make sure to create a new MergeContext before calling this as you lose data and prior context
+ '''
+ scan_code_lookup = {}
+
+ # Build dictionary of single ScanCodes first
+ for key, expr in self.data.items():
+ if expr[0].elems()[0] == 1 and expr[0].triggers[0][0][0].type == 'ScanCode':
+ scan_code_lookup[ key ] = expr
+
+ # Using this dictionary, replace all the trigger USB codes
+ new_data = copy.copy( scan_code_lookup )
+
+ # 1) Single USB Codes trigger results will replace the original ScanCode result
+ # 2)
+
+ #TODO
+ print("YAY")
+ print( scan_code_lookup )
+
+
+class AnimationData( Data ):
+ '''
+ KLL datastructure for Animation configuration
+
+ Animation -> modifiers
+ '''
+
+
+class AnimationFrameData( Data ):
+ '''
+ KLL datastructure for Animation Frame configuration
+
+ Animation -> Pixel Settings
+ '''
+
+
+class CapabilityData( Data ):
+ '''
+ KLL datastructure for Capability mapping
+
+ Capability -> C Function/Identifier
+ '''
+
+
+class DefineData( Data ):
+ '''
+ KLL datastructure for Define mapping
+
+ Variable -> C Define/Identifier
+ '''
+
+
+class PixelChannelData( Data ):
+ '''
+ KLL datastructure for Pixel Channel mapping
+
+ Pixel -> Channels
+ '''
+
+
+class PixelPositionData( Data ):
+ '''
+ KLL datastructure for Pixel Position mapping
+
+ Pixel -> Physical Location
+ '''
+
+
+class ScanCodePositionData( Data ):
+ '''
+ KLL datastructure for ScanCode Position mapping
+
+ ScanCode -> Physical Location
+ '''
+
+
+class VariableData( Data ):
+ '''
+ KLL datastructure for Variables and Arrays
+
+ Variable -> Data
+ Array -> Data
+ '''
+
+
+class Organization:
+ '''
+ Container class for KLL datastructures
+
+ The purpose of these datastructures is to symbolically store at first, and slowly solve/deduplicate expressions.
+ Since the order in which the merges occurs matters, this involves a number of intermediate steps.
+ '''
+
+ def __init__( self ):
+ '''
+ Intialize data structure
+ '''
+ # Setup each of the internal sub-datastructures
+ self.animation_data = AnimationData( self )
+ self.animation_frame_data = AnimationFrameData( self )
+ self.capability_data = CapabilityData( self )
+ self.define_data = DefineData( self )
+ self.mapping_data = MappingData( self )
+ self.pixel_channel_data = PixelChannelData( self )
+ self.pixel_position_data = PixelPositionData( self )
+ self.scan_code_position_data = ScanCodePositionData( self )
+ self.variable_data = VariableData( self )
+
+ # Expression to Datastructure mapping
+ self.data_mapping = {
+ 'AssignmentExpression' : {
+ 'Array' : self.variable_data,
+ 'Variable' : self.variable_data,
+ },
+ 'DataAssociationExpression' : {
+ 'Animation' : self.animation_data,
+ 'AnimationFrame' : self.animation_frame_data,
+ 'PixelPosition' : self.pixel_position_data,
+ 'ScanCodePosition' : self.scan_code_position_data,
+ },
+ 'MapExpression' : {
+ 'ScanCode' : self.mapping_data,
+ 'USBCode' : self.mapping_data,
+ 'Animation' : self.mapping_data,
+ 'PixelChannel' : self.pixel_channel_data,
+ },
+ 'NameAssociationExpression' : {
+ 'Capability' : self.capability_data,
+ 'Define' : self.define_data,
+ },
+ }
+
+ def stores( self ):
+ '''
+ Returns list of sub-datastructures
+ '''
+ return [
+ self.animation_data,
+ self.animation_frame_data,
+ self.capability_data,
+ self.define_data,
+ self.mapping_data,
+ self.pixel_channel_data,
+ self.pixel_position_data,
+ self.scan_code_position_data,
+ self.variable_data,
+ ]
+
+ def add_expression( self, expression, debug ):
+ '''
+ Add expression to datastructure
+
+ Will automatically determine which type of expression and place in the relevant store
+
+ @param expression: KLL Expression (fully tokenized and parsed)
+ @param debug: Enable debug output
+ '''
+ # Determine type of of Expression
+ expression_type = expression.__class__.__name__
+
+ # Determine Expression Subtype
+ expression_subtype = expression.type
+
+ # Locate datastructure
+ data = self.data_mapping[ expression_type ][ expression_subtype ]
+
+ # Debug output
+ if debug[0]:
+ output = "\t\033[4m{0}\033[0m".format( data.__class__.__name__ )
+ print( debug[1] and output or ansi_escape.sub( '', output ) )
+
+ # Add expression to determined datastructure
+ data.add_expression( expression, debug )
+
+ def merge( self, merge_in, debug ):
+ '''
+ Merge in the given organization to this organization
+
+ This organization serves as the base.
+
+ @param merge_in: Organization to merge into this one
+ @param debug: Enable debug out
+ '''
+ # Merge each of the sub-datastructures
+ for this, that in zip( self.stores(), merge_in.stores() ):
+ this.merge( that, debug )
+
+ def reduction( self ):
+ '''
+ Simplifies datastructure
+
+ NOTE: This will remove data, therefore, context is lost
+ '''
+ for store in self.stores():
+ store.reduction()
+
+ def __repr__( self ):
+ return "{0}".format( self.stores() )
+
diff --git a/common/parse.py b/common/parse.py
new file mode 100644
index 0000000..b9ee3f1
--- /dev/null
+++ b/common/parse.py
@@ -0,0 +1,830 @@
+#!/usr/bin/env python3
+'''
+KLL Parsing Expressions
+
+This file contains various parsing rules and processors used by funcparserlib for KLL
+
+REMEMBER: When editing parser BNF-like expressions, order matters. Specifically lexer tokens and parser |
+'''
+
+# Parser doesn't play nice with linters, disable some checks
+# pylint: disable=no-self-argument, too-many-public-methods, no-self-use, bad-builtin
+
+# Copyright (C) 2016 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 .
+
+### Imports ###
+
+from common.hid_dict import kll_hid_lookup_dictionary
+
+from common.id import (
+ AnimationId, AnimationFrameId,
+ CapArgId, CapId,
+ HIDId,
+ NoneId,
+ PixelId, PixelLayerId,
+ ScanCodeId
+)
+from common.modifier import AnimationModifierList
+from common.schedule import AnalogScheduleParam, ScheduleParam, Time
+
+from funcparserlib.lexer import Token
+from funcparserlib.parser import (some, a, many, oneplus, skip, maybe)
+
+
+
+
+### Decorators ###
+
+## Print Decorator Variables
+ERROR = '\033[5;1;31mERROR\033[0m:'
+WARNING = '\033[5;1;33mWARNING\033[0m:'
+
+
+
+### Classes ###
+
+## Parsing Functions
+
+class Make:
+ '''
+ Collection of parse string interpreters
+ '''
+
+ def scanCode( token ):
+ '''
+ Converts a raw scan code string into an ScanCodeId /w integer
+
+ S0x10 -> 16
+ '''
+ if isinstance( token, int ):
+ return ScanCodeId( token )
+ else:
+ return ScanCodeId( int( token[1:], 0 ) )
+
+ def hidCode( type, token ):
+ '''
+ Convert a given raw hid token string to an integer /w a type
+
+ U"Enter" -> USB, Enter(0x28)
+ '''
+ # If already converted to a HIDId, just return
+ if isinstance( token, HIDId ):
+ return token
+
+ # If first character is a U or I, strip
+ if token[0] == "U" or token[0] == "I":
+ token = token[1:]
+ # CONS specifier
+ elif 'CONS' in token:
+ token = token[4:]
+ # SYS specifier
+ elif 'SYS' in token:
+ token = token[3:]
+
+ # If using string representation of USB Code, do lookup, case-insensitive
+ if '"' in token:
+ try:
+ hidCode = kll_hid_lookup_dictionary[ type ][ token[1:-1].upper() ][1]
+ except LookupError as err:
+ print ( "{0} {1} is an invalid USB HID Code Lookup...".format( ERROR, err ) )
+ raise
+ else:
+ # Already tokenized
+ if (
+ type == 'USBCode' and token[0] == 'USB'
+ or
+ type == 'SysCode' and token[0] == 'SYS'
+ or
+ type == 'ConsCode' and token[0] == 'CONS'
+ or
+ type == 'IndCode' and token[0] == 'IND'
+ ):
+ hidCode = token[1]
+ # Convert
+ else:
+ hidCode = int( token, 0 )
+
+ return HIDId( type, hidCode )
+
+
+ def usbCode( token ):
+ '''
+ Convert a given raw USB Keyboard hid token string to an integer /w a type
+
+ U"Enter" -> USB, Enter(0x28)
+ '''
+ return Make.hidCode( 'USBCode', token )
+
+ def consCode( token ):
+ '''
+ Convert a given raw Consumer Control hid token string to an integer /w a type
+ '''
+ return Make.hidCode( 'ConsCode', token )
+
+ def sysCode( token ):
+ '''
+ Convert a given raw System Control hid token string to an integer /w a type
+ '''
+ return Make.hidCode( 'SysCode', token )
+
+ def indCode( token ):
+ '''
+ Convert a given raw Indicator hid token string to an integer /w a type
+ '''
+ return Make.hidCode( 'IndCode', token )
+
+ def animation( name ):
+ '''
+ Converts a raw animation value into an AnimationId /w name
+
+ A"myname" -> myname
+ '''
+ if name[0] == "A":
+ return AnimationId( name[2:-1] )
+ else:
+ return AnimationId( name )
+
+ def animationTrigger( animation, frame_indices ):
+ '''
+ Generate either an AnimationId or an AnimationFrameId
+
+ frame_indices indicate that this is an AnimationFrameId
+ '''
+ trigger_list = []
+ # AnimationFrameId
+ if len( frame_indices ) > 0:
+ for index in frame_indices:
+ trigger_list.append( [ [ AnimationFrameId( animation, index ) ] ] )
+ # AnimationId
+ else:
+ trigger_list.append( [ [ AnimationId( animation ) ] ] )
+
+ return trigger_list
+
+ def animationCapability( animation, modifiers ):
+ '''
+ Apply modifiers to AnimationId
+ '''
+ if modifiers is not None:
+ animation.setModifiers( modifiers )
+ return [ animation ]
+
+ def animationModlist( modifiers ):
+ '''
+ Build an AnimationModifierList
+
+ Only used for animation data association
+ '''
+ modlist = AnimationModifierList()
+ modlist.setModifiers( modifiers )
+ return modlist
+
+ def pixelCapability( pixels, modifiers ):
+ '''
+ Apply modifiers to list of pixels/pixellists
+
+ Results in a combination of pixel capabilities
+ '''
+ pixelcap_list = []
+ for pixel in pixels:
+ pixel.setModifiers( modifiers )
+ pixelcap_list.append( pixel )
+ return pixelcap_list
+
+ def pixel( token ):
+ '''
+ Converts a raw pixel value into a PixelId /w integer
+
+ P0x3 -> 3
+ '''
+ if isinstance( token, int ):
+ return PixelId( token )
+ else:
+ return PixelId( int( token[1:], 0 ) )
+
+ def pixel_list( pixel_list ):
+ '''
+ Converts a list a numbers into a list of PixelIds
+ '''
+ pixels = []
+ for pixel in pixel_list:
+ pixels.append( PixelId( pixel ) )
+ return pixels
+
+ def pixelLayer( token ):
+ '''
+ Converts a raw pixel layer value into a PixelLayerId /w integer
+
+ PL0x3 -> 3
+ '''
+ if isinstance( token, int ):
+ return PixelLayerId( token )
+ else:
+ return PixelLayerId( int( token[2:], 0 ) )
+
+ def pixelLayer_list( layer_list ):
+ '''
+ Converts a list a numbers into a list of PixelLayerIds
+ '''
+ layers = []
+ for layer in layer_list:
+ layers.append( PixelLayerId( layer ) )
+ return layers
+
+ def pixelchan( pixel_list, chans ):
+ '''
+ Apply channels to PixelId
+
+ Only one pixel at a time can be mapped, hence pixel_list[0]
+ '''
+ pixel = pixel_list[0]
+ pixel.setChannels( chans )
+ return pixel
+
+ def pixelmod( pixels, modifiers ):
+ '''
+ Apply modifiers to list of pixels/pixellists
+
+ Results in a combination of pixel capabilities
+ '''
+ pixelcap_list = []
+ for pixel in pixels:
+ pixel.setModifiers( modifiers )
+ pixelcap_list.append( pixel )
+ return pixelcap_list
+
+ def position( token ):
+ '''
+ Physical position split
+
+ x:20 -> (x, 20)
+ '''
+ return token.split(':')
+
+ def usbCode_number( token ):
+ '''
+ USB Keyboard HID Code lookup
+ '''
+ return HIDId( 'USBCode', token )
+
+ def consCode_number( token ):
+ '''
+ Consumer Control HID Code lookup
+ '''
+ return HIDId( 'ConsCode', token )
+
+ def sysCode_number( token ):
+ '''
+ System Control HID Code lookup
+ '''
+ return HIDId( 'SysCode', token )
+
+ def indCode_number( token ):
+ '''
+ Indicator HID Code lookup
+ '''
+ return HIDId( 'IndCode', token )
+
+ def none( token ):
+ '''
+ Replace key-word with NoneId specifier (which indicates a noneOut capability)
+ '''
+ return [[[NoneId()]]]
+
+ def seqString( token ):
+ '''
+ Converts sequence string to a sequence of combinations
+
+ 'Ab' -> U"Shift" + U"A", U"B"
+ '''
+ # TODO - Add locale support
+
+ # Shifted Characters, and amount to move by to get non-shifted version
+ # US ANSI
+ shiftCharacters = (
+ ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0x20 ),
+ ( "+", 0x12 ),
+ ( "&(", 0x11 ),
+ ( "!#$%", 0x10 ),
+ ( "*", 0x0E ),
+ ( ")", 0x07 ),
+ ( '"', 0x05 ),
+ ( ":", 0x01 ),
+ ( "@", -0x0E ),
+ ( "<>?", -0x10 ),
+ ( "~", -0x1E ),
+ ( "{}|", -0x20 ),
+ ( "^", -0x28 ),
+ ( "_", -0x32 ),
+ )
+
+ listOfLists = []
+ shiftKey = kll_hid_lookup_dictionary['USBCode']["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
+ usb_code = kll_hid_lookup_dictionary['USBCode'][ processedChar.upper() ]
+
+ # Create Combo for this character, add shift key if shifted
+ charCombo = []
+ if shiftCombo:
+ charCombo = [ [ HIDId( 'USBCode', shiftKey[1] ) ] ]
+ charCombo.append( [ HIDId( 'USBCode', usb_code[1] ) ] )
+
+ # Add to list of lists
+ listOfLists.append( charCombo )
+
+ return listOfLists
+
+ def string( token ):
+ '''
+ Converts a raw string to a Python string
+
+ "this string" -> this string
+ '''
+ return token[1:-1]
+
+ def unseqString( token ):
+ '''
+ Converts a raw sequence string to a Python string
+
+ 'this string' -> this string
+ '''
+ return token[1:-1]
+
+ def number( token ):
+ '''
+ Convert string number to Python integer
+ '''
+ return int( token, 0 )
+
+ def timing( token ):
+ '''
+ Convert raw timing parameter to integer time and determine units
+
+ 1ms -> 1, ms
+ '''
+ # Find ms, us, or s
+ if 'ms' in token:
+ unit = 'ms'
+ num = token.split('m')[0]
+ elif 'us' in token:
+ unit = 'us'
+ num = token.split('u')[0]
+ elif 'ns' in token:
+ unit = 'ns'
+ num = token.split('n')[0]
+ elif 's' in token:
+ unit = 's'
+ num = token.split('s')[0]
+ else:
+ print ( "{0} cannot find timing unit in token '{1}'".format( ERROR, token ) )
+
+ return Time( float( num ), unit )
+
+ def specifierTiming( timing ):
+ '''
+ When only timing is given, infer state at a later stage from the context of the mapping
+ '''
+ return ScheduleParam( None, timing )
+
+ def specifierState( state, timing=None ):
+ '''
+ Generate a Schedule Parameter
+ Automatically mutates itself into the correct object type
+ '''
+ return ScheduleParam( state, timing )
+
+ def specifierAnalog( value ):
+ '''
+ Generate an Analog Schedule Parameter
+ '''
+ return AnalogScheduleParam( value )
+
+ def specifierUnroll( identifier, schedule_params ):
+ '''
+ Unroll specifiers into the trigger/result identifier
+
+ First, combine all Schedule Parameters into a Schedul
+ Then attach Schedule to the identifier
+
+ If the identifier is a list, then iterate through them
+ and apply the schedule to each
+ '''
+ # Check if this is a list of identifiers
+ if isinstance( identifier, list ):
+ for ident in identifier:
+ ident.setSchedule( schedule_params )
+ return identifier
+ else:
+ identifier.setSchedule( schedule_params )
+
+ return [ identifier ]
+
+
+ # Range can go from high to low or low to high
+ def scanCode_range( rangeVals ):
+ '''
+ Scan Code range expansion
+
+ S[0x10-0x12] -> S0x10, S0x11, S0x12
+ '''
+ 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
+ values = list( range( start, end + 1 ) )
+
+ # Generate ScanCodeIds
+ return [ ScanCodeId( v ) for v in values ]
+
+ # Range can go from high to low or low to high
+ # Warn on 0-9 for USBCodes (as this does not do what one would expect) TODO
+ # Lookup USB HID tags and convert to a number
+ def hidCode_range( type, rangeVals ):
+ '''
+ HID Code range expansion
+
+ U["A"-"C"] -> U"A", U"B", U"C"
+ '''
+
+ # Check if already integers
+ if isinstance( rangeVals[0], int ):
+ start = rangeVals[0]
+ else:
+ start = Make.hidCode( type, rangeVals[0] ).uid
+
+ if isinstance( rangeVals[1], int ):
+ end = rangeVals[1]
+ else:
+ end = Make.hidCode( type, rangeVals[1] ).uid
+
+ # 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
+ listRange = list( range( start, end + 1 ) )
+
+ # Convert each item in the list to a tuple
+ for item in range( len( listRange ) ):
+ listRange[ item ] = HIDId( type, listRange[ item ] )
+ return listRange
+
+ def usbCode_range( rangeVals ):
+ '''
+ USB Keyboard HID Code range expansion
+ '''
+ return Make.hidCode_range( 'USBCode', rangeVals )
+
+ def sysCode_range( rangeVals ):
+ '''
+ System Control HID Code range expansion
+ '''
+ return Make.hidCode_range( 'SysCode', rangeVals )
+
+ def consCode_range( rangeVals ):
+ '''
+ Consumer Control HID Code range expansion
+ '''
+ return Make.hidCode_range( 'ConsCode', rangeVals )
+
+ def indCode_range( rangeVals ):
+ '''
+ Indicator HID Code range expansion
+ '''
+ return Make.hidCode_range( 'IndCode', rangeVals )
+
+ def range( start, end ):
+ '''
+ Converts a start and end points of a range to a list of numbers
+
+ Can go low to high or high to low
+ '''
+ # High to low
+ if end < start:
+ return list( range( end, start + 1 ) )
+
+ # Low to high
+ return list( range( start, end + 1 ) )
+
+ def capArg( argument, width=None ):
+ '''
+ Converts a capability argument:width to a CapArgId
+
+ If no width is specified, it is ignored
+ '''
+ return CapArgId( argument, width )
+
+ def capUsage( name, arguments ):
+ '''
+ Converts a capability tuple, argument list to a CapId Usage
+ '''
+ return CapId( name, 'Usage', arguments )
+
+
+
+### Rules ###
+
+## 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
+bracket = lambda s: a( Token( 'Bracket', s ) ) >> tokenValue
+eol = a( Token( 'EndOfLine', ';' ) )
+
+def maybeFlatten( items ):
+ '''
+ Iterate through top-level lists
+ Flatten, only if the element is also a list
+
+ [[1,2],3,[[4,5]]] -> [1,2,3,[4,5]]
+ '''
+ new_list = []
+ for elem in items:
+ # Flatten only if a list
+ if isinstance( elem, list ):
+ new_list.extend( elem )
+ else:
+ new_list.append( elem )
+ return new_list
+
+def listElem( item ):
+ '''
+ Convert to a list element
+ '''
+ return [ item ]
+
+def listToTuple( items ):
+ '''
+ Convert list to a tuple
+ '''
+ return tuple( items )
+
+def oneLayerFlatten( items ):
+ '''
+ Flatten only the top layer (list of lists of ...)
+ '''
+ mainList = []
+ for sublist in items:
+ for item in sublist:
+ mainList.append( item )
+
+ return mainList
+
+def optionExpansion( sequences ):
+ '''
+ Expand ranges of values in the 3rd dimension of the list, to a list of 2nd lists
+
+ i.e. [ sequence, [ combo, [ range ] ] ] --> [ [ sequence, [ combo ] ],