Client: Refactor cursors and fix various bugs
This patch is mostly a cleanup to prepare for implementations of xcursor-configuration, but also fixes a couple of issues. Most of the logic has now been moved out of QWaylandDisplay and QWaylandCursor and into QWaylandInputDevice and QWaylandInputDevice::Pointer. QWaylandDisplay now only contains mechanisms for avoiding loading the same theme multiple times. There is now only one setCursor method on QWaylandInputDevice, accepting a QCursor and storing its values so changing scale factor doesn't require calling setCursor again. QWaylandInputDevice::Pointer::updateCursor() is called instead. Cursor buffer scale is now set according to enter/leave events of the cursor surface itself instead of the current window, this fixes incorrect buffer scales for cursors on windows that span multiple outputs. The window buffer scale can still be passed into the seat as a fallback until the first enter event is received. This also fixes a bug where the QWaylandBuffer of a bitmap cursor could be deleted while it was being used as a cursor. [ChangeLog][QPA plugin] Fixed a bug where the DPI of bitmap cursors were not sent to the compositor, leading to the compositor incorrectly scaling the cursor up or down. [ChangeLog][QPA plugin] Fixed a bug where bitmap cursor hotspots were off when the screen scale factor was different from the bitmap cursor device pixel ratio. Task-number: QTBUG-68571 Change-Id: I747a47ffff01b7b5f6a0ede3552ab37884c4fa60 Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
parent
843c98d2d2
commit
a5bcdd0f86
@ -41,7 +41,6 @@
|
||||
|
||||
#include "qwaylanddisplay_p.h"
|
||||
#include "qwaylandinputdevice_p.h"
|
||||
#include "qwaylandscreen_p.h"
|
||||
#include "qwaylandshmbackingstore_p.h"
|
||||
|
||||
#include <QtGui/QImageReader>
|
||||
@ -53,12 +52,6 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QtWaylandClient {
|
||||
|
||||
QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size)
|
||||
{
|
||||
static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default"));
|
||||
return create(shm, size, themeName);
|
||||
}
|
||||
|
||||
QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size, const QString &themeName)
|
||||
{
|
||||
QByteArray nameBytes = themeName.toLocal8Bit();
|
||||
@ -244,56 +237,32 @@ struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape)
|
||||
return image;
|
||||
}
|
||||
|
||||
QWaylandCursor::QWaylandCursor(QWaylandScreen *screen)
|
||||
: mDisplay(screen->display())
|
||||
, mCursorTheme(mDisplay->loadCursorTheme(screen->devicePixelRatio()))
|
||||
QWaylandCursor::QWaylandCursor(QWaylandDisplay *display)
|
||||
: mDisplay(display)
|
||||
{
|
||||
}
|
||||
|
||||
QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapImage(const QCursor *cursor)
|
||||
QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor)
|
||||
{
|
||||
if (cursor->shape() != Qt::BitmapCursor)
|
||||
return QSharedPointer<QWaylandShmBuffer>();
|
||||
|
||||
Q_ASSERT(cursor->shape() == Qt::BitmapCursor);
|
||||
const QImage &img = cursor->pixmap().toImage();
|
||||
QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(mDisplay, img.size(), img.format()));
|
||||
memcpy(buffer->image()->bits(), img.bits(), img.sizeInBytes());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
struct wl_cursor_image *QWaylandCursor::cursorImage(Qt::CursorShape shape)
|
||||
{
|
||||
if (!mCursorTheme)
|
||||
return nullptr;
|
||||
return mCursorTheme->cursorImage(shape);
|
||||
QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(display, img.size(), img.format()));
|
||||
memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes()));
|
||||
return std::move(buffer);
|
||||
}
|
||||
|
||||
void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window)
|
||||
{
|
||||
const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor;
|
||||
Q_UNUSED(window);
|
||||
// Create the buffer here so we don't have to create one per input device
|
||||
QSharedPointer<QWaylandBuffer> bitmapBuffer;
|
||||
if (cursor && cursor->shape() == Qt::BitmapCursor)
|
||||
bitmapBuffer = cursorBitmapBuffer(mDisplay, cursor);
|
||||
|
||||
if (newShape == Qt::BlankCursor) {
|
||||
mDisplay->setCursor(nullptr, nullptr, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newShape == Qt::BitmapCursor) {
|
||||
mDisplay->setCursor(cursorBitmapImage(cursor), cursor->hotSpot(), window->screen()->devicePixelRatio());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mCursorTheme) {
|
||||
qCWarning(lcQpaWayland) << "Can't set cursor from shape with no cursor theme";
|
||||
return;
|
||||
}
|
||||
|
||||
if (struct ::wl_cursor_image *image = mCursorTheme->cursorImage(newShape)) {
|
||||
struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
|
||||
mDisplay->setCursor(buffer, image, window->screen()->devicePixelRatio());
|
||||
return;
|
||||
}
|
||||
|
||||
qCWarning(lcQpaWayland) << "Unable to change to cursor" << cursor;
|
||||
int fallbackOutputScale = int(window->devicePixelRatio());
|
||||
const auto seats = mDisplay->inputDevices();
|
||||
for (auto *seat : seats)
|
||||
seat->setCursor(cursor, bitmapBuffer, fallbackOutputScale);
|
||||
}
|
||||
|
||||
void QWaylandCursor::pointerEvent(const QMouseEvent &event)
|
||||
@ -312,6 +281,6 @@ void QWaylandCursor::setPos(const QPoint &pos)
|
||||
qCWarning(lcQpaWayland) << "Setting cursor position is not possible on wayland";
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace QtWaylandClient
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -73,7 +73,6 @@ class QWaylandShm;
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme
|
||||
{
|
||||
public:
|
||||
static QWaylandCursorTheme *create(QWaylandShm *shm, int size);
|
||||
static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName);
|
||||
~QWaylandCursorTheme();
|
||||
struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
|
||||
@ -122,19 +121,18 @@ private:
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandCursor : public QPlatformCursor
|
||||
{
|
||||
public:
|
||||
QWaylandCursor(QWaylandScreen *screen);
|
||||
explicit QWaylandCursor(QWaylandDisplay *display);
|
||||
|
||||
void changeCursor(QCursor *cursor, QWindow *window) override;
|
||||
void pointerEvent(const QMouseEvent &event) override;
|
||||
QPoint pos() const override;
|
||||
void setPos(const QPoint &pos) override;
|
||||
|
||||
QSharedPointer<QWaylandBuffer> cursorBitmapImage(const QCursor *cursor);
|
||||
static QSharedPointer<QWaylandBuffer> cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor);
|
||||
struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
|
||||
|
||||
private:
|
||||
QWaylandDisplay *mDisplay = nullptr;
|
||||
QWaylandCursorTheme *mCursorTheme = nullptr;
|
||||
QPoint mLastPos;
|
||||
};
|
||||
|
||||
|
@ -160,7 +160,7 @@ QWaylandDisplay::~QWaylandDisplay(void)
|
||||
delete mDndSelectionHandler.take();
|
||||
#endif
|
||||
#if QT_CONFIG(cursor)
|
||||
qDeleteAll(mCursorThemesBySize);
|
||||
qDeleteAll(mCursorThemes);
|
||||
#endif
|
||||
if (mDisplay)
|
||||
wl_display_disconnect(mDisplay);
|
||||
@ -505,40 +505,20 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
|
||||
void QWaylandDisplay::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr)
|
||||
QWaylandCursor *QWaylandDisplay::waylandCursor()
|
||||
{
|
||||
/* Qt doesn't tell us which input device we should set the cursor
|
||||
* for, so set it for all devices. */
|
||||
for (int i = 0; i < mInputDevices.count(); i++) {
|
||||
QWaylandInputDevice *inputDevice = mInputDevices.at(i);
|
||||
inputDevice->setCursor(buffer, image, dpr);
|
||||
}
|
||||
if (!mCursor)
|
||||
mCursor.reset(new QWaylandCursor(this));
|
||||
return mCursor.data();
|
||||
}
|
||||
|
||||
void QWaylandDisplay::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr)
|
||||
QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize)
|
||||
{
|
||||
/* Qt doesn't tell us which input device we should set the cursor
|
||||
* for, so set it for all devices. */
|
||||
for (int i = 0; i < mInputDevices.count(); i++) {
|
||||
QWaylandInputDevice *inputDevice = mInputDevices.at(i);
|
||||
inputDevice->setCursor(buffer, hotSpot, dpr);
|
||||
}
|
||||
}
|
||||
|
||||
QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(qreal devicePixelRatio)
|
||||
{
|
||||
constexpr int defaultCursorSize = 32;
|
||||
static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
|
||||
int cursorSize = xCursorSize > 0 ? xCursorSize : defaultCursorSize;
|
||||
|
||||
if (compositorVersion() >= 3) // set_buffer_scale is not supported on earlier versions
|
||||
cursorSize *= devicePixelRatio;
|
||||
|
||||
if (auto *theme = mCursorThemesBySize.value(cursorSize, nullptr))
|
||||
if (auto *theme = mCursorThemes.value({name, pixelSize}, nullptr))
|
||||
return theme;
|
||||
|
||||
if (auto *theme = QWaylandCursorTheme::create(shm(), cursorSize)) {
|
||||
mCursorThemesBySize[cursorSize] = theme;
|
||||
if (auto *theme = QWaylandCursorTheme::create(shm(), pixelSize, name)) {
|
||||
mCursorThemes[{name, pixelSize}] = theme;
|
||||
return theme;
|
||||
}
|
||||
|
||||
@ -547,6 +527,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(qreal devicePixelRatio)
|
||||
|
||||
#endif // QT_CONFIG(cursor)
|
||||
|
||||
}
|
||||
} // namespace QtWaylandClient
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -93,6 +93,7 @@ class QWaylandWindow;
|
||||
class QWaylandIntegration;
|
||||
class QWaylandHardwareIntegration;
|
||||
class QWaylandShellSurface;
|
||||
class QWaylandCursor;
|
||||
class QWaylandCursorTheme;
|
||||
|
||||
typedef void (*RegistryListener)(void *data,
|
||||
@ -121,9 +122,8 @@ public:
|
||||
|
||||
QWaylandWindowManagerIntegration *windowManagerIntegration() const;
|
||||
#if QT_CONFIG(cursor)
|
||||
void setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr);
|
||||
void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr);
|
||||
QWaylandCursorTheme *loadCursorTheme(qreal devicePixelRatio);
|
||||
QWaylandCursor *waylandCursor();
|
||||
QWaylandCursorTheme *loadCursorTheme(const QString &name, int pixelSize);
|
||||
#endif
|
||||
struct wl_display *wl_display() const { return mDisplay; }
|
||||
struct ::wl_registry *wl_registry() { return object(); }
|
||||
@ -210,7 +210,8 @@ private:
|
||||
QList<Listener> mRegistryListeners;
|
||||
QWaylandIntegration *mWaylandIntegration = nullptr;
|
||||
#if QT_CONFIG(cursor)
|
||||
QMap<int, QWaylandCursorTheme *> mCursorThemesBySize;
|
||||
QMap<std::pair<QString, int>, QWaylandCursorTheme *> mCursorThemes; // theme name and size
|
||||
QScopedPointer<QWaylandCursor> mCursor;
|
||||
#endif
|
||||
#if QT_CONFIG(wayland_datadevice)
|
||||
QScopedPointer<QWaylandDataDeviceManager> mDndSelectionHandler;
|
||||
|
@ -173,8 +173,8 @@ void QWaylandInputDevice::Keyboard::stopRepeat()
|
||||
mRepeatTimer.stop();
|
||||
}
|
||||
|
||||
QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *p)
|
||||
: mParent(p)
|
||||
QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *seat)
|
||||
: mParent(seat)
|
||||
{
|
||||
}
|
||||
|
||||
@ -186,6 +186,153 @@ QWaylandInputDevice::Pointer::~Pointer()
|
||||
wl_pointer_destroy(object());
|
||||
}
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
|
||||
class CursorSurface : public QtWayland::wl_surface {
|
||||
public:
|
||||
explicit CursorSurface(QWaylandInputDevice::Pointer *pointer, QWaylandDisplay *display)
|
||||
: m_pointer(pointer)
|
||||
{
|
||||
init(display->createSurface(this));
|
||||
//TODO: When we upgrade to libwayland 1.10, use wl_surface_get_version instead.
|
||||
m_version = display->compositorVersion();
|
||||
}
|
||||
|
||||
void hide()
|
||||
{
|
||||
m_pointer->set_cursor(m_pointer->mEnterSerial, nullptr, 0, 0);
|
||||
m_setSerial = 0;
|
||||
}
|
||||
|
||||
// Size and hotspot are in surface coordinates
|
||||
void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale)
|
||||
{
|
||||
// Calling code needs to ensure buffer scale is supported if != 1
|
||||
Q_ASSERT(bufferScale == 1 || m_version >= 3);
|
||||
|
||||
auto enterSerial = m_pointer->mEnterSerial;
|
||||
if (m_setSerial < enterSerial || m_hotspot != hotspot) {
|
||||
m_pointer->set_cursor(m_pointer->mEnterSerial, object(), hotspot.x(), hotspot.y());
|
||||
m_setSerial = enterSerial;
|
||||
m_hotspot = hotspot;
|
||||
}
|
||||
|
||||
if (m_version >= 3)
|
||||
set_buffer_scale(bufferScale);
|
||||
|
||||
attach(buffer, 0, 0);
|
||||
damage(0, 0, size.width(), size.height());
|
||||
commit();
|
||||
}
|
||||
|
||||
int outputScale() const { return m_outputScale; }
|
||||
|
||||
protected:
|
||||
void surface_enter(struct ::wl_output *output) override
|
||||
{
|
||||
//TODO: Can be improved to keep track of all entered screens
|
||||
int scale = QWaylandScreen::fromWlOutput(output)->scale();
|
||||
if (scale == m_outputScale)
|
||||
return;
|
||||
|
||||
m_outputScale = scale;
|
||||
m_pointer->updateCursor();
|
||||
}
|
||||
|
||||
private:
|
||||
QWaylandInputDevice::Pointer *m_pointer = nullptr;
|
||||
uint m_version = 0;
|
||||
uint m_setSerial = 0;
|
||||
QPoint m_hotspot;
|
||||
int m_outputScale = 0;
|
||||
};
|
||||
|
||||
QString QWaylandInputDevice::Pointer::cursorThemeName() const
|
||||
{
|
||||
static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default"));
|
||||
return themeName;
|
||||
}
|
||||
|
||||
int QWaylandInputDevice::Pointer::cursorSize() const
|
||||
{
|
||||
constexpr int defaultCursorSize = 32;
|
||||
static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
|
||||
return xCursorSize > 0 ? xCursorSize : defaultCursorSize;
|
||||
}
|
||||
|
||||
int QWaylandInputDevice::Pointer::idealCursorScale() const
|
||||
{
|
||||
// set_buffer_scale is not supported on earlier versions
|
||||
if (seat()->mQDisplay->compositorVersion() < 3)
|
||||
return 1;
|
||||
|
||||
if (auto *s = mCursor.surface.data()) {
|
||||
if (s->outputScale() > 0)
|
||||
return s->outputScale();
|
||||
}
|
||||
|
||||
return seat()->mCursor.fallbackOutputScale;
|
||||
}
|
||||
|
||||
void QWaylandInputDevice::Pointer::updateCursorTheme()
|
||||
{
|
||||
int scale = idealCursorScale();
|
||||
int pixelSize = cursorSize() * scale;
|
||||
auto *display = seat()->mQDisplay;
|
||||
mCursor.theme = display->loadCursorTheme(cursorThemeName(), pixelSize);
|
||||
mCursor.themeBufferScale = scale;
|
||||
}
|
||||
|
||||
void QWaylandInputDevice::Pointer::updateCursor()
|
||||
{
|
||||
if (mEnterSerial == 0)
|
||||
return;
|
||||
|
||||
auto shape = seat()->mCursor.shape;
|
||||
|
||||
if (shape == Qt::BlankCursor) {
|
||||
if (mCursor.surface)
|
||||
mCursor.surface->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (shape == Qt::BitmapCursor) {
|
||||
auto buffer = seat()->mCursor.bitmapBuffer;
|
||||
if (!buffer) {
|
||||
qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
|
||||
return;
|
||||
}
|
||||
auto hotspot = seat()->mCursor.hotspot;
|
||||
int bufferScale = seat()->mCursor.bitmapScale;
|
||||
getOrCreateCursorSurface()->update(buffer->buffer(), hotspot, buffer->size(), bufferScale);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mCursor.theme || idealCursorScale() != mCursor.themeBufferScale)
|
||||
updateCursorTheme();
|
||||
|
||||
// Set from shape using theme
|
||||
if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape)) {
|
||||
struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
|
||||
int bufferScale = mCursor.themeBufferScale;
|
||||
QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
|
||||
QSize size = QSize(image->width, image->height) / bufferScale;
|
||||
getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale);
|
||||
return;
|
||||
}
|
||||
|
||||
qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
|
||||
}
|
||||
|
||||
CursorSurface *QWaylandInputDevice::Pointer::getOrCreateCursorSurface()
|
||||
{
|
||||
if (!mCursor.surface)
|
||||
mCursor.surface.reset(new CursorSurface(this, seat()->mQDisplay));
|
||||
return mCursor.surface.get();
|
||||
}
|
||||
|
||||
#endif // QT_CONFIG(cursor)
|
||||
|
||||
QWaylandInputDevice::Touch::Touch(QWaylandInputDevice *p)
|
||||
: mParent(p)
|
||||
{
|
||||
@ -359,87 +506,33 @@ Qt::KeyboardModifiers QWaylandInputDevice::Keyboard::modifiers() const
|
||||
}
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
uint32_t QWaylandInputDevice::cursorSerial() const
|
||||
void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer, int fallbackOutputScale)
|
||||
{
|
||||
CursorState oldCursor = mCursor;
|
||||
mCursor = CursorState(); // Clear any previous state
|
||||
mCursor.shape = cursor ? cursor->shape() : Qt::ArrowCursor;
|
||||
mCursor.hotspot = cursor ? cursor->hotSpot() : QPoint();
|
||||
mCursor.fallbackOutputScale = fallbackOutputScale;
|
||||
|
||||
if (mCursor.shape == Qt::BitmapCursor) {
|
||||
mCursor.bitmapBuffer = cachedBuffer ? cachedBuffer : QWaylandCursor::cursorBitmapBuffer(mQDisplay, cursor);
|
||||
qreal dpr = cursor->pixmap().devicePixelRatio();
|
||||
mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
|
||||
// If there was a fractional part of the dpr, we need to scale the hotspot accordingly
|
||||
if (mCursor.bitmapScale < dpr)
|
||||
mCursor.hotspot *= dpr / mCursor.bitmapScale;
|
||||
}
|
||||
|
||||
// Return early if setCursor was called redundantly (mostly happens from decorations)
|
||||
if (mCursor.shape != Qt::BitmapCursor
|
||||
&& mCursor.shape == oldCursor.shape
|
||||
&& mCursor.hotspot == oldCursor.hotspot
|
||||
&& mCursor.fallbackOutputScale == oldCursor.fallbackOutputScale) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPointer)
|
||||
return mPointer->mCursorSerial;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QWaylandInputDevice::setCursor(Qt::CursorShape newShape, QWaylandScreen *screen)
|
||||
{
|
||||
struct wl_cursor_image *image = screen->waylandCursor()->cursorImage(newShape);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
|
||||
setCursor(buffer, image, screen->devicePixelRatio());
|
||||
}
|
||||
|
||||
void QWaylandInputDevice::setCursor(const QCursor &cursor, QWaylandScreen *screen)
|
||||
{
|
||||
if (mPointer->mCursorSerial >= mPointer->mEnterSerial && (cursor.shape() != Qt::BitmapCursor && cursor.shape() == mPointer->mCursorShape))
|
||||
return;
|
||||
|
||||
mPointer->mCursorShape = cursor.shape();
|
||||
if (cursor.shape() == Qt::BitmapCursor) {
|
||||
setCursor(screen->waylandCursor()->cursorBitmapImage(&cursor), cursor.hotSpot(), screen->devicePixelRatio());
|
||||
return;
|
||||
}
|
||||
setCursor(cursor.shape(), screen);
|
||||
}
|
||||
|
||||
void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, int bufferScale)
|
||||
{
|
||||
if (image) {
|
||||
// Convert from pixel coordinates to surface coordinates
|
||||
QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
|
||||
QSize size = QSize(image->width, image->height) / bufferScale;
|
||||
setCursor(buffer, hotspot, size, bufferScale);
|
||||
} else {
|
||||
setCursor(buffer, QPoint(), QSize(), bufferScale);
|
||||
}
|
||||
}
|
||||
|
||||
// size and hotspot are in surface coordinates
|
||||
void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale)
|
||||
{
|
||||
if (mCaps & WL_SEAT_CAPABILITY_POINTER) {
|
||||
bool force = mPointer->mEnterSerial > mPointer->mCursorSerial;
|
||||
|
||||
if (!force && mPointer->mCursorBuffer == buffer)
|
||||
return;
|
||||
|
||||
mPixmapCursor.clear();
|
||||
mPointer->mCursorSerial = mPointer->mEnterSerial;
|
||||
|
||||
mPointer->mCursorBuffer = buffer;
|
||||
|
||||
/* Hide cursor */
|
||||
if (!buffer)
|
||||
{
|
||||
mPointer->set_cursor(mPointer->mEnterSerial, nullptr, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pointerSurface)
|
||||
pointerSurface = mQDisplay->createSurface(this);
|
||||
|
||||
mPointer->set_cursor(mPointer->mEnterSerial, pointerSurface,
|
||||
hotSpot.x(), hotSpot.y());
|
||||
wl_surface_attach(pointerSurface, buffer, 0, 0);
|
||||
if (mQDisplay->compositorVersion() >= 3)
|
||||
wl_surface_set_buffer_scale(pointerSurface, bufferScale);
|
||||
wl_surface_damage(pointerSurface, 0, 0, size.width(), size.height());
|
||||
wl_surface_commit(pointerSurface);
|
||||
}
|
||||
}
|
||||
|
||||
void QWaylandInputDevice::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale)
|
||||
{
|
||||
setCursor(buffer->buffer(), hotSpot, buffer->size(), bufferScale);
|
||||
mPixmapCursor = buffer;
|
||||
mPointer->updateCursor();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -467,7 +560,7 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
// Depends on mEnterSerial being updated
|
||||
window->window()->setCursor(window->window()->cursor());
|
||||
updateCursor();
|
||||
#endif
|
||||
|
||||
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
|
||||
|
@ -88,6 +88,10 @@ class QWaylandWindow;
|
||||
class QWaylandDisplay;
|
||||
class QWaylandDataDevice;
|
||||
class QWaylandTextInput;
|
||||
#if QT_CONFIG(cursor)
|
||||
class QWaylandCursorTheme;
|
||||
class CursorSurface;
|
||||
#endif
|
||||
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice
|
||||
: public QObject
|
||||
@ -107,10 +111,7 @@ public:
|
||||
struct ::wl_seat *wl_seat() { return QtWayland::wl_seat::object(); }
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
void setCursor(const QCursor &cursor, QWaylandScreen *screen);
|
||||
void setCursor(struct wl_buffer *buffer, struct ::wl_cursor_image *image, int bufferScale);
|
||||
void setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale);
|
||||
void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale);
|
||||
void setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer = {}, int fallbackOutputScale = 1);
|
||||
#endif
|
||||
void handleWindowDestroyed(QWaylandWindow *window);
|
||||
void handleEndDrag();
|
||||
@ -134,22 +135,27 @@ public:
|
||||
Qt::KeyboardModifiers modifiers() const;
|
||||
|
||||
uint32_t serial() const;
|
||||
uint32_t cursorSerial() const;
|
||||
|
||||
virtual Keyboard *createKeyboard(QWaylandInputDevice *device);
|
||||
virtual Pointer *createPointer(QWaylandInputDevice *device);
|
||||
virtual Touch *createTouch(QWaylandInputDevice *device);
|
||||
|
||||
private:
|
||||
void setCursor(Qt::CursorShape cursor, QWaylandScreen *screen);
|
||||
|
||||
QWaylandDisplay *mQDisplay = nullptr;
|
||||
struct wl_display *mDisplay = nullptr;
|
||||
|
||||
int mVersion;
|
||||
uint32_t mCaps = 0;
|
||||
|
||||
struct wl_surface *pointerSurface = nullptr;
|
||||
#if QT_CONFIG(cursor)
|
||||
struct CursorState {
|
||||
QSharedPointer<QWaylandBuffer> bitmapBuffer; // not used with shape cursors
|
||||
int bitmapScale = 1;
|
||||
Qt::CursorShape shape = Qt::ArrowCursor;
|
||||
int fallbackOutputScale = 1;
|
||||
QPoint hotspot;
|
||||
} mCursor;
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(wayland_datadevice)
|
||||
QWaylandDataDevice *mDataDevice = nullptr;
|
||||
@ -169,8 +175,6 @@ private:
|
||||
|
||||
QTouchDevice *mTouchDevice = nullptr;
|
||||
|
||||
QSharedPointer<QWaylandBuffer> mPixmapCursor;
|
||||
|
||||
friend class QWaylandTouchExtension;
|
||||
friend class QWaylandQtKeyExtension;
|
||||
};
|
||||
@ -247,11 +251,20 @@ private:
|
||||
|
||||
class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QtWayland::wl_pointer
|
||||
{
|
||||
|
||||
public:
|
||||
Pointer(QWaylandInputDevice *p);
|
||||
explicit Pointer(QWaylandInputDevice *seat);
|
||||
~Pointer() override;
|
||||
#if QT_CONFIG(cursor)
|
||||
QString cursorThemeName() const;
|
||||
int cursorSize() const; // in surface coordinates
|
||||
int idealCursorScale() const;
|
||||
void updateCursorTheme();
|
||||
void updateCursor();
|
||||
CursorSurface *getOrCreateCursorSurface();
|
||||
#endif
|
||||
QWaylandInputDevice *seat() const { return mParent; }
|
||||
|
||||
protected:
|
||||
void pointer_enter(uint32_t serial, struct wl_surface *surface,
|
||||
wl_fixed_t sx, wl_fixed_t sy) override;
|
||||
void pointer_leave(uint32_t time, struct wl_surface *surface) override;
|
||||
@ -263,13 +276,19 @@ public:
|
||||
uint32_t axis,
|
||||
wl_fixed_t value) override;
|
||||
|
||||
public:
|
||||
void releaseButtons();
|
||||
|
||||
QWaylandInputDevice *mParent = nullptr;
|
||||
QPointer<QWaylandWindow> mFocus;
|
||||
uint32_t mEnterSerial = 0;
|
||||
#if QT_CONFIG(cursor)
|
||||
uint32_t mCursorSerial = 0;
|
||||
struct {
|
||||
uint32_t serial = 0;
|
||||
QWaylandCursorTheme *theme = nullptr;
|
||||
int themeBufferScale = 0;
|
||||
QScopedPointer<CursorSurface> surface;
|
||||
} mCursor;
|
||||
#endif
|
||||
QPointF mSurfacePos;
|
||||
QPointF mGlobalPos;
|
||||
|
@ -176,19 +176,10 @@ qreal QWaylandScreen::refreshRate() const
|
||||
}
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
|
||||
QPlatformCursor *QWaylandScreen::cursor() const
|
||||
{
|
||||
return const_cast<QWaylandScreen *>(this)->waylandCursor();
|
||||
return mWaylandDisplay->waylandCursor();
|
||||
}
|
||||
|
||||
QWaylandCursor *QWaylandScreen::waylandCursor()
|
||||
{
|
||||
if (!mWaylandCursor)
|
||||
mWaylandCursor.reset(new QWaylandCursor(this));
|
||||
return mWaylandCursor.data();
|
||||
}
|
||||
|
||||
#endif // QT_CONFIG(cursor)
|
||||
|
||||
QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
|
||||
|
@ -98,7 +98,6 @@ public:
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
QPlatformCursor *cursor() const override;
|
||||
QWaylandCursor *waylandCursor();
|
||||
#endif
|
||||
|
||||
uint32_t outputId() const { return m_outputId; }
|
||||
|
@ -962,7 +962,8 @@ void QWaylandWindow::handleScreenChanged()
|
||||
#if QT_CONFIG(cursor)
|
||||
void QWaylandWindow::setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor)
|
||||
{
|
||||
device->setCursor(cursor, waylandScreen());
|
||||
int fallbackBufferScale = int(devicePixelRatio());
|
||||
device->setCursor(&cursor, {}, fallbackBufferScale);
|
||||
}
|
||||
|
||||
void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device)
|
||||
|
@ -1,4 +1,9 @@
|
||||
include (../shared/shared.pri)
|
||||
|
||||
qtConfig(cursor) {
|
||||
QMAKE_USE += wayland-cursor
|
||||
QT += gui-private
|
||||
}
|
||||
|
||||
TARGET = tst_seatv4
|
||||
SOURCES += tst_seatv4.cpp
|
||||
|
@ -30,6 +30,12 @@
|
||||
|
||||
#include <QtGui/QRasterWindow>
|
||||
#include <QtGui/QOpenGLWindow>
|
||||
#if QT_CONFIG(cursor)
|
||||
#include <wayland-cursor.h>
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
#include <QtWaylandClient/private/qwaylanddisplay_p.h>
|
||||
#include <QtWaylandClient/private/qwaylandintegration_p.h>
|
||||
#endif
|
||||
|
||||
using namespace MockCompositor;
|
||||
|
||||
@ -59,6 +65,8 @@ class tst_seatv4 : public QObject, private SeatV4Compositor
|
||||
private slots:
|
||||
void cleanup();
|
||||
void bindsToSeat();
|
||||
void keyboardKeyPress();
|
||||
#if QT_CONFIG(cursor)
|
||||
void createsPointer();
|
||||
void setsCursorOnEnter();
|
||||
void usesEnterSerial();
|
||||
@ -67,8 +75,10 @@ private slots:
|
||||
void simpleAxis();
|
||||
void invalidPointerEvents();
|
||||
void scaledCursor();
|
||||
|
||||
void keyboardKeyPress();
|
||||
void bitmapCursor();
|
||||
void hidpiBitmapCursor();
|
||||
void hidpiBitmapCursorNonInt();
|
||||
#endif
|
||||
};
|
||||
|
||||
void tst_seatv4::cleanup()
|
||||
@ -83,6 +93,31 @@ void tst_seatv4::bindsToSeat()
|
||||
QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 4);
|
||||
}
|
||||
|
||||
void tst_seatv4::keyboardKeyPress()
|
||||
{
|
||||
class Window : public QRasterWindow {
|
||||
public:
|
||||
void keyPressEvent(QKeyEvent *) override { m_pressed = true; }
|
||||
bool m_pressed = false;
|
||||
};
|
||||
|
||||
Window window;
|
||||
window.resize(64, 64);
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
uint keyCode = 80; // arbitrarily chosen
|
||||
exec([&] {
|
||||
auto *surface = xdgSurface()->m_surface;
|
||||
keyboard()->sendEnter(surface);
|
||||
keyboard()->sendKey(client(), keyCode, Keyboard::key_state_pressed);
|
||||
keyboard()->sendKey(client(), keyCode, Keyboard::key_state_released);
|
||||
});
|
||||
QTRY_VERIFY(window.m_pressed);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
|
||||
void tst_seatv4::createsPointer()
|
||||
{
|
||||
QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1);
|
||||
@ -253,9 +288,36 @@ void tst_seatv4::invalidPointerEvents()
|
||||
xdgPingAndWaitForPong();
|
||||
}
|
||||
|
||||
static bool supportsCursorSize(uint size, wl_shm *shm)
|
||||
{
|
||||
auto *theme = wl_cursor_theme_load(nullptr, size, shm);
|
||||
if (!theme)
|
||||
return false;
|
||||
|
||||
constexpr std::array<const char *, 4> names{"left_ptr", "default", "left_arrow", "top_left_arrow"};
|
||||
for (const char *name : names) {
|
||||
if (auto *cursor = wl_cursor_theme_get_cursor(theme, name)) {
|
||||
auto *image = cursor->images[0];
|
||||
return image->width == image->height && image->width == size;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool supportsCursorSizes(const QVector<uint> &sizes)
|
||||
{
|
||||
auto *waylandIntegration = static_cast<QtWaylandClient::QWaylandIntegration *>(QGuiApplicationPrivate::platformIntegration());
|
||||
wl_shm *shm = waylandIntegration->display()->shm()->object();
|
||||
return std::all_of(sizes.begin(), sizes.end(), [=](uint size) {
|
||||
return supportsCursorSize(size, shm);
|
||||
});
|
||||
}
|
||||
|
||||
void tst_seatv4::scaledCursor()
|
||||
{
|
||||
QSKIP("Currently broken and should be fixed");
|
||||
if (!supportsCursorSizes({32, 64}))
|
||||
QSKIP("Cursor themes with sizes 32 and 64 not found.");
|
||||
|
||||
// Add a highdpi output
|
||||
exec([&] {
|
||||
OutputData d;
|
||||
@ -289,28 +351,122 @@ void tst_seatv4::scaledCursor()
|
||||
exec([&] { remove(output(1)); });
|
||||
}
|
||||
|
||||
void tst_seatv4::keyboardKeyPress()
|
||||
void tst_seatv4::bitmapCursor()
|
||||
{
|
||||
class Window : public QRasterWindow {
|
||||
public:
|
||||
void keyPressEvent(QKeyEvent *) override { m_pressed = true; }
|
||||
bool m_pressed = false;
|
||||
};
|
||||
// Add a highdpi output
|
||||
exec([&] {
|
||||
OutputData d;
|
||||
d.scale = 2;
|
||||
d.position = {1920, 0};
|
||||
add<Output>(d);
|
||||
});
|
||||
|
||||
Window window;
|
||||
QRasterWindow window;
|
||||
window.resize(64, 64);
|
||||
|
||||
QPixmap pixmap(24, 24);
|
||||
pixmap.setDevicePixelRatio(1);
|
||||
QPoint hotspot(12, 12); // In device pixel coordinates
|
||||
QCursor cursor(pixmap, hotspot.x(), hotspot.y());
|
||||
window.setCursor(cursor);
|
||||
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
uint keyCode = 80; // arbitrarily chosen
|
||||
exec([&] {
|
||||
auto *surface = xdgSurface()->m_surface;
|
||||
keyboard()->sendEnter(surface);
|
||||
keyboard()->sendKey(client(), keyCode, Keyboard::key_state_pressed);
|
||||
keyboard()->sendKey(client(), keyCode, Keyboard::key_state_released);
|
||||
exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
|
||||
QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
|
||||
QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()->m_committed.buffer);
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.buffer->size(), QSize(24, 24));
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.bufferScale, 1);
|
||||
QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12));
|
||||
|
||||
exec([=] {
|
||||
auto *surface = pointer()->cursorSurface();
|
||||
surface->sendEnter(getAll<Output>()[1]);
|
||||
surface->sendLeave(getAll<Output>()[0]);
|
||||
});
|
||||
QTRY_VERIFY(window.m_pressed);
|
||||
|
||||
xdgPingAndWaitForPong();
|
||||
|
||||
// Everything should remain the same, the cursor still has dpr 1
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.bufferScale, 1);
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.buffer->size(), QSize(24, 24));
|
||||
QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12));
|
||||
|
||||
// Remove the extra output to clean up for the next test
|
||||
exec([&] { remove(getAll<Output>()[1]); });
|
||||
}
|
||||
|
||||
void tst_seatv4::hidpiBitmapCursor()
|
||||
{
|
||||
// Add a highdpi output
|
||||
exec([&] {
|
||||
OutputData d;
|
||||
d.scale = 2;
|
||||
d.position = {1920, 0};
|
||||
add<Output>(d);
|
||||
});
|
||||
|
||||
QRasterWindow window;
|
||||
window.resize(64, 64);
|
||||
|
||||
QPixmap pixmap(48, 48);
|
||||
pixmap.setDevicePixelRatio(2);
|
||||
QPoint hotspot(12, 12); // In device pixel coordinates
|
||||
QCursor cursor(pixmap, hotspot.x(), hotspot.y());
|
||||
window.setCursor(cursor);
|
||||
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
|
||||
QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
|
||||
QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()->m_committed.buffer);
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.buffer->size(), QSize(48, 48));
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.bufferScale, 2);
|
||||
QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12));
|
||||
|
||||
exec([=] {
|
||||
auto *surface = pointer()->cursorSurface();
|
||||
surface->sendEnter(getAll<Output>()[1]);
|
||||
surface->sendLeave(getAll<Output>()[0]);
|
||||
});
|
||||
|
||||
xdgPingAndWaitForPong();
|
||||
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.bufferScale, 2);
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.buffer->size(), QSize(48, 48));
|
||||
QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12));
|
||||
|
||||
// Remove the extra output to clean up for the next test
|
||||
exec([&] { remove(getAll<Output>()[1]); });
|
||||
}
|
||||
|
||||
void tst_seatv4::hidpiBitmapCursorNonInt()
|
||||
{
|
||||
QRasterWindow window;
|
||||
window.resize(64, 64);
|
||||
|
||||
QPixmap pixmap(100, 100);
|
||||
pixmap.setDevicePixelRatio(2.5); // dpr width is now 100 / 2.5 = 40
|
||||
QPoint hotspot(20, 20); // In device pixel coordinates (middle of buffer)
|
||||
QCursor cursor(pixmap, hotspot.x(), hotspot.y());
|
||||
window.setCursor(cursor);
|
||||
|
||||
window.show();
|
||||
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
||||
|
||||
exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); });
|
||||
QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
|
||||
QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()->m_committed.buffer);
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.buffer->size(), QSize(100, 100));
|
||||
QCOMPOSITOR_COMPARE(pointer()->cursorSurface()->m_committed.bufferScale, 2);
|
||||
// Verify that the hotspot was scaled correctly
|
||||
// Surface size is now 100 / 2 = 50, so the middle should be at 25 in surface coordinates
|
||||
QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(25, 25));
|
||||
}
|
||||
|
||||
#endif // QT_CONFIG(cursor)
|
||||
|
||||
QCOMPOSITOR_TEST_MAIN(tst_seatv4)
|
||||
#include "tst_seatv4.moc"
|
||||
|
@ -258,18 +258,19 @@ uint Pointer::sendEnter(Surface *surface, const QPointF &position)
|
||||
{
|
||||
wl_fixed_t x = wl_fixed_from_double(position.x());
|
||||
wl_fixed_t y = wl_fixed_from_double(position.y());
|
||||
m_enterSerial = m_seat->m_compositor->nextSerial();
|
||||
|
||||
uint serial = m_seat->m_compositor->nextSerial();
|
||||
m_enterSerials << serial;
|
||||
|
||||
wl_client *client = surface->resource()->client();
|
||||
const auto pointerResources = resourceMap().values(client);
|
||||
for (auto *r : pointerResources)
|
||||
wl_pointer::send_enter(r->handle, m_enterSerial, surface->resource()->handle, x ,y);
|
||||
return m_enterSerial;
|
||||
wl_pointer::send_enter(r->handle, serial, surface->resource()->handle, x ,y);
|
||||
return serial;
|
||||
}
|
||||
|
||||
uint Pointer::sendLeave(Surface *surface)
|
||||
{
|
||||
m_enterSerial = 0;
|
||||
uint serial = m_seat->m_compositor->nextSerial();
|
||||
|
||||
wl_client *client = surface->resource()->client();
|
||||
@ -315,8 +316,6 @@ void Pointer::sendAxis(wl_client *client, axis axis, qreal value)
|
||||
void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y)
|
||||
{
|
||||
Q_UNUSED(resource);
|
||||
Q_UNUSED(hotspot_x);
|
||||
Q_UNUSED(hotspot_y);
|
||||
auto *s = fromResource<Surface>(surface);
|
||||
QVERIFY(s);
|
||||
|
||||
@ -328,7 +327,14 @@ void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resourc
|
||||
m_cursorRole = new CursorRole(s); //TODO: make sure we don't leak CursorRole
|
||||
s->m_role = m_cursorRole;
|
||||
}
|
||||
// QCOMPARE(serial, m_enterSerial); //TODO: uncomment when this bug is fixed
|
||||
|
||||
// Directly checking the last serial would be racy, we may just have sent leaves/enters which
|
||||
// the client hasn't yet seen. Instead just check if the serial matches an enter serial since
|
||||
// the last time the client sent a set_cursor request.
|
||||
QVERIFY(m_enterSerials.contains(serial));
|
||||
while (m_enterSerials.first() < serial) { m_enterSerials.removeFirst(); }
|
||||
|
||||
m_hotspot = QPoint(hotspot_x, hotspot_y);
|
||||
emit setCursor(serial);
|
||||
}
|
||||
|
||||
|
@ -284,7 +284,8 @@ public:
|
||||
void sendAxis(wl_client *client, axis axis, qreal value);
|
||||
|
||||
Seat *m_seat = nullptr;
|
||||
uint m_enterSerial = 0;
|
||||
QVector<uint> m_enterSerials;
|
||||
QPoint m_hotspot;
|
||||
|
||||
signals:
|
||||
void setCursor(uint serial); //TODO: add arguments?
|
||||
|
Loading…
x
Reference in New Issue
Block a user