- Required changing the ScanCode node datastructure - Interconnect Id's must be stored until the end as it's not possible to calculate the max per node ScanCode until after all the assignments are complete - Should make future additions more straight-forward (that require per ScanCode information to be stored)master
# Add the trigger macro scan code guide | # Add the trigger macro scan code guide | ||||
# See kiibohd controller Macros/PartialMap/kll.h for exact formatting details | # 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 | # For each combo in the sequence, add the length of the combo | ||||
self.fill_dict['TriggerMacros'] += "{0}, ".format( len( macros.triggersIndexSorted[ trigger ][0][ sequence ] ) ) | 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 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 Analog keys | ||||
# TODO Add support for LED states | # TODO Add support for LED states | ||||
self.fill_dict['MaxScanCode'] = "#define MaxScanCode 0x{0:X}".format( macros.overallMaxScanCode ) | 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 ## | ## Default Layer and Default Layer Scan Map ## | ||||
self.fill_dict['DefaultLayerTriggerList'] = "" | self.fill_dict['DefaultLayerTriggerList'] = "" | ||||
self.fill_dict['DefaultLayerScanMap'] = "const nat_ptr_t *default_scanMap[] = {\n" | 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 | # 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 | # 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 | # Add scanCode trigger list to Default Layer Scan Map | ||||
self.fill_dict['DefaultLayerScanMap'] += "default_tl_0x{0:02X}, ".format( triggerList ) | self.fill_dict['DefaultLayerScanMap'] += "default_tl_0x{0:02X}, ".format( triggerList ) | ||||
# Add each item of the trigger list | # 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'] += " };\n" | ||||
self.fill_dict['DefaultLayerTriggerList'] = self.fill_dict['DefaultLayerTriggerList'][:-1] # Remove last newline | self.fill_dict['DefaultLayerTriggerList'] = self.fill_dict['DefaultLayerTriggerList'][:-1] # Remove last newline |
return 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 | ## Evaluation Rules | ||||
def eval_scanCode( triggers, operator, results ): | def eval_scanCode( triggers, operator, results ): | ||||
triggers = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in triggers ) | 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 ) | 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 | # 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: | for result in results: | ||||
# Append Case | # Append Case | ||||
if operator == ":+": | if operator == ":+": |
## Containers | ## 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: | class Capabilities: | ||||
# Container for capabilities dictionary and convenience functions | # Container for capabilities dictionary and convenience functions | ||||
def __init__( self ): | def __init__( self ): | ||||
# Default layer (0) | # Default layer (0) | ||||
self.layer = 0 | self.layer = 0 | ||||
# Unique ScanCode Hash Id Lookup | |||||
self.scanCodeStore = ScanCodeStore() | |||||
# Macro Storage | # Macro Storage | ||||
self.macros = [ dict() ] | self.macros = [ dict() ] | ||||
self.triggerList = [] | self.triggerList = [] | ||||
self.maxScanCode = [] | self.maxScanCode = [] | ||||
self.firstScanCode = [] | self.firstScanCode = [] | ||||
self.interconnectOffset = [] | |||||
# USBCode Assignment Cache | # USBCode Assignment Cache | ||||
self.assignmentCache = [] | self.assignmentCache = [] | ||||
def generate( self ): | def generate( self ): | ||||
self.generateIndices() | self.generateIndices() | ||||
self.sortIndexLists() | self.sortIndexLists() | ||||
self.generateOffsetTable() | |||||
self.generateTriggerLists() | self.generateTriggerLists() | ||||
# Generates Index of Results and Triggers | # Generates Index of Results and Triggers | ||||
for trigger in self.triggersIndex.keys(): | for trigger in self.triggersIndex.keys(): | ||||
self.triggersIndexSorted[ self.triggersIndex[ trigger ] ] = trigger | 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 | # Generates Trigger Lists per layer using index lists | ||||
def generateTriggerLists( self ): | def generateTriggerLists( self ): | ||||
for layer in range( 0, len( self.macros ) ): | for layer in range( 0, len( self.macros ) ): | ||||
# Iterate over the trigger to locate the ScanCodes | # Iterate over the trigger to locate the ScanCodes | ||||
for sequence in trigger: | 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 | # Append triggerIndex for each found scanCode of the Trigger List | ||||
# Do not re-add if triggerIndex is already in the Trigger List | # Do not re-add if triggerIndex is already in the Trigger List | ||||
if not triggerIndex in self.triggerList[ layer ][ combo ]: | if not triggerIndex in self.triggerList[ layer ][ combo ]: |
// ----- Defines ----- | // ----- Defines ----- | ||||
// -- Interconnect Node Maximum -- | |||||
<|InterconnectNodeMax|> | |||||
<|Defines|> | <|Defines|> | ||||
<|PartialLayerTriggerLists|> | <|PartialLayerTriggerLists|> | ||||
// -- ScanCode Offset Map | |||||
// Maps interconnect ids to scancode offsets | |||||
// | |||||
// Only used for keyboards with an interconnect | |||||
<|ScanCodeInterconnectOffsetList|> | |||||
// -- ScanCode Indexed Maps | // -- ScanCode Indexed Maps | ||||
// Maps to a trigger list of macro pointers | // Maps to a trigger list of macro pointers | ||||
// _ | // _ |