I need to read several barcode scanners and bind the read data according to its source.
In other words, my application needs to know where from the keystrokes came to be able to take the correct actions like update the UI and send commands to dedicated external hardware.
How can I "route" the inputs of different keyboards/scanners to specific events in my app OR retrieve information that allow my app to find out where from the input came? (I start from the point that a barcode scanner is just a keyboard to the system.)
I know I can "open" the specific "device" to read raw data from it, but it is not the same as haveing a "keyboard event" in my app. (Consider also that my app is written in Qt, but I don't really need to be tied to it.)
Thanks.
EDIT: I'd better say that it must run on Linux. No windows nor .NET and no Embedded Linux too. I also plan to code it in C++/Qt, but am open to other frameworks. Sorry for the miss.
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.