Browse Source

Adding support for Interconnect Nodes

- 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
Jacob Alexander 8 years ago
parent
commit
114d666bd8
5 changed files with 157 additions and 9 deletions
  1. 22
    7
      backends/kiibohd.py
  2. 28
    1
      kll.py
  3. 96
    1
      kll_lib/containers.py
  4. 4
    0
      templates/kiibohdDefs.h
  5. 7
    0
      templates/kiibohdKeymap.h

+ 22
- 7
backends/kiibohd.py View File



# 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

+ 28
- 1
kll.py View File

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 == ":+":

+ 96
- 1
kll_lib/containers.py View File



## 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 ]:

+ 4
- 0
templates/kiibohdDefs.h View File





// ----- Defines ----- // ----- Defines -----

// -- Interconnect Node Maximum --
<|InterconnectNodeMax|>

<|Defines|> <|Defines|>



+ 7
- 0
templates/kiibohdKeymap.h View File

<|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
// _ // _