Revision control

++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:critical reason:network-protocol
++
++ #include "qwaylandpointer.h"
++ #include "qwaylandpointer_p.h"
++ #include <QtWaylandCompositor/QWaylandClient>
++ #include <QtWaylandCompositor/QWaylandCompositor>
++
++ QT_BEGIN_NAMESPACE
++
++ QWaylandSurfaceRole QWaylandPointerPrivate::s_role("wl_pointer");
++
++ QWaylandPointerPrivate::QWaylandPointerPrivate(QWaylandPointer *pointer, QWaylandSeat *seat)
++ : seat(seat)
++ {
++ Q_UNUSED(pointer);
++ }
++
++ uint QWaylandPointerPrivate::sendButton(Qt::MouseButton button, uint32_t state)
++ {
++ Q_Q(QWaylandPointer);
++ if (!q->mouseFocus() || !q->mouseFocus()->surface())
++ return 0;
++
++ wl_client *client = q->mouseFocus()->surface()->waylandClient();
++ uint32_t time = compositor()->currentTimeMsecs();
++ uint32_t serial = compositor()->nextSerial();
++ const auto resources = resourceMap().values(client);
++ for (auto resource : resources)
++ send_button(resource->handle, serial, time, q->toWaylandButton(button), state);
++ return serial;
++ }
++
++ void QWaylandPointerPrivate::sendMotion()
++ {
++ Q_ASSERT(enteredSurface);
++ uint32_t time = compositor()->currentTimeMsecs();
++ wl_fixed_t x = wl_fixed_from_double(localPosition.x());
++ wl_fixed_t y = wl_fixed_from_double(localPosition.y());
++ const auto resources = resourceMap().values(enteredSurface->waylandClient());
++ for (auto resource : resources)
++ wl_pointer_send_motion(resource->handle, time, x, y);
++ }
++
++ void QWaylandPointerPrivate::sendEnter(QWaylandSurface *surface)
++ {
++ Q_ASSERT(surface && !enteredSurface);
++ enterSerial = compositor()->nextSerial();
++
++ QWaylandKeyboard *keyboard = seat->keyboard();
++ if (keyboard)
++ keyboard->sendKeyModifiers(surface->client(), enterSerial);
++
++ wl_fixed_t x = wl_fixed_from_double(localPosition.x());
++ wl_fixed_t y = wl_fixed_from_double(localPosition.y());
++ const auto resources = resourceMap().values(surface->waylandClient());
++ for (auto resource : resources)
++ send_enter(resource->handle, enterSerial, surface->resource(), x, y);
++
++ enteredSurface = surface;
++ enteredSurfaceDestroyListener.listenForDestruction(surface->resource());
++ }
++
++ void QWaylandPointerPrivate::sendLeave()
++ {
++ Q_ASSERT(enteredSurface);
++ uint32_t serial = compositor()->nextSerial();
++ const auto resources = resourceMap().values(enteredSurface->waylandClient());
++ for (auto resource : resources)
++ send_leave(resource->handle, serial, enteredSurface->resource());
++ localPosition = QPointF();
++ enteredSurfaceDestroyListener.reset();
++ enteredSurface = nullptr;
++ }
++
++ void QWaylandPointerPrivate::ensureEntered(QWaylandSurface *surface)
++ {
++ if (enteredSurface == surface)
++ return;
++
++ if (enteredSurface)
++ sendLeave();
++
++ if (surface)
++ sendEnter(surface);
++ }
++
++ void QWaylandPointerPrivate::pointer_release(wl_pointer::Resource *resource)
++ {
++ wl_resource_destroy(resource->handle);
++ }
++
++ void QWaylandPointerPrivate::pointer_set_cursor(wl_pointer::Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y)
++ {
++ Q_UNUSED(serial);
++
++ if (!surface) {
++ seat->cursorSurfaceRequested(nullptr, 0, 0, QWaylandClient::fromWlClient(compositor(), resource->client()));
++ return;
++ }
++
++ QWaylandSurface *s = QWaylandSurface::fromResource(surface);
++ // XXX FIXME
++ // The role concept was formalized in wayland 1.7, so that release adds one error
++ // code for each interface that implements a role, and we are supposed to pass here
++ // the newly constructed resource and the correct error code so that if setting the
++ // role fails, a proper error can be sent to the client.
++ // However we're still using wayland 1.4, which doesn't have interface specific role
++ // errors, so the best we can do is to use wl_display's object_id error.
++ wl_resource *displayRes = wl_client_get_object(resource->client(), 1);
++ if (s->setRole(&QWaylandPointerPrivate::s_role, displayRes, WL_DISPLAY_ERROR_INVALID_OBJECT)) {
++ s->markAsCursorSurface(true);
++ seat->cursorSurfaceRequested(s, hotspot_x, hotspot_y, QWaylandClient::fromWlClient(compositor(), resource->client()));
++ }
++ }
++
++ /*!
++ * \class QWaylandPointer
++ * \inmodule QtWaylandCompositor
++ * \since 5.8
++ * \brief The QWaylandPointer class represents a pointer device.
++ *
++ * This class provides access to the pointer device in a QWaylandSeat. It corresponds to
++ * the Wayland interface wl_pointer.
++ */
++
++ /*!
++ * Constructs a QWaylandPointer for the given \a seat and with the given \a parent.
++ */
++ QWaylandPointer::QWaylandPointer(QWaylandSeat *seat, QObject *parent)
++ : QWaylandObject(* new QWaylandPointerPrivate(this, seat), parent)
++ {
++ connect(&d_func()->enteredSurfaceDestroyListener, &QWaylandDestroyListener::fired, this, &QWaylandPointer::enteredSurfaceDestroyed);
++ connect(seat, &QWaylandSeat::mouseFocusChanged, this, &QWaylandPointer::pointerFocusChanged);
++ }
++
++ /*!
++ * Returns the input device for this QWaylandPointer.
++ */
++ QWaylandSeat *QWaylandPointer::seat() const
++ {
++ Q_D(const QWaylandPointer);
++ return d->seat;
++ }
++
++ /*!
++ * Returns the compositor for this QWaylandPointer.
++ */
++ QWaylandCompositor *QWaylandPointer::compositor() const
++ {
++ Q_D(const QWaylandPointer);
++ return d->compositor();
++ }
++
++ /*!
++ * Returns the output for this QWaylandPointer.
++ */
++ QWaylandOutput *QWaylandPointer::output() const
++ {
++ Q_D(const QWaylandPointer);
++ return d->output;
++ }
++
++ /*!
++ * Sets the output for this QWaylandPointer to \a output.
++ */
++ void QWaylandPointer::setOutput(QWaylandOutput *output)
++ {
++ Q_D(QWaylandPointer);
++ if (d->output == output) return;
++ d->output = output;
++ outputChanged();
++ }
++
++ /*!
++ * Sends a mouse press event for \a button to the view currently holding mouse focus.
++ *
++ * Returns the serial number of the press event.
++ */
++ uint QWaylandPointer::sendMousePressEvent(Qt::MouseButton button)
++ {
++ Q_D(QWaylandPointer);
++ d->buttonCount++;
++
++ if (d->buttonCount == 1)
++ emit buttonPressedChanged();
++
++ return d->sendButton(button, WL_POINTER_BUTTON_STATE_PRESSED);
++ }
++
++ /*!
++ * Sends a mouse release event for \a button to the view currently holding mouse focus.
++ *
++ * Returns the serial number of the release event.
++ */
++ uint QWaylandPointer::sendMouseReleaseEvent(Qt::MouseButton button)
++ {
++ Q_D(QWaylandPointer);
++ d->buttonCount--;
++
++ if (d->buttonCount == 0)
++ emit buttonPressedChanged();
++
++ return d->sendButton(button, WL_POINTER_BUTTON_STATE_RELEASED);
++ }
++
++ /*!
++ * Sets the current mouse focus to \a view and sends a mouse move event to it with the
++ * local position \a localPos in surface coordinates and output space position \a outputSpacePos.
++ */
++ void QWaylandPointer::sendMouseMoveEvent(QWaylandView *view, const QPointF &localPos, const QPointF &outputSpacePos)
++ {
++ Q_D(QWaylandPointer);
++ if (view && (!view->surface() || view->surface()->isCursorSurface()))
++ view = nullptr;
++ d->seat->setMouseFocus(view);
++ d->localPosition = localPos;
++ d->spacePosition = outputSpacePos;
++
++ if (view) {
++ // We adjust if the mouse position is on the edge
++ // to work around Qt's event propagation
++ QSizeF size(view->surface()->destinationSize());
++ if (d->localPosition.x() == size.width())
++ d->localPosition.rx() -= 0.01;
++ if (d->localPosition.y() == size.height())
++ d->localPosition.ry() -= 0.01;
++
++ d->ensureEntered(view->surface());
++ d->sendMotion();
++
++ if (view->output())
++ setOutput(view->output());
++ }
++ }
++
++ /*!
++ * Sends a mouse wheel event with the given \a orientation and \a delta to the view that currently holds mouse focus.
++ */
++ void QWaylandPointer::sendMouseWheelEvent(Qt::Orientation orientation, int delta)
++ {
++ Q_D(QWaylandPointer);
++ if (!d->enteredSurface)
++ return;
++
++ uint32_t time = d->compositor()->currentTimeMsecs();
++ uint32_t axis = orientation == Qt::Horizontal ? WL_POINTER_AXIS_HORIZONTAL_SCROLL
++ : WL_POINTER_AXIS_VERTICAL_SCROLL;
++
++ const auto resources = d->resourceMap().values(d->enteredSurface->waylandClient());
++ for (auto resource : resources)
++ d->send_axis(resource->handle, time, axis, wl_fixed_from_int(-delta / 12));
++ }
++
++ /*!
++ * Returns the view that currently holds mouse focus.
++ */
++ QWaylandView *QWaylandPointer::mouseFocus() const
++ {
++ Q_D(const QWaylandPointer);
++ return d->seat->mouseFocus();
++ }
++
++ /*!
++ * Returns the current local position of the QWaylandPointer in surface coordinates.
++ */
++ QPointF QWaylandPointer::currentLocalPosition() const
++ {
++ Q_D(const QWaylandPointer);
++ return d->localPosition;
++ }
++
++ /*!
++ * Returns the current output space position of the QWaylandPointer.
++ */
++ QPointF QWaylandPointer::currentSpacePosition() const
++ {
++ Q_D(const QWaylandPointer);
++ return d->spacePosition;
++ }
++
++ /*!
++ * \property QWaylandPointer::isButtonPressed
++ *
++ * This property holds whether any mouse button is currently pressed.
++ *
++ * The value is \c true if at least one mouse button is pressed; otherwise
++ * \c false.
++ */
++
++ /*!
++ * Returns true if any button is currently pressed. Otherwise returns false.
++ */
++ bool QWaylandPointer::isButtonPressed() const
++ {
++ Q_D(const QWaylandPointer);
++ return d->buttonCount > 0;
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandPointer::addClient(QWaylandClient *client, uint32_t id, uint32_t version)
++ {
++ Q_D(QWaylandPointer);
++ wl_resource *resource = d->add(client->client(), id, qMin<uint32_t>(QtWaylandServer::wl_pointer::interfaceVersion(), version))->handle;
++ if (d->enteredSurface && client == d->enteredSurface->client()) {
++ d->send_enter(resource, d->enterSerial, d->enteredSurface->resource(),
++ wl_fixed_from_double(d->localPosition.x()),
++ wl_fixed_from_double(d->localPosition.y()));
++ }
++ }
++
++ /*!
++ * Returns a Wayland resource for this QWaylandPointer.
++ *
++ * This API doesn't actually make sense, since there may be many pointer resources per client
++ * It's here for compatibility reasons.
++ */
++ struct wl_resource *QWaylandPointer::focusResource() const
++ {
++ Q_D(const QWaylandPointer);
++ QWaylandView *focus = d->seat->mouseFocus();
++ if (!focus)
++ return nullptr;
++
++ // Just return the first resource we can find.
++ return d->resourceMap().value(focus->surface()->waylandClient())->handle;
++ }
++
++ /*!
++ * \internal
++ */
++ uint QWaylandPointer::sendButton(struct wl_resource *resource, uint32_t time, Qt::MouseButton button, uint32_t state)
++ {
++ // This method is here for compatibility reasons only, since it usually doesn't make sense to
++ // send button events to just one of the pointer resources for a client.
++ Q_D(QWaylandPointer);
++ uint32_t serial = d->compositor()->nextSerial();
++ d->send_button(resource, serial, time, toWaylandButton(button), state);
++ return serial;
++ }
++
++ /*!
++ * \internal
++ */
++ uint32_t QWaylandPointer::toWaylandButton(Qt::MouseButton button)
++ {
++ #ifndef BTN_LEFT
++ uint32_t BTN_LEFT = 0x110;
++ #endif
++ // the range of valid buttons (evdev module) is from 0x110
++ // through 0x11f. 0x120 is the first 'Joystick' button.
++ switch (button) {
++ case Qt::LeftButton: return BTN_LEFT;
++ case Qt::RightButton: return uint32_t(0x111);
++ case Qt::MiddleButton: return uint32_t(0x112);
++ case Qt::ExtraButton1: return uint32_t(0x113); // AKA Qt::BackButton, Qt::XButton1
++ case Qt::ExtraButton2: return uint32_t(0x114); // AKA Qt::ForwardButton, Qt::XButton2
++ case Qt::ExtraButton3: return uint32_t(0x115);
++ case Qt::ExtraButton4: return uint32_t(0x116);
++ case Qt::ExtraButton5: return uint32_t(0x117);
++ case Qt::ExtraButton6: return uint32_t(0x118);
++ case Qt::ExtraButton7: return uint32_t(0x119);
++ case Qt::ExtraButton8: return uint32_t(0x11a);
++ case Qt::ExtraButton9: return uint32_t(0x11b);
++ case Qt::ExtraButton10: return uint32_t(0x11c);
++ case Qt::ExtraButton11: return uint32_t(0x11d);
++ case Qt::ExtraButton12: return uint32_t(0x11e);
++ case Qt::ExtraButton13: return uint32_t(0x11f);
++ // default should not occur; but if it does, then return Wayland's highest possible button number.
++ default: return uint32_t(0x11f);
++ }
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandPointer::enteredSurfaceDestroyed(void *data)
++ {
++ Q_D(QWaylandPointer);
++ Q_UNUSED(data);
++ d->enteredSurfaceDestroyListener.reset();
++ d->enteredSurface = nullptr;
++
++ d->seat->setMouseFocus(nullptr);
++
++ if (d->buttonCount != 0) {
++ d->buttonCount = 0;
++ emit buttonPressedChanged();
++ }
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandPointer::pointerFocusChanged(QWaylandView *newFocus, QWaylandView *oldFocus)
++ {
++ Q_D(QWaylandPointer);
++ Q_UNUSED(oldFocus);
++ bool wasSameSurface = newFocus && newFocus->surface() == d->enteredSurface;
++ if (d->enteredSurface && !wasSameSurface)
++ d->sendLeave();
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qwaylandpointer.cpp"