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/>.
27 ## Print Decorator Variables
28 ERROR = '\033[5;1;31mERROR\033[0m:'
36 # Container for capabilities dictionary and convenience functions
38 self.capabilities = dict()
40 def __getitem__( self, name ):
41 return self.capabilities[ name ]
43 def __setitem__( self, name, contents ):
44 self.capabilities[ name ] = contents
47 return "Capabilities => {0}\nIndexed Capabilities => {1}".format( self.capabilities, sorted( self.capabilities, key = self.capabilities.get ) )
50 # Total bytes needed to store arguments
51 def totalArgBytes( self, name ):
54 # Iterate over the arguments, summing the total bytes
55 for arg in self.capabilities[ name ][ 1 ]:
56 totalBytes += int( arg[ 1 ] )
60 # Name of the capability function
61 def funcName( self, name ):
62 return self.capabilities[ name ][ 0 ]
65 # Only valid while dictionary keys are not added/removed
66 def getIndex( self, name ):
67 return sorted( self.capabilities, key = self.capabilities.get ).index( name )
69 def getName( self, index ):
70 return sorted( self.capabilities, key = self.capabilities.get )[ index ]
73 return sorted( self.capabilities, key = self.capabilities.get )
77 # Container for Trigger Macro : Result Macro correlation
78 # Layer selection for generating TriggerLists
80 # Only convert USB Code list once all the ResultMacros have been accumulated (does a macro reduction; not reversible)
81 # Two staged list for ResultMacros:
82 # 1) USB Code/Non-converted (may contain capabilities)
89 self.macros = [ dict() ]
92 self.baseLayout = None
93 self.layerLayoutMarkers = []
95 # Correlated Macro Data
96 self.resultsIndex = dict()
97 self.triggersIndex = dict()
98 self.resultsIndexSorted = []
99 self.triggersIndexSorted = []
100 self.triggerList = []
101 self.maxScanCode = []
103 # USBCode Assignment Cache
104 self.assignmentCache = []
106 def __repr__( self ):
107 return "{0}".format( self.macros )
109 def completeBaseLayout( self ):
110 # Copy base layout for later use when creating partial layers and add marker
111 self.baseLayout = copy.deepcopy( self.macros[ 0 ] )
112 self.layerLayoutMarkers.append( copy.deepcopy( self.baseLayout ) ) # Not used for default layer, just simplifies coding
114 def removeUnmarked( self ):
115 # Remove all of the unmarked mappings from the partial layer
116 for trigger in self.layerLayoutMarkers[ self.layer ].keys():
117 del self.macros[ self.layer ][ trigger ]
119 def addLayer( self ):
120 # Increment layer count, and append another macros dictionary
122 self.macros.append( copy.deepcopy( self.baseLayout ) )
124 # Add a layout marker for each layer
125 self.layerLayoutMarkers.append( copy.deepcopy( self.baseLayout ) )
127 # Use for ScanCode trigger macros
128 def appendScanCode( self, trigger, result ):
129 if not trigger in self.macros[ self.layer ]:
130 self.replaceScanCode( trigger, result )
132 self.macros[ self.layer ][ trigger ].append( result )
134 # Remove the given trigger/result pair
135 def removeScanCode( self, trigger, result ):
136 # Remove all instances of the given trigger/result pair
137 while result in self.macros[ self.layer ][ trigger ]:
138 self.macros[ self.layer ][ trigger ].remove( result )
140 # Replaces the given trigger with the given result
141 # If multiple results for a given trigger, clear, then add
142 def replaceScanCode( self, trigger, result ):
143 self.macros[ self.layer ][ trigger ] = [ result ]
145 # Mark layer scan code, so it won't be removed later
146 if not self.baseLayout is None:
147 del self.layerLayoutMarkers[ self.layer ][ trigger ]
149 # Return a list of ScanCode triggers with the given USB Code trigger
150 def lookupUSBCodes( self, usbCode ):
153 # Scan current layer for USB Codes
154 for macro in self.macros[ self.layer ].keys():
155 if usbCode in self.macros[ self.layer ][ macro ]:
156 scanCodeList.append( macro )
160 # Cache USBCode Assignment
161 def cacheAssignment( self, operator, scanCode, result ):
162 self.assignmentCache.append( [ operator, scanCode, result ] )
164 # Assign cached USBCode Assignments
165 def replayCachedAssignments( self ):
166 # Iterate over each item in the assignment cache
167 for item in self.assignmentCache:
168 # Check operator, and choose the specified assignment action
171 self.appendScanCode( item[1], item[2] )
174 elif item[0] == ":-":
175 self.removeScanCode( item[1], item[2] )
179 self.replaceScanCode( item[1], item[2] )
181 # Clear assignment cache
182 self.assignmentCache = []
184 # Generate/Correlate Layers
185 def generate( self ):
186 self.generateIndices()
187 self.sortIndexLists()
188 self.generateTriggerLists()
190 # Generates Index of Results and Triggers
191 def generateIndices( self ):
192 # Iterate over every trigger result, and add to the resultsIndex and triggersIndex
193 for layer in range( 0, len( self.macros ) ):
194 for trigger in self.macros[ layer ].keys():
195 # Each trigger has a list of results
196 for result in self.macros[ layer ][ trigger ]:
197 # Only add, with an index, if result hasn't been added yet
198 if not result in self.resultsIndex:
199 self.resultsIndex[ result ] = len( self.resultsIndex )
201 # Then add a trigger for each result, if trigger hasn't been added yet
202 triggerItem = tuple( [ trigger, self.resultsIndex[ result ] ] )
203 if not triggerItem in self.triggersIndex:
204 self.triggersIndex[ triggerItem ] = len( self.triggersIndex )
206 # Sort Index Lists using the indices rather than triggers/results
207 def sortIndexLists( self ):
208 self.resultsIndexSorted = [ None ] * len( self.resultsIndex )
209 # Iterate over the resultsIndex and sort by index
210 for result in self.resultsIndex.keys():
211 self.resultsIndexSorted[ self.resultsIndex[ result ] ] = result
213 self.triggersIndexSorted = [ None ] * len( self.triggersIndex )
214 # Iterate over the triggersIndex and sort by index
215 for trigger in self.triggersIndex.keys():
216 self.triggersIndexSorted[ self.triggersIndex[ trigger ] ] = trigger
218 # Generates Trigger Lists per layer using index lists
219 def generateTriggerLists( self ):
220 for layer in range( 0, len( self.macros ) ):
221 # Set max scancode to 0xFF (255)
222 # But keep track of the actual max scancode and reduce the list size
223 self.triggerList.append( [ [] ] * 0xFF )
224 self.maxScanCode.append( 0x00 )
226 # Iterate through trigger macros to locate necessary ScanCodes and corresponding triggerIndex
227 for trigger in self.macros[ layer ].keys():
228 for variant in range( 0, len( self.macros[ layer ][ trigger ] ) ):
229 # Identify result index
230 resultIndex = self.resultsIndex[ self.macros[ layer ][ trigger ][ variant ] ]
232 # Identify trigger index
233 triggerIndex = self.triggersIndex[ tuple( [ trigger, resultIndex ] ) ]
235 # Iterate over the trigger to locate the ScanCodes
236 for sequence in trigger:
237 for combo in sequence:
238 # Append triggerIndex for each found scanCode of the Trigger List
239 # Do not re-add if triggerIndex is already in the Trigger List
240 if not triggerIndex in self.triggerList[ layer ][ combo ]:
241 # Append is working strangely with list pre-initialization
242 # Doing a 0 check replacement instead -HaaTa
243 if len( self.triggerList[ layer ][ combo ] ) == 0:
244 self.triggerList[ layer ][ combo ] = [ triggerIndex ]
246 self.triggerList[ layer ][ combo ].append( triggerIndex )
248 # Look for max Scan Code
249 if combo > self.maxScanCode[ layer ]:
250 self.maxScanCode[ layer ] = combo
252 # Shrink triggerList to actual max size
253 self.triggerList[ layer ] = self.triggerList[ layer ][ : self.maxScanCode[ layer ] + 1 ]
255 # Determine overall maxScanCode
256 self.overallMaxScanCode = 0x00
257 for maxVal in self.maxScanCode:
258 if maxVal > self.overallMaxScanCode:
259 self.overallMaxScanCode = maxVal