Adjusted and fixed XCB patch.

* Adjusted xcb_icccm calls (for recent XCB versions).
* Fixed wrong parameter order in xcb_icccm_set_wm_protocols call.
* Fixed XCB_BUTTON_RELEASE spawning a MouseButtonPressed event.
* Moved files from obsolete Linux/ to Unix/ directory.
* Added m_useSizeHints fix.
* setTitle() converts to UTF-8 before passing to XCB -> Unicode window title
  support.
* Added XCB-util dependency.
* Replaced XSelectInput. Obtaining XCB connection when taking window handle.
* Adjusted X11 example for XCB.
* Removed AutoPointer, replaced by direct XCB and free() calls.
* Added key repeat workaround.
This commit is contained in:
Stefan Schindler 2014-04-24 12:55:47 +02:00
parent 135c1716e8
commit c7549cd292
7 changed files with 307 additions and 346 deletions

View File

@ -3,7 +3,8 @@
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window.hpp> #include <SFML/Window.hpp>
#include <X11/Xlib.h> #include <SFML/System/Err.hpp>
#include <X11/Xlib-xcb.h>
#include <GL/gl.h> #include <GL/gl.h>
#include <GL/glu.h> #include <GL/glu.h>
#include <iostream> #include <iostream>
@ -55,9 +56,9 @@ void draw(sf::Window& window, float elapsedTime)
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
glTranslatef(0.f, 0.f, -200.f); glTranslatef(0.f, 0.f, -200.f);
glRotatef(elapsedTime * 0.05f, 1.f, 0.f, 0.f); glRotatef(elapsedTime * 10.f, 1.f, 0.f, 0.f);
glRotatef(elapsedTime * 0.03f, 0.f, 1.f, 0.f); glRotatef(elapsedTime * 6.f, 0.f, 1.f, 0.f);
glRotatef(elapsedTime * 0.09f, 0.f, 0.f, 1.f); glRotatef(elapsedTime * 18.f, 0.f, 0.f, 1.f);
// Draw a cube // Draw a cube
glBegin(GL_QUADS); glBegin(GL_QUADS);
@ -115,85 +116,104 @@ int main()
if (!display) if (!display)
return EXIT_FAILURE; return EXIT_FAILURE;
// Get the default screen // Get the XCB connection for the opened display.
int screen = DefaultScreen(display); xcb_connection_t* xcbConnection = XGetXCBConnection(display);
// Let's create the main window if (!xcbConnection)
XSetWindowAttributes attributes; {
attributes.background_pixel = BlackPixel(display, screen); sf::err() << "Failed to get the XCB connection for opened display." << std::endl;
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)
return EXIT_FAILURE; return EXIT_FAILURE;
}
// Set the window's name // Get XCB screen.
XStoreName(display, window , "SFML Window"); 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 if (!screen)
Window view1 = XCreateWindow(display, window, {
10, 10, 310, 310, 0, sf::err() << "Failed to get the XCB screen." << std::endl;
DefaultDepth(display, screen), return EXIT_FAILURE;
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);
// Show our windows // Generate the XCB window IDs.
XMapWindow(display, window); xcb_window_t rootWindowId = xcb_generate_id(xcbConnection);
XFlush(display); 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 // Create our SFML views
sf::Window SFMLView1(view1); sf::Window sfmlView1(view1WindowId);
sf::Window SFMLView2(view2); sf::Window sfmlView2(view2WindowId);
// Create a clock for measuring elapsed time // Create a clock for measuring elapsed time
sf::Clock clock; sf::Clock clock;
// Initialize our views // Initialize our views
initialize(SFMLView1); initialize(sfmlView1);
initialize(SFMLView2); initialize(sfmlView2);
// Start the event loop // Start the event loop
bool running = true; bool running = true;
xcb_generic_event_t* event = NULL;
while (running) 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; running = false;
break;
}
} }
// Draw something into our views // Draw something into our views
draw(SFMLView1, clock.getElapsedTime().asSeconds()); draw(sfmlView1, clock.getElapsedTime().asSeconds());
draw(SFMLView2, clock.getElapsedTime().asSeconds() * 0.3f); draw(sfmlView2, clock.getElapsedTime().asSeconds() * 0.3f);
// Display the views on screen // Display the views on screen
SFMLView1.display(); sfmlView1.display();
SFMLView2.display(); sfmlView2.display();
} }
// Close the display
XCloseDisplay(display);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -69,8 +69,6 @@ if(SFML_OS_WINDOWS)
add_definitions(-DUNICODE -D_UNICODE) add_definitions(-DUNICODE -D_UNICODE)
elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD) elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD)
set(PLATFORM_SRC set(PLATFORM_SRC
${SRCROOT}/Linux/AutoPointer.cpp
${SRCROOT}/Linux/AutoPointer.hpp
${SRCROOT}/Unix/Display.cpp ${SRCROOT}/Unix/Display.cpp
${SRCROOT}/Unix/Display.hpp ${SRCROOT}/Unix/Display.hpp
${SRCROOT}/Unix/InputImpl.cpp ${SRCROOT}/Unix/InputImpl.cpp
@ -195,7 +193,7 @@ if(NOT SFML_OPENGL_ES)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIR}) include_directories(${OPENGL_INCLUDE_DIR})
if(SFML_OS_LINUX OR SFML_OS_FREEBSD) 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) if(NOT LIBXCB_FOUND)
message(FATAL_ERROR "Xcb library not found") message(FATAL_ERROR "Xcb library not found")
endif() endif()

View File

@ -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 <SFML/Window/Linux/AutoPointer.hpp>
#include <cstdlib>
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

View File

@ -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 <X11/Xlib-xcb.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Pointer Wrapper. Memory is automatically free'd
/// on deletion of the object.
///
////////////////////////////////////////////////////////////
template<typename T>
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<xcb_generic_error_t>
{
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

View File

@ -28,7 +28,6 @@
#include <SFML/Window/Unix/InputImpl.hpp> #include <SFML/Window/Unix/InputImpl.hpp>
#include <SFML/Window/Window.hpp> #include <SFML/Window/Window.hpp>
#include <SFML/Window/Unix/Display.hpp> #include <SFML/Window/Unix/Display.hpp>
#include <SFML/Window/Linux/AutoPointer.hpp>
#include <X11/Xlib-xcb.h> #include <X11/Xlib-xcb.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <cstdlib> #include <cstdlib>
@ -158,13 +157,16 @@ bool InputImpl::isKeyPressed(Keyboard::Key key)
if (keycode != 0) if (keycode != 0)
{ {
// Get the whole keyboard state // Get the whole keyboard state
AutoPointer<xcb_query_keymap_reply_t> 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 // Close the connection with the X server
CloseDisplay(display); CloseDisplay(display);
// Check our keycode // 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 else
{ {
@ -191,21 +193,25 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button)
xcb_connection_t* connection = XGetXCBConnection(display); xcb_connection_t* connection = XGetXCBConnection(display);
// Get pointer mask // Get pointer mask
AutoPointer<xcb_query_pointer_reply_t> pointer = xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL);
xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL);
// Close the connection with the X server // Close the connection with the X server
CloseDisplay(display); CloseDisplay(display);
bool result = false;
switch (button) switch (button)
{ {
case Mouse::Left: return pointer->mask & XCB_BUTTON_MASK_1; case Mouse::Left: result = pointer->mask & XCB_BUTTON_MASK_1;
case Mouse::Right: return pointer->mask & XCB_BUTTON_MASK_3; case Mouse::Right: result = pointer->mask & XCB_BUTTON_MASK_3;
case Mouse::Middle: return pointer->mask & XCB_BUTTON_MASK_2; case Mouse::Middle: result = pointer->mask & XCB_BUTTON_MASK_2;
case Mouse::XButton1: // not supported by X case Mouse::XButton1: // not supported by X
case Mouse::XButton2: // 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(); Display* display = OpenDisplay();
xcb_connection_t* connection = XGetXCBConnection(display); xcb_connection_t* connection = XGetXCBConnection(display);
AutoPointer<xcb_query_pointer_reply_t> pointer = xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL);
xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL);
// Close the connection with the X server // Close the connection with the X server
CloseDisplay(display); 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 // Open a connection with the X server
xcb_connection_t* connection = OpenConnection(); xcb_connection_t* connection = OpenConnection();
AutoPointer<xcb_query_pointer_reply_t> pointer = xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL);
xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL);
// Close the connection with the X server // Close the connection with the X server
CloseConnection(connection); 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 else
{ {

View File

@ -28,20 +28,25 @@
#include <SFML/Window/WindowStyle.hpp> // important to be included first (conflict with None) #include <SFML/Window/WindowStyle.hpp> // important to be included first (conflict with None)
#include <SFML/Window/Unix/WindowImplX11.hpp> #include <SFML/Window/Unix/WindowImplX11.hpp>
#include <SFML/Window/Unix/Display.hpp> #include <SFML/Window/Unix/Display.hpp>
#include <SFML/Window/Linux/AutoPointer.hpp>
#include <SFML/System/Utf.hpp> #include <SFML/System/Utf.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <xcb/xcb_atom.h> #include <xcb/xcb_atom.h>
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>
#include <xcb/xcb_image.h> #include <xcb/xcb_image.h>
#include <xcb/xcb_util.h>
#include <xcb/randr.h> #include <xcb/randr.h>
#include <unistd.h>
#include <libgen.h>
#include <cstring>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include <string> #include <string>
#include <iterator> #include <iterator>
#include <algorithm> #include <algorithm>
#include <iostream>
#ifdef SFML_OPENGL_ES #ifdef SFML_OPENGL_ES
#include <SFML/Window/EglContext.hpp> #include <SFML/Window/EglContext.hpp>
@ -106,6 +111,14 @@ m_useSizeHints(false)
// Open a connection with the X server // Open a connection with the X server
m_display = OpenDisplay(); m_display = OpenDisplay();
XSetEventQueueOwner(m_display, XCBOwnsEventQueue); 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); m_screen = DefaultScreen(m_display);
// Save the window handle // Save the window handle
@ -114,7 +127,12 @@ m_useSizeHints(false)
if (m_window) if (m_window)
{ {
// Make sure the window is listening to all the required events // 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 // Do some common initializations
initialize(); initialize();
@ -188,8 +206,11 @@ m_useSizeHints(false)
XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT, XCB_CW_EVENT_MASK | XCB_CW_OVERRIDE_REDIRECT,
value_list); value_list);
ErrorPointer errptr(m_connection, cookie); xcb_generic_error_t* errptr = xcb_request_check(m_connection, cookie);
if(! errptr.isNull()) bool createWindowFailed = (errptr != NULL);
free(errptr);
if (createWindowFailed)
{ {
err() << "Failed to create window" << std::endl; err() << "Failed to create window" << std::endl;
return; 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) // Set the window's style (tell the windows manager to change our window's decorations and functions according to the requested style)
if (!fullscreen) if (!fullscreen)
{ {
xcb_atom_t WMHintsAtom = xcb_atom_get(m_connection, "_MOTIF_WM_HINTS"); static const std::string MOTIF_WM_HINTS = "_MOTIF_WM_HINTS";
if (WMHintsAtom) 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_FUNCTIONS = 1 << 0;
static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1; static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1;
@ -254,17 +287,18 @@ m_useSizeHints(false)
const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&hints); const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&hints);
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, 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 // This is a hack to force some windows managers to disable resizing
if (!(style & Style::Resize)) if (!(style & Style::Resize))
{ {
m_useSizeHints = true;
xcb_size_hints_t sizeHints; 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_width = sizeHints.max_width = width;
sizeHints.min_height = sizeHints.max_height = height; 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,20 +366,73 @@ WindowHandle WindowImplX11::getSystemHandle() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplX11::processEvents() 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))
{
eventType = event->response_type & ~0x80;
// Key was pressed and one has been released prior to that.
if (eventType == XCB_KEY_PRESS && lastKeyReleaseEvent)
{
// If the key press event matches the held back key release event,
// then we have a key repeat and discard the held back release
// event.
if (lastKeyReleaseEvent->time == reinterpret_cast<xcb_key_press_event_t*>(event)->time &&
lastKeyReleaseEvent->detail == reinterpret_cast<xcb_key_press_event_t*>(event)->detail)
{
free(lastKeyReleaseEvent);
lastKeyReleaseEvent = NULL;
}
}
// If there's still a key release event held back, process it now.
if (lastKeyReleaseEvent)
{
processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent));
free(lastKeyReleaseEvent);
lastKeyReleaseEvent = NULL;
}
if (eventType == XCB_KEY_RELEASE)
{
// Remember this key release event.
lastKeyReleaseEvent = reinterpret_cast<xcb_key_release_event_t*>(event);
event = NULL; // For safety reasons.
}
else
{ {
processEvent(event); processEvent(event);
free(event); free(event);
} }
}
// Process any held back release event.
if (lastKeyReleaseEvent)
{
processEvent(reinterpret_cast<xcb_generic_event_t*>(lastKeyReleaseEvent));
free(lastKeyReleaseEvent);
lastKeyReleaseEvent = NULL;
}
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Vector2i WindowImplX11::getPosition() const Vector2i WindowImplX11::getPosition() const
{ {
AutoPointer<xcb_get_geometry_reply_t> reply = xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL);
xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL); Vector2i result(reply->x, reply->y);
return Vector2i(reply->x, reply->y); free(reply);
return result;
} }
@ -363,15 +450,26 @@ void WindowImplX11::setPosition(const Vector2i& position)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Vector2u WindowImplX11::getSize() const Vector2u WindowImplX11::getSize() const
{ {
AutoPointer<xcb_get_geometry_reply_t> reply = xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL);
xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_window), NULL); Vector2u result(reply->width, reply->height);
return Vector2u(reply->width, reply->height); free(reply);
return result;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplX11::setSize(const Vector2u& size) 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}; uint32_t values[] = {size.x, size.y};
xcb_configure_window(m_connection, m_window, xcb_configure_window(m_connection, m_window,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
@ -383,10 +481,16 @@ void WindowImplX11::setSize(const Vector2u& size)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplX11::setTitle(const String& title) void WindowImplX11::setTitle(const String& title)
{ {
const char* c_title = title.c_str(); // XCB takes UTF-8-encoded strings.
std::basic_string<sf::Uint8> utf8String;
sf::Utf<32>::toUtf8(
title.begin(), title.end(),
std::back_inserter( utf8String )
);
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window,
WM_NAME, STRING, XA_WM_NAME, XA_STRING,
8, title.length(), c_title); 8, utf8String.length(), utf8String.c_str());
xcb_flush(m_connection); 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); width, height, 0, 0, 0, defDepth, sizeof(iconPixels), iconPixels);
xcb_free_gc(m_connection, iconGC); xcb_free_gc(m_connection, iconGC);
ErrorPointer errptr(m_connection, cookie); xcb_generic_error_t* errptr = xcb_request_check(m_connection, cookie);
if (! errptr.isNull()) bool setWindowIconFailed = (errptr != NULL);
free(errptr);
if (setWindowIconFailed)
{ {
err() << "Failed to set the window's icon: Error code " << (int)errptr->error_code << std::endl; err() << "Failed to set the window's icon: Error code " << (int)errptr->error_code << std::endl;
return; 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); 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 // Send our new icon to the window through the WMHints
xcb_wm_hints_t hints; xcb_icccm_wm_hints_t hints;
hints.flags = XCB_WM_HINT_ICON_PIXMAP | XCB_WM_HINT_ICON_MASK; hints.flags = XCB_ICCCM_WM_HINT_ICON_PIXMAP | XCB_ICCCM_WM_HINT_ICON_MASK;
hints.icon_pixmap = iconPixmap; hints.icon_pixmap = iconPixmap;
hints.icon_mask = maskPixmap; 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); xcb_flush(m_connection);
} }
@ -607,15 +714,52 @@ void WindowImplX11::switchToFullscreen(const VideoMode& mode)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplX11::initialize() void WindowImplX11::initialize()
{ {
// Make sure the "last key release" is initialized with invalid values // Get the atoms for registering the close event
m_lastKeyReleaseEvent.response_type = -1; static const std::string WM_DELETE_WINDOW_NAME = "WM_DELETE_WINDOW";
m_lastKeyReleaseEvent.detail = 0;
m_lastKeyReleaseEvent.time = 0;
// Get the atom defining the close event xcb_intern_atom_cookie_t deleteWindowAtomRequest = xcb_intern_atom(
m_atomClose = xcb_atom_get(m_connection, "WM_DELETE_WINDOW"); m_connection,
xcb_atom_t wmprotocolsAtom = xcb_atom_get(m_connection, "WM_PROTOCOLS"); 0,
xcb_set_wm_protocols(m_connection, wmprotocolsAtom, m_window, 1, &m_atomClose); 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 // Create the input context
m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL); m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL);
@ -708,49 +852,6 @@ void WindowImplX11::cleanup()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent) bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
{ {
// This function implements a workaround to properly discard
// repeated key events when necessary. The problem is that the
// system's key events policy doesn't match SFML's one: X server will generate
// both repeated KeyPress and KeyRelease events when maintaining a key down, while
// SFML only wants repeated KeyPress events. Thus, we have to:
// - Discard duplicated KeyRelease events when KeyRepeatEnabled is true
// - Discard both duplicated KeyPress and KeyRelease events when KeyRepeatEnabled is false
// Detect repeated key events
if ((windowEvent->response_type == XCB_KEY_PRESS) ||
(windowEvent->response_type == XCB_KEY_RELEASE))
{
xcb_key_press_event_t* press = reinterpret_cast<xcb_key_press_event_t*>(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<xcb_query_keymap_reply_t> 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 // Convert the X11 event to a sf::Event
switch (windowEvent->response_type & ~0x80) switch (windowEvent->response_type & ~0x80)
{ {
@ -827,6 +928,7 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
case XCB_KEY_PRESS: case XCB_KEY_PRESS:
{ {
xcb_key_press_event_t* e = reinterpret_cast<xcb_key_press_event_t*>(windowEvent); xcb_key_press_event_t* e = reinterpret_cast<xcb_key_press_event_t*>(windowEvent);
// Get the keysym of the key that has been pressed // Get the keysym of the key that has been pressed
static XComposeStatus keyboard; static XComposeStatus keyboard;
char buffer[32]; char buffer[32];
@ -895,9 +997,10 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
} }
// Key up event // Key up event
case KeyRelease: case XCB_KEY_RELEASE:
{ {
xcb_key_release_event_t* e = reinterpret_cast<xcb_key_release_event_t*>(windowEvent); xcb_key_release_event_t* e = reinterpret_cast<xcb_key_release_event_t*>(windowEvent);
// Get the keysym of the key that has been pressed // Get the keysym of the key that has been pressed
char buffer[32]; char buffer[32];
KeySym symbol; KeySym symbol;
@ -1000,7 +1103,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Mouse entered // Mouse entered
case XCB_ENTER_NOTIFY: case XCB_ENTER_NOTIFY:
{ {
if (windowEvent.xcrossing.mode == NotifyNormal) xcb_enter_notify_event_t* enterNotifyEvent = reinterpret_cast<xcb_enter_notify_event_t*>(windowEvent);
if (enterNotifyEvent->mode == NotifyNormal)
{ {
Event event; Event event;
event.type = Event::MouseEntered; event.type = Event::MouseEntered;
@ -1012,7 +1117,9 @@ bool WindowImplX11::processEvent(xcb_generic_event_t* windowEvent)
// Mouse left // Mouse left
case XCB_LEAVE_NOTIFY: case XCB_LEAVE_NOTIFY:
{ {
if (windowEvent.xcrossing.mode == NotifyNormal) xcb_leave_notify_event_t* leaveNotifyEvent = reinterpret_cast<xcb_leave_notify_event_t*>(windowEvent);
if (leaveNotifyEvent->mode == NotifyNormal)
{ {
Event event; Event event;
event.type = Event::MouseLeft; event.type = Event::MouseLeft;