/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * 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. */ /* Note, elements of GraphicsWindowX11 have used Prodcer/RenderSurface_X11.cpp as both * a guide to use of X11/GLX and copiying directly in the case of setBorder(). * These elements are license under OSGPL as above, with Copyright (C) 2001-2004 Don Burns. */ #include #include #include #include #include #include #include #include #include /* For CARD16 */ #include using namespace osgViewer; namespace osgViewer { class GraphicsContextX11 : public osg::GraphicsContext { public: GraphicsContextX11(osg::GraphicsContext::Traits* traits): _valid(false), _display(0), _parent(0), _window(0) { _traits = traits; } virtual bool valid() const { return _valid; } /** Realise the GraphicsContext implementation, * Pure virtual - must be implemented by concrate implementations of GraphicsContext. */ virtual bool realizeImplementation() { osg::notify(osg::NOTICE)<<"GraphicsWindow::realizeImplementation() not implemented."<second; } protected: typedef std::map KeyMap; KeyMap _keymap; }; static int remapX11Key(int key) { static X11KeyboardMap s_x11KeyboardMap; return s_x11KeyboardMap.remapKey(key); } GraphicsWindowX11::~GraphicsWindowX11() { close(true); } Display* GraphicsWindowX11::getDisplayToUse() const { if (_threadOfLastMakeCurrent==0) { return _display; } if (OpenThreads::Thread::CurrentThread()==_threadOfLastMakeCurrent) { return _display; } else { return _eventDisplay; } } bool GraphicsWindowX11::createVisualInfo() { typedef std::vector Attributes; Attributes attributes; attributes.push_back(GLX_USE_GL); attributes.push_back(GLX_RGBA); if (_traits->doubleBuffer) attributes.push_back(GLX_DOUBLEBUFFER); if (_traits->quadBufferStereo) attributes.push_back(GLX_STEREO); attributes.push_back(GLX_RED_SIZE); attributes.push_back(_traits->red); attributes.push_back(GLX_GREEN_SIZE); attributes.push_back(_traits->green); attributes.push_back(GLX_BLUE_SIZE); attributes.push_back(_traits->blue); attributes.push_back(GLX_DEPTH_SIZE); attributes.push_back(_traits->depth); if (_traits->alpha) { attributes.push_back(GLX_ALPHA_SIZE); attributes.push_back(_traits->alpha); } if (_traits->stencil) { attributes.push_back(GLX_STENCIL_SIZE); attributes.push_back(_traits->stencil); } #if defined(GLX_SAMPLE_BUFFERS) && defined (GLX_SAMPLES) if (_traits->sampleBuffers) { attributes.push_back(GLX_SAMPLE_BUFFERS); attributes.push_back(_traits->sampleBuffers); } if (_traits->sampleBuffers) { attributes.push_back(GLX_SAMPLES); attributes.push_back(_traits->samples); } #endif // TODO // GLX_AUX_BUFFERS // GLX_ACCUM_RED_SIZE // GLX_ACCUM_GREEN_SIZE // GLX_SAMPLE_BUFFERS // GLX_SAMPLES attributes.push_back(None); _visualInfo = glXChooseVisual( _display, _traits->screenNum, &(attributes.front()) ); return _visualInfo != 0; } bool GraphicsWindowX11::setWindowDecorationImplementation(bool flag) { Display* display = getDisplayToUse(); Atom atom; if( (atom = XInternAtom( display, "_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(display, _window ); XChangeProperty( display, _window, atom, atom, 32, PropModeReplace, (unsigned char *)&wmHints, 5 ); XMapWindow(display, _window ); XFlush(display); XSync(display,0); #if 0 // now update the window dimensions to account for any size changes made by the window manager, XGetWindowAttributes( display, _window, &watt ); _traits->width = watt.width; _traits->height = watt.height; #endif // add usleep here to give window manager a chance to handle the request, if // we don't add this sleep then any X11 calls right afterwards can produce // X11 errors. usleep(100000); return true; } else { osg::notify(osg::NOTICE)<<"Error: GraphicsWindowX11::setBorder(" << flag << ") - couldn't change decorations." << std::endl; return false; } } bool GraphicsWindowX11::setWindowRectangleImplementation(int x, int y, int width, int height) { if (!_realized) return false; Display* display = getDisplayToUse(); XMoveResizeWindow(display, _window, x, y, width, height); XFlush(display); XSync(display, 0); // add usleep here to give window manager a chance to handle the request, if // we don't add this sleep then any X11 calls right afterwards can produce // X11 errors. usleep(100000); return true; } void GraphicsWindowX11::setCursor(MouseCursor mouseCursor) { Cursor newCursor = getOrCreateCursor(mouseCursor); if (newCursor == _currentCursor) return; _currentCursor = newCursor; if (!_window) return; Display* display = getDisplayToUse(); if (!display) return; XDefineCursor( display, _window, _currentCursor ); XFlush(display); XSync(display, 0); _traits->useCursor = (_currentCursor != getOrCreateCursor(NoCursor)); } Cursor GraphicsWindowX11::getOrCreateCursor(MouseCursor mouseCursor) { std::map::iterator i = _mouseCursorMap.find(mouseCursor); if (i != _mouseCursorMap.end()) return i->second; Display* display = getDisplayToUse(); if (!display) return None; switch (mouseCursor) { case NoCursor: { // create an empty mouse cursor, note that it is safe to destroy the Pixmap just past cursor creation // since the resource in the x server is reference counted. char buff[2] = {0,0}; XColor ncol = {0,0,0,0,DoRed|DoGreen|DoBlue,0}; Pixmap pixmap = XCreateBitmapFromData( display, _parent, buff, 1, 1); _mouseCursorMap[mouseCursor] = XCreatePixmapCursor( display, pixmap, pixmap, &ncol, &ncol, 0, 0 ); XFreePixmap(display, pixmap); // Important to have the pixmap and the buffer still available when the request is sent to the server ... XFlush(display); XSync(display, 0); break; } case RightArrowCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_left_ptr ); break; case LeftArrowCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_top_left_arrow ); break; case InfoCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_hand1 ); break; case DestroyCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_pirate ); break; case HelpCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_question_arrow ); break; case CycleCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_exchange ); break; case SprayCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_spraycan ); break; case WaitCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_watch ); break; case TextCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_xterm ); break; case CrosshairCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_crosshair ); break; case UpDownCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_sb_v_double_arrow ); break; case LeftRightCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_sb_h_double_arrow ); break; case TopSideCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_top_side ); break; case BottomSideCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_bottom_side ); break; case LeftSideCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_left_side ); break; case RightSideCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_right_side ); break; case TopLeftCorner: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_top_left_corner ); break; case TopRightCorner: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_top_right_corner ); break; case BottomRightCorner: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_bottom_right_corner ); break; case BottomLeftCorner: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_bottom_left_corner ); break; case InheritCursor: default: _mouseCursorMap[mouseCursor] = None; break; }; return _mouseCursorMap[mouseCursor]; } void GraphicsWindowX11::init() { if (_initialized) return; if (!_traits) { _valid = false; return; } WindowData* inheritedWindowData = dynamic_cast(_traits->inheritedWindowData.get()); _display = XOpenDisplay(_traits->displayName().c_str()); unsigned int screen = _traits->screenNum; if (!_display) { osg::notify(osg::NOTICE)<<"Error: Unable to open display \"" << XDisplayName(_traits->displayName().c_str()) << "\"."<displayName().c_str()) <<" has no GLX extension." << std::endl; XCloseDisplay( _display ); _display = 0; _valid = false; return; } // osg::notify(osg::NOTICE)<<"GLX extension, errorBase="<(_traits->sharedContext); if (sharedContextX11) { _glxContext = glXCreateContext( _display, _visualInfo, sharedContextX11->getGLXContext(), True ); } else { _glxContext = glXCreateContext( _display, _visualInfo, NULL, True ); } if (!_glxContext) { osg::notify(osg::NOTICE)<<"Error: Unable to create OpenGL graphics context."<displayName().c_str()); _parent = RootWindow( _display, screen ); XWindowAttributes watt; XGetWindowAttributes( _display, _parent, &watt ); // unsigned int parentWindowHeight = watt.height; XSetWindowAttributes swatt; swatt.colormap = XCreateColormap( _display, _parent, _visualInfo->visual, AllocNone); //swatt.colormap = DefaultColormap( _dpy, 10 ); swatt.background_pixel = 0; swatt.border_pixel = 0; swatt.event_mask = 0; unsigned long mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap; bool overrideRedirect = false; if (overrideRedirect) { swatt.override_redirect = true; mask |= CWOverrideRedirect; } _window = XCreateWindow( _display, _parent, _traits->x, _traits->y, _traits->width, _traits->height, 0, _visualInfo->depth, InputOutput, _visualInfo->visual, mask, &swatt ); if (!_window) { osg::notify(osg::NOTICE)<<"Error: Unable to create Window."<x; sh.y = _traits->y; sh.width = _traits->width; sh.height = _traits->height; XSetStandardProperties( _display, _window, _traits->windowName.c_str(), _traits->windowName.c_str(), None, 0, 0, &sh); setWindowDecoration(_traits->windowDecoration); useCursor(_traits->useCursor); _deleteWindow = XInternAtom (_display, "WM_DELETE_WINDOW", False); XSetWMProtocols(_display, _window, &_deleteWindow, 1); XFlush( _display ); XSync( _display, 0 ); // now update the window dimensions to account for any size changes made by the window manager, XGetWindowAttributes( _display, _window, &watt ); if (_traits->width != watt.width && _traits->height != watt.height) { resized( _traits->x, _traits->y, _traits->width, _traits->height ); } //osg::notify(osg::NOTICE)<<"After sync apply.x = "<(ev.xclient.data.l[0]) == _deleteWindow) { osg::notify(osg::INFO)<<"DeleteWindow event recieved"<closeWindow(); } } } } } void GraphicsWindowX11::checkEvents() { if (!_realized) return; Display* display = _eventDisplay; double baseTime = _timeOfLastCheckEvents; double eventTime = baseTime; double resizeTime = eventTime; _timeOfLastCheckEvents = getEventQueue()->getTime(); int windowX = _traits->x; int windowY = _traits->y; int windowWidth = _traits->width; int windowHeight = _traits->height; bool destroyWindowRequested = false; Time firstEventTime = 0; // osg::notify(osg::NOTICE)<<"Check events"<(ev.xclient.data.l[0]) == _deleteWindow) { osg::notify(osg::NOTICE)<<"DeleteWindow event recieved"<closeWindow(eventTime); } } case Expose : osg::notify(osg::INFO)<<"Expose x="<x || windowY != _traits->y || windowWidth != _traits->width || windowHeight != _traits->height) { resized(windowX, windowY, windowWidth, windowHeight); getEventQueue()->windowResize(windowX, windowY, windowWidth, windowHeight, resizeTime); } #if 0 if (destroyWindowRequested) { close(); } #endif } void GraphicsWindowX11::grabFocus() { Display* display = getDisplayToUse(); XSetInputFocus( display, _window, RevertToNone, CurrentTime ); XFlush(display); XSync(display,0); } void GraphicsWindowX11::grabFocusIfPointerInWindow() { Window win, root; int wx, wy, rx, ry; unsigned int buttons; Display* display = getDisplayToUse(); if( XQueryPointer( display, _window, &root, &win, &rx, &ry, &wx, &wy, &buttons)) { #if 0 if (wx>=0 && wx<_traits->width && wy>=0 && wy<_traits->height) { grabFocus(); } #else grabFocus(); #endif } } void GraphicsWindowX11::transformMouseXY(float& x, float& y) { if (getEventQueue()->getUseFixedMouseInputRange()) { osgGA::GUIEventAdapter* eventState = getEventQueue()->getCurrentEventState(); x = eventState->getXmin() + (eventState->getXmax()-eventState->getXmin())*x/float(_traits->width); y = eventState->getYmin() + (eventState->getYmax()-eventState->getYmin())*y/float(_traits->height); } } void GraphicsWindowX11::adaptKey(XKeyEvent& keyevent, int& keySymbol, unsigned int& modifierMask) { Display* display = _eventDisplay; static XComposeStatus state; unsigned char keybuf[32]; XLookupString( &keyevent, (char *)keybuf, sizeof(keybuf), NULL, &state ); modifierMask = 0; if( keyevent.state & ShiftMask ) { modifierMask |= osgGA::GUIEventAdapter::MODKEY_SHIFT; } if( keyevent.state & LockMask ) { modifierMask |= osgGA::GUIEventAdapter::MODKEY_CAPS_LOCK; } if( keyevent.state & ControlMask ) { modifierMask |= osgGA::GUIEventAdapter::MODKEY_CTRL; } if( keyevent.state & Mod1Mask ) { modifierMask |= osgGA::GUIEventAdapter::MODKEY_ALT; } if( keyevent.state & Mod2Mask ) { modifierMask |= osgGA::GUIEventAdapter::MODKEY_NUM_LOCK; } if( keyevent.state & Mod4Mask ) { modifierMask |= osgGA::GUIEventAdapter::MODKEY_META; } keySymbol = keybuf[0]; KeySym ks = XKeycodeToKeysym( display, keyevent.keycode, 0 ); int remappedKey = remapX11Key(ks); if (remappedKey & 0xff00) { // special keyboard character keySymbol = remappedKey; } else { // normal ascii key keySymbol = keybuf[0]; } } void GraphicsWindowX11::requestWarpPointer(float x,float y) { Display* display = getDisplayToUse(); XWarpPointer( display, None, _window, 0, 0, 0, 0, static_cast(x), static_cast(y) ); XFlush(display); XSync(display, 0); getEventQueue()->mouseWarped(x,y); } extern "C" { int X11ErrorHandling(Display* display, XErrorEvent* event) { osg::notify(osg::NOTICE)<<"Got an X11ErrorHandling call display="<request_code << std::endl; osg::notify(osg::NOTICE) << "Minor opcode: " << (int)event->minor_code << std::endl; osg::notify(osg::NOTICE) << "Error code: " << (int)event->error_code << std::endl; osg::notify(osg::NOTICE) << "Request serial: " << event->serial << std::endl; osg::notify(osg::NOTICE) << "Current serial: " << NextRequest( display ) - 1 << std::endl; switch( event->error_code ) { case BadValue: osg::notify(osg::NOTICE) << " Value: " << event->resourceid << std::endl; break; case BadAtom: osg::notify(osg::NOTICE) << " AtomID: " << event->resourceid << std::endl; break; default: osg::notify(osg::NOTICE) << " ResourceID: " << event->resourceid << std::endl; break; } return 0; } } struct X11WindowingSystemInterface : public osg::GraphicsContext::WindowingSystemInterface { X11WindowingSystemInterface() { osg::notify(osg::INFO)<<"X11WindowingSystemInterface()"<pbuffer) { osg::ref_ptr pbuffer = new GraphicsContextX11(traits); if (pbuffer->valid()) return pbuffer.release(); else return 0; } else { osg::ref_ptr window = new GraphicsWindowX11(traits); if (window->valid()) return window.release(); else return 0; } } }; struct RegisterWindowingSystemInterfaceProxy { RegisterWindowingSystemInterfaceProxy() { osg::notify(osg::INFO)<<"RegisterWindowingSystemInterfaceProxy()"<