#include <stdio.h>
#include "gimmelib/gimmeall.h"
#define GIM_BUILTIN
#include "gimmelib/macros.h"

/***
 *  NOTE: graphing from a subtask does not work
 *  unfortunately I haven't figured out why yet!!
 *
 *
 *  this looks more complicated than it is --
 *  it's just that there are a lot of options for different features to demo
 *  just think of all the left-aligned if-statements, like this:
 *	if( COLOR ) {
 *	    ...
 *	} comment COLOR comment
 *  as equivalent to
 *	#ifdef COLOR
 *	    ...
 *	#endif COLOR
 *
 *  As a matter of fact, that's what they were but I hated recompiling
 *  all the time for different options, so I changed them.
 */


#define SBMDEPTH	3

#define IDCMP_FLAGS	(CLOSEWINDOW | GADGETUP | GADGETDOWN | MENUPICK)
#define WIN_FLAGS	(ACTIVATE | BACKDROP | BORDERLESS | WINDOWCLOSE \
			    | SMART_REFRESH | NOCAREREFRESH | RMBTRAP)

#define STDSTUFF	(GSS_INTUITION | GSS_GFX | GSS_DISKFONT | GSS_TIMER \
			    | GSS_SUBTASK | GSS_MEMCHAIN | GSS_DISABLEABORT)

#define SYS_GADG	0	/* system gadget -- do not use for my IDs */
#define BOOL_GADG	1
#define CLEAR_GADG	2
#define BOOL_TOG_GADG	3
#define BOOL_IMAG_GADG	4
#define STRING_GADG	5
#define STRING_INT_GADG 6
#define PROP_H_GADG	7
#define PROP_V_GADG	8
#define H_SCROLLBAR	9
#define V_SCROLLBAR	10
#define ONESCROLL1	100	/* secondary gadgets */
#define ONESCROLL2	101

#define MAX_GADG	10
#define FIRST_GADG	BOOL_GADG-1

#define DI_Z		2	/* size of image data below */
#define DI_X		64
#define DI_Y		5

USHORT firstImageData[] = {		/* garbage image */
    0x0110, 0x3420, 0x7862, 0xefca,
    0x0110, 0x3420, 0x7862, 0xefca,
    0x0110, 0x3420, 0x7862, 0xefca,
    0x0110, 0x3420, 0x7862, 0xefca,
    0x0110, 0x3420, 0x7862, 0xefca,

    0x0435, 0x3428, 0x7092, 0xe97a,
    0x0110, 0x3440, 0x7062, 0xef34,
    0x2340, 0x3445, 0x7060, 0xef4a,
    0x0150, 0x3420, 0x7862, 0xe4ca,
    0x0140, 0x6520, 0x7782, 0xef56
  };

struct TextAttr ta = {
    (STRPTR) "topaz.font",
    9,
    FS_NORMAL,
    FPF_DISKFONT
  };

NEWGRAPH newgraph = {
    (UBYTE *) "GIMME GRAPH", (UBYTE *) "howdy! any luck?",
    (UBYTE *) "MONEY $$$",
    NULL, NULL, &ta,
    GIM_RED, GIM_BLACK, GIM_CYAN, GIM_YELLOW,
    GGR_FILLTOX | GGR_BLACKTOX | GGR_X_SPACING | GGR_Y_MAX,
    GGR_INITORIGIN | GGR_HIRES | GGR_NOCLEARSTART,
    {70, 200,  0, 2,  0,  0, 1, 1},
    {100, 60,  0, 100,	0,  0, 2, 1},
    NULL
  };

#define NUM_COORDS1	5
SHORT graphData[NUM_COORDS1][2] = {
    03, 10,
    16, 25,
    29, 40,
    48, 90,
    82, 30
  };

struct Screen *Screen = NULL;
struct Window *BackWindow = NULL;
struct Window *MyWindow = NULL;
struct Window *SubWindow = NULL;
struct BitMap *secbm = NULL;
SHORT SWidth = 0, SHeight = 0;
ULONG winflags = WIN_FLAGS;

struct Gadget *gadg[MAX_GADG];		/* initialize later */
struct Requester *request = NULL;

struct TextFont *tf = NULL;

struct IOStdReq *inputIOreq = NULL;

GRAPH *mygraph = NULL;
struct Task *mysubtask = NULL;
struct Menu *menustrip = NULL;


BYTE COLOR = 0, FONT = 0, GADGET = 0, MENU = 0, MYGRAPH = 0, POSTEXT = 0;
BYTE REQUESTER = 0, SUBTASK = 0, TIMER = 0, WINDOW = 0;
BYTE DBUF = 0, INPHAND = 0;


/* forward declarations */
struct Screen *initscreen();
HandlerInterface();
subtaskRoutine();


main( argc, argv )
    int argc;
    char **argv;
{
    register char   *temp;
    char	    *fontname = NULL;
    SHORT	    fontsize = 0;
    register SHORT  i;

    if( argc < 2 ) {
	help( argc, argv );
    } else {
	for( i = 1; i < argc; ++i ) {
	    temp = argv[i];
	    if( *temp == '-' ) {
		++temp;
next_flag: {}
		switch( tolower(*temp) ) {
		  case 'c':
		    COLOR = 1; break;
		  case 'd':
		    DBUF = 1; break;
		  case 'f':
		    FONT = 1; break;
		  case 'g':
		    GADGET = 1; break;
		  case 'h':
		  case '?':
		    help(); break;
		  case 'i':
		    INPHAND = 1; break;
		  case 'j':
		    MYGRAPH = 1; break;
		  case 'm':
		    MENU = 1; break;
		  case 'p':
		    POSTEXT = 1; break;
		  case 'r':
		    REQUESTER = 1; break;
		  case 's':
		    SUBTASK = 1; break;
		  case 't':
		    TIMER = 1; break;
		  case 'w':
		    WINDOW = 1; break;
		  default:
		    printf("unknown option '%c' ignored\n", *temp);
		    break;
		} /* switch */
		if( *(++temp) ) {
		    goto next_flag;
		}
	    } else if( !fontname ) {
		fontname = temp;
		if( argv[i+1][0] != '-' ) {
		    fontsize = atoi( argv[++i] );
		}
	    } else {
		help( argc, argv );
	    }
	} /* for */
    }
    if( fontname ) {
	ta.ta_Name = (STRPTR) fontname;
	if( fontsize ) {
	    ta.ta_YSize = fontsize;
	}
    }
    if( !openStuff() ) {
	runIt( argc, argv, BackWindow );
    }
    cleanUp();
} /* main */


help( argc, argv )
    int argc;
    char **argv;
{
    printf( "Usage: %s -cdfgihjmprstw? [name.font [size]]\n"
	    "     -c    : color (use gimme.lib palette)\n"
	    "     -d    : double buffer screen (not too pretty w/ Intuition)\n"
	    "     -f    : font (use another font)\n"
	    "     -g    : gadget\n"
	    "     -h    : help -- this info (also -?)\n"
	    "     -i    : input handler (make RMB work like LMB)\n"
	    "     -j    : graph Jan Sven\n"
	    "     -m    : menu\n"
	    "     -p    : postext (position text routine demo)\n"
	    "     -r    : requester\n"
	    "     -s    : subtask\n"
	    "     -t    : timer\n"
	    "     -w    : window\n"
	    "  note: mix&match options to demo features of gimme.lib\n",
	    argv[0]);
    exit( 0 );
} /* help */


openStuff()
{
    struct NewWindow	*nw;
    struct Gadget	*gp;
    struct Image	*ip;
    struct Window	*tempw;
    struct MenuItem	*menuitem;
    ULONG		flag;
    SHORT		x, y;
    register SHORT	i;

    flag = gimmeStdStuff( STDSTUFF, GSS_REVISION );
    if( flag ) {
	printf("openStuff: can't open standard thing for flag 0x0%lx\n", flag);
	return( -1 );
    }
if( FONT ) {
    if( !(tf = gimmeFont(&ta)) ) {      /* note: this updates ta.ta_YSize */
	printf("openStuff: can't open font '%s' size %d\n",
		    ta.ta_Name, ta.ta_YSize);
	return( -1 );
    }
} /* FONT */

if( MENU ) {
    winflags &= ~RMBTRAP;
} /* MENU */
    if( !(Screen = initscreen(&BackWindow)) ) {
	SystemError("can't open screen/window\n");
    }
if( COLOR ) {
    setColors( Screen, NULL, 1 << SBMDEPTH );
} /* COLOR */

if( MENU ) {
    menustrip = gimmeMenu( &gimChainMem, 0, -1, "Options", MENUENABLED );
    menuitem = gimmeMenuItem( &gimChainMem, 0, -1, -1, 'Q',
			"Quit", tf ? &ta : NULL,
			ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP );
    if( addMenuItem(menustrip, menuitem, 0, 0, 1, GMI_DEFAULT) == MENUNULL ) {
	SystemError("can't setup menu strip\n");
    }
    SetMenuStrip( BackWindow, menustrip );
} /* MENU */

if( WINDOW ) {
    nw = gimmeNewWindow( "howdy", Screen, 310, 100,
			    IDCMP_VSMART, FLAGS_VSMART );
    if( !nw ) {
	SystemError("can't get new window\n");
    }
    nw->Width = 300;
    nw->Height = 100;
    if( !(MyWindow = gimmeWindow(nw, 0, 0, 0)) ) {
	getRidOfNewWindow( nw );
	SystemError("can't open window\n");
    }
    getRidOfNewWindow( nw );
} /* WINDOW */

if( SUBTASK ) {
    /* give it a 5K stack and 1K data area */
    if( !(mysubtask = gimmeSubTask(&gimSubTasks, &gimMainPort, 5000L,
					1000L, NULL)) ) {
	SystemError("can't setup for subtask\n");
    }
    strcpy( mysubtask->tc_UserData, "guess what? I am a subtask!" );
} /* SUBTASK */

if( DBUF ) {
    /* double-buffering isn't the greatest thing with Intuition trying to
     * display things too
     */
    if( makeDBuf(Screen, &secbm) ) {
	printf("can't make second buffer\n");
    }
    swapDBuf( Screen, GIM_MINTERM_COPY );   /* so Intuition draws up front */
    swapDBuf( Screen, GIM_MINTERM_DEST );
} /* DBUF */


    /* always initialize gadg[] array */
    for( i = FIRST_GADG; i < MAX_GADG; ++i ) {
	gadg[i] = NULL;
    } /* for */

if( REQUESTER ) {
    SetBPen( BackWindow->RPort, (ULONG) GIM_RED );
    SetDrMd( BackWindow->RPort, (ULONG) JAM2 );
} /* REQUESTER */

    /* always create these two gadgets */
    gadg[BOOL_GADG-1] = gimmeBoolGadget( BackWindow, BOOL_GADG, 20, 20,
			    150, -1, "end", NULL, tf ? &ta : NULL,
			    GPT_DEFAULT | GPT_FULLWIDTH );
    if( !gadg[BOOL_GADG-1] ) {
	SystemError("can't make start gadget\n");
    }
    gadg[CLEAR_GADG-1] = gimmeBoolGadget( BackWindow, CLEAR_GADG, 20, 40,
			-1, -1, "clear", NULL, tf ? &ta : NULL, GPT_DEFAULT );
    if( !gadg[CLEAR_GADG-1] ) {
	SystemError("can't make clear gadget\n");
    }
if( REQUESTER ) {
    gadg[BOOL_GADG-1]->Activation |= ENDGADGET;
    gadg[BOOL_GADG-1]->GadgetType |= REQGADGET;
} /* REQUESTER */

if( GADGET ) {
    SetAPen( BackWindow->RPort, (ULONG) GIM_YELLOW );
    gadg[BOOL_TOG_GADG-1] = gimmeBoolGadget( BackWindow, BOOL_TOG_GADG, 200, 20,
		-1, -1, "toggle", "boolean", tf ? &ta : NULL, GPT_DEFAULT );
    SetAPen( BackWindow->RPort, (ULONG) GIM_WHITE );

    gadg[BOOL_IMAG_GADG-1] = gimmeBoolImageGadget( BackWindow, BOOL_IMAG_GADG,
			    290, 20, GOS_BITDEPTH, GOS_BITWIDTH, GOS_BITHEIGHT,
			    GPT_DEFAULT, DI_Z, DI_X, DI_Y );
    if( gadg[BOOL_IMAG_GADG-1] ) {
	gadg[BOOL_IMAG_GADG-1]->Flags = GADGIMAGE | GADGHNONE;
	gadg[BOOL_IMAG_GADG-1]->Activation = GADGIMMEDIATE;
	if( gadg[BOOL_IMAG_GADG-1]->GadgetRender ) {
	    copyDataImage( gimDataOneScrollLeft,
			(struct Image *) gadg[BOOL_IMAG_GADG-1]->GadgetRender );
	}
	if( gadg[BOOL_IMAG_GADG-1]->SelectRender ) {
	    copyDataImage( firstImageData,
			(struct Image *) gadg[BOOL_IMAG_GADG-1]->SelectRender );
	}
    }

    gadg[STRING_GADG-1] = gimmeStringGadget( BackWindow, STRING_GADG, 20, 70,
			    150, 20, "string", NULL, tf ? &ta : NULL,
			    GADGIMMEDIATE | RELVERIFY );
    gadg[STRING_INT_GADG-1] = gimmeStringGadget( BackWindow, STRING_INT_GADG,
			20, 110, 150, 20, "12345", "int gadg:", tf ? &ta : NULL,
			GADGIMMEDIATE | RELVERIFY | LONGINT );

    SetDrMd( BackWindow->RPort, (ULONG) JAM1 );
    gadg[PROP_H_GADG-1] = gimmePropGadget( BackWindow, PROP_H_GADG, 250, 50,
		    60, GOS_BITHEIGHT + 4, "horiz prop", tf ? &ta : NULL,
		    GADGIMMEDIATE | RELVERIFY, FREEHORIZ );
    if( ip = gimmeImage(&gadg[PROP_H_GADG-1]->UserData,
			    GOS_BITDEPTH, GOS_BITWIDTH, GOS_BITHEIGHT) ) {
	copyDataImage( gimDataOneScrollLeft, ip );
	gadg[PROP_H_GADG-1]->GadgetRender = (APTR) ip;
	((struct PropInfo *)gadg[PROP_H_GADG-1]->SpecialInfo)->Flags
				&= ~AUTOKNOB;
    }
    SetDrMd( BackWindow->RPort, (ULONG) JAM2 );

    gadg[PROP_V_GADG-1] = gimmePropGadget( BackWindow, PROP_V_GADG, 250, 70,
			    10, 50, "vert prop", tf ? &ta : NULL,
			    GADGIMMEDIATE | RELVERIFY,
			    FREEVERT | PROPBORDERLESS );

    /* note fullscrollbar returns a linked list of 3 gadgets */
    gadg[H_SCROLLBAR-1] = gimmeFullScrollBar( BackWindow, H_SCROLLBAR,
					    FREEHORIZ | KNOBHIT, GSB_DEFAULT,
					    ONESCROLL1+500, ONESCROLL2+500 );

    gadg[V_SCROLLBAR-1] = gimmeFullScrollBar( BackWindow, V_SCROLLBAR,
					    FREEVERT | KNOBHIT, GSB_DEFAULT,
					    ONESCROLL1, ONESCROLL2 );
    ((struct PropInfo *)gadg[V_SCROLLBAR-1]->SpecialInfo)->VertBody = 0x0fff;
} /* GADGET */

    /* link the gadgets */
    if( (gp = gadg[FIRST_GADG]) ) {
	for( i = FIRST_GADG + 1; i < MAX_GADG; ++i ) {
	    if( gadg[i] ) {
		gp->NextGadget = gadg[i];
		for( gp = gadg[i]; gp->NextGadget; gp = gp->NextGadget ) {
if( REQUESTER ) {
		    gp->GadgetType |= REQGADGET;
} /* REQUESTER */
		} /* for */
if( REQUESTER ) {
		gp->GadgetType |= REQGADGET;
} /* REQUESTER */
	    }
	} /* for */
    }

if( REQUESTER ) {
    SetBPen( BackWindow->RPort, (ULONG) GIM_BACKGROUND );
    SetDrMd( BackWindow->RPort, (ULONG) JAM1 );
} /* REQUESTER */


if( POSTEXT ) {
    tempw = MyWindow ? MyWindow : BackWindow;
    x = tempw->Width >> 1;
    y = tempw->Height >> 1;
    /* some weird horizontal text cropped on the borders */
    SetAPen( tempw->RPort, (long) GIM_RED );
    positionText( tempw->RPort, GPT_XLEFT | GPT_YCENTREBASE,
		    "left centbas", 0L, x, 0 );
    SetAPen( tempw->RPort, (long) GIM_BLUE );
    positionText( tempw->RPort, GPT_XCENTRE | GPT_YCENTRE,
		    "centre centre", 0L, x - 40, 0 );
    SetAPen( tempw->RPort, (long) GIM_GREEN );
    positionText( tempw->RPort, GPT_XRIGHT | GPT_YBOTTOM,
		    "right bot", 0L, x + 60, tempw->Height + 4 );
    /* some vertical text */
    SetAPen( tempw->RPort, (long) GIM_YELLOW );
    positionText( tempw->RPort, GPT_YTOP | GPT_YUPWARDS | GPT_XTHICKEN,
		    "dflt top", 0L, x, y );
    SetAPen( tempw->RPort, (long) GIM_CYAN );
    positionText( tempw->RPort, GPT_YCENTRE | GPT_YDOWNWARDS | GPT_XTHICKEN,
		    "dflt centre", 0L, x, y );
    SetAPen( tempw->RPort, (long) GIM_WHITE );
    positionText( tempw->RPort, GPT_YBOTTOM | GPT_YUPWARDS,
		    "dflt bot", 0L, x, y );
    SetAPen( tempw->RPort, (long) GIM_MAGENTA );
    Move( tempw->RPort, (long) x - 20, (long) y );
    Draw( tempw->RPort, (long) x + 20, (long) y );
    Move( tempw->RPort, (long) x, (long) y - 20 );
    Draw( tempw->RPort, (long) x, (long) y + 20 );
    SetAPen( tempw->RPort, (long) GIM_WHITE );
} /* POSTEXT */

if( MYGRAPH ) {
if( !SUBTASK ) {
    newgraph.rp = BackWindow->RPort;
    if( !(mygraph = gimmeGraph(&newgraph, NULL, NULL, NULL)) ) {
	printf("OpenStuff: couldn't open graph\n");
	MYGRAPH = 0;
    }
} /* !SUBTASK */
} /* MYGRAPH */

if( REQUESTER ) {
    request = gimmeRequester( &gimChainMem, 0, 0, -1, -1, GIM_RED,
			gadg[FIRST_GADG], "sample requester", &gimMyFont, 0L );
    if( !request ) {
	SystemError("can't get requester\n");
    }
    Request( request, BackWindow );
} else {
    if( gadg[FIRST_GADG] ) {
	AddGList( BackWindow, gadg[FIRST_GADG], -1L, -1L, NULL );
    }
    RefreshGadgets( gadg[FIRST_GADG], BackWindow, NULL );
} /* else !REQUESTER */

if( INPHAND ) {
    if( !(inputIOreq = addInputHandler(HandlerInterface, NULL, 51, NULL)) ) {
	printf("can't get input handler\n");
    }
} /* INPHAND */

    return( 0 );
} /* openStuff */


cleanUp()
{
    struct Gadget   *gp = NULL;
    ULONG	    flag;

    if( inputIOreq ) removeInputHandler( inputIOreq );
    if( mygraph ) getRidOfGraph( mygraph );
    if( BackWindow ) {
	gp = findMyFirstGadget( BackWindow->FirstGadget, SYS_GADG );
    }
    if( gp ) {
	RemoveGList( BackWindow, gp, -1L );
	getRidOfGadgets( gp );
    } else {
	if( gadg[FIRST_GADG] ) getRidOfGadgets( gadg[FIRST_GADG] );
    }
    if( MyWindow ) getRidOfWindow( MyWindow );
    if( Screen ) {
	if( secbm ) unmakeDBuf( Screen, &secbm, secbm );
	getRidOfScreen( Screen, BackWindow );
    }
    if( tf ) getRidOfFont( tf );
    if( flag = getRidOfStdStuff(GSS_CLOSEALL) ) {
	printf("cleanUp: can't close standard thing for flag 0x%0lx\n", flag);
	getRidOfStdStuff( GSS_KILLALL );
    }
#ifdef MEMTRACE
    freeall();
#endif
} /* cleanUp */


SystemError( string )
    char *string;
{
    if( Output() ) {
	Write( Output(), string, (ULONG)strlen(string) );
    }
    cleanUp();
    exit( 1 );
} /* SystemError */


int processMessage( window, class, code, iaddr )
    struct Window *window;
    ULONG   class;
    USHORT  code;
    APTR    iaddr;
{
    struct MenuItem *MenuItem;

    switch( class ) {
      case CLOSEWINDOW:
	return( -1 );
	break;
      case GADGETDOWN:
	toggleBoolGadget( window, (struct Gadget *) iaddr, request );
	break;
      case GADGETUP:
if( !INPHAND ) {
	if( ((struct Gadget *)iaddr)->GadgetID == CLEAR_GADG ) {
	    clearGadgets( gadg[FIRST_GADG]->NextGadget, window, request, -1 );
	}
} /* !INPHAND */
	break;
      case MENUPICK:
	while( code != MENUNULL ) {
	    MenuItem = ItemAddress( window->MenuStrip, (long) code );
	    if( MenuItem->Command == 'Q' ) {
		return( -1 );
	    }
	    code = MenuItem->NextSelect;
	} /* while */
	break;
      default:
	break;
    } /* switch */
    return( 0 );
} /* processMessage */


runIt( argc, argv, window )
    int argc;
    char **argv;
    struct Window *window;
{
    struct IntuiMessage *imsg;
    ULONG   class;
    USHORT  code;
    APTR    iaddr;
    ULONG   waitmask;
    SHORT   quitflag = 0;
    SHORT   i;

    waitmask = 1L << window->UserPort->mp_SigBit;
    if( MyWindow ) {
	waitmask |= 1L << MyWindow->UserPort->mp_SigBit;
    }
if( SUBTASK ) {
    if( mysubtask ) {
	startSubTask( mysubtask, "DEMO SUBTASK", 0, subtaskRoutine, NULL );
	waitmask |= 1L << gimMainPort->mp_SigBit;
    }
} else {
if( MYGRAPH ) {
    for( i = 0; i < NUM_COORDS1; ++i ) {
	addToGraph( mygraph, graphData[i][0], graphData[i][1] );
    } /* for */
} /* MYGRAPH */
    if( gimTimerPort ) {
	waitmask |= 1L << gimTimerPort->mp_SigBit;
    }
}/* else SUBTASK */
    while( !quitflag && !Chk_Abort() ) {
	Wait( waitmask );
	SetWindowTitles( BackWindow, "", -1L );
	while( imsg = (struct IntuiMessage *) GetMsg(window->UserPort) ) {
	    class = imsg->Class;
	    code = imsg->Code;
	    iaddr = imsg->IAddress;
	    ReplyMsg( imsg );
	    if( processMessage(window, class, code, iaddr) ) {
		quitflag = -1;
	    }
	} /* while */
	if( MyWindow ) {
	    while( imsg = (struct IntuiMessage *) GetMsg(MyWindow->UserPort) ) {
		class = imsg->Class;
		code = imsg->Code;
		iaddr = imsg->IAddress;
		ReplyMsg( imsg );
		if( processMessage(MyWindow, class, code, iaddr) ) {
		    waitmask &= ~(1L << MyWindow->UserPort->mp_SigBit);
		    getRidOfWindow( MyWindow );
		    MyWindow = NULL;
		    break;
		}
	    } /* while */
	}
	if( gimMainPort ) {
	    handleSpecialSubTaskMsg( &gimSubTasks, &gimMainPort );
	}
	if( quitflag && gimSubTasks ) {
	    SetWindowTitles( BackWindow, "hey! kill subtask first", -1L );
	    quitflag = 0;
	}
    } /* while */
} /* runIt */


struct Screen *initscreen( winptr )
    struct Window   **winptr;
{
    struct NewScreen	*ns;
    struct Screen	*screen = NULL;

    ns = gimmeNewScreen( HIRES, CUSTOMSCREEN, SBMDEPTH,
			    "Gimme.lib V1.2 testing", tf ? &ta : &gimMyFont );
    if( ns ) {
	screen = gimmeScreen( ns, winptr, SBMDEPTH, IDCMP_FLAGS, winflags );
	if( screen ) {
	    SWidth = screen->Width;
	    SHeight = screen->Height;
/***
	    ShowTitle( screen, TRUE );
***/
	}
	getRidOfNewScreen( ns );
    }
    return( screen );
} /* initscreen */


/*  example of a handler which makes the right mouse button
 *  behave like the left mouse button
 *  note: it's priority must be >50 (to be ahead of Intuition)
 */
struct InputEvent *myhandler( events, data )
    struct InputEvent	*events;
    APTR		data;
{
    register struct InputEvent	*ev;

    geta4();        /* is this really necessary ??? */
    for( ev = events; ev; ev = ev->ie_NextEvent ) {
	if( ev->ie_Class == IECLASS_RAWMOUSE ) {
	    if( (ev->ie_Code & ~IECODE_UP_PREFIX) == IECODE_RBUTTON ) {
		ev->ie_Code = (ev->ie_Code & ~IECODE_RBUTTON) | IECODE_LBUTTON;
	    }
	}
    } /* for */
    return( events );
} /* myhandler */


#asm
		; events pointer in A0
		; data pointer in A1
		; return events pointer in D0
_HandlerInterface:
    MOVEM.L	D2/D3/A4/A6,-(sp)       ; save some registers
    MOVEM.L	A0/A1,-(sp)             ; set up parms for C
    JSR 	_myhandler
    LEA 	8(sp),sp                ; pop parms
    MOVEM.L	(sp)+,D2/D3/A4/A6       ; restore registers
    RTS
#endasm


subtaskRoutine( data )
    UBYTE   *data;
{
    struct NewWindow	*nw;
    struct IntuiMessage *imsg;
    struct timerequest	*mytr = NULL, *tr;
    struct MsgPort	*myport = NULL;
    ULONG   class;
    USHORT  code;
    APTR    iaddr;
    ULONG   waitmask;
    SHORT   quitflag = 0;
    SHORT   i;
    BYTE    subtimer;

    subtimer = TIMER;
    doPrintf( gimMainPort, NULL, "message from subtask: %s\n", data);
    nw = gimmeNewWindow( "subtask'd window", Screen, 50, 50,
			    IDCMP_VSMART, FLAGS_VSMART );
    if( !nw ) {
	doPrintf(gimMainPort, NULL, "can't get new window\n");
	return;
    }
    nw->Width = 400;
    nw->Height = 150;
    if( !(SubWindow = gimmeWindow(nw, 0, 0, 0)) ) {
	getRidOfNewWindow( nw );
	doPrintf( gimMainPort, NULL, "can't open subtask window\n");
	return;
    }
    getRidOfNewWindow( nw );

/** this appears to be flaky -- but I don't know why!
if( MYGRAPH ) {
    newgraph.rp = SubWindow->RPort;
    if( !(mygraph = gimmeGraph(&newgraph, NULL, NULL, NULL)) ) {
	doPrintf(gimMainPort, NULL, "subtask: couldn't open graph\n");
	MYGRAPH = 0;
    }
}
**/

    waitmask = 1L << SubWindow->UserPort->mp_SigBit;
if( TIMER ) {
    if( gimTimerPort ) {
	/* access Timer to get my own port and signal */
	if( !(mytr = accessTimer(UNIT_VBLANK, &myport)) ) {
	    subtimer = 0;
	} else {		/* asynchronous 10 second delay */
	    timeDelayAsync( 10L, 0L, UNIT_VBLANK, mytr );
	}
	SetWindowTitles( SubWindow, "I'll go away in 10 seconds", -1L );
	waitmask |= 1L << myport->mp_SigBit;
    }
} /* TIMER */

/** this appears to be flaky -- but I don't know why!
if( MYGRAPH ) {
    for( i = 0; i < NUM_COORDS1; ++i ) {
	addToGraph( mygraph, graphData[i][0], graphData[i][1] );
    }
}
****/

    while( !quitflag ) {
	Wait( waitmask );
	while( imsg = (struct IntuiMessage *) GetMsg(SubWindow->UserPort) ) {
	    class = imsg->Class;
	    code = imsg->Code;
	    iaddr = imsg->IAddress;
	    ReplyMsg( imsg );
	    if( processMessage(SubWindow, class, code, iaddr) ) {
		quitflag = -1;
	    }
	} /* while */
	if( subtimer ) {
	    while( tr = (struct timerequest *) GetMsg(myport) ) {
		quitflag = -1;
	    } /* while */
	}
    } /* while */
    getRidOfWindow( SubWindow );
    if( mytr ) {
	releaseTimer( mytr, NULL );     /* will abort it if necessary */
    }
    SubWindow = NULL;
} /* subtaskRoutine */
