web-dev-qa-db-ja.com

Qemu / Spiceが他の場所にバインドされているキーを取得するのを防ぎます

私のセットアップ:X11を搭載したLinuxで実行されているlibvirtによって管理されるSpiceディスプレイを備えたQemu。

QemuクライアントがフォーカスされているときにウィンドウマネージャーとXサーバーでキーバインディングを保持する方法を探しています。libvirtまたはQemuオプション、コンパイルフラグ、またはX11マジック-何でも。

具体的な例:キー_Mod4+1_を押すと、WMをタグ1に切り替えます。現在、ゲストは入力として_1_を受け取り、WMは何も受け取りません。

グラフィカルなQemuクライアント(ここでは主にWindowsゲストですが、それは問題ではありません)は、xkbさえもバイパスして無差別にキーボード入力を取得しているようです。これは、これらのクライアントがcapslock(swapescape)などのオプションを無視するという事実から明らかです。

これはウィンドウマネージャーに大混乱をもたらします。例:クライアントを循環するときにQemuクライアントがフォーカスされている場合、マウスを押して問題のあるクライアントのフォーカスを解除するまで、WMのバインディングは役に立たなくなります。言うまでもなく、これはキーボード駆動のワークフローを壊します。腹立たしいです。

また、入力がクライアントに渡されるようになったため、ゲスト内のアプリケーションがそれらの入力をどのように処理するかによって、あらゆる種類の面白いことが起こる可能性があります…

編集:アップストリームは、これを望ましい動作と見なしているようです: 「キーボードフォーカスを取得したら、キーボードをつかみます。任意のキーを押してVMに移動します。キーボードフォーカスができたらすぐに」 –これはまさに私が避けたいと思っていることです。 Spiceクライアントがフォーカスされているかどうかに関係なく、allキーボード入力の資格を与えられるべき理由はありません。

3
phg

リペアレンティングウィンドウマネージャー が必要になります。ウィンドウマネージャーは、キーボードイベントの伝播がソースウィンドウからではなく親ウィンドウから開始されるようにする必要もあります( Xlibデフォルト )。

_LD_PRELOAD_トリックを使用して、XGrabKeyboardXlib関数(またはlibxcbの_xcb_grab_keyboard_)をオーバーライドできます。

例:

_$ cat xgkb.c
#include <X11/Xlib.h>
int XGrabKeyboard(Display *dpy, Window gw, Bool oe, int pm, int km, Time t){
        return 0;
}
$ cc -shared xgkb.c -o xgkb.so
$ LD_PRELOAD=`pwd`/xgkb.so your_program
_

もちろん、何らかのフラグが設定されている場合(ルートウィンドウのプロパティなど)にグラブを成功させることで、ラッパーから同じ引数を使用して実際のXGrabKeyboard()を呼び出すことで、これを改善できます。 (dlopen(3)dlsym(3)、_RTLD_NEXT_を探します)。

完了:

virt-viewerはXIGrabDevice(おそらくgtk経由)を使用してキーボードとポインターの両方を取得しているため、デバイスがキーボードの場合にのみ取得をザッピングする、もう少し複雑なものが必要です。

_$ cat xigd.c
#define _GNU_SOURCE
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
#include <dlfcn.h>

#include <err.h>

Status XIGrabDevice(
        Display*           dpy,
        int                deviceid,
        Window             grab_window,
        Time               time,
        Cursor             cursor,
        int                grab_mode,
        int                paired_device_mode,
        Bool               owner_events,
        XIEventMask        *mask
){
        int n, is_kb;
        static Status (*XIGrabDevice_orig)(Display*, int, Window, Time,
                Cursor, int, int, Bool, XIEventMask*);
        if(!XIGrabDevice_orig)
                XIGrabDevice_orig = dlsym(RTLD_NEXT, "XIGrabDevice");
        XIDeviceInfo *info = XIQueryDevice(dpy, deviceid, &n);
        is_kb = info->num_classes == 1 && info->classes[0]->type == XIKeyClass;
        warnx("trying XIGrabDevice %d %s is_kb=%d %p\n",
                deviceid, info->name, is_kb, XIGrabDevice_orig);
        XIFreeDeviceInfo(info);
        return is_kb ? 0 :
                XIGrabDevice_orig(dpy, deviceid, grab_window,
                        time, cursor, grab_mode, paired_device_mode,
                        owner_events, mask);
}
$ cc -shared -ldl -Wall -W xigd.c -o xigd.so
$ LD_PRELOAD=`pwd`/xigd.so virt-viewer ...
_
1
mosvy