::[ PART 08 - KEYBOARD HANDLING ]::[ Source ]::[ Printable Version ]::
::[ DISCLAIMER ]::
I will hold no responsibility to whatever happens to you, your computer, your sanity, your pet, or whatever that may happen to your existence for your reading these texts and for the outcome of the various source code(s) given in each tutorial. So in short, read at your own risk!

 
::[ INTRODUCTION ]::
In this tutorial, I'll discuss how to grab keyboard inputs as well as show you guys how to disable the Alt-F4 key-combination in your program. One of its use you probably know already (ahem, games...:) ).

This is one of the "more important" tutorials that I shall be discussing. The reason? Obviously, unless you're creating a small demo program that doesn't require user input, then you'd best learn handling inputs well...;).

SideNote: To those of you who think that learning this would not benefit you since you'll be learning or have learned (somehow) DirectX's DirectInput, well, it might surprise you guys that the performance of what will be discussed here is equivalent of that of DirectInput. What's good about DirectInput, however, is its support for joystick, force-feedback devices, and its action-mapping concepts, but for pure mouse and/or keyboard handling, DirectInput performs equally well with what will be discussed here and in Part 9 of my tutorials...:).

 
::[ TUTORIAL ]::
Let me start by telling you that there are two ways that you could detect (or read) keyboard inputs (I'm not sure if there ARE only two ways, I just know only two...hehe...:) ).

The first one will be through the WM_KEYDOWN or WM_KEYUP messages for non-system keystrokes and the WM_SYSKEYDOWN and WM_SYSKEYUP for system keystrokes.

(Another message, WM_CHAR, will be discussed further later...;) )

Simply put, system keystrokes are the key-combinations where MS Windows has more priority (such as Alt-Tab and Alt-Esc). The WM_SYSKEYDOWN and WM_SYSKEYUP are usually sent to our window's WndProc for keys typed in combination with the Alt key.

The WM_KEYDOWN and WM_KEYUP messages, on the other hand, are usually generated for key combinations WITHOUT the Alt key. You'd usually get inputs through WM_KEYDOWN and WM_KEYUP.

You should know by now that all window messages are handled inside a window's WndProc (i.e. you handle messages inside your window's event handler).

Now, for the four messages mentioned above, the wParam parameter of WndProc will hold the virtual key code (whooooo...:) ) of the keyboard input.

I can just hear some of you saying: "What the heck is this guy talking about? Virtual what??!??!...Curses! This is going to be difficult isn't it?!?"...

Err...no, it's not going to be difficult, in fact, it's too darn simple...don't believe me? Read on then...:).

As stated above, for the four messages introduced above, the wParam in our window's WndPoc will hold the virtual key code...simply put, virtual key codes are "codenames" for the keys that you see in your, well, keyboard! =)!

Let's have some examples:

      VK_BACK         - Backspace Key
      VK_RETURN       - Enter Key (either one, if you have two)
      VK_SHIFT        - Shift Keys (either one also)
      VK_CAPITAL      - Caps Lock Key
      VK_PRIOR        - Page Up Key
Obviously, this means that if the user presses the Enter key for example, the wParam in our window's WndProc will have the value VK_RETURN.

"Ahh...I get it now!" - Previously Confused Reader

Yup, I knew you would...;).

OK, so what we need now is know the virtual key codes that we could grab from our wParam.

Here's a list that I prepared just for you (yeah, I know, I'm very nice...:) ):

  //----8<----[ LIST BEGIN ]--------/
    Decimal   Hex   Virtual Key Code    Key Counterpart
    -------   ---   ----------------    ---------------
      8       08     VK_BACK             Backspace
      9       09     VK_TAB              Tab
      13      0D     VK_RETURN           Enter (either one)
      16      10     VK_SHIFT            Shift (either one)
      17      11     VK_CONTROL          Ctrl (either one)
      18      12     VK_MENU             Alt (either one)
      19      13     VK_PAUSE            Pause
      20      14     VK_ESCAPE           Esc
      32      20     VK_SPACE            Spacebar
      33      21     VK_PRIOR            Page Up
      34      22     VK_NEXT             Page Down
      35      23     VK_END              End
      36      24     VK_HOME             Home
      37      25     VK_LEFT             Left Arrow
      38      26     VK_UP               Up Arrow
      39      27     VK_RIGHT            Right Arrow
      40      28     VK_DOWN             Down Arrow
      44      2C     VK_SNAPSHOT         Print Screen
      45      2D     VK_INSERT           Insert
      46      2E     VK_DELETE           Delete
      48-57   30-39  None                0 to 9 on main keyboard
      65-90   41-5A  None                A to Z
      96-105  60-69  VK_NUMPAD0 to VK_NUMPAD9  
                                         Numeric Keypad 0 to 9 w/ Num Lock ON
      106     6A     VK_MULTIPLY         Numeric Keypad *
      107     6B     VK_ADD              Numeric Keypad + 
      109     6D     VK_SUBTRACT         Numeric Keypad -
      110     6E     VK_DECIMAL          Numeric Keypad .
      111     6F     VK_DIVIDE           Numeric Keypad /
      112-121 70-79  VK_F1 to VK_F10     Function Keys F1 to F10
      122-135 7A-87  VK_F11 to VK_F24    Function Keys F11 to F24
      144     90     VK_NUMLOCK          Num Lock
      145     91     VK_SCROLL           Scroll Lock
  //----8<----[ LIST END   ]--------//

Wow! What a long list! And to think I removed some keycodes already...:). (Don't worry, those I've removed aren't really used or found in our normal QWERTY keyboard anyway, so it's OK...:) )

Notice that we do not have virtual key codes for 0 to 9 and A to Z...notice also that 'A' and 'a' have the same decimal / hexadecimal value...well, remember about my very brief mention of WM_CHAR above? Well, we use this message to grab characters 0 to 9 and A to Z (regardless if they are in uppercase or lowercase...;) ).

Now look at the code below:

  //----8<----[ CODE BEGIN ]--------//
    while(GetMessage(&msg, hWindow, 0,0))  // keep on getting msgs
    { TranslateMessage(&msg);              // convert to "known" msg
      DispatchMessage(&msg);               // send to msg queue
    }
  //----8<----[ CODE END   ]--------//

Familiar? It should be, it's where you keep on grabbing messages...it's usually located in your window's WinMain().

Notice the TranslateMessage() function, this function helps us in grabbing character input by "converting" keyboard character input into ASCII characters...this means that when the message is dispatched (through the DispatchMessage()), the wParam of our window's WndProc will contain the ASCII equivalent of our character input.

Your WndProc will look something along the lines of:

  //----8<----[ CODE BEGIN ]--------//
    LRESULT CALLBACK WndProc(HWND hWnd,
                             UINT message, 
                             WPARAM wParam, 
                             LPARAM lParam)
    { 
      switch(message)
      { 
        case WM_CHAR:              // received a character message
          switch(wParam)           // check what was pressed through wParam
          {
            case 'a':
            case 'A':
              // ...
              // Place your code here in response to keyboard input A or a
              // ...
            break;

            case 'b':
            case 'B':
              // ...
              // Place your code here in response to keyboard input B or b
              // ...
            break;

            // ...and so on...
          }
        break;                     // break from WM_CHAR message

        case WM_KEYUP:
          switch(wParam)
          {
            case VK_F1:            // user has pressed and released the F1 key
              // ...
              // Place your code here in response to keyboard input F1
              // ...
              break;

            // ...and so on...
          }
        break;                     // break from WM_KEYUP message

      // ...and so on...
  //----8<----[ CODE END   ]--------//

Now you might be wond'rin' why there's a decimal and hex value counterpart for each virtual keycodes...well, again, messages are passed onto our window's WndProc (specifically, the wParam parameter), and well, wParam means that it's a word (the "w" in "wParam" gave it away...:) ), and a word accepts an integer best...which means all of the virtual keycodes are actually #define'd elsewhere with an equivalent hexadecimal value (I'm not sure with other compilers, but with Visual C++ 6.0, it's in the WINUSER.H file, under the INCLUDE directory...;) ).

This means you could use these decimal and hex values instead of the pre-defined virtual keycodes made by Microsoft...(this also means you could #define your own virtual key code and give each key your own name...;) ).

To summarize so far:

  • WM_CHAR messages are for character inputs (by 'character', I mean, all the characters that you can type on your favorite text editor...;) )
  • WM_KEYDOWN and WM_KEYUP are for non-system keystrokes. It is up to you if you want your program to respond while the key is pressed down (WM_KEYDOWN) or when the key is released (WM_KEYUP)
  • WM_SYSKEYDOWN and WM_SYSKEYUP are for system keystrokes. Again, it is up to you if you want your program to respond while the key is pressed down (WM_SYSKEYDOWN) or when the key is released (WM_SYSKEYUP)
 
"OK...I'm confused...I have, say VK_MULTIPLY which I can grab with the WM_KEYDOWN or WM_KEYUP message, but I can also just grab the equivalent '*' character through the WM_CHAR message...which should I use?"
-- Newly Perplexed Reader

Actually, it is up to you...just let your program react correctly when the user presses the asterisk key...;).

OK, we're half way through this tutorial...tired? I hope not...I'm the one doing the typing here...=).

Look at the NOMAD.SOURCE Section below to find out how to handle keyboard inputs through window messages.

Now, the second and simpler method (which I prefer) is:

  //----8<----[ CODE BEGIN ]--------//
    if (GetKeyState(VIRTUAL_KEY_CODE_HERE) & 0x80)
    {
      // Do stuffs here corresponding to what
      // key the user has pressed
    }
  //----8<----[ CODE END   ]--------//

The above code allows you to grab keyboard inputs from anywhere in your code and not just your window's WndProc (nice huh? -- just replace "VIRTUAL_KEY_CODE_HERE" in the code above with any virtual key code)...;).

The only thing that should baffle you with the above code is the introduction of GetKeyState()...simply put, GetKeyState() accepts a virtual key code (or any of its decimal and hexadecimal value equivalent) and returns the current state of the chosen virtual key code...we then simply use a BITWISE-AND (you do that with a single ampersand) with 0x80...

So why 0x80? Well, 0x80 is 10000000 in binary (0x80 is in hexadecimal -- just in case you don't know yet...:) ). This means we check the first bit of the current key state returned by GetKeyState()...if the first bit is 1, then the user is/has pressing/pressed the key that we want to grab...simple huh?

(Microsoft Windows provides a handy Calculator together with its operating system...it is there where I converted the 0x80 to 10000000 -- a handy tool...just thought you should know...;) )

IMPORTANT: Note that VK_SHIFT (Shift Key), VK_CONTROL (Ctrl Key), and VK_MENU (Alt Key) can only be used with the GetKeyState() function...they CANNOT be detected with any window message.

Now the following paragraphs will have nothing to do with keyboard handling...but I must introduce it to you nonetheless since it is used in the provided source code below (under the NOMAD.SOURCE Section).

When you read the source code below, you will see a function named MessageBox()...what this does is pop up a, well, message box (you know, those box that alert you with some info like "Do you want to overwrite the file?", etc.)...the prototype of this function is:

    MessageBox(hWnd,text,title bar,MB_OK);

The first parameter accepts a handle to a window (but you can just put 'NULL'), the next is the message in your message box, then the words to show in the message box's title bar, and lastly, MB_OK will put an "OK" button for the user to close the message box...simple! =).

Also, previously, we used GetMessage() to grab our window's messages...we change that now with PeekMessage(). Don't worry, both of 'em work in almost the same way...just read the code...;).

Check out the NOMAD.SOURCE Section below to see how this all fits together (don't worry, it's heavily commented...;) ).

At last, we're done with handling keyboard through the Win32 API...mouse handling, here we come!

(Oh yeah, note that in the NOMAD.SOURCE Section below, I also show how to disable the Alt-F4 keystroke...VERY useful in games...;))

 
::[ ENDING ]::
And that's that! Not hard eh? If there is anything that's not clear to you, your mind, or to that part of you that lets you comprehend the things here, you may email me at:

Part 9 will be the mouse's turn...:).

Briefly, we'll deal with new messages (again). These messages are (duh Willie!) for handling the mouse... so watch out for Part 9.

 
::[ REFERENCE ]::
I want to recommend the following site to you:

This is a site where you'll see guys creating a 3D game named Era (the Era engine was originally intended for their remake of the classic game Ultima 1, but due to some issues, they had to let that idea go). This site has some tutorials (from DOS graphics, to Win32, to collision detection!) worth checking out.

 
::[ NOMAD.SOURCE ]::

  gfxWINAPI3.c - Main source file
This tutorial was written on: 08.20.2002 | Updated: 09.06.2002
:.Site.:.Menu.:
:// .site..news.
:// .tutorials..1.
:// .tutorials..2.
:// .projects.
:// .forums.
:// .gbook.
:// .links.