Back to index

libsfml  1.6+dfsg2
WindowImplX11.cpp
Go to the documentation of this file.
00001 
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
00005 //
00006 // This software is provided 'as-is', without any express or implied warranty.
00007 // In no event will the authors be held liable for any damages arising from the use of this software.
00008 //
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it freely,
00011 // subject to the following restrictions:
00012 //
00013 // 1. The origin of this software must not be misrepresented;
00014 //    you must not claim that you wrote the original software.
00015 //    If you use this software in a product, an acknowledgment
00016 //    in the product documentation would be appreciated but is not required.
00017 //
00018 // 2. Altered source versions must be plainly marked as such,
00019 //    and must not be misrepresented as being the original software.
00020 //
00021 // 3. This notice may not be removed or altered from any source distribution.
00022 //
00024 
00026 // Headers
00028 #include <SFML/Window/WindowStyle.hpp> // important to be included first (conflict with None)
00029 #include <SFML/Window/Linux/WindowImplX11.hpp>
00030 #include <SFML/Window/glext/glxext.h>
00031 #include <SFML/Window/glext/glext.h>
00032 #include <SFML/System/Unicode.hpp>
00033 #include <X11/keysym.h>
00034 #include <X11/extensions/Xrandr.h>
00035 #include <iostream>
00036 #include <sstream>
00037 #include <vector>
00038 
00039 
00040 namespace
00041 {
00046     Bool CheckEvent(::Display*, XEvent* Event, XPointer UserData)
00047     {
00048         // Just check if the event matches the window
00049         return Event->xany.window == reinterpret_cast< ::Window >(UserData);
00050     }
00051 }
00052 
00053 namespace sf
00054 {
00055 namespace priv
00056 {
00058 // Static member data
00060 Display*       WindowImplX11::ourDisplay          = NULL;
00061 int            WindowImplX11::ourScreen           = 0;
00062 WindowImplX11* WindowImplX11::ourFullscreenWindow = NULL;
00063 unsigned int   WindowImplX11::ourWindowsCount     = 0;
00064 XIM            WindowImplX11::ourInputMethod      = NULL;
00065 unsigned long  WindowImplX11::ourEventMask        = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
00066                                                     PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask |
00067                                                     EnterWindowMask | LeaveWindowMask;
00068 
00069 
00074 WindowImplX11::WindowImplX11() :
00075 myWindow      (0),
00076 myIsExternal  (false),
00077 myGLContext   (NULL),
00078 myAtomClose   (0),
00079 myOldVideoMode(-1),
00080 myHiddenCursor(0),
00081 myInputContext(NULL),
00082 myKeyRepeat   (true)
00083 {
00084     // Open the display at first call
00085     if (!OpenDisplay())
00086         return;
00087 
00088     // Use small dimensions
00089     myWidth  = 1;
00090     myHeight = 1;
00091 
00092     // Create the rendering context
00093     XVisualInfo Visual;
00094     WindowSettings Params(0, 0, 0);
00095     if (!CreateContext(VideoMode(myWidth, myHeight, 32), Visual, Params))
00096         return;
00097 
00098     // Create a new color map with the chosen visual
00099     Colormap ColMap = XCreateColormap(ourDisplay, RootWindow(ourDisplay, ourScreen), Visual.visual, AllocNone);
00100 
00101     // Define the window attributes
00102     XSetWindowAttributes Attributes;
00103     Attributes.colormap = ColMap;
00104 
00105     // Create a dummy window (disabled and hidden)
00106     myWindow = XCreateWindow(ourDisplay,
00107                              RootWindow(ourDisplay, ourScreen),
00108                              0, 0,
00109                              myWidth, myHeight,
00110                              0,
00111                              Visual.depth,
00112                              InputOutput,
00113                              Visual.visual,
00114                              CWColormap, &Attributes);
00115 
00116     // Don't activate the dummy context by default
00117 }
00118 
00119 
00123 WindowImplX11::WindowImplX11(WindowHandle Handle, WindowSettings& Params) :
00124 myWindow      (0),
00125 myIsExternal  (true),
00126 myGLContext   (NULL),
00127 myAtomClose   (0),
00128 myOldVideoMode(-1),
00129 myHiddenCursor(0),
00130 myInputContext(NULL),
00131 myKeyRepeat   (true)
00132 {
00133     // Open the display at first call
00134     if (!OpenDisplay())
00135         return;
00136 
00137     // Save the window handle
00138     myWindow = Handle;
00139 
00140     if (myWindow)
00141     {
00142         // Get the window size
00143         XWindowAttributes WindowAttributes;
00144         if (XGetWindowAttributes(ourDisplay, myWindow, &WindowAttributes) == 0)
00145         {
00146             std::cerr << "Failed to get the window attributes" << std::endl;
00147             return;
00148         }
00149         myWidth  = WindowAttributes.width;
00150         myHeight = WindowAttributes.height;
00151 
00152         // Setup the visual infos to match
00153         XVisualInfo Template;
00154         Template.depth     = WindowAttributes.depth;
00155         Template.visualid  = XVisualIDFromVisual(WindowAttributes.visual);
00156         unsigned long Mask = VisualDepthMask | VisualIDMask;
00157 
00158         // Create the rendering context
00159         VideoMode Mode(myWidth, myHeight, VideoMode::GetDesktopMode().BitsPerPixel);
00160         XVisualInfo Visual;
00161         if (!CreateContext(Mode, Visual, Params, Template, Mask))
00162             return;
00163 
00164         // Create a new color map with the chosen visual
00165         Colormap ColMap = XCreateColormap(ourDisplay, RootWindow(ourDisplay, ourScreen), Visual.visual, AllocNone);
00166         XSetWindowColormap(ourDisplay, myWindow, ColMap);
00167 
00168         // Make sure the window is listening to all the requiered events
00169         XSelectInput(ourDisplay, myWindow, ourEventMask & ~ButtonPressMask);
00170 
00171         // Do some common initializations
00172         Initialize();
00173     }
00174 }
00175 
00176 
00180 WindowImplX11::WindowImplX11(VideoMode Mode, const std::string& Title, unsigned long WindowStyle, WindowSettings& Params) :
00181 myWindow      (0),
00182 myIsExternal  (false),
00183 myGLContext   (NULL),
00184 myAtomClose   (0),
00185 myOldVideoMode(-1),
00186 myHiddenCursor(0),
00187 myInputContext(NULL),
00188 myKeyRepeat   (true)
00189 {
00190     // Open the display at first call
00191     if (!OpenDisplay())
00192         return;
00193 
00194     // Compute position and size
00195     int Left, Top;
00196     bool Fullscreen = (WindowStyle & Style::Fullscreen) != 0;
00197     if (!Fullscreen)
00198     {
00199         Left = (DisplayWidth(ourDisplay, ourScreen)  - Mode.Width)  / 2;
00200         Top  = (DisplayHeight(ourDisplay, ourScreen) - Mode.Height) / 2;
00201     }
00202     else
00203     {
00204         Left = 0;
00205         Top  = 0;
00206     }
00207     int Width  = myWidth  = Mode.Width;
00208     int Height = myHeight = Mode.Height;
00209 
00210     // Switch to fullscreen if necessary
00211     if (Fullscreen)
00212         SwitchToFullscreen(Mode);
00213 
00214     // Create the rendering context
00215     XVisualInfo Visual;
00216     if (!CreateContext(Mode, Visual, Params))
00217         return;
00218 
00219     // Create a new color map with the chosen visual
00220     Colormap ColMap = XCreateColormap(ourDisplay, RootWindow(ourDisplay, ourScreen), Visual.visual, AllocNone);
00221 
00222     // Define the window attributes
00223     XSetWindowAttributes Attributes;
00224     Attributes.event_mask        = ourEventMask;
00225     Attributes.colormap          = ColMap;
00226     Attributes.override_redirect = Fullscreen;
00227 
00228     // Create the window
00229     myWindow = XCreateWindow(ourDisplay,
00230                              RootWindow(ourDisplay, ourScreen),
00231                              Left, Top,
00232                              Width, Height,
00233                              0,
00234                              Visual.depth,
00235                              InputOutput,
00236                              Visual.visual,
00237                              CWEventMask | CWColormap | CWOverrideRedirect, &Attributes);
00238     if (!myWindow)
00239     {
00240         std::cerr << "Failed to create window" << std::endl;
00241         return;
00242     }
00243 
00244     // Set the window's name
00245     XStoreName(ourDisplay, myWindow, Title.c_str());
00246 
00247     // Set the window's style (tell the windows manager to change our window's decorations and functions according to the requested style)
00248     if (!Fullscreen)
00249     {
00250         Atom WMHintsAtom = XInternAtom(ourDisplay, "_MOTIF_WM_HINTS", false);
00251         if (WMHintsAtom)
00252         {
00253             static const unsigned long MWM_HINTS_FUNCTIONS   = 1 << 0;
00254             static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1;
00255     
00256             //static const unsigned long MWM_DECOR_ALL         = 1 << 0;
00257             static const unsigned long MWM_DECOR_BORDER      = 1 << 1;
00258             static const unsigned long MWM_DECOR_RESIZEH     = 1 << 2;
00259             static const unsigned long MWM_DECOR_TITLE       = 1 << 3;
00260             static const unsigned long MWM_DECOR_MENU        = 1 << 4;
00261             static const unsigned long MWM_DECOR_MINIMIZE    = 1 << 5;
00262             static const unsigned long MWM_DECOR_MAXIMIZE    = 1 << 6;
00263 
00264             //static const unsigned long MWM_FUNC_ALL          = 1 << 0;
00265             static const unsigned long MWM_FUNC_RESIZE       = 1 << 1;
00266             static const unsigned long MWM_FUNC_MOVE         = 1 << 2;
00267             static const unsigned long MWM_FUNC_MINIMIZE     = 1 << 3;
00268             static const unsigned long MWM_FUNC_MAXIMIZE     = 1 << 4;
00269             static const unsigned long MWM_FUNC_CLOSE        = 1 << 5;
00270     
00271             struct WMHints
00272             {
00273                 unsigned long Flags;
00274                 unsigned long Functions;
00275                 unsigned long Decorations;
00276                 long          InputMode;
00277                 unsigned long State;
00278             };
00279     
00280             WMHints Hints;
00281             Hints.Flags       = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
00282             Hints.Decorations = 0;
00283             Hints.Functions   = 0;
00284 
00285             if (WindowStyle & Style::Titlebar)
00286             {
00287                 Hints.Decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU;
00288                 Hints.Functions   |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE;
00289             }
00290             if (WindowStyle & Style::Resize)
00291             {
00292                 Hints.Decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH;
00293                 Hints.Functions   |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
00294             }
00295             if (WindowStyle & Style::Close)
00296             {
00297                 Hints.Decorations |= 0;
00298                 Hints.Functions   |= MWM_FUNC_CLOSE;
00299             }
00300 
00301             const unsigned char* HintsPtr = reinterpret_cast<const unsigned char*>(&Hints);
00302             XChangeProperty(ourDisplay, myWindow, WMHintsAtom, WMHintsAtom, 32, PropModeReplace, HintsPtr, 5);
00303         }
00304 
00305         // This is a hack to force some windows managers to disable resizing
00306         if (!(WindowStyle & Style::Resize))
00307         {
00308             XSizeHints XSizeHints;
00309             XSizeHints.flags      = PMinSize | PMaxSize;
00310             XSizeHints.min_width  = XSizeHints.max_width  = Width;
00311             XSizeHints.min_height = XSizeHints.max_height = Height;
00312             XSetWMNormalHints(ourDisplay, myWindow, &XSizeHints); 
00313         }
00314     }
00315 
00316     // Do some common initializations
00317     Initialize();
00318 
00319     // In fullscreen mode, we must grab keyboard and mouse inputs
00320     if (Fullscreen)
00321     {
00322         XGrabPointer(ourDisplay, myWindow, true, 0, GrabModeAsync, GrabModeAsync, myWindow, None, CurrentTime);
00323         XGrabKeyboard(ourDisplay, myWindow, true, GrabModeAsync, GrabModeAsync, CurrentTime);
00324     }
00325 }
00326 
00327 
00331 WindowImplX11::~WindowImplX11()
00332 {
00333     // Cleanup graphical resources
00334     CleanUp();
00335 
00336     // Destroy the input context
00337     if (myInputContext)
00338     {
00339         XDestroyIC(myInputContext);
00340     }
00341 
00342     // Destroy the window
00343     if (myWindow && !myIsExternal)
00344     {
00345         XDestroyWindow(ourDisplay, myWindow);
00346         XFlush(ourDisplay);
00347     }
00348 
00349     // Close the display
00350     CloseDisplay();
00351 }
00352 
00353 
00357 bool WindowImplX11::IsContextActive()
00358 {
00359     return glXGetCurrentContext() != NULL;
00360 }
00361 
00362 
00366 void WindowImplX11::Display()
00367 {
00368     if (myWindow && myGLContext)
00369         glXSwapBuffers(ourDisplay, myWindow);
00370 }
00371 
00372 
00376 void WindowImplX11::ProcessEvents()
00377 {
00378     // This function implements a workaround to properly discard
00379     // repeated key events when necessary. The problem is that the
00380     // system's key events policy doesn't match SFML's one: X server will generate
00381     // both repeated KeyPress and KeyRelease events when maintaining a key down, while
00382     // SFML only wants repeated KeyPress events. Thus, we have to:
00383     // - Discard duplicated KeyRelease events when EnableKeyRepeat is true
00384     // - Discard both duplicated KeyPress and KeyRelease events when EnableKeyRepeat is false
00385 
00386 
00387     // Process any event in the queue matching our window
00388     XEvent Event;
00389     while (XCheckIfEvent(ourDisplay, &Event, &CheckEvent, reinterpret_cast<XPointer>(myWindow)))
00390     {
00391         // Detect repeated key events
00392         if ((Event.type == KeyPress) || (Event.type == KeyRelease))
00393         {
00394             if (Event.xkey.keycode < 256)
00395             {
00396                 // To detect if it is a repeated key event, we check the current state of the key.
00397                 // - If the state is "down", KeyReleased events must obviously be discarded.
00398                 // - KeyPress events are a little bit harder to handle: they depend on the EnableKeyRepeat state,
00399                 //   and we need to properly forward the first one.
00400                 char Keys[32];
00401                 XQueryKeymap(ourDisplay, Keys);
00402                 if (Keys[Event.xkey.keycode >> 3] & (1 << (Event.xkey.keycode % 8)))
00403                 {
00404                     // KeyRelease event + key down = repeated event --> discard
00405                     if (Event.type == KeyRelease)
00406                     {
00407                         myLastKeyReleaseEvent = Event;
00408                         continue;
00409                     }
00410 
00411                     // KeyPress event + key repeat disabled + matching KeyRelease event = repeated event --> discard
00412                     if ((Event.type == KeyPress) && !myKeyRepeat &&
00413                         (myLastKeyReleaseEvent.xkey.keycode == Event.xkey.keycode) &&
00414                         (myLastKeyReleaseEvent.xkey.time == Event.xkey.time))
00415                     {
00416                         continue;
00417                     }
00418                 }
00419             }
00420         }
00421 
00422         // Process the event
00423         ProcessEvent(Event);
00424    }
00425 }
00426 
00427 
00431 void WindowImplX11::SetActive(bool Active) const
00432 {
00433     if (Active)
00434     {
00435         if (myWindow && myGLContext && (glXGetCurrentContext() != myGLContext))
00436             glXMakeCurrent(ourDisplay, myWindow, myGLContext);
00437     }
00438     else
00439     {
00440         if (glXGetCurrentContext() == myGLContext)
00441             glXMakeCurrent(ourDisplay, None, NULL);
00442     }
00443 }
00444 
00445 
00449 void WindowImplX11::UseVerticalSync(bool Enabled)
00450 {
00451     const GLubyte* ProcAddress = reinterpret_cast<const GLubyte*>("glXSwapIntervalSGI");
00452     PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = reinterpret_cast<PFNGLXSWAPINTERVALSGIPROC>(glXGetProcAddress(ProcAddress));
00453     if (glXSwapIntervalSGI)
00454         glXSwapIntervalSGI(Enabled ? 1 : 0);
00455 }
00456 
00457 
00461 void WindowImplX11::ShowMouseCursor(bool Show)
00462 {
00463     XDefineCursor(ourDisplay, myWindow, Show ? None : myHiddenCursor);
00464     XFlush(ourDisplay);
00465 }
00466 
00467 
00471 void WindowImplX11::SetCursorPosition(unsigned int Left, unsigned int Top)
00472 {
00473     XWarpPointer(ourDisplay, None, myWindow, 0, 0, 0, 0, Left, Top);
00474     XFlush(ourDisplay);
00475 }
00476 
00477 
00481 void WindowImplX11::SetPosition(int Left, int Top)
00482 {
00483     XMoveWindow(ourDisplay, myWindow, Left, Top);
00484     XFlush(ourDisplay);
00485 }
00486 
00487 
00491 void WindowImplX11::SetSize(unsigned int Width, unsigned int Height)
00492 {
00493     XResizeWindow(ourDisplay, myWindow, Width, Height);
00494     XFlush(ourDisplay);
00495 }
00496 
00497 
00501 void WindowImplX11::Show(bool State)
00502 {
00503     if (State)
00504         XMapWindow(ourDisplay, myWindow);
00505     else
00506         XUnmapWindow(ourDisplay, myWindow);
00507 
00508     XFlush(ourDisplay);
00509 }
00510 
00511 
00515 void WindowImplX11::EnableKeyRepeat(bool Enabled)
00516 {
00517     myKeyRepeat = Enabled;
00518 }
00519 
00520 
00524 void WindowImplX11::SetIcon(unsigned int Width, unsigned int Height, const Uint8* Pixels)
00525 {
00526     // X11 wants BGRA pixels : swap red and blue channels
00527     // Note : this memory will never be freed, but it seems to cause a bug on exit if I do so
00528     Uint8* IconPixels = new Uint8[Width * Height * 4];
00529     for (std::size_t i = 0; i < Width * Height; ++i)
00530     {
00531         IconPixels[i * 4 + 0] = Pixels[i * 4 + 2];
00532         IconPixels[i * 4 + 1] = Pixels[i * 4 + 1];
00533         IconPixels[i * 4 + 2] = Pixels[i * 4 + 0];
00534         IconPixels[i * 4 + 3] = Pixels[i * 4 + 3];
00535     }
00536 
00537     // Create the icon pixmap
00538     Visual*      DefVisual = DefaultVisual(ourDisplay, ourScreen);
00539     unsigned int DefDepth  = DefaultDepth(ourDisplay, ourScreen);
00540     XImage* IconImage = XCreateImage(ourDisplay, DefVisual, DefDepth, ZPixmap, 0, (char*)IconPixels, Width, Height, 32, 0);
00541     if (!IconImage)
00542     {
00543         std::cerr << "Failed to set the window's icon" << std::endl;
00544         return;
00545     }
00546     Pixmap IconPixmap = XCreatePixmap(ourDisplay, RootWindow(ourDisplay, ourScreen), Width, Height, DefDepth);
00547     XGCValues Values;
00548     GC IconGC = XCreateGC(ourDisplay, IconPixmap, 0, &Values);
00549     XPutImage(ourDisplay, IconPixmap, IconGC, IconImage, 0, 0, 0, 0, Width, Height);
00550     XFreeGC(ourDisplay, IconGC);
00551     XDestroyImage(IconImage);
00552 
00553     // Create the mask pixmap (must have 1 bit depth)
00554     std::size_t Pitch = (Width + 7) / 8;
00555     static std::vector<Uint8> MaskPixels(Pitch * Height, 0);
00556     for (std::size_t j = 0; j < Height; ++j)
00557     {
00558         for (std::size_t i = 0; i < Pitch; ++i)
00559         {
00560             for (std::size_t k = 0; k < 8; ++k)
00561             {
00562                 if (i * 8 + k < Width)
00563                 {
00564                     Uint8 Opacity = (Pixels[(i * 8 + k + j * Width) * 4 + 3] > 0) ? 1 : 0;
00565                     MaskPixels[i + j * Pitch] |= (Opacity << k);                    
00566                 }
00567             }
00568         }
00569     }
00570     Pixmap MaskPixmap = XCreatePixmapFromBitmapData(ourDisplay, myWindow, (char*)&MaskPixels[0], Width, Height, 1, 0, 1);
00571 
00572     // Send our new icon to the window through the WMHints
00573     XWMHints* Hints = XAllocWMHints();
00574     Hints->flags       = IconPixmapHint | IconMaskHint;
00575     Hints->icon_pixmap = IconPixmap;
00576     Hints->icon_mask   = MaskPixmap;
00577     XSetWMHints(ourDisplay, myWindow, Hints);
00578     XFree(Hints);
00579 
00580     XFlush(ourDisplay);
00581 }
00582 
00583 
00587 void WindowImplX11::SwitchToFullscreen(const VideoMode& Mode)
00588 {
00589     // Check if the XRandR extension is present
00590     int Version;
00591     if (XQueryExtension(ourDisplay, "RANDR", &Version, &Version, &Version))
00592     {
00593         // Get the current configuration
00594         XRRScreenConfiguration* Config = XRRGetScreenInfo(ourDisplay, RootWindow(ourDisplay, ourScreen));
00595         if (Config)
00596         {
00597             // Get the current rotation
00598             Rotation CurrentRotation;
00599             myOldVideoMode = XRRConfigCurrentConfiguration(Config, &CurrentRotation);
00600 
00601             // Get the available screen sizes
00602             int NbSizes;
00603             XRRScreenSize* Sizes = XRRConfigSizes(Config, &NbSizes);
00604             if (Sizes && (NbSizes > 0))
00605             {
00606                 // Search a matching size
00607                 for (int i = 0; i < NbSizes; ++i)
00608                 {
00609                     if ((Sizes[i].width == static_cast<int>(Mode.Width)) && (Sizes[i].height == static_cast<int>(Mode.Height)))
00610                     {
00611                         // Switch to fullscreen mode
00612                         XRRSetScreenConfig(ourDisplay, Config, RootWindow(ourDisplay, ourScreen), i, CurrentRotation, CurrentTime);
00613 
00614                         // Set "this" as the current fullscreen window
00615                         ourFullscreenWindow = this;
00616                         break;
00617                     }
00618                 }
00619             }
00620 
00621             // Free the configuration instance
00622             XRRFreeScreenConfigInfo(Config);
00623         }
00624         else
00625         {
00626             // Failed to get the screen configuration
00627             std::cerr << "Failed to get the current screen configuration for fullscreen mode, switching to windiw mode" << std::endl;
00628         }
00629     }
00630     else
00631     {
00632         // XRandr extension is not supported : we cannot use fullscreen mode
00633         std::cerr << "Fullscreen is not supported, switching to window mode" << std::endl;
00634     }
00635 }
00636 
00637 
00641 bool WindowImplX11::CreateContext(const VideoMode& Mode, XVisualInfo& ChosenVisual, WindowSettings& Params, XVisualInfo Template, unsigned long Mask)
00642 {
00643     // Get all the visuals matching the template
00644     Template.screen = ourScreen;
00645     int NbVisuals = 0;
00646     XVisualInfo* Visuals = XGetVisualInfo(ourDisplay, Mask | VisualScreenMask, &Template, &NbVisuals);
00647     if (!Visuals || (NbVisuals == 0))
00648     {
00649         if (Visuals)
00650             XFree(Visuals);
00651         std::cerr << "There is no valid visual for the selected screen" << std::endl;
00652         return false;
00653     }
00654 
00655     // Find the best visual
00656     int          BestScore  = 0xFFFF;
00657     XVisualInfo* BestVisual = NULL;
00658     while (!BestVisual)
00659     {
00660         for (int i = 0; i < NbVisuals; ++i)
00661         {
00662             // Get the current visual attributes
00663             int RGBA, DoubleBuffer, Red, Green, Blue, Alpha, Depth, Stencil, MultiSampling, Samples;
00664             glXGetConfig(ourDisplay, &Visuals[i], GLX_RGBA,               &RGBA);
00665             glXGetConfig(ourDisplay, &Visuals[i], GLX_DOUBLEBUFFER,       &DoubleBuffer); 
00666             glXGetConfig(ourDisplay, &Visuals[i], GLX_RED_SIZE,           &Red);
00667             glXGetConfig(ourDisplay, &Visuals[i], GLX_GREEN_SIZE,         &Green); 
00668             glXGetConfig(ourDisplay, &Visuals[i], GLX_BLUE_SIZE,          &Blue); 
00669             glXGetConfig(ourDisplay, &Visuals[i], GLX_ALPHA_SIZE,         &Alpha); 
00670             glXGetConfig(ourDisplay, &Visuals[i], GLX_DEPTH_SIZE,         &Depth);        
00671             glXGetConfig(ourDisplay, &Visuals[i], GLX_STENCIL_SIZE,       &Stencil);
00672             glXGetConfig(ourDisplay, &Visuals[i], GLX_SAMPLE_BUFFERS_ARB, &MultiSampling);        
00673             glXGetConfig(ourDisplay, &Visuals[i], GLX_SAMPLES_ARB,        &Samples);
00674 
00675             // First check the mandatory parameters
00676             if ((RGBA == 0) || (DoubleBuffer == 0))
00677                 continue;
00678 
00679             // Evaluate the current configuration
00680             int Color = Red + Green + Blue + Alpha;
00681             int Score = EvaluateConfig(Mode, Params, Color, Depth, Stencil, MultiSampling ? Samples : 0);
00682 
00683             // Keep it if it's better than the current best
00684             if (Score < BestScore)
00685             {
00686                 BestScore  = Score;
00687                 BestVisual = &Visuals[i];
00688             }
00689         }
00690 
00691         // If no visual has been found, try a lower level of antialiasing
00692         if (!BestVisual)
00693         {
00694             if (Params.AntialiasingLevel > 2)
00695             {
00696                 std::cerr << "Failed to find a pixel format supporting "
00697                           << Params.AntialiasingLevel << " antialiasing levels ; trying with 2 levels" << std::endl;
00698                 Params.AntialiasingLevel = 2;
00699             }
00700             else if (Params.AntialiasingLevel > 0)
00701             {
00702                 std::cerr << "Failed to find a pixel format supporting antialiasing ; antialiasing will be disabled" << std::endl;
00703                 Params.AntialiasingLevel = 0;
00704             }
00705             else
00706             {
00707                 std::cerr << "Failed to find a suitable pixel format for the window -- cannot create OpenGL context" << std::endl;
00708                 return false;
00709             }
00710         }
00711     }
00712 
00713     // Create the OpenGL context
00714     myGLContext = glXCreateContext(ourDisplay, BestVisual, glXGetCurrentContext(), true);
00715     if (myGLContext == NULL)
00716     {
00717         std::cerr << "Failed to create an OpenGL context for this window" << std::endl;
00718         return false;
00719     }
00720 
00721     // Update the creation settings from the chosen format
00722     int Depth, Stencil;
00723     glXGetConfig(ourDisplay, BestVisual, GLX_DEPTH_SIZE,   &Depth);
00724     glXGetConfig(ourDisplay, BestVisual, GLX_STENCIL_SIZE, &Stencil);
00725     Params.DepthBits   = static_cast<unsigned int>(Depth);
00726     Params.StencilBits = static_cast<unsigned int>(Stencil);
00727 
00728     // Assign the chosen visual, and free the temporary visuals array
00729     ChosenVisual = *BestVisual;
00730     XFree(Visuals);
00731 
00732     // Activate the context
00733     SetActive(true);
00734 
00735     // Enable multisampling if needed
00736     if (Params.AntialiasingLevel > 0)
00737         glEnable(GL_MULTISAMPLE_ARB);
00738 
00739     return true;
00740 }
00741 
00742 
00746 void WindowImplX11::Initialize()
00747 {
00748     // Make sure the "last key release" is initialized with invalid values
00749     myLastKeyReleaseEvent.type = -1;
00750 
00751     // Get the atom defining the close event
00752     myAtomClose = XInternAtom(ourDisplay, "WM_DELETE_WINDOW", false);
00753     XSetWMProtocols(ourDisplay, myWindow, &myAtomClose, 1);
00754 
00755     // Create the input context
00756     if (ourInputMethod)
00757     {
00758         myInputContext = XCreateIC(ourInputMethod,
00759                                    XNClientWindow,  myWindow,
00760                                    XNFocusWindow,   myWindow,
00761                                    XNInputStyle,    XIMPreeditNothing  | XIMStatusNothing,
00762                                    NULL);
00763         
00764         if (!myInputContext)
00765             std::cerr << "Failed to create input context for window -- TextEntered event won't be able to return unicode" << std::endl;
00766     }
00767 
00768     // Show the window
00769     XMapWindow(ourDisplay, myWindow);
00770     XFlush(ourDisplay);
00771 
00772     // Create the hiden cursor
00773     CreateHiddenCursor();
00774 
00775     // Set our context as the current OpenGL context for rendering
00776     SetActive();
00777 
00778     // Flush the commands queue
00779     XFlush(ourDisplay);
00780 }
00781 
00782 
00786 void WindowImplX11::CreateHiddenCursor()
00787 {
00788     // Create the cursor's pixmap (1x1 pixels)
00789        Pixmap CursorPixmap = XCreatePixmap(ourDisplay, myWindow, 1, 1, 1);
00790     GC GraphicsContext = XCreateGC(ourDisplay, CursorPixmap, 0, NULL);
00791     XDrawPoint(ourDisplay, CursorPixmap, GraphicsContext, 0, 0);
00792     XFreeGC(ourDisplay, GraphicsContext);
00793 
00794     // Create the cursor, using the pixmap as both the shape and the mask of the cursor
00795        XColor Color;
00796        Color.flags = DoRed | DoGreen | DoBlue;
00797        Color.red = Color.blue = Color.green = 0;
00798     myHiddenCursor = XCreatePixmapCursor(ourDisplay, CursorPixmap, CursorPixmap, &Color, &Color, 0, 0);
00799 
00800     // We don't need the pixmap any longer, free it
00801        XFreePixmap(ourDisplay, CursorPixmap);
00802 }
00803 
00804 
00808 void WindowImplX11::CleanUp()
00809 {
00810     // Restore the previous video mode (in case we were running in fullscreen)
00811     if (ourFullscreenWindow == this)
00812     {
00813         // Get current screen info
00814         XRRScreenConfiguration* Config = XRRGetScreenInfo(ourDisplay, RootWindow(ourDisplay, ourScreen));
00815         if (Config) 
00816         {
00817             // Get the current rotation
00818             Rotation CurrentRotation;
00819             XRRConfigCurrentConfiguration(Config, &CurrentRotation);
00820 
00821             // Reset the video mode
00822             XRRSetScreenConfig(ourDisplay, Config, RootWindow(ourDisplay, ourScreen), myOldVideoMode, CurrentRotation, CurrentTime);
00823 
00824             // Free the configuration instance
00825             XRRFreeScreenConfigInfo(Config);
00826         } 
00827 
00828         // Reset the fullscreen window
00829         ourFullscreenWindow = NULL;
00830     }
00831 
00832     // Unhide the mouse cursor (in case it was hidden)
00833     ShowMouseCursor(true);
00834 
00835     // Destroy the OpenGL context
00836     if (myGLContext)
00837     {
00838         glXDestroyContext(ourDisplay, myGLContext);
00839         myGLContext = NULL;
00840     }
00841 }
00842 
00843 
00847 void WindowImplX11::ProcessEvent(XEvent WinEvent)
00848 {
00849     switch (WinEvent.type)
00850     {
00851         // Destroy event
00852         case DestroyNotify :
00853         {
00854             // The window is about to be destroyed : we must cleanup resources
00855             CleanUp();
00856             break;
00857         }
00858 
00859         // Gain focus event
00860         case FocusIn :
00861         {
00862             // Update the input context
00863             if (myInputContext)
00864                 XSetICFocus(myInputContext);
00865 
00866             Event Evt;
00867             Evt.Type = Event::GainedFocus;
00868             SendEvent(Evt);
00869             break;
00870         }
00871 
00872         // Lost focus event
00873         case FocusOut :
00874         {
00875             // Update the input context
00876             if (myInputContext)
00877                 XUnsetICFocus(myInputContext);
00878 
00879             Event Evt;
00880             Evt.Type = Event::LostFocus;
00881             SendEvent(Evt);
00882             break;
00883         }
00884 
00885         // Resize event
00886         case ConfigureNotify :
00887         {
00888             if ((WinEvent.xconfigure.width != static_cast<int>(myWidth)) || (WinEvent.xconfigure.height != static_cast<int>(myHeight)))
00889             {
00890                 myWidth  = WinEvent.xconfigure.width;
00891                 myHeight = WinEvent.xconfigure.height;
00892 
00893                 Event Evt;
00894                 Evt.Type        = Event::Resized;
00895                 Evt.Size.Width  = myWidth;
00896                 Evt.Size.Height = myHeight;
00897                 SendEvent(Evt);
00898             }
00899             break;
00900         }
00901 
00902         // Close event
00903         case ClientMessage :
00904         {
00905             if ((WinEvent.xclient.format == 32) && (WinEvent.xclient.data.l[0]) == static_cast<long>(myAtomClose))  
00906             {
00907                 Event Evt;
00908                 Evt.Type = Event::Closed;
00909                 SendEvent(Evt);
00910             }
00911             break;
00912         }
00913 
00914         // Key down event
00915         case KeyPress :
00916         {
00917             // Get the keysym of the key that has been pressed
00918             static XComposeStatus KeyboardStatus;
00919             char Buffer[32];
00920             KeySym Sym;
00921             XLookupString(&WinEvent.xkey, Buffer, sizeof(Buffer), &Sym, &KeyboardStatus);
00922 
00923             // Fill the event parameters
00924             Event Evt;
00925             Evt.Type        = Event::KeyPressed;
00926             Evt.Key.Code    = KeysymToSF(Sym);
00927             Evt.Key.Alt     = WinEvent.xkey.state & Mod1Mask;
00928             Evt.Key.Control = WinEvent.xkey.state & ControlMask;
00929             Evt.Key.Shift   = WinEvent.xkey.state & ShiftMask;
00930             SendEvent(Evt);
00931 
00932             // Generate a TextEntered event
00933             if (!XFilterEvent(&WinEvent, None))
00934             {
00935                 #ifdef X_HAVE_UTF8_STRING
00936                 if (myInputContext)
00937                 {
00938                     Status ReturnedStatus;
00939                     Uint8  KeyBuffer[16];
00940                     int Length = Xutf8LookupString(myInputContext, &WinEvent.xkey, reinterpret_cast<char*>(KeyBuffer), sizeof(KeyBuffer), NULL, &ReturnedStatus);
00941                     if (Length > 0)
00942                     {
00943                         Uint32 Unicode[2]; // just in case, but 1 character should be enough
00944                         const Uint32* End = Unicode::UTF8ToUTF32(KeyBuffer, KeyBuffer + Length, Unicode);
00945 
00946                         if (End > Unicode)
00947                         {
00948                             Event TextEvt;
00949                             TextEvt.Type         = Event::TextEntered;
00950                             TextEvt.Text.Unicode = Unicode[0];
00951                             SendEvent(TextEvt);
00952                         }
00953                     }
00954                 }
00955                 else
00956                 #endif
00957                 {
00958                     static XComposeStatus ComposeStatus;
00959                     char KeyBuffer[16];
00960                     if (XLookupString(&WinEvent.xkey, KeyBuffer, sizeof(KeyBuffer), NULL, &ComposeStatus))
00961                     {
00962                         Event TextEvt;
00963                         TextEvt.Type         = Event::TextEntered;
00964                         TextEvt.Text.Unicode = static_cast<Uint32>(KeyBuffer[0]);
00965                         SendEvent(TextEvt);
00966                     }
00967                 }
00968             }
00969 
00970             break;
00971         }
00972 
00973         // Key up event
00974         case KeyRelease :
00975         {
00976             // Get the keysym of the key that has been pressed
00977             char Buffer[32];
00978             KeySym Sym;
00979             XLookupString(&WinEvent.xkey, Buffer, 32, &Sym, NULL);
00980 
00981             // Fill the event parameters
00982             Event Evt;
00983             Evt.Type        = Event::KeyReleased;
00984             Evt.Key.Code    = KeysymToSF(Sym);
00985             Evt.Key.Alt     = WinEvent.xkey.state & Mod1Mask;
00986             Evt.Key.Control = WinEvent.xkey.state & ControlMask;
00987             Evt.Key.Shift   = WinEvent.xkey.state & ShiftMask;
00988 
00989             SendEvent(Evt);
00990             break;
00991         }
00992 
00993         // Mouse button pressed
00994         case ButtonPress :
00995         {
00996             unsigned int Button = WinEvent.xbutton.button;
00997             if ((Button == Button1) || (Button == Button2) || (Button == Button3) || (Button == 8) || (Button == 9))
00998             {
00999                 Event Evt;
01000                 Evt.Type          = Event::MouseButtonPressed;
01001                 Evt.MouseButton.X = WinEvent.xbutton.x;
01002                 Evt.MouseButton.Y = WinEvent.xbutton.y;
01003                 switch (Button)
01004                 {
01005                     case Button1 : Evt.MouseButton.Button = Mouse::Left;     break;
01006                     case Button2 : Evt.MouseButton.Button = Mouse::Middle;   break;
01007                     case Button3 : Evt.MouseButton.Button = Mouse::Right;    break;
01008                     case 8 :       Evt.MouseButton.Button = Mouse::XButton1; break;
01009                     case 9 :       Evt.MouseButton.Button = Mouse::XButton2; break;            
01010                 }
01011                 SendEvent(Evt);
01012             }
01013             break;
01014         }
01015 
01016         // Mouse button released
01017         case ButtonRelease :
01018         {
01019             unsigned int Button = WinEvent.xbutton.button;
01020             if ((Button == Button1) || (Button == Button2) || (Button == Button3) || (Button == 8) || (Button == 9))
01021             {
01022                 Event Evt;
01023                 Evt.Type          = Event::MouseButtonReleased;
01024                 Evt.MouseButton.X = WinEvent.xbutton.x;
01025                 Evt.MouseButton.Y = WinEvent.xbutton.y;
01026                 switch (Button)
01027                 {
01028                     case Button1 : Evt.MouseButton.Button = Mouse::Left;     break;
01029                     case Button2 : Evt.MouseButton.Button = Mouse::Middle;   break;
01030                     case Button3 : Evt.MouseButton.Button = Mouse::Right;    break;
01031                     case 8 :       Evt.MouseButton.Button = Mouse::XButton1; break;
01032                     case 9 :       Evt.MouseButton.Button = Mouse::XButton2; break;            
01033                 }
01034                 SendEvent(Evt);
01035             }
01036             else if ((Button == Button4) || (Button == Button5))
01037             {
01038                 Event Evt;
01039                 Evt.Type             = Event::MouseWheelMoved;
01040                 Evt.MouseWheel.Delta = WinEvent.xbutton.button == Button4 ? 1 : -1;
01041                 SendEvent(Evt);
01042             }
01043             break;
01044         }
01045 
01046         // Mouse moved
01047         case MotionNotify :
01048         {
01049             Event Evt;
01050             Evt.Type        = Event::MouseMoved;
01051             Evt.MouseMove.X = WinEvent.xmotion.x;
01052             Evt.MouseMove.Y = WinEvent.xmotion.y;
01053             SendEvent(Evt);
01054             break;
01055         }
01056 
01057         // Mouse entered
01058         case EnterNotify :
01059         {
01060             Event Evt;
01061             Evt.Type = Event::MouseEntered;
01062             SendEvent(Evt);
01063             break;
01064         }
01065 
01066         // Mouse left
01067         case LeaveNotify :
01068         {
01069             Event Evt;
01070             Evt.Type = Event::MouseLeft;
01071             SendEvent(Evt);
01072             break;
01073         }
01074     }
01075 }
01076 
01077 
01081 Key::Code WindowImplX11::KeysymToSF(KeySym Sym)
01082 {
01083     // First convert to uppercase (to avoid dealing with two different keysyms for the same key)
01084     KeySym Lower, Key;
01085     XConvertCase(Sym, &Lower, &Key);
01086 
01087     switch (Key)
01088     {
01089         case XK_Shift_L :      return Key::LShift;
01090         case XK_Shift_R :      return Key::RShift;
01091         case XK_Control_L :    return Key::LControl;
01092         case XK_Control_R :    return Key::RControl;
01093         case XK_Alt_L :        return Key::LAlt;
01094         case XK_Alt_R :        return Key::RAlt;
01095         case XK_Super_L :      return Key::LSystem;
01096         case XK_Super_R :      return Key::RSystem;
01097         case XK_Menu :         return Key::Menu;
01098         case XK_Escape :       return Key::Escape;
01099         case XK_semicolon :    return Key::SemiColon;
01100         case XK_slash :        return Key::Slash;
01101         case XK_equal :        return Key::Equal;
01102         case XK_minus :        return Key::Dash;
01103         case XK_bracketleft :  return Key::LBracket;
01104         case XK_bracketright : return Key::RBracket;
01105         case XK_comma :        return Key::Comma;
01106         case XK_period :       return Key::Period;
01107         case XK_dead_acute :   return Key::Quote;
01108         case XK_backslash :    return Key::BackSlash;
01109         case XK_dead_grave :   return Key::Tilde;
01110         case XK_space :        return Key::Space;
01111         case XK_Return :       return Key::Return;
01112         case XK_KP_Enter :     return Key::Return;
01113         case XK_BackSpace :    return Key::Back;
01114         case XK_Tab :          return Key::Tab;
01115         case XK_Prior :        return Key::PageUp;
01116         case XK_Next :         return Key::PageDown;
01117         case XK_End :          return Key::End;
01118         case XK_Home :         return Key::Home;
01119         case XK_Insert :       return Key::Insert;
01120         case XK_Delete :       return Key::Delete;
01121         case XK_KP_Add :       return Key::Add;
01122         case XK_KP_Subtract :  return Key::Subtract;
01123         case XK_KP_Multiply :  return Key::Multiply;
01124         case XK_KP_Divide :    return Key::Divide;
01125         case XK_Pause :        return Key::Pause;
01126         case XK_F1 :           return Key::F1;
01127         case XK_F2 :           return Key::F2;
01128         case XK_F3 :           return Key::F3;
01129         case XK_F4 :           return Key::F4;
01130         case XK_F5 :           return Key::F5;
01131         case XK_F6 :           return Key::F6;
01132         case XK_F7 :           return Key::F7;
01133         case XK_F8 :           return Key::F8;
01134         case XK_F9 :           return Key::F9;
01135         case XK_F10 :          return Key::F10;
01136         case XK_F11 :          return Key::F11;
01137         case XK_F12 :          return Key::F12;
01138         case XK_F13 :          return Key::F13;
01139         case XK_F14 :          return Key::F14;
01140         case XK_F15 :          return Key::F15;
01141         case XK_Left :         return Key::Left;
01142         case XK_Right :        return Key::Right;
01143         case XK_Up :           return Key::Up;
01144         case XK_Down :         return Key::Down;
01145         case XK_KP_0 :         return Key::Numpad0;
01146         case XK_KP_1 :         return Key::Numpad1;
01147         case XK_KP_2 :         return Key::Numpad2;
01148         case XK_KP_3 :         return Key::Numpad3;
01149         case XK_KP_4 :         return Key::Numpad4;
01150         case XK_KP_5 :         return Key::Numpad5;
01151         case XK_KP_6 :         return Key::Numpad6;
01152         case XK_KP_7 :         return Key::Numpad7;
01153         case XK_KP_8 :         return Key::Numpad8;
01154         case XK_KP_9 :         return Key::Numpad9;
01155         case XK_A :            return Key::A;
01156         case XK_Z :            return Key::Z;
01157         case XK_E :            return Key::E;
01158         case XK_R :            return Key::R;
01159         case XK_T :            return Key::T;
01160         case XK_Y :            return Key::Y;
01161         case XK_U :            return Key::U;
01162         case XK_I :            return Key::I;
01163         case XK_O :            return Key::O;
01164         case XK_P :            return Key::P;
01165         case XK_Q :            return Key::Q;
01166         case XK_S :            return Key::S;
01167         case XK_D :            return Key::D;
01168         case XK_F :            return Key::F;
01169         case XK_G :            return Key::G;
01170         case XK_H :            return Key::H;
01171         case XK_J :            return Key::J;
01172         case XK_K :            return Key::K;
01173         case XK_L :            return Key::L;
01174         case XK_M :            return Key::M;
01175         case XK_W :            return Key::W;
01176         case XK_X :            return Key::X;
01177         case XK_C :            return Key::C;
01178         case XK_V :            return Key::V;
01179         case XK_B :            return Key::B;
01180         case XK_N :            return Key::N;
01181         case XK_0 :            return Key::Num0;
01182         case XK_1 :            return Key::Num1;
01183         case XK_2 :            return Key::Num2;
01184         case XK_3 :            return Key::Num3;
01185         case XK_4 :            return Key::Num4;
01186         case XK_5 :            return Key::Num5;
01187         case XK_6 :            return Key::Num6;
01188         case XK_7 :            return Key::Num7;
01189         case XK_8 :            return Key::Num8;
01190         case XK_9 :            return Key::Num9;
01191     }
01192 
01193     return Key::Code(0);
01194 }
01195 
01196 
01200 bool WindowImplX11::OpenDisplay(bool AddWindow)
01201 {
01202     // If no display has been opened yet, open it
01203     if (ourDisplay == NULL)
01204     {
01205         ourDisplay = XOpenDisplay(NULL);
01206         if (ourDisplay)
01207         {
01208             ourScreen = DefaultScreen(ourDisplay);
01209 
01210             // Get the input method (XIM) object
01211             ourInputMethod = XOpenIM(ourDisplay, NULL, NULL, NULL);
01212         }
01213         else
01214         {
01215             std::cerr << "Failed to open a connection with the X server" << std::endl;
01216         }
01217     }
01218 
01219     // Increase the number of windows
01220     if (AddWindow)
01221         ourWindowsCount++;
01222 
01223     return ourDisplay != NULL;
01224 }
01225 
01226 
01230 void WindowImplX11::CloseDisplay()
01231 {
01232     // Decrease the number of windows
01233     ourWindowsCount--;
01234 
01235     // If all windows have been destroyed, then we can close the display
01236     if (ourWindowsCount == 0)
01237     {
01238         // Close the input method object
01239         if (ourInputMethod)
01240             XCloseIM(ourInputMethod);
01241 
01242         XCloseDisplay(ourDisplay);
01243         ourDisplay = NULL;
01244     }
01245 }
01246 
01247 } // namespace priv
01248 
01249 } // namespace sf