mirror of
https://github.com/SFML/SFML.git
synced 2024-11-25 04:41:05 +08:00
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:
parent
135c1716e8
commit
c7549cd292
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
{
|
||||
|
@ -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,20 +366,73 @@ 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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user