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
////////////////////////////////////////////////////////////
#include <SFML/Window.hpp>
#include <X11/Xlib.h>
#include <SFML/System/Err.hpp>
#include <X11/Xlib-xcb.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <iostream>
@ -55,9 +56,9 @@ void draw(sf::Window& window, float elapsedTime)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.f, 0.f, -200.f);
glRotatef(elapsedTime * 0.05f, 1.f, 0.f, 0.f);
glRotatef(elapsedTime * 0.03f, 0.f, 1.f, 0.f);
glRotatef(elapsedTime * 0.09f, 0.f, 0.f, 1.f);
glRotatef(elapsedTime * 10.f, 1.f, 0.f, 0.f);
glRotatef(elapsedTime * 6.f, 0.f, 1.f, 0.f);
glRotatef(elapsedTime * 18.f, 0.f, 0.f, 1.f);
// Draw a cube
glBegin(GL_QUADS);
@ -115,85 +116,104 @@ int main()
if (!display)
return EXIT_FAILURE;
// Get the default screen
int screen = DefaultScreen(display);
// Get the XCB connection for the opened display.
xcb_connection_t* xcbConnection = XGetXCBConnection(display);
// Let's create the main window
XSetWindowAttributes attributes;
attributes.background_pixel = BlackPixel(display, screen);
attributes.event_mask = KeyPressMask;
Window window = XCreateWindow(display, RootWindow(display, screen),
0, 0, 650, 330, 0,
DefaultDepth(display, screen),
InputOutput,
DefaultVisual(display, screen),
CWBackPixel | CWEventMask, &attributes);
if (!window)
if (!xcbConnection)
{
sf::err() << "Failed to get the XCB connection for opened display." << std::endl;
return EXIT_FAILURE;
}
// Set the window's name
XStoreName(display, window , "SFML Window");
// Get XCB screen.
const xcb_setup_t* xcbSetup = xcb_get_setup(xcbConnection);
xcb_screen_iterator_t xcbScreenIter = xcb_setup_roots_iterator(xcbSetup);
xcb_screen_t* screen = xcbScreenIter.data;
// Let's create the windows which will serve as containers for our SFML views
Window view1 = XCreateWindow(display, window,
10, 10, 310, 310, 0,
DefaultDepth(display, screen),
InputOutput,
DefaultVisual(display, screen),
0, NULL);
Window view2 = XCreateWindow(display, window,
330, 10, 310, 310, 0,
DefaultDepth(display, screen),
InputOutput,
DefaultVisual(display, screen),
0, NULL);
if (!screen)
{
sf::err() << "Failed to get the XCB screen." << std::endl;
return EXIT_FAILURE;
}
// Show our windows
XMapWindow(display, window);
XFlush(display);
// Generate the XCB window IDs.
xcb_window_t rootWindowId = xcb_generate_id(xcbConnection);
xcb_window_t view1WindowId = xcb_generate_id(xcbConnection);
xcb_window_t view2WindowId = xcb_generate_id(xcbConnection);
// Create the root window with a black background.
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
uint32_t attributes[2] = {screen->black_pixel, XCB_EVENT_MASK_KEY_PRESS};
xcb_create_window(xcbConnection,
XCB_COPY_FROM_PARENT,
rootWindowId,
screen->root,
0, 0, 650, 330,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
mask, attributes);
// Create windows for the SFML views.
xcb_create_window(xcbConnection,
XCB_COPY_FROM_PARENT,
view1WindowId,
rootWindowId,
10, 10, 310, 310,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
mask, attributes);
xcb_create_window(xcbConnection,
XCB_COPY_FROM_PARENT,
view2WindowId,
rootWindowId,
330, 10, 310, 310,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
mask, attributes);
// Map windows to screen.
xcb_map_window(xcbConnection, rootWindowId);
xcb_map_window(xcbConnection, view1WindowId);
xcb_map_window(xcbConnection, view2WindowId);
// Flush commands.
xcb_flush(xcbConnection);
// Create our SFML views
sf::Window SFMLView1(view1);
sf::Window SFMLView2(view2);
sf::Window sfmlView1(view1WindowId);
sf::Window sfmlView2(view2WindowId);
// Create a clock for measuring elapsed time
sf::Clock clock;
// Initialize our views
initialize(SFMLView1);
initialize(SFMLView2);
initialize(sfmlView1);
initialize(sfmlView2);
// Start the event loop
bool running = true;
xcb_generic_event_t* event = NULL;
while (running)
{
while (XPending(display))
while (event = xcb_poll_for_event(xcbConnection))
{
// Get the next pending event
XEvent event;
XNextEvent(display, &event);
// Process it
switch (event.type)
{
// Any key is pressed: quit
case KeyPress:
running = false;
break;
}
running = false;
}
// Draw something into our views
draw(SFMLView1, clock.getElapsedTime().asSeconds());
draw(SFMLView2, clock.getElapsedTime().asSeconds() * 0.3f);
draw(sfmlView1, clock.getElapsedTime().asSeconds());
draw(sfmlView2, clock.getElapsedTime().asSeconds() * 0.3f);
// Display the views on screen
SFMLView1.display();
SFMLView2.display();
sfmlView1.display();
sfmlView2.display();
}
// Close the display
XCloseDisplay(display);
return EXIT_SUCCESS;
}

View File

@ -69,8 +69,6 @@ if(SFML_OS_WINDOWS)
add_definitions(-DUNICODE -D_UNICODE)
elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD)
set(PLATFORM_SRC
${SRCROOT}/Linux/AutoPointer.cpp
${SRCROOT}/Linux/AutoPointer.hpp
${SRCROOT}/Unix/Display.cpp
${SRCROOT}/Unix/Display.hpp
${SRCROOT}/Unix/InputImpl.cpp
@ -195,7 +193,7 @@ if(NOT SFML_OPENGL_ES)
find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIR})
if(SFML_OS_LINUX OR SFML_OS_FREEBSD)
find_package(XCB COMPONENTS xlib_xcb atom icccm image randr REQUIRED)
find_package(XCB COMPONENTS xlib_xcb icccm image randr util REQUIRED)
if(NOT LIBXCB_FOUND)
message(FATAL_ERROR "Xcb library not found")
endif()

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/Window.hpp>
#include <SFML/Window/Unix/Display.hpp>
#include <SFML/Window/Linux/AutoPointer.hpp>
#include <X11/Xlib-xcb.h>
#include <X11/keysym.h>
#include <cstdlib>
@ -158,13 +157,16 @@ bool InputImpl::isKeyPressed(Keyboard::Key key)
if (keycode != 0)
{
// 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
CloseDisplay(display);
// Check our keycode
return (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0;
bool isPressed = (keymap->keys[keycode / 8] & (1 << (keycode % 8))) != 0;
free(keymap);
return isPressed;
}
else
{
@ -191,21 +193,25 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button)
xcb_connection_t* connection = XGetXCBConnection(display);
// Get pointer mask
AutoPointer<xcb_query_pointer_reply_t> pointer =
xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL);
xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL);
// Close the connection with the X server
CloseDisplay(display);
bool result = false;
switch (button)
{
case Mouse::Left: return pointer->mask & XCB_BUTTON_MASK_1;
case Mouse::Right: return pointer->mask & XCB_BUTTON_MASK_3;
case Mouse::Middle: return pointer->mask & XCB_BUTTON_MASK_2;
case Mouse::Left: result = pointer->mask & XCB_BUTTON_MASK_1;
case Mouse::Right: result = pointer->mask & XCB_BUTTON_MASK_3;
case Mouse::Middle: result = pointer->mask & XCB_BUTTON_MASK_2;
case Mouse::XButton1: // not supported by X
case Mouse::XButton2: // not supported by X
default: return false;
default: result = false;
}
free(pointer);
return result;
}
@ -216,13 +222,16 @@ Vector2i InputImpl::getMousePosition()
Display* display = OpenDisplay();
xcb_connection_t* connection = XGetXCBConnection(display);
AutoPointer<xcb_query_pointer_reply_t> pointer =
xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL);
xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, XDefaultRootWindow(display)), NULL);
// Close the connection with the X server
CloseDisplay(display);
return Vector2i(pointer->root_x, pointer->root_y);
// Prepare result.
Vector2i result(pointer->root_x, pointer->root_y);
free(pointer);
return result;
}
@ -235,13 +244,16 @@ Vector2i InputImpl::getMousePosition(const Window& relativeTo)
// Open a connection with the X server
xcb_connection_t* connection = OpenConnection();
AutoPointer<xcb_query_pointer_reply_t> pointer =
xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL);
xcb_query_pointer_reply_t* pointer = xcb_query_pointer_reply(connection, xcb_query_pointer(connection, handle), NULL);
// Close the connection with the X server
CloseConnection(connection);
return Vector2i(pointer->win_x, pointer->win_y);
// Prepare result.
Vector2i result(pointer->win_x, pointer->win_y);
free(pointer);
return result;
}
else
{

View File

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

View File

@ -228,19 +228,19 @@ private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
::Window m_window; ///< X11 structure defining our window
::Display* m_display; ///< Pointer to the display
xcb_connection_t* m_connection; ///< Pointer to the xcb connection
int m_screen; ///< Screen identifier
XIM m_inputMethod; ///< Input method linked to the X display
XIC m_inputContext; ///< Input context used to get unicode input in our window
bool m_isExternal; ///< Tell whether the window has been created externally or by SFML
Atom m_atomClose; ///< Atom used to identify the close event
int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen
Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one
bool m_keyRepeat; ///< Is the KeyRepeat feature enabled?
Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only)
bool m_useSizeHints; ///< Is the size of the window fixed with size hints?
::Window m_window; ///< X11 structure defining our window
::Display* m_display; ///< Pointer to the display
xcb_connection_t* m_connection; ///< Pointer to the xcb connection
int m_screen; ///< Screen identifier
XIM m_inputMethod; ///< Input method linked to the X display
XIC m_inputContext; ///< Input context used to get unicode input in our window
bool m_isExternal; ///< Tell whether the window has been created externally or by SFML
Atom m_atomClose; ///< Atom used to identify the close event
int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen
Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one
bool m_keyRepeat; ///< Is the KeyRepeat feature enabled?
Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only)
bool m_useSizeHints; ///< Is the size of the window fixed with size hints?
};
} // namespace priv