[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
////////////////////////////////////////////////////////////
#include <SFML/Window/DRM/CursorImpl.hpp>
#include <SFML/Window/DRM/DRMContext.hpp>
#include <SFML/System/Err.hpp>
#include <vector>
#include <cstring>
#include <xf86drm.h>
namespace sf
{
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()
: m_bo(NULL)
{
}
@ -42,47 +87,79 @@ 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)
{
return false;
}
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);
////////////////////////////////////////////////////////////
bool CursorImpl::loadFromPixelsARGB(const Uint8* /*pixels*/, Vector2u /*size*/, Vector2u /*hotspot*/)
{
return false;
}
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;
}
////////////////////////////////////////////////////////////
bool CursorImpl::loadFromPixelsMonochrome(const Uint8* /*pixels*/, Vector2u /*size*/, Vector2u /*hotspot*/)
{
return false;
m_hotspot = hotspot;
gbm_device *gbmDevice = DRMContext::getGbmDevice();
if (!gbmDevice)
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)
{
sf::err() << "Error creating cursor gbm buffer object: " << strerror(errno) << std::endl;
return false;
}
if (gbm_bo_write( m_bo, &curImg[0], capWidth * capHeight * 4 ))
{
sf::err() << "Error writing cursor gbm buffer object: " << strerror(errno) << std::endl;
return false;
}
return true;
}
////////////////////////////////////////////////////////////
bool CursorImpl::loadFromSystem(Cursor::Type /*type*/)
{
return false;
}
////////////////////////////////////////////////////////////
bool CursorImpl::isColorCursorSupported()
{
return false;
return loadFromPixels(reinterpret_cast<const Uint8 *>(defaultCursor16x16), Vector2u(16,16), Vector2u(0,0));
}
////////////////////////////////////////////////////////////
void CursorImpl::release()
{
if ( m_bo )
{
gbm_bo_destroy(m_bo);
m_bo = NULL;
}
m_hotspot = Vector2u();
}
} // namespace priv

View File

@ -33,6 +33,7 @@
#include <SFML/System/Vector2.hpp>
#include <SFML/Window/WindowStyle.hpp> // Prevent conflict with macro None from Xlib
#include <gbm.h>
namespace sf
{
@ -80,35 +81,16 @@ public:
private:
friend class WindowImplDRM;
////////////////////////////////////////////////////////////
/// \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);
friend class DRMContext;
////////////////////////////////////////////////////////////
/// \brief Release the cursor, if we have loaded one.
///
////////////////////////////////////////////////////////////
void release();
gbm_bo *m_bo;
Vector2u m_hotspot;
};
} // namespace priv

View File

@ -28,6 +28,7 @@
////////////////////////////////////////////////////////////
#include <SFML/Window/DRM/DRMContext.hpp>
#include <SFML/Window/DRM/WindowImplDRM.hpp>
#include <SFML/Window/DRM/CursorImpl.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Sleep.hpp>
#include <cerrno>
@ -62,6 +63,12 @@ namespace
int contextCount = 0;
EGLDisplay display = EGL_NO_DISPLAY;
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,
unsigned int sec, unsigned int usec, void* data)
@ -102,6 +109,8 @@ namespace
if (!initialized)
return;
drmModeSetCursor(drmNode.fileDescriptor, drmNode.crtcId, 0, 0, 0 );
drmModeSetCrtc(drmNode.fileDescriptor,
drmNode.originalCrtc->crtc_id,
drmNode.originalCrtc->buffer_id,
@ -118,6 +127,12 @@ namespace
eglTerminate(display);
display = EGL_NO_DISPLAY;
if ( mouseDefaultCursor )
{
delete mouseDefaultCursor;
mouseDefaultCursor=NULL;
}
gbm_device_destroy(gbmDevice);
gbmDevice = NULL;
@ -130,6 +145,9 @@ namespace
std::memset(&drmEventCtx, 0, sizeof(drmEventContext));
waitingForFlip = 0;
mouseVisible = false;
mouseUserCursor = NULL; // user is responsible for destruction of this
mouseVAdjust = 0;
initialized = false;
}
@ -873,6 +891,112 @@ Drm& DRMContext::getDRM()
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 sf

View File

@ -33,6 +33,7 @@
#include <SFML/Window/EGLCheck.hpp>
#include <SFML/Window/GlContext.hpp>
#include <SFML/Window/VideoMode.hpp>
#include <SFML/System/Vector2.hpp>
#include <glad/egl.h>
#include <gbm.h>
#include <xf86drmMode.h>
@ -57,6 +58,7 @@ struct Drm
};
class WindowImplDRM;
class CursorImpl;
class DRMContext : public GlContext
{
@ -178,16 +180,17 @@ public:
////////////////////////////////////////////////////////////
static GlFunctionPointer getFunction(const char* name);
protected:
friend class VideoModeImpl;
friend class WindowImplDRM;
////////////////////////////////////////////////////////////
/// \brief Get Direct Rendering Manager pointer
///
////////////////////////////////////////////////////////////
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:
@ -196,6 +199,7 @@ private:
///
////////////////////////////////////////////////////////////
void updateSettings();
static void updateCursorDisplay();
////////////////////////////////////////////////////////////
// Member data

View File

@ -26,6 +26,7 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/DRM/InputImplUDev.hpp>
#include <SFML/Window/DRM/DRMContext.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Mutex.hpp>
@ -60,7 +61,6 @@ namespace
};
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<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_touch = is_abs && (TEST_BIT(BTN_TOOL_FINGER, bitmask_key) || TEST_BIT(BTN_TOUCH, bitmask_key));
return is_keyboard || is_mouse || is_touch;
}
@ -359,6 +358,7 @@ namespace
}
ssize_t bytesRead;
sf::Vector2i mousePos = sf::priv::DRMContext::getCursorPos();
// Check all the open file descriptors for the next event
for (std::vector<int>::iterator itr = fileDescriptors.begin(); itr != fileDescriptors.end(); ++itr)
@ -448,6 +448,8 @@ namespace
if (posChange)
{
event.type = sf::Event::MouseMoved;
sf::priv::DRMContext::setCursorPos( mousePos );
mousePos = sf::priv::DRMContext::getCursorPos();
event.mouseMove.x = mousePos.x;
event.mouseMove.y = mousePos.y;
return true;
@ -627,7 +629,7 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button)
Vector2i InputImpl::getMousePosition()
{
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)
{
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)
{
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*/)
{
//TODO: not implemented
}
////////////////////////////////////////////////////////////
void WindowImplDRM::setMouseCursor(const CursorImpl& /*cursor*/)
void WindowImplDRM::setMouseCursor(const CursorImpl& cursor)
{
// TODO: not implemented
DRMContext::setUserCursor(cursor);
}
////////////////////////////////////////////////////////////