VirtualBox

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

Last change on this file was 103538, checked in by vboxsync, 3 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: 13.1 KB
RevLine 
[27335]1/* $Id: UIMultiScreenLayout.cpp 103538 2024-02-22 17:06:26Z vboxsync $ */
2/** @file
[52727]3 * VBox Qt GUI - UIMultiScreenLayout class implementation.
[27335]4 */
5
6/*
[98103]7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
[27335]8 *
[96407]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
[27335]26 */
27
[41587]28/* GUI includes: */
[76606]29#include "UIActionPoolRuntime.h"
[98518]30#include "UICommon.h"
31#include "UIDesktopWidgetWatchdog.h"
32#include "UIExtraDataManager.h"
[103538]33#include "UILoggingDefs.h"
[98518]34#include "UIMachine.h"
[76606]35#include "UIMachineLogic.h"
36#include "UIMessageCenter.h"
[98518]37#include "UIMultiScreenLayout.h"
[27335]38
39
40UIMultiScreenLayout::UIMultiScreenLayout(UIMachineLogic *pMachineLogic)
41 : m_pMachineLogic(pMachineLogic)
[98669]42 , m_cGuestScreens(0)
[98518]43 , m_cHostMonitors(0)
[27335]44{
[98518]45 prepare();
46}
[71615]47
[98518]48bool UIMultiScreenLayout::hasHostScreenForGuestScreen(int iScreenId) const
49{
50 return m_screenMap.contains(iScreenId);
[27335]51}
52
[98518]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
[27335]63void UIMultiScreenLayout::update()
64{
[46924]65 LogRelFlow(("UIMultiScreenLayout::update: Started...\n"));
66
[44839]67 /* Clear screen-map initially: */
68 m_screenMap.clear();
69
[44834]70 /* Make a pool of available host screens: */
[27335]71 QList<int> availableScreens;
[98518]72 for (int i = 0; i < m_cHostMonitors; ++i)
[27335]73 availableScreens << i;
[44834]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
[98518]77 * and all guests screens need there own host-monitor. */
78 const bool fShouldWeAutoMountGuestScreens = gEDataManager->autoMountGuestScreensEnabled(uiCommon().managedVMUuid());
[56126]79 LogRel(("GUI: UIMultiScreenLayout::update: GUI/AutomountGuestScreens is %s\n", fShouldWeAutoMountGuestScreens ? "enabled" : "disabled"));
[44838]80 foreach (int iGuestScreen, m_guestScreens)
[27335]81 {
[44837]82 /* Initialize variables: */
83 bool fValid = false;
84 int iHostScreen = -1;
85
86 if (!fValid)
[29076]87 {
[44837]88 /* If the user ever selected a combination in the view menu, we have the following entry: */
[79365]89 iHostScreen = gEDataManager->hostScreenForPassedGuestScreen(iGuestScreen, uiCommon().managedVMUuid());
[44837]90 /* Revalidate: */
[98518]91 fValid = iHostScreen >= 0 && iHostScreen < m_cHostMonitors /* In the host-monitor bounds? */
[44837]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.
[51587]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. */
[79365]101 QRect geo = gEDataManager->machineWindowGeometry(UIVisualStateType_Normal, iGuestScreen, uiCommon().managedVMUuid());
[51587]102 /* If geometry is valid: */
103 if (!geo.isNull())
[29076]104 {
[51587]105 /* Get top-left corner position: */
106 QPoint topLeftPosition(geo.topLeft());
107 /* Check which host-screen the position belongs to: */
[97681]108 iHostScreen = UIDesktopWidgetWatchdog::screenNumber(topLeftPosition);
[44837]109 /* Revalidate: */
[98518]110 fValid = iHostScreen >= 0 && iHostScreen < m_cHostMonitors /* In the host-monitor bounds? */
[44837]111 && m_screenMap.key(iHostScreen, -1) == -1; /* Not taken already? */
[29076]112 }
[44837]113 }
114
115 if (!fValid)
116 {
117 /* If still not valid, pick the next one
[98518]118 * if there is still available host-monitor: */
[44837]119 if (!availableScreens.isEmpty())
120 {
[44836]121 iHostScreen = availableScreens.first();
[44837]122 fValid = true;
123 }
[29076]124 }
[44837]125
126 if (fValid)
127 {
[98518]128 /* Register host-monitor for the guest-screen: */
[44837]129 m_screenMap.insert(iGuestScreen, iHostScreen);
130 /* Remove it from the list of available host screens: */
131 availableScreens.removeOne(iHostScreen);
132 }
[45402]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: */
[56126]137 LogRel(("GUI: UIMultiScreenLayout::update: Disabling excessive guest-screen %d\n", iGuestScreen));
[98518]138 uimachine()->setScreenVisibleHostDesires(iGuestScreen, false);
[98547]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? */);
[45402]146 }
[27335]147 }
148
[45402]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 {
[48892]161 /* Defaults: */
162 ULONG uWidth = 800;
163 ULONG uHeight = 600;
164 /* Try to get previous guest-screen arguments: */
[98452]165 const int iGuestScreen = m_disabledGuestScreens.at(iGuestScreenIndex);
[98518]166 const QSize guestScreenSize = uimachine()->guestScreenSize(iGuestScreen);
[48892]167 {
[98452]168 if (guestScreenSize.width() > 0)
169 uWidth = guestScreenSize.width();
170 if (guestScreenSize.height() > 0)
171 uHeight = guestScreenSize.height();
[48892]172 }
173 /* Re-enable guest-screen with proper resolution: */
[56126]174 LogRel(("GUI: UIMultiScreenLayout::update: Enabling guest-screen %d with following resolution: %dx%d\n",
[53287]175 iGuestScreen, uWidth, uHeight));
[98518]176 uimachine()->setScreenVisibleHostDesires(iGuestScreen, true);
[98547]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? */);
[45402]184 }
185 }
186
[71615]187 /* Make sure action-pool knows whether multi-screen layout has host-screen for guest-screen: */
[98518]188 actionPool()->toRuntime()->setHostScreenForGuestScreenMap(m_screenMap);
[46924]189
190 LogRelFlow(("UIMultiScreenLayout::update: Finished!\n"));
[27335]191}
192
[44839]193void UIMultiScreenLayout::rebuild()
194{
[46924]195 LogRelFlow(("UIMultiScreenLayout::rebuild: Started...\n"));
196
[44839]197 /* Recalculate host/guest screen count: */
198 calculateHostMonitorCount();
199 calculateGuestScreenCount();
[98518]200
[44839]201 /* Update layout: */
202 update();
[46924]203
204 LogRelFlow(("UIMultiScreenLayout::rebuild: Finished!\n"));
[44839]205}
206
[98518]207void UIMultiScreenLayout::sltHandleScreenLayoutChange(int iRequestedGuestScreen, int iRequestedHostMonitor)
[27335]208{
209 /* Search for the virtual screen which is currently displayed on the
[98518]210 * requested host-monitor. When there is one found, we swap both. */
[44834]211 QMap<int,int> tmpMap(m_screenMap);
[98518]212 const int iCurrentGuestScreen = tmpMap.key(iRequestedHostMonitor, -1);
[44837]213 if (iCurrentGuestScreen != -1 && tmpMap.contains(iRequestedGuestScreen))
[44836]214 tmpMap.insert(iCurrentGuestScreen, tmpMap.value(iRequestedGuestScreen));
[44837]215 else
216 tmpMap.remove(iCurrentGuestScreen);
[98518]217 tmpMap.insert(iRequestedGuestScreen, iRequestedHostMonitor);
[27335]218
[44834]219 /* Check the memory requirements first: */
[27419]220 bool fSuccess = true;
[98518]221 if (uimachine()->isGuestSupportsGraphics())
[27419]222 {
[98670]223 ulong uVRAMSize = 0;
224 uimachine()->acquireVRAMSize(uVRAMSize);
225 const quint64 uAvailBits = uVRAMSize * _1M * 8;
226 const quint64 uUsedBits = memoryRequirements(tmpMap);
[98518]227 fSuccess = uAvailBits >= uUsedBits;
[27419]228 if (!fSuccess)
229 {
[44834]230 /* We have too little video memory for the new layout, so say it to the user and revert all the changes: */
[98518]231 if (machineLogic()->visualStateType() == UIVisualStateType_Seamless)
232 msgCenter().cannotSwitchScreenInSeamless((((uUsedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
[27419]233 else
[98518]234 fSuccess = msgCenter().cannotSwitchScreenInFullscreen((((uUsedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
[27419]235 }
236 }
[44837]237 /* Make sure memory requirements matched: */
238 if (!fSuccess)
239 return;
[27419]240
[44837]241 /* Swap the maps: */
242 m_screenMap = tmpMap;
[52239]243
[71615]244 /* Make sure action-pool knows whether multi-screen layout has host-screen for guest-screen: */
[98518]245 actionPool()->toRuntime()->setHostScreenForGuestScreenMap(m_screenMap);
[71615]246
[52239]247 /* Save guest-to-host mapping: */
[98518]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 }
[52239]253
254 /* Notifies about layout change: */
255 emit sigScreenLayoutChange();
[27335]256}
257
[98518]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
[44830]288void UIMultiScreenLayout::calculateHostMonitorCount()
289{
[98518]290 m_cHostMonitors = UIDesktopWidgetWatchdog::screenCount();
[44830]291}
292
293void UIMultiScreenLayout::calculateGuestScreenCount()
294{
[44838]295 m_guestScreens.clear();
[45402]296 m_disabledGuestScreens.clear();
[98669]297 uimachine()->acquireMonitorCount(m_cGuestScreens);
[65234]298 for (uint iGuestScreen = 0; iGuestScreen < m_cGuestScreens; ++iGuestScreen)
[98518]299 if (uimachine()->isScreenVisible(iGuestScreen))
[44838]300 m_guestScreens << iGuestScreen;
[45402]301 else
302 m_disabledGuestScreens << iGuestScreen;
[44830]303}
304
[44833]305quint64 UIMultiScreenLayout::memoryRequirements(const QMap<int, int> &screenLayout) const
[27419]306{
[98518]307 quint64 uUsedBits = 0;
308 const UIVisualStateType enmVisualStateType = machineLogic()->visualStateType();
[44838]309 foreach (int iGuestScreen, m_guestScreens)
[27419]310 {
[98518]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: */
[98520]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);
[98518]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 */
[27419]329 }
[98518]330 uUsedBits += 4096 * 8; /* adapter info */
331 return uUsedBits;
[27419]332}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use