我的设置:带有由 libvirt 管理的 Spice 显示的 Qemu,在 Linux 上运行 X11。
我正在寻找一种方法来在 Qemu 客户端聚焦时保留窗口管理器和 X 服务器中的键绑定。libvirt 或 Qemu 选项、编译标志或一些 X11 魔法 – 任何东西。
一个具体的例子:当我按下按键时Mod4+1
我希望WM切换到标签1。目前,来宾接收1
作为输入,而 WM 接收不到任何内容。
图形 Qemu 客户端(这里主要是 Windows 客户端,但这并不重要)似乎会不加区别地获取键盘输入,甚至绕过 xkb。从这些客户端忽略诸如capslock(swapescape)
.
这会对窗口管理器造成严重破坏。例如当我循环浏览客户端时 Qemu 客户端聚焦时,WM 的绑定变得毫无用处直到我通过移动鼠标来分散有问题的客户端的焦点。不用说,这会破坏任何键盘驱动的工作流程。真令人气愤。
此外,由于输入现在传递给客户端,因此可能会发生各种有趣的事情,具体取决于来宾内部的应用程序选择如何处理这些输入......
编辑:上游似乎认为这是期望的行为:“当我们获得键盘焦点时,我们会抓住键盘,正如我们想要的那样任何一旦我们获得键盘焦点,就按下按键即可进入虚拟机” ——这正是我想要避免的。没有理由说明为什么 Spice 客户应该有权全部键盘输入,无论它们是否获得焦点。
答案1
您将需要一个重新设置窗口管理器的父子关系。您的窗口管理器还需要确保键盘事件的传播从父窗口而不是从源窗口开始(Xlib 默认值)。
答案2
您可以使用该LD_PRELOAD
技巧来覆盖XGrabKeyboard
from Xlib
(或xcb_grab_keyboard
from libxcb
) 的函数。
例子:
$ 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 ...