Xlib。 XGrabButtonがクリックを消費しないようにするには?

Xlib。 XGrabButtonがクリックを消費しないようにするには?

最初の質問です。ここで要件を見落とした場合は、ご容赦ください。

Arch Linux用のウィンドウマネージャを作成しようとしています。現在のウィンドウをマップする前にキャッチイベントを追加します。

私は以下を持っています:

XGrabButton(display, Button1, 0, window, false, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);

Button1 を押すと、次のコマンドを実行します。

XRaiseWindow(display, frame);

今問題は、ウィンドウで何もクリックできないことです。 ButtonPressed コールバック関数のみ実行中です。 (1)ウィンドウがButtonPressイベントも受信していることを確認し、(2)ウィンドウが現在のトップレベルウィンドウでない場合にのみXRaiseWindow関数を実行するにはどうすればよいですか(ここではstack_modeを使用できるとします)。

答え1

Arch Linux用のウィンドウマネージャを作成しようとしています。

これはかなり大きなプロジェクトです。あなたの立場では、まず既存のウィンドウマネージャのコードを見てみましょう。

現在のウィンドウをマップする前にキャッチイベントを追加します。

IIRC、それは彼らがすることではありません。代わりに「リダイレクト」を介してイベントを登録しますSubstructureRedirectMask。たとえば、次のように見てください。これ記事シリーズ。

今問題は、ウィンドウで何もクリックできないことです。

はい、ボタンイベントをキャッチしたので、すべてのボタンイベントがウィンドウの代わりにウィンドウマネージャに渡されます(これがウィンドウマネージャが通常キャッチを実行しない理由です)。これに固執する場合(そうではありません)、各ボタンイベントをウィンドウに渡す必要があるかどうかを判断し、キャッチをキャンセルし、2番目の合成イベントを生成してから、渡す必要がある各ボタンをもう一度キャッチする必要があります。窓口イベントに渡されます。これはあまり愚かなアプローチではありません。

ウィンドウが現在のトップレベルウィンドウでない場合にのみ、XRaiseWindow関数を実行してください。

それでは、これはフォーカス戦略(「クリックして上げる」)に関連していますか?実際にこれがどのように実装されているのかよくわかりません。上記のように、既存のウィンドウマネージャのコードを読んで調べました。 X自体で構成することもできます。

答え2

私(1)(ここで発見)。

ポインタモードをGrabModeSyncに変更して使用します。

XAllowEvents(display, ReplayPointer, event.time);
XSync(display, 0);

ButtonPressed コールバックにクリックイベントを送出します。

それでも(2)を探していますが、必要であることを確認せずにXRaiseWindowを呼び出すだけでは問題になりません。明らかに、私は何も紹介するのではなく、これは私の個人的な経験です。

答え3

(2)の場合、私がこれまでに学んだことを私のWMプロジェクトで共有することができます。もちろん、私はこれらの技術のいくつかについてさらに学ぶためにここに来ました。おそらく、これは他の人に役立ちます。少なくとも自分自身のために話すことができます。

まず、WMにXに、どのウィンドウが「アクティブ」なのかを問わずに知ることができる一種の変数がすでにあるでしょう。スタックの上部にあるウィンドウとフォーカスがあるウィンドウ(キーボード入力を受け取る)には違いがあります。

私たちに精通しているほとんどのデスクトップはほとんど常に同じですが、AFAICT Xはそれをあまり気にしません。する方法)。 XRaiseWindow() 自体ただウィンドウを上げてもフォーカスはまったく変わりません。

とにかく入力フォーカスを直接管理しているので、ウィンドウがアクティブなウィンドウであるか内部的に知る方法が既にあるはずです。ほとんどのページャ/タスクバーが読み取るルートウィンドウの_NET_ACTIVE_WINDOWプロパティを更新するには、この情報が必要です。フォーカスを直接リセットする必要があるいくつかの極端なケースがあります(たとえば、トップレベルのフォーカスを持つウィンドウが破壊された後にIME)。閉鎖。

私のWMでは、すべてのクライアント/コンテナ/装飾されたウィンドウ/特定のWMが呼び出すすべてを接続リストに保持し、ウィンドウが上がる/フォーカスが合うたびに、次のフォーカスを非常に簡単に見つけるシナリオを見つけました。クライアントリストの最後にある構造体へのポインタを移動します。これはリンクリストの削除+挿入なので、非常に手頃な作業です。その後、ルートウィンドウのFocusInを取得したり、フォーカスを更新するためにWMの助けが必要であると判断した場合は、リストの最後のウィンドウを取得してそのウィンドウにフォーカスを置くことができます。

私が言及したいもう一つのポイントは、あなたが見つけたようにGrabButtonとAllowEventsを使用して「クリックフォーカス」を検出してウィンドウをクリックしたときにタイトルバーだけでなく、タイトルバー/クライアント領域を所有していなかったためです。しかし、ここにいくつかのトリックがあります。現在ピントが合っていないウィンドウのボタンだけをつかむ必要があります。ユーザーがそのいずれかをクリックしてリフト+フォーカスを受け取ると、キャッチを解除します。実際にフォーカスを変更したいときはいつでも使用する「SetFocus」関数でこれを行います。これは、私がこの技術を学ぶときに持っていた最初の反対を解決するという意味です。私たちは、すべてのマウスクリックが常に傍受され、WMに返送されてから再放送されたくありません。これは非常に非効率的で、ユーザーが非常に強烈なFPSゲームなどをプレイしている場合、追加の待ち時間が見やすくないだけでなく、実際には重要かもしれませんか?ただし、ウィンドウのフォーカスに基づいてキャッチを追加および削除することで、目的の機能を完了するために必要なものだけが傍受され、ほとんどのクリックは二重返されません。

まだ把握していない唯一の部分は、アクティブなGrabButtonがあり、クリック後にAllowEventsを呼び出さない限り、Xはもはやボタンイベントを送信しないことです。なぜこれが起こるのか完全にはわかりませんが、これが起こる可能性があることに注意してください。キャッチ機能付きのウィンドウで、ボタン1(My GrabButtonと同じ条件)を含むButtonPressを受信するたびにAllowEventsを送信します。ただし、これは元のイベントと再生されたイベントを区別できるようにしたいという意味です。それ以外の場合は、クライアント領域の代わりにタイトルバー(*)をクリックするように求めると、ButtonReleaseを介さずに2つのButtonPressイベントを受け取ります。 WM符号化方法によっては、微妙な問題が生じる場合もあれば、そうでない場合もある。

2つを区別する必要がある場合、これまで私が見つけた最良の方法は、イベントの「時間」フィールドを使用することです。このフィールドは通常増加しますが、再生されたイベントが返されたときにキャプチャされたバージョンと同じです。したがって、最後にクリックした時間フィールドを記録することができ、対応するボタン+ステータスは、対応するウィンドウ/クライアントのメンバー変数から取得できます。たとえば、そこからButtonPressを修飾した以前の適格ButtonPressと同じ時間フィールドの取得を無視します。窓。

(*)私が作成したCONTAINER / DECORウィンドウのボタンをつかみ、元のクライアントウィンドウの代わりにクライアントの親を再割り当てしています。クライアントが私の子供にリセットされたため、まだクライアント領域のクリックをキャプチャします。コンテナ。クライアントウィンドウをキャッチすることで二重イベントを回避することはできますが、クライアントウィンドウを所有しておらず、クライアントが常に私のコードと対話できるため、必要以上にクライアントウィンドウと対話することは好きではありません。非同期的には、慎重に計画する必要があるすべての種類の競合条件を生成するか、親の再割り当てなどの重要な部分でXGrabServerを使用してからサーバーに問い合わせて、GrabServerの後にウィンドウがまだ存在するかどうかを確認する必要があります。 dwmなどの一部の最小WMは、これらの競合のためにサーバーから返される可能性があるエラーをキャプチャして無視します。しかし、これはできるだけ最小限に抑えるように努力しているので、現在はコンテナを先取りしています。

ただし、クライアントウィンドウでGrabButtonを実行すると、最初にキャッチを実行せずにSelectInputのみを実行できます。

関連情報