Handling Events

We've done a lot of work with the wHnd we found for writing to the console — how can we read from it, using our rHnd handle? The answer is that it is in fact very easy! We need to use two function calls, GetNumberOfConsoleInputEvents(...) to find out how many console input events have happened — and then ReadConsoleInput(...) to read them into a buffer so we can finally cycle through each event and decide what to do with it.

GetNumberOfConsoleInputEvents(...) requires two arguments — a HANDLE to the console input handle, and a pointer to a DWORD that will store the number of events that really happened. Once we have done that, and if the number of events that happened was not zero, we create an array of INPUT_RECORDs big enough to hold all the events, then use ReadConsoleInput(...) to copy the events into the buffer. ReadConsoleInput(...) takes four arguments:

  • Read handle for the console window;
  • Pointer to the buffer to store the INPUT_RECORD data for each event to. An INPUT_RECORD is made up of two properties — .EventType, which holds the type of event (was it a key press? Did the mouse move?) and .Event, which holds the specific information for that particular event (was the key pressed or released, and which key was it? Where did the mouse move to?)
  • How many events we need to read.
  • Pointer to a DWORD to save how many events were actually read.

Here is some code that illustrates the ideas above in motion. Note that for this sort of thing, an IDE capabale of intellisense (I am using Visual Studio) is very useful for working out what all the event types are...

#include <stdlib.h>
#include <stdio.h>
#include <Windows.h>
#include <Tchar.h>

HANDLE wHnd;    // Handle to write to the console.
HANDLE rHnd;    // Handle to read from the console.

int _tmain(int argc, _TCHAR* argv[]) {

    // ... code removed ...

    // Write the characters:
    WriteConsoleOutputA(wHnd, consoleBuffer, charBufSize, characterPos, &writeArea);

    // How many events have happened?
    DWORD numEvents = 0;

    // How many events have we read from the console?
    DWORD numEventsRead = 0;
    
    // Boolean flag to state whether app is running or not.
    bool appIsRunning = true;

    // If we set appIsRunning to false, the program will end!
    while (appIsRunning) {

        // Find out how many console events have happened:
        GetNumberOfConsoleInputEvents(rHnd, &numEvents);

        // If it's not zero (something happened...)
        if (numEvents != 0) {

            // Create a buffer of that size to store the events
            INPUT_RECORD *eventBuffer = new INPUT_RECORD[numEvents];

            // Read the console events into that buffer, and save how
            // many events have been read into numEventsRead.
            ReadConsoleInput(rHnd, eventBuffer, numEvents, &numEventsRead);

            // Now, cycle through all the events that have happened:
            for (DWORD i = 0; i < numEventsRead; ++i) {

                // Check the event type: was it a key?
                if (eventBuffer[i].EventType == KEY_EVENT) {

                    // Yes! Was the key code the escape key?
                    if (eventBuffer[i].Event.KeyEvent.wVirtualKeyCode==VK_ESCAPE) {

                        // Yes, it was, so set the appIsRunning to false.
                        appIsRunning = false;
                    }
                }
            }

            // Clean up our event buffer:
            delete[] eventBuffer;
        }
    }
}

All this does is find out how many events happened, allocates a buffer for them, reads them into it, then cycles through all the ones that was read, checking for a keypress, then checking for the escape key. Once it has done this, it deletes the event buffer and loops, providing appIsRunning is still true.

The available .EventTypes are:

  • KEY_EVENT — keyboard event;
  • MOUSE_EVENT — mouse event;
  • WINDOW_BUFFER_SIZE_EVENT — an event dealing with a resize of the window;
  • MENU_EVENT and FOCUS_EVENT — internal events which should be ignored.

The two event types we'll be concentrating on are KEY_EVENT and MOUSE_EVENT. The following information is lifted almost directly from MSDN.

Keyboard Events

The .Event structure contains the following members:

  • bKeyDown- If the key is pressed, this member is TRUE. Otherwise, this member is FALSE (the key is released).
  • wRepeatCount — Count indicating that a key is being held down. For example, when a key is held down, you might get five events with this member equal to 1, one event with this member equal to 5, or multiple events with this member greater than or equal to 1.
  • wVirtualKeyCode — Virtual-key code that identifies the given key in a device-independent manner.
  • wVirtualScanCode — Virtual scan code of the given key that represents the device-dependent value generated by the keyboard hardware.
  • uChar — Translated Unicode or ANSI character, depending on whether the wide-character (Unicode) or ANSI version of the ReadConsoleInput function was used (this depends on project/compiler settings).
  • dwControlKeyState — State of the control keys. This member can be one or more of the following values:
    • CAPSLOCK_ON — The CAPS LOCK light is on.
    • ENHANCED_KEY — The key is enhanced.
    • LEFT_ALT_PRESSED — The left ALT key is pressed.
    • LEFT_CTRL_PRESSED — The left CTRL key is pressed.
    • NUMLOCK_ON — The NUM LOCK light is on.
    • RIGHT_ALT_PRESSED — The right ALT key is pressed.
    • RIGHT_CTRL_PRESSED — The right CTRL key is pressed.
    • SCROLLLOCK_ON — The SCROLL LOCK light is on.
    • SHIFT_PRESSED — The SHIFT key is pressed.

Mouse Events

The .Event structure contains the following members:

  • dwMousePosition — A COORD structure that contains the location of the cursor, in terms of the console screen buffer's character-cell coordinates.
  • dwButtonState — Status of the mouse buttons. The least significant bit corresponds to the leftmost mouse button. The next least significant bit corresponds to the rightmost mouse button. The next bit indicates the next-to-leftmost mouse button. The bits then correspond left to right to the mouse buttons. A bit is 1 if the button was pressed. The following constants are defined for the first five mouse buttons:
    • FROM_LEFT_1ST_BUTTON_PRESSED
    • RIGHTMOST_BUTTON_PRESSED
    • FROM_LEFT_2ND_BUTTON_PRESSED
    • FROM_LEFT_3RD_BUTTON_PRESSED
    • FROM_LEFT_4TH_BUTTON_PRESSED
  • dwControlKeyState — State of the control keys. This member can be one or more of the following values.
    • RIGHT_ALT_PRESSED — The right ALT key is pressed.
    • LEFT_ALT_PRESSED — The left ALT key is pressed.
    • RIGHT_CTRL_PRESSED — The right CTRL key is pressed.
    • LEFT_CTRL_PRESSED — The left CTRL key is pressed.
    • CAPSLOCK_ON — The CAPS LOCK light is on.
    • ENHANCED_KEY — The key is enhanced.
    • NUMLOCK_ON — The NUM LOCK light is on.
    • SCROLLLOCK_ON — The SCROLL LOCK light is on.
    • SHIFT_PRESSED — The SHIFT key is pressed.
  • dwEventFlags — Type of mouse event. If this value is zero, it indicates a mouse button being pressed or released. Otherwise, this member is one of the following values.
    • DOUBLE_CLICK — The second click (button press) of a double-click occurred. The first click is returned as a regular button-press event.
    • MOUSE_MOVED — A change in mouse position occurred.
    • MOUSE_WHEELED — The mouse wheel was rolled.

PreviousNextShow All Index