complex modificationsのjson fileを利用したマルチボタンのマウス + macOS + Karabinerの設定

settings for multi button mouse + macOS + Karabiner with complex modifications .json file

何がしたいか

腱鞘炎対策にマルチボタンのついてる縦型マウスを買ったが,macOSは一部のメーカを除いてマウスごとのドライバというものがなく,またデフォルトではmagic mouse(左右ボタン+中ボタン兼スクローラ)と同程度の動作しか利用できない,そのためKarabinerといった入出力の変換ソフトが必要になる
今回はマルチボタンの有効化とその簡単な割り当てを紹介するが,基本的に左・右・中のボタン(1番・2番・3番)はあんまりイジらない,既存の動作に依存や干渉の関係があると面倒なので

注意

Karabinerは仕様の関係で割と動作が強力である(ほぼハードの動作と直結する)ため,下手をすると壊れて操作を受け付けなくなる可能性がある,なので自己責任で丁寧に作業をすること
一応はアプリごとに動作を変えられたりもするが,あくまでウィンドウがどこにあるか程度でしか判別してくれないので,もしそれがソフト側をイジるだけで解決する問題なら,なるべくソフト側だけをイジった方が良い
例えば ↓

soluna-eureka.hatenablog.com

インストール

cliで取得

brew install --cask karabiner-elements

formulae.brew.sh

これに限る
--caskなので/Applications/Karabiner-Elements.app/Applications/Karabiner-EventViewer.appがあればok,起動しよう

起動,システムへのアクセス許可

初回起動の時点で色々と権限を要求されるので,システム環境設定->セキュリティとプライバシーから適宜に承認する

フォルダの確認

~/.config/karabiner/assets/complex_modificationsが勝手に生成されていればOK,その配下に定義に則って作成された任意のjsonファイルを置くことで,自由に機能を追加できるし管理できる
実際なんだかんだでGUIには頼らずテキストで保存した方がいい,GUIだけでは複雑な挙動を検討できないし,ハードウェア設定をも考慮した本体プロファイルと分けないと混乱する

プログラムの確認

表からはKarabiner-ElementsKarabiner-EventViewerを起動できるが,前者は設定用・後者はデバッグ用に過ぎない
実際は初回起動において監視・制御のデーモンを裏で勝手に展開してくれる,次回起動からはログイン時に勝手にそれらが起動する,ログイン項目に登録する必要はない(ただし再読み込みはKarabiner-Elementsからやる必要がある)

f:id:Soluna_Eureka:20220221012821p:plain

設定

バイス登録

有線・無線・Bluetoothは関係なく,使いたいマウスを接続したらKarabiner-Elements->Devices->Basic configurationに移動,hoge Optional Mouse (fuga)が見つかるはずなので,左端チェックボックスから有効化する
ついでにVendor IDProduct IDをメモする,これらは後で条件設定の時に使いたい

f:id:Soluna_Eureka:20220221223804p:plain

Advancedは特に要らない

基本設定

混乱回避のため,Simple Modificationsを空欄にし,Function keysをリセットする(To Keyをそのまま割り振ると良い)

拡張ファイルの製作

以下に一例を示す

  • 共通設定
    • 5番のみでtab
    • 4番のみでshift+tab
  • ブラウザのみ
    • 1番+2番でcommand+r
    • 5番+6番でcommand+w
    • 1番+5番でcommand+click
    • 1番+4番でshift+click
    • 2番+5番でcommand+c
    • 2番+4番でcommand+v
{
    "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オブジェクトのtitlerules.descriptionのみ表示される)

f:id:Soluna_Eureka:20220221225737p:plain

うまく有効化できたらMisc->Restart Karabiner-Elementsで再起動,これで完全に動作が反映されるはず

特徴

  • 同時押しsimultaneousによって修飾キー以外による同時押しに対応
    • 単一押しと使用するボタンやキーが被る場合は,simultaneousさせたい動作についてjsonオブジェクト上に先に記述すること,後に記述すると単一押しの方が優先されて判定を受けてしまい,同時押しの方は問答無用で無視されてしまう
    • それぞれの動作の定義がrules.manipulatorsの配列に格納される形であるため,処理の優先度が前後関係で決定されている可能性がある,そのためなるべく少ないjsonファイルに関連する設定をまとめた方がむしろ安心
    • 実はKarabiner-Elements->Complex Modifications->Parametersから設定できる
      • マウスで使うにはあまりにもシビアなので,猶予時間は長めにとった方が良いかも
      • f:id:Soluna_Eureka:20220221225755p:plain
  • アプリケーション判定frontmost_application_ifによってブラウザ限定動作に対応
    • マルチディスプレイで別々のアプリをトップに表示すると両方とも有効と扱われるので注意,シングルディスプレイを想定した実装しかされていない(意図しない動作が思わず発生する可能性がある)

カスタマイズ

  • rules.manipulators[].conditions[].identifiersには対応するvendor_idproduct_idが必要,各自で確認
  • マウスのボタン配置も確認して使いやすい組み合わせを検討
  • rules.manipulators[].conditions[].bundle_identifiersには対応するアプリのidentifiersが必要各自で確認
    • アプリを起動してlsappinfo info -only bundleid hogeすれば確認できる

qiita.com

解説

何を読めば理解できるか

公式ドキュメント

karabiner-elements.pqrs.org

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:trueto_after_key_upをキャンセルして処理を変えることが可能になる.

もしくはfrom.simultaneousで修飾キー以外の複数入力を受け付けられるが,これはto系のメンバではなくfrom系のメンバであるために判定時間の概念が異なる.先に述べたようにrules.manipulatorsの配列の順番がfrom系のメンバの実行の優先順序になると推定されており,同じキーを利用した同時押しの判定をやりたいのであれば先に記述すべきである.

また包括されるキーの組み合わせを設定するのも全く推奨されない,例えばhoge+hugahogeを待ち受ける2つのfromオブジェクトがある場合,後者の設定をto_if_held_downでも設定しない限りaを下げて上げた瞬間に後者が起爆する.

総合的に考えると,今回の実装はえらく特殊なものである以上は,真似はしない方が良いだろう.
…実際に使うとなればKarabiner-Elements->Complex Modifications->Parametersto_if_alone_timeout_millisecondssimultaneous_threshold_millisecondsをイジるくらいで,これはデバイスの使用感に依存するだろう.
また変数をset_variableで設定してvariable_ifvariable_unlessで利用できるらしいが,使ったことがない…