/*
 * @(#)xdino.c
 *
 * Copyright 1995 - 2010  David A. Bagley, bagleyd@tux.org
 *
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of the author not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * 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.
 */

/* Driver file for Dino */

#ifndef WINVER
#include "version.h"
static const char aboutHelp[] = {
"Dino Version " VERSION "\n"
"Send bugs (reports or fixes) to the author: "
"David Bagley <bagleyd@tux.org>\n"
"The latest version is at: "
"http://www.tux.org/~bagleyd/puzzles.html"
};

static const char optionsHelp[] = {
"[-geometry [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]]\n"
"[-display [{host}]:[{vs}]] [-[no]mono] [-[no]{reverse|rv}]\n"
"[-{foreground|fg} {color}] [-{background|bg} {color}]\n"
"[-face{0|1|2|3|4|5} {color}] [-{border|bd} {color}]\n"
"[-delay msecs] [-[no]sound] [-moveSound {filename}]\n"
"[-{font|fn} {fontname}] [-view {int}]\n"
"[-{mode {int} | both}] [-[no]orient] [-[no]practice]\n"
"[-userName {string}] [-scoreFile {filename}] [-scores]\n"
"[-version]"
};
#endif

#if defined(HAVE_MOTIF) || defined(WINVER)
static const char descriptionHelp[] = {
"The period 2 turning (i.e. the edges turn with 180 "
"degree intervals) was suggested by Derek Bosch\n"
"<bosch@sgi.com>.  The physical puzzle has period 3 "
"turning (i.e. the points turn with 120 degree\n"
"intervals) is called a Triangle - 4 Cube - Dinosaur "
"with colored dinosaurs on it, (or Triangle - 4 Cube -\n"
"6 Colors with solid colors, (a 4 color and a 2 color "
"are also available with point oriented coloring))\n"
"manufactured by The International Puzzle & Games "
"(Great Animation International Corporation)."
};

static const char featuresHelp[] = {
"Press \"mouse-left\" button to move a piece.  Release "
"\"mouse-left\" button on a piece on the same face.\n"
"The pieces will then turn towards where the mouse "
"button was released.\n"
"\n"
"Click \"mouse-center\" button, or press \"P\" or \"p\" "
"keys, to toggle the practice mode (in practice mode the\n"
"record should say \"practice\").  This is good for learning "
"moves and experimenting.\n"
"\n"
"Click \"mouse-right\" button, or press \"Z\" or \"z\" "
"keys, to randomize the puzzle (this must be done first\n"
"to set a new record).\n"
"\n"
"Press \"G\" or \"g\" keys to get a saved puzzle.\n"
"\n"
"Press \"W\" or \"w\" keys to save (write) a puzzle.\n"
"\n"
"Press \"U\" or \"u\" keys to undo a move.\n"
"\n"
"Press \"R\" or \"r\" keys to redo a move.\n"
"\n"
"Press \"C\" or \"c\" keys to clear the puzzle.\n"
"\n"
"\"S\" or \"s\" keys to start auto-solver.  Only works for "
"Period 3 mode.\n"
"\n"
"Press \"O\" or \"o\" keys to toggle the orient mode.  One "
"has to orient the faces in orient mode, besides\n"
"getting all the faces to be the same color.  To do this "
"one has to get the lines to be oriented in\n"
"the same direction.  This does add complexity (ever so "
"slightly, there are only 2 possibilities) so there\n"
"are 2 sets of records.\n"
"\n"
"Press \"2\", \"3\", \"B\", or \"b\" keys (not the keypad "
"2, 3) to change modes to Period 2, Period 3, or Both.\n"
"\n"
"Press \"V\" or \"v\" keys to change the view of the cube.\n"
"\n"
"Press \">\" or \".\" keys to speed up the movement of pieces.\n"
"\n"
"Press \"<\" or \",\" keys to slow down the movement of pieces.\n"
"\n"
"Press \"@\" key to toggle the sound.\n"
"\n"
"Press \"Esc\" key to hide program.\n"
"\n"
"Press \"Q\", \"q\", or \"CTRL-C\" keys to kill program.\n"
"\n"
"Use the key pad or arrow keys to move without the mouse.\n"
"Key pad is defined for Dino2d as:\n"
"7 8 9  Upper Left, Up, Upper Right\n"
"  ^\n"
"4< >6  Left, Right\n"
"  v\n"
"1 2 3  Lower Left, Down, Lower Right\n"
"Note: Up, Left, Right, and Down only work when the "
"control key is pressed and there is no analog for\n"
"Dino3d.\n"
"Key pad for Dino3d, use must use your intuition (is this "
"a cop out or what?).  The key pad is defined\n"
"differently depending on which side of the cube your mouse "
"is pointing at.\n"
"\n"
"Use the alt key and the left mouse button, keypad, or arrow "
"keys to move the center of the cube.  The 2 opposite\n"
"corners do not move.\n"
"\n"
"Use the shift keys and the left mouse button, keypad, "
"or arrow key to access \"Period 2\" turns from \"Both\" mode,\n"
"otherwise it assumes \"Period 3\" turning.  Edges turn in "
"\"Period 2\" and corners turn in \"Period 3\".  The\n"
"\"Period 2\" mode has extra cuts around the faces.\n"
"\n"
"Use the control key and the left mouse button, keypad, or "
"arrow keys to move the whole cube.  This is\n"
"not recorded as a turn."
};

static const char referencesHelp[] = {
"International Puzzles & Games Catalog."
};
#endif

static const char solveHelp[] = {
"Auto-solver: sorry, only implemented for Period 3 mode."
};

#include "file.h"
#ifdef WINVER
#include "DinoP.h"
#define TITLE "wdino"

static DinoRec widget;

#ifndef SCOREPATH
#ifdef UNIXDELIM
#define SCOREPATH "c:/WINDOWS"
#else
#define SCOREPATH "c:\\WINDOWS"
#endif
#endif
#define PRINT_MESSAGE(b) (void) MessageBox(widget.core.hWnd, (LPCSTR) b, "Warning", MB_OK);
#define SET_STARTED(w,b) w->dino.started = b
#else
#include "xwin.h"
#include <X11/Shell.h>
#include <X11/cursorfont.h>
#ifdef HAVE_MOTIF
#include <Xm/PanedW.h>
#include <Xm/RowColumn.h>
#include <Xm/Label.h>
#include <Xm/LabelG.h>
#include <Xm/MessageB.h>
#include <Xm/PushBG.h>
#include <Xm/CascadeB.h>
#include <Xm/Scale.h>
#include <Xm/ToggleB.h>
#include <Xm/ToggleBG.h>
#ifdef MOUSEBITMAPS
#include "pixmaps/mouse-l.xbm"
#include "pixmaps/mouse-r.xbm"
#endif
#define PRINT_MESSAGE(b) printState(message, b)
#else
#define PRINT_MESSAGE(b) XtWarning(b)
#endif
#define SET_STARTED(w,b) XtVaSetValues(w, XtNstart, b, NULL)
#include "Dino.h"
#include "Dino2d.h"
#ifdef HAVE_OPENGL
#include "DinoGL.h"
#else
#include "Dino3d.h"
#endif
#ifdef HAVE_XPM
#include <X11/xpm.h>
#ifdef CONSTPIXMAPS
#include "dino.t.xpm"
#include "dino.p.xpm"
#include "dino.s.xpm"
#include "dino.m.xpm"
#include "dino.l.xpm"
#include "dino.xpm"
#else
#include "pixmaps/dino.t.xpm"
#include "pixmaps/dino.p.xpm"
#include "pixmaps/dino.s.xpm"
#include "pixmaps/dino.m.xpm"
#include "pixmaps/dino.l.xpm"
#include "pixmaps/dino.xpm"
#endif
#define RESIZE_XPM(s) ((char **) (((s)<=32)?\
(((s)<=22)?(((s)<=16)?dino_t_xpm:dino_p_xpm):\
(((s)<=24)?dino_s_xpm:dino_m_xpm)):\
(((s)<=48)?dino_l_xpm:dino_xpm)))
#endif
#include "pixmaps/dino.xbm"
#define DEFINE_XBM (char *) dino_bits, dino_width, dino_height
#ifndef SCOREPATH
#ifdef VMS
#define SCOREPATH "SYS$LOGIN:"
#else
#define SCOREPATH "/var/games/xpuzzles"
#endif
#endif
#endif

#ifndef SCOREFILE
#define SCOREFILE "dino.scores"
#endif

#define NEVER (-1)
#define FILE_NAME_LENGTH 1024
#define USER_NAME_LENGTH 120
#define MESSAGE_LENGTH (USER_NAME_LENGTH+64)
#define TITLE_LENGTH 2048
#define NOACCESS "noaccess"
#define NOBODY "nobody"

typedef struct {
	int score;
	char name[USER_NAME_LENGTH];
} PuzzleRecord;

static PuzzleRecord puzzleRecord[MAX_MODES][2];
static int movesDsp = 0;
static char messageDsp[MESSAGE_LENGTH] = "Welcome";
static char recordDsp[MESSAGE_LENGTH] = "NOT RECORDED";
#ifndef HAVE_MOTIF
static char titleDsp[TITLE_LENGTH] = "";
#endif
static char scoreFileName[FILE_NAME_LENGTH] = SCOREFILE;
static char fileName[FILE_NAME_LENGTH];
static Boolean randomized = False;
#ifdef WINVER
#define PROGRAM_NAME_LENGTH 80
static char progDsp[PROGRAM_NAME_LENGTH] = TITLE;
static char userNameDsp[USER_NAME_LENGTH] = "Guest";
#else
#ifdef HAVE_MOTIF
#define MIN_SPEED 1
#define MAX_SPEED 50
static Widget movesText, recordText, message;
static Widget modes[MAX_MODES], speedSlider;
static Widget orientizeSwitch, practiceSwitch;
static char buff[21];
static const char *modeString[] =
{
	"Period 2", "Period 3", "Both"
};
static Widget descriptionDialog, featuresDialog;
static Widget optionsDialog, referencesDialog, aboutDialog;
static Widget solveDialog, practiceDialog, randomizeDialog;
static Arg arg[3];
#else
static Widget shell;
#endif
static Pixmap pixmap = None;
static Widget topLevel, puzzle2d, puzzle3d;
static char *progDsp;
static char userNameDsp[USER_NAME_LENGTH] = "";

#ifdef HAVE_MOTIF
static void
printState(Widget w, char *msg)
{
	XmString xmstr;

	if (!XtIsSubclass(w, xmLabelWidgetClass))
		XtError("printState() requires a Label Widget");
	xmstr = XmStringCreateLtoR(msg, XmSTRING_DEFAULT_CHARSET);
	XtVaSetValues(w, XmNlabelString, xmstr, NULL);
}
#endif

static void
printRecords(void)
{
	int mode, orient;

	(void) printf("          DINO  HALL OF FAME\n\n");
	(void) printf("MODE ORIENT USER            MOVES\n");
	for (mode = 0; mode < MAX_MODES; mode++)
		for (orient = 0; orient < 2; orient++) {
			if (puzzleRecord[mode][orient].score > 0)
				(void) printf("%4d%7d %-12s%9d\n",
					mode + 2, orient,
					puzzleRecord[mode][orient].name,
					puzzleRecord[mode][orient].score);
		}
}
#endif

static void
initRecords(void)
{
	int mode, orient;

	for (mode = 0; mode < MAX_MODES; mode++)
		for (orient = 0; orient < 2; orient++) {
			puzzleRecord[mode][orient].score = NEVER;
			(void) strncpy(puzzleRecord[mode][orient].name, NOACCESS,
				USER_NAME_LENGTH);
		}
}

static void
readRecords(void)
{
	FILE *fp;
	int n, mode, orient;
	char userName[USER_NAME_LENGTH];
	char *buf1 = NULL, *buf2 = NULL;
	char *fname, *lname;

	stringCat(&buf1, CURRENTDELIM, scoreFileName);
	lname = buf1;
	stringCat(&buf1, SCOREPATH, FINALDELIM);
	stringCat(&buf2, buf1, SCOREFILE);
	free(buf1);
	fname = buf2;
	(void) strncpy(fileName, lname, USER_NAME_LENGTH);
	if ((fp = fopen(fileName, "r")) == NULL) {
		(void) strncpy(fileName, fname, USER_NAME_LENGTH);
		/* Try installed directory. */
		if ((fp = fopen(fileName, "r")) == NULL) {
			stringCat(&buf1, "Can not read ", fname);
			stringCat(&buf2, buf1, " or ");
			free(buf1);
			stringCat(&buf1, buf2, lname);
			free(buf2);
			PRINT_MESSAGE(buf1);
			free(buf1);
			free(lname);
			free(fname);
			return;
		}
/* Probably annoying */
#if 0
		else {
			stringCat(&buf1, "Can not read ", fname);
			stringCat(&buf2, buf1, ", falling back to ");
			free(buf1);
			stringCat(&buf1, buf2, lname);
			free(buf2);
			PRINT_MESSAGE(buf1);
			free(buf1);
		}
#endif
	}
	free(lname);
	free(fname);
	for (mode = 0; mode < MAX_MODES; mode++)
		for (orient = 0; orient < 2; orient++) {
			(void) fscanf(fp, "%d %s\n", &n, userName);
			if (n <= puzzleRecord[mode][orient].score ||
					puzzleRecord[mode][orient].score <= NEVER) {
				puzzleRecord[mode][orient].score = n;
				(void) strncpy(puzzleRecord[mode][orient].name,
					userName, USER_NAME_LENGTH);
			}
		}
	(void) fclose(fp);
}

static void
writeRecords(void)
{
	FILE *fp;
	int mode, orient;
	char *buf1 = NULL;

	if ((fp = fopen(fileName, "w")) == NULL) {
		stringCat(&buf1, "Can not write to ", fileName);
		PRINT_MESSAGE(buf1);
		free(buf1);
		return;
	}
	{
#if HAVE_FCNTL_H
		int lfd;
		char lockFile[FILE_NAME_LENGTH];

		(void) strncpy(lockFile, fileName, FILE_NAME_LENGTH - 6);
		lockFile[FILE_NAME_LENGTH - 6] = '\0';
		(void) strcat(lockFile, ".lock");
		while (((lfd = open(lockFile, O_CREAT | O_EXCL, 0644)) < 0) &&
				errno == EEXIST)
			(void) sleep(1);
		if (lfd < 0) {
#if 1
			(void) fprintf(stderr,
				"Lock file exists... guessing its an old one.\n");
#else
			(void) fprintf(stderr,
				"Lock file exists... score not recorded - sorry.\n");
			return;
#endif
		}
#endif
		for (mode = 0; mode < MAX_MODES; mode++) {
			for (orient = 0; orient < 2; orient++)
				(void) fprintf(fp, "%d %s\n",
					puzzleRecord[mode][orient].score,
					puzzleRecord[mode][orient].name);
			(void) fprintf(fp, "\n");
		}
#if HAVE_FCNTL_H
		(void) close(lfd);
		(void) unlink(lockFile);
#endif
		(void) fclose(fp);
	}
}

static void
printRecord(int mode, Boolean orient, Boolean practice)
{
	int i = mode - PERIOD2;
	int j = (orient) ? 1 : 0;

	if (practice) {
		(void) strncpy(recordDsp, "practice", MESSAGE_LENGTH);
	} else if (puzzleRecord[i][j].score <= NEVER) {
		(void) sprintf(recordDsp, "NEVER %s", NOACCESS);
	} else {
		(void) sprintf(recordDsp, "%d %s",
			puzzleRecord[i][j].score, puzzleRecord[i][j].name);
	}
#ifdef HAVE_MOTIF
	printState(recordText, recordDsp);
#endif
}

#ifndef WINVER
/* There is probably a better way to assure that they are the same
 * but I do not know it off hand. */
static void
makeEquivalent(String *userName, String *scoreFile, int *mode,
		Boolean *orient, Boolean *practice, int *delay)
{
	Boolean mono, reverse;
	Boolean scoreOnly, versionOnly;
	Pixel foreground, background, pieceBorder;
	String faceColor[MAX_FACES];

	XtVaGetValues(puzzle2d,
		XtNuserName, userName,
		XtNscoreFile, scoreFile,
		XtNmode, mode,
		XtNorient, orient,
		XtNpractice, practice,
		XtNmono, &mono,
		XtNreverseVideo, &reverse,
		XtNdelay, delay,
		XtNforeground, &foreground,
		XtNbackground, &background,
		XtNpieceBorder, &pieceBorder,
		XtNfaceColor0, &(faceColor[0]),
		XtNfaceColor1, &(faceColor[1]),
		XtNfaceColor2, &(faceColor[2]),
		XtNfaceColor3, &(faceColor[3]),
		XtNfaceColor4, &(faceColor[4]),
		XtNfaceColor5, &(faceColor[5]),
		XtNscoreOnly, &scoreOnly,
		XtNversionOnly, &versionOnly, NULL);
	if (versionOnly) {
		(void) printf("%s\n", aboutHelp);
		exit(0);
	}
	if (strcmp(*scoreFile, ""))
		(void) strncpy(scoreFileName, *scoreFile, FILE_NAME_LENGTH);
	if (scoreOnly) {
		initRecords();
		readRecords();
		printRecords();
		exit(0);
	}
	XtVaSetValues(puzzle2d,
		XtNdirection, IGNORE_DIR,
		XtNstart, False, NULL);
	XtVaSetValues(puzzle3d,
		XtNuserName, *userName,
		XtNmode, *mode,
		XtNorient, *orient,
		XtNpractice, *practice,
		XtNmono, mono,
		XtNreverseVideo, reverse,
		XtNdelay, *delay,
		XtNdirection, IGNORE_DIR,
		XtNstart, False,
		XtNforeground, foreground,
		XtNbackground, background,
		XtNpieceBorder, pieceBorder,
		XtNfaceColor0, faceColor[0],
		XtNfaceColor1, faceColor[1],
		XtNfaceColor2, faceColor[2],
		XtNfaceColor3, faceColor[3],
		XtNfaceColor4, faceColor[4],
		XtNfaceColor5, faceColor[5], NULL);
}
#endif

static void
printStatus(char *msg, int nMoves
#ifndef HAVE_MOTIF
		, int dim, int mode
#endif
		)
{
#ifdef HAVE_MOTIF
	printState(message, msg);
	(void) sprintf(buff, "%d", nMoves);
	printState(movesText, buff);
#else
	char mb[10];

	if (mode == BOTH)
		(void) strcpy(mb, "both");
	else
		(void) sprintf(mb, "%d", mode);
#ifdef HAVE_OPENGL
	if (dim == 4)
		(void) sprintf(titleDsp, "%sGL.%s: (%d/%s) - %s",
			progDsp, mb, nMoves, recordDsp, msg);
	else
#endif
		(void) sprintf(titleDsp, "%s%dd.%s: (%d/%s) - %s",
			progDsp, dim, mb, nMoves, recordDsp, msg);
#ifdef WINVER
	SetWindowText(widget.core.hWnd, (LPSTR) titleDsp);
#else
	XtVaSetValues(XtParent((dim == 2) ? puzzle2d : puzzle3d),
		XtNtitle, titleDsp, NULL);
#endif
#endif
}

static Boolean
handleSolved(int counter, int mode, Boolean orient)
{
	int i = mode - PERIOD2;
	int j = (orient) ? 1 : 0;

	if (counter < puzzleRecord[i][j].score ||
			puzzleRecord[i][j].score <= NEVER) {
		readRecords();	/* Maybe its been updated by another */
		puzzleRecord[i][j].score = counter;
		(void) strncpy(puzzleRecord[i][j].name, userNameDsp,
			USER_NAME_LENGTH);
		if (orient && (counter < puzzleRecord[!i][j].score ||
				puzzleRecord[!i][j].score <= NEVER)) {
			puzzleRecord[!i][j].score = counter;
			(void) strncpy(puzzleRecord[!i][j].name, userNameDsp,
				USER_NAME_LENGTH);
		}
		writeRecords();
		printRecord(mode, orient, False);
		return True;
	}
	return False;
}

static void
initialize(
#ifdef WINVER
DinoWidget w, HBRUSH brush
#else
void
#endif
)
{
	int mode;
	Boolean orient, practice;
	char *userName, *scoreFile;
#ifdef WINVER
	int dim;

	initializePuzzle(w, brush);

	mode = w->dino.mode;
	orient = w->dino.orient;
	practice = w->dino.practice;
	userName = w->dino.userName;
	scoreFile = w->dino.scoreFile;
	dim = w->dino.dim;
	SET_STARTED(w, False);
	if (strcmp(scoreFile, ""))
		(void) strncpy(scoreFileName, scoreFile, FILE_NAME_LENGTH);
#else
	int delay;

	makeEquivalent(&userName, &scoreFile, &mode,
		&orient, &practice, &delay);
#ifdef HAVE_MOTIF
	XmToggleButtonSetState(modes[mode - PERIOD2], True, False);
	XmToggleButtonSetState(orientizeSwitch, orient, True);
	XmToggleButtonSetState(practiceSwitch, practice, True);
	XmScaleSetValue(speedSlider, MAX_SPEED + MIN_SPEED - delay - 1);
#endif
#endif
	initRecords();
	readRecords();
#ifndef WINVER
	(void) strncpy(userNameDsp, userName, USER_NAME_LENGTH);
#endif
	if (!strcmp(userName, "") || !strcmp(userName, "(null)") ||
			!strcmp(userName, NOACCESS) ||
			!strcmp(userName, NOBODY)) {
#ifdef WINVER
		(void) strncpy(userNameDsp, userName, USER_NAME_LENGTH);
#else
		char *login = getlogin();

		if (login == NULL) {
			(void) strcpy(userNameDsp, "");
		} else {
			(void) sprintf(userNameDsp, "%s", login);
		}
		if (!strcmp(userNameDsp, "") ||
				!strcmp(userNameDsp, "(null)") ||
				!strcmp(userNameDsp, NOACCESS) ||
				!strcmp(userNameDsp, NOBODY))
			/* It really IS nobody */
			(void) sprintf(userNameDsp, "%s", "guest");
#endif
	}
	printRecord(mode, orient, practice);
#ifdef WINVER
	printStatus(messageDsp, movesDsp, dim, mode);
#else
#ifdef HAVE_MOTIF
	printStatus(messageDsp, movesDsp);
#else
	printStatus(messageDsp, movesDsp, 2, mode);
#ifdef HAVE_OPENGL
	printStatus(messageDsp, movesDsp, 4, mode);
#else
	printStatus(messageDsp, movesDsp, 3, mode);
#endif
#endif
#endif
}

#ifdef WINVER
void
setPuzzle(DinoWidget w, int reason)
#else
static void
puzzleListener(Widget w, caddr_t clientData, dinoCallbackStruct *callData)
#endif
{
	int mode;
	Boolean orient, practice, start, cheat;
#ifdef WINVER
	int dim = 0;
#else
	int reason = callData->reason;
	Widget otherw = (Widget) NULL;
#ifndef HAVE_MOTIF
	int dim = 0, otherdim = 0;
#endif

	if (w == puzzle2d) {
		otherw = puzzle3d;
#ifndef HAVE_MOTIF
		dim = 2;
#ifdef HAVE_OPENGL
		otherdim = 4;
#else
		otherdim = 3;
#endif
#endif
	} else if (w == puzzle3d) {
		otherw = puzzle2d;
#ifndef HAVE_MOTIF
		otherdim = 2;
#ifdef HAVE_OPENGL
		dim = 4;
#else
		dim = 3;
#endif
#endif
	}
#endif
	(void) strcpy(messageDsp, "");
#ifdef WINVER
	mode = w->dino.mode;
	orient = w->dino.orient;
	practice = w->dino.practice;
	cheat = w->dino.cheat;
	start = w->dino.started;
	dim = w->dino.dim;
#else
	XtVaGetValues(w,
		XtNorient, &orient,
		XtNmode, &mode,
		XtNpractice, &practice,
		XtNstart, &start,
		XtNcheat, &cheat, NULL);
#endif
	switch (reason) {
	case ACTION_HIDE:
#ifdef WINVER
		ShowWindow(w->core.hWnd, SW_SHOWMINIMIZED);
#else
		(void) XIconifyWindow(XtDisplay(topLevel),
			XtWindow(topLevel),
			XScreenNumberOfScreen(XtScreen(topLevel)));
#ifndef HAVE_MOTIF
		(void) XIconifyWindow(XtDisplay(shell),
			XtWindow(shell),
			XScreenNumberOfScreen(XtScreen(shell)));
#endif
#endif
		break;
#ifndef WINVER
	case ACTION_PRACTICE_QUERY:
#ifdef HAVE_MOTIF
		XtManageChild(practiceDialog);
#else
		XtVaSetValues(puzzle2d, XtNmenu, ACTION_PRACTICE, NULL);
#endif
		break;
	case ACTION_RANDOMIZE_QUERY:
#ifdef HAVE_MOTIF
		XtManageChild(randomizeDialog);
#else
		XtVaSetValues(puzzle2d, XtNmenu, ACTION_RANDOMIZE, NULL);
#endif
		break;
#endif
	case ACTION_SOLVE_MESSAGE:
#ifdef WINVER
		(void) MessageBox(w->core.hWnd, solveHelp,
			"Auto-solve", MB_OK);
#else
#ifdef HAVE_MOTIF
		XtManageChild(solveDialog);
#else
		(void) strncpy(messageDsp, solveHelp, MESSAGE_LENGTH);
#endif
#endif
		break;
	case ACTION_RESTORE:
		if (practice) {
			(void) strncpy(recordDsp, "practice",
				MESSAGE_LENGTH);
#ifdef HAVE_MOTIF
			printState(recordText, recordDsp);
#endif
		}
		movesDsp = 0;
		randomized = False;
#ifndef WINVER
		XtVaSetValues(otherw,
			XtNdirection, RESTORE_DIR, NULL);
		XtVaSetValues(w,
			XtNdirection, RESTORE_DIR, NULL);
#endif
		break;
	case ACTION_CLEAR:
		movesDsp = 0;
		randomized = False;
#ifndef WINVER
		XtVaSetValues(otherw,
			XtNdirection, CLEAR_DIR, NULL);
		XtVaSetValues(w,
			XtNdirection, CLEAR_DIR, NULL);
#endif
		break;
	case ACTION_RESET:
		movesDsp = 0;
		randomized = False;
		break;
	case ACTION_AMBIGUOUS:
		(void) strncpy(messageDsp, "Ambiguous move",
			MESSAGE_LENGTH);
		break;
	case ACTION_ILLEGAL:
		if (practice || randomized)
			(void) strncpy(messageDsp, "Illegal move",
				MESSAGE_LENGTH);
		else
			(void) strncpy(messageDsp,
				"Randomize to start", MESSAGE_LENGTH);
		break;
#ifndef WINVER
	case ACTION_MOVED:
		movesDsp++;
		XtVaSetValues(otherw,
			XtNstart, True,
			XtNface, callData->face,
			XtNpos, callData->position,
			XtNdirection, callData->direction,
			XtNstyle, callData->style,
			XtNcontrol, callData->control,
			XtNfast, callData->fast, NULL);
		SET_STARTED(w, True);
		break;
	case ACTION_CONTROL:
		XtVaSetValues(otherw,
			XtNface, callData->face,
			XtNpos, callData->position,
			XtNdirection, callData->direction,
			XtNstyle, callData->style,
			XtNcontrol, callData->control,
			XtNfast, callData->fast, NULL);
		break;
#endif
	case ACTION_SOLVED:
		if (practice)
			movesDsp = 0;
		else if (cheat)
			(void) sprintf(messageDsp,
				"No cheating %s!!", userNameDsp);
		else if (handleSolved(movesDsp, mode, orient))
			(void) sprintf(messageDsp,
				"Congratulations %s!!", userNameDsp);
		else
			(void) strncpy(messageDsp, "Solved!",
				MESSAGE_LENGTH);
		SET_STARTED(w, False);
#ifndef WINVER
		SET_STARTED(otherw, False);
#endif
		randomized = False;
		break;
	case ACTION_COMPUTED:
		SET_STARTED(w, False);
#ifndef WINVER
		SET_STARTED(otherw, False);
#endif
		break;
	case ACTION_PRACTICE:
		movesDsp = 0;
		randomized = False;
		practice = !practice;
		if (!practice)
			(void) strncpy(messageDsp, "Randomize to start",
				MESSAGE_LENGTH);
		printRecord(mode, orient, practice);
#ifdef WINVER
		w->dino.practice = practice;
		w->dino.started = False;
#else
		XtVaSetValues(w,
			XtNpractice, practice,
			XtNstart, False, NULL);
		XtVaSetValues(otherw,
			XtNpractice, practice,
			XtNstart, False, NULL);
#ifdef HAVE_MOTIF
		XmToggleButtonSetState(practiceSwitch, practice, True);
#endif
#endif
		break;
	case ACTION_RANDOMIZE:
		movesDsp = 0;
		randomized = True;
#ifdef WINVER
		w->dino.practice = False;
		w->dino.started = False;
#else
		XtVaSetValues(w,
			XtNpractice, False,
			XtNstart, False, NULL);
		XtVaSetValues(otherw,
			XtNpractice, False,
			XtNstart, False, NULL);
#endif
		break;
#ifdef HAVE_OPENGL
	case ACTION_VIEW:
		{
			int view;

#ifdef WINVER
			view = w->dino.view;
#else
			XtVaGetValues(puzzle3d,
				XtNview, &view, NULL);
#endif
			view = (view + 1) % (2 * MAX_VIEWS);
#ifdef WINVER
			w->dino.view = view;
#else
			XtVaSetValues(puzzle3d,
				XtNview, view, NULL);
#endif
		}
		break;
#endif
	case ACTION_ORIENTIZE:
		movesDsp = 0;
		orient = !orient;
		printRecord(mode, orient, practice);
#ifdef WINVER
		w->dino.orient = orient;
#else
		XtVaSetValues(w,
			XtNorient, orient, NULL);
		XtVaSetValues(otherw,
			XtNorient, orient, NULL);
#ifdef HAVE_MOTIF
		XmToggleButtonSetState(orientizeSwitch, orient, True);
#endif
#endif
		break;
	case ACTION_PERIOD2:
	case ACTION_PERIOD3:
	case ACTION_BOTH:
		movesDsp = 0;
		mode = reason - ACTION_PERIOD2 + PERIOD2;
		printRecord(mode, orient, practice);
#ifdef WINVER
		w->dino.mode = mode;
#else
		XtVaSetValues(w,
			XtNmode, mode, NULL);
		XtVaSetValues(otherw,
			XtNmode, mode, NULL);
#ifdef HAVE_MOTIF
		XmToggleButtonSetState(modes[mode - PERIOD2], True, True);
#endif
#endif
		break;
#ifndef WINVER
	case ACTION_UNDO:
		movesDsp--;
		XtVaSetValues(otherw,
			XtNstart, True,
			XtNface, callData->face,
			XtNpos, callData->position,
			XtNdirection, callData->direction,
			XtNstyle, callData->style, NULL);
		SET_STARTED(w, True);
		break;
#endif
#ifndef WINVER
	case ACTION_REDO:
		movesDsp++;
		XtVaSetValues(otherw,
			XtNstart, True,
			XtNface, callData->face,
			XtNpos, callData->position,
			XtNdirection, callData->direction,
			XtNstyle, callData->style, NULL);
		SET_STARTED(w, True);
		break;
#endif
#ifdef WINVER
	case ACTION_DIM:
		dim++;
#ifdef HAVE_OPENGL
		if (dim == 3)
			dim = 4;
		else if (dim > 4)
#else
		if (dim > 3)
#endif
			dim = 2;
		w->dino.dim = dim;
		break;
#endif
#ifndef WINVER
	case ACTION_CHEAT:
		XtVaSetValues(w,
			XtNcheat, cheat, NULL);
		XtVaSetValues(otherw,
			XtNcheat, cheat, NULL);
		break;
#endif
	}
#ifdef WINVER
	printStatus(messageDsp, movesDsp, dim, mode);
#else
#ifdef HAVE_MOTIF
	printStatus(messageDsp, movesDsp);
#else
	printStatus(messageDsp, movesDsp, dim, mode);
	printStatus(messageDsp, movesDsp, otherdim, mode);
#endif
#endif
}

#ifdef WINVER
void
setPuzzleMove(DinoWidget w, int reason, int face, int position, int direction,
		int style, int control, int fast)
{
	int mode;
	/* Boolean orient, practice, cheat; */
	int dim;

	(void) strcpy(messageDsp, "");
	mode = w->dino.mode;
#if 0
	orient = w->dino.orient;
	practice = w->dino.practice;
	cheat = w->dino.cheat;
#endif
	dim = w->dino.dim;
	switch (reason) {
	case ACTION_MOVED:
		movesDsp++;
		SET_STARTED(w, True);
		break;
	case ACTION_CONTROL:
		break;
	case ACTION_UNDO:
		movesDsp--;
		SET_STARTED(w, True);
		break;
	case ACTION_REDO:
		movesDsp++;
		SET_STARTED(w, True);
		break;
	}
	printStatus(messageDsp, movesDsp, dim, mode);
}

static LRESULT CALLBACK
about(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message == WM_COMMAND && LOWORD(wParam) == IDOK) {
		(void) EndDialog(hDlg, TRUE);
		return TRUE;
	}
	return FALSE;
}

static LRESULT CALLBACK
WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HBRUSH brush = (HBRUSH) NULL;
	PAINTSTRUCT paint;
	int shift = 0;

	widget.core.hWnd = hWnd;
	if (GetFocus()) {
		if (!widget.dino.focus) {
			widget.core.hDC = GetDC(hWnd);
			(void) SelectObject(widget.core.hDC,
				GetStockObject(NULL_BRUSH));
			enterPuzzle(&widget);
			(void) EndPaint(hWnd, &paint);
		}
	} else {
		if (widget.dino.focus) {
			widget.core.hDC = GetDC(hWnd);
			(void) SelectObject(widget.core.hDC,
				GetStockObject(NULL_BRUSH));
			leavePuzzle(&widget);
			(void) EndPaint(hWnd, &paint);
		}
	}
	switch (message) {
	case WM_CREATE:
		initialize(&widget, brush);
		break;
	case WM_DESTROY:
		destroyPuzzle(brush);
		break;
	case WM_SIZE:
		resizePuzzle(&widget);
		(void) InvalidateRect(hWnd, NULL, TRUE);
		break;
	case WM_PAINT:
		widget.core.hDC = BeginPaint(hWnd, &paint);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_PEN));
		exposePuzzle(&widget);
		(void) EndPaint(hWnd, &paint);
		break;
	case WM_RBUTTONDOWN:
		widget.core.hDC = GetDC(hWnd);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_PEN));
		randomizePuzzle(&widget);
		(void) ReleaseDC(hWnd, widget.core.hDC);
		break;
	case WM_LBUTTONDOWN:
		widget.core.hDC = GetDC(hWnd);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_PEN));
		selectPuzzle(&widget, LOWORD(lParam), HIWORD(lParam),
			(GetKeyState(VK_CONTROL) >> 1) ? 1 : 0);
		(void) ReleaseDC(hWnd, widget.core.hDC);
		break;
	case WM_LBUTTONUP:
		widget.core.hDC = GetDC(hWnd);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_PEN));
		releasePuzzle(&widget, LOWORD(lParam), HIWORD(lParam),
			((GetKeyState(VK_SHIFT) >> 1) ||
			(GetKeyState(VK_CAPITAL) & 1)),
			(GetKeyState(VK_CONTROL) >> 1) ? 1 : 0,
			(GetKeyState(VK_TAB) >> 1) ? 1 : 0);
		(void) ReleaseDC(hWnd, widget.core.hDC);
		break;
#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
	case WM_MOUSEWHEEL:
		widget.core.hDC = GetDC(hWnd);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_PEN));
		{
			int zDelta = ((short) HIWORD(wParam));
			POINT cursor, origin;

			origin.x = 0, origin.y = 0;
			ClientToScreen(hWnd, &origin);
			(void) GetCursorPos(&cursor);
			if (zDelta > (WHEEL_DELTA >> 1)) {
				movePuzzleInput(&widget,
					cursor.x - origin.x,
					cursor.y - origin.y,
					TOP,
					((GetKeyState(VK_SHIFT) >> 1) ||
					(GetKeyState(VK_CAPITAL) & 1)),
					(GetKeyState(VK_CONTROL) >> 1) ? 1 : 0,
					(GetKeyState(VK_TAB) >> 1) ? 1 : 0);
			} else if (zDelta < -(WHEEL_DELTA >> 1)) {
				movePuzzleInput(&widget,
					cursor.x - origin.x,
					cursor.y - origin.y,
					BOTTOM,
					((GetKeyState(VK_SHIFT) >> 1) ||
					(GetKeyState(VK_CAPITAL) & 1)),
					(GetKeyState(VK_CONTROL) >> 1) ? 1 : 0,
					(GetKeyState(VK_TAB) >> 1) ? 1 : 0);
			}
		}
		(void) ReleaseDC(hWnd, widget.core.hDC);
		break;
#endif
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case ACTION_GET:
			getPuzzle(&widget);
			resizePuzzle(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_WRITE:
			writePuzzle(&widget);
			break;
		case ACTION_EXIT:
			destroyPuzzle(brush);
			break;
		case ACTION_HIDE:
			hidePuzzle(&widget);
			break;
		case ACTION_UNDO:
			widget.core.hDC = GetDC(hWnd);
			(void) SelectObject(widget.core.hDC,
				GetStockObject(NULL_PEN));
			undoPuzzle(&widget);
			(void) ReleaseDC(hWnd, widget.core.hDC);
			break;
		case ACTION_REDO:
			widget.core.hDC = GetDC(hWnd);
			(void) SelectObject(widget.core.hDC,
				GetStockObject(NULL_PEN));
			redoPuzzle(&widget);
			(void) ReleaseDC(hWnd, widget.core.hDC);
			break;
		case ACTION_CLEAR:
			clearPuzzle(&widget);
			sizePuzzle(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_RANDOMIZE:
			widget.core.hDC = GetDC(hWnd);
			(void) SelectObject(widget.core.hDC,
				GetStockObject(NULL_PEN));
			randomizePuzzle(&widget);
			(void) ReleaseDC(hWnd, widget.core.hDC);
			break;
		case ACTION_PRACTICE:
			practicePuzzle(&widget);
			sizePuzzle(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_SOLVE:
			widget.core.hDC = GetDC(hWnd);
			(void) SelectObject(widget.core.hDC,
				GetStockObject(NULL_PEN));
			solvePuzzle(&widget);
			(void) ReleaseDC(hWnd, widget.core.hDC);
			break;
		case ACTION_ORIENTIZE:
			orientizePuzzle(&widget);
			sizePuzzle(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DIM:
			(void) dimPuzzle(&widget);
			resizePuzzle(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_VIEW:
			(void) viewPuzzle(&widget);
			resizePuzzle(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_SHIFT_TR:
		case ACTION_SHIFT_BR:
		case ACTION_SHIFT_BL:
		case ACTION_SHIFT_TL:
		case ACTION_SHIFT_CW:
		case ACTION_SHIFT_CCW:
		case ACTION_SHIFT_TOP:
		case ACTION_SHIFT_RIGHT:
		case ACTION_SHIFT_BOTTOM:
		case ACTION_SHIFT_LEFT:
			shift = 1;
		case ACTION_TR:
		case ACTION_BR:
		case ACTION_BL:
		case ACTION_TL:
		case ACTION_CW:
		case ACTION_CCW:
		case ACTION_TOP:
		case ACTION_RIGHT:
		case ACTION_BOTTOM:
		case ACTION_LEFT:
			{
				POINT cursor, origin;

				widget.core.hDC = GetDC(hWnd);
				(void) SelectObject(widget.core.hDC,
					GetStockObject(NULL_PEN));
				origin.x = 0, origin.y = 0;
				ClientToScreen(hWnd, &origin);
				(void) GetCursorPos(&cursor);
				shift = shift || (GetKeyState(VK_CAPITAL) & 1);
				(void) movePuzzleInput(&widget,
					cursor.x - origin.x,
					cursor.y - origin.y,
					(int) LOWORD(wParam) -
					((shift) ? ACTION_SHIFT_TR : ACTION_TR),
					shift, FALSE, FALSE);
				(void) ReleaseDC(hWnd, widget.core.hDC);
			}
			break;
		case ACTION_SHIFT_CONTROL_TR:
		case ACTION_SHIFT_CONTROL_BR:
		case ACTION_SHIFT_CONTROL_BL:
		case ACTION_SHIFT_CONTROL_TL:
		case ACTION_SHIFT_CONTROL_CW:
		case ACTION_SHIFT_CONTROL_CCW:
		case ACTION_SHIFT_CONTROL_TOP:
		case ACTION_SHIFT_CONTROL_RIGHT:
		case ACTION_SHIFT_CONTROL_BOTTOM:
		case ACTION_SHIFT_CONTROL_LEFT:
			shift = 1;
		case ACTION_CONTROL_TR:
		case ACTION_CONTROL_BR:
		case ACTION_CONTROL_BL:
		case ACTION_CONTROL_TL:
		case ACTION_CONTROL_CW:
		case ACTION_CONTROL_CCW:
		case ACTION_CONTROL_TOP:
		case ACTION_CONTROL_RIGHT:
		case ACTION_CONTROL_BOTTOM:
		case ACTION_CONTROL_LEFT:
			{
				POINT cursor, origin;

				widget.core.hDC = GetDC(hWnd);
				(void) SelectObject(widget.core.hDC,
					GetStockObject(NULL_PEN));
				origin.x = 0, origin.y = 0;
						ClientToScreen(hWnd, &origin);
						(void) GetCursorPos(&cursor);
				(void) movePuzzleInput(&widget,
					cursor.x - origin.x,
					cursor.y - origin.y,
					(int) LOWORD(wParam) -
					((shift) ? ACTION_SHIFT_CONTROL_TR :
					ACTION_CONTROL_TR),
					shift, TRUE, FALSE);
				(void) ReleaseDC(hWnd, widget.core.hDC);
			}
			break;
		case ACTION_CONTROL_ALT_TR:
		case ACTION_CONTROL_ALT_BR:
		case ACTION_CONTROL_ALT_BL:
		case ACTION_CONTROL_ALT_TL:
		case ACTION_CONTROL_ALT_CW:
		case ACTION_CONTROL_ALT_CCW:
		case ACTION_CONTROL_ALT_TOP:
		case ACTION_CONTROL_ALT_RIGHT:
		case ACTION_CONTROL_ALT_BOTTOM:
		case ACTION_CONTROL_ALT_LEFT:
			shift = 1;
		case ACTION_ALT_TR:
		case ACTION_ALT_BR:
		case ACTION_ALT_BL:
		case ACTION_ALT_TL:
		case ACTION_ALT_CW:
		case ACTION_ALT_CCW:
		case ACTION_ALT_TOP:
		case ACTION_ALT_RIGHT:
		case ACTION_ALT_BOTTOM:
		case ACTION_ALT_LEFT:
			{
				POINT cursor, origin;

				widget.core.hDC = GetDC(hWnd);
				(void) SelectObject(widget.core.hDC,
					GetStockObject(NULL_PEN));
				origin.x = 0, origin.y = 0;
				ClientToScreen(hWnd, &origin);
				(void) GetCursorPos(&cursor);
				(void) movePuzzleInput(&widget,
					cursor.x - origin.x,
					cursor.y - origin.y,
					(int) LOWORD(wParam) -
					((shift) ? ACTION_CONTROL_ALT_TR :
					ACTION_ALT_TR),
					FALSE, shift, TRUE);
				(void) ReleaseDC(hWnd, widget.core.hDC);
			}
			break;
		case ACTION_PERIOD2:
		case ACTION_PERIOD3:
		case ACTION_BOTH:
			periodModePuzzle(&widget,
				(int) LOWORD(wParam) - ACTION_PERIOD2);
			sizePuzzle(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_SPEED:
			speedUpPuzzle(&widget);
			break;
		case ACTION_SLOW:
			slowDownPuzzle(&widget);
			break;
		case ACTION_SOUND:
			toggleSoundPuzzle(&widget);
			break;
		case ACTION_DESCRIPTION:
			(void) MessageBox(hWnd, descriptionHelp,
				"Description", MB_OK);
			break;
		case ACTION_FEATURES:
			(void) MessageBox(hWnd, featuresHelp,
				"Features", MB_OK);
			break;
		case ACTION_REFERENCES:
			(void) MessageBox(hWnd, referencesHelp,
				"References", MB_OK);
			break;
		case ACTION_ABOUT:
			(void) DialogBox(widget.core.hInstance,
				"About", hWnd, (DLGPROC) about);
			break;
		}
		break;
	default:
		return (DefWindowProc(hWnd, message, wParam, lParam));
	}
	return FALSE;
}

int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
		int numCmdShow)
{
	HWND hWnd;
	MSG msg;
	WNDCLASS wc;
	HACCEL hAccel;

	if (!hPrevInstance) {
		wc.style = CS_HREDRAW | CS_VREDRAW;
		wc.lpfnWndProc = WindowProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = 0;
		wc.hInstance = hInstance;
		wc.hIcon = LoadIcon(hInstance, TITLE);
		wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
		wc.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH);
		wc.lpszMenuName = TITLE;
		wc.lpszClassName = TITLE;
		if (!RegisterClass(&wc))
			return FALSE;
	}
	widget.core.hInstance = hInstance;
	hWnd = CreateWindow(TITLE,
		TITLE,
		WS_OVERLAPPEDWINDOW,
		(signed) CW_USEDEFAULT,
		(signed) CW_USEDEFAULT,
		(signed) CW_USEDEFAULT,
		(signed) CW_USEDEFAULT,
		HWND_DESKTOP,
		(HMENU) NULL,
		hInstance,
		(void *) NULL);
	if (!hWnd)
		return FALSE;
	hAccel = (HACCEL) LoadAccelerators(hInstance, TITLE);
	(void) ShowWindow(hWnd, numCmdShow);
	(void) UpdateWindow(hWnd);
	while (GetMessage(&msg, (HWND) NULL, 0, 0))
		if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
			(void) TranslateMessage(&msg);
			(void) DispatchMessage(&msg);
		}
	return (msg.wParam);
}

#else
static void
usage(char *programName)
{
	unsigned int i;

	(void) fprintf(stderr, "usage: %s\n", programName);
	for (i = 0; i < strlen(optionsHelp); i++) {
		if (i == 0 || optionsHelp[i - 1] == '\n')
			(void) fprintf(stderr, "\t");
		(void) fprintf(stderr, "%c", (optionsHelp[i]));
	}
	(void) fprintf(stderr, "\n");
	exit(1);
}

static XrmOptionDescRec options[] =
{
	{(char *) "-mono", (char *) "*dino.mono", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-nomono", (char *) "*dino.mono", XrmoptionNoArg, (char *) "FALSE"},
	{(char *) "-rv", (char *) "*dino.reverseVideo", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-reverse", (char *) "*dino.reverseVideo", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-norv", (char *) "*dino.reverseVideo", XrmoptionNoArg, (char *) "FALSE"},
	{(char *) "-noreverse", (char *) "*dino.reverseVideo", XrmoptionNoArg, (char *) "FALSE"},
	{(char *) "-fg", (char *) "*dino.Foreground", XrmoptionSepArg, NULL},
	{(char *) "-foreground", (char *) "*dino.Foreground", XrmoptionSepArg, NULL},
	{(char *) "-bg", (char *) "*Background", XrmoptionSepArg, NULL},
	{(char *) "-background", (char *) "*Background", XrmoptionSepArg, NULL},
	{(char *) "-bd", (char *) "*dino.pieceBorder", XrmoptionSepArg, NULL},
	{(char *) "-border", (char *) "*dino.pieceBorder", XrmoptionSepArg, NULL},
	{(char *) "-face0", (char *) "*dino.faceColor0", XrmoptionSepArg, NULL},
	{(char *) "-face1", (char *) "*dino.faceColor1", XrmoptionSepArg, NULL},
	{(char *) "-face2", (char *) "*dino.faceColor2", XrmoptionSepArg, NULL},
	{(char *) "-face3", (char *) "*dino.faceColor3", XrmoptionSepArg, NULL},
	{(char *) "-face4", (char *) "*dino.faceColor4", XrmoptionSepArg, NULL},
	{(char *) "-face5", (char *) "*dino.faceColor5", XrmoptionSepArg, NULL},
	{(char *) "-delay", (char *) "*dino.delay", XrmoptionSepArg, NULL},
	{(char *) "-sound", (char *) "*dino.sound", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-nosound", (char *) "*dino.sound", XrmoptionNoArg, (char *) "FALSE"},
	{(char *) "-fn", (char *) "*dino.font", XrmoptionSepArg, NULL},
	{(char *) "-font", (char *) "*dino.font", XrmoptionSepArg, NULL},
	{(char *) "-view", (char *) "*dino.view", XrmoptionSepArg, NULL},
	{(char *) "-mode", (char *) "*dino.mode", XrmoptionSepArg, NULL},
	{(char *) "-both", (char *) "*dino.mode", XrmoptionNoArg, (char *) "4"},
	{(char *) "-orient", (char *) "*dino.orient", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-noorient", (char *) "*dino.orient", XrmoptionNoArg, (char *) "FALSE"},
	{(char *) "-practice", (char *) "*dino.practice", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-nopractice", (char *) "*dino.practice", XrmoptionNoArg, (char *) "FALSE"},
	{(char *) "-userName", (char *) "*dino.userName", XrmoptionSepArg, NULL},
	{(char *) "-scoreFile", (char *) "*dino.scoreFile", XrmoptionSepArg, NULL},
	{(char *) "-scores", (char *) "*dino.scoreOnly", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-version", (char *) "*dino.versionOnly", XrmoptionNoArg, (char *) "TRUE"}
};

#ifdef HAVE_MOTIF
static void
puzzlePracticeListener(Widget w, XtPointer clientData,
		XmAnyCallbackStruct *cbs)
{
	if (cbs->reason == XmCR_OK) {
		XtVaSetValues(puzzle2d, XtNmenu, ACTION_PRACTICE, NULL);
	}
}

static void
puzzleRandomizeListener(Widget w, XtPointer clientData,
		XmAnyCallbackStruct *cbs)
{
	if (cbs->reason == XmCR_OK) {
		XtVaSetValues(puzzle2d, XtNmenu, ACTION_RANDOMIZE, NULL);
	}
}
static void
modeToggle(Widget w, int mode, XmToggleButtonCallbackStruct *cbs)
{
	Boolean orient, practice;

	if (cbs->set) {
		XtVaGetValues(puzzle2d,
			XtNorient, &orient,
			XtNpractice, &practice, NULL);
		XtVaSetValues(puzzle2d,
			XtNmode, mode + PERIOD2, NULL);
		XtVaSetValues(puzzle3d,
			XtNmode, mode + PERIOD2, NULL);
		movesDsp = 0;
		(void) sprintf(buff, "%d", movesDsp);
		printState(movesText, buff);
		printRecord(mode + PERIOD2, orient, practice);
		(void) strcpy(messageDsp, "");
		printState(message, messageDsp);
	}
}

static void
orientToggle(Widget w, XtPointer clientData,
		XmToggleButtonCallbackStruct *cbs)
{
	int mode;
	Boolean orient = cbs->set, practice;

	XtVaGetValues(puzzle2d,
		XtNmode, &mode,
		XtNpractice, &practice, NULL);
	XtVaSetValues(puzzle2d,
		XtNorient, orient, NULL);
	XtVaSetValues(puzzle3d,
		XtNorient, orient, NULL);
	movesDsp = 0;
	(void) sprintf(buff, "%d", movesDsp);
	printState(movesText, buff);
	printRecord(mode, orient, practice);
	(void) strcpy(messageDsp, "");
	printState(message, messageDsp);
}

static void
practiceToggle(Widget w, XtPointer clientData,
		XmToggleButtonCallbackStruct *cbs)
{
	int mode;
	Boolean orient, practice = cbs->set;

	XtVaSetValues(puzzle2d,
		XtNpractice, practice,
		XtNstart, False, NULL);
	XtVaSetValues(puzzle3d,
		XtNpractice, practice,
		XtNstart, False, NULL);
	XtVaGetValues(puzzle2d,
		XtNmode, &mode,
		XtNpractice, &orient, NULL);
	movesDsp = 0;
	(void) sprintf(buff, "%d", movesDsp);
	printState(movesText, buff);
	printRecord(mode, orient, practice);
	if (practice)
		(void) strcpy(messageDsp, "");
	else
		(void) strncpy(messageDsp, "Randomize to start", MESSAGE_LENGTH);
	printState(message, messageDsp);
}

static void
speedChangeListener(Widget w, XtPointer clientData,
		XmScaleCallbackStruct *cbs)
{
	int delay = MAX_SPEED + MIN_SPEED - cbs->value - 1, oldDelay;

	XtVaGetValues(puzzle2d,
		XtNdelay, &oldDelay, NULL);
	if (oldDelay != delay) {
		XtVaSetValues(puzzle3d, XtNdelay, delay, NULL);
		XtVaSetValues(puzzle2d, XtNdelay, delay, NULL);
	}
}

static void
fileMenuListener(Widget w, void *value, void *clientData)
{
	int val = (int) value + ACTION_GET;

	if (val == ACTION_EXIT)
		exit(0);
	XtVaSetValues(puzzle2d, XtNmenu, val, NULL);
}

static void
playCB(Widget w, void *value, void *clientData)
{
	int val = (int) value + ACTION_UNDO;

	XtVaSetValues(puzzle2d, XtNmenu, val, NULL);
}

static Widget
createQuery(Widget w, char *text, char *title, XtCallbackProc callback)
{
	Widget button, messageBox;
	char titleDsp[FILE_NAME_LENGTH + 8];
	XmString titleString = NULL, messageString = NULL;
	static XmStringCharSet charSet =
		(XmStringCharSet) XmSTRING_DEFAULT_CHARSET;

	messageString = XmStringCreateLtoR(text, charSet);
	(void) sprintf(titleDsp, "%s: %s", progDsp, title);
	titleString = XmStringCreateSimple((char *) titleDsp);
	XtSetArg(arg[0], XmNdialogTitle, titleString);
	XtSetArg(arg[1], XmNmessageString, messageString);
	messageBox = XmCreateWarningDialog(w, (char *) "queryBox",
		arg, 2);
	button = XmMessageBoxGetChild(messageBox, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild(button);
	XmStringFree(titleString);
	XmStringFree(messageString);
	XtAddCallback(messageBox, XmNokCallback, callback, (XtPointer) NULL);
	XtAddCallback(messageBox, XmNcancelCallback, callback,
		(XtPointer) NULL);
	return messageBox;
}

static Widget
createHelp(Widget w, char *text, char *title)
{
	Widget button, messageBox;
	char titleDsp[FILE_NAME_LENGTH + 8];
	XmString titleString = NULL, messageString = NULL, buttonString = NULL;
	static XmStringCharSet charSet =
		(XmStringCharSet) XmSTRING_DEFAULT_CHARSET;

	messageString = XmStringCreateLtoR(text, charSet);
	(void) sprintf(titleDsp, "%s: %s", progDsp, title);
	titleString = XmStringCreateSimple((char *) titleDsp);
	buttonString = XmStringCreateSimple((char *) "OK");
	XtSetArg(arg[0], XmNdialogTitle, titleString);
	XtSetArg(arg[1], XmNokLabelString, buttonString);
	XtSetArg(arg[2], XmNmessageString, messageString);
	messageBox = XmCreateInformationDialog(w, (char *) "helpBox",
		arg, 3);
	button = XmMessageBoxGetChild(messageBox, XmDIALOG_CANCEL_BUTTON);
	XtUnmanageChild(button);
	button = XmMessageBoxGetChild(messageBox, XmDIALOG_HELP_BUTTON);
	XtUnmanageChild(button);
	XmStringFree(titleString);
	XmStringFree(buttonString);
	XmStringFree(messageString);
	return messageBox;
}

static void
helpMenuListener(Widget w, XtPointer value, XtPointer clientData)
{
	int val = (int) value;

	switch (val) {
	case 0:
		XtManageChild(descriptionDialog);
		break;
	case 1:
		XtManageChild(featuresDialog);
		break;
	case 2:
		XtManageChild(optionsDialog);
		break;
	case 3:
		XtManageChild(referencesDialog);
		break;
	case 4:
		XtManageChild(aboutDialog);
		break;
	default:
		{
			char *buf;

			intCat(&buf, "helpMenuListener: %d", val);
			XtWarning(buf);
			free(buf);
		}
	}
}
#endif

int
main(int argc, char **argv)
{
	int pixmapSize;
#ifdef HAVE_MOTIF
	Widget menuBar, pullDownMenu, widget;
	Widget menuBarPanel, mainPanel, controlPanel;
	Widget movesRowCol, labelRowCol, radioRowCol, switchRowCol;
	Widget messageRowCol;
	XmString fileString, playString;
	XmString getString, writeString, quitString;
	XmString undoString, redoString;
	XmString clearString, practiceString, randomizeString, solveString;
	XmString orientizeString;
	XmString speedString, slowString, soundString, viewString;
	unsigned int i;
#endif

	progDsp = argv[0];
	topLevel = XtInitialize(argv[0], "Dino",
		options, XtNumber(options), &argc, argv);
	if (argc != 1)
		usage(argv[0]);

#ifdef HAVE_MOTIF
	menuBarPanel = XtVaCreateManagedWidget("menuBarPanel",
		xmPanedWindowWidgetClass, topLevel,
		XmNseparatorOn, False,
		XmNsashWidth, 1,
		XmNsashHeight, 1, NULL);
	fileString = XmStringCreateSimple((char *) "File");
	playString = XmStringCreateSimple((char *) "Play");
	menuBar = XmVaCreateSimpleMenuBar(menuBarPanel, (char *) "menuBar",
		XmVaCASCADEBUTTON, fileString, 'F',
		XmVaCASCADEBUTTON, playString, 'P',
		NULL);
	XmStringFree(fileString);
	XmStringFree(playString);
	getString = XmStringCreateSimple((char *) "Get");
	writeString = XmStringCreateSimple((char *) "Write");
	quitString = XmStringCreateSimple((char *) "Exit");
	(void) XmVaCreateSimplePulldownMenu(menuBar, (char *) "file_menu",
		0, fileMenuListener,
		XmVaPUSHBUTTON, getString, 'G', NULL, NULL,
		XmVaPUSHBUTTON, writeString, 'W', NULL, NULL,
		XmVaSEPARATOR,
		XmVaPUSHBUTTON, quitString, 'x', NULL, NULL,
		NULL);
	XmStringFree(getString);
	XmStringFree(writeString);
	XmStringFree(quitString);
	undoString = XmStringCreateSimple((char *) "Undo");
	redoString = XmStringCreateSimple((char *) "Redo");
	clearString = XmStringCreateSimple((char *) "Clear");
	practiceString = XmStringCreateSimple((char *) "Practice");
	randomizeString = XmStringCreateSimple((char *) "Randomize");
	solveString = XmStringCreateSimple((char *) "Auto-solve");
	orientizeString = XmStringCreateSimple((char *) "Orientize");
	speedString = XmStringCreateSimple((char *) "Speed >");
	slowString = XmStringCreateSimple((char *) "Slow <");
	soundString = XmStringCreateSimple((char *) "Sound @");
	viewString = XmStringCreateSimple((char *) "View");
	(void) XmVaCreateSimplePulldownMenu(menuBar, (char *) "play_menu",
		1, playCB,
		XmVaPUSHBUTTON, undoString, 'U', NULL, NULL,
		XmVaPUSHBUTTON, redoString, 'R', NULL, NULL,
		XmVaPUSHBUTTON, clearString, 'C', NULL, NULL,
		XmVaPUSHBUTTON, practiceString, 'P', NULL, NULL,
		XmVaPUSHBUTTON, randomizeString, 'z', NULL, NULL,
		XmVaPUSHBUTTON, solveString, 's', NULL, NULL,
		XmVaPUSHBUTTON, orientizeString, 'O', NULL, NULL,
		XmVaPUSHBUTTON, speedString, '>', NULL, NULL,
		XmVaPUSHBUTTON, slowString, '<', NULL, NULL,
		XmVaPUSHBUTTON, soundString, '@', NULL, NULL,
		XmVaPUSHBUTTON, viewString, 'V', NULL, NULL,
		NULL);
	XmStringFree(undoString);
	XmStringFree(redoString);
	XmStringFree(clearString);
	XmStringFree(practiceString);
	XmStringFree(randomizeString);
	XmStringFree(solveString);
	XmStringFree(orientizeString);
	XmStringFree(speedString);
	XmStringFree(slowString);
	XmStringFree(soundString);
	XmStringFree(viewString);
	pullDownMenu = XmCreatePulldownMenu(menuBar,
		(char *) "helpPullDown", NULL, 0);
	widget = XtVaCreateManagedWidget("Help",
		xmCascadeButtonWidgetClass, menuBar,
		XmNsubMenuId, pullDownMenu,
		XmNmnemonic, 'H', NULL);
	XtVaSetValues(menuBar, XmNmenuHelpWidget, widget, NULL);
	widget = XtVaCreateManagedWidget("Description",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'D', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 0);
	widget = XtVaCreateManagedWidget("Features",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'F', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 1);
	widget = XtVaCreateManagedWidget("Options",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'O', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 2);
	widget = XtVaCreateManagedWidget("References",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'R', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 3);
	widget = XtVaCreateManagedWidget("About",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'A', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 4);
	XtManageChild(menuBar);
	descriptionDialog = createHelp(menuBar, (char *) descriptionHelp,
		(char *) "Description");
	featuresDialog = createHelp(menuBar, (char *) featuresHelp,
		(char *) "Features");
	optionsDialog = createHelp(menuBar, (char *) optionsHelp,
		(char *) "Options");
	referencesDialog = createHelp(menuBar, (char *) referencesHelp,
		(char *) "References");
	aboutDialog = createHelp(menuBar, (char *) aboutHelp,
		(char *) "About");
	solveDialog = createHelp(menuBar, (char *) solveHelp,
		(char *) "Solve");
	practiceDialog = createQuery(topLevel,
		(char *) "Are you sure you want to toggle the practice mode?",
		(char *) "Practice Query",
		(XtCallbackProc) puzzlePracticeListener);
	randomizeDialog = createQuery(topLevel,
		(char *) "Are you sure you want to randomize?",
		(char *) "Randomize Query",
		(XtCallbackProc) puzzleRandomizeListener);
	mainPanel = XtCreateManagedWidget("mainPanel",
		xmPanedWindowWidgetClass, menuBarPanel,
		NULL, 0);
	controlPanel = XtVaCreateManagedWidget("controlPanel",
		xmPanedWindowWidgetClass, mainPanel,
		XmNseparatorOn, False,
		XmNsashWidth, 1,
		XmNsashHeight, 1, NULL);
	movesRowCol = XtVaCreateManagedWidget("movesRowcol",
		xmRowColumnWidgetClass, controlPanel,
		XmNnumColumns, 2,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_COLUMN, NULL);
#ifdef MOUSEBITMAPS
	{
		/* Takes up valuable real estate. */
		Pixmap mouseLeftCursor, mouseRightCursor;
		Pixel fg, bg;

		(void) XtVaGetValues(movesRowCol,
			XmNforeground, &fg,
			XmNbackground, &bg, NULL);
		mouseLeftCursor = XCreatePixmapFromBitmapData(
			XtDisplay(movesRowCol),
			RootWindowOfScreen(XtScreen(movesRowCol)),
			(char *) mouse_left_bits,
			mouse_left_width, mouse_left_height, fg, bg,
			DefaultDepthOfScreen(XtScreen(movesRowCol)));
		mouseRightCursor = XCreatePixmapFromBitmapData(
			XtDisplay(movesRowCol),
			RootWindowOfScreen(XtScreen(movesRowCol)),
			(char *) mouse_right_bits,
			mouse_right_width, mouse_right_height, fg, bg,
			DefaultDepthOfScreen(XtScreen(movesRowCol)));
		(void) XtVaCreateManagedWidget("mouseLeftText",
			xmLabelGadgetClass, movesRowCol,
			XtVaTypedArg, XmNlabelString,
			XmRString, "Move", 5, NULL);
		(void) XtVaCreateManagedWidget("mouseLeft",
			xmLabelGadgetClass, movesRowCol,
			XmNlabelType, XmPIXMAP,
			XmNlabelPixmap, mouseLeftCursor, NULL);
		(void) XtVaCreateManagedWidget("mouseRightText",
			xmLabelGadgetClass, movesRowCol,
			XtVaTypedArg, XmNlabelString,
			XmRString, "Randomize", 10, NULL);
		(void) XtVaCreateManagedWidget("mouseRight",
			xmLabelGadgetClass, movesRowCol,
			XmNlabelType, XmPIXMAP,
			XmNlabelPixmap, mouseRightCursor, NULL);
	}
#endif
	(void) XtVaCreateManagedWidget("movesText",
		xmLabelGadgetClass, movesRowCol,
		XtVaTypedArg, XmNlabelString, XmRString, "Moves", 6, NULL);
	movesText = XtVaCreateManagedWidget("0",
		xmLabelWidgetClass, movesRowCol, NULL);
	(void) XtVaCreateManagedWidget("recordText",
		xmLabelGadgetClass, movesRowCol,
		XtVaTypedArg, XmNlabelString, XmRString, "Record", 7, NULL);
	recordText = XtVaCreateManagedWidget("0",
		xmLabelWidgetClass, movesRowCol, NULL);

	labelRowCol = XtVaCreateManagedWidget("labelRowCol",
		xmRowColumnWidgetClass, controlPanel, NULL);
	(void) XtVaCreateManagedWidget("PeriodText",
		xmLabelGadgetClass, labelRowCol,
		XtVaTypedArg, XmNlabelString, XmRString, "Periods:", 9, NULL);
	radioRowCol = XtVaCreateManagedWidget("radioRowCol",
		xmRowColumnWidgetClass, labelRowCol,
		XmNnumColumns, 1,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_COLUMN,
		XmNradioBehavior, True, NULL);
	for (i = 0; i < XtNumber(modeString); i++) {
		modes[i] = XtVaCreateManagedWidget(modeString[i],
			xmToggleButtonGadgetClass, radioRowCol,
			XmNradioBehavior, True, NULL);
		XtAddCallback(modes[i], XmNvalueChangedCallback,
			(XtCallbackProc) modeToggle, (XtPointer) i);
	}
	switchRowCol = XtVaCreateManagedWidget("switchRowCol",
		xmRowColumnWidgetClass, controlPanel,
		XmNnumColumns, 1,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_COLUMN, NULL);
	orientizeSwitch = XtVaCreateManagedWidget("Oriented",
		xmToggleButtonWidgetClass, switchRowCol,
		XmNset, DEFAULT_ORIENT, NULL);
	XtAddCallback(orientizeSwitch, XmNvalueChangedCallback,
		(XtCallbackProc) orientToggle, (XtPointer) NULL);
	practiceSwitch = XtVaCreateManagedWidget("Practice",
		xmToggleButtonWidgetClass, switchRowCol,
		XmNset, DEFAULT_PRACTICE, NULL);
	XtAddCallback(practiceSwitch, XmNvalueChangedCallback,
		(XtCallbackProc) practiceToggle, (XtPointer) NULL);
	speedSlider = XtVaCreateManagedWidget("speedSlider",
		xmScaleWidgetClass, switchRowCol,
		XtVaTypedArg, XmNtitleString, XmRString, "Animation Speed", 16,
		XmNminimum, MIN_SPEED,
		XmNmaximum, MAX_SPEED,
		XmNvalue, MAX_SPEED - 10,
		XmNshowValue, True,
		XmNorientation, XmHORIZONTAL, NULL);
	XtAddCallback(speedSlider, XmNvalueChangedCallback,
		(XtCallbackProc) speedChangeListener, (XtPointer) NULL);
	messageRowCol = XtVaCreateManagedWidget("messageRowCol",
		xmRowColumnWidgetClass, controlPanel,
		XmNnumColumns, 1,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_COLUMN, NULL);
	message = XtVaCreateManagedWidget("Play Dino! (use mouse and keypad)",
		xmLabelWidgetClass, messageRowCol, NULL);
	puzzle2d = XtCreateManagedWidget("dino",
		dino2dWidgetClass, mainPanel, NULL, 0);
	XtVaSetValues(puzzle2d,
		XtNheight, 200, NULL);
	XtAddCallback(puzzle2d, XtNselectCallback,
		(XtCallbackProc) puzzleListener, (XtPointer) NULL);
#ifdef HAVE_OPENGL
	puzzle3d = XtCreateManagedWidget("dino",
		dinoGLWidgetClass, mainPanel, NULL, 0);
#else
	puzzle3d = XtCreateManagedWidget("dino",
		dino3dWidgetClass, mainPanel, NULL, 0);
#endif
	XtVaSetValues(puzzle3d,
		XtNheight, 200, NULL);
#else
	shell = XtCreateApplicationShell(argv[0],
		topLevelShellWidgetClass, NULL, 0);
	puzzle2d = XtCreateManagedWidget("dino",
		dino2dWidgetClass, topLevel, NULL, 0);
	XtAddCallback(puzzle2d, XtNselectCallback,
		(XtCallbackProc) puzzleListener, (XtPointer) NULL);
#ifdef HAVE_OPENGL
	puzzle3d = XtCreateManagedWidget("dino",
		dinoGLWidgetClass, shell, NULL, 0);
#else
	puzzle3d = XtCreateManagedWidget("dino",
		dino3dWidgetClass, shell, NULL, 0);
#endif
#endif
	XtVaGetValues(puzzle2d,
		XtNpixmapSize, &pixmapSize, NULL);
#ifdef HAVE_XPM
	{
		XpmAttributes xpmAttributes;
		XpmColorSymbol transparentColor[1] = {{NULL,
			(char *) "none", 0 }};
		Pixel bg;

		xpmAttributes.valuemask = XpmColorSymbols | XpmCloseness;
		xpmAttributes.colorsymbols = transparentColor;
		xpmAttributes.numsymbols = 1;
		xpmAttributes.closeness = 40000;
		XtVaGetValues(topLevel, XtNbackground, &bg, NULL);
		transparentColor[0].pixel = bg;
		(void) XpmCreatePixmapFromData(XtDisplay(topLevel),
			RootWindowOfScreen(XtScreen(topLevel)),
			RESIZE_XPM(pixmapSize), &pixmap, NULL,
			&xpmAttributes);
	}
	if (pixmap == (Pixmap) NULL)
#endif
		pixmap = XCreateBitmapFromData(XtDisplay(topLevel),
			RootWindowOfScreen(XtScreen(topLevel)),
			DEFINE_XBM);
#ifdef HAVE_MOTIF
	XtVaSetValues(topLevel,
		XmNkeyboardFocusPolicy, XmPOINTER, /* not XmEXPLICIT */
		XtNiconPixmap, pixmap, NULL);
#else
	XtVaSetValues(topLevel,
		XtNinput, True,
		XtNiconPixmap, pixmap, NULL);
	XtVaSetValues(shell,
		XtNinput, True,
		XtNiconPixmap, pixmap, NULL);
#endif
	XtAddCallback(puzzle3d, XtNselectCallback,
		(XtCallbackProc) puzzleListener, (XtPointer) NULL);
	initialize();
	XtRealizeWidget(topLevel);
#ifndef HAVE_MOTIF
	XtRealizeWidget(shell);
#endif
	XGrabButton(XtDisplay(puzzle2d), (unsigned int) AnyButton, AnyModifier,
		XtWindow(puzzle2d), TRUE, (unsigned int) (ButtonPressMask |
		ButtonMotionMask | ButtonReleaseMask),
		GrabModeAsync, GrabModeAsync, XtWindow(puzzle2d),
		XCreateFontCursor(XtDisplay(puzzle2d), XC_hand2));
	XGrabButton(XtDisplay(puzzle3d), (unsigned int) AnyButton, AnyModifier,
		XtWindow(puzzle3d), TRUE, (unsigned int) (ButtonPressMask |
		ButtonMotionMask | ButtonReleaseMask),
		GrabModeAsync, GrabModeAsync, XtWindow(puzzle3d),
		XCreateFontCursor(XtDisplay(puzzle3d), XC_hand2));
	XtMainLoop();

#ifdef VMS
	return 1;
#else
	return 0;
#endif
}
#endif
