| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWaylandCompositor module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #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(); |
| for (auto resource : resourceMap().values(client)) |
| 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()); |
| for (auto resource : resourceMap().values(enteredSurface->waylandClient())) |
| 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()); |
| for (auto resource : resourceMap().values(surface->waylandClient())) |
| 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(); |
| for (auto resource : resourceMap().values(enteredSurface->waylandClient())) |
| send_leave(resource->handle, serial, enteredSurface->resource()); |
| enteredSurface = nullptr; |
| localPosition = QPointF(); |
| enteredSurfaceDestroyListener.reset(); |
| } |
| |
| 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(resource); |
| Q_UNUSED(serial); |
| |
| if (!surface) { |
| seat->cursorSurfaceRequest(nullptr, 0, 0); |
| 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->cursorSurfaceRequest(s, hotspot_x, hotspot_y); |
| } |
| } |
| |
| /*! |
| * \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; |
| |
| for (auto resource : d->resourceMap().values(d->enteredSurface->waylandClient())) |
| 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; |
| } |
| |
| /*! |
| * 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 |