Unix version of "kbhit()"?

Unix version of "kbhit()"?

Post by Pete Kruckenbe » Wed, 04 Jan 1995 08:24:06



I'm porting a DOS program to Linux, and I'm wondering how to port the
kbhit() call? I tried out using select() on STDIN, but it only returns
when I hit a RETURN key. I'd like to just poll the keyboard and see if
a key has been hit while focused on my application. Any ideas?

Thanks.
Pete Kruckenberg

 
 
 

Unix version of "kbhit()"?

Post by Jason Venn » Thu, 05 Jan 1995 09:17:40


to correctly emulate kbhit, you will need to set unbuffered mode on the console

Doing this correctly requires a couple of things:
        1: your program needs to restore the old console mode
                on exit (including abnormal exits).
                This is usually done via signal handles

        2: look at the man 2 termios for the calls on actually changing
                the console mode

 
 
 

Unix version of "kbhit()"?

Post by Raul Deluth Mill » Thu, 05 Jan 1995 04:17:30


Pete Kruckenberg:
   I'm porting a DOS program to Linux, and I'm wondering how to port the
   kbhit() call? I tried out using select() on STDIN, but it only returns
   when I hit a RETURN key. I'd like to just poll the keyboard and see if
   a key has been hit while focused on my application. Any ideas?

You can give select an argument that says how long to wait.  

From a man page on select:

     #include <sys/time.h>
     #include <sys/types.h>

     int select(int nfds, fd_set *readfds, fd_set *writefds,
          void fd_set *exceptfds, struct timeval *timeout);

Just set your timeout to something you like.  If timeout is a null
pointer select waits for ever.  However, if it's a pointer to a zero
timeval, select returns immediately.  [More likely, you'll want to
have the program wait a tenth of a second or more if there's no
computation being doing aside from the polling.]

--


                        1=t|e*d    NB. (,-:<:)pq is four large primes, e medium

 
 
 

Unix version of "kbhit()"?

Post by S. Joel Ka » Thu, 05 Jan 1995 04:43:49



Quote:>I'm porting a DOS program to Linux, and I'm wondering how to port the
>kbhit() call? I tried out using select() on STDIN, but it only returns
>when I hit a RETURN key. I'd like to just poll the keyboard and see if
>a key has been hit while focused on my application. Any ideas?

        I believe you can do a non-blocking read and just see if you get
at least one character. Only problem is, you can't push the character
back, so you better hope the kbhit() call is followed immediately by a
getch() call.

        One fix if this isn't the case is to write a getch() function
that checks if a flag is set, and if the flag is set gives the stored
character and lowers the flag, otherwise it waits for one. Similarly,
kbhit(), if it gets a character, stores the character and raises the flag.

--

S. Joel Katz           Information on Objectivism, Linux, 8031s, and atheism

 
 
 

Unix version of "kbhit()"?

Post by Daniel Stephe » Thu, 05 Jan 1995 05:59:29




   >I'm porting a DOS program to Linux, and I'm wondering how to port the
   >kbhit() call? I tried out using select() on STDIN, but it only returns
   >when I hit a RETURN key. I'd like to just poll the keyboard and see if
   >a key has been hit while focused on my application. Any ideas?

           I believe you can do a non-blocking read and just see if you get
   at least one character. Only problem is, you can't push the character
   back, so you better hope the kbhit() call is followed immediately by a
   getch() call.

 Arrrgghh! I can't believe i'm seeing this discussion here!!

 You need to enable 'cbreak' mode... which tells the system to send you a key
press as you press it, rather than buffering them all up until a return is
detected.  Now, I always use curses when i'm working with the keyboard at
that level so I just call 'cbreak()'... However if you're not then you'll have
to take a look at the curses source code to see how it does it (I'm at work
so I can't look it up now, sorry)...

 Once you enable cbreak mode, your select method will work properly.

Daniel.

--
/-           Daniel Stephens            ---------------------#----------------\

| .signature still under development 8-)                    ### ##  # # 'LIFE'|
\-----------------------------------------------                 #  ##       -/

 
 
 

Unix version of "kbhit()"?

Post by Jim Nan » Thu, 05 Jan 1995 20:42:57


Pete Kruckenberg:
   I'm porting a DOS program to Linux, and I'm wondering how to port the
   kbhit() call? I tried out using select() on STDIN, but it only returns
   when I hit a RETURN key. I'd like to just poll the keyboard and see if
   a key has been hit while focused on my application. Any ideas?

Take a look at this.  You have to compile it like:

gcc file.c -lcurses -ltermcap

#include <stdlib.h>
#include <curses.h>

int main(int argc, char **argv)
{
        int letter;

        initscr();
        cbreak();
        noecho();

        while((letter=getch())!=EOF){
                printf("Letter = %c \n", letter);
        }

Quote:}

 
 
 

Unix version of "kbhit()"?

Post by Pete Kruckenbe » Thu, 05 Jan 1995 14:09:39


: I'm porting a DOS program to Linux, and I'm wondering how to port the
: kbhit() call? I tried out using select() on STDIN, but it only returns
: when I hit a RETURN key. I'd like to just poll the keyboard and see if
: a key has been hit while focused on my application. Any ideas?

Thanks to all of you who replied. Many suggested using the cbreak()
function of ncurses. Since I'm not using ncurses, that didn't help
much, but I'll save it for a time when I *do* use ncurses.

Anyways, here is the final code I came up with. I thought maybe
someone else might be able to use it. So, here you go. A little heavy
on the documentation, but I'm making up for other parts of Linux :)

Pete Kruckenberg

----------------------------------------------------------------------

/* kbhit(void) : returns 1 if a key has been struck, 0 otherwise

  Port of the kbhit() MS-DOS function to Linux (and maybe other OS's).
  Checks to see if there are any keystrokes available, without blocking.
  If any keystrokes are available, they should be retrieved with
  getchar() or a similar function (this function won't retrieve them).



  This is released into the public domain by the author, Pete Kruckenberg.
  There is no guarantee or warranty, implied or otherwise, as to its
  proper functionality. The author shall not be liable in any manner for
  this code.
*/

#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <termios.h>

int kbhit(void) {
  /* return 0 for no key pressed, 1 for key pressed */
  int return_value = 0;

  /* variables to store the current tty state, create a new one */
  struct termios origtty, tty;
  /* time struct for the select() function, to only wait a little while */
  struct timeval select_time;
  /* file descriptor variable for the select() call */
  fd_set readset;

  /* we're only interested in STDIN */
  FD_ZERO(&readset);
  FD_SET(STDIN_FILENO, &readset);

  /* store the current tty settings */
  tcgetattr(0, &origtty);
  /* flush the tty */
  tcflush(0, TCIOFLUSH);

  /* start with the current settings */
  tty = origtty;
  /* make modifications to put it in raw mode, turn off echo */
  tty.c_iflag &= ~(IGNCR|IUCLC|ICRNL|INLCR);
  tty.c_oflag &= ~(ONLCR|OPOST|ONLCR|OCRNL|ONLRET|OLCUC);
  tty.c_lflag &= ~(ISIG|ICANON|XCASE|ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT| \
                   ECHOCTL|ECHOKE);
  tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 0;
  /* put the settings into effect */
  tcsetattr(0, TCSADRAIN, &tty);

  /* how long to block for - this must be > 0.0, but could be changed
     to some other setting. 10-18msec seems to work well and only
     minimally load the system (0% CPU loading) */
  select_time.tv_sec = 0;
  select_time.tv_usec = 10;  

  /* is there a keystroke there? */
  if (select(1, &readset, NULL, NULL, &select_time))
    /* yes, remember it */
    return_value = 1;

  /* reset the tty to its original settings */
  tcsetattr(0, TCSADRAIN, &origtty);

  /* return with what we found out */
  return return_value;

Quote:}

 
 
 

Unix version of "kbhit()"?

Post by Grant Edwar » Sat, 07 Jan 1995 07:56:45




: >I'm porting a DOS program to Linux, and I'm wondering how to port the
: >kbhit() call? I tried out using select() on STDIN, but it only returns
: >when I hit a RETURN key. I'd like to just poll the keyboard and see if
: >a key has been hit while focused on my application. Any ideas?

:       I believe you can do a non-blocking read and just see if you get
: at least one character. Only problem is, you can't push the character
: back, so you better hope the kbhit() call is followed immediately by a
: getch() call.

:       One fix if this isn't the case is to write a getch() function
: that checks if a flag is set, and if the flag is set gives the stored
: character and lowers the flag, otherwise it waits for one. Similarly,
: kbhit(), if it gets a character, stores the character and raises the flag.

[aside: this a general Unix programming question, and doesn't really belong
in a Linux newsgroup...]

I did what Joel describes (sort of) a while back -- except my_getch()
won't wait for a character if there isn't one waiting.  This means
that you _must_ call kbhit() before calling my_getch().  It would be
quite simple to make it wait for a character if there wasn't one
waiting as a result of the previous call to kbhit().

The following is SVR4 code, so you may need to make small adjustments
for Linux:

----------------------------------------------------------------------
#include <termios.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>

struct termios oldTermState;

void restoreTerminal(void)
  {
  tcsetattr(0,TCSANOW,&oldTermState);
  }

void initTerminal(void)
  {
  struct termios newTermState;
  tcgetattr(0,&oldTermState);

  /* set raw mode for keyboard input */
  newTermState = oldTermState;
  newTermState.c_lflag &= ~ICANON;
  newTermState.c_lflag &= ~ECHO;
  newTermState.c_cc[VMIN] = 1;
  newTermState.c_cc[VTIME] = 0;

  tcsetattr(0,TCSANOW,&newTermState);
  atexit(restoreTerminal);

  /* set non-blocking i/o so we can poll keyboard */
  fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);

  /* set unbuffered output */
  setbuf(stdout,NULL);
  }

char keyvalue;
int keyvaluePresent;

int my_getch(void)
  {
  if (keyvaluePresent)
    {
    keyvaluePresent = 0;
    return keyvalue;
    }
  else
    return 0;
  }

int kbhit(void)
  {
  return (keyvaluePresent || (keyvaluePresent =  (read(0,&keyvalue,1) == 1)));
  }

----------------------------------------------------------------------

--
Grant Edwards        | Microsoft isn't the   | Yow!  Well, O.K.  I'll
Rosemount Inc.       | answer. Microsoft     | compromise with my principles
                     | is the question, and  | because of EXISTENTIAL

 
 
 

Unix version of "kbhit()"?

Post by roo » Thu, 12 Jan 1995 05:02:02


Hi Pete,

SVGALib has a kbhit() - like function called vga_getkey(). It returns
0 if no key was hit and the keycode (or something like that) if it
was. You don't need to be in SVGA mode to use it.

Also, there was a thread on something called 'raw keyboard mode' around
here sometime ago (as far as my rusty brain can remember).

Hope it helps. If you need pointers to SVGALib, mail me.

Leander Conradie        Dept of Computer Science

--------------------------------------------------------------------------
If your'e not programming functionally, your'e programming dysfunctionally

 
 
 

Unix version of "kbhit()"?

Post by David LeBla » Thu, 12 Jan 1995 10:45:03



>Hi Pete,
>SVGALib has a kbhit() - like function called vga_getkey(). It returns
>0 if no key was hit and the keycode (or something like that) if it
>was. You don't need to be in SVGA mode to use it.
>Also, there was a thread on something called 'raw keyboard mode' around
>here sometime ago (as far as my rusty brain can remember).
>Hope it helps. If you need pointers to SVGALib, mail me.
>Leander Conradie    Dept of Computer Science

>--------------------------------------------------------------------------
>If your'e not programming functionally, your'e programming dysfunctionally

How about:

This program clears the ICANON and ECHO flags, and sets 'read' to accept
one character at a time. You can change 'c_cc[VTIME]' to any number which
is in 0.1 seconds, which will set a timeout for the read. Eg, you can
call KBHIT and it returns (-1) if the timeout occurs.

Disclaimer: It works on the SUN here, and on the ultrix boxes, so if it
            does not work in linux, that linux's fault. :-)

#include <sys/termio.h>

set_raw(t)
int t;
{
        static struct termio tty ;
        struct termio tmp ;

        if (t) {
                ioctl(0,TCGETA,&tty) ;
                tmp = tty ;
                tmp.c_lflag &= ~(ICANON|ECHO) ;
                tmp.c_cc[VMIN] = 1 ;
                tmp.c_cc[VTIME] = 0 ;
                ioctl(0,TCSETA,&tmp) ;
        }
        else
                ioctl(0,TCSETA,&tty) ;

Quote:}

char
kbhit() {
        char tmp ;
        if (read(0,&tmp,1) < 1)
           return -1 ;
        return tmp ;

Quote:}

main() {
        char c ;
        set_raw(1) ;
        do {
                c = kbhit() ;
                printf("Key hit = %c\n",c) ;
        } while (c != 'q') ;
        set_raw(0) ;
Quote:}

--

David Le BLanc                          | CSIRO Div. Exploration and Mining

 
 
 

1. Unix version of "kbhit()"

There are several ways to do this.

o use select() system call.

It should have worked. There is a timeout argument, a pointer to a timeval
structure, which as to be initialised properly.

#include <sys/types.h>
#include <sys/time.h>

int kbhit()
{
fd_set readfs;
struct timeval *timeout;

FD_ZERO(&readfs);
FD_SET(0, &readfs);

timeout.tv_sec = 0;
timeout.tv_usec = 0;

select(1, &readfs, NULL, NULL, NULL, &timeout);

return FD_ISSET(0, &readfs);

I am not sure, but this should work.

Another way to poll the keyboard is to set O_NBLOCK flag on stdin:

#include <fcntl.h>

char readkbd()
{
char buff;

fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);

if (read(0, &buff, 1) <= 0)
  buff = 0;

fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK);

return buff;

I prefer to reset blocking mode after each call, so that if the program
is interrupted, stdin remains in a valid state (the shell does not appreciate
a stdin in non blocking mode). But it should work also if you set non blocking
mode at the beginning of the program, and reset it before leaving (it should
be a little faster, but not much, fcntl() calls are not very time consuming).

Hope this helps.

Bruno.

2. IDE CDW working! but now CDROM won't mount

3. GETSERVBYNAME()????????????????????"""""""""""""

4. Let's all try to get along

5. """"""""My SoundBlast 16 pnp isn't up yet""""""""""""

6. Fix for hung procs on modem lines?

7. "Novell-like","non-TCP/IP","networking" OS to place Unix

8. X under Windows95,98 or NT?

9. "kbhit()" like function on UNIX

10. Need to move "unix.old" to "unix"!!!

11. Is it "UNIX" or "Unix"?

12. Type "(", ")" and "{", "}" in X...

13. Why the "x" in the words "Unix", "Linux"...?