<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 <NAME> 
 *
 * 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  <NAME>.
 */

#include <osgViewer/api/X11/GraphicsWindowX11>
#include <osgViewer/api/X11/PixelBufferX11>

#include <osg/DeleteHandler>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <X11/Xmd.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <X11/Intrinsic.h>

#include <X11/Xmd.h>        /* For CARD16 */

#include <unistd.h>

using namespace osgViewer;

class X11KeyboardMap
{
    public:

        X11KeyboardMap()
        {
            _keymap[XK_Escape       ] = osgGA::GUIEventAdapter::KEY_Escape;
            _keymap[XK_F1           ] = osgGA::GUIEventAdapter::KEY_F1;
            _keymap[XK_F2           ] = osgGA::GUIEventAdapter::KEY_F2;
            _keymap[XK_F3           ] = osgGA::GUIEventAdapter::KEY_F3;
            _keymap[XK_F4           ] = osgGA::GUIEventAdapter::KEY_F4;
            _keymap[XK_F5           ] = osgGA::GUIEventAdapter::KEY_F5;
            _keymap[XK_F6           ] = osgGA::GUIEventAdapter::KEY_F6;
            _keymap[XK_F7           ] = osgGA::GUIEventAdapter::KEY_F7;
            _keymap[XK_F8           ] = osgGA::GUIEventAdapter::KEY_F8;
            _keymap[XK_F9           ] = osgGA::GUIEventAdapter::KEY_F9;
            _keymap[XK_F10          ] = osgGA::GUIEventAdapter::KEY_F10;
            _keymap[XK_F11          ] = osgGA::GUIEventAdapter::KEY_F11;
            _keymap[XK_F12          ] = osgGA::GUIEventAdapter::KEY_F12;
            _keymap[XK_quoteleft    ] = '"';
            _keymap[XK_1            ] = '1';
            _keymap[XK_2            ] = '2';
            _keymap[XK_3            ] = '3';
            _keymap[XK_4            ] = '4';
            _keymap[XK_5            ] = '5';
            _keymap[XK_6            ] = '6';
            _keymap[XK_7            ] = '7';
            _keymap[XK_8            ] = '8';
            _keymap[XK_9            ] = '9';
            _keymap[XK_0            ] = '0';
            _keymap[XK_minus        ] = '-';
            _keymap[XK_equal        ] = '=';
            _keymap[XK_BackSpace    ] = osgGA::GUIEventAdapter::KEY_BackSpace;
            _keymap[XK_Tab          ] = osgGA::GUIEventAdapter::KEY_Tab;
            _keymap[XK_a            ] = 'A';
            _keymap[XK_b            ] = 'B';
            _keymap[XK_c            ] = 'C';
            _keymap[XK_d            ] = 'D';
            _keymap[XK_e            ] = 'E';
            _keymap[XK_f            ] = 'F';
            _keymap[XK_g            ] = 'G';
            _keymap[XK_h            ] = 'H';
            _keymap[XK_i            ] = 'I';
            _keymap[XK_j            ] = 'J';
            _keymap[XK_k            ] = 'K';
            _keymap[XK_l            ] = 'L';
            _keymap[XK_m            ] = 'M';
            _keymap[XK_n            ] = 'N';
            _keymap[XK_o            ] = 'O';
            _keymap[XK_p            ] = 'P';
            _keymap[XK_q            ] = 'Q';
            _keymap[XK_r            ] = 'R';
            _keymap[XK_s            ] = 'S';
            _keymap[XK_t            ] = 'T';
            _keymap[XK_u            ] = 'U';
            _keymap[XK_v            ] = 'V';
            _keymap[XK_w            ] = 'W';
            _keymap[XK_x            ] = 'X';
            _keymap[XK_y            ] = 'Y';
            _keymap[XK_z            ] = 'Z';
            _keymap[XK_bracketleft  ] = '(';
            _keymap[XK_bracketright ] = ')';
            _keymap[XK_backslash    ] = '\\';
            _keymap[XK_Caps_Lock    ] = osgGA::GUIEventAdapter::KEY_Caps_Lock;
            _keymap[XK_semicolon    ] = ';';
            _keymap[XK_apostrophe   ] = '\'';
            _keymap[XK_Return       ] = osgGA::GUIEventAdapter::KEY_Return;
            _keymap[XK_Shift_L      ] = osgGA::GUIEventAdapter::KEY_Shift_L;
            _keymap[XK_comma        ] = ',';
            _keymap[XK_period       ] = '.';
            _keymap[XK_slash        ] = '/';
            _keymap[XK_Shift_R      ] = osgGA::GUIEventAdapter::KEY_Shift_R;
            _keymap[XK_Control_L    ] = osgGA::GUIEventAdapter::KEY_Control_L;
            _keymap[XK_Super_L      ] = osgGA::GUIEventAdapter::KEY_Super_L;
            _keymap[XK_space        ] = ' ';
            _keymap[XK_Alt_L        ] = osgGA::GUIEventAdapter::KEY_Alt_L;
            _keymap[XK_Alt_R        ] = osgGA::GUIEventAdapter::KEY_Alt_R;
            _keymap[XK_Super_R      ] = osgGA::GUIEventAdapter::KEY_Super_R;
            _keymap[XK_Menu         ] = osgGA::GUIEventAdapter::KEY_Menu;
            _keymap[XK_Control_R    ] = osgGA::GUIEventAdapter::KEY_Control_R;
            _keymap[XK_Print        ] = osgGA::GUIEventAdapter::KEY_Print;
            _keymap[XK_Scroll_Lock  ] = osgGA::GUIEventAdapter::KEY_Scroll_Lock;
            _keymap[XK_Pause        ] = osgGA::GUIEventAdapter::KEY_Pause;
            _keymap[XK_Home         ] = osgGA::GUIEventAdapter::KEY_Home;
            _keymap[XK_Page_Up      ] = osgGA::GUIEventAdapter::KEY_Page_Up;
            _keymap[XK_End          ] = osgGA::GUIEventAdapter::KEY_End;
            _keymap[XK_Page_Down    ] = osgGA::GUIEventAdapter::KEY_Page_Down;
            _keymap[XK_Delete       ] = osgGA::GUIEventAdapter::KEY_Delete;
            _keymap[XK_Insert       ] = osgGA::GUIEventAdapter::KEY_Insert;
            _keymap[XK_Left         ] = osgGA::GUIEventAdapter::KEY_Left;
            _keymap[XK_Up           ] = osgGA::GUIEventAdapter::KEY_Up;
            _keymap[XK_Right        ] = osgGA::GUIEventAdapter::KEY_Right;
            _keymap[XK_Down         ] = osgGA::GUIEventAdapter::KEY_Down;
            _keymap[XK_Num_Lock     ] = osgGA::GUIEventAdapter::KEY_Num_Lock;
            _keymap[XK_KP_Divide    ] = osgGA::GUIEventAdapter::KEY_KP_Divide;
            _keymap[XK_KP_Multiply  ] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
            _keymap[XK_KP_Subtract  ] = osgGA::GUIEventAdapter::KEY_KP_Subtract;
            _keymap[XK_KP_Add       ] = osgGA::GUIEventAdapter::KEY_KP_Add;
            _keymap[XK_KP_Home      ] = osgGA::GUIEventAdapter::KEY_KP_Home;
            _keymap[XK_KP_Up        ] = osgGA::GUIEventAdapter::KEY_KP_Up;
            _keymap[XK_KP_Page_Up   ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up;
            _keymap[XK_KP_Left      ] = osgGA::GUIEventAdapter::KEY_KP_Left;
            _keymap[XK_KP_Begin     ] = osgGA::GUIEventAdapter::KEY_KP_Begin;
            _keymap[XK_KP_Right     ] = osgGA::GUIEventAdapter::KEY_KP_Right;
            _keymap[XK_KP_End       ] = osgGA::GUIEventAdapter::KEY_KP_End;
            _keymap[XK_KP_Down      ] = osgGA::GUIEventAdapter::KEY_KP_Down;
            _keymap[XK_KP_Page_Down ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down;
            _keymap[XK_KP_Insert    ] = osgGA::GUIEventAdapter::KEY_KP_Insert;
            _keymap[XK_KP_Delete    ] = osgGA::GUIEventAdapter::KEY_KP_Delete;
            _keymap[XK_KP_Enter     ] = osgGA::GUIEventAdapter::KEY_KP_Enter;
     
        }

        ~X11KeyboardMap() {}

        int remapKey(int key)
        {
            KeyMap::iterator itr = _keymap.find(key);
            if (itr == _keymap.end()) return key;
            else return itr->second;
        }
      
    protected:
    

        typedef std::map<int, int> 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<int> 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;
}
#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();
    
    Atom atom;
    if( (atom = XInternAtom( display, "_MOTIF_WM_HINTS", 0 )) != None )
    {
    
        struct
        {
            unsigned long flags;
            unsigned long functions;
            unsigned long decorations;
            long          inputMode;
            unsigned long status;
        } wmHints;
        
        wmHints.flags = 0;
        wmHints.functions = MWM_FUNC_ALL;
        wmHints.decorations = MWM_DECOR_ALL;
        wmHints.inputMode = 0;
        wmHints.status = 0;        
        
        if (!flag)
        {
            wmHints.flags = MWM_HINTS_DECORATIONS;
            wmHints.decorations = 0;
        }
        else
        {
            wmHints.flags |= MWM_HINTS_FUNCTIONS;
            if (_traits.valid() && !_traits->supportsResize) wmHints.functions |= MWM_FUNC_RESIZE;
        }

        XMapWindow(display, _window );
        XChangeProperty( display, _window, atom, atom, 32, PropModeReplace, (unsigned char *)&wmHints,  5 );

        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<MouseCursor,Cursor>::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;
    }
    
    getEventQueue()->setCurrentEventState(osgGA::GUIEventAdapter::getAccumulatedEventState().get());
    
    WindowData* inheritedWindowData =  dynamic_cast<WindowData*>(_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()) << "\"."<<std::endl;
        _valid = false;
        return;
    }

     // Query for GLX extension
    int errorBase, eventBase;
    if( glXQueryExtension( _display, &errorBase, &eventBase)  == False )
    {
        osg::notify(osg::NOTICE)<<"Error: " << XDisplayName(_traits->displayName().c_str()) <<" has no GLX extension." << std::endl;

        XCloseDisplay( _display );
        _display = 0;
        _valid = false;
        return;
    }
    
    // osg::notify(osg::NOTICE)<<"GLX extension, errorBase="<<errorBase<<" eventBase="<<eventBase<<std::endl;

    if (!createVisualInfo())
    {
        _traits->red /= 2; 
        _traits->green /= 2; 
        _traits->blue /= 2; 
        _traits->alpha /= 2; 
        _traits->depth /= 2; 
        
        osg::notify(osg::INFO)<<"Relaxing traits"<<std::endl;

        if (!createVisualInfo())
        {
            osg::notify(osg::NOTICE)<<"Error: Not able to create requested visual." << std::endl;
            XCloseDisplay( _display );
            _display = 0;
            _valid = false;
            return;
        }    
    }
    
    
    GLXContext sharedContextGLX = NULL;

    // get any shared GLX contexts    
    GraphicsWindowX11* graphicsWindowX11 = dynamic_cast<GraphicsWindowX11*>(_traits->sharedContext);
    if (graphicsWindowX11) 
    {
        sharedContextGLX = graphicsWindowX11->getGLXContext();
    }
    else
    {
        PixelBufferX11* pixelBufferX11 = dynamic_cast<PixelBufferX11*>(_traits->sharedContext);
        if (pixelBufferX11)
        {
            sharedContextGLX = pixelBufferX11->getGLXContext();
        }
    }
    
    _glxContext = glXCreateContext( _display, _visualInfo, sharedContextGLX, True );
    
    if (!_glxContext)
    {
        osg::notify(osg::NOTICE)<<"Error: Unable to create OpenGL graphics context."<<std::endl;
        XCloseDisplay( _display );
        _display = 0;
        _valid = false;
        return;
    }
    
    _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;

    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."<<std::endl;
        XCloseDisplay( _display );
        _display = 0;
        _glxContext = 0;
        _valid = false;
        return;
    }


    // 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 = _traits->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 = "<<watt.x<<" watt.y="<<watt.y<<" width="<<watt.width<<" height="<<watt.height<<std::endl;


    XSelectInput( _eventDisplay, _window, ExposureMask | StructureNotifyMask | 
                                     KeyPressMask | KeyReleaseMask |
                                     PointerMotionMask  | ButtonPressMask | ButtonReleaseMask);

    XFlush( _eventDisplay );
    XSync( _eventDisplay, 0 );

    _valid = true;
    _initialized = true;
}

bool GraphicsWindowX11::realizeImplementation()
{
    if (_realized)
    {
        osg::notify(osg::NOTICE)<<"GraphicsWindowX11::realizeImplementation() Already realized"<<std::endl;
        return true;
    }

    if (!_initialized) init();
    
    if (!_initialized) return false;
    
    XMapWindow( _display, _window );
    
//    Window temp = _window;
//    XSetWMColormapWindows( _display, _window, &temp, 1);
    
    _realized = true;

    return true;
}

bool GraphicsWindowX11::makeCurrentImplementation()
{
    if (!_realized)
    {
        osg::notify(osg::NOTICE)<<"Warning: GraphicsWindow not realized, cannot do makeCurrent."<<std::endl;
        return false;
    }

    // osg::notify(osg::NOTICE)<<"GraphicsWindowX11::makeCurrentImplementation "<<this<<" "<<OpenThreads::Thread::CurrentThread()<<std::endl;
    // osg::notify(osg::NOTICE)<<"   glXMakeCurrent ("<<_display<<","<<_window<<","<<_glxContext<<std::endl;

    return glXMakeCurrent( _display, _window, _glxContext )==True;
}

bool GraphicsWindowX11::releaseContextImplementation()
{
    if (!_realized)
    {
        osg::notify(osg::NOTICE)<<"Warning: GraphicsWindow not realized, cannot do makeCurrent."<<std::endl;
        return false;
    }

    // osg::notify(osg::NOTICE)<<"GraphicsWindowX11::releaseContextImplementation() "<<this<<" "<<OpenThreads::Thread::CurrentThread()<<std::endl;
    // osg::notify(osg::NOTICE)<<"   glXMakeCurrent ("<<_display<<std::endl;

    return glXMakeCurrent( _display, None, NULL )==True;
}


void GraphicsWindowX11::closeImplementation()
{
    // osg::notify(osg::NOTICE)<<"Closing GraphicsWindowX11"<<std::endl;

    if (_eventDisplay)
    {
        XCloseDisplay( _eventDisplay );
        _eventDisplay = 0;
    }

    if (_display)
    {
        if (_glxContext)
        {
            glXDestroyContext(_display, _glxContext );
        }
    
        if (_window)
        {
            XDestroyWindow(_display, _window);
        }

        XFlush( _display );
        XSync( _display,0 );
    }
    
    _window = 0;
    _parent = 0;
    _glxContext = 0;

    if (_visualInfo)
    {
        XFree(_visualInfo);
        _visualInfo = 0;
    }


    if (_display)
    {
        XCloseDisplay( _display );
        _display = 0;
    }

    _initialized = false;
    _realized = false;
    _valid = false;
}

void GraphicsWindowX11::swapBuffersImplementation()
{
    if (!_realized) return;

    // osg::notify(osg::NOTICE)<<"swapBuffersImplementation "<<this<<" "<<OpenThreads::Thread::CurrentThread()<<std::endl;

    glXSwapBuffers(_display, _window); 

    while( XPending(_display) )
    {
        XEvent ev;
        XNextEvent( _display, &ev );

        switch( ev.type )
        {
            case ClientMessage:
            {
                if (static_cast<Atom>(ev.xclient.data.l[0]) == _deleteWindow)
                {
                    osg::notify(osg::INFO)<<"DeleteWindow event recieved"<<std::endl;
                    getEventQueue()->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"<<std::endl;    
    while( XPending(display) )
    {
        XEvent ev;
        XNextEvent( display, &ev );

        switch( ev.type )
        {
            case ClientMessage:
            {
                osg::notify(osg::NOTICE)<<"ClientMessage event recieved"<<std::endl;
                if (static_cast<Atom>(ev.xclient.data.l[0]) == _deleteWindow)
                {
                    osg::notify(osg::NOTICE)<<"DeleteWindow event recieved"<<std::endl;
                    destroyWindowRequested = true;
                    getEventQueue()->closeWindow(eventTime);
                }
            }
            case Expose :
                osg::notify(osg::INFO)<<"Expose x="<<ev.xexpose.x<<" y="<<ev.xexpose.y<<" width="<<ev.xexpose.width<<", height="<<ev.xexpose.height<<std::endl;
                break;

            case GravityNotify :
                osg::notify(osg::INFO)<<"GravityNotify event recieved"<<std::endl;
                break;

            case UnmapNotify :
                osg::notify(osg::INFO)<<"UnmapNotify event recieved"<<std::endl;
                break;

            case ReparentNotify:
                osg::notify(osg::INFO)<<"ReparentNotify event recieved"<<std::endl;
                break;

            case DestroyNotify :
                osg::notify(osg::NOTICE)<<"DestroyNotify event recieved"<<std::endl;
                _realized =  false;
                _valid = false;
                break;
                
            case ConfigureNotify :
            {
                osg::notify(osg::INFO)<<"ConfigureNotify x="<<ev.xconfigure.x<<" y="<<ev.xconfigure.y<<" width="<<ev.xconfigure.width<<", height="<<ev.xconfigure.height<<std::endl;

                if (windowX != ev.xconfigure.x || 
                    windowX != ev.xconfigure.y ||
                    windowWidth != ev.xconfigure.width ||
                    windowHeight != ev.xconfigure.height)
                {
                    resizeTime = eventTime;

                    windowX = ev.xconfigure.x;
                    windowY = ev.xconfigure.y;
                    windowWidth = ev.xconfigure.width;
                    windowHeight = ev.xconfigure.height;
                }

                break;
            }
            
            case MapNotify :
            {
                osg::notify(osg::INFO)<<"MapNotify"<<std::endl;
                XWindowAttributes watt;
                do
                    XGetWindowAttributes(display, _window, &watt );
                while( watt.map_state != IsViewable );
                
                osg::notify(osg::INFO)<<"MapNotify x="<<watt.x<<" y="<<watt.y<<" width="<<watt.width<<", height="<<watt.height<<std::endl;

                if (windowWidth != watt.width || windowHeight != watt.height)
                {
                    resizeTime = eventTime;

                    windowWidth = watt.width;
                    windowHeight = watt.height;
                }

                break;
            }

           case MotionNotify :
           {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(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<int>(_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::notify(osg::NOTICE)<<"MotionNotify wx="<<wx<<" wy="<<wy<<" mx="<<mx<<" my="<<my<<std::endl;

                break;
            }
            
            case ButtonPress :
            {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(relativeTime)*0.001;

                if( ev.xbutton.button == Button4 )
                {
                    getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_UP, eventTime);
                }
                else if( ev.xbutton.button == Button5)
                {
                    getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_DOWN, eventTime);
                }
                else
                {
                    float mx = ev.xbutton.x;
                    float my = ev.xmotion.y;
                    transformMouseXY(mx, my);
                    getEventQueue()->mouseButtonPress(mx, my, ev.xbutton.button, eventTime);
                }
                break;
            }
            
            case ButtonRelease :
            {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(relativeTime)*0.001;

                if( ev.xbutton.button == Button4 )
                {
                    getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_UP, eventTime);
                }
                else if( ev.xbutton.button == Button5)
                {
                    getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_DOWN, eventTime);
                }
                else
                {
                    float mx = ev.xbutton.x;
                    float my = ev.xmotion.y;
                    transformMouseXY(mx, my);
                    getEventQueue()->mouseButtonRelease(mx, my, ev.xbutton.button, eventTime);
                }
                break;
            }
            
            case KeyPress:
            {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(relativeTime)*0.001;

                int keySymbol = 0;
                unsigned int modifierMask = 0;
                adaptKey(ev.xkey, keySymbol, modifierMask);

                //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
                getEventQueue()->keyPress(keySymbol, eventTime);
                break;
            }
            
            case KeyRelease:
            {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(relativeTime)*0.001;

                int keySymbol = 0;
                unsigned int modifierMask = 0;
                adaptKey(ev.xkey, keySymbol, modifierMask);
                
                //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
                getEventQueue()->keyRelease(keySymbol, eventTime);
                break;
            }
            
            default:
                osg::notify(osg::NOTICE)<<"Other event"<<std::endl;
                break;
                
        }<commit_msg>From <NAME>, "This patch causes GraphicsWindowX11 to not send key release events when a key press / release is caused by auto-repeat. This is consistent with Windows and Mac behavior, as well as other toolkits such as SDL."<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 <NAME> 
 *
 * 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  <NAME>.
 */

#include <osgViewer/api/X11/GraphicsWindowX11>
#include <osgViewer/api/X11/PixelBufferX11>

#include <osg/DeleteHandler>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <X11/Xmd.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <X11/Intrinsic.h>

#include <X11/Xmd.h>        /* For CARD16 */

#include <unistd.h>

using namespace osgViewer;

class X11KeyboardMap
{
    public:

        X11KeyboardMap()
        {
            _keymap[XK_Escape       ] = osgGA::GUIEventAdapter::KEY_Escape;
            _keymap[XK_F1           ] = osgGA::GUIEventAdapter::KEY_F1;
            _keymap[XK_F2           ] = osgGA::GUIEventAdapter::KEY_F2;
            _keymap[XK_F3           ] = osgGA::GUIEventAdapter::KEY_F3;
            _keymap[XK_F4           ] = osgGA::GUIEventAdapter::KEY_F4;
            _keymap[XK_F5           ] = osgGA::GUIEventAdapter::KEY_F5;
            _keymap[XK_F6           ] = osgGA::GUIEventAdapter::KEY_F6;
            _keymap[XK_F7           ] = osgGA::GUIEventAdapter::KEY_F7;
            _keymap[XK_F8           ] = osgGA::GUIEventAdapter::KEY_F8;
            _keymap[XK_F9           ] = osgGA::GUIEventAdapter::KEY_F9;
            _keymap[XK_F10          ] = osgGA::GUIEventAdapter::KEY_F10;
            _keymap[XK_F11          ] = osgGA::GUIEventAdapter::KEY_F11;
            _keymap[XK_F12          ] = osgGA::GUIEventAdapter::KEY_F12;
            _keymap[XK_quoteleft    ] = '"';
            _keymap[XK_1            ] = '1';
            _keymap[XK_2            ] = '2';
            _keymap[XK_3            ] = '3';
            _keymap[XK_4            ] = '4';
            _keymap[XK_5            ] = '5';
            _keymap[XK_6            ] = '6';
            _keymap[XK_7            ] = '7';
            _keymap[XK_8            ] = '8';
            _keymap[XK_9            ] = '9';
            _keymap[XK_0            ] = '0';
            _keymap[XK_minus        ] = '-';
            _keymap[XK_equal        ] = '=';
            _keymap[XK_BackSpace    ] = osgGA::GUIEventAdapter::KEY_BackSpace;
            _keymap[XK_Tab          ] = osgGA::GUIEventAdapter::KEY_Tab;
            _keymap[XK_a            ] = 'A';
            _keymap[XK_b            ] = 'B';
            _keymap[XK_c            ] = 'C';
            _keymap[XK_d            ] = 'D';
            _keymap[XK_e            ] = 'E';
            _keymap[XK_f            ] = 'F';
            _keymap[XK_g            ] = 'G';
            _keymap[XK_h            ] = 'H';
            _keymap[XK_i            ] = 'I';
            _keymap[XK_j            ] = 'J';
            _keymap[XK_k            ] = 'K';
            _keymap[XK_l            ] = 'L';
            _keymap[XK_m            ] = 'M';
            _keymap[XK_n            ] = 'N';
            _keymap[XK_o            ] = 'O';
            _keymap[XK_p            ] = 'P';
            _keymap[XK_q            ] = 'Q';
            _keymap[XK_r            ] = 'R';
            _keymap[XK_s            ] = 'S';
            _keymap[XK_t            ] = 'T';
            _keymap[XK_u            ] = 'U';
            _keymap[XK_v            ] = 'V';
            _keymap[XK_w            ] = 'W';
            _keymap[XK_x            ] = 'X';
            _keymap[XK_y            ] = 'Y';
            _keymap[XK_z            ] = 'Z';
            _keymap[XK_bracketleft  ] = '(';
            _keymap[XK_bracketright ] = ')';
            _keymap[XK_backslash    ] = '\\';
            _keymap[XK_Caps_Lock    ] = osgGA::GUIEventAdapter::KEY_Caps_Lock;
            _keymap[XK_semicolon    ] = ';';
            _keymap[XK_apostrophe   ] = '\'';
            _keymap[XK_Return       ] = osgGA::GUIEventAdapter::KEY_Return;
            _keymap[XK_Shift_L      ] = osgGA::GUIEventAdapter::KEY_Shift_L;
            _keymap[XK_comma        ] = ',';
            _keymap[XK_period       ] = '.';
            _keymap[XK_slash        ] = '/';
            _keymap[XK_Shift_R      ] = osgGA::GUIEventAdapter::KEY_Shift_R;
            _keymap[XK_Control_L    ] = osgGA::GUIEventAdapter::KEY_Control_L;
            _keymap[XK_Super_L      ] = osgGA::GUIEventAdapter::KEY_Super_L;
            _keymap[XK_space        ] = ' ';
            _keymap[XK_Alt_L        ] = osgGA::GUIEventAdapter::KEY_Alt_L;
            _keymap[XK_Alt_R        ] = osgGA::GUIEventAdapter::KEY_Alt_R;
            _keymap[XK_Super_R      ] = osgGA::GUIEventAdapter::KEY_Super_R;
            _keymap[XK_Menu         ] = osgGA::GUIEventAdapter::KEY_Menu;
            _keymap[XK_Control_R    ] = osgGA::GUIEventAdapter::KEY_Control_R;
            _keymap[XK_Print        ] = osgGA::GUIEventAdapter::KEY_Print;
            _keymap[XK_Scroll_Lock  ] = osgGA::GUIEventAdapter::KEY_Scroll_Lock;
            _keymap[XK_Pause        ] = osgGA::GUIEventAdapter::KEY_Pause;
            _keymap[XK_Home         ] = osgGA::GUIEventAdapter::KEY_Home;
            _keymap[XK_Page_Up      ] = osgGA::GUIEventAdapter::KEY_Page_Up;
            _keymap[XK_End          ] = osgGA::GUIEventAdapter::KEY_End;
            _keymap[XK_Page_Down    ] = osgGA::GUIEventAdapter::KEY_Page_Down;
            _keymap[XK_Delete       ] = osgGA::GUIEventAdapter::KEY_Delete;
            _keymap[XK_Insert       ] = osgGA::GUIEventAdapter::KEY_Insert;
            _keymap[XK_Left         ] = osgGA::GUIEventAdapter::KEY_Left;
            _keymap[XK_Up           ] = osgGA::GUIEventAdapter::KEY_Up;
            _keymap[XK_Right        ] = osgGA::GUIEventAdapter::KEY_Right;
            _keymap[XK_Down         ] = osgGA::GUIEventAdapter::KEY_Down;
            _keymap[XK_Num_Lock     ] = osgGA::GUIEventAdapter::KEY_Num_Lock;
            _keymap[XK_KP_Divide    ] = osgGA::GUIEventAdapter::KEY_KP_Divide;
            _keymap[XK_KP_Multiply  ] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
            _keymap[XK_KP_Subtract  ] = osgGA::GUIEventAdapter::KEY_KP_Subtract;
            _keymap[XK_KP_Add       ] = osgGA::GUIEventAdapter::KEY_KP_Add;
            _keymap[XK_KP_Home      ] = osgGA::GUIEventAdapter::KEY_KP_Home;
            _keymap[XK_KP_Up        ] = osgGA::GUIEventAdapter::KEY_KP_Up;
            _keymap[XK_KP_Page_Up   ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up;
            _keymap[XK_KP_Left      ] = osgGA::GUIEventAdapter::KEY_KP_Left;
            _keymap[XK_KP_Begin     ] = osgGA::GUIEventAdapter::KEY_KP_Begin;
            _keymap[XK_KP_Right     ] = osgGA::GUIEventAdapter::KEY_KP_Right;
            _keymap[XK_KP_End       ] = osgGA::GUIEventAdapter::KEY_KP_End;
            _keymap[XK_KP_Down      ] = osgGA::GUIEventAdapter::KEY_KP_Down;
            _keymap[XK_KP_Page_Down ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down;
            _keymap[XK_KP_Insert    ] = osgGA::GUIEventAdapter::KEY_KP_Insert;
            _keymap[XK_KP_Delete    ] = osgGA::GUIEventAdapter::KEY_KP_Delete;
            _keymap[XK_KP_Enter     ] = osgGA::GUIEventAdapter::KEY_KP_Enter;
     
        }

        ~X11KeyboardMap() {}

        int remapKey(int key)
        {
            KeyMap::iterator itr = _keymap.find(key);
            if (itr == _keymap.end()) return key;
            else return itr->second;
        }
      
    protected:
    

        typedef std::map<int, int> 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<int> 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;
}
#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();
    
    Atom atom;
    if( (atom = XInternAtom( display, "_MOTIF_WM_HINTS", 0 )) != None )
    {
    
        struct
        {
            unsigned long flags;
            unsigned long functions;
            unsigned long decorations;
            long          inputMode;
            unsigned long status;
        } wmHints;
        
        wmHints.flags = 0;
        wmHints.functions = MWM_FUNC_ALL;
        wmHints.decorations = MWM_DECOR_ALL;
        wmHints.inputMode = 0;
        wmHints.status = 0;        
        
        if (!flag)
        {
            wmHints.flags = MWM_HINTS_DECORATIONS;
            wmHints.decorations = 0;
        }
        else
        {
            wmHints.flags |= MWM_HINTS_FUNCTIONS;
            if (_traits.valid() && !_traits->supportsResize) wmHints.functions |= MWM_FUNC_RESIZE;
        }

        XMapWindow(display, _window );
        XChangeProperty( display, _window, atom, atom, 32, PropModeReplace, (unsigned char *)&wmHints,  5 );

        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<MouseCursor,Cursor>::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;
    }
    
    getEventQueue()->setCurrentEventState(osgGA::GUIEventAdapter::getAccumulatedEventState().get());
    
    WindowData* inheritedWindowData =  dynamic_cast<WindowData*>(_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()) << "\"."<<std::endl;
        _valid = false;
        return;
    }

     // Query for GLX extension
    int errorBase, eventBase;
    if( glXQueryExtension( _display, &errorBase, &eventBase)  == False )
    {
        osg::notify(osg::NOTICE)<<"Error: " << XDisplayName(_traits->displayName().c_str()) <<" has no GLX extension." << std::endl;

        XCloseDisplay( _display );
        _display = 0;
        _valid = false;
        return;
    }
    
    // osg::notify(osg::NOTICE)<<"GLX extension, errorBase="<<errorBase<<" eventBase="<<eventBase<<std::endl;

    if (!createVisualInfo())
    {
        _traits->red /= 2; 
        _traits->green /= 2; 
        _traits->blue /= 2; 
        _traits->alpha /= 2; 
        _traits->depth /= 2; 
        
        osg::notify(osg::INFO)<<"Relaxing traits"<<std::endl;

        if (!createVisualInfo())
        {
            osg::notify(osg::NOTICE)<<"Error: Not able to create requested visual." << std::endl;
            XCloseDisplay( _display );
            _display = 0;
            _valid = false;
            return;
        }    
    }
    
    
    GLXContext sharedContextGLX = NULL;

    // get any shared GLX contexts    
    GraphicsWindowX11* graphicsWindowX11 = dynamic_cast<GraphicsWindowX11*>(_traits->sharedContext);
    if (graphicsWindowX11) 
    {
        sharedContextGLX = graphicsWindowX11->getGLXContext();
    }
    else
    {
        PixelBufferX11* pixelBufferX11 = dynamic_cast<PixelBufferX11*>(_traits->sharedContext);
        if (pixelBufferX11)
        {
            sharedContextGLX = pixelBufferX11->getGLXContext();
        }
    }
    
    _glxContext = glXCreateContext( _display, _visualInfo, sharedContextGLX, True );
    
    if (!_glxContext)
    {
        osg::notify(osg::NOTICE)<<"Error: Unable to create OpenGL graphics context."<<std::endl;
        XCloseDisplay( _display );
        _display = 0;
        _valid = false;
        return;
    }
    
    _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;

    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."<<std::endl;
        XCloseDisplay( _display );
        _display = 0;
        _glxContext = 0;
        _valid = false;
        return;
    }


    // 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 = _traits->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 = "<<watt.x<<" watt.y="<<watt.y<<" width="<<watt.width<<" height="<<watt.height<<std::endl;


    XSelectInput( _eventDisplay, _window, ExposureMask | StructureNotifyMask | 
                                     KeyPressMask | KeyReleaseMask |
                                     PointerMotionMask  | ButtonPressMask | ButtonReleaseMask);

    XFlush( _eventDisplay );
    XSync( _eventDisplay, 0 );

    _valid = true;
    _initialized = true;
}

bool GraphicsWindowX11::realizeImplementation()
{
    if (_realized)
    {
        osg::notify(osg::NOTICE)<<"GraphicsWindowX11::realizeImplementation() Already realized"<<std::endl;
        return true;
    }

    if (!_initialized) init();
    
    if (!_initialized) return false;
    
    XMapWindow( _display, _window );
    
//    Window temp = _window;
//    XSetWMColormapWindows( _display, _window, &temp, 1);
    
    _realized = true;

    return true;
}

bool GraphicsWindowX11::makeCurrentImplementation()
{
    if (!_realized)
    {
        osg::notify(osg::NOTICE)<<"Warning: GraphicsWindow not realized, cannot do makeCurrent."<<std::endl;
        return false;
    }

    // osg::notify(osg::NOTICE)<<"GraphicsWindowX11::makeCurrentImplementation "<<this<<" "<<OpenThreads::Thread::CurrentThread()<<std::endl;
    // osg::notify(osg::NOTICE)<<"   glXMakeCurrent ("<<_display<<","<<_window<<","<<_glxContext<<std::endl;

    return glXMakeCurrent( _display, _window, _glxContext )==True;
}

bool GraphicsWindowX11::releaseContextImplementation()
{
    if (!_realized)
    {
        osg::notify(osg::NOTICE)<<"Warning: GraphicsWindow not realized, cannot do makeCurrent."<<std::endl;
        return false;
    }

    // osg::notify(osg::NOTICE)<<"GraphicsWindowX11::releaseContextImplementation() "<<this<<" "<<OpenThreads::Thread::CurrentThread()<<std::endl;
    // osg::notify(osg::NOTICE)<<"   glXMakeCurrent ("<<_display<<std::endl;

    return glXMakeCurrent( _display, None, NULL )==True;
}


void GraphicsWindowX11::closeImplementation()
{
    // osg::notify(osg::NOTICE)<<"Closing GraphicsWindowX11"<<std::endl;

    if (_eventDisplay)
    {
        XCloseDisplay( _eventDisplay );
        _eventDisplay = 0;
    }

    if (_display)
    {
        if (_glxContext)
        {
            glXDestroyContext(_display, _glxContext );
        }
    
        if (_window)
        {
            XDestroyWindow(_display, _window);
        }

        XFlush( _display );
        XSync( _display,0 );
    }
    
    _window = 0;
    _parent = 0;
    _glxContext = 0;

    if (_visualInfo)
    {
        XFree(_visualInfo);
        _visualInfo = 0;
    }


    if (_display)
    {
        XCloseDisplay( _display );
        _display = 0;
    }

    _initialized = false;
    _realized = false;
    _valid = false;
}

void GraphicsWindowX11::swapBuffersImplementation()
{
    if (!_realized) return;

    // osg::notify(osg::NOTICE)<<"swapBuffersImplementation "<<this<<" "<<OpenThreads::Thread::CurrentThread()<<std::endl;

    glXSwapBuffers(_display, _window); 

    while( XPending(_display) )
    {
        XEvent ev;
        XNextEvent( _display, &ev );

        switch( ev.type )
        {
            case ClientMessage:
            {
                if (static_cast<Atom>(ev.xclient.data.l[0]) == _deleteWindow)
                {
                    osg::notify(osg::INFO)<<"DeleteWindow event recieved"<<std::endl;
                    getEventQueue()->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"<<std::endl;    
    while( XPending(display) )
    {
        XEvent ev;
        XNextEvent( display, &ev );

        switch( ev.type )
        {
            case ClientMessage:
            {
                osg::notify(osg::NOTICE)<<"ClientMessage event recieved"<<std::endl;
                if (static_cast<Atom>(ev.xclient.data.l[0]) == _deleteWindow)
                {
                    osg::notify(osg::NOTICE)<<"DeleteWindow event recieved"<<std::endl;
                    destroyWindowRequested = true;
                    getEventQueue()->closeWindow(eventTime);
                }
            }
            case Expose :
                osg::notify(osg::INFO)<<"Expose x="<<ev.xexpose.x<<" y="<<ev.xexpose.y<<" width="<<ev.xexpose.width<<", height="<<ev.xexpose.height<<std::endl;
                break;

            case GravityNotify :
                osg::notify(osg::INFO)<<"GravityNotify event recieved"<<std::endl;
                break;

            case UnmapNotify :
                osg::notify(osg::INFO)<<"UnmapNotify event recieved"<<std::endl;
                break;

            case ReparentNotify:
                osg::notify(osg::INFO)<<"ReparentNotify event recieved"<<std::endl;
                break;

            case DestroyNotify :
                osg::notify(osg::NOTICE)<<"DestroyNotify event recieved"<<std::endl;
                _realized =  false;
                _valid = false;
                break;
                
            case ConfigureNotify :
            {
                osg::notify(osg::INFO)<<"ConfigureNotify x="<<ev.xconfigure.x<<" y="<<ev.xconfigure.y<<" width="<<ev.xconfigure.width<<", height="<<ev.xconfigure.height<<std::endl;

                if (windowX != ev.xconfigure.x || 
                    windowX != ev.xconfigure.y ||
                    windowWidth != ev.xconfigure.width ||
                    windowHeight != ev.xconfigure.height)
                {
                    resizeTime = eventTime;

                    windowX = ev.xconfigure.x;
                    windowY = ev.xconfigure.y;
                    windowWidth = ev.xconfigure.width;
                    windowHeight = ev.xconfigure.height;
                }

                break;
            }
            
            case MapNotify :
            {
                osg::notify(osg::INFO)<<"MapNotify"<<std::endl;
                XWindowAttributes watt;
                do
                    XGetWindowAttributes(display, _window, &watt );
                while( watt.map_state != IsViewable );
                
                osg::notify(osg::INFO)<<"MapNotify x="<<watt.x<<" y="<<watt.y<<" width="<<watt.width<<", height="<<watt.height<<std::endl;

                if (windowWidth != watt.width || windowHeight != watt.height)
                {
                    resizeTime = eventTime;

                    windowWidth = watt.width;
                    windowHeight = watt.height;
                }

                break;
            }

           case MotionNotify :
           {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(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<int>(_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::notify(osg::NOTICE)<<"MotionNotify wx="<<wx<<" wy="<<wy<<" mx="<<mx<<" my="<<my<<std::endl;

                break;
            }
            
            case ButtonPress :
            {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(relativeTime)*0.001;

                if( ev.xbutton.button == Button4 )
                {
                    getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_UP, eventTime);
                }
                else if( ev.xbutton.button == Button5)
                {
                    getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_DOWN, eventTime);
                }
                else
                {
                    float mx = ev.xbutton.x;
                    float my = ev.xmotion.y;
                    transformMouseXY(mx, my);
                    getEventQueue()->mouseButtonPress(mx, my, ev.xbutton.button, eventTime);
                }
                break;
            }
            
            case ButtonRelease :
            {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(relativeTime)*0.001;

                if( ev.xbutton.button == Button4 )
                {
                    getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_UP, eventTime);
                }
                else if( ev.xbutton.button == Button5)
                {
                    getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_DOWN, eventTime);
                }
                else
                {
                    float mx = ev.xbutton.x;
                    float my = ev.xmotion.y;
                    transformMouseXY(mx, my);
                    getEventQueue()->mouseButtonRelease(mx, my, ev.xbutton.button, eventTime);
                }
                break;
            }
            
            case KeyPress:
            {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(relativeTime)*0.001;

                int keySymbol = 0;
                unsigned int modifierMask = 0;
                adaptKey(ev.xkey, keySymbol, modifierMask);

                //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
                getEventQueue()->keyPress(keySymbol, eventTime);
                break;
            }
            
            case KeyRelease:
            {
                if (firstEventTime==0) firstEventTime = ev.xmotion.time;
                Time relativeTime = ev.xmotion.time - firstEventTime;
                eventTime = baseTime + static_cast<double>(relativeTime)*0.001;
#if 1                
                // Check for following KeyPress events and see if
                // the pair are the result of auto-repeat. If so, drop
                // this one on the floor, to be consistent with
                // Windows and Mac ports. The idea comes from libSDL sources. 
                XEvent nextev;
                if (XPending(display))
                {
                    XPeekEvent(display, &nextev);
                    if ((nextev.type == KeyPress)
                        && (nextev.xkey.keycode == ev.xkey.keycode)
                        && (nextev.xmotion.time - ev.xmotion.time < 2))
                    {
                        break;
                    }
                }
#endif                
                int keySymbol = 0;
                unsigned int modifierMask = 0;
                adaptKey(ev.xkey, keySymbol, modifierMask);
                
                //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
                getEventQueue()->keyRelease(keySymbol, eventTime);
                break;
            }
            
            default:
                osg::notify(osg::NOTICE)<<"Other event"<<std::endl;
                break;
                
        }
{'fraction_non_alphanumeric': 0.11158664915180022, 'fraction_numerical': 0.009378928485670297, 'mean_word_length': 1.9685940710302319, 'pattern_counts': {'":': 0, '<': 623, '<?xml version=': 0, '>': 216, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '506350', 'n_tokens_mistral': 23084, 'n_tokens_neox': 21836, 'n_words': 3899}