yabai + skhd での快適Mac生活
TL;DR
マウス嫌いでもmacOSが使いたい!()
MacOS用のタイル型ウィンドウマネージャであるところのyabai
とhotkey daemonのskhd
でi3wm
/swaywm
のような操作感にできる
3ヶ月くらい熟成したconfig
条件
- macOS
VenturaSonoma - M1 MacBook Pro (2021)
- SIP一部無効化済み(scripting additionのため)
-
Status Bar Widgetとして,ÜbersichtのSketchybarに移行simple-bar
を使用(若干カスタム済み)
#!/usr/bin/env sh
# loading sa
yabai -m signal --add event=dock_did_restart action="sudo yabai --load-sa"
sudo yabai --load-sa
# global settings
yabai -m config mouse_follows_focus off
yabai -m config focus_follows_mouse on
yabai -m config window_origin_display default
yabai -m config window_placement second_child
yabai -m config window_topmost off
yabai -m config window_shadow on
yabai -m config window_opacity off
yabai -m config window_opacity_duration 0.0
yabai -m config active_window_opacity 1.0
yabai -m config normal_window_opacity 0.90
yabai -m config window_border off
yabai -m config window_border_width 6
yabai -m config active_window_border_color 0xff775759
yabai -m config normal_window_border_color 0xff555555
yabai -m config insert_feedback_color 0xffd75f5f
yabai -m config split_ratio 0.50
yabai -m config auto_balance off
yabai -m config mouse_modifier fn
yabai -m config mouse_action1 move
yabai -m config mouse_action2 resize
yabai -m config mouse_drop_action swap
# general space settings
yabai -m config layout bsp
yabai -m config top_padding 12
yabai -m config bottom_padding 12
yabai -m config left_padding 12
yabai -m config right_padding 12
yabai -m config window_gap 06
# in external display, top padding is different
# on M1 or later macbook, the notch space working as padding
DISPLAY_UUID=37D8832A-2D66-02CA-B9F7-8F30A301B230 # UUID of built-in display
BAR_PADDING=50
SPACES_ON_SUB=$(yabai -m query --displays | jq ".[] | select(.uuid != \"${DISPLAY_UUID}\") | .spaces[]")
for idx in ${SPACES_ON_SUB}
do
yabai -m config --space "$idx" top_padding "$BAR_PADDING"
done
# Rules
yabai -m rule --add app="^System Settings$" manage=off
yabai -m rule --add app="^Termius$" manage=off
yabai -m rule --add app="^zoom\.us$" manage=off
yabai -m rule --add app="^Docker Desktop$" manage=off
yabai -m rule --add app="^Calculator$" manage=off
yabai -m rule --add app="^Finder$" title="^Connect to Server$" manage=off
yabai -m rule --add app="^Finder$" title="^Connecting to Server$" manage=off
yabai -m rule --add app="^Finder$" title="^.* Info$" manage=off
yabai -m rule --add app="^Finder$" title="^Copy$" manage=off
yabai -m rule --add app="^Inkdrop$" title="^Preferences$" manage=off
yabai -m rule --add app="^Weather$" manage=off
yabai -m rule --add app="^Clock$" manage=off
yabai -m rule --add app="^1Password$" manage=off
yabai -m rule --add app="^Discord$" title="^Discord Updater$" manage=off
yabai -m rule --add app="^Archive Utility$" manage=off
yabai -m rule --add app="^Microsoft To Do$" manage=off
yabai -m rule --add app="^UTM$" manage=off
yabai -m rule --add app="^GitKraken$" manage=off
yabai -m rule --add app="^Parallels Desktop$" manage=off
yabai -m rule --add app="^Plexamp$" sticky=on
echo "yabai configuration loaded.."
# focus window
alt - x : yabai -m window --focus recent
alt - h : yabai -m window --focus west
alt - j : yabai -m window --focus south
alt - k : yabai -m window --focus north
alt - l : yabai -m window --focus east
alt - n : ~/.scripts/yabai_next_window.sh
alt - p : ~/.scripts/yabai_prev_window.sh
# swap window
shift + alt - x : yabai -m window --swap recent
shift + alt - h : yabai -m window --swap west
shift + alt - j : yabai -m window --swap south
shift + alt - k : yabai -m window --swap north
shift + alt - l : yabai -m window --swap east
# move window
shift + cmd - h : yabai -m window --warp west
shift + cmd - j : yabai -m window --warp south
shift + cmd - k : yabai -m window --warp north
shift + cmd - l : yabai -m window --warp east
# move window
shift + ctrl - a : yabai -m window --move rel:-20:0
shift + ctrl - s : yabai -m window --move rel:0:20
shift + ctrl - w : yabai -m window --move rel:0:-20
shift + ctrl - d : yabai -m window --move rel:20:0
# increase window size
shift + alt - a : yabai -m window --resize left:-20:0
shift + alt - s : yabai -m window --resize bottom:0:20
shift + alt - w : yabai -m window --resize top:0:-20
shift + alt - d : yabai -m window --resize right:20:0
# decrease window size
shift + cmd - a : yabai -m window --resize left:20:0
shift + cmd - s : yabai -m window --resize bottom:0:-20
shift + cmd - w : yabai -m window --resize top:0:20
shift + cmd - d : yabai -m window --resize right:-20:0
# rotate tree
alt - r : yabai -m space --rotate 90
# mirror tree y-axis
alt - y : yabai -m space --mirror y-axis
# mirror tree x-axis
alt - x : yabai -m space --mirror x-axis
# toggle desktop offset
alt - a : yabai -m space --toggle padding && yabai -m space --toggle gap
# toggle window fullscreen zoom
alt - f : yabai -m window --toggle zoom-fullscreen
# toggle window native fullscreen
shift + alt - f : yabai -m window --toggle native-fullscreen
# toggle window split type
alt - e : yabai -m window --toggle split
# float / unfloat window and restore position
# alt - t : yabai -m window --toggle float && /tmp/yabai-restore/$(yabai -m query --windows --window | jq -re '.id').restore 2>/dev/null || true
alt - t : yabai -m window --toggle float && yabai -m window --grid 4:4:1:1:2:2
# toggle sticky window
alt - s : yabai -m window --toggle sticky
# restart yabai
shift + alt - r : yabai --restart-service && osascript -e 'tell application id "tracesOf.Uebersicht" to reload widget id "simple-bar-index-jsx"'
# restart skhd
shift + ctrl - r : skhd --restart-service
# Close app
alt - q : yabai -m window --close
# Focus spaces (require sa)
alt - 1 : yabai -m space --focus 1
alt - 2 : yabai -m space --focus 2
alt - 3 : yabai -m space --focus 3
alt - 4 : yabai -m space --focus 4
alt - 5 : yabai -m space --focus 5
alt - 6 : yabai -m space --focus 6
alt - 7 : yabai -m space --focus 7
alt - 8 : yabai -m space --focus 8
alt - 9 : yabai -m space --focus 9
alt - 0 : yabai -m space --focus 10
# Move spaces (require sa)
shift + alt - 1 : yabai -m window --space 1
shift + alt - 2 : yabai -m window --space 2
shift + alt - 3 : yabai -m window --space 3
shift + alt - 4 : yabai -m window --space 4
shift + alt - 5 : yabai -m window --space 5
shift + alt - 6 : yabai -m window --space 6
shift + alt - 7 : yabai -m window --space 7
shift + alt - 8 : yabai -m window --space 8
shift + alt - 9 : yabai -m window --space 9
shift + alt - 0 : yabai -m window --space 10
# Move display
shift + alt - p : yabai -m space --move prev
shift + alt - n : yabai -m space --move next
# Launch Apps
## WezTerm
alt - return : open -a "WezTerm"
## memos
alt - m : open "https://memos.myuu.dev"
## FireFox
alt - b : open -a "Firefox Developer Edition"
## Vivaldi
shift + alt - b : open -a "Firefox"
## Finder
alt - g : osascript ~/.config/skhd/finder.scpt
## Inkdrop
cmd + alt - m : open -a "Inkdrop"
## Code
cmd + alt - o : open -a "Visual Studio Code"
## 1Password
cmd + alt - k : open -a "1Password"
Built-inディスプレイと外部ディスプレイのpaddingをよしなにする
# in external display, top padding is different
# on M1 or later macbook, the notch space working as padding
DISPLAY_UUID=37D8832A-2D66-02CA-B9F7-8F30A301B230 # UUID of built-in display
BAR_PADDING=50
SPACES_ON_SUB=$(yabai -m query --displays | jq ".[] | select(.uuid != \"${DISPLAY_UUID}\") | .spaces[]")
for idx in ${SPACES_ON_SUB}
do
yabai -m config --space "$idx" top_padding "$BAR_PADDING"
done
Why
M1搭載以降のMBPは,ノッチを避けるためにデフォルトでパディングが存在する.
そのため,Built-inディスプレイではパディングなしで独自のステータスバー領域を確保できる.
しかし,外部ディスプレイにはノッチのためのパディングが確保されない(当然)ので,外部ディスプレイにのみyabai側でパディングを設定する.
How
各ディスプレイに振られるuuidを利用する.Built-inのuuidは基本変化しないとしてベタ書きし,これに該当しないディスプレイ上のspace対してのみパディングを与える.
- 外部ディスプレイ上のspaceの取得
yabai -m query --displays | jq ".[] | select(.uuid != \"${DISPLAY_UUID}\") | .spaces[]"
- spaceごとにパディングを設定
yabai -m config --space SPACE_ID top_padding PADDING
Space内でcyclicにwindowを切り替える
What
アクティブなspace内にあるwindowを巡回したかったが,yabai側でそれができる機能がなかったっぽい?
shellscriptでゴリゴリとやった
Why
floatingなwindowがあるとき,これにfocusする手段がなかった
Keybind
alt - n : ~/.scripts/yabai_next_window.sh
alt - p : ~/.scripts/yabai_prev_window.sh
next
と prev
はwindow idの前後
Script
#!/bin/bash
# focusされてるwindowの情報
FOCUSED=$(yabai -m query --windows --window 2> /dev/null)
# focusされてるwindow id
FOCUSED_ID=$(echo "$FOCUSED" | jq ".id")
# focusされてるwindowが存在するとき
if [ -n "$FOCUSED_ID" ]; then
UNFOCUSED=$(yabai -m query --windows --space | jq "[ .[] | if .id != $FOCUSED_ID then . else empty end]")
UPPER_ID=$(echo "$UNFOCUSED" | jq "[ .[] | if .id > $FOCUSED_ID then . else empty end]")
if [ "$(echo "$UPPER_ID" | jq ". | length")" -gt 0 ]; then
NEXT=$(echo "$UPPER_ID" | jq "[ .[].id ] | min")
yabai -m window --focus "$NEXT"
else
NEXT=$(echo "$UNFOCUSED" | jq "[ .[].id ] | min")
yabai -m window --focus "$NEXT"
fi
else # なにもfocusされてないとき
UNFOCUSED=$(yabai -m query --windows --space)
NEXT=$(echo "$UNFOCUSED" | jq "[ .[].id ] | min")
yabai -m window --focus "$NEXT"
fi
- window idに基づいて,直後・直前のwindowにfocusする
- なにもfocusされていない場合,idが先頭・末尾のwindowへfocus
-
prev
はidの大小比較を真逆にしたもの
SIPの無効化について
しないと実際どうなのか
個人的には不快.マウス操作したほうがマシだった.
なにができないのか
- スムーズなspace (仮想デスクトップ) の移動
- scripting-additionはアニメーションとかその他いらない視覚効果を無効化できる
- windowのspace間移動
- yabai側からのspace追加・削除
- sticky windowが使えない
- などなど…
指示通り設定するとシステム保護機能はこうなる
最低限の保護は効いてるはずなので,まあ個人使用マシンならそこまで問題はないだろう…
今のところその他アプリケーションへの影響もない.
Übersicht
React / jsx (tsx) で記述したウィジェットを画面上にレンダリングできる.
任意のコマンド実行結果を取得して,それを表示するなど.
Install
Homebrew or Website
brew install ubersicht
simple-bar
yabaiへのメッセージパッシングを実装しているÜbersichtのウィジェット.
yabai以外にSpotifyなどの表示にも対応している.
作者はApple Silicon非所持で動作保証はしないよ!と言っているが,今のところ支障はない.
(むしろAppleSilicon以降のノッチが丁度余白になってて良き)
Install
Übersichtのウィジェットディレクトリに直接cloneすればよい
~/Library/Application Support/Übersicht/widgets
カスタマイズ
Reactが分かれば容易.画面サイズとかに合わせてPadding/Marginはカスタムした方がよさそうな所感.
完全に自分用にカスタムしただけのやつ
Refresh / Reload
yabaiをrestartするとメッセージパッシングが効かなくなる(多分古いsocketを参照したままだから?)
skhdでyabaiのrestartにあわせてwidgetをreloadする設定にするといい.
CLIからのÜbersichtの制御はAppleScriptで可能.
# restart yabai
shift + alt - r : yabai --restart-service && osascript -e 'tell application id "tracesOf.Uebersicht" to reload widget id "simple-bar-index-jsx"'
現状
ÜbersichtをやめてSketchybarに移行した
理由
- 最近Übersichtの挙動が怪しかった
- 起動オーバーヘッドが著しい
- 一部コンテンツがバグる(たまに読み込まない)
- しばらく使ってるとyabaiとのやりとりが遅くなる(メッセージパッシングが詰まってるかのような挙動)
- ぶっちゃけReactである必要がない(そこまでカスタマイズしないので)
Sketchybar,非常に軽くて良い.見た目も思ったよりカスタムできるし,何よりも✝️黒魔術️✝️(ShellScript)で項目を作れるのがいい.
Sketchybar移行後を載せてなかったので現在の様子
SIP無効化をやめた
あれだけSIP無効化をした方がいいと言っていたが…結局無効化なしでの運用にした
Reason
- 起動時とかわりかし不安定になる
- Update降ってきたときにsudoersのハッシュ値更新するのが面倒
- masOSのアプデのときにCompatibleか気にするのがダルくなってきた
- (セキュリティの研究してんのにこういうことやってんのはどうなん?とか思っちゃったりした.あんま関係ないが)
使えなくなった機能の代替
Space切り替え
- Mission Controlの標準ショートカットで代用可能
- アニメーションは入るが,そこまで不快に感じなくなった
Space移動
- ここはキーボードOnlyで対応するのは厳しい
- マウスでウィンドウを保持しつつ,Space切り替えをすると移動できるとかいう裏技?のようなやり方でなんとかしている
Sticky
- ぶっちゃけあんまり使ってないので困らなかった
- Stickyにまでしなくても,標準機能でSpaceに固定割り当てすれば自分のニーズでは特に困らなかった