File indexing completed on 2024-06-09 05:25:27

0001 /*
0002     SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "wayland_display.h"
0008 #include "utils/memorymap.h"
0009 #include "wayland_logging.h"
0010 
0011 #include <KWayland/Client/compositor.h>
0012 #include <KWayland/Client/pointerconstraints.h>
0013 #include <KWayland/Client/pointergestures.h>
0014 #include <KWayland/Client/registry.h>
0015 #include <KWayland/Client/relativepointer.h>
0016 #include <KWayland/Client/seat.h>
0017 #include <KWayland/Client/subcompositor.h>
0018 #include <KWayland/Client/xdgdecoration.h>
0019 #include <KWayland/Client/xdgshell.h>
0020 
0021 #include <QMutex>
0022 #include <QThread>
0023 #include <QWaitCondition>
0024 
0025 #include <drm_fourcc.h>
0026 #include <fcntl.h>
0027 #include <poll.h>
0028 #include <span>
0029 #include <unistd.h>
0030 #include <wayland-client.h>
0031 #include <xf86drm.h>
0032 
0033 // Generated in src/wayland.
0034 #include "wayland-linux-dmabuf-unstable-v1-client-protocol.h"
0035 #include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
0036 #include "wayland-pointer-gestures-unstable-v1-server-protocol.h"
0037 #include "wayland-relative-pointer-unstable-v1-client-protocol.h"
0038 #include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
0039 #include "wayland-xdg-shell-client-protocol.h"
0040 
0041 namespace KWin
0042 {
0043 namespace Wayland
0044 {
0045 
0046 class WaylandEventThread : public QThread
0047 {
0048     Q_OBJECT
0049 
0050 public:
0051     WaylandEventThread(wl_display *display)
0052         : m_display(display)
0053         , m_fd(wl_display_get_fd(display))
0054         , m_quitPipe{-1, -1}
0055         , m_reading(true)
0056         , m_quitting(false)
0057     {
0058         if (pipe2(m_quitPipe, O_CLOEXEC) == -1) {
0059             qCWarning(KWIN_WAYLAND_BACKEND) << "Failed to create quite pipe in WaylandEventThread";
0060         }
0061     }
0062 
0063     ~WaylandEventThread() override
0064     {
0065         if (m_quitPipe[0] != -1) {
0066             close(m_quitPipe[0]);
0067             close(m_quitPipe[1]);
0068         }
0069     }
0070 
0071     void dispatch()
0072     {
0073         while (true) {
0074             if (wl_display_dispatch_pending(m_display) < 0) {
0075                 qFatal("Wayland connection broke");
0076             }
0077 
0078             wl_display_flush(m_display);
0079 
0080             if (m_reading.loadAcquire()) {
0081                 break;
0082             }
0083 
0084             if (wl_display_prepare_read(m_display) == 0) {
0085                 QMutexLocker lock(&m_mutex);
0086                 m_reading.storeRelease(true);
0087                 m_cond.wakeOne();
0088                 break;
0089             }
0090         }
0091     }
0092 
0093     void stop()
0094     {
0095         if (m_quitPipe[1] != -1) {
0096             write(m_quitPipe[1], "\0", 1);
0097         }
0098 
0099         m_mutex.lock();
0100         m_quitting = true;
0101         m_cond.wakeOne();
0102         m_mutex.unlock();
0103 
0104         wait();
0105     }
0106 
0107 Q_SIGNALS:
0108     void available();
0109 
0110 protected:
0111     void run() override
0112     {
0113         while (true) {
0114             m_reading.storeRelease(false);
0115 
0116             Q_EMIT available();
0117 
0118             m_mutex.lock();
0119             while (!m_reading.loadRelaxed() && !m_quitting) {
0120                 m_cond.wait(&m_mutex);
0121             }
0122             m_mutex.unlock();
0123 
0124             if (m_quitting) {
0125                 break;
0126             }
0127 
0128             pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_quitPipe[0], POLLIN, 0 } };
0129             poll(fds, 2, -1);
0130 
0131             if (fds[1].revents & POLLIN) {
0132                 wl_display_cancel_read(m_display);
0133                 break;
0134             }
0135 
0136             if (fds[0].revents & POLLIN) {
0137                 wl_display_read_events(m_display);
0138             } else {
0139                 wl_display_cancel_read(m_display);
0140             }
0141         }
0142     }
0143 
0144 private:
0145     wl_display *const m_display;
0146     int m_fd;
0147     int m_quitPipe[2];
0148     QAtomicInteger<bool> m_reading;
0149     QMutex m_mutex;
0150     QWaitCondition m_cond;
0151     bool m_quitting;
0152 };
0153 
0154 static dev_t deserializeDeviceId(wl_array *data)
0155 {
0156     Q_ASSERT(sizeof(dev_t) == data->size);
0157     dev_t ret;
0158     std::memcpy(&ret, data->data, data->size);
0159     return ret;
0160 }
0161 
0162 class WaylandLinuxDmabufFeedbackV1
0163 {
0164 public:
0165     WaylandLinuxDmabufFeedbackV1(zwp_linux_dmabuf_feedback_v1 *feedback)
0166         : feedback(feedback)
0167     {
0168         static const struct zwp_linux_dmabuf_feedback_v1_listener feedbackListener = {
0169             .done = done,
0170             .format_table = format_table,
0171             .main_device = main_device,
0172             .tranche_done = tranche_done,
0173             .tranche_target_device = tranche_target_device,
0174             .tranche_formats = tranche_formats,
0175             .tranche_flags = tranche_flags,
0176         };
0177         zwp_linux_dmabuf_feedback_v1_add_listener(feedback, &feedbackListener, this);
0178     }
0179 
0180     ~WaylandLinuxDmabufFeedbackV1()
0181     {
0182         zwp_linux_dmabuf_feedback_v1_destroy(feedback);
0183     }
0184 
0185     zwp_linux_dmabuf_feedback_v1 *feedback;
0186     QByteArray mainDevice;
0187     dev_t mainDeviceId = 0;
0188     dev_t trancheDeviceId = 0;
0189     MemoryMap formatTable;
0190     QHash<uint32_t, QList<uint64_t>> formats;
0191 
0192 private:
0193     static void done(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1)
0194     {
0195         // Nothing to do
0196     }
0197 
0198     static void format_table(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, int32_t fd, uint32_t size)
0199     {
0200         WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
0201 
0202         feedback->formatTable = MemoryMap(size, PROT_READ, MAP_PRIVATE, fd, 0);
0203         close(fd);
0204     }
0205 
0206     static void main_device(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *deviceId)
0207     {
0208         WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
0209 
0210         feedback->mainDeviceId = deserializeDeviceId(deviceId);
0211 
0212         drmDevice *device = nullptr;
0213         if (drmGetDeviceFromDevId(feedback->mainDeviceId, 0, &device) != 0) {
0214             qCWarning(KWIN_WAYLAND_BACKEND) << "drmGetDeviceFromDevId() failed";
0215             return;
0216         }
0217 
0218         if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
0219             feedback->mainDevice = QByteArray(device->nodes[DRM_NODE_RENDER]);
0220         } else if (device->available_nodes & (1 << DRM_NODE_PRIMARY)) {
0221             // We can't reliably find the render node from the primary node if the display and
0222             // render devices are split, so just fallback to the primary node.
0223             feedback->mainDevice = QByteArray(device->nodes[DRM_NODE_PRIMARY]);
0224         }
0225 
0226         drmFreeDevice(&device);
0227     }
0228 
0229     static void tranche_done(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1)
0230     {
0231         WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
0232 
0233         feedback->trancheDeviceId = 0;
0234         feedback->formatTable = MemoryMap{};
0235     }
0236 
0237     static void tranche_target_device(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *deviceId)
0238     {
0239         WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
0240 
0241         feedback->trancheDeviceId = deserializeDeviceId(deviceId);
0242     }
0243 
0244     static void tranche_formats(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *indices)
0245     {
0246         WaylandLinuxDmabufFeedbackV1 *feedback = static_cast<WaylandLinuxDmabufFeedbackV1 *>(data);
0247         if (!feedback->formatTable.isValid()) {
0248             return;
0249         }
0250         if (feedback->mainDeviceId != feedback->trancheDeviceId) {
0251             return;
0252         }
0253 
0254         struct linux_dmabuf_feedback_v1_table_entry
0255         {
0256             uint32_t format;
0257             uint32_t pad; // unused
0258             uint64_t modifier;
0259         };
0260 
0261         const auto entries = static_cast<linux_dmabuf_feedback_v1_table_entry *>(feedback->formatTable.data());
0262         for (const uint16_t &index : std::span(static_cast<uint16_t *>(indices->data), indices->size / sizeof(uint16_t))) {
0263             const linux_dmabuf_feedback_v1_table_entry &entry = entries[index];
0264             feedback->formats[entry.format].append(entry.modifier);
0265         }
0266     }
0267 
0268     static void tranche_flags(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags)
0269     {
0270         // Nothing to do
0271     }
0272 };
0273 
0274 WaylandLinuxDmabufV1::WaylandLinuxDmabufV1(wl_registry *registry, uint32_t name, uint32_t version)
0275 {
0276     m_dmabuf = static_cast<zwp_linux_dmabuf_v1 *>(wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version));
0277 
0278     static const struct zwp_linux_dmabuf_v1_listener dmabufListener = {
0279         .format = format,
0280         .modifier = modifier,
0281     };
0282     zwp_linux_dmabuf_v1_add_listener(m_dmabuf, &dmabufListener, this);
0283 
0284     m_defaultFeedback = std::make_unique<WaylandLinuxDmabufFeedbackV1>(zwp_linux_dmabuf_v1_get_default_feedback(m_dmabuf));
0285 }
0286 
0287 WaylandLinuxDmabufV1::~WaylandLinuxDmabufV1()
0288 {
0289     zwp_linux_dmabuf_v1_destroy(m_dmabuf);
0290 }
0291 
0292 zwp_linux_dmabuf_v1 *WaylandLinuxDmabufV1::handle() const
0293 {
0294     return m_dmabuf;
0295 }
0296 
0297 QByteArray WaylandLinuxDmabufV1::mainDevice() const
0298 {
0299     return m_defaultFeedback->mainDevice;
0300 }
0301 
0302 QHash<uint32_t, QList<uint64_t>> WaylandLinuxDmabufV1::formats() const
0303 {
0304     return m_defaultFeedback->formats;
0305 }
0306 
0307 void WaylandLinuxDmabufV1::format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format)
0308 {
0309     // Not sent in v4 and onward.
0310 }
0311 
0312 void WaylandLinuxDmabufV1::modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo)
0313 {
0314     // Not sent in v4 and onward.
0315 }
0316 
0317 WaylandDisplay::WaylandDisplay()
0318 {
0319 }
0320 
0321 WaylandDisplay::~WaylandDisplay()
0322 {
0323     m_eventThread->stop();
0324     m_eventThread.reset();
0325 
0326     m_compositor.reset();
0327     m_pointerConstraints.reset();
0328     m_pointerGestures.reset();
0329     m_relativePointerManager.reset();
0330     m_seat.reset();
0331     m_xdgDecorationManager.reset();
0332     m_xdgShell.reset();
0333     m_linuxDmabuf.reset();
0334 
0335     if (m_shm) {
0336         wl_shm_destroy(m_shm);
0337     }
0338     if (m_registry) {
0339         wl_registry_destroy(m_registry);
0340     }
0341     if (m_display) {
0342         wl_display_disconnect(m_display);
0343     }
0344 }
0345 
0346 void WaylandDisplay::flush()
0347 {
0348     m_eventThread->dispatch();
0349 }
0350 
0351 bool WaylandDisplay::initialize(const QString &socketName)
0352 {
0353     m_display = wl_display_connect(socketName.toUtf8());
0354     if (!m_display) {
0355         return false;
0356     }
0357 
0358     m_eventThread = std::make_unique<WaylandEventThread>(m_display);
0359     connect(m_eventThread.get(), &WaylandEventThread::available, this, &WaylandDisplay::flush, Qt::QueuedConnection);
0360     m_eventThread->start();
0361 
0362     static wl_registry_listener registryListener {
0363         .global = registry_global,
0364         .global_remove = registry_global_remove,
0365     };
0366     m_registry = wl_display_get_registry(m_display);
0367     wl_registry_add_listener(m_registry, &registryListener, this);
0368     wl_display_roundtrip(m_display);
0369     wl_display_roundtrip(m_display); // get dmabuf formats
0370 
0371     return true;
0372 }
0373 
0374 wl_display *WaylandDisplay::nativeDisplay() const
0375 {
0376     return m_display;
0377 }
0378 
0379 KWayland::Client::Compositor *WaylandDisplay::compositor() const
0380 {
0381     return m_compositor.get();
0382 }
0383 
0384 KWayland::Client::PointerConstraints *WaylandDisplay::pointerConstraints() const
0385 {
0386     return m_pointerConstraints.get();
0387 }
0388 
0389 KWayland::Client::PointerGestures *WaylandDisplay::pointerGestures() const
0390 {
0391     return m_pointerGestures.get();
0392 }
0393 
0394 KWayland::Client::RelativePointerManager *WaylandDisplay::relativePointerManager() const
0395 {
0396     return m_relativePointerManager.get();
0397 }
0398 
0399 wl_shm *WaylandDisplay::shm() const
0400 {
0401     return m_shm;
0402 }
0403 
0404 KWayland::Client::Seat *WaylandDisplay::seat() const
0405 {
0406     return m_seat.get();
0407 }
0408 
0409 KWayland::Client::XdgShell *WaylandDisplay::xdgShell() const
0410 {
0411     return m_xdgShell.get();
0412 }
0413 
0414 KWayland::Client::XdgDecorationManager *WaylandDisplay::xdgDecorationManager() const
0415 {
0416     return m_xdgDecorationManager.get();
0417 }
0418 
0419 WaylandLinuxDmabufV1 *WaylandDisplay::linuxDmabuf() const
0420 {
0421     return m_linuxDmabuf.get();
0422 }
0423 
0424 void WaylandDisplay::registry_global(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version)
0425 {
0426     WaylandDisplay *display = static_cast<WaylandDisplay *>(data);
0427 
0428     if (strcmp(interface, wl_compositor_interface.name) == 0) {
0429         if (version < 4) {
0430             qFatal("wl_compositor version 4 or later is required");
0431         }
0432         display->m_compositor = std::make_unique<KWayland::Client::Compositor>();
0433         display->m_compositor->setup(static_cast<wl_compositor *>(wl_registry_bind(registry, name, &wl_compositor_interface, std::min(version, 4u))));
0434     } else if (strcmp(interface, wl_shm_interface.name) == 0) {
0435         display->m_shm = static_cast<wl_shm *>(wl_registry_bind(registry, name, &wl_shm_interface, std::min(version, 1u)));
0436     } else if (strcmp(interface, wl_seat_interface.name) == 0) {
0437         display->m_seat = std::make_unique<KWayland::Client::Seat>();
0438         display->m_seat->setup(static_cast<wl_seat *>(wl_registry_bind(registry, name, &wl_seat_interface, std::min(version, 5u))));
0439     } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
0440         display->m_xdgShell = std::make_unique<KWayland::Client::XdgShellStable>();
0441         display->m_xdgShell->setup(static_cast<xdg_wm_base *>(wl_registry_bind(registry, name, &xdg_wm_base_interface, std::min(version, 1u))));
0442     } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) {
0443         display->m_pointerConstraints = std::make_unique<KWayland::Client::PointerConstraints>();
0444         display->m_pointerConstraints->setup(static_cast<zwp_pointer_constraints_v1 *>(wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, std::min(version, 1u))));
0445     } else if (strcmp(interface, zwp_pointer_gestures_v1_interface.name) == 0) {
0446         display->m_pointerGestures = std::make_unique<KWayland::Client::PointerGestures>();
0447         display->m_pointerGestures->setup(static_cast<zwp_pointer_gestures_v1 *>(wl_registry_bind(registry, name, &zwp_pointer_gestures_v1_interface, std::min(version, 1u))));
0448     } else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
0449         display->m_relativePointerManager = std::make_unique<KWayland::Client::RelativePointerManager>();
0450         display->m_relativePointerManager->setup(static_cast<zwp_relative_pointer_manager_v1 *>(wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, std::min(version, 1u))));
0451     } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
0452         display->m_xdgDecorationManager = std::make_unique<KWayland::Client::XdgDecorationManager>();
0453         display->m_xdgDecorationManager->setup(static_cast<zxdg_decoration_manager_v1 *>(wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, std::min(version, 1u))));
0454     } else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
0455         if (version < 4) {
0456             qWarning("zwp_linux_dmabuf_v1 v4 or newer is needed");
0457             return;
0458         }
0459         display->m_linuxDmabuf = std::make_unique<WaylandLinuxDmabufV1>(registry, name, std::min(version, 4u));
0460     }
0461 }
0462 
0463 void WaylandDisplay::registry_global_remove(void *data, wl_registry *registry, uint32_t name)
0464 {
0465 }
0466 
0467 }
0468 }
0469 
0470 #include "wayland_display.moc"
0471 
0472 #include "moc_wayland_display.cpp"