Skip to content

Instantly share code, notes, and snippets.

@zk-phi
Created August 29, 2018 04:00
Show Gist options
  • Save zk-phi/1852dc6dd198c8fa8e406ad04841a666 to your computer and use it in GitHub Desktop.
Save zk-phi/1852dc6dd198c8fa8e406ad04841a666 to your computer and use it in GitHub Desktop.
action_tapping.c

TAPPING_TERM コードリーディング

コアのエントリポイント

keyboard_task (keyboard.c)

  • キーマトリックスを読み取って、変化のあったそれぞれのキーについて action_exec を呼ぶ

  • 引数は (key_event_t){ .key = { .row, .col }, .pressed, .time }

    • 変化したキーの位置と、変化が押し下げか押し上げかと、変化の時刻
  • 一つのキーにも変化がなかった場合、 action_exec が1度だけ呼ばれる

    • 引数はダミーの key_event "TICK"
      • (key_event_t){ .key = { .row = 255, .col = 255 }, .pressed = false, .time = }

action_exec (action.c)

  • 渡ってきた key_event_t を (key_record_t){ .event, .tap = (tap_t){ .interrupted, .count } } でラップ

    • .interrupted は現時点で不明
    • tapping key が押されてから TAPPING_TERM 以内に別のキーが押された場合に true になるっぽい
    • .count は押し下げられた時点では 0 、押し上げられると 1 になるっぽい
      • 2 以上にはなるのか?これもまだ現時点で不明
        • TAPPING_TOGGLE ?のために?2以上もカウントするっぽい
  • この key_record を action_tapping_process に投げる

    • tapping 機能が無効になっている場合は直接 process_record に投げる
    • (action_tapping_process も結局 process_record に良い感じの key_record を投げる)

process_record (action.c)

  • store_or_get_action してそれを process_action に投げる

store_or_get_action (action_layer.c)

  • PREVENT_STUCK_MODIFIERS がセットされていない場合、または process_record_nocache 経由で呼ばれた場合

    • たんにキーマップからキーに対応するアクションを探して返す
  • それ以外の場合

    • もし渡されたのが押し上げイベントだった場合
      • 現在のレイヤーを記録してから、キーマップからキーに対応するアクションを探して返す
    • 押し下げイベントだった場合
      • 「押し上げイベントが起こった時点のレイヤーにおける」アクションを探して返す

process_action (action.c)

  • キーの押し下げ・押し上げに応じて適切なキーコードを生成する

  • count が 0 (押し下げのみ) か 1 以上か (押して離す) によって、レイヤーを切り替えたり単発キーを入力したり切り替わるみたい


難しいので後で読む

  • もしイベントが押し下げだったら、 weak mod (直後の1キーだけ有効な mod ?) をクリアしておく

  • ワンショットモディファイアのなんか前処理 (省略)

  • アクションの種類によって分岐

    • 通常のキー入力 (通常の MOD 含む) の場合
      • 押し下げの場合
        • MOD を適切にセット
        • send_keyboard_report を呼び出す (キーボードの状態を送信? ... まだ追えてない)
      • 押し上げの場合
        • MOD を適切に解除
        • send_keyboard_report を呼び出す
    • MOD TAP の場合
      • ワンショットの場合
        • 押し下げの場合
          • 初の押し下げの場合 (tap_count == 0)
            • MOD を適切にセット
          • 二度目の押し下げの場合 (tap_count == 1)
            • MOD をワンショットにセット
          • ONESHOT_TAP_TOGGLE 回目の押し下げの場合 (tap_count == ONESHOT_TAP_TOGGLE)
            • ワンショット MOD を解除して別の MOD を?セット
          • それ以外の場合 (2回目以降 ONESHOT_TAP_TOGGLE 回未満?)
            • たんに MOD をセット
        • 押し上げの場合
          • 初の押し下げの場合
            • ワンショット MOD をクリア、 MOD もクリア
            • ...
    • トグルの場合 (省略)
      • ... (読めてない)

TAPPING 系の処理 (action_tapping.c)

action_tapping.c のグローバル変数

  • 直近の tapping key (長押しで機能が変化するキー) 操作を記録するための tapping_key (keyrecord_t)

  • tapping key の処理中に後回しにしたイベントをためておくキュー waiting_buffer (keyrecord_t[])

    • waiting_buffer になにかが enqueue されるのは何かしらの tapping key の処理途中だけ
      • たとえば、あるキー X の押し上げ処理時に、対応する押し下げイベントがキューにいないこともある
        • tapping key ではないキー X を押し下げ
        • tapping key Y を押し下げ
        • X 押し上げ

action_tapping_process (action_tapping.c)

  • 渡された key_record を process_tapping に投げる

    • もし false が返ってきた場合(処理に失敗した場合)は waiting_buffer に enqueue しておく
  • waiting_buffer に溜まっている各イベントについて、先頭から順に:

    • 改めて process_tapping に投げてみる
      • だめだったらそこでループ終了
    • たぶん TICK とかで TAPPING_TERM 時限発火したりする用?

process_tapping (action_tapping.c)

※失敗と明記されているもの以外は成功が返る

tapping_key がセットされてない場合 (else)

  • イベントが tap key で、かつ押し下げの場合

    • tapping_key に key_record をセット
    • waiting_buffer_scan_tap を呼ぶ (waiting_buffer で待っているキーの押し上げを処理)
      • ?? 初めての押し下げ時は waiting_buffer は空ではないのか ??
        • どうやら tapping_key をクリアするときに一緒に waiting_buffer をクリアするとは限らないっぽい
    • ※この時点では押し下げは処理されない (長押しか単押しか未確定で、この時点ではやることがない?)
  • それ以外の場合 (tap key でないか、押し上げイベント)

    • たんに process_record に投げる

waiting_buffer_scan_tap

  • もし tapping_key の count がまだ 0 なら (まだ押し上げを検出していないなら)
    • waiting_buffer から、「tapping_key から TAPPING_TERM 以内の、そのキーに対する押し上げイベント」を探して、もし見つかれば
      • そのイベントと tapping_key の count を 1 にする

これはなんなんだ?どっかのタイミングで bugfix として入ったコードっぽい (PR になっていないので謎)。

新しい tap key を開始する時に、前の tap key で queue に積まれたイベントをお掃除するっぽい?

もとはなかったコードなので、いったん読み飛ばしても良いかも

tapping_key が押し下げイベントだった場合 (IS_TAPPING_PRESSED())

新しいイベントが tapping_key から TAPPING_TERM 以内だった場合 (WITHIN_TAPPING_TERM(event))

tapping_key の押し上げがまだ検出されていない場合 (tapping_key.tap.count == 0)

次のどれか:

  • 新しいイベントが tapping_key に対応する押し上げイベントだった場合 (1 度目の tap 終了)

    • tapping_key とイベントの count を 1 にして process_record に投げ (キー押し上げを処理)、 失敗 (enqueue)
      • 押し上げイベントが enqueue される
      • !TAPPING_FORCE_HOLD のときに二回目のタップを特別扱いするので、そのために残すのかな?
        • けど TICK で処理されちゃいそうな気もする?
        • というか tapping_key にもセットしたままなわけだから、二回目以降のタップもそこでよくない?
  • PERMISSIVE_HOLD が有効で、waiting_buffer 内の押し下げに対応する押し上げイベントだった場合

    • (A Dn -> B Dn -> B Up と操作するとこうなる? B Dn で interrupt されてる気がするけど、合ってる?)
    • tapping_key を process_record に投げて、tapping_key を無にセットして、 失敗 (enqueue)
      • enqueue されるのは B Up の方
      • A Dn が長押しで確定なので押し下げを処理して、 tapping_key は次の tap key に備える?
  • waiting_buffer にない押し下げに対する押し上げイベントだった場合

    • (B Dn -> A Dn (tap key) -> B Up と操作するとこうなる?)
    • それが modifier key だったら 失敗 (enqueue)
      • modifier key の押し上げだけ処理を後回しにするのはなぜ?
    • さもなければ押し上げイベントを普通に処理 (process_record)
  • 押し下げイベントだった場合

    • (A Dn (tap key) -> B Dn と操作するとこうなる?)
    • tapping_key の interrupted を true にして、 失敗 (enqueue)
  • それ以外の場合 (PERMISSIVE_HOLD が無効かつ waiting_buffer に対応する押し上げ?)

    • 失敗 (enqueue)
      • PERMISSIVE_HOLD が無効だと、TAPPING_TERM 以内に A Dn -> B Dn -> B Up と打っても振る舞いが未確定
tapping_key の押し上げがもう検出されている場合 (else)
  • tapping_key に対する押し上げだった場合 (二度目の押し上げ…?)

    • そのイベントの tap を tapping_key.tap (count, interrupted) で上書きして、 process_record に投げる
    • tapping_key をそのイベントにセット
      • event の時刻が更新される、 TAPPING_TERM の処理とかに影響がある?
  • tap key の押し下げだった場合 (Shift Dn -> Shift Up -> Ctrl Dn とか?)

    • tapping_key.count が 2 以上だった場合、強制的に tapping_key の押し上げを発行する
    • 新しいイベントを tapping_key にセット
    • waiting_buffer_scan_tap を呼ぶ
  • それ以外の場合

    • たんに process_record に投げる

tapping_key から TAPPING_TERM 以上経っている場合 (else)

  • まだ tapping_key の押し上げが検出されていない場合 (タイムアウト)
    • tapping_key を process_record に投げてクリア後、 失敗 (enqueue)
      • 失敗しておくのは、例によって再び押し下げが来た時に !TAPPING_FORCE_HOLD の処理をするため?

ここから下は TAPPING_TERM 以内に tapping_key 押し上げ→何かしら押し下げ した場合と同じっぽい

  • tapping_key に対する押し上げイベントの場合 (タイムアウト済みの tap のおわり)

    • そのイベントの tap を tapping_key.tap を上書きして、 process_record に投げる
    • tapping_key をそのイベントにセット
  • tap key の押し下げだった場合 (Shift Dn -> タイムアウト -> Ctrl Dn とか?)

    • tapping_key.count が 2 以上だった場合、強制的に tapping_key の押し上げを発行する
    • 新しいイベントを tapping_key にセット
    • waiting_buffer_scan_tap を呼ぶ
  • それ以外の場合

    • たんに process_record に投げる

tapping_key が押し上げイベントだった場合 (IS_TAPPING_RELEASED())

新しいイベントが tapping_key から TAPPING_TERM 以内だった場合 (WITHIN_TAPPING_TERM(event))

  • 新しいイベントが押し上げイベントだった場合

    • たんに process_record に投げる
  • 新しいイベントが押し上げられた tapping_key の押し下げだった場合

    • TAPPING_FORCE_HOLD でなく、 tapping_key が interrupt されず (単押しとして) 押し上げられている場合
      • 新しいイベントの tap を tapping_key.tap で上書き
      • もし tap.count が 15 回未満なら count をインクリメント
      • process_record にイベントを投げる
    • 新しいイベントを tapping_key にセット
  • tap key の押し下げだった場合

    • 新しいイベントを tapping_key にセット
    • waiting_buffer_scan_tap を呼び出す
  • それ以外 (tap key 以外の押し下げ) の場合

    • たんに process_record に投げる

tapping_key から TAPPING_TERM 以上経っている場合 (else)

(process_action すべき?という FIXME がある)

  • tapping_key をクリアして 失敗 (enqueue)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment