/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2006 Torus Knot Software Ltd Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. You may alternatively use this source under the terms of a specific version of the OGRE Unrestricted License provided you have obtained such a license from Torus Knot Software Ltd. ----------------------------------------------------------------------------- */ #include "OgreGLXWindow.h" #include "OgreRoot.h" #include "OgreGLRenderSystem.h" #include "OgreImageCodec.h" #include "OgreException.h" #include "OgreLogManager.h" #include "OgreStringConverter.h" #include "OgreGLXUtils.h" #include "OgreWindowEventUtilities.h" #include #include #include #include #include #include #ifndef NO_XRANDR #include #endif #include #include #include namespace Ogre { //-------------------------------------------------------------------------------------------------// GLXWindow::GLXWindow(Display *display) : mDisplay(display), mWindow(0), mGlxContext(0), mVisualInfo(0), mDelVisualInfo(false), mDelWindow(false), mClosed(false), mVisible(true), mOldMode(-1), mContext(0) { mActive = false; mIsFullScreen = false; } //-------------------------------------------------------------------------------------------------// GLXWindow::~GLXWindow() { if (mDelVisualInfo && mVisualInfo) XFree(mVisualInfo); if(mGlxContext) glXDestroyContext(mDisplay, mGlxContext); if(mDelWindow && mWindow) XDestroyWindow(mDisplay, mWindow); #ifndef NO_XRANDR if(mIsFullScreen) { // Restore original video mode. Window rootWindow = DefaultRootWindow(mDisplay); XRRScreenConfiguration *config; // Get current screen info config = XRRGetScreenInfo(mDisplay, rootWindow); if(config) { Rotation current_rotation; XRRConfigCurrentConfiguration (config, ¤t_rotation); //std::cerr << "Restore mode " << mOldMode << std::endl; LogManager::getSingleton().logMessage("GLXWindow::~GLXWindow -- Leaving full screen mode"); XRRSetScreenConfig(mDisplay, config, rootWindow, mOldMode, current_rotation, CurrentTime); XRRFreeScreenConfigInfo(config); } else { LogManager::getSingleton().logMessage("GLXWindow::~GLXWindow -- Could not switch from full screen mode: XRRGetScreenInfo failed"); } } #endif } //-------------------------------------------------------------------------------------------------// void GLXWindow::create(const String& name, unsigned int width, unsigned int height, bool fullScreen, const NameValuePairList *miscParams) { LogManager::getSingleton().logMessage("GLXWindow::create"); String title = name; size_t fsaa_samples = 0; // We will attempt to create new window on default screen op display 0 // unless external window handle passed below int screen = DefaultScreen(mDisplay); int depth = DisplayPlanes(mDisplay, screen); Window rootWindow = RootWindow(mDisplay,screen); Window parentWindow = rootWindow; // Make sure the window is centered if no left and top in parameters size_t left = (int)DisplayWidth(mDisplay, screen)/2 - width/2; size_t top = (int)DisplayHeight(mDisplay, screen)/2 - height/2; // Maybe user already created the window and passed its visualinfo in miscParams XVisualInfo * extVisualHandler = NULL; // Unless parentWindowHandle is given in miscParams we're top-level mTopLevel = true; LogManager::getSingleton().logMessage("Parsing miscParams"); if(miscParams) { // Parse miscellenous parameters NameValuePairList::const_iterator opt; // Full screen anti aliasing opt = miscParams->find("FSAA"); if(opt != miscParams->end()) //check for FSAA parameter, if not ignore it... fsaa_samples = StringConverter::parseUnsignedInt(opt->second); // left (x) opt = miscParams->find("left"); if(opt != miscParams->end()) left = StringConverter::parseUnsignedInt(opt->second); // top (y) opt = miscParams->find("top"); if(opt != miscParams->end()) top = StringConverter::parseUnsignedInt(opt->second); // Window title opt = miscParams->find("title"); if(opt != miscParams->end()) //check for FSAA parameter, if not ignore it... title = opt->second; opt = miscParams->find("parentWindowHandle"); if(opt != miscParams->end()) { // embedding OGRE std::vector tokens = StringUtil::split(opt->second, " :"); String new_display = tokens[0]; String new_screen = tokens[1]; String wid = tokens[2]; // Now set things to their correct values // This must be the ugliest line of code I have ever written :P mDisplay = reinterpret_cast(StringConverter::parseUnsignedLong(new_display)); screen = StringConverter::parseUnsignedInt(new_screen); parentWindow = StringConverter::parseUnsignedLong(wid); depth = DisplayPlanes(mDisplay, screen); rootWindow = RootWindow(mDisplay, screen); left = top = 0; fullScreen = false; // Can't be full screen if embedded in an app! } opt = miscParams->find("externalWindowHandle"); if(opt != miscParams->end()) // embedding OGRE in already created window { std::vector tokens = StringUtil::split(opt->second, " :"); String new_display = tokens[0]; String new_screen = tokens[1]; String wid = tokens[2]; mDisplay = reinterpret_cast(StringConverter::parseUnsignedLong(new_display)); screen = StringConverter::parseUnsignedInt(new_screen); mWindow = StringConverter::parseUnsignedLong(wid); if(tokens.size() > 3) // external visual was already setup { extVisualHandler = reinterpret_cast(StringConverter::parseUnsignedLong(tokens[3])); } depth = DisplayPlanes(mDisplay, screen); rootWindow = RootWindow(mDisplay, screen); left = top = 0; fullScreen = false; // Can't be full screen if embedded in an app! mTopLevel = false; // Can't be top-level if embedded } } // Check for full screen mode if FSAA was asked for if(!fullScreen && fsaa_samples>0) { LogManager::getSingleton().logMessage("GLXWindow::create -- FSAA only supported in fullscreen mode"); fsaa_samples = 0; } // Disable FSAA for now -- it doesn't work on NVIDIA fsaa_samples = 0; #ifndef NO_XRANDR // Attempt mode switch for fullscreen -- only if RANDR extension is there int dummy; if(fullScreen && ! XQueryExtension(mDisplay, "RANDR", &dummy, &dummy, &dummy)) { LogManager::getSingleton().logMessage("GLXWindow::create -- Could not switch to full screen mode: No XRANDR extension found"); } else if(fullScreen) { // Use Xrandr extension to switch video modes. This is much better than // XVidMode as you can't scroll away from the full-screen applications. XRRScreenConfiguration *config; XRRScreenSize *sizes; Rotation current_rotation; int nsizes; // Get current screen info config = XRRGetScreenInfo(mDisplay, rootWindow); // Get available sizes if(config) sizes = XRRConfigSizes (config, &nsizes); if(config && nsizes > 0) { // Get current size and rotation mOldMode = XRRConfigCurrentConfiguration (config, ¤t_rotation); // Find smallest matching mode int mode = -1; int mode_width = INT_MAX; int mode_height = INT_MAX; for(size_t i=0; i= static_cast(width) && sizes[i].height >= static_cast(height) && sizes[i].width < mode_width && sizes[i].height < mode_height) { mode = i; mode_width = sizes[i].width; mode_height = sizes[i].height; } } if(mode >= 0) { // Finally, set the screen configuration LogManager::getSingleton().logMessage("GLXWindow::create -- Entering full screen mode"); XRRSetScreenConfig(mDisplay, config, rootWindow, mode, current_rotation, CurrentTime); } else { LogManager::getSingleton().logMessage("GLXWindow::create -- Could not switch to full screen mode: No conforming mode was found"); } // Free configuration data XRRFreeScreenConfigInfo(config); } else { LogManager::getSingleton().logMessage("GLXWindow::create -- Could not switch to full screen mode: XRRGetScreenInfo failed"); } } #endif if(extVisualHandler == NULL) // user didn't create visual ( and window ) himself { // Apply some magic algorithm to get the best visual int best_visual = GLXUtils::findBestVisual(mDisplay, screen, fsaa_samples); if(best_visual == -1) { best_visual = GLXUtils::findBestVisual(mDisplay, screen); LogManager::getSingleton().logMessage("GLXWindow::create -- Requested FSAA of "+ StringConverter::toString(fsaa_samples)+" was not acquirable, defaulting to first suitable visual"); } LogManager::getSingleton().logMessage("GLXWindow::create -- Best visual is "+StringConverter::toString(best_visual)); // Get information about this so-called-best visual XVisualInfo templ; int nmatch; templ.visualid = best_visual; mVisualInfo = XGetVisualInfo(mDisplay, VisualIDMask, &templ, &nmatch); mDelVisualInfo = true; if(mVisualInfo==0 || nmatch==0) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "GLXWindow: error choosing visual", "GLXWindow::create"); } XSetWindowAttributes attr; unsigned long mask; attr.background_pixel = 0; attr.border_pixel = 0; attr.colormap = XCreateColormap(mDisplay,rootWindow,mVisualInfo->visual,AllocNone); attr.event_mask = StructureNotifyMask | VisibilityChangeMask; if(fullScreen) { mask = CWBackPixel | CWColormap | CWOverrideRedirect | CWSaveUnder | CWBackingStore | CWEventMask; attr.override_redirect = True; attr.backing_store = NotUseful; attr.save_under = False; // Fullscreen windows are always in the top left origin left = top = 0; } else mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; // Create window on server mWindow = XCreateWindow(mDisplay,parentWindow,left,top,width,height,0,mVisualInfo->depth,InputOutput,mVisualInfo->visual,mask,&attr); mDelWindow = true; if(!mWindow) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "GLXWindow: XCreateWindow failed", "GLXWindow::create"); } // Make sure the window is in normal state XWMHints *wm_hints; if ((wm_hints = XAllocWMHints()) != NULL) { wm_hints->initial_state = NormalState; wm_hints->input = True; wm_hints->flags = StateHint | InputHint; // Check if we can give it an icon if(depth == 24 || depth == 32) { // Woot! The right bit depth, we can load an icon if(GLXUtils::LoadIcon(mDisplay, rootWindow, "GLX_icon.png", &wm_hints->icon_pixmap, &wm_hints->icon_mask)) wm_hints->flags |= IconPixmapHint | IconMaskHint; } } // Set size and location hints XSizeHints *size_hints; if ((size_hints = XAllocSizeHints()) != NULL) { // Otherwise some window managers ignore our position request size_hints->flags = USPosition; } // Make text property from title XTextProperty titleprop; char *lst = (char*)title.c_str(); XStringListToTextProperty((char **)&lst, 1, &titleprop); XSetWMProperties(mDisplay, mWindow, &titleprop, NULL, NULL, 0, size_hints, wm_hints, NULL); // We don't like memory leaks. Free the clientside storage, but not the // pixmaps as they're still being used by the server. XFree(titleprop.value); XFree(wm_hints); XFree(size_hints); // Acquire atom to recognize window close events mAtomDeleteWindow = XInternAtom(mDisplay,"WM_DELETE_WINDOW",False); XSetWMProtocols(mDisplay,mWindow,&mAtomDeleteWindow,1); // Map window unto screen and focus it. XMapWindow(mDisplay,mWindow); // Make sure the server is up to date and focus the window XFlush(mDisplay); //Register only Ogre created windows (users can register their own) WindowEventUtilities::_addRenderWindow(this); } else { LogManager::getSingleton().logMessage("GLXWindow::create -- using external window handle"); mVisualInfo = extVisualHandler; mDelVisualInfo = false; } GLRenderSystem *rs = static_cast(Root::getSingleton().getRenderSystem()); GLXContext* mainContext = static_cast( rs->_getMainContext() ); if ( mainContext == 0 ) { // Finally, create a GL context // we want to share it with main mGlxContext = glXCreateContext(mDisplay,mVisualInfo,NULL,True); } else mGlxContext = glXCreateContext(mDisplay,mVisualInfo,mainContext->mCtx,True); if(!mGlxContext) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "glXCreateContext failed", "GLXWindow::create"); mName = name; mWidth = width; mHeight = height; mIsFullScreen = fullScreen; //Get the attributes of the screen XWindowAttributes temp; XGetWindowAttributes(mDisplay, mWindow, &temp); mLeft = temp.x; mTop = temp.y; // Create OGRE GL context mContext = new GLXContext(mDisplay, mWindow, mGlxContext, mVisualInfo); } //-------------------------------------------------------------------------------------------------// void GLXWindow::destroy(void) { WindowEventUtilities::_removeRenderWindow(this); // Unregister and destroy OGRE GLContext delete mContext; // Destroy GL context if(mGlxContext) glXDestroyContext(mDisplay, mGlxContext); if(mDelWindow && mWindow) XDestroyWindow(mDisplay, mWindow); mContext = 0; mWindow = 0; mGlxContext = 0; mActive = false; mVisible = false; mClosed = true; Root::getSingleton().getRenderSystem()->detachRenderTarget( this->getName() ); } //-------------------------------------------------------------------------------------------------// bool GLXWindow::isActive() const { return mActive; } //-------------------------------------------------------------------------------------------------// bool GLXWindow::isClosed() const { return mClosed; } //-------------------------------------------------------------------------------------------------// bool GLXWindow::isVisible() const { return mVisible; } //-------------------------------------------------------------------------------------------------// void GLXWindow::setVisible(bool visible) { mVisible = visible; } //-------------------------------------------------------------------------------------------------// void GLXWindow::reposition(int left, int top) { XMoveWindow(mDisplay,mWindow,left,top); } //-------------------------------------------------------------------------------------------------// void GLXWindow::resize(unsigned int width, unsigned int height) { // Check if the window size really changed if(mWidth == width && mHeight == height) return; mWidth = width; mHeight = height; if (!mTopLevel) { for (ViewportList::iterator it = mViewportList.begin(); it != mViewportList.end(); ++it) (*it).second->_updateDimensions(); } else { XResizeWindow(mDisplay, mWindow, width, height); /// Ogre handles window } } //-------------------------------------------------------------------------------------------------// void GLXWindow::windowMovedOrResized() { //Get the new attributes of the screen XWindowAttributes temp; XGetWindowAttributes(mDisplay, mWindow, &temp); mLeft = temp.x; mTop = temp.y; //Only update viewport dimensions if they did actually change if (static_cast(mWidth) == temp.width && static_cast(mHeight) == temp.height) return; mWidth = temp.width; mHeight = temp.height; // Notify viewports of resize ViewportList::iterator it, itend; itend = mViewportList.end(); for( it = mViewportList.begin(); it != itend; ++it ) (*it).second->_updateDimensions(); } //-------------------------------------------------------------------------------------------------// void GLXWindow::swapBuffers(bool waitForVSync) { glXSwapBuffers(mDisplay,mWindow); } //-------------------------------------------------------------------------------------------------// void GLXWindow::getCustomAttribute( const String& name, void* pData ) { if( name == "DISPLAY" ) { *static_cast(pData) = mDisplay; return; } else if( name == "ATOM" ) { *static_cast< ::Atom* >(pData) = mAtomDeleteWindow; return; } else if( name == "WINDOW" ) { *static_cast(pData) = mWindow; return; } else if( name == "GLCONTEXT" ) { *static_cast(pData) = mContext; return; } } //-------------------------------------------------------------------------------------------------// void GLXWindow::writeContentsToFile(const String& filename) { ImageCodec::ImageData* imgData = new ImageCodec::ImageData; imgData->width = mWidth; imgData->height = mHeight; imgData->format = PF_BYTE_RGB; // Allocate buffer uchar* pBuffer = new uchar[mWidth * mHeight * 3]; // Read pixels // I love GL: it does all the locking & colour conversion for us glReadPixels(0,0, mWidth, mHeight, GL_RGB, GL_UNSIGNED_BYTE, pBuffer); // Wrap buffer in a memory stream DataStreamPtr stream(new MemoryDataStream(pBuffer, mWidth * mHeight * 3, false)); // Need to flip the read data over in Y though Image img; img.loadRawData(stream, mWidth, mHeight, PF_BYTE_RGB ); img.flipAroundX(); MemoryDataStreamPtr streamFlipped(new MemoryDataStream(img.getData(), stream->size(), false)); // Get codec size_t pos = filename.find_last_of("."); String extension; if( pos == String::npos ) OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, "Unable to determine image type for '" + filename + "' - invalid extension.", "SDLWindow::writeContentsToFile" ); while( pos != filename.length() - 1 ) extension += filename[++pos]; // Get the codec Codec * pCodec = Codec::getCodec(extension); // Write out Codec::CodecDataPtr codecDataPtr(imgData); pCodec->codeToFile(streamFlipped, filename, codecDataPtr); delete [] pBuffer; } }