diff --git a/cmake/Modules/FindXCB.cmake b/cmake/Modules/FindXCB.cmake new file mode 100644 index 00000000..d6914610 --- /dev/null +++ b/cmake/Modules/FindXCB.cmake @@ -0,0 +1,97 @@ +# Try to find libxcb +# +# +# Once done this will define: +# LIBXCB_FOUND - True if xcb was found +# LIBXCB_INCLUDE_DIRS - Directories containing the headers +# LIBXCB_LIBRARIES - List of libraries to link to +# +# Also for each requested component: +# LIBXCB_${UPPER_COMPONENT_NAME}_FOUND +# LIBXCB_${UPPER_COMPONENT_NAME}_INCLUDE_DIRS +# LIBXCB_${UPPER_COMPONENT_NAME}_LIBRARIES + +include(FindPackageHandleStandardArgs) + +IF(NOT WIN32) + IF(LIBXCB_LIBRARIES AND LIBXCB_INCLUDE_DIR) + set(XCB_FIND_QUIETLY TRUE) + ENDIF() + + # Find xcb + FIND_PATH(LIBXCB_INCLUDE_DIR xcb/xcb.h) + FIND_LIBRARY(LIBXCB_LIBRARY NAMES xcb libxcb) + + # Add xcb info to LIBXCB_LIBRARIES and LIBXCB_INCLUDE_DIRS + SET(LIBXCB_LIBRARIES ${LIBXCB_LIBRARY}) + SET(LIBXCB_INCLUDE_DIRS ${LIBXCB_INCLUDE_DIR}) + + find_package_handle_standard_args(LIBXCB DEFAULT_MSG + LIBXCB_LIBRARY LIBXCB_INCLUDE_DIR) + + mark_as_advanced(LIBXCB_LIBRARY LIBXCB_INCLUDE_DIR) + + # Check whether we should search for XLIB_XCB + set(FIND_XLIB_XCB FALSE) + FOREACH(XCB_COMPONENT ${XCB_FIND_COMPONENTS}) + # Generate upper string of the component name + string(TOUPPER ${XCB_COMPONENT} XCB_COMPONENT_UPPER) + + IF(${XCB_COMPONENT_UPPER} MATCHES "XLIB_XCB") + set(FIND_XLIB_XCB TRUE) + ELSE() + # XCB_COMPONENTS is generated to be a copy of XCB_FIND_COMPONENTS + # without XLIB_XCB (for later component search) + set(XCB_COMPONENTS ${XCB_COMPONENTS} ${XCB_COMPONENT}) + ENDIF() + ENDFOREACH() + + # Find XLIB_XCB if requested + IF(FIND_XLIB_XCB) + FIND_PATH(XLIB_XCB_INCLUDE_DIR X11/Xlib-xcb.h) + FIND_LIBRARY(XLIB_XCB_LIBRARY NAMES X11-xcb libX11-xcb) + + SET(XLIB_XCB_LIBRARIES ${XLIB_XCB_LIBRARY}) + SET(XLIB_XCB_INCLUDE_DIRS ${XLIB_XCB_INCLUDE_DIR}) + + find_package_handle_standard_args(XLIB_XCB DEFAULT_MSG + XLIB_XCB_LIBRARY LIBXCB_INCLUDE_DIR) + + mark_as_advanced(XLIB_XCB_LIBRARY XLIB_XCB_INCLUDE_DIR) + + # Add xlib_xcb info to LIBXCB_LIBRARIES and LIBXCB_INCLUDE_DIRS + set(LIBXCB_LIBRARIES ${LIBXCB_LIBRARIES} ${XLIB_XCB_LIBRARIES}) + set(LIBXCB_INCLUDE_DIRS ${LIBXCB_INCLUDE_DIRS} ${XLIB_XCB_INCLUDE_DIR}) + + if(NOT XLIB_XCB_FOUND) + message(FATAL_ERROR "XlibXcb library not found") + endif() + ELSE() + # Add component name to the component list + set(${XCB_COMPONENTS} ${XCB_FIND_COMPONENTS}) + ENDIF() + + # Loop through requested xcb components (does not contain xlib_xcb) + FOREACH(XCB_COMPONENT ${XCB_COMPONENTS}) + # Generate lower and upper string of the component name + string(TOLOWER ${XCB_COMPONENT} XCB_COMPONENT_LOWER) + string(TOUPPER ${XCB_COMPONENT} XCB_COMPONENT_UPPER) + + # Find the specific component + FIND_LIBRARY(LIBXCB_${XCB_COMPONENT_UPPER}_LIBRARY + NAMES libxcb-${XCB_COMPONENT_LOWER} xcb-${XCB_COMPONENT_LOWER}) + + find_package_handle_standard_args(LIBXCB_${XCB_COMPONENT_UPPER} DEFAULT_MSG + LIBXCB_${XCB_COMPONENT_UPPER}_LIBRARY LIBXCB_INCLUDE_DIR) + + mark_as_advanced(LIBXCB_${XCB_COMPONENT_UPPER}_LIBRARY) + + # Append the component's library path to LIBXCB_LIBRARIES + set(LIBXCB_LIBRARIES ${LIBXCB_LIBRARIES} ${LIBXCB_${XCB_COMPONENT_UPPER}_LIBRARY}) + + if(NOT LIBXCB_${XCB_COMPONENT_UPPER}_FOUND) + message(FATAL_ERROR "xcb-${XCB_COMPONENT_LOWER} not found") + endif() + ENDFOREACH() + +endif() diff --git a/examples/X11/X11.cpp b/examples/X11/X11.cpp index 17bb4488..b8263a05 100644 --- a/examples/X11/X11.cpp +++ b/examples/X11/X11.cpp @@ -3,7 +3,8 @@ // Headers //////////////////////////////////////////////////////////// #include -#include +#include +#include #include #include #include @@ -55,9 +56,9 @@ void draw(sf::Window& window, float elapsedTime) glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.f, 0.f, -200.f); - glRotatef(elapsedTime * 0.05f, 1.f, 0.f, 0.f); - glRotatef(elapsedTime * 0.03f, 0.f, 1.f, 0.f); - glRotatef(elapsedTime * 0.09f, 0.f, 0.f, 1.f); + glRotatef(elapsedTime * 10.f, 1.f, 0.f, 0.f); + glRotatef(elapsedTime * 6.f, 0.f, 1.f, 0.f); + glRotatef(elapsedTime * 18.f, 0.f, 0.f, 1.f); // Draw a cube glBegin(GL_QUADS); @@ -115,85 +116,104 @@ int main() if (!display) return EXIT_FAILURE; - // Get the default screen - int screen = DefaultScreen(display); + // Get the XCB connection for the opened display. + xcb_connection_t* xcbConnection = XGetXCBConnection(display); - // Let's create the main window - XSetWindowAttributes attributes; - attributes.background_pixel = BlackPixel(display, screen); - attributes.event_mask = KeyPressMask; - Window window = XCreateWindow(display, RootWindow(display, screen), - 0, 0, 650, 330, 0, - DefaultDepth(display, screen), - InputOutput, - DefaultVisual(display, screen), - CWBackPixel | CWEventMask, &attributes); - if (!window) + if (!xcbConnection) + { + sf::err() << "Failed to get the XCB connection for opened display." << std::endl; return EXIT_FAILURE; + } - // Set the window's name - XStoreName(display, window , "SFML Window"); + // Get XCB screen. + const xcb_setup_t* xcbSetup = xcb_get_setup(xcbConnection); + xcb_screen_iterator_t xcbScreenIter = xcb_setup_roots_iterator(xcbSetup); + xcb_screen_t* screen = xcbScreenIter.data; - // Let's create the windows which will serve as containers for our SFML views - Window view1 = XCreateWindow(display, window, - 10, 10, 310, 310, 0, - DefaultDepth(display, screen), - InputOutput, - DefaultVisual(display, screen), - 0, NULL); - Window view2 = XCreateWindow(display, window, - 330, 10, 310, 310, 0, - DefaultDepth(display, screen), - InputOutput, - DefaultVisual(display, screen), - 0, NULL); + if (!screen) + { + sf::err() << "Failed to get the XCB screen." << std::endl; + return EXIT_FAILURE; + } - // Show our windows - XMapWindow(display, window); - XFlush(display); + // Generate the XCB window IDs. + xcb_window_t rootWindowId = xcb_generate_id(xcbConnection); + xcb_window_t view1WindowId = xcb_generate_id(xcbConnection); + xcb_window_t view2WindowId = xcb_generate_id(xcbConnection); + + // Create the root window with a black background. + uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; + uint32_t attributes[2] = {screen->black_pixel, XCB_EVENT_MASK_KEY_PRESS}; + + xcb_create_window(xcbConnection, + XCB_COPY_FROM_PARENT, + rootWindowId, + screen->root, + 0, 0, 650, 330, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + mask, attributes); + + // Create windows for the SFML views. + xcb_create_window(xcbConnection, + XCB_COPY_FROM_PARENT, + view1WindowId, + rootWindowId, + 10, 10, 310, 310, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + mask, attributes); + + xcb_create_window(xcbConnection, + XCB_COPY_FROM_PARENT, + view2WindowId, + rootWindowId, + 330, 10, 310, 310, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + mask, attributes); + + // Map windows to screen. + xcb_map_window(xcbConnection, rootWindowId); + xcb_map_window(xcbConnection, view1WindowId); + xcb_map_window(xcbConnection, view2WindowId); + + // Flush commands. + xcb_flush(xcbConnection); // Create our SFML views - sf::Window SFMLView1(view1); - sf::Window SFMLView2(view2); + sf::Window sfmlView1(view1WindowId); + sf::Window sfmlView2(view2WindowId); // Create a clock for measuring elapsed time sf::Clock clock; // Initialize our views - initialize(SFMLView1); - initialize(SFMLView2); + initialize(sfmlView1); + initialize(sfmlView2); // Start the event loop bool running = true; + xcb_generic_event_t* event = NULL; + while (running) { - while (XPending(display)) + while ((event = xcb_poll_for_event(xcbConnection))) { - // Get the next pending event - XEvent event; - XNextEvent(display, &event); - - // Process it - switch (event.type) - { - // Any key is pressed: quit - case KeyPress: - running = false; - break; - } + running = false; } // Draw something into our views - draw(SFMLView1, clock.getElapsedTime().asSeconds()); - draw(SFMLView2, clock.getElapsedTime().asSeconds() * 0.3f); + draw(sfmlView1, clock.getElapsedTime().asSeconds()); + draw(sfmlView2, clock.getElapsedTime().asSeconds() * 0.3f); // Display the views on screen - SFMLView1.display(); - SFMLView2.display(); + sfmlView1.display(); + sfmlView2.display(); } - // Close the display - XCloseDisplay(display); - return EXIT_SUCCESS; } diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index 3046fec6..83d6df67 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -193,11 +193,11 @@ if(NOT SFML_OPENGL_ES) find_package(OpenGL REQUIRED) include_directories(${OPENGL_INCLUDE_DIR}) if(SFML_OS_LINUX OR SFML_OS_FREEBSD) - find_package(X11 REQUIRED) - if(NOT X11_Xrandr_FOUND) - message(FATAL_ERROR "Xrandr library not found") + find_package(XCB COMPONENTS xlib_xcb icccm image randr util REQUIRED) + if(NOT LIBXCB_FOUND) + message(FATAL_ERROR "Xcb library not found") endif() - include_directories(${X11_INCLUDE_DIR}) + include_directories(${LIBXCB_INCLUDE_DIRS}) endif() endif() if(SFML_OPENGL_ES AND SFML_OS_LINUX) @@ -217,10 +217,11 @@ endif() # build the list of external libraries to link if(SFML_OS_WINDOWS) list(APPEND WINDOW_EXT_LIBS winmm gdi32) -elseif(SFML_OS_LINUX) - list(APPEND WINDOW_EXT_LIBS ${X11_X11_LIB} ${X11_Xrandr_LIB} ${UDEV_LIBRARIES}) -elseif(SFML_OS_FREEBSD) - list(APPEND WINDOW_EXT_LIBS ${X11_X11_LIB} ${X11_Xrandr_LIB} usbhid) +elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD) + list(APPEND WINDOW_EXT_LIBS ${LIBXCB_LIBRARIES} ${UDEV_LIBRARIES}) + if(SFML_OS_FREEBSD) + list(APPEND WINDOW_EXT_LIBS usbhid) + endif() elseif(SFML_OS_MACOSX) list(APPEND WINDOW_EXT_LIBS "-framework Foundation -framework AppKit -framework IOKit -framework Carbon") elseif(SFML_OS_IOS) diff --git a/src/SFML/Window/Unix/Display.cpp b/src/SFML/Window/Unix/Display.cpp index aad1b267..fddba307 100644 --- a/src/SFML/Window/Unix/Display.cpp +++ b/src/SFML/Window/Unix/Display.cpp @@ -63,6 +63,13 @@ Display* OpenDisplay() } +//////////////////////////////////////////////////////////// +xcb_connection_t* OpenConnection() +{ + return XGetXCBConnection(OpenDisplay()); +} + + //////////////////////////////////////////////////////////// void CloseDisplay(Display* display) { @@ -73,6 +80,27 @@ 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; +} + } // namespace priv } // namespace sf diff --git a/src/SFML/Window/Unix/Display.hpp b/src/SFML/Window/Unix/Display.hpp index df018b39..6d213f0b 100644 --- a/src/SFML/Window/Unix/Display.hpp +++ b/src/SFML/Window/Unix/Display.hpp @@ -28,7 +28,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include +#include namespace sf @@ -47,13 +47,42 @@ namespace priv Display* OpenDisplay(); //////////////////////////////////////////////////////////// -/// \brief Release a reference to the shared +/// \brief Get the xcb connection of the shared Display /// -/// \param display Display to release +/// 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 to release /// //////////////////////////////////////////////////////////// void CloseDisplay(Display* display); +//////////////////////////////////////////////////////////// +/// \brief Release a reference to the shared display +/// +/// \param Connection of display to release +/// +//////////////////////////////////////////////////////////// +void CloseConnection(xcb_connection_t* connection); + +//////////////////////////////////////////////////////////// +/// \brief Get screen of a display by index (equivalent to XScreenOfDisplay) +/// +/// \param The index of the screen +/// +/// \return Pointer to the screen +/// +//////////////////////////////////////////////////////////// +xcb_screen_t* XCBScreenOfDisplay(xcb_connection_t* connection, int screen_nbr); + } // namespace priv } // namespace sf diff --git a/src/SFML/Window/Unix/GlxContext.cpp b/src/SFML/Window/Unix/GlxContext.cpp index eac6099e..b346753b 100644 --- a/src/SFML/Window/Unix/GlxContext.cpp +++ b/src/SFML/Window/Unix/GlxContext.cpp @@ -44,18 +44,22 @@ m_ownsWindow(true) { // Open a connection with the X server m_display = OpenDisplay(); + m_connection = XGetXCBConnection(m_display); // 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); + xcb_screen_t* screen = XCBScreenOfDisplay(m_connection, DefaultScreen(m_display)); + m_window = xcb_generate_id(m_connection); + xcb_create_window( + m_connection, + screen->root_depth, + m_window, screen->root, + 0, 0, + 1, 1, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + 0, NULL + ); // Create the context createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, ContextSettings()); @@ -71,6 +75,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 +94,22 @@ m_ownsWindow(true) { // Open a connection with the X server m_display = OpenDisplay(); + m_connection = XGetXCBConnection(m_display); // 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); + xcb_screen_t* screen = XCBScreenOfDisplay(m_connection, DefaultScreen(m_display)); + m_window = xcb_generate_id(m_connection); + xcb_create_window( + m_connection, + screen->root_depth, + m_window, screen->root, + 0, 0, + width, height, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + 0, NULL + ); // Create the context createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, settings); @@ -121,8 +130,8 @@ GlxContext::~GlxContext() // 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 @@ -211,26 +220,88 @@ 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; + // Get 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; + } + + // Setup the visual infos to match + XVisualInfo tpl; + tpl.depth = windowAttributes.depth; + tpl.visualid = XVisualIDFromVisual(windowAttributes.visual); + tpl.screen = DefaultScreen(m_display); + + // Get all the visuals matching the template + int nbVisuals = 0; + XVisualInfo* visuals = XGetVisualInfo(m_display, VisualDepthMask | VisualIDMask | VisualScreenMask, &tpl, &nbVisuals); + if (!visuals || (nbVisuals == 0)) + { + if (visuals) + XFree(visuals); + err() << "There is no valid visual for the selected screen" << std::endl; + return; + } + + // Find the best visual + int bestScore = 0xFFFF; + XVisualInfo* bestVisual = NULL; + for (int i = 0; i < nbVisuals; ++i) + { + // Get the current visual attributes + int RGBA, doubleBuffer, red, green, blue, alpha, depth, stencil, multiSampling, samples; + glXGetConfig(m_display, &visuals[i], GLX_RGBA, &RGBA); + glXGetConfig(m_display, &visuals[i], GLX_DOUBLEBUFFER, &doubleBuffer); + glXGetConfig(m_display, &visuals[i], GLX_RED_SIZE, &red); + glXGetConfig(m_display, &visuals[i], GLX_GREEN_SIZE, &green); + glXGetConfig(m_display, &visuals[i], GLX_BLUE_SIZE, &blue); + glXGetConfig(m_display, &visuals[i], GLX_ALPHA_SIZE, &alpha); + glXGetConfig(m_display, &visuals[i], GLX_DEPTH_SIZE, &depth); + glXGetConfig(m_display, &visuals[i], GLX_STENCIL_SIZE, &stencil); + glXGetConfig(m_display, &visuals[i], GLX_SAMPLE_BUFFERS_ARB, &multiSampling); + glXGetConfig(m_display, &visuals[i], GLX_SAMPLES_ARB, &samples); + + // First check the mandatory parameters + if ((RGBA == 0) || (doubleBuffer == 0)) + continue; + + // Evaluate the current configuration + int color = red + green + blue + alpha; + int score = evaluateFormat(bitsPerPixel, m_settings, color, depth, stencil, multiSampling ? samples : 0); + + // Keep it if it's better than the current best + if (score < bestScore) + { + bestScore = score; + bestVisual = &visuals[i]; + } + } + + // Make sure that we have found a visual + if (!bestVisual) + { + err() << "Failed to find a suitable pixel format for the window -- cannot create OpenGL context" << std::endl; + return; + } + // 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) + while (!m_context && (m_settings.majorVersion >= 3)) { const GLubyte* name = reinterpret_cast("glXCreateContextAttribsARB"); PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = reinterpret_cast(glXGetProcAddress(name)); if (glXCreateContextAttribsARB) { - // Select a GLXFB config that matches the requested context settings int nbConfigs = 0; int fbAttributes[] = { @@ -250,43 +321,38 @@ void GlxContext::createContext(GlxContext* shared, unsigned int bitsPerPixel, co None }; GLXFBConfig* configs = glXChooseFBConfig(m_display, DefaultScreen(m_display), fbAttributes, &nbConfigs); + if (configs && nbConfigs) { - while (!m_context && (m_settings.majorVersion >= 3)) + // Create the context + int attributes[] = { - // Create the context - int attributes[] = - { - GLX_CONTEXT_MAJOR_VERSION_ARB, static_cast(m_settings.majorVersion), - GLX_CONTEXT_MINOR_VERSION_ARB, static_cast(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); + GLX_CONTEXT_MAJOR_VERSION_ARB, static_cast(m_settings.majorVersion), + GLX_CONTEXT_MINOR_VERSION_ARB, static_cast(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; - } - } - } + if (configs) XFree(configs); + } + + // 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_context) + { + 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; } } } @@ -298,23 +364,7 @@ void GlxContext::createContext(GlxContext* shared, unsigned int bitsPerPixel, co 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; - } - - // 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); - - // Create the context, using the target window's visual - m_context = glXCreateContext(m_display, visualInfo, toShare, true); + m_context = glXCreateContext(m_display, bestVisual, toShare, true); if (!m_context) { err() << "Failed to create an OpenGL context for this window" << std::endl; @@ -324,16 +374,21 @@ void GlxContext::createContext(GlxContext* shared, unsigned int bitsPerPixel, co // 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); + glXGetConfig(m_display, bestVisual, GLX_DEPTH_SIZE, &depth); + glXGetConfig(m_display, bestVisual, GLX_STENCIL_SIZE, &stencil); + glXGetConfig(m_display, bestVisual, GLX_SAMPLE_BUFFERS_ARB, &multiSampling); + glXGetConfig(m_display, bestVisual, GLX_SAMPLES_ARB, &samples); m_settings.depthBits = static_cast(depth); m_settings.stencilBits = static_cast(stencil); m_settings.antialiasingLevel = multiSampling ? samples : 0; - // Free the visual info - XFree(visualInfo); + // Change the target window's colormap so that it matches the context's one + ::Window root = RootWindow(m_display, DefaultScreen(m_display)); + Colormap colorMap = XCreateColormap(m_display, root, bestVisual->visual, AllocNone); + XSetWindowColormap(m_display, m_window, colorMap); + + // Free the temporary visuals array + XFree(visuals); } } // namespace priv diff --git a/src/SFML/Window/Unix/GlxContext.hpp b/src/SFML/Window/Unix/GlxContext.hpp index 0b5982fb..0e8bc402 100644 --- a/src/SFML/Window/Unix/GlxContext.hpp +++ b/src/SFML/Window/Unix/GlxContext.hpp @@ -29,7 +29,7 @@ // Headers //////////////////////////////////////////////////////////// #include -#include +#include #include @@ -135,10 +135,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/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp index 8ff76976..a41fa46b 100644 --- a/src/SFML/Window/Unix/InputImpl.cpp +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -28,8 +28,9 @@ #include #include #include -#include +#include #include +#include namespace sf @@ -149,20 +150,23 @@ bool InputImpl::isKeyPressed(Keyboard::Key key) // Open a connection with the X server Display* display = OpenDisplay(); + xcb_connection_t* connection = XGetXCBConnection(display); // Convert to keycode KeyCode keycode = XKeysymToKeycode(display, keysym); if (keycode != 0) { // Get the whole keyboard state - char keys[32]; - XQueryKeymap(display, keys); + xcb_query_keymap_reply_t* keymap = xcb_query_keymap_reply(connection, xcb_query_keymap(connection), NULL); // Close the connection with the X server CloseDisplay(display); // Check our keycode - return (keys[keycode / 8] & (1 << (keycode % 8))) != 0; + bool isPressed = (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0; + + free(keymap); + return isPressed; } else { @@ -175,7 +179,7 @@ bool InputImpl::isKeyPressed(Keyboard::Key key) //////////////////////////////////////////////////////////// -void InputImpl::setVirtualKeyboardVisible(bool visible) +void InputImpl::setVirtualKeyboardVisible(bool /*visible*/) { // Not applicable } @@ -186,29 +190,28 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button) { // Open a connection with the X server Display* display = OpenDisplay(); + xcb_connection_t* connection = XGetXCBConnection(display); - // we don't care about these but they are required - ::Window root, child; - int wx, wy; - int gx, gy; - - unsigned int buttons = 0; - XQueryPointer(display, DefaultRootWindow(display), &root, &child, &gx, &gy, &wx, &wy, &buttons); + // Get pointer mask + xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL); // Close the connection with the X server CloseDisplay(display); + bool result = false; + switch (button) { - case Mouse::Left: return buttons & Button1Mask; - case Mouse::Right: return buttons & Button3Mask; - case Mouse::Middle: return buttons & Button2Mask; - case Mouse::XButton1: return false; // not supported by X - case Mouse::XButton2: return false; // not supported by X - default: return false; + case Mouse::Left: result = pointer->mask & XCB_BUTTON_MASK_1; + case Mouse::Right: result = pointer->mask & XCB_BUTTON_MASK_3; + case Mouse::Middle: result = pointer->mask & XCB_BUTTON_MASK_2; + case Mouse::XButton1: // not supported by X + case Mouse::XButton2: // not supported by X + default: result = false; } - return false; + free(pointer); + return result; } @@ -217,20 +220,18 @@ Vector2i InputImpl::getMousePosition() { // Open a connection with the X server Display* display = OpenDisplay(); + xcb_connection_t* connection = XGetXCBConnection(display); - // we don't care about these but they are required - ::Window root, child; - int x, y; - unsigned int buttons; - - int gx = 0; - int gy = 0; - XQueryPointer(display, DefaultRootWindow(display), &root, &child, &gx, &gy, &x, &y, &buttons); + xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL); // Close the connection with the X server CloseDisplay(display); - return Vector2i(gx, gy); + // Prepare result. + Vector2i result(pointer->root_x, pointer->root_y); + free(pointer); + + return result; } @@ -241,21 +242,18 @@ 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; - - int x = 0; - int y = 0; - XQueryPointer(display, handle, &root, &child, &gx, &gy, &x, &y, &buttons); + xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL); // Close the connection with the X server - CloseDisplay(display); + CloseConnection(connection); - return Vector2i(x, y); + // Prepare result. + Vector2i result(pointer->win_x, pointer->win_y); + free(pointer); + + return result; } else { @@ -269,9 +267,10 @@ void InputImpl::setMousePosition(const Vector2i& position) { // Open a connection with the X server Display* display = OpenDisplay(); + xcb_connection_t* connection = XGetXCBConnection(display); - XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, position.x, position.y); - XFlush(display); + xcb_warp_pointer(connection, None, XDefaultRootWindow(display), 0, 0, 0, 0, position.x, position.y); + xcb_flush(connection); // Close the connection with the X server CloseDisplay(display); @@ -282,17 +281,17 @@ 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); + xcb_warp_pointer(connection, None, handle, 0, 0, 0, 0, position.x, position.y); + xcb_flush(connection); } // Close the connection with the X server - CloseDisplay(display); + CloseConnection(connection); } diff --git a/src/SFML/Window/Unix/VideoModeImpl.cpp b/src/SFML/Window/Unix/VideoModeImpl.cpp index c7e62040..2a3489fc 100644 --- a/src/SFML/Window/Unix/VideoModeImpl.cpp +++ b/src/SFML/Window/Unix/VideoModeImpl.cpp @@ -28,8 +28,7 @@ #include #include #include -#include -#include +#include #include @@ -44,63 +43,62 @@ std::vector VideoModeImpl::getFullscreenModes() // Open a connection with the X server Display* display = OpenDisplay(); + xcb_connection_t* connection = XGetXCBConnection(display); + if (display) { - // Retrieve the default screen number - int screen = DefaultScreen(display); + // Check if the XRandR extension is present + xcb_query_extension_cookie_t cookie = xcb_query_extension(connection, 5, "RANDR"); + + // Retrieve the default screen + xcb_screen_t* screen = XCBScreenOfDisplay(connection, DefaultScreen(display)); // Check if the XRandR extension is present - int version; - if (XQueryExtension(display, "RANDR", &version, &version, &version)) + xcb_query_extension_reply_t* randr_ext = xcb_query_extension_reply(connection, cookie, NULL); + if (randr_ext->present) { // Get the current configuration - XRRScreenConfiguration* config = XRRGetScreenInfo(display, RootWindow(display, screen)); - if (config) + xcb_generic_error_t* errors; + xcb_randr_get_screen_info_reply_t* config = xcb_randr_get_screen_info_reply( + connection, xcb_randr_get_screen_info(connection, screen->root), &errors); + if (! errors) { // Get the available screen sizes - int nbSizes; - XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes); - if (sizes && (nbSizes > 0)) + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config); + if (sizes && (config->nSizes > 0)) { // Get the list of supported depths - int nbDepths = 0; - int* depths = XListDepths(display, screen, &nbDepths); - if (depths && (nbDepths > 0)) + 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)) { - // Combine depths and sizes to fill the array of supported modes - for (int i = 0; i < nbDepths; ++i) + for (int j = 0; j < config->nSizes; ++j) { - for (int j = 0; j < nbSizes; ++j) - { - // Convert to VideoMode - VideoMode mode(sizes[j].width, sizes[j].height, depths[i]); + // Convert to VideoMode + VideoMode mode(sizes[j].width, sizes[j].height, iter.data->depth); - // 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); - } + // 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; } + // Free the configuration instance + free(errors); + free(config); } 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; } - + free(randr_ext); // Close the connection with the X server CloseDisplay(display); } @@ -121,37 +119,41 @@ VideoMode VideoModeImpl::getDesktopMode() // Open a connection with the X server Display* display = OpenDisplay(); + xcb_connection_t* connection = XGetXCBConnection(display); if (display) { - // Retrieve the default screen number - int screen = DefaultScreen(display); + xcb_query_extension_cookie_t cookie = xcb_query_extension(connection, 5, "RANDR"); + // Retrieve the default screen + xcb_screen_t* screen = XCBScreenOfDisplay(connection, DefaultScreen(display)); // Check if the XRandR extension is present - int version; - if (XQueryExtension(display, "RANDR", &version, &version, &version)) + xcb_query_extension_reply_t* randr_ext = xcb_query_extension_reply(connection, cookie, NULL); + if (randr_ext->present) { // Get the current configuration - XRRScreenConfiguration* config = XRRGetScreenInfo(display, RootWindow(display, screen)); - if (config) + xcb_generic_error_t* errors; + xcb_randr_get_screen_info_reply_t* config = + xcb_randr_get_screen_info_reply( + connection, xcb_randr_get_screen_info(connection, screen->root), &errors); + if (! errors) { // Get the current video mode - Rotation currentRotation; - int currentMode = XRRConfigCurrentConfiguration(config, ¤tRotation); + xcb_randr_mode_t currentMode = config->sizeID; // Get the available screen sizes - int nbSizes; - XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes); + int nbSizes = xcb_randr_get_screen_info_sizes_length(config); + xcb_randr_screen_size_t* sizes= xcb_randr_get_screen_info_sizes(config); if (sizes && (nbSizes > 0)) - desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, DefaultDepth(display, screen)); - - // Free the configuration instance - XRRFreeScreenConfigInfo(config); + desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, screen->root_depth); } else { // Failed to get the screen configuration err() << "Failed to retrieve the screen configuration while trying to get the desktop video modes" << std::endl; } + // Free the configuration instance + free(errors); + free(config); } else { @@ -159,6 +161,7 @@ VideoMode VideoModeImpl::getDesktopMode() err() << "Failed to use the XRandR extension while trying to get the desktop video modes" << std::endl; } + free(randr_ext); // Close the connection with the X server CloseDisplay(display); } diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 9e297576..ba3b96fd 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -30,17 +30,20 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include +#include #include #include #include #include #include #include +#include #ifdef SFML_OPENGL_ES #include @@ -61,13 +64,6 @@ namespace PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | EnterWindowMask | LeaveWindowMask; - // 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); - } - // Find the name of the current executable void findExecutableName(char* buffer, std::size_t bufferSize) { @@ -104,7 +100,19 @@ m_useSizeHints(false) { // Open a connection with the X server m_display = OpenDisplay(); - m_screen = DefaultScreen(m_display); + XSetEventQueueOwner(m_display, XCBOwnsEventQueue); + m_connection = XGetXCBConnection(m_display); + + if (!m_connection) + { + err() << "Failed cast Display object to an XCB connection object" << std::endl; + return; + } + + // Get connection info. + const xcb_setup_t* setup = xcb_get_setup(m_connection); + xcb_screen_iterator_t screenIter = xcb_setup_roots_iterator(setup); + m_screen = screenIter.data; // Save the window handle m_window = handle; @@ -112,7 +120,12 @@ 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(eventMask)}; + + xcb_change_window_attributes(m_connection, + m_window, + XCB_CW_EVENT_MASK, + value_list); // Do some common initializations initialize(); @@ -121,7 +134,7 @@ m_useSizeHints(false) //////////////////////////////////////////////////////////// -WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings) : +WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& /*settings*/) : m_window (0), m_inputMethod (NULL), m_inputContext(NULL), @@ -135,16 +148,26 @@ m_useSizeHints(false) { // Open a connection with the X server m_display = OpenDisplay(); - m_screen = DefaultScreen(m_display); - ::Window root = RootWindow(m_display, m_screen); + XSetEventQueueOwner(m_display, XCBOwnsEventQueue); + m_connection = XGetXCBConnection(m_display); + if (!m_connection) + { + err() << "Failed cast Display object to an XCB connection object" << std::endl; + return; + } + + // Get connection info. + const xcb_setup_t* setup = xcb_get_setup(m_connection); + xcb_screen_iterator_t screenIter = xcb_setup_roots_iterator(setup); + m_screen = screenIter.data; // 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; + left = (m_screen->width_in_pixels - mode.width) / 2; + top = (m_screen->height_in_pixels - mode.height) / 2; } else { @@ -158,26 +181,30 @@ m_useSizeHints(false) 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); + const uint32_t value_list[] = {fullscreen, static_cast(eventMask)}; // 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); + + xcb_void_cookie_t cookie = xcb_create_window_checked( + m_connection, + XCB_COPY_FROM_PARENT, + m_window, + m_screen->root, + left, top, + width, height, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + XCB_COPY_FROM_PARENT, + XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT, + value_list); + + xcb_generic_error_t* errptr = xcb_request_check(m_connection, cookie); + bool createWindowFailed = (errptr != NULL); + free(errptr); + + if (createWindowFailed) { err() << "Failed to create window" << std::endl; return; @@ -189,8 +216,20 @@ m_useSizeHints(false) // Set the window's style (tell the windows manager to change our window's decorations and functions according to the requested style) if (!fullscreen) { - Atom WMHintsAtom = XInternAtom(m_display, "_MOTIF_WM_HINTS", false); - if (WMHintsAtom) + static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS"; + xcb_intern_atom_cookie_t hintsAtomRequest = xcb_intern_atom( + m_connection, + 0, + MOTIF_WM_HINTS.size(), + MOTIF_WM_HINTS.c_str() + ); + xcb_intern_atom_reply_t* hintsAtomReply = xcb_intern_atom_reply( + m_connection, + hintsAtomRequest, + NULL + ); + + if (hintsAtomReply) { static const unsigned long MWM_HINTS_FUNCTIONS = 1 << 0; static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1; @@ -212,11 +251,11 @@ m_useSizeHints(false) struct WMHints { - unsigned long flags; - unsigned long functions; - unsigned long decorations; - long inputMode; - unsigned long state; + uint32_t flags; + uint32_t functions; + uint32_t decorations; + int32_t inputMode; + uint32_t state; }; WMHints hints; @@ -241,30 +280,28 @@ m_useSizeHints(false) } const unsigned char* ptr = reinterpret_cast(&hints); - XChangeProperty(m_display, m_window, WMHintsAtom, WMHintsAtom, 32, PropModeReplace, ptr, 5); + xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, + hintsAtomReply->atom, XCB_ATOM_WM_HINTS, 32, 5, ptr); + + free(hintsAtomReply); } // 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); + xcb_size_hints_t sizeHints; + sizeHints.flags = XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE; + sizeHints.min_width = sizeHints.max_width = width; + sizeHints.min_height = sizeHints.max_height = height; + xcb_icccm_set_wm_normal_hints(m_connection, m_window, &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); + xcb_icccm_set_wm_class_checked(m_connection, m_window, std::strlen(windowClass), windowClass); // Do some common initializations initialize(); @@ -272,8 +309,8 @@ m_useSizeHints(false) // In fullscreen mode, we must grab keyboard and mouse inputs if (fullscreen) { - XGrabPointer(m_display, m_window, true, 0, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime); - XGrabKeyboard(m_display, m_window, true, GrabModeAsync, GrabModeAsync, CurrentTime); + xcb_grab_pointer(m_connection, True, m_window, 0, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, m_window, XCB_NONE, XCB_CURRENT_TIME); + xcb_grab_keyboard(m_connection, True, m_window, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); } } @@ -286,7 +323,7 @@ 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) @@ -295,8 +332,8 @@ WindowImplX11::~WindowImplX11() // 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 @@ -321,10 +358,61 @@ WindowHandle WindowImplX11::getSystemHandle() const //////////////////////////////////////////////////////////// void WindowImplX11::processEvents() { - XEvent event; - while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast(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; + + while((event = xcb_poll_for_event(m_connection))) { - processEvent(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(event)->time && + lastKeyReleaseEvent->detail == reinterpret_cast(event)->detail) + { + free(lastKeyReleaseEvent); + lastKeyReleaseEvent = NULL; + } + } + + // If there's still a key release event held back, process it now. + if (lastKeyReleaseEvent) + { + processEvent(reinterpret_cast(lastKeyReleaseEvent)); + free(lastKeyReleaseEvent); + lastKeyReleaseEvent = NULL; + } + + if (eventType == XCB_KEY_RELEASE) + { + // Remember this key release event. + lastKeyReleaseEvent = reinterpret_cast(event); + event = NULL; // For safety reasons. + } + else + { + processEvent(event); + free(event); + } + } + + // Process any held back release event. + if (lastKeyReleaseEvent) + { + processEvent(reinterpret_cast(lastKeyReleaseEvent)); + free(lastKeyReleaseEvent); + lastKeyReleaseEvent = NULL; } } @@ -332,71 +420,87 @@ 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; + xcb_query_tree_cookie_t treeCookie; + xcb_query_tree_reply_t* treeReply = NULL; - XGetGeometry(m_display, m_window, &root, &localX, &localY, &width, &height, &border, &depth); - XTranslateCoordinates(m_display, m_window, root, localX, localY, &x, &y, &child); + // Get "top level" window, i.e. the window with the root window as its parent. + while (nextWindow != m_screen->root) + { + topLevelWindow = nextWindow; - return Vector2i(x, y); + treeCookie = xcb_query_tree(m_connection, topLevelWindow); + treeReply = xcb_query_tree_reply(m_connection, treeCookie, NULL); + nextWindow = treeReply->parent; + free(treeReply); + } + + xcb_get_geometry_cookie_t geometryCookie = xcb_get_geometry(m_connection, topLevelWindow); + xcb_get_geometry_reply_t* geometryReply = xcb_get_geometry_reply(m_connection, geometryCookie, NULL); + sf::Vector2i result(geometryReply->x, geometryReply->y); + free(geometryReply); + + return result; } //////////////////////////////////////////////////////////// void WindowImplX11::setPosition(const Vector2i& position) { - XMoveWindow(m_display, m_window, position.x, position.y); - XFlush(m_display); + uint32_t values[] = {static_cast(position.x), static_cast(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); + xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL); + Vector2u result(reply->width, reply->height); + free(reply); + + return result; } //////////////////////////////////////////////////////////// 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 ) { + xcb_size_hints_t sizeHints; + sizeHints.flags = XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE; + sizeHints.min_width = sizeHints.max_width = size.x; + sizeHints.min_height = sizeHints.max_height = size.y; + xcb_icccm_set_wm_normal_hints(m_connection, m_window, &sizeHints); } - XResizeWindow(m_display, m_window, size.x, size.y); - XFlush(m_display); + uint32_t values[] = {size.x, size.y}; + xcb_configure_window(m_connection, m_window, + XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, + values); + 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. + std::basic_string utf8String; + sf::Utf<32>::toUtf8( + title.begin(), title.end(), + std::back_inserter( utf8String ) + ); - // Convert to UTF-8 encoding. - std::basic_string utf8Title; - Utf32::toUtf8(title.begin(), title.end(), std::back_inserter(utf8Title)); - - // 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()); - - // 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_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, + XCB_ATOM_WM_NAME, XCB_ATOM_STRING, + 8, utf8String.length(), utf8String.c_str()); + xcb_flush(m_connection); } @@ -404,8 +508,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(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 +518,26 @@ 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); + xcb_create_pixmap(m_connection, m_screen->root_depth, iconPixmap, m_screen->root, + width, height); + + xcb_gcontext_t iconGC = xcb_generate_id(m_connection); + xcb_create_gc(m_connection, iconGC, iconPixmap, 0, NULL); + xcb_void_cookie_t cookie = 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); + xcb_free_gc(m_connection, iconGC); + + xcb_generic_error_t* errptr = xcb_request_check(m_connection, cookie); + bool setWindowIconFailed = (errptr != NULL); + free(errptr); + + if (setWindowIconFailed) { - err() << "Failed to set the window's icon" << std::endl; + err() << "Failed to set the window's icon: Error code " << (int)errptr->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 +556,16 @@ 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, (Uint8*)&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); + xcb_icccm_wm_hints_t hints; + hints.flags = XCB_ICCCM_WM_HINT_ICON_PIXMAP | XCB_ICCCM_WM_HINT_ICON_MASK; + hints.icon_pixmap = iconPixmap; + hints.icon_mask = maskPixmap; + xcb_icccm_set_wm_hints(m_connection, m_window, &hints); - XFlush(m_display); + xcb_flush(m_connection); } @@ -465,19 +573,20 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 void WindowImplX11::setVisible(bool visible) { if (visible) - XMapWindow(m_display, m_window); + xcb_map_window(m_connection, m_window); else - XUnmapWindow(m_display, m_window); + xcb_unmap_window(m_connection, m_window); - 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; + xcb_change_window_attributes(m_connection, m_window, XCB_CW_CURSOR, &values); + xcb_flush(m_connection); } @@ -506,34 +615,38 @@ void WindowImplX11::requestFocus() // Check if window is viewable (not on other desktop, ...) // TODO: Check also if minimized - XWindowAttributes attributes; - if (XGetWindowAttributes(m_display, m_window, &attributes) == 0) + xcb_get_window_attributes_cookie_t attribCookie = xcb_get_window_attributes(m_connection, m_window); + xcb_get_window_attributes_reply_t* attributes = xcb_get_window_attributes_reply(m_connection, + attribCookie, + NULL); + if (!attributes) { sf::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); + free(attributes); 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); + xcb_set_input_focus(m_connection, XCB_INPUT_FOCUS_POINTER_ROOT, m_window, XCB_CURRENT_TIME); + const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; + xcb_configure_window(m_connection, m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); } 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. + xcb_get_property_cookie_t hintsCookie = xcb_icccm_get_wm_hints_unchecked(m_connection, m_window); + xcb_icccm_wm_hints_t hints; + xcb_icccm_get_wm_hints_reply(m_connection, hintsCookie, &hints, NULL); + + // 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 |= XCB_ICCCM_WM_HINT_X_URGENCY; + xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints); } } @@ -541,11 +654,13 @@ void WindowImplX11::requestFocus() //////////////////////////////////////////////////////////// bool WindowImplX11::hasFocus() const { - ::Window focusedWindow = 0; - int revertToReturn = 0; - XGetInputFocus(m_display, &focusedWindow, &revertToReturn); + xcb_get_input_focus_cookie_t cookie = xcb_get_input_focus_unchecked(m_connection); + xcb_get_input_focus_reply_t* reply = xcb_get_input_focus_reply(m_connection, cookie, NULL); - return m_window == focusedWindow; + bool focussed = (reply->focus == m_window); + free(reply); + + return focussed; } @@ -553,29 +668,36 @@ bool WindowImplX11::hasFocus() const void WindowImplX11::switchToFullscreen(const VideoMode& mode) { // Check if the XRandR extension is present - int version; - if (XQueryExtension(m_display, "RANDR", &version, &version, &version)) + xcb_query_extension_reply_t* randr_ext = + xcb_query_extension_reply(m_connection, xcb_query_extension(m_connection, 5, "RANDR"), NULL); + if (randr_ext->present) { // Get the current configuration - XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen)); - if (config) + xcb_generic_error_t* errors; + 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), + &errors); + if (! errors) { - // Get the current rotation - Rotation currentRotation; - m_oldVideoMode = XRRConfigCurrentConfiguration(config, ¤tRotation); + // Save the current video mode before we switch to fullscreen + m_oldVideoMode = config->sizeID; // Get the available screen sizes - int nbSizes; - XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes); - if (sizes && (nbSizes > 0)) + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config); + if (sizes && (config->nSizes > 0)) { // Search a matching size - for (int i = 0; i < nbSizes; ++i) + for (int i = 0; i < config->nSizes; ++i) { if ((sizes[i].width == static_cast(mode.width)) && (sizes[i].height == static_cast(mode.height))) { // Switch to fullscreen mode - XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), i, currentRotation, CurrentTime); + xcb_randr_set_screen_config(m_connection, + m_screen->root, + config->timestamp, + config->config_timestamp, + i, config->rotation, config->rate); // Set "this" as the current fullscreen window fullscreenWindow = this; @@ -583,30 +705,76 @@ void WindowImplX11::switchToFullscreen(const VideoMode& mode) } } } - - // Free the configuration instance - XRRFreeScreenConfigInfo(config); } else { // Failed to get the screen configuration err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; } + free(errors); + free(config); } else { // XRandr extension is not supported: we cannot use fullscreen mode err() << "Fullscreen is not supported, switching to window mode" << std::endl; } + free(randr_ext); } //////////////////////////////////////////////////////////// void WindowImplX11::initialize() { - // Get the atom defining the close event - m_atomClose = XInternAtom(m_display, "WM_DELETE_WINDOW", false); - XSetWMProtocols(m_display, m_window, &m_atomClose, 1); + // Get the atoms for registering the close event + static const std::string WM_DELETE_WINDOW_NAME = "WM_DELETE_WINDOW"; + + xcb_intern_atom_cookie_t deleteWindowAtomRequest = xcb_intern_atom( + m_connection, + 0, + WM_DELETE_WINDOW_NAME.size(), + WM_DELETE_WINDOW_NAME.c_str() + ); + xcb_intern_atom_reply_t* deleteWindowAtomReply = xcb_intern_atom_reply( + m_connection, + deleteWindowAtomRequest, + NULL + ); + + static const std::string WM_PROTOCOLS_NAME = "WM_PROTOCOLS"; + + xcb_intern_atom_cookie_t protocolsAtomRequest = xcb_intern_atom( + m_connection, + 0, + WM_PROTOCOLS_NAME.size(), + WM_PROTOCOLS_NAME.c_str() + ); + xcb_intern_atom_reply_t* protocolsAtomReply = xcb_intern_atom_reply( + m_connection, + protocolsAtomRequest, + NULL + ); + + if (protocolsAtomReply && deleteWindowAtomReply) + { + xcb_icccm_set_wm_protocols( + m_connection, + m_window, + protocolsAtomReply->atom, + 1, + &deleteWindowAtomReply->atom + ); + + m_atomClose = deleteWindowAtomReply->atom; + } + else + { + // Should not happen, but better safe than sorry. + std::cerr << "Failed to request WM_PROTOCOLS/WM_DELETE_WINDOW_NAME atoms." << std::endl; + } + + free(protocolsAtomReply); + free(deleteWindowAtomReply); // Create the input context m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL); @@ -626,15 +794,15 @@ void WindowImplX11::initialize() 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); + xcb_map_window(m_connection, m_window); + xcb_flush(m_connection); // 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) allWindows.push_back(this); } @@ -643,20 +811,19 @@ void WindowImplX11::initialize() //////////////////////////////////////////////////////////// void WindowImplX11::createHiddenCursor() { + xcb_pixmap_t cursorPixmap = xcb_generate_id(m_connection); + m_hiddenCursor = 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); + xcb_create_pixmap(m_connection, 1, cursorPixmap, m_window, 1, 1); // 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); + xcb_create_cursor(m_connection, m_hiddenCursor, cursorPixmap, cursorPixmap, + 0, 0, 0, // Fore color + 0, 0, 0, // Back color + 0, 0); // We don't need the pixmap any longer, free it - XFreePixmap(m_display, cursorPixmap); + xcb_free_pixmap(m_connection, cursorPixmap); } @@ -667,19 +834,23 @@ void WindowImplX11::cleanup() if (fullscreenWindow == this) { // Get current screen info - XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen)); - if (config) + xcb_generic_error_t* errors; + 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), &errors); + if (!errors) { - // Get the current rotation - Rotation currentRotation; - XRRConfigCurrentConfiguration(config, ¤tRotation); - // Reset the video mode - XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), m_oldVideoMode, currentRotation, CurrentTime); + xcb_randr_set_screen_config(m_connection, + m_screen->root, + CurrentTime, + config->config_timestamp, + m_oldVideoMode, + config->rotation, config->rate); // Free the configuration instance - XRRFreeScreenConfigInfo(config); + free(config); } + free(errors); // Reset the fullscreen window fullscreenWindow = NULL; @@ -691,48 +862,13 @@ 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: { // The window is about to be destroyed: we must cleanup resources cleanup(); @@ -740,7 +876,7 @@ bool WindowImplX11::processEvent(XEvent windowEvent) } // Gain focus event - case FocusIn: + case XCB_FOCUS_IN: { // Update the input context if (m_inputContext) @@ -751,19 +887,19 @@ 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) - { - // Remove urgency (notification) flag from hints - hints->flags &= ~XUrgencyHint; - XSetWMHints(m_display, m_window, hints); - XFree(hints); - } + xcb_get_property_cookie_t hintsCookie = xcb_icccm_get_wm_hints_unchecked(m_connection, m_window); + xcb_icccm_wm_hints_t hints; + xcb_icccm_get_wm_hints_reply(m_connection, hintsCookie, &hints, NULL); + + // Remove urgency (notification) flag from hints + hints.flags &= ~XUrgencyHint; + xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints); + break; } // Lost focus event - case FocusOut: + case XCB_FOCUS_OUT: { // Update the input context if (m_inputContext) @@ -776,27 +912,22 @@ 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); - - m_previousSize.x = windowEvent.xconfigure.width; - m_previousSize.y = windowEvent.xconfigure.height; - } + xcb_configure_notify_event_t* e = reinterpret_cast(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(m_atomClose)) + xcb_client_message_event_t* e = reinterpret_cast(windowEvent); + if ((e->format == 32) && (e->data.data32[0]) == static_cast(m_atomClose)) { Event event; event.type = Event::Closed; @@ -806,34 +937,46 @@ bool WindowImplX11::processEvent(XEvent windowEvent) } // Key down event - case KeyPress: + case XCB_KEY_PRESS: { + xcb_key_press_event_t* e = reinterpret_cast(windowEvent); + // 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); + + // There is no xcb equivalent of XLookupString, so the xcb event + // has to be converted to an XEvent + XEvent fake_event; + fake_event.type = KeyPress; + fake_event.xany.display = m_display; + fake_event.xany.window = e->event; + fake_event.xkey.state = e->state; + fake_event.xkey.keycode = e->detail; + + XLookupString(&fake_event.xkey, buffer, sizeof(buffer), &symbol, &keyboard); // 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.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); // Generate a TextEntered event - if (!XFilterEvent(&windowEvent, None)) + if (!XFilterEvent(&fake_event, None)) { #ifdef X_HAVE_UTF8_STRING if (m_inputContext) { Status status; Uint8 keyBuffer[16]; - int length = Xutf8LookupString(m_inputContext, &windowEvent.xkey, reinterpret_cast(keyBuffer), sizeof(keyBuffer), NULL, &status); + int length = Xutf8LookupString(m_inputContext, &fake_event.xkey, reinterpret_cast(keyBuffer), sizeof(keyBuffer), NULL, &status); if (length > 0) { Uint32 unicode = 0; @@ -852,7 +995,7 @@ bool WindowImplX11::processEvent(XEvent windowEvent) { static XComposeStatus status; char keyBuffer[16]; - if (XLookupString(&windowEvent.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status)) + if (XLookupString(&fake_event.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status)) { Event textEvent; textEvent.type = Event::TextEntered; @@ -866,43 +1009,56 @@ bool WindowImplX11::processEvent(XEvent windowEvent) } // Key up event - case KeyRelease: + case XCB_KEY_RELEASE: { + xcb_key_release_event_t* e = reinterpret_cast(windowEvent); + // Get the keysym of the key that has been pressed char buffer[32]; KeySym symbol; - XLookupString(&windowEvent.xkey, buffer, 32, &symbol, NULL); + + // There is no xcb equivalent of XLookupString, so the xcb event + // has to be converted to an XEvent + XKeyEvent fake_event; + fake_event.display = m_display; + fake_event.state = e->state; + fake_event.keycode = e->detail; + XLookupString(&fake_event, buffer, 32, &symbol, NULL); // 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.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)) + xcb_button_press_event_t* e = reinterpret_cast(windowEvent); + + // XXX: Why button 8 and 9? + 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 +1066,58 @@ 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)) + xcb_button_release_event_t* e = reinterpret_cast(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.type = Event::MouseButtonPressed; + 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); } break; } // Mouse moved - case MotionNotify: + case XCB_MOTION_NOTIFY: { + xcb_motion_notify_event_t* e = reinterpret_cast(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) + xcb_enter_notify_event_t* enterNotifyEvent = reinterpret_cast(windowEvent); + + if (enterNotifyEvent->mode == NotifyNormal) { Event event; event.type = Event::MouseEntered; @@ -965,9 +1127,11 @@ bool WindowImplX11::processEvent(XEvent windowEvent) } // Mouse left - case LeaveNotify: + case XCB_LEAVE_NOTIFY: { - if (windowEvent.xcrossing.mode == NotifyNormal) + xcb_leave_notify_event_t* leaveNotifyEvent = reinterpret_cast(windowEvent); + + if (leaveNotifyEvent->mode == NotifyNormal) { Event event; event.type = Event::MouseLeft; @@ -977,9 +1141,9 @@ bool WindowImplX11::processEvent(XEvent windowEvent) } // Parent window changed - case ReparentNotify: + case XCB_REPARENT_NOTIFY: { - XSync(m_display, True); // Discard remaining events + xcb_flush(m_connection); // Discard remaining events break; } } @@ -989,7 +1153,7 @@ bool WindowImplX11::processEvent(XEvent windowEvent) //////////////////////////////////////////////////////////// -Keyboard::Key WindowImplX11::keysymToSF(KeySym symbol) +Keyboard::Key WindowImplX11::keysymToSF(xcb_keysym_t symbol) { // First convert to uppercase (to avoid dealing with two different keysyms for the same key) KeySym lower, key; diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index d824492c..edf3aad8 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include @@ -213,7 +213,7 @@ 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 @@ -223,23 +223,24 @@ private: /// \return Corresponding SFML key code /// //////////////////////////////////////////////////////////// - static Keyboard::Key keysymToSF(KeySym symbol); + static Keyboard::Key keysymToSF(xcb_keysym_t symbol); //////////////////////////////////////////////////////////// // 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? + ::Window m_window; ///< X11 structure 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 + 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 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? }; } // namespace priv