Teach Yourself Visual C++ 5 in 21 Days

Chapter 4: The Mouse and the Keyboard

Objectives:

This chapter introduces you to techniques that will detect whether a mouse button or a keyboard button has been pressed. The objectives important to this chapter are:

  • detecting a mouse button event
  • detecting a keyboard event
  • understanding the bitwise operator
Concepts:

The majority of the chapter is taken up with creating a simple drawing program. The point of the project is not to create great art, but to detect whether the user is pressing a button on the mouse. The project is depicted on page 118. It has only two controls in a dialog box: an instructional static text box, and an Exit button. By now, you should know how to code the Exit button.

Following the same basic instructions as in previous chapters, you create the new project, delete the default controls in the Dialog Box you create, and add the two desired controls. All the real work is done in code this time.

As before, the Exit button only has to call the OnOK() function to close the application. The new code begins on page 121, where the author takes a first stab at accomplishing his goal: to draw on the screen when the user holds down the left mouse button and drags the mouse.

The code you write is in the WM_MOUSEMOVE event for the Dialog Box. (As you have probably gathered, the WM stands for Windows Message. For our purposes, a message and and event are the same.) The code you add takes advantage of the parameters that are passed to the OnMouseMove() function, noted on page 121: nFlags and point. The value of nFlags is determined by what keys and mouse buttons are pressed at the time the value is read. We want to know if the left button of the mouse is being pressed. We already know that the mouse is being moved, else this function would not have been called.

To determine if the the left mouse button is being pressed, an if test is applied. To understand it, you have to understand several things:

  • nFlags is series of bits. Each bit turned on (set to 1) means that some keypress condition is met.
  • MK_LBUTTON is a series of bits that stands for the left button on the mouse being pressed.
  • We cannot test for the value of nFlags being equal to MK_LBUTTON, because other keys may be pressed as well. We do not care if they are, but their being pressed would cause nFlags to be different from the value of MK_LBUTTON, which only indicates that button being pressed.
  • The single ampersand is used here as the bitwise AND operator. It performs a comparison between the bits of two variables. The two variables must contain the same number of bits (each of them one byte, or each of them two bytes, etc.) The comparison results in a synthetic value, equal in number of bits to the variables compared.
  • We do not need to know the value stored in either variable compared here, we only need to be assured that they represent what we are told they represent.

Still confusing, isn't it? The test is this: the bitwise AND operator is applied to the bits of nFlags and MK_LBUTTON. The resulting value has each of its bits set independently of the others. A bit in the result is set equal to 1ONLY if BOTH of the corresponding bits in the two values being compared are already set equal to 1. The only bits in MK_LBUTTON that are set equal to 1 are those that mean the left mouse button is being pressed. All other bits in MK_LBUTTON are set equal to 0. All of those 0's will be represented as 0's in the result of the comparison, since they could only be 1's if BOTH variables had 1's in those positions. This means that the only bits in the result that even have a chance of being set to 1's are those that are set to 1 in MK_LBUTTON. So, if the nFlags variable has 1's in the same places as MK_LBUTTON, the result will look exactly like MK_LBUTTON. This is why the test compares the result of the bitwise comparison to MK_LBUTTON.

You author does a better job of explaining the CClientDC object. It is a device context, which means that it is a scratch pad in memory. This time it is a scratch pad that we draw on. When we draw on it, the drawing is transferred to the screen. Seems roundabout, but it works. The use of the argument this is confusing, but your online help explains it. Essentially, the word this in CClientDC dc(this) stands for a pointer to the object you are creating. The next line is clearer. If you understand that dc stands for the instance of the CClientDC class that you just created, then dc.SetPixel() is clearly a call to a public member function. The parameters tell it the x and y coordinates to draw on, as well as the color to draw. A pixel is, of course, one picture element, and this function call draws only on dot on the scratch pad in memory, which is immediately transferred to the screen.

The drawback to this method of drawing is pointed out in the next two pages. The problem is that the computer has other things to do than waste time watching for that function call all day. Using the code on pages 122 and 123, you get poor output: discontinuous points on the screen instead of lines.

The enhancement on the next several pages adds variables to remember the last points where pixels were drawn. Using these variables, other member functions of the dc object are called: MoveTo() and LineTo(). The cursor on the scratch pad is told to MoveTo the last point drawn, and draw a LineTo the current point. This makes a smoother, more continuous line.

Of course, the problem that comes up next is that the line is too continuous. You can only draw with this program like an Etch-a-Sketch©: no separate objects. The problem is solved on page 130, where you add code to the OnLButtonDown () function for the application. This is called when the left mouse button is pressed down, storing the current x and y coordinates of the mouse cursor in the variables for the position of the last pixel drawn. This essentially resets the position to draw a line from to a new start position every time the button is pressed again.

In going over this project, I was disappointed that we lost the color control that we had in drawing individual pixels when we started drawing lines. The answer to Exercise 2 on page 138 puts this functionality back by creating a virtual pen to draw with. When we do so, we can set the width and color of the pen easily.

The second project in this chapter is the capture key presses in the MyKey program. Note the instructions on page 133, that tell you to use the WM_KEYDOWN message/event. This is triggered whenever a key is pressed. Also, note the use of the itoa() function, which converts an integer to an ASCII string. The syntax is not explained, so I will do so here. The line:

	itoa(nChar, strnChar, 10);

is almost understandable. The variable nChar is the integer in memory that the keyboard generated when the key was pressed. The variable strnChar is the string space you created to hold the translation. (The translation is stored in the string space by the function. The number 10 tells the function that the integer passed to it in nChar is to be translated into a decimal integer (base 10) and stored in the string space. Had you wanted it in hexadecimal, you would have put 16 in place of 10 in the function call. Your authors were doing very well until they reversed the first and second arguments to the function in the example on page 135. Note that it is not worded like page 133, which works.