koktoh の雑記帳

気ままに書いていきます

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

はじめに

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

前回のおさらい

koktoh.hatenablog.com

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

探検開始

続きから探検していこう

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

action_tapping.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

/* period of tapping(ms) */
#ifndef TAPPING_TERM
#    define TAPPING_TERM 200
#endif

/* tap count needed for toggling a feature */
#ifndef TAPPING_TOGGLE
#    define TAPPING_TOGGLE 5
#endif

#define WAITING_BUFFER_SIZE 8

#ifndef NO_ACTION_TAPPING
uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
void     action_tapping_process(keyrecord_t record);

uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);
bool     get_permissive_hold(uint16_t keycode, keyrecord_t *record);
bool     get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record);
bool     get_tapping_force_hold(uint16_t keycode, keyrecord_t *record);
bool     get_retro_tapping(uint16_t keycode, keyrecord_t *record);
#endif

定数の初期化と、プロトタイプ宣言が行われている

TAPPING_TERM

キーのタップとホールドの閾値時間である
TAPPING_TERM 以上押し続けられるとホールドになり、それ未満で離されるとタップとなる

TAPPING_TOGGLE

TT(layer) で参照される値である
キーをホールドすると layer がアクティブになり、離すと非アクティブになる
TAPPING_TOGGLE の回数だけタップすると、レイヤーが切り替えられる

詳しくはここを参照いただきたい

docs.qmk.fm

action_tapping.c

github.com

ソース全体

#include <stdint.h>
#include <stdbool.h>
#include "action.h"
#include "action_layer.h"
#include "action_tapping.h"
#include "keycode.h"
#include "timer.h"

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

#ifndef NO_ACTION_TAPPING

#    define IS_TAPPING() !IS_NOEVENT(tapping_key.event)
#    define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
#    define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
#    define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))

__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; }

#    ifdef TAPPING_TERM_PER_KEY
#        define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_event_keycode(tapping_key.event, false), &tapping_key))
#    else
#        define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
#    endif

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

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

static keyrecord_t tapping_key                         = {};
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
static uint8_t     waiting_buffer_head                 = 0;
static uint8_t     waiting_buffer_tail                 = 0;

static bool process_tapping(keyrecord_t *record);
static bool waiting_buffer_enq(keyrecord_t record);
static void waiting_buffer_clear(void);
static bool waiting_buffer_typed(keyevent_t event);
static bool waiting_buffer_has_anykey_pressed(void);
static void waiting_buffer_scan_tap(void);
static void debug_tapping_key(void);
static void debug_waiting_buffer(void);

/** \brief Action Tapping Process
 *
 * FIXME: Needs doc
 */
void action_tapping_process(keyrecord_t record) {
    if (process_tapping(&record)) {
        if (!IS_NOEVENT(record.event)) {
            debug("processed: ");
            debug_record(record);
            debug("\n");
        }
    } else {
        if (!waiting_buffer_enq(record)) {
            // clear all in case of overflow.
            debug("OVERFLOW: CLEAR ALL STATES\n");
            clear_keyboard();
            waiting_buffer_clear();
            tapping_key = (keyrecord_t){};
        }
    }

    // process waiting_buffer
    if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
        debug("---- action_exec: process waiting_buffer -----\n");
    }
    for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
        if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {
            debug("processed: waiting_buffer[");
            debug_dec(waiting_buffer_tail);
            debug("] = ");
            debug_record(waiting_buffer[waiting_buffer_tail]);
            debug("\n\n");
        } else {
            break;
        }
    }
    if (!IS_NOEVENT(record.event)) {
        debug("\n");
    }
}

/** \brief Tapping
 *
 * Rule: Tap key is typed(pressed and released) within TAPPING_TERM.
 *       (without interfering by typing other key)
 */
/* return true when key event is processed or consumed. */
bool process_tapping(keyrecord_t *keyp) {
    keyevent_t event = keyp->event;

    // if tapping
    if (IS_TAPPING_PRESSED()) {
        if (WITHIN_TAPPING_TERM(event)) {
            if (tapping_key.tap.count == 0) {
                if (IS_TAPPING_KEY(event.key) && !event.pressed) {
                    // first tap!
                    debug("Tapping: First tap(0->1).\n");
                    tapping_key.tap.count = 1;
                    debug_tapping_key();
                    process_record(&tapping_key);

                    // copy tapping state
                    keyp->tap = tapping_key.tap;
                    // enqueue
                    return false;
                }
                /* Process a key typed within TAPPING_TERM
                 * This can register the key before settlement of tapping,
                 * useful for long TAPPING_TERM but may prevent fast typing.
                 */
#    if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY)
                else if (((
#        ifdef TAPPING_TERM_PER_KEY
                              get_tapping_term(get_event_keycode(tapping_key.event, false), keyp)
#        else
                              TAPPING_TERM
#        endif
                              >= 500)

#        ifdef PERMISSIVE_HOLD_PER_KEY
                          || get_permissive_hold(get_event_keycode(tapping_key.event, false), keyp)
#        elif defined(PERMISSIVE_HOLD)
                          || true
#        endif
                              ) &&
                         IS_RELEASED(event) && waiting_buffer_typed(event)) {
                    debug("Tapping: End. No tap. Interfered by typing key\n");
                    process_record(&tapping_key);
                    tapping_key = (keyrecord_t){};
                    debug_tapping_key();
                    // enqueue
                    return false;
                }
#    endif
                /* Process release event of a key pressed before tapping starts
                 * Without this unexpected repeating will occur with having fast repeating setting
                 * https://github.com/tmk/tmk_keyboard/issues/60
                 */
                else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) {
                    // Modifier should be retained till end of this tapping.
                    action_t action = layer_switch_get_action(event.key);
                    switch (action.kind.id) {
                        case ACT_LMODS:
                        case ACT_RMODS:
                            if (action.key.mods && !action.key.code) return false;
                            if (IS_MOD(action.key.code)) return false;
                            break;
                        case ACT_LMODS_TAP:
                        case ACT_RMODS_TAP:
                            if (action.key.mods && keyp->tap.count == 0) return false;
                            if (IS_MOD(action.key.code)) return false;
                            break;
                    }
                    // Release of key should be process immediately.
                    debug("Tapping: release event of a key pressed before tapping\n");
                    process_record(keyp);
                    return true;
                } else {
                    // set interrupted flag when other key preesed during tapping
                    if (event.pressed) {
                        tapping_key.tap.interrupted = true;
                    }
                    // enqueue
                    return false;
                }
            }
            // tap_count > 0
            else {
                if (IS_TAPPING_KEY(event.key) && !event.pressed) {
                    debug("Tapping: Tap release(");
                    debug_dec(tapping_key.tap.count);
                    debug(")\n");
                    keyp->tap = tapping_key.tap;
                    process_record(keyp);
                    tapping_key = *keyp;
                    debug_tapping_key();
                    return true;
                } else if (is_tap_key(event.key) && event.pressed) {
                    if (tapping_key.tap.count > 1) {
                        debug("Tapping: Start new tap with releasing last tap(>1).\n");
                        // unregister key
                        process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false});
                    } else {
                        debug("Tapping: Start while last tap(1).\n");
                    }
                    tapping_key = *keyp;
                    waiting_buffer_scan_tap();
                    debug_tapping_key();
                    return true;
                } else {
                    if (!IS_NOEVENT(event)) {
                        debug("Tapping: key event while last tap(>0).\n");
                    }
                    process_record(keyp);
                    return true;
                }
            }
        }
        // after TAPPING_TERM
        else {
            if (tapping_key.tap.count == 0) {
                debug("Tapping: End. Timeout. Not tap(0): ");
                debug_event(event);
                debug("\n");
                process_record(&tapping_key);
                tapping_key = (keyrecord_t){};
                debug_tapping_key();
                return false;
            } else {
                if (IS_TAPPING_KEY(event.key) && !event.pressed) {
                    debug("Tapping: End. last timeout tap release(>0).");
                    keyp->tap = tapping_key.tap;
                    process_record(keyp);
                    tapping_key = (keyrecord_t){};
                    return true;
                } else if (is_tap_key(event.key) && event.pressed) {
                    if (tapping_key.tap.count > 1) {
                        debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
                        // unregister key
                        process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false});
                    } else {
                        debug("Tapping: Start while last timeout tap(1).\n");
                    }
                    tapping_key = *keyp;
                    waiting_buffer_scan_tap();
                    debug_tapping_key();
                    return true;
                } else {
                    if (!IS_NOEVENT(event)) {
                        debug("Tapping: key event while last timeout tap(>0).\n");
                    }
                    process_record(keyp);
                    return true;
                }
            }
        }
    } else if (IS_TAPPING_RELEASED()) {
        if (WITHIN_TAPPING_TERM(event)) {
            if (event.pressed) {
                if (IS_TAPPING_KEY(event.key)) {
//#    ifndef TAPPING_FORCE_HOLD
#    if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
                    if (
#        ifdef TAPPING_FORCE_HOLD_PER_KEY
                        !get_tapping_force_hold(get_event_keycode(tapping_key.event, false), keyp) &&
#        endif
                        !tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
                        // sequential tap.
                        keyp->tap = tapping_key.tap;
                        if (keyp->tap.count < 15) keyp->tap.count += 1;
                        debug("Tapping: Tap press(");
                        debug_dec(keyp->tap.count);
                        debug(")\n");
                        process_record(keyp);
                        tapping_key = *keyp;
                        debug_tapping_key();
                        return true;
                    }
#    endif
                    // FIX: start new tap again
                    tapping_key = *keyp;
                    return true;
                } else if (is_tap_key(event.key)) {
                    // Sequential tap can be interfered with other tap key.
                    debug("Tapping: Start with interfering other tap.\n");
                    tapping_key = *keyp;
                    waiting_buffer_scan_tap();
                    debug_tapping_key();
                    return true;
                } else {
                    // should none in buffer
                    // FIX: interrupted when other key is pressed
                    tapping_key.tap.interrupted = true;
                    process_record(keyp);
                    return true;
                }
            } else {
                if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n");
                process_record(keyp);
                return true;
            }
        } else {
            // FIX: process_action here?
            // timeout. no sequential tap.
            debug("Tapping: End(Timeout after releasing last tap): ");
            debug_event(event);
            debug("\n");
            tapping_key = (keyrecord_t){};
            debug_tapping_key();
            return false;
        }
    }
    // not tapping state
    else {
        if (event.pressed && is_tap_key(event.key)) {
            debug("Tapping: Start(Press tap key).\n");
            tapping_key = *keyp;
            process_record_tap_hint(&tapping_key);
            waiting_buffer_scan_tap();
            debug_tapping_key();
            return true;
        } else {
            process_record(keyp);
            return true;
        }
    }
}

/** \brief Waiting buffer enq
 *
 * FIXME: Needs docs
 */
bool waiting_buffer_enq(keyrecord_t record) {
    if (IS_NOEVENT(record.event)) {
        return true;
    }

    if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
        debug("waiting_buffer_enq: Over flow.\n");
        return false;
    }

    waiting_buffer[waiting_buffer_head] = record;
    waiting_buffer_head                 = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;

    debug("waiting_buffer_enq: ");
    debug_waiting_buffer();
    return true;
}

/** \brief Waiting buffer clear
 *
 * FIXME: Needs docs
 */
void waiting_buffer_clear(void) {
    waiting_buffer_head = 0;
    waiting_buffer_tail = 0;
}

/** \brief Waiting buffer typed
 *
 * FIXME: Needs docs
 */
bool waiting_buffer_typed(keyevent_t event) {
    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
        if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
            return true;
        }
    }
    return false;
}

/** \brief Waiting buffer has anykey pressed
 *
 * FIXME: Needs docs
 */
__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {
    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
        if (waiting_buffer[i].event.pressed) return true;
    }
    return false;
}

/** \brief Scan buffer for tapping
 *
 * FIXME: Needs docs
 */
void waiting_buffer_scan_tap(void) {
    // tapping already is settled
    if (tapping_key.tap.count > 0) return;
    // invalid state: tapping_key released && tap.count == 0
    if (!tapping_key.event.pressed) return;

    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
        if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) {
            tapping_key.tap.count       = 1;
            waiting_buffer[i].tap.count = 1;
            process_record(&tapping_key);

            debug("waiting_buffer_scan_tap: found at [");
            debug_dec(i);
            debug("]\n");
            debug_waiting_buffer();
            return;
        }
    }
}

/** \brief Tapping key debug print
 *
 * FIXME: Needs docs
 */
static void debug_tapping_key(void) {
    debug("TAPPING_KEY=");
    debug_record(tapping_key);
    debug("\n");
}

/** \brief Waiting buffer debug print
 *
 * FIXME: Needs docs
 */
static void debug_waiting_buffer(void) {
    debug("{ ");
    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
        debug("[");
        debug_dec(i);
        debug("]=");
        debug_record(waiting_buffer[i]);
        debug(" ");
    }
    debug("}\n");
}

#endif

~13行

include 宣言である

~36行

各種マクロや、関数の定義である

IS_TAPPING()

tapping_key が有効な event を有しているかを判定する

IS_TAPPING_PRESSED()

tapping_key が有効な event を有していて、押されたかを判定する

IS_TAPPING_RELEASED()

tapping_key が有効な event を有していて、離されたかを判定する

IS_TAPPING_KEY(k)

tapping_key が有効な event を有していて、 k と同じかを判定する

uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record)

TAPPING_TERM を返す関数である
書き換え可能

WITHIN_TAPPING_TERM(e)

e (keyevent_t) が tapping_key から TAPPING_TERM 内に発生したかを判定する

bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record)

ここが詳しい

docs.qmk.fm

bool get_permissive_hold(uint16_t keycode, keyrecord_t *record)

ここが詳しい

docs.qmk.fm

~50行

変数の宣言、初期化と、プロトタイプ宣言である

static keyrecord_t tapping_key

タッピング処理の対象となるキー

static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE]

tapping_key の後にタップされたキーを溜める配列
WAITING_BUFFER_SIZE8

static uint8_t waiting_buffer_head

waiting_buffer[] の最初のインデックス

static uint8_t waiting_buffer_tail

waiting_buffer[] の最後のインデックス

~最終行

各関数について見ていこう
プロトタイプ宣言されている関数を下から見ていく

static void debug_waiting_buffer(void)

waiting_buffer[] の中身を順番に表示する関数である

static void debug_tapping_key(void)

tapping_key の中身を表示する関数である

void waiting_buffer_scan_tap(void)

tapping_key と同じキーで、処理されていないキーが waiting_buffer[] に存在するか確認する関数である
tapping_key.tap.count > 0 のときは処理されない
また、 waiting_buffer[] に同じキーが存在しても、そのキーが tapping_keyTAPPING_TERM 内にタップされていない場合も処理されない

処理条件に合致するキーがあった場合、 tapping_key と該当の waiting_buffer[] 内のキーの .tap.count1 にセットされ、 tapping_keyprocess_record() に渡される

bool waiting_buffer_has_anykey_pressed(void)

waiting_buffer[] 内に .event.pressed == true であるキーがあるか確認する関数である

bool waiting_buffer_typed(keyevent_t event)

waiting_buffer[] 内に渡された event と同じキー、かつ、 .pressed が異なるキーがあるか確認する関数である

void waiting_buffer_clear(void)

waiting_buffer[] を初期化する関数である
厳密には、 waiting_buffer_head, waiting_buffer_tail0 にして、 waiting_buffer[] に何らかの処理は行っていない

bool waiting_buffer_enq(keyrecord_t record)

waiting_buffer[] に新たな要素 (record) を追加する関数である
成功したら True を、失敗したら False を返す
record が有効な event を持っていない場合実行されず、正常終了する

WAITING_BUFFER_SIZE = 8 より、0番目 -> 1番目 -> ... -> 6番目 -> 7番目 -> 0番目 -> ... というように順番にループしながら要素を追加・上書きしていく
なお、何番目の要素に追加するかは waiting_buffer_head による

waiting_buffer_head の次のインデックスが waiting_buffer_tail と同じになる場合は、オーバーフローとして異常終了する

bool process_tapping(keyrecord_t *keyp)

タッピングの処理を行う関数である
処理が終了している場合や、 TAPPING_TERM が過ぎた場合は True を返す
タッピングが開始された時の処理、タッピング中の処理、タッピング中に別のキーイベントが発生した場合の処理などが書かれている
詳しくはコードを読んでいただきたい

void action_tapping_process(keyrecord_t record)

record のタッピング処理や waiting_buffer[] のタッピング処理を行う関数である

action_util.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 "report.h"

#ifdef __cplusplus
extern "C" {
#endif

extern report_keyboard_t *keyboard_report;

void send_keyboard_report(void);

/* key */
inline void add_key(uint8_t key) { add_key_to_report(keyboard_report, key); }

inline void del_key(uint8_t key) { del_key_from_report(keyboard_report, key); }

inline void clear_keys(void) { clear_keys_from_report(keyboard_report); }

/* modifier */
uint8_t get_mods(void);
void    add_mods(uint8_t mods);
void    del_mods(uint8_t mods);
void    set_mods(uint8_t mods);
void    clear_mods(void);

/* weak modifier */
uint8_t get_weak_mods(void);
void    add_weak_mods(uint8_t mods);
void    del_weak_mods(uint8_t mods);
void    set_weak_mods(uint8_t mods);
void    clear_weak_mods(void);

/* macro modifier */
uint8_t get_macro_mods(void);
void    add_macro_mods(uint8_t mods);
void    del_macro_mods(uint8_t mods);
void    set_macro_mods(uint8_t mods);
void    clear_macro_mods(void);

/* oneshot modifier */
uint8_t get_oneshot_mods(void);
void    add_oneshot_mods(uint8_t mods);
void    del_oneshot_mods(uint8_t mods);
void    set_oneshot_mods(uint8_t mods);
void    clear_oneshot_mods(void);
bool    has_oneshot_mods_timed_out(void);

uint8_t get_oneshot_locked_mods(void);
void    set_oneshot_locked_mods(uint8_t mods);
void    clear_oneshot_locked_mods(void);

typedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t;
void    set_oneshot_layer(uint8_t layer, uint8_t state);
uint8_t get_oneshot_layer(void);
void    clear_oneshot_layer_state(oneshot_fullfillment_t state);
void    reset_oneshot_layer(void);
bool    is_oneshot_layer_active(void);
uint8_t get_oneshot_layer_state(void);
bool    has_oneshot_layer_timed_out(void);
bool    has_oneshot_swaphands_timed_out(void);

void oneshot_locked_mods_changed_user(uint8_t mods);
void oneshot_locked_mods_changed_kb(uint8_t mods);
void oneshot_mods_changed_user(uint8_t mods);
void oneshot_mods_changed_kb(uint8_t mods);
void oneshot_layer_changed_user(uint8_t layer);
void oneshot_layer_changed_kb(uint8_t layer);

/* inspect */
uint8_t has_anymod(void);

#ifdef SWAP_HANDS_ENABLE
void set_oneshot_swaphands(void);
void release_oneshot_swaphands(void);
void use_oneshot_swaphands(void);
void clear_oneshot_swaphands(void);
#endif

#ifdef __cplusplus
}
#endif

モディファイア系を処理する関数のプロトタイプが宣言されている

action_util.c

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/>.
*/
#include "host.h"
#include "report.h"
#include "debug.h"
#include "action_util.h"
#include "action_layer.h"
#include "timer.h"
#include "keycode_config.h"

extern keymap_config_t keymap_config;

static uint8_t real_mods  = 0;
static uint8_t weak_mods  = 0;
static uint8_t macro_mods = 0;

#ifdef USB_6KRO_ENABLE
#    define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
#    define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
#    define RO_INC(a) RO_ADD(a, 1)
#    define RO_DEC(a) RO_SUB(a, 1)
static int8_t cb_head  = 0;
static int8_t cb_tail  = 0;
static int8_t cb_count = 0;
#endif

// TODO: pointer variable is not needed
// report_keyboard_t keyboard_report = {};
report_keyboard_t *keyboard_report = &(report_keyboard_t){};

extern inline void add_key(uint8_t key);
extern inline void del_key(uint8_t key);
extern inline void clear_keys(void);

#ifndef NO_ACTION_ONESHOT
static uint8_t oneshot_mods        = 0;
static uint8_t oneshot_locked_mods = 0;
uint8_t        get_oneshot_locked_mods(void) { return oneshot_locked_mods; }
void           set_oneshot_locked_mods(uint8_t mods) {
    if (mods != oneshot_locked_mods) {
        oneshot_locked_mods = mods;
        oneshot_locked_mods_changed_kb(oneshot_locked_mods);
    }
}
void clear_oneshot_locked_mods(void) {
    if (oneshot_locked_mods) {
        oneshot_locked_mods = 0;
        oneshot_locked_mods_changed_kb(oneshot_locked_mods);
    }
}
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
static uint16_t oneshot_time = 0;
bool            has_oneshot_mods_timed_out(void) { return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT; }
#    else
bool has_oneshot_mods_timed_out(void) { return false; }
#    endif
#endif

/* oneshot layer */
#ifndef NO_ACTION_ONESHOT
/** \brief oneshot_layer_data bits
 * LLLL LSSS
 * where:
 *   L => are layer bits
 *   S => oneshot state bits
 */
static int8_t oneshot_layer_data = 0;

inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; }
inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; }

#    ifdef SWAP_HANDS_ENABLE
enum {
    SHO_OFF,
    SHO_ACTIVE,   // Swap hands button was pressed, and we didn't send any swapped keys yet
    SHO_PRESSED,  // Swap hands button is currently pressed
    SHO_USED,     // Swap hands button is still pressed, and we already sent swapped keys
} swap_hands_oneshot = SHO_OFF;
#    endif

#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
static uint16_t oneshot_layer_time = 0;
inline bool     has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); }
#        ifdef SWAP_HANDS_ENABLE
static uint16_t oneshot_swaphands_time = 0;
inline bool     has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE); }
#        endif
#    endif

#    ifdef SWAP_HANDS_ENABLE

void set_oneshot_swaphands(void) {
    swap_hands_oneshot = SHO_PRESSED;
    swap_hands         = true;
#        if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
    oneshot_swaphands_time = timer_read();
    if (oneshot_layer_time != 0) {
        oneshot_layer_time = oneshot_swaphands_time;
    }
#        endif
}

void release_oneshot_swaphands(void) {
    if (swap_hands_oneshot == SHO_PRESSED) {
        swap_hands_oneshot = SHO_ACTIVE;
    }
    if (swap_hands_oneshot == SHO_USED) {
        clear_oneshot_swaphands();
    }
}

void use_oneshot_swaphands(void) {
    if (swap_hands_oneshot == SHO_PRESSED) {
        swap_hands_oneshot = SHO_USED;
    }
    if (swap_hands_oneshot == SHO_ACTIVE) {
        clear_oneshot_swaphands();
    }
}

void clear_oneshot_swaphands(void) {
    swap_hands_oneshot = SHO_OFF;
    swap_hands         = false;
#        if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
    oneshot_swaphands_time = 0;
#        endif
}

#    endif

/** \brief Set oneshot layer
 *
 * FIXME: needs doc
 */
void set_oneshot_layer(uint8_t layer, uint8_t state) {
    oneshot_layer_data = layer << 3 | state;
    layer_on(layer);
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
    oneshot_layer_time = timer_read();
#    endif
    oneshot_layer_changed_kb(get_oneshot_layer());
}
/** \brief Reset oneshot layer
 *
 * FIXME: needs doc
 */
void reset_oneshot_layer(void) {
    oneshot_layer_data = 0;
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
    oneshot_layer_time = 0;
#    endif
    oneshot_layer_changed_kb(get_oneshot_layer());
}
/** \brief Clear oneshot layer
 *
 * FIXME: needs doc
 */
void clear_oneshot_layer_state(oneshot_fullfillment_t state) {
    uint8_t start_state = oneshot_layer_data;
    oneshot_layer_data &= ~state;
    if (!get_oneshot_layer_state() && start_state != oneshot_layer_data) {
        layer_off(get_oneshot_layer());
        reset_oneshot_layer();
    }
}
/** \brief Is oneshot layer active
 *
 * FIXME: needs doc
 */
bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); }
#endif

/** \brief Send keyboard report
 *
 * FIXME: needs doc
 */
void send_keyboard_report(void) {
    keyboard_report->mods = real_mods;
    keyboard_report->mods |= weak_mods;
    keyboard_report->mods |= macro_mods;
#ifndef NO_ACTION_ONESHOT
    if (oneshot_mods) {
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
        if (has_oneshot_mods_timed_out()) {
            dprintf("Oneshot: timeout\n");
            clear_oneshot_mods();
        }
#    endif
        keyboard_report->mods |= oneshot_mods;
        if (has_anykey(keyboard_report)) {
            clear_oneshot_mods();
        }
    }

#endif
    host_keyboard_send(keyboard_report);
}

/** \brief Get mods
 *
 * FIXME: needs doc
 */
uint8_t get_mods(void) { return real_mods; }
/** \brief add mods
 *
 * FIXME: needs doc
 */
void add_mods(uint8_t mods) { real_mods |= mods; }
/** \brief del mods
 *
 * FIXME: needs doc
 */
void del_mods(uint8_t mods) { real_mods &= ~mods; }
/** \brief set mods
 *
 * FIXME: needs doc
 */
void set_mods(uint8_t mods) { real_mods = mods; }
/** \brief clear mods
 *
 * FIXME: needs doc
 */
void clear_mods(void) { real_mods = 0; }

/** \brief get weak mods
 *
 * FIXME: needs doc
 */
uint8_t get_weak_mods(void) { return weak_mods; }
/** \brief add weak mods
 *
 * FIXME: needs doc
 */
void add_weak_mods(uint8_t mods) { weak_mods |= mods; }
/** \brief del weak mods
 *
 * FIXME: needs doc
 */
void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; }
/** \brief set weak mods
 *
 * FIXME: needs doc
 */
void set_weak_mods(uint8_t mods) { weak_mods = mods; }
/** \brief clear weak mods
 *
 * FIXME: needs doc
 */
void clear_weak_mods(void) { weak_mods = 0; }

/* macro modifier */
/** \brief get macro mods
 *
 * FIXME: needs doc
 */
uint8_t get_macro_mods(void) { return macro_mods; }
/** \brief add macro mods
 *
 * FIXME: needs doc
 */
void add_macro_mods(uint8_t mods) { macro_mods |= mods; }
/** \brief del macro mods
 *
 * FIXME: needs doc
 */
void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; }
/** \brief set macro mods
 *
 * FIXME: needs doc
 */
void set_macro_mods(uint8_t mods) { macro_mods = mods; }
/** \brief clear macro mods
 *
 * FIXME: needs doc
 */
void clear_macro_mods(void) { macro_mods = 0; }

#ifndef NO_ACTION_ONESHOT
/** \brief get oneshot mods
 *
 * FIXME: needs doc
 */
uint8_t get_oneshot_mods(void) { return oneshot_mods; }

void add_oneshot_mods(uint8_t mods) {
    if ((oneshot_mods & mods) != mods) {
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
        oneshot_time = timer_read();
#    endif
        oneshot_mods |= mods;
        oneshot_mods_changed_kb(mods);
    }
}

void del_oneshot_mods(uint8_t mods) {
    if (oneshot_mods & mods) {
        oneshot_mods &= ~mods;
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
        oneshot_time = oneshot_mods ? timer_read() : 0;
#    endif
        oneshot_mods_changed_kb(oneshot_mods);
    }
}

/** \brief set oneshot mods
 *
 * FIXME: needs doc
 */
void set_oneshot_mods(uint8_t mods) {
    if (oneshot_mods != mods) {
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
        oneshot_time = timer_read();
#    endif
        oneshot_mods = mods;
        oneshot_mods_changed_kb(mods);
    }
}
/** \brief clear oneshot mods
 *
 * FIXME: needs doc
 */
void clear_oneshot_mods(void) {
    if (oneshot_mods) {
        oneshot_mods = 0;
#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
        oneshot_time = 0;
#    endif
        oneshot_mods_changed_kb(oneshot_mods);
    }
}
#endif

/** \brief Called when the one shot modifiers have been changed.
 *
 * \param mods Contains the active modifiers active after the change.
 */
__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {}

/** \brief Called when the locked one shot modifiers have been changed.
 *
 * \param mods Contains the active modifiers active after the change.
 */
__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) { oneshot_locked_mods_changed_user(mods); }

/** \brief Called when the one shot modifiers have been changed.
 *
 * \param mods Contains the active modifiers active after the change.
 */
__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {}

/** \brief Called when the one shot modifiers have been changed.
 *
 * \param mods Contains the active modifiers active after the change.
 */
__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) { oneshot_mods_changed_user(mods); }

/** \brief Called when the one shot layers have been changed.
 *
 * \param layer Contains the layer that is toggled on, or zero when toggled off.
 */
__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {}

/** \brief Called when the one shot layers have been changed.
 *
 * \param layer Contains the layer that is toggled on, or zero when toggled off.
 */
__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) { oneshot_layer_changed_user(layer); }

/** \brief inspect keyboard state
 *
 * FIXME: needs doc
 */
uint8_t has_anymod(void) { return bitpop(real_mods); }

モディファイア系の処理をする関数の実体が記述されている

まとめ

action_tapping.haction_util.h を探検した
action_tapping.c は条件分岐が多く、かなり苦労した

次回予告

ついに action.c を探検していこうと思う
かなりの量があるので、気合を入れて探検する必要があるだろう