diff options
Diffstat (limited to 'src/SFML/Window/Unix')
-rw-r--r-- | src/SFML/Window/Unix/Display.cpp | 78 | ||||
-rw-r--r-- | src/SFML/Window/Unix/Display.hpp | 62 | ||||
-rw-r--r-- | src/SFML/Window/Unix/GlxContext.cpp | 341 | ||||
-rw-r--r-- | src/SFML/Window/Unix/GlxContext.hpp | 148 | ||||
-rw-r--r-- | src/SFML/Window/Unix/InputImpl.cpp | 324 | ||||
-rw-r--r-- | src/SFML/Window/Unix/InputImpl.hpp | 168 | ||||
-rw-r--r-- | src/SFML/Window/Unix/JoystickImpl.cpp | 388 | ||||
-rw-r--r-- | src/SFML/Window/Unix/JoystickImpl.hpp | 126 | ||||
-rw-r--r-- | src/SFML/Window/Unix/SensorImpl.cpp | 88 | ||||
-rw-r--r-- | src/SFML/Window/Unix/SensorImpl.hpp | 101 | ||||
-rw-r--r-- | src/SFML/Window/Unix/VideoModeImpl.cpp | 176 | ||||
-rw-r--r-- | src/SFML/Window/Unix/WindowImplX11.cpp | 1109 | ||||
-rw-r--r-- | src/SFML/Window/Unix/WindowImplX11.hpp | 250 |
13 files changed, 3359 insertions, 0 deletions
diff --git a/src/SFML/Window/Unix/Display.cpp b/src/SFML/Window/Unix/Display.cpp new file mode 100644 index 0000000..aad1b26 --- /dev/null +++ b/src/SFML/Window/Unix/Display.cpp @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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/System/Err.hpp> +#include <SFML/Window/Unix/Display.hpp> +#include <cassert> +#include <cstdlib> + + +namespace +{ + // The shared display and its reference counter + Display* sharedDisplay = NULL; + unsigned int referenceCount = 0; +} + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +Display* OpenDisplay() +{ + if (referenceCount == 0) + { + sharedDisplay = XOpenDisplay(NULL); + + // Opening display failed: The best we can do at the moment is to output a meaningful error message + // and cause an abnormal program termination + if (!sharedDisplay) + { + err() << "Failed to open X11 display; make sure the DISPLAY environment variable is set correctly" << std::endl; + std::abort(); + } + } + + referenceCount++; + return sharedDisplay; +} + + +//////////////////////////////////////////////////////////// +void CloseDisplay(Display* display) +{ + assert(display == sharedDisplay); + + referenceCount--; + if (referenceCount == 0) + XCloseDisplay(display); +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/Unix/Display.hpp b/src/SFML/Window/Unix/Display.hpp new file mode 100644 index 0000000..df018b3 --- /dev/null +++ b/src/SFML/Window/Unix/Display.hpp @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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 SFML_SHAREDDISPLAY_HPP +#define SFML_SHAREDDISPLAY_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include <X11/Xlib.h> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Get the shared Display +/// +/// This function increments the reference count of the display, +/// it must be matched with a call to CloseDisplay. +/// +/// \return Pointer to the shared display +/// +//////////////////////////////////////////////////////////// +Display* OpenDisplay(); + +//////////////////////////////////////////////////////////// +/// \brief Release a reference to the shared +/// +/// \param display Display to release +/// +//////////////////////////////////////////////////////////// +void CloseDisplay(Display* display); + +} // namespace priv + +} // namespace sf + + +#endif // SFML_SHAREDDISPLAY_HPP diff --git a/src/SFML/Window/Unix/GlxContext.cpp b/src/SFML/Window/Unix/GlxContext.cpp new file mode 100644 index 0000000..eac6099 --- /dev/null +++ b/src/SFML/Window/Unix/GlxContext.cpp @@ -0,0 +1,341 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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/Unix/GlxContext.hpp> +#include <SFML/Window/Unix/WindowImplX11.hpp> +#include <SFML/Window/Unix/Display.hpp> +#include <SFML/OpenGL.hpp> +#include <SFML/System/Err.hpp> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +GlxContext::GlxContext(GlxContext* shared) : +m_window (0), +m_context (NULL), +m_ownsWindow(true) +{ + // Open a connection with the X server + m_display = OpenDisplay(); + + // Create a dummy window (disabled and hidden) + int screen = DefaultScreen(m_display); + m_window = XCreateWindow(m_display, + RootWindow(m_display, screen), + 0, 0, + 1, 1, + 0, + DefaultDepth(m_display, screen), + InputOutput, + DefaultVisual(m_display, screen), + 0, NULL); + + // Create the context + createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, ContextSettings()); +} + + +//////////////////////////////////////////////////////////// +GlxContext::GlxContext(GlxContext* shared, const ContextSettings& settings, const WindowImpl* owner, unsigned int bitsPerPixel) : +m_window (0), +m_context (NULL), +m_ownsWindow(false) +{ + // Open a connection with the X server + // (important: must be the same display as the owner window) + m_display = OpenDisplay(); + + // Get the owner window and its device context + m_window = static_cast< ::Window>(owner->getSystemHandle()); + + // Create the context + if (m_window) + createContext(shared, bitsPerPixel, settings); +} + + +//////////////////////////////////////////////////////////// +GlxContext::GlxContext(GlxContext* shared, const ContextSettings& settings, unsigned int width, unsigned int height) : +m_window (0), +m_context (NULL), +m_ownsWindow(true) +{ + // Open a connection with the X server + m_display = OpenDisplay(); + + // Create the hidden window + int screen = DefaultScreen(m_display); + m_window = XCreateWindow(m_display, + RootWindow(m_display, screen), + 0, 0, + width, height, + 0, + DefaultDepth(m_display, screen), + InputOutput, + DefaultVisual(m_display, screen), + 0, NULL); + + // Create the context + createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, settings); +} + + +//////////////////////////////////////////////////////////// +GlxContext::~GlxContext() +{ + // Destroy the context + if (m_context) + { + if (glXGetCurrentContext() == m_context) + glXMakeCurrent(m_display, None, NULL); + glXDestroyContext(m_display, m_context); + } + + // Destroy the window if we own it + if (m_window && m_ownsWindow) + { + XDestroyWindow(m_display, m_window); + XFlush(m_display); + } + + // Close the connection with the X server + CloseDisplay(m_display); +} + + +//////////////////////////////////////////////////////////// +bool GlxContext::makeCurrent() +{ + return m_context && glXMakeCurrent(m_display, m_window, m_context); +} + + +//////////////////////////////////////////////////////////// +void GlxContext::display() +{ + if (m_window) + glXSwapBuffers(m_display, m_window); +} + + +//////////////////////////////////////////////////////////// +void GlxContext::setVerticalSyncEnabled(bool enabled) +{ + const GLubyte* name = reinterpret_cast<const GLubyte*>("glXSwapIntervalSGI"); + PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = reinterpret_cast<PFNGLXSWAPINTERVALSGIPROC>(glXGetProcAddress(name)); + if (glXSwapIntervalSGI) + glXSwapIntervalSGI(enabled ? 1 : 0); +} + + +//////////////////////////////////////////////////////////// +XVisualInfo GlxContext::selectBestVisual(::Display* display, unsigned int bitsPerPixel, const ContextSettings& settings) +{ + // Retrieve all the visuals + int count; + XVisualInfo* visuals = XGetVisualInfo(display, 0, NULL, &count); + if (visuals) + { + // Evaluate all the returned visuals, and pick the best one + int bestScore = 0xFFFF; + XVisualInfo bestVisual; + for (int i = 0; i < count; ++i) + { + // Check mandatory attributes + int doubleBuffer; + glXGetConfig(display, &visuals[i], GLX_DOUBLEBUFFER, &doubleBuffer); + if (!doubleBuffer) + continue; + + // Extract the components of the current visual + int red, green, blue, alpha, depth, stencil, multiSampling, samples; + glXGetConfig(display, &visuals[i], GLX_RED_SIZE, &red); + glXGetConfig(display, &visuals[i], GLX_GREEN_SIZE, &green); + glXGetConfig(display, &visuals[i], GLX_BLUE_SIZE, &blue); + glXGetConfig(display, &visuals[i], GLX_ALPHA_SIZE, &alpha); + glXGetConfig(display, &visuals[i], GLX_DEPTH_SIZE, &depth); + glXGetConfig(display, &visuals[i], GLX_STENCIL_SIZE, &stencil); + glXGetConfig(display, &visuals[i], GLX_SAMPLE_BUFFERS_ARB, &multiSampling); + glXGetConfig(display, &visuals[i], GLX_SAMPLES_ARB, &samples); + + // Evaluate the visual + int color = red + green + blue + alpha; + int score = evaluateFormat(bitsPerPixel, settings, color, depth, stencil, multiSampling ? samples : 0); + + // If it's better than the current best, make it the new best + if (score < bestScore) + { + bestScore = score; + bestVisual = visuals[i]; + } + } + + // Free the array of visuals + XFree(visuals); + + return bestVisual; + } + else + { + // Should never happen... + err() << "No GLX visual found. You should check your graphics driver" << std::endl; + + return XVisualInfo(); + } +} + + +//////////////////////////////////////////////////////////// +void GlxContext::createContext(GlxContext* shared, unsigned int bitsPerPixel, const ContextSettings& settings) +{ + XVisualInfo* visualInfo = NULL; + + // Save the creation settings + m_settings = settings; + + // Get the context to share display lists with + GLXContext toShare = shared ? shared->m_context : NULL; + + // Create the OpenGL context -- first try context versions >= 3.0 if it is requested (they require special code) + if (m_settings.majorVersion >= 3) + { + const GLubyte* name = reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB"); + PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(name)); + if (glXCreateContextAttribsARB) + { + // Select a GLXFB config that matches the requested context settings + int nbConfigs = 0; + int fbAttributes[] = + { + GLX_DEPTH_SIZE, static_cast<int>(settings.depthBits), + GLX_STENCIL_SIZE, static_cast<int>(settings.stencilBits), + GLX_SAMPLE_BUFFERS, settings.antialiasingLevel > 0, + GLX_SAMPLES, static_cast<int>(settings.antialiasingLevel), + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, bitsPerPixel == 32 ? 8 : 0, + GLX_DOUBLEBUFFER, True, + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_CONFIG_CAVEAT, GLX_NONE, + None + }; + GLXFBConfig* configs = glXChooseFBConfig(m_display, DefaultScreen(m_display), fbAttributes, &nbConfigs); + if (configs && nbConfigs) + { + while (!m_context && (m_settings.majorVersion >= 3)) + { + // Create the context + int attributes[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, static_cast<int>(m_settings.majorVersion), + GLX_CONTEXT_MINOR_VERSION_ARB, static_cast<int>(m_settings.minorVersion), + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + 0, 0 + }; + m_context = glXCreateContextAttribsARB(m_display, configs[0], toShare, true, attributes); + + if (m_context) + { + // Ok: retrieve the config's visual + visualInfo = glXGetVisualFromFBConfig(m_display, configs[0]); + } + else + { + // If we couldn't create the context, lower the version number and try again -- stop at 3.0 + // Invalid version numbers will be generated by this algorithm (like 3.9), but we really don't care + if (m_settings.minorVersion > 0) + { + // If the minor version is not 0, we decrease it and try again + m_settings.minorVersion--; + } + else + { + // If the minor version is 0, we decrease the major version + m_settings.majorVersion--; + m_settings.minorVersion = 9; + } + } + } + XFree(configs); + } + } + } + + // If the OpenGL >= 3.0 context failed or if we don't want one, create a regular OpenGL 1.x/2.x context + if (!m_context) + { + // set the context version to 2.0 (arbitrary) + m_settings.majorVersion = 2; + m_settings.minorVersion = 0; + + // Retrieve the attributes of the target window + XWindowAttributes windowAttributes; + if (XGetWindowAttributes(m_display, m_window, &windowAttributes) == 0) + { + err() << "Failed to get the window attributes" << std::endl; + return; + } + + // Get its visual + XVisualInfo tpl; + tpl.screen = DefaultScreen(m_display); + tpl.visualid = XVisualIDFromVisual(windowAttributes.visual); + int nbVisuals = 0; + visualInfo = XGetVisualInfo(m_display, VisualIDMask | VisualScreenMask, &tpl, &nbVisuals); + + // Create the context, using the target window's visual + m_context = glXCreateContext(m_display, visualInfo, toShare, true); + if (!m_context) + { + err() << "Failed to create an OpenGL context for this window" << std::endl; + return; + } + } + + // Update the creation settings from the chosen format + int depth, stencil, multiSampling, samples; + glXGetConfig(m_display, visualInfo, GLX_DEPTH_SIZE, &depth); + glXGetConfig(m_display, visualInfo, GLX_STENCIL_SIZE, &stencil); + glXGetConfig(m_display, visualInfo, GLX_SAMPLE_BUFFERS_ARB, &multiSampling); + glXGetConfig(m_display, visualInfo, GLX_SAMPLES_ARB, &samples); + m_settings.depthBits = static_cast<unsigned int>(depth); + m_settings.stencilBits = static_cast<unsigned int>(stencil); + m_settings.antialiasingLevel = multiSampling ? samples : 0; + + // Free the visual info + XFree(visualInfo); +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/Unix/GlxContext.hpp b/src/SFML/Window/Unix/GlxContext.hpp new file mode 100644 index 0000000..0b5982f --- /dev/null +++ b/src/SFML/Window/Unix/GlxContext.hpp @@ -0,0 +1,148 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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 SFML_GLXCONTEXT_HPP +#define SFML_GLXCONTEXT_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include <SFML/Window/GlContext.hpp> +#include <X11/Xlib.h> +#include <GL/glx.h> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Linux (GLX) implementation of OpenGL contexts +/// +//////////////////////////////////////////////////////////// +class GlxContext : public GlContext +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Create a new default context + /// + /// \param shared Context to share the new one with (can be NULL) + /// + //////////////////////////////////////////////////////////// + GlxContext(GlxContext* shared); + + //////////////////////////////////////////////////////////// + /// \brief Create a new context attached to a window + /// + /// \param shared Context to share the new one with + /// \param settings Creation parameters + /// \param owner Pointer to the owner window + /// \param bitsPerPixel Pixel depth, in bits per pixel + /// + //////////////////////////////////////////////////////////// + GlxContext(GlxContext* shared, const ContextSettings& settings, const WindowImpl* owner, unsigned int bitsPerPixel); + + //////////////////////////////////////////////////////////// + /// \brief Create a new context that embeds its own rendering target + /// + /// \param shared Context to share the new one with + /// \param settings Creation parameters + /// \param width Back buffer width, in pixels + /// \param height Back buffer height, in pixels + /// + //////////////////////////////////////////////////////////// + GlxContext(GlxContext* shared, const ContextSettings& settings, unsigned int width, unsigned int height); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~GlxContext(); + + //////////////////////////////////////////////////////////// + /// \brief Activate the context as the current target for rendering + /// + /// \return True on success, false if any error happened + /// + //////////////////////////////////////////////////////////// + virtual bool makeCurrent(); + + //////////////////////////////////////////////////////////// + /// \brief Display what has been rendered to the context so far + /// + //////////////////////////////////////////////////////////// + virtual void display(); + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable vertical synchronization + /// + /// Activating vertical synchronization will limit the number + /// of frames displayed to the refresh rate of the monitor. + /// This can avoid some visual artifacts, and limit the framerate + /// to a good value (but not constant across different computers). + /// + /// \param enabled True to enable v-sync, false to deactivate + /// + //////////////////////////////////////////////////////////// + virtual void setVerticalSyncEnabled(bool enabled); + + //////////////////////////////////////////////////////////// + /// \brief Select the best GLX visual for a given set of settings + /// + /// \param display X display + /// \param bitsPerPixel Pixel depth, in bits per pixel + /// \param settings Requested context settings + /// + /// \return The best visual + /// + //////////////////////////////////////////////////////////// + static XVisualInfo selectBestVisual(::Display* display, unsigned int bitsPerPixel, const ContextSettings& settings); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Create the context + /// + /// \param shared Context to share the new one with (can be NULL) + /// \param bitsPerPixel Pixel depth, in bits per pixel + /// \param settings Creation parameters + /// + //////////////////////////////////////////////////////////// + void createContext(GlxContext* shared, unsigned int bitsPerPixel, const ContextSettings& settings); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + ::Display* m_display; ///< Connection to the X server + ::Window m_window; ///< Window to which the context is attached + GLXContext m_context; ///< OpenGL context + bool m_ownsWindow; ///< Do we own the window associated to the context? +}; + +} // namespace priv + +} // namespace sf + +#endif // SFML_GLXCONTEXT_HPP diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp new file mode 100644 index 0000000..8ff7697 --- /dev/null +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -0,0 +1,324 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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/Unix/InputImpl.hpp> +#include <SFML/Window/Window.hpp> +#include <SFML/Window/Unix/Display.hpp> +#include <X11/Xlib.h> +#include <X11/keysym.h> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +bool InputImpl::isKeyPressed(Keyboard::Key key) +{ + // Get the corresponding X11 keysym + KeySym keysym = 0; + switch (key) + { + case Keyboard::A: keysym = XK_A; break; + case Keyboard::B: keysym = XK_B; break; + case Keyboard::C: keysym = XK_C; break; + case Keyboard::D: keysym = XK_D; break; + case Keyboard::E: keysym = XK_E; break; + case Keyboard::F: keysym = XK_F; break; + case Keyboard::G: keysym = XK_G; break; + case Keyboard::H: keysym = XK_H; break; + case Keyboard::I: keysym = XK_I; break; + case Keyboard::J: keysym = XK_J; break; + case Keyboard::K: keysym = XK_K; break; + case Keyboard::L: keysym = XK_L; break; + case Keyboard::M: keysym = XK_M; break; + case Keyboard::N: keysym = XK_N; break; + case Keyboard::O: keysym = XK_O; break; + case Keyboard::P: keysym = XK_P; break; + case Keyboard::Q: keysym = XK_Q; break; + case Keyboard::R: keysym = XK_R; break; + case Keyboard::S: keysym = XK_S; break; + case Keyboard::T: keysym = XK_T; break; + case Keyboard::U: keysym = XK_U; break; + case Keyboard::V: keysym = XK_V; break; + case Keyboard::W: keysym = XK_W; break; + case Keyboard::X: keysym = XK_X; break; + case Keyboard::Y: keysym = XK_Y; break; + case Keyboard::Z: keysym = XK_Z; break; + case Keyboard::Num0: keysym = XK_0; break; + case Keyboard::Num1: keysym = XK_1; break; + case Keyboard::Num2: keysym = XK_2; break; + case Keyboard::Num3: keysym = XK_3; break; + case Keyboard::Num4: keysym = XK_4; break; + case Keyboard::Num5: keysym = XK_5; break; + case Keyboard::Num6: keysym = XK_6; break; + case Keyboard::Num7: keysym = XK_7; break; + case Keyboard::Num8: keysym = XK_8; break; + case Keyboard::Num9: keysym = XK_9; break; + case Keyboard::Escape: keysym = XK_Escape; break; + case Keyboard::LControl: keysym = XK_Control_L; break; + case Keyboard::LShift: keysym = XK_Shift_L; break; + case Keyboard::LAlt: keysym = XK_Alt_L; break; + case Keyboard::LSystem: keysym = XK_Super_L; break; + case Keyboard::RControl: keysym = XK_Control_R; break; + case Keyboard::RShift: keysym = XK_Shift_R; break; + case Keyboard::RAlt: keysym = XK_Alt_R; break; + case Keyboard::RSystem: keysym = XK_Super_R; break; + case Keyboard::Menu: keysym = XK_Menu; break; + case Keyboard::LBracket: keysym = XK_bracketleft; break; + case Keyboard::RBracket: keysym = XK_bracketright; break; + case Keyboard::SemiColon: keysym = XK_semicolon; break; + case Keyboard::Comma: keysym = XK_comma; break; + case Keyboard::Period: keysym = XK_period; break; + case Keyboard::Quote: keysym = XK_dead_acute; break; + case Keyboard::Slash: keysym = XK_slash; break; + case Keyboard::BackSlash: keysym = XK_backslash; break; + case Keyboard::Tilde: keysym = XK_dead_grave; break; + case Keyboard::Equal: keysym = XK_equal; break; + case Keyboard::Dash: keysym = XK_minus; break; + case Keyboard::Space: keysym = XK_space; break; + case Keyboard::Return: keysym = XK_Return; break; + case Keyboard::BackSpace: keysym = XK_BackSpace; break; + case Keyboard::Tab: keysym = XK_Tab; break; + case Keyboard::PageUp: keysym = XK_Prior; break; + case Keyboard::PageDown: keysym = XK_Next; break; + case Keyboard::End: keysym = XK_End; break; + case Keyboard::Home: keysym = XK_Home; break; + case Keyboard::Insert: keysym = XK_Insert; break; + case Keyboard::Delete: keysym = XK_Delete; break; + case Keyboard::Add: keysym = XK_KP_Add; break; + case Keyboard::Subtract: keysym = XK_KP_Subtract; break; + case Keyboard::Multiply: keysym = XK_KP_Multiply; break; + case Keyboard::Divide: keysym = XK_KP_Divide; break; + case Keyboard::Left: keysym = XK_Left; break; + case Keyboard::Right: keysym = XK_Right; break; + case Keyboard::Up: keysym = XK_Up; break; + case Keyboard::Down: keysym = XK_Down; break; + case Keyboard::Numpad0: keysym = XK_KP_0; break; + case Keyboard::Numpad1: keysym = XK_KP_1; break; + case Keyboard::Numpad2: keysym = XK_KP_2; break; + case Keyboard::Numpad3: keysym = XK_KP_3; break; + case Keyboard::Numpad4: keysym = XK_KP_4; break; + case Keyboard::Numpad5: keysym = XK_KP_5; break; + case Keyboard::Numpad6: keysym = XK_KP_6; break; + case Keyboard::Numpad7: keysym = XK_KP_7; break; + case Keyboard::Numpad8: keysym = XK_KP_8; break; + case Keyboard::Numpad9: keysym = XK_KP_9; break; + case Keyboard::F1: keysym = XK_F1; break; + case Keyboard::F2: keysym = XK_F2; break; + case Keyboard::F3: keysym = XK_F3; break; + case Keyboard::F4: keysym = XK_F4; break; + case Keyboard::F5: keysym = XK_F5; break; + case Keyboard::F6: keysym = XK_F6; break; + case Keyboard::F7: keysym = XK_F7; break; + case Keyboard::F8: keysym = XK_F8; break; + case Keyboard::F9: keysym = XK_F9; break; + case Keyboard::F10: keysym = XK_F10; break; + case Keyboard::F11: keysym = XK_F11; break; + case Keyboard::F12: keysym = XK_F12; break; + case Keyboard::F13: keysym = XK_F13; break; + case Keyboard::F14: keysym = XK_F14; break; + case Keyboard::F15: keysym = XK_F15; break; + case Keyboard::Pause: keysym = XK_Pause; break; + default: keysym = 0; break; + } + + // Open a connection with the X server + Display* display = OpenDisplay(); + + // Convert to keycode + KeyCode keycode = XKeysymToKeycode(display, keysym); + if (keycode != 0) + { + // Get the whole keyboard state + char keys[32]; + XQueryKeymap(display, keys); + + // Close the connection with the X server + CloseDisplay(display); + + // Check our keycode + return (keys[keycode / 8] & (1 << (keycode % 8))) != 0; + } + else + { + // Close the connection with the X server + CloseDisplay(display); + + return false; + } +} + + +//////////////////////////////////////////////////////////// +void InputImpl::setVirtualKeyboardVisible(bool visible) +{ + // Not applicable +} + + +//////////////////////////////////////////////////////////// +bool InputImpl::isMouseButtonPressed(Mouse::Button button) +{ + // Open a connection with the X server + Display* display = OpenDisplay(); + + // we don't care about these but they are required + ::Window root, child; + int wx, wy; + int gx, gy; + + unsigned int buttons = 0; + XQueryPointer(display, DefaultRootWindow(display), &root, &child, &gx, &gy, &wx, &wy, &buttons); + + // Close the connection with the X server + CloseDisplay(display); + + switch (button) + { + case Mouse::Left: return buttons & Button1Mask; + case Mouse::Right: return buttons & Button3Mask; + case Mouse::Middle: return buttons & Button2Mask; + case Mouse::XButton1: return false; // not supported by X + case Mouse::XButton2: return false; // not supported by X + default: return false; + } + + return false; +} + + +//////////////////////////////////////////////////////////// +Vector2i InputImpl::getMousePosition() +{ + // Open a connection with the X server + Display* display = OpenDisplay(); + + // we don't care about these but they are required + ::Window root, child; + int x, y; + unsigned int buttons; + + int gx = 0; + int gy = 0; + XQueryPointer(display, DefaultRootWindow(display), &root, &child, &gx, &gy, &x, &y, &buttons); + + // Close the connection with the X server + CloseDisplay(display); + + return Vector2i(gx, gy); +} + + +//////////////////////////////////////////////////////////// +Vector2i InputImpl::getMousePosition(const Window& relativeTo) +{ + WindowHandle handle = relativeTo.getSystemHandle(); + if (handle) + { + // Open a connection with the X server + Display* display = OpenDisplay(); + + // we don't care about these but they are required + ::Window root, child; + int gx, gy; + unsigned int buttons; + + int x = 0; + int y = 0; + XQueryPointer(display, handle, &root, &child, &gx, &gy, &x, &y, &buttons); + + // Close the connection with the X server + CloseDisplay(display); + + return Vector2i(x, y); + } + else + { + return Vector2i(); + } +} + + +//////////////////////////////////////////////////////////// +void InputImpl::setMousePosition(const Vector2i& position) +{ + // Open a connection with the X server + Display* display = OpenDisplay(); + + XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, position.x, position.y); + XFlush(display); + + // Close the connection with the X server + CloseDisplay(display); +} + + +//////////////////////////////////////////////////////////// +void InputImpl::setMousePosition(const Vector2i& position, const Window& relativeTo) +{ + // Open a connection with the X server + Display* display = OpenDisplay(); + + WindowHandle handle = relativeTo.getSystemHandle(); + if (handle) + { + XWarpPointer(display, None, handle, 0, 0, 0, 0, position.x, position.y); + XFlush(display); + } + + // Close the connection with the X server + CloseDisplay(display); +} + + +//////////////////////////////////////////////////////////// +bool InputImpl::isTouchDown(unsigned int /*finger*/) +{ + // Not applicable + return false; +} + + +//////////////////////////////////////////////////////////// +Vector2i InputImpl::getTouchPosition(unsigned int /*finger*/) +{ + // Not applicable + return Vector2i(); +} + + +//////////////////////////////////////////////////////////// +Vector2i InputImpl::getTouchPosition(unsigned int /*finger*/, const Window& /*relativeTo*/) +{ + // Not applicable + return Vector2i(); +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/Unix/InputImpl.hpp b/src/SFML/Window/Unix/InputImpl.hpp new file mode 100644 index 0000000..32c8f21 --- /dev/null +++ b/src/SFML/Window/Unix/InputImpl.hpp @@ -0,0 +1,168 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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 SFML_INPUTIMPLX11_HPP +#define SFML_INPUTIMPLX11_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include <SFML/Window/Keyboard.hpp> +#include <SFML/Window/Mouse.hpp> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Linux (X11) implementation of inputs (keyboard + mouse) +/// +//////////////////////////////////////////////////////////// +class InputImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Check if a key is pressed + /// + /// \param key Key to check + /// + /// \return True if the key is pressed, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \brief Show or hide the virtual keyboard + /// + /// \param visible True to show, false to hide + /// + //////////////////////////////////////////////////////////// + static void setVirtualKeyboardVisible(bool visible); + + //////////////////////////////////////////////////////////// + /// \brief Check if a mouse button is pressed + /// + /// \param button Button to check + /// + /// \return True if the button is pressed, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isMouseButtonPressed(Mouse::Button button); + + //////////////////////////////////////////////////////////// + /// \brief Get the current position of the mouse in desktop coordinates + /// + /// This function returns the current position of the mouse + /// cursor, in global (desktop) coordinates. + /// + /// \return Current position of the mouse + /// + //////////////////////////////////////////////////////////// + static Vector2i getMousePosition(); + + //////////////////////////////////////////////////////////// + /// \brief Get the current position of the mouse in window coordinates + /// + /// This function returns the current position of the mouse + /// cursor, relative to the given window. + /// If no window is used, it returns desktop coordinates. + /// + /// \param relativeTo Reference window + /// + /// \return Current position of the mouse + /// + //////////////////////////////////////////////////////////// + static Vector2i getMousePosition(const Window& relativeTo); + + //////////////////////////////////////////////////////////// + /// \brief Set the current position of the mouse in desktop coordinates + /// + /// This function sets the current position of the mouse + /// cursor in global (desktop) coordinates. + /// If no window is used, it sets the position in desktop coordinates. + /// + /// \param position New position of the mouse + /// + //////////////////////////////////////////////////////////// + static void setMousePosition(const Vector2i& position); + + //////////////////////////////////////////////////////////// + /// \brief Set the current position of the mouse in window coordinates + /// + /// This function sets the current position of the mouse + /// cursor, relative to the given window. + /// If no window is used, it sets the position in desktop coordinates. + /// + /// \param position New position of the mouse + /// \param relativeTo Reference window + /// + //////////////////////////////////////////////////////////// + static void setMousePosition(const Vector2i& position, const Window& relativeTo); + + //////////////////////////////////////////////////////////// + /// \brief Check if a touch event is currently down + /// + /// \param finger Finger index + /// + /// \return True if \a finger is currently touching the screen, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isTouchDown(unsigned int finger); + + //////////////////////////////////////////////////////////// + /// \brief Get the current position of a touch in desktop coordinates + /// + /// This function returns the current touch position + /// in global (desktop) coordinates. + /// + /// \param finger Finger index + /// + /// \return Current position of \a finger, or undefined if it's not down + /// + //////////////////////////////////////////////////////////// + static Vector2i getTouchPosition(unsigned int finger); + + //////////////////////////////////////////////////////////// + /// \brief Get the current position of a touch in window coordinates + /// + /// This function returns the current touch position + /// in global (desktop) coordinates. + /// + /// \param finger Finger index + /// \param relativeTo Reference window + /// + /// \return Current position of \a finger, or undefined if it's not down + /// + //////////////////////////////////////////////////////////// + static Vector2i getTouchPosition(unsigned int finger, const Window& relativeTo); +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_INPUTIMPLX11_HPP diff --git a/src/SFML/Window/Unix/JoystickImpl.cpp b/src/SFML/Window/Unix/JoystickImpl.cpp new file mode 100644 index 0000000..0b8d479 --- /dev/null +++ b/src/SFML/Window/Unix/JoystickImpl.cpp @@ -0,0 +1,388 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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/JoystickImpl.hpp> +#include <SFML/System/Err.hpp> +#include <sys/inotify.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <errno.h> +#include <libudev.h> +#include <unistd.h> +#include <cstdio> +#include <cstdlib> +#include <sstream> + +namespace +{ + int notifyFd = -1; + int inputFd = -1; + bool plugged[sf::Joystick::Count]; + + void updatePluggedList() + { + udev* udevContext = udev_new(); + + for (unsigned int i = 0; i < sf::Joystick::Count; ++i) + { + std::ostringstream name; + name << "js" << i; + std::string nameString = name.str(); + + int file = ::open(("/dev/input/" + nameString).c_str(), O_RDONLY); + + if (file < 0) + { + plugged[i] = false; + continue; + } + + ::close(file); + + // Check if the device is really a joystick or an + // accelerometer by inspecting whether + // ID_INPUT_ACCELEROMETER is present + if (!udevContext) + { + // Go safe and assume it is if udev isn't available + plugged[i] = true; + continue; + } + + udev_device* udevDevice = udev_device_new_from_subsystem_sysname(udevContext, "input", nameString.c_str()); + + if (!udevDevice) + { + // Go safe and assume it is if we can't get the device + plugged[i] = true; + continue; + } + + if (udev_device_get_property_value(udevDevice, "ID_INPUT_ACCELEROMETER")) + { + // This device is an accelerometer + plugged[i] = false; + } + else + { + // This device is not an accelerometer + // Assume it's a joystick + plugged[i] = true; + } + + udev_device_unref(udevDevice); + } + + if (udevContext) + udev_unref(udevContext); + } + + bool hasInotifyEvent() + { + fd_set descriptorSet; + FD_ZERO(&descriptorSet); + FD_SET(notifyFd, &descriptorSet); + timeval timeout = {0, 0}; + + return (select(notifyFd + 1, &descriptorSet, NULL, NULL, &timeout) > 0) && + FD_ISSET(notifyFd, &descriptorSet); + } + + // Get the joystick name + std::string getJoystickName(int file, unsigned int index) + { + // Get the name + char name[128]; + + if (ioctl(file, JSIOCGNAME(sizeof(name)), name) >= 0) + return std::string(name); + + sf::err() << "Unable to get name for joystick at index " << index << std::endl; + + return std::string("Unknown Joystick"); + } + + // Get a system attribute from a udev device as an unsigned int + unsigned int getUdevAttributeUint(udev_device* device, unsigned int index, const std::string& attributeName) + { + udev_device* udevDeviceParent = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_device"); + + if (!udevDeviceParent) + { + sf::err() << "Unable to get joystick attribute. " + << "Could not find parent USB device for joystick at index " << index << "." << std::endl; + return 0; + } + + const char* attributeString = udev_device_get_sysattr_value(udevDeviceParent, attributeName.c_str()); + + if (!attributeString) + { + sf::err() << "Unable to get joystick attribute '" << attributeName << "'. " + << "Attribute does not exist for joystick at index " << index << "." << std::endl; + return 0; + } + + return static_cast<unsigned int>(std::strtoul(attributeString, NULL, 16)); + } + + // Get a system attribute for a joystick at index as an unsigned int + unsigned int getAttributeUint(unsigned int index, const std::string& attributeName) + { + udev* udevContext = udev_new(); + + if (!udevContext) + { + sf::err() << "Unable to get joystick attribute. " + << "Could not create udev context." << std::endl; + return 0; + } + + std::ostringstream sysname("js"); + sysname << index; + + udev_device* udevDevice = udev_device_new_from_subsystem_sysname(udevContext, "input", sysname.str().c_str()); + + if (!udevDevice) + { + sf::err() << "Unable to get joystick attribute. " + << "Could not find USB device for joystick at index " << index << "." << std::endl; + udev_unref(udevContext); + return 0; + } + + unsigned int attribute = getUdevAttributeUint(udevDevice, index, attributeName); + + udev_device_unref(udevDevice); + udev_unref(udevContext); + return attribute; + } +} + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +void JoystickImpl::initialize() +{ + // Reset the array of plugged joysticks + std::fill(plugged, plugged + Joystick::Count, false); + + // Do an initial scan + updatePluggedList(); + + // Create the inotify instance + notifyFd = inotify_init(); + if (notifyFd < 0) + { + err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl; + return; + } + + // Watch nodes created and deleted in the /dev/input directory + inputFd = inotify_add_watch(notifyFd, "/dev/input", IN_CREATE | IN_DELETE); + if (inputFd < 0) + { + err() << "Failed to initialize inotify, joystick connections and disconnections won't be notified" << std::endl; + + // No need to hang on to the inotify handle in this case + ::close(notifyFd); + notifyFd = -1; + } +} + + +//////////////////////////////////////////////////////////// +void JoystickImpl::cleanup() +{ + // Stop watching the /dev/input directory + if (inputFd >= 0) + inotify_rm_watch(notifyFd, inputFd); + + // Close the inotify file descriptor + if (notifyFd >= 0) + ::close(notifyFd); +} + + +//////////////////////////////////////////////////////////// +bool JoystickImpl::isConnected(unsigned int index) +{ + // See if we can skip scanning if inotify is available + if (notifyFd < 0) + { + // inotify is not available, perform a scan every query + updatePluggedList(); + } + else if (hasInotifyEvent()) + { + // Check if new joysticks were added/removed since last update + // Don't bother decomposing and interpreting the filename, just do a full scan + updatePluggedList(); + + // Flush all the pending events + if (lseek(notifyFd, 0, SEEK_END) < 0) + err() << "Failed to flush inotify of all pending joystick events." << std::endl; + } + + // Then check if the joystick is connected + return plugged[index]; +} + +//////////////////////////////////////////////////////////// +bool JoystickImpl::open(unsigned int index) +{ + if (plugged[index]) + { + std::ostringstream name; + name << "/dev/input/js" << index; + + // Open the joystick's file descriptor (read-only and non-blocking) + m_file = ::open(name.str().c_str(), O_RDONLY | O_NONBLOCK); + if (m_file >= 0) + { + // Retrieve the axes mapping + ioctl(m_file, JSIOCGAXMAP, m_mapping); + + // Get info + m_identification.name = getJoystickName(m_file, index); + m_identification.vendorId = getAttributeUint(index, "idVendor"); + m_identification.productId = getAttributeUint(index, "idProduct"); + + // Reset the joystick state + m_state = JoystickState(); + + return true; + } + } + + return false; +} + + +//////////////////////////////////////////////////////////// +void JoystickImpl::close() +{ + ::close(m_file); +} + + +//////////////////////////////////////////////////////////// +JoystickCaps JoystickImpl::getCapabilities() const +{ + JoystickCaps caps; + + // Get the number of buttons + char buttonCount; + ioctl(m_file, JSIOCGBUTTONS, &buttonCount); + caps.buttonCount = buttonCount; + if (caps.buttonCount > Joystick::ButtonCount) + caps.buttonCount = Joystick::ButtonCount; + + // Get the supported axes + char axesCount; + ioctl(m_file, JSIOCGAXES, &axesCount); + for (int i = 0; i < axesCount; ++i) + { + switch (m_mapping[i]) + { + case ABS_X: caps.axes[Joystick::X] = true; break; + case ABS_Y: caps.axes[Joystick::Y] = true; break; + case ABS_Z: + case ABS_THROTTLE: caps.axes[Joystick::Z] = true; break; + case ABS_RZ: + case ABS_RUDDER: caps.axes[Joystick::R] = true; break; + case ABS_RX: caps.axes[Joystick::U] = true; break; + case ABS_RY: caps.axes[Joystick::V] = true; break; + case ABS_HAT0X: caps.axes[Joystick::PovX] = true; break; + case ABS_HAT0Y: caps.axes[Joystick::PovY] = true; break; + default: break; + } + } + + return caps; +} + + +//////////////////////////////////////////////////////////// +Joystick::Identification JoystickImpl::getIdentification() const +{ + return m_identification; +} + + +//////////////////////////////////////////////////////////// +JoystickState JoystickImpl::JoystickImpl::update() +{ + // pop events from the joystick file + js_event joyState; + while (read(m_file, &joyState, sizeof(joyState)) > 0) + { + switch (joyState.type & ~JS_EVENT_INIT) + { + // An axis was moved + case JS_EVENT_AXIS: + { + float value = joyState.value * 100.f / 32767.f; + switch (m_mapping[joyState.number]) + { + case ABS_X: m_state.axes[Joystick::X] = value; break; + case ABS_Y: m_state.axes[Joystick::Y] = value; break; + case ABS_Z: + case ABS_THROTTLE: m_state.axes[Joystick::Z] = value; break; + case ABS_RZ: + case ABS_RUDDER: m_state.axes[Joystick::R] = value; break; + case ABS_RX: m_state.axes[Joystick::U] = value; break; + case ABS_RY: m_state.axes[Joystick::V] = value; break; + case ABS_HAT0X: m_state.axes[Joystick::PovX] = value; break; + case ABS_HAT0Y: m_state.axes[Joystick::PovY] = value; break; + default: break; + } + break; + } + + // A button was pressed + case JS_EVENT_BUTTON: + { + if (joyState.number < Joystick::ButtonCount) + m_state.buttons[joyState.number] = (joyState.value != 0); + break; + } + } + } + + // Check the connection state of the joystick (read() fails with an error != EGAIN if it's no longer connected) + m_state.connected = (errno == EAGAIN); + + return m_state; +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/Unix/JoystickImpl.hpp b/src/SFML/Window/Unix/JoystickImpl.hpp new file mode 100644 index 0000000..50777ef --- /dev/null +++ b/src/SFML/Window/Unix/JoystickImpl.hpp @@ -0,0 +1,126 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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 SFML_JOYSTICKIMPLLINUX_HPP +#define SFML_JOYSTICKIMPLLINUX_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include <linux/joystick.h> +#include <fcntl.h> +#include <string> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Linux implementation of joysticks +/// +//////////////////////////////////////////////////////////// +class JoystickImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Perform the global initialization of the joystick module + /// + //////////////////////////////////////////////////////////// + static void initialize(); + + //////////////////////////////////////////////////////////// + /// \brief Perform the global cleanup of the joystick module + /// + //////////////////////////////////////////////////////////// + static void cleanup(); + + //////////////////////////////////////////////////////////// + /// \brief Check if a joystick is currently connected + /// + /// \param index Index of the joystick to check + /// + /// \return True if the joystick is connected, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isConnected(unsigned int index); + + //////////////////////////////////////////////////////////// + /// \brief Open the joystick + /// + /// \param index Index assigned to the joystick + /// + /// \return True on success, false on failure + /// + //////////////////////////////////////////////////////////// + bool open(unsigned int index); + + //////////////////////////////////////////////////////////// + /// \brief Close the joystick + /// + //////////////////////////////////////////////////////////// + void close(); + + //////////////////////////////////////////////////////////// + /// \brief Get the joystick capabilities + /// + /// \return Joystick capabilities + /// + //////////////////////////////////////////////////////////// + JoystickCaps getCapabilities() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the joystick identification + /// + /// \return Joystick identification + /// + //////////////////////////////////////////////////////////// + Joystick::Identification getIdentification() const; + + //////////////////////////////////////////////////////////// + /// \brief Update the joystick and get its new state + /// + /// \return Joystick state + /// + //////////////////////////////////////////////////////////// + JoystickState update(); + +private: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + int m_file; ///< File descriptor of the joystick + char m_mapping[ABS_MAX + 1]; ///< Axes mapping (index to axis id) + JoystickState m_state; ///< Current state of the joystick + sf::Joystick::Identification m_identification; ///< Identification of the joystick +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_JOYSTICKIMPLLINUX_HPP diff --git a/src/SFML/Window/Unix/SensorImpl.cpp b/src/SFML/Window/Unix/SensorImpl.cpp new file mode 100644 index 0000000..be5e439 --- /dev/null +++ b/src/SFML/Window/Unix/SensorImpl.cpp @@ -0,0 +1,88 @@ +//////////////////////////////////////////////////////////// +// +// 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/SensorImpl.hpp> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +void SensorImpl::initialize() +{ + // To be implemented +} + + +//////////////////////////////////////////////////////////// +void SensorImpl::cleanup() +{ + // To be implemented +} + + +//////////////////////////////////////////////////////////// +bool SensorImpl::isAvailable(Sensor::Type /*sensor*/) +{ + // To be implemented + return false; +} + + +//////////////////////////////////////////////////////////// +bool SensorImpl::open(Sensor::Type /*sensor*/) +{ + // To be implemented + return false; +} + + +//////////////////////////////////////////////////////////// +void SensorImpl::close() +{ + // To be implemented +} + + +//////////////////////////////////////////////////////////// +Vector3f SensorImpl::update() +{ + // To be implemented + return Vector3f(0, 0, 0); +} + + +//////////////////////////////////////////////////////////// +void SensorImpl::setEnabled(bool /*enabled*/) +{ + // To be implemented +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/Unix/SensorImpl.hpp b/src/SFML/Window/Unix/SensorImpl.hpp new file mode 100644 index 0000000..bbd705b --- /dev/null +++ b/src/SFML/Window/Unix/SensorImpl.hpp @@ -0,0 +1,101 @@ +//////////////////////////////////////////////////////////// +// +// 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. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_SENSORIMPLUNIX_HPP +#define SFML_SENSORIMPLUNIX_HPP + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Unix implementation of sensors +/// +//////////////////////////////////////////////////////////// +class SensorImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Perform the global initialization of the sensor module + /// + //////////////////////////////////////////////////////////// + static void initialize(); + + //////////////////////////////////////////////////////////// + /// \brief Perform the global cleanup of the sensor module + /// + //////////////////////////////////////////////////////////// + static void cleanup(); + + //////////////////////////////////////////////////////////// + /// \brief Check if a sensor is available + /// + /// \param sensor Sensor to check + /// + /// \return True if the sensor is available, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isAvailable(Sensor::Type sensor); + + //////////////////////////////////////////////////////////// + /// \brief Open the sensor + /// + /// \param sensor Type of the sensor + /// + /// \return True on success, false on failure + /// + //////////////////////////////////////////////////////////// + bool open(Sensor::Type sensor); + + //////////////////////////////////////////////////////////// + /// \brief Close the sensor + /// + //////////////////////////////////////////////////////////// + void close(); + + //////////////////////////////////////////////////////////// + /// \brief Update the sensor and get its new value + /// + /// \return Sensor value + /// + //////////////////////////////////////////////////////////// + Vector3f update(); + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable the sensor + /// + /// \param enabled True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + void setEnabled(bool enabled); +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_SENSORIMPLUNIX_HPP diff --git a/src/SFML/Window/Unix/VideoModeImpl.cpp b/src/SFML/Window/Unix/VideoModeImpl.cpp new file mode 100644 index 0000000..c7e6204 --- /dev/null +++ b/src/SFML/Window/Unix/VideoModeImpl.cpp @@ -0,0 +1,176 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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/VideoModeImpl.hpp> +#include <SFML/Window/Unix/Display.hpp> +#include <SFML/System/Err.hpp> +#include <X11/Xlib.h> +#include <X11/extensions/Xrandr.h> +#include <algorithm> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +std::vector<VideoMode> VideoModeImpl::getFullscreenModes() +{ + std::vector<VideoMode> modes; + + // Open a connection with the X server + Display* display = OpenDisplay(); + if (display) + { + // Retrieve the default screen number + int screen = DefaultScreen(display); + + // Check if the XRandR extension is present + int version; + if (XQueryExtension(display, "RANDR", &version, &version, &version)) + { + // Get the current configuration + XRRScreenConfiguration* config = XRRGetScreenInfo(display, RootWindow(display, screen)); + if (config) + { + // Get the available screen sizes + int nbSizes; + XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes); + if (sizes && (nbSizes > 0)) + { + // Get the list of supported depths + int nbDepths = 0; + int* depths = XListDepths(display, screen, &nbDepths); + if (depths && (nbDepths > 0)) + { + // Combine depths and sizes to fill the array of supported modes + for (int i = 0; i < nbDepths; ++i) + { + for (int j = 0; j < nbSizes; ++j) + { + // Convert to VideoMode + VideoMode mode(sizes[j].width, sizes[j].height, depths[i]); + + // Add it only if it is not already in the array + if (std::find(modes.begin(), modes.end(), mode) == modes.end()) + modes.push_back(mode); + } + } + + // Free the array of depths + XFree(depths); + } + } + + // Free the configuration instance + XRRFreeScreenConfigInfo(config); + } + else + { + // Failed to get the screen configuration + err() << "Failed to retrieve the screen configuration while trying to get the supported video modes" << std::endl; + } + } + else + { + // XRandr extension is not supported: we cannot get the video modes + err() << "Failed to use the XRandR extension while trying to get the supported video modes" << std::endl; + } + + // Close the connection with the X server + CloseDisplay(display); + } + else + { + // We couldn't connect to the X server + err() << "Failed to connect to the X server while trying to get the supported video modes" << std::endl; + } + + return modes; +} + + +//////////////////////////////////////////////////////////// +VideoMode VideoModeImpl::getDesktopMode() +{ + VideoMode desktopMode; + + // Open a connection with the X server + Display* display = OpenDisplay(); + if (display) + { + // Retrieve the default screen number + int screen = DefaultScreen(display); + + // Check if the XRandR extension is present + int version; + if (XQueryExtension(display, "RANDR", &version, &version, &version)) + { + // Get the current configuration + XRRScreenConfiguration* config = XRRGetScreenInfo(display, RootWindow(display, screen)); + if (config) + { + // Get the current video mode + Rotation currentRotation; + int currentMode = XRRConfigCurrentConfiguration(config, ¤tRotation); + + // Get the available screen sizes + int nbSizes; + XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes); + if (sizes && (nbSizes > 0)) + desktopMode = VideoMode(sizes[currentMode].width, sizes[currentMode].height, DefaultDepth(display, screen)); + + // Free the configuration instance + XRRFreeScreenConfigInfo(config); + } + else + { + // Failed to get the screen configuration + err() << "Failed to retrieve the screen configuration while trying to get the desktop video modes" << std::endl; + } + } + else + { + // XRandr extension is not supported: we cannot get the video modes + err() << "Failed to use the XRandR extension while trying to get the desktop video modes" << std::endl; + } + + // Close the connection with the X server + CloseDisplay(display); + } + else + { + // We couldn't connect to the X server + err() << "Failed to connect to the X server while trying to get the desktop video modes" << std::endl; + } + + return desktopMode; +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp new file mode 100644 index 0000000..9e29757 --- /dev/null +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -0,0 +1,1109 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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/Unix/WindowImplX11.hpp> +#include <SFML/Window/Unix/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 <libgen.h> +#include <unistd.h> +#include <cstring> +#include <sstream> +#include <vector> +#include <string> +#include <iterator> +#include <algorithm> + +#ifdef SFML_OPENGL_ES + #include <SFML/Window/EglContext.hpp> + typedef sf::priv::EglContext ContextType; +#else + #include <SFML/Window/Unix/GlxContext.hpp> + typedef sf::priv::GlxContext ContextType; +#endif + +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// +namespace +{ + sf::priv::WindowImplX11* fullscreenWindow = NULL; + std::vector<sf::priv::WindowImplX11*> allWindows; + 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 + void findExecutableName(char* buffer, std::size_t bufferSize) + { + //Default fallback name + const char* executableName = "sfml"; + std::size_t length = readlink("/proc/self/exe", buffer, bufferSize); + if ((length > 0) && (length < bufferSize)) + { + // Remove the path to keep the executable name only + buffer[length] = '\0'; + executableName = basename(buffer); + } + std::memmove(buffer, executableName, std::strlen(executableName) + 1); + } +} + + +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), +m_useSizeHints(false) +{ + // 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 required events + XSelectInput(m_display, m_window, eventMask & ~ButtonPressMask); + + // Do some common initializations + initialize(); + } +} + + +//////////////////////////////////////////////////////////// +WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings) : +m_window (0), +m_inputMethod (NULL), +m_inputContext(NULL), +m_isExternal (false), +m_atomClose (0), +m_oldVideoMode(-1), +m_hiddenCursor(0), +m_keyRepeat (true), +m_previousSize(-1, -1), +m_useSizeHints(false) +{ + // Open a connection with the X server + m_display = OpenDisplay(); + m_screen = DefaultScreen(m_display); + ::Window root = RootWindow(m_display, m_screen); + + // 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); + + // Choose the visual according to the context settings + XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings); + + // Define the window attributes + XSetWindowAttributes attributes; + attributes.override_redirect = fullscreen; + attributes.event_mask = eventMask; + attributes.colormap = XCreateColormap(m_display, root, visualInfo.visual, AllocNone); + + // Create the window + m_window = XCreateWindow(m_display, + root, + left, top, + width, height, + 0, + visualInfo.depth, + InputOutput, + visualInfo.visual, + CWEventMask | CWOverrideRedirect | CWColormap, &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)) + { + m_useSizeHints = true; + 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) + char windowClass[512]; + findExecutableName(windowClass, sizeof(windowClass)); + XClassHint* classHint = XAllocClassHint(); + classHint->res_name = windowClass; + classHint->res_class = 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); + + // Remove this window from the global list of windows (required for focus request) + allWindows.erase(std::find(allWindows.begin(), allWindows.end(), this)); +} + + +//////////////////////////////////////////////////////////// +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) +{ + // If we used size hint to fix the size of the window, we must update them + if (m_useSizeHints) + { + XSizeHints* sizeHints = XAllocSizeHints(); + sizeHints->flags = PMinSize | PMaxSize; + sizeHints->min_width = sizeHints->max_width = size.x; + sizeHints->min_height = sizeHints->max_height = size.y; + XSetWMNormalHints(m_display, m_window, sizeHints); + XFree(sizeHints); + } + + 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<Uint8> utf8Title; + 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::requestFocus() +{ + // Focus is only stolen among SFML windows, not between applications + // Check the global list of windows to find out whether an SFML window has the focus + // Note: can't handle console and other non-SFML windows belonging to the application. + bool sfmlWindowFocused = false; + for (std::vector<WindowImplX11*>::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr) + { + if ((*itr)->hasFocus()) + { + sfmlWindowFocused = true; + break; + } + } + + // Check if window is viewable (not on other desktop, ...) + // TODO: Check also if minimized + XWindowAttributes attributes; + if (XGetWindowAttributes(m_display, m_window, &attributes) == 0) + { + sf::err() << "Failed to check if window is viewable while requesting focus" << std::endl; + return; // error getting attribute + } + + bool windowViewable = (attributes.map_state == IsViewable); + + if (sfmlWindowFocused && windowViewable) + { + // Another SFML window of this application has the focus and the current window is viewable: + // steal focus (i.e. bring window to the front and give it input focus) + XRaiseWindow(m_display, m_window); + XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime); + } + else + { + // Otherwise: display urgency hint (flashing application logo) + // Ensure WM hints exist, allocate if necessary + XWMHints* hints = XGetWMHints(m_display, m_window); + if (hints == NULL) + hints = XAllocWMHints(); + + // Add urgency (notification) flag to hints + hints->flags |= XUrgencyHint; + XSetWMHints(m_display, m_window, hints); + XFree(hints); + } +} + + +//////////////////////////////////////////////////////////// +bool WindowImplX11::hasFocus() const +{ + ::Window focusedWindow = 0; + int revertToReturn = 0; + XGetInputFocus(m_display, &focusedWindow, &revertToReturn); + + return m_window == focusedWindow; +} + + +//////////////////////////////////////////////////////////// +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, ¤tRotation); + + // 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() +{ + // 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 hidden cursor + createHiddenCursor(); + + // Flush the commands queue + XFlush(m_display); + + // Add this window to the global list of windows (required for focus request) + allWindows.push_back(this); +} + + +//////////////////////////////////////////////////////////// +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, ¤tRotation); + + // 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 + // (code shamelessly taken from SDL) + if (windowEvent.type == KeyRelease) + { + // Check if there's a matching KeyPress event in the queue + XEvent nextEvent; + if (XPending(m_display)) + { + // Grab it but don't remove it from the queue, it still needs to be processed :) + XPeekEvent(m_display, &nextEvent); + if (nextEvent.type == KeyPress) + { + // Check if it is a duplicated event (same timestamp as the KeyRelease event) + if ((nextEvent.xkey.keycode == windowEvent.xkey.keycode) && + (nextEvent.xkey.time - windowEvent.xkey.time < 2)) + { + // If we don't want repeated events, remove the next KeyPress from the queue + if (!m_keyRepeat) + XNextEvent(m_display, &nextEvent); + + // This KeyRelease is a repeated event and we don't want it + 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); + + // If the window has been previously marked urgent (notification) as a result of a focus request, undo that + XWMHints* hints = XGetWMHints(m_display, m_window); + if (hints != NULL) + { + // Remove urgency (notification) flag from hints + hints->flags &= ~XUrgencyHint; + XSetWMHints(m_display, m_window, hints); + XFree(hints); + } + 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 actually 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; + } + + // Parent window changed + case ReparentNotify: + { + XSync(m_display, True); // Discard remaining events + 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 diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp new file mode 100644 index 0000000..d824492 --- /dev/null +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -0,0 +1,250 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2014 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 SFML_WINDOWIMPLX11_HPP +#define SFML_WINDOWIMPLX11_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include <SFML/Window/Event.hpp> +#include <SFML/Window/WindowImpl.hpp> +#include <SFML/System/String.hpp> +#include <X11/Xlib.h> +#include <set> + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Linux (X11) implementation of WindowImpl +/// +//////////////////////////////////////////////////////////// +class WindowImplX11 : public WindowImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Construct the window implementation from an existing control + /// + /// \param handle Platform-specific handle of the control + /// + //////////////////////////////////////////////////////////// + WindowImplX11(WindowHandle handle); + + //////////////////////////////////////////////////////////// + /// \brief Create the window implementation + /// + /// \param mode Video mode to use + /// \param title Title of the window + /// \param style Window style (resizable, fixed, or fullscren) + /// \param settings Additional settings for the underlying OpenGL context + /// + //////////////////////////////////////////////////////////// + WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~WindowImplX11(); + + //////////////////////////////////////////////////////////// + /// \brief Get the OS-specific handle of the window + /// + /// \return Handle of the window + /// + //////////////////////////////////////////////////////////// + virtual WindowHandle getSystemHandle() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the position of the window + /// + /// \return Position of the window, in pixels + /// + //////////////////////////////////////////////////////////// + virtual Vector2i getPosition() const; + + //////////////////////////////////////////////////////////// + /// \brief Change the position of the window on screen + /// + /// \param position New position of the window, in pixels + /// + //////////////////////////////////////////////////////////// + virtual void setPosition(const Vector2i& position); + + //////////////////////////////////////////////////////////// + /// \brief Get the client size of the window + /// + /// \return Size of the window, in pixels + /// + //////////////////////////////////////////////////////////// + virtual Vector2u getSize() const; + + //////////////////////////////////////////////////////////// + /// \brief Change the size of the rendering region of the window + /// + /// \param size New size, in pixels + /// + //////////////////////////////////////////////////////////// + virtual void setSize(const Vector2u& size); + + //////////////////////////////////////////////////////////// + /// \brief Change the title of the window + /// + /// \param title New title + /// + //////////////////////////////////////////////////////////// + virtual void setTitle(const String& title); + + //////////////////////////////////////////////////////////// + /// \brief Change the window's icon + /// + /// \param width Icon's width, in pixels + /// \param height Icon's height, in pixels + /// \param pixels Pointer to the pixels in memory, format must be RGBA 32 bits + /// + //////////////////////////////////////////////////////////// + virtual void setIcon(unsigned int width, unsigned int height, const Uint8* pixels); + + //////////////////////////////////////////////////////////// + /// \brief Show or hide the window + /// + /// \param visible True to show, false to hide + /// + //////////////////////////////////////////////////////////// + virtual void setVisible(bool visible); + + //////////////////////////////////////////////////////////// + /// \brief Show or hide the mouse cursor + /// + /// \param visible True to show, false to hide + /// + //////////////////////////////////////////////////////////// + virtual void setMouseCursorVisible(bool visible); + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable automatic key-repeat + /// + /// \param enabled True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + virtual void setKeyRepeatEnabled(bool enabled); + + //////////////////////////////////////////////////////////// + /// \brief Request the current window to be made the active + /// foreground window + /// + //////////////////////////////////////////////////////////// + virtual void requestFocus(); + + //////////////////////////////////////////////////////////// + /// \brief Check whether the window has the input focus + /// + /// \return True if window has focus, false otherwise + /// + //////////////////////////////////////////////////////////// + virtual bool hasFocus() const; + +protected: + + //////////////////////////////////////////////////////////// + /// \brief Process incoming events from the operating system + /// + //////////////////////////////////////////////////////////// + virtual void processEvents(); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Switch to fullscreen mode + /// + /// \param Mode video mode to switch to + /// + //////////////////////////////////////////////////////////// + void switchToFullscreen(const VideoMode& mode); + + //////////////////////////////////////////////////////////// + /// \brief Do some common initializations after the window has been created + /// + //////////////////////////////////////////////////////////// + void initialize(); + + //////////////////////////////////////////////////////////// + /// \brief Create a transparent mouse cursor + /// + //////////////////////////////////////////////////////////// + void createHiddenCursor(); + + //////////////////////////////////////////////////////////// + /// \brief Cleanup graphical resources attached to the window + /// + //////////////////////////////////////////////////////////// + void cleanup(); + + //////////////////////////////////////////////////////////// + /// \brief Process an incoming event from the window + /// + /// \param windowEvent Event which has been received + /// + /// \return True if the event was processed, false if it was discarded + /// + //////////////////////////////////////////////////////////// + bool processEvent(XEvent windowEvent); + + //////////////////////////////////////////////////////////// + /// \brief Convert a X11 keysym to SFML key code + /// + /// \param symbol Key symbol to convert + /// + /// \return Corresponding SFML key code + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key keysymToSF(KeySym symbol); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + ::Window m_window; ///< X11 structure defining our window + ::Display* m_display; ///< Pointer to the display + 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 hiding, 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 + +} // namespace sf + + +#endif // SFML_WINDOWIMPLX11_HPP |