LINUX.ORG.RU

[xorg] Лишние KeyRelease при прослушивании ввода «чужих» окон

 


0

1

Если есть люди, разбирающиеся в коде иксов, помогите выяснить причину такого поведения. Суть в следующем:

Когда клиент слушает KeyPress и KeyRelease для собственных окон, все сообщения приходят как и ожидается. Если же клиент слушает ввод для окна, которое создано другим клиентом, то перед каждым KeyPress он получает фейковый KeyRelease для этой же клавиши.

Например, запускаем xev с собственным окном и запускаем второй xev, который натравливаем на окно первого. Первый получает такие события:

KeyPress event, serial 41, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7167984, (838,-77), root:(953,658),
    state 0x0, keycode 65 (keysym 0x20, space), same_screen YES,
    XLookupString gives 1 bytes: (20) " "
    XmbLookupString gives 1 bytes: (20) " "
    XFilterEvent returns: False

KeyRelease event, serial 41, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7168102, (838,-77), root:(953,658),
    state 0x0, keycode 65 (keysym 0x20, space), same_screen YES,
    XLookupString gives 1 bytes: (20) " "
    XFilterEvent returns: False

KeyPress event, serial 41, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7174348, (838,-77), root:(953,658),
    state 0x0, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 41, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7174453, (838,-77), root:(953,658),
    state 0x1, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 41, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7176414, (838,-77), root:(953,658),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XmbLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False

KeyRelease event, serial 41, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7176494, (838,-77), root:(953,658),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False

А второй в это же время получает такие события:

KeyRelease event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7167984, (838,-77), root:(953,658),
    state 0x0, keycode 65 (keysym 0x20, space), same_screen YES,
    XLookupString gives 1 bytes: (20) " "
    XFilterEvent returns: False

KeyPress event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7167984, (838,-77), root:(953,658),
    state 0x0, keycode 65 (keysym 0x20, space), same_screen YES,
    XLookupString gives 1 bytes: (20) " "
    XmbLookupString gives 1 bytes: (20) " "
    XFilterEvent returns: False

KeyRelease event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7168102, (838,-77), root:(953,658),
    state 0x0, keycode 65 (keysym 0x20, space), same_screen YES,
    XLookupString gives 1 bytes: (20) " "
    XFilterEvent returns: False

KeyRelease event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7174348, (838,-77), root:(953,658),
    state 0x0, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7174348, (838,-77), root:(953,658),
    state 0x0, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7174453, (838,-77), root:(953,658),
    state 0x1, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7176414, (838,-77), root:(953,658),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False

KeyPress event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7176414, (838,-77), root:(953,658),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XmbLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False

KeyRelease event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 7176494, (838,-77), root:(953,658),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False

Поведение воспроизводится не только под xev, но в xneur. Т.е. определенно это не баг клиента, а фича иксов.

При чем, когда я вчера тестировал это поведение, то лишний KeyRelease приходил только для модификаторов. А сейчас, как видно по логу выше, он приходит и для алфавитных клавиш. Очень странно.

Используемая версия иксов: 1.10.3.901.

Копаясь в коде, я дошел до функции DeliverEventsToWindow() в файле dix/events.c, где происходит доставка сообщения сначала для «своего» клиента окна, а затем для всех остальных. Однако изучая, чем доставка для всех остальных отличается от доставки «своему», я не нашел ничего, что пролило бы свет на сабж. Единственное место, в котором подставляются фейковые KeyRelease — это функция TryClientEvents() (в том же файле), однако там KeyRelease вставляется для обработки удержания клавиши при автоповторе и, вроде бы, не имеет отношения к проблеме.

Если такое поведение в иксах было сделано специально, то для чего именно? Или же это баг? Гугл не помог.

Алсо, в тред кастуется svu.

geekless ★★
() автор топика

А как ты натравливаешь? Я вот натравил и какбэ нет такого. Запускаю xev со своим окном. Двигаю мышой над этим окошком с квадратиком. Каждое событие имеет параметр window 0x1400001. Это XID окна xev. Далее запускаю вторую компию xev

xev -id 0x1400001

Теперь жмем кнопки на окне и смотрим, что мониторит (нажимаю q):

KeyPress event, serial 33, synthetic NO, window 0x1400001,
    root 0x52, subw 0x0, time 4163766, (112,93), root:(170,132),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XmbLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False

KeyRelease event, serial 33, synthetic NO, window 0x1400001,
    root 0x52, subw 0x0, time 4163838, (112,93), root:(170,132),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False

KeyPress event, serial 33, synthetic NO, window 0x1400001,
    root 0x52, subw 0x0, time 4166614, (112,93), root:(170,132),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XmbLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False

KeyRelease event, serial 33, synthetic NO, window 0x1400001,
    root 0x52, subw 0x0, time 4166718, (112,93), root:(170,132),
    state 0x0, keycode 24 (keysym 0x71, q), same_screen YES,
    XLookupString gives 1 bytes: (71) "q"
    XFilterEvent returns: False
Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Запускаю xev со своим окном. Двигаю мышой над этим окошком с квадратиком. Каждое событие имеет параметр window 0x1400001. Это XID окна xev. Далее запускаю вторую компию xev

xev -id 0x1400001

Аналогично делаю.

При чем, я вот сейчас запустил xneur, в нём такая же фигня с модификаторами:

[TRA] 19:59:09 Получено KeyRelease 'Control_L' (тип события 3)
[TRA] 19:59:09 Получено KeyPress 'Control_L' (тип события 2)
[TRA] 19:59:09 Получено KeyRelease 'Control_L' (тип события 3)
[TRA] 19:59:11 Получено KeyRelease 'Control_L' (тип события 3)
[TRA] 19:59:11 Получено KeyPress 'Control_L' (тип события 2)
[TRA] 19:59:11 Получено KeyRelease 'Control_L' (тип события 3)

НО при этом не модификаторы не дают лишнего KeyRelease.

Следом на том же окне проверяю в xev. В нём уже KeyRelease есть и у модификаторов, и не у модификаторов. Я в замешательстве.

geekless ★★
() автор топика
Ответ на: комментарий от x3al

> А что в x11vis?

Пока ничего, т.к. пакет с x11vis не собрался из-за кривых зависимостей.

geekless ★★
() автор топика

Заюзал x11trace, чтобы убедиться, что это не xlib сомодеятельностью по генерации фейковых событий занимается. Нет, всё с сервера идёт:

000:>:0011: Event KeyRelease(3) keycode=0x25 time=0x0171659f root=0x00000199 event=0x01e00001 child=None(0x00000000) root-x=244 root-y=479 event-x=186 event-y=385 state=0 same-screen=true(0x01)
000:>:0011: Event KeyPress(2) keycode=0x25 time=0x0171659f root=0x00000199 event=0x01e00001 child=None(0x00000000) root-x=244 root-y=479 event-x=186 event-y=385 state=0 same-screen=true(0x01)

KeyRelease event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 24208799, (186,385), root:(244,479),
    state 0x0, keycode 37 (keysym 0xffe3, Control_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 24208799, (186,385), root:(244,479),
    state 0x0, keycode 37 (keysym 0xffe3, Control_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False
000:>:0011: Event KeyRelease(3) keycode=0x25 time=0x01716603 root=0x00000199 event=0x01e00001 child=None(0x00000000) root-x=244 root-y=479 event-x=186 event-y=385 state=Control same-screen=true(0x01)

KeyRelease event, serial 17, synthetic NO, window 0x1e00001,
    root 0x199, subw 0x0, time 24208899, (186,385), root:(244,479),
    state 0x4, keycode 37 (keysym 0xffe3, Control_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

geekless ★★
() автор топика
Ответ на: комментарий от geekless

Реально ли попробовать другой клавиатурный драйвер (это в порядке бреда). Вместо evdev старый kbd заюзать. Я просто не знаю, какая у тебя клава.

Еще глянь, не шлет ли клавиатура фейковый KeyRelease. Это тоже в порядке бреда, так как не всегда проявляется, но проверить надо. В тектовой консоли 'showkey -s' (через 10 секунд сама выйдет).

Заюзал x11trace, чтобы убедиться, что это не xlib сомодеятельностью по генерации фейковых событий занимается. Нет, всё с сервера идёт:

То, что с сервера - это ясно. Если бы какое-то приложение вгенерячило событие KeyRelease, то оно бы пошло как synthetic YES (то есть если его послали через SendEvent). Все с самого сервера идет.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Однако отмечу, что у меня все нормально работает с тем же набором и по тому же алгоритму проверки (я пробовал и kbd, и evdev). Так что это может быть и багом. Если долгопонимания не будет, то есть смысл в рассылку сходить и спросить.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

> Еще глянь, не шлет ли клавиатура фейковый KeyRelease. Это тоже в порядке бреда, так как не всегда проявляется, но проверить надо. В тектовой консоли 'showkey -s' (через 10 секунд сама выйдет).

Проверил. Сканкоды идут как им и положено.

То, что с сервера - это ясно. Если бы какое-то приложение вгенерячило событие KeyRelease, то оно бы пошло как synthetic YES (то есть если его послали через SendEvent). Все с самого сервера идет.

Не про то. Просто, тоже «в порядке бреда», мне пришла в голову мысль, а не вкорячил ли какой-нибудь сумрачный гений какую-то нетривиальную логику в xcb, приводящую к тому, что клиент у себя видит сообщение, которое на самом деле сервером не посылалось. Но, тут всё нормально.

Обновлюсь-ка я сейчас до тестинга с 1.11-ми иксами и посмотрю, как оно там. Если то же самое, то, видимо, придётся воспользоваться традиционным методом отладки «расставить printf-ы и пересобрать».

geekless ★★
() автор топика
Ответ на: комментарий от geekless

Просто, тоже «в порядке бреда», мне пришла в голову мысль, а не вкорячил ли какой-нибудь сумрачный гений какую-то нетривиальную логику в xcb, приводящую к тому, что клиент у себя видит сообщение, которое на самом деле сервером не посылалось. Но, тут всё нормально.

Теоретически такое, конечно, могло произойти. Но xcb простой как валенок. А вот xlib-xcb мог в принципе прочесть сообщение второй раз. Но тогда странно, что это не проявляется всегда.

Я глянул на всякий случай в код xev. Разница между своим окном и чужим окном только в том. что для своего окна xev разрешает все события, а для чужого закрывает пару масок.

    if (w) {
        XGetWindowAttributes(dpy, w, &wattr);
        if (wattr.all_event_masks & ButtonPressMask)
            attr.event_mask &= ~ButtonPressMask;
        attr.event_mask &= ~SubstructureRedirectMask;
        XSelectInput(dpy, w, attr.event_mask);

Но я не думаю, что это влияет.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

>Теоретически такое, конечно, могло произойти. Но xcb простой как валенок. А вот xlib-xcb мог в принципе прочесть сообщение второй раз.

Не, вряд ли. Сейчас глянул на твои выкладки - фейкового KeyRelease другой timestamp. Следовательно, это совсем другое сообщение, а не то же самое, прочитанное второй раз.

Zubok ★★★★★
()
Ответ на: комментарий от geekless

О, кстати, обрати внимание, у KeyPress и KeyRelease одинаковый timestamp во втором случае. В первом все нормально - события же в разный момент происходят. То есть какое-то странное поведение. Это может быть и xlib, и сервер.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

фейкового KeyRelease другой timestamp

Одинаковый. У пары KeyRelease-KeyPress одинаковый time. У следующего затем настоящего KeyRelease time, разумеется, отличается.

Это может быть и xlib

Таки не может. x11trace, который работает как прокси между сервером и клиентом, видит те же самые события:

000:>:0011: Event KeyRelease(3) keycode=0x25 time=0x0171659f root=0x00000199 event=0x01e00001 child=None(0x00000000) root-x=244 root-y=479 event-x=186 event-y=385 state=0 same-screen=true(0x01)
000:>:0011: Event KeyPress(2) keycode=0x25 time=0x0171659f root=0x00000199 event=0x01e00001 child=None(0x00000000) root-x=244 root-y=479 event-x=186 event-y=385 state=0 same-screen=true(0x01)
geekless ★★
() автор топика
Ответ на: комментарий от Zubok

Вот кстати если задать клавишу и посмотреть на события от автоповтора, то получается как раз так же — идущие подряд KeyRelease-KeyPress с одинаковым time. Так что пока я склонен подозревать глюк в алгоритме, который генерирует эти пары. Во всяком случае, другого места в коде, где «вручную» формируется и ставится в очередь KeyRelease, я не нашел.

geekless ★★
() автор топика
Ответ на: комментарий от geekless

>Одинаковый. У пары KeyRelease-KeyPress одинаковый time. У следующего затем настоящего KeyRelease time, разумеется, отличается.

Ну да, я потом это заметил и допольнил. То есть получается, что в каком-то месте по ошибке (возможно) одна и та же структура посылается как два разных события: KeyPress и KeyRelease. Может быть, где-то какой-то if не сработал или break; забыли. :)

Zubok ★★★★★
()
Ответ на: комментарий от geekless

>Вот кстати если задать клавишу и посмотреть на события от автоповтора, то получается как раз так же — идущие подряд KeyRelease-KeyPress с одинаковым time.

Тогда интересно, почему xev со своим окном не реагирует на эти события.

Zubok ★★★★★
()
Ответ на: комментарий от geekless

>Вот кстати если задать клавишу и посмотреть на события от автоповтора, то получается как раз так же — идущие подряд KeyRelease-KeyPress с одинаковым time.

А вот ты попробуй автоповтор отключить и глянь, идет ли нажатие клавиши с парой. Автоповтор на том и основан, что генерируются KeyRelease.

Zubok ★★★★★
()
Ответ на: комментарий от geekless

Ну, еще допишу наблюдение. Я xev со своим окном запустил и тоже зажал клавишу. Пошел автоповтор и тоже события с одинаковым timestamp. То есть эти фейковые события у тебя откуда-то оттуда лезут.

Вот у меня (убрал лишнее)

KeyRelease event, serial 33, synthetic NO, window 0x1200001,
    root 0x52, subw 0x0, time 5229152, (87,10), root:(271,211),

KeyPress event, serial 33, synthetic NO, window 0x1200001,
    root 0x52, subw 0x0, time 5229152, (87,10), root:(271,211),

KeyRelease event, serial 33, synthetic NO, window 0x1200001,
    root 0x52, subw 0x0, time 5229188, (87,10), root:(271,211),

KeyPress event, serial 33, synthetic NO, window 0x1200001,
    root 0x52, subw 0x0, time 5229188, (87,10), root:(271,211),

То есть получается, что при одиночном нажатии, у тебя какая-то часть алгоритма автоповтора срабатывает.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Отключение автоповтора не влияет на баг.

Но вот что интересно. Если зажать клавишу, то оба xev-а получают одинаковые пары KeyRelease-KeyPress. Отсюда интересный вывод: если бы лишний KeyRelease возникал где-то в другом месте, то последовательность бы выглядела так: KeyRelease-KeyRelease-KeyPress — т.е. один KeyRelease от автоповтора и еще один от бага.

geekless ★★
() автор топика
Ответ на: комментарий от Zubok

Всё, нашел я баг.

Они в качестве признака повтора используют поле sequenceNumber не по назначению. Видимо, запихать больше некуда было:

/**
 * Hack to allow detectable autorepeat for core and XI1 events.
 * The sequence number is unused until we send to the client and can be
 * misused to store data. More or less, anyway.
 *
 * Do not use this. It may change any time without warning, eat your babies
 * and piss on your cat.
 */
static void
EventSetKeyRepeatFlag(xEvent *event, BOOL on)
{
    event->u.u.sequenceNumber = on;
}

/**
 * Check if the event was marked as a repeat event before.
 * NOTE: This is a nasty hack and should NOT be used by anyone else but
 * TryClientEvents.
 */
BOOL
EventIsKeyRepeat(xEvent *event)
{
    return !!event->u.u.sequenceNumber;
}

Алгоритм в DeliverEventsToWindow() сначала отсылает сообщение «родному» клиенту. При этом в функции WriteEventsToClient() значение sequenceNumber затирается:

    for (i = 0; i < count; i++)
	if ((events[i].u.u.type & 0x7f) != KeymapNotify)
	    events[i].u.u.sequenceNumber = pClient->sequence;

Ну а потом происходит отсылка всем остальным клиентам, и EventIsKeyRepeat(), понятное дело, всегда возвращает 1 при проверке повтора.

geekless ★★
() автор топика
Ответ на: комментарий от geekless

Похоже. Но это проявляется именно тогда в случаях, когда событие слушают больше двух клиентов. При обычном использовании в одно рыло это как бы не должно проявляться, я так понимаю.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

s/больше двух клиентов/больше одного клиента/

geekless ★★
() автор топика
Ответ на: комментарий от geekless

Я бы баг зафайлил, а то забудут. Этим местом чаще всего занимаются Peter Hutterer и Daniel Stone. Вероятно, баг уже заводили. Ну это это так... чтобы напомнить. :)

Zubok ★★★★★
()
Ответ на: комментарий от geekless

>Собственно, а какая у вас версия иксов?

Я даже боюсь сказать. :) До 2009 года, коим датируется этот исходник. Я еще до сих пор на Debian Lenny, а тут 1.4.2 :)

Zubok ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.