
/***********************************************************************\
*			     2View V1.11				*
*	 A simple, fast ILBM viewer, for use under AmigaOS V2.x.	*
*      Written and ©1991 by Dave Schreiber.  All Rights Reserved.	*
*									*
* Usage:								*
*  2View FILE/A/M,FROM/K,SECS=SECONDS/K/N,TICKS/K/N,LOOP/S,PRINT	*
*									*
*  Where the following arguments are defined as follows:		*
*   FILE - The name of one (or more) IFF ILBM files                     *
*   FROM - A file containing a list of filenames.  Used instead of FILE *
*   SECS - Number of seconds to display a file				*
*   TICKS - Number of ticks (1/50ths of a second)                       *
*   LOOP - When finished showing the last pictures, start over		*
*   PRINT - Print each picture as it is shown				*
*									*
*  To compile (with SAS/C V5.10a):                                      *
*     lc -v 2View ARexx 						*
*     blink with 2View.lnk						*
*									*
*  Version history:							*
*     1.11 - Improved error reporting (with this version, if the user   *
*	     run 2View from Workbench and there's an error, a requester *
*	     is put up.  Previously, the user was not notified at all	*
*	     of the error).						*
*	     Released 9/11/91						*
*									*
*     1.10 - Added support for Workbench, ARexx, scrollable bitmaps,	*
*	     and printing.  Also, the user can now use CTRL-C to advance*
*	     to the next frame, and CTRL-D to abort a playlist. 	*
*	     Released 9/3/91						*
*									*
*     1.00 - Original version.	Released 7/24/91			*
*									*
\************************************************************************/


unsigned long availBytes,curPos,bufSize;

/*Include files*/

#include <exec/types.h>
#include <libraries/iffparse.h>
#include <dos/dos.h>
#include <intuition/screens.h>
#include <intuition/intuition.h>
#include <dos/rdargs.h>
#include <exec/memory.h>
#include <devices/printer.h>
#include <devices/prtgfx.h>
#include <workbench/startup.h>

/*Prototypes*/
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/iffparse_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>

/*Pragmas*/
#include <pragmas/exec_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/iffparse_pragmas.h>
#include <pragmas/dos_pragmas.h>

/*Other include files*/
#include "iff.h"
#include "2View.h"
#include "arexx.h"

/*Libraries we'll need*/
struct Library *IFFParseBase=NULL;
struct Library *IntuitionBase=NULL;
struct Library *GfxBase=NULL;

/*Provided by the compiler*/
extern struct Library *SysBase;
extern struct Library *DOSBase;

/*Generic screen and window definitions.  They will be used to define*/
/*the screen and window that the various pictures will be shown on*/
struct NewScreen newScreen=
{
   0,0,0,0,0,1,0,NULL,CUSTOMSCREEN|SCREENBEHIND|AUTOSCROLL,NULL,NULL,NULL,
   NULL
};

struct NewWindow newWindow =
{
   0,0,0,0,0,1,MENUDOWN|SELECTDOWN|ACTIVEWINDOW|VANILLAKEY,
      RMBTRAP|BORDERLESS|NOCAREREFRESH|ACTIVATE,NULL,NULL,NULL,NULL,NULL,
      0,0,640,400,CUSTOMSCREEN
};

struct Screen *screen=NULL;
struct Window *window=NULL;

/*A true here indicates the current ILBM file is compressed*/
BYTE Compression;

/*An error message that used in various places*/
char *errorMsg="An error occured while reading";

/*The version string.  Do a 'version 2View' to display it*/
char *version="$VER: QView V1.11 (11.9.91)";

/*Just so that the © message is part of the actual program*/
char *copyRightMsg="Copyright 1991 by Dave Schreiber.  All Rights Reserved.";

BYTE ExitFlag=FALSE;	/*'Exit now' flag*/
UWORD ticks=0;	      /*Delay requested by user.*/

/*The previous screen and window*/
struct Window *prevWindow=NULL;
struct Screen *prevScreen=NULL;

/*Data for a blank pointer*/
UWORD chip fakePointerData[]={0,0,0,0,0};

BPTR StdErr=NULL;	      /*'Standard error' for AmigaDOS IO functions*/
struct IFFHandle *iff=NULL;   /*IFF handle*/
BPTR pL=NULL;		      /*Playlist file pointer*/
BOOL masking,print,toFront,printPics;
extern struct WBStartup *WBenchMsg;

char *playListFilename=NULL;

/*Variables that have to be global so that ARexx.c can access them*/
ButtonTypes rexxAbort=none;
extern void dispRexxPort(void);
extern void dnRexxPort();
long arexxSigBit;
UWORD ticksRemaining=0;
BOOL loop=FALSE;

char *picFilename;
char buf[512];	     /* A place to dump mask information */

void _main();

struct TagItem TagList[]=
{
      /* This defines what part of the displayed picture is shown.  It's */
      /* necessary to have a line like this in here in order to get	 */
      /* 2.0 autoscrolling to work.					 */
   {SA_Overscan,OSCAN_VIDEO},
   {TAG_DONE,NULL}
};

char *about1="2View";
char *about2="Please";


void _main()
{
   UWORD c;
   LONG args[7];
   char **filenames;
   char curFilename[140];
   BYTE playList; /*True if a playlist is being used, false otherwise*/

      /*Initialize the argument buffers to NULL*/
   for(c=0;c<7;c++)
      args[c]=NULL;

      /*Open the libraries*/
   IFFParseBase=(struct Library *)OpenLibrary("iffparse.library",0L);
   if(IFFParseBase==NULL)
   {
      cleanup();
      exit(50);
   }

   IntuitionBase=(struct Library *)OpenLibrary("intuition.library",0L);
   if(IntuitionBase==NULL)
   {
      cleanup();
      exit(75);
   }

   GfxBase=(struct Library *)OpenLibrary("graphics.library",0L);
   if(GfxBase==NULL)
   {
      cleanup();
      exit(85);
   }

      /*Get the arguments*/
   if(WBenchMsg==NULL)
   {
      ParseArgs(args);

	 /*If a playlist filename was provided, store it for later use*/
      if(args[4]!=NULL)
      {
	 playListFilename=(char *)args[4];
	 playList=TRUE;
      }
      else  /*Otherwise, read the filenames from the command line*/
	 playList=FALSE;

	 /*If a time was provided (in ticks), use it*/
      if(args[1]!=NULL)
	 ticks=*(ULONG *)args[1]*50;

	 /*If a time was provided (in seconds), use it (overrides ticks)*/
      if(args[2]!=NULL && *(ULONG *)args[2]!=0)
	 ticks=*(ULONG *)args[2];

	 /*If neither a picture filename, nor a playlist filename, was*/
	 /*specified, print an error and exit.*/
      if(args[0]==NULL && !playList)
      {
	 printError("Please enter one or more filenames","");
	 cleanup();
	 exit(45);
      }

	 /*Determine if we should print the pictures we display or not*/
      printPics=(args[5]!=NULL);

	 /*Get the pointer to the list of filename*/
      filenames=(char **)args[0];

	 /*Will we loop back to the beginning once we finish displaying all*/
	 /*the pictures?*/
      loop=(args[3]!=NULL);
   }
   else
      if(WBenchMsg->sm_NumArgs==1)
      {
	 EasyRequest(NULL,&erError1Line,NULL,
		     "2View V1.11 (September 11, 1991)",
		     "Written by Dave Schreiber");
	 cleanup();
	 exit(0);
      }



      /* Initialize the ARexx port */
   arexxSigBit=initRexxPort();
   if(arexxSigBit==0)
   {
      cleanup();
      exit(47);
   }

      /*Allocate the IFF structure*/
   iff=AllocIFF();

      /*If the allocation failed, abort*/
   if(iff==NULL)
   {
      printError("Couldn't allocate necessary resources","");
      cleanup();
      exit(100);
   }

      /*Run until we run out of filenames, or the user aborts*/
   while(!ExitFlag)
   {
      picFilename=curFilename;	 /*Get a pointer to the filename buffer*/

	 /*Check to see if we're running from Workbench.  If so, and the*/
	 /*user provided names of pictures to display (by clicking on their*/
	 /*icons), display those pictures*/
      if(WBenchMsg!=NULL && WBenchMsg->sm_NumArgs>1)
      {
	 CurrentDir(WBenchMsg->sm_ArgList[1].wa_Lock);
	 picFilename=WBenchMsg->sm_ArgList[1].wa_Name;
      }
      else if(playList) /*If a playlist is being used*/
      {
	 pL=Open(playListFilename,MODE_OLDFILE);   /*Open the playlist*/

	 if(pL==NULL)   /*If we couldn't open the playlist, abort*/
	 {
	    printError("Can't open playlist","");
	    cleanup();
	    exit(199);
	 }

	 do    /*Loop until we run out of playlist, or get a valid name*/
	 {
	    if(FGets(pL,picFilename,140)==NULL) /*If end-of-file*/
	       picFilename=NULL;       /*Set as NULL as a flag*/
	 }
	 while(picFilename!=NULL && picFilename[0]==0x0A);

	 if(picFilename!=NULL)        /*If not NULL, it's a valid filename*/
	    picFilename[strlen(picFilename)-1]=NULL; /*Remove the linefeed*/
      }
      else  /*Otherwise, if a playlist isn't being used, get the current*/
	 picFilename=filenames[0];     /*filename*/


	 /*Loop while the user hasn't requested an abort, and while*/
	 /*there are still files to display*/
      for(c=0;!ExitFlag && picFilename!=NULL;c++)
      {
	 if((iff->iff_Stream=Open(picFilename,MODE_OLDFILE))==NULL)
	 {     /*If the ILBM file can't be opened...*/

	       /*Print an error...*/
	    printError("Can't open:  ", picFilename);

	    cleanup();
	    exit(200);
	 }

	 InitIFFasDOS(iff);      /*The IFF file will be read from disk*/

	 OpenIFF(iff,IFFF_READ); /*Make iffparse.library aware of the*/
				 /*ILBM file*/

	 /*Read in the file and display*/
	 ReadAndDisplay(picFilename,iff);

	 CloseIFF(iff);          /*Release iffparse's hold on the file*/

	 Close(iff->iff_Stream); /*Close the file*/

	    /*Get the next filename, either from Workbench,*/
	 if(WBenchMsg!=NULL)
	 {
	    if(WBenchMsg->sm_NumArgs > c+2)
	    {
	       CurrentDir(WBenchMsg->sm_ArgList[c+2].wa_Lock);
	       picFilename=WBenchMsg->sm_ArgList[c+2].wa_Name;
	    }
	    else
	       picFilename=NULL;
	 }
	 else if(playList)   /*The playlist,*/
	 {
	    do
	    {
	       if(FGets(pL,picFilename,140)==NULL)
		  picFilename=NULL;
	    }
	    while(picFilename!=NULL && picFilename[0]==0x0A);

	    if(picFilename!=NULL)
	       picFilename[strlen(picFilename)-1]=NULL;
	 }
	 else  /*or the command line*/
	    picFilename=filenames[c+1];
      }

	 /*We're finished with this run of pictures*/
      if(playList)         /*Close playlist, if open*/
	 Close(pL);
      pL=NULL;

      if(!loop && !printPics) /*If the loop flag wasn't given, exit*/
	 ExitFlag=TRUE;
   }

      /*Time to exit, so close stuff*/

   cleanup();
   exit(0); /*And exit*/
}

LONG ilbmprops[] = { ID_ILBM,ID_CMAP,ID_ILBM,ID_BMHD,ID_ILBM,ID_CAMG };

/*Read in an ILBM file and display it*/
void ReadAndDisplay(char *filename,struct IFFHandle *iff)
{
   int error;
   UBYTE *bodyBuffer;	/*Pointer to buffer holding 'BODY' chunk info*/
   ULONG ViewModes;	/*Holds the viewmodes flags*/
   UWORD c;
   ButtonTypes button;

      /*Structures required for IFF parsing*/
   struct StoredProperty *bmhd,*cmap,*camg;
   struct ContextNode *bodyContext;

      /*IntuiMessage...*/
   struct IntuiMessage *mesg;

      /*Indentify chunks that should be stored during parse*/
      /*(in this case, CMAP, BMHD, and CAMG)*/
   error=PropChunks(iff,ilbmprops,3);

      /*If there was an error, print a message and return*/
   if(error!=0)
   {
      printError(errorMsg,filename);
      ExitFlag=TRUE;
      return;
   }

      /*Tell iffparse to stop at a 'BODY' chunk*/
   error=StopChunk(iff,ID_ILBM,ID_BODY);

      /*Error handling yet again*/
   if(error!=0 && error!=-1)
   {
      printError(errorMsg,filename);
      ExitFlag=TRUE;
      return;
   }

      /*Do the actual parsing*/
   error=ParseIFF(iff,IFFPARSE_SCAN);

      /*Check for errors yet again*/
   if(error!=0 && error !=-1)
   {
      printError(errorMsg,filename);
      ExitFlag=TRUE;
      return;
   }

      /*Get the BMHD, CMAP, and CAMG chunks that were read from the file*/
   bmhd = FindProp(iff,ID_ILBM,ID_BMHD);
   cmap = FindProp(iff,ID_ILBM,ID_CMAP);
   camg = FindProp(iff,ID_ILBM,ID_CAMG);

      /*Get the descriptor for the BODY chunk*/
   bodyContext=CurrentChunk(iff);

      /*If there wasn't a BMHD, CMAP, or BODY chunk, abort*/
   if(!bmhd | !cmap | !bodyContext)
   {
      printError(filename,"is corrupted or is not in Amiga ILBM format");
      ExitFlag=TRUE;
      return;
   }

      /*Prepare to determine screen modes*/
   newScreen.ViewModes=NULL;

      /*If there was a CAMG chunk, use it to get the viewmodes*/
   if(camg!=NULL)
   {
      ViewModes=( ((CAMG *)(camg->sp_Data))->viewmodes );

      if(ViewModes & HAM)
	 newScreen.ViewModes|=HAM;

      if(ViewModes & EXTRA_HALFBRITE)
	 newScreen.ViewModes|=EXTRA_HALFBRITE;

      if(ViewModes & LACE)
	 newScreen.ViewModes|=LACE;

      if(ViewModes & HIRES)
	 newScreen.ViewModes|=HIRES;
   }

      /*Interpret the BMHD chunk*/
   getBMHD((struct BitMapHeader *)bmhd->sp_Data);

      /*Open a screen, defined by the BMHD and CAMG chunks*/

   screen=OpenScreenTagList(&newScreen,TagList);

      /*If the screen couldn't be opened, abort*/
   if(screen==NULL)
   {
      printError("Cannot open screen!","");
      ExitFlag=TRUE;
      return;
   }

      /*Set the window dimensions from the screen dimensions*/
   newWindow.Screen=screen;
   newWindow.Width=newScreen.Width;
   newWindow.Height=newScreen.Height;

      /*Open the window*/
   window=OpenWindow(&newWindow);

      /*Abort if the window couldn't be opened*/
   if(window==NULL)
   {
      printError("Cannot open window!","");

      ExitFlag=TRUE;
      return;
   }

      /*Allocate enough memory to hold the BODY data*/
      /*We want to find out what the biggest block of memory is.  If*/
      /*we have enough memory to hold the entire body chunk, we'll load*/
      /*the whole thing into memory and decompress from there.	If not,*/
      /*we'll load as much as we can and refill the buffer when it*/
      /*empties*/
      /*We do a Forbid() to keep anyone from allocating memory between*/
      /*the AvailMem() and AllocMem() calls.  This way, we're guaranteed*/
      /*that a memory block of the size given by Availmem() can be */
      /*allocated.  For this reason, we don't need to check the result*/
      /*of the AllocMem();  we're guaranteed that it worked*/
   Forbid();
   bufSize=AvailMem(MEMF_LARGEST);
   bufSize=MIN(bufSize,bodyContext->cn_Size+1);
   if(bufSize==0)    /*It'll never happen, but just in case...*/
   {
      Permit();
      ExitFlag=TRUE;
      return;
   }

   bodyBuffer=AllocMem(bufSize,0L);
   Permit();

   availBytes = bufSize;
   curPos = bufSize;

      /*Blank out the pointer*/
   SetPointer(window,fakePointerData,1,16,0,0);

      /*Set the screen colors to those provided in the CMAP chunk*/
   setScreenColors(screen,cmap->sp_Data,newScreen.Depth);

      /*Put the BODY chunk information into the bitmap*/
   ReadBodyIntoBitmap(&(screen->BitMap),bodyBuffer,bufSize);

      /*Activate the window, and flush any IDCMP message*/
   ActivateWindow(window);
   while((mesg=(struct IntuiMessage *)GetMsg(window->UserPort))!=NULL)
      ReplyMsg((struct Message *)mesg);

      /*Bring the screen to the front*/
   ScreenToFront(screen);


      /*If the user used the 'print' flag on the command line, print*/
      /*the picture*/
   if(printPics)
      dumpRastPort(&(screen->RastPort),&(screen->ViewPort));

   print=TRUE;

      /*Close the previous window and screen*/
   if(prevWindow!=NULL)
      CloseWindow(prevWindow);
   if(prevScreen!=NULL)
      CloseScreen(prevScreen);

      /*Free the buffer that holds the BODY chunk information*/
   FreeMem(bodyBuffer,bufSize);

      /*Store the current window & screen structures, so they can be*/
      /*closed later*/
   prevWindow=window;
   prevScreen=screen;

   screen=NULL;
   window=NULL;

   rexxAbort=none;
   if(ticks==0) /*If ticks==0, this means that no delay was specified*/
   {		  /*by the user.  So just wait for him to click a button*/

      while((button=checkButton())==none && rexxAbort==none)
      {
	 Wait((1<<prevWindow->UserPort->mp_SigBit) | arexxSigBit);
	 dispRexxPort();
      }

      if(button==menu || rexxAbort==menu)
	 ExitFlag=TRUE;
   }
   else     /*Otherwise, wait for the specified amount of time*/
   {
      for(c=0;c<ticks;c+=25)
      {
	 ticksRemaining=ticks-c;
	 Delay((ticksRemaining < 25) ? ticksRemaining : 25);

	 dispRexxPort();         /*Check ARexx port*/

	 button=checkButton();   /*After each 25 ticks, check to see if*/
	 if(button==menu || rexxAbort==menu)    /*the user wants to abort*/
	 {
	    ExitFlag=TRUE;
	    return;
	 }
	 if(button==select || rexxAbort==select)  /*Or advance prematurely*/
	    return;
      }
   }
}

/*Convert the CMAP information into a color map, then set the colors*/
/*of the screen according to that colormap*/
void setScreenColors(struct Screen *scr, UBYTE *colorMap, UBYTE depth)
{
   int i,numColors;

   numColors=1<<depth;	/*Get the number of colors (generally 2^depth)*/

   if(newScreen.ViewModes & HAM)    /*There are, of course, 2 exceptions*/
      numColors=16;
   if(newScreen.ViewModes & EXTRA_HALFBRITE)
      numColors=32;

      /*For each color, convert it from CMAP to Amiga form and*/
      /*install it*/
   for(i=0;i<numColors;i++)
      SetRGB4(&(scr->ViewPort),i,colorMap[i*3]>>4,colorMap[i*3+1]>>4,
	       colorMap[i*3+2]>>4);

   return;
}

/*Transform the information in the BODY chunk into a displayable picture,*/
/*decompressing if necessary*/
void ReadBodyIntoBitmap(struct BitMap *bm,UBYTE *buffer,ULONG bufferSize)
{
   register int d;
   register int repl;
   register UBYTE toRepl;
   BYTE *src,*dest;
   register int depth,rows;
   register int bytesInRow;
   register int reqDepth;

   src=buffer;
   bytesInRow=((newScreen.Width+15)/8) & (~1);

      /*If there's a mask plane, we have an extra bitplane to read in*/
   reqDepth=(masking) ? newScreen.Depth+1 : newScreen.Depth;

      /*Loop once for each row*/
   for(rows=0;rows<newScreen.Height;rows++)
	 /*Loop once for each in plane in each row*/
      for(depth=0;depth<reqDepth;depth++)
      {
	    /*If we've read all the valid bitplane information and are*/
	    /*now about to get to the mask, point the destination*/
	    /*buffer pointer at an unused buffer (a bit bucket)*/
	 if(masking && depth==reqDepth-1)
	    dest=buf;
	 else
	    dest=&(bm->Planes[depth][bytesInRow*rows]);

	 if(Compression==1)   /*If the file is compressed, decompress*/
	 {
	       /*Loop for each byte in the destination row*/
	    for(d=0;d<bytesInRow;)
	    {
	       /*If the compression buffer is full, read more data from*/
	       /*the disk*/
	       if(curPos >= availBytes)
	       {
		  ReadChunkBytes(iff,src,bufSize);
		  curPos=0;
	       }

		  /*Note: n = the value in the current byte.  [n]+1 = the*/
		  /*value in the next byte*/

		  /*If 128>n>=0, copy the next n+1 bytes literally*/

	       if(src[curPos]>=0 && src[curPos]<128)
	       {
		  repl=src[curPos++]+1;

		  /*If there aren't n+1 bytes in the buffer, transfer what */
		  /*is there to the bitmap, then refill the buffer*/
		  if(curPos+repl > bufSize)
		  {
		     repl-=(bufSize-curPos);

		     CopyMem(&src[curPos],&dest[d],bufSize-curPos);
		     d+=(bufSize-curPos);
		     ReadChunkBytes(iff,src,bufSize);
		     curPos=0;
		  }
		  CopyMem(&src[curPos],&dest[d],repl);
		  curPos+=repl;
		  d+=repl;
	       }
	       else if(src[curPos] < 0)   /*If n < 0, the next -n+1 bytes*/
	       {			  /*are to be set equal to [n]+1*/
		  repl=-src[curPos++]+1+d;
		  if(curPos >= availBytes)
		  {
		     ReadChunkBytes(iff,src,bufSize);
		     curPos=0;
		  }
		  toRepl=src[curPos++];
		  for(;d<repl;d++)
		     dest[d]=toRepl;
	       }
	       else	      /*Otherwise (n==128), just skip that byte*/
		  curPos++;
	    }
	 }
	 else  /*Otherwise, the source buffer isn't compressed, so just*/
	 {     /*copy the bytes*/

	    repl=bytesInRow;
	    d=0;

	    /*Again, if the buffer doesn't contain all the data we need,*/
	    /*transfer what's there and then refill the buffer*/
	    if(curPos+repl > bufSize)
	    {
	       repl-=(bufSize-curPos);
	       CopyMem(&src[curPos],&dest[d],bufSize-curPos);
	       d+=(bufSize-curPos);
	       ReadChunkBytes(iff,src,bufSize);
	       curPos=0;
	    }
	    CopyMem(&src[curPos],&dest[d],repl);

	    curPos+=repl;
	 }
      }
   return;
}

/*Make a newScreen structure using the BMHD chunk*/
void getBMHD(struct BitMapHeader *bmhd)
{
   if(bmhd->w > 400 && bmhd->w <=704)  /*Define the screen as hires*/
      newScreen.ViewModes|=HIRES;      /*If wider than 400 pixels*/

   if(bmhd->h > 300 && bmhd->h <=512)  /*Define the screen as interlaced*/
      newScreen.ViewModes|=LACE;       /*if the height is > 300 pixels*/

   newScreen.Width=bmhd->w;	       /*Store the rest of the values*/
   newScreen.Height=bmhd->h;

   newScreen.LeftEdge=bmhd->x;
   newScreen.TopEdge=bmhd->y;
   newScreen.Depth=bmhd->nplanes;

   masking=(bmhd->Masking == 1);

   Compression=bmhd->Compression;   /*Compression flag.  Store for*/
				    /*later use*/
   return;
}

/*Data structures for ReadArgs()*/
struct RDArgs ra=
{
   {NULL,0,0},
   NULL,
   buf,
   512,
   "FILE/A/M,SECS=SECONDS/K/N,TICKS/K/N,LOOP/S,FROM/K,PRINT/S"
};

/*Parse the argument list, using ReadArgs()*/
void ParseArgs(ULONG *args)
{
   ReadArgs("FILE/A/M,SECS=SECONDS/K/N,TICKS/K/N,LOOP/S,FROM/K,PRINT/S",
	    args,&ra);
   return;
}

/*Check to see which mouse buttons have been pressed*/
ButtonTypes checkButton(void)
{
   struct IntuiMessage *mesg;
   ButtonTypes Button=none;
   static justActivated=FALSE;

      /*This function disregards a select (left) mouse button click*/
      /*if the window's just been activated.  This is so that a user*/
      /*can click on another window, then make this one active again,*/
      /*without advancing to the next picture*/

	 /*While there are messages to be read...*/
   while((mesg=(struct IntuiMessage *)GetMsg(prevWindow->UserPort))!=NULL)
   {
	 /*Interpret them*/
      switch(mesg->Class)
      {
	 case ACTIVEWINDOW:   /*Set the appropriate flag if the window*/
	    justActivated=TRUE;  /*was just activated*/
	    break;
	 case VANILLAKEY:
	    switch(mesg->Code)
	    {
	       case 16:       /*CTRL-P - Print (if this picture hasn't been*/
		  if(print)   /*printed;  this is designed in case the user*/
		  {	      /*holds down CTRL-P: we don't want 10-20     */
			      /*print requests to get queued up 	   */
		     dumpRastPort(&(prevScreen->RastPort),
				  &(prevScreen->ViewPort));
		     print=FALSE;
		  }
		  break;
	       case 4:	      /*CTRL-D - Abort everything*/
		  Button=menu;
		  break;
	       case 3:
		  Button=select; /*CTRL-C - Advance to next picture*/
		  break;
	    }
	    break;
	 case MOUSEBUTTONS:   /*Interpret a button click*/
	    if(mesg->Code==SELECTDOWN) /*If the left button was pushed,*/
	       if(justActivated)       /*and not so as to activate the*/
	       {		       /*window, advance to the next*/
		  justActivated=FALSE;	     /*screen*/
		  break;
	       }
	       else
		  Button=select;
	    else if(mesg->Code == MENUDOWN)  /*If the right button was*/
	       Button=menu;		     /*pushed, we'll want to*/
	    break;			     /*abort*/
      }
      ReplyMsg((struct Message *)mesg);      /*Reply to the message*/
   }
   return(Button);                           /*Return the results*/
}

/*This prints an error to the console, if we were run from the CLI*/
/*This is done instead of using Output() so as to get around any redirection*/
/*that may be in place (just like the standard C stderr)*/
/*If we can't open a StdErr or 2View was run from Workbench, a requester */
/*is put up*/
void printError(char *error1,char *error2)
{
   if(StdErr==NULL)
      StdErr=Open("CONSOLE:",MODE_OLDFILE);

   /* If we can't open StdErr, or Output()==NULL (meaning we're running */
   /* Workbench), put up a requester */
   if(StdErr==NULL || Output()==NULL)
   {
      if(error2==NULL || error2[0]==NULL)
	 EasyRequest(NULL,&erError1Line,NULL,error1,"Exiting...");
      else
	 EasyRequest(NULL,&erError2Line,NULL,error1,error2,"Exiting...");
   }
   else
   {
      FPuts(StdErr,error1);
      FPuts(StdErr,error2);
      FPuts(StdErr,"\nExiting\n");
   }
   return;
}

/*Free allocated resources in anticipation of quitting*/
void cleanup()
{
      /*Close the ARexx port*/
   dnRexxPort();

      /*Close the standard-error file if opened*/
   if(StdErr!=NULL)
      Close(StdErr);

      /*Close a previous screen and window, if open*/
   if(prevWindow!=NULL)
      CloseWindow(prevWindow);
   if(prevScreen!=NULL)
      CloseScreen(prevScreen);

      /*Close a current screen and window, if open*/
   if(window!=NULL)
      CloseWindow(window);
   if(screen!=NULL)
      CloseScreen(screen);

   if(iff!=NULL)
      FreeIFF(iff);

   if(pL!=NULL)
      Close(pL);

   if(IFFParseBase!=NULL)
      CloseLibrary(IFFParseBase);

   if(IntuitionBase!=NULL)
      CloseLibrary(IntuitionBase);

   if(GfxBase!=NULL)
      CloseLibrary(GfxBase);
}

/*Print the specified RastPort (whose ViewPort is pointed to by vp*/
BOOL dumpRastPort(struct RastPort *rp,struct ViewPort *vp)
{
   struct IODRPReq *printerMsg;
   struct MsgPort *printerPort;
   static BOOL ableToPrint=TRUE;

   if(ableToPrint)
   {
      ableToPrint=FALSE;
      printerPort=CreatePort("2View.print.port",0);
      if(printerPort!=NULL)
      {
	 printerMsg=(struct IORequest *)CreateExtIO(printerPort,
				       (long)sizeof(struct IODRPReq));

	 if(printerMsg != NULL)
	 {
	    /*Open the printer device*/
	    if(OpenDevice("printer.device",0,printerMsg,0)==0)
	    {
	       /*Set up the IODRPReq structure*/
	       printerMsg->io_Command=PRD_DUMPRPORT;
	       printerMsg->io_RastPort=rp;
	       printerMsg->io_ColorMap=vp->ColorMap;
	       printerMsg->io_Modes=vp->Modes;
	       printerMsg->io_SrcX=0;
	       printerMsg->io_SrcY=0;
	       printerMsg->io_SrcWidth=vp->DWidth;
	       printerMsg->io_SrcHeight=vp->DHeight;
	       printerMsg->io_Special=SPECIAL_ASPECT|SPECIAL_FULLROWS|
				      SPECIAL_FULLCOLS;

	       /*Do it*/
	       if(DoIO(printerMsg)==0)
		  ableToPrint=TRUE;

	       CloseDevice(printerMsg);
	    }
	    DeleteExtIO(printerMsg);
	 }
	 DeletePort(printerPort);
      }
      return(ableToPrint);
   }
}

/*End of 2View.c*/

