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 "qxcbnativeinterface.h"
5
6#include "qxcbcursor.h"
7#include "qxcbscreen.h"
8#include "qxcbwindow.h"
9#include "qxcbintegration.h"
10#include "qxcbsystemtraytracker.h"
11
12#include <private/qguiapplication_p.h>
13#include <QtCore/QMap>
14
15#include <QtCore/QDebug>
16
17#include <QtGui/qopenglcontext.h>
18#include <QtGui/qscreen.h>
19
20#include <stdio.h>
21
22#include <algorithm>
23
24#include "qxcbnativeinterfacehandler.h"
25
26#if QT_CONFIG(vulkan)
27#include "qxcbvulkanwindow.h"
28#endif
29
30QT_BEGIN_NAMESPACE
31
32// return QXcbNativeInterface::ResourceType for the key.
33static int resourceType(const QByteArray &key)
34{
35 static const QByteArray names[] = { // match QXcbNativeInterface::ResourceType
36 QByteArrayLiteral("display"),
37 QByteArrayLiteral("connection"), QByteArrayLiteral("screen"),
38 QByteArrayLiteral("apptime"),
39 QByteArrayLiteral("appusertime"), QByteArrayLiteral("hintstyle"),
40 QByteArrayLiteral("startupid"), QByteArrayLiteral("traywindow"),
41 QByteArrayLiteral("gettimestamp"), QByteArrayLiteral("x11screen"),
42 QByteArrayLiteral("rootwindow"),
43 QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
44 QByteArrayLiteral("atspibus"),
45 QByteArrayLiteral("compositingenabled"),
46 QByteArrayLiteral("vksurface"),
47 QByteArrayLiteral("generatepeekerid"),
48 QByteArrayLiteral("removepeekerid"),
49 QByteArrayLiteral("peekeventqueue")
50 };
51 const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
52 const QByteArray *result = std::find(first: names, last: end, val: key);
53 return int(result - names);
54}
55
56QXcbNativeInterface::QXcbNativeInterface()
57{
58}
59
60static inline QXcbSystemTrayTracker *systemTrayTracker(const QScreen *s)
61{
62 if (!s)
63 return nullptr;
64
65 return static_cast<const QXcbScreen *>(s->handle())->connection()->systemTrayTracker();
66}
67
68void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString)
69{
70 QByteArray lowerCaseResource = resourceString.toLower();
71 void *result = handlerNativeResourceForIntegration(resource: lowerCaseResource);
72 if (result)
73 return result;
74
75 switch (resourceType(key: lowerCaseResource)) {
76 case StartupId:
77 result = startupId();
78 break;
79 case X11Screen:
80 result = x11Screen();
81 break;
82 case RootWindow:
83 result = rootWindow();
84 break;
85 case XDisplay:
86 result = display();
87 break;
88 case AtspiBus:
89 result = atspiBus();
90 break;
91 case Connection:
92 result = connection();
93 break;
94 default:
95 break;
96 }
97
98 return result;
99}
100
101void *QXcbNativeInterface::nativeResourceForContext(const QByteArray &resourceString, QOpenGLContext *context)
102{
103 QByteArray lowerCaseResource = resourceString.toLower();
104 void *result = handlerNativeResourceForContext(resource: lowerCaseResource, context);
105 return result;
106}
107
108void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceString, QScreen *screen)
109{
110 if (!screen) {
111 qWarning(msg: "nativeResourceForScreen: null screen");
112 return nullptr;
113 }
114
115 QByteArray lowerCaseResource = resourceString.toLower();
116 void *result = handlerNativeResourceForScreen(resource: lowerCaseResource, screen);
117 if (result)
118 return result;
119
120 const QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(screen->handle());
121 switch (resourceType(key: lowerCaseResource)) {
122 case XDisplay:
123#if QT_CONFIG(xcb_xlib)
124 result = xcbScreen->connection()->xlib_display();
125#endif
126 break;
127 case AppTime:
128 result = appTime(screen: xcbScreen);
129 break;
130 case AppUserTime:
131 result = appUserTime(screen: xcbScreen);
132 break;
133 case ScreenHintStyle:
134 result = reinterpret_cast<void *>(xcbScreen->hintStyle() + 1);
135 break;
136 case ScreenSubpixelType:
137 result = reinterpret_cast<void *>(xcbScreen->subpixelType() + 1);
138 break;
139 case ScreenAntialiasingEnabled:
140 result = reinterpret_cast<void *>(xcbScreen->antialiasingEnabled() + 1);
141 break;
142 case TrayWindow:
143 if (QXcbSystemTrayTracker *s = systemTrayTracker(s: screen))
144 result = (void *)quintptr(s->trayWindow());
145 break;
146 case GetTimestamp:
147 result = getTimestamp(screen: xcbScreen);
148 break;
149 case RootWindow:
150 result = reinterpret_cast<void *>(xcbScreen->root());
151 break;
152 case CompositingEnabled:
153 if (QXcbVirtualDesktop *vd = xcbScreen->virtualDesktop())
154 result = vd->compositingActive() ? this : nullptr;
155 break;
156 default:
157 break;
158 }
159 return result;
160}
161
162void *QXcbNativeInterface::nativeResourceForWindow(const QByteArray &resourceString, QWindow *window)
163{
164 QByteArray lowerCaseResource = resourceString.toLower();
165 void *result = handlerNativeResourceForWindow(resource: lowerCaseResource, window);
166 if (result)
167 return result;
168
169 switch (resourceType(key: lowerCaseResource)) {
170 case XDisplay:
171 result = displayForWindow(window);
172 break;
173 case Connection:
174 result = connectionForWindow(window);
175 break;
176 case Screen:
177 result = screenForWindow(window);
178 break;
179#if QT_CONFIG(vulkan)
180 case VkSurface:
181 if (window->surfaceType() == QSurface::VulkanSurface && window->handle()) {
182 // return a pointer to the VkSurfaceKHR value, not the value itself
183 result = static_cast<QXcbVulkanWindow *>(window->handle())->surface();
184 }
185 break;
186#endif
187 default:
188 break;
189 }
190
191 return result;
192}
193
194void *QXcbNativeInterface::nativeResourceForBackingStore(const QByteArray &resourceString, QBackingStore *backingStore)
195{
196 const QByteArray lowerCaseResource = resourceString.toLower();
197 void *result = handlerNativeResourceForBackingStore(resource: lowerCaseResource,backingStore);
198 return result;
199}
200
201#ifndef QT_NO_CURSOR
202void *QXcbNativeInterface::nativeResourceForCursor(const QByteArray &resource, const QCursor &cursor)
203{
204 if (resource == QByteArrayLiteral("xcbcursor")) {
205 if (const QScreen *primaryScreen = QGuiApplication::primaryScreen()) {
206 if (const QPlatformCursor *pCursor= primaryScreen->handle()->cursor()) {
207 xcb_cursor_t xcbCursor = static_cast<const QXcbCursor *>(pCursor)->xcbCursor(c: cursor);
208 return reinterpret_cast<void *>(quintptr(xcbCursor));
209 }
210 }
211 }
212 return nullptr;
213}
214#endif // !QT_NO_CURSOR
215
216QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterface::nativeResourceFunctionForIntegration(const QByteArray &resource)
217{
218 const QByteArray lowerCaseResource = resource.toLower();
219 QPlatformNativeInterface::NativeResourceForIntegrationFunction func = handlerNativeResourceFunctionForIntegration(resource: lowerCaseResource);
220 if (func)
221 return func;
222
223 if (lowerCaseResource == "setstartupid")
224 return NativeResourceForIntegrationFunction(reinterpret_cast<void *>(setStartupId));
225 if (lowerCaseResource == "generatepeekerid")
226 return NativeResourceForIntegrationFunction(reinterpret_cast<void *>(generatePeekerId));
227 if (lowerCaseResource == "removepeekerid")
228 return NativeResourceForIntegrationFunction(reinterpret_cast<void *>(removePeekerId));
229 if (lowerCaseResource == "peekeventqueue")
230 return NativeResourceForIntegrationFunction(reinterpret_cast<void *>(peekEventQueue));
231
232 return nullptr;
233}
234
235QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterface::nativeResourceFunctionForContext(const QByteArray &resource)
236{
237 const QByteArray lowerCaseResource = resource.toLower();
238 QPlatformNativeInterface::NativeResourceForContextFunction func = handlerNativeResourceFunctionForContext(resource: lowerCaseResource);
239 if (func)
240 return func;
241 return nullptr;
242}
243
244QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterface::nativeResourceFunctionForScreen(const QByteArray &resource)
245{
246 const QByteArray lowerCaseResource = resource.toLower();
247 NativeResourceForScreenFunction func = handlerNativeResourceFunctionForScreen(resource: lowerCaseResource);
248 if (func)
249 return func;
250
251 if (lowerCaseResource == "setapptime")
252 return NativeResourceForScreenFunction(reinterpret_cast<void *>(setAppTime));
253 else if (lowerCaseResource == "setappusertime")
254 return NativeResourceForScreenFunction(reinterpret_cast<void *>(setAppUserTime));
255 return nullptr;
256}
257
258QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterface::nativeResourceFunctionForWindow(const QByteArray &resource)
259{
260 const QByteArray lowerCaseResource = resource.toLower();
261 NativeResourceForWindowFunction func = handlerNativeResourceFunctionForWindow(resource: lowerCaseResource);
262 return func;
263}
264
265QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterface::nativeResourceFunctionForBackingStore(const QByteArray &resource)
266{
267 const QByteArray lowerCaseResource = resource.toLower();
268 NativeResourceForBackingStoreFunction func = handlerNativeResourceFunctionForBackingStore(resource: lowerCaseResource);
269 return func;
270}
271
272QFunctionPointer QXcbNativeInterface::platformFunction(const QByteArray &function) const
273{
274 const QByteArray lowerCaseFunction = function.toLower();
275 if (QFunctionPointer func = handlerPlatformFunction(function: lowerCaseFunction))
276 return func;
277
278 return nullptr;
279}
280
281void *QXcbNativeInterface::appTime(const QXcbScreen *screen)
282{
283 if (!screen)
284 return nullptr;
285
286 return reinterpret_cast<void *>(quintptr(screen->connection()->time()));
287}
288
289void *QXcbNativeInterface::appUserTime(const QXcbScreen *screen)
290{
291 if (!screen)
292 return nullptr;
293
294 return reinterpret_cast<void *>(quintptr(screen->connection()->netWmUserTime()));
295}
296
297void *QXcbNativeInterface::getTimestamp(const QXcbScreen *screen)
298{
299 if (!screen)
300 return nullptr;
301
302 return reinterpret_cast<void *>(quintptr(screen->connection()->getTimestamp()));
303}
304
305void *QXcbNativeInterface::startupId()
306{
307 QXcbIntegration* integration = QXcbIntegration::instance();
308 QXcbConnection *connection = integration->connection();
309 if (connection)
310 return reinterpret_cast<void *>(const_cast<char *>(connection->startupId().constData()));
311 return nullptr;
312}
313
314void *QXcbNativeInterface::x11Screen()
315{
316 QXcbIntegration *integration = QXcbIntegration::instance();
317 QXcbConnection *connection = integration->connection();
318 if (connection)
319 return reinterpret_cast<void *>(connection->primaryScreenNumber());
320 return nullptr;
321}
322
323void *QXcbNativeInterface::rootWindow()
324{
325 QXcbIntegration *integration = QXcbIntegration::instance();
326 QXcbConnection *connection = integration->connection();
327 if (connection)
328 return reinterpret_cast<void *>(connection->rootWindow());
329 return nullptr;
330}
331
332Display *QXcbNativeInterface::display() const
333{
334#if QT_CONFIG(xcb_xlib)
335 QXcbIntegration *integration = QXcbIntegration::instance();
336 if (QXcbConnection *connection = integration->connection())
337 return reinterpret_cast<Display *>(connection->xlib_display());
338#endif
339 return nullptr;
340}
341
342xcb_connection_t *QXcbNativeInterface::connection() const
343{
344 QXcbIntegration *integration = QXcbIntegration::instance();
345 return integration->connection()->xcb_connection();
346}
347
348void *QXcbNativeInterface::atspiBus()
349{
350 QXcbIntegration *integration = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration());
351 QXcbConnection *connection = integration->connection();
352 if (connection) {
353 auto atspiBusAtom = connection->atom(qatom: QXcbAtom::AtomAT_SPI_BUS);
354 auto reply = Q_XCB_REPLY(xcb_get_property, connection->xcb_connection(),
355 false, connection->rootWindow(),
356 atspiBusAtom, XCB_ATOM_STRING, 0, 128);
357 if (!reply)
358 return nullptr;
359
360 char *data = (char *)xcb_get_property_value(R: reply.get());
361 int length = xcb_get_property_value_length(R: reply.get());
362 return new QByteArray(data, length);
363 }
364
365 return nullptr;
366}
367
368void QXcbNativeInterface::setAppTime(QScreen* screen, xcb_timestamp_t time)
369{
370 if (screen) {
371 static_cast<QXcbScreen *>(screen->handle())->connection()->setTime(time);
372 }
373}
374
375void QXcbNativeInterface::setAppUserTime(QScreen* screen, xcb_timestamp_t time)
376{
377 if (screen) {
378 static_cast<QXcbScreen *>(screen->handle())->connection()->setNetWmUserTime(time);
379 }
380}
381
382qint32 QXcbNativeInterface::generatePeekerId()
383{
384 QXcbIntegration *integration = QXcbIntegration::instance();
385 return integration->connection()->eventQueue()->generatePeekerId();
386}
387
388bool QXcbNativeInterface::removePeekerId(qint32 peekerId)
389{
390 QXcbIntegration *integration = QXcbIntegration::instance();
391 return integration->connection()->eventQueue()->removePeekerId(peekerId);
392}
393
394bool QXcbNativeInterface::peekEventQueue(QXcbEventQueue::PeekerCallback peeker, void *peekerData,
395 QXcbEventQueue::PeekOptions option, qint32 peekerId)
396{
397 QXcbIntegration *integration = QXcbIntegration::instance();
398 return integration->connection()->eventQueue()->peekEventQueue(peeker, peekerData, option, peekerId);
399}
400
401void QXcbNativeInterface::setStartupId(const char *data)
402{
403 QByteArray startupId(data);
404 QXcbIntegration *integration = QXcbIntegration::instance();
405 QXcbConnection *connection = integration->connection();
406 if (connection)
407 connection->setStartupId(startupId);
408}
409
410QXcbScreen *QXcbNativeInterface::qPlatformScreenForWindow(QWindow *window)
411{
412 QXcbScreen *screen;
413 if (window) {
414 QScreen *qs = window->screen();
415 screen = static_cast<QXcbScreen *>(qs ? qs->handle() : nullptr);
416 } else {
417 QScreen *qs = QGuiApplication::primaryScreen();
418 screen = static_cast<QXcbScreen *>(qs ? qs->handle() : nullptr);
419 }
420 return screen;
421}
422
423void *QXcbNativeInterface::displayForWindow(QWindow *window)
424{
425#if QT_CONFIG(xcb_xlib)
426 QXcbScreen *screen = qPlatformScreenForWindow(window);
427 return screen ? screen->connection()->xlib_display() : nullptr;
428#else
429 Q_UNUSED(window);
430 return nullptr;
431#endif
432}
433
434void *QXcbNativeInterface::connectionForWindow(QWindow *window)
435{
436 QXcbScreen *screen = qPlatformScreenForWindow(window);
437 return screen ? screen->xcb_connection() : nullptr;
438}
439
440void *QXcbNativeInterface::screenForWindow(QWindow *window)
441{
442 QXcbScreen *screen = qPlatformScreenForWindow(window);
443 return screen ? screen->screen() : nullptr;
444}
445
446void QXcbNativeInterface::addHandler(QXcbNativeInterfaceHandler *handler)
447{
448 m_handlers.removeAll(t: handler);
449 m_handlers.prepend(t: handler);
450}
451
452void QXcbNativeInterface::removeHandler(QXcbNativeInterfaceHandler *handler)
453{
454 m_handlers.removeAll(t: handler);
455}
456
457QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterface::handlerNativeResourceFunctionForIntegration(const QByteArray &resource) const
458{
459 for (int i = 0; i < m_handlers.size(); i++) {
460 QXcbNativeInterfaceHandler *handler = m_handlers.at(i);
461 NativeResourceForIntegrationFunction result = handler->nativeResourceFunctionForIntegration(resource);
462 if (result)
463 return result;
464 }
465 return nullptr;
466}
467
468QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterface::handlerNativeResourceFunctionForContext(const QByteArray &resource) const
469{
470 for (int i = 0; i < m_handlers.size(); i++) {
471 QXcbNativeInterfaceHandler *handler = m_handlers.at(i);
472 NativeResourceForContextFunction result = handler->nativeResourceFunctionForContext(resource);
473 if (result)
474 return result;
475 }
476 return nullptr;
477}
478
479QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterface::handlerNativeResourceFunctionForScreen(const QByteArray &resource) const
480{
481 for (int i = 0; i < m_handlers.size(); i++) {
482 QXcbNativeInterfaceHandler *handler = m_handlers.at(i);
483 NativeResourceForScreenFunction result = handler->nativeResourceFunctionForScreen(resource);
484 if (result)
485 return result;
486 }
487 return nullptr;
488}
489
490QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterface::handlerNativeResourceFunctionForWindow(const QByteArray &resource) const
491{
492 for (int i = 0; i < m_handlers.size(); i++) {
493 QXcbNativeInterfaceHandler *handler = m_handlers.at(i);
494 NativeResourceForWindowFunction result = handler->nativeResourceFunctionForWindow(resource);
495 if (result)
496 return result;
497 }
498 return nullptr;
499}
500
501QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterface::handlerNativeResourceFunctionForBackingStore(const QByteArray &resource) const
502{
503 for (int i = 0; i < m_handlers.size(); i++) {
504 QXcbNativeInterfaceHandler *handler = m_handlers.at(i);
505 NativeResourceForBackingStoreFunction result = handler->nativeResourceFunctionForBackingStore(resource);
506 if (result)
507 return result;
508 }
509 return nullptr;
510}
511
512QFunctionPointer QXcbNativeInterface::handlerPlatformFunction(const QByteArray &function) const
513{
514 for (int i = 0; i < m_handlers.size(); i++) {
515 QXcbNativeInterfaceHandler *handler = m_handlers.at(i);
516 QFunctionPointer func = handler->platformFunction(function);
517 if (func)
518 return func;
519 }
520 return nullptr;
521}
522
523void *QXcbNativeInterface::handlerNativeResourceForIntegration(const QByteArray &resource) const
524{
525 NativeResourceForIntegrationFunction func = handlerNativeResourceFunctionForIntegration(resource);
526 if (func)
527 return func();
528 return nullptr;
529}
530
531void *QXcbNativeInterface::handlerNativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) const
532{
533 NativeResourceForContextFunction func = handlerNativeResourceFunctionForContext(resource);
534 if (func)
535 return func(context);
536 return nullptr;
537}
538
539void *QXcbNativeInterface::handlerNativeResourceForScreen(const QByteArray &resource, QScreen *screen) const
540{
541 NativeResourceForScreenFunction func = handlerNativeResourceFunctionForScreen(resource);
542 if (func)
543 return func(screen);
544 return nullptr;
545}
546
547void *QXcbNativeInterface::handlerNativeResourceForWindow(const QByteArray &resource, QWindow *window) const
548{
549 NativeResourceForWindowFunction func = handlerNativeResourceFunctionForWindow(resource);
550 if (func)
551 return func(window);
552 return nullptr;
553}
554
555void *QXcbNativeInterface::handlerNativeResourceForBackingStore(const QByteArray &resource, QBackingStore *backingStore) const
556{
557 NativeResourceForBackingStoreFunction func = handlerNativeResourceFunctionForBackingStore(resource);
558 if (func)
559 return func(backingStore);
560 return nullptr;
561}
562
563static void dumpNativeWindowsRecursion(const QXcbConnection *connection, xcb_window_t window,
564 int level, QTextStream &str)
565{
566 if (level)
567 str << QByteArray(2 * level, ' ');
568
569 xcb_connection_t *conn = connection->xcb_connection();
570 auto geomReply = Q_XCB_REPLY(xcb_get_geometry, conn, window);
571 if (!geomReply)
572 return;
573 const QRect geom(geomReply->x, geomReply->y, geomReply->width, geomReply->height);
574 if (!geom.isValid() || (geom.width() <= 3 && geom.height() <= 3))
575 return; // Skip helper/dummy windows.
576 str << "0x";
577 const int oldFieldWidth = str.fieldWidth();
578 const QChar oldPadChar =str.padChar();
579 str.setFieldWidth(8);
580 str.setPadChar(u'0');
581 str << Qt::hex << window;
582 str.setFieldWidth(oldFieldWidth);
583 str.setPadChar(oldPadChar);
584 str << Qt::dec << " \""
585 << QXcbWindow::windowTitle(conn: connection, window) << "\" "
586 << geom.width() << 'x' << geom.height() << Qt::forcesign << geom.x() << geom.y()
587 << Qt::noforcesign << '\n';
588
589 auto reply = Q_XCB_REPLY(xcb_query_tree, conn, window);
590 if (reply) {
591 const int count = xcb_query_tree_children_length(R: reply.get());
592 const xcb_window_t *children = xcb_query_tree_children(R: reply.get());
593 for (int i = 0; i < count; ++i)
594 dumpNativeWindowsRecursion(connection, window: children[i], level: level + 1, str);
595 }
596}
597
598QString QXcbNativeInterface::dumpConnectionNativeWindows(const QXcbConnection *connection, WId root) const
599{
600 QString result;
601 QTextStream str(&result);
602 if (root) {
603 dumpNativeWindowsRecursion(connection, window: xcb_window_t(root), level: 0, str);
604 } else {
605 for (const QXcbScreen *screen : connection->screens()) {
606 str << "Screen: \"" << screen->name() << "\"\n";
607 dumpNativeWindowsRecursion(connection, window: screen->root(), level: 0, str);
608 str << '\n';
609 }
610 }
611 return result;
612}
613
614QString QXcbNativeInterface::dumpNativeWindows(WId root) const
615{
616 return dumpConnectionNativeWindows(connection: QXcbIntegration::instance()->connection(), root);
617}
618
619QT_END_NAMESPACE
620
621#include "moc_qxcbnativeinterface.cpp"
622

source code of qtbase/src/plugins/platforms/xcb/qxcbnativeinterface.cpp