2014-08-26 14:07:39 +00:00
|
|
|
/********************************************************************
|
2014-09-17 13:57:56 +00:00
|
|
|
Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
|
2014-08-26 14:07:39 +00:00
|
|
|
|
2014-09-17 13:57:56 +00:00
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) version 3, or any
|
|
|
|
later version accepted by the membership of KDE e.V. (or its
|
|
|
|
successor approved by the membership of KDE e.V.), which shall
|
|
|
|
act as a proxy defined in Section 6 of version 3 of the license.
|
2014-08-26 14:07:39 +00:00
|
|
|
|
2014-09-17 13:57:56 +00:00
|
|
|
This library is distributed in the hope that it will be useful,
|
2014-08-26 14:07:39 +00:00
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2014-09-17 13:57:56 +00:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
2014-08-26 14:07:39 +00:00
|
|
|
|
2014-09-17 13:57:56 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
2014-08-26 14:07:39 +00:00
|
|
|
*********************************************************************/
|
|
|
|
#include "display.h"
|
2014-08-28 07:52:35 +00:00
|
|
|
#include "compositor_interface.h"
|
2014-11-04 14:10:22 +00:00
|
|
|
#include "datadevicemanager_interface.h"
|
2015-01-20 08:42:26 +00:00
|
|
|
#include "logging_p.h"
|
2014-08-26 14:07:39 +00:00
|
|
|
#include "output_interface.h"
|
2014-09-02 07:34:31 +00:00
|
|
|
#include "seat_interface.h"
|
2014-08-29 09:42:57 +00:00
|
|
|
#include "shell_interface.h"
|
2014-10-14 12:04:35 +00:00
|
|
|
#include "subcompositor_interface.h"
|
2014-08-26 14:07:39 +00:00
|
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QAbstractEventDispatcher>
|
|
|
|
#include <QSocketNotifier>
|
2015-02-18 08:32:40 +00:00
|
|
|
#include <QThread>
|
2014-08-26 14:07:39 +00:00
|
|
|
|
|
|
|
#include <wayland-server.h>
|
|
|
|
|
2015-03-03 08:43:30 +00:00
|
|
|
#include <EGL/egl.h>
|
|
|
|
|
2014-09-17 14:10:38 +00:00
|
|
|
namespace KWayland
|
2014-08-26 14:07:39 +00:00
|
|
|
{
|
2014-09-17 14:10:38 +00:00
|
|
|
namespace Server
|
2014-08-26 14:07:39 +00:00
|
|
|
{
|
|
|
|
|
2014-09-18 13:40:22 +00:00
|
|
|
class Display::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Private(Display *q);
|
|
|
|
void flush();
|
2015-04-21 06:56:19 +00:00
|
|
|
void dispatch();
|
2014-09-18 13:40:22 +00:00
|
|
|
void setRunning(bool running);
|
2014-11-07 10:41:37 +00:00
|
|
|
void installSocketNotifier();
|
2014-09-18 13:40:22 +00:00
|
|
|
|
|
|
|
wl_display *display = nullptr;
|
|
|
|
wl_event_loop *loop = nullptr;
|
|
|
|
QString socketName = QStringLiteral("wayland-0");
|
|
|
|
bool running = false;
|
|
|
|
QList<OutputInterface*> outputs;
|
2014-11-17 15:01:18 +00:00
|
|
|
QVector<ClientConnection*> clients;
|
2015-03-03 08:43:30 +00:00
|
|
|
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
|
2014-09-18 13:40:22 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
Display *q;
|
|
|
|
};
|
|
|
|
|
|
|
|
Display::Private::Private(Display *q)
|
|
|
|
: q(q)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-11-07 10:41:37 +00:00
|
|
|
void Display::Private::installSocketNotifier()
|
|
|
|
{
|
2015-02-18 08:32:40 +00:00
|
|
|
if (!QThread::currentThread()) {
|
2014-11-07 10:41:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
int fd = wl_event_loop_get_fd(loop);
|
|
|
|
if (fd == -1) {
|
2015-01-20 08:42:26 +00:00
|
|
|
qCWarning(KWAYLAND_SERVER) << "Did not get the file descriptor for the event loop";
|
2014-11-07 10:41:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
QSocketNotifier *m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, q);
|
2015-04-21 06:56:19 +00:00
|
|
|
QObject::connect(m_notifier, &QSocketNotifier::activated, q, [this] { dispatch(); } );
|
2015-02-18 08:32:40 +00:00
|
|
|
QObject::connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, q, [this] { flush(); });
|
2014-11-07 10:41:37 +00:00
|
|
|
setRunning(true);
|
|
|
|
}
|
|
|
|
|
2014-08-26 14:07:39 +00:00
|
|
|
Display::Display(QObject *parent)
|
|
|
|
: QObject(parent)
|
2014-09-18 13:40:22 +00:00
|
|
|
, d(new Private(this))
|
2014-08-26 14:07:39 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Display::~Display()
|
|
|
|
{
|
|
|
|
terminate();
|
|
|
|
}
|
|
|
|
|
2014-09-18 13:40:22 +00:00
|
|
|
void Display::Private::flush()
|
2015-04-21 06:56:19 +00:00
|
|
|
{
|
|
|
|
if (!display || !loop) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
wl_display_flush_clients(display);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Display::Private::dispatch()
|
2014-08-26 14:07:39 +00:00
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
if (!display || !loop) {
|
2014-08-26 14:07:39 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-09-18 13:40:22 +00:00
|
|
|
if (wl_event_loop_dispatch(loop, 0) != 0) {
|
2015-01-20 08:42:26 +00:00
|
|
|
qCWarning(KWAYLAND_SERVER) << "Error on dispatching Wayland event loop";
|
2014-08-26 14:07:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Display::setSocketName(const QString &name)
|
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
if (d->socketName == name) {
|
2014-08-26 14:07:39 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-09-18 13:40:22 +00:00
|
|
|
d->socketName = name;
|
|
|
|
emit socketNameChanged(d->socketName);
|
2014-08-26 14:07:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString Display::socketName() const
|
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
return d->socketName;
|
2014-08-26 14:07:39 +00:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:42:16 +00:00
|
|
|
void Display::start(StartMode mode)
|
2014-08-26 14:07:39 +00:00
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
Q_ASSERT(!d->running);
|
|
|
|
Q_ASSERT(!d->display);
|
|
|
|
d->display = wl_display_create();
|
2014-11-27 15:42:16 +00:00
|
|
|
if (mode == StartMode::ConnectToSocket) {
|
|
|
|
if (wl_display_add_socket(d->display, qPrintable(d->socketName)) != 0) {
|
|
|
|
return;
|
|
|
|
}
|
2014-08-26 14:07:39 +00:00
|
|
|
}
|
|
|
|
|
2014-09-18 13:40:22 +00:00
|
|
|
d->loop = wl_display_get_event_loop(d->display);
|
2014-11-07 10:41:37 +00:00
|
|
|
d->installSocketNotifier();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Display::startLoop()
|
|
|
|
{
|
|
|
|
Q_ASSERT(!d->running);
|
|
|
|
Q_ASSERT(d->display);
|
|
|
|
d->installSocketNotifier();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Display::dispatchEvents(int msecTimeout)
|
|
|
|
{
|
|
|
|
Q_ASSERT(!d->running);
|
|
|
|
Q_ASSERT(d->display);
|
|
|
|
wl_event_loop_dispatch(d->loop, msecTimeout);
|
|
|
|
wl_display_flush_clients(d->display);
|
2014-08-26 14:07:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Display::terminate()
|
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
if (!d->running) {
|
2014-08-26 14:07:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
emit aboutToTerminate();
|
2014-09-18 13:40:22 +00:00
|
|
|
wl_display_terminate(d->display);
|
|
|
|
wl_display_destroy(d->display);
|
|
|
|
d->display = nullptr;
|
|
|
|
d->loop = nullptr;
|
|
|
|
d->setRunning(false);
|
2014-08-26 14:07:39 +00:00
|
|
|
}
|
|
|
|
|
2014-09-18 13:40:22 +00:00
|
|
|
void Display::Private::setRunning(bool r)
|
2014-08-26 14:07:39 +00:00
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
Q_ASSERT(running != r);
|
|
|
|
running = r;
|
|
|
|
emit q->runningChanged(running);
|
2014-08-26 14:07:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
OutputInterface *Display::createOutput(QObject *parent)
|
|
|
|
{
|
|
|
|
OutputInterface *output = new OutputInterface(this, parent);
|
2014-09-18 13:40:22 +00:00
|
|
|
connect(output, &QObject::destroyed, this, [this,output] { d->outputs.removeAll(output); });
|
2014-08-26 14:07:39 +00:00
|
|
|
connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutput(output); });
|
2014-09-18 13:40:22 +00:00
|
|
|
d->outputs << output;
|
2014-08-26 14:07:39 +00:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2014-08-28 07:52:35 +00:00
|
|
|
CompositorInterface *Display::createCompositor(QObject *parent)
|
|
|
|
{
|
|
|
|
CompositorInterface *compositor = new CompositorInterface(this, parent);
|
|
|
|
connect(this, &Display::aboutToTerminate, compositor, [this,compositor] { delete compositor; });
|
|
|
|
return compositor;
|
|
|
|
}
|
|
|
|
|
2014-08-29 09:42:57 +00:00
|
|
|
ShellInterface *Display::createShell(QObject *parent)
|
|
|
|
{
|
|
|
|
ShellInterface *shell = new ShellInterface(this, parent);
|
|
|
|
connect(this, &Display::aboutToTerminate, shell, [this,shell] { delete shell; });
|
|
|
|
return shell;
|
|
|
|
}
|
|
|
|
|
2014-09-02 07:34:31 +00:00
|
|
|
SeatInterface *Display::createSeat(QObject *parent)
|
|
|
|
{
|
|
|
|
SeatInterface *seat = new SeatInterface(this, parent);
|
|
|
|
connect(this, &Display::aboutToTerminate, seat, [this,seat] { delete seat; });
|
|
|
|
return seat;
|
|
|
|
}
|
|
|
|
|
2014-10-14 12:04:35 +00:00
|
|
|
SubCompositorInterface *Display::createSubCompositor(QObject *parent)
|
|
|
|
{
|
|
|
|
auto c = new SubCompositorInterface(this, parent);
|
|
|
|
connect(this, &Display::aboutToTerminate, c, [this,c] { delete c; });
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2014-11-04 14:10:22 +00:00
|
|
|
DataDeviceManagerInterface *Display::createDataDeviceManager(QObject *parent)
|
|
|
|
{
|
|
|
|
auto m = new DataDeviceManagerInterface(this, parent);
|
|
|
|
connect(this, &Display::aboutToTerminate, m, [this,m] { delete m; });
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2014-08-28 12:22:53 +00:00
|
|
|
void Display::createShm()
|
|
|
|
{
|
2015-02-09 16:13:20 +00:00
|
|
|
Q_ASSERT(d->display);
|
2014-09-18 13:40:22 +00:00
|
|
|
wl_display_init_shm(d->display);
|
2014-08-28 12:22:53 +00:00
|
|
|
}
|
|
|
|
|
2014-08-26 14:07:39 +00:00
|
|
|
void Display::removeOutput(OutputInterface *output)
|
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
d->outputs.removeAll(output);
|
2014-08-26 14:07:39 +00:00
|
|
|
delete output;
|
|
|
|
}
|
|
|
|
|
2014-08-29 09:42:57 +00:00
|
|
|
quint32 Display::nextSerial()
|
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
return wl_display_next_serial(d->display);
|
2014-08-29 09:42:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
quint32 Display::serial()
|
|
|
|
{
|
2014-09-18 13:40:22 +00:00
|
|
|
return wl_display_get_serial(d->display);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Display::isRunning() const
|
|
|
|
{
|
|
|
|
return d->running;
|
2014-08-29 09:42:57 +00:00
|
|
|
}
|
|
|
|
|
2014-09-18 13:40:22 +00:00
|
|
|
Display::operator wl_display*()
|
|
|
|
{
|
|
|
|
return d->display;
|
|
|
|
}
|
|
|
|
|
|
|
|
Display::operator wl_display*() const
|
|
|
|
{
|
|
|
|
return d->display;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList< OutputInterface* > Display::outputs() const
|
|
|
|
{
|
|
|
|
return d->outputs;
|
|
|
|
}
|
|
|
|
|
2014-11-17 15:01:18 +00:00
|
|
|
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);
|
|
|
|
emit clientDisconnected(c);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
emit clientConnected(c);
|
|
|
|
return c;
|
|
|
|
}
|
2014-09-18 13:40:22 +00:00
|
|
|
|
2014-11-17 17:13:28 +00:00
|
|
|
QVector< ClientConnection* > Display::connections() const
|
|
|
|
{
|
|
|
|
return d->clients;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:16:54 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-03-03 08:43:30 +00:00
|
|
|
void Display::setEglDisplay(void *display)
|
|
|
|
{
|
|
|
|
if (d->eglDisplay != EGL_NO_DISPLAY) {
|
|
|
|
qCWarning(KWAYLAND_SERVER) << "EGLDisplay cannot be changed";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
d->eglDisplay = (EGLDisplay)display;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *Display::eglDisplay() const
|
|
|
|
{
|
|
|
|
return d->eglDisplay;
|
|
|
|
}
|
|
|
|
|
2014-08-26 14:07:39 +00:00
|
|
|
}
|
|
|
|
}
|