Annieの部屋

ウィーンのホーフブルク宮殿の中にある非常口がアイコンの、きっとエンジニアな人。

Hammerspoonでmacの画面分割を快適(?)にしてみた

windowsを使っている場合、「win」+「方向キー←↓↑→」で簡単に画面分割することができます。
macの画面分割は「Split View」というデフォルトの機能があります

support.apple.com

が、対象の画面を長押しないといけなかったり、全画面表示になっちゃったり、ショートカットキーでサクッと分割することができないので、ずっと有料のMagnet.appを使っていました。

そんな中、「実はこの画面分割って自分で実装できたりしないかな」という考えに至り、やってみたらできたので紹介したいと思います。

無料アプリ ShiftIt を使うという選択肢

無料アプリで同じようなことを実現しているものがないか探してみたところ、ShiftItというものがありました。

github.com

このままこれを使ってもいいかなと思ったのですが

Looking for a new maintainer #296.
This project is looking for a new maintainer. Until that transition is completed, there will likely not be further development on this project.
Alternatives
A highly recommended alternative to ShiftIt that can be implemented using Hammerspoon.

ということで、代替アプリとしてHammerspoonを使うことが推奨されていたのでHammerspoonにしてみることにしました。

ちなみに、最初アプリ名を「しふと えるてぃー」と読んでいましたが、Repositoryの文字列を ASCIIコード変換機 にかけてみたところ、6文字目は0x49だったので小文字のLではなく大文字のI(アイ)でした。

(個人的にはマニュアル車のシフトレバーのようなアイコンが素敵で惹かれました。)

Hammerspoon とは?

Hammerspoonとは、Macにホットキーなどを設定できるユーティリティだそうです。

www.hammerspoon.org

Getting Started

実行環境

Hammerspoon をインストール

インストール手順は公式のRepositoryにも記載されていますが、

  • 最新のアプリ(zip)をダウンロードしてApplicationsフォルダに配置する
  • brew installコマンドを使う

の2択です。とても簡単。

私はbrew installの方を実施しました。

$ brew install hammerspoon --cask

ちなみに2021年9月18日現在、Hammerspoonの最新versionは0.9.90です。

$ brew info hammerspoon
hammerspoon: 0.9.90 (auto_updates)
https://www.hammerspoon.org/
/usr/local/Caskroom/hammerspoon/0.9.90 (125B)
From: https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/hammerspoon.rb

画面分割の設定を書く

アプリを立ち上げて、メニューバーのOpen Configを押すと~/.hammerspoon/init.luaというファイルが開かれます。
ここに自分が設定したい内容を書いていけば良いのですが、拡張子からわかるように言語はLuaです。
Luaは初挑戦ですが、公式のRepositoryのWikiにサンプルがたくさん載っていたので、それを参考にしてなんとか実装できました。

今回設定する内容は、Magnet.appの画面分割の中でも個人的に使用頻度の高かった、画面を二分割するためのショートカットキーにしました。

  • Control (⌃) + Option (⌥) + ← : フォーカスしているwindowを左半分に寄せる
  • Control (⌃) + Option (⌥) + → : フォーカスしているwindowを右半分に寄せる
  • Control (⌃) + Option (⌥) + ↑ : フォーカスしているwindowを上半分に寄せる
  • Control (⌃) + Option (⌥) + ↓ : フォーカスしているwindowを下半分に寄せる

出来上がった設定ファイルはこんな感じです。

hs.window.animationDuration = 0
units = {
  right50       = { x = 0.50, y = 0.00, w = 0.50, h = 1.00 },
  left50        = { x = 0.00, y = 0.00, w = 0.50, h = 1.00 },
  top50         = { x = 0.00, y = 0.00, w = 1.00, h = 0.50 },
  bot50         = { x = 0.00, y = 0.50, w = 1.00, h = 0.50 }
}

mash = { 'ctrl', 'option' }
hs.hotkey.bind(mash, 'right', function() hs.window.focusedWindow():move(units.right50, nil, true) end)
hs.hotkey.bind(mash, 'left', function() hs.window.focusedWindow():move(units.left50, nil, true) end)
hs.hotkey.bind(mash, 'up', function() hs.window.focusedWindow():move(units.top50, nil, true) end)
hs.hotkey.bind(mash, 'down', function() hs.window.focusedWindow():move(units.bot50, nil, true) end)

Luaの構文だけでなく、Hammerspoon独自の関数なども確認する必要がありますが、思ったよりも直感的にかける印象を受けました。(たくさん登場しているhsはHammerspoonのfunctionを指している感じですね)

この設定をinit.luaに上書きしたら、メニューバーのReload Configを押すことで設定が反映されます!

完了!!

コードを見ていく

今回実装したコードを(理解できている範囲で)少しだけ見ていこうと思います。(間違っていたらこっそり教えて下さい・・・。)

windowの動かし方を決める

units = {
  right50       = { x = 0.50, y = 0.00, w = 0.50, h = 1.00 },
  left50        = { x = 0.00, y = 0.00, w = 0.50, h = 1.00 },
  top50         = { x = 0.00, y = 0.00, w = 1.00, h = 0.50 },
  bot50         = { x = 0.00, y = 0.50, w = 1.00, h = 0.50 }
}

こちらは、windowを右半分・左半分にするときに、windowの左上をスクリーンのどこ(x,y)にどんな幅&高さで配置するかというルールを記述しています。
C言語で言う構造体のように見えますが、これはLuaTable型で、keyとvalueで構成される連想配列らしいです。

各keyにはその後の画面分割の処理で使いやすいように、Hammerspoonの構文に従って{x=X, y=Y, w=W, h=H}という形式でtable型のvalueとして指定しています。

ショートカットキーとwindowの動かし方を組み合わせる

hs.hotkey.bind(mash, 'right', function() hs.window.focusedWindow():move(units.right50, nil, true) end)

hs.hotkey.bind の引数には、以下のような値を指定していきます。

  1. Command (⌘), Control (⌃), Option (⌥), Shift (⇧)キー名を0個以上指定
    • ここでは'ctrl''option'のtableを指定している
  2. キーボードのキー名またはキーボードの番号を指定
    • ここではのキー名の'right'を指定している
  3. キーが押されたときに呼び出す関数を指定
    • Luaの関数はfunction()で始まりendで終わる
    • ここでフォーカスしているwindowを移動させるHammerspoonの機能 hs.window.focusedWindow():move()を実行して、画面分割させる!

まとめ & 感想

画面分割のショートカットキーをHammerspoon + Luaで簡単に実装することができました!

Hammerspoonにkey bindingや画面を動かすための関数が用意されていて、そこに引数を渡すだけで済んだので、予想以上に短いコードで実現することができて驚いています。

そして楽しかったです!

今回のサンプルコード(+α)はこちらに載せています。 github.com