Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

Quickdrawing With XCMDs


MAY89: QUICKDRAWING WITH XCMDS

Jay is a professor of computer science in the department of mathematics and astronomy and can be reached at Franklin & Marshall College, Lancaster, PA 17604-3003. Before joining the F&M faculty, he was a software developer at Tymlabs Corp., Austin, Tex. and the director of Academic Computing and a professor of chemistry at Bryn Mawr College in Bryn Mawr, Penn.


It's no secret that XCMDs (eXternal CoMmanDs) can be used to provide access to the Macintosh Toolbox. Since the introduction of HyperCard, in fact, a variety of XCMDs that provide access to dialog boxes, the standard Macintosh file system, and the serial port have been published. In this article, I will examine a few examples of using external commands to provide access to QuickDraw.

Why is access to QuickDraw useful? The process of drawing with HyperTalk is directly tied to the painting tools that are provided with HyperCard. That is, to draw a straight line, you select the line tool and drag it from the starting to the ending point. To draw a straight line using one of the available painting patterns, select a brush tool, then a pattern, and finally drag the brush tool from the starting to the ending point. The drawing that has been done appears on the card and, unless the stack has been protected against writing, will be saved with the card when the stack is closed.

It is clear (even to a casual observer) that something happens in HyperCard when you select any of the painting tools. There is a noticeable pause, the cursor turns into the watch, and the menu bar (if visible) expands to include the paint and options menus. A similar transition takes place in reverse when you abandon the painting tools for the browse tool. In addition to the time penalty that you pay when you select a painting tool and then abandon it, the appearance of the card or stack also suffers. While the painting tools are being selected, the cursor flickers and bounces from browse to brush and back; the menubar extends to reveal two additional menus and then contracts when the painting tools have been abandoned. Although this activity is necessary and helpful when you are doing the painting, it is unattractive and distracting when you only expect to watch a drawing appear.

If you could avoid using the painting tools and make calls to QuickDraw directly, you could avoid both the time penalty in loading and unloading the painting tools, and the aesthetic penalty you experience while the cursor and menubar flicker and jump.

Access to QuickDraw can also be dangerous. QuickDraw operations take place on the screen, not on the card in a HyperCard stack. The person using a stack may not be able to distinguish between a display drawn with QuickDraw routines and a display drawn with HyperTalk painting tools; but the drawing made with the painting tools is (in principle) savable on the card in the stack, whereas the drawing made with QuickDraw routines is not. Furthermore, mixing QuickDraw routines with painting tools can lead to undesired results. Loading the painting tools, for example, refreshes the display so that only the card contents are visible; any drawing made on the screen with QuickDraw routines will disappear. Finally, the user of QuickDraw must be careful to confine the drawing to the active window on the screen, whose dimension is the same as the HyperCard card. The user of QuickDraw cannot draw on the card and cannot draw outside the region bounded by the card.

In a HyperCard tutorial, designed to demonstrate and teach the features of Graphic Session (a commercial terminal emulator), I needed to draw a gray cross hair on the display. When the mouse was clicked, I needed to erase the first gray cross hair and to draw another gray cross hair at the mouse position. Using HyperTalk and painting tools led me to the two scripts shown in Listing One.

Although these scripts serve to provide the necessary functionality, they are slow to execute and distracting to watch. Furthermore, this is just the situation where drawing on the screen and not on the card is useful: I do not want cross hairs left behind on the card after clicking. The user is supposed to practice clicking, thereby moving the cross hair; the user is not supposed to alter the tutorial stack. Consequently, I appealed to QuickDraw directly and developed two brief XCMDs --for pen movement with and without drawing. The XCMD that performs pen movement without drawing is called XMoveTo, and it is a simple "cover" for the QuickDraw routine MoveTo. The XCMD that performs pen movement with drawing is called XLineTo; besides serving as a "cover" for the QuickDraw routine LineTo, XLineTo also includes the ability to change the pen pattern and drawing mode. Therefore, XLineTo can both draw and erase. The syntax of these two XCMDs is shown in Listing Two. The new HyperTalk scripts for drawing the initial cross hair and erasing and redrawing the cross hair on a mouse click are shown in Listing Three.

These scripts are no more complex than the script using the painting tools; the drawing takes place faster with the XCMD script than with the tools script, and there is no distracting change to the cursor or menu bar while the drawing takes place.

The two XCMDs were developed using Think Technology's Lightspeed C (Version 3.0). The C source code for XMoveTo is shown in Listing Four.

The source code for XLineTo, shown in Listing Five, is slightly more complex; it permits a variable number of parameters (two are required, the third and fourth are optional) and it must select a QuickDraw pen pattern and pen drawing mode.

There are four steps to making a HyperTalk script using an XCMD, which is written in Lightspeed C. Two of these steps are handled within Lightspeed C itself.

    1. To construct a Lightspeed "project," load the appropriate Macintosh libraries, following the guidelines in the Lightspeed C manual. Then create and edit the source code file. Finally, build or make the project within Lightspeed C.

    2. Next, use Lightspeed C to construct an XCMD instead of an application. The XCMD is left -- as a pure code resource -- in a file.

    3. The third step in constructing the HyperTalk script using an XCMD requires a resource editor, such as ResEdit. By using ResEdit, you can move the XCMD resource, such as XMoveTo, from the file in which Lightspeed C leaves it into a HyperCard stack. These steps are repeated in order to create and embed each XCMD in the same stack. The XCMD's XLineTo and XMoveTo are now available anywhere within the stack, but in no other stack. If I wanted the XCMDs to be available to other stacks as well, I could have embedded them in the home stack or in HyperCard itself.

    4. The fourth step is to write HyperTalk scripts, as shown earlier, to use these XCMDs in order to achieve faster, less detracting drawing.

On a Macintosh with sufficiently large memory (probably 2.5 Mbytes or larger), you can run HyperCard, Lightspeed C, and ResEdit all under MultiFinder, and quickly bounce from editing and compiling to managing resources to constructing HyperTalk scripts. On a smaller Macintosh, or without the use of MultiFinder, you will have to perform these steps separately by quitting each application before starting another.

The same procedures that have been discussed here for using QuickDraw LineTo and MoveTo procedures from a HyperCard stack can be applied to other QuickDraw procedures as well. Although it did not come up in the design of the Graphic Session tutorial, I might equally well have wanted to demonstrate the positioning of a conventional box-like alpha cursor by a mouse click. In this situation, instead of drawing a gray cross hair at the location of the mouse click, I would draw a small black rectangle at the location of the mouse click. This leads naturally to the development of an XCMD, called XBox, which covers the QuickDraw procedure PaintRect, as shown in Listing Six.

Finally, some amusing effects can be achieved by combining QuickDraw operations on the screen with the painting of similar objects on the card. For example, the small rectangle painted with the XCMD XBox can be repeated often, leading to the effect of animation. This animated rectangle can then appear to come to rest in the form of a rectangle that is actually painted on a card. The script in Listing Seven illustrates this action: for this example, the animated rectangle painted by the XCMD XBox appears on the screen in front of a blank card; after the animation sequence is finished, a card with a painted rectangle is brought into view.

The construction of XCMDs that cover QuickDraw procedures can be accomplished easily within either C or Pascal, and can be done largely by rote. The use of XCMDs for access to QuickDraw allows you access to faster, but transient drawing.

Notes

    1. Graphic Session, software for the Macintosh that emulates an Hewlett-Packard HP 2393A monochrome graphics terminal, is available from Tymlabs Corp., 811 Barton Springs Rd., Austin, TX78704; 512-478-0611; the HyperCard tutorial for this product is available from the author, or for downloading on GE-nie and CompuServe.

    2. Version 3.0 of THINK C (Symantec Corp., 10201 Torre Ave., Cupertino, CA 95014; 408-253-9600) is MultiFinder compatible. Its predecessor, Lightspeed C Version 2.0x, was not.

    3. For well-worked examples in Lightspeed C, see Gary Bond, XCMDs for HyperCard (MIS Press, 1988).

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063; or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number and format (MS-DOS, Macintosh, Kaypro).

_Quickdrawing With XCMDs_ by Jay Martin Anderson

[LISTING ONE]

<a name="00f2_0007">


   on openCard
      -- draw the crosshair at 400, 200 when card is
      -- first displayed
      global oldH, oldV
      -- keep location of crosshair in global vars
      put 400 into oldH
      put 200 into oldV
      choose brush tool
      set the brush to 4
      set the pattern to 22
      drag from oldH-8,oldV to oldH+8,oldV
      drag from oldH,oldV-8 to oldH,oldV+8
      choose browse tool
   end openCard

   on mouseUp
      -- erase crosshair where it was
      -- draw crosshair at new location
      global oldH, oldV
      choose eraser tool
      drag from oldH-8,oldV to oldH+8,oldV
      drag from oldH,oldV-8 to oldH,oldV+8
      choose brush tool
      set the brush to 4
                set the  pattern to 22
      get the mouseH
      put it in oldH
      get the mouseV
      put it in oldV
      drag from oldH-8,oldV to oldH+8,oldV
      drag from oldH,oldV-8 to oldH,oldV+8
      choose browse tool
   end mouseUp





<a name="00f2_0008"><a name="00f2_0008">
<a name="00f2_0009">
[LISTING TWO]
<a name="00f2_0009">

   XMoveTo x,y
   -- x and y are coordinates in pixels in the usual
   --  QuickDraw coordinate system.
   XLineTo x,y,pen,mode
   -- x and y are coordinates as for XMoveTo; pen is
   -- the number of a QuickDraw pen pattern, selected
    -- from the system pattern list as shown on page I-474
        -- of Inside Macintosh; mode is 0 to erase and 1 to
   -- draw.  If mode is omitted, it is presumed to be 1
   -- (draw); if pen is omitted, it is presumed to be 1
   -- (solid black).





<a name="00f2_000a"><a name="00f2_000a">
<a name="00f2_000b">
[LISTING THREE]
<a name="00f2_000b">

   on openCard
      -- draw crosshair at 400, 200 when card is
      -- first displayed
      global oldX, oldY
      -- maintain position of crosshair in global var
      put 400 in oldX
      put 200 in oldY
      XMoveTo oldX-8,oldY
      XLineTo oldX+8,oldY,4,1
      XMoveTo oldX,oldY-8
      XLineTo oldX,oldY+8,4,1
   end openCard

   on mouseUp
      -- erase former crosshair and draw new crosshair
      -- at position of mouse-click
      global oldX, oldY
      -- erase former crosshair; pattern 4 (gray);
      -- mode 0 (erase).
      XMoveTo oldX-8, oldY
      XLineTo oldX+8, oldY,4,0
      XMoveTo oldX, oldY-8
      XLineTo oldX, oldY+8,4,0
      -- draw new crosshair; pattern 4 (gray),
      -- mode 1 (draw).
      put the mouseH in oldX
      put the mouseV in oldY
      XMoveTo oldX-8,oldY
      XLineTo oldX+8,oldY,4,1
      XMoveTo oldX,oldY-8
      XLineTo oldX,oldY+8,4,1
   end mouseUp





<a name="00f2_000c"><a name="00f2_000c">
<a name="00f2_000d">
[LISTING FOUR]
<a name="00f2_000d">

   #include <Types.h>
   #include <MemoryMgr.h>
   #include <OSUtil.h>
   #include <QuickDraw.h>
   #include "XCmd.h"
   pascal void main(paramPtr)
   XCmdBlockPtr paramPtr;
   {
      short x,y;
      Str31 str;
      /* There are two parameters to the XCMD XMoveTo:
       * the x and y coordinates to which to move.  These
       * are found as C-strings (null-terminated
       * strings), converted to Pascal-strings, and then
       * to integers.  There must be just two parameters;
       * if there are more or less, nothing happens.
       */
      if (paramPtr->paramCount == 2)
      {
         ZeroToPas(paramPtr,
            (char *)*(paramPtr->params[0]),
            (StringPtr)&str);
         x = StrToNum(paramPtr, &str);
         ZeroToPas(paramPtr,
            (char *)*(paramPtr->params[1]),
            (StringPtr)&str);
         y = StrToNum(paramPtr, &str);
         MoveTo(x, y);
      }
   }






<a name="00f2_000e"><a name="00f2_000e">
<a name="00f2_000f">
[LISTING FIVE]
<a name="00f2_000f">


   #include <Types.h>
   #include <MemoryMgr.h>
   #include <OSUtil.h>
   #include <QuickDraw.h>
   #include "XCmd.h"
   pascal void main(paramPtr)
   XCmdBlockPtr paramPter;
   {
      short x, y, pat, mode;
      Str31 str;
      Pattern thePattern;
      PenState oldPenState;
      /* There are four possible parameters; two are
       * required.  The first two (required) are the
       * x and y coordinates to which to draw.  The
       * third is the pattern, taken from SysPatList,
       * (see Inside Macintosh I-474); the fourth is
       * the mode (0 = erase, 1 = draw).  The four
       * parameters are found as C-strings (null-
       * terminated strings), converted to Pascal-
       * strings, and then to short integers.  If the
       * fourth parameter is omitted, it is presumed
       * to be "1" for draw.  If the third parameter
       * is also omitted, it is presumed to be "1" for
       * black.  If there are fewer than 2 or more than
       * 4 parameters, nothing happens.
       */
      if ((paramPtr->paramCount < 2) ||
         (paramPtr->paramCount > 4)) return;
      ZeroToPas(paramPtr, (char *)*(paramPtr->params[0]),
         (StringPtr)&str);
      x = StrToNum(paramPtr, &str);
      ZeroToPas(paramPtr, (char *)*(paramPtr->params[1]),
         (StringPtr)&str);
      y = StrToNum(paramPtr, &str);
      if (paramPtr->paramCount < 3)
         pat = 1;
      else
      {
         ZeroToPas(paramPtr,
            (char *)*(paramPtr->params[2]),(StringPtr)&str);
         pat = StrToNum(paramPtr, &str);
      }
      if (paramPtr->paramCount < 4)
         mode = 1;
      else
      {
         ZeroToPas(paramPtr,
            (char *)*(paramPtr->params[3]),
             (StringPtr)&str);
         mode = StrToNum(paramPtr, &str);
      }

       /* preserve former pen state */
      GetPenState(&oldPenState);

      /* set pen state to our pattern and mode */
      GetIndPattern(&thePattern, 0, pat);
      PenPat(thePattern);
      if (mode) PenMode(patCopy);
      else      PenMode(patBic);

      /* draw the line */
      LineTo(x, y);

            /* restore pen state */
      SetPenState(&oldPenState);
   }






<a name="00f2_0010"><a name="00f2_0010">
<a name="00f2_0011">
[LISTING SIX]
<a name="00f2_0011">
/*
   XBox -- a HyperCard user-defined command in C.
   J. M. Anderson, 1988
   All Rights Reserved.
   This XCMD draws a rectangle.
*/
#include <Types.h>
#include <MemoryMgr.h>
#include <OSUtil.h>
#include <QuickDraw.h>
#include "XCmd.h"

/* **** WARNING:  DO NOT USE GLOBAL VARIABLES! **** */

pascal void main(paramPtr)
   XCmdBlockPtr paramPtr;
{
   short   x, y, u, v, pat, mode;
   Rect box;
   Str31 str;
   Pattern thePattern;
   PenState oldPenState;

   /* The six parameters are the x and y coordinates of
         * the upper left corner of the box, its width and
    * height,the pattern (taken from the SysPatList)
    * and the mode with which to draw.  These are found
    * as C-strings, converted to P-strings, and then to
    * numbers.  The "mode" is either 0 to erase or 1 to
    * draw.  There may be four, five, or six
    * parameters.  If the sixth parameter is omitted, it
    * is presumed to be "1" for draw.  If the fifth
    * parameter is omitted,it is presumed to be "1" for
    * black.  The x and y coordinates and sizes u and v
         * must be present.  If there are fewer than 4 or more
    * than 6 parameters, nothing happens.
    */
   if ((paramPtr->paramCount < 4) ||
      (paramPtr->paramCount > 6)) return;
   ZeroToPas(paramPtr,(char *)
      *(paramPtr->params[0]),(StringPtr)&str);
   x = StrToNum(paramPtr,&str);
   ZeroToPas(paramPtr,(char *)
      *(paramPtr->params[1]),(StringPtr)&str);
   y = StrToNum(paramPtr,&str);
   ZeroToPas(paramPtr,(char *)
      *(paramPtr->params[2]),(StringPtr)&str);
   u = StrToNum(paramPtr,&str);
   ZeroToPas(paramPtr,(char *)
      *(paramPtr->params[3]),(StringPtr)&str);
   v = StrToNum(paramPtr,&str);
   if (paramPtr->paramCount < 5)
      pat = 1;
   else
   {
      ZeroToPas(paramPtr,(char *)
         *(paramPtr->params[4]),(StringPtr)&str);
      pat = StrToNum(paramPtr,&str);
   }
   if (paramPtr->paramCount < 6)
      mode = 1;
   else
   {
      ZeroToPas(paramPtr,(char *)
         *(paramPtr->params[5]),(StringPtr)&str);
      mode = StrToNum(paramPtr,&str);
   }

   /* preserve pen state */
   GetPenState(&oldPenState);

   /* set pen state to our pattern and mode */
   GetIndPattern(&thePattern, 0, pat);
   PenPat(thePattern);
   if (mode) PenMode(patCopy);
   else      PenMode(patBic);

   /* draw the box */
   SetRect(&box, x, y, x+u, y+v);
   PaintRect(&box);

   /* restore the pen state */
   SetPenState(&oldPenState);
}





<a name="00f2_0012"><a name="00f2_0012">
<a name="00f2_0013">
[LISTING SEVEN]
<a name="00f2_0013">

on mouseUp
   -- Effect animation of a small black rectangle.  This
   -- card is blank; the next card has a small black
   -- rectangle painted on the card at location 298, 167.
     -- draw and erase many times a QuickDraw black
        -- rectangle; its location starts at the left edge
     -- of the card (x = 5), and 167 pixels down.  It
     -- moves to x = 298, y = 167.
  put 3 into x
  repeat while x < 298
    XBox x, 167, 10, 10, 1, 1   -- draw
    XBox x-5, 167, 10, 10, 1, 0   -- erase
    put x + 5 into x
  end repeat
   -- now show next card with rectangle painted in place
  go to next card
end mouseUp













Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.