TweetFollow Us on Twitter

External Functions
Volume Number:6
Issue Number:1
Column Tag:Advanced Mac'ing

Related Info: Resource Manager

External Functions

By Mark Lankton, Boulder, CO

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

XDemo--Easy External Functions

Mark Lankton is a member of the technical staff at the University of Colorado in Boulder, where he spends his time teaching Macintoshes to talk to space-flight instruments. Much of his work involves real-time data acquisition and display.

Why can’t I be like the big kids?

What does HyperCard have that you don’t have? External commands and functions, that’s what. The Great Man and his team didn’t have to anticipate all the zillions of uses their creation would end up supporting. They were bright enough to build in a mechanism that allows (educated) users to add procedures of their own. Functions that weren’t even a gleam in Bill Atkinson’s eye can be programmed by a basement genius and used by HyperCard as if they had always been there.

Is that a neat trick? You bet it is. The huge supply of homegrown XCMDs and XFCNs is ample evidence of the idea’s popularity. HyperCard isn’t alone, either. Other big-time applications (can you say 4th Dimension?) are in on the gag too. The idea of externally-built modular functions is going to be very important in the object-oriented world that everyone assures us is coming.

This article describes a simple mechanism for building that kind of extensibility into your own application. It includes an extremely rudimentary application called XDemo, which has as its sole purpose in life the plotting of a small data set in a window. The interesting part is that if you don’t like the looks of the data, you can write an external function to change it and simply add the function to XDemo. You can write a whole batch of external functions and add them all in; they will all appear in a menu and you can call them any time you want. Your mom can write an external function and you can add that in, too. You don’t have to recompile XDemo; you don’t even need access to the source code. You do need a compiler and linker that will let you create a stand-alone ‘CODE’-like resource. XDemo is written in MPW C3.0, but other C compilers that are capable of creating things like WDEFs and XCMDs should work fine. A copy of ResEdit is handy, too.

How Can This Be?

The whole thing works because of a nice C trick: the pointer-to-function. Function pointers give you a way to actually execute code that lives in arbitrary locations in memory, locations that the linker never dreamed of when it originally built the application. (In fact, you can make the computer attempt to execute whatever it finds at any address you specify. If there is no code there, the results can be, shall we say, surprising. This capability is one of the reasons C has a historical reputation for being “unforgiving”.)

A pointer-to-function allows you to run a function’s code once it’s in memory, but how do you get it into memory in the first place? The answer is to make the executable code of the function into a resource and call on the Resource Manager. The Resource Manager can read that resource into memory and give us a handle to it. Dereference the handle into a pointer and there you go!

At this point some of you will be running for the exits to try this for yourselves. For those who remain, here are the details of how to define the interface between the main application and the external functions, how to build the external functions themselves, and how to load them into a menu at start-up time and use them.

Figure 1- Xdemo with a variety of XTRA functions installed.

Defining an Interface

Giving yourself and your users the capability to make up and use new functions is wonderful, but it only works if everyone agrees on the rules. You have to specify the parameters that will be passed to the external functions and the results that the functions will return. Deciding what kinds of data the external functions will be allowed to work on and what sorts of information need to be passed back and forth is really important, and you need to give it serious thought.

The most flexible way to pass information to an external function is to define a parameter block and pass a pointer to that block to the external function. The parameter block itself can be large, complicated and jam-packed with data (including other pointers and handles) or it can be very simple, depending on the needs of the application. XDemo uses a tiny parameter block called an XTRABlock, which looks like this:

/* 1 */

typedef struct XTRABlock{
 int  dataLength;
 short  *theData;
 }XTRABlock,*XTRABlockPtr,**XTRABlockHandle;

All that is included is the length of a data set, and a pointer to that data set. This means that external functions added to XDemo can do anything they want to the values in a particular bunch of data, but they can’t do much else. If your application needs more flexibility (and it probably will) you need to define a more elaborate parameter block.

You can help yourself considerably during development if you add a few unused fields to the parameter block, thus:

/* 2 */

typedef struct XTRABlock{
 int  dataLength;
 short  *theData;
 long unused1;
 long unused2;
 /*more if you’re really cautious...*/
 }XTRABlock,*XTRABlockPtr,**XTRABlockHandle;

That way when you decide to add features to the block your space requirements won’t change, and any external functions you have already built won’t need to be redone. Since what the application is actually passing is a pointer to the block, you don’t need to worry about stack space even if the size of the block gets really out of hand.

There are three obvious ways to get information from the external function back to the application. The first is to have the function return a result, which can be anything from the simple Boolean used in XDemo to a fiendishly complicated data structure. It’s often best to keep the return value simple; you’re already defining one tricky interface with the parameter block. You can use a Boolean result to let the external function indicate whether it succeeded in whatever it was trying, for instance. The second way is to let the function set or change values in the parameter block, and have the application look at them after the function returns. This is safe, and can be defined any way you want. The third way is to allow the function access (by way of a pointer in the parameter block) to the application’s global data. This is a simple and dangerous method, since the application is exposing its internals to the outside world. XDemo is brave, and uses this third method as well as the Boolean result trick.

The last step in defining the interface is the function prototype for the external functions. Here you can inform the compiler about what to expect when one of these functions is called. XDemo uses this prototype:

/* 3 */

pascal Boolean (*theXTRAProc)(XTRABlock *theBlockPtr);

In English, this means that “theXTRAProc” is a pointer to a function which takes one parameter (theBlockPtr) and returns a Boolean result. The “pascal” keyword means that parameters to the function will be pushed on the stack by the compiler in Pascal order (left-to-right) instead of C order (right-to-left). This is vital for any functions that might end up being used by Toolbox calls. It really isn’t needed in XDemo, but for Macintosh programmers it’s not a bad habit to get into. (For a function that takes only one parameter it is a rather fine distinction anyway.)

Writing the External Functions

Once the interface is clearly defined, you can start writing external functions. Make sure to declare each function using the same prototype form as used above. There are a couple of things to watch out for here. The external functions can’t directly use any of the application’s globals. The external functions can’t use any global or static data of their own, either, which can cause trouble if you want to use text strings. (If you’re using MPW C 3.0 you can use the “-b” compiler option to get around this.) Other than that, what you do in these functions is entirely up to you, subject to the limitations of the interface you have specified. The code for two very simple XDemo external functions is included below.

If you think your functions may need to use resources of their own, you may want to partition the set of legal resource ID numbers in some clever way. If nothing else, you can specify that any resources used by the function should have the same ID as the resource that contains the function itself. When the resources are installed into the application with ResEdit, make sure all the IDs agree. From inside the function itself you can find out the proper ID like this (assuming you have already picked out a name!):

/* 4 */

thisResource = Get1NamedResource(‘XTRA’, “\pThe Name”);
GetResInfo(thisResource,&theID,&theType,theName);

The exact method of compiling and linking these functions will depend on the development system you are using. Here’s how to do it using MPW:

#5

c myXTRAFunction.c
link -sg “My Extra Function” myXTRAFunction.c.o 
 -rt XTRA=128 -m MYXTRAFUNCTION 
 {cLibraries}CRuntime.o 
 {clibraries}cinterface.o 
 {clibraries}CSanelib.o 
 {libraries}interface.o 
 -o myXTRAFunction

This assumes that you have a C source file called myXTRAFunction.c, which contains the code for a function called myXTRAFunction. For those not familiar with MPW, these instructions mean the following:

First, run the C compiler on my source file.

Second, run the linker on the “.o” file produced by the compiler. Link it all into one segment named “My Extra Function”. Give it a resource type of ‘XTRA’ and a resource ID 128. The entry point is the module “MYEXTRAFUNCTION”. (Its name is all upper-case because it was declared to be a Pascal-type function, and Pascal changes everything to upper-case.) Use the CRuntime, CInterface, CSanelib and Interface libraries if you need them for the link. Finally, put the whole works into a file named “myXTRAFunction”.

At this point if you open “myXTRAFunction” using ResEdit you will find an XTRA resource named “My Extra Function”, ID 128, which you can copy into the main application. If you are brave, and keep close tabs on your resource ID numbers, you can link the function directly into the application by specifying “-o XDemo” in the last line of the Link command above. This is fast and clean, but you will demolish any XTRA resource with the same ID that was there before. Be careful when you do this. For your own application you should of course invent your own resource type, and use that instead of ‘XTRA’.

Loading and using the external functions

There are lots of ways to provide access to the external functions; here’s how it’s done in XDemo. The external functions are automatically installed at run-time in a menu called (in a flight of wild imagination) “Externals”. In XDemo’s SetupMenus() function, Count1Resources() is called to see how many XTRA resources there are. (To make life easy for the Menu Manager, XDemo will only use the first 32.) For each XTRA resource, Get1IndResource() returns a handle, which is installed in an array so it can be found easily. This array is 1-based, so that when the functions are installed into the Externals menu the menu item numbers will exactly correspond to the array indices. Calling MoveHHi() and HLock() makes sure that nobody escapes, since it is extremely unfortunate to have a piece of code move while the machine is trying to execute it! In a “real” program you would want to be much more sophisticated about memory management and avoid tying up memory until you really needed to. Finally, GetResInfo() finds the name of the XTRA resource, and AppendMenu() inserts that name into the menu’s item list.

When all this is done, XDemo has an Externals menu consisting of the names of all the external functions that have been attached to it, and an array of handles to those functions. Now, at last, it can really use them. When an item is selected from the Externals menu, XDemo grabs the corresponding handle from the handle array. The handle is already locked, so it is safe to use it. The handle is dereferenced once to obtain a pointer, and the pointer is jammed into theXTRAProc. To satisfy the compiler, both sides of the assignment statement are explicitly cast to type ProcPtr:

/* 6 */

(ProcPtr)theXTRAProc = (ProcPtr)*(XTRAArray[theItem]);

Then, assuming that the proper values are already loaded into an XTRABlock named “theBlock”, the function is called like this:

/* 7 */

theResult = (*theXTRAProc)(&theBlock);

This function call works just like a “normal” one, and XDemo take the returned result and goes about its business, which just means replotting its data and seeing what the external function may have done to it. If you are curious about how the pointer-to-function call really works, see “The Dirty Details” below.

Figure 2- Looking at an XTRA resource with ResEdit.

Go For the Code

It should be clear that XDemo’s implementation of external functions is very simple. Your use of the ideas presented here is limited only by your own cleverness. External functions provide a flexible way for you and your users to change and extend an application.

Are there any drawbacks? Yes, of course. Each external function chews up space in your application heap, so you have to allow for extra memory usage, and recover gracefully if you run out. Worse, when you give other people the power to modify your application, as you will if you publish your interface specs, you run the risk of getting modifications that work poorly, don’t work at all, or even crash the machine. Still, the added capabilities are extremely seductive. It’s a way of adding to an application without rebuilding it or changing its structure in any way. The effects are reversible; just zap any offending externals with ResEdit. And most important, you will be able to do something about the world’s most common complaint, “Well, why doesn’t it do this?”

So, read the source code, put on your wizard’s hat, and have fun!

The Dirty Details (For those who can’t get enough)

How does a call using a function pointer work? The best way to understand it is to see what the compiler does with the C code that makes the call. Using the MPW tool DumpObj on the compiled code lets us look directly at the assembly-level instructions that the 680x0 chip will execute. The twelve instructions below are extracted from the dump of XDemo’s DoCommand() function. They are the result of compiling these four lines of C:

/* 8 */

theBlock.dataLength = screenDataLength;
theBlock.theData = screenData;
(ProcPtr)theXTRAProc = (ProcPtr)*(XTRAArray[theItem]);
theResult = (*theXTRAProc)(&theBlock);

(No, DumpObj did not add the comments. It’s a wonderful tool, but it speaks little English.) The XTRABlock used in this function was declared as a local variable, so the compiler allocated space for it in a local stack frame below A6.

;9
          
;The compiler loads the XTRABlock with our
;two values: the length of the buffer, and a 
;pointer to it.
MOVE.L  screenDataLength,-$0008(A6)
MOVE.L    screenData,-$0004(A6)

;Next, the menuItem number is retrieved from D7
;where it has been stored all through 
;DoCommand().
;It will be used as an index into XTRAArray. Since
;handles are 4 bytes long, the compiler multiplies 
;the index by 4 to get the proper byte offset.
MOVE.L    D7,D0
ASL.W     #$2,D0 ;shift left two places

;Load address of XTRAArray into A0, retrieve the
;handle from our index offset, and plop it 
;into A0.
LEA       XTRAArray,A0        
MOVEA.L   $00(A0,D0.W),A0

;Here we can see that the MPW C compiler is being
;a little cautious. It dereferences the function
;handle, producing a pointer-to-function. It
;carefully copies it into the global function
;pointer “THEEXTRAPROC”, then copies it back
;into A0 before doing the JSR to it. If you
;were writing this assembly code you would skip
;the extra MOVEA instruction.
MOVE.L   (A0),THEXTRAPROC ;Save in global                      
 ;variable
SUBQ.L   #$2,A7  ;space on stack for ;Boolean result
PEA      -$0008(A6);push pointer to XTRABlock
MOVEA.L  THEXTRAPROC,A0   ;copy function                       
 ;pointer back into A0
JSR      (A0)    ;and jump to address of the                   
 ;first instruction of the  ;function

;When the external function is done, it returns
;here via an RTS instruction
MOVE.B   (A7)+,D5;Get Boolean result ;from stack 
;program continues...

The heart of the matter is retrieving a handle to an external function from the global array XTRAArray. That handle points to a master pointer, which in turn points to the first instruction of the external function. The JSR (Jump to subroutine) instruction takes us to the function, which runs until it is finished. Its last instruction, furnished by the C compiler when the function is built, will be an RTS (Return from subroutine) which returns control to the DoCommand() code right where it left off.

Listing:  MakeDemo
c -mbg ch8 -d USEDUMP XDemo.c
link -t ‘APPL’ -c ‘XDEM’ XDemo.c.o 
 {clibraries}cruntime.o 
 {clibraries}cinterface.o 
 {libraries}interface.o 
 -o XDemo
rez -append XDemo.r -o XDemo
XDemo

c -mbg ch8   sinewave.c

link -w  -sg “Sine Wave” sinewave.c.o 
 -rt XTRA=134 -m SINEWAVE 
 {cLibraries}CRuntime.o 
 {clibraries}cinterface.o 
 {clibraries}CSanelib.o 
 {libraries}interface.o 
 -o XDemo 
Listing:  XDemo.h
/*---- XDemo.h ------------------
by Mark Lankton, 1989, for MacTutor
*/
/*---- typedefs --------------*/
/*
This is the (dirt-simple) structure which allows communication
with the external functions. You can get very fancy here.
*/
typedef struct XTRABlock{
 int    dataLength;
 short  *theData;
 }XTRABlock,*XTRABlockPtr,**XTRABlockHandle;

/*------ defines --------------*/
#define appleID  128
#define fileID   129
#define editID   130
#define externalsID131

#define menuCount4

#define appleMenu1
#define aboutMeItem1

#define fileMenu 2
#define newItem  1
#define closeItem2
#define quitItem 3

#define editMenu 3
#define undoItem 1
/*item 2 is a dividing line */
#define cutItem  3
#define copyItem 4
#define pasteItem5

#define externalsMenu4

#define aboutID  128

#define TRUE   0xFF
#define FALSE  0x00
#define minWidth 200
#define minHeight80
#define mBarHeight 20
#define scrollBarAdjust 15

#define osEvent  app4Evt  
#define suspendResumeMessage1 
#define resumeMask 1 
#define mouseMovedMessage 0xFA

/*---------- globals ------------*/
Boolean allDone;
Boolean inBackground;

/*The array of MenuHandles
is 1-based. */
MenuHandlemyMenus[menuCount + 1];  
EventRecord myEvent;
WindowPtr theWindow;
Rect    screenRect,defaultWRect;
Rect    dragRect;
RgnHandle eventRgn;
long    sleepTime;

/*The array of Handles to the external
functions is 1-based, too, to match up
with the menu item numbers. */
Handle  XTRAArray[33];    /*1-based array!*/
short   *screenData; /*Our data buffer. */
intscreenDataLength;
Listing:  XDemo.c
/*-------------- XDemo.c --------------*/
/*
A teeny program that demonstrates the use of external functions.
By Mark Lankton, 1989, for MacTutor.
*/

#if !defined(USEDUMP)

#include <types.h>
#include <resources.h>
#include <quickdraw.h>
#include <windows.h>
#include <OSUtils.h>
#include <OSEvents.h>
#include <memory.h>
#include <fonts.h> 
#include <events.h>
#include <controls.h>
#include <menus.h> 
#include <dialogs.h> 
#include <desk.h>
#include <toolutils.h>    
#include <segload.h> 
#include <DiskInit.h>

 #if defined(MAKEDUMP)
 #pragma dump “HeaderDumpFile”
 #endif
 
#else
 #pragma load “HeaderDumpFile”
 
#endif

#include “XDemo.h”

extern _DataInit();

/*------------ prototypes ----------------*/
void  InitVars(void);
void  SetUpMenus(void);
void  OpenAWindow(void);
void  DoEvents(void);
void  DoActivate(WindowPtr thisWindow,Boolean becomingActive);
void  DoUpdate(WindowPtr thisWindow);
void  Redraw(WindowPtr thisWindow);
void  DoGoAway(WindowPtr thisWindow);
void  DoGrow(WindowPtr thisWindow);
void  DoWindowZoom(WindowPtr thisWindow,short partCode);
void  DoContent(WindowPtr thisWindow);
void  DoKeys(void);
void  DoCommand(long menuResult);
void  DoAbout(void);
intmain(void);

/*Here’s the place we’ll plug in the external functions. */
pascal Boolean (*theXTRAProc)(XTRABlock *theBlockPtr);

/*-------------- functions ----------------*/
void
InitVars()
{
 screenRect = qd.screenBits.bounds;
 ClipRect(&screenRect);
 
 SetRect(&dragRect,4,24,screenRect.right-4,
 screenRect.bottom-4);
 
 SetRect(&defaultWRect,screenRect.left,screenRect.top,
 screenRect.left + 500, screenRect.top + 200);
 OffsetRect(&defaultWRect,10,40);
 
 eventRgn = NewRgn();/*set up an empty region to pass to WaitNextEvent 
*/
 RectRgn(eventRgn,&defaultWRect);
 
 screenDataLength = 512; /*A completely arbitrary value... */
 /*Get a pointer to a little data area and fill it with zeroes. */
 screenData = (short *)NewPtrClear(screenDataLength * 2);
 /*Now a very rudimentary error check...*/
 if(!screenData)
 {
 SysBeep(5);
 ExitToShell();
 }
}

void
SetUpMenus()
{
 int    i,howMany;
 short  thisID;
 ResTypethisType;
 Str255 thisName;
 
 myMenus[appleMenu] = GetMenu(appleID);
 AddResMenu(myMenus[appleMenu], (ResType) ‘DRVR’);
 
 myMenus[fileMenu] = GetMenu(fileID);
 myMenus[editMenu] = GetMenu(editID);
 myMenus[externalsMenu] = GetMenu(externalsID);

 for (i = 1;i <= menuCount;i++)    
 InsertMenu(myMenus[i],0);
 
 /*Check for XTRA resources... if any, load them in and plug the names 
into the Externals menu. We use the ‘1’ resource calls because the current 
resource file is XDemo itself.
 */
 howMany = Count1Resources(‘XTRA’);
 if(howMany > 32)
 howMany = 32;
 for(i = 1;i <= howMany;i++)
 {
 XTRAArray[i] = Get1IndResource(‘XTRA’,i);
 if(!XTRAArray[i])
 break;
 MoveHHi(XTRAArray[i]);
 HLock(XTRAArray[i]);
 GetResInfo(XTRAArray[i],&thisID,&thisType,thisName);
 AppendMenu(myMenus[externalsMenu],thisName);
 }
 DrawMenuBar();
}

void
OpenAWindow()
{
 short  width,height;
 
 theWindow = GetNewWindow(128,(Ptr)nil,(WindowPtr)-1);
 SetPort(theWindow);

 width = defaultWRect.right - defaultWRect.left;
 height = defaultWRect.bottom - defaultWRect.top;
 SizeWindow(theWindow,width,height,TRUE);
 
 MoveWindow(theWindow,defaultWRect.left,defaultWRect.top,TRUE);
 ShowWindow(theWindow);
 
 /*We’ll only have one window at a time, so... */
 DisableItem(myMenus[fileMenu],newItem);
}

void
DoEvents()
{
 short  whatCode;
 Point  dialogPoint;
 BooleanignoreResult;
 short  resultCode;
 WindowPtrwhichWindow;

 if(!inBackground)
 sleepTime = 0x0A;
 else
 sleepTime = 0xFF;
 
 /*No tricks in this demo, just make sure it’s arrow. */
 InitCursor();
 
 ignoreResult = WaitNextEvent(everyEvent,&myEvent,sleepTime,eventRgn);
 switch (myEvent.what)
 {
 case nullEvent: break;
 
 case mouseDown: whatCode = FindWindow(myEvent.where,&whichWindow);
 switch (whatCode){
 case inMenuBar: DoCommand(MenuSelect(myEvent.where));
 break;
 case inSysWindow: SystemClick(&myEvent,whichWindow);
   break;
 case inDrag:  DragWindow(whichWindow,myEvent.where,
 &dragRect);
  break;
  
 case inGoAway:  DoGoAway(whichWindow);
 break;
 
 case inGrow:  DoGrow(whichWindow);
 break;
 
 case inContent: DoContent(whichWindow);
 break;
 
 case inZoomIn:  if (TrackBox(whichWindow,myEvent.where,inZoomIn))
 {
 DoWindowZoom(whichWindow,inZoomIn);
 }
 break;
 
 case inZoomOut: if (TrackBox(whichWindow,myEvent.where,inZoomOut))
 {
 DoWindowZoom(whichWindow,inZoomOut);
 }
 break;
 
 }
 break;
 
 case keyDown:
 case autoKey: DoKeys();
 break;
 
 case activateEvt:
 DoActivate((WindowPtr)myEvent.message,
 myEvent.modifiers & activeFlag);
 break; 
 
 case updateEvt: DoUpdate((WindowPtr)myEvent.message);
 break;
 
 case diskEvt: if(HiWord(myEvent.message))
 {
 SetPt(&dialogPoint,85,90);
 resultCode = DIBadMount(dialogPoint,myEvent.message);
 }
 break;
 
 
 case osEvent:
 switch (myEvent.message >> 24) 
 { 
 case mouseMovedMessage:  break;
 
 case suspendResumeMessage: 
 inBackground = ((myEvent.message & resumeMask) == 0);
 DoActivate(FrontWindow(), !inBackground);
 break;
 }
 break;
 }
}

void
DoActivate(thisWindow,becomingActive)
 WindowPtrthisWindow;
 BooleanbecomingActive;
{
 SetPort(thisWindow);
 
 if (((WindowPeek)thisWindow)->windowKind >= 8)
 {
 DrawGrowIcon(thisWindow);
 if (becomingActive)
 {
 theWindow = thisWindow;
 RectRgn(eventRgn,&(thisWindow->portRect));
 DisableItem(myMenus[editMenu],0);
 }
 else
 EnableItem(myMenus[editMenu],0);
 }
}

void
DoUpdate(thisWindow)
 WindowPtrthisWindow;
{
 GrafPtrsavedPort;
 
 GetPort(&savedPort);
 SetPort(thisWindow);
 if (((WindowPeek)thisWindow)->windowKind >= 8)
 {
 ClipRect(&screenRect);
 BeginUpdate(thisWindow);
 EraseRect(&thisWindow->portRect);
 DrawGrowIcon(thisWindow);
 Redraw(thisWindow);
 EndUpdate(thisWindow);
 }
 SetPort(savedPort);
}

void
Redraw(thisWindow)
 WindowPtrthisWindow;
{
 GrafPtrsavedPort;
 Rect   localRect;
 short  center;
 int    i;
 
 GetPort(&savedPort);
 SetPort(thisWindow);
 
 localRect = thisWindow->portRect;
 localRect.right -= scrollBarAdjust;
 localRect.bottom -= scrollBarAdjust;
 ClipRect(&localRect);
 
 center = localRect.bottom / 2;
 MoveTo(0,center);
 PenPat(qd.gray);
 Line(localRect.right,0);
 PenPat(qd.black);
 MoveTo(0,center);
 
 for(i = 0; i < screenDataLength;i++)
 LineTo(i,center - *(screenData + i));
 
 SetPort(savedPort);
}

void
DoGoAway(thisWindow)
 WindowPtrthisWindow;
{
 /*
 Our windows have the default windowKind (8)... don’t
 try to close someone else’s window!
 */
 if(((WindowPeek)thisWindow)->windowKind < 8)
 return;
 
 DisposeWindow(thisWindow);

 if(GetNextEvent(everyEvent,&myEvent))
 DoActivate((WindowPtr)myEvent.message,myEvent.modifiers & activeFlag);
 if(GetNextEvent(everyEvent,&myEvent))
 DoActivate((WindowPtr)myEvent.message,myEvent.modifiers & activeFlag);
 
 /*Turn the “New” item back on so we 
 can make another window if we want. */
 EnableItem(myMenus[fileMenu],newItem);
}

void
DoGrow(thisWindow)
 WindowPtrthisWindow;
{
 Rect sizeRect;
 long newSize;
 int    newWidth, newHeight;
 
 if (thisWindow != FrontWindow())
 SelectWindow(thisWindow);
 else 
 { 
 SetRect(&sizeRect, minWidth,minHeight,screenRect.right,
 screenRect.bottom - mBarHeight);
 newSize = GrowWindow(thisWindow,myEvent.where,&sizeRect);
 if (newSize)
 {
 newWidth = LoWord(newSize);
 newHeight = HiWord(newSize);
 SizeWindow(thisWindow,newWidth,newHeight,TRUE);
 
 InvalRect(&(thisWindow->portRect)); 
 }
 }
}

void
DoWindowZoom(thisWindow,partCode)
 WindowPtrthisWindow;
 short  partCode;
{
 
 EraseRect(&(thisWindow->portRect));
 ZoomWindow(thisWindow,partCode,TRUE);
 
 InvalRect(&thisWindow->portRect);
}

void
DoContent(thisWindow)
 WindowPtrthisWindow;
{
 /*Not much action here... */
 if (thisWindow != FrontWindow())
 SelectWindow(thisWindow);
}

void
DoKeys()
{
 short  temp;
 long   menuChoice;
 
 /*Only worry about command-key stuff. */
 temp = (short)(myEvent.message & charCodeMask);
 if (myEvent.modifiers & cmdKey)
 {
 if (myEvent.what != autoKey)
 {
 menuChoice = MenuKey(temp);
 DoCommand(menuChoice);
 }
 }
}

void
DoCommand(menuResult)
 long menuResult;
{
 Str255 name;
 short    theMenu,theItem;
 XTRABlocktheBlock;
 BooleantheResult;
 
 theMenu = HiWord(menuResult);/*Gives you the resource ID. */
 theItem = LoWord(menuResult);

 switch (theMenu){
 case appleID: 
 if (theItem == aboutMeItem)
 {
  DoAbout();
  break;
 }
 else EnableItem(myMenus[3],undoItem);
 GetItem(myMenus[1], theItem, name);
 (void)OpenDeskAcc(name);
 break;
 
 case fileID: 
 switch (theItem)
 {
 case newItem:   OpenAWindow();
 break;
 
 case closeItem: DoGoAway(FrontWindow());
 break;
 
 case quitItem:  allDone = TRUE;
 break;
 }
 break;
 
 /*We don’t use the Edit menu at all. */
 case editID:    break;
 
 case externalsID: theBlock.dataLength = screenDataLength;
 theBlock.theData = screenData;
 (ProcPtr)theXTRAProc = (ProcPtr)*(XTRAArray[theItem]);
 theResult = (*theXTRAProc)(&theBlock);
 if(!theResult)
 /*Do something here */
 ;
 InvalRect(&(theWindow->portRect));
 break;
 
 }
 HiliteMenu(0);
}  

void
DoAbout()
{
 DialogPtrthisDialog;
 GrafPtrsavedPort;
 Booleanwait = TRUE;
 Point  thePoint;
 
 GetPort(&savedPort);
 thisDialog = GetNewDialog(aboutID,nil,(WindowPtr)-1);
 SetPort(thisDialog);
 
 DrawDialog(thisDialog);
 
 while (wait)
 {
 if (GetNextEvent(mDownMask,&myEvent))
 {
 thePoint = myEvent.where;
 GlobalToLocal(&thePoint);
 if (PtInRect(thePoint,&thisDialog->portRect))
 wait = FALSE;
 else
 SysBeep(5);
 }
 }
 
 SetPort(savedPort);
 DisposDialog(thisDialog);
}

int
main()
{
 UnloadSeg(_DataInit);
 InitGraf(&qd.thePort);
 InitFonts();
 FlushEvents(everyEvent,0);
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs(nil);
 InitCursor();
 
 MaxApplZone();
 MoreMasters();
 MoreMasters();
 
 InitVars();
 SetUpMenus();
 OpenAWindow();  

 do {
 DoEvents();
 } while (allDone == FALSE);
 
 ExitToShell();
}
Listing:  SineWave.c

/*---------- SineWave.c ------------*/
/*A sample external function for XDemo.
by Mark Lankton, 1989 for MacTutor
*/
#include <types.h>
#include <resources.h>
#include <OSUtils.h>
#include <OSEvents.h>
#include <SANE.h>
#include <math.h>
#include <stdlib.h>

typedef struct XTRABlock{
 int    dataLength;
 short  *theData;
 }XTRABlock,*XTRABlockPtr,**XTRABlockHandle;
 
#define TRUE   0xFF;
#define FALSE  0x00;

#define twopi  (3.14159265358979 * 2)
/************************** prototype *********************/
pascal Boolean SineWave(XTRABlock  *blockPtr);

/************************** function **********************/
pascal Boolean
SineWave(blockPtr)
 XTRABlock *blockPtr;
{
 int    i;
 extended temp;
 
 for(i = 0; i < blockPtr->dataLength;i++)
 {
 temp = (extended)(i * (twopi / 100));
 *(blockPtr->theData + i) = (short)rint(sin(temp) * 20);
 }
 return TRUE;
}

Listting:  add20.c
/*---------- Add20.c ------------*/
/*A sample external function for XDemo.
by Mark Lankton, 1989 for MacTutor
*/
#include <types.h>
#include <resources.h>
#include <OSUtils.h>
#include <OSEvents.h>
#include <SANE.h>
#include <math.h>
#include <stdlib.h>

typedef struct XTRABlock{
 int    dataLength;
 short  *theData;
 }XTRABlock,*XTRABlockPtr,**XTRABlockHandle;
 
#define TRUE   0xFF;
#define FALSE  0x00;

/*---------- prototype ------------*/
pascal Boolean Add20(XTRABlock*blockPtr);

/*---------- function ------------*/
pascal Boolean
Add20(blockPtr)
 XTRABlock *blockPtr;
{
 int    i;
 
 for(i = 0; i < blockPtr->dataLength;i++)
 {
 *((blockPtr->theData) + i) += 20;
 }
 return TRUE;
}
Listing:  XDemo.r
/*---------- XDemo.r ----------------*/
/*by Mark Lankton, 1989 for Mac Tutor */
#include “Types.r”

#define AllItems 0x7FFFFFFF /* 31 flags */
#define MenuItem10b00001
#define MenuItem20b00010

type ‘XDEM’ as ‘STR ‘;

/* menus */
resource ‘MENU’ (128, “Apple”, preload) {
 128, textMenuProc,
 AllItems & ~MenuItem2, /* Disable item #2 */
 enabled, apple,
 {
 /* 1 */
 “About This ”,
 noicon, nokey, nomark, plain;
 
 /* 2 */
 “-”,
 noicon, nokey, nomark, plain
 }
};

resource ‘MENU’ (129, “File”, preload) {
 129, textMenuProc,
 AllEnabled,
 enabled, “File”,
 {
 /* 1 */
 “New”,
 noicon, “N”, nomark, plain;
 /* 2 */
 “Close”,
 noicon, “W”, nomark, plain;
 /* 3 */
 “Quit”,
 noicon, “Q”, nomark, plain
 }
};

resource ‘MENU’ (130, “Edit”, preload) {
 130, textMenuProc,
 AllItems & ~(MenuItem2), 
 enabled, “Edit”,
  {
   /* 1 */
 “Undo”,
 noicon, “Z”, nomark, plain;
 /* 2 */
 “-”,
 noicon, nokey, nomark, plain;
 /* 3 */
 “Cut”,
 noicon, “X”, nomark, plain;
 /* 4 */
 “Copy”,
 noicon, “C”, nomark, plain;
 /* 5 */
 “Paste”,
 noicon, “V”, nomark, plain
 }
};

resource ‘MENU’ (131,”Externals”) {
 131,textMenuProc,
 AllEnabled,
 enabled,”Externals”,
 {
 /*No items here until start-up time! */
 }
};

/* Windows */

resource ‘WIND’ (128) {
 {40, 10, 200, 200},
 zoomDocProc,
 invisible,
 goAway,
 0x0,
 “The Data”
};

/* DLOG */
resource ‘DLOG’ (128) {
 {40, 40, 240, 380},
 dBoxProc,
 visible,
 goAway,
 0x0,
 128,
 “”
};

resource ‘DITL’ (128) {
 { /* array DITLarray: 2 elements */
 /* [1] */
 {67, 56, 85, 289},
 StaticText {
 disabled,
 “External function demo program”
 },
 /* [2] */
 {110, 90, 152, 253},
 StaticText {
 disabled,
 “by Mark Lankton, 1989, for MacTutor.”
 }
 }
};

/* SIZE */
resource ‘SIZE’ (-1) {
 dontSaveScreen,
 acceptSuspendResumeEvents,
 enableOptionSwitch,
 canBackground,  
 multiFinderAware, 
 backgroundAndForeground,
 dontGetFrontClicks,
 ignoreChildDiedEvents,
 is32BitCompatible,
 reserved,
 reserved,
 reserved,
 reserved,
 reserved,
 reserved,
 reserved,
 24576,
 24576
};

resource ‘XDEM’ (0) {
 “Demo of external function tricks for MacTutor, 8/89”
};

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Safari Technology Preview 10.2 - The new...
Safari Technology Preview contains the most recent additions and improvements to WebKit and the latest advances in Safari web technologies. And once installed, you will receive notifications of... Read more
Together 3.8 - Store and organize all of...
Together helps you organize your Mac, giving you the ability to store, edit and preview your files in a single clean, uncluttered interface. Features Smart storage. With simple drag-and-drop... Read more
GraphicConverter 10.4 - $39.95
GraphicConverter is an all-purpose image-editing program that can import 200 different graphic-based formats, edit the image, and export it to any of 80 available file formats. The high-end editing... Read more
beaTunes 4.6.13 - Organize your music co...
beaTunes is a full-featured music player and organizational tool for music collections. How well organized is your music library? Are your artists always spelled the same way? Any R.E.M. vs REM?... Read more
Vienna 3.1.9 :e81515b: - RSS and Atom ne...
Vienna is a freeware and Open-Source RSS/Atom newsreader with article storage and management via a SQLite database, written in Objective-C and Cocoa, for the OS X operating system. It provides... Read more
iExplorer 4.0.12.0 - View and transfer f...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
OpenEmu 2.0.5 - Open Source game-emulati...
OpenEmu is about to change the world of video game emulation, one console at a time... For the first time, the 'It just works' philosophy now extends to open source video game emulation on the Mac.... Read more
TextSoap 8.3.3 - Automate tedious text d...
TextSoap can automatically remove unwanted characters, fix up messed up carriage returns, and do pretty much anything else that we can think of to text. Save time and effort. Be more productive. Stop... Read more
Apple iTunes 12.6 - Play Apple Music and...
Apple iTunes lets you organize and stream Apple Music, download and watch video and listen to Podcasts. It can automatically download new music, app, and book purchases across all your devices and... Read more
Airmail 3.2.4 - Powerful, minimal email...
Airmail is an mail client with fast performance and intuitive interaction. Support for iCloud, MS Exchange, Gmail, Google Apps, IMAP, POP3, Yahoo!, AOL, Outlook.com, Live.com. Airmail was designed... Read more

Leap to victory in Nexx Studios new plat...
You’re always a hop, skip, and a jump away from a fiery death in Temple Jump, a new platformer-cum-endless runner from Nexx Studio. It’s out now on both iOS and Android if you’re an adventurer seeking treasure in a crumbling, pixel-laden temple. | Read more »
Failbetter Games details changes coming...
Sunless Sea, Failbetter Games' dark and gloomy sea explorer, sets sail for the iPad tomorrow. Ahead of the game's launch, Failbetter took to Twitter to discuss what will be different in the mobile version of the game. Many of the changes make... | Read more »
Splish, splash! The Pokémon GO Water Fes...
Niantic is back with a new festival for dedicated Pokémon GO collectors. The Water Festival officially kicks off today at 1 P.M. PDT and runs through March 29. Magikarp, Squirtle, Totodile, and their assorted evolved forms will be appearing at... | Read more »
Death Road to Canada (Games)
Death Road to Canada 1.0 Device: iOS Universal Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: Get it now at the low launch price! Price will go up a dollar every major update. Update news at the bottom of this... | Read more »
Bean's Quest Beginner's Guide:...
Bean's Quest is a new take on both the classic platformer and the endless runner, and it's free on the App Store for the time being. Instead of running constantly, you can't stop jumping. That adds a surprising new level of challenge to the game... | Read more »
How to rake in the cash in Bit City
Our last Bit City guide covered the basics. Now it's time to get into some of the more advanced techniques. In the later cities, cash flow becomes much more difficult, so you'll want to develop some strategies if you want to complete each level.... | Read more »
PixelTerra (Games)
PixelTerra 1.1.1 Device: iOS Universal Category: Games Price: $.99, Version: 1.1.1 (iTunes) Description: The world of PixelTerra is quite dangerous so you need to build a shelter, find some food supply and get ready to protect... | Read more »
Tokaido™ (Games)
Tokaido™ 1.0 Device: iOS Universal Category: Games Price: $6.99, Version: 1.0 (iTunes) Description: Discover the digital adaptation of Tokaido, the boardgame phenomenon that has already sold more than 250,000 copies worldwide, and... | Read more »
Card Thief (Games)
Card Thief 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Card Thief is a solitaire style stealth game played with a deck of cards. In Card Thief you move through a deck of cards as a... | Read more »
Smilegate’s crafting battler Super Tank...
Super Tank Rumbleputs you in the seat of your very own, handcrafted tank. You can choose from over 100 different parts to create your Super Tank before taking it out to wreak havoc on your opponents in glorious PVP combat. Now, Smilegate is upping... | Read more »

Price Scanner via MacPrices.net

Logitech Rugged Combo Protective iPad Case an...
Logitech has announced its Logitech Rugged Combo, Logitech Rugged Case, and Logitech Add-on Keyboard for Rugged Case for Apple’s new, more affordable $329 9.7-inch iPad, a complete solution designed... Read more
T-Mobile To Offer iPhone 7 and iPhone 7 Plus...
T-Mobile has announced it will offer iPhone 7 and iPhone 7 Plus (PRODUCT)RED Special Edition in a vibrant red aluminum finish. The introduction of this special edition iPhone celebrates Apple’s 10... Read more
9-inch 128GB iPad Pros on sale for $50-$70 of...
B&H Photo has 9.7″ 128GB Apple WiFi iPad Pros on sale for up to $70 off MSRP, each including free shipping. B&H charges sales tax in NY only: - 9″ Space Gray 128GB WiFi iPad Pro: $649 $50... Read more
27-inch iMacs on sale for up to $200 off MSRP...
B&H Photo has 27″ Apple iMacs on sale for up to $200 off MSRP, each including free shipping plus NY sales tax only: - 27″ 3.3GHz iMac 5K: $2099 $200 off MSRP - 27″ 3.2GHz/1TB Fusion iMac 5K: $... Read more
Save up to $600 with Apple refurbished Mac Pr...
Apple has Certified Refurbished Mac Pros available for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The following... Read more
Save $230 with Apple Certified Refurbished 13...
Apple is now offering Certified Refurbished 2016 13″ 2.0GHz non-Touch Bar MacBook Pros for $230 off original MSRP. An Apple one-year warranty is included with each model, and shipping is free: - 13″... Read more
Apple price trackers, updated continuously
Scan our Apple Price Trackers for the latest information on sales, bundles, and availability on systems from Apple’s authorized internet/catalog resellers. We update the trackers continuously: - 15″... Read more
13-inch Touch Bar MacBook Pros on sale for up...
B&H Photo has the Apple 13″ Touch Bar MacBook Pros in stock today and on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.9GHz/512GB Touch Bar... Read more
Today only! 15-inch 2.7GHz Space Gray Touch B...
B&H Photo has the new 2016 15″ 2.7GHz Space Gray Apple Touch Bar MacBook Pro in stock today and on sale for $300 off MSRP for today only. Shipping is free, and B&H charges NY sales tax only... Read more
New $329 iPad A Fabulous Value; 10.5-Inch iPa...
Part of the iPad upgrade/new model puzzle is now in place. Yesterday, as KGI Securities financial services group analyst Ming-Chi Kuo last summer predicted they would, Apple released a new low-cost 9... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Fulltime aan de slag als shopmanager in een h...
Ben jij helemaal gek van Apple -producten en vind je het helemaal super om fulltime shopmanager te zijn in een jonge en hippe elektronicazaak? Wil jij werken in Read more
Starte Dein Karriere-Abenteuer in den Hauptst...
…mehrsprachigen Teams betreust Du Kunden von bekannten globale Marken wie Apple , Mercedes, Facebook, Expedia, und vielen anderen! Funktion Du wolltest schon Read more
Starte Dein Karriere-Abenteuer in den Hauptst...
…mehrsprachigen Teams betreust Du Kunden von bekannten globale Marken wie Apple , Mercedes, Facebook, Expedia, und vielen anderen! Funktion Du wolltest schon Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.