diff --git a/backends/kiibohd.py b/backends/kiibohd.py index 9f47150..ae7d97f 100644 --- a/backends/kiibohd.py +++ b/backends/kiibohd.py @@ -219,13 +219,17 @@ class Backend( BackendBase ): # Add the trigger macro scan code guide # See kiibohd controller Macros/PartialMap/kll.h for exact formatting details - for sequence in range( 0, len( macros.triggersIndexSorted[ trigger ][ 0 ] ) ): + for sequence in range( 0, len( macros.triggersIndexSorted[ trigger ][0] ) ): # For each combo in the sequence, add the length of the combo self.fill_dict['TriggerMacros'] += "{0}, ".format( len( macros.triggersIndexSorted[ trigger ][0][ sequence ] ) ) # For each combo, add the key type, key state and scan code - for combo in range( 0, len( macros.triggersIndexSorted[ trigger ][ 0 ][ sequence ] ) ): - triggerItem = macros.triggersIndexSorted[ trigger ][ 0 ][ sequence ][ combo ] + for combo in range( 0, len( macros.triggersIndexSorted[ trigger ][0][ sequence ] ) ): + triggerItemId = macros.triggersIndexSorted[ trigger ][0][ sequence ][ combo ] + + # Lookup triggerItem in ScanCodeStore + triggerItemObj = macros.scanCodeStore[ triggerItemId ] + triggerItem = triggerItemObj.offset( macros.interconnectOffset ) # TODO Add support for Analog keys # TODO Add support for LED states @@ -254,21 +258,32 @@ class Backend( BackendBase ): self.fill_dict['MaxScanCode'] = "#define MaxScanCode 0x{0:X}".format( macros.overallMaxScanCode ) + ## Interconnect ScanCode Offset List ## + self.fill_dict['ScanCodeInterconnectOffsetList'] = "const uint8_t InterconnectOffsetList[] = {\n" + for offset in range( 0, len( macros.interconnectOffset ) ): + self.fill_dict['ScanCodeInterconnectOffsetList'] += "\t0x{0:02X},\n".format( macros.interconnectOffset[ offset ] ) + self.fill_dict['ScanCodeInterconnectOffsetList'] += "};" + + + ## Max Interconnect Nodes ## + self.fill_dict['InterconnectNodeMax'] = "#define InterconnectNodeMax 0x{0:X}\n".format( len( macros.interconnectOffset ) ) + + ## Default Layer and Default Layer Scan Map ## self.fill_dict['DefaultLayerTriggerList'] = "" self.fill_dict['DefaultLayerScanMap'] = "const nat_ptr_t *default_scanMap[] = {\n" # Iterate over triggerList and generate a C trigger array for the default map and default map array - for triggerList in range( macros.firstScanCode[ 0 ], len( macros.triggerList[ 0 ] ) ): + for triggerList in range( macros.firstScanCode[0], len( macros.triggerList[0] ) ): # Generate ScanCode index and triggerList length - self.fill_dict['DefaultLayerTriggerList'] += "Define_TL( default, 0x{0:02X} ) = {{ {1}".format( triggerList, len( macros.triggerList[ 0 ][ triggerList ] ) ) + self.fill_dict['DefaultLayerTriggerList'] += "Define_TL( default, 0x{0:02X} ) = {{ {1}".format( triggerList, len( macros.triggerList[0][ triggerList ] ) ) # Add scanCode trigger list to Default Layer Scan Map self.fill_dict['DefaultLayerScanMap'] += "default_tl_0x{0:02X}, ".format( triggerList ) # Add each item of the trigger list - for trigger in macros.triggerList[ 0 ][ triggerList ]: - self.fill_dict['DefaultLayerTriggerList'] += ", {0}".format( trigger ) + for triggerItem in macros.triggerList[0][ triggerList ]: + self.fill_dict['DefaultLayerTriggerList'] += ", {0}".format( triggerItem ) self.fill_dict['DefaultLayerTriggerList'] += " };\n" self.fill_dict['DefaultLayerTriggerList'] = self.fill_dict['DefaultLayerTriggerList'][:-1] # Remove last newline diff --git a/kll.py b/kll.py index b2ca7ef..aab66d2 100755 --- a/kll.py +++ b/kll.py @@ -478,6 +478,15 @@ def hidCodeToCapability( items ): return items +# Convert tuple of tuples to list of lists +def listit( t ): + return list( map( listit, t ) ) if isinstance( t, ( list, tuple ) ) else t + +# Convert list of lists to tuple of tuples +def tupleit( t ): + return tuple( map( tupleit, t ) ) if isinstance( t, ( tuple, list ) ) else t + + ## Evaluation Rules def eval_scanCode( triggers, operator, results ): @@ -486,8 +495,26 @@ def eval_scanCode( triggers, operator, results ): triggers = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in triggers ) results = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in results ) + # Lookup interconnect id (Current file scope) + # Default to 0 if not specified + if 'ConnectId' not in variables_dict.overallVariables.keys(): + id_num = 0 + else: + id_num = int( variables_dict.overallVariables['ConnectId'] ) + # Iterate over all combinations of triggers and results - for trigger in triggers: + for sequence in triggers: + # Convert tuple of tuples to list of lists so each element can be modified + trigger = listit( sequence ) + + # Create ScanCode entries for trigger + for seq_index, combo in enumerate( sequence ): + for com_index, scancode in enumerate( combo ): + trigger[ seq_index ][ com_index ] = macros_map.scanCodeStore.append( ScanCode( scancode, id_num ) ) + + # Convert back to a tuple of tuples + trigger = tupleit( trigger ) + for result in results: # Append Case if operator == ":+": diff --git a/kll_lib/containers.py b/kll_lib/containers.py index 2436c7e..66aa2f2 100644 --- a/kll_lib/containers.py +++ b/kll_lib/containers.py @@ -34,6 +34,67 @@ ERROR = '\033[5;1;31mERROR\033[0m:' ## Containers +class ScanCode: + # Container for ScanCodes + # + # scancode - Non-interconnect adjusted scan code + # interconnect_id - Unique id for the interconnect node + def __init__( self, scancode, interconnect_id ): + self.scancode = scancode + self.interconnect_id = interconnect_id + + def __eq__( self, other ): + return self.dict() == other.dict() + + def __repr__( self ): + return repr( self.dict() ) + + def dict( self ): + return { + 'ScanCode' : self.scancode, + 'Id' : self.interconnect_id, + } + + # Calculate the actual scancode using the offset list + def offset( self, offsetList ): + if self.interconnect_id > 0: + return self.scancode + offsetList[ self.interconnect_id - 1 ] + else: + return self.scancode + + +class ScanCodeStore: + # Unique lookup for ScanCodes + def __init__( self ): + self.scancodes = [] + + def __getitem__( self, name ): + # First check if this is a ScanCode object + if isinstance( name, ScanCode ): + # Do a reverse lookup + for idx, scancode in enumerate( self.scancodes ): + if scancode == name: + return idx + + # Could not find scancode + return None + + # Return scancode using unique id + return self.scancodes[ name ] + + # Attempt add ScanCode to list, return unique id + def append( self, new_scancode ): + # Iterate through list to make sure this is a unique ScanCode + for idx, scancode in enumerate( self.scancodes ): + if new_scancode == scancode: + return idx + + # Unique entry, add to the list + self.scancodes.append( new_scancode ) + + return len( self.scancodes ) - 1 + + class Capabilities: # Container for capabilities dictionary and convenience functions def __init__( self ): @@ -87,6 +148,9 @@ class Macros: # Default layer (0) self.layer = 0 + # Unique ScanCode Hash Id Lookup + self.scanCodeStore = ScanCodeStore() + # Macro Storage self.macros = [ dict() ] @@ -102,6 +166,7 @@ class Macros: self.triggerList = [] self.maxScanCode = [] self.firstScanCode = [] + self.interconnectOffset = [] # USBCode Assignment Cache self.assignmentCache = [] @@ -199,6 +264,7 @@ class Macros: def generate( self ): self.generateIndices() self.sortIndexLists() + self.generateOffsetTable() self.generateTriggerLists() # Generates Index of Results and Triggers @@ -229,6 +295,34 @@ class Macros: for trigger in self.triggersIndex.keys(): self.triggersIndexSorted[ self.triggersIndex[ trigger ] ] = trigger + # Generates list of offsets for each of the interconnect ids + def generateOffsetTable( self ): + idMaxScanCode = [ 0 ] + + # Iterate over each layer to get list of max scancodes associated with each interconnect id + for layer in range( 0, len( self.macros ) ): + # Iterate through each trigger/sequence in the layer + for sequence in self.macros[ layer ].keys(): + # Iterate over the trigger to locate the ScanCodes + for combo in sequence: + # Iterate over each scancode id in the combo + for scancode_id in combo: + # Lookup ScanCode + scancode_obj = self.scanCodeStore[ scancode_id ] + + # Extend list if not large enough + if scancode_obj.interconnect_id >= len( idMaxScanCode ): + idMaxScanCode.extend( [ 0 ] * ( scancode_obj.interconnect_id - len( idMaxScanCode ) + 1 ) ) + + # Determine if the max seen id for this interconnect id + if scancode_obj.scancode > idMaxScanCode[ scancode_obj.interconnect_id ]: + idMaxScanCode[ scancode_obj.interconnect_id ] = scancode_obj.scancode + + # Generate interconnect offsets + self.interconnectOffset = [ idMaxScanCode[0] + 1 ] + for index in range( 1, len( idMaxScanCode ) ): + self.interconnectOffset.append( self.interconnectOffset[ index - 1 ] + idMaxScanCode[ index ] ) + # Generates Trigger Lists per layer using index lists def generateTriggerLists( self ): for layer in range( 0, len( self.macros ) ): @@ -248,7 +342,8 @@ class Macros: # Iterate over the trigger to locate the ScanCodes for sequence in trigger: - for combo in sequence: + for combo_id in sequence: + combo = self.scanCodeStore[ combo_id ].offset( self.interconnectOffset ) # Append triggerIndex for each found scanCode of the Trigger List # Do not re-add if triggerIndex is already in the Trigger List if not triggerIndex in self.triggerList[ layer ][ combo ]: diff --git a/templates/kiibohdDefs.h b/templates/kiibohdDefs.h index 00b45a6..93531b0 100644 --- a/templates/kiibohdDefs.h +++ b/templates/kiibohdDefs.h @@ -25,5 +25,9 @@ // ----- Defines ----- + +// -- Interconnect Node Maximum -- +<|InterconnectNodeMax|> + <|Defines|> diff --git a/templates/kiibohdKeymap.h b/templates/kiibohdKeymap.h index baf78d9..2a8c6d6 100644 --- a/templates/kiibohdKeymap.h +++ b/templates/kiibohdKeymap.h @@ -91,6 +91,13 @@ <|PartialLayerTriggerLists|> +// -- ScanCode Offset Map +// Maps interconnect ids to scancode offsets +// +// Only used for keyboards with an interconnect +<|ScanCodeInterconnectOffsetList|> + + // -- ScanCode Indexed Maps // Maps to a trigger list of macro pointers // _