
/* 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/xraz.c,v 2.1 92/10/19 17:05:43 ken Exp $
 *
 * $Log:	xraz.c,v $
 * Revision 2.1  92/10/19  17:05:43  ken
 * *** empty log message ***
 * 
 * Revision 1.1  92/10/19  17:04:43  ken
 * Initial revision
 * 
 */

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

#include <Xm/Xm.h>
#include <Xm/PushB.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/Scale.h>
#include <Xm/ToggleB.h>
#include <Xm/ToggleBG.h>
#include <Xm/FileSB.h>
#include <X11/Xatom.h>

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

#ifdef sgi
extern int	sginap(long);
#endif

static void GrabSelection(void);


XrazApp	*xraz;


/*VARARGS1*/
static XtArgVal TempString(char *fmt, ...)
{
    va_list		args;
    static char		tbuf[128];
    static XmString	lastString = NULL;
    
    /*CONSTCOND*/
    va_start(args, fmt);
    if (lastString)
	XmStringFree(lastString);
    
    (void) vsprintf(tbuf, fmt, args);
    lastString = XmStringCreateSimple(tbuf);
    
    return (XtArgVal) lastString;
}


void GetReply(void)
{
    register unsigned	i = 0;
    
    (void) fflush(xraz->razOut);
    
    while (1) {
	for (; i < xraz->replySize; ++i) {	
	    xraz->reply[i] = fgetc(xraz->razIn);
	    if (xraz->reply[i] == '\0') {
		if (xraz->debug & 0x1)
		    Error("R: %s", xraz->reply);
		return;
	    }
	}
	xraz->replySize *= 2;
	xraz->reply = ReallocType(xraz->reply, char, xraz->replySize);
	MemCheck(xraz->reply);
    }
}


void SendCmd(char *cmd)
{
    if (xraz->debug & 0x1)
	Error("C: %s", cmd);
    (void) fputs(cmd, xraz->razOut);
    GetReply();
}


void KillOffRaz(void)
{
    if (!xraz->razPID)
	return;
    
    SendCmd("quit\n");
#ifdef sgi
    (void) sginap(10);
#else
    (void) sleep(1);
#endif
    (void) kill(xraz->razPID, 15);
    xraz->razPID = 0;
}


void SetFrameMax(void)
{
    char	buf[128];
    int		nf, sv;
    
    SendCmd("print numframe\n");
    nf = atoi(xraz->reply);
    if (nf > 1) {
	XmScaleGetValue(GetWidget("frame"), &sv);
	if (sv > nf)
	    SetValue(XmNvalue, (XtArgVal) nf, NULL);
	SetValue(XmNmaximum, (XtArgVal) nf, GetWidget("frame"));
	(void) sprintf(buf, "%d", nf);
	XmTextFieldSetString(GetWidget("numraw"), buf);
    }
}


void SetFrameValue(void)
{
    SendCmd("print frame\n");
    XmScaleSetValue(GetWidget("frame"), atoi(xraz->reply) + 1);
}


static void StopRaz(void)
{
    SendCmd("stop\n");
    SetFrameValue();
    SetValue(XmNlabelString, TempString("Stopped"), GetWidget("status"));
    xraz->playing = FALSE;
}


/*ARGSUSED*/
static void PlayCB(XtPointer closure, XtIntervalId *id)
{
    if (!xraz->playing) {
	xraz->playTP = NULL;
	return;
    }
    
    SetFrameValue();
    if (xraz->timer) {
	float	fps, mps;
	
	SendCmd("mark\n");
	if (sscanf(xraz->reply, "%f%f", &fps, &mps) == 2)
	    SetValue(XmNlabelString,
		     TempString("%.2f f/s   %.2f MB/s", fps, mps),
		     GetWidget("status"));
    }
    
    xraz->playTP = XtAppAddTimeOut(xraz->app, xraz->timeout, 
				   PlayCB, (XtPointer) NULL);
    return;
}


static void PlayRaz(void)
{
    SendCmd("play\n");
    SetFrameMax();
    SetValue(XmNlabelString, TempString("Playing"), GetWidget("status"));
    xraz->playing = TRUE;
    if (xraz->timer)
	SendCmd("mark\n");
    if (!xraz->playTP)
	xraz->playTP = XtAppAddTimeOut(xraz->app, xraz->timeout, 
				       PlayCB, (XtPointer) NULL);
}


void SetImageToggle(void)
{
    XmToggleButtonGadgetSetState(GetWidget("playraw"), xraz->playRaw, 0);
    XmToggleButtonGadgetSetState(GetWidget("playmem"), !xraz->playRaw, 0);
    XmToggleButtonGadgetSetState(GetWidget("sourcefile"), 
				 xraz->source == 0, 0);
    XmToggleButtonGadgetSetState(GetWidget("sourceraw"), 
				 xraz->source == 1, 0);
    /*
     XmToggleButtonGadgetSetState(GetWidget("sourcetape"), 
     xraz->source == 2, 0);
     */
}


void SetModeToggle(void)
{
    XmToggleButtonGadgetSetState(GetWidget("zoom"), xraz->zoom, 0);
    XmToggleButtonGadgetSetState(GetWidget("timer"), xraz->timer, 0);
    XmToggleButtonGadgetSetState(GetWidget("doublebuf"), xraz->doublebuf, 0);
    XmToggleButtonGadgetSetState(GetWidget("reverse"), xraz->reverse, 0);
}


void SetFormatDialog(void)
{
    WidgetList	child;
    Cardinal	numChild;
    unsigned	i;
    char	buf[128];
    
    GetValue(XmNchildren, &child, NULL);
    GetValue(XmNnumChildren, &numChild, GetWidget("formatRC"));
    for (i = 0; i < numChild; ++i)
	XmToggleButtonGadgetSetState(child[i], i == xraz->format, 0);
    
    GetValue(XmNchildren, &child, NULL);
    GetValue(XmNnumChildren, &numChild, GetWidget("fsizeRC"));
    for (i = 0; i < numChild; ++i)
	XmToggleButtonGadgetSetState(child[i], 0, 0);
    
    (void) sprintf(buf, "%u", xraz->dim[0]);
    XmTextFieldSetString(GetWidget("filex"), buf);
    (void) sprintf(buf, "%u", xraz->dim[1]);
    XmTextFieldSetString(GetWidget("filey"), buf);
}


void SetDiskDialog(void)
{
    WidgetList	child;
    Cardinal	numChild;
    unsigned	i;
    char	buf[128];
    
    GetValue(XmNchildren, &child, NULL);
    GetValue(XmNnumChildren, &numChild, GetWidget("rawRC"));
    for (i = 0; i < numChild; ++i)
	XmToggleButtonGadgetSetState(child[i], 0, 0);
    
    XmTextFieldSetString(GetWidget("rawname"), xraz->rawName);
    if (xraz->offset && xraz->offset % 1024 == 0)
	(void) sprintf(buf, "%luk", xraz->offset/1024);
    else
	(void) sprintf(buf, "%lu", xraz->offset);
    XmTextFieldSetString(GetWidget("offset"), buf);
    (void) sprintf(buf, "%u", xraz->numraw);
    XmTextFieldSetString(GetWidget("numraw"), buf);
    if (xraz->blocksize && xraz->blocksize % 1024 == 0)
	(void) sprintf(buf, "%luk", xraz->blocksize/1024);
    else
	(void) sprintf(buf, "%lu", xraz->blocksize);
    XmTextFieldSetString(GetWidget("blocksize"), buf);
}


void SetSliders(void)
{
    int		i;
    
    i = xraz->speed * 10;
    i = MIN(i, 300);
    i = MAX(i, 10);
    SetValue(XmNvalue, (XtArgVal) i, GetWidget("speed"));
    
    i = xraz->replicate * 10;
    i = MIN(i, 50);
    i = MAX(i, 1);
    SetValue(XmNvalue, (XtArgVal) i, GetWidget("size"));
    
    i = xraz->border;
    i = MIN(i, 256);
    i = MAX(i, 0);
    SetValue(XmNvalue, (XtArgVal) i, GetWidget("border"));
    
    i = xraz->zoomReplicate * 10;
    i = MIN(i, 100);
    i = MAX(i, 1);
    SetValue(XmNvalue, (XtArgVal) i, GetWidget("zoomsize"));
}


void GetFormatDialog(void)
{
    char	buf[128];
    char	*tf;
    
    tf = XmTextFieldGetString(GetWidget("filex"));
    xraz->dim[0] = ScanLong(tf);
    XtFree(tf);
    tf = XmTextFieldGetString(GetWidget("filey"));
    xraz->dim[1] = ScanLong(tf);
    XtFree(tf);
    (void) sprintf(buf, "set datadim %u %u\n", xraz->dim[0], xraz->dim[1]);
    SendCmd(buf);
    
    switch (xraz->format) {
      case PSEUDO8:
	SendCmd("set imgformat pseudo8\n");
	break;
      case RGBBYTE:
	SendCmd("set imgformat rgbbyte\n");
	break;
      case RGBPLANE:
	SendCmd("set imgformat rgbplane\n");
	break;
    }
}


void GetDiskDialog(void)
{
    char	buf[128];
    char	*tf;
    int		sv;
    
    Free(xraz->rawName);
    xraz->rawName = XmTextFieldGetString(GetWidget("rawname"));
    tf = XmTextFieldGetString(GetWidget("offset"));
    xraz->offset = ScanLong(tf);
    XtFree(tf);
    tf = XmTextFieldGetString(GetWidget("numraw"));
    xraz->numraw = ScanLong(tf);
    XtFree(tf);
    tf = XmTextFieldGetString(GetWidget("blocksize"));
    xraz->blocksize = ScanLong(tf);
    XtFree(tf);
    
    (void) sprintf(buf, "set rawdiskname %s\n", xraz->rawName);
    SendCmd(buf);
    (void) sprintf(buf, "set offset %lu\n", xraz->offset);
    SendCmd(buf);
    (void) sprintf(buf, "set numraw %u\n", xraz->numraw);
    SendCmd(buf);
    (void) sprintf(buf, "set blocksize %lu\n", xraz->blocksize);
    SendCmd(buf);
    
    XmScaleGetValue(GetWidget("chunksize"), &sv);
    xraz->chunksize = sv;
    (void) sprintf(buf, "set chunksize %lu\n", xraz->chunksize);
    SendCmd(buf);
}


static void SetRazColors(void)
{
    char	buf[128];
    int		i;
    
    SendCmd("set colors\n");
    for (i = 0; i < 256; ++i) {
	(void) sprintf(buf, "%d %d %d %d\n", i, 
		       xraz->cmap[i][0], 
		       xraz->cmap[i][1], 
		       xraz->cmap[i][2]);
	SendCmd(buf);
    }
}


/*ARGSUSED*/
static Boolean ConvertSelect(Widget w, Atom *selection, Atom *target,
			     Atom *type, XtPointer *value, 
			     unsigned long *vallen, int *format)
{
    if (*target == XA_WINDOW) {
	Window	*cwin = MallocType(Window);
	
	MemCheck(cwin);
	*cwin = XtWindow(xraz->shell);
	*value = (XtPointer) cwin;
	*vallen = sizeof(Window);
	*type = XA_WINDOW;
	*format = 8;
	
	return True;
    }
    
    if (*target == xraz->xaColormap) {
	unsigned char	*cmap = CallocType(unsigned char, 768);
	unsigned	i;
	
	MemCheck(cmap);
	for (i = 0; i < 256; ++i) {
	    cmap[i*3]     = xraz->cmap[i][0];
	    cmap[i*3 + 1] = xraz->cmap[i][1];
	    cmap[i*3 + 2] = xraz->cmap[i][2];
	}
	*value = (XtPointer) cmap;
	*vallen = 768;
	*type = xraz->xaColormap;
	*format = 8;
	
	return True;
    }
    
    return False;
}


/*ARGSUSED*/
static void LoseSelect(Widget w, Atom *selection)
{
    xraz->ownSelect = False;
}


/*ARGSUSED*/
static void HandleProperty(Widget w, XtPointer closure, XEvent *event,
			   Boolean *dispatch)
{
    unsigned char	*cmap = NULL;
    Atom		type;
    int			format;
    unsigned long	nitems, after;
    unsigned		i;
    
    if (event->type != PropertyNotify  ||
	event->xproperty.state != PropertyNewValue)
	return;
    
    if (event->xproperty.atom == xraz->xaColormap) {
	XGetWindowProperty(xraz->display, XtWindow(xraz->shell),
			   xraz->xaColormap, 0, 192, False, xraz->xaColormap,
			   &type, &format, &nitems, &after, &cmap);
	if (format  &&  nitems == 768) {
	    for (i = 0; i < 256; ++i) {
		xraz->cmap[i][0] = cmap[i*3];
		xraz->cmap[i][1] = cmap[i*3 + 1];
		xraz->cmap[i][2] = cmap[i*3 + 2];
	    }
	    SetRazColors();
	}
	Free(cmap);
    }
}


static void GrabSelection(void)
{
    static unsigned	initialized = FALSE;
    
    if (!initialized) {
	xraz->xaColormap = XInternAtom(xraz->display, "IcolColormap", False);
	xraz->xaInterest = XInternAtom(xraz->display, "IcolInterest", False);
	XtAddEventHandler(xraz->shell, PropertyChangeMask, FALSE,
			  HandleProperty, NULL);
	initialized = TRUE;
    }
    
    if (!xraz->ownSelect)
	xraz->ownSelect = 
	    XtOwnSelection(xraz->shell, xraz->xaInterest,
			   XtLastTimestampProcessed(xraz->display),
			   ConvertSelect, LoseSelect, NULL);
}


void SendScript(void)
{
    String	script;
    unsigned	i = 0;
    
    script = XmTextGetString(GetWidget("scriptText"));
    if (!script)
	return;
    
    while (script[i] != '\0') {
	(void) fputc(script[i], xraz->razOut);
	if (script[i] == '\n')
	    GetReply();
	++i;
    }
    if (script[i-1] != '\n')
	SendCmd("\n");
    
    XtFree(script);
}


void SaveScript(char *fname)
{
    FILE	*dfile = fopen(fname, "w");
    
    if (!dfile) {
	Error("xraz: Cannot write to script file %s", fname);
	return;
    }
    
    (void) fprintf(dfile, "# Razfile - script raz\n");
    (void) fprintf(dfile, "# Created by xraz\n");
    SendCmd("print\n");
    (void) fputs(xraz->reply, dfile);
    SendCmd("print colors\n");
    (void) fputs(xraz->reply, dfile);
    if (xraz->source == 0) {
	SendCmd("print file\n");
	(void) fputs(xraz->reply, dfile);
    }
    
    (void) fclose(dfile);
}


static void RestartRaz(void)
{
    char	*tname = tmpnam(NULL);
    
    SaveScript(tname);
    KillOffRaz();
    StartRaz();
    InitRaz(tname);
    (void) unlink(tname);
    GetRazState();
    SetInterface();
}


/*ARGSUSED*/
void ButtonCB(Widget w, XtPointer closure, XtPointer callData)
{
    char	buf[128];
    int		i;
    
    GrabSelection();
    
    switch ((ButtonEnum) closure) {
      case ButQuit:
	xraz->running = FALSE;
	break;
      case ButOpen:
	xraz->fileSave = 0;
	break;
      case ButSave:
	xraz->fileSave = 1;
	break;
      case ButStartIcol:
	(void) system("icol -b 1024 -s &");
	break;
      case ButRestart:
	RestartRaz();
	break;
      case ButSendScript:
	StopRaz();
	SendScript();
	SetFrameMax();
	break;
      case ButFormat:
	GetFormatDialog();
	break;
      case ButRawDisk:
	GetDiskDialog();
	SetFrameMax();
	break;
      case ButBack:
	SendCmd("set direction -1\n");
	PlayRaz();
	break;
      case ButBackStep:
	StopRaz();
	XmScaleGetValue(GetWidget("frame"), &i);
	(void) sprintf(buf, "goto %d\n", i - 2);
	SendCmd(buf);
	SetFrameValue();
	SetFrameMax();
	break;
      case ButStop:
	StopRaz();
	SetFrameMax();
	break;
      case ButForStep: 
	StopRaz();
	XmScaleGetValue(GetWidget("frame"), &i);
	(void) sprintf(buf, "goto %d\n", i);
	SendCmd(buf);
	SetFrameValue();
	SetFrameMax();
	break;
      case ButFor:
	SendCmd("set direction 1\n");
	PlayRaz();
	break;
    }
}


/*ARGSUSED*/
void ToggleCB(Widget w, XtPointer closure, XtPointer callData)
{
    XmToggleButtonCallbackStruct *td = 
	(XmToggleButtonCallbackStruct *) callData;
    XmString	xms;
    char	*name = NULL;
    char	*s;
    
    GrabSelection();
    
    switch ((ToggleEnum) closure) {
      case TogRawName:
	if (td->set) {
	    GetValue(XmNlabelString, &xms, w);
	    if (XmStringGetLtoR(xms, XmSTRING_OS_CHARSET, &name))
		XmTextFieldSetString(GetWidget("rawname"), name);
	}
	break;
      case TogFormat:
	if (td->set) {
	    GetValue(XmNlabelString, &xms, w);
	    if (XmStringGetLtoR(xms, XmSTRING_OS_CHARSET, &name)) {
		s = strchr(name, ')');
		if (s)
		    s[0] = '\0';
		s = strchr(name, '(');
		if (s) {
		    ++s;
		    if (MATCH("pseudo8", s))
			xraz->format = PSEUDO8;
		    else if (MATCH("rgbbyte", s))
			xraz->format = RGBBYTE;
		    else if (MATCH("rgbplane", s))
			xraz->format = RGBPLANE;
		    else
			Error("Unrecognized format: %s\n", s);
		} else
		    Error("Error parsing format: %s\n", name);
	    }
	}
	break;
      case TogFileSize:
	if (td->set) {
	    GetValue(XmNlabelString, &xms, w);
	    if (XmStringGetLtoR(xms, XmSTRING_OS_CHARSET, &name)) {
		if (sscanf(name, "%u by %u", xraz->dim, xraz->dim+1) < 2)
		    Error("Error parsing file size: %s\n", name);
		else {
		    char	buf[64];
		    (void) sprintf(buf, "%u", xraz->dim[0]);
		    XmTextFieldSetString(GetWidget("filex"), buf);
		    (void) sprintf(buf, "%u", xraz->dim[1]);
		    XmTextFieldSetString(GetWidget("filey"), buf);
		}
	    }
	}
	break;
      case TogPlayRaw:
	if (td->set)
	    SendCmd("set playmode raw\n");
	xraz->playRaw = TRUE;
	SetImageToggle();
	break;
      case TogPlayMem: 
	if (td->set)
	    SendCmd("set playmode memory\n");
	xraz->playRaw = FALSE;
	SetImageToggle();
	break;
      case TogSourceFile:
	if (td->set)
	    SendCmd("set source file\n");
	xraz->source = 0;
	SetImageToggle();
	break;
      case TogSourceRaw:
	if (td->set)
	    SendCmd("set source raw\n");
	xraz->source = 1;
	SetImageToggle();
	break;
      case TogSourceTape:
	if (td->set)
	    SendCmd("set source tape\n");
	xraz->source = 2;
	SetImageToggle();
	break;
      case TogZoom:
	if (td->set)
	    SendCmd("zoom on\n");
	else
	    SendCmd("zoom off\n");
	xraz->zoom = td->set;
	break;
      case TogTimer:
	xraz->timer = td->set;
	break;
      case TogBuffer:
	if (td->set)
	    SendCmd("set double on\n");
	else
	    SendCmd("set double off\n");
	xraz->doublebuf = td->set;
	break;
      case TogReverse:
	if (td->set)
	    SendCmd("set bounce on\n");
	else
	    SendCmd("set bounce off\n");
	xraz->reverse = td->set;
	break;
    }
    
    Free(name);
}


static void SendImageFiles(void)
{
    XmString		*xmfile;
    XmString		dirMask;
    int			i, nfile, update;
    char		buf[128], val[128], *s, *fname;
    Widget		fileSB = GetWidget("fsImage");
    int			dim[2];
    
    GetValue(XmNdirMask, &dirMask, fileSB);
    XmFileSelectionDoSearch(fileSB, dirMask); 
    GetValue(XmNfileListItems, &xmfile, NULL);
    GetValue(XmNfileListItemCount, &nfile, fileSB);
    
    if (nfile) {
	(void) sprintf(buf, "set filename %d\n", nfile);
	SendCmd(buf);
	for (i = 0; i < nfile; ++i)
	    if (XmStringGetLtoR(xmfile[i], XmSTRING_OS_CHARSET, &fname)) {
		(void) sprintf(buf, "%s\n", fname);
		SendCmd(buf);
		free(fname);
	    }

	update = 0;
	(void) strcpy(buf, "set dimension");
	if (s = strstr(xraz->reply, buf))
	    if (sscanf(s, strcat(buf, "%d%d"), dim, dim+1) == 2) {
		xraz->dim[0] = dim[0];
		xraz->dim[1] = dim[1];
		update = 1;
	    }
	(void) strcpy(buf, "set imgformat");
	if (s = strstr(xraz->reply, buf))
	    if (sscanf(s, strcat(buf, "%s"), val) == 2) {
		if (MATCH("pseudo8", s))
		    xraz->format = PSEUDO8;
		else if (MATCH("rgbplane", s))
		    xraz->format = RGBPLANE;
		else if (MATCH("rgbbyte", s))
		    xraz->format = RGBBYTE;
		update = 1;
	    }
	if (update)
	    SetFormatDialog();
    }
    
    XmStringFree(dirMask);
}


/*ARGSUSED*/
void FileCB(Widget w, XtPointer closure, XtPointer callData)
{
    XmFileSelectionBoxCallbackStruct *fsb = 
	(XmFileSelectionBoxCallbackStruct *) callData;
    char	*fname;
    
    switch ((FileEnum) closure) {
      case FileOK:
	(void) XmStringGetLtoR(fsb->value, XmSTRING_OS_CHARSET, &fname);
	if (xraz->fileSave)
	    SaveScript(fname);
	else {
	    StopRaz();
	    InitRaz(fname);
	    GetRazState();
	    SetInterface();
	}
	break;
      case FileImageOK:
	SendImageFiles();
	break;
    }
}


/*ARGSUSED*/
void ScaleCB(Widget w, XtPointer closure, XtPointer callData)
{
    XmScaleCallbackStruct *sd = (XmScaleCallbackStruct *) callData;
    char	buf[128];
    
    GrabSelection();
    
    switch ((ScaleEnum) closure) {
      case ScaleFrame:
	SendCmd("stop\n");
	(void) sprintf(buf, "goto %d\n", sd->value - 1);
	SendCmd(buf);
	SetFrameValue();
	break;
      case ScaleSpeed:
	xraz->speed = (float) sd->value / 10.0;
	(void) sprintf(buf, "set speed %f\n", xraz->speed);
	SendCmd(buf);
	break;
      case ScaleSize:
	xraz->replicate = (float) sd->value / 10.0;
	(void) sprintf(buf, "set replication %f %f\n", 
		       xraz->replicate, xraz->replicate);
	SendCmd(buf);
	break;
      case ScaleBorder:
	xraz->border = sd->value;
	(void) sprintf(buf, "set border %u %u\n", 
		       xraz->border, xraz->border);
	SendCmd(buf);
	break;
      case ScaleZoom:
	xraz->zoomReplicate = (float) sd->value / 10.0;
	(void) sprintf(buf, "set zoom %f %f\n",
		       xraz->zoomReplicate, xraz->zoomReplicate);
	SendCmd(buf);
	break;
    }
}


/*ARGSUSED*/
void BugCB(Widget w, XtPointer closure, XtPointer callData)
{
    String	bugblab;
    char	mailcmd[256];
    FILE	*mailfile;
    
    bugblab = XmTextGetString(GetWidget("bugText"));
    if (!bugblab)
	return;
    
    (void) strncat(strcpy(mailcmd, "mail "), xraz->bugAddress, 240);
    mailfile = popen(mailcmd, "w");
    if (!mailfile)
	return;
    
    (void) fprintf(mailfile, "To: %s\n", xraz->bugAddress);
    (void) fprintf(mailfile, "Subject: Xraz Bug Report\n\n");
    (void) fprintf(mailfile, "%s\n", bugblab);
    (void) fclose(mailfile);
    
    XtFree(bugblab);
}


main(int argc, char *argv[])
{
    XEvent	event;
    
    if (!SetupXraz(argc, argv))
	return 1;
    
    GrabSelection();
    while (xraz->running) {
        XtAppNextEvent(xraz->app, &event);
	XtDispatchEvent(&event);
    }
    
    KillOffRaz();
    return 0;
}

