wayland_server.cpp 30.7 KB
Newer Older
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
1 2 3
/*
    KWin - the KDE window manager
    This file is part of the KDE project.
4

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
5
    SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
6

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
7 8
    SPDX-License-Identifier: GPL-2.0-or-later
*/
9
#include "wayland_server.h"
10
#include "abstract_wayland_output.h"
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
11
#include "x11client.h"
12
#include "platform.h"
13
#include "composite.h"
14
#include "idle_inhibition.h"
15
#include "inputpanelv1integration.h"
16
#include "screens.h"
17
#include "layershellv1integration.h"
18
#include "xdgshellintegration.h"
19
#include "workspace.h"
20
#include "xdgshellclient.h"
21
#include "service_utils.h"
22
#include "unmanaged.h"
23

24 25
// Client
#include <KWayland/Client/connection_thread.h>
26
#include <KWayland/Client/event_queue.h>
27
#include <KWayland/Client/registry.h>
28
#include <KWayland/Client/compositor.h>
29 30
#include <KWayland/Client/seat.h>
#include <KWayland/Client/datadevicemanager.h>
31
#include <KWayland/Client/surface.h>
32
// Server
33 34 35 36 37 38 39
#include <KWaylandServer/appmenu_interface.h>
#include <KWaylandServer/compositor_interface.h>
#include <KWaylandServer/datadevicemanager_interface.h>
#include <KWaylandServer/datasource_interface.h>
#include <KWaylandServer/display.h>
#include <KWaylandServer/dpms_interface.h>
#include <KWaylandServer/idle_interface.h>
40
#include <KWaylandServer/idleinhibit_v1_interface.h>
41 42 43 44 45
#include <KWaylandServer/linuxdmabuf_v1_interface.h>
#include <KWaylandServer/output_interface.h>
#include <KWaylandServer/plasmashell_interface.h>
#include <KWaylandServer/plasmavirtualdesktop_interface.h>
#include <KWaylandServer/plasmawindowmanagement_interface.h>
46
#include <KWaylandServer/pointerconstraints_v1_interface.h>
47
#include <KWaylandServer/pointergestures_v1_interface.h>
48 49 50 51 52 53 54 55
#include <KWaylandServer/seat_interface.h>
#include <KWaylandServer/server_decoration_interface.h>
#include <KWaylandServer/server_decoration_palette_interface.h>
#include <KWaylandServer/shadow_interface.h>
#include <KWaylandServer/subcompositor_interface.h>
#include <KWaylandServer/blur_interface.h>
#include <KWaylandServer/outputmanagement_interface.h>
#include <KWaylandServer/outputconfiguration_interface.h>
56
#include <KWaylandServer/xdgdecoration_v1_interface.h>
57
#include <KWaylandServer/xdgshell_interface.h>
58
#include <KWaylandServer/xdgforeign_v2_interface.h>
59
#include <KWaylandServer/xdgoutput_v1_interface.h>
60 61
#include <KWaylandServer/keystate_interface.h>
#include <KWaylandServer/filtered_display.h>
62
#include <KWaylandServer/keyboard_shortcuts_inhibit_v1_interface.h>
63
#include <KWaylandServer/inputmethod_v1_interface.h>
64

65
// Qt
66
#include <QCryptographicHash>
67 68
#include <QDir>
#include <QFileInfo>
69
#include <QThread>
70 71
#include <QWindow>

72 73 74
// system
#include <sys/types.h>
#include <sys/socket.h>
75
#include <unistd.h>
76

77 78 79
//screenlocker
#include <KScreenLocker/KsldApp>

80
using namespace KWaylandServer;
81 82 83 84 85 86 87 88 89

namespace KWin
{

KWIN_SINGLETON_FACTORY(WaylandServer)

WaylandServer::WaylandServer(QObject *parent)
    : QObject(parent)
{
90
    qRegisterMetaType<KWaylandServer::OutputInterface::DpmsMode>();
91 92
}

93 94 95 96
WaylandServer::~WaylandServer()
{
    destroyInputMethodConnection();
}
97

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
98 99 100 101 102
KWaylandServer::ClientConnection *WaylandServer::xWaylandConnection() const
{
    return m_xwaylandConnection;
}

103
void WaylandServer::destroyInternalConnection()
104
{
105
    emit terminatingInternalClientConnection();
106
    if (m_internalConnection.client) {
107 108 109 110 111 112 113 114 115
        // delete all connections hold by plugins like e.g. widget style
        const auto connections = KWayland::Client::ConnectionThread::connections();
        for (auto c : connections) {
            if (c == m_internalConnection.client) {
                continue;
            }
            emit c->connectionDied();
        }

116
        delete m_internalConnection.registry;
117
        delete m_internalConnection.compositor;
118 119
        delete m_internalConnection.seat;
        delete m_internalConnection.ddm;
120
        dispatch();
121 122 123
        m_internalConnection.client->deleteLater();
        m_internalConnection.clientThread->quit();
        m_internalConnection.clientThread->wait();
124
        delete m_internalConnection.clientThread;
125
        m_internalConnection.client = nullptr;
126
        m_internalConnection.server->destroy();
127
        m_internalConnection.server = nullptr;
128 129
    }
}
130

131 132 133 134
void WaylandServer::terminateClientConnections()
{
    destroyInternalConnection();
    destroyInputMethodConnection();
135 136 137 138 139
    if (m_display) {
        const auto connections = m_display->connections();
        for (auto it = connections.begin(); it != connections.end(); ++it) {
            (*it)->destroy();
        }
140 141 142
    }
}

143
void WaylandServer::registerShellClient(AbstractClient *client)
144
{
145 146 147 148 149 150 151 152
    if (client->readyForPainting()) {
        emit shellClientAdded(client);
    } else {
        connect(client, &AbstractClient::windowShown, this, &WaylandServer::shellClientShown);
    }
    m_clients << client;
}

153
void WaylandServer::registerXdgToplevelClient(XdgToplevelClient *client)
154
{
155
    // TODO: Find a better way and more generic to install extensions.
156

157
    SurfaceInterface *surface = client->surface();
158

159 160 161
    if (surface->client() == m_screenLockerClientConnection) {
        ScreenLocker::KSldApp::self()->lockScreenShown();
    }
162

163
    registerShellClient(client);
164

165
    auto it = std::find_if(m_plasmaShellSurfaces.begin(), m_plasmaShellSurfaces.end(),
166 167
        [surface] (PlasmaShellSurfaceInterface *plasmaSurface) {
            return plasmaSurface->surface() == surface;
168 169 170 171 172 173
        }
    );
    if (it != m_plasmaShellSurfaces.end()) {
        client->installPlasmaShellSurface(*it);
        m_plasmaShellSurfaces.erase(it);
    }
174 175 176
    if (auto decoration = ServerSideDecorationInterface::get(surface)) {
        client->installServerDecoration(decoration);
    }
177 178 179
    if (auto decoration = XdgToplevelDecorationV1Interface::get(client->shellSurface())) {
        client->installXdgDecoration(decoration);
    }
180
    if (auto menu = m_appMenuManager->appMenuForSurface(surface)) {
David Edmundson's avatar
David Edmundson committed
181 182
        client->installAppMenu(menu);
    }
183
    if (auto palette = m_paletteManager->paletteForSurface(surface)) {
184 185
        client->installPalette(palette);
    }
Marco Martin's avatar
Marco Martin committed
186

187
    connect(m_XdgForeign, &XdgForeignV2Interface::transientChanged, client, [this](SurfaceInterface *child) {
Marco Martin's avatar
Marco Martin committed
188 189
        emit foreignTransientChanged(child);
    });
190 191
}

192
void WaylandServer::registerXdgGenericClient(AbstractClient *client)
193
{
194 195 196
    XdgToplevelClient *toplevelClient = qobject_cast<XdgToplevelClient *>(client);
    if (toplevelClient) {
        registerXdgToplevelClient(toplevelClient);
197 198
        return;
    }
199 200 201
    XdgPopupClient *popupClient = qobject_cast<XdgPopupClient *>(client);
    if (popupClient) {
        registerShellClient(popupClient);
202 203 204 205 206 207 208 209 210 211 212 213 214

        SurfaceInterface *surface = client->surface();
        auto it = std::find_if(m_plasmaShellSurfaces.begin(), m_plasmaShellSurfaces.end(),
            [surface] (PlasmaShellSurfaceInterface *plasmaSurface) {
                return plasmaSurface->surface() == surface;
            }
        );

        if (it != m_plasmaShellSurfaces.end()) {
            popupClient->installPlasmaShellSurface(*it);
            m_plasmaShellSurfaces.erase(it);
        }

215 216 217
        return;
    }
    qCDebug(KWIN_CORE) << "Received invalid xdg client:" << client->surface();
218 219
}

220 221 222 223 224 225 226 227 228 229 230 231
AbstractWaylandOutput *WaylandServer::findOutput(KWaylandServer::OutputInterface *outputIface) const
{
    AbstractWaylandOutput *outputFound = nullptr;
    const auto outputs = kwinApp()->platform()->enabledOutputs();
    for (auto output : outputs) {
        if (static_cast<AbstractWaylandOutput *>(output)->waylandOutput() == outputIface) {
            outputFound = static_cast<AbstractWaylandOutput *>(output);
        }
    }
    return outputFound;
}

232
class KWinDisplay : public KWaylandServer::FilteredDisplay
233 234 235
{
public:
    KWinDisplay(QObject *parent)
236
        : KWaylandServer::FilteredDisplay(parent)
237 238 239 240 241 242 243 244 245 246 247 248 249 250
    {}

    static QByteArray sha256(const QString &fileName)
    {
        QFile f(fileName);
        if (f.open(QFile::ReadOnly)) {
            QCryptographicHash hash(QCryptographicHash::Sha256);
            if (hash.addData(&f)) {
                return hash.result();
            }
        }
        return QByteArray();
    }

251
    bool isTrustedOrigin(KWaylandServer::ClientConnection *client) const {
252
        const auto fullPathSha = sha256(client->executablePath());
253 254 255 256 257 258 259 260 261 262
        const auto localSha = sha256(QLatin1String("/proc/") + QString::number(client->processId()) + QLatin1String("/exe"));
        const bool trusted = !localSha.isEmpty() && fullPathSha == localSha;

        if (!trusted) {
            qCWarning(KWIN_CORE) << "Could not trust" << client->executablePath() << "sha" << localSha << fullPathSha;
        }

        return trusted;
    }

263
    QStringList fetchRequestedInterfaces(KWaylandServer::ClientConnection *client) const {
264
        return KWin::fetchRequestedInterfaces(client->executablePath());
265 266
    }

267
    const QSet<QByteArray> interfacesBlackList = {"org_kde_kwin_remote_access_manager", "org_kde_plasma_window_management", "org_kde_kwin_fake_input", "org_kde_kwin_keystate", "zkde_screencast_unstable_v1"};
268 269 270

    const QSet<QByteArray> inputmethodInterfaces = { "zwp_input_panel_v1", "zwp_input_method_v1" };

271
    QSet<QString> m_reported;
272

273
    bool allowInterface(KWaylandServer::ClientConnection *client, const QByteArray &interfaceName) override {
274 275 276 277
        if (client->processId() == getpid()) {
            return true;
        }

278 279 280 281
        if (client != waylandServer()->inputMethodConnection() && inputmethodInterfaces.contains(interfaceName)) {
            return false;
        }

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
        if (!interfacesBlackList.contains(interfaceName)) {
            return true;
        }

        if (client->executablePath().isEmpty()) {
            qCWarning(KWIN_CORE) << "Could not identify process with pid" << client->processId();
            return false;
        }

        {
            auto requestedInterfaces = client->property("requestedInterfaces");
            if (requestedInterfaces.isNull()) {
                requestedInterfaces = fetchRequestedInterfaces(client);
                client->setProperty("requestedInterfaces", requestedInterfaces);
            }
            if (!requestedInterfaces.toStringList().contains(QString::fromUtf8(interfaceName))) {
298 299 300 301 302 303 304
                if (KWIN_CORE().isDebugEnabled()) {
                    const QString id = client->executablePath() + QLatin1Char('|') + QString::fromUtf8(interfaceName);
                    if (!m_reported.contains({id})) {
                        m_reported.insert(id);
                        qCDebug(KWIN_CORE) << "Interface" << interfaceName << "not in X-KDE-Wayland-Interfaces of" << client->executablePath();
                    }
                }
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
                return false;
            }
        }

        {
            auto trustedOrigin = client->property("isPrivileged");
            if (trustedOrigin.isNull()) {
                trustedOrigin = isTrustedOrigin(client);
                client->setProperty("isPrivileged", trustedOrigin);
            }

            if (!trustedOrigin.toBool()) {
                return false;
            }
        }
        qCDebug(KWIN_CORE) << "authorized" << client->executablePath() << interfaceName;
        return true;
    }
};

325 326 327 328 329
bool WaylandServer::start()
{
    return m_display->start();
}

330
bool WaylandServer::init(const QByteArray &socketName, InitializationFlags flags)
331
{
332
    m_initFlags = flags;
333
    m_display = new KWinDisplay(this);
334 335
    if (!socketName.isNull() && !socketName.isEmpty()) {
        m_display->setSocketName(QString::fromUtf8(socketName));
336 337
    } else {
        m_display->setAutomaticSocketNaming(true);
338 339
    }
    m_compositor = m_display->createCompositor(m_display);
340 341 342 343 344 345 346 347
    connect(m_compositor, &CompositorInterface::surfaceCreated, this,
        [this] (SurfaceInterface *surface) {
            // check whether we have a Toplevel with the Surface's id
            Workspace *ws = Workspace::self();
            if (!ws) {
                // it's possible that a Surface gets created before Workspace is created
                return;
            }
348 349 350 351
            if (surface->client() != xWaylandConnection()) {
                // setting surface is only relevat for Xwayland clients
                return;
            }
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366

            X11Client *client = ws->findClient([surface](const X11Client *client) {
                return client->surfaceId() == surface->id();
            });
            if (client) {
                client->setSurface(surface);
                return;
            }

            Unmanaged *unmanaged = ws->findUnmanaged([surface](const Unmanaged *unmanaged) {
                return unmanaged->surfaceId() == surface->id();
            });
            if (unmanaged) {
                unmanaged->setSurface(surface);
                return;
367
            }
368 369

            // The surface will be bound later when a WL_SURFACE_ID message is received.
370 371
        }
    );
David Edmundson's avatar
David Edmundson committed
372

373
    m_tabletManagerV2 = m_display->createTabletManagerV2(m_display);
374
    m_keyboardShortcutsInhibitManager = m_display->createKeyboardShortcutsInhibitManagerV1(m_display);
375

376 377 378 379
    auto inputPanelV1Integration = new InputPanelV1Integration(this);
    connect(inputPanelV1Integration, &InputPanelV1Integration::clientCreated,
            this, &WaylandServer::registerShellClient);

380 381
    auto xdgShellIntegration = new XdgShellIntegration(this);
    connect(xdgShellIntegration, &XdgShellIntegration::clientCreated,
382
            this, &WaylandServer::registerXdgGenericClient);
383

384 385 386 387
    auto layerShellV1Integration = new LayerShellV1Integration(this);
    connect(layerShellV1Integration, &LayerShellV1Integration::clientCreated,
            this, &WaylandServer::registerShellClient);

388 389 390 391 392 393
    m_xdgDecorationManagerV1 = m_display->createXdgDecorationManagerV1(m_display);
    connect(m_xdgDecorationManagerV1, &XdgDecorationManagerV1Interface::decorationCreated, this,
        [this](XdgToplevelDecorationV1Interface *decoration) {
            if (XdgToplevelClient *toplevel = findXdgToplevelClient(decoration->toplevel()->surface())) {
                toplevel->installXdgDecoration(decoration);
            }
394
        }
395
    );
396

397
    m_display->createViewporter();
398
    m_display->createShm();
399 400
    m_seat = m_display->createSeat(m_display);
    m_seat->create();
401
    m_display->createPointerGesturesV1(m_display);
402
    m_display->createPointerConstraintsV1(m_display);
403
    m_dataDeviceManager = m_display->createDataDeviceManager(m_display);
404
    m_display->createDataControlDeviceManagerV1(m_display);
405
    m_display->createPrimarySelectionDeviceManagerV1(m_display);
406 407
    m_idle = m_display->createIdle(m_display);
    auto idleInhibition = new IdleInhibition(m_idle);
408
    connect(this, &WaylandServer::shellClientAdded, idleInhibition, &IdleInhibition::registerClient);
409
    m_display->createIdleInhibitManagerV1(m_display);
410 411 412 413
    m_plasmaShell = m_display->createPlasmaShell(m_display);
    m_plasmaShell->create();
    connect(m_plasmaShell, &PlasmaShellInterface::surfaceCreated,
        [this] (PlasmaShellSurfaceInterface *surface) {
414
            if (XdgSurfaceClient *client = findXdgSurfaceClient(surface->surface())) {
415
                client->installPlasmaShellSurface(surface);
416
                return;
417
            }
418

419 420 421 422
            m_plasmaShellSurfaces.append(surface);
            connect(surface, &QObject::destroyed, this, [this, surface] {
                m_plasmaShellSurfaces.removeOne(surface);
            });
423 424
        }
    );
David Edmundson's avatar
David Edmundson committed
425 426 427
    m_appMenuManager = m_display->createAppMenuManagerInterface(m_display);
    connect(m_appMenuManager, &AppMenuManagerInterface::appMenuCreated,
        [this] (AppMenuInterface *appMenu) {
428
            if (XdgToplevelClient *client = findXdgToplevelClient(appMenu->surface())) {
David Edmundson's avatar
David Edmundson committed
429 430 431 432
                client->installAppMenu(appMenu);
            }
        }
    );
433 434 435
    m_paletteManager = m_display->createServerSideDecorationPaletteManager(m_display);
    connect(m_paletteManager, &ServerSideDecorationPaletteManagerInterface::paletteCreated,
        [this] (ServerSideDecorationPaletteInterface *palette) {
436
            if (XdgToplevelClient *client = findXdgToplevelClient(palette->surface())) {
437 438 439 440
                client->installPalette(palette);
            }
        }
    );
David Edmundson's avatar
David Edmundson committed
441

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    m_windowManagement = m_display->createPlasmaWindowManagement(m_display);
    m_windowManagement->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Disabled);
    connect(m_windowManagement, &PlasmaWindowManagementInterface::requestChangeShowingDesktop, this,
        [] (PlasmaWindowManagementInterface::ShowingDesktopState state) {
            if (!workspace()) {
                return;
            }
            bool set = false;
            switch (state) {
            case PlasmaWindowManagementInterface::ShowingDesktopState::Disabled:
                set = false;
                break;
            case PlasmaWindowManagementInterface::ShowingDesktopState::Enabled:
                set = true;
                break;
            default:
                Q_UNREACHABLE();
                break;
            }
            if (set == workspace()->showingDesktop()) {
                return;
            }
            workspace()->setShowingDesktop(set);
        }
    );
467 468 469 470

    m_virtualDesktopManagement = m_display->createPlasmaVirtualDesktopManagement(m_display);
    m_windowManagement->setPlasmaVirtualDesktopManagementInterface(m_virtualDesktopManagement);

471
    m_display->createShadowManager(m_display);
472

473
    m_display->createDpmsManager(m_display);
474 475 476

    m_decorationManager = m_display->createServerSideDecorationManager(m_display);
    connect(m_decorationManager, &ServerSideDecorationManagerInterface::decorationCreated, this,
477 478 479
        [this] (ServerSideDecorationInterface *decoration) {
            if (XdgToplevelClient *client = findXdgToplevelClient(decoration->surface())) {
                client->installServerDecoration(decoration);
480
            }
481 482
            connect(decoration, &ServerSideDecorationInterface::modeRequested, this,
                [decoration] (ServerSideDecorationManagerInterface::Mode mode) {
483
                    // always acknowledge the requested mode
484
                    decoration->setMode(mode);
485 486
                }
            );
487 488
        }
    );
489 490 491

    m_outputManagement = m_display->createOutputManagement(m_display);
    connect(m_outputManagement, &OutputManagementInterface::configurationChangeRequested,
492
            this, [](KWaylandServer::OutputConfigurationInterface *config) {
493
                kwinApp()->platform()->requestOutputsChange(config);
494
    });
495
    m_outputManagement->create();
496

497
    m_xdgOutputManagerV1 = m_display->createXdgOutputManagerV1(m_display);
David Edmundson's avatar
David Edmundson committed
498

499
    m_display->createSubCompositor(m_display);
500

501
    m_XdgForeign = m_display->createXdgForeignV2Interface(m_display);
Marco Martin's avatar
Marco Martin committed
502

503 504
    m_keyState = m_display->createKeyStateInterface(m_display);

505 506
    m_inputMethod = m_display->createInputMethodInterface(m_display);

507
    return true;
508 509
}

510
KWaylandServer::LinuxDmabufUnstableV1Interface *WaylandServer::linuxDmabuf()
Roman Gilg's avatar
Roman Gilg committed
511 512 513 514 515 516 517 518
{
    if (!m_linuxDmabuf) {
        m_linuxDmabuf = m_display->createLinuxDmabufInterface(m_display);
        m_linuxDmabuf->create();
    }
    return m_linuxDmabuf;
}

Marco Martin's avatar
Marco Martin committed
519 520 521 522 523
SurfaceInterface *WaylandServer::findForeignTransientForSurface(SurfaceInterface *surface)
{
    return m_XdgForeign->transientFor(surface);
}

524
void WaylandServer::shellClientShown(Toplevel *toplevel)
525
{
526 527 528
    AbstractClient *client = qobject_cast<AbstractClient *>(toplevel);
    if (!client) {
        qCWarning(KWIN_CORE) << "Failed to cast a Toplevel which is supposed to be an AbstractClient to AbstractClient";
529 530
        return;
    }
531 532
    disconnect(client, &AbstractClient::windowShown, this, &WaylandServer::shellClientShown);
    emit shellClientAdded(client);
533 534
}

535 536
void WaylandServer::initWorkspace()
{
537 538
    VirtualDesktopManager::self()->setVirtualDesktopManagement(m_virtualDesktopManagement);

539 540 541
    if (m_windowManagement) {
        connect(workspace(), &Workspace::showingDesktopChanged, this,
            [this] (bool set) {
542
                using namespace KWaylandServer;
543 544 545 546 547 548
                m_windowManagement->setShowingDesktopState(set ?
                    PlasmaWindowManagementInterface::ShowingDesktopState::Enabled :
                    PlasmaWindowManagementInterface::ShowingDesktopState::Disabled
                );
            }
        );
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563

        connect(workspace(), &Workspace::workspaceInitialized, this, [this] {
            auto f = [this] () {
                QVector<quint32> ids;
                for (Toplevel *toplevel : workspace()->stackingOrder()) {
                    auto *client = qobject_cast<AbstractClient *>(toplevel);
                    if (client && client->windowManagementInterface()) {
                        ids << client->windowManagementInterface()->internalId();
                    }
                }
                m_windowManagement->setStackingOrder(ids);
            };
            f();
            connect(workspace(), &Workspace::stackingOrderChanged, this, f);
        });
564
    }
565

566
    if (hasScreenLockerIntegration()) {
567 568 569 570 571 572 573 574 575
        if (m_internalConnection.interfacesAnnounced) {
            initScreenLocker();
        } else {
            connect(m_internalConnection.registry, &KWayland::Client::Registry::interfacesAnnounced, this, &WaylandServer::initScreenLocker);
        }
    } else {
        emit initialized();
    }
}
576

577 578
void WaylandServer::initScreenLocker()
{
Roman Gilg's avatar
Roman Gilg committed
579 580
    auto *screenLockerApp = ScreenLocker::KSldApp::self();

581 582 583
    ScreenLocker::KSldApp::self()->setGreeterEnvironment(kwinApp()->processStartupEnvironment());
    ScreenLocker::KSldApp::self()->initialize();

Roman Gilg's avatar
Roman Gilg committed
584 585 586 587 588 589 590 591 592 593 594 595 596
    connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::aboutToLock, this,
        [this, screenLockerApp] () {
            if (m_screenLockerClientConnection) {
                // Already sent data to KScreenLocker.
                return;
            }
            int clientFd = createScreenLockerConnection();
            if (clientFd < 0) {
                return;
            }
            ScreenLocker::KSldApp::self()->setWaylandFd(clientFd);

            for (auto *seat : m_display->seats()) {
597
                connect(seat, &KWaylandServer::SeatInterface::timestampChanged,
Roman Gilg's avatar
Roman Gilg committed
598 599
                        screenLockerApp, &ScreenLocker::KSldApp::userActivity);
            }
600 601
        }
    );
602

603
    connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::unlocked, this,
Roman Gilg's avatar
Roman Gilg committed
604 605 606 607 608 609 610 611
        [this, screenLockerApp] () {
            if (m_screenLockerClientConnection) {
                m_screenLockerClientConnection->destroy();
                delete m_screenLockerClientConnection;
                m_screenLockerClientConnection = nullptr;
            }

            for (auto *seat : m_display->seats()) {
612
                disconnect(seat, &KWaylandServer::SeatInterface::timestampChanged,
Roman Gilg's avatar
Roman Gilg committed
613 614 615
                           screenLockerApp, &ScreenLocker::KSldApp::userActivity);
            }
            ScreenLocker::KSldApp::self()->setWaylandFd(-1);
616
        }
617 618
    );

619
    if (m_initFlags.testFlag(InitializationFlag::LockScreen)) {
620
        ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate);
621
    }
622
    emit initialized();
623
}
624

625
WaylandServer::SocketPairConnection WaylandServer::createConnection()
626
{
627
    SocketPairConnection ret;
628 629 630
    int sx[2];
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) {
        qCWarning(KWIN_CORE) << "Could not create socket";
631 632 633 634 635 636 637
        return ret;
    }
    ret.connection = m_display->createClient(sx[0]);
    ret.fd = sx[1];
    return ret;
}

Roman Gilg's avatar
Roman Gilg committed
638 639 640 641 642 643 644
int WaylandServer::createScreenLockerConnection()
{
    const auto socket = createConnection();
    if (!socket.connection) {
        return -1;
    }
    m_screenLockerClientConnection = socket.connection;
645
    connect(m_screenLockerClientConnection, &KWaylandServer::ClientConnection::disconnected,
Roman Gilg's avatar
Roman Gilg committed
646 647 648 649
            this, [this] { m_screenLockerClientConnection = nullptr; });
    return socket.fd;
}

650 651 652 653
int WaylandServer::createXWaylandConnection()
{
    const auto socket = createConnection();
    if (!socket.connection) {
654 655
        return -1;
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
656
    m_xwaylandConnection = socket.connection;
657
    return socket.fd;
658 659
}

660 661
void WaylandServer::destroyXWaylandConnection()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
662
    if (!m_xwaylandConnection) {
663 664
        return;
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
665 666
    m_xwaylandConnection->destroy();
    m_xwaylandConnection = nullptr;
667 668
}

669 670
int WaylandServer::createInputMethodConnection()
{
671 672
    const auto socket = createConnection();
    if (!socket.connection) {
673 674
        return -1;
    }
675 676
    m_inputMethodServerConnection = socket.connection;
    return socket.fd;
677 678
}

679 680 681 682 683 684 685 686 687
void WaylandServer::destroyInputMethodConnection()
{
    if (!m_inputMethodServerConnection) {
        return;
    }
    m_inputMethodServerConnection->destroy();
    m_inputMethodServerConnection = nullptr;
}

688 689
void WaylandServer::createInternalConnection()
{
690 691
    const auto socket = createConnection();
    if (!socket.connection) {
692 693
        return;
    }
694
    m_internalConnection.server = socket.connection;
695
    using namespace KWayland::Client;
696
    m_internalConnection.client = new ConnectionThread();
697
    m_internalConnection.client->setSocketFd(socket.fd);
698 699 700 701
    m_internalConnection.clientThread = new QThread;
    m_internalConnection.client->moveToThread(m_internalConnection.clientThread);
    m_internalConnection.clientThread->start();

702 703
    connect(m_internalConnection.client, &ConnectionThread::connected, this,
        [this] {
704
            Registry *registry = new Registry(this);
David Edmundson's avatar
David Edmundson committed
705
            EventQueue *eventQueue = new EventQueue(registry);
706 707
            eventQueue->setup(m_internalConnection.client);
            registry->setEventQueue(eventQueue);
708
            registry->create(m_internalConnection.client);
709
            m_internalConnection.registry = registry;
710
            connect(registry, &Registry::interfacesAnnounced, this,
711
                [this, registry] {
712
                    m_internalConnection.interfacesAnnounced = true;
713

714 715 716 717
                    const auto compInterface = registry->interface(Registry::Interface::Compositor);
                    if (compInterface.name != 0) {
                        m_internalConnection.compositor = registry->createCompositor(compInterface.name, compInterface.version, this);
                    }
718 719 720 721 722 723 724 725
                    const auto seatInterface = registry->interface(Registry::Interface::Seat);
                    if (seatInterface.name != 0) {
                        m_internalConnection.seat = registry->createSeat(seatInterface.name, seatInterface.version, this);
                    }
                    const auto ddmInterface = registry->interface(Registry::Interface::DataDeviceManager);
                    if (ddmInterface.name != 0) {
                        m_internalConnection.ddm = registry->createDataDeviceManager(ddmInterface.name, ddmInterface.version, this);
                    }
726 727
                }
            );
728 729 730 731 732 733
            registry->setup();
        }
    );
    m_internalConnection.client->initConnection();
}

734
void WaylandServer::removeClient(AbstractClient *c)
735 736 737 738 739
{
    m_clients.removeAll(c);
    emit shellClientRemoved(c);
}

740 741 742 743 744
void WaylandServer::dispatch()
{
    if (!m_display) {
        return;
    }
745 746
    if (m_internalConnection.server) {
        m_internalConnection.server->flush();
747 748 749 750
    }
    m_display->dispatchEvents(0);
}

751
static AbstractClient *findClientInList(const QList<AbstractClient *> &clients, quint32 id)
752
{
753
    auto it = std::find_if(clients.begin(), clients.end(),
754
        [id] (AbstractClient *c) {
755 756 757
            return c->windowId() == id;
        }
    );
758
    if (it == clients.end()) {
759 760 761 762 763
        return nullptr;
    }
    return *it;
}

764
static AbstractClient *findClientInList(const QList<AbstractClient *> &clients, KWaylandServer::SurfaceInterface *surface)
765 766
{
    auto it = std::find_if(clients.begin(), clients.end(),
767
        [surface] (AbstractClient *c) {
768 769 770 771 772 773 774 775 776
            return c->surface() == surface;
        }
    );
    if (it == clients.end()) {
        return nullptr;
    }
    return *it;
}

777
AbstractClient *WaylandServer::findClient(quint32 id) const
778 779 780 781
{
    if (id == 0) {
        return nullptr;
    }
782
    if (AbstractClient *c = findClientInList(m_clients, id)) {
783 784 785 786 787
        return c;
    }
    return nullptr;
}

788
AbstractClient *WaylandServer::findClient(SurfaceInterface *surface) const
789 790 791 792
{
    if (!surface) {
        return nullptr;
    }
793
    if (AbstractClient *c = findClientInList(m_clients, surface)) {
794 795 796 797 798
        return c;
    }
    return nullptr;
}

799
XdgToplevelClient *WaylandServer::findXdgToplevelClient(SurfaceInterface *surface) const
800
{
801
    return qobject_cast<XdgToplevelClient *>(findClient(surface));
802 803
}

804 805 806 807 808
XdgSurfaceClient *WaylandServer::findXdgSurfaceClient(SurfaceInterface *surface) const
{
    return qobject_cast<XdgSurfaceClient *>(findClient(surface));
}

809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
quint32 WaylandServer::createWindowId(SurfaceInterface *surface)
{
    auto it = m_clientIds.constFind(surface->client());
    quint16 clientId = 0;
    if (it != m_clientIds.constEnd()) {
        clientId = it.value();
    } else {
        clientId = createClientId(surface->client());
    }
    Q_ASSERT(clientId != 0);
    quint32 id = clientId;
    // TODO: this does not prevent that two surfaces of same client get same id
    id = (id << 16) | (surface->id() & 0xFFFF);
    if (findClient(id)) {
        qCWarning(KWIN_CORE) << "Invalid client windowId generated:" << id;
        return 0;
    }
    return id;
}

quint16 WaylandServer::createClientId(ClientConnection *c)
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
831
    const QSet<unsigned short> ids(m_clientIds.constBegin(), m_clientIds.constEnd());
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
    quint16 id = 1;
    if (!ids.isEmpty()) {
        for (quint16 i = ids.count() + 1; i >= 1 ; i--) {
            if (!ids.contains(i)) {
                id = i;
                break;
            }
        }
    }
    Q_ASSERT(!ids.contains(id));
    m_clientIds.insert(c, id);
    connect(c, &ClientConnection::disconnected, this,
        [this] (ClientConnection *c) {
            m_clientIds.remove(c);
        }
    );
    return id;
}

851 852
bool WaylandServer::isScreenLocked() const
{
853 854 855
    if (!hasScreenLockerIntegration()) {
        return false;
    }
856 857
    return ScreenLocker::KSldApp::self()->lockState() == ScreenLocker::KSldApp::Locked ||
           ScreenLocker::KSldApp::self()->lockState() == ScreenLocker::KSldApp::AcquiringLock;
858 859
}

860 861
bool WaylandServer::hasScreenLockerIntegration() const
{
862
    return !m_initFlags.testFlag(InitializationFlag::NoLockScreenIntegration);
863 864
}

865 866
bool WaylandServer::hasGlobalShortcutSupport() const
{
867
    return !m_initFlags.testFlag(InitializationFlag::NoGlobalShortcuts);
868 869
}

870 871 872 873 874 875 876
void WaylandServer::simulateUserActivity()
{
    if (m_idle) {
        m_idle->simulateUserActivity();
    }
}

877 878 879 880 881 882 883 884 885 886
void WaylandServer::updateKeyState(KWin::Xkb::LEDs leds)
{
    if (!m_keyState)
        return;

    m_keyState->setState(KeyStateInterface::Key::CapsLock, leds & KWin::Xkb::LED::CapsLock ? KeyStateInterface::State::Locked : KeyStateInterface::State::Unlocked);
    m_keyState->setState(KeyStateInterface::Key::NumLock, leds & KWin::Xkb::LED::NumLock ? KeyStateInterface::State::Locked : KeyStateInterface::State::Unlocked);
    m_keyState->setState(KeyStateInterface::Key::ScrollLock, leds & KWin::Xkb::LED::ScrollLock ? KeyStateInterface::State::Locked : KeyStateInterface::State::Unlocked);
}

887 888 889 890 891 892 893 894 895 896
bool WaylandServer::isKeyboardShortcutsInhibited() const
{
    auto surface = seat()->focusedKeyboardSurface();
    if (surface) {
        auto inhibitor = keyboardShortcutsInhibitManager()->findInhibitor(surface, seat());
        return inhibitor && inhibitor->isActive();
    }
    return false;
}

897
}