comparison Macro/PartialMap/trigger.c @ 433:0f7a6b593dc4

Initial refactoring of PartialMap for supporting custom Triggers - Requires a recent KLL - Functionality wise, nothing has changed
author Jacob Alexander <haata@kiibohd.com>
date Sun, 08 May 2016 18:50:28 -0700
parents
children
comparison
equal deleted inserted replaced
432:1a2fb67b0237 433:0f7a6b593dc4
1 /* Copyright (C) 2014-2016 by Jacob Alexander
2 *
3 * This file is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This file is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this file. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 // ----- Includes -----
18
19 // Compiler Includes
20 #include <Lib/MacroLib.h>
21
22 // Project Includes
23 #include <led.h>
24 #include <print.h>
25
26 // Local Includes
27 #include "trigger.h"
28 #include "kll.h"
29
30
31
32 // ----- Enums -----
33
34 // Bit positions are important, passes (correct key) always trump incorrect key votes
35 typedef enum TriggerMacroVote {
36 TriggerMacroVote_Release = 0x10, // Correct key
37 TriggerMacroVote_PassRelease = 0x18, // Correct key (both pass and release)
38 TriggerMacroVote_Pass = 0x8, // Correct key
39 TriggerMacroVote_DoNothingRelease = 0x4, // Incorrect key
40 TriggerMacroVote_DoNothing = 0x2, // Incorrect key
41 TriggerMacroVote_Fail = 0x1, // Incorrect key
42 TriggerMacroVote_Invalid = 0x0, // Invalid state
43 } TriggerMacroVote;
44
45 typedef enum TriggerMacroEval {
46 TriggerMacroEval_DoNothing,
47 TriggerMacroEval_DoResult,
48 TriggerMacroEval_DoResultAndRemove,
49 TriggerMacroEval_Remove,
50 } TriggerMacroEval;
51
52
53
54 // ----- Generated KLL Variables -----
55
56 extern const Capability CapabilitiesList[];
57
58 extern const TriggerMacro TriggerMacroList[];
59 extern TriggerMacroRecord TriggerMacroRecordList[];
60
61 extern const ResultMacro ResultMacroList[];
62
63
64
65 // ----- Variables -----
66
67 // Key Trigger List Buffer and Layer Cache
68 // The layer cache is set on press only, hold and release events refer to the value set on press
69 extern TriggerGuide macroTriggerListBuffer[];
70 extern var_uint_t macroTriggerListBufferSize;
71 extern var_uint_t macroTriggerListLayerCache[];
72
73 // Pending Trigger Macro Index List
74 // * Any trigger macros that need processing from a previous macro processing loop
75 // TODO, figure out a good way to scale this array size without wasting too much memory, but not rejecting macros
76 // Possibly could be calculated by the KLL compiler
77 // XXX It may be possible to calculate the worst case using the KLL compiler
78 index_uint_t macroTriggerMacroPendingList[ TriggerMacroNum ] = { 0 };
79 index_uint_t macroTriggerMacroPendingListSize = 0;
80
81
82
83 // ----- Protected Macro Functions -----
84
85 extern nat_ptr_t *Macro_layerLookup( TriggerGuide *guide, uint8_t latch_expire );
86
87 extern void Macro_appendResultMacroToPendingList( const TriggerMacro *triggerMacro );
88
89
90
91 // ----- Functions -----
92
93 // Determine if long ResultMacro (more than 1 seqence element)
94 inline uint8_t Macro_isLongResultMacro( const ResultMacro *macro )
95 {
96 // Check the second sequence combo length
97 // If non-zero return non-zero (long sequence)
98 // 0 otherwise (short sequence)
99 var_uint_t position = 1;
100 for ( var_uint_t result = 0; result < macro->guide[0]; result++ )
101 position += ResultGuideSize( (ResultGuide*)&macro->guide[ position ] );
102 return macro->guide[ position ];
103 }
104
105
106 // Determine if long TriggerMacro (more than 1 sequence element)
107 inline uint8_t Macro_isLongTriggerMacro( const TriggerMacro *macro )
108 {
109 // Check the second sequence combo length
110 // If non-zero return non-zero (long sequence)
111 // 0 otherwise (short sequence)
112 return macro->guide[ macro->guide[0] * TriggerGuideSize + 1 ];
113 }
114
115
116 // Votes on the given key vs. guide, short macros
117 inline TriggerMacroVote Macro_evalShortTriggerMacroVote( TriggerGuide *key, TriggerGuide *guide )
118 {
119 // Depending on key type
120 switch ( guide->type )
121 {
122 // Normal State Type
123 case 0x00:
124 // For short TriggerMacros completely ignore incorrect keys
125 if ( guide->scanCode == key->scanCode )
126 {
127 switch ( key->state )
128 {
129 // Correct key, pressed, possible passing
130 case 0x01:
131 return TriggerMacroVote_Pass;
132
133 // Correct key, held, possible passing or release
134 case 0x02:
135 return TriggerMacroVote_PassRelease;
136
137 // Correct key, released, possible release
138 case 0x03:
139 return TriggerMacroVote_Release;
140 }
141 }
142
143 return TriggerMacroVote_DoNothing;
144
145 // LED State Type
146 case 0x01:
147 erro_print("LED State Type - Not implemented...");
148 break;
149
150 // Analog State Type
151 case 0x02:
152 erro_print("Analog State Type - Not implemented...");
153 break;
154
155 // Invalid State Type
156 default:
157 erro_print("Invalid State Type. This is a bug.");
158 break;
159 }
160
161 // XXX Shouldn't reach here
162 return TriggerMacroVote_Invalid;
163 }
164
165
166 // Votes on the given key vs. guide, long macros
167 // A long macro is defined as a guide with more than 1 combo
168 inline TriggerMacroVote Macro_evalLongTriggerMacroVote( TriggerGuide *key, TriggerGuide *guide )
169 {
170 // Depending on key type
171 switch ( guide->type )
172 {
173 // Normal State Type
174 case 0x00:
175 // Depending on the state of the buffered key, make voting decision
176 // Incorrect key
177 if ( guide->scanCode != key->scanCode )
178 {
179 switch ( key->state )
180 {
181 // Wrong key, pressed, fail
182 case 0x01:
183 return TriggerMacroVote_Fail;
184
185 // Wrong key, held, do not pass (no effect)
186 case 0x02:
187 return TriggerMacroVote_DoNothing;
188
189 // Wrong key released, fail out if pos == 0
190 case 0x03:
191 return TriggerMacroVote_DoNothing | TriggerMacroVote_DoNothingRelease;
192 }
193 }
194
195 // Correct key
196 else
197 {
198 switch ( key->state )
199 {
200 // Correct key, pressed, possible passing
201 case 0x01:
202 return TriggerMacroVote_Pass;
203
204 // Correct key, held, possible passing or release
205 case 0x02:
206 return TriggerMacroVote_PassRelease;
207
208 // Correct key, released, possible release
209 case 0x03:
210 return TriggerMacroVote_Release;
211 }
212 }
213
214 break;
215
216 // LED State Type
217 case 0x01:
218 erro_print("LED State Type - Not implemented...");
219 break;
220
221 // Analog State Type
222 case 0x02:
223 erro_print("Analog State Type - Not implemented...");
224 break;
225
226 // Invalid State Type
227 default:
228 erro_print("Invalid State Type. This is a bug.");
229 break;
230 }
231
232 // XXX Shouldn't reach here
233 return TriggerMacroVote_Invalid;
234 }
235
236
237 // Evaluate/Update TriggerMacro
238 TriggerMacroEval Macro_evalTriggerMacro( var_uint_t triggerMacroIndex )
239 {
240 // Lookup TriggerMacro
241 const TriggerMacro *macro = &TriggerMacroList[ triggerMacroIndex ];
242 TriggerMacroRecord *record = &TriggerMacroRecordList[ triggerMacroIndex ];
243
244 // Check if macro has finished and should be incremented sequence elements
245 if ( record->state == TriggerMacro_Release )
246 {
247 record->state = TriggerMacro_Waiting;
248 record->pos = record->pos + macro->guide[ record->pos ] * TriggerGuideSize + 1;
249 }
250
251 // Current Macro position
252 var_uint_t pos = record->pos;
253
254 // Length of the combo being processed
255 uint8_t comboLength = macro->guide[ pos ] * TriggerGuideSize;
256
257 // If no combo items are left, remove the TriggerMacro from the pending list
258 if ( comboLength == 0 )
259 {
260 return TriggerMacroEval_Remove;
261 }
262
263 // Check if this is a long Trigger Macro
264 uint8_t longMacro = Macro_isLongTriggerMacro( macro );
265
266 // Iterate through the items in the combo, voting the on the key state
267 // If any of the pressed keys do not match, fail the macro
268 //
269 // The macro is waiting for input when in the TriggerMacro_Waiting state
270 // Once all keys have been pressed/held (only those keys), entered TriggerMacro_Press state (passing)
271 // Transition to the next combo (if it exists) when a single key is released (TriggerMacro_Release state)
272 // On scan after position increment, change to TriggerMacro_Waiting state
273 // TODO Add support for system LED states (NumLock, CapsLock, etc.)
274 // TODO Add support for analog key states
275 // TODO Add support for 0x00 Key state (not pressing a key, not all that useful in general)
276 // TODO Add support for Press/Hold/Release differentiation when evaluating (not sure if useful)
277 TriggerMacroVote overallVote = TriggerMacroVote_Invalid;
278 for ( uint8_t comboItem = pos + 1; comboItem < pos + comboLength + 1; comboItem += TriggerGuideSize )
279 {
280 // Assign TriggerGuide element (key type, state and scancode)
281 TriggerGuide *guide = (TriggerGuide*)(&macro->guide[ comboItem ]);
282
283 TriggerMacroVote vote = TriggerMacroVote_Invalid;
284 // Iterate through the key buffer, comparing to each key in the combo
285 for ( var_uint_t key = 0; key < macroTriggerListBufferSize; key++ )
286 {
287 // Lookup key information
288 TriggerGuide *keyInfo = &macroTriggerListBuffer[ key ];
289
290 // If vote is a pass (>= 0x08, no more keys in the combo need to be looked at)
291 // Also mask all of the non-passing votes
292 vote |= longMacro
293 ? Macro_evalLongTriggerMacroVote( keyInfo, guide )
294 : Macro_evalShortTriggerMacroVote( keyInfo, guide );
295 if ( vote >= TriggerMacroVote_Pass )
296 {
297 vote &= TriggerMacroVote_Release | TriggerMacroVote_PassRelease | TriggerMacroVote_Pass;
298 break;
299 }
300 }
301
302 // If no pass vote was found after scanning all of the keys
303 // Fail the combo, if this is a short macro (long macros already will have a fail vote)
304 if ( !longMacro && vote < TriggerMacroVote_Pass )
305 vote |= TriggerMacroVote_Fail;
306
307 // After voting, append to overall vote
308 overallVote |= vote;
309 }
310
311 // If no pass vote was found after scanning the entire combo
312 // And this is the first position in the combo, just remove it (nothing important happened)
313 if ( longMacro && overallVote & TriggerMacroVote_DoNothingRelease && pos == 0 )
314 overallVote |= TriggerMacroVote_Fail;
315
316 // Decide new state of macro after voting
317 // Fail macro, remove from pending list
318 if ( overallVote & TriggerMacroVote_Fail )
319 {
320 return TriggerMacroEval_Remove;
321 }
322 // Do nothing, incorrect key is being held or released
323 else if ( overallVote & TriggerMacroVote_DoNothing && longMacro )
324 {
325 // Just doing nothing :)
326 }
327 // If ready for transition and in Press state, set to Waiting and increment combo position
328 // Position is incremented (and possibly remove the macro from the pending list) on the next iteration
329 else if ( overallVote & TriggerMacroVote_Release && record->state == TriggerMacro_Press )
330 {
331 record->state = TriggerMacro_Release;
332
333 // If this is the last combo in the sequence, remove from the pending list
334 if ( macro->guide[ record->pos + macro->guide[ record->pos ] * TriggerGuideSize + 1 ] == 0 )
335 return TriggerMacroEval_DoResultAndRemove;
336 }
337 // If passing and in Waiting state, set macro state to Press
338 else if ( overallVote & TriggerMacroVote_Pass
339 && ( record->state == TriggerMacro_Waiting || record->state == TriggerMacro_Press ) )
340 {
341 record->state = TriggerMacro_Press;
342
343 // If in press state, and this is the final combo, send request for ResultMacro
344 // Check to see if the result macro only has a single element
345 // If this result macro has more than 1 key, only send once
346 // TODO Add option to have long macro repeat rate
347 if ( macro->guide[ pos + comboLength + 1 ] == 0 )
348 {
349 // Long result macro (more than 1 combo)
350 if ( Macro_isLongResultMacro( &ResultMacroList[ macro->result ] ) )
351 {
352 // Only ever trigger result once, on press
353 if ( overallVote == TriggerMacroVote_Pass )
354 {
355 return TriggerMacroEval_DoResultAndRemove;
356 }
357 }
358 // Short result macro
359 else
360 {
361 // Only trigger result once, on press, if long trigger (more than 1 combo)
362 if ( Macro_isLongTriggerMacro( macro ) )
363 {
364 return TriggerMacroEval_DoResultAndRemove;
365 }
366 // Otherwise, trigger result continuously
367 else
368 {
369 return TriggerMacroEval_DoResult;
370 }
371 }
372 }
373 }
374 // Otherwise, just remove the macro on key release
375 // One more result has to be called to indicate to the ResultMacro that the key transitioned to the release state
376 else if ( overallVote & TriggerMacroVote_Release )
377 {
378 return TriggerMacroEval_DoResultAndRemove;
379 }
380
381 // If this is a short macro, just remove it
382 // The state can be rebuilt on the next iteration
383 if ( !longMacro )
384 return TriggerMacroEval_Remove;
385
386 return TriggerMacroEval_DoNothing;
387 }
388
389
390 // Update pending trigger list
391 inline void Macro_updateTriggerMacroPendingList()
392 {
393 // Iterate over the macroTriggerListBuffer to add any new Trigger Macros to the pending list
394 for ( var_uint_t key = 0; key < macroTriggerListBufferSize; key++ )
395 {
396 // TODO LED States
397 // TODO Analog Switches
398 // Only add TriggerMacro to pending list if key was pressed (not held, released or off)
399 if ( macroTriggerListBuffer[ key ].state == 0x00 && macroTriggerListBuffer[ key ].state != 0x01 )
400 continue;
401
402 // TODO Analog
403 // If this is a release case, indicate to layer lookup for possible latch expiry
404 uint8_t latch_expire = macroTriggerListBuffer[ key ].state == 0x03;
405
406 // Lookup Trigger List
407 nat_ptr_t *triggerList = Macro_layerLookup( &macroTriggerListBuffer[ key ], latch_expire );
408
409 // If there was an error during lookup, skip
410 if ( triggerList == 0 )
411 continue;
412
413 // Number of Triggers in list
414 nat_ptr_t triggerListSize = triggerList[0];
415
416 // Iterate over triggerList to see if any TriggerMacros need to be added
417 // First item is the number of items in the TriggerList
418 for ( var_uint_t macro = 1; macro < triggerListSize + 1; macro++ )
419 {
420 // Lookup trigger macro index
421 var_uint_t triggerMacroIndex = triggerList[ macro ];
422
423 // Iterate over macroTriggerMacroPendingList to see if any macro in the scancode's
424 // triggerList needs to be added
425 var_uint_t pending = 0;
426 for ( ; pending < macroTriggerMacroPendingListSize; pending++ )
427 {
428 // Stop scanning if the trigger macro index is found in the pending list
429 if ( macroTriggerMacroPendingList[ pending ] == triggerMacroIndex )
430 break;
431 }
432
433 // If the triggerMacroIndex (macro) was not found in the macroTriggerMacroPendingList
434 // Add it to the list
435 if ( pending == macroTriggerMacroPendingListSize )
436 {
437 macroTriggerMacroPendingList[ macroTriggerMacroPendingListSize++ ] = triggerMacroIndex;
438
439 // Reset macro position
440 TriggerMacroRecordList[ triggerMacroIndex ].pos = 0;
441 TriggerMacroRecordList[ triggerMacroIndex ].state = TriggerMacro_Waiting;
442 }
443 }
444 }
445 }
446
447
448
449 void Trigger_state( uint8_t type, uint8_t state, uint8_t index )
450 {
451 }
452
453
454 uint8_t Trigger_update( uint8_t type, uint8_t state, uint8_t index )
455 {
456 return 0;
457 }
458
459
460 void Trigger_setup()
461 {
462 // Initialize TriggerMacro states
463 for ( var_uint_t macro = 0; macro < TriggerMacroNum; macro++ )
464 {
465 TriggerMacroRecordList[ macro ].pos = 0;
466 TriggerMacroRecordList[ macro ].state = TriggerMacro_Waiting;
467 }
468 }
469
470
471 void Trigger_process()
472 {
473 // Update pending trigger list, before processing TriggerMacros
474 Macro_updateTriggerMacroPendingList();
475
476 // Tail pointer for macroTriggerMacroPendingList
477 // Macros must be explicitly re-added
478 var_uint_t macroTriggerMacroPendingListTail = 0;
479
480 // Iterate through the pending TriggerMacros, processing each of them
481 for ( var_uint_t macro = 0; macro < macroTriggerMacroPendingListSize; macro++ )
482 {
483 switch ( Macro_evalTriggerMacro( macroTriggerMacroPendingList[ macro ] ) )
484 {
485 // Trigger Result Macro (purposely falling through)
486 case TriggerMacroEval_DoResult:
487 // Append ResultMacro to PendingList
488 Macro_appendResultMacroToPendingList( &TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ] );
489
490 default:
491 macroTriggerMacroPendingList[ macroTriggerMacroPendingListTail++ ] = macroTriggerMacroPendingList[ macro ];
492 break;
493
494 // Trigger Result Macro and Remove (purposely falling through)
495 case TriggerMacroEval_DoResultAndRemove:
496 // Append ResultMacro to PendingList
497 Macro_appendResultMacroToPendingList( &TriggerMacroList[ macroTriggerMacroPendingList[ macro ] ] );
498
499 // Remove Macro from Pending List, nothing to do, removing by default
500 case TriggerMacroEval_Remove:
501 break;
502 }
503 }
504
505 // Update the macroTriggerMacroPendingListSize with the tail pointer
506 macroTriggerMacroPendingListSize = macroTriggerMacroPendingListTail;
507 }
508