/* Copyright (c) Mark J. Kilgard, 1996. */
/* This program is freely distributable without licensing fees
and is provided without guarantee or warrantee expressed or
implied. This program is -not- in the public domain. */
/* Molecule Viewer user interface */
#include <stdlib.h>
#include <stdio.h>
#include <Xm/MainW.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/CascadeB.h>
#include <Xm/Frame.h>
#include <Xm/FileSB.h>
#include <Xm/Text.h>
#include <Xm/MessageB.h>
#include <Xm/LabelG.h>
#include <X11/GLw/GLwMDrawA.h> /* Motif OpenGL drawing area
widget */
#include <GL/glx.h>
#include "molview.h"
#include "sovLayerUtil.h"
static int config[] = {
None, None, /* Space for multisampling GLX
attributes if supported. */
GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 12,
GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
None
};
static int *dblBuf = &config[2];
static int *snglBuf = &config[3];
static String fallbackResources[] = {
"*sgiMode: true", /* Try to enable Indigo Magic look & feel */
"*useSchemes: all", /* and SGI schemes. */
"*title: Molecule Viewer",
"*filebox_popup*title: Open molecule...",
"*glxarea*width: 400",
"*glxarea*height: 300",
NULL
};
XtActionsRec actionsTable[] = {
{"startRotation", startRotation},
{"rotation", rotation},
{"doPick", doPick}
};
XtAppContext app;
Display *dpy;
Bool doubleBuffer = True, madeCurrent = False;
Widget toplevel, mainw, menubar, menupane, btn, cascade, frame, glxarea, dialog, popup, cmdw;
WidgetList menuWidgets;
Window glxwin;
GLXContext cx;
XVisualInfo *vi = NULL;
Arg args[10];
XFontStruct *fontInfo;
Dimension viewWidth, viewHeight;
char *textLabels[] = {"Atom Name:", "XYZ Position:", "Radius:"};
Widget labels[XtNumber(textLabels)];
Visual *overlayVisual = NULL;
int overlayDepth;
Colormap overlayColormap;
static void
detectOverlaySupport(Display *dpy)
{
sovVisualInfo template, *overlayVisuals;
int layer, nVisuals, i, entries;
/* Need more than two colormap entries for reasonable menus. */
entries = 2;
for (layer = 1; layer <= 3; layer++) {
template.layer = layer;
template.vinfo.screen = DefaultScreen(dpy);
overlayVisuals = sovGetVisualInfo(dpy,
VisualScreenMask | VisualLayerMask, &template, &nVisuals);
if (overlayVisuals) {
for (i = 0; i < nVisuals; i++) {
if (overlayVisuals[i].vinfo.visual->map_entries > entries) {
overlayVisual = overlayVisuals[i].vinfo.visual;
overlayDepth = overlayVisuals[i].vinfo.depth;
entries = overlayVisual->map_entries;
}
}
XFree(overlayVisuals);
}
}
if (overlayVisual)
overlayColormap = XCreateColormap(dpy, DefaultRootWindow(dpy),
overlayVisual, AllocNone);
}
static int
isSupportedByGLX(Display * dpy, char *extension)
{
#if defined(GLX_VERSION_1_1)
static const char *extensions = NULL;
const char *start;
char *where, *terminator;
int major, minor;
glXQueryVersion(dpy, &major, &minor);
/* Be careful not to call glXQueryExtensionsString if it
looks like the server doesn't support GLX 1.1.
Unfortunately, the original GLX 1.0 didn't have the notion
of GLX extensions. */
if ((major == 1 && minor >= 1) || (major > 1)) {
if (!extensions)
extensions = glXQueryExtensionsString(dpy, DefaultScreen(dpy));
/* It takes a bit of care to be fool-proof about parsing
the GLX extensions string. Don't be fooled by
sub-strings, etc. */
start = extensions;
for (;;) {
where = strstr(start, extension);
if (!where)
return 0;
terminator = where + strlen(extension);
if (where == start || *(where - 1) == ' ') {
if (*terminator == ' ' || *terminator == '\0') {
return 1;
}
}
start = terminator;
}
}
#else
/* No GLX extensions before GLX 1.1 */
#endif
return 0;
}
int
main(int argc, char *argv[])
{
static XmButtonType buttonTypes[] =
{
XmPUSHBUTTON, XmPUSHBUTTON, XmSEPARATOR, XmCHECKBUTTON, XmCHECKBUTTON
};
XmString buttonLabels[XtNumber(buttonTypes)];
int n, i;
toplevel = XtAppInitialize(&app, "Molview", NULL, 0, &argc, argv,
fallbackResources, NULL, 0);
XtAppAddActions(app, actionsTable, XtNumber(actionsTable));
dpy = XtDisplay(toplevel);
#if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample)
if (isSupportedByGLX(dpy, "GLX_SGIS_multisample")) {
config[0] = GLX_SAMPLES_SGIS;
config[1] = 4;
vi = glXChooseVisual(dpy, DefaultScreen(dpy), config);
}
#endif
if (vi == NULL) {
/* Find an OpenGL-capable RGB visual with depth buffer. */
vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
if (vi == NULL) {
vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
if (vi == NULL)
XtAppError(app, "no RGB visual with depth buffer");
doubleBuffer = False;
}
}
/* Create an OpenGL rendering context. */
cx = glXCreateContext(dpy, vi, /* No display list sharing */ None,
/* Favor direct rendering */ True);
if (cx == NULL)
XtAppError(app, "could not create rendering context");
mainw = XtVaCreateWidget("mainw", xmMainWindowWidgetClass, toplevel,
XmNcommandWindowLocation, XmCOMMAND_BELOW_WORKSPACE,
NULL);
XtManageChild(mainw);
XtAddEventHandler(toplevel, StructureNotifyMask, False,
mapStateChanged, NULL);
/* Try to find a good overlay visual for pulldown and popup menus. */
detectOverlaySupport(dpy);
/* Create menu bar. */
menubar = XmCreateMenuBar(mainw, "menubar", NULL, 0);
XtManageChild(menubar);
n = 0;
if (overlayVisual) {
XtSetArg(args[n], XmNvisual, overlayVisual);
n++;
XtSetArg(args[n], XmNdepth, overlayDepth);
n++;
XtSetArg(args[n], XmNcolormap, overlayColormap);
n++;
}
menupane = XmCreatePulldownMenu(menubar, "menupane", args, n);
if (overlayVisual) {
XtAddCallback(XtParent(menupane), XmNpopupCallback,
ensurePulldownColormapInstalled, NULL);
}
btn = XmCreatePushButton(menupane, "Open molecule...", NULL, 0);
XtAddCallback(btn, XmNactivateCallback, openMolecule, NULL);
XtManageChild(btn);
btn = XmCreatePushButton(menupane, "Quit", NULL, 0);
XtAddCallback(btn, XmNactivateCallback, quit, NULL);
XtManageChild(btn);
XtSetArg(args[0], XmNsubMenuId, menupane);
cascade = XmCreateCascadeButton(menubar, "File", args, 1);
XtManageChild(cascade);
/* Create framed drawing area for OpenGL rendering. */
frame = XmCreateFrame(mainw, "frame", NULL, 0);
XtManageChild(frame);
glxarea = XtVaCreateManagedWidget("glxarea", glwMDrawingAreaWidgetClass,
frame, GLwNvisualInfo, vi, NULL);
XtVaGetValues(glxarea, XtNwidth, &viewWidth, XtNheight, &viewHeight, NULL);
XtAddCallback(glxarea, XmNexposeCallback, draw, NULL);
XtAddCallback(glxarea, XmNresizeCallback, resize, NULL);
XtAddCallback(glxarea, GLwNginitCallback, init, NULL);
/* Create atom pick result text field. */
cmdw = XtVaCreateWidget("cmdw", xmRowColumnWidgetClass, mainw,
XmNpacking, XmPACK_COLUMN,
XmNnumColumns, XtNumber(textLabels),
XmNisAligned, True,
XmNentryAlignment, XmALIGNMENT_END,
XmNorientation, XmHORIZONTAL,
NULL);
for (i = 0; i < XtNumber(textLabels); i++) {
XtVaCreateManagedWidget(textLabels[i],
xmLabelGadgetClass, cmdw, NULL);
labels[i] = XtVaCreateManagedWidget("atom_info", xmTextWidgetClass, cmdw,
XmNeditable, False,
XmNcursorPositionVisible, False,
XmNtraversalOn, False,
XmNcolumns, 25,
NULL);
}
clearPickInfo();
XtManageChild(cmdw);
/* Create popup menu. */
buttonLabels[0] = XmStringCreateLocalized("Solid");
buttonLabels[1] = XmStringCreateLocalized("Wireframe");
buttonLabels[2] = NULL;
buttonLabels[3] = XmStringCreateLocalized("Spin Momentum");
buttonLabels[4] = XmStringCreateLocalized("Auto HiRes");
n = 0;
XtSetArg(args[n], XmNbuttonCount, XtNumber(buttonTypes)); n++;
XtSetArg(args[n], XmNbuttons, buttonLabels); n++;
XtSetArg(args[n], XmNbuttonType, buttonTypes); n++;
XtSetArg(args[n], XmNbuttonSet, 4); n++;
XtSetArg(args[n], XmNsimpleCallback, processMenuUse); n++;
if (overlayVisual) {
XtSetArg(args[n], XmNvisual, overlayVisual); n++;
XtSetArg(args[n], XmNdepth, overlayDepth); n++;
XtSetArg(args[n], XmNcolormap, overlayColormap); n++;
}
popup = XmCreateSimplePopupMenu(frame, "popup", args, n);
XtAddEventHandler(frame, ButtonPressMask, False, activateMenu, &popup);
XmStringFree(buttonLabels[0]);
XmStringFree(buttonLabels[1]);
XmStringFree(buttonLabels[3]);
XmStringFree(buttonLabels[4]);
XtVaGetValues(popup, XmNchildren, &menuWidgets, NULL);
XmToggleButtonSetState(menuWidgets[3], True, False);
XmToggleButtonSetState(menuWidgets[4], True, False);
/* Set up application's window layout. */
XmMainWindowSetAreas(mainw, menubar, cmdw, NULL, NULL, frame);
XtRealizeWidget(toplevel);
/* Once widget is realized (ie, associated with a created X
window), we can bind the OpenGL rendering context to the
window. */
glXMakeCurrent(dpy, XtWindow(glxarea), cx);
madeCurrent = True;
/* Get font for reporting no atom loaded. */
fontInfo = XLoadQueryFont(dpy, "-adobe-helvetica-medium-r-normal--18-*-*-*-p-*-iso8859-1");
if (fontInfo == NULL) {
fontInfo = XLoadQueryFont(dpy, "fixed");
if (fontInfo == NULL)
XtAppError(app, "no X font available?");
}
glXUseXFont(fontInfo->fid, 32, 96, 1024 + 32);
glListBase(1024);
XtAppMainLoop(app);
return 0;
}
/* for Ch only */
/* #ifdef _CH_ */
#pragma import "gui_run.c"
#pragma import "sovlayerutil.c"
#pragma import "pick.c"
#pragma import "mol_file.c"
#pragma import "render.c"
#pragma import "trackball.c"
/* #endif */