Revision control

++ // 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
++ // Qt-Security score:critical reason:network-protocol
++
++ #include "qtwaylandcompositorglobal_p.h"
++ #include "qwaylandcompositor.h"
++ #include "qwaylandcompositor_p.h"
++
++ #include <QtWaylandCompositor/qwaylandclient.h>
++ #include <QtWaylandCompositor/qwaylandseat.h>
++ #include <QtWaylandCompositor/qwaylandoutput.h>
++ #include <QtWaylandCompositor/qwaylandview.h>
++ #include <QtWaylandCompositor/qwaylandclient.h>
++ #include <QtWaylandCompositor/qwaylandkeyboard.h>
++ #include <QtWaylandCompositor/qwaylandpointer.h>
++ #include <QtWaylandCompositor/qwaylandtouch.h>
++ #include <QtWaylandCompositor/qwaylandsurfacegrabber.h>
++
++ #include <QtWaylandCompositor/private/qwaylandkeyboard_p.h>
++ #include <QtWaylandCompositor/private/qwaylandsurface_p.h>
++
++ #if QT_CONFIG(wayland_datadevice)
++ #include "wayland_wrapper/qwldatadevice_p.h"
++ #include "wayland_wrapper/qwldatadevicemanager_p.h"
++ #endif
++ #include "wayland_wrapper/qwlbuffermanager_p.h"
++
++ #include "hardware_integration/qwlclientbufferintegration_p.h"
++ #include "hardware_integration/qwlclientbufferintegrationfactory_p.h"
++ #include "hardware_integration/qwlserverbufferintegration_p.h"
++ #include "hardware_integration/qwlserverbufferintegrationfactory_p.h"
++
++ #if QT_CONFIG(opengl)
++ #include "hardware_integration/qwlhwintegration_p.h"
++ #endif
++
++ #include "extensions/qwaylandqtwindowmanager.h"
++
++ #include "qwaylandsharedmemoryformathelper_p.h"
++
++ #include <QtCore/QCoreApplication>
++ #include <QtCore/QStringList>
++ #include <QtCore/QSocketNotifier>
++ #include <QStandardPaths>
++
++ #include <QtGui/QDesktopServices>
++ #include <QtGui/QScreen>
++
++ #include <QtGui/qpa/qwindowsysteminterface_p.h>
++ #include <QtGui/qpa/qplatformnativeinterface.h>
++ #include <QtGui/private/qguiapplication_p.h>
++
++ #if QT_CONFIG(opengl)
++ # include <QOpenGLTextureBlitter>
++ # include <QOpenGLTexture>
++ # include <QOpenGLContext>
++ # include <QOpenGLFramebufferObject>
++ # include <QMatrix4x4>
++ #endif
++
++ QT_BEGIN_NAMESPACE
++
++ // These logging categories are public symbols. To remain binary-compatible we cannot hide them in
++ // a namespace like we do for all Qt-internal logging categories.
++ #define Q_PUBLIC_LOGGING_CATEGORY(name, ...) \
++ const QLoggingCategory &name() \
++ { \
++ static const QLoggingCategory category(__VA_ARGS__); \
++ return category; \
++ }
++
++ Q_PUBLIC_LOGGING_CATEGORY(qLcWaylandCompositor, "qt.waylandcompositor")
++ Q_PUBLIC_LOGGING_CATEGORY(qLcWaylandCompositorHardwareIntegration, "qt.waylandcompositor.hardwareintegration")
++ Q_PUBLIC_LOGGING_CATEGORY(qLcWaylandCompositorInputMethods, "qt.waylandcompositor.inputmethods")
++ Q_PUBLIC_LOGGING_CATEGORY(qLcWaylandCompositorTextInput, "qt.waylandcompositor.textinput")
++
++ namespace QtWayland {
++
++ class WindowSystemEventHandler : public QWindowSystemEventHandler
++ {
++ public:
++ WindowSystemEventHandler(QWaylandCompositor *c) : compositor(c) {}
++ bool sendEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) override
++ {
++ if (e->type == QWindowSystemInterfacePrivate::Key) {
++ QWindowSystemInterfacePrivate::KeyEvent *keyEvent = static_cast<QWindowSystemInterfacePrivate::KeyEvent *>(e);
++ handleKeyEvent(keyEvent);
++ } else {
++ QWindowSystemEventHandler::sendEvent(e);
++ }
++ return true;
++ }
++
++ void handleKeyEvent(QWindowSystemInterfacePrivate::KeyEvent *ke)
++ {
++ auto *seat = compositor->defaultSeat();
++ if (!seat)
++ return;
++
++ 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();
++
++ 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);
++
++ ke->key = qtkey;
++ ke->modifiers = modifiers;
++ ke->nativeVirtualKey = sym;
++ ke->nativeModifiers = keyb->xkbModsMask();
++ ke->unicode = text;
++ #endif
++ if (!ke->repeat)
++ keyb->keyEvent(code, isDown ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED);
++
++ QWindowSystemEventHandler::sendEvent(ke);
++
++ if (!ke->repeat) {
++ keyb->maybeUpdateKeymap();
++ keyb->updateModifierState(code, isDown ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED);
++ }
++ }
++
++ QWaylandCompositor *compositor = nullptr;
++ };
++
++ } // namespace
++
++ 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"));
++
++ if (!display) {
++ display = wl_display_create();
++ ownsDisplay = true;
++ }
++
++ eventHandler.reset(new QtWayland::WindowSystemEventHandler(compositor));
++ timer.start();
++
++ QWindowSystemInterfacePrivate::installWindowSystemEventHandler(eventHandler.data());
++
++ #if QT_CONFIG(xkbcommon)
++ mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
++ if (!mXkbContext) {
++ qWarning("Failed to create a XKB context: keymap will not be supported");
++ return;
++ }
++ #endif
++ }
++
++ void QWaylandCompositorPrivate::init()
++ {
++ Q_Q(QWaylandCompositor);
++ QStringList arguments = QCoreApplication::instance()->arguments();
++
++ if (socket_name.isEmpty()) {
++ 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, 5);
++ wl_subcompositor::init(display, 1);
++
++ #if QT_CONFIG(wayland_datadevice)
++ data_device_manager = new QtWayland::DataDeviceManager(q);
++ #endif
++ buffer_manager = new QtWayland::BufferManager(q);
++
++ wl_display_init_shm(display);
++
++ for (QWaylandCompositor::ShmFormat format : std::as_const(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: \"%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 default server socket. XDG_RUNTIME_DIR is: \"%s\"\n", getenv("XDG_RUNTIME_DIR"));
++ socket_name = autoSocketName;
++ emit q->socketNameChanged(socket_name);
++ }
++
++ connectToExternalSockets();
++
++ 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(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 : std::exchange(polish_objects, {})) {
++ if (object) {
++ QEvent polishEvent(QEvent::Polish);
++ QCoreApplication::sendEvent(object.data(), &polishEvent);
++ }
++ }
++
++ emit q->createdChanged();
++ }
++
++ QWaylandCompositorPrivate::~QWaylandCompositorPrivate()
++ {
++ // Take copies, since the lists will get modified as elements are deleted
++ const auto clientsToDelete = clients;
++ qDeleteAll(clientsToDelete);
++
++ const auto outputsToDelete = outputs;
++ qDeleteAll(outputsToDelete);
++
++ #if QT_CONFIG(wayland_datadevice)
++ delete data_device_manager;
++ #endif
++
++ // Some client buffer integrations need to clean up before the destroying the wl_display
++ qDeleteAll(client_buffer_integrations);
++
++ if (ownsDisplay)
++ wl_display_destroy(display);
++ }
++
++ void QWaylandCompositorPrivate::preInit()
++ {
++ Q_Q(QWaylandCompositor);
++
++ if (preInitialized)
++ return;
++
++ if (seats.empty())
++ seats.append(q->createSeat());
++
++ preInitialized = true;
++ }
++
++ void QWaylandCompositorPrivate::destroySurface(QWaylandSurface *surface)
++ {
++ Q_Q(QWaylandCompositor);
++ q->surfaceAboutToBeDestroyed(surface);
++
++ delete surface;
++ }
++
++ void QWaylandCompositorPrivate::unregisterSurface(QWaylandSurface *surface)
++ {
++ if (!all_surfaces.removeOne(surface))
++ qWarning("%s Unexpected state. Cant find registered surface\n", Q_FUNC_INFO);
++ }
++
++ void QWaylandCompositorPrivate::feedRetainedSelectionData(QMimeData *data)
++ {
++ Q_Q(QWaylandCompositor);
++ if (retainSelection)
++ q->retainedSelectionReceived(data);
++ }
++
++ void QWaylandCompositorPrivate::addPolishObject(QObject *object)
++ {
++ if (initialized) {
++ QCoreApplication::postEvent(object, new QEvent(QEvent::Polish));
++ } else {
++ polish_objects.push_back(object);
++ }
++ }
++
++ void QWaylandCompositorPrivate::connectToExternalSockets()
++ {
++ // Clear out any backlog of user-supplied external socket descriptors
++ 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();
++ }
++
++ void QWaylandCompositorPrivate::compositor_create_surface(wl_compositor::Resource *resource, uint32_t id)
++ {
++ Q_Q(QWaylandCompositor);
++ QWaylandClient *client = QWaylandClient::fromWlClient(q, resource->client());
++ emit q->surfaceRequested(client, id, resource->version());
++ #ifndef QT_NO_DEBUG
++ Q_ASSERT_X(!QWaylandSurfacePrivate::hasUninitializedSurface(), "QWaylandCompositor", QStringLiteral("Found uninitialized QWaylandSurface after emitting QWaylandCompositor::createSurface for id %1. All surfaces has to be initialized immediately after creation. See QWaylandSurface::initialize.").arg(id).toLocal8Bit().constData());
++ #endif
++ struct wl_resource *surfResource = wl_client_get_object(client->client(), id);
++
++ QWaylandSurface *surface = nullptr;
++ if (surfResource) {
++ surface = QWaylandSurface::fromResource(surfResource);
++ } else {
++ surface = createDefaultSurface();
++ surface->initialize(q, client, id, resource->version());
++ }
++ Q_ASSERT(surface);
++ all_surfaces.append(surface);
++ emit q->surfaceCreated(surface);
++ }
++
++ void QWaylandCompositorPrivate::compositor_create_region(wl_compositor::Resource *resource, uint32_t id)
++ {
++ new QtWayland::Region(resource->client(), id);
++ }
++
++ void QWaylandCompositorPrivate::subcompositor_get_subsurface(wl_subcompositor::Resource *resource, uint32_t id, wl_resource *surface, wl_resource *parent)
++ {
++ Q_Q(QWaylandCompositor);
++ QWaylandSurface *childSurface = QWaylandSurface::fromResource(surface);
++ QWaylandSurface *parentSurface = QWaylandSurface::fromResource(parent);
++
++ QWaylandSurfacePrivate *childSurface_d = QWaylandSurfacePrivate::get(childSurface);
++ QWaylandSurfacePrivate *parentSurface_d = QWaylandSurfacePrivate::get(parentSurface);
++ Q_ASSERT(childSurface_d != nullptr && parentSurface_d != nullptr);
++
++ // Walk the requested parent's existing ancestor chain and reject if it already includes the
++ // child surface.
++ for (QWaylandSurfacePrivate *ancestor = parentSurface_d;
++ ancestor != nullptr;
++ ancestor = ancestor->parentSurface()) {
++ if (ancestor == childSurface_d) {
++ wl_resource_post_error(resource->handle,
++ WL_SUBCOMPOSITOR_ERROR_BAD_PARENT,
++ "surface cannot be made a transitive parent of itself");
++ return;
++ }
++ }
++
++ QWaylandSurfacePrivate::get(childSurface)->initSubsurface(parentSurface, resource->client(), id, 1);
++ QWaylandSurfacePrivate::get(parentSurface)->subsurfaceChildren.append(childSurface);
++ emit q->subsurfaceChanged(childSurface, parentSurface);
++ }
++
++ /*!
++ \internal
++ Used to create a fallback QWaylandSurface when no surface was
++ created by emitting the QWaylandCompositor::createSurface signal.
++ */
++ 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)
++ hw_integration.reset(new QtWayland::HardwareIntegration(q));
++
++ loadClientBufferIntegration();
++ loadServerBufferIntegration();
++
++ for (auto *integration : std::as_const(client_buffer_integrations))
++ integration->initializeHardware(display);
++ #endif
++ }
++
++ void QWaylandCompositorPrivate::initializeSeats()
++ {
++ for (QWaylandSeat *seat : std::as_const(seats))
++ seat->initialize();
++ }
++
++ void QWaylandCompositorPrivate::loadClientBufferIntegration()
++ {
++ #if QT_CONFIG(opengl)
++ Q_Q(QWaylandCompositor);
++ QStringList keys = QtWayland::ClientBufferIntegrationFactory::keys();
++ QStringList targetKeys;
++ QByteArray clientBufferIntegration = qgetenv("QT_WAYLAND_HARDWARE_INTEGRATION");
++ if (clientBufferIntegration.isEmpty())
++ clientBufferIntegration = qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION");
++
++ const QList<QByteArray> clientBufferIntegrations = clientBufferIntegration.split(';');
++ for (const auto &b : clientBufferIntegrations) {
++ QString s = QString::fromLocal8Bit(b);
++ if (keys.contains(s))
++ targetKeys.append(s);
++ }
++
++ if (!clientBufferIntegration.isEmpty() && targetKeys.isEmpty()) {
++ qCWarning(qLcWaylandCompositorHardwareIntegration)
++ << "Failed to find any requested client buffer integration:"
++ << clientBufferIntegration
++ << ", available integrations:"
++ << keys;
++ }
++
++ if (targetKeys.isEmpty()) {
++ if (keys.contains(QString::fromLatin1("wayland-egl"))) {
++ targetKeys.append(QString::fromLatin1("wayland-egl"));
++ } else if (!keys.isEmpty()) {
++ targetKeys.append(keys.first());
++ }
++ }
++
++ QString hwIntegrationName;
++
++ for (const 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");
++ if (keys.contains(QString::fromLocal8Bit(serverBufferIntegration.constData()))) {
++ targetKey = QString::fromLocal8Bit(serverBufferIntegration.constData());
++ }
++ if (!targetKey.isEmpty()) {
++ server_buffer_integration.reset(QtWayland::ServerBufferIntegrationFactory::create(targetKey, QStringList()));
++ 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
++ \nativetype QWaylandCompositor
++ \inqmlmodule QtWayland.Compositor
++ \since 5.8
++ \brief Manages the Wayland display server.
++
++ The WaylandCompositor manages the connections to the clients, as well as the different
++ \l{WaylandOutput}{outputs} and \l{QWaylandSeat}{seats}.
++
++ Normally, a compositor application will have a single WaylandCompositor
++ instance, which can have several outputs as children. When a client
++ requests the compositor to create a surface, the request is handled by
++ the onSurfaceRequested handler.
++
++ Extensions that are supported by the compositor should be instantiated and added to the
++ extensions property or simply as direct children of WaylandCompositor.
++ */
++
++
++ /*!
++ \class QWaylandCompositor
++ \inmodule QtWaylandCompositor
++ \since 5.8
++ \brief The QWaylandCompositor class manages the Wayland display server.
++
++ The QWaylandCompositor manages the connections to the clients, as well as the different \l{QWaylandOutput}{outputs}
++ and \l{QWaylandSeat}{seats}.
++
++ Normally, a compositor application will have a single WaylandCompositor
++ instance, which can have several outputs as children.
++ */
++
++ /*!
++ \qmlsignal void QtWayland.Compositor::WaylandCompositor::surfaceRequested(WaylandClient client, int id, int version)
++
++ 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 \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 QtWayland.Compositor::WaylandCompositor::surfaceCreated(WaylandSurface surface)
++
++ 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 \a surface has been created.
++ */
++
++ /*!
++ * Constructs a QWaylandCompositor with the given \a parent.
++ */
++ QWaylandCompositor::QWaylandCompositor(QObject *parent)
++ : QWaylandObject(*new QWaylandCompositorPrivate(this), parent)
++ {
++ }
++
++ /*!
++ * \internal
++ * Constructs a QWaylandCompositor with the private object \a dptr and \a parent.
++ */
++ QWaylandCompositor::QWaylandCompositor(QWaylandCompositorPrivate &dptr, QObject *parent)
++ : QWaylandObject(dptr, parent)
++ {
++ }
++
++ /*!
++ * Destroys the QWaylandCompositor
++ */
++ QWaylandCompositor::~QWaylandCompositor()
++ {
++ }
++
++ /*!
++ * Initializes the QWaylandCompositor.
++ * If you override this function in your subclass, be sure to call the base class implementation.
++ */
++ void QWaylandCompositor::create()
++ {
++ Q_D(QWaylandCompositor);
++ d->preInit();
++ d->init();
++ }
++
++ /*!
++ * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::created
++ *
++ * This property is true if WaylandCompositor has been initialized,
++ * otherwise it's false.
++ */
++
++ /*!
++ * \property QWaylandCompositor::created
++ *
++ * This property is true if QWaylandCompositor has been initialized,
++ * otherwise it's false.
++ */
++ bool QWaylandCompositor::isCreated() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->initialized;
++ }
++
++ /*!
++ * \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.
++ *
++ * If the socketName is empty (the default), the contents of the start argument
++ * \c --wayland-socket-name are used instead. If the argument is not set, the
++ * compositor tries to find a socket name, which is \c{wayland-0} by default.
++ */
++
++ /*!
++ * \property QWaylandCompositor::socketName
++ *
++ * This property holds the socket name used by QWaylandCompositor to communicate with
++ * clients. This must be set before the QWaylandCompositor is \l{create()}{created}.
++ *
++ * If the socketName is empty (the default), the contents of the start argument
++ * \c --wayland-socket-name are used instead. If the argument is not set, the
++ * compositor tries to find a socket name, which is \c{wayland-0} by default.
++ */
++ void QWaylandCompositor::setSocketName(const QByteArray &name)
++ {
++ Q_D(QWaylandCompositor);
++
++ if (d->socket_name == name)
++ return;
++
++ if (d->initialized) {
++ qWarning("%s: Changing socket name after initializing the compositor is not supported.\n", Q_FUNC_INFO);
++ return;
++ }
++
++ d->socket_name = name;
++ emit socketNameChanged(name);
++ }
++
++ QByteArray QWaylandCompositor::socketName() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->socket_name;
++ }
++
++ /*!
++ * \qmlmethod void QtWayland.Compositor::WaylandCompositor::addSocketDescriptor(fd)
++ * \since 5.12
++ *
++ * Listen for client connections on a file descriptor, \a fd, referring to a
++ * server socket already bound and listening.
++ *
++ * Does not take ownership of the file descriptor; it must be closed
++ * explicitly if needed.
++ *
++ * \note This method is only available with libwayland 1.10.0 or
++ * newer. If built against an earlier libwayland runtime, this
++ * method is a noop.
++ */
++
++ /*!
++ * Listen for client connections on a file descriptor, \a fd, referring to a
++ * server socket already bound and listening.
++ *
++ * Does not take ownership of the file descriptor; it must be closed
++ * explicitly if needed.
++ *
++ * \note This method is only available with libwayland 1.10.0 or
++ * newer. If built against an earlier libwayland runtime, this
++ * method is a noop.
++ *
++ * \since 5.12
++ */
++ void QWaylandCompositor::addSocketDescriptor(int fd)
++ {
++ Q_D(QWaylandCompositor);
++ d->externally_added_socket_fds.append(fd);
++ if (isCreated())
++ d->connectToExternalSockets();
++ }
++
++ /*!
++ * \internal
++ */
++ struct wl_display *QWaylandCompositor::display() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->display;
++ }
++
++ /*!
++ * \internal
++ */
++ uint32_t QWaylandCompositor::nextSerial()
++ {
++ Q_D(QWaylandCompositor);
++ return wl_display_next_serial(d->display);
++ }
++
++ /*!
++ * \internal
++ */
++ QList<QWaylandClient *>QWaylandCompositor::clients() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->clients;
++ }
++
++ /*!
++ * \qmlmethod void QtWayland.Compositor::WaylandCompositor::destroyClientForSurface(surface)
++ *
++ * From the compositor side, destroys the client to which the WaylandSurface \a surface belongs
++ * by disconnecting from it.
++ */
++
++ /*!
++ * Destroys the client for the \a surface.
++ */
++ void QWaylandCompositor::destroyClientForSurface(QWaylandSurface *surface)
++ {
++ destroyClient(surface->client());
++ }
++
++ /*!
++ * \qmlmethod void QtWayland.Compositor::WaylandCompositor::destroyClient(client)
++ *
++ * From the compositor side, destroys the given WaylandClient \a client by disconnecting from it.
++ */
++
++ /*!
++ * Destroys the \a client.
++ */
++ void QWaylandCompositor::destroyClient(QWaylandClient *client)
++ {
++ if (!client)
++ return;
++
++ QWaylandQtWindowManager *wmExtension = QWaylandQtWindowManager::findIn(this);
++ if (wmExtension)
++ wmExtension->sendQuitMessage(client);
++
++ wl_client_destroy(client->client());
++ }
++
++ /*!
++ * \internal
++ */
++ QList<QWaylandSurface *> QWaylandCompositor::surfacesForClient(QWaylandClient* client) const
++ {
++ Q_D(const QWaylandCompositor);
++ QList<QWaylandSurface *> surfs;
++ for (QWaylandSurface *surface : d->all_surfaces) {
++ if (surface->client() == client)
++ surfs.append(surface);
++ }
++ return surfs;
++ }
++
++ /*!
++ * \internal
++ */
++ QList<QWaylandSurface *> QWaylandCompositor::surfaces() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->all_surfaces;
++ }
++
++ /*!
++ * Returns the QWaylandOutput that is connected to the given \a window.
++ */
++ QWaylandOutput *QWaylandCompositor::outputFor(QWindow *window) const
++ {
++ Q_D(const QWaylandCompositor);
++ for (QWaylandOutput *output : d->outputs) {
++ if (output->window() == window)
++ return output;
++ }
++
++ return nullptr;
++ }
++
++ /*!
++ * \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.
++ *
++ * Setting a new default output prepends it to the output list, making
++ * it the new default, but the previous default is not removed from
++ * the list.
++ */
++ /*!
++ * \property QWaylandCompositor::defaultOutput
++ *
++ * This property contains the first in the list of outputs added to the
++ * QWaylandCompositor, or null if no outputs have been added.
++ *
++ * Setting a new default output prepends it to the output list, making
++ * it the new default, but the previous default is not removed from
++ * the list. If the new default output was already in the list of outputs,
++ * it is moved to the beginning of the list.
++ */
++ QWaylandOutput *QWaylandCompositor::defaultOutput() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->defaultOutput();
++ }
++
++ void QWaylandCompositor::setDefaultOutput(QWaylandOutput *output)
++ {
++ Q_D(QWaylandCompositor);
++ if (Q_UNLIKELY(!output)) {
++ qWarning("Changing default output to null is not supported");
++ return;
++ }
++ if (d->outputs.size() && d->outputs.first() == output)
++ return;
++ bool alreadyAdded = d->outputs.removeOne(output);
++ d->outputs.prepend(output);
++ if (!alreadyAdded)
++ emit outputAdded(output);
++ emit defaultOutputChanged();
++ }
++
++ /*!
++ * \internal
++ */
++ QList<QWaylandOutput *> QWaylandCompositor::outputs() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->outputs;
++ }
++
++ /*!
++ * \internal
++ */
++ uint QWaylandCompositor::currentTimeMsecs() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->timer.elapsed();
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandCompositor::processWaylandEvents()
++ {
++ Q_D(QWaylandCompositor);
++ int ret = wl_event_loop_dispatch(d->loop, 0);
++ if (ret)
++ fprintf(stderr, "wl_event_loop_dispatch error: %d\n", ret);
++ wl_display_flush_clients(d->display);
++ }
++
++ /*!
++ * \internal
++ */
++ QWaylandSeat *QWaylandCompositor::createSeat()
++ {
++ return new QWaylandSeat(this);
++ }
++
++ /*!
++ * \internal
++ */
++ QWaylandPointer *QWaylandCompositor::createPointerDevice(QWaylandSeat *seat)
++ {
++ return new QWaylandPointer(seat);
++ }
++
++ /*!
++ * \internal
++ */
++ QWaylandKeyboard *QWaylandCompositor::createKeyboardDevice(QWaylandSeat *seat)
++ {
++ return new QWaylandKeyboard(seat);
++ }
++
++ /*!
++ * \internal
++ */
++ QWaylandTouch *QWaylandCompositor::createTouchDevice(QWaylandSeat *seat)
++ {
++ return new QWaylandTouch(seat);
++ }
++
++ /*!
++ * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::retainedSelection
++ * \default false
++ *
++ * This property controls whether the compositor retains a copy of clipboard selection data.
++ * When \c true, the compositor eagerly reads and caches all clipboard data from the owning client.
++ * This incurs a performance cost.
++ */
++
++ /*!
++ * \property QWaylandCompositor::retainedSelection
++ *
++ * This property controls whether the compositor retains a copy of clipboard selection data.
++ * When \c true, the compositor eagerly reads and caches all clipboard data from the owning client.
++ * This incurs a performance cost. The default is \c false.
++ *
++ * \sa QWaylandCompositor::retainedSelectionReceived(), QWaylandSurface::updateSelection()
++ */
++ void QWaylandCompositor::setRetainedSelectionEnabled(bool enabled)
++ {
++ Q_D(QWaylandCompositor);
++
++ if (d->retainSelection == enabled)
++ return;
++
++ d->retainSelection = enabled;
++ emit retainedSelectionChanged(enabled);
++ }
++
++ bool QWaylandCompositor::retainedSelectionEnabled() const
++ {
++ Q_D(const QWaylandCompositor);
++ return d->retainSelection;
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandCompositor::retainedSelectionReceived(QMimeData *)
++ {
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandCompositor::overrideSelection(const QMimeData *data)
++ {
++ Q_D(QWaylandCompositor);
++ #if QT_CONFIG(wayland_datadevice)
++ d->data_device_manager->overrideSelection(*data);
++ #endif
++ }
++
++ /*!
++ * \qmlproperty WaylandSeat QtWayland.Compositor::WaylandCompositor::defaultSeat
++ *
++ * This property contains the default seat for this
++ * WaylandCompositor.
++ */
++
++ /*!
++ * \property QWaylandCompositor::defaultSeat
++ *
++ * This property contains the default seat for this
++ * QWaylandCompositor.
++ */
++ QWaylandSeat *QWaylandCompositor::defaultSeat() const
++ {
++ Q_D(const QWaylandCompositor);
++ if (d->seats.size())
++ return d->seats.first();
++ return nullptr;
++ }
++
++ /*!
++ * 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);
++ return d->seatFor(inputEvent);
++ }
++
++ /*!
++ * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::useHardwareIntegrationExtension
++ * \default true
++ *
++ * This property holds whether the hardware integration extension should be enabled for
++ * this WaylandCompositor.
++ *
++ * This property must be set before the compositor component is completed.
++ */
++
++ /*!
++ * \property QWaylandCompositor::useHardwareIntegrationExtension
++ *
++ * This property holds whether the hardware integration extension should be enabled for
++ * this QWaylandCompositor.
++ *
++ * This property must be set before the compositor is \l{create()}{created}.
++ */
++ bool QWaylandCompositor::useHardwareIntegrationExtension() const
++ {
++ #if QT_CONFIG(opengl)
++ Q_D(const QWaylandCompositor);
++ return d->use_hw_integration_extension;
++ #else
++ return false;
++ #endif
++ }
++
++ void QWaylandCompositor::setUseHardwareIntegrationExtension(bool use)
++ {
++ #if QT_CONFIG(opengl)
++ Q_D(QWaylandCompositor);
++ if (use == d->use_hw_integration_extension)
++ return;
++
++ if (d->initialized)
++ qWarning("Setting QWaylandCompositor::useHardwareIntegrationExtension after initialization has no effect");
++
++ d->use_hw_integration_extension = use;
++ useHardwareIntegrationExtensionChanged();
++ #else
++ if (use)
++ qWarning() << "Hardware integration not supported without OpenGL support";
++ #endif
++ }
++
++ /*!
++ * Grab the surface content from the given \a buffer.
++ * The default implementation requires a OpenGL context to be bound to the current thread
++ * to work. If this is not possible, reimplement this function in your compositor subclass
++ * to implement custom logic.
++ * The default implementation only grabs shared memory and OpenGL buffers, reimplement this in your
++ * compositor subclass to handle more buffer types.
++ * \note You should not call this manually, but rather use QWaylandSurfaceGrabber (\a grabber).
++ */
++ void QWaylandCompositor::grabSurface(QWaylandSurfaceGrabber *grabber, const QWaylandBufferRef &buffer)
++ {
++ if (buffer.isSharedMemory()) {
++ emit grabber->success(buffer.image());
++ } else {
++ #if QT_CONFIG(opengl)
++ if (QOpenGLContext::currentContext()) {
++ QOpenGLFramebufferObject fbo(buffer.size());
++ fbo.bind();
++ QOpenGLTextureBlitter blitter;
++ blitter.create();
++
++
++ glViewport(0, 0, buffer.size().width(), buffer.size().height());
++
++ QOpenGLTextureBlitter::Origin surfaceOrigin =
++ buffer.origin() == QWaylandSurface::OriginTopLeft
++ ? QOpenGLTextureBlitter::OriginTopLeft
++ : QOpenGLTextureBlitter::OriginBottomLeft;
++
++ auto texture = buffer.toOpenGLTexture();
++ blitter.bind(texture->target());
++ blitter.blit(texture->textureId(), QMatrix4x4(), surfaceOrigin);
++ blitter.release();
++
++ emit grabber->success(fbo.toImage());
++ } else
++ #endif
++ emit grabber->failed(QWaylandSurfaceGrabber::UnknownBufferType);
++ }
++ }
++
++ /*!
++ * \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 be 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 be 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"