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