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

source code of qtx11extras/src/x11extras/qx11info_x11.cpp