/*
 * Backdrop (C) Copyright Eddy Carroll 1989, may be Freely Redistributed
 *
 * Backdrop allows you to define a pattern which will then be displayed
 * in the empty screen area behind all the windows (who said Macintosh? :-)
 *
 * Usage: Backdrop { -a# | -b# | -f | -p# | -q | -s | B1B2B3B4B5B6B7B8}
 *
 * If no parameters are given, the default is a half tone grey pattern.
 *
 * -a or -b followed by a number sets the foreground or background colour
 * to the corresponding pen number (0 - 3)
 *
 * -f and -s select either a SMART_REFRESH or SIMPLE_REFRESH window to
 * display the backdrop pattern in. The former gives fast screen updates,
 * but uses up quite a bit of memory, whereas the latter uses up very
 * little memory but is slower at updating the screen.
 *
 * -p followed by a number selects the built in pattern corresponding to that
 * number. If you try and select a pattern number not available, you get
 * the default.
 *
 * -q kills the background copy of Backdrop currently installed, if any.
 *
 * A list of 16 hex digits will be interpreted as a user defined pattern,
 * which should be viewed as an 8x8 grid.
 *
 * The first time you run Backdrop, it installs itself in memory. Future
 * invocations of Backdrop will merely tell this first copy about any
 * changes in parameters, until the -q option is used to remove it. Note
 * that all the parameters, including window type, can be changed even
 * while Backdrop is currently running.
 *
 * Compiles under Lattice C V4.0
 *
 */

#include <exec/types.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <string.h>

#define tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) + 'a' - 'A' : (c))
#define YES		1
#define NO		0
#define NUMPATS	19		/* Number of predefined patterns available		*/
#define REPEAT  9999	/* Special return value indicates reopen window */
#define UNSET	-1		/* Indicates a parameter is currently unset		*/

#define htoi(c) (((c) >= 'A' ? (c) - 7 : (c)) & 0x0F)	/* Hex to int */

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
ULONG BackGroundIO = 0;

char usage1[] = "\
Backdrop (C) Copyright Eddy Carroll 1989, adds backdrop to Workbench screen\n",
usage2[] = "\
Usage: Backdrop {-f | -p# | -q | -s | B1B2B3B4B5B6B7B8}\n\
\n\
		-a# - Set foreground colour to specified pen #\n\
		-b# - Set background colour to specified pen #\n",
usage3[] = "\
		-f  - Enable fast update (uses more memory)\n\
		-p# - Use specified pattern # (1-5)\n\
		-q  - Remove backdrop from memory\n\
		-s  - Enable slow update\n\
   B1B2B3B4B5B6B7B8 - 16 hex digits defining an 8x8 pattern\n\
\n> ";
char quit1[] = "Backdrop removed.\n";
char quit2[] = "Backdrop not installed.\n";

char Portname[] = "Backdrop";

struct Pattern {
	UWORD value[8];
} Patterns[] = {
	{ 0x5555,0xAAAA,0x5555,0xAAAA,0x5555,0xAAAA,0x5555,0xAAAA }, /* Check */
	{ 0xFFFF,0x4040,0x4040,0x4040,0xFFFF,0x0404,0x0404,0x0404 }, /* Brick */
	{ 0x8888,0x0000,0x0000,0x0000,0x8888,0x0000,0x0000,0x0000 },
	{ 0x8888,0x0000,0x2222,0x0000,0x8888,0x0000,0x2222,0x0000 },
	{ 0xAAAA,0x0000,0xAAAA,0x0000,0xAAAA,0x0000,0xAAAA,0x0000 },
	{ 0xAAAA,0x0000,0x5555,0x0000,0xAAAA,0x0000,0x5555,0x0000 },
	{ 0x8888,0x2222,0x8888,0x2222,0x8888,0x2222,0x8888,0x2222 },
	{ 0xAAAA,0xAAAA,0x5555,0x5555,0xAAAA,0xAAAA,0x5555,0x5555 },
	{ 0xCCCC,0x3333,0xCCCC,0x3333,0xCCCC,0x3333,0xCCCC,0x3333 },
	{ 0xFFFF,0x8181,0xBDBD,0xA5A5,0xA5A5,0xBDBD,0x8181,0xFFFF },
	{ 0xFEFE,0x8282,0xBABA,0xAAAA,0xBABA,0x8282,0xFEFE,0x0000 },
	{ 0x9999,0xCCCC,0x6666,0x3333,0x9999,0xCCCC,0x6666,0x3333 },
	{ 0x9999,0x3333,0x6666,0xCCCC,0x9999,0x3333,0x6666,0xCCCC },
	{ 0x0000,0x4444,0x2828,0x1010,0x2828,0x4444,0x0000,0x0000 },
	{ 0x0000,0x5454,0x0202,0x5858,0x1A1A,0x4040,0x2A2A,0x0000 },
	{ 0xAAAA,0x4444,0xAAAA,0x0000,0xAAAA,0x4444,0xAAAA,0x0000 },
	{ 0xC3C3,0x6666,0x3C3C,0x1818,0x3C3C,0x6666,0xC3C3,0x8181 },
	{ 0xDBDB,0x6666,0x3C3C,0x9999,0x9999,0x3C3C,0x6666,0xDBDB },
	{ 0x6666,0xF0F0,0x9999,0x0F0F,0x6666,0xF0F0,0x9999,0x0F0F },
	{ 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF }
};

struct MyMsgPort {
	struct MsgPort mp;				/* A genuine message port	*/
	struct Pattern pat;				/* Current pattern			*/
	ULONG  windowflags;				/* Flags for our window		*/
	UBYTE  foreground,background;	/* Colours for window		*/
} port;

typedef struct MessagePort	MP;
typedef struct MyMsgPort	MyMP;
typedef struct IntuiMessage	IM;

#define WINDOWFLAGS	(BORDERLESS | BACKDROP)

struct NewWindow nw = {
	0,0,
	320,200,	/* Max dimensions will be filled in at run time */
	0,1,		/* These colours are also filled in at run time */
	REFRESHWINDOW,
	NULL,	/* Window flags will be filled in at run time */
	NULL, NULL,
	NULL,
	NULL, NULL,
	0,0,0,0,
	WBENCHSCREEN
};

/*
 * The start of the program. All main does is interpret the command line
 * parameters, and then determine whether there is already a copy of Backdrop
 * running or not. If there isn't, a public message port is set up for
 * future reference, else the other copy of Backdrop is informed of the
 * changes to the parameters.
 *
 */

void main(argc,argv)
int argc;
char *argv[];
{
	int quit = NO, error = NO;
	UWORD n,i,j;
	MyMP *myport;
	struct Task *FindTask(), *othertask;
	char *p;
	int doback();
	void resloop();
	BPTR stdout;

	stdout = Output();

	/*   Check to see if we are already running. If we are, initialise
	 *   contents of our local port structure with the current contents,
	 *   else initialise them to default values.
	 */

	if ((myport = (MyMP *)FindPort(Portname)) == NULL) {
		/* --- First time being run --- */
		port.foreground = 1;					 /* Default pen colour     */
		port.background = 0;					 /* Default background col */
		port.windowflags = SMART_REFRESH | WINDOWFLAGS;	 /* Window type    */
		port.pat = Patterns[0];					 /* Default pattern        */
		port.mp.mp_Node.ln_Name = Portname;      /* Set the port name      */
		port.mp.mp_SigBit = AllocSignal(-1L);	 /* Get signal # for port  */
	} else {
		/* --- Already running a copy --- */
		port = *myport;	/* Get copy of current contents */
	}

	/* Now parse command line, updating parameters as necessary */

	while (argc > 1) {
		if (argv[1][0] == '-') {
			n = atoi(&argv[1][2]);	/* Get possible second parameter */
			switch (tolower(argv[1][1])) {
				case 'a':
					port.foreground = n;
					break;
				case 'b':
					port.background = n;
					break;
				case 'p':
					if (n < 0 || n > NUMPATS-1)
						n = 0;
					port.pat = Patterns[n];
					break;
				case 'q':
					quit = YES;
					break;
				case 'f':
					port.windowflags = WINDOWFLAGS | SMART_REFRESH;
					break;
				case 's':
					port.windowflags = WINDOWFLAGS | SIMPLE_REFRESH;
					break;
				default:
					error = YES;
					break;
			}
		} else if ((strlen(p = argv[1])) == 16) {
			/* Convert 16 digit hex value into pattern */
			for (i = 0; i < 8; i++) {
				for (n = 0, j = 0; j < 2; j++,p++) {
					n = n<<4 | htoi(*p);
				}
				port.pat.value[i] = n<<8 | n;
			}
		} else
			error = YES;

		if (error) {
			Write(stdout,usage1,sizeof(usage1));
			Write(stdout,usage2,sizeof(usage2));
			Write(stdout,usage3,sizeof(usage3));
			exit(10);
		}
		argv++;
		argc--;
	}

	/*   Now local copy of port contains an up-to-date copy of the
	 *   current settings
	 */

	if (myport == NULL) {
		/* --- First time being run --- */
		if (quit) {
			Write(stdout,quit2,sizeof(quit2));
			exit(10);	/* No work to do if backdrop not active already */
		}
		Write(stdout,usage1,sizeof(usage1));
		AddPort(&port);							 /* Make our port public  */
		/* Detach this process from CLI, and setup process ID in port	*/
		if (!res("Backdrop",4,resloop,4000))
			RemPort(&port);
		exit(0);
	} else {
		othertask = myport->mp.mp_SigTask;

		/* If quit, tell the other copy to quit */
		if (quit) {
			Write(stdout,quit1,sizeof(quit1));
			Signal(othertask, SIGBREAKF_CTRL_C);
			exit(0);
		}

		/* Else we have a new pattern to install */
		Forbid();	/* Stop other copy trying to access the array */
		*myport = port; /* Copy updated parameters back to global store */
		Permit();
		Signal(othertask, SIGBREAKF_CTRL_F); /* Force screen update */
		exit(0);
	}
}

/*
 * This is the main loop. It just keeps looping around, until it is told
 * to exit (via a special return value from doback()). When it returns,
 * it actually is returning to AmigaDOS() which will then remove it from
 * the system.
 *
 */
void resloop()
{
	port.mp.mp_SigTask = FindTask(0L);	/* Set pointer to our task */
	while (doback() == REPEAT)		 /* Do intuition stuff	  */
		;
	/* We got some kind of exit code, so clean up */
	RemPort(&port);	/* Kill our public port  */
}

/*
 * This routine is where all the actual work gets done. A backdrop window
 * is opened on the workbench screen, and filled with the current pattern.
 * Then it waits for messages from Intuition saying the window needs to be
 * refreshed, and for signals from other invocations of Backdrop. A CTRL-C
 * signal causes Backdrop to remove itself, and CTRL-F forces the screen
 * to be updated. The return value is an error code, in case some resources
 * couldn't be allocated (0 or 10).
 *
 */

#define OLIB(a,b) ((a = (struct a *)OpenLibrary(b,33L)) == NULL)

int doback()
{
	struct Window *win;
	struct RastPort *rp;
	struct Screen *scr;
	IM *msg;
	int err = 10, xmax, ymax,;
	ULONG signal,lock;

	if (OLIB(IntuitionBase,"intuition.library"))
		goto err1;

	if (OLIB(GfxBase,"graphics.library"))
		goto err2;

	/* Now find the maximum size for our window by peeking at the dimensions
	 * of the Workbench screen. If no workbench screen is open, we exit.
	 */
	lock = LockIBase(0L);
	for (scr = IntuitionBase->FirstScreen; scr; scr = scr->NextScreen) {
		if ((scr->Flags & SCREENTYPE) == WBENCHSCREEN) {
			xmax = scr->Width;
			ymax = scr->Height;
			break;
		}
	}
	UnlockIBase(lock);

	if (scr == NULL)
		goto err2;	/* If couldn't find workbench screen, we fail */

	nw.Width  = xmax;
	nw.Height = ymax;
	nw.Flags  = port.windowflags;

	if ((win = OpenWindow(&nw)) == NULL)
		goto err3;

	err = 0;	/* Everything opened ok, so indicate no error */
	rp = win->RPort;
	SetDrMd(rp,JAM2);
	SetAfPt(rp,&port.pat.value[0],3);	/* Set area pattern */


#define IDCMPMASK (1<<win->UserPort->mp_SigBit)
#define CTRL_C (SIGBREAKF_CTRL_C)
#define CTRL_F (SIGBREAKF_CTRL_F)

	while (1) {
		SetAPen(rp,port.foreground);
		SetBPen(rp,port.background);
		RectFill(rp,0,0,xmax,ymax);
		signal = Wait(CTRL_C | CTRL_F | IDCMPMASK);

		/* If we got a CTRL_C, break out of this loop */
		if (signal & CTRL_C)
			break;

		/* Else was CTRL_F or a refresh event from Intuition */

		if (signal & IDCMPMASK) {
			/* Flush Intuition messages */
			while ((msg = (IM *)GetMsg(win->UserPort)) != NULL)
				ReplyMsg(msg);
		}

		/* Now check if user wants a different sort of window type */

		if (nw.Flags != port.windowflags) {
			err = REPEAT;	/* Indicate we want to reopen window */
			break;
		}
		/* Else loop back to start, and force redraw of window */
	}

	/* We get down to here if a CTRL_C is received */

	err4:
		CloseWindow(win);

	err3:
		CloseLibrary(GfxBase);

	err2:
		CloseLibrary(IntuitionBase);

	err1:
		return(err);
}

/*
 *   These definitions just stop the corresponding library routines getting
 *   pulled in, which helps keep the code size down.
 */

int brk(){return(0);}
void MemCleanup(){}
void chkabort(){}
