XGrabButton не захватывает клики, когда нажата другая кнопка - PullRequest
2 голосов
/ 02 апреля 2012

У меня есть код, который использует XGrabButton для захвата щелчков мыши. Я хочу, чтобы он всегда фиксировал все нажатия на указанные кнопки, независимо от других проблем. В настоящее время он использует следующий вызов:

XSelectInput(display, window, ButtonPressMask);
XGrabButton(display, Button2, AnyModifier, window, True,
    ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(display, Button3, AnyModifier, window, True,
    ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(display, Button4, AnyModifier, window, True,
    ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);

Однако нажатие и удержание кнопки 1 (левая кнопка мыши, которая не фиксируется моим кодом) приводит к тому, что нажатия других кнопок не фиксируются. Как я могу предотвратить это?

Изменить для уточнения:

  • Я хочу захватывать кнопки 2-4 всегда, а кнопку 1 никогда.
  • Вышеуказанный вызов нормально фиксирует кнопки 2-4.
  • Это НЕ кнопка захвата 1 (щелчок левой кнопкой мыши).
  • Будет НЕ захватывать кнопки 2-4, пока нажата кнопка 1.

Как мне сделать так, чтобы кнопки 2-4 удерживались, пока кнопка 1 удерживается нажатой?

Ответы [ 2 ]

0 голосов
/ 20 апреля 2012

Надеюсь, в коде есть что-то еще, если он действует так, как вы говорите:

" Однако нажатие и удержание кнопки 1 (левая кнопка мыши, , которая не фиксируется моим кодом ) приводит к тому, что нажатия на другие кнопки не фиксируются. "

Строка:

XSelectInput(display, window, ButtonPressMask);

приведет к захвату всех кнопок. Дополнительные вызовы XGrabButton ( избыточны? ), так как вы не указываете никаких параметров, которые отличаются от параметров по умолчанию.

Тем не менее, если на самом деле это то, как вы говорите, что даже с этим кодом Button1 не захвачен, или у вас есть какой-то другой код, где, например, XSelectInput не вызывается - для начала я приму это как основу.


Распределение событий

В каждом окне есть очередь событий. Событие отправляется окну, когда оно генерируется в этом окне. Но только окно получает событие, если оно выбрало его или оно всегда выбрано. Или как побочный эффект для процедуры Xlib . Только когда получает событие, оно помещается в очередь.

Если мы используем XSelectInput() и выбираем большинство масок, / usr / include / X11 / Xh ОПРЕДЕЛЕНИЯ СОБЫТИЙ и используем XNextEvent(), чтобы посмотреть, как правило, мы увидим что-то вроде этого:

 ...
 19: Event EnterNotify     # Window Entry/Exit Events
 20: Event KeymapNotify    # X send after every EnterNotify and FocusIn event
 21: Event MotionNotify    # Keyboard and Pointer Events
 22: Event ButtonPress   2 # Button 2 down
 23: Event ButtonRelease 2 # Button 2 up
 24: Event ButtonPress   1 # Button 1 down
 25: Event ButtonPress   2 # Button 1 and 2 down
 ...

Если , мы удаляем ButtonPressMask и ButtonReleaseMask из XSelectInput и используем XGrabButton() для захвата событий для каждой кнопки, кроме 1 и снова запустить программу, события отображают тенденцию:

 22: Event LeaveNotify       # Button 1 pressed causes "blurring" of window.
 23: Event EnterNotify       # Button 1 released
 24: Event KeymapNotify
 25: Event ButtonPress   3   # Button 3 down
 26: Event ButtonPress   1   # Button 3 + 1 down; 1 registers as Press 
                             #                    even if not tracked.
 27: Event ButtonRelease 3   # Button 3 up
 28: Event MotionNotify
 29: Event ButtonRelease 1   # Button 1 up
 30: Event LeaveNotify       # Button 1 pressed, 1+2 pressed, 1+2+3 …
 31: Event EnterNotify       # Button 1 released, 2 released, …
 32: Event KeymapNotify
 33: Event ButtonPress   2
 34: Event ButtonPress   3
 35: Event ButtonPress   1
 36: Event ButtonRelease 2
 37: Event ButtonRelease 3

Мы видим, что события кнопки, при нажатии нескольких кнопок, наследуют статус первой. Вы также можете увидеть это с помощью ltrace, strace, xev и т. Д. Т.е. xev регистрирует кнопки, которые не захвачены внутри. Как упоминалось ранее; события отправляются в окно, но только зарегистрированные получены . Это за исключением некоторых побочных эффектов, которые, например, можно увидеть, когда:

  • первая кнопка нажата установлена ​​ для захвата; а затем тот, который не - тот, который не будет, также будет поставлен в очередь в списке событий.
  • первая нажатая кнопка не установлена ​​для захвата; а затем тот, который равен - тогда ни один не ставится в очередь.

Нажатие кнопки на том, который не настроен для захвата результатов в LeaveNotify , (окно оставлено ) - и параллельные события блокируются.


Как решить

Здесь так много вещей, которые нужно учитывать. Все зависит от логики остальной части кода проекта и т. Д. Однако есть некоторые основы. Самое простое - захватить все кнопки, пометить и отследить; XNextEvent .

static const char *event_names[] = {
    "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
    "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
    "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
    "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
    "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
    "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
    "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
    "ClientMessage", "MappingNotify"
};

/* ... */

Display *dpy;
XEvent  ev;
int k = 0;

/* ... */

for (;;) {
    XNextEvent(dpy, &ev);
    if ((ev.type == ButtonPress || ev.type == ButtonRelease) &&
                            ev.xbutton.button == Button1) {
        fprintf(stderr,
                "%04d: BLOCK!     %-18s %d\n",
                k++, event_names[ev.type],
                ev.xbutton.button);
        continue;
    }
    fprintf(stderr, "%04d: PASS! Work %-18s", k++, event_names[ev.type]);
    switch (ev.type) {
        case ButtonPress:
        case ButtonRelease:
            fprintf(stderr, " %d", ev.xbutton.button);
            break;
    }
    putchar('\n');
}

В результате чего-то вроде:

0005: PASS! Work FocusIn           
0006: BLOCK!     ButtonPress        1
0007: BLOCK!     ButtonRelease      1
0008: BLOCK!     ButtonPress        1
0009: BLOCK!     ButtonRelease      1
0010: BLOCK!     ButtonPress        1
0011: PASS! Work ButtonPress        3 # Button 1 is also down
0012: PASS! Work ButtonRelease      3
0013: BLOCK!     ButtonRelease      1
0014: PASS! Work ButtonPress        3
0015: PASS! Work ButtonRelease      3
0016: BLOCK!     ButtonPress        1
0017: PASS! Work ButtonPress        3
0018: PASS! Work ButtonRelease      3
0019: BLOCK!     ButtonRelease      1
0020: PASS! Work ButtonPress        2
0021: PASS! Work ButtonPress        3
0022: BLOCK!     ButtonPress        1
0023: BLOCK!     ButtonRelease      1
0024: PASS! Work ButtonRelease      2
0025: PASS! Work ButtonRelease      3
0026: BLOCK!     ButtonPress        1
0027: PASS! Work ButtonPress        3
0028: PASS! Work ButtonRelease      3
0029: PASS! Work ButtonPress        2
0030: PASS! Work ButtonRelease      2
0031: BLOCK!     ButtonRelease      1
0032: PASS! Work FocusOut          

Другим способом может быть использование XIfEvent , XCheckIfEvent() и т. Д. Все зависит от структуры. Одна вещь, которую нужно запомнить / проверить, это то, что некоторые функции; то есть XIfEvent делает не удалить событие из очереди, если оно не соответствует! Поэтому, если вы реализуете функцию predicate, которая возвращает False, если событие то есть Button1Press - это событие останется в очереди.

0 голосов
/ 18 апреля 2012

Не уверен, что это работает, но вы можете заставить не захватывать button1 в вашем коде:

XUngrabButton(display, Button1, AnyModifier, window);
...