/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2005 The OGRE Team 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. ----------------------------------------------------------------------------- */ #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 #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), mClosed(false),mFullScreen(false), mOldMode(-1), mContext(0) { mActive = false; } GLXWindow::~GLXWindow() { if(mGlxContext) glXDestroyContext(mDisplay, mGlxContext); if(mWindow) XDestroyWindow(mDisplay, mWindow); #ifndef NO_XRANDR if(mFullScreen) { // 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! mTopLevel = false; // Can't be top-level if embedded } 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= width && sizes[i].height >= 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 XVisualInfo* visualInfo = NULL; 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; visualInfo = XGetVisualInfo(mDisplay, VisualIDMask, &templ, &nmatch); if(visualInfo==0 || nmatch==0) { OGRE_EXCEPT(999, "GLXWindow: error choosing visual", "GLXWindow::create"); } XSetWindowAttributes attr; unsigned long mask; attr.background_pixel = 0; attr.border_pixel = 0; attr.colormap = XCreateColormap(mDisplay,rootWindow,visualInfo->visual,AllocNone); attr.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask; 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,visualInfo->depth,InputOutput,visualInfo->visual,mask,&attr); if(!mWindow) { OGRE_EXCEPT(999, "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); } else { LogManager::getSingleton().logMessage("GLXWindow::create -- using external window handle"); visualInfo = extVisualHandler; } 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,visualInfo,NULL,True); } else mGlxContext = glXCreateContext(mDisplay,visualInfo,mainContext->mCtx,True); if(!mGlxContext) { OGRE_EXCEPT(999, "glXCreateContext failed", "GLXWindow::create"); } // Free visual info if (extVisualHandler == NULL) XFree(visualInfo); mName = name; mWidth = width; mHeight = height; mFullScreen = fullScreen; // Create OGRE GL context mContext = new GLXContext(mDisplay, mWindow, mGlxContext); // Register the context with the rendersystem and associate it with this window rs->_registerContext(this, mContext); if ( rs->_getMainContext() == 0 ) glXMakeCurrent(mDisplay,mWindow,mGlxContext); } void GLXWindow::destroy(void) { // Unregister and destroy OGRE GLContext GLRenderSystem *rs = static_cast(Root::getSingleton().getRenderSystem()); rs->_unregisterContext(this); delete mContext; // Destroy GL context if(mGlxContext) glXDestroyContext(mDisplay, mGlxContext); if(mWindow) XDestroyWindow(mDisplay, mWindow); mWindow = 0; mGlxContext = 0; mActive = false; Root::getSingleton().getRenderSystem()->detachRenderTarget( this->getName() ); } bool GLXWindow::isActive() const { return mActive; } bool GLXWindow::isClosed() const { return mClosed; } void GLXWindow::reposition(int left, int top) { XMoveWindow(mDisplay,mWindow,left,top); } void GLXWindow::resize(unsigned int width, unsigned int height) { if (!mTopLevel) /// Embedded resized(width, height); else /// Ogre handles window XResizeWindow(mDisplay,mWindow,width,height); } void GLXWindow::swapBuffers(bool waitForVSync) { glXSwapBuffers(mDisplay,mWindow); } void GLXWindow::processEvent(const XEvent &event) { // Process only events for this window switch(event.type) { case ClientMessage: if(event.xclient.display != mDisplay || event.xclient.window != mWindow) // Not for me break; if(event.xclient.format == 32 && event.xclient.data.l[0] == (long)mAtomDeleteWindow) { // Window deleted -- oops, this does not work, ogre doesn't register the close //mClosed = true; //mActive = false; //Root::getSingleton().getRenderSystem()->detachRenderTarget( this->getName() ); } break; case ConfigureNotify: if(event.xconfigure.display != mDisplay || event.xconfigure.window != mWindow) // Not for me break; resized(event.xconfigure.width, event.xconfigure.height); break; case MapNotify: if(event.xconfigure.display != mDisplay || event.xconfigure.window != mWindow) // Not for me break; // Window was mapped to the screen exposed(true); break; case UnmapNotify: if(event.xconfigure.display != mDisplay || event.xconfigure.window != mWindow) // Not for me break; // Window was unmapped from the screen (user switched // to another workspace, for example) exposed(false); break; } } void GLXWindow::exposed(bool active) { mActive = active; } void GLXWindow::resized(size_t width, size_t height) { // Check if the window size really changed if(mWidth == width && mHeight == height) return; mWidth = width; mHeight = height; for (ViewportList::iterator it = mViewportList.begin(); it != mViewportList.end(); ++it) { (*it).second->_updateDimensions(); } } /// Pure virtual destructor must be defined GLXWindowInterface::~GLXWindowInterface() { } void GLXWindow::getCustomAttribute( const String& name, void* pData ) { if( name == "GLXWINDOW" ) { *static_cast(pData) = mWindow; return; } else if( name == "GLXDISPLAY" ) { *static_cast(pData) = mDisplay; return; } else if( name == "GLXWINDOWINTERFACE" ) { *static_cast(pData) = this; return; } RenderWindow::getCustomAttribute(name, pData); } 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-1, mHeight-1, 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; } }