VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineWindowSeamless.cpp@ 103977

Last change on this file since 103977 was 103538, checked in by vboxsync, 11 months ago

FE/Qt: Moving out logging stuff from UIDefs.h to separate UILoggingDefs.h; This breaks dependency of UIDefs/UICommon headers from VBox/log.h

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.0 KB
Line 
1/* $Id: UIMachineWindowSeamless.cpp 103538 2024-02-22 17:06:26Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMachineWindowSeamless class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-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 <QMenu>
30#include <QSpacerItem>
31#include <QTimer>
32
33/* GUI includes: */
34#include "UIActionPoolRuntime.h"
35#include "UICommon.h"
36#include "UIDesktopWidgetWatchdog.h"
37#include "UIExtraDataManager.h"
38#include "UILoggingDefs.h"
39#include "UIMachine.h"
40#include "UIMachineLogicSeamless.h"
41#include "UIMachineView.h"
42#include "UIMachineWindowSeamless.h"
43#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
44# include "UIMachineDefs.h"
45# include "UIMiniToolBar.h"
46#elif defined(VBOX_WS_MAC)
47# include "VBoxUtils.h"
48#endif /* VBOX_WS_MAC */
49
50/* COM includes: */
51#include "CSnapshot.h"
52
53
54UIMachineWindowSeamless::UIMachineWindowSeamless(UIMachineLogic *pMachineLogic, ulong uScreenId)
55 : UIMachineWindow(pMachineLogic, uScreenId)
56#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
57 , m_pMiniToolBar(0)
58#endif /* VBOX_WS_WIN || VBOX_WS_NIX */
59 , m_fWasMinimized(false)
60#ifdef VBOX_WS_NIX
61 , m_fIsMinimizationRequested(false)
62 , m_fIsMinimized(false)
63#endif
64{
65}
66
67#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
68void UIMachineWindowSeamless::sltMachineStateChanged()
69{
70 /* Call to base-class: */
71 UIMachineWindow::sltMachineStateChanged();
72
73 /* Update mini-toolbar: */
74 updateAppearanceOf(UIVisualElement_MiniToolBar);
75}
76
77void UIMachineWindowSeamless::sltRevokeWindowActivation()
78{
79#ifdef VBOX_WS_NIX
80 // WORKAROUND:
81 // We could be asked to minimize already, but just
82 // not yet executed that order to current moment.
83 if (m_fIsMinimizationRequested)
84 return;
85#endif
86
87 /* Make sure window is visible: */
88 if (!isVisible() || isMinimized())
89 return;
90
91 /* Revoke stolen activation: */
92#ifdef VBOX_WS_NIX
93 raise();
94#endif /* VBOX_WS_NIX */
95 activateWindow();
96}
97
98void UIMachineWindowSeamless::sltHandleMiniToolBarAutoHideToggled(bool fEnabled)
99{
100 /* Save mini-toolbar settings: */
101 gEDataManager->setAutoHideMiniToolbar(fEnabled, uiCommon().managedVMUuid());
102}
103#endif /* VBOX_WS_WIN || VBOX_WS_NIX */
104
105void UIMachineWindowSeamless::sltShowMinimized()
106{
107#ifdef VBOX_WS_NIX
108 /* Remember that we are asked to minimize: */
109 m_fIsMinimizationRequested = true;
110#endif
111
112 showMinimized();
113}
114
115void UIMachineWindowSeamless::prepareVisualState()
116{
117 /* Call to base-class: */
118 UIMachineWindow::prepareVisualState();
119
120 /* Make sure we have no background
121 * until the first one paint-event: */
122 setAttribute(Qt::WA_NoSystemBackground);
123
124#ifdef VBOX_WITH_TRANSLUCENT_SEAMLESS
125 /* Using Qt API to enable translucent background: */
126 setAttribute(Qt::WA_TranslucentBackground);
127#endif /* VBOX_WITH_TRANSLUCENT_SEAMLESS */
128
129#ifdef VBOX_WITH_MASKED_SEAMLESS
130 /* Make sure we have no background
131 * until the first one set-region-event: */
132 setMask(m_maskGuest);
133#endif /* VBOX_WITH_MASKED_SEAMLESS */
134
135#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
136 /* Prepare mini-toolbar: */
137 prepareMiniToolbar();
138#endif /* VBOX_WS_WIN || VBOX_WS_NIX */
139}
140
141#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
142void UIMachineWindowSeamless::prepareMiniToolbar()
143{
144 /* Make sure mini-toolbar is not restricted: */
145 if (!gEDataManager->miniToolbarEnabled(uiCommon().managedVMUuid()))
146 return;
147
148 /* Create mini-toolbar: */
149 m_pMiniToolBar = new UIMiniToolBar(this,
150 GeometryType_Available,
151 gEDataManager->miniToolbarAlignment(uiCommon().managedVMUuid()),
152 gEDataManager->autoHideMiniToolbar(uiCommon().managedVMUuid()),
153 screenId());
154 AssertPtrReturnVoid(m_pMiniToolBar);
155 {
156 /* Configure mini-toolbar: */
157 m_pMiniToolBar->addMenus(actionPool()->menus());
158 connect(m_pMiniToolBar, &UIMiniToolBar::sigMinimizeAction,
159 this, &UIMachineWindowSeamless::sltShowMinimized, Qt::QueuedConnection);
160 connect(m_pMiniToolBar, &UIMiniToolBar::sigExitAction,
161 actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &UIAction::trigger);
162 connect(m_pMiniToolBar, &UIMiniToolBar::sigCloseAction,
163 actionPool()->action(UIActionIndex_M_Application_S_Close), &UIAction::trigger);
164 connect(m_pMiniToolBar, &UIMiniToolBar::sigNotifyAboutWindowActivationStolen,
165 this, &UIMachineWindowSeamless::sltRevokeWindowActivation, Qt::QueuedConnection);
166 connect(m_pMiniToolBar, &UIMiniToolBar::sigAutoHideToggled,
167 this, &UIMachineWindowSeamless::sltHandleMiniToolBarAutoHideToggled);
168 }
169}
170#endif /* VBOX_WS_WIN || VBOX_WS_NIX */
171
172#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
173void UIMachineWindowSeamless::cleanupMiniToolbar()
174{
175 /* Delete mini-toolbar: */
176 delete m_pMiniToolBar;
177 m_pMiniToolBar = 0;
178}
179#endif /* VBOX_WS_WIN || VBOX_WS_NIX */
180
181void UIMachineWindowSeamless::cleanupVisualState()
182{
183#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
184 /* Cleanup mini-toolbar: */
185 cleanupMiniToolbar();
186#endif /* VBOX_WS_WIN || VBOX_WS_NIX */
187
188 /* Call to base-class: */
189 UIMachineWindow::cleanupVisualState();
190}
191
192void UIMachineWindowSeamless::placeOnScreen()
193{
194 /* Make sure this window has seamless logic: */
195 UIMachineLogicSeamless *pSeamlessLogic = qobject_cast<UIMachineLogicSeamless*>(machineLogic());
196 AssertPtrReturnVoid(pSeamlessLogic);
197
198 /* Get corresponding host-screen: */
199 const int iHostScreen = pSeamlessLogic->hostScreenForGuestScreen(m_uScreenId);
200 /* And corresponding working area: */
201 const QRect workingArea = gpDesktop->availableGeometry(iHostScreen);
202 Q_UNUSED(workingArea);
203
204#ifdef VBOX_WS_NIX
205
206 /* Make sure we are located on corresponding host-screen: */
207 if ( UIDesktopWidgetWatchdog::screenCount() > 1
208 && (x() != workingArea.x() || y() != workingArea.y()))
209 {
210 // WORKAROUND:
211 // With Qt5 on KDE we can't just move the window onto desired host-screen if
212 // window is maximized. So we have to show it normal first of all:
213 if (isVisible() && isMaximized())
214 showNormal();
215
216 // WORKAROUND:
217 // With Qt5 on X11 we can't just move the window onto desired host-screen if
218 // window size is more than the available geometry (working area) of that
219 // host-screen. So we are resizing it to a smaller size first of all:
220 const QSize newSize = workingArea.size() * .9;
221 LogRel(("GUI: UIMachineWindowSeamless::placeOnScreen: Resize window: %d to smaller size: %dx%d\n",
222 m_uScreenId, newSize.width(), newSize.height()));
223 resize(newSize);
224 /* Move window onto required screen: */
225 const QPoint newPosition = workingArea.topLeft();
226 LogRel(("GUI: UIMachineWindowSeamless::placeOnScreen: Move window: %d to: %dx%d\n",
227 m_uScreenId, newPosition.x(), newPosition.y()));
228 move(newPosition);
229 }
230
231#else
232
233 /* Set appropriate window geometry: */
234 const QSize newSize = workingArea.size();
235 LogRel(("GUI: UIMachineWindowSeamless::placeOnScreen: Resize window: %d to: %dx%d\n",
236 m_uScreenId, newSize.width(), newSize.height()));
237 resize(newSize);
238 const QPoint newPosition = workingArea.topLeft();
239 LogRel(("GUI: UIMachineWindowSeamless::placeOnScreen: Move window: %d to: %dx%d\n",
240 m_uScreenId, newPosition.x(), newPosition.y()));
241 move(newPosition);
242
243#endif
244}
245
246void UIMachineWindowSeamless::showInNecessaryMode()
247{
248 /* Make sure window has seamless logic: */
249 UIMachineLogicSeamless *pSeamlessLogic = qobject_cast<UIMachineLogicSeamless*>(machineLogic());
250 AssertPtrReturnVoid(pSeamlessLogic);
251
252 /* If window shouldn't be shown or mapped to some host-screen: */
253 if (!uimachine()->isScreenVisible(m_uScreenId) ||
254 !pSeamlessLogic->hasHostScreenForGuestScreen(m_uScreenId))
255 {
256 /* Remember whether the window was minimized: */
257 if (isMinimized())
258 m_fWasMinimized = true;
259
260 /* Hide window and reset it's state to NONE: */
261 setWindowState(Qt::WindowNoState);
262 hide();
263 }
264 /* If window should be shown and mapped to some host-screen: */
265 else
266 {
267 /* Check whether window was minimized: */
268 const bool fWasMinimized = isMinimized() && isVisible();
269 /* And reset it's state in such case before exposing: */
270 if (fWasMinimized)
271 setWindowState(Qt::WindowNoState);
272
273 /* Make sure window have appropriate geometry: */
274 placeOnScreen();
275
276#ifdef VBOX_WS_NIX
277 /* Show window: */
278 if (!isMaximized())
279 showMaximized();
280#else
281 /* Show window: */
282 show();
283#endif
284
285 /* Restore minimized state if necessary: */
286 if (m_fWasMinimized || fWasMinimized)
287 {
288 m_fWasMinimized = false;
289 QMetaObject::invokeMethod(this, "showMinimized", Qt::QueuedConnection);
290 }
291
292 /* Adjust machine-view size if necessary: */
293 adjustMachineViewSize();
294
295 /* Make sure machine-view have focus: */
296 m_pMachineView->setFocus();
297 }
298}
299
300#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
301void UIMachineWindowSeamless::updateAppearanceOf(int iElement)
302{
303 /* Call to base-class: */
304 UIMachineWindow::updateAppearanceOf(iElement);
305
306 /* Update mini-toolbar: */
307 if (iElement & UIVisualElement_MiniToolBar)
308 {
309 /* If there is a mini-toolbar: */
310 if (m_pMiniToolBar)
311 {
312 /* Get snapshot(s): */
313 QString strSnapshotName;
314 ulong uSnapshotCount = 0;
315 uimachine()->acquireSnapshotCount(uSnapshotCount);
316 if (uSnapshotCount > 0)
317 {
318 QString strCurrentSnapshotName;
319 uimachine()->acquireCurrentSnapshotName(strCurrentSnapshotName);
320 strSnapshotName = " (" + strCurrentSnapshotName + ")";
321 }
322 /* Update mini-toolbar text: */
323 m_pMiniToolBar->setText(machineName() + strSnapshotName);
324 }
325 }
326}
327#endif /* VBOX_WS_WIN || VBOX_WS_NIX */
328
329#ifdef VBOX_WS_NIX
330void UIMachineWindowSeamless::changeEvent(QEvent *pEvent)
331{
332 switch (pEvent->type())
333 {
334 case QEvent::WindowStateChange:
335 {
336 /* Watch for window state changes: */
337 QWindowStateChangeEvent *pChangeEvent = static_cast<QWindowStateChangeEvent*>(pEvent);
338 LogRel2(("GUI: UIMachineWindowSeamless::changeEvent: Window state changed from %d to %d\n",
339 (int)pChangeEvent->oldState(), (int)windowState()));
340 if ( windowState() == Qt::WindowMinimized
341 && pChangeEvent->oldState() == Qt::WindowNoState
342 && !m_fIsMinimized)
343 {
344 /* Mark window minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
345 LogRel2(("GUI: UIMachineWindowSeamless::changeEvent: Window minimized\n"));
346 m_fIsMinimized = true;
347 }
348 else
349 if ( windowState() == Qt::WindowNoState
350 && pChangeEvent->oldState() == Qt::WindowMinimized
351 && m_fIsMinimized)
352 {
353 /* Mark window restored, and do manual restoring with showInNecessaryMode(): */
354 LogRel2(("GUI: UIMachineWindowSeamless::changeEvent: Window restored\n"));
355 m_fIsMinimized = false;
356 /* Remember that we no more asked to minimize: */
357 m_fIsMinimizationRequested = false;
358 showInNecessaryMode();
359 }
360 break;
361 }
362 default:
363 break;
364 }
365
366 /* Call to base-class: */
367 UIMachineWindow::changeEvent(pEvent);
368}
369#endif /* VBOX_WS_NIX */
370
371#ifdef VBOX_WS_WIN
372void UIMachineWindowSeamless::showEvent(QShowEvent *pEvent)
373{
374 /* Expose workaround again,
375 * Qt devs will never fix that it seems.
376 * This time they forget to set 'Mapped'
377 * attribute for initially frame-less window. */
378 setAttribute(Qt::WA_Mapped);
379
380 /* Call to base-class: */
381 UIMachineWindow::showEvent(pEvent);
382}
383#endif /* VBOX_WS_WIN */
384
385#ifdef VBOX_WITH_MASKED_SEAMLESS
386void UIMachineWindowSeamless::setMask(const QRegion &maskGuest)
387{
388 /* Remember new guest mask: */
389 m_maskGuest = maskGuest;
390
391 /* Prepare full mask: */
392 QRegion maskFull(m_maskGuest);
393
394 /* Shift full mask if left or top spacer width is NOT zero: */
395 if (m_pLeftSpacer->geometry().width() || m_pTopSpacer->geometry().height())
396 maskFull.translate(m_pLeftSpacer->geometry().width(), m_pTopSpacer->geometry().height());
397
398 /* Seamless-window for empty full mask should be empty too,
399 * but the QWidget::setMask() wrapper doesn't allow this.
400 * Instead, we see a full guest-screen of available-geometry size.
401 * So we have to make sure full mask have at least one pixel. */
402 if (maskFull.isEmpty())
403 maskFull += QRect(0, 0, 1, 1);
404
405 /* Make sure full mask had changed: */
406 if (m_maskFull != maskFull)
407 {
408 /* Compose viewport region to update: */
409 QRegion toUpdate = m_maskFull + maskFull;
410 /* Remember new full mask: */
411 m_maskFull = maskFull;
412 /* Assign new full mask: */
413 UIMachineWindow::setMask(m_maskFull);
414 /* Update viewport region finally: */
415 if (m_pMachineView)
416 m_pMachineView->viewport()->update(toUpdate);
417 }
418}
419#endif /* VBOX_WITH_MASKED_SEAMLESS */
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