diff options
author | James Cowgill <jcowgill@debian.org> | 2018-10-16 22:26:31 +0100 |
---|---|---|
committer | James Cowgill <jcowgill@debian.org> | 2018-10-16 22:26:31 +0100 |
commit | f57a5d96a92d1208d252b7d3f97b3b5c3b69fd12 (patch) | |
tree | f2c152c29b5cac3a1b21cbae02c82b006ae7cdb4 /src | |
parent | 088cbaedcc0634d0949ca8a92236e62a6d6caf71 (diff) | |
parent | 08bb1c372efcc9e2054e64b80959f025c8f43744 (diff) |
Update upstream source from tag 'upstream/2.5.1+dfsg'
Update to upstream version '2.5.1+dfsg'
with Debian dir 8171c41179aa4509b53f92cff2b893d4ceafc883
Diffstat (limited to 'src')
23 files changed, 830 insertions, 303 deletions
diff --git a/src/SFML/Audio/SoundFileReaderWav.cpp b/src/SFML/Audio/SoundFileReaderWav.cpp index dc1dc52..84d92c1 100644 --- a/src/SFML/Audio/SoundFileReaderWav.cpp +++ b/src/SFML/Audio/SoundFileReaderWav.cpp @@ -155,7 +155,11 @@ Uint64 SoundFileReaderWav::read(Int16* samples, Uint64 maxCount) assert(m_stream); Uint64 count = 0; - while ((count < maxCount) && (static_cast<Uint64>(m_stream->tell()) < m_dataEnd)) + Uint64 startPos = m_stream->tell(); + + // Tracking of m_dataEnd is important to prevent sf::Music from reading + // data until EOF, as WAV files may have metadata at the end. + while ((count < maxCount) && (startPos + count * m_bytesPerSample < m_dataEnd)) { switch (m_bytesPerSample) { diff --git a/src/SFML/Graphics/Font.cpp b/src/SFML/Graphics/Font.cpp index 45f6baf..308070a 100644 --- a/src/SFML/Graphics/Font.cpp +++ b/src/SFML/Graphics/Font.cpp @@ -71,10 +71,10 @@ namespace return output; } - // Combine outline thickness, boldness and codepoint into a single 64-bit key - sf::Uint64 combine(float outlineThickness, bool bold, sf::Uint32 codePoint) + // Combine outline thickness, boldness and font glyph index into a single 64-bit key + sf::Uint64 combine(float outlineThickness, bool bold, sf::Uint32 index) { - return (static_cast<sf::Uint64>(reinterpret<sf::Uint32>(outlineThickness)) << 32) | (static_cast<sf::Uint64>(bold) << 31) | codePoint; + return (static_cast<sf::Uint64>(reinterpret<sf::Uint32>(outlineThickness)) << 32) | (static_cast<sf::Uint64>(bold) << 31) | index; } } @@ -346,8 +346,8 @@ const Glyph& Font::getGlyph(Uint32 codePoint, unsigned int characterSize, bool b // Get the page corresponding to the character size GlyphTable& glyphs = m_pages[characterSize].glyphs; - // Build the key by combining the code point, bold flag, and outline thickness - Uint64 key = combine(outlineThickness, bold, codePoint); + // Build the key by combining the glyph index (based on code point), bold flag, and outline thickness + Uint64 key = combine(outlineThickness, bold, FT_Get_Char_Index(static_cast<FT_Face>(m_face), codePoint)); // Search the glyph into the cache GlyphTable::const_iterator it = glyphs.find(key); @@ -727,6 +727,7 @@ IntRect Font::findGlyphRect(Page& page, unsigned int width, unsigned int height) // Make the texture 2 times bigger Texture newTexture; newTexture.create(textureWidth * 2, textureHeight * 2); + newTexture.setSmooth(true); newTexture.update(page.texture); page.texture.swap(newTexture); } @@ -776,17 +777,22 @@ bool Font::setCurrentSize(unsigned int characterSize) const err() << "Failed to set bitmap font size to " << characterSize << std::endl; err() << "Available sizes are: "; for (int i = 0; i < face->num_fixed_sizes; ++i) - err() << face->available_sizes[i].height << " "; + { + const unsigned int size = (face->available_sizes[i].y_ppem + 32) >> 6; + err() << size << " "; + } err() << std::endl; } + else + { + err() << "Failed to set font size to " << characterSize << std::endl; + } } return result == FT_Err_Ok; } - else - { - return true; - } + + return true; } diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index 7eccf82..9a6e301 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -129,7 +129,7 @@ RenderTarget::RenderTarget() : m_defaultView(), m_view (), m_cache (), -m_id (getUniqueId()) +m_id (0) { m_cache.glStatesSet = false; } @@ -547,6 +547,10 @@ void RenderTarget::initialize() // Set GL states only on first draw, so that we don't pollute user's states m_cache.glStatesSet = false; + + // Generate a unique ID for this RenderTarget to track + // whether it is active within a specific context + m_id = getUniqueId(); } @@ -704,7 +708,7 @@ void RenderTarget::drawPrimitives(PrimitiveType type, std::size_t firstVertex, s GLenum mode = modes[type]; // Draw the primitives - glCheck(glDrawArrays(mode, firstVertex, static_cast<GLsizei>(vertexCount))); + glCheck(glDrawArrays(mode, static_cast<GLint>(firstVertex), static_cast<GLsizei>(vertexCount))); } diff --git a/src/SFML/Graphics/RenderTexture.cpp b/src/SFML/Graphics/RenderTexture.cpp index 0805627..51abee9 100644 --- a/src/SFML/Graphics/RenderTexture.cpp +++ b/src/SFML/Graphics/RenderTexture.cpp @@ -161,7 +161,7 @@ bool RenderTexture::setActive(bool active) void RenderTexture::display() { // Update the target texture - if (priv::RenderTextureImplFBO::isAvailable() || setActive(true)) + if (m_impl && (priv::RenderTextureImplFBO::isAvailable() || setActive(true))) { m_impl->updateTexture(m_texture.m_texture); m_texture.m_pixelsFlipped = true; diff --git a/src/SFML/Graphics/RenderTextureImplFBO.cpp b/src/SFML/Graphics/RenderTextureImplFBO.cpp index 301aa04..c0debd7 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.cpp +++ b/src/SFML/Graphics/RenderTextureImplFBO.cpp @@ -53,6 +53,29 @@ namespace // Mutex to protect both active and stale frame buffer sets sf::Mutex mutex; + // This function is called either when a RenderTextureImplFBO is + // destroyed or via contextDestroyCallback when context destruction + // might trigger deletion of its contained stale FBOs + void destroyStaleFBOs() + { + sf::Uint64 contextId = sf::Context::getActiveContextId(); + + for (std::set<std::pair<sf::Uint64, unsigned int> >::iterator iter = staleFrameBuffers.begin(); iter != staleFrameBuffers.end();) + { + if (iter->first == contextId) + { + GLuint frameBuffer = static_cast<GLuint>(iter->second); + glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); + + staleFrameBuffers.erase(iter++); + } + else + { + ++iter; + } + } + } + // Callback that is called every time a context is destroyed void contextDestroyCallback(void* arg) { @@ -79,14 +102,7 @@ namespace } // Destroy stale frame buffer objects - for (std::set<std::pair<sf::Uint64, unsigned int> >::iterator iter = staleFrameBuffers.begin(); iter != staleFrameBuffers.end(); ++iter) - { - if (iter->first == contextId) - { - GLuint frameBuffer = static_cast<GLuint>(iter->second); - glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); - } - } + destroyStaleFBOs(); } } @@ -150,7 +166,7 @@ RenderTextureImplFBO::~RenderTextureImplFBO() staleFrameBuffers.insert(std::make_pair(iter->first, iter->second)); // Clean up FBOs - contextDestroyCallback(0); + destroyStaleFBOs(); // Delete the backup context if we had to create one delete m_context; diff --git a/src/SFML/Graphics/RenderWindow.cpp b/src/SFML/Graphics/RenderWindow.cpp index 906363b..446e906 100644 --- a/src/SFML/Graphics/RenderWindow.cpp +++ b/src/SFML/Graphics/RenderWindow.cpp @@ -81,7 +81,7 @@ bool RenderWindow::setActive(bool active) // If FBOs are available, make sure none are bound when we // try to draw to the default framebuffer of the RenderWindow - if (result && priv::RenderTextureImplFBO::isAvailable()) + if (active && result && priv::RenderTextureImplFBO::isAvailable()) { priv::RenderTextureImplFBO::unbind(); diff --git a/src/SFML/Graphics/Text.cpp b/src/SFML/Graphics/Text.cpp index 3773616..6687551 100644 --- a/src/SFML/Graphics/Text.cpp +++ b/src/SFML/Graphics/Text.cpp @@ -50,15 +50,17 @@ namespace // Add a glyph quad to the vertex array void addGlyphQuad(sf::VertexArray& vertices, sf::Vector2f position, const sf::Color& color, const sf::Glyph& glyph, float italicShear, float outlineThickness = 0) { - float left = glyph.bounds.left; - float top = glyph.bounds.top; - float right = glyph.bounds.left + glyph.bounds.width; - float bottom = glyph.bounds.top + glyph.bounds.height; + float padding = 1.0; - float u1 = static_cast<float>(glyph.textureRect.left); - float v1 = static_cast<float>(glyph.textureRect.top); - float u2 = static_cast<float>(glyph.textureRect.left + glyph.textureRect.width); - float v2 = static_cast<float>(glyph.textureRect.top + glyph.textureRect.height); + float left = glyph.bounds.left - padding; + float top = glyph.bounds.top - padding; + float right = glyph.bounds.left + glyph.bounds.width + padding; + float bottom = glyph.bounds.top + glyph.bounds.height + padding; + + float u1 = static_cast<float>(glyph.textureRect.left) - padding; + float v1 = static_cast<float>(glyph.textureRect.top) - padding; + float u2 = static_cast<float>(glyph.textureRect.left + glyph.textureRect.width) + padding; + float v2 = static_cast<float>(glyph.textureRect.top + glyph.textureRect.height) + padding; vertices.append(sf::Vertex(sf::Vector2f(position.x + left - italicShear * top - outlineThickness, position.y + top - outlineThickness), color, sf::Vector2f(u1, v1))); vertices.append(sf::Vertex(sf::Vector2f(position.x + right - italicShear * top - outlineThickness, position.y + top - outlineThickness), color, sf::Vector2f(u2, v1))); @@ -444,12 +446,15 @@ void Text::ensureGeometryUpdate() const { Uint32 curChar = m_string[i]; + // Skip the \r char to avoid weird graphical issues + if (curChar == '\r') + continue; + // Apply the kerning offset x += m_font->getKerning(prevChar, curChar, m_characterSize); - prevChar = curChar; // If we're using the underlined style and there's a new line, draw a line - if (isUnderlined && (curChar == L'\n')) + if (isUnderlined && (curChar == L'\n' && prevChar != L'\n')) { addLine(m_vertices, x, y, m_fillColor, underlineOffset, underlineThickness); @@ -458,7 +463,7 @@ void Text::ensureGeometryUpdate() const } // If we're using the strike through style and there's a new line, draw a line across all characters - if (isStrikeThrough && (curChar == L'\n')) + if (isStrikeThrough && (curChar == L'\n' && prevChar != L'\n')) { addLine(m_vertices, x, y, m_fillColor, strikeThroughOffset, underlineThickness); @@ -466,6 +471,8 @@ void Text::ensureGeometryUpdate() const addLine(m_outlineVertices, x, y, m_outlineColor, strikeThroughOffset, underlineThickness, m_outlineThickness); } + prevChar = curChar; + // Handle special characters if ((curChar == L' ') || (curChar == L'\n') || (curChar == L'\t')) { diff --git a/src/SFML/Graphics/Texture.cpp b/src/SFML/Graphics/Texture.cpp index faeef7d..fd94acf 100644 --- a/src/SFML/Graphics/Texture.cpp +++ b/src/SFML/Graphics/Texture.cpp @@ -129,6 +129,11 @@ bool Texture::create(unsigned int width, unsigned int height) return false; } + TransientContextLock lock; + + // Make sure that extensions are initialized + priv::ensureExtensionsInit(); + // Compute the internal texture dimensions depending on NPOT textures support Vector2u actualSize(getValidSize(width), getValidSize(height)); @@ -150,8 +155,6 @@ bool Texture::create(unsigned int width, unsigned int height) m_pixelsFlipped = false; m_fboAttachment = false; - TransientContextLock lock; - // Create the OpenGL texture if it doesn't exist yet if (!m_texture) { @@ -160,9 +163,6 @@ bool Texture::create(unsigned int width, unsigned int height) m_texture = static_cast<unsigned int>(texture); } - // Make sure that extensions are initialized - priv::ensureExtensionsInit(); - // Make sure that the current texture binding will be preserved priv::TextureSaver save; @@ -851,11 +851,6 @@ unsigned int Texture::getNativeHandle() const //////////////////////////////////////////////////////////// unsigned int Texture::getValidSize(unsigned int size) { - TransientContextLock lock; - - // Make sure that extensions are initialized - priv::ensureExtensionsInit(); - if (GLEXT_texture_non_power_of_two) { // If hardware supports NPOT textures, then just return the unmodified size diff --git a/src/SFML/Main/MainAndroid.cpp b/src/SFML/Main/MainAndroid.cpp index fcb8500..5e62133 100644 --- a/src/SFML/Main/MainAndroid.cpp +++ b/src/SFML/Main/MainAndroid.cpp @@ -93,6 +93,13 @@ static void initializeMain(ActivityStates* states) ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); states->looper = looper; + /** + * Acquire increments a reference counter on the looper. This keeps android + * from collecting it before the activity thread has a chance to detach its + * input queue. + */ + ALooper_acquire(states->looper); + // Get the default configuration states->config = AConfiguration_new(); AConfiguration_fromAssetManager(states->config, states->activity->assetManager); @@ -338,7 +345,7 @@ static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* wind // Wait for the event to be taken into account by SFML states->updated = false; - while(!states->updated) + while(!(states->updated | states->terminated)) { states->mutex.unlock(); sf::sleep(sf::milliseconds(10)); @@ -363,7 +370,7 @@ static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* wi // Wait for the event to be taken into account by SFML states->updated = false; - while(!states->updated) + while(!(states->updated | states->terminated)) { states->mutex.unlock(); sf::sleep(sf::milliseconds(10)); @@ -410,8 +417,10 @@ static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { sf::Lock lock(states->mutex); - states->inputQueue = NULL; AInputQueue_detachLooper(queue); + states->inputQueue = NULL; + + ALooper_release(states->looper); } } @@ -464,7 +473,7 @@ static void onLowMemory(ANativeActivity* activity) //////////////////////////////////////////////////////////// -void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) +JNIEXPORT void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) { // Create an activity states (will keep us in the know, about events we care) sf::priv::ActivityStates* states = NULL; @@ -542,7 +551,7 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_ // Wait for the main thread to be initialized states->mutex.lock(); - while (!states->initialized) + while (!(states->initialized | states->terminated)) { states->mutex.unlock(); sf::sleep(sf::milliseconds(20)); diff --git a/src/SFML/Network/TcpSocket.cpp b/src/SFML/Network/TcpSocket.cpp index e11f76b..d8effa1 100644 --- a/src/SFML/Network/TcpSocket.cpp +++ b/src/SFML/Network/TcpSocket.cpp @@ -246,7 +246,7 @@ Socket::Status TcpSocket::send(const void* data, std::size_t size, std::size_t& for (sent = 0; sent < size; sent += result) { // Send a chunk of data - result = ::send(getHandle(), static_cast<const char*>(data) + sent, size - sent, flags); + result = ::send(getHandle(), static_cast<const char*>(data) + sent, static_cast<int>(size - sent), flags); // Check for errors if (result < 0) diff --git a/src/SFML/System/FileInputStream.cpp b/src/SFML/System/FileInputStream.cpp index 852470d..89e77c4 100644 --- a/src/SFML/System/FileInputStream.cpp +++ b/src/SFML/System/FileInputStream.cpp @@ -26,7 +26,7 @@ // Headers //////////////////////////////////////////////////////////// #include <SFML/System/FileInputStream.hpp> -#ifdef ANDROID +#ifdef SFML_SYSTEM_ANDROID #include <SFML/System/Android/ResourceStream.hpp> #endif @@ -44,7 +44,7 @@ FileInputStream::FileInputStream() //////////////////////////////////////////////////////////// FileInputStream::~FileInputStream() { -#ifdef ANDROID +#ifdef SFML_SYSTEM_ANDROID if (m_file) delete m_file; #else @@ -57,7 +57,7 @@ FileInputStream::~FileInputStream() //////////////////////////////////////////////////////////// bool FileInputStream::open(const std::string& filename) { -#ifdef ANDROID +#ifdef SFML_SYSTEM_ANDROID if (m_file) delete m_file; m_file = new priv::ResourceStream(filename); @@ -76,7 +76,7 @@ bool FileInputStream::open(const std::string& filename) //////////////////////////////////////////////////////////// Int64 FileInputStream::read(void* data, Int64 size) { -#ifdef ANDROID +#ifdef SFML_SYSTEM_ANDROID return m_file->read(data, size); #else if (m_file) @@ -90,7 +90,7 @@ Int64 FileInputStream::read(void* data, Int64 size) //////////////////////////////////////////////////////////// Int64 FileInputStream::seek(Int64 position) { -#ifdef ANDROID +#ifdef SFML_SYSTEM_ANDROID return m_file->seek(position); #else if (m_file) @@ -111,7 +111,7 @@ Int64 FileInputStream::seek(Int64 position) //////////////////////////////////////////////////////////// Int64 FileInputStream::tell() { -#ifdef ANDROID +#ifdef SFML_SYSTEM_ANDROID return m_file->tell(); #else if (m_file) @@ -125,7 +125,7 @@ Int64 FileInputStream::tell() //////////////////////////////////////////////////////////// Int64 FileInputStream::getSize() { -#ifdef ANDROID +#ifdef SFML_SYSTEM_ANDROID return m_file->getSize(); #else if (m_file) diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index 916c1dc..98ea439 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -229,12 +229,24 @@ sfml_add_library(sfml-window SOURCES ${SRC} ${PLATFORM_SRC}) target_link_libraries(sfml-window PUBLIC sfml-system) +# When static linking on macOS, we need to add this flag for objective C to work +if ((NOT BUILD_SHARED_LIBS) AND SFML_OS_MACOSX) + target_link_libraries(sfml-window PRIVATE -ObjC) +endif() + # find and setup usage for external libraries if(SFML_OS_LINUX OR SFML_OS_FREEBSD OR SFML_OPENBSD) sfml_find_package(X11 INCLUDE "X11_INCLUDE_DIR" LINK "X11_X11_LIB" "X11_Xrandr_LIB") target_link_libraries(sfml-window PRIVATE X11) endif() +# CMake 3.11 and later prefer to choose GLVND, but we choose legacy OpenGL for backward compability +# (unless the OpenGL_GL_PREFERENCE was explicitly set) +# See CMP0072 for more details (cmake --help-policy CMP0072) +if ((NOT ${CMAKE_VERSION} VERSION_LESS 3.11) AND (NOT OpenGL_GL_PREFERENCE)) + set(OpenGL_GL_PREFERENCE "LEGACY") +endif() + if(SFML_OPENGL_ES) if(SFML_OS_IOS) target_link_libraries(sfml-window PRIVATE "-framework OpenGLES") @@ -242,7 +254,7 @@ if(SFML_OPENGL_ES) target_link_libraries(sfml-window PRIVATE EGL GLESv1_CM) endif() else() - sfml_find_package(OpenGL INCLUDE "OPENGL_INCLUDE_DIR" LINK "OPENGL_gl_LIBRARY") + sfml_find_package(OpenGL INCLUDE "OPENGL_INCLUDE_DIR" LINK "OPENGL_LIBRARIES") target_link_libraries(sfml-window PRIVATE OpenGL) endif() diff --git a/src/SFML/Window/OSX/CursorImpl.mm b/src/SFML/Window/OSX/CursorImpl.mm index 5f41376..ed905ef 100644 --- a/src/SFML/Window/OSX/CursorImpl.mm +++ b/src/SFML/Window/OSX/CursorImpl.mm @@ -32,6 +32,21 @@ #import <SFML/Window/OSX/NSImage+raw.h> #import <AppKit/AppKit.h> +namespace +{ + +//////////////////////////////////////////////////////////// +NSCursor* loadFromSelector(SEL selector) +{ + // The caller is responsible for retaining the cursor. + if ([NSCursor respondsToSelector:selector]) + return [NSCursor performSelector:selector]; + else + return nil; +} + +} + namespace sf { namespace priv @@ -56,6 +71,8 @@ CursorImpl::~CursorImpl() //////////////////////////////////////////////////////////// bool CursorImpl::loadFromPixels(const Uint8* pixels, Vector2u size, Vector2u hotspot) { + [m_cursor release]; + NSSize nssize = NSMakeSize(size.x, size.y); NSImage* image = [NSImage imageWithRawData:pixels andSize:nssize]; NSPoint nshotspot = NSMakePoint(hotspot.x, hotspot.y); @@ -65,10 +82,11 @@ bool CursorImpl::loadFromPixels(const Uint8* pixels, Vector2u size, Vector2u hot return m_cursor != nil; } - //////////////////////////////////////////////////////////// bool CursorImpl::loadFromSystem(Cursor::Type type) { + [m_cursor release]; + switch (type) { default: return false; @@ -80,14 +98,28 @@ bool CursorImpl::loadFromSystem(Cursor::Type type) case Cursor::SizeVertical: m_cursor = [NSCursor resizeUpDownCursor]; break; case Cursor::Cross: m_cursor = [NSCursor crosshairCursor]; break; case Cursor::NotAllowed: m_cursor = [NSCursor operationNotAllowedCursor]; break; + + // These cursor types are undocumented, may not be available on some platforms +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + case Cursor::SizeBottomLeftTopRight: + m_cursor = loadFromSelector(@selector(_windowResizeNorthEastSouthWestCursor)); + break; + + case Cursor::SizeTopLeftBottomRight: + m_cursor = loadFromSelector(@selector(_windowResizeNorthWestSouthEastCursor)); + break; + + case Cursor::Help: + m_cursor = loadFromSelector(@selector(_helpCursor)); + break; +#pragma clang diagnostic pop } - // Since we didn't allocate the cursor ourself, we have to retain it - // in order to not break the retain count. - [m_cursor retain]; + if (m_cursor) + [m_cursor retain]; - // For all non-default cases, it was a success. - return true; + return m_cursor != nil; } diff --git a/src/SFML/Window/Unix/ClipboardImpl.cpp b/src/SFML/Window/Unix/ClipboardImpl.cpp index 32ef21b..df2fd95 100644 --- a/src/SFML/Window/Unix/ClipboardImpl.cpp +++ b/src/SFML/Window/Unix/ClipboardImpl.cpp @@ -27,233 +27,356 @@ //////////////////////////////////////////////////////////// #include <SFML/Window/Unix/ClipboardImpl.hpp> #include <SFML/Window/Unix/Display.hpp> -#include <SFML/System/String.hpp> -#include <SFML/System/Sleep.hpp> -#include <iostream> -#include <string> -#include <X11/Xlib.h> +#include <SFML/System/Clock.hpp> +#include <SFML/System/Err.hpp> +#include <X11/Xatom.h> +#include <vector> namespace { + // 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); + } +} + +namespace sf +{ +namespace priv +{ + //////////////////////////////////////////////////////////// -void initClipboard(); -void* hostSelection(void*); +String ClipboardImpl::getString() +{ + return getInstance().getStringImpl(); +} -sf::String string; -pthread_mutex_t mutex; -pthread_t host_thread; +//////////////////////////////////////////////////////////// +void ClipboardImpl::setString(const String& text) +{ + getInstance().setStringImpl(text); +} -bool is_fail = false; -bool is_init = false; -bool is_host = false; -Display* display = NULL; -Window window = 0; +//////////////////////////////////////////////////////////// +void ClipboardImpl::processEvents() +{ + getInstance().processEventsImpl(); +} -Atom selection = 0; -Atom atom_targ = 0; -Atom atom_text = 0; -Atom utf8_text = 0; -int xa_string = 31; -int xa_atom = 4; //////////////////////////////////////////////////////////// -void initClipboard() +ClipboardImpl::ClipboardImpl() : +m_window (0), +m_requestResponded(false) { - is_init = true; + // Open a connection with the X server + m_display = OpenDisplay(); - display = XOpenDisplay(NULL); - int screen = DefaultScreen(display); - window = XCreateSimpleWindow(display, RootWindow(display, screen), - 0, 0, 1, 1, 0, BlackPixel(display, screen), WhitePixel(display, screen)); + // Get the atoms we need to make use of the clipboard + m_clipboard = getAtom("CLIPBOARD", false); + m_targets = getAtom("TARGETS", false); + m_text = getAtom("TEXT", false); + m_utf8String = getAtom("UTF8_STRING", true ); + m_targetProperty = getAtom("SFML_CLIPBOARD_TARGET_PROPERTY", false); - selection = XInternAtom(display, "CLIPBOARD", false); - atom_targ = XInternAtom(display, "TARGETS", false); - atom_text = XInternAtom(display, "TEXT", false); - utf8_text = XInternAtom(display, "UTF8_STRING", true); + // Create a hidden window that will broker our clipboard interactions with X + m_window = XCreateSimpleWindow(m_display, DefaultRootWindow(m_display), 0, 0, 1, 1, 0, 0, 0); - if(utf8_text == None) - { - std::cerr << "UTF-8 format unavailable on clipboard." << std::endl; - utf8_text = xa_string; - } + // Register the events we are interested in + XSelectInput(m_display, m_window, SelectionNotify | SelectionClear | SelectionRequest); +} - if(pthread_mutex_init(&mutex, NULL)) - { - is_fail = true; - std::cerr << "Unable to initialize mutex. Failed to initialize clipboard." << std::endl; - return; - } - if(pthread_create(&host_thread, NULL, hostSelection, NULL)) +//////////////////////////////////////////////////////////// +ClipboardImpl::~ClipboardImpl() +{ + // Destroy the window + if (m_window) { - is_fail = true; - std::cerr << "Unable to create host thread. Failed to initialize clipboard." << std::endl; - return; + XDestroyWindow(m_display, m_window); + XFlush(m_display); } + + // Close the connection with the X server + CloseDisplay(m_display); } + //////////////////////////////////////////////////////////// -void* hostSelection(void*) +ClipboardImpl& ClipboardImpl::getInstance() { - while(true) - { - if(XPending(display) && is_host) - { - XEvent event; + static ClipboardImpl instance; - pthread_mutex_lock(&mutex); - XNextEvent(display, &event); - pthread_mutex_unlock(&mutex); + return instance; +} - switch(event.type) - { - case SelectionClear: - { - pthread_mutex_lock(&mutex); - is_host = false; - pthread_mutex_unlock(&mutex); - break; - } - case SelectionRequest: - { - if(event.xselectionrequest.selection == selection) - { - XSelectionRequestEvent* sel_req_event = &event.xselectionrequest; - XSelectionEvent sel_event = {0}; - - int result = 0; - sel_event.type = SelectionNotify, - sel_event.display = sel_req_event->display, - sel_event.requestor = sel_req_event->requestor, - sel_event.selection = sel_req_event->selection, - sel_event.time = sel_req_event->time, - sel_event.target = sel_req_event->target, - sel_event.property = sel_req_event->property; - - std::basic_string<unsigned char> str = string.toUtf8(); - - if(sel_event.target == atom_targ) - result = XChangeProperty(sel_event.display, sel_event.requestor, - sel_event.property, xa_atom, 32, PropModeReplace, - reinterpret_cast<unsigned char*>(&utf8_text), 1); - else if(sel_event.target == xa_string || sel_event.target == atom_text) - result = XChangeProperty(sel_event.display, sel_event.requestor, - sel_event.property, xa_string, 8, PropModeReplace, - reinterpret_cast<unsigned char*>(&str[0]), str.size()); - else if(sel_event.target == utf8_text) - result = XChangeProperty(sel_event.display, sel_event.requestor, - sel_event.property, utf8_text, 8, PropModeReplace, - reinterpret_cast<unsigned char*>(&str[0]), str.size()); - else - sel_event.property = None; - - if((result & 2) == 0) - XSendEvent(display, sel_event.requestor, 0, 0, - reinterpret_cast<XEvent*>(&sel_event)); - } - break; - } - default: break; - } - } - else - sf::sleep(sf::milliseconds(20)); +//////////////////////////////////////////////////////////// +String ClipboardImpl::getStringImpl() +{ + // Check if anybody owns the current selection + if (XGetSelectionOwner(m_display, m_clipboard) == None) + { + m_clipboardContents.clear(); + + return m_clipboardContents; } -} + + // Process any already pending events + processEvents(); + + m_requestResponded = false; + + // Request the current selection to be converted to UTF-8 (or STRING + // if UTF-8 is not available) and written to our window property + XConvertSelection( + m_display, + m_clipboard, + (m_utf8String != None) ? m_utf8String : XA_STRING, + m_targetProperty, + m_window, + CurrentTime + ); + + Clock clock; + + // Wait for a response for up to 1000ms + while (!m_requestResponded && (clock.getElapsedTime().asMilliseconds() < 1000)) + processEvents(); + + // If no response was received within the time period, clear our clipboard contents + if (!m_requestResponded) + m_clipboardContents.clear(); + + return m_clipboardContents; } -namespace sf -{ -namespace priv -{ //////////////////////////////////////////////////////////// -String ClipboardImpl::getString() +void ClipboardImpl::setStringImpl(const String& text) { - if(!is_init) - initClipboard(); + m_clipboardContents = text; + + // Set our window as the current owner of the selection + XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime); - if(is_fail || is_host) - return string; + // Check if setting the selection owner was successful + if (XGetSelectionOwner(m_display, m_clipboard) != m_window) + err() << "Cannot set clipboard string: Unable to get ownership of X selection" << std::endl; +} - // Dangerous! Wipes all previous events! - XSync(display, true); - XConvertSelection(display, selection, utf8_text, atom_text, window, CurrentTime); +//////////////////////////////////////////////////////////// +void ClipboardImpl::processEventsImpl() +{ XEvent event; - pthread_mutex_lock(&mutex); - XNextEvent(display, &event); - pthread_mutex_unlock(&mutex); + // Pick out the events that are interesting for this window + while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast<XPointer>(m_window))) + m_events.push_back(event); - if(event.type == SelectionNotify) + // Handle the events for this window that we just picked out + while (!m_events.empty()) { - if(event.xselection.selection != selection || event.xselection.target != utf8_text) + event = m_events.front(); + m_events.pop_front(); + processEvent(event); + } +} + + +//////////////////////////////////////////////////////////// +void ClipboardImpl::processEvent(XEvent& windowEvent) +{ + switch (windowEvent.type) + { + case SelectionClear: { - std::cerr << "Failed to convert selection." << std::endl; - return string; + // We don't have any resources we need to clean up + // when losing selection ownership so we don't do + // anything when we receive SelectionClear + // We will still respond to any future SelectionRequest + // events since doing so doesn't really do any harm + break; } - - if(event.xselection.property) + case SelectionNotify: { - Atom target; - int format; - unsigned long size; - unsigned long byte_left; - unsigned char* data; + // Notification that the current selection owner + // has responded to our request + + XSelectionEvent& selectionEvent = *reinterpret_cast<XSelectionEvent*>(&windowEvent.xselection); - XGetWindowProperty(event.xselection.display, - event.xselection.requestor, event.xselection.property, - 0L, (~0L), false, AnyPropertyType, - &target, &format, &size, &byte_left, &data); + m_clipboardContents.clear(); - if(target == utf8_text) + // If retrieving the selection fails or conversion is unsuccessful + // we leave the contents of the clipboard empty since we don't + // own it and we don't know what it could currently be + if ((selectionEvent.property == None) || (selectionEvent.selection != m_clipboard)) + break; + + Atom type; + int format; + unsigned long items; + unsigned long remainingBytes; + unsigned char* data = 0; + + // The selection owner should have wrote the selection + // data to the specified window property + int result = XGetWindowProperty( + m_display, + m_window, + m_targetProperty, + 0, + 0x7fffffff, + False, + AnyPropertyType, + &type, + &format, + &items, + &remainingBytes, + &data + ); + + if (result == Success) { - std::basic_string<unsigned char> str(data, size); - string = sf::String::fromUtf8(str.begin(), str.end()); + // We don't support INCR for now + // It is very unlikely that this will be returned + // for purely text data transfer anyway + if (type != getAtom("INCR", false)) + { + // Only copy the data if the format is what we expect + if ((type == m_utf8String) && (format == 8)) + { + m_clipboardContents = String::fromUtf8(data, data + items); + } + else if ((type == XA_STRING) && (format == 8)) + { + // Convert from ANSI std::string to sf::String + m_clipboardContents = std::string(data, data + items); + } + } XFree(data); + + // The selection requestor must always delete the property themselves + XDeleteProperty(m_display, m_window, m_targetProperty); } - XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); + m_requestResponded = true; + + break; } - } + case SelectionRequest: + { + // Respond to a request for our clipboard contents + XSelectionRequestEvent& selectionRequestEvent = *reinterpret_cast<XSelectionRequestEvent*>(&windowEvent.xselectionrequest); - return string; -} + // Our reply + XSelectionEvent selectionEvent; + selectionEvent.type = SelectionNotify; + selectionEvent.requestor = selectionRequestEvent.requestor; + selectionEvent.selection = selectionRequestEvent.selection; + selectionEvent.property = selectionRequestEvent.property; + selectionEvent.time = selectionRequestEvent.time; -//////////////////////////////////////////////////////////// -void ClipboardImpl::setString(const String& text) -{ - if(!is_init) - initClipboard(); + if (selectionRequestEvent.selection == m_clipboard) + { + if (selectionRequestEvent.target == m_targets) + { + // Respond to a request for our valid conversion targets + std::vector<Atom> targets; - if(is_fail) - return; + targets.push_back(m_targets); + targets.push_back(m_text); + targets.push_back(XA_STRING); - if(!is_host) - { - XSetSelectionOwner(display, selection, window, CurrentTime); + if (m_utf8String != None) + targets.push_back(m_utf8String); - if(XGetSelectionOwner(display, selection) != window) - { - std::cerr << "Unable to get ownership of selection." << std::endl; - return; - } + XChangeProperty( + m_display, + selectionRequestEvent.requestor, + selectionRequestEvent.property, + XA_ATOM, + 32, + PropModeReplace, + reinterpret_cast<unsigned char*>(&targets[0]), + targets.size() + ); - pthread_mutex_lock(&mutex); - is_host = true; - pthread_mutex_unlock(&mutex); - } + // Notify the requestor that they can read the targets from their window property + selectionEvent.target = m_targets; + + XSendEvent(m_display, selectionRequestEvent.requestor, True, NoEventMask, reinterpret_cast<XEvent*>(&selectionEvent)); + + break; + } + else if ((selectionRequestEvent.target == XA_STRING) || ((m_utf8String == None) && (selectionRequestEvent.target == m_text))) + { + // Respond to a request for conversion to a Latin-1 string + std::string data = m_clipboardContents.toAnsiString(); + + XChangeProperty( + m_display, + selectionRequestEvent.requestor, + selectionRequestEvent.property, + XA_STRING, + 8, + PropModeReplace, + reinterpret_cast<const unsigned char*>(data.c_str()), + data.size() + ); - pthread_mutex_lock(&mutex); - string = text; - pthread_mutex_unlock(&mutex); + // Notify the requestor that they can read the data from their window property + selectionEvent.target = XA_STRING; + + XSendEvent(m_display, selectionRequestEvent.requestor, True, NoEventMask, reinterpret_cast<XEvent*>(&selectionEvent)); + + break; + } + else if ((m_utf8String != None) && ((selectionRequestEvent.target == m_utf8String) || (selectionRequestEvent.target == m_text))) + { + // Respond to a request for conversion to a UTF-8 string + // or an encoding of our choosing (we always choose UTF-8) + std::basic_string<Uint8> data = m_clipboardContents.toUtf8(); + + XChangeProperty( + m_display, + selectionRequestEvent.requestor, + selectionRequestEvent.property, + m_utf8String, + 8, + PropModeReplace, + reinterpret_cast<const unsigned char*>(data.c_str()), + data.size() + ); + + // Notify the requestor that they can read the data from their window property + selectionEvent.target = m_utf8String; + + XSendEvent(m_display, selectionRequestEvent.requestor, True, NoEventMask, reinterpret_cast<XEvent*>(&selectionEvent)); + + break; + } + } + + // Notify the requestor that we could not respond to their request + selectionEvent.target = selectionRequestEvent.target; + selectionEvent.property = None; + + XSendEvent(m_display, selectionRequestEvent.requestor, True, NoEventMask, reinterpret_cast<XEvent*>(&selectionEvent)); + + break; + } + default: + break; + } } } // namespace priv diff --git a/src/SFML/Window/Unix/ClipboardImpl.hpp b/src/SFML/Window/Unix/ClipboardImpl.hpp index f4aca98..48ec787 100644 --- a/src/SFML/Window/Unix/ClipboardImpl.hpp +++ b/src/SFML/Window/Unix/ClipboardImpl.hpp @@ -29,6 +29,8 @@ // Headers //////////////////////////////////////////////////////////// #include <SFML/System/String.hpp> +#include <X11/Xlib.h> +#include <deque> namespace sf @@ -67,6 +69,82 @@ public: /// //////////////////////////////////////////////////////////// static void setString(const String& text); + + //////////////////////////////////////////////////////////// + /// \brief Process pending events for the hidden clipboard window + /// + /// This function has to be called as part of normal window + /// event processing in order for our application to respond + /// to selection requests from other applications. + /// + //////////////////////////////////////////////////////////// + static void processEvents(); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Constructor + /// + //////////////////////////////////////////////////////////// + ClipboardImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~ClipboardImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Get singleton instance + /// + /// \return Singleton instance + /// + //////////////////////////////////////////////////////////// + static ClipboardImpl& getInstance(); + + //////////////////////////////////////////////////////////// + /// \brief getString implementation + /// + /// \return Current content of the clipboard + /// + //////////////////////////////////////////////////////////// + String getStringImpl(); + + //////////////////////////////////////////////////////////// + /// \brief setString implementation + /// + /// \param text sf::String object containing the data to be sent to the clipboard + /// + //////////////////////////////////////////////////////////// + void setStringImpl(const String& text); + + //////////////////////////////////////////////////////////// + /// \brief processEvents implementation + /// + //////////////////////////////////////////////////////////// + void processEventsImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Process an incoming event from the window + /// + /// \param windowEvent Event which has been received + /// + //////////////////////////////////////////////////////////// + void processEvent(XEvent& windowEvent); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + ::Window m_window; ///< X identifier defining our window + ::Display* m_display; ///< Pointer to the display + Atom m_clipboard; ///< X Atom identifying the CLIPBOARD selection + Atom m_targets; ///< X Atom identifying TARGETS + Atom m_text; ///< X Atom identifying TEXT + Atom m_utf8String; ///< X Atom identifying UTF8_STRING + Atom m_targetProperty; ///< X Atom identifying our destination window property + String m_clipboardContents; ///< Our clipboard contents + std::deque<XEvent> m_events; ///< Queue we use to store pending events for this window + bool m_requestResponded; ///< Holds whether our selection request has been responded to or not }; } // namespace priv diff --git a/src/SFML/Window/Unix/GlxExtensions.hpp b/src/SFML/Window/Unix/GlxExtensions.hpp index abbb132..fb2c7da 100644 --- a/src/SFML/Window/Unix/GlxExtensions.hpp +++ b/src/SFML/Window/Unix/GlxExtensions.hpp @@ -25,11 +25,12 @@ #ifndef SF_POINTER_C_GENERATED_HEADER_GLXWIN_HPP #define SF_POINTER_C_GENERATED_HEADER_GLXWIN_HPP -#ifdef __glxext_h_ +#if defined(__glxext_h_) || defined(__glx_glxext_h_) #error Attempt to include glx_exts after including glxext.h #endif #define __glxext_h_ +#define __glx_glxext_h_ #include <X11/Xlib.h> #include <X11/Xutil.h> diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index e2c8d29..da69750 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -26,6 +26,7 @@ // Headers //////////////////////////////////////////////////////////// #include <SFML/Window/Unix/WindowImplX11.hpp> +#include <SFML/Window/Unix/ClipboardImpl.hpp> #include <SFML/Window/Unix/Display.hpp> #include <SFML/Window/Unix/InputImpl.hpp> #include <SFML/System/Utf.hpp> @@ -173,7 +174,7 @@ namespace if (result != Success || actualType != XA_WINDOW || numItems != 1) { - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -205,7 +206,7 @@ namespace if (result != Success || actualType != XA_WINDOW || numItems != 1) { - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -269,7 +270,7 @@ namespace windowManagerName = sf::String::fromUtf8(begin, end); } - if(result == Success) + if (result == Success) XFree(data); sf::priv::CloseDisplay(display); @@ -486,6 +487,7 @@ m_inputMethod (NULL), m_inputContext (NULL), m_isExternal (true), m_oldVideoMode (0), +m_oldRRCrtc (0), m_hiddenCursor (0), m_lastCursor (None), m_keyRepeat (true), @@ -534,6 +536,7 @@ m_inputMethod (NULL), m_inputContext (NULL), m_isExternal (false), m_oldVideoMode (0), +m_oldRRCrtc (0), m_hiddenCursor (0), m_lastCursor (None), m_keyRepeat (true), @@ -555,8 +558,17 @@ m_lastInputTime (0) m_screen = DefaultScreen(m_display); // Compute position and size - int left = m_fullscreen ? 0 : (DisplayWidth(m_display, m_screen) - mode.width) / 2; - int top = m_fullscreen ? 0 : (DisplayHeight(m_display, m_screen) - mode.height) / 2; + Vector2i windowPosition; + if(m_fullscreen) + { + windowPosition = getPrimaryMonitorPosition(); + } + else + { + windowPosition.x = (DisplayWidth(m_display, m_screen) - mode.width) / 2; + windowPosition.y = (DisplayWidth(m_display, m_screen) - mode.height) / 2; + } + int width = mode.width; int height = mode.height; @@ -571,7 +583,7 @@ m_lastInputTime (0) m_window = XCreateWindow(m_display, DefaultRootWindow(m_display), - left, top, + windowPosition.x, windowPosition.y, width, height, 0, visualInfo.depth, @@ -668,9 +680,11 @@ m_lastInputTime (0) { m_useSizeHints = true; XSizeHints* sizeHints = XAllocSizeHints(); - sizeHints->flags = PMinSize | PMaxSize; + sizeHints->flags = PMinSize | PMaxSize | USPosition; sizeHints->min_width = sizeHints->max_width = width; sizeHints->min_height = sizeHints->max_height = height; + sizeHints->x = windowPosition.x; + sizeHints->y = windowPosition.y; XSetWMNormalHints(m_display, m_window, sizeHints); XFree(sizeHints); } @@ -707,7 +721,15 @@ m_lastInputTime (0) // Set fullscreen video mode and switch to fullscreen if necessary if (m_fullscreen) { - setPosition(Vector2i(0, 0)); + // Disable hint for min and max size, + // otherwise some windows managers will not remove window decorations + XSizeHints *sizeHints = XAllocSizeHints(); + long flags = 0; + XGetWMNormalHints(m_display, m_window, sizeHints, &flags); + sizeHints->flags &= ~(PMinSize | PMaxSize); + XSetWMNormalHints(m_display, m_window, sizeHints); + XFree(sizeHints); + setVideoMode(mode); switchToFullscreen(); } @@ -721,11 +743,11 @@ WindowImplX11::~WindowImplX11() cleanup(); // Destroy icon pixmap - if(m_iconPixmap) + if (m_iconPixmap) XFreePixmap(m_display, m_iconPixmap); // Destroy icon mask pixmap - if(m_iconMaskPixmap) + if (m_iconMaskPixmap) XFreePixmap(m_display, m_iconMaskPixmap); // Destroy the cursor @@ -779,6 +801,9 @@ void WindowImplX11::processEvents() m_events.pop_front(); processEvent(event); } + + // Process clipboard window events + priv::ClipboardImpl::processEvents(); } @@ -956,10 +981,10 @@ void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8 return; } - if(m_iconPixmap) + if (m_iconPixmap) XFreePixmap(m_display, m_iconPixmap); - if(m_iconMaskPixmap) + if (m_iconMaskPixmap) XFreePixmap(m_display, m_iconMaskPixmap); m_iconPixmap = XCreatePixmap(m_display, RootWindow(m_display, m_screen), width, height, defDepth); @@ -1236,53 +1261,99 @@ void WindowImplX11::setVideoMode(const VideoMode& mode) return; // Check if the XRandR extension is present - int version; - if (!XQueryExtension(m_display, "RANDR", &version, &version, &version)) + int xRandRMajor, xRandRMinor; + if (!checkXRandR(xRandRMajor, xRandRMinor)) { // XRandR extension is not supported: we cannot use fullscreen mode err() << "Fullscreen is not supported, switching to window mode" << std::endl; return; } - // Get the current configuration - XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen)); + // Get root window + ::Window rootWindow = RootWindow(m_display, m_screen); - if (!config) + // Get the screen resources + XRRScreenResources* res = XRRGetScreenResources(m_display, rootWindow); + if (!res) { - // Failed to get the screen configuration - err() << "Failed to get the current screen configuration for fullscreen mode, switching to window mode" << std::endl; + err() << "Failed to get the current screen resources for fullscreen mode, switching to window mode" << std::endl; return; } - // Save the current video mode before we switch to fullscreen - Rotation currentRotation; - m_oldVideoMode = XRRConfigCurrentConfiguration(config, ¤tRotation); + RROutput output = getOutputPrimary(rootWindow, res, xRandRMajor, xRandRMinor); + + // Get output info from output + XRROutputInfo* outputInfo = XRRGetOutputInfo(m_display, res, output); + if (!outputInfo || outputInfo->connection == RR_Disconnected) + { + XRRFreeScreenResources(res); - // Get the available screen sizes - int nbSizes; - XRRScreenSize* sizes = XRRConfigSizes(config, &nbSizes); + // If outputInfo->connection == RR_Disconnected, free output info + if (outputInfo) + XRRFreeOutputInfo(outputInfo); - // Search for a matching size - for (int i = 0; (sizes && i < nbSizes); ++i) + err() << "Failed to get output info for fullscreen mode, switching to window mode" << std::endl; + return; + } + + // Retreive current RRMode, screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, outputInfo->crtc); + if (!crtcInfo) { - XRRConfigRotations(config, ¤tRotation); + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to get crtc info for fullscreen mode, switching to window mode" << std::endl; + return; + } - if (currentRotation == RR_Rotate_90 || currentRotation == RR_Rotate_270) - std::swap(sizes[i].height, sizes[i].width); + // Find RRMode to set + bool modeFound = false; + RRMode xRandMode; - 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); + for (int i = 0; (i < res->nmode) && !modeFound; i++) + { + if (crtcInfo->rotation == RR_Rotate_90 || crtcInfo->rotation == RR_Rotate_270) + std::swap(res->modes[i].height, res->modes[i].width); - // Set "this" as the current fullscreen window - fullscreenWindow = this; - break; + // Check if screen size match + if (res->modes[i].width == static_cast<int>(mode.width) && + res->modes[i].height == static_cast<int>(mode.height)) + { + xRandMode = res->modes[i].id; + modeFound = true; } } - // Free the configuration instance - XRRFreeScreenConfigInfo(config); + if (!modeFound) + { + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to find a matching RRMode for fullscreen mode, switching to window mode" << std::endl; + return; + } + + // Save the current video mode before we switch to fullscreen + m_oldVideoMode = crtcInfo->mode; + m_oldRRCrtc = outputInfo->crtc; + + // Switch to fullscreen mode + XRRSetCrtcConfig(m_display, + res, + outputInfo->crtc, + CurrentTime, + crtcInfo->x, + crtcInfo->y, + xRandMode, + crtcInfo->rotation, + &output, + 1); + + // Set "this" as the current fullscreen window + fullscreenWindow = this; + + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + XRRFreeCrtcInfo(crtcInfo); } @@ -1291,19 +1362,55 @@ void WindowImplX11::resetVideoMode() { if (fullscreenWindow == this) { - // Get current screen info - XRRScreenConfiguration* config = XRRGetScreenInfo(m_display, RootWindow(m_display, m_screen)); - if (config) + // Try to set old configuration + // Check if the XRandR extension + int xRandRMajor, xRandRMinor; + if (checkXRandR(xRandRMajor, xRandRMinor)) { - // Get the current rotation - Rotation currentRotation; - XRRConfigCurrentConfiguration(config, ¤tRotation); + XRRScreenResources* res = XRRGetScreenResources(m_display, DefaultRootWindow(m_display)); + if (!res) + { + err() << "Failed to get the current screen resources to reset the video mode" << std::endl; + return; + } + + // Retreive current screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, m_oldRRCrtc); + if (!crtcInfo) + { + XRRFreeScreenResources(res); + err() << "Failed to get crtc info to reset the video mode" << std::endl; + return; + } + + RROutput output; + + // if version >= 1.3 get the primary screen else take the first screen + if ((xRandRMajor == 1 && xRandRMinor >= 3) || xRandRMajor > 1) + { + output = XRRGetOutputPrimary(m_display, DefaultRootWindow(m_display)); - // Reset the video mode - XRRSetScreenConfig(m_display, config, RootWindow(m_display, m_screen), m_oldVideoMode, currentRotation, CurrentTime); + // Check if returned output is valid, otherwise use the first screen + if (output == None) + output = res->outputs[0]; + } + else{ + output = res->outputs[0]; + } - // Free the configuration instance - XRRFreeScreenConfigInfo(config); + XRRSetCrtcConfig(m_display, + res, + m_oldRRCrtc, + CurrentTime, + crtcInfo->x, + crtcInfo->y, + m_oldVideoMode, + crtcInfo->rotation, + &output, + 1); + + XRRFreeCrtcInfo(crtcInfo); + XRRFreeScreenResources(res); } // Reset the fullscreen window @@ -1499,7 +1606,7 @@ void WindowImplX11::updateLastInputTime(::Time time) { Atom netWmUserTime = getAtom("_NET_WM_USER_TIME", true); - if(netWmUserTime) + if (netWmUserTime) { XChangeProperty(m_display, m_window, @@ -1969,6 +2076,106 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) return true; } + +//////////////////////////////////////////////////////////// +bool WindowImplX11::checkXRandR(int& xRandRMajor, int& xRandRMinor) +{ + // Check if the XRandR extension is present + int version; + if (!XQueryExtension(m_display, "RANDR", &version, &version, &version)) + { + err() << "XRandR extension is not supported" << std::endl; + return false; + } + + // Check XRandR version, 1.2 required + if (!XRRQueryVersion(m_display, &xRandRMajor, &xRandRMinor) || xRandRMajor < 1 || (xRandRMajor == 1 && xRandRMinor < 2 )) + { + err() << "XRandR is too old" << std::endl; + return false; + } + + return true; +} + + +//////////////////////////////////////////////////////////// +RROutput WindowImplX11::getOutputPrimary(::Window& rootWindow, XRRScreenResources* res, int xRandRMajor, int xRandRMinor) +{ + // if xRandR version >= 1.3 get the primary screen else take the first screen + if ((xRandRMajor == 1 && xRandRMinor >= 3) || xRandRMajor > 1) + { + RROutput output = XRRGetOutputPrimary(m_display, rootWindow); + + // Check if returned output is valid, otherwise use the first screen + if (output == None) + return res->outputs[0]; + else + return output; + } + + // xRandr version can't get the primary screen, use the first screen + return res->outputs[0]; +} + + +//////////////////////////////////////////////////////////// +Vector2i WindowImplX11::getPrimaryMonitorPosition() +{ + Vector2i monitorPosition; + + // Get root window + ::Window rootWindow = RootWindow(m_display, m_screen); + + // Get the screen resources + XRRScreenResources* res = XRRGetScreenResources(m_display, rootWindow); + if (!res) + { + err() << "Failed to get the current screen resources for.primary monitor position" << std::endl; + return monitorPosition; + } + + // Get xRandr version + int xRandRMajor, xRandRMinor; + if (!checkXRandR(xRandRMajor, xRandRMinor)) + xRandRMajor = xRandRMinor = 0; + + RROutput output = getOutputPrimary(rootWindow, res, xRandRMajor, xRandRMinor); + + // Get output info from output + XRROutputInfo* outputInfo = XRRGetOutputInfo(m_display, res, output); + if (!outputInfo || outputInfo->connection == RR_Disconnected) + { + XRRFreeScreenResources(res); + + // If outputInfo->connection == RR_Disconnected, free output info + if (outputInfo) + XRRFreeOutputInfo(outputInfo); + + err() << "Failed to get output info for.primary monitor position" << std::endl; + return monitorPosition; + } + + // Retreive current RRMode, screen position and rotation + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, outputInfo->crtc); + if (!crtcInfo) + { + XRRFreeScreenResources(res); + XRRFreeOutputInfo(outputInfo); + err() << "Failed to get crtc info for.primary monitor position" << std::endl; + return monitorPosition; + } + + monitorPosition.x = crtcInfo->x; + monitorPosition.y = crtcInfo->y; + + XRRFreeCrtcInfo(crtcInfo); + XRRFreeOutputInfo(outputInfo); + XRRFreeScreenResources(res); + + return monitorPosition; +} + } // namespace priv } // namespace sf diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 0ff694b..a025a1a 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -34,6 +34,7 @@ #include <SFML/Window/WindowStyle.hpp> // Prevent conflict with macro None from Xlib #include <X11/Xlib.h> #include <deque> +#include <X11/extensions/Xrandr.h> namespace sf @@ -265,6 +266,38 @@ private: bool processEvent(XEvent& windowEvent); //////////////////////////////////////////////////////////// + /// \brief Check if a valid version of XRandR extension is present + /// + /// \param xRandRMajor XRandR major version + /// \param xRandRMinor XRandR minor version + /// + /// \return True if a valid XRandR version found, false otherwise + /// + //////////////////////////////////////////////////////////// + bool checkXRandR(int& xRandRMajor, int& xRandRMinor); + + //////////////////////////////////////////////////////////// + /// \brief Get the RROutput of the primary monitor + /// + /// \param rootWindow the root window + /// \param res screen resources + /// \param xRandRMajor XRandR major version + /// \param xRandRMinor XRandR minor version + /// + /// \return RROutput of the primary monitor + /// + //////////////////////////////////////////////////////////// + RROutput getOutputPrimary(::Window& rootWindow, XRRScreenResources* res, int xRandRMajor, int xRandRMinor); + + //////////////////////////////////////////////////////////// + /// \brief Get coordinates of the primary monitor + /// + /// \return Position of the primary monitor + /// + //////////////////////////////////////////////////////////// + Vector2i getPrimaryMonitorPosition(); + + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// ::Window m_window; ///< X identifier defining our window @@ -275,6 +308,7 @@ private: std::deque<XEvent> m_events; ///< Queue we use to store pending events for this window bool m_isExternal; ///< Tell whether the window has been created externally or by SFML int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen + RRCrtc m_oldRRCrtc; ///< RRCrtc in use before we switch to fullscreen ::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hiding, we must create a transparent one ::Cursor m_lastCursor; ///< Last cursor used -- this data is not owned by the window and is required to be always valid bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? diff --git a/src/SFML/Window/Win32/CursorImpl.cpp b/src/SFML/Window/Win32/CursorImpl.cpp index 28f4274..d1913d6 100755 --- a/src/SFML/Window/Win32/CursorImpl.cpp +++ b/src/SFML/Window/Win32/CursorImpl.cpp @@ -69,7 +69,7 @@ bool CursorImpl::loadFromPixels(const Uint8* pixels, Vector2u size, Vector2u hot bitmapHeader.bV5BlueMask = 0x000000ff; bitmapHeader.bV5AlphaMask = 0xff000000; - Uint8* bitmapData = NULL; + Uint32* bitmapData = NULL; HDC screenDC = GetDC(NULL); HBITMAP color = CreateDIBSection( @@ -89,7 +89,12 @@ bool CursorImpl::loadFromPixels(const Uint8* pixels, Vector2u size, Vector2u hot } // Fill our bitmap with the cursor color data - std::memcpy(bitmapData, pixels, size.x * size.y * 4); + // We'll have to swap the red and blue channels here + Uint32* bitmapOffset = bitmapData; + for (std::size_t remaining = size.x * size.y; remaining > 0; --remaining, pixels += 4) + { + *bitmapOffset++ = (pixels[3] << 24) | (pixels[0] << 16) | (pixels[1] << 8) | pixels[2]; + } // Create a dummy mask bitmap (it won't be used) HBITMAP mask = CreateBitmap(size.x, size.y, 1, 1, NULL); diff --git a/src/SFML/Window/iOS/SFAppDelegate.mm b/src/SFML/Window/iOS/SFAppDelegate.mm index 6b17384..ea69f41 100644 --- a/src/SFML/Window/iOS/SFAppDelegate.mm +++ b/src/SFML/Window/iOS/SFAppDelegate.mm @@ -56,6 +56,10 @@ namespace //////////////////////////////////////////////////////////// + (SFAppDelegate*)getInstance { + NSAssert(delegateInstance, + @"SFAppDelegate instance is nil, this means SFML was not properly initialized. " + "Make sure that the file defining your main() function includes <SFML/Main.hpp>"); + return delegateInstance; } @@ -173,7 +177,7 @@ namespace NSArray *supportedOrientations = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UISupportedInterfaceOrientations"]; if (!supportedOrientations) - return false; + return (1 << orientation) & [rootViewController supportedInterfaceOrientations]; int appFlags = 0; if ([supportedOrientations containsObject:@"UIInterfaceOrientationPortrait"]) diff --git a/src/SFML/Window/iOS/SFView.mm b/src/SFML/Window/iOS/SFView.mm index 063c61a..940d0a8 100644 --- a/src/SFML/Window/iOS/SFView.mm +++ b/src/SFML/Window/iOS/SFView.mm @@ -202,5 +202,11 @@ return self; } +//////////////////////////////////////////////////////////// +- (UITextAutocorrectionType) autocorrectionType +{ + return UITextAutocorrectionTypeNo; +} + @end diff --git a/src/SFML/Window/iOS/SFViewController.hpp b/src/SFML/Window/iOS/SFViewController.hpp index b8a77aa..2b65d35 100644 --- a/src/SFML/Window/iOS/SFViewController.hpp +++ b/src/SFML/Window/iOS/SFViewController.hpp @@ -56,14 +56,6 @@ - (BOOL)shouldAutorotate; //////////////////////////////////////////////////////////// -/// \brief Returns the supported orientations (iOS >= 6) -/// -/// \return A combination of all the supported orientations -/// -//////////////////////////////////////////////////////////// -- (NSUInteger)supportedInterfaceOrientations; - -//////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// @property (nonatomic) bool orientationCanChange; ///< Tells whether the controller's view can rotate or not diff --git a/src/SFML/Window/iOS/SFViewController.mm b/src/SFML/Window/iOS/SFViewController.mm index d8be047..8c23cdc 100644 --- a/src/SFML/Window/iOS/SFViewController.mm +++ b/src/SFML/Window/iOS/SFViewController.mm @@ -46,12 +46,4 @@ return self.orientationCanChange; } - -//////////////////////////////////////////////////////////// -- (NSUInteger)supportedInterfaceOrientations -{ - return UIInterfaceOrientationMaskAll; -} - - @end |