File indexing completed on 2022-04-21 12:37:46

0001 /********************************************************************
0002  KSld - the KDE Screenlocker Daemon
0003  This file is part of the KDE project.
0004 
0005  Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007 This program is free software; you can redistribute it and/or modify
0008 it under the terms of the GNU General Public License as published by
0009 the Free Software Foundation; either version 2 of the License, or
0010 (at your option) any later version.
0011 
0012 This program is distributed in the hope that it will be useful,
0013 but WITHOUT ANY WARRANTY; without even the implied warranty of
0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015 GNU General Public License for more details.
0016 
0017 You should have received a copy of the GNU General Public License
0018 along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019 *********************************************************************/
0020 #include "waylandserver.h"
0021 // Qt
0022 #include <QAbstractEventDispatcher>
0023 #include <QCoreApplication>
0024 // ksld
0025 #include "kscreenlocker_logging.h"
0026 #include <config-kscreenlocker.h>
0027 // Wayland
0028 #include <wayland-ksld-server-protocol.h>
0029 // system
0030 #include <fcntl.h>
0031 #include <sys/socket.h>
0032 #include <sys/types.h>
0033 #include <unistd.h>
0034 
0035 namespace ScreenLocker
0036 {
0037 
0038 WaylandServer::WaylandServer(QObject *parent)
0039     : QObject(parent)
0040 {
0041     m_listener.server = this;
0042     m_listener.listener.notify = [](wl_listener *listener, void *data) {
0043         Q_UNUSED(data)
0044 
0045         WaylandServer *server = reinterpret_cast<Listener *>(listener)->server;
0046         server->m_greeter = nullptr;
0047     };
0048 }
0049 
0050 WaylandServer::~WaylandServer()
0051 {
0052     stop();
0053 }
0054 
0055 int WaylandServer::start()
0056 {
0057     stop();
0058 
0059     m_display = wl_display_create();
0060 
0061     wl_event_loop *eventLoop = wl_display_get_event_loop(m_display);
0062     const int fd = wl_event_loop_get_fd(eventLoop);
0063     if (fd == -1) {
0064         stop();
0065         return -1;
0066     }
0067 
0068     m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read);
0069     connect(m_notifier, &QSocketNotifier::activated, this, &WaylandServer::dispatchEvents);
0070 
0071     QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher();
0072     connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &WaylandServer::flush);
0073 
0074     int socketPair[2];
0075     if (socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair) == -1) {
0076         // failed creating socket
0077         stop();
0078         return -1;
0079     }
0080     fcntl(socketPair[0], F_SETFD, FD_CLOEXEC);
0081 
0082     m_greeter = wl_client_create(m_display, socketPair[0]);
0083     if (!m_greeter) {
0084         // failed creating the Wayland client
0085         stop();
0086         close(socketPair[0]);
0087         close(socketPair[1]);
0088         return -1;
0089     }
0090     wl_client_add_destroy_listener(m_greeter, &m_listener.listener);
0091 
0092     m_interface = wl_global_create(m_display, &org_kde_ksld_interface, 3, this, bind);
0093     return socketPair[1];
0094 }
0095 
0096 void WaylandServer::stop()
0097 {
0098     QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher();
0099     disconnect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &WaylandServer::flush);
0100 
0101     delete m_notifier;
0102     m_notifier = nullptr;
0103 
0104     if (m_interface) {
0105         wl_global_destroy(m_interface);
0106         m_interface = nullptr;
0107     }
0108     if (m_greeter) {
0109         wl_client_destroy(m_greeter);
0110         m_greeter = nullptr;
0111     }
0112     if (m_display) {
0113         wl_display_destroy(m_display);
0114         m_display = nullptr;
0115     }
0116 }
0117 
0118 void WaylandServer::flush()
0119 {
0120     wl_display_flush_clients(m_display);
0121 }
0122 
0123 void WaylandServer::dispatchEvents()
0124 {
0125     if (wl_event_loop_dispatch(wl_display_get_event_loop(m_display), 0) != 0) {
0126         qCWarning(KSCREENLOCKER) << "Error on dispatching WaylandServer event loop";
0127     }
0128 }
0129 
0130 void WaylandServer::bind(wl_client *client, void *data, uint32_t version, uint32_t id)
0131 {
0132     auto server = reinterpret_cast<WaylandServer *>(data);
0133     if (client != server->m_greeter) {
0134         // a proper error would be better
0135         wl_client_post_no_memory(client);
0136         return;
0137     }
0138 
0139     wl_resource *resource = wl_resource_create(server->m_greeter, &org_kde_ksld_interface, qMin(version, 1u), id);
0140     if (!resource) {
0141         wl_client_post_no_memory(client);
0142         return;
0143     }
0144 
0145     static const struct org_kde_ksld_interface s_interface = {
0146         .x11window =
0147             [](wl_client *client, wl_resource *resource, uint32_t id) {
0148                 auto s = reinterpret_cast<WaylandServer *>(wl_resource_get_user_data(resource));
0149                 if (s->m_greeter == client) {
0150                     Q_EMIT s->x11WindowAdded(id);
0151                 }
0152             },
0153     };
0154 
0155     wl_resource_set_implementation(resource, &s_interface, server, nullptr);
0156 }
0157 
0158 }