emacsclientのウィンドウが最大化する

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はそれを知りません。