/*
 * @(#)xthreed.c
 *
 * Copyright 1994 - 2022  David A. Bagley, bagleyd AT verizon.net
 *
 * 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 Threed */

#ifndef WINVER
#include "version.h"
static const char aboutHelp[] = {
"Threed Version " VERSION "\n"
"Send bugs (reports or fixes) to the author: "
"David Bagley <bagleyd AT verizon.net>\n"
"The latest version is at: "
"https://www.sillycycle.com/puzzles.html\n"
"Originally written by Thomas W. Olsen "
"in the \"The C Users Journal\"."
};

static const char synopsisHelp[] = {
"[-geometry [{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]]\n"
"[-display [{host}]:[{vs}]] [-{foreground|fg} {color}]\n"
"[-{background|bg} {color}] [-frame {color}]\n"
"[-white {color}] [-[lt|dk]gray {color}]\n"
"[-[ltlt|dkdk]gray {color}] [-black {color}]\n"
"[-another {color}] [-{border|bd} {color}] [-delay msecs]\n"
"[-[no]sound] [-bumpSound {filename}]\n"
"[-moveSound {filename}] [-[no]surface]\n"
"[-object {int}] [-x {int}] [-y {int}] [-z {int}]\n"
"[-theta {int}] [-phi {int}] [-psi {int}] [-version]"
};
#endif

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA) || defined(WINVER)
static const char descriptionHelp[] = {
"A simple 3D viewer (though may need some work).\n"
"\n"
"The roll part does not seem to be quite right and may not\n"
"track well.  Of note is the mouse to 3D surface detection.\n"
"\n"
"In the sample data, Cubes and F16 are big, so the polyhedrons\n"
"are scaled up to them in the data file."
};

static const char featuresHelp[] = {
"Press \"L\" or \"l\" keys to move object left.\n"
"\n"
"Press \"R\" or \"r\" keys to move object right.\n"
"\n"
"Press \"U\" or \"u\" keys to move object up.\n"
"\n"
"Press \"D\" or \"d\" keys to move object down.\n"
"\n"
"Press \"I\" or \"i\" keys to move object in.\n"
"\n"
"Press \"O\" or \"o\" keys to move object out.\n"
"\n"
"Press \"B\" or \"b\" keys to change object.\n"
"\n"
"Press \"S\" or \"s\" keys to change surface.\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 kill program.\n"
"\n"
"Use the key pad or arrow keys to rotate object without the mouse.\n"
"Key pad is defined for Threed as:\n"
"  /     Counterclockwise\n"
"  8     Up\n"
"  ^\n"
"4<5>6   Left, Clockwise, Right\n"
"  v\n"
"  2     Down"
};

static const char referencesHelp[] = {
"Original code by\n"
"Thomas W. Olsen \"The C Users Journal\".\n"
"Andre LaMothe \"Black Art of 3D Game Programming\".\n"
"Arash Partow \"http://www.partow.net\"."
};
#endif

#ifdef WINVER
#include "ThreedP.h"
#define TITLE "wthreed"

static ThreeDRec widget;

#define PRINT_MESSAGE(b) (void) MessageBox(widget.core.hWnd, (LPCSTR) b, "Warning", MB_OK);


static void
initialize(ThreeDWidget w, HBRUSH brush)
{
	HMENU hMenu;

	initializeThreeD(w, brush);

	hMenu = GetMenu(w->core.hWnd);
	(void) CheckMenuItem(hMenu, (unsigned int)
		w->threed.object + ACTION_TETRA, MF_CHECKED);
}

void
setThreed(ThreeDWidget w, int reason)
{
	HMENU hMenu;

	switch (reason) {
	case ACTION_HIDE:
		ShowWindow(w->core.hWnd, SW_SHOWMINIMIZED);
		break;
	case ACTION_OBJECT:
		hMenu = GetMenu(w->core.hWnd);
		if (w->threed.numObjects > 0) {
			(void) CheckMenuItem(hMenu, (unsigned int)
				w->threed.object + ACTION_TETRA, MF_UNCHECKED);
			w->threed.object = (w->threed.object + 1) %
				w->threed.numObjects;
			(void) CheckMenuItem(hMenu, (unsigned int)
				w->threed.object + ACTION_TETRA, MF_CHECKED);
		}
		break;
	case ACTION_SURFACE:
		w->threed.surface = !w->threed.surface;
		break;
	}
}

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;
	HMENU hMenu;
	PAINTSTRUCT paint;
	int vPos, hPos;
	static Boolean mousePressed = False;

	widget.core.hWnd = hWnd;
	switch (message) {
	case WM_CREATE:
		initialize(&widget, brush);
		break;
	case WM_DESTROY:
		destroyThreeD(brush);
		break;
	case WM_SIZE:
		(void) InvalidateRect(widget.core.hWnd, NULL, TRUE);
		break;
	case WM_PAINT:
		widget.core.hDC = BeginPaint(widget.core.hWnd, &paint);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_BRUSH));
		exposeThreeD(&widget);
		(void) EndPaint(hWnd, &paint);
		break;
	case WM_LBUTTONDOWN:
		mousePressed = True;
		widget.core.hDC = GetDC(hWnd);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_PEN));
		selectThreeD(&widget, LOWORD(lParam), HIWORD(lParam)
			/*, (GetKeyState(VK_CONTROL) >> 1) ? 1 : 0*/);
		(void) ReleaseDC(hWnd, widget.core.hDC);
		break;
	case WM_MOUSEMOVE:
		if (!(wParam & MK_LBUTTON)) {
			if (mousePressed) {
				mousePressed = False;
				widget.core.hDC = GetDC(hWnd);
				(void) SelectObject(widget.core.hDC,
					GetStockObject(NULL_PEN));
				releaseThreeD(&widget/*, LOWORD(lParam), HIWORD(lParam)
					((GetKeyState(VK_SHIFT) >> 1) || (GetKeyState(VK_CAPITAL) & 1)),
					(GetKeyState(VK_CONTROL) >> 1) ? 1 : 0*/);
				(void) ReleaseDC(hWnd, widget.core.hDC);
			}
			break;
		}
		widget.core.hDC = GetDC(hWnd);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_PEN));
		motionThreeD(&widget, LOWORD(lParam), HIWORD(lParam)
			/*, ((GetKeyState(VK_SHIFT) >> 1) || (GetKeyState(VK_CAPITAL) & 1)),
				(GetKeyState(VK_CONTROL) >> 1) ? 1 : 0*/);
		(void) ReleaseDC(hWnd, widget.core.hDC);
		break;
	case WM_LBUTTONUP:
		mousePressed = False;
		widget.core.hDC = GetDC(hWnd);
		(void) SelectObject(widget.core.hDC,
			GetStockObject(NULL_PEN));
		releaseThreeD(&widget/*, LOWORD(lParam), HIWORD(lParam)
			((GetKeyState(VK_SHIFT) >> 1) || (GetKeyState(VK_CAPITAL) & 1)),
			(GetKeyState(VK_CONTROL) >> 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)) {
				widget.threed.deltaAngle.phi =
					-widget.threed.angle.phi;
				widget.threed.angle.phi =
					(widget.threed.angle.phi +
					DELTADEGREES) % NUM_DEGREES;
				widget.threed.deltaAngle.phi +=
					widget.threed.angle.phi;
				(void) InvalidateRect(hWnd, NULL, TRUE);
			} else if (zDelta < -(WHEEL_DELTA >> 1)) {
				widget.threed.deltaAngle.phi =
					-widget.threed.angle.phi;
				widget.threed.angle.phi = (NUM_DEGREES +
					widget.threed.angle.phi -
					DELTADEGREES) % NUM_DEGREES;
				widget.threed.deltaAngle.phi +=
					widget.threed.angle.phi;
				(void) InvalidateRect(hWnd, NULL, TRUE);
			}
		}
		(void) ReleaseDC(hWnd, widget.core.hDC);
		break;
#endif
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case ACTION_EXIT:
			destroyThreeD(brush);
			break;
		case ACTION_TETRA:
		case ACTION_HEXA:
		case ACTION_OCTA:
		case ACTION_DODECA:
		case ACTION_ICOSA:
		case ACTION_RHOMBIC:
		case ACTION_TRIACONTA:
		case ACTION_TRUNC:
		case ACTION_IRREG:
		case ACTION_TETRADECA:
		case ACTION_STAR:
		case ACTION_GREAT_DODECA:
		case ACTION_SMALL_STEL_DODECA:
		case ACTION_GREAT_STEL_DODECA:
		case ACTION_GREAT_ICOSA:
		case ACTION_CUBOCT:
		case ACTION_SQUARE_ANTIPRISM:
		case ACTION_PYRAMID:
		case ACTION_CUBES:
		case ACTION_F16:
			hMenu = GetMenu(hWnd);
			(void) CheckMenuItem(hMenu,
				(unsigned int) widget.threed.object + ACTION_TETRA,
				MF_UNCHECKED);
			(void) CheckMenuItem(hMenu,
				LOWORD(wParam), MF_CHECKED);
			setobjectThreeD(&widget, LOWORD(wParam) - ACTION_TETRA);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_HIDE:
			hideThreeD(&widget);
			break;
		case ACTION_OBJECT:
			objectThreeD(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_SURFACE:
			surfaceThreeD(&widget);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DISTXI:
			if (widget.threed.distance.x <= MAX_DISTANCE -
					DELTADISTANCE)
				widget.threed.distance.x += DELTADISTANCE;
			(void) SetScrollPos(hWnd, SB_HORZ,
				widget.threed.distance.x, TRUE);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DISTXD:
			if (widget.threed.distance.x >= MIN_DISTANCE +
					DELTADISTANCE)
				widget.threed.distance.x -= DELTADISTANCE;
			(void) SetScrollPos(hWnd, SB_HORZ,
				widget.threed.distance.x, TRUE);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DISTYI:
			if (widget.threed.distance.y <= MAX_DISTANCE -
					DELTADISTANCE)
				widget.threed.distance.y += DELTADISTANCE;
			(void) SetScrollPos(hWnd, SB_VERT,
				widget.threed.distance.y, TRUE);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DISTYD:
			if (widget.threed.distance.y >= MIN_DISTANCE +
					DELTADISTANCE)
				widget.threed.distance.y -= DELTADISTANCE;
			(void) SetScrollPos(hWnd, SB_VERT,
				widget.threed.distance.y, TRUE);
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DEPTHZI:
			if (widget.threed.distance.z <= MAX_DEPTH - DELTADEPTH)
				widget.threed.distance.z += DELTADEPTH;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DEPTHZD:
			if (widget.threed.distance.z >= MIN_DEPTH + DELTADEPTH)
				widget.threed.distance.z -= DELTADEPTH;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_THETAI:
			widget.threed.deltaAngle.theta =
				-widget.threed.angle.theta;
			widget.threed.angle.theta = (widget.threed.angle.theta +
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.theta +=
				widget.threed.angle.theta;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_THETAD:
			widget.threed.deltaAngle.theta =
				-widget.threed.angle.theta;
			widget.threed.angle.theta = (NUM_DEGREES +
				widget.threed.angle.theta -
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.theta +=
				widget.threed.angle.theta;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_PHII:
			widget.threed.deltaAngle.phi =
				-widget.threed.angle.phi;
			widget.threed.angle.phi = (widget.threed.angle.phi +
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.phi +=
				widget.threed.angle.phi;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_PHID:
			widget.threed.deltaAngle.phi =
				-widget.threed.angle.phi;
			widget.threed.angle.phi = (NUM_DEGREES +
				widget.threed.angle.phi -
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.phi +=
				widget.threed.angle.phi;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_PSII:
			widget.threed.deltaAngle.psi =
				-widget.threed.angle.psi;
			widget.threed.angle.psi = (widget.threed.angle.psi +
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.psi +=
				widget.threed.angle.psi;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_PSID:
			widget.threed.deltaAngle.psi =
				-widget.threed.angle.psi;
			widget.threed.angle.psi = (NUM_DEGREES +
				widget.threed.angle.psi -
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.psi +=
				widget.threed.angle.psi;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DIAG0:
			widget.threed.deltaAngle.theta =
				-widget.threed.angle.theta;
			widget.threed.deltaAngle.phi =
				-widget.threed.angle.phi;
			widget.threed.angle.theta = (NUM_DEGREES +
				widget.threed.angle.theta -
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.angle.phi = (widget.threed.angle.phi +
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.theta +=
				widget.threed.angle.theta;
			widget.threed.deltaAngle.phi +=
				widget.threed.angle.phi;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DIAG1:
			widget.threed.deltaAngle.theta =
				-widget.threed.angle.theta;
			widget.threed.deltaAngle.phi =
				-widget.threed.angle.phi;
			widget.threed.angle.theta = (NUM_DEGREES +
				widget.threed.angle.theta -
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.angle.phi = (NUM_DEGREES +
				widget.threed.angle.phi -
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.theta +=
				widget.threed.angle.theta;
			widget.threed.deltaAngle.phi +=
				widget.threed.angle.phi;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DIAG2:
			widget.threed.deltaAngle.theta =
				-widget.threed.angle.theta;
			widget.threed.deltaAngle.phi =
				-widget.threed.angle.phi;
			widget.threed.angle.theta = (widget.threed.angle.theta +
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.angle.phi = (NUM_DEGREES +
				widget.threed.angle.phi -
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.theta +=
				widget.threed.angle.theta;
			widget.threed.deltaAngle.phi +=
				widget.threed.angle.phi;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_DIAG3:
			widget.threed.deltaAngle.theta =
				-widget.threed.angle.theta;
			widget.threed.deltaAngle.phi =
				-widget.threed.angle.phi;
			widget.threed.angle.theta = (widget.threed.angle.theta +
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.angle.phi = (widget.threed.angle.phi +
				DELTADEGREES) % NUM_DEGREES;
			widget.threed.deltaAngle.theta +=
				widget.threed.angle.theta;
			widget.threed.deltaAngle.phi +=
				widget.threed.angle.phi;
			(void) InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ACTION_SPEED:
			speedThreeD(&widget);
			break;
		case ACTION_SLOW:
			slowThreeD(&widget);
			break;
		case ACTION_SOUND:
			soundThreeD(&widget);
			break;
		case ACTION_DESCRIPTION:
			(void) MessageBox(hWnd, descriptionHelp,
				"Description", MB_OK | MB_ICONQUESTION);
			break;
		case ACTION_FEATURES:
			(void) MessageBox(hWnd, featuresHelp,
				"Features", MB_OK | MB_ICONEXCLAMATION);
			break;
		case ACTION_REFERENCES:
			(void) MessageBox(hWnd, referencesHelp,
				"References", MB_OK | MB_ICONINFORMATION);
			break;
		case ACTION_ABOUT:
			(void) DialogBox(widget.core.hInstance,
				"About", hWnd, (DLGPROC) about);
			break;
		}
		break;
	case WM_HSCROLL:
		if (wParam == SB_THUMBTRACK)
			break;
		hPos = GetScrollPos(hWnd, SB_HORZ);
		switch (wParam) {
		case SB_TOP:
			hPos = MAX_DISTANCE;
			break;
		case SB_BOTTOM:
			hPos = MIN_DISTANCE;
			break;
		case SB_LINEUP:
		case SB_PAGEUP:
			hPos -= DELTADISTANCE;
			break;
		case SB_PAGEDOWN:
		case SB_LINEDOWN:
			hPos += DELTADISTANCE;
			break;
		case SB_THUMBPOSITION:
			hPos = LOWORD(lParam);
			break;
		}
		if (hPos < MIN_DISTANCE)
			hPos = MIN_DISTANCE;
		else if (hPos > MAX_DISTANCE)
			hPos = MAX_DISTANCE;
		(void) SetScrollPos(hWnd, SB_HORZ, hPos, TRUE);
		widget.threed.distance.x = hPos;
		(void) InvalidateRect(hWnd, NULL, TRUE);
		break;
	case WM_VSCROLL:
		if (wParam == SB_THUMBTRACK)
			break;
		vPos = GetScrollPos(hWnd, SB_VERT);
		switch (wParam) {
		case SB_TOP:
			vPos = MIN_DISTANCE;
			break;
		case SB_BOTTOM:
			vPos = MAX_DISTANCE;
			break;
		case SB_LINEUP:
		case SB_PAGEUP:
			vPos -= DELTADISTANCE;
			break;
		case SB_PAGEDOWN:
		case SB_LINEDOWN:
			vPos += DELTADISTANCE;
			break;
		case SB_THUMBPOSITION:
			vPos = LOWORD(lParam);
			break;
		}
		if (vPos < MIN_DISTANCE)
			vPos = MIN_DISTANCE;
		else if (vPos > MAX_DISTANCE)
			vPos = MAX_DISTANCE;
		(void) SetScrollPos(hWnd, SB_VERT, vPos, TRUE);
		widget.threed.distance.y = -vPos;
		(void) InvalidateRect(hWnd, NULL, TRUE);
		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 | WS_VSCROLL | WS_HSCROLL,
		(signed) CW_USEDEFAULT,
		(signed) CW_USEDEFAULT,
		(signed) CW_USEDEFAULT,
		(signed) CW_USEDEFAULT,
		HWND_DESKTOP,
		(HMENU) NULL,
		hInstance,
		(XtPointer) NULL);
	if (!hWnd)
		return FALSE;
	(void) SetScrollRange(hWnd, SB_HORZ, MIN_DISTANCE, MAX_DISTANCE, TRUE);
	(void) SetScrollRange(hWnd, SB_VERT, MIN_DISTANCE, MAX_DISTANCE, TRUE);
	(void) SetScrollPos(hWnd, SB_HORZ, widget.threed.distance.x, TRUE);
	(void) SetScrollPos(hWnd, SB_VERT, widget.threed.distance.y, TRUE);
	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 (int) msg.wParam;
}

#else

#include "xwin.h"
#include "xgui.h"
#include <X11/StringDefs.h>
#include "Threed.h"
#ifdef HAVE_XPM
#include <X11/xpm.h>
#ifdef CONSTPIXMAPS
#include "16x16/threed.xpm"
#include "22x22/threed.xpm"
#include "24x24/threed.xpm"
#include "32x32/threed.xpm"
#include "48x48/threed.xpm"
#include "64x64/threed.xpm"
#else
#include "pixmaps/16x16/threed.xpm"
#include "pixmaps/22x22/threed.xpm"
#include "pixmaps/24x24/threed.xpm"
#include "pixmaps/32x32/threed.xpm"
#include "pixmaps/48x48/threed.xpm"
#include "pixmaps/64x64/threed.xpm"
#endif
#define RESIZE_XPM(s) ((char **) (((s)<=32)?\
(((s)<=22)?(((s)<=16)?threed_16x16:threed_22x22):\
(((s)<=24)?threed_24x24:threed_32x32)):\
(((s)<=48)?threed_48x48:threed_64x64)))
#endif
#include "pixmaps/64x64/threed.xbm"
#define DEFINE_XBM (char *) threed_64x64_bits, threed_64x64_width, threed_64x64_height

#ifdef HAVE_ATHENA
static Widget distXSliderLabel, distYSliderLabel, distZSliderLabel;
static Widget thetaSliderLabel, phiSliderLabel, psiSliderLabel;
static Widget objectLabel;
static const char *fileTypes[] =
{
	"Exit"
};
#define numFileTypes (sizeof(fileTypes)/sizeof(fileTypes[0]))
static const char *controlTypes[] =
{
	"Sound @  "
};
#define numControlTypes (sizeof(controlTypes)/sizeof(controlTypes[0]))
static const char *helpTypes[] =
{
	"Description...",
	"Features...",
	"Synopsis...",
	"References...",
	"About..."
};
#define numHelpTypes (sizeof(helpTypes)/sizeof(helpTypes[0]))
#endif
#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)
#define PRINT_MESSAGE(b) printState(message, b)
static Widget distXSlider, distYSlider, distZSlider;
static Widget thetaSlider, phiSlider, psiSlider;
static Widget objectMenu, surfaceToggle;
static const char *distXLabel = "    X:";
static const char *distYLabel = "  Y:";
static const char *distZLabel = "  Z:";
static const char *thetaLabel = "Theta:";
static const char *phiLabel = "Phi:";
static const char *psiLabel = "Psi:";
static Widget soundMenuItem;
static Widget descriptionDialog, featuresDialog;
static Widget synopsisDialog, referencesDialog, aboutDialog;
#else
#define PRINT_MESSAGE(b) XtWarning(b)
#endif
static Pixmap pixmap = None;
static Widget topLevel, threed;

static XrmOptionDescRec options[] = {
	{(char *) "-foreground", (char *) "*threed.Foreground", XrmoptionSepArg, NULL},
	{(char *) "-fg", (char *) "*threed.Foreground", XrmoptionSepArg, NULL},
	{(char *) "-background", (char *) "*Background", XrmoptionSepArg, NULL},
	{(char *) "-bg", (char *) "*Background", XrmoptionSepArg, NULL},
	{(char *) "-frame", (char *) "*threed.frameColor", XrmoptionSepArg, NULL},
	{(char *) "-white", (char *) "*threed.whiteBrush", XrmoptionSepArg, NULL},
	{(char *) "-ltgray", (char *) "*threed.ltgrayBrush", XrmoptionSepArg, NULL},
	{(char *) "-ltltgray", (char *) "*threed.ltltgrayBrush", XrmoptionSepArg, NULL},
	{(char *) "-gray", (char *) "*threed.grayBrush", XrmoptionSepArg, NULL},
	{(char *) "-dkgray", (char *) "*threed.dkgrayBrush", XrmoptionSepArg, NULL},
	{(char *) "-dkdkgray", (char *) "*threed.dkdkgrayBrush", XrmoptionSepArg, NULL},
	{(char *) "-black", (char *) "*threed.blackBrush", XrmoptionSepArg, NULL},
	{(char *) "-another", (char *) "*threed.anotherBrush", XrmoptionSepArg, NULL},
	{(char *) "-border", (char *) "*threed.surfaceBorder", XrmoptionSepArg, NULL},
	{(char *) "-bd", (char *) "*threed.surfaceBorder", XrmoptionSepArg, NULL},
	{(char *) "-delay", (char *) "*threed.delay", XrmoptionSepArg, NULL},
	{(char *) "-sound", (char *) "*threed.sound", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-nosound", (char *) "*threed.sound", XrmoptionNoArg, (char *) "FALSE"},
	{(char *) "-bumpSound", (char *) "*threed.bumpSound", XrmoptionSepArg, NULL},
	{(char *) "-moveSound", (char *) "*threed.moveSound", XrmoptionSepArg, NULL},
	{(char *) "-objectName", (char *) "*threed.name", XrmoptionSepArg, (char *) NULL},
	{(char *) "-object", (char *) "*threed.object", XrmoptionSepArg, (char *) NULL},
	{(char *) "-surface", (char *) "*threed.surface", XrmoptionNoArg, (char *) "TRUE"},
	{(char *) "-nosurface", (char *) "*threed.surface", XrmoptionNoArg, (char *) "FALSE"},
	{(char *) "-x", (char *) "*threed.distanceX", XrmoptionSepArg, (char *) NULL},
	{(char *) "-y", (char *) "*threed.distanceY", XrmoptionSepArg, (char *) NULL},
	{(char *) "-z", (char *) "*threed.distanceZ", XrmoptionSepArg, (char *) NULL},
	{(char *) "-theta", (char *) "*threed.thetaDegrees", XrmoptionSepArg, (char *) NULL},
	{(char *) "-phi", (char *) "*threed.phiDegrees", XrmoptionSepArg, (char *) NULL},
	{(char *) "-psi", (char *) "*threed.psiDegrees", XrmoptionSepArg, (char *) NULL},
	{(char *) "-version", (char *) "*threed.versionOnly", XrmoptionNoArg, (char *) "TRUE"}
};

#ifdef HAVE_MOTIF
static void
soundCallback(Widget w, XtPointer clientData,
		XmToggleButtonCallbackStruct *cbs)
{
	Boolean value = cbs->set;

	XtVaSetValues(threed,
		XtNsound, value, NULL);
}

#else

static void printState(Widget w, char *name, int object, Boolean surface,
		IntPoint3D distance, IntAngle3D angle)
{
	char titleDsp[TITLE_LENGTH] = "";
#ifdef HAVE_SNPRINTF
	(void) snprintf(titleDsp, TITLE_LENGTH,
		"%s %d %s:x %d:y %d:z %d:theta %d:phi %d:psi %d", name,
		object, (surface) ? "surface" : "wire",
		distance.x, distance.y, distance.z,
		angle.theta, angle.phi, angle.psi);
#else
	(void)  sprintf(titleDsp,
		"%s %d %s:x %d:y %d:z %d:theta %d:phi %d:psi %d", name,
		object, (surface) ? "surface" : "wire",
		distance.x, distance.y, distance.z,
		angle.theta, angle.phi, angle.psi);
#endif
	XtVaSetValues(w,
		XtNtitle, titleDsp, NULL);
}
#endif

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)
static void
surfaceCallback(Widget w, XtPointer clientData,
#ifdef HAVE_MOTIF
	XmToggleButtonCallbackStruct *cbs
#elif defined(HAVE_ATHENA)
	int state
#endif
	)
{
	XtVaSetValues(threed,
		XtNsurface,
#ifdef HAVE_MOTIF
		cbs->set,
#elif defined(HAVE_ATHENA)
		state,
#endif
		NULL);
}

static void
objectCallback(Widget w, XtPointer clientData,
#ifdef HAVE_MOTIF
	XmListCallbackStruct *cbs
#elif defined(HAVE_ATHENA)
	XtPointer callData
#endif
	)
{
	char *name;
	int object, old;

#ifdef HAVE_MOTIF
	object = cbs->item_position - 1;
#elif defined(HAVE_ATHENA)
	object = (size_t) clientData;
#endif
	XtVaGetValues(threed,
		XtNobject, &old, NULL);
	if (old == object)
		return;
	XtVaSetValues(threed,
		XtNobject, object, NULL);
	XtVaGetValues(threed,
		XtNobjectName, &name, NULL);
#ifdef HAVE_ATHENA
	replaceAll(name, '_', ' ');
	XtVaSetValues(objectLabel,
		XtNlabel, name, NULL);
#endif
}

static void
distXChangeCallback(Widget w, XtPointer clientData,
#ifdef HAVE_MOTIF
#ifdef USE_SPIN
		XmSpinBoxCallbackStruct *cbs
#else
		XmScaleCallbackStruct *cbs
#endif
#elif defined(HAVE_ATHENA)
		XtPointer callData
#endif
		)
{
	int dist, old;

#ifdef HAVE_MOTIF
#ifdef USE_SPIN
	dist = cbs->position;
#else
	dist = cbs->value;
#endif
#elif defined(HAVE_ATHENA)
	Widget label = (Widget) clientData;
	if (!thumbScroll(callData, &dist, MIN_DISTANCE, MAX_DISTANCE,
                        SCROLL_SIZE)) {
                return;
        }
#endif
	XtVaGetValues(threed,
		XtNdistanceX, &old, NULL);
	if (old == dist)
		return;
#ifdef HAVE_ATHENA
	setScale(w, label, dist, MIN_DISTANCE, MAX_DISTANCE, True);
#endif
	XtVaSetValues(threed,
		XtNdistanceX, dist, NULL);
}

static void
distYChangeCallback(Widget w, XtPointer clientData,
#ifdef HAVE_MOTIF
#ifdef USE_SPIN
		XmSpinBoxCallbackStruct *cbs
#else
		XmScaleCallbackStruct *cbs
#endif
#elif defined(HAVE_ATHENA)
		XtPointer callData
#endif
		)
{
	int dist, old;

#ifdef HAVE_MOTIF
#ifdef USE_SPIN
	dist = cbs->position;
#else
	dist = cbs->value;
#endif
#elif defined(HAVE_ATHENA)
	Widget label = (Widget) clientData;
	if (!thumbScroll(callData, &dist, MIN_DISTANCE, MAX_DISTANCE,
                        SCROLL_SIZE)) {
                return;
        }
#endif
	XtVaGetValues(threed,
		XtNdistanceY, &old, NULL);
	if (old == dist)
		return;
#ifdef HAVE_ATHENA
	setScale(w, label, dist, MIN_DISTANCE, MAX_DISTANCE, True);
#endif
	XtVaSetValues(threed,
		XtNdistanceY, dist, NULL);
}

static void
distZChangeCallback(Widget w, XtPointer clientData,
#ifdef HAVE_MOTIF
#ifdef USE_SPIN
		XmSpinBoxCallbackStruct *cbs
#else
		XmScaleCallbackStruct *cbs
#endif
#elif defined(HAVE_ATHENA)
		XtPointer callData
#endif
		)
{
	int dist, old;

#ifdef HAVE_MOTIF
#ifdef USE_SPIN
	dist = cbs->position;
#else
	dist = cbs->value;
#endif
#elif defined(HAVE_ATHENA)
	Widget label = (Widget) clientData;
	if (!thumbScroll(callData, &dist, MIN_DEPTH, MAX_DEPTH,
                        SCROLL_SIZE)) {
                return;
	}
#endif
	XtVaGetValues(threed,
		XtNdistanceZ, &old, NULL);
	if (old == dist)
		return;
#ifdef HAVE_ATHENA
	setScale(w, label, dist, MIN_DEPTH, MAX_DEPTH, True);
#endif
	XtVaSetValues(threed,
		XtNdistanceZ, dist, NULL);
}

static void
thetaChangeCallback(Widget w, XtPointer clientData,
#ifdef HAVE_MOTIF
#ifdef USE_SPIN
		XmSpinBoxCallbackStruct *cbs
#else
		XmScaleCallbackStruct *cbs
#endif
#elif defined(HAVE_ATHENA)
		XtPointer callData
#endif
		)
{
	int angle, old;

#ifdef HAVE_MOTIF
#ifdef USE_SPIN
	angle = cbs->position;
#else
	angle = cbs->value;
#endif
#elif defined(HAVE_ATHENA)
	Widget label = (Widget) clientData;
	if (!thumbScroll(callData, &angle, MIN_DEGREES, MAX_DEGREES,
                        SCROLL_SIZE)) {
                return;
	}
#endif
	XtVaGetValues(threed,
		XtNthetaDegrees, &old, NULL);
	if (old == angle)
		return;
#ifdef HAVE_ATHENA
	setScale(w, label, angle, MIN_DEGREES, MAX_DEGREES, True);
#endif
	XtVaSetValues(threed,
		XtNthetaDegrees, angle, NULL);
}

static void
phiChangeCallback(Widget w, XtPointer clientData,
#ifdef HAVE_MOTIF
#ifdef USE_SPIN
		XmSpinBoxCallbackStruct *cbs
#else
		XmScaleCallbackStruct *cbs
#endif
#elif defined(HAVE_ATHENA)
		XtPointer callData
#endif
		)
{
	int angle, old;

#ifdef HAVE_MOTIF
#ifdef USE_SPIN
	angle = cbs->position;
#else
	angle = cbs->value;
#endif
#elif defined(HAVE_ATHENA)
	Widget label = (Widget) clientData;
	if (!thumbScroll(callData, &angle, MIN_DEGREES, MAX_DEGREES,
                        SCROLL_SIZE)) {
                return;
	}
#endif
	XtVaGetValues(threed,
		XtNphiDegrees, &old, NULL);
	if (old == angle)
		return;
#ifdef HAVE_ATHENA
	setScale(w, label, angle, MIN_DEGREES, MAX_DEGREES, True);
#endif
	XtVaSetValues(threed,
		XtNphiDegrees, angle, NULL);
}


static void
psiChangeCallback(Widget w, XtPointer clientData,
#ifdef HAVE_MOTIF
#ifdef USE_SPIN
		XmSpinBoxCallbackStruct *cbs
#else
		XmScaleCallbackStruct *cbs
#endif
#elif defined(HAVE_ATHENA)
		XtPointer callData
#endif
		)
{
	int angle, old;

#ifdef HAVE_MOTIF
#ifdef USE_SPIN
	angle = cbs->position;
#else
	angle = cbs->value;
#endif
#elif defined(HAVE_ATHENA)
	Widget label = (Widget) clientData;
	if (!thumbScroll(callData, &angle, MIN_DEGREES, MAX_DEGREES,
                        SCROLL_SIZE)) {
                return;
	}
#endif
	XtVaGetValues(threed,
		XtNpsiDegrees, &old, NULL);
	if (old == angle)
		return;
#ifdef HAVE_ATHENA
	setScale(w, label, angle, MIN_DEGREES, MAX_DEGREES, True);
#endif
	XtVaSetValues(threed,
		XtNpsiDegrees, angle, NULL);
}

static void
fileMenuCallback(Widget w, XtPointer clientData, XtPointer callData)
{
	int value = (int) ((size_t) clientData);

	if (value == 0)
		exit(0);
}

static void
setSoundCheck(Widget w)
{
	Boolean sound;
	XtVaGetValues(threed,
		XtNsound, &sound, NULL);
#ifdef HAVE_MOTIF
	XmToggleButtonSetState(w, sound, False);
#elif defined(HAVE_ATHENA)
	menuCheck(topLevel, w, sound);
#endif
}

static void
controlsMenuCallback(Widget w, XtPointer clientData, XtPointer callData)
{
	int value = (int) ((size_t) clientData);

	XtVaSetValues(threed,
		XtNmenu, value +
#ifdef SPEED
ACTION_SPEED
#else
ACTION_SOUND
#endif
, NULL);
	if (value == ACTION_SOUND)
		setSoundCheck(w);
}

static void
helpMenuCallback(Widget w, XtPointer clientData, XtPointer callData)
{
	int value = (int) ((size_t) clientData);
	Widget dialog;

#ifdef HAVE_ATHENA
	if (wmDeleteWindow == None)
		wmDeleteWindow = XInternAtom(XtDisplay(topLevel),
			"WM_DELETE_WINDOW", FALSE);
#endif
	switch (value) {
	case 0:
		dialog = descriptionDialog;
		break;
	case 1:
		dialog = featuresDialog;
		break;
	case 2:
		dialog = synopsisDialog;
		break;
	case 3:
		dialog = referencesDialog;
		break;
	case 4:
		dialog = aboutDialog;
		break;
	default:
		{
			char *buf;

			intCat(&buf, "helpMenuCallback: %d", value);
			XtWarning(buf);
			free(buf);
			return;
		}
	}
#ifdef HAVE_MOTIF
	XtManageChild(dialog);
#elif defined(HAVE_ATHENA)
	XtPopup(dialog, XtGrabNone);
	XSetWMProtocols(XtDisplay(topLevel),
		XtWindow(dialog), &wmDeleteWindow, 1);
#endif
}
#endif

static void initialize(Widget w)
{
	IntPoint3D distance;
	IntAngle3D angle;
	int object;
	Boolean surface;
	Boolean versionOnly;
	char *name;
#if !defined(HAVE_MOTIF) && !defined(HAVE_ATHENA)
	char **list;
	int number, i;
#endif

	XtVaGetValues(w,
		XtNdistanceX, &(distance.x),
		XtNdistanceY, &(distance.y),
		XtNdistanceZ, &(distance.z),
		XtNthetaDegrees, &(angle.theta),
		XtNphiDegrees, &(angle.phi),
		XtNpsiDegrees, &(angle.psi),
		XtNobject, &object,
		XtNsurface, &surface,
		XtNobjectName, &name,
#if !defined(HAVE_MOTIF) && !defined(HAVE_ATHENA)
		XtNobjectNumber, &number,
		XtNobjectList, &list,
#endif
		XtNversionOnly, &versionOnly, NULL);
	if (versionOnly) {
		(void) printf("%s\n", aboutHelp);
		exit(0);
	}
#ifdef HAVE_MOTIF
#ifdef USE_SPIN
	XtVaSetValues(distXSlider,
		XmNposition, distance.x, NULL);
	XtVaSetValues(distYSlider,
		XmNposition, distance.y, NULL);
	XtVaSetValues(distZSlider,
		XmNposition, distance.z, NULL);
	XtVaSetValues(thetaSlider,
		XmNposition, angle.theta, NULL);
	XtVaSetValues(phiSlider,
		XmNposition, angle.phi, NULL);
	XtVaSetValues(psiSlider,
		XmNposition, angle.psi, NULL);
#else
	XmScaleSetValue(distXSlider, distance.x);
	XmScaleSetValue(distYSlider, distance.y);
	XmScaleSetValue(distZSlider, distance.z);
	XmScaleSetValue(thetaSlider, angle.theta);
	XmScaleSetValue(phiSlider, angle.phi);
	XmScaleSetValue(psiSlider, angle.psi);
#endif
	XmToggleButtonSetState(surfaceToggle, surface, True);
#elif defined(HAVE_ATHENA)
	if (!surface)
		XawToggleUnsetCurrent(surfaceToggle);
#else
	printState(XtParent(w), name, object, surface, distance, angle);
	(void) printf("Objects:\n");
	for (i = 0; i < number; i++)
		(void) printf("\t%s\n", list[i]);
#endif
}

#ifdef HAVE_MOTIF
static void makePosVisible(Widget list_w, int item_no)
{
	int top, visible;

	XtVaGetValues(list_w,
		XmNtopItemPosition, &top,
		XmNvisibleItemCount, &visible, NULL);
	if (item_no < top) {
		XmListSetPos(list_w, item_no);
	} else if (item_no >= top + visible) {
		XmListSetBottomPos(list_w, item_no);
	}
}
#endif

static void threeDCallback(Widget w, caddr_t clientData,
		threedCallbackStruct *callData)
{
	Boolean surface;
	IntPoint3D distance;
	IntAngle3D angle;
	int object, number;
	char *name;
#ifdef HAVE_ATHENA
	int max;
	char *defaultString;
	char **list;
#endif

	XtVaGetValues(w,
		XtNobject, &object,
		XtNsurface, &surface,
		XtNdistanceX, &(distance.x),
		XtNdistanceY, &(distance.y),
		XtNdistanceZ, &(distance.z),
		XtNthetaDegrees, &(angle.theta),
		XtNphiDegrees, &(angle.phi),
		XtNpsiDegrees, &(angle.psi),
		XtNobjectNumber, &number,
#ifdef HAVE_ATHENA
		XtNobjectList, &list,
#endif
		XtNobjectName, &name, NULL);
	switch (callData->reason) {
	case ACTION_HIDE:
		(void) XIconifyWindow(XtDisplay(topLevel), XtWindow(topLevel),
			XScreenNumberOfScreen(XtScreen(topLevel)));
		break;
	case ACTION_OBJECT:
		if (number > 0)
			object = (object + 1) % number;
		XtVaSetValues(w,
			XtNobject, object, NULL);
		XtVaGetValues(w,
			XtNobjectName, &name, NULL);
#ifdef HAVE_MOTIF
		makePosVisible(objectMenu, object + 1);
		XmListSelectPos(objectMenu, object + 1, True);
#elif defined(HAVE_ATHENA)
		max = findMaxLength((char **) list, number);
		createBlank(&defaultString, max, (char *) list[object], 0);
		replaceAll(defaultString, '_', ' ');
		XtVaSetValues(objectLabel,
			XtNlabel, defaultString, NULL);
		free(defaultString);
#endif
		break;
	case ACTION_SURFACE:
		surface = !surface;
		XtVaSetValues(w,
			XtNsurface, surface, NULL);
#ifdef HAVE_MOTIF
		XmToggleButtonSetState(surfaceToggle, surface, True);
#elif defined(HAVE_ATHENA)
		XtVaSetValues(surfaceToggle,
			XtNstate, surface, NULL);
#endif
		break;
#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)
	case ACTION_SOUND:
		setSoundCheck(soundMenuItem);
		break;
#endif
	}
#ifdef HAVE_MOTIF
#ifdef USE_SPIN
	XtVaSetValues(distXSlider,
		XmNposition, distance.x, NULL);
	XtVaSetValues(distYSlider,
		XmNposition, distance.y, NULL);
	XtVaSetValues(distZSlider,
		XmNposition, distance.z, NULL);
	XtVaSetValues(thetaSlider,
		XmNposition, angle.theta, NULL);
	XtVaSetValues(phiSlider,
		XmNposition, angle.phi, NULL);
	XtVaSetValues(psiSlider,
		XmNposition, angle.psi, NULL);
#else
	XmScaleSetValue(distXSlider, distance.x);
	XmScaleSetValue(distYSlider, distance.y);
	XmScaleSetValue(distZSlider, distance.z);
	XmScaleSetValue(thetaSlider, angle.theta);
	XmScaleSetValue(phiSlider, angle.phi);
	XmScaleSetValue(psiSlider, angle.psi);
#endif
#else
#ifdef HAVE_ATHENA
	setScaleCheck(distXSlider, distXSliderLabel, distance.x,
		MIN_DISTANCE, MAX_DISTANCE, True);
	setScaleCheck(distYSlider, distYSliderLabel, distance.y,
		MIN_DISTANCE, MAX_DISTANCE, True);
	setScaleCheck(distZSlider, distZSliderLabel, distance.z,
		MIN_DEPTH, MAX_DEPTH, True);
	setScaleCheck(thetaSlider, thetaSliderLabel, angle.theta,
		MIN_DEGREES, MAX_DEGREES, True);
	setScaleCheck(phiSlider, phiSliderLabel, angle.phi,
		MIN_DEGREES, MAX_DEGREES, True);
	setScaleCheck(psiSlider, psiSliderLabel, angle.psi,
		MIN_DEGREES, MAX_DEGREES, True);
#endif
	printState(XtParent(w), name, object, surface, distance, angle);
#endif
}

#ifdef HAVE_MOTIF
#if 0
static void
createMenu(Widget *menu, Widget rowCol, int init)
{
	XmString choice, tetra, hexa, octa, dodeca, icosa,
	XmString rhombic, triaconta, star, pyramid, cubes, f16;

	choice = XmStringCreateSimple("Object:");
	tetra = XmStringCreateSimple("Tetrahedron");
	hexa = XmStringCreateSimple("Hexahedron");
	octa = XmStringCreateSimple("Octahedron");
	dodeca = XmStringCreateSimple("Dodecahedron");
	icosa = XmStringCreateSimple("Icosahedron");
	rhombic = XmStringCreateSimple("Rhombic");
	triaconta = XmStringCreateSimple("Triaconta");
	star = XmStringCreateSimple("Star");
	pyramid = XmStringCreateSimple("Pyramid");
	cubes = XmStringCreateSimple("Cubes");
	f16 = XmStringCreateSimple("F16");
	*menu = XmVaCreateSimpleOptionMenu(rowCol, "modeMenu",
		choice, 'M', init, object_response,
		XmVaPUSHBUTTON, tetra, 'T', NULL, NULL,
		XmVaPUSHBUTTON, hexa, 'H', NULL, NULL,
		XmVaPUSHBUTTON, octa, 'O', NULL, NULL,
		XmVaPUSHBUTTON, dodeca, 'D', NULL, NULL,
		XmVaPUSHBUTTON, icosa, 'I', NULL, NULL,
		XmVaPUSHBUTTON, rhombic, 'R', NULL, NULL,
		XmVaPUSHBUTTON, triaconta, 'N', NULL, NULL,
		XmVaPUSHBUTTON, star, 't', NULL, NULL,
		XmVaPUSHBUTTON, pyramid, 'P', NULL, NULL,
		XmVaPUSHBUTTON, cubes, 'C', NULL, NULL,
		XmVaPUSHBUTTON, f16, 'F', NULL, NULL, NULL);
	XmStringFree(choice);
	XmStringFree(tetra);
	XmStringFree(hexa);
	XmStringFree(octa);
	XmStringFree(dodeca);
	XmStringFree(icosa);
	XmStringFree(rhombic);
	XmStringFree(triaconta);
	XmStringFree(star);
	XmStringFree(pyramid);
	XmStringFree(cubes);
	XmStringFree(f16);
	XtManageChild(*menu);
}
#endif

static void
createObjectList(Widget *rowCol, char **list, int init, int num)
{
	XmString label;
	XmStringTable table;
	int i;
	table = (XmStringTable) XtMalloc (num * sizeof (XmString *));
	for (i = 0; i < num; i++) {
		replaceAll(list[i], '_', ' ');
		table[i] = XmStringCreateSimple(list[i]);
	}

	label = XmStringCreateSimple((char *) "Object list:");
	(void) XtVaCreateManagedWidget("objectLabel",
		xmLabelWidgetClass, *rowCol,
		XmNlabelString, label, NULL);
	XmStringFree(label);
	objectMenu = XmCreateScrolledList(*rowCol, (char *) "scrolledList",
		NULL, 0);
	XtVaSetValues(objectMenu,
		XmNvisibleItemCount, 5,
		XmNitemCount, num,
		XmNitems, table, NULL);
	XtManageChild(objectMenu);
	XtAddCallback(objectMenu, XmNbrowseSelectionCallback,
		(XtCallbackProc) objectCallback, (XtPointer) NULL);
	XmListSelectPos(objectMenu, init + 1, True);
	makePosVisible(objectMenu, init + 1);

	for (i = 0; i < num; i++)
		XmStringFree(table[i]);
	XtFree((char *) table);
}
#endif

int
main(int argc, char **argv)
{
	int pixmapSize;
	XtAppContext appCon;
#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)
	Widget mainPanel, controlPanel, menuBar;
	Widget distRowCol, angleRowCol, shapeRowCol;
	Widget controlsMenu;
	char **list;
	int object, number;
#endif
#ifdef HAVE_MOTIF
	Widget distXChangerRowCol, distYChangerRowCol, distZChangerRowCol;
	Widget thetaChangerRowCol, phiChangerRowCol, psiChangerRowCol;
	Widget pullDownMenu, widget;
	Widget panel;
	XmString fileString, controlsString;
	XmString quitString;
#ifdef SPEED
	XmString speedString, slowString;
#endif
	XmString soundString;

#elif defined(HAVE_ATHENA)
	XtActionsRec actions[] = {
		{(char *) "DeleteWindowProc", deleteWindowProc},
		{(char *) "ClosePopupPanel", (XtActionProc) closePopupPanel}
	};
	String fallbackResources[] = {
		(char *) "?.translations: #override <Message>WM_PROTOCOLS: DeleteWindowProc()",
		(char *) "?.TransientShell.translations: #override <Message>WM_PROTOCOLS: ClosePopupPanel()",
		NULL
	};
	Widget distXBox, distYBox, distZBox;
	Widget thetaBox, phiBox, psiBox;
	Widget objectBox, surfaceBox;
	Widget w, fileMenu, helpMenu;
	Widget fileLabel, controlsLabel, helpLabel;
	int distX, distY, distZ;
	int theta, phi, psi;
	Boolean surface;
	unsigned int i;
#endif

#if defined(HAVE_MOTIF) || defined(HAVE_ATHENA)
	progDsp = strrchr(argv[0], '/');
	if (progDsp != NULL)
		progDsp++;
	if (progDsp == NULL)
		progDsp = argv[0];
#endif
	topLevel = XtVaAppInitialize(NULL, "Threed",
		options, XtNumber(options), &argc, argv,
#ifdef HAVE_ATHENA
		fallbackResources,
#else
		NULL,
#endif
		NULL);
	appCon = XtWidgetToApplicationContext(topLevel);
	if (argc != 1)
		usage(argv[0], synopsisHelp);
#ifdef HAVE_MOTIF
	panel = XtVaCreateManagedWidget("panel",
		xmPanedWindowWidgetClass, topLevel,
		XmNseparatorOn, False,
		XmNsashWidth, 1,
		XmNsashHeight, 1, NULL);
	fileString = XmStringCreateSimple((char *) "File");
	controlsString = XmStringCreateSimple((char *) "Controls");
	menuBar = XmVaCreateSimpleMenuBar(panel, (char *) "menuBar",
		XmVaCASCADEBUTTON, fileString, 'F',
		XmVaCASCADEBUTTON, controlsString, 'C', NULL);
	XmStringFree(fileString);
	XmStringFree(controlsString);
	quitString = XmStringCreateSimple((char *) "Exit");
	(void) XmVaCreateSimplePulldownMenu(menuBar, (char *) "fileMenu",
		0, fileMenuCallback,
		XmVaPUSHBUTTON, quitString, 'x', NULL, NULL, NULL);
	XmStringFree(quitString);
#ifdef SPEED
	speedString = XmStringCreateSimple((char *) "Speed >");
	slowString = XmStringCreateSimple((char *) "Slow <");
#endif
	soundString = XmStringCreateSimple((char *) "Sound @");
	controlsMenu = XmVaCreateSimplePulldownMenu(menuBar, (char *) "controlsMenu",
		1, controlsMenuCallback,
#ifdef SPEED
		XmVaPUSHBUTTON, speedString, '>', NULL, NULL,
		XmVaPUSHBUTTON, slowString, '<', NULL, NULL,
#endif
		XmVaTOGGLEBUTTON, soundString, '@', NULL, NULL, NULL);
#ifdef SPEED
	XmStringFree(speedString);
	XmStringFree(slowString);
#endif
	XmStringFree(soundString);
	pullDownMenu = XmCreatePulldownMenu(menuBar,
		(char *) "helpPullDown", NULL, 0);
	widget = XtVaCreateManagedWidget("Help",
		xmCascadeButtonWidgetClass, menuBar,
		XmNsubMenuId, pullDownMenu,
		XmNmnemonic, 'H', NULL); /* mnemonic warning on Cygwin */
	XtVaSetValues(menuBar,
		XmNmenuHelpWidget, widget, NULL);
	widget = XtVaCreateManagedWidget("Description...",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'D', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuCallback, (char *) 0);
	widget = XtVaCreateManagedWidget("Features...",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'F', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuCallback, (char *) 1);
	widget = XtVaCreateManagedWidget("Synopsis...",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'S', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuCallback, (char *) 2);
	widget = XtVaCreateManagedWidget("References...",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'R', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuCallback, (char *) 3);
	widget = XtVaCreateManagedWidget("About...",
		xmPushButtonGadgetClass, pullDownMenu,
		XmNmnemonic, 'A', NULL);
	XtAddCallback(widget, XmNactivateCallback, helpMenuCallback, (char *) 4);
	XtManageChild(menuBar);
	mainPanel = XtVaCreateManagedWidget("mainPanel",
		xmPanedWindowWidgetClass, panel, NULL);
	controlPanel = XtVaCreateManagedWidget("controlPanel",
		xmPanedWindowWidgetClass, mainPanel,
		XmNseparatorOn, False,
		XmNsashWidth, 1,
		XmNsashHeight, 1, NULL);
	distRowCol = XtVaCreateManagedWidget("distRowCol",
		xmRowColumnWidgetClass, controlPanel,
		XmNnumColumns, 1,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	distXChangerRowCol = XtVaCreateManagedWidget("distXChangerRowCol",
		xmRowColumnWidgetClass, distRowCol,
		XmNnumColumns, 2,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	XtManageChild(XmCreateLabel(distXChangerRowCol, (char *) distXLabel, NULL, 0));
	distXSlider = XtVaCreateManagedWidget("distXSlider",
#ifdef USE_SPIN
		xmSimpleSpinBoxWidgetClass, distXChangerRowCol,
		XmNspinBoxChildType, XmNUMERIC,
		XmNminimumValue, MIN_DISTANCE,
		XmNmaximumValue, MAX_DISTANCE,
		XmNposition, (MIN_DISTANCE + MAX_DISTANCE) / 2,
		XmNwrap, FALSE,
		XmNeditable, FALSE,
		XmNincrementValue, 1,
		XmNcolumns, 2,
#else
		xmScaleWidgetClass, distXChangerRowCol,
		XmNminimum, MIN_DISTANCE,
		XmNmaximum, MAX_DISTANCE,
		XmNvalue, (MIN_DISTANCE + MAX_DISTANCE) / 2,
#endif
		/*XtVaTypedArg, XmNtitleString, XmRString, "X", 2,*/
		XmNshowValue, True,
		XmNorientation, XmHORIZONTAL, NULL);
	XtAddCallback(distXSlider, XmNvalueChangedCallback,
		(XtCallbackProc) distXChangeCallback, (XtPointer) NULL);
	distYChangerRowCol = XtVaCreateManagedWidget("distYChangerRowCol",
		xmRowColumnWidgetClass, distRowCol,
		XmNnumColumns, 2,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	XtManageChild(XmCreateLabel(distYChangerRowCol, (char *) distYLabel, NULL, 0));
	distYSlider = XtVaCreateManagedWidget("distYSlider",
#ifdef USE_SPIN
		xmSimpleSpinBoxWidgetClass, distYChangerRowCol,
		XmNspinBoxChildType, XmNUMERIC,
		XmNminimumValue, MIN_DISTANCE,
		XmNmaximumValue, MAX_DISTANCE,
		XmNposition, (MIN_DISTANCE + MAX_DISTANCE) / 2,
		XmNwrap, FALSE,
		XmNeditable, FALSE,
		XmNincrementValue, 1,
		XmNcolumns, 2,
#else
		xmScaleWidgetClass, distYChangerRowCol,
		XmNminimum, MIN_DISTANCE,
		XmNmaximum, MAX_DISTANCE,
		XmNvalue, (MIN_DISTANCE + MAX_DISTANCE) / 2,
#endif
		/*XtVaTypedArg, XmNtitleString, XmRString, "Y", 2,*/
		XmNshowValue, True,
		XmNorientation, XmHORIZONTAL, NULL);
	XtAddCallback(distYSlider, XmNvalueChangedCallback,
		(XtCallbackProc) distYChangeCallback, (XtPointer) NULL);
	distZChangerRowCol = XtVaCreateManagedWidget("distZChangerRowCol",
		xmRowColumnWidgetClass, distRowCol,
		XmNnumColumns, 2,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	XtManageChild(XmCreateLabel(distZChangerRowCol, (char *) distZLabel, NULL, 0));
	distZSlider = XtVaCreateManagedWidget("distZSlider",
#ifdef USE_SPIN
		xmSimpleSpinBoxWidgetClass, distZChangerRowCol,
		XmNspinBoxChildType, XmNUMERIC,
		XmNminimumValue, MIN_DISTANCE,
		XmNmaximumValue, MAX_DISTANCE,
		XmNposition, (MIN_DISTANCE + MAX_DISTANCE) / 2,
		XmNwrap, FALSE,
		XmNeditable, FALSE,
		XmNincrementValue, 1,
		XmNcolumns, 2,
#else
		xmScaleWidgetClass, distZChangerRowCol,
		XmNminimum, MIN_DEPTH,
		XmNmaximum, MAX_DEPTH,
		XmNvalue, (MIN_DEPTH + MAX_DEPTH) / 2,
#endif
		/*XtVaTypedArg, XmNtitleString, XmRString, "Z", 2,*/
		XmNshowValue, True,
		XmNorientation, XmHORIZONTAL, NULL);
	XtAddCallback(distZSlider, XmNvalueChangedCallback,
		(XtCallbackProc) distZChangeCallback, (XtPointer) NULL);
	angleRowCol = XtVaCreateManagedWidget("angleRowCol",
		xmRowColumnWidgetClass, controlPanel,
		XmNnumColumns, 1,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	thetaChangerRowCol = XtVaCreateManagedWidget("thetaChangerRowCol",
		xmRowColumnWidgetClass, angleRowCol,
		XmNnumColumns, 2,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	XtManageChild(XmCreateLabel(thetaChangerRowCol, (char *) thetaLabel, NULL, 0));
	thetaSlider = XtVaCreateManagedWidget("thetaSlider",
#ifdef USE_SPIN
		xmSimpleSpinBoxWidgetClass, thetaChangerRowCol,
		XmNspinBoxChildType, XmNUMERIC,
		XmNminimumValue, MIN_DISTANCE,
		XmNmaximumValue, MAX_DISTANCE,
		XmNposition, (MIN_DISTANCE + MAX_DISTANCE) / 2,
		XmNwrap, FALSE,
		XmNeditable, FALSE,
		XmNincrementValue, 1,
		XmNcolumns, 2,
#else
		xmScaleWidgetClass, thetaChangerRowCol,
		XmNminimum, MIN_DEGREES,
		XmNmaximum, MAX_DEGREES,
		XmNvalue, MIN_DEGREES,
#endif
		/*XtVaTypedArg, XmNtitleString, XmRString, "Theta", 6,*/
		XmNshowValue, True,
		XmNorientation, XmHORIZONTAL, NULL);
	XtAddCallback(thetaSlider, XmNvalueChangedCallback,
		(XtCallbackProc) thetaChangeCallback, (XtPointer) NULL);
	phiChangerRowCol = XtVaCreateManagedWidget("phiChangerRowCol",
		xmRowColumnWidgetClass, angleRowCol,
		XmNnumColumns, 2,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	XtManageChild(XmCreateLabel(phiChangerRowCol, (char *) phiLabel, NULL, 0));
	phiSlider = XtVaCreateManagedWidget("phiSlider",
#ifdef USE_SPIN
		xmSimpleSpinBoxWidgetClass, phiChangerRowCol,
		XmNspinBoxChildType, XmNUMERIC,
		XmNminimumValue, MIN_DISTANCE,
		XmNmaximumValue, MAX_DISTANCE,
		XmNposition, (MIN_DISTANCE + MAX_DISTANCE) / 2,
		XmNwrap, FALSE,
		XmNeditable, FALSE,
		XmNincrementValue, 1,
		XmNcolumns, 2,
#else
		xmScaleWidgetClass, phiChangerRowCol,
		XmNminimum, MIN_DEGREES,
		XmNmaximum, MAX_DEGREES,
		XmNvalue, MIN_DEGREES,
#endif
		/*XtVaTypedArg, XmNtitleString, XmRString, "Phi", 4,*/
		XmNshowValue, True,
		XmNorientation, XmHORIZONTAL, NULL);
	XtAddCallback(phiSlider, XmNvalueChangedCallback,
		(XtCallbackProc) phiChangeCallback, (XtPointer) NULL);
	psiChangerRowCol = XtVaCreateManagedWidget("psiChangerRowCol",
		xmRowColumnWidgetClass, angleRowCol,
		XmNnumColumns, 2,
		XmNorientation, XmHORIZONTAL,
		XmNpacking, XmPACK_TIGHT, NULL);
	XtManageChild(XmCreateLabel(psiChangerRowCol, (char *) psiLabel, NULL, 0));
	psiSlider = XtVaCreateManagedWidget("psiSlider",
#ifdef USE_SPIN
		xmSimpleSpinBoxWidgetClass, psiChangerRowCol,
		XmNspinBoxChildType, XmNUMERIC,
		XmNminimumValue, MIN_DISTANCE,
		XmNmaximumValue, MAX_DISTANCE,
		XmNposition, (MIN_DISTANCE + MAX_DISTANCE) / 2,
		XmNwrap, FALSE,
		XmNeditable, FALSE,
		XmNincrementValue, 1,
		XmNcolumns, 2,
#else
		xmScaleWidgetClass, psiChangerRowCol,
		XmNminimum, MIN_DEGREES,
		XmNmaximum, MAX_DEGREES,
		XmNvalue, MIN_DEGREES,
#endif
		/*XtVaTypedArg, XmNtitleString, XmRString, "Psi", 4,*/
		XmNshowValue, True,
		XmNorientation, XmHORIZONTAL, NULL);
	XtAddCallback(psiSlider, XmNvalueChangedCallback,
		(XtCallbackProc) psiChangeCallback, (XtPointer) NULL);
	threed = XtVaCreateManagedWidget("threed",
		threedWidgetClass, panel, NULL);
#elif defined(HAVE_ATHENA)
	XtAppAddActions(appCon, actions, XtNumber(actions));
	createHelp(topLevel, &descriptionDialog, (char *) "Description",
		(char *) descriptionHelp, (XtCallbackProc) closePopupPanel2);
	createScrollHelp(topLevel, &featuresDialog, (char *) "Features",
		(char *) featuresHelp, (XtCallbackProc) closePopupPanel2);
	createHelp(topLevel, &synopsisDialog, (char *) "Synopsis",
		(char *) synopsisHelp, (XtCallbackProc) closePopupPanel2);
	createHelp(topLevel, &referencesDialog, (char *) "References",
		(char *) referencesHelp, (XtCallbackProc) closePopupPanel2);
	createHelp(topLevel, &aboutDialog, (char *) "About",
		(char *) aboutHelp, (XtCallbackProc) closePopupPanel2);
	mainPanel = XtVaCreateManagedWidget("form",
		panedWidgetClass, topLevel, NULL);
	menuBar = XtVaCreateManagedWidget("MenuBar",
		formWidgetClass, mainPanel,
		XtNborderWidth, 1, NULL);
	createMenu(menuBar, &fileLabel, &fileMenu, NULL,
		fileTypes, "File", numFileTypes,
		0, False, fileMenuCallback);
	controlsLabel = XtVaCreateManagedWidget("Controls",
		menuButtonWidgetClass, menuBar,
		XtNborderWidth, 0,
		XtNfromHoriz, fileLabel, NULL);
	controlsMenu = XtVaCreatePopupShell("menu",
		simpleMenuWidgetClass, controlsLabel,
		XtNrightMargin, 8, NULL); /* for check */
	for (i = 0; i < numControlTypes; i++) {
		w = XtVaCreateManagedWidget(controlTypes[i],
			smeBSBObjectClass, controlsMenu, NULL);
		if (i == numControlTypes - 1)
			soundMenuItem = w;
		XtAddCallback(w,
			XtNcallback, (XtCallbackProc) controlsMenuCallback,
			(XtPointer) (size_t) i);
	}
	createMenu(menuBar, &helpLabel, &helpMenu, controlsLabel,
		helpTypes, "Help", numHelpTypes,
		0, False, helpMenuCallback);
	controlPanel = XtVaCreateManagedWidget("controlPanel",
		formWidgetClass, mainPanel,
		XtNborderWidth, 0,
		XtNfromVert, menuBar, NULL);
	distRowCol = XtVaCreateManagedWidget("distRowCol",
		formWidgetClass, controlPanel,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0, NULL);
	distXBox = XtVaCreateManagedWidget("distXBox",
		boxWidgetClass, distRowCol,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0, NULL);
	distYBox = XtVaCreateManagedWidget("distYBox",
		boxWidgetClass, distRowCol,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0,
		XtNfromHoriz, distXBox, NULL);
	distZBox = XtVaCreateManagedWidget("distZBox",
		boxWidgetClass, distRowCol,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0,
		XtNfromHoriz, distYBox, NULL);
	angleRowCol = XtVaCreateManagedWidget("angleRowCol",
		boxWidgetClass, controlPanel,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0,
		XtNfromVert, distRowCol, NULL);
	thetaBox = XtVaCreateManagedWidget("thetaBox",
		boxWidgetClass, angleRowCol,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0, NULL);
	phiBox = XtVaCreateManagedWidget("phiBox",
		boxWidgetClass, angleRowCol,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0,
		XtNfromHoriz, thetaBox, NULL);
	psiBox = XtVaCreateManagedWidget("psiBox",
		boxWidgetClass, angleRowCol,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0,
		XtNfromHoriz, phiBox, NULL);
	shapeRowCol = XtVaCreateManagedWidget("shapeRowCol",
		boxWidgetClass, controlPanel,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0,
		XtNfromVert, angleRowCol, NULL);
	surfaceBox = XtVaCreateManagedWidget("Surface",
		boxWidgetClass, shapeRowCol,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0,
		XtNstate, DEFAULT_SURFACE, NULL);
	objectBox = XtVaCreateManagedWidget("objectBox",
		boxWidgetClass, shapeRowCol,
		XtNorientation, XtorientHorizontal,
		XtNborderWidth, 0,
		XtNvertDistance, 0,
		XtNfromHoriz, surfaceBox, NULL);
	threed = XtVaCreateManagedWidget("threed",
		threedWidgetClass, mainPanel,
		XtNfromVert, controlPanel, NULL);
	XtVaGetValues(threed,
		XtNdistanceX, &distX,
		XtNdistanceY, &distY,
		XtNdistanceZ, &distZ,
		XtNthetaDegrees, &theta,
		XtNphiDegrees, &phi,
		XtNpsiDegrees, &psi,
		XtNobject, &object,
		XtNobjectNumber, &number,
		XtNobjectList, &list,
		XtNsurface, &surface, NULL);
	createToggle(surfaceBox, &surfaceToggle, "Surface:", 0,
		surface, (XtCallbackProc) surfaceCallback);
	createStripPopupMenu(objectBox, &objectLabel, &objectMenu,
		list, "Object:", object, number,
		(XtCallbackProc) objectCallback);
#ifdef USE_SPIN
	createSpinner(distXBox, &distXSliderLabel,
		distXLabel, 0, distX, MIN_DISTANCE, MAX_DISTANCE, False,
		distXUpCallback, distXownCallback);
	createSpinner(distYBox, &distYSliderLabel,
		distYLabel, 0, distY, MIN_DISTANCE, MAX_DISTANCE, False,
		distYUpCallback, distYDownCallback);
	createSpinner(distZBox, &distZSliderLabel,
		distZLabel, 0, distZ, MIN_DISTANCE, MAX_DISTANCE, False,
		distZUpCallback, distZDownCallback);
	createSpinner(thetaBox, &thetaSliderLabel,
		thetaLabel, 0, theta, MIN_DEGREES, MAX_DEGREES, False,
		thetaUpCallback, thetaDownCallback);
	createSpinner(phiBox, &phiSliderLabel,
		phiLabel, 0, phi, MIN_DEGREES, MAX_DEGREES, False,
		phiUpCallback, phiDownCallback);
	createSpinner(psiBox, &psiSliderLabel,
		psiLabel, 0, psi, MIN_DEGREES, MAX_DEGREES, False,
		psiUpCallback, psiDownCallback);
#else
	createSlider(distXBox, &distXSliderLabel, &distXSlider,
		distXLabel, 0, distX, MIN_DISTANCE, MAX_DISTANCE, False,
		SCROLL_SIZE, distXChangeCallback, distXChangeCallback);
	createSlider(distYBox, &distYSliderLabel, &distYSlider,
		distYLabel, 0, distY, MIN_DISTANCE, MAX_DISTANCE, False,
		SCROLL_SIZE, distYChangeCallback, distYChangeCallback);
	createSlider(distZBox, &distZSliderLabel, &distZSlider,
		distZLabel, 0, distZ, MIN_DISTANCE, MAX_DISTANCE, False,
		SCROLL_SIZE, distZChangeCallback, distZChangeCallback);
	createSlider(thetaBox, &thetaSliderLabel, &thetaSlider,
		thetaLabel, 0, theta, MIN_DEGREES, MAX_DEGREES, False,
		SCROLL_SIZE,
		thetaChangeCallback, thetaChangeCallback);
	createSlider(phiBox, &phiSliderLabel, &phiSlider,
		phiLabel, 0, phi, MIN_DEGREES, MAX_DEGREES, False,
		SCROLL_SIZE,
		phiChangeCallback, phiChangeCallback);
	createSlider(psiBox, &psiSliderLabel, &psiSlider,
		psiLabel, 0, psi, MIN_DEGREES, MAX_DEGREES, False,
		SCROLL_SIZE,
		psiChangeCallback, psiChangeCallback);
#endif
	XtRealizeWidget(topLevel);
	if (wmDeleteWindow == None)
		wmDeleteWindow = XInternAtom(XtDisplay(topLevel),
			"WM_DELETE_WINDOW", FALSE);
	XSetWMProtocols(XtDisplay(topLevel), XtWindow(topLevel),
		&wmDeleteWindow, 1);
#else

	threed = XtVaCreateManagedWidget("threed",
		threedWidgetClass, topLevel, NULL);
#endif
	XtVaGetValues(threed,
#ifdef HAVE_MOTIF
		XtNobject, &object,
		XtNobjectNumber, &number,
		XtNobjectList, &list,
#endif
		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);
	XtVaSetValues(topLevel,
#ifdef HAVE_MOTIF
		XmNkeyboardFocusPolicy, XmPOINTER, /* not XmEXPLICIT */
#else
		XtNinput, True,
#endif
		XtNiconPixmap, pixmap, NULL);
	XtAddCallback(threed, XtNselectCallback,
		(XtCallbackProc) threeDCallback, (XtPointer) NULL);
#ifdef HAVE_MOTIF
	updateToggle(controlsMenu, &soundMenuItem,
		False,
#ifdef EXTRA
		2,
#else
		0,
#endif
		(XtCallbackProc) soundCallback);
	descriptionDialog = createHelp(menuBar,
		(char *) "Description", (char *) descriptionHelp);
	featuresDialog = createHelp(menuBar,
		(char *) "Features", (char *) featuresHelp);
	synopsisDialog = createHelp(menuBar,
		(char *) "Synopsis", (char *) synopsisHelp);
	referencesDialog = createHelp(menuBar,
		(char *) "References", (char *) referencesHelp);
	aboutDialog = createHelp(menuBar,
		(char *) "About", (char *) aboutHelp);
	shapeRowCol = XtVaCreateManagedWidget("shapeRowCol",
		xmRowColumnWidgetClass, controlPanel,
		XmNorientation, XmHORIZONTAL, NULL);
	/*createMenu(&objectMenu, shapeRowCol, object);*/
	createObjectList(&shapeRowCol, list, object, number);
	surfaceToggle = XtVaCreateManagedWidget("Surface",
		xmToggleButtonWidgetClass, shapeRowCol,
		XmNset, DEFAULT_SURFACE,
		NULL);
	XtAddCallback(surfaceToggle, XmNvalueChangedCallback,
		(XtCallbackProc) surfaceCallback, (XtPointer) NULL);
#endif
	initialize(threed);
	XtRealizeWidget(topLevel);
	XGrabButton(XtDisplay(threed), (unsigned int) AnyButton, AnyModifier,
		XtWindow(threed), TRUE, (unsigned int) (ButtonPressMask |
		ButtonMotionMask | ButtonReleaseMask),
		GrabModeAsync, GrabModeAsync, XtWindow(threed),
		XCreateFontCursor(XtDisplay(threed), XC_hand2));
	XtAppMainLoop(appCon);

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