Как вставить синтетические события мыши во входную очередь X11

У меня есть встроенное устройство под управлением Linux/X11, которое подключено к устройству, которое обеспечивает сенсорные события через USB-соединение. Это устройство не распознается как стандартная форма ввода указателя/мыши. Я пытаюсь найти способ «внедрять» события мыши в X11, когда внешнее устройство сообщает о событии.

Это избавит мое приложение (написанное на C с использованием Gtk+) от необходимости имитировать нажатие мыши вызовами Gtk+.

Если бы это можно было сделать, моему приложению Gtk+ не нужно было бы знать или заботиться об устройстве, генерирующем события касания. Это просто выглядело бы для приложения как стандартные события мыши.

Кто-нибудь знает, как вставить синтетические события мыши в X11?

Сейчас я делаю следующее, что работает, но не оптимально.

GtkWidget *btnSpin;     /* sample button */

gboolean buttonPress_cb( void *btn );
gboolean buttonDePress_cb( void *btn );


/*  make this call after the device library calls the TouchEvent_cb() callback
    and the application has determined which, if any, button was touched

    In this example we are assuming btnSpin was touched.

    This function will, in 5ms, begin the process of causing the button to do it's 
    normal animation ( button in, button out effects ) and then send the actual
    button_clicked event to the button.
*/
g_timeout_add(5, (GSourceFunc) buttonPress_cb, (void *)btnSpin);


/*  this callback is fired 5ms after the g_timeout_add() function above.
    It first sets the button state to ACTIVE to begin the animation cycle (pressed look)
    And then 250ms later calls buttonDePress_cb which will make the button look un-pressed
    and then send the button_clicked event.
*/    
gboolean buttonPress_cb( void *btn )
{

    gtk_widget_set_state((GtkWidget *)btn, GTK_STATE_ACTIVE);
    g_timeout_add(250, (GSourceFunc) buttonDePress_cb, btn);


    return( FALSE );
}

/*  Sets button state back to NORMAL ( not pressed look )
    and sends the button_clicked event so that the registered signal handler for the
    button can be activated
*/
gboolean buttonDePress_cb( void *btn )
{

    gtk_widget_set_state( btn, GTK_STATE_NORMAL);
    gtk_button_clicked( GTK_BUTTON( btn ));

    return( FALSE );
}

person Chimera    schedule 25.04.2012    source источник


Ответы (4)


Система ввода Linux имеет средство для реализации устройств ввода в пользовательском пространстве, называемое uinput. Вы можете написать фоновую программу, которая использует библиотеку обратного вызова вашего устройства для отправки событий ввода в ядро. X-сервер (при условии, что он использует модуль ввода evdev) будет обрабатывать их так же, как и любое другое событие мыши.

Существует библиотека под названием libsuinput, которая делает это довольно легко. Он даже включает пример программы ввода с помощью мыши, которую вы, вероятно, можете использовать в качестве модели. Однако, поскольку ваше устройство является сенсорным, оно, вероятно, будет использовать абсолютные оси (ABS_X, ABS_Y) вместо относительных (REL_X, REL_Y).

person Geoff Reedy    schedule 25.04.2012
comment
Отлично .. это может быть ответом. Мне нужно будет проверить, поддерживает ли мое встроенное устройство Linux систему uinput. Спасибо. - person Chimera; 25.04.2012

Есть несколько методов.

  1. Используйте 1_. Предостережение: некоторые платформы приложений игнорируют события, отправленные с помощью XSendEvent. Я думаю, что Gtk+ нет, но я не проверял.
  2. Используйте XTestFakeMotionEvent и XTestFakeButtonEvent. Вам нужно расширение XTest на вашем X-сервере.
  3. Напишите драйвер ядра для своего устройства, чтобы оно отображалось как мышь/тачпад.
person n. 1.8e9-where's-my-share m.    schedule 25.04.2012

Самым крутым было бы реализовать драйвер устройства внутри ядра, который создает файл /dev/input/eventX, говорящий на протоколе evdev. Я рекомендую вам прочитать книгу под названием Linux Device Drivers, если вы хотите это сделать. Книга находится в свободном доступе в сети.

Если вы хотите сделать это в пользовательском пространстве, я предлагаю вам использовать Xlib (или XCB). В простой Xlib (язык C) вы можете использовать расширение X Test Extension или XSendEvent().

Также есть бинарный файл xte из пакета xautomation (в Debian sudo apt-get install xautomation, а затем man xte). xte очень прост в использовании, и вы также можете посмотреть его исходный код, чтобы узнать, как использовать X Test Extension.

Указатели:

person pzanoni    schedule 25.04.2012
comment
Спасибо за подробный ответ. - person Chimera; 25.04.2012

Похоже, что после небольшого исследования Gtk+ использует библиотеку GDK, которая может делать то, что я хочу, без необходимости глубоко вникать в кодирование X11 или писать драйвер ядра. Хотя, если бы у меня было время, я бы предпочел написать драйвер Linux Kernel Mouse.

Используя Справочное руководство по GDK 2, я обнаружил, что можно сделать следующее:

Используйте gtk_event_put() для добавления GdkEvent типа GdkEventButton

Структура GdkEventButton такова:

struct GdkEventButton {
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble *axes;
  guint state;
  guint button;
  GdkDevice *device;
  gdouble x_root, y_root;
};

Большинство этих полей будет несложно заполнить, за исключением:

gdouble x;
    the x coordinate of the pointer relative to the window.

gdouble y;
    the y coordinate of the pointer relative to the window.

GdkDevice *device;
    the device where the event originated.

gdouble x_root;
    the x coordinate of the pointer relative to the root of the screen.

gdouble y_root;
    the y coordinate of the pointer relative to the root of the screen.

Мне нужно будет изучить, как преобразовать корневые координаты экрана в относительные координаты окна.

*device — я не уверен, нужно ли мне использовать это поле (установленное в NULL), потому что оно предназначено для расширенного устройства ввода. Однако, если мне нужно иметь действительное устройство здесь, я должен иметь возможность использовать gdk_devices_list()

person Chimera    schedule 25.04.2012