Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

OpenGLDisplayDevice.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2019 The Board of Trustees of the           
00004  *cr                        University of Illinois                       
00005  *cr                         All Rights Reserved                        
00006  *cr                                                                   
00007  ***************************************************************************/
00008 /***************************************************************************
00009  * RCS INFORMATION:
00010  *
00011  *      $RCSfile: OpenGLDisplayDevice.C,v $
00012  *      $Author: johns $        $Locker:  $             $State: Exp $
00013  *      $Revision: 1.217 $      $Date: 2021/08/24 16:52:01 $
00014  *
00015  ***************************************************************************/
00024 #include <stdlib.h>
00025 #include <math.h>
00026 #include <GL/gl.h>
00027 #include <GL/glx.h>
00028 #include <X11/Xlib.h>
00029 #include <X11/cursorfont.h>
00030 #include <X11/keysym.h>
00031 
00032 #if defined(VMDXINERAMA)
00033 #include <X11/extensions/Xinerama.h>
00034 #endif
00035 
00036 #include "OpenGLDisplayDevice.h"
00037 #include "Inform.h"
00038 #include "utilities.h"
00039 #include "config.h"   // VMD version strings etc
00040 
00041 #include "VMDApp.h"
00042 #include "VideoStream.h"
00043 
00044 #if defined(VMDOPTIXRTRT)
00045 #include "OptiXRenderer.h"
00046 #endif
00047 
00048 
00049 // static data for this object
00050 static const char *glStereoNameStr[OPENGL_STEREO_MODES] =
00051  { "Off",
00052    "QuadBuffered",
00053    "HDTV SideBySide",
00054    "Checkerboard",
00055    "ColumnInterleaved",
00056    "RowInterleaved",
00057    "Anaglyph",
00058    "SideBySide",
00059    "AboveBelow",
00060    "Left",
00061    "Right" };
00062 
00063 static const char *glRenderNameStr[OPENGL_RENDER_MODES] = 
00064 { "Normal",
00065   "GLSL",
00066 #if defined(VMDOPTIXRTRT)
00067   "Tachyon RTX RTRT",
00068 #endif
00069   "Acrobat3D" };
00070 
00071 static const char *glCacheNameStr[OPENGL_CACHE_MODES] = 
00072 { "Off",
00073   "On" };
00074 
00075 // determine if all of the ARB multisample extension routines are available
00076 #if defined(GL_ARB_multisample) && defined(GLX_SAMPLES_ARB) && defined(GLX_SAMPLE_BUFFERS_ARB)
00077 #define USEARBMULTISAMPLE 1
00078 #endif
00079 
00080 // colors for cursors
00081 static XColor cursorFG = { 0, 0xffff,      0,      0, 
00082                          DoRed | DoGreen | DoBlue, 0 };
00083 static XColor cursorBG = { 0, 0xffff, 0xffff, 0xffff, 
00084                          DoRed | DoGreen | DoBlue, 0 };
00085 
00087 
00088 #if defined(VMDXINPUT)
00089 #include <X11/extensions/XI.h>
00090 #include <X11/extensions/XInput.h>
00091 
00092 typedef struct {
00093   XDevice *dev;
00094   int motionevent;
00095   int motioneventclass;
00096   int buttonpressevent;
00097   int buttonpresseventclass;
00098   int buttonreleaseevent;
00099   int buttonreleaseeventclass;
00100   XEventClass evclasses[3];
00101 } xidevhandle;
00102 
00103 typedef struct {
00104   Display *dpy;
00105   Window win;
00106   xidevhandle *dev_spaceball;
00107   xidevhandle *dev_dialbox;
00108 } xinputhandle;
00109 
00110 
00111 static xidevhandle * xinput_open_device(xinputhandle *handle, XID devinfo) {
00112   xidevhandle *xdhandle = (xidevhandle *) malloc(sizeof(xidevhandle));
00113   memset(xdhandle, 0, sizeof(xidevhandle));
00114   xdhandle->dev = XOpenDevice(handle->dpy, devinfo);
00115   if (xdhandle->dev == NULL) {
00116     free(xdhandle);
00117     return NULL;
00118   }
00119 
00120   DeviceMotionNotify(xdhandle->dev, xdhandle->motionevent, xdhandle->motioneventclass); 
00121   DeviceButtonPress(xdhandle->dev, xdhandle->buttonpressevent, xdhandle->buttonpresseventclass); 
00122   DeviceButtonRelease(xdhandle->dev, xdhandle->buttonreleaseevent, xdhandle->buttonreleaseeventclass); 
00123 
00124   xdhandle->evclasses[0] = xdhandle->motioneventclass;
00125   xdhandle->evclasses[1] = xdhandle->buttonpresseventclass;
00126   xdhandle->evclasses[2] = xdhandle->buttonreleaseeventclass;
00127 
00128   XSelectExtensionEvent(handle->dpy, handle->win, xdhandle->evclasses, 3);
00129 
00130   return xdhandle;
00131 }
00132 
00133 
00134 static void xinput_close_device(xinputhandle *handle, xidevhandle *xdhandle) {
00135   if (handle == NULL || xdhandle == NULL)
00136     return;
00137 
00138   if (xdhandle->dev != NULL) {
00139     XCloseDevice(handle->dpy, xdhandle->dev);
00140   }
00141   free(xdhandle);
00142 }
00143 
00144 
00145 static int xinput_device_decode_event(xinputhandle *handle, xidevhandle *dev,
00146                                       XEvent *xev, spaceballevent *sballevent) {
00147   if (xev->type == dev->motionevent) {
00148     XDeviceMotionEvent *mptr = (XDeviceMotionEvent *) xev;
00149 
00150     // We assume that the axis mappings are in the order below,as this is
00151     // the axis ordering used by a few other applications as well.
00152     // We add the current control inputs to whatever we had previously,
00153     // so that we can process all queued events and not drop any inputs
00154     sballevent->tx     += mptr->axis_data[0]; // X translation
00155     sballevent->ty     += mptr->axis_data[1]; // Y translation
00156     sballevent->tz     += mptr->axis_data[2]; // Z translation
00157     sballevent->rx     += mptr->axis_data[3]; // A rotation
00158     sballevent->ry     += mptr->axis_data[4]; // B rotation
00159     sballevent->rz     += mptr->axis_data[5]; // C rotation
00160     sballevent->period += 50; // Period in milliseconds
00161     sballevent->event = 1;
00162     return 1;
00163   } else if (xev->type == dev->buttonpressevent) {
00164 //    XDeviceButtonEvent *bptr = (XDeviceButtonEvent *) xev;;
00165 //    sballevent->buttons |= (1 << xev->xclient.data.s[2]);
00166     sballevent->buttons |= 1;
00167     sballevent->event = 1;
00168     return 1;
00169   } else if (xev->type == dev->buttonreleaseevent) {
00170 //    XDeviceButtonEvent *bptr = (XDeviceButtonEvent *) xev;;
00171 //    sballevent->buttons &= ~(1 << xev->xclient.data.s[2]);
00172     sballevent->buttons &= ~1;
00173     sballevent->event = 1;
00174     return 1;
00175   }
00176  
00177   return 0;
00178 }
00179 
00180 
00181 static int xinput_decode_event(xinputhandle *handle, XEvent *xev,
00182                                spaceballevent *sballevent) {
00183   if (handle == NULL)
00184     return 0;
00185 
00186   if (handle->dev_spaceball != NULL) {
00187     return xinput_device_decode_event(handle, handle->dev_spaceball, xev, sballevent);
00188   }
00189 
00190   return 0;
00191 }
00192 
00193 
00194 // enable 6DOF input devices that use XInput 
00195 static xinputhandle * xinput_enable(Display *dpy, Window win) {
00196   xinputhandle *handle = NULL;
00197   int i, numdev, numextdev;
00198   XDeviceInfoPtr list;
00199   int ximajor, xiev, xierr;
00200   Atom sballdevtype;
00201 //  Atom dialboxdevtype; 
00202   xidevhandle *dev_spaceball = NULL;
00203 //  xidevhandle *dev_dialbox = NULL;
00204 
00205   /* check for availability of the XInput extension */
00206   if(!XQueryExtension(dpy,"XInputExtension", &ximajor, &xiev, &xierr)) {
00207     msgInfo << "X-Windows XInput extension unavailable." << sendmsg; 
00208     return NULL;
00209   }
00210 
00211   sballdevtype = XInternAtom(dpy, XI_SPACEBALL, True);
00212 //  dialboxdevtype = XInternAtom(dpy, XI_KNOB_BOX, True);
00213 
00214   /* Get the list of input devices attached to the display */
00215   list = (XDeviceInfoPtr) XListInputDevices(dpy, &numdev);
00216  
00217   numextdev = 0; 
00218   for (i = 0; i < numdev; i++) {
00219     if (list[i].use == IsXExtensionDevice) {
00220       // skip Xorg 'evdev brain' device
00221       if (!strupncmp(list[i].name, "evdev brain", strlen("evdev brain")))
00222         continue;
00223 
00224       numextdev++;
00225     }
00226   }
00227  
00228   if (numextdev > 0) {
00229     handle = (xinputhandle *) malloc(sizeof(xinputhandle));
00230     memset(handle, 0, sizeof(xinputhandle));
00231     handle->dpy = dpy;
00232     handle->win = win;
00233 
00234     msgInfo << "Detected " << numdev << " XInput devices, " 
00235             << numextdev << " usable device" 
00236             << ((numextdev > 1) ? "s:" : ":") << sendmsg;
00237 
00238     for (i = 0; i < numdev; i++) {
00239       if (list[i].use == IsXExtensionDevice) {
00240         // skip Xorg 'evdev brain' device
00241         if (!strupncmp(list[i].name, "evdev brain", strlen("evdev brain")))
00242           continue;
00243 
00244         /* list promising looking devices  */
00245         msgInfo << "  [" << list[i].id << "] " << list[i].name 
00246                 << ", type: " << (int) list[i].type 
00247                 << ", classes: " << (int) list[i].num_classes << sendmsg;
00248 
00249         /* Tag the first Spaceball device we find */
00250         if ((dev_spaceball == NULL) &&
00251             (((sballdevtype != None) && (list[i].type == sballdevtype)) ||
00252             !strupncmp(list[i].name, "SPACEBALL", strlen("SPACEBALL")) ||
00253             !strupncmp(list[i].name, "MAGELLAN", strlen("MAGELLAN")))) {
00254           dev_spaceball = xinput_open_device(handle, list[i].id);
00255         }
00256  
00257 #if 0 
00258         /* Tag the first dial box device we find */
00259         if ((dev_dialbox == NULL) &&
00260             ((dialboxdevtype != None) && (list[i].type == dialboxdevtype))) {
00261           dev_dialbox = xinput_open_device(handle, list[i].id);
00262         }
00263 #endif
00264       }
00265     }
00266     XFreeDeviceList(list);
00267   } else {
00268     // msgInfo << "No XInput devices found." << sendmsg; 
00269     XFreeDeviceList(list);
00270     return NULL;
00271   }
00272 
00273   if (dev_spaceball) {
00274     msgInfo << "Attached to XInput Spaceball" << sendmsg;
00275   }
00276 //  if (dev_dialbox) {
00277 //    msgInfo << "Attached to XInput Dial Box" << sendmsg;
00278 //  }
00279 
00280   if (dev_spaceball != NULL /* || dev_dialbox != NULL */) {
00281     handle->dev_spaceball = dev_spaceball;
00282 //    handle->dev_dialbox   = dev_dialbox;
00283   } else {
00284     free(handle);
00285     return NULL;
00286   }
00287 
00288   return handle;
00289 }
00290 
00291 void xinput_close(xinputhandle *handle) {
00292   if (handle != NULL) {
00293     xinput_close_device(handle, handle->dev_spaceball);
00294 //    xinput_close_device(handle, handle->dev_dialbox);
00295     free(handle);
00296   }
00297 }
00298 
00299 #endif
00300 
00301 
00302 // enable 3Dxware Spaceball / Magellan / SpaceNavigator events
00303 static spaceballhandle * spaceball_enable(Display *dpy, Window win) {
00304   // allocate and clear handle data structure
00305   spaceballhandle *handle = (spaceballhandle *) malloc(sizeof(spaceballhandle));
00306   memset(handle, 0, sizeof(spaceballhandle));  
00307 
00308   // find and store X atoms for the event types we care about
00309   handle->ev_motion         = XInternAtom(dpy, "MotionEvent", True);
00310   handle->ev_button_press   = XInternAtom(dpy, "ButtonPressEvent", True);
00311   handle->ev_button_release = XInternAtom(dpy, "ButtonReleaseEvent", True);
00312   handle->ev_command        = XInternAtom(dpy, "CommandEvent", True);
00313 
00314   if (!handle->ev_motion || !handle->ev_button_press || 
00315       !handle->ev_button_release || !handle->ev_command) {
00316     free(handle);
00317     return NULL; /* driver is not running */
00318   }
00319 
00320   // Find the root window of the driver
00321   Window root = RootWindow(dpy, DefaultScreen(dpy)); 
00322 
00323   // Find the driver's window
00324   Atom ActualType;
00325   int ActualFormat;
00326   unsigned long NItems, BytesReturn;
00327   unsigned char *PropReturn = NULL;
00328   XGetWindowProperty(dpy, root, handle->ev_command, 0, 1, FALSE,
00329                      AnyPropertyType, &ActualType, &ActualFormat, &NItems,
00330                      &BytesReturn, &PropReturn );
00331   if (PropReturn == NULL) {
00332     free(handle);
00333     return NULL;
00334   }
00335   handle->drv_win = *(Window *) PropReturn;
00336   XFree(PropReturn);
00337 
00338   XTextProperty sball_drv_winname;
00339   if (XGetWMName(dpy, handle->drv_win, &sball_drv_winname) != 0) {
00340     if (!strcmp("Magellan Window", (char *) sball_drv_winname.value)) {
00341       /* Send the application window to the Spaceball/Magellan driver */
00342       XEvent msg;
00343       msg.type = ClientMessage;
00344       msg.xclient.format = 16;
00345       msg.xclient.send_event = FALSE;
00346       msg.xclient.display = dpy;
00347       msg.xclient.window = handle->drv_win;
00348       msg.xclient.message_type = handle->ev_command;
00349 
00350       msg.xclient.data.s[0] = (short) (((win)>>16)&0x0000FFFF); // High 16
00351       msg.xclient.data.s[1] = (short) (((win))    &0x0000FFFF); // Low 16
00352       msg.xclient.data.s[2] = SBALL_COMMAND_APP_WINDOW; // 27695
00353 
00354       int rc = XSendEvent(dpy, handle->drv_win, FALSE, 0x0000, &msg);
00355       XFlush(dpy);
00356       if (rc == 0) {
00357         free(handle); 
00358         return NULL;
00359       }
00360     }
00361 
00362     XFree(sball_drv_winname.value);
00363   } 
00364 
00365   return handle;
00366 }
00367 
00368 
00369 static void spaceball_close(spaceballhandle *handle) {
00370   free(handle);
00371 }
00372 
00373 
00374 static int spaceball_decode_event(spaceballhandle *handle, const XEvent *xev, spaceballevent *sballevent) {
00375   unsigned int evtype;
00376 
00377   if (handle == NULL || xev == NULL || sballevent == NULL)
00378     return 0;
00379 
00380   if (xev->type != ClientMessage)
00381     return 0;
00382 
00383   evtype = xev->xclient.message_type;
00384 
00385   if (evtype == handle->ev_motion) {
00386     // We add the current control inputs to whatever we had previously,
00387     // so that we can process all queued events and not drop any inputs
00388     // xev->xclient.data.s[0] is Device Window High 16-bits 
00389     // xev->xclient.data.s[1] is Device Window Low 16-bits 
00390     sballevent->tx     += xev->xclient.data.s[2]; // X translation
00391     sballevent->ty     += xev->xclient.data.s[3]; // Y translation
00392     sballevent->tz     += xev->xclient.data.s[4]; // Z translation
00393     sballevent->rx     += xev->xclient.data.s[5]; // A rotation
00394     sballevent->ry     += xev->xclient.data.s[6]; // B rotation
00395     sballevent->rz     += xev->xclient.data.s[7]; // C rotation
00396     sballevent->period += xev->xclient.data.s[8]; // Period in milliseconds
00397     sballevent->event = 1;
00398     return 1;
00399   } else if (evtype == handle->ev_button_press) {
00400     // xev->xclient.data.s[0] is Device Window High 16-bits 
00401     // xev->xclient.data.s[1] is Device Window Low 16-bits 
00402     sballevent->buttons |= (1 << xev->xclient.data.s[2]);
00403     sballevent->event = 1;
00404     return 1;
00405   } else if (evtype == handle->ev_button_release) {
00406     // xev->xclient.data.s[0] is Device Window High 16-bits 
00407     // xev->xclient.data.s[1] is Device Window Low 16-bits 
00408     sballevent->buttons &= ~(1 << xev->xclient.data.s[2]);
00409     sballevent->event = 1;
00410     return 1;
00411   }
00412 
00413   return 0;
00414 }
00415 
00416 
00417 static void spaceball_init_event(spaceballevent *sballevent) {
00418   memset(sballevent, 0, sizeof(spaceballevent));
00419 }
00420 
00421 
00422 static void spaceball_clear_event(spaceballevent *sballevent) {
00423   sballevent->tx = 0;
00424   sballevent->ty = 0;
00425   sballevent->tz = 0;
00426   sballevent->rx = 0;
00427   sballevent->ry = 0;
00428   sballevent->rz = 0;
00429   sballevent->period = 0;
00430   sballevent->event = 0;
00431 }
00432 
00433 
00434 static XVisualInfo * vmd_get_visual(glxdata *glxsrv, int *stereo, int *msamp, int *numsamples) {
00435   // we want double-buffered RGB with a Z buffer (possibly with stereo)
00436   XVisualInfo *vi;
00437   int ns, dsize;
00438   int simplegraphics = 0;
00439   int disablestereo = 0;
00440   vi = NULL;
00441   *numsamples = 0;
00442   *msamp = FALSE; 
00443   *stereo = FALSE;
00444 
00445   if (getenv("VMDSIMPLEGRAPHICS")) {
00446     simplegraphics = 1;
00447   }
00448 
00449   if (getenv("VMDDISABLESTEREO")) {
00450     disablestereo = 1;
00451   } 
00452 
00453   // check for user-override of maximum antialiasing sample count
00454   int maxaasamples=4;
00455   const char *maxaasamplestr = getenv("VMDMAXAASAMPLES");
00456   if (maxaasamplestr) {
00457     int aatmp;
00458     if (sscanf(maxaasamplestr, "%d", &aatmp) == 1) {
00459       if (aatmp >= 0) {
00460         maxaasamples=aatmp;
00461         msgInfo << "User-requested OpenGL antialiasing sample depth: " 
00462                 << maxaasamples << sendmsg;
00463 
00464         if (maxaasamples < 2) {
00465           maxaasamples=1; 
00466           msgInfo << "OpenGL antialiasing disabled by user override."
00467                   << sendmsg;
00468         }
00469       } else {
00470         msgErr << "Ignoring user-requested OpenGL antialiasing sample depth: " 
00471                << aatmp << sendmsg;
00472       }
00473     } else {
00474       msgErr << "Unable to parse override of OpenGL antialiasing" << sendmsg;
00475       msgErr << "sample depth: '" << maxaasamplestr << "'" << sendmsg;
00476     }
00477   }
00478 
00479 
00480   // loop over a big range of depth buffer sizes, starting with biggest 
00481   // and working our way down from there.
00482   for (dsize=32; dsize >= 16; dsize-=4) { 
00483 
00484 // Try the OpenGL ARB multisample extension if available
00485 #if defined(USEARBMULTISAMPLE) 
00486     if (!simplegraphics && !disablestereo && (!vi || (vi->c_class != TrueColor))) {
00487       // Stereo, multisample antialising, stencil buffer
00488       for (ns=maxaasamples; ns>1; ns--) {
00489         int conf[]  = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, dsize, 
00490                        GLX_STEREO,
00491                        GLX_STENCIL_SIZE, 1, 
00492                        GLX_SAMPLE_BUFFERS_ARB, 1, GLX_SAMPLES_ARB, ns, None};
00493         vi = glXChooseVisual(glxsrv->dpy, glxsrv->dpyScreen, conf);
00494   
00495         if (vi && (vi->c_class == TrueColor)) {
00496           *numsamples = ns;
00497           *msamp = TRUE;
00498           *stereo = TRUE;
00499           break; // exit loop if we got a good visual
00500         } 
00501       }
00502     }
00503 #endif
00504 
00505     if (getenv("VMDPREFERSTEREO") != NULL && !disablestereo) {
00506       // The preferred 24-bit color, quad buffered stereo mode.
00507       // This hack allows NVidia Quadro users to avoid the mutually-exclusive
00508       // antialiasing/stereo options on their cards with current drivers.
00509       // This forces VMD to skip looking for multisample antialiasing capable
00510       // X visuals and look for stereo instead.
00511       if (!simplegraphics && (!vi || (vi->c_class != TrueColor))) {
00512         int conf[] = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, dsize, 
00513                       GLX_STEREO,
00514                       GLX_STENCIL_SIZE, 1, 
00515                       GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, None};
00516         vi = glXChooseVisual(glxsrv->dpy, glxsrv->dpyScreen, conf);
00517         ns = 0; // no multisample antialiasing
00518         *numsamples = ns;
00519         *msamp = FALSE; 
00520         *stereo = TRUE; 
00521       }
00522     } 
00523 #if defined(USEARBMULTISAMPLE) 
00524     else {
00525       // Try the OpenGL ARB multisample extension if available
00526       if (!simplegraphics && (!vi || (vi->c_class != TrueColor))) {
00527         // Non-Stereo, multisample antialising, stencil buffer
00528         for (ns=maxaasamples; ns>1; ns--) {
00529           int conf[]  = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, dsize, 
00530                          GLX_STENCIL_SIZE, 1, 
00531                          GLX_SAMPLE_BUFFERS_ARB, 1, GLX_SAMPLES_ARB, ns, None};
00532           vi = glXChooseVisual(glxsrv->dpy, glxsrv->dpyScreen, conf);
00533     
00534           if (vi && (vi->c_class == TrueColor)) {
00535             *numsamples = ns;
00536             *msamp = TRUE;
00537             *stereo = FALSE; 
00538             break; // exit loop if we got a good visual
00539           } 
00540         }
00541       }
00542     }
00543 #endif
00544 
00545   } // end of loop over a wide range of depth buffer sizes
00546 
00547   // Ideally we should fall back to accumulation buffer based antialiasing
00548   // here, but not currently implemented.  At this point no multisample
00549   // antialiasing mode is available.
00550 
00551   // The preferred 24-bit color, quad buffered stereo mode
00552   if (!simplegraphics && !disablestereo && (!vi || (vi->c_class != TrueColor))) {
00553     int conf[] = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_STEREO,
00554                   GLX_STENCIL_SIZE, 1, 
00555                   GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, None};
00556     vi = glXChooseVisual(glxsrv->dpy, glxsrv->dpyScreen, conf);
00557     ns = 0; // no multisample antialiasing
00558     *numsamples = ns;
00559     *msamp = FALSE; 
00560     *stereo = TRUE; 
00561   }
00562 
00563   // Mode for machines that provide stereo only in modes with 16-bit color.
00564   if (!simplegraphics && !disablestereo && (!vi || (vi->c_class != TrueColor))) {
00565     int conf[] = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_STEREO,
00566                   GLX_STENCIL_SIZE, 1, 
00567                   GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None};
00568     vi = glXChooseVisual(glxsrv->dpy, glxsrv->dpyScreen, conf);
00569     ns = 0; // no multisample antialiasing
00570     *numsamples = ns;
00571     *msamp = FALSE; 
00572     *stereo = TRUE; 
00573   }
00574 
00575   // Mode for machines that provide stereo only without a stencil buffer, 
00576   // and with reduced color precision.  Examples of this are the SGI Octane2
00577   // machines with V6 graphics, with recent IRIX patch levels.
00578   // Without this configuration attempt, these machines won't get stereo.
00579   if (!simplegraphics && !disablestereo && (!vi || (vi->c_class != TrueColor))) {
00580     int conf[] = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_STEREO,
00581                   GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None};
00582     vi = glXChooseVisual(glxsrv->dpy, glxsrv->dpyScreen, conf);
00583     ns = 0; // no multisample antialiasing
00584     *numsamples = ns;
00585     *msamp = FALSE; 
00586     *stereo = TRUE; 
00587   }
00588 
00589   // This mode gives up on trying to get stereo, and goes back to trying
00590   // to get a high quality non-stereo visual.
00591   if (!simplegraphics && (!vi || (vi->c_class != TrueColor))) {
00592     int conf[] = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, 
00593                   GLX_STENCIL_SIZE, 1, 
00594                   GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, None};
00595     vi = glXChooseVisual(glxsrv->dpy, glxsrv->dpyScreen, conf);
00596     ns = 0; // no multisample antialiasing
00597     *numsamples = ns;
00598     *msamp = FALSE; 
00599     *stereo = FALSE;
00600   }
00601   
00602   // check if we have a TrueColor visual.
00603   if(!vi || (vi->c_class != TrueColor)) {
00604     // still no TrueColor.  Try again, with a very basic request ...
00605     // This is a catch all, we're desperate for any truecolor
00606     // visual by this point.  We've given up hoping for 24-bit
00607     // color or stereo by this time.
00608     int conf[] = {GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16, 
00609                   GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None};
00610     vi = glXChooseVisual(glxsrv->dpy, glxsrv->dpyScreen, conf);
00611     ns = 0; // no multisample antialiasing
00612     *numsamples = ns;
00613     *msamp = FALSE; 
00614     *stereo = FALSE;
00615   }
00616 
00617   if (!vi || (vi->c_class != TrueColor)) {
00618     // complete failure
00619     ns = 0; // no multisample antialiasing
00620     *numsamples = ns;
00621     *msamp = FALSE; 
00622     *stereo = FALSE;
00623   }
00624 
00625   return vi;
00626 }
00627 
00628 
00629 // make an X11 window full-screen, or return it to normal state
00630 static void setfullscreen(int fson, Display *dpy, Window win, int xinescreen) {
00631   struct {
00632     unsigned long flags;
00633     unsigned long functions;
00634     unsigned long decorations;
00635     long inputMode;
00636     unsigned long status;
00637   } wmhints;
00638   
00639   memset(&wmhints, 0, sizeof(wmhints));
00640   wmhints.flags = 2;       // changing window decorations
00641   if (fson) {
00642     wmhints.decorations = 0; // 0 (false) no window decorations
00643   } else {
00644     wmhints.decorations = 1; // 1 (true) window decorations enabled
00645   }
00646 
00647 #if !defined(VMD_NANOHUB)
00648   Atom wmproperty = XInternAtom(dpy, "_MOTIF_WM_HINTS", True);
00649 #else
00650   // Intern the atom even if it doesn't exist (no wm).
00651   Atom wmproperty = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
00652 #endif
00653   XChangeProperty(dpy, win, wmproperty, wmproperty, 32, 
00654                   PropModeReplace, (unsigned char *) &wmhints, 5);
00655 
00656   // resize window to size of either the whole X display screen,
00657   // or to the size of one of the Xinerama component displays
00658   // if Xinerama is enabled, and xinescreen is not -1.
00659   if (fson) {
00660     int dpyScreen = DefaultScreen(dpy);
00661   
00662     XSizeHints sizeHints;
00663     memset((void *) &(sizeHints), 0, sizeof(sizeHints));
00664     sizeHints.flags |= USSize;
00665     sizeHints.flags |= USPosition;
00666 
00667     sizeHints.width = DisplayWidth(dpy, dpyScreen);
00668     sizeHints.height = DisplayHeight(dpy, dpyScreen);
00669     sizeHints.x = 0;
00670     sizeHints.y = 0;
00671 
00672 #if defined(VMDXINERAMA)
00673     if (xinescreen != -1) {
00674       int xinerr, xinevent, xinenumscreens;
00675       if (XineramaQueryExtension(dpy, &xinevent, &xinerr) &&
00676           XineramaIsActive(dpy)) {
00677         XineramaScreenInfo *screens = 
00678           XineramaQueryScreens(dpy, &xinenumscreens);
00679         if (xinescreen >= 0 && xinescreen < xinenumscreens) {
00680           sizeHints.width = screens[xinescreen].width;
00681           sizeHints.height = screens[xinescreen].height;
00682           sizeHints.x = screens[xinescreen].x_org;
00683           sizeHints.y = screens[xinescreen].y_org;
00684         }
00685         XFree(screens);
00686       }
00687     }
00688 #endif
00689  
00690     XMoveWindow(dpy, win, sizeHints.x, sizeHints.y);
00691     XResizeWindow(dpy, win, sizeHints.width, sizeHints.height);
00692   }
00693 }
00694 
00695 
00697 
00698 OpenGLDisplayDevice::OpenGLDisplayDevice()
00699 : OpenGLRenderer((char *) "VMD " VMDVERSION " OpenGL Display") {
00700 
00701   // set up data possible before opening window
00702   stereoNames = glStereoNameStr;
00703   stereoModes = OPENGL_STEREO_MODES;
00704 
00705   renderNames = glRenderNameStr;
00706   renderModes = OPENGL_RENDER_MODES;
00707 
00708   cacheNames = glCacheNameStr;
00709   cacheModes = OPENGL_CACHE_MODES;
00710 
00711   memset(&glxsrv, 0, sizeof(glxsrv));
00712   glxsrv.dpy = NULL;
00713   glxsrv.dpyScreen = 0;
00714   glxsrv.xinp  = NULL;
00715   glxsrv.sball = NULL;
00716   glxsrv.havefocus = 0;
00717   have_window = FALSE;
00718   screenX = screenY = 0;
00719 
00720 #if defined(VMDOPTIXRTRT)
00721   ort = NULL; // OptiXRenderer context
00722   ogl_optix_rtrt_passthrough = 0;
00723 #endif  
00724 
00725 }
00726 
00727 int OpenGLDisplayDevice::init(int argc, char **argv, VMDApp *app, int *size, int *loc) {
00728   vmdapp = app; // save VMDApp handle for use by drag-and-drop handlers
00729                 // and GPU memory management routines
00730 
00731   // open the window
00732   glxsrv.windowID = open_window(name, size, loc, argc, argv);
00733   if (!have_window) return FALSE;
00734 
00735   // set flags for the capabilities of this display
00736   // whether we can do antialiasing or not.
00737   if (ext->hasmultisample) 
00738     aaAvailable = TRUE;  // we use multisampling over other methods
00739   else
00740     aaAvailable = FALSE; // no non-multisample implementation yet
00741 
00742   // set default settings
00743   if (ext->hasmultisample) {
00744     aa_on();  // enable fast multisample based antialiasing by default
00745               // other antialiasing techniques are slow, so only multisample
00746               // makes sense to enable by default.
00747   } 
00748 
00749   cueingAvailable = TRUE;
00750   cueing_on(); // leave depth cueing on by default, despite the speed hit.
00751 
00752   cullingAvailable = TRUE;
00753   culling_off();
00754 
00755   set_sphere_mode(sphereMode);
00756   set_sphere_res(sphereRes);
00757   set_line_width(lineWidth);
00758   set_line_style(lineStyle);
00759 
00760   // reshape and clear the display, which initializes some other variables
00761   reshape();
00762   normal();
00763   clear();
00764   update();
00765 
00766 #if defined(VMDOPTIXRTRT)
00767   printf("OpenGLDisplayDevice) Creating OptiX RTRT context...\n");
00768   ort = new OptiXRenderer(vmdapp);
00769   if (ort != NULL) {
00770     int i;
00771 
00772     ort->setup_context(xSize, ySize);
00773 
00774     // reset internal state between renders
00775     // reinitialize material cache, clean context state
00776     ort->destroy_scene();
00777 
00778 //    ort->set_bg_color(backColor);
00779     ort->set_bg_mode(OptiXRenderer::RT_BACKGROUND_TEXTURE_SOLID);
00780     ort->shadows_on(shadows_enabled()); // shadowing mode required 
00781     ort->set_aa_samples(8);
00782     ort->set_ao_samples(ao_enabled());
00783     ort->set_ao_ambient(get_ao_ambient());
00784     ort->set_ao_direct(get_ao_direct());
00785     ort->dof_on(dof_enabled());
00786     ort->set_camera_dof_fnumber(get_dof_fnumber());
00787     ort->set_camera_dof_focal_dist(get_dof_focal_dist());
00788 
00789     ort->set_cue_mode(OptiXRenderer::RT_FOG_NONE, 
00790                       get_cue_start(), get_cue_end(), get_cue_density());
00791 
00792     ort->set_camera_projection(OptiXRenderer::RT_PERSPECTIVE);
00793     ort->set_camera_zoom(0.5f / ((eyePos[2] - zDist) / vSize));
00794 
00795     // set stereoscopic display parameters
00796     ort->set_camera_stereo_eyesep(eyeSep);
00797     ort->set_camera_stereo_convergence_dist(eyeDist);
00798 
00799     // clear all existing lights before (re)appending the current lights,
00800     // otherwise if the OptiX context is reused, we will crash and burn.
00801     ort->clear_all_lights();
00802 
00803     // directional lights
00804     for (i=0; i<DISP_LIGHTS; i++) {
00805       if (ogl_lightstate[i]) {
00806         ort->add_directional_light(ogl_lightpos[i], ogl_lightcolor[i]);
00807       }
00808     }
00809 
00810     // XXX what about generating a fully populated material table?
00811   }
00812 
00813   printf("OpenGLDisplayDevice) OptiX RTRT context created.\n");
00814 #endif
00815 
00816   // We have a window, return success.
00817   return TRUE;
00818 }
00819 
00820 // destructor ... close the window
00821 OpenGLDisplayDevice::~OpenGLDisplayDevice(void) {
00822 #if defined(VMDOPTIXRTRT)
00823   printf("OpenGLDisplayDevice) Destroying OptiX RTRT context...\n");
00824   delete ort;
00825   ort = NULL;
00826   printf("OpenGLDisplayDevice) OptiX RTRT context destroyed.\n");
00827 #endif
00828 
00829   if (have_window) {
00830 #if defined(VMDXINPUT)
00831     // detach from XInput devices
00832     if (glxsrv.xinp != NULL) {
00833       xinput_close((xinputhandle *) glxsrv.xinp); 
00834     }
00835 #endif
00836     
00837     // detach from Xlib ClientMessage-based spaceball
00838     if (glxsrv.sball != NULL) {
00839       spaceball_close(glxsrv.sball); 
00840     }
00841 
00842     free_opengl_ctx(); // free display lists, textures, etc
00843  
00844     // close and delete windows, contexts, and display connections
00845     XUnmapWindow(glxsrv.dpy, glxsrv.windowID);
00846     glXDestroyContext(glxsrv.dpy, glxsrv.cx);
00847     XDestroyWindow(glxsrv.dpy, glxsrv.windowID);
00848     XCloseDisplay(glxsrv.dpy);
00849   }
00850 }
00851 
00852 
00854 
00855 
00856 // create a new window and set it's characteristics
00857 Window OpenGLDisplayDevice::open_window(char *nm, int *size, int *loc,
00858                                         int argc, char** argv
00859 ) {
00860   Window win;
00861   int i, SX = 100, SY = 100, W, H;
00862  
00863   char *dispname;
00864   if ((dispname = getenv("VMDGDISPLAY")) == NULL)
00865     dispname = getenv("DISPLAY");
00866 
00867   if(!(glxsrv.dpy = XOpenDisplay(dispname))) {
00868     msgErr << "Exiting due to X-Windows OpenGL window creation failure." << sendmsg;
00869     if (dispname != NULL) {
00870       msgErr << "Failed to open display: " << dispname << sendmsg;
00871     }
00872     return (Window)0; 
00873   }
00874 
00875 
00876 #if 0
00877   //
00878   // Check for "Composite" extension and any others that might cause
00879   // stability issues and warn the user about any potential problems...
00880   //
00881   char **xextensionlist;
00882   int nextensions, xtn;
00883   int warncompositeext=0;
00884   xextensionlist = XListExtensions(glxsrv.dpy, &nextensions);
00885   for (xtn=0; xtn<nextensions; xtn++) {
00886 //    printf("xtn[%d]: '%s'\n", xtn, xextensionlist[xtn]);
00887     if (xextensionlist[xtn] && !strcmp(xextensionlist[xtn], "Composite")) {
00888       warncompositeext=1;
00889     }
00890   }
00891   if (warncompositeext) {
00892     msgWarn << "Detected X11 'Composite' extension: if incorrect display occurs" << sendmsg;
00893     msgWarn << "try disabling this X server option.  Most OpenGL drivers" << sendmsg;
00894     msgWarn << "disable stereoscopic display when 'Composite' is enabled." << sendmsg;
00895   }
00896   XFreeExtensionList(xextensionlist);
00897 #endif
00898 
00899 
00900   //
00901   // get info about root window
00902   //
00903   glxsrv.dpyScreen = DefaultScreen(glxsrv.dpy);
00904   glxsrv.rootWindowID = RootWindow(glxsrv.dpy, glxsrv.dpyScreen);
00905   screenX = DisplayWidth(glxsrv.dpy, glxsrv.dpyScreen);
00906   screenY = DisplayHeight(glxsrv.dpy, glxsrv.dpyScreen);
00907   W = size[0];
00908   H = size[1];
00909   if (loc) {
00910     SX = loc[0];
00911     // The X11 screen uses Y increasing from upper-left corner down; this is
00912     // opposite to what GL does, which is the way VMD was set up originally
00913     SY = (screenY - loc[1]) - H;
00914   }
00915 
00916   // (3) make sure the GLX extension is available
00917   if (!glXQueryExtension(glxsrv.dpy, NULL, NULL)) {
00918     msgErr << "The X server does not support the OpenGL GLX extension." 
00919            << "   Exiting ..." << sendmsg;
00920     XCloseDisplay(glxsrv.dpy);
00921     return (Window)0;
00922   }
00923 
00924   ext->hasstereo = TRUE;         // stereo on until we find out otherwise.
00925   ext->stereodrawforced = FALSE; // no need for force stereo draws initially
00926   ext->hasmultisample = TRUE;    // multisample on until we find out otherwise.
00927 
00928   // (4) find an appropriate X-Windows GLX-capable visual and colormap ...
00929   XVisualInfo *vi;
00930   vi =  vmd_get_visual(&glxsrv, &ext->hasstereo, &ext->hasmultisample, &ext->nummultisamples);
00931 
00932   // make sure we have what we want, darnit ...
00933   if (!vi) {
00934     msgErr << "A TrueColor visual is required, but not available." << sendmsg;
00935     msgErr << "The X server is not capable of displaying double-buffered," << sendmsg;
00936     msgErr << "RGB images with a Z buffer.   Exiting ..." << sendmsg;
00937     XCloseDisplay(glxsrv.dpy);
00938     return (Window)0;
00939   }
00940 
00941   // (5) create an OpenGL rendering context
00942   if(!(glxsrv.cx = glXCreateContext(glxsrv.dpy, vi, None, GL_TRUE))) {
00943     msgErr << "Could not create OpenGL rendering context-> Exiting..." 
00944            << sendmsg;
00945     return (Window)0;
00946   }
00947 
00948   // (6) setup cursors, icons, iconized mode title, etc.
00949   glxsrv.cursor[0] = XCreateFontCursor(glxsrv.dpy, XC_left_ptr);
00950   glxsrv.cursor[1] = XCreateFontCursor(glxsrv.dpy, XC_fleur);
00951   glxsrv.cursor[2] = XCreateFontCursor(glxsrv.dpy, XC_sb_h_double_arrow);
00952   glxsrv.cursor[3] = XCreateFontCursor(glxsrv.dpy, XC_crosshair);
00953   glxsrv.cursor[4] = XCreateFontCursor(glxsrv.dpy, XC_watch);
00954   for(i=0; i < 5; i++)
00955     XRecolorCursor(glxsrv.dpy, glxsrv.cursor[i], &cursorFG, &cursorBG);
00956 
00957 
00958   //
00959   // Create the window
00960   //
00961   XSetWindowAttributes swa;
00962 
00963   //   For StaticGray , StaticColor, and TrueColor,
00964   //   alloc must be AllocNone , or a BadMatch error results
00965   swa.colormap = XCreateColormap(glxsrv.dpy, glxsrv.rootWindowID, 
00966                                  vi->visual, AllocNone);
00967 
00968   swa.background_pixmap = None;
00969   swa.border_pixel=0;
00970   swa.event_mask = ExposureMask;
00971   swa.cursor = glxsrv.cursor[0];
00972 
00973   win = XCreateWindow(glxsrv.dpy, glxsrv.rootWindowID, SX, SY, W, H, 0,
00974                       vi->depth, InputOutput, vi->visual,
00975                       CWBorderPixel | CWColormap | CWEventMask, &swa);
00976   XInstallColormap(glxsrv.dpy, swa.colormap);
00977 
00978   XFree(vi); // free visual info
00979 
00980   //
00981   // create size hints for new window
00982   //
00983   memset((void *) &(glxsrv.sizeHints), 0, sizeof(glxsrv.sizeHints));
00984   glxsrv.sizeHints.flags |= USSize;
00985   glxsrv.sizeHints.flags |= USPosition;
00986   glxsrv.sizeHints.width = W;
00987   glxsrv.sizeHints.height = H;
00988   glxsrv.sizeHints.x = SX;
00989   glxsrv.sizeHints.y = SY;
00990 
00991   XSetStandardProperties(glxsrv.dpy, win, nm, "VMD", None, argv, argc, &glxsrv.sizeHints);
00992   XWMHints *wmHints = XAllocWMHints();
00993   wmHints->initial_state = NormalState;
00994   wmHints->flags = StateHint;
00995   XSetWMHints(glxsrv.dpy, win, wmHints);
00996   XFree(wmHints);
00997 
00998   // Cause X11 to generate a ClientMessage event for WM window closure
00999 #if !defined(VMD_NANOHUB)
01000   Atom wmDeleteWindow = XInternAtom(glxsrv.dpy, "WM_DELETE_WINDOW", False);
01001 #else
01002   Atom wmDeleteWindow = XInternAtom(glxsrv.dpy, "WM_DELETE_WINDOW", True);
01003 #endif
01004   XSetWMProtocols(glxsrv.dpy, win, &wmDeleteWindow, 1);
01005 
01006   // (7) bind the rendering context to the window
01007   glXMakeCurrent(glxsrv.dpy, win, glxsrv.cx);
01008 
01009 
01010   // (8) actually request the window to be displayed
01011   XSelectInput(glxsrv.dpy, win, 
01012                KeyPressMask | ButtonPressMask | ButtonReleaseMask | 
01013                StructureNotifyMask | ExposureMask | 
01014                EnterWindowMask | LeaveWindowMask | FocusChangeMask);
01015   XMapRaised(glxsrv.dpy, win);
01016 
01017   // If we have acquired a multisample buffer with GLX, we
01018   // still need to test to see if we can actually use it.
01019   if (ext->hasmultisample) {
01020     int msampeext = 0;
01021 
01022     // check for ARB multisampling
01023     if (ext->vmdQueryExtension("GL_ARB_multisample")) {
01024       msampeext = 1;
01025     }
01026 
01027     if (!msampeext) {
01028       ext->hasmultisample = FALSE;
01029       ext->nummultisamples = 0;
01030     }
01031   }
01032 
01033   // (9) configure the rendering properly
01034   setup_initial_opengl_state();  // setup initial OpenGL state
01035 
01036 #if defined(VMDXINPUT)
01037   // (10) check for XInput based 6DOF controllers etc
01038   if (getenv("VMDDISABLEXINPUT") == NULL) {
01039     glxsrv.xinp = xinput_enable(glxsrv.dpy, win);
01040   }
01041 #endif
01042 
01043   // (11) Enable receiving Xlib ClientMessage-based Spaceball 
01044   //      events to this window
01045   if (getenv("VMDDISABLESPACEBALLXDRV") == NULL) {
01046     if (getenv("VMDSPACEBALLXDRVGLOBALFOCUS") == NULL) {
01047       // the driver will do focus processing for us
01048       glxsrv.sball = spaceball_enable(glxsrv.dpy, InputFocus);
01049     } else {
01050       // we'll do focus processing for ourselves
01051       glxsrv.sball = spaceball_enable(glxsrv.dpy, win);
01052     }
01053   }
01054   if (glxsrv.sball != NULL) {
01055     msgInfo << "X-Windows ClientMessage-based Spaceball device available." 
01056             << sendmsg;
01057   } 
01058 
01059 
01060   // initialize spaceball event structure to known state
01061   spaceball_init_event(&glxsrv.sballevent);
01062 
01063   // normal return: window was successfully created
01064   have_window = TRUE;
01065 
01066   // return window id
01067   return win;
01068 }
01069 
01070 
01071 int OpenGLDisplayDevice::prepare3D(int do_clear) {
01072   // force reset of OpenGL context back to ours in case something
01073   // else modified the OpenGL state
01074   glXMakeCurrent(glxsrv.dpy, glxsrv.windowID, glxsrv.cx);
01075 
01076   return OpenGLRenderer::prepare3D(do_clear);
01077 }
01078 
01079 
01080 void OpenGLDisplayDevice::do_resize_window(int w, int h) {
01081   if (getenv("VMDFULLSCREEN")) {
01082     int xinescreen=0;
01083     if (getenv("VMDXINESCREEN")) {
01084       xinescreen = atoi(getenv("VMDXINESCREEN"));
01085     }
01086     setfullscreen(1, glxsrv.dpy, glxsrv.windowID, xinescreen);
01087   } else {
01088     setfullscreen(0, glxsrv.dpy, glxsrv.windowID, -1);
01089     XResizeWindow(glxsrv.dpy, glxsrv.windowID, w, h);
01090   }
01091 
01092 #if defined(VMDOPTIXRTRT)
01093   if (ort != NULL) ort->framebuffer_resize(w, h);
01094 #endif
01095 }
01096 
01097 void OpenGLDisplayDevice::do_reposition_window(int xpos, int ypos) {
01098   XMoveWindow(glxsrv.dpy, glxsrv.windowID, xpos, ypos);
01099 }
01100 
01102 
01103 //
01104 // get the current state of the device's pointer (i.e. cursor if it has one)
01105 //
01106 
01107 // abs pos of cursor from lower-left corner of display
01108 int OpenGLDisplayDevice::x(void) {
01109   Window rw, cw;
01110   int rx, ry, wx, wy;
01111   unsigned int keymask;
01112 
01113   // get pointer info
01114   XQueryPointer(glxsrv.dpy, glxsrv.windowID, &rw, &cw, &rx, &ry, &wx, &wy, &keymask);
01115 
01116   // return value
01117   return rx;
01118 }
01119 
01120 
01121 // same, for y direction
01122 int OpenGLDisplayDevice::y(void) {
01123   Window rw, cw;
01124   int rx, ry, wx, wy;
01125   unsigned int keymask;
01126 
01127   // get pointer info
01128   XQueryPointer(glxsrv.dpy, glxsrv.windowID, &rw, &cw, &rx, &ry, &wx, &wy, &keymask);
01129 
01130   // return value
01131   // return value ... must subtract position from total size since
01132   // X is opposite to GL in sizing the screen
01133   return screenY - ry;
01134 }
01135 
01136 // return the current state of the shift, control, and alt keys
01137 int OpenGLDisplayDevice::shift_state(void) {
01138   int retval = 0;
01139 
01140   // get pointer info
01141   Window rw, cw;
01142   int rx, ry, wx, wy;
01143   unsigned int keymask;
01144   XQueryPointer(glxsrv.dpy, glxsrv.windowID, &rw, &cw, &rx, &ry, &wx, &wy, &keymask);
01145 
01146   // determine state of keys, and OR results together
01147   if ((keymask & ShiftMask) != 0)
01148     retval |= SHIFT;
01149 
01150   if ((keymask & ControlMask) != 0)
01151     retval |= CONTROL;
01152 
01153   if ((keymask & Mod1Mask) != 0)
01154     retval |= ALT;
01155 
01156   // return the result
01157   return retval;
01158 }
01159 
01160 
01161 // return the spaceball state, if any
01162 int OpenGLDisplayDevice::spaceball(int *rx, int *ry, int *rz, int *tx, int *ty,
01163 int *tz, int *buttons) {
01164   // return event state we have from X11 windowing system events
01165   if ((glxsrv.sball != NULL || glxsrv.xinp != NULL)
01166        && glxsrv.sballevent.event == 1) {
01167     *rx = glxsrv.sballevent.rx;
01168     *ry = glxsrv.sballevent.ry;
01169     *rz = glxsrv.sballevent.rz;
01170     *tx = glxsrv.sballevent.tx;
01171     *ty = glxsrv.sballevent.ty;
01172     *tz = glxsrv.sballevent.tz;
01173     *buttons = glxsrv.sballevent.buttons;
01174     return 1;
01175   }
01176 
01177   return 0;
01178 }
01179 
01180 
01181 // set the Nth cursor shape as the current one.  If no arg given, the
01182 // default shape (n=0) is used.
01183 void OpenGLDisplayDevice::set_cursor(int n) {
01184   int cursorindex;
01185 
01186   switch (n) {
01187     default:
01188     case DisplayDevice::NORMAL_CURSOR: cursorindex = 0; break;
01189     case DisplayDevice::TRANS_CURSOR:  cursorindex = 1; break;
01190     case DisplayDevice::SCALE_CURSOR:  cursorindex = 2; break;
01191     case DisplayDevice::PICK_CURSOR:   cursorindex = 3; break;
01192     case DisplayDevice::WAIT_CURSOR:   cursorindex = 4; break;
01193   }
01194 
01195   XDefineCursor(glxsrv.dpy, glxsrv.windowID, glxsrv.cursor[cursorindex]);
01196 }
01197 
01198 
01199 //
01200 // event handling routines
01201 //
01202 
01203 // queue the standard events (need only be called once ... but this is
01204 // not done automatically by the window because it may not be necessary or
01205 // even wanted)
01206 void OpenGLDisplayDevice::queue_events(void) {
01207   XSelectInput(glxsrv.dpy, glxsrv.windowID, 
01208                KeyPressMask | ButtonPressMask | ButtonReleaseMask | 
01209                StructureNotifyMask | ExposureMask | 
01210                EnterWindowMask | LeaveWindowMask | FocusChangeMask);
01211 }
01212 
01213 
01214 // This version of read_event flushes the entire queue before returning the
01215 // last event to the caller.  It fixes buggy window resizing behavior on 
01216 // Linux when using the Nvidia OpenGL drivers.  
01217 int OpenGLDisplayDevice::read_event(long &retdev, long &retval) {
01218   XEvent xev;
01219   char keybuf[10];
01220   int keybuflen = 9;
01221   KeySym keysym;
01222   XComposeStatus comp;
01223 
01224   memset(keybuf, 0, sizeof(keybuf)); // clear keyboard input buffer
01225 
01226   // clear previous spaceball event state, except for button state which
01227   // must be left alone.
01228   spaceball_clear_event(&glxsrv.sballevent);
01229 
01230   retdev = WIN_NOEVENT;
01231   // read all events, handling the ones that need to be handled internally,
01232   // and returning the last one for processing.
01233   int need_reshape = FALSE;
01234   while (XPending(glxsrv.dpy)) {
01235     XNextEvent(glxsrv.dpy, &xev);
01236 
01237     // find what kind of event it was
01238     switch(xev.type) {
01239     case Expose:
01240     case ConfigureNotify:
01241     case ReparentNotify:
01242     case MapNotify:
01243       need_reshape = TRUE; // Probably not needed for Expose or Map
01244       _needRedraw = 1;
01245       // retdev not set; we handle this ourselves.
01246       break;
01247     case KeyPress:
01248       {
01249         int k = XLookupString(&(xev.xkey), keybuf, keybuflen,  &keysym, &comp);
01250         // handle all strictly alphanumeric keys here
01251         if (k > 0 && *keybuf != '\0') {
01252           retdev = WIN_KBD;
01253           retval = *keybuf;
01254         } else {
01255           switch (keysym) {
01256             case XK_Escape:      retdev = WIN_KBD_ESCAPE;    break;
01257             case XK_Up:          retdev = WIN_KBD_UP;        break;
01258             case XK_Down:        retdev = WIN_KBD_DOWN;      break;
01259             case XK_Left:        retdev = WIN_KBD_LEFT;      break;
01260             case XK_Right:       retdev = WIN_KBD_RIGHT;     break;
01261             case XK_Page_Up:     retdev = WIN_KBD_PAGE_UP;   break;
01262             case XK_Page_Down:   retdev = WIN_KBD_PAGE_UP;   break;
01263             case XK_Home:        retdev = WIN_KBD_HOME;      break;
01264             case XK_End:         retdev = WIN_KBD_END;       break;
01265             case XK_Insert:      retdev = WIN_KBD_INSERT;    break;
01266             case XK_Delete:      retdev = WIN_KBD_DELETE;    break;
01267             case XK_F1:          retdev = WIN_KBD_F1;        break;
01268             case XK_F2:          retdev = WIN_KBD_F2;        break;
01269             case XK_F3:          retdev = WIN_KBD_F3;        break;
01270             case XK_F4:          retdev = WIN_KBD_F4;        break;
01271             case XK_F5:          retdev = WIN_KBD_F5;        break;
01272             case XK_F6:          retdev = WIN_KBD_F6;        break;
01273             case XK_F7:          retdev = WIN_KBD_F7;        break;
01274             case XK_F8:          retdev = WIN_KBD_F8;        break;
01275             case XK_F9:          retdev = WIN_KBD_F9;        break;
01276             case XK_F10:         retdev = WIN_KBD_F10;       break;
01277             case XK_F11:         retdev = WIN_KBD_F11;       break;
01278             case XK_F12:         retdev = WIN_KBD_F12;       break;
01279           } 
01280         } 
01281         break;
01282       }
01283     case ButtonPress:
01284     case ButtonRelease:
01285       {
01286         unsigned int button = xev.xbutton.button;
01287         retval = (xev.type == ButtonPress);
01288         switch (button) {
01289           case Button1:
01290             retdev = WIN_LEFT;
01291             break;
01292           case Button2:
01293             retdev = WIN_MIDDLE;
01294             break;
01295           case Button3:
01296             retdev = WIN_RIGHT;
01297             break;
01298           case Button4:
01299             retdev = WIN_WHEELUP;
01300             break;
01301           case Button5:
01302             retdev = WIN_WHEELDOWN;
01303             break;
01304         }
01305         break;
01306       }
01307       break;
01308 
01309     case FocusIn:
01310     case EnterNotify:
01311       glxsrv.havefocus=1;
01312       break;
01313 
01314     case FocusOut:
01315     case LeaveNotify:
01316       glxsrv.havefocus=0;
01317       break;
01318 
01319     case ClientMessage:
01320 #if 1
01321       // let the spaceball driver take care of focus processing
01322       // if we have mouse/keyboard focus, then translate spaceball events
01323       spaceball_decode_event(glxsrv.sball, &xev, &glxsrv.sballevent);
01324 #else
01325       // do our own focus handling
01326       // if we have mouse/keyboard focus, then translate spaceball events
01327       if (glxsrv.havefocus) {
01328         spaceball_decode_event(glxsrv.sball, &xev, &glxsrv.sballevent);
01329       }
01330 #endif
01331       break;
01332 
01333     default:
01334 #if defined(VMDXINPUT)
01335       if (glxsrv.xinp != NULL) {
01336         if (xinput_decode_event((xinputhandle *) glxsrv.xinp, &xev, 
01337                                  &glxsrv.sballevent)) {
01338           break;
01339         }
01340       }
01341 #endif
01342 
01343 #if 0
01344       msgWarn << "Unrecognized X11 event" << xev.type << sendmsg;
01345 #endif      
01346       break;
01347 
01348     } 
01349   } 
01350 
01351   if (need_reshape) 
01352     reshape();
01353 
01354   return (retdev != WIN_NOEVENT);
01355 }
01356 
01357 //
01358 // virtual routines for preparing to draw, drawing, and finishing drawing
01359 //
01360 
01361 // reshape the display after a shape change
01362 void OpenGLDisplayDevice::reshape(void) {
01363 
01364   // get and store size of window
01365   XWindowAttributes xwa;
01366   Window childwin;                  // not used, just needed for X call
01367   int rx, ry;
01368 
01369   // 
01370   // XXX WireGL notes: 
01371   //   WireGL doesn't have a variable window size like normal 
01372   // OpenGL windows do.  Not only that, but the size values reported
01373   // by X11 will be widly different from those reported by 
01374   // the glGetIntegerv(GL_VIEWPORT) call, and cause schizophrenic
01375   // behavior.  For now, we don't do anything about this, but 
01376   // the default window that comes up on the tiled display is not
01377   // locked to the same size and aspect ratio as the host display,
01378   // so spheres can look rather egg shaped if the X window on the 
01379   // host display isn't adjusted. 
01380   //
01381 
01382   XGetWindowAttributes(glxsrv.dpy, glxsrv.windowID, &xwa);
01383   XTranslateCoordinates(glxsrv.dpy, glxsrv.windowID, glxsrv.rootWindowID, -xwa.border_width,
01384                         -xwa.border_width, &rx, &ry, &childwin);
01385 
01386   xSize = xwa.width;
01387   ySize = xwa.height;
01388   xOrig = rx;
01389   yOrig = screenY - ry - ySize;
01390   
01391   switch (inStereo) {
01392     case OPENGL_STEREO_SIDE:
01393       set_screen_pos(0.5f * (float)xSize / (float)ySize);
01394       break;
01395 
01396     case OPENGL_STEREO_ABOVEBELOW:
01397       set_screen_pos(2.0f * (float)xSize / (float)ySize);
01398       break;
01399 
01400     case OPENGL_STEREO_STENCIL_CHECKERBOARD:
01401     case OPENGL_STEREO_STENCIL_COLUMNS:
01402     case OPENGL_STEREO_STENCIL_ROWS:
01403       enable_stencil_stereo(inStereo);
01404       set_screen_pos((float)xSize / (float)ySize);
01405       break;
01406  
01407     default:
01408       set_screen_pos((float)xSize / (float)ySize);
01409       break;
01410   }
01411 #if defined(VMD_NANOHUB)
01412   init_offscreen_framebuffer(xSize, ySize);
01413 #endif
01414 }
01415 
01416 
01417 unsigned char * OpenGLDisplayDevice::readpixels_rgb3u(int &xs, int &ys) {
01418   unsigned char * img = NULL;
01419   xs = xSize;
01420   ys = ySize;
01421 
01422   // fall back to normal glReadPixels() if better methods fail
01423   if ((img = (unsigned char *) malloc(xs * ys * 3)) != NULL) {
01424     glPixelStorei(GL_PACK_ALIGNMENT, 1);
01425     glReadPixels(0, 0, xs, ys, GL_RGB, GL_UNSIGNED_BYTE, img);
01426     return img; 
01427   }
01428 
01429   // else bail out
01430   xs = 0;
01431   ys = 0;
01432   return NULL;
01433 }
01434 
01435 unsigned char * OpenGLDisplayDevice::readpixels_rgba4u(int &xs, int &ys) {
01436   unsigned char * img = NULL;
01437   xs = xSize;
01438   ys = ySize;
01439 
01440   // fall back to normal glReadPixels() if better methods fail
01441   if ((img = (unsigned char *) malloc(xs * ys * 4)) != NULL) {
01442     glPixelStorei(GL_PACK_ALIGNMENT, 1);
01443     glReadPixels(0, 0, xs, ys, GL_RGBA, GL_UNSIGNED_BYTE, img);
01444     return img; 
01445   }
01446 
01447   // else bail out
01448   xs = 0;
01449   ys = 0;
01450   return NULL;
01451 }
01452 
01453 
01454 int OpenGLDisplayDevice::drawpixels_rgba4u(unsigned char *rgba, int &xs, int &ys) {
01455 
01456 #if 0
01457 //  glDrawBuffer(GL_BACK);
01458 //  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
01459 //  glClearColor(0.0, 0.0, 0.0, 1.0); /* black */
01460 //  glClear(GL_COLOR_BUFFER_BIT);
01461 
01462   glPushMatrix();
01463   glDisable(GL_DEPTH_TEST);
01464 
01465   glViewport(0, 0, xs, ys);
01466 
01467   glShadeModel(GL_FLAT);
01468   glMatrixMode(GL_PROJECTION);
01469   glLoadIdentity();
01470   glOrtho(0.0, xs, 0.0, ys, -1.0, 1.0);
01471   glMatrixMode(GL_MODELVIEW);
01472   glLoadIdentity();
01473   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
01474   glPixelZoom(1.0, 1.0);
01475 
01476   glRasterPos2i(0, 0);
01477   glDrawPixels(xs, ys, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
01478 
01479   glEnable(GL_DEPTH_TEST);
01480   glPopMatrix();
01481 #elif 1
01482 //  glDrawBuffer(GL_BACK);
01483 //  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
01484 //  glClearColor(0.0, 0.0, 0.0, 1.0); /* black */
01485 //  glClear(GL_COLOR_BUFFER_BIT);
01486 
01487   glPushMatrix();
01488   glDisable(GL_DEPTH_TEST);
01489 
01490   glViewport(0, 0, xs, ys);
01491 
01492   glShadeModel(GL_FLAT);
01493   glMatrixMode(GL_PROJECTION);
01494   glLoadIdentity();
01495   glOrtho(0.0, xs, 0.0, ys, -1.0, 1.0);
01496   glMatrixMode(GL_MODELVIEW);
01497 
01498   GLuint texName = 0;
01499   GLfloat texborder[4] = {0.0, 0.0, 0.0, 1.0};
01500   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
01501   glBindTexture(GL_TEXTURE_2D, texName);
01502 
01503   /* black borders if we go rendering anything beyond texture coordinates */
01504   glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, texborder);
01505   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
01506   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
01507   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
01508   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
01509   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
01510 
01511   glLoadIdentity();
01512   glColor3f(1.0, 1.0, 1.0);
01513 
01514 #if 1
01515   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0,
01516                GL_RGBA, GL_UNSIGNED_BYTE, rgba);
01517   glEnable(GL_TEXTURE_2D);
01518 #endif
01519 
01520   glBegin(GL_QUADS);
01521   glTexCoord2f(0.0f, 0.0f);
01522   glVertex2f(0, 0);
01523   glTexCoord2f(0.0f, 1.0f);
01524   glVertex2f(0, GLfloat(ys));
01525   glTexCoord2f(1.0f, 1.0f);
01526   glVertex2f(GLfloat(xs), GLfloat(ys));
01527   glTexCoord2f(1.0f, 0.0f);
01528   glVertex2f(GLfloat(xs), 0.0f);
01529   glEnd();
01530 
01531 #if 1
01532   glDisable(GL_TEXTURE_2D);
01533 #endif
01534 
01535   glEnable(GL_DEPTH_TEST);
01536   glPopMatrix();
01537 #endif
01538 
01539   update();
01540 
01541   return 0;
01542 }
01543 
01544 
01545 // update after drawing
01546 void OpenGLDisplayDevice::update(int do_update) {
01547   if (wiregl) {
01548     glFinish(); // force cluster to synchronize before buffer swap, 
01549                 // this gives much better results than if the 
01550                 // synchronization is done implicitly by glXSwapBuffers.
01551   }
01552 
01553 #if 1
01554   // push latest frame into the video streaming pipeline
01555   // and pump the event handling mechanism afterwards
01556   if (vmdapp->uivs && vmdapp->uivs->srv_connected()) {
01557     // if no frame was provided, we grab the GL framebuffer
01558     int xs, ys;
01559     unsigned char *img = NULL;
01560     img = readpixels_rgba4u(xs, ys);
01561     if (img != NULL) {
01562       // srv_send_frame(img, xs * 4, xs, ys, vs_forceIframe);
01563       vmdapp->uivs->video_frame_pending(img, xs, ys);
01564       vmdapp->uivs->check_event();
01565       free(img);
01566     }
01567   }
01568 #endif
01569 
01570 #if !defined(VMD_NANOHUB)
01571   // Normal contexts are double-buffered, but Nanohub uses an FBO that is not.
01572   if (do_update)
01573     glXSwapBuffers(glxsrv.dpy, glxsrv.windowID);
01574 #endif
01575 
01576   glDrawBuffer(GL_BACK);
01577 }
01578 
01579 
01580 void OpenGLDisplayDevice::set_window_title(char *newtitlestr) {
01581 #if !defined(VMDSDL) && !defined(_MSC_VER)
01582   XStoreName(glxsrv.dpy, glxsrv.windowID, newtitlestr);
01583 #endif
01584 }
01585 
01586 
01587 
01588 

Generated on Fri Nov 8 02:44:57 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002