1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qwaylandintegration_p.h" |
5 | |
6 | #include "qwaylanddisplay_p.h" |
7 | #include "qwaylandshmwindow_p.h" |
8 | #include "qwaylandinputdevice_p.h" |
9 | #include "qwaylandinputcontext_p.h" |
10 | #include "qwaylandinputmethodcontext_p.h" |
11 | #include "qwaylandshmbackingstore_p.h" |
12 | #include "qwaylandnativeinterface_p.h" |
13 | #if QT_CONFIG(clipboard) |
14 | #include "qwaylandclipboard_p.h" |
15 | #endif |
16 | #include "qwaylanddnd_p.h" |
17 | #include "qwaylandwindowmanagerintegration_p.h" |
18 | #include "qwaylandscreen_p.h" |
19 | #include "qwaylandcursor_p.h" |
20 | |
21 | #if defined(Q_OS_MACOS) |
22 | # include <QtGui/private/qcoretextfontdatabase_p.h> |
23 | # include <QtGui/private/qfontengine_coretext_p.h> |
24 | #else |
25 | # include <QtGui/private/qgenericunixfontdatabase_p.h> |
26 | #endif |
27 | #include <QtGui/private/qgenericunixeventdispatcher_p.h> |
28 | #include <QtGui/private/qgenericunixthemes_p.h> |
29 | |
30 | #include <QtGui/private/qguiapplication_p.h> |
31 | |
32 | #include <qpa/qwindowsysteminterface.h> |
33 | #include <qpa/qplatformcursor.h> |
34 | #include <QtGui/QSurfaceFormat> |
35 | #if QT_CONFIG(opengl) |
36 | #include <QtGui/QOpenGLContext> |
37 | #endif // QT_CONFIG(opengl) |
38 | #include <QSocketNotifier> |
39 | |
40 | #include <qpa/qplatforminputcontextfactory_p.h> |
41 | #include <qpa/qplatformaccessibility.h> |
42 | #include <qpa/qplatforminputcontext.h> |
43 | |
44 | #include "qwaylandhardwareintegration_p.h" |
45 | #include "qwaylandclientbufferintegration_p.h" |
46 | #include "qwaylandclientbufferintegrationfactory_p.h" |
47 | |
48 | #include "qwaylandserverbufferintegration_p.h" |
49 | #include "qwaylandserverbufferintegrationfactory_p.h" |
50 | #include "qwaylandshellsurface_p.h" |
51 | |
52 | #include "qwaylandshellintegration_p.h" |
53 | #include "qwaylandshellintegrationfactory_p.h" |
54 | |
55 | #include "qwaylandinputdeviceintegration_p.h" |
56 | #include "qwaylandinputdeviceintegrationfactory_p.h" |
57 | #include "qwaylandwindow_p.h" |
58 | |
59 | #if QT_CONFIG(accessibility_atspi_bridge) |
60 | #include <QtGui/private/qspiaccessiblebridge_p.h> |
61 | #endif |
62 | |
63 | #if QT_CONFIG(xkbcommon) |
64 | #include <QtGui/private/qxkbcommon_p.h> |
65 | #endif |
66 | |
67 | #if QT_CONFIG(vulkan) |
68 | #include "qwaylandvulkaninstance_p.h" |
69 | #include "qwaylandvulkanwindow_p.h" |
70 | #endif |
71 | |
72 | QT_BEGIN_NAMESPACE |
73 | |
74 | namespace QtWaylandClient { |
75 | |
76 | QWaylandIntegration *QWaylandIntegration::sInstance = nullptr; |
77 | |
78 | QWaylandIntegration::QWaylandIntegration() |
79 | #if defined(Q_OS_MACOS) |
80 | : mFontDb(new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>) |
81 | #else |
82 | : mFontDb(new QGenericUnixFontDatabase()) |
83 | #endif |
84 | { |
85 | mDisplay.reset(other: new QWaylandDisplay(this)); |
86 | |
87 | QWaylandWindow::fixedToplevelPositions = |
88 | !qEnvironmentVariableIsSet(varName: "QT_WAYLAND_DISABLE_FIXED_POSITIONS" ); |
89 | |
90 | sInstance = this; |
91 | } |
92 | |
93 | QWaylandIntegration::~QWaylandIntegration() |
94 | { |
95 | sInstance = nullptr; |
96 | } |
97 | |
98 | bool QWaylandIntegration::init() |
99 | { |
100 | return mDisplay->initialize(); |
101 | } |
102 | |
103 | QPlatformNativeInterface * QWaylandIntegration::nativeInterface() const |
104 | { |
105 | return mNativeInterface.data(); |
106 | } |
107 | |
108 | bool QWaylandIntegration::hasCapability(QPlatformIntegration::Capability cap) const |
109 | { |
110 | switch (cap) { |
111 | case ThreadedPixmaps: return true; |
112 | case OpenGL: |
113 | return mDisplay->clientBufferIntegration(); |
114 | case ThreadedOpenGL: |
115 | return mDisplay->clientBufferIntegration() && mDisplay->clientBufferIntegration()->supportsThreadedOpenGL(); |
116 | case BufferQueueingOpenGL: |
117 | return true; |
118 | case MultipleWindows: |
119 | case NonFullScreenWindows: |
120 | return true; |
121 | case RasterGLSurface: |
122 | return true; |
123 | case WindowActivation: |
124 | return false; |
125 | case ScreenWindowGrabbing: // whether QScreen::grabWindow() is supported |
126 | return false; |
127 | default: return QPlatformIntegration::hasCapability(cap); |
128 | } |
129 | } |
130 | |
131 | QPlatformWindow *QWaylandIntegration::createPlatformWindow(QWindow *window) const |
132 | { |
133 | if ((window->surfaceType() == QWindow::OpenGLSurface || window->surfaceType() == QWindow::RasterGLSurface) |
134 | && mDisplay->clientBufferIntegration()) |
135 | return mDisplay->clientBufferIntegration()->createEglWindow(window); |
136 | |
137 | #if QT_CONFIG(vulkan) |
138 | if (window->surfaceType() == QSurface::VulkanSurface) |
139 | return new QWaylandVulkanWindow(window, mDisplay.data()); |
140 | #endif // QT_CONFIG(vulkan) |
141 | |
142 | return new QWaylandShmWindow(window, mDisplay.data()); |
143 | } |
144 | |
145 | #if QT_CONFIG(opengl) |
146 | QPlatformOpenGLContext *QWaylandIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const |
147 | { |
148 | if (mDisplay->clientBufferIntegration()) |
149 | return mDisplay->clientBufferIntegration()->createPlatformOpenGLContext(glFormat: context->format(), share: context->shareHandle()); |
150 | return nullptr; |
151 | } |
152 | #endif // opengl |
153 | |
154 | QPlatformBackingStore *QWaylandIntegration::createPlatformBackingStore(QWindow *window) const |
155 | { |
156 | return new QWaylandShmBackingStore(window, mDisplay.data()); |
157 | } |
158 | |
159 | QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const |
160 | { |
161 | return createUnixEventDispatcher(); |
162 | } |
163 | |
164 | QPlatformNativeInterface *QWaylandIntegration::createPlatformNativeInterface() |
165 | { |
166 | return new QWaylandNativeInterface(this); |
167 | } |
168 | |
169 | // Support platform specific initialization |
170 | void QWaylandIntegration::initializePlatform() |
171 | { |
172 | mDisplay->initEventThread(); |
173 | |
174 | mNativeInterface.reset(other: createPlatformNativeInterface()); |
175 | initializeInputDeviceIntegration(); |
176 | #if QT_CONFIG(clipboard) |
177 | mClipboard.reset(other: new QWaylandClipboard(mDisplay.data())); |
178 | #endif |
179 | #if QT_CONFIG(draganddrop) |
180 | mDrag.reset(other: new QWaylandDrag(mDisplay.data())); |
181 | #endif |
182 | |
183 | reconfigureInputContext(); |
184 | } |
185 | |
186 | void QWaylandIntegration::initialize() |
187 | { |
188 | initializePlatform(); |
189 | |
190 | // Call this after initializing event thread for QWaylandDisplay::flushRequests() |
191 | QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; |
192 | QObject::connect(dispatcher, SIGNAL(aboutToBlock()), mDisplay.data(), SLOT(flushRequests())); |
193 | QObject::connect(dispatcher, SIGNAL(awake()), mDisplay.data(), SLOT(flushRequests())); |
194 | |
195 | // Qt does not support running with no screens |
196 | mDisplay->ensureScreen(); |
197 | } |
198 | |
199 | QPlatformFontDatabase *QWaylandIntegration::fontDatabase() const |
200 | { |
201 | return mFontDb.data(); |
202 | } |
203 | |
204 | #if QT_CONFIG(clipboard) |
205 | QPlatformClipboard *QWaylandIntegration::clipboard() const |
206 | { |
207 | return mClipboard.data(); |
208 | } |
209 | #endif |
210 | |
211 | #if QT_CONFIG(draganddrop) |
212 | QPlatformDrag *QWaylandIntegration::drag() const |
213 | { |
214 | return mDrag.data(); |
215 | } |
216 | #endif // draganddrop |
217 | |
218 | QPlatformInputContext *QWaylandIntegration::inputContext() const |
219 | { |
220 | return mInputContext.data(); |
221 | } |
222 | |
223 | QVariant QWaylandIntegration::styleHint(StyleHint hint) const |
224 | { |
225 | if (hint == ShowIsFullScreen && mDisplay->windowManagerIntegration()) |
226 | return mDisplay->windowManagerIntegration()->showIsFullScreen(); |
227 | |
228 | return QPlatformIntegration::styleHint(hint); |
229 | } |
230 | |
231 | #if QT_CONFIG(accessibility) |
232 | QPlatformAccessibility *QWaylandIntegration::accessibility() const |
233 | { |
234 | if (!mAccessibility) { |
235 | #if QT_CONFIG(accessibility_atspi_bridge) |
236 | Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QWaylandIntegration" , |
237 | "Initializing accessibility without event-dispatcher!" ); |
238 | mAccessibility.reset(other: new QSpiAccessibleBridge()); |
239 | #else |
240 | mAccessibility.reset(new QPlatformAccessibility()); |
241 | #endif |
242 | } |
243 | return mAccessibility.data(); |
244 | } |
245 | #endif |
246 | |
247 | QPlatformServices *QWaylandIntegration::services() const |
248 | { |
249 | return mDisplay->windowManagerIntegration(); |
250 | } |
251 | |
252 | QWaylandDisplay *QWaylandIntegration::display() const |
253 | { |
254 | return mDisplay.data(); |
255 | } |
256 | |
257 | Qt::KeyboardModifiers QWaylandIntegration::queryKeyboardModifiers() const |
258 | { |
259 | if (auto *seat = mDisplay->currentInputDevice(); seat && seat->keyboardFocus()) { |
260 | return seat->modifiers(); |
261 | } |
262 | return Qt::NoModifier; |
263 | } |
264 | |
265 | QList<int> QWaylandIntegration::possibleKeys(const QKeyEvent *event) const |
266 | { |
267 | if (auto *seat = mDisplay->currentInputDevice()) |
268 | return seat->possibleKeys(event); |
269 | return {}; |
270 | } |
271 | |
272 | QStringList QWaylandIntegration::themeNames() const |
273 | { |
274 | return QGenericUnixTheme::themeNames(); |
275 | } |
276 | |
277 | QPlatformTheme *QWaylandIntegration::createPlatformTheme(const QString &name) const |
278 | { |
279 | return QGenericUnixTheme::createUnixTheme(name); |
280 | } |
281 | |
282 | QWaylandScreen *QWaylandIntegration::createPlatformScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id) const |
283 | { |
284 | return new QWaylandScreen(waylandDisplay, version, id); |
285 | } |
286 | |
287 | QWaylandCursor *QWaylandIntegration::createPlatformCursor(QWaylandDisplay *display) const |
288 | { |
289 | return new QWaylandCursor(display); |
290 | } |
291 | |
292 | #if QT_CONFIG(vulkan) |
293 | QPlatformVulkanInstance *QWaylandIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const |
294 | { |
295 | return new QWaylandVulkanInstance(instance); |
296 | } |
297 | #endif // QT_CONFIG(vulkan) |
298 | |
299 | // May be called from non-GUI threads |
300 | QWaylandClientBufferIntegration *QWaylandIntegration::clientBufferIntegration() const |
301 | { |
302 | // Do an inexpensive check first to avoid locking whenever possible |
303 | if (Q_UNLIKELY(!mClientBufferIntegrationInitialized)) |
304 | const_cast<QWaylandIntegration *>(this)->initializeClientBufferIntegration(); |
305 | |
306 | Q_ASSERT(mClientBufferIntegrationInitialized); |
307 | return mClientBufferIntegration && mClientBufferIntegration->isValid() ? mClientBufferIntegration.data() : nullptr; |
308 | } |
309 | |
310 | QWaylandServerBufferIntegration *QWaylandIntegration::serverBufferIntegration() const |
311 | { |
312 | if (!mServerBufferIntegrationInitialized) |
313 | const_cast<QWaylandIntegration *>(this)->initializeServerBufferIntegration(); |
314 | |
315 | return mServerBufferIntegration.data(); |
316 | } |
317 | |
318 | QWaylandShellIntegration *QWaylandIntegration::shellIntegration() const |
319 | { |
320 | if (!mShellIntegrationInitialized) |
321 | const_cast<QWaylandIntegration *>(this)->initializeShellIntegration(); |
322 | |
323 | return mShellIntegration.data(); |
324 | } |
325 | |
326 | // May be called from non-GUI threads |
327 | void QWaylandIntegration::initializeClientBufferIntegration() |
328 | { |
329 | QMutexLocker lock(&mClientBufferInitLock); |
330 | if (mClientBufferIntegrationInitialized) |
331 | return; |
332 | |
333 | QString targetKey = QString::fromLocal8Bit(ba: qgetenv(varName: "QT_WAYLAND_CLIENT_BUFFER_INTEGRATION" )); |
334 | |
335 | if (targetKey.isEmpty()) { |
336 | if (mDisplay->hardwareIntegration() |
337 | && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("wayland-eglstream-controller" ) |
338 | && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("linux-dmabuf-unstable-v1" )) { |
339 | targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration(); |
340 | } else { |
341 | targetKey = QLatin1String("wayland-egl" ); |
342 | } |
343 | } |
344 | |
345 | if (targetKey.isEmpty()) { |
346 | qWarning(msg: "Failed to determine what client buffer integration to use" ); |
347 | } else { |
348 | QStringList keys = QWaylandClientBufferIntegrationFactory::keys(); |
349 | qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys; |
350 | |
351 | if (keys.contains(str: targetKey)) |
352 | mClientBufferIntegration.reset(other: QWaylandClientBufferIntegrationFactory::create(name: targetKey, args: QStringList())); |
353 | |
354 | if (mClientBufferIntegration) { |
355 | qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey; |
356 | mClientBufferIntegration->initialize(display: mDisplay.data()); |
357 | } else { |
358 | qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey; |
359 | qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys; |
360 | } |
361 | } |
362 | |
363 | // This must be set last to make sure other threads don't use the |
364 | // integration before initialization is complete. |
365 | mClientBufferIntegrationInitialized = true; |
366 | } |
367 | |
368 | void QWaylandIntegration::initializeServerBufferIntegration() |
369 | { |
370 | mServerBufferIntegrationInitialized = true; |
371 | |
372 | QString targetKey = QString::fromLocal8Bit(ba: qgetenv(varName: "QT_WAYLAND_SERVER_BUFFER_INTEGRATION" )); |
373 | |
374 | if (targetKey.isEmpty() && mDisplay->hardwareIntegration()) |
375 | targetKey = mDisplay->hardwareIntegration()->serverBufferIntegration(); |
376 | |
377 | if (targetKey.isEmpty()) { |
378 | qWarning(msg: "Failed to determine what server buffer integration to use" ); |
379 | return; |
380 | } |
381 | |
382 | QStringList keys = QWaylandServerBufferIntegrationFactory::keys(); |
383 | qCDebug(lcQpaWayland) << "Available server buffer integrations:" << keys; |
384 | |
385 | if (keys.contains(str: targetKey)) |
386 | mServerBufferIntegration.reset(other: QWaylandServerBufferIntegrationFactory::create(name: targetKey, args: QStringList())); |
387 | |
388 | if (mServerBufferIntegration) { |
389 | qCDebug(lcQpaWayland) << "Initializing server buffer integration" << targetKey; |
390 | mServerBufferIntegration->initialize(display: mDisplay.data()); |
391 | } else { |
392 | qCWarning(lcQpaWayland) << "Failed to load server buffer integration: " << targetKey; |
393 | qCWarning(lcQpaWayland) << "Available server buffer integrations:" << keys; |
394 | } |
395 | } |
396 | |
397 | void QWaylandIntegration::initializeShellIntegration() |
398 | { |
399 | mShellIntegrationInitialized = true; |
400 | |
401 | QByteArray integrationNames = qgetenv(varName: "QT_WAYLAND_SHELL_INTEGRATION" ); |
402 | QString targetKeys = QString::fromLocal8Bit(ba: integrationNames); |
403 | |
404 | QStringList preferredShells; |
405 | if (!targetKeys.isEmpty()) { |
406 | preferredShells = targetKeys.split(sep: QLatin1Char(';')); |
407 | } else { |
408 | preferredShells << QLatin1String("xdg-shell" ); |
409 | preferredShells << QLatin1String("wl-shell" ) << QLatin1String("ivi-shell" ); |
410 | preferredShells << QLatin1String("qt-shell" ); |
411 | } |
412 | |
413 | for (const QString &preferredShell : std::as_const(t&: preferredShells)) { |
414 | mShellIntegration.reset(other: createShellIntegration(interfaceName: preferredShell)); |
415 | if (mShellIntegration) { |
416 | qCDebug(lcQpaWayland, "Using the '%s' shell integration" , qPrintable(preferredShell)); |
417 | break; |
418 | } |
419 | } |
420 | |
421 | if (!mShellIntegration) { |
422 | qCWarning(lcQpaWayland) << "Loading shell integration failed." ; |
423 | qCWarning(lcQpaWayland) << "Attempted to load the following shells" << preferredShells; |
424 | } |
425 | |
426 | QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); |
427 | } |
428 | |
429 | QWaylandInputDevice *QWaylandIntegration::createInputDevice(QWaylandDisplay *display, int version, uint32_t id) const |
430 | { |
431 | if (mInputDeviceIntegration) { |
432 | return mInputDeviceIntegration->createInputDevice(d: display, version, id); |
433 | } |
434 | return new QWaylandInputDevice(display, version, id); |
435 | } |
436 | |
437 | void QWaylandIntegration::initializeInputDeviceIntegration() |
438 | { |
439 | QByteArray integrationName = qgetenv(varName: "QT_WAYLAND_INPUTDEVICE_INTEGRATION" ); |
440 | QString targetKey = QString::fromLocal8Bit(ba: integrationName); |
441 | |
442 | if (targetKey.isEmpty()) { |
443 | return; |
444 | } |
445 | |
446 | QStringList keys = QWaylandInputDeviceIntegrationFactory::keys(); |
447 | if (keys.contains(str: targetKey)) { |
448 | mInputDeviceIntegration.reset(other: QWaylandInputDeviceIntegrationFactory::create(name: targetKey, args: QStringList())); |
449 | qDebug(msg: "Using the '%s' input device integration" , qPrintable(targetKey)); |
450 | } else { |
451 | qWarning(msg: "Wayland inputdevice integration '%s' not found, using default" , qPrintable(targetKey)); |
452 | } |
453 | } |
454 | |
455 | void QWaylandIntegration::reconfigureInputContext() |
456 | { |
457 | if (!mDisplay) { |
458 | // This function can be called from QWaylandDisplay::registry_global() when we |
459 | // are in process of constructing QWaylandDisplay. Configuring input context |
460 | // in that case is done by calling reconfigureInputContext() from QWaylandIntegration |
461 | // constructor, after QWaylandDisplay has been constructed. |
462 | return; |
463 | } |
464 | |
465 | const QString &requested = QPlatformInputContextFactory::requested(); |
466 | if (requested == QLatin1String("qtvirtualkeyboard" )) |
467 | qCWarning(lcQpaWayland) << "qtvirtualkeyboard currently is not supported at client-side," |
468 | " use QT_IM_MODULE=qtvirtualkeyboard at compositor-side." ; |
469 | |
470 | if (!mDisplay->isClientSideInputContextRequested()) { |
471 | if (mDisplay->textInputMethodManager() != nullptr) |
472 | mInputContext.reset(other: new QWaylandInputMethodContext(mDisplay.data())); |
473 | #if QT_WAYLAND_TEXT_INPUT_V4_WIP |
474 | else if (mDisplay->textInputManagerv1() != nullptr || mDisplay->textInputManagerv2() != nullptr || mDisplay->textInputManagerv4() != nullptr) |
475 | #else // QT_WAYLAND_TEXT_INPUT_V4_WIP |
476 | else if (mDisplay->textInputManagerv1() != nullptr || mDisplay->textInputManagerv2() != nullptr) |
477 | #endif // QT_WAYLAND_TEXT_INPUT_V4_WIP |
478 | mInputContext.reset(other: new QWaylandInputContext(mDisplay.data())); |
479 | } else { |
480 | mInputContext.reset(other: QPlatformInputContextFactory::create(key: requested)); |
481 | } |
482 | |
483 | const QString defaultInputContext(QStringLiteral("compose" )); |
484 | if ((!mInputContext || !mInputContext->isValid()) && requested != defaultInputContext) |
485 | mInputContext.reset(other: QPlatformInputContextFactory::create(key: defaultInputContext)); |
486 | |
487 | #if QT_CONFIG(xkbcommon) |
488 | QXkbCommon::setXkbContext(inputContext: mInputContext.data(), context: mDisplay->xkbContext()); |
489 | if (QWaylandInputContext* waylandInput = qobject_cast<QWaylandInputContext*>(object: mInputContext.get())) { |
490 | waylandInput->setXkbContext(mDisplay->xkbContext()); |
491 | } |
492 | #endif |
493 | |
494 | qCDebug(lcQpaWayland) << "using input method:" << (inputContext() ? inputContext()->metaObject()->className() : "<none>" ); |
495 | } |
496 | |
497 | QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QString &integrationName) |
498 | { |
499 | if (QWaylandShellIntegrationFactory::keys().contains(str: integrationName)) { |
500 | return QWaylandShellIntegrationFactory::create(name: integrationName, display: mDisplay.data()); |
501 | } else { |
502 | qCWarning(lcQpaWayland) << "No shell integration named" << integrationName << "found" ; |
503 | return nullptr; |
504 | } |
505 | } |
506 | |
507 | void QWaylandIntegration::reset() |
508 | { |
509 | mServerBufferIntegration.reset(); |
510 | mServerBufferIntegrationInitialized = false; |
511 | |
512 | mInputDeviceIntegration.reset(); |
513 | |
514 | mClientBufferIntegration.reset(); |
515 | mClientBufferIntegrationInitialized = false; |
516 | } |
517 | |
518 | void QWaylandIntegration::setApplicationBadge(qint64 number) |
519 | { |
520 | auto unixServices = mDisplay->windowManagerIntegration(); |
521 | unixServices->setApplicationBadge(number); |
522 | } |
523 | } |
524 | |
525 | QT_END_NAMESPACE |
526 | |