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 com开发者_StackOverflow中文版mands 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.
I'm working on a very similar problem and found this answer very helpful. Just to augment the code example provided, here is the header and main code needed to create an executable. Note that the int qt_version
parameter was omitted from the class constructor, as it's unused in the class implementation. A trivial destructor also needs to be added to the qxi2application.cpp file.
qxi2application.h
#ifndef QXI2APPLICATION_H
#define QXI2APPLICATION_H
#include <QApplication>
#include <QMainWindow>
class QXI2Application : public QApplication
{
Q_OBJECT
public:
QXI2Application(int &argc, char **argv);
~QXI2Application();
void setMainWindow( QMainWindow *wnd );
bool x11EventFilter( XEvent *event );
private:
Display* _display;
int xi_opcode;
};
#endif // QXI2APPLICATION_H
main.cpp
#include "qxi2application.h"
#include <QApplication>
#include <QMainWindow>
int main(int argc, char *argv[])
{
QXI2Application a(argc, argv);
QMainWindow* wind = new QMainWindow;
a.setMainWindow(wind);
wind->show();
return a.exec();
}
精彩评论