将字符串(字符)发送到活动窗口

时间:2009-03-13 16:03:31

标签: c++ c x11 keyboard-events

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

程序在速度更快的计算机上运行良好,但在速度较慢的计算机上,情况会发生(很多时候)不会按照发送的顺序收到字符。例如,扫描程序将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;
}
//-----------------------------------------------------------------------------

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

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