//========================================================================
// GLFW - An OpenGL framework
// File:        x11_window.c
// Platform:    X11 (Unix)
// API version: 2.6
// WWW:         http://glfw.sourceforge.net
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Camilla Berglund
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would
//    be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
//    be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
//    distribution.
//
//========================================================================

#include "internal.h"


/* Defines some GLX FSAA tokens if not yet defined */
#ifndef GLX_SAMPLE_BUFFERS
# define GLX_SAMPLE_BUFFERS  100000
#endif 
#ifndef GLX_SAMPLES 
# define GLX_SAMPLES         100001
#endif 


/* KDE decoration values */
enum {
  KDE_noDecoration = 0,
  KDE_normalDecoration = 1,
  KDE_tinyDecoration = 2,
  KDE_noFocus = 256,
  KDE_standaloneMenuBar = 512,
  KDE_desktopIcon = 1024 ,
  KDE_staysOnTop = 2048
};


//************************************************************************
//****                  GLFW internal functions                       ****
//************************************************************************

//========================================================================
// _glfwWaitForMapNotify()
//========================================================================

Bool _glfwWaitForMapNotify( Display *d, XEvent *e, char *arg )
{
    return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
}


//========================================================================
// _glfwWaitForUnmapNotify()
//========================================================================

Bool _glfwWaitForUnmapNotify( Display *d, XEvent *e, char *arg )
{
    return (e->type == UnmapNotify) && (e->xmap.window == (Window)arg);
}


//========================================================================
// _glfwDisableDecorations() - Turn off window decorations
// Based on xawdecode: src/wmhooks.c
//========================================================================

#define MWM_HINTS_DECORATIONS (1L << 1)

static void _glfwDisableDecorations( void )
{
    int                   RemovedDecorations;
    Atom                  HintAtom;
    XSetWindowAttributes  attributes;

    RemovedDecorations = 0;

    // First try to set MWM hints
    HintAtom = XInternAtom( _glfwLibrary.Dpy, "_MOTIF_WM_HINTS", True );
    if ( HintAtom != None )
    {
        struct {
            unsigned long flags;
            unsigned long functions;
            unsigned long decorations;
                     long input_mode;
            unsigned long status;
        } MWMHints = { MWM_HINTS_DECORATIONS, 0, 0, 0, 0 };

        XChangeProperty( _glfwLibrary.Dpy, _glfwWin.Win, HintAtom, HintAtom,
                         32, PropModeReplace, (unsigned char *)&MWMHints,
                         sizeof(MWMHints)/4 );
        RemovedDecorations = 1;
    }

    // Now try to set KWM hints
    HintAtom = XInternAtom( _glfwLibrary.Dpy, "KWM_WIN_DECORATION", True );
    if ( HintAtom != None )
    {
        long KWMHints = KDE_tinyDecoration;

        XChangeProperty( _glfwLibrary.Dpy, _glfwWin.Win, HintAtom, HintAtom,
                         32, PropModeReplace, (unsigned char *)&KWMHints,
                         sizeof(KWMHints)/4 );
        RemovedDecorations = 1;
    }

    // Now try to set GNOME hints
    HintAtom = XInternAtom(_glfwLibrary.Dpy, "_WIN_HINTS", True );
    if ( HintAtom != None )
    {
        long GNOMEHints = 0;

        XChangeProperty( _glfwLibrary.Dpy, _glfwWin.Win, HintAtom, HintAtom,
                         32, PropModeReplace, (unsigned char *)&GNOMEHints,
                         sizeof(GNOMEHints)/4 );
        RemovedDecorations = 1;
    }

    // Now try to set KDE NET_WM hints
    HintAtom = XInternAtom( _glfwLibrary.Dpy, "_NET_WM_WINDOW_TYPE", True );
    if ( HintAtom != None )
    {
        Atom NET_WMHints[2];

        NET_WMHints[0] = XInternAtom( _glfwLibrary.Dpy, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE", True );
        /* define a fallback... */
        NET_WMHints[1] = XInternAtom( _glfwLibrary.Dpy, "_NET_WM_WINDOW_TYPE_NORMAL", True );

        XChangeProperty( _glfwLibrary.Dpy, _glfwWin.Win, HintAtom, XA_ATOM,
                         32, PropModeReplace, (unsigned char *)&NET_WMHints,
                         2 );
        RemovedDecorations = 1;
    }

    // Set ICCCM fullscreen WM hint
    HintAtom = XInternAtom( _glfwLibrary.Dpy, "_NET_WM_STATE", True );
    if ( HintAtom != None )
    {
        Atom NET_WMHints[1];

        NET_WMHints[0] = XInternAtom( _glfwLibrary.Dpy, "_NET_WM_STATE_FULLSCREEN", True );

        XChangeProperty( _glfwLibrary.Dpy, _glfwWin.Win, HintAtom, XA_ATOM,
                         32, PropModeReplace, (unsigned char *)&NET_WMHints, 1 );
    }


    // Did we sucessfully remove the window decorations?
    if( RemovedDecorations )
    {
        // Finally set the transient hints
        XSetTransientForHint( _glfwLibrary.Dpy, _glfwWin.Win, RootWindow(_glfwLibrary.Dpy, _glfwWin.Scrn) );
        XUnmapWindow( _glfwLibrary.Dpy, _glfwWin.Win );
        XMapWindow( _glfwLibrary.Dpy, _glfwWin.Win );
    }
    else
    {
        // The Butcher way of removing window decorations
        attributes.override_redirect = True;
        XChangeWindowAttributes( _glfwLibrary.Dpy, _glfwWin.Win,
                                 CWOverrideRedirect, &attributes );
        _glfwWin.OverrideRedirect = GL_TRUE;
    }
}


//========================================================================
// _glfwEnableDecorations() - Turn on window decorations
//========================================================================

static void _glfwEnableDecorations( void )
{
    int                   ActivatedDecorations;
    Atom                  HintAtom;

    // If this is an override redirect window, skip it...
    if( _glfwWin.OverrideRedirect )
    {
        return;
    }

    ActivatedDecorations = 0;

    // First try to unset MWM hints
    HintAtom = XInternAtom( _glfwLibrary.Dpy, "_MOTIF_WM_HINTS", True );
    if ( HintAtom != None )
    {
        XDeleteProperty( _glfwLibrary.Dpy, _glfwWin.Win, HintAtom );
        ActivatedDecorations = 1;
    }

    // Now try to unset KWM hints
    HintAtom = XInternAtom( _glfwLibrary.Dpy, "KWM_WIN_DECORATION", True );
    if ( HintAtom != None )
    {
        XDeleteProperty( _glfwLibrary.Dpy, _glfwWin.Win, HintAtom );
        ActivatedDecorations = 1;
    }

    // Now try to unset GNOME hints
    HintAtom = XInternAtom( _glfwLibrary.Dpy, "_WIN_HINTS", True );
    if ( HintAtom != None )
    {
        XDeleteProperty( _glfwLibrary.Dpy, _glfwWin.Win, HintAtom );
        ActivatedDecorations = 1;
    }

    // Now try to unset NET_WM hints
    HintAtom = XInternAtom( _glfwLibrary.Dpy, "_NET_WM_WINDOW_TYPE", True );
    if ( HintAtom != None )
    {
        Atom NET_WMHints = XInternAtom( _glfwLibrary.Dpy, "_NET_WM_WINDOW_TYPE_NORMAL", True);
        if( NET_WMHints != None )
        {
            XChangeProperty( _glfwLibrary.Dpy, _glfwWin.Win,
                            HintAtom, XA_ATOM, 32, PropModeReplace,
                            (unsigned char *)&NET_WMHints, 1 );
            ActivatedDecorations = 1;
        }
    }

    // Finally unset the transient hints if necessary
    if( ActivatedDecorations )
    {
        // NOTE: Does this work?
        XSetTransientForHint( _glfwLibrary.Dpy, _glfwWin.Win, None);
        XUnmapWindow( _glfwLibrary.Dpy, _glfwWin.Win );
        XMapWindow( _glfwLibrary.Dpy, _glfwWin.Win );
    }
}


//========================================================================
// _glfwChooseVisual() - We do our own function here, since
// glXChooseVisual does not behave as we want it to (not according to the
// GLFW specs)
//========================================================================

XVisualInfo * _glfwChooseVisual( Display *Dpy, int Screen, int r, int g,
    int b, int a, int d, int s, int ar, int ag, int ab, int aa, int aux,
				 int fsaa, int stereo)
{
    XVisualInfo *VI, *VI_list, VI_tmp;
    int  nitems_return, i;
    int  vi_gl, vi_rgba, vi_double, vi_stereo;
    int  vi_r, vi_g, vi_b, vi_a, vi_d, vi_s, vi_ar, vi_ag, vi_ab, vi_aa;
    int  vi_aux;
    int  color, accum, vi_accum;
    int  missing, color_diff, extra_diff;
    int  best_vis, best_missing, best_color_diff, best_extra_diff;
    int  samples, samplebuffers, vi_samples, vi_samplebuffers;

    // Get list of visuals for this screen & display
    VI_tmp.screen = Screen;
    VI_list = XGetVisualInfo( Dpy, VisualScreenMask, &VI_tmp,
                              &nitems_return );
    if( VI_list == NULL )
    {
        return NULL;
    }

    // Pick some prefered color depth if the user did not request a
    // specific depth (note: if the user did not request a specific color
    // depth, this will not be a driving demand, it's only here to avoid
    // selection randomness)
    color = (r > 0 || g > 0 || b > 0);
    if( !color )
    {
        r = g = b = 8;
    }

    // Make sure that stereo is 1 or 0
    stereo = stereo ? 1 : 0;

    // Convenience pre-calculation
    accum = (ar > 0 || ag > 0 || ab > 0 || aa > 0);
    
    samples = fsaa;
    samplebuffers = (fsaa > 0) ? 1 : 0;
    
    

    // Loop through list of visuals to find best match
    best_vis        = -1;
    best_missing    = 0x7fffffff;
    best_color_diff = 0x7fffffff;
    best_extra_diff = 0x7fffffff;
    for( i = 0; i < nitems_return; i ++ )
    {
        // We want GL, RGBA & DOUBLEBUFFER, and NOT STEREO / STEREO
        glXGetConfig( Dpy, &VI_list[i], GLX_USE_GL, &vi_gl );
        glXGetConfig( Dpy, &VI_list[i], GLX_RGBA, &vi_rgba );
        glXGetConfig( Dpy, &VI_list[i], GLX_DOUBLEBUFFER, &vi_double );
        glXGetConfig( Dpy, &VI_list[i], GLX_STEREO, &vi_stereo );
        vi_stereo = vi_stereo ? 1 : 0;
        if( vi_gl && vi_rgba && vi_double && (vi_stereo == stereo) )
        {
            // Get visual color parameters
            glXGetConfig( Dpy, &VI_list[i], GLX_RED_SIZE, &vi_r );
            glXGetConfig( Dpy, &VI_list[i], GLX_GREEN_SIZE, &vi_g );
            glXGetConfig( Dpy, &VI_list[i], GLX_BLUE_SIZE, &vi_b );

            // Get visual "extra" parameters
            glXGetConfig( Dpy, &VI_list[i], GLX_ALPHA_SIZE, &vi_a );
            glXGetConfig( Dpy, &VI_list[i], GLX_DEPTH_SIZE, &vi_d );
            glXGetConfig( Dpy, &VI_list[i], GLX_STENCIL_SIZE, &vi_s );
            glXGetConfig( Dpy, &VI_list[i], GLX_ACCUM_RED_SIZE, &vi_ar );
            glXGetConfig( Dpy, &VI_list[i], GLX_ACCUM_GREEN_SIZE, &vi_ag );
            glXGetConfig( Dpy, &VI_list[i], GLX_ACCUM_BLUE_SIZE, &vi_ab );
            glXGetConfig( Dpy, &VI_list[i], GLX_ACCUM_ALPHA_SIZE, &vi_aa );
            glXGetConfig( Dpy, &VI_list[i], GLX_AUX_BUFFERS, &vi_aux );
	    glXGetConfig( Dpy, &VI_list[i], GLX_SAMPLE_BUFFERS, &vi_samplebuffers );
	    glXGetConfig( Dpy, &VI_list[i], GLX_SAMPLES, &vi_samples );
	    
            vi_accum = (vi_ar > 0 || vi_ag > 0 || vi_ab > 0 || vi_aa > 0);

            // Check how many buffers are missing
            missing = 0;
            if( a > 0 && vi_a == 0 ) missing ++;
            if( d > 0 && vi_d == 0 ) missing ++;
            if( s > 0 && vi_s == 0 ) missing ++;
            if( accum && !vi_accum ) missing ++;
            if( aux > 0 && vi_aux == 0 ) missing ++;
	    if( samplebuffers > 0 && vi_samplebuffers == 0 ) missing ++;
	    

            // Calculate color diff
            color_diff = (r - vi_r) * (r - vi_r) +
                         (g - vi_g) * (g - vi_g) +
                         (b - vi_b) * (b - vi_b);

            // Calculate "extra" diff
            extra_diff = 0;
            if( a > 0 )
            {
                extra_diff += (a - vi_a) * (a - vi_a);
            }
            if( d > 0 )
            {
                extra_diff += (d - vi_d) * (d - vi_d);
            }
            if( s > 0 )
            {
                extra_diff += (s - vi_s) * (s - vi_s);
            }
            if( accum )
            {
                extra_diff += (ar - vi_ar) * (ar - vi_ar) +
                              (ag - vi_ag) * (ag - vi_ag) +
                              (ab - vi_ab) * (ab - vi_ab) +
                              (aa - vi_aa) * (aa - vi_aa);
            }
            if( aux > 0 )
            {
                extra_diff += (aux - vi_aux) * (aux - vi_aux);
            }
	    if( samples > 0 )
	    {
	      extra_diff += (samples - vi_samples) * (samples - vi_samples);
	      
	    }
            // Check if this is a better match. We implement some
            // complicated rules, by prioritizing in this order:
            //  1) Visuals with the least number of missing buffers always
            //     have priority
            //  2a) If (r,g,b)!=(0,0,0), color depth has priority over
            //      other buffers
            //  2b) If (r,g,b)==(0,0,0), other buffers have priority over
            //      color depth
            if( missing < best_missing )
            {
                best_vis = i;
            }
            else if( missing == best_missing )
            {
                if( color )
                {
                    if( (color_diff < best_color_diff) ||
                        (color_diff == best_color_diff &&
                         extra_diff < best_extra_diff) )
                    {
                        best_vis = i;
                    }
                }
                else
                {
                    if( (extra_diff < best_extra_diff) ||
                        (extra_diff == best_extra_diff &&
                         color_diff < best_color_diff) )
                    {
                        best_vis = i;
                    }
                }
            }
            if( best_vis == i )
            {
                best_missing    = missing;
                best_color_diff = color_diff;
                best_extra_diff = extra_diff;
            }
        }
    }

    // Copy best visual to a visual to return
    if( best_vis >= 0 )
    {
        VI = XGetVisualInfo( Dpy, VisualIDMask, &VI_list[ best_vis ],
                             &nitems_return );
    }
    else
    {
        VI = NULL;
    }

    // Free visuals list
    XFree( VI_list );

    return VI;
}


//========================================================================
// _glfwTranslateKey() - Translates an X Window key to internal coding
//========================================================================

static int _glfwTranslateKey( int keycode )
{
    KeySym key, key_lc, key_uc;

    // Try secondary keysym, for numeric keypad keys
    // Note: This way we always force "NumLock = ON", which at least
    // enables GLFW users to detect numeric keypad keys
    key = XKeycodeToKeysym( _glfwLibrary.Dpy, keycode, 1 );
    switch( key )
    {
        // Numeric keypad
        case XK_KP_0:         return GLFW_KEY_KP_0;
        case XK_KP_1:         return GLFW_KEY_KP_1;
        case XK_KP_2:         return GLFW_KEY_KP_2;
        case XK_KP_3:         return GLFW_KEY_KP_3;
        case XK_KP_4:         return GLFW_KEY_KP_4;
        case XK_KP_5:         return GLFW_KEY_KP_5;
        case XK_KP_6:         return GLFW_KEY_KP_6;
        case XK_KP_7:         return GLFW_KEY_KP_7;
        case XK_KP_8:         return GLFW_KEY_KP_8;
        case XK_KP_9:         return GLFW_KEY_KP_9;
        case XK_KP_Separator:
        case XK_KP_Decimal:   return GLFW_KEY_KP_DECIMAL;
        case XK_KP_Equal:     return GLFW_KEY_KP_EQUAL;
        case XK_KP_Enter:     return GLFW_KEY_KP_ENTER;
        default:              break;
    }

    // Now try pimary keysym
    key = XKeycodeToKeysym( _glfwLibrary.Dpy, keycode, 0 );
    switch( key )
    {
        // Special keys (non character keys)
        case XK_Escape:       return GLFW_KEY_ESC;
        case XK_Tab:          return GLFW_KEY_TAB;
        case XK_Shift_L:      return GLFW_KEY_LSHIFT;
        case XK_Shift_R:      return GLFW_KEY_RSHIFT;
        case XK_Control_L:    return GLFW_KEY_LCTRL;
        case XK_Control_R:    return GLFW_KEY_RCTRL;
        case XK_Meta_L:
        case XK_Alt_L:        return GLFW_KEY_LALT;
        case XK_Mode_switch:  // Mapped to Alt_R on many keyboards
        case XK_Meta_R:
        case XK_Alt_R:        return GLFW_KEY_RALT;
        case XK_KP_Delete:
        case XK_Delete:       return GLFW_KEY_DEL;
        case XK_BackSpace:    return GLFW_KEY_BACKSPACE;
        case XK_Return:       return GLFW_KEY_ENTER;
        case XK_KP_Home:
        case XK_Home:         return GLFW_KEY_HOME;
        case XK_KP_End:
        case XK_End:          return GLFW_KEY_END;
        case XK_KP_Page_Up:
        case XK_Page_Up:      return GLFW_KEY_PAGEUP;
        case XK_KP_Page_Down:
        case XK_Page_Down:    return GLFW_KEY_PAGEDOWN;
        case XK_KP_Insert:
        case XK_Insert:       return GLFW_KEY_INSERT;
        case XK_KP_Left:
        case XK_Left:         return GLFW_KEY_LEFT;
        case XK_KP_Right:
        case XK_Right:        return GLFW_KEY_RIGHT;
        case XK_KP_Down:
        case XK_Down:         return GLFW_KEY_DOWN;
        case XK_KP_Up:
        case XK_Up:           return GLFW_KEY_UP;
        case XK_F1:           return GLFW_KEY_F1;
        case XK_F2:           return GLFW_KEY_F2;
        case XK_F3:           return GLFW_KEY_F3;
        case XK_F4:           return GLFW_KEY_F4;
        case XK_F5:           return GLFW_KEY_F5;
        case XK_F6:           return GLFW_KEY_F6;
        case XK_F7:           return GLFW_KEY_F7;
        case XK_F8:           return GLFW_KEY_F8;
        case XK_F9:           return GLFW_KEY_F9;
        case XK_F10:          return GLFW_KEY_F10;
        case XK_F11:          return GLFW_KEY_F11;
        case XK_F12:          return GLFW_KEY_F12;
        case XK_F13:          return GLFW_KEY_F13;
        case XK_F14:          return GLFW_KEY_F14;
        case XK_F15:          return GLFW_KEY_F15;
        case XK_F16:          return GLFW_KEY_F16;
        case XK_F17:          return GLFW_KEY_F17;
        case XK_F18:          return GLFW_KEY_F18;
        case XK_F19:          return GLFW_KEY_F19;
        case XK_F20:          return GLFW_KEY_F20;
        case XK_F21:          return GLFW_KEY_F21;
        case XK_F22:          return GLFW_KEY_F22;
        case XK_F23:          return GLFW_KEY_F23;
        case XK_F24:          return GLFW_KEY_F24;
        case XK_F25:          return GLFW_KEY_F25;

        // Numeric keypad (should have been detected in secondary keysym!)
        case XK_KP_Divide:    return GLFW_KEY_KP_DIVIDE;
        case XK_KP_Multiply:  return GLFW_KEY_KP_MULTIPLY;
        case XK_KP_Subtract:  return GLFW_KEY_KP_SUBTRACT;
        case XK_KP_Add:       return GLFW_KEY_KP_ADD;
        case XK_KP_Equal:     return GLFW_KEY_KP_EQUAL;
        case XK_KP_Enter:     return GLFW_KEY_KP_ENTER;

        // The rest (should be printable keys)
        default:
            // Make uppercase
            XConvertCase( key, &key_lc, &key_uc );
            key = key_uc;

            // Valid ISO 8859-1 character?
            if( (key >=  32 && key <= 126) ||
                (key >= 160 && key <= 255) )
            {
                return (int) key;
            }
            return GLFW_KEY_UNKNOWN;
    }
}


//========================================================================
// _glfwTranslateChar() - Translates an X Window event to Unicode
//========================================================================

static int _glfwTranslateChar( XKeyEvent *event )
{
    KeySym keysym;

    // Get X11 keysym
    XLookupString( event, NULL, 0, &keysym, NULL );

    // Convert to Unicode (see x11_keysym2unicode.c)
    return (int) _glfwKeySym2Unicode( keysym );
}



//========================================================================
// Get next X event (called by glfwPollEvents)
//========================================================================

static int _glfwGetNextEvent( void )
{
    XEvent event, next_event;

    // Pull next event from event queue
    XNextEvent( _glfwLibrary.Dpy, &event );

    // Handle certain window messages
    switch( event.type )
    {
        // Is a key being pressed?
        case KeyPress:
	{
            // Translate and report key press
            _glfwInputKey( _glfwTranslateKey( event.xkey.keycode ), GLFW_PRESS );

            // Translate and report character input
            if( _glfwWin.CharCallback )
            {
                _glfwInputChar( _glfwTranslateChar( &event.xkey ), GLFW_PRESS );
            }
            break;
	}

        // Is a key being released?
        case KeyRelease:
	{
            // Do not report key releases for key repeats. For key repeats
            // we will get KeyRelease/KeyPress pairs with identical time
            // stamps. User selected key repeat filtering is handled in
            // _glfwInputKey()/_glfwInputChar().
            if( XEventsQueued( _glfwLibrary.Dpy, QueuedAfterReading ) )
            {
                XPeekEvent( _glfwLibrary.Dpy, &next_event );
                if( next_event.type == KeyPress &&
                    next_event.xkey.window == event.xkey.window &&
                    next_event.xkey.keycode == event.xkey.keycode &&
                    next_event.xkey.time == event.xkey.time )
                {
                    // Do not report anything for this event
                    break;
                }
            }

            // Translate and report key release
            _glfwInputKey( _glfwTranslateKey( event.xkey.keycode ), GLFW_RELEASE );

            // Translate and report character input
            if( _glfwWin.CharCallback )
            {
                _glfwInputChar( _glfwTranslateChar( &event.xkey ), GLFW_RELEASE );
            }
            break;
	}

        // Were any of the mouse-buttons pressed?
        case ButtonPress:
	{
            if( event.xbutton.button == Button1 )
            {
                _glfwInputMouseClick( GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS );
            }
            else if( event.xbutton.button == Button2 )
            {
                _glfwInputMouseClick( GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS );
            }
            else if( event.xbutton.button == Button3 )
            {
                _glfwInputMouseClick( GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS );
            }

            // XFree86 3.3.2 and later translates mouse wheel up/down into
            // mouse button 4 & 5 presses
            else if( event.xbutton.button == Button4 )
            {
                _glfwInput.WheelPos++;  // To verify: is this up or down?
                if( _glfwWin.MouseWheelCallback )
                {
                    _glfwWin.MouseWheelCallback( _glfwInput.WheelPos );
                }
            }
            else if( event.xbutton.button == Button5 )
            {
                _glfwInput.WheelPos--;
                if( _glfwWin.MouseWheelCallback )
                {
                    _glfwWin.MouseWheelCallback( _glfwInput.WheelPos );
                }
            }
            break;
	}

        // Were any of the mouse-buttons released?
        case ButtonRelease:
	{
            if( event.xbutton.button == Button1 )
            {
                _glfwInputMouseClick( GLFW_MOUSE_BUTTON_LEFT,
                                      GLFW_RELEASE );
            }
            else if( event.xbutton.button == Button2 )
            {
                _glfwInputMouseClick( GLFW_MOUSE_BUTTON_MIDDLE,
                                      GLFW_RELEASE );
            }
            else if( event.xbutton.button == Button3 )
            {
                _glfwInputMouseClick( GLFW_MOUSE_BUTTON_RIGHT,
                                      GLFW_RELEASE );
            }
            break;
	}

        // Was the mouse moved?
        case MotionNotify:
	{
            if( event.xmotion.x != _glfwInput.CursorPosX ||
                event.xmotion.y != _glfwInput.CursorPosY )
            {
                if( _glfwWin.MouseLock )
                {
                    _glfwInput.MousePosX += event.xmotion.x -
                                            _glfwInput.CursorPosX;
                    _glfwInput.MousePosY += event.xmotion.y -
                                            _glfwInput.CursorPosY;
                }
                else
                {
                    _glfwInput.MousePosX = event.xmotion.x;
                    _glfwInput.MousePosY = event.xmotion.y;
                }
                _glfwInput.CursorPosX = event.xmotion.x;
                _glfwInput.CursorPosY = event.xmotion.y;
                _glfwInput.MouseMoved = GL_TRUE;

                // Call user callback function
                if( _glfwWin.MousePosCallback )
                {
                    _glfwWin.MousePosCallback( _glfwInput.MousePosX,
                                               _glfwInput.MousePosY );
                }
            }
            break;
	}

        // Was the window resized?
        case ConfigureNotify:
	{
            if( event.xconfigure.width != _glfwWin.Width ||
                event.xconfigure.height != _glfwWin.Height )
            {
                _glfwWin.Width = event.xconfigure.width;
                _glfwWin.Height = event.xconfigure.height;
                if( _glfwWin.WindowSizeCallback )
                {
                    _glfwWin.WindowSizeCallback( _glfwWin.Width,
                                                 _glfwWin.Height );
                }
            }
            break;
	}

        // Was the window closed by the window manager?
        case ClientMessage:
	{
            if( (Atom) event.xclient.data.l[ 0 ] == _glfwWin.WMDeleteWindow )
            {
                return GL_TRUE;
            }

	    if( (Atom) event.xclient.data.l[ 0 ] == _glfwWin.WMPing )
	    {
		XSendEvent( _glfwLibrary.Dpy,
			    RootWindow( _glfwLibrary.Dpy, _glfwWin.VI->screen ),
			    False, SubstructureNotifyMask | SubstructureRedirectMask, &event );
	    }
            break;
	}

        // Was the window mapped (un-iconified)?
        case MapNotify:
            _glfwWin.MapNotifyCount++;
            break;

        // Was the window unmapped (iconified)?
        case UnmapNotify:
            _glfwWin.MapNotifyCount--;
            break;

        // Was the window activated?
        case FocusIn:
            _glfwWin.FocusInCount++;
            break;

        // Was the window de-activated?
        case FocusOut:
            _glfwWin.FocusInCount--;
            break;

        // Was the window contents damaged?
        case Expose:
	{
            // Call user callback function
            if( _glfwWin.WindowRefreshCallback )
            {
                _glfwWin.WindowRefreshCallback();
            }
            break;
	}

        // Was the window destroyed?
        case DestroyNotify:
            return GL_TRUE;

        default:
	{
#if defined( _GLFW_HAS_XRANDR )
	    switch( event.type - _glfwLibrary.XRandR.EventBase )
	    {
		case RRScreenChangeNotify:
		{
		    // Show XRandR that we really care
		    XRRUpdateConfiguration( &event );
		    break;
		}
	    }
#endif
            break;
	}
    }

    // The window was not destroyed
    return GL_FALSE;
}


//========================================================================
// _glfwCreateNULLCursor() - Create a blank cursor (for locked mouse mode)
//========================================================================

Cursor _glfwCreateNULLCursor( Display *display, Window root )
{
    Pixmap    cursormask;
    XGCValues xgc;
    GC        gc;
    XColor    col;
    Cursor    cursor;

    cursormask = XCreatePixmap( display, root, 1, 1, 1 );
    xgc.function = GXclear;
    gc = XCreateGC( display, cursormask, GCFunction, &xgc );
    XFillRectangle( display, cursormask, gc, 0, 0, 1, 1 );
    col.pixel = 0;
    col.red = 0;
    col.flags = 4;
    cursor = XCreatePixmapCursor( display, cursormask, cursormask,
                                  &col,&col, 0,0 );
    XFreePixmap( display, cursormask );
    XFreeGC( display, gc );

    return cursor;
}


//========================================================================
// _glfwInitGLXExtensions() - Initialize GLX-specific extensions
//========================================================================

static void _glfwInitGLXExtensions( void )
{
    int has_swap_control;

    // Initialize OpenGL extension: GLX_SGI_swap_control
    has_swap_control = _glfwPlatformExtensionSupported(
                           "GLX_SGI_swap_control"
                       );

    if( has_swap_control )
    {
        _glfwWin.SwapInterval = (GLXSWAPINTERVALSGI_T)
            _glfw_glXGetProcAddress( (GLubyte*) "glXSwapIntervalSGI" );
    }
    else
    {
        _glfwWin.SwapInterval = NULL;
    }
}



//************************************************************************
//****               Platform implementation functions                ****
//************************************************************************

//========================================================================
// _glfwPlatformOpenWindow() - Here is where the window is created, and
// the OpenGL rendering context is created
//========================================================================

int _glfwPlatformOpenWindow( int width, int height, int redbits,
    int greenbits, int bluebits, int alphabits, int depthbits,
    int stencilbits, int mode, _GLFWhints* hints )
{
    Colormap    cmap;
    XSetWindowAttributes wa;
    XEvent      event;
    Atom protocols[2];

    // Clear platform specific GLFW window state
    _glfwWin.VI               = NULL;
    _glfwWin.CX               = (GLXContext)0;
    _glfwWin.Win              = (Window)0;
    _glfwWin.Hints            = NULL;
    _glfwWin.PointerGrabbed   = GL_FALSE;
    _glfwWin.KeyboardGrabbed  = GL_FALSE;
    _glfwWin.OverrideRedirect = GL_FALSE;
    _glfwWin.FS.ModeChanged   = GL_FALSE;
    _glfwWin.Saver.Changed    = GL_FALSE;
    _glfwWin.RefreshRate      = hints->RefreshRate;

    // Fullscreen & screen saver settings
    // Check if GLX is supported on this display
    if( !glXQueryExtension( _glfwLibrary.Dpy, NULL, NULL ) )
    {
        _glfwPlatformCloseWindow();
        return GL_FALSE;
    }

    // Get screen ID for this window
    _glfwWin.Scrn = _glfwLibrary.DefaultScreen;

    // Get an appropriate visual
    _glfwWin.VI = _glfwChooseVisual( _glfwLibrary.Dpy,
                                     _glfwWin.Scrn,
                                     redbits, greenbits, bluebits,
                                     alphabits, depthbits, stencilbits,
                                     hints->AccumRedBits, hints->AccumGreenBits,
                                     hints->AccumBlueBits, hints->AccumAlphaBits,
                                     hints->AuxBuffers, hints->Samples, hints->Stereo );
    if( _glfwWin.VI == NULL )
    {
        _glfwPlatformCloseWindow();
        return GL_FALSE;
    }

    // Create a GLX context
    _glfwWin.CX = glXCreateContext( _glfwLibrary.Dpy, _glfwWin.VI, 0, GL_TRUE );
    if( _glfwWin.CX == NULL )
    {
        _glfwPlatformCloseWindow();
        return GL_FALSE;
    }

    // Create a colormap
    cmap = XCreateColormap( _glfwLibrary.Dpy, RootWindow( _glfwLibrary.Dpy,
               _glfwWin.VI->screen), _glfwWin.VI->visual, AllocNone );

    // Do we want fullscreen?
    if( mode == GLFW_FULLSCREEN )
    {
        // Change video mode
        _glfwSetVideoMode( _glfwWin.Scrn, &_glfwWin.Width,
                           &_glfwWin.Height, &_glfwWin.RefreshRate );

        // Remember old screen saver settings
        XGetScreenSaver( _glfwLibrary.Dpy, &_glfwWin.Saver.Timeout,
                         &_glfwWin.Saver.Interval, &_glfwWin.Saver.Blanking,
                         &_glfwWin.Saver.Exposure );

        // Disable screen saver
        XSetScreenSaver( _glfwLibrary.Dpy, 0, 0, DontPreferBlanking,
                         DefaultExposures );
    }

    // Attributes for window
    wa.colormap = cmap;
    wa.border_pixel = 0;
    wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
        PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
        ExposureMask | FocusChangeMask | VisibilityChangeMask;

    // Create a window
    _glfwWin.Win = XCreateWindow(
        _glfwLibrary.Dpy,
        RootWindow( _glfwLibrary.Dpy, _glfwWin.VI->screen ),
        0, 0,                            // Upper left corner
        _glfwWin.Width, _glfwWin.Height, // Width, height
        0,                               // Borderwidth
        _glfwWin.VI->depth,              // Depth
        InputOutput,
        _glfwWin.VI->visual,
        CWBorderPixel | CWColormap | CWEventMask,
        &wa
    );
    if( !_glfwWin.Win )
    {
        _glfwPlatformCloseWindow();
        return GL_FALSE;
    }

    // Set window & icon name
    _glfwPlatformSetWindowTitle( "Teeworlds" );

    // Get the delete window WM protocol atom
    _glfwWin.WMDeleteWindow = XInternAtom( _glfwLibrary.Dpy,
                                           "WM_DELETE_WINDOW",
                                           False );

    // Get the ping WM protocol atom
    _glfwWin.WMPing = XInternAtom( _glfwLibrary.Dpy, "_NET_WM_PING", False );

    protocols[0] = _glfwWin.WMDeleteWindow;
    protocols[1] = _glfwWin.WMPing;

    // Allow us to trap the Window Close protocol
    XSetWMProtocols( _glfwLibrary.Dpy, _glfwWin.Win, protocols,
                     sizeof(protocols) / sizeof(Atom) );

    // Remove window decorations for fullscreen windows
    if( mode == GLFW_FULLSCREEN )
    {
        _glfwDisableDecorations();
    }

    _glfwWin.Hints = XAllocSizeHints();

    if( hints->WindowNoResize )
    {
	_glfwWin.Hints->flags |= (PMinSize | PMaxSize);
        _glfwWin.Hints->min_width = _glfwWin.Hints->max_width = _glfwWin.Width;
        _glfwWin.Hints->min_height = _glfwWin.Hints->max_height = _glfwWin.Height;
    }

    if( mode == GLFW_FULLSCREEN )
    {
	_glfwWin.Hints->flags |= PPosition;
	_glfwWin.Hints->x = 0;
	_glfwWin.Hints->y = 0;
    }

    XSetWMNormalHints( _glfwLibrary.Dpy, _glfwWin.Win, _glfwWin.Hints );

    // Map window
    XMapWindow( _glfwLibrary.Dpy, _glfwWin.Win );

    // Wait for map notification
    XIfEvent( _glfwLibrary.Dpy, &event, _glfwWaitForMapNotify,
              (char*)_glfwWin.Win );

    // Make sure that our window ends up on top of things
    XRaiseWindow( _glfwLibrary.Dpy, _glfwWin.Win );

    // Fullscreen mode "post processing"
    if( mode == GLFW_FULLSCREEN )
    {
#if defined( _GLFW_HAS_XRANDR )
	// Request screen change notifications
	if( _glfwLibrary.XRandR.Available )
	{
	    XRRSelectInput( _glfwLibrary.Dpy,
	                    _glfwWin.Win,
			    RRScreenChangeNotifyMask );
	}
#endif

        // Force window position/size (some WMs do their own window
        // geometry, which we want to override)
        XMoveWindow( _glfwLibrary.Dpy, _glfwWin.Win, 0, 0 );
        XResizeWindow( _glfwLibrary.Dpy, _glfwWin.Win, _glfwWin.Width,
                       _glfwWin.Height );

        // Grab keyboard
        if( XGrabKeyboard( _glfwLibrary.Dpy, _glfwWin.Win, True,
                           GrabModeAsync, GrabModeAsync, CurrentTime ) ==
            GrabSuccess )
        {
            _glfwWin.KeyboardGrabbed = GL_TRUE;
        }

        // Grab mouse cursor
        if( XGrabPointer( _glfwLibrary.Dpy, _glfwWin.Win, True,
                          ButtonPressMask | ButtonReleaseMask |
                          PointerMotionMask, GrabModeAsync, GrabModeAsync,
                          _glfwWin.Win, None, CurrentTime ) ==
            GrabSuccess )
        {
            _glfwWin.PointerGrabbed = GL_TRUE;
        }

        // Try to get window inside viewport (for virtual displays) by
        // moving the mouse cursor to the upper left corner (and then to
        // the center) - this works for XFree86
        XWarpPointer( _glfwLibrary.Dpy, None, _glfwWin.Win, 0,0,0,0, 0,0 );
        XWarpPointer( _glfwLibrary.Dpy, None, _glfwWin.Win, 0,0,0,0,
                      _glfwWin.Width/2, _glfwWin.Height/2 );
    }

    // Connect the context to the window
    glXMakeCurrent( _glfwLibrary.Dpy, _glfwWin.Win, _glfwWin.CX );

    // Start by clearing the front buffer to black (avoid ugly desktop
    // remains in our OpenGL window)
    glClear( GL_COLOR_BUFFER_BIT );
    glXSwapBuffers( _glfwLibrary.Dpy, _glfwWin.Win );

    // Initialize GLX-specific OpenGL extensions
    _glfwInitGLXExtensions();

    return GL_TRUE;
}


//========================================================================
// Properly kill the window/video display
//========================================================================

void _glfwPlatformCloseWindow( void )
{
#if defined( _GLFW_HAS_XRANDR )
    XRRScreenConfiguration *sc;
    Window root;
#endif

    // Free WM size hints
    if( _glfwWin.Hints )
    {
	XFree( _glfwWin.Hints );
	_glfwWin.Hints = NULL;
    }

    // Do we have a rendering context?
    if( _glfwWin.CX )
    {
        // Release the context
        glXMakeCurrent( _glfwLibrary.Dpy, None, NULL );

        // Delete the context
        glXDestroyContext( _glfwLibrary.Dpy, _glfwWin.CX );
        _glfwWin.CX = NULL;
    }

    // Ungrab pointer and/or keyboard?
    if( _glfwWin.KeyboardGrabbed )
    {
        XUngrabKeyboard( _glfwLibrary.Dpy, CurrentTime );
        _glfwWin.KeyboardGrabbed = GL_FALSE;
    }
    if( _glfwWin.PointerGrabbed )
    {
        XUngrabPointer( _glfwLibrary.Dpy, CurrentTime );
        _glfwWin.PointerGrabbed = GL_FALSE;
    }

    // Do we have a window?
    if( _glfwWin.Win )
    {
        // Unmap the window
        XUnmapWindow( _glfwLibrary.Dpy, _glfwWin.Win );

        // Destroy the window
        XDestroyWindow( _glfwLibrary.Dpy, _glfwWin.Win );
        _glfwWin.Win = (Window) 0;
    }

    // Did we change the fullscreen resolution?
    if( _glfwWin.FS.ModeChanged )
    {
#if defined( _GLFW_HAS_XRANDR )
	if( _glfwLibrary.XRandR.Available )
	{
	    root = RootWindow( _glfwLibrary.Dpy, _glfwWin.Scrn );
	    sc = XRRGetScreenInfo( _glfwLibrary.Dpy, root );

	    XRRSetScreenConfig( _glfwLibrary.Dpy,
	                        sc,
				root,
			        _glfwWin.FS.OldSizeID,
				_glfwWin.FS.OldRotation,
				CurrentTime );

	    XRRFreeScreenConfigInfo( sc );
	}
#elif defined( _GLFW_HAS_XF86VIDMODE )
        if( _glfwLibrary.XF86VidMode.Available )
        {
            // Unlock mode switch
            XF86VidModeLockModeSwitch( _glfwLibrary.Dpy,
                                       _glfwWin.Scrn,
                                       0 );

            // Change the video mode back to the old mode
            XF86VidModeSwitchToMode( _glfwLibrary.Dpy,
                _glfwWin.Scrn, &_glfwWin.FS.OldMode );
        }
#endif
        _glfwWin.FS.ModeChanged = GL_FALSE;
    }

    // Did we change the screen saver setting?
    if( _glfwWin.Saver.Changed )
    {
        // Restore old screen saver settings
        XSetScreenSaver( _glfwLibrary.Dpy, _glfwWin.Saver.Timeout,
                         _glfwWin.Saver.Interval, _glfwWin.Saver.Blanking,
                         _glfwWin.Saver.Exposure );
        _glfwWin.Saver.Changed = GL_FALSE;
    }

    XSync( _glfwLibrary.Dpy, True );
}


//========================================================================
// _glfwPlatformSetWindowTitle() - Set the window title.
//========================================================================

void _glfwPlatformSetWindowTitle( const char *title )
{
    // Set window & icon title
    XStoreName( _glfwLibrary.Dpy, _glfwWin.Win, title );
    XSetIconName( _glfwLibrary.Dpy, _glfwWin.Win, title );
}


//========================================================================
// _glfwPlatformSetWindowSize() - Set the window size.
//========================================================================

void _glfwPlatformSetWindowSize( int width, int height )
{
    int     mode = 0, rate, sizechanged = GL_FALSE;
    GLint   drawbuffer;
    GLfloat clearcolor[4];

    rate = _glfwWin.RefreshRate;

    // If we are in fullscreen mode, get some info about the current mode
    if( _glfwWin.Fullscreen )
    {
        // Get closest match for target video mode
        mode = _glfwGetClosestVideoMode( _glfwWin.Scrn, &width, &height, &rate );
    }

    if( _glfwWin.WindowNoResize )
    {
        _glfwWin.Hints->min_width = _glfwWin.Hints->max_width = width;
        _glfwWin.Hints->min_height = _glfwWin.Hints->max_height = height;
    }

    XSetWMNormalHints( _glfwLibrary.Dpy, _glfwWin.Win, _glfwWin.Hints );

    // Change window size before changing fullscreen mode?
    if( _glfwWin.Fullscreen && (width > _glfwWin.Width) )
    {
        XResizeWindow( _glfwLibrary.Dpy, _glfwWin.Win, width, height );
        sizechanged = GL_TRUE;
    }

    // Change fullscreen video mode?
    if( _glfwWin.Fullscreen )
    {
        // Change video mode (keeping current rate)
        _glfwSetVideoModeMODE( _glfwWin.Scrn, mode, _glfwWin.RefreshRate );

        // Clear the front buffer to black (avoid ugly desktop remains in
        // our OpenGL window)
        glGetIntegerv( GL_DRAW_BUFFER, &drawbuffer );
        glGetFloatv( GL_COLOR_CLEAR_VALUE, clearcolor );
        glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
        glClear( GL_COLOR_BUFFER_BIT );
        if( drawbuffer == GL_BACK )
        {
            glXSwapBuffers( _glfwLibrary.Dpy, _glfwWin.Win );
        }
        glClearColor( clearcolor[0], clearcolor[1], clearcolor[2],
                      clearcolor[3] );
    }

    // Set window size (if not already changed)
    if( !sizechanged )
    {
        XResizeWindow( _glfwLibrary.Dpy, _glfwWin.Win, width, height );
    }
}


//========================================================================
// _glfwPlatformSetWindowPos() - Set the window position.
//========================================================================

void _glfwPlatformSetWindowPos( int x, int y )
{
    // Set window position
    XMoveWindow( _glfwLibrary.Dpy, _glfwWin.Win, x, y );
}


//========================================================================
// _glfwPlatformIconfyWindow() - Window iconification
//========================================================================

void _glfwPlatformIconifyWindow( void )
{
    // We can't do this for override redirect windows
    if( _glfwWin.OverrideRedirect )
    {
        return;
    }

    // In fullscreen mode, we need to restore the desktop video mode
    if( _glfwWin.Fullscreen )
    {
#if defined( _GLFW_HAS_XF86VIDMODE )
        if( _glfwLibrary.XF86VidMode.Available )
        {
            // Unlock mode switch
            XF86VidModeLockModeSwitch( _glfwLibrary.Dpy,
                                       _glfwWin.Scrn,
                                       0 );

            // Change the video mode back to the old mode
            XF86VidModeSwitchToMode( _glfwLibrary.Dpy,
                _glfwWin.Scrn, &_glfwWin.FS.OldMode );
        }
#endif
        _glfwWin.FS.ModeChanged = GL_FALSE;
    }

    // Show mouse pointer
    if( _glfwWin.PointerHidden )
    {
        XUndefineCursor( _glfwLibrary.Dpy, _glfwWin.Win );
        _glfwWin.PointerHidden = GL_FALSE;
    }

    // Un-grab mouse pointer
    if( _glfwWin.PointerGrabbed )
    {
        XUngrabPointer( _glfwLibrary.Dpy, CurrentTime );
        _glfwWin.PointerGrabbed = GL_FALSE;
    }

    // Iconify window
    XIconifyWindow( _glfwLibrary.Dpy, _glfwWin.Win,
                    _glfwWin.Scrn );

    // Window is now iconified
    _glfwWin.Iconified = GL_TRUE;
}


//========================================================================
// Window un-iconification
//========================================================================

void _glfwPlatformRestoreWindow( void )
{
    // We can't do this for override redirect windows
    if( _glfwWin.OverrideRedirect )
    {
        return;
    }

    // In fullscreen mode, change back video mode to user selected mode
    if( _glfwWin.Fullscreen )
    {
        _glfwSetVideoMode( _glfwWin.Scrn,
	                   &_glfwWin.Width, &_glfwWin.Height, &_glfwWin.RefreshRate );
    }

    // Un-iconify window
    XMapWindow( _glfwLibrary.Dpy, _glfwWin.Win );

    // In fullscreen mode...
    if( _glfwWin.Fullscreen )
    {
        // Make sure window is in upper left corner
        XMoveWindow( _glfwLibrary.Dpy, _glfwWin.Win, 0, 0 );

        // Get input focus
        XSetInputFocus( _glfwLibrary.Dpy, _glfwWin.Win, RevertToParent,
                        CurrentTime );
    }

    // Lock mouse, if necessary
    if( _glfwWin.MouseLock )
    {
        // Hide cursor
        if( !_glfwWin.PointerHidden )
        {
            XDefineCursor( _glfwLibrary.Dpy, _glfwWin.Win,
                           _glfwCreateNULLCursor( _glfwLibrary.Dpy,
                                                  _glfwWin.Win ) );
            _glfwWin.PointerHidden = GL_TRUE;
        }

        // Grab cursor
        if( !_glfwWin.PointerGrabbed )
        {
            if( XGrabPointer( _glfwLibrary.Dpy, _glfwWin.Win, True,
                              ButtonPressMask | ButtonReleaseMask |
                              PointerMotionMask, GrabModeAsync,
                              GrabModeAsync, _glfwWin.Win, None,
                              CurrentTime ) == GrabSuccess )
            {
                _glfwWin.PointerGrabbed = GL_TRUE;
            }
        }
    }

    // Window is no longer iconified
    _glfwWin.Iconified = GL_FALSE;
}


//========================================================================
// _glfwPlatformSwapBuffers() - Swap buffers (double-buffering) and poll
// any new events.
//========================================================================

void _glfwPlatformSwapBuffers( void )
{
    // Update display-buffer
    glXSwapBuffers( _glfwLibrary.Dpy, _glfwWin.Win );
}


//========================================================================
// _glfwPlatformSwapInterval() - Set double buffering swap interval
//========================================================================

void _glfwPlatformSwapInterval( int interval )
{
    if( _glfwWin.SwapInterval )
    {
        _glfwWin.SwapInterval( interval );
    }
}


//========================================================================
// _glfwPlatformRefreshWindowParams()
//========================================================================

void _glfwPlatformRefreshWindowParams( void )
{
#if defined( _GLFW_HAS_XRANDR )
    XRRScreenConfiguration *sc;
#elif defined( _GLFW_HAS_XF86VIDMODE )
    XF86VidModeModeLine modeline;
    int dotclock;
    float pixels_per_second, pixels_per_frame;
#endif
    int sample_buffers;

    // AFAIK, there is no easy/sure way of knowing if OpenGL is hardware
    // accelerated
    _glfwWin.Accelerated = GL_TRUE;

    // "Standard" window parameters
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_RED_SIZE,
                  &_glfwWin.RedBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_GREEN_SIZE,
                  &_glfwWin.GreenBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_BLUE_SIZE,
                  &_glfwWin.BlueBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_ALPHA_SIZE,
                  &_glfwWin.AlphaBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_DEPTH_SIZE,
                  &_glfwWin.DepthBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_STENCIL_SIZE,
                  &_glfwWin.StencilBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_ACCUM_RED_SIZE,
                  &_glfwWin.AccumRedBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_ACCUM_GREEN_SIZE,
                  &_glfwWin.AccumGreenBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_ACCUM_BLUE_SIZE,
                  &_glfwWin.AccumBlueBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_ACCUM_ALPHA_SIZE,
                  &_glfwWin.AccumAlphaBits );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_AUX_BUFFERS,
                  &_glfwWin.AuxBuffers );

    // Get stereo rendering setting
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_STEREO,
                  &_glfwWin.Stereo );
    _glfwWin.Stereo = _glfwWin.Stereo ? 1 : 0;

    // Get multisample buffer samples
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_SAMPLES,
		  &_glfwWin.Samples );
    glXGetConfig( _glfwLibrary.Dpy, _glfwWin.VI, GLX_SAMPLE_BUFFERS, 
		  &sample_buffers );
    if( sample_buffers == 0 )
      _glfwWin.Samples = 0;
    
    // Default to refresh rate unknown (=0 according to GLFW spec)
    _glfwWin.RefreshRate = 0;
		  
    // Retrieve refresh rate, if possible
#if defined( _GLFW_HAS_XRANDR )
    if( _glfwLibrary.XRandR.Available )
    {
	sc = XRRGetScreenInfo( _glfwLibrary.Dpy,
	                       RootWindow( _glfwLibrary.Dpy, _glfwWin.Scrn ) );
	_glfwWin.RefreshRate = XRRConfigCurrentRate( sc );
	XRRFreeScreenConfigInfo( sc );
    }
#elif defined( _GLFW_HAS_XF86VIDMODE )
    if( _glfwLibrary.XF86VidMode.Available )
    {
        // Use the XF86VidMode extension to get current video mode
        XF86VidModeGetModeLine( _glfwLibrary.Dpy, _glfwWin.Scrn,
                                &dotclock, &modeline );
        pixels_per_second = 1000.0f * (float) dotclock;
        pixels_per_frame  = (float) modeline.htotal * modeline.vtotal;
        _glfwWin.RefreshRate = (int)(pixels_per_second/pixels_per_frame+0.5);
    }
#endif
}


//========================================================================
// _glfwPlatformPollEvents() - Poll for new window and input events
//========================================================================

void _glfwPlatformPollEvents( void )
{
    int winclosed = GL_FALSE;

    // Flag that the cursor has not moved
    _glfwInput.MouseMoved = GL_FALSE;

    // Clear MapNotify and FocusIn counts
    _glfwWin.MapNotifyCount = 0;
    _glfwWin.FocusInCount = 0;

    // Use XSync to synchronise events to the X display.
    // I don't know if this can have a serious performance impact. My
    // benchmarks with a GeForce card under Linux shows no difference with
    // or without XSync, but when the GL window is rendered over a slow
    // network I have noticed bad event syncronisation problems when XSync
    // is not used, so I decided to use it.
    XSync( _glfwLibrary.Dpy, False );

    // Empty the window event queue
    while( XPending( _glfwLibrary.Dpy ) )
    {
        if( _glfwGetNextEvent() )
        {
            winclosed = GL_TRUE;
        }
    }

    // Did we get mouse movement in locked cursor mode?
    if( _glfwInput.MouseMoved && _glfwWin.MouseLock )
    {
        int maxx, minx, maxy, miny;

        // Calculate movement threshold
        minx = _glfwWin.Width / 4;
        maxx = (_glfwWin.Width * 3) / 4;
        miny = _glfwWin.Height / 4;
        maxy = (_glfwWin.Height * 3) / 4;

        // Did the mouse cursor move beyond our movement threshold
        if(_glfwInput.CursorPosX < minx || _glfwInput.CursorPosX > maxx ||
           _glfwInput.CursorPosY < miny || _glfwInput.CursorPosY > maxy)
        {
            // Move the mouse pointer back to the window center so that it
            // does not wander off...
            _glfwPlatformSetMouseCursorPos( _glfwWin.Width/2,
                                            _glfwWin.Height/2 );
            XSync( _glfwLibrary.Dpy, False );
        }
    }

    // Was the window (un)iconified?
    if( _glfwWin.MapNotifyCount < 0 && !_glfwWin.Iconified )
    {
        // Show mouse pointer
        if( _glfwWin.PointerHidden )
        {
            XUndefineCursor( _glfwLibrary.Dpy, _glfwWin.Win );
            _glfwWin.PointerHidden = GL_FALSE;
        }

        // Un-grab mouse pointer
        if( _glfwWin.PointerGrabbed )
        {
            XUngrabPointer( _glfwLibrary.Dpy, CurrentTime );
            _glfwWin.PointerGrabbed = GL_FALSE;
        }

        _glfwWin.Iconified = GL_TRUE;
    }
    else if( _glfwWin.MapNotifyCount > 0 && _glfwWin.Iconified )
    {
        // Restore fullscreen mode properties
        if( _glfwWin.Fullscreen )
        {
            // Change back video mode to user selected mode
            _glfwSetVideoMode( _glfwWin.Scrn, &_glfwWin.Width,
                               &_glfwWin.Height, &_glfwWin.RefreshRate );

            // Disable window manager decorations
            _glfwEnableDecorations();

            // Make sure window is in upper left corner
            XMoveWindow( _glfwLibrary.Dpy, _glfwWin.Win, 0, 0 );

            // Get input focus
            XSetInputFocus( _glfwLibrary.Dpy, _glfwWin.Win,
                            RevertToParent, CurrentTime );
        }

        // Hide cursor if necessary
        if( _glfwWin.MouseLock && !_glfwWin.PointerHidden )
        {
            if( !_glfwWin.PointerHidden )
            {
                XDefineCursor( _glfwLibrary.Dpy, _glfwWin.Win,
                    _glfwCreateNULLCursor( _glfwLibrary.Dpy,
                                           _glfwWin.Win ) );
                _glfwWin.PointerHidden = GL_TRUE;
            }
        }

        // Grab cursor if necessary
        if( (_glfwWin.MouseLock || _glfwWin.Fullscreen) &&
            !_glfwWin.PointerGrabbed )
        {
            if( XGrabPointer( _glfwLibrary.Dpy, _glfwWin.Win, True,
                    ButtonPressMask | ButtonReleaseMask |
                    PointerMotionMask, GrabModeAsync,
                    GrabModeAsync, _glfwWin.Win, None,
                    CurrentTime ) == GrabSuccess )
            {
                _glfwWin.PointerGrabbed = GL_TRUE;
            }
        }

        _glfwWin.Iconified = GL_FALSE;
    }

    // Did the window get/lose focus
    if( _glfwWin.FocusInCount > 0 && !_glfwWin.Active )
    {
        // If we are in fullscreen mode, restore window
        if( _glfwWin.Fullscreen && _glfwWin.Iconified )
        {
            _glfwPlatformRestoreWindow();
        }

        // Window is now active
        _glfwWin.Active = GL_TRUE;
    }
    else if( _glfwWin.FocusInCount < 0 && _glfwWin.Active )
    {
        // If we are in fullscreen mode, iconfify window
        if( _glfwWin.Fullscreen )
        {
            _glfwPlatformIconifyWindow();
        }

        // Window is not active
        _glfwWin.Active = GL_FALSE;
	_glfwInputDeactivation();
    }

    // Was there a window close request?
    if( winclosed && _glfwWin.WindowCloseCallback )
    {
        // Check if the program wants us to close the window
        winclosed = _glfwWin.WindowCloseCallback();
    }
    if( winclosed )
    {
        glfwCloseWindow();
    }
}


//========================================================================
// _glfwPlatformWaitEvents() - Wait for new window and input events
//========================================================================

void _glfwPlatformWaitEvents( void )
{
    XEvent event;

    // Wait for new events (blocking)
    XNextEvent( _glfwLibrary.Dpy, &event );
    XPutBackEvent( _glfwLibrary.Dpy, &event );

    // Poll events from queue
    _glfwPlatformPollEvents();
}


//========================================================================
// _glfwPlatformHideMouseCursor() - Hide mouse cursor (lock it)
//========================================================================

void _glfwPlatformHideMouseCursor( void )
{
    // Hide cursor
    if( !_glfwWin.PointerHidden )
    {
        XDefineCursor( _glfwLibrary.Dpy, _glfwWin.Win,
                       _glfwCreateNULLCursor( _glfwLibrary.Dpy,
                                              _glfwWin.Win ) );
        _glfwWin.PointerHidden = GL_TRUE;
    }

    // Grab cursor to user window
    if( !_glfwWin.PointerGrabbed )
    {
        if( XGrabPointer( _glfwLibrary.Dpy, _glfwWin.Win, True,
                          ButtonPressMask | ButtonReleaseMask |
                          PointerMotionMask, GrabModeAsync, GrabModeAsync,
                          _glfwWin.Win, None, CurrentTime ) ==
            GrabSuccess )
        {
            _glfwWin.PointerGrabbed = GL_TRUE;
        }
    }
}


//========================================================================
// _glfwPlatformShowMouseCursor() - Show mouse cursor (unlock it)
//========================================================================

void _glfwPlatformShowMouseCursor( void )
{
    // Un-grab cursor (only in windowed mode: in fullscreen mode we still
    // want the mouse grabbed in order to confine the cursor to the window
    // area)
    if( _glfwWin.PointerGrabbed && !_glfwWin.Fullscreen )
    {
        XUngrabPointer( _glfwLibrary.Dpy, CurrentTime );
        _glfwWin.PointerGrabbed = GL_FALSE;
    }

    // Show cursor
    if( _glfwWin.PointerHidden )
    {
        XUndefineCursor( _glfwLibrary.Dpy, _glfwWin.Win );
        _glfwWin.PointerHidden = GL_FALSE;
    }
}


//========================================================================
// _glfwPlatformSetMouseCursorPos() - Set physical mouse cursor position
//========================================================================

void _glfwPlatformSetMouseCursorPos( int x, int y )
{
    // Change cursor position
    _glfwInput.CursorPosX = x;
    _glfwInput.CursorPosY = y;
    XWarpPointer( _glfwLibrary.Dpy, None, _glfwWin.Win, 0,0,0,0, x, y );
}

