/* rainbow.c : Marauder-style rainbow generator - (c) 1987 DJH

	As some would put it, this program is a "parlour trick" very
  similar to that used in the Marauder II disk copier. Quite a simple
  concept, actually; install a user copper list such that the background
  color (hardware color 0) is changed every few scan lines. I was even able
  to change the background color every WORD during testing! Try filling the
  screen with WorkBench color 1 and rendering text in color 0 for a Genlock-
  style effect; the rainbow shows through the letters themselves!

  P.S. This program compiled under Manx 3.4; no need to declare RKM includes
  if you condense them all into one big precompiled symbol table!

  P.P.S. Sorry for the lack of comments; but then again, this program was
  a fast hack. Should be highly readable anyway.
*/

void *IntuitionBase,*GfxBase;

#define NUMCOLORS 66

UWORD colors[NUMCOLORS] = {
  0xce3,0xae3,0x8e3,0x7e3,0x5e3,0x4e3,0x3e4,0x3e5,0x3e7,0x3e8,
  0x3ea,0x3eb,0x3ec,0x3ee,0x3de,0x3ce,0x3ae,0x39e,0x37e,0x34e,
  0x33e,0x43e,0x63e,0x73e,0x83e,0xa3e,0xb3e,0xc3e,0xe3e,0xe3d,
  0xe3b,0xe3a,0xe39,0xe37,0xe36,0xe34,0xe33,0xe53,0xe63,0xe83,
  0xe93,0xea3,0xeb3,0xec3,0xee3,0xde3,0xbe3,0x8e3,0x7e3,0x4e3,
  0x3e4,0x3e5,0x3e6,0x3e8,0x3e9,0x3ea,0x3ec,0x3ed,0x3ee,0x3de,
  0x3be,0x3ae,0x38e,0x37e,0x36e,0x34e
};

main(argc,argv)
int argc;
char *argv[];
{
  struct Window *window;
  struct ViewPort *vp;
  struct UCopList *ucop;

  void *dspins,*sprins,*clrins; /* intermediate Copper cache ptrs */

  struct NewWindow nw;
  UWORD bgpen=0,i,j=1;

  if (argc==2) bgpen=*argv[1]-'0';
  if (bgpen>3) { puts("Usage : rainbow [default color (0-3)]"); exit(0); }
       
  GfxBase=OpenLibrary("graphics.library",0);
  IntuitionBase=OpenLibrary("intuition.library",0);

  setmem(&nw,sizeof(nw),0);

  nw.Height=50; nw.Width=316;
  nw.DetailPen=nw.BlockPen=-1;
  nw.Flags=WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|SMART_REFRESH;
  nw.IDCMPFlags=CLOSEWINDOW;
  nw.Title=(UBYTE *)"Rainbow (c) 1987 John Hodgson";
  nw.Type=WBENCHSCREEN; /* 'cuz we want the WB Screen ptr */

  window=OpenWindow(&nw);
  vp=ViewPortAddress(window);

  /* look into CINIT for 1.2 or later */

  while (!GetMsg(window->UserPort)) {

  /* the goal is to assemble all the Copper lists and THEN update the
     ViewPort pointer so Intuition won't see any partially constructed
     lists. Note use of double-buffering; we don't want funny effects
     if user tries to drag the screen around! */

    ucop=AllocMem(sizeof(struct UCopList),MEMF_CHIP|MEMF_CLEAR);

    for (i=0;i<NUMCOLORS;i++) {
      CWAIT(ucop,i*(200/NUMCOLORS),0);
      CMOVE(ucop,custom.color[bgpen],colors[(i+j) % NUMCOLORS]);
    }
    CEND(ucop); j++;

    /* Since the Copper lists are dynamically created, we'll need to free
       'em up before creating new ones. It would be wasteful to free ALL
       the ViewPort lists, since we'll only have to remake 'em later. By
       limiting processing to the UCopList only, we can avoid a time-
       consuming MakeScreen() operation. */

    /* cache all the Copper lists we want kept */
    dspins=vp->DspIns; sprins=vp->SprIns; clrins=vp->ClrIns;

    Forbid(); /* don't let Intuition see our cleanup */

    vp->DspIns=vp->SprIns=vp->ClrIns=0; /* hide these from FVPCL() */
    FreeVPortCopLists(vp); /* free UCopIns from previous pass */

    /* cleanup done, uncover "hidden" copper lists */
    vp->DspIns=dspins; vp->SprIns=sprins; vp->ClrIns=clrins;

    vp->UCopIns=ucop; /* install new UCopIns */

    Permit();
    RethinkDisplay();
  }

  /* No need to optimize for cleanup; free ALL lists & recreate 'em */

  FreeVPortCopLists(vp); RemakeDisplay();

  CloseWindow(window);
  CloseLibrary(IntuitionBase);
  CloseLibrary(GfxBase);
}
