
/*	MerlinFract - produces Mandelbrot Sets with Merlin

		Rainer Trunz, January 1995

	This program calculates the so called "Mandelbrot Sets"
	in a PIP-window on a Merlin graphic board

	History:
	V1.1 21.01.1995  First version, already stable, 'Quick' and
					 dirty hack, that is, it was meant as a 'simple'
					 test for programming within a 2.6.1/2.3.3 - gcc
					 baserel-environment (and the Merlin board, too).

	V1.2 26.01.1995  Second version, added doublclick-zoom
*/

#include "amiga.h"

#include <proto/icon.h>

#include <stdlib.h>
#include <math.h>

#include <inline/strsup.h>

#include <sys/types.h>
#include <sys/signal.h>
#include <sys/time.h>

int __nocommandline=1; /* Disable commandline parsing  */

extern struct WBStartup *_WBenchMsg;

/* Define BOOLEAN, TRUE, and FALSE. */

#define BOOLEAN int
#define TRUE      1
#define FALSE     0

#define ENVNAME "MerlinFract.prefs"
#define ENVSIZE 256

#define OUTPUT(s)      if (!quietFlag) FPrintf (stdout, s)
#define OUTPUT1(s,a)   if (!quietFlag) FPrintf (stdout, s, a)
#define OUTPUT2(s,a,b) if (!quietFlag) FPrintf (stdout, s, a, b)

#define SET_A_PEN(a)   OUTPUT1("\033[3%ldm", a)

#define BITS2SHIFT 27

/* something to use to track the time between messages
** to test for double-clicks.
*/
typedef struct myTimeVal
{
    ULONG LeftSeconds;
    ULONG LeftMicros;
    ULONG RightSeconds;
    ULONG RightMicros;
} MYTIMEVAL;

/*============================================================================*
 *  Oeffentliche Variablen / Strukturen.                                      *
 *============================================================================*/

int quietFlag = FALSE;
int verbose = FALSE;

int ZoomNr = 0;
int ZoomBox = 8;		/* 1/xth of InnerWidth/Height */
int ZoomFlag = FALSE;

BOOL IEEEflag = FALSE;

struct Device		*TimerBase	= NULL;
struct timerequest	*TimerIO	= NULL;
struct MsgPort		*TimerPort	= NULL;
struct EClockVal	time0		= {NULL};
struct EClockVal	time1		= {NULL};

ULONG timerclosed = TRUE;
double micros_per_eclock;	/* Length of EClock tick in microseconds */

char fileName[256];

struct ExecBase		 *SysBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase       *GfxBase       = NULL;
struct Library       *GadToolsBase  = NULL;
struct Library       *AslBase       = NULL;
struct Library       *HRGSystemBase = NULL,
                     *HRGRenderBase = NULL;
struct HRHandle      *hrh = NULL;

BPTR fd = NULL;				/* The main file descriptor */
BPTR stdout = NULL;			/* Standard Output  */

ULONG FrameSize = NULL;

int offx = 0, offy = 0,
    offl = 0, offt = 0;

struct IntPotParms {
 int C_Real;
 int C_Imag;
 int ScreenReal;
 int ScreenImag;
 int MaxIter;
};

double RealLow, ImagLow;
double RealHigh, ImagHigh;

double RealGap, ImagGap;

int MaxIteration = 32;

/*============================================================================*
 *  Lokale Variablen / Strukturen.                                            *
 *============================================================================*/

static struct Window        *FRA_win = NULL;
static struct RastPort      *rp      = NULL;
static struct IntuiMessage  *imsg    = NULL;
static struct Screen        *Screen  = NULL;
static struct Menu          *Menus   = NULL;
static struct IntuiMessage  msg;

static struct HRBitMap      *hrbm    = NULL;
static struct HRModePrefDat MyPrefs;

static APTR VisualInfo = NULL;

static UWORD CurrentGadget = NO_GADGET;
static ULONG soerror = 0;

ULONG *coltab = NULL, *curctab = NULL;

static int InnerWidth = 0, InnerHeight = 0,
           ScrollX = 0, ScrollY = 0,
           Width = 0, Height = 0,
           left = 0, top = 0;

static struct Gadget *LeftGadget  = NULL,
                     *RightGadget = NULL,
                     *UpGadget    = NULL,
                     *DownGadget  = NULL,
                     *HorizGadget = NULL,
                     *VertGadget  = NULL;

static struct Image  *UpImage     = NULL,
                     *DownImage   = NULL,
                     *LeftImage   = NULL,
                     *RightImage  = NULL;

static struct NewMenu NewMenu[] = {
    NM_TITLE, (STRPTR)"Project",            NULL,       0, 0L, NULL,
    NM_ITEM,  (STRPTR)"About...",          (STRPTR)"?", 0, 0L, NULL,
    NM_ITEM,  (STRPTR)NM_BARLABEL,          NULL,       0, 0L, NULL,
    NM_ITEM,  (STRPTR)"Use Int  (32 Bit)",  NULL, CHECKIT|CHECKED, 8L, NULL,
    NM_ITEM,  (STRPTR)"Use IEEE (64 Bit)",  NULL, CHECKIT, 4L, NULL,
    NM_ITEM,  (STRPTR)"Reset Mand",        (STRPTR)"M", 0, 0L, NULL,
    NM_ITEM,  (STRPTR)NM_BARLABEL,          NULL,       0, 0L, NULL,
    NM_ITEM,  (STRPTR)"Quit",              (STRPTR)"Q", 0, 0L, NULL,
    NM_TITLE, (STRPTR)"Window",             NULL      , 0, 0L, NULL,
    NM_ITEM,  (STRPTR)"Reset To Defaults", (STRPTR)"R", 0, 0L, NULL,
    NM_ITEM,  (STRPTR)"Save As Defaults",  (STRPTR)"S", 0, 0L, NULL,
    NM_ITEM,  (STRPTR)"Last Saved",        (STRPTR)"L", 0, 0L, NULL,
    NM_ITEM,  (STRPTR)NM_BARLABEL,          NULL,       0, 0L, NULL,
    NM_ITEM,  (STRPTR)"Minimize",          (STRPTR)"-", 0, 0L, NULL,
    NM_ITEM,  (STRPTR)"Maximize",          (STRPTR)"+", 0, 0L, NULL,
    NM_END,   NULL, NULL, 0, 0L,   NULL };

static struct EasyStruct AboutRequest = {
    sizeof(struct EasyStruct),
    0,
    "MerlinFract/C",
    "Mandelbrot Displayer\n"
    "for the Merlin Graphics Accelerator\n\n"
    "Version 1.2 ("__DATE__")\n\n"
    "Copyright © 1995 Rainer Trunz",
    "Continue"
};

static char *version = "$VER: MerlinFract 1.2 ("__DATE__")";

static int wwidth, wheight;	/* Width and height of the window */

/* prototypes */

#ifndef LARGE

/*****************************************************************************
 *
 *  EasyReqArgs.asm	- EasyRequestArgs() für baserel Gnu-C ;-(((
 *
 *  ©`94 Rainer F. Trunz. Geschrieben für Gnu-C 2.3.3 !
 *
 *****************************************************************************/

ULONG EasyReqArgs (struct Window *window, struct EasyStruct *easyStruct,
						 ULONG *idcmpPtr, ULONG args,...)
{
  __asm __volatile ("			| (ostring, format, {values})

	moveml	a2/a3/a6,sp@-

	lea		sp@(4*4),a3
	moveml	a3@+,a0/a1/a2		| a3 is now ptr to tag-field
	movel	a4@(_IntuitionBase:W),a6
	jsr		a6@(-588)			| _LVOEasyRequestArgs

	moveml	sp@+,a2/a3/a6"

	: /* No Output */
	: /* No Input */
	: "d0","d1","a0","a1");
}

/*****************************************************************************
 *
 *  SetGadAttrs.asm - SetGadgetAttrsA() für baserel Gnu-C ;-(((
 *
 *  ©`94 Rainer F. Trunz. Geschrieben für Gnu-C 2.3.3 !
 *
 *****************************************************************************/

ULONG SetGadAttrs (struct Gadget *gad, struct Window *win,
						struct Requester *req, ULONG args,...)
{
  __asm __volatile ("

	moveml	a2/a3/a6,sp@-

	lea		sp@(4*4),a3
	moveml	a3@+,a0/a1/a2		| a3 is now ptr to tag-field
	movel	a4@(_IntuitionBase:W),a6
	jsr		a6@(-660)			| _LVOSetGadgetAttrsA

	moveml	sp@+,a2/a3/a6"

	: /* No Output */
	: /* No Input */
	: "d0","d1","a0","a1");
}

#endif

/*****************************************************************************
 * 32 bit fixed point generator
 */

static inline int Int32_Height_Fast (struct IntPotParms *Parms)
{
  register int _res __asm("d0");
  register struct IntPotParms * a2 __asm("a2") = Parms;

  __asm __volatile ("

fscreeni	=	4			| fscreenr	= 0
fcurx		=	8
fcury		=	12
maxi		=	16

Zcurx		=	8
Zcury		=	12

	| d1 - BITS2SHIFT
	| d2 - k
	| d4 - a
	| d5 - b
	| d6 - a2
	| d7 - b2

	| cura = curb = curc = curd = 0;

   movel   a2@(maxi),d1
   moveq   #27,d2
   movel   a2@,d4
   movel   a2@(fscreeni),d5
   movel   d4,a0			| a2@(fcurx),a0
   movel   d5,a1			| a2@(fcury),a1
   jra     Zpos2

ZLoop:	| cura = cura2 - curb2 + curx;

   exg     d6,d4
   subl    d7,d4			|  movel   a0,d0
   addl    a0,d4
   mulsl   d6,d0:d5			| curb = cura * curb
   asll    #5,d0
   lsrl    d2,d5
   orl     d0,d5			| curb += curb + cury;
   addl    d5,d5			|  movel   a1,d0
   addl    a1,d5

Zpos2:	| cura2 = cura * cura;

   movel   d4,d6
   mulsl   d6,d0:d6
   asll    #5,d0
   jvs     Zbailout

   lsrl    d2,d6
   orl     d0,d6
   movel   d5,d7			| curb2 = curb * curb;
   mulsl   d7,d0:d7
   asll    #5,d0
   jvs     Zbailout

   lsrl    d2,d7
   orl     d0,d7

   movel   d6,d0
   addl    d7,d0			| cmpl    #536870912,d0
   jvs     Zbailout			| bge      Zbailout

   dbra    d1,ZLoop			| addq     #1,d1
   moveq   #1,d0			| black
   jra	   setBl

Zbailout:
   movel   a2@(maxi),d0
   subw    d1,d0			| subqw   #1,d0
   extl    d0
setBl:"
	: "=r" (_res)
	: "r" (a2)
	: "d0","d1","d2","d4","d5","d6","d7","a0","a1");

	return _res;
}

/* void SyncWait(secs, microsecs);
   wait for specified amount of time and then return */

static inline void
SyncWait (unsigned long secs, unsigned long microsecs)
{
  struct timeval tv_temp;

  tv_temp.tv_secs = secs;
  tv_temp.tv_micro = microsecs;

  TimerIO->tr_node.io_Command = TR_ADDREQUEST;
  TimerIO->tr_time = tv_temp;

  DoIO ((struct IORequest *) TimerIO);
}

/*============================================================================*/

#ifdef HACK

static inline void
hrWriteRect (struct HRBitMap *hrbm, UBYTE *buf,
			 int srcx,  int srcy,
			 int width, int height)
{
	UBYTE *src, *dest;
	int anz, i, mod;

/*	UWORD BytesPerRow;
	UWORD Rows;
	UBYTE PixelModulo;
	ULONG ByteSize;

	PixelModulo = (hrbm)->PixelModulo;
	Rows = (hrbm)->Rows;
	BytesPerRow = (hrbm)->BytesPerRow;
	ByteSize = (hrbm)->ByteSize;
*/
    hrSLockBitMap (hrbm);

	if (!srcx && !srcy && (width == InnerWidth) && (height == InnerHeight))
	  CopyMemQuick (buf, (hrbm)->MemPtr[0], FrameSize);
	else {
	  mod  = srcy*InnerWidth;
	  dest = (hrbm)->MemPtr[0] + mod;
	  src  = buf + mod;
	  i    = width;

	  /* printf ("srcx: %ld, srcy: %ld, W: %ld, H: %ld, mod: %ld\n",
		srcx, srcy, width, height, mod); */

	  for (anz = 0; anz < height; anz++,
								  mod += InnerWidth,
								  dest = (hrbm)->MemPtr[0] + mod + srcx,
								  src = buf + mod + srcx,
								  i = width)
		while (i)
		  {*(dest++) = *(src++); i--;}
	}
    hrUnlockBitMap (hrbm);
}

#endif

/*============================================================================
 *
 * contents: this file contains the functions to calculate Mandelbrot and
 * Julia pictures using a special and fast fixed point (scaled ints) format.
 * It has generators for 68000 and 68020 in assembly.
 */

/* 32 bit fixed point generator */

void MandelbrotInt32II()
{
  int i, j;		/* struct RastPort *Rp; */

  UBYTE *CountPtr;
  int gapx, gapy, startx;

  struct IntPotParms Parms;
  double time;

  RealGap = (RealHigh - RealLow) / (double)InnerWidth;
  ImagGap = (ImagHigh - ImagLow) / (double)InnerHeight;

  /* Figure out horizontal and verticle distances between points in plane.
   * Convert them to fixed point format */

  gapy	 = (int) (ImagGap*((double)(1<<BITS2SHIFT)));
  gapx	 = (int) (RealGap*((double)(1<<BITS2SHIFT)));

  startx = (int) (RealLow*((double)(1<<BITS2SHIFT)));

  /* for each point in the image, calculate Mandelbrot */

  Parms.C_Imag = (int)(ImagLow*((double)(1<<BITS2SHIFT)));

  Parms.ScreenReal = 0;
  Parms.ScreenImag = 0;

  Parms.MaxIter = MaxIteration;

  if (TimerBase)
	ReadEClock (&time0);	/* start time */

  hrSLockBitMap (hrbm);
  CountPtr = (hrbm)->MemPtr[0];

  for (i = 0; i < InnerHeight; i++) {
     Parms.C_Real = startx;

     for (j = 0; j < InnerWidth; j++) {
        *CountPtr++ = Int32_Height_Fast (&Parms);
        Parms.C_Real += gapx;
     }
     Parms.C_Imag += gapy;
  }
  hrUnlockBitMap (hrbm);

  if (TimerBase) {
	ReadEClock (&time1);	/* find out and display how long it took */

	time = ((time1.ev_hi - time0.ev_hi) * 4294967296.0 +
			(time1.ev_lo - time0.ev_lo)) * micros_per_eclock;

	Printf ("\nTime = %02ld:%02ld.%ld [m:s.µ]\n",
			(int) ((time / 1000000.0) / 60.0),
			(int) ((time / 1000000.0) - ((int)((int)(time / 1000000.0) / 60.0) * 60)),
			(int) fmod (time, 1000000.0) / 10000);
  }
} /* MandelbrotInt32II */

/*============================================================================*
 * FPU Mandelbrot Set
 *============================================================================*/

/* This function calculates the 'potential' of a given location
 * in the complex plane. */

static __inline int DBL_Height (double posx, double posy, double juliax, double juliay)

	/* posx;		Location in screen coordinate space         */
	/* posy;		Location in screen coordinate space         */
	/* juliax:	Real part of location on complex plane      */
	/* juliay;	Imaginary part of location on complex plane */
{
  double cura, curb;
  double cura2, curb2;

  double DBL16 = 16.0;

  LONG k;

  cura = cura2 = posx;
  curb = curb2 = posy;

  cura2 = cura2*cura2;
  curb2 = curb2*curb2;

  for (k = 0; k < MaxIteration; k ++ ) {

    curb = curb*cura;				/* b = 2 * a * b  + juliay */
    curb = curb+curb+juliay;

    cura = cura2-curb2+juliax;		/* a = a2 - b2 + juliax */

    cura2 = cura*cura;				/* a2 = a * a */
    curb2 = curb*curb;				/* b2 = b * b */

    if (cura2+curb2 >= DBL16)		/* quit if a2+b2 is too big */
	  return (k==1 ? k+3:k);
  }
  return 1L;						/* Black for set ! */
}

/* This is also a Mandelbrot potential calculator, that is tailored to
 * identify points that are 'in' the Mandelbrot set. Points outside the
 * Mandelbrot set diverge.  Points inside converge.
 * This code uses a trace table mechanism to detect convergence.
 */

static __inline int DBL_Trace_Height (double posx, double posy, double curx, double cury)

   /* posx; Real part of location on complex plane      */
   /* posy; Imaginary part of location on complex plane */

   /* curx; Real part of location on complex plane      */
   /* cury; Imaginary part of location on complex plane */
{
  LONG  k, l;

  LONG TraceSize = 32;

  double olda[32], oldb[32]; /* Convergence trace table */

  double cura, curb, cura2, curb2;
  double *RealTrace, *ImagTrace;

  double DBL16 = 16.0;

  cura = cura2 = posx;
  curb = curb2 = posy;

  cura2 = cura2*cura2;
  curb2 = curb2*curb2;

  for (k = 0; k < MaxIteration; k += TraceSize) {
    RealTrace = olda;
    ImagTrace = oldb;

    for (l = 0; l < TraceSize; l++) {
     *(RealTrace++) = cura;
     *(ImagTrace++) = curb;

     curb = curb*cura;				/* b = 2 * a * b  + juliay */
     curb = curb+curb+cury;

     cura = cura2-curb2+curx;		/* a = a2 - b2 + juliax */

     cura2 = cura*cura;				/* a2 = a * a */
     curb2 = curb*curb;				/* b2 = b * b */

	  if (cura2+curb2 >= DBL16)		/* quit if a2+b2 is too big */
		return ((k+l)==1 ? k+l+3:k+l);
    }

    /* Scope out trace table for convergence */

    RealTrace = olda;
    ImagTrace = oldb;

    for (l = 0; l < TraceSize; l++)
      if ((cura == *(RealTrace++)) && (curb == *(ImagTrace++))) {
        /* k += MaxIter; */
        return (MaxIteration);
      }
  }
  return 1L;						/* Black for set ! */
}

/*** IEEE double-floating Point Mandelbrot Generator ***/

void Mandelbrot881()
{
  int i, j, k;
  UBYTE *CountPtr;

  double time;
  double curx, cury;
  double startx, starty;	/* gapx, gapy; */

  BOOL MandFlag;

  RealGap = (RealHigh - RealLow) / (double)InnerWidth;
  ImagGap = (ImagHigh - ImagLow) / (double)InnerHeight;

  startx = RealLow;
  starty = ImagLow;

  /* start in the upper left hand corner */

  cury = starty;

  if (TimerBase)
	ReadEClock (&time0);	/* start time */

  hrSLockBitMap (hrbm);
  CountPtr = (hrbm)->MemPtr[0];

  for (i = 0; i < InnerHeight; i++) {		/* for each row */
    curx = startx;
    MandFlag = 0;
    for (j = 0; j < InnerWidth; j++) {		/* for each column */

    /* if we've hit a Mandelbrot point,
	   then use the convergence detector
       to reduce compute time */

      if (MandFlag)
        k = DBL_Trace_Height (0.0, 0.0, curx, cury);
      else
        k = DBL_Height (0.0, 0.0, curx, cury);	/* Normal calculator */

      MandFlag = (k == MaxIteration);
      *CountPtr = k;						/* save it in ram */
      CountPtr++;
      curx = curx + RealGap;
    }
    cury = cury + ImagGap;
  }
  hrUnlockBitMap (hrbm);

  if (TimerBase) {
	ReadEClock (&time1);	/* find out and display how long it took */

	time = ((time1.ev_hi - time0.ev_hi) * 4294967296.0 +
			(time1.ev_lo - time0.ev_lo)) * micros_per_eclock;

	Printf ("\nTime = %02ld:%02ld.%ld [m:s.µ]\n",
			(int) ((time / 1000000.0) / 60.0),
			(int) ((time / 1000000.0) - ((int)((int)(time / 1000000.0) / 60.0) * 60)),
			(int) fmod (time, 1000000.0) / 10000);
  }
} /* Mandelbrot881 */

/*============================================================================*
 * Reset Mandelbrot Set
 *============================================================================*/

static void
ResetMand()
{
	ZoomNr = 0;
	ZoomFlag = TRUE;
	MaxIteration = 32;

	RealLow = -2.1;
	ImagLow = -1.125;
	RealHigh = 0.5;
	ImagHigh = 1.125;
}

/*============================================================================*
 * Calculate Zoom-Area
 *============================================================================*/

static void
DoZoom (struct IntuiMessage *msg)
{
	int Breite, Hoehe;
	double tmp;

	ZoomNr++;
	ZoomFlag = TRUE;
	MaxIteration += MaxIteration/2;

	Breite = InnerWidth  / ZoomBox;
	Hoehe  = InnerHeight / ZoomBox;

	tmp = RealLow;
	RealLow  = tmp + RealGap * ((msg->MouseX - left) - Breite/2);
	RealHigh = tmp + RealGap * ((msg->MouseX - left) + Breite/2);
	tmp = ImagLow;
	ImagLow  = tmp + ImagGap * ((msg->MouseY - top) - Hoehe/2);
	ImagHigh = tmp + ImagGap * ((msg->MouseY - top) + Hoehe/2);

	SetWindowTitles (FRA_win, -1L, GetTitle (fileName));
}

/*============================================================================*/

int
ASLRequestFile (char *Dir)
{
    struct FileRequester *FileRequester;
    char buffer[256];
    BOOL Result = FALSE;
    struct TagItem tags[6];
    LONG cnt = 0;

    SETTAG (ASL_Hail,       "Select Fractal file...");
    SETTAG (ASL_OKText,     "Load");
    SETTAG (ASL_CancelText, "Cancel");

    if (Dir && strlen(Dir)) {
        SETTAG (ASL_Dir, Dir);
    }

    SETTAG (TAG_END, 0);

    if (AslBase = OpenLibrary ("asl.library", 37)) {
        if (FileRequester = (struct FileRequester *) AllocAslRequest (ASL_FileRequest, tags)) {
            if (AslRequest (FileRequester, NULL)) {
                strcpy (buffer, FileRequester->rf_Dir);
                AddPart (buffer, FileRequester->rf_File, 255);
				strcpy (fileName, buffer);
                Result = TRUE;
            }
            FreeAslRequest (FileRequester);
        }
        CloseLibrary (AslBase);
    }
    return Result;
}

/*============================================================================*
 *  Alles ausser den Libraries schliessen und den Speicher wieder freigeben.  *
 *============================================================================*/

void
CloseDown (void)
{
    if (!timerclosed) {
      CloseDevice ((struct IORequest *) TimerIO);
	  if (TimerPort) {
		DeleteMsgPort (TimerPort);
		TimerPort = NULL;
	  }    
	  if (TimerIO) {
		DeleteIORequest ((struct IORequest *) TimerIO);
		TimerIO = NULL;
	  }    
      timerclosed = TRUE;
      TimerBase = NULL;
    }
    if (FRA_win) {
      if (Menus) {
          ClearMenuStrip (FRA_win);
          FreeMenus (Menus);
          Menus = NULL;
      }
      if (VisualInfo) {
          FreeVisualInfo (VisualInfo);
          VisualInfo = NULL;
      }
      CloseWindow (FRA_win);
      FRA_win = NULL;
    }

    if (hrh) {

      hrSwitchSecondView (hrh, FALSE);

      if (coltab) {
        hrSetPaletteRGBA (hrh, coltab, 0, 255);
        free (coltab);
        coltab = NULL;
      }

      if (curctab) {
        free (curctab);
        curctab = NULL;
      }

      hrFreeSecondView(hrh);
      hrh = NULL;
    }

    if (UpImage)     DisposeObject (UpImage);
    if (DownImage)   DisposeObject (DownImage);
    if (LeftImage)   DisposeObject (LeftImage);
    if (RightImage)  DisposeObject (RightImage);
    if (LeftGadget)  DisposeObject (LeftGadget);
    if (RightGadget) DisposeObject (RightGadget);
    if (UpGadget)    DisposeObject (UpGadget);
    if (DownGadget)  DisposeObject (DownGadget);
    if (VertGadget)  DisposeObject (VertGadget);
    if (HorizGadget) DisposeObject (HorizGadget);

    UpImage = DownImage = LeftImage = RightImage = NULL;
    LeftGadget = RightGadget = HorizGadget = NULL;
    UpGadget   = DownGadget  = VertGadget  = NULL;

    if (HRGSystemBase) CloseLibrary(HRGSystemBase);
                       HRGSystemBase = NULL;
    if (HRGRenderBase) CloseLibrary(HRGRenderBase);
                       HRGRenderBase = NULL;
    if (GadToolsBase)  CloseLibrary(GadToolsBase);
                       GadToolsBase = NULL;
    if (IntuitionBase) CloseLibrary((struct Library *) IntuitionBase);
                       IntuitionBase = NULL;
    if (GfxBase)       CloseLibrary((struct Library *) GfxBase);
                       GfxBase = NULL;
}

/*============================================================================*
 *  Programm mit einer Fehlermeldung beenden                                  *
 *============================================================================*/

void
Quit (int failcode)
{
    char *ErrMsg = "";

    if (!quietFlag) {
        switch (failcode) {
            case NO_ERR  : break;
            case REQ_ERR : break;
            case PAR_ERR : break;
            case MEM_ERR : ErrMsg = "Out of memory"; break;
            case GFX_ERR : ErrMsg = "Need graphics.library V37"; break;
            case INT_ERR : ErrMsg = "Need intuition.library V37"; break;
            case SYS_ERR : ErrMsg = "Need hrgsystem.library V38"; break;
            case REN_ERR : ErrMsg = "Need hrgrender.library V38"; break;
            case DBL_ERR : ErrMsg = "Can't open two PIP Windows"; break;
            case SCR_ERR : ErrMsg = "Can't open Merlin-Screen"; break;
            case WIN_ERR : ErrMsg = "Can't open Window"; break;
            case FND_ERR : ErrMsg = "Can't find Merlin-Screen"; break;
            case PIP_ERR : ErrMsg = "Screen for PIP to small (640x400)"; break;
            case DRI_ERR : ErrMsg = "Can't get ScreenDrawInfo"; break;
            case MPG_ERR : ErrMsg = "This is no Fractal File"; break;
            case RNG_ERR : ErrMsg = "Fatal error. Ring buffer full."; break;
            case GET_ERR : ErrMsg = "Unexpected read error."; break;
            case BRK_ERR : ErrMsg = "Interrupted!"; break;
            case GAD_ERR : ErrMsg = "Need gadtools.library V37"; break;
            case VIS_ERR : ErrMsg = "Can't get VisualInfo"; break;
            case MEN_ERR : ErrMsg = "Can't create Menu"; break;
            case TIM_ERR : ErrMsg = "Can't open timer.device"; break;
            default:       ErrMsg = "Unknown error";
        }
        OUTPUT1 ("\n%s\n", ErrMsg);
    }
    CloseDown ();
    exit (failcode);
}

/*============================================================================*
 *  Display allgemein initialisieren durch Oeffnen der Libraries.             *
 *============================================================================*/

void
InitLibraries (void)
{
	SysBase = (struct ExecBase *)(*(void **)4);

    if (!(GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 37))) {
        Quit (GFX_ERR);
    }
    if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 37))) {
        Quit (INT_ERR);
    }
    if (!(GadToolsBase = (struct Library *) OpenLibrary ("gadtools.library", 37))) {
        Quit (GAD_ERR);
    }
    if (!(HRGSystemBase = (struct Library *) OpenLibrary ("hrgsystem.library", 38))) {
        Quit (SYS_ERR);
    }
    if (!(HRGRenderBase = (struct Library *) OpenLibrary ("hrgrender.library", 38))) {
        Quit (REN_ERR);
    }

/*============================================================================*
 * timer stuff
 *============================================================================*/

	/* message port for communicating with timer device */

	if (!(TimerPort = CreateMsgPort()))
		Quit (TIM_ERR);

	/* our primary IO structure */

	if (!(TimerIO = (struct timerequest *)
	  CreateIORequest (TimerPort, sizeof(struct timerequest))))
		Quit (TIM_ERR);

	if (timerclosed = OpenDevice (TIMERNAME, UNIT_VBLANK,
						(struct IORequest *) TimerIO, 0))
		Quit (TIM_ERR);

	TimerBase = (struct Library *) TimerIO->tr_node.io_Device;

	micros_per_eclock = 1000000.0 / (double)ReadEClock (&time0);
}

/*============================================================================*
 *  Window und SecondView PIP vergoessern/verkleinern.                        *
 *============================================================================*/

static void
Resize (void)
{
    ULONG HorizPot, VertPot, dX, dY;

    GetAttr (PGA_Top, HorizGadget, (ULONG *)&HorizPot);
    GetAttr (PGA_Top, VertGadget,  (ULONG *)&VertPot);

    hrResizeSecondView (hrh, FRA_win->Width - Screen->WBorLeft - DownImage->Width,
                             FRA_win->Height - (Screen->Font->ta_YSize + Screen->WBorTop + 1) - RightImage->Height);
#ifndef LARGE
    SetGadAttrs (HorizGadget, FRA_win, NULL,
        PGA_Top,      HorizPot,
        PGA_Visible,  FRA_win->Width,
        PGA_Total,    Width,
        TAG_DONE);
#else
    SetGadgetAttrs (HorizGadget, FRA_win, NULL,
        PGA_Top,      HorizPot,
        PGA_Visible,  FRA_win->Width,
        PGA_Total,    Width,
        TAG_DONE);
#endif

    GetAttr (PGA_Top, HorizGadget, (ULONG *)&dX);

#ifndef LARGE
    SetGadAttrs (VertGadget, FRA_win, NULL,
        PGA_Top,      VertPot,
        PGA_Visible,  FRA_win->Height,
        PGA_Total,    Height,
        TAG_DONE);
#else
    SetGadgetAttrs (VertGadget, FRA_win, NULL,
        PGA_Top,      VertPot,
        PGA_Visible,  FRA_win->Height,
        PGA_Total,    Height,
        TAG_DONE);
#endif

    GetAttr (PGA_Top, VertGadget, (ULONG *)&dY);

    hrScrollSecondView (hrh, dX, dY);
    ScrollX = dX; ScrollY = dY;
}

/*============================================================================*
 *  Auf Knopfdruck und Verschieben der Rollbalken reagieren.                  *
 *============================================================================*/

static void
CheckGadget (UWORD GadgetID)
{
    int step, HorizPot, VertPot;
    ULONG dX = ScrollX, dY = ScrollY;

    switch (GadgetID) {
        case HORIZ_ID: GetAttr (PGA_Top, HorizGadget, (ULONG *)&dX);
                       break;

        case LEFT_ID : GetAttr (PGA_Top, HorizGadget, (ULONG *)&HorizPot);
                       step = max (HorizPot - WIDTH_STEP, 0);
#ifndef LARGE
					   SetGadAttrs (HorizGadget, FRA_win, NULL,
                           PGA_Top,      step,
                           PGA_Visible,  FRA_win->Width,
                           PGA_Total,    Width,
                           TAG_DONE);
#else
					   SetGadgetAttrs (HorizGadget, FRA_win, NULL,
                           PGA_Top,      step,
                           PGA_Visible,  FRA_win->Width,
                           PGA_Total,    Width,
                           TAG_DONE);
#endif
                       GetAttr (PGA_Top, HorizGadget, (ULONG *)&dX);
                       break;

        case RIGHT_ID: GetAttr (PGA_Top, HorizGadget, (ULONG *)&HorizPot);
                       step = min (HorizPot + WIDTH_STEP, Width);
#ifndef LARGE
					   SetGadAttrs (HorizGadget, FRA_win, NULL,
                           PGA_Top,      step,
                           PGA_Visible,  FRA_win->Width,
                           PGA_Total,    Width,
                           TAG_DONE);
#else
					   SetGadgetAttrs (HorizGadget, FRA_win, NULL,
                           PGA_Top,      step,
                           PGA_Visible,  FRA_win->Width,
                           PGA_Total,    Width,
                           TAG_DONE);
#endif
                       GetAttr (PGA_Top, HorizGadget, (ULONG *)&dX);
                       break;

        case VERT_ID : GetAttr (PGA_Top, VertGadget, (ULONG *)&dY);
                       break;

        case UP_ID   : GetAttr (PGA_Top, VertGadget,  (ULONG *)&VertPot);
                       step = max (VertPot - HEIGHT_STEP, 0);
#ifndef LARGE
					   SetGadAttrs (VertGadget, FRA_win, NULL,
                           PGA_Top,      step,
                           PGA_Visible,  FRA_win->Height,
                           PGA_Total,    Height,
                           TAG_DONE);
#else
					   SetGadgetAttrs (VertGadget, FRA_win, NULL,
                           PGA_Top,      step,
                           PGA_Visible,  FRA_win->Height,
                           PGA_Total,    Height,
                           TAG_DONE);
#endif
                       GetAttr (PGA_Top, VertGadget, (ULONG *)&dY);
                       break;

        case DOWN_ID : GetAttr (PGA_Top, VertGadget,  (ULONG *)&VertPot);
                       step = min (VertPot + HEIGHT_STEP, Height);
#ifndef LARGE
					   SetGadAttrs (VertGadget, FRA_win, NULL,
                           PGA_Top,      step,
                           PGA_Visible,  FRA_win->Height,
                           PGA_Total,    Height,
                           TAG_DONE);
#else
					   SetGadgetAttrs (VertGadget, FRA_win, NULL,
                           PGA_Top,      step,
                           PGA_Visible,  FRA_win->Height,
                           PGA_Total,    Height,
                           TAG_DONE);
#endif
                       GetAttr (PGA_Top, VertGadget, (ULONG *)&dY);
                       break;
    }
    if ((dX != ScrollX) || (dY != ScrollY)) {
      hrScrollSecondView (hrh, dX, dY);
      ScrollX = dX; ScrollY = dY;
    }
}

/*============================================================================*
 *  Auf Tastendruck reagieren.                                                *
 *============================================================================*/

static void
CheckKey (UWORD KeyID, ULONG Qualifier)
{
    int step, HorizPot, VertPot;

    ULONG dX = ScrollX, dY = ScrollY;

    switch (KeyID) {
        case KEY_LEFT:  GetAttr (PGA_Top, HorizGadget, (ULONG *)&HorizPot);
                        if (SHIFT_KEY(Qualifier)) {
                            step = max (HorizPot - FRA_win->Width, 0);
                        } else {
                            step = max (HorizPot - WIDTH_STEP, 0);
                        }
#ifndef LARGE
						SetGadAttrs (HorizGadget, FRA_win, NULL,
                            PGA_Top,      step,
                            PGA_Visible,  FRA_win->Width,
                            PGA_Total,    Width,
                            TAG_DONE);
#else
						SetGadgetAttrs (HorizGadget, FRA_win, NULL,
                            PGA_Top,      step,
                            PGA_Visible,  FRA_win->Width,
                            PGA_Total,    Width,
                            TAG_DONE);
#endif
                        GetAttr (PGA_Top, HorizGadget, (ULONG *)&dX);
                        break;

        case KEY_RIGHT: GetAttr (PGA_Top, HorizGadget, (ULONG *)&HorizPot);
                        if (SHIFT_KEY(Qualifier)) {
                            step = min (HorizPot + FRA_win->Width, Width);
                        } else {
                            step = min (HorizPot + WIDTH_STEP, Width);
                        }
#ifndef LARGE
						SetGadAttrs (HorizGadget, FRA_win, NULL,
                            PGA_Top,      step,
                            PGA_Visible,  FRA_win->Width,
                            PGA_Total,    Width,
                            TAG_DONE);
#else
						SetGadgetAttrs (HorizGadget, FRA_win, NULL,
                            PGA_Top,      step,
                            PGA_Visible,  FRA_win->Width,
                            PGA_Total,    Width,
                            TAG_DONE);
#endif
                        GetAttr (PGA_Top, HorizGadget, (ULONG *)&dX);
                        break;

        case KEY_UP:    GetAttr (PGA_Top, VertGadget,  (ULONG *)&VertPot);
                        if (SHIFT_KEY(Qualifier)) {
                            step = max (VertPot - FRA_win->Height, 0);
                        } else {
                            step = max (VertPot - HEIGHT_STEP, 0);
                        }
#ifndef LARGE
						SetGadAttrs (VertGadget, FRA_win, NULL,
                            PGA_Top,      step,
                            PGA_Visible,  FRA_win->Height,
                            PGA_Total,    Height,
                            TAG_DONE);
#else
						SetGadgetAttrs (VertGadget, FRA_win, NULL,
                            PGA_Top,      step,
                            PGA_Visible,  FRA_win->Height,
                            PGA_Total,    Height,
                            TAG_DONE);
#endif
                        GetAttr (PGA_Top, VertGadget, (ULONG *)&dY);
                        break;

        case KEY_DOWN:  GetAttr (PGA_Top, VertGadget, (ULONG *)&VertPot);
                        if (SHIFT_KEY(Qualifier)) {
                            step = min (VertPot + FRA_win->Height, Height);
                        } else {
                            step = min (VertPot + HEIGHT_STEP, Height);
                        }
#ifndef LARGE
						SetGadAttrs (VertGadget, FRA_win, NULL,
                            PGA_Top,      step,
                            PGA_Visible,  FRA_win->Height,
                            PGA_Total,    Height,
                            TAG_DONE);
#else
						SetGadgetAttrs (VertGadget, FRA_win, NULL,
                            PGA_Top,      step,
                            PGA_Visible,  FRA_win->Height,
                            PGA_Total,    Height,
                            TAG_DONE);
#endif
                        GetAttr (PGA_Top, VertGadget, (ULONG *)&dY);
                        break;

	/*	case KEY_MINUS:	fdelay++;
						break;*/
	}

    if ((dX != ScrollX) || (dY != ScrollY)) {
      hrScrollSecondView (hrh, dX, dY);
      ScrollX = dX; ScrollY = dY;
    }
}

/*============================================================================*
 *  Auf Menuewahl reagieren.                                                  *
 *============================================================================*/

static void
ChangeWindow (int x, int y, int w, int h)
{
    int dx, dy;

    dx = x - FRA_win->LeftEdge;
    dy = y - FRA_win->TopEdge;

    if (dx || dy ) {
      MoveWindow (FRA_win, dx, dy);
      hrMoveSecondView (hrh, FRA_win->LeftEdge + left, FRA_win->TopEdge + top);
    }
    dx = w - FRA_win->Width;
    dy = h - FRA_win->Height;

    if (dx || dy ) {
      SizeWindow (FRA_win, dx, dy);
      Resize ();
    }
}

static void
ResetWindow (void)
{
    int ox, oy;

    ox = (hrh->ModeInfo->ResWidth  - InnerWidth) / 2;
    oy = (hrh->ModeInfo->ResHeight - InnerHeight) / 2;

    ChangeWindow (ox, oy, FRA_win->MaxWidth, FRA_win->MaxHeight);
	SetVar (ENVNAME, NULL, -1, GVF_GLOBAL_ONLY);
}

static void
CheckMenu (UWORD MenuNumber)
{
 UWORD ItemNum;
 struct MenuItem *Item;
 char envbuf[ENVSIZE];
 int winx, winy,
     winw, winh;

 while (MenuNumber != MENUNULL) {
      Item = (struct MenuItem *) ItemAddress (Menus, MenuNumber);
      ItemNum = ITEMNUM (MenuNumber);

      switch (MENUNUM (MenuNumber)) {
       case 0: switch (ItemNum) {
         case 0:
#ifndef LARGE
				EasyReqArgs (FRA_win, &AboutRequest, NULL, (LONG *) NULL);
#else
				EasyRequestArgs (FRA_win, &AboutRequest, NULL, (LONG *) NULL);
#endif
				break;

         case 2:
         case 3: IEEEflag = !IEEEflag;
				 ZoomFlag = TRUE;
                 break;

         case 4: ResetMand();
                 break;

         case 6: Quit (NO_ERR);
       }
       break;

	   case 1: switch (ItemNum) {

         case 0: ResetWindow ();
                 break;

         case 1: sprintf (envbuf ,"%d %d %d %d %G %G %G %G",
							FRA_win->LeftEdge, FRA_win->TopEdge,
                            FRA_win->Width, FRA_win->Height,
							RealLow, RealHigh, ImagLow, ImagHigh);
				 SetVar (ENVNAME, envbuf, -1, GVF_GLOBAL_ONLY);
                 break;

         case 2: if ((GetVar (ENVNAME, envbuf, ENVSIZE, GVF_GLOBAL_ONLY)) > 0) {

					sscanf (envbuf, "%d %d %d %d %G %G %G %G",
							&winx, &winy, &winw, &winh,
							&RealLow, &RealHigh, &ImagLow, &ImagHigh);

					winw = min (max (winw, FRA_win->MinWidth),  FRA_win->MaxWidth);
					winh = min (max (winh, FRA_win->MinHeight), FRA_win->MaxHeight);
					winx = min (max (winx, 0), FRA_win->WScreen->Width  - winw);
					winy = min (max (winy, 0), FRA_win->WScreen->Height - winh);
					ChangeWindow (winx, winy, winw, winh);
				 } else {
					ResetWindow ();
				 }
                 break;

         case 4: ChangeWindow (FRA_win->LeftEdge, FRA_win->TopEdge,
							   FRA_win->MinWidth, FRA_win->MinHeight);
                 break;

         case 5: ChangeWindow  (min (FRA_win->WScreen->Width  - FRA_win->MaxWidth,
								FRA_win->LeftEdge),
                                min (FRA_win->WScreen->Height - FRA_win->MaxHeight,
								FRA_win->TopEdge),
                                FRA_win->MaxWidth, FRA_win->MaxHeight);
                 break;
       }
       break;
      }
      MenuNumber = Item->NextSelect;
 }
}

/*============================================================================*
 *  Rollbalken und Pfeilknoepfe erzeugen.                                     *
 *============================================================================*/

static void
AddGadgets (void)
{
    static struct TagItem ArrowMappings[] = { GA_ID, GA_ID, TAG_END };
    struct DrawInfo *DrawInfo;
    BOOL Borderless = FALSE;
    int ImageSize;
    UWORD Border;
    ULONG Mode;

    Mode = GetVPModeID (&Screen->ViewPort);

    if (Mode == INVALID_ID) {
		Mode = HIRES_KEY;
    }
    if ((Mode & HIRES_KEY) || ((Mode & MONITOR_ID_MASK) == MERLIN_KEY)) {	/* get system images */
		ImageSize = SYSISIZE_MEDRES;
		Border = 4;
    } else {
		ImageSize = SYSISIZE_LOWRES;
		Border = 3;
    }

    if (!(DrawInfo = (struct DrawInfo *) GetScreenDrawInfo (Screen)))
		Quit (DRI_ERR);

    UpImage = (struct Image *) NewObject (NULL, "sysiclass",
        SYSIA_Size,     ImageSize,
        SYSIA_Which,    UPIMAGE,
        SYSIA_DrawInfo, DrawInfo,
        TAG_END);
    DownImage = (struct Image *) NewObject (NULL, "sysiclass",
        SYSIA_Size,     ImageSize,
        SYSIA_Which,    DOWNIMAGE,
        SYSIA_DrawInfo, DrawInfo,
        TAG_END);
    LeftImage = (struct Image *) NewObject (NULL, "sysiclass",
        SYSIA_Size,     ImageSize,
        SYSIA_Which,    LEFTIMAGE,
        SYSIA_DrawInfo, DrawInfo,
        TAG_END);
    RightImage = (struct Image *) NewObject (NULL, "sysiclass",
        SYSIA_Size,     ImageSize,
        SYSIA_Which,    RIGHTIMAGE,
        SYSIA_DrawInfo, DrawInfo,
        TAG_END);

    FreeScreenDrawInfo (Screen, DrawInfo);

    if (!UpImage || !DownImage || !LeftImage || !RightImage)
		Quit (MEM_ERR);

    if ((SysBase->LibNode.lib_Version >= 39) &&		/* OS2.0 und Mono Roll- */
        (Screen->RastPort.BitMap->Depth > 1)) {		/* balken verbessern */
        Borderless = TRUE;
    }
    VertGadget = NewObject (NULL,"propgclass",
        GA_ID,          VERT_ID,
        GA_RelHeight,   - (UpImage->Height + DownImage->Height + RightImage->Height + (Screen->Font->ta_YSize + Screen->WBorTop + 1)) - 2,
        GA_Width,       DownImage->Width - 2 * Border,
        GA_Top,         (Screen->Font->ta_YSize + Screen->WBorTop + 1) + 1,
        GA_RelRight,    - (DownImage->Width - 2 * Border) - Border + 1,
        GA_Immediate,   TRUE,
        GA_FollowMouse, TRUE,
        GA_RelVerify,   TRUE,
        GA_RightBorder, TRUE,
        PGA_Borderless, Borderless,
        PGA_Freedom,    FREEVERT,
        PGA_NewLook,    TRUE,
        PGA_Visible,    1,
        PGA_Total,      1,
        TAG_DONE);

    UpGadget = NewObject (NULL,"buttongclass",
        GA_ID,          UP_ID,
        GA_Image,       UpImage,
        GA_Width,       UpImage->Width,
        GA_Height,      UpImage->Height,
        GA_RelBottom,   1 - RightImage->Height - DownImage->Height - UpImage->Height,
        GA_RelRight,    1 - UpImage->Width,
        GA_Immediate,   TRUE,
        GA_RelVerify,   TRUE,
        GA_Previous,    VertGadget,
        GA_RightBorder, TRUE,
        ICA_TARGET,     ICTARGET_IDCMP,
        ICA_MAP,        ArrowMappings,
        TAG_DONE);

    DownGadget = NewObject (NULL,"buttongclass",
        GA_ID,          DOWN_ID,
        GA_Image,       DownImage,
        GA_Width,       DownImage->Width,
        GA_Height,      DownImage->Height,
        GA_RelBottom,   1 - RightImage->Height - DownImage->Height,
        GA_RelRight,    1 - DownImage->Width,
        GA_Immediate,   TRUE,
        GA_RelVerify,   TRUE,
        GA_Previous,    UpGadget,
        GA_RightBorder, TRUE,
        ICA_TARGET,     ICTARGET_IDCMP,
        ICA_MAP,        ArrowMappings,
        TAG_DONE);

    HorizGadget = NewObject (NULL,"propgclass",
        GA_ID,          HORIZ_ID,
        GA_RelWidth,    - (LeftImage->Width + RightImage->Width + DownImage->Width + Screen->WBorLeft) - 1,
        GA_Height,      RightImage->Height - 2 * 2,
        GA_RelBottom,   - (RightImage->Height - 2 * 2) - 1,
        GA_Left,        Screen->WBorLeft - 1,
        GA_Immediate,   TRUE,
        GA_FollowMouse, TRUE,
        GA_RelVerify,   TRUE,
        GA_Previous,    DownGadget,
        GA_BottomBorder,TRUE,
        PGA_Borderless, Borderless,
        PGA_Freedom,    FREEHORIZ,
        PGA_NewLook,    TRUE,
        PGA_Visible,    1,
        PGA_Total,      1,
        TAG_DONE);

    LeftGadget = NewObject (NULL,"buttongclass",
        GA_ID,          LEFT_ID,
        GA_Image,       LeftImage,
        GA_RelRight,    1 - DownImage->Width - RightImage->Width - LeftImage->Width,
        GA_RelBottom,   1 - LeftImage->Height,
        GA_Height,      LeftImage->Height,
        GA_Width,       LeftImage->Width,
        GA_Immediate,   TRUE,
        GA_RelVerify,   TRUE,
        GA_Previous,    HorizGadget,
        GA_BottomBorder,TRUE,
        ICA_TARGET,     ICTARGET_IDCMP,
        ICA_MAP,        ArrowMappings,
        TAG_DONE);

    RightGadget = NewObject (NULL,"buttongclass",
        GA_ID,          RIGHT_ID,
        GA_Image,       RightImage,
        GA_Width,       RightImage->Width,
        GA_Height,      RightImage->Height,
        GA_RelBottom,   1 - RightImage->Height,
        GA_RelRight,    1 - DownImage->Width - RightImage->Width,
        GA_Immediate,   TRUE,
        GA_RelVerify,   TRUE,
        GA_Previous,    LeftGadget,
        GA_BottomBorder,TRUE,
        ICA_TARGET,     ICTARGET_IDCMP,
        ICA_MAP,        ArrowMappings,
        TAG_DONE);

    if (!LeftGadget || !RightGadget || !HorizGadget ||
		!UpGadget   || !DownGadget  || !VertGadget)
			Quit (MEM_ERR);

	/* Windowgroesse (Brutto) */

    Width  = InnerWidth + Screen->WBorLeft + DownImage->Width;
    Height = InnerHeight + (Screen->Font->ta_YSize + Screen->WBorTop + 1) + RightImage->Height;
}

/*============================================================================*
 *  Oeffnen eines Windows mit einem SecondView PIP.                           *
 *============================================================================*/

static char *
GetBaseName (char *name)
{
    char *fname;

    if (name) {
        if (name[strlen(name)-1] == '/') {
            name[strlen(name)-1] = '\0';
        }
        if (fname = strrchr (name, '/')) {
            return fname+1;
        } else if (fname = strrchr (name, ':')) {
            return fname+1;
        }
        return name;
    }
    return version+6;
}

static char *
GetTitle (char *name)
{
    if (name) {
        if (ZoomNr == 0) {
		  sprintf (name,"%s = Left: %G Right: %G dX: %G dY: %G",
					"Basic Set", RealLow, RealHigh, RealHigh-RealLow, ImagHigh-ImagLow);
        } else {
		  sprintf (name,"Zoom %d = Left: %G Right: %G dX: %G dY: %G",
					ZoomNr, RealLow, RealHigh, RealHigh-RealLow, ImagHigh-ImagLow);
        }
    }
	return name;
}

static void
OpenMerlinPIP (int w, int h)
{
    struct TagItem tags[17];
    struct HRList *hrl;
    struct HRHandle *hrh2;
    char envbuf[ENVSIZE];
    LONG cnt;
    int winx, winy, winw, winh;
    int ox, oy;

    hrl = hrLockHRList();
    hrh2 = hrl->laston;
    hrUnlockHRList (hrl);

    if (!hrh2 || !(Screen = hrh2->IScr)) {	/* Richtigen IntuiScreen suchen */
      Quit (FND_ERR);
    }
    if (hrh2->SecondView != NULL) {			/* Nur ein PIP erlaubt */
      Quit (DBL_ERR);
    }
    if ((hrh2->ModeInfo->ResWidth < 640) || (hrh2->ModeInfo->ResHeight < 400)) {
      Quit (PIP_ERR);
    }

    AddGadgets();

    left = Screen->WBorLeft;
    top  = Screen->Font->ta_YSize + Screen->WBorTop + 1;

    ox = (hrh2->ModeInfo->ResWidth  - w) / 2;
    oy = (hrh2->ModeInfo->ResHeight - h) / 2;

    cnt = 0;

    if ((GetVar (ENVNAME, envbuf, ENVSIZE, GVF_GLOBAL_ONLY)) > 0) {
		sscanf (envbuf, "%d %d %d %d %G %G %G %G",
				&winx, &winy, &winw, &winh,
				&RealLow, &RealHigh, &ImagLow, &ImagHigh);
        winw = min (max (winw, MIN_WIDTH),  Width);
        winh = min (max (winh, MIN_HEIGHT), Height);
        winx = min (max (winx, 0), hrh2->ModeInfo->ResWidth  - winw);
        winy = min (max (winy, 0), hrh2->ModeInfo->ResHeight - winh);

        SETTAG (WA_Left,    winx);
        SETTAG (WA_Top,     winy);
        SETTAG (WA_Width,   winw);
        SETTAG (WA_Height,  winh);
    } else {
        ox = (hrh2->ModeInfo->ResWidth  - w) / 2;
        oy = (hrh2->ModeInfo->ResHeight - h) / 2;

        SETTAG (WA_Left,    ox - left);
        SETTAG (WA_Top,     oy - top);
        SETTAG (WA_Width,   Width);
        SETTAG (WA_Height,  Height);
    }
    SETTAG (WA_Title,       version+6);
    SETTAG (WA_PubScreen,   Screen);
    SETTAG (WA_MaxWidth,    Width);
    SETTAG (WA_MaxHeight,   Height);
    SETTAG (WA_MinWidth,    MIN_WIDTH);
    SETTAG (WA_MinHeight,   MIN_HEIGHT);
    SETTAG (WA_IDCMP,       IDCMP_GADGETUP | IDCMP_GADGETDOWN | IDCMP_RAWKEY |
                            IDCMP_INTUITICKS | IDCMP_CLOSEWINDOW | IDCMP_MENUPICK |
                            IDCMP_CHANGEWINDOW | IDCMP_MOUSEBUTTONS);
    SETTAG (WA_Flags,       WFLG_SIZEGADGET | WFLG_SIZEBRIGHT | WFLG_SIZEBBOTTOM | WFLG_ACTIVATE |
                            WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET );
    SETTAG (WA_Gadgets,     VertGadget);
    SETTAG (WA_ScreenTitle, GetTitle(fileName));
    SETTAG (WA_NewLookMenus,TRUE);
    SETTAG (TAG_END,        0);

    if (!(FRA_win = OpenWindowTagList (NULL, tags))) {  /* Window oeffnen */
      Quit (WIN_ERR);
    }
    rp = FRA_win->RPort;

    if (!(coltab = (ULONG *) malloc (256 * sizeof(ULONG))))
      Quit (MEM_ERR);

    hrGetPaletteRGBA (hrh2, coltab, 0, 255);	/* Farbpalette retten */

    hrAllocSecondView (hrh2, w, h, 0);			/* PIP erzeugen */

    hrh = hrh2;
    hrbm = &hrh->SecondView->BitMap;

    hrClearToCol (hrbm, 1);

    hrMoveSecondView (hrh, FRA_win->LeftEdge + left,
                           FRA_win->TopEdge  + top);
    Resize ();

    if (!(Menus = (struct Menu *) CreateMenus (NewMenu, TAG_DONE)))
      Quit (MEN_ERR);

    if (!(VisualInfo = (APTR) GetVisualInfo (Screen, TAG_DONE)))
      Quit (VIS_ERR);

    LayoutMenus (Menus, VisualInfo, GTMN_NewLookMenus, TRUE, TAG_DONE);
    SetMenuStrip (FRA_win, Menus);

    hrSwitchSecondView (hrh, TRUE);
}

/*============================================================================*
 * Print a usage message and exit
 *============================================================================*/

/*============================================================================*
 * Main :)
 *============================================================================*/

static char template[] =
    "W=Width/K/N,H=Height/K/N,"
    "B=Box/K/N,D=Depth/K/N,Q=Quiet/S";

static char usage[] =
    " Width <n>  - Sets the width of the Merlin-PIP\n"
    " Height <n> - Sets the height of the Merlin-PIP\n"
    " Box <n>    - Sets the zoom box (indirectly, 1/nth of InnerWidth/Height)\n"
    " Depth <n>  - Sets the iteration depth\n"
    " Quiet      - Supresses printing of timing information, and most error\n"
    "              messages.\n";

struct {
    int  *width;
    int  *height;
    int  *box;
    int  *depth;
    LONG Quiet;
} cliarray = {
    NULL, NULL, NULL, NULL, 0
};

/*
 *--------------------------------------------------------------
 *
 * main --
 *
 *      Parses command line, starts decoding and displaying.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

static void
ParseArgs (void)
{
    struct RDArgs *cliargs;

    wwidth  = 320;
    wheight = 256;

    if (!(cliargs = ReadArgs (template, (LONG *)&cliarray, NULL))) {
        SET_A_PEN(3); OUTPUT1 ("%s", &version[6]); SET_A_PEN(1);
        OUTPUT1 ("\n%s\n", template);
        OUTPUT1 ("\n%s\n", usage);
        exit (NO_ERR);
    }
    if (cliarray.Quiet)  quietFlag = TRUE;
    if (cliarray.width)  wwidth = *cliarray.width;
    if (cliarray.height) wheight = *cliarray.height;
    if (cliarray.depth)  MaxIteration = *cliarray.depth;
    if (cliarray.box)    ZoomBox = *cliarray.box;

    FreeArgs (cliargs);
}

static void
ParseToolTypes ()
{
    struct DiskObject *dobj;
    char **toolarray, *s, *Directory = NULL;
    struct WBArg *wbarg;
    SHORT i;
    char buffer[256];

    quietFlag = TRUE;

    if (!(IconBase = OpenLibrary ("icon.library", 33))) {
        return;
    }
    for (i=0, wbarg=_WBenchMsg->sm_ArgList; i < _WBenchMsg->sm_NumArgs; i++, wbarg++) {

        if (!i) {
            wbarg->wa_Lock = GetProgramDir ();
            NameFromLock (wbarg->wa_Lock, buffer, 255);
            AddPart (buffer, wbarg->wa_Name, 255);

            if (*buffer && (dobj = GetDiskObject (buffer))) {
                toolarray = (char **)dobj->do_ToolTypes;

                if (s = (char *) FindToolType (toolarray, "WIDTH")) {
                  wwidth = atoi (s);
                }
                if (s = (char *) FindToolType (toolarray, "HEIGHT")) {
                  wheight = atoi (s);
                }
                if (s = (char *) FindToolType (toolarray, "DEPTH")) {
                  MaxIteration = atoi (s);
                }
                if (s = (char *) FindToolType (toolarray, "BOX")) {
                  ZoomBox = atoi (s);
                }
                FreeDiskObject (dobj);
            }
        } else if (!fileName) {
            NameFromLock (wbarg->wa_Lock, buffer, 255);
            AddPart (buffer, wbarg->wa_Name, 255);
          /*  fileName = strdup (buffer); */
        }
    }
    CloseLibrary (IconBase);

/*    if (!fileName) {
        if (!(ASLRequestFile (Directory))) {
            exit (NO_ERR);
        }
    }
    if (Directory) free (Directory);
*/
}

/*============================================================================*/

void
main ()
{
	MYTIMEVAL tv;

    if (_WBenchMsg == NULL)
      ParseArgs ();
    else
      ParseToolTypes ();

    if (!quietFlag) {
      SET_A_PEN(3); OUTPUT1 ("%s", &version[6]); SET_A_PEN(1);
    }
/*
	if (!verbose)
		quietFlag = TRUE;

	if (argc != 1) {
	  if (!ASLRequestFile (NULL))
		usage();
	}
	else {
		strcpy (fileName, argv[1]);
	}  

	if (argc == 1 && argv[1][0] == '?')
		usage();
*/
	stdout = Output();

/*============================================================================*
 *  Initialisieren und Oeffnen des Displays.
 *============================================================================*/

	InitLibraries();

	/* Create a wwidth x wheight window to show the fract */

    InnerWidth  = wwidth;	/* Animationsgroesse (netto) merken */
    InnerHeight = wheight;

	FrameSize = wwidth * wheight;

	ResetMand();

	OpenMerlinPIP (wwidth, wheight);

	tv.LeftSeconds = 0;		/* initial values for testing double-click */
	tv.LeftMicros  = 0;
	tv.RightSeconds = 0;
	tv.RightMicros  = 0;

/*============================================================================*/

	if (!IEEEflag)
	  MandelbrotInt32II();
	else
	  Mandelbrot881();

/*============================================================================*
 * IDCMP-Messages am Window-UserPort abfragen und entsprechende Maßnahmen     *
 * ergreifen.                                                                 *
 *============================================================================*/

	while (TRUE) {
	  ZoomFlag = FALSE;
      while (imsg = (struct IntuiMessage *) GetMsg (FRA_win->UserPort)) {
        msg = *imsg;
        ReplyMsg ((struct Message *) imsg);
        switch (msg.Class) {
          case IDCMP_CLOSEWINDOW : Quit (NO_ERR);
          case IDCMP_CHANGEWINDOW: hrMoveSecondView (hrh, FRA_win->LeftEdge + left,
                                                          FRA_win->TopEdge  + top);
                                   Resize ();
								   break;
          case IDCMP_GADGETDOWN  : CurrentGadget = GADGETID(msg);
								   break;
          case IDCMP_GADGETUP    : CheckGadget (CurrentGadget);
                                   CurrentGadget = NO_GADGET;
								   break;
          case IDCMP_INTUITICKS  : CheckGadget (CurrentGadget);
								   break;
          case IDCMP_RAWKEY      : CheckKey (msg.Code, msg.Qualifier);
								   break;
          case IDCMP_MENUPICK    : CheckMenu (msg.Code);
								   break;
          case IDCMP_MOUSEBUTTONS:
			switch (msg.Code) {

			  case SELECTDOWN:
				if (DoubleClick (tv.LeftSeconds, tv.LeftMicros, msg.Seconds, msg.Micros))
					DoZoom (&msg);
				else {
					tv.LeftSeconds = msg.Seconds;
					tv.LeftMicros  = msg.Micros;
					tv.RightSeconds = 0;
					tv.RightMicros  = 0;
				}
				break;

			  case MENUDOWN:	/* not reached, but looks nice :) */
				if (DoubleClick(tv.RightSeconds, tv.RightMicros, msg.Seconds, msg.Micros))
					DoZoom (&msg);
				else {
					tv.LeftSeconds = 0;
					tv.LeftMicros  = 0;
					tv.RightSeconds = msg.Seconds;
					tv.RightMicros  = msg.Micros;
				}
				break;
			}
			break;
        }
      }
	  if (ZoomFlag){
		if (!IEEEflag)
		  MandelbrotInt32II();
		else
		  Mandelbrot881();
	  }
	}
}

