/**********************************************************************
 * Nano-GUI -- a Nano-X environment for Linux                         *
 *                          By Darran D. Rimron <darran@rimron.co.uk> *
 *                                                                    *
 * Based on Nano-Term, a Nano-X demo                                  *
 *                                         By Al Riddoch & Greg Haerr *
 *                                                                    *
 * ---------+--------+---------+------------------------------------- *
 * Date     | Author | Version | Reason                               *
 * ---------+--------+---------+------------------------------------- *
 * 19991112 | Rimron | 0.0.1   | Start Application                    *
 * 19991226 | Rimron | 0.0.2   | Add clock, improve terminal          *
 * ---------+--------+---------+------------------------------------- *
 * See NanoGUI.CHANGES and CHANGELOG for full and graphic change info *
 * ------------------------------------------------------------------ *
 *                                                                    *
 *      Best viewed with console-vim & a tabstop of 3 (:set ts=3)     *
 **********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include "nano-X.h"				/* The nano-X graphics server */
#include "nanogui.h"				/* Special routines used by nanoGUI */
#include "terminal.h"			/* For the main terminal window, of course */
#include "clock.h"				/* For the clock at the top of the GUI */

#define	HAVEBLIT 		0		/* set if have bitblit (experimental)*/

/* nanoGui Specific variables.... */
int		active_window;			/* which window should I plot into? */

int main(int argc, char ** argv)
{
	GR_BITMAP	text_cursor_fg[15];	/* mouse cursor when in Text window*/
	GR_BITMAP	text_cursor_bg[15];
	GR_BITMAP	clock_cursor_fg[7];  /* mouse cursor when in Clock window */
	GR_BITMAP	clock_cursor_bg[7];
	CLOCK_TYPE=DIGITAL;
	if (GrOpen() < 0) {
		fprintf(stderr, "nanoGui: cannot open graphics\n");
		exit(1);
	}
	
	GrGetScreenInfo(&si);

	GrSetErrorHandler(errorcatcher);

	main_window = GrNewWindow(GR_ROOT_WINDOW_ID, 1, 67, si.cols - 2,
		si.rows - 68, 1, MAIN_PAPER_COLOR, ACTIVE_BORDER_COLOR);

	clock_window = GrNewWindow(GR_ROOT_WINDOW_ID, 1, 1, 64,
		64, 1, CLOCK_PAPER_COLOR, INACTIVE_BORDER_COLOR);

	GrSelectEvents(clock_window, GR_EVENT_MASK_BUTTON_DOWN |
		GR_EVENT_MASK_KEY_DOWN | GR_EVENT_MASK_EXPOSURE |
		GR_EVENT_MASK_FOCUS_IN | GR_EVENT_MASK_FOCUS_OUT);

	GrSelectEvents(main_window, GR_EVENT_MASK_BUTTON_DOWN |
		GR_EVENT_MASK_KEY_DOWN | GR_EVENT_MASK_EXPOSURE |
		GR_EVENT_MASK_FOCUS_IN | GR_EVENT_MASK_FOCUS_OUT);

	GrMapWindow(clock_window);
	GrMapWindow(main_window);

	gc1 = GrNewGC();
	gc2 = GrNewGC();
	gc3 = GrNewGC();

	GrSetGCForeground(gc1, SCREEN_PEN_COLOR);
	GrSetGCBackground(gc1, SCREEN_PAPER_COLOR);
	GrSetGCForeground(gc2, CLOCK_PEN_COLOR);
	GrSetGCBackground(gc2, CLOCK_PAPER_COLOR);
	GrSetGCFont(gc2, FONT_OEM_FIXED);
	GrSetGCForeground(gc3, MAIN_PEN_COLOR);
	GrSetGCBackground(gc3, MAIN_PAPER_COLOR);
	GrSetGCFont(gc3, FONT_OEM_FIXED);

	text_cursor_fg[0]  = MASK(_,X,X,_,X,X,_);
	text_cursor_fg[1]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[2]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[3]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[4]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[5]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[6]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[7]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[8]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[9]  = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[10] = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[11] = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[12] = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[13] = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[14] = MASK(_,_,_,X,_,_,_);
	text_cursor_fg[15] = MASK(_,X,X,_,X,X,_);

	text_cursor_bg[0]  = MASK(X,X,X,X,X,X,X);
	text_cursor_bg[1]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[2]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[3]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[4]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[5]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[6]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[7]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[8]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[9]  = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[10] = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[11] = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[12] = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[13] = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[14] = MASK(_,_,X,X,X,_,_);
	text_cursor_bg[15] = MASK(X,X,X,X,X,X,X);

	clock_cursor_fg[0] = MASK(_,_,_,X,_,_,_);
	clock_cursor_fg[1] = MASK(_,X,_,X,_,X,_);
	clock_cursor_fg[2] = MASK(_,_,_,X,_,_,_);
	clock_cursor_fg[3] = MASK(X,_,_,X,X,_,X);
	clock_cursor_fg[4] = MASK(_,_,_,_,_,_,_);
	clock_cursor_fg[5] = MASK(_,X,_,_,_,X,_);
	clock_cursor_fg[6] = MASK(_,_,_,X,_,_,_);

	clock_cursor_bg[0] = MASK(_,_,X,X,X,_,_);
	clock_cursor_bg[1] = MASK(_,X,X,X,X,X,_);
	clock_cursor_bg[2] = MASK(X,X,X,X,X,X,X);
	clock_cursor_bg[3] = MASK(X,X,X,X,X,X,X);
	clock_cursor_bg[4] = MASK(X,X,X,X,X,X,X);
	clock_cursor_bg[5] = MASK(_,X,X,X,X,X,_);
	clock_cursor_bg[6] = MASK(_,_,X,X,X,_,_);

	GrSetCursor(main_window, 7, 16, 3, 3, MAIN_CURSOR_PEN_COLOR, MAIN_CURSOR_PAPER_COLOR, text_cursor_fg, text_cursor_bg);
	GrSetCursor(clock_window, 7, 7, 3, 3, MAIN_CURSOR_PEN_COLOR, MAIN_CURSOR_PAPER_COLOR, clock_cursor_fg, clock_cursor_bg);

/*	GrFillRect(GR_ROOT_WINDOW_ID, gc1, 0, 0, si.cols, si.rows);*/

	text_init();
	if (term_init() < 0) {
		GrClose();
		exit(1);
	}

	/* we want shellIO events also*/
	GrRegisterInput(shellIO);

#if 0 
	GrMainLoop(HandleEvent);
#else
	while(1) {
		GR_EVENT ev;

		update_clock();
		GrCheckNextEvent(&ev);
		HandleEvent(&ev);
	}
#endif
/* in theory this should never be reached but we code for the impossible */
	return 0;
}

void HandleEvent(GR_EVENT *ep)
{
	switch (ep->type) {
		case GR_EVENT_TYPE_KEY_DOWN:
			do_keystroke(&ep->keystroke);
			break;

		case GR_EVENT_TYPE_FOCUS_IN:
			do_focusin(&ep->general);
			break;

		case GR_EVENT_TYPE_FOCUS_OUT:
			do_focusout(&ep->general);
			break;

		case GR_EVENT_TYPE_FDINPUT:
			do_fdinput();
			break;
	}
}

#if ELKS
char * nargv[2] = {"/bin/sash", NULL};
#else
char * nargv[2] = {"/bin/sh", NULL};
#endif

void sigchild(int signo)
{
/*	printg("We have a signal right now!\n", MAIN_PEN_COLOR, MAIN_PAPER_COLOR);
*/	GrClose();
	exit(0);
}

int term_init()
{
	char pty_name[12];
	int n = 0;
	pid_t pid;

again:
	sprintf(pty_name, "/dev/ptyp%d", n);
	if ((shellIO = open(pty_name, O_RDWR | O_NONBLOCK)) < 0) {
		if ((errno == EBUSY || errno == EIO) && n < 10) {
			n++;
			goto again;
		}
		fprintf(stderr, "Can't create pty %s\n", pty_name);
		return -1;
	}
	signal(SIGCHLD, sigchild);
	signal(SIGINT, sigchild);
	if ((pid = fork()) == -1) {
		fprintf(stderr, "No processes\n");
		return -1;
	}
	if (!pid) {
		close(STDIN_FILENO);
		close(STDOUT_FILENO);
		close(STDERR_FILENO);
		close(shellIO);
		
		setsid();
		pty_name[5] = 't';
		if ((shellIO = open(pty_name, O_RDWR)) < 0) {
			fprintf(stderr, "Child: Can't open pty %s\n", pty_name);
			exit(1);
		}
		dup2(shellIO, STDIN_FILENO);
		dup2(shellIO, STDOUT_FILENO);
		dup2(shellIO, STDERR_FILENO);
		execv(nargv[0], nargv);
		exit(1);
	}
	return 0;
}
	
void text_init()
{
	GrGetGCTextSize(gc1, "A", 1, &width, &height, &base);
}

/*
 * Here when a keyboard press occurs.
 */
void
do_keystroke(kp)
	GR_EVENT_KEYSTROKE	*kp;
{
	char foo;

	foo = kp->ch;
	write(shellIO, &foo, 1);
}


/*
 * Here when a focus in event occurs.
 */
void
do_focusin(gp)
	GR_EVENT_GENERAL	*gp;
{
	if(gp->wid==main_window) 
		{ GrSetBorderColor(main_window, ACTIVE_BORDER_COLOR); return; }
	if(gp->wid==clock_window) 
		{ GrSetBorderColor(clock_window, ACTIVE_BORDER_COLOR); return; }
}

/*
 * Here when a focus out event occurs.
 */
void
do_focusout(gp)
	GR_EVENT_GENERAL	*gp;
{
	if(gp->wid==main_window) 
		{ GrSetBorderColor(main_window, INACTIVE_BORDER_COLOR); return; }
	if(gp->wid==clock_window) 
		{ GrSetBorderColor(clock_window, INACTIVE_BORDER_COLOR); return; }
}

/*
 * Here to read the shell input file descriptor.
 */
void
do_fdinput()
{
	char	c;

	if (read(shellIO, &c, 1) == 1)
		char_out(c);
}

/*
 * Here on an unrecoverable error.
 */
void
errorcatcher(code, name, id)
	GR_ERROR	code;				/* error code */
	GR_FUNC_NAME	name;		/* function name which failed */
	GR_ID		id;				/* resource id */
{
	GrClose();
	fprintf(stderr, "DEMO ERROR: code %d, function %s, resource id %d\n",
		code, name, id);
	exit(1);
}
