2009-03-13 3 views
2

Я написал простую программу, которая считывает символы с внешнего устройства (сканера штрих-кода) из последовательного порта (/ 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; 
} 
//----------------------------------------------------------------------------- 

ответ

3

проблема в вашем коде заключается в том, что вы каждый раз открываете дисплей с помощью XOpenDisplay (...). Каждый вызов XOpenDisplay создает новый контекст протокола. Вы должны открывать дисплей только один раз и всегда использовать один и тот же маркер дисплея при отправке событий. В контексте одного дескриптора отображения события гарантированно остаются упорядоченными.

Инициализируйте дисплей один раз, например. в основном (...), прежде чем вы начнете вызывать sendChar (...) и всегда будете использовать один и тот же указатель дисплея. Закройте дисплей только тогда, когда вы закончите, как и для COM-порта.

+0

Я не могу воспроизвести исходную проблему (что странно), поэтому я не могу быть уверен, что это изменение исправляет ее. Но, спасибо, я попробую, как только проблема снова возникнет. –

+1

В общем, XOpenDisplay (...) открывает новый сокет. Это зависит от состояния вашего X-сервера, как он обрабатывает запросы, поступающие через разные сокеты, поэтому проблема может возникнуть и уйти. Но определенно это «ошибка» в программе, которую вы называете XOpenDisplay (...) много раз; обычно вы называете это только один раз. –

1

Пробовали ли вы заставить метку времени увеличивается на единицу для каждого? Это должно сказать серверу X, что события распространяются во времени, но это также означает, что это затрудняет сканирование сканера примерно на 142 сканирования/секунду. Это звучит хорошо, хотя, предполагая, что люди вовлечены в его использование.

+0

Как вы можете видеть в коде, я устанавливаю CurrentTime для каждого символа. Вы подразумеваете, что мой код выполняется так быстро, и несколько символов получают одну и ту же метку времени? Это может объяснить, почему это только создает проблемы на быстрых машинах. Теперь, какую временную метку я должен установить? Прошлое или будущее? –