/* -*-c++-*- Producer - Copyright (C) 2001-2004 Don Burns * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #ifdef _X11_IMPLEMENTATION #include #include #include #include #include #include #include using namespace Producer; // HP, SGI and SUN don't implement glXGetProcAddressARB... #if defined (__sgi) || defined (sun) || defined( __hpux ) #include void (*glXGetProcAddressARB(const GLubyte *procName))(void) { static void *handle = dlopen( (const char *)0L, RTLD_LAZY ); return (void (*)(void))dlsym(handle,(const char *)procName); } #endif #if (defined(__APPLE__) || defined(macintosh)) & defined (GLX_VERSION_1_4) #define glXGetProcAddressARB glXGetProcAddress #endif #if (defined(__APPLE__) || defined(macintosh)) || defined(_AIX) #define USE_LOCAL_glXGetFBConfigFromVisual #endif #ifdef USE_LOCAL_glXGetFBConfigFromVisual static GLXFBConfig _glXGetFBConfigFromVisual(::Display *dpy, XVisualInfo *visinfo) { int screen = visinfo->screen; int nelements; GLXFBConfig *configs = glXGetFBConfigs(dpy, screen, &nelements); for( int i = 0; i < nelements; i++ ) { int visual_id; if( glXGetFBConfigAttrib( dpy, configs[i], GLX_VISUAL_ID, &visual_id ) == 0 ) { if( (unsigned int)visual_id == visinfo->visualid ) return configs[i]; } } return NULL; } #endif unsigned int RenderSurface::getNumberOfScreens() { if( _numScreens == UnknownAmount ) { Display *dpy = XOpenDisplay(""); if( dpy == NULL ) { std::cerr << "Unable to open display \"" << XDisplayName( "") << "\". Is the DISPLAY environmental variable set?\n"; return 0; } _numScreens = ScreenCount(dpy); XCloseDisplay(dpy); } return _numScreens; } static OpenThreads::Mutex realize_mutex; bool RenderSurface::realize( VisualChooser *vc, GLContext sharedGLContext ) { if( _realized ) return _realized; OpenThreads::ScopedLock lock(realize_mutex); if( vc != NULL ) _visualChooser = vc; if( sharedGLContext != 0L ) _sharedGLContext = sharedGLContext; else _sharedGLContext = _globallySharedGLContext; if( _init() == false ) return false; if( _drawableType == DrawableType_Window ) { if( _useConfigEventThread ) { startThread(); _threadReady->block(); } XMapWindow( _dpy, _win ); Window temp = _win; XSetWMColormapWindows( _dpy, _win, &temp, 1); } makeCurrent(); #if 0 // NVidia 1.0-4496 driver breaks (once again), glXGetVideoSync. This problem was seen // earlier on laptops (Geforce4Go), but seems to be present on desktops as // wells now. A bug will be filed with NVidia again. // // glXWaitVideoSyncSGI is used for synchronizing the CPU bound process to // video retrace signal and is ultimately not necessary, but would provide // consistent frame rates in the CPU if it was present. // // Note that testVSync hangs if the window has not been mapped. If the window // has been mapped, glXGetVideoSync() fails #endif testVSync(); if( _overrideRedirectFlag == true ) { XFlush(_dpy); XSync(_dpy,0); usleep(500000); // Hack to allow window manager to catch up XSetInputFocus( _dpy, _win, RevertToParent, CurrentTime ); XFlush(_dpy); XSync(_dpy,0); } _realized = true; std::vector >::iterator p; for( p = _realizeCallbacks.begin(); p != _realizeCallbacks.end(); p++ ) { if( (*p).valid() ) (*(*p).get())( *this ); } _realizeCallbacks.clear(); _realizeBlock->release(); return _realized; } bool RenderSurface::_createVisualInfo() { // If Window was already defined, lets grab that visual if( _win != 0 ) { XWindowAttributes watt; XGetWindowAttributes( _dpy, _win, &watt ); XVisualInfo temp; temp.visualid = XVisualIDFromVisual(watt.visual); int n; _visualInfo = XGetVisualInfo( _dpy, VisualIDMask, &temp, &n ); } else { if( !_visualChooser.valid() ) { ref_ptr lvc = new VisualChooser; lvc->setSimpleConfiguration(_drawableType != DrawableType_PBuffer); _visualInfo = lvc->choose(_dpy, _screen); } else { _visualInfo = _visualChooser->choose(_dpy,_screen); } } _visualID = 0; if( _visualInfo == NULL ) return false; _visualID = _visualInfo->visualid; return true; } bool RenderSurface::_init() { char dpyname[128]; bool _dpy_created_locally = false; if( _hostname.empty() ) sprintf( dpyname, ":%d.%d", _displayNum, _screen ); else sprintf( dpyname, "%s:%d.%d", _hostname.c_str(), _displayNum, _screen ); // Set Display if( _dpy == NULL ) { if( (_dpy = XOpenDisplay( dpyname )) == (Display *)0L ) { std::cerr << "RenderSurface::realize() - unable to open display \"" <visual, AllocNone); //swatt.colormap = DefaultColormap( _dpy, 0 ); swatt.background_pixel = 0; swatt.border_pixel = 0; swatt.event_mask = 0; unsigned long mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap; if( _overrideRedirectFlag == true ) { swatt.override_redirect = true; mask |= CWOverrideRedirect; } _win = XCreateWindow( _dpy, _parent, _windowX, _parentWindowHeight - (_windowHeight + _windowY), _windowWidth, _windowHeight, 0, _visualInfo->depth, InputOutput, _visualInfo->visual, mask, &swatt ); // This positions the window at _windowX, _windowY XSizeHints sh; sh.flags = 0; sh.flags |= USSize; sh.flags &= 0x7; sh.flags |= USPosition; sh.flags &= 0xB; sh.x = _windowX; sh.y = _parentWindowHeight - (_windowHeight + _windowY); sh.width = _windowWidth; sh.height = _windowHeight; XSetStandardProperties( _dpy, _win, _windowName.c_str(), _windowName.c_str(), None, 0, 0, &sh); // This turns off border decorations if( _decorations == false ) _setBorder(_decorations); // Create default Cursor { _defaultCursor = XCreateFontCursor( _dpy, XC_left_ptr ); } // Create Null Cursor { Pixmap pixmap; static char buff[2] = {0,0}; static XColor ncol = {0,0,0,0,DoRed|DoGreen|DoBlue,0}; pixmap = XCreateBitmapFromData( _dpy, DefaultRootWindow(_dpy), buff, 1, 1); _nullCursor = XCreatePixmapCursor( _dpy, pixmap, pixmap, &ncol, &ncol, 0, 0 ); } _setCursorToDefault(); } else if( _drawableType == DrawableType_PBuffer ) { #ifdef GLX_VERSION_1_3 #ifdef USE_LOCAL_glXGetFBConfigFromVisual GLXFBConfig fbconfig = _glXGetFBConfigFromVisual( _dpy, _visualInfo ); #else GLXFBConfig fbconfig = glXGetFBConfigFromVisualSGIX( _dpy, _visualInfo ); #endif if( _windowWidth == UnknownDimension ) _windowWidth = _screenWidth; if( _windowHeight == UnknownDimension ) _windowHeight = _screenHeight; std::vector attributeList(_user_pbattr); attributeList.push_back( GLX_PBUFFER_WIDTH ); attributeList.push_back( _windowWidth ); attributeList.push_back( GLX_PBUFFER_HEIGHT ); attributeList.push_back( _windowHeight ); attributeList.push_back( 0L ); _win = glXCreatePbuffer(_dpy, fbconfig, &attributeList.front() ); #else _win = 0L; return false; #endif } } else { XWindowAttributes watt; XGetWindowAttributes( _dpy, _win, &watt ); _windowX = watt.x; _windowY = watt.y; _windowWidth = watt.width; _windowHeight = watt.height; // We can't know if the imposed window may resize or not _mayFullScreen = false; // We must not check for events. Leave that up to the externally created window _checkOwnEvents = false; // Set Screen Number _screen = -1; for( int i = 0; i < ScreenCount(_dpy); i++ ) { if( watt.screen == ScreenOfDisplay(_dpy,i) ) { _screen = i; break; } } if( _screen == -1 ) { // A warning might be appropriate here _screen = 0; } } if( _windowHeight == _screenHeight && _windowWidth == _screenWidth ) _isFullScreen = true; else if( _useCustomFullScreen && _windowHeight == _customFullScreenHeight && _windowWidth == _customFullScreenWidth ) _isFullScreen = true; else _isFullScreen = false; if( !_useConfigEventThread && _drawableType == DrawableType_Window ) XSelectInput( _dpy, _win, ExposureMask | StructureNotifyMask ); XFlush( _dpy ); XSync( _dpy, 0 ); return true; } bool RenderSurface::makeCurrent( void ) const { if( _readDrawableRenderSurface != 0L && _readDrawableRenderSurface->isRealized()) { #ifdef GLX_VERSION_1_3 glXMakeContextCurrent(_dpy, _win, _readDrawableRenderSurface->getWindow(), _glcontext ); #else glXMakeCurrent( _dpy, _win, _glcontext ); #endif } else glXMakeCurrent( _dpy, _win, _glcontext ); return true; } void RenderSurface::swapBuffers( void ) { if( !_realized ) return; makeCurrent(); glXSwapBuffers( _dpy, _win ); if( _checkOwnEvents && _useConfigEventThread == false ) { while( XPending(_dpy) ) _checkEvents(_dpy); } } void RenderSurface::sync( int divisor ) { if( !_realized ) return; // In spite of repeated efforts to get Nvidia and the ARB to // Communicate on this issue, the ARB definition of glXWaitVideoSyncSGI() // is ambiguous and NVidia's implementation of it favors the least // likely interpretation. // So, it is necessary to call GetVideoSync before EVERY WaitVideoSync // if you want WaitVideoSync to work correctly when divisor = 1, on // NVidia. On SGI, the penalty is an extra call to GetVideoSync if( __glxGetVideoSyncSGI != NULL && __glxWaitVideoSyncSGI != NULL ) { // This may be fixed now... // 5/16/03 Keep this in. If divisor is anything other than // 0 this still does not work __glxGetVideoSyncSGI( &_frameCount ); __glxWaitVideoSyncSGI( divisor, 0, &_frameCount ); } // else do nothing. Warning already printed in testVSync. } void RenderSurface::testVSync( void ) { unsigned int frame = 0; __glxGetRefreshRateSGI = reinterpret_cast (glXGetProcAddressARB((GLubyte *)"glXGetRefreshRateSGI" )); __glxWaitVideoSyncSGI = reinterpret_cast (glXGetProcAddressARB((GLubyte *)"glXWaitVideoSyncSGI" )); __glxGetVideoSyncSGI = reinterpret_cast (glXGetProcAddressARB((GLubyte * )"glXGetVideoSyncSGI")); if( __glxGetRefreshRateSGI != NULL ) __glxGetRefreshRateSGI(&_refreshRate); std::string emsg( "Warning: Producer::RenderSurface, under the X11 environment\n" " has tested the glXWaitVideoSyncSGI extension and found it\n" " to not be working properly. This does not necessarily mean\n" " that glXSwapBuffers will not block on VSYNC, but it does mean\n" " that the CPU bound application will not be able to synchronize\n" " with the graphics video retrace signal. Most operations will work\n" " fine."); // Prior reference to a bug here in the nVidia drivers is moot. // Bug now seems to be fixed. if( __glxGetVideoSyncSGI != NULL && __glxWaitVideoSyncSGI != NULL ) { __glxGetVideoSyncSGI(&frame); if( frame != 0 ) { __glxWaitVideoSyncSGI( 1, 0, &frame ); _frameCount = frame; __glxWaitVideoSyncSGI( 1, 0, &frame ); __glxWaitVideoSyncSGI( 1, 0, &frame ); if( frame != (_frameCount + 2) ) std::cerr << emsg << std::endl; } else std::cerr << emsg << std::endl; } else std::cerr << emsg << std::endl; _frameCount = frame; } bool RenderSurface::_checkEvents( Display *dpy ) { bool ret = true; XEvent ev; XNextEvent( dpy, &ev ); // Window manager will reparent and reset the x,y location relative // to the window manager's border window. On a reparent, we set // this flag. Seems to work but ..... static bool _ignore_resize_flag_hack = false; switch( ev.type ) { case Expose : break; case ConfigureNotify : if( !_isFullScreen && !_ignore_resize_flag_hack ) { _windowWidth = ev.xconfigure.width; _windowHeight = ev.xconfigure.height; _windowX = ev.xconfigure.x; _windowY = DisplayHeight(dpy,_screen) - (ev.xconfigure.y+_windowHeight); if( _bindInputRectangleToWindowSize == true ) _inputRectangle.set( 0.0, float(_windowWidth), 0.0, float (_windowHeight)); } _ignore_resize_flag_hack = false; break; case GravityNotify : break; case MapNotify : { XWindowAttributes watt; do XGetWindowAttributes(dpy, _win, &watt ); while( watt.map_state != IsViewable ); XSetInputFocus( dpy, _win, RevertToNone, CurrentTime ); XFlush(dpy); XSync(dpy,0); } break; case UnmapNotify : break; case ReparentNotify: _ignore_resize_flag_hack = true; break; case DestroyNotify : _realized = false; ret = false; break; } return ret; } void RenderSurface::run( void ) { Display *ldpy; char dpyname[128]; if( _hostname.empty() ) sprintf( dpyname, ":%d.%d", _displayNum, _screen ); else sprintf( dpyname, "%s:%d.%d", _hostname.c_str(), _displayNum, _screen ); if( (ldpy = XOpenDisplay( dpyname )) == (Display *)0L ) { std::cerr << "RenderSurface() : Reconfigure event thread - " "Unable to open display \"" << XDisplayName( dpyname ) << "\"" << std::endl; //return (void *)1; return; } const unsigned long mask = ExposureMask | StructureNotifyMask; XSelectInput( ldpy, _win, mask ); if( _threadReady != 0L ) _threadReady->block(); for( ;; ) { #if 0 fd_set rfds; int fd = ConnectionNumber(ldpy); FD_ZERO(&rfds); FD_SET( fd, &rfds ); if( select(fd+1,&rfds,0,0,0) < 0 ) testCancel(); #endif do { _checkEvents(ldpy); testCancel(); } while(XPending(ldpy)); } XCloseDisplay( ldpy ); } void RenderSurface::fullScreen( bool flag ) { if( _drawableType != DrawableType_Window ) return; if( flag == _isFullScreen ) return; if( flag ) { if( _mayFullScreen == false || !_realized ) { if( !_realized ) std::cerr << "RenderSurface::fullScreen() - Cannot set full screen before realization\n"; return; } _isFullScreen = true; _setBorder(false); if( _useCustomFullScreen ) { XMoveResizeWindow( _dpy, _win, _customFullScreenOriginX, _screenHeight - (_customFullScreenOriginY + _customFullScreenHeight), _customFullScreenWidth, _customFullScreenHeight ); } else { XMoveResizeWindow( _dpy, _win, 0, 0, _screenWidth, _screenHeight ); } XFlush(_dpy); XSync(_dpy,0); usleep(100000); // Hack to allow window manager to catch up } else { _isFullScreen = false; _resizeWindow(); _setBorder(_decorations); XFlush(_dpy); XSync(_dpy,0); usleep(100000); // Hack to allow window manager to catch up } } void RenderSurface::_resizeWindow() { if( _drawableType != DrawableType_Window ) return; XMoveResizeWindow( _dpy, _win, _windowX, _screenHeight - (_windowY + _windowHeight), _windowWidth, _windowHeight ); XFlush(_dpy); XSync(_dpy,0); usleep(100000); // Hack to allow window manager to do its thing. } void RenderSurface::_setBorder( bool flag ) { if( _drawableType != DrawableType_Window ) return; Atom atom; if( (atom = XInternAtom( _dpy, "_MOTIF_WM_HINTS", 0 )) != None ) { // Hack for sending 64 bit atom to Xserver #if defined( _MIPS_SIM) && (_MIPS_SIM == _MIPS_SIM_ABI64) || defined ( __ia64 ) || defined (__amd64 ) || defined(__x86_64__) struct { CARD32 flags0; CARD32 flags1; CARD32 functions0; CARD32 functions1; CARD32 decorations0; CARD32 decorations1; INT32 input_mode0; INT32 input_mode1; CARD32 status0; CARD32 status1; } wmHints; #if defined( __ia64 ) || defined (__amd64) || defined (__x86_64__) wmHints.flags0 = (1L << 1); wmHints.functions0 = 0; wmHints.decorations0 = flag; wmHints.input_mode0 = 0; #else wmHints.flags1 = (1L << 1); wmHints.functions1 = 0; wmHints.decorations1 = flag; wmHints.input_mode1 = 0; #endif #else struct { CARD32 flags; CARD32 functions; CARD32 decorations; INT32 input_mode; CARD32 status; } wmHints; wmHints.flags = (1L << 1); wmHints.functions = 0; wmHints.decorations = flag; wmHints.input_mode = 0; #endif XUnmapWindow(_dpy, _win ); XChangeProperty( _dpy, _win, atom, atom, 32, PropModeReplace, (unsigned char *)&wmHints, 5 ); XMapWindow(_dpy, _win ); XFlush(_dpy); XSync(_dpy,0); } else std::cerr << "RenderSurface::setBorder(" << flag << ") - Warning : Couldn't change decorations." << std::endl; } void RenderSurface::_setWindowName( const std::string &name ) { if( !_realized ) return; if( _drawableType != DrawableType_Window ) return; char *slist[] = { (char *)name.c_str(), 0L }; XTextProperty xtp; XStringListToTextProperty( slist, 1, &xtp ); XSetWMName( _dpy, _win, &xtp ); XFlush(_dpy); XSync(_dpy,0); } void RenderSurface::_useCursor(bool flag) { if( _drawableType != DrawableType_Window ) return; _useCursorFlag = flag; if( _realized ) _setCursor(_currentCursor); } void RenderSurface::_setCursor(Cursor cursor) { if( _drawableType != DrawableType_Window ) return; if( _useCursorFlag == false ) { XDefineCursor( _dpy, _win, _nullCursor ); } else { _currentCursor = cursor ? cursor : _nullCursor; XDefineCursor( _dpy, _win, _currentCursor ); } XFlush(_dpy); XSync(_dpy,0); } void RenderSurface::_setCursorToDefault() { _setCursor(_defaultCursor); //XUndefineCursor( _dpy, _win ); } void RenderSurface::_positionPointer( int x, int y ) { if( !_realized ) return; if( _drawableType != DrawableType_Window ) return; if( _isFullScreen ) y = _screenHeight - y; else y = _windowHeight - y; XWarpPointer( _dpy, None, _win, 0, 0, 0, 0, x, y ); XFlush(_dpy); XSync(_dpy,0); } void RenderSurface::_computeScreenSize( unsigned int &width, unsigned int &height ) const { Display *dpy = XOpenDisplay(""); if( dpy == NULL ) { std::cerr << "Unable to open display \"" << XDisplayName("") << "\". Is the DISPLAY environmental variable set?\n"; return; } width = DisplayWidth(dpy, _screen); height = DisplayHeight(dpy,_screen); XCloseDisplay(dpy); } void RenderSurface::mapWindow() { if( _drawableType != DrawableType_Window ) return; if( _realized && _parent == RootWindow( _dpy, _screen ) ) { XMapWindow( _dpy, _win ); XFlush(_dpy); XSync(_dpy,0); } } void RenderSurface::unmapWindow() { if( _drawableType != DrawableType_Window ) return; if( _parent == RootWindow( _dpy, _screen ) ) { XUnmapWindow( _dpy, _win ); XFlush(_dpy); XSync(_dpy,0); } } void RenderSurface::_fini() { if( _realized ) { if( _drawableType == DrawableType_Window ) { //glXDestroyContext( _dpy, _glcontext ); XDestroyWindow(_dpy, _win ); } else if( _drawableType == DrawableType_PBuffer ) { #ifdef GLX_VERSION_1_3 glXDestroyPbuffer( _dpy, _win ); #endif } XFlush( _dpy ); XSync(_dpy,0); } if( _visualInfo != NULL ) XFree(_visualInfo); } void RenderSurface::bindPBufferToTexture(BufferType buffer) const { glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, _windowWidth, _windowHeight); } void RenderSurface::_useOverrideRedirect( bool flag) { if( _drawableType != DrawableType_Window ) return; if( _realized ) { std::cerr << "RenderSurface::_useOverrideRedirect(" << flag << "): WARNING - Has no effect after realize() has been called" << std::endl; return; } else _overrideRedirectFlag = flag; } void RenderSurface::_initThreads() { // For multi-threaded X environment we must initialize the // multi-threaded support before we do anything else -- LJE static bool initialized = false; // We shouldn't do this multiple times... if( initialized ) return; initialized = true; #ifdef GLX_SUN_init_threads Display *dpy = XOpenDisplay(":0.0"); if( dpy == NULL ) { std::cerr << "Unable to open display \"" << XDisplayName( ":0.0") << "\". Is the DISPLAY environmental variable set?\n"; exit(1); } if ( strstr ( glXGetClientString (dpy, GLX_EXTENSIONS ), "GLX_SUN_init_threads" ) ) { if ( ! glXInitThreadsSUN ( ) ) { std::cerr << "Unable to init GL for multithreaded rendering\n" << std::endl; exit(1); } else { std::cout << "Initialized Threads...\n"; } } XCloseDisplay(dpy); #else // InitThreads is only required for Sun (for some unknown reason). // XInitThreads man page reads: // // It is only necessary to call this function if multiple threads might use Xlib concurrently. If all // calls to Xlib functions are protected by some other access mechanism (for example, a mutual exclusion // lock in a toolkit or through explicit client programming), Xlib thread initialization is not required. // It is recommended that single-threaded programs not call this function. // // Since Producer enforces a policy of one-thread per client, this is not necessary and, potentially, detrimental // Don Burns - May 3, 2005 #if 0 if (XInitThreads() == 0) { std::cerr << "Error in RenderSurface::InitThreads(): XInitThreads() failed. Aborting." << std::endl; exit(1); } else { std::cout << "RenderSurface::InitThreads(): multi-threaded X support initialized.\n"; } #endif #endif } #endif