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