Closed dkarter closed 8 years ago
Solved by @Eric-L-T in this gist: https://gist.github.com/Eric-L-T/09fd1423106119c65aa3
case 0:
if (record->event.pressed) {
if (record->tap.count <= 1) {
register_code(KC_SCLN);
} else if (record->tap.count = 2) {
register_code(KC_LSFT);
register_code(KC_SCLN);
unregister_code(KC_SCLN);
unregister_code(KC_LSFT);
record->tap.count = 0;
}
} else if (record->tap.count <= 1) {
unregister_code(KC_SCLN);
record->tap.count = 0;
}
break;
Thanks!!!
Actually it did not work, that was Karabiner on my machine that made it "work" - reopening.
The tap.count
is not being incremented at all. Stays at 0.
I have set the TAPPING_TERM
to 300, mapped the key using M(1)
and tested using the following:
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
{
// MACRODOWN only works in this function
switch(id) {
case 0:
/* ... */
break;
case 1:
print("macro pressed \n");
if (record->event.pressed) {
print("taps: ");
pdec(record->tap.count);
print("\n");
if (record->tap.count == 1) {
register_code(KC_SCLN);
} else if (record->tap.count == 2) {
register_code(KC_COLN);
}
} else {
if (record->tap.count == 1) {
unregister_code(KC_SCLN);
} else if (record->tap.count == 2) {
unregister_code(KC_COLN);
}
}
break;
}
return MACRO_NONE;
};
I am getting back:
print("macro pressed \n");
taps: 0
print("macro pressed \n");
Any ideas what I'm doing wrong?
@jackhumbert / @ezuk ?
I'm not sure how tap.count is implemented, but this might be easier with a timer and a initialiser variable. The timer would be necessary to make sure the semicolon isn't sent every time.
Also, register_code(KC_COLN);
is incorrect - you can't use the special keycodes with register_code
. You'd need to send the shift key like in your first example.
As Jack said this will never work as first tap of your double tap will be identified as tap.count==1 A timer is definitively needed here, get a read on press and another one on depress, then it's just a matter of adjusting the threshold for taps to your liking.
Oh and you need the timer interrupt in loop to check for single tap timeout on fast single tap when no second tap is done but I'm not sure it will allow to register well when taping other keys then, the following code kinda works but do not register fast single tap (note that it handles layer momentary switch on hold as well) :
void action_mods_tap_layer_tmp(keyrecord_t *record, uint8_t layer, uint8_t mod_tap, uint8_t key_tap, uint8_t two_tap)
{
timer_init(); // needed to take prescaler value into account
static uint32_t start;
static uint32_t epress;
static uint32_t dpress;
if (record->event.pressed) {
start = timer_read32();
if (record->tap.count > 0) {
if (record->tap.interrupted) {
record->tap.count = 0; // ad hoc: cancel tap
layer_on(layer);
debug("interrupted layer on\n");
} else {
epress = timer_elapsed(start);
dprintf("epress:(%u)\n",epress);
}
} else {
layer_on(layer);
debug("layer on\n");
}
} else {
if (record->tap.count > 0) {
dpress = timer_elapsed32(start);
dprintf("dpress:(%u)\n",dpress);
if (record->tap.count >= 2) {
register_code(two_tap);
debug("two taps\n");
unregister_code(two_tap);
record->tap.count = 0; // ad hoc: cancel tap
} else {
if (epress - dpress > 0) {
debug("one tap\n");
dprintf("elapsed:(%u)\n",timer_elapsed32(start));
if (mod_tap > 0) {
add_weak_mods(MOD_BIT(mod_tap));
send_keyboard_report();
}
register_code(key_tap);
if (mod_tap > 0) {
del_weak_mods(MOD_BIT(mod_tap));
send_keyboard_report();
}
unregister_code(key_tap);
record->tap.count = 0; // ad hoc: cancel tap
}
}
} else {
layer_off(layer);
}
}
}
The timer approach was the correct one, it worked out great! thank you all for helping out!!!
Here's the my solution:
static uint16_t key_timer;
static uint16_t semi_colon_taps = 0;
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
{
// MACRODOWN only works in this function
switch(id) {
case 0:
/* ... */
break;
case 1:
if (record->event.pressed) {
// key pressed -> immidiately send ;, start timer
// count time elapsed since last key pressed
// if bigger than 200ms -> reset timer
// if smaller than 200ms -> send backspace and :, reset timer
if (!key_timer || timer_elapsed(key_timer) > 200) {
semi_colon_taps = 1;
} else {
semi_colon_taps += 1;
}
key_timer = timer_read();
if (semi_colon_taps == 1) {
register_code(KC_SCLN);
} else {
// delete semicolon
register_code(KC_BSPC);
unregister_code(KC_BSPC);
// insert colon
register_code(KC_LSFT);
register_code(KC_SCLN);
unregister_code(KC_SCLN);
unregister_code(KC_LSFT);
}
} else {
unregister_code(KC_SCLN);
}
break;
}
return MACRO_NONE;
};
I also implemented sticky shift using a similar idea, if you are interested: https://github.com/dkarter/qmk_firmware/blob/master/keyboard/ergodox_ez/keymaps/doriank_osx/keymap.c
Just wondering, do you have a layout in keymaps/ that uses this? Would love to see this in a pull request and documented :)
On 7 February 2016 at 20:03, Dorian Karter notifications@github.com wrote:
The timer approach was the correct one, it worked out great! thank you all for helping out!!!
Here's the my solution:
static uint16_t key_timer;static uint16_t semi_colon_taps = 0; const macro_t _action_get_macro(keyrecord_t record, uint8_t id, uint8t opt) { // MACRODOWN only works in this function switch(id) { case 0: / ... / break; case 1: if (record->event.pressed) { // key pressed -> immidiately send ;, start timer // count time elapsed since last key pressed // if bigger than 200ms -> reset timer // if smaller than 200ms -> send backspace and :, reset timer
if (!key_timer || timer_elapsed(key_timer) > 200) { semi_colon_taps = 1; } else { semi_colon_taps += 1; } key_timer = timer_read(); if (semi_colon_taps == 1) { register_code(KC_SCLN); } else { // delete semicolon register_code(KC_BSPC); unregister_code(KC_BSPC); // insert colon register_code(KC_LSFT); register_code(KC_SCLN); unregister_code(KC_SCLN); unregister_code(KC_LSFT); } } else { unregister_code(KC_SCLN); } break;
} return MACRO_NONE; };
— Reply to this email directly or view it on GitHub https://github.com/jackhumbert/qmk_firmware/issues/127#issuecomment-181149975 .
Problem with that solution (which is how Karabiner handles single tap noise by adding a backspace output) is that you can't use keys for inputing sequences in software ie if software is waiting for 3-d and you send 3-x-backspace-d, it won't make it.
I want to be able to double tap the semicolon button and send a colon but have it behave normally when I press it once, is that possible? What would be the easiest way to do that?
Single press -> semicolon Double tap -> colon Shift + Single press -> colon
I'm currently doing that with Karabiner on Mac but would like to have it programmed into the keyboard so that I can take it with me.