//======================================================================== // 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. // //------------------------------------------------------------------------ // $Id: x11_window.c,v 1.19 2007/05/02 20:47:14 elmindreda Exp $ //======================================================================== #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; } // 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 ); // Get input focus XSetInputFocus( _glfwLibrary.Dpy, _glfwWin.Win, RevertToParent, CurrentTime ); // 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 ); } // Set window & icon name _glfwPlatformSetWindowTitle( "GLFW Window" ); // 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 ); }