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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtwayland/src/client/qwaylandintegration.cpp