Actually it is a solution.
I stil have no working application to show, but the concept is using XI2.
I am going to digest XI2 Recipes and try to bind it to QApplication::x11EventFilter()
.
As illustrated in the XI2 Recipes, Part 3, I can determine the source of an event with the field sourceid
present in XIButtonClassInfo
, XIKeyClassInfo
and XIValuatorClassInfo
.
Recipes, Part 4 shows how to print information about the source of an event (in void print_deviceevent(XIDeviceEvent* event)
). Sounds easy this way.
(Even having no working solution yet, I decided to post an answer so that it can help anyone else having the same problem. As soon as I make progress I'll edit my own answer with better reports.)
EDIT:
As promised, here is a working snippet that prints out the source of keyboard events:
#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;
}
It outputs something like (commented):
-------------------------------------------
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
The output of xinput list
is:
# 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)]
This ultra simple test shows that although all the events come from master dev_ev->deviceid = 3
, the slave is distinguishable by dev_ev->sourceid
.
I think now I'll be able to route the incoming events to the respective "clients" based on the dev_ev->sourceid
configured on application.