You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

303 lines
13 KiB

  1. # マクロ
  2. <!---
  3. original document: 0.9.43:docs/feature_macros.md
  4. git diff 0.9.43 HEAD -- docs/feature_macros.md | cat
  5. -->
  6. マクロにより、1つのキーを押すだけで複数のキーストロークを送信することができます。QMK にはマクロを定義し使う方法が幾つかあります。これらはなんでもすることができます: よく使うフレーズの入力、コピーペースト、反復的なゲームの動き、あるいはコードを書くことさえ手助けします。
  7. !> **セキュリティの注意**: マクロを使って、パスワード、クレジットカード番号、その他の機密情報のいずれも送信することが可能ですが、それは非常に悪い考えです。あなたのキーボードを手に入れた人は誰でもテキストエディタを開いてその情報にアクセスすることができます。
  8. ## `SEND_STRING()` と `process_record_user`
  9. 単語またはフレーズを入力するキーが欲しい時があります。最も一般的な状況のために `SEND_STRING()` を提供しています。これは文字列(つまり、文字のシーケンス)を入力します。簡単にキーコードに変換することができる全ての ASCII 文字がサポートされています (例えば、`qmk 123\n\t`)。
  10. 以下は2キーのキーボードのための `keymap.c` の例です:
  11. ```c
  12. enum custom_keycodes {
  13. QMKBEST = SAFE_RANGE,
  14. };
  15. bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  16. switch (keycode) {
  17. case QMKBEST:
  18. if (record->event.pressed) {
  19. // キーコード QMKBEST が押された時
  20. SEND_STRING("QMK is the best thing ever!");
  21. } else {
  22. // キーコード QMKBEST が放された時
  23. }
  24. break;
  25. }
  26. return true;
  27. };
  28. const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  29. [0] = {
  30. {QMKBEST, KC_ESC},
  31. // ...
  32. },
  33. };
  34. ```
  35. ここで起きることは以下の通りです:
  36. 最初に他のキーコードで使用されていない範囲で新しいカスタムキーコードを定義します。
  37. 次に、`process_record_user` 関数を使います。これはキーが押されるか放されるたびに呼び出され、カスタムキーコードがアクティブかどうかを確認します。
  38. アクティブな場合、`SEND_STRING` マクロ (これは C プロセッサのマクロで、QMK のマクロと混同しないでください)を介して文字列 `"QMK is the best thing ever!"` をコンピュータに送信します。
  39. 呼び出し元に、処理したばかりのキー押下を通常通り(機能を置き換えたり変更したりしなかったので)処理し続けるよう指示するため、`true` を返します。
  40. 最後に、最初のボタンがマクロをアクティブにし、2番目のボタンが単なるエスケープボタンになるようにキーマップを定義します。
  41. 複数のマクロを追加することもできます。
  42. 以下のように、別のキーコードを追加し、switch 文に別の case ラベルを追加することで、それを行うことができます:
  43. ```c
  44. enum custom_keycodes {
  45. QMKBEST = SAFE_RANGE,
  46. QMKURL,
  47. MY_OTHER_MACRO,
  48. };
  49. bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  50. switch (keycode) {
  51. case QMKBEST:
  52. if (record->event.pressed) {
  53. // キーコード QMKBEST が押された時
  54. SEND_STRING("QMK is the best thing ever!");
  55. } else {
  56. // キーコード QMKBEST が放された時
  57. }
  58. break;
  59. case QMKURL:
  60. if (record->event.pressed) {
  61. // キーコード QMKURL が押された場合
  62. SEND_STRING("https://qmk.fm/\n");
  63. } else {
  64. // キーコード QMKURL が放された場合
  65. }
  66. break;
  67. case MY_OTHER_MACRO:
  68. if (record->event.pressed) {
  69. SEND_STRING(SS_LCTL("ac")); // 全てを選択しコピーします
  70. }
  71. break;
  72. }
  73. return true;
  74. };
  75. const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  76. [0] = {
  77. {MY_CUSTOM_MACRO, MY_OTHER_MACRO},
  78. // ...
  79. },
  80. };
  81. ```
  82. ### 高度なマクロ
  83. `process_record_user()` 関数のほかに、`post_process_record_user()` 関数があります。これは `process_record` の後に実行され、キーストロークが送信された後の処理に使用できます。これは例えば、通常のキーの前に押され、通常のキーの後で放されるキーがほしい場合に便利です。
  84. この例では、通常のキー入力を変更して、キーストロークが通常送信される前に `F22` が押されるようにし、キーが放された__後にのみ__ `F22` キーを放します。
  85. ```c
  86. static uint8_t f22_tracker;
  87. bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  88. switch (keycode) {
  89. case KC_A ... KC_F21: // F22 をスキップする方法に注意してください
  90. case KC_F23 ... KC_EXSEL: //exsel は修飾キーの直前のキーです
  91. if (record->event.pressed) {
  92. register_code(KC_F22); //これは F22 を押したことを送信することを意味します
  93. f22_tracker++;
  94. register_code(keycode);
  95. return false;
  96. }
  97. break;
  98. }
  99. return true;
  100. }
  101. void post_process_record_user(uint16_t keycode, keyrecord_t *record) {
  102. switch (keycode) {
  103. case KC_A ... KC_F21: // F22 をスキップする方法に注意してください
  104. case KC_F23 ... KC_EXSEL: //exsel は修飾キーの直前のキーです
  105. if (!record->event.pressed) {
  106. f22_tracker--;
  107. if (!f22_tracker) {
  108. unregister_code(KC_F22); //これは F22 を放したことを送信することを意味します
  109. }
  110. }
  111. break;
  112. }
  113. }
  114. ```
  115. ### タップ、ダウン、アップ
  116. `Ctrl` あるいは `Home` など、ソースコードに文字列として表記できないキーをマクロで使うこともできます。
  117. 以下のようにラップすることで任意のコードを送信することができます:
  118. * `SS_TAP()` キーを押して放します。
  119. * `SS_DOWN()` キーを押します (ただし、放しません)。
  120. * `SS_UP()` キーを放します。
  121. 例えば:
  122. SEND_STRING(SS_TAP(X_HOME));
  123. `KC_HOME` をタップします - プリフィックスが `X_``KC_` ではないことに注意してください。以下のように、他の文字列と組み合わせることもできます:
  124. SEND_STRING("VE"SS_TAP(X_HOME)"LO");
  125. これは "VE" に続けて `KC_HOME` をタップ、そして "LO" (新しい行の場合は "LOVE" と綴る)を送信します。
  126. 文字列に遅延を追加することもできます:
  127. * `SS_DELAY(msecs)` は指定されたミリ秒だけ遅らせます。
  128. 例えば:
  129. SEND_STRING("VE" SS_DELAY(1000) SS_TAP(X_HOME) "LO");
  130. これは "VE" 、1秒の遅延、`KC_HOME` をタップ、"LO" (新しい行の場合は "LOVE" と綴るが、中間に遅延がある) を送信します。
  131. 使用できるモッドショートカットもいくつかあります:
  132. * `SS_LCTL(文字列)`
  133. * `SS_LSFT(文字列)`
  134. * `SS_LALT(文字列)`、`SS_LOPT(文字列)`
  135. * `SS_LGUI(文字列)`、`SS_LCMD(文字列)`、`SS_LWIN(文字列)`
  136. * `SS_RCTL(文字列)`
  137. * `SS_RSFT(文字列)`
  138. * `SS_RALT(文字列)`、`SS_ROPT(文字列)`、`SS_ALGR(文字列)`
  139. * `SS_RGUI(文字列)`、`SS_RCMD(文字列)`、`SS_RWIN(文字列)`
  140. これらはそれぞれの修飾キーを押し、指定された文字列を送信してから、修飾キーを解放します。
  141. それらは以下のように使うことができます:
  142. SEND_STRING(SS_LCTL("a"));
  143. これは、左 Control +`a` (左 Control をダウンし、`a`、左 Control をアップ)を送信します - それらは文字列(例えば `"k"`)であり、`X_K` キーコードでは無いことに注意してください。
  144. ### 代替キーマップ
  145. デフォルトでは、QWERTY レイアウトの US キーマップを想定しています; それを変更したい場合(例えば OS がソフトウェア Colemak を使う場合)、キーマップのどこかに以下を含めます:
  146. ```c
  147. #include "sendstring_colemak.h"
  148. ```
  149. ### メモリ内の文字列
  150. 何らかの理由で文字列を操作していて、(リテラル、文字列定数の代わりに)生成したばかりのものを出力する必要がある場合は、以下のように `send_string()` を使うことができます:
  151. ```c
  152. char my_str[4] = "ok.";
  153. send_string(my_str);
  154. ```
  155. 上で定義したショートカットは `send_string()` では動作しないですが、必要に応じて別の行に分けることができます:
  156. ```c
  157. char my_str[4] = "ok.";
  158. SEND_STRING("I said: ");
  159. send_string(my_str);
  160. SEND_STRING(".."SS_TAP(X_END));
  161. ```
  162. ## 高度なマクロ関数 :id=advanced-macro-functions
  163. マクロの生成に役立つ関数が幾つかあります。マクロの中にかなり高度なコードを書くことができますが、機能が複雑になりすぎる場合は、代わりにカスタムキーコードを定義することをお勧めします。マクロはシンプルにしなければなりません。
  164. ?> 追加の機能として、[便利な関数](ja/ref_functions.md) の中で説明される関数を使うこともできます。例えば `reset_keyboard()` によりマクロの一部としてキーボードをリセットすることができます。
  165. ### `record->event.pressed`
  166. これでスイッチが押されているか放されているかどうかをテストすることができます。以下が例です。
  167. ```c
  168. if (record->event.pressed) {
  169. // キーダウン時
  170. } else {
  171. // キーアップ時
  172. }
  173. ```
  174. ### `register_code(<kc>);`
  175. これはコンピュータに `<kc>` キーダウンイベントを送信します。例として `KC_ESC`、`KC_C`、`KC_4` や、`KC_LSFT` と `KC_LGUI` のような修飾キーなどもあります。
  176. ### `unregister_code(<kc>);`
  177. `register_code` 関数と対応して、これは `<kc>` キーアップイベントをコンピュータに送信します。これを使わない場合、キーは送信されるまで押し続けられます。
  178. ### `tap_code(<kc>);`
  179. これは `register_code(<kc>)` を送信し、その後 `unregister_code(<kc>)` を送信します。押下とリリースイベントの両方を送信する場合に便利です (押し続けるのではなく、キーを"タップ"する)。
  180. タップの登録(解除)に問題がある場合、`config.h` ファイルで `#define TAP_CODE_DELAY 100` を設定することで、登録イベントと解除イベントの間に遅延を追加することができます。値はミリ秒です。
  181. ### `register_code16(<kc>);`、`unregister_code16(<kc>);`、`tap_code16(<kc>);`
  182. これらの関数は対応する通常の関数と同様に機能しますが、修飾キーで修飾されたキーコードを使うことができます (Shift、Alt、Control、GUI を適用)。
  183. 例えば、修飾キーを押して(`register_code()`して)、キーコードを押す(`register_code()`する)代わりに、`register_code16(S(KC_5));` を使うことができます。
  184. ### `clear_keyboard();`
  185. これは現在押されている全ての修飾キーとキーをクリアします。
  186. ### `clear_mods();`
  187. これは現在押されている全ての修飾キーをクリアします。
  188. ### `clear_keyboard_but_mods();`
  189. これは現在押されている修飾キー以外の全てのキーをクリアします。
  190. ## 高度な例:
  191. ### スーパー ALT↯TAB
  192. このマクロは `KC_LALT` を登録し、`KC_TAB` をタップして、1000ms 待ちます。キーが再度タップされると、別の `KC_TAB` が送信されます; タップが無い場合、`KC_LALT` が登録解除され、ウィンドウを切り替えることができます。
  193. ```c
  194. bool is_alt_tab_active = false; // keymap.c の先頭付近にこれを追加します
  195. uint16_t alt_tab_timer = 0; // すぐにそれらを使います
  196. enum custom_keycodes { // 素晴らしいキーコードを用意してください
  197. ALT_TAB = SAFE_RANGE,
  198. };
  199. bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  200. switch (keycode) { // これはキーコードを利用したつまらない作業のほとんどを行います。
  201. case ALT_TAB:
  202. if (record->event.pressed) {
  203. if (!is_alt_tab_active) {
  204. is_alt_tab_active = true;
  205. register_code(KC_LALT);
  206. }
  207. alt_tab_timer = timer_read();
  208. register_code(KC_TAB);
  209. } else {
  210. unregister_code(KC_TAB);
  211. }
  212. break;
  213. }
  214. return true;
  215. }
  216. void matrix_scan_user(void) { // とても重要なタイマー
  217. if (is_alt_tab_active) {
  218. if (timer_elapsed(alt_tab_timer) > 1000) {
  219. unregister_code(KC_LALT);
  220. is_alt_tab_active = false;
  221. }
  222. }
  223. }
  224. ```