def merge_lazy_operators(self, debug): ''' Lazy Set :: is not applied as a Set : until after the merge_in_log has been pruned Intended to be called during reduction. ''' # Build dictionary of single ScanCodes first result_code_lookup = {} for key, expr in self.data.items(): if expr[0].elems( )[0] == 1 and expr[0].triggers[0][0][0].type == 'ScanCode': result_code_lookup.setdefault(expr[0].result_str(), []).append(key) # Build list of lazy keys from log lazy_keys = {} for key, expr, enabled in reversed(self.merge_in_log): if key[0:2] == '::' or key[0:3] == 'i::': if key not in lazy_keys.keys(): # Debug info if debug: print("\033[1mLazy\033[0m", key, expr) # Determine the target key from the expression target_key = expr.trigger_str() lazy_keys[target_key] = expr # Check if we need to do a lazy replacement if target_key in result_code_lookup.keys(): expr_keys = result_code_lookup[target_key] for target_expr_key in expr_keys: # Calculate new key new_expr = self.data[target_expr_key][0] new_key = "{0}{1}".format( new_expr.operator, new_expr.unique_keys()[0][0]) # Determine action based on the new_expr.operator orig_expr = self.data[new_key][0] if debug: print( "\t\033[1;32mREPLACE\033[0m {0} -> {1}\n\t{2} => {3}" .format(target_expr_key, new_key, expr, new_expr)) # Do replacement self.data[new_key] = [ expression.MapExpression( orig_expr.triggers, orig_expr.operator, expr.results) ] self.data[new_key][ 0].connect_id = orig_expr.connect_id # Unset basemap on expression self.data[new_key][0].base_map = False
def reduction(self, debug=False): ''' Simplifies datastructure Used to replace all trigger HIDCode(USBCode)s with ScanCodes NOTE: Make sure to create a new MergeContext before calling this as you lose data and prior context ''' result_code_lookup = {} # Build dictionary of single ScanCodes first for key, expr in self.data.items(): if expr[0].elems( )[0] == 1 and expr[0].triggers[0][0][0].type == 'ScanCode': result_code_lookup[expr[0].result_str()] = expr # Using this dictionary, replace all the trigger USB codes # Iterate over a copy so we can modify the dictionary in place for key, expr in self.data.copy().items(): for sub_expr in expr: # 1) Single USB Codes trigger results will replace the original ScanCode result if sub_expr.elems( )[0] == 1 and sub_expr.triggers[0][0][0].type != 'ScanCode': # Debug info if debug: print("\033[1mSingle\033[0m", key, expr) # Lookup trigger to see if it exists trigger_str = sub_expr.trigger_str() if trigger_str in result_code_lookup.keys(): # Calculate new key new_expr = result_code_lookup[trigger_str][0] new_key = "{0}{1}".format(new_expr.operator, new_expr.unique_keys()[0][0]) # Determine action based on the new_expr.operator orig_expr = self.data[new_key][0] # Replace expression if sub_expr.operator in ['::', ':']: if debug: print( "\t\033[1;32mREPLACE\033[0m {0} -> {1}\n\t{2} => {3}" .format(key, new_key, sub_expr, new_expr)) # Do replacement self.data[new_key] = [ expression.MapExpression( orig_expr.triggers, orig_expr.operator, sub_expr.results) ] # Unset basemap on expression self.data[new_key][0].base_map = False # Add expression elif sub_expr.operator in [':+']: if debug: print( "\t\033[1;42mADD\033[0m {0} -> {1}\n\t{2} => {3}" .format(key, new_key, sub_expr, new_expr)) # Add expression self.data[new_key].append( expression.MapExpression( orig_expr.triggers, orig_expr.operator, sub_expr.results)) # Unset basemap on sub results for sub_expr in self.data[new_key]: sub_expr.base_map = False # Remove expression elif sub_expr.operator in [':-']: if debug: print( "\t\033[1;41mREMOVE\033[0m {0} -> {1}\n\t{2} => {3}" .format(key, new_key, sub_expr, new_expr)) # Remove old key if key in self.data.keys(): del self.data[key] # Otherwise drop HID expression else: if debug: print("\t\033[1;34mDROP\033[0m") del self.data[key] # 2) Complex triggers are processed to replace out any USB Codes with Scan Codes elif sub_expr.elems()[0] > 1: # Debug info if debug: print("\033[1;4mMulti\033[0m ", key, expr) # Lookup each trigger element and replace # If any trigger element doesn't exist, drop expression # Dive through sequence->combo->identifier (sequence of combos of ids) replace = False drop = False for seq_in, sequence in enumerate(sub_expr.triggers): for com_in, combo in enumerate(sequence): for ident_in, identifier in enumerate(combo): ident_str = "({0})".format(identifier) # Replace identifier if ident_str in result_code_lookup.keys(): match_expr = result_code_lookup[ident_str] sub_expr.triggers[seq_in][com_in][ ident_in] = match_expr[0].triggers[0][ 0][0] replace = True # Ignore ScanCodes elif identifier.type == 'ScanCode': pass # Drop everything else else: drop = True # Trigger Identifier was replaced if replace: if debug: print("\t\033[1;32mREPLACE\033[0m", expr) # Trigger Identifier failed (may still occur if there was a replacement) if drop: if debug: print("\t\033[1;34mDROP\033[0m") del self.data[key] # Show results of reduction if debug: print(self)
def reduction(self, debug=False): ''' Simplifies datastructure Used to replace all trigger HIDCode(USBCode)s with ScanCodes NOTE: Make sure to create a new MergeContext before calling this as you lose data and prior context ''' result_code_lookup = {} # Prune merge_in_log merge_in_pruned = self.merge_in_log_prune(debug) # Build dictionary of single ScanCodes first for key, expr in self.data.items(): if expr[0].elems( )[0] == 1 and expr[0].triggers[0][0][0].type == 'ScanCode': result_code_lookup[expr[0].result_str()] = expr # Skip if dict is empty if len(self.data.keys()) == 0: return # Instead of using the .data dictionary, use the merge_in_log which maintains the expression application order # Using this list, replace all the trigger USB codes for key, log_expr, active in self.merge_in_log: # Skip if not active if not active: continue # Lookup currently merged expression if key not in self.data.keys(): continue expr = self.data[key] for sub_expr in expr: # 1) Single USB Codes trigger results will replace the original ScanCode result if sub_expr.elems( )[0] == 1 and sub_expr.triggers[0][0][0].type in [ 'USBCode', 'SysCode', 'ConsCode' ]: # Debug info if debug: print("\033[1mSingle\033[0m", key, expr) # Lookup trigger to see if it exists trigger_str = sub_expr.trigger_str() if trigger_str in result_code_lookup.keys(): # Calculate new key new_expr = result_code_lookup[trigger_str][0] new_key = "{0}{1}".format(new_expr.operator, new_expr.unique_keys()[0][0]) # Determine action based on the new_expr.operator orig_expr = self.data[new_key][0] # Replace expression if sub_expr.operator in [':']: if debug: print( "\t\033[1;32mREPLACE\033[0m {0} -> {1}\n\t{2} => {3}" .format(key, new_key, sub_expr, new_expr)) # Do replacement self.data[new_key] = [ expression.MapExpression( orig_expr.triggers, orig_expr.operator, sub_expr.results) ] # Transfer connect_id self.data[new_key][ 0].connect_id = orig_expr.connect_id # Unset basemap on expression self.data[new_key][0].base_map = False # Add expression elif sub_expr.operator in [':+']: if debug: print( "\t\033[1;42mADD\033[0m {0} -> {1}\n\t{2} => {3}" .format(key, new_key, sub_expr, new_expr)) # Add expression self.data[new_key].append( expression.MapExpression( orig_expr.triggers, orig_expr.operator, sub_expr.results)) # Unset basemap on sub results for sub_expr in self.data[new_key]: sub_expr.base_map = False # Remove expression elif sub_expr.operator in [':-']: if debug: print( "\t\033[1;41mREMOVE\033[0m {0} -> {1}\n\t{2} => {3}" .format(key, new_key, sub_expr, new_expr)) # Remove old key if key in self.data.keys(): del self.data[key] # Otherwise drop HID expression else: if debug: print("\t\033[1;34mDROP\033[0m") if key in self.data.keys(): del self.data[key] # 2) Complex triggers are processed to replace out any USB Codes with Scan Codes elif sub_expr.elems()[0] > 1: # Debug info if debug: print("\033[1;4mMulti\033[0m ", key, expr) # Lookup each trigger element and replace # If any trigger element doesn't exist, drop expression # Dive through sequence->combo->identifier (sequence of combos of ids) replace = False drop = False for seq_in, sequence in enumerate(sub_expr.triggers): for com_in, combo in enumerate(sequence): for ident_in, identifier in enumerate(combo): ident_str = "({0})".format(identifier) # Replace identifier if ident_str in result_code_lookup.keys(): match_expr = result_code_lookup[ident_str] sub_expr.triggers[seq_in][com_in][ ident_in] = match_expr[0].triggers[0][ 0][0] replace = True # Ignore non-USB triggers elif identifier.type in [ 'IndCode', 'GenericTrigger', 'Layer', 'LayerLock', 'LayerShift', 'LayerLatch', 'ScanCode' ]: pass # Drop everything else else: drop = True # Trigger Identifier was replaced if replace: if debug: print("\t\033[1;32mREPLACE\033[0m", expr) # Trigger Identifier failed (may still occur if there was a replacement) if drop: if debug: print("\t\033[1;34mDROP\033[0m") del self.data[key] # Finally we can merge in the Lazy :: Set operators self.merge_lazy_operators(debug) # Show results of reduction if debug: print(self)