Device Control


Buffering Mouse Events

Michael Kelly


Michael Kelly has held a variety of jobs, including auto mechanic and electro-mechanical technician. For the past three years, he has been engaged in a program of self-study concerning programming and related subjects. Joining the C Users' Group stimulated his interest in writing portable code. He intends to enter the computer field in some professional capacity. Kelly can be reached at 254 Gold St., Boston, MA 02127.

This article demonstrates a technique for asynchronously buffering mouse events in a queue. This approach, written in C and assembler, has several advantages over polling.

First, the mouse driver forwards information as events are detected instead of when your program finally asks for it. A user can become annoyed with an application where moving the mouse moves the cursor on the screen, but clicking a button does nothing. The event queuing approach provides users with a consistent feel. When a program is ready to process mouse events, it should activate the handler and turn on the mouse cursor. When the program is busy for a while, executing a user selection perhaps, it should turn off the mouse cursor, de-activate the handler, and either free the queue memory (if needed by your program), or adjust the queue pointers to indicate an empty condition.

Second, capturing all mouse input with one handler makes isolating system-dependent code easier. You can construct mouse interface functions to send messages to your portable application code, and change only the mouse managment module to accommodate other environments.

Third, this technique allows your application code to take on some of the "event-driven" feel common to many windowing environments. You can test this paradigm before making an investment.

Implementation Details

The queue is implemented as a circular, singly-linked list, with pointers head and tail that chase each other around the queue in a manner reminiscent of the PC BIOS keystroke buffer. I represented the queue as a linked list rather than as an array of structures to avoid multiplies (a very slow operation on the 8088) inside the mouse handler. Handler exectution disables interrupts, and the handler is likely to be called many times per second. The queue test driver program in Listing 1 uses INT 33H, Function OCH to register the mouse event handler with the mouse driver. The test driver should work with devices compatible with the Microsoft Mouse Driver Interface. Once you have initialized the mouse and registered the handler, the mouse driver will execute a far call to the handler when it senses a mouse event corresponding to one or more of the event types set during handler registration.

On entry to the handler (Listing 2) , the machine registers DX, CX, BX, and AX contain information about the mouse cursor vertical position, mouse cursor horizontal position, mouse button status, and type of mouse events in effect at the time of the call, respectively. Also, the DS register is left pointing to the mouse driver's data segment, rather than that owned by the application. These peculiarities, combined with the need for the handler to execute quickly, dictate that it be written in assembler.

To function properly, HANDLER must know the internal representation of a link in the queue, be able to access the pointers head and tail, and follow the same protocol as the function that removes events from the queue. In this example if tail ->next equals head, the queue is considered to be full and HANDLER exits. Otherwise, HANDLER stores the mouse event information in the queue, sets the member variable valid to 1, and advances tail by one link. The dequeuing function resets the valid member variable to 0 before moving head, then processes the mouse events.

Listing 3 and Listing 4 define the C functions responsible for queue construction and destruction. The set_que() function performs memory allocation, list linking, and initialization of head and tail. Its sole parameter, que_entries, is the desired number of event entries in the queue. If successful, set_que returns 1, and head and tail both point to the first link in the list. If set_que is unable to allocate the requested number of links, it calls free_que() to release any memory allocated to the queue, and returns 0.

Listing 1 contains a trivial test driver program. If a mouse is detected, the driver calls set_que(). If set_que() returns 1, the handler is registered with all event bits set. The program pokes a character to the screen at the mouse cursor position (if you're running on a CGA or EGA monitor in text mode 2 or 3) while the user drags the mouse using the left mouse button. It terminates when the user presses the right mouse button.

Included on the code disk is a small model link library (TC_MOUSE. LIB and TC_MOUSE. H) containing most of the mouse interface functions. If you use a compiler other than Turbo C v1.5+ you can use int86() and int86x(), loading the registers as described in Table 1 to accomplish the same ends.

References

Dettman, Terry, DOS Programmer's Reference, Que Corp., 1988.

Duncan, Ray, Advanced MS-DOS Programming, 2nd Edition, Microsoft Press, 1988.