VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.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.1 KB
Line 
1/* $Id: UIMultiScreenLayout.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMultiScreenLayout 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/* GUI includes: */
29#include "UIActionPoolRuntime.h"
30#include "UICommon.h"
31#include "UIDesktopWidgetWatchdog.h"
32#include "UIExtraDataManager.h"
33#include "UILoggingDefs.h"
34#include "UIMachine.h"
35#include "UIMachineLogic.h"
36#include "UIMessageCenter.h"
37#include "UIMultiScreenLayout.h"
38
39
40UIMultiScreenLayout::UIMultiScreenLayout(UIMachineLogic *pMachineLogic)
41 : m_pMachineLogic(pMachineLogic)
42 , m_cGuestScreens(0)
43 , m_cHostMonitors(0)
44{
45 prepare();
46}
47
48bool UIMultiScreenLayout::hasHostScreenForGuestScreen(int iScreenId) const
49{
50 return m_screenMap.contains(iScreenId);
51}
52
53int UIMultiScreenLayout::hostScreenForGuestScreen(int iScreenId) const
54{
55 return m_screenMap.value(iScreenId, 0);
56}
57
58quint64 UIMultiScreenLayout::memoryRequirements() const
59{
60 return memoryRequirements(m_screenMap);
61}
62
63void UIMultiScreenLayout::update()
64{
65 LogRelFlow(("UIMultiScreenLayout::update: Started...\n"));
66
67 /* Clear screen-map initially: */
68 m_screenMap.clear();
69
70 /* Make a pool of available host screens: */
71 QList<int> availableScreens;
72 for (int i = 0; i < m_cHostMonitors; ++i)
73 availableScreens << i;
74
75 /* Load all combinations stored in the settings file.
76 * We have to make sure they are valid, which means there have to be unique combinations
77 * and all guests screens need there own host-monitor. */
78 const bool fShouldWeAutoMountGuestScreens = gEDataManager->autoMountGuestScreensEnabled(uiCommon().managedVMUuid());
79 LogRel(("GUI: UIMultiScreenLayout::update: GUI/AutomountGuestScreens is %s\n", fShouldWeAutoMountGuestScreens ? "enabled" : "disabled"));
80 foreach (int iGuestScreen, m_guestScreens)
81 {
82 /* Initialize variables: */
83 bool fValid = false;
84 int iHostScreen = -1;
85
86 if (!fValid)
87 {
88 /* If the user ever selected a combination in the view menu, we have the following entry: */
89 iHostScreen = gEDataManager->hostScreenForPassedGuestScreen(iGuestScreen, uiCommon().managedVMUuid());
90 /* Revalidate: */
91 fValid = iHostScreen >= 0 && iHostScreen < m_cHostMonitors /* In the host-monitor bounds? */
92 && m_screenMap.key(iHostScreen, -1) == -1; /* Not taken already? */
93 }
94
95 if (!fValid)
96 {
97 /* Check the position of the guest window in normal mode.
98 * This makes sure that on first use fullscreen/seamless window opens on the same host-screen as the normal window was before.
99 * This even works with multi-screen. The user just have to move all the normal windows to the target host-screens
100 * and they will magically open there in fullscreen/seamless also. */
101 QRect geo = gEDataManager->machineWindowGeometry(UIVisualStateType_Normal, iGuestScreen, uiCommon().managedVMUuid());
102 /* If geometry is valid: */
103 if (!geo.isNull())
104 {
105 /* Get top-left corner position: */
106 QPoint topLeftPosition(geo.topLeft());
107 /* Check which host-screen the position belongs to: */
108 iHostScreen = UIDesktopWidgetWatchdog::screenNumber(topLeftPosition);
109 /* Revalidate: */
110 fValid = iHostScreen >= 0 && iHostScreen < m_cHostMonitors /* In the host-monitor bounds? */
111 && m_screenMap.key(iHostScreen, -1) == -1; /* Not taken already? */
112 }
113 }
114
115 if (!fValid)
116 {
117 /* If still not valid, pick the next one
118 * if there is still available host-monitor: */
119 if (!availableScreens.isEmpty())
120 {
121 iHostScreen = availableScreens.first();
122 fValid = true;
123 }
124 }
125
126 if (fValid)
127 {
128 /* Register host-monitor for the guest-screen: */
129 m_screenMap.insert(iGuestScreen, iHostScreen);
130 /* Remove it from the list of available host screens: */
131 availableScreens.removeOne(iHostScreen);
132 }
133 /* Do we have opinion about what to do with excessive guest-screen? */
134 else if (fShouldWeAutoMountGuestScreens)
135 {
136 /* Then we have to disable excessive guest-screen: */
137 LogRel(("GUI: UIMultiScreenLayout::update: Disabling excessive guest-screen %d\n", iGuestScreen));
138 uimachine()->setScreenVisibleHostDesires(iGuestScreen, false);
139 uimachine()->setVideoModeHint(iGuestScreen,
140 false /* enabled? */,
141 false /* change origin? */,
142 0 /* origin x */, 0 /* origin y */,
143 0 /* width */, 0 /* height*/,
144 0 /* bits per pixel */,
145 true /* notify? */);
146 }
147 }
148
149 /* Are we still have available host-screens
150 * and have opinion about what to do with disabled guest-screens? */
151 if (!availableScreens.isEmpty() && fShouldWeAutoMountGuestScreens)
152 {
153 /* How many excessive host-screens do we have? */
154 int cExcessiveHostScreens = availableScreens.size();
155 /* How many disabled guest-screens do we have? */
156 int cDisabledGuestScreens = m_disabledGuestScreens.size();
157 /* We have to try to enable disabled guest-screens if any: */
158 int cGuestScreensToEnable = qMin(cExcessiveHostScreens, cDisabledGuestScreens);
159 for (int iGuestScreenIndex = 0; iGuestScreenIndex < cGuestScreensToEnable; ++iGuestScreenIndex)
160 {
161 /* Defaults: */
162 ULONG uWidth = 800;
163 ULONG uHeight = 600;
164 /* Try to get previous guest-screen arguments: */
165 const int iGuestScreen = m_disabledGuestScreens.at(iGuestScreenIndex);
166 const QSize guestScreenSize = uimachine()->guestScreenSize(iGuestScreen);
167 {
168 if (guestScreenSize.width() > 0)
169 uWidth = guestScreenSize.width();
170 if (guestScreenSize.height() > 0)
171 uHeight = guestScreenSize.height();
172 }
173 /* Re-enable guest-screen with proper resolution: */
174 LogRel(("GUI: UIMultiScreenLayout::update: Enabling guest-screen %d with following resolution: %dx%d\n",
175 iGuestScreen, uWidth, uHeight));
176 uimachine()->setScreenVisibleHostDesires(iGuestScreen, true);
177 uimachine()->setVideoModeHint(iGuestScreen,
178 true/* enabled? */,
179 false /* change origin? */,
180 0 /* origin x */, 0 /* origin y */,
181 uWidth, uHeight,
182 32/* bits per pixel */,
183 true /* notify? */);
184 }
185 }
186
187 /* Make sure action-pool knows whether multi-screen layout has host-screen for guest-screen: */
188 actionPool()->toRuntime()->setHostScreenForGuestScreenMap(m_screenMap);
189
190 LogRelFlow(("UIMultiScreenLayout::update: Finished!\n"));
191}
192
193void UIMultiScreenLayout::rebuild()
194{
195 LogRelFlow(("UIMultiScreenLayout::rebuild: Started...\n"));
196
197 /* Recalculate host/guest screen count: */
198 calculateHostMonitorCount();
199 calculateGuestScreenCount();
200
201 /* Update layout: */
202 update();
203
204 LogRelFlow(("UIMultiScreenLayout::rebuild: Finished!\n"));
205}
206
207void UIMultiScreenLayout::sltHandleScreenLayoutChange(int iRequestedGuestScreen, int iRequestedHostMonitor)
208{
209 /* Search for the virtual screen which is currently displayed on the
210 * requested host-monitor. When there is one found, we swap both. */
211 QMap<int,int> tmpMap(m_screenMap);
212 const int iCurrentGuestScreen = tmpMap.key(iRequestedHostMonitor, -1);
213 if (iCurrentGuestScreen != -1 && tmpMap.contains(iRequestedGuestScreen))
214 tmpMap.insert(iCurrentGuestScreen, tmpMap.value(iRequestedGuestScreen));
215 else
216 tmpMap.remove(iCurrentGuestScreen);
217 tmpMap.insert(iRequestedGuestScreen, iRequestedHostMonitor);
218
219 /* Check the memory requirements first: */
220 bool fSuccess = true;
221 if (uimachine()->isGuestSupportsGraphics())
222 {
223 ulong uVRAMSize = 0;
224 uimachine()->acquireVRAMSize(uVRAMSize);
225 const quint64 uAvailBits = uVRAMSize * _1M * 8;
226 const quint64 uUsedBits = memoryRequirements(tmpMap);
227 fSuccess = uAvailBits >= uUsedBits;
228 if (!fSuccess)
229 {
230 /* We have too little video memory for the new layout, so say it to the user and revert all the changes: */
231 if (machineLogic()->visualStateType() == UIVisualStateType_Seamless)
232 msgCenter().cannotSwitchScreenInSeamless((((uUsedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
233 else
234 fSuccess = msgCenter().cannotSwitchScreenInFullscreen((((uUsedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
235 }
236 }
237 /* Make sure memory requirements matched: */
238 if (!fSuccess)
239 return;
240
241 /* Swap the maps: */
242 m_screenMap = tmpMap;
243
244 /* Make sure action-pool knows whether multi-screen layout has host-screen for guest-screen: */
245 actionPool()->toRuntime()->setHostScreenForGuestScreenMap(m_screenMap);
246
247 /* Save guest-to-host mapping: */
248 foreach (const int &iGuestScreen, m_guestScreens)
249 {
250 const int iHostScreen = m_screenMap.value(iGuestScreen, -1);
251 gEDataManager->setHostScreenForPassedGuestScreen(iGuestScreen, iHostScreen, uiCommon().managedVMUuid());
252 }
253
254 /* Notifies about layout change: */
255 emit sigScreenLayoutChange();
256}
257
258void UIMultiScreenLayout::prepare()
259{
260 /* Make sure logic is always set: */
261 AssertPtrReturnVoid(machineLogic());
262
263 /* Recalculate host/guest screen count: */
264 calculateHostMonitorCount();
265 calculateGuestScreenCount();
266
267 /* Prepare connections: */
268 prepareConnections();
269}
270
271void UIMultiScreenLayout::prepareConnections()
272{
273 /* Connect action-pool: */
274 connect(actionPool()->toRuntime(), &UIActionPoolRuntime::sigNotifyAboutTriggeringViewScreenRemap,
275 this, &UIMultiScreenLayout::sltHandleScreenLayoutChange);
276}
277
278UIMachine *UIMultiScreenLayout::uimachine() const
279{
280 return machineLogic() ? machineLogic()->uimachine() : 0;
281}
282
283UIActionPool *UIMultiScreenLayout::actionPool() const
284{
285 return machineLogic() ? machineLogic()->actionPool() : 0;
286}
287
288void UIMultiScreenLayout::calculateHostMonitorCount()
289{
290 m_cHostMonitors = UIDesktopWidgetWatchdog::screenCount();
291}
292
293void UIMultiScreenLayout::calculateGuestScreenCount()
294{
295 m_guestScreens.clear();
296 m_disabledGuestScreens.clear();
297 uimachine()->acquireMonitorCount(m_cGuestScreens);
298 for (uint iGuestScreen = 0; iGuestScreen < m_cGuestScreens; ++iGuestScreen)
299 if (uimachine()->isScreenVisible(iGuestScreen))
300 m_guestScreens << iGuestScreen;
301 else
302 m_disabledGuestScreens << iGuestScreen;
303}
304
305quint64 UIMultiScreenLayout::memoryRequirements(const QMap<int, int> &screenLayout) const
306{
307 quint64 uUsedBits = 0;
308 const UIVisualStateType enmVisualStateType = machineLogic()->visualStateType();
309 foreach (int iGuestScreen, m_guestScreens)
310 {
311 /* Make sure corresponding screen is valid: */
312 const QRect screen = enmVisualStateType == UIVisualStateType_Fullscreen
313 ? gpDesktop->screenGeometry(screenLayout.value(iGuestScreen, 0))
314 : enmVisualStateType == UIVisualStateType_Seamless
315 ? gpDesktop->availableGeometry(screenLayout.value(iGuestScreen, 0))
316 : QRect();
317 AssertReturn(screen.isValid(), 0);
318
319 /* Get some useful screen info: */
320 ulong uDummy = 0, uGuestBpp = 0;
321 long iDummy = 0;
322 KGuestMonitorStatus enmDummy = KGuestMonitorStatus_Disabled;
323 uimachine()->acquireGuestScreenParameters(iGuestScreen, uDummy, uDummy, uGuestBpp,
324 iDummy, iDummy, enmDummy);
325 uUsedBits += screen.width() * /* display width */
326 screen.height() * /* display height */
327 uGuestBpp + /* guest bits per pixel */
328 _1M * 8; /* current cache per screen - may be changed in future */
329 }
330 uUsedBits += 4096 * 8; /* adapter info */
331 return uUsedBits;
332}
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