macOSでEmacsをデーモン運用した際、emacsclient から作られるフレームが最大化されない問題に直面しました。window-setup-hook だけでなく、after-make-frame-functions にも、フレームを最大化する処理をフックしました。
(defun my/maximize-frame (&optional frame)
(let ((f (or frame (selected-frame))))
(when (display-graphic-p f)
(run-at-time 0.05 nil
(lambda (fr)
(when (frame-live-p fr)
(with-selected-frame fr
(set-frame-parameter fr 'fullscreen 'maximized))))
f)))
(add-hook 'window-setup-hook #'my/maximize-frame)
(add-hook 'after-make-frame-functions #'my/maximize-frame)Code language: Lisp (lisp)
1. デーモン起動とフレーム生成の違い
macOSでEmacsをデーモン(server)起動し、普段は emacsclient から編集を始める、という運用をしています。
ただ、この運用に切り替えてから、emacsclient を起動すると、ウィンドウが最大化されないのです。
まずは、単純に「最大化の設定が足りないのだろう」と考えました。
そこで、「どんなモニタでもフレームを最大表示する設定 #init.el」を参考に init.el に書きました。
(defun maximize-frame ()
(set-frame-parameter nil 'fullscreen 'maximized))
(add-hook 'window-setup-hook 'maximize-frame)Code language: Lisp (lisp)
通常起動(emacs コマンド)では、これで問題なく最大化されます。
ところが、デーモン起動(emacs --daemon)の場合、emacsclient で開いたウィンドウには効きませんでした。
ここで気づいたのは、「Emacs本体の起動」と「フレーム(ウィンドウ)の生成」は別物だ、という点です。
デーモン運用では、Emacs自体は先に起動し、その後にクライアント要求ごとにフレームが追加されます。
window-setup-hook は、あくまでEmacs本体の初期セットアップ時に一度だけ呼ばれます。
あとから作られるフレームには、関知しません。
この挙動を理解していなかったのが、最初のつまずきでした。
2. after-make-frame-functions に切り替える
そこで次に試したのが、フレーム生成時に呼ばれるフックです。
(add-hook 'after-make-frame-functions #'my/maximize-frame)Code language: Lisp (lisp)
理屈としては正しく、実際に動くこともありました。
ただ、macOSでは「たまに最大化されない」という現象が残りました。
どうも、ウィンドウが「まだOSに完全に認識されていないタイミング」で最大化を指示してしまい、結果として無視されているようです。
3. 少し待ってから最大化する、という解決
最終的に落ち着いたのが、処理をわずかに遅らせる方法です。
(defun my/maximize-frame (&optional frame)
(let ((f (or frame (selected-frame))))
(when (display-graphic-p f)
(run-at-time 0.05 nil
(lambda (fr)
(when (frame-live-p fr)
(with-selected-frame fr
(set-frame-parameter fr 'fullscreen 'maximized))))
f)))
(add-hook 'window-setup-hook #'my/maximize-frame)
(add-hook 'after-make-frame-functions #'my/maximize-frame)Code language: Lisp (lisp)
run-at-time を使い、0.05秒だけ遅延させています。
数値自体に深い根拠はなく、何度か試して安定した値です。
この変更で、emacsclient 起動時も、ほぼ確実に最大化されるようになりました。「イベントが完全に落ち着くのを、ほんの少し待つ」という発想がポイントだと思います。
3.1. サーバ再起動が必要
ちなみに、設定を変えたあと、Emacsサーバを一度 kill して再起動しないと挙動が変わりません。
emacsclient -e '(kill-emacs)'Code language: JavaScript (javascript)
これは冷静に考えると当然で、init.el はデーモン起動時に一度だけ読み込まれます。
設定を書き換えても、常駐しているEmacsはそれを知りません。