ウィンドウマネージャー:yabai + skhd と Magnet の比較(macOS)

yabai + skhd と Magnet の比較(macOS)のアイキャッチ画像です

Macで複数のウィンドウを効率よく操作したいと考えたとき、選択肢は大きく分けて2つあります。
ひとつは Magnet のようなGUIアプリ、もうひとつは yabai + skhd のようなタイル型ウィンドウマネージャーです。この記事では両者を実際に導入して比較し、それぞれの特長についてまとめました。

この記事を読むとできること
  • あなたが実際に Magnet と「yabai + skhd」のどちらを選べばいいかわかる
  • yabai と skhd のインストールから実際に使えるようになります(おまけ)
    • この「おまけ」を読んで難しくてよくわからない、という方には Magnet が圧倒的におすすめです!
本記事の結論
  • Magnet → 「手軽にウィンドウ整理したい人」向け ※ただし有料
  • yabai + skhd → 「本気で効率化したい人」向け ※ただし学習コスト高め
    • 無料で導入したい貧乏人
    • 自動タイル整列させたい怠け者
    • キーボードだけでいろんな操作を完結させたい変態
    • 高いカスタマイズ性を求める効率厨
    • マルチディスプレイの操作対応を求める完璧主義の人など
Contents

Magnet とは?

Magnet は Apple の App Store (Mac) で購入できる有料アプリ(現在は¥490ほど)です。
マウスを使った直感的操作またはショートカットキーを使って、誰でも簡単にウィンドウを左右2分割・上下2分割・四隅に配置することができます。

メリット
  • 導入が簡単(App Storeからインストールするだけ!)
  • GUIで直感的に誰でも操作できる!
  • ショートカットもすでに用意されている
  • 設定など最小限で済む(これが真の効率化)
デメリット
  • カスタマイズ性はほぼゼロ
  • 高度な自動整列やスクリプト連携は不可

yabai + skhd とは?

ただのコマンドツールです。CLI上で操作が可能です。そうです、これが最大の特徴なんです。

つまり、あらゆる操作がコマンドひとつで可能であるがゆえに、カスタマイズ性が高い。その設定はただのスクリプトファイルで完結します。

yabai

yabai は macOS 向けのタイル型ウィンドウマネージャーです。
Linux の i3 や bspwm を使ったことがある人にはおなじみだと思います。
ウィンドウを自動整列させたり、自在にリサイズ・移動が可能です。

skhd

skhd はシンプルなホットキーバインディングツールです。
yabai のコマンドを割り当てることで、自在なキーボード操作が可能になります。

実際の動作デモ

  • Magnet
    • マウスで画面端にドラッグすると、自動で半分のサイズに整列
    • キーボードショートカットキーも用意されている
    • 直感的でシンプル、難しい設定は必要ない
  • yabai + skhd (私の設定環境下)
    • alt + hを押すと次のウィンドウにフォーカスが移動
    • shift + alt + [ または shift + alt + ] を押せば幅が縮小
      shift + alt + e で等幅に戻る
    • 作業効率を極限まで高めたい人には圧倒的に便利

実演デモ ※GIFアニメです

Magnet の強み(yabai + skhdにできないこと)

  • GUIでの直感操作
    Magnetはマウス操作でも使える。誰でも使える。ユーザーフレンドリー。
  • App Storeで簡単に導入
    セキュリティ権限周り(アクセシビリティ・SIP無効化など)が不要。
    長年に渡るメンテナンスで信頼と実績のあるアプリ。
  • 初心者向けの手軽さ
    とりあえず「サクッとウィンドウ整理したい」人にはMagnetが圧倒的に手軽。

yabai + skhd の強み(Magnetにはできないこと)

  • マウスオーバーで自動フォーカス
    ウィンドウにカーソルを乗せるだけでフォーカスが移る。クリック不要で効率的。
  • 外部ディスプレイ間でのウィンドウ移動とフォーカス
    ショートカットひとつで隣のディスプレイへウィンドウを移せる。ディスプレイ間のフォーカスの移動も設定可能。マルチモニタ作業に最適。
  • 完全なタイル型自動整列
    新しいウィンドウが開かれると自動で再配置。手動操作が不要。
    • yabai では Floating mode という概念が重要になります。
      Floating mode(私の設定では「opt + f」)でウィンドウ制御を解除できます。いつものようにウィンドウをマウスで自由に移動可能になります。
      再びウィンドウレイアウトを戻すには 「shift + opt + space」を入力してください。

まとめ

Magnet は導入したそのときから簡単に使える学習コスト・管理コストの低さが最大の魅力です。
「まずはMagnetでお試し → もっと効率化したいならyabaiへ移行」という感じになると思います。

ちなみに

Magnet は安かったし購入してみました。ちなみに Magnet と yabai を2つ同時に起動しても、競合はするけどそんなに問題ない感じです。yabai の方を floating mode にすれば、Magnet が効きます。floating mode にしなければ yabai の自動タイリングが Magnet に勝ちます。

おまけ — yabai と skhd のインストール手順と設定

Homebrew をインストール(未導入の場合)

Homebrewの公式サイト

以下のコードをターミナルに入力してください。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  • 「Next steps:」以下の指示に従ってPATHを通してください。

yabai と skhd をインストール

brew install koekeishiya/formulae/yabai
brew install koekeishiya/formulae/skhd

設定ファイルの作成

ホームディレクトリ直下に以下の2つの設定ファイルを作ってください。これらは制作者のサンプル設定ファイルになります。適宜カスタマイズして使用してください。.skhdrc ファイルは適宜コメントアウトを戻して使用してください。そのままではショートカットキーバインディングは機能しません。

参考として私の設定も下記に示してあります。

  • 「XDG Base Directory Specification」に準じている方は適宜読み換えてください。
  • 私の設定で動かすには jq が必要になると思います。適宜Homebrewなどでインストールしてください。
brew install jq

~/.yabairc

#!/usr/bin/env sh

#
# for this to work you must configure sudo such that
# it will be able to run the command without password
#
# see this wiki page for information:
#  - https://github.com/koekeishiya/yabai/wiki/Installing-yabai-(latest-release)#configure-scripting-addition
#
# yabai -m signal --add event=dock_did_restart action="sudo yabai --load-sa"
# sudo yabai --load-sa
#

# global settings
yabai -m config                                 \
    external_bar                 off:40:0       \
    menubar_opacity              1.0            \
    mouse_follows_focus          off            \
    focus_follows_mouse          off            \
    display_arrangement_order    default        \
    window_origin_display        default        \
    window_placement             second_child   \
    window_insertion_point       focused        \
    window_zoom_persist          on             \
    window_shadow                on             \
    window_animation_duration    0.0            \
    window_animation_easing      ease_out_circ  \
    window_opacity_duration      0.0            \
    active_window_opacity        1.0            \
    normal_window_opacity        0.90           \
    window_opacity               off            \
    insert_feedback_color        0xffd75f5f     \
    split_ratio                  0.50           \
    split_type                   auto           \
    auto_balance                 off            \
    top_padding                  12             \
    bottom_padding               12             \
    left_padding                 12             \
    right_padding                12             \
    window_gap                   06             \
    layout                       bsp            \
    mouse_modifier               fn             \
    mouse_action1                move           \
    mouse_action2                resize         \
    mouse_drop_action            swap

echo "yabai configuration loaded.."

~/.skhdrc

# ################################################################ #
# THE FOLLOWING IS AN EXPLANATION OF THE GRAMMAR THAT SKHD PARSES. #
# FOR SIMPLE EXAMPLE MAPPINGS LOOK FURTHER DOWN THIS FILE..        #
# ################################################################ #

# A list of all built-in modifier and literal keywords can
# be found at https://github.com/koekeishiya/skhd/issues/1
#
# A hotkey is written according to the following rules:
#
#   hotkey       = <mode> '<' <action> | <action>
#
#   mode         = 'name of mode' | <mode> ',' <mode>
#
#   action       = <keysym> '[' <proc_map_lst> ']' | <keysym> '->' '[' <proc_map_lst> ']'
#                  <keysym> ':' <command>          | <keysym> '->' ':' <command>
#                  <keysym> ';' <mode>             | <keysym> '->' ';' <mode>
#
#   keysym       = <mod> '-' <key> | <key>
#
#   mod          = 'modifier keyword' | <mod> '+' <mod>
#
#   key          = <literal> | <keycode>
#
#   literal      = 'single letter or built-in keyword'
#
#   keycode      = 'apple keyboard kVK_<Key> values (0x3C)'
#
#   proc_map_lst = * <proc_map>
#
#   proc_map     = <string> ':' <command> | <string>     '~' |
#                  '*'      ':' <command> | '*'          '~'
#
#   string       = '"' 'sequence of characters' '"'
#
#   command      = command is executed through '$SHELL -c' and
#                  follows valid shell syntax. if the $SHELL environment
#                  variable is not set, it will default to '/bin/bash'.
#                  when bash is used, the ';' delimeter can be specified
#                  to chain commands.
#
#                  to allow a command to extend into multiple lines,
#                  prepend '\' at the end of the previous line.
#
#                  an EOL character signifies the end of the bind.
#
#   ->           = keypress is not consumed by skhd
#
#   *            = matches every application not specified in <proc_map_lst>
#
#   ~            = application is unbound and keypress is forwarded per usual, when specified in a <proc_map>
#
# A mode is declared according to the following rules:
#
#   mode_decl = '::' <name> '@' ':' <command> | '::' <name> ':' <command> |
#               '::' <name> '@'               | '::' <name>
#
#   name      = desired name for this mode,
#
#   @         = capture keypresses regardless of being bound to an action
#
#   command   = command is executed through '$SHELL -c' and
#               follows valid shell syntax. if the $SHELL environment
#               variable is not set, it will default to '/bin/bash'.
#               when bash is used, the ';' delimeter can be specified
#               to chain commands.
#
#               to allow a command to extend into multiple lines,
#               prepend '\' at the end of the previous line.
#
#               an EOL character signifies the end of the bind.

# ############################################################### #
# THE FOLLOWING SECTION CONTAIN SIMPLE MAPPINGS DEMONSTRATING HOW #
# TO INTERACT WITH THE YABAI WM. THESE ARE SUPPOSED TO BE USED AS #
# A REFERENCE ONLY, WHEN MAKING YOUR OWN CONFIGURATION..          #
# ############################################################### #

# focus window
# alt - h : yabai -m window --focus west

# swap managed window
# shift + alt - h : yabai -m window --swap north

# move managed window
# shift + cmd - h : yabai -m window --warp east

# balance size of windows
# shift + alt - 0 : yabai -m space --balance

# make floating window fill screen
# shift + alt - up     : yabai -m window --grid 1:1:0:0:1:1

# make floating window fill left-half of screen
# shift + alt - left   : yabai -m window --grid 1:2:0:0:1:1

# create desktop, move window and follow focus - uses jq for parsing json (brew install jq)
# shift + cmd - n : yabai -m space --create && \
#                   index="$(yabai -m query --spaces --display | jq 'map(select(."is-native-fullscreen" == false))[-1].index')" && \
#                   yabai -m window --space "${index}" && \
#                   yabai -m space --focus "${index}"

# fast focus desktop
# cmd + alt - x : yabai -m space --focus recent
# cmd + alt - 1 : yabai -m space --focus 1

# send window to desktop and follow focus
# shift + cmd - z : yabai -m window --space next; yabai -m space --focus next
# shift + cmd - 2 : yabai -m window --space  2; yabai -m space --focus 2

# focus monitor
# ctrl + alt - z  : yabai -m display --focus prev
# ctrl + alt - 3  : yabai -m display --focus 3

# send window to monitor and follow focus
# ctrl + cmd - c  : yabai -m window --display next; yabai -m display --focus next
# ctrl + cmd - 1  : yabai -m window --display 1; yabai -m display --focus 1

# move floating window
# shift + ctrl - a : yabai -m window --move rel:-20:0
# shift + ctrl - s : yabai -m window --move rel:0:20

# increase window size
# shift + alt - a : yabai -m window --resize left:-20:0
# shift + alt - w : yabai -m window --resize top:0:-20

# decrease window size
# shift + cmd - s : yabai -m window --resize bottom:0:-20
# shift + cmd - w : yabai -m window --resize top:0:20

# set insertion point in focused container
# ctrl + alt - h : yabai -m window --insert west

# toggle window zoom
# alt - d : yabai -m window --toggle zoom-parent
# alt - f : yabai -m window --toggle zoom-fullscreen

# toggle window split type
# alt - e : yabai -m window --toggle split

# float / unfloat window and center on screen
# alt - t : yabai -m window --toggle float --grid 4:4:1:1:2:2

# toggle sticky(+float), picture-in-picture
# alt - p : yabai -m window --toggle sticky --toggle pip

参考

以下は私の実際の設定です。よろしければ参考にしてください。

.yabairc
#!/usr/bin/env sh

# Layout config
yabai -m config window_gap                   6
yabai -m config window_placement             second_child
yabai -m config layout                       bsp
# yabai -m config bottom_padding               100

# set float layout for MBP display
space_indices_on_mbp_display="$(
    yabai -m query --displays \
        | jq -e 'map(select(.uuid == "2F917C32-1D54-4830-4A41-1078CDA43226")) | .[0].spaces[]'
)"
for index in "$space_indices_on_mbp_display"; do
    yabai -m config --space "$index" layout stack
done

# Mouse
yabai -m config mouse_modifier               ctrl
yabai -m config mouse_follows_focus          on
yabai -m config focus_follows_mouse          autoraise

# Debug
yabai -m config debug_output on

# Rules
yabai -m rule --add app="^Simulator$"          manage=off
yabai -m rule --add app="^System Settings$"    manage=off
yabai -m rule --add app="^1Password$"          manage=off
yabai -m rule --add app="^Raycast$"            manage=off
yabai -m rule --add app="^Music$"              manage=off
yabai -m rule --add app="^iTerm2$"             manage=off

echo "yabai configuration loaded..."
.skhdrc
###########################################################
# Layout
###########################################################

alt - f : yabai -m space --layout float
# Toggle between bsp and stack
alt + shift - space : [ "$(yabai -m query --spaces --space | jq -r '.type')" = bsp ] \
    && yabai -m space --layout stack \
    || yabai -m space --layout bsp

###########################################################
# Window
###########################################################

# insert mode
alt - i : yabai -m window --insert east
alt + shift - i : yabai -m window --insert south

# Applications
# alt - return : alacritty --working-directory $HOME/projects/github.com/JunichiSugiura
alt - return : open /Applications/Alacritty.app
alt - 0 : open -n /Applications/Brave\ Browser.app
alt + shift - n : open /Applications/Notion.app

# close
# alt - c : yabai -m window --close

# navigate
alt - k : yabai -m window --focus stack.prev \
    || yabai -m window --focus prev
alt - j : yabai -m window --focus stack.next \
    || yabai -m window --focus next
alt - tab : yabai -m window --focus stack.recent \
    || yabai -m window --focus recent

# mirror
alt - m : yabai -m space --mirror y-axis \
    && yabai -m window --focus first

# rotate
alt - r : yabai -m space --rotate 270

# warp
alt + shift - k : yabai -m window --warp prev
alt + shift - j : yabai -m window --warp next

# full screen
alt - space : yabai -m window --toggle zoom-fullscreen

# resize
# Alt + Shift + ]
alt + shift - 0x21 : yabai -m window --resize left:-40:0 \
    || yabai -m window --resize right:-40:0
# Alt + Shift + [
alt + shift - 0x1E : yabai -m window --resize right:40:0 \
    || yabai -m window --resize left:40:0

###########################################################
# Display
###########################################################

# navigate
# Alt + ,
alt - 0x2B : yabai -m display --focus prev \
    && yabai -m display --focus stack.prev
# Alt + .
alt - 0x2F : yabai -m display --focus next \
    && yabai -m display --focus stack.next

###########################################################
# Space
###########################################################

# send window
# Shift + Alt + ,
shift + alt - 0x2B : yabai -m window --display prev \
    && yabai -m display --focus prev
# Shift + Alt + .
shift + alt - 0x2F : yabai -m window --display next \
    && yabai -m display --focus next

# toggle mission control
shift + alt - m : yabai -m space --toggle mission-control

# reset split balance
shift + alt - e : yabai -m space --balance

# 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

# fload / unfloat window and show center position
shift + alt - c : yabai -m window --toggle float && yabai -m window --grid 10:10:1:1:8:8

# float / unfloat window and resize half window
# ctrl + alt - 0x7B : yabai -m window --toggle float && yabai -m window --grid 1:2:0:0:1:1
ctrl + alt - 1 : yabai -m window --toggle float && yabai -m window --grid 1:2:0:0:1:1
# ctrl + alt - 0x7C : yabai -m window --toggle float && yabai -m window --grid 1:2:1:0:1:1
ctrl + alt - 0 : yabai -m window --toggle float && yabai -m window --grid 1:2:1:0:1:11

###########################################################
# Stackline
###########################################################
# shift + alt - b : hs -c 'stackline.config:toggle("appearance.showIcons")'

Mac起動時に自動で起動させる

ターミナルに以下のコマンドを入力してください。これでMac起動時に自動で動くようになると思います。

brew services start yabai
brew services start skhd
  • 初回起動時にMac操作のアクセス許可を求められますので許可してください。

挙動がおかしいなと思ったとき

yabai / skhd の挙動がおかしいときは以下のコマンドを入力すれば直ると思います。

yabai --restart-service
skhd --restart-service

コラム —— 効率化とは?

私は yabai + skhd を最初からずっと使ってきました。yabai を導入したときはまだ github も知らなかったし、ターミナルも触ったことがなかった一般Macユーザーでした。いま現在でもプログラミングを囓った程度の素人です。そのとき設定した ~/.config/ 以下のファイルは dotfiles で管理していますし、コマンドひとつで今現在のPC環境を構築できるようにしています。

しかし、そのときにいろいろ勉強になったことがあって良かったと思う反面、効率化を求めるためには学習コストをかけなければならず非効率ではないのか?という疑問が常に私にはありました。
(そして「真の学習」では非効率である方がいいと思っています —— 「急がば回れ」です)

世の中には効率的なタクスワークフローを提供する便利なアプリがたくさんあると思います。
私はよくこんな経験をたくさんしてきました ———

効率化を求めすぎると逆に非効率に陥ってしまう

——— 私はこれを「効率化の罠」と呼んでいます。
(効率化を求める前に、まずはそれに実際に着手することがもっとも重要です)

同じような経験をしてきた人はあまたに及ぶと思っています。


これと関係なさそうでありそうな話をツラツラとしてみます。

最近 Apple Watch を買いました。Apple Watch で使うと最高のアプリ、それはApple純正の「リマインダー」です。「◯◯◯◯◯を今日の夜リマインドして」と喋りかけるだけOKです。後回しにしたいことをメモ代わりにすれば、一旦そのことを忘れることができます。あとで腕に振動(通知)が来てそれを思い出せます。

色々なアプリを使ってみて思うことがあります。シームレスな純正アプリの優位性は検索にあるということです。これが(効率的な使い方ではなく)効果的な使い方だと最近思っています。

Apple製品を使っている人は純正アプリを(Androidの人はGoogleのアプリを)できるだけ使ってみてください。MacやiPhoneでスポットライト検索すれば内容が引っかかるようになります。メモ帳に「読みたい書籍リスト」を殴り書いておけば(キレイにまとめようとしないこと!)、スポットライト検索で「読みたい書籍」と検索すれば一発で出てきます。わざわざアプリを立ち上げたり探したりする手間がかなり省けます。

最近はスマホ新法なるものが注目されているみたいですね。アプリストア市場の自由化よりも、「スポットライト検索で、すべてのアプリケーションの中身についても検索対象にしなければならない」とされた方が、純正アプリのシームレスな連携の最大の優位性を失うと思っています。

よかったらシェアしてね!
Contents