Emacs をすばやく使うために、みなさんemacsclient
を活用されていることかと思います。いちいち Emacs を起動しなくてよいため非常にべんりですが、いつからか(最初からかも?)emacsclient
を叩いた際にかってにフレームを前面にもってきてくれなくなっていて、つらい思いをする日々が続いていました。
ということでめんどくさがらずに、ちゃんとコードを書いて使いやすいようにしましたというお話です。
用意するもの
ここではwmctrl
というツールを利用します。コマンドラインから X ウィンドウマネージャを制御するものです。Wayland 環境のみなさんはごめんなさいですが、今回の方法は使えないと思います。
(余談: 個人的に SKK ユーザということもあり、SandS を実現するためにxcape
を利用しているので、X11 を使いつづけています。Wayland 環境での SandS ベストプラクティス情報ほしいです……)
閑話休題、apt
なりなんなりでインストールしましょう
# apt install wmctrl
Emacs の設定
Emacs のフレーム(一般的用語におけるウィンドウですね)を最小化するにはiconify-frame
、ポップアップするにはraise-frame
という関数が用意されている……のですが、raise-frame
を使っても最小化された Emacs のフレームがポップアップされません。なぜだ。
ということで外部ツールを利用する必要があります。先ほど紹介したwmctrl
ですね。Elisp にはcall-process
という関数があり、これで外部プログラムを実行できるため、この仕組みを利用します。
(defun raise-frame-with-wmctrl (&optional frame)
;; (call-process "wmctrl" nil nil nil "-s" "1") ;; 仮想デスクトップ使ってるときはこれも追加で必要
(call-process "wmctrl" nil nil nil "-i" "-R"
(frame-parameter (or frame (selected-frame)) 'outer-window-id)))
(advice-add 'raise-frame :after #'raise-frame-with-wmctrl)
(add-hook 'server-switch-hook #'raise-frame)
(add-hook 'server-done-hook #'iconify-frame)
上記のコードをinit.el
に書いておきましょう。やっていることは単純で、現在のフレームの Window id を取得し、それをwmctrl
に渡しているだけです。
最小化するiconify-frame
については、特に問題ない(と思う)ので、そのまま hook に追加しておくだけでよいでしょう。
これで、端末からemacsclient
でファイルを開いた際に勝手にウィンドウがポップアップし、編集を終了すると勝手に最小化されるはずです。
追記(2019-08-29)
この設定だと Magit で commit メッセージを書いた後にも Emacs のフレームが最小化されてしまいます。これは commit メッセージ入力用のバッファを開く際に emacsclient が利用されているためです。このような仕組みになっている理由はこちらにまとめられていますが、なんにしてもこれではとてつもなく不便なので、この挙動を修正しましょう。
(defun suppress-iconify (&rest arg)
(remove-hook 'server-done-hook #'iconify-emacs-when-server-is-done))
(defun apply-iconify (&rest arg)
(add-hook 'server-done-hook #'iconify-emacs-when-server-is-done))
(advice-add 'magit-run-git-with-editor :before #'surpress-iconify)
(advice-add 'with-editor-finish :after #'apply-iconify)
commit メッセージ入力用のバッファを開く関数が magit-run-git-with-editor
です。なのでこの関数が呼ばれる前に server-done-hook
から最小化用の関数を外します。同様に編集を完了させる関数が with-editor-finish
なので、この後に再度最小化用の関数をserver-done-hook
に追加しています。1つの hook に同じ関数が2つ以上設定されることはないようになっているので、ふつうに emacsclient を使った場合でも問題はありません。