[drm] Implement sf::Cursor functionality for DRM

This commit is contained in:
Andrew Mickelson 2024-07-10 21:32:46 -07:00
parent 15e001608a
commit e2d004986d
6 changed files with 249 additions and 58 deletions

View File

@ -26,15 +26,60 @@
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/DRM/CursorImpl.hpp> #include <SFML/Window/DRM/CursorImpl.hpp>
#include <SFML/Window/DRM/DRMContext.hpp>
#include <SFML/System/Err.hpp>
#include <vector> #include <vector>
#include <cstring>
#include <xf86drm.h>
namespace sf namespace sf
{ {
namespace priv namespace priv
{ {
const char *defaultCursor16x16 =
"\000\000\000\000\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\377\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\377\377\377\377\377\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\377\377\377\377\377\377\377\377\377\000\000\000\377\210\203\203\377\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\000\000\000\377\210\203\203\377\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\000\000\000\377\210"
"\203\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\000\000\000\377\210\203\203\377\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\000\000\000\377\210\203\203\377\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\000\000\000\377\000\000\000\377"
"\000\000\000\377\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\377\377\377\377\377\377\377\377\377\000\000\000\377\377\377\377\377"
"\377\377\377\377\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\377\377\377\377\000\000\000\377\210"
"\203\203\377\000\000\000\377\377\377\377\377\377\377\377\377\000\000\000\377\210\203"
"\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
"\377\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\377\377\377"
"\377\377\377\377\377\377\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\377\000\000\000\377\000\000\000\377\000\000\000\377\210\203\203\377\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000\000";
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
CursorImpl::CursorImpl() CursorImpl::CursorImpl()
: m_bo(NULL)
{ {
} }
@ -42,47 +87,79 @@ CursorImpl::CursorImpl()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
CursorImpl::~CursorImpl() CursorImpl::~CursorImpl()
{ {
release();
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool CursorImpl::loadFromPixels(const Uint8* /*pixels*/, Vector2u /*size*/, Vector2u /*hotspot*/) bool CursorImpl::loadFromPixels(const Uint8* pixels, Vector2u size, Vector2u hotspot)
{ {
release();
Drm &drm = DRMContext::getDRM();
uint64_t capWidth(0), capHeight(0);
drmGetCap(drm.fileDescriptor, DRM_CAP_CURSOR_WIDTH, &capWidth);
drmGetCap(drm.fileDescriptor, DRM_CAP_CURSOR_HEIGHT, &capHeight);
std::vector<Uint8> curImg(capWidth * capHeight * 4);
std::size_t pitch = std::min(static_cast<std::size_t>(size.x), static_cast<std::size_t>(capWidth)) * 4;
unsigned int rows = std::min(size.y, static_cast<unsigned int>(capHeight));
uint64_t srcStride = size.x * 4;
uint64_t dstStride = capWidth * 4;
const Uint8* srcPixels = pixels;
Uint8* dstPixels = &curImg[0];
for (unsigned int i=0; i<rows; ++i)
{
std::memcpy(dstPixels, srcPixels, pitch);
srcPixels += srcStride;
dstPixels += dstStride;
}
m_hotspot = hotspot;
gbm_device *gbmDevice = DRMContext::getGbmDevice();
if (!gbmDevice)
return false; return false;
}
m_bo = gbm_bo_create( gbmDevice,
static_cast<uint32_t>(capWidth),
static_cast<uint32_t>(capHeight),
GBM_BO_FORMAT_ARGB8888,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE );
//////////////////////////////////////////////////////////// if (!m_bo)
bool CursorImpl::loadFromPixelsARGB(const Uint8* /*pixels*/, Vector2u /*size*/, Vector2u /*hotspot*/) {
{ sf::err() << "Error creating cursor gbm buffer object: " << strerror(errno) << std::endl;
return false; return false;
} }
if (gbm_bo_write( m_bo, &curImg[0], capWidth * capHeight * 4 ))
//////////////////////////////////////////////////////////// {
bool CursorImpl::loadFromPixelsMonochrome(const Uint8* /*pixels*/, Vector2u /*size*/, Vector2u /*hotspot*/) sf::err() << "Error writing cursor gbm buffer object: " << strerror(errno) << std::endl;
{
return false; return false;
}
return true;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool CursorImpl::loadFromSystem(Cursor::Type /*type*/) bool CursorImpl::loadFromSystem(Cursor::Type /*type*/)
{ {
return false; return loadFromPixels(reinterpret_cast<const Uint8 *>(defaultCursor16x16), Vector2u(16,16), Vector2u(0,0));
}
////////////////////////////////////////////////////////////
bool CursorImpl::isColorCursorSupported()
{
return false;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void CursorImpl::release() void CursorImpl::release()
{ {
if ( m_bo )
{
gbm_bo_destroy(m_bo);
m_bo = NULL;
}
m_hotspot = Vector2u();
} }
} // namespace priv } // namespace priv

View File

@ -33,6 +33,7 @@
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <SFML/Window/WindowStyle.hpp> // Prevent conflict with macro None from Xlib #include <SFML/Window/WindowStyle.hpp> // Prevent conflict with macro None from Xlib
#include <gbm.h>
namespace sf namespace sf
{ {
@ -80,35 +81,16 @@ public:
private: private:
friend class WindowImplDRM; friend class DRMContext;
////////////////////////////////////////////////////////////
/// \brief Checks if colored cursors are supported for this display.
///
////////////////////////////////////////////////////////////
bool isColorCursorSupported();
////////////////////////////////////////////////////////////
/// \brief Create a cursor with the provided image (ARGB support)
///
/// Refer to sf::Cursor::loadFromPixels().
///
////////////////////////////////////////////////////////////
bool loadFromPixelsARGB(const Uint8* pixels, Vector2u size, Vector2u hotspot);
////////////////////////////////////////////////////////////
/// \brief Create a cursor with the provided image (monochrome)
///
/// Refer to sf::Cursor::loadFromPixels().
///
////////////////////////////////////////////////////////////
bool loadFromPixelsMonochrome(const Uint8* pixels, Vector2u size, Vector2u hotspot);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Release the cursor, if we have loaded one. /// \brief Release the cursor, if we have loaded one.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void release(); void release();
gbm_bo *m_bo;
Vector2u m_hotspot;
}; };
} // namespace priv } // namespace priv

View File

@ -28,6 +28,7 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/DRM/DRMContext.hpp> #include <SFML/Window/DRM/DRMContext.hpp>
#include <SFML/Window/DRM/WindowImplDRM.hpp> #include <SFML/Window/DRM/WindowImplDRM.hpp>
#include <SFML/Window/DRM/CursorImpl.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <SFML/System/Sleep.hpp> #include <SFML/System/Sleep.hpp>
#include <cerrno> #include <cerrno>
@ -62,6 +63,12 @@ namespace
int contextCount = 0; int contextCount = 0;
EGLDisplay display = EGL_NO_DISPLAY; EGLDisplay display = EGL_NO_DISPLAY;
int waitingForFlip = 0; int waitingForFlip = 0;
sf::Vector2i mousePos; // current mouse position
sf::Vector2u mouseBounds;
bool mouseVisible = false;
int mouseVAdjust = 0;
sf::priv::CursorImpl *mouseDefaultCursor = NULL;
const sf::priv::CursorImpl *mouseUserCursor = NULL;
static void pageFlipHandler(int fd, unsigned int frame, static void pageFlipHandler(int fd, unsigned int frame,
unsigned int sec, unsigned int usec, void* data) unsigned int sec, unsigned int usec, void* data)
@ -102,6 +109,8 @@ namespace
if (!initialized) if (!initialized)
return; return;
drmModeSetCursor(drmNode.fileDescriptor, drmNode.crtcId, 0, 0, 0 );
drmModeSetCrtc(drmNode.fileDescriptor, drmModeSetCrtc(drmNode.fileDescriptor,
drmNode.originalCrtc->crtc_id, drmNode.originalCrtc->crtc_id,
drmNode.originalCrtc->buffer_id, drmNode.originalCrtc->buffer_id,
@ -118,6 +127,12 @@ namespace
eglTerminate(display); eglTerminate(display);
display = EGL_NO_DISPLAY; display = EGL_NO_DISPLAY;
if ( mouseDefaultCursor )
{
delete mouseDefaultCursor;
mouseDefaultCursor=NULL;
}
gbm_device_destroy(gbmDevice); gbm_device_destroy(gbmDevice);
gbmDevice = NULL; gbmDevice = NULL;
@ -130,6 +145,9 @@ namespace
std::memset(&drmEventCtx, 0, sizeof(drmEventContext)); std::memset(&drmEventCtx, 0, sizeof(drmEventContext));
waitingForFlip = 0; waitingForFlip = 0;
mouseVisible = false;
mouseUserCursor = NULL; // user is responsible for destruction of this
mouseVAdjust = 0;
initialized = false; initialized = false;
} }
@ -873,6 +891,112 @@ Drm& DRMContext::getDRM()
return drmNode; return drmNode;
} }
gbm_device *DRMContext::getGbmDevice()
{
initDrm();
return gbmDevice;
}
sf::Vector2i DRMContext::getCursorPos()
{
sf::Vector2i temp=mousePos;
temp.y -= mouseVAdjust;
return temp;
}
void DRMContext::setCursorPos( const sf::Vector2i &pos )
{
sf::Vector2i temp=pos;
if (temp.x > static_cast<int>(mouseBounds.x))
temp.x = static_cast<int>(mouseBounds.x);
else if (temp.x < 0)
temp.x=0;
temp.y += mouseVAdjust;
if (temp.y > static_cast<int>(mouseBounds.y) + mouseVAdjust)
temp.y = static_cast<int>(mouseBounds.y) + mouseVAdjust;
else if (temp.y < mouseVAdjust)
temp.y=mouseVAdjust;
mousePos=temp;
drmModeMoveCursor( drmNode.fileDescriptor, drmNode.crtcId, mousePos.x, mousePos.y );
}
void DRMContext::setCursorBounds( const sf::Vector2u &bound )
{
sf::Vector2u temp=bound;
mouseVAdjust=0;
if (drmNode.mode)
{
if (temp.x > drmNode.mode->hdisplay)
temp.x = drmNode.mode->hdisplay;
if (temp.y > drmNode.mode->vdisplay)
temp.y = drmNode.mode->vdisplay;
mouseVAdjust=static_cast<int>(drmNode.mode->vdisplay - temp.y);
}
mouseBounds=temp;
setCursorPos( getCursorPos() );
}
void DRMContext::setCursorVisible( bool vis )
{
bool update = ( vis != mouseVisible );
mouseVisible = vis;
if (update)
updateCursorDisplay();
}
void DRMContext::setUserCursor( const CursorImpl &cur )
{
bool update = mouseVisible && ( mouseUserCursor != &cur );
mouseUserCursor = &cur;
if (update)
updateCursorDisplay();
}
void DRMContext::updateCursorDisplay()
{
const CursorImpl *p = mouseUserCursor;
if (!p || !p->m_bo)
{
if (!mouseDefaultCursor)
{
mouseDefaultCursor = new CursorImpl();
if (!mouseDefaultCursor->loadFromSystem(sf::Cursor::Type::Arrow))
{
sf::err() << "Error loading default cursor." << std::endl;
}
}
p=mouseDefaultCursor;
}
int ret;
if (!(p->m_bo) || !mouseVisible)
{
ret = drmModeSetCursor(drmNode.fileDescriptor, drmNode.crtcId, 0, 0, 0 );
}
else
{
setCursorPos(getCursorPos());
ret = drmModeSetCursor2(drmNode.fileDescriptor, drmNode.crtcId, gbm_bo_get_handle(p->m_bo).u32,
gbm_bo_get_width(p->m_bo), gbm_bo_get_height(p->m_bo), static_cast<int32_t>(p->m_hotspot.x),
static_cast<int32_t>(p->m_hotspot.y));
}
if (ret)
{
sf::err() << "drmModeSetCursor failed to set cursor: " << strerror(errno) << std::endl;
}
}
} // namespace priv } // namespace priv
} // namespace sf } // namespace sf

View File

@ -33,6 +33,7 @@
#include <SFML/Window/EGLCheck.hpp> #include <SFML/Window/EGLCheck.hpp>
#include <SFML/Window/GlContext.hpp> #include <SFML/Window/GlContext.hpp>
#include <SFML/Window/VideoMode.hpp> #include <SFML/Window/VideoMode.hpp>
#include <SFML/System/Vector2.hpp>
#include <glad/egl.h> #include <glad/egl.h>
#include <gbm.h> #include <gbm.h>
#include <xf86drmMode.h> #include <xf86drmMode.h>
@ -57,6 +58,7 @@ struct Drm
}; };
class WindowImplDRM; class WindowImplDRM;
class CursorImpl;
class DRMContext : public GlContext class DRMContext : public GlContext
{ {
@ -178,16 +180,17 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
static GlFunctionPointer getFunction(const char* name); static GlFunctionPointer getFunction(const char* name);
protected:
friend class VideoModeImpl;
friend class WindowImplDRM;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get Direct Rendering Manager pointer /// \brief Get Direct Rendering Manager pointer
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
static Drm& getDRM(); static Drm& getDRM();
static gbm_device *getGbmDevice();
static sf::Vector2i getCursorPos();
static void setCursorPos( const sf::Vector2i &pos );
static void setCursorBounds( const sf::Vector2u &bounds );
static void setCursorVisible( bool vis );
static void setUserCursor( const CursorImpl &cur );
private: private:
@ -196,6 +199,7 @@ private:
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void updateSettings(); void updateSettings();
static void updateCursorDisplay();
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data

View File

@ -26,6 +26,7 @@
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/DRM/InputImplUDev.hpp> #include <SFML/Window/DRM/InputImplUDev.hpp>
#include <SFML/Window/DRM/DRMContext.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <SFML/System/Lock.hpp> #include <SFML/System/Lock.hpp>
#include <SFML/System/Mutex.hpp> #include <SFML/System/Mutex.hpp>
@ -60,7 +61,6 @@ namespace
}; };
sf::Mutex inputMutex; // threadsafe? maybe... sf::Mutex inputMutex; // threadsafe? maybe...
sf::Vector2i mousePos; // current mouse position
std::vector<int> fileDescriptors; // list of open file descriptors for /dev/input std::vector<int> fileDescriptors; // list of open file descriptors for /dev/input
std::vector<bool> mouseMap(sf::Mouse::ButtonCount, false); // track whether keys are down std::vector<bool> mouseMap(sf::Mouse::ButtonCount, false); // track whether keys are down
@ -120,7 +120,6 @@ namespace
bool is_mouse = (is_abs || is_rel) && TEST_BIT(BTN_MOUSE, bitmask_key); bool is_mouse = (is_abs || is_rel) && TEST_BIT(BTN_MOUSE, bitmask_key);
bool is_touch = is_abs && (TEST_BIT(BTN_TOOL_FINGER, bitmask_key) || TEST_BIT(BTN_TOUCH, bitmask_key)); bool is_touch = is_abs && (TEST_BIT(BTN_TOOL_FINGER, bitmask_key) || TEST_BIT(BTN_TOUCH, bitmask_key));
return is_keyboard || is_mouse || is_touch; return is_keyboard || is_mouse || is_touch;
} }
@ -359,6 +358,7 @@ namespace
} }
ssize_t bytesRead; ssize_t bytesRead;
sf::Vector2i mousePos = sf::priv::DRMContext::getCursorPos();
// Check all the open file descriptors for the next event // Check all the open file descriptors for the next event
for (std::vector<int>::iterator itr = fileDescriptors.begin(); itr != fileDescriptors.end(); ++itr) for (std::vector<int>::iterator itr = fileDescriptors.begin(); itr != fileDescriptors.end(); ++itr)
@ -448,6 +448,8 @@ namespace
if (posChange) if (posChange)
{ {
event.type = sf::Event::MouseMoved; event.type = sf::Event::MouseMoved;
sf::priv::DRMContext::setCursorPos( mousePos );
mousePos = sf::priv::DRMContext::getCursorPos();
event.mouseMove.x = mousePos.x; event.mouseMove.x = mousePos.x;
event.mouseMove.y = mousePos.y; event.mouseMove.y = mousePos.y;
return true; return true;
@ -627,7 +629,7 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button)
Vector2i InputImpl::getMousePosition() Vector2i InputImpl::getMousePosition()
{ {
Lock lock(inputMutex); Lock lock(inputMutex);
return mousePos; return sf::priv::DRMContext::getCursorPos();
} }
@ -642,7 +644,7 @@ Vector2i InputImpl::getMousePosition(const WindowBase& /*relativeTo*/)
void InputImpl::setMousePosition(const Vector2i& position) void InputImpl::setMousePosition(const Vector2i& position)
{ {
Lock lock(inputMutex); Lock lock(inputMutex);
mousePos = position; sf::priv::DRMContext::setCursorPos( position );
} }

View File

@ -50,6 +50,7 @@ WindowImplDRM::WindowImplDRM(VideoMode mode, const String& /*title*/, unsigned l
m_size(mode.width, mode.height) m_size(mode.width, mode.height)
{ {
sf::priv::InputImpl::setTerminalConfig(); sf::priv::InputImpl::setTerminalConfig();
DRMContext::setCursorBounds(Vector2u(mode.width,mode.height));
} }
@ -88,8 +89,10 @@ Vector2u WindowImplDRM::getSize() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplDRM::setSize(const Vector2u& /*size*/) void WindowImplDRM::setSize(const Vector2u& size)
{ {
m_size=size;
DRMContext::setCursorBounds(m_size);
} }
@ -111,21 +114,20 @@ void WindowImplDRM::setVisible(bool /*visible*/)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplDRM::setMouseCursorVisible(bool /*visible*/) void WindowImplDRM::setMouseCursorVisible(bool visible)
{ {
// TODO: not implemented DRMContext::setCursorVisible(visible);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplDRM::setMouseCursorGrabbed(bool /*grabbed*/) void WindowImplDRM::setMouseCursorGrabbed(bool /*grabbed*/)
{ {
//TODO: not implemented
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void WindowImplDRM::setMouseCursor(const CursorImpl& /*cursor*/) void WindowImplDRM::setMouseCursor(const CursorImpl& cursor)
{ {
// TODO: not implemented DRMContext::setUserCursor(cursor);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////