Breakout, the game the course builds in HiSoft C, running on the Tatung Einstein: coloured brick rows, the bat and the ball on a black court
← Back to Courses
module
22

Keys Without ENTER

Introduction

S14's rawin() waits for a key. A game cannot wait: the ball keeps moving whether or not the player does anything, and the loop has to ask "is a key down?" and carry on either way. The Einstein library's kbd() is that question, S12 used it to stop a loop, and this section finishes it: what it returns for every key the game will use, how to tell one press from a held key, and a habit to get into at the end of every program that uses it.

kbd()

c = kbd();

returns 0 if no key is down at this instant, and the key's code if one is. It asks the operating system's keyboard scan directly (S23: one inline call) and comes straight back, in about seven-tenths of a millisecond. It does not echo, and it does not wait.

Two things follow from "at this instant". First, a held key returns its code on every call: hold RIGHT down and a loop calling kbd() a thousand times a second sees RIGHT a thousand times, which is exactly what a bat wants - keep going while the key is down. Second, a tap can be missed if the loop is busy elsewhere when it happens, so a game loop calls kbd() once every time round and does its other work quickly.

To see each press once, wait for the key to go away again:

while ((c = kbd()) == 0)
    ;
printf("%d ", c);
while (kbd() != 0)
    ;

The first loop waits for a key, the second for its release. That is what the listing does.

The Codes

Each key gives one number, and the arrow keys give the characters they type at the prompt (S3):

KeyCodeAs a character
LEFT91[
RIGHT93]
UP94^
DOWN10line feed
SPACE32' '
ENTER13carriage return
A to Z65 to 90'A' to 'Z'
0 to 948 to 57'0' to '9'

Letters come as capitals when CAPS LOCK is on, which it is when the Einstein starts; a game that wants Q should test for 'Q' and 'q' both. rawin() returns the same codes. In the game, '[' and ']' are left and right, and ' ' will serve the ball.

The Keys You Looked At Are Still There

kbd() looks; it does not take. Every key the player presses during a kbd() loop also goes into the operating system's type-ahead buffer, where it waits for someone to read it - and when the program ends, that someone is the prompt. Run a kbd() program, press a dozen keys, and when 0: comes back it comes back with []^ after it and No File when you press ENTER, and the next command you type is prefixed with whatever is left.

The two ways of taking the keys out that you might reach for do not work here: calling rawin() after each kbd() hit went wrong, and a loop of keyhit() and rawin() never came back. What does work is the simplest thing: at the end of the program, ask for ENTER and read a line.

printf("Press ENTER to finish\n");
gets(line);

gets reads the buffer up to the ENTER, the stray keys and all, and the prompt is clean. Every game in this course ends that way, and so should every program of yours that calls kbd().

Wait, Or Look?

Two functions, then, for two jobs:

  • rawin() waits for a key and takes it. For "press any key", for a menu, for the end of a game. Nothing runs while it waits.
  • kbd() looks and comes back. For a loop that must keep running. It leaves the key behind, so the program drains the buffer before it exits.

The game loop uses kbd() every tick, and rawin() at the start and the end.

The Code

#include STDIO.H

main()
{
    int c;
    char line[40];
    printf("Press keys; Q to stop\n");
    for (;;) {
        while ((c = kbd()) == 0)
            ;
        printf("%d ", c);
        if (c == 'Q')
            break;
        while (kbd() != 0)
            ;
    }
    printf("\nPress ENTER to finish\n");
    gets(line);
    printf("finished\n");
}

#include ?EIN.LIB?
#include ?STDIO.LIB?

Starting from

Typed as KEYS.C; compiled and run. Press LEFT, RIGHT, A, then Q; then ENTER.

What you should see

Press keys; Q to stop
91 93 65 81
Press ENTER to finish
[
finished
0:

The [ on its own line is the buffered keys being read by gets and echoed as it goes. The prompt after finished is clean: type DIR and it works.

Change One Thing

  • Take out the while (kbd() != 0) loop and its ;, and hold a key down for a moment when you run it. What is printed, and how many times?
  • Take out the gets(line) line and the printf before it. Run it, press the same keys, and see what the prompt says afterwards. Press ENTER until it is clean.
  • Change rawin for kbd in S14's ASK4.C - c = kbd(); - and run it without pressing anything. What is printed?

Exercises

22.1 Print left or right each time an arrow key is pressed, and stop on Q.

22.2 Count how many times round a loop the program goes while you hold SPACE down for about a second, and print the count when you let go.

22.3 Write waitkey() that waits for any key and returns it without echo, using kbd() twice - once for the press and once for the release - and use it in place of rawin() in S24's SCREEN.C. Does the prompt need draining afterwards?

Worked solutions are in Appendix II.

When It Goes Wrong

SymptomCause
The prompt comes back with letters after it, or No FileKeys pressed during kbd() were left in the buffer. End with gets.
A tap prints many timesThe key was still down on the next call. Wait for release.
A tap is never seenThe loop was busy when it happened. Call kbd() more often.
Q does not stop the programCAPS LOCK is off and the key sent q. Test both.
The program stops dead at kbd()It is rawin(), which waits.
undefined symbol kbdNo #include ?EIN.LIB?.

Summary

kbd() returns the key that is down now, or 0, without waiting or taking it; rawin() waits and takes. Arrows are [ ] ^ and 10, SPACE 32, ENTER 13, letters as capitals. A held key repeats; wait for release to see one press. Keys kbd() saw wait in the buffer for the prompt: end with Press ENTER and gets.

Next

S23, Reading A Real Program: EIN.LIB - the library these calls come from, read from the top: what a library is, how it reaches the machine, and a bug in it.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.