📄 mouse.doc
字号:
0000000001111111 0111111100000000
0000000000111111 0111111110000000
0000000000011111 0111111111000000
0000000111111111 0111110000000000
0001000011111111 0100011000000000
0011000011111111 0000011000000000
1111100001111111 0000001100000000
1111100001111111 0000001100000000
1111110000111111 0000000110000000
Screen Mask Cursor Mask
Figure 1: Example graphics cursor
-----------------------------------------------------------------------
The graphics mask pair are defined together in a single array of
type unsigned integer. For clarity, comment fields were added in cur-
sor.h to show what the masks will actually produce. This is helpful not
only in identifying what a cursor looks like, but also in designing new
cursors. The GraphicsCursor class constructor is used for declaring a
cursor. It takes as arguments the cursor hot spot, mask array, width
and height. The width and height are the actual pixel dimensions of the
image. In Figure 1, the width is 11 and the height is 16 - these are
the maximum dimensions of the defined screen mask, even though the
overall array is 16x16. An instance of GraphicsCursor can be passed to
SetCursor() to make it the current cursor.
SetCursor() can also set the text cursor type and shape. There are
two cursor types in text mode. The hardware cursor places the normal
video cursor (the cursor normally associated with the keyboard) under
the control of the mouse. The software cursor is independent of the
video cursor and behaves similarly to the graphics cursor. The software
cursor is used in most applications so that the user is presented with
the normal video cursor for typing and a mouse cursor for menu selec-
tion, etc. The cursor type - 0 for software, 1 for hardware - is part
of the TextCursor class.
Like the graphics cursor, the text software cursor also requires a
screen and cursor mask, but no hot spot since the cursor always takes
up a whole character cell, regardless of where the mouse cursor is
actually positioned in that cell. (If a hardware cursor is implemented,
the starting and ending scan lines for the cursor are required.) The
two masks operate only on a character cell, not a 16x16 pixel array.
Each cell consists of a one-byte character value and a one-byte charac-
ter attribute (which sets the foreground and background colors), so the
masks must each be two bytes long. The upper byte masks the attribute,
and the lower byte masks the character. Like the graphics masks, the
screen mask is XORed with the underlying character cell, and the cursor
mask is ANDed with the resulting value. The difference is that in text
mode, both the character value and its color can be changed, whereas in
graphics mode, the pixel colors cannot be controlled.
To get a better understanding of what the masks will do, we need to
look at an example of a screen character:
6B24h = 0110 1011 0010 0100 b
|--- ---- ---------
| | | |__ character value (24h)
| | |__________ foreground color (11d)
| |______________ background color (6d)
|________________ blinking bit (1=blinking)
This value will display a '$' (24h) with a background color of brown
(6) and a foreground color of lightcyan (11). With the text cursor, we
can mask each bit of the character and attribute, so not only can we
set the mouse cursor character, but we can also control the foreground
and background colors (and even blinking). The cursor masks default to
a reverse box, which is
Screen Mask = 77FFh = 0111 0111 1111 1111 b
Cursor Mask = 7700h = 0111 0111 0000 0000 b
The first byte of the screen mask, 77h in this example, is ANDed with
the underlying characters attribute. The first 7h is ANDed with the
background color so that all colors but the blinking bit are passed
through. Masking the blinking bit to 0 will allow the mouse cursor to
rest on a blinking character without itself blinking. The second 7h is
ANDed with the foreground color so that those colors are also passed,
except that the high intensity colors will be converted to low inten-
sity. The FFh also passes the screen character. In the cursor mask, the
77h will invert both the foreground and background colors, while the
00h creates an "empty" character, which is a character cell with no-
thing in it (a box).
If you want to create a cursor with a particular character and
color, you would set the screen mask to 0000h and the cursor mask to
whatever color and character values desired. For example,
Screen Mask = 0000h = 0000 0000 0000 0000b
Cursor Mask = 0F23h = 0000 1111 0010 0011b
will set the cursor character to a '#' with a background color of black
and a foreground color of white. More complex combinations are possi-
ble, such as setting the cursor's foreground color to a constant value
but allowing the screen's background color to show through. Again, the
best way to understand it is to try it, and cursor.h defines several
text cursors.
In graphics mode, the mouse driver takes care of drawing the cursor
and restoring the image under the cursor when it moves. One drawback to
this is that you cannot use color cursors in graphics mode using the
mouse driver. You can instead create a color image, manually draw it to
the screen, and manually restore the underlying image. This is exactly
how the ColorGraphicsCursors work. If a CGC is installed using the
SetCursor() command, then the special drawing functions will trigger
off of the event handler, thereby drawing the cursor whenever the mouse
moves. CGC cursors are defined just like standard graphics cursors, ex-
cept that the screen mask is split into the four video bit planes. See
cursor.h for examples.
The color drawing functions in cgc.cpp were written purely in C++,
with no assembly language. The reason for this was to clearly show how
the methods work. The results are that the color cursor image has a
slight flicker, but overall the speed is acceptable. Also, these func-
tions do not make any attempt to save and restore the video card state.
This could be a problem because the drawing functions are triggered by
the mouse event handler and could therefore be activated at any time,
even during another graphics draw. You can avoid problems by turning
the cursor off (using Hide()) before drawing any graphics, which is a
good idea anyway. Finally, these functions only support standard EGA
and VGA resolutions.
A better method is to use your favorite graphics library to draw
the cursor and restore the screen. This will probably result in better
speed and might also support higher resolutions such as SuperVGA/VESA.
(This approach also solves another drawback of the mouse drivers - they
also do not support high-res modes.) To implement another graphics li-
brary, you need only modify the VideoRead(), VideoWrite(), and Cursor-
Write() funtions. An example using the BGI is provided in cgc_bgi.cpp.
-----------------------------------------------------------------------
The Event Handler
There is another way to capture mouse events. The mouse event hand-
ler is similar to a TSR program in that it can be set up to capture
events in background. The handler can be set to trigger off any combi-
nation of mouse events (Table 1), and can then execute any user code,
providing it does not issue a DOS or I/O call. However, since the main
program is suspended during the event handler execution, the handler
code should be fast so it does not to noticeably slow down the main
program. When an event occurs, certain mouse information is automati-
cally placed in the registers for the handler to use. The function
Save() can be used to stuff these parameters into the event buffer for
later use. Any event handler routines you write must be declared as an
interrupt type and terminate with a far return. The macro EventExit()
provides the proper exit code and should be the last statement in the
handler function:
void interrupt lb_handler()
{
mouse.Save();
do_something();
EventExit();
}
You can then use this function and an event mask to create an in-
stance of a MouseEventHandler. Installing it is accomplished with the
function InstallHandler():
#include "mouse.h"
void interrupt lb_handler();
main()
{
unsigned char eventmask = LB_PRESSED | LB_RELEASED;
MouseEventHandler myHandler(eventmask, lb_handler);
if(mouse.Exists())
{
mouse.InstallHandler(myHandler);
mouse.Enable();
mouse.Show();
...
}
}
eventmask determines which mouse events will trigger the event handler.
In the example above, the handler will execute whenever the left button
is pressed or released. Because the event handler should be kept as
small as possible, you normally only want to call Save() for storing
the mouse information and then exit. The MouseEventHandler class pro-
vides such a handler by default if you do not specify a handler func-
tion name:
main()
{
MouseEventHandler myHandler(LB_PRESSED | LB_RELEASED);
if(mouse.Exists())
{
mouse.InstallHandler(myHandler);
// Here's another way of doing it:
// mouse.InstallHandler(LB_PRESSED | LB_RELEASED);
mouse.Enable();
mouse.Show();
...
}
}
There might be some cases where you want the handler to do more, such
as when the program is waiting for user input and not doing much other
processing. In such cases you may want to load different handlers, de-
pending on what the program is doing. A more functional handler can be
loaded for pulldown menus, for instance, and a bare-bones handler (or
none at all) can be installed in speed critical areas where the mouse
has little use.
The Save() function called by the event handler stores the mouse
information in a buffer instead of writing it directly into the class.
This allows events to be captured even while your program is busy doing
something else. (The keyboard has a similar buffer that provides type-
ahead capability.) Save() also calls the ColorGraphicsCursor funtions,
so if you are using CGC's and also want to define custom event hand-
lers, you will need to consider this. To get the event information out
of the buffer, you must call the function GetEvent():
for(;;)
{
mouse.GetEvent(); // get an event from the buffer
if(mouse.LB_Dn()) // check for left button down
{
if(mouse.x() > 320 || mouse.y() > 100)
do_something();
}
else
do_something_else();
}
When using the event handler you also have access to two other fea-
tures: MultiClick detection and the Repeater. MultiClicks occur when a
mouse button is rapidly clicked more than once. You can set up the but-
tons for MultiClicks by calling the SetClickThreshold() function and
then check them with the DoubleClick() and MultiClick() functions:
mouse.SetClickThreshold(250); // set threshold to 250ms
for(;;)
{
mouse.GetEvent();
if(mouse.DoubleClick(LEFTBUTTON)) // check for 2 clicks
do_something();
if(mouse.MultiClick(LEFTBUTTON) == 3) // check for 3 clicks
do_something_else();
}
When using this feature, you should realize that multiple clicks will
load the buffer with several PRESSED and RELEASED events. The Multi-
Click buffer does not get updated until a call to GetEvent() occurs.
Therefore, it will take three passes through the loop above before a
triple click shows up. If the do_something() routine for a double click
clears the mouse buffer, then the do_something_else() code will never
execute because a triple click will never occur.
The Repeater allows a button to be pressed and held down to gener-
ate multiple PRESSED events, much like the keyboard does. This would
typically be used for a scrollbar routine. Unlike the MultiClick fea-
ture, the Repeater can be set up for individual buttons. Use the
SetRepeatRate() function to set up the Repeater, and then simply check
for events:
// This will set the intial delay to 250ms and the repeat rate
// to 100ms for the left button:
mouse.SetRepeatRate(LEFTBUTTON, 250, 100);
for(;;)
{
mouse.GetEvent();
if(mouse.LB_Dn())
do_something();
}
The mouse class installs the Repeater by hooking it to the system clock
interrupt which is called every 18ms.
Properly used, event handlers can add a whole new dimension to a
mouse driven interface. However, there are some pitfalls to avoid. If
you set the event mask to trigger on mouse movement, then the handler
will execute quite often (every time the mouse is moved) and could no-
ticeably slow program execution during periods of heavy computation.
Reasons to trigger on movement include updating a motion counter such
as in the demo program, having cursors that dynamically change depen-
ding on the cursor position such as in a GUI, or when using CGC's. As
mentioned before, the handler should not issue a DOS, ROM, or I/O call.
The handler, like a TSR, is an interrupt routine that runs while the
main DOS program is suspended. DOS is not a re-entrant operating sys-
tem and issuing such a call will usually crash the system. Also, some
standard C library routines make use of these system functions (such as
printf()), so often the only way of finding out is by trial and reboot.
-----------------------------------------------------------------------
0x01 MOUSE_MOVED
0x02 LB_PRESSED
0x04 LB_RELEASED
0x08 RB_PRESSED
0x10 RB_RELEASED
0x20 CB_PRESSED
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -