Skip to content

Instantly share code, notes, and snippets.

@citizen-erased
Last active December 18, 2015 07:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save citizen-erased/5745591 to your computer and use it in GitHub Desktop.
Save citizen-erased/5745591 to your computer and use it in GitHub Desktop.
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <cstdio>
#include <SFML/Window.hpp>
#include <SFML/OpenGL.hpp>
////////////////////////////////////////////////////////////
/// Entry point of application
///
/// \return Application exit code
///
////////////////////////////////////////////////////////////
int main()
{
// Request a 32-bits depth buffer when creating the window
sf::ContextSettings contextSettings;
contextSettings.depthBits = 32;
// Create the main window
sf::Window window(sf::VideoMode(640, 480), "SFML window with OpenGL", sf::Style::Default, contextSettings);
// Make it the active window for OpenGL calls
window.setActive();
// Set the color and depth clear values
glClearDepth(1.f);
glClearColor(0.f, 0.f, 0.f, 1.f);
// Enable Z-buffer read and write
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
// Disable lighting and texturing
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
// Configure the viewport (the same size as the window)
glViewport(0, 0, window.getSize().x, window.getSize().y);
// Setup a perspective projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat ratio = static_cast<float>(window.getSize().x) / window.getSize().y;
glFrustum(-ratio, ratio, -1.f, 1.f, 1.f, 500.f);
// Define a 3D cube (6 faces made of 2 triangles composed by 3 vertices)
GLfloat cube[] =
{
// positions // colors (r, g, b, a)
-50, -50, -50, 0, 0, 1, 1,
-50, 50, -50, 0, 0, 1, 1,
-50, -50, 50, 0, 0, 1, 1,
-50, -50, 50, 0, 0, 1, 1,
-50, 50, -50, 0, 0, 1, 1,
-50, 50, 50, 0, 0, 1, 1,
50, -50, -50, 0, 1, 0, 1,
50, 50, -50, 0, 1, 0, 1,
50, -50, 50, 0, 1, 0, 1,
50, -50, 50, 0, 1, 0, 1,
50, 50, -50, 0, 1, 0, 1,
50, 50, 50, 0, 1, 0, 1,
-50, -50, -50, 1, 0, 0, 1,
50, -50, -50, 1, 0, 0, 1,
-50, -50, 50, 1, 0, 0, 1,
-50, -50, 50, 1, 0, 0, 1,
50, -50, -50, 1, 0, 0, 1,
50, -50, 50, 1, 0, 0, 1,
-50, 50, -50, 0, 1, 1, 1,
50, 50, -50, 0, 1, 1, 1,
-50, 50, 50, 0, 1, 1, 1,
-50, 50, 50, 0, 1, 1, 1,
50, 50, -50, 0, 1, 1, 1,
50, 50, 50, 0, 1, 1, 1,
-50, -50, -50, 1, 0, 1, 1,
50, -50, -50, 1, 0, 1, 1,
-50, 50, -50, 1, 0, 1, 1,
-50, 50, -50, 1, 0, 1, 1,
50, -50, -50, 1, 0, 1, 1,
50, 50, -50, 1, 0, 1, 1,
-50, -50, 50, 1, 1, 0, 1,
50, -50, 50, 1, 1, 0, 1,
-50, 50, 50, 1, 1, 0, 1,
-50, 50, 50, 1, 1, 0, 1,
50, -50, 50, 1, 1, 0, 1,
50, 50, 50, 1, 1, 0, 1,
};
// Enable position and color vertex components
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 7 * sizeof(GLfloat), cube);
glColorPointer(4, GL_FLOAT, 7 * sizeof(GLfloat), cube + 3);
// Disable normal and texture coordinates vertex components
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// Create a clock for measuring the time elapsed
sf::Clock clock;
// Start the game loop
while (window.isOpen())
{
bool had_event = false;
// Process events
sf::Event event;
while (window.pollEvent(event))
{
// Close window: exit
if (event.type == sf::Event::Closed)
window.close();
// Escape key: exit
if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))
window.close();
// Resize event: adjust the viewport
if (event.type == sf::Event::Resized)
glViewport(0, 0, event.size.width, event.size.height);
if(event.type == sf::Event::KeyPressed)
{
printf("SFML: [key=%d down=%d]\n", event.key.code, 1);
had_event = true;
}
else if(event.type == sf::Event::KeyReleased)
{
printf("SFML: [key=%d down=%d]\n", event.key.code, 0);
had_event = true;
}
}
if(had_event)
printf("-----------------------\n");
// Clear the color and depth buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Apply some transformations to rotate the cube
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.f, 0.f, -200.f);
glRotatef(clock.getElapsedTime().asSeconds() * 50, 1.f, 0.f, 0.f);
glRotatef(clock.getElapsedTime().asSeconds() * 30, 0.f, 1.f, 0.f);
glRotatef(clock.getElapsedTime().asSeconds() * 90, 0.f, 0.f, 1.f);
// Draw the cube
glDrawArrays(GL_TRIANGLES, 0, 36);
// Finally, display the rendered frame on screen
window.display();
}
return EXIT_SUCCESS;
}
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2013 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/WindowStyle.hpp> // important to be included first (conflict with None)
#include <SFML/Window/Linux/WindowImplX11.hpp>
#include <SFML/Window/Linux/Display.hpp>
#include <SFML/System/Utf.hpp>
#include <SFML/System/Err.hpp>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/extensions/Xrandr.h>
#include <unistd.h>
#include <cstring>
#include <sstream>
#include <vector>
#include <string>
#include <iterator>
#include <cstdio>
////////////////////////////////////////////////////////////
// Private data
////////////////////////////////////////////////////////////
namespace
{
sf::priv::WindowImplX11* fullscreenWindow = NULL;
unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
PointerMotionMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask |
EnterWindowMask | LeaveWindowMask;
// Filter the events received by windows (only allow those matching a specific window)
Bool checkEvent(::Display*, XEvent* event, XPointer userData)
{
// Just check if the event matches the window
return event->xany.window == reinterpret_cast< ::Window >(userData);
}
// Find the name of the current executable
const char* findExecutableName()
{
char buffer[512];
std::size_t length = readlink("/proc/self/exe", buffer, sizeof(buffer));
if ((length > 0) && (length < sizeof(buffer)))
{
// Remove the path to keep the executable name only
buffer[length] = '\0';
return basename(buffer);
}
else
{
// Fallback name
return "sfml";
}
}
}
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
WindowImplX11::WindowImplX11(WindowHandle handle) :
m_window (0),
m_inputMethod (NULL),
m_inputContext(NULL),
m_isExternal (true),
m_atomClose (0),
m_oldVideoMode(-1),
m_hiddenCursor(0),
m_keyRepeat (true),
m_previousSize(-1, -1)
{
// Open a connection with the X server
m_display = OpenDisplay();
m_screen = DefaultScreen(m_display);
// Save the window handle
m_window = handle;
if (m_window)
{
// Make sure the window is listening to all the requiered events
XSelectInput(m_display, m_window, eventMask & ~ButtonPressMask);
// Do some common initializations
initialize();
}
}
////////////////////////////////////////////////////////////
WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style) :
m_window (0),
m_inputMethod (NULL),
m_inputContext(NULL),
m_isExternal (false),
m_atomClose (0),
m_oldVideoMode(-1),
m_hiddenCursor(0),
m_keyRepeat (true),
m_previousSize(-1, -1)
{
// Open a connection with the X server
m_display = OpenDisplay();
m_screen = DefaultScreen(m_display);
// Compute position and size
int left, top;
bool fullscreen = (style & Style::Fullscreen) != 0;
if (!fullscreen)
{
left = (DisplayWidth(m_display, m_screen) - mode.width) / 2;
top = (DisplayHeight(m_display, m_screen) - mode.height) / 2;
}
else
{
left = 0;
top = 0;
}
int width = mode.width;
int height = mode.height;
// Switch to fullscreen if necessary
if (fullscreen)
switchToFullscreen(mode);
// Define the window attributes
XSetWindowAttributes attributes;
attributes.event_mask = eventMask;
attributes.override_redirect = fullscreen;
// Create the window
m_window = XCreateWindow(m_display,
RootWindow(m_display, m_screen),
left, top,
width, height,
0,
DefaultDepth(m_display, m_screen),
InputOutput,
DefaultVisual(m_display, m_screen),
CWEventMask | CWOverrideRedirect, &attributes);
if (!m_window)
{
err() << "Failed to create window" << std::endl;
return;
}
// Set the window's name
setTitle(title);
// Set the window's style (tell the windows manager to change our window's decorations and functions according to the requested style)
if (!fullscreen)
{
Atom WMHintsAtom = XInternAtom(m_display, "_MOTIF_WM_HINTS", false);
if (WMHintsAtom)
{
static const unsigned long MWM_HINTS_FUNCTIONS = 1 << 0;
static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1;
//static const unsigned long MWM_DECOR_ALL = 1 << 0;
static const unsigned long MWM_DECOR_BORDER = 1 << 1;
static const unsigned long MWM_DECOR_RESIZEH = 1 << 2;
static const unsigned long MWM_DECOR_TITLE = 1 << 3;
static const unsigned long MWM_DECOR_MENU = 1 << 4;
static const unsigned long MWM_DECOR_MINIMIZE = 1 << 5;
static const unsigned long MWM_DECOR_MAXIMIZE = 1 << 6;
//static const unsigned long MWM_FUNC_ALL = 1 << 0;
static const unsigned long MWM_FUNC_RESIZE = 1 << 1;
static const unsigned long MWM_FUNC_MOVE = 1 << 2;
static const unsigned long MWM_FUNC_MINIMIZE = 1 << 3;
static const unsigned long MWM_FUNC_MAXIMIZE = 1 << 4;
static const unsigned long MWM_FUNC_CLOSE = 1 << 5;
struct WMHints
{
unsigned long flags;
unsigned long functions;
unsigned long decorations;
long inputMode;
unsigned long state;
};
WMHints hints;
hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
hints.decorations = 0;
hints.functions = 0;
if (style & Style::Titlebar)
{
hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU;
hints.functions |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE;
}
if (style & Style::Resize)
{
hints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH;
hints.functions |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
}
if (style & Style::Close)
{
hints.decorations |= 0;
hints.functions |= MWM_FUNC_CLOSE;
}
const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&hints);
XChangeProperty(m_display, m_window, WMHintsAtom, WMHintsAtom, 32, PropModeReplace, ptr, 5);
}
// This is a hack to force some windows managers to disable resizing
if (!(style & Style::Resize))
{
XSizeHints* sizeHints = XAllocSizeHints();
sizeHints->flags = PMinSize | PMaxSize;
sizeHints->min_width = sizeHints->max_width = width;
sizeHints->min_height = sizeHints->max_height = height;
XSetWMNormalHints(m_display, m_window, sizeHints);
XFree(sizeHints);
}
}
// Set the window's WM class (this can be used by window managers)
const char* windowClass = findExecutableName();
XClassHint* classHint = XAllocClassHint();
classHint->res_name = const_cast<char*>(windowClass);
classHint->res_class = const_cast<char*>(windowClass);
XSetClassHint(m_display, m_window, classHint);
XFree(classHint);
// Do some common initializations
initialize();
// In fullscreen mode, we must grab keyboard and mouse inputs
if (fullscreen)
{
XGrabPointer(m_display, m_window, true, 0, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime);
XGrabKeyboard(m_display, m_window, true, GrabModeAsync, GrabModeAsync, CurrentTime);
}
}
////////////////////////////////////////////////////////////
WindowImplX11::~WindowImplX11()
{
// Cleanup graphical resources
cleanup();
// Destroy the cursor
if (m_hiddenCursor)
XFreeCursor(m_display, m_hiddenCursor);
// Destroy the input context
if (m_inputContext)
XDestroyIC(m_inputContext);
// Destroy the window
if (m_window && !m_isExternal)
{
XDestroyWindow(m_display, m_window);
XFlush(m_display);
}
// Close the input method
if (m_inputMethod)
XCloseIM(m_inputMethod);
// Close the connection with the X server
CloseDisplay(m_display);
}
////////////////////////////////////////////////////////////
WindowHandle WindowImplX11::getSystemHandle() const
{
return m_window;
}
////////////////////////////////////////////////////////////
void WindowImplX11::processEvents()
{
XEvent event;
while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast<XPointer>(m_window)))
{
processEvent(event);
}
}
////////////////////////////////////////////////////////////
Vector2i WindowImplX11::getPosition() const
{
::Window root, child;
int localX, localY, x, y;
unsigned int width, height, border, depth;
XGetGeometry(m_display, m_window, &root, &localX, &localY, &width, &height, &border, &depth);
XTranslateCoordinates(m_display, m_window, root, localX, localY, &x, &y, &child);
return Vector2i(x, y);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setPosition(const Vector2i& position)
{
XMoveWindow(m_display, m_window, position.x, position.y);
XFlush(m_display);
}
////////////////////////////////////////////////////////////
Vector2u WindowImplX11::getSize() const
{
XWindowAttributes attributes;
XGetWindowAttributes(m_display, m_window, &attributes);
return Vector2u(attributes.width, attributes.height);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setSize(const Vector2u& size)
{
XResizeWindow(m_display, m_window, size.x, size.y);
XFlush(m_display);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setTitle(const String& title)
{
// Bare X11 has no Unicode window title support.
// There is however an option to tell the window manager your unicode title via hints.
// Convert to UTF-8 encoding.
std::basic_string<sf::Uint8> utf8Title;
sf::Utf32::toUtf8(title.begin(), title.end(), std::back_inserter(utf8Title));
// Set the _NET_WM_NAME atom, which specifies a UTF-8 encoded window title.
Atom wmName = XInternAtom(m_display, "_NET_WM_NAME", False);
Atom useUtf8 = XInternAtom(m_display, "UTF8_STRING", False);
XChangeProperty(m_display, m_window, wmName, useUtf8, 8,
PropModeReplace, utf8Title.c_str(), utf8Title.size());
// Set the non-Unicode title as a fallback for window managers who don't support _NET_WM_NAME.
XStoreName(m_display, m_window, title.toAnsiString().c_str());
}
////////////////////////////////////////////////////////////
void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8* pixels)
{
// X11 wants BGRA pixels : swap red and blue channels
// Note: this memory will be freed by XDestroyImage
Uint8* iconPixels = static_cast<Uint8*>(std::malloc(width * height * 4));
for (std::size_t i = 0; i < width * height; ++i)
{
iconPixels[i * 4 + 0] = pixels[i * 4 + 2];
iconPixels[i * 4 + 1] = pixels[i * 4 + 1];
iconPixels[i * 4 + 2] = pixels[i * 4 + 0];
iconPixels[i * 4 + 3] = pixels[i * 4 + 3];
}
// Create the icon pixmap
Visual* defVisual = DefaultVisual(m_display, m_screen);
unsigned int defDepth = DefaultDepth(m_display, m_screen);
XImage* iconImage = XCreateImage(m_display, defVisual, defDepth, ZPixmap, 0, (char*)iconPixels, width, height, 32, 0);
if (!iconImage)
{
err() << "Failed to set the window's icon" << std::endl;
return;
}
Pixmap iconPixmap = XCreatePixmap(m_display, RootWindow(m_display, m_screen), width, height, defDepth);
XGCValues values;
GC iconGC = XCreateGC(m_display, iconPixmap, 0, &values);
XPutImage(m_display, iconPixmap, iconGC, iconImage, 0, 0, 0, 0, width, height);
XFreeGC(m_display, iconGC);
XDestroyImage(iconImage);
// Create the mask pixmap (must have 1 bit depth)
std::size_t pitch = (width + 7) / 8;
static std::vector<Uint8> maskPixels(pitch * height, 0);
for (std::size_t j = 0; j < height; ++j)
{
for (std::size_t i = 0; i < pitch; ++i)
{
for (std::size_t k = 0; k < 8; ++k)
{
if (i * 8 + k < width)
{
Uint8 opacity = (pixels[(i * 8 + k + j * width) * 4 + 3] > 0) ? 1 : 0;
maskPixels[i + j * pitch] |= (opacity << k);
}
}
}
}
Pixmap maskPixmap = XCreatePixmapFromBitmapData(m_display, m_window, (char*)&maskPixels[0], width, height, 1, 0, 1);
// Send our new icon to the window through the WMHints
XWMHints* hints = XAllocWMHints();
hints->flags = IconPixmapHint | IconMaskHint;
hints->icon_pixmap = iconPixmap;
hints->icon_mask = maskPixmap;
XSetWMHints(m_display, m_window, hints);
XFree(hints);
XFlush(m_display);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setVisible(bool visible)
{
if (visible)
XMapWindow(m_display, m_window);
else
XUnmapWindow(m_display, m_window);
XFlush(m_display);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setMouseCursorVisible(bool visible)
{
XDefineCursor(m_display, m_window, visible ? None : m_hiddenCursor);
XFlush(m_display);
}
////////////////////////////////////////////////////////////
void WindowImplX11::setKeyRepeatEnabled(bool enabled)
{
m_keyRepeat = enabled;
}
////////////////////////////////////////////////////////////
void WindowImplX11::switchToFullscreen(const VideoMode& mode)
{
// Check if the XRandR extension is present
int version;
if (XQueryExtension(m_display, "RANDR", &version, &version, &version))
{
// Get the current configuration
XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen));
if (config)
{
// Get the current rotation
Rotation currentRotation;
m_oldVideoMode = XRRConfigCurrentConfiguration(config, &currentRotation);
// Get the available screen sizes
int nbSizes;
XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes);
if (sizes && (nbSizes > 0))
{
// Search a matching size
for (int i = 0; i < nbSizes; ++i)
{
if ((sizes[i].width == static_cast<int>(mode.width)) && (sizes[i].height == static_cast<int>(mode.height)))
{
// Switch to fullscreen mode
XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), i, currentRotation, CurrentTime);
// Set "this" as the current fullscreen window
fullscreenWindow = this;
break;
}
}
}
// Free the configuration instance
XRRFreeScreenConfigInfo(config);
}
else
{
// Failed to get the screen configuration
err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl;
}
}
else
{
// XRandr extension is not supported : we cannot use fullscreen mode
err() << "Fullscreen is not supported, switching to window mode" << std::endl;
}
}
////////////////////////////////////////////////////////////
void WindowImplX11::initialize()
{
// Make sure the "last key release" is initialized with invalid values
m_lastKeyReleaseEvent.type = -1;
m_lastKeyReleaseEvent.xkey.keycode = 0;
m_lastKeyReleaseEvent.xkey.time = 0;
// Get the atom defining the close event
m_atomClose = XInternAtom(m_display, "WM_DELETE_WINDOW", false);
XSetWMProtocols(m_display, m_window, &m_atomClose, 1);
// Create the input context
m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL);
if (m_inputMethod)
{
m_inputContext = XCreateIC(m_inputMethod,
XNClientWindow, m_window,
XNFocusWindow, m_window,
XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
(void*)NULL);
}
else
{
m_inputContext = NULL;
}
if (!m_inputContext)
err() << "Failed to create input context for window -- TextEntered event won't be able to return unicode" << std::endl;
// Show the window
XMapWindow(m_display, m_window);
XFlush(m_display);
// Create the hiden cursor
createHiddenCursor();
// Flush the commands queue
XFlush(m_display);
}
////////////////////////////////////////////////////////////
void WindowImplX11::createHiddenCursor()
{
// Create the cursor's pixmap (1x1 pixels)
Pixmap cursorPixmap = XCreatePixmap(m_display, m_window, 1, 1, 1);
GC graphicsContext = XCreateGC(m_display, cursorPixmap, 0, NULL);
XDrawPoint(m_display, cursorPixmap, graphicsContext, 0, 0);
XFreeGC(m_display, graphicsContext);
// Create the cursor, using the pixmap as both the shape and the mask of the cursor
XColor color;
color.flags = DoRed | DoGreen | DoBlue;
color.red = color.blue = color.green = 0;
m_hiddenCursor = XCreatePixmapCursor(m_display, cursorPixmap, cursorPixmap, &color, &color, 0, 0);
// We don't need the pixmap any longer, free it
XFreePixmap(m_display, cursorPixmap);
}
////////////////////////////////////////////////////////////
void WindowImplX11::cleanup()
{
// Restore the previous video mode (in case we were running in fullscreen)
if (fullscreenWindow == this)
{
// Get current screen info
XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen));
if (config)
{
// Get the current rotation
Rotation currentRotation;
XRRConfigCurrentConfiguration(config, &currentRotation);
// Reset the video mode
XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), m_oldVideoMode, currentRotation, CurrentTime);
// Free the configuration instance
XRRFreeScreenConfigInfo(config);
}
// Reset the fullscreen window
fullscreenWindow = NULL;
}
// Unhide the mouse cursor (in case it was hidden)
setMouseCursorVisible(true);
}
////////////////////////////////////////////////////////////
bool WindowImplX11::processEvent(XEvent 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.type == KeyPress) || (windowEvent.type == KeyRelease)) && (windowEvent.xkey.keycode < 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 KeyRepeatEnabled state,
// and we need to properly forward the first one
// Check if the key is currently down
char keys[32];
XQueryKeymap(m_display, keys);
bool isDown = keys[windowEvent.xkey.keycode / 8] & (1 << (windowEvent.xkey.keycode % 8));
// Check if it's a duplicate event
bool isDuplicate = (windowEvent.xkey.keycode == m_lastKeyReleaseEvent.xkey.keycode) &&
(windowEvent.xkey.time - m_lastKeyReleaseEvent.xkey.time <= 5);
int timediff = windowEvent.xkey.time - m_lastKeyReleaseEvent.xkey.time;
bool discard = ((windowEvent.type == KeyRelease) && (isDown || isDuplicate)) ||
((windowEvent.type == KeyPress) && isDuplicate && !m_keyRepeat);
printf("X11: duplicate=%d [keycode=%d mode=%s time=%d] discard=%d\n", isDuplicate ? 1 : 0,
windowEvent.xkey.keycode,
windowEvent.type == KeyPress ? "down" : "up ",
timediff,
discard ? 1 : 0);
// Keep track of the last KeyRelease event
if (windowEvent.type == KeyRelease)
m_lastKeyReleaseEvent = windowEvent;
// KeyRelease event + key down or duplicate event = repeated event --> discard
if ((windowEvent.type == KeyRelease) && (isDown || isDuplicate))
return false;
// KeyPress event + matching KeyRelease event = repeated event --> discard if key repeat is disabled
if ((windowEvent.type == KeyPress) && isDuplicate && !m_keyRepeat)
return false;
}
// Convert the X11 event to a sf::Event
switch (windowEvent.type)
{
// Destroy event
case DestroyNotify :
{
// The window is about to be destroyed : we must cleanup resources
cleanup();
break;
}
// Gain focus event
case FocusIn :
{
// Update the input context
if (m_inputContext)
XSetICFocus(m_inputContext);
Event event;
event.type = Event::GainedFocus;
pushEvent(event);
break;
}
// Lost focus event
case FocusOut :
{
// Update the input context
if (m_inputContext)
XUnsetICFocus(m_inputContext);
Event event;
event.type = Event::LostFocus;
pushEvent(event);
break;
}
// Resize event
case ConfigureNotify :
{
// ConfigureNotify can be triggered for other reasons, check if the size has acutally changed
if ((windowEvent.xconfigure.width != m_previousSize.x) || (windowEvent.xconfigure.height != m_previousSize.y))
{
Event event;
event.type = Event::Resized;
event.size.width = windowEvent.xconfigure.width;
event.size.height = windowEvent.xconfigure.height;
pushEvent(event);
m_previousSize.x = windowEvent.xconfigure.width;
m_previousSize.y = windowEvent.xconfigure.height;
}
break;
}
// Close event
case ClientMessage :
{
if ((windowEvent.xclient.format == 32) && (windowEvent.xclient.data.l[0]) == static_cast<long>(m_atomClose))
{
Event event;
event.type = Event::Closed;
pushEvent(event);
}
break;
}
// Key down event
case KeyPress :
{
// Get the keysym of the key that has been pressed
static XComposeStatus keyboard;
char buffer[32];
KeySym symbol;
XLookupString(&windowEvent.xkey, buffer, sizeof(buffer), &symbol, &keyboard);
// Fill the event parameters
// TODO: if modifiers are wrong, use XGetModifierMapping to retrieve the actual modifiers mapping
Event event;
event.type = Event::KeyPressed;
event.key.code = keysymToSF(symbol);
event.key.alt = windowEvent.xkey.state & Mod1Mask;
event.key.control = windowEvent.xkey.state & ControlMask;
event.key.shift = windowEvent.xkey.state & ShiftMask;
event.key.system = windowEvent.xkey.state & Mod4Mask;
pushEvent(event);
// Generate a TextEntered event
if (!XFilterEvent(&windowEvent, None))
{
#ifdef X_HAVE_UTF8_STRING
if (m_inputContext)
{
Status status;
Uint8 keyBuffer[16];
int length = Xutf8LookupString(m_inputContext, &windowEvent.xkey, reinterpret_cast<char*>(keyBuffer), sizeof(keyBuffer), NULL, &status);
if (length > 0)
{
Uint32 unicode = 0;
Utf8::decode(keyBuffer, keyBuffer + length, unicode, 0);
if (unicode != 0)
{
Event textEvent;
textEvent.type = Event::TextEntered;
textEvent.text.unicode = unicode;
pushEvent(textEvent);
}
}
}
else
#endif
{
static XComposeStatus status;
char keyBuffer[16];
if (XLookupString(&windowEvent.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status))
{
Event textEvent;
textEvent.type = Event::TextEntered;
textEvent.text.unicode = static_cast<Uint32>(keyBuffer[0]);
pushEvent(textEvent);
}
}
}
break;
}
// Key up event
case KeyRelease :
{
// Get the keysym of the key that has been pressed
char buffer[32];
KeySym symbol;
XLookupString(&windowEvent.xkey, buffer, 32, &symbol, NULL);
// Fill the event parameters
Event event;
event.type = Event::KeyReleased;
event.key.code = keysymToSF(symbol);
event.key.alt = windowEvent.xkey.state & Mod1Mask;
event.key.control = windowEvent.xkey.state & ControlMask;
event.key.shift = windowEvent.xkey.state & ShiftMask;
event.key.system = windowEvent.xkey.state & Mod4Mask;
pushEvent(event);
break;
}
// Mouse button pressed
case ButtonPress :
{
unsigned int button = windowEvent.xbutton.button;
if ((button == Button1) || (button == Button2) || (button == Button3) || (button == 8) || (button == 9))
{
Event event;
event.type = Event::MouseButtonPressed;
event.mouseButton.x = windowEvent.xbutton.x;
event.mouseButton.y = windowEvent.xbutton.y;
switch (button)
{
case Button1 : event.mouseButton.button = Mouse::Left; break;
case Button2 : event.mouseButton.button = Mouse::Middle; break;
case Button3 : event.mouseButton.button = Mouse::Right; break;
case 8 : event.mouseButton.button = Mouse::XButton1; break;
case 9 : event.mouseButton.button = Mouse::XButton2; break;
}
pushEvent(event);
}
break;
}
// Mouse button released
case ButtonRelease :
{
unsigned int button = windowEvent.xbutton.button;
if ((button == Button1) || (button == Button2) || (button == Button3) || (button == 8) || (button == 9))
{
Event event;
event.type = Event::MouseButtonReleased;
event.mouseButton.x = windowEvent.xbutton.x;
event.mouseButton.y = windowEvent.xbutton.y;
switch (button)
{
case Button1 : event.mouseButton.button = Mouse::Left; break;
case Button2 : event.mouseButton.button = Mouse::Middle; break;
case Button3 : event.mouseButton.button = Mouse::Right; break;
case 8 : event.mouseButton.button = Mouse::XButton1; break;
case 9 : event.mouseButton.button = Mouse::XButton2; break;
}
pushEvent(event);
}
else if ((button == Button4) || (button == Button5))
{
Event event;
event.type = Event::MouseWheelMoved;
event.mouseWheel.delta = windowEvent.xbutton.button == Button4 ? 1 : -1;
event.mouseWheel.x = windowEvent.xbutton.x;
event.mouseWheel.y = windowEvent.xbutton.y;
pushEvent(event);
}
break;
}
// Mouse moved
case MotionNotify :
{
Event event;
event.type = Event::MouseMoved;
event.mouseMove.x = windowEvent.xmotion.x;
event.mouseMove.y = windowEvent.xmotion.y;
pushEvent(event);
break;
}
// Mouse entered
case EnterNotify :
{
if (windowEvent.xcrossing.mode == NotifyNormal)
{
Event event;
event.type = Event::MouseEntered;
pushEvent(event);
}
break;
}
// Mouse left
case LeaveNotify :
{
if (windowEvent.xcrossing.mode == NotifyNormal)
{
Event event;
event.type = Event::MouseLeft;
pushEvent(event);
}
break;
}
}
return true;
}
////////////////////////////////////////////////////////////
Keyboard::Key WindowImplX11::keysymToSF(KeySym symbol)
{
// First convert to uppercase (to avoid dealing with two different keysyms for the same key)
KeySym lower, key;
XConvertCase(symbol, &lower, &key);
switch (key)
{
case XK_Shift_L : return Keyboard::LShift;
case XK_Shift_R : return Keyboard::RShift;
case XK_Control_L : return Keyboard::LControl;
case XK_Control_R : return Keyboard::RControl;
case XK_Alt_L : return Keyboard::LAlt;
case XK_Alt_R : return Keyboard::RAlt;
case XK_Super_L : return Keyboard::LSystem;
case XK_Super_R : return Keyboard::RSystem;
case XK_Menu : return Keyboard::Menu;
case XK_Escape : return Keyboard::Escape;
case XK_semicolon : return Keyboard::SemiColon;
case XK_slash : return Keyboard::Slash;
case XK_equal : return Keyboard::Equal;
case XK_minus : return Keyboard::Dash;
case XK_bracketleft : return Keyboard::LBracket;
case XK_bracketright : return Keyboard::RBracket;
case XK_comma : return Keyboard::Comma;
case XK_period : return Keyboard::Period;
case XK_dead_acute : return Keyboard::Quote;
case XK_backslash : return Keyboard::BackSlash;
case XK_dead_grave : return Keyboard::Tilde;
case XK_space : return Keyboard::Space;
case XK_Return : return Keyboard::Return;
case XK_KP_Enter : return Keyboard::Return;
case XK_BackSpace : return Keyboard::BackSpace;
case XK_Tab : return Keyboard::Tab;
case XK_Prior : return Keyboard::PageUp;
case XK_Next : return Keyboard::PageDown;
case XK_End : return Keyboard::End;
case XK_Home : return Keyboard::Home;
case XK_Insert : return Keyboard::Insert;
case XK_Delete : return Keyboard::Delete;
case XK_KP_Add : return Keyboard::Add;
case XK_KP_Subtract : return Keyboard::Subtract;
case XK_KP_Multiply : return Keyboard::Multiply;
case XK_KP_Divide : return Keyboard::Divide;
case XK_Pause : return Keyboard::Pause;
case XK_F1 : return Keyboard::F1;
case XK_F2 : return Keyboard::F2;
case XK_F3 : return Keyboard::F3;
case XK_F4 : return Keyboard::F4;
case XK_F5 : return Keyboard::F5;
case XK_F6 : return Keyboard::F6;
case XK_F7 : return Keyboard::F7;
case XK_F8 : return Keyboard::F8;
case XK_F9 : return Keyboard::F9;
case XK_F10 : return Keyboard::F10;
case XK_F11 : return Keyboard::F11;
case XK_F12 : return Keyboard::F12;
case XK_F13 : return Keyboard::F13;
case XK_F14 : return Keyboard::F14;
case XK_F15 : return Keyboard::F15;
case XK_Left : return Keyboard::Left;
case XK_Right : return Keyboard::Right;
case XK_Up : return Keyboard::Up;
case XK_Down : return Keyboard::Down;
case XK_KP_0 : return Keyboard::Numpad0;
case XK_KP_1 : return Keyboard::Numpad1;
case XK_KP_2 : return Keyboard::Numpad2;
case XK_KP_3 : return Keyboard::Numpad3;
case XK_KP_4 : return Keyboard::Numpad4;
case XK_KP_5 : return Keyboard::Numpad5;
case XK_KP_6 : return Keyboard::Numpad6;
case XK_KP_7 : return Keyboard::Numpad7;
case XK_KP_8 : return Keyboard::Numpad8;
case XK_KP_9 : return Keyboard::Numpad9;
case XK_A : return Keyboard::A;
case XK_Z : return Keyboard::Z;
case XK_E : return Keyboard::E;
case XK_R : return Keyboard::R;
case XK_T : return Keyboard::T;
case XK_Y : return Keyboard::Y;
case XK_U : return Keyboard::U;
case XK_I : return Keyboard::I;
case XK_O : return Keyboard::O;
case XK_P : return Keyboard::P;
case XK_Q : return Keyboard::Q;
case XK_S : return Keyboard::S;
case XK_D : return Keyboard::D;
case XK_F : return Keyboard::F;
case XK_G : return Keyboard::G;
case XK_H : return Keyboard::H;
case XK_J : return Keyboard::J;
case XK_K : return Keyboard::K;
case XK_L : return Keyboard::L;
case XK_M : return Keyboard::M;
case XK_W : return Keyboard::W;
case XK_X : return Keyboard::X;
case XK_C : return Keyboard::C;
case XK_V : return Keyboard::V;
case XK_B : return Keyboard::B;
case XK_N : return Keyboard::N;
case XK_0 : return Keyboard::Num0;
case XK_1 : return Keyboard::Num1;
case XK_2 : return Keyboard::Num2;
case XK_3 : return Keyboard::Num3;
case XK_4 : return Keyboard::Num4;
case XK_5 : return Keyboard::Num5;
case XK_6 : return Keyboard::Num6;
case XK_7 : return Keyboard::Num7;
case XK_8 : return Keyboard::Num8;
case XK_9 : return Keyboard::Num9;
}
return Keyboard::Unknown;
}
} // namespace priv
} // namespace sf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment