// ResultMacro.state -> <last key state>
// ResultMacro.stateType -> <last key state type>
+// ResultMacro struct, one is created per ResultMacro, no duplicates
typedef struct ResultMacro {
uint8_t *guide;
unsigned int pos;
// TriggerMacro.guide -> [<combo length>|<key1 type>|<key1 state>|<key1>...<keyn type>|<keyn state>|<keyn>|<combo length>...|0]
// TriggerMacro.result -> <index to result macro>
// TriggerMacro.pos -> <current combo position>
+// TriggerMacro.state -> <status of the macro pos>
+// TriggerMacro states
+typedef enum TriggerMacroState {
+ TriggerMacro_Press, // Combo in sequence is passing
+ TriggerMacro_Release, // Move to next combo in sequence (or finish if at end of sequence)
+ TriggerMacro_Waiting, // Awaiting user input
+} TriggerMacroState;
+
+// TriggerMacro struct, one is created per TriggerMacro, no duplicates
typedef struct TriggerMacro {
uint8_t *guide;
unsigned int result;
unsigned int pos;
+ TriggerMacroState state;
} TriggerMacro;
// Guide, key element
// * result - Result Macro index number which is triggered by this Trigger Macro
#define Guide_TM( index ) static uint8_t tm##index##_guide[]
#define Define_TM( index, result ) { tm##index##_guide, result, 0 }
-#define tm( index ) (unsigned int)&TriggerMacroList[ index ]
Guide_TM( 0 ) = { 1, 0x10, 0x01, 0x73, 0 };
Guide_TM( 1 ) = { 1, 0x0F, 0x01, 0x73, 1, 0x00, 0x01, 0x75, 0 };
Define_TL( default, 0x70 ) = { 0 };
Define_TL( default, 0x71 ) = { 0 };
Define_TL( default, 0x72 ) = { 0 };
-Define_TL( default, 0x73 ) = { 3, tm(0), tm(1), tm(2) };
-Define_TL( default, 0x74 ) = { 1, tm(2) };
-Define_TL( default, 0x75 ) = { 1, tm(1) };
-Define_TL( default, 0x76 ) = { 1, tm(3) };
+Define_TL( default, 0x73 ) = { 3, 0, 1, 2 };
+Define_TL( default, 0x74 ) = { 1, 2 };
+Define_TL( default, 0x75 ) = { 1, 1 };
+Define_TL( default, 0x76 ) = { 1, 3 };
Define_TL( default, 0x77 ) = { 0 };
Define_TL( default, 0x78 ) = { 0 };
Define_TL( default, 0x79 ) = { 0 };
+// ----- Enums -----
+
+// Bit positions are important, passes (correct key) always trump incorrect key votes
+typedef enum TriggerMacroVote {
+ TriggerMacroVote_Release = 0x8, // Correct key
+ TriggerMacroVote_PassRelease = 0xC, // Correct key (both pass and release)
+ TriggerMacroVote_Pass = 0x4, // Correct key
+ TriggerMacroVote_DoNothing = 0x2, // Incorrect key
+ TriggerMacroVote_Fail = 0x1, // Incorrect key
+ TriggerMacroVote_Invalid = 0x0, // Invalid state
+} TriggerMacroVote;
+
+typedef enum TriggerMacroEval {
+ TriggerMacroEval_DoNothing,
+ TriggerMacroEval_DoResult,
+ TriggerMacroEval_DoResultAndRemove,
+ TriggerMacroEval_Remove,
+} TriggerMacroEval;
+
+
+
// ----- Variables -----
// Macro Module command dictionary
}
-// Evaluate/Update TriggerMacro
-void Macro_evalTriggerMacro( TriggerMacro *triggerMacro )
+// Append result macro to pending list, checking for duplicates
+// Do nothing if duplicate
+inline void Macro_appendResultMacroToPendingList( unsigned int resultMacroIndex )
{
- // Which combo in the sequence is being evaluated
- unsigned int comboPos = triggerMacro->pos;
+ // Iterate through result macro pending list, making sure this macro hasn't been added yet
+ for ( unsigned int macro = 0; macro < macroResultMacroPendingListSize; macro++ )
+ {
+ // If duplicate found, do nothing
+ if ( macroResultMacroPendingList[ macro ] == resultMacroIndex )
+ return;
+ }
+
+ // No duplicates found, add to pending list
+ macroResultMacroPendingList[ macroResultMacroPendingListSize++ ] = resultMacroIndex;
+}
- // If combo length is more than 1, cancel trigger macro if an incorrect key is found
- uint8_t comboLength = triggerMacro->guide[ comboPos ];
- // Iterate over list of keys currently pressed
- for ( uint8_t keyPressed = 0; keyPressed < macroTriggerListBufferSize; keyPressed++ )
+// Determine if long ResultMacro (more than 1 seqence element)
+inline uint8_t Macro_isLongResultMacro( ResultMacro *macro )
+{
+ // Check the second sequence combo length
+ // If non-zero return 1 (long sequence)
+ // 0 otherwise (short sequence)
+ return macro->guide[ macro->guide[0] * ResultGuideSize( ((ResultGuide*)macro->guide) ) ] > 0 ? 1 : 0;
+}
+
+
+// Votes on the given key vs. guide
+inline TriggerMacroVote Macro_evalTriggerMacroVote( TriggerGuide *key, TriggerGuide *guide )
+{
+ // Depending on key type
+ switch ( guide->type )
{
- // Compare with keys in combo
- for ( unsigned int comboKey = 0; comboKey < comboLength; comboKey++ )
+ // Normal State Type
+ case 0x00:
+ // Depending on the state of the buffered key, make voting decision
+ // Incorrect key
+ if ( guide->scanCode != key->scanCode )
{
- // Lookup key in combo
- uint8_t guideKey = triggerMacro->guide[ comboPos + comboKey + 2 ]; // TODO Only Press/Hold/Release atm
+ switch ( key->state )
+ {
+ // Wrong key, pressed, fail
+ case 0x01:
+ return TriggerMacroVote_Fail;
+
+ // Wrong key, held or released, do not pass (no effect)
+ case 0x02:
+ case 0x03:
+ return TriggerMacroVote_DoNothing;
+ }
+ }
- // Sequence Case
- if ( comboLength == 1 )
+ // Correct key
+ else
+ {
+ switch ( key->state )
{
- // If key matches and only 1 key pressed, increment the TriggerMacro combo position
- if ( guideKey == macroTriggerListBuffer[ keyPressed ].scanCode && macroTriggerListBufferSize == 1 )
- {
- triggerMacro->pos += comboLength * 2 + 1;
- // TODO check if TriggerMacro is finished, register ResultMacro
- return;
- }
-
- // If key does not match or more than 1 key pressed, reset the TriggerMacro combo position
- triggerMacro->pos = 0;
- return;
+ // Correct key, pressed, possible passing
+ case 0x01:
+ return TriggerMacroVote_Pass;
+
+ // Correct key, held, possible passing or release
+ case 0x02:
+ return TriggerMacroVote_PassRelease;
+
+ // Correct key, released, possible release
+ case 0x03:
+ return TriggerMacroVote_Release;
}
- // Combo Case
- else
+ }
+
+ break;
+
+ // LED State Type
+ case 0x01:
+ erro_print("LED State Type - Not implemented...");
+ break;
+
+ // Analog State Type
+ case 0x02:
+ erro_print("Analog State Type - Not implemented...");
+ break;
+
+ // Invalid State Type
+ default:
+ erro_print("Invalid State Type. This is a bug.");
+ break;
+ }
+
+ // XXX Shouldn't reach here
+ return TriggerMacroVote_Invalid;
+}
+
+
+// Evaluate/Update TriggerMacro
+inline TriggerMacroEval Macro_evalTriggerMacro( unsigned int triggerMacroIndex )
+{
+ // 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 )
{
- // TODO
+ 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;
}
// Evaluate/Update ResultMacro
-void Macro_evalResultMacro( ResultMacro *resultMacro )
+void Macro_evalResultMacro( unsigned int resultMacroIndex )
{
// TODO
}
+// Update pending trigger list
+void Macro_updateTriggerMacroPendingList()
+{
+ // Iterate over the macroTriggerListBuffer to add any new Trigger Macros to the pending list
+ for ( uint8_t key = 0; key < macroTriggerListBufferSize; key++ )
+ {
+ // Lookup Trigger List
+ unsigned int *triggerList = Macro_layerLookup( macroTriggerListBuffer[ key ].scanCode );
+
+ // Number of Triggers in list
+ unsigned int triggerListSize = triggerList[0];
+
+ // Iterate over triggerList to see if any TriggerMacros need to be added
+ // First item is the number of items in the TriggerList
+ for ( unsigned int macro = 1; macro < triggerListSize + 1; macro++ )
+ {
+ // Lookup trigger macro index
+ unsigned int triggerMacroIndex = triggerList[ macro ];
+
+ // Iterate over macroTriggerMacroPendingList to see if any macro in the scancode's
+ // triggerList needs to be added
+ unsigned int pending = 0;
+ for ( ; pending < macroTriggerMacroPendingListSize; pending++ )
+ {
+ // Stop scanning if the trigger macro index is found in the pending list
+ if ( macroTriggerMacroPendingList[ pending ] == triggerMacroIndex )
+ break;
+ }
+
+ // If the triggerMacroIndex (macro) was not found in the macroTriggerMacroPendingList
+ // Add it to the list
+ if ( pending == macroTriggerMacroPendingListSize )
+ {
+ macroTriggerMacroPendingList[ macroTriggerMacroPendingListSize++ ] = triggerMacroIndex;
+ }
+ }
+ }
+}
+
+
// Macro Procesing Loop
// Called once per USB buffer send
inline void Macro_process()
return;
// If the pause flag is set, only process if the step counter is non-zero
- if ( macroPauseMode && macroStepCounter == 0 )
- {
- return;
- }
- // Proceed, decrementing the step counter
- else
+ if ( macroPauseMode )
{
+ if ( macroStepCounter == 0 )
+ return;
+
+ // Proceed, decrementing the step counter
macroStepCounter--;
}
- // Loop through macro trigger buffer
- for ( uint8_t index = 0; index < macroTriggerListBufferSize; index++ )
- {
- // Get scanCode, first item of macroTriggerListBuffer pairs
- uint8_t scanCode = macroTriggerListBuffer[ index ].scanCode;
-
- // Lookup trigger list for this key
- unsigned int *triggerList = Macro_layerLookup( scanCode );
-
- // Skip, if no trigger list
- if ( triggerList == 0 )
- continue;
+ // Update pending trigger list, before processing TriggerMacros
+ Macro_updateTriggerMacroPendingList();
- // The first element is the length of the trigger list
- unsigned int triggerListSize = triggerList[0];
+ // Tail pointer for macroTriggerMacroPendingList
+ // Macros must be explicitly re-added
+ unsigned int macroTriggerMacroPendingListTail = 0;
- // Loop through the trigger list
- for ( unsigned int trigger = 0; trigger < triggerListSize; trigger++ )
+ // Iterate through the pending TriggerMacros, processing each of them
+ for ( unsigned int macro = 0; macro < macroTriggerMacroPendingListSize; macro++ )
+ {
+ switch ( Macro_evalTriggerMacro( macroTriggerMacroPendingList[ macro ] ) )
{
- // Lookup TriggerMacro
- TriggerMacro *triggerMacro = (TriggerMacro*)triggerList[ trigger + 1 ];
+ // Trigger Result Macro (purposely falling through)
+ case TriggerMacroEval_DoResult:
+ // Append ResultMacro to PendingList
+ Macro_appendResultMacroToPendingList( TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ].result );
+
+ // Otherwise, just re-add
+ default:
+ macroTriggerMacroPendingList[ macroTriggerMacroPendingListTail++ ] = macroTriggerMacroPendingList[ macro ];
+ break;
- // Get triggered state of scan code, second item of macroTriggerListBuffer pairs
- uint8_t state = macroTriggerListBuffer[ index ].state;
+ // Trigger Result Macro and Remove (purposely falling through)
+ case TriggerMacroEval_DoResultAndRemove:
+ // Append ResultMacro to PendingList
+ Macro_appendResultMacroToPendingList( TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ].result );
- // Evaluate Macro
- Macro_evalTriggerMacro( triggerMacro );
+ // Remove Macro from Pending List, nothing to do, removing by default
+ case TriggerMacroEval_Remove:
+ break;
}
}
+ // Update the macroResultMacroPendingListSize with the tail pointer
+ macroTriggerMacroPendingListSize = macroTriggerMacroPendingListTail;
-
-
+ // Iterate through the pending ResultMacros, processing each of them
+ for ( unsigned int macro = 0; macro < macroResultMacroPendingListSize; macro++ )
+ {
+ Macro_evalResultMacro( macroResultMacroPendingList[ macro ] );
+ }
/* TODO
// Loop through input buffer
for ( uint8_t index = 0; index < KeyIndex_BufferUsed && !macroDebugMode; index++ )
{
- //print(" KEYS: ");
- //printInt8( KeyIndex_BufferUsed );
// Get the keycode from the buffer
uint8_t key = KeyIndex_Buffer[index];
break;
}
- // Allow ignoring keys with 0's
- if ( key != 0 )
- {
USBKeys_Array[USBKeys_Sent++] = key;
- }
- else
- {
- // Key was not mapped
- erro_msg( "Key not mapped... - " );
- printHex( key );
- errorLED( 1 );
- }
}
*/
// Make sure macro trigger buffer is empty
macroTriggerListBufferSize = 0;
+
+ // Initialize TriggerMacro states
+ for ( unsigned int macro = 0; macro < TriggerMacroNum; macro++ )
+ {
+ TriggerMacroList[ macro ].result = 0;
+ TriggerMacroList[ macro ].pos = 0;
+ TriggerMacroList[ macro ].state = TriggerMacro_Waiting;
+ }
+
+ // Initialize ResultMacro states
+ for ( unsigned int macro = 0; macro < ResultMacroNum; macro++ )
+ {
+ ResultMacroList[ macro ].pos = 0;
+ ResultMacroList[ macro ].state = 0;
+ ResultMacroList[ macro ].stateType = 0;
+ }
}