Path: utzoo!attcan!utgpu!watmath!iuvax!rutgers!bellcore!wind!sdh
From: sdh@wind.bellcore.com (Stephen D Hawley)
Newsgroups: comp.sys.mac
Subject: Re: Real Multifinder
Message-ID: <17421@bellcore.bellcore.com>
Date: 17 Aug 89 14:33:59 GMT
References: <46100321@uxe.cso.uiuc.edu> <1989Aug15.001507.14552@sj.ate.slb.com> <24626@iuvax.cs.indiana.edu> <3576@internal.Apple.COM> <1989Aug16.175351.24310@sj.ate.slb.com>
Reply-To: sdh@wind.UUCP (Stephen D Hawley)
Organization: Bellcore, Morristown, NJ
Lines: 105

In article <1989Aug16.175351.24310@sj.ate.slb.com> enk@slcs.slb.com (Edan Kabatchnik) writes:
>     Granted, (parts of) UNIX should be done away with.  But, there is a
>superior form to the main event loop: callbacks (found in X and Xerox
>Artificial Intelligence Workstations from which the Macintosh developed its
>user interface.)  Instead of having to dispatch on every possible event in the
>main event loop, one establishes a hook that is automatically called when an
>event takes place.

Yes, and no.  Callbacks are not superior, they are only different.  Keep in
mind that you have to do one of 2 things:
	1) make hooks for every possible call back routine in every
	  combination.  Set up all these vectors (in pascal, this will be
	  particularly painful without an initialized static data type),
	  and then go.
--or--
	2) Make a well thought out set of call back functions that are
	  grouped by types (like window events, controls, etc) and force
	  the call back routines to check a parameter for an action to be
	  taken.

The first is painful at best, the second is exactly the same as the event
loop.  The only difference is that you have taken the dispatcher away from
the user and put it somewhere else.  Think about it, when an event happens,
it would have to be sent through a function that picks a function pointer
from a vector of call back routines and executes it.  This is exactly
equivalent to having the all-too famous:
		switch(myEvent.what) {
		case ...
		}
Each case corresponds to a component of the vector of call back routines.

I do not think that either is superior at this level, only different.

Consider the following code:

#define N_EVENTS 16 /* from inside mac */
#define EVER (;;)
extern int no_op();
typedef int (*FUNCPTR)();
static FUNCPTR vector[N_EVENTS] = {
	no_op, no_op, no_op, no_op, no_op, no_op, no_op, no_op,
	no_op, no_op, no_op, no_op, no_op, no_op, no_op, no_op
};

int
no_op(evt)
eventRecord *evt;
{
	/* should a no_op return anything? */
	return(0);
}

SetEventCallBack(evt, func)
int evt;
FUNCPTR func;
{
	vector[evt] = func; /* record new callback */
}

FUNCPTR
GetEventCallBack(evt)
int evt;
{
	return(vector[evt]); /* return old callback */
}

SetNullEvent(evt)
int evt;
{
	SetEvent(evt, no_op); /* set a no_op */
}

DoEvents()
{
	EventRecord evt;

	 for EVER {
		/* infinite loop is no problem,
		 * just leave the program with a call to
		 * ExitToShell().
		 */
		if (GetNextEvent(everyEvent, &evt)) {
		/*
		 * Since the evnt codes go from 0-15, use
		 * that as an index into the vector, and
		 * execute.
		 */
			(*(vector[evt.what]))(&evt);
			/*
			 * pass in the event so the callback knows
			 * what happened.
			 */
		}
	}
}

Ok.  Now you have code to do all you events will callbacks.  There is
NO significant difference.  All you're doing is factoring out the switch
statement, and forcing yourself to write all the code to do calls to
SetCallBack() and GetCallBack().  The work isn't reduced, its just changed.

Steve Hawley
sdh@flash.bellcore.com
"Up is where you hang your hat."
	--Jim Blandy, computer scientist