tmk / tmk_keyboard

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

Is it possible to override modifier behaviours? #233

Closed kaansoral closed 9 years ago

kaansoral commented 9 years ago

For example: Convert Shift+Esc to a pure ~ Convert Alt+I to F13+delay+I+delay+F13 Convert Ctrl+[ to F12 while keeping Ctrl+C as Ctrl+C etc.

While also keeping the regular Shift/Alt/Ctrl behaviours

This requires momentarily sending keyup's and sending keydown's after the combo is completed, I'm not sure whether tmk_keyboard supports this, if it does, I'm probably going to switch from the infinity firmware to tmk_keyboard

(I've read the docs, but couldn't find anything specific)

Thanks in Advance

tmk commented 9 years ago

I think it is possible if you write C code in keymap file. Yes, not documented. You'll have to read code. In short they are not supported :)

kaansoral commented 9 years ago

Thanks for the swift reply, it seems I will have to dive deeper to achieve what I want :)

tmk commented 9 years ago

I tried to write code for the first and third of your example. This will not be exactly what you want but it will be useful as starting point.

This uses functions from common/action.h and action_util.h, which are unfortunately not documented well and not designed elaborately.

#include "keymap_common.h"
#include "wait.h"

const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] PROGMEM = {
    /* Layer 0: Default Layer
     * ,-----------------------------------------------------------.
     * |Esc|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|  \|  `|
     * |-----------------------------------------------------------|
     * |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|Backs|
     * |-----------------------------------------------------------|
     * |Contro|  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|Enter   |
     * |-----------------------------------------------------------|
     * |Shift   |  Z|  X|  C|  V|  B|  N|  M|  ,|  .|  /|Shift |Fn0|
     * `-----------------------------------------------------------'
     *       |Alt|Gui  |         Space         |Gui  |Alt|
     *       `-------------------------------------------'
     */
    KEYMAP(ESC, 1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, BSLS,GRV,   \
           TAB, Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   FN3, RBRC,BSPC,       \
           LCTL,A,   S,   D,   F,   G,   H,   J,   K,   L,   SCLN,QUOT,ENT,             \
           FN1, Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, SLSH,FN2, FN0,             \
                LALT,LGUI,          SPC,                RGUI,RALT),
    /* Layer 1: HHKB mode (HHKB Fn)
     * ,-----------------------------------------------------------.
     * |Pwr| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|Ins|Del|
     * |-----------------------------------------------------------|
     * |Caps |   |   |   |   |   |   |   |Psc|Slk|Pus|Up |   |Backs|
     * |-----------------------------------------------------------|
     * |      |VoD|VoU|Mut|   |   |  *|  /|Hom|PgU|Lef|Rig|Enter   |
     * |-----------------------------------------------------------|
     * |        |   |   |   |   |   |  +|  -|End|PgD|Dow|      |   |
     * `-----------------------------------------------------------'
     *       |   |     |                       |     |   |
     *       `-------------------------------------------'
     */
    [1] = \
    KEYMAP(PWR, F1,  F2,  F3,  F4,  F5,  F6,  F7,  F8,  F9,  F10, F11, F12, INS, DEL,   \
           CAPS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,PSCR,SLCK,PAUS, UP, TRNS, BSPC,      \
           TRNS,VOLD,VOLU,MUTE,TRNS,TRNS,PAST,PSLS,HOME,PGUP,LEFT,RGHT,PENT,            \
           TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,PPLS,PMNS,END, PGDN,DOWN,TRNS,TRNS,            \
                TRNS,TRNS,          TRNS,               TRNS,TRNS),
    [2] = \
    KEYMAP(GRV, TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,  \
           TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,       \
           TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,            \
           TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,            \
                TRNS,TRNS,          TRNS,               TRNS,TRNS),
};

enum function_id {
    LCTL_LBRACKET,  // Ctrl+[
};

const uint16_t fn_actions[] PROGMEM = {
    [0]  = ACTION_LAYER_MOMENTARY(1),
    [1]  = ACTION_LAYER_MODS(2, MOD_LSFT),
    [2]  = ACTION_LAYER_MODS(2, MOD_LSFT),       // RShift cannot be accepted
    [3]  = ACTION_FUNCTION(LCTL_LBRACKET),

};

void action_function(keyrecord_t *record, uint8_t id, uint8_t opt)
{
    static bool lctl_lbracket = false;
    switch (id) {
        case LCTL_LBRACKET:
            if (record->event.pressed) {
                if (keyboard_report->mods & MOD_BIT(KC_LCTL)) {
                    lctl_lbracket = true;
                    unregister_code(KC_LCTL);
                    wait_ms(1);
                    register_code(KC_F12);
                    wait_ms(1);
                    unregister_code(KC_F12);
                    wait_ms(1);
                    register_code(KC_LCTL);
                } else {
                    register_code(KC_LBRACKET);
                }
            } else {
                if (lctl_lbracket) {
                    lctl_lbracket = false;
                } else {
                    unregister_code(KC_LBRACKET);
                }
            }
            break;
    }
}
kaansoral commented 9 years ago

Thanks a lot, it indeed looks like a good starting point

One question, I'm guessing things are synchronous and the keyboard is unusable in that 3ms waited, is that correct?

For example, is it possible to wait 2000ms, type some stuff, and after 2000ms have those things registered

I'm asking question to understand whether output is directly tied to the input, or whether the output has it's own queue

tmk commented 9 years ago

Yes, during execution of action_function() keyboard is not responsive, namely it doesn't scan switch matrix. You may want to do things there as quickly as possible.

TMK has no queue or suchlike and keyboard is blocked for 2000ms when using wait_ms(2000).

kaansoral commented 9 years ago

I understand

Off topic, have you ever considered separating the input and output logic to potentially make the keyboards more robust, the keyboard could register and process input while the output could progress at it's own pace, so similar to a scan loop, there could be an output loop

Initially, without any added feature or fancy stuff, it should be simple to do and it shouldn't affect the existing expected behaviour of the firmware but rather remove the bottlenecks

In this scenario, wait_ms would just make the output routine skip until the deadline has passed and during that duration the keypress state wouldn't change but the input/scan logic would continue to function

tmk commented 9 years ago

I don't have plan to implement framework like such, but interesting and useful in some situation.

kaansoral commented 9 years ago

Thanks so much for sharing this method, it's a bit tedious to override each key with a function, but at least it enables me to do what I want

include "action_util.h" was needed for keyboard_report by the way

-- Can it be ensured that this routine is triggered only for one modifier by the way, like Ctrl+I but not Alt+Ctrl+I or Shift+Ctrl+I -- It would have been simpler if the modifier could have been extended instead