VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 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: 13.2 KB
Line 
1/* $Id: UIMachineLogicSeamless.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMachineLogicSeamless 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#ifndef VBOX_WS_MAC
30# include <QTimer>
31#endif /* !VBOX_WS_MAC */
32
33/* GUI includes: */
34#include "UIActionPoolRuntime.h"
35#include "UICommon.h"
36#include "UILoggingDefs.h"
37#include "UIMachine.h"
38#include "UIMachineLogicSeamless.h"
39#include "UIMachineWindowSeamless.h"
40#include "UIMessageCenter.h"
41#include "UIMultiScreenLayout.h"
42#include "UIShortcutPool.h"
43#ifndef VBOX_WS_MAC
44# include "QIMenu.h"
45#else
46# include "VBoxUtils.h"
47#endif
48
49/* COM includes: */
50#include "CGraphicsAdapter.h"
51
52
53UIMachineLogicSeamless::UIMachineLogicSeamless(UIMachine *pMachine)
54 : UIMachineLogic(pMachine)
55#ifndef VBOX_WS_MAC
56 , m_pPopupMenu(0)
57#endif /* !VBOX_WS_MAC */
58{
59 /* Create multiscreen layout: */
60 m_pScreenLayout = new UIMultiScreenLayout(this);
61}
62
63UIMachineLogicSeamless::~UIMachineLogicSeamless()
64{
65 /* Delete multiscreen layout: */
66 delete m_pScreenLayout;
67}
68
69int UIMachineLogicSeamless::hostScreenForGuestScreen(int iScreenId) const
70{
71 return m_pScreenLayout->hostScreenForGuestScreen(iScreenId);
72}
73
74bool UIMachineLogicSeamless::hasHostScreenForGuestScreen(int iScreenId) const
75{
76 return m_pScreenLayout->hasHostScreenForGuestScreen(iScreenId);
77}
78
79bool UIMachineLogicSeamless::checkAvailability()
80{
81 /* Check if there is enough physical memory to enter seamless: */
82 if (uimachine()->isGuestSupportsSeamless())
83 {
84 ulong uVRAMSize = 0;
85 uimachine()->acquireVRAMSize(uVRAMSize);
86 quint64 uAvailBits = uVRAMSize * _1M /* MiB to bytes */ * 8 /* to bits */;
87 quint64 uUsedBits = m_pScreenLayout->memoryRequirements();
88 if (uAvailBits < uUsedBits)
89 {
90 msgCenter().cannotEnterSeamlessMode(0, 0, 0,
91 (((uUsedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
92 return false;
93 }
94 }
95
96 /* Show the info message. */
97 const UIShortcut &shortcut =
98 gShortcutPool->shortcut(actionPool()->shortcutsExtraDataID(),
99 actionPool()->action(UIActionIndexRT_M_View_T_Seamless)->shortcutExtraDataID());
100 const QString strHotKey = QString("Host+%1").arg(shortcut.primaryToPortableText());
101 if (!msgCenter().confirmGoingSeamless(strHotKey))
102 return false;
103
104 return true;
105}
106
107void UIMachineLogicSeamless::adjustMachineWindowsGeometry()
108{
109 LogRel(("GUI: UIMachineLogicSeamless::adjustMachineWindowsGeometry\n"));
110
111 /* Rebuild multi-screen layout: */
112 m_pScreenLayout->rebuild();
113
114 /* Make sure all machine-window(s) have proper geometry: */
115 foreach (UIMachineWindow *pMachineWindow, machineWindows())
116 pMachineWindow->showInNecessaryMode();
117}
118
119void UIMachineLogicSeamless::sltCheckForRequestedVisualStateType()
120{
121 LogRel(("GUI: UIMachineLogicSeamless::sltCheckForRequestedVisualStateType: Requested-state=%d, Machine-state=%d\n",
122 uimachine()->requestedVisualState(), uimachine()->machineState()));
123
124 /* Do not try to change visual-state type if machine was not started yet: */
125 if (!uimachine()->isRunning() && !uimachine()->isPaused())
126 return;
127
128 /* Do not try to change visual-state type in 'manual override' mode: */
129 if (uimachine()->isManualOverrideMode())
130 return;
131
132 /* If 'seamless' visual-state type is no more supported: */
133 if (!uimachine()->isGuestSupportsSeamless())
134 {
135 LogRel(("GUI: UIMachineLogicSeamless::sltCheckForRequestedVisualStateType: "
136 "Leaving 'seamless' as it is no more supported...\n"));
137 uimachine()->setRequestedVisualState(UIVisualStateType_Seamless);
138 uimachine()->asyncChangeVisualState(UIVisualStateType_Normal);
139 }
140}
141
142void UIMachineLogicSeamless::sltMachineStateChanged()
143{
144 /* Call to base-class: */
145 UIMachineLogic::sltMachineStateChanged();
146
147 /* If machine-state changed from 'paused' to 'running': */
148 if (uimachine()->isRunning() && uimachine()->wasPaused())
149 {
150 LogRel(("GUI: UIMachineLogicSeamless::sltMachineStateChanged:"
151 "Machine-state changed from 'paused' to 'running': "
152 "Adjust machine-window geometry...\n"));
153
154 /* Make sure further code will be called just once: */
155 uimachine()->forgetPreviousMachineState();
156 /* Adjust machine-window geometry if necessary: */
157 adjustMachineWindowsGeometry();
158 }
159}
160
161#ifndef VBOX_WS_MAC
162void UIMachineLogicSeamless::sltInvokePopupMenu()
163{
164 /* Popup main-menu if present: */
165 if (m_pPopupMenu && !m_pPopupMenu->isEmpty())
166 {
167 m_pPopupMenu->popup(activeMachineWindow()->geometry().center());
168 QTimer::singleShot(0, m_pPopupMenu, SLOT(sltHighlightFirstAction()));
169 }
170}
171#endif /* !VBOX_WS_MAC */
172
173void UIMachineLogicSeamless::sltScreenLayoutChanged()
174{
175 LogRel(("GUI: UIMachineLogicSeamless::sltScreenLayoutChanged: Multi-screen layout changed.\n"));
176
177 /* Make sure all machine-window(s) have proper geometry: */
178 foreach (UIMachineWindow *pMachineWindow, machineWindows())
179 pMachineWindow->showInNecessaryMode();
180}
181
182void UIMachineLogicSeamless::sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo)
183{
184 LogRel(("GUI: UIMachineLogicSeamless: Guest-screen count changed.\n"));
185
186 /* Rebuild multi-screen layout: */
187 m_pScreenLayout->rebuild();
188
189 /* Call to base-class: */
190 UIMachineLogic::sltGuestMonitorChange(changeType, uScreenId, screenGeo);
191}
192
193void UIMachineLogicSeamless::sltHostScreenCountChange()
194{
195 LogRel(("GUI: UIMachineLogicSeamless: Host-screen count changed.\n"));
196
197 /* Rebuild multi-screen layout: */
198 m_pScreenLayout->rebuild();
199
200 /* Call to base-class: */
201 UIMachineLogic::sltHostScreenCountChange();
202}
203
204void UIMachineLogicSeamless::sltAdditionsStateChanged()
205{
206 /* Call to base-class: */
207 UIMachineLogic::sltAdditionsStateChanged();
208
209 LogRel(("GUI: UIMachineLogicSeamless: Additions-state actual-change event, rebuild multi-screen layout\n"));
210 /* Rebuild multi-screen layout: */
211 m_pScreenLayout->rebuild();
212}
213
214void UIMachineLogicSeamless::prepareActionGroups()
215{
216 /* Call to base-class: */
217 UIMachineLogic::prepareActionGroups();
218
219 /* Restrict 'Adjust Window', 'Guest Autoresize', 'Status Bar' and 'Resize' actions for 'View' menu: */
220 actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
221 (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
222 (UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow |
223 UIExtraDataMetaDefs::RuntimeMenuViewActionType_GuestAutoresize |
224 UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar |
225 UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar |
226 UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize));
227#ifdef VBOX_WS_MAC
228 /* Restrict 'Window' menu: */
229 actionPool()->toRuntime()->setRestrictionForMenuBar(UIActionRestrictionLevel_Logic,
230 UIExtraDataMetaDefs::MenuType_Window);
231#endif /* VBOX_WS_MAC */
232
233 /* Take care of view-action toggle state: */
234 UIAction *pActionSeamless = actionPool()->action(UIActionIndexRT_M_View_T_Seamless);
235 if (!pActionSeamless->isChecked())
236 {
237 pActionSeamless->blockSignals(true);
238 pActionSeamless->setChecked(true);
239 pActionSeamless->blockSignals(false);
240 }
241}
242
243void UIMachineLogicSeamless::prepareActionConnections()
244{
245 /* Call to base-class: */
246 UIMachineLogic::prepareActionConnections();
247
248 /* Prepare 'View' actions connections: */
249 connect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &QAction::triggered,
250 this, &UIMachineLogicSeamless::sltChangeVisualStateToNormal);
251 connect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &QAction::triggered,
252 this, &UIMachineLogicSeamless::sltChangeVisualStateToFullscreen);
253 connect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &QAction::triggered,
254 this, &UIMachineLogicSeamless::sltChangeVisualStateToScale);
255}
256
257void UIMachineLogicSeamless::prepareMachineWindows()
258{
259 /* Do not create machine-window(s) if they created already: */
260 if (isMachineWindowsCreated())
261 return;
262
263#ifdef VBOX_WS_MAC
264 /* We have to make sure that we are getting the front most process.
265 * This is necessary for Qt versions > 4.3.3: */
266 darwinSetFrontMostProcess();
267#endif /* VBOX_WS_MAC */
268
269 /* Update the multi-screen layout: */
270 m_pScreenLayout->update();
271
272 /* Acquire monitor count: */
273 ulong cMonitorCount = 0;
274 uimachine()->acquireMonitorCount(cMonitorCount);
275
276 /* Create machine-window(s): */
277 for (uint cScreenId = 0; cScreenId < cMonitorCount; ++cScreenId)
278 addMachineWindow(UIMachineWindow::create(this, cScreenId));
279
280 /* Connect multi-screen layout change handler: */
281 connect(m_pScreenLayout, &UIMultiScreenLayout::sigScreenLayoutChange,
282 this, &UIMachineLogicSeamless::sltScreenLayoutChanged);
283
284 /* Mark machine-window(s) created: */
285 setMachineWindowsCreated(true);
286
287#ifdef VBOX_WS_NIX
288 switch (uiCommon().typeOfWindowManager())
289 {
290 case X11WMType_GNOMEShell:
291 case X11WMType_Mutter:
292 {
293 // WORKAROUND:
294 // Under certain WMs we can loose machine-window activation due to any Qt::Tool
295 // overlay asynchronously shown above it. Qt is not become aware of such event.
296 // We are going to ask to return machine-window activation in let's say 100ms.
297 QTimer::singleShot(100, machineWindows().first(), SLOT(sltActivateWindow()));
298 break;
299 }
300 default:
301 break;
302 }
303#endif /* VBOX_WS_NIX */
304}
305
306#ifndef VBOX_WS_MAC
307void UIMachineLogicSeamless::prepareMenu()
308{
309 /* Prepare popup-menu: */
310 m_pPopupMenu = new QIMenu;
311 AssertPtrReturnVoid(m_pPopupMenu);
312 {
313 /* Prepare popup-menu: */
314 foreach (QMenu *pMenu, actionPool()->menus())
315 m_pPopupMenu->addMenu(pMenu);
316 }
317}
318#endif /* !VBOX_WS_MAC */
319
320#ifndef VBOX_WS_MAC
321void UIMachineLogicSeamless::cleanupMenu()
322{
323 /* Cleanup popup-menu: */
324 delete m_pPopupMenu;
325 m_pPopupMenu = 0;
326}
327#endif /* !VBOX_WS_MAC */
328
329void UIMachineLogicSeamless::cleanupMachineWindows()
330{
331 /* Do not destroy machine-window(s) if they destroyed already: */
332 if (!isMachineWindowsCreated())
333 return;
334
335 /* Mark machine-window(s) destroyed: */
336 setMachineWindowsCreated(false);
337
338 /* Destroy machine-window(s): */
339 foreach (UIMachineWindow *pMachineWindow, machineWindows())
340 UIMachineWindow::destroy(pMachineWindow);
341}
342
343void UIMachineLogicSeamless::cleanupActionConnections()
344{
345 /* "View" actions disconnections: */
346 disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &QAction::triggered,
347 this, &UIMachineLogicSeamless::sltChangeVisualStateToNormal);
348 disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &QAction::triggered,
349 this, &UIMachineLogicSeamless::sltChangeVisualStateToFullscreen);
350 disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &QAction::triggered,
351 this, &UIMachineLogicSeamless::sltChangeVisualStateToScale);
352
353 /* Call to base-class: */
354 UIMachineLogic::cleanupActionConnections();
355}
356
357void UIMachineLogicSeamless::cleanupActionGroups()
358{
359 /* Take care of view-action toggle state: */
360 UIAction *pActionSeamless = actionPool()->action(UIActionIndexRT_M_View_T_Seamless);
361 if (pActionSeamless->isChecked())
362 {
363 pActionSeamless->blockSignals(true);
364 pActionSeamless->setChecked(false);
365 pActionSeamless->blockSignals(false);
366 }
367
368 /* Allow 'Adjust Window', 'Guest Autoresize', 'Status Bar' and 'Resize' actions for 'View' menu: */
369 actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
370 UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid);
371#ifdef VBOX_WS_MAC
372 /* Allow 'Window' menu: */
373 actionPool()->toRuntime()->setRestrictionForMenuBar(UIActionRestrictionLevel_Logic,
374 UIExtraDataMetaDefs::MenuType_Invalid);
375#endif /* VBOX_WS_MAC */
376
377 /* Call to base-class: */
378 UIMachineLogic::cleanupActionGroups();
379}
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