- Requires a recent KLL - Functionality wise, nothing has changedrevert-113-ghost_fix
@@ -1,10 +1,10 @@ | |||
Name = PartialMapCapabilities; | |||
Version = 0.2; | |||
Author = "HaaTa (Jacob Alexander) 2014-2015"; | |||
KLL = 0.3b; | |||
Version = 0.3; | |||
Author = "HaaTa (Jacob Alexander) 2014-2016"; | |||
KLL = 0.3d; | |||
# Modified Date | |||
Date = 2015-09-24; | |||
Date = 2016-04-08; | |||
# Capabilties available to the PartialMap module | |||
@@ -22,3 +22,6 @@ layerRotate => Macro_layerRotate_capability( previous : 1 ); | |||
stateWordSize => StateWordSize_define; | |||
stateWordSize = 8; # Default for now, increase to 16 or 32 for higher limits | |||
indexWordSize => IndexWordSize_define; | |||
indexWordSize = 16; # Default for now, increase to 32 for higher limits (8 for less resource usage) | |||
@@ -1,4 +1,4 @@ | |||
/* Copyright (C) 2014-2015 by Jacob Alexander | |||
/* Copyright (C) 2014-2016 by Jacob Alexander | |||
* | |||
* This file is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -48,6 +48,20 @@ typedef uint8_t var_uint_t; | |||
#error "Invalid StateWordSize, possible values: 32, 16 and 8." | |||
#endif | |||
// - NOTE - | |||
// It is possible to change the maximum number of trigger/result index sizes | |||
// This will affect SRAM and flash usage, so it can be used to fit code on smaller uCs. | |||
// Also allows for over 4 billion triggers and results (triggers and results have separate indices) | |||
#if IndexWordSize_define == 32 | |||
typedef uint32_t index_uint_t; | |||
#elif IndexWordSize_define == 16 | |||
typedef uint16_t index_uint_t; | |||
#elif IndexWordSize_define == 8 | |||
typedef uint8_t index_uint_t; | |||
#else | |||
#error "Invalid IndexWordSize, possible values: 32, 16 and 8." | |||
#endif | |||
// - NOTE - | |||
// Native pointer length | |||
// This needs to be defined per microcontroller | |||
@@ -159,7 +173,8 @@ typedef struct Capability { | |||
} Capability; | |||
// Total Number of Capabilities | |||
#define CapabilitiesNum sizeof( CapabilitiesList ) / sizeof( Capability ) | |||
// (generated by KLL) | |||
#define CapabilitiesNum CapabilitiesNum_KLL | |||
// -- Result Macros | |||
@@ -179,7 +194,8 @@ typedef struct Capability { | |||
// Total number of result macros (rm's) | |||
// Used to create pending rm's table | |||
#define ResultMacroNum sizeof( ResultMacroList ) / sizeof( ResultMacro ) | |||
// (generated by KLL) | |||
#define ResultMacroNum ResultMacroNum_KLL | |||
// -- Trigger Macros | |||
@@ -199,7 +215,8 @@ typedef struct Capability { | |||
// Total number of trigger macros (tm's) | |||
// Used to create pending tm's table | |||
#define TriggerMacroNum sizeof( TriggerMacroList ) / sizeof( TriggerMacro ) | |||
// (generated by KLL) | |||
#define TriggerMacroNum TriggerMacroNum_KLL | |||
@@ -248,6 +265,6 @@ typedef struct Layer { | |||
// * first - First scan code used (most keyboards start at 0, some start higher e.g. 0x40) | |||
#define Layer_IN( map, name, first ) { map, name, first, sizeof( map ) / sizeof( nat_ptr_t ) - 1 + first } | |||
// Total number of layers | |||
#define LayerNum sizeof( LayerIndex ) / sizeof( Layer ) | |||
// Total number of layers (generated by KLL) | |||
#define LayerNum LayerNum_KLL | |||
@@ -1,4 +1,4 @@ | |||
/* Copyright (C) 2014-2015 by Jacob Alexander | |||
/* Copyright (C) 2014-2016 by Jacob Alexander | |||
* | |||
* This file is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
@@ -35,6 +35,8 @@ | |||
#endif | |||
// Local Includes | |||
#include "trigger.h" | |||
#include "result.h" | |||
#include "macro.h" | |||
@@ -57,33 +59,6 @@ void cliFunc_macroStep ( char* args ); | |||
// ----- Enums ----- | |||
// Bit positions are important, passes (correct key) always trump incorrect key votes | |||
typedef enum TriggerMacroVote { | |||
TriggerMacroVote_Release = 0x10, // Correct key | |||
TriggerMacroVote_PassRelease = 0x18, // Correct key (both pass and release) | |||
TriggerMacroVote_Pass = 0x8, // Correct key | |||
TriggerMacroVote_DoNothingRelease = 0x4, // Incorrect 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; | |||
typedef enum ResultMacroEval { | |||
ResultMacroEval_DoNothing, | |||
ResultMacroEval_Remove, | |||
} ResultMacroEval; | |||
// ----- Variables ----- | |||
// Macro Module command dictionary | |||
@@ -138,23 +113,16 @@ TriggerGuide macroTriggerListBuffer[ MaxScanCode ]; | |||
var_uint_t macroTriggerListBufferSize = 0; | |||
var_uint_t macroTriggerListLayerCache[ MaxScanCode ]; | |||
// Pending Trigger Macro Index List | |||
// * Any trigger macros that need processing from a previous macro processing loop | |||
// TODO, figure out a good way to scale this array size without wasting too much memory, but not rejecting macros | |||
// Possibly could be calculated by the KLL compiler | |||
// XXX It may be possible to calculate the worst case using the KLL compiler | |||
uint16_t macroTriggerMacroPendingList[ TriggerMacroNum ] = { 0 }; | |||
uint16_t macroTriggerMacroPendingListSize = 0; | |||
// Layer Index Stack | |||
// * When modifying layer state and the state is non-0x0, the stack must be adjusted | |||
uint16_t macroLayerIndexStack[ LayerNum + 1 ] = { 0 }; | |||
uint16_t macroLayerIndexStackSize = 0; | |||
index_uint_t macroLayerIndexStack[ LayerNum + 1 ] = { 0 }; | |||
index_uint_t macroLayerIndexStackSize = 0; | |||
// Pending Result Macro Index List | |||
// * Any result macro that needs processing from a previous macro processing loop | |||
uint16_t macroResultMacroPendingList[ ResultMacroNum ] = { 0 }; | |||
uint16_t macroResultMacroPendingListSize = 0; | |||
// TODO REMOVE when dependency no longer exists | |||
extern index_uint_t macroResultMacroPendingList[]; | |||
extern index_uint_t macroResultMacroPendingListSize; | |||
extern index_uint_t macroTriggerMacroPendingList[]; | |||
extern index_uint_t macroTriggerMacroPendingListSize; | |||
// Interconnect ScanCode Cache | |||
#if defined(ConnectEnabled_define) | |||
@@ -229,7 +197,7 @@ void Macro_layerState( uint8_t state, uint8_t stateType, uint16_t layer, uint8_t | |||
dbug_msg("Layer "); | |||
// Iterate over each of the layers displaying the state as a hex value | |||
for ( uint16_t index = 0; index < LayerNum; index++ ) | |||
for ( index_uint_t index = 0; index < LayerNum; index++ ) | |||
{ | |||
printHex_op( LayerState[ index ], 0 ); | |||
} | |||
@@ -238,7 +206,7 @@ void Macro_layerState( uint8_t state, uint8_t stateType, uint16_t layer, uint8_t | |||
print(" 0"); | |||
// Iterate over the layer stack starting from the bottom of the stack | |||
for ( uint16_t index = macroLayerIndexStackSize; index > 0; index-- ) | |||
for ( index_uint_t index = macroLayerIndexStackSize; index > 0; index-- ) | |||
{ | |||
print(":"); | |||
printHex_op( macroLayerIndexStack[ index - 1 ], 0 ); | |||
@@ -672,7 +640,7 @@ inline void Macro_ledState( uint8_t ledCode, uint8_t state ) | |||
// Append result macro to pending list, checking for duplicates | |||
// Do nothing if duplicate | |||
inline void Macro_appendResultMacroToPendingList( const TriggerMacro *triggerMacro ) | |||
void Macro_appendResultMacroToPendingList( const TriggerMacro *triggerMacro ) | |||
{ | |||
// Lookup result macro index | |||
var_uint_t resultMacroIndex = triggerMacro->result; | |||
@@ -713,412 +681,6 @@ inline void Macro_appendResultMacroToPendingList( const TriggerMacro *triggerMac | |||
} | |||
// Determine if long ResultMacro (more than 1 seqence element) | |||
inline uint8_t Macro_isLongResultMacro( const ResultMacro *macro ) | |||
{ | |||
// Check the second sequence combo length | |||
// If non-zero return non-zero (long sequence) | |||
// 0 otherwise (short sequence) | |||
var_uint_t position = 1; | |||
for ( var_uint_t result = 0; result < macro->guide[0]; result++ ) | |||
position += ResultGuideSize( (ResultGuide*)¯o->guide[ position ] ); | |||
return macro->guide[ position ]; | |||
} | |||
// Determine if long TriggerMacro (more than 1 sequence element) | |||
inline uint8_t Macro_isLongTriggerMacro( const TriggerMacro *macro ) | |||
{ | |||
// Check the second sequence combo length | |||
// If non-zero return non-zero (long sequence) | |||
// 0 otherwise (short sequence) | |||
return macro->guide[ macro->guide[0] * TriggerGuideSize + 1 ]; | |||
} | |||
// Votes on the given key vs. guide, short macros | |||
inline TriggerMacroVote Macro_evalShortTriggerMacroVote( TriggerGuide *key, TriggerGuide *guide ) | |||
{ | |||
// Depending on key type | |||
switch ( guide->type ) | |||
{ | |||
// Normal State Type | |||
case 0x00: | |||
// For short TriggerMacros completely ignore incorrect keys | |||
if ( guide->scanCode == key->scanCode ) | |||
{ | |||
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; | |||
} | |||
} | |||
return TriggerMacroVote_DoNothing; | |||
// 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; | |||
} | |||
// Votes on the given key vs. guide, long macros | |||
// A long macro is defined as a guide with more than 1 combo | |||
inline TriggerMacroVote Macro_evalLongTriggerMacroVote( 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 ) | |||
{ | |||
switch ( key->state ) | |||
{ | |||
// Wrong key, pressed, fail | |||
case 0x01: | |||
return TriggerMacroVote_Fail; | |||
// Wrong key, held, do not pass (no effect) | |||
case 0x02: | |||
return TriggerMacroVote_DoNothing; | |||
// Wrong key released, fail out if pos == 0 | |||
case 0x03: | |||
return TriggerMacroVote_DoNothing | TriggerMacroVote_DoNothingRelease; | |||
} | |||
} | |||
// 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 | |||
TriggerMacroEval Macro_evalTriggerMacro( var_uint_t triggerMacroIndex ) | |||
{ | |||
// Lookup TriggerMacro | |||
const TriggerMacro *macro = &TriggerMacroList[ triggerMacroIndex ]; | |||
TriggerMacroRecord *record = &TriggerMacroRecordList[ triggerMacroIndex ]; | |||
// Check if macro has finished and should be incremented sequence elements | |||
if ( record->state == TriggerMacro_Release ) | |||
{ | |||
record->state = TriggerMacro_Waiting; | |||
record->pos = record->pos + macro->guide[ record->pos ] * TriggerGuideSize + 1; | |||
} | |||
// Current Macro position | |||
var_uint_t pos = record->pos; | |||
// Length of the combo being processed | |||
uint8_t comboLength = macro->guide[ pos ] * TriggerGuideSize; | |||
// If no combo items are left, remove the TriggerMacro from the pending list | |||
if ( comboLength == 0 ) | |||
{ | |||
return TriggerMacroEval_Remove; | |||
} | |||
// Check if this is a long Trigger Macro | |||
uint8_t longMacro = Macro_isLongTriggerMacro( macro ); | |||
// Iterate through the items in the combo, voting the on the key state | |||
// 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 comboItem = pos + 1; comboItem < pos + comboLength + 1; comboItem += TriggerGuideSize ) | |||
{ | |||
// Assign TriggerGuide element (key type, state and scancode) | |||
TriggerGuide *guide = (TriggerGuide*)(¯o->guide[ comboItem ]); | |||
TriggerMacroVote vote = TriggerMacroVote_Invalid; | |||
// Iterate through the key buffer, comparing to each key in the combo | |||
for ( var_uint_t key = 0; key < macroTriggerListBufferSize; key++ ) | |||
{ | |||
// Lookup key information | |||
TriggerGuide *keyInfo = ¯oTriggerListBuffer[ key ]; | |||
// 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 |= longMacro | |||
? Macro_evalLongTriggerMacroVote( keyInfo, guide ) | |||
: Macro_evalShortTriggerMacroVote( keyInfo, guide ); | |||
if ( vote >= TriggerMacroVote_Pass ) | |||
{ | |||
vote &= TriggerMacroVote_Release | TriggerMacroVote_PassRelease | TriggerMacroVote_Pass; | |||
break; | |||
} | |||
} | |||
// If no pass vote was found after scanning all of the keys | |||
// Fail the combo, if this is a short macro (long macros already will have a fail vote) | |||
if ( !longMacro && vote < TriggerMacroVote_Pass ) | |||
vote |= TriggerMacroVote_Fail; | |||
// After voting, append to overall vote | |||
overallVote |= vote; | |||
} | |||
// If no pass vote was found after scanning the entire combo | |||
// And this is the first position in the combo, just remove it (nothing important happened) | |||
if ( longMacro && overallVote & TriggerMacroVote_DoNothingRelease && pos == 0 ) | |||
overallVote |= TriggerMacroVote_Fail; | |||
// 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 && longMacro ) | |||
{ | |||
// Just doing nothing :) | |||
} | |||
// 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 && record->state == TriggerMacro_Press ) | |||
{ | |||
record->state = TriggerMacro_Release; | |||
// If this is the last combo in the sequence, remove from the pending list | |||
if ( macro->guide[ record->pos + macro->guide[ record->pos ] * TriggerGuideSize + 1 ] == 0 ) | |||
return TriggerMacroEval_DoResultAndRemove; | |||
} | |||
// If passing and in Waiting state, set macro state to Press | |||
else if ( overallVote & TriggerMacroVote_Pass | |||
&& ( record->state == TriggerMacro_Waiting || record->state == TriggerMacro_Press ) ) | |||
{ | |||
record->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 long macro repeat rate | |||
if ( macro->guide[ pos + comboLength + 1 ] == 0 ) | |||
{ | |||
// Long result macro (more than 1 combo) | |||
if ( Macro_isLongResultMacro( &ResultMacroList[ macro->result ] ) ) | |||
{ | |||
// Only ever trigger result once, on press | |||
if ( overallVote == TriggerMacroVote_Pass ) | |||
{ | |||
return TriggerMacroEval_DoResultAndRemove; | |||
} | |||
} | |||
// Short result macro | |||
else | |||
{ | |||
// Only trigger result once, on press, if long trigger (more than 1 combo) | |||
if ( Macro_isLongTriggerMacro( macro ) ) | |||
{ | |||
return TriggerMacroEval_DoResultAndRemove; | |||
} | |||
// Otherwise, trigger result continuously | |||
else | |||
{ | |||
return TriggerMacroEval_DoResult; | |||
} | |||
} | |||
} | |||
} | |||
// Otherwise, just remove the macro on key release | |||
// One more result has to be called to indicate to the ResultMacro that the key transitioned to the release state | |||
else if ( overallVote & TriggerMacroVote_Release ) | |||
{ | |||
return TriggerMacroEval_DoResultAndRemove; | |||
} | |||
// If this is a short macro, just remove it | |||
// The state can be rebuilt on the next iteration | |||
if ( !longMacro ) | |||
return TriggerMacroEval_Remove; | |||
return TriggerMacroEval_DoNothing; | |||
} | |||
// Evaluate/Update ResultMacro | |||
inline ResultMacroEval Macro_evalResultMacro( var_uint_t resultMacroIndex ) | |||
{ | |||
// Lookup ResultMacro | |||
const ResultMacro *macro = &ResultMacroList[ resultMacroIndex ]; | |||
ResultMacroRecord *record = &ResultMacroRecordList[ resultMacroIndex ]; | |||
// Current Macro position | |||
var_uint_t pos = record->pos; | |||
// Length of combo being processed | |||
uint8_t comboLength = macro->guide[ pos ]; | |||
// Function Counter, used to keep track of the combo items processed | |||
var_uint_t funcCount = 0; | |||
// Combo Item Position within the guide | |||
var_uint_t comboItem = pos + 1; | |||
// Iterate through the Result Combo | |||
while ( funcCount < comboLength ) | |||
{ | |||
// Assign TriggerGuide element (key type, state and scancode) | |||
ResultGuide *guide = (ResultGuide*)(¯o->guide[ comboItem ]); | |||
// Do lookup on capability function | |||
void (*capability)(uint8_t, uint8_t, uint8_t*) = (void(*)(uint8_t, uint8_t, uint8_t*))(CapabilitiesList[ guide->index ].func); | |||
// Call capability | |||
capability( record->state, record->stateType, &guide->args ); | |||
// Increment counters | |||
funcCount++; | |||
comboItem += ResultGuideSize( (ResultGuide*)(¯o->guide[ comboItem ]) ); | |||
} | |||
// Move to next item in the sequence | |||
record->pos = comboItem; | |||
// If the ResultMacro is finished, remove | |||
if ( macro->guide[ comboItem ] == 0 ) | |||
{ | |||
record->pos = 0; | |||
return ResultMacroEval_Remove; | |||
} | |||
// Otherwise leave the macro in the list | |||
return ResultMacroEval_DoNothing; | |||
} | |||
// Update pending trigger list | |||
inline void Macro_updateTriggerMacroPendingList() | |||
{ | |||
// Iterate over the macroTriggerListBuffer to add any new Trigger Macros to the pending list | |||
for ( var_uint_t key = 0; key < macroTriggerListBufferSize; key++ ) | |||
{ | |||
// TODO LED States | |||
// TODO Analog Switches | |||
// Only add TriggerMacro to pending list if key was pressed (not held, released or off) | |||
if ( macroTriggerListBuffer[ key ].state == 0x00 && macroTriggerListBuffer[ key ].state != 0x01 ) | |||
continue; | |||
// TODO Analog | |||
// If this is a release case, indicate to layer lookup for possible latch expiry | |||
uint8_t latch_expire = macroTriggerListBuffer[ key ].state == 0x03; | |||
// Lookup Trigger List | |||
nat_ptr_t *triggerList = Macro_layerLookup( ¯oTriggerListBuffer[ key ], latch_expire ); | |||
// If there was an error during lookup, skip | |||
if ( triggerList == 0 ) | |||
continue; | |||
// Number of Triggers in list | |||
nat_ptr_t 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 ( var_uint_t macro = 1; macro < triggerListSize + 1; macro++ ) | |||
{ | |||
// Lookup trigger macro index | |||
var_uint_t triggerMacroIndex = triggerList[ macro ]; | |||
// Iterate over macroTriggerMacroPendingList to see if any macro in the scancode's | |||
// triggerList needs to be added | |||
var_uint_t 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; | |||
// Reset macro position | |||
TriggerMacroRecordList[ triggerMacroIndex ].pos = 0; | |||
TriggerMacroRecordList[ triggerMacroIndex ].state = TriggerMacro_Waiting; | |||
} | |||
} | |||
} | |||
} | |||
// Macro Procesing Loop | |||
// Called once per USB buffer send | |||
inline void Macro_process() | |||
@@ -1187,65 +749,12 @@ inline void Macro_process() | |||
dbug_print("Macro Step"); | |||
} | |||
// Update pending trigger list, before processing TriggerMacros | |||
Macro_updateTriggerMacroPendingList(); | |||
// Tail pointer for macroTriggerMacroPendingList | |||
// Macros must be explicitly re-added | |||
var_uint_t macroTriggerMacroPendingListTail = 0; | |||
// Process Trigger Macros | |||
Trigger_process(); | |||
// Iterate through the pending TriggerMacros, processing each of them | |||
for ( var_uint_t macro = 0; macro < macroTriggerMacroPendingListSize; macro++ ) | |||
{ | |||
switch ( Macro_evalTriggerMacro( macroTriggerMacroPendingList[ macro ] ) ) | |||
{ | |||
// Trigger Result Macro (purposely falling through) | |||
case TriggerMacroEval_DoResult: | |||
// Append ResultMacro to PendingList | |||
Macro_appendResultMacroToPendingList( &TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ] ); | |||
default: | |||
macroTriggerMacroPendingList[ macroTriggerMacroPendingListTail++ ] = macroTriggerMacroPendingList[ macro ]; | |||
break; | |||
// Trigger Result Macro and Remove (purposely falling through) | |||
case TriggerMacroEval_DoResultAndRemove: | |||
// Append ResultMacro to PendingList | |||
Macro_appendResultMacroToPendingList( &TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ] ); | |||
// Remove Macro from Pending List, nothing to do, removing by default | |||
case TriggerMacroEval_Remove: | |||
break; | |||
} | |||
} | |||
// Update the macroTriggerMacroPendingListSize with the tail pointer | |||
macroTriggerMacroPendingListSize = macroTriggerMacroPendingListTail; | |||
// Tail pointer for macroResultMacroPendingList | |||
// Macros must be explicitly re-added | |||
var_uint_t macroResultMacroPendingListTail = 0; | |||
// Iterate through the pending ResultMacros, processing each of them | |||
for ( var_uint_t macro = 0; macro < macroResultMacroPendingListSize; macro++ ) | |||
{ | |||
switch ( Macro_evalResultMacro( macroResultMacroPendingList[ macro ] ) ) | |||
{ | |||
// Re-add macros to pending list | |||
case ResultMacroEval_DoNothing: | |||
default: | |||
macroResultMacroPendingList[ macroResultMacroPendingListTail++ ] = macroResultMacroPendingList[ macro ]; | |||
break; | |||
// Remove Macro from Pending List, nothing to do, removing by default | |||
case ResultMacroEval_Remove: | |||
break; | |||
} | |||
} | |||
// Update the macroResultMacroPendingListSize with the tail pointer | |||
macroResultMacroPendingListSize = macroResultMacroPendingListTail; | |||
// Process result macros | |||
Result_process(); | |||
// Signal buffer that we've used it | |||
Scan_finishedWithMacro( macroTriggerListBufferSize ); | |||
@@ -1282,20 +791,11 @@ inline void Macro_setup() | |||
// Set the current rotated layer to 0 | |||
Macro_rotationLayer = 0; | |||
// Initialize TriggerMacro states | |||
for ( var_uint_t macro = 0; macro < TriggerMacroNum; macro++ ) | |||
{ | |||
TriggerMacroRecordList[ macro ].pos = 0; | |||
TriggerMacroRecordList[ macro ].state = TriggerMacro_Waiting; | |||
} | |||
// Setup Triggers | |||
Trigger_setup(); | |||
// Initialize ResultMacro states | |||
for ( var_uint_t macro = 0; macro < ResultMacroNum; macro++ ) | |||
{ | |||
ResultMacroRecordList[ macro ].pos = 0; | |||
ResultMacroRecordList[ macro ].state = 0; | |||
ResultMacroRecordList[ macro ].stateType = 0; | |||
} | |||
// Setup Results | |||
Result_setup(); | |||
} | |||
@@ -0,0 +1,156 @@ | |||
/* Copyright (C) 2014-2016 by Jacob Alexander | |||
* | |||
* This file is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This file is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Includes ----- | |||
// Compiler Includes | |||
#include <Lib/MacroLib.h> | |||
// Project Includes | |||
#include <led.h> | |||
#include <print.h> | |||
// Local Includes | |||
#include "result.h" | |||
#include "kll.h" | |||
// ----- Enums ----- | |||
typedef enum ResultMacroEval { | |||
ResultMacroEval_DoNothing, | |||
ResultMacroEval_Remove, | |||
} ResultMacroEval; | |||
// ----- KLL Generated Variables ----- | |||
extern const Capability CapabilitiesList[]; | |||
extern const ResultMacro ResultMacroList[]; | |||
extern ResultMacroRecord ResultMacroRecordList[]; | |||
// ----- Variables ----- | |||
// Pending Result Macro Index List | |||
// * Any result macro that needs processing from a previous macro processing loop | |||
index_uint_t macroResultMacroPendingList[ ResultMacroNum ] = { 0 }; | |||
index_uint_t macroResultMacroPendingListSize = 0; | |||
// ----- Functions ----- | |||
// Evaluate/Update ResultMacro | |||
inline ResultMacroEval Macro_evalResultMacro( var_uint_t resultMacroIndex ) | |||
{ | |||
// Lookup ResultMacro | |||
const ResultMacro *macro = &ResultMacroList[ resultMacroIndex ]; | |||
ResultMacroRecord *record = &ResultMacroRecordList[ resultMacroIndex ]; | |||
// Current Macro position | |||
var_uint_t pos = record->pos; | |||
// Length of combo being processed | |||
uint8_t comboLength = macro->guide[ pos ]; | |||
// Function Counter, used to keep track of the combo items processed | |||
var_uint_t funcCount = 0; | |||
// Combo Item Position within the guide | |||
var_uint_t comboItem = pos + 1; | |||
// Iterate through the Result Combo | |||
while ( funcCount < comboLength ) | |||
{ | |||
// Assign TriggerGuide element (key type, state and scancode) | |||
ResultGuide *guide = (ResultGuide*)(¯o->guide[ comboItem ]); | |||
// Do lookup on capability function | |||
void (*capability)(uint8_t, uint8_t, uint8_t*) = (void(*)(uint8_t, uint8_t, uint8_t*))(CapabilitiesList[ guide->index ].func); | |||
// Call capability | |||
capability( record->state, record->stateType, &guide->args ); | |||
// Increment counters | |||
funcCount++; | |||
comboItem += ResultGuideSize( (ResultGuide*)(¯o->guide[ comboItem ]) ); | |||
} | |||
// Move to next item in the sequence | |||
record->pos = comboItem; | |||
// If the ResultMacro is finished, remove | |||
if ( macro->guide[ comboItem ] == 0 ) | |||
{ | |||
record->pos = 0; | |||
return ResultMacroEval_Remove; | |||
} | |||
// Otherwise leave the macro in the list | |||
return ResultMacroEval_DoNothing; | |||
} | |||
void Result_add( uint32_t index ) | |||
{ | |||
} | |||
void Result_setup() | |||
{ | |||
// Initialize ResultMacro states | |||
for ( var_uint_t macro = 0; macro < ResultMacroNum; macro++ ) | |||
{ | |||
ResultMacroRecordList[ macro ].pos = 0; | |||
ResultMacroRecordList[ macro ].state = 0; | |||
ResultMacroRecordList[ macro ].stateType = 0; | |||
} | |||
} | |||
void Result_process() | |||
{ | |||
// Tail pointer for macroResultMacroPendingList | |||
// Macros must be explicitly re-added | |||
var_uint_t macroResultMacroPendingListTail = 0; | |||
// Iterate through the pending ResultMacros, processing each of them | |||
for ( var_uint_t macro = 0; macro < macroResultMacroPendingListSize; macro++ ) | |||
{ | |||
switch ( Macro_evalResultMacro( macroResultMacroPendingList[ macro ] ) ) | |||
{ | |||
// Re-add macros to pending list | |||
case ResultMacroEval_DoNothing: | |||
default: | |||
macroResultMacroPendingList[ macroResultMacroPendingListTail++ ] = macroResultMacroPendingList[ macro ]; | |||
break; | |||
// Remove Macro from Pending List, nothing to do, removing by default | |||
case ResultMacroEval_Remove: | |||
break; | |||
} | |||
} | |||
// Update the macroResultMacroPendingListSize with the tail pointer | |||
macroResultMacroPendingListSize = macroResultMacroPendingListTail; | |||
} | |||
@@ -0,0 +1,34 @@ | |||
/* Copyright (C) 2014-2016 by Jacob Alexander | |||
* | |||
* This file is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This file is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#pragma once | |||
// ----- Includes ----- | |||
// Compiler Includes | |||
#include <stdint.h> | |||
// ----- Functions ----- | |||
// Add to results queue | |||
// If event was alraedy added to the queue, new event is dropped | |||
void Result_add( uint32_t result ); | |||
void Result_process(); | |||
void Result_setup(); | |||
@@ -1,6 +1,6 @@ | |||
###| CMake Kiibohd Controller Macro Module |### | |||
# | |||
# Written by Jacob Alexander in 2014-2015 for the Kiibohd Controller | |||
# Written by Jacob Alexander in 2014-2016 for the Kiibohd Controller | |||
# | |||
# Released into the Public Domain | |||
# | |||
@@ -13,6 +13,8 @@ | |||
set ( Module_SRCS | |||
macro.c | |||
result.c | |||
trigger.c | |||
) | |||
@@ -0,0 +1,508 @@ | |||
/* Copyright (C) 2014-2016 by Jacob Alexander | |||
* | |||
* This file is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This file is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// ----- Includes ----- | |||
// Compiler Includes | |||
#include <Lib/MacroLib.h> | |||
// Project Includes | |||
#include <led.h> | |||
#include <print.h> | |||
// Local Includes | |||
#include "trigger.h" | |||
#include "kll.h" | |||
// ----- Enums ----- | |||
// Bit positions are important, passes (correct key) always trump incorrect key votes | |||
typedef enum TriggerMacroVote { | |||
TriggerMacroVote_Release = 0x10, // Correct key | |||
TriggerMacroVote_PassRelease = 0x18, // Correct key (both pass and release) | |||
TriggerMacroVote_Pass = 0x8, // Correct key | |||
TriggerMacroVote_DoNothingRelease = 0x4, // Incorrect 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; | |||
// ----- Generated KLL Variables ----- | |||
extern const Capability CapabilitiesList[]; | |||
extern const TriggerMacro TriggerMacroList[]; | |||
extern TriggerMacroRecord TriggerMacroRecordList[]; | |||
extern const ResultMacro ResultMacroList[]; | |||
// ----- Variables ----- | |||
// Key Trigger List Buffer and Layer Cache | |||
// The layer cache is set on press only, hold and release events refer to the value set on press | |||
extern TriggerGuide macroTriggerListBuffer[]; | |||
extern var_uint_t macroTriggerListBufferSize; | |||
extern var_uint_t macroTriggerListLayerCache[]; | |||
// Pending Trigger Macro Index List | |||
// * Any trigger macros that need processing from a previous macro processing loop | |||
// TODO, figure out a good way to scale this array size without wasting too much memory, but not rejecting macros | |||
// Possibly could be calculated by the KLL compiler | |||
// XXX It may be possible to calculate the worst case using the KLL compiler | |||
index_uint_t macroTriggerMacroPendingList[ TriggerMacroNum ] = { 0 }; | |||
index_uint_t macroTriggerMacroPendingListSize = 0; | |||
// ----- Protected Macro Functions ----- | |||
extern nat_ptr_t *Macro_layerLookup( TriggerGuide *guide, uint8_t latch_expire ); | |||
extern void Macro_appendResultMacroToPendingList( const TriggerMacro *triggerMacro ); | |||
// ----- Functions ----- | |||
// Determine if long ResultMacro (more than 1 seqence element) | |||
inline uint8_t Macro_isLongResultMacro( const ResultMacro *macro ) | |||
{ | |||
// Check the second sequence combo length | |||
// If non-zero return non-zero (long sequence) | |||
// 0 otherwise (short sequence) | |||
var_uint_t position = 1; | |||
for ( var_uint_t result = 0; result < macro->guide[0]; result++ ) | |||
position += ResultGuideSize( (ResultGuide*)¯o->guide[ position ] ); | |||
return macro->guide[ position ]; | |||
} | |||
// Determine if long TriggerMacro (more than 1 sequence element) | |||
inline uint8_t Macro_isLongTriggerMacro( const TriggerMacro *macro ) | |||
{ | |||
// Check the second sequence combo length | |||
// If non-zero return non-zero (long sequence) | |||
// 0 otherwise (short sequence) | |||
return macro->guide[ macro->guide[0] * TriggerGuideSize + 1 ]; | |||
} | |||
// Votes on the given key vs. guide, short macros | |||
inline TriggerMacroVote Macro_evalShortTriggerMacroVote( TriggerGuide *key, TriggerGuide *guide ) | |||
{ | |||
// Depending on key type | |||
switch ( guide->type ) | |||
{ | |||
// Normal State Type | |||
case 0x00: | |||
// For short TriggerMacros completely ignore incorrect keys | |||
if ( guide->scanCode == key->scanCode ) | |||
{ | |||
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; | |||
} | |||
} | |||
return TriggerMacroVote_DoNothing; | |||
// 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; | |||
} | |||
// Votes on the given key vs. guide, long macros | |||
// A long macro is defined as a guide with more than 1 combo | |||
inline TriggerMacroVote Macro_evalLongTriggerMacroVote( 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 ) | |||
{ | |||
switch ( key->state ) | |||
{ | |||
// Wrong key, pressed, fail | |||
case 0x01: | |||
return TriggerMacroVote_Fail; | |||
// Wrong key, held, do not pass (no effect) | |||
case 0x02: | |||
return TriggerMacroVote_DoNothing; | |||
// Wrong key released, fail out if pos == 0 | |||
case 0x03: | |||
return TriggerMacroVote_DoNothing | TriggerMacroVote_DoNothingRelease; | |||
} | |||
} | |||
// 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 | |||
TriggerMacroEval Macro_evalTriggerMacro( var_uint_t triggerMacroIndex ) | |||
{ | |||
// Lookup TriggerMacro | |||
const TriggerMacro *macro = &TriggerMacroList[ triggerMacroIndex ]; | |||
TriggerMacroRecord *record = &TriggerMacroRecordList[ triggerMacroIndex ]; | |||
// Check if macro has finished and should be incremented sequence elements | |||
if ( record->state == TriggerMacro_Release ) | |||
{ | |||
record->state = TriggerMacro_Waiting; | |||
record->pos = record->pos + macro->guide[ record->pos ] * TriggerGuideSize + 1; | |||
} | |||
// Current Macro position | |||
var_uint_t pos = record->pos; | |||
// Length of the combo being processed | |||
uint8_t comboLength = macro->guide[ pos ] * TriggerGuideSize; | |||
// If no combo items are left, remove the TriggerMacro from the pending list | |||
if ( comboLength == 0 ) | |||
{ | |||
return TriggerMacroEval_Remove; | |||
} | |||
// Check if this is a long Trigger Macro | |||
uint8_t longMacro = Macro_isLongTriggerMacro( macro ); | |||
// Iterate through the items in the combo, voting the on the key state | |||
// 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 comboItem = pos + 1; comboItem < pos + comboLength + 1; comboItem += TriggerGuideSize ) | |||
{ | |||
// Assign TriggerGuide element (key type, state and scancode) | |||
TriggerGuide *guide = (TriggerGuide*)(¯o->guide[ comboItem ]); | |||
TriggerMacroVote vote = TriggerMacroVote_Invalid; | |||
// Iterate through the key buffer, comparing to each key in the combo | |||
for ( var_uint_t key = 0; key < macroTriggerListBufferSize; key++ ) | |||
{ | |||
// Lookup key information | |||
TriggerGuide *keyInfo = ¯oTriggerListBuffer[ key ]; | |||
// 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 |= longMacro | |||
? Macro_evalLongTriggerMacroVote( keyInfo, guide ) | |||
: Macro_evalShortTriggerMacroVote( keyInfo, guide ); | |||
if ( vote >= TriggerMacroVote_Pass ) | |||
{ | |||
vote &= TriggerMacroVote_Release | TriggerMacroVote_PassRelease | TriggerMacroVote_Pass; | |||
break; | |||
} | |||
} | |||
// If no pass vote was found after scanning all of the keys | |||
// Fail the combo, if this is a short macro (long macros already will have a fail vote) | |||
if ( !longMacro && vote < TriggerMacroVote_Pass ) | |||
vote |= TriggerMacroVote_Fail; | |||
// After voting, append to overall vote | |||
overallVote |= vote; | |||
} | |||
// If no pass vote was found after scanning the entire combo | |||
// And this is the first position in the combo, just remove it (nothing important happened) | |||
if ( longMacro && overallVote & TriggerMacroVote_DoNothingRelease && pos == 0 ) | |||
overallVote |= TriggerMacroVote_Fail; | |||
// 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 && longMacro ) | |||
{ | |||
// Just doing nothing :) | |||
} | |||
// 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 && record->state == TriggerMacro_Press ) | |||
{ | |||
record->state = TriggerMacro_Release; | |||
// If this is the last combo in the sequence, remove from the pending list | |||
if ( macro->guide[ record->pos + macro->guide[ record->pos ] * TriggerGuideSize + 1 ] == 0 ) | |||
return TriggerMacroEval_DoResultAndRemove; | |||
} | |||
// If passing and in Waiting state, set macro state to Press | |||
else if ( overallVote & TriggerMacroVote_Pass | |||
&& ( record->state == TriggerMacro_Waiting || record->state == TriggerMacro_Press ) ) | |||
{ | |||
record->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 long macro repeat rate | |||
if ( macro->guide[ pos + comboLength + 1 ] == 0 ) | |||
{ | |||
// Long result macro (more than 1 combo) | |||
if ( Macro_isLongResultMacro( &ResultMacroList[ macro->result ] ) ) | |||
{ | |||
// Only ever trigger result once, on press | |||
if ( overallVote == TriggerMacroVote_Pass ) | |||
{ | |||
return TriggerMacroEval_DoResultAndRemove; | |||
} | |||
} | |||
// Short result macro | |||
else | |||
{ | |||
// Only trigger result once, on press, if long trigger (more than 1 combo) | |||
if ( Macro_isLongTriggerMacro( macro ) ) | |||
{ | |||
return TriggerMacroEval_DoResultAndRemove; | |||
} | |||
// Otherwise, trigger result continuously | |||
else | |||
{ | |||
return TriggerMacroEval_DoResult; | |||
} | |||
} | |||
} | |||
} | |||
// Otherwise, just remove the macro on key release | |||
// One more result has to be called to indicate to the ResultMacro that the key transitioned to the release state | |||
else if ( overallVote & TriggerMacroVote_Release ) | |||
{ | |||
return TriggerMacroEval_DoResultAndRemove; | |||
} | |||
// If this is a short macro, just remove it | |||
// The state can be rebuilt on the next iteration | |||
if ( !longMacro ) | |||
return TriggerMacroEval_Remove; | |||
return TriggerMacroEval_DoNothing; | |||
} | |||
// Update pending trigger list | |||
inline void Macro_updateTriggerMacroPendingList() | |||
{ | |||
// Iterate over the macroTriggerListBuffer to add any new Trigger Macros to the pending list | |||
for ( var_uint_t key = 0; key < macroTriggerListBufferSize; key++ ) | |||
{ | |||
// TODO LED States | |||
// TODO Analog Switches | |||
// Only add TriggerMacro to pending list if key was pressed (not held, released or off) | |||
if ( macroTriggerListBuffer[ key ].state == 0x00 && macroTriggerListBuffer[ key ].state != 0x01 ) | |||
continue; | |||
// TODO Analog | |||
// If this is a release case, indicate to layer lookup for possible latch expiry | |||
uint8_t latch_expire = macroTriggerListBuffer[ key ].state == 0x03; | |||
// Lookup Trigger List | |||
nat_ptr_t *triggerList = Macro_layerLookup( ¯oTriggerListBuffer[ key ], latch_expire ); | |||
// If there was an error during lookup, skip | |||
if ( triggerList == 0 ) | |||
continue; | |||
// Number of Triggers in list | |||
nat_ptr_t 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 ( var_uint_t macro = 1; macro < triggerListSize + 1; macro++ ) | |||
{ | |||
// Lookup trigger macro index | |||
var_uint_t triggerMacroIndex = triggerList[ macro ]; | |||
// Iterate over macroTriggerMacroPendingList to see if any macro in the scancode's | |||
// triggerList needs to be added | |||
var_uint_t 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; | |||
// Reset macro position | |||
TriggerMacroRecordList[ triggerMacroIndex ].pos = 0; | |||
TriggerMacroRecordList[ triggerMacroIndex ].state = TriggerMacro_Waiting; | |||
} | |||
} | |||
} | |||
} | |||
void Trigger_state( uint8_t type, uint8_t state, uint8_t index ) | |||
{ | |||
} | |||
uint8_t Trigger_update( uint8_t type, uint8_t state, uint8_t index ) | |||
{ | |||
return 0; | |||
} | |||
void Trigger_setup() | |||
{ | |||
// Initialize TriggerMacro states | |||
for ( var_uint_t macro = 0; macro < TriggerMacroNum; macro++ ) | |||
{ | |||
TriggerMacroRecordList[ macro ].pos = 0; | |||
TriggerMacroRecordList[ macro ].state = TriggerMacro_Waiting; | |||
} | |||
} | |||
void Trigger_process() | |||
{ | |||
// Update pending trigger list, before processing TriggerMacros | |||
Macro_updateTriggerMacroPendingList(); | |||
// Tail pointer for macroTriggerMacroPendingList | |||
// Macros must be explicitly re-added | |||
var_uint_t macroTriggerMacroPendingListTail = 0; | |||
// Iterate through the pending TriggerMacros, processing each of them | |||
for ( var_uint_t macro = 0; macro < macroTriggerMacroPendingListSize; macro++ ) | |||
{ | |||
switch ( Macro_evalTriggerMacro( macroTriggerMacroPendingList[ macro ] ) ) | |||
{ | |||
// Trigger Result Macro (purposely falling through) | |||
case TriggerMacroEval_DoResult: | |||
// Append ResultMacro to PendingList | |||
Macro_appendResultMacroToPendingList( &TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ] ); | |||
default: | |||
macroTriggerMacroPendingList[ macroTriggerMacroPendingListTail++ ] = macroTriggerMacroPendingList[ macro ]; | |||
break; | |||
// Trigger Result Macro and Remove (purposely falling through) | |||
case TriggerMacroEval_DoResultAndRemove: | |||
// Append ResultMacro to PendingList | |||
Macro_appendResultMacroToPendingList( &TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ] ); | |||
// Remove Macro from Pending List, nothing to do, removing by default | |||
case TriggerMacroEval_Remove: | |||
break; | |||
} | |||
} | |||
// Update the macroTriggerMacroPendingListSize with the tail pointer | |||
macroTriggerMacroPendingListSize = macroTriggerMacroPendingListTail; | |||
} | |||
@@ -0,0 +1,38 @@ | |||
/* Copyright (C) 2014-2016 by Jacob Alexander | |||
* | |||
* This file is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This file is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#pragma once | |||
// ----- Includes ----- | |||
// Compiler Includes | |||
#include <stdint.h> | |||
// ----- Functions ----- | |||
// Updates trigger state, but does not add event to processing queue | |||
void Trigger_state( uint8_t type, uint8_t state, uint8_t index ); | |||
// Updates trigger state, adds event to processing queue | |||
// If event was already added this cycle, it will be discarded, state is not updated, and returns 0 | |||
// Returns 1 otherwise | |||
uint8_t Trigger_update( uint8_t type, uint8_t state, uint8_t index ); | |||
void Trigger_process(); | |||
void Trigger_setup(); | |||