
/* Copyright (C) 1992  AHPCRC, Univeristy of Minnesota
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program in a file named 'Copying'; if not, write to
 * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139.
 */

/* Author:
 *	Ken Chin-Purcell (ken@ahpcrc.umn.edu)
 *	Army High Performance Computing Research Center (AHPCRC)
 *	Univeristy of Minnesota
 *
 * $Header: /usr/people/ken/gvl/raz/RCS/setup.c,v 2.1 92/10/19 17:05:40 ken Exp $
 *
 * $Log:	setup.c,v $
 * Revision 2.1  92/10/19  17:05:40  ken
 * *** empty log message ***
 * 
 * Revision 1.1  92/10/19  17:04:41  ken
 * Initial revision
 * 
 */

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>
#include <signal.h>

#include <Xm/Xm.h>
#include <Xm/ArrowB.h>
#include <Xm/ArrowBG.h>
#include <Xm/BulletinB.h>
#include <Xm/CascadeB.h>
#include <Xm/CascadeBG.h>
#include <Xm/DialogS.h>
#include <Xm/DrawingA.h>
#include <Xm/DrawnB.h>
#include <Xm/FileSB.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/Label.h>
#include <Xm/LabelG.h>
#include <Xm/List.h>
#include <Xm/MessageB.h>
#include <Xm/PanedW.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/RowColumn.h>
#include <Xm/Scale.h>
#include <Xm/ScrollBar.h>
#include <Xm/ScrolledW.h>
#include <Xm/SeparatoG.h>
#include <Xm/Separator.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/ToggleB.h>
#include <Xm/ToggleBG.h>

#include <X11/StringDefs.h>
#include <X11/Shell.h>

/* I don't normally use these widgets
 #include <Xm/Command.h>
 #include <Xm/MainW.h>
 #include <Xm/MenuShell.h>
 #include <Xm/SelectioB.h>
 */

#include "util.h"
#include "xtutil.h"
#include "xraz.h"
#include "setup.h"

#define	DLINELEN	256


#define XrazOff(member)	XtOffsetOf(struct _XrazApp, member)

static XtResource resources[] = {
    { "help",		"Help",		XtRInt, sizeof(int),
	  XrazOff(help), XtRImmediate,		(XtPointer) 0 },
    { "debug",		"Debug",	XtRInt, sizeof(int),
	  XrazOff(debug), XtRImmediate,		(XtPointer) 0 },
    { "timeout",	"Timeout",	XtRInt, sizeof(int),
	  XrazOff(timeout), XtRImmediate,	(XtPointer) 2000 },
    { "bugAddress", "BugAddress",	XtRString, sizeof(String),
	XrazOff(bugAddress),XtRImmediate,(XtPointer) "gvlware@ahpcrc.umn.edu"},
};

#undef XrazOff

static  XrmOptionDescRec options[] = {
    { "-help",		"help",		XrmoptionNoArg,		"1"  },
    { "-h",		"help",		XrmoptionNoArg,		"1"  },
    { "-debug",		"debug",	XrmoptionSepArg,	NULL },
    { "-t",		"timeout",	XrmoptionSepArg,	NULL },
};


static void Usage(void)
{
    Error("\n");
    Error("usage: xraz [ options ] [ raz_script ]\n");
    Error("	-t msec		Timeout for frame update during play\n");
    Error("	-d level	Set debug level.  1 == print protocol\n");
    Error("	-help | -h	This message\n");
    Error("\n");
}


/* I suppose I should implement the darn help buttons,
 * instead of removing them...
 */
void RemoveHelp(Widget w)
{
    Widget	help = NULL;
    
    if (XtIsSubclass(w, xmFileSelectionBoxWidgetClass))
	help = XmFileSelectionBoxGetChild(w, XmDIALOG_HELP_BUTTON);
    else if (XtIsSubclass(w, xmMessageBoxWidgetClass))
	help = XmMessageBoxGetChild(w, XmDIALOG_HELP_BUTTON);
    if (help)
	XtUnmanageChild(help);
}


static WidgetSpec widgetSpec[] = {
    { 0x101, "rawSelect",	XmCreateFormDialog },
    { 0x000, "rawRC",		XmCreateSimpleRadioBox },
    { 0x000, "rawname",		XmCreateTextField },
    { 0x000, "rawlabel",	XmCreateLabel },
    { 0x000, "offset",		XmCreateTextField },
    { 0x000, "offlabel",	XmCreateLabel },
    { 0x000, "numraw",		XmCreateTextField },
    { 0x000, "numlabel",	XmCreateLabel },
    { 0x000, "blocksize",	XmCreateTextField },
    { 0x000, "blocklabel",	XmCreateLabel },
    { 0x000, "chunksize",	XmCreateScale },
    { 0x000, "chunklabel",	XmCreateLabel },
    { 0x000, "dsep2",		XmCreateSeparator },
    { 0x000, "rawok",		XmCreatePushButton },
    { 0x000, "rawcancel",	XmCreatePushButton },
    { 0x010, "dsep3",		XmCreateSeparator },
    
    { 0x101, "formatSelect",	XmCreateFormDialog },
    { 0x000, "formatRC",	XmCreateSimpleRadioBox },
    { 0x000, "fsep1",		XmCreateSeparator },
    { 0x000, "filex",		XmCreateTextField },
    { 0x000, "fxlabel",		XmCreateLabel },
    { 0x000, "filey",		XmCreateTextField },
    { 0x000, "fylabel",		XmCreateLabel },
    { 0x000, "fsizeRC",		XmCreateSimpleRadioBox },
    { 0x000, "fsep2",		XmCreateSeparator },
    { 0x000, "formatok",	XmCreatePushButton },
    { 0x000, "formatcancel",	XmCreatePushButton },
    { 0x010, "fsep3",		XmCreateSeparator },
    
    { 0x001, "fsBox",		XmCreateFileSelectionDialog },
    
    { 0x001, "fsImage",		XmCreateFileSelectionDialog },
    
    { 0x001, "copyright",	XmCreateInformationDialog },
    
    { 0x101, "bugBox",		XmCreateFormDialog },
    { 0x000, "bugsend",		XmCreatePushButtonGadget },
    { 0x000, "bugcancel",	XmCreatePushButtonGadget },
    { 0x000, "sep",		XmCreateSeparatorGadget },
    { 0x010, "bugText",		XmCreateText },
    
    { 0x101, "scriptBox",	XmCreateFormDialog },
    { 0x000, "scriptsend",	XmCreatePushButtonGadget },
    { 0x000, "scriptdone",	XmCreatePushButtonGadget },
    { 0x000, "sep",		XmCreateSeparatorGadget },
    { 0x010, "scriptText",	XmCreateText },
    
    { 0x110, "topform",		XmCreateForm },
    
    { 0x100, "menuBar",		XmCreateMenuBar },
    
    { 0x000, "control",		XmCreateCascadeButtonGadget },
    { 0x101, "controlMenu",	XmCreatePulldownMenu },
    { 0x000, "open",		XmCreatePushButtonGadget },
    { 0x000, "save",		XmCreatePushButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x000, "script",		XmCreatePushButtonGadget },
    { 0x000, "starticol",	XmCreatePushButtonGadget },
    { 0x000, "restart",		XmCreatePushButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x010, "quit",		XmCreatePushButtonGadget },
    
    { 0x000, "image",		XmCreateCascadeButtonGadget },
    { 0x101, "imageMenu",	XmCreatePulldownMenu },
    { 0x000, "imgfile",		XmCreatePushButtonGadget },
    { 0x000, "format",		XmCreatePushButtonGadget },
    { 0x000, "rawdisk",		XmCreatePushButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x000, "playraw",		XmCreateToggleButtonGadget },
    { 0x000, "playmem",		XmCreateToggleButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x000, "sourcefile",	XmCreateToggleButtonGadget },
    { 0x010, "sourceraw",	XmCreateToggleButtonGadget },
    /*
    { 0x010, "sourcetape",	XmCreateToggleButtonGadget },
    */
    
    { 0x000, "mode",		XmCreateCascadeButtonGadget },
    { 0x111, "modeMenu",	XmCreatePulldownMenu },
    { 0x000, "zoom",		XmCreateToggleButtonGadget },
    { 0x000, "timer",		XmCreateToggleButtonGadget },
    { 0x000, "doublebuf",	XmCreateToggleButtonGadget },
    { 0x010, "reverse",		XmCreateToggleButtonGadget },
    
    { 0x000, "ahpcrc",		XmCreatePushButton },

    { 0x100, "playFrame",	XmCreateFrame },
    { 0x110, "play",		XmCreateForm },
    { 0x000, "anchor",		XmCreateSeparatorGadget },
    { 0x110, "buttonBox",	XmCreateRowColumn },
    { 0x000, "playRev",		XmCreateArrowButton },
    { 0x000, "jogRev",		XmCreateArrowButton },
    { 0x000, "stop",		XmCreatePushButton },
    { 0x000, "jogFwd",		XmCreateArrowButton },
    { 0x010, "playFwd",		XmCreateArrowButton },
    
    { 0x000, "status",		XmCreateLabel },
    
    { 0x110, "sliderFrame",	XmCreateFrame },
    { 0x110, "slider",		XmCreateForm },
    { 0x000, "frameLabel",	XmCreateLabel },
    { 0x000, "speedLabel",	XmCreateLabel },
    { 0x000, "sizeLabel",	XmCreateLabel },
    { 0x000, "borderLabel",	XmCreateLabel },
    { 0x000, "zoomLabel",	XmCreateLabel },
    { 0x000, "frame",		XmCreateScale },
    { 0x000, "speed",		XmCreateScale },
    { 0x000, "size",		XmCreateScale },
    { 0x000, "border",		XmCreateScale },
    { 0x010, "zoomsize",	XmCreateScale },
    
    { 0x000, NULL,		NULL }			/*ENDTABLE*/
};


#include "stop1.x"
#include "stop2.x"

static void InstallXrazPixmap(void)
{
    Window	root = RootWindowOfScreen(XtScreen(xraz->shell));
    Pixmap	pix;
    int		depth;
    Pixel	fg, bg;
    
    /* Create the bitmaps using the root window, because
     * This widget may not be realized yet.
     */
    
    GetValue(XmNdepth, &depth, NULL);
    GetValue(XmNbackground, &bg, NULL);
    GetValue(XmNforeground, &fg, GetWidget("stop"));
    
    pix = XCreatePixmapFromBitmapData(xraz->display, root, stop1_bits, 
				      stop1_width, stop1_height,
				      fg, bg, depth);
    if (pix)
	SetValue(XmNlabelPixmap, (XtArgVal) pix, GetWidget("stop"));
    
    pix = XCreatePixmapFromBitmapData(xraz->display, root, stop2_bits, 
				      stop2_width, stop2_height,
				      fg, bg, depth);
    if (pix)
	SetValue(XmNarmPixmap, (XtArgVal) pix, GetWidget("stop"));
}


static void DressWidgetTree(void)
{
    Widget	w;
    WidgetList	child;
    Cardinal	numChild;
    unsigned	i;
    
#define AC(wn,rn,cb,cd)	XtAddCallback(GetWidget(wn),rn,cb,(XtPointer)cd)
    
    SetValue(XmNsubMenuId, (XtArgVal) GetWidget("controlMenu"),
	     GetWidget("control"));
    SetValue(XmNsubMenuId, (XtArgVal) GetWidget("imageMenu"),
	     GetWidget("image"));
    SetValue(XmNsubMenuId, (XtArgVal) GetWidget("modeMenu"),
	     GetWidget("mode"));
    
    RemoveHelp(GetWidget("fsBox"));
    RemoveHelp(GetWidget("copyright"));
    
    w = GetWidget("fsImage");
    XtUnmanageChild(XmFileSelectionBoxGetChild(w, XmDIALOG_HELP_BUTTON));
    XtUnmanageChild(XmFileSelectionBoxGetChild(w, XmDIALOG_SELECTION_LABEL));
    XtUnmanageChild(XmFileSelectionBoxGetChild(w, XmDIALOG_TEXT));
    
    InstallXrazPixmap();
    
    GetValue(XmNchildren, &child, NULL);
    GetValue(XmNnumChildren, &numChild, GetWidget("rawRC"));
    for (i = 0; i < numChild; ++i)
	XtAddCallback(child[i], XmNvalueChangedCallback,
		      ToggleCB, (XtPointer)TogRawName);
    
    GetValue(XmNchildren, &child, NULL);
    GetValue(XmNnumChildren, &numChild, GetWidget("formatRC"));
    for (i = 0; i < numChild; ++i)
	XtAddCallback(child[i], XmNvalueChangedCallback,
		      ToggleCB, (XtPointer)TogFormat);
    
    GetValue(XmNchildren, &child, NULL);
    GetValue(XmNnumChildren, &numChild, GetWidget("fsizeRC"));
    for (i = 0; i < numChild; ++i)
	XtAddCallback(child[i], XmNvalueChangedCallback,
		      ToggleCB, (XtPointer)TogFileSize);
    
    AC("playRev",	XmNactivateCallback,	ButtonCB, ButBack);
    AC("jogRev",	XmNactivateCallback,	ButtonCB, ButBackStep);
    AC("stop",		XmNactivateCallback,	ButtonCB, ButStop);
    AC("playFwd",	XmNactivateCallback,	ButtonCB, ButFor);
    AC("jogFwd",	XmNactivateCallback,	ButtonCB, ButForStep);
    
    AC("scriptsend",	XmNactivateCallback,	ButtonCB, ButSendScript);
    AC("formatok",	XmNactivateCallback,	ButtonCB, ButFormat);
    AC("rawok",		XmNactivateCallback,	ButtonCB, ButRawDisk);
    
    AC("fsBox",		XmNokCallback,		FileCB, FileOK);
    AC("fsBox",		XmNokCallback,		UnmanageCB, 0);
    AC("fsBox",		XmNcancelCallback, 	UnmanageCB, 0);
    
    AC("fsImage",	XmNokCallback,		FileCB, FileImageOK);
    AC("fsImage",	XmNokCallback,		UnmanageCB, 0);
    AC("fsImage",	XmNcancelCallback, 	UnmanageCB, 0);
    
    AC("ahpcrc",	XmNactivateCallback, ManageCB, GetWidget("copyright"));
    
    AC("copyright",	XmNcancelCallback,   ManageCB, GetWidget("bugBox"));
    
    AC("bugsend",	XmNactivateCallback,	BugCB, 0);
    
    AC("scriptsend",	XmNactivateCallback,	ButtonCB, ButScript);
    AC("scriptdone", XmNactivateCallback, UnmanageCB, GetWidget("scriptBox"));
    
    AC("open",		XmNactivateCallback,	ButtonCB, ButOpen);
    AC("open",		XmNactivateCallback,	ManageCB, GetWidget("fsBox"));
    AC("save",		XmNactivateCallback,	ButtonCB, ButSave);
    AC("save",		XmNactivateCallback,	ManageCB, GetWidget("fsBox"));
    AC("script",	XmNactivateCallback, ManageCB, GetWidget("scriptBox"));
    AC("starticol",	XmNactivateCallback,	ButtonCB, ButStartIcol);
    AC("restart",	XmNactivateCallback,	ButtonCB, ButRestart);
    AC("quit",		XmNactivateCallback,	ButtonCB, ButQuit);
    
    AC("format",  XmNactivateCallback, ManageCB, GetWidget("formatSelect"));
    AC("imgfile", XmNactivateCallback, ManageCB, GetWidget("fsImage"));
    AC("rawdisk",	XmNactivateCallback, ManageCB, GetWidget("rawSelect"));
    
    
    AC("playraw",	XmNvalueChangedCallback,ToggleCB, TogPlayRaw);
    AC("playmem",	XmNvalueChangedCallback,ToggleCB, TogPlayMem);
    AC("sourcefile",	XmNvalueChangedCallback,ToggleCB, TogSourceFile);
    AC("sourceraw",	XmNvalueChangedCallback,ToggleCB, TogSourceRaw);
    /*
    AC("sourcetape",	XmNvalueChangedCallback,ToggleCB, TogSourceTape);
    */
    AC("zoom",		XmNvalueChangedCallback,ToggleCB, TogZoom);
    AC("timer",		XmNvalueChangedCallback,ToggleCB, TogTimer);
    AC("doublebuf",	XmNvalueChangedCallback,ToggleCB, TogBuffer);
    AC("reverse",	XmNvalueChangedCallback,ToggleCB, TogReverse);
    
    AC("frame",		XmNvalueChangedCallback,ScaleCB, ScaleFrame);
    AC("frame",		XmNdragCallback,	ScaleCB, ScaleFrame);
    AC("speed",		XmNvalueChangedCallback,ScaleCB, ScaleSpeed);
    AC("speed",		XmNdragCallback,	ScaleCB, ScaleSpeed);
    AC("size",		XmNvalueChangedCallback,ScaleCB, ScaleSize);
    AC("size",		XmNdragCallback,	ScaleCB, ScaleSize);
    AC("border",	XmNvalueChangedCallback,ScaleCB, ScaleBorder);
    AC("border",	XmNdragCallback,	ScaleCB, ScaleBorder);
    AC("zoomsize",	XmNvalueChangedCallback,ScaleCB, ScaleZoom);
    AC("zoomsize",	XmNdragCallback,	ScaleCB, ScaleZoom);
}


static char *GetState(char *name)
{
    char	buf[128];
    static char	ans[128];
    char	*s;
    
    (void) sprintf(buf, "set %s", name);
    if (s = strstr(xraz->reply, buf))
	if (sscanf(s, strcat(buf, " %[^\n]"), ans) == 1)
	    return ans;
    (void) strcpy(ans, "");
    return ans;
}


static void GetRazColors(void)
{
    char	*s;
    int		ix, n, r, g, b;
    
    SendCmd("print colors\n");
    s = xraz->reply;
    if (sscanf(s, "set colors%n", &n) < 0)
	return;
    s += n;
    
    while (sscanf(s, "%d%d%d%d%n", &ix, &r, &g, &b, &n) == 4) {
	ix = MAX(0, ix);
	ix = MIN(255, ix);
	xraz->cmap[ix][0] = r;
	xraz->cmap[ix][1] = g;
	xraz->cmap[ix][2] = b;
	s += n;
    }
}


void GetRazState(void)
{
    char	*s;
    
    SendCmd("print\n");
    
    xraz->rawName = NewString(GetState("rawdiskname"), 0);
    xraz->numraw = ScanLong(GetState("numraw"));
    xraz->offset = ScanLong(GetState("offset"));
    xraz->blocksize = ScanLong(GetState("blocksize"));
    xraz->chunksize = ScanLong(GetState("chunksize"));
    
    xraz->speed = atof(GetState("speed"));
    
    xraz->playRaw = MATCH("raw", GetState("playmode"));
    s = GetState("source");
    if (MATCH("file", s))
	xraz->source = 0;
    else if (MATCH("raw", s))
	xraz->source = 1;
    else if (MATCH("tape", s))
	xraz->source = 2;
    
    s = GetState("imgformat");
    if (MATCH("pseudo8", s))
	xraz->format = PSEUDO8;
    else if (MATCH("rgbplane", s))
	xraz->format = RGBPLANE;
    else if (MATCH("rgbbyte", s))
	xraz->format = RGBBYTE;
    
    (void) sscanf(GetState("datadim"), "%u%u", xraz->dim, xraz->dim+1);
    
    xraz->border = ScanLong(GetState("border"));
    xraz->replicate = atof(GetState("replication"));
    xraz->zoomReplicate = atof(GetState("zoom"));
    xraz->doublebuf = MATCH("on", GetState("double"));
    xraz->reverse = MATCH("on", GetState("bounce"));
    
    GetRazColors();
}


void SetInterface(void)
{
    SetImageToggle();
    SetModeToggle();
    SetFormatDialog();
    SetDiskDialog();
    SetSliders();
}


static char *RazArgs[] = {
    "raz",
    "-a",
    NULL,
};

void StartRaz(void)
{
    int		rfd[2];
    
    xraz->razPID = ppopen(RazArgs, rfd);
    if (xraz->razPID < 0) {
	Error("xraz: raz cannot be started\n");
	exit(1);
    }
    xraz->razIn = fdopen(rfd[0], "r");
    Verify(xraz->razIn, "Bad input open on raz pipe");
    xraz->razOut = fdopen(rfd[1], "w");
    Verify(xraz->razOut, "Bad output open on raz pipe");
    
    SendCmd("ping\n");
}


void InitRaz(char *iname)
{
    int		c;
    unsigned	newline = FALSE;
    FILE	*ifile;
    
    /* Open initial file and send contents to raz.
     */
    if (MATCH("-", iname))
	ifile = stdin;
    else
	ifile = fopen(iname, "r");
    if (ifile == NULL) {
	Error("xraz: Can't open input script.\n");
	return;
    }
    
    c = fgetc(ifile);
    while (c != EOF) {
	(void) fputc(c, xraz->razOut);
	newline = c == '\n';
	if (newline)
	    GetReply();
	c = fgetc(ifile);
    }
    if (!newline)
	SendCmd("\n");
    
    if (ifile != stdin)
	(void) fclose(ifile);
}


static void CatchSignal(int signo)
{
    KillOffRaz();
    (void) kill(getpid(), signo);
}


static void SetErrorHandlers(void)
{
    int		i;
    void	(*oldcatcher)();
    
    (void) atexit(KillOffRaz);
    for (i = 0; i <= SIGTERM; i++)
	switch (i) {
	  case SIGSTOP:		/* don't choke on shell signals */
	  case SIGTSTP:
	  case SIGCONT:
	    break;
	  case SIGPIPE:
	    (void) signal(i, SIG_IGN);
	    break;
	  default:
	    oldcatcher = signal(i, CatchSignal);
	    if (oldcatcher == SIG_IGN)
		(void) signal(i, SIG_IGN);	/* oops, sorry... */
	    break;
	}
}


static void AddConverters(XtAppContext app);

#ifdef USEFALLBACK
static String fallbackResources[] = { 
#include "fallback.h"
    NULL,
};
#else
static String *fallbackResources = NULL;
#endif

int SetupXraz(int argc, char *argv[])
{
#if XtSpecificationRelease <= 4
    Cardinal		narg = argc;
#else
    int			narg = argc;
#endif
    
    SetErrorHandlers();
    xraz = MallocType(XrazApp);
    
    MemCheck(xraz);
    ZeroType(xraz, XrazApp);
    
    (void) signal(SIGPIPE, SIG_IGN);
    
    xraz->shell = XtAppInitialize(&xraz->app, CLASS_NAME, 
				  options, XtNumber(options),
				  &narg, argv, fallbackResources, NULL, 0);
    AddWidgetConverter(xraz->app);
    xraz->display = XtDisplay(xraz->shell);
    
    XtGetApplicationResources(xraz->shell, (XtPointer) xraz, 
			      resources, XtNumber(resources), NULL, 0);
    if (xraz->help) {
	Usage();
	return 0;
    }
    
    xraz->running = TRUE;
    xraz->needLoad = TRUE;
    xraz->replySize = REPLYLEN;
    xraz->reply = CallocType(char, xraz->replySize);
    MemCheck(xraz->reply);
    
    BuildWidgetTree(xraz->shell, widgetSpec);
    DressWidgetTree();
    XtRealizeWidget(xraz->shell);
    
    StartRaz();
    if (narg > 1)
	InitRaz(argv[1]);
    GetRazState();
    SetInterface();
    
    return 1;
}
