summaryrefslogtreecommitdiff
path: root/src/SFML/Window/Unix
diff options
context:
space:
mode:
authorJames Cowgill <james410@cowgill.org.uk>2015-05-08 21:14:39 +0100
committerJames Cowgill <james410@cowgill.org.uk>2015-05-08 21:14:39 +0100
commitebd1b636e5bf0f8fa6d210690582757e8b47f141 (patch)
tree02dc3aacf1c6f351154432247be0b4347fb14330 /src/SFML/Window/Unix
parentfa21c65d0c764705cfc377bf0d0de08fac26874e (diff)
Imported Upstream version 2.3+dfsg
Diffstat (limited to 'src/SFML/Window/Unix')
-rw-r--r--src/SFML/Window/Unix/Display.cpp278
-rw-r--r--src/SFML/Window/Unix/Display.hpp79
-rw-r--r--src/SFML/Window/Unix/GlxContext.cpp509
-rw-r--r--src/SFML/Window/Unix/GlxContext.hpp25
-rw-r--r--src/SFML/Window/Unix/GlxExtensions.cpp197
-rw-r--r--src/SFML/Window/Unix/GlxExtensions.hpp203
-rw-r--r--src/SFML/Window/Unix/GlxExtensions.txt11
-rw-r--r--src/SFML/Window/Unix/InputImpl.cpp442
-rw-r--r--src/SFML/Window/Unix/InputImpl.hpp2
-rw-r--r--src/SFML/Window/Unix/JoystickImpl.cpp568
-rw-r--r--src/SFML/Window/Unix/JoystickImpl.hpp7
-rw-r--r--src/SFML/Window/Unix/ScopedXcbPtr.hpp102
-rw-r--r--src/SFML/Window/Unix/ScopedXcbPtr.inl72
-rw-r--r--src/SFML/Window/Unix/SensorImpl.cpp2
-rw-r--r--src/SFML/Window/Unix/SensorImpl.hpp2
-rw-r--r--src/SFML/Window/Unix/VideoModeImpl.cpp262
-rw-r--r--src/SFML/Window/Unix/WindowImplX11.cpp2361
-rw-r--r--src/SFML/Window/Unix/WindowImplX11.hpp135
18 files changed, 4124 insertions, 1133 deletions
diff --git a/src/SFML/Window/Unix/Display.cpp b/src/SFML/Window/Unix/Display.cpp
index aad1b26..e537224 100644
--- a/src/SFML/Window/Unix/Display.cpp
+++ b/src/SFML/Window/Unix/Display.cpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -27,8 +27,12 @@
////////////////////////////////////////////////////////////
#include <SFML/System/Err.hpp>
#include <SFML/Window/Unix/Display.hpp>
+#include <SFML/Window/Unix/ScopedXcbPtr.hpp>
+#include <X11/keysym.h>
#include <cassert>
#include <cstdlib>
+#include <algorithm>
+#include <map>
namespace
@@ -36,6 +40,182 @@ namespace
// The shared display and its reference counter
Display* sharedDisplay = NULL;
unsigned int referenceCount = 0;
+
+ typedef std::map<std::string, xcb_atom_t> AtomMap;
+ AtomMap atoms;
+
+ bool mapBuilt = false;
+ xcb_keycode_t firstKeycode = 255;
+ xcb_keycode_t lastKeycode = 0;
+
+ // We use a simple array instead of a map => constant time lookup
+ // xcb_keycode_t can only contain 256 distinct values
+ xcb_keysym_t keysymMap[256];
+
+ xcb_keysym_t keysymToLower(xcb_keysym_t keysym)
+ {
+ switch(keysym >> 8)
+ {
+ // Latin 1
+ case 0:
+ {
+ if ((keysym >= XK_A) && (keysym <= XK_Z))
+ return keysym + (XK_a - XK_A);
+ else if ((keysym >= XK_Agrave) && (keysym <= XK_Odiaeresis))
+ return keysym + (XK_agrave - XK_Agrave);
+ else if ((keysym >= XK_Ooblique) && (keysym <= XK_Thorn))
+ return keysym + (XK_oslash - XK_Ooblique);
+ break;
+ }
+
+ // Latin 2
+ case 1:
+ {
+ if (keysym == XK_Aogonek)
+ return XK_aogonek;
+ else if (keysym >= XK_Lstroke && keysym <= XK_Sacute)
+ return keysym + (XK_lstroke - XK_Lstroke);
+ else if (keysym >= XK_Scaron && keysym <= XK_Zacute)
+ return keysym + (XK_scaron - XK_Scaron);
+ else if (keysym >= XK_Zcaron && keysym <= XK_Zabovedot)
+ return keysym + (XK_zcaron - XK_Zcaron);
+ else if (keysym >= XK_Racute && keysym <= XK_Tcedilla)
+ return keysym + (XK_racute - XK_Racute);
+ break;
+ }
+
+ // Latin 3
+ case 2:
+ {
+ if (keysym >= XK_Hstroke && keysym <= XK_Hcircumflex)
+ return keysym + (XK_hstroke - XK_Hstroke);
+ else if (keysym >= XK_Gbreve && keysym <= XK_Jcircumflex)
+ return keysym + (XK_gbreve - XK_Gbreve);
+ else if (keysym >= XK_Cabovedot && keysym <= XK_Scircumflex)
+ return keysym + (XK_cabovedot - XK_Cabovedot);
+ break;
+ }
+
+ // Latin 4
+ case 3:
+ {
+ if (keysym >= XK_Rcedilla && keysym <= XK_Tslash)
+ return keysym + (XK_rcedilla - XK_Rcedilla);
+ else if (keysym == XK_ENG)
+ return XK_eng;
+ else if (keysym >= XK_Amacron && keysym <= XK_Umacron)
+ return keysym + (XK_amacron - XK_Amacron);
+ break;
+ }
+
+ // Cyrillic
+ case 6:
+ {
+ if (keysym >= XK_Serbian_DJE && keysym <= XK_Serbian_DZE)
+ return keysym - (XK_Serbian_DJE - XK_Serbian_dje);
+ else if (keysym >= XK_Cyrillic_YU && keysym <= XK_Cyrillic_HARDSIGN)
+ return keysym - (XK_Cyrillic_YU - XK_Cyrillic_yu);
+ break;
+ }
+
+ // Greek
+ case 7:
+ {
+ if (keysym >= XK_Greek_ALPHAaccent && keysym <= XK_Greek_OMEGAaccent)
+ return keysym + (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
+ else if (keysym >= XK_Greek_ALPHA && keysym <= XK_Greek_OMEGA)
+ return keysym + (XK_Greek_alpha - XK_Greek_ALPHA);
+ break;
+ }
+
+ // Armenian
+ case 0x14:
+ {
+ if (keysym >= XK_Armenian_AYB && keysym <= XK_Armenian_fe) {
+ return (keysym | 1);
+ }
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ return keysym;
+ }
+
+ void buildMap()
+ {
+ // Open a connection with the X server
+ xcb_connection_t* connection = sf::priv::OpenConnection();
+
+ firstKeycode = xcb_get_setup(connection)->min_keycode;
+ lastKeycode = xcb_get_setup(connection)->max_keycode;
+
+ sf::priv::ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ sf::priv::ScopedXcbPtr<xcb_get_keyboard_mapping_reply_t> keyboardMapping(xcb_get_keyboard_mapping_reply(
+ connection,
+ xcb_get_keyboard_mapping(
+ connection,
+ firstKeycode,
+ lastKeycode - firstKeycode + 1
+ ),
+ &error
+ ));
+
+ sf::priv::CloseConnection(connection);
+
+ if (error || !keyboardMapping)
+ {
+ sf::err() << "Failed to get keyboard mapping" << std::endl;
+ return;
+ }
+
+ uint8_t keysymsPerKeycode = keyboardMapping->keysyms_per_keycode;
+
+ if (!keysymsPerKeycode)
+ {
+ sf::err() << "Error: No keysyms per keycode" << std::endl;
+ return;
+ }
+
+ const xcb_keysym_t* keysyms = xcb_get_keyboard_mapping_keysyms(keyboardMapping.get());
+
+ if (!keysyms)
+ {
+ sf::err() << "Failed to get keyboard mapping keysyms" << std::endl;
+ return;
+ }
+
+ xcb_keycode_t range = lastKeycode - firstKeycode + 1;
+
+ std::fill(keysymMap, keysymMap + 256, XK_VoidSymbol);
+
+ for (xcb_keycode_t i = firstKeycode; ; ++i)
+ {
+ const xcb_keysym_t* keysym = &keysyms[(i - firstKeycode) * keysymsPerKeycode];
+
+ if ((keysymsPerKeycode == 1) || (keysym[1] == XCB_NO_SYMBOL))
+ {
+ keysymMap[i] = keysymToLower(keysym[0]);
+
+ if (i == lastKeycode)
+ break;
+
+ continue;
+ }
+
+ keysymMap[i] = keysym[0];
+
+ if (i == lastKeycode)
+ break;
+ }
+
+ mapBuilt = true;
+ }
}
namespace sf
@@ -64,6 +244,13 @@ Display* OpenDisplay()
////////////////////////////////////////////////////////////
+xcb_connection_t* OpenConnection()
+{
+ return XGetXCBConnection(OpenDisplay());
+}
+
+
+////////////////////////////////////////////////////////////
void CloseDisplay(Display* display)
{
assert(display == sharedDisplay);
@@ -73,6 +260,95 @@ void CloseDisplay(Display* display)
XCloseDisplay(display);
}
+
+////////////////////////////////////////////////////////////
+void CloseConnection(xcb_connection_t* connection)
+{
+ assert(connection == XGetXCBConnection(sharedDisplay));
+ return CloseDisplay(sharedDisplay);
+}
+
+
+////////////////////////////////////////////////////////////
+xcb_screen_t* XCBScreenOfDisplay(xcb_connection_t* connection, int screen_nbr)
+{
+ xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
+
+ for (; iter.rem; --screen_nbr, xcb_screen_next (&iter))
+ {
+ if (screen_nbr == 0)
+ return iter.data;
+ }
+
+ return NULL;
+}
+
+
+////////////////////////////////////////////////////////////
+xcb_screen_t* XCBDefaultScreen(xcb_connection_t* connection)
+{
+ assert(connection == XGetXCBConnection(sharedDisplay));
+ return XCBScreenOfDisplay(connection, XDefaultScreen(sharedDisplay));
+}
+
+
+////////////////////////////////////////////////////////////
+xcb_window_t XCBDefaultRootWindow(xcb_connection_t* connection)
+{
+ assert(connection == XGetXCBConnection(sharedDisplay));
+ xcb_screen_t* screen = XCBScreenOfDisplay(connection, XDefaultScreen(sharedDisplay));
+ if (screen)
+ return screen->root;
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////
+xcb_atom_t getAtom(const std::string& name, bool onlyIfExists)
+{
+ AtomMap::const_iterator iter = atoms.find(name);
+
+ if (iter != atoms.end())
+ return iter->second;
+
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ xcb_connection_t* connection = OpenConnection();
+
+ ScopedXcbPtr<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(
+ connection,
+ xcb_intern_atom(
+ connection,
+ onlyIfExists,
+ name.size(),
+ name.c_str()
+ ),
+ &error
+ ));
+
+ CloseConnection(connection);
+
+ if (error || !reply)
+ {
+ err() << "Failed to get " << name << " atom." << std::endl;
+ return XCB_ATOM_NONE;
+ }
+
+ atoms[name] = reply->atom;
+
+ return reply->atom;
+}
+
+
+////////////////////////////////////////////////////////////
+const xcb_keysym_t* getKeysymMap()
+{
+ if (!mapBuilt)
+ buildMap();
+
+ return keysymMap;
+}
+
} // namespace priv
} // namespace sf
diff --git a/src/SFML/Window/Unix/Display.hpp b/src/SFML/Window/Unix/Display.hpp
index df018b3..1cda4a7 100644
--- a/src/SFML/Window/Unix/Display.hpp
+++ b/src/SFML/Window/Unix/Display.hpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -28,7 +28,8 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
-#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+#include <string>
namespace sf
@@ -47,13 +48,85 @@ namespace priv
Display* OpenDisplay();
////////////////////////////////////////////////////////////
-/// \brief Release a reference to the shared
+/// \brief Get the xcb connection of the shared Display
+///
+/// This function increments the reference count of the display,
+/// it must be matched with a call to CloseDisplay.
+///
+/// \return Pointer to the shared connection
+///
+////////////////////////////////////////////////////////////
+xcb_connection_t* OpenConnection();
+
+////////////////////////////////////////////////////////////
+/// \brief Release a reference to the shared display
///
/// \param display Display to release
///
////////////////////////////////////////////////////////////
void CloseDisplay(Display* display);
+////////////////////////////////////////////////////////////
+/// \brief Release a reference to the shared display
+///
+/// \param connection Connection of display to release
+///
+////////////////////////////////////////////////////////////
+void CloseConnection(xcb_connection_t* connection);
+
+////////////////////////////////////////////////////////////
+/// \brief Get screen of a display by index (equivalent to XScreenOfDisplay)
+///
+/// \param connection Connection of display
+/// \param screen_nbr The index of the screen
+///
+/// \return Pointer to the screen
+///
+////////////////////////////////////////////////////////////
+xcb_screen_t* XCBScreenOfDisplay(xcb_connection_t* connection, int screen_nbr);
+
+////////////////////////////////////////////////////////////
+/// \brief Get default screen of a display (equivalent to XDefaultScreen)
+///
+/// \param connection Connection of display
+///
+/// \return Pointer to the default screen of the display
+///
+////////////////////////////////////////////////////////////
+xcb_screen_t* XCBDefaultScreen(xcb_connection_t* connection);
+
+////////////////////////////////////////////////////////////
+/// \brief Get default root window of a display (equivalent to XDefaultRootWindow)
+///
+/// \param connection Connection of display
+///
+/// \return Root window of the display
+///
+////////////////////////////////////////////////////////////
+xcb_window_t XCBDefaultRootWindow(xcb_connection_t* connection);
+
+////////////////////////////////////////////////////////////
+/// \brief Get the atom with the specified name
+///
+/// \param name Name of the atom
+/// \param onlyIfExists Don't try to create the atom if it doesn't already exist
+///
+/// \return Atom if it exists or XCB_ATOM_NONE (0) if it doesn't
+///
+////////////////////////////////////////////////////////////
+xcb_atom_t getAtom(const std::string& name, bool onlyIfExists = false);
+
+////////////////////////////////////////////////////////////
+/// \brief Get the keycode to keysym map
+///
+/// Contains 255 values. Use the keycode as the index
+/// into the array to retrieve its keysym.
+///
+/// \return Keycode to keysym map
+///
+////////////////////////////////////////////////////////////
+const xcb_keysym_t* getKeysymMap();
+
} // namespace priv
} // namespace sf
diff --git a/src/SFML/Window/Unix/GlxContext.cpp b/src/SFML/Window/Unix/GlxContext.cpp
index eac6099..c89c9d3 100644
--- a/src/SFML/Window/Unix/GlxContext.cpp
+++ b/src/SFML/Window/Unix/GlxContext.cpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -28,15 +28,72 @@
#include <SFML/Window/Unix/GlxContext.hpp>
#include <SFML/Window/Unix/WindowImplX11.hpp>
#include <SFML/Window/Unix/Display.hpp>
-#include <SFML/OpenGL.hpp>
+#include <SFML/System/Mutex.hpp>
+#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
+#if !defined(GLX_DEBUGGING) && defined(SFML_DEBUG)
+ // Enable this to print messages to err() everytime GLX produces errors
+ //#define GLX_DEBUGGING
+#endif
+
+namespace
+{
+ sf::Mutex glxErrorMutex;
+ bool glxErrorOccurred = false;
+
+ int HandleXError(::Display*, XErrorEvent*)
+ {
+ glxErrorOccurred = true;
+ return 0;
+ }
+
+ class GlxErrorHandler
+ {
+ public:
+
+ GlxErrorHandler(::Display* display) :
+ m_display(display),
+ m_lock (glxErrorMutex)
+ {
+ glxErrorOccurred = false;
+ m_previousHandler = XSetErrorHandler(HandleXError);
+ }
+
+ ~GlxErrorHandler()
+ {
+ XSync(m_display, False);
+ XSetErrorHandler(m_previousHandler);
+ }
+
+ private:
+ sf::Lock m_lock;
+ ::Display* m_display;
+ int (*m_previousHandler)(::Display*, XErrorEvent*);
+ };
+}
+
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
+void ensureExtensionsInit(::Display* display, int screen)
+{
+ static bool initialized = false;
+ if (!initialized)
+ {
+ initialized = true;
+
+ // We don't check the return value since the extension
+ // flags are cleared even if loading fails
+ sfglx_LoadFunctions(display, screen);
+ }
+}
+
+
+////////////////////////////////////////////////////////////
GlxContext::GlxContext(GlxContext* shared) :
m_window (0),
m_context (NULL),
@@ -44,18 +101,32 @@ m_ownsWindow(true)
{
// Open a connection with the X server
m_display = OpenDisplay();
+ m_connection = XGetXCBConnection(m_display);
+ xcb_screen_t* screen = XCBScreenOfDisplay(m_connection, DefaultScreen(m_display));
+
+ // Choose the visual according to the context settings
+ XVisualInfo visualInfo = selectBestVisual(m_display, VideoMode::getDesktopMode().bitsPerPixel, ContextSettings());
+
+ // Define the window attributes
+ xcb_colormap_t colormap = xcb_generate_id(m_connection);
+ xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, screen->root, visualInfo.visualid);
+ const uint32_t value_list[] = {colormap};
// Create a dummy window (disabled and hidden)
- int screen = DefaultScreen(m_display);
- m_window = XCreateWindow(m_display,
- RootWindow(m_display, screen),
- 0, 0,
- 1, 1,
- 0,
- DefaultDepth(m_display, screen),
- InputOutput,
- DefaultVisual(m_display, screen),
- 0, NULL);
+ m_window = xcb_generate_id(m_connection);
+ xcb_create_window(
+ m_connection,
+ static_cast<uint8_t>(visualInfo.depth),
+ m_window,
+ screen->root,
+ 0, 0,
+ 1, 1,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ visualInfo.visualid,
+ XCB_CW_COLORMAP,
+ value_list
+ );
// Create the context
createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, ContextSettings());
@@ -71,6 +142,7 @@ m_ownsWindow(false)
// Open a connection with the X server
// (important: must be the same display as the owner window)
m_display = OpenDisplay();
+ m_connection = XGetXCBConnection(m_display);
// Get the owner window and its device context
m_window = static_cast< ::Window>(owner->getSystemHandle());
@@ -89,18 +161,32 @@ m_ownsWindow(true)
{
// Open a connection with the X server
m_display = OpenDisplay();
+ m_connection = XGetXCBConnection(m_display);
+ xcb_screen_t* screen = XCBScreenOfDisplay(m_connection, DefaultScreen(m_display));
+
+ // Choose the visual according to the context settings
+ XVisualInfo visualInfo = selectBestVisual(m_display, VideoMode::getDesktopMode().bitsPerPixel, settings);
+
+ // Define the window attributes
+ xcb_colormap_t colormap = xcb_generate_id(m_connection);
+ xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, screen->root, visualInfo.visualid);
+ const uint32_t value_list[] = {colormap};
// Create the hidden window
- int screen = DefaultScreen(m_display);
- m_window = XCreateWindow(m_display,
- RootWindow(m_display, screen),
- 0, 0,
- width, height,
- 0,
- DefaultDepth(m_display, screen),
- InputOutput,
- DefaultVisual(m_display, screen),
- 0, NULL);
+ m_window = xcb_generate_id(m_connection);
+ xcb_create_window(
+ m_connection,
+ static_cast<uint8_t>(visualInfo.depth),
+ m_window,
+ screen->root,
+ 0, 0,
+ width, height,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ visualInfo.visualid,
+ XCB_CW_COLORMAP,
+ value_list
+ );
// Create the context
createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, settings);
@@ -113,16 +199,25 @@ GlxContext::~GlxContext()
// Destroy the context
if (m_context)
{
+#if defined(GLX_DEBUGGING)
+ GlxErrorHandler handler(m_display);
+#endif
+
if (glXGetCurrentContext() == m_context)
glXMakeCurrent(m_display, None, NULL);
glXDestroyContext(m_display, m_context);
+
+#if defined(GLX_DEBUGGING)
+ if (glxErrorOccurred)
+ err() << "GLX error in GlxContext::~GlxContext()" << std::endl;
+#endif
}
// Destroy the window if we own it
if (m_window && m_ownsWindow)
{
- XDestroyWindow(m_display, m_window);
- XFlush(m_display);
+ xcb_destroy_window(m_connection, m_window);
+ xcb_flush(m_connection);
}
// Close the connection with the X server
@@ -131,27 +226,88 @@ GlxContext::~GlxContext()
////////////////////////////////////////////////////////////
+GlFunctionPointer GlxContext::getFunction(const char* name)
+{
+ return reinterpret_cast<GlFunctionPointer>(glXGetProcAddressARB(reinterpret_cast<const GLubyte*>(name)));
+}
+
+
+////////////////////////////////////////////////////////////
bool GlxContext::makeCurrent()
{
- return m_context && glXMakeCurrent(m_display, m_window, m_context);
+ if (!m_context)
+ return false;
+
+#if defined(GLX_DEBUGGING)
+ GlxErrorHandler handler(m_display);
+#endif
+
+ bool result = glXMakeCurrent(m_display, m_window, m_context);
+
+#if defined(GLX_DEBUGGING)
+ if (glxErrorOccurred)
+ err() << "GLX error in GlxContext::makeCurrent()" << std::endl;
+#endif
+
+ return result;
}
////////////////////////////////////////////////////////////
void GlxContext::display()
{
+#if defined(GLX_DEBUGGING)
+ GlxErrorHandler handler(m_display);
+#endif
+
if (m_window)
glXSwapBuffers(m_display, m_window);
+
+#if defined(GLX_DEBUGGING)
+ if (glxErrorOccurred)
+ err() << "GLX error in GlxContext::display()" << std::endl;
+#endif
}
////////////////////////////////////////////////////////////
void GlxContext::setVerticalSyncEnabled(bool enabled)
{
- const GLubyte* name = reinterpret_cast<const GLubyte*>("glXSwapIntervalSGI");
- PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = reinterpret_cast<PFNGLXSWAPINTERVALSGIPROC>(glXGetProcAddress(name));
- if (glXSwapIntervalSGI)
- glXSwapIntervalSGI(enabled ? 1 : 0);
+ // Make sure that extensions are initialized
+ ensureExtensionsInit(m_display, DefaultScreen(m_display));
+
+ int result = 0;
+
+ // Prioritize the EXT variant and fall back to MESA or SGI if needed
+ // We use the direct pointer to the MESA entry point instead of the alias
+ // because glx.h declares the entry point as an external function
+ // which would require us to link in an additional library
+ if (sfglx_ext_EXT_swap_control == sfglx_LOAD_SUCCEEDED)
+ {
+ glXSwapIntervalEXT(m_display, glXGetCurrentDrawable(), enabled ? 1 : 0);
+ }
+ else if (sfglx_ext_MESA_swap_control == sfglx_LOAD_SUCCEEDED)
+ {
+ result = sf_ptrc_glXSwapIntervalMESA(enabled ? 1 : 0);
+ }
+ else if (sfglx_ext_SGI_swap_control == sfglx_LOAD_SUCCEEDED)
+ {
+ result = glXSwapIntervalSGI(enabled ? 1 : 0);
+ }
+ else
+ {
+ static bool warned = false;
+
+ if (!warned)
+ {
+ err() << "Setting vertical sync not supported" << std::endl;
+
+ warned = true;
+ }
+ }
+
+ if (result != 0)
+ err() << "Setting vertical sync failed" << std::endl;
}
@@ -164,7 +320,7 @@ XVisualInfo GlxContext::selectBestVisual(::Display* display, unsigned int bitsPe
if (visuals)
{
// Evaluate all the returned visuals, and pick the best one
- int bestScore = 0xFFFF;
+ int bestScore = 0x7FFFFFFF;
XVisualInfo bestVisual;
for (int i = 0; i < count; ++i)
{
@@ -176,18 +332,30 @@ XVisualInfo GlxContext::selectBestVisual(::Display* display, unsigned int bitsPe
// Extract the components of the current visual
int red, green, blue, alpha, depth, stencil, multiSampling, samples;
- glXGetConfig(display, &visuals[i], GLX_RED_SIZE, &red);
- glXGetConfig(display, &visuals[i], GLX_GREEN_SIZE, &green);
- glXGetConfig(display, &visuals[i], GLX_BLUE_SIZE, &blue);
- glXGetConfig(display, &visuals[i], GLX_ALPHA_SIZE, &alpha);
- glXGetConfig(display, &visuals[i], GLX_DEPTH_SIZE, &depth);
- glXGetConfig(display, &visuals[i], GLX_STENCIL_SIZE, &stencil);
- glXGetConfig(display, &visuals[i], GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
- glXGetConfig(display, &visuals[i], GLX_SAMPLES_ARB, &samples);
+ glXGetConfig(display, &visuals[i], GLX_RED_SIZE, &red);
+ glXGetConfig(display, &visuals[i], GLX_GREEN_SIZE, &green);
+ glXGetConfig(display, &visuals[i], GLX_BLUE_SIZE, &blue);
+ glXGetConfig(display, &visuals[i], GLX_ALPHA_SIZE, &alpha);
+ glXGetConfig(display, &visuals[i], GLX_DEPTH_SIZE, &depth);
+ glXGetConfig(display, &visuals[i], GLX_STENCIL_SIZE, &stencil);
+
+ if (sfglx_ext_ARB_multisample == sfglx_LOAD_SUCCEEDED)
+ {
+ glXGetConfig(display, &visuals[i], GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
+ glXGetConfig(display, &visuals[i], GLX_SAMPLES_ARB, &samples);
+ }
+ else
+ {
+ multiSampling = 0;
+ samples = 0;
+ }
+
+ // TODO: Replace this with proper acceleration detection
+ bool accelerated = true;
// Evaluate the visual
int color = red + green + blue + alpha;
- int score = evaluateFormat(bitsPerPixel, settings, color, depth, stencil, multiSampling ? samples : 0);
+ int score = evaluateFormat(bitsPerPixel, settings, color, depth, stencil, multiSampling ? samples : 0, accelerated);
// If it's better than the current best, make it the new best
if (score < bestScore)
@@ -211,126 +379,207 @@ XVisualInfo GlxContext::selectBestVisual(::Display* display, unsigned int bitsPe
}
}
-
////////////////////////////////////////////////////////////
void GlxContext::createContext(GlxContext* shared, unsigned int bitsPerPixel, const ContextSettings& settings)
{
- XVisualInfo* visualInfo = NULL;
-
// Save the creation settings
m_settings = settings;
+ // Retrieve the attributes of the target window
+ XWindowAttributes windowAttributes;
+ if (XGetWindowAttributes(m_display, m_window, &windowAttributes) == 0)
+ {
+ err() << "Failed to get the window attributes" << std::endl;
+ return;
+ }
+
+ // Get its visuals
+ XVisualInfo tpl;
+ tpl.screen = DefaultScreen(m_display);
+ tpl.visualid = XVisualIDFromVisual(windowAttributes.visual);
+ int nbVisuals = 0;
+ XVisualInfo* visualInfo = XGetVisualInfo(m_display, VisualIDMask | VisualScreenMask, &tpl, &nbVisuals);
+
// Get the context to share display lists with
GLXContext toShare = shared ? shared->m_context : NULL;
- // Create the OpenGL context -- first try context versions >= 3.0 if it is requested (they require special code)
- if (m_settings.majorVersion >= 3)
+ // There are no GLX versions prior to 1.0
+ int major = 0;
+ int minor = 0;
+
+ if (!glXQueryVersion(m_display, &major, &minor))
+ err() << "Failed to query GLX version, limited to legacy context creation" << std::endl;
+
+ // Make sure that extensions are initialized if this is not the shared context
+ // The shared context is the context used to initialize the extensions
+ if (shared)
+ ensureExtensionsInit(m_display, DefaultScreen(m_display));
+
+ // Check if glXCreateContextAttribsARB is available (requires GLX 1.3 or greater)
+ bool hasCreateContextArb = (sfglx_ext_ARB_create_context == sfglx_LOAD_SUCCEEDED) && ((major > 1) || (minor >= 3));
+
+ // Check if we need to use glXCreateContextAttribsARB
+ bool needCreateContextArb = false;
+
+ if (m_settings.attributeFlags)
+ needCreateContextArb = true;
+ else if (m_settings.majorVersion >= 3)
+ needCreateContextArb = true;
+
+ // Create the OpenGL context -- first try using glXCreateContextAttribsARB if we need to
+ if (hasCreateContextArb && needCreateContextArb)
{
- const GLubyte* name = reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB");
- PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(name));
- if (glXCreateContextAttribsARB)
+ // Get a GLXFBConfig that matches the the window's visual, for glXCreateContextAttribsARB
+ GLXFBConfig* config = NULL;
+
+ // We don't supply attributes to match against, since
+ // the visual we are matching against was already
+ // deemed suitable in selectBestVisual()
+ int nbConfigs = 0;
+ GLXFBConfig* configs = glXChooseFBConfig(m_display, DefaultScreen(m_display), NULL, &nbConfigs);
+
+ for (int i = 0; configs && (i < nbConfigs); ++i)
{
- // Select a GLXFB config that matches the requested context settings
- int nbConfigs = 0;
- int fbAttributes[] =
+ XVisualInfo* visual = glXGetVisualFromFBConfig(m_display, configs[i]);
+
+ if (!visual)
+ continue;
+
+ if (visual->visualid == visualInfo->visualid)
+ {
+ config = &configs[i];
+ break;
+ }
+ }
+
+ if (!config)
+ err() << "Failed to get GLXFBConfig which corresponds to the window's visual" << std::endl;
+
+ while (config && !m_context && m_settings.majorVersion)
+ {
+ // Check if setting the profile is supported
+ if (sfglx_ext_ARB_create_context_profile == sfglx_LOAD_SUCCEEDED)
+ {
+ int profile = (m_settings.attributeFlags & ContextSettings::Core) ? GLX_CONTEXT_CORE_PROFILE_BIT_ARB : GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
+ int debug = (m_settings.attributeFlags & ContextSettings::Debug) ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
+
+ // Create the context
+ int attributes[] =
+ {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, static_cast<int>(m_settings.majorVersion),
+ GLX_CONTEXT_MINOR_VERSION_ARB, static_cast<int>(m_settings.minorVersion),
+ GLX_CONTEXT_PROFILE_MASK_ARB, profile,
+ GLX_CONTEXT_FLAGS_ARB, debug,
+ 0, 0
+ };
+
+ // RAII GLX error handler (we simply ignore errors here)
+ // On an error, glXCreateContextAttribsARB will return 0 anyway
+ GlxErrorHandler handler(m_display);
+
+ m_context = glXCreateContextAttribsARB(m_display, *config, toShare, true, attributes);
+ }
+ else
{
- GLX_DEPTH_SIZE, static_cast<int>(settings.depthBits),
- GLX_STENCIL_SIZE, static_cast<int>(settings.stencilBits),
- GLX_SAMPLE_BUFFERS, settings.antialiasingLevel > 0,
- GLX_SAMPLES, static_cast<int>(settings.antialiasingLevel),
- GLX_RED_SIZE, 8,
- GLX_GREEN_SIZE, 8,
- GLX_BLUE_SIZE, 8,
- GLX_ALPHA_SIZE, bitsPerPixel == 32 ? 8 : 0,
- GLX_DOUBLEBUFFER, True,
- GLX_X_RENDERABLE, True,
- GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
- GLX_RENDER_TYPE, GLX_RGBA_BIT,
- GLX_CONFIG_CAVEAT, GLX_NONE,
- None
- };
- GLXFBConfig* configs = glXChooseFBConfig(m_display, DefaultScreen(m_display), fbAttributes, &nbConfigs);
- if (configs && nbConfigs)
+ if ((m_settings.attributeFlags & ContextSettings::Core) || (m_settings.attributeFlags & ContextSettings::Debug))
+ err() << "Selecting a profile during context creation is not supported,"
+ << "disabling comptibility and debug" << std::endl;
+
+ m_settings.attributeFlags = ContextSettings::Default;
+
+ // Create the context
+ int attributes[] =
+ {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, static_cast<int>(m_settings.majorVersion),
+ GLX_CONTEXT_MINOR_VERSION_ARB, static_cast<int>(m_settings.minorVersion),
+ 0, 0
+ };
+
+ // RAII GLX error handler (we simply ignore errors here)
+ // On an error, glXCreateContextAttribsARB will return 0 anyway
+ GlxErrorHandler handler(m_display);
+
+ m_context = glXCreateContextAttribsARB(m_display, *config, toShare, true, attributes);
+ }
+
+ if (!m_context)
{
- while (!m_context && (m_settings.majorVersion >= 3))
+ // If we couldn't create the context, first try disabling flags,
+ // then lower the version number and try again -- stop at 0.0
+ // Invalid version numbers will be generated by this algorithm (like 3.9), but we really don't care
+ if (m_settings.attributeFlags != ContextSettings::Default)
{
- // Create the context
- int attributes[] =
- {
- GLX_CONTEXT_MAJOR_VERSION_ARB, static_cast<int>(m_settings.majorVersion),
- GLX_CONTEXT_MINOR_VERSION_ARB, static_cast<int>(m_settings.minorVersion),
- GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
- 0, 0
- };
- m_context = glXCreateContextAttribsARB(m_display, configs[0], toShare, true, attributes);
-
- if (m_context)
- {
- // Ok: retrieve the config's visual
- visualInfo = glXGetVisualFromFBConfig(m_display, configs[0]);
- }
- else
- {
- // If we couldn't create the context, lower the version number and try again -- stop at 3.0
- // Invalid version numbers will be generated by this algorithm (like 3.9), but we really don't care
- if (m_settings.minorVersion > 0)
- {
- // If the minor version is not 0, we decrease it and try again
- m_settings.minorVersion--;
- }
- else
- {
- // If the minor version is 0, we decrease the major version
- m_settings.majorVersion--;
- m_settings.minorVersion = 9;
- }
- }
+ m_settings.attributeFlags = ContextSettings::Default;
+ }
+ else if (m_settings.minorVersion > 0)
+ {
+ // If the minor version is not 0, we decrease it and try again
+ m_settings.minorVersion--;
+
+ m_settings.attributeFlags = settings.attributeFlags;
+ }
+ else
+ {
+ // If the minor version is 0, we decrease the major version
+ m_settings.majorVersion--;
+ m_settings.minorVersion = 9;
+
+ m_settings.attributeFlags = settings.attributeFlags;
}
- XFree(configs);
}
}
+
+ if (configs)
+ XFree(configs);
}
- // If the OpenGL >= 3.0 context failed or if we don't want one, create a regular OpenGL 1.x/2.x context
+ // If glXCreateContextAttribsARB failed, use glXCreateContext
if (!m_context)
{
- // set the context version to 2.0 (arbitrary)
+ // set the context version to 2.1 (arbitrary) and disable flags
m_settings.majorVersion = 2;
- m_settings.minorVersion = 0;
-
- // Retrieve the attributes of the target window
- XWindowAttributes windowAttributes;
- if (XGetWindowAttributes(m_display, m_window, &windowAttributes) == 0)
- {
- err() << "Failed to get the window attributes" << std::endl;
- return;
- }
+ m_settings.minorVersion = 1;
+ m_settings.attributeFlags = ContextSettings::Default;
- // Get its visual
- XVisualInfo tpl;
- tpl.screen = DefaultScreen(m_display);
- tpl.visualid = XVisualIDFromVisual(windowAttributes.visual);
- int nbVisuals = 0;
- visualInfo = XGetVisualInfo(m_display, VisualIDMask | VisualScreenMask, &tpl, &nbVisuals);
+#if defined(GLX_DEBUGGING)
+ GlxErrorHandler handler(m_display);
+#endif
// Create the context, using the target window's visual
m_context = glXCreateContext(m_display, visualInfo, toShare, true);
- if (!m_context)
+
+#if defined(GLX_DEBUGGING)
+ if (glxErrorOccurred)
+ err() << "GLX error in GlxContext::createContext()" << std::endl;
+#endif
+ }
+
+ if (!m_context)
+ {
+ err() << "Failed to create an OpenGL context for this window" << std::endl;
+ }
+ else
+ {
+ // Update the creation settings from the chosen format
+ int depth, stencil, multiSampling, samples;
+ glXGetConfig(m_display, visualInfo, GLX_DEPTH_SIZE, &depth);
+ glXGetConfig(m_display, visualInfo, GLX_STENCIL_SIZE, &stencil);
+
+ if (sfglx_ext_ARB_multisample == sfglx_LOAD_SUCCEEDED)
{
- err() << "Failed to create an OpenGL context for this window" << std::endl;
- return;
+ glXGetConfig(m_display, visualInfo, GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
+ glXGetConfig(m_display, visualInfo, GLX_SAMPLES_ARB, &samples);
+ }
+ else
+ {
+ multiSampling = 0;
+ samples = 0;
}
- }
- // Update the creation settings from the chosen format
- int depth, stencil, multiSampling, samples;
- glXGetConfig(m_display, visualInfo, GLX_DEPTH_SIZE, &depth);
- glXGetConfig(m_display, visualInfo, GLX_STENCIL_SIZE, &stencil);
- glXGetConfig(m_display, visualInfo, GLX_SAMPLE_BUFFERS_ARB, &multiSampling);
- glXGetConfig(m_display, visualInfo, GLX_SAMPLES_ARB, &samples);
- m_settings.depthBits = static_cast<unsigned int>(depth);
- m_settings.stencilBits = static_cast<unsigned int>(stencil);
- m_settings.antialiasingLevel = multiSampling ? samples : 0;
+ m_settings.depthBits = static_cast<unsigned int>(depth);
+ m_settings.stencilBits = static_cast<unsigned int>(stencil);
+ m_settings.antialiasingLevel = multiSampling ? samples : 0;
+ }
// Free the visual info
XFree(visualInfo);
diff --git a/src/SFML/Window/Unix/GlxContext.hpp b/src/SFML/Window/Unix/GlxContext.hpp
index 0b5982f..e1ad899 100644
--- a/src/SFML/Window/Unix/GlxContext.hpp
+++ b/src/SFML/Window/Unix/GlxContext.hpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -29,8 +29,8 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/GlContext.hpp>
-#include <X11/Xlib.h>
-#include <GL/glx.h>
+#include <SFML/Window/Unix/GlxExtensions.hpp>
+#include <X11/Xlib-xcb.h>
namespace sf
@@ -82,6 +82,16 @@ public:
~GlxContext();
////////////////////////////////////////////////////////////
+ /// \brief Get the address of an OpenGL function
+ ///
+ /// \param name Name of the function to get the address of
+ ///
+ /// \return Address of the OpenGL function, 0 on failure
+ ///
+ ////////////////////////////////////////////////////////////
+ static GlFunctionPointer getFunction(const char* name);
+
+ ////////////////////////////////////////////////////////////
/// \brief Activate the context as the current target for rendering
///
/// \return True on success, false if any error happened
@@ -135,10 +145,11 @@ private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
- ::Display* m_display; ///< Connection to the X server
- ::Window m_window; ///< Window to which the context is attached
- GLXContext m_context; ///< OpenGL context
- bool m_ownsWindow; ///< Do we own the window associated to the context?
+ ::Display* m_display; ///< Connection to the X server
+ ::Window m_window; ///< Window to which the context is attached
+ xcb_connection_t* m_connection; ///< Pointer to the xcb connection
+ GLXContext m_context; ///< OpenGL context
+ bool m_ownsWindow; ///< Do we own the window associated to the context?
};
} // namespace priv
diff --git a/src/SFML/Window/Unix/GlxExtensions.cpp b/src/SFML/Window/Unix/GlxExtensions.cpp
new file mode 100644
index 0000000..029095c
--- /dev/null
+++ b/src/SFML/Window/Unix/GlxExtensions.cpp
@@ -0,0 +1,197 @@
+////////////////////////////////////////////////////////////
+//
+// SFML - Simple and Fast Multimedia Library
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
+//
+// 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.
+//
+////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////
+// Headers
+////////////////////////////////////////////////////////////
+#include <SFML/Window/Unix/GlxExtensions.hpp>
+#include <SFML/Window/Context.hpp>
+#include <cstdlib>
+#include <cstring>
+#include <cstddef>
+
+static sf::GlFunctionPointer IntGetProcAddress(const char* name)
+{
+ return sf::Context::getFunction(name);
+}
+
+int sfglx_ext_EXT_swap_control = sfglx_LOAD_FAILED;
+int sfglx_ext_MESA_swap_control = sfglx_LOAD_FAILED;
+int sfglx_ext_SGI_swap_control = sfglx_LOAD_FAILED;
+int sfglx_ext_ARB_multisample = sfglx_LOAD_FAILED;
+int sfglx_ext_ARB_create_context = sfglx_LOAD_FAILED;
+int sfglx_ext_ARB_create_context_profile = sfglx_LOAD_FAILED;
+
+void (CODEGEN_FUNCPTR *sf_ptrc_glXSwapIntervalEXT)(Display *, GLXDrawable, int) = NULL;
+
+static int Load_EXT_swap_control(void)
+{
+ int numFailed = 0;
+ sf_ptrc_glXSwapIntervalEXT = (void (CODEGEN_FUNCPTR *)(Display *, GLXDrawable, int))IntGetProcAddress("glXSwapIntervalEXT");
+ if(!sf_ptrc_glXSwapIntervalEXT) numFailed++;
+ return numFailed;
+}
+
+int (CODEGEN_FUNCPTR *sf_ptrc_glXSwapIntervalMESA)(int) = NULL;
+
+static int Load_MESA_swap_control(void)
+{
+ int numFailed = 0;
+ sf_ptrc_glXSwapIntervalMESA = (int (CODEGEN_FUNCPTR *)(int))IntGetProcAddress("glXSwapIntervalMESA");
+ if(!sf_ptrc_glXSwapIntervalMESA) numFailed++;
+ return numFailed;
+}
+
+int (CODEGEN_FUNCPTR *sf_ptrc_glXSwapIntervalSGI)(int) = NULL;
+
+static int Load_SGI_swap_control(void)
+{
+ int numFailed = 0;
+ sf_ptrc_glXSwapIntervalSGI = (int (CODEGEN_FUNCPTR *)(int))IntGetProcAddress("glXSwapIntervalSGI");
+ if(!sf_ptrc_glXSwapIntervalSGI) numFailed++;
+ return numFailed;
+}
+
+GLXContext (CODEGEN_FUNCPTR *sf_ptrc_glXCreateContextAttribsARB)(Display *, GLXFBConfig, GLXContext, Bool, const int *) = NULL;
+
+static int Load_ARB_create_context(void)
+{
+ int numFailed = 0;
+ sf_ptrc_glXCreateContextAttribsARB = (GLXContext (CODEGEN_FUNCPTR *)(Display *, GLXFBConfig, GLXContext, Bool, const int *))IntGetProcAddress("glXCreateContextAttribsARB");
+ if(!sf_ptrc_glXCreateContextAttribsARB) numFailed++;
+ return numFailed;
+}
+
+typedef int (*PFN_LOADFUNCPOINTERS)(void);
+typedef struct sfglx_StrToExtMap_s
+{
+ const char *extensionName;
+ int *extensionVariable;
+ PFN_LOADFUNCPOINTERS LoadExtension;
+} sfglx_StrToExtMap;
+
+static sfglx_StrToExtMap ExtensionMap[6] = {
+ {"GLX_EXT_swap_control", &sfglx_ext_EXT_swap_control, Load_EXT_swap_control},
+ {"GLX_MESA_swap_control", &sfglx_ext_MESA_swap_control, Load_MESA_swap_control},
+ {"GLX_SGI_swap_control", &sfglx_ext_SGI_swap_control, Load_SGI_swap_control},
+ {"GLX_ARB_multisample", &sfglx_ext_ARB_multisample, NULL},
+ {"GLX_ARB_create_context", &sfglx_ext_ARB_create_context, Load_ARB_create_context},
+ {"GLX_ARB_create_context_profile", &sfglx_ext_ARB_create_context_profile, NULL},
+};
+
+static int g_extensionMapSize = 6;
+
+static sfglx_StrToExtMap *FindExtEntry(const char *extensionName)
+{
+ int loop;
+ sfglx_StrToExtMap *currLoc = ExtensionMap;
+ for(loop = 0; loop < g_extensionMapSize; ++loop, ++currLoc)
+ {
+ if(strcmp(extensionName, currLoc->extensionName) == 0)
+ return currLoc;
+ }
+
+ return NULL;
+}
+
+static void ClearExtensionVars(void)
+{
+ sfglx_ext_EXT_swap_control = sfglx_LOAD_FAILED;
+ sfglx_ext_MESA_swap_control = sfglx_LOAD_FAILED;
+ sfglx_ext_SGI_swap_control = sfglx_LOAD_FAILED;
+ sfglx_ext_ARB_multisample = sfglx_LOAD_FAILED;
+ sfglx_ext_ARB_create_context = sfglx_LOAD_FAILED;
+ sfglx_ext_ARB_create_context_profile = sfglx_LOAD_FAILED;
+}
+
+
+static void LoadExtByName(const char *extensionName)
+{
+ sfglx_StrToExtMap *entry = NULL;
+ entry = FindExtEntry(extensionName);
+ if(entry)
+ {
+ if(entry->LoadExtension)
+ {
+ int numFailed = entry->LoadExtension();
+ if(numFailed == 0)
+ {
+ *(entry->extensionVariable) = sfglx_LOAD_SUCCEEDED;
+ }
+ else
+ {
+ *(entry->extensionVariable) = sfglx_LOAD_SUCCEEDED + numFailed;
+ }
+ }
+ else
+ {
+ *(entry->extensionVariable) = sfglx_LOAD_SUCCEEDED;
+ }
+ }
+}
+
+
+static void ProcExtsFromExtString(const char *strExtList)
+{
+ size_t iExtListLen = strlen(strExtList);
+ const char *strExtListEnd = strExtList + iExtListLen;
+ const char *strCurrPos = strExtList;
+ char strWorkBuff[256];
+
+ while(*strCurrPos)
+ {
+ /*Get the extension at our position.*/
+ int iStrLen = 0;
+ const char *strEndStr = strchr(strCurrPos, ' ');
+ int iStop = 0;
+ if(strEndStr == NULL)
+ {
+ strEndStr = strExtListEnd;
+ iStop = 1;
+ }
+
+ iStrLen = (int)((ptrdiff_t)strEndStr - (ptrdiff_t)strCurrPos);
+
+ if(iStrLen > 255)
+ return;
+
+ strncpy(strWorkBuff, strCurrPos, iStrLen);
+ strWorkBuff[iStrLen] = '\0';
+
+ LoadExtByName(strWorkBuff);
+
+ strCurrPos = strEndStr + 1;
+ if(iStop) break;
+ }
+}
+
+int sfglx_LoadFunctions(Display *display, int screen)
+{
+ ClearExtensionVars();
+
+
+ ProcExtsFromExtString((const char *)glXQueryExtensionsString(display, screen));
+ return sfglx_LOAD_SUCCEEDED;
+}
+
diff --git a/src/SFML/Window/Unix/GlxExtensions.hpp b/src/SFML/Window/Unix/GlxExtensions.hpp
new file mode 100644
index 0000000..9e9a749
--- /dev/null
+++ b/src/SFML/Window/Unix/GlxExtensions.hpp
@@ -0,0 +1,203 @@
+////////////////////////////////////////////////////////////
+//
+// SFML - Simple and Fast Multimedia Library
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
+//
+// 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.
+//
+////////////////////////////////////////////////////////////
+
+#ifndef SF_POINTER_C_GENERATED_HEADER_GLXWIN_HPP
+#define SF_POINTER_C_GENERATED_HEADER_GLXWIN_HPP
+
+#ifdef __glxext_h_
+#error Attempt to include glx_exts after including glxext.h
+#endif
+
+#define __glxext_h_
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <GL/glx.h>
+#ifdef CODEGEN_FUNCPTR
+#undef CODEGEN_FUNCPTR
+#endif /*CODEGEN_FUNCPTR*/
+#define CODEGEN_FUNCPTR
+
+#ifndef GL_LOAD_GEN_BASIC_OPENGL_TYPEDEFS
+#define GL_LOAD_GEN_BASIC_OPENGL_TYPEDEFS
+
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef signed char GLbyte;
+typedef short GLshort;
+typedef int GLint;
+typedef int GLsizei;
+typedef unsigned char GLubyte;
+typedef unsigned short GLushort;
+typedef unsigned int GLuint;
+typedef float GLfloat;
+typedef float GLclampf;
+typedef double GLdouble;
+typedef double GLclampd;
+#define GLvoid void
+
+#endif /*GL_LOAD_GEN_BASIC_OPENGL_TYPEDEFS*/
+
+
+#ifndef GL_LOAD_GEN_BASIC_OPENGL_TYPEDEFS
+#define GL_LOAD_GEN_BASIC_OPENGL_TYPEDEFS
+
+
+#endif /*GL_LOAD_GEN_BASIC_OPENGL_TYPEDEFS*/
+
+
+#ifndef GLEXT_64_TYPES_DEFINED
+/* This code block is duplicated in glext.h, so must be protected */
+#define GLEXT_64_TYPES_DEFINED
+/* Define int32_t, int64_t, and uint64_t types for UST/MSC */
+/* (as used in the GLX_OML_sync_control extension). */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+#elif defined(__sun__) || defined(__digital__)
+#include <inttypes.h>
+#if defined(__STDC__)
+#if defined(__arch64__) || defined(_LP64)
+typedef long int int64_t;
+typedef unsigned long int uint64_t;
+#else
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#endif /* __arch64__ */
+#endif /* __STDC__ */
+#elif defined( __VMS ) || defined(__sgi)
+#include <inttypes.h>
+#elif defined(__SCO__) || defined(__USLC__)
+#include <stdint.h>
+#elif defined(__UNIXOS2__) || defined(__SOL64__)
+typedef long int int32_t;
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#elif defined(_WIN32) && defined(__GNUC__)
+#include <stdint.h>
+#elif defined(_WIN32)
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+/* Fallback if nothing above works */
+#include <inttypes.h>
+#endif
+#endif
+ typedef struct __GLXFBConfigRec *GLXFBConfig;
+ typedef XID GLXContextID;
+ typedef struct __GLXcontextRec *GLXContext;
+ typedef XID GLXPixmap;
+ typedef XID GLXDrawable;
+ typedef XID GLXPbuffer;
+ typedef void (APIENTRY *__GLXextFuncPtr)(void);
+ typedef XID GLXVideoCaptureDeviceNV;
+ typedef unsigned int GLXVideoDeviceNV;
+ typedef XID GLXVideoSourceSGIX;
+ typedef struct __GLXFBConfigRec *GLXFBConfigSGIX;
+ typedef XID GLXPbufferSGIX;
+ typedef struct {
+ char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */
+ int networkId;
+} GLXHyperpipeNetworkSGIX;
+ typedef struct {
+ char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */
+ int channel;
+ unsigned int participationType;
+ int timeSlice;
+} GLXHyperpipeConfigSGIX;
+ typedef struct {
+ char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */
+ int srcXOrigin, srcYOrigin, srcWidth, srcHeight;
+ int destXOrigin, destYOrigin, destWidth, destHeight;
+} GLXPipeRect;
+ typedef struct {
+ char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */
+ int XOrigin, YOrigin, maxHeight, maxWidth;
+} GLXPipeRectLimits;
+
+#ifdef __cplusplus
+extern "C" {
+#endif /*__cplusplus*/
+
+extern int sfglx_ext_EXT_swap_control;
+extern int sfglx_ext_MESA_swap_control;
+extern int sfglx_ext_SGI_swap_control;
+extern int sfglx_ext_ARB_multisample;
+extern int sfglx_ext_ARB_create_context;
+extern int sfglx_ext_ARB_create_context_profile;
+
+#define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2
+#define GLX_SWAP_INTERVAL_EXT 0x20F1
+
+#define GLX_SAMPLES_ARB 100001
+#define GLX_SAMPLE_BUFFERS_ARB 100000
+
+#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001
+#define GLX_CONTEXT_FLAGS_ARB 0x2094
+#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
+
+#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
+
+#ifndef GLX_EXT_swap_control
+#define GLX_EXT_swap_control 1
+extern void (CODEGEN_FUNCPTR *sf_ptrc_glXSwapIntervalEXT)(Display *, GLXDrawable, int);
+#define glXSwapIntervalEXT sf_ptrc_glXSwapIntervalEXT
+#endif /*GLX_EXT_swap_control*/
+
+// Declare entry point even if GLX header already provides glXSwapIntervalMESA
+// We won't make use of an alias here
+extern int (CODEGEN_FUNCPTR *sf_ptrc_glXSwapIntervalMESA)(int);
+
+#ifndef GLX_SGI_swap_control
+#define GLX_SGI_swap_control 1
+extern int (CODEGEN_FUNCPTR *sf_ptrc_glXSwapIntervalSGI)(int);
+#define glXSwapIntervalSGI sf_ptrc_glXSwapIntervalSGI
+#endif /*GLX_SGI_swap_control*/
+
+#ifndef GLX_ARB_create_context
+#define GLX_ARB_create_context 1
+extern GLXContext (CODEGEN_FUNCPTR *sf_ptrc_glXCreateContextAttribsARB)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
+#define glXCreateContextAttribsARB sf_ptrc_glXCreateContextAttribsARB
+#endif /*GLX_ARB_create_context*/
+
+
+enum sfglx_LoadStatus
+{
+ sfglx_LOAD_FAILED = 0,
+ sfglx_LOAD_SUCCEEDED = 1
+};
+
+int sfglx_LoadFunctions(Display *display, int screen);
+
+
+#ifdef __cplusplus
+}
+#endif /*__cplusplus*/
+
+#endif /* SF_POINTER_C_GENERATED_HEADER_GLXWIN_HPP */
diff --git a/src/SFML/Window/Unix/GlxExtensions.txt b/src/SFML/Window/Unix/GlxExtensions.txt
new file mode 100644
index 0000000..b6b06fa
--- /dev/null
+++ b/src/SFML/Window/Unix/GlxExtensions.txt
@@ -0,0 +1,11 @@
+// Created with:
+// https://bitbucket.org/Anteru/glloadgen-reloaded
+// Commit 20f19482b7a844d20b9785c3e3fd1f16419f6e0a
+// lua LoadGen.lua -style=pointer_c -spec=glX -indent=space -prefix=sf -extfile=GlxExtensions.txt GlxExtensions
+
+EXT_swap_control
+// MESA_swap_control
+SGI_swap_control
+GLX_ARB_multisample
+GLX_ARB_create_context
+GLX_ARB_create_context_profile \ No newline at end of file
diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp
index 8ff7697..4d815f8 100644
--- a/src/SFML/Window/Unix/InputImpl.cpp
+++ b/src/SFML/Window/Unix/InputImpl.cpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -25,11 +25,148 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
+#include <SFML/Window/Window.hpp> // important to be included first (conflict with None)
#include <SFML/Window/Unix/InputImpl.hpp>
-#include <SFML/Window/Window.hpp>
#include <SFML/Window/Unix/Display.hpp>
-#include <X11/Xlib.h>
+#include <SFML/Window/Unix/ScopedXcbPtr.hpp>
+#include <SFML/System/Err.hpp>
+#include <xcb/xcb.h>
#include <X11/keysym.h>
+#include <cstdlib>
+
+////////////////////////////////////////////////////////////
+// Private data
+////////////////////////////////////////////////////////////
+namespace
+{
+ bool mapBuilt = false;
+
+ // We use a simple array instead of a map => constant time lookup
+ xcb_keycode_t keycodeMap[sf::Keyboard::KeyCount];
+
+ xcb_keycode_t getKeycode(xcb_keysym_t keysym)
+ {
+ const xcb_keysym_t* keysymMap = sf::priv::getKeysymMap();
+
+ for (xcb_keycode_t i = 0; ; ++i)
+ {
+ if (keysymMap[i] == keysym)
+ return i;
+
+ if (i == 255)
+ break;
+ }
+
+ return 255;
+ }
+
+ void buildMap()
+ {
+ keycodeMap[sf::Keyboard::A] = getKeycode(XK_a);
+ keycodeMap[sf::Keyboard::B] = getKeycode(XK_b);
+ keycodeMap[sf::Keyboard::C] = getKeycode(XK_c);
+ keycodeMap[sf::Keyboard::D] = getKeycode(XK_d);
+ keycodeMap[sf::Keyboard::E] = getKeycode(XK_e);
+ keycodeMap[sf::Keyboard::F] = getKeycode(XK_f);
+ keycodeMap[sf::Keyboard::G] = getKeycode(XK_g);
+ keycodeMap[sf::Keyboard::H] = getKeycode(XK_h);
+ keycodeMap[sf::Keyboard::I] = getKeycode(XK_i);
+ keycodeMap[sf::Keyboard::J] = getKeycode(XK_j);
+ keycodeMap[sf::Keyboard::K] = getKeycode(XK_k);
+ keycodeMap[sf::Keyboard::L] = getKeycode(XK_l);
+ keycodeMap[sf::Keyboard::M] = getKeycode(XK_m);
+ keycodeMap[sf::Keyboard::N] = getKeycode(XK_n);
+ keycodeMap[sf::Keyboard::O] = getKeycode(XK_o);
+ keycodeMap[sf::Keyboard::P] = getKeycode(XK_p);
+ keycodeMap[sf::Keyboard::Q] = getKeycode(XK_q);
+ keycodeMap[sf::Keyboard::R] = getKeycode(XK_r);
+ keycodeMap[sf::Keyboard::S] = getKeycode(XK_s);
+ keycodeMap[sf::Keyboard::T] = getKeycode(XK_t);
+ keycodeMap[sf::Keyboard::U] = getKeycode(XK_u);
+ keycodeMap[sf::Keyboard::V] = getKeycode(XK_v);
+ keycodeMap[sf::Keyboard::W] = getKeycode(XK_w);
+ keycodeMap[sf::Keyboard::X] = getKeycode(XK_x);
+ keycodeMap[sf::Keyboard::Y] = getKeycode(XK_y);
+ keycodeMap[sf::Keyboard::Z] = getKeycode(XK_z);
+ keycodeMap[sf::Keyboard::Num0] = getKeycode(XK_0);
+ keycodeMap[sf::Keyboard::Num1] = getKeycode(XK_1);
+ keycodeMap[sf::Keyboard::Num2] = getKeycode(XK_2);
+ keycodeMap[sf::Keyboard::Num3] = getKeycode(XK_3);
+ keycodeMap[sf::Keyboard::Num4] = getKeycode(XK_4);
+ keycodeMap[sf::Keyboard::Num5] = getKeycode(XK_5);
+ keycodeMap[sf::Keyboard::Num6] = getKeycode(XK_6);
+ keycodeMap[sf::Keyboard::Num7] = getKeycode(XK_7);
+ keycodeMap[sf::Keyboard::Num8] = getKeycode(XK_8);
+ keycodeMap[sf::Keyboard::Num9] = getKeycode(XK_9);
+ keycodeMap[sf::Keyboard::Escape] = getKeycode(XK_Escape);
+ keycodeMap[sf::Keyboard::LControl] = getKeycode(XK_Control_L);
+ keycodeMap[sf::Keyboard::LShift] = getKeycode(XK_Shift_L);
+ keycodeMap[sf::Keyboard::LAlt] = getKeycode(XK_Alt_L);
+ keycodeMap[sf::Keyboard::LSystem] = getKeycode(XK_Super_L);
+ keycodeMap[sf::Keyboard::RControl] = getKeycode(XK_Control_R);
+ keycodeMap[sf::Keyboard::RShift] = getKeycode(XK_Shift_R);
+ keycodeMap[sf::Keyboard::RAlt] = getKeycode(XK_Alt_R);
+ keycodeMap[sf::Keyboard::RSystem] = getKeycode(XK_Super_R);
+ keycodeMap[sf::Keyboard::Menu] = getKeycode(XK_Menu);
+ keycodeMap[sf::Keyboard::LBracket] = getKeycode(XK_bracketleft);
+ keycodeMap[sf::Keyboard::RBracket] = getKeycode(XK_bracketright);
+ keycodeMap[sf::Keyboard::SemiColon] = getKeycode(XK_semicolon);
+ keycodeMap[sf::Keyboard::Comma] = getKeycode(XK_comma);
+ keycodeMap[sf::Keyboard::Period] = getKeycode(XK_period);
+ keycodeMap[sf::Keyboard::Quote] = getKeycode(XK_apostrophe);
+ keycodeMap[sf::Keyboard::Slash] = getKeycode(XK_slash);
+ keycodeMap[sf::Keyboard::BackSlash] = getKeycode(XK_backslash);
+ keycodeMap[sf::Keyboard::Tilde] = getKeycode(XK_grave);
+ keycodeMap[sf::Keyboard::Equal] = getKeycode(XK_equal);
+ keycodeMap[sf::Keyboard::Dash] = getKeycode(XK_minus);
+ keycodeMap[sf::Keyboard::Space] = getKeycode(XK_space);
+ keycodeMap[sf::Keyboard::Return] = getKeycode(XK_Return);
+ keycodeMap[sf::Keyboard::BackSpace] = getKeycode(XK_BackSpace);
+ keycodeMap[sf::Keyboard::Tab] = getKeycode(XK_Tab);
+ keycodeMap[sf::Keyboard::PageUp] = getKeycode(XK_Prior);
+ keycodeMap[sf::Keyboard::PageDown] = getKeycode(XK_Next);
+ keycodeMap[sf::Keyboard::End] = getKeycode(XK_End);
+ keycodeMap[sf::Keyboard::Home] = getKeycode(XK_Home);
+ keycodeMap[sf::Keyboard::Insert] = getKeycode(XK_Insert);
+ keycodeMap[sf::Keyboard::Delete] = getKeycode(XK_Delete);
+ keycodeMap[sf::Keyboard::Add] = getKeycode(XK_KP_Add);
+ keycodeMap[sf::Keyboard::Subtract] = getKeycode(XK_KP_Subtract);
+ keycodeMap[sf::Keyboard::Multiply] = getKeycode(XK_KP_Multiply);
+ keycodeMap[sf::Keyboard::Divide] = getKeycode(XK_KP_Divide);
+ keycodeMap[sf::Keyboard::Left] = getKeycode(XK_Left);
+ keycodeMap[sf::Keyboard::Right] = getKeycode(XK_Right);
+ keycodeMap[sf::Keyboard::Up] = getKeycode(XK_Up);
+ keycodeMap[sf::Keyboard::Down] = getKeycode(XK_Down);
+ keycodeMap[sf::Keyboard::Numpad0] = getKeycode(XK_KP_0);
+ keycodeMap[sf::Keyboard::Numpad1] = getKeycode(XK_KP_1);
+ keycodeMap[sf::Keyboard::Numpad2] = getKeycode(XK_KP_2);
+ keycodeMap[sf::Keyboard::Numpad3] = getKeycode(XK_KP_3);
+ keycodeMap[sf::Keyboard::Numpad4] = getKeycode(XK_KP_4);
+ keycodeMap[sf::Keyboard::Numpad5] = getKeycode(XK_KP_5);
+ keycodeMap[sf::Keyboard::Numpad6] = getKeycode(XK_KP_6);
+ keycodeMap[sf::Keyboard::Numpad7] = getKeycode(XK_KP_7);
+ keycodeMap[sf::Keyboard::Numpad8] = getKeycode(XK_KP_8);
+ keycodeMap[sf::Keyboard::Numpad9] = getKeycode(XK_KP_9);
+ keycodeMap[sf::Keyboard::F1] = getKeycode(XK_F1);
+ keycodeMap[sf::Keyboard::F2] = getKeycode(XK_F2);
+ keycodeMap[sf::Keyboard::F3] = getKeycode(XK_F3);
+ keycodeMap[sf::Keyboard::F4] = getKeycode(XK_F4);
+ keycodeMap[sf::Keyboard::F5] = getKeycode(XK_F5);
+ keycodeMap[sf::Keyboard::F6] = getKeycode(XK_F6);
+ keycodeMap[sf::Keyboard::F7] = getKeycode(XK_F7);
+ keycodeMap[sf::Keyboard::F8] = getKeycode(XK_F8);
+ keycodeMap[sf::Keyboard::F9] = getKeycode(XK_F9);
+ keycodeMap[sf::Keyboard::F10] = getKeycode(XK_F10);
+ keycodeMap[sf::Keyboard::F11] = getKeycode(XK_F11);
+ keycodeMap[sf::Keyboard::F12] = getKeycode(XK_F12);
+ keycodeMap[sf::Keyboard::F13] = getKeycode(XK_F13);
+ keycodeMap[sf::Keyboard::F14] = getKeycode(XK_F14);
+ keycodeMap[sf::Keyboard::F15] = getKeycode(XK_F15);
+ keycodeMap[sf::Keyboard::Pause] = getKeycode(XK_Pause);
+
+ mapBuilt = true;
+ }
+}
namespace sf
@@ -39,143 +176,47 @@ namespace priv
////////////////////////////////////////////////////////////
bool InputImpl::isKeyPressed(Keyboard::Key key)
{
- // Get the corresponding X11 keysym
- KeySym keysym = 0;
- switch (key)
- {
- case Keyboard::A: keysym = XK_A; break;
- case Keyboard::B: keysym = XK_B; break;
- case Keyboard::C: keysym = XK_C; break;
- case Keyboard::D: keysym = XK_D; break;
- case Keyboard::E: keysym = XK_E; break;
- case Keyboard::F: keysym = XK_F; break;
- case Keyboard::G: keysym = XK_G; break;
- case Keyboard::H: keysym = XK_H; break;
- case Keyboard::I: keysym = XK_I; break;
- case Keyboard::J: keysym = XK_J; break;
- case Keyboard::K: keysym = XK_K; break;
- case Keyboard::L: keysym = XK_L; break;
- case Keyboard::M: keysym = XK_M; break;
- case Keyboard::N: keysym = XK_N; break;
- case Keyboard::O: keysym = XK_O; break;
- case Keyboard::P: keysym = XK_P; break;
- case Keyboard::Q: keysym = XK_Q; break;
- case Keyboard::R: keysym = XK_R; break;
- case Keyboard::S: keysym = XK_S; break;
- case Keyboard::T: keysym = XK_T; break;
- case Keyboard::U: keysym = XK_U; break;
- case Keyboard::V: keysym = XK_V; break;
- case Keyboard::W: keysym = XK_W; break;
- case Keyboard::X: keysym = XK_X; break;
- case Keyboard::Y: keysym = XK_Y; break;
- case Keyboard::Z: keysym = XK_Z; break;
- case Keyboard::Num0: keysym = XK_0; break;
- case Keyboard::Num1: keysym = XK_1; break;
- case Keyboard::Num2: keysym = XK_2; break;
- case Keyboard::Num3: keysym = XK_3; break;
- case Keyboard::Num4: keysym = XK_4; break;
- case Keyboard::Num5: keysym = XK_5; break;
- case Keyboard::Num6: keysym = XK_6; break;
- case Keyboard::Num7: keysym = XK_7; break;
- case Keyboard::Num8: keysym = XK_8; break;
- case Keyboard::Num9: keysym = XK_9; break;
- case Keyboard::Escape: keysym = XK_Escape; break;
- case Keyboard::LControl: keysym = XK_Control_L; break;
- case Keyboard::LShift: keysym = XK_Shift_L; break;
- case Keyboard::LAlt: keysym = XK_Alt_L; break;
- case Keyboard::LSystem: keysym = XK_Super_L; break;
- case Keyboard::RControl: keysym = XK_Control_R; break;
- case Keyboard::RShift: keysym = XK_Shift_R; break;
- case Keyboard::RAlt: keysym = XK_Alt_R; break;
- case Keyboard::RSystem: keysym = XK_Super_R; break;
- case Keyboard::Menu: keysym = XK_Menu; break;
- case Keyboard::LBracket: keysym = XK_bracketleft; break;
- case Keyboard::RBracket: keysym = XK_bracketright; break;
- case Keyboard::SemiColon: keysym = XK_semicolon; break;
- case Keyboard::Comma: keysym = XK_comma; break;
- case Keyboard::Period: keysym = XK_period; break;
- case Keyboard::Quote: keysym = XK_dead_acute; break;
- case Keyboard::Slash: keysym = XK_slash; break;
- case Keyboard::BackSlash: keysym = XK_backslash; break;
- case Keyboard::Tilde: keysym = XK_dead_grave; break;
- case Keyboard::Equal: keysym = XK_equal; break;
- case Keyboard::Dash: keysym = XK_minus; break;
- case Keyboard::Space: keysym = XK_space; break;
- case Keyboard::Return: keysym = XK_Return; break;
- case Keyboard::BackSpace: keysym = XK_BackSpace; break;
- case Keyboard::Tab: keysym = XK_Tab; break;
- case Keyboard::PageUp: keysym = XK_Prior; break;
- case Keyboard::PageDown: keysym = XK_Next; break;
- case Keyboard::End: keysym = XK_End; break;
- case Keyboard::Home: keysym = XK_Home; break;
- case Keyboard::Insert: keysym = XK_Insert; break;
- case Keyboard::Delete: keysym = XK_Delete; break;
- case Keyboard::Add: keysym = XK_KP_Add; break;
- case Keyboard::Subtract: keysym = XK_KP_Subtract; break;
- case Keyboard::Multiply: keysym = XK_KP_Multiply; break;
- case Keyboard::Divide: keysym = XK_KP_Divide; break;
- case Keyboard::Left: keysym = XK_Left; break;
- case Keyboard::Right: keysym = XK_Right; break;
- case Keyboard::Up: keysym = XK_Up; break;
- case Keyboard::Down: keysym = XK_Down; break;
- case Keyboard::Numpad0: keysym = XK_KP_0; break;
- case Keyboard::Numpad1: keysym = XK_KP_1; break;
- case Keyboard::Numpad2: keysym = XK_KP_2; break;
- case Keyboard::Numpad3: keysym = XK_KP_3; break;
- case Keyboard::Numpad4: keysym = XK_KP_4; break;
- case Keyboard::Numpad5: keysym = XK_KP_5; break;
- case Keyboard::Numpad6: keysym = XK_KP_6; break;
- case Keyboard::Numpad7: keysym = XK_KP_7; break;
- case Keyboard::Numpad8: keysym = XK_KP_8; break;
- case Keyboard::Numpad9: keysym = XK_KP_9; break;
- case Keyboard::F1: keysym = XK_F1; break;
- case Keyboard::F2: keysym = XK_F2; break;
- case Keyboard::F3: keysym = XK_F3; break;
- case Keyboard::F4: keysym = XK_F4; break;
- case Keyboard::F5: keysym = XK_F5; break;
- case Keyboard::F6: keysym = XK_F6; break;
- case Keyboard::F7: keysym = XK_F7; break;
- case Keyboard::F8: keysym = XK_F8; break;
- case Keyboard::F9: keysym = XK_F9; break;
- case Keyboard::F10: keysym = XK_F10; break;
- case Keyboard::F11: keysym = XK_F11; break;
- case Keyboard::F12: keysym = XK_F12; break;
- case Keyboard::F13: keysym = XK_F13; break;
- case Keyboard::F14: keysym = XK_F14; break;
- case Keyboard::F15: keysym = XK_F15; break;
- case Keyboard::Pause: keysym = XK_Pause; break;
- default: keysym = 0; break;
- }
+ if (!mapBuilt)
+ buildMap();
- // Open a connection with the X server
- Display* display = OpenDisplay();
+ // Sanity checks
+ if (key < 0 || key >= sf::Keyboard::KeyCount)
+ return false;
// Convert to keycode
- KeyCode keycode = XKeysymToKeycode(display, keysym);
- if (keycode != 0)
- {
- // Get the whole keyboard state
- char keys[32];
- XQueryKeymap(display, keys);
+ xcb_keycode_t keycode = keycodeMap[key];
- // Close the connection with the X server
- CloseDisplay(display);
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
- // Check our keycode
- return (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
- }
- else
+ // Open a connection with the X server
+ xcb_connection_t* connection = OpenConnection();
+
+ // Get the whole keyboard state
+ ScopedXcbPtr<xcb_query_keymap_reply_t> keymap(
+ xcb_query_keymap_reply(
+ connection,
+ xcb_query_keymap(connection),
+ &error
+ )
+ );
+
+ // Close the connection with the X server
+ CloseConnection(connection);
+
+ if (error)
{
- // Close the connection with the X server
- CloseDisplay(display);
+ err() << "Failed to query keymap" << std::endl;
return false;
}
+
+ // Check our keycode
+ return (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0;
}
////////////////////////////////////////////////////////////
-void InputImpl::setVirtualKeyboardVisible(bool visible)
+void InputImpl::setVirtualKeyboardVisible(bool /*visible*/)
{
// Not applicable
}
@@ -185,30 +226,43 @@ void InputImpl::setVirtualKeyboardVisible(bool visible)
bool InputImpl::isMouseButtonPressed(Mouse::Button button)
{
// Open a connection with the X server
- Display* display = OpenDisplay();
+ xcb_connection_t* connection = OpenConnection();
+
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ // Get pointer mask
+ ScopedXcbPtr<xcb_query_pointer_reply_t> pointer(
+ xcb_query_pointer_reply(
+ connection,
+ xcb_query_pointer(
+ connection,
+ XCBDefaultRootWindow(connection)
+ ),
+ &error
+ )
+ );
- // we don't care about these but they are required
- ::Window root, child;
- int wx, wy;
- int gx, gy;
+ // Close the connection with the X server
+ CloseConnection(connection);
- unsigned int buttons = 0;
- XQueryPointer(display, DefaultRootWindow(display), &root, &child, &gx, &gy, &wx, &wy, &buttons);
+ if (error)
+ {
+ err() << "Failed to query pointer" << std::endl;
- // Close the connection with the X server
- CloseDisplay(display);
+ return false;
+ }
+
+ uint16_t buttons = pointer->mask;
switch (button)
{
- case Mouse::Left: return buttons & Button1Mask;
- case Mouse::Right: return buttons & Button3Mask;
- case Mouse::Middle: return buttons & Button2Mask;
+ case Mouse::Left: return buttons & XCB_BUTTON_MASK_1;
+ case Mouse::Right: return buttons & XCB_BUTTON_MASK_3;
+ case Mouse::Middle: return buttons & XCB_BUTTON_MASK_2;
case Mouse::XButton1: return false; // not supported by X
case Mouse::XButton2: return false; // not supported by X
default: return false;
}
-
- return false;
}
@@ -216,21 +270,32 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button)
Vector2i InputImpl::getMousePosition()
{
// Open a connection with the X server
- Display* display = OpenDisplay();
+ xcb_connection_t* connection = OpenConnection();
- // we don't care about these but they are required
- ::Window root, child;
- int x, y;
- unsigned int buttons;
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
- int gx = 0;
- int gy = 0;
- XQueryPointer(display, DefaultRootWindow(display), &root, &child, &gx, &gy, &x, &y, &buttons);
+ ScopedXcbPtr<xcb_query_pointer_reply_t> pointer(
+ xcb_query_pointer_reply(
+ connection,
+ xcb_query_pointer(
+ connection,
+ XCBDefaultRootWindow(connection)
+ ),
+ &error
+ )
+ );
// Close the connection with the X server
- CloseDisplay(display);
+ CloseConnection(connection);
- return Vector2i(gx, gy);
+ if (error)
+ {
+ err() << "Failed to query pointer" << std::endl;
+
+ return Vector2i(0, 0);
+ }
+
+ return Vector2i(pointer->root_x, pointer->root_y);
}
@@ -241,21 +306,32 @@ Vector2i InputImpl::getMousePosition(const Window& relativeTo)
if (handle)
{
// Open a connection with the X server
- Display* display = OpenDisplay();
+ xcb_connection_t* connection = OpenConnection();
- // we don't care about these but they are required
- ::Window root, child;
- int gx, gy;
- unsigned int buttons;
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
- int x = 0;
- int y = 0;
- XQueryPointer(display, handle, &root, &child, &gx, &gy, &x, &y, &buttons);
+ ScopedXcbPtr<xcb_query_pointer_reply_t> pointer(
+ xcb_query_pointer_reply(
+ connection,
+ xcb_query_pointer(
+ connection,
+ handle
+ ),
+ &error
+ )
+ );
// Close the connection with the X server
- CloseDisplay(display);
+ CloseConnection(connection);
+
+ if (error)
+ {
+ err() << "Failed to query pointer" << std::endl;
+
+ return Vector2i(0, 0);
+ }
- return Vector2i(x, y);
+ return Vector2i(pointer->win_x, pointer->win_y);
}
else
{
@@ -268,13 +344,27 @@ Vector2i InputImpl::getMousePosition(const Window& relativeTo)
void InputImpl::setMousePosition(const Vector2i& position)
{
// Open a connection with the X server
- Display* display = OpenDisplay();
+ xcb_connection_t* connection = OpenConnection();
+
+ ScopedXcbPtr<xcb_generic_error_t> error(xcb_request_check(
+ connection,
+ xcb_warp_pointer(
+ connection,
+ None, // Source window
+ XCBDefaultRootWindow(connection), // Destination window
+ 0, 0, // Source position
+ 0, 0, // Source size
+ position.x, position.y // Destination position
+ )
+ ));
+
+ if (error)
+ err() << "Failed to set mouse position" << std::endl;
- XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, position.x, position.y);
- XFlush(display);
+ xcb_flush(connection);
// Close the connection with the X server
- CloseDisplay(display);
+ CloseConnection(connection);
}
@@ -282,17 +372,31 @@ void InputImpl::setMousePosition(const Vector2i& position)
void InputImpl::setMousePosition(const Vector2i& position, const Window& relativeTo)
{
// Open a connection with the X server
- Display* display = OpenDisplay();
+ xcb_connection_t* connection = OpenConnection();
WindowHandle handle = relativeTo.getSystemHandle();
if (handle)
{
- XWarpPointer(display, None, handle, 0, 0, 0, 0, position.x, position.y);
- XFlush(display);
+ ScopedXcbPtr<xcb_generic_error_t> error(xcb_request_check(
+ connection,
+ xcb_warp_pointer(
+ connection,
+ None, // Source window
+ handle, // Destination window
+ 0, 0, // Source position
+ 0, 0, // Source size
+ position.x, position.y // Destination position
+ )
+ ));
+
+ if (error)
+ err() << "Failed to set mouse position" << std::endl;
+
+ xcb_flush(connection);
}
// Close the connection with the X server
- CloseDisplay(display);
+ CloseConnection(connection);
}
diff --git a/src/SFML/Window/Unix/InputImpl.hpp b/src/SFML/Window/Unix/InputImpl.hpp
index 32c8f21..9425c3d 100644
--- a/src/SFML/Window/Unix/InputImpl.hpp
+++ b/src/SFML/Window/Unix/InputImpl.hpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
diff --git a/src/SFML/Window/Unix/JoystickImpl.cpp b/src/SFML/Window/Unix/JoystickImpl.cpp
index 0b8d479..d60075d 100644
--- a/src/SFML/Window/Unix/JoystickImpl.cpp
+++ b/src/SFML/Window/Unix/JoystickImpl.cpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -27,159 +27,410 @@
////////////////////////////////////////////////////////////
#include <SFML/Window/JoystickImpl.hpp>
#include <SFML/System/Err.hpp>
-#include <sys/inotify.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <errno.h>
+#include <linux/joystick.h>
#include <libudev.h>
#include <unistd.h>
-#include <cstdio>
-#include <cstdlib>
-#include <sstream>
+#include <fcntl.h>
+#include <errno.h>
+#include <vector>
+#include <string>
+#include <cstring>
namespace
{
- int notifyFd = -1;
- int inputFd = -1;
- bool plugged[sf::Joystick::Count];
+ udev* udevContext = 0;
+ udev_monitor* udevMonitor = 0;
+
+ struct JoystickRecord
+ {
+ std::string deviceNode;
+ std::string systemPath;
+ bool plugged;
+ };
+
+ typedef std::vector<JoystickRecord> JoystickList;
+ JoystickList joystickList;
- void updatePluggedList()
+ bool isJoystick(udev_device* udevDevice)
{
- udev* udevContext = udev_new();
+ // If anything goes wrong, we go safe and return true
+
+ // No device to check, assume not a joystick
+ if (!udevDevice)
+ return false;
+
+ const char* devnode = udev_device_get_devnode(udevDevice);
+
+ // We only consider devices with a device node
+ if (!devnode)
+ return false;
+
+ // SFML doesn't support evdev yet, so make sure we only handle /js nodes
+ if (!std::strstr(devnode, "/js"))
+ return false;
- for (unsigned int i = 0; i < sf::Joystick::Count; ++i)
+ // Check if this device is a joystick
+ if (udev_device_get_property_value(udevDevice, "ID_INPUT_JOYSTICK"))
+ return true;
+
+ // Check if this device is something that isn't a joystick
+ // We do this because the absence of any ID_INPUT_ property doesn't
+ // necessarily mean that the device isn't a joystick, whereas the
+ // presence of any ID_INPUT_ property that isn't ID_INPUT_JOYSTICK does
+ if (udev_device_get_property_value(udevDevice, "ID_INPUT_ACCELEROMETER") ||
+ udev_device_get_property_value(udevDevice, "ID_INPUT_KEY") ||
+ udev_device_get_property_value(udevDevice, "ID_INPUT_KEYBOARD") ||
+ udev_device_get_property_value(udevDevice, "ID_INPUT_MOUSE") ||
+ udev_device_get_property_value(udevDevice, "ID_INPUT_TABLET") ||
+ udev_device_get_property_value(udevDevice, "ID_INPUT_TOUCHPAD") ||
+ udev_device_get_property_value(udevDevice, "ID_INPUT_TOUCHSCREEN"))
+ return false;
+
+ // On some platforms (older udev), ID_INPUT_ properties are not present, instead
+ // the system makes use of the ID_CLASS property to identify the device class
+ const char* idClass = udev_device_get_property_value(udevDevice, "ID_CLASS");
+
+ if (idClass)
{
- std::ostringstream name;
- name << "js" << i;
- std::string nameString = name.str();
+ // Check if the device class matches joystick
+ if (std::strstr(idClass, "joystick"))
+ return true;
+
+ // Check if the device class matches something that isn't a joystick
+ // Rationale same as above
+ if (std::strstr(idClass, "accelerometer") ||
+ std::strstr(idClass, "key") ||
+ std::strstr(idClass, "keyboard") ||
+ std::strstr(idClass, "mouse") ||
+ std::strstr(idClass, "tablet") ||
+ std::strstr(idClass, "touchpad") ||
+ std::strstr(idClass, "touchscreen"))
+ return false;
+ }
+
+ // At this point, assume it is a joystick
+ return true;
+ }
- int file = ::open(("/dev/input/" + nameString).c_str(), O_RDONLY);
+ void updatePluggedList(udev_device* udevDevice = NULL)
+ {
+ if (udevDevice)
+ {
+ const char* action = udev_device_get_action(udevDevice);
- if (file < 0)
+ if (action)
{
- plugged[i] = false;
- continue;
+ if (isJoystick(udevDevice))
+ {
+ // Since isJoystick returned true, this has to succeed
+ const char* devnode = udev_device_get_devnode(udevDevice);
+
+ JoystickList::iterator record;
+
+ for (record = joystickList.begin(); record != joystickList.end(); ++record)
+ {
+ if (record->deviceNode == devnode)
+ {
+ if (std::strstr(action, "add"))
+ {
+ // The system path might have changed so update it
+ const char* syspath = udev_device_get_syspath(udevDevice);
+
+ record->plugged = true;
+ record->systemPath = syspath ? syspath : "";
+ break;
+ }
+ else if (std::strstr(action, "remove"))
+ {
+ record->plugged = false;
+ break;
+ }
+ }
+ }
+
+ if (record == joystickList.end())
+ {
+ if (std::strstr(action, "add"))
+ {
+ // If not mapped before and it got added, map it now
+ const char* syspath = udev_device_get_syspath(udevDevice);
+
+ JoystickRecord record;
+ record.deviceNode = devnode;
+ record.systemPath = syspath ? syspath : "";
+ record.plugged = true;
+
+ joystickList.push_back(record);
+ }
+ else if (std::strstr(action, "remove"))
+ {
+ // Not mapped during the initial scan, and removed (shouldn't happen)
+ sf::err() << "Trying to disconnect joystick that wasn't connected" << std::endl;
+ }
+ }
+ }
+
+ return;
}
- ::close(file);
+ // Do a full rescan if there was no action just to be sure
+ }
- // Check if the device is really a joystick or an
- // accelerometer by inspecting whether
- // ID_INPUT_ACCELEROMETER is present
- if (!udevContext)
- {
- // Go safe and assume it is if udev isn't available
- plugged[i] = true;
- continue;
- }
+ // Reset the plugged status of each mapping since we are doing a full rescan
+ for (JoystickList::iterator record = joystickList.begin(); record != joystickList.end(); ++record)
+ record->plugged = false;
- udev_device* udevDevice = udev_device_new_from_subsystem_sysname(udevContext, "input", nameString.c_str());
+ udev_enumerate* udevEnumerator = udev_enumerate_new(udevContext);
- if (!udevDevice)
- {
- // Go safe and assume it is if we can't get the device
- plugged[i] = true;
- continue;
- }
+ if (!udevEnumerator)
+ {
+ sf::err() << "Error while creating udev enumerator" << std::endl;
+ return;
+ }
- if (udev_device_get_property_value(udevDevice, "ID_INPUT_ACCELEROMETER"))
- {
- // This device is an accelerometer
- plugged[i] = false;
- }
- else
+ int result = 0;
+
+ result = udev_enumerate_add_match_subsystem(udevEnumerator, "input");
+
+ if (result < 0)
+ {
+ sf::err() << "Error while adding udev enumerator match" << std::endl;
+ return;
+ }
+
+ result = udev_enumerate_scan_devices(udevEnumerator);
+
+ if (result < 0)
+ {
+ sf::err() << "Error while enumerating udev devices" << std::endl;
+ return;
+ }
+
+ udev_list_entry* devices = udev_enumerate_get_list_entry(udevEnumerator);
+ udev_list_entry* device;
+
+ udev_list_entry_foreach(device, devices) {
+ const char* syspath = udev_list_entry_get_name(device);
+ udev_device* udevDevice = udev_device_new_from_syspath(udevContext, syspath);
+
+ if (udevDevice && isJoystick(udevDevice))
{
- // This device is not an accelerometer
- // Assume it's a joystick
- plugged[i] = true;
+ // Since isJoystick returned true, this has to succeed
+ const char* devnode = udev_device_get_devnode(udevDevice);
+
+ JoystickList::iterator record;
+
+ // Check if the device node has been mapped before
+ for (record = joystickList.begin(); record != joystickList.end(); ++record)
+ {
+ if (record->deviceNode == devnode)
+ {
+ record->plugged = true;
+ break;
+ }
+ }
+
+ // If not mapped before, map it now
+ if (record == joystickList.end())
+ {
+ JoystickRecord record;
+ record.deviceNode = devnode;
+ record.systemPath = syspath;
+ record.plugged = true;
+
+ joystickList.push_back(record);
+ }
}
udev_device_unref(udevDevice);
}
- if (udevContext)
- udev_unref(udevContext);
+ udev_enumerate_unref(udevEnumerator);
}
- bool hasInotifyEvent()
+ bool hasMonitorEvent()
{
+ // This will not fail since we make sure udevMonitor is valid
+ int monitorFd = udev_monitor_get_fd(udevMonitor);
+
fd_set descriptorSet;
FD_ZERO(&descriptorSet);
- FD_SET(notifyFd, &descriptorSet);
+ FD_SET(monitorFd, &descriptorSet);
timeval timeout = {0, 0};
- return (select(notifyFd + 1, &descriptorSet, NULL, NULL, &timeout) > 0) &&
- FD_ISSET(notifyFd, &descriptorSet);
+ return (select(monitorFd + 1, &descriptorSet, NULL, NULL, &timeout) > 0) &&
+ FD_ISSET(monitorFd, &descriptorSet);
}
- // Get the joystick name
- std::string getJoystickName(int file, unsigned int index)
+ // Get a property value from a udev device
+ const char* getUdevAttribute(udev_device* udevDevice, const std::string& attributeName)
{
- // Get the name
- char name[128];
+ return udev_device_get_property_value(udevDevice, attributeName.c_str());
+ }
- if (ioctl(file, JSIOCGNAME(sizeof(name)), name) >= 0)
- return std::string(name);
+ // Get a system attribute from a USB device
+ const char* getUsbAttribute(udev_device* udevDevice, const std::string& attributeName)
+ {
+ udev_device* udevDeviceParent = udev_device_get_parent_with_subsystem_devtype(udevDevice, "usb", "usb_device");
- sf::err() << "Unable to get name for joystick at index " << index << std::endl;
+ if (!udevDeviceParent)
+ return NULL;
- return std::string("Unknown Joystick");
+ return udev_device_get_sysattr_value(udevDeviceParent, attributeName.c_str());
}
- // Get a system attribute from a udev device as an unsigned int
- unsigned int getUdevAttributeUint(udev_device* device, unsigned int index, const std::string& attributeName)
+ // Get a USB attribute for a joystick as an unsigned int
+ unsigned int getUsbAttributeUint(udev_device* udevDevice, const std::string& attributeName)
{
- udev_device* udevDeviceParent = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_device");
+ if (!udevDevice)
+ return 0;
- if (!udevDeviceParent)
+ const char* attribute = getUsbAttribute(udevDevice, attributeName);
+ unsigned int value = 0;
+
+ if (attribute)
+ value = static_cast<unsigned int>(std::strtoul(attribute, NULL, 16));
+
+ return value;
+ }
+
+ // Get a udev property value for a joystick as an unsigned int
+ unsigned int getUdevAttributeUint(udev_device* udevDevice, const std::string& attributeName)
+ {
+ if (!udevDevice)
+ return 0;
+
+ const char* attribute = getUdevAttribute(udevDevice, attributeName);
+ unsigned int value = 0;
+
+ if (attribute)
+ value = static_cast<unsigned int>(std::strtoul(attribute, NULL, 16));
+
+ return value;
+ }
+
+ // Get the joystick vendor id
+ unsigned int getJoystickVendorId(unsigned int index)
+ {
+ if (!udevContext)
{
- sf::err() << "Unable to get joystick attribute. "
- << "Could not find parent USB device for joystick at index " << index << "." << std::endl;
+ sf::err() << "Failed to get vendor ID of joystick " << joystickList[index].deviceNode << std::endl;
return 0;
}
- const char* attributeString = udev_device_get_sysattr_value(udevDeviceParent, attributeName.c_str());
+ udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
- if (!attributeString)
+ if (!udevDevice)
{
- sf::err() << "Unable to get joystick attribute '" << attributeName << "'. "
- << "Attribute does not exist for joystick at index " << index << "." << std::endl;
+ sf::err() << "Failed to get vendor ID of joystick " << joystickList[index].deviceNode << std::endl;
return 0;
}
- return static_cast<unsigned int>(std::strtoul(attributeString, NULL, 16));
+ unsigned int id = 0;
+
+ // First try using udev
+ id = getUdevAttributeUint(udevDevice, "ID_VENDOR_ID");
+
+ if (id)
+ {
+ udev_device_unref(udevDevice);
+ return id;
+ }
+
+ // Fall back to using USB attribute
+ id = getUsbAttributeUint(udevDevice, "idVendor");
+
+ udev_device_unref(udevDevice);
+
+ if (id)
+ return id;
+
+ sf::err() << "Failed to get vendor ID of joystick " << joystickList[index].deviceNode << std::endl;
+
+ return 0;
}
- // Get a system attribute for a joystick at index as an unsigned int
- unsigned int getAttributeUint(unsigned int index, const std::string& attributeName)
+ // Get the joystick product id
+ unsigned int getJoystickProductId(unsigned int index)
{
- udev* udevContext = udev_new();
-
if (!udevContext)
{
- sf::err() << "Unable to get joystick attribute. "
- << "Could not create udev context." << std::endl;
+ sf::err() << "Failed to get product ID of joystick " << joystickList[index].deviceNode << std::endl;
return 0;
}
- std::ostringstream sysname("js");
- sysname << index;
-
- udev_device* udevDevice = udev_device_new_from_subsystem_sysname(udevContext, "input", sysname.str().c_str());
+ udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
if (!udevDevice)
{
- sf::err() << "Unable to get joystick attribute. "
- << "Could not find USB device for joystick at index " << index << "." << std::endl;
- udev_unref(udevContext);
+ sf::err() << "Failed to get product ID of joystick " << joystickList[index].deviceNode << std::endl;
return 0;
}
- unsigned int attribute = getUdevAttributeUint(udevDevice, index, attributeName);
+ unsigned int id = 0;
+
+ // First try using udev
+ id = getUdevAttributeUint(udevDevice, "ID_MODEL_ID");
+
+ if (id)
+ {
+ udev_device_unref(udevDevice);
+ return id;
+ }
+
+ // Fall back to using USB attribute
+ id = getUsbAttributeUint(udevDevice, "idProduct");
udev_device_unref(udevDevice);
- udev_unref(udevContext);
- return attribute;
+
+ if (id)
+ return id;
+
+ sf::err() << "Failed to get product ID of joystick " << joystickList[index].deviceNode << std::endl;
+
+ return 0;
+ }
+
+ // Get the joystick name
+ std::string getJoystickName(unsigned int index)
+ {
+ std::string devnode = joystickList[index].deviceNode;
+
+ // First try using ioctl with JSIOCGNAME
+ int fd = ::open(devnode.c_str(), O_RDONLY | O_NONBLOCK);
+
+ if (fd >= 0)
+ {
+ // Get the name
+ char name[128];
+ std::memset(name, 0, sizeof(name));
+
+ int result = ioctl(fd, JSIOCGNAME(sizeof(name)), name);
+
+ ::close(fd);
+
+ if (result >= 0)
+ return std::string(name);
+ }
+
+ // Fall back to manual USB chain walk via udev
+ if (udevContext)
+ {
+ udev_device* udevDevice = udev_device_new_from_syspath(udevContext, joystickList[index].systemPath.c_str());
+
+ if (udevDevice)
+ {
+ const char* product = getUsbAttribute(udevDevice, "product");
+ udev_device_unref(udevDevice);
+
+ if (product)
+ return std::string(product);
+ }
+ }
+
+ sf::err() << "Unable to get name for joystick " << devnode << std::endl;
+
+ return std::string("Unknown Joystick");
}
}
@@ -191,95 +442,133 @@ namespace priv
////////////////////////////////////////////////////////////
void JoystickImpl::initialize()
{
- // Reset the array of plugged joysticks
- std::fill(plugged, plugged + Joystick::Count, false);
+ udevContext = udev_new();
- // Do an initial scan
- updatePluggedList();
-
- // Create the inotify instance
- notifyFd = inotify_init();
- if (notifyFd < 0)
+ if (!udevContext)
{
- err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl;
+ sf::err() << "Failed to create udev context, joystick support not available" << std::endl;
return;
}
- // Watch nodes created and deleted in the /dev/input directory
- inputFd = inotify_add_watch(notifyFd, "/dev/input", IN_CREATE | IN_DELETE);
- if (inputFd < 0)
+ udevMonitor = udev_monitor_new_from_netlink(udevContext, "udev");
+
+ if (!udevMonitor)
{
- err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl;
+ err() << "Failed to create udev monitor, joystick connections and disconnections won't be notified" << std::endl;
+ }
+ else
+ {
+ int error = udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, "input", NULL);
+
+ if (error < 0)
+ {
+ err() << "Failed to add udev monitor filter, joystick connections and disconnections won't be notified: " << error << std::endl;
+
+ udev_monitor_unref(udevMonitor);
+ udevMonitor = 0;
+ }
+ else
+ {
+ error = udev_monitor_enable_receiving(udevMonitor);
- // No need to hang on to the inotify handle in this case
- ::close(notifyFd);
- notifyFd = -1;
+ if (error < 0)
+ {
+ err() << "Failed to enable udev monitor, joystick connections and disconnections won't be notified: " << error << std::endl;
+
+ udev_monitor_unref(udevMonitor);
+ udevMonitor = 0;
+ }
+ }
}
+
+ // Do an initial scan
+ updatePluggedList();
}
////////////////////////////////////////////////////////////
void JoystickImpl::cleanup()
{
- // Stop watching the /dev/input directory
- if (inputFd >= 0)
- inotify_rm_watch(notifyFd, inputFd);
+ // Unreference the udev monitor to destroy it
+ if (udevMonitor)
+ {
+ udev_monitor_unref(udevMonitor);
+ udevMonitor = 0;
+ }
- // Close the inotify file descriptor
- if (notifyFd >= 0)
- ::close(notifyFd);
+ // Unreference the udev context to destroy it
+ if (udevContext)
+ {
+ udev_unref(udevContext);
+ udevContext = 0;
+ }
}
////////////////////////////////////////////////////////////
bool JoystickImpl::isConnected(unsigned int index)
{
- // See if we can skip scanning if inotify is available
- if (notifyFd < 0)
+ // See if we can skip scanning if udev monitor is available
+ if (!udevMonitor)
{
- // inotify is not available, perform a scan every query
+ // udev monitor is not available, perform a scan every query
updatePluggedList();
}
- else if (hasInotifyEvent())
+ else if (hasMonitorEvent())
{
// Check if new joysticks were added/removed since last update
- // Don't bother decomposing and interpreting the filename, just do a full scan
- updatePluggedList();
+ udev_device* udevDevice = udev_monitor_receive_device(udevMonitor);
- // Flush all the pending events
- if (lseek(notifyFd, 0, SEEK_END) < 0)
- err() << "Failed to flush inotify of all pending joystick events." << std::endl;
+ // If we can get the specific device, we check that,
+ // otherwise just do a full scan if udevDevice == NULL
+ updatePluggedList(udevDevice);
+
+ if (udevDevice)
+ udev_device_unref(udevDevice);
}
+ if (index >= joystickList.size())
+ return false;
+
// Then check if the joystick is connected
- return plugged[index];
+ return joystickList[index].plugged;
}
////////////////////////////////////////////////////////////
bool JoystickImpl::open(unsigned int index)
{
- if (plugged[index])
+ if (index >= joystickList.size())
+ return false;
+
+ if (joystickList[index].plugged)
{
- std::ostringstream name;
- name << "/dev/input/js" << index;
+ std::string devnode = joystickList[index].deviceNode;
// Open the joystick's file descriptor (read-only and non-blocking)
- m_file = ::open(name.str().c_str(), O_RDONLY | O_NONBLOCK);
+ m_file = ::open(devnode.c_str(), O_RDONLY | O_NONBLOCK);
if (m_file >= 0)
{
// Retrieve the axes mapping
ioctl(m_file, JSIOCGAXMAP, m_mapping);
// Get info
- m_identification.name = getJoystickName(m_file, index);
- m_identification.vendorId = getAttributeUint(index, "idVendor");
- m_identification.productId = getAttributeUint(index, "idProduct");
+ m_identification.name = getJoystickName(index);
+
+ if (udevContext)
+ {
+ m_identification.vendorId = getJoystickVendorId(index);
+ m_identification.productId = getJoystickProductId(index);
+ }
// Reset the joystick state
m_state = JoystickState();
return true;
}
+ else
+ {
+ err() << "Failed to open joystick " << devnode << ": " << errno << std::endl;
+ }
}
return false;
@@ -290,6 +579,7 @@ bool JoystickImpl::open(unsigned int index)
void JoystickImpl::close()
{
::close(m_file);
+ m_file = -1;
}
@@ -298,6 +588,9 @@ JoystickCaps JoystickImpl::getCapabilities() const
{
JoystickCaps caps;
+ if (m_file < 0)
+ return caps;
+
// Get the number of buttons
char buttonCount;
ioctl(m_file, JSIOCGBUTTONS, &buttonCount);
@@ -340,9 +633,16 @@ Joystick::Identification JoystickImpl::getIdentification() const
////////////////////////////////////////////////////////////
JoystickState JoystickImpl::JoystickImpl::update()
{
+ if (m_file < 0)
+ {
+ m_state = JoystickState();
+ return m_state;
+ }
+
// pop events from the joystick file
js_event joyState;
- while (read(m_file, &joyState, sizeof(joyState)) > 0)
+ int result = read(m_file, &joyState, sizeof(joyState));
+ while (result > 0)
{
switch (joyState.type & ~JS_EVENT_INIT)
{
@@ -375,10 +675,18 @@ JoystickState JoystickImpl::JoystickImpl::update()
break;
}
}
+
+ result = read(m_file, &joyState, sizeof(joyState));
}
- // Check the connection state of the joystick (read() fails with an error != EGAIN if it's no longer connected)
- m_state.connected = (errno == EAGAIN);
+ // Check the connection state of the joystick
+ // read() returns -1 and errno != EGAIN if it's no longer connected
+ // We need to check the result of read() as well, since errno could
+ // have been previously set by some other function call that failed
+ // result can be either negative or 0 at this point
+ // If result is 0, assume the joystick is still connected
+ // If result is negative, check errno and disconnect if it is not EAGAIN
+ m_state.connected = (!result || (errno == EAGAIN));
return m_state;
}
diff --git a/src/SFML/Window/Unix/JoystickImpl.hpp b/src/SFML/Window/Unix/JoystickImpl.hpp
index 50777ef..34fce2e 100644
--- a/src/SFML/Window/Unix/JoystickImpl.hpp
+++ b/src/SFML/Window/Unix/JoystickImpl.hpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -28,9 +28,8 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
-#include <linux/joystick.h>
-#include <fcntl.h>
-#include <string>
+#include <SFML/Window/JoystickImpl.hpp>
+#include <linux/input.h>
namespace sf
diff --git a/src/SFML/Window/Unix/ScopedXcbPtr.hpp b/src/SFML/Window/Unix/ScopedXcbPtr.hpp
new file mode 100644
index 0000000..6c98065
--- /dev/null
+++ b/src/SFML/Window/Unix/ScopedXcbPtr.hpp
@@ -0,0 +1,102 @@
+////////////////////////////////////////////////////////////
+//
+// SFML - Simple and Fast Multimedia Library
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
+//
+// 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.
+//
+////////////////////////////////////////////////////////////
+
+#ifndef SFML_SCOPEDXCBPTR_HPP
+#define SFML_SCOPEDXCBPTR_HPP
+
+////////////////////////////////////////////////////////////
+// Headers
+////////////////////////////////////////////////////////////
+#include <cstdlib>
+
+
+namespace sf
+{
+namespace priv
+{
+////////////////////////////////////////////////////////////
+/// \brief Scoped pointer that frees memory returned in XCB replies
+///
+////////////////////////////////////////////////////////////
+template<typename T>
+class ScopedXcbPtr
+{
+public:
+ ////////////////////////////////////////////////////////////
+ /// \brief Constructor
+ ///
+ /// \param pointer Pointer value to store
+ ///
+ ////////////////////////////////////////////////////////////
+ ScopedXcbPtr(T* pointer);
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Destructor, calls std::free() on the stored pointer
+ ///
+ ////////////////////////////////////////////////////////////
+ ~ScopedXcbPtr();
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Structure dereference operator
+ ///
+ /// \return Stored pointer
+ ///
+ ////////////////////////////////////////////////////////////
+ T* operator ->() const;
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Address operator.
+ ///
+ /// \return Address of the stored pointer
+ ///
+ ////////////////////////////////////////////////////////////
+ T** operator &();
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Check if stored pointer is valid
+ ///
+ /// \return true if stored pointer is valid
+ ///
+ ////////////////////////////////////////////////////////////
+ operator bool() const;
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Retrieve the stored pointer.
+ ///
+ /// \return The stored pointer
+ ///
+ ////////////////////////////////////////////////////////////
+ T* get() const;
+
+private:
+ T* m_pointer; ///< Stored pointer
+};
+
+#include <SFML/Window/Unix/ScopedXcbPtr.inl>
+
+} // namespace priv
+
+} // namespace sf
+
+#endif // SFML_SCOPEDXCBPTR_HPP
diff --git a/src/SFML/Window/Unix/ScopedXcbPtr.inl b/src/SFML/Window/Unix/ScopedXcbPtr.inl
new file mode 100644
index 0000000..6683a1e
--- /dev/null
+++ b/src/SFML/Window/Unix/ScopedXcbPtr.inl
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////
+//
+// SFML - Simple and Fast Multimedia Library
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
+//
+// 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.
+//
+////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////
+template <typename T>
+inline ScopedXcbPtr<T>::ScopedXcbPtr(T* pointer) :
+m_pointer(pointer)
+{
+
+}
+
+
+////////////////////////////////////////////////////////////
+template <typename T>
+inline ScopedXcbPtr<T>::~ScopedXcbPtr()
+{
+ std::free(m_pointer);
+}
+
+
+////////////////////////////////////////////////////////////
+template <typename T>
+inline T* ScopedXcbPtr<T>::operator ->() const
+{
+ return m_pointer;
+}
+
+
+////////////////////////////////////////////////////////////
+template <typename T>
+inline T** ScopedXcbPtr<T>::operator &()
+{
+ return &m_pointer;
+}
+
+
+////////////////////////////////////////////////////////////
+template <typename T>
+inline ScopedXcbPtr<T>::operator bool() const
+{
+ return m_pointer != NULL;
+}
+
+
+////////////////////////////////////////////////////////////
+template <typename T>
+inline T* ScopedXcbPtr<T>::get() const
+{
+ return m_pointer;
+}
diff --git a/src/SFML/Window/Unix/SensorImpl.cpp b/src/SFML/Window/Unix/SensorImpl.cpp
index be5e439..144c6d7 100644
--- a/src/SFML/Window/Unix/SensorImpl.cpp
+++ b/src/SFML/Window/Unix/SensorImpl.cpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2013 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
diff --git a/src/SFML/Window/Unix/SensorImpl.hpp b/src/SFML/Window/Unix/SensorImpl.hpp
index bbd705b..49ced87 100644
--- a/src/SFML/Window/Unix/SensorImpl.hpp
+++ b/src/SFML/Window/Unix/SensorImpl.hpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2013 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
diff --git a/src/SFML/Window/Unix/VideoModeImpl.cpp b/src/SFML/Window/Unix/VideoModeImpl.cpp
index c7e6204..9ee812f 100644
--- a/src/SFML/Window/Unix/VideoModeImpl.cpp
+++ b/src/SFML/Window/Unix/VideoModeImpl.cpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -27,9 +27,9 @@
////////////////////////////////////////////////////////////
#include <SFML/Window/VideoModeImpl.hpp>
#include <SFML/Window/Unix/Display.hpp>
+#include <SFML/Window/Unix/ScopedXcbPtr.hpp>
#include <SFML/System/Err.hpp>
-#include <X11/Xlib.h>
-#include <X11/extensions/Xrandr.h>
+#include <xcb/randr.h>
#include <algorithm>
@@ -43,73 +43,96 @@ std::vector<VideoMode> VideoModeImpl::getFullscreenModes()
std::vector<VideoMode> modes;
// Open a connection with the X server
- Display* display = OpenDisplay();
- if (display)
+ xcb_connection_t* connection = OpenConnection();
+
+ // Retrieve the default screen
+ xcb_screen_t* screen = XCBDefaultScreen(connection);
+
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(connection, &xcb_randr_id);
+
+ if (!randrExt || !randrExt->present)
{
- // Retrieve the default screen number
- int screen = DefaultScreen(display);
+ // Randr extension is not supported: we cannot get the video modes
+ err() << "Failed to use the RandR extension while trying to get the supported video modes" << std::endl;
- // Check if the XRandR extension is present
- int version;
- if (XQueryExtension(display, "RANDR", &version, &version, &version))
- {
- // Get the current configuration
- XRRScreenConfiguration* config = XRRGetScreenInfo(display, RootWindow(display, screen));
- if (config)
- {
- // Get the available screen sizes
- int nbSizes;
- XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes);
- if (sizes && (nbSizes > 0))
- {
- // Get the list of supported depths
- int nbDepths = 0;
- int* depths = XListDepths(display, screen, &nbDepths);
- if (depths && (nbDepths > 0))
- {
- // Combine depths and sizes to fill the array of supported modes
- for (int i = 0; i < nbDepths; ++i)
- {
- for (int j = 0; j < nbSizes; ++j)
- {
- // Convert to VideoMode
- VideoMode mode(sizes[j].width, sizes[j].height, depths[i]);
-
- // Add it only if it is not already in the array
- if (std::find(modes.begin(), modes.end(), mode) == modes.end())
- modes.push_back(mode);
- }
- }
-
- // Free the array of depths
- XFree(depths);
- }
- }
-
- // Free the configuration instance
- XRRFreeScreenConfigInfo(config);
- }
- else
- {
- // Failed to get the screen configuration
- err() << "Failed to retrieve the screen configuration while trying to get the supported video modes" << std::endl;
- }
- }
- else
- {
- // XRandr extension is not supported: we cannot get the video modes
- err() << "Failed to use the XRandR extension while trying to get the supported video modes" << std::endl;
- }
+ // Close the connection with the X server
+ CloseConnection(connection);
+
+ return modes;
+ }
+
+ // Load RandR and check its version
+ ScopedXcbPtr<xcb_randr_query_version_reply_t> randrVersion(xcb_randr_query_version_reply(
+ connection,
+ xcb_randr_query_version(
+ connection,
+ 1,
+ 1
+ ),
+ &error
+ ));
+
+ if (error)
+ {
+ err() << "Failed to load the RandR extension while trying to get the supported video modes" << std::endl;
// Close the connection with the X server
- CloseDisplay(display);
+ CloseConnection(connection);
+
+ return modes;
}
- else
+
+ // Get the current configuration
+ ScopedXcbPtr<xcb_randr_get_screen_info_reply_t> config(xcb_randr_get_screen_info_reply(
+ connection,
+ xcb_randr_get_screen_info(
+ connection,
+ screen->root
+ ),
+ &error
+ ));
+
+ if (error)
+ {
+ // Failed to get the screen configuration
+ err() << "Failed to retrieve the screen configuration while trying to get the supported video modes" << std::endl;
+
+ // Close the connection with the X server
+ CloseConnection(connection);
+
+ return modes;
+ }
+
+ // Get the available screen sizes
+ xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get());
+ if (sizes && (config->nSizes > 0))
{
- // We couldn't connect to the X server
- err() << "Failed to connect to the X server while trying to get the supported video modes" << std::endl;
+ // Get the list of supported depths
+ xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen);
+ // Combine depths and sizes to fill the array of supported modes
+ for (; iter.rem; xcb_depth_next(&iter))
+ {
+ for (int j = 0; j < config->nSizes; ++j)
+ {
+ // Convert to VideoMode
+ VideoMode mode(sizes[j].width, sizes[j].height, iter.data->depth);
+
+ if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
+ config->rotation == XCB_RANDR_ROTATION_ROTATE_270)
+ std::swap(mode.width, mode.height);
+
+ // Add it only if it is not already in the array
+ if (std::find(modes.begin(), modes.end(), mode) == modes.end())
+ modes.push_back(mode);
+ }
+ }
}
+ // Close the connection with the X server
+ CloseConnection(connection);
+
return modes;
}
@@ -120,54 +143,91 @@ VideoMode VideoModeImpl::getDesktopMode()
VideoMode desktopMode;
// Open a connection with the X server
- Display* display = OpenDisplay();
- if (display)
+ xcb_connection_t* connection = OpenConnection();
+
+ // Retrieve the default screen
+ xcb_screen_t* screen = XCBDefaultScreen(connection);
+
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ // Check if the RandR extension is present
+ const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(connection, &xcb_randr_id);
+
+ if (!randrExt || !randrExt->present)
{
- // Retrieve the default screen number
- int screen = DefaultScreen(display);
+ // Randr extension is not supported: we cannot get the video modes
+ err() << "Failed to use the RandR extension while trying to get the desktop video mode" << std::endl;
- // Check if the XRandR extension is present
- int version;
- if (XQueryExtension(display, "RANDR", &version, &version, &version))
- {
- // Get the current configuration
- XRRScreenConfiguration* config = XRRGetScreenInfo(display, RootWindow(display, screen));
- if (config)
- {
- // Get the current video mode
- Rotation currentRotation;
- int currentMode = XRRConfigCurrentConfiguration(config, &currentRotation);
-
- // Get the available screen sizes
- int nbSizes;
- XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes);
- if (sizes && (nbSizes > 0))
- desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, DefaultDepth(display, screen));
-
- // Free the configuration instance
- XRRFreeScreenConfigInfo(config);
- }
- else
- {
- // Failed to get the screen configuration
- err() << "Failed to retrieve the screen configuration while trying to get the desktop video modes" << std::endl;
- }
- }
- else
- {
- // XRandr extension is not supported: we cannot get the video modes
- err() << "Failed to use the XRandR extension while trying to get the desktop video modes" << std::endl;
- }
+ // Close the connection with the X server
+ CloseConnection(connection);
+
+ return desktopMode;
+ }
+
+ // Load RandR and check its version
+ ScopedXcbPtr<xcb_randr_query_version_reply_t> randrVersion(xcb_randr_query_version_reply(
+ connection,
+ xcb_randr_query_version(
+ connection,
+ 1,
+ 1
+ ),
+ &error
+ ));
+
+ if (error)
+ {
+ err() << "Failed to load the RandR extension while trying to get the desktop video mode" << std::endl;
// Close the connection with the X server
- CloseDisplay(display);
+ CloseConnection(connection);
+
+ return desktopMode;
+ }
+
+ // Get the current configuration
+ ScopedXcbPtr<xcb_randr_get_screen_info_reply_t> config(xcb_randr_get_screen_info_reply(
+ connection,
+ xcb_randr_get_screen_info(
+ connection,
+ screen->root
+ ),
+ &error
+ ));
+
+ if (error)
+ {
+ // Failed to get the screen configuration
+ err() << "Failed to retrieve the screen configuration while trying to get the desktop video mode" << std::endl;
+
+ // Close the connection with the X server
+ CloseConnection(connection);
+
+ return desktopMode;
+ }
+
+ // Get the current video mode
+ xcb_randr_mode_t currentMode = config->sizeID;
+
+ // Get the available screen sizes
+ int nbSizes = xcb_randr_get_screen_info_sizes_length(config.get());
+ xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get());
+ if (sizes && (nbSizes > 0))
+ {
+ desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, screen->root_depth);
+
+ if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
+ config->rotation == XCB_RANDR_ROTATION_ROTATE_270)
+ std::swap(desktopMode.width, desktopMode.height);
}
else
{
- // We couldn't connect to the X server
- err() << "Failed to connect to the X server while trying to get the desktop video modes" << std::endl;
+ err() << "Failed to retrieve any screen sizes while trying to get the desktop video mode" << std::endl;
}
+ // Close the connection with the X server
+ CloseConnection(connection);
+
return desktopMode;
}
diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp
index 9e29757..d8c26e0 100644
--- a/src/SFML/Window/Unix/WindowImplX11.cpp
+++ b/src/SFML/Window/Unix/WindowImplX11.cpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -28,19 +28,27 @@
#include <SFML/Window/WindowStyle.hpp> // important to be included first (conflict with None)
#include <SFML/Window/Unix/WindowImplX11.hpp>
#include <SFML/Window/Unix/Display.hpp>
+#include <SFML/Window/Unix/ScopedXcbPtr.hpp>
#include <SFML/System/Utf.hpp>
#include <SFML/System/Err.hpp>
-#include <X11/Xutil.h>
-#include <X11/keysym.h>
-#include <X11/extensions/Xrandr.h>
-#include <libgen.h>
+#include <SFML/System/Mutex.hpp>
+#include <SFML/System/Lock.hpp>
+#include <xcb/xcb_image.h>
+#include <xcb/randr.h>
+#include <X11/Xlibint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
-#include <cstring>
-#include <sstream>
+#include <libgen.h>
+#include <fcntl.h>
+#include <algorithm>
#include <vector>
#include <string>
-#include <iterator>
-#include <algorithm>
+#include <cstring>
+
+// So we don't have to require xcb dri2 to be present
+#define XCB_DRI2_BUFFER_SWAP_COMPLETE 0
+#define XCB_DRI2_INVALIDATE_BUFFERS 1
#ifdef SFML_OPENGL_ES
#include <SFML/Window/EglContext.hpp>
@@ -57,30 +65,430 @@ namespace
{
sf::priv::WindowImplX11* fullscreenWindow = NULL;
std::vector<sf::priv::WindowImplX11*> allWindows;
- unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
- PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask |
- EnterWindowMask | LeaveWindowMask;
+ sf::Mutex allWindowsMutex;
+ sf::String windowManagerName;
- // Filter the events received by windows (only allow those matching a specific window)
- Bool checkEvent(::Display*, XEvent* event, XPointer userData)
- {
- // Just check if the event matches the window
- return event->xany.window == reinterpret_cast< ::Window >(userData);
- }
+ bool mapBuilt = false;
+
+ // We use a simple array instead of a map => constant time lookup
+ // xcb_keycode_t can only contain 256 distinct values
+ sf::Keyboard::Key sfKeyMap[256];
+
+ static const unsigned long eventMask = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_PRESS |
+ XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION |
+ XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_KEY_PRESS |
+ XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
+ XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW;
// Find the name of the current executable
- void findExecutableName(char* buffer, std::size_t bufferSize)
+ std::string findExecutableName()
{
- //Default fallback name
- const char* executableName = "sfml";
- std::size_t length = readlink("/proc/self/exe", buffer, bufferSize);
- if ((length > 0) && (length < bufferSize))
+ // We use /proc/self/cmdline to get the command line
+ // the user used to invoke this instance of the application
+ int file = ::open("/proc/self/cmdline", O_RDONLY | O_NONBLOCK);
+
+ if (file < 0)
+ return "sfml";
+
+ std::vector<char> buffer(256, 0);
+ std::size_t offset = 0;
+ ssize_t result = 0;
+
+ while ((result = read(file, &buffer[offset], 256)) > 0)
+ {
+ buffer.resize(buffer.size() + result, 0);
+ offset += result;
+ }
+
+ ::close(file);
+
+ if (offset)
{
+ buffer[offset] = 0;
+
// Remove the path to keep the executable name only
- buffer[length] = '\0';
- executableName = basename(buffer);
+ return basename(&buffer[0]);
+ }
+
+ // Default fallback name
+ return "sfml";
+ }
+
+ // Check if Extended Window Manager Hints are supported
+ bool ewmhSupported()
+ {
+ static bool checked = false;
+ static bool ewmhSupported = false;
+
+ if (checked)
+ return ewmhSupported;
+
+ checked = true;
+
+ xcb_connection_t* connection = sf::priv::OpenConnection();
+
+ xcb_atom_t netSupportingWmCheck = sf::priv::getAtom("_NET_SUPPORTING_WM_CHECK", true);
+ xcb_atom_t netSupported = sf::priv::getAtom("_NET_SUPPORTED", true);
+
+ if (!netSupportingWmCheck || !netSupported)
+ return false;
+
+ sf::priv::ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ sf::priv::ScopedXcbPtr<xcb_get_property_reply_t> rootSupportingWindow(xcb_get_property_reply(
+ connection,
+ xcb_get_property(
+ connection,
+ 0,
+ sf::priv::XCBDefaultRootWindow(connection),
+ netSupportingWmCheck,
+ XCB_ATOM_WINDOW,
+ 0,
+ 1
+ ),
+ &error
+ ));
+
+ if (!rootSupportingWindow || rootSupportingWindow->length != 1)
+ {
+ sf::priv::CloseConnection(connection);
+ return false;
+ }
+
+ xcb_window_t* rootWindow = reinterpret_cast<xcb_window_t*>(xcb_get_property_value(rootSupportingWindow.get()));
+
+ if (!rootWindow)
+ {
+ sf::priv::CloseConnection(connection);
+ return false;
+ }
+
+ sf::priv::ScopedXcbPtr<xcb_get_property_reply_t> childSupportingWindow(xcb_get_property_reply(
+ connection,
+ xcb_get_property(
+ connection,
+ 0,
+ *rootWindow,
+ netSupportingWmCheck,
+ XCB_ATOM_WINDOW,
+ 0,
+ 1
+ ),
+ &error
+ ));
+
+ if (!childSupportingWindow || childSupportingWindow->length != 1)
+ {
+ sf::priv::CloseConnection(connection);
+ return false;
+ }
+
+ xcb_window_t* childWindow = reinterpret_cast<xcb_window_t*>(xcb_get_property_value(childSupportingWindow.get()));
+
+ if (!childWindow)
+ {
+ sf::priv::CloseConnection(connection);
+ return false;
+ }
+
+ // Conforming window managers should return the same window for both queries
+ if (*rootWindow != *childWindow)
+ {
+ sf::priv::CloseConnection(connection);
+ return false;
+ }
+
+ ewmhSupported = true;
+
+ // We try to get the name of the window manager
+ // for window manager specific workarounds
+ xcb_atom_t netWmName = sf::priv::getAtom("_NET_WM_NAME", true);
+ xcb_atom_t utf8StringType = sf::priv::getAtom("UTF8_STRING");
+
+ if (!utf8StringType)
+ utf8StringType = XCB_ATOM_STRING;
+
+ if (!netWmName)
+ {
+ sf::priv::CloseConnection(connection);
+ return true;
+ }
+
+ sf::priv::ScopedXcbPtr<xcb_get_property_reply_t> wmName(xcb_get_property_reply(
+ connection,
+ xcb_get_property(
+ connection,
+ 0,
+ *childWindow,
+ netWmName,
+ utf8StringType,
+ 0,
+ 0x7fffffff
+ ),
+ &error
+ ));
+
+ sf::priv::CloseConnection(connection);
+
+ const char* name = reinterpret_cast<const char*>(xcb_get_property_value(wmName.get()));
+
+ windowManagerName = name;
+
+ return true;
+ }
+
+ xcb_query_extension_reply_t getDriExtension()
+ {
+ xcb_connection_t* connection = sf::priv::OpenConnection();
+
+ sf::priv::ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ // Check if the DRI2 extension is present
+ // We don't use xcb_get_extension_data here to avoid having to link to xcb_dri2
+ static const std::string DRI2 = "DRI2";
+ sf::priv::ScopedXcbPtr<xcb_query_extension_reply_t> driExt(xcb_query_extension_reply(
+ connection,
+ xcb_query_extension(
+ connection,
+ DRI2.size(),
+ DRI2.c_str()
+ ),
+ &error
+ ));
+
+ // Close the connection with the X server
+ sf::priv::CloseConnection(connection);
+
+ if (error || !driExt || !driExt->present)
+ {
+ xcb_query_extension_reply_t reply;
+ std::memset(&reply, 0, sizeof(reply));
+ return reply;
+ }
+
+ return *driExt.get();
+ }
+
+ void dumpXcbExtensions()
+ {
+ xcb_connection_t* connection = sf::priv::OpenConnection();
+
+ sf::priv::ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ // Check if the RandR extension is present
+ sf::priv::ScopedXcbPtr<xcb_list_extensions_reply_t> extensions(xcb_list_extensions_reply(
+ connection,
+ xcb_list_extensions(
+ connection
+ ),
+ &error
+ ));
+
+ if (error || !extensions)
+ {
+ // Close the connection with the X server
+ sf::priv::CloseConnection(connection);
+
+ sf::err() << "Couldn't get list of X extensions" << std::endl;
+ return;
+ }
+
+ xcb_str_iterator_t iter = xcb_list_extensions_names_iterator(extensions.get());
+
+ sf::err() << "X Extensions:" << std::endl;
+
+ while (iter.rem)
+ {
+ std::string name(xcb_str_name(iter.data), xcb_str_name_length(iter.data));
+
+ sf::priv::ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ // Check if the RandR extension is present
+ sf::priv::ScopedXcbPtr<xcb_query_extension_reply_t> extension(xcb_query_extension_reply(
+ connection,
+ xcb_query_extension(
+ connection,
+ name.size(),
+ name.c_str()
+ ),
+ &error
+ ));
+
+ if (error || !extension || !extension->present)
+ {
+ sf::err() << "\t" << name << " - Failed to query" << std::endl;
+ continue;
+ }
+
+ int firstEvent = extension->first_event;
+
+ sf::err() << "\t" << name << " - First event: " << firstEvent << std::endl;
+ iter.data += xcb_str_name_length(iter.data) + 1;
+ --iter.rem;
+ }
+
+ // Close the connection with the X server
+ sf::priv::CloseConnection(connection);
+ }
+
+ void dumpUnhandledEvent(uint8_t type)
+ {
+ static std::vector<uint8_t> types;
+
+ // Check if we already reported this type
+ if (std::find(types.begin(), types.end(), type) != types.end())
+ return;
+
+ // Insert it if new
+ types.push_back(type);
+
+ static bool dumpedExtensions = false;
+
+ if (!dumpedExtensions)
+ {
+ dumpXcbExtensions();
+ dumpedExtensions = true;
+ }
+
+ sf::err() << "Unhandled event type: " << (static_cast<int>(type) & ~0x80) << std::endl
+ << "Report this to the SFML maintainers if possible" << std::endl;
+ }
+
+ xcb_keysym_t getKeysymFromKeycode(xcb_keycode_t keycode)
+ {
+ return sf::priv::getKeysymMap()[keycode];
+ }
+
+ sf::Keyboard::Key keysymToSF(xcb_keysym_t symbol)
+ {
+ switch (symbol)
+ {
+ case XK_Shift_L: return sf::Keyboard::LShift;
+ case XK_Shift_R: return sf::Keyboard::RShift;
+ case XK_Control_L: return sf::Keyboard::LControl;
+ case XK_Control_R: return sf::Keyboard::RControl;
+ case XK_Alt_L: return sf::Keyboard::LAlt;
+ case XK_Alt_R: return sf::Keyboard::RAlt;
+ case XK_Super_L: return sf::Keyboard::LSystem;
+ case XK_Super_R: return sf::Keyboard::RSystem;
+ case XK_Menu: return sf::Keyboard::Menu;
+ case XK_Escape: return sf::Keyboard::Escape;
+ case XK_semicolon: return sf::Keyboard::SemiColon;
+ case XK_slash: return sf::Keyboard::Slash;
+ case XK_equal: return sf::Keyboard::Equal;
+ case XK_minus: return sf::Keyboard::Dash;
+ case XK_bracketleft: return sf::Keyboard::LBracket;
+ case XK_bracketright: return sf::Keyboard::RBracket;
+ case XK_comma: return sf::Keyboard::Comma;
+ case XK_period: return sf::Keyboard::Period;
+ case XK_apostrophe: return sf::Keyboard::Quote;
+ case XK_backslash: return sf::Keyboard::BackSlash;
+ case XK_grave: return sf::Keyboard::Tilde;
+ case XK_space: return sf::Keyboard::Space;
+ case XK_Return: return sf::Keyboard::Return;
+ case XK_KP_Enter: return sf::Keyboard::Return;
+ case XK_BackSpace: return sf::Keyboard::BackSpace;
+ case XK_Tab: return sf::Keyboard::Tab;
+ case XK_Prior: return sf::Keyboard::PageUp;
+ case XK_Next: return sf::Keyboard::PageDown;
+ case XK_End: return sf::Keyboard::End;
+ case XK_Home: return sf::Keyboard::Home;
+ case XK_Insert: return sf::Keyboard::Insert;
+ case XK_Delete: return sf::Keyboard::Delete;
+ case XK_KP_Add: return sf::Keyboard::Add;
+ case XK_KP_Subtract: return sf::Keyboard::Subtract;
+ case XK_KP_Multiply: return sf::Keyboard::Multiply;
+ case XK_KP_Divide: return sf::Keyboard::Divide;
+ case XK_Pause: return sf::Keyboard::Pause;
+ case XK_F1: return sf::Keyboard::F1;
+ case XK_F2: return sf::Keyboard::F2;
+ case XK_F3: return sf::Keyboard::F3;
+ case XK_F4: return sf::Keyboard::F4;
+ case XK_F5: return sf::Keyboard::F5;
+ case XK_F6: return sf::Keyboard::F6;
+ case XK_F7: return sf::Keyboard::F7;
+ case XK_F8: return sf::Keyboard::F8;
+ case XK_F9: return sf::Keyboard::F9;
+ case XK_F10: return sf::Keyboard::F10;
+ case XK_F11: return sf::Keyboard::F11;
+ case XK_F12: return sf::Keyboard::F12;
+ case XK_F13: return sf::Keyboard::F13;
+ case XK_F14: return sf::Keyboard::F14;
+ case XK_F15: return sf::Keyboard::F15;
+ case XK_Left: return sf::Keyboard::Left;
+ case XK_Right: return sf::Keyboard::Right;
+ case XK_Up: return sf::Keyboard::Up;
+ case XK_Down: return sf::Keyboard::Down;
+ case XK_KP_0: return sf::Keyboard::Numpad0;
+ case XK_KP_1: return sf::Keyboard::Numpad1;
+ case XK_KP_2: return sf::Keyboard::Numpad2;
+ case XK_KP_3: return sf::Keyboard::Numpad3;
+ case XK_KP_4: return sf::Keyboard::Numpad4;
+ case XK_KP_5: return sf::Keyboard::Numpad5;
+ case XK_KP_6: return sf::Keyboard::Numpad6;
+ case XK_KP_7: return sf::Keyboard::Numpad7;
+ case XK_KP_8: return sf::Keyboard::Numpad8;
+ case XK_KP_9: return sf::Keyboard::Numpad9;
+ case XK_a: return sf::Keyboard::A;
+ case XK_b: return sf::Keyboard::B;
+ case XK_c: return sf::Keyboard::C;
+ case XK_d: return sf::Keyboard::D;
+ case XK_e: return sf::Keyboard::E;
+ case XK_f: return sf::Keyboard::F;
+ case XK_g: return sf::Keyboard::G;
+ case XK_h: return sf::Keyboard::H;
+ case XK_i: return sf::Keyboard::I;
+ case XK_j: return sf::Keyboard::J;
+ case XK_k: return sf::Keyboard::K;
+ case XK_l: return sf::Keyboard::L;
+ case XK_m: return sf::Keyboard::M;
+ case XK_n: return sf::Keyboard::N;
+ case XK_o: return sf::Keyboard::O;
+ case XK_p: return sf::Keyboard::P;
+ case XK_q: return sf::Keyboard::Q;
+ case XK_r: return sf::Keyboard::R;
+ case XK_s: return sf::Keyboard::S;
+ case XK_t: return sf::Keyboard::T;
+ case XK_u: return sf::Keyboard::U;
+ case XK_v: return sf::Keyboard::V;
+ case XK_w: return sf::Keyboard::W;
+ case XK_x: return sf::Keyboard::X;
+ case XK_y: return sf::Keyboard::Y;
+ case XK_z: return sf::Keyboard::Z;
+ case XK_0: return sf::Keyboard::Num0;
+ case XK_1: return sf::Keyboard::Num1;
+ case XK_2: return sf::Keyboard::Num2;
+ case XK_3: return sf::Keyboard::Num3;
+ case XK_4: return sf::Keyboard::Num4;
+ case XK_5: return sf::Keyboard::Num5;
+ case XK_6: return sf::Keyboard::Num6;
+ case XK_7: return sf::Keyboard::Num7;
+ case XK_8: return sf::Keyboard::Num8;
+ case XK_9: return sf::Keyboard::Num9;
+ }
+
+ return sf::Keyboard::Unknown;
+ }
+
+ void buildMap()
+ {
+ for (xcb_keycode_t i = 0; ; ++i)
+ {
+ sfKeyMap[i] = keysymToSF(getKeysymFromKeycode(i));
+
+ if (i == 255)
+ break;
}
- std::memmove(buffer, executableName, std::strlen(executableName) + 1);
+
+ mapBuilt = true;
+ }
+
+ sf::Keyboard::Key keycodeToSF(xcb_keycode_t keycode)
+ {
+ if (!mapBuilt)
+ buildMap();
+
+ return sfKeyMap[keycode];
}
}
@@ -91,20 +499,33 @@ namespace priv
{
////////////////////////////////////////////////////////////
WindowImplX11::WindowImplX11(WindowHandle handle) :
-m_window (0),
-m_inputMethod (NULL),
-m_inputContext(NULL),
-m_isExternal (true),
-m_atomClose (0),
-m_oldVideoMode(-1),
-m_hiddenCursor(0),
-m_keyRepeat (true),
-m_previousSize(-1, -1),
-m_useSizeHints(false)
+m_window (0),
+m_inputMethod (NULL),
+m_inputContext (NULL),
+m_isExternal (true),
+m_hiddenCursor (0),
+m_keyRepeat (true),
+m_previousSize (-1, -1),
+m_useSizeHints (false),
+m_fullscreen (false)
{
// Open a connection with the X server
m_display = OpenDisplay();
- m_screen = DefaultScreen(m_display);
+ m_connection = XGetXCBConnection(m_display);
+
+ std::memset(&m_oldVideoMode, 0, sizeof(m_oldVideoMode));
+
+ if (!m_connection)
+ {
+ err() << "Failed cast Display object to an XCB connection object" << std::endl;
+ return;
+ }
+
+ // Make sure to check for EWMH support before we do anything
+ ewmhSupported();
+
+ m_screen = XCBDefaultScreen(m_connection);
+ XSetEventQueueOwner(m_display, XCBOwnsEventQueue);
// Save the window handle
m_window = handle;
@@ -112,7 +533,17 @@ m_useSizeHints(false)
if (m_window)
{
// Make sure the window is listening to all the required events
- XSelectInput(m_display, m_window, eventMask & ~ButtonPressMask);
+ const uint32_t value_list[] = {static_cast<uint32_t>(eventMask)};
+
+ xcb_change_window_attributes(
+ m_connection,
+ m_window,
+ XCB_CW_EVENT_MASK,
+ value_list
+ );
+
+ // Set the WM protocols
+ setProtocols();
// Do some common initializations
initialize();
@@ -122,158 +553,139 @@ m_useSizeHints(false)
////////////////////////////////////////////////////////////
WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings) :
-m_window (0),
-m_inputMethod (NULL),
-m_inputContext(NULL),
-m_isExternal (false),
-m_atomClose (0),
-m_oldVideoMode(-1),
-m_hiddenCursor(0),
-m_keyRepeat (true),
-m_previousSize(-1, -1),
-m_useSizeHints(false)
+m_window (0),
+m_inputMethod (NULL),
+m_inputContext (NULL),
+m_isExternal (false),
+m_hiddenCursor (0),
+m_keyRepeat (true),
+m_previousSize (-1, -1),
+m_useSizeHints (false),
+m_fullscreen ((style & Style::Fullscreen) != 0)
{
// Open a connection with the X server
m_display = OpenDisplay();
- m_screen = DefaultScreen(m_display);
- ::Window root = RootWindow(m_display, m_screen);
+ m_connection = XGetXCBConnection(m_display);
- // Compute position and size
- int left, top;
- bool fullscreen = (style & Style::Fullscreen) != 0;
- if (!fullscreen)
- {
- left = (DisplayWidth(m_display, m_screen) - mode.width) / 2;
- top = (DisplayHeight(m_display, m_screen) - mode.height) / 2;
- }
- else
+ std::memset(&m_oldVideoMode, 0, sizeof(m_oldVideoMode));
+
+ if (!m_connection)
{
- left = 0;
- top = 0;
+ err() << "Failed cast Display object to an XCB connection object" << std::endl;
+ return;
}
+
+ // Make sure to check for EWMH support before we do anything
+ ewmhSupported();
+
+ m_screen = XCBDefaultScreen(m_connection);
+ XSetEventQueueOwner(m_display, XCBOwnsEventQueue);
+
+ // Compute position and size
+ int left = m_fullscreen ? 0 : (m_screen->width_in_pixels - mode.width) / 2;
+ int top = m_fullscreen ? 0 : (m_screen->height_in_pixels - mode.height) / 2;
int width = mode.width;
int height = mode.height;
- // Switch to fullscreen if necessary
- if (fullscreen)
- switchToFullscreen(mode);
-
// Choose the visual according to the context settings
XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings);
// Define the window attributes
- XSetWindowAttributes attributes;
- attributes.override_redirect = fullscreen;
- attributes.event_mask = eventMask;
- attributes.colormap = XCreateColormap(m_display, root, visualInfo.visual, AllocNone);
+ xcb_colormap_t colormap = xcb_generate_id(m_connection);
+ xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, m_screen->root, visualInfo.visualid);
+ const uint32_t value_list[] = {m_fullscreen && !ewmhSupported(), static_cast<uint32_t>(eventMask), colormap};
// Create the window
- m_window = XCreateWindow(m_display,
- root,
- left, top,
- width, height,
- 0,
- visualInfo.depth,
- InputOutput,
- visualInfo.visual,
- CWEventMask | CWOverrideRedirect | CWColormap, &attributes);
- if (!m_window)
+ m_window = xcb_generate_id(m_connection);
+
+ ScopedXcbPtr<xcb_generic_error_t> errptr(xcb_request_check(
+ m_connection,
+ xcb_create_window_checked(
+ m_connection,
+ static_cast<uint8_t>(visualInfo.depth),
+ m_window,
+ m_screen->root,
+ left, top,
+ width, height,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ visualInfo.visualid,
+ XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP,
+ value_list
+ )
+ ));
+
+ if (errptr)
{
err() << "Failed to create window" << std::endl;
return;
}
- // Set the window's name
- setTitle(title);
-
- // Set the window's style (tell the windows manager to change our window's decorations and functions according to the requested style)
- if (!fullscreen)
+ // Set the WM protocols
+ setProtocols();
+
+ // Set the WM initial state to the normal state
+ WMHints hints;
+ std::memset(&hints, 0, sizeof(hints));
+ hints.initial_state = 1;
+ hints.flags |= 1 << 1;
+ setWMHints(hints);
+
+ // If not in fullscreen, set the window's style (tell the window manager to
+ // change our window's decorations and functions according to the requested style)
+ if (!m_fullscreen)
+ setMotifHints(style);
+
+ WMSizeHints sizeHints;
+ std::memset(&sizeHints, 0, sizeof(sizeHints));
+
+ // This is a hack to force some windows managers to disable resizing
+ // Fullscreen is bugged on Openbox. Unless size hints are set, there
+ // will be a region of the window that is off-screen. We try to workaround
+ // this by setting size hints even in fullscreen just for Openbox.
+ if ((!m_fullscreen || (windowManagerName == "Openbox")) && !(style & Style::Resize))
{
- Atom WMHintsAtom = XInternAtom(m_display, "_MOTIF_WM_HINTS", false);
- if (WMHintsAtom)
- {
- static const unsigned long MWM_HINTS_FUNCTIONS = 1 << 0;
- static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1;
-
- //static const unsigned long MWM_DECOR_ALL = 1 << 0;
- static const unsigned long MWM_DECOR_BORDER = 1 << 1;
- static const unsigned long MWM_DECOR_RESIZEH = 1 << 2;
- static const unsigned long MWM_DECOR_TITLE = 1 << 3;
- static const unsigned long MWM_DECOR_MENU = 1 << 4;
- static const unsigned long MWM_DECOR_MINIMIZE = 1 << 5;
- static const unsigned long MWM_DECOR_MAXIMIZE = 1 << 6;
-
- //static const unsigned long MWM_FUNC_ALL = 1 << 0;
- static const unsigned long MWM_FUNC_RESIZE = 1 << 1;
- static const unsigned long MWM_FUNC_MOVE = 1 << 2;
- static const unsigned long MWM_FUNC_MINIMIZE = 1 << 3;
- static const unsigned long MWM_FUNC_MAXIMIZE = 1 << 4;
- static const unsigned long MWM_FUNC_CLOSE = 1 << 5;
-
- struct WMHints
- {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long inputMode;
- unsigned long state;
- };
-
- WMHints hints;
- hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
- hints.decorations = 0;
- hints.functions = 0;
-
- if (style & Style::Titlebar)
- {
- hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU;
- hints.functions |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE;
- }
- if (style & Style::Resize)
- {
- hints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH;
- hints.functions |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
- }
- if (style & Style::Close)
- {
- hints.decorations |= 0;
- hints.functions |= MWM_FUNC_CLOSE;
- }
-
- const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&hints);
- XChangeProperty(m_display, m_window, WMHintsAtom, WMHintsAtom, 32, PropModeReplace, ptr, 5);
- }
-
- // This is a hack to force some windows managers to disable resizing
- if (!(style & Style::Resize))
- {
- m_useSizeHints = true;
- XSizeHints* sizeHints = XAllocSizeHints();
- sizeHints->flags = PMinSize | PMaxSize;
- sizeHints->min_width = sizeHints->max_width = width;
- sizeHints->min_height = sizeHints->max_height = height;
- XSetWMNormalHints(m_display, m_window, sizeHints);
- XFree(sizeHints);
- }
+ m_useSizeHints = true;
+ sizeHints.flags |= ((1 << 4) | (1 << 5));
+ sizeHints.min_width = width;
+ sizeHints.max_width = width;
+ sizeHints.min_height = height;
+ sizeHints.max_height = height;
}
+ // Set the WM hints of the normal state
+ setWMSizeHints(sizeHints);
+
// Set the window's WM class (this can be used by window managers)
- char windowClass[512];
- findExecutableName(windowClass, sizeof(windowClass));
- XClassHint* classHint = XAllocClassHint();
- classHint->res_name = windowClass;
- classHint->res_class = windowClass;
- XSetClassHint(m_display, m_window, classHint);
- XFree(classHint);
+ // The WM_CLASS property actually consists of 2 parts,
+ // the instance name and the class name both of which should be
+ // null terminated strings.
+ // The instance name should be something unique to this invokation
+ // of the application but is rarely if ever used these days.
+ // For simplicity, we retrieve it via the base executable name.
+ // The class name identifies a class of windows that
+ // "are of the same type". We simply use the initial window name as
+ // the class name.
+ std::string windowClass = findExecutableName();
+ windowClass += '\0'; // Important to separate instance from class
+ windowClass += title.toAnsiString();
+
+ // We add 1 to the size of the string to include the null at the end
+ if (!changeWindowProperty(XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, windowClass.size() + 1, windowClass.c_str()))
+ sf::err() << "Failed to set WM_CLASS property" << std::endl;
+
+ // Set the window's name
+ setTitle(title);
// Do some common initializations
initialize();
- // In fullscreen mode, we must grab keyboard and mouse inputs
- if (fullscreen)
+ // Set fullscreen video mode and switch to fullscreen if necessary
+ if (m_fullscreen)
{
- XGrabPointer(m_display, m_window, true, 0, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime);
- XGrabKeyboard(m_display, m_window, true, GrabModeAsync, GrabModeAsync, CurrentTime);
+ setPosition(Vector2i(0, 0));
+ setVideoMode(mode);
+ switchToFullscreen();
}
}
@@ -286,17 +698,16 @@ WindowImplX11::~WindowImplX11()
// Destroy the cursor
if (m_hiddenCursor)
- XFreeCursor(m_display, m_hiddenCursor);
+ xcb_free_cursor(m_connection, m_hiddenCursor);
// Destroy the input context
if (m_inputContext)
XDestroyIC(m_inputContext);
-
// Destroy the window
if (m_window && !m_isExternal)
{
- XDestroyWindow(m_display, m_window);
- XFlush(m_display);
+ xcb_destroy_window(m_connection, m_window);
+ xcb_flush(m_connection);
}
// Close the input method
@@ -307,6 +718,7 @@ WindowImplX11::~WindowImplX11()
CloseDisplay(m_display);
// Remove this window from the global list of windows (required for focus request)
+ Lock lock(allWindowsMutex);
allWindows.erase(std::find(allWindows.begin(), allWindows.end(), this));
}
@@ -321,10 +733,105 @@ WindowHandle WindowImplX11::getSystemHandle() const
////////////////////////////////////////////////////////////
void WindowImplX11::processEvents()
{
- XEvent event;
- while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast<XPointer>(m_window)))
+ // Key repeat workaround: If key repeat is enabled, XCB will spawn two
+ // events for each repeat interval: key release and key press. Both have
+ // the same timestamp and key code. We are holding back the release event
+ // to check for the matching key press event and if so, discard the release
+ // event.
+
+ xcb_generic_event_t* event = NULL;
+ xcb_key_release_event_t* lastKeyReleaseEvent = NULL;
+ uint8_t eventType = 0;
+
+ if (!m_xcbEvents.empty())
{
- processEvent(event);
+ event = m_xcbEvents.front();
+ m_xcbEvents.pop_front();
+ }
+ else
+ {
+ event = xcb_poll_for_event(m_connection);
+ }
+
+ while (event)
+ {
+ eventType = event->response_type & ~0x80;
+
+ // Key was pressed and one has been released prior to that.
+ if (eventType == XCB_KEY_PRESS && lastKeyReleaseEvent)
+ {
+ // If the key press event matches the held back key release event,
+ // then we have a key repeat and discard the held back release
+ // event.
+ if (lastKeyReleaseEvent->time == reinterpret_cast<xcb_key_press_event_t*>(event)->time &&
+ lastKeyReleaseEvent->detail == reinterpret_cast<xcb_key_press_event_t*>(event)->detail)
+ {
+ free(lastKeyReleaseEvent);
+ lastKeyReleaseEvent = NULL;
+
+ // If key repeat is disabled, discard the matching key press event as well
+ if (!m_keyRepeat)
+ {
+ free(event);
+
+ if (!m_xcbEvents.empty())
+ {
+ event = m_xcbEvents.front();
+ m_xcbEvents.pop_front();
+ }
+ else
+ {
+ event = xcb_poll_for_event(m_connection);
+ }
+
+ continue;
+ }
+ }
+ }
+
+ // If there's still a key release event held back, process it now.
+ if (lastKeyReleaseEvent)
+ {
+ if (processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent)))
+ free(lastKeyReleaseEvent);
+
+ lastKeyReleaseEvent = NULL;
+ }
+
+ if (eventType == XCB_KEY_RELEASE)
+ {
+ // Check if we're in charge of the key release
+ if (!passEvent(event, reinterpret_cast<xcb_key_release_event_t*>(event)->event))
+ {
+ // Remember this key release event.
+ lastKeyReleaseEvent = reinterpret_cast<xcb_key_release_event_t*>(event);
+ event = NULL; // For safety reasons.
+ }
+ }
+ else
+ {
+ if (processEvent(event))
+ free(event);
+ }
+
+ if (!m_xcbEvents.empty())
+ {
+ event = m_xcbEvents.front();
+ m_xcbEvents.pop_front();
+ }
+ else
+ {
+ event = xcb_poll_for_event(m_connection);
+ }
+ }
+
+ // Process any held back release event.
+ if (lastKeyReleaseEvent)
+ {
+ if (processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent)))
+ free(lastKeyReleaseEvent);
+
+ lastKeyReleaseEvent = NULL;
}
}
@@ -332,71 +839,146 @@ void WindowImplX11::processEvents()
////////////////////////////////////////////////////////////
Vector2i WindowImplX11::getPosition() const
{
- ::Window root, child;
- int localX, localY, x, y;
- unsigned int width, height, border, depth;
+ ::Window topLevelWindow = m_window;
+ ::Window nextWindow = topLevelWindow;
+
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ // Get "top level" window, i.e. the window with the root window as its parent.
+ while (nextWindow != m_screen->root)
+ {
+ topLevelWindow = nextWindow;
+
+ ScopedXcbPtr<xcb_query_tree_reply_t> treeReply(xcb_query_tree_reply(
+ m_connection,
+ xcb_query_tree(
+ m_connection,
+ topLevelWindow
+ ),
+ &error
+ ));
+
+ if (error)
+ {
+ err() << "Failed to get window position (query_tree)" << std::endl;
+ return Vector2i(0, 0);
+ }
+
+ nextWindow = treeReply->parent;
+ }
+
+ ScopedXcbPtr<xcb_get_geometry_reply_t> geometryReply(xcb_get_geometry_reply(
+ m_connection,
+ xcb_get_geometry(
+ m_connection,
+ topLevelWindow
+ ),
+ &error
+ ));
- XGetGeometry(m_display, m_window, &root, &localX, &localY, &width, &height, &border, &depth);
- XTranslateCoordinates(m_display, m_window, root, localX, localY, &x, &y, &child);
+ if (error)
+ {
+ err() << "Failed to get window position (get_geometry)" << std::endl;
+ return Vector2i(0, 0);
+ }
- return Vector2i(x, y);
+ return Vector2i(geometryReply->x, geometryReply->y);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setPosition(const Vector2i& position)
{
- XMoveWindow(m_display, m_window, position.x, position.y);
- XFlush(m_display);
+ uint32_t values[] = {static_cast<uint32_t>(position.x), static_cast<uint32_t>(position.y)};
+ xcb_configure_window(m_connection, m_window,
+ XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
+ values);
+ xcb_flush(m_connection);
}
////////////////////////////////////////////////////////////
Vector2u WindowImplX11::getSize() const
{
- XWindowAttributes attributes;
- XGetWindowAttributes(m_display, m_window, &attributes);
- return Vector2u(attributes.width, attributes.height);
+ ScopedXcbPtr<xcb_get_geometry_reply_t> reply(xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL));
+
+ return Vector2u(reply->width, reply->height);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setSize(const Vector2u& size)
{
- // If we used size hint to fix the size of the window, we must update them
- if (m_useSizeHints)
- {
- XSizeHints* sizeHints = XAllocSizeHints();
- sizeHints->flags = PMinSize | PMaxSize;
- sizeHints->min_width = sizeHints->max_width = size.x;
- sizeHints->min_height = sizeHints->max_height = size.y;
- XSetWMNormalHints(m_display, m_window, sizeHints);
- XFree(sizeHints);
+ // If resizing is disable for the window we have to update the size hints (required by some window managers).
+ if( m_useSizeHints ) {
+ WMSizeHints sizeHints;
+ std::memset(&sizeHints, 0, sizeof(sizeHints));
+
+ sizeHints.flags |= (1 << 4 | 1 << 5);
+ sizeHints.min_width = size.x;
+ sizeHints.max_width = size.x;
+ sizeHints.min_height = size.y;
+ sizeHints.max_height = size.y;
+
+ setWMSizeHints(sizeHints);
}
- XResizeWindow(m_display, m_window, size.x, size.y);
- XFlush(m_display);
+ uint32_t values[] = {size.x, size.y};
+
+ ScopedXcbPtr<xcb_generic_error_t> configureWindowError(xcb_request_check(
+ m_connection,
+ xcb_configure_window(
+ m_connection,
+ m_window,
+ XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
+ values
+ )
+ ));
+
+ if (configureWindowError)
+ err() << "Failed to set window size" << std::endl;
+
+ xcb_flush(m_connection);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setTitle(const String& title)
{
- // Bare X11 has no Unicode window title support.
- // There is however an option to tell the window manager your Unicode title via hints.
+ // XCB takes UTF-8-encoded strings.
+ xcb_atom_t utf8StringType = getAtom("UTF8_STRING");
+
+ if (!utf8StringType)
+ utf8StringType = XCB_ATOM_STRING;
+
+ std::string utf8String;
+ Utf<32>::toUtf8(title.begin(), title.end(), std::back_inserter(utf8String));
+
+ if (!changeWindowProperty(XCB_ATOM_WM_NAME, utf8StringType, 8, utf8String.length(), utf8String.c_str()))
+ err() << "Failed to set window title" << std::endl;
- // Convert to UTF-8 encoding.
- std::basic_string<Uint8> utf8Title;
- Utf32::toUtf8(title.begin(), title.end(), std::back_inserter(utf8Title));
+ if (!changeWindowProperty(XCB_ATOM_WM_ICON_NAME, utf8StringType, 8, utf8String.length(), utf8String.c_str()))
+ err() << "Failed to set WM_ICON_NAME property" << std::endl;
- // Set the _NET_WM_NAME atom, which specifies a UTF-8 encoded window title.
- Atom wmName = XInternAtom(m_display, "_NET_WM_NAME", False);
- Atom useUtf8 = XInternAtom(m_display, "UTF8_STRING", False);
- XChangeProperty(m_display, m_window, wmName, useUtf8, 8,
- PropModeReplace, utf8Title.c_str(), utf8Title.size());
+ if (ewmhSupported())
+ {
+ xcb_atom_t netWmName = getAtom("_NET_WM_NAME", true);
+ xcb_atom_t netWmIconName = getAtom("_NET_WM_ICON_NAME", true);
+
+ if (utf8StringType && netWmName)
+ {
+ if (!changeWindowProperty(netWmName, utf8StringType, 8, utf8String.length(), utf8String.c_str()))
+ err() << "Failed to set _NET_WM_NAME property" << std::endl;
+ }
+
+ if (utf8StringType && netWmName)
+ {
+ if (!changeWindowProperty(netWmIconName, utf8StringType, 8, utf8String.length(), utf8String.c_str()))
+ err() << "Failed to set _NET_WM_ICON_NAME property" << std::endl;
+ }
+ }
- // Set the non-Unicode title as a fallback for window managers who don't support _NET_WM_NAME.
- XStoreName(m_display, m_window, title.toAnsiString().c_str());
+ xcb_flush(m_connection);
}
@@ -404,8 +986,7 @@ void WindowImplX11::setTitle(const String& title)
void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8* pixels)
{
// X11 wants BGRA pixels: swap red and blue channels
- // Note: this memory will be freed by XDestroyImage
- Uint8* iconPixels = static_cast<Uint8*>(std::malloc(width * height * 4));
+ Uint8 iconPixels[width * height * 4];
for (std::size_t i = 0; i < width * height; ++i)
{
iconPixels[i * 4 + 0] = pixels[i * 4 + 2];
@@ -415,20 +996,85 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8
}
// Create the icon pixmap
- Visual* defVisual = DefaultVisual(m_display, m_screen);
- unsigned int defDepth = DefaultDepth(m_display, m_screen);
- XImage* iconImage = XCreateImage(m_display, defVisual, defDepth, ZPixmap, 0, (char*)iconPixels, width, height, 32, 0);
- if (!iconImage)
+ xcb_pixmap_t iconPixmap = xcb_generate_id(m_connection);
+
+ ScopedXcbPtr<xcb_generic_error_t> createPixmapError(xcb_request_check(
+ m_connection,
+ xcb_create_pixmap_checked(
+ m_connection,
+ m_screen->root_depth,
+ iconPixmap,
+ m_screen->root,
+ width,
+ height
+ )
+ ));
+
+ if (createPixmapError)
{
- err() << "Failed to set the window's icon" << std::endl;
+ err() << "Failed to set the window's icon (create_pixmap): ";
+ err() << "Error code " << static_cast<int>(createPixmapError->error_code) << std::endl;
+ return;
+ }
+
+ xcb_gcontext_t iconGC = xcb_generate_id(m_connection);
+
+ ScopedXcbPtr<xcb_generic_error_t> createGcError(xcb_request_check(
+ m_connection,
+ xcb_create_gc(
+ m_connection,
+ iconGC,
+ iconPixmap,
+ 0,
+ NULL
+ )
+ ));
+
+ if (createGcError)
+ {
+ err() << "Failed to set the window's icon (create_gc): ";
+ err() << "Error code " << static_cast<int>(createGcError->error_code) << std::endl;
+ return;
+ }
+
+ ScopedXcbPtr<xcb_generic_error_t> putImageError(xcb_request_check(
+ m_connection,
+ xcb_put_image_checked(
+ m_connection,
+ XCB_IMAGE_FORMAT_Z_PIXMAP,
+ iconPixmap,
+ iconGC,
+ width,
+ height,
+ 0,
+ 0,
+ 0,
+ m_screen->root_depth,
+ sizeof(iconPixels),
+ iconPixels
+ )
+ ));
+
+ ScopedXcbPtr<xcb_generic_error_t> freeGcError(xcb_request_check(
+ m_connection,
+ xcb_free_gc(
+ m_connection,
+ iconGC
+ )
+ ));
+
+ if (freeGcError)
+ {
+ err() << "Failed to free icon GC: ";
+ err() << "Error code " << static_cast<int>(freeGcError->error_code) << std::endl;
+ }
+
+ if (putImageError)
+ {
+ err() << "Failed to set the window's icon (put_image): ";
+ err() << "Error code " << static_cast<int>(putImageError->error_code) << std::endl;
return;
}
- Pixmap iconPixmap = XCreatePixmap(m_display, RootWindow(m_display, m_screen), width, height, defDepth);
- XGCValues values;
- GC iconGC = XCreateGC(m_display, iconPixmap, 0, &values);
- XPutImage(m_display, iconPixmap, iconGC, iconImage, 0, 0, 0, 0, width, height);
- XFreeGC(m_display, iconGC);
- XDestroyImage(iconImage);
// Create the mask pixmap (must have 1 bit depth)
std::size_t pitch = (width + 7) / 8;
@@ -447,17 +1093,43 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8
}
}
}
- Pixmap maskPixmap = XCreatePixmapFromBitmapData(m_display, m_window, (char*)&maskPixels[0], width, height, 1, 0, 1);
+
+ xcb_pixmap_t maskPixmap = xcb_create_pixmap_from_bitmap_data(
+ m_connection,
+ m_window,
+ reinterpret_cast<uint8_t*>(&maskPixels[0]),
+ width,
+ height,
+ 1,
+ 0,
+ 1,
+ NULL
+ );
// Send our new icon to the window through the WMHints
- XWMHints* hints = XAllocWMHints();
- hints->flags = IconPixmapHint | IconMaskHint;
- hints->icon_pixmap = iconPixmap;
- hints->icon_mask = maskPixmap;
- XSetWMHints(m_display, m_window, hints);
- XFree(hints);
-
- XFlush(m_display);
+ WMHints hints;
+ std::memset(&hints, 0, sizeof(hints));
+ hints.flags |= ((1 << 2) | (1 << 5));
+ hints.icon_pixmap = iconPixmap;
+ hints.icon_mask = maskPixmap;
+
+ setWMHints(hints);
+
+ xcb_flush(m_connection);
+
+ ScopedXcbPtr<xcb_generic_error_t> freePixmapError(xcb_request_check(
+ m_connection,
+ xcb_free_pixmap_checked(
+ m_connection,
+ iconPixmap
+ )
+ ));
+
+ if (freePixmapError)
+ {
+ err() << "Failed to free icon pixmap: ";
+ err() << "Error code " << static_cast<int>(freePixmapError->error_code) << std::endl;
+ }
}
@@ -465,19 +1137,55 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8
void WindowImplX11::setVisible(bool visible)
{
if (visible)
- XMapWindow(m_display, m_window);
+ {
+ ScopedXcbPtr<xcb_generic_error_t> error(xcb_request_check(
+ m_connection,
+ xcb_map_window(
+ m_connection,
+ m_window
+ )
+ ));
+
+ if (error)
+ err() << "Failed to change window visibility" << std::endl;
+ }
else
- XUnmapWindow(m_display, m_window);
+ {
+ ScopedXcbPtr<xcb_generic_error_t> error(xcb_request_check(
+ m_connection,
+ xcb_unmap_window(
+ m_connection,
+ m_window
+ )
+ ));
+
+ if (error)
+ err() << "Failed to change window visibility" << std::endl;
+ }
- XFlush(m_display);
+ xcb_flush(m_connection);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setMouseCursorVisible(bool visible)
{
- XDefineCursor(m_display, m_window, visible ? None : m_hiddenCursor);
- XFlush(m_display);
+ const uint32_t values = visible ? XCB_NONE : m_hiddenCursor;
+
+ ScopedXcbPtr<xcb_generic_error_t> error(xcb_request_check(
+ m_connection,
+ xcb_change_window_attributes(
+ m_connection,
+ m_window,
+ XCB_CW_CURSOR,
+ &values
+ )
+ ));
+
+ if (error)
+ err() << "Failed to change mouse cursor visibility" << std::endl;
+
+ xcb_flush(m_connection);
}
@@ -495,45 +1203,73 @@ void WindowImplX11::requestFocus()
// Check the global list of windows to find out whether an SFML window has the focus
// Note: can't handle console and other non-SFML windows belonging to the application.
bool sfmlWindowFocused = false;
- for (std::vector<WindowImplX11*>::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr)
+
{
- if ((*itr)->hasFocus())
+ Lock lock(allWindowsMutex);
+ for (std::vector<WindowImplX11*>::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr)
{
- sfmlWindowFocused = true;
- break;
+ if ((*itr)->hasFocus())
+ {
+ sfmlWindowFocused = true;
+ break;
+ }
}
}
-
+
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
// Check if window is viewable (not on other desktop, ...)
// TODO: Check also if minimized
- XWindowAttributes attributes;
- if (XGetWindowAttributes(m_display, m_window, &attributes) == 0)
+ ScopedXcbPtr<xcb_get_window_attributes_reply_t> attributes(xcb_get_window_attributes_reply(
+ m_connection,
+ xcb_get_window_attributes(
+ m_connection,
+ m_window
+ ),
+ &error
+ ));
+
+ if (error || !attributes)
{
- sf::err() << "Failed to check if window is viewable while requesting focus" << std::endl;
+ err() << "Failed to check if window is viewable while requesting focus" << std::endl;
return; // error getting attribute
}
- bool windowViewable = (attributes.map_state == IsViewable);
-
+ bool windowViewable = (attributes->map_state == XCB_MAP_STATE_VIEWABLE);
+
if (sfmlWindowFocused && windowViewable)
{
// Another SFML window of this application has the focus and the current window is viewable:
// steal focus (i.e. bring window to the front and give it input focus)
- XRaiseWindow(m_display, m_window);
- XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
+ grabFocus();
}
else
{
- // Otherwise: display urgency hint (flashing application logo)
- // Ensure WM hints exist, allocate if necessary
- XWMHints* hints = XGetWMHints(m_display, m_window);
- if (hints == NULL)
- hints = XAllocWMHints();
-
- // Add urgency (notification) flag to hints
- hints->flags |= XUrgencyHint;
- XSetWMHints(m_display, m_window, hints);
- XFree(hints);
+ // Get current WM hints.
+ ScopedXcbPtr<xcb_get_property_reply_t> hintsReply(xcb_get_property_reply(
+ m_connection,
+ xcb_get_property(m_connection, 0, m_window, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 0, 9),
+ &error
+ ));
+
+ if (error || !hintsReply)
+ {
+ err() << "Failed to get WM hints while requesting focus" << std::endl;
+ return;
+ }
+
+ WMHints* hints = reinterpret_cast<WMHints*>(xcb_get_property_value(hintsReply.get()));
+
+ if (!hints)
+ {
+ err() << "Failed to get WM hints while requesting focus" << std::endl;
+ return;
+ }
+
+ // Even if no hints were returned, we can simply set the proper flags we need and go on. This is
+ // different from Xlib where XAllocWMHints() has to be called.
+ hints->flags |= (1 << 8);
+ setWMHints(*hints);
}
}
@@ -541,101 +1277,498 @@ void WindowImplX11::requestFocus()
////////////////////////////////////////////////////////////
bool WindowImplX11::hasFocus() const
{
- ::Window focusedWindow = 0;
- int revertToReturn = 0;
- XGetInputFocus(m_display, &focusedWindow, &revertToReturn);
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ ScopedXcbPtr<xcb_get_input_focus_reply_t> reply(xcb_get_input_focus_reply(
+ m_connection,
+ xcb_get_input_focus_unchecked(
+ m_connection
+ ),
+ &error
+ ));
- return m_window == focusedWindow;
+ if (error)
+ err() << "Failed to check if window has focus" << std::endl;
+
+ return (reply->focus == m_window);
}
////////////////////////////////////////////////////////////
-void WindowImplX11::switchToFullscreen(const VideoMode& mode)
+void WindowImplX11::grabFocus()
{
- // Check if the XRandR extension is present
- int version;
- if (XQueryExtension(m_display, "RANDR", &version, &version, &version))
+ xcb_atom_t netActiveWindow = XCB_ATOM_NONE;
+
+ if (ewmhSupported())
+ netActiveWindow = getAtom("_NET_ACTIVE_WINDOW");
+
+ if (netActiveWindow)
+ {
+ xcb_client_message_event_t event;
+ std::memset(&event, 0, sizeof(event));
+
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.window = m_window;
+ event.format = 32;
+ event.sequence = 0;
+ event.type = netActiveWindow;
+ event.data.data32[0] = 1; // Normal application
+ event.data.data32[1] = XCB_CURRENT_TIME;
+ event.data.data32[2] = 0; // We don't know the currently active window
+
+ ScopedXcbPtr<xcb_generic_error_t> activeWindowError(xcb_request_check(
+ m_connection,
+ xcb_send_event_checked(
+ m_connection,
+ 0,
+ XCBDefaultRootWindow(m_connection),
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ reinterpret_cast<char*>(&event)
+ )
+ ));
+
+ if (activeWindowError)
+ err() << "Setting fullscreen failed, could not send \"_NET_ACTIVE_WINDOW\" event" << std::endl;
+ }
+ else
{
- // Get the current configuration
- XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen));
- if (config)
+ ScopedXcbPtr<xcb_generic_error_t> setInputFocusError(xcb_request_check(
+ m_connection,
+ xcb_set_input_focus(
+ m_connection,
+ XCB_INPUT_FOCUS_POINTER_ROOT,
+ m_window,
+ XCB_CURRENT_TIME
+ )
+ ));
+
+ if (setInputFocusError)
{
- // Get the current rotation
- Rotation currentRotation;
- m_oldVideoMode = XRRConfigCurrentConfiguration(config, &currentRotation);
-
- // Get the available screen sizes
- int nbSizes;
- XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes);
- if (sizes && (nbSizes > 0))
- {
- // Search a matching size
- for (int i = 0; i < nbSizes; ++i)
- {
- if ((sizes[i].width == static_cast<int>(mode.width)) && (sizes[i].height == static_cast<int>(mode.height)))
- {
- // Switch to fullscreen mode
- XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), i, currentRotation, CurrentTime);
+ err() << "Failed to change active window (set_input_focus)" << std::endl;
+ return;
+ }
- // Set "this" as the current fullscreen window
- fullscreenWindow = this;
- break;
- }
- }
- }
+ const uint32_t values[] = {XCB_STACK_MODE_ABOVE};
+
+ ScopedXcbPtr<xcb_generic_error_t> configureWindowError(xcb_request_check(
+ m_connection,
+ xcb_configure_window(
+ m_connection,
+ m_window,
+ XCB_CONFIG_WINDOW_STACK_MODE,
+ values
+ )
+ ));
+
+ if (configureWindowError)
+ err() << "Failed to change active window (configure_window)" << std::endl;
+ }
+}
+
+
+////////////////////////////////////////////////////////////
+void WindowImplX11::setVideoMode(const VideoMode& mode)
+{
+ // Skip mode switching if the new mode is equal to the desktop mode
+ if (mode == VideoMode::getDesktopMode())
+ return;
+
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ // Check if the RandR extension is present
+ const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(m_connection, &xcb_randr_id);
- // Free the configuration instance
- XRRFreeScreenConfigInfo(config);
+ if (!randrExt || !randrExt->present)
+ {
+ // RandR extension is not supported: we cannot use fullscreen mode
+ err() << "Fullscreen is not supported, switching to window mode" << std::endl;
+ return;
+ }
+
+ // Load RandR and check its version
+ ScopedXcbPtr<xcb_randr_query_version_reply_t> randrVersion(xcb_randr_query_version_reply(
+ m_connection,
+ xcb_randr_query_version(
+ m_connection,
+ 1,
+ 1
+ ),
+ &error
+ ));
+
+ if (error)
+ {
+ err() << "Failed to load RandR, switching to window mode" << std::endl;
+ return;
+ }
+
+ // Get the current configuration
+ ScopedXcbPtr<xcb_randr_get_screen_info_reply_t> config(xcb_randr_get_screen_info_reply(
+ m_connection,
+ xcb_randr_get_screen_info(
+ m_connection,
+ m_screen->root
+ ),
+ &error
+ ));
+
+ if (error || !config)
+ {
+ // Failed to get the screen configuration
+ err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl;
+ return;
+ }
+
+ // Save the current video mode before we switch to fullscreen
+ m_oldVideoMode = *config.get();
+
+ // Get the available screen sizes
+ xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get());
+
+ if (!sizes || !config->nSizes)
+ {
+ err() << "Failed to get the fullscreen sizes, switching to window mode" << std::endl;
+ return;
+ }
+
+ // Search for a matching size
+ for (int i = 0; i < config->nSizes; ++i)
+ {
+ if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
+ config->rotation == XCB_RANDR_ROTATION_ROTATE_270)
+ std::swap(sizes[i].height, sizes[i].width);
+
+ if ((sizes[i].width == static_cast<int>(mode.width)) &&
+ (sizes[i].height == static_cast<int>(mode.height)))
+ {
+ // Switch to fullscreen mode
+ ScopedXcbPtr<xcb_randr_set_screen_config_reply_t> setScreenConfig(xcb_randr_set_screen_config_reply(
+ m_connection,
+ xcb_randr_set_screen_config(
+ m_connection,
+ config->root,
+ XCB_CURRENT_TIME,
+ config->config_timestamp,
+ i,
+ config->rotation,
+ config->rate
+ ),
+ &error
+ ));
+
+ if (error)
+ err() << "Failed to set new screen configuration" << std::endl;
+
+ // Set "this" as the current fullscreen window
+ fullscreenWindow = this;
+ return;
}
- else
+ }
+
+ err() << "Failed to find matching fullscreen size, switching to window mode" << std::endl;
+}
+
+
+////////////////////////////////////////////////////////////
+void WindowImplX11::resetVideoMode()
+{
+ if (fullscreenWindow == this)
+ {
+ // Get current screen info
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ // Reset the video mode
+ ScopedXcbPtr<xcb_randr_set_screen_config_reply_t> setScreenConfig(xcb_randr_set_screen_config_reply(
+ m_connection,
+ xcb_randr_set_screen_config(
+ m_connection,
+ m_oldVideoMode.root,
+ XCB_CURRENT_TIME,
+ m_oldVideoMode.config_timestamp,
+ m_oldVideoMode.sizeID,
+ m_oldVideoMode.rotation,
+ m_oldVideoMode.rate
+ ),
+ &error
+ ));
+
+ if (error)
+ err() << "Failed to reset old screen configuration" << std::endl;
+
+ // Reset the fullscreen window
+ fullscreenWindow = NULL;
+ }
+}
+
+
+////////////////////////////////////////////////////////////
+void WindowImplX11::switchToFullscreen()
+{
+ grabFocus();
+
+ if (ewmhSupported())
+ {
+ xcb_atom_t netWmBypassCompositor = getAtom("_NET_WM_BYPASS_COMPOSITOR");
+
+ if (netWmBypassCompositor)
+ {
+ static const Uint32 bypassCompositor = 1;
+
+ // Not being able to bypass the compositor is not a fatal error
+ if (!changeWindowProperty(netWmBypassCompositor, XCB_ATOM_CARDINAL, 32, 1, &bypassCompositor))
+ err() << "xcb_change_property failed, unable to set _NET_WM_BYPASS_COMPOSITOR" << std::endl;
+ }
+
+ xcb_atom_t netWmState = getAtom("_NET_WM_STATE", true);
+ xcb_atom_t netWmStateFullscreen = getAtom("_NET_WM_STATE_FULLSCREEN", true);
+
+ if (!netWmState || !netWmStateFullscreen)
{
- // Failed to get the screen configuration
- err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl;
+ err() << "Setting fullscreen failed. Could not get required atoms" << std::endl;
+ return;
}
+
+ xcb_client_message_event_t event;
+ std::memset(&event, 0, sizeof(event));
+
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.window = m_window;
+ event.format = 32;
+ event.sequence = 0;
+ event.type = netWmState;
+ event.data.data32[0] = 1; // _NET_WM_STATE_ADD
+ event.data.data32[1] = netWmStateFullscreen;
+ event.data.data32[2] = 0; // No second property
+ event.data.data32[3] = 1; // Normal window
+
+ ScopedXcbPtr<xcb_generic_error_t> wmStateError(xcb_request_check(
+ m_connection,
+ xcb_send_event_checked(
+ m_connection,
+ 0,
+ XCBDefaultRootWindow(m_connection),
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ reinterpret_cast<char*>(&event)
+ )
+ ));
+
+ if (wmStateError)
+ err() << "Setting fullscreen failed. Could not send \"_NET_WM_STATE\" event" << std::endl;
+ }
+}
+
+
+////////////////////////////////////////////////////////////
+void WindowImplX11::setProtocols()
+{
+ xcb_atom_t wmProtocols = getAtom("WM_PROTOCOLS");
+ xcb_atom_t wmDeleteWindow = getAtom("WM_DELETE_WINDOW");
+
+ if (!wmProtocols)
+ {
+ err() << "Failed to request WM_PROTOCOLS atom." << std::endl;
+ return;
+ }
+
+ std::vector<xcb_atom_t> atoms;
+
+ if (wmDeleteWindow)
+ {
+ atoms.push_back(wmDeleteWindow);
}
else
{
- // XRandr extension is not supported: we cannot use fullscreen mode
- err() << "Fullscreen is not supported, switching to window mode" << std::endl;
+ err() << "Failed to request WM_DELETE_WINDOW atom." << std::endl;
+ }
+
+ xcb_atom_t netWmPing = XCB_ATOM_NONE;
+ xcb_atom_t netWmPid = XCB_ATOM_NONE;
+
+ if (ewmhSupported())
+ {
+ netWmPing = getAtom("_NET_WM_PING", true);
+ netWmPid = getAtom("_NET_WM_PID", true);
+ }
+
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ if (netWmPing && netWmPid)
+ {
+ uint32_t pid = getpid();
+
+ if (changeWindowProperty(netWmPid, XCB_ATOM_CARDINAL, 32, 1, &pid))
+ atoms.push_back(netWmPing);
+ }
+
+ if (!atoms.empty())
+ {
+ if (!changeWindowProperty(wmProtocols, XCB_ATOM_ATOM, 32, atoms.size(), &atoms[0]))
+ err() << "Failed to set window protocols" << std::endl;
+ }
+ else
+ {
+ err() << "Didn't set any window protocols" << std::endl;
}
}
////////////////////////////////////////////////////////////
-void WindowImplX11::initialize()
+void WindowImplX11::setMotifHints(unsigned long style)
{
- // Get the atom defining the close event
- m_atomClose = XInternAtom(m_display, "WM_DELETE_WINDOW", false);
- XSetWMProtocols(m_display, m_window, &m_atomClose, 1);
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS";
+ ScopedXcbPtr<xcb_intern_atom_reply_t> hintsAtomReply(xcb_intern_atom_reply(
+ m_connection,
+ xcb_intern_atom(
+ m_connection,
+ 0,
+ MOTIF_WM_HINTS.size(),
+ MOTIF_WM_HINTS.c_str()
+ ),
+ &error
+ ));
+
+ if (!error && hintsAtomReply)
+ {
+ static const unsigned long MWM_HINTS_FUNCTIONS = 1 << 0;
+ static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1;
+
+ //static const unsigned long MWM_DECOR_ALL = 1 << 0;
+ static const unsigned long MWM_DECOR_BORDER = 1 << 1;
+ static const unsigned long MWM_DECOR_RESIZEH = 1 << 2;
+ static const unsigned long MWM_DECOR_TITLE = 1 << 3;
+ static const unsigned long MWM_DECOR_MENU = 1 << 4;
+ static const unsigned long MWM_DECOR_MINIMIZE = 1 << 5;
+ static const unsigned long MWM_DECOR_MAXIMIZE = 1 << 6;
+
+ //static const unsigned long MWM_FUNC_ALL = 1 << 0;
+ static const unsigned long MWM_FUNC_RESIZE = 1 << 1;
+ static const unsigned long MWM_FUNC_MOVE = 1 << 2;
+ static const unsigned long MWM_FUNC_MINIMIZE = 1 << 3;
+ static const unsigned long MWM_FUNC_MAXIMIZE = 1 << 4;
+ static const unsigned long MWM_FUNC_CLOSE = 1 << 5;
+
+ struct MotifWMHints
+ {
+ uint32_t flags;
+ uint32_t functions;
+ uint32_t decorations;
+ int32_t inputMode;
+ uint32_t state;
+ };
+
+ MotifWMHints hints;
+ hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
+ hints.decorations = 0;
+ hints.functions = 0;
+ hints.inputMode = 0;
+ hints.state = 0;
+
+ if (style & Style::Titlebar)
+ {
+ hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU;
+ hints.functions |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE;
+ }
+ if (style & Style::Resize)
+ {
+ hints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH;
+ hints.functions |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
+ }
+ if (style & Style::Close)
+ {
+ hints.decorations |= 0;
+ hints.functions |= MWM_FUNC_CLOSE;
+ }
+
+ if (!changeWindowProperty(hintsAtomReply->atom, hintsAtomReply->atom, 32, 5, &hints))
+ err() << "xcb_change_property failed, could not set window hints" << std::endl;
+ }
+ else
+ {
+ err() << "Failed to request _MOTIF_WM_HINTS atom." << std::endl;
+ }
+}
+
+////////////////////////////////////////////////////////////
+void WindowImplX11::setWMHints(const WMHints& hints)
+{
+ if (!changeWindowProperty(XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 32, sizeof(hints) / 4, &hints))
+ sf::err() << "Failed to set WM_HINTS property" << std::endl;
+}
+
+
+////////////////////////////////////////////////////////////
+void WindowImplX11::setWMSizeHints(const WMSizeHints& hints)
+{
+ if (!changeWindowProperty(XCB_ATOM_WM_NORMAL_HINTS, XCB_ATOM_WM_SIZE_HINTS, 32, sizeof(hints) / 4, &hints))
+ sf::err() << "Failed to set XCB_ATOM_WM_NORMAL_HINTS property" << std::endl;
+}
+
+
+////////////////////////////////////////////////////////////
+bool WindowImplX11::changeWindowProperty(xcb_atom_t property, xcb_atom_t type, uint8_t format, uint32_t length, const void* data)
+{
+ ScopedXcbPtr<xcb_generic_error_t> error(xcb_request_check(
+ m_connection,
+ xcb_change_property_checked(
+ m_connection,
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ property,
+ type,
+ format,
+ length,
+ data
+ )
+ ));
+
+ return !error;
+}
+
+
+////////////////////////////////////////////////////////////
+void WindowImplX11::initialize()
+{
// Create the input context
m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL);
+
if (m_inputMethod)
{
- m_inputContext = XCreateIC(m_inputMethod,
- XNClientWindow, m_window,
- XNFocusWindow, m_window,
- XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
- (void*)NULL);
+ m_inputContext = XCreateIC(
+ m_inputMethod,
+ XNClientWindow,
+ m_window,
+ XNFocusWindow,
+ m_window,
+ XNInputStyle,
+ XIMPreeditNothing | XIMStatusNothing,
+ reinterpret_cast<void*>(NULL)
+ );
}
else
{
m_inputContext = NULL;
}
+
if (!m_inputContext)
err() << "Failed to create input context for window -- TextEntered event won't be able to return unicode" << std::endl;
// Show the window
- XMapWindow(m_display, m_window);
- XFlush(m_display);
+ setVisible(true);
+
+ // Raise the window and grab input focus
+ grabFocus();
// Create the hidden cursor
createHiddenCursor();
// Flush the commands queue
- XFlush(m_display);
-
+ xcb_flush(m_connection);
+
// Add this window to the global list of windows (required for focus request)
+ Lock lock(allWindowsMutex);
allWindows.push_back(this);
}
@@ -643,20 +1776,58 @@ void WindowImplX11::initialize()
////////////////////////////////////////////////////////////
void WindowImplX11::createHiddenCursor()
{
+ xcb_pixmap_t cursorPixmap = xcb_generate_id(m_connection);
+
// Create the cursor's pixmap (1x1 pixels)
- Pixmap cursorPixmap = XCreatePixmap(m_display, m_window, 1, 1, 1);
- GC graphicsContext = XCreateGC(m_display, cursorPixmap, 0, NULL);
- XDrawPoint(m_display, cursorPixmap, graphicsContext, 0, 0);
- XFreeGC(m_display, graphicsContext);
+ ScopedXcbPtr<xcb_generic_error_t> createPixmapError(xcb_request_check(
+ m_connection,
+ xcb_create_pixmap(
+ m_connection,
+ 1,
+ cursorPixmap,
+ m_window,
+ 1,
+ 1
+ )
+ ));
+
+ if (createPixmapError)
+ {
+ err() << "Failed to create pixmap for hidden cursor" << std::endl;
+ return;
+ }
+
+ m_hiddenCursor = xcb_generate_id(m_connection);
// Create the cursor, using the pixmap as both the shape and the mask of the cursor
- XColor color;
- color.flags = DoRed | DoGreen | DoBlue;
- color.red = color.blue = color.green = 0;
- m_hiddenCursor = XCreatePixmapCursor(m_display, cursorPixmap, cursorPixmap, &color, &color, 0, 0);
+ ScopedXcbPtr<xcb_generic_error_t> createCursorError(xcb_request_check(
+ m_connection,
+ xcb_create_cursor(
+ m_connection,
+ m_hiddenCursor,
+ cursorPixmap,
+ cursorPixmap,
+ 0, 0, 0, // Foreground RGB color
+ 0, 0, 0, // Background RGB color
+ 0, // X
+ 0 // Y
+ )
+ ));
+
+ if (createCursorError)
+ err() << "Failed to create hidden cursor" << std::endl;
// We don't need the pixmap any longer, free it
- XFreePixmap(m_display, cursorPixmap);
+ ScopedXcbPtr<xcb_generic_error_t> freePixmapError(xcb_request_check(
+ m_connection,
+ xcb_free_pixmap(
+ m_connection,
+ cursorPixmap
+ )
+ ));
+
+ if (freePixmapError)
+ err() << "Failed to free pixmap for hidden cursor" << std::endl;
}
@@ -664,26 +1835,7 @@ void WindowImplX11::createHiddenCursor()
void WindowImplX11::cleanup()
{
// Restore the previous video mode (in case we were running in fullscreen)
- if (fullscreenWindow == this)
- {
- // Get current screen info
- XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen));
- if (config)
- {
- // Get the current rotation
- Rotation currentRotation;
- XRRConfigCurrentConfiguration(config, &currentRotation);
-
- // Reset the video mode
- XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), m_oldVideoMode, currentRotation, CurrentTime);
-
- // Free the configuration instance
- XRRFreeScreenConfigInfo(config);
- }
-
- // Reset the fullscreen window
- fullscreenWindow = NULL;
- }
+ resetVideoMode();
// Unhide the mouse cursor (in case it was hidden)
setMouseCursorVisible(true);
@@ -691,57 +1843,28 @@ void WindowImplX11::cleanup()
////////////////////////////////////////////////////////////
-bool WindowImplX11::processEvent(XEvent windowEvent)
+bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
{
- // This function implements a workaround to properly discard
- // repeated key events when necessary. The problem is that the
- // system's key events policy doesn't match SFML's one: X server will generate
- // both repeated KeyPress and KeyRelease events when maintaining a key down, while
- // SFML only wants repeated KeyPress events. Thus, we have to:
- // - Discard duplicated KeyRelease events when KeyRepeatEnabled is true
- // - Discard both duplicated KeyPress and KeyRelease events when KeyRepeatEnabled is false
-
- // Detect repeated key events
- // (code shamelessly taken from SDL)
- if (windowEvent.type == KeyRelease)
- {
- // Check if there's a matching KeyPress event in the queue
- XEvent nextEvent;
- if (XPending(m_display))
- {
- // Grab it but don't remove it from the queue, it still needs to be processed :)
- XPeekEvent(m_display, &nextEvent);
- if (nextEvent.type == KeyPress)
- {
- // Check if it is a duplicated event (same timestamp as the KeyRelease event)
- if ((nextEvent.xkey.keycode == windowEvent.xkey.keycode) &&
- (nextEvent.xkey.time - windowEvent.xkey.time < 2))
- {
- // If we don't want repeated events, remove the next KeyPress from the queue
- if (!m_keyRepeat)
- XNextEvent(m_display, &nextEvent);
-
- // This KeyRelease is a repeated event and we don't want it
- return false;
- }
- }
- }
- }
-
// Convert the X11 event to a sf::Event
- switch (windowEvent.type)
+ switch (windowEvent->response_type & ~0x80)
{
// Destroy event
- case DestroyNotify:
+ case XCB_DESTROY_NOTIFY:
{
+ if (passEvent(windowEvent, reinterpret_cast<xcb_destroy_notify_event_t*>(windowEvent)->window))
+ return false;
+
// The window is about to be destroyed: we must cleanup resources
cleanup();
break;
}
// Gain focus event
- case FocusIn:
+ case XCB_FOCUS_IN:
{
+ if (passEvent(windowEvent, reinterpret_cast<xcb_focus_in_event_t*>(windowEvent)->event))
+ return false;
+
// Update the input context
if (m_inputContext)
XSetICFocus(m_inputContext);
@@ -751,20 +1874,42 @@ bool WindowImplX11::processEvent(XEvent windowEvent)
pushEvent(event);
// If the window has been previously marked urgent (notification) as a result of a focus request, undo that
- XWMHints* hints = XGetWMHints(m_display, m_window);
- if (hints != NULL)
+ ScopedXcbPtr<xcb_generic_error_t> error(NULL);
+
+ ScopedXcbPtr<xcb_get_property_reply_t> hintsReply(xcb_get_property_reply(
+ m_connection,
+ xcb_get_property(m_connection, 0, m_window, XCB_ATOM_WM_HINTS, XCB_ATOM_WM_HINTS, 0, 9),
+ &error
+ ));
+
+ if (error || !hintsReply)
+ {
+ err() << "Failed to get WM hints in XCB_FOCUS_IN" << std::endl;
+ break;
+ }
+
+ WMHints* hints = reinterpret_cast<WMHints*>(xcb_get_property_value(hintsReply.get()));
+
+ if (!hints)
{
- // Remove urgency (notification) flag from hints
- hints->flags &= ~XUrgencyHint;
- XSetWMHints(m_display, m_window, hints);
- XFree(hints);
+ err() << "Failed to get WM hints in XCB_FOCUS_IN" << std::endl;
+ break;
}
+
+ // Remove urgency (notification) flag from hints
+ hints->flags &= ~(1 << 8);
+
+ setWMHints(*hints);
+
break;
}
// Lost focus event
- case FocusOut:
+ case XCB_FOCUS_OUT:
{
+ if (passEvent(windowEvent, reinterpret_cast<xcb_focus_out_event_t*>(windowEvent)->event))
+ return false;
+
// Update the input context
if (m_inputContext)
XUnsetICFocus(m_inputContext);
@@ -776,64 +1921,110 @@ bool WindowImplX11::processEvent(XEvent windowEvent)
}
// Resize event
- case ConfigureNotify:
+ case XCB_CONFIGURE_NOTIFY:
{
- // ConfigureNotify can be triggered for other reasons, check if the size has actually changed
- if ((windowEvent.xconfigure.width != m_previousSize.x) || (windowEvent.xconfigure.height != m_previousSize.y))
- {
- Event event;
- event.type = Event::Resized;
- event.size.width = windowEvent.xconfigure.width;
- event.size.height = windowEvent.xconfigure.height;
- pushEvent(event);
+ if (passEvent(windowEvent, reinterpret_cast<xcb_configure_notify_event_t*>(windowEvent)->window))
+ return false;
- m_previousSize.x = windowEvent.xconfigure.width;
- m_previousSize.y = windowEvent.xconfigure.height;
- }
+ xcb_configure_notify_event_t* e = reinterpret_cast<xcb_configure_notify_event_t*>(windowEvent);
+ Event event;
+ event.type = Event::Resized;
+ event.size.width = e->width;
+ event.size.height = e->height;
+ pushEvent(event);
break;
}
// Close event
- case ClientMessage:
+ case XCB_CLIENT_MESSAGE:
{
- if ((windowEvent.xclient.format == 32) && (windowEvent.xclient.data.l[0]) == static_cast<long>(m_atomClose))
+ if (passEvent(windowEvent, reinterpret_cast<xcb_client_message_event_t*>(windowEvent)->window))
+ return false;
+
+ xcb_client_message_event_t* e = reinterpret_cast<xcb_client_message_event_t*>(windowEvent);
+
+ static xcb_atom_t wmProtocols = getAtom("WM_PROTOCOLS");
+
+ // Handle window manager protocol messages we support
+ if (e->type == wmProtocols)
{
- Event event;
- event.type = Event::Closed;
- pushEvent(event);
+ static xcb_atom_t wmDeleteWindow = getAtom("WM_DELETE_WINDOW");
+ static xcb_atom_t netWmPing = ewmhSupported() ? getAtom("_NET_WM_PING", true) : XCB_ATOM_NONE;
+
+ if ((e->format == 32) && (e->data.data32[0]) == static_cast<long>(wmDeleteWindow))
+ {
+ // Handle the WM_DELETE_WINDOW message
+ Event event;
+ event.type = Event::Closed;
+ pushEvent(event);
+ }
+ else if (netWmPing && (e->format == 32) && (e->data.data32[0]) == static_cast<long>(netWmPing))
+ {
+ // Handle the _NET_WM_PING message, send pong back to WM to show that we are responsive
+ e->window = XCBDefaultRootWindow(m_connection);
+
+ ScopedXcbPtr<xcb_generic_error_t> pongError(xcb_request_check(
+ m_connection,
+ xcb_send_event_checked(
+ m_connection,
+ 0,
+ XCBDefaultRootWindow(m_connection),
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ reinterpret_cast<char*>(e)
+ )
+ ));
+
+ if (pongError)
+ err() << "Could not send pong event back to WM" << std::endl;
+ }
}
break;
}
// Key down event
- case KeyPress:
+ case XCB_KEY_PRESS:
{
- // Get the keysym of the key that has been pressed
- static XComposeStatus keyboard;
- char buffer[32];
- KeySym symbol;
- XLookupString(&windowEvent.xkey, buffer, sizeof(buffer), &symbol, &keyboard);
+ if (passEvent(windowEvent, reinterpret_cast<xcb_key_press_event_t*>(windowEvent)->event))
+ return false;
+
+ xcb_key_press_event_t* e = reinterpret_cast<xcb_key_press_event_t*>(windowEvent);
// Fill the event parameters
// TODO: if modifiers are wrong, use XGetModifierMapping to retrieve the actual modifiers mapping
Event event;
event.type = Event::KeyPressed;
- event.key.code = keysymToSF(symbol);
- event.key.alt = windowEvent.xkey.state & Mod1Mask;
- event.key.control = windowEvent.xkey.state & ControlMask;
- event.key.shift = windowEvent.xkey.state & ShiftMask;
- event.key.system = windowEvent.xkey.state & Mod4Mask;
+ event.key.code = keycodeToSF(e->detail);
+ event.key.alt = e->state & XCB_MOD_MASK_1;
+ event.key.control = e->state & XCB_MOD_MASK_CONTROL;
+ event.key.shift = e->state & XCB_MOD_MASK_SHIFT;
+ event.key.system = e->state & XCB_MOD_MASK_4;
pushEvent(event);
+ XEvent fakeEvent;
+ fakeEvent.type = KeyPress;
+ fakeEvent.xany.display = m_display;
+ fakeEvent.xany.window = e->event;
+ fakeEvent.xkey.state = e->state;
+ fakeEvent.xkey.keycode = e->detail;
+
// Generate a TextEntered event
- if (!XFilterEvent(&windowEvent, None))
+ if (!XFilterEvent(&fakeEvent, None))
{
#ifdef X_HAVE_UTF8_STRING
if (m_inputContext)
{
Status status;
Uint8 keyBuffer[16];
- int length = Xutf8LookupString(m_inputContext, &windowEvent.xkey, reinterpret_cast<char*>(keyBuffer), sizeof(keyBuffer), NULL, &status);
+
+ int length = Xutf8LookupString(
+ m_inputContext,
+ &fakeEvent.xkey,
+ reinterpret_cast<char*>(keyBuffer),
+ sizeof(keyBuffer),
+ NULL,
+ &status
+ );
+
if (length > 0)
{
Uint32 unicode = 0;
@@ -852,7 +2043,7 @@ bool WindowImplX11::processEvent(XEvent windowEvent)
{
static XComposeStatus status;
char keyBuffer[16];
- if (XLookupString(&windowEvent.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status))
+ if (XLookupString(&fakeEvent.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status))
{
Event textEvent;
textEvent.type = Event::TextEntered;
@@ -866,43 +2057,54 @@ bool WindowImplX11::processEvent(XEvent windowEvent)
}
// Key up event
- case KeyRelease:
+ case XCB_KEY_RELEASE:
{
- // Get the keysym of the key that has been pressed
- char buffer[32];
- KeySym symbol;
- XLookupString(&windowEvent.xkey, buffer, 32, &symbol, NULL);
+ if (passEvent(windowEvent, reinterpret_cast<xcb_key_release_event_t*>(windowEvent)->event))
+ return false;
+
+ xcb_key_release_event_t* e = reinterpret_cast<xcb_key_release_event_t*>(windowEvent);
// Fill the event parameters
Event event;
event.type = Event::KeyReleased;
- event.key.code = keysymToSF(symbol);
- event.key.alt = windowEvent.xkey.state & Mod1Mask;
- event.key.control = windowEvent.xkey.state & ControlMask;
- event.key.shift = windowEvent.xkey.state & ShiftMask;
- event.key.system = windowEvent.xkey.state & Mod4Mask;
+ event.key.code = keycodeToSF(e->detail);
+ event.key.alt = e->state & XCB_MOD_MASK_1;
+ event.key.control = e->state & XCB_MOD_MASK_CONTROL;
+ event.key.shift = e->state & XCB_MOD_MASK_SHIFT;
+ event.key.system = e->state & XCB_MOD_MASK_4;
pushEvent(event);
break;
}
// Mouse button pressed
- case ButtonPress:
+ case XCB_BUTTON_PRESS:
{
- unsigned int button = windowEvent.xbutton.button;
- if ((button == Button1) || (button == Button2) || (button == Button3) || (button == 8) || (button == 9))
+ if (passEvent(windowEvent, reinterpret_cast<xcb_button_press_event_t*>(windowEvent)->event))
+ return false;
+
+ xcb_button_press_event_t* e = reinterpret_cast<xcb_button_press_event_t*>(windowEvent);
+
+ // XXX: Why button 8 and 9?
+ // Because 4 and 5 are the vertical wheel and 6 and 7 are horizontal wheel ;)
+ xcb_button_t button = e->detail;
+ if ((button == XCB_BUTTON_INDEX_1) ||
+ (button == XCB_BUTTON_INDEX_2) ||
+ (button == XCB_BUTTON_INDEX_3) ||
+ (button == 8) ||
+ (button == 9))
{
Event event;
event.type = Event::MouseButtonPressed;
- event.mouseButton.x = windowEvent.xbutton.x;
- event.mouseButton.y = windowEvent.xbutton.y;
- switch (button)
+ event.mouseButton.x = e->event_x;
+ event.mouseButton.y = e->event_y;
+ switch(button)
{
- case Button1: event.mouseButton.button = Mouse::Left; break;
- case Button2: event.mouseButton.button = Mouse::Middle; break;
- case Button3: event.mouseButton.button = Mouse::Right; break;
- case 8: event.mouseButton.button = Mouse::XButton1; break;
- case 9: event.mouseButton.button = Mouse::XButton2; break;
+ case XCB_BUTTON_INDEX_1: event.mouseButton.button = Mouse::Left; break;
+ case XCB_BUTTON_INDEX_2: event.mouseButton.button = Mouse::Middle; break;
+ case XCB_BUTTON_INDEX_3: event.mouseButton.button = Mouse::Right; break;
+ case 8: event.mouseButton.button = Mouse::XButton1; break;
+ case 9: event.mouseButton.button = Mouse::XButton2; break;
}
pushEvent(event);
}
@@ -910,52 +2112,88 @@ bool WindowImplX11::processEvent(XEvent windowEvent)
}
// Mouse button released
- case ButtonRelease:
+ case XCB_BUTTON_RELEASE:
{
- unsigned int button = windowEvent.xbutton.button;
- if ((button == Button1) || (button == Button2) || (button == Button3) || (button == 8) || (button == 9))
+ if (passEvent(windowEvent, reinterpret_cast<xcb_button_release_event_t*>(windowEvent)->event))
+ return false;
+
+ xcb_button_release_event_t* e = reinterpret_cast<xcb_button_press_event_t*>(windowEvent);
+
+ xcb_button_t button = e->detail;
+ if ((button == XCB_BUTTON_INDEX_1) ||
+ (button == XCB_BUTTON_INDEX_2) ||
+ (button == XCB_BUTTON_INDEX_3) ||
+ (button == 8) ||
+ (button == 9))
{
Event event;
event.type = Event::MouseButtonReleased;
- event.mouseButton.x = windowEvent.xbutton.x;
- event.mouseButton.y = windowEvent.xbutton.y;
- switch (button)
+ event.mouseButton.x = e->event_x;
+ event.mouseButton.y = e->event_y;
+ switch(button)
{
- case Button1: event.mouseButton.button = Mouse::Left; break;
- case Button2: event.mouseButton.button = Mouse::Middle; break;
- case Button3: event.mouseButton.button = Mouse::Right; break;
- case 8: event.mouseButton.button = Mouse::XButton1; break;
- case 9: event.mouseButton.button = Mouse::XButton2; break;
+ case XCB_BUTTON_INDEX_1: event.mouseButton.button = Mouse::Left; break;
+ case XCB_BUTTON_INDEX_2: event.mouseButton.button = Mouse::Middle; break;
+ case XCB_BUTTON_INDEX_3: event.mouseButton.button = Mouse::Right; break;
+ case 8: event.mouseButton.button = Mouse::XButton1; break;
+ case 9: event.mouseButton.button = Mouse::XButton2; break;
}
pushEvent(event);
}
- else if ((button == Button4) || (button == Button5))
+ else if ((button == XCB_BUTTON_INDEX_4) || (button == XCB_BUTTON_INDEX_5))
{
Event event;
+
event.type = Event::MouseWheelMoved;
- event.mouseWheel.delta = windowEvent.xbutton.button == Button4 ? 1 : -1;
- event.mouseWheel.x = windowEvent.xbutton.x;
- event.mouseWheel.y = windowEvent.xbutton.y;
+ event.mouseWheel.delta = (button == XCB_BUTTON_INDEX_4) ? 1 : -1;
+ event.mouseWheel.x = e->event_x;
+ event.mouseWheel.y = e->event_y;
+ pushEvent(event);
+
+ event.type = Event::MouseWheelScrolled;
+ event.mouseWheelScroll.wheel = Mouse::VerticalWheel;
+ event.mouseWheelScroll.delta = (button == XCB_BUTTON_INDEX_4) ? 1 : -1;
+ event.mouseWheelScroll.x = e->event_x;
+ event.mouseWheelScroll.y = e->event_y;
+ pushEvent(event);
+ }
+ else if ((button == 6) || (button == 7))
+ {
+ Event event;
+ event.type = Event::MouseWheelScrolled;
+ event.mouseWheelScroll.wheel = Mouse::HorizontalWheel;
+ event.mouseWheelScroll.delta = (button == 6) ? 1 : -1;
+ event.mouseWheelScroll.x = e->event_x;
+ event.mouseWheelScroll.y = e->event_y;
pushEvent(event);
}
break;
}
// Mouse moved
- case MotionNotify:
+ case XCB_MOTION_NOTIFY:
{
+ if (passEvent(windowEvent, reinterpret_cast<xcb_motion_notify_event_t*>(windowEvent)->event))
+ return false;
+
+ xcb_motion_notify_event_t* e = reinterpret_cast<xcb_motion_notify_event_t*>(windowEvent);
Event event;
event.type = Event::MouseMoved;
- event.mouseMove.x = windowEvent.xmotion.x;
- event.mouseMove.y = windowEvent.xmotion.y;
+ event.mouseMove.x = e->event_x;
+ event.mouseMove.y = e->event_y;
pushEvent(event);
break;
}
// Mouse entered
- case EnterNotify:
+ case XCB_ENTER_NOTIFY:
{
- if (windowEvent.xcrossing.mode == NotifyNormal)
+ if (passEvent(windowEvent, reinterpret_cast<xcb_enter_notify_event_t*>(windowEvent)->event))
+ return false;
+
+ xcb_enter_notify_event_t* enterNotifyEvent = reinterpret_cast<xcb_enter_notify_event_t*>(windowEvent);
+
+ if (enterNotifyEvent->mode == NotifyNormal)
{
Event event;
event.type = Event::MouseEntered;
@@ -965,9 +2203,14 @@ bool WindowImplX11::processEvent(XEvent windowEvent)
}
// Mouse left
- case LeaveNotify:
+ case XCB_LEAVE_NOTIFY:
{
- if (windowEvent.xcrossing.mode == NotifyNormal)
+ if (passEvent(windowEvent, reinterpret_cast<xcb_leave_notify_event_t*>(windowEvent)->event))
+ return false;
+
+ xcb_leave_notify_event_t* leaveNotifyEvent = reinterpret_cast<xcb_leave_notify_event_t*>(windowEvent);
+
+ if (leaveNotifyEvent->mode == NotifyNormal)
{
Event event;
event.type = Event::MouseLeft;
@@ -977,9 +2220,91 @@ bool WindowImplX11::processEvent(XEvent windowEvent)
}
// Parent window changed
- case ReparentNotify:
+ case XCB_REPARENT_NOTIFY:
+ {
+ if (passEvent(windowEvent, reinterpret_cast<xcb_reparent_notify_event_t*>(windowEvent)->window))
+ return false;
+
+ // Catch reparent events to properly apply fullscreen on
+ // some "strange" window managers (like Awesome) which
+ // seem to make use of temporary parents during mapping
+ if (m_fullscreen)
+ switchToFullscreen();
+
+ xcb_flush(m_connection); // Discard remaining events
+ break;
+ }
+
+ // The stuff that might pop up but we don't care about
+ // Whitelist more when necessary
+ // Window visibility changed (hide/unhide)
+ case XCB_MAP_NOTIFY:
+ case XCB_UNMAP_NOTIFY:
{
- XSync(m_display, True); // Discard remaining events
+ break;
+ }
+
+ // Handle the rest
+ default:
+ {
+ uint8_t responseType = windowEvent->response_type & ~0x80;
+
+ // Handle any extension events first
+
+ // DRI2
+ static xcb_query_extension_reply_t driExtension = getDriExtension();
+ if (driExtension.present)
+ {
+ // Because we are using the XCB event queue instead of the Xlib event
+ // queue, Mesa breaks a bit (VSync among other things) because DRI2 still
+ // expects certain Xlib events to come its way. We work around this by
+ // emulating the old Xlib event queue for these specific wire events.
+ // Sources (retrieved 27 Mar 2015):
+ // http://wrl.illest.net/post/45342765813/code-tip-glx-and-xcbownseventqueue
+ // https://bugs.freedesktop.org/show_bug.cgi?id=42131
+ // https://bugs.freedesktop.org/show_bug.cgi?id=35945
+ // Mesa src/glx/dri2.c
+ // QtBase src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
+ // QtBase Commit bb22b4965070409df4658f16fdf549f0362e8a9c
+ // Qt Change-Id I3b4ef3f6e3efbae25f49f161e229e9b15e951778
+ // QtBase Commit 13c5c81cfa934c9b610720fe79e07465b00ebc8d
+ // Qt Change-Id Ia93eb8be1cbbc3d8ae7913a934c195af6b5ec538
+ // QtBase Commit decb88693c8a7f0c073889b91151f01a850e3adf
+ // Qt Change-Id Ic466ff26487937b03f072a57e0ee4df335492a5f
+ if ((responseType == driExtension.first_event + XCB_DRI2_BUFFER_SWAP_COMPLETE) ||
+ (responseType == driExtension.first_event + XCB_DRI2_INVALIDATE_BUFFERS))
+ {
+ // DRI2 BufferSwapComplete and InvalidateBuffers
+
+ // We lock/unlock the display to protect against concurrent access
+ XLockDisplay(m_display);
+
+ typedef Bool (*wireEventHandler)(Display*, XEvent*, xEvent*);
+
+ // Probe for any handlers that are registered for this event type
+ wireEventHandler handler = XESetWireToEvent(m_display, responseType, 0);
+
+ if (handler)
+ {
+ // Restore the previous handler if one was registered
+ XESetWireToEvent(m_display, responseType, handler);
+
+ XEvent event;
+ windowEvent->sequence = LastKnownRequestProcessed(m_display);
+
+ // Pretend to be the Xlib event queue
+ handler(m_display, &event, reinterpret_cast<xEvent*>(windowEvent));
+ }
+
+ XUnlockDisplay(m_display);
+
+ return true;
+ }
+ }
+
+ // Print any surprises to stderr (would be nice if people report when this happens)
+ dumpUnhandledEvent(responseType);
+
break;
}
}
@@ -989,119 +2314,29 @@ bool WindowImplX11::processEvent(XEvent windowEvent)
////////////////////////////////////////////////////////////
-Keyboard::Key WindowImplX11::keysymToSF(KeySym symbol)
+bool WindowImplX11::passEvent(xcb_generic_event_t* windowEvent, xcb_window_t window)
{
- // First convert to uppercase (to avoid dealing with two different keysyms for the same key)
- KeySym lower, key;
- XConvertCase(symbol, &lower, &key);
+ // Check if this is our event
+ if (window == m_window)
+ return false;
+
+ Lock lock(allWindowsMutex);
- switch (key)
+ // If we are the only window, there is nobody else to pass to
+ if (allWindows.size() == 1)
+ return false;
+
+ for (std::vector<WindowImplX11*>::iterator i = allWindows.begin(); i != allWindows.end(); ++i)
{
- case XK_Shift_L: return Keyboard::LShift;
- case XK_Shift_R: return Keyboard::RShift;
- case XK_Control_L: return Keyboard::LControl;
- case XK_Control_R: return Keyboard::RControl;
- case XK_Alt_L: return Keyboard::LAlt;
- case XK_Alt_R: return Keyboard::RAlt;
- case XK_Super_L: return Keyboard::LSystem;
- case XK_Super_R: return Keyboard::RSystem;
- case XK_Menu: return Keyboard::Menu;
- case XK_Escape: return Keyboard::Escape;
- case XK_semicolon: return Keyboard::SemiColon;
- case XK_slash: return Keyboard::Slash;
- case XK_equal: return Keyboard::Equal;
- case XK_minus: return Keyboard::Dash;
- case XK_bracketleft: return Keyboard::LBracket;
- case XK_bracketright: return Keyboard::RBracket;
- case XK_comma: return Keyboard::Comma;
- case XK_period: return Keyboard::Period;
- case XK_dead_acute: return Keyboard::Quote;
- case XK_backslash: return Keyboard::BackSlash;
- case XK_dead_grave: return Keyboard::Tilde;
- case XK_space: return Keyboard::Space;
- case XK_Return: return Keyboard::Return;
- case XK_KP_Enter: return Keyboard::Return;
- case XK_BackSpace: return Keyboard::BackSpace;
- case XK_Tab: return Keyboard::Tab;
- case XK_Prior: return Keyboard::PageUp;
- case XK_Next: return Keyboard::PageDown;
- case XK_End: return Keyboard::End;
- case XK_Home: return Keyboard::Home;
- case XK_Insert: return Keyboard::Insert;
- case XK_Delete: return Keyboard::Delete;
- case XK_KP_Add: return Keyboard::Add;
- case XK_KP_Subtract: return Keyboard::Subtract;
- case XK_KP_Multiply: return Keyboard::Multiply;
- case XK_KP_Divide: return Keyboard::Divide;
- case XK_Pause: return Keyboard::Pause;
- case XK_F1: return Keyboard::F1;
- case XK_F2: return Keyboard::F2;
- case XK_F3: return Keyboard::F3;
- case XK_F4: return Keyboard::F4;
- case XK_F5: return Keyboard::F5;
- case XK_F6: return Keyboard::F6;
- case XK_F7: return Keyboard::F7;
- case XK_F8: return Keyboard::F8;
- case XK_F9: return Keyboard::F9;
- case XK_F10: return Keyboard::F10;
- case XK_F11: return Keyboard::F11;
- case XK_F12: return Keyboard::F12;
- case XK_F13: return Keyboard::F13;
- case XK_F14: return Keyboard::F14;
- case XK_F15: return Keyboard::F15;
- case XK_Left: return Keyboard::Left;
- case XK_Right: return Keyboard::Right;
- case XK_Up: return Keyboard::Up;
- case XK_Down: return Keyboard::Down;
- case XK_KP_0: return Keyboard::Numpad0;
- case XK_KP_1: return Keyboard::Numpad1;
- case XK_KP_2: return Keyboard::Numpad2;
- case XK_KP_3: return Keyboard::Numpad3;
- case XK_KP_4: return Keyboard::Numpad4;
- case XK_KP_5: return Keyboard::Numpad5;
- case XK_KP_6: return Keyboard::Numpad6;
- case XK_KP_7: return Keyboard::Numpad7;
- case XK_KP_8: return Keyboard::Numpad8;
- case XK_KP_9: return Keyboard::Numpad9;
- case XK_A: return Keyboard::A;
- case XK_Z: return Keyboard::Z;
- case XK_E: return Keyboard::E;
- case XK_R: return Keyboard::R;
- case XK_T: return Keyboard::T;
- case XK_Y: return Keyboard::Y;
- case XK_U: return Keyboard::U;
- case XK_I: return Keyboard::I;
- case XK_O: return Keyboard::O;
- case XK_P: return Keyboard::P;
- case XK_Q: return Keyboard::Q;
- case XK_S: return Keyboard::S;
- case XK_D: return Keyboard::D;
- case XK_F: return Keyboard::F;
- case XK_G: return Keyboard::G;
- case XK_H: return Keyboard::H;
- case XK_J: return Keyboard::J;
- case XK_K: return Keyboard::K;
- case XK_L: return Keyboard::L;
- case XK_M: return Keyboard::M;
- case XK_W: return Keyboard::W;
- case XK_X: return Keyboard::X;
- case XK_C: return Keyboard::C;
- case XK_V: return Keyboard::V;
- case XK_B: return Keyboard::B;
- case XK_N: return Keyboard::N;
- case XK_0: return Keyboard::Num0;
- case XK_1: return Keyboard::Num1;
- case XK_2: return Keyboard::Num2;
- case XK_3: return Keyboard::Num3;
- case XK_4: return Keyboard::Num4;
- case XK_5: return Keyboard::Num5;
- case XK_6: return Keyboard::Num6;
- case XK_7: return Keyboard::Num7;
- case XK_8: return Keyboard::Num8;
- case XK_9: return Keyboard::Num9;
+ if ((*i)->m_window == window)
+ {
+ (*i)->m_xcbEvents.push_back(windowEvent);
+ return true;
+ }
}
- return Keyboard::Unknown;
+ // It seems nobody wants the event, we'll just handle it ourselves
+ return false;
}
} // namespace priv
diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp
index d824492..2e31fbf 100644
--- a/src/SFML/Window/Unix/WindowImplX11.hpp
+++ b/src/SFML/Window/Unix/WindowImplX11.hpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
-// Copyright (C) 2007-2014 Laurent Gomila (laurent.gom@gmail.com)
+// Copyright (C) 2007-2015 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
@@ -31,8 +31,9 @@
#include <SFML/Window/Event.hpp>
#include <SFML/Window/WindowImpl.hpp>
#include <SFML/System/String.hpp>
-#include <X11/Xlib.h>
-#include <set>
+#include <X11/Xlib-xcb.h>
+#include <xcb/randr.h>
+#include <deque>
namespace sf
@@ -179,13 +180,100 @@ protected:
private:
+ struct WMHints
+ {
+ int32_t flags;
+ uint32_t input;
+ int32_t initial_state;
+ xcb_pixmap_t icon_pixmap;
+ xcb_window_t icon_window;
+ int32_t icon_x;
+ int32_t icon_y;
+ xcb_pixmap_t icon_mask;
+ xcb_window_t window_group;
+ };
+
+ struct WMSizeHints
+ {
+ uint32_t flags;
+ int32_t x, y;
+ int32_t width, height;
+ int32_t min_width, min_height;
+ int32_t max_width, max_height;
+ int32_t width_inc, height_inc;
+ int32_t min_aspect_num, min_aspect_den;
+ int32_t max_aspect_num, max_aspect_den;
+ int32_t base_width, base_height;
+ uint32_t win_gravity;
+ };
+
////////////////////////////////////////////////////////////
- /// \brief Switch to fullscreen mode
+ /// \brief Request the WM to make the current window active
+ ///
+ ////////////////////////////////////////////////////////////
+ void grabFocus();
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Set fullscreen video mode
///
/// \param Mode video mode to switch to
///
////////////////////////////////////////////////////////////
- void switchToFullscreen(const VideoMode& mode);
+ void setVideoMode(const VideoMode& mode);
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Reset to desktop video mode
+ ///
+ ////////////////////////////////////////////////////////////
+ void resetVideoMode();
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Switch to fullscreen mode
+ ///
+ ////////////////////////////////////////////////////////////
+ void switchToFullscreen();
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Set the WM protocols we support
+ ///
+ ////////////////////////////////////////////////////////////
+ void setProtocols();
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Set Motif WM hints
+ ///
+ ////////////////////////////////////////////////////////////
+ void setMotifHints(unsigned long style);
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Set WM hints
+ ///
+ /// \param hints Hints
+ ///
+ ////////////////////////////////////////////////////////////
+ void setWMHints(const WMHints& hints);
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Set WM size hints
+ ///
+ /// \param hints Size hints
+ ///
+ ////////////////////////////////////////////////////////////
+ void setWMSizeHints(const WMSizeHints& hints);
+
+ ////////////////////////////////////////////////////////////
+ /// \brief Change a XCB window property
+ ///
+ /// \param property Property to change
+ /// \param type Type of the property
+ /// \param format Format of the property
+ /// \param length Length of the new value
+ /// \param data The new value of the property
+ ///
+ /// \return True if successful, false if unsuccessful
+ ///
+ ////////////////////////////////////////////////////////////
+ bool changeWindowProperty(xcb_atom_t property, xcb_atom_t type, uint8_t format, uint32_t length, const void* data);
////////////////////////////////////////////////////////////
/// \brief Do some common initializations after the window has been created
@@ -213,33 +301,36 @@ private:
/// \return True if the event was processed, false if it was discarded
///
////////////////////////////////////////////////////////////
- bool processEvent(XEvent windowEvent);
+ bool processEvent(xcb_generic_event_t* windowEvent);
////////////////////////////////////////////////////////////
- /// \brief Convert a X11 keysym to SFML key code
+ /// \brief Pass an incoming event to another window
///
- /// \param symbol Key symbol to convert
+ /// \param windowEvent Event which is being processed
+ /// \param window Window to pass to
///
- /// \return Corresponding SFML key code
+ /// \return True if the event was passed to another window, false if it is destined for the current window
///
////////////////////////////////////////////////////////////
- static Keyboard::Key keysymToSF(KeySym symbol);
+ bool passEvent(xcb_generic_event_t* windowEvent, xcb_window_t window);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
- ::Window m_window; ///< X11 structure defining our window
- ::Display* m_display; ///< Pointer to the display
- int m_screen; ///< Screen identifier
- XIM m_inputMethod; ///< Input method linked to the X display
- XIC m_inputContext; ///< Input context used to get Unicode input in our window
- bool m_isExternal; ///< Tell whether the window has been created externally or by SFML
- Atom m_atomClose; ///< Atom used to identify the close event
- int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen
- Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hiding, we must create a transparent one
- bool m_keyRepeat; ///< Is the KeyRepeat feature enabled?
- Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only)
- bool m_useSizeHints; ///< Is the size of the window fixed with size hints?
+ xcb_window_t m_window; ///< xcb identifier defining our window
+ ::Display* m_display; ///< Pointer to the display
+ xcb_connection_t* m_connection; ///< Pointer to the xcb connection
+ xcb_screen_t* m_screen; ///< Screen identifier
+ XIM m_inputMethod; ///< Input method linked to the X display
+ XIC m_inputContext; ///< Input context used to get unicode input in our window
+ std::deque<xcb_generic_event_t*> m_xcbEvents; ///< Events that were received in another window's loop
+ bool m_isExternal; ///< Tell whether the window has been created externally or by SFML
+ xcb_randr_get_screen_info_reply_t m_oldVideoMode; ///< Video mode in use before we switch to fullscreen
+ Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one
+ bool m_keyRepeat; ///< Is the KeyRepeat feature enabled?
+ Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only)
+ bool m_useSizeHints; ///< Is the size of the window fixed with size hints?
+ bool m_fullscreen; ///< Is window in fullscreen?
};
} // namespace priv