/* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-FileCopyrightText: 2020 Adrien Faveraux SPDX-FileCopyrightText: 2021 Vlad Zahorodnii SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "pointer_interface.h" #include "clientconnection.h" #include "display.h" #include "logging.h" #include "pointer_interface_p.h" #include "pointergestures_v1_interface_p.h" #include "relativepointer_v1_interface_p.h" #include "seat_interface.h" #include "surface_interface.h" #include "surfacerole_p.h" #include "utils.h" namespace KWaylandServer { class CursorPrivate { public: CursorPrivate(Cursor *q, PointerInterface *pointer); Cursor *q; PointerInterface *pointer; quint32 enteredSerial = 0; QPoint hotspot; QPointer surface; void update(SurfaceInterface *surface, quint32 serial, const QPoint &hotspot); }; PointerInterfacePrivate *PointerInterfacePrivate::get(PointerInterface *pointer) { return pointer->d.data(); } PointerInterfacePrivate::PointerInterfacePrivate(PointerInterface *q, SeatInterface *seat) : q(q) , seat(seat) , relativePointersV1(new RelativePointerV1Interface(q)) , swipeGesturesV1(new PointerSwipeGestureV1Interface(q)) , pinchGesturesV1(new PointerPinchGestureV1Interface(q)) { } PointerInterfacePrivate::~PointerInterfacePrivate() { } QList PointerInterfacePrivate::pointersForClient(ClientConnection *client) const { return resourceMap().values(client->client()); } void PointerInterfacePrivate::pointer_set_cursor(Resource *resource, uint32_t serial, ::wl_resource *surface_resource, int32_t hotspot_x, int32_t hotspot_y) { SurfaceInterface *surface = nullptr; if (!focusedSurface) { return; } if (focusedSurface->client()->client() != resource->client()) { qCDebug(KWAYLAND_SERVER, "Denied set_cursor request from unfocused client"); return; } if (surface_resource) { surface = SurfaceInterface::get(surface_resource); if (!surface) { wl_resource_post_error(resource->handle, WL_DISPLAY_ERROR_INVALID_OBJECT, "invalid surface"); return; } const SurfaceRole *surfaceRole = SurfaceRole::get(surface); if (surfaceRole) { wl_resource_post_error(resource->handle, error_role, "the wl_surface already has a role assigned %s", surfaceRole->name().constData()); return; } } if (!cursor) { // TODO: Assign the cursor surface role. cursor = new Cursor(q); cursor->d->update(surface, serial, QPoint(hotspot_x, hotspot_y)); QObject::connect(cursor, &Cursor::changed, q, &PointerInterface::cursorChanged); emit q->cursorChanged(); } else { cursor->d->update(surface, serial, QPoint(hotspot_x, hotspot_y)); } } void PointerInterfacePrivate::pointer_release(Resource *resource) { wl_resource_destroy(resource->handle); } void PointerInterfacePrivate::pointer_bind_resource(Resource *resource) { const ClientConnection *focusedClient = focusedSurface ? focusedSurface->client() : nullptr; if (focusedClient && focusedClient->client() == resource->client()) { const quint32 serial = seat->display()->nextSerial(); send_enter(resource->handle, serial, focusedSurface->resource(), wl_fixed_from_double(lastPosition.x()), wl_fixed_from_double(lastPosition.y())); if (resource->version() >= WL_POINTER_FRAME_SINCE_VERSION) { send_frame(resource->handle); } } } void PointerInterfacePrivate::sendLeave(quint32 serial) { const QList pointerResources = pointersForClient(focusedSurface->client()); for (Resource *resource : pointerResources) { send_leave(resource->handle, serial, focusedSurface->resource()); } } void PointerInterfacePrivate::sendEnter(const QPointF &position, quint32 serial) { const QList pointerResources = pointersForClient(focusedSurface->client()); for (Resource *resource : pointerResources) { send_enter(resource->handle, serial, focusedSurface->resource(), wl_fixed_from_double(position.x()), wl_fixed_from_double(position.y())); } } void PointerInterfacePrivate::sendFrame() { const QList pointerResources = pointersForClient(focusedSurface->client()); for (Resource *resource : pointerResources) { if (resource->version() >= WL_POINTER_FRAME_SINCE_VERSION) { send_frame(resource->handle); } } } PointerInterface::PointerInterface(SeatInterface *seat) : d(new PointerInterfacePrivate(this, seat)) { } PointerInterface::~PointerInterface() { } SurfaceInterface *PointerInterface::focusedSurface() const { return d->focusedSurface; } void PointerInterface::setFocusedSurface(SurfaceInterface *surface, const QPointF &position, quint32 serial) { if (d->focusedSurface == surface) { return; } if (d->focusedSurface) { d->sendLeave(serial); if (!surface || d->focusedSurface->client() != surface->client()) { d->sendFrame(); } disconnect(d->destroyConnection); } d->focusedSurface = surface; if (d->focusedSurface) { d->destroyConnection = connect(d->focusedSurface, &SurfaceInterface::aboutToBeDestroyed, this, [this]() { d->sendLeave(d->seat->display()->nextSerial()); d->sendFrame(); d->focusedSurface = nullptr; emit focusedSurfaceChanged(); }); d->sendEnter(position, serial); d->sendFrame(); d->lastPosition = position; } emit focusedSurfaceChanged(); } void PointerInterface::sendButton(quint32 button, PointerButtonState state, quint32 serial) { if (!d->focusedSurface) { return; } const auto pointerResources = d->pointersForClient(d->focusedSurface->client()); for (PointerInterfacePrivate::Resource *resource : pointerResources) { d->send_button(resource->handle, serial, d->seat->timestamp(), button, quint32(state)); } } void PointerInterface::sendAxis(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source) { if (!d->focusedSurface) { return; } const auto pointerResources = d->pointersForClient(d->focusedSurface->client()); for (PointerInterfacePrivate::Resource *resource : pointerResources) { const quint32 version = resource->version(); const auto wlOrientation = (orientation == Qt::Vertical) ? PointerInterfacePrivate::axis_vertical_scroll : PointerInterfacePrivate::axis_horizontal_scroll; if (source != PointerAxisSource::Unknown && version >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) { PointerInterfacePrivate::axis_source wlSource; switch (source) { case PointerAxisSource::Wheel: wlSource = PointerInterfacePrivate::axis_source_wheel; break; case PointerAxisSource::Finger: wlSource = PointerInterfacePrivate::axis_source_finger; break; case PointerAxisSource::Continuous: wlSource = PointerInterfacePrivate::axis_source_continuous; break; case PointerAxisSource::WheelTilt: wlSource = PointerInterfacePrivate::axis_source_wheel_tilt; break; default: Q_UNREACHABLE(); break; } d->send_axis_source(resource->handle, wlSource); } if (delta != 0.0) { if (discreteDelta && version >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) { d->send_axis_discrete(resource->handle, wlOrientation, discreteDelta); } d->send_axis(resource->handle, d->seat->timestamp(), wlOrientation, wl_fixed_from_double(delta)); } else if (version >= WL_POINTER_AXIS_STOP_SINCE_VERSION) { d->send_axis_stop(resource->handle, d->seat->timestamp(), wlOrientation); } } } void PointerInterface::sendMotion(const QPointF &position) { d->lastPosition = position; if (!d->focusedSurface) { return; } const auto pointerResources = d->pointersForClient(d->focusedSurface->client()); for (PointerInterfacePrivate::Resource *resource : pointerResources) { d->send_motion(resource->handle, d->seat->timestamp(), wl_fixed_from_double(position.x()), wl_fixed_from_double(position.y())); } } void PointerInterface::sendFrame() { if (d->focusedSurface) { d->sendFrame(); } } Cursor *PointerInterface::cursor() const { return d->cursor; } SeatInterface *PointerInterface::seat() const { return d->seat; } PointerInterface *PointerInterface::get(wl_resource *native) { if (PointerInterfacePrivate *pointerPrivate = resource_cast(native)) { return pointerPrivate->q; } return nullptr; } CursorPrivate::CursorPrivate(Cursor *q, PointerInterface *pointer) : q(q) , pointer(pointer) { } void CursorPrivate::update(SurfaceInterface *s, quint32 serial, const QPoint &p) { bool emitChanged = false; if (enteredSerial != serial) { enteredSerial = serial; emitChanged = true; emit q->enteredSerialChanged(); } if (hotspot != p) { hotspot = p; emitChanged = true; emit q->hotspotChanged(); } if (surface != s) { if (!surface.isNull()) { QObject::disconnect(surface.data(), &SurfaceInterface::damaged, q, &Cursor::changed); } surface = s; if (!surface.isNull()) { QObject::connect(surface.data(), &SurfaceInterface::damaged, q, &Cursor::changed); } emitChanged = true; emit q->surfaceChanged(); } if (emitChanged) { emit q->changed(); } } Cursor::Cursor(PointerInterface *parent) : QObject(parent) , d(new CursorPrivate(this, parent)) { } Cursor::~Cursor() { } quint32 Cursor::enteredSerial() const { return d->enteredSerial; } QPoint Cursor::hotspot() const { return d->hotspot; } PointerInterface *Cursor::pointer() const { return d->pointer; } SurfaceInterface *Cursor::surface() const { return d->surface; } } // namespace KWaylandServer