/* -*-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 copying directly in the case of setBorder(). * These elements are licensed under OSGPL as above, with Copyright (C) 2001-2004 Don Burns. */ // TODO: // implement http://www.opengl.org/registry/specs/OML/glx_swap_method.txt #include #include #include #include #include #include #include #include #include #include /* For CARD16 */ #ifdef OSGVIEWER_USE_XRANDR #include #endif #include using namespace osgViewer; #ifdef OSG_USE_EGL bool checkEGLError(const char* str) { EGLint err = eglGetError(); if (err != EGL_SUCCESS) { OSG_WARN<<"Warning: "<second; itr = _standardKeymap.find(key); if (itr != _standardKeymap.end()) return itr->second; return key; } bool remapExtendedKey(int& key) { KeyMap::iterator itr = _extendedKeymap.find(key); if (itr != _extendedKeymap.end()) { key = itr->second; return true; } else return false; } protected: typedef std::map KeyMap; KeyMap _extendedKeymap; KeyMap _standardKeymap; }; static bool remapExtendedX11Key(int& key) { static X11KeyboardMap s_x11KeyboardMap; return s_x11KeyboardMap.remapExtendedKey(key); } // Functions to handle key maps of type char[32] as contained in // an XKeymapEvent or returned by XQueryKeymap(). static inline bool keyMapGetKey(const char* map, unsigned int key) { return (map[(key & 0xff) / 8] & (1 << (key & 7))) != 0; } static inline void keyMapSetKey(char* map, unsigned int key) { map[(key & 0xff) / 8] |= (1 << (key & 7)); } static inline void keyMapClearKey(char* map, unsigned int key) { map[(key & 0xff) / 8] &= ~(1 << (key & 7)); } 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() { if (_visualInfo) { #ifdef OSG_USE_EGL delete _visualInfo; #else XFree(_visualInfo); #endif _visualInfo = 0; } if( _window != 0 ) { XWindowAttributes watt; XGetWindowAttributes( _display, _window, &watt ); XVisualInfo temp; temp.visualid = XVisualIDFromVisual(watt.visual); int n; _visualInfo = XGetVisualInfo( _display, VisualIDMask, &temp, &n ); } else { #ifdef OSG_USE_EGL _visualInfo = new XVisualInfo; int depth = DefaultDepth( _display, _traits->screenNum ); if (XMatchVisualInfo( _display, _traits->screenNum, depth, TrueColor, _visualInfo )==0) { OSG_NOTICE<<"GraphicsWindowX11::createVisualInfo() failed."< 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->samples) { attributes.push_back(GLX_SAMPLES); attributes.push_back(_traits->samples); } #endif // TODO // GLX_AUX_BUFFERS // GLX_ACCUM_RED_SIZE // GLX_ACCUM_GREEN_SIZE attributes.push_back(None); _visualInfo = glXChooseVisual( _display, _traits->screenNum, &(attributes.front()) ); #endif } return _visualInfo != 0; } bool GraphicsWindowX11::checkAndSendEventFullScreenIfNeeded(Display* display, int x, int y, int width, int height, bool windowDecoration) { osg::GraphicsContext::WindowingSystemInterface *wsi = osg::GraphicsContext::getWindowingSystemInterface(); if (wsi == NULL) { OSG_NOTICE << "Error, no WindowSystemInterface available, cannot toggle window fullscreen." << std::endl; return false; } unsigned int screenWidth; unsigned int screenHeight; wsi->getScreenResolution(*_traits, screenWidth, screenHeight); bool isFullScreen = x == 0 && y == 0 && width == (int)screenWidth && height == (int)screenHeight && !windowDecoration; if (isFullScreen) { resized(x, y, width, height); getEventQueue()->windowResize(x, y, width, height, getEventQueue()->getTime()); } Atom netWMStateAtom = XInternAtom(display, "_NET_WM_STATE", True); Atom netWMStateFullscreenAtom = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", True); if (netWMStateAtom != None && netWMStateFullscreenAtom != None) { XEvent xev; xev.xclient.type = ClientMessage; xev.xclient.serial = 0; xev.xclient.send_event = True; xev.xclient.window = _window; xev.xclient.message_type = netWMStateAtom; xev.xclient.format = 32; xev.xclient.data.l[0] = isFullScreen ? 1 : 0; xev.xclient.data.l[1] = netWMStateFullscreenAtom; xev.xclient.data.l[2] = 0; XSendEvent(display, RootWindow(display, DefaultScreen(display)), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); return true; } return false; } #define MWM_HINTS_FUNCTIONS (1L << 0) #define MWM_HINTS_DECORATIONS (1L << 1) #define MWM_HINTS_INPUT_MODE (1L << 2) #define MWM_HINTS_STATUS (1L << 3) #define MWM_DECOR_ALL (1L<<0) #define MWM_DECOR_BORDER (1L<<1) #define MWM_DECOR_RESIZEH (1L<<2) #define MWM_DECOR_TITLE (1L<<3) #define MWM_DECOR_MENU (1L<<4) #define MWM_DECOR_MINIMIZE (1L<<5) #define MWM_DECOR_MAXIMIZE (1L<<6) #define MWM_FUNC_ALL (1L<<0) #define MWM_FUNC_RESIZE (1L<<1) #define MWM_FUNC_MOVE (1L<<2) #define MWM_FUNC_MINIMIZE (1L<<3) #define MWM_FUNC_MAXIMIZE (1L<<4) #define MWM_FUNC_CLOSE (1L<<5) bool GraphicsWindowX11::setWindowDecorationImplementation(bool flag) { Display* display = getDisplayToUse(); XMapWindow(display, _window ); checkAndSendEventFullScreenIfNeeded(display, _traits->x, _traits->y, _traits->width, _traits->height, flag); struct { unsigned long flags; unsigned long functions; unsigned long decorations; long inputMode; unsigned long status; } wmHints; Atom atom; bool result = false; if( (atom = XInternAtom( display, "_MOTIF_WM_HINTS", 0 )) != None ) { if (flag) { wmHints.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS; wmHints.functions = MWM_FUNC_ALL; wmHints.decorations = MWM_DECOR_ALL; wmHints.inputMode = 0; wmHints.status = 0; // if traits says not resize we want to set the functions to exlude MWM_FUNC_RESIZE, // but this bitmask needs to be set if the MWM_FUNC_ALL bit is already set in order to toggle it off. if (_traits.valid() && !_traits->supportsResize) wmHints.functions = wmHints.functions | MWM_FUNC_RESIZE; } else { wmHints.flags = MWM_HINTS_DECORATIONS; wmHints.functions = 0; wmHints.decorations = 0; wmHints.inputMode = 0; wmHints.status = 0; } XChangeProperty( display, _window, atom, atom, 32, PropModeReplace, (unsigned char *)&wmHints, 5 ); result = true; } else { OSG_NOTICE<<"Error: GraphicsWindowX11::setWindowDecorationImplementation(" << flag << ") - couldn't change decorations." << std::endl; result = false; } 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 result; } bool GraphicsWindowX11::setWindowRectangleImplementation(int x, int y, int width, int height) { if (!_initialized) return false; Display* display = getDisplayToUse(); checkAndSendEventFullScreenIfNeeded(display, x, y, width, height, _traits->windowDecoration); 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::setWindowName(const std::string& name) { if( _window == 0) return; // char *slist[] = { name.c_str(), 0L }; // XTextProperty xtp; // XStringListToTextProperty( slist, 1, &xtp ); Display* display = getDisplayToUse(); if( !display ) return; // XSetWMName( display, _window, &xtp ); XStoreName( display, _window, name.c_str() ); XSetIconName( display, _window, name.c_str() ); XFlush(display); XSync(display,0); _traits->windowName = name; } 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 HandCursor: _mouseCursorMap[mouseCursor] = XCreateFontCursor( display, XC_hand1 ); break; case InheritCursor: default: _mouseCursorMap[mouseCursor] = None; break; }; return _mouseCursorMap[mouseCursor]; } void GraphicsWindowX11::init() { if (_initialized) return; if (!_traits) { _valid = false; return; } // getEventQueue()->setCurrentEventState(osgGA::GUIEventAdapter::getAccumulatedEventState().get()); WindowData* inheritedWindowData = dynamic_cast(_traits->inheritedWindowData.get()); Window windowHandle = inheritedWindowData ? inheritedWindowData->_window : 0; _ownsWindow = windowHandle == 0; _display = XOpenDisplay(_traits->displayName().c_str()); if (!_display) { 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; } #endif // OSG_NOTICE<<"GLX extension, errorBase="<(_traits->sharedContext.get()); Context sharedContext = graphicsHandleX11 ? graphicsHandleX11->getContext() : 0; #ifdef OSG_USE_EGL _valid = _ownsWindow ? createWindow() : setWindow(windowHandle); if (!_valid) { XCloseDisplay( _display ); _display = 0; return; } OSG_NOTICE<<"GraphicsWindowX11::init() - window created ="<<_valid< Attributes; Attributes attributes; attributes.push_back(EGL_RED_SIZE); attributes.push_back(_traits->red); attributes.push_back(EGL_GREEN_SIZE); attributes.push_back(_traits->green); attributes.push_back(EGL_BLUE_SIZE); attributes.push_back(_traits->blue); attributes.push_back(EGL_DEPTH_SIZE); attributes.push_back(_traits->depth); if (_traits->alpha) { attributes.push_back(EGL_ALPHA_SIZE); attributes.push_back(_traits->alpha); } if (_traits->stencil) { attributes.push_back(EGL_STENCIL_SIZE); attributes.push_back(_traits->stencil); } if (_traits->sampleBuffers) { attributes.push_back(EGL_SAMPLE_BUFFERS); attributes.push_back(_traits->sampleBuffers); } if (_traits->samples) { attributes.push_back(EGL_SAMPLES); attributes.push_back(_traits->samples); } attributes.push_back(EGL_RENDERABLE_TYPE); attributes.push_back(OSG_EGL_OPENGL_TARGET_BIT); attributes.push_back(EGL_NONE); attributes.push_back(EGL_NONE); int numConfigs; if (!eglChooseConfig(_eglDisplay, &(attributes.front()), &eglConfig, 1, &numConfigs) || (numConfigs != 1)) { OSG_NOTICE<<"GraphicsWindowX11::init() - eglChooseConfig() failed."<syncWindowRectangleWithGraphcisContext(); } bool GraphicsWindowX11::createWindow() { unsigned int screen = _traits->screenNum; _eventDisplay = XOpenDisplay(_traits->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; if (_traits->overrideRedirect) { swatt.override_redirect = true; mask |= CWOverrideRedirect; OSG_INFO<<"Setting override redirect"<x; int y = _traits->y; int width = _traits->width; int height = _traits->height; unsigned int screenWidth; unsigned int screenHeight; wsi->getScreenResolution(*_traits, screenWidth, screenHeight); bool doFullSceenWorkAround = false; bool isFullScreen = x == 0 && y == 0 && width == (int)screenWidth && height == (int)screenHeight && !_traits->windowDecoration; if (isFullScreen && !_traits->overrideRedirect) { // follows is hack to get around problems with toggling off full screen with modern X11 window // managers that try to be too clever when toggling off full screen and ignore the window size // calls made by the OSG when the initial window size is full screen. Atom netWMStateAtom = XInternAtom(_display, "_NET_WM_STATE", True); Atom netWMStateFullscreenAtom = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", True); // we have a modern X11 server so assume we need the do the full screen hack. if (netWMStateAtom != None && netWMStateFullscreenAtom != None) { // artifically reduce the initial window size so that the windowing // system has a size to go back to when toggling off full screen, // we don't have to worry about the window being initially smaller as the // setWindowDecoration(..) implementation with enable full screen for us x = width/4; y = height/4; width /= 2; height /= 2; doFullSceenWorkAround = true; } } _window = XCreateWindow( _display, _parent, x, y, width, height, 0, _visualInfo->depth, InputOutput, _visualInfo->visual, mask, &swatt ); if (!_window) { 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->x != watt.x || _traits->y != watt.y ||_traits->width != watt.width || _traits->height != watt.height) { if (doFullSceenWorkAround) { OSG_INFO<<"Full Screen failed, resizing manually"<x, _traits->y, _traits->width, _traits->height); XFlush(_display); XSync(_display, 0); XGetWindowAttributes( _display, _window, &watt ); } resized( watt.x, watt.y, watt.width, watt.height ); } //OSG_NOTICE<<"After sync apply.x = "<x = watt.x; _traits->y = watt.y; _traits->width = watt.width; _traits->height = watt.height; _parent = DefaultRootWindow( _display ); //_traits->supportsResize = false; _traits->windowDecoration = false; if (_traits->windowName.size()) setWindowName(_traits->windowName); _eventDisplay = XOpenDisplay(_traits->displayName().c_str()); XFlush( _eventDisplay ); XSync( _eventDisplay, 0 ); return true; } bool GraphicsWindowX11::realizeImplementation() { if (_realized) { OSG_NOTICE<<"GraphicsWindowX11::realizeImplementation() Already realized"<syncWindowRectangleWithGraphcisContext(); // Window temp = _window; // XSetWMColormapWindows( _display, _window, &temp, 1); _realized = true; return true; } bool GraphicsWindowX11::makeCurrentImplementation() { if (!_realized) { OSG_NOTICE<<"Warning: GraphicsWindow not realized, cannot do makeCurrent."<vsync) { unsigned int counter; glXGetVideoSyncSGI(&counter); glXWaitVideoSyncSGI(1, 0, &counter); } #endif glXSwapBuffers( _display, _window ); #endif while( XPending(_display) ) { XEvent ev; XNextEvent( _display, &ev ); switch( ev.type ) { case ClientMessage: { if (static_cast(ev.xclient.data.l[0]) == _deleteWindow) { OSG_INFO<<"DeleteWindow event received"<closeWindow(); } } } } } void GraphicsWindowX11::checkEvents() { if (!_realized) return; Display* display = _eventDisplay; double baseTime = _timeOfLastCheckEvents; double eventTime = baseTime; double resizeTime = eventTime; _timeOfLastCheckEvents = getEventQueue()->getTime(); if (baseTime>_timeOfLastCheckEvents) baseTime = _timeOfLastCheckEvents; // OSG_NOTICE<<"GraphicsWindowX11::checkEvents() : getEventQueue()->getCurrentEventState()->getGraphicsContext()="<getCurrentEventState()->getGraphicsContext()<x; int windowY = _traits->y; int windowWidth = _traits->width; int windowHeight = _traits->height; Time firstEventTime = 0; // OSG_NOTICE<<"Check events"<(ev.xclient.data.l[0]) == _deleteWindow) { OSG_NOTICE<<"DeleteWindow event received"<closeWindow(eventTime); } break; } case Expose : OSG_INFO<<"Expose x="<(relativeTime)*0.001; int wx, wy; Window win = 0L; if( ev.xmotion.same_screen ) { wx = ev.xmotion.x; wy = ev.xmotion.y; } else { // the mouse in on another screen so need to compute the // coordinates of the mouse position relative to an absolute position // then take away the position of the original window/screen to get // the coordinates relative to the original position. Window root; int rx, ry; unsigned int buttons; int screenOrigin_x = 0; int screenOrigin_y = 0; int i; for(i= 0; i < ScreenCount(display); i++ ) { if( XQueryPointer( display, RootWindow(display, i), &root, &win, &rx, &ry, &wx, &wy, &buttons) ) { break; } screenOrigin_x += DisplayWidth(display, i); } for(i= 0; i < static_cast(_traits->screenNum); i++ ) { screenOrigin_x -= DisplayWidth(display, i); } int dest_x_return, dest_y_return; Window child_return; XTranslateCoordinates(display, _window, _parent, 0, 0, &dest_x_return, &dest_y_return, &child_return); wx += (screenOrigin_x - dest_x_return); wy += (screenOrigin_y - dest_y_return); } float mx = wx; float my = wy; transformMouseXY(mx, my); getEventQueue()->mouseMotion(mx, my, eventTime); // OSG_NOTICE<<"MotionNotify wx="<x || windowY != _traits->y || windowWidth != _traits->width || windowHeight != _traits->height) { resized(windowX, windowY, windowWidth, windowHeight); getEventQueue()->windowResize(windowX, windowY, windowWidth, windowHeight, resizeTime); // request window repaint if window size was changed if (windowWidth != _traits->width || windowHeight != _traits->height) { requestRedraw(); } } } 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, int& unmodifiedKeySymbol) { unsigned char buffer_return[32]; int bytes_buffer = 32; KeySym keysym_return; int numChars = XLookupString(&keyevent, reinterpret_cast(buffer_return), bytes_buffer, &keysym_return, NULL); keySymbol = keysym_return; if (!remapExtendedX11Key(keySymbol) && (numChars==1)) { keySymbol = buffer_return[0]; } unmodifiedKeySymbol = XkbKeycodeToKeysym(keyevent.display, keyevent.keycode, 0, 0); } // Function to inject artificial key presses/releases. void GraphicsWindowX11::forceKey(int key, double time, bool state) { if (!(state ^ keyMapGetKey(_keyMap, key))) return; // already pressed/released XKeyEvent event; event.serial = 0; event.send_event = True; event.display = _eventDisplay; event.window = _window; event.subwindow = 0; event.time = 0; event.x = 0; event.y = 0; event.x_root = 0; event.y_root = 0; event.state = getModifierMask() | (_modifierState & (LockMask | _numLockMask)); event.keycode = key; event.same_screen = True; int keySymbol = 0; int unmodifiedKeySymbol = 0; if (state) { event.type = KeyPress; adaptKey(event, keySymbol, unmodifiedKeySymbol); getEventQueue()->keyPress(keySymbol, time, unmodifiedKeySymbol); keyMapSetKey(_keyMap, key); } else { event.type = KeyRelease; adaptKey(event, keySymbol, unmodifiedKeySymbol); getEventQueue()->keyRelease(keySymbol, time, unmodifiedKeySymbol); keyMapClearKey(_keyMap, key); } } void GraphicsWindowX11::syncLocks() { unsigned int mask = getEventQueue()->getCurrentEventState()->getModKeyMask(); if (_modifierState & LockMask) mask |= osgGA::GUIEventAdapter::MODKEY_CAPS_LOCK; else mask &= ~osgGA::GUIEventAdapter::MODKEY_CAPS_LOCK; if (_modifierState & _numLockMask) mask |= osgGA::GUIEventAdapter::MODKEY_NUM_LOCK; else mask &= ~osgGA::GUIEventAdapter::MODKEY_NUM_LOCK; getEventQueue()->getCurrentEventState()->setModKeyMask(mask); } void GraphicsWindowX11::rescanModifierMapping() { XModifierKeymap *mkm = XGetModifierMapping(_eventDisplay); KeyCode *m = mkm->modifiermap; KeyCode numlock = XKeysymToKeycode(_eventDisplay, XK_Num_Lock); _numLockMask = 0; for (int i = 0; i < mkm->max_keypermod * 8; i++, m++) { if (*m == numlock) { _numLockMask = 1 << (i / mkm->max_keypermod); break; } } XFree(mkm->modifiermap); XFree(mkm); } void GraphicsWindowX11::flushKeyEvents() { XEvent e; while (XCheckMaskEvent(_eventDisplay, KeyPressMask|KeyReleaseMask, &e)) continue; } // Returns char[32] keymap with bits for every modifier key set. void GraphicsWindowX11::getModifierMap(char* keymap) const { memset(keymap, 0, 32); XModifierKeymap *mkm = XGetModifierMapping(_eventDisplay); KeyCode *m = mkm->modifiermap; for (int i = 0; i < mkm->max_keypermod * 8; i++, m++) { if (*m) keyMapSetKey(keymap, *m); } XFree(mkm->modifiermap); XFree(mkm); } int GraphicsWindowX11::getModifierMask() const { int mask = 0; XModifierKeymap *mkm = XGetModifierMapping(_eventDisplay); for (int i = 0; i < mkm->max_keypermod * 8; i++) { unsigned int key = mkm->modifiermap[i]; if (key && keyMapGetKey(_keyMap, key)) { mask |= 1 << (i / mkm->max_keypermod); } } XFree(mkm->modifiermap); XFree(mkm); return mask; } void GraphicsWindowX11::requestWarpPointer(float x,float y) { if (!_realized) { OSG_INFO<<"GraphicsWindowX11::requestWarpPointer() - Window not realized; cannot warp pointer, screenNum="<< _traits->screenNum<(x), static_cast(y) ); XFlush(display); XSync(display, 0); getEventQueue()->mouseWarped(x,y); } extern "C" { typedef int (*X11ErrorHandler)(Display*, XErrorEvent*); int X11ErrorHandling(Display* display, XErrorEvent* event) { OSG_NOTICE<<"Got an X11ErrorHandling call display="<request_code << std::endl; OSG_NOTICE << "Minor opcode: " << (int)event->minor_code << std::endl; OSG_NOTICE << "Error code: " << (int)event->error_code << std::endl; OSG_NOTICE << "Request serial: " << event->serial << std::endl; OSG_NOTICE << "Current serial: " << NextRequest( display ) - 1 << std::endl; switch( event->error_code ) { case BadValue: OSG_NOTICE << " Value: " << event->resourceid << std::endl; break; case BadAtom: OSG_NOTICE << " AtomID: " << event->resourceid << std::endl; break; default: OSG_NOTICE << " ResourceID: " << event->resourceid << std::endl; break; } return 0; } } class X11WindowingSystemInterface : public osg::GraphicsContext::WindowingSystemInterface { #ifdef OSGVIEWER_USE_XRANDR // TODO: Investigate whether or not Robert thinks we should store/restore the original // resolution in the destructor; I'm not sure the other ones do this, and it may be the // responsibility of the user. bool _setScreen(const osg::GraphicsContext::ScreenIdentifier& si, unsigned int width, unsigned int height, unsigned int colorDepth, double rate) { if (colorDepth>0) OSG_NOTICE << "X11WindowingSystemInterface::_setScreen() is not fully implemented (missing depth)."<(width) && ss[i].height == static_cast(height)) { short* rates = XRRConfigRates(sc, i, &numRates); bool rateFound = false; // Search for our rate in the list of acceptable rates given to us by Xrandr. // If it's not found, rateFound will still be false and the call will never // be made to XRRSetScreenConfigAndRate since the rate will be invalid. for(int r = 0; r < numRates; r++) { if(rates[r] == static_cast(rate)) { rateFound = true; break; } } if(rate > 0.0f && !rateFound) { OSG_NOTICE << "Unable to find valid refresh rate " << rate << " on display \"" << XDisplayName(si.displayName().c_str()) << "\"."<(rate), CurrentTime) != RRSetConfigSuccess) { OSG_NOTICE << "Unable to set resolution to " << width << "x" << height << " on display \"" << XDisplayName(si.displayName().c_str()) << "\"."<setNumFramesToRetainObjects(0); osg::Referenced::getDeleteHandler()->flushAll(); } //OSG_NOTICE<<"~X11WindowingSystemInterface()"< 1 || ( major == 1 && minor >= 2 ) ); } #endif return false; } virtual void getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& si, osg::GraphicsContext::ScreenSettings & resolution ) { Display* display = XOpenDisplay(si.displayName().c_str()); if(display) { resolution.width = DisplayWidth(display, si.screenNum); resolution.height = DisplayHeight(display, si.screenNum); resolution.colorDepth = DefaultDepth(display, si.screenNum); resolution.refreshRate = 0; // Missing call. Need a X11 expert. #ifdef OSGVIEWER_USE_XRANDR if (supportsRandr(display)) { XRRScreenConfiguration* screenConfig = XRRGetScreenInfo ( display, RootWindow(display, si.screenNum) ); resolution.refreshRate = XRRConfigCurrentRate ( screenConfig ); XRRFreeScreenConfigInfo( screenConfig ); } #endif XCloseDisplay(display); } else { OSG_NOTICE << "Unable to open display \"" << XDisplayName(si.displayName().c_str()) << "\"."<0) { for(int i=0; i0) { for(int j=0; jpbuffer) { #if 1 osg::ref_ptr pbuffer = new PixelBufferX11(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; #endif } else { osg::ref_ptr window = new GraphicsWindowX11(traits); if (window->valid()) return window.release(); else return 0; } } }; struct RegisterWindowingSystemInterfaceProxy { RegisterWindowingSystemInterfaceProxy() { OSG_INFO<<"RegisterWindowingSystemInterfaceProxy()"<setNumFramesToRetainObjects(0); osg::Referenced::getDeleteHandler()->flushAll(); } osg::GraphicsContext::setWindowingSystemInterface(0); } }; RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy; // declare C entry point for static compilation. extern "C" void graphicswindow_X11(void) { osg::GraphicsContext::setWindowingSystemInterface(new X11WindowingSystemInterface); } void GraphicsWindowX11::raiseWindow() { Display* display = getDisplayToUse(); XWindowAttributes winAttrib; Window root_return, parent_return, *children; unsigned int nchildren, i=0; XTextProperty windowName; bool xraise = false; XQueryTree(display, _parent, &root_return, &parent_return, &children, &nchildren); while (!xraise && iwindowName.c_str(),(const char *)windowName.value) == 0)) xraise = true; } if (xraise) XRaiseWindow(display,_window); else { XGetWindowAttributes(display, _window, &winAttrib); XReparentWindow(display, _window, _parent, winAttrib.x, winAttrib.y); } XFree(children); XFlush(display); XSync(display,0); }