When a key is pressed on the keyboard, the signal travels into the computer, where the kernel gets it. These signals, or "keycodes" as they are called, are raw data that needs to be translated into ASCII characters. The kernel performs this conversion, as well as obtaining other information about the keystroke. The necessary information is encoded into a message, and is sent to the currently active window. Simultaneously, the message is sent to the currently activecaret.
Inside Windows, there is a large terminology problem. So many different things all need to get their own names, so we can program with them, and keep everything straight. A perfect example of this is the difference between thecursor and thecaret. The cursor is the graphical image that represents the mouse. It can either be an arrow for pointing, a hand, an hourglass, or an I-shaped text selector. Thecaret, on the other hand, is the blinking object that is used to enter text. When you type, the letter appears at the caret, and the caret moves forward by 1 space. It is important to keep these terms straight, because if you confuse them inside your program, you could have a lot of debugging to do.
There are a few keypress messages that the program can choose to handle. It is important to note that not all of these messages need to be handled in a program, and in fact it is usually a good idea to not handle many of them.
| Bits of LPARAM | Purpose |
|---|---|
| 0-15 | Key Count |
| 16-23 | Scan Code |
| 29 | Context Code |
| 30 | Previous State |
| 31 | Key Transition |
Windows users will no doubt be familiar with some of the common key combinations that are used with large windows programs. CTRL+C copies an object to the clipboard. CTRL+P prints the current document. CTRL+S saves the current document. There are dozens more, and it seems like each program has its own specific key combinations.
These key combinations are known in the Windows world as "Accelerators". A program that uses accelerators will define anaccelerator table. This table will contain all the different key combinations, and the command identifier that they each map to. When an accelerator is pressed on the keyboard, the program doesn't receive the keypress messages, it instead receives a WM_COMMAND message, with the command identifier in the WPARAM field.
To translate the accelerator keypress combinations into WM_COMMAND messages, the function TranslateAccelerator needs to be used in conjunction with the message loop, as such:
while(GetMessage(&msg, NULL, 0, 0)){ if(!TranslateAccelerator(&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); }}The TranslateAccelerator function will automatically dispatch the WM_COMMAND message to the appropriate window.
Each program may only have 1 active caret, and worse than that, the entire system may only have 1 active caret on the screen at a time. When using a caret, the programmer needs to take special care to destroy the caret when it is not in use, and to recreate the caret when needed. This can be accomplished relatively easily by creating the caret on the WM_SETFOCUS message (when the window is made active), and by destroying the caret on the WM_KILLFOCUS and WM_DESTROY messages.
The mouse has more messages associated, because the mouse is capable of more unique tasks than the keyboard is. For instance, the mouse has at least 2 buttons (frequently 3 or more), it often has a trackball, and it can be hovered over objects on the screen. Each of these functions of the mouse can be handled via messages, so we are going to need several messages
There are a number of mouse messages that may be handled by the program.
In addition, the LPARAM field will contain information about the cursor location, in X-Y coordinates, and the WPARAM field will contain information about the state of the shift and CTRL keys.
The system will handle the graphical mouse movements, so you don't need to worry that your program is going to lock up the mouse. However, the program is capable of changing the mouse cursor, and sending mouse messages to other windows, if needed.
Timers are used to space the flow of a program via pauses, so that a group of actions can be allowed to process before another group interacts with the result. In a strict sense, the Windows Timer is not a user input device, although the timer can send input messages to the window, so it is generally covered in the same manner as the mouse and the keyboard. Specifically, Charles Petzold's famous book, "Programming Windows", treated the timer as an input device.
A common use of a timer is to notify the program of the end of a pause so that it can erase an image previously painted on the screen, such as in screensavers that display various images from one folder. The native timer function, however, is not considered accurate for games or time-critical responses. The DirectX API is preferred for games.
Each time the specified time interval assigned to the Timer elapses, the system sends a WM_TIMER message to the window associated to the Timer.
The new Timer starts timing the interval as soon as it is created by theSetTimer function. When you create a Timer you retrieve a unique identifier that can be used by theKillTimer function to destroy the Timer. This identifier is also present in the first parameter of WM_TIMER message.
Let's see the functions syntax.
UINT_PTR SetTimer( HWND hWnd, //Handle of the window associated to the timer UINT nIDEvent, //an identifier for the timer UINT uElapse, //the time-out value, in ms TIMERPROC lpTimerFunc //the address of the time procedure (see below));BOOL KillTimer( HWND hWnd, // Handle of the window associated to the timer UINT_PTR uIDEvent // the identifier of the timer to destroy);
If you want to reset an existing timer you have to set the first argument to NULL, and the second to an existing timer ID.
You can process WM_TIMER message in two different ways:
- by processing the WM_TIMER message in the window procedure of the window passed as first argument.
- by defining a TimerProc callback function (fourth argument) that process the message instead of a window procedure.
Let's see the first one.
#define IDT_TIMER1 1001#define IDT_TIMER2 1002...SetTimer(hwnd, //handle of the window associated to the timer IDT_TIMER1, //timer identifier 5000, // 5 seconds timeout (TIMERPROC)NULL); //no timer procedure, process WM_TIMER in the window procedureSetTimer(hwnd, IDT_TIMER2, 10000, (TIMERPROC)NULL);...LRESULT CALLBACK WinProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){ ... case WM_TIMER: switch(wParam) { case IDT_TIMER1: //process the 5 seconds timer break; case IDT_TIMER2: //process the 10 seconds timer break; } .../* to destroy the timers */KillTimer(hwnd, IDT_TIMER1);KillTimer(hwnd, IDT_TIMER2);
Now using aTimerProc.
VOID CALLBACK TimerProc( HWND hwnd,// handle of window for timer messages UINT uMsg,// WM_TIMER message UINT idEvent,// timer identifier DWORD dwTime // current system time );
It is called by the system to process an associated Timer's WM_TIMER message.Let's see some code.
#define IDT_TIMER1 1001.../* The Timer Procedure */VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) { MessageBox(NULL, "One second is passed, the timer procedure is called, killing the timer", "Timer Procedure", MB_OK); KillTimer(hwnd, idEvent); }.../* Creating the timer */SetTimer(hwnd, IDT_TIMER1, 1000, (TIMERPROC)TimerProc);...The timer only comes with a single message, WM_TIMER, and the WPARAM field will contain the timer ID number.