检测键盘/条形码扫描仪事件的来源

Posted

技术标签:

【中文标题】检测键盘/条形码扫描仪事件的来源【英文标题】:Detect the source of a keyboard/barcode scanner event 【发布时间】:2011-02-25 20:21:35 【问题描述】:

我需要读取几个条码扫描器,并根据其来源绑定读取的数据。

换句话说,我的应用程序需要知道从何处击键才能采取正确的操作,例如更新 UI 并将命令发送到专用的外部硬件。

如何将不同键盘/扫描仪的输入“路由”到我的应用中的特定事件,或检索允许我的应用找出输入来源的信息? (我从条形码扫描仪只是系统的键盘这一点开始。)

我知道我可以“打开”特定的“设备”以从中读取原始数据,但这与在我的应用程序中拥有“键盘事件”不同。 (还请考虑我的应用程序是用 Qt 编写的,但我真的不需要与之绑定。)

谢谢。

编辑: 我最好说它必须在 Linux 上运行。没有 Windows 也没有 .NET,也没有嵌入式 Linux。 我还计划用 C++/Qt 编写代码,但对其他框架持开放态度。 很抱歉错过了。

【问题讨论】:

这个问题已经被多次以不同的方式提出:***.com/questions/5088374***.com/questions/4764274***.com/questions/4487153***.com/questions/4464072 链接 1 没有足够的解决方案。链接 3 和 4 是关于 .NET 的,我需要一个 linux 解决方案。链接 2 有一个答案说它很容易,但没有说明更多......不过,它现在是更好的指针。谢谢。 【参考方案1】:

其实是个解决办法。

我仍然没有可用的应用程序显示,但这个概念是使用 XI2。 我将消化XI2 Recipes 并尝试将其绑定到QApplication::x11EventFilter()

如 XI2 Recipes, Part 3 中所示,我可以通过 XIButtonClassInfoXIKeyClassInfoXIValuatorClassInfo 中的字段 sourceid 来确定事件的来源。

Recipes, Part 4 显示如何打印有关事件源的信息(在void print_deviceevent(XIDeviceEvent* event) 中)。这样听起来很容易。

(即使还没有可行的解决方案,我还是决定发布一个答案,以便它可以帮助遇到同样问题的其他人。一旦我取得进展,我将用更好的报告编辑我自己的答案。)


编辑:

正如所承诺的,这是一个打印出键盘事件来源的工作 sn-p:

#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)]

这个超简单的测试表明,虽然所有的事件都来自masterdev_ev-&gt;deviceid = 3,但是slave可以通过dev_ev-&gt;sourceid来区分。

我想现在我可以根据应用程序上配置的dev_ev-&gt;sourceid 将传入事件路由到相应的“客户端”。

【讨论】:

【参考方案2】:

我正在处理一个非常相似的问题,发现这个答案非常有帮助。只是为了扩充所提供的代码示例,这里是创建可执行文件所需的标头和主要代码。请注意,int qt_version 参数从类构造函数中省略,因为它在类实现中未使用。还需要在 qxi2application.cpp 文件中添加一个简单的析构函数。

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();

【讨论】:

以上是关于检测键盘/条形码扫描仪事件的来源的主要内容,如果未能解决你的问题,请参考以下文章

.NET 的 POS |区分(条形码)扫描仪和键盘输入

拦截条码扫描事件

C# winform 怎么让窗体接受条码扫描器的输入

使用jquery检测扫描仪输入

来自蓝牙键盘的 IOS7 上 Safari 中的 onkeyup 事件

在iOS中使用条形码扫描仪作为键盘的方法?