LINUX.ORG.RU

Xlib, как правильно послать клик мышкой в окно

 , ,


0

1

Доброго времени суток,

есть id окна, есть координаты, нужно в это окно послать клик мышкой по данным координатам.

Создаю своё окно, указываю ему в параметрах

event_mask=ButtonPressMask

А при вызове createWindow указываю маску CWEventMask.

Далее отслеживаю события в своём окне, при событии ButtonPress посылаю сигнал клика в другое окно:

case ButtonPress: xEvent.xbutton.window = targetWindowId;

xEvent.type = ButtonPress; XSendEvent (d, targetWindowId, False, ButtonPressMask, &xEvent);

nanosleep(&clickDelay, NULL);

xEvent.type = ButtonRelease; XSendEvent (d, targetWindowId, False, ButtonReleaseMask, &xEvent);

Проблемы: - Разным окнам задержка в clickDelay нужна разная, кому то меньше кому то больше. - Для некоторых окон пришлось использовать XWarpPointer, ждать и только потом клик слать. - Но некоторым окнам и этого было мало, пришлось MotionEvent посылать. - И всё равно остались окна, которым по барабану. - Так же есть окна, например netbeans, в которых элементы меню работают хорошо, а поле редактора либо вообще не принимает клик, либо в одном и том же месте.

Вопросы: - Как правильно послать клик мышкой окну с известным id? - Если провести по окну приложения, которое не в фокусе курсором, то там где курсор попадает на элементы меню, эти элементы реагируют, а если я в своём окне отлавливаю MotionNotify, и пересылаю его другому окну, то они не подсвечаиваются, хотя xev показывает что все события отлично доходят, почему так?



Последнее исправление: awpe (всего исправлений: 1)

Один из вариантов - какое-то приложение может отслеживать Motion и запоминать координаты курсора, а при событии Button смотреть лишь на номер кнопки, а коордирдинаты использовать запомненные. Как следствие, нужно перед Button послать Motion, иначе будут глюки.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от Zubok

Спасибо за ссылку, открыл исходники xdotool, у меня почти тоже самое было написано, даже названия переменных часто один в один :D

Ошибка оказалась очень глупой, я ловил событие ButtonPress, и по нему двигал курсор и делал клик, это не работало, т.к. видимо иксы ждали, пока я мышку в своём окне отпущу :D

Самый первый свой код перевёл на событие ButtonRelease, и всё работает как часы, даже время между передвижением курсора и кликом поставил на 0, всё работает.

Единственная вещь, которая напрягает - надо сначала передвинуть курсор потом кликать, просто послать клик не прокатывает, как будто бы XSendEvent не берёт координаты из структуры ивента, а сам смотрит где сейчас мышь... И если окно не отрисовано, то клика не происходит, т.е. если оно свёрнуто то ясно понятно, но если его перкрыть частично, то в перекрытую часть клики уже не проходят :(

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

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

Вот, например, поясняющий текст из man xdotool:

SENDEVENT NOTES
       If you are trying to send key input to a specific window, and it does
       not appear to be working, then it's likely your application is ignoring
       the events xdotool is generating. This is fairly common.

       Sending keystrokes to a specific window uses a different API than
       simply typing to the active window. If you specify 'xdotool type
       --window 12345 hello' xdotool will generate key events and send them
       directly to window 12345.  However, X11 servers will set a special flag
       on all events generated in this way (see XEvent.xany.send_event in
       X11's manual). Many programs observe this flag and reject these events.

       It is important to note that for key and mouse events, we only use
       XSendEvent when a specific window is targeted. Otherwise, we use XTEST.

       Some programs can be configured to accept events even if they are
       generated by xdotool. Seek the documentation of your application for
       help.

       Specific application notes (from the author's testing): * Firefox 3
       seems to ignore all input when it does not have focus.  * xterm can be
       configured while running with ctrl+leftclick, 'Allow SendEvents' *
       gnome-terminal appears to accept generated input by default.

То есть это недостаток XSendEvent. Этот метод (как и твой) используется xdotool в случае, если явно указывается xid окна в коммандной строке. Но, как видишь. это может не заработать в некоторых приложениях, потому что они фильтруют по флагу. UPD в этом месте: но с событиями мыши дело может обстоять лучше. С событиями клавиатуры хуже.

А XTEST обладает тем недостатком, что нельзя послать событие конкретному окну. Чтобы конкертному, надо его отыскать, где оно находится, поднять его (передать фокус), отыскать, куда именно клацнуть.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 2)
Ответ на: комментарий от Zubok

Странно — у меня все приложения заработали без XTEST, единственное опять кучу времени потратил на kwin, оказалось, чтобы повесить на своё окно прослушку ButtonRelease, надо не только BUttonReleaseMask, но и ButtonPressMask устанавливать ему в свойствах.

В остальном с десяток приложений работает, на openbox, xmonad, kwin в среде lxde, kde и без среды.

А про xinput2 можете подсказать? - Там тоже будет эта возможная проблема?

XTEST получается работает не с событиями иксов, а заставляет иксы получить сигнал как от драйвера мыши и клик получает самое верхнее окно?

Ещё хотел уточнить, а есть разница между мышкой и экранным тачем? Разве это не одно и тоже с точки зрения иксов? Просто заметил в Xinput2 отдельные структуры для тача...

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

А нафига вообще может стоять фильтрация? Ну то есть они (разрабы какого-нидуь приложения) поставили дополнительное условие if .artificial then reject. Зачем фильтровать?

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

Firefox последний тоже работает, как и urxvt, konsole, lxterminal. Только если окно перекрыто другим окном или свёрнуто, то клики не проходят в скрытую область, получается у меня XSendEvent в режиме XTEST работает? :)

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

Думаю это может быть критично для сайтов типа интернет банка :D

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

А нафига вообще может стоять фильтрация? Ну то есть они (разрабы какого-нидуь приложения) поставили дополнительное условие if .artificial then reject. Зачем фильтровать?

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

Догадываюсь, что таким образом некоторые приложения делают такие вещи для пущей безопасности, чтобы только человек через реальные устройства ввода мог в приложении что-то делать, а всякие программы-зловреды не могли. На это указывает тот факт, что в xterm есть опция, которая позволяет разрешить XSendEvent от других клиентов явно и она выключена по умолчанию. Объяснено как раз безопасностью:

 allowSendEvents (class AllowSendEvents)
               Specifies  whether or not synthetic key and button events (gen‐
               erated using the X protocol SendEvent request) should be inter‐
               preted  or  discarded.  The default is “false” meaning they are
               discarded.  Note that allowing such events would create a  very
               large  security  hole,  therefore enabling this resource force‐
               fully disables  the  allowXXXOps  resources.   The  default  is
               “false”.

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

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

Странно — у меня все приложения заработали без XTEST, единственное опять кучу времени потратил на kwin, оказалось, чтобы повесить на своё окно прослушку ButtonRelease, надо не только BUttonReleaseMask, но и ButtonPressMask устанавливать ему в свойствах.

Ничего странного. Значит, приложения (или тулкиты) не отсеивают синтетические сообщения.

XTEST получается работает не с событиями иксов, а заставляет иксы получить сигнал как от драйвера мыши и клик получает самое верхнее окно?

Ага, команды расширения XTEST позволяют X-клиенту попросить X-сервер сгенерировать события устройств ввода (переместить мышку, нажать кнопку мышки или клавиатуры), будто это реальные устройства делают. Сообщение проходит стандартным образом и абсолютно не отличается от сообщений реальных устройств. Приложение не сможет отличить, настоящее оно или нет. Все эти замуты с безопасностью от XSendEvent сводятся на нет расширением XTEST (просто его когда-то не существовало вовсе): всегда можно отыскать, где сидит то или иное окно, сделать его активным и ввести что угодно. Разница только в том, что сгенерированное событие проходит до окон стандартным образом, адресация тому или иному окну невозможна. X-сервер же тоже получает от мышки только координаты, а адресация уже потом осуществляется в соответствии с фокусом или взаимным перекрытием.

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

Все эти замуты с безопасностью от XSendEvent сводятся на нет расширением XTEST

При этом тулкиты продолжают фильтровать artificial сообщения. цЫрк.

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

При этом тулкиты продолжают фильтровать artificial сообщения. цЫрк.

А фильтруют? Я не пробовал. Вроде как у ТС'а не фильтруется и работает большинство.

Все эти замуты с безопасностью от XSendEvent сводятся на нет расширением XTEST

Кстати, XTEST можно запретить загружать в настройках сервера в секции Extensions.

Section "Extensions"
    Option "XTEST" "Disable"
EndSection
Zubok ★★★★★
()
Ответ на: комментарий от awpe

Firefox последний тоже работает, как и urxvt, konsole, lxterminal. Только если окно перекрыто другим окном или свёрнуто, то клики не проходят в скрытую область, получается у меня XSendEvent в режиме XTEST работает? :)

Потому что работает механизм диспетчеризации сообщений. Попробуй-ка active grab сделать, поделать свои виртуальные дела мышкой над перекрытым окном, а потом ungrab:

http://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html:

The source of the event is the viewable window that the pointer is in. The window used by the X server to report these events depends on the window's position in the window hierarchy and whether any intervening window prohibits the generation of these events. Starting with the source window, the X server searches up the window hierarchy until it locates the first window specified by a client as having an interest in these events. If one of the intervening windows has its do-not-propagate-mask set to prohibit generation of the event type, the events of those types will be suppressed. Clients can modify the actual window used for reporting by performing active grabs and, in the case of keyboard events, by using the focus window.

Далее это http://tronche.com/gui/x/xlib/input/pointer-grabbing.html

Потом расскажи, что получилось.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

Я вообще так понял, что active grab это зло, использую только для отслеживания system-wide нажатий например. Но с active grab большая проблема, если кто то уже забиндил за собой какое то событие, то забиндить его для себя не получится, только пассивная прослушка окна поможет. Но и это ещё не все, есть удалённая машина, там XGrabKey срабатывает на рутовое окно, но реально приложение моё не получает сигналов, как будто кто то вытягивает все клавиатурные ивенты из очереди иксов, такое возможно?

А насколько костыльным считается пройти по всему дереву окон, и повесить там везде XSelectInput, чтобы не маяться с active grab?

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

Но и это ещё не все, есть удалённая машина, там XGrabKey срабатывает на рутовое окно, но реально приложение моё не получает сигналов, как будто кто то вытягивает все клавиатурные ивенты из очереди иксов, такое возможно?

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

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

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

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

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

Я не понимаю, при чем тут получение события? Я же понимаю, что тебе надо клик выполнить, а не события получать.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от awpe

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

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

И нажатия клавиш надо получать, вне зависимости от состояния окна и клики мышкой, но уже только если по открытому окну.

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

Я не понимаю ничего. Я комментировал проблему, при которой частично закрытые окна не получают события по нажатию мышки. Это событие «глотает» приложение, которое находится поверх. Можешь проверить это, наложив два одинаковых окна приложения (два раза запустить одно приложение) друг на друга и посылая клик нижнему. Несмотря на указанный window id нижнего, событие получит верхнее окно, которое тебя не интересует. Это легко проверяется xdotool-ом.

Значит, надо форсировать получение события нижним окном, сграбив указатель. В приницпе, да, грабить указатель не лучшая затея. Она только тем плоха, что если ты не вернешь указатель мыши взад, то надо будет прибивать этого клиента тем или иным способом.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 2)
Ответ на: комментарий от Zubok

Я не понимаю ничего. Я комментировал проблему, при которой частично закрытые окна не получают события по нажатию мышки. Это событие «глотает» приложение, которое находится поверх. Можешь проверить это, наложив два одинаковых окна приложения (два раза запустить одно приложение) друг на друга и посылая клик нижнему. Несмотря на указанный window id нижнего, событие получит верхнее окно, которое тебя не интересует. Это легко проверяется xdotool-ом.

Это всё я понял и прочувствовал втч и с помощью xdotool'а, вопрос про system-wide мониторинг нажатых клавиш клавиатуры, и граббинг мыши(тача) для окна.

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

Если FF сграбил мышку для своих целей, то ты не сграбишь мышку. Но это не значит, что FF грабит мышку постоянно. Он грабит ее иногда. И тем более тогда, когда он в фокусе, когда пользователь, скажем, по менюшкам ходит, а кнопку мыши не отпускает. Тогда мышь, скорее всего, будет сграблена. А между этими событиями мышка должна быть ок.

Ты попробуй. Я тоже не знаю, что получится (не пускался в такие эксперименты). Две функции добавить. Зачем теоретизировать, получится или нет :) Надо проверить.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

А я не ошибусь если скажу, что работа с иксами позволяет одно и тоже сделать кучей способов, с разными последствиями и подводными камнями? Т.е. это нормально что приходится так возится, или где то есть статьи от программистов, как с этим ПРАВИЛЬНО работать? :)

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

Ты попробуй. Я тоже не знаю, что получится (не пускался в такие эксперименты). Две функции добавить. Зачем теоретизировать, получится или нет :) Надо проверить.

А если не секрет, ты в иксах по работе разбираешься или хобби?

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

вопрос про system-wide мониторинг нажатых клавиш клавиатуры, и граббинг мыши(тача) для окна.

Вот, кстати, конкретно мониторинг нажатых клавиш клавиатуры можно попробовать сделать, вызвав XSelectInput на окно root. По идее должно работать.

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

Ну как и было сказано в документации, только когда рут под фокусом, на другие окна не распространяется. Только если пройти по дереву и на каждое окно повесить XSelectInput, видимо на этом придётся остановится, так как это проще и прозрачнее чем создавать callback для X Record Extension и писать все события в очередь, потом эту очередь проверять на наличие сочетаний...

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

Надо попробовать, я так понял что XSelectInput только для окна с фокусом работает.

А как, по-твоему, оконный менеджер перехватывает свои комбинации? Он первым получает все события клавиатуры, а потом уже приложения. Вот прямо сейчас я назначил кнопку «a» на оконный менеджер. Результат: активное приложение больше букву «a» не получает вообще. Оконный менеджер первый ее забрал.

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

А как ты пытался букву «а» получить? Через XGrabKey или XSelectInput?

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

пройти по дереву и на каждое окно повесить XSelectInput

И появление новых окон отслеживать, которых не было до XQueryTree

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

Еще можешь попробовать

$ xinput --test <id клавиатуры, увидеть можно по xinput --list>

Оно и мышку покажет, если укажешь ее id

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от awpe

На root window повесить при помощи XSelectInput либо MapNotify/UnmapNotify, либо CreateNotify/DestroyNotify, чтобы апдейтить дерево. Но вообще идея с деревом - это какое-то чесание левого уха правой рукой.

UPD. что-то типа такого

    dpy = XOpenDisplay(NULL);
    root = DefaultRootWindow(dpy);
    XSelectInput(dpy, root, SubstructureNotifyMask);

    while (1) {
        XNextEvent(dpy, &event);

        /* Слушаем сообщения */
    }
Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 4)
Ответ на: комментарий от Zubok

Но вообще идея с деревом - это какое-то чесание левого уха правой рукой.

С деревом, конечно, есть и профит, на первый взгляд: можно узнать, какому окну пришло сообщение. Если просто устройства ввода слушать, то идут обезличенные координаты и коды кнопок, а если слушать события окон, то ты знаешь всегда, какое окно слушаешь. И даже можешь узнать, что вот эти последовательности пошли в окно FF, а эти — в терминал. Хотя надо будет уточнить, какой идентификатор окна придет в событиях...

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 2)
Ответ на: комментарий от Zubok

С деревом, конечно, есть и профит, на первый взгляд

Хотя... в событиях XDeviceMotionEvent, XDeviceKeyEvent, XDeviceButtonEvent и др. из X Input Extension тоже в событии идет event window (поле event), но там, похоже, сообщается окно, над которым указатель находится. Надо проверить.

http://www.x.org/archive/X11R7.6/doc/inputproto/XIproto.txt

3.1 Button, Key, and Motion Events

               DeviceKeyPress
               DeviceKeyRelease
               DeviceButtonPress,
               DeviceButtonRelease
               DeviceMotionNotify
                       device: CARD8
                       root, event: WINDOW
                       child: Window or None
                       same-screen: BOOL
                       root-x, root-y, event-x, event-y: INT16
                       detail: <see below>
                       state: SETofKEYBUTMASK
                       time: TIMESTAMP

   These events are generated when a key, button, or valuator
   logically changes state. The generation of these logical
   changes may lag the physical changes, if device event
   processing is frozen. Note that DeviceKeyPress and
   DeviceKeyRelease are generated for all keys, even those mapped
   to modifier bits. The “source” of the event is the window the
   pointer is in. The window with respect to which the event is
   normally reported is found by looking up the hierarchy
   (starting with the source window) for the first window on which
   any client has selected interest in the event. The actual
   window used for reporting can be modified by active grabs and
   by the focus window.The window the event is reported with
   respect to is called the “event” window.
Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 3)
Ответ на: комментарий от Zubok

Мне для клавиш окно совсем не интересно, мне сам факт, что были нажаты такие то клавиши интересен, и от окна в фокусе ничего не должно зависеть...

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

Мне для клавиш окно совсем не интересно, мне сам факт, что были нажаты такие то клавиши интересен, и от окна в фокусе ничего не должно зависеть...

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

И посмотри, как сделано в утилите xinput (параметры --test и --test-xi2). Там предельно все просто. Получишь коды нажатых клавиш вообще вне зависимости от всего, но с особенностью: модификаторы тоже будут коды клавиш слать. В xinput, как следует из названия, используется X Input (1 и 2).

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

Ну я ещё когда с тачем разбирался задумывался об использовании расширения xinput, только второй версии, хотел без расширений решить всё, но видимо так оптимальней будет...

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

Интересно, можно послать на устройство какой нибудь сигнал и проверить ответ, чтобы знать что устройство не подвисло?

Например

char buf[] = "что то на что ответит не зависшее устройство";
write(deviceFd, buf, sizeof(buf));
int res = pselect(...);
if (res == -1)
{
//reset device
}

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

только второй версии, хотел без расширений решить всё, но видимо так оптимальней будет...

В качестве каприза можно попробовать.

Интересно, можно послать на устройство какой нибудь сигнал и проверить ответ, чтобы знать что устройство не подвисло?

А что значит, подвисло? Ну, собсна, в X Input есть список устройств ввода и их свойства. Если ты что-то от USB отключаешь, то устройство пропадает. Вот и проверили. С PS/2 и COM, врочем, не уверен, что пройдет фокус. А так ядро сообщает, что что-то отключилось, а X-сервер отрабатывает. Но вот что такое «подвисшее» состояние... Драйвер завис?

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

Имеем хаб usb2.0, на нём большой светодиод, в него включено несколько устройств, предполагается что он активный, выключаем питание из розетки, включаем подсветку на клавиатуре - мышь светится, в списке устройств присутствует, по пока не подашь внешнее питание на хаб, курсор на движения мыши не реагирует, как этот момент проверить?

Я уже второй день ищу как с помощью xinput2 слушать устройство... Все примеры втч код из GTK и QT, предполагает делать так

XISelectEvents(...)
Для каждого виджета в окне, также в нескольких xinput1->xinput2 conversion guide, предлагают вешать прослушку на окно, печальный опыт с XSelectEvent, подсказывает что даже если повесить на корневое окно, пока фокус не нём не появится ничего прослушать не получится, на данный момент итог такой:

  • Идём по дереву и вешаем на каждое окно XSelectEvent, отслеживаем создание новых окон и на них тоже ставим прослушку
  • Не пользуемся xinput2, но используя xinput1 слушаем устройство вне зависимости от окна
    XOpenDevice(...)

Неужели xinput2 не предусматривает прослушивание устройства вне зависимости от окна?

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

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

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