# HG changeset patch # User Jacob Alexander # Date 1462758628 25200 # Node ID 0f7a6b593dc41ca4cbb3f08958292c70951391a0 # Parent 1a2fb67b023704d08670139ad3ad8d3ee71e03ed Initial refactoring of PartialMap for supporting custom Triggers - Requires a recent KLL - Functionality wise, nothing has changed diff -r 1a2fb67b0237 -r 0f7a6b593dc4 Macro/PartialMap/capabilities.kll --- a/Macro/PartialMap/capabilities.kll Thu May 05 12:57:30 2016 -0700 +++ b/Macro/PartialMap/capabilities.kll Sun May 08 18:50:28 2016 -0700 @@ -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 @@ 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) + diff -r 1a2fb67b0237 -r 0f7a6b593dc4 Macro/PartialMap/kll.h --- a/Macro/PartialMap/kll.h Thu May 05 12:57:30 2016 -0700 +++ b/Macro/PartialMap/kll.h Sun May 08 18:50:28 2016 -0700 @@ -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 @@ -49,6 +49,20 @@ #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 // e.g. mk20s -> 32 bit @@ -159,7 +173,8 @@ } Capability; // Total Number of Capabilities -#define CapabilitiesNum sizeof( CapabilitiesList ) / sizeof( Capability ) +// (generated by KLL) +#define CapabilitiesNum CapabilitiesNum_KLL // -- Result Macros @@ -179,7 +194,8 @@ // 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 @@ // 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 @@ // * 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 diff -r 1a2fb67b0237 -r 0f7a6b593dc4 Macro/PartialMap/macro.c --- a/Macro/PartialMap/macro.c Thu May 05 12:57:30 2016 -0700 +++ b/Macro/PartialMap/macro.c Sun May 08 18:50:28 2016 -0700 @@ -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 @@ -// ----- 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ } -// 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 @@ 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; - - // 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; + // Process Trigger Macros + Trigger_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; + // Process result macros + Result_process(); // Signal buffer that we've used it Scan_finishedWithMacro( macroTriggerListBufferSize ); @@ -1282,20 +791,11 @@ // 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(); } diff -r 1a2fb67b0237 -r 0f7a6b593dc4 Macro/PartialMap/result.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Macro/PartialMap/result.c Sun May 08 18:50:28 2016 -0700 @@ -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 . + */ + +// ----- Includes ----- + +// Compiler Includes +#include + +// Project Includes +#include +#include + +// 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; +} + diff -r 1a2fb67b0237 -r 0f7a6b593dc4 Macro/PartialMap/result.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Macro/PartialMap/result.h Sun May 08 18:50:28 2016 -0700 @@ -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 . + */ + +#pragma once + +// ----- Includes ----- + +// Compiler Includes +#include + + + +// ----- 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(); + diff -r 1a2fb67b0237 -r 0f7a6b593dc4 Macro/PartialMap/setup.cmake --- a/Macro/PartialMap/setup.cmake Thu May 05 12:57:30 2016 -0700 +++ b/Macro/PartialMap/setup.cmake Sun May 08 18:50:28 2016 -0700 @@ -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 ) diff -r 1a2fb67b0237 -r 0f7a6b593dc4 Macro/PartialMap/trigger.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Macro/PartialMap/trigger.c Sun May 08 18:50:28 2016 -0700 @@ -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 . + */ + +// ----- Includes ----- + +// Compiler Includes +#include + +// Project Includes +#include +#include + +// 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; +} + diff -r 1a2fb67b0237 -r 0f7a6b593dc4 Macro/PartialMap/trigger.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Macro/PartialMap/trigger.h Sun May 08 18:50:28 2016 -0700 @@ -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 . + */ + +#pragma once + +// ----- Includes ----- + +// Compiler Includes +#include + + + +// ----- 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(); +