Looking for a Keyboard Hit

The C language is famously platform independent. This feature may seem unimportant these days, but back in the early computer era having a language you code code on multiple systems was key to the C language’s success. This benefit may be one reason why C lacks a specific function to check on the keyboard status to determine whether a key has been pressed. Such a function is hardware-dependent.

Even so, C does have a function that lets you plumb the depths of a specific piece of hardware. It’s the mysterious and often feared function ioctl(). Rather than struggle to pronounce it, consider it as the Input/Output Control function.

I’ve written about the ioctl() function before, specifically with regards to counting the number of lines and columns on the terminal window. The function gathers information about a file descriptor. Like the terminal, standard input is also a file descriptor.

One of the aspects of the standard input file descriptor is counting the number of characters waiting in the input queue. In other words, keyboard input that has yet to be read. (And, yes, I know that standard input can also come from another source, but this Lesson is focusing on the keyboard.)

The specific statement is:

ioctl(STDIN_FILENO,FIONREAD,&k);

The ioctl() function reads file descriptor zero for standard input using the STDIN_FILENO defined constant (unistd.h). This constant is a file descriptor data type; the stdin constant for standard input is a FILE* data type, not a file descriptor. The FIONREAD defined constant represents input characters waiting to be read. The final argument is the address of an integer to hold the count of input characters in the queue.

Traditionally in C programming, the function that checks on waiting characters for the keyboard is named kbhit(). This name was used in the old Borland TurboC programming language, and I believe it’s used in C++.

The following code updates the busy-loop presented in last week’s Lesson, adding the kbhit() function to keep the loop spinning while still checking on a key press.

2024_03_30-Lesson.c

#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>

int kbhit(void)
{
    int k;

    ioctl(STDIN_FILENO,FIONREAD,&k);

    return(k);
}

int main()
{
    char a;

    a = 'A';
    while(1)
    {
        printf(" %c",a++);
        if( a=='Z' )
            a = 'A';
        /* terminate on key */
        if( kbhit() )
        {
            getchar();
            break;
        }
    }

    return 0;
}

The kbhit() function reads the status of the standard input device, which can be assumed to be the keyboard. The number of characters waiting to be read is stored in variable k and returned.

In the main() function, the while loop outputs letters of the alphabet repeatedly from A through Z. The kbhit() function is called in an if test. When the function returns zero (meaning no key has been pressed), the if test fails and the loop continues.

When a key is waiting, the kbhit() function returns a TRUE value. The value indicates the number of characters to read, but that’s not important. Upon triggering a TRUE condition for the if statement, a character is read and the loop breaks. The program ends.

For sample output, the program spews the alphabet until you tap a key on the keyboard. then the keyboard’s input queue is read. Or, more specifically, the standard input queue is read.

Network programming also involves polling for input. Open network sockets are listened to similar to the way this lesson’s program listens to standard input. In next week’s Lesson, I continue my exploration of polling techniques by apply a technique used to read sockets for pending network input to the sample keyboard input program.

6 thoughts on “Looking for a Keyboard Hit

  1. Ah, yes, I remember Borland C++… as well as kbhit(), getch() and friends ☺

    If (n)curses is an option, then the following could be an alternative:

    #if defined(_WIN32)
      #include   /* _kbhit(), _getch() */
    #elif defined(__linux__)
      #include  /* initscr(), getch(), endwin() */
    #endif

    int kbhit (void)
    {
      #if defined(_WIN32)
      return (_kbhit ());

      #elif defined(__linux__)
      int ch = getch();

      if (ch != ERR)
        ungetch(ch);

      return (ch != ERR);
      #endif
    }

    However, I think the presented ioctl actually is a better (and nicer) solution… not least because it introduces one less dependency.

  2. I use Ncurses when I must write some interactive text mode program. It’s awesome! Otherwise, I stick to stream I/O in the spirit of the language.

    Also: It’s amazing how poorly documented the ioctl() function is! I’m sure there’s a lot of power there.

  3. I agree completely—coming by good information on ioctl() always seems harder than it should be! (At least if someone uses them as “regularly” as I do.)

  4. Ah, yes… thanks for reminding me of that!

    Just ordered it on Amazon and am excited to see what Dan has written on the subject.

Leave a Reply