2009-03-13 11 views
2

J'ai écrit un programme simple qui lit les caractères d'un périphérique externe (scanner de code à barres) depuis le port série (/ dev/ttyS1) et l'alimente dans la fenêtre active (avec XSendEvent) . Le programme fonctionne correctement sur les ordinateurs plus rapides, mais sur les ordinateurs lents, il arrive (très souvent) que les caractères ne soient pas reçus dans le même ordre que celui où ils ont été envoyés. Par exemple, scanner envoie 1234567 au port série, mon programme envoie des événements char 1234567, mais le programme actif (xterm par exemple) reçoit 3127456. J'ai essayé d'appeler XSync à divers endroits et en ajoutant usleep appels, mais cela n'a pas aidé.Envoi d'une chaîne (de caractères) à la fenêtre active

Est-ce que quelqu'un a une idée sur la façon de forcer l'ordre des caractères?

Ou existe-t-il un autre moyen d'envoyer une chaîne à la fenêtre active (cela ne me dérange même pas d'utiliser un programme externe si nécessaire)?

est ici le code, peut-être que je vais juste faire quelque chose de mal:

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

Répondre

3

le problème dans votre code est que vous ouvrez l'affichage à chaque fois avec XOpenDisplay (...). Chaque appel à XOpenDisplay crée un nouveau contexte de protocole. Vous ne devez ouvrir l'affichage qu'une seule fois et toujours utiliser la même poignée d'affichage lorsque vous envoyez les événements. Dans le contexte d'une seule poignée d'affichage, les événements sont garantis pour rester commandés.

Initialisez l'affichage une fois, par ex. dans main (...), avant de commencer à appeler sendChar (...), et toujours utiliser le même pointeur d'affichage. Fermez l'écran uniquement lorsque vous avez terminé, comme vous le faites avec le port Com.

+0

Je suis incapable de reproduire le problème d'origine maintenant (ce qui est étrange), donc je ne peux pas être sûr que ce changement le corrige. Mais, merci, je vais essayer dès que le problème se pose à nouveau. –

+1

En général, XOpenDisplay (...) ouvre une nouvelle socket. Cela dépend de l'état de votre serveur X, de la façon dont il traite les demandes provenant de différentes sockets, de sorte que le problème peut aller et venir. Mais c'est définitivement une "erreur" dans le programme que vous appelez XOpenDisplay (...) plusieurs fois; Habituellement, vous l'appelez une seule fois. –

1

Avez-vous essayé de forcer l'horodatage à incrémenter par un pour chacun? Cela devrait indiquer au serveur X que les événements sont étalés dans le temps, mais cela signifie également que votre scanner subit un goulot d'étranglement à environ 142 balayages/seconde. Cela semble correct cependant, en supposant que les humains sont impliqués dans son utilisation.

+0

Comme vous pouvez le voir dans le code, je définis le CurrentTime pour chaque caractère. Impliquez-vous que mon code s'exécute si rapidement et que plusieurs caractères obtiennent le même horodatage? Cela pourrait expliquer pourquoi il ne fait que des problèmes sur les machines rapides. Maintenant, quel horodateur dois-je définir? Un passé ou un futur? –