私のセットアップ: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キーボード入力の資格を与えられるべき理由はありません。
リペアレンティングウィンドウマネージャー が必要になります。ウィンドウマネージャーは、キーボードイベントの伝播がソースウィンドウからではなく親ウィンドウから開始されるようにする必要もあります( Xlibデフォルト )。
_LD_PRELOAD
_トリックを使用して、XGrabKeyboard
のXlib
関数(または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 ...
_