Fat Bits
Volume Number: 2
Issue Number: 6
Column Tag: C Workshop
Mouse DA shows off Fat Bits
By Rick Flott, FlottWare, C handler, AZ
Mouse Position Desk Accessory
Introduction
Setting up screen graphics on the Mac can be a very tedious and time consuming
job. Previously I have used "Mouse Position" desk accessories (DA's) to show the
position of the mouse, but none of them had the features I wanted. So as a true
programmer I wrote one myself. I borrowed some ideas from others and added a few
new ones of my own.
First, I must give credit where it is due. I used an old DA called Magnifying Glass
to get a "Fat Bits" view of the screen. I really thought the "Fat Bits" was neat, but
wanted other information. This Mouse Position DA also has this feature, but uses it in a
little different way.
Using the Mouse Position DA
The window created by the Mouse Position DA looks like this -
The top line gives the name of the window the cursor is currently over. The next
two lines (labeled 'L' and 'G') give the local and global coordinates of the mouse (both
horizontal and vertical portions are given). The bottom graphics give a "Fat Bits" view
of the mouse position. There are a few differences between these "Fat Bits" and others
you may have seen.
First, the actual position of the cursor is in the center of the screen (not in the
top left corner). Second, the gray lines symbolize the real point (as you all know from
reading Inside Macintosh - points do not occupy space, they are at the intersection of
infinitely thin horizontal and vertical grid lines). The "hot spot" of the cursor is the
point immediately below and to the right of the gray "crosshairs" (the tip of the
paintbrush of the MacPaint Icon shown in the window). The "crosshairs" do not cover
up any pixels, they just split the rectangle surrounding the cursor into four planes.
There are also a few other features in this DA -
• when the mouse is over the desktop, the window name is set to "DeskTop" and no
local coordinates are shown (since they don't exist).
• the window name, local, and global coordinates can be "Cut" or "Copied" to the
clipboard and "Pasted" into your program.
• when the CapsLock key is down and the coordinates are Cut or Copied, they are
appended to the clipboard. This is very handy when setting up rectangles or other
complex graphics that require multiple points. Just press CapsLock and start
Copying!
• remember - in most applications Cut and Copy only work on the topmost
window, hence the Mouse Position window must be in front for these commands to
work.
Code Description
As in any desk accessory, 5 routines must be present -
• open (initializes the DA)
• close (stops the DA)
• control (receives commands from system)
• prime
• status
The latter two routines do not need to perform any functions, but they must be
present.
The open routine in the Mouse Position DA is the "main()" of this C program. It
allocates the DA window from the heap, sets up the font for this window, and draws the
"static" portion of the window. It also stores the reference number of the DA in the
windowKind field of the window. This is very important since this the only way the Mac
knows that this is a " system" window and to pass the proper events to it and not to the
running application. In addition to this, the pointer to the window is kept in the device
control entry for retrieval later on. Remember, a desk accessory is viewed as 5
separate routines called by the system (unlike an application).
The Close routine does the opposite of the open. It releases the memory used by
the window and resets the proper fields in the device control entry record.
The Control routine does all the work. It takes commands from the system
(passed to it when the application calls SystemTask, SystemEdit, or SystemClick) and
processes them. In this DA, only two commands are processed - accRun (periodic
command telling the DA to run) and accEvent (command telling the DA to handle an
event). As you will see later on, this DA is set up to run as often as possible so that the
mouse position coordinates appear to be updated continuously in the window.
This routine starts off by obtaining the window pointer from the device control
entry and parsing the command (CSCode) sent to it. If it is a run command (accRun)
then the new mouse position is displayed (by calling the routine dspMousePos). If it is
the event command (accEvent) then the doEvent routine is called.
The doEvent routine handles 4 types of events - keyDown, autoKey, updateEvt,
and activateEvt. The updateEvt will re-draw the coordinates and the window name. The
activateEvt only re-draws the coordinates. The keyDown and autoKey events do most of
the work in this code. When either of these events occur, a string is allocated, the
coordinates and window name placed into the string (with the Munger ROM call), and
moved to the clipboard.
Why did I append perfectly good strings into another string? I'm lazy I guess
(plus the string is temporarily allocated off the heap and is released immediately).
Notice that if the Cut or Copy keys are capitals (the CapsLock key is down) then the
current TEXT contents of the clipboard are placed in the string. This allows the user to
"append" coordinates together as previously described.
The dspMousePos routine first gets the current coordinates of the mouse. If the
mouse has moved from the last time this routine was called, then processing continues,
otherwise this routine just returns. This keeps down the "flicker" of the display, since
the window is only updated when the mouse moves. It also keeps the DA from being a
CPU hog.
Next, the name of the window the mouse is currently over is retrieved. If it is a
different window than the last time, the new name is displayed (by the routine
dspWindowTitle).
If the mouse is not on the desktop, then the local coordinates must be recomputed.
GetMouse gives the mouse in local coordinates of the active window, not the window the
mouse is currently over (which may be inactive or even invisible). Hence, the local
coordinates must be recomputed with respect to the window the mouse is over. The rest
of this routine is straightforward.
The coordinates are converted to strings, the "h" and "v" characters are appended
to them, and these strings are displayed right justified in the window. Next, the four
"Fat Bit" rectangles are displayed. Notice that 24 pixels surrounding the cursor in the
horizontal direction and 16 pixels surrounding the cursor in the vertical direction are
displayed (using CopyBits) in the window.
The dspWindowTitle routine first determines if the window passed to it is the
desktop or a real window. If it is a real window, then its name is retrieved. Otherwise
the name "DeskTop" is used. It then displays this name center justified in the DA
window. It also adds a carriage return ('\n') to the name so that when the name is Cut or
Copied to the clipboard, the coordinates will be on the next line (like in the window).
The drawWindow routine just draws the "static" portion of the window. This
includes the "G" and "L" characters, the horizontal dividing lines, and the gray
"crosshairs".
Consulair's DeskMaker
Now all of you desk accessory veterans are saying - "this stuff will never work,
look at all of the global variables this guy has in a DA". Well, an application called
DeskMaker by Consulair (included with their Mac C development system) takes alot of
the headache out of writing DA's. After talking with Bill Duvall, he informed me that
the way DeskMaker works is that it takes all of the global variables (referenced off of
A4 by the #Options R=4 line) and makes them part of the DRVR resource (he appends
them to the end of the code). This allows them to be global and still be accessed by the
code in the DA. Of course, I didn't call him until I had already stayed up all night
allocating my globals from the heap and accessing them through a handle. Oh well, it
wasn't the first time I threw out code, and it won't be the last.
In addition to this, DeskMaker takes commands from a ".desk" text file to set up
the DA's flags and header constants they require. You can turn any flag on or off, set up
the other constants (like drvrDelay, drvrMask, etc.), and specify the names of the
standard routines (Open, Close, Control, etc.) He has also added another command "Test
which allows you temporarily install the DA in the system menu to try it out. This is
nice since you do not have to use Font/DA Mover or any of the other applications/DA's
that run DA's from a file.
The "MousePos.Desk" listing shows the commands sent to DeskMaker for this DA.
First, the filename, map, and name of the DA is set up. Next, the names of the 5 routines
are defined. Finally, the drvrEMask, drvrDelay, and drvrFlags are set up. This DA was
given the ID of 22 and the test flag was set to allow debugging. Since most of the C
compilers handle DAs differently, this information should allow you to recreate this DA
on other compilers.
MousePos.c Lisiting
/*************************************************************
MousePos.c
This is a desk accessory that shows three things about the
position of the mouse cursor -
- Name of the window the cursor is currently over
- Local and global coordinates of the cursor
- "Fat Bits" display of a rectangle around the cursor
It also allows the user to Copy or Cut the window name and the
local & global coordinates to the clipboard (CapsLock-Copy or
CapsLock-Cut will append the coordinates to the clipboard).
Written by: Rick Flott Mac C (Consulair)
V 4.5
*************************************************************/
#Options R=4 L=500 F=8000 Z Q=0 O=200
#include "MacCDefs.h" // Mac ROM data structure def's
#include "Events.h
#include "Window.h
#include "Font.h
#include "TextEdit.h
#include "Osmisc.h
#include "Osio.h
#include "Desk.h
/*----------------------------------------------------------------
Global Data
-----------------------------------------------------------------*/
/* ------ Constants ----- */
#define FALSE 0
#define TRUE 0xFF
/* ------- Types ------- */
typedef struct // 6 char strings for the coord's
{
char count;
char s[6];
} Str6;
/* ---- Rectangles ---- */
Rect titleRect = { 0, 0, 10,100}, // Window title rect
localStrRect = { 11, 1, 21, 6},
localHRect = { 11, 6, 21, 49}, // Local horiz coord's
rect
localVRect = { 11, 52, 21, 94}, // Local vert coord's
rect
globalStrRect = { 21, 1, 31, 6},
globalHRect = { 21, 6, 31, 49}, // Global horiz coord's
rect
globalVRect = { 21, 52, 31, 94}, // Global vert coord's
rect
fBTopLeftRect = { 32, 0, 64, 48}, // "Fat Bits" rect's
fBBotLeftRect = { 68, 0,100, 48},
fBTopRightRect = { 32, 52, 64,100},
fBBotRightRect = { 68, 52,100,100},
windowRect = { 50, 5,150,105}; // DA window rect
/* ----- Strings ------ */
char deskTopTitle[] = {"\pDeskTop"}; // Constant desktop str
Str6 localVStr, localHStr, // Local coord strings
globalVStr, globalHStr; // Global coord strings