+ // Lookup TriggerMacro
+ TriggerMacro *macro = &TriggerMacroList[ triggerMacroIndex ];
+
+ // Check if macro has finished and should be incremented sequence elements
+ if ( macro->state == TriggerMacro_Release )
+ {
+ macro->state = TriggerMacro_Waiting;
+ macro->pos = macro->pos + macro->guide[ macro->pos ] * TriggerGuideSize;
+ }
+
+ // Current Macro position
+ unsigned int pos = macro->pos;
+
+ // Length of the combo being processed
+ uint8_t comboLength = macro->guide[ pos ];
+
+ // If no combo items are left, remove the TriggerMacro from the pending list
+ if ( comboLength == 0 )
+ {
+ return TriggerMacroEval_Remove;
+ }
+
+ // Iterate through the key buffer, comparing to each key in the combo
+ // If any of the pressed keys do not match, fail the macro
+ //
+ // The macro is waiting for input when in the TriggerMacro_Waiting state
+ // Once all keys have been pressed/held (only those keys), entered TriggerMacro_Press state (passing)
+ // Transition to the next combo (if it exists) when a single key is released (TriggerMacro_Release state)
+ // On scan after position increment, change to TriggerMacro_Waiting state
+ // TODO Add support for system LED states (NumLock, CapsLock, etc.)
+ // TODO Add support for analog key states
+ // TODO Add support for 0x00 Key state (not pressing a key, not all that useful in general)
+ // TODO Add support for Press/Hold/Release differentiation when evaluating (not sure if useful)
+ TriggerMacroVote overallVote = TriggerMacroVote_Invalid;
+ for ( uint8_t key = 0; key < macroTriggerListBufferSize; key++ )
+ {
+ // Lookup key information
+ TriggerGuide *keyInfo = ¯oTriggerListBuffer[ key ];
+
+ // Iterate through the items in the combo, voting the on the key state
+ TriggerMacroVote vote = TriggerMacroVote_Invalid;
+ for ( uint8_t comboItem = pos + 1; comboItem < pos + comboLength + 1; comboItem += TriggerGuideSize )
+ {
+ // Assign TriggerGuide element (key type, state and scancode)
+ TriggerGuide *guide = (TriggerGuide*)(¯o->guide[ comboItem ]);
+
+ // If vote is a pass (>= 0x08, no more keys in the combo need to be looked at)
+ // Also mask all of the non-passing votes
+ vote |= Macro_evalTriggerMacroVote( keyInfo, guide );
+ if ( vote >= TriggerMacroVote_Pass )
+ {
+ vote &= TriggerMacroVote_Release | TriggerMacroVote_PassRelease | TriggerMacroVote_Pass;
+ break;
+ }
+ }
+
+ // After voting, append to overall vote
+ overallVote |= vote;
+ }
+
+ // Decide new state of macro after voting
+ // Fail macro, remove from pending list
+ if ( overallVote & TriggerMacroVote_Fail )
+ {
+ return TriggerMacroEval_Remove;
+ }
+ // Do nothing, incorrect key is being held or released
+ else if ( overallVote & TriggerMacroVote_DoNothing )
+ {
+ // Just doing nothing :)
+ }
+ // If passing and in Waiting state, set macro state to Press
+ else if ( overallVote & TriggerMacroVote_Pass && macro->state == TriggerMacro_Waiting )
+ {
+ macro->state = TriggerMacro_Press;
+
+ // If in press state, and this is the final combo, send request for ResultMacro
+ // Check to see if the result macro only has a single element
+ // If this result macro has more than 1 key, only send once
+ // TODO Add option to have macro repeat rate
+ if ( macro->guide[ pos + comboLength ] == 0 )
+ {
+ // Long Macro, only send once (more than 1 sequence item)
+ // Short Macro (only 1 sequence item)
+ return Macro_isLongResultMacro( &ResultMacroList[ macro->result ] )
+ ? TriggerMacroEval_DoResult
+ : TriggerMacroEval_DoResultAndRemove;
+ }
+
+ }
+ // If ready for transition and in Press state, set to Waiting and increment combo position
+ // Position is incremented (and possibly remove the macro from the pending list) on the next iteration
+ else if ( overallVote & TriggerMacroVote_Release && macro->state == TriggerMacro_Press )
+ {
+ macro->state = TriggerMacro_Release;
+ }
+
+ return TriggerMacroEval_DoNothing;