1 | /* $Id: UIMultiScreenLayout.cpp 29078 2010-05-05 13:17:30Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | *
|
---|
4 | * VBox frontends: Qt GUI ("VirtualBox"):
|
---|
5 | * UIMultiScreenLayout class implementation
|
---|
6 | */
|
---|
7 |
|
---|
8 | /*
|
---|
9 | * Copyright (C) 2010 Oracle Corporation
|
---|
10 | *
|
---|
11 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
12 | * available from http://www.virtualbox.org. This file is free software;
|
---|
13 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
14 | * General Public License (GPL) as published by the Free Software
|
---|
15 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
16 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
17 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
18 | */
|
---|
19 |
|
---|
20 | /* Local includes */
|
---|
21 | #include "UIMultiScreenLayout.h"
|
---|
22 | #include "COMDefs.h"
|
---|
23 | #include "UIActionsPool.h"
|
---|
24 | #include "UIMachineLogic.h"
|
---|
25 | #include "UISession.h"
|
---|
26 | #include "VBoxProblemReporter.h"
|
---|
27 |
|
---|
28 | /* Global includes */
|
---|
29 | #include <QApplication>
|
---|
30 | #include <QDesktopWidget>
|
---|
31 | #include <QMap>
|
---|
32 | #include <QMenu>
|
---|
33 |
|
---|
34 | UIMultiScreenLayout::UIMultiScreenLayout(UIMachineLogic *pMachineLogic)
|
---|
35 | : m_pMachineLogic(pMachineLogic)
|
---|
36 | , m_pScreenMap(new QMap<int, int>())
|
---|
37 | {
|
---|
38 | CMachine machine = m_pMachineLogic->session().GetMachine();
|
---|
39 | /* Get host/guest monitor count: */
|
---|
40 | #if (QT_VERSION >= 0x040600)
|
---|
41 | m_cHostScreens = QApplication::desktop()->screenCount();
|
---|
42 | #else /* (QT_VERSION >= 0x040600) */
|
---|
43 | m_cHostScreens = QApplication::desktop()->numScreens();
|
---|
44 | #endif /* !(QT_VERSION >= 0x040600) */
|
---|
45 | m_cGuestScreens = machine.GetMonitorCount();
|
---|
46 | }
|
---|
47 |
|
---|
48 | UIMultiScreenLayout::~UIMultiScreenLayout()
|
---|
49 | {
|
---|
50 | delete m_pScreenMap;
|
---|
51 | }
|
---|
52 |
|
---|
53 | void UIMultiScreenLayout::initialize(QMenu *pMenu)
|
---|
54 | {
|
---|
55 | pMenu->clear();
|
---|
56 | for (int i = 0; i < m_cGuestScreens; ++i)
|
---|
57 | {
|
---|
58 | QMenu *pScreenMenu = pMenu->addMenu(tr("Virtual Screen %1").arg(i + 1));
|
---|
59 | QActionGroup *pScreenGroup = new QActionGroup(pScreenMenu);
|
---|
60 | pScreenGroup->setExclusive(true);
|
---|
61 | connect(pScreenGroup, SIGNAL(triggered(QAction*)),
|
---|
62 | this, SLOT(sltScreenLayoutChanged(QAction*)));
|
---|
63 | for (int a = 0; a < m_cHostScreens; ++a)
|
---|
64 | {
|
---|
65 | QAction *pAction = pScreenGroup->addAction(tr("Use Host Screen %1").arg(a + 1));
|
---|
66 | pAction->setCheckable(true);
|
---|
67 | pAction->setData(RT_MAKE_U32(i, a));
|
---|
68 | }
|
---|
69 | pScreenMenu->addActions(pScreenGroup->actions());
|
---|
70 | }
|
---|
71 | }
|
---|
72 |
|
---|
73 | void UIMultiScreenLayout::update()
|
---|
74 | {
|
---|
75 | CMachine machine = m_pMachineLogic->session().GetMachine();
|
---|
76 | /* Make a pool of available host screens. */
|
---|
77 | QList<int> availableScreens;
|
---|
78 | for (int i = 0; i < m_cHostScreens; ++i)
|
---|
79 | availableScreens << i;
|
---|
80 | /* Load all combinations stored in the settings file. We have to make sure
|
---|
81 | * they are valid, which means there have to be unique combinations and all
|
---|
82 | * guests screens need there own host screen. */
|
---|
83 | QDesktopWidget *pDW = QApplication::desktop();
|
---|
84 | for (int i = 0; i < m_cGuestScreens; ++i)
|
---|
85 | {
|
---|
86 | /* If the user ever selected a combination in the view menu, we have
|
---|
87 | * the following entry: */
|
---|
88 | QString strTest = machine.GetExtraData(QString("%1%2").arg(VBoxDefs::GUI_VirtualScreenToHostScreen).arg(i));
|
---|
89 | bool fOk;
|
---|
90 | int cScreen = strTest.toInt(&fOk);
|
---|
91 | /* Check if valid: */
|
---|
92 | if (!( fOk /* Valid data */
|
---|
93 | && cScreen >= 0 && cScreen < m_cHostScreens /* In the host screen bounds? */
|
---|
94 | && m_pScreenMap->key(cScreen, -1) == -1)) /* Not taken already? */
|
---|
95 | {
|
---|
96 | /* If not, check the position of the guest window in normal mode.
|
---|
97 | * This makes sure that on first use the window opens on the same
|
---|
98 | * screen as the normal window was before. This even works with
|
---|
99 | * multi-screen. The user just have to move all the normal windows
|
---|
100 | * to the target screens and they will magically open there in
|
---|
101 | * seamless/fullscreen also. */
|
---|
102 | QString strTest1 = machine.GetExtraData(VBoxDefs::GUI_LastWindowPosition + (i > 0 ? QString::number(i): ""));
|
---|
103 | QRegExp posParser("(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
|
---|
104 | if (posParser.exactMatch(strTest1))
|
---|
105 | {
|
---|
106 | /* If parsing was successfully, convert it to a position. */
|
---|
107 | bool fOk1, fOk2;
|
---|
108 | QPoint p(posParser.cap(1).toInt(&fOk1), posParser.cap(2).toInt(&fOk2));
|
---|
109 | /* Check to which screen the position belongs. */
|
---|
110 | cScreen = pDW->screenNumber(p);
|
---|
111 | if (!( fOk1 /* Valid data */
|
---|
112 | && fOk2 /* Valid data */
|
---|
113 | && cScreen >= 0 && cScreen < m_cHostScreens /* In the host screen bounds? */
|
---|
114 | && m_pScreenMap->key(cScreen, -1) == -1)) /* Not taken already? */
|
---|
115 | /* If not, simply pick the next one of the still available
|
---|
116 | * host screens. */
|
---|
117 | cScreen = availableScreens.first();
|
---|
118 | }
|
---|
119 | else
|
---|
120 | /* If not, simply pick the next one of the still available host
|
---|
121 | * screens. */
|
---|
122 | cScreen = availableScreens.first();
|
---|
123 | }
|
---|
124 | m_pScreenMap->insert(i, cScreen);
|
---|
125 | /* Remove the just selected screen from the list of available screens. */
|
---|
126 | availableScreens.removeOne(cScreen);
|
---|
127 | }
|
---|
128 |
|
---|
129 | QList<QAction*> actions = m_pMachineLogic->actionsPool()->action(UIActionIndex_Menu_View)->menu()->actions();
|
---|
130 | for (int i = 0; i < m_pScreenMap->size(); ++i)
|
---|
131 | {
|
---|
132 | int hostScreen = m_pScreenMap->value(i);
|
---|
133 | QList<QAction*> actions1 = actions.at(i)->menu()->actions();
|
---|
134 | for (int w = 0; w < actions1.size(); ++w)
|
---|
135 | {
|
---|
136 | QAction *pTmpAction = actions1.at(w);
|
---|
137 | pTmpAction->blockSignals(true);
|
---|
138 | pTmpAction->setChecked(RT_HIWORD(pTmpAction->data().toInt()) == hostScreen);
|
---|
139 | pTmpAction->blockSignals(false);
|
---|
140 | }
|
---|
141 | }
|
---|
142 | }
|
---|
143 |
|
---|
144 | int UIMultiScreenLayout::hostScreenCount() const
|
---|
145 | {
|
---|
146 | return m_cHostScreens;
|
---|
147 | }
|
---|
148 |
|
---|
149 | int UIMultiScreenLayout::guestScreenCount() const
|
---|
150 | {
|
---|
151 | return m_cGuestScreens;
|
---|
152 | }
|
---|
153 |
|
---|
154 | int UIMultiScreenLayout::hostScreenForGuestScreen(int screenId) const
|
---|
155 | {
|
---|
156 | return m_pScreenMap->value(screenId, 0);
|
---|
157 | }
|
---|
158 |
|
---|
159 | quint64 UIMultiScreenLayout::memoryRequirements() const
|
---|
160 | {
|
---|
161 | return memoryRequirements(m_pScreenMap);
|
---|
162 | }
|
---|
163 |
|
---|
164 | bool UIMultiScreenLayout::isHostTaskbarCovert() const
|
---|
165 | {
|
---|
166 | /* Check for all screens which are in use if they have some
|
---|
167 | * taskbar/menubar/dock on it. Its done by comparing the available with the
|
---|
168 | * screen geometry. Only if they are the same for all screens, there are no
|
---|
169 | * host area covert. This is a little bit ugly, but there seems no other
|
---|
170 | * way to find out if we are on a screen where the taskbar/dock or whatever
|
---|
171 | * is present. */
|
---|
172 | QDesktopWidget *pDW = QApplication::desktop();
|
---|
173 | for (int i = 0; i < m_pScreenMap->size(); ++i)
|
---|
174 | {
|
---|
175 | int hostScreen = m_pScreenMap->value(i);
|
---|
176 | if (pDW->availableGeometry(hostScreen) != pDW->screenGeometry(hostScreen))
|
---|
177 | return true;
|
---|
178 | }
|
---|
179 | return false;
|
---|
180 | }
|
---|
181 |
|
---|
182 | void UIMultiScreenLayout::sltScreenLayoutChanged(QAction *pAction)
|
---|
183 | {
|
---|
184 | int a = pAction->data().toInt();
|
---|
185 | int cGuestScreen = RT_LOWORD(a);
|
---|
186 | int cHostScreen = RT_HIWORD(a);
|
---|
187 |
|
---|
188 | CMachine machine = m_pMachineLogic->session().GetMachine();
|
---|
189 | QMap<int,int> *pTmpMap = new QMap<int,int>(*m_pScreenMap);
|
---|
190 | /* Search for the virtual screen which is currently displayed on the
|
---|
191 | * requested host screen. When there is one found, we swap both. */
|
---|
192 | int r = pTmpMap->key(cHostScreen, -1);
|
---|
193 | if (r != -1)
|
---|
194 | pTmpMap->insert(r, pTmpMap->value(cGuestScreen));
|
---|
195 | /* Set the new host screen */
|
---|
196 | pTmpMap->insert(cGuestScreen, cHostScreen);
|
---|
197 |
|
---|
198 | bool fSuccess = true;
|
---|
199 | if (m_pMachineLogic->uisession()->isGuestAdditionsActive())
|
---|
200 | {
|
---|
201 | quint64 availBits = machine.GetVRAMSize() /* VRAM */
|
---|
202 | * _1M /* MiB to bytes */
|
---|
203 | * 8; /* to bits */
|
---|
204 | quint64 usedBits = memoryRequirements(pTmpMap);
|
---|
205 |
|
---|
206 | fSuccess = availBits >= usedBits;
|
---|
207 | if (!fSuccess)
|
---|
208 | {
|
---|
209 | /* We have to little video memory for the new layout, so say it to the
|
---|
210 | * user and revert all changes. */
|
---|
211 | if (m_pMachineLogic->visualStateType() == UIVisualStateType_Seamless)
|
---|
212 | vboxProblem().cannotSwitchScreenInSeamless((((usedBits + 7) / 8 + _1M - 1) / _1M) * _1M);
|
---|
213 | else
|
---|
214 | fSuccess = vboxProblem().cannotSwitchScreenInFullscreen((((usedBits + 7) / 8 + _1M - 1) / _1M) * _1M) != QIMessageBox::Cancel;
|
---|
215 | }
|
---|
216 | }
|
---|
217 | if (fSuccess)
|
---|
218 | {
|
---|
219 | /* Swap the temporary with the previous map. */
|
---|
220 | delete m_pScreenMap;
|
---|
221 | m_pScreenMap = pTmpMap;
|
---|
222 | }
|
---|
223 |
|
---|
224 | /* Update the menu items. Even if we can't switch we have to revert the
|
---|
225 | * menu items. */
|
---|
226 | QList<QAction*> actions = m_pMachineLogic->actionsPool()->action(UIActionIndex_Menu_View)->menu()->actions();
|
---|
227 | /* Update the settings. */
|
---|
228 | for (int i = 0; i < m_cGuestScreens; ++i)
|
---|
229 | {
|
---|
230 | int hostScreen = m_pScreenMap->value(i);
|
---|
231 | machine.SetExtraData(QString("%1%2").arg(VBoxDefs::GUI_VirtualScreenToHostScreen).arg(i), QString::number(hostScreen));
|
---|
232 | QList<QAction*> actions1 = actions.at(i)->menu()->actions();
|
---|
233 | for (int w = 0; w < actions1.size(); ++w)
|
---|
234 | {
|
---|
235 | QAction *pTmpAction = actions1.at(w);
|
---|
236 | pTmpAction->blockSignals(true);
|
---|
237 | pTmpAction->setChecked(RT_HIWORD(pTmpAction->data().toInt()) == hostScreen);
|
---|
238 | pTmpAction->blockSignals(false);
|
---|
239 | }
|
---|
240 | }
|
---|
241 |
|
---|
242 | /* On success inform the observer. */
|
---|
243 | if (fSuccess)
|
---|
244 | emit screenLayoutChanged();
|
---|
245 | }
|
---|
246 |
|
---|
247 | quint64 UIMultiScreenLayout::memoryRequirements(const QMap<int, int> *pScreenLayout) const
|
---|
248 | {
|
---|
249 | ULONG width = 0;
|
---|
250 | ULONG height = 0;
|
---|
251 | ULONG guestBpp = 0;
|
---|
252 | quint64 usedBits = 0;
|
---|
253 | CDisplay display = m_pMachineLogic->uisession()->session().GetConsole().GetDisplay();
|
---|
254 | for (int i = 0; i < m_cGuestScreens; ++ i)
|
---|
255 | {
|
---|
256 | QRect screen;
|
---|
257 | if (m_pMachineLogic->visualStateType() == UIVisualStateType_Seamless)
|
---|
258 | screen = QApplication::desktop()->availableGeometry(pScreenLayout->value(i, 0));
|
---|
259 | else
|
---|
260 | screen = QApplication::desktop()->screenGeometry(pScreenLayout->value(i, 0));
|
---|
261 | display.GetScreenResolution(i, width, height, guestBpp);
|
---|
262 | usedBits += screen.width() * /* display width */
|
---|
263 | screen.height() * /* display height */
|
---|
264 | guestBpp + /* guest bits per pixel */
|
---|
265 | _1M * 8; /* current cache per screen - may be changed in future */
|
---|
266 | }
|
---|
267 | usedBits += 4096 * 8; /* adapter info */
|
---|
268 | return usedBits;
|
---|
269 | }
|
---|
270 |
|
---|