The Mathematica 7 Release

Mathematica 7 was released today (okay, late last night). It has been under active development for 18 months. While not as gigantic in scope as Mathematica 6, it’s still a very solid, feature-filled upgrade.

The headlining features for version 7 are:

A list of all major new features is available here. In addition to the highly publicized features, here is an incomplete list of other improvements I worked on for this release:

  • QuickLook plugin provides previews for notebook documents on Mac OS X 10.5
  • text & cell selections use system highlight color rather than XOR drawing
  • better sub-pixel accuracy of screen drawing & vector graphics export
  • BezierCurve support for EPS & PDF Export, and PDF Import
  • PDF export can attach arbitrary files (including the source notebook) to the exported PDF file
  • PDF import can read file attachments
  • PDF import of encrypted files works with default (empty) or user-supplied password
  • decreased EPS & PDF export file sizes in some cases
  • decreased notebook file sizes in some cases
  • ControllerState supports MIDI devices (e.g. keyboards, mixers, etc.) on Mac OS X

Additionally, the minimum Mac OS X version increased from 10.3 to 10.4.

The Indy Marathon Chase

“Whoa, check out that guy! He makes Speedy Gonzalez look like Regular Gonzalez!”Philip J. Fry

Melissa runs marathons. I chase them.

Saturday she ran the inaugural Indianapolis Monumental Marathon.

I rode my bike to several different places on the course to cheer her on and take pictures.

The marathon went rather well for Melissa, and as a result, less well for me. For you see, she ran so fast that half of the places where I went to watch the race I arrived too late and missed her. The first time it was clear that she had already passed so I didn’t wait around long. The remaining times I waited several minutes before I could be sure she had already passed.

We were both wearing GPS watches, so using that data I put together this animation using Mathematica that shows our locations throughout the race. Melissa’s path is blue, Rob’s path is red. It’s kind of funny to watch my red path wait around at a certain location while Melissa moves farther and farther away.

Update 2009-04-17: I have written an entry for my company blog showing in great detail how I made this movie.

The World, Re-Justified

I just came across this web site describing The World, Justified, a piece of (ASCII) art created by a couple of Brazilians. Very interesting idea. I was able to replicate it using Mathematica 6 in just a few lines of code.

In[1]:=

map = CountryData[“World”, {“Shape”, “Equirectangular”}]

Out[1]=

In[2]:=

g = Rasterize[map, ImageSize→80]

Out[2]=

In[3]:=

raster = First @ Cases[g, _Raster, ∞] ;

In[4]:=

txt = Reverse @ First[raster]/.{ {255, 255, 255} →” “, {_, _, _} →”*”} ;

In[5]:=

CellPrint @ Cell[StringJoin @@ Riffle[StringJoin/@txt, “\n”], FontFamily→”Courier”]

In[6]:=

CellPrint @ Cell[StringJoin @@ Riffle[StringReplace[#, ” “→””] &/@txt, “\n”], TextAlignment→Left, FontFamily→”Courier”]

In[7]:=

CellPrint @ Cell[StringJoin @@ Riffle[StringReplace[#, ” “→””] &/@txt, “\n”], TextAlignment→Right, FontFamily→”Courier”]

In[8]:=

CellPrint @ Cell[StringJoin @@ Riffle[StringReplace[#, ” “→””] &/@txt, “\n”], TextAlignment→Center, FontFamily→”Courier”]

The Geoffender

You read me my rights and then you said “Let’s go” and nothing more.

Blondie (as covered by The Mr. T Experience).

The Iron Coder competition from the recent C4[2] conference I attended had a required API (iPhone OS’s CoreLocation) and a theme (paranoia). I actually did take a couple hours on Sunday morning to throw together a submission. Sure, it wasn’t going to be polished, but still creative perhaps. I figured several people would do something like a crime map. I tried a variant on that, dealing only with one (particularly nasty) type of crime.

The app I threw together is quite simple. You press the “Geoffend” button. The app determines your location from the iPhone’s built in GPS. The app fetches from the internet and displays a list of registered sex offenders who live near your current location. If that doesn’t induce paranoia, I don’t know what will. I call it Geoffender (combining Geo with offender).

I got the app working in the iPhone simulator on my computer, but I ran into problems running the app on my actual iPhone hardware. I recently acquired a new iPhone, and I hadn’t yet set it up for development. When I tried to set it up before the contest I absolutely could not get it working. I tried everything. The iPhone platform is pretty well locked down. In order to do development you have to have various digital certificates and keys from Apple. I have these. The problem is installing them correctly is not completely straightforward. So the demo was a no go.

It’s just as well. There were many other submissions to the contest which were much better. I also learned a few things, so the time wasn’t wasted.

The Backscatter

Yesterday was mostly wasted. At 7:37 A.M., shortly before I intended to sit down to start working, I started receiving spam backscatter emails. What happened was some asshole, somewhere in the world, sent probably hundreds of thousands of spam emails with my email address forged in the From: line. Tens of thousands of the email addresses they spammed were invalid. When one sends email to an invalid address the mail server usually sends an automated reply informing you the address was invalid. Since my address was the From: address of these spams, I received automated responses to these spam emails yesterday. It continued all day. I received dozens per minute. All day.

I was completely overwhelmed and there was absolutely nothing I could do about it. Over the past few years I have received a few random backscatters. A couple months ago I received 8-10 in a single instance. A few weeks ago I received about 400 at once. Yesterday I received 15,673. The first several hundred went straight to my inbox. Eventually my junk mail filter started recognizing them and filtered thousands automatically, but there were still several hundred in my inbox. I literally spent all day dealing with this shit. There was at least one legitimate, work-related email that I completely missed because of this. I only learned about it by searching my junk mailbox after someone asked me about the missing email. Who knows how many other legitimate emails are mixed in with them? I am still receiving them, but thankfully they are becoming less frequent (maybe one per hour now).

What an asshole.

The 2008 WWDC Keynote

Yesterday was the keynote presentation for the 2008 Apple Worldwide Developer Conference (WWDC) in San Francisco. The big news Steve Jobs announced during the keynote was a new iPhone revision with faster cellular networking (nice in theory but only available in major cities, i.e. nowhere near where I live), GPS (yay), a lower price tag (yay), better battery life (yay), a non-cracked screen (I suppose the original model has this feature as well, until you drop it on concrete), and a higher monthly service fee to AT&T (boo).

2008-06-10-Cracked-Screen
Stupid cracked screen

Even more interesting to me is the iPhone 2.0 software (which will also be available for first generation iPhones) which allows 3rd party applications to be installed and run. This was originally announced a couple months ago but a few more details were provided during the keynote. There were also lots of demonstrations from 3rd party developers who have been working on games, productivity, and educational applications for the iPhone. Who knows, maybe the DataBook software will be available on an iPhone in the future…

The Reunion

Last weekend in Washington D.C. there was a reunion of former ASP summer staff members. I worked for the Appalachia Service Project during the summers of 1997-2000 while I was in college. Around 75 former staffers descended upon the nation’s capital (well, a number of them already live there) for festivities.

ASP Summer Staff 1998 (I’m the one in the bright yellow shirt)

After a 12 hour drive (and a one hour bike ride) I arrived at the Potter’s House bookstore and cafe for some live bluegrass music. As soon as I walked in I recognized four or five familiar friendly faces… and about 30 very unfamiliar ones. Has it been that long?

I scanned the store and noticed my former staffer Jill on the other side of the room. The way ASP works is there is a separate center in each of 20 or 25 counties throughout central Appalachia. Each center is run by four staffers. Jill & I worked together at the same center in the summer of 1999. Great, I thought, I’ll go say hello.

Jill & Rob in 1999

As I walked about half way across the room I took another look, then I stopped in my tracks. Wait a minute… that’s not Jill… is it? She looks like she hasn’t aged at all. My confusion was further compounded when she made eye contact with me and showed absolutely no sign of recognition. This was a person I worked with very closely for several months, what’s going on???

While I was standing there, not knowing what to do, Phoebe walked by and I asked her if that was Jill. No. It was Jill’s younger sister, who looks exactly like Jill, and who apparently also worked for ASP. Well, that solves that mystery. The real Jill was present at the banquet on Saturday night. We had a good chance to catch up a little. She is finishing up with medical school and getting married soon. ¡Felicitaciones!

After the long drive and a few hours in the coffee house I was really ready for bed. Will works in D.C. for CEDC, a nonprofit that has dormitories, and he arranged for me (and a few others) to stay there fairly inexpensively. It was nice, and the price couldn’t be beat. I slept in a little Saturday morning before heading downtown to see the monuments.

In the afternoon, the event organizers planned a service project at the Capital Area Food Bank. Around 20 or so of us from ASP helped unbox bulk canned foods and reorganize and repackage them for distribution to individuals and food kitchens. It was a good time. I used the opportunity to get to know some of the younger people who were on staff in the years following me. Interestingly, a few of them already knew who I was. You get bonus points if you can guess how — The Rob Song.

After finishing up at the food bank I got ready and headed over to the banquet on the other side of town. During my cross town trip I encountered four roundabouts (traffic circles). Fantastic. Anyone who knows me knows that I love roundabouts. I believe they are the greatest traffic control device in existence. The problem is that D.C. has the worst roundabouts I’ve ever seen (in the U.S., Nicaragua, France, or Belgium). Typically, traffic entering the roundabout has to yield to traffic already in the roundabout, but traffic in the roundabout should keep moving. Not only did these D.C. roundabouts have stop lights (rather than yield signs) upon entering the circle, they also had stoplights inside the circle itself. ¡Que terrible! So instead of none of the traffic stopping ever, all of the traffic stopped multiple times. This is how the first three roundabouts were. When I reached the fourth roundabout, it was normal (no stop lights, no stopping). Traffic moved so much more smoothly and safely through the fourth roundabout. I cannot possibly fathom why the first three were so messed up.

Once I got to the reunion banquet I saw many more familiar faces. There were a couple dozen people whose employment with ASP overlapped mine. One of the first people I saw was Ben, who immediately asked me “were you ever on stage during a Steve Jobs keynote?” That came out of nowhere. Yes, I was on stage during Steve Jobs’ 2005 WWDC keynote presentation. My boss (Theo) & I demonstrated Mathematica running on the just announced Intel based Mac computers. Ben watched the video of the keynote (here’s the relevant clip), recognized me, and remembered to ask me about it the next time he saw me (three years later). Funny.

Besides Jill, my only other former staffer (with whom I worked closely) at the reunion was Meryl (a.k.a. Marl, who was recently married). We too had a good chance to catch up with each other (though we didn’t get the chance to reenact our infamous, no holds barred wrestling matches).

Rob stuffs Meryl into a trash can in 2000

Meryl gets her revenge

Some poor, innocent bystander gets caught in the middle

The banquet was a really good time.

  • We ate Appalachian food (whatever that is).
  • We viewed a wonderful slideshow of old ASP photos.
  • A preselected representative of each decade (70’s, 80’s, 90’s, 00’s) shared some of their fondest ASP memories. Michelle K. (from my decade, the 90’s) did an exceptional job.
  • We sang a few of the songs we used to sing on a daily basis while working for ASP. It was a little emotional.
  • There was an auction of memorabilia to benefit ASP. I ended up with a really neat framed photograph of a curvy mountain road. I like mountains.
  • There was a contest to guess the number of skittles in a jar. Michelle R. & I both guessed 1200, which was the closest to the actual count of 1106. For this we won the skittles, which she yielded to me. Fortunately, the rest of the group helped pare that number down a little before we left.

After the official reunion ended, the unofficial reunion moved to some bar in an extremely busy area of D.C. I should have planned a little better, but I didn’t, and I ended up driving there. I searched for 25 minutes to find a parking place and I ended up in the tightest spot in which I’ve ever parked (and remember Iris is a very small car).

We all got a little carried away chatting it up with old friends. I ended up getting back to my quarters at 3:30 am. I haven’t stayed up that late in many a year. Juech was planning to run the Cherry Blossom 10 mile race at 7:30 am on Sunday. I since found out that he did indeed finish… barely. It was still probably before I even woke up.

The Heart Shaped Window

On our first Valentine’s Day Melissa was mad at me for some reason. So for our second Valentine’s Day I decided to make her a really special gift. I stayed up until about 2-3 a.m. (which was quite rare, as I like to get to bed early) the night before writing a computer program that made a heart shaped window with a customized message in it. With the state of software in 1998 this was actually a pretty challenging task, but I worked hard and made something I thought we could both be proud of.

When I showed it to Melissa the next day (2/14/1998) I didn’t quite get the reaction I was hoping for. Her attitude was basically that if I hadn’t spent so much time working on the program I would have had more time to spend with her. Sheesh.

Anyway, here’s what it looked like:

And here’s the source code (wow I used to write some really ugly code):

//
// HeartWDEF.c
//

#define HASGOAWAY 0


pascal long main(short, CWindowPtr, short, long);
long  DoDraw(CWindowPtr, long);
long  DoHit(CWindowPtr, long);
long  DoCalcRgns(CWindowPtr, long);
void GetStrucRegion(CWindowPtr window, RgnHandle rgn);
void  GetGoAwayBox(CWindowPtr w, Rect *r);

OSErr PictToRgn(PicHandle pic, RgnHandle rgn){
 int   width = 0, height = 0;
 GrafPtr  mask, savePort;
 Rect   r = (*pic)->picFrame;
 OSErr  err;
 
 width = r.right - r.left;
 height = r.bottom - r.top;
 
 SetRect(&r, 0, 0, width, height);
 //OffsetRect(&r, r.left, r.top);

 mask = (GrafPtr) NewPtr(sizeof(GrafPort));
 OpenPort(mask);
 
 mask->portBits.bounds = r;
 mask->portBits.rowBytes = ((width + 15) / 8);
 mask->portBits.baseAddr = NewPtr(mask->portBits.rowBytes * height);
 
 GetPort(&savePort);
 SetPort(mask);
 
 EraseRect(&r);
 DrawPicture(pic, &r);
 
 SetPort(savePort);
 
 err = BitMapToRegion(rgn, &(mask->portBits));
 
 DisposePtr((Ptr) mask->portBits.baseAddr);
 DisposePtr((Ptr) mask);
 
 return err;
}

pascal long main(short varCode, CWindowPtr w, short message, long param){
 long  val = 0;
 
 switch(message){
  case wDraw:
   val = DoDraw(w, param);
   break;
  
  case wHit:
   val = DoHit(w, param);
   break;
   
  case wCalcRgns:
   val = DoCalcRgns(w, param);
   break;
   
  case wNew:
  case wDispose:
   break;
 }
 return val;
}

long DoDraw(CWindowPtr w, long param){
 long  val = 0;
 WindowPeek wp;
 Rect  r;
 RGBColor save;
 PicHandle pict;
 Point  p;
 WindowPtr window;
 GWorldPtr saveWorld;
 GDHandle device;
 PenState savePen;
 
 GetForeColor(&save);
 GetPenState(&savePen);
 
 PenNormal();
 wp = (WindowPeek) w;
 
 if(param == 0){
  // copy the regions so we can offset them
  RgnHandle diff = NewRgn();
  
  DiffRgn(wp->strucRgn, wp->contRgn, diff);
  
  ForeColor(redColor);
  PaintRgn(diff);
  
  PenNormal();
  /*if(wp->hilited){
   ForeColor(blackColor);
   FrameRgn(diff);
  }*/
  
  DisposeRgn(diff);

#if HASGOAWAY  
  if(wp->hilited){
   GetGoAwayBox(w, &r);
   
   ForeColor(whiteColor);
   PaintRect(&r);
  }
#endif

 }
 
#if HASGOAWAY 
 else if(param == wInGoAway){
  GetGoAwayBox(w, &r);
  InvertRect(&r);
 }
#endif

 SetPenState(&savePen);
 RGBForeColor(&save);
 
 return val;
}

long DoHit(CWindowPtr w, long param){
 long  val = 0;
 WindowPeek wp;
 Point  p;
 Rect  r;
 
 p.h = LoWord(param);
 p.v = HiWord(param);
 
 wp = (WindowPeek) w;
 
 if(PtInRgn(p, wp->contRgn)) val = wInContent;
 
 else if(PtInRgn(p, wp->strucRgn)){
  val = wInDrag;
  
#if HASGOAWAY  
  GetGoAwayBox(w, &r);
  if(PtInRect(p, &r)) val = wInGoAway;
#endif

 }
 else val = wNoHit;
 
 return val;
}

long DoCalcRgns(CWindowPtr w, long param){
 SInt32  val = 0;
 WindowPeek wp;
 PicHandle pic;
 Rect  r;
 RgnHandle rgn;
 
 wp = (WindowPeek) w;
 
 r = w->portRect;
 OffsetRect(&r, - ((WindowPtr)w)->portBits.bounds.left, - ((WindowPtr)w)->portBits.bounds.top);
 
 rgn = NewRgn();
 pic = GetPicture(4096);
 PictToRgn(pic, rgn);
 ReleaseResource((Handle) pic);
 OffsetRgn(rgn, r.left, r.top);
 CopyRgn(rgn, wp->strucRgn);
 DisposeRgn(rgn);
 
 rgn = NewRgn();
 pic = GetPicture(4097);
 PictToRgn(pic, rgn);
 ReleaseResource((Handle) pic);
 OffsetRgn(rgn, r.left, r.top);
 CopyRgn(rgn, wp->contRgn);
 DisposeRgn(rgn);
 
 return val;
}

void GetGoAwayBox(CWindowPtr w, Rect *r){
 WindowPeek  wp = (WindowPeek) w;
 
 *r = (*(wp->contRgn))->rgnBBox;
 
 r->top -= 0;
 r->bottom = r->top + 10;
 r->left += 12;
 r->right = r->left + 10;
}


//
// HeartWindow.c
//
#define kNumFields 7

typedef struct{
	Str255	str[kNumFields];
} Message;
typedef Message* MessagePtr;
typedef MessagePtr* MessageHandle;

void DoUpdate(void);

Boolean 		quitting;
WindowPtr		window;
Message			message;

void LiveDragger(WindowPtr w, Point p, Rect *r){
	Point				mouse, newmouse, original, origin;
	EventRecord			e;
	GrafPtr				save;
	
	EventAvail(mouseDown, &e);
	
	if(!(e.modifiers & optionKey)){
		DragWindow(w, p, r);
	}
	else{
		GetPort(&save);
		SetPort(w);
		
		GetMouse(&mouse);
		LocalToGlobal(&mouse);
		SetPt(&origin, 0, 0);
		LocalToGlobal(&origin);
		original = origin;
		
		SetPort(save);
		
		while(StillDown()){
			if(WaitNextEvent(updateMask, &e, 0, NULL)){
				DoUpdate();
			}
			else{
				GetPort(&save);
				SetPort(w);
				
				GetMouse(&newmouse);
				LocalToGlobal(&newmouse);
				
				if(newmouse.h != mouse.h || newmouse.v != mouse.v){
					if(PtInRect(newmouse, r)){
						MoveWindow(w, origin.h + newmouse.h - mouse.h, origin.v + newmouse.v - mouse.v, false);
						SetPt(&origin,0,0);
						LocalToGlobal(&origin);
						mouse = newmouse;
					}
					else{
						MoveWindow(w, original.h, original.v, false);
					}
				}
				SetPort(save);
			}
		}
		
		GetPort(&save);
		while(w != nil){
			SetPort(w);
			InvalRect(&w->portRect);
			w = (WindowPtr) ((WindowPeek) w)->nextWindow;
		}
		SetPort(save);	
	}		
}

void GetItemHandle(DialogPtr d, short index, Handle *h){
	short	type;
	Rect	r;
	
	GetDialogItem(d, index, &type, h, &r);
}

void EraseWindow(){
	Rect		r;
	GrafPtr		savePort;
	
	GetPort(&savePort);
	SetPort(window);
			
	r = (*(((WindowPeek) window)->contRgn))->rgnBBox;
	OffsetRect(&r, -r.left, -r.top);
	EraseRect(&r);
	InvalRect(&r);
	
	SetPort(savePort);
}

void PStringCopy( ConstStr255Param srcString, Str255 destString ){
	SInt16 index = StrLength( srcString );

	do {
		*destString++ = *srcString++;
	} while ( --index >= 0 );
}

void CopyMessage(MessagePtr src, MessagePtr dst){
	int		i;
	
	for(i = 0; i < kNumFields; i++) PStringCopy(src->str[i], dst->str[i]);
}

void LoadMessage(){
	int				i;
	MessageHandle 	mh = (MessageHandle) GetResource('hwms', 128);
	
	if(mh == nil){	
		for(i = 0; i < kNumFields; i++){
			GetIndString(message.str[i], 128, i + 1);
		}
	}
	else{
		CopyMessage(*mh, &message);
		ReleaseResource((Handle) mh);
	}
}

void EditMessage(){
	short	item = 0, i;
	Str255	str;
	Handle	h;
	
	DialogPtr d = GetNewDialog(129, nil, (WindowPtr) -1);
	
	SetDialogDefaultItem(d, 1);
	SetDialogCancelItem(d, 2);
	SetDialogTracksCursor(d, true);
	
	for(i = 0; i < kNumFields; i++){
		GetItemHandle(d, i + 3, &h);
		
		SetDialogItemText(h, message.str[i]);
	}
	
	ShowWindow(d);
	while(item != 1 && item != 2){
		ModalDialog(nil, &item);
	}
	HideWindow(d);
	
	// if the user hit ok
	if(item == 1){
		MessageHandle	old;
		
		// save the info to the app's resource fork
		for(i = 0; i < kNumFields; i++){
			GetItemHandle(d, i + 3, &h);
			
			GetDialogItemText(h, message.str[i]);
		}
		
		old = (MessageHandle) GetResource('hwms', 128);
		if(old == nil){
			old = (MessageHandle) NewHandle(sizeof(Message));
		
			if(old == nil){
				StopAlert(130, nil);
			}
            else{	
				CopyMessage(&message, *old);
		
				AddResource((Handle) old, 'hwms', 128, "\p");
				ChangedResource((Handle) old);
				
				ReleaseResource((Handle) old);
			}
		}
		else{
			CopyMessage(&message, *old);
		
			WriteResource((Handle) old);
			ChangedResource((Handle) old);
			
			ReleaseResource((Handle) old);
		}
		
		
		EraseWindow();
	}
	
	DisposeDialog(d);
	InitCursor();
}

void DoAbout(){
	Alert(128, nil);
}

void DoAppleChoice(short item){
	if(item == 1) DoAbout();
	else{
		Str255	daName;

		GetMenuItemText(GetMenuHandle(128), item, daName);
		OpenDeskAcc(daName);
	}
}

void DoFileChoice(short item){
	switch(item){
		case 1:
			// edit the text
			EditMessage();
			break;
			
		default:
			quitting = true;
			break;
	}
}	

void DoEditChoice(short item){

}

void DoMenuChoice(long choice, EventModifiers modifiers){
	short	id, item;

	id = HiWord(choice);
	item = LoWord(choice);

	switch(id){
		case 128:
			DoAppleChoice(item);
			break;

		case 129:
			DoFileChoice(item);
			break;

		case 130:
			DoEditChoice(item);
			break;
	}

	HiliteMenu(0);
}

void DoDiskEvent(const EventRecord *e){
	Point 	dialogCorner;

	if((e->message >> 16 ) != noErr){
		SetPt(&dialogCorner, 112, 80);
		DIBadMount(dialogCorner, e->message);
	}
}

void DoOSEvent(const EventRecord *e){
	SInt16		osMessage;
	WindowPtr	w;

	osMessage = (e->message & osEvtMessageMask) >> 24;

	switch(osMessage){
		case suspendResumeMessage:
			break;

		case mouseMovedMessage:
			break;
	}
}

void DoNullEvent(const EventRecord *e){
	WindowPtr 	w;	
}

void DrawCenteredString(Str255 str, short y){
	MoveTo((328 - StringWidth(str))/2, y);
	DrawString(str);
}

void DoUpdate(){
	GrafPtr		savePort;
	RGBColor	saveColor;
	Rect		r;
	int			size = 18, i, y = 116;
	Str255		str;
	
	BeginUpdate(window);
	GetForeColor(&saveColor);
	GetPort(&savePort);
	SetPort(window);
	
	ForeColor(redColor);
	TextFont(applFont);
	TextSize(size);
	
	for(i = 0; i < kNumFields; i++, y += (size + 6)){
		DrawCenteredString(message.str[i], y);
	}
	
	SetPort(savePort);
	RGBForeColor(&saveColor);
	EndUpdate(window);
}

void DoMouseDown(EventRecord *e){
	WindowPtr	w;
	SInt16 		part;

	part = FindWindow(e->where, &w);

	switch(part){
		case inMenuBar:
			DoMenuChoice(MenuSelect(e->where), e->modifiers);
			break;
			
		case inSysWindow:
			SystemClick(e, w);
			break;

		case inContent:
			//quitting = true;
			break;

		case inDrag:
			//DragWindow(w, e->where, &qd.screenBits.bounds);
			LiveDragger(w, e->where, &qd.screenBits.bounds);
			break;

		case inGoAway:
			if(TrackGoAway(w, e->where)){
				quitting = true;
			}
			break;
	}
}

void DoKeyDown(const EventRecord *e){
	SInt16 	key;

	key = (e->message & charCodeMask);

	if(e->modifiers & cmdKey){
		DoMenuChoice(MenuKey(key), e->modifiers);
	}
}

void ProcessEvents(void){
	EventRecord 	e;

	if(WaitNextEvent(everyEvent, &e, 0, nil)){
		
		SetCursor(&qd.arrow);
		
		switch(e.what){
			case nullEvent:
				DoNullEvent(&e);
				break;
	
			case mouseDown:
				DoMouseDown(&e);
				break;
				
			case keyDown:
			case autoKey:
				DoKeyDown(&e);
				break;
			
			case updateEvt:
				DoUpdate();
			
			case diskEvt:
				DoDiskEvent(&e);
				break;
			
			case kHighLevelEvent:
				AEProcessAppleEvent(&e);
				break;
		}
	}
	
	DoNullEvent(&e);
}

pascal OSErr	HandleOpenDocument(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){
	return noErr;
}

pascal OSErr	HandleOpenApplication(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){
	
	return noErr;
}

pascal OSErr	HandleQuitApplication(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){
	
	quitting = true;
	
	return noErr;
}

OSErr InitEvents(void){
	OSErr	err;

	if((err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, NewAEEventHandlerProc(HandleOpenApplication), 0, false)) != noErr)
		return err;

	if((err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerProc(HandleOpenDocument), 0, false)) != noErr)
		return err;

	if((err = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerProc(HandleOpenDocument), 0, false)) != noErr)
		return err;

	if((err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerProc(HandleQuitApplication), 0, false)) != noErr)
		return err;

	return noErr;
}

void CheckMachine(){
	SysEnvRec	thisMac;

	SysEnvirons(7, &thisMac);
  	
  	if(!thisMac.hasColorQD){
  		StopAlert(131, nil);
  		ExitToShell();	
  	}
	
	if(thisMac.systemVersion < 0x0700){
		StopAlert(131, nil);
		ExitToShell();
	}
}

OSErr Initialize(void){
	OSErr		err;
	short		w1, w2, h1, h2;
	Handle		code;
	MenuHandle	menu;
	
	InitGraf(&qd.thePort);
	InitFonts();
	InitWindows();
	InitMenus();
	TEInit();	
	InitDialogs(nil);
	InitCursor();
	FlushEvents(everyEvent, 0);

	MaxApplZone();
	MoreMasters();
	MoreMasters();
	MoreMasters();

	CheckMachine();

	err = InitEvents();
	if(err != noErr) return err;
	
	LoadMessage(&message);
	
	SetMenuBar(GetNewMBar(128));
	menu = GetMenu(128);
	AppendResMenu(menu, 'DRVR');
	DrawMenuBar();
	
	quitting = false;
	
	window = GetNewCWindow(128, nil, (WindowPtr) -1);
	
	code = GetResource('WDEF',4096);						
	if(code != nil) ((WindowPeek) window)->windowDefProc = code;
	
	ShowWindow(window);
	
	return noErr;
}

void Finalize(void){
	HideWindow(window);
	DisposeWindow(window);
}

void main(void){
	if(Initialize() == noErr){
		
		while(!quitting){
			ProcessEvents();
		}
	}
	
	Finalize();
}