2

我编写了一个简单的程序,它从串行端口(/dev/ttyS1)读取外部设备(条形码扫描仪)的字符并将其提供给当前活动的窗口(使用 XSendEvent)。

程序在速度较快的计算机上运行良好,但在速度较慢的计算机上,情况会发生(经常),字符的接收顺序与发送的顺序不同。例如,scanner 发送 1234567 到串口,我的程序发送 char 事件 1234567,但是活动程序(例如 xterm)收到 3127456。我尝试在各个地方调用 XSync 并添加usleep调用,但没有帮助。

有谁知道如何强制角色的“顺序”?

还是有其他方法可以将字符串发送到活动窗口(如果需要,我什至不介意使用外部程序)?

这是代码,也许我只是做错了什么:

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>  // serial port stuff
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <X11/Xlib.h>

/*
    BarCode KeyboardFeeder: BCKF
    Copyright (c) Milan Babuskov
    Licence: GNU General Public Licence

    Compile with: g++ bckf.cpp -lX11 -L /usr/X11R6/lib/ -o bckf
    Keycodes:  /usr/X11R6/include/X11/keysymdef.h
*/
//-----------------------------------------------------------------------------
void SendEvent(XKeyEvent *event, bool press)
{
    if (press)
        XSendEvent(event->display, event->window, True, KeyPressMask, (XEvent *)event);
    else
        XSendEvent(event->display, event->window, True, KeyReleaseMask, (XEvent *)event);
    XSync(event->display, False);
}
//-----------------------------------------------------------------------------
bool sendChar(int c)
{
    if (c >= 0x08 && c <= 0x1b)     // send CR twice
    {
        sendChar(0xff0d);
        c = 0xff0d;
    }

    printf("Sending char : 0x%02x\n", c);
    char disp[] = ":0";
    char *dp = getenv("DISPLAY");
    if (!dp)
        dp = disp;
    else
        printf("Using env.variable $DISPLAY = %s.\n", dp);
    Display *dpy = XOpenDisplay(dp);
    if (dpy == NULL)
    {
        printf("ERROR! Couldn't connect to display %s.\n", dp);
        return false;
    }
    else
    {
        Window cur_focus;   // focused window
        int revert_to;      // focus state
        XGetInputFocus(dpy, &cur_focus, &revert_to);    // get window with focus
        if (cur_focus == None)
        {
            printf("WARNING! No window is focused\n");
            return true;
        }
        else
        {
            XKeyEvent event;
            event.display = dpy;
            event.window = cur_focus;
            event.root = RootWindow(event.display, DefaultScreen(event.display));
            event.subwindow = None;
            event.time = CurrentTime;
            event.x = 1;
            event.y = 1;
            event.x_root = 1;
            event.y_root = 1;
            event.same_screen = True;
            event.type = KeyPress;
            event.state = 0;
            event.keycode = XKeysymToKeycode(dpy, c);
            SendEvent(&event, true);
            event.type = KeyRelease;
            SendEvent(&event, false);
        }
        XCloseDisplay(dpy);
    }

    usleep(20);
    return true;
}
//-----------------------------------------------------------------------------
// Forward declaration
int InitComPort(const char *port);
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("Usage: bckf serial_port\n");
        return 1;
    }

    int port = InitComPort(argv[1]);
    if (port == -1)
        return 1;

    while (true)
    {
        char buf[30];
        ssize_t res = read(port, buf, 30);
        if (res > 0)
        {
            for (ssize_t i=0; i<res; ++i)
            {
                int c = buf[i];
                printf("Received char: 0x%02x\n", c);
                if (c >= '0' && c <= '9' || c >= 0x08 && c <= 0x1b)
                    if (!sendChar(c))   // called from console?
                        break;
            }
        }
    }
    return 0;
}
//-----------------------------------------------------------------------------
int InitComPort(const char *port)
{
    int c, res;
    struct termios newtio;
    struct termios oldtio;

    // Open modem device for reading and writing and not as controlling tty
    // because we don't want to get killed if linenoise sends CTRL-C.
    int fdSerial = open(port, O_RDWR | O_NOCTTY );
    if (fdSerial < 0)
    {
        printf(0, "Error opening port: %s\n%s", port, strerror(errno));
        return -1;
    }

    tcgetattr(fdSerial,&oldtio); // save current port settings
    memset(&newtio, 0, sizeof(newtio));
    newtio.c_cflag = B9600 | CS8 | CLOCAL | CREAD;                  // CREAD   : enable receiving characters
                                                                    // CS8     : character size 8
                                                                    // CLOCAL  : Ignore modem control lines
    newtio.c_iflag = IGNPAR;                                        // IGNPAR  : ignore bytes with parity errors
    newtio.c_oflag = 0;                                             // 0       : raw output (no echo, non-canonical)
    //newtio.c_cc[VTIME]    = timeout;                              // 10=1sec : inter-character timer (deciseconds)
    newtio.c_cc[VMIN]     = 1;                                      // 50      : blocking read until 50 chars received
    tcflush(fdSerial, TCIOFLUSH);                                   // clear the line and...
    tcsetattr(fdSerial,TCSANOW,&newtio);                            // ...activate new settings for the port
    return fdSerial;
}
//-----------------------------------------------------------------------------
4

2 回答 2

4

您的代码中的问题是您每次都使用 XOpenDisplay(...) 打开显示。每次调用 XOpenDisplay 都会创建一个新的协议上下文。您应该只打开一次显示,并且在发送事件时始终使用相同的显示句柄。在单个显示句柄的上下文中,事件保证保持有序。

在开始调用 sendChar(...) 之前初始化一次显示,例如在 main(...) 中,并始终使用相同的 Display 指针。仅在完成后关闭显示,就像使用 Com 端口一样。

于 2009-03-13T19:33:48.800 回答
1

您是否尝试强制时间戳每增加一?这应该告诉 X 服务器事件是随时间传播的,但这也意味着它会使您的扫描仪成为大约 142 次扫描/秒的瓶颈。不过,这听起来不错,假设人类参与了它的使用。

于 2009-03-13T16:08:10.823 回答