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

发布于 2024-10-19 04:38:22 字数 392 浏览 2 评论 0原文

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

换句话说,我的应用程序需要知道击键的来源,以便能够采取正确的操作,例如更新 UI 并向专用外部硬件发送命令。

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

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

谢谢。

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

耳钉梦 2024-10-26 04:38:22

其实这是一个解决方案。

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

如 XI2 食谱,第 3 部分 中所示,我可以使用 XIButtonClassInfoXIKeyClassInfoXIValuatorClassInfo 中存在的 sourceid 字段确定事件源。

食谱,第 4 部分展示了如何打印信息有关事件源的信息(在 void print_deviceevent(XIDeviceEvent* event) 中)。这样听起来很容易。

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


编辑:

正如所承诺的,这里是一个打印出键盘事件源的工作片段:

#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 将传入事件路由到相应的“客户端”。

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.

苍白女子 2024-10-26 04:38:22

我正在研究一个非常相似的问题,发现这个答案非常有帮助。只是为了补充提供的代码示例,这里是创建可执行文件所需的标头和主要代码。请注意,类构造函数中省略了 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();
}

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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文