2009-03-13 12 views
2

Ich habe ein einfaches Programm geschrieben, das die Zeichen vom externen Gerät (Barcode-Scanner) vom seriellen Port (/ dev/ttyS1) liest und es dem gerade aktiven Fenster zuführt (mit XSendEvent) .Zeichenfolge (Zeichen) an aktives Fenster senden

Programm funktioniert auf schnelleren Computern gut, aber auf langsamen passiert die Situation (sehr oft), dass Zeichen nicht in der gleichen Reihenfolge empfangen werden, in der sie gesendet wurden. Zum Beispiel sendet scanner 1234567 an die serielle Schnittstelle, mein Programm sendet Char-Ereignisse 1234567, aber das aktive Programm (xterm zum Beispiel) empfängt 3127456. Ich versuchte, XSync an verschiedenen Orten und Hinzufügen usleep Anrufe, aber es hat nicht geholfen.

Hat jemand eine Idee, wie man die "Reihenfolge" von Zeichen erzwingt?

Oder gibt es eine andere Möglichkeit, um eine Zeichenfolge an das aktive Fenster zu senden (ich habe nicht einmal etwas dagegen, ein externes Programm zu verwenden, wenn nötig)?

Hier ist der Code, vielleicht bin ich nur etwas falsch zu machen:

#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; 
} 
//----------------------------------------------------------------------------- 

Antwort

3

Das Problem in Ihrem Code ist, dass Sie die Anzeige jedes Mal mit XOpenDisplay öffnen (...). Jeder Aufruf von XOpenDisplay erstellt einen neuen Protokollkontext. Sie sollten die Anzeige nur einmal öffnen und beim Senden der Ereignisse immer dieselbe Anzeige verwenden. Im Rahmen eines einzigen Anzeige-Handles bleiben die Ereignisse garantiert geordnet.

Initialisieren Sie die Anzeige einmal, z. in main (...), bevor Sie sendChar (...) aufrufen und immer den gleichen Display-Zeiger verwenden. Schließen Sie die Anzeige nur, wenn Sie fertig sind, wie Sie es mit dem Com-Port tun.

+0

Ich kann das ursprüngliche Problem jetzt nicht reproduzieren (was seltsam ist), daher kann ich nicht sicher sein, dass diese Änderung es behebt. Aber, danke, ich werde es versuchen sobald das Problem wieder auftritt. –

+1

Im Allgemeinen öffnet XOpenDisplay (...) einen neuen Socket. Es hängt vom Status Ihres X-Servers ab, wie er Anforderungen behandelt, die über verschiedene Sockets eingehen, so dass das Problem kommen und gehen kann. Aber definitiv ist es ein "Fehler" im Programm, dass Sie XOpenDisplay (...) viele Male aufrufen; normalerweise rufst du es nur einmal an. –

1

Haben Sie versucht, den Zeitstempel zu zwingen, indem man für jeden zu erhöhen? Das sollte dem X-Server mitteilen, dass die Ereignisse über die Zeit verteilt sind, aber es bedeutet auch, dass es den Scanner auf etwa 142 Scans/Sekunde beschränkt. Das klingt aber okay, wenn man annimmt, dass Menschen an seiner Verwendung beteiligt sind.

+0

Wie Sie im Code sehen können, stelle ich die CurrentTime für jedes Zeichen ein. Implizieren Sie, dass mein Code so schnell ausgeführt wird und mehrere Zeichen denselben Zeitstempel erhalten? Das könnte erklären, warum es nur Probleme auf schnellen Maschinen macht. Nun, welchen Zeitstempel soll ich setzen? Eine Vergangenheit oder eine Zukunft? –