diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index b420fad1..e39fcd3c 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -75,6 +75,8 @@ elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD) ${SRCROOT}/Unix/Display.hpp ${SRCROOT}/Unix/InputImpl.cpp ${SRCROOT}/Unix/InputImpl.hpp + ${SRCROOT}/Unix/ScopedXcbPtr.hpp + ${SRCROOT}/Unix/ScopedXcbPtr.inl ${SRCROOT}/Unix/SensorImpl.cpp ${SRCROOT}/Unix/SensorImpl.hpp ${SRCROOT}/Unix/VideoModeImpl.cpp @@ -197,7 +199,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 icccm image randr util REQUIRED) + find_package(XCB COMPONENTS xlib_xcb icccm image randr util ewmh keysyms REQUIRED) if(NOT LIBXCB_FOUND) message(FATAL_ERROR "Xcb library not found") endif() diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp index ecdf47fa..63fdb4fb 100644 --- a/src/SFML/Window/Unix/InputImpl.cpp +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -29,10 +29,159 @@ #include #include #include +#include +#include #include #include #include +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// +namespace +{ + bool mapBuilt = false; + + // We use a simple array instead of a map => constant time lookup + xcb_keycode_t keycodeMap[sf::Keyboard::KeyCount]; + + xcb_keycode_t getKeycode(xcb_key_symbols_t* keySymbols, xcb_keysym_t keysym) + { + // keycodes is actually a vector of keycodes + // Since we only care about the unmodified keycode, + // we simply return the first one + sf::priv::ScopedXcbPtr keycodes(xcb_key_symbols_get_keycode(keySymbols, keysym)); + + if (keycodes) + return *keycodes.get(); + + return 0; + } + + void buildMap() + { + // Open a connection with the X server + xcb_connection_t* connection = sf::priv::OpenConnection(); + + xcb_key_symbols_t* keySymbols = xcb_key_symbols_alloc(connection); + + if (!keySymbols) + { + sf::err() << "Failed to allocate key symbols" << std::endl; + return; + } + + keycodeMap[sf::Keyboard::A] = getKeycode(keySymbols, XK_A); + keycodeMap[sf::Keyboard::B] = getKeycode(keySymbols, XK_B); + keycodeMap[sf::Keyboard::C] = getKeycode(keySymbols, XK_C); + keycodeMap[sf::Keyboard::D] = getKeycode(keySymbols, XK_D); + keycodeMap[sf::Keyboard::E] = getKeycode(keySymbols, XK_E); + keycodeMap[sf::Keyboard::F] = getKeycode(keySymbols, XK_F); + keycodeMap[sf::Keyboard::G] = getKeycode(keySymbols, XK_G); + keycodeMap[sf::Keyboard::H] = getKeycode(keySymbols, XK_H); + keycodeMap[sf::Keyboard::I] = getKeycode(keySymbols, XK_I); + keycodeMap[sf::Keyboard::J] = getKeycode(keySymbols, XK_J); + keycodeMap[sf::Keyboard::K] = getKeycode(keySymbols, XK_K); + keycodeMap[sf::Keyboard::L] = getKeycode(keySymbols, XK_L); + keycodeMap[sf::Keyboard::M] = getKeycode(keySymbols, XK_M); + keycodeMap[sf::Keyboard::N] = getKeycode(keySymbols, XK_N); + keycodeMap[sf::Keyboard::O] = getKeycode(keySymbols, XK_O); + keycodeMap[sf::Keyboard::P] = getKeycode(keySymbols, XK_P); + keycodeMap[sf::Keyboard::Q] = getKeycode(keySymbols, XK_Q); + keycodeMap[sf::Keyboard::R] = getKeycode(keySymbols, XK_R); + keycodeMap[sf::Keyboard::S] = getKeycode(keySymbols, XK_S); + keycodeMap[sf::Keyboard::T] = getKeycode(keySymbols, XK_T); + keycodeMap[sf::Keyboard::U] = getKeycode(keySymbols, XK_U); + keycodeMap[sf::Keyboard::V] = getKeycode(keySymbols, XK_V); + keycodeMap[sf::Keyboard::W] = getKeycode(keySymbols, XK_W); + keycodeMap[sf::Keyboard::X] = getKeycode(keySymbols, XK_X); + keycodeMap[sf::Keyboard::Y] = getKeycode(keySymbols, XK_Y); + keycodeMap[sf::Keyboard::Z] = getKeycode(keySymbols, XK_Z); + keycodeMap[sf::Keyboard::Num0] = getKeycode(keySymbols, XK_0); + keycodeMap[sf::Keyboard::Num1] = getKeycode(keySymbols, XK_1); + keycodeMap[sf::Keyboard::Num2] = getKeycode(keySymbols, XK_2); + keycodeMap[sf::Keyboard::Num3] = getKeycode(keySymbols, XK_3); + keycodeMap[sf::Keyboard::Num4] = getKeycode(keySymbols, XK_4); + keycodeMap[sf::Keyboard::Num5] = getKeycode(keySymbols, XK_5); + keycodeMap[sf::Keyboard::Num6] = getKeycode(keySymbols, XK_6); + keycodeMap[sf::Keyboard::Num7] = getKeycode(keySymbols, XK_7); + keycodeMap[sf::Keyboard::Num8] = getKeycode(keySymbols, XK_8); + keycodeMap[sf::Keyboard::Num9] = getKeycode(keySymbols, XK_9); + keycodeMap[sf::Keyboard::Escape] = getKeycode(keySymbols, XK_Escape); + keycodeMap[sf::Keyboard::LControl] = getKeycode(keySymbols, XK_Control_L); + keycodeMap[sf::Keyboard::LShift] = getKeycode(keySymbols, XK_Shift_L); + keycodeMap[sf::Keyboard::LAlt] = getKeycode(keySymbols, XK_Alt_L); + keycodeMap[sf::Keyboard::LSystem] = getKeycode(keySymbols, XK_Super_L); + keycodeMap[sf::Keyboard::RControl] = getKeycode(keySymbols, XK_Control_R); + keycodeMap[sf::Keyboard::RShift] = getKeycode(keySymbols, XK_Shift_R); + keycodeMap[sf::Keyboard::RAlt] = getKeycode(keySymbols, XK_Alt_R); + keycodeMap[sf::Keyboard::RSystem] = getKeycode(keySymbols, XK_Super_R); + keycodeMap[sf::Keyboard::Menu] = getKeycode(keySymbols, XK_Menu); + keycodeMap[sf::Keyboard::LBracket] = getKeycode(keySymbols, XK_bracketleft); + keycodeMap[sf::Keyboard::RBracket] = getKeycode(keySymbols, XK_bracketright); + keycodeMap[sf::Keyboard::SemiColon] = getKeycode(keySymbols, XK_semicolon); + keycodeMap[sf::Keyboard::Comma] = getKeycode(keySymbols, XK_comma); + keycodeMap[sf::Keyboard::Period] = getKeycode(keySymbols, XK_period); + keycodeMap[sf::Keyboard::Quote] = getKeycode(keySymbols, XK_apostrophe); + keycodeMap[sf::Keyboard::Slash] = getKeycode(keySymbols, XK_slash); + keycodeMap[sf::Keyboard::BackSlash] = getKeycode(keySymbols, XK_backslash); + keycodeMap[sf::Keyboard::Tilde] = getKeycode(keySymbols, XK_grave); + keycodeMap[sf::Keyboard::Equal] = getKeycode(keySymbols, XK_equal); + keycodeMap[sf::Keyboard::Dash] = getKeycode(keySymbols, XK_minus); + keycodeMap[sf::Keyboard::Space] = getKeycode(keySymbols, XK_space); + keycodeMap[sf::Keyboard::Return] = getKeycode(keySymbols, XK_Return); + keycodeMap[sf::Keyboard::BackSpace] = getKeycode(keySymbols, XK_BackSpace); + keycodeMap[sf::Keyboard::Tab] = getKeycode(keySymbols, XK_Tab); + keycodeMap[sf::Keyboard::PageUp] = getKeycode(keySymbols, XK_Prior); + keycodeMap[sf::Keyboard::PageDown] = getKeycode(keySymbols, XK_Next); + keycodeMap[sf::Keyboard::End] = getKeycode(keySymbols, XK_End); + keycodeMap[sf::Keyboard::Home] = getKeycode(keySymbols, XK_Home); + keycodeMap[sf::Keyboard::Insert] = getKeycode(keySymbols, XK_Insert); + keycodeMap[sf::Keyboard::Delete] = getKeycode(keySymbols, XK_Delete); + keycodeMap[sf::Keyboard::Add] = getKeycode(keySymbols, XK_KP_Add); + keycodeMap[sf::Keyboard::Subtract] = getKeycode(keySymbols, XK_KP_Subtract); + keycodeMap[sf::Keyboard::Multiply] = getKeycode(keySymbols, XK_KP_Multiply); + keycodeMap[sf::Keyboard::Divide] = getKeycode(keySymbols, XK_KP_Divide); + keycodeMap[sf::Keyboard::Left] = getKeycode(keySymbols, XK_Left); + keycodeMap[sf::Keyboard::Right] = getKeycode(keySymbols, XK_Right); + keycodeMap[sf::Keyboard::Up] = getKeycode(keySymbols, XK_Up); + keycodeMap[sf::Keyboard::Down] = getKeycode(keySymbols, XK_Down); + keycodeMap[sf::Keyboard::Numpad0] = getKeycode(keySymbols, XK_KP_0); + keycodeMap[sf::Keyboard::Numpad1] = getKeycode(keySymbols, XK_KP_1); + keycodeMap[sf::Keyboard::Numpad2] = getKeycode(keySymbols, XK_KP_2); + keycodeMap[sf::Keyboard::Numpad3] = getKeycode(keySymbols, XK_KP_3); + keycodeMap[sf::Keyboard::Numpad4] = getKeycode(keySymbols, XK_KP_4); + keycodeMap[sf::Keyboard::Numpad5] = getKeycode(keySymbols, XK_KP_5); + keycodeMap[sf::Keyboard::Numpad6] = getKeycode(keySymbols, XK_KP_6); + keycodeMap[sf::Keyboard::Numpad7] = getKeycode(keySymbols, XK_KP_7); + keycodeMap[sf::Keyboard::Numpad8] = getKeycode(keySymbols, XK_KP_8); + keycodeMap[sf::Keyboard::Numpad9] = getKeycode(keySymbols, XK_KP_9); + keycodeMap[sf::Keyboard::F1] = getKeycode(keySymbols, XK_F1); + keycodeMap[sf::Keyboard::F2] = getKeycode(keySymbols, XK_F2); + keycodeMap[sf::Keyboard::F3] = getKeycode(keySymbols, XK_F3); + keycodeMap[sf::Keyboard::F4] = getKeycode(keySymbols, XK_F4); + keycodeMap[sf::Keyboard::F5] = getKeycode(keySymbols, XK_F5); + keycodeMap[sf::Keyboard::F6] = getKeycode(keySymbols, XK_F6); + keycodeMap[sf::Keyboard::F7] = getKeycode(keySymbols, XK_F7); + keycodeMap[sf::Keyboard::F8] = getKeycode(keySymbols, XK_F8); + keycodeMap[sf::Keyboard::F9] = getKeycode(keySymbols, XK_F9); + keycodeMap[sf::Keyboard::F10] = getKeycode(keySymbols, XK_F10); + keycodeMap[sf::Keyboard::F11] = getKeycode(keySymbols, XK_F11); + keycodeMap[sf::Keyboard::F12] = getKeycode(keySymbols, XK_F12); + keycodeMap[sf::Keyboard::F13] = getKeycode(keySymbols, XK_F13); + keycodeMap[sf::Keyboard::F14] = getKeycode(keySymbols, XK_F14); + keycodeMap[sf::Keyboard::F15] = getKeycode(keySymbols, XK_F15); + keycodeMap[sf::Keyboard::Pause] = getKeycode(keySymbols, XK_Pause); + + xcb_key_symbols_free(keySymbols); + + // Close the connection with the X server + sf::priv::CloseConnection(connection); + + mapBuilt = true; + } +} + namespace sf { @@ -41,138 +190,42 @@ namespace priv //////////////////////////////////////////////////////////// bool InputImpl::isKeyPressed(Keyboard::Key key) { - // Get the corresponding X11 keysym - KeySym keysym = 0; - switch (key) - { - case Keyboard::A: keysym = XK_A; break; - case Keyboard::B: keysym = XK_B; break; - case Keyboard::C: keysym = XK_C; break; - case Keyboard::D: keysym = XK_D; break; - case Keyboard::E: keysym = XK_E; break; - case Keyboard::F: keysym = XK_F; break; - case Keyboard::G: keysym = XK_G; break; - case Keyboard::H: keysym = XK_H; break; - case Keyboard::I: keysym = XK_I; break; - case Keyboard::J: keysym = XK_J; break; - case Keyboard::K: keysym = XK_K; break; - case Keyboard::L: keysym = XK_L; break; - case Keyboard::M: keysym = XK_M; break; - case Keyboard::N: keysym = XK_N; break; - case Keyboard::O: keysym = XK_O; break; - case Keyboard::P: keysym = XK_P; break; - case Keyboard::Q: keysym = XK_Q; break; - case Keyboard::R: keysym = XK_R; break; - case Keyboard::S: keysym = XK_S; break; - case Keyboard::T: keysym = XK_T; break; - case Keyboard::U: keysym = XK_U; break; - case Keyboard::V: keysym = XK_V; break; - case Keyboard::W: keysym = XK_W; break; - case Keyboard::X: keysym = XK_X; break; - case Keyboard::Y: keysym = XK_Y; break; - case Keyboard::Z: keysym = XK_Z; break; - case Keyboard::Num0: keysym = XK_0; break; - case Keyboard::Num1: keysym = XK_1; break; - case Keyboard::Num2: keysym = XK_2; break; - case Keyboard::Num3: keysym = XK_3; break; - case Keyboard::Num4: keysym = XK_4; break; - case Keyboard::Num5: keysym = XK_5; break; - case Keyboard::Num6: keysym = XK_6; break; - case Keyboard::Num7: keysym = XK_7; break; - case Keyboard::Num8: keysym = XK_8; break; - case Keyboard::Num9: keysym = XK_9; break; - case Keyboard::Escape: keysym = XK_Escape; break; - case Keyboard::LControl: keysym = XK_Control_L; break; - case Keyboard::LShift: keysym = XK_Shift_L; break; - case Keyboard::LAlt: keysym = XK_Alt_L; break; - case Keyboard::LSystem: keysym = XK_Super_L; break; - case Keyboard::RControl: keysym = XK_Control_R; break; - case Keyboard::RShift: keysym = XK_Shift_R; break; - case Keyboard::RAlt: keysym = XK_Alt_R; break; - case Keyboard::RSystem: keysym = XK_Super_R; break; - case Keyboard::Menu: keysym = XK_Menu; break; - case Keyboard::LBracket: keysym = XK_bracketleft; break; - case Keyboard::RBracket: keysym = XK_bracketright; break; - case Keyboard::SemiColon: keysym = XK_semicolon; break; - case Keyboard::Comma: keysym = XK_comma; break; - case Keyboard::Period: keysym = XK_period; break; - case Keyboard::Quote: keysym = XK_dead_acute; break; - case Keyboard::Slash: keysym = XK_slash; break; - case Keyboard::BackSlash: keysym = XK_backslash; break; - case Keyboard::Tilde: keysym = XK_dead_grave; break; - case Keyboard::Equal: keysym = XK_equal; break; - case Keyboard::Dash: keysym = XK_minus; break; - case Keyboard::Space: keysym = XK_space; break; - case Keyboard::Return: keysym = XK_Return; break; - case Keyboard::BackSpace: keysym = XK_BackSpace; break; - case Keyboard::Tab: keysym = XK_Tab; break; - case Keyboard::PageUp: keysym = XK_Prior; break; - case Keyboard::PageDown: keysym = XK_Next; break; - case Keyboard::End: keysym = XK_End; break; - case Keyboard::Home: keysym = XK_Home; break; - case Keyboard::Insert: keysym = XK_Insert; break; - case Keyboard::Delete: keysym = XK_Delete; break; - case Keyboard::Add: keysym = XK_KP_Add; break; - case Keyboard::Subtract: keysym = XK_KP_Subtract; break; - case Keyboard::Multiply: keysym = XK_KP_Multiply; break; - case Keyboard::Divide: keysym = XK_KP_Divide; break; - case Keyboard::Left: keysym = XK_Left; break; - case Keyboard::Right: keysym = XK_Right; break; - case Keyboard::Up: keysym = XK_Up; break; - case Keyboard::Down: keysym = XK_Down; break; - case Keyboard::Numpad0: keysym = XK_KP_0; break; - case Keyboard::Numpad1: keysym = XK_KP_1; break; - case Keyboard::Numpad2: keysym = XK_KP_2; break; - case Keyboard::Numpad3: keysym = XK_KP_3; break; - case Keyboard::Numpad4: keysym = XK_KP_4; break; - case Keyboard::Numpad5: keysym = XK_KP_5; break; - case Keyboard::Numpad6: keysym = XK_KP_6; break; - case Keyboard::Numpad7: keysym = XK_KP_7; break; - case Keyboard::Numpad8: keysym = XK_KP_8; break; - case Keyboard::Numpad9: keysym = XK_KP_9; break; - case Keyboard::F1: keysym = XK_F1; break; - case Keyboard::F2: keysym = XK_F2; break; - case Keyboard::F3: keysym = XK_F3; break; - case Keyboard::F4: keysym = XK_F4; break; - case Keyboard::F5: keysym = XK_F5; break; - case Keyboard::F6: keysym = XK_F6; break; - case Keyboard::F7: keysym = XK_F7; break; - case Keyboard::F8: keysym = XK_F8; break; - case Keyboard::F9: keysym = XK_F9; break; - case Keyboard::F10: keysym = XK_F10; break; - case Keyboard::F11: keysym = XK_F11; break; - case Keyboard::F12: keysym = XK_F12; break; - case Keyboard::F13: keysym = XK_F13; break; - case Keyboard::F14: keysym = XK_F14; break; - case Keyboard::F15: keysym = XK_F15; break; - case Keyboard::Pause: keysym = XK_Pause; break; - default: keysym = 0; break; - } + if (!mapBuilt) + buildMap(); - // Open a connection with the X server - Display* display = OpenDisplay(); - xcb_connection_t* connection = XGetXCBConnection(display); + // Sanity checks + if (key < 0 || key >= sf::Keyboard::KeyCount) + return false; // Convert to keycode - KeyCode keycode = XKeysymToKeycode(display, keysym); - if (keycode != 0) - { - // Get the whole keyboard state - ScopedXcbPtr keymap(xcb_query_keymap_reply(connection, xcb_query_keymap(connection), NULL)); + xcb_keycode_t keycode = keycodeMap[key]; - // Close the connection with the X server - CloseDisplay(display); + ScopedXcbPtr error(NULL); - // Check our keycode - return (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0; - } - else + // Open a connection with the X server + xcb_connection_t* connection = OpenConnection(); + + // Get the whole keyboard state + ScopedXcbPtr keymap( + xcb_query_keymap_reply( + connection, + xcb_query_keymap(connection), + &error + ) + ); + + // Close the connection with the X server + CloseConnection(connection); + + if (error) { - // Close the connection with the X server - CloseDisplay(display); + err() << "Failed to query keymap" << std::endl; return false; } + + // Check our keycode + return (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0; } @@ -189,13 +242,32 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button) // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); + ScopedXcbPtr error(NULL); + // Get pointer mask - ScopedXcbPtr pointer(xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XCBDefaultRootWindow(connection)), NULL)); - uint16_t buttons = pointer->mask; + ScopedXcbPtr pointer( + xcb_query_pointer_reply( + connection, + xcb_query_pointer( + connection, + XCBDefaultRootWindow(connection) + ), + &error + ) + ); // Close the connection with the X server CloseConnection(connection); + if (error) + { + err() << "Failed to query pointer" << std::endl; + + return false; + } + + uint16_t buttons = pointer->mask; + switch (button) { case Mouse::Left: return buttons & XCB_BUTTON_MASK_1; @@ -214,11 +286,29 @@ Vector2i InputImpl::getMousePosition() // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); - ScopedXcbPtr pointer(xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XCBDefaultRootWindow(connection)), NULL)); + ScopedXcbPtr error(NULL); + + ScopedXcbPtr pointer( + xcb_query_pointer_reply( + connection, + xcb_query_pointer( + connection, + XCBDefaultRootWindow(connection) + ), + &error + ) + ); // Close the connection with the X server CloseConnection(connection); + if (error) + { + err() << "Failed to query pointer" << std::endl; + + return Vector2i(0, 0); + } + return Vector2i(pointer->root_x, pointer->root_y); } @@ -232,11 +322,29 @@ Vector2i InputImpl::getMousePosition(const Window& relativeTo) // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); - ScopedXcbPtr pointer(xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL)); + ScopedXcbPtr error(NULL); + + ScopedXcbPtr pointer( + xcb_query_pointer_reply( + connection, + xcb_query_pointer( + connection, + XCBDefaultRootWindow(connection) + ), + &error + ) + ); // Close the connection with the X server CloseConnection(connection); + if (error) + { + err() << "Failed to query pointer" << std::endl; + + return Vector2i(0, 0); + } + return Vector2i(pointer->win_x, pointer->win_y); } else @@ -252,7 +360,21 @@ void InputImpl::setMousePosition(const Vector2i& position) // Open a connection with the X server xcb_connection_t* connection = OpenConnection(); - xcb_warp_pointer(connection, None, XCBDefaultRootWindow(connection), 0, 0, 0, 0, position.x, position.y); + ScopedXcbPtr error(xcb_request_check( + connection, + xcb_warp_pointer( + connection, + None, // Source window + XCBDefaultRootWindow(connection), // Destination window + 0, 0, // Source position + 0, 0, // Source size + position.x, position.y // Destination position + ) + )); + + if (error) + err() << "Failed to set mouse position" << std::endl; + xcb_flush(connection); // Close the connection with the X server @@ -269,7 +391,21 @@ void InputImpl::setMousePosition(const Vector2i& position, const Window& relativ WindowHandle handle = relativeTo.getSystemHandle(); if (handle) { - xcb_warp_pointer(connection, None, handle, 0, 0, 0, 0, position.x, position.y); + ScopedXcbPtr error(xcb_request_check( + connection, + xcb_warp_pointer( + connection, + None, // Source window + handle, // Destination window + 0, 0, // Source position + 0, 0, // Source size + position.x, position.y // Destination position + ) + )); + + if (error) + err() << "Failed to set mouse position" << std::endl; + xcb_flush(connection); } diff --git a/src/SFML/Window/Unix/VideoModeImpl.cpp b/src/SFML/Window/Unix/VideoModeImpl.cpp index cef67bcd..9ee812f2 100644 --- a/src/SFML/Window/Unix/VideoModeImpl.cpp +++ b/src/SFML/Window/Unix/VideoModeImpl.cpp @@ -48,64 +48,86 @@ std::vector VideoModeImpl::getFullscreenModes() // Retrieve the default screen xcb_screen_t* screen = XCBDefaultScreen(connection); - // Check if the XRandR extension is present - static const std::string RANDR = "RANDR"; - ScopedXcbPtr randr_ext(xcb_query_extension_reply( + ScopedXcbPtr error(NULL); + + const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(connection, &xcb_randr_id); + + if (!randrExt || !randrExt->present) + { + // Randr extension is not supported: we cannot get the video modes + err() << "Failed to use the RandR extension while trying to get the supported video modes" << std::endl; + + // Close the connection with the X server + CloseConnection(connection); + + return modes; + } + + // Load RandR and check its version + ScopedXcbPtr randrVersion(xcb_randr_query_version_reply( connection, - xcb_query_extension( + xcb_randr_query_version( connection, - RANDR.size(), - RANDR.c_str() - ), - NULL + 1, + 1 + ), + &error )); - if (randr_ext->present) + if (error) { - // Get the current configuration - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + err() << "Failed to load the RandR extension while trying to get the supported video modes" << std::endl; + + // Close the connection with the X server + CloseConnection(connection); + + return modes; + } + + // Get the current configuration + ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + connection, + xcb_randr_get_screen_info( connection, - xcb_randr_get_screen_info( - connection, - screen->root - ), - &error - )); + screen->root + ), + &error + )); - if (!error) + if (error) + { + // Failed to get the screen configuration + err() << "Failed to retrieve the screen configuration while trying to get the supported video modes" << std::endl; + + // Close the connection with the X server + CloseConnection(connection); + + return modes; + } + + // Get the available screen sizes + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); + if (sizes && (config->nSizes > 0)) + { + // Get the list of supported depths + xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen); + // Combine depths and sizes to fill the array of supported modes + for (; iter.rem; xcb_depth_next(&iter)) { - // Get the available screen sizes - xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); - if (sizes && (config->nSizes > 0)) + for (int j = 0; j < config->nSizes; ++j) { - // Get the list of supported depths - xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen); - // Combine depths and sizes to fill the array of supported modes - for (; iter.rem; xcb_depth_next(&iter)) - { - for (int j = 0; j < config->nSizes; ++j) - { - // Convert to VideoMode - VideoMode mode(sizes[j].width, sizes[j].height, iter.data->depth); + // 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); - } - } + if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 || + config->rotation == XCB_RANDR_ROTATION_ROTATE_270) + std::swap(mode.width, mode.height); + + // Add it only if it is not already in the array + if (std::find(modes.begin(), modes.end(), mode) == modes.end()) + modes.push_back(mode); } } - else - { - // Failed to get the screen configuration - err() << "Failed to retrieve the screen configuration while trying to get the supported video modes" << std::endl; - } - } - else - { - // XRandr extension is not supported: we cannot get the video modes - err() << "Failed to use the XRandR extension while trying to get the supported video modes" << std::endl; } // Close the connection with the X server @@ -126,52 +148,81 @@ VideoMode VideoModeImpl::getDesktopMode() // Retrieve the default screen xcb_screen_t* screen = XCBDefaultScreen(connection); - // Check if the XRandR extension is present - static const std::string RANDR = "RANDR"; - ScopedXcbPtr randr_ext(xcb_query_extension_reply( + ScopedXcbPtr error(NULL); + + // Check if the RandR extension is present + const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(connection, &xcb_randr_id); + + if (!randrExt || !randrExt->present) + { + // Randr extension is not supported: we cannot get the video modes + err() << "Failed to use the RandR extension while trying to get the desktop video mode" << std::endl; + + // Close the connection with the X server + CloseConnection(connection); + + return desktopMode; + } + + // Load RandR and check its version + ScopedXcbPtr randrVersion(xcb_randr_query_version_reply( connection, - xcb_query_extension( + xcb_randr_query_version( connection, - RANDR.size(), - RANDR.c_str() - ), - NULL + 1, + 1 + ), + &error )); - if (randr_ext->present) + if (error) { - // Get the current configuration - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + err() << "Failed to load the RandR extension while trying to get the desktop video mode" << std::endl; + + // Close the connection with the X server + CloseConnection(connection); + + return desktopMode; + } + + // Get the current configuration + ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + connection, + xcb_randr_get_screen_info( connection, - xcb_randr_get_screen_info( - connection, - screen->root - ), - &error - )); + screen->root + ), + &error + )); - if (!error) - { - // Get the current video mode - xcb_randr_mode_t currentMode = config->sizeID; + if (error) + { + // Failed to get the screen configuration + err() << "Failed to retrieve the screen configuration while trying to get the desktop video mode" << std::endl; - // Get the available screen sizes - int nbSizes = xcb_randr_get_screen_info_sizes_length(config.get()); - xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); - if (sizes && (nbSizes > 0)) - desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, screen->root_depth); - } - else - { - // Failed to get the screen configuration - err() << "Failed to retrieve the screen configuration while trying to get the desktop video modes" << std::endl; - } + // Close the connection with the X server + CloseConnection(connection); + + return desktopMode; + } + + // Get the current video mode + xcb_randr_mode_t currentMode = config->sizeID; + + // Get the available screen sizes + int nbSizes = xcb_randr_get_screen_info_sizes_length(config.get()); + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); + if (sizes && (nbSizes > 0)) + { + desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, screen->root_depth); + + if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 || + config->rotation == XCB_RANDR_ROTATION_ROTATE_270) + std::swap(desktopMode.width, desktopMode.height); } else { - // XRandr extension is not supported: we cannot get the video modes - err() << "Failed to use the XRandR extension while trying to get the desktop video modes" << std::endl; + err() << "Failed to retrieve any screen sizes while trying to get the desktop video mode" << std::endl; } // Close the connection with the X server diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index d6bb52b8..2af4deeb 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -31,22 +31,21 @@ #include #include #include -#include +#include +#include #include #include -#include #include -#include -#include +#include #include -#include -#include -#include +#include #include #include -#include -#include -#include +#include + +// So we don't have to require xcb dri2 to be present +#define XCB_DRI2_BUFFER_SWAP_COMPLETE 0 +#define XCB_DRI2_INVALIDATE_BUFFERS 1 #ifdef SFML_OPENGL_ES #include @@ -63,28 +62,240 @@ namespace { sf::priv::WindowImplX11* fullscreenWindow = NULL; std::vector allWindows; - unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | - PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | - EnterWindowMask | LeaveWindowMask; + sf::Mutex allWindowsMutex; + unsigned long eventMask = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_PRESS | + XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | + XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_KEY_PRESS | + XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW; // Find the name of the current executable std::string findExecutableName() { - struct stat linkStat; - if (!lstat("/proc/self/exe", &linkStat)) { - std::vector buffer(0, linkStat.st_size + 1); - std::size_t length = readlink("/proc/self/exe", &buffer[0], buffer.size()); - if ((length > 0) && (length < buffer.size())) - { - // Remove the path to keep the executable name only - buffer[length] = '\0'; - return basename(&buffer[0]); - } + // We use /proc/self/cmdline to get the command line + // the user used to invoke this instance of the application + int file = ::open("/proc/self/cmdline", O_RDONLY | O_NONBLOCK); + + if (file < 0) + return "sfml"; + + std::vector buffer(256, 0); + std::size_t offset = 0; + ssize_t result = 0; + + while ((result = read(file, &buffer[offset], 256)) > 0) + { + buffer.resize(buffer.size() + result, 0); + offset += result; } - //Default fallback name + ::close(file); + + if (offset) + { + buffer[offset] = 0; + + // Remove the path to keep the executable name only + return basename(&buffer[0]); + } + + // Default fallback name return "sfml"; } + + // Check if Extended Window Manager Hints are supported + bool ewmhSupported() + { + static bool checked = false; + static bool ewmhSupported = false; + + if (checked) + return ewmhSupported; + + checked = true; + + xcb_connection_t* connection = sf::priv::OpenConnection(); + xcb_ewmh_connection_t ewmhConnection; + + sf::priv::ScopedXcbPtr error(NULL); + + uint8_t result = xcb_ewmh_init_atoms_replies( + &ewmhConnection, + xcb_ewmh_init_atoms( + connection, + &ewmhConnection + ), + &error + ); + + if (!result || error) + { + xcb_ewmh_connection_wipe(&ewmhConnection); + sf::priv::CloseConnection(connection); + return false; + } + + xcb_window_t rootWindow; + + result = xcb_ewmh_get_supporting_wm_check_reply( + &ewmhConnection, + xcb_ewmh_get_supporting_wm_check( + &ewmhConnection, + sf::priv::XCBDefaultRootWindow(connection) + ), + &rootWindow, + &error + ); + + if (!result || error) + { + xcb_ewmh_connection_wipe(&ewmhConnection); + sf::priv::CloseConnection(connection); + return false; + } + + xcb_window_t childWindow; + + result = xcb_ewmh_get_supporting_wm_check_reply( + &ewmhConnection, + xcb_ewmh_get_supporting_wm_check( + &ewmhConnection, + rootWindow + ), + &childWindow, + &error + ); + + xcb_ewmh_connection_wipe(&ewmhConnection); + sf::priv::CloseConnection(connection); + + if (!result || error) + return false; + + // Conforming window managers should return the same window for both queries + if (rootWindow != childWindow) + return false; + + ewmhSupported = true; + return true; + } + + xcb_query_extension_reply_t getDriExtension() + { + xcb_connection_t* connection = sf::priv::OpenConnection(); + + sf::priv::ScopedXcbPtr error(NULL); + + // Check if the DRI2 extension is present + // We don't use xcb_get_extension_data here to avoid having to link to xcb_dri2 + static const std::string DRI2 = "DRI2"; + sf::priv::ScopedXcbPtr driExt(xcb_query_extension_reply( + connection, + xcb_query_extension( + connection, + DRI2.size(), + DRI2.c_str() + ), + &error + )); + + // Close the connection with the X server + sf::priv::CloseConnection(connection); + + if (error || !driExt || !driExt->present) + { + xcb_query_extension_reply_t reply; + std::memset(&reply, 0, sizeof(reply)); + return reply; + } + + return *driExt.get(); + } + + void dumpXcbExtensions() + { + xcb_connection_t* connection = sf::priv::OpenConnection(); + + sf::priv::ScopedXcbPtr error(NULL); + + // Check if the RandR extension is present + sf::priv::ScopedXcbPtr extensions(xcb_list_extensions_reply( + connection, + xcb_list_extensions( + connection + ), + &error + )); + + if (error || !extensions) + { + // Close the connection with the X server + sf::priv::CloseConnection(connection); + + sf::err() << "Couldn't get list of X extensions" << std::endl; + return; + } + + xcb_str_iterator_t iter = xcb_list_extensions_names_iterator(extensions.get()); + + sf::err() << "X Extensions:" << std::endl; + + while (iter.rem) + { + std::string name(xcb_str_name(iter.data), xcb_str_name_length(iter.data)); + + sf::priv::ScopedXcbPtr error(NULL); + + // Check if the RandR extension is present + sf::priv::ScopedXcbPtr extension(xcb_query_extension_reply( + connection, + xcb_query_extension( + connection, + name.size(), + name.c_str() + ), + &error + )); + + if (error || !extension || !extension->present) + { + sf::err() << "\t" << name << " - Failed to query" << std::endl; + continue; + } + + int firstEvent = extension->first_event; + + sf::err() << "\t" << name << " - First event: " << firstEvent << std::endl; + iter.data += xcb_str_name_length(iter.data) + 1; + --iter.rem; + } + + // Close the connection with the X server + sf::priv::CloseConnection(connection); + } + + void dumpUnhandledEvent(uint8_t type) + { + static std::vector types; + + // Check if we already reported this type + if (std::find(types.begin(), types.end(), type) != types.end()) + return; + + // Insert it if new + types.push_back(type); + + static bool dumpedExtensions = false; + + if (!dumpedExtensions) + { + dumpXcbExtensions(); + dumpedExtensions = true; + } + + sf::err() << "Unhandled event type: " << (static_cast(type) & ~0x80) << std::endl + << "Report this to the SFML maintainers if possible" << std::endl; + } } @@ -94,27 +305,49 @@ namespace priv { //////////////////////////////////////////////////////////// WindowImplX11::WindowImplX11(WindowHandle handle) : -m_window (0), -m_inputMethod (NULL), -m_inputContext(NULL), -m_isExternal (true), -m_atomClose (0), -m_oldVideoMode(-1), -m_hiddenCursor(0), -m_keyRepeat (true), -m_previousSize(-1, -1), -m_useSizeHints(false) +m_window (0), +m_inputMethod (NULL), +m_inputContext (NULL), +m_keySymbols (NULL), +m_isExternal (true), +m_atomWmProtocols(0), +m_atomClose (0), +m_hiddenCursor (0), +m_keyRepeat (true), +m_previousSize (-1, -1), +m_useSizeHints (false), +m_fullscreen (false) { // Open a connection with the X server m_display = OpenDisplay(); m_connection = XGetXCBConnection(m_display); + std::memset(&m_oldVideoMode, 0, sizeof(m_oldVideoMode)); + if (!m_connection) { err() << "Failed cast Display object to an XCB connection object" << std::endl; return; } + // Make sure to check for EWMH support before we do anything + if (ewmhSupported()) + { + ScopedXcbPtr error(NULL); + + uint8_t result = xcb_ewmh_init_atoms_replies( + &m_ewmhConnection, + xcb_ewmh_init_atoms( + m_connection, + &m_ewmhConnection + ), + &error + ); + + if (!result || error) + err() << "Failed to initialize EWMH atoms" << std::endl; + } + m_screen = XCBDefaultScreen(m_connection); XSetEventQueueOwner(m_display, XCBOwnsEventQueue); @@ -133,6 +366,9 @@ m_useSizeHints(false) value_list ); + // Set the WM protocols + setProtocols(); + // Do some common initializations initialize(); } @@ -141,48 +377,65 @@ m_useSizeHints(false) //////////////////////////////////////////////////////////// WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings) : -m_window (0), -m_inputMethod (NULL), -m_inputContext(NULL), -m_isExternal (false), -m_atomClose (0), -m_oldVideoMode(-1), -m_hiddenCursor(0), -m_keyRepeat (true), -m_previousSize(-1, -1), -m_useSizeHints(false) +m_window (0), +m_inputMethod (NULL), +m_inputContext (NULL), +m_keySymbols (NULL), +m_isExternal (false), +m_atomWmProtocols(0), +m_atomClose (0), +m_hiddenCursor (0), +m_keyRepeat (true), +m_previousSize (-1, -1), +m_useSizeHints (false), +m_fullscreen ((style & Style::Fullscreen) != 0) { // Open a connection with the X server m_display = OpenDisplay(); m_connection = XGetXCBConnection(m_display); + std::memset(&m_oldVideoMode, 0, sizeof(m_oldVideoMode)); + if (!m_connection) { err() << "Failed cast Display object to an XCB connection object" << std::endl; return; } + // Make sure to check for EWMH support before we do anything + if (ewmhSupported()) + { + ScopedXcbPtr error(NULL); + + uint8_t result = xcb_ewmh_init_atoms_replies( + &m_ewmhConnection, + xcb_ewmh_init_atoms( + m_connection, + &m_ewmhConnection + ), + &error + ); + + if (!result || error) + err() << "Failed to initialize EWMH atoms" << std::endl; + } + m_screen = XCBDefaultScreen(m_connection); XSetEventQueueOwner(m_display, XCBOwnsEventQueue); // Compute position and size - bool fullscreen = (style & Style::Fullscreen) != 0; - int left = fullscreen ? 0 : (m_screen->width_in_pixels - mode.width) / 2; - int top = fullscreen ? 0 : (m_screen->height_in_pixels - mode.height) / 2; + int left = m_fullscreen ? 0 : (m_screen->width_in_pixels - mode.width) / 2; + int top = m_fullscreen ? 0 : (m_screen->height_in_pixels - mode.height) / 2; int width = mode.width; int height = mode.height; - // Switch to fullscreen if necessary - if (fullscreen) - switchToFullscreen(mode); - // Choose the visual according to the context settings XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings); // Define the window attributes xcb_colormap_t colormap = xcb_generate_id(m_connection); xcb_create_colormap(m_connection, XCB_COLORMAP_ALLOC_NONE, colormap, m_screen->root, visualInfo.visualid); - const uint32_t value_list[] = {fullscreen, static_cast(eventMask), colormap}; + const uint32_t value_list[] = {m_fullscreen && !ewmhSupported(), static_cast(eventMask), colormap}; // Create the window m_window = xcb_generate_id(m_connection); @@ -210,128 +463,61 @@ m_useSizeHints(false) return; } - // Set the window's name - setTitle(title); + // Set the WM protocols + setProtocols(); + + // Set the WM initial state to the normal state + xcb_icccm_wm_hints_t hints; + std::memset(&hints, 0, sizeof(hints)); + xcb_icccm_wm_hints_set_normal(&hints); + xcb_icccm_set_wm_hints(m_connection, m_window, &hints); + + xcb_size_hints_t sizeHints; + std::memset(&sizeHints, 0, sizeof(sizeHints)); // Set the window's style (tell the window manager to change our window's decorations and functions according to the requested style) - if (!fullscreen) + setMotifHints(style); + + // This is a hack to force some windows managers to disable resizing + if (!(style & Style::Resize)) { - static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS"; - ScopedXcbPtr hintsAtomReply(xcb_intern_atom_reply( - m_connection, - xcb_intern_atom( - m_connection, - 0, - MOTIF_WM_HINTS.size(), - MOTIF_WM_HINTS.c_str() - ), - NULL - )); - - if (hintsAtomReply) - { - static const unsigned long MWM_HINTS_FUNCTIONS = 1 << 0; - static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1; - - //static const unsigned long MWM_DECOR_ALL = 1 << 0; - static const unsigned long MWM_DECOR_BORDER = 1 << 1; - static const unsigned long MWM_DECOR_RESIZEH = 1 << 2; - static const unsigned long MWM_DECOR_TITLE = 1 << 3; - static const unsigned long MWM_DECOR_MENU = 1 << 4; - static const unsigned long MWM_DECOR_MINIMIZE = 1 << 5; - static const unsigned long MWM_DECOR_MAXIMIZE = 1 << 6; - - //static const unsigned long MWM_FUNC_ALL = 1 << 0; - static const unsigned long MWM_FUNC_RESIZE = 1 << 1; - static const unsigned long MWM_FUNC_MOVE = 1 << 2; - static const unsigned long MWM_FUNC_MINIMIZE = 1 << 3; - static const unsigned long MWM_FUNC_MAXIMIZE = 1 << 4; - static const unsigned long MWM_FUNC_CLOSE = 1 << 5; - - struct WMHints - { - uint32_t flags; - uint32_t functions; - uint32_t decorations; - int32_t inputMode; - uint32_t state; - }; - - WMHints hints; - hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; - hints.decorations = 0; - hints.functions = 0; - - if (style & Style::Titlebar) - { - hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU; - hints.functions |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE; - } - if (style & Style::Resize) - { - hints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH; - hints.functions |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE; - } - if (style & Style::Close) - { - hints.decorations |= 0; - hints.functions |= MWM_FUNC_CLOSE; - } - - xcb_change_property( - m_connection, - XCB_PROP_MODE_REPLACE, - m_window, - hintsAtomReply->atom, - XCB_ATOM_WM_HINTS, - sizeof(hints.flags) * 8, - sizeof(hints) / sizeof(hints.flags), - reinterpret_cast(&hints) - ); - } - - // 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_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); - } + m_useSizeHints = true; + xcb_icccm_size_hints_set_min_size(&sizeHints, width, height); + xcb_icccm_size_hints_set_max_size(&sizeHints, width, height); } + // Set the WM hints of the normal state + xcb_icccm_set_wm_normal_hints(m_connection, m_window, &sizeHints); + // Set the window's WM class (this can be used by window managers) + // The WM_CLASS property actually consists of 2 parts, + // the instance name and the class name both of which should be + // null terminated strings. + // The instance name should be something unique to this invokation + // of the application but is rarely if ever used these days. + // For simplicity, we retrieve it via the base executable name. + // The class name identifies a class of windows that + // "are of the same type". We simply use the initial window name as + // the class name. std::string windowClass = findExecutableName(); - xcb_icccm_set_wm_class_checked(m_connection, m_window, windowClass.size(), windowClass.c_str()); + windowClass += '\0'; // Important to separate instance from class + windowClass += title.toAnsiString(); + + // We add 1 to the size of the string to include the null at the end + xcb_icccm_set_wm_class_checked(m_connection, m_window, windowClass.size() + 1, windowClass.c_str()); + + // Set the window's name + setTitle(title); // Do some common initializations initialize(); - // In fullscreen mode, we must grab keyboard and mouse inputs - if (fullscreen) + // Set fullscreen video mode and switch to fullscreen if necessary + if (m_fullscreen) { - 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 - ); + setPosition(Vector2i(0, 0)); + setVideoMode(mode); + switchToFullscreen(); } } @@ -357,14 +543,22 @@ WindowImplX11::~WindowImplX11() xcb_flush(m_connection); } + // Free key symbols + if (m_keySymbols) + xcb_key_symbols_free(m_keySymbols); + // Close the input method if (m_inputMethod) XCloseIM(m_inputMethod); + // Clean up the EWMH connection + xcb_ewmh_connection_wipe(&m_ewmhConnection); + // Close the connection with the X server CloseDisplay(m_display); // Remove this window from the global list of windows (required for focus request) + Lock lock(allWindowsMutex); allWindows.erase(std::find(allWindows.begin(), allWindows.end(), this)); } @@ -389,7 +583,17 @@ void WindowImplX11::processEvents() xcb_key_release_event_t* lastKeyReleaseEvent = NULL; uint8_t eventType = 0; - while((event = xcb_poll_for_event(m_connection))) + if (!m_xcbEvents.empty()) + { + event = m_xcbEvents.front(); + m_xcbEvents.pop_front(); + } + else + { + event = xcb_poll_for_event(m_connection); + } + + while (event) { eventType = event->response_type & ~0x80; @@ -410,29 +614,45 @@ void WindowImplX11::processEvents() // If there's still a key release event held back, process it now. if (lastKeyReleaseEvent) { - processEvent(reinterpret_cast(lastKeyReleaseEvent)); - free(lastKeyReleaseEvent); + if (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. + // Check if we're in charge of the key release + if (!passEvent(event, reinterpret_cast(event)->event)) + { + // Remember this key release event. + lastKeyReleaseEvent = reinterpret_cast(event); + event = NULL; // For safety reasons. + } } else { - processEvent(event); - free(event); + if (processEvent(event)) + free(event); + } + + if (!m_xcbEvents.empty()) + { + event = m_xcbEvents.front(); + m_xcbEvents.pop_front(); + } + else + { + event = xcb_poll_for_event(m_connection); } } // Process any held back release event. if (lastKeyReleaseEvent) { - processEvent(reinterpret_cast(lastKeyReleaseEvent)); - free(lastKeyReleaseEvent); + if (processEvent(reinterpret_cast(lastKeyReleaseEvent))) + free(lastKeyReleaseEvent); + lastKeyReleaseEvent = NULL; } } @@ -444,12 +664,28 @@ Vector2i WindowImplX11::getPosition() const ::Window topLevelWindow = m_window; ::Window nextWindow = topLevelWindow; + ScopedXcbPtr error(NULL); + // Get "top level" window, i.e. the window with the root window as its parent. while (nextWindow != m_screen->root) { topLevelWindow = nextWindow; - ScopedXcbPtr treeReply(xcb_query_tree_reply(m_connection, xcb_query_tree(m_connection, topLevelWindow), NULL)); + ScopedXcbPtr treeReply(xcb_query_tree_reply( + m_connection, + xcb_query_tree( + m_connection, + topLevelWindow + ), + &error + )); + + if (error) + { + err() << "Failed to get window position (query_tree)" << std::endl; + return Vector2i(0, 0); + } + nextWindow = treeReply->parent; } @@ -459,10 +695,16 @@ Vector2i WindowImplX11::getPosition() const m_connection, topLevelWindow ), - NULL + &error )); - return sf::Vector2i(geometryReply->x, geometryReply->y); + if (error) + { + err() << "Failed to get window position (get_geometry)" << std::endl; + return Vector2i(0, 0); + } + + return Vector2i(geometryReply->x, geometryReply->y); } @@ -492,19 +734,38 @@ 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); + std::memset(&sizeHints, 0, sizeof(sizeHints)); + xcb_icccm_size_hints_set_min_size(&sizeHints, size.x, size.y); + xcb_icccm_size_hints_set_max_size(&sizeHints, size.x, size.y); + + ScopedXcbPtr configureWindowError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_normal_hints( + m_connection, + m_window, + &sizeHints + ) + )); + + if (configureWindowError) + err() << "Failed to set window size hints" << std::endl; } uint32_t values[] = {size.x, size.y}; - xcb_configure_window( + + ScopedXcbPtr configureWindowError(xcb_request_check( m_connection, - m_window, - XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, - values - ); + xcb_configure_window( + m_connection, + m_window, + XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, + values + ) + )); + + if (configureWindowError) + err() << "Failed to set window size" << std::endl; + xcb_flush(m_connection); } @@ -513,22 +774,68 @@ void WindowImplX11::setSize(const Vector2u& size) void WindowImplX11::setTitle(const String& title) { // XCB takes UTF-8-encoded strings. - std::basic_string utf8String; - sf::Utf<32>::toUtf8( - title.begin(), title.end(), - std::back_inserter( utf8String ) - ); + std::string utf8String; + Utf<32>::toUtf8(title.begin(), title.end(), std::back_inserter(utf8String)); - xcb_change_property( + ScopedXcbPtr wmNameError(xcb_request_check( m_connection, - XCB_PROP_MODE_REPLACE, - m_window, - XCB_ATOM_WM_NAME, - XCB_ATOM_STRING, - sizeof(std::basic_string::value_type) * 8, - utf8String.length(), - utf8String.c_str() - ); + xcb_icccm_set_wm_name_checked( + m_connection, + m_window, + XCB_ATOM_STRING, + sizeof(std::basic_string::value_type) * 8, + utf8String.length(), + utf8String.c_str() + ) + )); + + if (wmNameError) + err() << "Failed to set window title" << std::endl; + + ScopedXcbPtr wmIconNameError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_icon_name_checked( + m_connection, + m_window, + XCB_ATOM_STRING, + sizeof(std::basic_string::value_type) * 8, + utf8String.length(), + utf8String.c_str() + ) + )); + + if (wmIconNameError) + err() << "Failed to set WM_ICON_NAME property" << std::endl; + + if (ewmhSupported()) + { + ScopedXcbPtr ewmhNameError(xcb_request_check( + m_connection, + xcb_ewmh_set_wm_name_checked( + &m_ewmhConnection, + m_window, + utf8String.length(), + utf8String.c_str() + ) + )); + + if (ewmhNameError) + err() << "Failed to set _NET_WM_NAME property" << std::endl; + + ScopedXcbPtr ewmhIconNameError(xcb_request_check( + m_connection, + xcb_ewmh_set_wm_icon_name_checked( + &m_ewmhConnection, + m_window, + utf8String.length(), + utf8String.c_str() + ) + )); + + if (ewmhIconNameError) + err() << "Failed to set _NET_WM_ICON_NAME property" << std::endl; + } + xcb_flush(m_connection); } @@ -548,19 +855,47 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 // Create the icon pixmap xcb_pixmap_t iconPixmap = xcb_generate_id(m_connection); - xcb_create_pixmap( + + ScopedXcbPtr createPixmapError(xcb_request_check( m_connection, - m_screen->root_depth, - iconPixmap, - m_screen->root, - width, - height - ); + xcb_create_pixmap_checked( + m_connection, + m_screen->root_depth, + iconPixmap, + m_screen->root, + width, + height + ) + )); + + if (createPixmapError) + { + err() << "Failed to set the window's icon (create_pixmap): "; + err() << "Error code " << static_cast(createPixmapError->error_code) << std::endl; + return; + } xcb_gcontext_t iconGC = xcb_generate_id(m_connection); - xcb_create_gc(m_connection, iconGC, iconPixmap, 0, NULL); - ScopedXcbPtr errptr(xcb_request_check( + ScopedXcbPtr createGcError(xcb_request_check( + m_connection, + xcb_create_gc( + m_connection, + iconGC, + iconPixmap, + 0, + NULL + ) + )); + + if (createGcError) + { + err() << "Failed to set the window's icon (create_gc): "; + err() << "Error code " << static_cast(createGcError->error_code) << std::endl; + return; + } + + ScopedXcbPtr putImageError(xcb_request_check( m_connection, xcb_put_image_checked( m_connection, @@ -578,11 +913,24 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 ) )); - xcb_free_gc(m_connection, iconGC); + ScopedXcbPtr freeGcError(xcb_request_check( + m_connection, + xcb_free_gc( + m_connection, + iconGC + ) + )); - if (errptr) + if (freeGcError) { - err() << "Failed to set the window's icon: Error code " << static_cast(errptr->error_code) << std::endl; + err() << "Failed to free icon GC: "; + err() << "Error code " << static_cast(freeGcError->error_code) << std::endl; + } + + if (putImageError) + { + err() << "Failed to set the window's icon (put_image): "; + err() << "Error code " << static_cast(putImageError->error_code) << std::endl; return; } @@ -618,12 +966,40 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 // Send our new icon to the window through the WMHints 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); + std::memset(&hints, 0, sizeof(hints)); + xcb_icccm_wm_hints_set_icon_pixmap(&hints, iconPixmap); + xcb_icccm_wm_hints_set_icon_mask(&hints, maskPixmap); + + ScopedXcbPtr setWmHintsError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_hints( + m_connection, + m_window, + &hints + ) + )); + + if (setWmHintsError) + { + err() << "Failed to set the window's icon (icccm_set_wm_hints): "; + err() << "Error code " << static_cast(setWmHintsError->error_code) << std::endl; + } xcb_flush(m_connection); + + ScopedXcbPtr freePixmapError(xcb_request_check( + m_connection, + xcb_free_pixmap_checked( + m_connection, + iconPixmap + ) + )); + + if (freePixmapError) + { + err() << "Failed to free icon pixmap: "; + err() << "Error code " << static_cast(freePixmapError->error_code) << std::endl; + } } @@ -631,9 +1007,31 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 void WindowImplX11::setVisible(bool visible) { if (visible) - xcb_map_window(m_connection, m_window); + { + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_map_window( + m_connection, + m_window + ) + )); + + if (error) + err() << "Failed to change window visibility" << std::endl; + } else - xcb_unmap_window(m_connection, m_window); + { + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_unmap_window( + m_connection, + m_window + ) + )); + + if (error) + err() << "Failed to change window visibility" << std::endl; + } xcb_flush(m_connection); } @@ -643,7 +1041,20 @@ void WindowImplX11::setVisible(bool visible) void WindowImplX11::setMouseCursorVisible(bool visible) { const uint32_t values = visible ? XCB_NONE : m_hiddenCursor; - xcb_change_window_attributes(m_connection, m_window, XCB_CW_CURSOR, &values); + + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_change_window_attributes( + m_connection, + m_window, + XCB_CW_CURSOR, + &values + ) + )); + + if (error) + err() << "Failed to change mouse cursor visibility" << std::endl; + xcb_flush(m_connection); } @@ -662,15 +1073,19 @@ void WindowImplX11::requestFocus() // Check the global list of windows to find out whether an SFML window has the focus // Note: can't handle console and other non-SFML windows belonging to the application. bool sfmlWindowFocused = false; - for (std::vector::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr) + { - if ((*itr)->hasFocus()) + Lock lock(allWindowsMutex); + for (std::vector::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr) { - sfmlWindowFocused = true; - break; + if ((*itr)->hasFocus()) + { + sfmlWindowFocused = true; + break; + } } } - + // Check if window is viewable (not on other desktop, ...) // TODO: Check also if minimized ScopedXcbPtr attributes(xcb_get_window_attributes_reply( @@ -684,24 +1099,24 @@ void WindowImplX11::requestFocus() if (!attributes) { - sf::err() << "Failed to check if window is viewable while requesting focus" << std::endl; + err() << "Failed to check if window is viewable while requesting focus" << std::endl; return; // error getting attribute } bool windowViewable = (attributes->map_state == XCB_MAP_STATE_VIEWABLE); - + if (sfmlWindowFocused && windowViewable) { // Another SFML window of this application has the focus and the current window is viewable: // steal focus (i.e. bring window to the front and give it input focus) - 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); + grabFocus(); } else { // Get current WM hints. xcb_icccm_wm_hints_t hints; + std::memset(&hints, 0, sizeof(hints)); + xcb_icccm_get_wm_hints_reply( m_connection, xcb_icccm_get_wm_hints_unchecked( @@ -714,7 +1129,7 @@ void WindowImplX11::requestFocus() // 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_wm_hints_set_urgency(&hints); xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints); } } @@ -723,109 +1138,286 @@ void WindowImplX11::requestFocus() //////////////////////////////////////////////////////////// bool WindowImplX11::hasFocus() const { + ScopedXcbPtr error(NULL); + ScopedXcbPtr reply(xcb_get_input_focus_reply( m_connection, xcb_get_input_focus_unchecked( m_connection ), - NULL + &error )); + if (error) + err() << "Failed to check if window has focus" << std::endl; + return (reply->focus == m_window); } //////////////////////////////////////////////////////////// -void WindowImplX11::switchToFullscreen(const VideoMode& mode) +void WindowImplX11::grabFocus() { - // Check if the XRandR extension is present - static const std::string RANDR = "RANDR"; - ScopedXcbPtr randr_ext(xcb_query_extension_reply( - m_connection, - xcb_query_extension( - m_connection, - RANDR.size(), - RANDR.c_str() - ), - NULL - )); - - if (randr_ext->present) + if (ewmhSupported()) { - // Get the current configuration - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + ScopedXcbPtr activeWindowError(xcb_request_check( m_connection, - xcb_randr_get_screen_info( - m_connection, - m_screen->root - ), - &error + xcb_ewmh_request_change_active_window( + &m_ewmhConnection, + XDefaultScreen(m_display), + m_window, + XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL, + XCB_CURRENT_TIME, + None + ) )); - if (!error) + if (activeWindowError) { - // Save the current video mode before we switch to fullscreen - m_oldVideoMode = config->sizeID; - - // Get the available screen sizes - xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); - if (sizes && (config->nSizes > 0)) - { - // Search a matching size - 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 - 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; - break; - } - } - } - } - else - { - // Failed to get the screen configuration - err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; + err() << "Failed to change active window (request_change_active_window)" << std::endl; + return; } } else { - // XRandR extension is not supported: we cannot use fullscreen mode - err() << "Fullscreen is not supported, switching to window mode" << std::endl; + ScopedXcbPtr setInputFocusError(xcb_request_check( + m_connection, + xcb_set_input_focus( + m_connection, + XCB_INPUT_FOCUS_POINTER_ROOT, + m_window, + XCB_CURRENT_TIME + ) + )); + + if (setInputFocusError) + { + err() << "Failed to change active window (set_input_focus)" << std::endl; + return; + } + + const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; + + ScopedXcbPtr configureWindowError(xcb_request_check( + m_connection, + xcb_configure_window( + m_connection, + m_window, + XCB_CONFIG_WINDOW_STACK_MODE, + values + ) + )); + + if (configureWindowError) + err() << "Failed to change active window (configure_window)" << std::endl; } } //////////////////////////////////////////////////////////// -void WindowImplX11::initialize() +void WindowImplX11::setVideoMode(const VideoMode& mode) { - // Get the atoms for registering the close event - static const std::string WM_DELETE_WINDOW_NAME = "WM_DELETE_WINDOW"; - ScopedXcbPtr deleteWindowAtomReply(xcb_intern_atom_reply( + // Skip mode switching if the new mode is equal to the desktop mode + if (mode == VideoMode::getDesktopMode()) + return; + + ScopedXcbPtr error(NULL); + + // Check if the RandR extension is present + const xcb_query_extension_reply_t* randrExt = xcb_get_extension_data(m_connection, &xcb_randr_id); + + if (!randrExt || !randrExt->present) + { + // RandR extension is not supported: we cannot use fullscreen mode + err() << "Fullscreen is not supported, switching to window mode" << std::endl; + return; + } + + // Load RandR and check its version + ScopedXcbPtr randrVersion(xcb_randr_query_version_reply( m_connection, - xcb_intern_atom( + xcb_randr_query_version( m_connection, - 0, - WM_DELETE_WINDOW_NAME.size(), - WM_DELETE_WINDOW_NAME.c_str() + 1, + 1 ), - NULL + &error )); + if (error) + { + err() << "Failed to load RandR, switching to window mode" << std::endl; + return; + } + + // Get the current configuration + ScopedXcbPtr config(xcb_randr_get_screen_info_reply( + m_connection, + xcb_randr_get_screen_info( + m_connection, + m_screen->root + ), + &error + )); + + if (error || !config) + { + // Failed to get the screen configuration + err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; + return; + } + + // Save the current video mode before we switch to fullscreen + m_oldVideoMode = *config.get(); + + // Get the available screen sizes + xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(config.get()); + + if (!sizes || !config->nSizes) + { + err() << "Failed to get the fullscreen sizes, switching to window mode" << std::endl; + return; + } + + // Search for a matching size + for (int i = 0; i < config->nSizes; ++i) + { + if (config->rotation == XCB_RANDR_ROTATION_ROTATE_90 || + config->rotation == XCB_RANDR_ROTATION_ROTATE_270) + std::swap(sizes[i].height, sizes[i].width); + + if ((sizes[i].width == static_cast(mode.width)) && + (sizes[i].height == static_cast(mode.height))) + { + // Switch to fullscreen mode + ScopedXcbPtr setScreenConfig(xcb_randr_set_screen_config_reply( + m_connection, + xcb_randr_set_screen_config( + m_connection, + config->root, + XCB_CURRENT_TIME, + config->config_timestamp, + i, + config->rotation, + config->rate + ), + &error + )); + + if (error) + err() << "Failed to set new screen configuration" << std::endl; + + // Set "this" as the current fullscreen window + fullscreenWindow = this; + return; + } + } + + err() << "Failed to find matching fullscreen size, switching to window mode" << std::endl; +} + + +//////////////////////////////////////////////////////////// +void WindowImplX11::resetVideoMode() +{ + if (fullscreenWindow == this) + { + // Get current screen info + ScopedXcbPtr error(NULL); + + // Reset the video mode + ScopedXcbPtr setScreenConfig(xcb_randr_set_screen_config_reply( + m_connection, + xcb_randr_set_screen_config( + m_connection, + m_oldVideoMode.root, + XCB_CURRENT_TIME, + m_oldVideoMode.config_timestamp, + m_oldVideoMode.sizeID, + m_oldVideoMode.rotation, + m_oldVideoMode.rate + ), + &error + )); + + if (error) + err() << "Failed to reset old screen configuration" << std::endl; + + // Reset the fullscreen window + fullscreenWindow = NULL; + } +} + + +//////////////////////////////////////////////////////////// +void WindowImplX11::switchToFullscreen() +{ + grabFocus(); + + if (ewmhSupported()) + { + // Create atom for _NET_WM_BYPASS_COMPOSITOR. + static const std::string netWmStateBypassCompositor = "_NET_WM_BYPASS_COMPOSITOR"; + ScopedXcbPtr compReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + False, + netWmStateBypassCompositor.size(), + netWmStateBypassCompositor.c_str() + ), + 0 + )); + + static const Uint32 bypassCompositor = 1; + + // Disable compositor. + ScopedXcbPtr compositorError(xcb_request_check( + m_connection, + xcb_change_property_checked( + m_connection, + XCB_PROP_MODE_REPLACE, + m_window, + compReply->atom, + XCB_ATOM_CARDINAL, + 32, + 1, + &bypassCompositor + ) + )); + + // Not being able to bypass the compositor is not a fatal error + if (compositorError) + err() << "xcb_change_property failed, unable to set _NET_WM_BYPASS_COMPOSITOR" << std::endl; + + ScopedXcbPtr wmStateError(xcb_request_check( + m_connection, + xcb_ewmh_request_change_wm_state( + &m_ewmhConnection, + XDefaultScreen(m_display), + m_window, + XCB_EWMH_WM_STATE_ADD, + m_ewmhConnection._NET_WM_STATE_FULLSCREEN, + 0, + XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL + ) + )); + + if (wmStateError) + { + err() << "Setting fullscreen failed, could not change WM state" << std::endl; + return; + } + } +} + + +//////////////////////////////////////////////////////////// +void WindowImplX11::setProtocols() +{ + ScopedXcbPtr error(NULL); + + // Get the atom for setting the window protocols we support static const std::string WM_PROTOCOLS_NAME = "WM_PROTOCOLS"; ScopedXcbPtr protocolsAtomReply(xcb_intern_atom_reply( m_connection, @@ -835,27 +1427,180 @@ void WindowImplX11::initialize() WM_PROTOCOLS_NAME.size(), WM_PROTOCOLS_NAME.c_str() ), - NULL + &error )); - if (protocolsAtomReply && deleteWindowAtomReply) + if (error || !protocolsAtomReply) { - xcb_icccm_set_wm_protocols( - m_connection, - m_window, - protocolsAtomReply->atom, - 1, - &deleteWindowAtomReply->atom - ); + err() << "Failed to request WM_PROTOCOLS atom." << std::endl; + return; + } + m_atomWmProtocols = protocolsAtomReply->atom; + + std::vector atoms; + + // Get the atom for registering the close event + static const std::string WM_DELETE_WINDOW_NAME = "WM_DELETE_WINDOW"; + ScopedXcbPtr deleteWindowAtomReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + WM_DELETE_WINDOW_NAME.size(), + WM_DELETE_WINDOW_NAME.c_str() + ), + &error + )); + + if (!error && deleteWindowAtomReply) + { + atoms.push_back(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; + err() << "Failed to request WM_DELETE_WINDOW atom." << std::endl; } + if (ewmhSupported() && m_ewmhConnection._NET_WM_PING && m_ewmhConnection._NET_WM_PID) + { + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_ewmh_set_wm_pid( + &m_ewmhConnection, + m_window, + getpid() + ) + )); + + if (!error) + atoms.push_back(m_ewmhConnection._NET_WM_PING); + } + + if (!atoms.empty()) + { + ScopedXcbPtr error(xcb_request_check( + m_connection, + xcb_icccm_set_wm_protocols( + m_connection, + m_window, + protocolsAtomReply->atom, + atoms.size(), + &atoms[0] + ) + )); + + if (error) + err() << "Failed to set window protocols" << std::endl; + } + else + { + err() << "Didn't set any window protocols" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +void WindowImplX11::setMotifHints(unsigned long style) +{ + ScopedXcbPtr error(NULL); + + static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS"; + ScopedXcbPtr hintsAtomReply(xcb_intern_atom_reply( + m_connection, + xcb_intern_atom( + m_connection, + 0, + MOTIF_WM_HINTS.size(), + MOTIF_WM_HINTS.c_str() + ), + &error + )); + + if (!error && hintsAtomReply) + { + static const unsigned long MWM_HINTS_FUNCTIONS = 1 << 0; + static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1; + + //static const unsigned long MWM_DECOR_ALL = 1 << 0; + static const unsigned long MWM_DECOR_BORDER = 1 << 1; + static const unsigned long MWM_DECOR_RESIZEH = 1 << 2; + static const unsigned long MWM_DECOR_TITLE = 1 << 3; + static const unsigned long MWM_DECOR_MENU = 1 << 4; + static const unsigned long MWM_DECOR_MINIMIZE = 1 << 5; + static const unsigned long MWM_DECOR_MAXIMIZE = 1 << 6; + + //static const unsigned long MWM_FUNC_ALL = 1 << 0; + static const unsigned long MWM_FUNC_RESIZE = 1 << 1; + static const unsigned long MWM_FUNC_MOVE = 1 << 2; + static const unsigned long MWM_FUNC_MINIMIZE = 1 << 3; + static const unsigned long MWM_FUNC_MAXIMIZE = 1 << 4; + static const unsigned long MWM_FUNC_CLOSE = 1 << 5; + + struct WMHints + { + uint32_t flags; + uint32_t functions; + uint32_t decorations; + int32_t inputMode; + uint32_t state; + }; + + WMHints hints; + hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; + hints.decorations = 0; + hints.functions = 0; + hints.inputMode = 0; + hints.state = 0; + + // Decorate only if not in fullscreen + if (!(style & Style::Fullscreen)) + { + if (style & Style::Titlebar) + { + hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU; + hints.functions |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE; + } + if (style & Style::Resize) + { + hints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH; + hints.functions |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE; + } + if (style & Style::Close) + { + hints.decorations |= 0; + hints.functions |= MWM_FUNC_CLOSE; + } + } + + ScopedXcbPtr propertyError(xcb_request_check( + m_connection, + xcb_change_property_checked( + m_connection, + XCB_PROP_MODE_REPLACE, + m_window, + hintsAtomReply->atom, + hintsAtomReply->atom, + sizeof(hints.flags) * 8, + sizeof(hints) / sizeof(hints.flags), + reinterpret_cast(&hints) + ) + )); + + if (propertyError) + err() << "xcb_change_property failed, could not set window hints" << std::endl; + } + else + { + err() << "Failed to request _MOTIF_WM_HINTS atom." << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +void WindowImplX11::initialize() +{ // Create the input context m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL); @@ -865,7 +1610,7 @@ void WindowImplX11::initialize() m_inputMethod, XNClientWindow, m_window, - XNFocusWindow, + XNFocusWindow, m_window, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, @@ -880,9 +1625,17 @@ void WindowImplX11::initialize() if (!m_inputContext) err() << "Failed to create input context for window -- TextEntered event won't be able to return unicode" << std::endl; + // Allocate our key symbols + m_keySymbols = xcb_key_symbols_alloc(m_connection); + + if (!m_keySymbols) + err() << "Failed to allocate key symbols" << std::endl; + // Show the window - xcb_map_window(m_connection, m_window); - xcb_flush(m_connection); + setVisible(true); + + // Raise the window and grab input focus + grabFocus(); // Create the hidden cursor createHiddenCursor(); @@ -891,6 +1644,7 @@ void WindowImplX11::initialize() xcb_flush(m_connection); // Add this window to the global list of windows (required for focus request) + Lock lock(allWindowsMutex); allWindows.push_back(this); } @@ -899,24 +1653,57 @@ 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) - xcb_create_pixmap(m_connection, 1, cursorPixmap, m_window, 1, 1); + ScopedXcbPtr createPixmapError(xcb_request_check( + m_connection, + xcb_create_pixmap( + m_connection, + 1, + cursorPixmap, + m_window, + 1, + 1 + ) + )); + + if (createPixmapError) + { + err() << "Failed to create pixmap for hidden cursor" << std::endl; + return; + } + + m_hiddenCursor = xcb_generate_id(m_connection); // Create the cursor, using the pixmap as both the shape and the mask of the cursor - xcb_create_cursor( + ScopedXcbPtr createCursorError(xcb_request_check( m_connection, - m_hiddenCursor, - cursorPixmap, - cursorPixmap, - 0, 0, 0, // Foreground RGB color - 0, 0, 0, // Background RGB color - 0, // X - 0 // Y - ); + xcb_create_cursor( + m_connection, + m_hiddenCursor, + cursorPixmap, + cursorPixmap, + 0, 0, 0, // Foreground RGB color + 0, 0, 0, // Background RGB color + 0, // X + 0 // Y + ) + )); + + if (createCursorError) + err() << "Failed to create hidden cursor" << std::endl; // We don't need the pixmap any longer, free it - xcb_free_pixmap(m_connection, cursorPixmap); + ScopedXcbPtr freePixmapError(xcb_request_check( + m_connection, + xcb_free_pixmap( + m_connection, + cursorPixmap + ) + )); + + if (freePixmapError) + err() << "Failed to free pixmap for hidden cursor" << std::endl; } @@ -924,36 +1711,7 @@ void WindowImplX11::createHiddenCursor() void WindowImplX11::cleanup() { // Restore the previous video mode (in case we were running in fullscreen) - if (fullscreenWindow == this) - { - // Get current screen info - ScopedXcbPtr error(NULL); - ScopedXcbPtr config(xcb_randr_get_screen_info_reply( - m_connection, - xcb_randr_get_screen_info( - m_connection, - m_screen->root - ), - &error - )); - - if (!error) - { - // Reset the video mode - xcb_randr_set_screen_config( - m_connection, - m_screen->root, - CurrentTime, - config->config_timestamp, - m_oldVideoMode, - config->rotation, - config->rate - ); - } - - // Reset the fullscreen window - fullscreenWindow = NULL; - } + resetVideoMode(); // Unhide the mouse cursor (in case it was hidden) setMouseCursorVisible(true); @@ -969,6 +1727,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Destroy event case XCB_DESTROY_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->window)) + return false; + // The window is about to be destroyed: we must cleanup resources cleanup(); break; @@ -977,6 +1738,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Gain focus event case XCB_FOCUS_IN: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + // Update the input context if (m_inputContext) XSetICFocus(m_inputContext); @@ -987,19 +1751,39 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // If the window has been previously marked urgent (notification) as a result of a focus request, undo that xcb_icccm_wm_hints_t hints; - xcb_icccm_get_wm_hints_reply( + + ScopedXcbPtr error(NULL); + + uint8_t result = xcb_icccm_get_wm_hints_reply( m_connection, xcb_icccm_get_wm_hints_unchecked( m_connection, m_window ), &hints, - NULL + &error ); + if (!result || error) + { + err() << "Failed to get WM hints in XCB_FOCUS_IN" << std::endl; + break; + } + // Remove urgency (notification) flag from hints - hints.flags &= ~XUrgencyHint; - xcb_icccm_set_wm_hints_checked(m_connection, m_window, &hints); + hints.flags &= ~XCB_ICCCM_WM_HINT_X_URGENCY; + + ScopedXcbPtr setWmHintsError(xcb_request_check( + m_connection, + xcb_icccm_set_wm_hints_checked( + m_connection, + m_window, + &hints + ) + )); + + if (setWmHintsError) + err() << "Failed to set WM hints in XCB_FOCUS_IN" << std::endl; break; } @@ -1007,6 +1791,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Lost focus event case XCB_FOCUS_OUT: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + // Update the input context if (m_inputContext) XUnsetICFocus(m_inputContext); @@ -1020,6 +1807,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Resize event case XCB_CONFIGURE_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->window)) + return false; + xcb_configure_notify_event_t* e = reinterpret_cast(windowEvent); Event event; event.type = Event::Resized; @@ -1032,12 +1822,40 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Close event case XCB_CLIENT_MESSAGE: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->window)) + return false; + xcb_client_message_event_t* e = reinterpret_cast(windowEvent); - if ((e->format == 32) && (e->data.data32[0]) == static_cast(m_atomClose)) + + // Handle window manager protocol messages we support + if (e->type == m_atomWmProtocols) { - Event event; - event.type = Event::Closed; - pushEvent(event); + if ((e->format == 32) && (e->data.data32[0]) == static_cast(m_atomClose)) + { + // Handle the WM_DELETE_WINDOW message + Event event; + event.type = Event::Closed; + pushEvent(event); + } + else if (ewmhSupported() && (e->format == 32) && (e->data.data32[0]) == static_cast(m_ewmhConnection._NET_WM_PING)) + { + // Handle the _NET_WM_PING message, send pong back to WM to show that we are responsive + e->window = XCBDefaultRootWindow(m_connection); + + ScopedXcbPtr pongError(xcb_request_check( + m_connection, + xcb_send_event_checked( + m_connection, + 0, + XCBDefaultRootWindow(m_connection), + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + reinterpret_cast(e) + ) + )); + + if (pongError) + err() << "Could not send pong event back to WM" << std::endl; + } } break; } @@ -1045,23 +1863,14 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Key down event case XCB_KEY_PRESS: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + 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; - - // 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); + // We don't pass e->state as the last parameter because we want the unmodified keysym + xcb_keysym_t symbol = xcb_key_press_lookup_keysym(m_keySymbols, e, 0); // Fill the event parameters // TODO: if modifiers are wrong, use XGetModifierMapping to retrieve the actual modifiers mapping @@ -1074,8 +1883,15 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) event.key.system = e->state & XCB_MOD_MASK_4; pushEvent(event); + XEvent fakeEvent; + fakeEvent.type = KeyPress; + fakeEvent.xany.display = m_display; + fakeEvent.xany.window = e->event; + fakeEvent.xkey.state = e->state; + fakeEvent.xkey.keycode = e->detail; + // Generate a TextEntered event - if (!XFilterEvent(&fake_event, None)) + if (!XFilterEvent(&fakeEvent, None)) { #ifdef X_HAVE_UTF8_STRING if (m_inputContext) @@ -1085,7 +1901,7 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) int length = Xutf8LookupString( m_inputContext, - &fake_event.xkey, + &fakeEvent.xkey, reinterpret_cast(keyBuffer), sizeof(keyBuffer), NULL, @@ -1110,7 +1926,7 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) { static XComposeStatus status; char keyBuffer[16]; - if (XLookupString(&fake_event.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status)) + if (XLookupString(&fakeEvent.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status)) { Event textEvent; textEvent.type = Event::TextEntered; @@ -1126,19 +1942,14 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Key up event case XCB_KEY_RELEASE: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_key_release_event_t* e = reinterpret_cast(windowEvent); // Get the keysym of the key that has been pressed - char buffer[32]; - KeySym symbol; - - // 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); + // We don't pass e->state as the last parameter because we want the unmodified keysym + xcb_keysym_t symbol = xcb_key_release_lookup_keysym(m_keySymbols, e, 0); // Fill the event parameters Event event; @@ -1156,6 +1967,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse button pressed case XCB_BUTTON_PRESS: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_button_press_event_t* e = reinterpret_cast(windowEvent); // XXX: Why button 8 and 9? @@ -1187,6 +2001,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse button released case XCB_BUTTON_RELEASE: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_button_release_event_t* e = reinterpret_cast(windowEvent); xcb_button_t button = e->detail; @@ -1241,6 +2058,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse moved case XCB_MOTION_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_motion_notify_event_t* e = reinterpret_cast(windowEvent); Event event; event.type = Event::MouseMoved; @@ -1253,6 +2073,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse entered case XCB_ENTER_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_enter_notify_event_t* enterNotifyEvent = reinterpret_cast(windowEvent); if (enterNotifyEvent->mode == NotifyNormal) @@ -1267,6 +2090,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Mouse left case XCB_LEAVE_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->event)) + return false; + xcb_leave_notify_event_t* leaveNotifyEvent = reinterpret_cast(windowEvent); if (leaveNotifyEvent->mode == NotifyNormal) @@ -1281,15 +2107,124 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) // Parent window changed case XCB_REPARENT_NOTIFY: { + if (passEvent(windowEvent, reinterpret_cast(windowEvent)->window)) + return false; + + // Catch reparent events to properly apply fullscreen on + // some "strange" window managers (like Awesome) which + // seem to make use of temporary parents during mapping + if (m_fullscreen) + switchToFullscreen(); + xcb_flush(m_connection); // Discard remaining events break; } + + // The stuff that might pop up but we don't care about + // Whitelist more when necessary + // Window visibility changed (hide/unhide) + case XCB_MAP_NOTIFY: + case XCB_UNMAP_NOTIFY: + { + break; + } + + // Handle the rest + default: + { + uint8_t responseType = windowEvent->response_type & ~0x80; + + // Handle any extension events first + + // DRI2 + static xcb_query_extension_reply_t driExtension = getDriExtension(); + if (driExtension.present) + { + // Because we are using the XCB event queue instead of the Xlib event + // queue, Mesa breaks a bit (VSync among other things) because DRI2 still + // expects certain Xlib events to come its way. We work around this by + // emulating the old Xlib event queue for these specific wire events. + // Sources (retrieved 27 Mar 2015): + // http://wrl.illest.net/post/45342765813/code-tip-glx-and-xcbownseventqueue + // https://bugs.freedesktop.org/show_bug.cgi?id=42131 + // https://bugs.freedesktop.org/show_bug.cgi?id=35945 + // Mesa src/glx/dri2.c + // QtBase src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp + // QtBase Commit bb22b4965070409df4658f16fdf549f0362e8a9c + // Qt Change-Id I3b4ef3f6e3efbae25f49f161e229e9b15e951778 + // QtBase Commit 13c5c81cfa934c9b610720fe79e07465b00ebc8d + // Qt Change-Id Ia93eb8be1cbbc3d8ae7913a934c195af6b5ec538 + // QtBase Commit decb88693c8a7f0c073889b91151f01a850e3adf + // Qt Change-Id Ic466ff26487937b03f072a57e0ee4df335492a5f + if ((responseType == driExtension.first_event + XCB_DRI2_BUFFER_SWAP_COMPLETE) || + (responseType == driExtension.first_event + XCB_DRI2_INVALIDATE_BUFFERS)) + { + // DRI2 BufferSwapComplete and InvalidateBuffers + + // We lock/unlock the display to protect against concurrent access + XLockDisplay(m_display); + + typedef Bool (*wireEventHandler)(Display*, XEvent*, xEvent*); + + // Probe for any handlers that are registered for this event type + wireEventHandler handler = XESetWireToEvent(m_display, responseType, 0); + + if (handler) + { + // Restore the previous handler if one was registered + XESetWireToEvent(m_display, responseType, handler); + + XEvent event; + windowEvent->sequence = LastKnownRequestProcessed(m_display); + + // Pretend to be the Xlib event queue + handler(m_display, &event, reinterpret_cast(windowEvent)); + } + + XUnlockDisplay(m_display); + + return true; + } + } + + // Print any surprises to stderr (would be nice if people report when this happens) + dumpUnhandledEvent(responseType); + + break; + } } return true; } +//////////////////////////////////////////////////////////// +bool WindowImplX11::passEvent(xcb_generic_event_t* windowEvent, xcb_window_t window) +{ + // Check if this is our event + if (window == m_window) + return false; + + Lock lock(allWindowsMutex); + + // If we are the only window, there is nobody else to pass to + if (allWindows.size() == 1) + return false; + + for (std::vector::iterator i = allWindows.begin(); i != allWindows.end(); ++i) + { + if ((*i)->m_window == window) + { + (*i)->m_xcbEvents.push_back(windowEvent); + return true; + } + } + + // It seems nobody wants the event, we'll just handle it ourselves + return false; +} + + //////////////////////////////////////////////////////////// Keyboard::Key WindowImplX11::keysymToSF(xcb_keysym_t symbol) { @@ -1317,9 +2252,9 @@ Keyboard::Key WindowImplX11::keysymToSF(xcb_keysym_t symbol) case XK_bracketright: return Keyboard::RBracket; case XK_comma: return Keyboard::Comma; case XK_period: return Keyboard::Period; - case XK_dead_acute: return Keyboard::Quote; + case XK_apostrophe: return Keyboard::Quote; case XK_backslash: return Keyboard::BackSlash; - case XK_dead_grave: return Keyboard::Tilde; + case XK_grave: return Keyboard::Tilde; case XK_space: return Keyboard::Space; case XK_Return: return Keyboard::Return; case XK_KP_Enter: return Keyboard::Return; diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 1b7527a5..65427907 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -32,7 +32,10 @@ #include #include #include -#include +#include +#include +#include +#include namespace sf @@ -180,12 +183,42 @@ protected: private: //////////////////////////////////////////////////////////// - /// \brief Switch to fullscreen mode + /// \brief Request the WM to make the current window active + /// + //////////////////////////////////////////////////////////// + void grabFocus(); + + //////////////////////////////////////////////////////////// + /// \brief Set fullscreen video mode /// /// \param Mode video mode to switch to /// //////////////////////////////////////////////////////////// - void switchToFullscreen(const VideoMode& mode); + void setVideoMode(const VideoMode& mode); + + //////////////////////////////////////////////////////////// + /// \brief Reset to desktop video mode + /// + //////////////////////////////////////////////////////////// + void resetVideoMode(); + + //////////////////////////////////////////////////////////// + /// \brief Switch to fullscreen mode + /// + //////////////////////////////////////////////////////////// + void switchToFullscreen(); + + //////////////////////////////////////////////////////////// + /// \brief Set the WM protocols we support + /// + //////////////////////////////////////////////////////////// + void setProtocols(); + + //////////////////////////////////////////////////////////// + /// \brief Set Motif WM hints + /// + //////////////////////////////////////////////////////////// + void setMotifHints(unsigned long style); //////////////////////////////////////////////////////////// /// \brief Do some common initializations after the window has been created @@ -213,7 +246,18 @@ private: /// \return True if the event was processed, false if it was discarded /// //////////////////////////////////////////////////////////// - bool processEvent(xcb_generic_event_t *windowEvent); + bool processEvent(xcb_generic_event_t* windowEvent); + + //////////////////////////////////////////////////////////// + /// \brief Pass an incoming event to another window + /// + /// \param windowEvent Event which is being processed + /// \param window Window to pass to + /// + /// \return True if the event was passed to another window, false if it is destined for the current window + /// + //////////////////////////////////////////////////////////// + bool passEvent(xcb_generic_event_t* windowEvent, xcb_window_t window); //////////////////////////////////////////////////////////// /// \brief Convert a X11 keysym to SFML key code @@ -228,19 +272,24 @@ 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 - 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? + xcb_window_t m_window; ///< xcb identifier defining our window + ::Display* m_display; ///< Pointer to the display + xcb_connection_t* m_connection; ///< Pointer to the xcb connection + xcb_ewmh_connection_t m_ewmhConnection; ///< xcb EWMH 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 + xcb_key_symbols_t* m_keySymbols; ///< Symbols used to look up keysyms + std::deque m_xcbEvents; ///< Events that were received in another window's loop + bool m_isExternal; ///< Tell whether the window has been created externally or by SFML + Atom m_atomWmProtocols; ///< Atom used to identify WM protocol messages + Atom m_atomClose; ///< Atom used to identify the close event + xcb_randr_get_screen_info_reply_t m_oldVideoMode; ///< Video mode in use before we switch to fullscreen + Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one + bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? + Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only) + bool m_useSizeHints; ///< Is the size of the window fixed with size hints? + bool m_fullscreen; ///< Is window in fullscreen? }; } // namespace priv