koktoh の雑記帳

気ままに書いていきます

【不定期連載】QMK 探検隊 -その6-

はじめに

これは、「C言語とか組み込みなんもわからん」男が QMK というジャングルを探検していく、ノンフィクションドキュメンタリーである……
なお、更新は亀の歩みである……

前回のおさらい

koktoh.hatenablog.com

quantum.h の include を順番に探検していたら、 keymap.h もいっぱい include されていたので、順番に探検していたら、 action.c もいっぱい include されていたので、順番に見ていくことにした

探検開始

続きから探検していこう

  1. keycode.h
  2. quantum_keycodes.h
  3. keycode_config.h
  4. debug.h
  5. report.h
  6. host.h
  7. action_macro.h
  8. action.h

action.c

github.com

ソース全体

/*
Copyright 2012,2013 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "host.h"
#include "keycode.h"
#include "keyboard.h"
#include "mousekey.h"
#include "command.h"
#include "led.h"
#include "action_layer.h"
#include "action_tapping.h"
#include "action_macro.h"
#include "action_util.h"
#include "action.h"
#include "wait.h"

#ifdef BACKLIGHT_ENABLE
#    include "backlight.h"
#endif

#ifdef DEBUG_ACTION
#    include "debug.h"
#else
#    include "nodebug.h"
#endif

#ifdef POINTING_DEVICE_ENABLE
#    include "pointing_device.h"
#endif

int tp_buttons;

#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
int retro_tapping_counter = 0;
#endif

#ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
__attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; }
#endif

#ifdef RETRO_TAPPING_PER_KEY
__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; }
#endif

#ifndef TAP_CODE_DELAY
#    define TAP_CODE_DELAY 0
#endif
#ifndef TAP_HOLD_CAPS_DELAY
#    define TAP_HOLD_CAPS_DELAY 80
#endif
/** \brief Called to execute an action.
 *
 * FIXME: Needs documentation.
 */
void action_exec(keyevent_t event) {
    if (!IS_NOEVENT(event)) {
        dprint("\n---- action_exec: start -----\n");
        dprint("EVENT: ");
        debug_event(event);
        dprintln();
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
        retro_tapping_counter++;
#endif
    }

    if (event.pressed) {
        // clear the potential weak mods left by previously pressed keys
        clear_weak_mods();
    }

#ifdef SWAP_HANDS_ENABLE
    if (!IS_NOEVENT(event)) {
        process_hand_swap(&event);
    }
#endif

    keyrecord_t record = {.event = event};

#ifndef NO_ACTION_ONESHOT
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
    if (has_oneshot_layer_timed_out()) {
        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
    }
    if (has_oneshot_mods_timed_out()) {
        clear_oneshot_mods();
    }
#        ifdef SWAP_HANDS_ENABLE
    if (has_oneshot_swaphands_timed_out()) {
        clear_oneshot_swaphands();
    }
#        endif
#    endif
#endif

#ifndef NO_ACTION_TAPPING
    action_tapping_process(record);
#else
    process_record(&record);
    if (!IS_NOEVENT(record.event)) {
        dprint("processed: ");
        debug_record(record);
        dprintln();
    }
#endif
}

#ifdef SWAP_HANDS_ENABLE
bool swap_hands = false;
bool swap_held  = false;

/** \brief Process Hand Swap
 *
 * FIXME: Needs documentation.
 */
void process_hand_swap(keyevent_t *event) {
    static swap_state_row_t swap_state[MATRIX_ROWS];

    keypos_t         pos     = event->key;
    swap_state_row_t col_bit = (swap_state_row_t)1 << pos.col;
    bool             do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit);

    if (do_swap) {
        event->key = hand_swap_config[pos.row][pos.col];
        swap_state[pos.row] |= col_bit;
    } else {
        swap_state[pos.row] &= ~(col_bit);
    }
}
#endif

#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
bool disable_action_cache = false;

void process_record_nocache(keyrecord_t *record) {
    disable_action_cache = true;
    process_record(record);
    disable_action_cache = false;
}
#else
void process_record_nocache(keyrecord_t *record) { process_record(record); }
#endif

__attribute__((weak)) bool process_record_quantum(keyrecord_t *record) { return true; }

__attribute__((weak)) void post_process_record_quantum(keyrecord_t *record) {}

#ifndef NO_ACTION_TAPPING
/** \brief Allows for handling tap-hold actions immediately instead of waiting for TAPPING_TERM or another keypress.
 *
 * FIXME: Needs documentation.
 */
void process_record_tap_hint(keyrecord_t *record) {
    action_t action = layer_switch_get_action(record->event.key);

    switch (action.kind.id) {
#    ifdef SWAP_HANDS_ENABLE
        case ACT_SWAP_HANDS:
            switch (action.swap.code) {
                case OP_SH_ONESHOT:
                    break;
                case OP_SH_TAP_TOGGLE:
                default:
                    swap_hands = !swap_hands;
                    swap_held  = true;
            }
            break;
#    endif
    }
}
#endif

/** \brief Take a key event (key press or key release) and processes it.
 *
 * FIXME: Needs documentation.
 */
void process_record(keyrecord_t *record) {
    if (IS_NOEVENT(record->event)) {
        return;
    }

    if (!process_record_quantum(record)) {
#ifndef NO_ACTION_ONESHOT
        if (is_oneshot_layer_active() && record->event.pressed) {
            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
        }
#endif
        return;
    }

    process_record_handler(record);
    post_process_record_quantum(record);
}

void process_record_handler(keyrecord_t *record) {
    action_t action = store_or_get_action(record->event.pressed, record->event.key);
    dprint("ACTION: ");
    debug_action(action);
#ifndef NO_ACTION_LAYER
    dprint(" layer_state: ");
    layer_debug();
    dprint(" default_layer_state: ");
    default_layer_debug();
#endif
    dprintln();

    process_action(record, action);
}

#if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE)
void register_button(bool pressed, enum mouse_buttons button) {
#    ifdef PS2_MOUSE_ENABLE
    tp_buttons = pressed ? tp_buttons | button : tp_buttons & ~button;
#    endif
#    ifdef POINTING_DEVICE_ENABLE
    report_mouse_t currentReport = pointing_device_get_report();
    currentReport.buttons        = pressed ? currentReport.buttons | button : currentReport.buttons & ~button;
    pointing_device_set_report(currentReport);
#    endif
}
#endif

/** \brief Take an action and processes it.
 *
 * FIXME: Needs documentation.
 */
void process_action(keyrecord_t *record, action_t action) {
    keyevent_t event = record->event;
#ifndef NO_ACTION_TAPPING
    uint8_t tap_count = record->tap.count;
#endif

#ifndef NO_ACTION_ONESHOT
    bool do_release_oneshot = false;
    // notice we only clear the one shot layer if the pressed key is not a modifier.
    if (is_oneshot_layer_active() && event.pressed && (action.kind.id == ACT_USAGE || !IS_MOD(action.key.code))
#    ifdef SWAP_HANDS_ENABLE
        && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)
#    endif
    ) {
        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
        do_release_oneshot = !is_oneshot_layer_active();
    }
#endif

    switch (action.kind.id) {
        /* Key and Mods */
        case ACT_LMODS:
        case ACT_RMODS: {
            uint8_t mods = (action.kind.id == ACT_LMODS) ? action.key.mods : action.key.mods << 4;
            if (event.pressed) {
                if (mods) {
                    if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
                        // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
                        // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT).
                        // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).
                        add_mods(mods);
                    } else {
                        add_weak_mods(mods);
                    }
                    send_keyboard_report();
                }
                register_code(action.key.code);
            } else {
                unregister_code(action.key.code);
                if (mods) {
                    if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
                        del_mods(mods);
                    } else {
                        del_weak_mods(mods);
                    }
                    send_keyboard_report();
                }
            }
        } break;
#ifndef NO_ACTION_TAPPING
        case ACT_LMODS_TAP:
        case ACT_RMODS_TAP: {
            uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ? action.key.mods : action.key.mods << 4;
            switch (action.layer_tap.code) {
#    ifndef NO_ACTION_ONESHOT
                case MODS_ONESHOT:
                    // Oneshot modifier
                    if (event.pressed) {
                        if (tap_count == 0) {
                            dprint("MODS_TAP: Oneshot: 0\n");
                            register_mods(mods | get_oneshot_mods());
                        } else if (tap_count == 1) {
                            dprint("MODS_TAP: Oneshot: start\n");
                            set_oneshot_mods(mods | get_oneshot_mods());
#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
                        } else if (tap_count == ONESHOT_TAP_TOGGLE) {
                            dprint("MODS_TAP: Toggling oneshot");
                            clear_oneshot_mods();
                            set_oneshot_locked_mods(mods);
                            register_mods(mods);
#        endif
                        } else {
                            register_mods(mods | get_oneshot_mods());
                        }
                    } else {
                        if (tap_count == 0) {
                            clear_oneshot_mods();
                            unregister_mods(mods);
                        } else if (tap_count == 1) {
                            // Retain Oneshot mods
#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
                            if (mods & get_mods()) {
                                clear_oneshot_locked_mods();
                                clear_oneshot_mods();
                                unregister_mods(mods);
                            }
                        } else if (tap_count == ONESHOT_TAP_TOGGLE) {
                            // Toggle Oneshot Layer
#        endif
                        } else {
                            clear_oneshot_mods();
                            unregister_mods(mods);
                        }
                    }
                    break;
#    endif
                case MODS_TAP_TOGGLE:
                    if (event.pressed) {
                        if (tap_count <= TAPPING_TOGGLE) {
                            register_mods(mods);
                        }
                    } else {
                        if (tap_count < TAPPING_TOGGLE) {
                            unregister_mods(mods);
                        }
                    }
                    break;
                default:
                    if (event.pressed) {
                        if (tap_count > 0) {
#    if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
                            if (
#        ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
                                !get_ignore_mod_tap_interrupt(get_event_keycode(record->event, false), record) &&
#        endif
                                record->tap.interrupted) {
                                dprint("mods_tap: tap: cancel: add_mods\n");
                                // ad hoc: set 0 to cancel tap
                                record->tap.count = 0;
                                register_mods(mods);
                            } else
#    endif
                            {
                                dprint("MODS_TAP: Tap: register_code\n");
                                register_code(action.key.code);
                            }
                        } else {
                            dprint("MODS_TAP: No tap: add_mods\n");
                            register_mods(mods);
                        }
                    } else {
                        if (tap_count > 0) {
                            dprint("MODS_TAP: Tap: unregister_code\n");
                            if (action.layer_tap.code == KC_CAPS) {
                                wait_ms(TAP_HOLD_CAPS_DELAY);
                            } else {
                                wait_ms(TAP_CODE_DELAY);
                            }
                            unregister_code(action.key.code);
                        } else {
                            dprint("MODS_TAP: No tap: add_mods\n");
                            unregister_mods(mods);
                        }
                    }
                    break;
            }
        } break;
#endif
#ifdef EXTRAKEY_ENABLE
        /* other HID usage */
        case ACT_USAGE:
            switch (action.usage.page) {
                case PAGE_SYSTEM:
                    if (event.pressed) {
                        host_system_send(action.usage.code);
                    } else {
                        host_system_send(0);
                    }
                    break;
                case PAGE_CONSUMER:
                    if (event.pressed) {
                        host_consumer_send(action.usage.code);
                    } else {
                        host_consumer_send(0);
                    }
                    break;
            }
            break;
#endif
#ifdef MOUSEKEY_ENABLE
        /* Mouse key */
        case ACT_MOUSEKEY:
            if (event.pressed) {
                mousekey_on(action.key.code);
            } else {
                mousekey_off(action.key.code);
            }
            switch (action.key.code) {
#    if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE)
#        ifdef POINTING_DEVICE_ENABLE
                case KC_MS_BTN1 ... KC_MS_BTN8:
#        else
                case KC_MS_BTN1 ... KC_MS_BTN3:
#        endif
                    register_button(event.pressed, MOUSE_BTN_MASK(action.key.code - KC_MS_BTN1));
                    break;
#    endif
                default:
                    mousekey_send();
                    break;
            }
            break;
#endif
#ifndef NO_ACTION_LAYER
        case ACT_LAYER:
            if (action.layer_bitop.on == 0) {
                /* Default Layer Bitwise Operation */
                if (!event.pressed) {
                    uint8_t       shift = action.layer_bitop.part * 4;
                    layer_state_t bits  = ((layer_state_t)action.layer_bitop.bits) << shift;
                    layer_state_t mask  = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0;
                    switch (action.layer_bitop.op) {
                        case OP_BIT_AND:
                            default_layer_and(bits | mask);
                            break;
                        case OP_BIT_OR:
                            default_layer_or(bits | mask);
                            break;
                        case OP_BIT_XOR:
                            default_layer_xor(bits | mask);
                            break;
                        case OP_BIT_SET:
                            default_layer_set(bits | mask);
                            break;
                    }
                }
            } else {
                /* Layer Bitwise Operation */
                if (event.pressed ? (action.layer_bitop.on & ON_PRESS) : (action.layer_bitop.on & ON_RELEASE)) {
                    uint8_t       shift = action.layer_bitop.part * 4;
                    layer_state_t bits  = ((layer_state_t)action.layer_bitop.bits) << shift;
                    layer_state_t mask  = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0;
                    switch (action.layer_bitop.op) {
                        case OP_BIT_AND:
                            layer_and(bits | mask);
                            break;
                        case OP_BIT_OR:
                            layer_or(bits | mask);
                            break;
                        case OP_BIT_XOR:
                            layer_xor(bits | mask);
                            break;
                        case OP_BIT_SET:
                            layer_state_set(bits | mask);
                            break;
                    }
                }
            }
            break;
        case ACT_LAYER_MODS:
            if (event.pressed) {
                layer_on(action.layer_mods.layer);
                register_mods(action.layer_mods.mods);
            } else {
                unregister_mods(action.layer_mods.mods);
                layer_off(action.layer_mods.layer);
            }
            break;
#    ifndef NO_ACTION_TAPPING
        case ACT_LAYER_TAP:
        case ACT_LAYER_TAP_EXT:
            switch (action.layer_tap.code) {
                case OP_TAP_TOGGLE:
                    /* tap toggle */
                    if (event.pressed) {
                        if (tap_count < TAPPING_TOGGLE) {
                            layer_invert(action.layer_tap.val);
                        }
                    } else {
                        if (tap_count <= TAPPING_TOGGLE) {
                            layer_invert(action.layer_tap.val);
                        }
                    }
                    break;
                case OP_ON_OFF:
                    event.pressed ? layer_on(action.layer_tap.val) : layer_off(action.layer_tap.val);
                    break;
                case OP_OFF_ON:
                    event.pressed ? layer_off(action.layer_tap.val) : layer_on(action.layer_tap.val);
                    break;
                case OP_SET_CLEAR:
                    event.pressed ? layer_move(action.layer_tap.val) : layer_clear();
                    break;
#        ifndef NO_ACTION_ONESHOT
                case OP_ONESHOT:
                    // Oneshot modifier
#            if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
                    do_release_oneshot = false;
                    if (event.pressed) {
                        del_mods(get_oneshot_locked_mods());
                        if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
                            reset_oneshot_layer();
                            layer_off(action.layer_tap.val);
                            break;
                        } else if (tap_count < ONESHOT_TAP_TOGGLE) {
                            layer_on(action.layer_tap.val);
                            set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
                        }
                    } else {
                        add_mods(get_oneshot_locked_mods());
                        if (tap_count >= ONESHOT_TAP_TOGGLE) {
                            reset_oneshot_layer();
                            clear_oneshot_locked_mods();
                            set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
                        } else {
                            clear_oneshot_layer_state(ONESHOT_PRESSED);
                        }
                    }
#            else
                    if (event.pressed) {
                        layer_on(action.layer_tap.val);
                        set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
                    } else {
                        clear_oneshot_layer_state(ONESHOT_PRESSED);
                        if (tap_count > 1) {
                            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
                        }
                    }
#            endif
                    break;
#        endif
                default:
                    /* tap key */
                    if (event.pressed) {
                        if (tap_count > 0) {
                            dprint("KEYMAP_TAP_KEY: Tap: register_code\n");
                            register_code(action.layer_tap.code);
                        } else {
                            dprint("KEYMAP_TAP_KEY: No tap: On on press\n");
                            layer_on(action.layer_tap.val);
                        }
                    } else {
                        if (tap_count > 0) {
                            dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n");
                            if (action.layer_tap.code == KC_CAPS) {
                                wait_ms(TAP_HOLD_CAPS_DELAY);
                            } else {
                                wait_ms(TAP_CODE_DELAY);
                            }
                            unregister_code(action.layer_tap.code);
                        } else {
                            dprint("KEYMAP_TAP_KEY: No tap: Off on release\n");
                            layer_off(action.layer_tap.val);
                        }
                    }
                    break;
            }
            break;
#    endif
#endif
            /* Extentions */
#ifndef NO_ACTION_MACRO
        case ACT_MACRO:
            action_macro_play(action_get_macro(record, action.func.id, action.func.opt));
            break;
#endif
#ifdef SWAP_HANDS_ENABLE
        case ACT_SWAP_HANDS:
            switch (action.swap.code) {
                case OP_SH_TOGGLE:
                    if (event.pressed) {
                        swap_hands = !swap_hands;
                    }
                    break;
                case OP_SH_ON_OFF:
                    swap_hands = event.pressed;
                    break;
                case OP_SH_OFF_ON:
                    swap_hands = !event.pressed;
                    break;
                case OP_SH_ON:
                    if (!event.pressed) {
                        swap_hands = true;
                    }
                    break;
                case OP_SH_OFF:
                    if (!event.pressed) {
                        swap_hands = false;
                    }
                    break;
#    ifndef NO_ACTION_ONESHOT
                case OP_SH_ONESHOT:
                    if (event.pressed) {
                        set_oneshot_swaphands();
                    } else {
                        release_oneshot_swaphands();
                    }
                    break;
#    endif

#    ifndef NO_ACTION_TAPPING
                case OP_SH_TAP_TOGGLE:
                    /* tap toggle */

                    if (event.pressed) {
                        if (swap_held) {
                            swap_held = false;
                        } else {
                            swap_hands = !swap_hands;
                        }
                    } else {
                        if (tap_count < TAPPING_TOGGLE) {
                            swap_hands = !swap_hands;
                        }
                    }
                    break;
                default:
                    /* tap key */
                    if (tap_count > 0) {
                        if (swap_held) {
                            swap_hands = !swap_hands;  // undo hold set up in _tap_hint
                            swap_held  = false;
                        }
                        if (event.pressed) {
                            register_code(action.swap.code);
                        } else {
                            wait_ms(TAP_CODE_DELAY);
                            unregister_code(action.swap.code);
                            *record = (keyrecord_t){};  // hack: reset tap mode
                        }
                    } else {
                        if (swap_held && !event.pressed) {
                            swap_hands = !swap_hands;  // undo hold set up in _tap_hint
                            swap_held  = false;
                        }
                    }
#    endif
            }
#endif
#ifndef NO_ACTION_FUNCTION
        case ACT_FUNCTION:
            action_function(record, action.func.id, action.func.opt);
            break;
#endif
        default:
            break;
    }

#ifndef NO_ACTION_LAYER
    // if this event is a layer action, update the leds
    switch (action.kind.id) {
        case ACT_LAYER:
        case ACT_LAYER_MODS:
#    ifndef NO_ACTION_TAPPING
        case ACT_LAYER_TAP:
        case ACT_LAYER_TAP_EXT:
#    endif
            led_set(host_keyboard_leds());
            break;
        default:
            break;
    }
#endif

#ifndef NO_ACTION_TAPPING
#    if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
    if (!is_tap_action(action)) {
        retro_tapping_counter = 0;
    } else {
        if (event.pressed) {
            if (tap_count > 0) {
                retro_tapping_counter = 0;
            }
        } else {
            if (tap_count > 0) {
                retro_tapping_counter = 0;
            } else {
                if (
#        ifdef RETRO_TAPPING_PER_KEY
                    get_retro_tapping(get_event_keycode(record->event, false), record) &&
#        endif
                    retro_tapping_counter == 2) {
                    tap_code(action.layer_tap.code);
                }
                retro_tapping_counter = 0;
            }
        }
    }
#    endif
#endif

#ifdef SWAP_HANDS_ENABLE
#    ifndef NO_ACTION_ONESHOT
    if (event.pressed && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)) {
        use_oneshot_swaphands();
    }
#    endif
#endif

#ifndef NO_ACTION_ONESHOT
    /* Because we switch layers after a oneshot event, we need to release the
     * key before we leave the layer or no key up event will be generated.
     */
    if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED)) {
        record->event.pressed = false;
        layer_on(get_oneshot_layer());
        process_record(record);
        layer_off(get_oneshot_layer());
    }
#endif
}

/** \brief Utilities for actions. (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
void register_code(uint8_t code) {
    if (code == KC_NO) {
        return;
    }
#ifdef LOCKING_SUPPORT_ENABLE
    else if (KC_LOCKING_CAPS == code) {
#    ifdef LOCKING_RESYNC_ENABLE
        // Resync: ignore if caps lock already is on
        if (host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK)) return;
#    endif
        add_key(KC_CAPSLOCK);
        send_keyboard_report();
        wait_ms(100);
        del_key(KC_CAPSLOCK);
        send_keyboard_report();
    }

    else if (KC_LOCKING_NUM == code) {
#    ifdef LOCKING_RESYNC_ENABLE
        if (host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)) return;
#    endif
        add_key(KC_NUMLOCK);
        send_keyboard_report();
        wait_ms(100);
        del_key(KC_NUMLOCK);
        send_keyboard_report();
    }

    else if (KC_LOCKING_SCROLL == code) {
#    ifdef LOCKING_RESYNC_ENABLE
        if (host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK)) return;
#    endif
        add_key(KC_SCROLLLOCK);
        send_keyboard_report();
        wait_ms(100);
        del_key(KC_SCROLLLOCK);
        send_keyboard_report();
    }
#endif

    else if
        IS_KEY(code) {
            // TODO: should push command_proc out of this block?
            if (command_proc(code)) return;

#ifndef NO_ACTION_ONESHOT
/* TODO: remove
        if (oneshot_state.mods && !oneshot_state.disabled) {
            uint8_t tmp_mods = get_mods();
            add_mods(oneshot_state.mods);

            add_key(code);
            send_keyboard_report();

            set_mods(tmp_mods);
            send_keyboard_report();
            oneshot_cancel();
        } else
*/
#endif
            {
                // Force a new key press if the key is already pressed
                // without this, keys with the same keycode, but different
                // modifiers will be reported incorrectly, see issue #1708
                if (is_key_pressed(keyboard_report, code)) {
                    del_key(code);
                    send_keyboard_report();
                }
                add_key(code);
                send_keyboard_report();
            }
        }
    else if
        IS_MOD(code) {
            add_mods(MOD_BIT(code));
            send_keyboard_report();
        }
#ifdef EXTRAKEY_ENABLE
    else if
        IS_SYSTEM(code) { host_system_send(KEYCODE2SYSTEM(code)); }
    else if
        IS_CONSUMER(code) { host_consumer_send(KEYCODE2CONSUMER(code)); }
#endif
#ifdef MOUSEKEY_ENABLE
    else if
        IS_MOUSEKEY(code) {
            mousekey_on(code);
            mousekey_send();
        }
#endif
}

/** \brief Utilities for actions. (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
void unregister_code(uint8_t code) {
    if (code == KC_NO) {
        return;
    }
#ifdef LOCKING_SUPPORT_ENABLE
    else if (KC_LOCKING_CAPS == code) {
#    ifdef LOCKING_RESYNC_ENABLE
        // Resync: ignore if caps lock already is off
        if (!(host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK))) return;
#    endif
        add_key(KC_CAPSLOCK);
        send_keyboard_report();
        del_key(KC_CAPSLOCK);
        send_keyboard_report();
    }

    else if (KC_LOCKING_NUM == code) {
#    ifdef LOCKING_RESYNC_ENABLE
        if (!(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) return;
#    endif
        add_key(KC_NUMLOCK);
        send_keyboard_report();
        del_key(KC_NUMLOCK);
        send_keyboard_report();
    }

    else if (KC_LOCKING_SCROLL == code) {
#    ifdef LOCKING_RESYNC_ENABLE
        if (!(host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK))) return;
#    endif
        add_key(KC_SCROLLLOCK);
        send_keyboard_report();
        del_key(KC_SCROLLLOCK);
        send_keyboard_report();
    }
#endif

    else if
        IS_KEY(code) {
            del_key(code);
            send_keyboard_report();
        }
    else if
        IS_MOD(code) {
            del_mods(MOD_BIT(code));
            send_keyboard_report();
        }
    else if
        IS_SYSTEM(code) { host_system_send(0); }
    else if
        IS_CONSUMER(code) { host_consumer_send(0); }
#ifdef MOUSEKEY_ENABLE
    else if
        IS_MOUSEKEY(code) {
            mousekey_off(code);
            mousekey_send();
        }
#endif
}

/** \brief Tap a keycode with a delay.
 *
 * \param code The basic keycode to tap.
 * \param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it.
 */
void tap_code_delay(uint8_t code, uint16_t delay) {
    register_code(code);
    for (uint16_t i = delay; i > 0; i--) {
        wait_ms(1);
    }
    unregister_code(code);
}

/** \brief Tap a keycode with the default delay.
 *
 * \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
 */
void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }

/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately.
 *
 * \param mods A bitfield of modifiers to register.
 */
void register_mods(uint8_t mods) {
    if (mods) {
        add_mods(mods);
        send_keyboard_report();
    }
}

/** \brief Removes the given physically pressed modifiers and sends a keyboard report immediately.
 *
 * \param mods A bitfield of modifiers to unregister.
 */
void unregister_mods(uint8_t mods) {
    if (mods) {
        del_mods(mods);
        send_keyboard_report();
    }
}

/** \brief Adds the given weak modifiers and sends a keyboard report immediately.
 *
 * \param mods A bitfield of modifiers to register.
 */
void register_weak_mods(uint8_t mods) {
    if (mods) {
        add_weak_mods(mods);
        send_keyboard_report();
    }
}

/** \brief Removes the given weak modifiers and sends a keyboard report immediately.
 *
 * \param mods A bitfield of modifiers to unregister.
 */
void unregister_weak_mods(uint8_t mods) {
    if (mods) {
        del_weak_mods(mods);
        send_keyboard_report();
    }
}

/** \brief Utilities for actions. (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
void clear_keyboard(void) {
    clear_mods();
    clear_keyboard_but_mods();
}

/** \brief Utilities for actions. (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
void clear_keyboard_but_mods(void) {
    clear_keys();
    clear_keyboard_but_mods_and_keys();
}

/** \brief Utilities for actions. (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
void clear_keyboard_but_mods_and_keys() {
#ifdef EXTRAKEY_ENABLE
    host_system_send(0);
    host_consumer_send(0);
#endif
    clear_weak_mods();
    clear_macro_mods();
    send_keyboard_report();
#ifdef MOUSEKEY_ENABLE
    mousekey_clear();
    mousekey_send();
#endif
}

/** \brief Utilities for actions. (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
bool is_tap_key(keypos_t key) {
    action_t action = layer_switch_get_action(key);
    return is_tap_action(action);
}

/** \brief Utilities for actions. (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
bool is_tap_action(action_t action) {
    switch (action.kind.id) {
        case ACT_LMODS_TAP:
        case ACT_RMODS_TAP:
        case ACT_LAYER_TAP:
        case ACT_LAYER_TAP_EXT:
            switch (action.layer_tap.code) {
                case KC_NO ... KC_RGUI:
                case OP_TAP_TOGGLE:
                case OP_ONESHOT:
                    return true;
            }
            return false;
        case ACT_SWAP_HANDS:
            switch (action.swap.code) {
                case KC_NO ... KC_RGUI:
                case OP_SH_TAP_TOGGLE:
                    return true;
            }
            return false;
        case ACT_MACRO:
        case ACT_FUNCTION:
            if (action.func.opt & FUNC_TAP) {
                return true;
            }
            return false;
    }
    return false;
}

/** \brief Debug print (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
void debug_event(keyevent_t event) { dprintf("%04X%c(%u)", (event.key.row << 8 | event.key.col), (event.pressed ? 'd' : 'u'), event.time); }
/** \brief Debug print (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
void debug_record(keyrecord_t record) {
    debug_event(record.event);
#ifndef NO_ACTION_TAPPING
    dprintf(":%u%c", record.tap.count, (record.tap.interrupted ? '-' : ' '));
#endif
}

/** \brief Debug print (FIXME: Needs better description)
 *
 * FIXME: Needs documentation.
 */
void debug_action(action_t action) {
    switch (action.kind.id) {
        case ACT_LMODS:
            dprint("ACT_LMODS");
            break;
        case ACT_RMODS:
            dprint("ACT_RMODS");
            break;
        case ACT_LMODS_TAP:
            dprint("ACT_LMODS_TAP");
            break;
        case ACT_RMODS_TAP:
            dprint("ACT_RMODS_TAP");
            break;
        case ACT_USAGE:
            dprint("ACT_USAGE");
            break;
        case ACT_MOUSEKEY:
            dprint("ACT_MOUSEKEY");
            break;
        case ACT_LAYER:
            dprint("ACT_LAYER");
            break;
        case ACT_LAYER_MODS:
            dprint("ACT_LAYER_MODS");
            break;
        case ACT_LAYER_TAP:
            dprint("ACT_LAYER_TAP");
            break;
        case ACT_LAYER_TAP_EXT:
            dprint("ACT_LAYER_TAP_EXT");
            break;
        case ACT_MACRO:
            dprint("ACT_MACRO");
            break;
        case ACT_FUNCTION:
            dprint("ACT_FUNCTION");
            break;
        case ACT_SWAP_HANDS:
            dprint("ACT_SWAP_HANDS");
            break;
        default:
            dprint("UNKNOWN");
            break;
    }
    dprintf("[%X:%02X]", action.kind.param >> 8, action.kind.param & 0xff);
}

新たな include がいくつかある

  • mousekey.h
  • command.h
  • action_layer.h
  • action_tapping.h
  • action_util.h

順番に見ていこう

mousekey.h

github.com

ソース全体

/*
Copyright 2011 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <stdint.h>
#include "host.h"

#ifndef MK_3_SPEED

/* max value on report descriptor */
#    ifndef MOUSEKEY_MOVE_MAX
#        define MOUSEKEY_MOVE_MAX 127
#    elif MOUSEKEY_MOVE_MAX > 127
#        error MOUSEKEY_MOVE_MAX needs to be smaller than 127
#    endif

#    ifndef MOUSEKEY_WHEEL_MAX
#        define MOUSEKEY_WHEEL_MAX 127
#    elif MOUSEKEY_WHEEL_MAX > 127
#        error MOUSEKEY_WHEEL_MAX needs to be smaller than 127
#    endif

#    ifndef MOUSEKEY_MOVE_DELTA
#        ifndef MK_KINETIC_SPEED
#            define MOUSEKEY_MOVE_DELTA 5
#        else
#            define MOUSEKEY_MOVE_DELTA 25
#        endif
#    endif
#    ifndef MOUSEKEY_WHEEL_DELTA
#        define MOUSEKEY_WHEEL_DELTA 1
#    endif
#    ifndef MOUSEKEY_DELAY
#        ifndef MK_KINETIC_SPEED
#            define MOUSEKEY_DELAY 300
#        else
#            define MOUSEKEY_DELAY 8
#        endif
#    endif
#    ifndef MOUSEKEY_INTERVAL
#        ifndef MK_KINETIC_SPEED
#            define MOUSEKEY_INTERVAL 50
#        else
#            define MOUSEKEY_INTERVAL 8
#        endif
#    endif
#    ifndef MOUSEKEY_MAX_SPEED
#        define MOUSEKEY_MAX_SPEED 10
#    endif
#    ifndef MOUSEKEY_TIME_TO_MAX
#        define MOUSEKEY_TIME_TO_MAX 20
#    endif
#    ifndef MOUSEKEY_WHEEL_DELAY
#        define MOUSEKEY_WHEEL_DELAY 300
#    endif
#    ifndef MOUSEKEY_WHEEL_INTERVAL
#        define MOUSEKEY_WHEEL_INTERVAL 100
#    endif
#    ifndef MOUSEKEY_WHEEL_MAX_SPEED
#        define MOUSEKEY_WHEEL_MAX_SPEED 8
#    endif
#    ifndef MOUSEKEY_WHEEL_TIME_TO_MAX
#        define MOUSEKEY_WHEEL_TIME_TO_MAX 40
#    endif

#    ifndef MOUSEKEY_INITIAL_SPEED
#        define MOUSEKEY_INITIAL_SPEED 100
#    endif
#    ifndef MOUSEKEY_BASE_SPEED
#        define MOUSEKEY_BASE_SPEED 1000
#    endif
#    ifndef MOUSEKEY_DECELERATED_SPEED
#        define MOUSEKEY_DECELERATED_SPEED 400
#    endif
#    ifndef MOUSEKEY_ACCELERATED_SPEED
#        define MOUSEKEY_ACCELERATED_SPEED 3000
#    endif
#    ifndef MOUSEKEY_WHEEL_INITIAL_MOVEMENTS
#        define MOUSEKEY_WHEEL_INITIAL_MOVEMENTS 16
#    endif
#    ifndef MOUSEKEY_WHEEL_BASE_MOVEMENTS
#        define MOUSEKEY_WHEEL_BASE_MOVEMENTS 32
#    endif
#    ifndef MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS
#        define MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS 48
#    endif
#    ifndef MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS
#        define MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS 8
#    endif

#else /* #ifndef MK_3_SPEED */

#    ifndef MK_C_OFFSET_UNMOD
#        define MK_C_OFFSET_UNMOD 16
#    endif
#    ifndef MK_C_INTERVAL_UNMOD
#        define MK_C_INTERVAL_UNMOD 16
#    endif
#    ifndef MK_C_OFFSET_0
#        define MK_C_OFFSET_0 1
#    endif
#    ifndef MK_C_INTERVAL_0
#        define MK_C_INTERVAL_0 32
#    endif
#    ifndef MK_C_OFFSET_1
#        define MK_C_OFFSET_1 4
#    endif
#    ifndef MK_C_INTERVAL_1
#        define MK_C_INTERVAL_1 16
#    endif
#    ifndef MK_C_OFFSET_2
#        define MK_C_OFFSET_2 32
#    endif
#    ifndef MK_C_INTERVAL_2
#        define MK_C_INTERVAL_2 16
#    endif

#    ifndef MK_W_OFFSET_UNMOD
#        define MK_W_OFFSET_UNMOD 1
#    endif
#    ifndef MK_W_INTERVAL_UNMOD
#        define MK_W_INTERVAL_UNMOD 40
#    endif
#    ifndef MK_W_OFFSET_0
#        define MK_W_OFFSET_0 1
#    endif
#    ifndef MK_W_INTERVAL_0
#        define MK_W_INTERVAL_0 360
#    endif
#    ifndef MK_W_OFFSET_1
#        define MK_W_OFFSET_1 1
#    endif
#    ifndef MK_W_INTERVAL_1
#        define MK_W_INTERVAL_1 120
#    endif
#    ifndef MK_W_OFFSET_2
#        define MK_W_OFFSET_2 1
#    endif
#    ifndef MK_W_INTERVAL_2
#        define MK_W_INTERVAL_2 20
#    endif

#endif /* #ifndef MK_3_SPEED */

#ifdef __cplusplus
extern "C" {
#endif

extern uint8_t mk_delay;
extern uint8_t mk_interval;
extern uint8_t mk_max_speed;
extern uint8_t mk_time_to_max;
extern uint8_t mk_wheel_max_speed;
extern uint8_t mk_wheel_time_to_max;

void mousekey_task(void);
void mousekey_on(uint8_t code);
void mousekey_off(uint8_t code);
void mousekey_clear(void);
void mousekey_send(void);

#ifdef __cplusplus
}
#endif

~158行

マウスに関する設定の初期化処理である
各設定にの意味などはここが詳しい

docs.qmk.fm

~最終行

あとは関数のプロトタイプ宣言である

mousekey.c

github.com

ソース全体

/*
 * Copyright 2011 Jun Wako <wakojun@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdint.h>
#include "keycode.h"
#include "host.h"
#include "timer.h"
#include "print.h"
#include "debug.h"
#include "mousekey.h"

inline int8_t times_inv_sqrt2(int8_t x) {
    // 181/256 is pretty close to 1/sqrt(2)
    // 0.70703125                 0.707106781
    // 1 too small for x=99 and x=198
    // This ends up being a mult and discard lower 8 bits
    return (x * 181) >> 8;
}

static report_mouse_t mouse_report = {0};
static void           mousekey_debug(void);
static uint8_t        mousekey_accel        = 0;
static uint8_t        mousekey_repeat       = 0;
static uint8_t        mousekey_wheel_repeat = 0;
#ifdef MK_KINETIC_SPEED
static uint16_t mouse_timer = 0;
#endif

#ifndef MK_3_SPEED

static uint16_t last_timer_c = 0;
static uint16_t last_timer_w = 0;

/*
 * Mouse keys acceleration algorithm
 *  http://en.wikipedia.org/wiki/Mouse_keys
 *
 *  speed = delta * max_speed * (repeat / time_to_max)**((1000+curve)/1000)
 */
/* milliseconds between the initial key press and first repeated motion event (0-2550) */
uint8_t mk_delay = MOUSEKEY_DELAY / 10;
/* milliseconds between repeated motion events (0-255) */
uint8_t mk_interval = MOUSEKEY_INTERVAL;
/* steady speed (in action_delta units) applied each event (0-255) */
uint8_t mk_max_speed = MOUSEKEY_MAX_SPEED;
/* number of events (count) accelerating to steady speed (0-255) */
uint8_t mk_time_to_max = MOUSEKEY_TIME_TO_MAX;
/* ramp used to reach maximum pointer speed (NOT SUPPORTED) */
// int8_t mk_curve = 0;
/* wheel params */
/* milliseconds between the initial key press and first repeated motion event (0-2550) */
uint8_t mk_wheel_delay = MOUSEKEY_WHEEL_DELAY / 10;
/* milliseconds between repeated motion events (0-255) */
uint8_t mk_wheel_interval    = MOUSEKEY_WHEEL_INTERVAL;
uint8_t mk_wheel_max_speed   = MOUSEKEY_WHEEL_MAX_SPEED;
uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;

#    ifndef MK_COMBINED

static uint8_t move_unit(void) {
    uint16_t unit;
    if (mousekey_accel & (1 << 0)) {
        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 4;
    } else if (mousekey_accel & (1 << 1)) {
        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;
    } else if (mousekey_accel & (1 << 2)) {
        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed);
    } else if (mousekey_repeat == 0) {
        unit = MOUSEKEY_MOVE_DELTA;
    } else if (mousekey_repeat >= mk_time_to_max) {
        unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;
    } else {
        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;
    }
    return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));
}

static uint8_t wheel_unit(void) {
    uint16_t unit;
    if (mousekey_accel & (1 << 0)) {
        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 4;
    } else if (mousekey_accel & (1 << 1)) {
        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2;
    } else if (mousekey_accel & (1 << 2)) {
        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed);
    } else if (mousekey_wheel_repeat == 0) {
        unit = MOUSEKEY_WHEEL_DELTA;
    } else if (mousekey_wheel_repeat >= mk_wheel_time_to_max) {
        unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed;
    } else {
        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_wheel_repeat) / mk_wheel_time_to_max;
    }
    return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));
}

#    else /* #ifndef MK_COMBINED */
#        ifdef MK_KINETIC_SPEED

/*
 * Kinetic movement  acceleration algorithm
 *
 *  current speed = I + A * T/50 + A * 0.5 * T^2 | maximum B
 *
 * T: time since the mouse movement started
 * E: mouse events per second (set through MOUSEKEY_INTERVAL, UHK sends 250, the
 *    pro micro on my Signum 3.0 sends only 125!)
 * I: initial speed at time 0
 * A: acceleration
 * B: base mouse travel speed
 */
const uint16_t mk_accelerated_speed = MOUSEKEY_ACCELERATED_SPEED;
const uint16_t mk_base_speed        = MOUSEKEY_BASE_SPEED;
const uint16_t mk_decelerated_speed = MOUSEKEY_DECELERATED_SPEED;
const uint16_t mk_initial_speed     = MOUSEKEY_INITIAL_SPEED;

static uint8_t move_unit(void) {
    float speed = mk_initial_speed;

    if (mousekey_accel & ((1 << 0) | (1 << 2))) {
        speed = mousekey_accel & (1 << 2) ? mk_accelerated_speed : mk_decelerated_speed;
    } else if (mousekey_repeat && mouse_timer) {
        const float time_elapsed = timer_elapsed(mouse_timer) / 50;
        speed                    = mk_initial_speed + MOUSEKEY_MOVE_DELTA * time_elapsed + MOUSEKEY_MOVE_DELTA * 0.5 * time_elapsed * time_elapsed;

        speed = speed > mk_base_speed ? mk_base_speed : speed;
    }

    /* convert speed to USB mouse speed 1 to 127 */
    speed = (uint8_t)(speed / (1000.0f / mk_interval));
    speed = speed < 1 ? 1 : speed;

    return speed > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : speed;
}

float mk_wheel_interval = 1000.0f / MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;

static uint8_t wheel_unit(void) {
    float speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;

    if (mousekey_accel & ((1 << 0) | (1 << 2))) {
        speed = mousekey_accel & (1 << 2) ? MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS : MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS;
    } else if (mousekey_repeat && mouse_timer) {
        if (mk_wheel_interval != MOUSEKEY_WHEEL_BASE_MOVEMENTS) {
            const float time_elapsed = timer_elapsed(mouse_timer) / 50;
            speed                    = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS + 1 * time_elapsed + 1 * 0.5 * time_elapsed * time_elapsed;
        }
        speed = speed > MOUSEKEY_WHEEL_BASE_MOVEMENTS ? MOUSEKEY_WHEEL_BASE_MOVEMENTS : speed;
    }

    mk_wheel_interval = 1000.0f / speed;

    return 1;
}

#        else /* #ifndef MK_KINETIC_SPEED */

static uint8_t move_unit(void) {
    uint16_t unit;
    if (mousekey_accel & (1 << 0)) {
        unit = 1;
    } else if (mousekey_accel & (1 << 1)) {
        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;
    } else if (mousekey_accel & (1 << 2)) {
        unit = MOUSEKEY_MOVE_MAX;
    } else if (mousekey_repeat == 0) {
        unit = MOUSEKEY_MOVE_DELTA;
    } else if (mousekey_repeat >= mk_time_to_max) {
        unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;
    } else {
        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;
    }
    return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));
}

static uint8_t wheel_unit(void) {
    uint16_t unit;
    if (mousekey_accel & (1 << 0)) {
        unit = 1;
    } else if (mousekey_accel & (1 << 1)) {
        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2;
    } else if (mousekey_accel & (1 << 2)) {
        unit = MOUSEKEY_WHEEL_MAX;
    } else if (mousekey_repeat == 0) {
        unit = MOUSEKEY_WHEEL_DELTA;
    } else if (mousekey_repeat >= mk_wheel_time_to_max) {
        unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed;
    } else {
        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_repeat) / mk_wheel_time_to_max;
    }
    return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));
}

#        endif /* #ifndef MK_KINETIC_SPEED */
#    endif     /* #ifndef MK_COMBINED */

void mousekey_task(void) {
    // report cursor and scroll movement independently
    report_mouse_t const tmpmr = mouse_report;

    mouse_report.x = 0;
    mouse_report.y = 0;
    mouse_report.v = 0;
    mouse_report.h = 0;

    if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > (mousekey_repeat ? mk_interval : mk_delay * 10)) {
        if (mousekey_repeat != UINT8_MAX) mousekey_repeat++;
        if (tmpmr.x != 0) mouse_report.x = move_unit() * ((tmpmr.x > 0) ? 1 : -1);
        if (tmpmr.y != 0) mouse_report.y = move_unit() * ((tmpmr.y > 0) ? 1 : -1);

        /* diagonal move [1/sqrt(2)] */
        if (mouse_report.x && mouse_report.y) {
            mouse_report.x = times_inv_sqrt2(mouse_report.x);
            if (mouse_report.x == 0) {
                mouse_report.x = 1;
            }
            mouse_report.y = times_inv_sqrt2(mouse_report.y);
            if (mouse_report.y == 0) {
                mouse_report.y = 1;
            }
        }
    }
    if ((tmpmr.v || tmpmr.h) && timer_elapsed(last_timer_w) > (mousekey_wheel_repeat ? mk_wheel_interval : mk_wheel_delay * 10)) {
        if (mousekey_wheel_repeat != UINT8_MAX) mousekey_wheel_repeat++;
        if (tmpmr.v != 0) mouse_report.v = wheel_unit() * ((tmpmr.v > 0) ? 1 : -1);
        if (tmpmr.h != 0) mouse_report.h = wheel_unit() * ((tmpmr.h > 0) ? 1 : -1);

        /* diagonal move [1/sqrt(2)] */
        if (mouse_report.v && mouse_report.h) {
            mouse_report.v = times_inv_sqrt2(mouse_report.v);
            if (mouse_report.v == 0) {
                mouse_report.v = 1;
            }
            mouse_report.h = times_inv_sqrt2(mouse_report.h);
            if (mouse_report.h == 0) {
                mouse_report.h = 1;
            }
        }
    }

    if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send();
    mouse_report = tmpmr;
}

void mousekey_on(uint8_t code) {
#    ifdef MK_KINETIC_SPEED
    if (mouse_timer == 0) {
        mouse_timer = timer_read();
    }
#    endif /* #ifdef MK_KINETIC_SPEED */

    if (code == KC_MS_UP)
        mouse_report.y = move_unit() * -1;
    else if (code == KC_MS_DOWN)
        mouse_report.y = move_unit();
    else if (code == KC_MS_LEFT)
        mouse_report.x = move_unit() * -1;
    else if (code == KC_MS_RIGHT)
        mouse_report.x = move_unit();
    else if (code == KC_MS_WH_UP)
        mouse_report.v = wheel_unit();
    else if (code == KC_MS_WH_DOWN)
        mouse_report.v = wheel_unit() * -1;
    else if (code == KC_MS_WH_LEFT)
        mouse_report.h = wheel_unit() * -1;
    else if (code == KC_MS_WH_RIGHT)
        mouse_report.h = wheel_unit();
    else if (IS_MOUSEKEY_BUTTON(code))
        mouse_report.buttons |= 1 << (code - KC_MS_BTN1);
    else if (code == KC_MS_ACCEL0)
        mousekey_accel |= (1 << 0);
    else if (code == KC_MS_ACCEL1)
        mousekey_accel |= (1 << 1);
    else if (code == KC_MS_ACCEL2)
        mousekey_accel |= (1 << 2);
}

void mousekey_off(uint8_t code) {
    if (code == KC_MS_UP && mouse_report.y < 0)
        mouse_report.y = 0;
    else if (code == KC_MS_DOWN && mouse_report.y > 0)
        mouse_report.y = 0;
    else if (code == KC_MS_LEFT && mouse_report.x < 0)
        mouse_report.x = 0;
    else if (code == KC_MS_RIGHT && mouse_report.x > 0)
        mouse_report.x = 0;
    else if (code == KC_MS_WH_UP && mouse_report.v > 0)
        mouse_report.v = 0;
    else if (code == KC_MS_WH_DOWN && mouse_report.v < 0)
        mouse_report.v = 0;
    else if (code == KC_MS_WH_LEFT && mouse_report.h < 0)
        mouse_report.h = 0;
    else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0)
        mouse_report.h = 0;
    else if (IS_MOUSEKEY_BUTTON(code))
        mouse_report.buttons &= ~(1 << (code - KC_MS_BTN1));
    else if (code == KC_MS_ACCEL0)
        mousekey_accel &= ~(1 << 0);
    else if (code == KC_MS_ACCEL1)
        mousekey_accel &= ~(1 << 1);
    else if (code == KC_MS_ACCEL2)
        mousekey_accel &= ~(1 << 2);
    if (mouse_report.x == 0 && mouse_report.y == 0) {
        mousekey_repeat = 0;
#    ifdef MK_KINETIC_SPEED
        mouse_timer = 0;
#    endif /* #ifdef MK_KINETIC_SPEED */
    }
    if (mouse_report.v == 0 && mouse_report.h == 0) mousekey_wheel_repeat = 0;
}

#else /* #ifndef MK_3_SPEED */

enum { mkspd_unmod, mkspd_0, mkspd_1, mkspd_2, mkspd_COUNT };
#    ifndef MK_MOMENTARY_ACCEL
static uint8_t  mk_speed                 = mkspd_1;
#    else
static uint8_t mk_speed      = mkspd_unmod;
static uint8_t mkspd_DEFAULT = mkspd_unmod;
#    endif
static uint16_t last_timer_c             = 0;
static uint16_t last_timer_w             = 0;
uint16_t        c_offsets[mkspd_COUNT]   = {MK_C_OFFSET_UNMOD, MK_C_OFFSET_0, MK_C_OFFSET_1, MK_C_OFFSET_2};
uint16_t        c_intervals[mkspd_COUNT] = {MK_C_INTERVAL_UNMOD, MK_C_INTERVAL_0, MK_C_INTERVAL_1, MK_C_INTERVAL_2};
uint16_t        w_offsets[mkspd_COUNT]   = {MK_W_OFFSET_UNMOD, MK_W_OFFSET_0, MK_W_OFFSET_1, MK_W_OFFSET_2};
uint16_t        w_intervals[mkspd_COUNT] = {MK_W_INTERVAL_UNMOD, MK_W_INTERVAL_0, MK_W_INTERVAL_1, MK_W_INTERVAL_2};

void mousekey_task(void) {
    // report cursor and scroll movement independently
    report_mouse_t const tmpmr = mouse_report;
    mouse_report.x             = 0;
    mouse_report.y             = 0;
    mouse_report.v             = 0;
    mouse_report.h             = 0;

    if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > c_intervals[mk_speed]) {
        mouse_report.x = tmpmr.x;
        mouse_report.y = tmpmr.y;
    }
    if ((tmpmr.h || tmpmr.v) && timer_elapsed(last_timer_w) > w_intervals[mk_speed]) {
        mouse_report.v = tmpmr.v;
        mouse_report.h = tmpmr.h;
    }

    if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send();
    mouse_report = tmpmr;
}

void adjust_speed(void) {
    uint16_t const c_offset = c_offsets[mk_speed];
    uint16_t const w_offset = w_offsets[mk_speed];
    if (mouse_report.x > 0) mouse_report.x = c_offset;
    if (mouse_report.x < 0) mouse_report.x = c_offset * -1;
    if (mouse_report.y > 0) mouse_report.y = c_offset;
    if (mouse_report.y < 0) mouse_report.y = c_offset * -1;
    if (mouse_report.h > 0) mouse_report.h = w_offset;
    if (mouse_report.h < 0) mouse_report.h = w_offset * -1;
    if (mouse_report.v > 0) mouse_report.v = w_offset;
    if (mouse_report.v < 0) mouse_report.v = w_offset * -1;
    // adjust for diagonals
    if (mouse_report.x && mouse_report.y) {
        mouse_report.x = times_inv_sqrt2(mouse_report.x);
        if (mouse_report.x == 0) {
            mouse_report.x = 1;
        }
        mouse_report.y = times_inv_sqrt2(mouse_report.y);
        if (mouse_report.y == 0) {
            mouse_report.y = 1;
        }
    }
    if (mouse_report.h && mouse_report.v) {
        mouse_report.h = times_inv_sqrt2(mouse_report.h);
        mouse_report.v = times_inv_sqrt2(mouse_report.v);
    }
}

void mousekey_on(uint8_t code) {
    uint16_t const c_offset  = c_offsets[mk_speed];
    uint16_t const w_offset  = w_offsets[mk_speed];
    uint8_t const  old_speed = mk_speed;
    if (code == KC_MS_UP)
        mouse_report.y = c_offset * -1;
    else if (code == KC_MS_DOWN)
        mouse_report.y = c_offset;
    else if (code == KC_MS_LEFT)
        mouse_report.x = c_offset * -1;
    else if (code == KC_MS_RIGHT)
        mouse_report.x = c_offset;
    else if (code == KC_MS_WH_UP)
        mouse_report.v = w_offset;
    else if (code == KC_MS_WH_DOWN)
        mouse_report.v = w_offset * -1;
    else if (code == KC_MS_WH_LEFT)
        mouse_report.h = w_offset * -1;
    else if (code == KC_MS_WH_RIGHT)
        mouse_report.h = w_offset;
    else if (IS_MOUSEKEY_BUTTON(code))
        mouse_report.buttons |= 1 << (code - KC_MS_BTN1);
    else if (code == KC_MS_ACCEL0)
        mk_speed = mkspd_0;
    else if (code == KC_MS_ACCEL1)
        mk_speed = mkspd_1;
    else if (code == KC_MS_ACCEL2)
        mk_speed = mkspd_2;
    if (mk_speed != old_speed) adjust_speed();
}

void mousekey_off(uint8_t code) {
#    ifdef MK_MOMENTARY_ACCEL
    uint8_t const old_speed = mk_speed;
#    endif
    if (code == KC_MS_UP && mouse_report.y < 0)
        mouse_report.y = 0;
    else if (code == KC_MS_DOWN && mouse_report.y > 0)
        mouse_report.y = 0;
    else if (code == KC_MS_LEFT && mouse_report.x < 0)
        mouse_report.x = 0;
    else if (code == KC_MS_RIGHT && mouse_report.x > 0)
        mouse_report.x = 0;
    else if (code == KC_MS_WH_UP && mouse_report.v > 0)
        mouse_report.v = 0;
    else if (code == KC_MS_WH_DOWN && mouse_report.v < 0)
        mouse_report.v = 0;
    else if (code == KC_MS_WH_LEFT && mouse_report.h < 0)
        mouse_report.h = 0;
    else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0)
        mouse_report.h = 0;
    else if (IS_MOUSEKEY_BUTTON(code))
        mouse_report.buttons &= ~(1 << (code - KC_MS_BTN1));
#    ifdef MK_MOMENTARY_ACCEL
    else if (code == KC_MS_ACCEL0)
        mk_speed = mkspd_DEFAULT;
    else if (code == KC_MS_ACCEL1)
        mk_speed = mkspd_DEFAULT;
    else if (code == KC_MS_ACCEL2)
        mk_speed = mkspd_DEFAULT;
    if (mk_speed != old_speed) adjust_speed();
#    endif
}

#endif /* #ifndef MK_3_SPEED */

void mousekey_send(void) {
    mousekey_debug();
    uint16_t time = timer_read();
    if (mouse_report.x || mouse_report.y) last_timer_c = time;
    if (mouse_report.v || mouse_report.h) last_timer_w = time;
    host_mouse_send(&mouse_report);
}

void mousekey_clear(void) {
    mouse_report          = (report_mouse_t){};
    mousekey_repeat       = 0;
    mousekey_wheel_repeat = 0;
    mousekey_accel        = 0;
}

static void mousekey_debug(void) {
    if (!debug_mouse) return;
    print("mousekey [btn|x y v h](rep/acl): [");
    print_hex8(mouse_report.buttons);
    print("|");
    print_decs(mouse_report.x);
    print(" ");
    print_decs(mouse_report.y);
    print(" ");
    print_decs(mouse_report.v);
    print(" ");
    print_decs(mouse_report.h);
    print("](");
    print_dec(mousekey_repeat);
    print("/");
    print_dec(mousekey_accel);
    print(")\n");
}

~24行

include 宣言である
いくつか初めてのものがある

  • timer.h
  • print.h

~32行

計算を簡便にするための関数が定義されている

int8_t times_inv_sqrt2(int8_t x)

181/256 = 0.707031251/\sqrt{2} =0.707106781 が近似であることから、 x に 181 をかけ、 8bit 右シフトする(256で割る)ことで 1/\sqrt{2} を実現している
math.h を include する必要もなく、単純に (int8_t)(x * sqrt(2)) を行うよりも高速だろう

~70行

定数や変数の宣言である

~最終行

残りはマウスポインタの移動量の計算などである
加速度や、定速モードに切り替えたときの移動量の計算などが記述されている
特に詳しくは見る必要もないだろう

マウスキーについてはここが詳しい

docs.qmk.fm

timer.h

github.com

ソース全体

/*
Copyright 2011 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <stdint.h>
#include <stdbool.h>

#if defined(__AVR__)
#    include "avr/timer_avr.h"
#endif

#define TIMER_DIFF(a, b, max) ((max == UINT8_MAX) ? ((uint8_t)((a) - (b))) : ((max == UINT16_MAX) ? ((uint16_t)((a) - (b))) : ((max == UINT32_MAX) ? ((uint32_t)((a) - (b))) : ((a) >= (b) ? (a) - (b) : (max) + 1 - (b) + (a)))))
#define TIMER_DIFF_8(a, b) TIMER_DIFF(a, b, UINT8_MAX)
#define TIMER_DIFF_16(a, b) TIMER_DIFF(a, b, UINT16_MAX)
#define TIMER_DIFF_32(a, b) TIMER_DIFF(a, b, UINT32_MAX)
#define TIMER_DIFF_RAW(a, b) TIMER_DIFF_8(a, b)

#ifdef __cplusplus
extern "C" {
#endif

extern volatile uint32_t timer_count;

void     timer_init(void);
void     timer_clear(void);
uint16_t timer_read(void);
uint32_t timer_read32(void);
uint16_t timer_elapsed(uint16_t last);
uint32_t timer_elapsed32(uint32_t last);

// Utility functions to check if a future time has expired & autmatically handle time wrapping if checked / reset frequently (half of max value)
#define timer_expired(current, future) ((uint16_t)(current - future) < UINT16_MAX / 2)
#define timer_expired32(current, future) ((uint32_t)(current - future) < UINT32_MAX / 2)

#ifdef __cplusplus
}
#endif

タイマーを使うための関数のプロトタイプ宣言である
各 CPU などに合わせて関数がそれぞれ実装されている

ARM

github.com

AVR

github.com

ChibiOS

github.com

print.h

github.com

ソース全体

/* Copyright 2012 Jun Wako <wakojun@gmail.com> */
/* Very basic print functions, intended to be used with usb_debug_only.c
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2008 PJRC.COM, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "util.h"
#include "sendchar.h"
#include "progmem.h"

void print_set_sendchar(sendchar_func_t func);

#ifndef NO_PRINT
#    if __has_include_next("_print.h")
#        include_next "_print.h" /* Include the platforms print.h */
#    else
// Fall back to lib/printf
#        include "printf.h"  // lib/printf/printf.h

// Create user & normal print defines
#        define print(s) printf(s)
#        define println(s) printf(s "\r\n")
#        define xprintf printf
#        define uprint(s) printf(s)
#        define uprintln(s) printf(s "\r\n")
#        define uprintf printf

#    endif /* __AVR__ / PROTOCOL_CHIBIOS / PROTOCOL_ARM_ATSAM */
#else      /* NO_PRINT */
#    undef xprintf
// Remove print defines
#    define print(s)
#    define println(s)
#    define xprintf(fmt, ...)
#    define uprintf(fmt, ...)
#    define uprint(s)
#    define uprintln(s)

#endif /* NO_PRINT */

#ifdef USER_PRINT
// Remove normal print defines
#    undef print
#    undef println
#    undef xprintf
#    define print(s)
#    define println(s)
#    define xprintf(fmt, ...)
#endif

#define print_dec(i) xprintf("%u", i)
#define print_decs(i) xprintf("%d", i)
/* hex */
#define print_hex4(i) xprintf("%X", i)
#define print_hex8(i) xprintf("%02X", i)
#define print_hex16(i) xprintf("%04X", i)
#define print_hex32(i) xprintf("%08lX", i)
/* binary */
#define print_bin4(i) xprintf("%04b", i)
#define print_bin8(i) xprintf("%08b", i)
#define print_bin16(i) xprintf("%016b", i)
#define print_bin32(i) xprintf("%032lb", i)
#define print_bin_reverse8(i) xprintf("%08b", bitrev(i))
#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i))
#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i))
/* print value utility */
#define print_val_dec(v) xprintf(#v ": %u\n", v)
#define print_val_decs(v) xprintf(#v ": %d\n", v)
#define print_val_hex8(v) xprintf(#v ": %X\n", v)
#define print_val_hex16(v) xprintf(#v ": %02X\n", v)
#define print_val_hex32(v) xprintf(#v ": %04lX\n", v)
#define print_val_bin8(v) xprintf(#v ": %08b\n", v)
#define print_val_bin16(v) xprintf(#v ": %016b\n", v)
#define print_val_bin32(v) xprintf(#v ": %032lb\n", v)
#define print_val_bin_reverse8(v) xprintf(#v ": %08b\n", bitrev(v))
#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v))
#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v))

// User print disables the normal print messages in the body of QMK/TMK code and
// is meant as a lightweight alternative to NOPRINT. Use it when you only want to do
// a spot of debugging but lack flash resources for allowing all of the codebase to
// print (and store their wasteful strings).
//
// !!! DO NOT USE USER PRINT CALLS IN THE BODY OF QMK/TMK !!!

/* decimal */
#define uprint_dec(i) uprintf("%u", i)
#define uprint_decs(i) uprintf("%d", i)
/* hex */
#define uprint_hex4(i) uprintf("%X", i)
#define uprint_hex8(i) uprintf("%02X", i)
#define uprint_hex16(i) uprintf("%04X", i)
#define uprint_hex32(i) uprintf("%08lX", i)
/* binary */
#define uprint_bin4(i) uprintf("%04b", i)
#define uprint_bin8(i) uprintf("%08b", i)
#define uprint_bin16(i) uprintf("%016b", i)
#define uprint_bin32(i) uprintf("%032lb", i)
#define uprint_bin_reverse8(i) uprintf("%08b", bitrev(i))
#define uprint_bin_reverse16(i) uprintf("%016b", bitrev16(i))
#define uprint_bin_reverse32(i) uprintf("%032lb", bitrev32(i))
/* print value utility */
#define uprint_val_dec(v) uprintf(#v ": %u\n", v)
#define uprint_val_decs(v) uprintf(#v ": %d\n", v)
#define uprint_val_hex8(v) uprintf(#v ": %X\n", v)
#define uprint_val_hex16(v) uprintf(#v ": %02X\n", v)
#define uprint_val_hex32(v) uprintf(#v ": %04lX\n", v)
#define uprint_val_bin8(v) uprintf(#v ": %08b\n", v)
#define uprint_val_bin16(v) uprintf(#v ": %016b\n", v)
#define uprint_val_bin32(v) uprintf(#v ": %032lb\n", v)
#define uprint_val_bin_reverse8(v) uprintf(#v ": %08b\n", bitrev(v))
#define uprint_val_bin_reverse16(v) uprintf(#v ": %016b\n", bitrev16(v))
#define uprint_val_bin_reverse32(v) uprintf(#v ": %032lb\n", bitrev32(v))

一般的に使われそうな print 系の関数が定義されている

command.h

github.com

ソース全体

/*
Copyright 2011 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

/* FIXME: Add doxygen comments for the behavioral defines in here. */

/* TODO: Refactoring */
typedef enum { ONESHOT, CONSOLE, MOUSEKEY } command_state_t;
extern command_state_t command_state;

/* This allows to extend commands. Return false when command is not processed. */
bool command_extra(uint8_t code);
bool command_console_extra(uint8_t code);

#ifdef COMMAND_ENABLE
uint8_t numkey2num(uint8_t code);
bool    command_proc(uint8_t code);
#else
#    define command_proc(code) false
#endif

#ifndef IS_COMMAND
#    define IS_COMMAND() (get_mods() == MOD_MASK_SHIFT)
#endif

#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS
#    define MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS true
#endif

#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS
#    define MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS true
#endif

#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM
#    define MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM false
#endif

#ifndef MAGIC_KEY_HELP
#    define MAGIC_KEY_HELP H
#endif

#ifndef MAGIC_KEY_HELP_ALT
#    define MAGIC_KEY_HELP_ALT SLASH
#endif

#ifndef MAGIC_KEY_DEBUG
#    define MAGIC_KEY_DEBUG D
#endif

#ifndef MAGIC_KEY_DEBUG_MATRIX
#    define MAGIC_KEY_DEBUG_MATRIX X
#endif

#ifndef MAGIC_KEY_DEBUG_KBD
#    define MAGIC_KEY_DEBUG_KBD K
#endif

#ifndef MAGIC_KEY_DEBUG_MOUSE
#    define MAGIC_KEY_DEBUG_MOUSE M
#endif

#ifndef MAGIC_KEY_VERSION
#    define MAGIC_KEY_VERSION V
#endif

#ifndef MAGIC_KEY_STATUS
#    define MAGIC_KEY_STATUS S
#endif

#ifndef MAGIC_KEY_CONSOLE
#    define MAGIC_KEY_CONSOLE C
#endif

#ifndef MAGIC_KEY_LAYER0
#    define MAGIC_KEY_LAYER0 0
#endif

#ifndef MAGIC_KEY_LAYER0_ALT
#    define MAGIC_KEY_LAYER0_ALT GRAVE
#endif

#ifndef MAGIC_KEY_LAYER1
#    define MAGIC_KEY_LAYER1 1
#endif

#ifndef MAGIC_KEY_LAYER2
#    define MAGIC_KEY_LAYER2 2
#endif

#ifndef MAGIC_KEY_LAYER3
#    define MAGIC_KEY_LAYER3 3
#endif

#ifndef MAGIC_KEY_LAYER4
#    define MAGIC_KEY_LAYER4 4
#endif

#ifndef MAGIC_KEY_LAYER5
#    define MAGIC_KEY_LAYER5 5
#endif

#ifndef MAGIC_KEY_LAYER6
#    define MAGIC_KEY_LAYER6 6
#endif

#ifndef MAGIC_KEY_LAYER7
#    define MAGIC_KEY_LAYER7 7
#endif

#ifndef MAGIC_KEY_LAYER8
#    define MAGIC_KEY_LAYER8 8
#endif

#ifndef MAGIC_KEY_LAYER9
#    define MAGIC_KEY_LAYER9 9
#endif

#ifndef MAGIC_KEY_BOOTLOADER
#    define MAGIC_KEY_BOOTLOADER B
#endif

#ifndef MAGIC_KEY_BOOTLOADER_ALT
#    define MAGIC_KEY_BOOTLOADER_ALT ESC
#endif

#ifndef MAGIC_KEY_LOCK
#    define MAGIC_KEY_LOCK CAPS
#endif

#ifndef MAGIC_KEY_EEPROM
#    define MAGIC_KEY_EEPROM E
#endif

#ifndef MAGIC_KEY_EEPROM_CLEAR
#    define MAGIC_KEY_EEPROM_CLEAR BSPACE
#endif

#ifndef MAGIC_KEY_NKRO
#    define MAGIC_KEY_NKRO N
#endif

#ifndef MAGIC_KEY_SLEEP_LED
#    define MAGIC_KEY_SLEEP_LED Z

#endif

#define XMAGIC_KC(key) KC_##key
#define MAGIC_KC(key) XMAGIC_KC(key)

コマンドで使用する定数の初期化を行っている

command.c

github.com

ソース全体

/*
Copyright 2011 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
#include "wait.h"
#include "keycode.h"
#include "host.h"
#include "keymap.h"
#include "print.h"
#include "debug.h"
#include "util.h"
#include "timer.h"
#include "keyboard.h"
#include "bootloader.h"
#include "action_layer.h"
#include "action_util.h"
#include "eeconfig.h"
#include "sleep_led.h"
#include "led.h"
#include "command.h"
#include "quantum.h"
#include "version.h"

#ifdef BACKLIGHT_ENABLE
#    include "backlight.h"
#endif

#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
#    include "mousekey.h"
#endif

#ifdef AUDIO_ENABLE
#    include "audio.h"
#endif /* AUDIO_ENABLE */

static bool command_common(uint8_t code);
static void command_common_help(void);
static void print_version(void);
static void print_status(void);
static bool command_console(uint8_t code);
static void command_console_help(void);
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
static bool mousekey_console(uint8_t code);
static void mousekey_console_help(void);
#endif

static void switch_default_layer(uint8_t layer);

command_state_t command_state = ONESHOT;

bool command_proc(uint8_t code) {
    switch (command_state) {
        case ONESHOT:
            if (!IS_COMMAND()) return false;
            return (command_extra(code) || command_common(code));
            break;
        case CONSOLE:
            if (IS_COMMAND())
                return (command_extra(code) || command_common(code));
            else
                return (command_console_extra(code) || command_console(code));
            break;
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
        case MOUSEKEY:
            mousekey_console(code);
            break;
#endif
        default:
            command_state = ONESHOT;
            return false;
    }
    return true;
}

/* TODO: Refactoring is needed. */
/* This allows to define extra commands. return false when not processed. */
bool command_extra(uint8_t code) __attribute__((weak));
bool command_extra(uint8_t code) {
    (void)code;
    return false;
}

bool command_console_extra(uint8_t code) __attribute__((weak));
bool command_console_extra(uint8_t code) {
    (void)code;
    return false;
}

/***********************************************************
 * Command common
 ***********************************************************/
static void command_common_help(void) {
    print("\n\t- Magic -\n" STR(MAGIC_KEY_DEBUG) ": Debug Message Toggle\n" STR(MAGIC_KEY_DEBUG_MATRIX) ":  Matrix Debug Mode Toggle - Show keypresses in matrix grid\n" STR(MAGIC_KEY_DEBUG_KBD) ":    Keyboard Debug Toggle - Show keypress report\n" STR(MAGIC_KEY_DEBUG_MOUSE) ":   Debug Mouse Toggle\n" STR(MAGIC_KEY_VERSION) ": Version\n" STR(MAGIC_KEY_STATUS) ": Status\n" STR(MAGIC_KEY_CONSOLE) ": Activate Console Mode\n"

#if MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM
          STR(MAGIC_KEY_LAYER0) ":   Switch to Layer 0\n" STR(MAGIC_KEY_LAYER1) ":   Switch to Layer 1\n" STR(MAGIC_KEY_LAYER2) ":   Switch to Layer 2\n" STR(MAGIC_KEY_LAYER3) ":   Switch to Layer 3\n" STR(MAGIC_KEY_LAYER4) ":   Switch to Layer 4\n" STR(MAGIC_KEY_LAYER5) ":   Switch to Layer 5\n" STR(MAGIC_KEY_LAYER6) ":   Switch to Layer 6\n" STR(MAGIC_KEY_LAYER7) ":   Switch to Layer 7\n" STR(MAGIC_KEY_LAYER8) ":   Switch to Layer 8\n" STR(MAGIC_KEY_LAYER9) ":   Switch to Layer 9\n"
#endif

#if MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS
                                                                                                                                                                                                                                                                                                                                                                                                                                                                               "F1-F10:  Switch to Layer 0-9 (F10 = L0)\n"
#endif

#if MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS
                                                                                                                                                                                                                                                                                                                                                                                                                                                                               "0-9: Switch to Layer 0-9\n"
#endif

          STR(MAGIC_KEY_LAYER0_ALT) ":   Switch to Layer 0 (alternate)\n"

          STR(MAGIC_KEY_BOOTLOADER) ":   Jump to Bootloader\n" STR(MAGIC_KEY_BOOTLOADER_ALT) ":  Jump to Bootloader (alternate)\n"

#ifdef KEYBOARD_LOCK_ENABLE
          STR(MAGIC_KEY_LOCK) ": Lock Keyboard\n"
#endif

          STR(MAGIC_KEY_EEPROM) ":   Print EEPROM Settings\n" STR(MAGIC_KEY_EEPROM_CLEAR) ": Clear EEPROM\n"

#ifdef NKRO_ENABLE
          STR(MAGIC_KEY_NKRO) ": NKRO Toggle\n"
#endif

#ifdef SLEEP_LED_ENABLE
          STR(MAGIC_KEY_SLEEP_LED) ":    Sleep LED Test\n"
#endif
    );
}

static void print_version(void) {
    // print version & information
    print("\n\t- Version -\n");
    print("VID: " STR(VENDOR_ID) "(" STR(MANUFACTURER) ") "
                                                       "PID: " STR(PRODUCT_ID) "(" STR(PRODUCT) ") "
                                                                                                "VER: " STR(DEVICE_VER) "\n");
    print("BUILD:  (" __DATE__ ")\n");
#ifndef SKIP_VERSION
#    ifdef PROTOCOL_CHIBIOS
    print("CHIBIOS: " STR(CHIBIOS_VERSION) ", CONTRIB: " STR(CHIBIOS_CONTRIB_VERSION) "\n");
#    endif
#endif

    /* build options */
    print("OPTIONS:"

#ifdef PROTOCOL_LUFA
          " LUFA"
#endif
#ifdef PROTOCOL_VUSB
          " VUSB"
#endif
#ifdef BOOTMAGIC_ENABLE
          " BOOTMAGIC"
#endif
#ifdef MOUSEKEY_ENABLE
          " MOUSEKEY"
#endif
#ifdef EXTRAKEY_ENABLE
          " EXTRAKEY"
#endif
#ifdef CONSOLE_ENABLE
          " CONSOLE"
#endif
#ifdef COMMAND_ENABLE
          " COMMAND"
#endif
#ifdef NKRO_ENABLE
          " NKRO"
#endif
#ifdef LTO_ENABLE
          " LTO"
#endif

          " " STR(BOOTLOADER_SIZE) "\n");

    print("GCC: " STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__)
#if defined(__AVR__)
              " AVR-LIBC: " __AVR_LIBC_VERSION_STRING__ " AVR_ARCH: avr" STR(__AVR_ARCH__)
#endif
                  "\n");

    return;
}

static void print_status(void) {
    print("\n\t- Status -\n");

    print_val_hex8(host_keyboard_leds());
#ifndef PROTOCOL_VUSB
    // these aren't set on the V-USB protocol, so we just ignore them for now
    print_val_hex8(keyboard_protocol);
    print_val_hex8(keyboard_idle);
#endif
#ifdef NKRO_ENABLE
    print_val_hex8(keymap_config.nkro);
#endif
    print_val_hex32(timer_read32());
    return;
}

static void print_eeconfig(void) {
// Print these variables if NO_PRINT or USER_PRINT are not defined.
#if !defined(NO_PRINT) && !defined(USER_PRINT)

    print("default_layer: ");
    print_dec(eeconfig_read_default_layer());
    print("\n");

    debug_config_t dc;
    dc.raw = eeconfig_read_debug();
    print("debug_config.raw: ");
    print_hex8(dc.raw);
    print("\n");
    print(".enable: ");
    print_dec(dc.enable);
    print("\n");
    print(".matrix: ");
    print_dec(dc.matrix);
    print("\n");
    print(".keyboard: ");
    print_dec(dc.keyboard);
    print("\n");
    print(".mouse: ");
    print_dec(dc.mouse);
    print("\n");

    keymap_config_t kc;
    kc.raw = eeconfig_read_keymap();
    print("keymap_config.raw: ");
    print_hex8(kc.raw);
    print("\n");
    print(".swap_control_capslock: ");
    print_dec(kc.swap_control_capslock);
    print("\n");
    print(".capslock_to_control: ");
    print_dec(kc.capslock_to_control);
    print("\n");
    print(".swap_lctl_lgui: ");
    print_dec(kc.swap_lctl_lgui);
    print("\n");
    print(".swap_rctl_rgui: ");
    print_dec(kc.swap_rctl_rgui);
    print("\n");
    print(".swap_lalt_lgui: ");
    print_dec(kc.swap_lalt_lgui);
    print("\n");
    print(".swap_ralt_rgui: ");
    print_dec(kc.swap_ralt_rgui);
    print("\n");
    print(".no_gui: ");
    print_dec(kc.no_gui);
    print("\n");
    print(".swap_grave_esc: ");
    print_dec(kc.swap_grave_esc);
    print("\n");
    print(".swap_backslash_backspace: ");
    print_dec(kc.swap_backslash_backspace);
    print("\n");
    print(".nkro: ");
    print_dec(kc.nkro);
    print("\n");

#    ifdef BACKLIGHT_ENABLE
    backlight_config_t bc;
    bc.raw = eeconfig_read_backlight();
    print("backlight_config.raw: ");
    print_hex8(bc.raw);
    print("\n");
    print(".enable: ");
    print_dec(bc.enable);
    print("\n");
    print(".level: ");
    print_dec(bc.level);
    print("\n");
#    endif /* BACKLIGHT_ENABLE */

#endif /* !NO_PRINT */
}

static bool command_common(uint8_t code) {
#ifdef KEYBOARD_LOCK_ENABLE
    static host_driver_t *host_driver = 0;
#endif

    switch (code) {
#ifdef SLEEP_LED_ENABLE

        // test breathing sleep LED
        case MAGIC_KC(MAGIC_KEY_SLEEP_LED):
            print("Sleep LED Test\n");
            sleep_led_toggle();
            led_set(host_keyboard_leds());
            break;
#endif

        // print stored eeprom config
        case MAGIC_KC(MAGIC_KEY_EEPROM):
            print("eeconfig:\n");
            print_eeconfig();
            break;

        // clear eeprom
        case MAGIC_KC(MAGIC_KEY_EEPROM_CLEAR):
            print("Clearing EEPROM\n");
            eeconfig_init();
            break;

#ifdef KEYBOARD_LOCK_ENABLE

        // lock/unlock keyboard
        case MAGIC_KC(MAGIC_KEY_LOCK):
            if (host_get_driver()) {
                host_driver = host_get_driver();
                clear_keyboard();
                host_set_driver(0);
                print("Locked.\n");
            } else {
                host_set_driver(host_driver);
                print("Unlocked.\n");
            }
            break;
#endif

        // print help
        case MAGIC_KC(MAGIC_KEY_HELP):
        case MAGIC_KC(MAGIC_KEY_HELP_ALT):
            command_common_help();
            break;

        // activate console
        case MAGIC_KC(MAGIC_KEY_CONSOLE):
            debug_matrix   = false;
            debug_keyboard = false;
            debug_mouse    = false;
            debug_enable   = false;
            command_console_help();
            print("C> ");
            command_state = CONSOLE;
            break;

        // jump to bootloader
        case MAGIC_KC(MAGIC_KEY_BOOTLOADER):
        case MAGIC_KC(MAGIC_KEY_BOOTLOADER_ALT):
            print("\n\nJumping to bootloader... ");
            reset_keyboard();
            break;

        // debug toggle
        case MAGIC_KC(MAGIC_KEY_DEBUG):
            debug_enable = !debug_enable;
            if (debug_enable) {
                print("\ndebug: on\n");
            } else {
                print("\ndebug: off\n");
                debug_matrix   = false;
                debug_keyboard = false;
                debug_mouse    = false;
            }
            break;

        // debug matrix toggle
        case MAGIC_KC(MAGIC_KEY_DEBUG_MATRIX):
            debug_matrix = !debug_matrix;
            if (debug_matrix) {
                print("\nmatrix: on\n");
                debug_enable = true;
            } else {
                print("\nmatrix: off\n");
            }
            break;

        // debug keyboard toggle
        case MAGIC_KC(MAGIC_KEY_DEBUG_KBD):
            debug_keyboard = !debug_keyboard;
            if (debug_keyboard) {
                print("\nkeyboard: on\n");
                debug_enable = true;
            } else {
                print("\nkeyboard: off\n");
            }
            break;

        // debug mouse toggle
        case MAGIC_KC(MAGIC_KEY_DEBUG_MOUSE):
            debug_mouse = !debug_mouse;
            if (debug_mouse) {
                print("\nmouse: on\n");
                debug_enable = true;
            } else {
                print("\nmouse: off\n");
            }
            break;

        // print version
        case MAGIC_KC(MAGIC_KEY_VERSION):
            print_version();
            break;

        // print status
        case MAGIC_KC(MAGIC_KEY_STATUS):
            print_status();
            break;

#ifdef NKRO_ENABLE

        // NKRO toggle
        case MAGIC_KC(MAGIC_KEY_NKRO):
            clear_keyboard();  // clear to prevent stuck keys
            keymap_config.nkro = !keymap_config.nkro;
            if (keymap_config.nkro) {
                print("NKRO: on\n");
            } else {
                print("NKRO: off\n");
            }
            break;
#endif

            // switch layers

        case MAGIC_KC(MAGIC_KEY_LAYER0_ALT):
            switch_default_layer(0);
            break;

#if MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM

        case MAGIC_KC(MAGIC_KEY_LAYER0):
            switch_default_layer(0);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER1):
            switch_default_layer(1);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER2):
            switch_default_layer(2);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER3):
            switch_default_layer(3);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER4):
            switch_default_layer(4);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER5):
            switch_default_layer(5);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER6):
            switch_default_layer(6);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER7):
            switch_default_layer(7);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER8):
            switch_default_layer(8);
            break;

        case MAGIC_KC(MAGIC_KEY_LAYER9):
            switch_default_layer(9);
            break;
#endif

#if MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS

        case KC_F1 ... KC_F9:
            switch_default_layer((code - KC_F1) + 1);
            break;
        case KC_F10:
            switch_default_layer(0);
            break;
#endif

#if MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS

        case KC_1 ... KC_9:
            switch_default_layer((code - KC_1) + 1);
            break;
        case KC_0:
            switch_default_layer(0);
            break;
#endif

        default:
            print("?");
            return false;
    }
    return true;
}

/***********************************************************
 * Command console
 ***********************************************************/
static void command_console_help(void) {
    print("\n\t- Console -\n"
          "ESC/q:    quit\n"
#ifdef MOUSEKEY_ENABLE
          "m:    mousekey\n"
#endif
    );
}

static bool command_console(uint8_t code) {
    switch (code) {
        case KC_H:
        case KC_SLASH: /* ? */
            command_console_help();
            break;
        case KC_Q:
        case KC_ESC:
            command_state = ONESHOT;
            return false;
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
        case KC_M:
            mousekey_console_help();
            print("M> ");
            command_state = MOUSEKEY;
            return true;
#endif
        default:
            print("?");
            return false;
    }
    print("C> ");
    return true;
}

#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
/***********************************************************
 * Mousekey console
 ***********************************************************/
static uint8_t mousekey_param = 0;

static void mousekey_param_print(void) {
// Print these variables if NO_PRINT or USER_PRINT are not defined.
#    if !defined(NO_PRINT) && !defined(USER_PRINT)
    print("\n\t- Values -\n");
    print("1: delay(*10ms): ");
    print_dec(mk_delay);
    print("\n");
    print("2: interval(ms): ");
    print_dec(mk_interval);
    print("\n");
    print("3: max_speed: ");
    print_dec(mk_max_speed);
    print("\n");
    print("4: time_to_max: ");
    print_dec(mk_time_to_max);
    print("\n");
    print("5: wheel_max_speed: ");
    print_dec(mk_wheel_max_speed);
    print("\n");
    print("6: wheel_time_to_max: ");
    print_dec(mk_wheel_time_to_max);
    print("\n");
#    endif /* !NO_PRINT */
}

//#define PRINT_SET_VAL(v)  print(#v " = "); print_dec(v); print("\n");
#    define PRINT_SET_VAL(v) xprintf(#    v " = %d\n", (v))
static void mousekey_param_inc(uint8_t param, uint8_t inc) {
    switch (param) {
        case 1:
            if (mk_delay + inc < UINT8_MAX)
                mk_delay += inc;
            else
                mk_delay = UINT8_MAX;
            PRINT_SET_VAL(mk_delay);
            break;
        case 2:
            if (mk_interval + inc < UINT8_MAX)
                mk_interval += inc;
            else
                mk_interval = UINT8_MAX;
            PRINT_SET_VAL(mk_interval);
            break;
        case 3:
            if (mk_max_speed + inc < UINT8_MAX)
                mk_max_speed += inc;
            else
                mk_max_speed = UINT8_MAX;
            PRINT_SET_VAL(mk_max_speed);
            break;
        case 4:
            if (mk_time_to_max + inc < UINT8_MAX)
                mk_time_to_max += inc;
            else
                mk_time_to_max = UINT8_MAX;
            PRINT_SET_VAL(mk_time_to_max);
            break;
        case 5:
            if (mk_wheel_max_speed + inc < UINT8_MAX)
                mk_wheel_max_speed += inc;
            else
                mk_wheel_max_speed = UINT8_MAX;
            PRINT_SET_VAL(mk_wheel_max_speed);
            break;
        case 6:
            if (mk_wheel_time_to_max + inc < UINT8_MAX)
                mk_wheel_time_to_max += inc;
            else
                mk_wheel_time_to_max = UINT8_MAX;
            PRINT_SET_VAL(mk_wheel_time_to_max);
            break;
    }
}

static void mousekey_param_dec(uint8_t param, uint8_t dec) {
    switch (param) {
        case 1:
            if (mk_delay > dec)
                mk_delay -= dec;
            else
                mk_delay = 0;
            PRINT_SET_VAL(mk_delay);
            break;
        case 2:
            if (mk_interval > dec)
                mk_interval -= dec;
            else
                mk_interval = 0;
            PRINT_SET_VAL(mk_interval);
            break;
        case 3:
            if (mk_max_speed > dec)
                mk_max_speed -= dec;
            else
                mk_max_speed = 0;
            PRINT_SET_VAL(mk_max_speed);
            break;
        case 4:
            if (mk_time_to_max > dec)
                mk_time_to_max -= dec;
            else
                mk_time_to_max = 0;
            PRINT_SET_VAL(mk_time_to_max);
            break;
        case 5:
            if (mk_wheel_max_speed > dec)
                mk_wheel_max_speed -= dec;
            else
                mk_wheel_max_speed = 0;
            PRINT_SET_VAL(mk_wheel_max_speed);
            break;
        case 6:
            if (mk_wheel_time_to_max > dec)
                mk_wheel_time_to_max -= dec;
            else
                mk_wheel_time_to_max = 0;
            PRINT_SET_VAL(mk_wheel_time_to_max);
            break;
    }
}

static void mousekey_console_help(void) {
    print("\n\t- Mousekey -\n"
          "ESC/q:    quit\n"
          "1:    delay(*10ms)\n"
          "2:    interval(ms)\n"
          "3:    max_speed\n"
          "4:    time_to_max\n"
          "5:    wheel_max_speed\n"
          "6:    wheel_time_to_max\n"
          "\n"
          "p:    print values\n"
          "d:    set defaults\n"
          "up:   +1\n"
          "down: -1\n"
          "pgup: +10\n"
          "pgdown:   -10\n"
          "\n"
          "speed = delta * max_speed * (repeat / time_to_max)\n");
    xprintf("where delta: cursor=%d, wheel=%d\n"
            "See http://en.wikipedia.org/wiki/Mouse_keys\n",
            MOUSEKEY_MOVE_DELTA, MOUSEKEY_WHEEL_DELTA);
}

static bool mousekey_console(uint8_t code) {
    switch (code) {
        case KC_H:
        case KC_SLASH: /* ? */
            mousekey_console_help();
            break;
        case KC_Q:
        case KC_ESC:
            if (mousekey_param) {
                mousekey_param = 0;
            } else {
                print("C> ");
                command_state = CONSOLE;
                return false;
            }
            break;
        case KC_P:
            mousekey_param_print();
            break;
        case KC_1:
        case KC_2:
        case KC_3:
        case KC_4:
        case KC_5:
        case KC_6:
            mousekey_param = numkey2num(code);
            break;
        case KC_UP:
            mousekey_param_inc(mousekey_param, 1);
            break;
        case KC_DOWN:
            mousekey_param_dec(mousekey_param, 1);
            break;
        case KC_PGUP:
            mousekey_param_inc(mousekey_param, 10);
            break;
        case KC_PGDN:
            mousekey_param_dec(mousekey_param, 10);
            break;
        case KC_D:
            mk_delay             = MOUSEKEY_DELAY / 10;
            mk_interval          = MOUSEKEY_INTERVAL;
            mk_max_speed         = MOUSEKEY_MAX_SPEED;
            mk_time_to_max       = MOUSEKEY_TIME_TO_MAX;
            mk_wheel_max_speed   = MOUSEKEY_WHEEL_MAX_SPEED;
            mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
            print("set default\n");
            break;
        default:
            print("?");
            return false;
    }
    if (mousekey_param) {
        xprintf("M%d> ", mousekey_param);
    } else {
        print("M>");
    }
    return true;
}
#endif

/***********************************************************
 * Utilities
 ***********************************************************/
uint8_t numkey2num(uint8_t code) {
    switch (code) {
        case KC_1:
            return 1;
        case KC_2:
            return 2;
        case KC_3:
            return 3;
        case KC_4:
            return 4;
        case KC_5:
            return 5;
        case KC_6:
            return 6;
        case KC_7:
            return 7;
        case KC_8:
            return 8;
        case KC_9:
            return 9;
        case KC_0:
            return 0;
    }
    return 0;
}

static void switch_default_layer(uint8_t layer) {
    xprintf("L%d\n", layer);
    default_layer_set(1UL << layer);
    clear_keyboard();
}

各種コマンドの動作が記述されている
また、コンソールへの出力等も行っている

コマンドについてはここが詳しい

docs.qmk.fm

action_layer.h

github.com

ソース全体

/*
Copyright 2013 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <stdint.h>
#include "keyboard.h"
#include "action.h"

#if defined(LAYER_STATE_8BIT)
typedef uint8_t layer_state_t;
#    define MAX_LAYER_BITS 3
#    ifndef MAX_LAYER
#        define MAX_LAYER 8
#    endif
#    define get_highest_layer(state) biton(state)
#elif defined(LAYER_STATE_16BIT)
typedef uint16_t layer_state_t;
#    define MAX_LAYER_BITS 4
#    ifndef MAX_LAYER
#        define MAX_LAYER 16
#    endif
#    define get_highest_layer(state) biton16(state)
#else
typedef uint32_t layer_state_t;
#    define MAX_LAYER_BITS 5
#    ifndef MAX_LAYER
#        define MAX_LAYER 32
#    endif
#    define get_highest_layer(state) biton32(state)
#endif

/*
 * Default Layer
 */
extern layer_state_t default_layer_state;
void                 default_layer_debug(void);
void                 default_layer_set(layer_state_t state);

__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state);
__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state);

#ifndef NO_ACTION_LAYER
/* bitwise operation */
void default_layer_or(layer_state_t state);
void default_layer_and(layer_state_t state);
void default_layer_xor(layer_state_t state);
#else
#    define default_layer_or(state)
#    define default_layer_and(state)
#    define default_layer_xor(state)
#endif

/*
 * Keymap Layer
 */
#ifndef NO_ACTION_LAYER
extern layer_state_t layer_state;

void layer_state_set(layer_state_t state);
bool layer_state_is(uint8_t layer);
bool layer_state_cmp(layer_state_t layer1, uint8_t layer2);

void layer_debug(void);
void layer_clear(void);
void layer_move(uint8_t layer);
void layer_on(uint8_t layer);
void layer_off(uint8_t layer);
void layer_invert(uint8_t layer);
/* bitwise operation */
void          layer_or(layer_state_t state);
void          layer_and(layer_state_t state);
void          layer_xor(layer_state_t state);
layer_state_t layer_state_set_user(layer_state_t state);
layer_state_t layer_state_set_kb(layer_state_t state);
#else
#    define layer_state 0

#    define layer_state_set(layer)
#    define layer_state_is(layer) (layer == 0)
#    define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & 1UL << layer) != 0)

#    define layer_debug()
#    define layer_clear()
#    define layer_move(layer) (void)layer
#    define layer_on(layer) (void)layer
#    define layer_off(layer) (void)layer
#    define layer_invert(layer) (void)layer
#    define layer_or(state) (void)state
#    define layer_and(state) (void)state
#    define layer_xor(state) (void)state
#    define layer_state_set_kb(state) (void)state
#    define layer_state_set_user(state) (void)state
#endif

/* pressed actions cache */
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)

void    update_source_layers_cache(keypos_t key, uint8_t layer);
uint8_t read_source_layers_cache(keypos_t key);
#endif
action_t store_or_get_action(bool pressed, keypos_t key);

/* return the topmost non-transparent layer currently associated with key */
uint8_t layer_switch_get_layer(keypos_t key);

/* return action depending on current layer status */
action_t layer_switch_get_action(keypos_t key);

ほぼ定数の初期化や、プロトタイプ宣言である

action_layter.c

github.com

ソース全体

#include <stdint.h>
#include "keyboard.h"
#include "action.h"
#include "util.h"
#include "action_layer.h"

#ifdef DEBUG_ACTION
#    include "debug.h"
#else
#    include "nodebug.h"
#endif

/** \brief Default Layer State
 */
layer_state_t default_layer_state = 0;

/** \brief Default Layer State Set At user Level
 *
 * Run user code on default layer state change
 */
__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) { return state; }

/** \brief Default Layer State Set At Keyboard Level
 *
 *  Run keyboard code on default layer state change
 */
__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) { return default_layer_state_set_user(state); }

/** \brief Default Layer State Set
 *
 * Static function to set the default layer state, prints debug info and clears keys
 */
static void default_layer_state_set(layer_state_t state) {
    state = default_layer_state_set_kb(state);
    debug("default_layer_state: ");
    default_layer_debug();
    debug(" to ");
    default_layer_state = state;
    default_layer_debug();
    debug("\n");
#ifdef STRICT_LAYER_RELEASE
    clear_keyboard_but_mods();  // To avoid stuck keys
#else
    clear_keyboard_but_mods_and_keys();  // Don't reset held keys
#endif
}

/** \brief Default Layer Print
 *
 * Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit.
 */
void default_layer_debug(void) { dprintf("%08lX(%u)", default_layer_state, get_highest_layer(default_layer_state)); }

/** \brief Default Layer Set
 *
 * Sets the default layer state.
 */
void default_layer_set(layer_state_t state) { default_layer_state_set(state); }

#ifndef NO_ACTION_LAYER
/** \brief Default Layer Or
 *
 * Turns on the default layer based on matching bits between specifed layer and existing layer state
 */
void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); }
/** \brief Default Layer And
 *
 * Turns on default layer based on matching enabled bits between specifed layer and existing layer state
 */
void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); }
/** \brief Default Layer Xor
 *
 * Turns on default layer based on non-matching bits between specifed layer and existing layer state
 */
void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); }
#endif

#ifndef NO_ACTION_LAYER
/** \brief Keymap Layer State
 */
layer_state_t layer_state = 0;

/** \brief Layer state set user
 *
 * Runs user code on layer state change
 */
__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) { return state; }

/** \brief Layer state set keyboard
 *
 * Runs keyboard code on layer state change
 */
__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { return layer_state_set_user(state); }

/** \brief Layer state set
 *
 * Sets the layer to match the specifed state (a bitmask)
 */
void layer_state_set(layer_state_t state) {
    state = layer_state_set_kb(state);
    dprint("layer_state: ");
    layer_debug();
    dprint(" to ");
    layer_state = state;
    layer_debug();
    dprintln();
#    ifdef STRICT_LAYER_RELEASE
    clear_keyboard_but_mods();  // To avoid stuck keys
#    else
    clear_keyboard_but_mods_and_keys();  // Don't reset held keys
#    endif
}

/** \brief Layer clear
 *
 * Turn off all layers
 */
void layer_clear(void) { layer_state_set(0); }

/** \brief Layer state is
 *
 * Return whether the given state is on (it might still be shadowed by a higher state, though)
 */
bool layer_state_is(uint8_t layer) { return layer_state_cmp(layer_state, layer); }

/** \brief Layer state compare
 *
 * Used for comparing layers {mostly used for unit testing}
 */
bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) {
    if (!cmp_layer_state) {
        return layer == 0;
    }
    return (cmp_layer_state & (1UL << layer)) != 0;
}

/** \brief Layer move
 *
 * Turns on the given layer and turn off all other layers
 */
void layer_move(uint8_t layer) { layer_state_set(1UL << layer); }

/** \brief Layer on
 *
 * Turns on given layer
 */
void layer_on(uint8_t layer) { layer_state_set(layer_state | (1UL << layer)); }

/** \brief Layer off
 *
 * Turns off given layer
 */
void layer_off(uint8_t layer) { layer_state_set(layer_state & ~(1UL << layer)); }

/** \brief Layer invert
 *
 * Toggle the given layer (set it if it's unset, or unset it if it's set)
 */
void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ (1UL << layer)); }

/** \brief Layer or
 *
 * Turns on layers based on matching bits between specifed layer and existing layer state
 */
void layer_or(layer_state_t state) { layer_state_set(layer_state | state); }
/** \brief Layer and
 *
 * Turns on layers based on matching enabled bits between specifed layer and existing layer state
 */
void layer_and(layer_state_t state) { layer_state_set(layer_state & state); }
/** \brief Layer xor
 *
 * Turns on layers based on non-matching bits between specifed layer and existing layer state
 */
void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); }

/** \brief Layer debug printing
 *
 * Print out the hex value of the 32-bit layer state, as well as the value of the highest bit.
 */
void layer_debug(void) { dprintf("%08lX(%u)", layer_state, get_highest_layer(layer_state)); }
#endif

#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
/** \brief source layer cache
 */

uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}};

/** \brief update source layers cache
 *
 * Updates the cached keys when changing layers
 */
void update_source_layers_cache(keypos_t key, uint8_t layer) {
    const uint8_t key_number  = key.col + (key.row * MATRIX_COLS);
    const uint8_t storage_row = key_number / 8;
    const uint8_t storage_bit = key_number % 8;

    for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
        source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit);
    }
}

/** \brief read source layers cache
 *
 * reads the cached keys stored when the layer was changed
 */
uint8_t read_source_layers_cache(keypos_t key) {
    const uint8_t key_number  = key.col + (key.row * MATRIX_COLS);
    const uint8_t storage_row = key_number / 8;
    const uint8_t storage_bit = key_number % 8;
    uint8_t       layer       = 0;

    for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
        layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number;
    }

    return layer;
}
#endif

/** \brief Store or get action (FIXME: Needs better summary)
 *
 * Make sure the action triggered when the key is released is the same
 * one as the one triggered on press. It's important for the mod keys
 * when the layer is switched after the down event but before the up
 * event as they may get stuck otherwise.
 */
action_t store_or_get_action(bool pressed, keypos_t key) {
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
    if (disable_action_cache) {
        return layer_switch_get_action(key);
    }

    uint8_t layer;

    if (pressed) {
        layer = layer_switch_get_layer(key);
        update_source_layers_cache(key, layer);
    } else {
        layer = read_source_layers_cache(key);
    }
    return action_for_key(layer, key);
#else
    return layer_switch_get_action(key);
#endif
}

/** \brief Layer switch get layer
 *
 * Gets the layer based on key info
 */
uint8_t layer_switch_get_layer(keypos_t key) {
#ifndef NO_ACTION_LAYER
    action_t action;
    action.code = ACTION_TRANSPARENT;

    layer_state_t layers = layer_state | default_layer_state;
    /* check top layer first */
    for (int8_t i = MAX_LAYER - 1; i >= 0; i--) {
        if (layers & (1UL << i)) {
            action = action_for_key(i, key);
            if (action.code != ACTION_TRANSPARENT) {
                return i;
            }
        }
    }
    /* fall back to layer 0 */
    return 0;
#else
    return get_highest_layer(default_layer_state);
#endif
}

/** \brief Layer switch get layer
 *
 * Gets action code based on key position
 */
action_t layer_switch_get_action(keypos_t key) { return action_for_key(layer_switch_get_layer(key), key); }

~11行

include 宣言である

~最終行

各変数や関数について見ていこう

layer_state_t default_layer_state

デフォルトの layer_state_t を初期化している
layer_state_tuint8_t, uint16_t, uint32_t のいずれかである(デフォルトは uint32_t
初期化処理を除いて、基本的には参照されるだけだと思われる

layer_state_t default_layer_state_set_user(layer_state_t state)

ユーザーが上書き可能な関数である
渡された state を処理することで default_layer_state を定義できる
主に keymap.c などで実装する

layer_state_t default_layer_state_set_kb(layer_state_t state)

ユーザーが上書き可能な関数である
渡された state を処理することで default_layer_state を定義できる
主に <keyboard>.c などで実装する

void default_layer_state_set(layer_state_t state)

default_layer_state を定義する関数である
レイヤの状態の表示も行う

void default_layer_debug(void)

default_layer_state と、1地番上のレイヤを表示する関数である

void default_layer_set(layer_state_t state)

default_layer_state を定義する関数である

void default_layer_or(layer_state_t state)

default_layer_state と渡された state との OR 演算を行い、 default_layer_state に代入する関数である

void default_layer_and(layer_state_t state)

default_layer_state と渡された state との AND 演算を行い、 default_layer_state に代入する関数である

void default_layer_xor(layer_state_t state)

default_layer_state と渡された state との XOR 演算を行い、 default_layer_state に代入する関数である

layer_state_t layer_state

現在の layer_state_t の状態を保持する変数である

layer_state_t layer_state_set_user(layer_state_t state)

ユーザーが上書き可能な関数である
渡された state を処理することで layer_state を定義できる
主に keymap.c などで実装する

layer_state_t layer_state_set_kb(layer_state_t state)

ユーザーが上書き可能な関数である
渡された state を処理することで layer_state を定義できる
主に <keyboard>.c などで実装する

void layer_state_set(layer_state_t state)

layer_state を定義する関数である
レイヤの状態の表示も行う

void layer_clear(void)

layer_state0 に初期化する関数である

bool layer_state_is(uint8_t layer)

layer_statelayer と一致するかを返す関数である

bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer)

cmp_layer_state の下から layer bit 目が立っているかを判定する関数である

void layer_move(uint8_t layer)

layer_state の下から layer bit 目を立て、ほかのビットを倒す関数である

void layer_on(uint8_t layer)

layer_state の下から layer bit 目を立てる関数である(ほかのビットは触れない)

void layer_off(uint8_t layer)

layer_state の下から layer bit 目を倒す関数である(ほかのビットは触れない)

void layer_invert(uint8_t layer)

layer_state の下から layer bit 目を反転する関数である(ほかのビットは触れない)
1 -> 0, 0 -> 1

void layer_or(layer_state_t state)

layer_state と渡された state との OR 演算を行い、 layer_state に代入する関数である

void layer_and(layer_state_t state)

layer_state と渡された state との AND 演算を行い、 layer_state に代入する関数である

void layer_xor(layer_state_t state)

layer_state と渡された state との XOR 演算を行い、 layer_state に代入する関数である

void layer_debug(void)

現在の layer_state と、1地番上のレイヤを表示する関数である

uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS]

以下のようなキーボードを考える

/*--------------------*/

#define MATRIX_ROWS 3
#define MATRIX_COLS 3

/*--------------------*/

// action_layer.h L39-44 より
#define MAX_LAYER_BITS 5
#define MAX_LAYER 32

/*--------------------*/

/*

      | 0 | 1 | 2 |
  --- ,-----------.
   0  | a | b | c |
  --- |---+---+---|
   1  | d | e | f |
  --- |---+---+---|
   2  | g | h | i |
  --- `-----------'

*/

a ~ i はキーを識別しやすくするために振っているもので、キーコードではない
この時 uint8_t source_layers_cache[][] は以下のようになる

(MATRIX_ROWS * MATRIX_COLS +7) / 8 = (3 * 3 + 7) / 8
                                   = 16 / 8
                                   = 2

MAX_LAYER_BITS = 5

source_layers_cache[2][5] = 
[
  [0000 0000, 0000 0000, 0000 0000, 0000 0000, 0000 0000],
  [0000 0000, 0000 0000, 0000 0000, 0000 0000, 0000 0000]
]

void update_source_layers_cache(keypos_t key, uint8_t layer)

keypos_t は以下のように定義されている

// https://github.com/qmk/qmk_firmware/blob/master/tmk_core/common/keyboard.h#L28-L31
typedef struct {
    uint8_t col;
    uint8_t row;
} keypos_t;

例えば、上で示したキーボードで layer = 1 のとき f のキーを押したとする
すると、渡される keylayer は以下である

key.col = 2
key.row = 1

layer = 1

よって、各変数は以下のようになる

key_number = key.col + (key.row * MATRIX_COLS)
           = 2 + (1 * 3)
           = 5

storage_row = key_number / 8
            = 5 / 8
            = 0

storage_bit = key_number % 8
            = 5 % 8
            = 5

つまり、 source_layer_cache[][] の 0 行目、 6bit 目に値を格納する という意味になる
storage_bit0 から始まるため、 + 1 している
実行すると、 source_layer_cache[][] の中身はこのようになる

source_layers_cache[2][5] = 
[
  [0010 0000, 0000 0000, 0000 0000, 0000 0000, 0000 0000],
  [0000 0000, 0000 0000, 0000 0000, 0000 0000, 0000 0000]
]

さて、どのようにキーの位置を保持しているかを考えよう
key_number の計算式を見ればわかるだろう
上で示したキーボードの例で考えるとこのようになる

row col key_number
a 0 0 0
b 0 1 1
c 0 2 2
d 1 0 3
e 1 1 4
f 1 2 5
g 2 0 6
h 2 1 7
i 2 2 8

では、 source_layer_cache[][] にこれに基づいて値を入れてみよう
全部記述するのはわかりにくいので、一番左の要素のみ記述する

押されたキー : a
key_number = 0

source_layers_cache[2][5] = 
[
  [0000 0001, ...],
  [0000 0000, ...]
]
押されたキー : b
key_number = 1

source_layers_cache[2][5] = 
[
  [0000 0010, ...],
  [0000 0000, ...]
]
押されたキー : c
key_number = 2

source_layers_cache[2][5] = 
[
  [0000 0100, ...],
  [0000 0000, ...]
]
押されたキー : d
key_number = 3

source_layers_cache[2][5] = 
[
  [0000 1000, ...],
  [0000 0000, ...]
]
押されたキー : e
key_number = 4

source_layers_cache[2][5] = 
[
  [0001 0000, ...],
  [0000 0000, ...]
]
押されたキー : f
key_number = 5

source_layers_cache[2][5] = 
[
  [0010 0000, ...],
  [0000 0000, ...]
]
押されたキー : g
key_number = 6

source_layers_cache[2][5] = 
[
  [0100 0000, ...],
  [0000 0000, ...]
]
押されたキー : h
key_number = 7

source_layers_cache[2][5] = 
[
  [1000 0000, ...],
  [0000 0000, ...]
]
押されたキー : i
key_number = 8

source_layers_cache[2][5] = 
[
  [0000 0000, ...],
  [0000 0001, ...]
]

key_number = 8 つまり 9bit 目を立てる場合、2行目に入るので注意してほしい

キーの位置をどうやって保持しているかは分かった
では、レイヤーの情報はどのように保持しているのだろうか

source_layers_cache[][] は要素を5つ持つ配列の配列であった
この 要素が5つあること が意味を持つ
各要素が2進数の桁を表しているのだ

 <--1桁-->  <--2桁-->  <--3桁-->  <--4桁-->  <--5桁-->
[0000 0000, 0000 0000, 0000 0000, 0000 0000, 0000 0000],

例えば、 layer = 5 のとき、2進数で表すと 0000 0101 となる
この時 e (key.col = 1, key.row = 1) のキーを押したとすると、 key_number = 4 となり、 source_layers_cache[][] は次のようになる

source_layers_cache[2][5] = 
[
  [0001 0000, 0000 0000, 0001 0000, 0000 0000, 0000 0000],
  [0000 0000, 0000 0000, 0000 0000, 0000 0000, 0000 0000]
]

これで、キーの位置とレイヤーの情報が保持できた
さらに別のキーを別のレイヤーで押しても、同じように保持される
layer = 15d (key.col = 0, key.row = 1, key_number = 3) のキーを押すと、次のように更新される

source_layers_cache[2][5] = 
[
  [0001 1000, 0000 1000, 0001 1000, 0000 1000, 0000 0000],
  [0000 0000, 0000 0000, 0000 0000, 0000 0000, 0000 0000]
]

uint8_t read_source_layers_cache(keypos_t key)

source_layers_cache[][]key の位置で確認し、レイヤーのインデックスを返す関数である

action_t store_or_get_action(bool pressed, keypos_t key)

press = true のとき update_source_layers_cache() を実行して source_layers_cache[][] を更新する
press = false のとき read_source_layers_cache() を実行してレイヤーのインデックスを取得し、 action_t を返す
action_t は以下で定義されている

github.com

uint8_t layer_switch_get_layer(keypos_t key)

key の位置において、 ACTION_TRANSPARENT (キーコードで言う KC_TRNS, _______)でない最上位のレイヤーを返す関数である

action_t layer_switch_get_action(keypos_t key)

key の位置の action_t を返す関数である
layer_switch_get_layer()ACTION_TRANSPARENT でないレイヤーを取得しているので有効な action_t が実行される

まとめ

action.c に include されているものを順番に探検してきた
action_layer.c でのキーの位置とレイヤーの情報の持ち方には感嘆しかなかった

次回予告

次回は action.c に include されているものの続きを見ていこうと思う