tmk / tmk_keyboard

Keyboard firmwares for Atmel AVR and Cortex-M
3.98k stars 1.7k forks source link

Toggle layer with tap key #244

Closed alexanderjeurissen closed 9 years ago

alexanderjeurissen commented 9 years ago

I would like to have a tap key that toggles a layer whenever pressed down long enough.

the concrete FN keys That I have in my keymap:

    [9] =  ACTION_MODS_KEY(MOD_LSFT|MOD_LGUI, KC_BSLS), // FN9 = SHIFT + CMD + Backslash 
    [10] = ACTION_MODS_KEY(MOD_LGUI|MOD_LALT, KC_D), // FN10 = CMD + ALT + D 
    [11] = ACTION_LAYER_TAP_KEY(1, KC_7),                            // toggle layer 1, tap KC_7
    [12] = ACTION_LAYER_TAP_KEY(2, KC_FN9),                      // toggle layer 2, tap KC_FN9
    [13] = ACTION_LAYER_TAP_KEY(3, KC_FN10),                    // toggle layer 3, tap KC_FN10

problem being that the KC_FN keys are never triggered on tap, and that the ACTION_LAYER_TAP_KEY is momentarily switching layers not toggeling.

I know the later is by design and I assume that tap keys only work with regular KC and not with FN keys

I was wondering if I could achieve my preferred behaviour by writing a custom macro. I'm not really experienced with C so if you could give me some pointers / help me on my way that would be much appreciated. or maybe I'm missing something obvious and my use case can be achieved in a easier way.

Thanks in advance for your help and for making this awesome project.

P.S. I'm using the Ergodox fork of this project by @cub-uanic so it's possible I'm not up to date on some features.

alexanderjeurissen commented 9 years ago

After seeing some examples of custom functions in the HHKB subdirectory, I came up with the following code. I tested the sections that are now commented out with another KC and the Tap behaviour is working. However I'm not sure how to activate or toggle layers inside a custom function.

    // Toggle Layer 1 on hold, and KC_7 on tap
    //
    if (id == LAYER_1_TAP_7) {
      if (record->event.pressed) {
            if (record->tap.count > 0 && !record->tap.interrupted) {
                if (record->tap.interrupted) {
                    dprint("tap interrupted\n");
                    // Toggle layer 1
                }
            } else {
              // Toggle layer 1
            }
        } else {
            if (record->tap.count > 0 && !(record->tap.interrupted)) {
                // Send KC_7
                register_code(KC_7);
                unregister_code(KC_7);
                send_keyboard_report();
                record->tap.count = 0;  // ad hoc: cancel tap
            } else {
              // Not sure if this else clause is necesary
            }
        }
    }
alexanderjeurissen commented 9 years ago

I looked into the source of action_layer.c and found that you expose a layer_invert function which I can use. So that fixes one of my use cases, I assume the other use cases are just as easy only requiring additional register_code and register_mods sequences.

my custom function for toggle layer 1 with KC_7 as tap:

// Toggle Layer 1 on hold, and KC_7 on tap
    //
    if (id == LAYER_1_TAP_7) {
      if (record->event.pressed) {
            if (record->tap.count > 0 && !record->tap.interrupted) {
                if (record->tap.interrupted) {
                    dprint("tap interrupted\n");
                    // Toggle layer 1
                    layer_invert(1);
                }
            } else {
              // Toggle layer 1
              layer_invert(1);
            }
        } else {
            if (record->tap.count > 0 && !(record->tap.interrupted)) {
                // Send KC_7
                register_code(KC_7);
                unregister_code(KC_7);
                send_keyboard_report();
                record->tap.count = 0;  // ad hoc: cancel tap
            } 
        }
    }

this works because the keycap is TRNS on all layers but layer 0.

alexanderjeurissen commented 9 years ago

ok getting the hang of it. below some code in case people stumble upon this issue and want to implement something similar.


    // Toggle Layer 2 on hold, and  SHIFT + CMD + Backslash on tap
    //
    if (id == LAYER_2_TAP_TABS) {
      if (record->event.pressed) {
            if (record->tap.count > 0 && !record->tap.interrupted) {
                if (record->tap.interrupted) {
                    dprint("tap interrupted\n");
                    // Toggle layer 2
                    layer_invert(2);
                }
            } else {
              // Toggle layer 2
              layer_invert(2);
            }
        } else {
            if (record->tap.count > 0 && !(record->tap.interrupted)) {
                // Send MOD_LSHIFT + MOD_LGUI + KC_BSLS
                register_mods(MOD_BIT(KC_LSHIFT));
                register_mods(MOD_BIT(KC_LGUI));
                register_code(KC_BSLS);
                unregister_code(KC_BSLS);
                send_keyboard_report();
                unregister_mods(MOD_BIT(KC_LSHIFT));
                unregister_mods(MOD_BIT(KC_LGUI));
                record->tap.count = 0;  // ad hoc: cancel tap
            }
        }
    }

This stuff is awesome !!