Как заставить все события с одного устройства обрабатываться одним окном, в то же время позволяя нормально обрабатывать все остальные события со всех других устройств? - PullRequest
0 голосов
/ 23 октября 2018

У меня есть приложение, которое используется в качестве системы управления для презентации под Linux и с использованием X11.У меня есть USB-пульт дистанционного управления, который действует как очень миниатюрная клавиатура (четыре кнопки: Page Up, Page Down и две другие), которые можно использовать для перемещения вперед и назад в презентации.Мне бы хотелось, чтобы мое презентационное приложение получало все события с этого пульта независимо от того, где находится фокус мыши.Но я также хотел бы иметь возможность получать обычные события мыши и клавиатуры, если текущее окно фокусируется на приложении презентации.Используя XIGrabDevice(), я мог получать все события от пульта в приложении презентации независимо от текущего фокуса, но я не мог получать никаких событий от мыши или клавиатуры, когда захват был активен.

1 Ответ

0 голосов
/ 03 мая 2019

Я закончил тем, что настроил отдельную программу для захвата ключей пульта, а затем передал эти ключи своей основной программе.Я сделал это так, потому что оригинальная программа использовала старое расширение XInput, и мне нужно было использовать более новое расширение XInput2, и они не существуют вместе.Вот некоторый код C ++ (он не проверяет ошибки, но это должно быть сделано в реальной программе):

// Open connection to X Server
Display *dpy = XOpenDisplay(NULL);

// Get opcode for XInput Extension; we'll need it for processing events
int xi_opcode = -1, event, error;
XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error);

// Allow user to select a device
int num_devices;
XIDeviceInfo *info = XIQueryDevice(dpy, XIAllDevices, &num_devices);
for (int i = 0; i < num_devices; ++i)
{
    XIDeviceInfo *dev = &info[i];
    std::cout << dev->deviceid << " " << dev->name << "\n";
}
XIFreeDeviceInfo(info);

std::cout << "Enter the device number: ";
std::string input;
std::cin >> input;
int deviceid = -1;
std::istringstream istr(input);
istr >> deviceid;

// Create an InputOnly window that is just used to grab events from this device
XSetWindowAttributes attrs;
long attrmask = 0;
memset(&attrs, 0, sizeof(attrs));
attrs.override_redirect = True;   // Required to grab device
attrmask |= CWOverrideRedirect;

Window win = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, attrmask, &attrs);

// Make window without decorations
PropMotifWmHints hints;
hints.flags = 2;
hints.decorations = 0;
Atom property = XInternAtom(dpy, "_MOTIF_WM_HINTS", True);
XChangeProperty(dpy, win, property, property, 32, PropModeReplace, (unsigned char *)&hints, PROP_MOTIF_WM_HINTS_ELEMENTS);

// We are interested in key presses and hierarchy changes. We also need to get key releases or else we get an infinite stream of key presses.
XIEventMask evmasks[1];
unsigned char mask0[XIMaskLen(XI_LASTEVENT)];
memset(mask0, 0, sizeof(mask0));
XISetMask(mask0, XI_KeyPress);
XISetMask(mask0, XI_KeyRelease);
XISetMask(mask0, XI_HierarchyChanged);
evmasks[0].deviceid = XIAllDevices;
evmasks[0].mask_len = sizeof(mask0);
evmasks[0].mask = mask0;
XISelectEvents(dpy, win, evmasks, 1);

XMapWindow(dpy, win);
XFlush(dpy);

XEvent ev;
bool grab_success = false, grab_changed;
while (1)
{
    grab_changed = false; 
    if (!grab_success)
    {
        XIEventMask masks[1];
        unsigned char mask0[XIMaskLen(XI_LASTEVENT)];
        memset(mask0, 0, sizeof(mask0));
        XISetMask(mask0, XI_KeyPress);
        masks[0].deviceid = deviceid;
        masks[0].mask_len = sizeof(mask0);
        masks[0].mask = mask0;
        XIGrabDevice(dpy, deviceid, win, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, XIOwnerEvents, masks);
    }
    XNextEvent(dpy, &ev);
    XGenericEventCookie *cookie = &ev.xcookie;
    if (cookie->type == GenericEvent && cookie->extension == xi_opcode && XGetEventData(dpy, cookie))
    {
        if (cookie->evtype == XI_KeyPress)
        {
            XIDeviceEvent *de = (XIDeviceEvent*)cookie->data;
            std::cout << "found XI_KeyPress event: keycode " << de->detail << "\n";
        }
        else if (cookie->evtype == XI_HierarchyChanged)
        {
            // Perhaps a device was unplugged. The client is expected to re-read the list of devices to find out what changed.
            std::cout << "found XI_HierarchyChanged event.\n";
            grab_changed = true;
        }
        XFreeEventData(dpy, cookie);
    }

    if (grab_changed)
    {
        XIUngrabDevice(dpy, deviceid, CurrentTime);
        grab_success = false;
        break;
    }
}

Мне показались полезными следующие ссылки:

Peter Hutterer's 6блог на XInput2: 1 2 3 4 5 6

Эта запись в блоге была полезна для определения, к какому классу приводить указатель cookie-> data, в зависимости от cookie-> evtype: 7

...