1

My setup: Qemu with Spice display managed by libvirt running on Linux with X11.

I’m looking for a way to preserve key bindings in the window manager and the X server when a Qemu client is focused. A libvirt or Qemu option, a compile flag, or some X11 magic – anything.

A concrete example: when I press the keys Mod4+1 I want the WM to switch to tag 1. Currently, the guest receives the 1 as an input and the WM receives nothing.

Graphical Qemu clients (mostly Windows guests here but that shouldn’t matter) seem to grab keyboard inputs indiscriminately bypassing even xkb. This is obvious from the fact that these clients ignore options like capslock(swapescape).

This wreaks havoc on the window manager. E. g. when a Qemu client is focused when I cycle through clients, the WM’s bindings are rendered useless until I unfocus the offending client by shoving the mouse around. Needless to say, this breaks any keyboard-driven workflow. It’s infuriating.

Also, since inputs are now passed on to the client, all kinds of funny stuff can happen depending on what the application inside the guest chooses to do with those inputs …

Edit: Upstream seemingly considers this the desired behavior: “we grab the keyboard when we get the keyboard focus, as we want any key presses to go to the vm as soon as we have keyboard focus” – which is exactly what I intend to avoid. There is no reason as to why Spice clients should be entitled to all keyboard inputs regardless of whether they are focused.

  • Is changing the keyboard shortcuts for either the host or guest an option? – dsstorefile1 Aug 21 '18 at 10:48
  • “Is changing the keyboard shortcuts for either the host or guest an option?” – Changing the keybindings of the WM is out of the question. The guest key bindings are irrelevant. The goal is to prevent inputs from reaching the guest in the first place. – phg Aug 21 '18 at 10:51
2
+100

You will need a re-parenting window manager. Your window manager would also need to ensure that the propagation of keyboard events starts from the parent window instead of from the source window (Xlib default).

  • “You will need a re-parenting window manager.” – I run awesomewm which is indeed a reparenting WM. The thing is, I haven’t observed this behavior on any client except the Spice ones. Even the same Qemu guests with VNC instead of Spice don’t act this way. Though VNC brings in another set of issues of its own which is why I’d prefer Spice. – phg Aug 24 '18 at 8:24
  • It has been ages since I last used awesomewm, but I remember distinctly that, back then, it was not reparenting. That has probably changed now. Anyway, Xlib's default is to give control of the event queue to the source window. Since your application (Spice) greedly grabs all keyboard events to itself, your WM has to make sure to hijack the event queue and only pass to clients those keyboard events it doesn't handle. Whether awesomewm currently does this, I do not know. – Hermógenes Oliveira Aug 25 '18 at 13:17
  • I briefly browsed the source code of awesomewm and it seems it has indeed been a reparenting WM for a while. The issue with the keyboard events queue, however, seems to be unsettled. – Hermógenes Oliveira Aug 25 '18 at 14:18
  • Thanks for digging that out. I’m still at a loss as to why Spice is the odd one out. No other X clients behave that way, at least none that I know of. Not sure what other options there are for intercepting inputs at the WM level. I’d rather see Spice turned into a Good Citizen™. – phg Aug 27 '18 at 7:54
  • You got the bounty for researching the WM implementation; could you update your post with what you added in the comments? – phg Aug 31 '18 at 7:38
1

You could use the LD_PRELOAD trick to override the XGrabKeyboard function from Xlib (or xcb_grab_keyboard from libxcb).

Example:

$ 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

Notice that LD_PRELOAD needs absolute paths, LD_PRELOAD=./foo.so won't do.

Of course, you can refine that by letting the grab succeed if some flag is set (eg. some property on the root window), by calling the real XGrabKeyboard() with the same arguments from the wrapper. (look for dlopen(3), dlsym(3), RTLD_NEXT).

Completion:

virt-viewer is using XIGrabDevice (probably via gtk) to grab both the keyboard and the pointer, so something a bit more involved is needed, which will only zapp the grab if the device is a keyboard:

$ 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 ...
  • No difference, but that’s probably due to how the display is invoked: LD_PRELOAD=`pwd`/xgkb.so virt-viewer --connect qemu+ssh://yourstruly@otherbox.lan/system some-guest-with-gui. I’m not sure what libvirt does to acquire the guest display on the remote box. – phg Aug 31 '18 at 7:36
  • That's because virt-viewer is not using the XGrabKeyboard, but something fancier (XIGrabDevice) -- probably via gtk. – mosvy Aug 31 '18 at 9:42
  • So, does the second hack (with xinput2, XIGrabDevice) work? I don't care about the missed bounty, I'm just curious if there are other ways an X11 client could steal the keyboard other than X_GrabKeyboard, xinput2.X_XIGrabDevice and xinput.X_GrabDevice (and, of course X_GrabServer). – mosvy Sep 1 '18 at 6:19
  • I’ll have to test the second hack. – phg Sep 5 '18 at 13:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.