/* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-License-Identifier: GPL-2.0-or-later */ #include "waylandserver.h" // Qt #include #include // ksld #include "kscreenlocker_logging.h" #include // Wayland #include // system #include #include #include #include namespace ScreenLocker { WaylandServer::WaylandServer(QObject *parent) : QObject(parent) { m_listener.server = this; m_listener.listener.notify = [](wl_listener *listener, void *data) { Q_UNUSED(data) WaylandServer *server = reinterpret_cast(listener)->server; server->m_greeter = nullptr; }; } WaylandServer::~WaylandServer() { stop(); } int WaylandServer::start() { stop(); m_display = wl_display_create(); if (!m_display) { return -1; } wl_event_loop *eventLoop = wl_display_get_event_loop(m_display); const int fd = wl_event_loop_get_fd(eventLoop); if (fd == -1) { stop(); return -1; } m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read); connect(m_notifier, &QSocketNotifier::activated, this, &WaylandServer::dispatchEvents); QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &WaylandServer::flush); int socketPair[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair) == -1) { // failed creating socket stop(); return -1; } fcntl(socketPair[0], F_SETFD, FD_CLOEXEC); m_greeter = wl_client_create(m_display, socketPair[0]); if (!m_greeter) { // failed creating the Wayland client stop(); close(socketPair[0]); close(socketPair[1]); return -1; } wl_client_add_destroy_listener(m_greeter, &m_listener.listener); m_interface = wl_global_create(m_display, &org_kde_ksld_interface, 3, this, bind); return socketPair[1]; } void WaylandServer::stop() { QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); disconnect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &WaylandServer::flush); delete m_notifier; m_notifier = nullptr; if (m_interface) { wl_global_destroy(m_interface); m_interface = nullptr; } if (m_greeter) { wl_client_destroy(m_greeter); m_greeter = nullptr; } if (m_display) { wl_display_destroy(m_display); m_display = nullptr; } } void WaylandServer::flush() { wl_display_flush_clients(m_display); } void WaylandServer::dispatchEvents() { if (wl_event_loop_dispatch(wl_display_get_event_loop(m_display), 0) != 0) { qCWarning(KSCREENLOCKER) << "Error on dispatching WaylandServer event loop"; } } void WaylandServer::bind(wl_client *client, void *data, uint32_t version, uint32_t id) { auto server = reinterpret_cast(data); if (client != server->m_greeter) { // a proper error would be better wl_client_post_no_memory(client); return; } wl_resource *resource = wl_resource_create(server->m_greeter, &org_kde_ksld_interface, qMin(version, 1u), id); if (!resource) { wl_client_post_no_memory(client); return; } static const struct org_kde_ksld_interface s_interface = { .x11window = [](wl_client *client, wl_resource *resource, uint32_t id) { auto s = reinterpret_cast(wl_resource_get_user_data(resource)); if (s->m_greeter == client) { Q_EMIT s->x11WindowAdded(id); } }, }; wl_resource_set_implementation(resource, &s_interface, server, nullptr); } } #include "moc_waylandserver.cpp"