Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qxcbintegration.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
7#include "qxcbscreen.h"
8#include "qxcbwindow.h"
9#include "qxcbcursor.h"
10#include "qxcbkeyboard.h"
13#include "qxcbclipboard.h"
14#include "qxcbeventqueue.h"
16#if QT_CONFIG(draganddrop)
17#include "qxcbdrag.h"
18#endif
20
21#ifndef QT_NO_SESSIONMANAGER
23#endif
24#include "qxcbxsettings.h"
25
26#include <xcb/xcb.h>
27
28#include <QtGui/private/qgenericunixfontdatabase_p.h>
29#include <QtGui/private/qdesktopunixservices_p.h>
30
31#include <stdio.h>
32
33#include <QtGui/private/qguiapplication_p.h>
34
35#if QT_CONFIG(xcb_xlib)
36#define register /* C++17 deprecated register */
37#include <X11/Xlib.h>
38#undef register
39#endif
40#if QT_CONFIG(xcb_native_painting)
41#include "qxcbnativepainting.h"
42#include "qpixmap_x11_p.h"
43#include "qbackingstore_x11_p.h"
44#endif
45
46#include <qpa/qplatforminputcontextfactory_p.h>
47#include <private/qgenericunixtheme_p.h>
48#if QT_CONFIG(dbus)
49#include <private/qkdetheme_p.h>
50#endif
51#include <qpa/qplatforminputcontext.h>
52
53#include <QtGui/QOpenGLContext>
54#include <QtGui/QScreen>
55#include <QtGui/QOffscreenSurface>
56#if QT_CONFIG(accessibility)
57#include <qpa/qplatformaccessibility.h>
58#if QT_CONFIG(accessibility_atspi_bridge)
59#include <QtGui/private/qspiaccessiblebridge_p.h>
60#endif
61#endif
62
63#include <QtCore/QFileInfo>
64
65#if QT_CONFIG(vulkan)
66#include "qxcbvulkaninstance.h"
67#include "qxcbvulkanwindow.h"
68#endif
69
71
72using namespace Qt::StringLiterals;
73
74// Find out if our parent process is gdb by looking at the 'exe' symlink under /proc,.
75// or, for older Linuxes, read out 'cmdline'.
77{
78#if defined(Q_OS_LINUX)
79 const QString parentProc = "/proc/"_L1 + QString::number(getppid());
80 const QFileInfo parentProcExe(parentProc + "/exe"_L1);
81 if (parentProcExe.isSymLink())
82 return parentProcExe.symLinkTarget().endsWith("/gdb"_L1);
83 QFile f(parentProc + "/cmdline"_L1);
84 if (!f.open(QIODevice::ReadOnly))
85 return false;
86 QByteArray s;
87 char c;
88 while (f.getChar(&c) && c) {
89 if (c == '/')
90 s.clear();
91 else
92 s += c;
93 }
94 return s == "gdb";
95#else
96 return false;
97#endif
98}
99
101{
102public:
103 QString portalWindowIdentifier(QWindow *window) override;
104 void registerDBusMenuForWindow(QWindow *window, const QString &service, const QString &path) override;
105 void unregisterDBusMenuForWindow(QWindow *window) override;
106};
107
108
109QXcbIntegration *QXcbIntegration::m_instance = nullptr;
110
111QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char **argv)
112 : m_services(new QXcbUnixServices)
113 , m_instanceName(nullptr)
114 , m_canGrab(true)
115 , m_defaultVisualId(UINT_MAX)
116{
117 Q_UNUSED(parameters);
118
119 m_instance = this;
120 qApp->setAttribute(Qt::AA_CompressHighFrequencyEvents, true);
121
122 qRegisterMetaType<QXcbWindow*>();
123#if QT_CONFIG(xcb_xlib)
124 XInitThreads();
125#endif
126 m_nativeInterface.reset(new QXcbNativeInterface);
127
128 // Parse arguments
129 const char *displayName = nullptr;
130 bool noGrabArg = false;
131 bool doGrabArg = false;
132 if (argc) {
133 int j = 1;
134 for (int i = 1; i < argc; i++) {
135 QByteArray arg(argv[i]);
136 if (arg.startsWith("--"))
137 arg.remove(0, 1);
138 if (arg == "-display" && i < argc - 1)
139 displayName = argv[++i];
140 else if (arg == "-name" && i < argc - 1)
141 m_instanceName = argv[++i];
142 else if (arg == "-nograb")
143 noGrabArg = true;
144 else if (arg == "-dograb")
145 doGrabArg = true;
146 else if (arg == "-visual" && i < argc - 1) {
147 bool ok = false;
148 m_defaultVisualId = QByteArray(argv[++i]).toUInt(&ok, 0);
149 if (!ok)
150 m_defaultVisualId = UINT_MAX;
151 }
152 else
153 argv[j++] = argv[i];
154 }
155 argc = j;
156 } // argc
157
158 bool underDebugger = runningUnderDebugger();
159 if (noGrabArg && doGrabArg && underDebugger) {
160 qWarning("Both -nograb and -dograb command line arguments specified. Please pick one. -nograb takes precedence");
161 doGrabArg = false;
162 }
163
164 if (!noGrabArg && !doGrabArg && underDebugger) {
165 qCDebug(lcQpaXcb, "Qt: gdb: -nograb added to command-line options.\n"
166 "\t Use the -dograb option to enforce grabbing.");
167 }
168 m_canGrab = (!underDebugger && !noGrabArg) || (underDebugger && doGrabArg);
169
170 static bool canNotGrabEnv = qEnvironmentVariableIsSet("QT_XCB_NO_GRAB_SERVER");
171 if (canNotGrabEnv)
172 m_canGrab = false;
173
174 m_connection = new QXcbConnection(m_nativeInterface.data(), m_canGrab, m_defaultVisualId, displayName);
175 if (!m_connection->isConnected()) {
176 delete m_connection;
177 m_connection = nullptr;
178 return;
179 }
180 m_services->setConnection(m_connection);
181
182 m_fontDatabase.reset(new QGenericUnixFontDatabase());
183
184#if QT_CONFIG(xcb_native_painting)
185 if (nativePaintingEnabled()) {
186 qCDebug(lcQpaXcb, "QXCB USING NATIVE PAINTING");
187 qt_xcb_native_x11_info_init(connection());
188 }
189#endif
190}
191
192QXcbIntegration::~QXcbIntegration()
193{
194 delete m_connection;
195 m_connection = nullptr;
196 m_instance = nullptr;
197}
198
199QPlatformPixmap *QXcbIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type) const
200{
201#if QT_CONFIG(xcb_native_painting)
202 if (nativePaintingEnabled())
203 return new QX11PlatformPixmap(type);
204#endif
205
206 return QPlatformIntegration::createPlatformPixmap(type);
207}
208
209QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window) const
210{
211 QXcbGlIntegration *glIntegration = nullptr;
212 const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window);
213 if (!isTrayIconWindow) {
214 if (window->supportsOpenGL()) {
215 glIntegration = connection()->glIntegration();
216 if (glIntegration) {
217 QXcbWindow *xcbWindow = glIntegration->createWindow(window);
218 xcbWindow->create();
219 return xcbWindow;
220 }
221#if QT_CONFIG(vulkan)
222 } else if (window->surfaceType() == QSurface::VulkanSurface) {
223 QXcbWindow *xcbWindow = new QXcbVulkanWindow(window);
224 xcbWindow->create();
225 return xcbWindow;
226#endif
227 }
228 }
229
230 Q_ASSERT(isTrayIconWindow || !window->supportsOpenGL()
231 || (!glIntegration && window->surfaceType() == QSurface::RasterSurface)); // for VNC
232 QXcbWindow *xcbWindow = new QXcbWindow(window);
233 xcbWindow->create();
234 return xcbWindow;
235}
236
237QPlatformWindow *QXcbIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const
238{
239 return new QXcbForeignWindow(window, nativeHandle);
240}
241
242#ifndef QT_NO_OPENGL
243QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
244{
245 QXcbGlIntegration *glIntegration = m_connection->glIntegration();
246 if (!glIntegration) {
247 qWarning("QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled");
248 return nullptr;
249 }
250 return glIntegration->createPlatformOpenGLContext(context);
251}
252
253# if QT_CONFIG(xcb_glx_plugin)
254QOpenGLContext *QXcbIntegration::createOpenGLContext(GLXContext context, void *visualInfo, QOpenGLContext *shareContext) const
255{
256 using namespace QNativeInterface::Private;
257 if (auto *glxIntegration = dynamic_cast<QGLXIntegration*>(m_connection->glIntegration()))
258 return glxIntegration->createOpenGLContext(context, visualInfo, shareContext);
259 else
260 return nullptr;
261}
262# endif
263
264#if QT_CONFIG(egl)
265QOpenGLContext *QXcbIntegration::createOpenGLContext(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext) const
266{
267 using namespace QNativeInterface::Private;
268 if (auto *eglIntegration = dynamic_cast<QEGLIntegration*>(m_connection->glIntegration()))
269 return eglIntegration->createOpenGLContext(context, display, shareContext);
270 else
271 return nullptr;
272}
273#endif
274
275#endif // QT_NO_OPENGL
276
277QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *window) const
278{
279 QPlatformBackingStore *backingStore = nullptr;
280
281 const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window);
282 if (isTrayIconWindow) {
283 backingStore = new QXcbSystemTrayBackingStore(window);
284#if QT_CONFIG(xcb_native_painting)
285 } else if (nativePaintingEnabled()) {
286 backingStore = new QXcbNativeBackingStore(window);
287#endif
288 } else {
289 backingStore = new QXcbBackingStore(window);
290 }
291 Q_ASSERT(backingStore);
292 return backingStore;
293}
294
295QPlatformOffscreenSurface *QXcbIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
296{
297 QXcbScreen *screen = static_cast<QXcbScreen *>(surface->screen()->handle());
298 QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();
299 if (!glIntegration) {
300 qWarning("QXcbIntegration: Cannot create platform offscreen surface, neither GLX nor EGL are enabled");
301 return nullptr;
302 }
303 return glIntegration->createPlatformOffscreenSurface(surface);
304}
305
306bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
307{
308 switch (cap) {
309 case OpenGL:
310 case ThreadedOpenGL:
311 {
312 if (const auto *integration = connection()->glIntegration())
313 return cap != ThreadedOpenGL || integration->supportsThreadedOpenGL();
314 return false;
315 }
316
317 case ThreadedPixmaps:
318 case WindowMasks:
319 case MultipleWindows:
320 case ForeignWindows:
321 case SyncState:
322 return true;
323
324 case SwitchableWidgetComposition:
325 {
326 return m_connection->glIntegration()
327 && m_connection->glIntegration()->supportsSwitchableWidgetComposition();
328 }
329
330 default: return QPlatformIntegration::hasCapability(cap);
331 }
332}
333
334QAbstractEventDispatcher *QXcbIntegration::createEventDispatcher() const
335{
336 return QXcbEventDispatcher::createEventDispatcher(connection());
337}
338
339static const auto xsNetCursorBlink = "Net/CursorBlink"_ba;
340static const auto xsNetCursorBlinkTime = "Net/CursorBlinkTime"_ba;
341static const auto xsNetDoubleClickTime = "Net/DoubleClickTime"_ba;
342static const auto xsNetDoubleClickDistance = "Net/DoubleClickDistance"_ba;
343static const auto xsNetDndDragThreshold = "Net/DndDragThreshold"_ba;
344
345void QXcbIntegration::initialize()
346{
347 const auto defaultInputContext = "compose"_L1;
348 // Perform everything that may potentially need the event dispatcher (timers, socket
349 // notifiers) here instead of the constructor.
350 auto icStrs = QPlatformInputContextFactory::requested();
351 if (icStrs.isEmpty())
352 icStrs = { defaultInputContext };
353 m_inputContext.reset(QPlatformInputContextFactory::create(icStrs));
354 if (!m_inputContext && !icStrs.contains(defaultInputContext)
355 && icStrs != QStringList{"none"_L1})
356 m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));
357
358 connection()->keyboard()->initialize();
359
360 auto notifyThemeChanged = [](QXcbVirtualDesktop *, const QByteArray &, const QVariant &, void *) {
361 QWindowSystemInterface::handleThemeChange();
362 };
363
364 auto *xsettings = connection()->primaryScreen()->xSettings();
365 xsettings->registerCallbackForProperty(xsNetCursorBlink, notifyThemeChanged, this);
366 xsettings->registerCallbackForProperty(xsNetCursorBlinkTime, notifyThemeChanged, this);
367 xsettings->registerCallbackForProperty(xsNetDoubleClickTime, notifyThemeChanged, this);
368 xsettings->registerCallbackForProperty(xsNetDoubleClickDistance, notifyThemeChanged, this);
369 xsettings->registerCallbackForProperty(xsNetDndDragThreshold, notifyThemeChanged, this);
370}
371
372void QXcbIntegration::moveToScreen(QWindow *window, int screen)
373{
374 Q_UNUSED(window);
375 Q_UNUSED(screen);
376}
377
378QPlatformFontDatabase *QXcbIntegration::fontDatabase() const
379{
380 return m_fontDatabase.data();
381}
382
383QPlatformNativeInterface * QXcbIntegration::nativeInterface() const
384{
385 return m_nativeInterface.data();
386}
387
388#ifndef QT_NO_CLIPBOARD
389QPlatformClipboard *QXcbIntegration::clipboard() const
390{
391 return m_connection->clipboard();
392}
393#endif
394
395#if QT_CONFIG(draganddrop)
396#include <private/qsimpledrag_p.h>
397QPlatformDrag *QXcbIntegration::drag() const
398{
399 static const bool useSimpleDrag = qEnvironmentVariableIsSet("QT_XCB_USE_SIMPLE_DRAG");
400 if (Q_UNLIKELY(useSimpleDrag)) { // This is useful for testing purposes
401 static QSimpleDrag *simpleDrag = nullptr;
402 if (!simpleDrag)
403 simpleDrag = new QSimpleDrag();
404 return simpleDrag;
405 }
406
407 return m_connection->drag();
408}
409#endif
410
411QPlatformInputContext *QXcbIntegration::inputContext() const
412{
413 return m_inputContext.data();
414}
415
416#if QT_CONFIG(accessibility)
417QPlatformAccessibility *QXcbIntegration::accessibility() const
418{
419#if !defined(QT_NO_ACCESSIBILITY_ATSPI_BRIDGE)
420 if (!m_accessibility) {
421 Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QXcbIntegration",
422 "Initializing accessibility without event-dispatcher!");
423 m_accessibility.reset(new QSpiAccessibleBridge());
424 }
425#endif
426
427 return m_accessibility.data();
428}
429#endif
430
431QPlatformServices *QXcbIntegration::services() const
432{
433 return m_services.data();
434}
435
436QPlatformKeyMapper *QXcbIntegration::keyMapper() const
437{
438 return m_connection->keyboard();
439}
440
441QStringList QXcbIntegration::themeNames() const
442{
443 return QGenericUnixTheme::themeNames();
444}
445
446QPlatformTheme *QXcbIntegration::createPlatformTheme(const QString &name) const
447{
448 return QGenericUnixTheme::createUnixTheme(name);
449}
450
451#define RETURN_VALID_XSETTINGS(key) {
452 auto value = connection()->primaryScreen()->xSettings()->setting(key);
453 if (value.isValid()) return value; \
454}
455
456QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
457{
458 switch (hint) {
459 case QPlatformIntegration::CursorFlashTime: {
460 bool ok = false;
461 // If cursor blinking is off, returns 0 to keep the cursor awlays display.
462 if (connection()->primaryScreen()->xSettings()->setting(xsNetCursorBlink).toInt(&ok) == 0 && ok)
463 return 0;
464
465 RETURN_VALID_XSETTINGS(xsNetCursorBlinkTime);
466 break;
467 }
468 case QPlatformIntegration::MouseDoubleClickInterval:
469 RETURN_VALID_XSETTINGS(xsNetDoubleClickTime);
470 break;
471 case QPlatformIntegration::MouseDoubleClickDistance:
472 RETURN_VALID_XSETTINGS(xsNetDoubleClickDistance);
473 break;
474 case QPlatformIntegration::KeyboardInputInterval:
475 case QPlatformIntegration::StartDragTime:
476 case QPlatformIntegration::KeyboardAutoRepeatRate:
477 case QPlatformIntegration::PasswordMaskDelay:
478 case QPlatformIntegration::StartDragVelocity:
479 case QPlatformIntegration::UseRtlExtensions:
480 case QPlatformIntegration::PasswordMaskCharacter:
481 case QPlatformIntegration::FlickMaximumVelocity:
482 case QPlatformIntegration::FlickDeceleration:
483 // TODO using various xcb, gnome or KDE settings
484 break; // Not implemented, use defaults
485 case QPlatformIntegration::FlickStartDistance:
486 case QPlatformIntegration::StartDragDistance: {
487 RETURN_VALID_XSETTINGS(xsNetDndDragThreshold);
488 // The default (in QPlatformTheme::defaultThemeHint) is 10 pixels, but
489 // on a high-resolution screen it makes sense to increase it.
490 qreal dpi = 100;
491 if (const QXcbScreen *screen = connection()->primaryScreen()) {
492 if (screen->logicalDpi().first > dpi)
493 dpi = screen->logicalDpi().first;
494 if (screen->logicalDpi().second > dpi)
495 dpi = screen->logicalDpi().second;
496 }
497 return (hint == QPlatformIntegration::FlickStartDistance ? qreal(15) : qreal(10)) * dpi / qreal(100);
498 }
499 case QPlatformIntegration::ShowIsFullScreen:
500 // X11 always has support for windows, but the
501 // window manager could prevent it (e.g. matchbox)
502 return false;
503 case QPlatformIntegration::ReplayMousePressOutsidePopup:
504 return false;
505 default:
506 break;
507 }
508 return QPlatformIntegration::styleHint(hint);
509}
510
512{
513 QString result;
514 const QStringList arguments = QCoreApplication::arguments();
515 if (!arguments.isEmpty() && !arguments.front().isEmpty()) {
516 result = arguments.front();
517 const int lastSlashPos = result.lastIndexOf(u'/');
518 if (lastSlashPos != -1)
519 result.remove(0, lastSlashPos + 1);
520 }
521 return result;
522}
523
524static const char resourceNameVar[] = "RESOURCE_NAME";
525
526QByteArray QXcbIntegration::wmClass() const
527{
528 if (m_wmClass.isEmpty()) {
529 // Instance name according to ICCCM 4.1.2.5
530 QString name;
531 if (m_instanceName)
532 name = QString::fromLocal8Bit(m_instanceName);
533 if (name.isEmpty() && qEnvironmentVariableIsSet(resourceNameVar))
534 name = qEnvironmentVariable(resourceNameVar);
535 if (name.isEmpty())
536 name = argv0BaseName();
537
538 // Note: QCoreApplication::applicationName() cannot be called from the QGuiApplication constructor,
539 // hence this delayed initialization.
540 QString className = QCoreApplication::applicationName();
541 if (className.isEmpty()) {
542 className = argv0BaseName();
543 if (!className.isEmpty() && className.at(0).isLower())
544 className[0] = className.at(0).toUpper();
545 }
546
547 if (!name.isEmpty() && !className.isEmpty())
548 m_wmClass = std::move(name).toLocal8Bit() + '\0' + std::move(className).toLocal8Bit() + '\0';
549 }
550 return m_wmClass;
551}
552
553#if QT_CONFIG(xcb_sm)
554QPlatformSessionManager *QXcbIntegration::createPlatformSessionManager(const QString &id, const QString &key) const
555{
556 return new QXcbSessionManager(id, key);
557}
558#endif
559
560void QXcbIntegration::sync()
561{
562 m_connection->sync();
563}
564
565// For QApplication::beep()
566void QXcbIntegration::beep() const
567{
568 QScreen *priScreen = QGuiApplication::primaryScreen();
569 if (!priScreen)
570 return;
571 QPlatformScreen *screen = priScreen->handle();
572 if (!screen)
573 return;
574 xcb_connection_t *connection = static_cast<QXcbScreen *>(screen)->xcb_connection();
575 xcb_bell(connection, 0);
576 xcb_flush(connection);
577}
578
579bool QXcbIntegration::nativePaintingEnabled() const
580{
581#if QT_CONFIG(xcb_native_painting)
582 static bool enabled = qEnvironmentVariableIsSet("QT_XCB_NATIVE_PAINTING");
583 return enabled;
584#else
585 return false;
586#endif
587}
588
589#if QT_CONFIG(vulkan)
590QPlatformVulkanInstance *QXcbIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
591{
592 return new QXcbVulkanInstance(instance);
593}
594#endif
595
596void QXcbIntegration::setApplicationBadge(qint64 number)
597{
598 auto unixServices = dynamic_cast<QDesktopUnixServices *>(services());
599 unixServices->setApplicationBadge(number);
600}
601
603{
604 return "x11:"_L1 + QString::number(window->winId(), 16);
605}
606
607void QXcbUnixServices::registerDBusMenuForWindow(QWindow *window, const QString &service, const QString &path)
608{
609 const QByteArray serviceValue = service.toLatin1();
610 const QByteArray pathValue = path.toLatin1();
611
612 xcb_change_property(xcb_connection(),
613 XCB_PROP_MODE_REPLACE, window->winId(),
615 XCB_ATOM_STRING, 8,
616 serviceValue.length(),
617 serviceValue.constData());
618
619 xcb_change_property(xcb_connection(),
620 XCB_PROP_MODE_REPLACE, window->winId(),
622 XCB_ATOM_STRING, 8,
623 pathValue.length(),
624 pathValue.constData());
625}
626
628{
629 xcb_delete_property(xcb_connection(), window->winId(), atom(QXcbAtom::Atom_KDE_NET_WM_APPMENU_SERVICE_NAME));
630 xcb_delete_property(xcb_connection(), window->winId(), atom(QXcbAtom::Atom_KDE_NET_WM_APPMENU_OBJECT_PATH));
631}
632
633QT_END_NAMESPACE
@ Atom_KDE_NET_WM_APPMENU_SERVICE_NAME
Definition qxcbatom.h:216
@ Atom_KDE_NET_WM_APPMENU_OBJECT_PATH
Definition qxcbatom.h:217
xcb_connection_t * xcb_connection() const
Definition qxcbobject.h:20
xcb_atom_t atom(QXcbAtom::Atom atom) const
Definition qxcbobject.h:19
QString portalWindowIdentifier(QWindow *window) override
void unregisterDBusMenuForWindow(QWindow *window) override
void registerDBusMenuForWindow(QWindow *window, const QString &service, const QString &path) override
static const auto xsNetDoubleClickDistance
static QString argv0BaseName()
static const auto xsNetCursorBlink
static const auto xsNetDoubleClickTime
static bool runningUnderDebugger()
static const auto xsNetDndDragThreshold
static const auto xsNetCursorBlinkTime
#define RETURN_VALID_XSETTINGS(key)
static const char resourceNameVar[]