Home > Articles > Programming > Games

This chapter is from the book

Handling Important Events

As you've been painfully learning, Windows is an event-based operating system. Responding to events is one of the most important aspects of a standard Windows program. This next section covers some of the more important events that have to do with window manipulation, input devices, and timing. If you can handle these basic events, you'll have more than you need in your Windows arsenal to handle anything that might come up as part of a DirectX game, which itself relies very little on events and the Windows operating system.

Window Manipulation

There are a number of messages that Windows sends to notify you that the user has manipulated your window. Table 3.4 contains a small list of some of the more interesting manipulation messages that Windows generates.

Table 3.4 Window Manipulation Messages

Value

Description

WM_ACTIVATE

Sent when a window is being activated or deactivated. This message is sent first to the window procedure of the top-level window being deactivated. It is then sent to the window procedure of the top-level window being activated.

WM_ACTIVATEAPP

Sent when a window belonging to an application other than the active window is about to be activated. The message is sent both to the application whose window is being activated and to the application whose window is being deactivated.

WM_CLOSE

Sent as a signal that a window or an application should terminate.

WM_MOVE

Sent after a window has been moved.

WM_MOVING

Sent to a window that the user is moving. By processing this message, an application can monitor the size and position of the drag rectangle and, if needed, change its size or position.

WM_SIZE

Sent to a window after its size has changed.

WM_SIZING

Sent to a window that the user is resizing. By processing this message, an application can monitor the size and position of the resizing rectangle and, if needed, change its size or position.


Let's take a look at WM_ACTIVATE, WM_CLOSE, WM_SIZE, and WM_MOVE and what they do. For each one of these messages, I'm going to list the message, wparam, lparam, and some comments, along with a short example WinProc() handler for the event.

Message: WM_ACTIVATE

Parameterization:

fActive   = LOWORD(wParam);    // activation flag
fMinimized  = (BOOL)HIWORD(wParam); // minimized flag
hwndPrevious = (HWND)lParam;     // window handle

The fActive parameter basically defines what is happening to the window—that is, is the window being activated or deactivated? This information is stored in the low-order word of wparam and can take on the values shown in Table 3.5.

Table 3.5 The Activation Flags for WM_ACTIVATE

Value

Description

WA_CLICKACTIVE

Activated by a mouse click.

WA_ACTIVE

The window has been activated by some means other than the mouse, such as the keyboard interface.

WA_INACTIVE

The window is being deactivated.


The fMinimized variable simply indicates if the window was minimized. This is true if the variable is nonzero. Lastly, the hwndPrevious value identifies the window being activated or deactivated, depending on the value of the fActive parameter. If the value of fActive is WA_INACTIVE, hwndPrevious is the handle of the window being activated. If the value of fActive is WA_ACTIVE or WA_CLICKACTIVE, hwndPrevious is the handle of the window being deactivated. This handle can be NULL. That makes sense, huh?

In essence, you use the WM_ACTIVATE message if you want to know when your application is being activated or deactivated. This might be useful if your application keeps track of every time the user Alt+Tabs away or selects another application with the mouse. On the other hand, when your application is reactivated, maybe you want to play a sound or do something. Whatever, it's up to you.

Here's how you code when your application is being activated in the main WinProc():

case WM_ACTIVATE:
{
// test if window is being activated
if (LOWORD(wparam)!=WA_INACTIVE)
  {
  // application is being activated
  } // end if
else
  {
  // application is being deactivated
  } // end else

} break;

Message: WM_CLOSE

Parameterization: None

The WM_CLOSE message is very cool. It is sent right before a WM_DESTROY and the following WM_QUIT are sent. The WM_CLOSE indicates that the user is trying to close your window. If you simply return(0) in your WinProc(), nothing will happen and the user won't be able to close your window! Take a look at DEMO3_7.CPP and the executable DEMO3_7.EXE to see this in action. Try killing the application—you won't be able to!

CAUTION

Don't panic when you can't kill DEMO3_7.EXE. Simply press Ctrl+Alt+Del, and the Task Manager will come up. Then select and terminate the DEMO3_7.EXE application. It will cease to exist—just like service at electronics stores starting with "F" in Silicon Valley.

Here's the coding of the empty WM_CLOSE handler in the WinProc() as coded in DEMO3_7.CPP:

case WM_CLOSE:
  {
  // kill message, so no further WM_DESTROY is sent
  return(0);
} break;

If making the user mad is your goal, the preceding code will do it. However, a better use of trapping the WM_CLOSE message might be to include a message box that confirms that the application is going to close or maybe do some housework. DEMO3_8.CPP and the executable take this route. When you try to close the window, a message box asks if you're certain. The logic flow for this is shown in Figure 3.20.

Figure 3.20Figure 3.20 The logic flow for WM_CLOSE.

Here's the code from DEMO3_8.CPP that processes the WM_CLOSE message:

case WM_CLOSE:
{
// display message box
int result = MessageBox(hwnd, 
  "Are you sure you want to close this application?",
       "WM_CLOSE Message Processor",
        MB_YESNO | MB_ICONQUESTION);

// does the user want to close?
if (result == IDYES)
  {
  // call default handler
  return (DefWindowProc(hwnd, msg, wparam, lparam));
  } // end if
else // throw message away
  return(0);

} break;

Cool, huh? Notice the call to the default message handler, DefWindowProc(). This occurs when the user answers Yes and you want the standard shutdown process to continue. If you knew how to, you could have sent a WM_DESTROY message instead, but since you haven't learned how to send messages yet, you just called the default handler. Either way is fine, though.

Next, let's take a look at the WM_SIZE message, which is an important message to process if you've written a windowed game and the user keeps resizing the view window!

Message: WM_SIZE

Parameterization:

fwSizeType = wParam;     // resizing flag
nWidth   = LOWORD(lParam); // width of client area
nHeight  = HIWORD(lParam); // height of client area

The fwSizeType flag indicates what kind of resizing just occurred, as shown in Table 3.6, and the low and high word of lParam indicate the new window client dimensions.

Table 3.6 Resizing Flags for WM_SIZE

Value

Description

SIZE_MAXHIDE

Message is sent to all pop-up windows when some other window is maximized.

SIZE_MAXIMIZED

Window has been maximized.

SIZE_MAXSHOW

Message is sent to all pop-up windows when some other window has been restored to its former size.

SIZE_MINIMIZED

Window has been minimized.

SIZE_RESTORED

Window has been resized, but neither the SIZE_MINIMIZED nor SIZE_MAXIMIZED value applies.


As I said, processing the WM_SIZE message can be very important for windowed games because when the window is resized, the graphics display must be scaled to fit. This will never happen if your game is running in full-screen, but in a windowed game, you can count on the user trying to make the window larger and smaller. When this happens, you must recenter the display and scale the universe or whatever to keep the image looking correct. As an example of tracking the WM_SIZE message, DEMO3_9.CPP prints out the new size of the window as it's resized. The code that tracks the WM_SIZE message in DEMO3_9.CPP is shown here:

case WM_SIZE:
     {
     // extract size info
     int width = LOWORD(lparam);
     int height = HIWORD(lparam);

     // get a graphics context
     hdc = GetDC(hwnd);

     // set the foreground color to green
     SetTextColor(hdc, RGB(0,255,0));

     // set the background color to black
     SetBkColor(hdc, RGB(0,0,0));

     // set the transparency mode to OPAQUE
     SetBkMode(hdc, OPAQUE);

     // draw the size of the window
     sprintf(buffer,
     "WM_SIZE Called - New Size = (%d,%d)", width, height);
     TextOut(hdc, 0,0, buffer, strlen(buffer));

     // release the dc back
     ReleaseDC(hwnd, hdc);

     } break;

CAUTION

You should know that the code for the WM_SIZE message handler has a potential problem: When a window is resized, not only is a WM_SIZE message sent, but a WM_PAINT message is sent as well! Therefore, if the WM_PAINT message was sent after the WM_SIZE, the code in WM_PAINT could erase the background and thus the information just printed in WM_SIZE. Luckily, this isn't the case, but it's a good example of problems that can occur when messages are out of order or when they aren't sent in the order you think they are.

Last, but not least, let's take a look at the WM_MOVE message. It's almost identical to WM_SIZE, but it is sent when a window is moved rather than resized. Here are the details:

Message: WM_MOVE

Parameterization:

xPos = (int) LOWORD(lParam); // new horizontal position in screen coords
yPos = (int) HIWORD(lParam); // new vertical position in screen coords

WM_MOVE is sent whenever a window is moved to a new position, as shown in Figure 3.21. However, the message is sent after the window has been moved, not during the movement in real time. If you want to track the exact pixel-by-pixel movement of a window, you need to process the WM_MOVING message. However, in most cases, processing stops until the user is done moving your window.

Figure 3.21Figure 3.21 Generation of the WM_MOVE message.

As an example of tracking the motion of a window, DEMO3_10.CPP and the associated executable DEMO3_10.EXE print out the new position of a window whenever it's moved. Here's the code that handles the WM_MOVE processing:

case WM_MOVE:
     {
     // extract the position
     int xpos = LOWORD(lparam);
     int ypos = HIWORD(lparam);

     // get a graphics context
     hdc = GetDC(hwnd);

     // set the foreground color to green
     SetTextColor(hdc, RGB(0,255,0));

     // set the background color to black
     SetBkColor(hdc, RGB(0,0,0));

     // set the transparency mode to OPAQUE
     SetBkMode(hdc, OPAQUE);

     // draw the size of the window
     sprintf(buffer,
    "WM_MOVE Called - New Position = (%d,%d)", xpos, ypos);
     TextOut(hdc, 0,0, buffer, strlen(buffer));

     // release the dc back
     ReleaseDC(hwnd, hdc);

     } break;

Well, that's it for window manipulation messages. There are a lot more, obviously, but you should have the hang of it now. The thing to remember is that there is a message for everything. If you want to track something, just look in the Win32 Help and sure enough, you'll find a message that works for you!

The next sections cover input devices so you can interact with the user (or yourself) and make much more interesting demos and experiments that will help you master Windows programming.

Banging on the Keyboard

Back in the old days, accessing the keyboard required sorcery. You had to write an interrupt handler, create a state table, and perform a number of other interesting feats to make it work. I'm a low-level programmer, but I can say without regret that I don't miss writing keyboard handlers anymore!

Ultimately you're going to use DirectInput to access the keyboard, mouse, joystick, and any other input devices. Nevertheless, you still need to learn how to use the Win32 library to access the keyboard and mouse. If for nothing else, you'll need them to respond to GUI interactions and/or to create more engaging demos throughout the book until we cover DirectInput. So without further ado, let's see how the keyboard works.

The keyboard consists of a number of keys, a microcontroller, and support electronics. When you press a key or keys on the keyboard, a serial stream of packets is sent to Windows describing the key(s) that you pressed. Windows then processes this stream and sends your window keyboard event messages. The beauty is that under Windows, you can access the keyboard messages in a number of ways:

  • With the WM_CHAR message

  • With the WM_KEYDOWN and WM_KEYUP messages

  • With a call to GetAsyncKeyState()

Each one of these methods works in a slightly different manner. The WM_CHAR and WM_KEYDOWN messages are generated by Windows whenever a keyboard keypress or event occurs. However, there is a difference between the types of information encapsulated in the two messages. When you press a key on the keyboard, such as A, two pieces of data are generated:

  • The scan code

  • The ASCII code

The scan code is a unique code that is assigned to each key of the keyboard and has nothing to do with ASCII. In many cases, you just want to know if the A key was pressed; you're not interested in whether or not the Shift key was held down and so on. Basically, you just want to use the keyboard like a set of momentary switches. This is accomplished by using scan codes. The WM_KEYDOWN message is responsible for generating scan codes when keys are pressed.

The ASCII code, on the other hand, is cooked data. This means that if you press the A key on the keyboard but the Shift key is not pressed or the Caps Lock key is not engaged, you see an a character. Similarly, if you press Shift+A, you see an A. The WM_CHAR message sends these kinds of messages.

You can use either technique—it's up to you. For example, if you were writing a word processor, you would probably want to use the WM_CHAR message because the character case matters and you want ASCII codes, not virtual scan codes. On the other hand, if you're making a game and F is fire, S is thrust, and the Shift key is the shields, who cares what the ASCII code is? You just want to know if a particular button on the keyboard is up or down.

The final method of reading the keyboard is to use the Win32 function GetAsyncKeyState(), which tracks the last known keyboard state of the keys in a state table—like an array of Boolean switches. This is the method I prefer because you don't have to write a keyboard handler.

Now that you know a little about each method, let's cover the details of each one in order, starting with the WM_CHAR message.

The WM_CHAR message has the following parameterization:

Wparam—Contains the ASCII code of the key pressed.
Lparam—Contains a bit-encoded state vector that describes other special control keys that may be pressed. The bit encoding is shown in Table 3.7.

Table 3.7 Bit Encoding for the Key State Vector

Bits

Description

0–15

Contains the repeat count, which is the number of times the keystroke is repeated as a result of the user holding down the key.

16–23

Contains the scan code. The value depends on the original equipment manufacturer (OEM).

24

Boolean; extended key flag. If it's 1, the key is an extended key, such as the right-hand Alt and Ctrl keys that appear on an enhanced 101- or 102-key keyboard.

29

Boolean; indicates whether the Alt key is down.

30

Boolean; indicates the previous key state. It's useless.

31

Boolean; indicates the key transition state. If the value is 1, the key is being released; otherwise, the key is being pressed.


To process the WM_CHAR message, all you have to do is write a message handle for it, like this:

case WM_CHAR:
{
// extract ascii code and state vector
int ascii_code = wparam;
int key_state = lparam;

// take whatever action

} break;

And of course, you can test for various state information that might be of interest. For example, here's how you would test for the Alt key being pressed down:

// test the 29th bit of key_state to see if it's true

#define ALT_STATE_BIT 0x20000000
if (key_state & ALT_STATE_BIT)
  {
  // do something
  } // end if

And you can test for the other states with similar bitwise tests and manipulations.

As an example of processing the WM_CHAR message, I have created a demo that prints out the character and the state vector in hexadecimal form as you press keys. The program is called DEMO3_11.CPP, and the executable is of course DEMO3_11.EXE. Try pressing weird key combinations and see what happens. The code that processes and displays the WM_CHAR information is shown here, excerpted from the WinProc():

case WM_CHAR:
     {
     // get the character
     char ascii_code = wparam;
     unsigned int key_state = lparam;

     // get a graphics context
     hdc = GetDC(hwnd);

     // set the foreground color to green
     SetTextColor(hdc, RGB(0,255,0));

     // set the background color to black
     SetBkColor(hdc, RGB(0,0,0));

     // set the transparency mode to OPAQUE
     SetBkMode(hdc, OPAQUE);

     // print the ascii code and key state
     sprintf(buffer,"WM_CHAR: Character = %c  ",ascii_code);
     TextOut(hdc, 0,0, buffer, strlen(buffer));

     sprintf(buffer,"Key State = 0X%X ",key_state);
     TextOut(hdc, 0,16, buffer, strlen(buffer));

     // release the dc back
     ReleaseDC(hwnd, hdc);

     } break;

The next keyboard event message, WM_KEYDOWN, is similar to WM_CHAR, except that the information is not "cooked." The key data sent during a WM_KEYDOWN message is the virtual scan code of the key rather than the ASCII code. The virtual scan codes are similar to the standard scan codes generated by any keyboard, except that virtual scan codes are guaranteed to be the same for any keyboard. For example, it's possible that the scan code for a particular key on your 101 AT–style keyboard is 67, but on another manufacturer's keyboard, it might be 69. See the problem?

The solution used in Windows was to virtualize the real scan codes to virtual scan code with a lookup table. As programmers, we use the virtual scan codes and let Windows do the translation. Thanks, Windows! With that in mind, here are the details of the WM_KEYDOWN message:

Message: WM_KEYDOWN

Wparam—Contains the virtual key code of the key pressed. Table 3.8 contains a list of the most common keys that you might be interested in.

lpara—Contains a bit-encoded state vector that describes other special control keys that may be pressed. The bit encoding is shown in Table 3.8.

Table 3.8 Virtual Key Codes

Symbol

Value (Hexadecimal)

Description

VK_BACK

08

Backspace key

VK_TAB

09

Tab key

VK_RETURN

0D

Enter key

VK_SHIFT

10

Shift key

VK_CONTROL

11

Ctrl key

VK_PAUSE

13

Pause key

VK_ESCAPE

1B

Esc key

VK_SPACE

20

Spacebar

VK_PRIOR

21

Page Up key

VK_NEXT

22

Page Down key

VK_END

23

End key

VK_HOME

24

Home key

VK_LEFT

25

Left-arrow key

VK_UP

26

Up-arrow key

VK_RIGHT

27

Right-arrow key

VK_INSERT

2D

Ins key

VK_DELETE

2E

Del key

VK_HELP

2F

Help key

No VK_Code

30–39

0–9 keys

No VK_Code

41–5A

A–Z keys

VK_F1 - VK_F12

70–7B

F1–F12 keys


Note: The keys A–Z and 0–9 have no VK_ codes. You must use the numeric constants or define your own.

In addition to the WM_KEYDOWN message, there is WM_KEYUP. It has the same parameterization—that is, wparam contains the virtual key code, and lparam contains the key state vector. The only difference is that WM_KEYUP is sent when a key is released.

For example, if you're using the WM_KEYDOWN message to control something, take a look at the code here:

case WM_KEYDOWN:
   {
   // get virtual key code and data bits
   int virtual_code = (int)wparam;
   int key_state  = (int)lparam;

   // switch on the virtual_key code to be clean
   switch(virtual_code)
      {
      case VK_RIGHT:{ } break;
      case VK_LEFT: { } break;
      case VK_UP:  { } break;
      case VK_DOWN: { } break;
      // more cases...

      default: break;
      } // end switch

   // tell windows that you processed the message
   return(0);
   } break;

As an experiment, try modifying the code in DEMO3_11.CPP to support the WM_KEYDOWN message instead of WM_CHAR. When you're done, come back and we'll talk about the last method of reading the keyboard.

The final method of reading the keyboard is to make a call to one of the keyboard state functions: GetKeyboardState(), GetKeyState(), or GetAsyncKeyState(). We'll focus on GetAsyncKeyState() because it works for a single key, which is what you're usually interested in rather than the entire keyboard. If you're interested in the other functions, you can always look them up in the Win32 SDK. Anyway, GetAsyncKeyState() as the following prototype:

SHORT GetAsyncKeyState(int virtual_key);

You simply send the function the virtual key code that you want to test, and if the high bit of the return value is 1, the key is pressed. Otherwise, it's not. I have written some macros to make this easier:

#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code)  ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

The beauty of using GetAsyncKeyState() is that it's not coupled to the event loop. You can test for keypresses anywhere you want. For example, say that you're writing a game and you want to track the arrow keys, spacebar, and maybe the Ctrl key. You don't want to have to deal with the WM_CHAR or WM_KEYDOWN messages; you just want to code something like this:

if (KEYDOWN(VK_DOWN))
  {
  // move ship down, whatever
  } // end if

if (KEYDOWN(VK_SPACE))
  {
  // fire weapons maybe?
  } // end if

// and so on

Similarly, you might want to detect when a key is released to turn something off. Here's an example:

if (KEYUP(VK_ENTER))
  {
  // disengage engines
  } // end if

As an example, I have created a demo that continually prints out the status of the arrow keys in the WinMain(). It's called DEMO3_12.CPP, and the executable is DEMO3_12.EXE. Here's the WinMain() from the program:

int WINAPI WinMain(HINSTANCE hinstance,
      HINSTANCE hprevinstance,
      LPSTR lpcmdline,
        int ncmdshow)
{
WNDCLASSEX winclass; // this will hold the class we create
HWND    hwnd;   // generic window handle
MSG    msg;   // generic message
HDC    hdc;   // graphics device context

// first fill in the window class stucture
winclass.cbSize    = sizeof(WNDCLASSEX);
winclass.style    = CS_DBLCLKS | CS_OWNDC |
            CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon   = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor  = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground  = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName  = NULL;
winclass.lpszClassName  = WINDOW_CLASS_NAME;
winclass.hIconSm   = LoadIcon(NULL, IDI_APPLICATION);

// save hinstance in global
hinstance_app = hinstance;

// register the window class
if (!RegisterClassEx(&winclass))
  return(0);

// create the window
if (!(hwnd = CreateWindowEx(NULL,         // extended style
        WINDOW_CLASS_NAME,   // class
        "GetAsyncKeyState() Demo", // title
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        0,0,   // initial x,y
        400,300, // initial width, height
        NULL,   // handle to parent
        NULL,   // handle to menu
        hinstance,// instance of this application
        NULL)))  // extra creation parms
return(0);

// save main window handle
main_window_handle = hwnd;

// enter main event loop, but this time we use PeekMessage()
// instead of GetMessage() to retrieve messages
while(TRUE)
  {
  // test if there is a message in queue, if so get it
  if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {
    // test if this is a quit
    if (msg.message == WM_QUIT)
      break;

    // translate any accelerator keys
    TranslateMessage(&msg);

    // send the message to the window proc
    DispatchMessage(&msg);
    } // end if

    // main game processing goes here

    // get a graphics context
    hdc = GetDC(hwnd);

    // set the foreground color to green
    SetTextColor(hdc, RGB(0,255,0));

    // set the background color to black
    SetBkColor(hdc, RGB(0,0,0));

    // set the transparency mode to OPAQUE
    SetBkMode(hdc, OPAQUE);

    // print out the state of each arrow key
    sprintf(buffer,"Up Arrow: = %d  ",KEYDOWN(VK_UP));
    TextOut(hdc, 0,0, buffer, strlen(buffer));

    sprintf(buffer,"Down Arrow: = %d  ",KEYDOWN(VK_DOWN));
    TextOut(hdc, 0,16, buffer, strlen(buffer));

    sprintf(buffer,"Right Arrow: = %d  ",KEYDOWN(VK_RIGHT));
    TextOut(hdc, 0,32, buffer, strlen(buffer));

    sprintf(buffer,"Left Arrow: = %d  ",KEYDOWN(VK_LEFT));
    TextOut(hdc, 0,48, buffer, strlen(buffer));

    // release the dc back
    ReleaseDC(hwnd, hdc);

  } // end while

// return to Windows like this
return(msg.wParam);

} // end WinMain

Also, if you review the entire source on the CD-ROM, you'll notice that there aren't handlers for WM_CHAR or WM_KEYDOWN in the message handler for the window. The fewer messages that you have to handle in the WinProc(), the better! In addition, this is the first time you have seen action taking place in the WinMain(), which is the section that does all game processing. Notice that there isn't any timing delay or synchronization, so the redrawing of the information is free-running (in other words, working as fast as possible). In Chapter 4, "Windows GDI, Controls, and Last-Minute Gift Ideas," you'll learn about timing issues, how to keep processes locked to a certain frame rate, and so forth. But for now, let's move on to the mouse.

Squeezing the Mouse

The mouse is probably the most innovative computer input device ever created. You point and click, and the mouse pad is physically mapped to the screen surface—that's innovation! Anyway, as you guessed, Windows has a truckload of messages for the mouse, but we're going to look at only two classes of messages: WM_MOUSEMOVE and WM_*BUTTON*.

Let's start with the WM_MOUSEMOVE message. The first thing to remember about the mouse is that its position is relative to the client area of the window that it's in. Referring to Figure 3.22, the mouse sends coordinates relative to the upper-left corner of your window, which is 0,0.

Other than that, the WM_MOUSEMOVE message is fairly straightforward.

Message: WM_MOUSEMOVE

Parameterization:

int mouse_x = (int)LOWORD(lParam);
int mouse_y = (int)HIWORD(lParam);

int buttons = (int)wParam;

Basically, the position is encoded as 16-bit entries in the lparam, and the buttons are encoded in the wparam, as shown in Table 3.9.

Figure 3.22Figure 3.22 The details of mouse movement.

Table 3.9 Button Bit Encoding for WM_MOUSEMOVE

Value

Description

MK_LBUTTON

Set if the left mouse button is down.

MK_MBUTTON

Set if the middle mouse button is down.

MK_RBUTTON

Set if the right mouse button is down.

MK_CONTROL

Set if the Ctrl key is down.

MK_SHIFT

Set if the Shift key is down.


So all you have to do is logically AND one of the bit codes with the button state and you can detect which mouse buttons are pressed. Here's an example of tracking the x,y position of the mouse along with the left and right buttons:

case WM_MOUSEMOVE:
{
// get the position of the mouse
int mouse_x = (int)LOWORD(lParam);
int mouse_y = (int)HIWORD(lParam);

// get the button state
int buttons = (int)wParam;

// test if left button is down
if (buttons & MK_LBUTTON)
  {
  // do something
  } // end if

// test if right button is down
if (buttons & MK_RBUTTON)
  {
  // do something
  } // end if

} break;

Trivial, ooh, trivial! For an example of mouse tracking, take a look at DEMO3_13.CPP on the CD-ROM and the associated executable. The program prints out the position of the mouse and the state of the buttons using the preceding code as a starting point. Take note of how the button changes only when the mouse is moving. This is as you would expect because the message is sent when the mouse moves rather than when the buttons are pressed.

Now for some details. The WM_MOUSEMOVE is not guaranteed to be sent all the time. You may move the mouse too quickly for it to track. Therefore, don't assume that you'll be able to track individual mouse movements that well—for the most part, it's not a problem, but keep it in mind. Also, you should be scratching your head right now, wondering how to track if a mouse button was pressed without a mouse move. Of course, there is a whole set of messages just for that. Take a look at Table 3.10.

Table 3.10 Mouse Button Messages

Message

Description

WM_LBUTTONDBLCLK

The left mouse button was double-clicked.

WM_LBUTTONDOWN

The left mouse button was pressed.

WM_LBUTTONUP

The left mouse button was released.

WM_MBUTTONDBLCLK

The middle mouse button was double-clicked.

WM_MBUTTONDOWN

The middle mouse button was pressed.

WM_MBUTTONUP

The middle mouse button was released.

WM_RBUTTONDBLCLK

The right mouse button was double-clicked.

WM_RBUTTONDOWN

The right mouse button was pressed.

WM_RBUTTONUP

The right mouse button was released.


The button messages also have the position of the mouse encoded just as they were for the WM_MOUSEMOVE message—in the wparam and lparam. For example, to test for a left button double-click, you would do this:

case WM_LBUTTONDBLCLK:
   {
   // extract x,y and buttons
   int mouse_x = (int)LOWORD(lParam);
   int mouse_y = (int)HIWORD(lParam);

   // do something intelligent

   // tell windows you handled it
   return(0);
   } // break;

Killer! I feel powerful, don't you? Windows is almost at our feet!

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020