VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMultiScreenLayout.cpp@ 100064

Last change on this file since 100064 was 98670, checked in by vboxsync, 16 months ago

FE/Qt: bugref:10322: Runtime UI: Reworking CMachine wrapper usage step-by-step; Rest of graphics adapter stuff.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use