
/* This program is a CW toy for sending morse code using two
 * windows. 
 *
 * Speech Toy was written by David M Lucas and that is what
 * Rob Frohne started with to create CWToy.   Thanks also to Rob Peck for his
 * audiotools2.c without which this would probably never have
 * been completed.  
 * If you find somthing you don't like, fix it! Have fun! 
 * Thanks Amiga. */

#include "cwtoy.h"

/* Revision number is used on calls to OpenLibrary
 * to let the system know what revision level the
 * program was written for. Usually refers to the
 * ROM release number or the release number of a
 * disk based library.
 */
 
#define Enable_Abort 1
#define REVISION 1

#define CONTWINDW 283   /* Overall Control Window Width/Height */
#define CONTWINDH 100

/* Pen numbers to draw gadget borders/images/text with */
#define REDP   3        /* color in register 3 once was red */
#define BLKP   2        /* color in register 2 was black */
#define WHTP   1        /* color in register 1 was white */
#define BLUP   0        /* color in register 0 was blue */
/* the length of the English and phonetic buffers */

#define NUMPROPS 4      /* number of proportional gadgets */

#define SPEEDSIZE 20     /* the number of digits in the speed */
#define StartChannel(c) ControlChannel(c, CMD_START)
#define StopChannel(c) ControlChannel(c, CMD_STOP)

/* Ranges of proportional data */
#define MAXWEIGHT  2000
#define MINWEIGHT  500
#define MAXSPEED   99
#define MINSPEED   3
#define MAXPITCH1  2000
#define MINPITCH1  100
#define RNGWEIGHT  (MAXWEIGHT - MINWEIGHT)  +1
#define RNGSPEED   (MAXSPEED - MINSPEED)  +1
#define RNGPITCH1  (MAXPITCH1 - MINPITCH1) +1
#define RNGVOL     (MAXVOL   - MINVOL)   +1
#define NORMSPACING  1000
/*#define DEFWEIGHT  1000 */
/*#define DEFSPEED   20 */
/*#define DEFPITCH1  440 */
#define CIRQSIZE   4096

struct TextAttr TestFont = /* Needed for opening screen */
   {
   (STRPTR)"topaz.font",
   TOPAZ_EIGHTY, 0, 0
};

struct Code {
	int speed;
	int pitch;
	int volume;
	int weight;
        int spacing;
};

struct Code cw = {
	20, 440, 64, 1000, 1000 
};

USHORT circleq[CIRQSIZE]; /* This is the circlular Q buffer that will
		        * allow us to edit the buffer before it is
		        * sent if we desire. */

/* Initialize the circular Q pointers. */
int inptr = 0, outptr = 0;

int ctrls = 0;

/* String Gadgets *********************************************
 * First the string gadgets.
 * 1) because the Phonetic string is refreshed programaticly
 * (that is, deleted and added again) quite often, and doing
 * this requires the use of RefreshGadgets(), and this causes
 * gadgets that are closer to the beginning of the list than
 * the gadget given to RefreshGadgets() to flicker.
 * 2) because they don't flicker when OTHER gadgets
 * (ie female/male, coming up) are deleted and added.
 *
 * These'll be used to put a nice double line border around
 * each of the two string gadgets.
 *
 * y,x pairs drawn as a connected line. Be sure to have an even
 * number of arguments (ie complete pairs).
 */

SHORT StrVectors[] = {
   0, 0,   160, 0,   160, 13,   0, 13,
   0, 1,   159, 1,   159, 12,   1, 12,
   1, 1
};
struct Border StrBorder = {
   -4, -3,           /* initial offsets, gadget relative */
   WHTP, BLUP, JAM1, /* pens (fore, back) and drawmode */
   9,                /* number of vectors */
   StrVectors,     /* pointer to the actual array of vectors */
   NULL       /* no next Border, can point to another border */
};

/* The same undo buffer is used for both string gadgets,
 * this is sized to largest so that largest fits.
 */
UBYTE UndoBuffer[SPEEDSIZE];

/* Speed Gadget is where the speed will be displayed. */
/* default text */
UBYTE Speed[SPEEDSIZE] = "20 Words per Minute";
UBYTE Space[2] = " ";
struct StringInfo EnglInfo = {
   Speed,         /* pointer to I/O buffer */
   UndoBuffer,    /* pointer to undo buffer */
   0,             /* buffer position */
   SPEEDSIZE,     /* max number of chars, including NULL */
   0, 0,          /* first char in display, undo positions */
   20,             /* number of chars (currently) in the buffer */
   0, 0, 0,       /* position variables calculated by Intuition */
   NULL,          /* no pointer to RastPort */
   0,             /* not a LongInt string gadget */
   NULL           /* no pointer to alternate keymap */
};
struct IntuiText EnglText = {
   WHTP, BLUP,    /* FrontPen, BackPen */
   JAM1,          /* DrawMode */
   0, -13,        /* LeftEdge, TopEdge (relative to gadget) */
   &TestFont,     /* pointer to TextFont */
   (UBYTE *) "Speed:",      /* pointer to Text */
   NULL           /* no pointer to NextText */
};
struct Gadget SpeedGadget = {
   NULL,             /* pointer to Next Gadget */
   11, 63, 290, 10,  /* (Left Top Width Height) Hit Box */
   GADGHCOMP,        /* Flags */
   RELVERIFY,        /* Activation flags */
   STRGADGET,        /* Type */
   (APTR)&StrBorder, /* pointer to Border Image */
   NULL,             /* no pointer to SelectRender */
   &EnglText,        /* pointer to GadgetText */
   0,                /* no MutualExclude */
   (APTR)&EnglInfo,  /* pointer to SpecialInfo */
   0,                /* no ID */
   NULL              /* no pointer to special data */
};


/* Now the proportional gadgets. */
/* Proportional Gadgets **************************************/
/* The following variables are used to create proportional
 * Gadgets. These variables will be filled in with copies of
 * the generic Gadgetry below.
 */
SHORT PropCount = 0;       /* index to next available Gadget */
struct IntuiText PTexts[NUMPROPS];/* get copies of TPropText */
/* dummy AUTOKNOB Images are required */
struct Image PImages[NUMPROPS];
/* These get copies of TPropInfo */
struct PropInfo PInfos[NUMPROPS];
/* These get copies of TPropGadget */
struct Gadget Props[NUMPROPS];
struct IntuiText TPropText = {
   WHTP, BLUP,   /* FrontPen, BackPen */
   JAM1,         /* DrawMode */
   0, -10,       /* LeftEdge, TopEdge (relative to gadget) */
   &TestFont,    /* pointer to TextFont */
   NULL,         /* pointer to Text is filled at runtime */
   NULL          /* no pointer to NextText */
};   
struct PropInfo TPropInfo = {
   AUTOKNOB | FREEHORIZ,  /* Flags */
   0, 0,            /* Pots:  Horiz, Vert: both start at 0 */
   0x1FFF, 0x1FFF,  /* Bodies: Horiz is 1/8, Vert is 1/8 */
   0, 0, 0, 0, 0, 0 /* System usage stuff */
};
/* this is the template for the Gadget of a horizontal */
/* Proportional Gadget */
struct Gadget TPropGadget = {
   &SpeedGadget,              /* pointer to NextGadget */
   7, 12, 115, 10,            /* Select Box L T W H */
   GADGHCOMP | GADGIMAGE,     /* Flags */
   GADGIMMEDIATE | RELVERIFY, /* Activation flags */
   PROPGADGET,                /* Type */
   NULL,       /* pointer to Image filled in later */
   NULL,       /* no pointer to SelectRender */
   NULL,       /* no pointer to GadgetText */
   0,          /* no MutualExclude */
   NULL,       /* SpecialInfo proportional data filled later */
   0,          /* no ID */
   NULL        /* no pointer to special data */
};

struct IntuitionBase *IntuitionBase = 0;
struct GfxBase *GfxBase = 0;

/* Only one menu. */

ULONG MenuNumber;
ULONG TheMenu;
ULONG TheItem;

struct IntuiText MenuItemText = {
   BLUP,         /* Front Pen */
   WHTP,         /* Back pen */
   JAM2,         /* Draw Mode */
   0,            /* Left Edge */
   0,            /* Top */
   &TestFont,    /* pointer to TextFont */
   (UBYTE *) "About CW Toy...", /* text */
   NULL          /* next */
};
struct MenuItem MyMenuItem = {
   NULL,                /* pointer to next item */
   0,                   /* left */
   0,                   /* top */
   150,                 /* width */
   8,                   /* height */
   ITEMTEXT | ITEMENABLED | HIGHCOMP,  /* flags */
   0,                   /* no mutual exclude */
   (APTR)&MenuItemText, /* Render */
   NULL,                /* pointer to alternate image */
   NULL,                /* Command "amiga" char */
   NULL,                /* Sub Item */
   MENUNULL             /* nextselect */
};

struct Menu MyMenu = {
   NULL,          /* pointer to next menu */
   0,0,150,0,     /* left,0,Width,0 */
   MENUENABLED,   /* flags */
   (BYTE *) "CW Toy Menu", /* menu name */
   &MyMenuItem    /* First Item in list */
};

struct IntuiText ReqText1 = {
   BLUP,         /* Front Pen */
   WHTP,         /* Back pen */
   JAM2,         /* Draw Mode */
   5,            /* Left Edge */
   23,           /* Top */
   &TestFont,    /* pointer to TextFont */
   (UBYTE *) "Version 1.0  30 June, 1991",  /* text */
   NULL          /* next */
};
struct IntuiText ReqText2 = {
   BLUP,         /* Front Pen */
   WHTP,         /* Back pen */
   JAM2,         /* Draw Mode */
   5,            /* Left Edge */
   13,           /* Top */
   &TestFont,    /* pointer to TextFont */
   (UBYTE *) "Freeware - Public Domain ", /* text */
   &ReqText1     /* next */
};
struct IntuiText ReqText3 = {
   BLUP,         /* Front Pen */
   WHTP,         /* Back pen */
   JAM2,         /* Draw Mode */
   5,            /* Left Edge */
   3,            /* Top */
   &TestFont,    /* pointer to TextFont */
   (UBYTE *) "By Rob Frohne (KL7NA)", /* text */
   &ReqText2     /* next */
};

struct IntuiText OKIText = {
   BLUP, WHTP, /* FrontPen, BackPen */
   JAM2,       /* DrawMode */
   6, 3,       /* LeftEdge, TopEdge (relative to gadget) */
   &TestFont,  /* pointer to TextFont */
   (UBYTE *) "OK",       /* pointer to Text */
   NULL        /* no pointer to NextText */
};

struct Requester *AboutRequester;

USHORT autoret;
struct Window *ControlWindow = NULL;
struct IntuiMessage *MyIntuiMessage;
struct NewWindow NewControlWindow = {
   00, 11,                    /* start LeftEdge, TopEdge */
   CONTWINDW, CONTWINDH,      /* start Width, Height */
   -1, -1,                    /* DetailPen, BlockPen */
   GADGETUP | CLOSEWINDOW | MENUPICK
   | VANILLAKEY,              /* IDCMP FLAGS */
   WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE
   | GIMMEZEROZERO | ACTIVATE,/* Flags (can be NULL) */
   &Props[NUMPROPS-1],        /* Pointer to FirstGadget */
   NULL,                      /* no pointer to first CheckMark */
   (UBYTE *) "CW Toy",                  /* Title (can be NULL) */
   NULL,                      /* no Pointer to Screen */
   NULL,                      /* no Pointer to BitMap */
   20, 20,                    /* Min/max  Sizable to (w/h) */
   CONTWINDW, CONTWINDH,      /* These aint used, can't size */
   WBENCHSCREEN         /* Type of screen window appears in */
};

struct NewWindow NewDisplayWindow = {
	00,(CONTWINDH + 10),
	CONTWINDW,50,
	-1,-1,
	MENUPICK|VANILLAKEY,              /* IDCMP FLAGS */
	GIMMEZEROZERO|ACTIVATE|WINDOWSIZING|WINDOWDEPTH|WINDOWDRAG|REFRESHWINDOW,
	0,
	NULL,
	(UBYTE *) "CW Buffer",
	NULL,
	NULL,
	10,10,700,440,
	WBENCHSCREEN };
    
/* Some audio stuff for Rob Peck's Audio Tools. */
   extern char *w1;
   extern int ControlChannel();
   extern struct MsgPort *InitAudio();
   struct MsgPort *myport;   /* CHANGE from article */
   LONG channel;

/* Some stuff for the Console.  */
   extern struct ConIOBlocks *CreateConsole();
   struct ConIOBlocks *cio;
   struct Window *DisplayWindow, *OpenWindow();



/** start of code ***************************/
main( argc, argv )
int argc;
char *argv[];
{
   ULONG Signals;                     /* Wait() tells me which to look at */
   ULONG MIClass;                     /* Save quickly, ReplyMsg() asap */
   USHORT MICode;
   APTR MIAddress;
   LONG error;
   int i,j,k,goaway = 0;
   char numspeed[SPEEDSIZE];
   FILE *fp, *fopen();   
   fp = 0; /* Initialize fp */
   
   /* check for a question mark as the first argument */
   if ( argv[1][0] == '?' ) {
       printf("Usage: %s [message] [-f filename] [-e] [-s speed] [-p pitch] [-v volume] [-w weighting]\n [-b spaceing]\n", argv[0] );
       exit(0);
   }   /* end of checking for a question mark */
   
   /* Parse the input line */
   for ( i=1; i < argc; i++ ) {

       /* see if it starts with a minus (then s,p,v,w,f) */
       if ( argv[i][0] != '-' ) {
           /* Get the message if it is there. */
           for ( j=0; argv[i][j] != '\0'; j++ )
              circleq[inptr++] = argv[i][j];
           circleq[inptr++] = 0x20;
       }
       else {
           /* Parse the switches. */
           switch( argv[i][1] ) {
               case 'f' : /* File to send. */
                   if ((fp = fopen(argv[++i],"r")) == NULL) {
		      printf("cwtoy: can't open %s\n",argv[++i]);
		      exit(1);
		  }
                   break;
               case 'e' : /* Exit after sending message of file. */
	           goaway = 1;
                   break;
               case 's' : /* Speed */
                   cw.speed = atoi(argv[++i]);
	           ftoa((double)cw.speed,numspeed,0,1);
	           if(cw.speed < 10) strcat(numspeed,Space);
   	           strcat(numspeed,(Speed + 2));
    	           strcpy(Speed,numspeed);
                   break;
               case 'p' : /* Pitch */
                   cw.pitch = atoi(argv[++i]);
                   break;
               case 'v' : /* Volume */
                   cw.volume = atoi(argv[++i]);
                   break;
               case 'w' : /* Weighting */
                   cw.weight = atoi(argv[++i]);
                   break;
               case 'b' : /* Spacingfactor */
                   cw.spacing = atoi(argv[++i]);
                   break;
               default :
                   printf("Usage: %s [message] [-f filename] [-e] [-s speed] [-p pitch] [-v volume] [-w weighting]\n [-b spaceing]\n", argv[0] );
           	  exit(0);
                   break;
           }   /* end of switch for the arguments */
       }   /* end of checking for a switch argument */        
   }
   
   /*  First initialize the audio routines using Rob Peck's Audio Tools. */
   myport = InitAudio();   /* now returns address of message port */
   if(myport == 0) 
   {
      printf("Problem in InitAudio!\n");
      MyCleanup();
   }
   {   
      channel = GetChannel(-1);
      if(channel == -1)
      {
         printf("cannot get a channel!\n");
         MyCleanup();
      }
      error = StopChannel(channel);
      if(error) 
      {  
         printf("error in stopping channel = %ld\n",error);
         MyCleanup();
      }
   }

   /* Open those libraries that the program uses directly */
   if ((IntuitionBase = (struct IntuitionBase *)
  	    OpenLibrary("intuition.library", REVISION)) == 0) {
      MyCleanup();
      exit(1);
   }

   if ((GfxBase = (struct GfxBase *)
	    OpenLibrary("graphics.library", REVISION)) == 0) {
      MyCleanup();
      exit(1);
   }

   /* This is where the proportional gadgets are set up, using
    * the templates that were declared staticly.
    */
   for(PropCount = 0; PropCount < NUMPROPS; PropCount++) {
      PTexts[PropCount] = TPropText;
      Props[PropCount] = TPropGadget;
      PInfos[PropCount] = TPropInfo;
      Props[PropCount].GadgetText = (struct IntuiText *)
     	  &PTexts[PropCount];
      Props[PropCount].GadgetRender = (APTR)
      	 &PImages[PropCount];
      Props[PropCount].SpecialInfo = (APTR)&PInfos[PropCount];
      switch (PropCount) {
      case 0:
         PTexts[PropCount].IText = (UBYTE *) "Weighting:";
         if (cw.weight == MAXWEIGHT)
            PInfos[PropCount].HorizPot = 65535;
         else
            PInfos[PropCount].HorizPot = ((cw.weight - MINWEIGHT)
             << 16) / (MAXWEIGHT - MINWEIGHT);
         break;
      case 1:
         PTexts[PropCount].IText = (UBYTE *) "Speed:";
         Props[PropCount].TopEdge += 22;
         Props[PropCount].NextGadget = &Props[PropCount-1];
         if (cw.speed == MAXSPEED )
            PInfos[PropCount].HorizPot = 65535;
         else
            PInfos[PropCount].HorizPot = ((cw.speed - MINSPEED)
             << 16) / (MAXSPEED - MINSPEED);
         break;
      case 2:
         PTexts[PropCount].IText = (UBYTE *) "Pitch:";
         Props[PropCount].LeftEdge += 145;
         Props[PropCount].NextGadget = &Props[PropCount-1];
         if (cw.pitch == MAXPITCH1)
            PInfos[PropCount].HorizPot = 65535;
         else
            PInfos[PropCount].HorizPot = ((cw.pitch - MINPITCH1)
             << 16) / (MAXPITCH1 - MINPITCH1);
         break;
      case 3:
         PTexts[PropCount].IText = (UBYTE *) "Volume:";
         Props[PropCount].TopEdge += 22;
         Props[PropCount].LeftEdge += 145;
         Props[PropCount].NextGadget = &Props[PropCount-1];
         if (cw.volume == MAXVOL)
            PInfos[PropCount].HorizPot = 65535;
         else
            PInfos[PropCount].HorizPot = ((cw.volume - MINVOL)
             << 16) / (MAXVOL - MINVOL);
         break;
      default:
         break;
      }
   }
   if ((ControlWindow = (struct Window *)OpenWindow
	    (&NewControlWindow)) == NULL) {
      MyCleanup();
      exit(1);
   }
   if ((DisplayWindow = OpenWindow(&NewDisplayWindow)) == NULL) {
      MyCleanup();
      exit(1);
   }
   if ((cio=CreateConsole(DisplayWindow)) == 0) {
      MyCleanup();
   }

   /* fill background of window */
   SetAPen(ControlWindow->RPort, BLKP);
   RectFill(ControlWindow->RPort,0L,0L,
	    (LONG)ControlWindow->GZZWidth, (LONG)ControlWindow->GZZHeight);
   RefreshGadgets(&Props[NUMPROPS-1],ControlWindow,NULL);

   SetMenuStrip(ControlWindow, &MyMenu);  
   SetMenuStrip(DisplayWindow, &MyMenu);  
   
   if (fp != 0) { /* If we opened a file to send, send it first! */
       filesend(fp);
       if (goaway == 1) {
          MyCleanup();
          exit(0);
       }
   }
   if (inptr > 0) send(); /* Got a -m message to start already! */
   
/* !!! Ah, But what if Control Window's not been opened? */
/* Rob changed some tricky stuff here! */
   for (;;) {  /* ever wait for a signal and process it */
      /* wait lets the rest of the system run, */
      /* this program sleeps */
      Signals = Wait((1 << myport->mp_SigBit) | 
	  (1 << DisplayWindow->UserPort->mp_SigBit) |
	  (1 << ControlWindow->UserPort->mp_SigBit));
      /* now check to see to what we owe the intrusion */

      Chk_Abort();
      if (Signals & (1<< myport->mp_SigBit)) {
         if(MayGetNote(myport,FALSE) == 1L) {
            ++outptr % CIRQSIZE; /* Always increment the outptr after we are
	 		     * done sending a character. */
   	    /* If not caught up and no pause in effect, send the next one. */
	    if (inptr != outptr) {
	       	if (ctrls == 0) send(); 
            }
            else {
	         if (goaway == 1) {
		     MyCleanup();
                      exit(0);
	         }
            }
         }
      }
       
      if (Signals & (1<< ControlWindow->UserPort->mp_SigBit)) {
         /* Process the Intuition message */
         while (MyIntuiMessage=(struct IntuiMessage *)
                      GetMsg(ControlWindow->UserPort)) {
            /* Get all the needed info and give message back */
            MIClass = MyIntuiMessage->Class;
            MICode = MyIntuiMessage->Code;
            MIAddress = MyIntuiMessage->IAddress;
            ReplyMsg(MyIntuiMessage);
            /* Now, what was it you wanted? */
            switch (MIClass) {
               case VANILLAKEY:
               	 /* Service the character buffer. */
		 switch(qUp(MICode)) {
			case 0:
                 			  /* If we are caught up
		 			   * initialize the sending. 
					   * (Remember that qUp
					   * incremented inptr.) */
		                  if((inptr % CIRQSIZE == (outptr + 1) % CIRQSIZE) 
					&& (ctrls == 0))
					send();
                                   break;
		 	case 1:
					  /* Check for Control C. */
				exit(0);
			case 2:
			case 3:
                          case 4:
				break;
		 }
                  break;
               case MENUPICK:
                  menumessage(MICode, ControlWindow);
                  break;
               case GADGETUP:         /* reply, then process */
                  gadgetmessage(MIAddress, ControlWindow);
                  break;
               case CLOSEWINDOW:       /* bye! */
                  while (MyIntuiMessage = (struct
                   IntuiMessage *) GetMsg(ControlWindow->UserPort))
                   ReplyMsg(MyIntuiMessage);
                  MyCleanup();
                  exit(0);
               default:
                  break;
            }  /* switch */
         } /* while */
      } /* if */
      if (Signals & (1<< DisplayWindow->UserPort->mp_SigBit)) {
         /* Process the Intuition message */
         while (MyIntuiMessage=(struct IntuiMessage *)
                      GetMsg(DisplayWindow->UserPort)) {
            /* Get all the needed info and give message back */
            MIClass = MyIntuiMessage->Class;
            MICode = MyIntuiMessage->Code;
            MIAddress = MyIntuiMessage->IAddress;
            ReplyMsg(MyIntuiMessage);
            /* Now, what was it you wanted? */
            switch (MIClass) {
               case VANILLAKEY:
               	 /* Service the character buffer. */
                  /* For comments see similar section under ControlWindow. */
		 switch(qUp(MICode)) {
			case 0:
		                  if((inptr % CIRQSIZE == (outptr + 1) % CIRQSIZE) 
					&& (ctrls == 0))
					send();
                                   break;
		 	case 1:
				exit(0);
			case 2:
			case 3:
                          case 4:
				break;
		 }
                  break;
               case MENUPICK:
                  menumessage(MICode, DisplayWindow);
                  break;
               default:
                  break;
            }  /* switch */
         } /* while */
      } /* if */
   }/* for */
}  /* main */

/* a MENUPICK has been received, this
 * routine takes the appropriate action
 */
 
menumessage(code, w)

USHORT code;
struct Window *w;
{
   switch (MENUNUM(code)) {
      case 0:
         switch (ITEMNUM(code)) {
            case 0:
               AutoRequest(w, &ReqText3, NULL,
                           &OKIText, 0, 0, 280, 47);
               break;
         }
      break;
   }
   return(0);
}

/* a GADGETUP has been received, this
 * routine takes the appropriate action
 */
gadgetmessage(address, w)
APTR address;
struct Window *w;
{
   long PropRange;
   UBYTE i;
   int length;
   char numspeed[SPEEDSIZE];

   if (address == (APTR)&Props[2]) {
      PropRange = RNGPITCH1;
      cw.pitch = (((PInfos[2].HorizPot >> 1)
       * PropRange) >> 15) + MINPITCH1;
   }
   else if (address == (APTR)&Props[3]) {
      PropRange = RNGVOL;
      cw.volume = (((PInfos[3].HorizPot >> 1)
       * PropRange) >> 15) + MINVOL;
  }

   else if (address == (APTR)&Props[0]) {
      PropRange = RNGWEIGHT;
      cw.weight = (( (PInfos[0].HorizPot >> 1)
       * PropRange) >> 15) + MINWEIGHT;
   }
   
   /* Since the program changes the contents of the string
    * gadgets' buffer and it's size, which is something else
    * intuition doesn't expect a program (as opposed to the
    * user) to do. The program must remove, then change, then
    * add this gadget, and then by passing the address of this
    * gadget to RefreshGadgets(), only the gadgets from here
    * to the start of the list will be refreshed, which
    * minimizes the visible flash that RefreshGadgets() can
    * introduce.
    */
   else if (address == (APTR)&Props[1]) {
      PropRange = RNGSPEED ;
      cw.speed = (((PInfos[1].HorizPot >> 1)
       * PropRange) >> 15) + MINSPEED;
      i = RemoveGadget(ControlWindow, &SpeedGadget);
/*      if((length = stci_d(numspeed, cw.speed)) < 4)
	{
         if(length < 3) strcat(numspeed,Space);
         strcat(numspeed,(Speed + 2));
         strcpy(Speed,numspeed);
        }  */ /* This was commented out to modify it to Manx. */
/* The 3 lines below were added to make it Manx compatible. */
      ftoa((double)cw.speed,numspeed,0,1);
      if(cw.speed < 10) strcat(numspeed,Space);
      strcat(numspeed,(Speed + 2));
      strcpy(Speed,numspeed);
/* The 3 lines above this were added to make it Manx compatible. */
      AddGadget(ControlWindow, &SpeedGadget, i);
      RefreshGadgets(&SpeedGadget, ControlWindow, NULL);
   }
   return(0);
}


/* Deallocate any memory, and close all of the
 * windows/screens/devices/libraries in reverse order to
 * make things work smoothly. And be sure to check
 * that the open/allocation was successful before
 * closing/deallocating.
 */
MyCleanup()
{
   FinishAudio(myport);   /* NEW parameter for FinishAudio */
   if (myport != 0) 
      DeletePort(myport);
   if (cio != 0)
      DeleteConsole(cio);
   if (DisplayWindow != NULL)
      CloseWindow(DisplayWindow);
   if (ControlWindow != NULL)
      CloseWindow(ControlWindow);
   /* freeimages makes sure image allocation was successful */
   if (GfxBase != 0)
      CloseLibrary(GfxBase);
   if (IntuitionBase != 0)
      CloseLibrary(IntuitionBase);
   return(0);
}

UBYTE Lookup[] = {0x01, 0x6, 0x11, 0x15, 0x09, 0x02, 0x14, 0x0b, 0x10, 0x04, 0x1e,
   0x0d, 0x12, 0x07, 0x05, 0x0f, 0x16, 0x1b, 0x0a, 0x08, 0x03, 0x0c, 0x18,
   0x0e, 0x19, 0x1d, 0x13, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
   0x01, 0x52, 0x01, 0xc8,
   0x01, 0x01, 0x5d, 0x2d, 0x6d, 0x22, 0x2a, 0x73, 0x61, 0x6a, 0x29, 0x3f, 0x3e, 0x3c, 
   0x38, 0x30, 0x20, 0x21, 0x23, 0x27, 0x2f, 0x47, 0x55, 0x01, 0x31, 0x01, 0x4c, 
   0x01, 0x06, 0x11, 0x15, 0x09, 0x02, 0x14, 0x0b, 0x10, 0x04, 0x1e,
   0x0d, 0x12, 0x07, 0x05, 0x0f, 0x16, 0x1b, 0x0a, 0x08, 0x03, 0x0c, 0x18,
   0x0e, 0x19, 0x1d, 0x13, 0x2d, 0x29, 0x6d, 0x01, 0xac, 0x01, 
   0x06, 0x11, 0x15, 0x09, 0x02, 0x14, 0x0b, 0x10, 0x04, 0x1e,
   0x0d, 0x12, 0x07, 0x05, 0x0f, 0x16, 0x1b, 0x0a, 0x08, 0x03, 0x0c, 0x18,
   0x0e, 0x19, 0x1d, 0x13, 0x2d, 0x29, 0x6d, 0x01, 0x01}; 

int qUp(letter)
/* qUp is the only place that inptr should ever change! */

USHORT letter;

{
	char buffer[1];
        /* Check for backspace and that we can backup. */
	if( (letter == 0x08) && (inptr % CIRQSIZE != (outptr + 1) % CIRQSIZE) 
			  && (inptr != outptr) 
			  && (inptr % CIRQSIZE != (outptr -1) % CIRQSIZE) ) {
		(--inptr) % CIRQSIZE;
		/*(--inptr) % CIRQSIZE;*/
		buffer[0] = (char)(letter & 0xff); /* Backspace in the 
						* DisplayWindow. */
   		if(letter != 0x9b) WriteConsole(cio,buffer,1);
		return(2);
	}
        
        /* Check for Control C.  If so, blow it away. */
	else if(letter == 0x03) {
		MyCleanup();
                 return(1);
	}
        
	/* Control S will pause the sending and control Q 
	 * will resume it where it left off. */
        /* Control S */
        else if(letter == 0x13) {
		ctrls = 1;
        	return(2);
        }
        /* Control Q */
        else if(letter == 0x11) {
		ctrls = 0; 
                 if(inptr != outptr) send();
        	return(2);
        }
        
        /* Check for Control X.  If so clear the circlular Q. */
        else if(letter == 0x18) {
                 /* How do we handle the wrap around? */
        	while (inptr % CIRQSIZE != (outptr + 1) % CIRQSIZE) {
                 	(--inptr) % CIRQSIZE; 
			buffer[0] = (char)(0x08 & 0xff);
   			if(letter != 0x9b) WriteConsole(cio,buffer,1);
                 }
                 return(2);
	}        
        
        /* Make the return key have no effect. */
        else if(letter == 0x0d) return(3);
	
        /* Main Circular Character Storage. */
        /* Make sure that the inptr doesn't catch up with the outptr *
         * If it doesn't stow it in the buffer and print it in the   *
	 * display window. */
	else if(((inptr + 1) % CIRQSIZE != outptr % CIRQSIZE) 
			&& (letter != 0x08)) {
        	circleq[inptr] = letter;
		buffer[0] = (char)(letter & 0xff);
   		if(letter != 0x9b) WriteConsole(cio,buffer,1);
                 (++inptr) % CIRQSIZE; /* increment the inptr so the *
				     * next character will go in  *
				     * the right place.	       */
		return(0);
        }
        
        /* If the circular Q is full... */
	else if((inptr + 1) % CIRQSIZE == outptr % CIRQSIZE) {
        	DisplayBeep(NULL);  /* Flash the display. */
		printf("Circular Q buffer is full!\n");
                 return(2);
	}
        
        /* The default (maybe the operator wanted to backspace but wasn't
	 * quick enough) or .... */
        else 
	/*(--inptr) % CIRQSIZE;*/
	return(3);
}
	
send()
/* Sends one letter at a time gotten from the user after the file has
 * been sent of one was. */

{
        long error;
	UBYTE inproc;   /* Pattern in process of being sent. */
    
        inproc = Lookup[circleq[outptr]];
        if(circleq[outptr] == 32) space();
        while((inproc) != 1){
		if(01 & inproc) dah();
		else dit();
		inproc >>= 1;
	}
        charspace();
        error = StartChannel(channel);
        if(error)
   	{
		printf("error starting channel = %ld\n",error);
		MyCleanup();
	}
	return(0);
}

dit()
{
    
	PlayFreq(channel,cw.pitch,w1,cw.volume,(LONG)(1200/cw.speed),0L,0L,0L);
	PlayFreq(channel,cw.pitch,w1,0L,(LONG)(1200/cw.speed),0L,0L,0L);
        return(0);
}

dah()
{
    
	PlayFreq(channel,cw.pitch,w1,cw.volume,
		(LONG)(3600*cw.weight/1000/ cw.speed),0L,0L,0L);
	PlayFreq(channel,cw.pitch,w1,0L,(LONG)(1200/cw.speed),0L,0L,0L);
        return(0);
}
    
space()
{
	PlayFreq(channel,cw.pitch,w1,0L,
		(LONG)(4800*cw.spacing/NORMSPACING/cw.speed),0L,0L,0L);
        return(0);
}	
charspace()
{
	PlayFreq(channel,(LONG)cw.pitch,w1,0L,
		(LONG)(2400*cw.spacing/NORMSPACING/cw.speed),0L,myport,1L);
        return(0);
}	



filesend(fp)

FILE *fp;

{
    ULONG Signals;                     /* Wait() tells me which to look at */
    ULONG MIClass;                     /* Save quickly, ReplyMsg() asap */
    USHORT MICode;
    APTR MIAddress;
    LONG error;
    int letter;
    UBYTE inproc;   /* Pattern in process of being sent. */
    
    while((letter = getc(fp)) != EOF) {
        Chk_Abort();
        inproc = Lookup[letter];
        if (letter == 0x0a) inproc = 0x01;
	if(letter == 32) space();
       	while((inproc) != 1){
		if(01 & inproc) dah();
		else dit();
		inproc >>= 1;
	}
        charspace();
        if (error = StartChannel(channel))
        {
     	   printf("error starting channel = %ld\n",error);
	   MyCleanup();
        }
nosend:
        /* Wait lets the rest of the system run,
         * while this program sleeps. */
        Signals = Wait((1 << myport->mp_SigBit) | 
  	  	(1 << DisplayWindow->UserPort->mp_SigBit) |
	  	(1 << ControlWindow->UserPort->mp_SigBit));
        /* now check to see to what we owe the intrusion */

        if (Signals & (1<< myport->mp_SigBit)) {
           MayGetNote(myport,FALSE);
           }
	     
         
        if (Signals & (1<< ControlWindow->UserPort->mp_SigBit)) {
           /* Process the Intuition message */
           while (MyIntuiMessage=(struct IntuiMessage *)
                      GetMsg(ControlWindow->UserPort)) {
              /* Get all the needed info and give message back */
              MIClass = MyIntuiMessage->Class;
              MICode = MyIntuiMessage->Code;
              MIAddress = MyIntuiMessage->IAddress;
              ReplyMsg(MyIntuiMessage);
              /* Now, what was it you wanted? */
              switch (MIClass) {
                 case VANILLAKEY: /* We are accepting no input for sending
				* from the Display Window. */
                    switch(MICode) {
		      case 0x03: /* Control C */
		         MyCleanup();
		         exit(0);
                       case 0x18: /* Control X */
		         goto finishfile;
		      case 0x13: /* Control S */
                          ctrls = 1;
                          break;
		      case 0x11: /* Control Q */
                          ctrls = 0;
                          break;
                       default:
		         break;
		   }
                    break;
                 case MENUPICK:
                    menumessage(MICode, ControlWindow);
                    break;
                 case GADGETUP:         /* reply, then process */
                    gadgetmessage(MIAddress, ControlWindow);
                    break;
                 case CLOSEWINDOW:       /* bye! */
                    while (MyIntuiMessage = (struct
                       IntuiMessage *) GetMsg(ControlWindow->UserPort))
                       ReplyMsg(MyIntuiMessage);
                    MyCleanup();
                    exit(0);
                    break;
                 default:
                    break;
              }  /* switch */
           } /* while */
        } /* if */
        if (Signals & (1<< DisplayWindow->UserPort->mp_SigBit)) {
           /* Process the Intuition message */
           while (MyIntuiMessage=(struct IntuiMessage *)
                      GetMsg(DisplayWindow->UserPort)) {
              /* Get all the needed info and give message back */
              MIClass = MyIntuiMessage->Class;
              MICode = MyIntuiMessage->Code;
              MIAddress = MyIntuiMessage->IAddress;
              ReplyMsg(MyIntuiMessage);
              /* Now, what was it you wanted? */
              switch (MIClass) {
                 case VANILLAKEY: /* We are accepting no input for sending
				* from the Display Window. */
                    switch(MICode) {
		      case 0x03: /* Control C */
		         MyCleanup();
		         exit(0);
                       case 0x18: /* Control X */
		         goto finishfile;
		      case 0x13: /* Control S */
                          ctrls = 1;
                          break;
		      case 0x11: /* Control Q */
                          ctrls = 0;
                          break;
                       default:
		         break;
		   }
                    break;
                 case MENUPICK:
                    menumessage(MICode, DisplayWindow);
                    break;
                 default:
                    break;
              }  /* switch */
           } /* while */
        } /* if */
        if (ctrls == 1) goto nosend;
    } /* while */
finishfile:
    fclose(fp);
    ctrls = 0;
    PlayFreq(channel,(LONG)cw.pitch,w1,0L,(LONG)(2400/cw.speed),0L,myport,2L);
    if (error = StartChannel(channel)) {
        printf("error starting channel = %ld\n",error);
        MyCleanup();
    }
    for(;;) { /* Wait until the entire file has been sent. */
        if(MayGetNote(myport,TRUE) == 2L) return(0);
    }
    return(0);
}

_abort()
{
   MyCleanup();
   exit(0);
}