diff --git a/examples/X11/X11.cpp b/examples/X11/X11.cpp index 17bb4488..5b623d93 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 20fb2b31..94e471b2 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -69,8 +69,6 @@ if(SFML_OS_WINDOWS) add_definitions(-DUNICODE -D_UNICODE) elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD) set(PLATFORM_SRC - ${SRCROOT}/Linux/AutoPointer.cpp - ${SRCROOT}/Linux/AutoPointer.hpp ${SRCROOT}/Unix/Display.cpp ${SRCROOT}/Unix/Display.hpp ${SRCROOT}/Unix/InputImpl.cpp @@ -195,7 +193,7 @@ if(NOT SFML_OPENGL_ES) find_package(OpenGL REQUIRED) include_directories(${OPENGL_INCLUDE_DIR}) if(SFML_OS_LINUX OR SFML_OS_FREEBSD) - find_package(XCB COMPONENTS xlib_xcb atom icccm image randr REQUIRED) + find_package(XCB COMPONENTS xlib_xcb icccm image randr util REQUIRED) if(NOT LIBXCB_FOUND) message(FATAL_ERROR "Xcb library not found") endif() diff --git a/src/SFML/Window/Linux/AutoPointer.cpp b/src/SFML/Window/Linux/AutoPointer.cpp deleted file mode 100644 index e20d01d6..00000000 --- a/src/SFML/Window/Linux/AutoPointer.cpp +++ /dev/null @@ -1,44 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2012 Laurent Gomila (laurent.gom@gmail.com) -// -// 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 -#include - -namespace sf -{ -namespace priv -{ -//////////////////////////////////////////////////////////// -ErrorPointer::ErrorPointer(xcb_connection_t *&connection, xcb_void_cookie_t &cookie) - : AutoPointer(xcb_request_check(connection, cookie)) -{ -} - -} // namespace priv - -} // namespace sf diff --git a/src/SFML/Window/Linux/AutoPointer.hpp b/src/SFML/Window/Linux/AutoPointer.hpp deleted file mode 100644 index 0d929682..00000000 --- a/src/SFML/Window/Linux/AutoPointer.hpp +++ /dev/null @@ -1,132 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2012 Laurent Gomila (laurent.gom@gmail.com) -// -// 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 AUTOPOINTER_HPP -#define AUTOPOINTER_HPP - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include - -namespace sf -{ -namespace priv -{ -//////////////////////////////////////////////////////////// -/// \brief Pointer Wrapper. Memory is automatically free'd -/// on deletion of the object. -/// -//////////////////////////////////////////////////////////// -template -class AutoPointer -{ -public: - //////////////////////////////////////////////////////////// - /// \brief Constructor - /// - //////////////////////////////////////////////////////////// - AutoPointer(T* ptr) - { - pointer = ptr; - } - - //////////////////////////////////////////////////////////// - /// \brief Constructor - /// - //////////////////////////////////////////////////////////// - AutoPointer() - : pointer(NULL) - { - } - - //////////////////////////////////////////////////////////// - /// \brief Destructor, frees the error pointer - /// - //////////////////////////////////////////////////////////// - ~AutoPointer() - { - free(pointer); - } - - //////////////////////////////////////////////////////////// - /// \brief Operator for error member access - /// - //////////////////////////////////////////////////////////// - T* operator->() const - { - return pointer; - } - - //////////////////////////////////////////////////////////// - /// \brief Assignment operator. Frees the previous pointer - /// - //////////////////////////////////////////////////////////// - T* operator=(T*& new_pointer) const - { - free(pointer); - pointer = new_pointer; - } - - T** operator&() - { - return &pointer; - } - - //////////////////////////////////////////////////////////// - /// \brief Check if reqeust succeeded - /// - /// \return true if reqeust succeeded - /// - //////////////////////////////////////////////////////////// - bool isNull() const - { - return pointer == NULL; - } - -private: - T* pointer; -}; - - -//////////////////////////////////////////////////////////// -/// \brief Wrapper for automatically creating and freeing -/// an error struct out of a cookie -/// -//////////////////////////////////////////////////////////// -class ErrorPointer : public AutoPointer -{ -public: - //////////////////////////////////////////////////////////// - /// \brief Construct the error pointer from a cookie - /// - //////////////////////////////////////////////////////////// - ErrorPointer(xcb_connection_t*& connection, xcb_void_cookie_t& cookie); -}; - -} // namespace priv - -} // namespace sf - -#endif // AUTOPOINTER_HPP diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp index 68e396b6..d6aa5fb4 100644 --- a/src/SFML/Window/Unix/InputImpl.cpp +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -158,13 +157,16 @@ bool InputImpl::isKeyPressed(Keyboard::Key key) if (keycode != 0) { // Get the whole keyboard state - AutoPointer keymap = xcb_query_keymap_reply(connection, xcb_query_keymap(connection), NULL); + 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 (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0; + bool isPressed = (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0; + + free(keymap); + return isPressed; } else { @@ -191,21 +193,25 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button) xcb_connection_t* connection = XGetXCBConnection(display); // Get pointer mask - AutoPointer pointer = - xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL); + 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 pointer->mask & XCB_BUTTON_MASK_1; - case Mouse::Right: return pointer->mask & XCB_BUTTON_MASK_3; - case Mouse::Middle: return pointer->mask & XCB_BUTTON_MASK_2; + 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: return false; + default: result = false; } + + free(pointer); + return result; } @@ -216,13 +222,16 @@ Vector2i InputImpl::getMousePosition() Display* display = OpenDisplay(); xcb_connection_t* connection = XGetXCBConnection(display); - AutoPointer pointer = - xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL); + 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(pointer->root_x, pointer->root_y); + // Prepare result. + Vector2i result(pointer->root_x, pointer->root_y); + free(pointer); + + return result; } @@ -235,13 +244,16 @@ Vector2i InputImpl::getMousePosition(const Window& relativeTo) // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); - AutoPointer pointer = - xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL); + xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL); // Close the connection with the X server CloseConnection(connection); - return Vector2i(pointer->win_x, pointer->win_y); + // Prepare result. + Vector2i result(pointer->win_x, pointer->win_y); + free(pointer); + + return result; } else { diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 22ad3303..98b8e0ab 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -28,20 +28,25 @@ #include // important to be included first (conflict with None) #include #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 @@ -106,6 +111,14 @@ m_useSizeHints(false) // Open a connection with the X server m_display = OpenDisplay(); 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; + } + m_screen = DefaultScreen(m_display); // Save the window handle @@ -114,7 +127,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[] = {eventMask}; + + xcb_change_window_attributes(m_connection, + m_window, + XCB_CW_EVENT_MASK, + value_list); // Do some common initializations initialize(); @@ -188,8 +206,11 @@ m_useSizeHints(false) XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT, value_list); - ErrorPointer errptr(m_connection, cookie); - if(! errptr.isNull()) + 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; @@ -201,8 +222,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) { - xcb_atom_t WMHintsAtom = xcb_atom_get(m_connection, "_MOTIF_WM_HINTS"); - 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; @@ -254,17 +287,18 @@ m_useSizeHints(false) const unsigned char* ptr = reinterpret_cast(&hints); xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, - WMHintsAtom, WM_HINTS, 32, 5, ptr); + hintsAtomReply->atom, XA_WM_HINTS, 32, 5, ptr); } // This is a hack to force some windows managers to disable resizing if (!(style & Style::Resize)) { + m_useSizeHints = true; xcb_size_hints_t sizeHints; - sizeHints.flags = XCB_SIZE_HINT_P_MIN_SIZE | XCB_SIZE_HINT_P_MAX_SIZE; + 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_set_wm_normal_hints(m_connection, m_window, &sizeHints); + xcb_icccm_set_wm_normal_hints(m_connection, m_window, &sizeHints); } } @@ -332,10 +366,61 @@ WindowHandle WindowImplX11::getSystemHandle() const //////////////////////////////////////////////////////////// void WindowImplX11::processEvents() { - while(xcb_generic_event_t* event = xcb_poll_for_event(m_connection)) + // 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); - free(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; } } @@ -343,9 +428,11 @@ void WindowImplX11::processEvents() //////////////////////////////////////////////////////////// Vector2i WindowImplX11::getPosition() const { - AutoPointer reply = - xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL); - return Vector2i(reply->x, reply->y); + xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL); + Vector2i result(reply->x, reply->y); + free(reply); + + return result; } @@ -363,15 +450,26 @@ void WindowImplX11::setPosition(const Vector2i& position) //////////////////////////////////////////////////////////// Vector2u WindowImplX11::getSize() const { - AutoPointer reply = - xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL); - return Vector2u(reply->width, reply->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 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); + } + uint32_t values[] = {size.x, size.y}; xcb_configure_window(m_connection, m_window, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, @@ -383,10 +481,16 @@ void WindowImplX11::setSize(const Vector2u& size) //////////////////////////////////////////////////////////// void WindowImplX11::setTitle(const String& title) { - const char* c_title = title.c_str(); + // XCB takes UTF-8-encoded strings. + std::basic_string utf8String; + sf::Utf<32>::toUtf8( + title.begin(), title.end(), + std::back_inserter( utf8String ) + ); + xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, - WM_NAME, STRING, - 8, title.length(), c_title); + XA_WM_NAME, XA_STRING, + 8, utf8String.length(), utf8String.c_str()); xcb_flush(m_connection); } @@ -418,8 +522,11 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 width, height, 0, 0, 0, defDepth, sizeof(iconPixels), iconPixels); xcb_free_gc(m_connection, iconGC); - ErrorPointer errptr(m_connection, cookie); - if (! errptr.isNull()) + 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: Error code " << (int)errptr->error_code << std::endl; return; @@ -445,11 +552,11 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 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 - xcb_wm_hints_t hints; - hints.flags = XCB_WM_HINT_ICON_PIXMAP | XCB_WM_HINT_ICON_MASK; + 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_set_wm_hints(m_connection, m_window, &hints); + xcb_icccm_set_wm_hints(m_connection, m_window, &hints); xcb_flush(m_connection); } @@ -607,15 +714,52 @@ void WindowImplX11::switchToFullscreen(const VideoMode& mode) //////////////////////////////////////////////////////////// void WindowImplX11::initialize() { - // Make sure the "last key release" is initialized with invalid values - m_lastKeyReleaseEvent.response_type = -1; - m_lastKeyReleaseEvent.detail = 0; - m_lastKeyReleaseEvent.time = 0; + // Get the atoms for registering the close event + static const std::string WM_DELETE_WINDOW_NAME = "WM_DELETE_WINDOW"; - // Get the atom defining the close event - m_atomClose = xcb_atom_get(m_connection, "WM_DELETE_WINDOW"); - xcb_atom_t wmprotocolsAtom = xcb_atom_get(m_connection, "WM_PROTOCOLS"); - xcb_set_wm_protocols(m_connection, wmprotocolsAtom, m_window, 1, &m_atomClose); + 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; + } // Create the input context m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL); @@ -708,49 +852,6 @@ void WindowImplX11::cleanup() //////////////////////////////////////////////////////////// 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 - if ((windowEvent->response_type == XCB_KEY_PRESS) || - (windowEvent->response_type == XCB_KEY_RELEASE)) - { - xcb_key_press_event_t* press = reinterpret_cast(windowEvent); - if (press->detail < 256) - { - // To detect if it is a repeated key event, we check the current state of the key. - // - If the state is "down", KeyReleased events must obviously be discarded. - // - KeyPress events are a little bit harder to handle: they depend on the EnableKeyRepeat state, - // and we need to properly forward the first one. - xcb_query_keymap_cookie_t cookie = xcb_query_keymap(m_connection); - AutoPointer keymap = xcb_query_keymap_reply(m_connection, - cookie, NULL); - - if (keymap->keys[press->detail / 8] & (1 << (press->detail % 8))) - { - // KeyRelease event + key down = repeated event --> discard - if (windowEvent->response_type == XCB_KEY_RELEASE) - { - m_lastKeyReleaseEvent = *press; - return false; - } - - // KeyPress event + key repeat disabled + matching KeyRelease event = repeated event --> discard - if ((windowEvent->response_type == XCB_KEY_PRESS) && !m_keyRepeat && - (m_lastKeyReleaseEvent.detail == press->detail) && - (m_lastKeyReleaseEvent.time == press->time)) - { - return false; - } - } - } - } - // Convert the X11 event to a sf::Event switch (windowEvent->response_type & ~0x80) { @@ -827,6 +928,7 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) 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]; @@ -895,9 +997,10 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* 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; @@ -1000,7 +1103,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse entered 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; @@ -1012,7 +1117,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse left 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; diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 7882aa01..5a625dc4 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -228,19 +228,19 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - ::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 - 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 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? + ::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 + 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 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