-
-
Notifications
You must be signed in to change notification settings - Fork 39.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Keyboard combination triggers #960
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
eac8fa7
Implemented basic key combination feature
ofples b6bf4e0
Added support for timing out combos if a key as been pressed for long…
ofples 6e7cfa8
Refactored as well as added support for action keys in combos
ofples 6a462c8
Merge branch 'master' into feature/combos
ofples 40abf8b
Moved combo processing lower down in process logic
ofples File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
#include "process_combo.h" | ||
#include "print.h" | ||
|
||
|
||
#define COMBO_TIMER_ELAPSED -1 | ||
|
||
|
||
__attribute__ ((weak)) | ||
combo_t key_combos[] = { | ||
|
||
}; | ||
|
||
__attribute__ ((weak)) | ||
void process_combo_event(uint8_t combo_index, bool pressed) { | ||
|
||
} | ||
|
||
static uint8_t current_combo_index = 0; | ||
|
||
static inline void send_combo(uint16_t action, bool pressed) | ||
{ | ||
if (action) { | ||
if (pressed) { | ||
register_code16(action); | ||
} else { | ||
unregister_code16(action); | ||
} | ||
} else { | ||
process_combo_event(current_combo_index, pressed); | ||
} | ||
} | ||
|
||
#define ALL_COMBO_KEYS_ARE_DOWN (((1<<count)-1) == combo->state) | ||
#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state) | ||
#define KEY_STATE_DOWN(key) do{ combo->state |= (1<<key); } while(0) | ||
#define KEY_STATE_UP(key) do{ combo->state &= ~(1<<key); } while(0) | ||
static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) | ||
{ | ||
uint8_t count = 0; | ||
uint8_t index = -1; | ||
/* Find index of keycode and number of combo keys */ | ||
for (const uint16_t *keys = combo->keys; ;++count) { | ||
uint16_t key = pgm_read_word(&keys[count]); | ||
if (keycode == key) index = count; | ||
if (COMBO_END == key) break; | ||
} | ||
|
||
/* Return if not a combo key */ | ||
if (-1 == (int8_t)index) return false; | ||
|
||
/* The combos timer is used to signal whether the combo is active */ | ||
bool is_combo_active = COMBO_TIMER_ELAPSED == combo->timer ? false : true; | ||
|
||
if (record->event.pressed) { | ||
KEY_STATE_DOWN(index); | ||
|
||
if (is_combo_active) { | ||
if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */ | ||
send_combo(combo->keycode, true); | ||
combo->timer = COMBO_TIMER_ELAPSED; | ||
} else { /* Combo key was pressed */ | ||
combo->timer = timer_read(); | ||
#ifdef COMBO_ALLOW_ACTION_KEYS | ||
combo->prev_record = *record; | ||
#else | ||
combo->prev_key = keycode; | ||
#endif | ||
} | ||
} | ||
} else { | ||
if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */ | ||
send_combo(combo->keycode, false); | ||
} | ||
|
||
if (is_combo_active) { /* Combo key was tapped */ | ||
#ifdef COMBO_ALLOW_ACTION_KEYS | ||
record->event.pressed = true; | ||
process_action(record, store_or_get_action(record->event.pressed, record->event.key)); | ||
record->event.pressed = false; | ||
process_action(record, store_or_get_action(record->event.pressed, record->event.key)); | ||
#else | ||
register_code16(keycode); | ||
send_keyboard_report(); | ||
unregister_code16(keycode); | ||
#endif | ||
combo->timer = 0; | ||
} | ||
|
||
KEY_STATE_UP(index); | ||
} | ||
|
||
if (NO_COMBO_KEYS_ARE_DOWN) { | ||
combo->timer = 0; | ||
} | ||
|
||
return is_combo_active; | ||
} | ||
|
||
bool process_combo(uint16_t keycode, keyrecord_t *record) | ||
{ | ||
bool is_combo_key = false; | ||
|
||
for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) { | ||
combo_t *combo = &key_combos[current_combo_index]; | ||
is_combo_key |= process_single_combo(combo, keycode, record); | ||
} | ||
|
||
return !is_combo_key; | ||
} | ||
|
||
void matrix_scan_combo(void) | ||
{ | ||
for (int i = 0; i < COMBO_COUNT; ++i) { | ||
combo_t *combo = &key_combos[i]; | ||
if (combo->timer && | ||
combo->timer != COMBO_TIMER_ELAPSED && | ||
timer_elapsed(combo->timer) > COMBO_TERM) { | ||
|
||
/* This disables the combo, meaning key events for this | ||
* combo will be handled by the next processors in the chain | ||
*/ | ||
combo->timer = COMBO_TIMER_ELAPSED; | ||
|
||
#ifdef COMBO_ALLOW_ACTION_KEYS | ||
process_action(&combo->prev_record, | ||
store_or_get_action(combo->prev_record.event.pressed, | ||
combo->prev_record.event.key)); | ||
#else | ||
unregister_code16(combo->prev_key); | ||
register_code16(combo->prev_key); | ||
#endif | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#ifndef PROCESS_COMBO_H | ||
#define PROCESS_COMBO_H | ||
|
||
#include <stdint.h> | ||
#include "progmem.h" | ||
#include "quantum.h" | ||
|
||
typedef struct | ||
{ | ||
const uint16_t *keys; | ||
uint16_t keycode; | ||
#ifdef EXTRA_EXTRA_LONG_COMBOS | ||
uint32_t state; | ||
#elif EXTRA_LONG_COMBOS | ||
uint16_t state; | ||
#else | ||
uint8_t state; | ||
#endif | ||
uint16_t timer; | ||
#ifdef COMBO_ALLOW_ACTION_KEYS | ||
keyrecord_t prev_record; | ||
#else | ||
uint16_t prev_key; | ||
#endif | ||
} combo_t; | ||
|
||
|
||
#define COMBO(ck, ca) {.keys = &(ck)[0], .keycode = (ca)} | ||
#define COMBO_ACTION(ck) {.keys = &(ck)[0]} | ||
|
||
#define COMBO_END 0 | ||
#ifndef COMBO_COUNT | ||
#define COMBO_COUNT 0 | ||
#endif | ||
#ifndef COMBO_TERM | ||
#define COMBO_TERM TAPPING_TERM | ||
#endif | ||
|
||
bool process_combo(uint16_t keycode, keyrecord_t *record); | ||
void matrix_scan_combo(void); | ||
void process_combo_event(uint8_t combo_index, bool pressed); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this causes the issue #1370, reported by @dunkarooftop.
The code just blindly sends the keypress on release. The fix does not appear to be easy though, it would requre something similar to what are done for the actions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey @fredizzimo, sorry for rezzing an old thread but can you explain this a bit more? I'm developing a key profile for the gherkin that relies heavily on combos: http://www.keyboard-layout-editor.com/#/gists/3d8f9946bd7833f0e9938b63977443d9 and I'm starting to notice the out of order keys. When you say 'it would require something similar to what are done for the actions', do you mean directly above or in some other file somewhere? I'm new to this codebase, and it's been a while since I've done C, but my keymap requires this feature, so if it's in my power I'd love to fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually that wasn't too crazy, I've got a proof of concept working with a global register. I'd love input around properly re-triggering records and their lifecycle but maybe that's more appropriately resolved in Gitter