
/*
 * Filename: 	main.c
 *
 * This file is part of the Muse authoring system,
 * developed at MIT Project Athena.
 *
 * Author(s):	Matthew E. Hodges,  Digital Equipment Corporation
 *              Russell M. Sasnett, GTE Laboratories Incorporated
 *              V. Judson Harward,  MIT Project Athena
 * 
 * Copyright 1989, 1990 by the Massachusetts Institute of Technology;
 * see the file COPYRIGHTS for a complete notice.
 *
 * CHANGE LOG:
 * ===========
 *
 * $Log:	main.c,v $
 * Revision 2.2  91/01/17  11:50:09  jud
 * added XFlush
 * 
 * 
 * Revision 2.1  91/01/02  17:31:21  jud
 * Muse 2 initial version for Athena Release 7.2
 * 
 *
 */

#ifndef lint
static char *rcsid_main_c = 
"MuseID: main.c $Revision: 2.2 $ $Date: 91/01/17 11:50:09 $ $Author: jud $";
#endif /* lint */

#define MAIN_C

/* ------------------------------------------------------------------- */

#include <stdio.h>

#include "xdefs.h"

#include <signal.h>
#include <sys/time.h>

#ifndef i386
#include <sys/types.h>
#endif

/* for EventScripts */
#include <ev_tokens.h>

extern DictPtr  LoadedEnvs;
extern DictPtr  theObjectDict();

#include "prsdefs.h"
#include "structs.h"
#include "widgets.h"
#include "internal.h"
#include "vdisc.h"
#include "y.tab.h"

#ifdef X11R3
#include <X11/Viewport.h>
#include <X11/List.h>
#include <X11/Xmu.h>
#else
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/List.h>
#include <X11/Xmu/Xmu.h>
#endif

#include <X11/Xatom.h>

/* 
 * place to save program name, and name
 * of any optional script included on command line
 */
char           *progname = NULL;
char           *scriptname = NULL;

#include "memfile.h"

static int      sync = 0;	/* whether to synchronize server */

/* 
 * EventScript stuff
 */

extern int      ev_Debug;
extern ENV_PTR  curenv;

Display        *myDisplay;	/* externally visible to ES */
#ifdef OBSOLETE
int             myScreen;	/* ditto */
#endif
Colormap        myColormap;	/* double-ditto */

Colormap        CreatePseudo8Colormap();
Colormap        CreatePseudo4Colormap();


#include <X11/Xutil.h>
extern XContext ElementContext;
extern XContext WidgetContext;

Package        *first_package;
extern Package *readfile();
extern CVideo  *cvideo;

/* 
 * following is used for compatibility with window managers
 */
#include <strings.h>
char           *wm_res_name;
char           *wm_res_class;

MEMFILE        *fp,
               *tmp_fp;

int             UsingVideo = 0;

/* 
 * EvScript needs these (though Muse doesn't)
 */
int             ev_LoadingDb = 0;
int             ev_Kbd = 0;

/* 
 * stuff for logging events
 */
#define MAX_NUM 0xffffffff
#define TIME_DIFF(op1,op2) \
	(op1) >= (op2) ? ((op1)-(op2)) : (MAX_NUM-(op1)+(op2))

Time            LastMuseEventTime = (Time) 0;
Time            timeLastSelection = (Time) 0;
char *          MuseExportString = (char *)NULL;
char *          MuseImportString = (char *)NULL;

static void     Select();
static Boolean  furnishString();
static void     loseString();

static char    *LogFile = "/tmp/MuseEventLog";
static FILE    *LogFp = NULL;
static Time     last_time = (Time) 0;
static XEvent   current;
static long     root_mask = 0;
static Window   PtrWindow = (Window) 0;	/* window ptr is currently in */

int             Logging = 0;
int             LogSwapped = 0;	/* event log needs to be swapped while
				 * reading */

#include "swapbytes.h"

#include "Layout.h"		/* local Muse widget; like HP bulletin board */
#include <X11/Shell.h>

/* 
 * following widget stuff is for the dialog box routines
 */

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

#include <X11/cursorfont.h>

#ifdef X11R3
#include <X11/Form.h>
#include <X11/Label.h>
#include <X11/AsciiText.h>
#include <X11/Command.h>
#else
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Command.h>
#endif

typedef struct
{
    int             inited;
    int             waiting;
    int             hit_okay;
    int             hit_cancel;
    int             fg;
    int             bg;
    Widget          popup;
    Widget          form;
    Widget          label;
    Widget          text;
    Widget          okay;
    Widget          cancel;
    XFontStruct    *font1;
    XFontStruct    *font2;
    char            dlog_string[256];
}               MuseDialogData;

MuseDialogData  MDD;

typedef struct
{
    int             inited;
    int             waiting;
    int             hit_cancel;
    int             fg;
    int             bg;
    Widget          popup;
    Widget          form;
    Widget          label;
    Widget          view;
    Widget          list;
    Widget          cancel;
    XFontStruct    *font1;
    XFontStruct    *font2;
    int             list_index;
    char *          list_string;
}               MusePickListData;

MusePickListData  MPLD;

static String default_list[] = {
  "the first list entry is to make the viewport wide",
  "the second list entry",
  "the third list entry",
  "the fourth list entry",
  "the fifth list entry",
  "the sixth list entry",
  "the seventh list entry",
  "the eighth list entry",
  "the ninth list entry",
  "the tenth list entry",
  NULL
  };

#define NO_TEXT_STRING "-*notext*-"

#define TRANS_STRING "MMotionTransparentColor"
Atom transAtom = (Atom)NULL;

static void
PickItem(w, closure, call_data)
     Widget w;
     XtPointer closure, call_data;
{
#ifdef X11R3
  XtListReturnStruct *item = (XtListReturnStruct*)call_data;
#else
  XawListReturnStruct *item = (XawListReturnStruct*)call_data;
#endif
  MPLD.list_index = item->list_index;
  MPLD.list_string = item->string;
  MPLD.waiting = 0;
}

void
dlog_button_press(w, client_data, call_data)
    Widget          w;
    caddr_t         client_data;
    caddr_t         call_data;
{
    if (w == MDD.okay)
    {
	MDD.hit_okay = 1;
	MDD.hit_cancel = 0;
	MDD.waiting = 0;
    }
    else
    if (w == MDD.cancel)
    {
	MDD.hit_okay = 0;
	MDD.hit_cancel = 1;
	MDD.waiting = 0;
    }
    else
    if (w == MPLD.cancel)
    {
	MPLD.hit_cancel = 1;
	MPLD.waiting = 0;
    }
}

void
redirect_keys(w, client_data, event)
    Widget          w;
    caddr_t         client_data;
    XEvent         *event;
{
    event->xkey.window = XtWindow(MDD.text);
    event->xkey.subwindow = 0;
    event->xkey.x = 2;
    event->xkey.y = 2;

    XSendEvent(XtDisplay(w), XtWindow(MDD.text), False,
	       KeyPressMask | KeyReleaseMask, event);
}

void
dlog_focus(w, client_data, event)
    Widget          w;
    caddr_t         client_data;
    XEvent         *event;
{
  XSetInputFocus(XtDisplay(MDD.text), XtWindow(MDD.text), 
		 *(int *)client_data, 
		 event->xcrossing.time);
}

void
return_action(w, event, params, num_params)
    Widget          w;
    XEvent         *event;
    String         *params;
    int             num_params;
{
    XtCallCallbacks(MDD.okay, XtNcallback, NULL);
}

XtActionsRec    popupActions[] =
{
 {"return_action", return_action},
};

static Arg      MuseBBargs[] =
{
 {XtNwidth, (XtArgVal) 1},
 {XtNheight, (XtArgVal) 1},
};


/* 
 * following is for setting properties on video windows
 */


Atom            VideoAtom;	/* global */
char           *VideoPropName = "PLX_VIDEO";
int             VideoPropVal = 0;


Atom            MuseCmapAtom;
char           *MuseCmapName = "MUSE_COLORMAP_ID";

Atom            MusePmapAtom;
char           *MusePmapName = "MUSE_PIXMAP_ID";

Display *       MuseVideoServerDpy;
Pixmap          MuseVideoServerPixmap;


int
Quit()
{
    if (LogFp)
    {
	if (Logging)
	    stop_logging();
	else
	    stop_playback();
    }
    XFlush(u_Display);
    XCloseDisplay(u_Display);
    exit(0);
}

static XtResource muse_resources[] =
{
  { "debugLevel", "DebugLevel", XtRInt, sizeof(int),
      XtOffset(u_OptionsPtr,debug_level), XtRImmediate, (caddr_t)0 },
  { "dataFile", "DataFile", XtRString, sizeof(String),
      XtOffset(u_OptionsPtr,data_file_name), XtRImmediate, NULL },
  { "scriptFile", "ScriptFile", XtRString, sizeof(String),
      XtOffset(u_OptionsPtr,script_file_name), XtRImmediate, NULL },
  { "galateaHost", "GalateaHost", XtRString, sizeof(String),
      XtOffset(u_OptionsPtr,galatea_host_name), XtRImmediate, NULL },
  { "monochrome", "Monochrome", XtRBoolean, sizeof(Boolean),
      XtOffset(u_OptionsPtr,monochrome), XtRImmediate, (caddr_t)False },
  { "noVideo", "NoVideo", XtRBoolean, sizeof(Boolean),
      XtOffset(u_OptionsPtr,novideo), XtRImmediate, (caddr_t)False },
  { "reverseVideo", "ReverseVideo", XtRBoolean, sizeof(Boolean),
      XtOffset(u_OptionsPtr,reverse_video), XtRImmediate, (caddr_t)False },
  { "sync", "Sync", XtRBoolean, sizeof(Boolean),
      XtOffset(u_OptionsPtr,sync), XtRImmediate, (caddr_t)False },
  { "override", "Override", XtRBoolean, sizeof(Boolean),
      XtOffset(u_OptionsPtr,override), XtRImmediate, (caddr_t)False },
  { "help",  "Help", XtRBoolean, sizeof(Boolean),
      XtOffset(u_OptionsPtr,help), XtRImmediate, (caddr_t)False },
};

static XrmOptionDescRec muse_option_table[] =
{
  { "-debug",   "debugLevel",  XrmoptionNoArg,    "5"    },
  { "-debugLevel", "debugLevel", XrmoptionSepArg, NULL   },
  { "-override","override",    XrmoptionNoArg,     "True"  },
  { "-sync",    "sync",        XrmoptionNoArg,     "True"  },
  { "-gserv",   "galateaHost", XrmoptionSepArg,    NULL    },
  { "-data",    "dataFile",    XrmoptionSepArg,    NULL    },
  { "-script",  "scriptFile",  XrmoptionSepArg,    NULL    },
  { "-mono",    "monochrome",  XrmoptionNoArg,     "True"  },
  { "-novideo", "noVideo",     XrmoptionNoArg,     "True"  },
  { "-nv",      "noVideo",     XrmoptionNoArg,     "True"  },
  { "-rv",      "reverseVideo",XrmoptionNoArg,     "True"  },
  { "-help",    "help",        XrmoptionNoArg,     "True"  },
};

int
usage(s)
    char           *s;
{
    fprintf(stderr, "usage: %s datafile [scriptfile] [options ...]\n", s);
    fprintf(stderr, "where:\n");
    fprintf(stderr, "       datafile is the Muse data file\n");
    fprintf(stderr, "       scriptfile is the optional EventScript file\n");
    fprintf(stderr, "and the other options are:\n");
    fprintf(stderr, "       -sync to run X in synchronous mode\n");
    fprintf(stderr, "       -debug to turn on debugging output\n");
    fprintf(stderr, "       -gserv NAME to set the Galatea host to NAME\n");
    fprintf(stderr, "       -mono to disable color output\n");
    fprintf(stderr, "       -rv to reverse fg & bg (monochrome only)\n");
    fprintf(stderr, "       -novideo or -nv to disable video output\n");
    fprintf(stderr, "       -override to force window placement\n");
    fprintf(stderr, "       -help to print out this message\n");
    fprintf(stderr, "       -data FILE to set the data file to FILE\n");
    fprintf(stderr, "       -script FILE to set the script file to FILE\n");
    exit(1);
}


main(argc, argv)
    int             argc;
    char          **argv;
{
    static char    *func = "main";
    char           *getenv();
    XEvent          ev;
    int             fd;
    Widget          toplevel, BBshell;
    char           *lslash;
    extern char    *xsave_string();
    Arg             args[10];
    int             n;
    Visual         *visual;
    int             bad_dpy = 0;
    int             maj, fev, fer;

    progname = xsave_string(argv[0]);

    /* 
     * Get application name and class
     */

    if (lslash = rindex(argv[0], '/'))
    {
	wm_res_name = lslash + 1;
    }
    else
    {
	wm_res_name = argv[0];
    }
    wm_res_class = "Muse";

    /* 
     * Initialize the toolkit
     */

    toplevel = XtInitialize(wm_res_name, wm_res_class, 
			 muse_option_table, XtNumber(muse_option_table),
			 &argc, argv);

    XtGetApplicationResources(toplevel,&MuseOptions,
			      muse_resources,XtNumber(muse_resources),
			      NULL,0);

    /*
     * argv[] should now contain the program name,
     * the name of a Muse data file, and optionally
     * the name of an EventScript file
     */ 

    switch(argc)
      {
      case 1:
	if(!MuseOptions.data_file_name)
		usage(argv[0]);
	break;
      case 2:
	MuseOptions.data_file_name = xsave_string(argv[1]);
	break;
      case 3:
	MuseOptions.data_file_name = xsave_string(argv[1]);
	MuseOptions.script_file_name = xsave_string(argv[2]);
        scriptname = MuseOptions.script_file_name;
	break;
      default:
	usage(argv[0]);
	break;
      }

    if(MuseOptions.help)
	usage(argv[0]);

    ev_Debug = MuseOptions.debug_level;

    /* 
     * build global X things
     */

    u_Display = XtDisplay(toplevel);
    if (MuseOptions.sync)
    {
	printf("putting X into synchronized mode\n");
	XSynchronize(u_Display, 1);
    }

    /*
     * create video atom
     */

    VideoAtom = XInternAtom(u_Display, VideoPropName, False);

    u_RootWindow = DefaultRootWindow(u_Display);

    /*
     * initialize video server globals
     */

    u_Rpdcount = 0;
    u_Disccount = 0;

    /* 
     * set EventScript globals
     */

    myDisplay = u_Display;
    myScreen = DefaultScreen(u_Display);
    visual = DefaultVisual(u_Display,myScreen);
    u_Depth = DefaultDepth(u_Display,myScreen);

    switch(u_Depth)
      {
      case 1:
	u_DpyIsMono = 1;
	u_DpyIsPlx = 0;
	u_DpyIsPseudo8 = 0;
	break;
      case 4:
	u_DpyIsMono = 0;
	u_DpyIsPlx = 0;
	u_DpyIsPseudo8 = 0;
	switch(visual->class)
	  {
	  case PseudoColor:
	    u_DpyIsPseudo4 = 1;
	    break;
	  default:
	    fprintf(stderr,"Error: 4-plane display is not PseudoColor\n");
	    bad_dpy = 1;
	    break;
	  }
	break;
      case 8:
	u_DpyIsMono = 0;
	switch(visual->class)
	  {
	  case PseudoColor:
	    u_DpyIsPseudo8 = 1;
	    if (XQueryExtension(u_Display, "ParallaxVideo", 
				 &maj, &fev, &fer))
	      u_DpyIsPlx = 1;
	    else
	      u_DpyIsPlx = 0;
	    break;
	  default:
	    fprintf(stderr,"Error: 8-plane display is not PseudoColor\n");
	    bad_dpy = 1;
	    break;
	  }
	break;
      default:
	fprintf(stderr,"Error: %d-display too deep; max is 8 planes\n",
		u_Depth);
	bad_dpy = 1;
	break;
      }

    if(bad_dpy)
      {
	fprintf(stderr,"\n");
	fprintf(stderr,"** Fatal Error: unsupported display type.\n");
	fprintf(stderr,"   Muse only works on 1-plane Monochrome,\n");
	fprintf(stderr,"   and 4-plane or 8-plane PseudoColor X Displays.\n");
	exit(1);
      }

    if(MuseOptions.reverse_video && !MuseOptions.monochrome 
       && !u_DpyIsMono)
      {
	fprintf(stderr,"\n");
	fprintf(stderr,"** Fatal Error: inconsistent use of options.\n");
	fprintf(stderr,"   Reverse-Video mode only works on a Monochrome\n");
	fprintf(stderr,"   Display, or when the -mono switch is set on a\n");
	fprintf(stderr,"   4-plane or 8-plane PseudoColor X Display.\n");
	usage(args[0]);
	exit(1);
      }

    n = 0;

    if(u_DpyIsPseudo8)
      {
	if(MuseOptions.monochrome)
	  {
	    u_Colormap = myColormap = DefaultColormap(u_Display,myScreen);
	  }
	else
	  {
	    /* 
	     * look for colormap prop on root window; if it
	     * doesn't exist, make our own colormap
	     */

	    Atom req_type;
	    Atom got_type;
	    int got_format;
	    unsigned long nitems;
	    unsigned long bytes_after;
	    unsigned char * propval;
	    
	    MuseCmapAtom = XInternAtom(u_Display,MuseCmapName,True);
	    if(MuseCmapAtom == None)
	      {
		MuseCmapAtom = XInternAtom(u_Display,MuseCmapName,False);
	      }

	    XGetWindowProperty(u_Display,u_RootWindow,MuseCmapAtom,0,8192,
			       False,XA_COLORMAP,&got_type,&got_format,
			       &nitems,&bytes_after,&propval);
	    if(got_format)
	      {
		u_Colormap = (Colormap)(*(long *)propval);
		XFree(propval);
		printf("** Muse: using value of MUSE_COLORMAP_ID property\n");
	      }
	    else
	      {
		u_Colormap = CreatePseudo8Colormap(u_Display);
	      }

	    myColormap = u_Colormap;

	    load_lum_table(lum_table);
	
	    XtSetArg(args[n], XtNcolormap, u_Colormap);
	    n++;
	  }
      }
    else if(u_DpyIsPseudo4)
      {
	if(MuseOptions.monochrome)
	  {
	    u_Colormap = myColormap = DefaultColormap(u_Display,myScreen);
	  }
	else
	  {
	    u_Colormap = CreatePseudo4Colormap(u_Display);
	    myColormap = u_Colormap;

	    load_lum_table(lum_table);
	
	    XtSetArg(args[n], XtNcolormap, u_Colormap);
	    n++;
	  }
      }
    else
      {
	u_Colormap = myColormap = DefaultColormap(u_Display,myScreen);
      }

    XtSetArg(args[n], XtNgeometry, "1x1+5000+5000" );
    n++;
    XtSetArg(args[n], XtNinput, True);
    n++;
    XtSetArg(args[n], XtNmappedWhenManaged, False);
    n++;

    BBshell = XtAppCreateShell(NULL, "Muse",
                               topLevelShellWidgetClass, XtDisplay(toplevel),
                               args, n);

/*    XtSetValues(toplevel, args, n); */

    create_gcs();

    /* 
     * make no-constraints layout widget (like HP bulletin board)
     * as grandparent of all Muse widgets
     */

    MuseBB = XtCreateManagedWidget("MuseBB", layoutWidgetClass,
/*				   toplevel,  */
				   BBshell,
				   MuseBBargs, XtNumber(MuseBBargs));

/*    XtRealizeWidget(toplevel); */
    XtRealizeWidget(BBshell); 

#ifdef notdef
    {
      XWMHints wmhints;

      wmhints.flags = InputHint;
      wmhints.input = True;
      XSetWMHints(XtDisplay(MuseBB),XtWindow(MuseBB),&wmhints);
    }
#endif

    /* 
     * add Muse-specific actions to the toolkit
     */

    add_Xt_actions();		/* defined in make_Xtk11.c */

    /* 
     * create global contexts; these let you find
     * Muse widget wrapper structures given a widget ID or element ID 
     */

    ElementContext = XUniqueContext();
    WidgetContext = XUniqueContext();

    if ((fp = mfopen(MuseOptions.data_file_name)) == NULL)
    {
	fprintf(stderr,"Muse: Can't open data file '%s'\n", 
		MuseOptions.data_file_name);
	exit(1);
    }

    if(ev_Debug)
        printf("BEGINNING yacc parse\n");
    yyparse();
    if(ev_Debug)
        printf("FINISHED yacc parse\n");

    InitErrorHandler();		/* for EvScript; handles X errors */
    InitColors();		/* for EvScript; color naming stuff */

    disc = NULL;		/* not using video yet */

    /*
     * NOTE: any script file used on the command line will NOT be written out
     * by any of the write_data routines; this is a FEATURE; only data which
     * was in a muse file is written out 
     */

    if (MuseOptions.script_file_name)
	ParseScriptFile(MuseOptions.script_file_name);

    MuseSyncState();

    /* 
     * set default initial window for event logging
     */
  
    PtrWindow = u_RootWindow;

    while (1)
    {
	XtNextEvent(&ev);

	if (ev.type == EnterNotify && !ev.xany.send_event)
	    PtrWindow = ev.xcrossing.window;
	else
	if (ev.type == LeaveNotify && !ev.xany.send_event)
	    PtrWindow = u_RootWindow;

	if (Logging)
	    log_event(&ev);
	else
	if (LogFp)		/* means we must be playing back a log file */
	{

	    /*
	     * don't process genuine motion events that are sent to us when a
	     * log file is playing back; they are usually generated by
	     * WarpPointer; the state in these events holds CURRENT pointer
	     * state, not the saved state; so DON'T pass it to
	     * XtDispatchEvent() 
	     */

	    if (ev.type == MotionNotify && !ev.xany.send_event)
		continue;	/* probably due to warp */

	    if (ev.xany.send_event)
	    {
		/* 
		 * fix up the window and coordinates;
		 * don't need to go down tree to translate coords, 
		 * just use PtrWindow from above
		 */
		ev.xbutton.window = PtrWindow;
	    }
	}

	switch(ev.type)
	  {
	  case ButtonPress:
	  case ButtonRelease:
	    LastMuseEventTime = ev.xbutton.time;
	    break;
	  case MotionNotify:
	    LastMuseEventTime = ev.xmotion.time;
	    break;
	  case EnterNotify:
	  case LeaveNotify:
	    LastMuseEventTime = ev.xcrossing.time;
	    break;
	  case KeyPress:
	  case KeyRelease:
	    LastMuseEventTime = ev.xkey.time;
	    break;
	  default:
	    break;
	  }

	(void) XtDispatchEvent(&ev);
	handle_default_exposures(&ev);
	handle_msgs(&ev);
    }
}

int
handle_default_exposures(ep)
    XEvent         *ep;
{
    char           *data;
    Element        *e;

    if (!ep || ep->type != Expose)
	return -1;

    if( ep->xexpose.count > 0 )	/* only handle the last one */
      return 0;

    if (!XFindContext(u_Display, ep->xexpose.window, ElementContext, &data))
    {
	e = (Element *) data;

	/* 
	 * check to be sure element and its package are active
	 */

	if (e && e->p && e->active && e->p->active)
	  {
	    if (ev_Debug)
	      printf("handle_default_exposure(): element <%s>\n",
		     e->name);
	    refresh(e);
	  }
    }

    return 0;
}

/* 
 * stuff for creating fixed Muse colormap for image display
 */

#include "pixfile.h"

#define PLX_DITHER_LEVELS	5
#define PLX_BW_LEVELS 		32
#define PLX_DITHER_COLORS 	125
#define PLX_START_BW		35
#define PLX_END_BW		66
#define PLX_START_COLOR 	67
#define PLX_END_COLOR 		191
#define TOTAL_COLORS		(DITHER_COLORS + BW_LEVELS)

int
LoadMuseColors(colors)
	XColor colors[256];
{
int i;
int levels, levelsq, ncolors;
float N;

	for( i=0; i<PLX_BW_LEVELS; i++ )
	{
	unsigned long val;

		val = (((float)i/(PLX_BW_LEVELS-1))*0xffff);
		colors[PLX_START_BW+i].pixel = PLX_START_BW+i;
		colors[PLX_START_BW+i].red = 
		colors[PLX_START_BW+i].green = 
		colors[PLX_START_BW+i].blue = val;
		colors[PLX_START_BW+i].flags = DoRed | DoGreen | DoBlue;
	}

	/* now for the 5x5x5 color dither space */

	levels = PLX_DITHER_LEVELS;
	levelsq = levels * levels;      /* squared */
	ncolors = levelsq * levels;     /* and cubed */
	N = 255.0 / (levels-1.0);       /* Get size of each step */

#define rnd(x)  ((int)((x)+0.5))    /* round off a float to an int */

	for( i=0; i<PLX_DITHER_COLORS; i++ )
	{
		colors[PLX_START_COLOR+i].pixel = PLX_START_COLOR+i;
	        colors[PLX_START_COLOR+i].red = 
			(u_short)(rnd((i%levels)*N)*257);
        	colors[PLX_START_COLOR+i].green = 
			(u_short)(rnd(((i/levels)%levels)*N)*257);
	        colors[PLX_START_COLOR+i].blue = 
			(u_short)(rnd(((i/levelsq)%levels)*N)*257);
	        colors[PLX_START_COLOR+i].flags = DoRed | DoGreen | DoBlue;
	}

#undef rnd

}

int
load_lum_table(tbl)
	int tbl[];
{
XColor colors[256];
int i;

    LoadMuseColors(colors);

    for (i = 35; i < 192; i++)
    {
	tbl[i] = colors[i].red * .299 +
	    colors[i].green * .587 +
	    colors[i].blue * .114;
	tbl[i] >>= 12;     /* yields 0-15 */
	tbl[i] = ((tbl[i] / 15.0) * 14);
	if(tbl[i] == 14)
	  tbl[i] = 1; /* white */
	else if(tbl[i] != 0)
	  tbl[i] += 1;
	if(tbl[i] > 14)
	  tbl[i] = 14;
	if (tbl[i] > 14 || tbl[i] < 0)
	{
	    printf("** lum builder failed\n");
	    exit(1);
	}
    }
}

int
GetMMotionTransColor(dpy)
     Display *dpy;
{
  Atom req_type;
  Atom got_type;
  int got_format;
  unsigned long nitems;
  unsigned long bytes_after;
  unsigned char * propval;
  int trans;

  /* see if property already exists */
  if(!transAtom)
    {
      transAtom = XInternAtom(dpy,TRANS_STRING,True);
      if(transAtom == None)
	{
	  transAtom = XInternAtom(dpy,TRANS_STRING,False);
	}
    }

  XGetWindowProperty(dpy,DefaultRootWindow(dpy),transAtom,0,8192,
		     False,XA_INTEGER,&got_type,&got_format,
		     &nitems,&bytes_after,&propval);
  if(got_format)
    {
      trans = *(int *)propval;
      XFree(propval);
    }
  else
    {
      trans = -1;
    }

  return trans;
}


Colormap
CreatePseudo4Colormap(dpy)
     Display *dpy;
{
  Colormap cmap;
  int screen;
  int ncells;
  Window root;
  XColor colors[16];
  int i;
  int white, black;
  int trans = 15;
  int got_trans;

  /* hack city: to work on IBM VGA w/MMotion card;
     want black and white pixels in new map to be
     same as old (to avoid pyrotechnics); and don't
     want to use #15 (0xf) because it should be used
     only for video shine-through */
  
  root = DefaultRootWindow(dpy);
  screen = DefaultScreen(dpy);
  ncells = DisplayCells(dpy,screen);

  white = WhitePixel(dpy,screen);
  black = BlackPixel(dpy,screen);

  if(white > 1 || black > 1)
    {
      printf("warning: CreateVGAColormap(): white not 1 or black not 0\n");
    }

  got_trans = GetMMotionTransColor(dpy,root);
  if(got_trans < 0)
    {
      fprintf(stderr,"WARNING: MMotion property not found; run 'xtrans'\n");
    }
  else if(got_trans != 15)
    {
      fprintf(stderr,"WARNING: MMotion property is %d, not 15; run 'xtrans'\n",
	      got_trans);
    }
  
  cmap = XCreateColormap(dpy, root,
			 DefaultVisual(dpy,screen), AllocAll);
  
  if(!cmap)
    {
      printf("failed to allocate new colormap\n");
      exit(1);
    }
  
  /* can't use the transparent color (15); that leaves 14
     colors; and don't want to change black/white pixel
     in the new map; here's what it should look like:
     
     0: 0%
     1: 100%
     2: 1/14
     3: 2/14
     4: 3/14
     5: 4/14
     6: 5/14
     7: 6/14
     8: 7/14
     9: 8/14
     10:9/14
     11:10/14
     12:11/14
     13:12/14
     14:13/14
     15:0% (same as black)
     */
  
  for(i=1;i<14;i++)
    {
      unsigned long val;
      
      val = (((float)i/14)*0xffff);
      colors[i+1].pixel = i+1;
      colors[i+1].red = 
	colors[i+1].green = 
	  colors[i+1].blue = val;
      colors[i+1].flags = DoRed | DoGreen | DoBlue;
    }
  
  colors[white].pixel = white;
  colors[white].red = colors[white].green = colors[white].blue =
    0xffff;
  colors[white].flags = DoRed | DoGreen | DoBlue;
  
  colors[black].pixel = black;
  colors[black].red = colors[black].green = colors[black].blue =
    0x0000;
  colors[black].flags = DoRed | DoGreen | DoBlue;
  
  /* transparency */
  colors[trans].pixel = trans;
  colors[trans].red = colors[trans].green = 0xffff;
  colors[trans].blue = 0x0000;
  colors[trans].flags = DoRed | DoGreen | DoBlue;
  
  XStoreColors(dpy,cmap,colors,ncells);
  
  return cmap;
}

Colormap
CreatePseudo8Colormap(dpy)
    Display        *dpy;
{
    Colormap        new,
                    old;
    XColor          colors[256];
    unsigned long   pixels[256];
    int             i;
    int             levels,
                    levelsq,
                    ncolors;
    float           N;
    int             ncells;
    int             remaining;
    int             firstfree = 0;

    /*
     * create a new colormap, and copy all of the old colors into it; then
     * set up the new regions for B&W and dithered color 
     */

    ncells = DisplayCells(dpy, DefaultScreen(dpy));

    old = DefaultColormap(dpy, DefaultScreen(dpy));

    /* get an idea of where the old map left off */

    XAllocColorCells(dpy, old, 0, NULL, 0, pixels, 1);
    XFreeColors(dpy, old, pixels, 1, 0);
    firstfree = pixels[0];

    for (i = 0; i < ncells; i++)
	colors[i].pixel = i;

    XQueryColors(dpy, old, colors, ncells);

    /****************************************************************
        hmmmm ... if you use AllocAll when creating the colormap,
	it generates an X error when you later try to free some
	colors (at least on Parallax server). Seems to work if
	you use AllocNone and then allocate them all. Go figure.
    ****************************************************************/

    new = XCreateColormap(dpy, DefaultRootWindow(dpy),
			  DefaultVisual(dpy, DefaultScreen(dpy)), AllocNone);

    XAllocColorCells(dpy, new, 0, NULL, 0, pixels, ncells);

    /* 
     * first build the b&w linear ramp
     */

    for (i = 0; i < PLX_BW_LEVELS; i++)
    {
	unsigned long   val;

	val = (((float) i / (PLX_BW_LEVELS - 1)) * 0xffff);
	colors[PLX_START_BW + i].pixel = PLX_START_BW + i;
	colors[PLX_START_BW + i].red =
	    colors[PLX_START_BW + i].green =
	    colors[PLX_START_BW + i].blue = val;
	colors[PLX_START_BW + i].flags = DoRed | DoGreen | DoBlue;
    }

    /* 
     * now create the 5x5x5 color dither space
     */

    levels = PLX_DITHER_LEVELS;
    levelsq = levels * levels;	/* squared */
    ncolors = levelsq * levels;	/* and cubed */
    N = 255.0 / (levels - 1.0);	/* Get size of each step */

#define rnd(x)  ((int)((x)+0.5))/* round off a float to an int */

    for (i = 0; i < PLX_DITHER_COLORS; i++)
    {
	colors[PLX_START_COLOR + i].pixel = PLX_START_COLOR + i;
	colors[PLX_START_COLOR + i].red =
	    (u_short) (rnd((i % levels) * N) * 257);
	colors[PLX_START_COLOR + i].green =
	    (u_short) (rnd(((i / levels) % levels) * N) * 257);
	colors[PLX_START_COLOR + i].blue =
	    (u_short) (rnd(((i / levelsq) % levels) * N) * 257);
	colors[PLX_START_COLOR + i].flags = DoRed | DoGreen | DoBlue;
    }

    XStoreColors(dpy, new, colors, ncells);

    /* 
     * free the cells we didn't use
     */

    remaining = ncells - (PLX_DITHER_COLORS + PLX_BW_LEVELS + firstfree);

    for (i = 0; i < remaining; i++)
    {
	pixels[i] = i + firstfree;
    }

    if (remaining)
    {
	XFreeColors(dpy, new, pixels, remaining, 0);
    }

    return new;
}

/* 
 * SetVideoProp sets the stage for video
 * window dump utility on Parallax; once you
 * know a window has YUV data, it's easy to translate
 * to B&W for printing
 */

int
SetVideoProp(win, mode)
    Window          win;
    int             mode;	/* if 0, still; if 1, live */
{

    VideoPropVal = mode ? 1 : 0;
    XChangeProperty(u_Display, win, VideoAtom, XA_INTEGER,
		    32, PropModeReplace, (unsigned char *)&VideoPropVal, 1);
}

int
ScreenIsVideo(s)
     u_Screen *s;
{
  if(s->type == STILL || s->type == VIDEO)
    return 1;
  if(s->w && WindowIsVideo(s->w))
    return 1;
  return 0;
}

int
WindowIsVideo(win)
    Window          win;
{
  Atom req_type;
  Atom got_type;
  int got_format;
  unsigned long nitems;
  unsigned long bytes_after;
  unsigned char *propval;

  XGetWindowProperty(u_Display,win,VideoAtom,0,8192,
		     False,XA_INTEGER,&got_type,&got_format,
		     &nitems,&bytes_after,&propval);
  if(got_format)
    XFree(propval);
  return got_format;
}

int
start_logging(fn)
    char           *fn;
{
    static int      foo = MAGIC_NUMBER;
    XWindowAttributes xwa;

    if (Logging)
	stop_logging();
    if (LogFp)			/* must be playing back */
	stop_playback();

    LogFp = fopen(STRLEN(fn) ? fn : LogFile, "w");
    fwrite(&foo, sizeof(int), 1, LogFp);

    XGetWindowAttributes(u_Display,
			 DefaultRootWindow(u_Display),
			 &xwa);

    root_mask = xwa.your_event_mask;

    XSelectInput(u_Display,
		 DefaultRootWindow(u_Display),
		 xwa.your_event_mask | PointerMotionMask);

    Logging = 1;

    fprintf(stderr, "LOG STARTED on '%s'\n",
	    STRLEN(fn) ? fn : LogFile);

    return 0;
}

int
stop_logging()
{
    if (!Logging || !LogFp)
	return -1;
    fclose(LogFp);
    LogFp = NULL;
    Logging = 0;

    /* restore previous event mask */

    XSelectInput(u_Display,
		 DefaultRootWindow(u_Display),
		 root_mask);

    fprintf(stderr, "LOG STOPPED\n");

    return 0;
}

int
log_event(evp)
    XEvent         *evp;
{
    Time            temp;

    if (!evp || !LogFp)
	return -1;

    switch (evp->type)
    {
    case KeyPress:
    case KeyRelease:
    case ButtonPress:
    case ButtonRelease:
    case MotionNotify:
	break;
    default:
	return 0;
	break;
    }

    if (!last_time)
	last_time = evp->xbutton.time;

    temp = evp->xbutton.time;
    evp->xbutton.time = TIME_DIFF(evp->xbutton.time, last_time);
    last_time = temp;

    fwrite(evp, sizeof(*evp), 1, LogFp);

    return 0;
}

swap_event(evp)
    XEvent         *evp;
{
    swapbytearray(evp, sizeof(XEvent) / sizeof(int));
}

/* 
 * special stuff for dealing with Parallax
 * zooms as virtual events; useful for creating
 * standalone scripted demos
 */

#define PLXZOOMON  0x00fffff0
#define PLXZOOMOFF 0x00fffff1

#define MAXH 1024		/* for bug in video extension */

void
playback_event(garbage, id)
    char           *garbage;
    XtIntervalId    id;
{
    static int      myMask = (ButtonPress | ButtonRelease | KeyPress | KeyRelease
			      | MotionNotify);
    int             delay;
    static GC       gc = (GC) NULL;
    static Window   win = (Window) NULL;

    if (!gc)
	gc = DefaultGC(u_Display, DefaultScreen(u_Display));
    if (!win)
	win = DefaultRootWindow(u_Display);

    /*
     * send current event to self; then read in next, and reset timeout 
     */

    if (LogSwapped)
	swap_event(&current);

    delay = current.xbutton.time;

    current.xany.display = u_Display;
    current.xbutton.root = win;
    current.xbutton.time = CurrentTime;
    current.xbutton.same_screen = True;

    switch (current.type)
    {
    case PLXZOOMON:

	XPlxZoomOn(u_Display, win, gc,
		   current.xbutton.x,
		   MAXH + current.xbutton.y,
		   current.xbutton.x_root,
		   current.xbutton.y_root);
	/* XXX - not sure if should flush or not */
	XFlush(u_Display);	

	break;

    case PLXZOOMOFF:

	XPlxZoomOff(u_Display, win, gc, 0, 0);
	/* XXX - not sure if should flush or not */
	XFlush(u_Display);

	break;

    case KeyPress:
    case KeyRelease:

	XSendEvent(u_Display, InputFocus,
		   True, myMask, &current);

	break;
    case ButtonPress:
    case ButtonRelease:

	XSendEvent(u_Display, PointerWindow,
		   True, myMask, &current);

	break;
    case MotionNotify:

	/*
	 * send a warp to move the cursor; but this generates MotionNotify
	 * events with the CURRENT state of the pointer, not the saved state;
	 * so send a motion event, too; when playing back, and you get a
	 * non-send_event MotionNotify, ignore it; do NOT pass it to
	 * XtDispatch() 
	 */

	XWarpPointer(u_Display, None,
		     win,
		     0, 0, 0, 0,
		     current.xmotion.x_root, current.xmotion.y_root);

	XSendEvent(u_Display, PointerWindow,
		   True, myMask, &current);

	break;
    default:
	break;
    }

    if (feof(LogFp))
	stop_playback();
    else
    {
	fread(&current, sizeof(XEvent), 1, LogFp);
	XtAddTimeOut(delay, playback_event, NULL);
    }
}

int
start_playback(fn)
    char           *fn;
{
    int             ref = MAGIC_NUMBER;
    int             foo;

    if (Logging)
	stop_logging();
    if (LogFp)			/* already playing back */
	stop_playback();

    LogFp = fopen(STRLEN(fn) ? fn : LogFile, "r");
    if (!LogFp)
    {
	fprintf(stderr, "start_playback(): can't open log file <%s>\n",
		STRLEN(fn) ? fn : LogFile);
	exit(1);
    }

    fread(&foo, sizeof(int), 1, LogFp);
    if (foo != MAGIC_NUMBER)
    {
	LogSwapped = 1;
	if (initswap(&foo, &ref))
	{
	    fprintf(stderr, "start_playback(): failed initswap()\n");
	    fprintf(stderr, "** file format error??\n");
	    stop_playback();
	    exit(1);
	}
	swapbytes(&foo);
	if (foo != MAGIC_NUMBER)
	{
	    fprintf(stderr, "start_playback(): failed swapbytes()\n");
	    fprintf(stderr, "** file format error??\n");
	    stop_playback();
	    exit(1);
	}
    }
    else
	LogSwapped = 0;

    /* 
     * read event and set timeout for it
     */

    if (feof(LogFp))
	stop_playback();
    else
    {
	fprintf(stderr, "PLAYBACK STARTED\n");

	fread(&current, sizeof(XEvent), 1, LogFp);
	playback_event(NULL, NULL);
    }
    return 0;
}

int
stop_playback()
{
    if (Logging)
	return -1;
    if (LogFp)
    {
	fclose(LogFp);
	LogFp = NULL;

	fprintf(stderr, "PLAYBACK STOPPED\n");
    }
    else
	return -1;
    return 0;
}

int
AskFor(prompt, initial, answer, no_cancel, win, x, y, fg, bg)
    char           *prompt;
    char           *initial;
    char           *answer;	/* RETURN */
    int             no_cancel;
    Window          win;	/* if 0, use current mouse coords */
    Position        x,
                    y;		/* if win, use coords relative to win */
    Pixel           fg,
                    bg;
{
    Arg             args[32];
    int             i;
    Dimension       width,
                    height;
    Position        ok_x,
                    ok_y;
    Dimension       ok_w,
                    ok_h;
    static Cursor   curs;
    XtTranslations  text_trans;
    Pixel           fg_ret,
                    bg_ret;
    char           *str;
    Window          focus_win = (Window)0;
    static int      revert_to = 0;
    int             no_colors = 0;
    int             new_colors = 1;
    int             output_only = 0;

    if(!strcmp(initial,NO_TEXT_STRING))
       output_only = 1;

    if((u_DpyIsPseudo8 || u_DpyIsPseudo4) && !MuseOptions.monochrome)
      {
	if(u_DpyIsPseudo8)
	  {
	    if(!MDD.inited || fg != MDD.fg || bg != MDD.bg)
	      new_colors = 1;
	    MDD.fg = fg;
	    MDD.bg = bg;
	  }
	else if(u_DpyIsPseudo4)
	  {
	    if(!MDD.inited || lum_table[fg] != MDD.fg || lum_table[bg] != MDD.bg)
	      new_colors = 1;
	    MDD.fg = lum_table[fg];
	    MDD.bg = lum_table[bg];
	  }
      }
    else
      {
	no_colors = 1;
	new_colors = 0;
	MDD.bg = bg;
	MDD.fg = fg;
      }

    /* 
     * trying to fix refresh problems under dialog box
     */

    XFlush(u_Display);
    XSync(u_Display, 0);
    ProcessMuseEvents();

    if (!MDD.inited)
    {
	curs = XCreateFontCursor(XtDisplay(MuseBB), XC_left_ptr);

	i = 0;
	XtSetArg(args[i], XtNallowShellResize, True);
	i++;
	XtSetArg(args[i], XtNborderWidth, 4);
	i++;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNborderColor, MDD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;
	XtSetArg(args[i], XtNinput, True);
	i++;

	MDD.popup = XtCreatePopupShell("MuseDialog", 
				       transientShellWidgetClass,
				       MuseBB, args, i);

	MDD.font1 = XLoadQueryFont(XtDisplay(MuseBB), 
				   "*times-bold-r-normal--18-*");
	MDD.font2 = XLoadQueryFont(XtDisplay(MuseBB), 
				   "*times-medium-r-normal--18-*");

	/* R3 text widget doesn't do fg/bg with SetValues; must repeat below */

	strcpy(MDD.dlog_string, "MMMMMMMMMMMMMMMMMMMM");

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MDD.bg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MDD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNdefaultDistance, 6);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

	MDD.form = XtCreateManagedWidget("form", formWidgetClass,
					 MDD.popup, args, i);

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MDD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MDD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MDD.fg);
	    i++;
	  }
#ifndef X11R3
	XtSetArg(args[i], XtNlabel, 
		 "MMMMMMMMMMMMMMMMMMMMMM\nMMMMMMMMMMMMMMMMMMMM");
#else
	XtSetArg(args[i], XtNlabel, "MMMMMMMMMMMMMMMMMMMMMMMMM");
#endif

	i++;
	XtSetArg(args[i], XtNfont, MDD.font1);
	i++;
	XtSetArg(args[i], XtNhorizDistance, 4);
	i++;
	XtSetArg(args[i], XtNleft, XtChainLeft);
	i++;
	XtSetArg(args[i], XtNvertDistance, 10);
	i++;
	XtSetArg(args[i], XtNtop, XtChainTop);
	i++;
	XtSetArg(args[i], XtNborderWidth, 0);
	i++;
	XtSetArg(args[i], XtNright, XtChainRight);
	i++;
	XtSetArg(args[i], XtNjustify, XtJustifyLeft);
	i++;
	XtSetArg(args[i], XtNinternalWidth, 0);
	i++;
	XtSetArg(args[i], XtNinternalHeight, 2);
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

	MDD.label = XtCreateManagedWidget("label", labelWidgetClass,
					  MDD.form, args, i);

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MDD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MDD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MDD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNfont, MDD.font2);
	i++;
	XtSetArg(args[i], XtNhorizDistance, 4);
	i++;
	XtSetArg(args[i], XtNleft, XtChainLeft);
	i++;
	XtSetArg(args[i], XtNright, XtChainRight);
	i++;
	XtSetArg(args[i], XtNvertDistance, 4);
	i++;
	XtSetArg(args[i], XtNfromVert, MDD.label);
	i++;
	XtSetArg(args[i], XtNresizable, True);
	i++;
	XtSetArg(args[i], XtNstring, MDD.dlog_string);
	i++;
#ifdef X11R3
	XtSetArg(args[i], XtNtextOptions, resizeWidth);
	i++;
	XtSetArg(args[i], XtNlength, 256);
	i++;
#else				/* X11R4 */
	XtSetArg(args[i], XtNresize, XawtextResizeWidth);
	i++;
	XtSetArg(args[i], XtNtype, XawAsciiString);
	i++;
#endif
	XtSetArg(args[i], XtNeditType, XttextEdit);
	i++;
	XtSetArg(args[i], XtNborderWidth, 1);
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

#ifdef X11R3
	MDD.text = XtCreateManagedWidget("text", asciiStringWidgetClass,
					 MDD.form, args, i);
#else				/* X11R4 */
	MDD.text = XtCreateManagedWidget("text", asciiTextWidgetClass,
					 MDD.form, args, i);
#endif

	XtAddActions(popupActions, 1);
	text_trans = 
	  XtParseTranslationTable("<Key>Return:	return_action()");
	XtOverrideTranslations(MDD.text, text_trans);

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MDD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MDD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MDD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNlabel, "Okay");
	i++;
	XtSetArg(args[i], XtNfont, MDD.font1);
	i++;
	XtSetArg(args[i], XtNhorizDistance, 4);
	i++;
	XtSetArg(args[i], XtNleft, XtChainLeft);
	i++;
	XtSetArg(args[i], XtNbottom, XtChainBottom);
	i++;
	XtSetArg(args[i], XtNvertDistance, 10);
	i++;
	XtSetArg(args[i], XtNfromVert, MDD.text);
	i++;
	XtSetArg(args[i], XtNborderWidth, 2);
	i++;
	XtSetArg(args[i], XtNinternalWidth, 6);
	i++;
	XtSetArg(args[i], XtNinternalHeight, 2);
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

	MDD.okay = XtCreateManagedWidget("okay", commandWidgetClass,
					 MDD.form, args, i);
	XtAddCallback(MDD.okay, XtNcallback, dlog_button_press, "Okay");

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MDD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MDD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MDD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNlabel, "Cancel");
	i++;
	XtSetArg(args[i], XtNfont, MDD.font1);
	i++;
	XtSetArg(args[i], XtNhorizDistance, 10);
	i++;
	XtSetArg(args[i], XtNfromHoriz, MDD.okay);
	i++;
	XtSetArg(args[i], XtNbottom, XtChainBottom);
	i++;
	XtSetArg(args[i], XtNvertDistance, 10);
	i++;
	XtSetArg(args[i], XtNfromVert, MDD.text);
	i++;
	XtSetArg(args[i], XtNborderWidth, 2);
	i++;
	XtSetArg(args[i], XtNinternalWidth, 6);
	i++;
	XtSetArg(args[i], XtNinternalHeight, 2);
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

	MDD.cancel = XtCreateManagedWidget("cancel", commandWidgetClass,
					   MDD.form, args, i);
	XtAddCallback(MDD.cancel, XtNcallback, dlog_button_press, "Cancel");

	XtRealizeWidget(MDD.popup);

#ifdef notdef
	{
	    XSetWindowAttributes xswa;
	    XWMHints wmhints;

	    xswa.override_redirect = True;
	    XChangeWindowAttributes(XtDisplay(MDD.popup),
				    XtWindow(MDD.popup),
				    CWOverrideRedirect,
				    &xswa);
	}
#endif

	XSetWindowColormap(XtDisplay(MDD.popup), 
			   XtWindow(MDD.popup), u_Colormap);
	XSetWindowColormap(XtDisplay(MDD.popup), 
			   XtWindow(MDD.form), u_Colormap);
	XSetWindowColormap(XtDisplay(MDD.popup), 
			   XtWindow(MDD.label), u_Colormap);
	XSetWindowColormap(XtDisplay(MDD.popup), 
			   XtWindow(MDD.text), u_Colormap);
	XSetWindowColormap(XtDisplay(MDD.popup), 
			   XtWindow(MDD.okay), u_Colormap);
	XSetWindowColormap(XtDisplay(MDD.popup), 
			   XtWindow(MDD.cancel), u_Colormap);

#ifdef notdef
	XtAddEventHandler(MDD.popup, EnterWindowMask,
			  FALSE,
			  dlog_focus, &revert_to);
#endif

	XtAddEventHandler(MDD.popup, KeyPressMask | KeyReleaseMask,
			  FALSE,
			  redirect_keys, MDD.text);
	XtAddEventHandler(MDD.form, KeyPressMask | KeyReleaseMask,
			  FALSE,
			  redirect_keys, MDD.text);
	XtAddEventHandler(MDD.label, KeyPressMask | KeyReleaseMask,
			  FALSE,
			  redirect_keys, MDD.text);
	XtAddEventHandler(MDD.okay, KeyPressMask | KeyReleaseMask,
			  FALSE,
			  redirect_keys, MDD.text);
	XtAddEventHandler(MDD.cancel, KeyPressMask | KeyReleaseMask,
			  FALSE,
			  redirect_keys, MDD.text);

	MDD.inited = 1;
    }

    /* 
     * remake text widget if need to
     */
    if (new_colors)
    {
#ifdef X11R3

	XtDestroyWidget(MDD.text);

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MDD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MDD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MDD.fg);
	    i++;
	  }

	XtSetArg(args[i], XtNfont, MDD.font2);
	i++;
	XtSetArg(args[i], XtNhorizDistance, 4);
	i++;
	XtSetArg(args[i], XtNleft, XtChainLeft);
	i++;
	XtSetArg(args[i], XtNright, XtChainRight);
	i++;
	XtSetArg(args[i], XtNvertDistance, 4);
	i++;
	XtSetArg(args[i], XtNfromVert, MDD.label);
	i++;
	XtSetArg(args[i], XtNresizable, True);
	i++;
	XtSetArg(args[i], XtNstring, MDD.dlog_string);
	i++;
	XtSetArg(args[i], XtNlength, 256);
	i++;
	XtSetArg(args[i], XtNtextOptions, resizeWidth);
	i++;

	XtSetArg(args[i], XtNeditType, XttextEdit);
	i++;
	XtSetArg(args[i], XtNborderWidth, 1);
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

	MDD.text = XtCreateManagedWidget("text", asciiStringWidgetClass,
					 MDD.form, args, i);

	XtOverrideTranslations(MDD.text, text_trans);

	XSetWindowColormap(XtDisplay(MDD.popup), 
			   XtWindow(MDD.text), u_Colormap);
#endif /* X11R3 */

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MDD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MDD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MDD.fg);
	    i++;
	    
	    XtSetValues(MDD.form, args, i);
	    XtSetValues(MDD.label, args, i);
#ifndef X11R3
	    XtSetValues(MDD.text, args, i);
#endif
	    XtSetValues(MDD.okay, args, i);
	    XtSetValues(MDD.cancel, args, i);
	  }
    }

    if (no_cancel)
	XtUnmanageChild(MDD.cancel);
    else
	XtManageChild(MDD.cancel);

    if (output_only)
	XtUnmanageChild(MDD.text);
    else
	XtManageChild(MDD.text);

    if (initial && initial[0])
	strcpy(MDD.dlog_string, initial);
    else
	MDD.dlog_string[0] = 0;

#ifdef X11R3
    XtTextSetLastPos(MDD.text, strlen(MDD.dlog_string));
#else /* X11R4 */
    i = 0;
    XtSetArg(args[i], XtNstring, MDD.dlog_string);
    i++;
    XtSetValues(MDD.text, args, i);
#endif
    XtTextSetInsertionPoint(MDD.text, strlen(MDD.dlog_string));
    XtTextDisplay(MDD.text);

    i = 0;
    XtSetArg(args[i], XtNlabel, prompt);
    i++;

    XtSetValues(MDD.label, args, i);

    i = 0;
    if(!no_colors)
      {
	XtSetArg(args[i], XtNbackground, MDD.bg);
	i++;
	XtSetArg(args[i], XtNborderColor, MDD.fg);
	i++;
        XtSetArg(args[i], XtNforeground, fg);
        i++;
      }

    XtSetArg(args[i], XtNcursor, curs);
    i++;

    XtSetValues(MDD.popup, args, i);
    XtSetValues(MDD.form, args, i);
    XtSetValues(MDD.label, args, i);
    XtSetValues(MDD.okay, args, i);
    XtSetValues(MDD.cancel, args, i);

    if (x < 0)
	x = 0;
    if (y < 0)
	y = 0;

    if (win == 0)
    {
	Window          root,
	                child;
	int             root_x,
	                root_y,
	                win_x,
	                win_y,
	                mask;

	XQueryPointer(XtDisplay(MDD.popup),
		      DefaultRootWindow(XtDisplay(MDD.popup)),
		      &root, &child,
		      &root_x, &root_y, &win_x, &win_y, (unsigned int *)&mask);

	x = root_x;
	y = root_y;
    }
    else
    {
	Window          child;
	int             root_x,
	                root_y;

	XTranslateCoordinates(XtDisplay(MDD.popup), win,
			      DefaultRootWindow(XtDisplay(MDD.popup)),
			      x, y, &root_x, &root_y, &child);

	x = root_x;
	y = root_y;
    }

    i = 0;
    XtSetArg(args[i], XtNwidth, &width);
    i++;
    XtSetArg(args[i], XtNheight, &height);
    i++;

    XtGetValues(MDD.popup, args, i);

    /* 
     * fix up width of text; should be width-(2*defaultDistance)-(2*bw)
     */
    i = 0;
    XtSetArg(args[i], XtNwidth, (width - 14));
    i++;

    XtSetValues(MDD.text, args, i);

    i = 0;
    XtSetArg(args[i], XtNx, &ok_x);
    i++;
    XtSetArg(args[i], XtNy, &ok_y);
    i++;
    XtSetArg(args[i], XtNwidth, &ok_w);
    i++;
    XtSetArg(args[i], XtNheight, &ok_h);
    i++;

    XtGetValues(MDD.okay, args, i);

    if (win == 0)
    {
	x = x - ok_x - ok_w + 4;
	y = y - ok_y - ok_h + 4;
    }

    if (x < 0)
	x = 0;
    if (y < 0)
	y = 0;

    if ((x + width) > WidthOfScreen(XtScreen(MDD.popup)))
	x = WidthOfScreen(XtScreen(MDD.popup)) - width;

    if ((y + height) > HeightOfScreen(XtScreen(MDD.popup)))
	y = HeightOfScreen(XtScreen(MDD.popup)) - height;

    i = 0;
    XtSetArg(args[i], XtNx, x);
    i++;
    XtSetArg(args[i], XtNy, y);
    i++;

    XtSetValues(MDD.popup, args, i);

    MDD.waiting = 1;

    /* 
     * hackery: get current input focus, and claim to be
     * a transient for that window by setting the WM_TRANSIENT_FOR 
     * property; having trouble getting mwm to give us focus, since
     * MuseBB is not really the parent window; we'll also try
     * setting the focus explicitly
     */

    XGetInputFocus(u_Display, &focus_win, &revert_to);
    if(focus_win)
      {
	Atom trans_atom;

	trans_atom = XInternAtom(u_Display, "WM_TRANSIENT_FOR", False);
	XChangeProperty(u_Display, XtWindow(MDD.popup), 
			trans_atom, XA_WINDOW, 
			32, PropModeReplace, 
			(unsigned char *)&focus_win, 1);
      }

    XtPopup(MDD.popup, XtGrabExclusive);

/*
    XSetInputFocus(u_Display, XtWindow(MDD.text), revert_to, 
		   LastMuseEventTime);
*/

    while (MDD.waiting)
    {
	XEvent          ev;

	XtNextEvent(&ev);

	switch (ev.type)
	{
	case ButtonPress:
	case ButtonRelease:
	    if (ev.xbutton.window == XtWindow(MDD.text) ||
		ev.xbutton.window == XtWindow(MDD.okay) ||
		ev.xbutton.window == XtWindow(MDD.cancel))
		break;
	    XBell(XtDisplay(MDD.popup), 0);
	    continue;
	    break;
	case KeyPress:
	case KeyRelease:
	    if (ev.xbutton.window == XtWindow(MDD.popup) ||
		ev.xbutton.window == XtWindow(MDD.form) ||
		ev.xbutton.window == XtWindow(MDD.label) ||
		ev.xbutton.window == XtWindow(MDD.text) ||
		ev.xbutton.window == XtWindow(MDD.okay) ||
		ev.xbutton.window == XtWindow(MDD.cancel))
		break;
	    XBell(XtDisplay(MDD.popup), 0);
	    continue;
	    break;
	default:
	    break;
	}

	XtDispatchEvent(&ev);
	handle_default_exposures(&ev);
	handle_msgs(&ev);
    }

    XtPopdown(MDD.popup);
    XFlush(XtDisplay(MDD.popup));

#ifndef X11R3
    i=0;
    XtSetArg(args[i], XtNstring, &str);
    i++;
    XtGetValues(MDD.text, args, i);
    strcpy(MDD.dlog_string,str);
#endif
    strcpy(answer, MDD.dlog_string);

    if (MDD.hit_okay)
	return 0;
    else
	return -1;
}

int
AskForString(prompt, initial, answer, no_cancel, fg, bg)
    char           *prompt;
    char           *initial;
    char           *answer;	/* RETURN */
    int             no_cancel;
    Pixel           fg,
                    bg;
{
  return AskFor(prompt, initial, answer, no_cancel, 0, 0, 0, fg, bg);
}

int
InformUser(message,fg,bg)
     char *message;
     Pixel fg,bg;
{
  char reply[256];

  return AskForString(message, NO_TEXT_STRING, reply, 1, fg, bg);
}

int
PickFromListArray(prompt,list,no_cancel,ret_index,ret_str,fg,bg)
     char *prompt;
     char *list[];  /* last entry MUST be NULL! */
     int   no_cancel, fg, bg;
     char *ret_index;
     char *ret_str;
{
  Arg args[32];
  int i;
  int no_colors = 0;
  int new_colors = 0;
  Window root, parent;
  Window *children;
  int nchildren;
  Cursor curs;
  Dimension       width,
                    height;
	Window          child;
	int             root_x,
	                root_y,
	                win_x,
	                win_y,
	                mask, x, y;

  if((u_DpyIsPseudo8 || u_DpyIsPseudo4) && !MuseOptions.monochrome)
    {
      if(u_DpyIsPseudo8)
	{
	  if(!MPLD.inited || fg != MPLD.fg || bg != MPLD.bg)
	    new_colors = 1;
	  MPLD.fg = fg;
	  MPLD.bg = bg;
	}
      else if(u_DpyIsPseudo4)
	{
	  if(!MPLD.inited || lum_table[fg] != MPLD.fg || lum_table[bg] != MPLD.bg)
	    new_colors = 1;
	  MPLD.fg = lum_table[fg];
	  MPLD.bg = lum_table[bg];
	}
    }
  else
    {
      no_colors = 1;
      new_colors = 0;
      MPLD.fg = fg;
      MPLD.bg = bg;
    }

    /* 
     * trying to fix refresh problems under dialog box
     */

    XFlush(u_Display);
    XSync(u_Display, 0);
    ProcessMuseEvents();

    if (!MPLD.inited)
    {
	curs = XCreateFontCursor(XtDisplay(MuseBB), XC_left_ptr);

	i = 0;
	XtSetArg(args[i], XtNallowShellResize, True);
	i++;
	XtSetArg(args[i], XtNborderWidth, 4);
	i++;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNborderColor, MPLD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;
	XtSetArg(args[i], XtNinput, True);
	i++;

	MPLD.popup = XtCreatePopupShell("MusePickList", 
				       transientShellWidgetClass,
				       MuseBB, args, i);

	MPLD.font1 = XLoadQueryFont(XtDisplay(MuseBB), 
				   "*times-bold-r-normal--18-*");
	MPLD.font2 = XLoadQueryFont(XtDisplay(MuseBB), 
				   "*times-medium-r-normal--14-*");

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MPLD.bg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MPLD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNdefaultDistance, 6);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

	MPLD.form = XtCreateManagedWidget("form", formWidgetClass,
					 MPLD.popup, args, i);

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MPLD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MPLD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MPLD.fg);
	    i++;
	  }
#ifndef X11R3
	XtSetArg(args[i], XtNlabel, 
		 "MMMMMMMMMMMMMMMMMM\nMMMMMMMMMMMMMMMMMMMM");
#else
	XtSetArg(args[i], XtNlabel, "MMMMMMMMMMMMMMMMMMMMMM");
#endif
	i++;
	XtSetArg(args[i], XtNfont, MPLD.font1);
	i++;
	XtSetArg(args[i], XtNhorizDistance, 4);
	i++;
	XtSetArg(args[i], XtNleft, XtChainLeft);
	i++;
	XtSetArg(args[i], XtNvertDistance, 10);
	i++;
	XtSetArg(args[i], XtNtop, XtChainTop);
	i++;
	XtSetArg(args[i], XtNborderWidth, 0);
	i++;
	XtSetArg(args[i], XtNright, XtChainRight);
	i++;
	XtSetArg(args[i], XtNjustify, XtJustifyLeft);
	i++;
	XtSetArg(args[i], XtNinternalWidth, 0);
	i++;
	XtSetArg(args[i], XtNinternalHeight, 2);
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

	MPLD.label = XtCreateManagedWidget("label", labelWidgetClass,
					  MPLD.form, args, i);

	i = 0;
	XtSetArg(args[i], XtNallowVert, True); 
	i++;
	XtSetArg(args[i], XtNallowHoriz, True); 
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
/*
	XtSetArg(args[i], XtNwidth, 250);
	i++;
*/
	XtSetArg(args[i], XtNvertDistance, 10);
	i++;
	XtSetArg(args[i], XtNfromVert, MPLD.label);
	i++;
/*
	XtSetArg(args[i], XtNright, XtChainRight);
	i++;
*/
	XtSetArg(args[i], XtNleft, XtChainLeft);
	i++;
/*
	XtSetArg(args[i], XtNjustify, XtJustifyLeft);
	i++;
*/
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MPLD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MPLD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MPLD.fg);
	    i++;
	  }

	MPLD.view = XtCreateManagedWidget("view", viewportWidgetClass, 
					  MPLD.form, args, i);
	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MPLD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MPLD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MPLD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNfont, MPLD.font2);
	i++;
	XtSetArg(args[i], XtNborderWidth, 1);
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;
	/*
	 * this should force list to be ten lines long
	 */
	XtSetArg(args[i], XtNlist, default_list);
	i++;
	XtSetArg(args[i], XtNdefaultColumns, 1); 
	i++;
	XtSetArg(args[i], XtNforceColumns, True); 
	i++;

	MPLD.list = XtCreateManagedWidget("list", listWidgetClass,
					 MPLD.view, args, i);
	XtAddCallback(MPLD.list, XtNcallback, PickItem, (XtPointer)NULL);

	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MPLD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MPLD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MPLD.fg);
	    i++;
	  }
	XtSetArg(args[i], XtNlabel, "Cancel");
	i++;
	XtSetArg(args[i], XtNfont, MPLD.font1);
	i++;
	XtSetArg(args[i], XtNhorizDistance, 4);
	i++;
	XtSetArg(args[i], XtNleft, XtChainLeft);
	i++;
	XtSetArg(args[i], XtNbottom, XtChainBottom);
	i++;
	XtSetArg(args[i], XtNvertDistance, 10);
	i++;
	XtSetArg(args[i], XtNfromVert, MPLD.view);
	i++;
	XtSetArg(args[i], XtNborderWidth, 2);
	i++;
	XtSetArg(args[i], XtNinternalWidth, 6);
	i++;
	XtSetArg(args[i], XtNinternalHeight, 2);
	i++;
	XtSetArg(args[i], XtNcursor, curs);
	i++;
	XtSetArg(args[i], XtNcolormap, u_Colormap);
	i++;

	MPLD.cancel = XtCreateManagedWidget("cancel", commandWidgetClass,
					 MPLD.form, args, i);
	XtAddCallback(MPLD.cancel, XtNcallback, dlog_button_press, "Cancel");

	XtRealizeWidget(MPLD.popup);

#ifdef notdef
	{
	    XSetWindowAttributes xswa;
	    XWMHints wmhints;

	    xswa.override_redirect = True;
	    XChangeWindowAttributes(XtDisplay(MPLD.popup),
				    XtWindow(MPLD.popup),
				    CWOverrideRedirect,
				    &xswa);
	}
#endif

	XSetWindowColormap(XtDisplay(MPLD.popup), 
			   XtWindow(MPLD.popup), u_Colormap);
	XSetWindowColormap(XtDisplay(MPLD.popup), 
			   XtWindow(MPLD.form), u_Colormap);
	XSetWindowColormap(XtDisplay(MPLD.popup), 
			   XtWindow(MPLD.label), u_Colormap);
	XSetWindowColormap(XtDisplay(MPLD.popup), 
			   XtWindow(MPLD.view), u_Colormap);
	XSetWindowColormap(XtDisplay(MPLD.popup), 
			   XtWindow(MPLD.list), u_Colormap);
	XSetWindowColormap(XtDisplay(MPLD.popup), 
			   XtWindow(MPLD.cancel), u_Colormap);

	/*
	 * try to set up viewport so it remains 10 lines long
	 */

	i = 0;
	XtSetArg(args[i], XtNheight, &height);
	i++;

	XtGetValues(MPLD.view, args, i);

        i = 0;
	XtSetArg(args[i], XtNheight, height);
	i++;

	XtSetValues(MPLD.view, args, i);

	MPLD.inited = 1;

    } /* end if(!MPLD.inited) */

    if (new_colors)
    {
	i = 0;
	if(!no_colors)
	  {
	    XtSetArg(args[i], XtNbackground, MPLD.bg);
	    i++;
	    XtSetArg(args[i], XtNforeground, MPLD.fg);
	    i++;
	    XtSetArg(args[i], XtNborderColor, MPLD.fg);
	    i++;
	  }
	XtSetValues(MPLD.popup, args, i);
	XtSetValues(MPLD.form, args, i);
	XtSetValues(MPLD.label, args, i);
	XtSetValues(MPLD.view, args, i);
	XtSetValues(MPLD.list, args, i);
	XtSetValues(MPLD.cancel, args, i);
    }

    if (no_cancel)
	XtUnmanageChild(MPLD.cancel);
    else
	XtManageChild(MPLD.cancel);

    i = 0;
    XtSetArg(args[i], XtNlabel, prompt);
    i++;

    XtSetValues(MPLD.label, args, i);

    /*
     * reset the list resource
     */
  
#ifdef X11R3
    XtListChange(MPLD.list,list,-1,-1,True);
#else
    XawListChange(MPLD.list,list,-1,-1,True);
#endif


	XQueryPointer(XtDisplay(MPLD.popup),
		      DefaultRootWindow(XtDisplay(MPLD.popup)),
		      &root, &child,
		      &root_x, &root_y, &win_x, &win_y, (unsigned int *)&mask);

       x = root_x;
       y = root_y;


    i = 0;
    XtSetArg(args[i], XtNwidth, &width);
    i++;
    XtSetArg(args[i], XtNheight, &height);
    i++;
   

    XtGetValues(MPLD.popup, args, i);

    /* 
     * fix up width of viewport; should be width-(2*defaultDistance)-(2*bw)
     */
    i = 0;
    XtSetArg(args[i], XtNwidth, (width - 14));
    i++;

/*
    XtSetValues(MPLD.view, args, i);
*/
/*
    XQueryTree(XtDisplay(MPLD.view), XtWindow(MPLD.view),
	       &root, &parent, &children, (unsigned int *)&nchildren);
*/

    x = root_x - 30;
    y = root_y - (height / 2);

    if(x < 0)
	x = 0;
    if(y < 0)
        y = 0;

    if ((x + width) > WidthOfScreen(XtScreen(MPLD.popup)))
	x = WidthOfScreen(XtScreen(MPLD.popup)) - width;

    if ((y + height) > HeightOfScreen(XtScreen(MPLD.popup)))
	y = HeightOfScreen(XtScreen(MPLD.popup)) - height;

    i=0;
    XtSetArg(args[i], XtNx, x);
    i++;
    XtSetArg(args[i], XtNy, y);
    i++;

    XtSetValues(MPLD.popup, args, i);

    MPLD.waiting = 1;
    MPLD.cancel = 0;

    XtPopup(MPLD.popup, XtGrabExclusive);

    XQueryTree(XtDisplay(MPLD.view), XtWindow(MPLD.view),
	       &root, &parent, &children, (unsigned int *)&nchildren);

    while (MPLD.waiting)
    {
	XEvent          ev;
	int i;

	XtNextEvent(&ev);

	switch (ev.type)
	{
	case ButtonPress:
	case ButtonRelease:
	    if (ev.xbutton.window == XtWindow(MPLD.list) ||
		ev.xbutton.window == XtWindow(MPLD.view) ||
		ev.xbutton.window == XtWindow(MPLD.cancel) )
		break;
	    for(i=0;i<nchildren;i++)
	      if(ev.xbutton.window == children[i])
		break;
            if(i < nchildren)
		break;
	    XBell(XtDisplay(MPLD.popup), 0);
	    continue;
	default:
	    break;
	}

	XtDispatchEvent(&ev);
	handle_default_exposures(&ev);
	handle_msgs(&ev);
    }

    XtPopdown(MPLD.popup);
    XFlush(XtDisplay(MPLD.popup));

    if(nchildren)
      XFree((char *)children);

    if (MPLD.hit_cancel)
	return -1;
    else
      {
	sprintf(ret_index,"%d",MPLD.list_index);
	sprintf(ret_str,"%s",MPLD.list_string);
	return 0;
      }
}

int
PickFromListString(prompt,string,no_cancel,ret_index,ret_str,fg,bg)
     char *prompt;
     char  string[];  /* '\n' separates list items */
     int   no_cancel, fg, bg;
     char *ret_index;
     char *ret_str;
{
  char **str;
  int ret;
  int nlines = 0;
  int len;
  char *p = string;
  int i;

  len = strlen(string);
  for(i=0;i<len;i++)
    if(string[i] == '\n')
      ++nlines;

  nlines = 0;
  str = (char **) calloc(nlines+2);
  for(i=0;i<len;i++)
    {
      if(string[i] == '\n')
	{
	  string[i] = 0;
	  str[nlines] = p;
	  p = &string[i+1];
	  ++nlines;
	}
    }
  str[nlines] = p;
  str[nlines+1] = NULL;
  
  ret = PickFromListArray(prompt,str,no_cancel,ret_index,ret_str,fg,bg);

  /* restore string to original state */

  for(i=0;i<len;i++)
    if(string[i] == 0)
      string[i] = '\n';
  string[len] = 0;

  /* free up temporary array */

  free(str);

  return ret;
}

int
u_SetSelection(str)
     char *str;
{
  if(!str || !str[0])
    return -1;
  Select(MuseBB,str);
  XStoreBytes(u_Display,str,strlen(str));
  return 0;
}

/* Following selection stuff is cribbed from Jud Harward's 
	fontmenu.c program */

/*
 *  Select(w,string) sets the primary selection to the selected
 *  string, with widget 'w' as owner.
 */

static void
Select(w, string)
    Widget          w;
    char           *string;
{
    int             ret;
    char *          strsave();

    timeLastSelection = LastMuseEventTime;

    if(MuseExportString)
      free(MuseExportString);
    MuseExportString = strsave(string);

    ret = XtOwnSelection(w,
			 XA_PRIMARY,
			 timeLastSelection,
			 furnishString,
			 loseString,
			 NULL);

}


/* this is the XtLoseSelectionProc */

static void
loseString(w, selection)
     Widget w;
     Atom *selection;
{
  if(MuseExportString)
    free(MuseExportString);
  MuseExportString = (char *)NULL;
}

/*
 *  This is the XtConvertSelectionProc for the selected string.
 *  Targets TARGETS and TIMESTAMP are included for ICCCM compatibility.
 *  Note that Xmu targets are really macros with the display as
 *  argument, and are resolved at run time via a call to XmuInternAtom.
 */

static          Boolean
furnishString(widget, selection, target, type, value, length, format)
    Widget          widget;
    Atom           *selection;
    Atom           *target;
    Atom           *type;
    caddr_t        *value;
    unsigned long  *length;
    int            *format;
{

  char *str;

    if (*selection != XA_PRIMARY)
    {
	fprintf(stderr, "Bad selection type\n");
	exit(1);
    }
    if (*target == XA_TARGETS(u_Display))
    {

	*type = XA_ATOM;
	*value = XtCalloc(1, sizeof(Atom));
	**((Atom **) value) = XA_STRING;
	*length = 1;
	*format = 32;
	return (True);

    }
    else
    if (*target == XA_TIMESTAMP(u_Display))
    {

	*type = XA_INTEGER;
	*value = XtCalloc(1, sizeof(int));
	**((int **) value) = timeLastSelection;
	*length = 1;
	*format = 32;
	return (True);

    }
    else
    {

	switch (*target)
	{

	case XA_STRING:
	    *type = XA_STRING;
	    *length = strlen(MuseExportString);
	    *value = XtCalloc(1, *length + 1);
	    strcpy(*value, MuseExportString);
	    *format = 8;
	    return (True);

	default:
	    return (False);
	}
    }
}
