/*---------------------------------------------
 * TwoTasks.c
 *
 * DUAL TASK Demo in 16-bit Manx C   
 *
 * Brian Jackson (B.J. on GEnie)
 *
 * This is just a toy that I wrote to learn how to spawn a child
 * task from a parent.  This simply creates a second task that opens
 * a window and draws lines in that window (just like the parent task).
 * In fact, it uses the same NewWindow structure for both tasks.  There
 * is some sharing of global variables.  You can turn on/off the rendering
 * of the graphics in the child task with Function keys 1 & 2.  This is
 * accomplished via message passing (as is the notification that it is
 * time to shut down.)  Note that you cannot use any AmigaDOS calls in the
 * child (ie., Delay()) so the timing of things gets a bit tricky.
 *
 * I found that the main hassle is the debugging of the child.
 * Since a spawned task cannot use DOS calls (printf(), puts(), etc.), 
 * it's hard to find out why things crashed.  
 *
 * Like I said, this was just my own personal toy but, now that it (appears)
 * to work ok, I thought y'all might find it useful as an example.
 *
 * The header files listed in the program may not be complete.  I use the
 * Manx pre-compiled headers so the compiler automatically finds ANY thing
 * it might need regardless of what I have listed as header files. You may 
 * need to add some.
 *
 * This has been coded to run from the CLI. What with the numerous calls
 * to puts() some minor adjustments would be needed for WB use.
 * 
 *  compile with:    cc TwoTasks.c
 *  link with:       ln TwoTasks.o -lc
 *
 *   Nov 21, 1988
 *--------------------------------------------------
 */

 #include <stdio.h>
 #include <intuition/intuition.h>
 #include <functions.h>
 #include <exec/types.h>
 #include <exec/errors.h>
 #include <exec/memory.h>
 #include <devices/timer.h>
 #include <devices/audio.h>
 #include <libraries/dos.h>
 #include <graphics/rastport.h>


 #define  RN          33L              /* the current library revision # */
 #define  ACTION_START_GRAPHICS  1L
 #define  ACTION_STOP_GRAPHICS   2L
 #define  ACTION_CLEANUP         3L
 #define  FKEY_1                 0x50  /* IDCMP Rawkey 'code' values */
 #define  FKEY_2                 0x51

 struct IntuitionBase *IntuitionBase = NULL ;
 struct GfxBase       *GfxBase       = NULL ;
 struct Window        *MW            = NULL ;
 struct IntuiMessage  *message       = NULL ;
 struct Task          *secondtask    = NULL ;
 struct RastPort      *rp            = NULL ; 
 struct RastPort      *rp2           = NULL ;

 VOID   TaskTwo() ;
 LONG   errorval ;

 struct MyMessage {
     struct Message msg ;
     LONG Action ;
     } ; 

 struct MyMessage *T2Msg ;

 struct NewWindow newwindow = {
     0,0,
     320,199,
     0,1,
     CLOSEWINDOW| RAWKEY,
     WINDOWDEPTH | WINDOWDRAG
      | SMART_REFRESH | ACTIVATE 
      | NOCAREREFRESH | WINDOWCLOSE,
     NULL,
     NULL,
     (UBYTE *)" PARENT ",
     NULL,
     NULL,
     320,199,
     320,199,
     WBENCHSCREEN
     };

 struct MsgPort *replyport, *task2port ;
 
 VOID _cli_parse() {}               /* make stubs of these */
 VOID _wb_parse() {}

 main()
 {

 ULONG class;
 USHORT code ;
 LONG left = 20L, right = 300L, ypos = 20L, pen = 1L ;

   errorval = 0L ;

   IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",RN) ;
   if( ! IntuitionBase )
       GetOut ("Error:  No Intuition");

   GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",RN) ;
   if( ! GfxBase )
       GetOut ("Error:  No Graphics.lib");

   T2Msg = (struct MyMessage *)AllocMem((LONG)sizeof(*T2Msg), MEMF_PUBLIC) ;
   if( ! T2Msg )  
       GetOut("Couldn't alloc memory for T2Msg" ) ;

   replyport = (struct MsgPort *)CreatePort( 0L , 0L ) ;
   if( ! replyport ) 
       GetOut( "Error: no reply port" ) ;

   MW = (struct Window *)OpenWindow(&newwindow) ;
   if( ! MW )
       GetOut ("Error:  No Window");

   rp = MW->RPort ;

   secondtask = (struct Task *)CreateTask( "tasktwo", 0L, TaskTwo, 2000L) ;
   if( ! secondtask ) 
       GetOut( "Can't spawn child" ) ;

   Delay( 20L ) ;

   task2port = (struct MsgPort *)FindPort( "childport" ) ;
   if( !task2port )  
       GetOut( "can't find child port" ) ;
       
   task2port->mp_Node.ln_Type   = NT_MSGPORT ;
   task2port->mp_Flags          = PA_IGNORE ;
   T2Msg -> msg.mn_Node.ln_Type = NT_MESSAGE ;
   T2Msg -> msg.mn_Length       = (LONG)sizeof( struct MyMessage ) ;
   T2Msg -> msg.mn_ReplyPort    = replyport ;
 
   SetAPen( rp, 1L ) ;
   Move( rp, 40L, 194L ) ;
   Text( rp, "F1 Stops Child Graphics", 23L ) ;

   FOREVER  {

       if( errorval ) {
           DeleteTask( secondtask ) ;
           Delay( 30L ) ;
           GetOut( "Error: child task didn't start up properly" ) ;
           }

       while(message = (struct IntuiMessage *)GetMsg(MW->UserPort)) {
           class = message->Class;
           code  = message->Code ;
           ReplyMsg(message);

           if (class == CLOSEWINDOW)  {
               T2Msg->Action = ACTION_CLEANUP ;
               PutMsg( task2port, T2Msg ) ;
               WaitPort( replyport ) ;
               Delay( 20L ) ;         /* wait for exec to kill child */
               GetOut(NULL);
               }
           else if ( class == RAWKEY ) {
               if( code == FKEY_1 )
                   T2Msg->Action = ACTION_STOP_GRAPHICS ;
               else if( code == FKEY_2 )
                   T2Msg->Action = ACTION_START_GRAPHICS ;
               else 
                   break ;
               PutMsg( task2port, T2Msg ) ;
               WaitPort( replyport ) ;
               }
           }

       if( ++ypos > 180L)  {
           ypos = 20L ;
           if( ++pen > 3L ) 
               pen = 1L ;
           SetAPen( rp, pen ) ;
           }
       Move( rp, left, ypos ) ;
       Draw( rp, right, ypos++ ) ;
       Delay( 1L ) ;                 /* can't use this in the child! */

       }                                /* end forever           */
  }                                     /* end main              */



 GetOut( lastword )
 TEXT *lastword ;             /* Close things in REVERSE order!! */
 {
     if(MW)               CloseWindow(MW);
     if( replyport )      DeletePort( replyport ) ;
     if( T2Msg )          FreeMem( T2Msg, (LONG)sizeof( *T2Msg ) ) ;
     if(GfxBase)          CloseLibrary(GfxBase);
     if(IntuitionBase)    CloseLibrary(IntuitionBase);
     if( lastword )       puts(lastword) ;
     exit(0) ;
 }      /* end function */

 UBYTE blockaddress[20] ;
 UBYTE *title ;

 /* --------------------------------------------------------
  * This is the child task.  It's mostly just like any other
  * stand alone program except that you have to be careful not
  * to use any calls to AmigaDOS functions.  (Trust me! :-))
  *
  * The variable 'errorval' is a globals one that both tasks
  * know about.  It's the way I let the parent know if problems
  * occurred during the spawning process.  If an error occurs,
  * the child sets this variable to non-null and then goes into
  * an infinite Wait(), waiting for the parent to Delete the task.
  *
  * I use the same NewWindow structure except that I kill the
  * CloseWindow and Activate flags as well as adjust the location
  * of it's opening.
  *
  * One other thing to note.  Because the child is always busy doing
  * something we just loop and check for messages as we work. If, on
  * the other hand, the child was to be one that was waiting for a
  * message before it did a single act and then waited again, I would
  * replace the FOREVER with a WaitPort() and do "the act" when a 
  * message arrived.
  *
  * Also note that before I call DeleteTask() I call Forbid() but never
  * call Permit().  The DeleteTask() function deals with that for you.
  *-----------------------------------------------------------
  */


 VOID
 TaskTwo()
 {

 LONG left = 20L, right = 300L, ypos = 40L, pen = 1L ;
 struct MsgPort *port ;
 struct Window *window ;
 struct IntuiMessage *mess ;
 struct MyMessage *MyMess ;
 LONG   what_to_do, x ;
 BOOL   do_graphics = TRUE ;

  geta4 () ;   /* Manx mandatory for use of small code/data ! */

  port = (struct MsgPort *)CreatePort( "childport", 0L ) ;
  if( ! port ) {
      errorval == 1L ;
      Wait( 0L ) ;
      }

  newwindow.LeftEdge = 315 ;   /* fiddle with the window defs */
  newwindow.Title = (UBYTE *)" CHILD " ;
  newwindow.Flags &= ~(WINDOWCLOSE|ACTIVATE) ;

  window = (struct Window *)OpenWindow(&newwindow) ;
  if( ! window ) {
      errorval = 2L  ;
      DeletePort( port ) ;
      Wait( 0L ) ;
      }

  rp2 = window->RPort ;

  SetAPen( rp2, 1L ) ;
  Move( rp2, 40L, 194L ) ;
  Text( rp2, "F2 Starts Child Graphics", 24L ) ;


  FOREVER  {
      while( MyMess = (struct MyMessage *)GetMsg( port )) {
          what_to_do = MyMess->Action ;
          ReplyMsg( MyMess ) ;
          if( what_to_do == ACTION_CLEANUP) {
              CloseWindow( window ) ;
                                      /* Forbid() will prevent any new  */
                                      /* messages from being posted to  */
                                      /* the port until we can close it */
              Forbid() ;           
                                      /* Make sure to empty the port    */
              while( MyMess = (struct MyMessage *)GetMsg( port ) ) {
                  Remove( MyMess ) ;
                  ReplyMsg( MyMess ) ;
                  }
              DeletePort( port ) ;
              DeleteTask((struct Task *)FindTask(0L)) ;
              }
          else if( what_to_do == ACTION_START_GRAPHICS )
              do_graphics = TRUE ;
          else if( what_to_do == ACTION_STOP_GRAPHICS )
              do_graphics = FALSE ;
          }                           /* end while MyMess     */
          
      if( do_graphics ) {
          if( ++ypos > 180L)  {
              ypos = 20L ;
              if( ++pen > 3L ) 
                  pen = 1L ;
              SetAPen( rp2, pen ) ;
              }
          Move( rp2, left, ypos ) ;
          Draw( rp2, right, ypos++ ) ;
          }
      }                                /* end forever           */
 }
