GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Input-Focus.html ・ emacs-doc.conao3.com

30.11 入力フォーカス

Emacsでは、常に1つのフレームが選択フレームとなります。選択中のウィンドウ(ウィンドウの選択を参照)は、常に選択フレーム上に配置されます。

Emacsが複数のターミナルにフレームを表示する場合(複数ターミナルを参照)、各ターミナルにはそれぞれ選択済みのフレームがあります。しかし、その中で唯一の選択済みフレームとなるのが、直近の入力があったターミナルに属するフレームです。つまり、Emacsがあるターミナルから届いたコマンドを実行する際、選択済みフレームはそのターミナルのフレームになります。Emacsは一度に一つのコマンドしか実行しないため、一度に考慮すべき選択済みフレームは一つだけであり、このフレームを本マニュアルでは選択済みフレームと呼びます。選択済みフレームが表示されるディスプレイを選択済みフレームのディスプレイと呼びます。

関数: selected-frame

この関数は、選択されたフレームを返します。

ウィンドウシステムやウィンドウマネージャによっては、キーボード入力をマウスが位置するウィンドウオブジェクトに直接送るものもあれば、さまざまなウィンドウオブジェクトへフォーカスを移動させるために明示的なクリックやコマンドを必要とするものもあります。いずれの場合でも、Emacsはどのフレームにフォーカスがあるかを自動的に追跡します。Lisp関数から明示的に別のフレームに切り替えるには、select-frame-set-input-focusを呼び出してください。

前段の複数形「フレーム」は意図的なものです。Emacs自体で選択されているフレームは1つだけですが、Emacsは多くの異なるターミナル上にフレームを持つことができ(ウィンドウシステムへの接続もターミナルとしてカウントされることを思い出してください)、各ターミナルは、どのフレームが入力フォーカスを持っているかについて独自の認識を持っています。ユーザー入力が個別の「シート」に整理されるX Window Systemでは、各シートがさらに独自の入力フォーカスを持つことができます。フレームに入力フォーカスを設定すると、そのフレームのターミナルについて、最後にEmacsとやり取りしたシートにフォーカスが設定されますが、他のターミナルやシート上のフレームは、引き続きフォーカスされたままになる場合があります。

指定された端末でユーザー操作が行われる前に入力フォーカスが設定された場合、Xサーバーはランダムなシート(通常は番号が最も小さいもの)を選択し、そこに入力フォーカスを設定します。

Lispプログラムは、関数 select-frame を呼び出すことで一時的にフレームを切り替えることができます。これによりウィンドウシステムのフォーカス概念が変わるわけではなく、むしろ、何らかの形で制御が再確立されるまで、ウィンドウマネージャの制御から脱出することになります。

テキスト端末を使用する場合、端末上に一度に表示できるフレームは1つだけであるため、select-frame を呼び出した後の次の再表示では、実際に新しく選択されたフレームが表示されます。このフレームは、次に select-frame を呼び出すまで選択されたままとなります。テキスト端末上の各フレームには番号が割り当てられており、バッファ名の前のモードラインに表示されます(モードラインで使用される変数を参照)。

関数: select-frame-set-input-focus frame &optional norecord

この関数は frame を選択し(他のフレームに隠れている場合に備えて)、それを前面に表示して、ウィンドウシステムのフォーカスを得ようとします。テキスト端末では、次の再表示時に新しいフレームが端末画面全体に表示されます。オプション引数 norecord は select-frame と同様の意味を持ちます(後述)。この関数の戻り値は重要ではありません。

理想的には、次に説明する関数は、フレームを他のフレームより上に持ち上げることなく、そのフレームにフォーカスを当てるべきです。残念ながら、多くのウィンドウシステムやウィンドウマネージャでは、これに対応しない場合があります。

関数: x-focus-frame frame &optional noactivate

この関数は、必ずしもフォーカスを引き上げることなく、frame に X サーバーのフォーカスを与えます。frame および nil は、選択済みのフレームを使用することを意味します。X では、オプション引数 noactivate が nil でない場合、frame のウィンドウシステムウィンドウを「アクティブ」ウィンドウにしないことを意味し、これにより frame を他のフレームより上に引き上げることをより強く抑制します。

MS-Windowsでは、noactivate 引数は効果を持ちません。ただし、frame が子フレーム(子フレームを参照)である場合、この関数は通常、frame を他の子フレームより前面に出すことなくフォーカスします。

ウィンドウシステムのサポートがない場合、この関数は何もしません。

コマンド: select-frame frame &optional norecord

この関数は、Xサーバーのフォーカスがある場合はそれを一時的に無視して、フレーム frame を選択します。frame の選択状態は、ユーザーが別のフレームを選択するための操作を行うか、あるいはこの関数が次に呼び出されるまで維持されます。(ウィンドウシステムを使用している場合、コマンドループに戻った後、以前に選択されていたフレームが依然としてウィンドウシステムの入力フォーカスを保持している可能性があるため、そのフレームが選択状態として復元されることがあります。)

指定された frame が選択フレームとなり、その終端が選択終端となります。その後、この関数は select-window をサブルーチンとして呼び出し、第1引数に frame 内で選択されたウィンドウを、第2引数に norecord を渡します(したがって、norecord が nil 以外であれば、最近選択されたウィンドウとバッファリストの順序が変わるのを避けることができます)。ウィンドウの選択 を参照してください。

この関数は frame を返し、frame が削除されている場合は nil を返します。

一般的に、作業終了後に元のターミナルに戻らずに別のターミナルへ切り替わってしまうような形で、select-frame を使用してはいけません。

関数: frame-use-time &optional frame

この関数は、フレーム frame の使用時間を返します。frame はライブフレームである必要があり、デフォルトでは選択中のフレームが指定されます。

フレームの使用時間とは、frame上の任意のウィンドウのwindow-use-timeによって報告される最大の使用時間のことです。

関数: get-mru-frame &optional all-frames exclude-child-frames exclude-frame

この関数は、frame-use-time で報告された使用時間が最も長いフレームを返します。候補となるフレームが見つからない場合は nil を返しますが、これは通常、オプション引数を用いてフレームが除外された場合に発生します。

デフォルトでは、ツールチップフレームおよびミニバッファ専用フレームは候補になりません。オプション引数 exclude-child-frames が nil でない場合、子フレームも除外されます。exclude-frame 引数が指定されている場合、指定されたフレームも同様に除外されます。実際には、直近で使用されたフレームが常に選択状態となるため、通常は選択中のフレームを指定する nil や exclude-frame 以外の引数を用いてこの関数を呼び出すのが適切です。

オプション引数 all-frames で、対象とするフレームを指定します:

  • visibleは、現在の端末またはexclude-frameの端末上のすべての可視フレームを考慮することを意味します。
  • 0は、現在の端末またはexclude-frameの端末上にある、すべての可視フレームまたはアイコン化されたフレームを考慮することを意味します。
  • それ以外となると、すべてのフレームを検討することになる。
関数: get-mru-frames &optional all-frames exclude-child-frames exclude-frame

この関数は、各フレームの直近で使用されたウィンドウを用いて算出される frame-use-time で報告された使用時間の長い順にソートされたフレームの一覧を返します。

デフォルトでは、ツールチップおよびミニバッファ専用のフレームは候補になりません。オプション引数 exclude-child-frames が nil でない場合、子フレームも除外されます。exclude-frame 引数が指定されている場合、それが指定するフレームも同様に除外されます。

nil を返すことがあり、これはオプション引数を用いてフレームを除外した場合に起こり得ます。例えば、フレームが1つのみで、exclude-frame が選択されたフレームである場合などが該当します。

オプション引数 all-frames で、対象とするフレームを指定します:

  • visibleは、現在の端末またはexclude-frameの端末上のすべての可視フレームを考慮することを意味します。
  • 0は、現在の端末またはexclude-frameの端末上にある、すべての可視フレームまたはアイコン化されたフレームを考慮することを意味します。
  • それ以外となると、すべてのフレームを検討することになる。
コマンド: select-frame-by-id id &optional noerror

この関数は、オープン状態で削除不能なフレームの中から、一致するフレーム識別子 id を検索します(Frames を参照)。該当するフレームが見つかった場合、必要に応じて削除を解除し、最前面に表示してフォーカスを与え、選択フレームとします。テキスト端末では、フレームを最前面に表示すると、端末ディスプレイ全体を占めるようになります。

この関数は、選択されたフレームを返すか、id が見つからない場合はエラーを返します。ただし、noerror が nil でない場合は、nil を返します。

コマンド: undelete-frame-by-id id &optional noerror

この関数は、削除不能なフレームの中から一致するフレーム識別子 id を検索します(Frames を参照)。見つかった場合、そのフレームの削除状態が解除され、最前面に表示され、フォーカスが当たり、選択フレームとなります。テキスト端末では、フレームを最前面に表示すると、端末ディスプレイ全体を占めるようになります。

この関数は、削除されていないフレームを返すか、id が見つからない場合はエラーを返します。ただし、noerror が nil でない場合は、nil を返します。

Emacsは、サーバーおよびウィンドウマネージャの要求に応じてフレームが選択されるよう制御することで、ウィンドウシステムと連携します。ウィンドウシステムからEmacsに対し、フレームのいずれかが選択されたことが通知されると、Emacsは内部的にfocus-inイベントを生成します。フォーカス変更通知をサポートするxtermなどのテキストターミナルエミュレータ上でEmacsフレームが表示される場合、テキストモードのフレームであってもfocus-inおよびfocus-outイベントが利用可能です。フォーカスイベントは通常、handle-focus-inによって処理されます。

コマンド: handle-focus-in event

この関数は、明示的なフォーカス通知をサポートするウィンドウシステムやターミナルからのフォーカスインイベントを処理します。frame-focus-state がクエリするフレームごとのフォーカスフラグを更新し、after-focus-change-function を呼び出します。さらに、Emacs における選択フレームの概念を、いずれかのターミナルで直近にフォーカスされたフレームに切り替えるため、switch-frame イベントを生成します。ここで注意すべき点は、Emacs の選択フレームを直近にフォーカスされたフレームに切り替えるからといって、他のフレームがそれぞれのターミナルで引き続きフォーカスを保持しなくなるわけではないということです。この関数を自身で呼び出さないでください。代わりに、after-focus-change-function にロジックを紐付けてください。

コマンド: handle-switch-frame frame

この関数はスイッチフレームイベントを処理します。Emacsは、フォーカス通知時や、直前のイベントとは異なるフレームに入力イベントが届いた場合など、さまざまな状況でこのイベントを自身に対して発生させます。この関数を自身で呼び出さないでください。

関数: redirect-frame-focus frame &optional focus-frame

この関数は、フォーカスを frame から focus-frame へ切り替えます。これにより、focus-frame は frame 宛ての後続のキー入力やイベントを受け取ることになります。このようなイベントの後、last-event-frame の値は focus-frame になります。また、frame を指定するスイッチフレームイベントは、代わりに focus-frame を選択します。

If focus-frame is omitted or nil, that cancels any existing redirection for frame, which therefore once again receives its own events.

フォーカスのリダイレクトの用途の一つに、ミニバッファを持たないフレームがあります。こうしたフレームは、他のフレームのミニバッファを利用します。別のフレームのミニバッファを有効化すると、フォーカスがそのフレームにリダイレクトされます。これにより、マウスがミニバッファを有効化したフレームに留まったままであっても、本来あるべき場所であるミニバッファのフレームにフォーカスが当たります。

フレームを選択すると、フォーカスのリダイレクト先が変更されることもあります。fooが選択されていた状態でフレームbarを選択すると、fooを指していたリダイレクト先がすべてbarを指すようになります。これにより、ユーザーがselect-windowを介してあるフレームから別のフレームへ切り替えた際にも、フォーカスのリダイレクトが正しく機能します。

つまり、フォーカスが自身にリダイレクトされたフレームは、フォーカスがリダイレクトされていないフレームとは異なる扱いを受けるということです。select-frameは前者には影響しますが、後者には影響しません。

リダイレクトは、変更するために redirect-frame-focus が呼び出されるまで継続します。

関数: frame-focus-state &optional frame

この関数は、frame の最後に確認されたフォーカス状態を取得します。frame はデフォルトで選択中のフレームとなります。

フレームがフォーカスされていないことが分かっている場合は nil を、フォーカスされていることが分かっている場合は t を、Emacs がフレームのフォーカス状態を把握していない場合は unknown を返します。(明示的なフォーカス通知をサポートしていないターミナルで動作している TTY フレームでは、この最後の状態が見られることがあります。)

変数: after-focus-change-function

この関数は、Emacsがフレームのフォーカス獲得または喪失を検知した際に、引数なしで呼び出されます。フォーカスイベントは非同期に配信され、想定通りの順序で配信されるとは限らないため、フォーカスされているフレームの状態に応じて処理を行いたいコードは、すべてのフレームを走査して確認する必要があります。

例えば、フレームにフォーカスがあるか否かに応じて背景色を設定する、シンプルなサンプル関数を以下に示します。

(add-function :after after-focus-change-function
              #'my-change-background)
(defun my-change-background ()
  (dolist (frame (frame-list))
    (pcase (frame-focus-state frame)
      (`t (set-face-background 'default "black" frame))
      (`nil (set-face-background 'default "#404040" frame)))))

フォーカスイベントの配信方式の違いや、複数のEmacsターミナルの存在、その他の要因により、複数のフレームが同時に入力フォーカスを得ているように見える場合があり、コードはこうした状況に対しても堅牢である必要があります。

ウィンドウシステムによっては、フォーカスイベントが期待される値に落ち着くまで、異なるフォーカス状態で繰り返し通知される場合があります。フォーカス通知に依存するコードでは、フォーカス変更に伴い発生するユーザーから見える更新を、例えば再表示まで処理を遅延させるなどして「デバウンス」させるべきです。

この関数は、read-event 内部からも含め、任意のコンテキストで呼び出されることがあるため、プロセスフィルタを記述する際と同様の注意を払ってください。

ユーザーオプション: focus-follows-mouse

このオプションは、マウスポインタをフレーム内に移動させた際に、ウィンドウマネージャがフォーカスを転送するかどうか、およびその方法をEmacsに通知します。これには3つの意味のある値を指定できます:

nil

ウィンドウマネージャが「クリック・トゥ・フォーカス」方式を採用しており、フレームにフォーカスを当てるにはそのフレーム内でマウスをクリックする必要がある場合は、デフォルト値の nil を使用してください。

t

ウィンドウマネージャがフォーカスをマウスポインタの位置に自動的に追従させる仕様であるものの、フォーカスを得たフレームが自動的に最前面に表示されず、他のウィンドウシステムのウィンドウに遮られたままになる可能性がある場合には、値 t を使用してください。

auto-raise

ウィンドウマネージャがマウスポインタの位置に自動的にフォーカスを合わせ、フォーカスを得たフレームが自動的に表示される場合は、値 auto-raise を使用してください。

このオプションが nil 以外の場合、Emacs はマウスポインタを select-frame-set-input-focus で選択されたフレームに移動します。この機能は、例えば other-frame や pop-to-buffer など、多くのコマンドで使用されています。

「通常」のフレームでは、ウィンドウマネージャが通常それらを最前面に表示するため、値 t と auto-raise の区別は不要です。mouse-autoselect-window を介して子フレームを自動的に最前面に表示させることは有用です(マウスウィンドウの自動選択を参照)。

なお、このオプションでは、「スロップィ」フォーカス(それまでフォーカスされていたフレームが、マウスポインタが別のウィンドウシステムウィンドウに移動しない限りフォーカスを維持する状態)と、「ストリクティ」フォーカス(マウスポインタが離れた瞬間にフレームがフォーカスを失う状態)を区別しません。また、ウィンドウマネージャが、新しいフレームがフォーカスされるか、あるいは自動的に最前面に表示されるまでの時間を明示的に指定できる遅延フォーカスやオートレイジングをサポートしているかどうかも認識しません。

変数 mouse-autoselect-window をカスタマイズすることで、個々の Emacs ウィンドウに対して「フォーカスはマウスに従う」ポリシーを適用できます(マウスウィンドウの自動選択 を参照)。