eap@bucsb.bu.edu.UUCP (Eric Pearce) (07/13/87)
I would like to write a routine that performs a repeated sequence of statements (i.e. a loop) that would check for input from the keyboard and do something according to what was typed in. Also, it would be able to continue doing the loop regardless of whether or not anything was typed in from the keyboard. like this: Begin Loop check for keyboard input if a key was pressed case 1) do something 2) do something else ... esac fi do some more stuff End Loop I used to be able to do this easily on my Apple ][. You could check a memory location to see if a key had been pressed and which one it was, without having the program wait for user input. (good for games) Is there a way to do this in C? Or maybe a different approach to the problem? (this is intended for a program on a mainframe) ANY ideas welcome! -Eric ------------------------------------------------------------------------------- Several people asked what machine I would be running this on: Encore MULTIMAX 4.3 BSD UNIX and a VAX 11/750 running the same... ------------------------------------------------------------------------------- -- ******************************************************************************* * UUCP : ..!harvard!bu-cs!bucsb!eap |-+-+ +-+-+-+-+-+-+-+\ /-+-+-+-+-+-+-+-+* * ARPANET: eap@bucsb.bu.edu | > : : : - @ - | g * * CSNET : eap%bucsb@bu-cs |-+-+-+-+-+-+-+-+-+-+/ \ +-+-+ +-+-+ +-+* * BITNET : cscc8vc@bostonu | | Blasted by ZAXXON | ; | * *******************************************************************************
edw@ius2.cs.cmu.edu (Eddie Wyatt) (07/13/87)
In article <1043@bucsb.bu.edu.UUCP>, eap@bucsb.bu.edu.UUCP (Eric Pearce) writes: > > > > I would like to write a routine that performs a repeated sequence of statements > (i.e. a loop) that would check for input from the keyboard and do something > according to what was typed in. Also, it would be able to continue doing the > loop regardless of whether or not anything was typed in from the keyboard. > This seems to be a common question in Unix. To follow, one procedure that reads one character without waiting for a return and one procedure that determines if a port is ready to read from. Modify them to fit your needs if you want. Do a man stty and ioctl to find out what the proper include files are. /************************************************************************** * * * w_read_char * * * ************************************************************************** Purpose : This function reads a single character from input without waiting for a return to be typed. Programmer : Eddie Wyatt Date : July 1987 Input : None Output : returns the current character from standard input Locals : ch - the character read omode - the old tty mode (used in restoring mode after the character is read) mode - use to change the tty mode so a single character can be read without waiting for a return Globals : stdin - not modified ************************************************************************/ int w_read_char() { struct sgttyb omode, mode; int ch; gtty(fileno(stdin),&mode); bcopy((char *) &mode, (char *) &omode,sizeof(struct sgttyb)); mode.sg_flags |= CBREAK; stty(fileno(stdin),&mode); ch = getchar(); stty(fileno(stdin),&omode); return(ch); } /************************************************************************** * * * ready_to_read * * * ************************************************************************** Purpose : This function returns TRUE if the port (fd) is ready to read from. Programmer : Eddie Wyatt Date : January 1987 Input : fd - a port Output : returns TRUE if the port is ready to read Locals : num - the number of bytes ready to read Globals : None ************************************************************************/ int ready_to_read(fd) int fd; { int num; ioctl(fd,FIONREAD,(char *)&num); return(num > 0); } -- Eddie Wyatt e-mail: edw@ius2.cs.cmu.edu terrorist, cryptography, DES, drugs, cipher, secret, decode, NSA, CIA, NRO.
rwhite@nu3b2.UUCP (Robert C. White Jr.) (07/14/87)
In article <1043@bucsb.bu.edu.UUCP>, eap@bucsb.bu.edu.UUCP (Eric Pearce) writes: > > I used to be able to do this easily on my Apple ][. You could check a memory > location to see if a key had been pressed and which one it was, without having > the program wait for user input. (good for games) > > Is there a way to do this in C? Or maybe a different approach to the problem? > (this is intended for a program on a mainframe) It would apear that an apple ][ isnt a mainframe after all ;-) SERIOUSLY: use the function ioctl to set O_NDELAY on standard input, use read to read your character. if there are no characters waiting, and you are not at an EOF condition read will return -1 and errno will be set to EAGAIN. This is how it is on our machine, check read(2) in your programmers refrence [or wherever] for details. AND REMEMBER TO ALWAS CLEAN UP YOUR ioctl BEFORE BRANCHING/EXITING FROM THE LOOP OR GO UP IN FLAMES! Robert. Disclaimer: My mind is so fragmented by random excursions into a wilderness of abstractions and incipient ideas that the practical purposes of the moment are often submerged in my consciousness and I don't know what I'm doing. [my employers certainly have no idea]
john@bby-bc.UUCP (john) (07/17/87)
> SERIOUSLY: use the function ioctl to set O_NDELAY on standard input, > use read to read your character. if there are no characters waiting, and you > are not at an EOF condition read will return -1 and errno will be set to > EAGAIN. > > This is how it is on our machine, check read(2) in your programmers > refrence [or wherever] for details. AND REMEMBER TO ALWAS CLEAN UP > YOUR ioctl BEFORE BRANCHING/EXITING FROM THE LOOP OR GO UP IN FLAMES! Isn't it supposed to return 0 if there are no characters read. Also isn't there an ioctl() settable value that specifies the minimum number of characters which must be in the buffer before a request is satisfied, along with a time period after which this many characters don't need to be there? john
guy%gorodish@Sun.COM (Guy Harris) (07/18/87)
> > SERIOUSLY: use the function ioctl to set O_NDELAY on standard input, > > use read to read your character. if there are no characters waiting, and you > > are not at an EOF condition read will return -1 and errno will be set to > > EAGAIN. > Isn't it supposed to return 0 if there are no characters read. It's like this: In systems that implement 4.[23]BSD-style no-delay I/O, if there are no characters waiting, "read" will return -1 and "errno" will be set to EWOULDBLOCK. In systems that implement System V-style no-delay I/O: if the descriptor doesn't refer to a stream, "read" will return 0; if the descriptor does refer to a stream, "read" will return -1 and "errno" will be set to EAGAIN. S5 prior to R3 didn't have streams. (Note that some systems may implement both styles of no-delay I/O.) In POSIX-style non-blocking I/O (selected with O_NONBLOCK, not O_NDELAY), "read" will return -1 and "errno" will be set to EAGAIN. I don't know if any systems implement this yet. (If your system doesn't define O_NONBLOCK, it doesn't implement it.) > Also isn't there an ioctl() settable value that specifies the minimum number > of characters which must be in the buffer before a request is satisfied, > along with a time period after which this many characters don't need to > be there? There is, but not under 4.[23]BSD; the person in question is running systems based on 4.[23]BSD. In those systems, there is an "ioctl" call FIONREAD that will tell you how many characters are waiting to be read; with this, you don't have to go into no-delay mode (and thus don't have to leave it, either). This is arguably more convenient. Guy Harris {ihnp4, decvax, seismo, decwrl, ...}!sun!guy guy@sun.com
greg@ncrcan.UUCP (Greg Foisy) (07/24/87)
In article <23758@sun.uucp> guy%gorodish@Sun.COM (Guy Harris) writes: >> Also isn't there an ioctl() settable value that specifies the minimum number >> of characters which must be in the buffer before a request is satisfied, >> along with a time period after which this many characters don't need to >> be there? >There is, but not under 4.[23]BSD; the person in question is running >systems based on 4.[23]BSD. In those systems, there is an "ioctl" >call FIONREAD that will tell you how many characters are waiting to >be read; with this, you don't have to go into no-delay mode (and thus >don't have to leave it, either). This is arguably more convenient. Under system V if you wish to set the number of characters needed to satisfy a read request you can disable cononical processing and set VMIN and VTIME to values which represent the minimum number of characters needed to satisfy a read request and the minimum amount of time respectively. You do this through an ioctl, setting the lflags to disable cononical processing. VMIN and VTIME are set in c_cc at VMIN and VTIME. (termio.c_cc[VMIN] and termio.c_cc[VTIME]). This may have the undesired effect of disabling the erase and kill edit functions. greg foisy.
mouse@mcgill-vision.UUCP (der Mouse) (07/24/87)
In article <805@nu3b2.UUCP>, rwhite@nu3b2.UUCP (Robert C. White Jr.) writes: > In article <1043@bucsb.bu.edu.UUCP>, eap@bucsb.bu.edu.UUCP (Eric Pearce) writes: >> I used to be able to do this [check for keyboard input without >> waiting] easily on my Apple ][. >> Is there a way to do this in C? This is a system-dependent area; presumably you are talking about C under UNIX. C per se does not address this sort of question - all operating system interface issues are handled through library routines. Unfortunately, there is enough variation across different versions of UNIX to make it impossible to give a blanket answer. > [U]se the function ioctl to set O_NDELAY on standard input, Please specify what flavor of UNIX! Under 4.3 BSD, for example, O_NDELAY is a flag to open(), not something one uses with ioctl() (the ioctl() is called FIONBIO, also usable with fcntl()). > [if no input and not EOF], read will return -1 and errno will be set > to EAGAIN. Another difference. Under 4.3, errno is set to EWOULDBLOCK. Please qualify such statements with what flavor of operating system your question or answer is for! (Yes rwhite, I know you said "on our system", but that doesn't help eap decide whether it applies to his system.) So, to sum up: it depends on your system. Under most (all?) current variants of UNIX, it is possible, but the details of how depend on the particular system. (I can provide detail for 4.3BSD UNIX, and general ideas for 4.2BSD, but please send mail rather than cluttering the net!) der Mouse (mouse@mcgill-vision.uucp)
hitchens@godzilla.cs.utexas.edu (Ron Hitchens, Sun Wiz) (07/27/87)
In article <1043@bucsb.bu.edu.UUCP> eap@bucsb.UUCP (Eric Pearce) writes: >I would like to write a routine that performs a repeated sequence of statements >(i.e. a loop) that would check for input from the keyboard and do something >according to what was typed in. Also, it would be able to continue doing the >loop regardless of whether or not anything was typed in from the keyboard. > >like this: > > Begin Loop > check for keyboard input > if a key was pressed > case > 1) do something > 2) do something else > ... > esac > fi > do some more stuff > End Loop > >Is there a way to do this in C? Or maybe a different approach to the problem? >(this is intended for a program on a mainframe) > ANY ideas welcome! > -Eric >------------------------------------------------------------------------------- >Several people asked what machine I would be running this on: > Encore MULTIMAX 4.3 BSD UNIX > and a VAX 11/750 running the same... >******************************************************************************* >* UUCP : ..!harvard!bu-cs!bucsb!eap |-+-+ +-+-+-+-+-+-+-+\ /-+-+-+-+-+-+-+-+* >* ARPANET: eap@bucsb.bu.edu | > : : : - @ - | g * >* CSNET : eap%bucsb@bu-cs |-+-+-+-+-+-+-+-+-+-+/ \ +-+-+ +-+-+ +-+* >* BITNET : cscc8vc@bostonu | | Blasted by ZAXXON | ; | * >******************************************************************************* This article is almost two weeks old, I resisted answering until I got caught up on comp.unix.questions. A couple of people answered it with solutions using non-blocking reads and ioctl()s, but this is a job for select(). If select() is available, and it is on Eric's BSD systems listed above, it's much better than using ioctl()s. Below is some code which does just what Eric describes, using select(). This code is from a program which displays rwho information on the screen using curses(), and updates its display every 20 seconds. This loop does the "every time" stuff at the top of the loop, and the only keyboard input it looks for is a refresh command, anything else means to quit. For an application like this, select is definitely better, since the process is asleep in a system call until either input is ready or the timer runs out. With non-blocking I/O and ioctl()s, your process must run around in a busy loop polling the keyboard. A select() polling loop can still be done by providing the address of a timer set to zero. See the select() man page for details. Ron Hitchens hitchens@ut-sally.uucp hitchens@godzilla.cs.utexas.edu --------------- loop () { static struct timeval timer = {REFRESH_DEFAULT, 0}; char c; int inmask, nfds; timer.tv_sec = refresh_time; /* settable on cmd line */ while (1) { show_stat (); /* do it */ inmask = 1; /* gotta set the mask always */ if ((nfds = select (32, &inmask, 0, 0, &timer)) < 0) { printf ("Error doing select, I'm gettin' outta here\n"); return; } if (nfds == 0) continue; /* timer expired */ read (0, &c, 1); /* the sucker hit a key */ switch (c) { case 'r': case 'R': case 0x12: /* ^R */ case 'l': case 'L': case 0x0C: /* ^L */ (void) wclear (stdscr); /* clear the physical screen */ (void) wrefresh (stdscr);/* for reassuring feedback */ break; default: return; } } } The above code puts the tty into CBREAK mode before calling loop(). You'll want to do this if you want to get each char as it becomes available. If you leave it in cooked mode, select() will not indicate data is avaliable until an entire line has been entered and a newline typed (or some other "break" character, such as ^C or ESC). The tty driver will also do backspace handling transparently to you, if you use CBREAK, you'll have to do it yourself (if appropriate). There is a gotcha to watch out for if you mix select() and stdio. Select() works on file descriptors, stdio implements a buffering system between your code and the raw file descriptor. This means that the data returned to you by getchar(), gets(), etc, were probably read earlier and are being returned from a buffer. Doing a select() on an fd being used by stdio will only be indicative of new data available on the fd, it won't know about any data previously read and buffered by the stdio routines. In general, it's not a good idea to mix stdio and fd operations. ---------
emc@unicus.UUCP (Eric M. Carroll) (07/27/87)
greg@ncrcan.UUCP writes: > Under system V if you wish to set the number of characters needed to satisfy > a read request you can disable cononical processing and set VMIN and VTIME to > values which represent the minimum number of characters needed to satisfy a > read request and the minimum amount of time respectively. > > You do this through an ioctl, setting the lflags to disable cononical > processing. VMIN and VTIME are set in c_cc at VMIN and VTIME. > (termio.c_cc[VMIN] and termio.c_cc[VTIME]). It should be noted here that VTIME takes affect *AFTER* the first character is received. ie the timeout is only for BETWEEN characters; the 0th to 1st character transition is not covered in this timeout. Thus trying to check if there is anything in the queue by using VTIME and attempting to read one character will fail. -- Eric Carroll Unicus Corporation, Toronto Ont. Eric.M.Carroll@Unicus.COM (Internet) {seismo!mnetor, utzoo!utgpu!utcsri}!unicus!Eric.M.Carroll (dumb UUCP) mnetor!unicus!Eric.M.Carroll@seismo.css.GOV (dumb ARPA)
edw@ius1.cs.cmu.edu (Eddie Wyatt) (07/27/87)
In article <8569@ut-sally.UUCP>, hitchens@godzilla.cs.utexas.edu (Ron Hitchens, Sun Wiz) writes: > > This article is almost two weeks old, I resisted answering until I got > caught up on comp.unix.questions. A couple of people answered it with > solutions using non-blocking reads and ioctl()s, but this is a job for > select(). If select() is available, and it is on Eric's BSD systems listed > above, it's much better than using ioctl()s. If your talking about my posting of "ready_to_read" for determining if a characters are ready to read, there is a reason why it doesn't used "select". Plain in simple, select is a dog of a system call. When I measure the time it takes to call both select and ioctl on a fd associated with a socket, I found ioctl FNREAD to be about 3 times as fast. To the orignal poster, you want to run both read_to_read or select with CBREAKs on. BTW select is only better than ioctl for determining if a SINGLE port has characters on it to read from, in that you can specify a time out value if needed. May I also sugguest you read the man page entry for "select". The synopsis specificly says the call is intended for multiplexing. Polling a single line doesn't constitute multiplexing, through the system call "select" can obviously be used for such operations. > > loop () > { > static struct timeval timer = {REFRESH_DEFAULT, 0}; > char c; > int inmask, nfds; > > timer.tv_sec = refresh_time; /* settable on cmd line */ > while (1) { > show_stat (); /* do it */ > inmask = 1; /* gotta set the mask always */ ^^^^^^^^^^^^^^^^^^^^ A mood point here. If you are using BSD 4.2 this should be: inmask = (1<<(fileno(stdout))); In BSD 4.3 this should be : { fd_set inmask; ..... FD_SET(fileno(stdout),&inmask); ......... } > if ((nfds = select (32, &inmask, 0, 0, &timer)) < 0) { > printf ("Error doing select, I'm gettin' outta here\n"); > return; > } > if (nfds == 0) > continue; /* timer expired */ > read (0, &c, 1); /* the sucker hit a key */ ^^^^^^^^^^ Another mood point. this should be - read(fileno(stdout),&c,sizeof(char)); And if you are overly cautious if ((readval = read(fileno(stdout),&c,sizeof(char))) == -1) perror("read failed"); else if (readval == 0) fprintf(stderr,"EOF encounter\n"); > switch (c) { > case 'r': > case 'R': > case 0x12: /* ^R */ > case 'l': > case 'L': > case 0x0C: /* ^L */ > (void) wclear (stdscr); /* clear the physical screen */ > (void) wrefresh (stdscr);/* for reassuring feedback */ > break; > default: > return; > } > } > } -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
rsh27@chemabs.UUCP (Robert S. Hall) (07/27/87)
In article <1043@bucsb.bu.edu.UUCP> eap@bucsb.UUCP (Eric Pearce) writes: >I would like to write a routine that performs a repeated sequence of statements >(i.e. a loop) that would check for input from the keyboard and do something >according to what was typed in. Also, it would be able to continue doing the >loop regardless of whether or not anything was typed in from the keyboard. On a System V version of Unix you could open stdin using O_NDELAY (By the way on our Ultrix 1.2 system, in System V emulation mode, a program that uses O_NODELAY mode on the keyboard will cause a user to be logged off when it terminates.) On 4.[2,3] Berkley Unix you could use "ioctl(0,FIONREAD,&lngintvar);" this will return the number of characters that are ready to be returned by a read call in a long int variable named "lngintvar". See the "tty(4)" documentation for more information. 6
hitchens@ut-sally.UUCP (Ron Hitchens, TP Repairman) (07/28/87)
In article <1021@ius1.cs.cmu.edu> edw@ius1.cs.cmu.edu (Eddie Wyatt) writes: }In article <8569@ut-sally.UUCP>, hitchens@godzilla.cs.utexas.edu (Ron Hitchens, Sun Wiz) writes: }> ...this is a job for select()... } If your talking about my posting of "ready_to_read" for determining }if a characters are ready to read, there is a reason why it doesn't used }"select". Plain in simple, select is a dog of a system call. When I measure }the time it takes to call both select and ioctl on a fd associated with a }socket, I found ioctl FNREAD to be about 3 times as fast. Ya gotta point, Eddie. Select does indeed incur quite a bit more overhead than an ioctl does. Though in fairness select is more flexible and works at a higher level than an ioctl does. In looking at this again, I think I made an assumption that the code would want to do periodic processing, while also watching the keyboard for input. This may not be the case, if you want to spend as much time as possible computing, but also notice that a key has been pressed, using a FIONREAD ioctl is quicker and less expensive. But if you want to go to sleep until either something is ready to read or a time interval expires, select is clearly better. This is in fact multiplexing, multiplexing a timer and an fd. This is a big win over polling loops for that sort of thing. } To the orignal poster, you want to run both read_to_read or select with }CBREAKs on. Yeah, and remember to reset the tty modes before you exit. You may also want to catch INT and QUIT signals so that you can reset the modes if you're interrupted. } BTW select is only better than ioctl for determining if a SINGLE port }has characters on it to read from, in that you can specify a time out value if }needed. May I also sugguest you read the man page entry for "select". The }synopsis specificly says the call is intended for multiplexing. Polling a }single line doesn't constitute multiplexing, through the system call "select" }can obviously be used for such operations. Agreed. Polling an fd with a timeout is what I had in mind. This is not always what is desired, but is a good coding practice for interactive applications. It allows you to surrender control to the kernel, so that other processes can run, and have it wake you up when one of two (or more) conditions is met. Again, I agree that select is not a good choice for doing a straight poll on a single fd, though it can be done. A direct ioctl is less expensive for that. }> loop () }> { }> static struct timeval timer = {REFRESH_DEFAULT, 0}; }> char c; }> int inmask, nfds; }> }> timer.tv_sec = refresh_time; /* settable on cmd line */ }> while (1) { }> show_stat (); /* do it */ }> inmask = 1; /* gotta set the mask always */ } ^^^^^^^^^^^^^^^^^^^^ } A mood point here. If you are using BSD 4.2 this should be: } inmask = (1<<(fileno(stdout))); } } In BSD 4.3 this should be : } { } fd_set inmask; } ..... } } FD_SET(fileno(stdout),&inmask); } ......... } } Right, except that I'm using stdin, not stdout. The hardcoded 1 (shame on me!) is the bit for fd 0 which is (usually) stdin. Sorry about that, this is ancient code and I should know better. }> if ((nfds = select (32, &inmask, 0, 0, &timer)) < 0) { }> printf ("Error doing select, I'm gettin' outta here\n"); }> return; }> } }> if (nfds == 0) }> continue; /* timer expired */ }> read (0, &c, 1); /* the sucker hit a key */ } ^^^^^^^^^^ } Another mood point. this should be - } read(fileno(stdout),&c,sizeof(char)); Right again. I really do code more portably now (honest). This code is from a program which is necessarily BSD specific (it looks at rwhod packets, which is a hack only Berkeley could love), so I went ahead and harcoded those fd values, a habit I've mostly broken now. I could argue about sizeof(char), in that I want to only read one byte, even if sizeof(char) wasn't 1, but it's not worth it. } And if you are overly cautious } } if ((readval = read(fileno(stdout),&c,sizeof(char))) == -1) } perror("read failed"); } else if (readval == 0) } fprintf(stderr,"EOF encounter\n"); I wasn't overcautious, I wanted to quit on the slightest excuse and leave. If there was a read error or EOF, I'd exit through the switch statement, though I should have cleared the character before reading. }> switch (c) { }> case 'r': }> case 'R': }> case 0x12: /* ^R */ }> case 'l': }> case 'L': }> case 0x0C: /* ^L */ }> (void) wclear (stdscr); /* clear the physical screen */ }> (void) wrefresh (stdscr);/* for reassuring feedback */ }> break; }> default: }> return; }> } }> } }> } } Eddie Wyatt }e-mail: edw@ius1.cs.cmu.edu I'm a little embarrassed by the number of nits to be picked in my sample code, I should have cleaned it up before posting. But anyway, I'm still a fan of select, when used appropriately it can greatly simplify your life, and achieving the same functionality without it is a major pain. Ron Hitchens hitchens@ut-sally.uucp hitchens@godzilla.cs.utexas.edu
gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/28/87)
In article <196@chemabs.UUCP> rsh27@UNIX14.UUCP (PUT YOUR NAME HERE) writes: >(By the way on our Ultrix 1.2 system, in System V emulation mode, a program >that uses O_NODELAY mode on the keyboard will cause a user to be logged off >when it terminates.) That's inherent in O_NDELAY. If you don't reset it, when the shell tries to read from the terminal it gets back what it interprets as EOF and exits.
guy%gorodish@Sun.COM (Guy Harris) (07/29/87)
> It should be noted here that VTIME takes affect *AFTER* the first character > is received. Unless VMIN is zero, in which case (under System V, but not under System III), the timer is started when the "read" is done, and the "read" is satisfied as soon as a single character arrives. If VTIME is also zero, the effect is the same as if you'd turned no-delay mode on; the "read" is immediately satisfied regardless of whether there are any characters present or not. Of course, the person who asked the original question was using machines that, most likely, have a 4BSD terminal driver, so none of this is pertinent. Furthermore, all they wanted to do was know whether there was any input present; FIONREAD, which *is* available under 4BSD, does this job better than either no-delay I/O or VMIN/VTIME. Guy Harris {ihnp4, decvax, seismo, decwrl, ...}!sun!guy guy@sun.com