blob: e021b742b57210428d03f74e33317d802167f1ec [file] [log] [blame]
/****************************************************************************
**
** 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: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 "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 <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
Q_LOGGING_CATEGORY(qLcWaylandCompositor, "qt.waylandcompositor")
Q_LOGGING_CATEGORY(qLcWaylandCompositorHardwareIntegration, "qt.waylandcompositor.hardwareintegration")
Q_LOGGING_CATEGORY(qLcWaylandCompositorInputMethods, "qt.waylandcompositor.inputmethods")
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());
uint32_t code = ke->nativeScanCode;
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);
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)
{
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();
}
wl_compositor::init(display, 3);
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);
const QVector<wl_shm_format> formats = QWaylandSharedMemoryFormatHelper::supportedWaylandFormats();
for (wl_shm_format format : formats)
wl_display_add_shm_format(display, format);
if (!socket_name.isEmpty()) {
if (wl_display_add_socket(display, socket_name.constData()))
qFatal("Fatal: Failed to open server socket\n");
} else {
const char *autoSocketName = wl_display_add_socket_auto(display);
if (!autoSocketName)
qFatal("Fatal: Failed to open server socket\n");
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()));
QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher;
QObject::connect(dispatcher, SIGNAL(aboutToBlock()), q, SLOT(processWaylandEvents()));
initializeHardwareIntegration();
initializeSeats();
initialized = true;
for (const QPointer<QObject> &object : qExchange(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
#if QT_CONFIG(opengl)
// Some client buffer integrations need to clean up before the destroying the wl_display
client_buffer_integration.reset();
#endif
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);
}
}
#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)) {
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)
{
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::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();
}
void QWaylandCompositorPrivate::initializeHardwareIntegration()
{
#if QT_CONFIG(opengl)
Q_Q(QWaylandCompositor);
if (use_hw_integration_extension)
hw_integration.reset(new QtWayland::HardwareIntegration(q));
loadClientBufferIntegration();
loadServerBufferIntegration();
#endif
}
void QWaylandCompositorPrivate::initializeSeats()
{
for (QWaylandSeat *seat : qAsConst(seats))
seat->initialize();
}
void QWaylandCompositorPrivate::loadClientBufferIntegration()
{
#if QT_CONFIG(opengl)
Q_Q(QWaylandCompositor);
QStringList keys = QtWayland::ClientBufferIntegrationFactory::keys();
QString targetKey;
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();
}
if (!targetKey.isEmpty()) {
client_buffer_integration.reset(QtWayland::ClientBufferIntegrationFactory::create(targetKey, QStringList()));
if (client_buffer_integration) {
qCDebug(qLcWaylandCompositorHardwareIntegration) << "Loaded client buffer integration:" << targetKey;
client_buffer_integration->setCompositor(q);
if (!client_buffer_integration->initializeHardware(display)) {
qCWarning(qLcWaylandCompositorHardwareIntegration)
<< "Failed to initialize hardware for client buffer integration:" << targetKey;
client_buffer_integration.reset();
}
} else {
qCWarning(qLcWaylandCompositorHardwareIntegration)
<< "Failed to load client buffer integration:" << targetKey;
}
}
if (!client_buffer_integration) {
qCWarning(qLcWaylandCompositorHardwareIntegration)
<< "No client buffer integration was loaded, this means that clients will fall back"
<< "to use CPU buffers (wl_shm) for transmitting buffers instead of using zero-copy"
<< "GPU buffer handles. Expect serious performance impact with OpenGL clients due"
<< "to potentially multiple copies between CPU and GPU memory per buffer.\n"
<< "See the QtWayland readme for more info about how to build and configure Qt for"
<< "your device.";
return;
}
if (client_buffer_integration && hw_integration)
hw_integration->setClientBufferIntegration(targetKey);
#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->setServerBufferIntegration(targetKey);
#endif
}
/*!
\qmltype WaylandCompositor
\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.
*/
/*!
\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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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)
{
#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
}
/*!
* \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 QtWaylandCompositor::WaylandCompositor::destroyClientForSurface(surface)
*
* Destroys the client for the WaylandSurface \a surface.
*/
/*!
* Destroys the client for the \a surface.
*/
void QWaylandCompositor::destroyClientForSurface(QWaylandSurface *surface)
{
destroyClient(surface->client());
}
/*!
* \qmlmethod QtWaylandCompositor::WaylandCompositor::destroyClient(client)
*
* Destroys the given WaylandClient \a client.
*/
/*!
* 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 QtWaylandCompositor::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 (d->outputs.size() && d->outputs.first() == output)
return;
bool alreadyAdded = d->outputs.removeOne(output);
d->outputs.prepend(output);
emit defaultOutputChanged();
if (!alreadyAdded)
emit outputAdded(output);
}
/*!
* \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 QtWaylandCompositor::WaylandCompositor::retainedSelection
*
* This property holds whether retained selection is enabled.
*/
/*!
* \property QWaylandCompositor::retainedSelection
*
* This property holds whether retained selection is enabled.
*/
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 QtWaylandCompositor::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;
}
/*!
* \internal
*
* Currently, Qt only supports a single seat, so this exists for
* future proofing the APIs.
*/
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;
}
/*!
* \qmlproperty bool QtWaylandCompositor::WaylandCompositor::useHardwareIntegrationExtension
*
* 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);
}
}
QT_END_NAMESPACE