Processing Keyboard and Mouse Events

This section is dedicated to descriptions of the keyboard and mouse events in console applications. These capabilities make console applications flexible and powerful, considerably extending the range of tasks that can be carried out in this mode.

Before proceeding any further, however, consider one unusual but useful API function. This is the wsprintfA function. I specially emphasize that this is an API function, which is provided to applications by the operating system. This function is an analogue of the sprintf built-in C function. Its first parameter is the pointer to the buffer, into which the result of formatting is loaded. The second argument is the pointer to the format string (e.g., " Numbers : %lu, %1u "). Then follow the pointers to parameters (or parameters themselves if they are numeric). The number of parameters is limited only by the contents of the format string. Now, the most important point must be considered : Because the number of parameters is undefined, you have to clear the stack on your own. The example illustrating the use of this function will be provided later. It is also necessary to mention that for the IMPORT32.LIB (TASM32) library, the prototype of this function will be _wsprintfa instead of wsprintfA . Finally, note that if the function has completed successfully, then the length of the copied string will be returned into EAX .

The ReadConsole Input function serves as a basis for getting information about the keyboard and mouse in the console mode. Parameters of this function are briefly described here:

  • FirstDescriptor of the console's input buffer

  • SecondThe pointer to the structure (or an array of structures) containing information about the events that occurred in this console (later, I will cover this structure in more detail)

  • ThirdThe number of received records (structures)

  • FourthThe pointer to the double word containing the number of records actually received

Now consider the structure containing information about the console event in more detail. First, I'd like to note that in C this structure is defined using the UNION data type (data types will be covered in detail in Chapter 12). In my opinion, frequent use of this word obscures its actual meaning. Therefore, when describing this structure, I will do without the STRUCT and UNION keywords. Also, note that this block of data starts with the double word whose least significant word defines the event type. Depending on the value of this word, all further bytes (18 at most) will be interpreted in a specific way. If you are already acquainted with various structures used in C and macroassembler, you must now understand why UNION is relevant here.

However, let's return to the event type. The total number of events reserved by the system is five:

 KEY_EVENT                 equ  1h ; Keyboard event MOUSE_EVENT               equ  2h ; Mcuse event WINDOW_BUFFER_SIZE_EVENT  equ  4h ; Window size has changed MENU_EVENT                equ  3h ; Reserved FOCUS_EVENT               equ 10h ; Reserved 

Now, it is time to describe the values of other bytes of this structure depending on the event that took place. The description of the KEY_EVENT event is provided in Table 8.1.

Table 8.1: The KEY_EVENT event

Offset

Length

Value

+4

4

The field value is greater than zero when a key is pressed.

+8

2

This shows the number of repetitions when a key is held.

+10

2

This field holds the virtual code of the key.

+12

2

Here is the scan code of the key.

+14

2

For the ReadConsolelnputA function, the least significant byte is equal to the ASCII code of the key. For the ReadConsoleInputw function, the word contains the Unicode code of the key.

+16

4

This contains the status of the control keys. It can represent the sum of the following constants:

  • RIGHT_ALT_PRESSED equ 1h

  • LEFT_ALT_PRESSED equ 2h

  • RIGHT_CTRL_PRESSED equ 4h

  • LEFT_CTRL_PRESSED equ 8h

  • SHIFT_PRESSED equ l0h

  • NUMLOCK_ON equ 20h

  • SCROLLLOCK_ON equ 40h

  • CAPSLOCK_ON equ 80h

  • ENHANCED_KEY equ 100h

   

The meaning of these constants is obvious.

The description of the MOUSE_EVENT event is provided in Table 8.2.

Table 8.2: The MOUSE_EVENT event

Offset

Length

Value

+4

4

The least significant word is the X-coordinate of the mouse, and the most significant word is the Y-coordinate.

+8

4

This field describes the state of the mouse buttons . The first bit corresponds to the left button, the second bit corresponds to the right button, and the third bit corresponds to the third button. (Recall that bit numbering starts from zero.) If a bit is set to one, this means that the key is pressed.

+12

4

This shows the state of control keys, similar to the field of the previous table.

+16

4

This field can contain the following values:

  • MOUSE_MOV equ 1h ; The mouse was moved

  • DOUBLE_CL equ 2h ; There was a double-click

Consider the WINDOW_BUFFER_SIZE_EVENT event.

The double word containing the new size of the console window resides by the offset +4. The least significant word is the X-size, and the most significant word is the Y-size. Note that when you are dealing with a console window, all size and coordinate values are given in "symbolic" units.

As relates to the last two events, the most significant word is the one located by the offset +4. Listing 8.4 provides a simple example illustrating the processing of console events.

Listing 8.4: Processing keyboard and mouse events for a console application
image from book
 .586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE  equ -11 STD_INPUT_HANDLE   equ -10 ; Event type KEY_EV             equ 1h MOUSE_EV           equ 2h ; ConstantsKeyboard status RIGHT_ALT_PRESSED  equ 1h LEFT_ALT_PRESSED   equ 2h RIGHT_CTRL_PRESSED equ 4h LEFT_CTRL_PRESSED  equ 8h SHIFT_PRESSED      equ l0h NUMLOCK_ON         equ 20h SCROLLLOCK_DN      equ 40h CAPSLOCK_ON        equ 80h ENHANCED_KEY       equ l00h ; Prototypes of external procedures EXTERN  wsprintfA:NEAR EXTERN  GetStdHandle@4:NEAR EXTERN  WriteConsoleA@20:NEAR EXTERN  SetConsoleCursorPosition@8:NEAR EXTERN  SetConsoleTitleA@4:NEAR EXTERN  FreeConsole@0:NEAR EXTERN  AllocConsole@0:NEAR EXTERN  CharToOemA@8:NEAR EXTERN  SetConsoleTextAttribute@8:NEAR EXTERN  ReadConsoleInputA@16:NEAR EXTERN  ExitProcess@4:NEAR ; INCLUDELIB directives includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------ ; Structure for defining events COOR STRUC X  WORD ? Y  WORD ? COOR  ENDS ; Data segment _DATA SEGMENT HANDL DWORD ? HANDL1 DWORD ?         TITL DB "Processing mouse events", 0         BUF DB 200 dup(?)         LENS DWORD ? ; Number of displayed characters         CO DWORD ?         FORM DB "Coordinates: %u %u "         CRD COOR <?>         STR1 DB "Press ESC to exit", 0         MOUS_KEY WORD 9 dup(?) _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Create a console ; First, release the existing console     CALL  FreeConsole@0     CALL  AllocConsole@0 ; Get the HANDL1 input handle     PUSH  STD_INPUT_HANDLE     CALL  GetStdHandle@4     MOV   HANDL1, EAX ; Get the HANDL output handle     PUSH  STD_OUTPUT_HANDLE     CALL  GetStdHandle@4     MOV   HANDL, EAX ; Create the console window header     PUSH  OFFSET TITL     CALL  SetConsoleTitleA@4 ;*********************************** ; String reencoding     PUSH  OFFSET STR1     PUSH  OFFSET STR1     CALL  CharToOemA@8 ; String length     PUSH  OFFSET STR1     CALL  LENSTR ; Display the string     PUSH  0     PUSH  OFFSET LENS     PUSH  EBX     PUSH  OFFSET STR1     PUSH  HANDL     CALL  WriteConsoleA@20 ; Waiting loop  Mouse move or double-click LOO: ; Cursor coordinates     MOV   CRD.X, 0     MOV   CRD.Y, 10     PUSH  CRD     PUSH  HANDL     CALL  SetConsoleCursorPosition@8 ; Read one record about the event     PUSH  OFFSET CO     PUSH  1     PUSH  OFFSET MOUS_KEY     PUSH  HANDL1     CALL  ReadConsoleInputA@16 ; Check events from the mouse     CMP   WORD PTR MOUS_KEY, MOUSE_EV     JNE   LOO1 ; Convert the mouse coordinates to a string     MOV   AX, WORD PTR MOUS_KEY+6 ; Y-mouse ; Copy and res et the most significant bits to zero     MOVZX EAX, AX     PUSH  EAX     MOV   AX, WORD PTR MOUS_KEY+4 ; X-mouse ; Copy and reset the most significant bits to zero     MOVZX EAX, AX     PUSH  EAX     PUSH  OFFSET FORM     PUSH  OFFSET BUF     CALL  wsprintfA ; Restore the stack     ADD   ESP, 16 ; Convert the output string     PUSH  OFFSET BUF     PUSH  OFFSET BUF     CALL  CharToOemA@8 ; String length     PUSH  OFFSET BUF     CALL  LENSTR ; Display the cursor coordinates     PUSH  0     PUSH  OFFSET LENS     PUSH  EBX     PUSH  OFFSET BUF     PUSH  HANDL     CALL  WriteConsoleA@20     JMP   LOO ; Jump to the starting point of the loop LOO1: ; Check for keyboard events     CMP   WORD PTR MOUS_KEY, KEY_EV     JNE   LOO ; There was a keyboard event; which one?     CMP   BYTE PTR MOUS_KEY+14, 27     JNE   LOO ;************************** ; Close the console     CALL  FreeConsole@0     PUSH  0     CALL  ExitProcess@4     RET ; The procedure for determining the string length ; String - [EBP+08H] ; Length in EBX LENSTR    PROC     ENTER 0, 0     PUSH  EAX     CLD     MOV   EDI, DWORD PTR [EBP+08H]     MOV   EBX, EDI     MOV   ECX, 100 ; Limit the string length     XOR   AL, AL     REPNE SCASB ; Find the 0 character     SUB   EDI, EBX ; String length including 0     MOV   EBX, EDI     DEC   EBX     POP   EAX     LEAVE     RET   4 LENSTR ENDP _TEXT ENDS END START 
image from book
 

Consider this program in more detail.

First, consider the wsprintfA function. As I have already mentioned, this is an unusual function.

  • It has an unlimited number of parameters. The first two parameters are required. The first parameter is the pointer to the buffer, into which the resulting string will be copied. The next parameter is the pointer to the format string. The format string can contain text and the format of the parameters for output. The fields containing information about the parameters start from the % character. The format of these fields is practically the same as the format of the fields used in standard C library functions, such as printf and sprintf . The only exception is the lack of real numbers in the format for the wsprintf function. There is no need to consider this format in detail. It is only necessary to note that every field in the format string corresponds to a parameter (starting from the third one). For example, in the program under consideration, the format string appeared as follows : " Coordinates: %u %u ". This means that two numeric parameters of the WORD type will be pushed into the stack. In fact, you will have pushed into the stack two double words, having previously reset the most significant words to zero. For such an operation, it is expedient to use the MOVZX microprocessor command, which copies the second operand into the first so that the bits of the most significant word are filled with zeroes. If the parameters were double words, then instead of the %u field you would use %lu . If the field of the format string defines a string parameter, for example, " %S ", then the pointer to that string must be pushed into the stack (which is natural). [i]

  • Because the function has no information about the number of parameters that can be passed to it, the developers decided not to complicate the code of the function and left the task of clearing the stack to the programmer's discretion. [ii] The stack is released by the ADD ESP, N command, where N is the number of bytes to be deleted.

Now, turn your attention to the ReadConsoleInputA function. To the information already provided about it, I'll only add that if the event buffer is empty, the function will wait until any event occurs to the console window and will return the control only after such an event. Besides this, you can specify that the function should return not one but two or more records on the events that occurred to the console window. In this case, several information records will be pushed into the stack. However, I won't concentrate on this aspect.

As usual, I'd like to explain how to compile this program using TASM32. As before, it is necessary to remove all @N entries, specify the IMPORT32.LIB library in the INCLUDELIB directive, and replace wsprintfA with _wsprintfA .

[i] I can't help mentioning that in some publications on Assembly language, I have encountered information that says all parameters for this function pushed into the stack are pointers. As you can see, this statement is generally incorrect.

[ii] Naturally, the C compiler carries out this task automatically.



The Assembly Programming Master Book
The Assembly Programming Master Book
ISBN: 8170088178
EAN: 2147483647
Year: 2004
Pages: 140
Authors: Vlad Pirogov

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net