300 lines
7.4 KiB
C++
300 lines
7.4 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
|
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
|
|
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
*/
|
|
#include "display.h"
|
|
#include "clientbufferintegration.h"
|
|
#include "display_p.h"
|
|
#include "drmclientbuffer.h"
|
|
#include "logging.h"
|
|
#include "output_interface.h"
|
|
#include "shmclientbuffer.h"
|
|
|
|
#include <QAbstractEventDispatcher>
|
|
#include <QCoreApplication>
|
|
#include <QDebug>
|
|
#include <QRect>
|
|
|
|
namespace KWaylandServer
|
|
{
|
|
DisplayPrivate *DisplayPrivate::get(Display *display)
|
|
{
|
|
return display->d.data();
|
|
}
|
|
|
|
DisplayPrivate::DisplayPrivate(Display *q)
|
|
: q(q)
|
|
{
|
|
}
|
|
|
|
void DisplayPrivate::registerSocketName(const QString &socketName)
|
|
{
|
|
socketNames.append(socketName);
|
|
Q_EMIT q->socketNamesChanged();
|
|
}
|
|
|
|
Display::Display(QObject *parent)
|
|
: QObject(parent)
|
|
, d(new DisplayPrivate(this))
|
|
{
|
|
d->display = wl_display_create();
|
|
d->loop = wl_display_get_event_loop(d->display);
|
|
}
|
|
|
|
Display::~Display()
|
|
{
|
|
wl_display_destroy_clients(d->display);
|
|
wl_display_destroy(d->display);
|
|
}
|
|
|
|
bool Display::addSocketFileDescriptor(int fileDescriptor, const QString &name)
|
|
{
|
|
if (wl_display_add_socket_fd(d->display, fileDescriptor)) {
|
|
qCWarning(KWAYLAND_SERVER, "Failed to add %d fd to display", fileDescriptor);
|
|
return false;
|
|
}
|
|
if (!name.isEmpty()) {
|
|
d->registerSocketName(name);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Display::addSocketName(const QString &name)
|
|
{
|
|
if (name.isEmpty()) {
|
|
const char *socket = wl_display_add_socket_auto(d->display);
|
|
if (!socket) {
|
|
qCWarning(KWAYLAND_SERVER, "Failed to find a free display socket");
|
|
return false;
|
|
}
|
|
d->registerSocketName(QString::fromUtf8(socket));
|
|
} else {
|
|
if (wl_display_add_socket(d->display, qPrintable(name))) {
|
|
qCWarning(KWAYLAND_SERVER, "Failed to add %s socket to display", qPrintable(name));
|
|
return false;
|
|
}
|
|
d->registerSocketName(name);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
QStringList Display::socketNames() const
|
|
{
|
|
return d->socketNames;
|
|
}
|
|
|
|
bool Display::start()
|
|
{
|
|
if (d->running) {
|
|
return true;
|
|
}
|
|
|
|
const int fileDescriptor = wl_event_loop_get_fd(d->loop);
|
|
if (fileDescriptor == -1) {
|
|
qCWarning(KWAYLAND_SERVER) << "Did not get the file descriptor for the event loop";
|
|
return false;
|
|
}
|
|
|
|
d->socketNotifier = new QSocketNotifier(fileDescriptor, QSocketNotifier::Read, this);
|
|
connect(d->socketNotifier, &QSocketNotifier::activated, this, &Display::dispatchEvents);
|
|
|
|
QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
|
|
connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &Display::flush);
|
|
|
|
d->running = true;
|
|
Q_EMIT runningChanged(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Display::dispatchEvents()
|
|
{
|
|
if (wl_event_loop_dispatch(d->loop, 0) != 0) {
|
|
qCWarning(KWAYLAND_SERVER) << "Error on dispatching Wayland event loop";
|
|
}
|
|
}
|
|
|
|
void Display::flush()
|
|
{
|
|
wl_display_flush_clients(d->display);
|
|
}
|
|
|
|
void Display::createShm()
|
|
{
|
|
Q_ASSERT(d->display);
|
|
new ShmClientBufferIntegration(this);
|
|
}
|
|
|
|
quint32 Display::nextSerial()
|
|
{
|
|
return wl_display_next_serial(d->display);
|
|
}
|
|
|
|
quint32 Display::serial()
|
|
{
|
|
return wl_display_get_serial(d->display);
|
|
}
|
|
|
|
bool Display::isRunning() const
|
|
{
|
|
return d->running;
|
|
}
|
|
|
|
Display::operator wl_display *()
|
|
{
|
|
return d->display;
|
|
}
|
|
|
|
Display::operator wl_display *() const
|
|
{
|
|
return d->display;
|
|
}
|
|
|
|
QList<OutputInterface *> Display::outputs() const
|
|
{
|
|
return d->outputs;
|
|
}
|
|
|
|
QList< OutputDeviceV2Interface* > Display::outputDevices() const
|
|
{
|
|
return d->outputdevicesV2;
|
|
}
|
|
|
|
QVector<OutputInterface *> Display::outputsIntersecting(const QRect &rect) const
|
|
{
|
|
QVector<OutputInterface *> outputs;
|
|
for (auto *output : qAsConst(d->outputs)) {
|
|
const QRect outputGeometry(output->globalPosition(), output->pixelSize() / output->scale());
|
|
if (rect.intersects(outputGeometry)) {
|
|
outputs << output;
|
|
}
|
|
}
|
|
return outputs;
|
|
}
|
|
|
|
QVector<SeatInterface *> Display::seats() const
|
|
{
|
|
return d->seats;
|
|
}
|
|
|
|
ClientConnection *Display::getConnection(wl_client *client)
|
|
{
|
|
Q_ASSERT(client);
|
|
auto it = std::find_if(d->clients.constBegin(), d->clients.constEnd(), [client](ClientConnection *c) {
|
|
return c->client() == client;
|
|
});
|
|
if (it != d->clients.constEnd()) {
|
|
return *it;
|
|
}
|
|
// no ConnectionData yet, create it
|
|
auto c = new ClientConnection(client, this);
|
|
d->clients << c;
|
|
connect(c, &ClientConnection::disconnected, this, [this](ClientConnection *c) {
|
|
const int index = d->clients.indexOf(c);
|
|
Q_ASSERT(index != -1);
|
|
d->clients.remove(index);
|
|
Q_ASSERT(d->clients.indexOf(c) == -1);
|
|
Q_EMIT clientDisconnected(c);
|
|
});
|
|
Q_EMIT clientConnected(c);
|
|
return c;
|
|
}
|
|
|
|
QVector<ClientConnection *> Display::connections() const
|
|
{
|
|
return d->clients;
|
|
}
|
|
|
|
ClientConnection *Display::createClient(int fd)
|
|
{
|
|
Q_ASSERT(fd != -1);
|
|
Q_ASSERT(d->display);
|
|
wl_client *c = wl_client_create(d->display, fd);
|
|
if (!c) {
|
|
return nullptr;
|
|
}
|
|
return getConnection(c);
|
|
}
|
|
|
|
void Display::setEglDisplay(void *display)
|
|
{
|
|
if (d->eglDisplay != EGL_NO_DISPLAY) {
|
|
qCWarning(KWAYLAND_SERVER) << "EGLDisplay cannot be changed";
|
|
return;
|
|
}
|
|
d->eglDisplay = (EGLDisplay)display;
|
|
new DrmClientBufferIntegration(this);
|
|
}
|
|
|
|
void *Display::eglDisplay() const
|
|
{
|
|
return d->eglDisplay;
|
|
}
|
|
|
|
struct ClientBufferDestroyListener : wl_listener {
|
|
ClientBufferDestroyListener(Display *display, ClientBuffer *buffer);
|
|
~ClientBufferDestroyListener();
|
|
|
|
Display *display;
|
|
};
|
|
|
|
void bufferDestroyCallback(wl_listener *listener, void *data)
|
|
{
|
|
ClientBufferDestroyListener *destroyListener = static_cast<ClientBufferDestroyListener *>(listener);
|
|
DisplayPrivate *displayPrivate = DisplayPrivate::get(destroyListener->display);
|
|
|
|
ClientBuffer *buffer = displayPrivate->q->clientBufferForResource(static_cast<wl_resource *>(data));
|
|
displayPrivate->unregisterClientBuffer(buffer);
|
|
|
|
buffer->markAsDestroyed();
|
|
}
|
|
|
|
ClientBufferDestroyListener::ClientBufferDestroyListener(Display *display, ClientBuffer *buffer)
|
|
: display(display)
|
|
{
|
|
notify = bufferDestroyCallback;
|
|
|
|
link.prev = nullptr;
|
|
link.next = nullptr;
|
|
|
|
wl_resource_add_destroy_listener(buffer->resource(), this);
|
|
}
|
|
|
|
ClientBufferDestroyListener::~ClientBufferDestroyListener()
|
|
{
|
|
wl_list_remove(&link);
|
|
}
|
|
|
|
ClientBuffer *Display::clientBufferForResource(wl_resource *resource) const
|
|
{
|
|
ClientBuffer *buffer = d->resourceToBuffer.value(resource);
|
|
if (buffer) {
|
|
return buffer;
|
|
}
|
|
|
|
for (ClientBufferIntegration *integration : qAsConst(d->bufferIntegrations)) {
|
|
ClientBuffer *buffer = integration->createBuffer(resource);
|
|
if (buffer) {
|
|
d->registerClientBuffer(buffer);
|
|
return buffer;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void DisplayPrivate::registerClientBuffer(ClientBuffer *buffer)
|
|
{
|
|
resourceToBuffer.insert(buffer->resource(), buffer);
|
|
bufferToListener.insert(buffer, new ClientBufferDestroyListener(q, buffer));
|
|
}
|
|
|
|
void DisplayPrivate::unregisterClientBuffer(ClientBuffer *buffer)
|
|
{
|
|
Q_ASSERT_X(buffer->resource(), "unregisterClientBuffer", "buffer must have valid resource");
|
|
resourceToBuffer.remove(buffer->resource());
|
|
delete bufferToListener.take(buffer);
|
|
}
|
|
|
|
}
|