File indexing completed on 2024-11-10 04:56:33
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, ®istryListener, 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"