TweetFollow Us on Twitter

Resource Utility
Volume Number:1
Issue Number:12
Column Tag:A Resource Utility DA with TML Pascal

A Resource Utility DA with TML Pascal

By Alan Wootton, President , Top-Notch Productions, MacTutor Contributing Editor

The subject this month is a sample Desk Accessory, but the real news of note is the emergance of a Pascal compiler for the Mac. The Desk Accessory (DA) presented here will be compiled with TML Pascal and is installed using Apples Font/DA Mover program. The DA is called ShowRes and is used to view the resources that are open on the heap. Between the compiler, the desk accessory, and the subject of resources there is more than can be presented in one month. The strategy will be to discuss the overall structure of DAs, how to compile, and details on resources. We'll put off until later the complete exposition of desk accessories. In order to show the overall structure of a DA I have a MacPascal program that will emulate the systems treatment of DAs. I will present this program next month.

How to Compile

The compiler is not a simple, one-application system, like MacPascal is. The TML Pascal compiler works with what I call the MDS system. This means it is compatable with the Assembler, Linker, and Editor sold by Apple. As such, there are several applications you run before a completed program is produced. First there is the editor. The editor is the application EDIT that edits files of type TEXT. This same editor is provided with several other programs, and you can also use any editor that will create TEXT type files. MacWrite for instance. With the editor you create the Pascal source code, and the Rmaker source (if needed). In figure 1 I have provided a diagram of what steps you might use in two different situations.

The next stage is the compiler. The compiler will produce assembly language, if desired, but for convenience you may skip the assembly phase and make .rel files directly (.rel is the assemblers normal output). The compiler comes with everything except the assembler, so, if you want to assemble you must get the assembler from Apple. The Consulair C compiler can also be used as an assembler in this system (Consulair also has a more professional linker that you can use). After you have converted your code to .rel files you invoke the linker. The linkers normal output is a file with CODE type resources in it. Other resources may be copied in from another file if desired. Depending upon your situation, you may be done. If not, Rmaker is used to create resources for use by the program (there are other ways also). If you are writing a DA you use Rmaker to create your DLOG and other resources, and also to copy the CODE 1 resource from the linker output file and convert it into a DRVR resource.

Figure 1, Compilation Steps

With six programs involved I can't very well describe the detailed operation of each individually. So, I will make it brief. Compiling ShowRes consists of following the sequence illustrated in figure 1 for DAs. Use the files ShowRes.pas, and ShowRes.R. Both are listed below. There is an option on one of the compiler menus that you choose if you are making a DA and not an application. Don't forget it, or you will end up with the wrong kind of code. Figure 2 shows all the files involved in creating ShowRes.

Figure 2, files used in the creation of ShowRes

When you invoke the DA option of the compiler it causes your program to be linked with a header that is in assembly language. The structure of this header matches what is described in Inside Mac for the structure of DAs in assembly language (Inside Mac does not even hint that DAs can be done in Pascal). This DA header is shown below:

;---------------------------------------
;---File AccHead.asm
;---------------------------------------
;---This is a generic Desk Accessory
;---interface for Pascal.
;---------------------------------------
;---It is linked with a .rel module 
;---containing the three xdefs  
;---Open, Ctl, and Close.  These must 
;---take two pointers on the stack.
;---------------------------------------
;---written 9/85 by Alan Wootton.
;---------------------------------------
 INCLUDE  MDS:SysEquX.D
 INCLUDE  MDS:ToolEquX.D
 INCLUDEMDS:QuickEquX.D
 INCLUDEMDS:FSEqu.D
 INCLUDEMDS:MacTraps.D

; declare the three Pascal procedures
; that are used for a Desk Accessory
 
 xref open
 xref close
 xref ctl

; Driver flags and information
; those below should fit most needs
;---------------------------------------
;Flags are: 
; 1- dNeedTime: recieve periodic service
; 2- dCtlEnable: can take control calls
;All other flags are not set.
;
; service rate is one call every second.
; menuID is filled by open if used

 dc.w $2400 ; Flags 
 dc.w $003C ; service rate
 dc.w $FFFF ; event mask
 dc.w $0000 ; MenuID goes here
  
; Entry point offset table

 dc.w   OrnOpen  ; open routine
 dc.w   OrnDone  ; prime
 dc.w   OrnCtl   ; control
 dc.w   OrnDone  ; status
 dc.w   OrnClose ; close

OrnOpen 
 moveM.la0/a1,-(sp);save regs
 move.l a1,-(sp) ;stack Device
 move.l a0,-(sp) ;stack Param 
 jsr    Open;visit Pascal land
 bra.s  orndone

OrnClose
 moveM.la0/a1,-(sp);save regs
 move.l a1,-(sp) ;stack Device
 move.l a0,-(sp) ;stack Param 
 jsr    Close    ;visit Pascal land
 ;bra.s orndone

Orndone
 moveM.l(sp)+,a0/a1;restore regs
 clr.l  d0
 rts

OrnCtl  
 moveM.la0/a1,-(sp);save regs
 move.l a1,-(sp) ;stack Device
 move.l a0,-(sp) ;stack Param 
 jsr    Ctl ;visit Pascal land
 
 moveM.l(sp)+,a0/a1;restore regs
 move.l jIODone,-(sp)
 rts

    end ;of assembly
;---------------------------------------
;-end of file
;---------------------------------------

Don't be put off by the many steps described to make a program. If all you want is a "plain vanilla" Pascal program, then there is a compiler option that will open a window, initialize the system for you, and allow regular readln and writeln in that window. You may also draw in the plain vanilla window if you wish. The whole bit with Rmaker is just for those who wish to make a Mac-style program with menus and windows and everything. Most simple MacPascal programs can be compiled with very little modification this plain vanilla way.

What a Desk Accesssory is

If you have a Mac and you are reading this publication you undoubtedly know what a DA looks like. You may not be aware of what its structure as a program is. The main difference between a DA and an application, from a programming standpoint, is that a DA has no main procedure. There is no main loop that obtains events, and there are no global variables. It is easiest if you imagine that you have written a standard program shell that does some of the parts of a program and that your task is to complete three routines to do the rest. The three routines are Open, Close and Ctl (short for Control). The system will call Open whenever the DA is selected from the Apple menu, and it will call Close when the close box on the window is clicked, or when someone calls CloseDeskAcc (see desk manager chapter of Inside Mac). Between these two the system will call Ctl to enable your DA to handle certian occasions.

Two records are passed to DA routines by the system. They are the Parameter Block Record and the Device Control Entry (DCE). The Parameter Block is the same record used in file manager calls and device manager calls (a DA is a type of driver). A new parameter block is formed, by the system, for each call the DA recieves. Its main use is the csCode field, which indicates the purpose of the control calls. Some values of csCode are predefined (and are declared in the program), others are available for your use (if you wanted to communicate between DAs, for instance). The Device Control Entry is created by the system for it to use to keep track of your DA. Certain of the fields are available for your use. DctlStorage, dctlWindow, dcltEmask, and dctlMenu are safe to modify. The others are for use by the system. I have redeclared the DCE record in ShowRes because it is convient for dctlStorage to be defined as a handle to my data and not simply as a handle to a byte.

Besides the issue of control, there is the problem of data. A DA has no global data. You cannot simply say VAR and then list the variables you are going to use, they would get destroyed at the end of that procedure. The system does, however, provide a facility for you. The Device Control Entry. You use this table as your only source of permanent storage. It is manditory to put your window pointer into dctlWindow and you may use dctlStorage as a handle to additional data. Once you get the idea of what's going on you will realise that there are a great many pathological conditions to be dealt with. How much is done by the system, and how much is left for the DA? For instance, do you need to ever call DragWindow? If you look at the code below you will see many strange things. Why is the windowKind field set? I could cover some of these here. If I did then this article would be twice the length it is, so, I'm going to try to list everything there is to know next month. You should be able to make simple DAs by following the example given, at least for a while.

One issue won't wait. A DA is installed in the system file, and after it is installed it no longer has a file or an icon. So, where are it's resources? A DAs resources are transferred along with it into the system file it resides in. In order to prevent collisions among the various DAs Apple has come up with a funky scheme for numbering the resources owned by DAs. "All DAs will have 32 resource IDs allotted to them. The IDs for DAs will begin at $C000 and number upwards." This means that if you are DRVR 16 then your resource numbers will begin at $C000+32*16. All resources outside this range will not be transferred by the Font/DA Mover! Note that if the font/DA Mover decides to change the number of your DA it will also change the numbers of all your resources. So, you must calculate the ID of your resources at run time. This calculation is performed in the open routine of ShowRes and the result stored in the dctlMenu field of the DCE. Note that the dctlRefNum is not exactly the ID of the driver, to convert use the formula ID:=-1*(1+dctlRefNum).

The font/DA Mover tries hard, but does an imperfect job of keeping your resources in correct reference to each other. It changes the part of a DLOG resource that refers to a DITL but when renumbering MENU types it does not change the menuID field of the menu record. This is the purpose of the patch you see in the Activate procedure of ShowRes. Apple claims that DLOG and ALRT references to DITLs are renumbered correctly, and that DITL references to ICON, PICT, and CTRL are right. References by MENUs to MDEFs are supposed to work, but you can bet all other cross references are left incorrect. Your average DA will get by just fine using only dialogs and menus, so lets leave these problems to advanced users.

Resource Rundown

Cruising through Inside Macintosh one is likely to get the impression that resource files are some mysterious place that window and menu definitions go. The thing to remember is that resource files are files. It is helpful to imagine what you would do if you were writing a program that maintained 100 or 200 pieces of information of all different sizes. What would you do? You could write the data into a file with length words and then traverse the data like a list of strings. In addition to the length, you might include some identifying information. Another way would be to have an array of offsets into the file, and to use this to find individual items. Eventually, you will find the need to read and write single pieces of the data without traversing, or loading, the whole file. In general you would want a complete and bullet proof collection of routines to handle these functions. Fortunately, Apple has provided a set of traps to handle these kind of things for us.

What Apple has done is to arrange references to the pieces of data into a "map" at the end of the file. The map contains information relating to the file manager access path for the file being used, as well as a complete list of the data pieces, or resources, contained in the file. As an added bonus, the data is broken into broad categories called "types" and sub-categories refered to by using an "ID". It is as if Apple created a whole filing system contained within a file (just as the file manager maintains files in a volume). The pieces of data can be recalled by the order they were put in, ie. "give me the 5th resource". Use GetIndResource for this. Or, the data items have IDs so you can recall them by ID match, "give me the resource with ID #1234". Use GetResource for this. As an example, when you open a dialog with ID #257, the dialog manager makes the call GetResource('DLOG',257) to get the information it needs.

When the file manager creates a file it initializes two lists of disk space for that file. Either of these lists can be empty (or both! it is possible to have files that occupy 0 K of disk space). The nomenclature "Data fork" and "Resource fork" are used to refer to these two lists. When the resource fork of a file is opened (by calling OpenResFile) the map is read into memory. A field in the map known as the "resource file reference number " is set to the ioRefNum of the open file. The maps of all open resource files are arranged in a list in memory. New files opened are added to the head of the list. One may call GetResource or GetNamedResource to retrieve the individual resources. Beware of the fact that since all open maps are in the list, the resource manager will return the first resource it finds that fits the description of what it is looking for. It is possible to avoid searching some of the maps at the head of the list, but in general it is difficult to search only one map. In order to survey all the resources in all the maps a function named GetIndResource is provided. A count of the total number of resources in all the maps may be obtained by using CountTypes, and CountResources. There are also routines for adding resources and modifying their status. The function of the ShowRes desk accessory is to display the contents of the maps.

In the maps, besides the descriptive information of a resources type, ID, name, and attributes, is a handle to the resource data. Many of the resource routines use the handle to refer, unambiguously, to the resource in question. You must use GetResource, GetNamedResource, or GetIndResource to obtain the handle. The resource map contains a space to store the handle, and this space can be in one of three possible conditions. First, when the map is first opened the space is zeroed out, ie. there is no handle. Later, if the resource is referred to, a handle is obtained from the memory manager. The handle may simply be allocated out of the memory managers list of master pointers without actually loading the data from the file. At this point the the map, and anyone else who asks, has a handle which points to a master pointer. However, unless the data is actually loaded, the master pointer is zero and there is no space allocated on the heap. If the data is then loaded, heap space is allocated, and the master pointer is set to point to the data on the heap. As you can see, the memory manager and the resource manager enjoy a very symbiotic relationship. We don't have time to discuss the memory manager in full here, but let me note that the resource manager uses some of the advanced features of both the memory manager, and also the file manager. Note that you may refer to a resource using it's handle, even if it occupies no space on the heap.

the Desk Accessory ShowRes

I created ShowRes to be an example DA. Rather than just do a 'shell' I decided to give it a useful function. The function is to peek at the resources that are available while an application is running. Many times I want to just see if a resource exists that is supposed to. Or, I need to find the length of a particular resource, or find where a piece of code begins on the heap. It is a hassle to return to the finder for such simple tasks. It also a hassle to read the maps in memory, in hex, with a debugger. Hence, ShowRes. The way ShowRes works is that it maintains variables for; a resource type: TheType; for an ID:TheID; and for a resource handle:TheRes. If all these variables are empty (set to 0) then a list of all the types is drawn in the window. If TheType is set then a list of all the id's of that type are drawn. If TheType, and TheRes are set then the information for that resource is drawn. Clicks in the information drawn are interpreted as a selection of that type (or ID) and the information about 'what is known' is updated accordingly. To reselect a type or ID you use a button that will reduce 'what is known' (ie. if just TheType is known, clear it, if TheType and TheRes are known, just clear TheRes). The worst part of the code is the calculation of which type or ID a click has landed on. Since the information is drawn in a grid in the window, all it takes is some div's and mod's to find where in the grid the click occured. Once you know which cell in the grid was clicked on, you can find which type or ID it was. It can be hard to get this kind of stuff right the first time so I worked with it in MacPascal until I had it right (it would have been better to use a box with scroll bars for this, but that would have taken a lot more code). To see the overall structure of ShowRes see figure 4.

MiniEdit

How would you like to have the source code to a complete Macintosh application? An application with multiple windows, a full menu bar, desk accessory support, the works. It would be a good place to start on your project wouldn't it? How about if this code were in Pascal and you could modify and recompile it at will? Further, imagine that this code is very modularly written and fully commented. Interested? Now, what if in addition to the comments in the code there were an excellently written, and comprehensive, document that described the detailed function of every procedure? I'm not talking about twenty or fifty pages of programmers notes. I'm talking about eleven hundred pages of carefully prepared and cross referenced material. In addition to the program description, you get a description of every toolbox routine used (and perhaps some that aren't).

Well, you can have it! It's called Macintosh Revealed by Stephen Chernicoff. You buy both volumes. The first one is basic toolbox calls, and the second volume is more advanced toolbox stuff and a description of the program MiniEdit. You then order the source code from Hayden Books for $19.95. That's it. Watch this column for my adventures while compiling MiniEdit with TML Pascal (it is actually written in Lisa Pascal, but that should be no problem).

The nice thing about Pascal is that there are examples like this. TML Pascal will also compile the many program examples provided to developers by Apple (with slight modification).

MacPascal and inlines

If you have been following my column you may have noticed that it is difficult to do very many Mac-like things in MacPascal without resorting to one of the inline statements (inlineP for procedures, BinlineF for functions returning booleans, WinlineF for those returning integers, and LinlineF for those returning 32 bit results). On the one hand, this is a blessing, since it enables one to hack around with the toolbox from within a very friendly environment. On the other hand I have come to the conclusion that unless you have a machine language dubugger and know how to use it, inline will eventually give you a great deal of trouble. With this in mind, it becomes very difficult to describe how inline works. If I lapse into 68000 terminology then it is easy, but trivial. If I stick to to Pascal then all I can say is that inline will invoke a toolbox trap in the place of a procedure or function. This is not strictly accurate though. What really happens is that the first word of the inline statement is actually executed by the processor, and the rest is put on the stack. If the first word is $Axxx, where xxx is any three hex digits, then an A-trap is executed. All the A-traps are toolbox routines. If the first word is something else then anything could happen. For instance theStack:=LinlineF($2E8F) will return the position of the hardware stack, no toolbox traps are involved. The reason for this is that $2E8F is the code for move.l sp,(sp) in assembly language. With this capability it is possible to do all sorts of weird things. For instance, this is how I call the routine "access" I have used in previous articles. I am not particulary proud of this. It looks real ugly in the code. Most languages have the capability for this sort of hacking, but usually it is hidden inside some interface files and you don't have to know how it works. The better way to use MacPascal would be to make declarations like:

Function GetNewWindow ( windowID: INTEGER; wStorage:Ptr;             
                                                behind:WindowPtr ):WindowPtr;
      begin
 GetNewWindow := Pointer( LinlineF( $A9BD , 
 WindowID , wStorage  , behind ) );
 end;

When you wanted a window you would just say GetNewWindow, with no inline to mess with. This also makes conversion to TML Pascal a snap. Unfortunately, if you did this there would be little room left for any program! After you pass 16K of text in MacPascal things get very slow, eventually it will die altogether. This is not my last word on this issue. Next time I'm using inline I'll bring it up again.

MacPascal Books

There are now five books about learning Pascal using MacPascal. Some seem better that others. All have many interesting, short, Pascal examples. I don't know what to say. There are too many of them!

MacPascal books I have heard of

Pascal for the Macintosh by Andrew Singer and Henry Legard, Addison Wesley

Macintosh Pascal by Lowell A. Carmony and Robert L. Holliday, Computer Science Press

MacPascal Programming by Drew Berentes, Tab Books

The MacPascal Book by Paul Goodman and Alan Zeldin, Brady Communications Company, Inc. A Prentice-Hall Publishing Company

Macintosh Pascal by Robert Moll and Rachel Folsom, with Mary Elting, Houghton-Mifflin

Other Books with Pascal examples

Macintosh Revealed Volume One and Two, by Stephen Chernicoff, Hayden Book Company

One Flew Over the Quickdraws Nest by Greg A. Lewis, Valuable Information Press

(Also available from the MacTutor Mail Order Store)

Pascal Compiler

MacLanguage Series Pascal: $99

TML Systems

582 North Wickham Rd., Suite 93

Melbourne Fla. 32935

(305) 242-1873

(Also available from the MacTutor Mail Order Store)

TML Pascal program for ShowRes Desk Accessory
program ShowResource;{ file ShowRes.pas }

{                                         
  ShowRes is a desk accessory that displays  
  the resources that are on the heap. 
  Written 9/85 by Alan Wootton. Compile
  with TML Pascal using make DA option.
  Resource compile ShowRes.R to add menu and 
  dialog stuff and to convert CODE to DRVR.           
                                         }

{  $I=Include these interface files }
(*$I MemTypes.ipas  *)
(*$I QuickDraw.ipas *)
(*$I OSIntf.ipas    *)
(*$I ToolIntf.ipas  *)

(*$A+ Write source code to .ASM file *)
(*$B+ set bundle bit*)
(*$T  ???? ????  type and creator *)

{   The values of the ParamBlock CSCode 
     field will have the following values. 
    These constants are not found in any 
    of the Macintosh interface files.        }

CONST accEvent   = 64;
               accRun= 65;
         accCursor = 66;
         accMenu = 67;
         accUndo = 68;
         accCut  = 70;
           accCopy = 71;
           accPaste= 72;
           accClear= 73;

 TYPE
    Lptr = ^longint;
    chBlock= packed array [0..15,0..15] of char;
    chBlockPtr=^chBlock;
  
{  This is our data.  A handle to it will be stored 
    in the dctlStorage field of the DCtlEntry record }
    
    mainDataP = ^maindata;
    mainDataH = ^mainDataP;
    mainData  = record     { our personal Data }
 TheType : ResType;{ type of subject }
 TheRes : Handle;   { to subject }
 TheID : integer;    { ID of subject resource }
 ViewR : Rect;       { lower portion of box }
 InfoR : Rect;         { upper left }
 MaxR : Rect;         { giant Rect }
 click : point;       { where a mdown was }
     end;
  
{ All drivers have one of these.  It is used to store
    system information about the state of the driver. 
    It will also be passed to us on all calls from 
    system.  Note that this record IS defined in 
    the interface files above (as DCtlEntry) so it is 
    NOT strictly necessary to redefine it here.  I am
    doing it because I wish to use dctlStorage^^ to refer
    to MainData without coercing types. }
  
    DeviceControlRec = record
 dCltDriver : Handle;
 DcltFlags : integer;
 dctlQueue : integer;
 DctlQHead : Lptr;
 DctlQtail : Lptr;
 DctlPosition : longint;
 DctlStorage : maindataH;
 dCtlRefNum : integer;
 dCtlCurTicks : longint;
 dCtlWindow : GrafPtr;
 dCtlDelay : integer;
 dCtlEmask : integer;
 dCtlMenu : integer;
     end;


{ var  variables for main program; not used }    

{                                         
     This first section is some data drawing procs.  
                                         }

{ Draw one hex character in current port}
{ at current pen position. }

 procedure PlotHex (c : char);
 begin
    if ord(c) < 10 then
       DrawChar(chr(ord(c) + ord('0')))
    else
       DrawChar(chr(ord(c) + ord('A') - 10));
 end;

{ Draw one Hex byte. }

 procedure PlotByte (c : char);
  begin
     PlotHex(chr(ord(c) div 16));
     PlotHex(chr(ord(c) mod 16));
  end;

 { Draw 256 Hex bytes, and 256 ASCII chars. }
 
 procedure PlotHexBlock ( chptr: chBlockPtr );
   var
      i, j : integer;
      xy : point;
      ch : char;
 begin
    TextMode(srcOr);
    GetPen(xy);
       for i := 0 to 15 do
   for j := 0 to 15 do
      begin
         ch:=chptr^[i,j];
 MoveTo(xy.h+j*16, xy.v+i*12);
 PlotByte(ch);
 MoveTo(xy.h+j*8+256, xy.v+i*12);
 DrawChar(ch);
      end;
    TextMode(srcCopy);
 end;

{ This recursive little beast draws longints without
  leading zeros. But it won't draw zero. }

 procedure DrawLong (i : longint);
 begin
    if i < 0 then
       begin
   i := -i;
   DrawChar('-');
       end;
    if i <> 0 then
       begin
   DrawLong(i div 10);
   DrawChar(chr((i mod 10) + ord('0')));
       end;
 end;

{ Draw an integer, or '0' if none. }

procedure DrawInteger (i : integer);
 begin
    if i = 0 then
       DrawChar('0')
    else
       DrawLong(i);
 end;
 
{ This works just like DrawLong above 
    but draws the number in binary }

 procedure DrawBinary (i : integer);
 begin
    if (i <> 0) and (i <> -1) then
       begin
   DrawBinary(i div 2);
   DrawChar(chr((i mod 2) + ord('0')));
      end;
 end;

{ convert a ResType into a str255 }

 procedure TypetoStr (var thetype : Restype;
                                         var str : str255);
begin
    str := '1234';{ so str has right length }
    BlockMove(@thetype, @str[1], 4);
 end;


{                                         
     Here are the procs that draw data into the
    window and calculate where in the data a 
    click occured.                                              
                                         }

{ Given a click in the list of IDs, 
   calculate which Resource it was.  }

 procedure ClickID (var MainVars : MainData );
   var
      i : integer;
 begin
 with MainVars do
    begin
     { first we find the x part ( mod 4 ) then we 
        add the y part ( * 4 ), then we have an index }
      i := ((click.h - ViewR.left) div 72) mod 4;
      i := i + (((click.v - ViewR.top) div 12) * 4);
      if i > CountResources(TheType)  then   
  i := 0;
      if i > 0 then
  begin
     SetResLoad(False);
     TheRes := GetIndResource(TheType,i);
     SetResLoad(true);
  end
      else
  TheRes := nil;
    end;
 end;

{ Given a click in the list of types, calculate which type it was. }

 procedure ClickType (var MainVars : MainData );
  var
     i : integer;
 begin
 with MainVars do 
   begin
      { this works the same as above except there 
         are 8 items per line, not 4 }
      i := ((click.h - viewR.left) div 36) mod 8;
      i := i + (((click.v - viewR.top) div 12) * 8);
      if i > countTypes then 
   i := 0;
      if i > 0 then
   GetIndType(TheType,i)
      else
  TheType:=ResType(pointer(0));
   end;
 end;

{ Draw list of types in ViewR. }

 procedure DrawTypes (var MainVars : MainData);
  var
     i : integer;
     tmpType : ResType;
     str : str255;
 begin
 with MainVars do
   begin
      moveto(infoR.left, infoR.top + 12);
      DrawString('Click on one of the types below');
      for i:=1 to countTypes do  
  begin
     moveto(viewR.left, viewR.top + 12);
     move((i mod 8) * 36, (i div 8) * 12);
     GetIndType(tmpType,i) ;
     TypeToStr(tmpType, Str);
     DrawString(str);
  end;
    end;
 end;

{ Draw list of ID's in ViewR. }

 procedure DrawIDs ( var MainVars : MainData );
  var
     tmpID, i : integer;
     tmpName, str : str255;
     H : Handle;
     tempType : ResType;
 begin
 with MainVars do
   begin
     moveto(infoR.left, infoR.top + 12);
     TypeToStr(TheType, str);
     DrawString('These are the resources of type ');
     DrawString(str);
   
     for i:= 1 to CountResources(TheType) do 
 begin
    moveto(viewR.left, viewR.top + 12);
    move((i mod 4) * 72, (i div 4) * 12);
       
    SetResLoad(False);
    H:=GetIndResource(TheType,i);
    SetResLoad(true);
    GetResInfo(H,tmpID,tempType,tmpName);
       
    DrawChar(' ');
    DrawInteger(tmpID);
    DrawChar(':');
    DrawString(tmpName);
 end;
  end;
 end;

{ Draw Info about the Resource. }

 procedure DrawResInfo (var MainVars : MainData );
  var
     tmpID : integer;
     tmpName, str : str255;
     H : Handle;
     tempType : ResType;
 begin
  with MainVars do
   begin
     if TheRes <> nil then
 begin
    tmpName := '';
    GetResInfo
         (TheRes,tmpID,TempType,tmpName);
    TypeToStr(tempType, str);
       
    moveto(infoR.left, infoR.top +8);
    DrawString('TYPE:');
    DrawString(str);
    DrawString(' Name:');
    DrawString(tmpName);
       
    moveto(infoR.left, infoR.top + 18 );
    DrawString('ID:');
    DrawInteger(tmpID);
    DrawString('  attributes:');
    DrawBinary(GetResAttrs(TheRes));  
    DrawString('  Size:');
    DrawLong(SizeResource(TheRes));
       
    MoveTo(infoR.left, infoR.top+ 28 );
    DrawString('  Handle is:');
    DrawLong(ord(TheRes));
    DrawString('  belongs to map#:');
    DrawInteger(HomeResFile(TheRes));
       
    MoveTo(viewR.left, viewR.top + 12);
    if TheRes^ <> nil then
       PlotHexBlock(pointer(ord(TheRes^)));
 end;
    end;
 end;


{                                         
     Following are procs to service the window.
     Windows need updating, and activation, etc
                                         }

{ a window activation function. We take this }
{ opportunity to put our menu in the menu bar }

 procedure activate (var device : deviceControlRec);
   var
     menH : menuHandle;
 begin
    with device do
       begin
   menH:= GetMenu(dctlMenu);
   
          {   the Font/DA mover does not correct the
   ID of menus when it renumbers them. 
   So, we must do it here  }
   menH^^.menuID:=dctlMenu;
   
   InsertMenu(MenH,0);
   DrawMenuBar; 
       end;
 end;

{ A window deactivation function.  We must remove }
{ our menu from the bar at this time }

 procedure deactivate(var device:deviceControlRec);
 begin
    with device do 
      begin 
  deleteMenu(dctlMenu);
  DrawMenuBar;
      end;
 end;

{ Redraw the window.  Draw type list, or ID list, or
  resource information depending upon what is set. }

 procedure updateself (var device:deviceControlRec);
  var
     max : integer;
 begin
  with device do
     begin
 beginupdate(dctlWindow);
 drawdialog(dctlWindow);
 with dctlStorage^^ do { with MainData }
    begin
       if  Longint(TheType)=0  then
  DrawTypes(dctlStorage^^)
       else 
  if TheRes = nil then
      DrawIDs(dctlStorage^^)
  else
      DrawResInfo(dctlStorage^^);
    end;
 endupdate(dctlWindow);
     end;
 end;

{                                        
     Finally we have the three main procs.
    Open, Ctl, and Close.  All calls from the
   'outside' will be handled by one of these three.  
    Note that Ctl has the event case split out
    for easier readability.
                                         }

{ Open:The first call from the system.  Make a 
  window and setup our private storage.
  Note that we could get an open call even after we
  are already open.   }

 procedure open (var device : deviceControlRec;
                            var block : ParamBlockRec);
  var
     Wpk : WindowPeek;
     Dtyp, ID : integer;
     Dhan : Handle;
     tmptr : ptr;
     dummy:MainData;
 begin
  ID := $C000 - 32 * (1 + device.dctlRefNum);
{ use this ID for all resource access }
  device.dctlMenu:=ID;

  with device do
     if dctlWindow = nil then
        begin
    dctlstorage := pointer(NewHandle( SizeOf(dummy)));
    { This is a handle to our private data }
      
    { It is good voodo to keep window records }
    { off of the bottom of the applications heap }
    tmptr:=NewPtr($1000); { 4K on bottom of heap}
      
    dctlWindow:=GetNewDialog(ID,nil,nil);  

           { Must set windowKind field.  Never forget this !}
    wpk := pointer(ord(dctlWindow));
    wpk^.windowKind := dctlRefNum;

    setport(dctlWindow);
    textMode(srcCopy);
    textfont(3);
    textsize(9);
      
    disposptr(tmptr);
 { leaving 4K hole under window record} 
      
    Hlock(Handle(DctlStorage));
    with DctlStorage^^ do
      begin                   { initialize  our  storage  }
             GetDitem(dctlWindow,2,Dtyp,Dhan,viewR);    
 GetDitem(dctlWindow,3,Dtyp,Dhan,InfoR);  
 TheType:=ResType(pointer(0));
 TheRes := nil;  
 SetRect(MaxR, -999, -999, 999, 999);
      end;{ of with storage }
           Hunlock(Handle(DctlStorage));
        end;{ of with }
 end;{ of open }

{ This is the end and there is no way to say no.
  The last call we will ever get. 
  We must clean up our act completely. }

 procedure close (var device : deviceControlRec;
                             var block : ParamBlockRec  );
 begin
    deactivate(device);{ remove menu }
    with device do
       begin
   disposHandle(Handle(dctlstorage));{ kill data }
   disposDialog(dctlWindow);{ erase window }
   dctlWindow := nil;
       end;{ of with }
 end;{ of close }

{ We are here because there has been an event 
  concerning our window. 
  Block.csParam[0] and [1]  point at the event record   }

 procedure Event (var device : deviceControlRec;
                              var block : ParamBlockRec  );
  var
     item, part : integer;
     tmpDptr:DialogPtr;
     evP:^EventRecord;
 begin
   blockMove(@block.csParam[0],@evP,4);
   { above is the best way I could think of to 
      convert 2 integers into a pointer }
      
   with evP^ do        { with eventRecord }
   with device.dctlStorage^^ do  { with MainData }
     begin
  case what of
    MouseDown :{1}
      begin
 click:= where;
 globaltolocal(click);
 if dialogselect(evP^,tmpDptr,item) then
     { normally one would compare tmpDptr 
       with ones own window, but a DA won't 
       get an event unless it's in his window  }
    begin
       case item of
        1 :               { close }
   if TheRes = nil then
     TheType := ResType(pointer(0))
   else
      TheRes := nil;
        2 :              {viewRect }
   begin
      if Longint(TheType) = 0 then
        ClickType(device.dctlStorage^^)
      else
        if TheRes = nil then
   ClickID(device.dctlStorage^^);
   end;
        otherwise
 ;
       end; { of item case }
   InvalRect(MaxR); { force update }
  end; { if DialogSelect }
      end; { Mdown case }
   UpdateEvt : {6} 
      updateself(device);
   ActivateEvt : {8}
 if odd(modifiers) then
    activate(device)
 else
    deactivate(device);
   otherwise
    ;{ ignore all other events }
       end;{ case of what }
    end;{ with eventRecord}
 end;{ procedure event }


{ Here is the main entry point for system calls. 
  The parameter block tells us what the nature of 
  this call is.  Note that in a more complex program
  the menu case could be made into a separate proc
  like the event case.   }

 procedure ctl (var device : deviceControlRec;
                          var block : ParamBlockRec  );
  var
      poi : point;
      cHan : cursHandle;
      wpnt : Grafptr;
      item : integer;
 begin
    setport(device.dctlWindow);
    Hlock(Handle(device.dctlStorage));
    with device.dctlStorage^^ do
     with block do
 begin
    case csCode of
       accEvent: 
             Event(device, block);
       accCursor:
  begin
     GetMouse(poi); 
     if ptInRect(poi, ViewR) then
        begin
           cHan:=GetCursor(CrossCursor);  
           SetCursor(cHan^^); 
        end
     else
        initcursor;
  end;
       accMenu: 
   begin        { case out menu item number }
      case  csParam[1]  of
 1 :                               { about... }
   begin
                  Wpnt:=GetNewDialog(
              device.dctlMenu+1,nil, pointer(-1));
      ModalDialog(nil,item);
      DisposDialog(wpnt);
   end; 
 3 :                            { LoadResource }
    if TheRes <> nil then
       LoadResource(TheRes)
    else
       sysbeep(10);
 4 :                   { ReleaseResource }
    if TheRes^ <> nil then
        begin
           ReleaseResource(TheRes);
                                            TheRes := nil;
        end
    else
        sysbeep(10);
 otherwise
    sysbeep(10);
        end;{ of case of menu }
    InvalRect(MaxR);{ force update }
    HiliteMenu(0);
 end;{ menu case of code }
       otherwise
        ;
    end;{ case of code }
 end;{ of with block }
     Hunlock(Handle(device.dctlStorage));
 end;{ of control  }

{                                         
    Observe the poor main procedure.  Since we
     will link with another module that will call
     Open, Ctl,and Close directly; there will 
    never be any use for a main proc.  
    Furthermore, there can be no global data for 
    a main proc to use.
                                         }
BEGIN
   (* No main program allowed *)
END.

Rmaker input file for ShowRes Desk Accessory
;;                                     
;;  file ShowRes.R
;; 
;;  Feeding this to Rmaker is the last
;;  step when compiling ShowRes.
;; 
;;  The CODE 1 resource is read from 
;;  ShowResL, the link output
;; 
;;  A Font/DA Mover data file is made.
;;                                     
;;   All Resources must be be between
;;  -15872 and -15841 inclusive if they
;;  are to travel with DRVR number 16
;;                                     
ShowRes.acc;;;  destination file name 
DFILDMOV;;  type and creator 
;;                                     

type DRVR = PROC
ShowRes,16;; name, DRVR number 16
ShowRes

;;                                     
;;  This DLOG is the main window. 
;;  The StatText items are used only
;;  for their rectangles.
;;                                     
type DLOG
ShRsDLOG,-15872;;id owned by #16
Show Resource;;;;window title
53 55 260 469  ;;global bounds
Visible GoAway
4       ;;wind. type 4
0              ;;refcon 0
-15872         ;;id of DITL

type DITL
ShRsDITL,-15872
3

BtnItem Enabled
5 320 25 411 
Close

StatText Enabled
25 5 206 410 
;;; View, clicks taken in here

StatText Enabled
0 5 26 225 
;;; Info, title to data drawn here

;;                                     
;;  This is the menu used by the DA. 
;;                                     
type MENU
ShRsMENU,-15872
ShowRes;;        menu title
about..;;        menu1
(-;;             dotted line
LoadResource;;   menu3
ReleaseResource;;menu4

;;                                     
;;  This second DLOG is for the 
;;  'about..' menu item.  The button
;;  and icons do nothing. They are 
;;  only for decoration.
;;                                     
type DLOG
ShRsDLOG2,-15871
About ShowRes;;    title
60 50 300 455;;    size
Visible NoGoAway
4
0
-15871

type DITL
ShRsDITL2,-15871
10

StatText enabled
5 5 91 400 
ShowRes -  an accessory to view ++
resources on the heap. Created ++
by Alan Wootton 9/85. ++
Written using Macintosh Pascal ++
and MacLanguage Pascal ++
from TML systems, Melbourne Fla.  ++
Published in MacTutor, The Macintosh ++
Programming Journal.;;

BtnItem enabled
95 5 102 395 
{|||||||||||||};;;used as separator

StatText enabled
105 5 191 400 
To select Resources click on the ++
list displayed. Use the button to ++
return to a previous list.  ++
Resources loaded will show the ++
first 256 bytes.  ++
Load resources with "LoadResource" ++
menu item. Use "ReleaseResource"++
carefully!!!;;

IconItem Disabled;; Icons along bottom
195 20 227 52    ;; for fun. 
0
IconItem Disabled
195 75 227 107 
1
IconItem Disabled
195 130 227 162 
2
IconItem Disabled
195 185 227 217 
1
IconItem Disabled
195 240 227 272 
2
IconItem Disabled
195 295 227 327 
1
IconItem Disabled
195 350 227 382 
0

;;                                     
;;  end of file ShowRes.R
;;                                     
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Capture One 15.3.1 - RAW workflow softwa...
Capture One is a professional RAW converter offering you ultimate image quality with accurate colors and incredible detail from more than 400 high-end cameras - straight out of the box. It offers... Read more
Connect Fonts 23.0.3 - Font management s...
Connect Fonts is the creative professional's font manager. Every professional font manager should deliver the basics: spectacular previews, powerful search tools, and efficient font organization. You... Read more
CleanMyMac X 4.11.0 - Delete files that...
CleanMyMac X makes space for the things you love. Sporting a range of ingenious new features, CleanMyMac lets you safely and intelligently scan and clean your entire system, delete large, unused... Read more
Firefox 102.0 - Fast, safe Web browser.
Firefox offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals and casual... Read more
Hopper Disassembler 5.6.1 - Binary disas...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
Skim 1.6.11 - PDF reader and note-taker...
Skim is a PDF reader and note-taker for OS X. It is designed to help you read and annotate scientific papers in PDF, but is also great for viewing any PDF file. Skim includes many features and has a... Read more
Alfred 4.6.7 - Quick launcher for apps a...
Alfred is an award-winning productivity application for OS X. Alfred saves you time when you search for files online or on your Mac. Be more productive with hotkeys, keywords, and file actions at... Read more
Transmit 5.8.7 - Excellent FTP/SFTP clie...
Transmit is an excellent FTP (file transfer protocol), SFTP, S3 (Amazon.com file hosting) and iDisk/WebDAV client that allows you to upload, download, and delete files over the internet. With the... Read more
Adobe Lightroom Classic 11.4.1 - Import,...
You can download Lightroom for Mac as a part of Creative Cloud for only $9.99/month with Photoshop, included as part of the photography package. The latest version of Lightroom gives you all of the... Read more
MarsEdit 4.5.9 - Quick and convenient bl...
MarsEdit is a blog editor for OS X that makes editing your blog like writing email, with spell-checking, drafts, multiple windows, and even AppleScript support. It works with with most blog services... Read more

Latest Forum Discussions

See All

Apple Arcade Weekly Round-Up: Major Upda...
Apple recently revealed July’s upcoming Apple Arcade releases in a new App Store Story, and this week’s new release is My Bowling 3D+ featuring offline and online multiplayer support, and more. It arrives from the developers of Pro Darts 2022+ and... | Read more »
Downhill Mountain Biking Game ‘Descender...
Just over three years ago in May of 2019 developer RageSquid and publisher No More Robots released a quirky downhill mountain biking game called Descenders on PC and Xbox One. Bemoaning a lack of “extreme sports" titles in recent years led RageSquid... | Read more »
SwitchArcade Round-Up: ‘Monster Hunter R...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for June 30th, 2022. Thursday is once more upon us, and that means a bunch of new releases to look at. We start things off with DLC for some very big games, Monster Hunter Rise and... | Read more »
‘HOOK 2’ Review – A Sharp Left Hook From...
The original HOOK ($1.99) had a very simple idea behind it. You were presented with a tangled mess of hooks and loops, and you needed to remove each one without snagging any others. Extremely simple at first, but as the puzzles rolled along,... | Read more »
‘Dicey Dungeons’ Mobile Version Launchin...
After a very long wait, Terry Cavanagh’s dungeon crawling roguelite deckbuiler hybrid experience Dicey Dungeons is coming to mobile platforms next week alongside a huge free DLC pack on all platforms. This DLC will be included in the mobile... | Read more »
Distract Yourself With These Great Mobil...
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
‘Danganronpa S: Ultimate Summer Camp’ is...
If you’ve been following Danganronp over the last few years, Spike Chunsoft celebrated its anniversary by bringing the series to mobile in the form of anniversary editions. After the first two released, there was a long delay for V3, but it finally... | Read more »
Out Now: ‘HOOK 2’, ‘Incoherence’, ‘Juras...
Each and every day new mobile games are hitting the App Store, and so each week we put together a big old list of all the best new releases of the past seven days. Back in the day the App Store would showcase the same games for a week, and then... | Read more »
Upcoming Mobile MMO RPG Shooter ‘Avatar:...
This past January a contingent of developers made up of Archosaur Games, Tencent, Lightstorm Entertainment, and Disney announced a new mobile game set in James Cameron’s Avatar universe titled Avatar: Reckoning. | Read more »
Culinary Platformer ‘Chefy-Chef’ Coming...
If your name is Chefy, it’s pretty much a given that you should be a chef. Such is the case with Chefy-Chef, a game from Bug Studio about a chef named Chefy who must travel to all sorts of exotic locations using a magical refrigerator in an effort... | Read more »

Price Scanner via MacPrices.net

July 4th sale at Verizon: Apple AirPods Pro f...
Verizon has Apple AirPods Pro on sale for $179.99 on their online store as part of their Fourth of July sale. Their price is $70 (28%) off Apple’s MSRP, and it’s among the lowest prices currently... Read more
Apple is now selling Certified Refurbished Ma...
Apple has added a full line of standard-configuration Mac Studios available in their Certified Refurbished section starting at only $1799 and ranging up to $400 off MSRP. Each Mac Studio comes with... Read more
Open-box 14″ M1 Pro MacBook Pros in stock tod...
QuickShip Electronics has open-box return Space Gray 14″ M1 Pro MacBook Pros in stock and on sale for $300-$450 off MSRP on their eBay store today. According to QuickShip, “The item in this listing... Read more
Can Being An iPhone User Really Determine Whe...
FEATURE: – If you’re traveling on the road today for the July 4th holiday, you might want to keep your Apple smartphone locked up inside the car’s glove compartment for your (and, everyone else’s)... Read more
2nd generation 4K Apple TVs with Siri remote...
Apple has restocked a full line of Certified Refurbished 2nd generation 32GB and 64GB 4K Apple TVs with Siri remotes for $30 off the cost of new models. Apple’s standard one-year warranty is included... Read more
Back in stock: Apple Watch Series 7 models fo...
Apple has restocked Certified Refurbished Apple Watch Series 7 WiFi-only models in their online store for $60-$70 off MSRP, starting at $339. Each Watch includes Apple’s standard one-year warranty, a... Read more
July 4th Sale at Expercom: $200 off any 16″ M...
Apple reseller Expercom has 16″ M1 Pro and M1 Max MacBook Pros available for $200 off MSRP as part of their July 4th sale. In addition to their MacBook Pro sale prices, take $50 off AppleCare+ when... Read more
10.2″ Apple iPads (WiFi models) are on sale f...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for up to $20-$50 off MSRP for a limited time. Their prices are the lowest price currently available for one of these iPads. All models are... Read more
10-Core M1 Pro 14″ MacBook Pros on sale for $...
B&H Photo is offering $200 discounts on Apple’s new 14″ M1 Pro MacBook Pros with 10-Core CPUs (16GB RAM/1TB SSDs). Free 1-2 day shipping is available to most US addresses, and both models are in... Read more
B&H has 16-inch M1 Pro MacBook Pros in st...
New Space Gray 16″ MacBook Pros with Apple’s M1 Pro CPUs are in stock and on sale today at B&H Photo for $200 off Apple’s MSRP. Sale prices are for M1 Pro models with 512GB or 1TB of SSD storage... Read more

Jobs Board

VP, Software Engineering - *Apple* and Andr...
…Client Application Software Engineering team is seeking a VP, Software Engineering for Apple and Android. You will lead the client engineering team building Disney+, Read more
I/S Senior Engineer - *Apple* Systems Engin...
**19647BR** **Position Title:** I/S Senior Engineer - Apple Systems Engineering - Remote **Department:** Information Systems **Location:** Lakeland, FL between Read more
*Apple* IT Support Analyst - 2nd Shift - Zon...
Apple IT Support Analyst - 2nd Shift Professional Services Albany, New York Malta, New York Clifton Park, New York Menands, New York Syracuse, New York Watertown, Read more
Infotainment Certification Test Engineer (XC)...
…integration - CarPlay, android auto, MirrorLink, Baidu Carlife, MFi/iPod certification testing; Apple PPID preparation, Google HUCD and GTM preparation + 3 years of Read more
Workplace Services *Apple* Device Managemen...
…3350 Riverwood Parkway Suite 900, Atlanta, GA, 30339 USA **Workplace Services Apple Device Management** **Role Overview** Carrier is seeking an experienced and Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.