/******************************************************************************
*									      *
*  BReq: opens a window on a custom screen with two gadgets for a startup     *
*	 option selection. Originally intended for use with NetBSD-Amiga.     *
*									      *
* Copyright (c)1994 by Eric R. Augustine (voodoo@well.sf.ca.us)               *
*									      *
* This is Copyrighted FreeWare so you may do with it as you wish as long as   *
* any distribution contains all original sources and documentation and that   *
* distribution is not done for the purpose of profitable gains.  The author   *
* accepts no responsibility for  any damage as	a result of the use of this   *
* program nor  does he make any claims or garantees as to what this program   *
* can do.								      *
*									      *
******************************************************************************/

#include <graphics/gfxbase.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <stdlib.h>
#include <string.h>

#define TRUE  1
#define FALSE 0

#define PALHEIGHT 512
#define PALGADTOP  56

/*  these two for action based on user response or timeout
 */
#define SYSA 1
#define SYSB 2

/*  yes I know there's already a type BOOL
 */
typedef int bool;

/*  library bases needed for closing the libraries at cleanup
 */
struct IntuitionBase *IntuitionBase;
extern struct  GfxBase *GfxBase;
struct Library *DosBase;

/*  build blank screen pointer
 */
USHORT chip BlankPoint[4] = {
   0x0000, 0x0000,
   0x0000, 0x0000
};

/*  create gadget box:	makes a beveled box 96 pixels wide by
 *  32 pixels high.
 */
SHORT BoxPoints[] = {
    0,	0,
   96,	0,
   96, 32,
    0, 32,
    0,	0,
    4,	4,
   92,	4,
   96,	0,
   92,	4,
   92, 28,
   96, 32,
   92, 28,
    4, 28,
    0, 32,
    4, 28,
    4,	4
};

/*  make gadget borders from boxes
 */
struct Border BRBorder = {
   0,		    /* LeftEdge   */
   0,		    /* TopEdge	  */
   1,		    /* FrontPen   */
   0,		    /* BackPen	  */
   JAM1,	    /* DrawMode   */
   16,		    /* Count	  */
   BoxPoints,	    /* XY	  */
   NULL,	    /* NextBorder */
};

/*  build left gadget
 */
UBYTE BRLeftString[] = "A";

struct IntuiText BRLeftText = {
   1,		    /* FrontPen  */
   0,		    /* BackPen	 */
   JAM1,	    /* DrawMode  */
   8,		    /* LeftEdge  */
   12,		    /* TopEdge	 */
   NULL,	    /* ITextFont */
   BRLeftString,    /* IText	 */
   NULL 	    /* NextText  */
};

struct Gadget BRLeftGad = {
   NULL,	    /* NextGadget    */
   50,		    /* LeftEdge      */
   157, 	    /* TopEdge	     */
   96,		    /* Width	     */
   32,		    /* Height	     */
   GADGHCOMP,	    /* Flags	     */
   GADGIMMEDIATE|   /* Activation    */
   RELVERIFY,
   BOOLGADGET,	    /* GadgetType    */
   (APTR)&BRBorder, /* GadgetRender  */
   NULL,	    /* SelectRender  */
   &BRLeftText,     /* GadgetText    */
   NULL,	    /* MutualExclude */
   NULL,	    /* SpecialInfo   */
   0,		    /* GadgetID      */
   NULL 	    /* UserData      */
};

/*  build comment string for left button
 */
UBYTE BRLeftComment[] = " ";

struct IntuiText BRLeftCommentText = {
   1,		    /* FrontPen  */
   0,		    /* BackPen	 */
   JAM1,	    /* DrawMode  */
   8,		    /* LeftEdge  */
   12,		    /* TopEdge	 */
   NULL,	    /* ITextFont */
   BRLeftComment,   /* IText	 */
   NULL 	    /* NextText  */
};

/*  build right gadget
 */
UBYTE BRRightString[] = "B";

struct IntuiText BRRightText = {
   1,		    /* FrontPen  */
   0,		    /* BackPen	 */
   JAM1,	    /* DrawMode  */
   8,		    /* LeftEdge  */
   12,		    /* TopEdge.  */
   NULL,	    /* ITextFont */
   BRRightString,   /* IText	 */
   NULL 	    /* NextText  */
};

struct Gadget BRRightGad = {
   &BRLeftGad,	    /* NextGadget */
   50,		    /* LeftEdge   */
   210, 	    /* TopEdge	  */
   96,		    /* Width	  */
   32,		    /* Height	  */
   GADGHCOMP,	    /* Flags	  */
   GADGIMMEDIATE|   /* Activation */
   RELVERIFY,
   BOOLGADGET,	    /* GadgetType    */
   (APTR)&BRBorder, /* GadgetRender  */
   NULL,	    /* SelectRender  */
   &BRRightText,    /* GadgetText    */
   NULL,	    /* MutualExclude */
   NULL,	    /* SpecialInfo   */
   0,		    /* GadgetID      */
   NULL 	    /* UserData      */
};

/*  build comment string for right button
 */
UBYTE BRRightComment[] = " ";

struct IntuiText BRRightCommentText = {
   1,		    /* FrontPen  */
   0,		    /* BackPen	 */
   JAM1,	    /* DrawMode  */
   8,		    /* LeftEdge  */
   12,		    /* TopEdge	 */
   NULL,	    /* ITextFont */
   BRRightComment,  /* IText	 */
   NULL 	    /* NextText  */
};

/*  BReq's window
 */
struct Window *BRWin;

/*  Vanilla required new window
 */
struct NewWindow NWin = {
   0,		       /* LeftEdge    */
   0,		       /* TopEdge     */
   640, 	       /* Width       */
   400, 	       /* Height      */
   0,		       /* DetailPen   */
   1,		       /* BlockPen    */
   CLOSEWINDOW|        /* IDCMPFlags  */
   INTUITICKS|
   GADGETDOWN|
   VANILLAKEY|
   MOUSEBUTTONS|
   MOUSEMOVE,
   WFLG_SMART_REFRESH| /* Flags       */
   WFLG_BORDERLESS|
   RMBTRAP|
   REPORTMOUSE|
   ACTIVATE,
   &BRRightGad,        /* FirstGadget */
   NULL,	       /* CheckMark   */
   NULL,	       /* Title       */
   NULL,	       /* Screen      */
   NULL,	       /* BitMap      */
   0,		       /* MinWidth    */
   0,		       /* MinHeight   */
   640, 	       /* MaxWidth    */
   400, 	       /* MaxHeight   */
   CUSTOMSCREEN        /* Type	      */
};

/*  BReq's custom screen
 */
struct Screen *BScreen;

/*  new screen structure - could use OpenScreenTags()
 *  but, I want to account for PAL modes as well.
 */
struct NewScreen NScreen = {
   0,		       /* LeftEdge     */
   0,		       /* TopEdge      */
   640, 	       /* Width        */
   400, 	       /* Height       */
   4,		       /* Depth        */
   0,		       /* DetailPen    */
   1,		       /* BlockPen     */
   HIRES|LACE,	       /* ViewModes    */
   CUSTOMSCREEN,       /* Type	       */
   NULL,	       /* Font	       */
   NULL,	       /* DefaultTitle */
   NULL,	       /* Gadgets      */
   NULL 	       /* CustomBitMap */
};

/*  readargs structure for user preferences
 */
struct internal_args {
   char  *timeout;	     /* idle time timeout	   */
   char  *blank;	     /* idle time blank screen	   */
   const char *defresponse;  /* select default for timeout */
   char  *buttons;	     /* title individual buttons   */
   bool  reverse;	     /* reverse colors		   */
   char  *atext;	     /* itext to left button	   */
   char  *btext;	     /* itext to right button	   */
   char  *sysa; 	     /* system call string top	   */
   char  *sysb; 	     /* system call string bottom  */
};

/*  avoid larger stdio code by using this very simple printing routine
 *  using just dos library routines and string.
 */
void error(char *errstr) {
   Write(Output(),errstr,strlen(errstr));
}

main(int argc, char *argv[]) {
   APTR   address;		    /* IDCMP response address for gadgets      */
   bool   user_response = FALSE;    /* whether or not user has made a response */
   bool   BLANKDONE = FALSE;	    /* blanker currently in process	       */
   char   *buttonstr;		    /* modifiable button titles for user       */
   int	  action;		    /* action to take once BReq closes	       */
   int	  hmode = 0;		    /* height of comments based on display     */
   ULONG  class;		    /* IDCMP message Class		       */
   ULONG  seconds;		    /* Seconds counted by IDCMP 	       */
   ULONG  startsecs;		    /* seconds at start of BReq 	       */
   ULONG  blanksecs;		    /* seconds to blank 		       */
   ULONG  blank_start;		    /* same as startsecs until blank	       */
   ULONG  user_secs;		    /* modifiable timeout value 	       */
   USHORT code; 		    /* IDCMP keycodes via VANILLAKEY	       */
   struct IntuiMessage *BRMessage;  /* BReq's IDCMP Message port               */
   void   CleanUp(void);            /* close screen and libraries              */

/*  init command line args
 */
   struct internal_args ia = { "31536000",                        /* timeout,     one year       */
			       "180",                             /* blank,       three minutes  */
			       "A",                               /* defresponse, bottom gadget  */
			       "NetBSD|AmigaOS",                  /* buttons,     titles         */
			       FALSE,				  /* reverse,	  reverse	 */
			       " ",                               /* atext,       top comment    */
			       " ",                               /* btext,       bottom comment */
			       "sys:loadbsd sys:vmunix -a",       /* sysa,        load NetBSD    */
			       NULL };				  /* sysb,	  no task	 */

/*  open libraries
 */
   if(!(DosBase = (struct Library *)OpenLibrary("dos.library",0))) {
      error("BReq: can't open dos.library\n");
      exit(1);
   }
   if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0))) {
      error("BReq: can't open intuition.library\n");
      exit(1);
   }
   if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0))) {
      error("BReq: can't open graphics.library\n");
      exit(1);
   }

/*  get user preferences:  use AmigaOS RDArgs rather than C standard.
 */
   (struct RDArgs *)ReadArgs("TIMEOUT/K,BLANK/K,DEFAULT/K,BUTTONS/K,REVERSE/S,ATEXT/K,BTEXT/K,SYSA/K,SYSB/K", (long *)&ia, NULL);

/*  get current system seconds from intuition
 */
   CurrentTime(&startsecs,NULL);

/*  copy starting seconds for blanker
 */
   blank_start = startsecs;


/*  check for user specified default response: compare the character (string)
 *  from the command line with the acceptable values.  if failure then use
 *  internal default value.
 */
   if((strcmp(ia.defresponse,"A") == 0) || (strcmp(ia.defresponse,"a") == 0))
      action = SYSA;
   else if((strcmp(ia.defresponse,"B") == 0) || (strcmp(ia.defresponse,"b") == 0))
      action = SYSB;
   else {
      error("BReq: invalid DEFAULT \"");
      error((char *)ia.defresponse);
      error("\", using \"A\".\n");
      action = SYSA;
   }

/*  get timeout value: for some reason the intuitive way of doing this direct
 *  from the command line didn't work so using atoi() (clib) to do the job.
 */
   user_secs  = atoi(ia.timeout);
   blanksecs  = atoi(ia.blank);

/*  make gadget titles.  use routines from clib (defined in string.h) to get
 *  individual gadgets from one string.  (using "|" for compatibility to 1.12)
 */
   if(strstr(ia.buttons,"|") == NULL)
      error("BReq: \"|\" separater missing, using default gadgets.\n");
   else {
      BRLeftText.IText	= strtok(ia.buttons,"|");
      buttonstr 	= strchr(ia.buttons,'|');
      BRRightText.IText = strtok(buttonstr,NULL);
   }

/*  make gadget comments
 */
   BRLeftCommentText.IText  = ia.atext;
   BRRightCommentText.IText = ia.btext;

/*  check for PAL screen mode and set screen, window and
 *  gadgets accordingly.
 */
   if(GfxBase->DisplayFlags & PAL) {
      NScreen.Height	 = PALHEIGHT;
      NWin.Height	 = PALHEIGHT;
      BRLeftGad.TopEdge  = PALGADTOP + 157;
      BRRightGad.TopEdge = PALGADTOP + 210;
      hmode = PALGADTOP;
   }

/*  do gui:  open the screen then the window on which the gadgets are drawn.
 *  use IDCMP to count passing seconds and get user response, if any.  set
 *  action value based on user response or execute default on timeout.
 */
  if(!(BScreen = OpenScreen(&NScreen))) {
     error("BReq: can't open screen\n");
     exit(2);
  }

/*  if user wants reverse screen just switch bits.  for white, 11 was used
 *  instead of 15 (brightest) since 15 was too bright.
 */
   if(ia.reverse) {
      SetRGB4(&BScreen->ViewPort,1,11,11,11);
      SetRGB4(&BScreen->ViewPort,0,0,0,0);
   }
   else {
      SetRGB4(&BScreen->ViewPort,1,0,0,0);
      SetRGB4(&BScreen->ViewPort,0,8,9,9);
   }

/*  set the window's screen to the CUSTOMSCREEN
 */
   NWin.Screen = BScreen;

/*  open BReq's window
 */
   if(!(BRWin = (struct Window *)OpenWindow(&NWin))) {
      error("BReq: can't open window\n");
      CloseLibrary((struct Library *)IntuitionBase);
      CloseLibrary((struct Library *)DosBase);
      CloseLibrary((struct Library *)GfxBase);
      CloseScreen(BScreen);
      exit(3);
   }

/*  write button comments to window
 */
   PrintIText(BRWin->RPort,&BRLeftCommentText,150,hmode + 157);
   PrintIText(BRWin->RPort,&BRRightCommentText,150,hmode + 210);

/*  event loop:  Wait() for message signals and take action based on Class
 *  of IDCMP message.
 */
   while(!(user_response)) {                     /* wait for response or timeout */
      Wait( 1 << BRWin->UserPort->mp_SigBit );   /* listen for IDCMP messages    */
      while(BRMessage = (struct IntuiMessage *)GetMsg(BRWin->UserPort)) {
      if(BRMessage) {
	 class	 = BRMessage->Class;		 /* IDCMP response flag        */
	 address = BRMessage->IAddress; 	 /* response address	       */
	 seconds = BRMessage->Seconds;		 /* keep track of passing time */
	 code	 = BRMessage->Code;		 /* key code here	       */
	 ReplyMsg((struct Message *)BRMessage);  /* don't keep Intui waiting   */
	 switch(class) {                         /* what sort of response?     */
	    case INTUITICKS:			 /* timer blank and timeout    */
	       if(((int)(seconds - startsecs)) >= user_secs) {
		  user_response = TRUE; 	 /* user response not before timeout */
	       }
	       else if((((int)(seconds - blank_start)) >= blanksecs)   /* compare to blank delay  */
			&& (BLANKDONE == FALSE)) {                     /* and if screen not blank */
		  SetRGB4(&BScreen->ViewPort,1,0,0,0);                 /* blank background */
		  SetRGB4(&BScreen->ViewPort,0,0,0,0);                 /* blank text       */
		  SetPointer(BRWin,BlankPoint,0,0,0,0);                /* blank pointer    */
		  BLANKDONE = TRUE;				       /* screen is blank  */
	       }
	       break;
	    case GADGETDOWN:			 /* user pressed gadget */
	       user_response = TRUE;
	       if(address == (APTR)&BRLeftGad)   /* get which gadget    */
		 action = SYSA;
	       else
		 action = SYSB;
	       break;
	    case MOUSEMOVE: case MOUSEBUTTONS:	 /* user used mouse */
	       if(BLANKDONE) {                   /* if screen blank then unblank */
		  if(ia.reverse)
		     SetRGB4(&BScreen->ViewPort,1,11,11,11);
		  else {
		     SetRGB4(&BScreen->ViewPort,1,0,0,0);
		     SetRGB4(&BScreen->ViewPort,0,8,9,9);
		  }
		  ClearPointer(BRWin);           /* and bring back pointer */
	       }
	       BLANKDONE = FALSE;		 /* screen no longer blank */
	       blank_start = (int)seconds;       /* reset blanker timer */
	       break;
	    case VANILLAKEY:			 /* user hit keys */
	       if((code == 'v') || (code == 'y')) {
		  user_response = TRUE; 	 /* user wants left gadget */
		  action = SYSA;
	       }
	       else if((code == 'b') || (code == 'n')) {
		  user_response = TRUE; 	 /* user wants right gadget */
		  action = SYSB;
	       }
	       else if(code == 13)               /* ASCII RETURN */
		  user_response = TRUE; 	 /* user wants default */
	       else if(code == 27) {             /* ASCII ESCape */
		  user_response = TRUE; 	 /* user wants opposite to default */
		  if(action == SYSB)
		     action = SYSA;
		  else
		     action = SYSB;
	       }
	       if(BLANKDONE) {                   /* if blank, unblank */
		  if(ia.reverse) {
		     SetRGB4(&BScreen->ViewPort,1,11,11,11);
		  }
		  else {
		     SetRGB4(&BScreen->ViewPort,1,0,0,0);
		     SetRGB4(&BScreen->ViewPort,0,8,9,9);
		  }
		  ClearPointer(BRWin);           /* and bring back pointer */
	       }
	       BLANKDONE = FALSE;		 /* screen should no longer be blank */
	       blank_start = (int)seconds;       /* reset blanker timer */
	       break;
	    }
	 }
      }
   }

/*  take action either on user gadget selection or timeout default
 */
   switch(action) {
      case 1:
	 CleanUp();
	 system(ia.sysa);
	 break;
      case 2:
	 if(ia.sysb == NULL)
	    CleanUp();
	 else {
	    CleanUp();
	    system(ia.sysb);
	 }
	 break;
   }
}

/*  clean up and return to calling environment
 */
void CleanUp(void) {
      CloseWindow(BRWin);
      CloseScreen(BScreen);
      CloseLibrary((struct Library *)IntuitionBase);
      CloseLibrary((struct Library *)DosBase);
      CloseLibrary((struct Library *)GfxBase);
      return;
}

