// // Vrml 97 library // Copyright (C) 1998 Chris Morley // // ViewerXt.cpp // Display of VRML models using OpenGL/XToolkit. // #include "config.h" #if HAVE_XT #include "ViewerXt.h" #include "GLwDrawA.h" #include // XtNcolormap #include // XA_RGB_DEFAULT #include // XK_* #include // XC_* #include // gluOrtho2D #include "System.h" ViewerXt::ViewerXt(VrmlScene *scene, const char *name, Widget parent, WidgetClass c ) : ViewerOpenGL(scene), d_context(0), d_widget(0), d_cmap(0), d_timer(0) { if (c == 0) c = glwDrawingAreaWidgetClass; createWidget( name, parent, c ); } ViewerXt::~ViewerXt() { if (d_widget) { if (d_context) glXDestroyContext(XtDisplay(d_widget), d_context); //if (d_cmap) XFreeColormap(XtDisplay(d_widget), d_cmap); //XtDestroyWidget(d_widget); d_widget = 0; } } // Graphics initialization callback. static void ginit(Widget w, XtPointer clientData, XtPointer) { XVisualInfo *visinfo; Dimension width, height; // Create rendering context (no dlist sharing, direct render true). XtVaGetValues( w, GLwNvisualInfo, &visinfo, XtNwidth, &width, XtNheight, &height, 0); ViewerXt *v = (ViewerXt*)clientData; if (v) { GLXContext c = glXCreateContext(XtDisplay(w), visinfo, 0, True); if (! c) theSystem->error("ViewerXt: couldn't create GLXContext."); else { v->setContext( c ); v->resize( width, height ); } } } // Do redraw callback. static void expose(Widget, XtPointer clientData, XtPointer) { ViewerXt *viewer = (ViewerXt*)clientData; if (viewer) { viewer->flushEvents(); viewer->redraw(); } } // Input callback static void xinput(Widget, XtPointer clientData, XtPointer callData) { ViewerXt *viewer = (ViewerXt*)clientData; if (viewer) { GLwDrawingAreaCallbackStruct *x; x = (GLwDrawingAreaCallbackStruct *)callData; ViewerOpenGL::EventInfo e; char cbuf[32]; KeySym keysym; switch (x->event->type) { case KeyPress: { int nc = XLookupString((XKeyEvent*)(x->event), cbuf, sizeof(cbuf)-1, &keysym, 0); e.event = ViewerOpenGL::EVENT_KEY_DOWN; switch (keysym) { case XK_Home: e.what = ViewerOpenGL::KEY_HOME; break; case XK_Left: e.what = ViewerOpenGL::KEY_LEFT; break; case XK_Up: e.what = ViewerOpenGL::KEY_UP; break; case XK_Right: e.what = ViewerOpenGL::KEY_RIGHT; break; case XK_Down: e.what = ViewerOpenGL::KEY_DOWN; break; case XK_Page_Up: e.what = ViewerOpenGL::KEY_PAGE_UP; break; case XK_Page_Down: e.what = ViewerOpenGL::KEY_PAGE_DOWN; break; default: if (nc <= 0) return; // Unhandled non-printable key e.what = cbuf[0]; break; } e.x = ((XKeyEvent*)(x->event))->x; e.y = ((XKeyEvent*)(x->event))->y; break; } case ButtonPress: e.event = ViewerOpenGL::EVENT_MOUSE_CLICK; switch (((XButtonEvent*)(x->event))->button) { default: case Button1: e.what = 0; break; case Button2: e.what = 1; break; case Button3: e.what = 2; break; } e.x = ((XButtonEvent*)(x->event))->x; e.y = ((XButtonEvent*)(x->event))->y; break; case ButtonRelease: e.event = ViewerOpenGL::EVENT_MOUSE_RELEASE; switch (((XButtonEvent*)(x->event))->button) { default: case Button1: e.what = 0; break; case Button2: e.what = 1; break; case Button3: e.what = 2; break; } e.x = ((XButtonEvent*)(x->event))->x; e.y = ((XButtonEvent*)(x->event))->y; break; case MotionNotify: e.event = ViewerOpenGL::EVENT_MOUSE_DRAG; e.what = 0; if (((XMotionEvent*)(x->event))->state & Button1Mask) e.what = 0; else if (((XMotionEvent*)(x->event))->state & Button2Mask) e.what = 1; else if (((XMotionEvent*)(x->event))->state & Button3Mask) e.what = 2; else e.event = ViewerOpenGL::EVENT_MOUSE_MOVE; e.x = ((XMotionEvent*)(x->event))->x; e.y = ((XMotionEvent*)(x->event))->y; break; default: return; } viewer->input( &e ); } } // Input event handler static void xinputEvent(Widget w, XtPointer clientData, XEvent *event, Boolean *propagate) { GLwDrawingAreaCallbackStruct x; x.event = event; xinput(w, clientData, (XtPointer) &x); *propagate = False; } // Window resize callback static void xresize(Widget, XtPointer clientData, XtPointer callData) { ViewerXt *viewer = (ViewerXt*)clientData; if (viewer) { GLwDrawingAreaCallbackStruct *x; x = (GLwDrawingAreaCallbackStruct *)callData; viewer->setContext(); glXWaitX(); // Wait for the resize to happen viewer->resize(x->width, x->height); } } // Not declared on Solaris5.5.1 extern "C" int XmuLookupStandardColormap( Display*, int, VisualID, int, int, int, int); // From MJK, OpenGL Programming for X, p50 static Colormap getcmap(XVisualInfo *vi, Display *dpy) { Status status; XStandardColormap *standardCmaps; Colormap cmap; int i, numCmaps; Atom *rgbmap, atoms[3] = { XA_RGB_DEFAULT_MAP, XA_RGB_COLOR_MAP, 0 }; //if (vi->c_class != TrueColor) // return 0; for (rgbmap=&atoms[0]; *rgbmap; ++rgbmap) { status = XmuLookupStandardColormap(dpy, vi->screen, vi->visualid, vi->depth, *rgbmap, False, True); if (status == 1) { status = XGetRGBColormaps(dpy, RootWindow(dpy, vi->screen), &standardCmaps, &numCmaps, *rgbmap); if (status == 1) { for (i=0; ivisualid) { cmap = standardCmaps[i].colormap; XFree(standardCmaps); return cmap; } XFree(standardCmaps); } } } theSystem->inform("Creating colormap"); cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone); return cmap; } void ViewerXt::createWidget( const char *name, Widget parent, WidgetClass c ) { Display *dpy = XtDisplay(parent); int screen = XScreenNumberOfScreen(XtScreen(parent)); // Request a double-buffered RGBA buffer with Z-buffer: int vType[] = { GLX_ALPHA_SIZE, 1, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 1, GLX_RGBA, GLX_RED_SIZE, 1, //GLX_X_VISUAL_TYPE_EXT, GLX_TRUE_COLOR_EXT, (int)None }; XVisualInfo *visinfo = 0; for (int v=0; v<3; ++v) if ((visinfo = glXChooseVisual(dpy, screen, &vType[v])) != 0) break; // What now? if (! visinfo) { theSystem->error("ViewerXt: glXChooseVisual failed.\n"); return; } d_cmap = getcmap(visinfo, dpy); theSystem->debug("ViewerXt: visual ID 0x%x, %d bits, class %d, cmap 0x%x.\n", visinfo->visualid, visinfo->depth, visinfo->c_class, d_cmap); // If you are having trouble with colormap flashing on an 8 bit // display, try restarting your X server (eg, log out and back in // in an xdm environment) and running "xstdcmap -default" BEFORE // running anything else. In fact, you might put that command in // your X startup script. if (! d_cmap) { theSystem->warn("ViewerXt: couldn't get a TrueColor colormap (class %d)\n", visinfo->c_class); d_widget = XtVaCreateWidget(name, c, parent, GLwNvisualInfo, visinfo, GLwNallocateBackground, False, GLwNallocateOtherColors, False, GLwNinstallBackground, False, 0); } else d_widget = XtVaCreateWidget(name, c, parent, GLwNvisualInfo, visinfo, XtNcolormap, d_cmap, GLwNallocateBackground, False, GLwNallocateOtherColors, False, GLwNinstallBackground, False, 0); if (! d_widget) { theSystem->error("ViewerXt: couldn't create GL widget.\n"); return; } // Register callbacks XtAddCallback(d_widget, GLwNginitCallback, ginit, this); XtAddCallback(d_widget, GLwNexposeCallback, expose, this); XtAddCallback(d_widget, GLwNresizeCallback, xresize, this); XtAddCallback(d_widget, GLwNinputCallback, xinput, this); // GLwNinputCallback doesn't seem to get pointer motion events XtAddEventHandler(d_widget, PointerMotionMask, True, xinputEvent, this); // If you are not getting keyboard events, check focus policy (see xmlookat) XtManageChild( d_widget ); } void ViewerXt::setContext(GLXContext glxcontext) { if (glxcontext) d_context = glxcontext; if (d_context) glXMakeCurrent(XtDisplay(d_widget), XtWindow(d_widget), d_context); } // void ViewerXt::wsPostRedraw() { //if (d_context) redraw(); if (d_widget) { XEvent e; e.xexpose.type = Expose; e.xexpose.serial = 0; e.xexpose.send_event = True; e.xexpose.display = XtDisplay(d_widget); e.xexpose.window = XtWindow(d_widget); e.xexpose.x = e.xexpose.y = 0; e.xexpose.width = e.xexpose.height = 1; e.xexpose.count = 0; XSendEvent(XtDisplay(d_widget), XtWindow(d_widget), False, 0L, &e); } } void ViewerXt::flushEvents() { if (d_widget) { XEvent e; while (XCheckTypedWindowEvent(XtDisplay(d_widget), XtWindow(d_widget), Expose, &e)) ; } } typedef struct _CursorTable { int glyph; Cursor cursor; } CursorTable; void ViewerXt::wsSetCursor( CursorStyle c ) { static CursorTable cursorMap[] = { { (int)None, None }, { XC_hand1, None }, { XC_exchange, None }, { XC_sb_v_double_arrow, None }, { XC_crosshair, None } }; if (c == CURSOR_INHERIT) XDefineCursor( XtDisplay(d_widget), XtWindow(d_widget), None ); else { if (cursorMap[c].cursor == None) cursorMap[c].cursor = XCreateFontCursor(XtDisplay(d_widget), cursorMap[c].glyph); XDefineCursor( XtDisplay(d_widget), XtWindow(d_widget), cursorMap[c].cursor ); } XFlush( XtDisplay(d_widget) ); } void ViewerXt::wsSwapBuffers() { glXSwapBuffers(XtDisplay(d_widget),XtWindow(d_widget)); } // Timer callback calls the viewer update() method. static void timer(XtPointer clientData, XtIntervalId *) { ViewerXt *viewer = (ViewerXt*)clientData; if (viewer) viewer->timerUpdate(); } void ViewerXt::timerUpdate() { d_timer = 0; update( 0.0 ); } // void ViewerXt::wsSetTimer( double t ) { if (! d_timer) { unsigned int millis = (unsigned int) (1000.0 * t); d_timer = (unsigned long) XtAppAddTimeOut( XtWidgetToApplicationContext(d_widget), millis, timer, this ); } } #endif // HAVE_XT