1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Richard Moore <rich@kde.org>
3// Copyright (C) 2016 David Faure <david.faure@kdab.com>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qtx11extras_p.h"
7
8#include <qpa/qplatformnativeinterface.h>
9#include <qpa/qplatformwindow.h>
10#include <qpa/qplatformscreen_p.h>
11#include <qpa/qplatformscreen.h>
12#include <qscreen.h>
13#include <qwindow.h>
14#include <qguiapplication.h>
15#include <xcb/xcb.h>
16#include <QtCore/qdebug.h>
17
18QT_BEGIN_NAMESPACE
19
20using namespace Qt::StringLiterals;
21
22static QScreen *findScreenForVirtualDesktop(int virtualDesktopNumber)
23{
24 const auto screens = QGuiApplication::screens();
25 for (QScreen *screen : screens) {
26 auto *qxcbScreen = dynamic_cast<QNativeInterface::Private::QXcbScreen *>(screen->handle());
27 if (qxcbScreen && qxcbScreen->virtualDesktopNumber() == virtualDesktopNumber)
28 return screen;
29 }
30 return nullptr;
31}
32
33/*!
34 \class QX11Info
35 \inmodule QtGui
36 \since 6.2
37 \internal
38
39 \brief Provides information about the X display configuration.
40
41 The class provides two APIs: a set of non-static functions that
42 provide information about a specific widget or pixmap, and a set
43 of static functions that provide the default information for the
44 application.
45
46 \warning This class is only available on X11. For querying
47 per-screen information in a portable way, use QScreen.
48*/
49
50/*!
51 Constructs an empty QX11Info object.
52*/
53QX11Info::QX11Info()
54{
55}
56
57/*!
58 Returns true if the application is currently running on X11.
59
60 \since 6.2
61 */
62bool QX11Info::isPlatformX11()
63{
64 return QGuiApplication::platformName() == "xcb"_L1;
65}
66
67/*!
68 Returns the horizontal resolution of the given \a screen in terms of the
69 number of dots per inch.
70
71 The \a screen argument is an X screen number. Be aware that if
72 the user's system uses Xinerama (as opposed to traditional X11
73 multiscreen), there is only one X screen. Use QScreen to
74 query for information about Xinerama screens.
75
76 \sa appDpiY()
77*/
78int QX11Info::appDpiX(int screen)
79{
80 if (screen == -1) {
81 const QScreen *scr = QGuiApplication::primaryScreen();
82 if (!scr)
83 return 75;
84 return qRound(d: scr->logicalDotsPerInchX());
85 }
86
87 QScreen *scr = findScreenForVirtualDesktop(virtualDesktopNumber: screen);
88 if (!scr)
89 return 0;
90
91 return scr->logicalDotsPerInchX();
92}
93
94/*!
95 Returns the vertical resolution of the given \a screen in terms of the
96 number of dots per inch.
97
98 The \a screen argument is an X screen number. Be aware that if
99 the user's system uses Xinerama (as opposed to traditional X11
100 multiscreen), there is only one X screen. Use QScreen to
101 query for information about Xinerama screens.
102
103 \sa appDpiX()
104*/
105int QX11Info::appDpiY(int screen)
106{
107 if (screen == -1) {
108 const QScreen *scr = QGuiApplication::primaryScreen();
109 if (!scr)
110 return 75;
111 return qRound(d: scr->logicalDotsPerInchY());
112 }
113
114 QScreen *scr = findScreenForVirtualDesktop(virtualDesktopNumber: screen);
115 if (!scr)
116 return 0;
117
118 return scr->logicalDotsPerInchY();
119}
120
121/*!
122 Returns a handle for the applications root window on the given \a screen.
123
124 The \a screen argument is an X screen number. Be aware that if
125 the user's system uses Xinerama (as opposed to traditional X11
126 multiscreen), there is only one X screen. Use QScreen to
127 query for information about Xinerama screens.
128*/
129quint32 QX11Info::appRootWindow(int screen)
130{
131 if (!qApp)
132 return 0;
133 QPlatformNativeInterface *native = qApp->platformNativeInterface();
134 if (!native)
135 return 0;
136 QScreen *scr = screen == -1 ? QGuiApplication::primaryScreen() : findScreenForVirtualDesktop(virtualDesktopNumber: screen);
137 if (!scr)
138 return 0;
139 return static_cast<xcb_window_t>(reinterpret_cast<quintptr>(native->nativeResourceForScreen(QByteArrayLiteral("rootwindow"), screen: scr)));
140}
141
142/*!
143 Returns the number of the screen where the application is being
144 displayed.
145
146 This method refers to screens in the original X11 meaning with a
147 different DISPLAY environment variable per screen.
148 This information is only useful if your application needs to know
149 on which X screen it is running.
150
151 In a typical multi-head configuration, multiple physical monitors
152 are combined in one X11 screen. This means this method returns the
153 same number for each of the physical monitors. In such a setup you
154 are interested in the monitor information as provided by the X11
155 RandR extension. This is available through QScreen.
156
157 \sa display()
158*/
159int QX11Info::appScreen()
160{
161 if (!qApp)
162 return 0;
163 QPlatformNativeInterface *native = qApp->platformNativeInterface();
164 if (!native)
165 return 0;
166 return reinterpret_cast<qintptr>(native->nativeResourceForIntegration(QByteArrayLiteral("x11screen")));
167}
168
169/*!
170 Returns the X11 time.
171
172 \sa setAppTime(), appUserTime()
173*/
174quint32 QX11Info::appTime()
175{
176 if (!qApp)
177 return 0;
178 QPlatformNativeInterface *native = qApp->platformNativeInterface();
179 if (!native)
180 return 0;
181 QScreen* screen = QGuiApplication::primaryScreen();
182 return static_cast<xcb_timestamp_t>(reinterpret_cast<quintptr>(native->nativeResourceForScreen(resource: "apptime", screen)));
183}
184
185/*!
186 Returns the X11 user time.
187
188 \sa setAppUserTime(), appTime()
189*/
190quint32 QX11Info::appUserTime()
191{
192 if (!qApp)
193 return 0;
194 QPlatformNativeInterface *native = qApp->platformNativeInterface();
195 if (!native)
196 return 0;
197 QScreen* screen = QGuiApplication::primaryScreen();
198 return static_cast<xcb_timestamp_t>(reinterpret_cast<quintptr>(native->nativeResourceForScreen(resource: "appusertime", screen)));
199}
200
201/*!
202 Sets the X11 time to the value specified by \a time.
203
204 \sa appTime(), setAppUserTime()
205*/
206void QX11Info::setAppTime(quint32 time)
207{
208 if (!qApp)
209 return;
210 QPlatformNativeInterface *native = qApp->platformNativeInterface();
211 if (!native)
212 return;
213 typedef void (*SetAppTimeFunc)(QScreen *, xcb_timestamp_t);
214 QScreen* screen = QGuiApplication::primaryScreen();
215 SetAppTimeFunc func = reinterpret_cast<SetAppTimeFunc>(reinterpret_cast<void *>(native->nativeResourceFunctionForScreen(resource: "setapptime")));
216 if (func)
217 func(screen, time);
218 else
219 qWarning(msg: "Internal error: QPA plugin doesn't implement setAppTime");
220}
221
222/*!
223 Sets the X11 user time as specified by \a time.
224
225 \sa appUserTime(), setAppTime()
226*/
227void QX11Info::setAppUserTime(quint32 time)
228{
229 if (!qApp)
230 return;
231 QPlatformNativeInterface *native = qApp->platformNativeInterface();
232 if (!native)
233 return;
234 typedef void (*SetAppUserTimeFunc)(QScreen *, xcb_timestamp_t);
235 QScreen* screen = QGuiApplication::primaryScreen();
236 SetAppUserTimeFunc func = reinterpret_cast<SetAppUserTimeFunc>(reinterpret_cast<void *>(native->nativeResourceFunctionForScreen(resource: "setappusertime")));
237 if (func)
238 func(screen, time);
239 else
240 qWarning(msg: "Internal error: QPA plugin doesn't implement setAppUserTime");
241}
242
243/*!
244 Fetches the current X11 time stamp from the X Server.
245
246 This method creates a property notify event and blocks till it is
247 received back from the X Server.
248*/
249quint32 QX11Info::getTimestamp()
250{
251 if (!qApp)
252 return 0;
253 QPlatformNativeInterface *native = qApp->platformNativeInterface();
254 if (!native)
255 return 0;
256 QScreen* screen = QGuiApplication::primaryScreen();
257 return static_cast<xcb_timestamp_t>(reinterpret_cast<quintptr>(native->nativeResourceForScreen(resource: "gettimestamp", screen)));
258}
259
260/*!
261 Returns the startup ID that will be used for the next window to be shown by this process.
262
263 After the next window is shown, the next startup ID will be empty.
264
265 http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
266
267 \sa setNextStartupId()
268*/
269QByteArray QX11Info::nextStartupId()
270{
271 if (!qApp)
272 return QByteArray();
273 QPlatformNativeInterface *native = qApp->platformNativeInterface();
274 if (!native)
275 return QByteArray();
276 return static_cast<char *>(native->nativeResourceForIntegration(resource: "startupid"));
277}
278
279/*!
280 Sets the next startup ID to \a id.
281
282 This is the startup ID that will be used for the next window to be shown by this process.
283
284 The startup ID of the first window comes from the environment variable DESKTOP_STARTUP_ID.
285 This method is useful for subsequent windows, when the request comes from another process
286 (e.g. via DBus).
287
288 \sa nextStartupId()
289*/
290void QX11Info::setNextStartupId(const QByteArray &id)
291{
292 if (!qApp)
293 return;
294 QPlatformNativeInterface *native = qApp->platformNativeInterface();
295 if (!native)
296 return;
297 typedef void (*SetStartupIdFunc)(const char*);
298 SetStartupIdFunc func = reinterpret_cast<SetStartupIdFunc>(reinterpret_cast<void *>(native->nativeResourceFunctionForIntegration(resource: "setstartupid")));
299 if (func)
300 func(id.constData());
301 else
302 qWarning(msg: "Internal error: QPA plugin doesn't implement setStartupId");
303}
304
305/*!
306 Returns the default display for the application.
307
308 \sa appScreen()
309*/
310Display *QX11Info::display()
311{
312 if (!qApp)
313 return nullptr;
314 QPlatformNativeInterface *native = qApp->platformNativeInterface();
315 if (!native)
316 return nullptr;
317
318 void *display = native->nativeResourceForIntegration(resource: QByteArray("display"));
319 return reinterpret_cast<Display *>(display);
320}
321
322/*!
323 Returns the default XCB connection for the application.
324
325 \sa display()
326*/
327xcb_connection_t *QX11Info::connection()
328{
329 if (!qApp)
330 return nullptr;
331 QPlatformNativeInterface *native = qApp->platformNativeInterface();
332 if (!native)
333 return nullptr;
334
335 void *connection = native->nativeResourceForIntegration(resource: QByteArray("connection"));
336 return reinterpret_cast<xcb_connection_t *>(connection);
337}
338
339/*!
340 Returns true if there is a compositing manager running for the connection
341 attached to \a screen.
342
343 If \a screen equals -1, the application's primary screen is used.
344*/
345bool QX11Info::isCompositingManagerRunning(int screen)
346{
347 if (!qApp)
348 return false;
349 QPlatformNativeInterface *native = qApp->platformNativeInterface();
350 if (!native)
351 return false;
352
353 QScreen *scr = screen == -1 ? QGuiApplication::primaryScreen() : findScreenForVirtualDesktop(virtualDesktopNumber: screen);
354 if (!scr) {
355 qWarning() << "isCompositingManagerRunning: Could not find screen number" << screen;
356 return false;
357 }
358
359 return native->nativeResourceForScreen(resource: QByteArray("compositingEnabled"), screen: scr);
360}
361
362/*!
363 Returns a new peeker id or -1 if some internal error has occurred.
364 Each peeker id is associated with an index in the buffered native
365 event queue.
366
367 For more details see QX11Info::PeekOption and peekEventQueue().
368
369 \sa peekEventQueue(), removePeekerId()
370*/
371qint32 QX11Info::generatePeekerId()
372{
373 if (!qApp)
374 return -1;
375 QPlatformNativeInterface *native = qApp->platformNativeInterface();
376 if (!native)
377 return -1;
378
379 typedef qint32 (*GeneratePeekerIdFunc)(void);
380 GeneratePeekerIdFunc generatepeekerid = reinterpret_cast<GeneratePeekerIdFunc>(
381 reinterpret_cast<void *>(native->nativeResourceFunctionForIntegration(resource: "generatepeekerid")));
382 if (!generatepeekerid) {
383 qWarning(msg: "Internal error: QPA plugin doesn't implement generatePeekerId");
384 return -1;
385 }
386
387 return generatepeekerid();
388}
389
390/*!
391 Removes \a peekerId, which was earlier obtained via generatePeekerId().
392
393 Returns \c true on success or \c false if unknown peeker id was
394 provided or some internal error has occurred.
395
396 \sa generatePeekerId()
397*/
398bool QX11Info::removePeekerId(qint32 peekerId)
399{
400 if (!qApp)
401 return false;
402 QPlatformNativeInterface *native = qApp->platformNativeInterface();
403 if (!native)
404 return false;
405
406 typedef bool (*RemovePeekerIdFunc)(qint32);
407 RemovePeekerIdFunc removePeekerId = reinterpret_cast<RemovePeekerIdFunc>(
408 reinterpret_cast<void *>(native->nativeResourceFunctionForIntegration(resource: "removepeekerid")));
409 if (!removePeekerId) {
410 qWarning(msg: "Internal error: QPA plugin doesn't implement removePeekerId");
411 return false;
412 }
413
414 return removePeekerId(peekerId);
415}
416
417/*!
418 \enum QX11Info::PeekOption
419 \brief An enum to tune the behavior of QX11Info::peekEventQueue().
420
421 \value PeekDefault
422 Peek from the beginning of the buffered native event queue. A peeker
423 id is optional with PeekDefault. If a peeker id is provided to
424 peekEventQueue() when using PeekDefault, then peeking starts from
425 the beginning of the queue, not from the cached index; thus, this
426 can be used to manually reset a cached index to peek from the start
427 of the queue. When this operation completes, the associated index
428 will be updated to the new position in the queue.
429
430 \value PeekFromCachedIndex
431 QX11Info::peekEventQueue() can optimize the peeking algorithm by
432 skipping events that it already has seen in earlier calls to
433 peekEventQueue(). When control returns to the main event loop,
434 which causes the buffered native event queue to be flushed to Qt's
435 event queue, the cached indices are marked invalid and will be
436 reset on the next access. The same is true if the program
437 explicitly flushes the buffered native event queue by
438 QCoreApplication::processEvents().
439*/
440
441/*!
442 \typedef QX11Info::PeekerCallback
443 Typedef for a pointer to a function with the following signature:
444
445 \code
446 bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData);
447 \endcode
448
449 The \a event is a native XCB event.
450 The \a peekerData is a pointer to data, passed in via peekEventQueue().
451
452 Return \c true from this function to stop examining the buffered
453 native event queue or \c false to continue.
454
455 \note A non-capturing lambda can serve as a PeekerCallback.
456*/
457
458/*!
459 \brief Peek into the buffered XCB event queue.
460
461 You can call peekEventQueue() periodically, when your program is busy
462 performing a long-running operation, to peek into the buffered native
463 event queue. The more time the long-running operation blocks the
464 program from returning control to the main event loop, the more
465 events will accumulate in the buffered XCB event queue. Once control
466 returns to the main event loop these events will be flushed to Qt's
467 event queue, which is a separate event queue from the queue this
468 function is peeking into.
469
470 \note It is usually better to run CPU-intensive operations in a
471 non-GUI thread, instead of blocking the main event loop.
472
473 The buffered XCB event queue is populated from a non-GUI thread and
474 therefore might be ahead of the current GUI state. To handle native
475 events as they are processed by the GUI thread, see
476 QAbstractNativeEventFilter::nativeEventFilter().
477
478 The \a peeker is a callback function as documented in PeekerCallback.
479 The \a peekerData can be used to pass in arbitrary data to the \a
480 peeker callback.
481 The \a option is an enum that tunes the behavior of peekEventQueue().
482 The \a peekerId is used to track an index in the queue, for more
483 details see QX11Info::PeekOption. There can be several indices,
484 each tracked individually by a peeker id obtained via generatePeekerId().
485
486 This function returns \c true when the peeker has stopped the event
487 proccesing by returning \c true from the callback. If there were no
488 events in the buffered native event queue to peek at or all the
489 events have been processed by the peeker, this function returns \c
490 false.
491
492 \sa generatePeekerId(), QAbstractNativeEventFilter::nativeEventFilter()
493*/
494bool QX11Info::peekEventQueue(PeekerCallback peeker, void *peekerData, PeekOptions option,
495 qint32 peekerId)
496{
497 if (!peeker || !qApp)
498 return false;
499 QPlatformNativeInterface *native = qApp->platformNativeInterface();
500 if (!native)
501 return false;
502
503 typedef bool (*PeekEventQueueFunc)(PeekerCallback, void *, PeekOptions, qint32);
504 PeekEventQueueFunc peekeventqueue = reinterpret_cast<PeekEventQueueFunc>(
505 reinterpret_cast<void *>(native->nativeResourceFunctionForIntegration(resource: "peekeventqueue")));
506 if (!peekeventqueue) {
507 qWarning(msg: "Internal error: QPA plugin doesn't implement peekEventQueue");
508 return false;
509 }
510
511 return peekeventqueue(peeker, peekerData, option, peekerId);
512}
513
514QT_END_NAMESPACE
515

source code of qtbase/src/gui/platform/unix/qtx11extras.cpp