settings for multi button mouse + macOS + Karabiner with complex modifications .json file
何がしたいか
腱鞘炎対策にマルチボタンのついてる縦型マウスを買ったが,macOSは一部のメーカを除いてマウスごとのドライバというものがなく,またデフォルトではmagic mouse(左右ボタン+中ボタン兼スクローラ)と同程度の動作しか利用できない,そのためKarabinerといった入出力の変換ソフトが必要になる
今回はマルチボタンの有効化とその簡単な割り当てを紹介するが,基本的に左・右・中のボタン(1番・2番・3番)はあんまりイジらない,既存の動作に依存や干渉の関係があると面倒なので
注意
Karabiner
は仕様の関係で割と動作が強力である(ほぼハードの動作と直結する)ため,下手をすると壊れて操作を受け付けなくなる可能性がある,なので自己責任で丁寧に作業をすること
一応はアプリごとに動作を変えられたりもするが,あくまでウィンドウがどこにあるか程度でしか判別してくれないので,もしそれがソフト側をイジるだけで解決する問題なら,なるべくソフト側だけをイジった方が良い
例えば
↓
インストール
cliで取得
brew install --cask karabiner-elements
これに限る
--cask
なので/Applications/Karabiner-Elements.app
と/Applications/Karabiner-EventViewer.app
があればok,起動しよう
起動,システムへのアクセス許可
初回起動の時点で色々と権限を要求されるので,システム環境設定
->セキュリティとプライバシー
から適宜に承認する
フォルダの確認
~/.config/karabiner/assets/complex_modifications
が勝手に生成されていればOK,その配下に定義に則って作成された任意のjson
ファイルを置くことで,自由に機能を追加できるし管理できる
実際なんだかんだでGUIには頼らずテキストで保存した方がいい,GUIだけでは複雑な挙動を検討できないし,ハードウェア設定をも考慮した本体プロファイルと分けないと混乱する
プログラムの確認
表からはKarabiner-Elements
とKarabiner-EventViewer
を起動できるが,前者は設定用・後者はデバッグ用に過ぎない
実際は初回起動において監視・制御のデーモンを裏で勝手に展開してくれる,次回起動からはログイン時に勝手にそれらが起動する,ログイン項目に登録する必要はない(ただし再読み込みはKarabiner-Elements
からやる必要がある)
設定
デバイス登録
有線・無線・Bluetoothは関係なく,使いたいマウスを接続したらKarabiner-Elements
->Devices
->Basic configuration
に移動,hoge Optional Mouse (fuga)
が見つかるはずなので,左端チェックボックスから有効化する
ついでにVendor ID
とProduct ID
をメモする,これらは後で条件設定の時に使いたい
Advanced
は特に要らない
基本設定
混乱回避のため,Simple Modifications
を空欄にし,Function keys
をリセットする(To Key
をそのまま割り振ると良い)
拡張ファイルの製作
以下に一例を示す
- 共通設定
- 5番のみで
tab
- 4番のみで
shift
+tab
- 5番のみで
- ブラウザのみ
- 1番+2番で
command
+r
- 5番+6番で
command
+w
- 1番+5番で
command
+click
- 1番+4番で
shift
+click
- 2番+5番で
command
+c
- 2番+4番で
command
+v
- 1番+2番で
{ "title": "MouseWithFiveButtons", "rules": [{ "description": "Define how how a mouse with 5 buttons works", "manipulators": [{ "description": "Rcmd+'w',for Browsers (safari, chrome, firefox, vivaldi, ...) and Editor(vscode, ...)", "conditions": [{ "type": "device_if", "identifiers": [{ "vendor_id": 7119, "product_id": 5, "description": "a mouse with 5 buttons" }] }, { "type": "frontmost_application_if", "bundle_identifiers": [ "^com\\.apple\\.Safari$", "^com\\.google\\.Chrome$", "^org\\.mozilla\\.firefox$", "^com\\.vivaldi\\.Vivaldi$", "^com\\.microsoft\\.VSCode$" ] } ], "type": "basic", "from": { "simultaneous": [{ "pointing_button": "button4" }, { "pointing_button": "button5" } ] }, "to": [{ "key_code": "w", "modifiers": [ "right_command" ] }] }, { "description": "Rcmd+'w',for Browsers (safari, chrome, firefox, vivaldi, ...) and Editor(vscode, ...)", "conditions": [{ "type": "device_if", "identifiers": [{ "vendor_id": 7119, "product_id": 5, "description": "a mouse with 5 buttons" }] }, { "type": "frontmost_application_if", "bundle_identifiers": [ "^com\\.apple\\.Safari$", "^com\\.google\\.Chrome$", "^org\\.mozilla\\.firefox$", "^com\\.vivaldi\\.Vivaldi$", "^com\\.microsoft\\.VSCode$" ] } ], "type": "basic", "from": { "simultaneous": [{ "pointing_button": "button1" }, { "pointing_button": "button2" } ] }, "to": [{ "key_code": "r", "modifiers": [ "right_command" ] }] }, { "description": "Lsft+Lclk, for Browsers (safari, chrome, firefox, vivaldi, ...) and Editor(vscode, ...)", "conditions": [{ "type": "device_if", "identifiers": [{ "vendor_id": 7119, "product_id": 5, "description": "a mouse with 5 buttons" }] }, { "type": "frontmost_application_if", "bundle_identifiers": [ "^com\\.apple\\.Safari$", "^com\\.google\\.Chrome$", "^org\\.mozilla\\.firefox$", "^com\\.vivaldi\\.Vivaldi$", "^com\\.microsoft\\.VSCode$" ] } ], "type": "basic", "from": { "simultaneous": [{ "pointing_button": "button1" }, { "pointing_button": "button4" } ] }, "to": [{ "pointing_button": "button1", "modifiers": [ "left_shift" ] }] }, { "description": "Lcmd+Lclk,for Browsers (safari, chrome, firefox, vivaldi, ...) and Editor(vscode, ...)", "conditions": [{ "type": "device_if", "identifiers": [{ "vendor_id": 7119, "product_id": 5, "description": "a mouse with 5 buttons" }] }, { "type": "frontmost_application_if", "bundle_identifiers": [ "^com\\.apple\\.Safari$", "^com\\.google\\.Chrome$", "^org\\.mozilla\\.firefox$", "^com\\.vivaldi\\.Vivaldi$", "^com\\.microsoft\\.VSCode$" ] } ], "type": "basic", "from": { "simultaneous": [{ "pointing_button": "button1" }, { "pointing_button": "button5" } ] }, "to": [{ "pointing_button": "button1", "modifiers": [ "left_command" ] }] }, { "description": "Rcmd+'v',for Browsers (safari, chrome, firefox, vivaldi, ...) and Editor(vscode, ...)", "conditions": [{ "type": "device_if", "identifiers": [{ "vendor_id": 7119, "product_id": 5, "description": "a mouse with 5 buttons" }] }, { "type": "frontmost_application_if", "bundle_identifiers": [ "^com\\.apple\\.Safari$", "^com\\.google\\.Chrome$", "^org\\.mozilla\\.firefox$", "^com\\.vivaldi\\.Vivaldi$", "^com\\.microsoft\\.VSCode$" ] } ], "type": "basic", "from": { "simultaneous": [{ "pointing_button": "button2" }, { "pointing_button": "button4" } ] }, "to": [{ "key_code": "v", "modifiers": [ "right_command" ] }] }, { "description": "Rcmd+'c',for Browsers (safari, chrome, firefox, vivaldi, ...) and Editor(vscode, ...)", "conditions": [{ "type": "device_if", "identifiers": [{ "vendor_id": 7119, "product_id": 5, "description": "a mouse with 5 buttons" }] }, { "type": "frontmost_application_if", "bundle_identifiers": [ "^com\\.apple\\.Safari$", "^com\\.google\\.Chrome$", "^org\\.mozilla\\.firefox$", "^com\\.vivaldi\\.Vivaldi$", "^com\\.microsoft\\.VSCode$" ] } ], "type": "basic", "from": { "simultaneous": [{ "pointing_button": "button2" }, { "pointing_button": "button5" } ] }, "to": [{ "key_code": "c", "modifiers": [ "right_command" ] }] }, { "description": "global settings for mouse button 4", "conditions": [{ "type": "device_if", "identifiers": [{ "vendor_id": 7119, "product_id": 5, "description": "a mouse with 5 buttons" }] }], "type": "basic", "from": { "pointing_button": "button4" }, "to": [{ "key_code": "tab", "modifiers": [ "right_shift" ] }] }, { "description": "global settings for mouse button 5", "conditions": [{ "type": "device_if", "identifiers": [{ "vendor_id": 7119, "product_id": 5, "description": "a mouse with 5 buttons" }] }], "type": "basic", "from": { "pointing_button": "button5" }, "to": [{ "key_code": "tab" }] } ] }] }
これを~/.config/karabiner/assets/complex_modifications
に作成する
作成したらKarabiner-Elements
->Complex Modifications
->Rules
->Add rule
を見る,構文ミスさえなければ表示されるので右端のEnable
ボタンでで有効化,ちなみにここにはファイル名は表示されないので注意(json
オブジェクトのtitle
とrules.description
のみ表示される)
うまく有効化できたらMisc
->Restart Karabiner-Elements
で再起動,これで完全に動作が反映されるはず
特徴
- 同時押し
simultaneous
によって修飾キー以外による同時押しに対応- 単一押しと使用するボタンやキーが被る場合は,
simultaneous
させたい動作についてjson
オブジェクト上に先に記述すること,後に記述すると単一押しの方が優先されて判定を受けてしまい,同時押しの方は問答無用で無視されてしまう - それぞれの動作の定義が
rules.manipulators
の配列に格納される形であるため,処理の優先度が前後関係で決定されている可能性がある,そのためなるべく少ないjson
ファイルに関連する設定をまとめた方がむしろ安心 - 実は
Karabiner-Elements
->Complex Modifications
->Parameters
から設定できる- マウスで使うにはあまりにもシビアなので,猶予時間は長めにとった方が良いかも
- 単一押しと使用するボタンやキーが被る場合は,
- アプリケーション判定
frontmost_application_if
によってブラウザ限定動作に対応- マルチディスプレイで別々のアプリをトップに表示すると両方とも有効と扱われるので注意,シングルディスプレイを想定した実装しかされていない(意図しない動作が思わず発生する可能性がある)
カスタマイズ
rules.manipulators[].conditions[].identifiers
には対応するvendor_id
とproduct_id
が必要,各自で確認- マウスのボタン配置も確認して使いやすい組み合わせを検討
rules.manipulators[].conditions[].bundle_identifiers
には対応するアプリのidentifiers
が必要各自で確認- アプリを起動して
lsappinfo info -only bundleid hoge
すれば確認できる
- アプリを起動して
解説
何を読めば理解できるか
公式ドキュメント
json
オブジェクトについて記載されている
判定時間の設定について
基本的には「キーを下げる」がpress
の定義であって,一般的にはto
にて処理が起爆される.後述のhalt
も使えるとは思うが動作は未確認,ドキュメントには例文どころか説明すら載ってないし使う価値もなさそう.
また「キーを(指定した時間より長く)下げ続ける」と処理が起爆するto_if_held_down
,「下げたキーを上げる」と処理が起爆するto_after_key_up
があり,これら2つは両立して存在できる.その場合,to_if_held_down
の中にhalt:true
を用いることで後続のto_after_key_up
をキャンセルする,すなわちキーを下げ続ける時間で処理を変えることが可能になる.
to_if_alone
は「キーを(指定した時間より短く)単独で下げ続ける」と処理が起爆する,長く押し続けると判定がto
に移行する.同様にhalt:true
でto_after_key_up
をキャンセルして処理を変えることが可能になる.
もしくはfrom.simultaneous
で修飾キー以外の複数入力を受け付けられるが,これはto
系のメンバではなくfrom
系のメンバであるために判定時間の概念が異なる.先に述べたようにrules.manipulators
の配列の順番がfrom
系のメンバの実行の優先順序になると推定されており,同じキーを利用した同時押しの判定をやりたいのであれば先に記述すべきである.
また包括されるキーの組み合わせを設定するのも全く推奨されない,例えばhoge
+huga
とhoge
を待ち受ける2つのfrom
オブジェクトがある場合,後者の設定をto_if_held_down
でも設定しない限りa
を下げて上げた瞬間に後者が起爆する.
総合的に考えると,今回の実装はえらく特殊なものである以上は,真似はしない方が良いだろう.
…実際に使うとなればKarabiner-Elements
->Complex Modifications
->Parameters
のto_if_alone_timeout_milliseconds
とsimultaneous_threshold_milliseconds
をイジるくらいで,これはデバイスの使用感に依存するだろう.
また変数をset_variable
で設定してvariable_if
やvariable_unless
で利用できるらしいが,使ったことがない…