Weztermで透過設定を有効にしていました。設定ファイルにはwindow_background_opacity = 0.9と記述し、美しい半透明のターミナルを実現していました1。しかし、Emacsを起動した瞬間、その透過効果が消えました。
正確には、Emacsのウィンドウだけが完全に不透明になったのです。他のアプリケーションではWeztermの透過設定が正しく機能しています。Emacsを起動すると、背景が真っ黒になります。設定ファイルを何度確認しても、間違いはありません。
問題はWezterm側ではなく、Emacs側にありました。
1. Emacsの透過設定が原因だった
調査を進めると、Emacs 29以降に導入されたalpha-backgroundというフレームパラメータが原因だと分かりました2。このパラメータは、テキストを不透明に保ったまま、背景だけを透過できる優れた機能です3。しかし、設定されていると、ターミナルエミュレータの透過設定を上書きしてしまいます。
私のinit.elには、以前設定した透過関連のコードが残っていました。そのコードが、Weztermの制御を奪っていたのです。
Emacsには独自の描画システムがあります。フレームという単位でウィンドウを管理し、そのフレームに対して様々なパラメータを設定できます。alpha-backgroundもその一つです。一度このパラメータが設定されると、Emacsは背景の描画を自分で制御しようとします。ターミナルエミュレータが「透過にしてください」と指示しても、Emacsは「いや、私が管理します」と応答するわけです。
この仕組みは、Emacsを直接起動する場合には便利です。しかし、ターミナルエミュレータ内で動作させる場合、制御の主導権が曖昧になります。結果として、意図しない動作が発生しました。
2. unspecified-bgという解決策
解決策は、Emacsに背景色の制御を放棄させることでした。具体的には、背景色をunspecified-bgに設定します4。
~/.emacs.d/init.elに以下のコードを追加しました。
;; 背景色をターミナルに任せる
(set-face-background 'default "unspecified-bg" nil)
;; フレームの透過設定を削除
(set-frame-parameter nil 'alpha-background nil)
(set-frame-parameter nil 'alpha nil)
Code language: JavaScript (javascript)
最初の行が核心です。set-face-background関数で、デフォルトフェイスの背景色をunspecified-bgに設定しています。フェイスとは、Emacsにおける文字の見た目を制御する仕組みです。デフォルトフェイスは、特に指定がない場合の標準的な見た目を定義します。
unspecified-bgは特殊な値です。これを設定すると、Emacsは「背景色は私が決めません」と宣言します。代わりに、ターミナルエミュレータの設定を尊重します。Weztermが透過を指定していれば、その透過設定がそのまま適用されます。
続く2行は、既存の透過関連パラメータをクリアしています。alpha-backgroundとalphaの両方をnilに設定することで、Emacsの透過制御を完全に無効化します。念を押すための処理です。
3. 設定を反映させる
コードを追加したら、Emacsを再起動します。単に設定ファイルを保存するだけでは不十分です。フレームパラメータの変更は、フレームの初期化時に読み込まれるためです。
再起動後、Weztermの透過設定が復活しました。Emacsのウィンドウ背景が半透明になり、デスクトップの壁紙が透けて見えます。他のアプリケーションと同じように、Emacsも透過効果を持つようになりました。
4. 確認方法
設定が正しく反映されているか確認する方法があります。Emacs内で以下を実行します。
M-: (face-background 'default) RET
Code language: HTTP (http)
M-:はeval-expressionコマンドのキーバインドです。このコマンドは、入力したEmacs Lispコードをその場で評価します。(face-background 'default)は、デフォルトフェイスの背景色を取得する関数呼び出しです。
正しく設定されていれば、ミニバッファに"unspecified-bg"という文字列が表示されます。もし別の値(例えば"#000000"のような色コード)が表示された場合、設定が反映されていません。init.elの記述を再確認してください。
5. なぜこの方法が有効なのか
unspecified-bgを使う方法は、制御の所在を明確にします。Emacsは背景色について何も主張しません。すべての決定権をターミナルエミュレータに委譲します。
この考え方は、レイヤーの分離に基づいています。Weztermは描画レイヤーを担当し、Emacsはテキスト編集レイヤーを担当します。それぞれが自分の責任範囲に集中することで、衝突を避けられます。
ただし、この方法にも限界があります。Emacsの透過度を細かく制御したい場合、unspecified-bgでは不十分です。例えば、「アクティブなウィンドウは透過度80%、非アクティブは50%」といった設定はできません。そのような制御が必要なら、Wezterm側の透過を諦め、Emacsのalpha-backgroundを使う必要があります。
6. 別のアプローチ: Emacsで透過度を管理
逆の発想もあります。Wezterm側の透過を完全に無効にし、Emacs側で管理する方法です。
Weztermの設定を不透明に戻します。
-- ~/.config/wezterm/wezterm.lua
config.window_background_opacity = 1.0
Code language: JavaScript (javascript)
そして、Emacsで透過度を設定します5。
;; Emacsで透過度を制御
(set-frame-parameter nil 'alpha-background 85)
(add-to-list 'default-frame-alist '(alpha-background . 85))
Code language: PHP (php)
この方法なら、Emacsごとに異なる透過度を設定できます。GUI版Emacsとターミナル版Emacsで透過度を変えることも可能です。柔軟性は高まりますが、設定箇所が増えるため、管理が複雑になります。
私はunspecified-bgを選びました。設定の一元管理ができるからです。Weztermの設定ファイル一箇所で透過度を制御できます。シンプルです。
7. まとめ
Emacsを起動すると、Weztermの透過設定が効かなくなる問題に直面しました。原因は、Emacsのalpha-backgroundパラメータがターミナルの制御を上書きしていたことでした。
解決策は、Emacsの背景色をunspecified-bgに設定することでした。これにより、Emacsは背景色の制御をターミナルエミュレータに委譲し、Weztermの透過設定が正しく反映されるようになりました。
設定は簡単です。init.elに3行追加するだけです。しかし、その背後には、制御の所在という重要な概念があります。誰が何を管理するのか。責任範囲を明確にすることで、予期しない動作を防げます。
透過ウィンドウは些細な機能かもしれません。しかし、こうした小さな問題の解決過程から、ソフトウェアの設計思想を学べます。それが、技術を扱う面白さの一つです。
- window_background_opacityは0.0(完全透過)から1.0(完全不透明)の範囲で指定します。この機能を使用するには、macOS、Windows、WaylandなどのコンポジティングをサポートするOSが必要です。X11環境では別途コンポジティングウィンドウマネージャーが必要です。 – Colors & Appearance – Wez’s Terminal Emulator
- alpha-backgroundは2022年1月30日にEmacs本体に統合されました。それ以前は、Håkon Flatvalによるパッチ版として提供されていました。このパラメータは、テキストを不透明に保ったまま背景のみを透過できる機能です。 – True Emacs Transparency
- 従来のalphaパラメータは、ウィンドウ全体(テキストと背景の両方)を透過させるため、テキストの可読性が低下する問題がありました。alpha-backgroundはこの問題を解決し、背景のみを透過させることができます。 – TransparentEmacs – EmacsWiki
- unspecifiedはEmacs内部で使用される特殊な値で、属性が明示的に指定されていないことを示します。unspecified-bgは背景色、unspecified-fgは前景色を未指定にする際に使用されます。 – Face Attributes – GNU Emacs Lisp Reference Manual
- alpha-backgroundの値は0から100の範囲で指定し、100が完全不透明、0が完全透過を表します。set-frame-parameterで現在のフレームに、default-frame-alistで今後作成されるすべてのフレームに適用されます。 – TransparentEmacs – EmacsWiki