VirtualBox

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

Last change on this file was 103988, checked in by vboxsync, 2 months ago

FE/Qt. bugref:10624. Adding missing override keywords gcc has found.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use