Mercurial > louis > kiibohd-controller
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*)¯o->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*)(¯o->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 = ¯oTriggerListBuffer[ 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( ¯oTriggerListBuffer[ 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 |