Запись символов в конвейер в C

У меня есть следующая программа, которая в основном читает символы с клавиатуры (getch() делает это без необходимости нажимать «ENTER», функция взята отсюда: Захватывать символы из стандартного ввода, не дожидаясь нажатия клавиши ввода) , а затем, если char является одним из допустимых ходов (ВЛЕВО, ВПРАВО и т. д.), он записывает его в канал, из которого считывается draw.out.

draw.out считывает полученный ввод и выводит его на экран. (предположим, что эта программа работает нормально, я уверяю, что проблема только в следующем коде).

Проблема в том, что:

  1. draw.out ведет себя так, будто получает мой ввод снова и снова. Это означает, что по какой-то причине, хотя я нажимаю, например, клавишу DOWN только один раз, он отправляет draw.out, как если бы я нажимал на него много раз.

  2. После отправки одного ввода больше отправить не могу, как будто цикл останавливается.

Пытался ломать голову над этим много часов... Буду очень признателен за помощь.

int main(int argc, const char* argv[])
{
pid_t child_pid;
int fda[2];
if(pipe(fda)<0)
    perror("pipe error");

if((child_pid=fork())<0)
    perror("fork error");
else
{
    //if we're in father
    if(child_pid>0)
    {
        char c=' ';

        //close read side
        close(fda[0]);

        while(c!=QUIT)
        {
            //get an instruction from keyboard
            while(c!=ROTATE && c!=LEFT && c!=RIGHT &&
                    c!=DOWN && c!=QUIT)
            {
                c=getch();
            }

            //write the instruction to pipe
            write(fda[1],&c,1);
            //notify the child
            kill(child_pid,SIGUSR2);

        }
    }
    //if we're in child process
    else
    {
        dup2(fda[0],0);
        close(fda[0]);
        close(fda[1]);
        execl("./draw.out","./draw.out",NULL);
    }
}

//close everything
close(fda[0]);
close(fda[1]);
return 0;
}

//this function works well in linux with tsch installed or in SSH bash shell
char getch()
{
char buf = 0;
struct termios old = {0};
if (tcgetattr(0, &old) < 0)
        perror("tcsetattr()");
old.c_lflag &= ~ICANON;
old.c_lflag &= ~ECHO;
old.c_cc[VMIN] = 1;
old.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &old) < 0)
        perror("tcsetattr ICANON");
if (read(0, &buf, 1) < 0)
        perror ("read()");
old.c_lflag |= ICANON;
old.c_lflag |= ECHO;
if (tcsetattr(0, TCSADRAIN, &old) < 0)
        perror ("tcsetattr ~ICANON");
return (buf);
}

person Jjang    schedule 20.04.2013    source источник


Ответы (2)


После того, как вы отправили символ c, вам необходимо установить для него значение, отличное от ROTATE, LEFT, RIGHT или DOWN.

Если вы не getch(), вас никогда не позовут во второй раз...

Итак, в конце внешнего while (сразу после kill) или в начале цикла (непосредственно перед while, содержащим вызов getch()) добавьте что-то вроде

if (c != QUIT)
  c = ' ';
person fredrik    schedule 20.04.2013
comment
О Боже! большое спасибо! 6 часов и много головной боли! Я чувствую такое облегчение. спасибо - person Jjang; 20.04.2013

Измените цикл while на цикл do/while, чтобы getch() всегда вызывался хотя бы один раз.

do
{
    c = getch();
}
while (c != ROTATE && c != LEFT && c != RIGHT && c != DOWN && c != QUIT);
person John Kugelman    schedule 20.04.2013