Jjang
Jjang

Reputation: 11444

Writing chars to pipe in C

I have the following program which is basically reading chars from keyboard (getch() does this without the need to click 'ENTER', function is taken from here:Capture characters from standard input without waiting for enter to be pressed) , and then if the char is one of the legal moves (LEFT, RIGHT, etc) it is writing it to a pipe, which draw.out is reading from.

draw.out is reading the input it recieved and printing to screen. (assume this program works fine, I assure the problem is in the following code only).

The problem is, that:

  1. draw.out is acting like it has recieving my input over and over again. Which means, for some reason, even though I press, for exmaple, DOWN key only once, it sends draw.out as if I clicked many times on it.

  2. After sending one input, I cannot send anymore, as if the loop is stopping.

Tried to break my head over this for many hours... I'd really appreciate help.

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);
}

Upvotes: 1

Views: 2633

Answers (2)

John Kugelman
John Kugelman

Reputation: 361899

Change the while loop to a do/while loop to ensure getch() is always called at least once.

do
{
    c = getch();
}
while (c != ROTATE && c != LEFT && c != RIGHT && c != DOWN && c != QUIT);

Upvotes: 1

fredrik
fredrik

Reputation: 6638

After you have sent the char c you need to set it to a value other than ROTATE, LEFT, RIGHT or DOWN.

If you don't getch() will never be called a second time...

So at the end of the outer while (just after kill) or at the start of the loop (just before the while containing the getch() call) add something like

if (c != QUIT)
  c = ' ';

Upvotes: 1

Related Questions