Как прочитать событие щелчка мыши с X-сервера

Я хочу регистрировать свои позиции щелчка мыши. Я пробовал это;

#include <stdio.h>
#include <stddef.h>
#include <X11/Xlib.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>


int working = 1;

void signal_callback_handler(int signum) {
    working = 0;
}

int main () {
    signal(SIGINT, signal_callback_handler);
    signal(SIGTSTP, signal_callback_handler);
    signal(SIGTERM, signal_callback_handler);

    Display *d = XOpenDisplay(NULL);
    assert(d);

    XSelectInput(d, DefaultRootWindow(d), ButtonPressMask);
    while(working) {
        XEvent e;
        XNextEvent(d,&e);
        if (e.type == ButtonPress) {
            printf("%dx%d",e.xbutton.x,e.xbutton.y);
        }
    }


    return 0;
}

Но я вижу эту ошибку:

X Error of failed request: BadAccess (attempt to access private resource denied)
Major opcode of failed request: 2 (X_ChangeWindowAttributes)
Serial number of failed request: 7
Current serial number in output stream: 7

Что не так с моим кодом и как это исправить?

Обновить

Я исследовал это немного больше и получил некоторую помощь от людей из #xorg-dev. Кажется, что это невозможно сделать с обычным Xlib, потому что только один клиент может зарегистрироваться для нажатия кнопки в окне. В этом случае мой WM уже зарегистрирован, поэтому я получаю плохой доступ. Кажется, это можно сделать, используя расширения ввода X и прослушивая событие XI_RawButtonPress, что я все еще пытаюсь понять, как это сделать. Вот что у меня есть до сих пор;

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
#include <signal.h>
#include <assert.h>

int working = 1;

void signal_callback_handler(int signum) {
    working = 0;
}

int main() {


    signal(SIGINT, signal_callback_handler);
    signal(SIGTSTP, signal_callback_handler);
    signal(SIGTERM, signal_callback_handler);

    /* Connect to the X server */
    Display *dpy = XOpenDisplay(NULL);
    assert(dpy);

    /* XInput Extension available? */
    int opcode, event, error;
    if (!XQueryExtension(dpy, "XInputExtension", &opcode, &event, &error)) {
        printf("X Input extension not available.\n");
        return -1;
    }

    /* Which version of XI2? We support 2.0 */
    int major = 2, minor = 0;
    if (XIQueryVersion(dpy, &major, &minor) == BadRequest) {
        printf("XI2 not available. Server supports %d.%d\n", major, minor);
        return -1;
    }


    XIEventMask eventmask;

    unsigned char mask[1] = { 0 }; /* the actual mask */

    eventmask.deviceid = 2;
    eventmask.mask_len = sizeof(mask); /* always in bytes */
    eventmask.mask = mask;

    /* now set the mask */
    XISetMask(mask, XI_RawButtonPress);

    /* select on the window */
    XISelectEvents(dpy, DefaultRootWindow(dpy), &eventmask, 1);

    while(working) {
        XEvent ev;
        XNextEvent(dpy, &ev);
        if (ev.xcookie.type == GenericEvent &&
                ev.xcookie.extension == opcode &&
                XGetEventData(dpy, &ev.xcookie))
        {
            switch(ev.xcookie.evtype)
            {
                case XI_RawButtonPress:
                    printf("RawButtonPress");
                    break;
            }
        }
        XFreeEventData(dpy, &ev.xcookie);
    }
}

Однако я получаю эту ошибку;

X Error of failed request:  XI_BadDevice (invalid Device parameter)
  Major opcode of failed request:  131 (XInputExtension)
  Minor opcode of failed request:  46 ()
  Device id in failed request: 0xad
  Serial number of failed request:  15
  Current serial number in output stream:  15

Обновление 2

Я пытался сделать это с помощью ButtonRelaseEvent, но не получаю никакого события. XNextEvent блокируется навсегда, независимо от того, где я нажимаю/отпускаю кнопку. Вот коды;

#include <stdio.h>
#include <stddef.h>
#include <X11/Xlib.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>


int working = 1;

void signal_callback_handler(int signum) {
    working = 0;
}

int main () {
    signal(SIGINT, signal_callback_handler);
    signal(SIGTSTP, signal_callback_handler);
    signal(SIGTERM, signal_callback_handler);

    Display *d = XOpenDisplay(NULL);
    assert(d);

    XSelectInput(d,DefaultRootWindow(d), ButtonReleaseMask);

    while(working) {
        XEvent e;
        XNextEvent(d, &e);
    printf("Something Occured");
        if (e.type == ButtonRelease) {
            printf("%dx%d",e.xbutton.x,e.xbutton.y);
        }
    }


    return 0;
}

person yasar    schedule 28.01.2013    source источник


Ответы (2)


Попробуйте XWindowEvent вместо XNextEvent.

Например, чтобы схватить мышь, вы можете сделать это:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>

int main(){
    Display* display;
    int screen_num;
    Screen *screen;
    Window root_win;
    XEvent report;
    XButtonEvent *xb = (XButtonEvent *)&report;
    int i;
    Cursor cursor;
    display = XOpenDisplay(0);
    if (display == NULL){
        perror("Cannot connect to X server");
        exit (-1);
    }
    screen_num = DefaultScreen(display);
    screen = XScreenOfDisplay(display, screen_num);
    root_win = RootWindow(display, XScreenNumberOfScreen(screen));
    cursor = XCreateFontCursor(display, XC_crosshair);
    i = XGrabPointer(display, root_win, False,
                ButtonReleaseMask | ButtonPressMask|Button1MotionMask, GrabModeSync,
                GrabModeAsync, root_win, cursor, CurrentTime);
    if(i != GrabSuccess){
        perror("Can't grab the mouse");
        exit(-1);
    }
    for(i = 0; i < 10; i++){
        XAllowEvents(display, SyncPointer, CurrentTime);
        XWindowEvent(display, root_win, ButtonPressMask | ButtonReleaseMask, &report);
        switch(report.type){
            case ButtonPress:
                printf("Press @ (%d, %d)\n", xb->x_root, xb->y_root);
            break;
            case ButtonRelease:
                printf("Release @ (%d, %d)\n", xb->x_root, xb->y_root);
            break;
        }
    }
    XFlush(display);
    XUngrabServer(display);
    XCloseDisplay( display );
    return 0;
}
person Eddy_Em    schedule 28.01.2013
comment
Можете ли вы привести упрощенный пример? Я начал изучать Xlib 1 час назад и не могу уследить за всем, что происходит в этом примере. - person yasar; 28.01.2013
comment
Разве мы не можем сделать это, не хватая мышь? Я хочу, чтобы эта программа работала в фоновом режиме, пока я занимаюсь своими обычными делами. - person yasar; 28.01.2013
comment
Возможно, но я пробовал разные варианты, и все они потерпели неудачу. - person Eddy_Em; 28.01.2013

Да, из спецификации протокола x11:

Несколько клиентов могут выбирать ввод в одном и том же окне; их маски событий не пересекаются. Когда событие сгенерировано, о нем будет сообщено всем заинтересованным клиентам. Однако одновременно только один клиент может выбрать SubstructureRedirect, только один клиент может выбрать ResizeRedirect и только один клиент может выбрать ButtonPress. Попытка нарушить эти ограничения приводит к ошибке доступа.

Однако нескольким клиентам разрешено выбирать событие ButtonRelease — я только что проверил с двумя клиентами, и оба получают события.

person Andrey Sidorov    schedule 29.01.2013
comment
Только что попробовал ваш пример - он действительно получает события ButtonRelease, но они буферизуются. Попробуйте выбрать ButtonReleaseMask|PointerMotionMask и некоторое время двигать мышью. Обратите внимание, что на большинстве настольных компьютеров / WM корневое окно обычно скрыто, но некоторые другие полноэкранные окна. Используйте Xephyr или Xnest для проверки - person Andrey Sidorov; 30.01.2013