Skip to content
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 5 commits into from
Feb 7, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Refactored as well as added support for action keys in combos
  • Loading branch information
ofples committed Dec 16, 2016
commit 6e7cfa83b9424061914793b02757fa4ec75b356b
123 changes: 70 additions & 53 deletions quantum/process_keycode/process_combo.c
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
#include "process_combo.h"
#include "print.h"

#define SEND_KEY(key) \
do { \
register_code16(key); \
send_keyboard_report(); \
unregister_code16(key); \
} while(0)

#define COMBO_TIMER_ELAPSED -1

#if COMBO_TERM
#define IS_COMBO_KEY_HELD(combo) (COMBO_TIMER_ELAPSED == combo->timer ? false : true)
#define RESET_COMBO_TIMER_AND_KEY(combo) combo->timer = 0; combo->key = 0
#else
#define IS_COMBO_KEY_HELD(combo) (true)
#define RESET_COMBO_TIMER_AND_KEY(combo) do {} while (0)
#endif


__attribute__ ((weak))
combo_t key_combos[COMBO_COUNT] = {
combo_t key_combos[] = {

};

static inline void reset_combo(combo_t *combo)
__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)
{
combo->state = 0;
RESET_COMBO_TIMER_AND_KEY(combo);
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)
#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;
Expand All @@ -46,42 +46,51 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
}

/* Return if not a combo key */
if (-1 == index) return false;
if (-1 == (int8_t)index) return false;

bool is_combo_active = IS_COMBO_KEY_HELD(combo);
/* 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 COMBO_TERM

if (is_combo_active) {
combo->timer = timer_read();
combo->key = keycode;
}
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 (is_combo_active && combo->state) { /* Combo key was tapped */
RESET_COMBO_TIMER_AND_KEY(combo);
SEND_KEY(keycode);
if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */
send_combo(combo->keycode, false);
}

#if COMBO_TERM
if (!is_combo_active && keycode == combo->key) { /* Held combo key was released */
unregister_code16(combo->key);
}
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
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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

combo->timer = 0;
}

KEY_STATE_UP(index);
KEY_STATE_UP(index);
}

if (ALL_COMBO_KEYS_ARE_DOWN && is_combo_active) {
SEND_KEY(combo->action);
reset_combo(combo);
}

if(NO_COMBO_KEYS_ARE_DOWN && !is_combo_active) {
reset_combo(combo);
if (NO_COMBO_KEYS_ARE_DOWN) {
combo->timer = 0;
}

return is_combo_active;
Expand All @@ -91,8 +100,8 @@ bool process_combo(uint16_t keycode, keyrecord_t *record)
{
bool is_combo_key = false;

for (int i = 0; i < COMBO_COUNT; ++i) {
combo_t *combo = &key_combos[i];
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);
}

Expand All @@ -101,17 +110,25 @@ bool process_combo(uint16_t keycode, keyrecord_t *record)

void matrix_scan_combo(void)
{
#if COMBO_TERM
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;
unregister_code16(combo->key);
register_code16(combo->key);

#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
}
}
#endif
}
}
34 changes: 19 additions & 15 deletions quantum/process_keycode/process_combo.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,39 @@
#include "progmem.h"
#include "quantum.h"

#ifndef COMBO_TERM
#define COMBO_TERM TAPPING_TERM
#endif

typedef struct
{
const uint16_t *keys;
uint16_t action;
uint16_t keycode;
#ifdef EXTRA_EXTRA_LONG_COMBOS
uint32_t state;
#if COMBO_TERM
#elif EXTRA_LONG_COMBOS
uint16_t state;
#else
uint8_t state;
#endif
uint16_t timer;
uint16_t key;
#ifdef COMBO_ALLOW_ACTION_KEYS
keyrecord_t prev_record;
#else
uint16_t prev_key;
#endif
} combo_t;


#if COMBO_TERM
#define COMBO(ck, ca) {.keys = &(ck)[0], .action = (ca), .state = 0, .timer = 0, .key = 0}
#else
#define COMBO(ck, ca) {.keys = &(ck)[0], .action = (ca), .state = 0 }
#endif
#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

extern combo_t key_combos[COMBO_COUNT];
#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
#endif