diff options
Diffstat (limited to 'src/compositor/compositor_api/qwaylandcompositor.cpp')
-rw-r--r-- | src/compositor/compositor_api/qwaylandcompositor.cpp | 328 |
1 files changed, 210 insertions, 118 deletions
diff --git a/src/compositor/compositor_api/qwaylandcompositor.cpp b/src/compositor/compositor_api/qwaylandcompositor.cpp index e2d617c37..e6f5955b8 100644 --- a/src/compositor/compositor_api/qwaylandcompositor.cpp +++ b/src/compositor/compositor_api/qwaylandcompositor.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> -** 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:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or 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.GPL2 and 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qtwaylandcompositorglobal_p.h" #include "qwaylandcompositor.h" @@ -77,6 +41,7 @@ #include <QtCore/QCoreApplication> #include <QtCore/QStringList> #include <QtCore/QSocketNotifier> +#include <QStandardPaths> #include <QtGui/QDesktopServices> #include <QtGui/QScreen> @@ -98,6 +63,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcWaylandCompositor, "qt.waylandcompositor") Q_LOGGING_CATEGORY(qLcWaylandCompositorHardwareIntegration, "qt.waylandcompositor.hardwareintegration") Q_LOGGING_CATEGORY(qLcWaylandCompositorInputMethods, "qt.waylandcompositor.inputmethods") +Q_LOGGING_CATEGORY(qLcWaylandCompositorTextInput, "qt.waylandcompositor.textinput") namespace QtWayland { @@ -124,14 +90,25 @@ public: QWaylandKeyboardPrivate *keyb = QWaylandKeyboardPrivate::get(seat->keyboard()); +#if defined(Q_OS_QNX) + // The QNX platform plugin delivers scan codes that haven't been adjusted to be + // xkbcommon compatible. xkbcommon requires that the scan codes be bumped up by + // 8 because that's how evdev/XKB deliver scan codes. You might think that it + // would've been better to remove this (odd) requirement from xkbcommon on QNX + // but it turns out that conforming to it has much less impact. + static int offset = QGuiApplication::platformName() == QStringLiteral("qnx") ? 8 : 0; + ke->nativeScanCode += offset; +#endif uint32_t code = ke->nativeScanCode; + if (code == 0) + code = seat->keyboard()->keyToScanCode(ke->key); bool isDown = ke->keyType == QEvent::KeyPress; #if QT_CONFIG(xkbcommon) xkb_state *xkbState = keyb->xkbState(); - Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(xkbState); const xkb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code); + Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(xkbState, sym); int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code); QString text = QXkbCommon::lookupString(xkbState, code); @@ -159,6 +136,9 @@ public: QWaylandCompositorPrivate::QWaylandCompositorPrivate(QWaylandCompositor *compositor) { + // Create XDG_RUNTIME_DIR, if it does not already exist + QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + if (QGuiApplication::platformNativeInterface()) display = static_cast<wl_display*>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("server_wl_display")); @@ -190,8 +170,10 @@ void QWaylandCompositorPrivate::init() const int socketArg = arguments.indexOf(QLatin1String("--wayland-socket-name")); if (socketArg != -1 && socketArg + 1 < arguments.size()) socket_name = arguments.at(socketArg + 1).toLocal8Bit(); + if (socket_name.isEmpty()) + socket_name = qgetenv("WAYLAND_DISPLAY"); } - wl_compositor::init(display, 3); + wl_compositor::init(display, 4); wl_subcompositor::init(display, 1); #if QT_CONFIG(wayland_datadevice) @@ -200,41 +182,44 @@ void QWaylandCompositorPrivate::init() buffer_manager = new QtWayland::BufferManager(q); wl_display_init_shm(display); - const QVector<wl_shm_format> formats = QWaylandSharedMemoryFormatHelper::supportedWaylandFormats(); - for (wl_shm_format format : formats) - wl_display_add_shm_format(display, format); + + for (QWaylandCompositor::ShmFormat format : shmFormats) + wl_display_add_shm_format(display, wl_shm_format(format)); if (!socket_name.isEmpty()) { if (wl_display_add_socket(display, socket_name.constData())) - qFatal("Fatal: Failed to open server socket\n"); + qFatal("Fatal: Failed to open server socket: \"%s\". XDG_RUNTIME_DIR is: \"%s\"\n", socket_name.constData(), getenv("XDG_RUNTIME_DIR")); } else { const char *autoSocketName = wl_display_add_socket_auto(display); if (!autoSocketName) - qFatal("Fatal: Failed to open server socket\n"); + qFatal("Fatal: Failed to open default server socket. XDG_RUNTIME_DIR is: \"%s\"\n", getenv("XDG_RUNTIME_DIR")); socket_name = autoSocketName; emit q->socketNameChanged(socket_name); } -#if WAYLAND_VERSION_MAJOR >= 1 && (WAYLAND_VERSION_MAJOR != 1 || WAYLAND_VERSION_MINOR >= 10) connectToExternalSockets(); -#endif loop = wl_display_get_event_loop(display); int fd = wl_event_loop_get_fd(loop); QSocketNotifier *sockNot = new QSocketNotifier(fd, QSocketNotifier::Read, q); - QObject::connect(sockNot, SIGNAL(activated(int)), q, SLOT(processWaylandEvents())); + QObject::connect(sockNot, SIGNAL(activated(QSocketDescriptor)), q, SLOT(processWaylandEvents())); QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; QObject::connect(dispatcher, SIGNAL(aboutToBlock()), q, SLOT(processWaylandEvents())); + QObject::connect(static_cast<QGuiApplication *>(QGuiApplication::instance()), + &QGuiApplication::applicationStateChanged, + q, + &QWaylandCompositor::applicationStateChanged); + initializeHardwareIntegration(); initializeSeats(); initialized = true; - for (const QPointer<QObject> &object : qExchange(polish_objects, {})) { + for (const QPointer<QObject> &object : std::exchange(polish_objects, {})) { if (object) { QEvent polishEvent(QEvent::Polish); QCoreApplication::sendEvent(object.data(), &polishEvent); @@ -246,18 +231,19 @@ void QWaylandCompositorPrivate::init() QWaylandCompositorPrivate::~QWaylandCompositorPrivate() { - qDeleteAll(clients); + // Take copies, since the lists will get modified as elements are deleted + const auto clientsToDelete = clients; + qDeleteAll(clientsToDelete); - qDeleteAll(outputs); + const auto outputsToDelete = outputs; + qDeleteAll(outputsToDelete); #if QT_CONFIG(wayland_datadevice) delete data_device_manager; #endif -#if QT_CONFIG(opengl) // Some client buffer integrations need to clean up before the destroying the wl_display - client_buffer_integration.reset(); -#endif + qDeleteAll(client_buffer_integrations); if (ownsDisplay) wl_display_destroy(display); @@ -306,17 +292,15 @@ void QWaylandCompositorPrivate::addPolishObject(QObject *object) } } -#if WAYLAND_VERSION_MAJOR >= 1 && (WAYLAND_VERSION_MAJOR != 1 || WAYLAND_VERSION_MINOR >= 10) void QWaylandCompositorPrivate::connectToExternalSockets() { // Clear out any backlog of user-supplied external socket descriptors - for (int fd : qAsConst(externally_added_socket_fds)) { + for (int fd : std::as_const(externally_added_socket_fds)) { if (wl_display_add_socket_fd(display, fd) != 0) qWarning() << "Failed to integrate user-supplied socket fd into the Wayland event loop"; } externally_added_socket_fds.clear(); } -#endif void QWaylandCompositorPrivate::compositor_create_surface(wl_compositor::Resource *resource, uint32_t id) { @@ -365,9 +349,28 @@ QWaylandSurface *QWaylandCompositorPrivate::createDefaultSurface() return new QWaylandSurface(); } +class SharedMemoryClientBufferIntegration : public QtWayland::ClientBufferIntegration +{ +public: + void initializeHardware(wl_display *display) override; + QtWayland::ClientBuffer *createBufferFor(wl_resource *buffer) override; +}; + +void SharedMemoryClientBufferIntegration::initializeHardware(wl_display *) +{ +} + +QtWayland::ClientBuffer *SharedMemoryClientBufferIntegration::createBufferFor(wl_resource *buffer) +{ + if (wl_shm_buffer_get(buffer)) + return new QtWayland::SharedMemoryBuffer(buffer); + return nullptr; +} void QWaylandCompositorPrivate::initializeHardwareIntegration() { + client_buffer_integrations.prepend(new SharedMemoryClientBufferIntegration); // TODO: clean up the opengl dependency + #if QT_CONFIG(opengl) Q_Q(QWaylandCompositor); if (use_hw_integration_extension) @@ -376,16 +379,14 @@ void QWaylandCompositorPrivate::initializeHardwareIntegration() loadClientBufferIntegration(); loadServerBufferIntegration(); - if (client_buffer_integration) - client_buffer_integration->initializeHardware(display); - if (server_buffer_integration) - server_buffer_integration->initializeHardware(q); + for (auto *integration : std::as_const(client_buffer_integrations)) + integration->initializeHardware(display); #endif } void QWaylandCompositorPrivate::initializeSeats() { - for (QWaylandSeat *seat : qAsConst(seats)) + for (QWaylandSeat *seat : std::as_const(seats)) seat->initialize(); } @@ -394,33 +395,47 @@ void QWaylandCompositorPrivate::loadClientBufferIntegration() #if QT_CONFIG(opengl) Q_Q(QWaylandCompositor); QStringList keys = QtWayland::ClientBufferIntegrationFactory::keys(); - QString targetKey; + QStringList targetKeys; QByteArray clientBufferIntegration = qgetenv("QT_WAYLAND_HARDWARE_INTEGRATION"); if (clientBufferIntegration.isEmpty()) clientBufferIntegration = qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION"); - if (keys.contains(QString::fromLocal8Bit(clientBufferIntegration.constData()))) { - targetKey = QString::fromLocal8Bit(clientBufferIntegration.constData()); - } else if (keys.contains(QString::fromLatin1("wayland-egl"))) { - targetKey = QString::fromLatin1("wayland-egl"); - } else if (!keys.isEmpty()) { - targetKey = keys.first(); + + for (auto b : clientBufferIntegration.split(';')) { + QString s = QString::fromLocal8Bit(b); + if (keys.contains(s)) + targetKeys.append(s); } - if (!targetKey.isEmpty()) { - client_buffer_integration.reset(QtWayland::ClientBufferIntegrationFactory::create(targetKey, QStringList())); - if (client_buffer_integration) { - client_buffer_integration->setCompositor(q); - if (hw_integration) - hw_integration->setClientBufferIntegration(targetKey); + if (targetKeys.isEmpty()) { + if (keys.contains(QString::fromLatin1("wayland-egl"))) { + targetKeys.append(QString::fromLatin1("wayland-egl")); + } else if (!keys.isEmpty()) { + targetKeys.append(keys.first()); } } - //BUG: if there is no client buffer integration, bad things will happen when opengl is used + + QString hwIntegrationName; + + for (auto targetKey : std::as_const(targetKeys)) { + auto *integration = QtWayland::ClientBufferIntegrationFactory::create(targetKey, QStringList()); + if (integration) { + integration->setCompositor(q); + client_buffer_integrations.append(integration); + if (hwIntegrationName.isEmpty()) + hwIntegrationName = targetKey; + } + } + + if (hw_integration && !hwIntegrationName.isEmpty()) + hw_integration->setClientBufferIntegrationName(hwIntegrationName); + #endif } void QWaylandCompositorPrivate::loadServerBufferIntegration() { #if QT_CONFIG(opengl) + Q_Q(QWaylandCompositor); QStringList keys = QtWayland::ServerBufferIntegrationFactory::keys(); QString targetKey; QByteArray serverBufferIntegration = qgetenv("QT_WAYLAND_SERVER_BUFFER_INTEGRATION"); @@ -429,14 +444,41 @@ void QWaylandCompositorPrivate::loadServerBufferIntegration() } if (!targetKey.isEmpty()) { server_buffer_integration.reset(QtWayland::ServerBufferIntegrationFactory::create(targetKey, QStringList())); - if (hw_integration) - hw_integration->setServerBufferIntegration(targetKey); + if (server_buffer_integration) { + qCDebug(qLcWaylandCompositorHardwareIntegration) + << "Loaded server buffer integration:" << targetKey; + if (!server_buffer_integration->initializeHardware(q)) { + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to initialize hardware for server buffer integration:" << targetKey; + server_buffer_integration.reset(); + } + } else { + qCWarning(qLcWaylandCompositorHardwareIntegration) + << "Failed to load server buffer integration:" << targetKey; + } } + + if (server_buffer_integration && hw_integration) + hw_integration->setServerBufferIntegrationName(targetKey); #endif } +QWaylandSeat *QWaylandCompositorPrivate::seatFor(QInputEvent *inputEvent) +{ + QWaylandSeat *dev = nullptr; + for (int i = 0; i < seats.size(); i++) { + QWaylandSeat *candidate = seats.at(i); + if (candidate->isOwner(inputEvent)) { + dev = candidate; + break; + } + } + return dev; +} + /*! \qmltype WaylandCompositor + \instantiates QWaylandCompositor \inqmlmodule QtWayland.Compositor \since 5.8 \brief Manages the Wayland display server. @@ -468,35 +510,37 @@ void QWaylandCompositorPrivate::loadServerBufferIntegration() */ /*! - \qmlsignal void QtWaylandCompositor::WaylandCompositor::surfaceRequested(WaylandClient client, int id, int version) + \qmlsignal void QtWayland.Compositor::WaylandCompositor::surfaceRequested(WaylandClient client, int id, int version) - This signal is emitted when a client has created a surface. - The slot connecting to this signal may create and initialize - a WaylandSurface instance in the scope of the slot. - Otherwise a default surface is created. + This signal is emitted when a \a client has created a surface with id \a id. + The interface \a version is also available. + + The slot connecting to this signal may create and initialize a WaylandSurface + instance in the scope of the slot. Otherwise a default surface is created. */ /*! \fn void QWaylandCompositor::surfaceRequested(QWaylandClient *client, uint id, int version) - This signal is emitted when a client has created a surface. - The slot connecting to this signal may create and initialize - a QWaylandSurface instance in the scope of the slot. - Otherwise a default surface is created. + This signal is emitted when a \a client has created a surface with id \a id. + The interface \a version is also available. + + The slot connecting to this signal may create and initialize a QWaylandSurface + instance in the scope of the slot. Otherwise a default surface is created. Connections to this signal must be of Qt::DirectConnection connection type. */ /*! - \qmlsignal void QtWaylandCompositor::WaylandCompositor::surfaceCreated(QWaylandSurface *surface) + \qmlsignal void QtWayland.Compositor::WaylandCompositor::surfaceCreated(WaylandSurface surface) - This signal is emitted when a new WaylandSurface instance has been created. + This signal is emitted when a new WaylandSurface instance \a surface has been created. */ /*! \fn void QWaylandCompositor::surfaceCreated(QWaylandSurface *surface) - This signal is emitted when a new QWaylandSurface instance has been created. + This signal is emitted when a new QWaylandSurface instance \a surface has been created. */ /*! @@ -535,7 +579,7 @@ void QWaylandCompositor::create() } /*! - * \qmlproperty bool QtWaylandCompositor::WaylandCompositor::created + * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::created * * This property is true if WaylandCompositor has been initialized, * otherwise it's false. @@ -554,7 +598,7 @@ bool QWaylandCompositor::isCreated() const } /*! - * \qmlproperty string QtWaylandCompositor::WaylandCompositor::socketName + * \qmlproperty string QtWayland.Compositor::WaylandCompositor::socketName * * This property holds the socket name used by WaylandCompositor to communicate with * clients. It must be set before the component is completed. @@ -597,7 +641,7 @@ QByteArray QWaylandCompositor::socketName() const } /*! - * \qmlmethod QtWaylandCompositor::WaylandCompositor::addSocketDescriptor(fd) + * \qmlmethod QtWayland.Compositor::WaylandCompositor::addSocketDescriptor(fd) * \since 5.12 * * Listen for client connections on a file descriptor, \a fd, referring to a @@ -626,15 +670,10 @@ QByteArray QWaylandCompositor::socketName() const */ void QWaylandCompositor::addSocketDescriptor(int fd) { -#if WAYLAND_VERSION_MAJOR >= 1 && (WAYLAND_VERSION_MAJOR != 1 || WAYLAND_VERSION_MINOR >= 10) Q_D(QWaylandCompositor); d->externally_added_socket_fds.append(fd); if (isCreated()) d->connectToExternalSockets(); -#else - Q_UNUSED(fd); - qWarning() << "QWaylandCompositor::addSocketDescriptor() does nothing on libwayland versions prior to 1.10.0"; -#endif } /*! @@ -665,7 +704,7 @@ QList<QWaylandClient *>QWaylandCompositor::clients() const } /*! - * \qmlmethod QtWaylandCompositor::WaylandCompositor::destroyClientForSurface(surface) + * \qmlmethod QtWayland.Compositor::WaylandCompositor::destroyClientForSurface(surface) * * Destroys the client for the WaylandSurface \a surface. */ @@ -679,7 +718,7 @@ void QWaylandCompositor::destroyClientForSurface(QWaylandSurface *surface) } /*! - * \qmlmethod QtWaylandCompositor::WaylandCompositor::destroyClient(client) + * \qmlmethod QtWayland.Compositor::WaylandCompositor::destroyClient(client) * * Destroys the given WaylandClient \a client. */ @@ -737,7 +776,7 @@ QWaylandOutput *QWaylandCompositor::outputFor(QWindow *window) const } /*! - * \qmlproperty WaylandOutput QtWaylandCompositor::WaylandCompositor::defaultOutput + * \qmlproperty WaylandOutput QtWayland.Compositor::WaylandCompositor::defaultOutput * * This property contains the first in the list of outputs added to the * WaylandCompositor, or null if no outputs have been added. @@ -838,7 +877,7 @@ QWaylandTouch *QWaylandCompositor::createTouchDevice(QWaylandSeat *seat) } /*! - * \qmlproperty bool QtWaylandCompositor::WaylandCompositor::retainedSelection + * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::retainedSelection * * This property holds whether retained selection is enabled. */ @@ -884,7 +923,7 @@ void QWaylandCompositor::overrideSelection(const QMimeData *data) } /*! - * \qmlproperty WaylandSeat QtWaylandCompositor::WaylandCompositor::defaultSeat + * \qmlproperty WaylandSeat QtWayland.Compositor::WaylandCompositor::defaultSeat * * This property contains the default seat for this * WaylandCompositor. @@ -905,27 +944,17 @@ QWaylandSeat *QWaylandCompositor::defaultSeat() const } /*! - * \internal - * - * Currently, Qt only supports a single seat, so this exists for - * future proofing the APIs. + * Select the seat for a given input event \a inputEvent. + * Currently, Qt only supports a single seat. */ QWaylandSeat *QWaylandCompositor::seatFor(QInputEvent *inputEvent) { Q_D(QWaylandCompositor); - QWaylandSeat *dev = nullptr; - for (int i = 0; i < d->seats.size(); i++) { - QWaylandSeat *candidate = d->seats.at(i); - if (candidate->isOwner(inputEvent)) { - dev = candidate; - break; - } - } - return dev; + return d->seatFor(inputEvent); } /*! - * \qmlproperty bool QtWaylandCompositor::WaylandCompositor::useHardwareIntegrationExtension + * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::useHardwareIntegrationExtension * * This property holds whether the hardware integration extension should be enabled for * this WaylandCompositor. @@ -1010,4 +1039,67 @@ void QWaylandCompositor::grabSurface(QWaylandSurfaceGrabber *grabber, const QWay } } +/*! + * \qmlproperty list<enum> QtWayland.Compositor::WaylandCompositor::additionalShmFormats + * + * This property holds the list of additional wl_shm formats advertised as supported by the + * compositor. + * + * By default, only the required ShmFormat_ARGB8888 and ShmFormat_XRGB8888 are listed and this + * list will empty. Additional formats may require conversion internally and can thus affect + * performance. + * + * This property must be set before the compositor component is completed. Subsequent changes + * will have no effect. + * + * \since 6.0 + */ + +/*! + * \property QWaylandCompositor::additionalShmFormats + * + * This property holds the list of additional wl_shm formats advertised as supported by the + * compositor. + * + * By default, only the required ShmFormat_ARGB8888 and ShmFormat_XRGB8888 are listed and this + * list will empty. + * + * This property must be set before the compositor is \l{create()}{created}. Subsequent changes + * will have no effect. + * + * \since 6.0 + */ +void QWaylandCompositor::setAdditionalShmFormats(const QVector<ShmFormat> &additionalShmFormats) +{ + Q_D(QWaylandCompositor); + if (d->initialized) + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Setting QWaylandCompositor::additionalShmFormats after initialization has no effect"; + + d->shmFormats = additionalShmFormats; + emit additionalShmFormatsChanged(); +} + +QVector<QWaylandCompositor::ShmFormat> QWaylandCompositor::additionalShmFormats() const +{ + Q_D(const QWaylandCompositor); + return d->shmFormats; +} + +void QWaylandCompositor::applicationStateChanged(Qt::ApplicationState state) +{ +#if QT_CONFIG(xkbcommon) + if (state == Qt::ApplicationInactive) { + auto *seat = defaultSeat(); + if (seat != nullptr) { + QWaylandKeyboardPrivate *keyb = QWaylandKeyboardPrivate::get(seat->keyboard()); + keyb->resetKeyboardState(); + } + } +#else + Q_UNUSED(state); +#endif +} + QT_END_NAMESPACE + +#include "moc_qwaylandcompositor.cpp" |