VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.cpp@ 82781

Last change on this file since 82781 was 81964, checked in by vboxsync, 5 years ago

Main/GraphicsAdapter: Split off a few attributes from Machine interface, which affects quite a few other interfaces.
Frontends/VirtualBox+VBoxManage+VBoxSDL+VBoxShell: Adapt accordingly.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.8 KB
Line 
1/* $Id: UIMachineWindow.cpp 81964 2019-11-18 20:42:02Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMachineWindow class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QCloseEvent>
20#include <QGridLayout>
21#include <QProcess>
22#include <QStyle>
23#include <QTimer>
24
25/* GUI includes: */
26#include "UICommon.h"
27#include "UIConverter.h"
28#include "UIModalWindowManager.h"
29#include "UIExtraDataManager.h"
30#include "UIMessageCenter.h"
31#include "UISession.h"
32#include "UIMachineLogic.h"
33#include "UIMachineWindow.h"
34#include "UIMachineWindowNormal.h"
35#include "UIMachineWindowFullscreen.h"
36#include "UIMachineWindowSeamless.h"
37#include "UIMachineWindowScale.h"
38#include "UIMachineView.h"
39#include "UIKeyboardHandler.h"
40#include "UIMouseHandler.h"
41#include "UIVMCloseDialog.h"
42#include "VBox2DHelpers.h"
43
44/* COM includes: */
45#include "CConsole.h"
46#include "CGraphicsAdapter.h"
47#include "CSnapshot.h"
48
49/* Other VBox includes: */
50#include <VBox/version.h>
51#ifdef VBOX_BLEEDING_EDGE
52# include <iprt/buildconfig.h>
53#endif /* VBOX_BLEEDING_EDGE */
54
55
56/* static */
57UIMachineWindow* UIMachineWindow::create(UIMachineLogic *pMachineLogic, ulong uScreenId)
58{
59 /* Create machine-window: */
60 UIMachineWindow *pMachineWindow = 0;
61 switch (pMachineLogic->visualStateType())
62 {
63 case UIVisualStateType_Normal:
64 pMachineWindow = new UIMachineWindowNormal(pMachineLogic, uScreenId);
65 break;
66 case UIVisualStateType_Fullscreen:
67 pMachineWindow = new UIMachineWindowFullscreen(pMachineLogic, uScreenId);
68 break;
69 case UIVisualStateType_Seamless:
70 pMachineWindow = new UIMachineWindowSeamless(pMachineLogic, uScreenId);
71 break;
72 case UIVisualStateType_Scale:
73 pMachineWindow = new UIMachineWindowScale(pMachineLogic, uScreenId);
74 break;
75 default:
76 AssertMsgFailed(("Incorrect visual state!"));
77 break;
78 }
79 /* Prepare machine-window: */
80 pMachineWindow->prepare();
81 /* Return machine-window: */
82 return pMachineWindow;
83}
84
85/* static */
86void UIMachineWindow::destroy(UIMachineWindow *pWhichWindow)
87{
88 /* Cleanup machine-window: */
89 pWhichWindow->cleanup();
90 /* Delete machine-window: */
91 delete pWhichWindow;
92}
93
94void UIMachineWindow::prepare()
95{
96 /* Prepare session-connections: */
97 prepareSessionConnections();
98
99 /* Prepare main-layout: */
100 prepareMainLayout();
101
102 /* Prepare menu: */
103 prepareMenu();
104
105 /* Prepare status-bar: */
106 prepareStatusBar();
107
108 /* Prepare visual-state: */
109 prepareVisualState();
110
111 /* Prepare machine-view: */
112 prepareMachineView();
113
114 /* Prepare handlers: */
115 prepareHandlers();
116
117 /* Load settings: */
118 loadSettings();
119
120 /* Retranslate window: */
121 retranslateUi();
122
123 /* Show (must be done before updating the appearance): */
124 showInNecessaryMode();
125
126 /* Update all the elements: */
127 updateAppearanceOf(UIVisualElement_AllStuff);
128
129#ifdef VBOX_WS_X11
130 /* Prepare default class/name values: */
131 const QString strWindowClass = QString("VirtualBox Machine");
132 QString strWindowName = strWindowClass;
133 /* Check if we want Window Manager to distinguish Virtual Machine windows: */
134 if (gEDataManager->distinguishMachineWindowGroups(uiCommon().managedVMUuid()))
135 strWindowName = QString("VirtualBox Machine UUID: %1").arg(uiCommon().managedVMUuid().toString());
136 /* Assign WM_CLASS property: */
137 UICommon::setWMClass(this, strWindowName, strWindowClass);
138#endif
139}
140
141void UIMachineWindow::cleanup()
142{
143 /* Save window settings: */
144 saveSettings();
145
146 /* Cleanup handlers: */
147 cleanupHandlers();
148
149 /* Cleanup visual-state: */
150 cleanupVisualState();
151
152 /* Cleanup machine-view: */
153 cleanupMachineView();
154
155 /* Cleanup status-bar: */
156 cleanupStatusBar();
157
158 /* Cleanup menu: */
159 cleanupMenu();
160
161 /* Cleanup main layout: */
162 cleanupMainLayout();
163
164 /* Cleanup session connections: */
165 cleanupSessionConnections();
166}
167
168void UIMachineWindow::sltMachineStateChanged()
169{
170 /* Update window-title: */
171 updateAppearanceOf(UIVisualElement_WindowTitle);
172}
173
174UIMachineWindow::UIMachineWindow(UIMachineLogic *pMachineLogic, ulong uScreenId)
175 : QIWithRetranslateUI2<QMainWindow>(0, pMachineLogic->windowFlags(uScreenId))
176 , m_pMachineLogic(pMachineLogic)
177 , m_pMachineView(0)
178 , m_uScreenId(uScreenId)
179 , m_pMainLayout(0)
180 , m_pTopSpacer(0)
181 , m_pBottomSpacer(0)
182 , m_pLeftSpacer(0)
183 , m_pRightSpacer(0)
184{
185#ifndef VBOX_WS_MAC
186 /* Set machine-window icon if any: */
187 // On macOS window icon is referenced in info.plist.
188 if (uisession() && uisession()->machineWindowIcon())
189 setWindowIcon(*uisession()->machineWindowIcon());
190#endif /* !VBOX_WS_MAC */
191}
192
193UIActionPool* UIMachineWindow::actionPool() const
194{
195 return machineLogic()->actionPool();
196}
197
198UISession* UIMachineWindow::uisession() const
199{
200 return machineLogic()->uisession();
201}
202
203CSession& UIMachineWindow::session() const
204{
205 return uisession()->session();
206}
207
208CMachine& UIMachineWindow::machine() const
209{
210 return uisession()->machine();
211}
212
213CConsole& UIMachineWindow::console() const
214{
215 return uisession()->console();
216}
217
218const QString& UIMachineWindow::machineName() const
219{
220 return uisession()->machineName();
221}
222
223void UIMachineWindow::adjustMachineViewSize()
224{
225 /* We need to adjust guest-screen size if necessary: */
226 machineView()->adjustGuestScreenSize();
227}
228
229void UIMachineWindow::sendMachineViewSizeHint()
230{
231 /* Send machine-view size-hint to the guest: */
232 machineView()->resendSizeHint();
233}
234
235#ifdef VBOX_WITH_MASKED_SEAMLESS
236void UIMachineWindow::setMask(const QRegion &region)
237{
238 /* Call to base-class: */
239 QMainWindow::setMask(region);
240}
241#endif /* VBOX_WITH_MASKED_SEAMLESS */
242
243void UIMachineWindow::retranslateUi()
244{
245 /* Compose window-title prefix: */
246 m_strWindowTitlePrefix = VBOX_PRODUCT;
247#ifdef VBOX_BLEEDING_EDGE
248 m_strWindowTitlePrefix += UIMachineWindow::tr(" EXPERIMENTAL build %1r%2 - %3")
249 .arg(RTBldCfgVersion())
250 .arg(RTBldCfgRevisionStr())
251 .arg(VBOX_BLEEDING_EDGE);
252#endif /* VBOX_BLEEDING_EDGE */
253 /* Update appearance of the window-title: */
254 updateAppearanceOf(UIVisualElement_WindowTitle);
255}
256
257bool UIMachineWindow::event(QEvent *pEvent)
258{
259 /* Call to base-class: */
260 const bool fResult = QIWithRetranslateUI2<QMainWindow>::event(pEvent);
261
262 /* Handle particular events: */
263 switch (pEvent->type())
264 {
265 case QEvent::WindowActivate:
266 {
267 /* Initiate registration in the modal window manager: */
268 windowManager().setMainWindowShown(this);
269 break;
270 }
271 default:
272 break;
273 }
274
275 /* Return result: */
276 return fResult;
277}
278
279void UIMachineWindow::showEvent(QShowEvent *pEvent)
280{
281 /* Call to base-class: */
282 QMainWindow::showEvent(pEvent);
283
284 /* Initiate registration in the modal window manager: */
285 windowManager().setMainWindowShown(this);
286
287 /* Update appearance for indicator-pool: */
288 updateAppearanceOf(UIVisualElement_IndicatorPoolStuff);
289}
290
291void UIMachineWindow::hideEvent(QHideEvent *pEvent)
292{
293 /* Update registration in the modal window manager: */
294 if (windowManager().mainWindowShown() == this)
295 {
296 if (machineLogic()->activeMachineWindow())
297 windowManager().setMainWindowShown(machineLogic()->activeMachineWindow());
298 else
299 windowManager().setMainWindowShown(machineLogic()->mainMachineWindow());
300 }
301
302 /* Call to base-class: */
303 QMainWindow::hideEvent(pEvent);
304}
305
306void UIMachineWindow::closeEvent(QCloseEvent *pCloseEvent)
307{
308 /* Always ignore close-event first: */
309 pCloseEvent->ignore();
310
311 /* Make sure machine is in one of the allowed states: */
312 if (!uisession()->isRunning() && !uisession()->isPaused() && !uisession()->isStuck())
313 return;
314
315 /* If there is a close hook script defined: */
316 const QString strScript = gEDataManager->machineCloseHookScript(uiCommon().managedVMUuid());
317 if (!strScript.isEmpty())
318 {
319 /* Execute asynchronously and leave: */
320 QProcess::startDetached(strScript, QStringList() << machine().GetId().toString());
321 return;
322 }
323
324 /* Choose the close action: */
325 MachineCloseAction closeAction = MachineCloseAction_Invalid;
326
327 /* If default close-action defined and not restricted: */
328 MachineCloseAction defaultCloseAction = uisession()->defaultCloseAction();
329 MachineCloseAction restrictedCloseActions = uisession()->restrictedCloseActions();
330 if ((defaultCloseAction != MachineCloseAction_Invalid) &&
331 !(restrictedCloseActions & defaultCloseAction))
332 {
333 switch (defaultCloseAction)
334 {
335 /* If VM is stuck, and the default close-action is 'detach', 'save-state' or 'shutdown',
336 * we should ask the user about what to do: */
337 case MachineCloseAction_Detach:
338 case MachineCloseAction_SaveState:
339 case MachineCloseAction_Shutdown:
340 closeAction = uisession()->isStuck() ? MachineCloseAction_Invalid : defaultCloseAction;
341 break;
342 /* Otherwise we just use what we have: */
343 default:
344 closeAction = defaultCloseAction;
345 break;
346 }
347 }
348
349 /* If the close-action still undefined: */
350 if (closeAction == MachineCloseAction_Invalid)
351 {
352 /* Prepare close-dialog: */
353 QWidget *pParentDlg = windowManager().realParentWindow(this);
354 QPointer<UIVMCloseDialog> pCloseDlg = new UIVMCloseDialog(pParentDlg, machine(),
355 console().GetGuestEnteredACPIMode(),
356 restrictedCloseActions);
357 /* Configure close-dialog: */
358 if (uisession() && uisession()->machineWindowIcon())
359 pCloseDlg->setIcon(*uisession()->machineWindowIcon());
360
361 /* Make sure close-dialog is valid: */
362 if (pCloseDlg->isValid())
363 {
364 /* We are going to show close-dialog: */
365 bool fShowCloseDialog = true;
366 /* Check if VM is paused or stuck: */
367 const bool fWasPaused = uisession()->isPaused();
368 const bool fIsStuck = uisession()->isStuck();
369 /* If VM is NOT paused and NOT stuck: */
370 if (!fWasPaused && !fIsStuck)
371 {
372 /* We should pause it first: */
373 const bool fIsPaused = uisession()->pause();
374 /* If we were unable to pause VM: */
375 if (!fIsPaused)
376 {
377 /* If that is NOT the separate VM process UI: */
378 if (!uiCommon().isSeparateProcess())
379 {
380 /* We are not going to show close-dialog: */
381 fShowCloseDialog = false;
382 }
383 /* If that is the separate VM process UI: */
384 else
385 {
386 /* We are going to show close-dialog only
387 * if headless frontend stopped/killed already: */
388 CMachine machine = uisession()->machine();
389 KMachineState machineState = machine.GetState();
390 fShowCloseDialog = !machine.isOk() || machineState == KMachineState_Null;
391 }
392 }
393 }
394 /* If we are going to show close-dialog: */
395 if (fShowCloseDialog)
396 {
397 /* Show close-dialog to let the user make the choice: */
398 windowManager().registerNewParent(pCloseDlg, pParentDlg);
399 closeAction = static_cast<MachineCloseAction>(pCloseDlg->exec());
400
401 /* Make sure the dialog still valid: */
402 if (!pCloseDlg)
403 return;
404
405 /* If VM was not paused before but paused now,
406 * we should resume it if user canceled dialog or chosen shutdown: */
407 if (!fWasPaused && uisession()->isPaused() &&
408 (closeAction == MachineCloseAction_Invalid ||
409 closeAction == MachineCloseAction_Detach ||
410 closeAction == MachineCloseAction_Shutdown))
411 {
412 /* If we unable to resume VM, cancel closing: */
413 if (!uisession()->unpause())
414 closeAction = MachineCloseAction_Invalid;
415 }
416 }
417 }
418 else
419 {
420 /* Else user misconfigured .vbox file, we will reject closing UI: */
421 closeAction = MachineCloseAction_Invalid;
422 }
423
424 /* Cleanup close-dialog: */
425 delete pCloseDlg;
426 }
427
428 /* Depending on chosen result: */
429 switch (closeAction)
430 {
431 case MachineCloseAction_Detach:
432 {
433 /* Just close Runtime UI: */
434 LogRel(("GUI: Request for close-action to detach GUI.\n"));
435 machineLogic()->detach();
436 break;
437 }
438 case MachineCloseAction_SaveState:
439 {
440 /* Save VM state: */
441 LogRel(("GUI: Request for close-action to save VM state.\n"));
442 machineLogic()->saveState();
443 break;
444 }
445 case MachineCloseAction_Shutdown:
446 {
447 /* Shutdown VM: */
448 LogRel(("GUI: Request for close-action to shutdown VM.\n"));
449 machineLogic()->shutdown();
450 break;
451 }
452 case MachineCloseAction_PowerOff:
453 case MachineCloseAction_PowerOff_RestoringSnapshot:
454 {
455 /* Power VM off: */
456 LogRel(("GUI: Request for close-action to power VM off.\n"));
457 machineLogic()->powerOff(closeAction == MachineCloseAction_PowerOff_RestoringSnapshot);
458 break;
459 }
460 default:
461 break;
462 }
463}
464
465void UIMachineWindow::prepareSessionConnections()
466{
467 /* We should watch for console events: */
468 connect(uisession(), &UISession::sigMachineStateChange, this, &UIMachineWindow::sltMachineStateChanged);
469}
470
471void UIMachineWindow::prepareMainLayout()
472{
473 /* Create central-widget: */
474 setCentralWidget(new QWidget);
475
476 /* Create main-layout: */
477 m_pMainLayout = new QGridLayout(centralWidget());
478 m_pMainLayout->setMargin(0);
479 m_pMainLayout->setSpacing(0);
480
481 /* Create shifting-spacers: */
482 m_pTopSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
483 m_pBottomSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
484 m_pLeftSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed);
485 m_pRightSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed);
486
487 /* Add shifting-spacers into main-layout: */
488 m_pMainLayout->addItem(m_pTopSpacer, 0, 1);
489 m_pMainLayout->addItem(m_pBottomSpacer, 2, 1);
490 m_pMainLayout->addItem(m_pLeftSpacer, 1, 0);
491 m_pMainLayout->addItem(m_pRightSpacer, 1, 2);
492}
493
494void UIMachineWindow::prepareMachineView()
495{
496#ifdef VBOX_WITH_VIDEOHWACCEL
497 /* Need to force the QGL framebuffer in case 2D Video Acceleration is supported & enabled: */
498 bool bAccelerate2DVideo = machine().GetGraphicsAdapter().GetAccelerate2DVideoEnabled() && VBox2DHelpers::isAcceleration2DVideoAvailable();
499#endif /* VBOX_WITH_VIDEOHWACCEL */
500
501 /* Get visual-state type: */
502 UIVisualStateType visualStateType = machineLogic()->visualStateType();
503
504 /* Create machine-view: */
505 m_pMachineView = UIMachineView::create( this
506 , m_uScreenId
507 , visualStateType
508#ifdef VBOX_WITH_VIDEOHWACCEL
509 , bAccelerate2DVideo
510#endif /* VBOX_WITH_VIDEOHWACCEL */
511 );
512
513 /* Listen for frame-buffer resize: */
514 connect(m_pMachineView, &UIMachineView::sigFrameBufferResize, this, &UIMachineWindow::sigFrameBufferResize);
515
516 /* Add machine-view into main-layout: */
517 m_pMainLayout->addWidget(m_pMachineView, 1, 1, viewAlignment(visualStateType));
518
519 /* Install focus-proxy: */
520 setFocusProxy(m_pMachineView);
521}
522
523void UIMachineWindow::prepareHandlers()
524{
525 /* Register keyboard-handler: */
526 machineLogic()->keyboardHandler()->prepareListener(m_uScreenId, this);
527
528 /* Register mouse-handler: */
529 machineLogic()->mouseHandler()->prepareListener(m_uScreenId, this);
530}
531
532void UIMachineWindow::cleanupHandlers()
533{
534 /* Unregister mouse-handler: */
535 machineLogic()->mouseHandler()->cleanupListener(m_uScreenId);
536
537 /* Unregister keyboard-handler: */
538 machineLogic()->keyboardHandler()->cleanupListener(m_uScreenId);
539}
540
541void UIMachineWindow::cleanupMachineView()
542{
543 /* Destroy machine-view: */
544 UIMachineView::destroy(m_pMachineView);
545 m_pMachineView = 0;
546}
547
548void UIMachineWindow::cleanupSessionConnections()
549{
550 /* We should stop watching for console events: */
551 disconnect(uisession(), &UISession::sigMachineStateChange, this, &UIMachineWindow::sltMachineStateChanged);
552}
553
554void UIMachineWindow::updateAppearanceOf(int iElement)
555{
556 /* Update window title: */
557 if (iElement & UIVisualElement_WindowTitle)
558 {
559 /* Get machine state: */
560 KMachineState state = uisession()->machineState();
561 /* Prepare full name: */
562 QString strSnapshotName;
563 if (machine().GetSnapshotCount() > 0)
564 {
565 CSnapshot snapshot = machine().GetCurrentSnapshot();
566 strSnapshotName = " (" + snapshot.GetName() + ")";
567 }
568 QString strMachineName = machineName() + strSnapshotName;
569 if (state != KMachineState_Null)
570 strMachineName += " [" + gpConverter->toString(state) + "]";
571 /* Unusual on the Mac. */
572#ifndef VBOX_WS_MAC
573 const QString strUserProductName = uisession()->machineWindowNamePostfix();
574 strMachineName += " - " + (strUserProductName.isEmpty() ? defaultWindowTitle() : strUserProductName);
575#endif /* !VBOX_WS_MAC */
576 if (machine().GetGraphicsAdapter().GetMonitorCount() > 1)
577 strMachineName += QString(" : %1").arg(m_uScreenId + 1);
578 setWindowTitle(strMachineName);
579 }
580}
581
582#ifdef VBOX_WITH_DEBUGGER_GUI
583void UIMachineWindow::updateDbgWindows()
584{
585 /* The debugger windows are bind to the main VM window. */
586 if (m_uScreenId == 0)
587 machineLogic()->dbgAdjustRelativePos();
588}
589#endif /* VBOX_WITH_DEBUGGER_GUI */
590
591/* static */
592Qt::Alignment UIMachineWindow::viewAlignment(UIVisualStateType visualStateType)
593{
594 switch (visualStateType)
595 {
596 case UIVisualStateType_Normal: return 0;
597 case UIVisualStateType_Fullscreen: return Qt::AlignVCenter | Qt::AlignHCenter;
598 case UIVisualStateType_Seamless: return 0;
599 case UIVisualStateType_Scale: return 0;
600 case UIVisualStateType_Invalid: case UIVisualStateType_All: break; /* Shut up, MSC! */
601 }
602 AssertMsgFailed(("Incorrect visual state!"));
603 return 0;
604}
605
606#ifdef VBOX_WS_MAC
607void UIMachineWindow::handleStandardWindowButtonCallback(StandardWindowButtonType enmButtonType, bool fWithOptionKey)
608{
609 switch (enmButtonType)
610 {
611 case StandardWindowButtonType_Zoom:
612 {
613 /* Handle 'Zoom' button for 'Normal' and 'Scaled' modes: */
614 if ( machineLogic()->visualStateType() == UIVisualStateType_Normal
615 || machineLogic()->visualStateType() == UIVisualStateType_Scale)
616 {
617 if (fWithOptionKey)
618 {
619 /* Toggle window zoom: */
620 darwinToggleWindowZoom(this);
621 }
622 else
623 {
624 /* Enter 'full-screen' mode: */
625 uisession()->setRequestedVisualState(UIVisualStateType_Invalid);
626 uisession()->changeVisualState(UIVisualStateType_Fullscreen);
627 }
628 }
629 break;
630 }
631 default:
632 break;
633 }
634}
635
636/* static */
637void UIMachineWindow::handleNativeNotification(const QString &strNativeNotificationName, QWidget *pWidget)
638{
639 /* Handle arrived notification: */
640 LogRel(("GUI: UIMachineWindow::handleNativeNotification: Notification '%s' received\n",
641 strNativeNotificationName.toLatin1().constData()));
642 AssertPtrReturnVoid(pWidget);
643 if (UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(pWidget))
644 {
645 /* Redirect arrived notification: */
646 LogRel2(("UIMachineWindow::handleNativeNotification: Redirecting '%s' notification to corresponding machine-window...\n",
647 strNativeNotificationName.toLatin1().constData()));
648 pMachineWindow->handleNativeNotification(strNativeNotificationName);
649 }
650}
651
652/* static */
653void UIMachineWindow::handleStandardWindowButtonCallback(StandardWindowButtonType enmButtonType, bool fWithOptionKey, QWidget *pWidget)
654{
655 /* Handle arrived callback: */
656 LogRel(("GUI: UIMachineWindow::handleStandardWindowButtonCallback: Callback for standard window button '%d' with option key '%d' received\n",
657 (int)enmButtonType, (int)fWithOptionKey));
658 AssertPtrReturnVoid(pWidget);
659 if (UIMachineWindow *pMachineWindow = qobject_cast<UIMachineWindow*>(pWidget))
660 {
661 /* Redirect arrived callback: */
662 LogRel2(("UIMachineWindow::handleStandardWindowButtonCallback: Redirecting callback for standard window button '%d' with option key '%d' to corresponding machine-window...\n",
663 (int)enmButtonType, (int)fWithOptionKey));
664 pMachineWindow->handleStandardWindowButtonCallback(enmButtonType, fWithOptionKey);
665 }
666}
667#endif /* VBOX_WS_MAC */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use