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 | |
59 | QT_BEGIN_NAMESPACE |
60 | |
61 | static 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 | */ |
90 | QX11Info::QX11Info() |
91 | { |
92 | } |
93 | |
94 | /*! |
95 | Returns true if the application is currently running on X11. |
96 | |
97 | \since 5.2 |
98 | */ |
99 | bool 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 | */ |
115 | int 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 | */ |
142 | int 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 | */ |
170 | unsigned long QX11Info::appRootWindow(int screen) |
171 | #else |
172 | quint32 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 | */ |
203 | int 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 | */ |
218 | unsigned 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 | */ |
234 | unsigned 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 | */ |
250 | void 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 | */ |
271 | void 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 | */ |
295 | unsigned 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 | */ |
316 | QByteArray 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 | */ |
338 | void 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 | */ |
358 | Display *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 | */ |
375 | xcb_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 | */ |
395 | bool 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 | */ |
422 | qint32 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 | */ |
450 | bool 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 | */ |
550 | bool 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 | |
570 | QT_END_NAMESPACE |
571 | |