質問

シリアルポート(/ 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;
}
//-----------------------------------------------------------------------------
役に立ちましたか?

解決

コードの問題は、XOpenDisplay(...)で毎回ディスプレイを開くことです。 XOpenDisplayを呼び出すたびに、新しいプロトコルコンテキストが作成されます。ディスプレイは一度だけ開き、イベントを送信するときには常に同じディスプレイハンドルを使用する必要があります。単一のディスプレイハンドルのコンテキスト内で、イベントは順序付けられたままになります。

ディスプレイを1回初期化します。 main(...)で、sendChar(...)の呼び出しを開始する前に、常に同じDisplayポインターを使用します。 Comポートの場合のように、完了したらディスプレイを閉じます。

他のヒント

タイムスタンプを1つずつ強制的に増分しようとしましたか?これは、イベントが時間とともに広がることをXサーバーに伝える必要がありますが、スキャナーが約142スキャン/秒にボトルネックになることも意味します。しかし、人間がその使用に関与していると仮定すると、それは大丈夫に聞こえます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top