File indexing completed on 2024-03-24 17:02:36

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004 SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "waylandserver.h"
0007 // Qt
0008 #include <QAbstractEventDispatcher>
0009 #include <QCoreApplication>
0010 // ksld
0011 #include "kscreenlocker_logging.h"
0012 #include <config-kscreenlocker.h>
0013 // Wayland
0014 #include <wayland-ksld-server-protocol.h>
0015 // system
0016 #include <fcntl.h>
0017 #include <sys/socket.h>
0018 #include <sys/types.h>
0019 #include <unistd.h>
0020 
0021 namespace ScreenLocker
0022 {
0023 
0024 WaylandServer::WaylandServer(QObject *parent)
0025     : QObject(parent)
0026 {
0027     m_listener.server = this;
0028     m_listener.listener.notify = [](wl_listener *listener, void *data) {
0029         Q_UNUSED(data)
0030 
0031         WaylandServer *server = reinterpret_cast<Listener *>(listener)->server;
0032         server->m_greeter = nullptr;
0033     };
0034 }
0035 
0036 WaylandServer::~WaylandServer()
0037 {
0038     stop();
0039 }
0040 
0041 int WaylandServer::start()
0042 {
0043     stop();
0044 
0045     m_display = wl_display_create();
0046 
0047     wl_event_loop *eventLoop = wl_display_get_event_loop(m_display);
0048     const int fd = wl_event_loop_get_fd(eventLoop);
0049     if (fd == -1) {
0050         stop();
0051         return -1;
0052     }
0053 
0054     m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read);
0055     connect(m_notifier, &QSocketNotifier::activated, this, &WaylandServer::dispatchEvents);
0056 
0057     QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher();
0058     connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &WaylandServer::flush);
0059 
0060     int socketPair[2];
0061     if (socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair) == -1) {
0062         // failed creating socket
0063         stop();
0064         return -1;
0065     }
0066     fcntl(socketPair[0], F_SETFD, FD_CLOEXEC);
0067 
0068     m_greeter = wl_client_create(m_display, socketPair[0]);
0069     if (!m_greeter) {
0070         // failed creating the Wayland client
0071         stop();
0072         close(socketPair[0]);
0073         close(socketPair[1]);
0074         return -1;
0075     }
0076     wl_client_add_destroy_listener(m_greeter, &m_listener.listener);
0077 
0078     m_interface = wl_global_create(m_display, &org_kde_ksld_interface, 3, this, bind);
0079     return socketPair[1];
0080 }
0081 
0082 void WaylandServer::stop()
0083 {
0084     QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher();
0085     disconnect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &WaylandServer::flush);
0086 
0087     delete m_notifier;
0088     m_notifier = nullptr;
0089 
0090     if (m_interface) {
0091         wl_global_destroy(m_interface);
0092         m_interface = nullptr;
0093     }
0094     if (m_greeter) {
0095         wl_client_destroy(m_greeter);
0096         m_greeter = nullptr;
0097     }
0098     if (m_display) {
0099         wl_display_destroy(m_display);
0100         m_display = nullptr;
0101     }
0102 }
0103 
0104 void WaylandServer::flush()
0105 {
0106     wl_display_flush_clients(m_display);
0107 }
0108 
0109 void WaylandServer::dispatchEvents()
0110 {
0111     if (wl_event_loop_dispatch(wl_display_get_event_loop(m_display), 0) != 0) {
0112         qCWarning(KSCREENLOCKER) << "Error on dispatching WaylandServer event loop";
0113     }
0114 }
0115 
0116 void WaylandServer::bind(wl_client *client, void *data, uint32_t version, uint32_t id)
0117 {
0118     auto server = reinterpret_cast<WaylandServer *>(data);
0119     if (client != server->m_greeter) {
0120         // a proper error would be better
0121         wl_client_post_no_memory(client);
0122         return;
0123     }
0124 
0125     wl_resource *resource = wl_resource_create(server->m_greeter, &org_kde_ksld_interface, qMin(version, 1u), id);
0126     if (!resource) {
0127         wl_client_post_no_memory(client);
0128         return;
0129     }
0130 
0131     static const struct org_kde_ksld_interface s_interface = {
0132         .x11window =
0133             [](wl_client *client, wl_resource *resource, uint32_t id) {
0134                 auto s = reinterpret_cast<WaylandServer *>(wl_resource_get_user_data(resource));
0135                 if (s->m_greeter == client) {
0136                     Q_EMIT s->x11WindowAdded(id);
0137                 }
0138             },
0139     };
0140 
0141     wl_resource_set_implementation(resource, &s_interface, server, nullptr);
0142 }
0143 
0144 }