VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/platform/nix/VBoxUtils-nix.cpp@ 106354

Last change on this file since 106354 was 106354, checked in by vboxsync, 5 months ago

FE/Qt: bugref:10407. Check Qt version before using QWaylandApplication struct as it is introduced in Qt 6.5.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.9 KB
Line 
1/* $Id: VBoxUtils-nix.cpp 106354 2024-10-16 11:40:22Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Declarations of utility classes and functions for handling X11 specific tasks.
4 */
5
6/*
7 * Copyright (C) 2008-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#ifdef VBOX_WITH_SCREENSAVER_CONTROL
30# include <QtDBus/QDBusConnection>
31# include <QtDBus/QDBusConnectionInterface>
32# include <QtDBus/QDBusInterface>
33# include <QtDBus/QDBusReply>
34# include <QtXml/QDomDocument>
35# include <QtXml/QDomElement>
36#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
37#include <QWidget>
38#include <QGuiApplication>
39
40/* GUI includes: */
41#include "UILoggingDefs.h"
42#include "VBoxUtils-nix.h"
43
44/* Other VBox includes: */
45#include <iprt/assert.h>
46#include <iprt/env.h>
47#include <iprt/process.h>
48#include <iprt/string.h>
49
50/* Other includes: */
51#undef BOOL /* Undefine the VBox/com/defs.h variant */
52#define BOOL X11BOOL /* Typedef'ed in Xmd.h via dpms.h, causing -Wpch-invalid to trigger. */
53#include <X11/Xatom.h>
54#include <X11/Xutil.h>
55#include <X11/extensions/dpms.h>
56#undef BOOL /* Restore the VBox/com/defs.h variant */
57#define BOOL PRBool
58#include <dlfcn.h>
59
60VBGHDISPLAYSERVERTYPE NativeWindowSubsystem::displayServerType()
61{
62 if (!qApp)
63 return VBGHDISPLAYSERVERTYPE_NONE;
64 QNativeInterface::QX11Application *pX11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
65 if (pX11App)
66 return VBGHDISPLAYSERVERTYPE_X11;
67#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
68 QNativeInterface::QWaylandApplication *pWaylandApp = qApp->nativeInterface<QNativeInterface::QWaylandApplication>();
69 if (pWaylandApp)
70 return VBGHDISPLAYSERVERTYPE_PURE_WAYLAND;
71#else
72 if (QGuiApplication::platformName().contains("wayland", Qt::CaseInsensitive))
73 return VBGHDISPLAYSERVERTYPE_PURE_WAYLAND;
74#endif
75 return VBGHDISPLAYSERVERTYPE_NONE;
76}
77
78bool NativeWindowSubsystem::isCompositingManagerRunning()
79{
80 if (displayServerType() == VBGHDISPLAYSERVERTYPE_X11)
81 return X11IsCompositingManagerRunning();
82 else if (displayServerType() == VBGHDISPLAYSERVERTYPE_PURE_WAYLAND)
83 return WaylandIsCompositingManagerRunning();
84 return false;
85}
86
87bool NativeWindowSubsystem::X11IsCompositingManagerRunning()
88{
89 /* For each screen it manage, compositing manager MUST acquire ownership
90 * of a selection named _NET_WM_CM_Sn, where n is the screen number. */
91 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
92 Atom atom_property_name = XInternAtom(pDisplay, "_NET_WM_CM_S0", True);
93 return XGetSelectionOwner(pDisplay, atom_property_name);
94}
95
96bool NativeWindowSubsystem::WaylandIsCompositingManagerRunning()
97{
98#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
99 QNativeInterface::QWaylandApplication *pWaylandApp = qApp->nativeInterface<QNativeInterface::QWaylandApplication>();
100 if (pWaylandApp)
101 {
102 if (pWaylandApp->compositor())
103 return true;
104 }
105#endif
106 return false;
107}
108
109X11WMType NativeWindowSubsystem::windowManagerType()
110{
111 if (displayServerType() == VBGHDISPLAYSERVERTYPE_X11)
112 return X11WindowManagerType();
113 else if (displayServerType() == VBGHDISPLAYSERVERTYPE_PURE_WAYLAND)
114 return WaylandWindowManagerType();
115 return X11WMType_Unknown;
116}
117
118X11WMType NativeWindowSubsystem::WaylandWindowManagerType()
119{
120 /// @wayland implement
121 return X11WMType_Unknown;
122}
123
124X11WMType NativeWindowSubsystem::X11WindowManagerType()
125{
126 /* Ask if root-window supports check for WM name: */
127 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
128 Atom atom_property_name;
129 Atom atom_returned_type;
130 int iReturnedFormat;
131 unsigned long ulReturnedItemCount;
132 unsigned long ulDummy;
133 unsigned char *pcData = 0;
134 X11WMType wmType = X11WMType_Unknown;
135 atom_property_name = XInternAtom(pDisplay, "_NET_SUPPORTING_WM_CHECK", True);
136 if (XGetWindowProperty(pDisplay, NativeWindowSubsystem::X11GetAppRootWindow(), atom_property_name,
137 0, 512, False, XA_WINDOW, &atom_returned_type,
138 &iReturnedFormat, &ulReturnedItemCount, &ulDummy, &pcData) == Success)
139 {
140 Window WMWindow = None;
141 if (atom_returned_type == XA_WINDOW && iReturnedFormat == 32)
142 WMWindow = *((Window*)pcData);
143 if (pcData)
144 XFree(pcData);
145 if (WMWindow != None)
146 {
147 /* Ask root-window for WM name: */
148 atom_property_name = XInternAtom(pDisplay, "_NET_WM_NAME", True);
149 Atom utf8Atom = XInternAtom(pDisplay, "UTF8_STRING", True);
150 if (XGetWindowProperty(pDisplay, WMWindow, atom_property_name,
151 0, 512, False, utf8Atom, &atom_returned_type,
152 &iReturnedFormat, &ulReturnedItemCount, &ulDummy, &pcData) == Success)
153 {
154 /** @todo r=bird: 6 QString conversions cannot be very efficient. */
155 if (QString((const char*)pcData).contains("Compiz", Qt::CaseInsensitive))
156 wmType = X11WMType_Compiz;
157 else
158 if (QString((const char*)pcData).contains("GNOME Shell", Qt::CaseInsensitive))
159 wmType = X11WMType_GNOMEShell;
160 else
161 if (QString((const char*)pcData).contains("KWin", Qt::CaseInsensitive))
162 wmType = X11WMType_KWin;
163 else
164 if (QString((const char*)pcData).contains("Metacity", Qt::CaseInsensitive))
165 wmType = X11WMType_Metacity;
166 else
167 if (QString((const char*)pcData).contains("Mutter", Qt::CaseInsensitive))
168 wmType = X11WMType_Mutter;
169 else
170 if (QString((const char*)pcData).contains("Xfwm4", Qt::CaseInsensitive))
171 wmType = X11WMType_Xfwm4;
172 if (pcData)
173 XFree(pcData);
174 }
175 }
176 }
177 return wmType;
178}
179
180bool NativeWindowSubsystem::checkExtension(bool fIsXServerAvailable, const char *pExtensionName)
181{
182 if (fIsXServerAvailable)
183 return X11CheckExtension(pExtensionName);
184 return WaylandCheckExtension(pExtensionName);
185}
186
187bool NativeWindowSubsystem::X11CheckExtension(const char *pExtensionName)
188{
189 /* Check extension: */
190 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
191 int major_opcode;
192 int first_event;
193 int first_error;
194 return XQueryExtension(pDisplay, pExtensionName, &major_opcode, &first_event, &first_error);
195}
196
197bool NativeWindowSubsystem::WaylandCheckExtension(const char *pExtensionName)
198{
199 Q_UNUSED(pExtensionName);
200 /// @todo implement
201 return false;
202}
203
204#ifdef VBOX_WITH_SCREENSAVER_CONTROL
205bool checkDBusConnection(const QDBusConnection &connection)
206{
207 if (!connection.isConnected())
208 {
209 const QDBusError lastError = connection.lastError();
210 if (lastError.isValid())
211 {
212 LogRel(("QDBus error. Could not connect to D-Bus server: %s: %s\n",
213 lastError.name().toUtf8().constData(),
214 lastError.message().toUtf8().constData()));
215 }
216 else
217 LogRel(("QDBus error. Could not connect to D-Bus server: Unable to load dbus libraries\n"));
218 return false;
219 }
220 return true;
221}
222
223QStringList findDBusScreenSaverServices(const QDBusConnection &connection)
224{
225 QStringList serviceNames;
226
227 QDBusReply<QStringList> replyr = connection.interface()->registeredServiceNames();
228 if (!replyr.isValid())
229 {
230 const QDBusError replyError = replyr.error();
231 LogRel(("QDBus error. Could not query registered service names %s %s",
232 replyError.name().toUtf8().constData(), replyError.message().toUtf8().constData()));
233 return serviceNames;
234 }
235
236 for (int i = 0; i < replyr.value().size(); ++i)
237 {
238 const QString strServiceName = replyr.value()[i];
239 if (strServiceName.contains("screensaver", Qt::CaseInsensitive))
240 serviceNames << strServiceName;
241 }
242 if (serviceNames.isEmpty())
243 LogRel(("QDBus error. No screen saver service found among registered DBus services."));
244
245 return serviceNames;
246}
247#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
248
249bool NativeWindowSubsystem::checkDBusScreenSaverServices()
250{
251#ifdef VBOX_WITH_SCREENSAVER_CONTROL
252 QDBusConnection connection = QDBusConnection::sessionBus();
253 if (!checkDBusConnection(connection))
254 return false;
255
256 QDBusReply<QStringList> replyr = connection.interface()->registeredServiceNames();
257 if (!replyr.isValid())
258 {
259 const QDBusError replyError = replyr.error();
260 LogRel(("QDBus error. Could not query registered service names %s %s",
261 replyError.name().toUtf8().constData(), replyError.message().toUtf8().constData()));
262 return false;
263 }
264 for (int i = 0; i < replyr.value().size(); ++i)
265 {
266 const QString strServiceName = replyr.value()[i];
267 if (strServiceName.contains("screensaver", Qt::CaseInsensitive))
268 return true;
269 }
270 LogRel(("QDBus error. No screen saver service found among registered DBus services."));
271#else
272 printf("not defined\n");
273#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
274 return false;
275}
276
277#ifdef VBOX_WITH_SCREENSAVER_CONTROL
278void introspectDBusInterfaceNode(const QDomElement &interface,
279 const QString &strServiceName,
280 QVector<DBusScreenSaverInhibitMethod*> &methods)
281{
282 QDomElement child = interface.firstChildElement();
283 while (!child.isNull())
284 {
285 if (child.tagName() == "method" && child.attribute("name") == "Inhibit")
286 {
287 DBusScreenSaverInhibitMethod *newMethod = new DBusScreenSaverInhibitMethod;
288 newMethod->m_iCookie = 0;
289 newMethod->m_strServiceName = strServiceName;
290 newMethod->m_strInterface = interface.attribute("name");
291 newMethod->m_strPath = "/";
292 newMethod->m_strPath.append(interface.attribute("name"));
293 newMethod->m_strPath.replace(".", "/");
294 methods.append(newMethod);
295 }
296 child = child.nextSiblingElement();
297 }
298}
299
300void introspectDBusServices(const QDBusConnection &connection,
301 const QString &strService,
302 const QString &strPath,
303 QVector<DBusScreenSaverInhibitMethod*> &methods)
304{
305 QDBusMessage call = QDBusMessage::createMethodCall(strService, strPath.isEmpty() ? QLatin1String("/") : strPath,
306 QLatin1String("org.freedesktop.DBus.Introspectable"),
307 QLatin1String("Introspect"));
308 QDBusReply<QString> xmlReply = connection.call(call);
309
310 if (!xmlReply.isValid())
311 return;
312
313 QDomDocument doc;
314 doc.setContent(xmlReply);
315 QDomElement node = doc.documentElement();
316 QDomElement child = node.firstChildElement();
317 while (!child.isNull())
318 {
319 if (child.tagName() == QLatin1String("node"))
320 {
321 QString subPath = strPath + QLatin1Char('/') + child.attribute(QLatin1String("name"));
322 introspectDBusServices(connection, strService, subPath, methods);
323 }
324 else if (child.tagName() == QLatin1String("interface"))
325 introspectDBusInterfaceNode(child, strService, methods);
326 child = child.nextSiblingElement();
327 }
328}
329#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
330
331QVector<DBusScreenSaverInhibitMethod*> NativeWindowSubsystem::findDBusScrenSaverInhibitMethods()
332{
333 QVector<DBusScreenSaverInhibitMethod*> methods;
334#ifdef VBOX_WITH_SCREENSAVER_CONTROL
335 QDBusConnection connection = QDBusConnection::sessionBus();
336 if (!checkDBusConnection(connection))
337 return methods;
338
339 QStringList services = findDBusScreenSaverServices(connection);
340 foreach(const QString &strServiceName, services)
341 introspectDBusServices(connection, strServiceName, "", methods);
342#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
343 return methods;
344}
345
346void NativeWindowSubsystem::toggleHostScrenSaver(bool fInhibit, QVector<DBusScreenSaverInhibitMethod*> &inOutInhibitMethods)
347{
348#ifdef VBOX_WITH_SCREENSAVER_CONTROL
349 QDBusConnection connection = QDBusConnection::sessionBus();
350 if (!checkDBusConnection(connection))
351 return;
352 for (int i = 0; i < inOutInhibitMethods.size(); ++i)
353 {
354 QDBusInterface screenSaverInterface(inOutInhibitMethods[i]->m_strServiceName, inOutInhibitMethods[i]->m_strPath,
355 inOutInhibitMethods[i]->m_strInterface, connection);
356 if (!screenSaverInterface.isValid())
357 {
358 QDBusError error = screenSaverInterface.lastError();
359 LogRel(("QDBus error for service %s: %s. %s\n",
360 inOutInhibitMethods[i]->m_strServiceName.toUtf8().constData(),
361 error.name().toUtf8().constData(),
362 error.message().toUtf8().constData()));
363 continue;
364 }
365 QDBusReply<uint> reply;
366 if (fInhibit)
367 {
368 reply = screenSaverInterface.call("Inhibit", "Oracle VirtualBox", "ScreenSaverInhibit");
369 if (reply.isValid())
370 inOutInhibitMethods[i]->m_iCookie = reply.value();
371 }
372 else
373 {
374 reply = screenSaverInterface.call("UnInhibit", inOutInhibitMethods[i]->m_iCookie);
375 }
376 if (!reply.isValid())
377 {
378 QDBusError error = reply.error();
379 LogRel(("QDBus inhibition call error for service %s: %s. %s\n",
380 inOutInhibitMethods[i]->m_strServiceName.toUtf8().constData(),
381 error.name().toUtf8().constData(),
382 error.message().toUtf8().constData()));
383 }
384 }
385#else
386 Q_UNUSED(fInhibit);
387 Q_UNUSED(inOutInhibitMethods);
388#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
389}
390
391char *XXGetProperty(Display *pDpy, Window windowHandle, Atom propType, const char *pszPropName)
392{
393 Atom propNameAtom = XInternAtom(pDpy, pszPropName, True /* only_if_exists */);
394 if (propNameAtom == None)
395 return NULL;
396
397 Atom actTypeAtom = None;
398 int actFmt = 0;
399 unsigned long nItems = 0;
400 unsigned long nBytesAfter = 0;
401 unsigned char *propVal = NULL;
402 int rc = XGetWindowProperty(pDpy, windowHandle, propNameAtom,
403 0, LONG_MAX, False /* delete */,
404 propType, &actTypeAtom, &actFmt,
405 &nItems, &nBytesAfter, &propVal);
406 if (rc != Success)
407 return NULL;
408
409 return reinterpret_cast<char*>(propVal);
410}
411
412bool XXSendClientMessage(Display *pDpy, Window windowHandle, const char *pszMsg,
413 unsigned long aData0 = 0, unsigned long aData1 = 0,
414 unsigned long aData2 = 0, unsigned long aData3 = 0,
415 unsigned long aData4 = 0)
416{
417 Atom msgAtom = XInternAtom(pDpy, pszMsg, True /* only_if_exists */);
418 if (msgAtom == None)
419 return false;
420
421 XEvent ev;
422
423 ev.xclient.type = ClientMessage;
424 ev.xclient.serial = 0;
425 ev.xclient.send_event = True;
426 ev.xclient.display = pDpy;
427 ev.xclient.window = windowHandle;
428 ev.xclient.message_type = msgAtom;
429
430 /* Always send as 32 bit for now: */
431 ev.xclient.format = 32;
432 ev.xclient.data.l[0] = aData0;
433 ev.xclient.data.l[1] = aData1;
434 ev.xclient.data.l[2] = aData2;
435 ev.xclient.data.l[3] = aData3;
436 ev.xclient.data.l[4] = aData4;
437
438 return XSendEvent(pDpy, DefaultRootWindow(pDpy), False,
439 SubstructureRedirectMask, &ev) != 0;
440}
441
442bool NativeWindowSubsystem::activateWindow(WId wId, bool fSwitchDesktop)
443{
444 if (displayServerType() == VBGHDISPLAYSERVERTYPE_X11)
445 return X11ActivateWindow(wId, fSwitchDesktop);
446 else if (displayServerType() == VBGHDISPLAYSERVERTYPE_PURE_WAYLAND)
447 return WaylandActivateWindow(wId, fSwitchDesktop);
448 return false;
449}
450
451bool NativeWindowSubsystem::X11ActivateWindow(WId wId, bool fSwitchDesktop)
452{
453 bool fResult = true;
454 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
455
456 if (fSwitchDesktop)
457 {
458 /* Try to find the desktop ID using the NetWM property: */
459 CARD32 *pDesktop = (CARD32*)XXGetProperty(pDisplay, wId, XA_CARDINAL, "_NET_WM_DESKTOP");
460 if (pDesktop == NULL)
461 // WORKAROUND:
462 // if the NetWM properly is not supported try to find
463 // the desktop ID using the GNOME WM property.
464 pDesktop = (CARD32*)XXGetProperty(pDisplay, wId, XA_CARDINAL, "_WIN_WORKSPACE");
465
466 if (pDesktop != NULL)
467 {
468 bool ok = XXSendClientMessage(pDisplay, DefaultRootWindow(pDisplay), "_NET_CURRENT_DESKTOP", *pDesktop);
469 if (!ok)
470 {
471 Log1WarningFunc(("Couldn't switch to pDesktop=%08X\n", pDesktop));
472 fResult = false;
473 }
474 XFree(pDesktop);
475 }
476 else
477 {
478 Log1WarningFunc(("Couldn't find a pDesktop ID for wId=%08X\n", wId));
479 fResult = false;
480 }
481 }
482
483 bool ok = XXSendClientMessage(pDisplay, wId, "_NET_ACTIVE_WINDOW");
484 fResult &= !!ok;
485
486 XRaiseWindow(pDisplay, wId);
487 return fResult;
488}
489
490bool NativeWindowSubsystem::WaylandActivateWindow(WId wId, bool fSwitchDesktop)
491{
492 /// @todo implement
493 Q_UNUSED(wId);
494 Q_UNUSED(fSwitchDesktop);
495 return false;
496}
497
498bool NativeWindowSubsystem::X11SupportsFullScreenMonitorsProtocol()
499{
500 /* This method tests whether the current X11 window manager supports full-screen mode as we need it.
501 * Unfortunately the EWMH specification was not fully clear about whether we can expect to find
502 * all of these atoms on the _NET_SUPPORTED root window property, so we have to test with all
503 * interesting window managers. If this fails for a user when you think it should succeed
504 * they should try executing:
505 * xprop -root | egrep -w '_NET_WM_FULLSCREEN_MONITORS|_NET_WM_STATE|_NET_WM_STATE_FULLSCREEN'
506 * in an X11 terminal window.
507 * All three strings should be found under a property called "_NET_SUPPORTED(ATOM)". */
508
509 /* Using a global to get at the display does not feel right, but that is how it is done elsewhere in the code. */
510 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
511 Atom atomSupported = XInternAtom(pDisplay, "_NET_SUPPORTED",
512 True /* only_if_exists */);
513 Atom atomWMFullScreenMonitors = XInternAtom(pDisplay,
514 "_NET_WM_FULLSCREEN_MONITORS",
515 True /* only_if_exists */);
516 Atom atomWMState = XInternAtom(pDisplay,
517 "_NET_WM_STATE",
518 True /* only_if_exists */);
519 Atom atomWMStateFullScreen = XInternAtom(pDisplay,
520 "_NET_WM_STATE_FULLSCREEN",
521 True /* only_if_exists */);
522 bool fFoundFullScreenMonitors = false;
523 bool fFoundState = false;
524 bool fFoundStateFullScreen = false;
525 Atom atomType;
526 int cFormat;
527 unsigned long cItems;
528 unsigned long cbLeft;
529 Atom *pAtomHints;
530 int rc;
531 unsigned i;
532
533 if ( atomSupported == None || atomWMFullScreenMonitors == None
534 || atomWMState == None || atomWMStateFullScreen == None)
535 return false;
536 /* Get atom value: */
537 rc = XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay),
538 atomSupported, 0, 0x7fffffff /*LONG_MAX*/,
539 False /* delete */, XA_ATOM, &atomType,
540 &cFormat, &cItems, &cbLeft,
541 (unsigned char **)&pAtomHints);
542 if (rc != Success)
543 return false;
544 if (pAtomHints == NULL)
545 return false;
546 if (atomType == XA_ATOM && cFormat == 32 && cbLeft == 0)
547 for (i = 0; i < cItems; ++i)
548 {
549 if (pAtomHints[i] == atomWMFullScreenMonitors)
550 fFoundFullScreenMonitors = true;
551 if (pAtomHints[i] == atomWMState)
552 fFoundState = true;
553 if (pAtomHints[i] == atomWMStateFullScreen)
554 fFoundStateFullScreen = true;
555 }
556 XFree(pAtomHints);
557 return fFoundFullScreenMonitors && fFoundState && fFoundStateFullScreen;
558}
559
560bool NativeWindowSubsystem::X11SetFullScreenMonitor(QWidget *pWidget, ulong uScreenId)
561{
562 return XXSendClientMessage(NativeWindowSubsystem::X11GetDisplay(),
563 pWidget->window()->winId(),
564 "_NET_WM_FULLSCREEN_MONITORS",
565 uScreenId, uScreenId, uScreenId, uScreenId,
566 1 /* Source indication (1 = normal application) */);
567}
568
569QVector<Atom> flagsNetWmState(QWidget *pWidget)
570{
571 /* Get display: */
572 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
573
574 /* Prepare atoms: */
575 QVector<Atom> resultNetWmState;
576 Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
577
578 /* Get the size of the property data: */
579 Atom actual_type;
580 int iActualFormat;
581 ulong uPropertyLength;
582 ulong uBytesLeft;
583 uchar *pPropertyData = 0;
584 if (XGetWindowProperty(pDisplay, pWidget->window()->winId(),
585 net_wm_state, 0, 0, False, XA_ATOM, &actual_type, &iActualFormat,
586 &uPropertyLength, &uBytesLeft, &pPropertyData) == Success &&
587 actual_type == XA_ATOM && iActualFormat == 32)
588 {
589 resultNetWmState.resize(uBytesLeft / 4);
590 XFree((char*)pPropertyData);
591 pPropertyData = 0;
592
593 /* Fetch all data: */
594 if (XGetWindowProperty(pDisplay, pWidget->window()->winId(),
595 net_wm_state, 0, resultNetWmState.size(), False, XA_ATOM, &actual_type, &iActualFormat,
596 &uPropertyLength, &uBytesLeft, &pPropertyData) != Success)
597 resultNetWmState.clear();
598 else if (uPropertyLength != (ulong)resultNetWmState.size())
599 resultNetWmState.resize(uPropertyLength);
600
601 /* Put it into resultNetWmState: */
602 if (!resultNetWmState.isEmpty())
603 memcpy(resultNetWmState.data(), pPropertyData, resultNetWmState.size() * sizeof(Atom));
604 if (pPropertyData)
605 XFree((char*)pPropertyData);
606 }
607
608 /* Return result: */
609 return resultNetWmState;
610}
611
612#if 0 // unused for now?
613bool NativeWindowSubsystem::isFullScreenFlagSet(QWidget *pWidget)
614{
615 /* Get display: */
616 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
617
618 /* Prepare atoms: */
619 Atom net_wm_state_fullscreen = XInternAtom(pDisplay, "_NET_WM_STATE_FULLSCREEN", True /* only if exists */);
620
621 /* Check if flagsNetWmState(pWidget) contains full-screen flag: */
622 return flagsNetWmState(pWidget).contains(net_wm_state_fullscreen);
623}
624
625void NativeWindowSubsystem::setFullScreenFlag(QWidget *pWidget)
626{
627 /* Get display: */
628 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
629
630 /* Prepare atoms: */
631 QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
632 Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
633 Atom net_wm_state_fullscreen = XInternAtom(pDisplay, "_NET_WM_STATE_FULLSCREEN", True /* only if exists */);
634
635 /* Append resultNetWmState with fullscreen flag if necessary: */
636 if (!resultNetWmState.contains(net_wm_state_fullscreen))
637 {
638 resultNetWmState.append(net_wm_state_fullscreen);
639 /* Apply property to widget again: */
640 XChangeProperty(pDisplay, pWidget->window()->winId(),
641 net_wm_state, XA_ATOM, 32, PropModeReplace,
642 (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
643 }
644}
645#endif // unused for now?
646
647void NativeWindowSubsystem::X11SetSkipTaskBarFlag(QWidget *pWidget)
648{
649 /* Get display: */
650 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
651
652 /* Prepare atoms: */
653 QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
654 Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
655 Atom net_wm_state_skip_taskbar = XInternAtom(pDisplay, "_NET_WM_STATE_SKIP_TASKBAR", True /* only if exists */);
656
657 /* Append resultNetWmState with skip-taskbar flag if necessary: */
658 if (!resultNetWmState.contains(net_wm_state_skip_taskbar))
659 {
660 resultNetWmState.append(net_wm_state_skip_taskbar);
661 /* Apply property to widget again: */
662 XChangeProperty(pDisplay, pWidget->window()->winId(),
663 net_wm_state, XA_ATOM, 32, PropModeReplace,
664 (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
665 }
666}
667
668void NativeWindowSubsystem::X11SetSkipPagerFlag(QWidget *pWidget)
669{
670 /* Get display: */
671 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
672
673 /* Prepare atoms: */
674 QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
675 Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
676 Atom net_wm_state_skip_pager = XInternAtom(pDisplay, "_NET_WM_STATE_SKIP_PAGER", True /* only if exists */);
677
678 /* Append resultNetWmState with skip-pager flag if necessary: */
679 if (!resultNetWmState.contains(net_wm_state_skip_pager))
680 {
681 resultNetWmState.append(net_wm_state_skip_pager);
682 /* Apply property to widget again: */
683 XChangeProperty(pDisplay, pWidget->window()->winId(),
684 net_wm_state, XA_ATOM, 32, PropModeReplace,
685 (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
686 }
687}
688
689void NativeWindowSubsystem::setWMClass(QWidget *pWidget, const QString &strNameString, const QString &strClassString)
690{
691 if (displayServerType() == VBGHDISPLAYSERVERTYPE_X11)
692 X11SetWMClass(pWidget, strNameString, strClassString);
693 else if (displayServerType() == VBGHDISPLAYSERVERTYPE_PURE_WAYLAND)
694 WaylandSetWMClass(pWidget, strNameString, strClassString);
695}
696
697void NativeWindowSubsystem::X11SetWMClass(QWidget *pWidget, const QString &strNameString, const QString &strClassString)
698{
699 /* Make sure all arguments set: */
700 AssertReturnVoid(pWidget && !strNameString.isNull() && !strClassString.isNull());
701
702 /* Define QByteArray objects to make sure data is alive within the scope: */
703 QByteArray nameByteArray;
704 /* Check the existence of RESOURCE_NAME env. variable and override name string if necessary: */
705 const char resourceName[] = "RESOURCE_NAME";
706 if (qEnvironmentVariableIsSet(resourceName))
707 nameByteArray = qgetenv(resourceName);
708 else
709 nameByteArray = strNameString.toLatin1();
710 QByteArray classByteArray = strClassString.toLatin1();
711
712 AssertReturnVoid(nameByteArray.data() && classByteArray.data());
713
714 XClassHint windowClass;
715 windowClass.res_name = nameByteArray.data();
716 windowClass.res_class = classByteArray.data();
717 /* Set WM_CLASS of the window to passed name and class strings: */
718 XSetClassHint(NativeWindowSubsystem::X11GetDisplay(), pWidget->window()->winId(), &windowClass);
719}
720
721void NativeWindowSubsystem::WaylandSetWMClass(QWidget *pWidget, const QString &strNameString, const QString &strClassString)
722{
723 Q_UNUSED(pWidget);
724 Q_UNUSED(strNameString);
725 Q_UNUSED(strClassString);
726 /// @todo implement
727}
728
729void NativeWindowSubsystem::setXwaylandMayGrabKeyboardFlag(QWidget *pWidget)
730{
731 if (displayServerType() == VBGHDISPLAYSERVERTYPE_X11)
732 XXSendClientMessage(NativeWindowSubsystem::X11GetDisplay(), pWidget->window()->winId(),
733 "_XWAYLAND_MAY_GRAB_KEYBOARD", 1);
734}
735
736Display *NativeWindowSubsystem::X11GetDisplay()
737{
738 Display *pDisplay = 0;
739 if (qApp)
740 {
741 QNativeInterface::QX11Application *pX11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
742 if (pX11App)
743 pDisplay = pX11App->display();
744 }
745 Assert(pDisplay);
746 return pDisplay;
747}
748
749xcb_connection_t *NativeWindowSubsystem::X11GetConnection()
750{
751 xcb_connection_t *pConnection = 0;
752 if (qApp)
753 {
754 QNativeInterface::QX11Application *pX11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
755 if (pX11App)
756 pConnection = pX11App->connection();
757 }
758 Assert(pConnection);
759 return pConnection;
760}
761
762uint32_t NativeWindowSubsystem::X11GetAppRootWindow()
763{
764 Window idWindow = 0;
765 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
766 if (pDisplay)
767 idWindow = DefaultRootWindow(pDisplay);
768 return idWindow;
769}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette