Sometimes things aren't as simple as they should be. Most of the time I write code for embedded systems that don't come with things like screens and keyboards. That wasn't true a few days ago, when I realized that I didn't know how to make a C program sleep until a key is pressed. It sounds pretty easy, but it turns out there isn't a way to do it with ANSI C. If you don't believe me, read the FAQ.

I wanted something for a Linux system, and that can be done. After doing a lot of reading I came up with something that met my needs. Then on my way home from work, I thought about writing something more generic, and I came up with this. If you're in a hurry and just want code feel free to scroll down, copy it, and move along.

There are several websites discussing the problem and providing variations on the same theme. This web page discusses what I came up with and the design decisions that I made. If you have any interesting ideas, comments, or just happen to be board, feel free to contact me at mdipper@alumni.engr.ucsb.edu.


Objective

ANSI C provides a getchar function that basically returns the next character input from stdin. getchar is blocking, but the tread it's in sleeps. That's exactly what I wanted. The only problems are that getchar() always echos the key pressed and it normally won't recognize the input until the enter key is pressed.

I wanted to code that did not require the enter key to be pressed before the key press would be seen by stdin. I also did not want to echo the key press to stdout, and I did not need to know what key was pressed. All the key press needed to do is unblock an idle thread.

Sample Code

Below is a listing of a function that achieves all of my stated goals. It provides the option to enable/disable echoing the key to stdout and returns the value of character value of the key pressed in case you have a use for it.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/* sleep until a key is pressed and return value. echo = 0 disables key echo. */ int keypress(unsigned char echo) { struct termios savedState, newState; int c; if (-1 == tcgetattr(STDIN_FILENO, &savedState)) { return EOF; /* error on tcgetattr */ } newState = savedState; if ((echo = !echo)) /* yes i'm doing an assignment in an if clause */ { echo = ECHO; /* echo bit to disable echo */ } /* disable canonical input and disable echo. set minimal input to 1. */ newState.c_lflag &= ~(echo | ICANON); newState.c_cc[VMIN] = 1; if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &newState)) { return EOF; /* error on tcsetattr */ } c = getchar(); /* block (withot spinning) until we get a keypress */ /* restore the saved state */ if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &savedState)) { return EOF; /* error on tcsetattr */ } return c; }

How it Works

All of the magic required to change the way your terminal behaves lies in the functions tcgetattr and tcgetattr. These functions are responsible for getting and setting terminal attributes. For more information see http://linux.die.net/man/3/tcgetattr for the tcgetattr and tcgetattr man page.

Line 7 of my function gets the current stdin attributes ans saves it in the variable savedState.

Lines 12 through 21 compute a new set of terminal attributes for stdin that are just like the old ones, except canonical mode and possibly echo are disabled (Line 20).

Disabling canonical mode causes characters to be sent to stdin after a timeout or a minimum number have been received. Line 21 sets that minimum number to 1.

Line 23 applies the new set of attributes to stdin.

Line 28 uses getchar() to block until a character can be read from stdin. The value of the character is saved just in case it's required by the calling function.

Line 31 restores the attributes of stdin to their previous values.

Using the Code

Using the keypress() function is straight forward. Call it with a non-zero value if you want to allow the key press to be echoed. Calling keypress() with a value of zero will prevent the key press from being echoed.

keypress() returns the same integer value for the key pressed as getchar() or an EOF on error. The return value may be used or ignored as desired.

Required Header Files

The keypress() function requires items defined by studio.h, termios.h, and unistd.h. The following lines should be added to any program using keypress():


#include <stdio.h>
#include <termios.h>
#include <unistd.h>
    

Sample Calls


/* simply wait for a key to be pressed */
printf("press a key to continue\n");
keypress(0);

/* wait for the space bar */
printf("hit the spacebar to continue\n");
while (keypress(0) != ' ');
    

Home
Last updated on January 6, 2013