VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 4 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: 31.8 KB
Line 
1/* $Id: UIMachineLogicFullscreen.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMachineLogicFullscreen 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#include <QTimer>
30
31/* GUI includes: */
32#include "QIMenu.h"
33#include "UIActionPoolRuntime.h"
34#include "UICommon.h"
35#include "UIDesktopWidgetWatchdog.h"
36#include "UILoggingDefs.h"
37#include "UIMachine.h"
38#include "UIMachineLogicFullscreen.h"
39#include "UIMachineView.h"
40#include "UIMachineWindowFullscreen.h"
41#include "UIMessageCenter.h"
42#include "UIMultiScreenLayout.h"
43#include "UIShortcutPool.h"
44#ifdef VBOX_WS_MAC
45# include "UICocoaApplication.h"
46# include "UIExtraDataManager.h"
47# include "VBoxUtils.h"
48# include <Carbon/Carbon.h>
49#endif /* VBOX_WS_MAC */
50
51/* COM includes: */
52#include "CGraphicsAdapter.h"
53
54
55UIMachineLogicFullscreen::UIMachineLogicFullscreen(UIMachine *pMachine)
56 : UIMachineLogic(pMachine)
57 , m_pPopupMenu(0)
58#ifdef VBOX_WS_MAC
59 , m_fScreensHaveSeparateSpaces(darwinScreensHaveSeparateSpaces())
60#endif /* VBOX_WS_MAC */
61{
62 /* Create multiscreen layout: */
63 m_pScreenLayout = new UIMultiScreenLayout(this);
64}
65
66UIMachineLogicFullscreen::~UIMachineLogicFullscreen()
67{
68 /* Delete multiscreen layout: */
69 delete m_pScreenLayout;
70}
71
72int UIMachineLogicFullscreen::hostScreenForGuestScreen(int iScreenId) const
73{
74 return m_pScreenLayout->hostScreenForGuestScreen(iScreenId);
75}
76
77bool UIMachineLogicFullscreen::hasHostScreenForGuestScreen(int iScreenId) const
78{
79 return m_pScreenLayout->hasHostScreenForGuestScreen(iScreenId);
80}
81
82bool UIMachineLogicFullscreen::checkAvailability()
83{
84 /* Check if there is enough physical memory to enter fullscreen: */
85 if (uimachine()->isGuestSupportsGraphics())
86 {
87 ulong uVRAMSize = 0;
88 uimachine()->acquireVRAMSize(uVRAMSize);
89 quint64 uAvailBits = uVRAMSize * _1M /* MiB to bytes */ * 8 /* to bits */;
90 quint64 uUsedBits = m_pScreenLayout->memoryRequirements();
91 if (uAvailBits < uUsedBits)
92 {
93 if (!msgCenter().cannotEnterFullscreenMode(0, 0, 0, (((uUsedBits + 7) / 8 + _1M - 1) / _1M) * _1M))
94 return false;
95 }
96 }
97
98 /* Show the info message. */
99 const UIShortcut &shortcut =
100 gShortcutPool->shortcut(actionPool()->shortcutsExtraDataID(),
101 actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen)->shortcutExtraDataID());
102 const QString strHotKey = QString("Host+%1").arg(shortcut.primaryToPortableText());
103 if (!msgCenter().confirmGoingFullscreen(strHotKey))
104 return false;
105
106 return true;
107}
108
109Qt::WindowFlags UIMachineLogicFullscreen::windowFlags(ulong uScreenId) const
110{
111 Q_UNUSED(uScreenId);
112#ifdef VBOX_WS_MAC
113 return uScreenId == 0 || screensHaveSeparateSpaces() ? Qt::Window : Qt::FramelessWindowHint;
114#else /* !VBOX_WS_MAC */
115 return Qt::FramelessWindowHint;
116#endif /* !VBOX_WS_MAC */
117}
118
119void UIMachineLogicFullscreen::adjustMachineWindowsGeometry()
120{
121 LogRel(("GUI: UIMachineLogicFullscreen::adjustMachineWindowsGeometry\n"));
122
123 /* Rebuild multi-screen layout: */
124 m_pScreenLayout->rebuild();
125
126#ifdef VBOX_WS_MAC
127 /* Revalidate native fullscreen: */
128 revalidateNativeFullScreen();
129#else /* !VBOX_WS_MAC */
130 /* Make sure all machine-window(s) have proper geometry: */
131 foreach (UIMachineWindow *pMachineWindow, machineWindows())
132 pMachineWindow->showInNecessaryMode();
133#endif /* !VBOX_WS_MAC */
134}
135
136#ifdef RT_OS_DARWIN
137void UIMachineLogicFullscreen::sltHandleNativeFullscreenWillEnter()
138{
139 /* Get sender machine-window: */
140 UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
141 AssertPtrReturnVoid(pMachineWindow);
142 LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenWillEnter: "
143 "Machine-window #%d will enter native fullscreen\n",
144 (int)pMachineWindow->screenId()));
145}
146
147void UIMachineLogicFullscreen::sltHandleNativeFullscreenDidEnter()
148{
149 /* Get sender machine-window: */
150 UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
151 AssertPtrReturnVoid(pMachineWindow);
152 LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenDidEnter: "
153 "Machine-window #%d did enter native fullscreen\n",
154 (int)pMachineWindow->screenId()));
155
156 /* Add machine-window to corresponding set: */
157 m_fullscreenMachineWindows.insert(pMachineWindow);
158 AssertReturnVoid(m_fullscreenMachineWindows.contains(pMachineWindow));
159
160 /* Rebuild multi-screen layout: */
161 m_pScreenLayout->rebuild();
162 /* Revalidate native fullscreen: */
163 revalidateNativeFullScreen();
164}
165
166void UIMachineLogicFullscreen::sltHandleNativeFullscreenWillExit()
167{
168 /* Get sender machine-window: */
169 UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
170 AssertPtrReturnVoid(pMachineWindow);
171 LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenWillExit: "
172 "Machine-window #%d will exit native fullscreen\n",
173 (int)pMachineWindow->screenId()));
174}
175
176void UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit()
177{
178 /* Get sender machine-window: */
179 UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
180 AssertPtrReturnVoid(pMachineWindow);
181
182 /* Remove machine-window from corresponding set: */
183 bool fResult = m_fullscreenMachineWindows.remove(pMachineWindow);
184 AssertReturnVoid(!m_fullscreenMachineWindows.contains(pMachineWindow));
185
186 /* We have same signal if window did fail to enter native fullscreen.
187 * In that case window missed in m_fullscreenMachineWindows,
188 * ignore this signal silently: */
189 if (!fResult)
190 return;
191
192 /* If that window was invalidated: */
193 if (m_invalidFullscreenMachineWindows.contains(pMachineWindow))
194 {
195 LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit: "
196 "Machine-window #%d exited invalidated native fullscreen, revalidate it\n",
197 (int)pMachineWindow->screenId()));
198
199 /* Exclude machine-window from invalidation set: */
200 m_invalidFullscreenMachineWindows.remove(pMachineWindow);
201 AssertReturnVoid(!m_invalidFullscreenMachineWindows.contains(pMachineWindow));
202
203 /* Rebuild multi-screen layout: */
204 m_pScreenLayout->rebuild();
205 /* Revalidate native fullscreen: */
206 revalidateNativeFullScreen();
207 }
208 /* If there are no invalidated windows: */
209 else if (m_invalidFullscreenMachineWindows.isEmpty())
210 {
211 /* If there are 'fullscreen' windows: */
212 if (!m_fullscreenMachineWindows.isEmpty())
213 {
214 LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit: "
215 "Machine-window #%d exited native fullscreen, asking others to exit too...\n",
216 (int)pMachineWindow->screenId()));
217
218 /* Ask window(s) to exit 'fullscreen' mode: */
219 emit sigNotifyAboutNativeFullscreenShouldBeExited();
220 }
221 /* If there are no 'fullscreen' windows: */
222 else
223 {
224 LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit: "
225 "Machine-window #%d exited native fullscreen, changing visual-state to requested...\n",
226 (int)pMachineWindow->screenId()));
227
228 /* Change visual-state to requested: */
229 UIVisualStateType type = uimachine()->requestedVisualState();
230 if (type == UIVisualStateType_Invalid)
231 type = UIVisualStateType_Normal;
232 uimachine()->setRequestedVisualState(UIVisualStateType_Invalid);
233 uimachine()->asyncChangeVisualState(type);
234 }
235 }
236}
237
238void UIMachineLogicFullscreen::sltHandleNativeFullscreenFailToEnter()
239{
240 /* Get sender machine-window: */
241 UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(sender());
242 AssertReturnVoid(pMachineWindow);
243
244 /* Make sure this window is not registered somewhere: */
245 AssertReturnVoid(!m_fullscreenMachineWindows.remove(pMachineWindow));
246 AssertReturnVoid(!m_invalidFullscreenMachineWindows.remove(pMachineWindow));
247
248 /* If there are 'fullscreen' windows: */
249 if (!m_fullscreenMachineWindows.isEmpty())
250 {
251 LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenFailToEnter: "
252 "Machine-window #%d failed to enter native fullscreen, asking others to exit...\n",
253 (int)pMachineWindow->screenId()));
254
255 /* Ask window(s) to exit 'fullscreen' mode: */
256 emit sigNotifyAboutNativeFullscreenShouldBeExited();
257 }
258 /* If there are no 'fullscreen' windows: */
259 else
260 {
261 LogRel(("GUI: UIMachineLogicFullscreen::sltHandleNativeFullscreenFailToEnter: "
262 "Machine-window #%d failed to enter native fullscreen, requesting change visual-state to normal...\n",
263 (int)pMachineWindow->screenId()));
264
265 /* Ask session to change 'fullscreen' mode to 'normal': */
266 uimachine()->setRequestedVisualState(UIVisualStateType_Normal);
267
268 /* If machine UI already initialized => push mode-change directly: */
269 if (uimachine()->isInitialized())
270 sltCheckForRequestedVisualStateType();
271 }
272}
273
274void UIMachineLogicFullscreen::sltChangeVisualStateToNormal()
275{
276 /* Request 'normal' (window) visual-state: */
277 uimachine()->setRequestedVisualState(UIVisualStateType_Normal);
278 /* Ask window(s) to exit 'fullscreen' mode: */
279 emit sigNotifyAboutNativeFullscreenShouldBeExited();
280}
281
282void UIMachineLogicFullscreen::sltChangeVisualStateToSeamless()
283{
284 /* Request 'seamless' visual-state: */
285 uimachine()->setRequestedVisualState(UIVisualStateType_Seamless);
286 /* Ask window(s) to exit 'fullscreen' mode: */
287 emit sigNotifyAboutNativeFullscreenShouldBeExited();
288}
289
290void UIMachineLogicFullscreen::sltChangeVisualStateToScale()
291{
292 /* Request 'scale' visual-state: */
293 uimachine()->setRequestedVisualState(UIVisualStateType_Scale);
294 /* Ask window(s) to exit 'fullscreen' mode: */
295 emit sigNotifyAboutNativeFullscreenShouldBeExited();
296}
297
298void UIMachineLogicFullscreen::sltCheckForRequestedVisualStateType()
299{
300 LogRel(("GUI: UIMachineLogicFullscreen::sltCheckForRequestedVisualStateType: Requested-state=%d, Machine-state=%d\n",
301 uimachine()->requestedVisualState(), uimachine()->machineState()));
302
303 /* Do not try to change visual-state type if machine was not started yet: */
304 if (!uimachine()->isRunning() && !uimachine()->isPaused())
305 return;
306
307 /* Do not try to change visual-state type in 'manual override' mode: */
308 if (uimachine()->isManualOverrideMode())
309 return;
310
311 /* Check requested visual-state types: */
312 switch (uimachine()->requestedVisualState())
313 {
314 /* If 'normal' visual-state type is requested: */
315 case UIVisualStateType_Normal:
316 {
317 LogRel(("GUI: UIMachineLogicFullscreen::sltCheckForRequestedVisualStateType: "
318 "Going 'normal' as requested...\n"));
319 uimachine()->setRequestedVisualState(UIVisualStateType_Invalid);
320 uimachine()->asyncChangeVisualState(UIVisualStateType_Normal);
321 break;
322 }
323 default:
324 break;
325 }
326}
327#endif /* RT_OS_DARWIN */
328
329void UIMachineLogicFullscreen::sltMachineStateChanged()
330{
331 /* Call to base-class: */
332 UIMachineLogic::sltMachineStateChanged();
333
334 /* If machine-state changed from 'paused' to 'running': */
335 if (uimachine()->isRunning() && uimachine()->wasPaused())
336 {
337 LogRel(("GUI: UIMachineLogicFullscreen::sltMachineStateChanged:"
338 "Machine-state changed from 'paused' to 'running': "
339 "Adjust machine-window geometry...\n"));
340
341 /* Make sure further code will be called just once: */
342 uimachine()->forgetPreviousMachineState();
343 /* Adjust machine-window geometry if necessary: */
344 adjustMachineWindowsGeometry();
345 }
346}
347
348void UIMachineLogicFullscreen::sltInvokePopupMenu()
349{
350 /* Popup main-menu if present: */
351 if (m_pPopupMenu && !m_pPopupMenu->isEmpty())
352 {
353 m_pPopupMenu->popup(activeMachineWindow()->geometry().center());
354 QTimer::singleShot(0, m_pPopupMenu, SLOT(sltHighlightFirstAction()));
355 }
356}
357
358void UIMachineLogicFullscreen::sltScreenLayoutChanged()
359{
360 LogRel(("GUI: UIMachineLogicFullscreen::sltScreenLayoutChanged: Multi-screen layout changed\n"));
361
362#ifdef VBOX_WS_MAC
363 /* Revalidate native fullscreen: */
364 revalidateNativeFullScreen();
365#else /* !VBOX_WS_MAC */
366 /* Make sure all machine-window(s) have proper geometry: */
367 foreach (UIMachineWindow *pMachineWindow, machineWindows())
368 pMachineWindow->showInNecessaryMode();
369#endif /* !VBOX_WS_MAC */
370}
371
372void UIMachineLogicFullscreen::sltGuestMonitorChange(KGuestMonitorChangedEventType changeType, ulong uScreenId, QRect screenGeo)
373{
374 LogRel(("GUI: UIMachineLogicFullscreen: Guest-screen count changed\n"));
375
376 /* Rebuild multi-screen layout: */
377 m_pScreenLayout->rebuild();
378
379#ifdef VBOX_WS_MAC
380 /* Revalidate native fullscreen: */
381 RT_NOREF(changeType, uScreenId, screenGeo);
382 revalidateNativeFullScreen();
383#else /* !VBOX_WS_MAC */
384 /* Call to base-class: */
385 UIMachineLogic::sltGuestMonitorChange(changeType, uScreenId, screenGeo);
386#endif /* !VBOX_WS_MAC */
387}
388
389void UIMachineLogicFullscreen::sltHostScreenCountChange()
390{
391 LogRel(("GUI: UIMachineLogicFullscreen: Host-screen count changed\n"));
392
393 /* Rebuild multi-screen layout: */
394 m_pScreenLayout->rebuild();
395
396#ifdef VBOX_WS_MAC
397 /* Revalidate native fullscreen: */
398 revalidateNativeFullScreen();
399#else /* !VBOX_WS_MAC */
400 /* Call to base-class: */
401 UIMachineLogic::sltHostScreenCountChange();
402#endif /* !VBOX_WS_MAC */
403}
404
405void UIMachineLogicFullscreen::sltHostScreenAvailableAreaChange()
406{
407 LogRel2(("GUI: UIMachineLogicFullscreen: Host-screen available-area change ignored\n"));
408}
409
410void UIMachineLogicFullscreen::sltAdditionsStateChanged()
411{
412 /* Call to base-class: */
413 UIMachineLogic::sltAdditionsStateChanged();
414
415 LogRel(("GUI: UIMachineLogicFullscreen: Additions-state actual-change event, rebuild multi-screen layout\n"));
416 /* Rebuild multi-screen layout: */
417 m_pScreenLayout->rebuild();
418}
419
420void UIMachineLogicFullscreen::prepareActionGroups()
421{
422 /* Call to base-class: */
423 UIMachineLogic::prepareActionGroups();
424
425 /* Restrict 'Adjust Window', 'Status Bar' and 'Resize' actions for 'View' menu: */
426 actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
427 (UIExtraDataMetaDefs::RuntimeMenuViewActionType)
428 (UIExtraDataMetaDefs::RuntimeMenuViewActionType_AdjustWindow |
429 UIExtraDataMetaDefs::RuntimeMenuViewActionType_MenuBar |
430 UIExtraDataMetaDefs::RuntimeMenuViewActionType_StatusBar |
431 UIExtraDataMetaDefs::RuntimeMenuViewActionType_Resize));
432#ifdef VBOX_WS_MAC
433 /* Restrict 'Window' menu: */
434 actionPool()->toRuntime()->setRestrictionForMenuBar(UIActionRestrictionLevel_Logic,
435 UIExtraDataMetaDefs::MenuType_Window);
436#endif /* VBOX_WS_MAC */
437
438 /* Take care of view-action toggle state: */
439 UIAction *pActionFullscreen = actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen);
440 if (!pActionFullscreen->isChecked())
441 {
442 pActionFullscreen->blockSignals(true);
443 pActionFullscreen->setChecked(true);
444 pActionFullscreen->blockSignals(false);
445 }
446}
447
448void UIMachineLogicFullscreen::prepareActionConnections()
449{
450 /* Call to base-class: */
451 UIMachineLogic::prepareActionConnections();
452
453 /* Prepare 'View' actions connections: */
454 connect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &UIAction::triggered,
455 this, &UIMachineLogicFullscreen::sltChangeVisualStateToNormal);
456 connect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &UIAction::triggered,
457 this, &UIMachineLogicFullscreen::sltChangeVisualStateToSeamless);
458 connect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &UIAction::triggered,
459 this, &UIMachineLogicFullscreen::sltChangeVisualStateToScale);
460}
461
462void UIMachineLogicFullscreen::prepareMachineWindows()
463{
464 /* Do not create machine-window(s) if they created already: */
465 if (isMachineWindowsCreated())
466 return;
467
468#ifdef VBOX_WS_MAC
469 /* Register to native notifications: */
470 UICocoaApplication::instance()->registerToNotificationOfWorkspace("NSWorkspaceDidActivateApplicationNotification", this,
471 UIMachineLogicFullscreen::nativeHandlerForApplicationActivation);
472 UICocoaApplication::instance()->registerToNotificationOfWorkspace("NSWorkspaceActiveSpaceDidChangeNotification", this,
473 UIMachineLogicFullscreen::nativeHandlerForActiveSpaceChange);
474
475 /* We have to make sure that we are getting the front most process.
476 * This is necessary for Qt versions > 4.3.3: */
477 darwinSetFrontMostProcess();
478#endif /* VBOX_WS_MAC */
479
480 /* Update the multi-screen layout: */
481 m_pScreenLayout->update();
482
483 /* Acquire monitor count: */
484 ulong cMonitorCount = 0;
485 uimachine()->acquireMonitorCount(cMonitorCount);
486
487 /* Create machine-window(s): */
488 for (uint cScreenId = 0; cScreenId < cMonitorCount; ++cScreenId)
489 addMachineWindow(UIMachineWindow::create(this, cScreenId));
490
491 /* Connect multi-screen layout change handler: */
492 connect(m_pScreenLayout, &UIMultiScreenLayout::sigScreenLayoutChange,
493 this, &UIMachineLogicFullscreen::sltScreenLayoutChanged);
494
495#ifdef VBOX_WS_MAC
496 /* Enable native fullscreen support: */
497 foreach (UIMachineWindow *pMachineWindow, machineWindows())
498 {
499 UIMachineWindowFullscreen *pMachineWindowFullscreen = qobject_cast<UIMachineWindowFullscreen*>(pMachineWindow);
500 if (!pMachineWindow)
501 continue;
502 /* Logic => window signals: */
503 connect(this, &UIMachineLogicFullscreen::sigNotifyAboutNativeFullscreenShouldBeEntered,
504 pMachineWindowFullscreen, &UIMachineWindowFullscreen::sltEnterNativeFullscreen);
505 connect(this, &UIMachineLogicFullscreen::sigNotifyAboutNativeFullscreenShouldBeExited,
506 pMachineWindowFullscreen, &UIMachineWindowFullscreen::sltExitNativeFullscreen);
507 /* Window => logic signals: */
508 connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenWillEnter,
509 this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenWillEnter,
510 Qt::QueuedConnection);
511 connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenDidEnter,
512 this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenDidEnter,
513 Qt::QueuedConnection);
514 connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenWillExit,
515 this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenWillExit,
516 Qt::QueuedConnection);
517 connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenDidExit,
518 this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenDidExit,
519 Qt::QueuedConnection);
520 connect(pMachineWindowFullscreen, &UIMachineWindowFullscreen::sigNotifyAboutNativeFullscreenFailToEnter,
521 this, &UIMachineLogicFullscreen::sltHandleNativeFullscreenFailToEnter,
522 Qt::QueuedConnection);
523 }
524 /* Revalidate native fullscreen: */
525 revalidateNativeFullScreen();
526#endif /* VBOX_WS_MAC */
527
528 /* Mark machine-window(s) created: */
529 setMachineWindowsCreated(true);
530
531#ifdef VBOX_WS_NIX
532 switch (uiCommon().typeOfWindowManager())
533 {
534 case X11WMType_GNOMEShell:
535 case X11WMType_Mutter:
536 {
537 // WORKAROUND:
538 // Under certain WMs we can loose machine-window activation due to any Qt::Tool
539 // overlay asynchronously shown above it. Qt is not become aware of such event.
540 // We are going to ask to return machine-window activation in let's say 100ms.
541 QTimer::singleShot(100, machineWindows().first(), SLOT(sltActivateWindow()));
542 break;
543 }
544 default:
545 break;
546 }
547#endif /* VBOX_WS_NIX */
548}
549
550void UIMachineLogicFullscreen::prepareMenu()
551{
552 /* Prepare popup-menu: */
553 m_pPopupMenu = new QIMenu;
554 AssertPtrReturnVoid(m_pPopupMenu);
555 {
556 /* Prepare popup-menu: */
557 foreach (QMenu *pMenu, actionPool()->menus())
558 m_pPopupMenu->addMenu(pMenu);
559 }
560}
561
562void UIMachineLogicFullscreen::cleanupMenu()
563{
564 /* Cleanup popup-menu: */
565 delete m_pPopupMenu;
566 m_pPopupMenu = 0;
567}
568
569void UIMachineLogicFullscreen::cleanupMachineWindows()
570{
571 /* Do not destroy machine-window(s) if they destroyed already: */
572 if (!isMachineWindowsCreated())
573 return;
574
575#ifdef VBOX_WS_MAC
576 /* Unregister from native notifications: */
577 UICocoaApplication::instance()->unregisterFromNotificationOfWorkspace("NSWorkspaceDidActivateApplicationNotification", this);
578 UICocoaApplication::instance()->unregisterFromNotificationOfWorkspace("NSWorkspaceActiveSpaceDidChangeNotification", this);
579#endif/* VBOX_WS_MAC */
580
581 /* Mark machine-window(s) destroyed: */
582 setMachineWindowsCreated(false);
583
584 /* Destroy machine-window(s): */
585 foreach (UIMachineWindow *pMachineWindow, machineWindows())
586 UIMachineWindow::destroy(pMachineWindow);
587}
588
589void UIMachineLogicFullscreen::cleanupActionConnections()
590{
591 /* "View" actions disconnections: */
592 disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen), &QAction::triggered,
593 this, &UIMachineLogicFullscreen::sltChangeVisualStateToNormal);
594 disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Seamless), &QAction::triggered,
595 this, &UIMachineLogicFullscreen::sltChangeVisualStateToSeamless);
596 disconnect(actionPool()->action(UIActionIndexRT_M_View_T_Scale), &QAction::triggered,
597 this, &UIMachineLogicFullscreen::sltChangeVisualStateToScale);
598
599 /* Call to base-class: */
600 UIMachineLogic::cleanupActionConnections();
601}
602
603void UIMachineLogicFullscreen::cleanupActionGroups()
604{
605 /* Take care of view-action toggle state: */
606 UIAction *pActionFullscreen = actionPool()->action(UIActionIndexRT_M_View_T_Fullscreen);
607 if (pActionFullscreen->isChecked())
608 {
609 pActionFullscreen->blockSignals(true);
610 pActionFullscreen->setChecked(false);
611 pActionFullscreen->blockSignals(false);
612 }
613
614 /* Allow 'Adjust Window', 'Status Bar' and 'Resize' actions for 'View' menu: */
615 actionPool()->toRuntime()->setRestrictionForMenuView(UIActionRestrictionLevel_Logic,
616 UIExtraDataMetaDefs::RuntimeMenuViewActionType_Invalid);
617#ifdef VBOX_WS_MAC
618 /* Allow 'Window' menu: */
619 actionPool()->toRuntime()->setRestrictionForMenuBar(UIActionRestrictionLevel_Logic,
620 UIExtraDataMetaDefs::MenuType_Invalid);
621#endif /* VBOX_WS_MAC */
622
623 /* Call to base-class: */
624 UIMachineLogic::cleanupActionGroups();
625}
626
627#ifdef VBOX_WS_MAC
628void UIMachineLogicFullscreen::revalidateNativeFullScreen(UIMachineWindow *pMachineWindow)
629{
630 /* Make sure that is full-screen machine-window: */
631 UIMachineWindowFullscreen *pMachineWindowFullscreen = qobject_cast<UIMachineWindowFullscreen*>(pMachineWindow);
632 AssertPtrReturnVoid(pMachineWindowFullscreen);
633
634 /* Make sure window is not already invalidated: */
635 if (m_invalidFullscreenMachineWindows.contains(pMachineWindow))
636 return;
637
638 /* Ignore window if it is in 'fullscreen transition': */
639 if (pMachineWindowFullscreen->isInFullscreenTransition())
640 return;
641
642 /* Get screen ID: */
643 const ulong uScreenID = pMachineWindow->screenId();
644 LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: For machine-window #%d\n",
645 (int)uScreenID));
646
647 /* Validate window which can't be fullscreen: */
648 if (uScreenID != 0 && !screensHaveSeparateSpaces())
649 {
650 /* We are hiding transient window if:
651 * 1. primary window is not on active user-space
652 * 2. there is no fullscreen window or it's invalidated. */
653 if ( !darwinIsOnActiveSpace(machineWindows().first())
654 || m_fullscreenMachineWindows.isEmpty() || !m_invalidFullscreenMachineWindows.isEmpty())
655 {
656 LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
657 "Ask transient machine-window #%d to hide\n", (int)uScreenID));
658
659 /* Make sure window hidden: */
660 pMachineWindow->hide();
661 }
662 /* If there is valid fullscreen window: */
663 else
664 {
665 LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
666 "Ask transient machine-window #%d to show/normalize\n", (int)uScreenID));
667
668 /* Make sure window have proper geometry and shown: */
669 pMachineWindow->showInNecessaryMode();
670 }
671 }
672 /* Validate window which can be fullscreen: */
673 else
674 {
675 /* Validate window which is not in fullscreen: */
676 if (!darwinIsInFullscreenMode(pMachineWindow))
677 {
678 /* If that window
679 * 1. should really be shown and
680 * 2. is mapped to some host-screen: */
681 if ( uimachine()->isScreenVisible(uScreenID)
682 && hasHostScreenForGuestScreen(uScreenID))
683 {
684 LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
685 "Ask machine-window #%d to enter native fullscreen\n", (int)uScreenID));
686
687 /* Make sure window have proper geometry and shown: */
688 pMachineWindow->showInNecessaryMode();
689
690 /* Ask window to enter 'fullscreen' mode: */
691 emit sigNotifyAboutNativeFullscreenShouldBeEntered(pMachineWindow);
692 }
693 /* If that window
694 * is shown while shouldn't: */
695 else if (pMachineWindow->isVisible())
696 {
697 LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
698 "Ask machine-window #%d to hide\n", (int)uScreenID));
699
700 /* Make sure window hidden: */
701 pMachineWindow->hide();
702 }
703 }
704 /* Validate window which is in fullscreen: */
705 else
706 {
707 /* Variables to compare: */
708 const int iWantedHostScreenIndex = hostScreenForGuestScreen((int)uScreenID);
709 const int iCurrentHostScreenIndex = UIDesktopWidgetWatchdog::screenNumber(pMachineWindow);
710 const QSize guestScreenSize = uimachine()->guestScreenSize(uScreenID);
711 const QSize hostScreenSize = gpDesktop->screenGeometry(iWantedHostScreenIndex).size();
712
713 /* If that window
714 * 1. shouldn't really be shown or
715 * 2. isn't mapped to some host-screen or
716 * 3. should be located on another host-screen than currently. */
717 if ( !uimachine()->isScreenVisible(uScreenID)
718 || !hasHostScreenForGuestScreen(uScreenID)
719 || iWantedHostScreenIndex != iCurrentHostScreenIndex)
720 {
721 LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
722 "Ask machine-window #%d to exit native fullscreen\n", (int)uScreenID));
723
724 /* Mark window as invalidated: */
725 m_invalidFullscreenMachineWindows << pMachineWindow;
726
727 /* Ask window to exit 'fullscreen' mode: */
728 emit sigNotifyAboutNativeFullscreenShouldBeExited(pMachineWindow);
729 return;
730 }
731
732 /* If that window
733 * 1. have another size than actually should. */
734 else if (guestScreenSize != hostScreenSize)
735 {
736 LogRel(("GUI: UIMachineLogicFullscreen::revalidateNativeFullScreen: "
737 "Ask machine-window #%d to adjust guest geometry\n", (int)uScreenID));
738
739 /* Just adjust machine-view size if necessary: */
740 pMachineWindow->adjustMachineViewSize();
741 return;
742 }
743 }
744 }
745}
746
747void UIMachineLogicFullscreen::revalidateNativeFullScreen()
748{
749 /* Revalidate all fullscreen windows: */
750 foreach (UIMachineWindow *pMachineWindow, machineWindows())
751 revalidateNativeFullScreen(pMachineWindow);
752}
753
754/* static */
755void UIMachineLogicFullscreen::nativeHandlerForApplicationActivation(QObject *pObject, const QMap<QString, QString> &userInfo)
756{
757 /* Handle arrived notification: */
758 UIMachineLogicFullscreen *pLogic = qobject_cast<UIMachineLogicFullscreen*>(pObject);
759 AssertPtrReturnVoid(pLogic);
760 {
761 /* Redirect arrived notification: */
762 pLogic->nativeHandlerForApplicationActivation(userInfo);
763 }
764}
765
766void UIMachineLogicFullscreen::nativeHandlerForApplicationActivation(const QMap<QString, QString> &userInfo)
767{
768 /* Make sure we have BundleIdentifier key: */
769 AssertReturnVoid(userInfo.contains("BundleIdentifier"));
770 /* Skip other applications: */
771 QStringList ourBundleIdentifiers;
772 ourBundleIdentifiers << "org.virtualbox.app.VirtualBox";
773 ourBundleIdentifiers << "org.virtualbox.app.VirtualBoxVM";
774 ourBundleIdentifiers << "com.citrix.DesktopPlayerVM";
775 if (!ourBundleIdentifiers.contains(userInfo.value("BundleIdentifier")))
776 return;
777
778 /* Skip if 'screen have separate spaces': */
779 if (screensHaveSeparateSpaces())
780 return;
781
782 /* Skip if there is another than needed user-space is active: */
783 if (!darwinIsOnActiveSpace(machineWindows().first()))
784 return;
785
786 LogRel(("GUI: UIMachineLogicFullscreen::nativeHandlerForApplicationActivation: "
787 "Full-screen application activated\n"));
788
789 /* Revalidate full-screen mode for transient machine-window(s): */
790 foreach (UIMachineWindow *pMachineWindow, machineWindows())
791 if (pMachineWindow->screenId() > 0)
792 revalidateNativeFullScreen(pMachineWindow);
793}
794
795/* static */
796void UIMachineLogicFullscreen::nativeHandlerForActiveSpaceChange(QObject *pObject, const QMap<QString, QString> &userInfo)
797{
798 /* Handle arrived notification: */
799 UIMachineLogicFullscreen *pLogic = qobject_cast<UIMachineLogicFullscreen*>(pObject);
800 AssertPtrReturnVoid(pLogic);
801 {
802 /* Redirect arrived notification: */
803 pLogic->nativeHandlerForActiveSpaceChange(userInfo);
804 }
805}
806
807void UIMachineLogicFullscreen::nativeHandlerForActiveSpaceChange(const QMap<QString, QString>&)
808{
809 /* Skip if 'screen have separate spaces': */
810 if (screensHaveSeparateSpaces())
811 return;
812
813 /* Skip if there is another than needed user-space is active: */
814 if (!darwinIsOnActiveSpace(machineWindows().first()))
815 return;
816
817 LogRel(("GUI: UIMachineLogicFullscreen::nativeHandlerForActiveSpaceChange: "
818 "Full-screen user-space activated\n"));
819
820 /* Revalidate full-screen mode for transient machine-window(s): */
821 foreach (UIMachineWindow *pMachineWindow, machineWindows())
822 if (pMachineWindow->screenId() > 0)
823 revalidateNativeFullScreen(pMachineWindow);
824}
825#endif /* VBOX_WS_MAC */
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