[comp.sys.mac] Question about WDEF routines.

domino@sunybcs (Michael Domino) (01/03/88)

In volume 2 of The Complete MacTutor, page 287 there is an article about
creating WDEF routines.  I would like to be able to do that, so I came
up with the following code that doesn't work, using the article as a
guide.  If I fill in the ProcID field in ResEdit with one of the standard
WDEFs, and comment out everything but the call to GetNewWindow,I get a
window just fine.

Can someone tell me what's wrong, please?  This is all in Lightspeed C.

main()
{
  WindowPtr w;

    ...usual init stuff...
        
        /* The window is created invisible. */          
        w = GetNewWindow(windowID, NULL, (WindowPtr)(-1L));
        (WindowPeek)w->windowDefProc = NewHandle(0L);
        *((WindowPeek)w->windowDefProc) = (Ptr)WindowDef;
        ShowWindow(w);  

     ...event loop stuff...
}

/* I just want a simple window def right now, one that just draws a frame. */
pascal long WindowDef(varCode, theWindow, message, param)
int varCode, message;
WindowPtr theWindow;
long param;
{
  Rect r;
                 
   switch(message)
  {
         case wDraw:
           r = (**((WindowPeek)theWindow->strucRgn)).rgnBBox;
           PenNormal();
           FrameRect(&r);
           InsetRect(&r, 1, 1);
           EraseRect(&r);
           break;
         case wHit:
           break;
         case wCalcRgns:
           break;
         case wNew:
           break;
         case wDispose:
           break;
         case wGrow:
           break;
         case wDrawGIcon:
           break;          
   }       
   return 0L;
}

And while we're on the subject, it says in Inside Mac that the window
definition id passed as a parameter to NewWindow (or set with ResEdit
for GetNewWindow) has the resource id of the WDEF in the upper 12 bits
and a variation code in the lower 4 bits.  This does not seem to jive
with the ids of the standard windows given on page 273 of IM, vol 1,
which are all very small numbers.  What's going on here?


Michael Domino @ SUNY/Buffalo

internet: domino@cs.buffalo.edu

lsr@apple.UUCP (Larry Rosenstein) (01/05/88)

In article <7595@sunybcs.UUCP> domino@sunybcs.UUCP (Michael Domino) writes:
>
>Can someone tell me what's wrong, please?  This is all in Lightspeed C.
>
>main()
>{
>  WindowPtr w;
>
>    ...usual init stuff...
>        
>        /* The window is created invisible. */          
>        w = GetNewWindow(windowID, NULL, (WindowPtr)(-1L));
>        (WindowPeek)w->windowDefProc = NewHandle(0L);
>        *((WindowPeek)w->windowDefProc) = (Ptr)WindowDef;
>        ShowWindow(w);  

One problem is that this code trashes the heap.  It is creating a handle and
replacing the master pointer.  The block that was created contains a back
pointer to the master pointer, which the Memory Manager uses when the heap
needs to be compacted.

I mentioned this to the author of the article and the editor of MacTutor.
Also, I believe there was a letter to the editor regarding this.
Unfortunately, this had no effect because in the article on tear-off menus
(a couple of months ago) the same technique was used.  (I think this was the
same author.)

The correct thing to do is allocate a 6-byte handle and install a 68000 JMP
instruction in the handle, which goes to the defproc.  The first 2 bytes of
such an instruction are $4EF9; the last 4 bytes should be the address of the
defproc procedure.  Then you simply install the handle in the window record.

As you can see, doing the right thing doesn't take much extra code.  In my
opinion, it is purely a matter of laziness on the part of the author.  His
only response is to include a disclaimer saying that this technique
shouldn't be used in a real program.  The end effect is that people type in
the program and it doesn't work.

-- 
Larry Rosenstein

Object Specialist
Apple Computer

AppleLink: Rosenstein1
UUCP:  {sun, voder, nsc, mtxinu, dual}!apple!lsr
CSNET: lsr@Apple.com

olson@endor.harvard.edu (Eric K. Olson) (01/05/88)

In a recent article Larry Rosenstein writes:
>In article <7595@sunybcs.UUCP> domino@sunybcs.UUCP (Michael Domino) writes:
>>    ...usual init stuff...
>>        
>>        /* The window is created invisible. */          
>>        w = GetNewWindow(windowID, NULL, (WindowPtr)(-1L));
>>        (WindowPeek)w->windowDefProc = NewHandle(0L);
>>        *((WindowPeek)w->windowDefProc) = (Ptr)WindowDef;
>>        ShowWindow(w);  
>
>One problem is that this code trashes the heap.  It is creating a handle and
>replacing the master pointer.  The block that was created contains a back
>pointer to the master pointer, which the Memory Manager uses when the heap
>needs to be compacted.
>
>[About how MacTutor has published this technique more than once]
>
>The correct thing to do is allocate a 6-byte handle and install a 68000 JMP
>instruction in the handle, which goes to the defproc.  The first 2 bytes of
>such an instruction are $4EF9; the last 4 bytes should be the address of the
>defproc procedure.  Then you simply install the handle in the window record.

While writing a WDEF I got sick of repeatedly compiling it into a resource
and came up with another method for testing code fragments that need to
be referenced as handles.  I believe it will not trash the heap, and is
correct (if somewhat unorthadox):

If you have a compiler that can specify code segments (LSC, for example),
move the WDEF code into a segment of its own and do a GetResource('CODE',id)
and a DetachResource of the returned handle.  This gives you a private
copy of the code, referenced as a handle.  Detaching it ensures that it will
not be mucked with by the segment loader or resource manager.  You may also
need to HNoPurge the handle, depending on how it is flagged in the resource
file.

This works well for me, and I can't see a reason why it would screw anything
up.  On the other hand, I would never do such a thing after the WDEF is
done being developed, since the WDEF resource/ProcId Window Field combo
works so very well.

-Eric


                      (defun maybe (x) (maybe (not x)))
Eric K. Olson     olson@endor.harvard.edu     harvard!endor!olson     D0760
   (Name)                (ArpaNet)                 (UseNet)        (AppleLink)

lsr@apple.UUCP (Larry Rosenstein) (01/06/88)

In article <3709@husc6.harvard.edu> olson@endor.UUCP (Eric K. Olson) writes:
>
>If you have a compiler that can specify code segments (LSC, for example),
>move the WDEF code into a segment of its own and do a GetResource('CODE',id)
>and a DetachResource of the returned handle.  This gives you a private
>copy of the code, referenced as a handle.  Detaching it ensures that it will
>not be mucked with by the segment loader or resource manager.  You may also
>need to HNoPurge the handle, depending on how it is flagged in the resource
>file.

There is supposed to be a 4 byte segment header in each CODE segment
(indicating the location and number of jump table entries).  Do you do
something special to get around this?

It is possible that for your programs, these 4 bytes ended up being 68000
instructions that don't do anything harmful. (For example, small 2-byte
numbers are OR.B instructions.)  As long as you don't have too many jump
table entries, it should be benign.

Ignoring this problem, you should not have to detach the segment.  The
Window Manager calls LoadResource to ensure that the WDEF is loaded into
memory before calling it, so even if the segment is purged everything will
work.  If you DO detach it, then you will have to call HNOPurge, because it
won't be a resource and LoadResource won't do anything with it.

In MPW, it would be better to read the resource in by name, since then you
don't have to depend on the ID remaining constant.  The MPW linker, by
default, names the resource the same as the segment.

-- 
Larry Rosenstein

Object Specialist
Apple Computer

AppleLink: Rosenstein1
UUCP:  {sun, voder, nsc, mtxinu, dual}!apple!lsr
CSNET: lsr@Apple.com

olson@endor.harvard.edu (Eric K. Olson) (01/06/88)

Folks, I must apoligize:

In a recent article Larry Rosenstein writes:
>In article <3709@husc6.harvard.edu> olson@endor.UUCP (Eric K. Olson) writes:
>>
>>If you have a compiler that can specify code segments (LSC, for example),
>>move the WDEF code into a segment of its own and do a GetResource('CODE',id)
>>and a DetachResource of the returned handle.  This gives you a private
>>copy of the code, referenced as a handle.  Detaching it ensures that it will
>>not be mucked with by the segment loader or resource manager.  You may also
>>need to HNoPurge the handle, depending on how it is flagged in the resource
>>file.

I'm not sure what drugs I was on when I wrote this.  I may have tried it,
but it certainly didn't work.  The reason below is good enough for me:

>There is supposed to be a 4 byte segment header in each CODE segment
>(indicating the location and number of jump table entries).  Do you do
>something special to get around this?

No, I probably gave up on that idea when it crashed....

In fact, I wasn't even testing a WDEF when I tried to incorporate a 
definition procedure's code into the test application.  I was writing
a CDEF!!!  (I wrote a WDEF just before the CDEF, so you can understand
my confusion with myself).  [BTW, CDEFs should be tested the same way
as WDEFs-- the way Larry originally suggested, with a 6-byte jump-to-the-
correct-spot handle].

I have erred.  In correcting the correct, I have erred twice.  In not
recognizing my error, I have erred three times.  I must apply my prime
directive.  Sterilize.  Sterilize.  Ste...ri...lizzzzz..... [Star Trek]

In fact, what I did is this [incorrect technique-- don't use this]:

	GetDItem(theDlg,1,&dType,&dHandle,&dRect);
	control=dHandle;
	
	cdefptr=&cdef;
	(*control)->contrlDefProc=&cdefptr;
	
cdefptr is a global Ptr.  This worked [really!], but not for any good
reason.  In fact, any memory manager calls applied to this "handle" (it's
not a handle) will fail miserably.  I just got lucky.

*Sigh*.  Sorry for the confusion, everybody.

-Eric


                      (defun maybe (x) (maybe (not x)))
Eric K. Olson     olson@endor.harvard.edu     harvard!endor!olson     D0760
   (Name)                (ArpaNet)                 (UseNet)        (AppleLink)

earleh@eleazar.Dartmouth.EDU (Earle R. Horton) (01/09/88)

In article <3709@husc6.harvard.edu>, 
	olson@endor.harvard.edu (Eric K. Olson) writes:
> In a recent article Larry Rosenstein writes:
> >The correct thing to do is allocate a 6-byte handle and install a 68000 JMP
> >instruction in the handle, which goes to the defproc.  The first 2 bytes of
> >such an instruction are $4EF9; the last 4 bytes should be the address of the
> >defproc procedure.  Then you simply install the handle in the window record.

I may be missing something here, but it seems to me the best way to test
a resource containing code is to compile it into a resource right off
the bat.  Make a resource with 68000 code in it, with entry point at the
top, flags, and whatever is needed in the final resource.  At run time,
you load the resource using GetResource(), and there is your handle,
right in the function return value.

I have to admit I haven't done 'WDEF's yet, but I load many types of
code this way, and some even work right the very first time!  I have
even been known to extract a problem function from out of a large
application, place it in a little code resource of its own for easier
debugging and faster recompiling, and LEAVE it there once I finally got
it running.

Let me know if I'm off base here, but doing it right the first time seems
to me to be the least amount of work.

-- 
*********************************************************************
*Earle R. Horton, H.B. 8000, Dartmouth College, Hanover, NH 03755   *
*********************************************************************