1 | // Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> |
---|---|
2 | // Copyright (C) 2020 The Qt Company Ltd. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
4 | |
5 | #include "qtwaylandcompositorglobal_p.h" |
6 | #include "qwaylandcompositor.h" |
7 | #include "qwaylandcompositor_p.h" |
8 | |
9 | #include <QtWaylandCompositor/qwaylandclient.h> |
10 | #include <QtWaylandCompositor/qwaylandseat.h> |
11 | #include <QtWaylandCompositor/qwaylandoutput.h> |
12 | #include <QtWaylandCompositor/qwaylandview.h> |
13 | #include <QtWaylandCompositor/qwaylandclient.h> |
14 | #include <QtWaylandCompositor/qwaylandkeyboard.h> |
15 | #include <QtWaylandCompositor/qwaylandpointer.h> |
16 | #include <QtWaylandCompositor/qwaylandtouch.h> |
17 | #include <QtWaylandCompositor/qwaylandsurfacegrabber.h> |
18 | |
19 | #include <QtWaylandCompositor/private/qwaylandkeyboard_p.h> |
20 | #include <QtWaylandCompositor/private/qwaylandsurface_p.h> |
21 | |
22 | #if QT_CONFIG(wayland_datadevice) |
23 | #include "wayland_wrapper/qwldatadevice_p.h" |
24 | #include "wayland_wrapper/qwldatadevicemanager_p.h" |
25 | #endif |
26 | #include "wayland_wrapper/qwlbuffermanager_p.h" |
27 | |
28 | #include "hardware_integration/qwlclientbufferintegration_p.h" |
29 | #include "hardware_integration/qwlclientbufferintegrationfactory_p.h" |
30 | #include "hardware_integration/qwlserverbufferintegration_p.h" |
31 | #include "hardware_integration/qwlserverbufferintegrationfactory_p.h" |
32 | |
33 | #if QT_CONFIG(opengl) |
34 | #include "hardware_integration/qwlhwintegration_p.h" |
35 | #endif |
36 | |
37 | #include "extensions/qwaylandqtwindowmanager.h" |
38 | |
39 | #include "qwaylandsharedmemoryformathelper_p.h" |
40 | |
41 | #include <QtCore/QCoreApplication> |
42 | #include <QtCore/QStringList> |
43 | #include <QtCore/QSocketNotifier> |
44 | #include <QStandardPaths> |
45 | |
46 | #include <QtGui/QDesktopServices> |
47 | #include <QtGui/QScreen> |
48 | |
49 | #include <QtGui/qpa/qwindowsysteminterface_p.h> |
50 | #include <QtGui/qpa/qplatformnativeinterface.h> |
51 | #include <QtGui/private/qguiapplication_p.h> |
52 | |
53 | #if QT_CONFIG(opengl) |
54 | # include <QOpenGLTextureBlitter> |
55 | # include <QOpenGLTexture> |
56 | # include <QOpenGLContext> |
57 | # include <QOpenGLFramebufferObject> |
58 | # include <QMatrix4x4> |
59 | #endif |
60 | |
61 | QT_BEGIN_NAMESPACE |
62 | |
63 | Q_LOGGING_CATEGORY(qLcWaylandCompositor, "qt.waylandcompositor") |
64 | Q_LOGGING_CATEGORY(qLcWaylandCompositorHardwareIntegration, "qt.waylandcompositor.hardwareintegration") |
65 | Q_LOGGING_CATEGORY(qLcWaylandCompositorInputMethods, "qt.waylandcompositor.inputmethods") |
66 | Q_LOGGING_CATEGORY(qLcWaylandCompositorTextInput, "qt.waylandcompositor.textinput") |
67 | |
68 | namespace QtWayland { |
69 | |
70 | class WindowSystemEventHandler : public QWindowSystemEventHandler |
71 | { |
72 | public: |
73 | WindowSystemEventHandler(QWaylandCompositor *c) : compositor(c) {} |
74 | bool sendEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) override |
75 | { |
76 | if (e->type == QWindowSystemInterfacePrivate::Key) { |
77 | QWindowSystemInterfacePrivate::KeyEvent *keyEvent = static_cast<QWindowSystemInterfacePrivate::KeyEvent *>(e); |
78 | handleKeyEvent(ke: keyEvent); |
79 | } else { |
80 | QWindowSystemEventHandler::sendEvent(event: e); |
81 | } |
82 | return true; |
83 | } |
84 | |
85 | void handleKeyEvent(QWindowSystemInterfacePrivate::KeyEvent *ke) |
86 | { |
87 | auto *seat = compositor->defaultSeat(); |
88 | if (!seat) |
89 | return; |
90 | |
91 | QWaylandKeyboardPrivate *keyb = QWaylandKeyboardPrivate::get(keyboard: seat->keyboard()); |
92 | |
93 | #if defined(Q_OS_QNX) |
94 | // The QNX platform plugin delivers scan codes that haven't been adjusted to be |
95 | // xkbcommon compatible. xkbcommon requires that the scan codes be bumped up by |
96 | // 8 because that's how evdev/XKB deliver scan codes. You might think that it |
97 | // would've been better to remove this (odd) requirement from xkbcommon on QNX |
98 | // but it turns out that conforming to it has much less impact. |
99 | static int offset = QGuiApplication::platformName() == QStringLiteral("qnx") ? 8 : 0; |
100 | ke->nativeScanCode += offset; |
101 | #endif |
102 | uint32_t code = ke->nativeScanCode; |
103 | if (code == 0) |
104 | code = seat->keyboard()->keyToScanCode(qtKey: ke->key); |
105 | bool isDown = ke->keyType == QEvent::KeyPress; |
106 | |
107 | #if QT_CONFIG(xkbcommon) |
108 | xkb_state *xkbState = keyb->xkbState(); |
109 | |
110 | const xkb_keysym_t sym = xkb_state_key_get_one_sym(state: xkbState, key: code); |
111 | Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(state: xkbState, keysym: sym); |
112 | int qtkey = QXkbCommon::keysymToQtKey(keysym: sym, modifiers, state: xkbState, code); |
113 | QString text = QXkbCommon::lookupString(state: xkbState, code); |
114 | |
115 | ke->key = qtkey; |
116 | ke->modifiers = modifiers; |
117 | ke->nativeVirtualKey = sym; |
118 | ke->nativeModifiers = keyb->xkbModsMask(); |
119 | ke->unicode = text; |
120 | #endif |
121 | if (!ke->repeat) |
122 | keyb->keyEvent(code, isDown ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED); |
123 | |
124 | QWindowSystemEventHandler::sendEvent(event: ke); |
125 | |
126 | if (!ke->repeat) { |
127 | keyb->maybeUpdateKeymap(); |
128 | keyb->updateModifierState(code, isDown ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED); |
129 | } |
130 | } |
131 | |
132 | QWaylandCompositor *compositor = nullptr; |
133 | }; |
134 | |
135 | } // namespace |
136 | |
137 | QWaylandCompositorPrivate::QWaylandCompositorPrivate(QWaylandCompositor *compositor) |
138 | { |
139 | // Create XDG_RUNTIME_DIR, if it does not already exist |
140 | QStandardPaths::writableLocation(type: QStandardPaths::RuntimeLocation); |
141 | |
142 | if (QGuiApplication::platformNativeInterface()) |
143 | display = static_cast<wl_display*>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration(resource: "server_wl_display")); |
144 | |
145 | if (!display) { |
146 | display = wl_display_create(); |
147 | ownsDisplay = true; |
148 | } |
149 | |
150 | eventHandler.reset(other: new QtWayland::WindowSystemEventHandler(compositor)); |
151 | timer.start(); |
152 | |
153 | QWindowSystemInterfacePrivate::installWindowSystemEventHandler(handler: eventHandler.data()); |
154 | |
155 | #if QT_CONFIG(xkbcommon) |
156 | mXkbContext.reset(p: xkb_context_new(flags: XKB_CONTEXT_NO_FLAGS)); |
157 | if (!mXkbContext) { |
158 | qWarning(msg: "Failed to create a XKB context: keymap will not be supported"); |
159 | return; |
160 | } |
161 | #endif |
162 | } |
163 | |
164 | void QWaylandCompositorPrivate::init() |
165 | { |
166 | Q_Q(QWaylandCompositor); |
167 | QStringList arguments = QCoreApplication::instance()->arguments(); |
168 | |
169 | if (socket_name.isEmpty()) { |
170 | const int socketArg = arguments.indexOf(needle: QLatin1String("--wayland-socket-name")); |
171 | if (socketArg != -1 && socketArg + 1 < arguments.size()) |
172 | socket_name = arguments.at(i: socketArg + 1).toLocal8Bit(); |
173 | if (socket_name.isEmpty()) |
174 | socket_name = qgetenv(varName: "WAYLAND_DISPLAY"); |
175 | } |
176 | wl_compositor::init(display, 4); |
177 | wl_subcompositor::init(display, 1); |
178 | |
179 | #if QT_CONFIG(wayland_datadevice) |
180 | data_device_manager = new QtWayland::DataDeviceManager(q); |
181 | #endif |
182 | buffer_manager = new QtWayland::BufferManager(q); |
183 | |
184 | wl_display_init_shm(display); |
185 | |
186 | for (QWaylandCompositor::ShmFormat format : shmFormats) |
187 | wl_display_add_shm_format(display, wl_shm_format(format)); |
188 | |
189 | if (!socket_name.isEmpty()) { |
190 | if (wl_display_add_socket(display, name: socket_name.constData())) |
191 | qFatal(msg: "Fatal: Failed to open server socket: \"%s\". XDG_RUNTIME_DIR is: \"%s\"\n", socket_name.constData(), getenv(name: "XDG_RUNTIME_DIR")); |
192 | } else { |
193 | const char *autoSocketName = wl_display_add_socket_auto(display); |
194 | if (!autoSocketName) |
195 | qFatal(msg: "Fatal: Failed to open default server socket. XDG_RUNTIME_DIR is: \"%s\"\n", getenv(name: "XDG_RUNTIME_DIR")); |
196 | socket_name = autoSocketName; |
197 | emit q->socketNameChanged(socketName: socket_name); |
198 | } |
199 | |
200 | connectToExternalSockets(); |
201 | |
202 | loop = wl_display_get_event_loop(display); |
203 | |
204 | int fd = wl_event_loop_get_fd(loop); |
205 | |
206 | QSocketNotifier *sockNot = new QSocketNotifier(fd, QSocketNotifier::Read, q); |
207 | QObject::connect(sender: sockNot, SIGNAL(activated(QSocketDescriptor)), receiver: q, SLOT(processWaylandEvents())); |
208 | |
209 | QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; |
210 | QObject::connect(sender: dispatcher, SIGNAL(aboutToBlock()), receiver: q, SLOT(processWaylandEvents())); |
211 | |
212 | QObject::connect(sender: static_cast<QGuiApplication *>(QGuiApplication::instance()), |
213 | signal: &QGuiApplication::applicationStateChanged, |
214 | context: q, |
215 | slot: &QWaylandCompositor::applicationStateChanged); |
216 | |
217 | initializeHardwareIntegration(); |
218 | initializeSeats(); |
219 | |
220 | initialized = true; |
221 | |
222 | for (const QPointer<QObject> &object : std::exchange(obj&: polish_objects, new_val: {})) { |
223 | if (object) { |
224 | QEvent polishEvent(QEvent::Polish); |
225 | QCoreApplication::sendEvent(receiver: object.data(), event: &polishEvent); |
226 | } |
227 | } |
228 | |
229 | emit q->createdChanged(); |
230 | } |
231 | |
232 | QWaylandCompositorPrivate::~QWaylandCompositorPrivate() |
233 | { |
234 | // Take copies, since the lists will get modified as elements are deleted |
235 | const auto clientsToDelete = clients; |
236 | qDeleteAll(c: clientsToDelete); |
237 | |
238 | const auto outputsToDelete = outputs; |
239 | qDeleteAll(c: outputsToDelete); |
240 | |
241 | #if QT_CONFIG(wayland_datadevice) |
242 | delete data_device_manager; |
243 | #endif |
244 | |
245 | // Some client buffer integrations need to clean up before the destroying the wl_display |
246 | qDeleteAll(c: client_buffer_integrations); |
247 | |
248 | if (ownsDisplay) |
249 | wl_display_destroy(display); |
250 | } |
251 | |
252 | void QWaylandCompositorPrivate::preInit() |
253 | { |
254 | Q_Q(QWaylandCompositor); |
255 | |
256 | if (preInitialized) |
257 | return; |
258 | |
259 | if (seats.empty()) |
260 | seats.append(t: q->createSeat()); |
261 | |
262 | preInitialized = true; |
263 | } |
264 | |
265 | void QWaylandCompositorPrivate::destroySurface(QWaylandSurface *surface) |
266 | { |
267 | Q_Q(QWaylandCompositor); |
268 | q->surfaceAboutToBeDestroyed(surface); |
269 | |
270 | delete surface; |
271 | } |
272 | |
273 | void QWaylandCompositorPrivate::unregisterSurface(QWaylandSurface *surface) |
274 | { |
275 | if (!all_surfaces.removeOne(t: surface)) |
276 | qWarning(msg: "%s Unexpected state. Cant find registered surface\n", Q_FUNC_INFO); |
277 | } |
278 | |
279 | void QWaylandCompositorPrivate::feedRetainedSelectionData(QMimeData *data) |
280 | { |
281 | Q_Q(QWaylandCompositor); |
282 | if (retainSelection) |
283 | q->retainedSelectionReceived(mimeData: data); |
284 | } |
285 | |
286 | void QWaylandCompositorPrivate::addPolishObject(QObject *object) |
287 | { |
288 | if (initialized) { |
289 | QCoreApplication::postEvent(receiver: object, event: new QEvent(QEvent::Polish)); |
290 | } else { |
291 | polish_objects.push_back(x: object); |
292 | } |
293 | } |
294 | |
295 | void QWaylandCompositorPrivate::connectToExternalSockets() |
296 | { |
297 | // Clear out any backlog of user-supplied external socket descriptors |
298 | for (int fd : std::as_const(t&: externally_added_socket_fds)) { |
299 | if (wl_display_add_socket_fd(display, sock_fd: fd) != 0) |
300 | qWarning() << "Failed to integrate user-supplied socket fd into the Wayland event loop"; |
301 | } |
302 | externally_added_socket_fds.clear(); |
303 | } |
304 | |
305 | void QWaylandCompositorPrivate::compositor_create_surface(wl_compositor::Resource *resource, uint32_t id) |
306 | { |
307 | Q_Q(QWaylandCompositor); |
308 | QWaylandClient *client = QWaylandClient::fromWlClient(compositor: q, wlClient: resource->client()); |
309 | emit q->surfaceRequested(client, id, version: resource->version()); |
310 | #ifndef QT_NO_DEBUG |
311 | Q_ASSERT_X(!QWaylandSurfacePrivate::hasUninitializedSurface(), "QWaylandCompositor", QStringLiteral( "Found uninitialized QWaylandSurface after emitting QWaylandCompositor::createSurface for id %1. All surfaces has to be initialized immediately after creation. See QWaylandSurface::initialize.").arg(id).toLocal8Bit().constData()); |
312 | #endif |
313 | struct wl_resource *surfResource = wl_client_get_object(client: client->client(), id); |
314 | |
315 | QWaylandSurface *surface = nullptr; |
316 | if (surfResource) { |
317 | surface = QWaylandSurface::fromResource(resource: surfResource); |
318 | } else { |
319 | surface = createDefaultSurface(); |
320 | surface->initialize(compositor: q, client, id, version: resource->version()); |
321 | } |
322 | Q_ASSERT(surface); |
323 | all_surfaces.append(t: surface); |
324 | emit q->surfaceCreated(surface); |
325 | } |
326 | |
327 | void QWaylandCompositorPrivate::compositor_create_region(wl_compositor::Resource *resource, uint32_t id) |
328 | { |
329 | new QtWayland::Region(resource->client(), id); |
330 | } |
331 | |
332 | void QWaylandCompositorPrivate::subcompositor_get_subsurface(wl_subcompositor::Resource *resource, uint32_t id, wl_resource *surface, wl_resource *parent) |
333 | { |
334 | Q_Q(QWaylandCompositor); |
335 | QWaylandSurface *childSurface = QWaylandSurface::fromResource(resource: surface); |
336 | QWaylandSurface *parentSurface = QWaylandSurface::fromResource(resource: parent); |
337 | QWaylandSurfacePrivate::get(surface: childSurface)->initSubsurface(parent: parentSurface, client: resource->client(), id, version: 1); |
338 | QWaylandSurfacePrivate::get(surface: parentSurface)->subsurfaceChildren.append(t: childSurface); |
339 | emit q->subsurfaceChanged(child: childSurface, parent: parentSurface); |
340 | } |
341 | |
342 | /*! |
343 | \internal |
344 | Used to create a fallback QWaylandSurface when no surface was |
345 | created by emitting the QWaylandCompositor::createSurface signal. |
346 | */ |
347 | QWaylandSurface *QWaylandCompositorPrivate::createDefaultSurface() |
348 | { |
349 | return new QWaylandSurface(); |
350 | } |
351 | |
352 | class SharedMemoryClientBufferIntegration : public QtWayland::ClientBufferIntegration |
353 | { |
354 | public: |
355 | void initializeHardware(wl_display *display) override; |
356 | QtWayland::ClientBuffer *createBufferFor(wl_resource *buffer) override; |
357 | }; |
358 | |
359 | void SharedMemoryClientBufferIntegration::initializeHardware(wl_display *) |
360 | { |
361 | } |
362 | |
363 | QtWayland::ClientBuffer *SharedMemoryClientBufferIntegration::createBufferFor(wl_resource *buffer) |
364 | { |
365 | if (wl_shm_buffer_get(resource: buffer)) |
366 | return new QtWayland::SharedMemoryBuffer(buffer); |
367 | return nullptr; |
368 | } |
369 | |
370 | void QWaylandCompositorPrivate::initializeHardwareIntegration() |
371 | { |
372 | client_buffer_integrations.prepend(t: new SharedMemoryClientBufferIntegration); // TODO: clean up the opengl dependency |
373 | |
374 | #if QT_CONFIG(opengl) |
375 | Q_Q(QWaylandCompositor); |
376 | if (use_hw_integration_extension) |
377 | hw_integration.reset(other: new QtWayland::HardwareIntegration(q)); |
378 | |
379 | loadClientBufferIntegration(); |
380 | loadServerBufferIntegration(); |
381 | |
382 | for (auto *integration : std::as_const(t&: client_buffer_integrations)) |
383 | integration->initializeHardware(display); |
384 | #endif |
385 | } |
386 | |
387 | void QWaylandCompositorPrivate::initializeSeats() |
388 | { |
389 | for (QWaylandSeat *seat : std::as_const(t&: seats)) |
390 | seat->initialize(); |
391 | } |
392 | |
393 | void QWaylandCompositorPrivate::loadClientBufferIntegration() |
394 | { |
395 | #if QT_CONFIG(opengl) |
396 | Q_Q(QWaylandCompositor); |
397 | QStringList keys = QtWayland::ClientBufferIntegrationFactory::keys(); |
398 | QStringList targetKeys; |
399 | QByteArray clientBufferIntegration = qgetenv(varName: "QT_WAYLAND_HARDWARE_INTEGRATION"); |
400 | if (clientBufferIntegration.isEmpty()) |
401 | clientBufferIntegration = qgetenv(varName: "QT_WAYLAND_CLIENT_BUFFER_INTEGRATION"); |
402 | |
403 | for (auto b : clientBufferIntegration.split(sep: ';')) { |
404 | QString s = QString::fromLocal8Bit(ba: b); |
405 | if (keys.contains(str: s)) |
406 | targetKeys.append(t: s); |
407 | } |
408 | |
409 | if (targetKeys.isEmpty()) { |
410 | if (keys.contains(str: QString::fromLatin1(ba: "wayland-egl"))) { |
411 | targetKeys.append(t: QString::fromLatin1(ba: "wayland-egl")); |
412 | } else if (!keys.isEmpty()) { |
413 | targetKeys.append(t: keys.first()); |
414 | } |
415 | } |
416 | |
417 | QString hwIntegrationName; |
418 | |
419 | for (auto targetKey : std::as_const(t&: targetKeys)) { |
420 | auto *integration = QtWayland::ClientBufferIntegrationFactory::create(name: targetKey, args: QStringList()); |
421 | if (integration) { |
422 | integration->setCompositor(q); |
423 | client_buffer_integrations.append(t: integration); |
424 | if (hwIntegrationName.isEmpty()) |
425 | hwIntegrationName = targetKey; |
426 | } |
427 | } |
428 | |
429 | if (hw_integration && !hwIntegrationName.isEmpty()) |
430 | hw_integration->setClientBufferIntegrationName(hwIntegrationName); |
431 | |
432 | #endif |
433 | } |
434 | |
435 | void QWaylandCompositorPrivate::loadServerBufferIntegration() |
436 | { |
437 | #if QT_CONFIG(opengl) |
438 | Q_Q(QWaylandCompositor); |
439 | QStringList keys = QtWayland::ServerBufferIntegrationFactory::keys(); |
440 | QString targetKey; |
441 | QByteArray serverBufferIntegration = qgetenv(varName: "QT_WAYLAND_SERVER_BUFFER_INTEGRATION"); |
442 | if (keys.contains(str: QString::fromLocal8Bit(ba: serverBufferIntegration.constData()))) { |
443 | targetKey = QString::fromLocal8Bit(ba: serverBufferIntegration.constData()); |
444 | } |
445 | if (!targetKey.isEmpty()) { |
446 | server_buffer_integration.reset(other: QtWayland::ServerBufferIntegrationFactory::create(name: targetKey, args: QStringList())); |
447 | if (server_buffer_integration) { |
448 | qCDebug(qLcWaylandCompositorHardwareIntegration) |
449 | << "Loaded server buffer integration:"<< targetKey; |
450 | if (!server_buffer_integration->initializeHardware(q)) { |
451 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
452 | << "Failed to initialize hardware for server buffer integration:"<< targetKey; |
453 | server_buffer_integration.reset(); |
454 | } |
455 | } else { |
456 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
457 | << "Failed to load server buffer integration:"<< targetKey; |
458 | } |
459 | } |
460 | |
461 | if (server_buffer_integration && hw_integration) |
462 | hw_integration->setServerBufferIntegrationName(targetKey); |
463 | #endif |
464 | } |
465 | |
466 | QWaylandSeat *QWaylandCompositorPrivate::seatFor(QInputEvent *inputEvent) |
467 | { |
468 | QWaylandSeat *dev = nullptr; |
469 | for (int i = 0; i < seats.size(); i++) { |
470 | QWaylandSeat *candidate = seats.at(i); |
471 | if (candidate->isOwner(inputEvent)) { |
472 | dev = candidate; |
473 | break; |
474 | } |
475 | } |
476 | return dev; |
477 | } |
478 | |
479 | /*! |
480 | \qmltype WaylandCompositor |
481 | \nativetype QWaylandCompositor |
482 | \inqmlmodule QtWayland.Compositor |
483 | \since 5.8 |
484 | \brief Manages the Wayland display server. |
485 | |
486 | The WaylandCompositor manages the connections to the clients, as well as the different |
487 | \l{WaylandOutput}{outputs} and \l{QWaylandSeat}{seats}. |
488 | |
489 | Normally, a compositor application will have a single WaylandCompositor |
490 | instance, which can have several outputs as children. When a client |
491 | requests the compositor to create a surface, the request is handled by |
492 | the onSurfaceRequested handler. |
493 | |
494 | Extensions that are supported by the compositor should be instantiated and added to the |
495 | extensions property. |
496 | */ |
497 | |
498 | |
499 | /*! |
500 | \class QWaylandCompositor |
501 | \inmodule QtWaylandCompositor |
502 | \since 5.8 |
503 | \brief The QWaylandCompositor class manages the Wayland display server. |
504 | |
505 | The QWaylandCompositor manages the connections to the clients, as well as the different \l{QWaylandOutput}{outputs} |
506 | and \l{QWaylandSeat}{seats}. |
507 | |
508 | Normally, a compositor application will have a single WaylandCompositor |
509 | instance, which can have several outputs as children. |
510 | */ |
511 | |
512 | /*! |
513 | \qmlsignal void QtWayland.Compositor::WaylandCompositor::surfaceRequested(WaylandClient client, int id, int version) |
514 | |
515 | This signal is emitted when a \a client has created a surface with id \a id. |
516 | The interface \a version is also available. |
517 | |
518 | The slot connecting to this signal may create and initialize a WaylandSurface |
519 | instance in the scope of the slot. Otherwise a default surface is created. |
520 | */ |
521 | |
522 | /*! |
523 | \fn void QWaylandCompositor::surfaceRequested(QWaylandClient *client, uint id, int version) |
524 | |
525 | This signal is emitted when a \a client has created a surface with id \a id. |
526 | The interface \a version is also available. |
527 | |
528 | The slot connecting to this signal may create and initialize a QWaylandSurface |
529 | instance in the scope of the slot. Otherwise a default surface is created. |
530 | |
531 | Connections to this signal must be of Qt::DirectConnection connection type. |
532 | */ |
533 | |
534 | /*! |
535 | \qmlsignal void QtWayland.Compositor::WaylandCompositor::surfaceCreated(WaylandSurface surface) |
536 | |
537 | This signal is emitted when a new WaylandSurface instance \a surface has been created. |
538 | */ |
539 | |
540 | /*! |
541 | \fn void QWaylandCompositor::surfaceCreated(QWaylandSurface *surface) |
542 | |
543 | This signal is emitted when a new QWaylandSurface instance \a surface has been created. |
544 | */ |
545 | |
546 | /*! |
547 | * Constructs a QWaylandCompositor with the given \a parent. |
548 | */ |
549 | QWaylandCompositor::QWaylandCompositor(QObject *parent) |
550 | : QWaylandObject(*new QWaylandCompositorPrivate(this), parent) |
551 | { |
552 | } |
553 | |
554 | /*! |
555 | * \internal |
556 | * Constructs a QWaylandCompositor with the private object \a dptr and \a parent. |
557 | */ |
558 | QWaylandCompositor::QWaylandCompositor(QWaylandCompositorPrivate &dptr, QObject *parent) |
559 | : QWaylandObject(dptr, parent) |
560 | { |
561 | } |
562 | |
563 | /*! |
564 | * Destroys the QWaylandCompositor |
565 | */ |
566 | QWaylandCompositor::~QWaylandCompositor() |
567 | { |
568 | } |
569 | |
570 | /*! |
571 | * Initializes the QWaylandCompositor. |
572 | * If you override this function in your subclass, be sure to call the base class implementation. |
573 | */ |
574 | void QWaylandCompositor::create() |
575 | { |
576 | Q_D(QWaylandCompositor); |
577 | d->preInit(); |
578 | d->init(); |
579 | } |
580 | |
581 | /*! |
582 | * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::created |
583 | * |
584 | * This property is true if WaylandCompositor has been initialized, |
585 | * otherwise it's false. |
586 | */ |
587 | |
588 | /*! |
589 | * \property QWaylandCompositor::created |
590 | * |
591 | * This property is true if QWaylandCompositor has been initialized, |
592 | * otherwise it's false. |
593 | */ |
594 | bool QWaylandCompositor::isCreated() const |
595 | { |
596 | Q_D(const QWaylandCompositor); |
597 | return d->initialized; |
598 | } |
599 | |
600 | /*! |
601 | * \qmlproperty string QtWayland.Compositor::WaylandCompositor::socketName |
602 | * |
603 | * This property holds the socket name used by WaylandCompositor to communicate with |
604 | * clients. It must be set before the component is completed. |
605 | * |
606 | * If the socketName is empty (the default), the contents of the start argument |
607 | * \c --wayland-socket-name are used instead. If the argument is not set, the |
608 | * compositor tries to find a socket name, which is \c{wayland-0} by default. |
609 | */ |
610 | |
611 | /*! |
612 | * \property QWaylandCompositor::socketName |
613 | * |
614 | * This property holds the socket name used by QWaylandCompositor to communicate with |
615 | * clients. This must be set before the QWaylandCompositor is \l{create()}{created}. |
616 | * |
617 | * If the socketName is empty (the default), the contents of the start argument |
618 | * \c --wayland-socket-name are used instead. If the argument is not set, the |
619 | * compositor tries to find a socket name, which is \c{wayland-0} by default. |
620 | */ |
621 | void QWaylandCompositor::setSocketName(const QByteArray &name) |
622 | { |
623 | Q_D(QWaylandCompositor); |
624 | |
625 | if (d->socket_name == name) |
626 | return; |
627 | |
628 | if (d->initialized) { |
629 | qWarning(msg: "%s: Changing socket name after initializing the compositor is not supported.\n", Q_FUNC_INFO); |
630 | return; |
631 | } |
632 | |
633 | d->socket_name = name; |
634 | emit socketNameChanged(socketName: name); |
635 | } |
636 | |
637 | QByteArray QWaylandCompositor::socketName() const |
638 | { |
639 | Q_D(const QWaylandCompositor); |
640 | return d->socket_name; |
641 | } |
642 | |
643 | /*! |
644 | * \qmlmethod QtWayland.Compositor::WaylandCompositor::addSocketDescriptor(fd) |
645 | * \since 5.12 |
646 | * |
647 | * Listen for client connections on a file descriptor, \a fd, referring to a |
648 | * server socket already bound and listening. |
649 | * |
650 | * Does not take ownership of the file descriptor; it must be closed |
651 | * explicitly if needed. |
652 | * |
653 | * \note This method is only available with libwayland 1.10.0 or |
654 | * newer. If built against an earlier libwayland runtime, this |
655 | * method is a noop. |
656 | */ |
657 | |
658 | /*! |
659 | * Listen for client connections on a file descriptor, \a fd, referring to a |
660 | * server socket already bound and listening. |
661 | * |
662 | * Does not take ownership of the file descriptor; it must be closed |
663 | * explicitly if needed. |
664 | * |
665 | * \note This method is only available with libwayland 1.10.0 or |
666 | * newer. If built against an earlier libwayland runtime, this |
667 | * method is a noop. |
668 | * |
669 | * \since 5.12 |
670 | */ |
671 | void QWaylandCompositor::addSocketDescriptor(int fd) |
672 | { |
673 | Q_D(QWaylandCompositor); |
674 | d->externally_added_socket_fds.append(t: fd); |
675 | if (isCreated()) |
676 | d->connectToExternalSockets(); |
677 | } |
678 | |
679 | /*! |
680 | * \internal |
681 | */ |
682 | struct wl_display *QWaylandCompositor::display() const |
683 | { |
684 | Q_D(const QWaylandCompositor); |
685 | return d->display; |
686 | } |
687 | |
688 | /*! |
689 | * \internal |
690 | */ |
691 | uint32_t QWaylandCompositor::nextSerial() |
692 | { |
693 | Q_D(QWaylandCompositor); |
694 | return wl_display_next_serial(display: d->display); |
695 | } |
696 | |
697 | /*! |
698 | * \internal |
699 | */ |
700 | QList<QWaylandClient *>QWaylandCompositor::clients() const |
701 | { |
702 | Q_D(const QWaylandCompositor); |
703 | return d->clients; |
704 | } |
705 | |
706 | /*! |
707 | * \qmlmethod QtWayland.Compositor::WaylandCompositor::destroyClientForSurface(surface) |
708 | * |
709 | * Destroys the client for the WaylandSurface \a surface. |
710 | */ |
711 | |
712 | /*! |
713 | * Destroys the client for the \a surface. |
714 | */ |
715 | void QWaylandCompositor::destroyClientForSurface(QWaylandSurface *surface) |
716 | { |
717 | destroyClient(client: surface->client()); |
718 | } |
719 | |
720 | /*! |
721 | * \qmlmethod QtWayland.Compositor::WaylandCompositor::destroyClient(client) |
722 | * |
723 | * Destroys the given WaylandClient \a client. |
724 | */ |
725 | |
726 | /*! |
727 | * Destroys the \a client. |
728 | */ |
729 | void QWaylandCompositor::destroyClient(QWaylandClient *client) |
730 | { |
731 | if (!client) |
732 | return; |
733 | |
734 | QWaylandQtWindowManager *wmExtension = QWaylandQtWindowManager::findIn(container: this); |
735 | if (wmExtension) |
736 | wmExtension->sendQuitMessage(client); |
737 | |
738 | wl_client_destroy(client: client->client()); |
739 | } |
740 | |
741 | /*! |
742 | * \internal |
743 | */ |
744 | QList<QWaylandSurface *> QWaylandCompositor::surfacesForClient(QWaylandClient* client) const |
745 | { |
746 | Q_D(const QWaylandCompositor); |
747 | QList<QWaylandSurface *> surfs; |
748 | for (QWaylandSurface *surface : d->all_surfaces) { |
749 | if (surface->client() == client) |
750 | surfs.append(t: surface); |
751 | } |
752 | return surfs; |
753 | } |
754 | |
755 | /*! |
756 | * \internal |
757 | */ |
758 | QList<QWaylandSurface *> QWaylandCompositor::surfaces() const |
759 | { |
760 | Q_D(const QWaylandCompositor); |
761 | return d->all_surfaces; |
762 | } |
763 | |
764 | /*! |
765 | * Returns the QWaylandOutput that is connected to the given \a window. |
766 | */ |
767 | QWaylandOutput *QWaylandCompositor::outputFor(QWindow *window) const |
768 | { |
769 | Q_D(const QWaylandCompositor); |
770 | for (QWaylandOutput *output : d->outputs) { |
771 | if (output->window() == window) |
772 | return output; |
773 | } |
774 | |
775 | return nullptr; |
776 | } |
777 | |
778 | /*! |
779 | * \qmlproperty WaylandOutput QtWayland.Compositor::WaylandCompositor::defaultOutput |
780 | * |
781 | * This property contains the first in the list of outputs added to the |
782 | * WaylandCompositor, or null if no outputs have been added. |
783 | * |
784 | * Setting a new default output prepends it to the output list, making |
785 | * it the new default, but the previous default is not removed from |
786 | * the list. |
787 | */ |
788 | /*! |
789 | * \property QWaylandCompositor::defaultOutput |
790 | * |
791 | * This property contains the first in the list of outputs added to the |
792 | * QWaylandCompositor, or null if no outputs have been added. |
793 | * |
794 | * Setting a new default output prepends it to the output list, making |
795 | * it the new default, but the previous default is not removed from |
796 | * the list. If the new default output was already in the list of outputs, |
797 | * it is moved to the beginning of the list. |
798 | */ |
799 | QWaylandOutput *QWaylandCompositor::defaultOutput() const |
800 | { |
801 | Q_D(const QWaylandCompositor); |
802 | return d->defaultOutput(); |
803 | } |
804 | |
805 | void QWaylandCompositor::setDefaultOutput(QWaylandOutput *output) |
806 | { |
807 | Q_D(QWaylandCompositor); |
808 | if (d->outputs.size() && d->outputs.first() == output) |
809 | return; |
810 | bool alreadyAdded = d->outputs.removeOne(t: output); |
811 | d->outputs.prepend(t: output); |
812 | emit defaultOutputChanged(); |
813 | if (!alreadyAdded) |
814 | emit outputAdded(output); |
815 | } |
816 | |
817 | /*! |
818 | * \internal |
819 | */ |
820 | QList<QWaylandOutput *> QWaylandCompositor::outputs() const |
821 | { |
822 | Q_D(const QWaylandCompositor); |
823 | return d->outputs; |
824 | } |
825 | |
826 | /*! |
827 | * \internal |
828 | */ |
829 | uint QWaylandCompositor::currentTimeMsecs() const |
830 | { |
831 | Q_D(const QWaylandCompositor); |
832 | return d->timer.elapsed(); |
833 | } |
834 | |
835 | /*! |
836 | * \internal |
837 | */ |
838 | void QWaylandCompositor::processWaylandEvents() |
839 | { |
840 | Q_D(QWaylandCompositor); |
841 | int ret = wl_event_loop_dispatch(d->loop, 0); |
842 | if (ret) |
843 | fprintf(stderr, format: "wl_event_loop_dispatch error: %d\n", ret); |
844 | wl_display_flush_clients(display: d->display); |
845 | } |
846 | |
847 | /*! |
848 | * \internal |
849 | */ |
850 | QWaylandSeat *QWaylandCompositor::createSeat() |
851 | { |
852 | return new QWaylandSeat(this); |
853 | } |
854 | |
855 | /*! |
856 | * \internal |
857 | */ |
858 | QWaylandPointer *QWaylandCompositor::createPointerDevice(QWaylandSeat *seat) |
859 | { |
860 | return new QWaylandPointer(seat); |
861 | } |
862 | |
863 | /*! |
864 | * \internal |
865 | */ |
866 | QWaylandKeyboard *QWaylandCompositor::createKeyboardDevice(QWaylandSeat *seat) |
867 | { |
868 | return new QWaylandKeyboard(seat); |
869 | } |
870 | |
871 | /*! |
872 | * \internal |
873 | */ |
874 | QWaylandTouch *QWaylandCompositor::createTouchDevice(QWaylandSeat *seat) |
875 | { |
876 | return new QWaylandTouch(seat); |
877 | } |
878 | |
879 | /*! |
880 | * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::retainedSelection |
881 | * |
882 | * This property holds whether retained selection is enabled. |
883 | */ |
884 | |
885 | /*! |
886 | * \property QWaylandCompositor::retainedSelection |
887 | * |
888 | * This property holds whether retained selection is enabled. |
889 | */ |
890 | void QWaylandCompositor::setRetainedSelectionEnabled(bool enabled) |
891 | { |
892 | Q_D(QWaylandCompositor); |
893 | |
894 | if (d->retainSelection == enabled) |
895 | return; |
896 | |
897 | d->retainSelection = enabled; |
898 | emit retainedSelectionChanged(retainedSelection: enabled); |
899 | } |
900 | |
901 | bool QWaylandCompositor::retainedSelectionEnabled() const |
902 | { |
903 | Q_D(const QWaylandCompositor); |
904 | return d->retainSelection; |
905 | } |
906 | |
907 | /*! |
908 | * \internal |
909 | */ |
910 | void QWaylandCompositor::retainedSelectionReceived(QMimeData *) |
911 | { |
912 | } |
913 | |
914 | /*! |
915 | * \internal |
916 | */ |
917 | void QWaylandCompositor::overrideSelection(const QMimeData *data) |
918 | { |
919 | Q_D(QWaylandCompositor); |
920 | #if QT_CONFIG(wayland_datadevice) |
921 | d->data_device_manager->overrideSelection(mimeData: *data); |
922 | #endif |
923 | } |
924 | |
925 | /*! |
926 | * \qmlproperty WaylandSeat QtWayland.Compositor::WaylandCompositor::defaultSeat |
927 | * |
928 | * This property contains the default seat for this |
929 | * WaylandCompositor. |
930 | */ |
931 | |
932 | /*! |
933 | * \property QWaylandCompositor::defaultSeat |
934 | * |
935 | * This property contains the default seat for this |
936 | * QWaylandCompositor. |
937 | */ |
938 | QWaylandSeat *QWaylandCompositor::defaultSeat() const |
939 | { |
940 | Q_D(const QWaylandCompositor); |
941 | if (d->seats.size()) |
942 | return d->seats.first(); |
943 | return nullptr; |
944 | } |
945 | |
946 | /*! |
947 | * Select the seat for a given input event \a inputEvent. |
948 | * Currently, Qt only supports a single seat. |
949 | */ |
950 | QWaylandSeat *QWaylandCompositor::seatFor(QInputEvent *inputEvent) |
951 | { |
952 | Q_D(QWaylandCompositor); |
953 | return d->seatFor(inputEvent); |
954 | } |
955 | |
956 | /*! |
957 | * \qmlproperty bool QtWayland.Compositor::WaylandCompositor::useHardwareIntegrationExtension |
958 | * |
959 | * This property holds whether the hardware integration extension should be enabled for |
960 | * this WaylandCompositor. |
961 | * |
962 | * This property must be set before the compositor component is completed. |
963 | */ |
964 | |
965 | /*! |
966 | * \property QWaylandCompositor::useHardwareIntegrationExtension |
967 | * |
968 | * This property holds whether the hardware integration extension should be enabled for |
969 | * this QWaylandCompositor. |
970 | * |
971 | * This property must be set before the compositor is \l{create()}{created}. |
972 | */ |
973 | bool QWaylandCompositor::useHardwareIntegrationExtension() const |
974 | { |
975 | #if QT_CONFIG(opengl) |
976 | Q_D(const QWaylandCompositor); |
977 | return d->use_hw_integration_extension; |
978 | #else |
979 | return false; |
980 | #endif |
981 | } |
982 | |
983 | void QWaylandCompositor::setUseHardwareIntegrationExtension(bool use) |
984 | { |
985 | #if QT_CONFIG(opengl) |
986 | Q_D(QWaylandCompositor); |
987 | if (use == d->use_hw_integration_extension) |
988 | return; |
989 | |
990 | if (d->initialized) |
991 | qWarning(msg: "Setting QWaylandCompositor::useHardwareIntegrationExtension after initialization has no effect"); |
992 | |
993 | d->use_hw_integration_extension = use; |
994 | useHardwareIntegrationExtensionChanged(); |
995 | #else |
996 | if (use) |
997 | qWarning() << "Hardware integration not supported without OpenGL support"; |
998 | #endif |
999 | } |
1000 | |
1001 | /*! |
1002 | * Grab the surface content from the given \a buffer. |
1003 | * The default implementation requires a OpenGL context to be bound to the current thread |
1004 | * to work. If this is not possible, reimplement this function in your compositor subclass |
1005 | * to implement custom logic. |
1006 | * The default implementation only grabs shared memory and OpenGL buffers, reimplement this in your |
1007 | * compositor subclass to handle more buffer types. |
1008 | * \note You should not call this manually, but rather use QWaylandSurfaceGrabber (\a grabber). |
1009 | */ |
1010 | void QWaylandCompositor::grabSurface(QWaylandSurfaceGrabber *grabber, const QWaylandBufferRef &buffer) |
1011 | { |
1012 | if (buffer.isSharedMemory()) { |
1013 | emit grabber->success(image: buffer.image()); |
1014 | } else { |
1015 | #if QT_CONFIG(opengl) |
1016 | if (QOpenGLContext::currentContext()) { |
1017 | QOpenGLFramebufferObject fbo(buffer.size()); |
1018 | fbo.bind(); |
1019 | QOpenGLTextureBlitter blitter; |
1020 | blitter.create(); |
1021 | |
1022 | |
1023 | glViewport(x: 0, y: 0, width: buffer.size().width(), height: buffer.size().height()); |
1024 | |
1025 | QOpenGLTextureBlitter::Origin surfaceOrigin = |
1026 | buffer.origin() == QWaylandSurface::OriginTopLeft |
1027 | ? QOpenGLTextureBlitter::OriginTopLeft |
1028 | : QOpenGLTextureBlitter::OriginBottomLeft; |
1029 | |
1030 | auto texture = buffer.toOpenGLTexture(); |
1031 | blitter.bind(target: texture->target()); |
1032 | blitter.blit(texture: texture->textureId(), targetTransform: QMatrix4x4(), sourceOrigin: surfaceOrigin); |
1033 | blitter.release(); |
1034 | |
1035 | emit grabber->success(image: fbo.toImage()); |
1036 | } else |
1037 | #endif |
1038 | emit grabber->failed(error: QWaylandSurfaceGrabber::UnknownBufferType); |
1039 | } |
1040 | } |
1041 | |
1042 | /*! |
1043 | * \qmlproperty list<enum> QtWayland.Compositor::WaylandCompositor::additionalShmFormats |
1044 | * |
1045 | * This property holds the list of additional wl_shm formats advertised as supported by the |
1046 | * compositor. |
1047 | * |
1048 | * By default, only the required ShmFormat_ARGB8888 and ShmFormat_XRGB8888 are listed and this |
1049 | * list will empty. Additional formats may require conversion internally and can thus affect |
1050 | * performance. |
1051 | * |
1052 | * This property must be set before the compositor component is completed. Subsequent changes |
1053 | * will have no effect. |
1054 | * |
1055 | * \since 6.0 |
1056 | */ |
1057 | |
1058 | /*! |
1059 | * \property QWaylandCompositor::additionalShmFormats |
1060 | * |
1061 | * This property holds the list of additional wl_shm formats advertised as supported by the |
1062 | * compositor. |
1063 | * |
1064 | * By default, only the required ShmFormat_ARGB8888 and ShmFormat_XRGB8888 are listed and this |
1065 | * list will empty. |
1066 | * |
1067 | * This property must be set before the compositor is \l{create()}{created}. Subsequent changes |
1068 | * will have no effect. |
1069 | * |
1070 | * \since 6.0 |
1071 | */ |
1072 | void QWaylandCompositor::setAdditionalShmFormats(const QVector<ShmFormat> &additionalShmFormats) |
1073 | { |
1074 | Q_D(QWaylandCompositor); |
1075 | if (d->initialized) |
1076 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Setting QWaylandCompositor::additionalShmFormats after initialization has no effect"; |
1077 | |
1078 | d->shmFormats = additionalShmFormats; |
1079 | emit additionalShmFormatsChanged(); |
1080 | } |
1081 | |
1082 | QVector<QWaylandCompositor::ShmFormat> QWaylandCompositor::additionalShmFormats() const |
1083 | { |
1084 | Q_D(const QWaylandCompositor); |
1085 | return d->shmFormats; |
1086 | } |
1087 | |
1088 | void QWaylandCompositor::applicationStateChanged(Qt::ApplicationState state) |
1089 | { |
1090 | #if QT_CONFIG(xkbcommon) |
1091 | if (state == Qt::ApplicationInactive) { |
1092 | auto *seat = defaultSeat(); |
1093 | if (seat != nullptr) { |
1094 | QWaylandKeyboardPrivate *keyb = QWaylandKeyboardPrivate::get(keyboard: seat->keyboard()); |
1095 | keyb->resetKeyboardState(); |
1096 | } |
1097 | } |
1098 | #else |
1099 | Q_UNUSED(state); |
1100 | #endif |
1101 | } |
1102 | |
1103 | QT_END_NAMESPACE |
1104 | |
1105 | #include "moc_qwaylandcompositor.cpp" |
1106 |
Definitions
- qLcWaylandCompositor
- qLcWaylandCompositorHardwareIntegration
- qLcWaylandCompositorInputMethods
- qLcWaylandCompositorTextInput
- WindowSystemEventHandler
- WindowSystemEventHandler
- sendEvent
- handleKeyEvent
- QWaylandCompositorPrivate
- init
- ~QWaylandCompositorPrivate
- preInit
- destroySurface
- unregisterSurface
- feedRetainedSelectionData
- addPolishObject
- connectToExternalSockets
- compositor_create_surface
- compositor_create_region
- subcompositor_get_subsurface
- createDefaultSurface
- SharedMemoryClientBufferIntegration
- initializeHardware
- createBufferFor
- initializeHardwareIntegration
- initializeSeats
- loadClientBufferIntegration
- loadServerBufferIntegration
- seatFor
- QWaylandCompositor
- QWaylandCompositor
- ~QWaylandCompositor
- create
- isCreated
- setSocketName
- socketName
- addSocketDescriptor
- display
- nextSerial
- clients
- destroyClientForSurface
- destroyClient
- surfacesForClient
- surfaces
- outputFor
- defaultOutput
- setDefaultOutput
- outputs
- currentTimeMsecs
- processWaylandEvents
- createSeat
- createPointerDevice
- createKeyboardDevice
- createTouchDevice
- setRetainedSelectionEnabled
- retainedSelectionEnabled
- retainedSelectionReceived
- overrideSelection
- defaultSeat
- seatFor
- useHardwareIntegrationExtension
- setUseHardwareIntegrationExtension
- grabSurface
- setAdditionalShmFormats
- additionalShmFormats
Learn Advanced QML with KDAB
Find out more