VirtualBox

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

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

Copyright year updates by scm.

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