blob: 38cb5d7f2a9c23e75473252234d089a1fff26586 [file] [log] [blame]
/****************************************************************************
**
** 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