Archived
1
0

Adding TriggerMacro processing.

- Not tested, but basic features should be complete
This commit is contained in:
Jacob Alexander 2014-08-05 11:33:55 -07:00
parent 7ef7973f5a
commit 34198635b9
2 changed files with 318 additions and 82 deletions

View File

@ -42,6 +42,7 @@
// 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;
@ -79,11 +80,21 @@ typedef struct ResultGuide {
// 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
@ -199,7 +210,6 @@ ResultMacro ResultMacroList[] = {
// * 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 };
@ -359,10 +369,10 @@ Define_TL( default, 0x6F ) = { 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 };

View File

@ -52,6 +52,27 @@ void cliFunc_macroStep ( char* args );
// ----- 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
@ -198,56 +219,251 @@ inline void Macro_ledState( uint8_t ledCode, uint8_t state )
}
// 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;
// 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++ )
// Iterate through result macro pending list, making sure this macro hasn't been added yet
for ( unsigned int macro = 0; macro < macroResultMacroPendingListSize; macro++ )
{
// Compare with keys in combo
for ( unsigned int comboKey = 0; comboKey < comboLength; comboKey++ )
// If duplicate found, do nothing
if ( macroResultMacroPendingList[ macro ] == resultMacroIndex )
return;
}
// No duplicates found, add to pending list
macroResultMacroPendingList[ macroResultMacroPendingListSize++ ] = resultMacroIndex;
}
// 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 )
{
// 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
// Sequence Case
if ( comboLength == 1 )
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;
}
// Wrong key, pressed, fail
case 0x01:
return TriggerMacroVote_Fail;
// If key does not match or more than 1 key pressed, reset the TriggerMacro combo position
triggerMacro->pos = 0;
return;
}
// Combo Case
else
{
// TODO
// Wrong key, held or released, do not pass (no effect)
case 0x02:
case 0x03:
return TriggerMacroVote_DoNothing;
}
}
// Correct key
else
{
switch ( key->state )
{
// 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;
}
}
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 = &macroTriggerListBuffer[ 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*)(&macro->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;
}
// 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()
@ -257,56 +473,61 @@ 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++ )
// Update pending trigger list, before processing TriggerMacros
Macro_updateTriggerMacroPendingList();
// Tail pointer for macroTriggerMacroPendingList
// Macros must be explicitly re-added
unsigned int macroTriggerMacroPendingListTail = 0;
// Iterate through the pending TriggerMacros, processing each of them
for ( unsigned int macro = 0; macro < macroTriggerMacroPendingListSize; macro++ )
{
// 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;
// The first element is the length of the trigger list
unsigned int triggerListSize = triggerList[0];
// Loop through the trigger list
for ( unsigned int trigger = 0; trigger < triggerListSize; trigger++ )
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 );
// Get triggered state of scan code, second item of macroTriggerListBuffer pairs
uint8_t state = macroTriggerListBuffer[ index ].state;
// Otherwise, just re-add
default:
macroTriggerMacroPendingList[ macroTriggerMacroPendingListTail++ ] = macroTriggerMacroPendingList[ macro ];
break;
// Evaluate Macro
Macro_evalTriggerMacro( triggerMacro );
// Trigger Result Macro and Remove (purposely falling through)
case TriggerMacroEval_DoResultAndRemove:
// Append ResultMacro to PendingList
Macro_appendResultMacroToPendingList( TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ].result );
// 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];
@ -327,18 +548,7 @@ inline void Macro_process()
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 );
}
}
*/
@ -371,6 +581,22 @@ inline void Macro_setup()
// 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;
}
}