2 # KLL Compiler Containers
4 # Copyright (C) 2014 by Jacob Alexander
6 # This file is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This file is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this file. If not, see <http://www.gnu.org/licenses/>.
25 ## Print Decorator Variables
26 ERROR = '\033[5;1;31mERROR\033[0m:'
34 # Container for capabilities dictionary and convenience functions
36 self.capabilities = dict()
38 def __getitem__( self, name ):
39 return self.capabilities[ name ]
41 def __setitem__( self, name, contents ):
42 self.capabilities[ name ] = contents
45 return "Capabilities => {0}\nIndexed Capabilities => {1}".format( self.capabilities, sorted( self.capabilities, key = self.capabilities.get ) )
48 # Total bytes needed to store arguments
49 def totalArgBytes( self, name ):
52 # Iterate over the arguments, summing the total bytes
53 for arg in self.capabilities[ name ][ 1 ]:
54 totalBytes += int( arg[ 1 ] )
58 # Name of the capability function
59 def funcName( self, name ):
60 return self.capabilities[ name ][ 0 ]
63 # Only valid while dictionary keys are not added/removed
64 def getIndex( self, name ):
65 return sorted( self.capabilities, key = self.capabilities.get ).index( name )
67 def getName( self, index ):
68 return sorted( self.capabilities, key = self.capabilities.get )[ index ]
71 return sorted( self.capabilities, key = self.capabilities.get )
75 # Container for Trigger Macro : Result Macro correlation
76 # Layer selection for generating TriggerLists
78 # Only convert USB Code list once all the ResultMacros have been accumulated (does a macro reduction; not reversible)
79 # Two staged list for ResultMacros:
80 # 1) USB Code/Non-converted (may contain capabilities)
87 self.macros = [ dict() ]
89 # Correlated Macro Data
90 self.resultsIndex = dict()
91 self.triggersIndex = dict()
92 self.resultsIndexSorted = []
93 self.triggersIndexSorted = []
97 # USBCode Assignment Cache
98 self.assignmentCache = []
100 def __repr__( self ):
101 return "{0}".format( self.macros )
103 def addLayer( self ):
104 # Increment layer count, and append another macros dictionary
106 self.macros.append( dict() )
108 # Use for ScanCode trigger macros
109 def appendScanCode( self, trigger, result ):
110 if not trigger in self.macros[ self.layer ]:
111 self.replaceScanCode( trigger, result )
113 self.macros[ self.layer ][ trigger ].append( result )
115 # Remove the given trigger/result pair
116 def removeScanCode( self, trigger, result ):
117 # Remove all instances of the given trigger/result pair
118 while result in self.macros[ self.layer ][ trigger ]:
119 self.macros[ self.layer ][ trigger ].remove( result )
121 # Replaces the given trigger with the given result
122 # If multiple results for a given trigger, clear, then add
123 def replaceScanCode( self, trigger, result ):
124 self.macros[ self.layer ][ trigger ] = [ result ]
126 # Return a list of ScanCode triggers with the given USB Code trigger
127 def lookupUSBCodes( self, usbCode ):
130 # Scan current layer for USB Codes
131 for macro in self.macros[ self.layer ].keys():
132 if usbCode in self.macros[ self.layer ][ macro ]:
133 scanCodeList.append( macro )
137 # Cache USBCode Assignment
138 def cacheAssignment( self, operator, scanCode, result ):
139 self.assignmentCache.append( [ operator, scanCode, result ] )
141 # Assign cached USBCode Assignments
142 def replayCachedAssignments( self ):
143 # Iterate over each item in the assignment cache
144 for item in self.assignmentCache:
145 # Check operator, and choose the specified assignment action
148 self.appendScanCode( item[1], item[2] )
151 elif item[0] == ":-":
152 self.removeScanCode( item[1], item[2] )
156 self.replaceScanCode( item[1], item[2] )
158 # Clear assignment cache
159 self.assignmentCache = []
161 # Generate/Correlate Layers
162 def generate( self ):
163 self.generateIndices()
164 self.sortIndexLists()
165 self.generateTriggerLists()
167 # Generates Index of Results and Triggers
168 def generateIndices( self ):
169 # Iterate over every trigger result, and add to the resultsIndex and triggersIndex
170 for layer in range( 0, len( self.macros ) ):
171 for trigger in self.macros[ layer ].keys():
172 # Each trigger has a list of results
173 for result in self.macros[ layer ][ trigger ]:
174 # Only add, with an index, if result hasn't been added yet
175 if not result in self.resultsIndex:
176 self.resultsIndex[ result ] = len( self.resultsIndex )
178 # Then add a trigger for each result, if trigger hasn't been added yet
179 triggerItem = tuple( [ trigger, self.resultsIndex[ result ] ] )
180 if not triggerItem in self.triggersIndex:
181 self.triggersIndex[ triggerItem ] = len( self.triggersIndex )
183 # Sort Index Lists using the indices rather than triggers/results
184 def sortIndexLists( self ):
185 self.resultsIndexSorted = [ None ] * len( self.resultsIndex )
186 # Iterate over the resultsIndex and sort by index
187 for result in self.resultsIndex.keys():
188 self.resultsIndexSorted[ self.resultsIndex[ result ] ] = result
190 self.triggersIndexSorted = [ None ] * len( self.triggersIndex )
191 # Iterate over the triggersIndex and sort by index
192 for trigger in self.triggersIndex.keys():
193 self.triggersIndexSorted[ self.triggersIndex[ trigger ] ] = trigger
195 # Generates Trigger Lists per layer using index lists
196 def generateTriggerLists( self ):
197 for layer in range( 0, len( self.macros ) ):
198 # Set max scancode to 0xFF (255)
199 # But keep track of the actual max scancode and reduce the list size
200 self.triggerList.append( [ [] ] * 0xFF )
201 self.maxScanCode.append( 0x00 )
203 # Iterate through trigger macros to locate necessary ScanCodes and corresponding triggerIndex
204 for trigger in self.macros[ layer ].keys():
205 for variant in range( 0, len( self.macros[ layer ][ trigger ] ) ):
206 # Identify result index
207 resultIndex = self.resultsIndex[ self.macros[ layer ][ trigger ][ variant ] ]
209 # Identify trigger index
210 triggerIndex = self.triggersIndex[ tuple( [ trigger, resultIndex ] ) ]
212 # Iterate over the trigger to locate the ScanCodes
213 for sequence in trigger:
214 for combo in sequence:
215 # Append triggerIndex for each found scanCode of the Trigger List
216 # Do not re-add if triggerIndex is already in the Trigger List
217 if not triggerIndex in self.triggerList[ layer ][ combo ]:
218 # Append is working strangely with list pre-initialization
219 # Doing a 0 check replacement instead -HaaTa
220 if len( self.triggerList[ layer ][ combo ] ) == 0:
221 self.triggerList[ layer ][ combo ] = [ triggerIndex ]
223 self.triggerList[ layer ][ combo ].append( triggerIndex )
225 # Look for max Scan Code
226 if combo > self.maxScanCode[ layer ]:
227 self.maxScanCode[ layer ] = combo
229 # Shrink triggerList to actual max size
230 self.triggerList[ layer ] = self.triggerList[ layer ][ : self.maxScanCode[ layer ] + 1 ]
232 # Determine overall maxScanCode
233 self.overallMaxScanCode = 0x00
234 for maxVal in self.maxScanCode:
235 if maxVal > self.overallMaxScanCode:
236 self.overallMaxScanCode = maxVal