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