diff --git a/include/SFML/Window/Cursor.hpp b/include/SFML/Window/Cursor.hpp index c85ac6996..0a70e4965 100644 --- a/include/SFML/Window/Cursor.hpp +++ b/include/SFML/Window/Cursor.hpp @@ -57,17 +57,17 @@ public: /// ------------------------------------|:-----:|:--------:|:--------: /// sf::Cursor::Arrow | yes | yes | yes /// sf::Cursor::ArrowWait | no | no | yes - /// sf::Cursor::Wait | no | no | yes - /// sf::Cursor::Text | no | yes | yes - /// sf::Cursor::Hand | no | yes | yes - /// sf::Cursor::SizeHorizontal | no | yes | yes - /// sf::Cursor::SizeVertical | no | yes | yes + /// sf::Cursor::Wait | yes | no | yes + /// sf::Cursor::Text | yes | yes | yes + /// sf::Cursor::Hand | yes | yes | yes + /// sf::Cursor::SizeHorizontal | yes | yes | yes + /// sf::Cursor::SizeVertical | yes | yes | yes /// sf::Cursor::SizeTopLeftBottomRight | no | no | yes /// sf::Cursor::SizeBottomLeftTopRight | no | no | yes - /// sf::Cursor::SizeAll | no | no | yes - /// sf::Cursor::Cross | no | yes | yes - /// sf::Cursor::Help | no | no | yes - /// sf::Cursor::NotAllowed | no | yes | yes + /// sf::Cursor::SizeAll | yes | no | yes + /// sf::Cursor::Cross | yes | yes | yes + /// sf::Cursor::Help | yes | no | yes + /// sf::Cursor::NotAllowed | yes | yes | yes /// //////////////////////////////////////////////////////////// enum Type @@ -126,6 +126,11 @@ public: /// position is. Any mouse actions that are performed will /// return the window/screen location of the hotspot. /// + /// \warning On Unix, the pixels are mapped into a monochrome + /// bitmap: pixels with an alpha channel to 0 are + /// transparent, black if the RGB channel are close + /// to zero, and white otherwise. + /// /// \param pixels Array of pixels of the image /// \param size Width and height of the image /// \param hotspot (x,y) location of the hotspot diff --git a/src/SFML/Window/Unix/CursorImpl.cpp b/src/SFML/Window/Unix/CursorImpl.cpp index 3791feca9..66aca2ec3 100644 --- a/src/SFML/Window/Unix/CursorImpl.cpp +++ b/src/SFML/Window/Unix/CursorImpl.cpp @@ -26,6 +26,12 @@ // Headers //////////////////////////////////////////////////////////// #include +#include +#include +#include +#include +#include +#include namespace sf { @@ -33,30 +39,114 @@ namespace priv { //////////////////////////////////////////////////////////// -CursorImpl::CursorImpl() +CursorImpl::CursorImpl() : +m_display(OpenDisplay()), +m_cursor(None) { - // TODO + // That's it. } //////////////////////////////////////////////////////////// CursorImpl::~CursorImpl() { - // TODO + release(); + + CloseDisplay(m_display); } //////////////////////////////////////////////////////////// bool CursorImpl::loadFromPixels(const Uint8* pixels, Vector2u size, Vector2u hotspot) { - // TODO + release(); + + // Convert the image into a bitmap (monochrome!). + std::size_t bytes = (size.x + 7) / 8 * size.y; + std::vector mask(bytes, 0); // Defines which pixel is transparent. + std::vector data(bytes, 1); // Defines which pixel is white/black. + + for (std::size_t j = 0; j < size.y; ++j) + { + for (std::size_t i = 0; i < size.x; ++i) + { + std::size_t pixelIndex = i + j * size.x; + std::size_t byteIndex = pixelIndex / 8; + std::size_t bitIndex = i % 8; + + // Turn on pixel that are not transparent + Uint8 opacity = pixels[pixelIndex * 4 + 3] > 0 ? 1 : 0; + mask[byteIndex] |= opacity << bitIndex; + + // Choose between black/background & white/foreground color for each pixel, + // based on the pixel color intensity: on average, if a channel is "active" + // at 25%, the bit is white. + int intensity = pixels[pixelIndex * 4 + 0] + pixels[pixelIndex * 4 + 1] + pixels[pixelIndex * 4 + 2]; + Uint8 bit = intensity > 64 ? 1 : 0; + data[byteIndex] |= bit << bitIndex; + } + } + + Pixmap maskPixmap = XCreateBitmapFromData(m_display, XDefaultRootWindow(m_display), + (char*)&mask[0], size.x, size.y); + Pixmap dataPixmap = XCreateBitmapFromData(m_display, XDefaultRootWindow(m_display), + (char*)&data[0], size.x, size.y); + + // Define the foreground color as white and the background as black. + XColor fg, bg; + fg.red = fg.blue = fg.green = -1; + bg.red = bg.blue = bg.green = 0; + + // Create the monochrome cursor. + m_cursor = XCreatePixmapCursor(m_display, + dataPixmap, maskPixmap, + &fg, &bg, + hotspot.x, hotspot.y); + + // Free the resources + XFreePixmap(m_display, dataPixmap); + XFreePixmap(m_display, maskPixmap); + + // We assume everything went fine... + return true; } //////////////////////////////////////////////////////////// bool CursorImpl::loadFromSystem(Cursor::Type type) { - // TODO + release(); + + unsigned int shape; + switch (type) + { + default: return false; + + case Cursor::Arrow: shape = XC_arrow; break; + case Cursor::Wait: shape = XC_watch; break; + case Cursor::Text: shape = XC_xterm; break; + case Cursor::Hand: shape = XC_hand1; break; + case Cursor::SizeHorizontal: shape = XC_sb_h_double_arrow; break; + case Cursor::SizeVertical: shape = XC_sb_v_double_arrow; break; + case Cursor::SizeAll: shape = XC_fleur; break; + case Cursor::Cross: shape = XC_crosshair; break; + case Cursor::Help: shape = XC_question_arrow; break; + case Cursor::NotAllowed: shape = XC_X_cursor; break; + } + + m_cursor = XCreateFontCursor(m_display, shape); + return true; +} + + +//////////////////////////////////////////////////////////// +void CursorImpl::release() +{ + if (m_cursor != None) + { + XFreeCursor(m_display, m_cursor); + m_cursor = None; + } } diff --git a/src/SFML/Window/Unix/CursorImpl.hpp b/src/SFML/Window/Unix/CursorImpl.hpp index 245714efe..a23800946 100644 --- a/src/SFML/Window/Unix/CursorImpl.hpp +++ b/src/SFML/Window/Unix/CursorImpl.hpp @@ -31,7 +31,8 @@ #include #include #include - +#include // Prevent conflict with macro None from Xlib +#include namespace sf { @@ -82,9 +83,17 @@ private: friend class WindowImplX11; + //////////////////////////////////////////////////////////// + /// \brief Release the cursor, if we have loaded one. + /// + //////////////////////////////////////////////////////////// + void release(); + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// + ::Display* m_display; + ::Cursor m_cursor; }; } // namespace priv diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 13d679f7c..5712f04b8 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -25,7 +25,6 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include // important to be included first (conflict with None) #include #include #include @@ -387,6 +386,7 @@ m_inputContext (NULL), m_isExternal (true), m_oldVideoMode (0), m_hiddenCursor (0), +m_lastCursor (None), m_keyRepeat (true), m_previousSize (-1, -1), m_useSizeHints (false), @@ -434,6 +434,7 @@ m_inputContext (NULL), m_isExternal (false), m_oldVideoMode (0), m_hiddenCursor (0), +m_lastCursor (None), m_keyRepeat (true), m_previousSize (-1, -1), m_useSizeHints (false), @@ -895,7 +896,7 @@ void WindowImplX11::setVisible(bool visible) //////////////////////////////////////////////////////////// void WindowImplX11::setMouseCursorVisible(bool visible) { - XDefineCursor(m_display, m_window, visible ? None : m_hiddenCursor); + XDefineCursor(m_display, m_window, visible ? m_lastCursor : m_hiddenCursor); XFlush(m_display); } @@ -903,7 +904,8 @@ void WindowImplX11::setMouseCursorVisible(bool visible) //////////////////////////////////////////////////////////// void WindowImplX11::setMouseCursor(const CursorImpl& cursor) { - // TODO + m_lastCursor = cursor.m_cursor; + XDefineCursor(m_display, m_window, m_lastCursor); } @@ -1313,6 +1315,9 @@ void WindowImplX11::initialize() // Raise the window and grab input focus grabFocus(); + // Create the hidden cursor + createHiddenCursor(); + // Flush the commands queue XFlush(m_display); @@ -1346,6 +1351,26 @@ void WindowImplX11::updateLastInputTime(::Time time) } +//////////////////////////////////////////////////////////// +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() { diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index 631fefcc0..13596f82f 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -31,6 +31,7 @@ #include #include #include +#include // Prevent conflict with macro None from Xlib #include #include @@ -241,6 +242,12 @@ private: //////////////////////////////////////////////////////////// void initialize(); + //////////////////////////////////////////////////////////// + /// \brief Create a transparent mouse cursor + /// + //////////////////////////////////////////////////////////// + void createHiddenCursor(); + //////////////////////////////////////////////////////////// /// \brief Cleanup graphical resources attached to the window /// @@ -267,7 +274,8 @@ private: 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 int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen - ::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one + ::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? 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?