macOSのタイル型ウィンドウマネージャであるchunkwmを試してみた

Linuxを使っていてよかったうちの1つがタイル型ウィンドウマネージャがすごく便利なことでした。

前までは1つのスペース(仮想デスクトップ)でCommand+Tabを酷使してウィンドウ管理をしていたのですが、Linuxでi3wmというタイル型ウィンドウマネージャを使ってみたらすごく便利だったのでぜひmacOSでも使いたいと思ってました。

ただmacOSのタイル型ウィンドウマネージャで有名な[Amethyst](https://github.com/ianyh/Amethyst)は私的には微妙で他のタイル型ウィンドウマネージャを探したところ、[chunkwm](https://github.com/koekeishiya/chunkwm)がなかなかよかったのでその備忘録です。

__注意__: chunkwmは2018/1現在Alpha版です。私が使っている限りでは比較的安定していますが、ときどきバグっぽい挙動があったり仕様が変わる可能性があります。



また、設定はGUIではなくCLIからなのである程度Terminalからのコマンド操作になれていることが前提です。

## 環境
- MacBookPro mid 2015
- macOS High Sierra
- chunkwm 0.2.36

## chunkwmとは

作者が同じタイル型ウィンドウマネージャである[kwm](https://github.com/koekeishiya/kwm)の後継でプラグインアーキテクチャを使い拡張性が高い?ウィンドウマネージャです。



[bspwm](https://github.com/baskerville/bspwm)に影響を受けているらしいですが、私はbspwmは使ったことないのでよくわかりません。

ただ使ってみた感じ、ウィンドウ管理においてi3に似た操作感を実現できる気がしました(気がしただけでやっぱり違うけど)

こんな感じでウィンドウが重ならないように分割できます。

![chunkwm-take2](https://user-images.githubusercontent.com/11902894/34652590-d3428cfc-f423-11e7-8209-ffc4e8e9e326.gif)
(画質が荒いのはffmpeg+gifsicleで圧縮したらやりすぎた)


## インストール

brew tapしてinstallができます。

```sh
# clone tap
brew tap crisidev/homebrew-chunkwm

# install latest stable version
brew install chunkwm

# install from git repo
brew install --HEAD chunkwm
```



brewからインストールした場合、`brew services`を使って起動時に自動的に実行できます。



ただし最初はAccesibilityの許可が必要なため、

```sh
brew services restart chunkwm
```

でサービスを再起動し表示されるダイアログの`Open System Preferences`を選択し、

<img width="458" alt="Screen Shot 2018-01-07 at 22.31.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/146345/7d2f5341-0831-8a63-8e40-a106cd6ed784.png">

`Security & Privacy　> Privacy > Accesibility > chunkwm`にチェックをする必要があります。

<img width="668" alt="Screen Shot 2018-01-07 at 22.32.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/146345/e11d1946-37d5-e9b9-f28f-f87827fbd4d8.png">

## コンフィグ

chunkwmは起動時デフォルトで`~/.chunkwmrc`を読み込むのでファイルを作ってやります(コンフィグファイルがない場合は起動に失敗します)

また、`.chunkwmrc`は実行権限が必要なのでそれもつけてやります。

```sh
touch ~/.chunkwmrc
chmod +x ~/.chunkwmrc
```



コンフィグファイルは[公式にサンプル](https://github.com/koekeishiya/chunkwm/blob/master/examples/chunkwmrc)がありますが、そのまま適用するとMacBookProの15inchでは表示崩れが激しいので変更しています。



```sh:~/.chunkwmrc
#!/bin/bash

#
# NOTE: specify the absolutepath to the directory to use when
#       loading a plugin. '~' expansion is supported.
#

# プラグインのディレクトリ
# brewから入れた場合、/usr/local/Cellar/chunkwm/0.2.36/share/chunkwm/pluginsにあるのでそれを直接指定してもいい気がする。
# chunkc core::plugin_dir  /usr/local/Cellar/chunkwm/0.2.36/share/chunkwm/plugins/usr/local/Cellar/chunkwm/0.2.36/share/chunkwm/plugins
# 公式通り~/.chunkwm_pluginsを使いたい場合は事前に
# cp /usr/local/Cellar/chunkwm/0.2.36/share/chunkwm/plugins/* .chunkwm_plugins/
# 等で.soファイルをコピーする必要がある。
chunkc core::plugin_dir ~/.chunkwm_plugins

#
# NOTE: if enabled, chunkwm will monitor the specified plugin_dir
#       and automatically reload any '.so' file that is changed.
#
# Pluginを開発しない限り多分使わない
chunkc core::hotload 0

# レイアウトモード
# global_desktop_modeでデフォルトの表示モード
# {index}_desktop_modeでSpaceによって表示モードを変更できる
# 
# - bsp: ウィンドウが分割されていくやつ
# - monocle: ウィンドウが画面いっぱいに拡大され重なる
# - float: 普段使っているようなウィンドウを自由に移動できるやつ

chunkc set global_desktop_mode           bsp
# chunkc set 2_desktop_mode                monocle
# chunkc set 5_desktop_mode                float

# ウィンドウと画面端の距離を設定
# gapはウィンドウとウィンドウの距離
# {index}_desktop_offset_{position}でそのSpaceの値を決定できる
chunkc set global_desktop_offset_top     12
chunkc set global_desktop_offset_bottom  12
chunkc set global_desktop_offset_left    12
chunkc set global_desktop_offset_right   12
chunkc set global_desktop_offset_gap     16

# chunkc set 1_desktop_offset_top          190
# chunkc set 1_desktop_offset_bottom       190
# chunkc set 1_desktop_offset_left         190
# chunkc set 1_desktop_offset_right        190
# chunkc set 1_desktop_offset_gap          15

chunkc set desktop_padding_step_size     10.0
chunkc set desktop_gap_step_size         5.0

# 新しいウィンドウを開くとき、現在フォーカスしているウィンドウの左に開くかどうか
# true: 1
# false: 0
chunkc set bsp_spawn_left                0
chunkc set bsp_optimal_ratio             1.618
#  分割モード
# - horizontal: 水平分割(-)
# - vertical: 垂直分割(|)
# - optimal: よしなに
chunkc set bsp_split_mode                optimal
# 分割されたときの比率
chunkc set bsp_split_ratio               0.5

chunkc set monitor_focus_cycle           1
chunkc set window_focus_cycle            monitor

# フォーカスの移動にマウスが追従するかどうか
chunkc set mouse_follows_focus           1

chunkc set window_float_next             0

# windowサイズをマウスで変更できないようにするか
chunkc set window_region_locked          1

#
# NOTE: shell commands require escaped quotes
#       to pass value containing a whitespace.
#
# chunkc set mouse_modifier              \"cmd shift\"
chunkc set mouse_modifier                fn

chunkc set preselect_border_color        0xffd75f5f
chunkc set preselect_border_width        5
chunkc set preselect_border_radius       0

#
# NOTE: these settings require chwm-sa.
#       (https://github.com/koekeishiya/chwm-sa)
#

chunkc set window_float_topmost          0
chunkc set window_fade_inactive          0
chunkc set window_fade_alpha             0.85
chunkc set window_fade_duration          0.25
chunkc set window_use_cgs_move           0

#
# NOTE: the following are config variables for the chunkwm-border plugin.
#

chunkc set focused_border_color          0xff0f6288
chunkc set focused_border_width          5
chunkc set focused_border_radius         5
chunkc set focused_border_skip_floating  0

#
# NOTE: specify plugins to load when chunkwm starts.
#       if chunkc plugin_dir is not set, the absolutepath is necessary.
#

chunkc core::load border.so
chunkc core::load tiling.so
chunkc core::load ffm.so

# 個別のウィンドウ表示設定
# --ownerがアプリ名で--nameがタイトルバーの名前
chunkc tiling::rule --owner Finder --name Copy --state float
chunkc tiling::rule --owner \"App Store\" --state float
chunkc tiling::rule --owner \"ConfigDialog\" --name \"Google Japanese Input Preferences\" --state float
chunkc tiling::rule --owner \"IntelliJ IDEA\" --name \"Default Preferences\" --state float

```



## コマンド

ウィンドウの操作はすべてコマンド経由で行います。



### フォーカスの移動

ウィンドウのフォーカスの移動をします。

現在フォーカスしているウィンドウを中心に東西南北(右左下上)で指定します。

その方向にウィンドウがない場合は何も起こりません。

``` sh
# 現在フォーカスしているウィンドウの西(左)にあるウィンドウにフォーカスを移動
chunkc tiling::window --focus west
```



### ウィンドウの移動

現在フォーカスしているウィンドウの移動を行います。

だいたいフォーカスの移動と同じ感じです。

```sh
# 現在フォーカスしているウィンドウを西(左)に移動する
chunkc tiling::window --warp west
```



### 別スペースにウィンドウを飛ばす

現在フォーカスしているウィンドウを別スペースに飛ばします。

```sh
# 1つ前のスペースに飛ばす
chunkc tiling::window --send-to-desktop prev

# 1つあとのスペースに飛ばす
chunkc tiling::window --send-to-desktop next

# 番号を指定するとそのスペースに飛ばせる
chunkc tiling::window --send-to-desktop prev
```



### ウィンドウのモード切り替え

現在フォーカスしているウィンドウのモードを切り替えます。

```sh
# floatモードに切り替え(floatモードだったら元に戻る)
chunkc tiling::window --toggle float

# フルスクリーン化の切り替え
# 大きさはウィンドウがスペースに1つしかないときと同じ大きさ
chunkc tiling::window --toggle fullscreen

# フルスクリーン化の切り替え
# 大きさはmacOSのウィンドウ左上にある緑色のボタンを押したときと同じ
hunkc tiling::window --toggle native-fullscreen
```



### ウィンドウサイズの変更

現在フォーカスしているウィンドウのサイズを変更します。

東西南北の方向に増やしたり減らしたりできます(なので画面を2分割しているときに左にあるウィンドウを選択した状態でwestを増やしても何も起こらない)。

また、`--use-temporary-ratio`に指定する数字は割合で値は`-1 < value < 1`です。



```sh
# ウィンドウを西(左)へ伸ばす
chunkc tiling::window --use-temporary-ratio 0.01 --adjust-window-edge west

# ウィンドウを西(左)へ縮める
chunkc tiling::window --use-temporary-ratio -0.01 --adjust-window-edge west
```



### 分割モードの変更

次に開くウィンドウの分割の仕方を変更します。



```sh
# 縦方向に分割
chunkc set bsp_split_mode vertical

# 横方向に分割
chunkc set bsp_split_mode horizontal

# 現在のウィンドウの大きさを元によしなに決める
chunkc set bsp_split_mode optimal
```



他にも色々あるので[chunkwm-tilingのリファレンス](https://github.com/koekeishiya/chunkwm/tree/master/src/plugins/tiling/README.md)や[chunkwm-borderのリファレンス](https://github.com/koekeishiya/chunkwm/tree/master/src/plugins/border/README.md)を見たりキーバインディングの設定のサンプルを見ていろいろ試してみてください。



- shkdの設定のサンプル: https://github.com/koekeishiya/chunkwm/tree/master/src/plugins/tiling/examples/skhdrc
- khdの設定のサンプル: https://github.com/koekeishiya/chunkwm/tree/master/src/plugins/tiling/examples/khdrc



## キーバインディング

chunkwmはデフォルトでキーバインディングを提供していません。

なので自分で上記のコマンドとキーを紐付ける必要があります。



私はKarabiner-Elementを使っていて、いつの間にかシェルの実行に対応していたので全てKarabiner-Elementで設定しました。



とりあえずシェルを実行できればなんでもいいと思います。



### Karabiner-Elementの設定

Karabiner-Elementで独自のキーバインディングをするには`~/.config/karabiner/assets/complex_modifications`ディレクトリ内に設定ファイルを用意してあげます。



今回はchunkwmについての設定だったのでそのまま`chunkwm.json`という名前にしました。



```sh
touch ~/.config/karabiner/assets/complex_modifications/chunkwm.json
```



あとは自分の好みに合わせて作成すれば大丈夫だと思います。



Karabiner-Elementsの設定ファイルの書き方や設定方法などは

https://qiita.com/s-show/items/a1fd228b04801477729c

を参考にしました。ありがとうございます!



一応私の設定ファイルです。Vim風に移動できるようにしたりしています。

本当はスペースの移動がcontrol+矢印キーなのでcontrolが良かったのですが、IDEとかで使う気がしたのでoptionキーをベースに設定しています。



```json:~/.config/karabiner/assets/complex_modifications/chunkwm.json
{
    "title": "chunkwm key bindings",
    "rules": [
        {
            "description": "focus window",
            "manipulators": [
                {
                    "type": "basic",
                    "from": {
                        "key_code": "h",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to": [
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --focus west"
                        }
                    ]
                },
                {
                    "type": "basic",
                    "from": {
                        "key_code": "j",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to": [
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --focus south"
                        }
                    ]
                },
                {
                    "type": "basic",
                    "from": {
                        "key_code": "k",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to": [
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --focus north"
                        }
                    ]
                },
                {
                    "type": "basic",
                    "from": {
                        "key_code": "l",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to": [
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --focus east"
                        }
                    ]
                }
            ]
        },
        {
            "description":"move window",
            "manipulators":[
                {
                    "type":"basic",
                    "from":{
                        "key_code":"h",
                        "modifiers":{
                            "mandatory":[
                                "shift",
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --warp west"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"j",
                        "modifiers":{
                            "mandatory":[
                                "shift",
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --warp south"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"k",
                        "modifiers":{
                            "mandatory":[
                                "shift",
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --warp north"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"l",
                        "modifiers":{
                            "mandatory":[
                                "shift",
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --warp east"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"open_bracket",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "shift"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --send-to-desktop prev"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"close_bracket",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "shift"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --send-to-desktop next"
                        }
                    ]
                }
            ]
        },
        {
            "description":"change window layout",
            "manipulators":[
                {
                    "type":"basic",
                    "from":{
                        "key_code":"spacebar",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --toggle float"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"f",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --toggle fullscreen"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"f",
                        "modifiers":{
                            "mandatory":[
                                "shift",
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --toggle native-fullscreen"
                        }
                    ]
                }
            ]
        },
        {
            "description":"change window size",
            "manipulators":[
                {
                    "type":"basic",
                    "from":{
                        "key_code":"e",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::desktop --equalize"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"h",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "control"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --use-temporary-ratio -0.01 --adjust-window-edge east"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"j",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "control"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --use-temporary-ratio 0.01 --adjust-window-edge south"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"k",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "control"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --use-temporary-ratio -0.01 --adjust-window-edge south"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"l",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "control"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --use-temporary-ratio 0.01 --adjust-window-edge east"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"h",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "control",
                                "shift"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --use-temporary-ratio 0.01 --adjust-window-edge west"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"j",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "control",
                                "shift"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --use-temporary-ratio -0.01 --adjust-window-edge north"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"k",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "control",
                                "shift"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --use-temporary-ratio 0.01 --adjust-window-edge north"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"l",
                        "modifiers":{
                            "mandatory":[
                                "option",
                                "control",
                                "shift"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc tiling::window --use-temporary-ratio -0.01 --adjust-window-edge west"
                        }
                    ]
                }
            ]
        },
        {
            "description":"config variable",
            "manipulators":[
                {
                    "type":"basic",
                    "from":{
                        "key_code":"v",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc set bsp_split_mode vertical"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"b",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc set bsp_split_mode horizontal"
                        }
                    ]
                },
                {
                    "type":"basic",
                    "from":{
                        "key_code":"o",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to":[
                        {
                            "shell_command":"/usr/local/bin/chunkc set bsp_split_mode optimal"
                        }
                    ]
                }
            ]
        },
        {
            "description":"new terminal window",
            "manipulators":[
                {
                    "type": "basic",
                    "from": {
                        "key_code": "n",
                        "modifiers":{
                            "mandatory":[
                                "option"
                            ]
                        }
                    },
                    "to": [
                        {
                            "shell_command":"open -na Terminal.app"
                        }
                    ]
                }
            ]
        }
    ]
}

```



コマンドは`chunkc`だけだと`command not found`になったのでフルパスで指定しています。

ウィンドウの幅の変更はi3みたいに特定のキーを押すとresizeモードになるみたいな挙動にしてみたいのですが、気力が・・・



あとIDEとかでcontrolをあまり使わなかったらcontrolにしたいと思います。



## Tips

### 設定するとき

設定をするときは`brew services stop chunkwm`で一回chunkwmの自動起動をやめてTerminalで`chunkwm`を実行して出力を見ながらいろいろ弄ったほうが楽です。

エラーがあった場合そこに表示されますし、コンフィグファイルで`tiling::rule`を設定するときの`--owner`と`--name`はウィンドウをフォーカスしたときなどに表示される

```
Google Chrome:New Tab:1172 window title changed
```

の最初の'Google Chrome'が`--owner`で'New Tab'が`--name`です(Google Chromeの場合)。



設定が全て終わったら`brew services start chunkwm`でchunkwmの自動起動を有効にすればOKです。



### 'chunkwm: failed to initialize daemon! abort..'で起動できない

既にchunkwmが起動していた場合、chunkwmの起動に失敗します。

もしどこかでゾンビプロセスになってた場合は

```sh
killall -QUIT "chunkwm"
```

などで成仏させてあげましょう。



## まとめ

macOSでタイル型ウィンドウマネージャを実現するchunkwmを使ってみました。

拡張性が高く、全てCLIからなので自分でいろいろカスタマイズできてエンジニア的には嬉しいです。


また、まだAlpha版なのでこれからの発展に期待したいです。