VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp@ 102493

Last change on this file since 102493 was 100650, checked in by vboxsync, 14 months ago

FE/Qt: bugref:10450: Forgot something in r158075.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.6 KB
Line 
1/* $Id: UIDesktopWidgetWatchdog.cpp 100650 2023-07-19 11:52:04Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIDesktopWidgetWatchdog class implementation.
4 */
5
6/*
7 * Copyright (C) 2015-2023 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#include <QApplication>
30#include <QWidget>
31#include <QScreen>
32#ifdef VBOX_WS_WIN
33# include <QLibrary>
34#endif
35#ifdef VBOX_WS_NIX
36# include <QTimer>
37#endif
38#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
39# include <QDesktopWidget>
40#endif /* Qt < 5.10 */
41
42/* GUI includes: */
43#include "UIDesktopWidgetWatchdog.h"
44#ifdef VBOX_WS_MAC
45# include "VBoxUtils-darwin.h"
46#endif
47#ifdef VBOX_WS_WIN
48# include "VBoxUtils-win.h"
49#endif
50#ifdef VBOX_WS_NIX
51# include "UICommon.h"
52# include "VBoxUtils-nix.h"
53# ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
54# include "UIConverter.h"
55# endif
56#endif
57
58/* Other VBox includes: */
59#include <iprt/asm.h>
60#include <iprt/assert.h>
61#include <iprt/ldr.h>
62#include <VBox/log.h>
63#ifdef VBOX_WS_WIN
64# include <iprt/win/windows.h>
65#endif
66
67/* External includes: */
68#include <math.h>
69#ifdef VBOX_WS_NIX
70# include <xcb/xcb.h>
71#endif
72
73
74#ifdef VBOX_WS_WIN
75
76# ifndef DPI_ENUMS_DECLARED
77typedef enum _MONITOR_DPI_TYPE // gently stolen from MSDN
78{
79 MDT_EFFECTIVE_DPI = 0,
80 MDT_ANGULAR_DPI = 1,
81 MDT_RAW_DPI = 2,
82 MDT_DEFAULT = MDT_EFFECTIVE_DPI
83} MONITOR_DPI_TYPE;
84# endif
85typedef void (WINAPI *PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
86
87/** Set when dynamic API import is reoslved. */
88static bool volatile g_fResolved;
89/** Pointer to Shcore.dll!GetDpiForMonitor, introduced in windows 8.1. */
90static PFN_GetDpiForMonitor g_pfnGetDpiForMonitor = NULL;
91
92/** @returns true if all APIs found, false if missing APIs */
93static bool ResolveDynamicImports(void)
94{
95 if (!g_fResolved)
96 {
97 PFN_GetDpiForMonitor pfn = (decltype(pfn))RTLdrGetSystemSymbol("Shcore.dll", "GetDpiForMonitor");
98 g_pfnGetDpiForMonitor = pfn;
99 ASMCompilerBarrier();
100
101 g_fResolved = true;
102 }
103 return g_pfnGetDpiForMonitor != NULL;
104}
105
106static BOOL CALLBACK MonitorEnumProcF(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lpClipRect, LPARAM dwData) RT_NOTHROW_DEF
107{
108 /* These required for clipped screens only: */
109 RT_NOREF(hdcMonitor, lpClipRect);
110
111 /* Acquire effective DPI (available since Windows 8.1): */
112 AssertReturn(g_pfnGetDpiForMonitor, false);
113 UINT uOutX = 0;
114 UINT uOutY = 0;
115 g_pfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &uOutX, &uOutY);
116 reinterpret_cast<QList<QPair<int, int> >*>(dwData)->append(qMakePair(uOutX, uOutY));
117
118 return TRUE;
119}
120
121#endif /* VBOX_WS_WIN */
122
123
124#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
125
126/** QWidget extension used as
127 * an invisible window on the basis of which we
128 * can calculate available host-screen geometry. */
129class UIInvisibleWindow : public QWidget
130{
131 Q_OBJECT;
132
133signals:
134
135 /** Notifies listeners about host-screen available-geometry was calulated.
136 * @param iHostScreenIndex holds the index of the host-screen this window created for.
137 * @param availableGeometry holds the available-geometry of the host-screen this window created for. */
138 void sigHostScreenAvailableGeometryCalculated(int iHostScreenIndex, QRect availableGeometry);
139
140public:
141
142 /** Constructs invisible window for the host-screen with @a iHostScreenIndex. */
143 UIInvisibleWindow(int iHostScreenIndex);
144
145private slots:
146
147 /** Performs fallback drop. */
148 void sltFallback();
149
150private:
151
152 /** Move @a pEvent handler. */
153 void moveEvent(QMoveEvent *pEvent);
154 /** Resize @a pEvent handler. */
155 void resizeEvent(QResizeEvent *pEvent);
156
157 /** Holds the index of the host-screen this window created for. */
158 const int m_iHostScreenIndex;
159
160 /** Holds whether the move event came. */
161 bool m_fMoveCame;
162 /** Holds whether the resize event came. */
163 bool m_fResizeCame;
164};
165
166
167/*********************************************************************************************************************************
168* Class UIInvisibleWindow implementation. *
169*********************************************************************************************************************************/
170
171UIInvisibleWindow::UIInvisibleWindow(int iHostScreenIndex)
172 : QWidget(0, Qt::Window | Qt::FramelessWindowHint)
173 , m_iHostScreenIndex(iHostScreenIndex)
174 , m_fMoveCame(false)
175 , m_fResizeCame(false)
176{
177 /* Resize to minimum size of 1 pixel: */
178 resize(1, 1);
179 /* Apply visual and mouse-event mask for that 1 pixel: */
180 setMask(QRect(0, 0, 1, 1));
181 /* For composite WMs make this 1 pixel transparent: */
182 if (uiCommon().isCompositingManagerRunning())
183 setAttribute(Qt::WA_TranslucentBackground);
184 /* Install fallback handler: */
185 QTimer::singleShot(5000, this, SLOT(sltFallback()));
186}
187
188void UIInvisibleWindow::sltFallback()
189{
190 /* Sanity check for fallback geometry: */
191 QRect fallbackGeometry(x(), y(), width(), height());
192 if ( fallbackGeometry.width() <= 1
193 || fallbackGeometry.height() <= 1)
194 fallbackGeometry = gpDesktop->screenGeometry(m_iHostScreenIndex);
195 LogRel(("GUI: UIInvisibleWindow::sltFallback: %s event haven't came. "
196 "Screen: %d, work area: %dx%d x %dx%d\n",
197 !m_fMoveCame ? "Move" : !m_fResizeCame ? "Resize" : "Some",
198 m_iHostScreenIndex, fallbackGeometry.x(), fallbackGeometry.y(), fallbackGeometry.width(), fallbackGeometry.height()));
199 emit sigHostScreenAvailableGeometryCalculated(m_iHostScreenIndex, fallbackGeometry);
200}
201
202void UIInvisibleWindow::moveEvent(QMoveEvent *pEvent)
203{
204 /* We do have both move and resize events,
205 * with no idea who will come first, but we need
206 * to send a final signal after last of events arrived. */
207
208 /* Call to base-class: */
209 QWidget::moveEvent(pEvent);
210
211 /* Ignore 'not-yet-shown' case: */
212 if (!isVisible())
213 return;
214
215 /* Mark move event as received: */
216 m_fMoveCame = true;
217
218 /* If the resize event already came: */
219 if (m_fResizeCame)
220 {
221 /* Notify listeners about host-screen available-geometry was calulated: */
222 LogRel2(("GUI: UIInvisibleWindow::moveEvent: Screen: %d, work area: %dx%d x %dx%d\n", m_iHostScreenIndex,
223 x(), y(), width(), height()));
224 emit sigHostScreenAvailableGeometryCalculated(m_iHostScreenIndex, QRect(x(), y(), width(), height()));
225 }
226}
227
228void UIInvisibleWindow::resizeEvent(QResizeEvent *pEvent)
229{
230 /* We do have both move and resize events,
231 * with no idea who will come first, but we need
232 * to send a final signal after last of events arrived. */
233
234 /* Call to base-class: */
235 QWidget::resizeEvent(pEvent);
236
237 /* Ignore 'not-yet-shown' case: */
238 if (!isVisible())
239 return;
240
241 /* Mark resize event as received: */
242 m_fResizeCame = true;
243
244 /* If the move event already came: */
245 if (m_fMoveCame)
246 {
247 /* Notify listeners about host-screen available-geometry was calulated: */
248 LogRel2(("GUI: UIInvisibleWindow::resizeEvent: Screen: %d, work area: %dx%d x %dx%d\n", m_iHostScreenIndex,
249 x(), y(), width(), height()));
250 emit sigHostScreenAvailableGeometryCalculated(m_iHostScreenIndex, QRect(x(), y(), width(), height()));
251 }
252}
253
254#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
255
256
257/*********************************************************************************************************************************
258* Class UIDesktopWidgetWatchdog implementation. *
259*********************************************************************************************************************************/
260
261/* static */
262UIDesktopWidgetWatchdog *UIDesktopWidgetWatchdog::s_pInstance = 0;
263
264/* static */
265void UIDesktopWidgetWatchdog::create()
266{
267 /* Make sure instance isn't created: */
268 AssertReturnVoid(!s_pInstance);
269
270 /* Create/prepare instance: */
271 new UIDesktopWidgetWatchdog;
272 AssertReturnVoid(s_pInstance);
273 s_pInstance->prepare();
274}
275
276/* static */
277void UIDesktopWidgetWatchdog::destroy()
278{
279 /* Make sure instance is created: */
280 AssertReturnVoid(s_pInstance);
281
282 /* Cleanup/destroy instance: */
283 s_pInstance->cleanup();
284 delete s_pInstance;
285 AssertReturnVoid(!s_pInstance);
286}
287
288UIDesktopWidgetWatchdog::UIDesktopWidgetWatchdog()
289#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
290 : m_enmSynthTestPolicy(DesktopWatchdogPolicy_SynthTest_Both)
291#endif
292{
293 /* Initialize instance: */
294 s_pInstance = this;
295}
296
297UIDesktopWidgetWatchdog::~UIDesktopWidgetWatchdog()
298{
299 /* Deinitialize instance: */
300 s_pInstance = 0;
301}
302
303/* static */
304int UIDesktopWidgetWatchdog::screenCount()
305{
306 return QGuiApplication::screens().size();
307}
308
309/* static */
310int UIDesktopWidgetWatchdog::primaryScreenNumber()
311{
312 return screenToIndex(QGuiApplication::primaryScreen());
313}
314
315/* static */
316int UIDesktopWidgetWatchdog::screenNumber(const QWidget *pWidget)
317{
318 QScreen *pScreen = 0;
319 if (pWidget)
320 if (QWindow *pWindow = pWidget->windowHandle())
321 pScreen = pWindow->screen();
322
323 return screenToIndex(pScreen);
324}
325
326/* static */
327int UIDesktopWidgetWatchdog::screenNumber(const QPoint &point)
328{
329#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
330 return screenToIndex(QGuiApplication::screenAt(point));
331#else /* Qt < 5.10 */
332 return QApplication::desktop()->screenNumber(point);
333#endif /* Qt < 5.10 */
334}
335
336QRect UIDesktopWidgetWatchdog::screenGeometry(QScreen *pScreen) const
337{
338 /* Just return screen geometry: */
339 return pScreen->geometry();
340}
341
342QRect UIDesktopWidgetWatchdog::screenGeometry(int iHostScreenIndex /* = -1 */) const
343{
344 /* Gather suitable screen, use primary if failed: */
345 QScreen *pScreen = QGuiApplication::screens().value(iHostScreenIndex, QGuiApplication::primaryScreen());
346
347 /* Redirect call to wrapper above: */
348 return screenGeometry(pScreen);
349}
350
351QRect UIDesktopWidgetWatchdog::screenGeometry(const QWidget *pWidget) const
352{
353 /* Gather suitable screen, use primary if failed: */
354 QScreen *pScreen = QGuiApplication::primaryScreen();
355 if (pWidget)
356 if (QWindow *pWindow = pWidget->windowHandle())
357 pScreen = pWindow->screen();
358
359 /* Redirect call to wrapper above: */
360 return screenGeometry(pScreen);
361}
362
363QRect UIDesktopWidgetWatchdog::screenGeometry(const QPoint &point) const
364{
365#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
366 /* Gather suitable screen, use primary if failed: */
367 QScreen *pScreen = QGuiApplication::screenAt(point);
368 if (!pScreen)
369 pScreen = QGuiApplication::primaryScreen();
370
371 /* Redirect call to wrapper above: */
372 return screenGeometry(pScreen);
373#else /* Qt < 5.10 */
374 /* Gather suitable screen index: */
375 const int iHostScreenIndex = QApplication::desktop()->screenNumber(point);
376
377 /* Redirect call to wrapper above: */
378 return screenGeometry(iHostScreenIndex);
379#endif /* Qt < 5.10 */
380}
381
382QRect UIDesktopWidgetWatchdog::availableGeometry(QScreen *pScreen) const
383{
384#ifdef VBOX_WS_NIX
385# ifdef VBOX_GUI_WITH_CUSTOMIZATIONS1
386 // WORKAROUND:
387 // For customer WM we don't want Qt to return wrong available geometry,
388 // so we are returning fallback screen geometry in any case..
389 return screenGeometry(pScreen);
390# else /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
391 /* Get cached available-geometry: */
392 const QRect availableGeometry = m_availableGeometryData.value(screenToIndex(pScreen));
393 /* Return cached available-geometry if it's valid or screen-geometry otherwise: */
394 return availableGeometry.isValid() ? availableGeometry : screenGeometry(pScreen);
395# endif /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
396#else /* !VBOX_WS_NIX */
397 /* Just return screen available-geometry: */
398 return pScreen->availableGeometry();
399#endif /* !VBOX_WS_NIX */
400}
401
402QRect UIDesktopWidgetWatchdog::availableGeometry(int iHostScreenIndex /* = -1 */) const
403{
404 /* Gather suitable screen, use primary if failed: */
405 QScreen *pScreen = QGuiApplication::screens().value(iHostScreenIndex, QGuiApplication::primaryScreen());
406
407 /* Redirect call to wrapper above: */
408 return availableGeometry(pScreen);
409}
410
411QRect UIDesktopWidgetWatchdog::availableGeometry(const QWidget *pWidget) const
412{
413 /* Gather suitable screen, use primary if failed: */
414 QScreen *pScreen = QGuiApplication::primaryScreen();
415 if (pWidget)
416 if (QWindow *pWindow = pWidget->windowHandle())
417 pScreen = pWindow->screen();
418
419 /* Redirect call to wrapper above: */
420 return availableGeometry(pScreen);
421}
422
423QRect UIDesktopWidgetWatchdog::availableGeometry(const QPoint &point) const
424{
425#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
426 /* Gather suitable screen, use primary if failed: */
427 QScreen *pScreen = QGuiApplication::screenAt(point);
428 if (!pScreen)
429 pScreen = QGuiApplication::primaryScreen();
430
431 /* Redirect call to wrapper above: */
432 return availableGeometry(pScreen);
433#else /* Qt < 5.10 */
434 /* Gather suitable screen index: */
435 const int iHostScreenIndex = QApplication::desktop()->screenNumber(point);
436
437 /* Redirect call to wrapper above: */
438 return availableGeometry(iHostScreenIndex);
439#endif /* Qt < 5.10 */
440}
441
442/* static */
443QRegion UIDesktopWidgetWatchdog::overallScreenRegion()
444{
445 /* Calculate region: */
446 QRegion region;
447 foreach (QScreen *pScreen, QGuiApplication::screens())
448 region += gpDesktop->screenGeometry(pScreen);
449 return region;
450}
451
452/* static */
453QRegion UIDesktopWidgetWatchdog::overallAvailableRegion()
454{
455 /* Calculate region: */
456 QRegion region;
457 foreach (QScreen *pScreen, QGuiApplication::screens())
458 {
459 /* Get enumerated screen's available area: */
460 QRect rect = gpDesktop->availableGeometry(pScreen);
461#ifdef VBOX_WS_WIN
462 /* On Windows host window can exceed the available
463 * area in maximized/sticky-borders state: */
464 rect.adjust(-10, -10, 10, 10);
465#endif /* VBOX_WS_WIN */
466 /* Append rectangle: */
467 region += rect;
468 }
469 /* Return region: */
470 return region;
471}
472
473#ifdef VBOX_WS_NIX
474/* static */
475bool UIDesktopWidgetWatchdog::isFakeScreenDetected()
476{
477 // WORKAROUND:
478 // In 5.6.1 Qt devs taught the XCB plugin to silently swap last detached screen
479 // with a fake one, and there is no API-way to distinguish fake from real one
480 // because all they do is erasing output for the last real screen, keeping
481 // all other screen attributes stale. Gladly output influencing screen name
482 // so we can use that horrible workaround to detect a fake XCB screen.
483 return qApp->screens().size() == 0 /* zero-screen case is impossible after 5.6.1 */
484 || (qApp->screens().size() == 1 && qApp->screens().first()->name() == ":0.0");
485}
486#endif /* VBOX_WS_NIX */
487
488/* static */
489double UIDesktopWidgetWatchdog::devicePixelRatio(int iHostScreenIndex /* = -1 */)
490{
491 /* First, we should check whether the screen is valid: */
492 QScreen *pScreen = iHostScreenIndex == -1
493 ? QGuiApplication::primaryScreen()
494 : QGuiApplication::screens().value(iHostScreenIndex);
495 AssertPtrReturn(pScreen, 1.0);
496
497 /* Then acquire device-pixel-ratio: */
498 return pScreen->devicePixelRatio();
499}
500
501/* static */
502double UIDesktopWidgetWatchdog::devicePixelRatio(QWidget *pWidget)
503{
504 /* Redirect call to wrapper above: */
505 return devicePixelRatio(screenNumber(pWidget));
506}
507
508/* static */
509double UIDesktopWidgetWatchdog::devicePixelRatioActual(int iHostScreenIndex /* = -1 */)
510{
511 /* First, we should check whether the screen is valid: */
512 QScreen *pScreen = 0;
513 if (iHostScreenIndex == -1)
514 {
515 pScreen = QGuiApplication::primaryScreen();
516 iHostScreenIndex = QGuiApplication::screens().indexOf(pScreen);
517 }
518 else
519 pScreen = QGuiApplication::screens().value(iHostScreenIndex);
520 AssertPtrReturn(pScreen, 1.0);
521
522#ifdef VBOX_WS_WIN
523 /* Enumerate available monitors through EnumDisplayMonitors if GetDpiForMonitor is available: */
524 if (ResolveDynamicImports())
525 {
526 QList<QPair<int, int> > listOfScreenDPI;
527 EnumDisplayMonitors(0, 0, MonitorEnumProcF, (LPARAM)&listOfScreenDPI);
528 if (iHostScreenIndex >= 0 && iHostScreenIndex < listOfScreenDPI.size())
529 {
530 const QPair<int, int> dpiPair = listOfScreenDPI.at(iHostScreenIndex);
531 if (dpiPair.first > 0)
532 return (double)dpiPair.first / 96 /* dpi unawarness value */;
533 }
534 }
535#endif /* VBOX_WS_WIN */
536
537 /* Then acquire device-pixel-ratio: */
538 return pScreen->devicePixelRatio();
539}
540
541/* static */
542double UIDesktopWidgetWatchdog::devicePixelRatioActual(QWidget *pWidget)
543{
544 /* Redirect call to wrapper above: */
545 return devicePixelRatioActual(screenNumber(pWidget));
546}
547
548/* static */
549QRect UIDesktopWidgetWatchdog::normalizeGeometry(const QRect &rectangle,
550 const QRegion &boundRegion,
551 bool fCanResize /* = true */)
552{
553 /* Perform direct and flipped search of position for @a rectangle to make sure it is fully contained
554 * inside @a boundRegion region by moving & resizing (if @a fCanResize is specified) @a rectangle if
555 * necessary. Selects the minimum shifted result between direct and flipped variants. */
556
557 /* Direct search for normalized rectangle: */
558 QRect var1(getNormalized(rectangle, boundRegion, fCanResize));
559
560 /* Flipped search for normalized rectangle: */
561 QRect var2(flip(getNormalized(flip(rectangle).boundingRect(),
562 flip(boundRegion), fCanResize)).boundingRect());
563
564 /* Calculate shift from starting position for both variants: */
565 double dLength1 = sqrt(pow((double)(var1.x() - rectangle.x()), (double)2) +
566 pow((double)(var1.y() - rectangle.y()), (double)2));
567 double dLength2 = sqrt(pow((double)(var2.x() - rectangle.x()), (double)2) +
568 pow((double)(var2.y() - rectangle.y()), (double)2));
569
570 /* Return minimum shifted variant: */
571 return dLength1 > dLength2 ? var2 : var1;
572}
573
574/* static */
575QRect UIDesktopWidgetWatchdog::getNormalized(const QRect &rectangle,
576 const QRegion &boundRegion,
577 bool /* fCanResize = true */)
578{
579 /* Ensures that the given rectangle @a rectangle is fully contained within the region @a boundRegion
580 * by moving @a rectangle if necessary. If @a rectangle is larger than @a boundRegion, top left
581 * corner of @a rectangle is aligned with the top left corner of maximum available rectangle and,
582 * if @a fCanResize is true, @a rectangle is shrinked to become fully visible. */
583
584 /* Storing available horizontal sub-rectangles & vertical shifts: */
585 const int iWindowVertical = rectangle.center().y();
586 QList<QRect> rectanglesList;
587 QList<int> shiftsList;
588 for (QRegion::const_iterator it = boundRegion.begin(); it != boundRegion.end(); ++it)
589 {
590 QRect currentItem = *it;
591 const int iCurrentDelta = qAbs(iWindowVertical - currentItem.center().y());
592 const int iShift2Top = currentItem.top() - rectangle.top();
593 const int iShift2Bot = currentItem.bottom() - rectangle.bottom();
594
595 int iTtemPosition = 0;
596 foreach (QRect item, rectanglesList)
597 {
598 const int iDelta = qAbs(iWindowVertical - item.center().y());
599 if (iDelta > iCurrentDelta)
600 break;
601 else
602 ++iTtemPosition;
603 }
604 rectanglesList.insert(iTtemPosition, currentItem);
605
606 int iShift2TopPos = 0;
607 foreach (int iShift, shiftsList)
608 if (qAbs(iShift) > qAbs(iShift2Top))
609 break;
610 else
611 ++iShift2TopPos;
612 shiftsList.insert(iShift2TopPos, iShift2Top);
613
614 int iShift2BotPos = 0;
615 foreach (int iShift, shiftsList)
616 if (qAbs(iShift) > qAbs(iShift2Bot))
617 break;
618 else
619 ++iShift2BotPos;
620 shiftsList.insert(iShift2BotPos, iShift2Bot);
621 }
622
623 /* Trying to find the appropriate place for window: */
624 QRect result;
625 for (int i = -1; i < shiftsList.size(); ++i)
626 {
627 /* Move to appropriate vertical: */
628 QRect newRectangle(rectangle);
629 if (i >= 0)
630 newRectangle.translate(0, shiftsList[i]);
631
632 /* Search horizontal shift: */
633 int iMaxShift = 0;
634 foreach (QRect item, rectanglesList)
635 {
636 QRect trectangle(newRectangle.translated(item.left() - newRectangle.left(), 0));
637 if (!item.intersects(trectangle))
638 continue;
639
640 if (newRectangle.left() < item.left())
641 {
642 const int iShift = item.left() - newRectangle.left();
643 iMaxShift = qAbs(iShift) > qAbs(iMaxShift) ? iShift : iMaxShift;
644 }
645 else if (newRectangle.right() > item.right())
646 {
647 const int iShift = item.right() - newRectangle.right();
648 iMaxShift = qAbs(iShift) > qAbs(iMaxShift) ? iShift : iMaxShift;
649 }
650 }
651
652 /* Shift across the horizontal direction: */
653 newRectangle.translate(iMaxShift, 0);
654
655 /* Check the translated rectangle to feat the rules: */
656 if (boundRegion.united(newRectangle) == boundRegion)
657 result = newRectangle;
658
659 if (!result.isNull())
660 break;
661 }
662
663 if (result.isNull())
664 {
665 /* Resize window to feat desirable size
666 * using max of available rectangles: */
667 QRect maxRectangle;
668 quint64 uMaxSquare = 0;
669 foreach (QRect item, rectanglesList)
670 {
671 const quint64 uSquare = item.width() * item.height();
672 if (uSquare > uMaxSquare)
673 {
674 uMaxSquare = uSquare;
675 maxRectangle = item;
676 }
677 }
678
679 result = rectangle;
680 result.moveTo(maxRectangle.x(), maxRectangle.y());
681 if (maxRectangle.right() < result.right())
682 result.setRight(maxRectangle.right());
683 if (maxRectangle.bottom() < result.bottom())
684 result.setBottom(maxRectangle.bottom());
685 }
686
687 return result;
688}
689
690void UIDesktopWidgetWatchdog::centerWidget(QWidget *pWidget,
691 QWidget *pRelative,
692 bool fCanResize /* = true */) const
693{
694 /* If necessary, pWidget's position is adjusted to make it fully visible within
695 * the available desktop area. If pWidget is bigger then this area, it will also
696 * be resized unless fCanResize is false or there is an inappropriate minimum
697 * size limit (in which case the top left corner will be simply aligned with the top
698 * left corner of the available desktop area). pWidget must be a top-level widget.
699 * pRelative may be any widget, but if it's not top-level itself, its top-level
700 * widget will be used for calculations. pRelative can also be NULL, in which case
701 * pWidget will be centered relative to the available desktop area. */
702
703 AssertReturnVoid(pWidget);
704 AssertReturnVoid(pWidget->isWindow());
705
706 QRect deskGeo, parentGeo;
707 if (pRelative)
708 {
709 pRelative = pRelative->window();
710 deskGeo = availableGeometry(pRelative);
711 parentGeo = pRelative->frameGeometry();
712 // WORKAROUND:
713 // On X11/Gnome, geo/frameGeo.x() and y() are always 0 for top level
714 // widgets with parents, what a shame. Use mapToGlobal() to workaround.
715 QPoint d = pRelative->mapToGlobal(QPoint(0, 0));
716 d.rx() -= pRelative->geometry().x() - pRelative->x();
717 d.ry() -= pRelative->geometry().y() - pRelative->y();
718 parentGeo.moveTopLeft(d);
719 }
720 else
721 {
722 deskGeo = availableGeometry();
723 parentGeo = deskGeo;
724 }
725
726 // WORKAROUND:
727 // On X11, there is no way to determine frame geometry (including WM
728 // decorations) before the widget is shown for the first time. Stupidly
729 // enumerate other top level widgets to find the thickest frame. The code
730 // is based on the idea taken from QDialog::adjustPositionInternal().
731
732 int iExtraW = 0;
733 int iExtraH = 0;
734
735 QWidgetList list = QApplication::topLevelWidgets();
736 QListIterator<QWidget*> it(list);
737 while ((iExtraW == 0 || iExtraH == 0) && it.hasNext())
738 {
739 int iFrameW, iFrameH;
740 QWidget *pCurrent = it.next();
741 if (!pCurrent->isVisible())
742 continue;
743
744 iFrameW = pCurrent->frameGeometry().width() - pCurrent->width();
745 iFrameH = pCurrent->frameGeometry().height() - pCurrent->height();
746
747 iExtraW = qMax(iExtraW, iFrameW);
748 iExtraH = qMax(iExtraH, iFrameH);
749 }
750
751 /* On non-X11 platforms, the following would be enough instead of the above workaround: */
752 // QRect geo = frameGeometry();
753 QRect geo = QRect(0, 0, pWidget->width() + iExtraW,
754 pWidget->height() + iExtraH);
755
756 geo.moveCenter(QPoint(parentGeo.x() + (parentGeo.width() - 1) / 2,
757 parentGeo.y() + (parentGeo.height() - 1) / 2));
758
759 /* Ensure the widget is within the available desktop area: */
760 QRect newGeo = normalizeGeometry(geo, deskGeo, fCanResize);
761#ifdef VBOX_WS_MAC
762 // WORKAROUND:
763 // No idea why, but Qt doesn't respect if there is a unified toolbar on the
764 // ::move call. So manually add the height of the toolbar before setting
765 // the position.
766 if (pRelative)
767 newGeo.translate(0, ::darwinWindowToolBarHeight(pWidget));
768#endif /* VBOX_WS_MAC */
769
770 pWidget->move(newGeo.topLeft());
771
772 if ( fCanResize
773 && (geo.width() != newGeo.width() || geo.height() != newGeo.height()))
774 pWidget->resize(newGeo.width() - iExtraW, newGeo.height() - iExtraH);
775}
776
777/* static */
778void UIDesktopWidgetWatchdog::restoreWidget(QWidget *pWidget)
779{
780 pWidget->show();
781 pWidget->setWindowState(pWidget->windowState() & ~Qt::WindowMinimized);
782 pWidget->activateWindow();
783 pWidget->raise();
784}
785
786/* static */
787void UIDesktopWidgetWatchdog::setTopLevelGeometry(QWidget *pWidget, int x, int y, int w, int h)
788{
789 AssertPtrReturnVoid(pWidget);
790#ifdef VBOX_WS_NIX
791# define QWINDOWSIZE_MAX ((1<<24)-1)
792 if (pWidget->isWindow() && pWidget->isVisible() && uiCommon().X11ServerAvailable())
793 {
794 // WORKAROUND:
795 // X11 window managers are not required to accept geometry changes on
796 // the top-level window. Unfortunately, current at Qt 5.6 and 5.7, Qt
797 // assumes that the change will succeed, and resizes all sub-windows
798 // unconditionally. By calling ConfigureWindow directly, Qt will see
799 // our change request as an externally triggered one on success and not
800 // at all if it is rejected.
801 const double dDPR = devicePixelRatio(pWidget);
802 uint16_t fMask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
803 | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
804 uint32_t values[] = { (uint32_t)(x * dDPR), (uint32_t)(y * dDPR), (uint32_t)(w * dDPR), (uint32_t)(h * dDPR) };
805 xcb_configure_window(NativeWindowSubsystem::X11GetConnection(), (xcb_window_t)pWidget->winId(),
806 fMask, values);
807 xcb_size_hints_t hints;
808 hints.flags = 1 /* XCB_ICCCM_SIZE_HINT_US_POSITION */
809 | 2 /* XCB_ICCCM_SIZE_HINT_US_SIZE */
810 | 512 /* XCB_ICCCM_SIZE_P_WIN_GRAVITY */;
811 hints.x = x * dDPR;
812 hints.y = y * dDPR;
813 hints.width = w * dDPR;
814 hints.height = h * dDPR;
815 hints.min_width = pWidget->minimumSize().width() * dDPR;
816 hints.min_height = pWidget->minimumSize().height() * dDPR;
817 hints.max_width = pWidget->maximumSize().width() * dDPR;
818 hints.max_height = pWidget->maximumSize().height() * dDPR;
819 hints.width_inc = pWidget->sizeIncrement().width() * dDPR;
820 hints.height_inc = pWidget->sizeIncrement().height() * dDPR;
821 hints.base_width = pWidget->baseSize().width() * dDPR;
822 hints.base_height = pWidget->baseSize().height() * dDPR;
823 hints.win_gravity = XCB_GRAVITY_STATIC;
824 if (hints.min_width > 0 || hints.min_height > 0)
825 hints.flags |= 16 /* XCB_ICCCM_SIZE_HINT_P_MIN_SIZE */;
826 if (hints.max_width < QWINDOWSIZE_MAX || hints.max_height < QWINDOWSIZE_MAX)
827 hints.flags |= 32 /* XCB_ICCCM_SIZE_HINT_P_MAX_SIZE */;
828 if (hints.width_inc > 0 || hints.height_inc)
829 hints.flags |= 64 /* XCB_ICCCM_SIZE_HINT_P_MIN_SIZE */
830 | 256 /* XCB_ICCCM_SIZE_HINT_BASE_SIZE */;
831 xcb_change_property(NativeWindowSubsystem::X11GetConnection(), XCB_PROP_MODE_REPLACE,
832 (xcb_window_t)pWidget->winId(), XCB_ATOM_WM_NORMAL_HINTS,
833 XCB_ATOM_WM_SIZE_HINTS, 32, sizeof(hints) >> 2, &hints);
834 xcb_flush(NativeWindowSubsystem::X11GetConnection());
835 }
836 else
837 // WORKAROUND:
838 // Call the Qt method if the window is not visible as otherwise no
839 // Configure event will arrive to tell Qt what geometry we want.
840 pWidget->setGeometry(x, y, w, h);
841# else /* !VBOX_WS_NIX */
842 pWidget->setGeometry(x, y, w, h);
843# endif /* !VBOX_WS_NIX */
844}
845
846/* static */
847void UIDesktopWidgetWatchdog::setTopLevelGeometry(QWidget *pWidget, const QRect &rect)
848{
849 UIDesktopWidgetWatchdog::setTopLevelGeometry(pWidget, rect.x(), rect.y(), rect.width(), rect.height());
850}
851
852/* static */
853bool UIDesktopWidgetWatchdog::activateWindow(WId wId, bool fSwitchDesktop /* = true */)
854{
855 Q_UNUSED(fSwitchDesktop);
856 bool fResult = true;
857
858#if defined(VBOX_WS_WIN)
859
860 fResult &= NativeWindowSubsystem::WinActivateWindow(wId, fSwitchDesktop);
861
862#elif defined(VBOX_WS_NIX)
863
864 fResult &= NativeWindowSubsystem::activateWindow(uiCommon().X11ServerAvailable(), wId, fSwitchDesktop);
865
866#else
867
868 NOREF(wId);
869 NOREF(fSwitchDesktop);
870 AssertFailed();
871 fResult = false;
872
873#endif
874
875 if (!fResult)
876 Log1WarningFunc(("Couldn't activate wId=%08X\n", wId));
877
878 return fResult;
879}
880
881void UIDesktopWidgetWatchdog::sltHostScreenAdded(QScreen *pHostScreen)
882{
883// printf("UIDesktopWidgetWatchdog::sltHostScreenAdded(%d)\n", screenCount());
884
885 /* Listen for screen signals: */
886 connect(pHostScreen, &QScreen::geometryChanged,
887 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenResized);
888 connect(pHostScreen, &QScreen::availableGeometryChanged,
889 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized);
890
891#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
892 /* Update host-screen configuration: */
893 updateHostScreenConfiguration();
894#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
895
896 /* Notify listeners: */
897 emit sigHostScreenCountChanged(screenCount());
898}
899
900void UIDesktopWidgetWatchdog::sltHostScreenRemoved(QScreen *pHostScreen)
901{
902// printf("UIDesktopWidgetWatchdog::sltHostScreenRemoved(%d)\n", screenCount());
903
904 /* Forget about screen signals: */
905 disconnect(pHostScreen, &QScreen::geometryChanged,
906 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenResized);
907 disconnect(pHostScreen, &QScreen::availableGeometryChanged,
908 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized);
909
910#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
911 /* Update host-screen configuration: */
912 updateHostScreenConfiguration();
913#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
914
915 /* Notify listeners: */
916 emit sigHostScreenCountChanged(screenCount());
917}
918
919void UIDesktopWidgetWatchdog::sltHandleHostScreenResized(const QRect &geometry)
920{
921 /* Get the screen: */
922 QScreen *pScreen = sender() ? qobject_cast<QScreen*>(sender()) : 0;
923 AssertPtrReturnVoid(pScreen);
924
925 /* Determine screen index: */
926 const int iHostScreenIndex = qApp->screens().indexOf(pScreen);
927 AssertReturnVoid(iHostScreenIndex != -1);
928 LogRel(("GUI: UIDesktopWidgetWatchdog::sltHandleHostScreenResized: "
929 "Screen %d is formally resized to: %dx%d x %dx%d\n",
930 iHostScreenIndex, geometry.x(), geometry.y(),
931 geometry.width(), geometry.height()));
932
933#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
934 /* Update host-screen available-geometry: */
935 updateHostScreenAvailableGeometry(iHostScreenIndex);
936#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
937
938 /* Notify listeners: */
939 emit sigHostScreenResized(iHostScreenIndex);
940}
941
942void UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized(const QRect &availableGeometry)
943{
944 /* Get the screen: */
945 QScreen *pScreen = sender() ? qobject_cast<QScreen*>(sender()) : 0;
946 AssertPtrReturnVoid(pScreen);
947
948 /* Determine screen index: */
949 const int iHostScreenIndex = qApp->screens().indexOf(pScreen);
950 AssertReturnVoid(iHostScreenIndex != -1);
951 LogRel(("GUI: UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized: "
952 "Screen %d work area is formally resized to: %dx%d x %dx%d\n",
953 iHostScreenIndex, availableGeometry.x(), availableGeometry.y(),
954 availableGeometry.width(), availableGeometry.height()));
955
956#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
957 /* Update host-screen available-geometry: */
958 updateHostScreenAvailableGeometry(iHostScreenIndex);
959#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
960
961 /* Notify listeners: */
962 emit sigHostScreenWorkAreaResized(iHostScreenIndex);
963}
964
965#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
966void UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculated(int iHostScreenIndex, QRect availableGeometry)
967{
968 LogRel(("GUI: UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculated: "
969 "Screen %d work area is actually resized to: %dx%d x %dx%d\n",
970 iHostScreenIndex, availableGeometry.x(), availableGeometry.y(),
971 availableGeometry.width(), availableGeometry.height()));
972
973 /* Apply received data: */
974 const bool fSendSignal = m_availableGeometryData.value(iHostScreenIndex).isValid();
975 m_availableGeometryData[iHostScreenIndex] = availableGeometry;
976 /* Forget finished worker: */
977 AssertPtrReturnVoid(m_availableGeometryWorkers.value(iHostScreenIndex));
978 m_availableGeometryWorkers.value(iHostScreenIndex)->disconnect();
979 m_availableGeometryWorkers.value(iHostScreenIndex)->deleteLater();
980 m_availableGeometryWorkers[iHostScreenIndex] = 0;
981
982 /* Notify listeners: */
983 if (fSendSignal)
984 emit sigHostScreenWorkAreaRecalculated(iHostScreenIndex);
985}
986#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
987
988void UIDesktopWidgetWatchdog::prepare()
989{
990 /* Prepare connections: */
991 connect(qApp, &QGuiApplication::screenAdded,
992 this, &UIDesktopWidgetWatchdog::sltHostScreenAdded);
993 connect(qApp, &QGuiApplication::screenRemoved,
994 this, &UIDesktopWidgetWatchdog::sltHostScreenRemoved);
995 foreach (QScreen *pHostScreen, qApp->screens())
996 {
997 connect(pHostScreen, &QScreen::geometryChanged,
998 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenResized);
999 connect(pHostScreen, &QScreen::availableGeometryChanged,
1000 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized);
1001 }
1002
1003#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
1004 /* Load Synthetic Test policy: */
1005 const QString strSynthTestPolicy = QString::fromLocal8Bit(qgetenv(VBox_DesktopWatchdogPolicy_SynthTest));
1006 m_enmSynthTestPolicy = gpConverter->fromInternalString<DesktopWatchdogPolicy_SynthTest>(strSynthTestPolicy);
1007
1008 /* Update host-screen configuration: */
1009 updateHostScreenConfiguration();
1010#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
1011}
1012
1013void UIDesktopWidgetWatchdog::cleanup()
1014{
1015 /* Cleanup connections: */
1016 disconnect(qApp, &QGuiApplication::screenAdded,
1017 this, &UIDesktopWidgetWatchdog::sltHostScreenAdded);
1018 disconnect(qApp, &QGuiApplication::screenRemoved,
1019 this, &UIDesktopWidgetWatchdog::sltHostScreenRemoved);
1020 foreach (QScreen *pHostScreen, qApp->screens())
1021 {
1022 disconnect(pHostScreen, &QScreen::geometryChanged,
1023 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenResized);
1024 disconnect(pHostScreen, &QScreen::availableGeometryChanged,
1025 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenWorkAreaResized);
1026 }
1027
1028#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
1029 /* Cleanup existing workers finally: */
1030 cleanupExistingWorkers();
1031#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
1032}
1033
1034/* static */
1035int UIDesktopWidgetWatchdog::screenToIndex(QScreen *pScreen)
1036{
1037 if (pScreen)
1038 {
1039 unsigned iScreen = 0;
1040 foreach (QScreen *pCurScreen, QGuiApplication::screens())
1041 {
1042 if ( pCurScreen == pScreen
1043 || ( pCurScreen->geometry() == pScreen->geometry()
1044 && pCurScreen->serialNumber() == pScreen->serialNumber()))
1045 return iScreen;
1046 ++iScreen;
1047 }
1048 }
1049 return -1;
1050}
1051
1052/* static */
1053QRegion UIDesktopWidgetWatchdog::flip(const QRegion &region)
1054{
1055 QRegion result;
1056 for (QRegion::const_iterator it = region.begin(); it != region.end(); ++it)
1057 result += QRect(it->y(), it->x(),
1058 it->height(), it->width());
1059 return result;
1060}
1061
1062#if defined(VBOX_WS_NIX) && !defined(VBOX_GUI_WITH_CUSTOMIZATIONS1)
1063bool UIDesktopWidgetWatchdog::isSynchTestRestricted() const
1064{
1065 return m_enmSynthTestPolicy == DesktopWatchdogPolicy_SynthTest_Disabled
1066 || ( m_enmSynthTestPolicy == DesktopWatchdogPolicy_SynthTest_ManagerOnly
1067 && uiCommon().uiType() == UICommon::UIType_RuntimeUI)
1068 || ( m_enmSynthTestPolicy == DesktopWatchdogPolicy_SynthTest_MachineOnly
1069 && uiCommon().uiType() == UICommon::UIType_SelectorUI);
1070}
1071
1072void UIDesktopWidgetWatchdog::updateHostScreenConfiguration(int cHostScreenCount /* = -1 */)
1073{
1074 /* Check the policy: */
1075 if (isSynchTestRestricted())
1076 return;
1077
1078 /* Acquire new host-screen count: */
1079 if (cHostScreenCount == -1)
1080 cHostScreenCount = screenCount();
1081
1082 /* Cleanup existing workers first: */
1083 cleanupExistingWorkers();
1084
1085 /* Resize workers vectors to new host-screen count: */
1086 m_availableGeometryWorkers.resize(cHostScreenCount);
1087 m_availableGeometryData.resize(cHostScreenCount);
1088
1089 /* Update host-screen available-geometry for each particular host-screen: */
1090 for (int iHostScreenIndex = 0; iHostScreenIndex < cHostScreenCount; ++iHostScreenIndex)
1091 updateHostScreenAvailableGeometry(iHostScreenIndex);
1092}
1093
1094void UIDesktopWidgetWatchdog::updateHostScreenAvailableGeometry(int iHostScreenIndex)
1095{
1096 /* Check the policy: */
1097 if (isSynchTestRestricted())
1098 return;
1099
1100 /* Make sure index is valid: */
1101 if (iHostScreenIndex < 0 || iHostScreenIndex >= screenCount())
1102 {
1103 iHostScreenIndex = UIDesktopWidgetWatchdog::primaryScreenNumber();
1104 AssertReturnVoid(iHostScreenIndex >= 0 && iHostScreenIndex < screenCount());
1105 }
1106
1107 /* Create invisible frame-less window worker: */
1108 UIInvisibleWindow *pWorker = new UIInvisibleWindow(iHostScreenIndex);
1109 AssertPtrReturnVoid(pWorker);
1110 {
1111 /* Remember created worker (replace if necessary): */
1112 if (m_availableGeometryWorkers.value(iHostScreenIndex))
1113 delete m_availableGeometryWorkers.value(iHostScreenIndex);
1114 m_availableGeometryWorkers[iHostScreenIndex] = pWorker;
1115
1116 /* Get the screen-geometry: */
1117 const QRect hostScreenGeometry = screenGeometry(iHostScreenIndex);
1118
1119 /* Connect worker listener: */
1120 connect(pWorker, &UIInvisibleWindow::sigHostScreenAvailableGeometryCalculated,
1121 this, &UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculated);
1122
1123 /* Place worker to corresponding host-screen: */
1124 pWorker->move(hostScreenGeometry.center());
1125 /* And finally, maximize it: */
1126 pWorker->showMaximized();
1127 }
1128}
1129
1130void UIDesktopWidgetWatchdog::cleanupExistingWorkers()
1131{
1132 /* Check the policy: */
1133 if (isSynchTestRestricted())
1134 return;
1135
1136 /* Destroy existing workers: */
1137 qDeleteAll(m_availableGeometryWorkers);
1138 /* And clear their vector: */
1139 m_availableGeometryWorkers.clear();
1140}
1141
1142# include "UIDesktopWidgetWatchdog.moc"
1143#endif /* VBOX_WS_NIX && !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
Note: See TracBrowser for help on using the repository browser.

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