На самом деле это решение.
У меня еще нет рабочего приложения, которое можно показать, но концепция использует XI2.
Я собираюсь переварить XI2 Recipes и попытаться связать его с QApplication::x11EventFilter()
.
Как показано в рецептах XI2 , часть 3 , я могу определить источник события с полем sourceid
, присутствующим в XIButtonClassInfo
, XIKeyClassInfo
и XIValuatorClassInfo
.
Рецепты, часть 4 показывает, как распечатать информацию об источнике события (в void print_deviceevent(XIDeviceEvent* event)
). Звучит просто так.
(Даже не имея рабочего решения, я решил опубликовать ответ, чтобы он мог помочь кому-то еще, имеющему ту же проблему. Как только я добьюсь прогресса, я отредактирую свой собственный ответ с лучшими отчетами.)
EDIT:
Как и было обещано, вот рабочий фрагмент, который распечатывает источник событий клавиатуры:
#include <QDebug>
#include "qxi2application.h"
#include <QX11Info>
#include <X11/extensions/XInput2.h>
// XI2 Event types.
static const char *_xi2_event_names[] =
{
"Reserved 0",
"XI_DeviceChanged",
"XI_KeyPress",
"XI_KeyRelease",
"XI_ButtonPress",
"XI_ButtonRelease",
"XI_Motion",
"XI_Enter",
"XI_Leave",
"XI_FocusIn",
"XI_FocusOut",
"XI_HierarchyChanged",
"XI_PropertyEvent",
"XI_RawKeyPress",
"XI_RawKeyRelease",
"XI_RawButtonPress",
"XI_RawButtonRelease",
"XI_RawMotion"
};
#include <QMainWindow>
QXI2Application::QXI2Application( int &argc, char **argv, int qt_version )
: QApplication( argc, argv, qt_version )
{
int event, error;
_display = QX11Info::display( );
if ( !XQueryExtension( _display, "XInputExtension", &xi_opcode, &event, &error ) )
qDebug( ) << "X Input extension not available.\n";
// We support XI 2.0.
int major = 2;
int minor = 0;
int rc = XIQueryVersion( _display, &major, &minor );
if ( rc == BadRequest )
qDebug( ) << "No XI2 support. Server supports version " << major << "." << minor << " only.\n";
else if ( rc != Success )
qDebug( ) << "Internal Error! This is a bug in Xlib.\n";
else
qDebug( ) << "XI2 supported. Server provides version " << major << "." << minor;
}
void QXI2Application::setMainWindow( QMainWindow *wnd )
{
XIEventMask evmasks[ 1 ];
unsigned char mask1[ ( XI_LASTEVENT + 7 ) / 8 ];
memset( mask1, 0, sizeof( mask1 ) );
// Select for key events from all master devices.
XISetMask( mask1, XI_KeyPress );
XISetMask( mask1, XI_KeyRelease );
evmasks[ 0 ].deviceid = XIAllMasterDevices;
evmasks[ 0 ].mask_len = sizeof( mask1 );
evmasks[ 0 ].mask = mask1;
XISelectEvents( _display, wnd->winId( ), evmasks, 1 );
XFlush( _display );
}
bool QXI2Application::x11EventFilter( XEvent *event )
{
XGenericEventCookie *cookie = &event->xcookie;
if ( event->type != GenericEvent
|| cookie->extension != xi_opcode
|| !XGetEventData( _display, cookie ) )
{
return false;
}
qDebug( ) << "cookie->evtype = " << cookie->evtype << " ("
<< _xi2_event_names[ cookie->evtype < XI_LASTEVENT ? cookie->evtype : XI_LASTEVENT ] << ")";
switch( cookie->evtype )
{
case XI_KeyPress:
{
qDebug( ) << "\tXI_KeyPress";
XIDeviceEvent *dev_ev = ( XIDeviceEvent * )event->xcookie.data;
qDebug( ) << "\tdev_ev->deviceid = " << dev_ev->deviceid;
qDebug( ) << "\tdev_ev->sourceid = " << dev_ev->sourceid;
break;
}
case XI_KeyRelease:
{
qDebug( ) << "\tXI_KeyRelease";
XIDeviceEvent *dev_ev = ( XIDeviceEvent * )event->xcookie.data;
qDebug( ) << "\tdev_ev->deviceid = " << dev_ev->deviceid;
qDebug( ) << "\tdev_ev->sourceid = " << dev_ev->sourceid;
break;
}
default:
qDebug( ) << "\tcookie->evtype = " << cookie->evtype;
break;
}
XFreeEventData( _display, cookie );
return false;
}
Выводит что-то вроде (прокомментировано):
-------------------------------------------
XI2 supported. Server provides version 2 . 0
-------------------------------------------
[Keyboard] ↳ Dell Dell USB Keyboard id=8 [slave keyboard (3)]
cookie->evtype = 2 ( XI_KeyPress )
XI_KeyPress
dev_ev->deviceid = 3
dev_ev->sourceid = 8
cookie->evtype = 3 ( XI_KeyRelease )
XI_KeyRelease
dev_ev->deviceid = 3
dev_ev->sourceid = 8
-------------------------------------------
[Barcode] ↳ Cypress-Weikeng USB Adapter id=10 [slave keyboard (3)]
cookie->evtype = 2 ( XI_KeyPress )
XI_KeyPress
dev_ev->deviceid = 3
dev_ev->sourceid = 10
cookie->evtype = 3 ( XI_KeyRelease )
XI_KeyRelease
dev_ev->deviceid = 3
dev_ev->sourceid = 10
Выход xinput list
:
# xinput list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ Dell Dell USB Optical Mouse id=9 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Power Button id=7 [slave keyboard (3)]
↳ Dell Dell USB Keyboard id=8 [slave keyboard (3)]
↳ Cypress-Weikeng USB Adapter id=10 [slave keyboard (3)]
Этот ультра-простой тест показывает, что, хотя все события происходят от ведущего dev_ev->deviceid = 3
, ведомое устройство различается по dev_ev->sourceid
.
Думаю, теперь я смогу перенаправлять входящие события соответствующим «клиентам» на основе dev_ev->sourceid
, настроенного в приложении.