summaryrefslogtreecommitdiffstats
path: root/src/VBox/Frontends/VirtualBox/src/platform
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Frontends/VirtualBox/src/platform
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Frontends/VirtualBox/src/platform')
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/UIDesktopServices.h51
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.h65
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.mm366
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp2197
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.h96
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/DockIconPreview.h49
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/Info.plist68
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/PkgInfo1
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.cpp144
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.h86
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.h135
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.mm494
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.h56
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.mm342
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.h99
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm179
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin.cpp48
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_cocoa.mm74
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_p.h47
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp372
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.h98
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxCocoaHelper.h73
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm730
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.cpp777
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.h319
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-Info.plist22
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-PkgInfo1
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/tstDarwinKeyboard.cpp108
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-Info.plist30
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-PkgInfo1
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter.mm140
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.asm71
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.cpp198
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.h49
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/UIDesktopServices_win.cpp88
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.cpp128
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.h57
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/VirtualBox.VisualElementsManifest.xml9
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp310
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.h102
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/Makefile.kup0
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/UIDesktopServices_x11.cpp86
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.cpp679
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.h140
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard-new.cpp289
-rw-r--r--src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard.h55
49 files changed, 9529 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/platform/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/UIDesktopServices.h b/src/VBox/Frontends/VirtualBox/src/platform/UIDesktopServices.h
new file mode 100644
index 00000000..95bfa6a0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/UIDesktopServices.h
@@ -0,0 +1,51 @@
+/* $Id: UIDesktopServices.h $ */
+/** @file
+ * VBox Qt GUI - Desktop Services..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_UIDesktopServices_h
+#define FEQT_INCLUDED_SRC_platform_UIDesktopServices_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes */
+#include <QUuid>
+
+/** Name of the executable (image) used to start VMs. */
+#define VBOX_GUI_VMRUNNER_IMAGE "VirtualBoxVM"
+
+/* Qt forward declarations */
+class QString;
+
+class UIDesktopServices
+{
+public:
+ static bool createMachineShortcut(const QString &strSrcFile, const QString &strDstPath, const QString &strName, const QUuid &uUuid);
+ static bool openInFileManager(const QString &strFile);
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_UIDesktopServices_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.h
new file mode 100644
index 00000000..bbf8abcb
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.h
@@ -0,0 +1,65 @@
+/* $Id: CocoaEventHelper.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Darwin Cocoa specific event-handling tasks.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_CocoaEventHelper_h
+#define FEQT_INCLUDED_SRC_platform_darwin_CocoaEventHelper_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+#include <VBox/VBoxCocoa.h>
+
+/* Cocoa declarations: */
+ADD_COCOA_NATIVE_REF(NSEvent);
+
+
+RT_C_DECLS_BEGIN
+
+/** Calls the -(NSUInteger)modifierFlags method on @a pEvent object and converts the flags to carbon style. */
+uint32_t darwinEventModifierFlagsXlated(ConstNativeNSEventRef pEvent);
+
+/** Get the name for a Cocoa @a enmEventType. */
+const char *darwinEventTypeName(unsigned long enmEventType);
+
+/** Debug helper function for dumping a Cocoa event to stdout.
+ * @param pszPrefix Brings the message prefix.
+ * @param pEvent Brings the Cocoa event. */
+void darwinPrintEvent(const char *pszPrefix, ConstNativeNSEventRef pEvent);
+
+/** Posts stripped mouse event based on passed @a pEvent. */
+SHARED_LIBRARY_STUFF void darwinPostStrippedMouseEvent(ConstNativeNSEventRef pEvent);
+
+RT_C_DECLS_END
+
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_CocoaEventHelper_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.mm
new file mode 100644
index 00000000..09152b37
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/CocoaEventHelper.mm
@@ -0,0 +1,366 @@
+/* $Id: CocoaEventHelper.mm $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Darwin Cocoa specific event handling tasks.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "CocoaEventHelper.h"
+#include "DarwinKeyboard.h"
+
+/* External includes: */
+#import <Cocoa/Cocoa.h>
+#import <AppKit/NSEvent.h>
+#include <Carbon/Carbon.h>
+
+/* They just had to rename a whole load of constants in 10.12. Just wrap the carp up
+ in some defines for now as we need to keep building with both 10.9 and 10.13+: */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+# define VBOX_NSAlphaShiftKeyMask NSEventModifierFlagCapsLock
+# define VBOX_NSAlternateKeyMask NSEventModifierFlagOption
+# define VBOX_NSAppKitDefined NSEventTypeAppKitDefined
+# define VBOX_NSApplicationDefined NSEventTypeApplicationDefined
+# define VBOX_NSCommandKeyMask NSEventModifierFlagCommand
+# define VBOX_NSControlKeyMask NSEventModifierFlagControl
+# define VBOX_NSCursorUpdate NSEventTypeCursorUpdate
+# define VBOX_NSFlagsChanged NSEventTypeFlagsChanged
+# define VBOX_NSFunctionKeyMask NSEventModifierFlagFunction
+# define VBOX_NSHelpKeyMask NSEventModifierFlagHelp
+# define VBOX_NSKeyDown NSEventTypeKeyDown
+# define VBOX_NSKeyUp NSEventTypeKeyUp
+# define VBOX_NSLeftMouseDown NSEventTypeLeftMouseDown
+# define VBOX_NSLeftMouseDragged NSEventTypeLeftMouseDragged
+# define VBOX_NSLeftMouseUp NSEventTypeLeftMouseUp
+# define VBOX_NSMouseEntered NSEventTypeMouseEntered
+# define VBOX_NSMouseExited NSEventTypeMouseExited
+# define VBOX_NSMouseMoved NSEventTypeMouseMoved
+# define VBOX_NSNumericPadKeyMask NSEventModifierFlagNumericPad
+# define VBOX_NSOtherMouseDown NSEventTypeOtherMouseDown
+# define VBOX_NSOtherMouseDragged NSEventTypeOtherMouseDragged
+# define VBOX_NSOtherMouseUp NSEventTypeOtherMouseUp
+# define VBOX_NSPeriodic NSEventTypePeriodic
+# define VBOX_NSRightMouseDown NSEventTypeRightMouseDown
+# define VBOX_NSRightMouseDragged NSEventTypeRightMouseDragged
+# define VBOX_NSRightMouseUp NSEventTypeRightMouseUp
+# define VBOX_NSScrollWheel NSEventTypeScrollWheel
+# define VBOX_NSShiftKeyMask NSEventModifierFlagShift
+# define VBOX_NSSystemDefined NSEventTypeSystemDefined
+# define VBOX_NSTabletPoint NSEventTypeTabletPoint
+# define VBOX_NSTabletProximity NSEventTypeTabletProximity
+#else
+# define VBOX_NSAlphaShiftKeyMask NSAlphaShiftKeyMask
+# define VBOX_NSAlternateKeyMask NSAlternateKeyMask
+# define VBOX_NSAppKitDefined NSAppKitDefined
+# define VBOX_NSApplicationDefined NSApplicationDefined
+# define VBOX_NSCommandKeyMask NSCommandKeyMask
+# define VBOX_NSControlKeyMask NSControlKeyMask
+# define VBOX_NSCursorUpdate NSCursorUpdate
+# define VBOX_NSFlagsChanged NSFlagsChanged
+# define VBOX_NSFunctionKeyMask NSFunctionKeyMask
+# define VBOX_NSHelpKeyMask NSHelpKeyMask
+# define VBOX_NSKeyDown NSKeyDown
+# define VBOX_NSKeyUp NSKeyUp
+# define VBOX_NSLeftMouseDown NSLeftMouseDown
+# define VBOX_NSLeftMouseDragged NSLeftMouseDragged
+# define VBOX_NSLeftMouseUp NSLeftMouseUp
+# define VBOX_NSMouseEntered NSMouseEntered
+# define VBOX_NSMouseExited NSMouseExited
+# define VBOX_NSMouseMoved NSMouseMoved
+# define VBOX_NSNumericPadKeyMask NSNumericPadKeyMask
+# define VBOX_NSOtherMouseDown NSOtherMouseDown
+# define VBOX_NSOtherMouseDragged NSOtherMouseDragged
+# define VBOX_NSOtherMouseUp NSOtherMouseUp
+# define VBOX_NSPeriodic NSPeriodic
+# define VBOX_NSRightMouseDown NSRightMouseDown
+# define VBOX_NSRightMouseDragged NSRightMouseDragged
+# define VBOX_NSRightMouseUp NSRightMouseUp
+# define VBOX_NSScrollWheel NSScrollWheel
+# define VBOX_NSShiftKeyMask NSShiftKeyMask
+# define VBOX_NSSystemDefined NSSystemDefined
+# define VBOX_NSTabletPoint NSTabletPoint
+# define VBOX_NSTabletProximity NSTabletProximity
+#endif
+
+uint32_t darwinEventModifierFlagsXlated(ConstNativeNSEventRef pEvent)
+{
+ NSUInteger fCocoa = [pEvent modifierFlags];
+ uint32_t fCarbon = 0;
+ if (fCocoa)
+ {
+ if (fCocoa & VBOX_NSAlphaShiftKeyMask)
+ fCarbon |= alphaLock;
+ if (fCocoa & (VBOX_NSShiftKeyMask | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
+ {
+ if (fCocoa & (NX_DEVICERSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
+ {
+ if (fCocoa & NX_DEVICERSHIFTKEYMASK)
+ fCarbon |= rightShiftKey;
+ if (fCocoa & NX_DEVICELSHIFTKEYMASK)
+ fCarbon |= shiftKey;
+ }
+ else
+ fCarbon |= shiftKey;
+ }
+
+ if (fCocoa & (VBOX_NSControlKeyMask | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
+ {
+ if (fCocoa & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
+ {
+ if (fCocoa & NX_DEVICERCTLKEYMASK)
+ fCarbon |= rightControlKey;
+ if (fCocoa & NX_DEVICELCTLKEYMASK)
+ fCarbon |= controlKey;
+ }
+ else
+ fCarbon |= controlKey;
+ }
+
+ if (fCocoa & (VBOX_NSAlternateKeyMask | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
+ {
+ if (fCocoa & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
+ {
+ if (fCocoa & NX_DEVICERALTKEYMASK)
+ fCarbon |= rightOptionKey;
+ if (fCocoa & NX_DEVICELALTKEYMASK)
+ fCarbon |= optionKey;
+ }
+ else
+ fCarbon |= optionKey;
+ }
+
+ if (fCocoa & (VBOX_NSCommandKeyMask | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
+ {
+ if (fCocoa & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
+ {
+ if (fCocoa & NX_DEVICERCMDKEYMASK)
+ fCarbon |= kEventKeyModifierRightCmdKeyMask;
+ if (fCocoa & NX_DEVICELCMDKEYMASK)
+ fCarbon |= cmdKey;
+ }
+ else
+ fCarbon |= cmdKey;
+ }
+
+ /*
+ if (fCocoa & VBOX_NSNumericPadKeyMask)
+ fCarbon |= ???;
+
+ if (fCocoa & VBOX_NSHelpKeyMask)
+ fCarbon |= ???;
+
+ if (fCocoa & VBOX_NSFunctionKeyMask)
+ fCarbon |= ???;
+ */
+ }
+
+ return fCarbon;
+}
+
+const char *darwinEventTypeName(unsigned long enmEventType)
+{
+ switch (enmEventType)
+ {
+#define EVT_CASE(nm) case nm: return #nm
+ EVT_CASE(VBOX_NSLeftMouseDown);
+ EVT_CASE(VBOX_NSLeftMouseUp);
+ EVT_CASE(VBOX_NSRightMouseDown);
+ EVT_CASE(VBOX_NSRightMouseUp);
+ EVT_CASE(VBOX_NSMouseMoved);
+ EVT_CASE(VBOX_NSLeftMouseDragged);
+ EVT_CASE(VBOX_NSRightMouseDragged);
+ EVT_CASE(VBOX_NSMouseEntered);
+ EVT_CASE(VBOX_NSMouseExited);
+ EVT_CASE(VBOX_NSKeyDown);
+ EVT_CASE(VBOX_NSKeyUp);
+ EVT_CASE(VBOX_NSFlagsChanged);
+ EVT_CASE(VBOX_NSAppKitDefined);
+ EVT_CASE(VBOX_NSSystemDefined);
+ EVT_CASE(VBOX_NSApplicationDefined);
+ EVT_CASE(VBOX_NSPeriodic);
+ EVT_CASE(VBOX_NSCursorUpdate);
+ EVT_CASE(VBOX_NSScrollWheel);
+ EVT_CASE(VBOX_NSTabletPoint);
+ EVT_CASE(VBOX_NSTabletProximity);
+ EVT_CASE(VBOX_NSOtherMouseDown);
+ EVT_CASE(VBOX_NSOtherMouseUp);
+ EVT_CASE(VBOX_NSOtherMouseDragged);
+#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
+ EVT_CASE(NSEventTypeGesture);
+ EVT_CASE(NSEventTypeMagnify);
+ EVT_CASE(NSEventTypeSwipe);
+ EVT_CASE(NSEventTypeRotate);
+ EVT_CASE(NSEventTypeBeginGesture);
+ EVT_CASE(NSEventTypeEndGesture);
+#endif
+#undef EVT_CASE
+ default:
+ return "Unknown!";
+ }
+}
+
+void darwinPrintEvent(const char *pszPrefix, ConstNativeNSEventRef pEvent)
+{
+ NSEventType enmEventType = [pEvent type];
+ NSUInteger fEventMask = [pEvent modifierFlags];
+ NSWindow *pEventWindow = [pEvent window];
+ NSInteger iEventWindow = [pEvent windowNumber];
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ NSGraphicsContext *pEventGraphicsContext = nil; /* NSEvent::context is deprecated and said to always return nil. */
+#else
+ NSGraphicsContext *pEventGraphicsContext = [pEvent context];
+#endif
+
+ printf("%s%p: Type=%lu Modifiers=%08lx pWindow=%p #Wnd=%ld pGraphCtx=%p %s\n",
+ pszPrefix, (void*)pEvent, (unsigned long)enmEventType, (unsigned long)fEventMask, (void*)pEventWindow,
+ (long)iEventWindow, (void*)pEventGraphicsContext, darwinEventTypeName(enmEventType));
+
+ /* Dump type specific info: */
+ switch (enmEventType)
+ {
+ case VBOX_NSLeftMouseDown:
+ case VBOX_NSLeftMouseUp:
+ case VBOX_NSRightMouseDown:
+ case VBOX_NSRightMouseUp:
+ case VBOX_NSMouseMoved:
+
+ case VBOX_NSLeftMouseDragged:
+ case VBOX_NSRightMouseDragged:
+ case VBOX_NSMouseEntered:
+ case VBOX_NSMouseExited:
+ break;
+
+ case VBOX_NSKeyDown:
+ case VBOX_NSKeyUp:
+ {
+ NSUInteger i;
+ NSUInteger cch;
+ NSString *pChars = [pEvent characters];
+ NSString *pCharsIgnMod = [pEvent charactersIgnoringModifiers];
+ BOOL fIsARepeat = [pEvent isARepeat];
+ unsigned short KeyCode = [pEvent keyCode];
+
+ printf(" KeyCode=%04x isARepeat=%d", KeyCode, fIsARepeat);
+ if (pChars)
+ {
+ cch = [pChars length];
+ printf(" characters={");
+ for (i = 0; i < cch; i++)
+ printf(i == 0 ? "%02x" : ",%02x", [pChars characterAtIndex: i]);
+ printf("}");
+ }
+
+ if (pCharsIgnMod)
+ {
+ cch = [pCharsIgnMod length];
+ printf(" charactersIgnoringModifiers={");
+ for (i = 0; i < cch; i++)
+ printf(i == 0 ? "%02x" : ",%02x", [pCharsIgnMod characterAtIndex: i]);
+ printf("}");
+ }
+ printf("\n");
+ break;
+ }
+
+ case VBOX_NSFlagsChanged:
+ {
+ NSUInteger fOddBits = VBOX_NSAlphaShiftKeyMask | VBOX_NSShiftKeyMask | VBOX_NSControlKeyMask | VBOX_NSAlternateKeyMask
+ | VBOX_NSCommandKeyMask | VBOX_NSNumericPadKeyMask | VBOX_NSHelpKeyMask | VBOX_NSFunctionKeyMask
+ | NX_DEVICELCTLKEYMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK
+ | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK | NX_DEVICELALTKEYMASK
+ | NX_DEVICERALTKEYMASK | NX_DEVICERCTLKEYMASK;
+
+ printf(" KeyCode=%04x", (int)[pEvent keyCode]);
+#define PRINT_MOD(cnst, nm) do { if (fEventMask & (cnst)) printf(" %s", #nm); } while (0)
+ /* device-independent: */
+ PRINT_MOD(VBOX_NSAlphaShiftKeyMask, "AlphaShift");
+ PRINT_MOD(VBOX_NSShiftKeyMask, "Shift");
+ PRINT_MOD(VBOX_NSControlKeyMask, "Ctrl");
+ PRINT_MOD(VBOX_NSAlternateKeyMask, "Alt");
+ PRINT_MOD(VBOX_NSCommandKeyMask, "Cmd");
+ PRINT_MOD(VBOX_NSNumericPadKeyMask, "NumLock");
+ PRINT_MOD(VBOX_NSHelpKeyMask, "Help");
+ PRINT_MOD(VBOX_NSFunctionKeyMask, "Fn");
+ /* device-dependent (sort of): */
+ PRINT_MOD(NX_DEVICELCTLKEYMASK, "$L-Ctrl");
+ PRINT_MOD(NX_DEVICELSHIFTKEYMASK, "$L-Shift");
+ PRINT_MOD(NX_DEVICERSHIFTKEYMASK, "$R-Shift");
+ PRINT_MOD(NX_DEVICELCMDKEYMASK, "$L-Cmd");
+ PRINT_MOD(NX_DEVICERCMDKEYMASK, "$R-Cmd");
+ PRINT_MOD(NX_DEVICELALTKEYMASK, "$L-Alt");
+ PRINT_MOD(NX_DEVICERALTKEYMASK, "$R-Alt");
+ PRINT_MOD(NX_DEVICERCTLKEYMASK, "$R-Ctrl");
+#undef PRINT_MOD
+
+ fOddBits = fEventMask & ~fOddBits;
+ if (fOddBits)
+ printf(" fOddBits=%#08lx", (unsigned long)fOddBits);
+#undef KNOWN_BITS
+ printf("\n");
+ break;
+ }
+
+ case VBOX_NSAppKitDefined:
+ case VBOX_NSSystemDefined:
+ case VBOX_NSApplicationDefined:
+ case VBOX_NSPeriodic:
+ case VBOX_NSCursorUpdate:
+ case VBOX_NSScrollWheel:
+ case VBOX_NSTabletPoint:
+ case VBOX_NSTabletProximity:
+ case VBOX_NSOtherMouseDown:
+ case VBOX_NSOtherMouseUp:
+ case VBOX_NSOtherMouseDragged:
+#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
+ case NSEventTypeGesture:
+ case NSEventTypeMagnify:
+ case NSEventTypeSwipe:
+ case NSEventTypeRotate:
+ case NSEventTypeBeginGesture:
+ case NSEventTypeEndGesture:
+#endif
+ default:
+ printf(" Unknown!\n");
+ break;
+ }
+}
+
+void darwinPostStrippedMouseEvent(ConstNativeNSEventRef pEvent)
+{
+ /* Create and post new stripped event: */
+ NSEvent *pNewEvent = [NSEvent mouseEventWithType:[pEvent type]
+ location:[pEvent locationInWindow]
+ modifierFlags:0
+ timestamp:[pEvent timestamp] // [NSDate timeIntervalSinceReferenceDate] ?
+ windowNumber:[pEvent windowNumber]
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ context:nil /* NSEvent::context is deprecated and said to always return nil. */
+#else
+ context:[pEvent context]
+#endif
+ eventNumber:[pEvent eventNumber]
+ clickCount:[pEvent clickCount]
+ pressure:[pEvent pressure]];
+ [NSApp postEvent:pNewEvent atStart:YES];
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp
new file mode 100644
index 00000000..4f2e2971
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp
@@ -0,0 +1,2197 @@
+/* $Id: DarwinKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Darwin Keyboard specific tasks.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Defines: */
+#define LOG_GROUP LOG_GROUP_GUI
+#define VBOX_WITH_KBD_LEDS_SYNC
+//#define VBOX_WITHOUT_KBD_LEDS_SYNC_FILTERING
+
+/* GUI includes: */
+#include "DarwinKeyboard.h"
+#ifndef USE_HID_FOR_MODIFIERS
+# include "CocoaEventHelper.h"
+#endif
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#include <VBox/log.h>
+#ifdef DEBUG_PRINTF
+# include <iprt/stream.h>
+#endif
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+# include <iprt/errcore.h>
+# include <iprt/semaphore.h>
+# include <VBox/sup.h>
+#endif
+
+/* External includes: */
+#include <ApplicationServices/ApplicationServices.h>
+#include <Carbon/Carbon.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDLib.h>
+#include <IOKit/usb/USB.h>
+#ifdef USE_HID_FOR_MODIFIERS
+# include <CoreFoundation/CoreFoundation.h>
+# include <IOKit/hid/IOHIDUsageTables.h>
+# include <mach/mach.h>
+# include <mach/mach_error.h>
+#endif
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+# include <IOKit/IOMessage.h>
+# include <IOKit/usb/IOUSBLib.h>
+#endif
+
+
+RT_C_DECLS_BEGIN
+/* Private interface in 10.3 and later. */
+typedef int CGSConnection;
+typedef enum
+{
+ kCGSGlobalHotKeyEnable = 0,
+ kCGSGlobalHotKeyDisable,
+ kCGSGlobalHotKeyDisableExceptUniversalAccess,
+ kCGSGlobalHotKeyInvalid = -1 /* bird */
+} CGSGlobalHotKeyOperatingMode;
+extern CGSConnection _CGSDefaultConnection(void);
+extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode *enmMode);
+extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode enmMode);
+RT_C_DECLS_END
+
+
+/* Defined Constants And Macros: */
+#define QZ_RMETA 0x36
+#define QZ_LMETA 0x37
+#define QZ_LSHIFT 0x38
+#define QZ_CAPSLOCK 0x39
+#define QZ_LALT 0x3A
+#define QZ_LCTRL 0x3B
+#define QZ_RSHIFT 0x3C
+#define QZ_RALT 0x3D
+#define QZ_RCTRL 0x3E
+// Found the definition of the fn-key in:
+// http://stuff.mit.edu/afs/sipb/project/darwin/src/modules/IOHIDFamily/IOHIDSystem/IOHIKeyboardMapper.cpp &
+// http://stuff.mit.edu/afs/sipb/project/darwin/src/modules/AppleADBKeyboard/AppleADBKeyboard.cpp
+// Maybe we need this in the future.
+#define QZ_FN 0x3F
+#define QZ_NUMLOCK 0x47
+/** Short hand for an extended key. */
+#define K_EX VBOXKEY_EXTENDED
+/** Short hand for a modifier key. */
+#define K_MOD VBOXKEY_MODIFIER
+/** Short hand for a lock key. */
+#define K_LOCK VBOXKEY_LOCK
+#ifdef USE_HID_FOR_MODIFIERS
+/** An attempt at catching reference leaks. */
+# define MY_CHECK_CREFS(cRefs) do { AssertMsg(cRefs < 25, ("%ld\n", cRefs)); NOREF(cRefs); } while (0)
+#endif
+
+
+/** This is derived partially from SDL_QuartzKeys.h and partially from testing.
+ * (The funny thing about the virtual scan codes on the mac is that they aren't
+ * offically documented, which is rather silly to say the least. Thus, the need
+ * for looking at SDL and other odd places for docs.) */
+static const uint16_t g_aDarwinToSet1[] =
+{
+ /* set-1 SDL_QuartzKeys.h */
+ 0x1e, /* QZ_a 0x00 */
+ 0x1f, /* QZ_s 0x01 */
+ 0x20, /* QZ_d 0x02 */
+ 0x21, /* QZ_f 0x03 */
+ 0x23, /* QZ_h 0x04 */
+ 0x22, /* QZ_g 0x05 */
+ 0x2c, /* QZ_z 0x06 */
+ 0x2d, /* QZ_x 0x07 */
+ 0x2e, /* QZ_c 0x08 */
+ 0x2f, /* QZ_v 0x09 */
+ 0x56, /* between lshift and z. 'INT 1'? */
+ 0x30, /* QZ_b 0x0B */
+ 0x10, /* QZ_q 0x0C */
+ 0x11, /* QZ_w 0x0D */
+ 0x12, /* QZ_e 0x0E */
+ 0x13, /* QZ_r 0x0F */
+ 0x15, /* QZ_y 0x10 */
+ 0x14, /* QZ_t 0x11 */
+ 0x02, /* QZ_1 0x12 */
+ 0x03, /* QZ_2 0x13 */
+ 0x04, /* QZ_3 0x14 */
+ 0x05, /* QZ_4 0x15 */
+ 0x07, /* QZ_6 0x16 */
+ 0x06, /* QZ_5 0x17 */
+ 0x0d, /* QZ_EQUALS 0x18 */
+ 0x0a, /* QZ_9 0x19 */
+ 0x08, /* QZ_7 0x1A */
+ 0x0c, /* QZ_MINUS 0x1B */
+ 0x09, /* QZ_8 0x1C */
+ 0x0b, /* QZ_0 0x1D */
+ 0x1b, /* QZ_RIGHTBRACKET 0x1E */
+ 0x18, /* QZ_o 0x1F */
+ 0x16, /* QZ_u 0x20 */
+ 0x1a, /* QZ_LEFTBRACKET 0x21 */
+ 0x17, /* QZ_i 0x22 */
+ 0x19, /* QZ_p 0x23 */
+ 0x1c, /* QZ_RETURN 0x24 */
+ 0x26, /* QZ_l 0x25 */
+ 0x24, /* QZ_j 0x26 */
+ 0x28, /* QZ_QUOTE 0x27 */
+ 0x25, /* QZ_k 0x28 */
+ 0x27, /* QZ_SEMICOLON 0x29 */
+ 0x2b, /* QZ_BACKSLASH 0x2A */
+ 0x33, /* QZ_COMMA 0x2B */
+ 0x35, /* QZ_SLASH 0x2C */
+ 0x31, /* QZ_n 0x2D */
+ 0x32, /* QZ_m 0x2E */
+ 0x34, /* QZ_PERIOD 0x2F */
+ 0x0f, /* QZ_TAB 0x30 */
+ 0x39, /* QZ_SPACE 0x31 */
+ 0x29, /* QZ_BACKQUOTE 0x32 */
+ 0x0e, /* QZ_BACKSPACE 0x33 */
+ 0x9c, /* QZ_IBOOK_ENTER 0x34 */
+ 0x01, /* QZ_ESCAPE 0x35 */
+ 0x5c|K_EX|K_MOD, /* QZ_RMETA 0x36 */
+ 0x5b|K_EX|K_MOD, /* QZ_LMETA 0x37 */
+ 0x2a|K_MOD, /* QZ_LSHIFT 0x38 */
+ 0x3a|K_LOCK, /* QZ_CAPSLOCK 0x39 */
+ 0x38|K_MOD, /* QZ_LALT 0x3A */
+ 0x1d|K_MOD, /* QZ_LCTRL 0x3B */
+ 0x36|K_MOD, /* QZ_RSHIFT 0x3C */
+ 0x38|K_EX|K_MOD, /* QZ_RALT 0x3D */
+ 0x1d|K_EX|K_MOD, /* QZ_RCTRL 0x3E */
+ 0, /* */
+ 0, /* */
+ 0x53, /* QZ_KP_PERIOD 0x41 */
+ 0, /* */
+ 0x37, /* QZ_KP_MULTIPLY 0x43 */
+ 0, /* */
+ 0x4e, /* QZ_KP_PLUS 0x45 */
+ 0, /* */
+ 0x45|K_LOCK, /* QZ_NUMLOCK 0x47 */
+ 0, /* */
+ 0, /* */
+ 0, /* */
+ 0x35|K_EX, /* QZ_KP_DIVIDE 0x4B */
+ 0x1c|K_EX, /* QZ_KP_ENTER 0x4C */
+ 0, /* */
+ 0x4a, /* QZ_KP_MINUS 0x4E */
+ 0, /* */
+ 0, /* */
+ 0x0d/*?*/, /* QZ_KP_EQUALS 0x51 */
+ 0x52, /* QZ_KP0 0x52 */
+ 0x4f, /* QZ_KP1 0x53 */
+ 0x50, /* QZ_KP2 0x54 */
+ 0x51, /* QZ_KP3 0x55 */
+ 0x4b, /* QZ_KP4 0x56 */
+ 0x4c, /* QZ_KP5 0x57 */
+ 0x4d, /* QZ_KP6 0x58 */
+ 0x47, /* QZ_KP7 0x59 */
+ 0, /* */
+ 0x48, /* QZ_KP8 0x5B */
+ 0x49, /* QZ_KP9 0x5C */
+ 0x7d, /* yen, | (JIS) 0x5D */
+ 0x73, /* _, ro (JIS) 0x5E */
+ 0, /* */
+ 0x3f, /* QZ_F5 0x60 */
+ 0x40, /* QZ_F6 0x61 */
+ 0x41, /* QZ_F7 0x62 */
+ 0x3d, /* QZ_F3 0x63 */
+ 0x42, /* QZ_F8 0x64 */
+ 0x43, /* QZ_F9 0x65 */
+ 0x29, /* Zen/Han (JIS) 0x66 */
+ 0x57, /* QZ_F11 0x67 */
+ 0x29, /* Zen/Han (JIS) 0x68 */
+ 0x37|K_EX, /* QZ_PRINT / F13 0x69 */
+ 0x63, /* QZ_F16 0x6A */
+ 0x46|K_LOCK, /* QZ_SCROLLOCK 0x6B */
+ 0, /* */
+ 0x44, /* QZ_F10 0x6D */
+ 0x5d|K_EX, /* */
+ 0x58, /* QZ_F12 0x6F */
+ 0, /* */
+ 0/* 0xe1,0x1d,0x45*/, /* QZ_PAUSE 0x71 */
+ 0x52|K_EX, /* QZ_INSERT / HELP 0x72 */
+ 0x47|K_EX, /* QZ_HOME 0x73 */
+ 0x49|K_EX, /* QZ_PAGEUP 0x74 */
+ 0x53|K_EX, /* QZ_DELETE 0x75 */
+ 0x3e, /* QZ_F4 0x76 */
+ 0x4f|K_EX, /* QZ_END 0x77 */
+ 0x3c, /* QZ_F2 0x78 */
+ 0x51|K_EX, /* QZ_PAGEDOWN 0x79 */
+ 0x3b, /* QZ_F1 0x7A */
+ 0x4b|K_EX, /* QZ_LEFT 0x7B */
+ 0x4d|K_EX, /* QZ_RIGHT 0x7C */
+ 0x50|K_EX, /* QZ_DOWN 0x7D */
+ 0x48|K_EX, /* QZ_UP 0x7E */
+ 0,/*0x5e|K_EX*/ /* QZ_POWER 0x7F */ /* have different break key! */
+ /* do NEVER deliver the Power
+ * scancode as e.g. Windows will
+ * handle it, @bugref{7692}. */
+};
+
+
+/** Holds whether we've connected or not. */
+static bool g_fConnectedToCGS = false;
+/** Holds the cached connection. */
+static CGSConnection g_CGSConnection;
+
+
+#ifdef USE_HID_FOR_MODIFIERS
+
+/** Holds the IO Master Port. */
+static mach_port_t g_MasterPort = NULL;
+
+/** Holds the amount of keyboards in the cache. */
+static unsigned g_cKeyboards = 0;
+/** Array of cached keyboard data. */
+static struct KeyboardCacheData
+{
+ /** The device interface. */
+ IOHIDDeviceInterface **ppHidDeviceInterface;
+ /** The queue interface. */
+ IOHIDQueueInterface **ppHidQueueInterface;
+
+ /** Cookie translation array. */
+ struct KeyboardCacheCookie
+ {
+ /** The cookie. */
+ IOHIDElementCookie Cookie;
+ /** The corresponding modifier mask. */
+ uint32_t fMask;
+ } aCookies[64];
+ /** Number of cookies in the array. */
+ unsigned cCookies;
+} g_aKeyboards[128];
+/** Holds the keyboard cache creation timestamp. */
+static uint64_t g_u64KeyboardTS = 0;
+
+/** Holds the HID queue status. */
+static bool g_fHIDQueueEnabled;
+/** Holds the current modifier mask. */
+static uint32_t g_fHIDModifierMask;
+/** Holds the old modifier mask. */
+static uint32_t g_fOldHIDModifierMask;
+
+#endif /* USE_HID_FOR_MODIFIERS */
+
+
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+
+#define VBOX_BOOL_TO_STR_STATE(x) (x) ? "ON" : "OFF"
+/** HID LEDs synchronization data: LED states. */
+typedef struct VBoxLedState_t
+{
+ /** Holds the state of NUM LOCK. */
+ bool fNumLockOn;
+ /** Holds the state of CAPS LOCK. */
+ bool fCapsLockOn;
+ /** Holds the state of SCROLL LOCK. */
+ bool fScrollLockOn;
+} VBoxLedState_t;
+
+/** HID LEDs synchronization data: keyboard states. */
+typedef struct VBoxKbdState_t
+{
+ /** Holds the reference to IOKit HID device. */
+ IOHIDDeviceRef pDevice;
+ /** Holds the LED states. */
+ VBoxLedState_t LED;
+ /** Holds the pointer to a VBoxHidsState_t instance where VBoxKbdState_t instance is stored. */
+ void *pParentContainer;
+ /** Holds the position in global storage (used to simplify CFArray navigation when removing detached device). */
+ CFIndex idxPosition;
+ /** Holds the KBD CAPS LOCK key hold timeout (some Apple keyboards only). */
+ uint64_t cCapsLockTimeout;
+ /** Holds the HID Location ID: unique for an USB device registered in the system. */
+ uint32_t idLocation;
+} VBoxKbdState_t;
+
+/** A struct that used to pass input event info from IOKit callback to a Carbon one */
+typedef struct VBoxKbdEvent_t
+{
+ VBoxKbdState_t *pKbd;
+ uint32_t iKeyCode;
+ uint64_t tsKeyDown;
+} VBoxKbdEvent_t;
+
+/** HID LEDs synchronization data: IOKit specific data. */
+typedef struct VBoxHidsState_t
+{
+ /** Holds the IOKit HID manager reference. */
+ IOHIDManagerRef hidManagerRef;
+ /** Holds the array which consists of VBoxKbdState_t elements. */
+ CFMutableArrayRef pDeviceCollection;
+ /** Holds the LED states that were stored during last broadcast and reflect a guest LED states. */
+ VBoxLedState_t guestState;
+
+ /** Holds the queue which will be appended in IOKit input callback. Carbon input callback will extract data from it. */
+ CFMutableArrayRef pFifoEventQueue;
+ /** Holds the lock for pFifoEventQueue. */
+ RTSEMMUTEX fifoEventQueueLock;
+
+ /** Holds the IOService notification reference: USB HID device matching. */
+ io_iterator_t pUsbHidDeviceMatchNotify;
+ /** Holds the IOService notification reference: USB HID general interest notifications (IOService messages). */
+ io_iterator_t pUsbHidGeneralInterestNotify;
+ /** Holds the IOService notification port reference: device match and device general interest message. */
+ IONotificationPortRef pNotificationPrortRef;
+
+ CFMachPortRef pTapRef;
+ CFRunLoopSourceRef pLoopSourceRef;
+} VBoxHidsState_t;
+
+#endif /* VBOX_WITH_KBD_LEDS_SYNC */
+
+
+unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode)
+{
+ if (uKeyCode >= RT_ELEMENTS(g_aDarwinToSet1))
+ return 0;
+ return g_aDarwinToSet1[uKeyCode];
+}
+
+UInt32 DarwinAdjustModifierMask(UInt32 fModifiers, const void *pvCocoaEvent)
+{
+ /* Check if there is anything to adjust and perform the adjustment. */
+ if (fModifiers & (shiftKey | rightShiftKey | controlKey | rightControlKey | optionKey | rightOptionKey | cmdKey | kEventKeyModifierRightCmdKeyMask))
+ {
+#ifndef USE_HID_FOR_MODIFIERS
+ // WORKAROUND:
+ // Convert the Cocoa modifiers to Carbon ones (the Cocoa modifier
+ // definitions are tucked away in Objective-C headers, unfortunately).
+ //
+ // Update: CGEventTypes.h includes what looks like the Cocoa modifiers
+ // and the NX_* defines should be available as well. We should look
+ // into ways to intercept the CG (core graphics) events in the Carbon
+ // based setup and get rid of all this HID mess. */
+ AssertPtr(pvCocoaEvent);
+ //::darwinPrintEvent("dbg-adjMods: ", pvCocoaEvent);
+ uint32_t fAltModifiers = ::darwinEventModifierFlagsXlated(pvCocoaEvent);
+#else /* USE_HID_FOR_MODIFIERS */
+ /* Update the keyboard cache. */
+ darwinHIDKeyboardCacheUpdate();
+ const UInt32 fAltModifiers = g_fHIDModifierMask;
+#endif /* USE_HID_FOR_MODIFIERS */
+
+#ifdef DEBUG_PRINTF
+ RTPrintf("dbg-fAltModifiers=%#x fModifiers=%#x", fAltModifiers, fModifiers);
+#endif
+ if ( (fModifiers & (rightShiftKey | shiftKey))
+ && (fAltModifiers & (rightShiftKey | shiftKey)))
+ {
+ fModifiers &= ~(rightShiftKey | shiftKey);
+ fModifiers |= fAltModifiers & (rightShiftKey | shiftKey);
+ }
+
+ if ( (fModifiers & (rightControlKey | controlKey))
+ && (fAltModifiers & (rightControlKey | controlKey)))
+ {
+ fModifiers &= ~(rightControlKey | controlKey);
+ fModifiers |= fAltModifiers & (rightControlKey | controlKey);
+ }
+
+ if ( (fModifiers & (optionKey | rightOptionKey))
+ && (fAltModifiers & (optionKey | rightOptionKey)))
+ {
+ fModifiers &= ~(optionKey | rightOptionKey);
+ fModifiers |= fAltModifiers & (optionKey | rightOptionKey);
+ }
+
+ if ( (fModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask))
+ && (fAltModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask)))
+ {
+ fModifiers &= ~(cmdKey | kEventKeyModifierRightCmdKeyMask);
+ fModifiers |= fAltModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask);
+ }
+#ifdef DEBUG_PRINTF
+ RTPrintf(" -> %#x\n", fModifiers);
+#endif
+ }
+ return fModifiers;
+}
+
+unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers)
+{
+ unsigned uScanCode = DarwinModifierMaskToDarwinKeycode(fModifiers);
+ if (uScanCode < RT_ELEMENTS(g_aDarwinToSet1))
+ uScanCode = g_aDarwinToSet1[uScanCode];
+ else
+ Assert(uScanCode == ~0U);
+ return uScanCode;
+}
+
+unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers)
+{
+ unsigned uKeyCode;
+
+ /** @todo find symbols for these keycodes... */
+ fModifiers &= shiftKey | rightShiftKey | controlKey | rightControlKey | optionKey | rightOptionKey | cmdKey
+ | kEventKeyModifierRightCmdKeyMask | kEventKeyModifierNumLockMask | alphaLock | kEventKeyModifierFnMask;
+ if (fModifiers == shiftKey)
+ uKeyCode = QZ_LSHIFT;
+ else if (fModifiers == rightShiftKey)
+ uKeyCode = QZ_RSHIFT;
+ else if (fModifiers == controlKey)
+ uKeyCode = QZ_LCTRL;
+ else if (fModifiers == rightControlKey)
+ uKeyCode = QZ_RCTRL;
+ else if (fModifiers == optionKey)
+ uKeyCode = QZ_LALT;
+ else if (fModifiers == rightOptionKey)
+ uKeyCode = QZ_RALT;
+ else if (fModifiers == cmdKey)
+ uKeyCode = QZ_LMETA;
+ else if (fModifiers == kEventKeyModifierRightCmdKeyMask /* hack */)
+ uKeyCode = QZ_RMETA;
+ else if (fModifiers == alphaLock)
+ uKeyCode = QZ_CAPSLOCK;
+ else if (fModifiers == kEventKeyModifierNumLockMask)
+ uKeyCode = QZ_NUMLOCK;
+ else if (fModifiers == kEventKeyModifierFnMask)
+ uKeyCode = QZ_FN;
+ else if (fModifiers == 0)
+ uKeyCode = 0;
+ else
+ uKeyCode = ~0U; /* multiple */
+ return uKeyCode;
+}
+
+UInt32 DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode)
+{
+ UInt32 fModifiers;
+
+ /** @todo find symbols for these keycodes... */
+ if (uKeyCode == QZ_LSHIFT)
+ fModifiers = shiftKey;
+ else if (uKeyCode == QZ_RSHIFT)
+ fModifiers = rightShiftKey;
+ else if (uKeyCode == QZ_LCTRL)
+ fModifiers = controlKey;
+ else if (uKeyCode == QZ_RCTRL)
+ fModifiers = rightControlKey;
+ else if (uKeyCode == QZ_LALT)
+ fModifiers = optionKey;
+ else if (uKeyCode == QZ_RALT)
+ fModifiers = rightOptionKey;
+ else if (uKeyCode == QZ_LMETA)
+ fModifiers = cmdKey;
+ else if (uKeyCode == QZ_RMETA)
+ fModifiers = kEventKeyModifierRightCmdKeyMask; /* hack */
+ else if (uKeyCode == QZ_CAPSLOCK)
+ fModifiers = alphaLock;
+ else if (uKeyCode == QZ_NUMLOCK)
+ fModifiers = kEventKeyModifierNumLockMask;
+ else if (uKeyCode == QZ_FN)
+ fModifiers = kEventKeyModifierFnMask;
+ else
+ fModifiers = 0;
+ return fModifiers;
+}
+
+
+void DarwinDisableGlobalHotKeys(bool fDisable)
+{
+ static unsigned s_cComplaints = 0;
+
+ /* Lazy connect to the core graphics service. */
+ if (!g_fConnectedToCGS)
+ {
+ g_CGSConnection = _CGSDefaultConnection();
+ g_fConnectedToCGS = true;
+ }
+
+ /* Get the current mode. */
+ CGSGlobalHotKeyOperatingMode enmMode = kCGSGlobalHotKeyInvalid;
+ CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmMode);
+ if ( enmMode != kCGSGlobalHotKeyEnable
+ && enmMode != kCGSGlobalHotKeyDisable
+ && enmMode != kCGSGlobalHotKeyDisableExceptUniversalAccess)
+ {
+ AssertMsgFailed(("%d\n", enmMode));
+ if (s_cComplaints++ < 32)
+ LogRel(("DarwinDisableGlobalHotKeys: Unexpected enmMode=%d\n", enmMode));
+ return;
+ }
+
+ /* Calc the new mode. */
+ if (fDisable)
+ {
+ if (enmMode != kCGSGlobalHotKeyEnable)
+ return;
+ enmMode = kCGSGlobalHotKeyDisableExceptUniversalAccess;
+ }
+ else
+ {
+ if (enmMode != kCGSGlobalHotKeyDisableExceptUniversalAccess)
+ return;
+ enmMode = kCGSGlobalHotKeyEnable;
+ }
+
+ /* Try set it and check the actual result. */
+ CGSSetGlobalHotKeyOperatingMode(g_CGSConnection, enmMode);
+ CGSGlobalHotKeyOperatingMode enmNewMode = kCGSGlobalHotKeyInvalid;
+ CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmNewMode);
+ if (enmNewMode != enmMode)
+ {
+ /* If the screensaver kicks in we should ignore failure here. */
+ AssertMsg(enmMode == kCGSGlobalHotKeyEnable, ("enmNewMode=%d enmMode=%d\n", enmNewMode, enmMode));
+ if (s_cComplaints++ < 32)
+ LogRel(("DarwinDisableGlobalHotKeys: Failed to change mode; enmNewMode=%d enmMode=%d\n", enmNewMode, enmMode));
+ }
+}
+
+
+#ifdef USE_HID_FOR_MODIFIERS
+
+/** Callback function for consuming queued events.
+ * @param pvTarget Brings the queue?
+ * @param rcIn Brings what?
+ * @param pvRefcon Brings the pointer to the keyboard cache entry.
+ * @param pvSender Brings what? */
+static void darwinQueueCallback(void *pvTarget, IOReturn rcIn, void *pvRefcon, void *pvSender)
+{
+ struct KeyboardCacheData *pKeyboardEntry = (struct KeyboardCacheData *)pvRefcon;
+ if (!pKeyboardEntry->ppHidQueueInterface)
+ return;
+ NOREF(pvTarget);
+ NOREF(rcIn);
+ NOREF(pvSender);
+
+ /* Consume the events. */
+ g_fOldHIDModifierMask = g_fHIDModifierMask;
+ for (;;)
+ {
+#ifdef DEBUG_PRINTF
+ RTPrintf("dbg-ev: "); RTStrmFlush(g_pStdOut);
+#endif
+ IOHIDEventStruct Event;
+ AbsoluteTime ZeroTime = {0,0};
+ IOReturn rc = (*pKeyboardEntry->ppHidQueueInterface)->getNextEvent(pKeyboardEntry->ppHidQueueInterface,
+ &Event, ZeroTime, 0);
+ if (rc != kIOReturnSuccess)
+ break;
+
+ /* Translate the cookie value to a modifier mask. */
+ uint32_t fMask = 0;
+ unsigned i = pKeyboardEntry->cCookies;
+ while (i-- > 0)
+ {
+ if (pKeyboardEntry->aCookies[i].Cookie == Event.elementCookie)
+ {
+ fMask = pKeyboardEntry->aCookies[i].fMask;
+ break;
+ }
+ }
+
+ /* Adjust the modifier mask. */
+ if (Event.value)
+ g_fHIDModifierMask |= fMask;
+ else
+ g_fHIDModifierMask &= ~fMask;
+#ifdef DEBUG_PRINTF
+ RTPrintf("t=%d c=%#x v=%#x cblv=%d lv=%p m=%#X\n", Event.type, Event.elementCookie, Event.value, Event.longValueSize, Event.value, fMask); RTStrmFlush(g_pStdOut);
+#endif
+ }
+#ifdef DEBUG_PRINTF
+ RTPrintf("dbg-ev: done\n"); RTStrmFlush(g_pStdOut);
+#endif
+}
+
+/* Forward declaration for darwinBruteForcePropertySearch. */
+static void darwinBruteForcePropertySearch(CFDictionaryRef DictRef, struct KeyboardCacheData *pKeyboardEntry);
+
+/** Element enumeration callback. */
+static void darwinBruteForcePropertySearchApplier(const void *pvValue, void *pvCacheEntry)
+{
+ if (CFGetTypeID(pvValue) == CFDictionaryGetTypeID())
+ darwinBruteForcePropertySearch((CFMutableDictionaryRef)pvValue, (struct KeyboardCacheData *)pvCacheEntry);
+}
+
+/** Recurses through the keyboard properties looking for certain keys. */
+static void darwinBruteForcePropertySearch(CFDictionaryRef DictRef, struct KeyboardCacheData *pKeyboardEntry)
+{
+ CFTypeRef ObjRef;
+
+ /* Check for the usage page and usage key we want. */
+ long lUsage;
+ ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementUsageKey));
+ if ( ObjRef
+ && CFGetTypeID(ObjRef) == CFNumberGetTypeID()
+ && CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lUsage))
+ {
+ switch (lUsage)
+ {
+ case kHIDUsage_KeyboardLeftControl:
+ case kHIDUsage_KeyboardLeftShift:
+ case kHIDUsage_KeyboardLeftAlt:
+ case kHIDUsage_KeyboardLeftGUI:
+ case kHIDUsage_KeyboardRightControl:
+ case kHIDUsage_KeyboardRightShift:
+ case kHIDUsage_KeyboardRightAlt:
+ case kHIDUsage_KeyboardRightGUI:
+ {
+ long lPage;
+ ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementUsagePageKey));
+ if ( !ObjRef
+ || CFGetTypeID(ObjRef) != CFNumberGetTypeID()
+ || !CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lPage)
+ || lPage != kHIDPage_KeyboardOrKeypad)
+ break;
+
+ if (pKeyboardEntry->cCookies >= RT_ELEMENTS(pKeyboardEntry->aCookies))
+ {
+ AssertMsgFailed(("too many cookies!\n"));
+ break;
+ }
+
+ /* Get the cookie and modifier mask. */
+ long lCookie;
+ ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementCookieKey));
+ if ( !ObjRef
+ || CFGetTypeID(ObjRef) != CFNumberGetTypeID()
+ || !CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lCookie))
+ break;
+
+ uint32_t fMask;
+ switch (lUsage)
+ {
+ case kHIDUsage_KeyboardLeftControl : fMask = controlKey; break;
+ case kHIDUsage_KeyboardLeftShift : fMask = shiftKey; break;
+ case kHIDUsage_KeyboardLeftAlt : fMask = optionKey; break;
+ case kHIDUsage_KeyboardLeftGUI : fMask = cmdKey; break;
+ case kHIDUsage_KeyboardRightControl: fMask = rightControlKey; break;
+ case kHIDUsage_KeyboardRightShift : fMask = rightShiftKey; break;
+ case kHIDUsage_KeyboardRightAlt : fMask = rightOptionKey; break;
+ case kHIDUsage_KeyboardRightGUI : fMask = kEventKeyModifierRightCmdKeyMask; break;
+ default: AssertMsgFailed(("%ld\n",lUsage)); fMask = 0; break;
+ }
+
+ /* If we've got a queue, add the cookie to the queue. */
+ if (pKeyboardEntry->ppHidQueueInterface)
+ {
+ IOReturn rc = (*pKeyboardEntry->ppHidQueueInterface)->addElement(pKeyboardEntry->ppHidQueueInterface, (IOHIDElementCookie)lCookie, 0);
+ AssertMsg(rc == kIOReturnSuccess, ("rc=%d\n", rc));
+#ifdef DEBUG_PRINTF
+ RTPrintf("dbg-add: u=%#lx c=%#lx\n", lUsage, lCookie);
+#endif
+ }
+
+ /* Add the cookie to the keyboard entry. */
+ pKeyboardEntry->aCookies[pKeyboardEntry->cCookies].Cookie = (IOHIDElementCookie)lCookie;
+ pKeyboardEntry->aCookies[pKeyboardEntry->cCookies].fMask = fMask;
+ ++pKeyboardEntry->cCookies;
+ break;
+ }
+ }
+ }
+
+
+ /* Get the elements key and recursively iterate the elements looking for they key cookies. */
+ ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementKey));
+ if ( ObjRef
+ && CFGetTypeID(ObjRef) == CFArrayGetTypeID())
+ {
+ CFArrayRef ArrayObjRef = (CFArrayRef)ObjRef;
+ CFRange Range = {0, CFArrayGetCount(ArrayObjRef)};
+ CFArrayApplyFunction(ArrayObjRef, Range, darwinBruteForcePropertySearchApplier, pKeyboardEntry);
+ }
+}
+
+/** Creates a keyboard cache entry.
+ * @param pKeyboardEntry Brings the pointer to the entry.
+ * @param KeyboardDevice Brings the keyboard device to create the entry for. */
+static bool darwinHIDKeyboardCacheCreateEntry(struct KeyboardCacheData *pKeyboardEntry, io_object_t KeyboardDevice)
+{
+ unsigned long cRefs = 0;
+ memset(pKeyboardEntry, 0, sizeof(*pKeyboardEntry));
+
+ /* Query the HIDDeviceInterface for this HID (keyboard) object. */
+ SInt32 Score = 0;
+ IOCFPlugInInterface **ppPlugInInterface = NULL;
+ IOReturn rc = IOCreatePlugInInterfaceForService(KeyboardDevice, kIOHIDDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
+ if (rc == kIOReturnSuccess)
+ {
+ IOHIDDeviceInterface **ppHidDeviceInterface = NULL;
+ HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
+ CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
+ (LPVOID *)&ppHidDeviceInterface);
+ cRefs = (*ppPlugInInterface)->Release(ppPlugInInterface); MY_CHECK_CREFS(cRefs);
+ ppPlugInInterface = NULL;
+ if (hrc == S_OK)
+ {
+ rc = (*ppHidDeviceInterface)->open(ppHidDeviceInterface, 0);
+ if (rc == kIOReturnSuccess)
+ {
+ /* Create a removal callback. */
+ /** @todo */
+
+ /* Create the queue so we can insert elements while searching the properties. */
+ IOHIDQueueInterface **ppHidQueueInterface = (*ppHidDeviceInterface)->allocQueue(ppHidDeviceInterface);
+ if (ppHidQueueInterface)
+ {
+ rc = (*ppHidQueueInterface)->create(ppHidQueueInterface, 0, 32);
+ if (rc != kIOReturnSuccess)
+ {
+ AssertMsgFailed(("rc=%d\n", rc));
+ cRefs = (*ppHidQueueInterface)->Release(ppHidQueueInterface); MY_CHECK_CREFS(cRefs);
+ ppHidQueueInterface = NULL;
+ }
+ }
+ else
+ AssertFailed();
+ pKeyboardEntry->ppHidQueueInterface = ppHidQueueInterface;
+
+ /* Brute force getting of attributes. */
+ /** @todo read up on how to do this in a less resource intensive way! Suggestions are welcome! */
+ CFMutableDictionaryRef PropertiesRef = 0;
+ kern_return_t krc = IORegistryEntryCreateCFProperties(KeyboardDevice, &PropertiesRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ darwinBruteForcePropertySearch(PropertiesRef, pKeyboardEntry);
+ CFRelease(PropertiesRef);
+ }
+ else
+ AssertMsgFailed(("krc=%#x\n", krc));
+
+ if (ppHidQueueInterface)
+ {
+ /* Now install our queue callback. */
+ CFRunLoopSourceRef RunLoopSrcRef = NULL;
+ rc = (*ppHidQueueInterface)->createAsyncEventSource(ppHidQueueInterface, &RunLoopSrcRef);
+ if (rc == kIOReturnSuccess)
+ {
+ CFRunLoopRef RunLoopRef = (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop());
+ CFRunLoopAddSource(RunLoopRef, RunLoopSrcRef, kCFRunLoopDefaultMode);
+ }
+
+ /* Now install our queue callback. */
+ rc = (*ppHidQueueInterface)->setEventCallout(ppHidQueueInterface, darwinQueueCallback, ppHidQueueInterface, pKeyboardEntry);
+ if (rc != kIOReturnSuccess)
+ AssertMsgFailed(("rc=%d\n", rc));
+ }
+
+ /* Complete the new keyboard cache entry. */
+ pKeyboardEntry->ppHidDeviceInterface = ppHidDeviceInterface;
+ pKeyboardEntry->ppHidQueueInterface = ppHidQueueInterface;
+ return true;
+ }
+
+ AssertMsgFailed(("rc=%d\n", rc));
+ cRefs = (*ppHidDeviceInterface)->Release(ppHidDeviceInterface); MY_CHECK_CREFS(cRefs);
+ }
+ else
+ AssertMsgFailed(("hrc=%#x\n", hrc));
+ }
+ else
+ AssertMsgFailed(("rc=%d\n", rc));
+
+ return false;
+}
+
+/** Destroys a keyboard cache entry. */
+static void darwinHIDKeyboardCacheDestroyEntry(struct KeyboardCacheData *pKeyboardEntry)
+{
+ unsigned long cRefs;
+
+ /* Destroy the queue. */
+ if (pKeyboardEntry->ppHidQueueInterface)
+ {
+ IOHIDQueueInterface **ppHidQueueInterface = pKeyboardEntry->ppHidQueueInterface;
+ pKeyboardEntry->ppHidQueueInterface = NULL;
+
+ /* Stop it just in case we haven't done so. doesn't really matter I think. */
+ (*ppHidQueueInterface)->stop(ppHidQueueInterface);
+
+ /* Deal with the run loop source. */
+ CFRunLoopSourceRef RunLoopSrcRef = (*ppHidQueueInterface)->getAsyncEventSource(ppHidQueueInterface);
+ if (RunLoopSrcRef)
+ {
+ CFRunLoopRef RunLoopRef = (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop());
+ CFRunLoopRemoveSource(RunLoopRef, RunLoopSrcRef, kCFRunLoopDefaultMode);
+
+ CFRelease(RunLoopSrcRef);
+ }
+
+ /* Dispose of and release the queue. */
+ (*ppHidQueueInterface)->dispose(ppHidQueueInterface);
+ cRefs = (*ppHidQueueInterface)->Release(ppHidQueueInterface); MY_CHECK_CREFS(cRefs);
+ }
+
+ /* Release the removal hook? */
+ /** @todo */
+
+ /* Close and release the device interface. */
+ if (pKeyboardEntry->ppHidDeviceInterface)
+ {
+ IOHIDDeviceInterface **ppHidDeviceInterface = pKeyboardEntry->ppHidDeviceInterface;
+ pKeyboardEntry->ppHidDeviceInterface = NULL;
+
+ (*ppHidDeviceInterface)->close(ppHidDeviceInterface);
+ cRefs = (*ppHidDeviceInterface)->Release(ppHidDeviceInterface); MY_CHECK_CREFS(cRefs);
+ }
+}
+
+/** Zap the keyboard cache. */
+static void darwinHIDKeyboardCacheZap(void)
+{
+ /* Release the old cache data first. */
+ while (g_cKeyboards > 0)
+ {
+ unsigned i = --g_cKeyboards;
+ darwinHIDKeyboardCacheDestroyEntry(&g_aKeyboards[i]);
+ }
+}
+
+/** Updates the cached keyboard data.
+ * @todo The current implementation is very brute force...
+ * Rewrite it so that it doesn't flush the cache completely but simply checks whether
+ * anything has changed in the HID config. With any luck, there might even be a callback
+ * or something we can poll for HID config changes...
+ * setRemovalCallback() is a start... */
+static void darwinHIDKeyboardCacheDoUpdate(void)
+{
+ g_u64KeyboardTS = RTTimeMilliTS();
+
+ /* Dispense with the old cache data. */
+ darwinHIDKeyboardCacheZap();
+
+ /* Open the master port on the first invocation. */
+ if (!g_MasterPort)
+ {
+ kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort);
+ AssertReturnVoid(krc == KERN_SUCCESS);
+ }
+
+ /* Create a matching dictionary for searching for keyboards devices. */
+ static const UInt32 s_Page = kHIDPage_GenericDesktop;
+ static const UInt32 s_Usage = kHIDUsage_GD_Keyboard;
+ CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOHIDDeviceKey);
+ AssertReturnVoid(RefMatchingDict);
+ CFDictionarySetValue(RefMatchingDict, CFSTR(kIOHIDPrimaryUsagePageKey),
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &s_Page));
+ CFDictionarySetValue(RefMatchingDict, CFSTR(kIOHIDPrimaryUsageKey),
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &s_Usage));
+
+ /* Perform the search and get a collection of keyboard devices. */
+ io_iterator_t Keyboards = NULL;
+ IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &Keyboards);
+ AssertMsgReturnVoid(rc == kIOReturnSuccess, ("rc=%d\n", rc));
+ RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+ /* Enumerate the keyboards and query the cache data. */
+ unsigned i = 0;
+ io_object_t KeyboardDevice;
+ while ( i < RT_ELEMENTS(g_aKeyboards)
+ && (KeyboardDevice = IOIteratorNext(Keyboards)) != 0)
+ {
+ if (darwinHIDKeyboardCacheCreateEntry(&g_aKeyboards[i], KeyboardDevice))
+ i++;
+ IOObjectRelease(KeyboardDevice);
+ }
+ g_cKeyboards = i;
+
+ IOObjectRelease(Keyboards);
+}
+
+/** Updates the keyboard cache if it's time to do it again. */
+static void darwinHIDKeyboardCacheUpdate(void)
+{
+ if ( !g_cKeyboards
+ /*|| g_u64KeyboardTS - RTTimeMilliTS() > 7500*/ /* 7.5sec */)
+ darwinHIDKeyboardCacheDoUpdate();
+}
+
+/** Queries the modifier keys from the (IOKit) HID Manager. */
+static UInt32 darwinQueryHIDModifiers(void)
+{
+ /* Iterate thru the keyboards collecting their modifier masks. */
+ UInt32 fHIDModifiers = 0;
+ unsigned i = g_cKeyboards;
+ while (i-- > 0)
+ {
+ IOHIDDeviceInterface **ppHidDeviceInterface = g_aKeyboards[i].ppHidDeviceInterface;
+ if (!ppHidDeviceInterface)
+ continue;
+
+ unsigned j = g_aKeyboards[i].cCookies;
+ while (j-- > 0)
+ {
+ IOHIDEventStruct HidEvent;
+ IOReturn rc = (*ppHidDeviceInterface)->getElementValue(ppHidDeviceInterface,
+ g_aKeyboards[i].aCookies[j].Cookie,
+ &HidEvent);
+ if (rc == kIOReturnSuccess)
+ {
+ if (HidEvent.value)
+ fHIDModifiers |= g_aKeyboards[i].aCookies[j].fMask;
+ }
+ else
+ AssertMsgFailed(("rc=%#x\n", rc));
+ }
+ }
+
+ return fHIDModifiers;
+}
+
+#endif /* USE_HID_FOR_MODIFIERS */
+
+
+void DarwinGrabKeyboard(bool fGlobalHotkeys)
+{
+ LogFlow(("DarwinGrabKeyboard: fGlobalHotkeys=%RTbool\n", fGlobalHotkeys));
+
+#ifdef USE_HID_FOR_MODIFIERS
+ /* Update the keyboard cache. */
+ darwinHIDKeyboardCacheUpdate();
+
+ /* Start the keyboard queues and query the current mask. */
+ g_fHIDQueueEnabled = true;
+
+ unsigned i = g_cKeyboards;
+ while (i-- > 0)
+ {
+ if (g_aKeyboards[i].ppHidQueueInterface)
+ (*g_aKeyboards[i].ppHidQueueInterface)->start(g_aKeyboards[i].ppHidQueueInterface);
+ }
+
+ g_fHIDModifierMask = darwinQueryHIDModifiers();
+#endif /* USE_HID_FOR_MODIFIERS */
+
+ /* Disable hotkeys if requested. */
+ if (fGlobalHotkeys)
+ DarwinDisableGlobalHotKeys(true);
+}
+
+void DarwinReleaseKeyboard()
+{
+ LogFlow(("DarwinReleaseKeyboard\n"));
+
+ /* Re-enable hotkeys. */
+ DarwinDisableGlobalHotKeys(false);
+
+#ifdef USE_HID_FOR_MODIFIERS
+ /* Stop and drain the keyboard queues. */
+ g_fHIDQueueEnabled = false;
+
+#if 0
+ unsigned i = g_cKeyboards;
+ while (i-- > 0)
+ {
+ if (g_aKeyboards[i].ppHidQueueInterface)
+ {
+
+ (*g_aKeyboards[i].ppHidQueueInterface)->stop(g_aKeyboards[i].ppHidQueueInterface);
+
+ /* drain it */
+ IOReturn rc;
+ unsigned c = 0;
+ do
+ {
+ IOHIDEventStruct Event;
+ AbsoluteTime MaxTime = {0,0};
+ rc = (*g_aKeyboards[i].ppHidQueueInterface)->getNextEvent(g_aKeyboards[i].ppHidQueueInterface,
+ &Event, MaxTime, 0);
+ } while ( rc == kIOReturnSuccess
+ && c++ < 32);
+ }
+ }
+#else
+ /* Kill the keyboard cache. */
+ darwinHIDKeyboardCacheZap();
+#endif
+
+ /* Clear the modifier mask. */
+ g_fHIDModifierMask = 0;
+#endif /* USE_HID_FOR_MODIFIERS */
+}
+
+
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+
+/** Prepares dictionary that will be used to match HID LED device(s) while discovering. */
+static CFDictionaryRef darwinQueryLedDeviceMatchingDictionary()
+{
+ CFDictionaryRef deviceMatchingDictRef;
+
+ // Use two (key, value) pairs:
+ // - (kIOHIDDeviceUsagePageKey, kHIDPage_GenericDesktop),
+ // - (kIOHIDDeviceUsageKey, kHIDUsage_GD_Keyboard). */
+
+ CFNumberRef usagePageKeyCFNumberRef; int usagePageKeyCFNumberValue = kHIDPage_GenericDesktop;
+ CFNumberRef usageKeyCFNumberRef; int usageKeyCFNumberValue = kHIDUsage_GD_Keyboard;
+
+ usagePageKeyCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePageKeyCFNumberValue);
+ if (usagePageKeyCFNumberRef)
+ {
+ usageKeyCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usageKeyCFNumberValue);
+ if (usageKeyCFNumberRef)
+ {
+ CFStringRef dictionaryKeys[2] = { CFSTR(kIOHIDDeviceUsagePageKey), CFSTR(kIOHIDDeviceUsageKey) };
+ CFNumberRef dictionaryVals[2] = { usagePageKeyCFNumberRef, usageKeyCFNumberRef };
+
+ deviceMatchingDictRef = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **)dictionaryKeys,
+ (const void **)dictionaryVals,
+ 2, /** two (key, value) pairs */
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (deviceMatchingDictRef)
+ {
+ CFRelease(usageKeyCFNumberRef);
+ CFRelease(usagePageKeyCFNumberRef);
+
+ return deviceMatchingDictRef;
+ }
+
+ CFRelease(usageKeyCFNumberRef);
+ }
+
+ CFRelease(usagePageKeyCFNumberRef);
+ }
+
+ return NULL;
+}
+
+/** Prepare dictionary that will be used to match HID LED device element(s) while discovering. */
+static CFDictionaryRef darwinQueryLedElementMatchingDictionary()
+{
+ CFDictionaryRef elementMatchingDictRef;
+
+ // Use only one (key, value) pair to match LED device element:
+ // - (kIOHIDElementUsagePageKey, kHIDPage_LEDs). */
+
+ CFNumberRef usagePageKeyCFNumberRef; int usagePageKeyCFNumberValue = kHIDPage_LEDs;
+
+ usagePageKeyCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePageKeyCFNumberValue);
+ if (usagePageKeyCFNumberRef)
+ {
+ CFStringRef dictionaryKeys[1] = { CFSTR(kIOHIDElementUsagePageKey), };
+ CFNumberRef dictionaryVals[1] = { usagePageKeyCFNumberRef, };
+
+ elementMatchingDictRef = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void **)dictionaryKeys,
+ (const void **)dictionaryVals,
+ 1, /** one (key, value) pair */
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (elementMatchingDictRef)
+ {
+ CFRelease(usagePageKeyCFNumberRef);
+ return elementMatchingDictRef;
+ }
+
+ CFRelease(usagePageKeyCFNumberRef);
+ }
+
+ return NULL;
+}
+
+/** Turn ON or OFF a particular LED. */
+static int darwinLedElementSetValue(IOHIDDeviceRef hidDevice, IOHIDElementRef element, bool fEnabled)
+{
+ IOHIDValueRef valueRef;
+ IOReturn rc = kIOReturnError;
+
+ /* Try to resume suspended keyboard devices. Abort if failed in order to avoid GUI freezes. */
+ int rc1 = SUPR3ResumeSuspendedKeyboards();
+ if (RT_FAILURE(rc1))
+ return rc1;
+
+ valueRef = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, element, 0, (fEnabled) ? 1 : 0);
+ if (valueRef)
+ {
+ rc = IOHIDDeviceSetValue(hidDevice, element, valueRef);
+ if (rc != kIOReturnSuccess)
+ LogRel2(("Warning! Something went wrong in attempt to turn %s HID device led (error %d)!\n", ((fEnabled) ? "on" : "off"), rc));
+ else
+ LogRel2(("Led (%d) is turned %s\n", (int)IOHIDElementGetUsage(element), ((fEnabled) ? "on" : "off")));
+
+ CFRelease(valueRef);
+ }
+
+ return rc;
+}
+
+/** Get state of a particular led. */
+static int darwinLedElementGetValue(IOHIDDeviceRef hidDevice, IOHIDElementRef element, bool *fEnabled)
+{
+ /* Try to resume suspended keyboard devices. Abort if failed in order to avoid GUI freezes. */
+ int rc1 = SUPR3ResumeSuspendedKeyboards();
+ if (RT_FAILURE(rc1))
+ return rc1;
+
+ IOHIDValueRef valueRef;
+ IOReturn rc = IOHIDDeviceGetValue(hidDevice, element, &valueRef);
+ if (rc == kIOReturnSuccess)
+ {
+ CFIndex integerValue = IOHIDValueGetIntegerValue(valueRef);
+ switch (integerValue)
+ {
+ case 0:
+ *fEnabled = false;
+ break;
+ case 1:
+ *fEnabled = true;
+ break;
+ default:
+ rc = kIOReturnError;
+ }
+
+ /*CFRelease(valueRef); - IOHIDDeviceGetValue does not return a reference, so no need to release it. */
+ }
+
+ return rc;
+}
+
+/** Set corresponding states from NumLock, CapsLock and ScrollLock leds. */
+static int darwinSetDeviceLedsState(IOHIDDeviceRef hidDevice, CFDictionaryRef elementMatchingDict,
+ bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+ CFArrayRef matchingElementsArrayRef;
+ int rc2 = 0;
+
+ matchingElementsArrayRef = IOHIDDeviceCopyMatchingElements(hidDevice, elementMatchingDict, kIOHIDOptionsTypeNone);
+ if (matchingElementsArrayRef)
+ {
+ CFIndex cElements = CFArrayGetCount(matchingElementsArrayRef);
+
+ /* Cycle though all the elements we found */
+ for (CFIndex i = 0; i < cElements; i++)
+ {
+ IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(matchingElementsArrayRef, i);
+ uint32_t usage = IOHIDElementGetUsage(element);
+ int rc = 0;
+
+ switch (usage)
+ {
+ case kHIDUsage_LED_NumLock:
+ rc = darwinLedElementSetValue(hidDevice, element, fNumLockOn);
+ break;
+
+ case kHIDUsage_LED_CapsLock:
+ rc = darwinLedElementSetValue(hidDevice, element, fCapsLockOn);
+ break;
+ case kHIDUsage_LED_ScrollLock:
+ rc = darwinLedElementSetValue(hidDevice, element, fScrollLockOn);
+ break;
+ }
+ if (rc != 0)
+ {
+ LogRel2(("Failed to set led (%d) state\n", (int)IOHIDElementGetUsage(element)));
+ rc2 = kIOReturnError;
+ }
+ }
+
+ CFRelease(matchingElementsArrayRef);
+ }
+
+ return rc2;
+}
+
+/** Get corresponding states for NumLock, CapsLock and ScrollLock leds. */
+static int darwinGetDeviceLedsState(IOHIDDeviceRef hidDevice, CFDictionaryRef elementMatchingDict,
+ bool *fNumLockOn, bool *fCapsLockOn, bool *fScrollLockOn)
+{
+ CFArrayRef matchingElementsArrayRef;
+ int rc2 = 0;
+
+ matchingElementsArrayRef = IOHIDDeviceCopyMatchingElements(hidDevice, elementMatchingDict, kIOHIDOptionsTypeNone);
+ if (matchingElementsArrayRef)
+ {
+ CFIndex cElements = CFArrayGetCount(matchingElementsArrayRef);
+
+ /* Cycle though all the elements we found */
+ for (CFIndex i = 0; i < cElements; i++)
+ {
+ IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(matchingElementsArrayRef, i);
+ uint32_t usage = IOHIDElementGetUsage(element);
+ int rc = 0;
+
+ switch (usage)
+ {
+ case kHIDUsage_LED_NumLock:
+ rc = darwinLedElementGetValue(hidDevice, element, fNumLockOn);
+ break;
+
+ case kHIDUsage_LED_CapsLock:
+ rc = darwinLedElementGetValue(hidDevice, element, fCapsLockOn);
+ break;
+ case kHIDUsage_LED_ScrollLock:
+ rc = darwinLedElementGetValue(hidDevice, element, fScrollLockOn);
+ break;
+ }
+ if (rc != 0)
+ {
+ LogRel2(("Failed to get led (%d) state\n", (int)IOHIDElementGetUsage(element)));
+ rc2 = kIOReturnError;
+ }
+ }
+
+ CFRelease(matchingElementsArrayRef);
+ }
+
+ return rc2;
+}
+
+/** Get integer property of HID device */
+static uint32_t darwinQueryIntProperty(IOHIDDeviceRef pHidDeviceRef, CFStringRef pProperty)
+{
+ CFTypeRef pNumberRef;
+ uint32_t value = 0;
+
+ AssertReturn(pHidDeviceRef, 0);
+ AssertReturn(pProperty, 0);
+
+ pNumberRef = IOHIDDeviceGetProperty(pHidDeviceRef, pProperty);
+ if (pNumberRef)
+ {
+ if (CFGetTypeID(pNumberRef) == CFNumberGetTypeID())
+ {
+ if (CFNumberGetValue((CFNumberRef)pNumberRef, kCFNumberSInt32Type, &value))
+ return value;
+ }
+ }
+
+ return 0;
+}
+
+/** Get HID Vendor ID */
+static uint32_t darwinHidVendorId(IOHIDDeviceRef pHidDeviceRef)
+{
+ return darwinQueryIntProperty(pHidDeviceRef, CFSTR(kIOHIDVendorIDKey));
+}
+
+/** Get HID Product ID */
+static uint32_t darwinHidProductId(IOHIDDeviceRef pHidDeviceRef)
+{
+ return darwinQueryIntProperty(pHidDeviceRef, CFSTR(kIOHIDProductIDKey));
+}
+
+/** Get HID Location ID */
+static uint32_t darwinHidLocationId(IOHIDDeviceRef pHidDeviceRef)
+{
+ return darwinQueryIntProperty(pHidDeviceRef, CFSTR(kIOHIDLocationIDKey));
+}
+
+/** Some keyboard devices might freeze after LEDs manipulation. We filter out such devices here.
+ * In the list below, devices that known to have such issues. If you want to add new device,
+ * then add it here. Currently, we only filter devices by Vendor ID.
+ * In future it might make sense to take Product ID into account as well. */
+static bool darwinHidDeviceSupported(IOHIDDeviceRef pHidDeviceRef)
+{
+#ifndef VBOX_WITHOUT_KBD_LEDS_SYNC_FILTERING
+ bool fSupported = true;
+ uint32_t vendorId = darwinHidVendorId(pHidDeviceRef);
+ uint32_t productId = darwinHidProductId(pHidDeviceRef);
+
+ if (vendorId == 0x05D5) /* Genius */
+ {
+ if (productId == 0x8001) /* GK-04008/C keyboard */
+ fSupported = false;
+ }
+ if (vendorId == 0xE6A) /* Megawin Technology */
+ {
+ if (productId == 0x6001) /* Japanese flexible keyboard */
+ fSupported = false;
+ }
+
+ LogRel2(("HID device [VendorID=0x%X, ProductId=0x%X] %s in the list of supported devices.\n", vendorId, productId, (fSupported ? "is" : "is not")));
+
+ return fSupported;
+#else /* !VBOX_WITH_KBD_LEDS_SYNC_FILTERING */
+ return true;
+#endif
+}
+
+/** IOKit key press callback helper: take care about key-down event.
+ * This code should be executed within a critical section under pHidState->fifoEventQueueLock. */
+static void darwinHidInputCbKeyDown(VBoxKbdState_t *pKbd, uint32_t iKeyCode, VBoxHidsState_t *pHidState)
+{
+ VBoxKbdEvent_t *pEvent = (VBoxKbdEvent_t *)malloc(sizeof(VBoxKbdEvent_t));
+
+ if (pEvent)
+ {
+ /* Queue Key-Down event. */
+ pEvent->tsKeyDown = RTTimeSystemMilliTS();
+ pEvent->pKbd = pKbd;
+ pEvent->iKeyCode = iKeyCode;
+
+ CFArrayAppendValue(pHidState->pFifoEventQueue, (void *)pEvent);
+
+ LogRel2(("IOHID: KBD %d: Modifier Key-Down event\n", (int)pKbd->idxPosition));
+ }
+ else
+ LogRel2(("IOHID: Modifier Key-Up event. Unable to find memory for KBD %d event\n", (int)pKbd->idxPosition));
+}
+
+/** IOkit and Carbon key press callbacks helper: CapsLock timeout checker.
+ *
+ * Returns FALSE if CAPS LOCK timeout not occurred and its state still was not switched (Apple kbd).
+ * Returns TRUE if CAPS LOCK timeout occurred and its state was switched (Apple kbd).
+ * Returns TRUE for non-Apple kbd. */
+static bool darwinKbdCapsEventMatches(VBoxKbdEvent_t *pEvent, bool fCapsLed)
+{
+ // CapsLock timeout is only applicable if conditions
+ // below are satisfied:
+ //
+ // a) Key pressed on Apple keyboard
+ // b) CapsLed is OFF at the moment when CapsLock key is pressed
+
+ bool fAppleKeyboard = (pEvent->pKbd->cCapsLockTimeout > 0);
+
+ /* Apple keyboard */
+ if (fAppleKeyboard && !fCapsLed)
+ {
+ uint64_t tsDiff = RTTimeSystemMilliTS() - pEvent->tsKeyDown;
+ if (tsDiff < pEvent->pKbd->cCapsLockTimeout)
+ return false;
+ }
+
+ return true;
+}
+
+/** IOKit key press callback helper: take care about key-up event.
+ * This code should be executed within a critical section under pHidState->fifoEventQueueLock. */
+static void darwinHidInputCbKeyUp(VBoxKbdState_t *pKbd, uint32_t iKeyCode, VBoxHidsState_t *pHidState)
+{
+ CFIndex iQueue = 0;
+ VBoxKbdEvent_t *pEvent = NULL;
+
+ // Key-up event assumes that key-down event occured previously. If so, an event
+ // data should be in event queue. Attempt to find it.
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pFifoEventQueue); i++)
+ {
+ VBoxKbdEvent_t *pCachedEvent = (VBoxKbdEvent_t *)CFArrayGetValueAtIndex(pHidState->pFifoEventQueue, i);
+
+ if (pCachedEvent && pCachedEvent->pKbd == pKbd && pCachedEvent->iKeyCode == iKeyCode)
+ {
+ pEvent = pCachedEvent;
+ iQueue = i;
+ break;
+ }
+ }
+
+ /* Event found. */
+ if (pEvent)
+ {
+ // NUM LOCK should not have timeout and its press should immidiately trigger Carbon callback.
+ // Therefore, if it is still in queue this is a problem because it was not handled by Carbon callback.
+ // This mean that NUM LOCK is most likely out of sync.
+ if (iKeyCode == kHIDUsage_KeypadNumLock)
+ {
+ LogRel2(("IOHID: KBD %d: Modifier Key-Up event. Key-Down event was not habdled by Carbon callback. "
+ "NUM LOCK is most likely out of sync\n", (int)pKbd->idxPosition));
+ }
+ else if (iKeyCode == kHIDUsage_KeyboardCapsLock)
+ {
+ // If CAPS LOCK key-press event still not match CAPS LOCK timeout criteria, Carbon callback
+ // should not be triggered for this event at all. Threfore, event should be removed from queue.
+ if (!darwinKbdCapsEventMatches(pEvent, pHidState->guestState.fCapsLockOn))
+ {
+ CFArrayRemoveValueAtIndex(pHidState->pFifoEventQueue, iQueue);
+
+ LogRel2(("IOHID: KBD %d: Modifier Key-Up event on Apple keyboard. Key-Down event was triggered %llu ms "
+ "ago. Carbon event should not be triggered, removed from queue\n", (int)pKbd->idxPosition,
+ RTTimeSystemMilliTS() - pEvent->tsKeyDown));
+ free(pEvent);
+ }
+ else
+ {
+ // CAPS LOCK key-press event matches to CAPS LOCK timeout criteria and still present in queue.
+ // This might mean that Carbon callback was triggered for this event, but cached keyboard state was not updated.
+ // It also might mean that Carbon callback still was not triggered, but it will be soon.
+ // Threfore, CAPS LOCK might be out of sync.
+ LogRel2(("IOHID: KBD %d: Modifier Key-Up event. Key-Down event was triggered %llu ms "
+ "ago and still was not handled by Carbon callback. CAPS LOCK might out of sync if "
+ "Carbon will not handle this\n", (int)pKbd->idxPosition, RTTimeSystemMilliTS() - pEvent->tsKeyDown));
+ }
+ }
+ }
+ else
+ LogRel2(("IOHID: KBD %d: Modifier Key-Up event. Modifier state change was "
+ "successfully handled by Carbon callback\n", (int)pKbd->idxPosition));
+}
+
+/** IOKit key press callback. Triggered before Carbon callback. We remember which keyboard produced a keypress here. */
+static void darwinHidInputCallback(void *pData, IOReturn unused, void *unused1, IOHIDValueRef pValueRef)
+{
+ (void)unused;
+ (void)unused1;
+
+ AssertReturnVoid(pValueRef);
+
+ IOHIDElementRef pElementRef = IOHIDValueGetElement(pValueRef);
+ AssertReturnVoid(pElementRef);
+
+ uint32_t usage = IOHIDElementGetUsage(pElementRef);
+
+ if (IOHIDElementGetUsagePage(pElementRef) == kHIDPage_KeyboardOrKeypad) /* Keyboard or keypad event */
+ if (usage == kHIDUsage_KeyboardCapsLock || /* CapsLock key has been pressed */
+ usage == kHIDUsage_KeypadNumLock) /* ... or NumLock key has been pressed */
+ {
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)pData;
+
+ if (pKbd && pKbd->pParentContainer)
+ {
+ bool fKeyDown = (IOHIDValueGetIntegerValue(pValueRef) == 1);
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer;
+
+ AssertReturnVoid(pHidState);
+
+ if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
+ return ;
+
+ /* Handle corresponding event. */
+ if (fKeyDown)
+ darwinHidInputCbKeyDown(pKbd, usage, pHidState);
+ else
+ darwinHidInputCbKeyUp(pKbd, usage, pHidState);
+
+ RTSemMutexRelease(pHidState->fifoEventQueueLock);
+ }
+ else
+ LogRel2(("IOHID: No KBD: A modifier key has been pressed\n"));
+ }
+}
+
+/** Carbon key press callback helper: find last occured KBD event in queue
+ * (ignoring those events which do not match CAPS LOCK timeout criteria).
+ * Once event found, it is removed from queue. This code should be executed
+ * within a critical section under pHidState->fifoEventQueueLock. */
+static VBoxKbdEvent_t *darwinCarbonCbFindEvent(VBoxHidsState_t *pHidState)
+{
+ VBoxKbdEvent_t *pEvent = NULL;
+
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pFifoEventQueue); i++)
+ {
+ pEvent = (VBoxKbdEvent_t *)CFArrayGetValueAtIndex(pHidState->pFifoEventQueue, i);
+
+ /* Paranoia: skip potentially dangerous data items. */
+ if (!pEvent || !pEvent->pKbd) continue;
+
+ if ( pEvent->iKeyCode == kHIDUsage_KeypadNumLock
+ || (pEvent->iKeyCode == kHIDUsage_KeyboardCapsLock && darwinKbdCapsEventMatches(pEvent, pHidState->guestState.fCapsLockOn)))
+ {
+ /* Found one. Remove it from queue. */
+ CFArrayRemoveValueAtIndex(pHidState->pFifoEventQueue, i);
+
+ LogRel2(("CARBON: Found event in queue: %d (KBD %d, tsKeyDown=%llu, pressed %llu ms ago)\n", (int)i,
+ (int)pEvent->pKbd->idxPosition, pEvent->tsKeyDown, RTTimeSystemMilliTS() - pEvent->tsKeyDown));
+
+ break;
+ }
+ else
+ LogRel2(("CARBON: Skip keyboard event from KBD %d, key pressed %llu ms ago\n",
+ (int)pEvent->pKbd->idxPosition, RTTimeSystemMilliTS() - pEvent->tsKeyDown));
+
+ pEvent = NULL;
+ }
+
+ return pEvent;
+}
+
+/** Carbon key press callback. Triggered after IOKit callback. */
+static CGEventRef darwinCarbonCallback(CGEventTapProxy unused, CGEventType unused1, CGEventRef pEventRef, void *pData)
+{
+ (void)unused;
+ (void)unused1;
+
+ CGEventFlags fMask = CGEventGetFlags(pEventRef);
+ bool fCaps = (bool)(fMask & NX_ALPHASHIFTMASK);
+ bool fNum = (bool)(fMask & NX_NUMERICPADMASK);
+ CGKeyCode key = CGEventGetIntegerValueField(pEventRef, kCGKeyboardEventKeycode);
+
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pData;
+ AssertReturn(pHidState, pEventRef);
+
+ if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
+ return pEventRef;
+
+ if (key == kHIDUsage_KeyboardCapsLock ||
+ key == kHIDUsage_KeypadNumLock)
+ {
+ /* Attempt to find an event queued by IOKit callback. */
+ VBoxKbdEvent_t *pEvent = darwinCarbonCbFindEvent(pHidState);
+ if (pEvent)
+ {
+ VBoxKbdState_t *pKbd = pEvent->pKbd;
+
+ LogRel2(("CARBON: KBD %d: caps=%s, num=%s. tsKeyDown=%llu, tsKeyUp=%llu [tsDiff=%llu ms]. %d events in queue.\n",
+ (int)pKbd->idxPosition, VBOX_BOOL_TO_STR_STATE(fCaps), VBOX_BOOL_TO_STR_STATE(fNum),
+ pEvent->tsKeyDown, RTTimeSystemMilliTS(), RTTimeSystemMilliTS() - pEvent->tsKeyDown,
+ CFArrayGetCount(pHidState->pFifoEventQueue)));
+
+ pKbd->LED.fCapsLockOn = fCaps;
+ pKbd->LED.fNumLockOn = fNum;
+
+ /* Silently resync last touched KBD device */
+ if (pHidState)
+ {
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ (void)darwinSetDeviceLedsState(pKbd->pDevice,
+ elementMatchingDict,
+ pHidState->guestState.fNumLockOn,
+ pHidState->guestState.fCapsLockOn,
+ pHidState->guestState.fScrollLockOn);
+
+ CFRelease(elementMatchingDict);
+ }
+ }
+
+ free(pEvent);
+ }
+ else
+ LogRel2(("CARBON: No KBD to take care when modifier key has been pressed: caps=%s, num=%s (%d events in queue)\n",
+ VBOX_BOOL_TO_STR_STATE(fCaps), VBOX_BOOL_TO_STR_STATE(fNum), CFArrayGetCount(pHidState->pFifoEventQueue)));
+ }
+
+ RTSemMutexRelease(pHidState->fifoEventQueueLock);
+
+ return pEventRef;
+}
+
+/** Helper function to obtain interface for IOUSBInterface IOService. */
+static IOUSBDeviceInterface ** darwinQueryUsbHidInterfaceInterface(io_service_t service)
+{
+ kern_return_t rc;
+ IOCFPlugInInterface **ppPluginInterface = NULL;
+ SInt32 iScore;
+
+ rc = IOCreatePlugInInterfaceForService(service, kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &ppPluginInterface, &iScore);
+
+ if (rc == kIOReturnSuccess && ppPluginInterface != NULL)
+ {
+ IOUSBDeviceInterface **ppUsbDeviceInterface = NULL;
+
+ rc = (*ppPluginInterface)->QueryInterface(ppPluginInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+ (LPVOID *)&ppUsbDeviceInterface);
+ IODestroyPlugInInterface(ppPluginInterface);
+
+ if (rc == kIOReturnSuccess && ppUsbDeviceInterface != NULL)
+ return ppUsbDeviceInterface;
+ else
+ LogRel2(("Failed to query plugin interface for USB device\n"));
+
+ }
+ else
+ LogRel2(("Failed to create plugin interface for USB device\n"));
+
+ return NULL;
+}
+
+/** Helper function for IOUSBInterface IOService general interest notification callback: resync LEDs. */
+static void darwinUsbHidResyncLeds(VBoxKbdState_t *pKbd)
+{
+ AssertReturnVoid(pKbd);
+
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer;
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ LogRel2(("Do HID device resync at location 0x%X \n", pKbd->idLocation));
+ (void)darwinSetDeviceLedsState(pKbd->pDevice, elementMatchingDict,
+ pHidState->guestState.fNumLockOn, pHidState->guestState.fCapsLockOn, pHidState->guestState.fScrollLockOn);
+ CFRelease(elementMatchingDict);
+ }
+}
+
+/** IOUSBInterface IOService general interest notification callback. When we receive it, we do
+ * silently resync kbd which has just changed its state. */
+static void darwinUsbHidGeneralInterestCb(void *pData, io_service_t unused1, natural_t msg, void *unused2)
+{
+ NOREF(unused1);
+ NOREF(unused2);
+
+ AssertReturnVoid(pData);
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)pData;
+
+ switch (msg)
+ {
+ case kIOUSBMessagePortHasBeenSuspended:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenSuspended for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ break;
+ }
+
+ case kIOUSBMessagePortHasBeenResumed:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenResumed for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ break;
+ }
+
+ case kIOUSBMessagePortHasBeenReset:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessagePortHasBeenReset for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ darwinUsbHidResyncLeds(pKbd);
+ break;
+ }
+
+ case kIOUSBMessageCompositeDriverReconfigured:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOUSBMessageCompositeDriverReconfigured for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ break;
+ }
+
+ case kIOMessageServiceWasClosed:
+ {
+ LogRel2(("IOUSBInterface IOService general interest notification kIOMessageServiceWasClosed for KBD %d (Location ID: 0x%X)\n",
+ (int)(pKbd->idxPosition), pKbd->idLocation));
+ break;
+ }
+
+ default:
+ LogRel2(("IOUSBInterface IOService general interest notification 0x%X for KBD %d (Location ID: 0x%X)\n",
+ msg, (int)(pKbd->idxPosition), pKbd->idLocation));
+ }
+}
+
+/** Get pre-cached KBD device by its Location ID. */
+static VBoxKbdState_t *darwinUsbHidQueryKbdByLocationId(uint32_t idLocation, VBoxHidsState_t *pHidState)
+{
+ AssertReturn(pHidState, NULL);
+
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pDeviceCollection); i++)
+ {
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pHidState->pDeviceCollection, i);
+ if (pKbd && pKbd->idLocation == idLocation)
+ {
+ LogRel2(("Lookup USB HID Device by location ID 0x%X: found match\n", idLocation));
+ return pKbd;
+ }
+ }
+
+ LogRel2(("Lookup USB HID Device by location ID 0x%X: no matches found:\n", idLocation));
+
+ return NULL;
+}
+
+/** IOUSBInterface IOService match notification callback: issued when IOService instantinates.
+ * We subscribe to general interest notifications for available IOServices here. */
+static void darwinUsbHidDeviceMatchCb(void *pData, io_iterator_t iter)
+{
+ AssertReturnVoid(pData);
+
+ io_service_t service;
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pData;
+
+ while ((service = IOIteratorNext(iter)))
+ {
+ kern_return_t rc;
+
+ IOUSBDeviceInterface **ppUsbDeviceInterface = darwinQueryUsbHidInterfaceInterface(service);
+
+ if (ppUsbDeviceInterface)
+ {
+ uint8_t idDeviceClass, idDeviceSubClass;
+ UInt32 idLocation;
+
+ rc = (*ppUsbDeviceInterface)->GetLocationID (ppUsbDeviceInterface, &idLocation); AssertMsg(rc == 0, ("Failed to get Location ID"));
+ rc = (*ppUsbDeviceInterface)->GetDeviceClass (ppUsbDeviceInterface, &idDeviceClass); AssertMsg(rc == 0, ("Failed to get Device Class"));
+ rc = (*ppUsbDeviceInterface)->GetDeviceSubClass(ppUsbDeviceInterface, &idDeviceSubClass); AssertMsg(rc == 0, ("Failed to get Device Subclass"));
+
+ if (idDeviceClass == kUSBHIDInterfaceClass && idDeviceSubClass == kUSBHIDBootInterfaceSubClass)
+ {
+ VBoxKbdState_t *pKbd = darwinUsbHidQueryKbdByLocationId((uint32_t)idLocation, pHidState);
+
+ if (pKbd)
+ {
+ rc = IOServiceAddInterestNotification(pHidState->pNotificationPrortRef, service, kIOGeneralInterest,
+ darwinUsbHidGeneralInterestCb, pKbd, &pHidState->pUsbHidGeneralInterestNotify);
+
+ AssertMsg(rc == 0, ("Failed to add general interest notification"));
+
+ LogRel2(("Found HID device at location 0x%X: class 0x%X, subclass 0x%X\n", idLocation, idDeviceClass, idDeviceSubClass));
+ }
+ }
+
+ rc = (*ppUsbDeviceInterface)->Release(ppUsbDeviceInterface); AssertMsg(rc == 0, ("Failed to release USB device interface"));
+ }
+
+ IOObjectRelease(service);
+ }
+}
+
+/** Register IOUSBInterface IOService match notification callback in order to recync KBD
+ * device when it reports state change. */
+static int darwinUsbHidSubscribeInterestNotifications(VBoxHidsState_t *pHidState)
+{
+ AssertReturn(pHidState, kIOReturnBadArgument);
+
+ int rc = kIOReturnNoMemory;
+ CFDictionaryRef pDictionary = IOServiceMatching(kIOUSBInterfaceClassName);
+
+ if (pDictionary)
+ {
+ pHidState->pNotificationPrortRef = IONotificationPortCreate(kIOMasterPortDefault);
+ if (pHidState->pNotificationPrortRef)
+ {
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(pHidState->pNotificationPrortRef), kCFRunLoopDefaultMode);
+
+ rc = IOServiceAddMatchingNotification(pHidState->pNotificationPrortRef, kIOMatchedNotification,
+ pDictionary, darwinUsbHidDeviceMatchCb, pHidState,
+ &pHidState->pUsbHidDeviceMatchNotify);
+
+ if (rc == kIOReturnSuccess && pHidState->pUsbHidDeviceMatchNotify != IO_OBJECT_NULL)
+ {
+ darwinUsbHidDeviceMatchCb(pHidState, pHidState->pUsbHidDeviceMatchNotify);
+ LogRel2(("Successfully subscribed to IOUSBInterface IOService match notifications\n"));
+ }
+ else
+ LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: subscription error 0x%X\n", rc));
+ }
+ else
+ LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: unable to create notification port\n"));
+ }
+ else
+ LogRel2(("Failed to subscribe to IOUSBInterface IOService match notifications: no memory\n"));
+
+ return rc;
+}
+
+/** Remove IOUSBInterface IOService match notification subscription. */
+static void darwinUsbHidUnsubscribeInterestNotifications(VBoxHidsState_t *pHidState)
+{
+ AssertReturnVoid(pHidState);
+
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(pHidState->pNotificationPrortRef), kCFRunLoopDefaultMode);
+ IONotificationPortDestroy(pHidState->pNotificationPrortRef);
+ pHidState->pNotificationPrortRef = NULL;
+
+ LogRel2(("Successfully un-subscribed from IOUSBInterface IOService match notifications\n"));
+}
+
+/** This callback is called when user physically removes HID device. We remove device from cache here. */
+static void darwinHidRemovalCallback(void *pData, IOReturn unused, void *unused1)
+{
+ (void)unused;
+ (void)unused1;
+
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)pData; AssertReturnVoid(pKbd);
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer; AssertReturnVoid(pHidState);
+
+ AssertReturnVoid(pHidState->pDeviceCollection);
+
+ LogRel2(("Forget KBD %d\n", (int)pKbd->idxPosition));
+
+ //if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
+ // return ;
+
+ CFArrayRemoveValueAtIndex(pHidState->pDeviceCollection, pKbd->idxPosition);
+ free(pKbd);
+
+ //RTSemMutexRelease(pHidState->fifoEventQueueLock);
+}
+
+/** Check if we already cached given device */
+static bool darwinIsDeviceInCache(VBoxHidsState_t *pState, IOHIDDeviceRef pDevice)
+{
+ AssertReturn(pState, false);
+ AssertReturn(pState->pDeviceCollection, false);
+
+ for (CFIndex i = 0; i < CFArrayGetCount(pState->pDeviceCollection); i++)
+ {
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pState->pDeviceCollection, i);
+ if (pKbd && pKbd->pDevice == pDevice)
+ return true;
+ }
+
+ return false;
+}
+
+/** Add device to cache. */
+static void darwinHidAddDevice(VBoxHidsState_t *pHidState, IOHIDDeviceRef pDevice, bool fApplyLedState)
+{
+ int rc;
+
+ if (!darwinIsDeviceInCache(pHidState, pDevice))
+ {
+ if (IOHIDDeviceConformsTo(pDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)
+ && darwinHidDeviceSupported(pDevice))
+ {
+ VBoxKbdState_t *pKbd = (VBoxKbdState_t *)malloc(sizeof(VBoxKbdState_t));
+ if (pKbd)
+ {
+ pKbd->pDevice = pDevice;
+ pKbd->pParentContainer = (void *)pHidState;
+ pKbd->idxPosition = CFArrayGetCount(pHidState->pDeviceCollection);
+ pKbd->idLocation = darwinHidLocationId(pDevice);
+
+ // Some Apple keyboards have CAPS LOCK key timeout. According to corresponding
+ // kext plist files, it is equals to 75 ms. For such devices we only add info into our FIFO event
+ // queue if the time between Key-Down and Key-Up events >= 75 ms.
+ pKbd->cCapsLockTimeout = (darwinHidVendorId(pKbd->pDevice) == kIOUSBVendorIDAppleComputer) ? 75 : 0;
+
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ rc = darwinGetDeviceLedsState(pKbd->pDevice,
+ elementMatchingDict,
+ &pKbd->LED.fNumLockOn,
+ &pKbd->LED.fCapsLockOn,
+ &pKbd->LED.fScrollLockOn);
+
+ // This should never happen, but if happened -- mark all the leds of current
+ // device as turned OFF.
+ if (rc != 0)
+ {
+ LogRel2(("Unable to get leds state for device %d. Mark leds as turned off\n", (int)(pKbd->idxPosition)));
+ pKbd->LED.fNumLockOn =
+ pKbd->LED.fCapsLockOn =
+ pKbd->LED.fScrollLockOn = false;
+ }
+
+ /* Register per-device removal callback */
+ IOHIDDeviceRegisterRemovalCallback(pKbd->pDevice, darwinHidRemovalCallback, (void *)pKbd);
+
+ /* Register per-device input callback */
+ IOHIDDeviceRegisterInputValueCallback(pKbd->pDevice, darwinHidInputCallback, (void *)pKbd);
+ IOHIDDeviceScheduleWithRunLoop(pKbd->pDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+ CFArrayAppendValue(pHidState->pDeviceCollection, (void *)pKbd);
+
+ LogRel2(("Saved LEDs for KBD %d (%p): fNumLockOn=%s, fCapsLockOn=%s, fScrollLockOn=%s\n",
+ (int)pKbd->idxPosition, pKbd, VBOX_BOOL_TO_STR_STATE(pKbd->LED.fNumLockOn), VBOX_BOOL_TO_STR_STATE(pKbd->LED.fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(pKbd->LED.fScrollLockOn)));
+
+ if (fApplyLedState)
+ {
+ rc = darwinSetDeviceLedsState(pKbd->pDevice, elementMatchingDict, pHidState->guestState.fNumLockOn,
+ pHidState->guestState.fCapsLockOn, pHidState->guestState.fScrollLockOn);
+ if (rc != 0)
+ LogRel2(("Unable to apply guest state to newly attached device\n"));
+ }
+
+ CFRelease(elementMatchingDict);
+ return;
+ }
+
+ free(pKbd);
+ }
+ }
+ }
+}
+
+/** This callback is called when new HID device discovered by IOHIDManager. We add devices to cache here and only here! */
+static void darwinHidMatchingCallback(void *pData, IOReturn unused, void *unused1, IOHIDDeviceRef pDevice)
+{
+ (void)unused;
+ (void)unused1;
+
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pData;
+
+ AssertReturnVoid(pHidState);
+ AssertReturnVoid(pHidState->pDeviceCollection);
+ AssertReturnVoid(pDevice);
+
+ darwinHidAddDevice(pHidState, pDevice, true);
+}
+
+/** Register Carbon key press callback. */
+static int darwinAddCarbonHandler(VBoxHidsState_t *pHidState)
+{
+ CFMachPortRef pTapRef;
+ CGEventMask fMask = CGEventMaskBit(kCGEventFlagsChanged);
+
+ AssertReturn(pHidState, kIOReturnError);
+
+ /* Create FIFO event queue for keyboard events */
+ pHidState->pFifoEventQueue = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
+ AssertReturn(pHidState->pFifoEventQueue, kIOReturnError);
+
+ /* Create Lock for FIFO event queue */
+ if (RT_FAILURE(RTSemMutexCreate(&pHidState->fifoEventQueueLock)))
+ {
+ LogRel2(("Unable to create Lock for FIFO event queue\n"));
+ CFRelease(pHidState->pFifoEventQueue);
+ pHidState->pFifoEventQueue = NULL;
+ return kIOReturnError;
+ }
+
+ pTapRef = CGEventTapCreate(kCGSessionEventTap, kCGTailAppendEventTap, kCGEventTapOptionDefault, fMask,
+ darwinCarbonCallback, (void *)pHidState);
+ if (pTapRef)
+ {
+ CFRunLoopSourceRef pLoopSourceRef;
+ pLoopSourceRef = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, pTapRef, 0);
+ if (pLoopSourceRef)
+ {
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), pLoopSourceRef, kCFRunLoopDefaultMode);
+ CGEventTapEnable(pTapRef, true);
+
+ pHidState->pTapRef = pTapRef;
+ pHidState->pLoopSourceRef = pLoopSourceRef;
+
+ return 0;
+ }
+ else
+ LogRel2(("Unable to create a loop source\n"));
+
+ CFRelease(pTapRef);
+ }
+ else
+ LogRel2(("Unable to create an event tap\n"));
+
+ return kIOReturnError;
+}
+
+/** Remove Carbon key press callback. */
+static void darwinRemoveCarbonHandler(VBoxHidsState_t *pHidState)
+{
+ AssertReturnVoid(pHidState);
+ AssertReturnVoid(pHidState->pTapRef);
+ AssertReturnVoid(pHidState->pLoopSourceRef);
+ AssertReturnVoid(pHidState->pFifoEventQueue);
+
+ CGEventTapEnable(pHidState->pTapRef, false);
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pHidState->pLoopSourceRef, kCFRunLoopDefaultMode);
+ CFRelease(pHidState->pLoopSourceRef);
+ CFRelease(pHidState->pTapRef);
+
+ RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT);
+ CFRelease(pHidState->pFifoEventQueue);
+ pHidState->pFifoEventQueue = NULL;
+ RTSemMutexRelease(pHidState->fifoEventQueueLock);
+
+ RTSemMutexDestroy(pHidState->fifoEventQueueLock);
+}
+
+#endif /* !VBOX_WITH_KBD_LEDS_SYNC */
+
+
+void *DarwinHidDevicesKeepLedsState()
+{
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+ IOReturn rc;
+ VBoxHidsState_t *pHidState;
+
+ pHidState = (VBoxHidsState_t *)malloc(sizeof(VBoxHidsState_t));
+ AssertReturn(pHidState, NULL);
+
+ pHidState->hidManagerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+ if (pHidState->hidManagerRef)
+ {
+ CFDictionaryRef deviceMatchingDictRef = darwinQueryLedDeviceMatchingDictionary();
+ if (deviceMatchingDictRef)
+ {
+ IOHIDManagerScheduleWithRunLoop(pHidState->hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ IOHIDManagerSetDeviceMatching(pHidState->hidManagerRef, deviceMatchingDictRef);
+
+ rc = IOHIDManagerOpen(pHidState->hidManagerRef, kIOHIDOptionsTypeNone);
+ if (rc == kIOReturnSuccess)
+ {
+ pHidState->pDeviceCollection = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
+ if (pHidState->pDeviceCollection)
+ {
+ if (darwinAddCarbonHandler(pHidState) == 0)
+ {
+ /* Populate cache with HID devices */
+ CFSetRef pDevicesSet = IOHIDManagerCopyDevices(pHidState->hidManagerRef);
+ if (pDevicesSet)
+ {
+ CFIndex cDevices = CFSetGetCount(pDevicesSet);
+
+ IOHIDDeviceRef *ppDevices = (IOHIDDeviceRef *)malloc((size_t)cDevices * sizeof(IOHIDDeviceRef));
+ if (ppDevices)
+ {
+ CFSetGetValues(pDevicesSet, (const void **)ppDevices);
+ for (CFIndex i= 0; i < cDevices; i++)
+ darwinHidAddDevice(pHidState, (IOHIDDeviceRef)ppDevices[i], false);
+
+ free(ppDevices);
+ }
+
+ CFRelease(pDevicesSet);
+ }
+
+ IOHIDManagerRegisterDeviceMatchingCallback(pHidState->hidManagerRef, darwinHidMatchingCallback, (void *)pHidState);
+
+ CFRelease(deviceMatchingDictRef);
+
+ /* This states should be set on broadcast */
+ pHidState->guestState.fNumLockOn =
+ pHidState->guestState.fCapsLockOn =
+ pHidState->guestState.fScrollLockOn = false;
+
+ /* Finally, subscribe to USB HID notifications in order to prevent LED artifacts on
+ automatic power management */
+ if (darwinUsbHidSubscribeInterestNotifications(pHidState) == 0)
+ return pHidState;
+ }
+ }
+
+ rc = IOHIDManagerClose(pHidState->hidManagerRef, 0);
+ if (rc != kIOReturnSuccess)
+ LogRel2(("Warning! Something went wrong in attempt to close HID device manager!\n"));
+ }
+
+ CFRelease(deviceMatchingDictRef);
+ }
+
+ CFRelease(pHidState->hidManagerRef);
+ }
+
+ free(pHidState);
+
+ return NULL;
+#else /* !VBOX_WITH_KBD_LEDS_SYNC */
+ return NULL;
+#endif
+}
+
+
+int DarwinHidDevicesApplyAndReleaseLedsState(void *pState)
+{
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pState;
+ IOReturn rc, rc2 = 0;
+
+ AssertReturn(pHidState, kIOReturnError);
+
+ darwinUsbHidUnsubscribeInterestNotifications(pHidState);
+
+ /* Need to unregister Carbon stuff first: */
+ darwinRemoveCarbonHandler(pHidState);
+
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ /* Restore LEDs: */
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pDeviceCollection); i++)
+ {
+ /* Cycle through supported devices only. */
+ VBoxKbdState_t *pKbd;
+ pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pHidState->pDeviceCollection, i);
+
+ if (pKbd)
+ {
+ rc = darwinSetDeviceLedsState(pKbd->pDevice,
+ elementMatchingDict,
+ pKbd->LED.fNumLockOn,
+ pKbd->LED.fCapsLockOn,
+ pKbd->LED.fScrollLockOn);
+ if (rc != 0)
+ {
+ LogRel2(("Unable to restore led states for device (%d)!\n", (int)i));
+ rc2 = kIOReturnError;
+ }
+
+ IOHIDDeviceUnscheduleFromRunLoop(pKbd->pDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+ LogRel2(("Restored LEDs for KBD %d (%p): fNumLockOn=%s, fCapsLockOn=%s, fScrollLockOn=%s\n",
+ (int)i, pKbd, VBOX_BOOL_TO_STR_STATE(pKbd->LED.fNumLockOn), VBOX_BOOL_TO_STR_STATE(pKbd->LED.fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(pKbd->LED.fScrollLockOn)));
+
+ free(pKbd);
+ }
+ }
+
+ CFRelease(elementMatchingDict);
+ }
+
+ /* Free resources: */
+ CFRelease(pHidState->pDeviceCollection);
+
+ rc = IOHIDManagerClose(pHidState->hidManagerRef, 0);
+ if (rc != kIOReturnSuccess)
+ {
+ LogRel2(("Warning! Something went wrong in attempt to close HID device manager!\n"));
+ rc2 = kIOReturnError;
+ }
+
+ IOHIDManagerUnscheduleFromRunLoop(pHidState->hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+ CFRelease(pHidState->hidManagerRef);
+
+ free(pHidState);
+
+ return rc2;
+#else /* !VBOX_WITH_KBD_LEDS_SYNC */
+ (void)pState;
+ return 0;
+#endif /* !VBOX_WITH_KBD_LEDS_SYNC */
+}
+
+void DarwinHidDevicesBroadcastLeds(void *pState, bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+#ifdef VBOX_WITH_KBD_LEDS_SYNC
+ VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pState;
+ IOReturn rc;
+
+ AssertReturnVoid(pHidState);
+ AssertReturnVoid(pHidState->pDeviceCollection);
+
+ CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
+ if (elementMatchingDict)
+ {
+ LogRel2(("Start LEDs broadcast: fNumLockOn=%s, fCapsLockOn=%s, fScrollLockOn=%s\n",
+ VBOX_BOOL_TO_STR_STATE(fNumLockOn), VBOX_BOOL_TO_STR_STATE(fCapsLockOn), VBOX_BOOL_TO_STR_STATE(fScrollLockOn)));
+
+ for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pDeviceCollection); i++)
+ {
+ /* Cycle through supported devices only. */
+ VBoxKbdState_t *pKbd;
+ pKbd = (VBoxKbdState_t *)CFArrayGetValueAtIndex(pHidState->pDeviceCollection, i);
+
+ if (pKbd && darwinHidDeviceSupported(pKbd->pDevice))
+ {
+ rc = darwinSetDeviceLedsState(pKbd->pDevice,
+ elementMatchingDict,
+ fNumLockOn,
+ fCapsLockOn,
+ fScrollLockOn);
+ if (rc != 0)
+ LogRel2(("Unable to restore led states for device (%d)!\n", (int)i));
+ }
+ }
+
+ LogRel2(("LEDs broadcast completed\n"));
+
+ CFRelease(elementMatchingDict);
+ }
+
+ /* Dynamically attached device will use these states: */
+ pHidState->guestState.fNumLockOn = fNumLockOn;
+ pHidState->guestState.fCapsLockOn = fCapsLockOn;
+ pHidState->guestState.fScrollLockOn = fScrollLockOn;
+#else /* !VBOX_WITH_KBD_LEDS_SYNC */
+ (void)fNumLockOn;
+ (void)fCapsLockOn;
+ (void)fScrollLockOn;
+#endif /* !VBOX_WITH_KBD_LEDS_SYNC */
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.h
new file mode 100644
index 00000000..359b1622
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.h
@@ -0,0 +1,96 @@
+/* $Id: DarwinKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Darwin Keyboard specific tasks.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_DarwinKeyboard_h
+#define FEQT_INCLUDED_SRC_platform_darwin_DarwinKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/cdefs.h>
+
+/* External includes: */
+#include <CoreFoundation/CFBase.h>
+
+
+RT_C_DECLS_BEGIN
+
+/** Private hack for missing rightCmdKey enum. */
+#define kEventKeyModifierRightCmdKeyMask (1<<27)
+
+/** The scancode mask. */
+#define VBOXKEY_SCANCODE_MASK 0x007f
+/** Extended key. */
+#define VBOXKEY_EXTENDED 0x0080
+/** Modifier key. */
+#define VBOXKEY_MODIFIER 0x0400
+/** Lock key (like num lock and caps lock). */
+#define VBOXKEY_LOCK 0x0800
+
+/** Converts a darwin (virtual) key code to a set 1 scan code. */
+SHARED_LIBRARY_STUFF unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode);
+/** Adjusts the modifier mask left / right using the current keyboard state. */
+SHARED_LIBRARY_STUFF UInt32 DarwinAdjustModifierMask(UInt32 fModifiers, const void *pvCocoaEvent);
+/** Converts a single modifier to a set 1 scan code. */
+SHARED_LIBRARY_STUFF unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers);
+/** Converts a single modifier to a darwin keycode. */
+SHARED_LIBRARY_STUFF unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers);
+/** Converts a darwin keycode to a modifier mask. */
+SHARED_LIBRARY_STUFF UInt32 DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode);
+
+/** Disables or enabled global hot keys. */
+SHARED_LIBRARY_STUFF void DarwinDisableGlobalHotKeys(bool fDisable);
+
+/** Start grabbing keyboard events.
+ * @param fGlobalHotkeys Brings whether to disable global hotkeys or not. */
+SHARED_LIBRARY_STUFF void DarwinGrabKeyboard(bool fGlobalHotkeys);
+/** Reverses the actions taken by DarwinGrabKeyboard. */
+SHARED_LIBRARY_STUFF void DarwinReleaseKeyboard();
+
+/** Saves the states of leds for all HID devices attached to the system and return it. */
+SHARED_LIBRARY_STUFF void *DarwinHidDevicesKeepLedsState();
+
+/** Applies LEDs @a pState release its resources afterwards. */
+SHARED_LIBRARY_STUFF int DarwinHidDevicesApplyAndReleaseLedsState(void *pState);
+/** Set states for host keyboard LEDs.
+ * @note This function will set led values for all
+ * keyboard devices attached to the system.
+ * @param pState Brings the pointer to saved LEDs state.
+ * @param fNumLockOn Turns on NumLock led if TRUE, off otherwise
+ * @param fCapsLockOn Turns on CapsLock led if TRUE, off otherwise
+ * @param fScrollLockOn Turns on ScrollLock led if TRUE, off otherwise */
+SHARED_LIBRARY_STUFF void DarwinHidDevicesBroadcastLeds(void *pState, bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn);
+
+RT_C_DECLS_END
+
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_DarwinKeyboard_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/DockIconPreview.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DockIconPreview.h
new file mode 100644
index 00000000..313676f0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/DockIconPreview.h
@@ -0,0 +1,49 @@
+/* $Id: DockIconPreview.h $ */
+/** @file
+ * VBox Qt GUI - UIDockIconPreview class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_DockIconPreview_h
+#define FEQT_INCLUDED_SRC_platform_darwin_DockIconPreview_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UICocoaDockIconPreview.h"
+
+
+/** UICocoaDockIconPreview extension to be used for VM. */
+class UIDockIconPreview : public UICocoaDockIconPreview
+{
+public:
+
+ /** Constructor taking passed @a pSession and @a overlayImage. */
+ UIDockIconPreview(UISession *pSession, const QPixmap& overlayImage)
+ : UICocoaDockIconPreview(pSession, overlayImage) {}
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_DockIconPreview_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/Info.plist b/src/VBox/Frontends/VirtualBox/src/platform/darwin/Info.plist
new file mode 100644
index 00000000..44d2d00c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/Info.plist
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundlePackageType</key> <string>APPL</string>
+ <key>CFBundleSignature</key> <string>VBOX</string>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.app.VirtualBox</string>
+ <key>CFBundleName</key> <string>VirtualBox</string>
+ <key>CFBundleExecutable</key> <string>VirtualBox</string>
+ <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ Manager @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+ <key>CFBundleIconFile</key> <string>virtualbox</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>LSCanProvideIMVideoDataSource</key> <false/>
+ <key>NSHighResolutionCapable</key> <true/>
+ <key>NSSupportsAutomaticGraphicsSwitching</key><true/>
+ <key>NSCameraUsageDescription</key> <string>VirtualBox needs camera access for emulated webcam passthrough</string>
+ <key>NSMicrophoneUsageDescription</key> <string>VirtualBox needs microphone access for guest audio input</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeName</key> <string>VirtualBox Extension Pack</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vbox-extpack</string></array>
+ <key>CFBundleTypeRole</key> <string>Viewer</string>
+ <key>LSHandlerRank</key> <string>Owner</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vbox-extpack</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Open Virtualization Format</string>
+ <key>CFBundleTypeExtensions</key> <array><string>ovf</string></array>
+ <key>CFBundleTypeRole</key> <string>Viewer</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-ovf</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Open Virtualization Format Archive</string>
+ <key>CFBundleTypeExtensions</key> <array><string>ova</string></array>
+ <key>CFBundleTypeRole</key> <string>Viewer</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-ova</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Virtual Disk Image</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vdi</string></array>
+ <key>CFBundleTypeRole</key> <string>None</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vdi</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Virtual Machine Disk Format</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vmdk</string></array>
+ <key>CFBundleTypeRole</key> <string>None</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vmdk</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Virtual Hard Disk</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vhd</string></array>
+ <key>CFBundleTypeRole</key> <string>None</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vhd</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key> <string>Virtual Hard Disk</string>
+ <key>CFBundleTypeExtensions</key> <array><string>hdd</string></array>
+ <key>CFBundleTypeRole</key> <string>None</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-hdd</string>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/platform/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/PkgInfo b/src/VBox/Frontends/VirtualBox/src/platform/darwin/PkgInfo
new file mode 100644
index 00000000..30c80e6c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/PkgInfo
@@ -0,0 +1 @@
+APPLVBOX \ No newline at end of file
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.cpp
new file mode 100644
index 00000000..092d86a1
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.cpp
@@ -0,0 +1,144 @@
+/* $Id: UIAbstractDockIconPreview.cpp $ */
+/** @file
+ * VBox Qt GUI - Realtime Dock Icon Preview
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QStyle>
+
+/* GUI includes: */
+#include "UIAbstractDockIconPreview.h"
+#include "UIConverter.h"
+#include "UIExtraDataManager.h"
+#include "UIFrameBuffer.h"
+#include "UIMachineLogic.h"
+#include "UIMachineView.h"
+#include "UISession.h"
+#include "UICommon.h"
+
+/* COM includes: */
+#include "COMEnums.h"
+
+
+UIAbstractDockIconPreview::UIAbstractDockIconPreview(UISession * /* pSession */, const QPixmap& /* overlayImage */)
+{
+}
+
+void UIAbstractDockIconPreview::updateDockPreview(UIFrameBuffer *pFrameBuffer)
+{
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ Assert(cs);
+ /* Create the image copy of the framebuffer */
+ CGDataProviderRef dp = CGDataProviderCreateWithData(pFrameBuffer, pFrameBuffer->address(), pFrameBuffer->bitsPerPixel() / 8 * pFrameBuffer->width() * pFrameBuffer->height(), NULL);
+ Assert(dp);
+ CGImageRef ir = CGImageCreate(pFrameBuffer->width(), pFrameBuffer->height(), 8, 32, pFrameBuffer->bytesPerLine(), cs,
+ kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, dp, 0, false,
+ kCGRenderingIntentDefault);
+ Assert(ir);
+
+ /* Update the dock preview icon */
+ updateDockPreview(ir);
+
+ /* Release the temp data and image */
+ CGImageRelease(ir);
+ CGDataProviderRelease(dp);
+ CGColorSpaceRelease(cs);
+}
+
+UIAbstractDockIconPreviewHelper::UIAbstractDockIconPreviewHelper(UISession *pSession, const QPixmap& overlayImage)
+ : m_pSession(pSession)
+ , m_dockIconRect(CGRectMake(0, 0, 128, 128))
+ , m_dockMonitor(NULL)
+ , m_dockMonitorGlossy(NULL)
+ , m_updateRect(CGRectMake(0, 0, 0, 0))
+ , m_monitorRect(CGRectMake(0, 0, 0, 0))
+{
+ m_overlayImage = ::darwinToCGImageRef(&overlayImage);
+ Assert(m_overlayImage);
+}
+
+void* UIAbstractDockIconPreviewHelper::currentPreviewWindowId() const
+{
+ /* Get the MachineView which is currently previewed and return the win id
+ of the viewport. */
+ UIMachineView* pView = m_pSession->machineLogic()->dockPreviewView();
+ if (pView)
+ return (void*)pView->viewport()->winId();
+ return 0;
+}
+
+UIAbstractDockIconPreviewHelper::~UIAbstractDockIconPreviewHelper()
+{
+ CGImageRelease(m_overlayImage);
+ if (m_dockMonitor)
+ CGImageRelease(m_dockMonitor);
+ if (m_dockMonitorGlossy)
+ CGImageRelease(m_dockMonitorGlossy);
+}
+
+void UIAbstractDockIconPreviewHelper::initPreviewImages()
+{
+ if (!m_dockMonitor)
+ {
+ m_dockMonitor = ::darwinToCGImageRef("monitor.png");
+ Assert(m_dockMonitor);
+ /* Center it on the dock icon context */
+ m_monitorRect = centerRect(CGRectMake(0, 0,
+ CGImageGetWidth(m_dockMonitor),
+ CGImageGetWidth(m_dockMonitor)));
+ }
+
+ if (!m_dockMonitorGlossy)
+ {
+ m_dockMonitorGlossy = ::darwinToCGImageRef("monitor_glossy.png");
+ Assert(m_dockMonitorGlossy);
+ /* This depends on the content of monitor.png */
+ m_updateRect = CGRectMake(m_monitorRect.origin.x + 8 /* left-frame */ + 1 /* indent-size */,
+ m_monitorRect.origin.y + 8 /* top-frame */ + 1 /* indent-size */,
+ 128 /* .png-width */ - 8 /* left-frame */ - 8 /* right-frame */ - 2 * 1 /* indent-size */,
+ 128 /* .png-height */ - 8 /* top-frame */ - 25 /* bottom-frame */ - 2 * 1 /* indent-size */);
+ }
+}
+
+void UIAbstractDockIconPreviewHelper::drawOverlayIcons(CGContextRef context)
+{
+ /* Determine whether dock icon overlay is not disabled: */
+ if (!gEDataManager->dockIconDisableOverlay(uiCommon().managedVMUuid()))
+ {
+ /* Initialize overlay rectangle: */
+ CGRect overlayRect = CGRectMake(0, 0, 0, 0);
+ /* Make sure overlay image is valid: */
+ if (m_overlayImage)
+ {
+ /* Draw overlay image at bottom-right of dock icon: */
+ overlayRect = CGRectMake(m_dockIconRect.size.width - CGImageGetWidth(m_overlayImage),
+ m_dockIconRect.size.height - CGImageGetHeight(m_overlayImage),
+ CGImageGetWidth(m_overlayImage),
+ CGImageGetHeight(m_overlayImage));
+ CGContextDrawImage(context, flipRect(overlayRect), m_overlayImage);
+ }
+ }
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.h
new file mode 100644
index 00000000..cb84b436
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIAbstractDockIconPreview.h
@@ -0,0 +1,86 @@
+/* $Id: UIAbstractDockIconPreview.h $ */
+/** @file
+ * VBox Qt GUI - Abstract class for the dock icon preview.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UIAbstractDockIconPreview_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UIAbstractDockIconPreview_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* System includes */
+#include <ApplicationServices/ApplicationServices.h>
+
+/* VBox includes */
+#include "VBoxUtils-darwin.h"
+
+class UIFrameBuffer;
+class UISession;
+
+class QPixmap;
+
+class UIAbstractDockIconPreview
+{
+public:
+ UIAbstractDockIconPreview(UISession *pSession, const QPixmap& overlayImage);
+ virtual ~UIAbstractDockIconPreview() {}
+
+ virtual void updateDockOverlay() = 0;
+ virtual void updateDockPreview(CGImageRef VMImage) = 0;
+ virtual void updateDockPreview(UIFrameBuffer *pFrameBuffer);
+
+ virtual void setOriginalSize(int /* aWidth */, int /* aHeight */) {}
+};
+
+class UIAbstractDockIconPreviewHelper
+{
+public:
+ UIAbstractDockIconPreviewHelper(UISession *pSession, const QPixmap& overlayImage);
+ virtual ~UIAbstractDockIconPreviewHelper();
+ void initPreviewImages();
+ void drawOverlayIcons(CGContextRef context);
+
+ void* currentPreviewWindowId() const;
+
+ /* Flipping is necessary cause the drawing context in Mac OS X is flipped by 180 degree */
+ inline CGRect flipRect(CGRect rect) const { return ::darwinFlipCGRect(rect, m_dockIconRect); }
+ inline CGRect centerRect(CGRect rect) const { return ::darwinCenterRectTo(rect, m_dockIconRect); }
+ inline CGRect centerRectTo(CGRect rect, const CGRect& toRect) const { return ::darwinCenterRectTo(rect, toRect); }
+
+ /* Private member vars */
+ UISession *m_pSession;
+ const CGRect m_dockIconRect;
+
+ CGImageRef m_overlayImage;
+ CGImageRef m_dockMonitor;
+ CGImageRef m_dockMonitorGlossy;
+
+ CGRect m_updateRect;
+ CGRect m_monitorRect;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UIAbstractDockIconPreview_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.h
new file mode 100644
index 00000000..5d17cb0d
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.h
@@ -0,0 +1,135 @@
+/* $Id: UICocoaApplication.h $ */
+/** @file
+ * VBox Qt GUI - UICocoaApplication class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UICocoaApplication_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UICocoaApplication_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+
+/* GUI includes: */
+#include "VBoxCocoaHelper.h"
+#include "VBoxUtils-darwin.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QObject;
+class QWidget;
+
+/* Cocoa declarations: */
+ADD_COCOA_NATIVE_REF(UICocoaApplicationPrivate);
+ADD_COCOA_NATIVE_REF(NSAutoreleasePool);
+ADD_COCOA_NATIVE_REF(NSString);
+ADD_COCOA_NATIVE_REF(NSWindow);
+ADD_COCOA_NATIVE_REF(NSButton);
+
+
+/** Event handler callback. */
+typedef bool (*PFNVBOXCACALLBACK)(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);
+
+/** Native notification callback type for QObject. */
+typedef void (*PfnNativeNotificationCallbackForQObject)(QObject *pObject, const QMap<QString, QString> &userInfo);
+/** Native notification callback type for QWidget. */
+typedef void (*PfnNativeNotificationCallbackForQWidget)(const QString &strNativeNotificationName, QWidget *pWidget);
+/** Standard window button callback type for QWidget. */
+typedef void (*PfnStandardWindowButtonCallbackForQWidget)(StandardWindowButtonType emnButtonType, bool fWithOptionKey, QWidget *pWidget);
+
+
+/** Singleton prototype for our private NSApplication object. */
+class SHARED_LIBRARY_STUFF UICocoaApplication
+{
+public:
+
+ /** Returns singleton instance. */
+ static UICocoaApplication *instance();
+
+ /** Destructs cocoa application. */
+ virtual ~UICocoaApplication();
+
+ /** Returns whether application is currently active. */
+ bool isActive() const;
+
+ /** Hides the application. */
+ void hide();
+
+ /** Hides user elements such as menu-bar and dock. */
+ void hideUserElements();
+
+ /** Register native @a pfnCallback of the @a pvUser taking event @a fMask into account. */
+ void registerForNativeEvents(uint32_t fMask, PFNVBOXCACALLBACK pfnCallback, void *pvUser);
+ /** Unregister native @a pfnCallback of the @a pvUser taking event @a fMask into account. */
+ void unregisterForNativeEvents(uint32_t fMask, PFNVBOXCACALLBACK pfnCallback, void *pvUser);
+
+ /** Register passed @a pObject to native notification @a strNativeNotificationName, using @a pCallback as handler. */
+ void registerToNotificationOfWorkspace(const QString &strNativeNotificationName, QObject *pObject, PfnNativeNotificationCallbackForQObject pCallback);
+ /** Unregister passed @a pWidget from native notification @a strNativeNotificationName. */
+ void unregisterFromNotificationOfWorkspace(const QString &strNativeNotificationName, QObject *pObject);
+
+ /** Register passed @a pWidget to native notification @a strNativeNotificationName, using @a pCallback as handler. */
+ void registerToNotificationOfWindow(const QString &strNativeNotificationName, QWidget *pWidget, PfnNativeNotificationCallbackForQWidget pCallback);
+ /** Unregister passed @a pWidget from native notification @a strNativeNotificationName. */
+ void unregisterFromNotificationOfWindow(const QString &strNativeNotificationName, QWidget *pWidget);
+
+ /** Redirects native notification @a pstrNativeNotificationName for application to registered listener. */
+ void nativeNotificationProxyForObject(NativeNSStringRef pstrNativeNotificationName, const QMap<QString, QString> &userInfo);
+ /** Redirects native notification @a pstrNativeNotificationName for window @a pWindow to registered listener. */
+ void nativeNotificationProxyForWidget(NativeNSStringRef pstrNativeNotificationName, NativeNSWindowRef pWindow);
+
+ /** Register callback for standard window @a buttonType of passed @a pWidget as @a pCallback. */
+ void registerCallbackForStandardWindowButton(QWidget *pWidget, StandardWindowButtonType enmButtonType, PfnStandardWindowButtonCallbackForQWidget pCallback);
+ /** Unregister callback for standard window @a buttonType of passed @a pWidget. */
+ void unregisterCallbackForStandardWindowButton(QWidget *pWidget, StandardWindowButtonType enmButtonType);
+ /** Redirects standard window button selector to registered callback. */
+ void nativeCallbackProxyForStandardWindowButton(NativeNSButtonRef pButton, bool fWithOptionKey);
+
+private:
+
+ /** Constructs cocoa application. */
+ UICocoaApplication();
+
+ /** Holds the singleton access instance. */
+ static UICocoaApplication *s_pInstance;
+
+ /** Holds the private NSApplication instance. */
+ NativeUICocoaApplicationPrivateRef m_pNative;
+ /** Holds the private NSAutoreleasePool instance. */
+ NativeNSAutoreleasePoolRef m_pPool;
+
+ /** Map of notification callbacks registered for corresponding QObject(s). */
+ QMap<QObject*, QMap<QString, PfnNativeNotificationCallbackForQObject> > m_objectCallbacks;
+ /** Map of notification callbacks registered for corresponding QWidget(s). */
+ QMap<QWidget*, QMap<QString, PfnNativeNotificationCallbackForQWidget> > m_widgetCallbacks;
+
+ /** Map of callbacks registered for standard window button(s) of corresponding QWidget(s). */
+ QMap<QWidget*, QMap<StandardWindowButtonType, PfnStandardWindowButtonCallbackForQWidget> > m_stdWindowButtonCallbacks;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UICocoaApplication_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.mm
new file mode 100644
index 00000000..42196b26
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaApplication.mm
@@ -0,0 +1,494 @@
+/* $Id: UICocoaApplication.mm $ */
+/** @file
+ * VBox Qt GUI - UICocoaApplication class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "UICocoaApplication.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+/* External includes: */
+#import <AppKit/NSApplication.h>
+#import <AppKit/NSButton.h>
+#import <AppKit/NSEvent.h>
+#import <AppKit/NSWindow.h>
+#import <Foundation/NSArray.h>
+
+
+/** Class for tracking a callback. */
+@interface CallbackData : NSObject
+{
+@public
+ /** Holds the mask of events to send to this callback. */
+ uint32_t fMask;
+ /** Holds the callback. */
+ PFNVBOXCACALLBACK pfnCallback;
+ /** Holds the user argument. */
+ void *pvUser;
+}
+- (id)initWithMask :(uint32)mask callback :(PFNVBOXCACALLBACK)callback user :(void *)user;
+@end /* @interface CallbackData */
+
+@implementation CallbackData
+/** Performs initialization. */
+- (id)initWithMask :(uint32)mask callback :(PFNVBOXCACALLBACK)callback user :(void *)user
+{
+ self = [super init];
+ if (self)
+ {
+ fMask = mask;
+ pfnCallback = callback;
+ pvUser = user;
+ }
+ return self;
+}
+@end /* @implementation CallbackData */
+
+
+/** Class for event handling. */
+@interface UICocoaApplicationPrivate : NSApplication
+{
+ /** The event mask for which there currently are callbacks. */
+ uint32_t m_fMask;
+ /** Array of callbacks. */
+ NSMutableArray *m_pCallbacks;
+}
+- (id)init;
+- (void)sendEvent :(NSEvent *)theEvent;
+- (void)setCallback :(uint32_t)fMask :(PFNVBOXCACALLBACK)pfnCallback :(void *)pvUser;
+- (void)unsetCallback :(uint32_t)fMask :(PFNVBOXCACALLBACK)pfnCallback :(void *)pvUser;
+
+- (void)registerToNotificationOfWorkspace :(NSString *)pstrNotificationName;
+- (void)unregisterFromNotificationOfWorkspace :(NSString *)pstrNotificationName;
+
+- (void)registerToNotificationOfWindow :(NSString *)pstrNotificationName :(NSWindow *)pWindow;
+- (void)unregisterFromNotificationOfWindow :(NSString *)pstrNotificationName :(NSWindow *)pWindow;
+
+- (void)notificationCallbackOfObject :(NSNotification *)notification;
+- (void)notificationCallbackOfWindow :(NSNotification *)notification;
+
+- (void)registerSelectorForStandardWindowButton :(NSWindow *)pWindow :(StandardWindowButtonType)enmButtonType;
+- (void)selectorForStandardWindowButton :(NSButton *)pButton;
+@end /* @interface UICocoaApplicationPrivate */
+
+@implementation UICocoaApplicationPrivate
+/** Performs initialization. */
+- (id) init
+{
+ self = [super init];
+ if (self)
+ m_pCallbacks = [[NSMutableArray alloc] init];
+
+ // WORKAROUND:
+ // Gently disable El Capitan tries to break everything with the Enter Full Screen action.
+ // S.a. https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/ for reference.
+ [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"];
+
+ return self;
+}
+
+/** Sends an event.
+ * @param pEvent Brings the event to be sent. */
+- (void) sendEvent :(NSEvent *)pEvent
+{
+ /* Check if the type matches any of the registered callbacks. */
+ uint32_t const fMask = m_fMask;
+#if 0 /* for debugging */
+ ::darwinPrintEvent("sendEvent: ", pEvent);
+#endif
+ if (fMask != 0)
+ {
+ NSEventType EvtType = [pEvent type];
+ uint32_t fEvtMask = RT_LIKELY(EvtType < 32) ? RT_BIT_32(EvtType) : 0;
+ if (fMask & fEvtMask)
+ {
+ /* Do the callouts in LIFO order. */
+ for (CallbackData *pData in [m_pCallbacks reverseObjectEnumerator])
+ {
+ if (pData->fMask & fEvtMask)
+ {
+ if (pData->pfnCallback(pEvent, [pEvent eventRef], pData->pvUser))
+ return;
+ }
+
+ }
+ }
+ }
+
+ /* Get on with it. */
+ [super sendEvent:pEvent];
+}
+
+/** Registers an event callback.
+ * @param fMask Brings the event mask for which the callback is to be invoked.
+ * @param pfnCallback Brings the callback function.
+ * @param pvUser Brings the user argument. */
+- (void) setCallback :(uint32_t)fMask :(PFNVBOXCACALLBACK)pfnCallback :(void *)pvUser
+{
+ /* Add the callback data to the array: */
+ CallbackData *pData = [[[CallbackData alloc] initWithMask:fMask callback:pfnCallback user:pvUser] autorelease];
+ [m_pCallbacks addObject:pData];
+
+ /* Update the global mask: */
+ m_fMask |= fMask;
+}
+
+/** Deregisters an event callback.
+ * @param fMask Brings the event mask for which the callback is to be invoked.
+ * @param pfnCallback Brings the callback function.
+ * @param pvUser Brings the user argument. */
+- (void) unsetCallback: (uint32_t)fMask :(PFNVBOXCACALLBACK)pfnCallback :(void *)pvUser
+{
+ /* Loop the event array LIFO fashion searching for a matching callback. */
+ for (CallbackData *pData in [m_pCallbacks reverseObjectEnumerator])
+ {
+ if ( pData->pfnCallback == pfnCallback
+ && pData->pvUser == pvUser
+ && pData->fMask == fMask)
+ {
+ [m_pCallbacks removeObject:pData];
+ break;
+ }
+ }
+ uint32_t fNewMask = 0;
+ for (CallbackData *pData in m_pCallbacks)
+ fNewMask |= pData->fMask;
+ m_fMask = fNewMask;
+}
+
+/** Registers to cocoa notification @a pstrNotificationName. */
+- (void) registerToNotificationOfWorkspace :(NSString *)pstrNotificationName
+{
+ /* Register notification observer: */
+ NSNotificationCenter *pNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
+ [pNotificationCenter addObserver:self
+ selector:@selector(notificationCallbackOfObject:)
+ name:pstrNotificationName
+ object:nil];
+}
+
+/** Unregister @a pWindow from cocoa notification @a pstrNotificationName. */
+- (void) unregisterFromNotificationOfWorkspace :(NSString *)pstrNotificationName
+{
+ /* Uninstall notification observer: */
+ NSNotificationCenter *pNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
+ [pNotificationCenter removeObserver:self
+ name:pstrNotificationName
+ object:nil];
+}
+
+/** Register @a pWindow to cocoa notification @a pstrNotificationName. */
+- (void) registerToNotificationOfWindow :(NSString *)pstrNotificationName :(NSWindow *)pWindow
+{
+ /* Register notification observer: */
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(notificationCallbackOfWindow:)
+ name:pstrNotificationName
+ object:pWindow];
+}
+
+/** Unregister @a pWindow from cocoa notification @a pstrNotificationName. */
+- (void) unregisterFromNotificationOfWindow :(NSString *)pstrNotificationName :(NSWindow *)pWindow
+{
+ /* Uninstall notification observer: */
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:pstrNotificationName
+ object:pWindow];
+}
+
+/** Redirects cocoa @a notification to UICocoaApplication instance. */
+- (void) notificationCallbackOfObject :(NSNotification *)notification
+{
+ /* Get current notification name: */
+ NSString *pstrName = [notification name];
+
+ /* Prepare user-info: */
+ QMap<QString, QString> userInfo;
+
+ /* Process known notifications: */
+ if ( [pstrName isEqualToString :@"NSWorkspaceDidActivateApplicationNotification"]
+ || [pstrName isEqualToString :@"NSWorkspaceDidDeactivateApplicationNotification"])
+ {
+ NSDictionary *pUserInfo = [notification userInfo];
+ NSRunningApplication *pApplication = [pUserInfo valueForKey :@"NSWorkspaceApplicationKey"];
+ NSString *pstrBundleIndentifier = [pApplication bundleIdentifier];
+ userInfo.insert("BundleIdentifier", darwinFromNativeString((NativeNSStringRef)pstrBundleIndentifier));
+ }
+
+ /* Redirect known notifications to objects: */
+ UICocoaApplication::instance()->nativeNotificationProxyForObject(pstrName, userInfo);
+}
+
+/** Redirects cocoa @a notification to UICocoaApplication instance. */
+- (void) notificationCallbackOfWindow :(NSNotification *)notification
+{
+ /* Get current notification name: */
+ NSString *pstrName = [notification name];
+
+ /* Redirect known notifications to widgets: */
+ UICocoaApplication::instance()->nativeNotificationProxyForWidget(pstrName, [notification object]);
+}
+
+/** Registers selector for standard window @a enmButtonType of the passed @a pWindow. */
+- (void)registerSelectorForStandardWindowButton :(NSWindow *)pWindow :(StandardWindowButtonType)enmButtonType
+{
+ /* Retrieve corresponding button: */
+ NSButton *pButton = Nil;
+ switch (enmButtonType)
+ {
+ case StandardWindowButtonType_Close: pButton = [pWindow standardWindowButton:NSWindowCloseButton]; break;
+ case StandardWindowButtonType_Miniaturize: pButton = [pWindow standardWindowButton:NSWindowMiniaturizeButton]; break;
+ case StandardWindowButtonType_Zoom: pButton = [pWindow standardWindowButton:NSWindowZoomButton]; break;
+ case StandardWindowButtonType_Toolbar: pButton = [pWindow standardWindowButton:NSWindowToolbarButton]; break;
+ case StandardWindowButtonType_DocumentIcon: pButton = [pWindow standardWindowButton:NSWindowDocumentIconButton]; break;
+ case StandardWindowButtonType_DocumentVersions: /*pButton = [pWindow standardWindowButton:NSWindowDocumentVersionsButton];*/ break;
+ case StandardWindowButtonType_FullScreen: /*pButton = [pWindow standardWindowButton:NSWindowFullScreenButton];*/ break;
+ }
+
+ /* Register selector if button exists: */
+ if (pButton != Nil)
+ {
+ [pButton setTarget:self];
+ [pButton setAction:@selector(selectorForStandardWindowButton:)];
+ }
+}
+
+/** Redirects selector of the standard window @a pButton to UICocoaApplication instance callback. */
+- (void)selectorForStandardWindowButton :(NSButton *)pButton
+{
+ /* Check if Option key is currently held: */
+ const bool fWithOptionKey = [NSEvent modifierFlags] & NSAlternateKeyMask;
+
+ /* Redirect selector to callback: */
+ UICocoaApplication::instance()->nativeCallbackProxyForStandardWindowButton(pButton, fWithOptionKey);
+}
+@end /* @implementation UICocoaApplicationPrivate */
+
+
+/*********************************************************************************************************************************
+* Class UICocoaApplication implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UICocoaApplication* UICocoaApplication::s_pInstance = 0;
+
+/* static */
+UICocoaApplication* UICocoaApplication::instance()
+{
+ if (!s_pInstance)
+ s_pInstance = new UICocoaApplication;
+
+ return s_pInstance;
+}
+
+UICocoaApplication::UICocoaApplication()
+{
+ /* Make sure our private NSApplication object is created: */
+ m_pNative = (UICocoaApplicationPrivate*)[UICocoaApplicationPrivate sharedApplication];
+ // WORKAROUND":
+ // Create one auto release pool which is in place for all the
+ // initialization and deinitialization stuff. That is when the
+ // NSApplication is not running the run loop (there is a separate
+ // auto release pool defined).
+ m_pPool = [[NSAutoreleasePool alloc] init];
+}
+
+UICocoaApplication::~UICocoaApplication()
+{
+ [m_pNative release];
+ [m_pPool release];
+}
+
+bool UICocoaApplication::isActive() const
+{
+ return [m_pNative isActive];
+}
+
+void UICocoaApplication::hide()
+{
+ [m_pNative hide:m_pNative];
+}
+
+void UICocoaApplication::hideUserElements()
+{
+ [m_pNative setPresentationOptions:NSApplicationPresentationHideMenuBar | NSApplicationPresentationHideDock];
+}
+
+void UICocoaApplication::registerForNativeEvents(uint32_t fMask, PFNVBOXCACALLBACK pfnCallback, void *pvUser)
+{
+ [m_pNative setCallback:fMask :pfnCallback :pvUser];
+}
+
+void UICocoaApplication::unregisterForNativeEvents(uint32_t fMask, PFNVBOXCACALLBACK pfnCallback, void *pvUser)
+{
+ [m_pNative unsetCallback:fMask :pfnCallback :pvUser];
+}
+
+void UICocoaApplication::registerToNotificationOfWorkspace(const QString &strNativeNotificationName, QObject *pObject,
+ PfnNativeNotificationCallbackForQObject pCallback)
+{
+ /* Make sure it is not registered yet: */
+ AssertReturnVoid(!m_objectCallbacks.contains(pObject) || !m_objectCallbacks[pObject].contains(strNativeNotificationName));
+
+ /* Remember callback: */
+ m_objectCallbacks[pObject][strNativeNotificationName] = pCallback;
+
+ /* Register observer: */
+ NativeNSStringRef pstrNativeNotificationName = darwinToNativeString(strNativeNotificationName.toLatin1().constData());
+ [m_pNative registerToNotificationOfWorkspace :pstrNativeNotificationName];
+}
+
+void UICocoaApplication::unregisterFromNotificationOfWorkspace(const QString &strNativeNotificationName, QObject *pObject)
+{
+ /* Make sure it is registered yet: */
+ AssertReturnVoid(m_objectCallbacks.contains(pObject) && m_objectCallbacks[pObject].contains(strNativeNotificationName));
+
+ /* Forget callback: */
+ m_objectCallbacks[pObject].remove(strNativeNotificationName);
+ if (m_objectCallbacks[pObject].isEmpty())
+ m_objectCallbacks.remove(pObject);
+
+ /* Unregister observer: */
+ NativeNSStringRef pstrNativeNotificationName = darwinToNativeString(strNativeNotificationName.toLatin1().constData());
+ [m_pNative unregisterFromNotificationOfWorkspace :pstrNativeNotificationName];
+}
+
+void UICocoaApplication::registerToNotificationOfWindow(const QString &strNativeNotificationName, QWidget *pWidget,
+ PfnNativeNotificationCallbackForQWidget pCallback)
+{
+ /* Make sure it is not registered yet: */
+ AssertReturnVoid(!m_widgetCallbacks.contains(pWidget) || !m_widgetCallbacks[pWidget].contains(strNativeNotificationName));
+
+ /* Remember callback: */
+ m_widgetCallbacks[pWidget][strNativeNotificationName] = pCallback;
+
+ /* Register observer: */
+ NativeNSStringRef pstrNativeNotificationName = darwinToNativeString(strNativeNotificationName.toLatin1().constData());
+ NativeNSWindowRef pWindow = darwinToNativeWindow(pWidget);
+ [m_pNative registerToNotificationOfWindow :pstrNativeNotificationName :pWindow];
+}
+
+void UICocoaApplication::unregisterFromNotificationOfWindow(const QString &strNativeNotificationName, QWidget *pWidget)
+{
+ /* Make sure it is registered yet: */
+ AssertReturnVoid(m_widgetCallbacks.contains(pWidget) && m_widgetCallbacks[pWidget].contains(strNativeNotificationName));
+
+ /* Forget callback: */
+ m_widgetCallbacks[pWidget].remove(strNativeNotificationName);
+ if (m_widgetCallbacks[pWidget].isEmpty())
+ m_widgetCallbacks.remove(pWidget);
+
+ /* Unregister observer: */
+ NativeNSStringRef pstrNativeNotificationName = darwinToNativeString(strNativeNotificationName.toLatin1().constData());
+ NativeNSWindowRef pWindow = darwinToNativeWindow(pWidget);
+ [m_pNative unregisterFromNotificationOfWindow :pstrNativeNotificationName :pWindow];
+}
+
+void UICocoaApplication::nativeNotificationProxyForObject(NativeNSStringRef pstrNotificationName,
+ const QMap<QString,
+ QString> &userInfo)
+{
+ /* Get notification name: */
+ QString strNotificationName = darwinFromNativeString(pstrNotificationName);
+
+ /* Check if existing object(s) have corresponding notification handler: */
+ foreach (QObject *pObject, m_objectCallbacks.keys())
+ {
+ const QMap<QString, PfnNativeNotificationCallbackForQObject> &callbacks = m_objectCallbacks[pObject];
+ if (callbacks.contains(strNotificationName))
+ callbacks[strNotificationName](pObject, userInfo);
+ }
+}
+
+void UICocoaApplication::nativeNotificationProxyForWidget(NativeNSStringRef pstrNotificationName, NativeNSWindowRef pWindow)
+{
+ /* Get notification name: */
+ QString strNotificationName = darwinFromNativeString(pstrNotificationName);
+
+ /* Check if existing widget(s) have corresponding notification handler: */
+ foreach (QWidget *pWidget, m_widgetCallbacks.keys())
+ {
+ if (darwinToNativeWindow(pWidget) == pWindow)
+ {
+ const QMap<QString, PfnNativeNotificationCallbackForQWidget> &callbacks = m_widgetCallbacks[pWidget];
+ if (callbacks.contains(strNotificationName))
+ callbacks[strNotificationName](strNotificationName, pWidget);
+ }
+ }
+}
+
+void UICocoaApplication::registerCallbackForStandardWindowButton(QWidget *pWidget, StandardWindowButtonType enmButtonType,
+ PfnStandardWindowButtonCallbackForQWidget pCallback)
+{
+ /* Make sure it is not registered yet: */
+ AssertReturnVoid( !m_stdWindowButtonCallbacks.contains(pWidget)
+ || !m_stdWindowButtonCallbacks.value(pWidget).contains(enmButtonType));
+
+ /* Remember callback: */
+ m_stdWindowButtonCallbacks[pWidget][enmButtonType] = pCallback;
+
+ /* Register selector: */
+ NativeNSWindowRef pWindow = darwinToNativeWindow(pWidget);
+ [m_pNative registerSelectorForStandardWindowButton :pWindow :enmButtonType];
+}
+
+void UICocoaApplication::unregisterCallbackForStandardWindowButton(QWidget *pWidget, StandardWindowButtonType enmButtonType)
+{
+ /* Make sure it is registered yet: */
+ AssertReturnVoid( m_stdWindowButtonCallbacks.contains(pWidget)
+ && m_stdWindowButtonCallbacks.value(pWidget).contains(enmButtonType));
+
+ /* Forget callback: */
+ m_stdWindowButtonCallbacks[pWidget].remove(enmButtonType);
+ if (m_stdWindowButtonCallbacks.value(pWidget).isEmpty())
+ m_stdWindowButtonCallbacks.remove(pWidget);
+}
+
+void UICocoaApplication::nativeCallbackProxyForStandardWindowButton(NativeNSButtonRef pButton, bool fWithOptionKey)
+{
+ // WORKAROUND:
+ // Why not using nested foreach, will you ask?
+ // It's because Qt 4.x has shadowing issue in Q_FOREACH macro.
+ // Bug record QTBUG-33585 opened for Qt 4.8.4 and closed as _won't fix_ by one of Qt devs.
+
+ /* Check if passed button is one of the buttons of the registered widget(s): */
+ const QList<QWidget*> widgets = m_stdWindowButtonCallbacks.keys();
+ for (int iWidgetIndex = 0; iWidgetIndex < widgets.size(); ++iWidgetIndex)
+ {
+ QWidget *pWidget = widgets.at(iWidgetIndex);
+ const QMap<StandardWindowButtonType, PfnStandardWindowButtonCallbackForQWidget> callbacks
+ = m_stdWindowButtonCallbacks.value(pWidget);
+ const QList<StandardWindowButtonType> buttonTypes = callbacks.keys();
+ for (int iButtonTypeIndex = 0; iButtonTypeIndex < buttonTypes.size(); ++iButtonTypeIndex)
+ {
+ StandardWindowButtonType enmButtonType = buttonTypes.at(iButtonTypeIndex);
+ if (darwinNativeButtonOfWindow(pWidget, enmButtonType) == pButton)
+ return callbacks.value(enmButtonType)(enmButtonType, fWithOptionKey, pWidget);
+ }
+ }
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.h
new file mode 100644
index 00000000..cd401f4a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.h
@@ -0,0 +1,56 @@
+/* $Id: UICocoaDockIconPreview.h $ */
+/** @file
+ * VBox Qt GUI - UICocoaDockIconPreview class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UICocoaDockIconPreview_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UICocoaDockIconPreview_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes */
+#include "UIAbstractDockIconPreview.h"
+
+class UICocoaDockIconPreviewPrivate;
+
+class UICocoaDockIconPreview: public UIAbstractDockIconPreview
+{
+public:
+ UICocoaDockIconPreview(UISession *pSession, const QPixmap& overlayImage);
+ ~UICocoaDockIconPreview();
+
+ virtual void updateDockOverlay();
+ virtual void updateDockPreview(CGImageRef VMImage);
+ virtual void updateDockPreview(UIFrameBuffer *pFrameBuffer);
+
+ virtual void setOriginalSize(int aWidth, int aHeight);
+
+private:
+ UICocoaDockIconPreviewPrivate *d;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UICocoaDockIconPreview_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.mm
new file mode 100644
index 00000000..baba2a4b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaDockIconPreview.mm
@@ -0,0 +1,342 @@
+/* $Id: UICocoaDockIconPreview.mm $ */
+/** @file
+ * VBox Qt GUI - Cocoa helper for the dock icon preview.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UICocoaDockIconPreview.h"
+#include "VBoxCocoaHelper.h"
+
+/* System includes */
+#import <Cocoa/Cocoa.h>
+
+@interface UIDockTileMonitor: NSView
+{
+ UICocoaDockIconPreviewPrivate *p;
+
+ NSImageView *mScreenContent;
+ NSImageView *mMonitorGlossy;
+}
+- (id)initWithFrame:(NSRect)frame parent:(UICocoaDockIconPreviewPrivate*)parent;
+- (NSImageView*)screenContent;
+- (void)resize:(NSSize)size;
+@end
+
+@interface UIDockTileOverlay: NSView
+{
+ UICocoaDockIconPreviewPrivate *p;
+}
+- (id)initWithFrame:(NSRect)frame parent:(UICocoaDockIconPreviewPrivate*)parent;
+@end
+
+@interface UIDockTile: NSView
+{
+ UICocoaDockIconPreviewPrivate *p;
+
+ UIDockTileMonitor *mMonitor;
+ NSImageView *mAppIcon;
+
+ UIDockTileOverlay *mOverlay;
+}
+- (id)initWithParent:(UICocoaDockIconPreviewPrivate*)parent;
+- (void)destroy;
+- (NSView*)screenContentWithParentView:(NSView*)parentView;
+- (void)cleanup;
+- (void)restoreAppIcon;
+- (void)updateAppIcon;
+- (void)restoreMonitor;
+- (void)updateMonitorWithImage:(CGImageRef)image;
+- (void)resizeMonitor:(NSSize)size;
+@end
+
+/*
+ * Helper class which allow us to access all members/methods of AbstractDockIconPreviewHelper
+ * from any Cocoa class.
+ */
+class UICocoaDockIconPreviewPrivate: public UIAbstractDockIconPreviewHelper
+{
+public:
+ inline UICocoaDockIconPreviewPrivate(UISession *pSession, const QPixmap& overlayImage)
+ :UIAbstractDockIconPreviewHelper(pSession, overlayImage)
+ {
+ mUIDockTile = [[UIDockTile alloc] initWithParent:this];
+ }
+
+ inline ~UICocoaDockIconPreviewPrivate()
+ {
+
+ [mUIDockTile destroy];
+ [mUIDockTile release];
+ }
+
+ UIDockTile *mUIDockTile;
+};
+
+/*
+ * Cocoa wrapper for the abstract dock icon preview class
+ */
+UICocoaDockIconPreview::UICocoaDockIconPreview(UISession *pSession, const QPixmap& overlayImage)
+ : UIAbstractDockIconPreview(pSession, overlayImage)
+{
+ CocoaAutoreleasePool pool;
+
+ d = new UICocoaDockIconPreviewPrivate(pSession, overlayImage);
+}
+
+UICocoaDockIconPreview::~UICocoaDockIconPreview()
+{
+ CocoaAutoreleasePool pool;
+
+ delete d;
+}
+
+void UICocoaDockIconPreview::updateDockOverlay()
+{
+ CocoaAutoreleasePool pool;
+
+ [d->mUIDockTile updateAppIcon];
+}
+
+void UICocoaDockIconPreview::updateDockPreview(CGImageRef VMImage)
+{
+ CocoaAutoreleasePool pool;
+
+ [d->mUIDockTile updateMonitorWithImage:VMImage];
+}
+
+void UICocoaDockIconPreview::updateDockPreview(UIFrameBuffer *pFrameBuffer)
+{
+ CocoaAutoreleasePool pool;
+
+ UIAbstractDockIconPreview::updateDockPreview(pFrameBuffer);
+}
+
+void UICocoaDockIconPreview::setOriginalSize(int width, int height)
+{
+ CocoaAutoreleasePool pool;
+
+ [d->mUIDockTile resizeMonitor:NSMakeSize(width, height)];
+}
+
+/*
+ * Class for arranging/updating the layers for the glossy monitor preview.
+ */
+@implementation UIDockTileMonitor
+- (id)initWithFrame:(NSRect)frame parent:(UICocoaDockIconPreviewPrivate*)parent
+{
+ self = [super initWithFrame:frame];
+
+ if (self != nil)
+ {
+ p = parent;
+ /* The screen content view */
+ mScreenContent = [[NSImageView alloc] initWithFrame:NSRectFromCGRect(p->flipRect(p->m_updateRect))];
+// [mScreenContent setImageAlignment: NSImageAlignCenter];
+ [mScreenContent setImageAlignment: NSImageAlignTopLeft];
+ [mScreenContent setImageScaling: NSImageScaleAxesIndependently];
+ [self addSubview: mScreenContent];
+ /* The state view */
+ mMonitorGlossy = [[NSImageView alloc] initWithFrame:NSRectFromCGRect(p->flipRect(p->m_monitorRect))];
+ [mMonitorGlossy setImage: ::darwinToNSImageRef(p->m_dockMonitorGlossy)];
+ [self addSubview: mMonitorGlossy];
+ }
+
+ return self;
+}
+
+- (void)drawRect:(NSRect)aRect
+{
+ NSImage *dockMonitor = ::darwinToNSImageRef(p->m_dockMonitor);
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ [dockMonitor drawInRect:NSRectFromCGRect(p->flipRect(p->m_monitorRect)) fromRect:aRect operation:NSCompositingOperationSourceOver fraction:1.0];
+#else
+ [dockMonitor drawInRect:NSRectFromCGRect(p->flipRect(p->m_monitorRect)) fromRect:aRect operation:NSCompositeSourceOver fraction:1.0];
+#endif
+ [dockMonitor release];
+}
+
+- (NSImageView*)screenContent
+{
+ return mScreenContent;
+}
+
+- (void)resize:(NSSize)size
+{
+ /* Calculate the new size based on the aspect ratio of the original screen
+ size */
+ float w, h;
+ if (size.width > size.height)
+ {
+ w = p->m_updateRect.size.width;
+ h = ((float)size.height / size.width * p->m_updateRect.size.height);
+ }
+ else
+ {
+ w = ((float)size.width / size.height * p->m_updateRect.size.width);
+ h = p->m_updateRect.size.height;
+ }
+ CGRect r = (p->flipRect (p->centerRectTo (CGRectMake (0, 0, (int)w, (int)h), p->m_updateRect)));
+ r.origin.x = (int)r.origin.x;
+ r.origin.y = (int)r.origin.y;
+ r.size.width = (int)r.size.width;
+ r.size.height = (int)r.size.height;
+// printf("gui %f %f %f %f\n", r.origin.x, r.origin.y, r.size.width, r.size.height);
+ /* Center within the update rect */
+ [mScreenContent setFrame:NSRectFromCGRect (r)];
+}
+@end
+
+/*
+ * Simple implementation for the overlay of the OS & the state icon. Is used both
+ * in the application icon & preview mode.
+ */
+@implementation UIDockTileOverlay
+- (id)initWithFrame:(NSRect)frame parent:(UICocoaDockIconPreviewPrivate*)parent
+{
+ self = [super initWithFrame:frame];
+
+ if (self != nil)
+ p = parent;
+
+ return self;
+}
+
+- (void)drawRect:(NSRect)aRect
+{
+ RT_NOREF(aRect);
+ NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];
+ CGContextRef pCGContext = (CGContextRef)[nsContext graphicsPort];
+ p->drawOverlayIcons (pCGContext);
+}
+@end
+
+/*
+ * VirtualBox Dock Tile implementation. Manage the switching between the icon
+ * and preview mode & forwards all update request to the appropriate methods.
+ */
+@implementation UIDockTile
+- (id)initWithParent:(UICocoaDockIconPreviewPrivate*)parent
+{
+ self = [super init];
+
+ if (self != nil)
+ {
+ p = parent;
+ /* Add self as the content view of the dock tile */
+ NSDockTile *dock = [[NSApplication sharedApplication] dockTile];
+ [dock setContentView: self];
+ /* App icon is default */
+ [self restoreAppIcon];
+ /* The overlay */
+ mOverlay = [[UIDockTileOverlay alloc] initWithFrame:NSRectFromCGRect(p->flipRect (p->m_dockIconRect)) parent:p];
+ [self addSubview: mOverlay];
+ }
+
+ return self;
+}
+
+- (void)destroy
+{
+ /* Remove all content from the application dock tile. */
+ [mOverlay removeFromSuperview];
+ [mOverlay release];
+ mOverlay = nil;
+ NSDockTile *dock = [[NSApplication sharedApplication] dockTile];
+ [dock setContentView: nil];
+ /* Cleanup all other resources */
+ [self cleanup];
+}
+
+- (NSView*)screenContentWithParentView:(NSView*)parentView
+{
+ if (mMonitor != nil)
+ {
+ void *pId = p->currentPreviewWindowId();
+ if (parentView == pId)
+ return [mMonitor screenContent];
+ }
+ return nil;
+}
+
+- (void)cleanup
+{
+ if (mAppIcon != nil)
+ {
+ [mAppIcon removeFromSuperview];
+ [mAppIcon release];
+ mAppIcon = nil;
+ }
+ if (mMonitor != nil)
+ {
+ [mMonitor removeFromSuperview];
+ [mMonitor release];
+ mMonitor = nil;
+ }
+}
+
+- (void)restoreAppIcon
+{
+ if (mAppIcon == nil)
+ {
+ [self cleanup];
+ mAppIcon = [[NSImageView alloc] initWithFrame:NSRectFromCGRect (p->flipRect (p->m_dockIconRect))];
+ [mAppIcon setImage: [NSImage imageNamed:@"NSApplicationIcon"]];
+ [self addSubview: mAppIcon positioned:NSWindowBelow relativeTo:mOverlay];
+ }
+}
+
+- (void)updateAppIcon
+{
+ [self restoreAppIcon];
+ [[[NSApplication sharedApplication] dockTile] display];
+}
+
+- (void)restoreMonitor
+{
+ if (mMonitor == nil)
+ {
+ p->initPreviewImages();
+ [self cleanup];
+ mMonitor = [[UIDockTileMonitor alloc] initWithFrame:NSRectFromCGRect (p->flipRect (p->m_dockIconRect)) parent:p];
+ [self addSubview: mMonitor positioned:NSWindowBelow relativeTo:mOverlay];
+ }
+}
+
+- (void)updateMonitorWithImage:(CGImageRef)image
+{
+ [self restoreMonitor];
+ NSImage *nsimage = ::darwinToNSImageRef(image);
+ [[mMonitor screenContent] setImage: nsimage];
+ [nsimage release];
+ [[[NSApplication sharedApplication] dockTile] display];
+}
+
+- (void)resizeMonitor:(NSSize)size
+{
+ [self restoreMonitor];
+ [mMonitor resize:size];
+}
+@end
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.h
new file mode 100644
index 00000000..773ee8c9
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.h
@@ -0,0 +1,99 @@
+/* $Id: UICocoaSpecialControls.h $ */
+/** @file
+ * VBox Qt GUI - UICocoaSpecialControls class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UICocoaSpecialControls_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UICocoaSpecialControls_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+#ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
+
+/* Qt includes: */
+#include <QWidget>
+#ifndef VBOX_IS_QT6_OR_LATER /** @todo qt6: ... */
+# include <QMacCocoaViewContainer>
+#endif
+
+/* GUI includes: */
+#include "VBoxCocoaHelper.h"
+#include "UILibraryDefs.h"
+
+/* Add typedefs for Cocoa types: */
+ADD_COCOA_NATIVE_REF(NSButton);
+
+/** QMacCocoaViewContainer extension,
+ * used as cocoa button container. */
+class SHARED_LIBRARY_STUFF UICocoaButton
+#ifdef VBOX_IS_QT6_OR_LATER /** @todo qt6: ... */
+ : public QWidget
+#else
+ : public QMacCocoaViewContainer
+#endif
+{
+ Q_OBJECT
+
+signals:
+
+ /** Notifies about button click and whether it's @a fChecked. */
+ void clicked(bool fChecked = false);
+
+public:
+
+ /** Cocoa button types. */
+ enum CocoaButtonType
+ {
+ HelpButton,
+ CancelButton,
+ ResetButton
+ };
+
+ /** Constructs cocoa button passing @a pParent to the base-class.
+ * @param enmType Brings the button type. */
+ UICocoaButton(QWidget *pParent, CocoaButtonType enmType);
+ /** Destructs cocoa button. */
+ ~UICocoaButton();
+
+ /** Returns size-hint. */
+ QSize sizeHint() const;
+
+ /** Defines button @a strText. */
+ void setText(const QString &strText);
+ /** Defines button @a strToolTip. */
+ void setToolTip(const QString &strToolTip);
+
+ /** Handles button click. */
+ void onClicked();
+
+private:
+
+ /** Returns native cocoa button reference. */
+ NativeNSButtonRef nativeRef() const { return static_cast<NativeNSButtonRef>(cocoaView()); }
+};
+
+#endif /* VBOX_DARWIN_USE_NATIVE_CONTROLS */
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UICocoaSpecialControls_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm
new file mode 100644
index 00000000..b83b2e15
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm
@@ -0,0 +1,179 @@
+/* $Id: UICocoaSpecialControls.mm $ */
+/** @file
+ * VBox Qt GUI - UICocoaSpecialControls implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
+
+/* Qt includes: */
+#include <QMacCocoaViewContainer>
+
+/* GUI includes: */
+#include "VBoxUtils-darwin.h"
+#include "UICocoaSpecialControls.h"
+
+/* System includes: */
+#import <AppKit/NSButton.h>
+
+
+/** Private button-target interface. */
+@interface UIButtonTargetPrivate : NSObject
+{
+ UICocoaButton *m_pRealTarget;
+}
+// WORKAROUND:
+// The next method used to be called initWithObject, but Xcode 4.1 preview 5
+// cannot cope with that for some reason. Hope this doesn't break anything.
+-(id)initWithObjectAndLionTrouble:(UICocoaButton*)pObject;
+-(IBAction)clicked:(id)pSender;
+@end
+
+
+/*********************************************************************************************************************************
+* Class UIButtonTargetPrivate implementation. *
+*********************************************************************************************************************************/
+
+@implementation UIButtonTargetPrivate
+-(id)initWithObjectAndLionTrouble:(UICocoaButton*)pObject
+{
+ self = [super init];
+
+ m_pRealTarget = pObject;
+
+ return self;
+}
+
+-(IBAction)clicked:(id)pSender
+{
+ Q_UNUSED(pSender);
+ m_pRealTarget->onClicked();
+}
+@end
+
+
+/*********************************************************************************************************************************
+* Class UICocoaButton implementation. *
+*********************************************************************************************************************************/
+
+UICocoaButton::UICocoaButton(QWidget *pParent, CocoaButtonType enmType)
+ : QMacCocoaViewContainer(0, pParent)
+{
+ /* Prepare auto-release pool: */
+ NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
+
+ /* Prepare native button reference: */
+ NativeNSButtonRef pNativeRef;
+ NSRect initFrame;
+
+ /* Configure button: */
+ switch (enmType)
+ {
+ case HelpButton:
+ {
+ pNativeRef = [[NSButton alloc] init];
+ [pNativeRef setTitle: @""];
+ [pNativeRef setBezelStyle: NSHelpButtonBezelStyle];
+ [pNativeRef setBordered: YES];
+ [pNativeRef setAlignment: NSCenterTextAlignment];
+ [pNativeRef sizeToFit];
+ initFrame = [pNativeRef frame];
+ initFrame.size.width += 12; /* Margin */
+ [pNativeRef setFrame:initFrame];
+ break;
+ };
+ case CancelButton:
+ {
+ pNativeRef = [[NSButton alloc] initWithFrame: NSMakeRect(0, 0, 13, 13)];
+ [pNativeRef setTitle: @""];
+ [pNativeRef setBezelStyle:NSShadowlessSquareBezelStyle];
+ [pNativeRef setButtonType:NSMomentaryChangeButton];
+ [pNativeRef setImage: [NSImage imageNamed: NSImageNameStopProgressFreestandingTemplate]];
+ [pNativeRef setBordered: NO];
+ [[pNativeRef cell] setImageScaling: NSImageScaleProportionallyDown];
+ initFrame = [pNativeRef frame];
+ break;
+ }
+ case ResetButton:
+ {
+ pNativeRef = [[NSButton alloc] initWithFrame: NSMakeRect(0, 0, 13, 13)];
+ [pNativeRef setTitle: @""];
+ [pNativeRef setBezelStyle:NSShadowlessSquareBezelStyle];
+ [pNativeRef setButtonType:NSMomentaryChangeButton];
+ [pNativeRef setImage: [NSImage imageNamed: NSImageNameRefreshFreestandingTemplate]];
+ [pNativeRef setBordered: NO];
+ [[pNativeRef cell] setImageScaling: NSImageScaleProportionallyDown];
+ initFrame = [pNativeRef frame];
+ break;
+ }
+ }
+
+ /* Install click listener: */
+ UIButtonTargetPrivate *bt = [[UIButtonTargetPrivate alloc] initWithObjectAndLionTrouble:this];
+ [pNativeRef setTarget:bt];
+ [pNativeRef setAction:@selector(clicked:)];
+
+ /* Put the button to the QCocoaViewContainer: */
+ setCocoaView(pNativeRef);
+ /* Release our reference, since our super class
+ * takes ownership and we don't need it anymore. */
+ [pNativeRef release];
+
+ /* Finally resize the widget: */
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ setFixedSize(NSWidth(initFrame), NSHeight(initFrame));
+
+ /* Cleanup auto-release pool: */
+ [pPool release];
+}
+
+UICocoaButton::~UICocoaButton()
+{
+}
+
+QSize UICocoaButton::sizeHint() const
+{
+ NSRect frame = [nativeRef() frame];
+ return QSize(frame.size.width, frame.size.height);
+}
+
+void UICocoaButton::setText(const QString &strText)
+{
+ QString s(strText);
+ /* Set it for accessibility reasons as alternative title: */
+ [nativeRef() setAlternateTitle: ::darwinQStringToNSString(s.remove('&'))];
+}
+
+void UICocoaButton::setToolTip(const QString &strToolTip)
+{
+ [nativeRef() setToolTip: ::darwinQStringToNSString(strToolTip)];
+}
+
+void UICocoaButton::onClicked()
+{
+ emit clicked(false);
+}
+
+#endif /* VBOX_DARWIN_USE_NATIVE_CONTROLS */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin.cpp
new file mode 100644
index 00000000..ca0ba697
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin.cpp
@@ -0,0 +1,48 @@
+/* $Id: UIDesktopServices_darwin.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to darwin..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UIDesktopServices.h"
+#include "UIDesktopServices_darwin_p.h"
+#include "VBoxUtils-darwin.h"
+
+/* Qt includes */
+#include <QString>
+
+bool UIDesktopServices::createMachineShortcut(const QString &strSrcFile, const QString &strDstPath, const QString &strName, const QUuid &uUuid)
+{
+ return ::darwinCreateMachineShortcut(::darwinToNativeString(strSrcFile.toUtf8().constData()),
+ ::darwinToNativeString(strDstPath.toUtf8().constData()),
+ ::darwinToNativeString(strName.toUtf8().constData()),
+ ::darwinToNativeString(uUuid.toString().toUtf8().constData()));
+}
+
+bool UIDesktopServices::openInFileManager(const QString &strFile)
+{
+ return ::darwinOpenInFileManager(::darwinToNativeString(strFile.toUtf8().constData()));
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_cocoa.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_cocoa.mm
new file mode 100644
index 00000000..95ea82d8
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_cocoa.mm
@@ -0,0 +1,74 @@
+/* $Id: UIDesktopServices_darwin_cocoa.mm $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to darwin.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UIDesktopServices_darwin_p.h"
+
+/* System includes */
+#include <Carbon/Carbon.h>
+#import <AppKit/NSWorkspace.h>
+
+/* Create desktop alias using a bookmark stuff. */
+bool darwinCreateMachineShortcut(NativeNSStringRef pstrSrcFile, NativeNSStringRef pstrDstPath, NativeNSStringRef pstrName, NativeNSStringRef /* pstrUuid */)
+{
+ RT_NOREF(pstrName);
+ if (!pstrSrcFile || !pstrDstPath)
+ return false;
+
+ NSError *pErr = nil;
+ NSURL *pSrcUrl = [NSURL fileURLWithPath:pstrSrcFile];
+
+ NSString *pVmFileName = [pSrcUrl lastPathComponent];
+ NSString *pSrcPath = [NSString stringWithFormat:@"%@/%@", pstrDstPath, [pVmFileName stringByDeletingPathExtension]];
+ NSURL *pDstUrl = [NSURL fileURLWithPath:pSrcPath];
+
+ bool rc = false;
+
+ if (!pSrcUrl || !pDstUrl)
+ return false;
+
+ NSData *pBookmark = [pSrcUrl bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:&pErr];
+
+ if (pBookmark)
+ {
+ rc = [NSURL writeBookmarkData:pBookmark
+ toURL:pDstUrl
+ options:0
+ error:&pErr];
+ }
+
+ return rc;
+}
+
+bool darwinOpenInFileManager(NativeNSStringRef pstrFile)
+{
+ return [[NSWorkspace sharedWorkspace] selectFile:pstrFile inFileViewerRootedAtPath:@""];
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_p.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_p.h
new file mode 100644
index 00000000..a02e7d09
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIDesktopServices_darwin_p.h
@@ -0,0 +1,47 @@
+/* $Id: UIDesktopServices_darwin_p.h $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to darwin..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UIDesktopServices_darwin_p_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UIDesktopServices_darwin_p_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/VBoxCocoa.h>
+#include <iprt/cdefs.h> /* for RT_C_DECLS_BEGIN/RT_C_DECLS_END & stuff */
+
+ADD_COCOA_NATIVE_REF(NSString);
+
+RT_C_DECLS_BEGIN
+
+bool darwinCreateMachineShortcut(NativeNSStringRef pstrSrcFile, NativeNSStringRef pstrDstPath, NativeNSStringRef pstrName, NativeNSStringRef pstrUuid);
+bool darwinOpenInFileManager(NativeNSStringRef pstrFile);
+
+RT_C_DECLS_END
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UIDesktopServices_darwin_p_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp
new file mode 100644
index 00000000..01b4bcbe
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.cpp
@@ -0,0 +1,372 @@
+/* $Id: UIWindowMenuManager.cpp $ */
+/** @file
+ * VBox Qt GUI - UIWindowMenuManager class implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QActionGroup>
+#include <QApplication>
+#include <QMenu>
+
+/* GUI includes: */
+#include "UIWindowMenuManager.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+
+
+/** QObject extension
+ * used as Mac OS X 'Window' menu helper. */
+class UIMenuHelper : public QObject
+{
+ Q_OBJECT;
+
+public:
+
+ /** Constructs menu-helper on the basis of passed @a windows. */
+ UIMenuHelper(const QList<QWidget*> &windows);
+ /** Destructs menu-helper. */
+ virtual ~UIMenuHelper() RT_OVERRIDE;
+
+ /** Returns 'Window' menu. */
+ QMenu *menu() const { return m_pWindowMenu; }
+
+ /** Adds @a pWindow into 'Window' menu. */
+ QAction *addWindow(QWidget *pWindow);
+ /** Removes @a pWindow from 'Window' menu. */
+ void removeWindow(QWidget *pWindow);
+
+ /** Handles translation event. */
+ void retranslateUi();
+
+ /** Updates toggle action states according to passed @a pActiveWindow. */
+ void updateStatus(QWidget *pActiveWindow);
+
+private slots:
+
+ /** Handles request to minimize active-window. */
+ void sltMinimizeActiveWindow();
+
+ /** Handles request to raise sender window. */
+ void sltRaiseSender();
+
+private:
+
+ /** Holds the 'Window' menu instance. */
+ QMenu *m_pWindowMenu;
+ /** Holds the action group instance. */
+ QActionGroup *m_pGroup;
+ /** Holds the 'Minimize' action instance. */
+ QAction *m_pMinimizeAction;
+ /** Holds the hash of the registered menu-helper instances. */
+ QHash<QWidget*, QAction*> m_windows;
+};
+
+
+/*********************************************************************************************************************************
+* Class UIMenuHelper implementation. *
+*********************************************************************************************************************************/
+
+UIMenuHelper::UIMenuHelper(const QList<QWidget*> &windows)
+{
+ /* Prepare 'Window' menu: */
+ m_pWindowMenu = new QMenu;
+
+ /* Prepare action group: */
+ m_pGroup = new QActionGroup(this);
+ m_pGroup->setExclusive(true);
+
+ /* Prepare 'Minimize' action: */
+ m_pMinimizeAction = new QAction(this);
+ m_pWindowMenu->addAction(m_pMinimizeAction);
+ connect(m_pMinimizeAction, SIGNAL(triggered(bool)),
+ this, SLOT(sltMinimizeActiveWindow()));
+
+ /* Make sure all already available windows are
+ * properly registered within this menu: */
+ for (int i = 0; i < windows.size(); ++i)
+ addWindow(windows.at(i));
+
+ /* Apply language settings: */
+ retranslateUi();
+}
+
+UIMenuHelper::~UIMenuHelper()
+{
+ /* Cleanup 'Window' menu: */
+ delete m_pWindowMenu;
+
+ /* Cleanup actions: */
+ qDeleteAll(m_windows);
+}
+
+QAction *UIMenuHelper::addWindow(QWidget *pWindow)
+{
+ QAction *pAction = 0;
+ if ( pWindow
+ && !m_windows.contains(pWindow))
+ {
+ if (m_windows.size() < 2)
+ m_pWindowMenu->addSeparator();
+
+ /* The main window always first: */
+ pAction = new QAction(this);
+ pAction->setText(pWindow->windowTitle());
+ pAction->setMenuRole(QAction::NoRole);
+ pAction->setData(QVariant::fromValue(pWindow));
+ pAction->setCheckable(true);
+
+ /* The first registered one is always
+ * considered as the main window: */
+ if (m_windows.size() == 0)
+ pAction->setShortcut(QKeySequence("Ctrl+0"));
+ m_pGroup->addAction(pAction);
+ connect(pAction, SIGNAL(triggered(bool)),
+ this, SLOT(sltRaiseSender()));
+ m_pWindowMenu->addAction(pAction);
+ m_windows[pWindow] = pAction;
+ }
+ return pAction;
+}
+
+void UIMenuHelper::removeWindow(QWidget *pWindow)
+{
+ if (m_windows.contains(pWindow))
+ {
+ delete m_windows.value(pWindow);
+ m_windows.remove(pWindow);
+ }
+}
+
+void UIMenuHelper::retranslateUi()
+{
+ /* Translate menu: */
+ m_pWindowMenu->setTitle(QApplication::translate("UIActionPool", "&Window"));
+
+ /* Translate menu 'Minimize' action: */
+ m_pMinimizeAction->setText(QApplication::translate("UIActionPool", "&Minimize"));
+ m_pMinimizeAction->setShortcut(QKeySequence("Ctrl+M"));
+
+ /* Translate other menu-actions: */
+ foreach (QAction *pAction, m_windows.values())
+ {
+ /* Get corresponding window from action's data: */
+ QWidget *pWindow = pAction->data().value<QWidget*>();
+ /* Use the window's title as the action's text: */
+ pAction->setText(pWindow->windowTitle());
+ }
+}
+
+void UIMenuHelper::updateStatus(QWidget *pActiveWindow)
+{
+ /* 'Minimize' action is enabled if there is active-window: */
+ m_pMinimizeAction->setEnabled(pActiveWindow != 0);
+
+ /* If there is active-window: */
+ if (pActiveWindow)
+ {
+ /* Toggle corresponding action on: */
+ if (m_windows.contains(pActiveWindow))
+ m_windows.value(pActiveWindow)->setChecked(true);
+ }
+ /* If there is no active-window: */
+ else
+ {
+ /* Make sure corresponding action toggled off: */
+ if (QAction *pChecked = m_pGroup->checkedAction())
+ pChecked->setChecked(false);
+ }
+}
+
+void UIMenuHelper::sltMinimizeActiveWindow()
+{
+ if (QWidget *pActiveWindow = qApp->activeWindow())
+ pActiveWindow->showMinimized();
+}
+
+void UIMenuHelper::sltRaiseSender()
+{
+ AssertReturnVoid(sender());
+ if (QAction *pAction = qobject_cast<QAction*>(sender()))
+ {
+ if (QWidget *pWidget = pAction->data().value<QWidget*>())
+ {
+ pWidget->show();
+ pWidget->raise();
+ pWidget->activateWindow();
+ }
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Class UIWindowMenuManager implementation. *
+*********************************************************************************************************************************/
+
+/* static */
+UIWindowMenuManager* UIWindowMenuManager::s_pInstance = 0;
+
+/* static */
+void UIWindowMenuManager::create()
+{
+ /* Make sure 'Window' menu Manager is not created: */
+ AssertReturnVoid(!s_pInstance);
+
+ /* Create 'Window' menu Manager: */
+ new UIWindowMenuManager;
+}
+
+/* static */
+void UIWindowMenuManager::destroy()
+{
+ /* Make sure 'Window' menu Manager is created: */
+ AssertPtrReturnVoid(s_pInstance);
+
+ /* Delete 'Window' menu Manager: */
+ delete s_pInstance;
+}
+
+QMenu *UIWindowMenuManager::createMenu(QWidget *pWindow)
+{
+ /* Create helper: */
+ UIMenuHelper *pHelper = new UIMenuHelper(m_windows);
+ /* Register it: */
+ m_helpers[pWindow] = pHelper;
+
+ /* Return menu of created helper: */
+ return pHelper->menu();
+}
+
+void UIWindowMenuManager::destroyMenu(QWidget *pWindow)
+{
+ /* If window is registered: */
+ if (m_helpers.contains(pWindow))
+ {
+ /* Delete helper: */
+ delete m_helpers.value(pWindow);
+ /* Unregister it: */
+ m_helpers.remove(pWindow);
+ }
+}
+
+void UIWindowMenuManager::addWindow(QWidget *pWindow)
+{
+ /* Register window: */
+ m_windows.append(pWindow);
+ /* Add window to all menus we have: */
+ foreach (UIMenuHelper *pHelper, m_helpers.values())
+ pHelper->addWindow(pWindow);
+}
+
+void UIWindowMenuManager::removeWindow(QWidget *pWindow)
+{
+ /* Remove window from all menus we have: */
+ foreach (UIMenuHelper *pHelper, m_helpers.values())
+ pHelper->removeWindow(pWindow);
+ /* Unregister window: */
+ m_windows.removeAll(pWindow);
+}
+
+void UIWindowMenuManager::retranslateUi()
+{
+ /* Translate all the helpers: */
+ foreach (UIMenuHelper *pHelper, m_helpers.values())
+ pHelper->retranslateUi();
+}
+
+UIWindowMenuManager::UIWindowMenuManager()
+{
+ /* Assign instance: */
+ s_pInstance = this;
+
+ /* Install global event-filter: */
+ qApp->installEventFilter(this);
+}
+
+UIWindowMenuManager::~UIWindowMenuManager()
+{
+ /* Cleanup all helpers: */
+ qDeleteAll(m_helpers);
+
+ /* Unassign instance: */
+ s_pInstance = 0;
+}
+
+bool UIWindowMenuManager::eventFilter(QObject *pObject, QEvent *pEvent)
+{
+ /* Acquire event type: */
+ const QEvent::Type type = pEvent->type();
+
+#ifdef VBOX_OSE /// @todo Do we still need it?
+ // WORKAROUND:
+ // Stupid Qt: Qt doesn't check if a window is minimized when a command is
+ // executed. This leads to strange behaviour. The minimized window is
+ // partly restored, but not usable. As a workaround we raise the parent
+ // window before we let execute the command.
+ // Note: fixed in our local Qt build since 4.7.0.
+ if (pObject && type == QEvent::Show)
+ {
+ QWidget *pWidget = qobject_cast<QWidget*>(pObject);
+ if ( pWidget
+ && pWidget->parentWidget()
+ && pWidget->parentWidget()->isMinimized())
+ {
+ pWidget->parentWidget()->show();
+ pWidget->parentWidget()->raise();
+ pWidget->parentWidget()->activateWindow();
+ }
+ }
+#endif /* VBOX_OSE */
+
+ /* We need to track several events which leads to different window
+ * activation and change the menu items in that case. */
+ if ( type == QEvent::ActivationChange
+ || type == QEvent::WindowActivate
+ || type == QEvent::WindowDeactivate
+ || type == QEvent::WindowStateChange
+ || type == QEvent::Show
+ || type == QEvent::Close
+ || type == QEvent::Hide)
+ {
+ QWidget *pActiveWindow = qApp->activeWindow();
+ foreach (UIMenuHelper *pHelper, m_helpers.values())
+ pHelper->updateStatus(pActiveWindow);
+ }
+
+ /* Besides our own retranslation, we should also retranslate
+ * everything on any registered widget title change event: */
+ if (pObject && type == QEvent::WindowTitleChange)
+ {
+ QWidget *pWidget = qobject_cast<QWidget*>(pObject);
+ if (pWidget && m_helpers.contains(pWidget))
+ retranslateUi();
+ }
+
+ /* Call to base-class: */
+ return QIWithRetranslateUI3<QObject>::eventFilter(pObject, pEvent);
+}
+
+
+#include "UIWindowMenuManager.moc"
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.h
new file mode 100644
index 00000000..33d10ab0
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/UIWindowMenuManager.h
@@ -0,0 +1,98 @@
+/* $Id: UIWindowMenuManager.h $ */
+/** @file
+ * VBox Qt GUI - UIWindowMenuManager class declaration.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_UIWindowMenuManager_h
+#define FEQT_INCLUDED_SRC_platform_darwin_UIWindowMenuManager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QHash>
+#include <QObject>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+
+/* Forward declarations: */
+class QMenu;
+class UIMenuHelper;
+
+/** Singleton QObject extension
+ * used as Mac OS X 'Window' menu Manager. */
+class SHARED_LIBRARY_STUFF UIWindowMenuManager : public QIWithRetranslateUI3<QObject>
+{
+ Q_OBJECT;
+
+public:
+
+ /** Creates instance. */
+ static void create();
+ /** Destroyes instance. */
+ static void destroy();
+ /** Returns current instance. */
+ static UIWindowMenuManager *instance() { return s_pInstance; }
+
+ /** Creates 'Window' menu for passed @a pWindow. */
+ QMenu *createMenu(QWidget *pWindow);
+ /** Destroys 'Window' menu for passed @a pWindow. */
+ void destroyMenu(QWidget *pWindow);
+
+ /** Adds @a pWindow to all 'Window' menus. */
+ void addWindow(QWidget *pWindow);
+ /** Removes @a pWindow from all 'Window' menus. */
+ void removeWindow(QWidget *pWindow);
+
+ /** Handles translation event. */
+ virtual void retranslateUi() RT_OVERRIDE;
+
+protected:
+
+ /** Constructs 'Window' menu Manager. */
+ UIWindowMenuManager();
+ /** Destructs 'Window' menu Manager. */
+ ~UIWindowMenuManager();
+
+ /** Preprocesses any Qt @a pEvent for passed @a pObject. */
+ virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
+
+private:
+
+ /** Holds the static instance. */
+ static UIWindowMenuManager *s_pInstance;
+
+ /** Holds the list of the registered window references. */
+ QList<QWidget*> m_windows;
+
+ /** Holds the hash of the registered menu-helper instances. */
+ QHash<QWidget*, UIMenuHelper*> m_helpers;
+};
+
+/** Singleton 'Window' menu Manager 'official' name. */
+#define gpWindowMenuManager UIWindowMenuManager::instance()
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_UIWindowMenuManager_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxCocoaHelper.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxCocoaHelper.h
new file mode 100644
index 00000000..1156292c
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxCocoaHelper.h
@@ -0,0 +1,73 @@
+/* $Id: VBoxCocoaHelper.h $ */
+/** @file
+ * VBox Qt GUI - VBoxCocoa Helper.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_VBoxCocoaHelper_h
+#define FEQT_INCLUDED_SRC_platform_darwin_VBoxCocoaHelper_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Global includes */
+#include <VBox/VBoxCocoa.h>
+
+#ifdef __OBJC__
+
+/* System includes */
+#import <AppKit/NSImage.h>
+#import <Foundation/NSAutoreleasePool.h>
+#import <CoreFoundation/CFString.h>
+
+/* Qt includes */
+#include <QString>
+#include <QVarLengthArray>
+
+inline NSString *darwinQStringToNSString(const QString &aString)
+{
+ const UniChar *chars = reinterpret_cast<const UniChar *>(aString.unicode());
+ CFStringRef str = CFStringCreateWithCharacters(0, chars, aString.length());
+ return [(NSString*)CFStringCreateMutableCopy(0, 0, str) autorelease];
+}
+
+inline QString darwinNSStringToQString(const NSString *aString)
+{
+ CFStringRef str = reinterpret_cast<const CFStringRef>(aString);
+ if(!str)
+ return QString();
+ CFIndex length = CFStringGetLength(str);
+ const UniChar *chars = CFStringGetCharactersPtr(str);
+ if (chars)
+ return QString(reinterpret_cast<const QChar *>(chars), length);
+
+ QVarLengthArray<UniChar> buffer(length);
+ CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
+ return QString(reinterpret_cast<const QChar *>(buffer.constData()), length);
+}
+
+#endif /* __OBJC__ */
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_VBoxCocoaHelper_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm
new file mode 100644
index 00000000..cd355442
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin-cocoa.mm
@@ -0,0 +1,730 @@
+/* $Id: VBoxUtils-darwin-cocoa.mm $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling Darwin Cocoa specific tasks.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "VBoxUtils-darwin.h"
+#include "VBoxCocoaHelper.h"
+
+#include <QMenu>
+
+#include <iprt/assert.h>
+
+#import <AppKit/NSEvent.h>
+#import <AppKit/NSColor.h>
+#import <AppKit/NSFont.h>
+#import <AppKit/NSScreen.h>
+#import <AppKit/NSScroller.h>
+#import <AppKit/NSWindow.h>
+#import <AppKit/NSImageView.h>
+
+#import <objc/objc-class.h>
+
+/* For the keyboard stuff */
+#include <Carbon/Carbon.h>
+#include "DarwinKeyboard.h"
+
+/** Easy way of dynamical call for 10.7 AppKit functionality we do not yet support. */
+#define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7)
+#define NSFullScreenWindowMask (1 << 14)
+
+NativeNSWindowRef darwinToNativeWindowImpl(NativeNSViewRef pView)
+{
+ NativeNSWindowRef window = NULL;
+ if (pView)
+ window = [pView window];
+
+ return window;
+}
+
+NativeNSViewRef darwinToNativeViewImpl(NativeNSWindowRef pWindow)
+{
+ NativeNSViewRef view = NULL;
+ if (pWindow)
+ view = [pWindow contentView];
+
+ return view;
+}
+
+NativeNSButtonRef darwinNativeButtonOfWindowImpl(NativeNSWindowRef pWindow, StandardWindowButtonType enmButtonType)
+{
+ /* Return corresponding button: */
+ switch (enmButtonType)
+ {
+ case StandardWindowButtonType_Close: return [pWindow standardWindowButton:NSWindowCloseButton];
+ case StandardWindowButtonType_Miniaturize: return [pWindow standardWindowButton:NSWindowMiniaturizeButton];
+ case StandardWindowButtonType_Zoom: return [pWindow standardWindowButton:NSWindowZoomButton];
+ case StandardWindowButtonType_Toolbar: return [pWindow standardWindowButton:NSWindowToolbarButton];
+ case StandardWindowButtonType_DocumentIcon: return [pWindow standardWindowButton:NSWindowDocumentIconButton];
+ case StandardWindowButtonType_DocumentVersions: /*return [pWindow standardWindowButton:NSWindowDocumentVersionsButton];*/ break;
+ case StandardWindowButtonType_FullScreen: /*return [pWindow standardWindowButton:NSWindowFullScreenButton];*/ break;
+ }
+ /* Return Nul by default: */
+ return Nil;
+}
+
+NativeNSImageRef darwinToNSImageRef(const CGImageRef pImage)
+{
+ /* Create a bitmap rep from the image. */
+ NSBitmapImageRep *bitmapRep = [[[NSBitmapImageRep alloc] initWithCGImage:pImage] autorelease];
+ /* Create an NSImage and add the bitmap rep to it */
+ NSImage *image = [[NSImage alloc] init];
+ [image addRepresentation:bitmapRep];
+ return image;
+}
+
+NativeNSImageRef darwinToNSImageRef(const QImage *pImage)
+{
+ /* Create CGImage on the basis of passed QImage: */
+ CGImageRef pCGImage = ::darwinToCGImageRef(pImage);
+ NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
+ CGImageRelease(pCGImage);
+ /* Apply device pixel ratio: */
+ double dScaleFactor = pImage->devicePixelRatio();
+ NSSize imageSize = { (CGFloat)pImage->width() / dScaleFactor,
+ (CGFloat)pImage->height() / dScaleFactor };
+ [pNSImage setSize:imageSize];
+ /* Return result: */
+ return pNSImage;
+}
+
+NativeNSImageRef darwinToNSImageRef(const QPixmap *pPixmap)
+{
+ CGImageRef pCGImage = ::darwinToCGImageRef(pPixmap);
+ NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
+ CGImageRelease(pCGImage);
+ return pNSImage;
+}
+
+NativeNSImageRef darwinToNSImageRef(const char *pczSource)
+{
+ CGImageRef pCGImage = ::darwinToCGImageRef(pczSource);
+ NativeNSImageRef pNSImage = ::darwinToNSImageRef(pCGImage);
+ CGImageRelease(pCGImage);
+ return pNSImage;
+}
+
+NativeNSStringRef darwinToNativeString(const char* pcszString)
+{
+ return [NSString stringWithUTF8String: pcszString];
+}
+
+QString darwinFromNativeString(NativeNSStringRef pString)
+{
+ return [pString cStringUsingEncoding :NSASCIIStringEncoding];
+}
+
+void darwinSetShowsToolbarButtonImpl(NativeNSWindowRef pWindow, bool fEnabled)
+{
+ [pWindow setShowsToolbarButton:fEnabled];
+}
+
+void darwinLabelWindow(NativeNSWindowRef pWindow, NativeNSImageRef pImage, double dDpr)
+{
+ /* Get the parent view of the close button. */
+ NSView *wv = [[pWindow standardWindowButton:NSWindowCloseButton] superview];
+ if (wv)
+ {
+ /* We have to calculate the size of the title bar for the center case. */
+ NSSize s = [pImage size];
+ NSSize s1 = [wv frame].size;
+ /* Correctly position the label. */
+ NSImageView *iv = [[NSImageView alloc] initWithFrame:NSMakeRect(s1.width - s.width / dDpr,
+ s1.height - s.height / dDpr - 1,
+ s.width / dDpr, s.height / dDpr)];
+ /* Configure the NSImageView for auto moving. */
+ [iv setImage:pImage];
+ [iv setAutoresizesSubviews:true];
+ [iv setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
+ /* Add it to the parent of the close button. */
+ [wv addSubview:iv positioned:NSWindowBelow relativeTo:nil];
+ }
+}
+
+void darwinSetShowsResizeIndicatorImpl(NativeNSWindowRef pWindow, bool fEnabled)
+{
+ [pWindow setShowsResizeIndicator:fEnabled];
+}
+
+void darwinSetHidesAllTitleButtonsImpl(NativeNSWindowRef pWindow)
+{
+ /* Remove all title buttons by changing the style mask. This method is
+ available from 10.6 on only. */
+ if ([pWindow respondsToSelector: @selector(setStyleMask:)])
+ [pWindow performSelector: @selector(setStyleMask:) withObject: (id)NSTitledWindowMask];
+ else
+ {
+ /* On pre 10.6 disable all the buttons currently displayed. Don't use
+ setHidden cause this remove the buttons, but didn't release the
+ place used for the buttons. */
+ NSButton *pButton = [pWindow standardWindowButton:NSWindowCloseButton];
+ if (pButton != Nil)
+ [pButton setEnabled: NO];
+ pButton = [pWindow standardWindowButton:NSWindowMiniaturizeButton];
+ if (pButton != Nil)
+ [pButton setEnabled: NO];
+ pButton = [pWindow standardWindowButton:NSWindowZoomButton];
+ if (pButton != Nil)
+ [pButton setEnabled: NO];
+ pButton = [pWindow standardWindowButton:NSWindowDocumentIconButton];
+ if (pButton != Nil)
+ [pButton setEnabled: NO];
+ }
+}
+
+void darwinSetShowsWindowTransparentImpl(NativeNSWindowRef pWindow, bool fEnabled)
+{
+ if (fEnabled)
+ {
+ [pWindow setOpaque:NO];
+ [pWindow setBackgroundColor:[NSColor clearColor]];
+ [pWindow setHasShadow:NO];
+ }
+ else
+ {
+ [pWindow setOpaque:YES];
+ [pWindow setBackgroundColor:[NSColor windowBackgroundColor]];
+ [pWindow setHasShadow:YES];
+ }
+}
+
+void darwinSetWindowHasShadow(NativeNSWindowRef pWindow, bool fEnabled)
+{
+ if (fEnabled)
+ [pWindow setHasShadow :YES];
+ else
+ [pWindow setHasShadow :NO];
+}
+
+void darwinMinaturizeWindow(NativeNSWindowRef pWindow)
+{
+ RT_NOREF(pWindow);
+// [[NSApplication sharedApplication] miniaturizeAll];
+// printf("bla\n");
+// [pWindow miniaturize:pWindow];
+// [[NSApplication sharedApplication] deactivate];
+// [pWindow performMiniaturize:nil];
+}
+
+void darwinEnableFullscreenSupport(NativeNSWindowRef pWindow)
+{
+ [pWindow setCollectionBehavior :NSWindowCollectionBehaviorFullScreenPrimary];
+}
+
+void darwinEnableTransienceSupport(NativeNSWindowRef pWindow)
+{
+ [pWindow setCollectionBehavior :NSWindowCollectionBehaviorTransient];
+}
+
+void darwinToggleFullscreenMode(NativeNSWindowRef pWindow)
+{
+ /* Toggle native fullscreen mode for passed pWindow. This method is available since 10.7 only. */
+ if ([pWindow respondsToSelector: @selector(toggleFullScreen:)])
+ [pWindow performSelector: @selector(toggleFullScreen:) withObject: (id)nil];
+}
+
+void darwinToggleWindowZoom(NativeNSWindowRef pWindow)
+{
+ /* Toggle native window zoom for passed pWindow. This method is available since 10.0. */
+ if ([pWindow respondsToSelector: @selector(zoom:)])
+ [pWindow performSelector: @selector(zoom:)];
+}
+
+bool darwinIsInFullscreenMode(NativeNSWindowRef pWindow)
+{
+ /* Check whether passed pWindow is in native fullscreen mode. */
+ return [pWindow styleMask] & NSFullScreenWindowMask;
+}
+
+bool darwinIsOnActiveSpace(NativeNSWindowRef pWindow)
+{
+ /* Check whether passed pWindow is on active space. */
+ return [pWindow isOnActiveSpace];
+}
+
+bool darwinScreensHaveSeparateSpaces()
+{
+ /* Check whether screens have separate spaces.
+ * This method is available since 10.9 only. */
+ if ([NSScreen respondsToSelector: @selector(screensHaveSeparateSpaces)])
+ return [NSScreen performSelector: @selector(screensHaveSeparateSpaces)];
+ else
+ return false;
+}
+
+bool darwinIsScrollerStyleOverlay()
+{
+ /* Check whether scrollers by default have legacy style.
+ * This method is available since 10.7 only. */
+ if ([NSScroller respondsToSelector: @selector(preferredScrollerStyle)])
+ {
+ const int enmType = (int)(intptr_t)[NSScroller performSelector: @selector(preferredScrollerStyle)];
+ return enmType == NSScrollerStyleOverlay;
+ }
+ else
+ return false;
+}
+
+/**
+ * Calls the + (void)setMouseCoalescingEnabled:(BOOL)flag class method.
+ *
+ * @param fEnabled Whether to enable or disable coalescing.
+ */
+void darwinSetMouseCoalescingEnabled(bool fEnabled)
+{
+ [NSEvent setMouseCoalescingEnabled:fEnabled];
+}
+
+void darwinWindowAnimateResizeImpl(NativeNSWindowRef pWindow, int x, int y, int width, int height)
+{
+ RT_NOREF(x, y, width);
+
+ /* It seems that Qt doesn't return the height of the window with the
+ * toolbar height included. So add this size manually. Could easily be that
+ * the Trolls fix this in the final release. */
+ NSToolbar *toolbar = [pWindow toolbar];
+ NSRect windowFrame = [pWindow frame];
+ int toolbarHeight = 0;
+ if(toolbar && [toolbar isVisible])
+ toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
+ int h = height + toolbarHeight;
+ int h1 = h - NSHeight(windowFrame);
+ windowFrame.size.height = h;
+ windowFrame.origin.y -= h1;
+
+ [pWindow setFrame:windowFrame display:YES animate: YES];
+}
+
+void darwinWindowAnimateResizeNewImpl(NativeNSWindowRef pWindow, int height, bool fAnimate)
+{
+ /* It seems that Qt doesn't return the height of the window with the
+ * toolbar height included. So add this size manually. Could easily be that
+ * the Trolls fix this in the final release. */
+ NSToolbar *toolbar = [pWindow toolbar];
+ NSRect windowFrame = [pWindow frame];
+ int toolbarHeight = 0;
+ if(toolbar && [toolbar isVisible])
+ toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
+ int h = height + toolbarHeight;
+ int h1 = h - NSHeight(windowFrame);
+ windowFrame.size.height = h;
+ windowFrame.origin.y -= h1;
+
+ [pWindow setFrame:windowFrame display:YES animate: fAnimate ? YES : NO];
+}
+
+void darwinTest(NativeNSViewRef pViewOld, NativeNSViewRef pViewNew, int h)
+{
+ NSMutableDictionary *pDicts[3] = { nil, nil, nil };
+ int c = 0;
+
+ /* Scaling necessary? */
+ if (h != -1)
+ {
+ NSWindow *pWindow = [(pViewOld ? pViewOld : pViewNew) window];
+ NSToolbar *toolbar = [pWindow toolbar];
+ NSRect windowFrame = [pWindow frame];
+ /* Dictionary containing all animation parameters. */
+ pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
+ /* Specify the animation target. */
+ [pDicts[c] setObject:pWindow forKey:NSViewAnimationTargetKey];
+ /* Scaling effect. */
+ [pDicts[c] setObject:[NSValue valueWithRect:windowFrame] forKey:NSViewAnimationStartFrameKey];
+ int toolbarHeight = 0;
+ if(toolbar && [toolbar isVisible])
+ toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]);
+ int h1 = h + toolbarHeight;
+ int h2 = h1 - NSHeight(windowFrame);
+ windowFrame.size.height = h1;
+ windowFrame.origin.y -= h2;
+ [pDicts[c] setObject:[NSValue valueWithRect:windowFrame] forKey:NSViewAnimationEndFrameKey];
+ ++c;
+ }
+ /* Fade out effect. */
+ if (pViewOld)
+ {
+ /* Dictionary containing all animation parameters. */
+ pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
+ /* Specify the animation target. */
+ [pDicts[c] setObject:pViewOld forKey:NSViewAnimationTargetKey];
+ /* Fade out effect. */
+ [pDicts[c] setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
+ ++c;
+ }
+ /* Fade in effect. */
+ if (pViewNew)
+ {
+ /* Dictionary containing all animation parameters. */
+ pDicts[c] = [NSMutableDictionary dictionaryWithCapacity:2];
+ /* Specify the animation target. */
+ [pDicts[c] setObject:pViewNew forKey:NSViewAnimationTargetKey];
+ /* Fade in effect. */
+ [pDicts[c] setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
+ ++c;
+ }
+ /* Create our animation object. */
+ NSViewAnimation *pAni = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:pDicts count:c]];
+ [pAni setDuration:.15];
+ [pAni setAnimationCurve:NSAnimationEaseIn];
+ [pAni setAnimationBlockingMode:NSAnimationBlocking];
+// [pAni setAnimationBlockingMode:NSAnimationNonblockingThreaded];
+
+ /* Run the animation. */
+ [pAni startAnimation];
+ /* Cleanup */
+ [pAni release];
+}
+
+void darwinWindowInvalidateShadowImpl(NativeNSWindowRef pWindow)
+{
+ [pWindow invalidateShadow];
+}
+
+int darwinWindowToolBarHeight(NativeNSWindowRef pWindow)
+{
+ NSToolbar *toolbar = [pWindow toolbar];
+ NSRect windowFrame = [pWindow frame];
+ int toolbarHeight = 0;
+ int theight = (NSHeight([NSWindow contentRectForFrameRect:[pWindow frame] styleMask:[pWindow styleMask]]) - NSHeight([[pWindow contentView] frame]));
+ /* toolbar height: */
+ if(toolbar && [toolbar isVisible])
+ /* title bar height: */
+ toolbarHeight = NSHeight(windowFrame) - NSHeight([[pWindow contentView] frame]) - theight;
+
+ return toolbarHeight;
+}
+
+int darwinWindowTitleHeight(NativeNSWindowRef pWindow)
+{
+ NSView *pSuperview = [[pWindow standardWindowButton:NSWindowCloseButton] superview];
+ NSSize sz = [pSuperview frame].size;
+ return sz.height;
+}
+
+bool darwinIsToolbarVisible(NativeNSWindowRef pWindow)
+{
+ NSToolbar *pToolbar = [pWindow toolbar];
+
+ return [pToolbar isVisible] == YES;
+}
+
+bool darwinIsWindowMaximized(NativeNSWindowRef pWindow)
+{
+ /* Mac OS X API NSWindow isZoomed returns true even for almost maximized windows,
+ * So implementing this by ourseleves by comparing visible screen-frame & window-frame: */
+ NSRect windowFrame = [pWindow frame];
+ NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
+
+ return (windowFrame.origin.x == screenFrame.origin.x) &&
+ (windowFrame.origin.y == screenFrame.origin.y) &&
+ (windowFrame.size.width == screenFrame.size.width) &&
+ (windowFrame.size.height == screenFrame.size.height);
+}
+
+bool darwinOpenFile(NativeNSStringRef pstrFile)
+{
+ return [[NSWorkspace sharedWorkspace] openFile:pstrFile];
+}
+
+float darwinSmallFontSize()
+{
+ float size = [NSFont systemFontSizeForControlSize: NSSmallControlSize];
+
+ return size;
+}
+
+bool darwinMouseGrabEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
+{
+ NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
+ NSEventType EvtType = [pEvent type];
+ NSWindow *pWin = ::darwinToNativeWindow((QWidget*)pvUser);
+ if ( pWin == [pEvent window]
+ && ( EvtType == NSLeftMouseDown
+ || EvtType == NSLeftMouseUp
+ || EvtType == NSRightMouseDown
+ || EvtType == NSRightMouseUp
+ || EvtType == NSOtherMouseDown
+ || EvtType == NSOtherMouseUp
+ || EvtType == NSLeftMouseDragged
+ || EvtType == NSRightMouseDragged
+ || EvtType == NSOtherMouseDragged
+ || EvtType == NSMouseMoved
+ || EvtType == NSScrollWheel))
+ {
+ /* When the mouse position is not associated to the mouse cursor, the x
+ and y values are reported as delta values. */
+ float x = [pEvent deltaX];
+ float y = [pEvent deltaY];
+ if (EvtType == NSScrollWheel)
+ {
+ /* In the scroll wheel case we have to do some magic, cause a
+ normal scroll wheel on a mouse behaves different to a trackpad.
+ The following is used within Qt. We use the same to get a
+ similar behavior. */
+ if ([pEvent respondsToSelector:@selector(deviceDeltaX:)])
+ x = (float)(intptr_t)[pEvent performSelector:@selector(deviceDeltaX)] * 2;
+ else
+ x = qBound(-120, (int)(x * 10000), 120);
+ if ([pEvent respondsToSelector:@selector(deviceDeltaY:)])
+ y = (float)(intptr_t)[pEvent performSelector:@selector(deviceDeltaY)] * 2;
+ else
+ y = qBound(-120, (int)(y * 10000), 120);
+ }
+ /* Get the buttons which where pressed when this event occurs. We have
+ to use Carbon here, cause the Cocoa method [NSEvent pressedMouseButtons]
+ is >= 10.6. */
+ uint32 buttonMask = 0;
+ GetEventParameter((EventRef)pvCarbonEvent, kEventParamMouseChord, typeUInt32, 0,
+ sizeof(buttonMask), 0, &buttonMask);
+ /* Produce a Qt event out of our info. */
+ ::darwinSendMouseGrabEvents((QWidget*)pvUser, EvtType, [pEvent buttonNumber], buttonMask, x, y);
+ return true;
+ }
+ return false;
+}
+
+/* Cocoa event handler which checks if the user right clicked at the unified
+ toolbar or the title area. */
+bool darwinUnifiedToolbarEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
+{
+ RT_NOREF(pvCarbonEvent);
+
+ NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
+ NSEventType EvtType = [pEvent type];
+ NSWindow *pWin = ::darwinToNativeWindow((QWidget*)pvUser);
+ /* First check for the right event type and that we are processing events
+ from the window which was registered by the user. */
+ if ( EvtType == NSRightMouseDown
+ && pWin == [pEvent window])
+ {
+ /* Get the mouse position of the event (screen coordinates) */
+ NSPoint point = [NSEvent mouseLocation];
+ /* Get the frame rectangle of the window (screen coordinates) */
+ NSRect winFrame = [pWin frame];
+ /* Calculate the height of the title and the toolbar */
+ int i = NSHeight(winFrame) - NSHeight([[pWin contentView] frame]);
+ /* Based on that height create a rectangle of the unified toolbar + title */
+ winFrame.origin.y += winFrame.size.height - i;
+ winFrame.size.height = i;
+ /* Check if the mouse press event was on the unified toolbar or title */
+ if (NSMouseInRect(point, winFrame, NO))
+ /* Create a Qt context menu event, with flipped screen coordinates */
+ ::darwinCreateContextMenuEvent(pvUser, point.x, NSHeight([[pWin screen] frame]) - point.y);
+ }
+ return false;
+}
+
+/**
+ * Check for some default application key combinations a Mac user expect, like
+ * CMD+Q or CMD+H.
+ *
+ * @returns true if such a key combo was hit, false otherwise.
+ * @param pEvent The Cocoa event.
+ */
+bool darwinIsApplicationCommand(ConstNativeNSEventRef pEvent)
+{
+ NSEventType eEvtType = [pEvent type];
+ bool fGlobalHotkey = false;
+//
+// if ( (eEvtType == NSKeyDown || eEvtType == NSKeyUp)
+// && [[NSApp mainMenu] performKeyEquivalent:pEvent])
+// return true;
+// return false;
+// && [[[NSApp mainMenu] delegate] menuHasKeyEquivalent:[NSApp mainMenu] forEvent:pEvent target:b action:a])
+
+ switch (eEvtType)
+ {
+ case NSKeyDown:
+ case NSKeyUp:
+ {
+ NSUInteger fEvtMask = [pEvent modifierFlags];
+ unsigned short KeyCode = [pEvent keyCode];
+ if ( ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) /* L+CMD */
+ || ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK))) /* R+CMD */
+ {
+ if ( KeyCode == 0x0c /* CMD+Q (Quit) */
+ || KeyCode == 0x04) /* CMD+H (Hide) */
+ fGlobalHotkey = true;
+ }
+ else if ( ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK)) /* L+ALT+CMD */
+ || ((fEvtMask & (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICERCMDKEYMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK)) == (NX_NONCOALSESCEDMASK | NX_ALTERNATEMASK | NX_DEVICERCMDKEYMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK))) /* R+ALT+CMD */
+ {
+ if (KeyCode == 0x04) /* ALT+CMD+H (Hide-Others) */
+ fGlobalHotkey = true;
+ }
+ break;
+ }
+ default: break;
+ }
+ return fGlobalHotkey;
+}
+
+void darwinRetranslateAppMenu()
+{
+ /* This is purely Qt internal. If the Trolls change something here, it will
+ not work anymore, but at least it will not be a burning man. */
+ if ([NSApp respondsToSelector:@selector(qt_qcocoamenuLoader)])
+ {
+ id loader = [NSApp performSelector:@selector(qt_qcocoamenuLoader)];
+ if ([loader respondsToSelector:@selector(qtTranslateApplicationMenu)])
+ [loader performSelector:@selector(qtTranslateApplicationMenu)];
+ }
+}
+
+/* Our resize proxy singleton. This class has two major tasks. First it is used
+ to proxy the windowWillResize selector of the Qt delegate. As this is class
+ global and therewith done for all Qt window instances, we have to track the
+ windows we are interested in. This is the second task. */
+@interface UIResizeProxy: NSObject
+{
+ NSMutableArray *m_pArray;
+ bool m_fInit;
+}
++(UIResizeProxy*)sharedResizeProxy;
+-(void)addWindow:(NSWindow*)pWindow;
+-(void)removeWindow:(NSWindow*)pWindow;
+-(BOOL)containsWindow:(NSWindow*)pWindow;
+@end
+
+static UIResizeProxy *gSharedResizeProxy = nil;
+
+@implementation UIResizeProxy
++(UIResizeProxy*)sharedResizeProxy
+{
+ if (gSharedResizeProxy == nil)
+ gSharedResizeProxy = [[super allocWithZone:NULL] init];
+ return gSharedResizeProxy;
+}
+-(id)init
+{
+ self = [super init];
+
+ m_fInit = false;
+
+ return self;
+}
+- (void)addWindow:(NSWindow*)pWindow
+{
+ if (!m_fInit)
+ {
+ /* Create an array which contains the registered windows. */
+ m_pArray = [[NSMutableArray alloc] init];
+ /* Swizzle the windowWillResize method. This means replacing the
+ original method with our own one and reroute the original one to
+ another name. */
+ Class oriClass = [[pWindow delegate] class];
+ Class myClass = [UIResizeProxy class];
+ SEL oriSel = @selector(windowWillResize:toSize:);
+ SEL qtSel = @selector(qtWindowWillResize:toSize:);
+ Method m1 = class_getInstanceMethod(oriClass, oriSel);
+ Method m2 = class_getInstanceMethod(myClass, oriSel);
+ Method m3 = class_getInstanceMethod(myClass, qtSel);
+ /* Overwrite the original implementation with our own one. old contains
+ the old implementation. */
+ IMP old = method_setImplementation(m1, method_getImplementation(m2));
+ /* Add a new method to our class with the old implementation. */
+ class_addMethod(oriClass, qtSel, old, method_getTypeEncoding(m3));
+ m_fInit = true;
+ }
+ [m_pArray addObject:pWindow];
+}
+- (void)removeWindow:(NSWindow*)pWindow
+{
+ [m_pArray removeObject:pWindow];
+}
+- (BOOL)containsWindow:(NSWindow*)pWindow
+{
+ return [m_pArray containsObject:pWindow];
+}
+- (NSSize)qtWindowWillResize:(NSWindow *)pWindow toSize:(NSSize)newFrameSize
+{
+ RT_NOREF(pWindow);
+ /* This is a fake implementation. It will be replaced by the original Qt
+ method. */
+ return newFrameSize;
+}
+- (NSSize)windowWillResize:(NSWindow *)pWindow toSize:(NSSize)newFrameSize
+{
+ /* Call the original implementation for newFrameSize. */
+ NSSize qtSize = [self qtWindowWillResize:pWindow toSize:newFrameSize];
+ /* Check if we are responsible for this window. */
+ if (![[UIResizeProxy sharedResizeProxy] containsWindow:pWindow])
+ return qtSize;
+ /* The static modifier method in NSEvent is >= 10.6. It allows us to check
+ the shift modifier state during the resize. If it is not available the
+ user have to press shift before he start to resize. */
+ if ([NSEvent respondsToSelector:@selector(modifierFlags)])
+ {
+ if (((int)(intptr_t)[NSEvent performSelector:@selector(modifierFlags)] & NSShiftKeyMask) == NSShiftKeyMask)
+ return qtSize;
+ }
+ else
+ {
+ /* Shift key pressed when this resize event was initiated? */
+ if (([pWindow resizeFlags] & NSShiftKeyMask) == NSShiftKeyMask)
+ return qtSize;
+ }
+ /* The default case is to calculate the aspect radio of the old size and
+ used it for the new size. */
+ NSSize s = [pWindow frame].size;
+ double a = (double)s.width / s.height;
+ NSSize newSize = NSMakeSize(newFrameSize.width, newFrameSize.width / a);
+ /* We have to make sure the new rectangle meets the minimum requirements. */
+ NSSize testSize = [self qtWindowWillResize:pWindow toSize:newSize];
+ if ( testSize.width > newSize.width
+ || testSize.height > newSize.height)
+ {
+ double w1 = testSize.width / newSize.width;
+ double h1 = testSize.height / newSize.height;
+ if ( w1 > 1
+ && w1 > h1)
+ {
+ newSize.width = testSize.width;
+ newSize.height = testSize.width * a;
+ }else if (h1 > 1)
+ {
+ newSize.width = testSize.height * a;
+ newSize.height = testSize.height;
+ }
+ }
+ return newSize;
+}
+@end
+
+void darwinInstallResizeDelegate(NativeNSWindowRef pWindow)
+{
+ [[UIResizeProxy sharedResizeProxy] addWindow:pWindow];
+}
+
+void darwinUninstallResizeDelegate(NativeNSWindowRef pWindow)
+{
+ [[UIResizeProxy sharedResizeProxy] removeWindow:pWindow];
+}
+
+void *darwinCocoaToCarbonEvent(void *pvCocoaEvent)
+{
+ NSEvent *pEvent = (NSEvent*)pvCocoaEvent;
+ return (void*)[pEvent eventRef];
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.cpp
new file mode 100644
index 00000000..e5dbfc13
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.cpp
@@ -0,0 +1,777 @@
+/* $Id: VBoxUtils-darwin.cpp $ */
+/** @file
+ * VBox Qt GUI - Utility Classes and Functions specific to Darwin.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#include <QMainWindow>
+#include <QApplication>
+#include <QWidget>
+#include <QToolBar>
+#include <QPainter>
+#include <QPixmap>
+#include <QContextMenuEvent>
+
+/* GUI includes: */
+#include "VBoxUtils-darwin.h"
+#include "VBoxCocoaHelper.h"
+#include "UICocoaApplication.h"
+
+/* Other VBox includes: */
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+
+/* System includes: */
+#include <Carbon/Carbon.h>
+
+NativeNSViewRef darwinToNativeView(QWidget *pWidget)
+{
+ if (pWidget)
+ return reinterpret_cast<NativeNSViewRef>(pWidget->winId());
+ return nil;
+}
+
+NativeNSWindowRef darwinToNativeWindow(QWidget *pWidget)
+{
+ if (pWidget)
+ return ::darwinToNativeWindowImpl(::darwinToNativeView(pWidget));
+ return nil;
+}
+
+NativeNSWindowRef darwinToNativeWindow(NativeNSViewRef aView)
+{
+ return ::darwinToNativeWindowImpl(aView);
+}
+
+NativeNSViewRef darwinToNativeView(NativeNSWindowRef aWindow)
+{
+ return ::darwinToNativeViewImpl(aWindow);
+}
+
+NativeNSWindowRef darwinNativeButtonOfWindow(QWidget *pWidget, StandardWindowButtonType enmButtonType)
+{
+ return ::darwinNativeButtonOfWindowImpl(::darwinToNativeWindow(pWidget), enmButtonType);
+}
+
+void darwinSetShowsToolbarButton(QToolBar *aToolBar, bool fEnabled)
+{
+ QWidget *parent = aToolBar->parentWidget();
+ if (parent)
+ ::darwinSetShowsToolbarButtonImpl(::darwinToNativeWindow(parent), fEnabled);
+}
+
+void darwinLabelWindow(QWidget *pWidget, QPixmap *pPixmap)
+{
+ ::darwinLabelWindow(::darwinToNativeWindow(pWidget), ::darwinToNSImageRef(pPixmap), pPixmap->devicePixelRatio());
+}
+
+void darwinSetHidesAllTitleButtons(QWidget *pWidget)
+{
+ /* Currently only necessary in the Cocoa version */
+ ::darwinSetHidesAllTitleButtonsImpl(::darwinToNativeWindow(pWidget));
+}
+
+void darwinSetShowsWindowTransparent(QWidget *pWidget, bool fEnabled)
+{
+ ::darwinSetShowsWindowTransparentImpl(::darwinToNativeWindow(pWidget), fEnabled);
+}
+
+void darwinSetWindowHasShadow(QWidget *pWidget, bool fEnabled)
+{
+ ::darwinSetWindowHasShadow(::darwinToNativeWindow(pWidget), fEnabled);
+}
+
+void darwinWindowAnimateResize(QWidget *pWidget, const QRect &aTarget)
+{
+ ::darwinWindowAnimateResizeImpl(::darwinToNativeWindow(pWidget), aTarget.x(), aTarget.y(), aTarget.width(), aTarget.height());
+}
+
+void darwinWindowAnimateResizeNew(QWidget *pWidget, int h, bool fAnimate)
+{
+ ::darwinWindowAnimateResizeNewImpl(::darwinToNativeWindow(pWidget), h, fAnimate);
+}
+
+void darwinTest(QWidget *pWidget1, QWidget *pWidget2, int h)
+{
+ ::darwinTest(::darwinToNativeView(pWidget1), ::darwinToNativeView(pWidget2), h);
+}
+
+void darwinWindowInvalidateShape(QWidget *pWidget)
+{
+ /* Here a simple update is enough! */
+ pWidget->update();
+}
+
+void darwinWindowInvalidateShadow(QWidget *pWidget)
+{
+ ::darwinWindowInvalidateShadowImpl(::darwinToNativeWindow(pWidget));
+}
+
+void darwinSetShowsResizeIndicator(QWidget *pWidget, bool fEnabled)
+{
+ ::darwinSetShowsResizeIndicatorImpl(::darwinToNativeWindow(pWidget), fEnabled);
+}
+
+bool darwinIsWindowMaximized(QWidget *pWidget)
+{
+ /* Currently only necessary in the Cocoa version */
+ return ::darwinIsWindowMaximized(::darwinToNativeWindow(pWidget));
+}
+
+void darwinMinaturizeWindow(QWidget *pWidget)
+{
+ return ::darwinMinaturizeWindow(::darwinToNativeWindow(pWidget));
+}
+
+void darwinEnableFullscreenSupport(QWidget *pWidget)
+{
+ return ::darwinEnableFullscreenSupport(::darwinToNativeWindow(pWidget));
+}
+
+void darwinEnableTransienceSupport(QWidget *pWidget)
+{
+ return ::darwinEnableTransienceSupport(::darwinToNativeWindow(pWidget));
+}
+
+void darwinToggleFullscreenMode(QWidget *pWidget)
+{
+ return ::darwinToggleFullscreenMode(::darwinToNativeWindow(pWidget));
+}
+
+void darwinToggleWindowZoom(QWidget *pWidget)
+{
+ return ::darwinToggleWindowZoom(::darwinToNativeWindow(pWidget));
+}
+
+bool darwinIsInFullscreenMode(QWidget *pWidget)
+{
+ return ::darwinIsInFullscreenMode(::darwinToNativeWindow(pWidget));
+}
+
+bool darwinIsOnActiveSpace(QWidget *pWidget)
+{
+ return ::darwinIsOnActiveSpace(::darwinToNativeWindow(pWidget));
+}
+
+void darwinInstallResizeDelegate(QWidget *pWidget)
+{
+ ::darwinInstallResizeDelegate(::darwinToNativeWindow(pWidget));
+}
+
+void darwinUninstallResizeDelegate(QWidget *pWidget)
+{
+ ::darwinUninstallResizeDelegate(::darwinToNativeWindow(pWidget));
+}
+
+bool darwinOpenFile(const QString& strFile)
+{
+ return ::darwinOpenFile(darwinToNativeString(strFile.toUtf8().constData()));
+}
+
+QString darwinSystemLanguage(void)
+{
+ /* Get the locales supported by our bundle */
+ CFArrayRef supportedLocales = ::CFBundleCopyBundleLocalizations(::CFBundleGetMainBundle());
+ /* Check them against the languages currently selected by the user */
+ CFArrayRef preferredLocales = ::CFBundleCopyPreferredLocalizationsFromArray(supportedLocales);
+ /* Get the one which is on top */
+ CFStringRef localeId = (CFStringRef)::CFArrayGetValueAtIndex(preferredLocales, 0);
+ /* Convert them to a C-string */
+ char localeName[20];
+ ::CFStringGetCString(localeId, localeName, sizeof(localeName), kCFStringEncodingUTF8);
+ /* Some cleanup */
+ ::CFRelease(supportedLocales);
+ ::CFRelease(preferredLocales);
+ QString id(localeName);
+ /* Check for some misbehavior */
+ if (id.isEmpty() ||
+ id.toLower() == "english")
+ id = "en";
+ return id;
+}
+
+void darwinDisableIconsInMenus(void)
+{
+ /* No icons in the menu of a mac application. */
+ QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, true);
+}
+
+int darwinWindowToolBarHeight(QWidget *pWidget)
+{
+ NOREF(pWidget);
+ return 0;
+}
+
+int darwinWindowTitleHeight(QWidget *pWidget)
+{
+ return ::darwinWindowTitleHeight(::darwinToNativeWindow(pWidget));
+}
+
+bool darwinIsToolbarVisible(QToolBar *pToolBar)
+{
+ bool fResult = false;
+ QWidget *pParent = pToolBar->parentWidget();
+ if (pParent)
+ fResult = ::darwinIsToolbarVisible(::darwinToNativeWindow(pParent));
+ return fResult;
+}
+
+
+bool darwinSetFrontMostProcess()
+{
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ return ::SetFrontProcess(&psn) == 0;
+}
+
+uint64_t darwinGetCurrentProcessId()
+{
+ uint64_t processId = 0;
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ if (::GetCurrentProcess(&psn) == 0)
+ processId = RT_MAKE_U64(psn.lowLongOfPSN, psn.highLongOfPSN);
+ return processId;
+}
+
+/* Proxy icon creation */
+QPixmap darwinCreateDragPixmap(const QPixmap& aPixmap, const QString &aText)
+{
+ QFontMetrics fm(qApp->font());
+ QRect tbRect = fm.boundingRect(aText);
+ const int h = qMax(aPixmap.height(), fm.ascent() + 1);
+ const int m = 2;
+ QPixmap dragPixmap(aPixmap.width() + tbRect.width() + m, h);
+ dragPixmap.fill(Qt::transparent);
+ QPainter painter(&dragPixmap);
+ painter.drawPixmap(0, qAbs(h - aPixmap.height()) / 2.0, aPixmap);
+ painter.setPen(Qt::white);
+ painter.drawText(QRect(aPixmap.width() + m, 1, tbRect.width(), h - 1), Qt::AlignLeft | Qt::AlignVCenter, aText);
+ painter.setPen(Qt::black);
+ painter.drawText(QRect(aPixmap.width() + m, 0, tbRect.width(), h - 1), Qt::AlignLeft | Qt::AlignVCenter, aText);
+ painter.end();
+ return dragPixmap;
+}
+
+/**
+ * Callback for deleting the QImage object when CGImageCreate is done
+ * with it (which is probably not until the returned CFGImageRef is released).
+ *
+ * @param info Pointer to the QImage.
+ */
+static void darwinDataProviderReleaseQImage(void *info, const void *, size_t)
+{
+ QImage *qimg = (QImage *)info;
+ delete qimg;
+}
+
+/**
+ * Converts a QPixmap to a CGImage.
+ *
+ * @returns CGImageRef for the new image. (Remember to release it when finished with it.)
+ * @param aPixmap Pointer to the QPixmap instance to convert.
+ */
+CGImageRef darwinToCGImageRef(const QImage *pImage)
+{
+ QImage *imageCopy = new QImage(*pImage);
+ /** @todo this code assumes 32-bit image input, the lazy bird convert image to 32-bit method is anything but optimal... */
+ if (imageCopy->format() != QImage::Format_ARGB32)
+ *imageCopy = imageCopy->convertToFormat(QImage::Format_ARGB32);
+ Assert(!imageCopy->isNull());
+
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ CGDataProviderRef dp = CGDataProviderCreateWithData(imageCopy, pImage->bits(), pImage->sizeInBytes(),
+ darwinDataProviderReleaseQImage);
+
+ CGBitmapInfo bmpInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
+ CGImageRef ir = CGImageCreate(imageCopy->width(), imageCopy->height(), 8, 32, imageCopy->bytesPerLine(), cs,
+ bmpInfo, dp, 0 /*decode */, 0 /* shouldInterpolate */,
+ kCGRenderingIntentDefault);
+ CGColorSpaceRelease(cs);
+ CGDataProviderRelease(dp);
+
+ Assert(ir);
+ return ir;
+}
+
+/**
+ * Converts a QPixmap to a CGImage.
+ *
+ * @returns CGImageRef for the new image. (Remember to release it when finished with it.)
+ * @param aPixmap Pointer to the QPixmap instance to convert.
+ */
+CGImageRef darwinToCGImageRef(const QPixmap *pPixmap)
+{
+ /* It seems Qt releases the memory to an returned CGImageRef when the
+ * associated QPixmap is destroyed. This shouldn't happen as long a
+ * CGImageRef has a retrain count. As a workaround we make a real copy. */
+ int bitmapBytesPerRow = pPixmap->width() * 4;
+ int bitmapByteCount = (bitmapBytesPerRow * pPixmap->height());
+ /* Create a memory block for the temporary image. It is initialized by zero
+ * which means black & zero alpha. */
+ void *pBitmapData = RTMemAllocZ(bitmapByteCount);
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ /* Create a context to paint on */
+ CGContextRef ctx = CGBitmapContextCreate(pBitmapData,
+ pPixmap->width(),
+ pPixmap->height(),
+ 8,
+ bitmapBytesPerRow,
+ cs,
+ kCGImageAlphaPremultipliedFirst);
+ /* Get the CGImageRef from Qt */
+ CGImageRef qtPixmap = pPixmap->toImage().toCGImage();
+ /* Draw the image from Qt & convert the context back to a new CGImageRef. */
+ CGContextDrawImage(ctx, CGRectMake(0, 0, pPixmap->width(), pPixmap->height()), qtPixmap);
+ CGImageRef newImage = CGBitmapContextCreateImage(ctx);
+ /* Now release all used resources */
+ CGImageRelease(qtPixmap);
+ CGContextRelease(ctx);
+ CGColorSpaceRelease(cs);
+ RTMemFree(pBitmapData);
+
+ /* Return the new CGImageRef */
+ return newImage;
+}
+
+/**
+ * Loads an image using Qt and converts it to a CGImage.
+ *
+ * @returns CGImageRef for the new image. (Remember to release it when finished with it.)
+ * @param aSource The source name.
+ */
+CGImageRef darwinToCGImageRef(const char *pczSource)
+{
+ QPixmap qpm(QString(":/") + pczSource);
+ Assert(!qpm.isNull());
+ return ::darwinToCGImageRef(&qpm);
+}
+
+void darwinRegisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow)
+{
+ UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(3) /* NSRightMouseDown */, ::darwinUnifiedToolbarEvents, pWindow);
+}
+
+void darwinUnregisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow)
+{
+ UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(3) /* NSRightMouseDown */, ::darwinUnifiedToolbarEvents, pWindow);
+}
+
+void darwinMouseGrab(QWidget *pWidget)
+{
+ CGAssociateMouseAndMouseCursorPosition(false);
+ UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(1) | /* NSLeftMouseDown */
+ RT_BIT_32(2) | /* NSLeftMouseUp */
+ RT_BIT_32(3) | /* NSRightMouseDown */
+ RT_BIT_32(4) | /* NSRightMouseUp */
+ RT_BIT_32(5) | /* NSMouseMoved */
+ RT_BIT_32(6) | /* NSLeftMouseDragged */
+ RT_BIT_32(7) | /* NSRightMouseDragged */
+ RT_BIT_32(25) | /* NSOtherMouseDown */
+ RT_BIT_32(26) | /* NSOtherMouseUp */
+ RT_BIT_32(27) | /* NSOtherMouseDragged */
+ RT_BIT_32(22), /* NSScrollWheel */
+ ::darwinMouseGrabEvents, pWidget);
+}
+
+void darwinMouseRelease(QWidget *pWidget)
+{
+ UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(1) | /* NSLeftMouseDown */
+ RT_BIT_32(2) | /* NSLeftMouseUp */
+ RT_BIT_32(3) | /* NSRightMouseDown */
+ RT_BIT_32(4) | /* NSRightMouseUp */
+ RT_BIT_32(5) | /* NSMouseMoved */
+ RT_BIT_32(6) | /* NSLeftMouseDragged */
+ RT_BIT_32(7) | /* NSRightMouseDragged */
+ RT_BIT_32(25) | /* NSOtherMouseDown */
+ RT_BIT_32(26) | /* NSOtherMouseUp */
+ RT_BIT_32(27) | /* NSOtherMouseDragged */
+ RT_BIT_32(22), /* NSScrollWheel */
+ ::darwinMouseGrabEvents, pWidget);
+ CGAssociateMouseAndMouseCursorPosition(true);
+}
+
+void darwinSendMouseGrabEvents(QWidget *pWidget, int type, int button, int buttons, int x, int y)
+{
+ QEvent::Type qtType = QEvent::None;
+ Qt::MouseButtons qtButtons = Qt::NoButton;
+ Qt::MouseButton qtButton = Qt::NoButton;
+ Qt::MouseButton qtExtraButton = Qt::NoButton;
+ Qt::Orientation qtOrientation = Qt::Horizontal;
+ int wheelDelta = 0;
+ /* Which button is used in the NSOtherMouse... cases? */
+ if (button == 0)
+ qtExtraButton = Qt::LeftButton;
+ else if (button == 1)
+ qtExtraButton = Qt::RightButton;
+ else if (button == 2)
+ qtExtraButton = Qt::MiddleButton;
+ else if (button == 3)
+ qtExtraButton = Qt::XButton1;
+ else if (button == 4)
+ qtExtraButton = Qt::XButton2;
+ /* Map the NSEvent to a QEvent and define the Qt::Buttons when necessary. */
+ switch(type)
+ {
+ case 1: /* NSLeftMouseDown */
+ {
+ qtType = QEvent::MouseButtonPress;
+ qtButton = Qt::LeftButton;
+ break;
+ }
+ case 2: /* NSLeftMouseUp */
+ {
+ qtType = QEvent::MouseButtonRelease;
+ qtButton = Qt::LeftButton;
+ break;
+ }
+ case 3: /* NSRightMouseDown */
+ {
+ qtType = QEvent::MouseButtonPress;
+ qtButton = Qt::RightButton;
+ break;
+ }
+ case 4: /* NSRightMouseUp */
+ {
+ qtType = QEvent::MouseButtonRelease;
+ qtButton = Qt::RightButton;
+ break;
+ }
+ case 5: /* NSMouseMoved */
+ {
+ qtType = QEvent::MouseMove;
+ break;
+ }
+ case 6: /* NSLeftMouseDragged */
+ {
+ qtType = QEvent::MouseMove;
+ qtButton = Qt::LeftButton;
+ break;
+ }
+ case 7: /* NSRightMouseDragged */
+ {
+ qtType = QEvent::MouseMove;
+ qtButton = Qt::RightButton;
+ break;
+ }
+ case 22: /* NSScrollWheel */
+ {
+ qtType = QEvent::Wheel;
+ if (y != 0)
+ {
+ wheelDelta = y;
+ qtOrientation = Qt::Vertical;
+ }
+ else if (x != 0)
+ {
+ wheelDelta = x;
+ qtOrientation = Qt::Horizontal;
+ }
+ x = y = 0;
+ break;
+ }
+ case 25: /* NSOtherMouseDown */
+ {
+ qtType = QEvent::MouseButtonPress;
+ qtButton = qtExtraButton;
+ break;
+ }
+ case 26: /* NSOtherMouseUp */
+ {
+ qtType = QEvent::MouseButtonRelease;
+ qtButton = qtExtraButton;
+ break;
+ }
+ case 27: /* NSOtherMouseDragged */
+ {
+ qtType = QEvent::MouseMove;
+ qtButton = qtExtraButton;
+ break;
+ }
+ default: return;
+ }
+ /* Create a Qt::MouseButtons Mask. */
+ if ((buttons & RT_BIT_32(0)) == RT_BIT_32(0))
+ qtButtons |= Qt::LeftButton;
+ if ((buttons & RT_BIT_32(1)) == RT_BIT_32(1))
+ qtButtons |= Qt::RightButton;
+ if ((buttons & RT_BIT_32(2)) == RT_BIT_32(2))
+ qtButtons |= Qt::MiddleButton;
+ if ((buttons & RT_BIT_32(3)) == RT_BIT_32(3))
+ qtButtons |= Qt::XButton1;
+ if ((buttons & RT_BIT_32(4)) == RT_BIT_32(4))
+ qtButtons |= Qt::XButton2;
+ /* Create a new mouse delta event and send it to the widget. */
+ UIGrabMouseEvent *pEvent = new UIGrabMouseEvent(qtType, qtButton, qtButtons, x, y, wheelDelta, qtOrientation);
+ qApp->sendEvent(pWidget, pEvent);
+}
+
+void darwinCreateContextMenuEvent(void *pvUser, int x, int y)
+{
+ QWidget *pWin = static_cast<QWidget*>(pvUser);
+ QPoint global(x, y);
+ QPoint local = pWin->mapFromGlobal(global);
+ qApp->postEvent(pWin, new QContextMenuEvent(QContextMenuEvent::Mouse, local, global));
+}
+
+QString darwinResolveAlias(const QString &strFile)
+{
+ OSErr err = noErr;
+ FSRef fileRef;
+ QString strTarget;
+ do
+ {
+ Boolean fDir;
+ if ((err = FSPathMakeRef((const UInt8*)strFile.toUtf8().constData(), &fileRef, &fDir)) != noErr)
+ break;
+ Boolean fAlias = FALSE;
+ if ((err = FSIsAliasFile(&fileRef, &fAlias, &fDir)) != noErr)
+ break;
+ if (fAlias == TRUE)
+ {
+ if ((err = FSResolveAliasFile(&fileRef, TRUE, &fAlias, &fDir)) != noErr)
+ break;
+ char pszPath[1024];
+ if ((err = FSRefMakePath(&fileRef, (UInt8*)pszPath, 1024)) != noErr)
+ break;
+ strTarget = QString::fromUtf8(pszPath);
+ }
+ else
+ strTarget = strFile;
+ }while(0);
+
+ return strTarget;
+}
+
+
+/********************************************************************************
+ *
+ * Old carbon stuff. Have to convert soon!
+ *
+ ********************************************************************************/
+
+/* Event debugging stuff. Borrowed from Knuts Qt patch. */
+#if defined (DEBUG)
+
+# define MY_CASE(a) case a: return #a
+const char * DarwinDebugEventName(UInt32 ekind)
+{
+ switch (ekind)
+ {
+# if !__LP64__
+ MY_CASE(kEventWindowUpdate );
+ MY_CASE(kEventWindowDrawContent );
+# endif
+ MY_CASE(kEventWindowActivated );
+ MY_CASE(kEventWindowDeactivated );
+ MY_CASE(kEventWindowHandleActivate );
+ MY_CASE(kEventWindowHandleDeactivate );
+ MY_CASE(kEventWindowGetClickActivation );
+ MY_CASE(kEventWindowGetClickModality );
+ MY_CASE(kEventWindowShowing );
+ MY_CASE(kEventWindowHiding );
+ MY_CASE(kEventWindowShown );
+ MY_CASE(kEventWindowHidden );
+ MY_CASE(kEventWindowCollapsing );
+ MY_CASE(kEventWindowCollapsed );
+ MY_CASE(kEventWindowExpanding );
+ MY_CASE(kEventWindowExpanded );
+ MY_CASE(kEventWindowZoomed );
+ MY_CASE(kEventWindowBoundsChanging );
+ MY_CASE(kEventWindowBoundsChanged );
+ MY_CASE(kEventWindowResizeStarted );
+ MY_CASE(kEventWindowResizeCompleted );
+ MY_CASE(kEventWindowDragStarted );
+ MY_CASE(kEventWindowDragCompleted );
+ MY_CASE(kEventWindowClosed );
+ MY_CASE(kEventWindowTransitionStarted );
+ MY_CASE(kEventWindowTransitionCompleted );
+# if !__LP64__
+ MY_CASE(kEventWindowClickDragRgn );
+ MY_CASE(kEventWindowClickResizeRgn );
+ MY_CASE(kEventWindowClickCollapseRgn );
+ MY_CASE(kEventWindowClickCloseRgn );
+ MY_CASE(kEventWindowClickZoomRgn );
+ MY_CASE(kEventWindowClickContentRgn );
+ MY_CASE(kEventWindowClickProxyIconRgn );
+ MY_CASE(kEventWindowClickToolbarButtonRgn );
+ MY_CASE(kEventWindowClickStructureRgn );
+# endif
+ MY_CASE(kEventWindowCursorChange );
+ MY_CASE(kEventWindowCollapse );
+ MY_CASE(kEventWindowCollapseAll );
+ MY_CASE(kEventWindowExpand );
+ MY_CASE(kEventWindowExpandAll );
+ MY_CASE(kEventWindowClose );
+ MY_CASE(kEventWindowCloseAll );
+ MY_CASE(kEventWindowZoom );
+ MY_CASE(kEventWindowZoomAll );
+ MY_CASE(kEventWindowContextualMenuSelect );
+ MY_CASE(kEventWindowPathSelect );
+ MY_CASE(kEventWindowGetIdealSize );
+ MY_CASE(kEventWindowGetMinimumSize );
+ MY_CASE(kEventWindowGetMaximumSize );
+ MY_CASE(kEventWindowConstrain );
+# if !__LP64__
+ MY_CASE(kEventWindowHandleContentClick );
+# endif
+ MY_CASE(kEventWindowGetDockTileMenu );
+ MY_CASE(kEventWindowProxyBeginDrag );
+ MY_CASE(kEventWindowProxyEndDrag );
+ MY_CASE(kEventWindowToolbarSwitchMode );
+ MY_CASE(kEventWindowFocusAcquired );
+ MY_CASE(kEventWindowFocusRelinquish );
+ MY_CASE(kEventWindowFocusContent );
+ MY_CASE(kEventWindowFocusToolbar );
+ MY_CASE(kEventWindowFocusDrawer );
+ MY_CASE(kEventWindowSheetOpening );
+ MY_CASE(kEventWindowSheetOpened );
+ MY_CASE(kEventWindowSheetClosing );
+ MY_CASE(kEventWindowSheetClosed );
+ MY_CASE(kEventWindowDrawerOpening );
+ MY_CASE(kEventWindowDrawerOpened );
+ MY_CASE(kEventWindowDrawerClosing );
+ MY_CASE(kEventWindowDrawerClosed );
+ MY_CASE(kEventWindowDrawFrame );
+ MY_CASE(kEventWindowDrawPart );
+ MY_CASE(kEventWindowGetRegion );
+ MY_CASE(kEventWindowHitTest );
+ MY_CASE(kEventWindowInit );
+ MY_CASE(kEventWindowDispose );
+ MY_CASE(kEventWindowDragHilite );
+ MY_CASE(kEventWindowModified );
+ MY_CASE(kEventWindowSetupProxyDragImage );
+ MY_CASE(kEventWindowStateChanged );
+ MY_CASE(kEventWindowMeasureTitle );
+ MY_CASE(kEventWindowDrawGrowBox );
+ MY_CASE(kEventWindowGetGrowImageRegion );
+ MY_CASE(kEventWindowPaint );
+ }
+ static char s_sz[64];
+ sprintf(s_sz, "kind=%u", (uint)ekind);
+ return s_sz;
+}
+# undef MY_CASE
+
+/* Convert a class into the 4 char code defined in
+ * 'Developer/Headers/CFMCarbon/CarbonEvents.h' to
+ * identify the event. */
+const char * darwinDebugClassName(UInt32 eclass)
+{
+ char *pclass = (char*)&eclass;
+ static char s_sz[11];
+ sprintf(s_sz, "class=%c%c%c%c", pclass[3],
+ pclass[2],
+ pclass[1],
+ pclass[0]);
+ return s_sz;
+}
+
+void darwinDebugPrintEvent(const char *psz, EventRef evtRef)
+{
+ if (!evtRef)
+ return;
+ UInt32 ekind = GetEventKind(evtRef), eclass = GetEventClass(evtRef);
+ if (eclass == kEventClassWindow)
+ {
+ switch (ekind)
+ {
+# if !__LP64__
+ case kEventWindowDrawContent:
+ case kEventWindowUpdate:
+# endif
+ case kEventWindowBoundsChanged:
+ break;
+ default:
+ {
+ WindowRef wid = NULL;
+ GetEventParameter(evtRef, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &wid);
+ QWidget *widget = QWidget::find((WId)wid);
+ printf("%d %s: (%s) %#x win=%p wid=%p (%s)\n", (int)time(NULL), psz, darwinDebugClassName(eclass), (uint)ekind, wid, widget, DarwinDebugEventName(ekind));
+ break;
+ }
+ }
+ }
+ else if (eclass == kEventClassCommand)
+ {
+ WindowRef wid = NULL;
+ GetEventParameter(evtRef, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &wid);
+ QWidget *widget = QWidget::find((WId)wid);
+ const char *name = "Unknown";
+ switch (ekind)
+ {
+ case kEventCommandProcess:
+ name = "kEventCommandProcess";
+ break;
+ case kEventCommandUpdateStatus:
+ name = "kEventCommandUpdateStatus";
+ break;
+ }
+ printf("%d %s: (%s) %#x win=%p wid=%p (%s)\n", (int)time(NULL), psz, darwinDebugClassName(eclass), (uint)ekind, wid, widget, name);
+ }
+ else if (eclass == kEventClassKeyboard)
+ {
+ printf("%d %s: %#x(%s) %#x (kEventClassKeyboard)", (int)time(NULL), psz, (uint)eclass, darwinDebugClassName(eclass), (uint)ekind);
+
+ UInt32 keyCode = 0;
+ ::GetEventParameter(evtRef, kEventParamKeyCode, typeUInt32, NULL,
+ sizeof(keyCode), NULL, &keyCode);
+ printf(" keyCode=%d (%#x) ", (int)keyCode, (unsigned)keyCode);
+
+ char macCharCodes[8] = {0,0,0,0, 0,0,0,0};
+ ::GetEventParameter(evtRef, kEventParamKeyCode, typeChar, NULL,
+ sizeof(macCharCodes), NULL, &macCharCodes[0]);
+ printf(" macCharCodes={");
+ for (unsigned i =0; i < 8 && macCharCodes[i]; i++)
+ printf( i == 0 ? "%02x" : ",%02x", macCharCodes[i]);
+ printf("}");
+
+ UInt32 modifierMask = 0;
+ ::GetEventParameter(evtRef, kEventParamKeyModifiers, typeUInt32, NULL,
+ sizeof(modifierMask), NULL, &modifierMask);
+ printf(" modifierMask=%08x", (unsigned)modifierMask);
+
+ UniChar keyUnicodes[8] = {0,0,0,0, 0,0,0,0};
+ ::GetEventParameter(evtRef, kEventParamKeyUnicodes, typeUnicodeText, NULL,
+ sizeof(keyUnicodes), NULL, &keyUnicodes[0]);
+ printf(" keyUnicodes={");
+ for (unsigned i =0; i < 8 && keyUnicodes[i]; i++)
+ printf( i == 0 ? "%02x" : ",%02x", keyUnicodes[i]);
+ printf("}");
+
+ UInt32 keyboardType = 0;
+ ::GetEventParameter(evtRef, kEventParamKeyboardType, typeUInt32, NULL,
+ sizeof(keyboardType), NULL, &keyboardType);
+ printf(" keyboardType=%08x", (unsigned)keyboardType);
+
+ EventHotKeyID evtHotKeyId = {0,0};
+ ::GetEventParameter(evtRef, typeEventHotKeyID, typeEventHotKeyID, NULL,
+ sizeof(evtHotKeyId), NULL, &evtHotKeyId);
+ printf(" evtHotKeyId={signature=%08x, .id=%08x}", (unsigned)evtHotKeyId.signature, (unsigned)evtHotKeyId.id);
+ printf("\n");
+ }
+ else
+ printf("%d %s: %#x(%s) %#x\n", (int)time(NULL), psz, (uint)eclass, darwinDebugClassName(eclass), (uint)ekind);
+}
+
+#endif /* DEBUG */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.h b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.h
new file mode 100644
index 00000000..f545cf11
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VBoxUtils-darwin.h
@@ -0,0 +1,319 @@
+/* $Id: VBoxUtils-darwin.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling Darwin specific tasks.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_darwin_VBoxUtils_darwin_h
+#define FEQT_INCLUDED_SRC_platform_darwin_VBoxUtils_darwin_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QRect>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <VBox/VBoxCocoa.h>
+#include <ApplicationServices/ApplicationServices.h>
+#undef PVM // Stupid, stupid apple headers (sys/param.h)!!
+#include <iprt/cdefs.h>
+
+/* External includes: */
+#include <ApplicationServices/ApplicationServices.h>
+
+/* Forward declarations: */
+class QImage;
+class QMainWindow;
+class QMenu;
+class QPixmap;
+class QToolBar;
+class QWidget;
+
+/* Cocoa declarations: */
+ADD_COCOA_NATIVE_REF(NSButton);
+ADD_COCOA_NATIVE_REF(NSEvent);
+ADD_COCOA_NATIVE_REF(NSImage);
+ADD_COCOA_NATIVE_REF(NSString);
+ADD_COCOA_NATIVE_REF(NSView);
+ADD_COCOA_NATIVE_REF(NSWindow);
+
+
+/** Mac OS X: Standard window button types. */
+enum StandardWindowButtonType
+{
+ StandardWindowButtonType_Close, // Since OS X 10.2
+ StandardWindowButtonType_Miniaturize, // Since OS X 10.2
+ StandardWindowButtonType_Zoom, // Since OS X 10.2
+ StandardWindowButtonType_Toolbar, // Since OS X 10.2
+ StandardWindowButtonType_DocumentIcon, // Since OS X 10.2
+ StandardWindowButtonType_DocumentVersions, // Since OS X 10.7
+ StandardWindowButtonType_FullScreen // Since OS X 10.7
+};
+
+
+RT_C_DECLS_BEGIN
+
+/********************************************************************************
+ *
+ * Window/View management (OS System native)
+ *
+ ********************************************************************************/
+NativeNSWindowRef darwinToNativeWindowImpl(NativeNSViewRef pView);
+NativeNSViewRef darwinToNativeViewImpl(NativeNSWindowRef pWindow);
+NativeNSButtonRef darwinNativeButtonOfWindowImpl(NativeNSWindowRef pWindow, StandardWindowButtonType enmButtonType);
+SHARED_LIBRARY_STUFF NativeNSStringRef darwinToNativeString(const char* pcszString);
+QString darwinFromNativeString(NativeNSStringRef pString);
+
+/********************************************************************************
+ *
+ * Simple setter methods (OS System native)
+ *
+ ********************************************************************************/
+void darwinSetShowsToolbarButtonImpl(NativeNSWindowRef pWindow, bool fEnabled);
+void darwinSetShowsResizeIndicatorImpl(NativeNSWindowRef pWindow, bool fEnabled);
+void darwinSetHidesAllTitleButtonsImpl(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinLabelWindow(NativeNSWindowRef pWindow, NativeNSImageRef pImage, double dDpr);
+void darwinSetShowsWindowTransparentImpl(NativeNSWindowRef pWindow, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinSetWindowHasShadow(NativeNSWindowRef pWindow, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinSetMouseCoalescingEnabled(bool fEnabled);
+
+void darwintest(NativeNSWindowRef pWindow);
+/********************************************************************************
+ *
+ * Simple helper methods (OS System native)
+ *
+ ********************************************************************************/
+void darwinWindowAnimateResizeImpl(NativeNSWindowRef pWindow, int x, int y, int width, int height);
+void darwinWindowAnimateResizeNewImpl(NativeNSWindowRef pWindow, int height, bool fAnimate);
+void darwinTest(NativeNSViewRef pView, NativeNSViewRef pView1, int h);
+void darwinWindowInvalidateShapeImpl(NativeNSWindowRef pWindow);
+void darwinWindowInvalidateShadowImpl(NativeNSWindowRef pWindow);
+int darwinWindowToolBarHeight(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF int darwinWindowTitleHeight(NativeNSWindowRef pWindow);
+bool darwinIsToolbarVisible(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF bool darwinIsWindowMaximized(NativeNSWindowRef pWindow);
+void darwinMinaturizeWindow(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinEnableFullscreenSupport(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinEnableTransienceSupport(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinToggleFullscreenMode(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF void darwinToggleWindowZoom(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF bool darwinIsInFullscreenMode(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF bool darwinIsOnActiveSpace(NativeNSWindowRef pWindow);
+SHARED_LIBRARY_STUFF bool darwinScreensHaveSeparateSpaces();
+SHARED_LIBRARY_STUFF bool darwinIsScrollerStyleOverlay();
+
+bool darwinOpenFile(NativeNSStringRef pstrFile);
+
+SHARED_LIBRARY_STUFF float darwinSmallFontSize();
+SHARED_LIBRARY_STUFF bool darwinSetFrontMostProcess();
+SHARED_LIBRARY_STUFF uint64_t darwinGetCurrentProcessId();
+
+void darwinInstallResizeDelegate(NativeNSWindowRef pWindow);
+void darwinUninstallResizeDelegate(NativeNSWindowRef pWindow);
+
+bool darwinUnifiedToolbarEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);
+bool darwinMouseGrabEvents(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);
+void darwinCreateContextMenuEvent(void *pvWin, int x, int y);
+
+SHARED_LIBRARY_STUFF bool darwinIsApplicationCommand(ConstNativeNSEventRef pEvent);
+
+void darwinRetranslateAppMenu();
+
+void darwinSendMouseGrabEvents(QWidget *pWidget, int type, int button, int buttons, int x, int y);
+
+SHARED_LIBRARY_STUFF QString darwinResolveAlias(const QString &strFile);
+
+RT_C_DECLS_END
+
+DECLINLINE(CGRect) darwinToCGRect(const QRect& aRect) { return CGRectMake(aRect.x(), aRect.y(), aRect.width(), aRect.height()); }
+DECLINLINE(CGRect) darwinFlipCGRect(CGRect aRect, double aTargetHeight) { aRect.origin.y = aTargetHeight - aRect.origin.y - aRect.size.height; return aRect; }
+DECLINLINE(CGRect) darwinFlipCGRect(CGRect aRect, const CGRect &aTarget) { return darwinFlipCGRect(aRect, aTarget.size.height); }
+DECLINLINE(CGRect) darwinCenterRectTo(CGRect aRect, const CGRect& aToRect)
+{
+ aRect.origin.x = aToRect.origin.x + (aToRect.size.width - aRect.size.width) / 2.0;
+ aRect.origin.y = aToRect.origin.y + (aToRect.size.height - aRect.size.height) / 2.0;
+ return aRect;
+}
+
+/********************************************************************************
+ *
+ * Window/View management (Qt Wrapper)
+ *
+ ********************************************************************************/
+
+/**
+ * Returns a reference to the native View of the QWidget.
+ *
+ * @returns either HIViewRef or NSView* of the QWidget.
+ * @param pWidget Pointer to the QWidget
+ */
+NativeNSViewRef darwinToNativeView(QWidget *pWidget);
+
+/**
+ * Returns a reference to the native Window of the QWidget.
+ *
+ * @returns either WindowRef or NSWindow* of the QWidget.
+ * @param pWidget Pointer to the QWidget
+ */
+NativeNSWindowRef darwinToNativeWindow(QWidget *pWidget);
+
+/* This is necessary because of the C calling convention. Its a simple wrapper
+ for darwinToNativeWindowImpl to allow operator overloading which isn't
+ allowed in C. */
+/**
+ * Returns a reference to the native Window of the View..
+ *
+ * @returns either WindowRef or NSWindow* of the View.
+ * @param pWidget Pointer to the native View
+ */
+NativeNSWindowRef darwinToNativeWindow(NativeNSViewRef pView);
+
+/**
+ * Returns a reference to the native View of the Window.
+ *
+ * @returns either HIViewRef or NSView* of the Window.
+ * @param pWidget Pointer to the native Window
+ */
+NativeNSViewRef darwinToNativeView(NativeNSWindowRef pWindow);
+
+/**
+ * Returns a reference to the native button of QWidget.
+ *
+ * @returns corresponding NSButton* of the QWidget.
+ * @param pWidget Brings the pointer to the QWidget.
+ * @param enmButtonType Brings the type of the native button required.
+ */
+NativeNSButtonRef darwinNativeButtonOfWindow(QWidget *pWidget, StandardWindowButtonType enmButtonType);
+
+/********************************************************************************
+ *
+ * Graphics stuff (Qt Wrapper)
+ *
+ ********************************************************************************/
+/**
+ * Returns a reference to the CGContext of the QWidget.
+ *
+ * @returns CGContextRef of the QWidget.
+ * @param pWidget Pointer to the QWidget
+ */
+SHARED_LIBRARY_STUFF CGImageRef darwinToCGImageRef(const QImage *pImage);
+SHARED_LIBRARY_STUFF CGImageRef darwinToCGImageRef(const QPixmap *pPixmap);
+SHARED_LIBRARY_STUFF CGImageRef darwinToCGImageRef(const char *pczSource);
+
+SHARED_LIBRARY_STUFF NativeNSImageRef darwinToNSImageRef(const CGImageRef pImage);
+SHARED_LIBRARY_STUFF NativeNSImageRef darwinToNSImageRef(const QImage *pImage);
+SHARED_LIBRARY_STUFF NativeNSImageRef darwinToNSImageRef(const QPixmap *pPixmap);
+SHARED_LIBRARY_STUFF NativeNSImageRef darwinToNSImageRef(const char *pczSource);
+
+#include <QEvent>
+class UIGrabMouseEvent: public QEvent
+{
+public:
+ enum { GrabMouseEvent = QEvent::User + 200 };
+
+ UIGrabMouseEvent(QEvent::Type type, Qt::MouseButton button, Qt::MouseButtons buttons, int x, int y, int wheelDelta, Qt::Orientation o)
+ : QEvent((QEvent::Type)GrabMouseEvent)
+ , m_type(type)
+ , m_button(button)
+ , m_buttons(buttons)
+ , m_x(x)
+ , m_y(y)
+ , m_wheelDelta(wheelDelta)
+ , m_orientation(o)
+ {}
+ QEvent::Type mouseEventType() const { return m_type; }
+ Qt::MouseButton button() const { return m_button; }
+ Qt::MouseButtons buttons() const { return m_buttons; }
+ int xDelta() const { return m_x; }
+ int yDelta() const { return m_y; }
+ int wheelDelta() const { return m_wheelDelta; }
+ Qt::Orientation orientation() const { return m_orientation; }
+
+private:
+ /* Private members */
+ QEvent::Type m_type;
+ Qt::MouseButton m_button;
+ Qt::MouseButtons m_buttons;
+ int m_x;
+ int m_y;
+ int m_wheelDelta;
+ Qt::Orientation m_orientation;
+};
+
+/********************************************************************************
+ *
+ * Simple setter methods (Qt Wrapper)
+ *
+ ********************************************************************************/
+void darwinSetShowsToolbarButton(QToolBar *aToolBar, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinLabelWindow(QWidget *pWidget, QPixmap *pPixmap);
+void darwinSetShowsResizeIndicator(QWidget *pWidget, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinSetHidesAllTitleButtons(QWidget *pWidget);
+void darwinSetShowsWindowTransparent(QWidget *pWidget, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinSetWindowHasShadow(QWidget *pWidget, bool fEnabled);
+SHARED_LIBRARY_STUFF void darwinDisableIconsInMenus(void);
+
+void darwinTest(QWidget *pWidget1, QWidget *pWidget2, int h);
+
+/********************************************************************************
+ *
+ * Simple helper methods (Qt Wrapper)
+ *
+ ********************************************************************************/
+SHARED_LIBRARY_STUFF void darwinWindowAnimateResize(QWidget *pWidget, const QRect &aTarget);
+void darwinWindowAnimateResizeNew(QWidget *pWidget, int h, bool fAnimate);
+void darwinWindowInvalidateShape(QWidget *pWidget);
+void darwinWindowInvalidateShadow(QWidget *pWidget);
+int darwinWindowToolBarHeight(QWidget *pWidget);
+SHARED_LIBRARY_STUFF int darwinWindowTitleHeight(QWidget *pWidget);
+bool darwinIsToolbarVisible(QToolBar *pToolBar);
+SHARED_LIBRARY_STUFF bool darwinIsWindowMaximized(QWidget *pWidget);
+void darwinMinaturizeWindow(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinEnableFullscreenSupport(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinEnableTransienceSupport(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinToggleFullscreenMode(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinToggleWindowZoom(QWidget *pWidget);
+SHARED_LIBRARY_STUFF bool darwinIsInFullscreenMode(QWidget *pWidget);
+SHARED_LIBRARY_STUFF bool darwinIsOnActiveSpace(QWidget *pWidget);
+bool darwinOpenFile(const QString &strFile);
+
+QString darwinSystemLanguage(void);
+QPixmap darwinCreateDragPixmap(const QPixmap& aPixmap, const QString &aText);
+
+void darwinInstallResizeDelegate(QWidget *pWidget);
+void darwinUninstallResizeDelegate(QWidget *pWidget);
+
+SHARED_LIBRARY_STUFF void darwinRegisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow);
+SHARED_LIBRARY_STUFF void darwinUnregisterForUnifiedToolbarContextMenuEvents(QMainWindow *pWindow);
+
+SHARED_LIBRARY_STUFF void darwinMouseGrab(QWidget *pWidget);
+SHARED_LIBRARY_STUFF void darwinMouseRelease(QWidget *pWidget);
+
+SHARED_LIBRARY_STUFF void *darwinCocoaToCarbonEvent(void *pvCocoaEvent);
+
+#endif /* !FEQT_INCLUDED_SRC_platform_darwin_VBoxUtils_darwin_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-Info.plist b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-Info.plist
new file mode 100644
index 00000000..db617644
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundlePackageType</key> <string>APPL</string>
+ <key>CFBundleSignature</key> <string>VBVM</string>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.app.VirtualBoxVM</string>
+ <key>CFBundleName</key> <string>VirtualBox VM</string>
+ <key>CFBundleExecutable</key> <string>VirtualBoxVM</string>
+ <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+ <key>CFBundleIconFile</key> <string>virtualbox</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>LSCanProvideIMVideoDataSource</key> <true/>
+ <key>NSHighResolutionCapable</key> <true/>
+ <key>NSSupportsAutomaticGraphicsSwitching</key><true/>
+ <key>NSCameraUsageDescription</key> <string>VirtualBox needs camera access for emulated webcam passthrough</string>
+ <key>NSMicrophoneUsageDescription</key> <string>VirtualBox needs microphone access for guest audio input</string>
+</dict>
+</plist>
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-PkgInfo b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-PkgInfo
new file mode 100644
index 00000000..be1f395e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/VM-PkgInfo
@@ -0,0 +1 @@
+APPLVBVM \ No newline at end of file
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/tstDarwinKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/platform/darwin/tstDarwinKeyboard.cpp
new file mode 100644
index 00000000..0c6955ae
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/tstDarwinKeyboard.cpp
@@ -0,0 +1,108 @@
+/* $Id: tstDarwinKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI Testcase - Common GUI Library - Darwin Keyboard routines.
+ *
+ * @todo Move this up somewhere so that the two SDL GUIs can use parts of this code too (-HID crap).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/assert.h>
+
+#include "DarwinKeyboard.h"
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ AssertReleaseRCReturn(rc, 1);
+
+ /*
+ * Warmup tests.
+ */
+ RTPrintf("tstDarwinKeyboard: Warmup...\n");
+
+ RTTimeNanoTS();
+ DarwinGrabKeyboard(true);
+ DarwinReleaseKeyboard();
+
+ RTTimeNanoTS();
+ DarwinGrabKeyboard(true);
+ DarwinReleaseKeyboard();
+
+/* Test these too:
+unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode);
+UInt32 DarwinAdjustModifierMask(UInt32 fModifiers);
+unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers);
+unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers);
+UInt32 DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode);
+unsigned DarwinEventToSet1Scancode(EventRef Event, UInt32 *pfCurKeyModifiers);
+void DarwinDisableGlobalHotKeys(bool fDisable);
+*/
+
+ /*
+ * Grab and release the keyboard a lot of times and time it.
+ * We're looking both at performance and for memory and reference leaks here.
+ */
+ RTPrintf("tstDarwinKeyboard: Profiling Grab and Release");
+ RTStrmFlush(g_pStdOut);
+ const uint64_t u64Start = RTTimeNanoTS();
+ uint64_t u64Grab = 0;
+ uint64_t u64Release = 0;
+ unsigned i;
+ for (i = 0; i < 20; i++)
+ {
+ uint64_t u64 = RTTimeNanoTS();
+ DarwinGrabKeyboard(argc != 1);
+ u64Grab += RTTimeNanoTS() - u64;
+
+ u64 = RTTimeNanoTS();
+ DarwinReleaseKeyboard();
+ u64Release += RTTimeNanoTS() - u64;
+
+ if ((i % 10) == 0)
+ {
+ RTPrintf(".");
+ RTStrmFlush(g_pStdOut);
+ }
+ }
+ const uint64_t u64Elapsed = RTTimeNanoTS() - u64Start;
+ RTPrintf("\n"
+ "tstDarwinKeyboard: %u times in %RU64 ms - %RU64 ms per call\n",
+ i, u64Elapsed / 1000000, (u64Elapsed / i) / 1000000);
+ RTPrintf("tstDarwinKeyboard: DarwinGrabKeyboard: %RU64 ms total - %RU64 ms per call\n",
+ u64Grab / 1000000, (u64Grab / i) / 1000000);
+ RTPrintf("tstDarwinKeyboard: DarwinReleaseKeyboard: %RU64 ms total - %RU64 ms per call\n",
+ u64Release / 1000000, (u64Release / i) / 1000000);
+
+ return 0;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-Info.plist b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-Info.plist
new file mode 100644
index 00000000..303e0d48
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundlePackageType</key> <string>APPL</string>
+ <key>CFBundleSignature</key> <string>VMST</string>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.app.vmstarter</string>
+ <key>CFBundleName</key> <string>vmstarter</string>
+ <key>CFBundleExecutable</key> <string>vmstarter</string>
+ <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ vmstarter @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+ <key>CFBundleIconFile</key> <string>virtualbox</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>LSUIElement</key> <true/>
+ <key>NSHighResolutionCapable</key> <true/>
+ <key>NSSupportsAutomaticGraphicsSwitching</key><true/>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeName</key> <string>VirtualBox Machine Definition</string>
+ <key>CFBundleTypeExtensions</key> <array><string>vbox</string></array>
+ <key>CFBundleTypeRole</key> <string>Editor</string>
+ <key>LSHandlerRank</key> <string>Owner</string>
+ <key>CFBundleTypeIconFile</key> <string>virtualbox-vbox</string>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-PkgInfo b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-PkgInfo
new file mode 100644
index 00000000..c1388087
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter-PkgInfo
@@ -0,0 +1 @@
+APPLVBST
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter.mm b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter.mm
new file mode 100644
index 00000000..1aac4bd6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/darwin/vmstarter.mm
@@ -0,0 +1,140 @@
+/* $Id: vmstarter.mm $ */
+/** @file
+ * VBox Qt GUI - Helper application for starting vbox the right way when the user double clicks on a file type association.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <iprt/cdefs.h>
+
+@interface AppDelegate: NSObject
+{
+NSString *m_strVBoxPath;
+}
+@end
+
+@implementation AppDelegate
+-(id) init
+{
+ self = [super init];
+ if (self)
+ {
+ /* Get the path of VBox by looking where our bundle is located. */
+ m_strVBoxPath = [[[[NSBundle mainBundle] bundlePath]
+ stringByAppendingPathComponent:@"/../../../../VirtualBox.app"]
+ stringByStandardizingPath];
+ /* We kill ourself after 1 seconds */
+ [NSTimer scheduledTimerWithTimeInterval:1.0
+ target:NSApp
+ selector:@selector(terminate:)
+ userInfo:nil
+ repeats:NO];
+ }
+
+ return self;
+}
+
+- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
+{
+ RT_NOREF(sender);
+
+ BOOL fResult = FALSE;
+ NSWorkspace *pWS = [NSWorkspace sharedWorkspace];
+ /* We need to check if vbox is running already. If so we sent an open
+ event. If not we start a new process with the file as parameter. */
+ NSArray *pApps = [pWS runningApplications];
+ bool fVBoxRuns = false;
+ for (NSRunningApplication *pApp in pApps)
+ {
+ if ([pApp.bundleIdentifier isEqualToString:@"org.virtualbox.app.VirtualBox"])
+ {
+ fVBoxRuns = true;
+ break;
+ }
+ }
+ if (fVBoxRuns)
+ {
+ /* Send the open event.
+ * Todo: check for an method which take a list of files. */
+ for (NSString *filename in filenames)
+ fResult = [pWS openFile:filename withApplication:m_strVBoxPath andDeactivate:TRUE];
+ }
+ else
+ {
+ /* Fire up a new instance of VBox. We prefer LSOpenApplication over
+ NSTask, cause it makes sure that VBox will become the front most
+ process after starting up. */
+/** @todo should replace all this with -[NSWorkspace
+ * launchApplicationAtURL:options:configuration:error:] because LSOpenApplication is deprecated in
+ * 10.10 while, FSPathMakeRef is deprecated since 10.8. */
+ /* The state horror show starts right here: */
+ OSStatus err = noErr;
+ Boolean fDir;
+ void *asyncLaunchRefCon = NULL;
+ FSRef fileRef;
+ CFStringRef file = NULL;
+ CFArrayRef args = NULL;
+ void **list = (void**)malloc(sizeof(void*) * [filenames count]);
+ for (size_t i = 0; i < [filenames count]; ++i)
+ list[i] = [filenames objectAtIndex:i];
+ do
+ {
+ NSString *strVBoxExe = [m_strVBoxPath stringByAppendingPathComponent:@"Contents/MacOS/VirtualBox"];
+ err = FSPathMakeRef((const UInt8*)[strVBoxExe UTF8String], &fileRef, &fDir);
+ if (err != noErr)
+ break;
+ args = CFArrayCreate(NULL, (const void **)list, [filenames count], &kCFTypeArrayCallBacks);
+ if (args == NULL)
+ break;
+ LSApplicationParameters par = { 0, 0, &fileRef, asyncLaunchRefCon, 0, args, 0 };
+ err = LSOpenApplication(&par, NULL);
+ if (err != noErr)
+ break;
+ fResult = TRUE;
+ }while(0);
+ if (list) /* Why bother checking, because you've crashed already if it's NULL! */
+ free(list);
+ if (file)
+ CFRelease(file);
+ if (args)
+ CFRelease(args);
+ }
+}
+@end
+
+int main()
+{
+ /* Global auto release pool. */
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ /* Create our own delegate for the application. */
+ AppDelegate *pAppDelegate = [[AppDelegate alloc] init];
+ [[NSApplication sharedApplication] setDelegate: (id<NSApplicationDelegate>)pAppDelegate]; /** @todo check out ugly cast */
+ pAppDelegate = nil;
+ /* Start the event loop. */
+ [NSApp run];
+ /* Cleanup */
+ [pool release];
+ return 0;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.asm b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.asm
new file mode 100644
index 00000000..573c797a
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.asm
@@ -0,0 +1,71 @@
+; $Id: VBoxHlp.asm $
+;; @file
+; VBox Qt GUI - Implementation of OS/2-specific helpers that require to reside in a DLL.
+;
+; This stub is used to avoid linking the helper DLL to the C runtime.
+;
+
+;
+; Copyright (C) 2008-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; SPDX-License-Identifier: GPL-3.0-only
+;
+
+;; @todo BEGINCODE gives us this:
+;
+; 02-03-2008 22:19:37 SYS3175 PID 4383 TID 0001 Slot 0076
+; D:\CODING\INNOTEK\VBOX\OUT\OS2.X86\RELEASE\BIN\VIRTUALBOX.EXE
+; c0000005
+; 17d40000
+; P1=00000008 P2=0000bea4 P3=XXXXXXXX P4=XXXXXXXX
+; EAX=00001489 EBX=00000000 ECX=00000000 EDX=00000000
+; ESI=00000000 EDI=00001489
+; DS=be7f DSACC=00f3 DSLIM=0000003f
+; ES=0053 ESACC=f0f3 ESLIM=ffffffff
+; FS=150b FSACC=00f3 FSLIM=00000030
+; GS=0000 GSACC=**** GSLIM=********
+; CS:EIP=bea7:00000000 CSACC=00f2 CSLIM=00000002
+; SS:ESP=01d7:0000ffe8 SSACC=00f3 SSLIM=0000ffff
+; EBP=00000000 FLG=00012202
+;
+; VBOXHLP.DLL 0003:00000000
+;
+; Looks like the previous 'segment TEXT32 ...' definition in asmdefs.mac
+; is ignored and the segment is redefined as if it had no 'CLASS=CODE...'
+; attributes...
+
+;%include "iprt/asmdefs.mac"
+;
+;BEGINCODE
+
+segment TEXT32 public CLASS=CODE align=16 use32 flat
+
+extern _DLL_InitTerm
+
+; Low-level DLL entry point - Forward to the C code.
+..start:
+ jmp _DLL_InitTerm
+
+
+; emxomfld may generate references to this for weak symbols. It is usually
+; found in in libend.lib.
+ABSOLUTE 0
+global WEAK$ZERO
+WEAK$ZERO:
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.cpp b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.cpp
new file mode 100644
index 00000000..b5dd9eda
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.cpp
@@ -0,0 +1,198 @@
+/* $Id: VBoxHlp.cpp $ */
+/** @file
+ * VBox Qt GUI - Implementation of OS/2-specific helpers that require to reside in a DLL
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define OS2EMX_PLAIN_CHAR
+
+#define INCL_BASE
+#define INCL_PM
+#define INCL_DOSINFOSEG
+#define INCL_DOSDEVIOCTL
+#include <os2.h>
+
+#include "VBoxHlp.h"
+
+/**
+ * Undocumented PM hook that is called before the pressed key is checked
+ * against the global accelerator table.
+ *
+ * Taken from the xWorkplace source code where it appears to come from the
+ * ProgramCommander/2 source code. Thanks to Ulrich Moeller and Roman Stangl.
+ */
+#define HK_PREACCEL 17
+
+/* NOTE: all global data is per-process (DATA32 is multiple, nonshared). */
+
+/* Module handle of this DLL */
+static HMODULE gThisModule = NULLHANDLE;
+
+static PGINFOSEG gGIS = NULL;
+static PLINFOSEG gLIS = NULL;
+
+/* Parameters for the keyboard hook (VBoxHlpInstallKbdHook()) */
+HAB gKbdHookHab = NULLHANDLE;
+HWND gKbdHookHwnd = NULLHANDLE;
+ULONG gKbdHookMsg = 0;
+
+/**
+ * Message Input hook used to monitor the system message queue.
+ *
+ * @param aHab Anchor handle.
+ * @param aHwnd Pointer to the QMSG structure.
+ * @param aFS Flags from WinPeekMsg(), either PM_NOREMOVE or
+ * PM_REMOVE.
+ *
+ * @return @c TRUE to steal the given message or @c FALSE to pass it to the
+ * rest of the hook chain.
+ */
+static
+BOOL EXPENTRY vboxInputHook (HAB aHab, PQMSG aMsg, ULONG aFS)
+{
+ if (aMsg->msg == WM_CHAR)
+ {
+ /* For foreign processes that didn't call VBoxHlpInstallKbdHook(),
+ * gKbdHookHwnd remains NULL. If it's the case while in this input
+ * hook, it means that the given foreign process is in foreground
+ * now. Since forwarding should work only for processes that
+ * called VBoxHlpInstallKbdHook(), we ignore the message. */
+ if (gKbdHookHwnd != NULLHANDLE)
+ {
+ MRESULT reply =
+ WinSendMsg (gKbdHookHwnd, gKbdHookMsg, aMsg->mp1, aMsg->mp2);
+ return (BOOL) reply;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * Installs a hook that will intercept all keyboard input (WM_CHAR) messages
+ * and forward them to the given window handle using the given message
+ * identifier. Messages are intercepted only when the given top-level window
+ * is the active desktop window (i.e. a window receiving keyboard input).
+ *
+ * When the WM_CHAR message is intercepted, it is forwarded as is (including
+ * all parameters) except that the message ID is changed to the given message
+ * ID. The result of the WinSendMsg() call is then converted to BOOL and if
+ * it results to TRUE then the message is considered to be processed,
+ * otherwise it is passed back to the system for normal processing.
+ *
+ * If the hook is already installed for the same or another window, this
+ * method will return @c false.
+ *
+ * @note This function is not thread-safe and must be called only on the main
+ * thread once per process.
+ *
+ * @param aHab Window anchor block.
+ * @param aHwnd Top-level window handle to forward WM_CHAR messages to.
+ * @param aMsg Message ID to use when forwarding.
+ *
+ * @return @c true on success and @c false otherwise. */
+VBOXHLPDECL(bool) VBoxHlpInstallKbdHook (HAB aHab, HWND aHwnd,
+ unsigned long aMsg)
+{
+ if (gKbdHookHwnd != NULLHANDLE ||
+ aHwnd == NULLHANDLE)
+ return false;
+
+ BOOL ok = WinSetHook (aHab, NULLHANDLE, HK_PREACCEL,
+ (PFN) vboxInputHook, gThisModule);
+
+ if (ok)
+ {
+ gKbdHookHab = aHab;
+ gKbdHookHwnd = aHwnd;
+ gKbdHookMsg = aMsg;
+ }
+
+ return (bool) ok;
+}
+
+/**
+ * Uninstalls the keyboard hook installed by VBoxHlpInstallKbdHook().
+ * All arguments must match arguments passed to VBoxHlpInstallKbdHook(),
+ * otherwise this method will do nothing and return @c false.
+ *
+ * @return @c true on success and @c false otherwise.
+ */
+VBOXHLPDECL(bool) VBoxHlpUninstallKbdHook (HAB aHab, HWND aHwnd,
+ unsigned long aMsg)
+{
+ if (gKbdHookHab != aHab ||
+ gKbdHookHwnd != aHwnd ||
+ gKbdHookMsg != aMsg)
+ return false;
+
+ BOOL ok = WinReleaseHook (aHab, NULLHANDLE, HK_PREACCEL,
+ (PFN) vboxInputHook, gThisModule);
+
+ if (ok)
+ {
+ gKbdHookHab = NULLHANDLE;
+ gKbdHookHwnd = NULLHANDLE;
+ gKbdHookMsg = 0;
+ }
+
+ return (bool) ok;
+}
+
+/**
+ * DLL entry point.
+ *
+ * @param aHandle DLL module handle.
+ * @param aFlag 0 on initialization or 1 on termination.
+ *
+ * @return Non-zero for success or 0 for failure.
+ */
+ULONG _System _DLL_InitTerm (HMODULE aHandle, ULONG aFlag)
+{
+ bool ok = true;
+
+ if (aFlag == 0)
+ {
+ /* DLL initialization */
+
+ gThisModule = aHandle;
+
+ gGIS = GETGINFOSEG();
+ gLIS = GETLINFOSEG();
+ }
+ else
+ {
+ /* DLL termination */
+
+ /* Make sure we release the hook if the user forgets to do so. */
+ if (gKbdHookHwnd != NULLHANDLE)
+ WinReleaseHook (gKbdHookHab, NULLHANDLE, HK_PREACCEL,
+ (PFN) vboxInputHook, gThisModule);
+
+ gThisModule = NULLHANDLE;
+ }
+
+ return (unsigned long) ok;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.h b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.h
new file mode 100644
index 00000000..0df97704
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/os2/VBoxHlp.h
@@ -0,0 +1,49 @@
+/* $Id: VBoxHlp.h $ */
+/** @file
+ * VBox Qt GUI - Declaration of OS/2-specific helpers that require to reside in a DLL.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_os2_VBoxHlp_h
+#define FEQT_INCLUDED_SRC_platform_os2_VBoxHlp_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+
+#ifdef IN_VBOXHLP
+# define VBOXHLPDECL(type) DECLEXPORT(type) RTCALL
+#else
+# define VBOXHLPDECL(type) DECLIMPORT(type) RTCALL
+#endif
+
+VBOXHLPDECL(bool) VBoxHlpInstallKbdHook (HAB aHab, HWND aHwnd,
+ unsigned long aMsg);
+
+VBOXHLPDECL(bool) VBoxHlpUninstallKbdHook (HAB aHab, HWND aHwnd,
+ unsigned long aMsg);
+
+#endif /* !FEQT_INCLUDED_SRC_platform_os2_VBoxHlp_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/platform/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/UIDesktopServices_win.cpp b/src/VBox/Frontends/VirtualBox/src/platform/win/UIDesktopServices_win.cpp
new file mode 100644
index 00000000..df4deef6
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/UIDesktopServices_win.cpp
@@ -0,0 +1,88 @@
+/* $Id: UIDesktopServices_win.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to Windows..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UIDesktopServices.h"
+
+/* Qt includes */
+#include <QDir>
+#include <QCoreApplication>
+#include <QUuid>
+
+/* System includes */
+#include <iprt/win/shlobj.h>
+
+
+bool UIDesktopServices::createMachineShortcut(const QString & /* strSrcFile */, const QString &strDstPath, const QString &strName, const QUuid &uUuid)
+{
+ IShellLink *pShl = NULL;
+ IPersistFile *pPPF = NULL;
+ const QString strVBox = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/" + VBOX_GUI_VMRUNNER_IMAGE);
+ QFileInfo fi(strVBox);
+ QString strVBoxDir = QDir::toNativeSeparators(fi.absolutePath());
+ HRESULT rc = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)(&pShl));
+ if (FAILED(rc))
+ return false;
+ do
+ {
+ rc = pShl->SetPath(strVBox.utf16());
+ if (FAILED(rc))
+ break;
+ rc = pShl->SetWorkingDirectory(strVBoxDir.utf16());
+ if (FAILED(rc))
+ break;
+ QString strArgs = QString("--comment \"%1\" --startvm \"%2\"").arg(strName).arg(uUuid.toString());
+ rc = pShl->SetArguments(strArgs.utf16());
+ if (FAILED(rc))
+ break;
+ QString strDesc = QString("Starts the VirtualBox machine %1").arg(strName);
+ rc = pShl->SetDescription(strDesc.utf16());
+ if (FAILED(rc))
+ break;
+ rc = pShl->QueryInterface(IID_IPersistFile, (void**)&pPPF);
+ if (FAILED(rc))
+ break;
+ QString strLink = QString("%1\\%2.lnk").arg(strDstPath).arg(strName);
+ rc = pPPF->Save(strLink.utf16(), TRUE);
+ } while(0);
+ if (pPPF)
+ pPPF->Release();
+ if (pShl)
+ pShl->Release();
+ return SUCCEEDED(rc);
+}
+
+bool UIDesktopServices::openInFileManager(const QString &strFile)
+{
+ QFileInfo fi(strFile);
+ QString strTmp = QDir::toNativeSeparators(fi.absolutePath());
+
+ intptr_t rc = (intptr_t)ShellExecute(NULL, L"explore", strTmp.utf16(), NULL, NULL, SW_SHOWNORMAL);
+
+ return rc > 32 ? true : false;
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.cpp b/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.cpp
new file mode 100644
index 00000000..7bd7c440
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.cpp
@@ -0,0 +1,128 @@
+/* $Id: VBoxUtils-win.cpp $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling Windows specific tasks.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* GUI includes: */
+#include "VBoxUtils-win.h"
+
+
+/** Namespace for native window sub-system functions. */
+namespace NativeWindowSubsystem
+{
+ /** Enumerates visible always-on-top (top-most) windows. */
+ BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) RT_NOTHROW_PROTO;
+ /** Contains visible top-most-window rectangles. */
+ QList<QRect> topMostRects;
+}
+
+BOOL CALLBACK NativeWindowSubsystem::EnumWindowsProc(HWND hWnd, LPARAM) RT_NOTHROW_DEF
+{
+ /* Ignore NULL HWNDs: */
+ if (!hWnd)
+ return TRUE;
+
+ /* Ignore hidden windows: */
+ if (!IsWindowVisible(hWnd))
+ return TRUE;
+
+ /* Get window style: */
+ LONG uStyle = GetWindowLong(hWnd, GWL_STYLE);
+ /* Ignore minimized windows: */
+ if (uStyle & WS_MINIMIZE)
+ return TRUE;
+
+ /* Get extended window style: */
+ LONG uExtendedStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
+ /* Ignore non-top-most windows: */
+ if (!(uExtendedStyle & WS_EX_TOPMOST))
+ return TRUE;
+
+ /* Get that window rectangle: */
+ RECT rect;
+ GetWindowRect(hWnd, &rect);
+ topMostRects << QRect(QPoint(rect.left, rect.top), QPoint(rect.right - 1, rect.bottom - 1));
+
+ /* Proceed to the next window: */
+ return TRUE;
+}
+
+const QRegion NativeWindowSubsystem::areaCoveredByTopMostWindows()
+{
+ /* Prepare the top-most region: */
+ QRegion topMostRegion;
+ /* Initialize the list of the top-most rectangles: */
+ topMostRects.clear();
+ /* Populate the list of top-most rectangles: */
+ EnumWindows((WNDENUMPROC)EnumWindowsProc, 0);
+ /* Update the top-most region with top-most rectangles: */
+ for (int iRectIndex = 0; iRectIndex < topMostRects.size(); ++iRectIndex)
+ topMostRegion += topMostRects[iRectIndex];
+ /* Return top-most region: */
+ return topMostRegion;
+}
+
+const void NativeWindowSubsystem::setScreenSaverActive(BOOL fDisableScreenSaver)
+{
+ BOOL fIsActive;
+ SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fIsActive, 0);
+ if (fIsActive == !fDisableScreenSaver)
+ return;
+ //printf("before %d\n", fIsActive);
+
+ SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, !fDisableScreenSaver, NULL, 0);
+
+ SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fIsActive, 0);
+ /*if (fIsActive == !fDisableScreenSaver)
+ printf("success %d %d\n", fIsActive, fDisableScreenSaver);
+*/
+}
+
+BOOL NativeWindowSubsystem::ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
+{
+ BOOL fResult = FALSE;
+ typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONCREATE)(HWND hWnd, LPCWSTR pwszReason);
+
+ PFNSHUTDOWNBLOCKREASONCREATE pfn = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(
+ GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
+ _ASSERTE(pfn);
+ if (pfn)
+ fResult = pfn(hWnd, pwszReason);
+ return fResult;
+}
+
+bool NativeWindowSubsystem::WinActivateWindow(WId wId, bool)
+{
+ bool fResult = true;
+ HWND handle = (HWND)wId;
+
+ if (IsIconic(handle))
+ fResult &= !!ShowWindow(handle, SW_RESTORE);
+ else if (!IsWindowVisible(handle))
+ fResult &= !!ShowWindow(handle, SW_SHOW);
+
+ fResult &= !!SetForegroundWindow(handle);
+ return fResult;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.h b/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.h
new file mode 100644
index 00000000..6153684e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/VBoxUtils-win.h
@@ -0,0 +1,57 @@
+/* $Id: VBoxUtils-win.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling Windows specific tasks.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_win_VBoxUtils_win_h
+#define FEQT_INCLUDED_SRC_platform_win_VBoxUtils_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QRegion>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* External includes: */
+#include <iprt/win/windows.h>
+
+/* Namespace for native window sub-system functions: */
+namespace NativeWindowSubsystem
+{
+ /* Returns area covered by visible always-on-top (top-most) windows: */
+ SHARED_LIBRARY_STUFF const QRegion areaCoveredByTopMostWindows();
+ SHARED_LIBRARY_STUFF const void setScreenSaverActive(BOOL fDisableScreenSaver);
+
+ /** Wraps WinAPI ShutdownBlockReasonCreate function. */
+ SHARED_LIBRARY_STUFF BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason);
+
+ /** Activates window with certain @a wId, @a fSwitchDesktop if requested. */
+ bool WinActivateWindow(WId wId, bool fSwitchDesktop);
+}
+
+#endif /* !FEQT_INCLUDED_SRC_platform_win_VBoxUtils_win_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/VirtualBox.VisualElementsManifest.xml b/src/VBox/Frontends/VirtualBox/src/platform/win/VirtualBox.VisualElementsManifest.xml
new file mode 100644
index 00000000..2a511ebf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/VirtualBox.VisualElementsManifest.xml
@@ -0,0 +1,9 @@
+<Application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <VisualElements
+ BackgroundColor="#447aaa"
+ ShowNameOnSquare150x150Logo="off"
+ ForegroundText="light"
+ Square150x150Logo="VirtualBox_150px.png"
+ Square70x70Logo="VirtualBox_70px.png"
+ />
+</Application>
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp b/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp
new file mode 100644
index 00000000..a50c1f79
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp
@@ -0,0 +1,310 @@
+/* $Id: WinKeyboard.cpp $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Windows Keyboard specific tasks.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Defines: */
+#define LOG_GROUP LOG_GROUP_GUI
+
+/* GUI includes: */
+#include "WinKeyboard.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+/* External includes: */
+#include <stdio.h>
+
+
+/* Beautification of log output */
+#define VBOX_BOOL_TO_STR_STATE(x) (x) ? "ON" : "OFF"
+#define VBOX_CONTROL_TO_STR_NAME(x) ((x == VK_CAPITAL) ? "CAPS" : (x == VK_SCROLL ? "SCROLL" : ((x == VK_NUMLOCK) ? "NUM" : "UNKNOWN")))
+
+/* A structure that contains internal control state representation */
+typedef struct VBoxModifiersState_t {
+ bool fNumLockOn; /** A state of NUM LOCK */
+ bool fCapsLockOn; /** A state of CAPS LOCK */
+ bool fScrollLockOn; /** A state of SCROLL LOCK */
+} VBoxModifiersState_t;
+
+/**
+ * Get current state of a keyboard modifier.
+ *
+ * @param idModifier Modifier to examine (VK_CAPITAL, VK_SCROLL or VK_NUMLOCK)
+ *
+ * @returns modifier state or asserts if wrong modifier is specified.
+ */
+static bool winGetModifierState(int idModifier)
+{
+ AssertReturn((idModifier == VK_CAPITAL) || (idModifier == VK_SCROLL) || (idModifier == VK_NUMLOCK), false);
+ return (GetKeyState(idModifier) & 0x0001) != 0;
+}
+
+/**
+ * Set current state of a keyboard modifier.
+ *
+ * @param idModifier Modifier to set (VK_CAPITAL, VK_SCROLL or VK_NUMLOCK)
+ * @param fState State to set
+ */
+static void winSetModifierState(int idModifier, bool fState)
+{
+ AssertReturnVoid((idModifier == VK_CAPITAL) || (idModifier == VK_SCROLL) || (idModifier == VK_NUMLOCK));
+
+ /* If the modifier is already in desired state, just do nothing. Otherwise, toggle it. */
+ if (winGetModifierState(idModifier) != fState)
+ {
+ /* Simulate KeyUp+KeyDown keystroke */
+ keybd_event(idModifier, 0, KEYEVENTF_EXTENDEDKEY, 0);
+ keybd_event(idModifier, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+
+ /* Process posted above keyboard events immediately: */
+ MSG msg;
+ while (::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
+ ::DispatchMessage(&msg);
+
+ LogRel2(("HID LEDs sync: setting %s state to %s (0x%X).\n",
+ VBOX_CONTROL_TO_STR_NAME(idModifier), VBOX_BOOL_TO_STR_STATE(fState), MapVirtualKey(idModifier, MAPVK_VK_TO_VSC)));
+ }
+ else
+ {
+ LogRel2(("HID LEDs sync: setting %s state: skipped: state is already %s (0x%X).\n",
+ VBOX_CONTROL_TO_STR_NAME(idModifier), VBOX_BOOL_TO_STR_STATE(fState), MapVirtualKey(idModifier, MAPVK_VK_TO_VSC)));
+ }
+}
+
+/** Set all HID LEDs at once. */
+static bool winSetHidLeds(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+ winSetModifierState(VK_NUMLOCK, fNumLockOn);
+ winSetModifierState(VK_CAPITAL, fCapsLockOn);
+ winSetModifierState(VK_SCROLL, fScrollLockOn);
+ return true;
+}
+
+/** Check if specified LED states correspond to the system modifier states. */
+bool winHidLedsInSync(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+ if (winGetModifierState(VK_NUMLOCK) != fNumLockOn)
+ return false;
+
+ if (winGetModifierState(VK_CAPITAL) != fCapsLockOn)
+ return false;
+
+ if (winGetModifierState(VK_SCROLL) != fScrollLockOn)
+ return false;
+
+ return true;
+}
+
+/**
+ * Allocate memory and store modifier states there.
+ *
+ * @returns allocated memory witch contains modifier states or NULL.
+ */
+void * WinHidDevicesKeepLedsState(void)
+{
+ VBoxModifiersState_t *pState;
+
+ pState = (VBoxModifiersState_t *)malloc(sizeof(VBoxModifiersState_t));
+ if (pState)
+ {
+ pState->fNumLockOn = winGetModifierState(VK_NUMLOCK);
+ pState->fCapsLockOn = winGetModifierState(VK_CAPITAL);
+ pState->fScrollLockOn = winGetModifierState(VK_SCROLL);
+
+ LogRel2(("HID LEDs sync: host state captured: NUM(%s) CAPS(%s) SCROLL(%s)\n",
+ VBOX_BOOL_TO_STR_STATE(pState->fNumLockOn),
+ VBOX_BOOL_TO_STR_STATE(pState->fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(pState->fScrollLockOn)));
+
+ return (void *)pState;
+ }
+
+ LogRel2(("HID LEDs sync: unable to allocate memory for HID LEDs synchronization data. LEDs sync will be disabled.\n"));
+
+ return NULL;
+}
+
+/**
+ * Restore host modifier states and free memory.
+ */
+void WinHidDevicesApplyAndReleaseLedsState(void *pData)
+{
+ VBoxModifiersState_t *pState = (VBoxModifiersState_t *)pData;
+
+ if (pState)
+ {
+ LogRel2(("HID LEDs sync: attempt to restore host state: NUM(%s) CAPS(%s) SCROLL(%s)\n",
+ VBOX_BOOL_TO_STR_STATE(pState->fNumLockOn),
+ VBOX_BOOL_TO_STR_STATE(pState->fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(pState->fScrollLockOn)));
+
+ if (winSetHidLeds(pState->fNumLockOn, pState->fCapsLockOn, pState->fScrollLockOn))
+ LogRel2(("HID LEDs sync: host state restored\n"));
+ else
+ LogRel2(("HID LEDs sync: host state restore failed\n"));
+
+ free(pState);
+ }
+}
+
+/**
+ * Broadcast HID modifier states.
+ *
+ * @param fNumLockOn NUM LOCK state
+ * @param fCapsLockOn CAPS LOCK state
+ * @param fScrollLockOn SCROLL LOCK state
+ */
+void WinHidDevicesBroadcastLeds(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
+{
+ LogRel2(("HID LEDs sync: start broadcast guest modifier states: NUM(%s) CAPS(%s) SCROLL(%s)\n",
+ VBOX_BOOL_TO_STR_STATE(fNumLockOn),
+ VBOX_BOOL_TO_STR_STATE(fCapsLockOn),
+ VBOX_BOOL_TO_STR_STATE(fScrollLockOn)));
+
+ if (winSetHidLeds(fNumLockOn, fCapsLockOn, fScrollLockOn))
+ LogRel2(("HID LEDs sync: broadcast completed\n"));
+ else
+ LogRel2(("HID LEDs sync: broadcast failed\n"));
+}
+
+/** @brief doesCurrentLayoutHaveAltGr
+ *
+ * @return true if this keyboard layout has an AltGr key, false otherwise
+ * Check to see whether the current keyboard layout actually has an AltGr key
+ * by checking whether any of the keys which might do produce a symbol when
+ * AltGr (Control + Alt) is depressed. Generally this loop will exit pretty
+ * early (it exits on the first iteration for my German layout). If there is
+ * no AltGr key in the layout then it will run right through, but that should
+ * hopefully not happen very often.
+ *
+ * In theory we could do this once and cache the result, but that involves
+ * tracking layout switches to invalidate the cache, and I don't think that the
+ * added complexity is worth the price. */
+static bool doesCurrentLayoutHaveAltGr()
+{
+ /* Keyboard state array with VK_CONTROL and VK_MENU depressed. */
+ static const BYTE s_auKeyStates[256] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x80 };
+ WORD ach;
+ unsigned i;
+
+ for (i = '0'; i <= VK_OEM_102; ++i)
+ {
+ if (ToAscii(i, 0, s_auKeyStates, &ach, 0))
+ break;
+ /* Skip ranges of virtual keys which are undefined or not relevant. */
+ if (i == '9')
+ i = 'A' - 1;
+ if (i == 'Z')
+ i = VK_OEM_1 - 1;
+ if (i == VK_OEM_3)
+ i = VK_OEM_4 - 1;
+ if (i == VK_OEM_8)
+ i = VK_OEM_102 - 1;
+ }
+ if (i > VK_OEM_102)
+ return false;
+ return true;
+}
+
+void WinAltGrMonitor::updateStateFromKeyEvent(unsigned iDownScanCode,
+ bool fKeyDown, bool fExtendedKey)
+{
+ LONG messageTime = GetMessageTime();
+ /* We do not want the make/break: */
+ AssertRelease(~iDownScanCode & 0x80);
+ /* Depending on m_enmFakeControlDetectionState: */
+ switch (m_enmFakeControlDetectionState)
+ {
+ case NONE:
+ case FAKE_CONTROL_DOWN:
+ if ( iDownScanCode == 0x1D /* left control */
+ && fKeyDown
+ && !fExtendedKey)
+ m_enmFakeControlDetectionState = LAST_EVENT_WAS_LEFT_CONTROL_DOWN;
+ else
+ m_enmFakeControlDetectionState = NONE;
+ break;
+ case LAST_EVENT_WAS_LEFT_CONTROL_DOWN:
+ if ( iDownScanCode == 0x38 /* Alt */
+ && fKeyDown
+ && fExtendedKey
+ && m_timeOfLastKeyEvent == messageTime
+ && doesCurrentLayoutHaveAltGr())
+ {
+ m_enmFakeControlDetectionState = FAKE_CONTROL_DOWN;
+ break;
+ }
+ else
+ m_enmFakeControlDetectionState = LEFT_CONTROL_DOWN;
+ RT_FALL_THRU();
+ case LEFT_CONTROL_DOWN:
+ if ( iDownScanCode == 0x1D /* left control */
+ && !fKeyDown
+ && !fExtendedKey)
+ m_enmFakeControlDetectionState = NONE;
+ break;
+ default:
+ AssertReleaseMsgFailed(("Unknown AltGr detection state.\n"));
+ }
+ m_timeOfLastKeyEvent = messageTime;
+}
+
+bool WinAltGrMonitor::isLeftControlReleaseNeeded() const
+{
+ return m_enmFakeControlDetectionState == FAKE_CONTROL_DOWN;
+}
+
+bool WinAltGrMonitor::isCurrentEventDefinitelyFake(unsigned iDownScanCode,
+ bool fKeyDown,
+ bool fExtendedKey) const
+{
+ if (iDownScanCode != 0x1d /* scan code: Control */ || fExtendedKey)
+ return false;
+
+ LONG messageTime = GetMessageTime();
+ MSG peekMsg;
+ if (!PeekMessage(&peekMsg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE))
+ return false;
+ if (messageTime != (LONG)peekMsg.time)
+ return false;
+
+ if ( fKeyDown
+ && peekMsg.message != WM_KEYDOWN
+ && peekMsg.message != WM_SYSKEYDOWN)
+ return false;
+ if ( !fKeyDown
+ && peekMsg.message != WM_KEYUP
+ && peekMsg.message != WM_SYSKEYUP)
+ return false;
+ if ( (RT_HIWORD(peekMsg.lParam) & 0xFF) != 0x38 /* scan code: Alt */
+ || !(RT_HIWORD(peekMsg.lParam) & KF_EXTENDED))
+ return false;
+ if (!doesCurrentLayoutHaveAltGr())
+ return false;
+ return true;
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.h b/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.h
new file mode 100644
index 00000000..c9eb5c6b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.h
@@ -0,0 +1,102 @@
+/* $Id: WinKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility functions for handling Windows Keyboard specific tasks.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_win_WinKeyboard_h
+#define FEQT_INCLUDED_SRC_platform_win_WinKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Other VBox includes: */
+#include <iprt/win/windows.h>
+
+SHARED_LIBRARY_STUFF void * WinHidDevicesKeepLedsState(void);
+SHARED_LIBRARY_STUFF void WinHidDevicesApplyAndReleaseLedsState(void *pData);
+SHARED_LIBRARY_STUFF void WinHidDevicesBroadcastLeds(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn);
+
+SHARED_LIBRARY_STUFF bool winHidLedsInSync(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn);
+
+/** Helper class to deal with Windows AltGr handling.
+ *
+ * Background: Windows sends AltGr key down and up events as two events: a
+ * left control event and a right alt one. Since the left control event does
+ * not correspond to actually pressing or releasing the left control key we
+ * would like to detect it and handle it. This class monitors all key down and
+ * up events and if it detects that a left control down event has been sendt
+ * although left control should be up it tells us to insert a left control up
+ * event into the event stream. While this does not let us filter out the
+ * unwanted event at source, it should still make guest system keyboard handling
+ * work correctly. */
+class SHARED_LIBRARY_STUFF WinAltGrMonitor
+{
+public:
+
+ /** Constructor. */
+ WinAltGrMonitor() : m_enmFakeControlDetectionState(NONE), m_timeOfLastKeyEvent(0) {}
+
+ /** All key events should be fed to this method.
+ * @param iDownScanCode the scan code stripped of the make/break bit
+ * @param fKeyDown is this a key down event?
+ * @param fExtended is this an extended scan code? */
+ void updateStateFromKeyEvent(unsigned iDownScanCode, bool fKeyDown, bool fExtended);
+
+ /** Do we need to insert a left control up into the stream? */
+ bool isLeftControlReleaseNeeded() const;
+
+ /** Can we tell for sure at this point that the current message is a fake
+ * control event? This method might fail to recognise a fake event, but
+ * should never incorrectly flag a non-fake one.
+ * @note We deliberately do not call this from the host combination editor
+ * in an attempt to ensure that the other code path also gets enough
+ * test coverage.
+ */
+ bool isCurrentEventDefinitelyFake(unsigned iDownScanCode,
+ bool fKeyDown,
+ bool fExtendedKey) const;
+
+private:
+
+ /** State detection for fake control events which we may have missed. */
+ enum
+ {
+ /** No interesting state. */
+ NONE,
+ /** The last keypress might be a fake control. */
+ LAST_EVENT_WAS_LEFT_CONTROL_DOWN,
+ /** Left control is down, so we ignore fake control events. */
+ LEFT_CONTROL_DOWN,
+ /** A fake control down event and no up was passed to the guest. */
+ FAKE_CONTROL_DOWN
+ } m_enmFakeControlDetectionState;
+ LONG m_timeOfLastKeyEvent;
+};
+
+#endif /* !FEQT_INCLUDED_SRC_platform_win_WinKeyboard_h */
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/Makefile.kup b/src/VBox/Frontends/VirtualBox/src/platform/x11/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/Makefile.kup
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/UIDesktopServices_x11.cpp b/src/VBox/Frontends/VirtualBox/src/platform/x11/UIDesktopServices_x11.cpp
new file mode 100644
index 00000000..ca1a6c1e
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/UIDesktopServices_x11.cpp
@@ -0,0 +1,86 @@
+/* $Id: UIDesktopServices_x11.cpp $ */
+/** @file
+ * VBox Qt GUI - Qt GUI - Utility Classes and Functions specific to X11..
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* VBox includes */
+#include "UIDesktopServices.h"
+
+/* Qt includes */
+#include <QCoreApplication>
+#include <QDesktopServices>
+#include <QDir>
+#include <QFile>
+#include <QTextStream>
+#include <QUrl>
+
+
+bool UIDesktopServices::createMachineShortcut(const QString & /* strSrcFile */, const QString &strDstPath, const QString &strName, const QUuid &uUuid)
+{
+ QFile link(strDstPath + QDir::separator() + strName + ".desktop");
+ if (link.open(QFile::WriteOnly | QFile::Truncate))
+ {
+ const QString strVBox = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/" + VBOX_GUI_VMRUNNER_IMAGE);
+ QTextStream out(&link);
+#ifndef VBOX_IS_QT6_OR_LATER /* QTextStream defaults to UTF-8 only since qt6 */
+ out.setCodec("UTF-8");
+#endif
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+# define QT_ENDL Qt::endl
+#else
+# define QT_ENDL endl
+#endif
+ /* Create a link which starts VirtualBox with the machine uuid. */
+ out << "[Desktop Entry]" << QT_ENDL
+ << "Encoding=UTF-8" << QT_ENDL
+ << "Version=1.0" << QT_ENDL
+ << "Name=" << strName << QT_ENDL
+ << "Comment=Starts the VirtualBox machine " << strName << QT_ENDL
+ << "Type=Application" << QT_ENDL
+ << "Exec=" << strVBox << " --comment \"" << strName << "\" --startvm \"" << uUuid.toString() << "\"" << QT_ENDL
+ << "Icon=virtualbox-vbox.png" << QT_ENDL;
+ /* This would be a real file link entry, but then we could also simply
+ * use a soft link (on most UNIX fs):
+ out << "[Desktop Entry]" << QT_ENDL
+ << "Encoding=UTF-8" << QT_ENDL
+ << "Version=1.0" << QT_ENDL
+ << "Name=" << strName << QT_ENDL
+ << "Type=Link" << QT_ENDL
+ << "Icon=virtualbox-vbox.png" << QT_ENDL
+ */
+ link.setPermissions(link.permissions() | QFile::ExeOwner);
+ /** @todo r=bird: check status here perhaps, might've run out of disk space or
+ * some such thing... */
+ return true;
+ }
+ return false;
+}
+
+bool UIDesktopServices::openInFileManager(const QString &strFile)
+{
+ QFileInfo fi(strFile);
+ return QDesktopServices::openUrl(QUrl("file://" + QDir::toNativeSeparators(fi.absolutePath()), QUrl::TolerantMode));
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.cpp b/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.cpp
new file mode 100644
index 00000000..e8b8db43
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.cpp
@@ -0,0 +1,679 @@
+/* $Id: VBoxUtils-x11.cpp $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling X11 specific tasks.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Qt includes: */
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusConnectionInterface>
+#include <QtDBus/QDBusInterface>
+#include <QtDBus/QDBusReply>
+#include <QtXml/QDomDocument>
+#include <QtXml/QDomElement>
+#endif
+#include <QWidget>
+#ifdef VBOX_IS_QT6_OR_LATER /** @todo qt6: ... */
+# include <QGuiApplication>
+#else
+# include <QX11Info>
+#endif
+
+/* GUI includes: */
+#include "VBoxUtils-x11.h"
+
+/* Other VBox includes: */
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+/* Other includes: */
+#undef BOOL /* Undefine the VBox/com/defs.h variant */
+#define BOOL X11BOOL /* Typedef'ed in Xmd.h via dpms.h, causing -Wpch-invalid to trigger. */
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/dpms.h>
+#undef BOOL /* Restore the VBox/com/defs.h variant */
+#define BOOL PRBool
+
+
+bool NativeWindowSubsystem::X11IsCompositingManagerRunning()
+{
+ /* For each screen it manage, compositing manager MUST acquire ownership
+ * of a selection named _NET_WM_CM_Sn, where n is the screen number. */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ Atom atom_property_name = XInternAtom(pDisplay, "_NET_WM_CM_S0", True);
+ return XGetSelectionOwner(pDisplay, atom_property_name);
+}
+
+X11WMType NativeWindowSubsystem::X11WindowManagerType()
+{
+ /* Ask if root-window supports check for WM name: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ Atom atom_property_name;
+ Atom atom_returned_type;
+ int iReturnedFormat;
+ unsigned long ulReturnedItemCount;
+ unsigned long ulDummy;
+ unsigned char *pcData = 0;
+ X11WMType wmType = X11WMType_Unknown;
+ atom_property_name = XInternAtom(pDisplay, "_NET_SUPPORTING_WM_CHECK", True);
+ if (XGetWindowProperty(pDisplay, NativeWindowSubsystem::X11GetAppRootWindow(), atom_property_name,
+ 0, 512, False, XA_WINDOW, &atom_returned_type,
+ &iReturnedFormat, &ulReturnedItemCount, &ulDummy, &pcData) == Success)
+ {
+ Window WMWindow = None;
+ if (atom_returned_type == XA_WINDOW && iReturnedFormat == 32)
+ WMWindow = *((Window*)pcData);
+ if (pcData)
+ XFree(pcData);
+ if (WMWindow != None)
+ {
+ /* Ask root-window for WM name: */
+ atom_property_name = XInternAtom(pDisplay, "_NET_WM_NAME", True);
+ Atom utf8Atom = XInternAtom(pDisplay, "UTF8_STRING", True);
+ if (XGetWindowProperty(pDisplay, WMWindow, atom_property_name,
+ 0, 512, False, utf8Atom, &atom_returned_type,
+ &iReturnedFormat, &ulReturnedItemCount, &ulDummy, &pcData) == Success)
+ {
+ /** @todo r=bird: 6 QString conversions cannot be very efficient. */
+ if (QString((const char*)pcData).contains("Compiz", Qt::CaseInsensitive))
+ wmType = X11WMType_Compiz;
+ else
+ if (QString((const char*)pcData).contains("GNOME Shell", Qt::CaseInsensitive))
+ wmType = X11WMType_GNOMEShell;
+ else
+ if (QString((const char*)pcData).contains("KWin", Qt::CaseInsensitive))
+ wmType = X11WMType_KWin;
+ else
+ if (QString((const char*)pcData).contains("Metacity", Qt::CaseInsensitive))
+ wmType = X11WMType_Metacity;
+ else
+ if (QString((const char*)pcData).contains("Mutter", Qt::CaseInsensitive))
+ wmType = X11WMType_Mutter;
+ else
+ if (QString((const char*)pcData).contains("Xfwm4", Qt::CaseInsensitive))
+ wmType = X11WMType_Xfwm4;
+ if (pcData)
+ XFree(pcData);
+ }
+ }
+ }
+ return wmType;
+}
+
+bool NativeWindowSubsystem::X11CheckExtension(const char *pExtensionName)
+{
+ /* Check extension: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ int major_opcode;
+ int first_event;
+ int first_error;
+ return XQueryExtension(pDisplay, pExtensionName, &major_opcode, &first_event, &first_error);
+}
+
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+bool X11CheckDBusConnection(const QDBusConnection &connection)
+{
+ if (!connection.isConnected())
+ {
+ const QDBusError lastError = connection.lastError();
+ if (lastError.isValid())
+ {
+ LogRel(("QDBus error. Could not connect to D-Bus server: %s: %s\n",
+ lastError.name().toUtf8().constData(),
+ lastError.message().toUtf8().constData()));
+ }
+ else
+ LogRel(("QDBus error. Could not connect to D-Bus server: Unable to load dbus libraries\n"));
+ return false;
+ }
+ return true;
+}
+
+QStringList X11FindDBusScreenSaverServices(const QDBusConnection &connection)
+{
+ QStringList serviceNames;
+
+ QDBusReply<QStringList> replyr = connection.interface()->registeredServiceNames();
+ if (!replyr.isValid())
+ {
+ const QDBusError replyError = replyr.error();
+ LogRel(("QDBus error. Could not query registered service names %s %s",
+ replyError.name().toUtf8().constData(), replyError.message().toUtf8().constData()));
+ return serviceNames;
+ }
+
+ for (int i = 0; i < replyr.value().size(); ++i)
+ {
+ const QString strServiceName = replyr.value()[i];
+ if (strServiceName.contains("screensaver", Qt::CaseInsensitive))
+ serviceNames << strServiceName;
+ }
+ if (serviceNames.isEmpty())
+ LogRel(("QDBus error. No screen saver service found among registered DBus services."));
+
+ return serviceNames;
+}
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+
+bool NativeWindowSubsystem::X11CheckDBusScreenSaverServices()
+{
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ if (!X11CheckDBusConnection(connection))
+ return false;
+
+ QDBusReply<QStringList> replyr = connection.interface()->registeredServiceNames();
+ if (!replyr.isValid())
+ {
+ const QDBusError replyError = replyr.error();
+ LogRel(("QDBus error. Could not query registered service names %s %s",
+ replyError.name().toUtf8().constData(), replyError.message().toUtf8().constData()));
+ return false;
+ }
+ for (int i = 0; i < replyr.value().size(); ++i)
+ {
+ const QString strServiceName = replyr.value()[i];
+ if (strServiceName.contains("screensaver", Qt::CaseInsensitive))
+ return true;
+ }
+ LogRel(("QDBus error. No screen saver service found among registered DBus services."));
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+ return false;
+}
+
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+void X11IntrospectInterfaceNode(const QDomElement &interface,
+ const QString &strServiceName,
+ QVector<X11ScreenSaverInhibitMethod*> &methods)
+{
+ QDomElement child = interface.firstChildElement();
+ while (!child.isNull())
+ {
+ if (child.tagName() == "method" && child.attribute("name") == "Inhibit")
+ {
+ X11ScreenSaverInhibitMethod *newMethod = new X11ScreenSaverInhibitMethod;
+ newMethod->m_iCookie = 0;
+ newMethod->m_strServiceName = strServiceName;
+ newMethod->m_strInterface = interface.attribute("name");
+ newMethod->m_strPath = "/";
+ newMethod->m_strPath.append(interface.attribute("name"));
+ newMethod->m_strPath.replace(".", "/");
+ methods.append(newMethod);
+ }
+ child = child.nextSiblingElement();
+ }
+}
+
+void X11IntrospectServices(const QDBusConnection &connection,
+ const QString &strService,
+ const QString &strPath,
+ QVector<X11ScreenSaverInhibitMethod*> &methods)
+{
+ QDBusMessage call = QDBusMessage::createMethodCall(strService, strPath.isEmpty() ? QLatin1String("/") : strPath,
+ QLatin1String("org.freedesktop.DBus.Introspectable"),
+ QLatin1String("Introspect"));
+ QDBusReply<QString> xmlReply = connection.call(call);
+
+ if (!xmlReply.isValid())
+ return;
+
+ QDomDocument doc;
+ doc.setContent(xmlReply);
+ QDomElement node = doc.documentElement();
+ QDomElement child = node.firstChildElement();
+ while (!child.isNull())
+ {
+ if (child.tagName() == QLatin1String("node"))
+ {
+ QString subPath = strPath + QLatin1Char('/') + child.attribute(QLatin1String("name"));
+ X11IntrospectServices(connection, strService, subPath, methods);
+ }
+ else if (child.tagName() == QLatin1String("interface"))
+ X11IntrospectInterfaceNode(child, strService, methods);
+ child = child.nextSiblingElement();
+ }
+}
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+
+QVector<X11ScreenSaverInhibitMethod*> NativeWindowSubsystem::X11FindDBusScrenSaverInhibitMethods()
+{
+ QVector<X11ScreenSaverInhibitMethod*> methods;
+
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ if (!X11CheckDBusConnection(connection))
+ return methods;
+
+ QStringList services = X11FindDBusScreenSaverServices(connection);
+ foreach(const QString &strServiceName, services)
+ X11IntrospectServices(connection, strServiceName, "", methods);
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+
+ return methods;
+}
+
+void NativeWindowSubsystem::X11InhibitUninhibitScrenSaver(bool fInhibit, QVector<X11ScreenSaverInhibitMethod*> &inOutInhibitMethods)
+{
+#ifdef VBOX_WITH_SCREENSAVER_CONTROL
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ if (!X11CheckDBusConnection(connection))
+ return;
+ for (int i = 0; i < inOutInhibitMethods.size(); ++i)
+ {
+ QDBusInterface screenSaverInterface(inOutInhibitMethods[i]->m_strServiceName, inOutInhibitMethods[i]->m_strPath,
+ inOutInhibitMethods[i]->m_strInterface, connection);
+ if (!screenSaverInterface.isValid())
+ {
+ QDBusError error = screenSaverInterface.lastError();
+ LogRel(("QDBus error for service %s: %s. %s\n",
+ inOutInhibitMethods[i]->m_strServiceName.toUtf8().constData(),
+ error.name().toUtf8().constData(),
+ error.message().toUtf8().constData()));
+ continue;
+ }
+ QDBusReply<uint> reply;
+ if (fInhibit)
+ {
+ reply = screenSaverInterface.call("Inhibit", "Oracle VirtualBox", "ScreenSaverInhibit");
+ if (reply.isValid())
+ inOutInhibitMethods[i]->m_iCookie = reply.value();
+ }
+ else
+ {
+ reply = screenSaverInterface.call("UnInhibit", inOutInhibitMethods[i]->m_iCookie);
+ }
+ if (!reply.isValid())
+ {
+ QDBusError error = reply.error();
+ LogRel(("QDBus inhibition call error for service %s: %s. %s\n",
+ inOutInhibitMethods[i]->m_strServiceName.toUtf8().constData(),
+ error.name().toUtf8().constData(),
+ error.message().toUtf8().constData()));
+ }
+ }
+#else
+ Q_UNUSED(fInhibit);
+ Q_UNUSED(inOutInhibitMethods);
+#endif /* VBOX_WITH_SCREENSAVER_CONTROL */
+}
+
+char *XXGetProperty(Display *pDpy, Window windowHandle, Atom propType, const char *pszPropName)
+{
+ Atom propNameAtom = XInternAtom(pDpy, pszPropName, True /* only_if_exists */);
+ if (propNameAtom == None)
+ return NULL;
+
+ Atom actTypeAtom = None;
+ int actFmt = 0;
+ unsigned long nItems = 0;
+ unsigned long nBytesAfter = 0;
+ unsigned char *propVal = NULL;
+ int rc = XGetWindowProperty(pDpy, windowHandle, propNameAtom,
+ 0, LONG_MAX, False /* delete */,
+ propType, &actTypeAtom, &actFmt,
+ &nItems, &nBytesAfter, &propVal);
+ if (rc != Success)
+ return NULL;
+
+ return reinterpret_cast<char*>(propVal);
+}
+
+bool XXSendClientMessage(Display *pDpy, Window windowHandle, const char *pszMsg,
+ unsigned long aData0 = 0, unsigned long aData1 = 0,
+ unsigned long aData2 = 0, unsigned long aData3 = 0,
+ unsigned long aData4 = 0)
+{
+ Atom msgAtom = XInternAtom(pDpy, pszMsg, True /* only_if_exists */);
+ if (msgAtom == None)
+ return false;
+
+ XEvent ev;
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.serial = 0;
+ ev.xclient.send_event = True;
+ ev.xclient.display = pDpy;
+ ev.xclient.window = windowHandle;
+ ev.xclient.message_type = msgAtom;
+
+ /* Always send as 32 bit for now: */
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = aData0;
+ ev.xclient.data.l[1] = aData1;
+ ev.xclient.data.l[2] = aData2;
+ ev.xclient.data.l[3] = aData3;
+ ev.xclient.data.l[4] = aData4;
+
+ return XSendEvent(pDpy, DefaultRootWindow(pDpy), False,
+ SubstructureRedirectMask, &ev) != 0;
+}
+
+bool NativeWindowSubsystem::X11ActivateWindow(WId wId, bool fSwitchDesktop)
+{
+ bool fResult = true;
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ if (fSwitchDesktop)
+ {
+ /* Try to find the desktop ID using the NetWM property: */
+ CARD32 *pDesktop = (CARD32*)XXGetProperty(pDisplay, wId, XA_CARDINAL, "_NET_WM_DESKTOP");
+ if (pDesktop == NULL)
+ // WORKAROUND:
+ // if the NetWM properly is not supported try to find
+ // the desktop ID using the GNOME WM property.
+ pDesktop = (CARD32*)XXGetProperty(pDisplay, wId, XA_CARDINAL, "_WIN_WORKSPACE");
+
+ if (pDesktop != NULL)
+ {
+ bool ok = XXSendClientMessage(pDisplay, DefaultRootWindow(pDisplay), "_NET_CURRENT_DESKTOP", *pDesktop);
+ if (!ok)
+ {
+ Log1WarningFunc(("Couldn't switch to pDesktop=%08X\n", pDesktop));
+ fResult = false;
+ }
+ XFree(pDesktop);
+ }
+ else
+ {
+ Log1WarningFunc(("Couldn't find a pDesktop ID for wId=%08X\n", wId));
+ fResult = false;
+ }
+ }
+
+ bool ok = XXSendClientMessage(pDisplay, wId, "_NET_ACTIVE_WINDOW");
+ fResult &= !!ok;
+
+ XRaiseWindow(pDisplay, wId);
+ return fResult;
+}
+
+bool NativeWindowSubsystem::X11SupportsFullScreenMonitorsProtocol()
+{
+ /* This method tests whether the current X11 window manager supports full-screen mode as we need it.
+ * Unfortunately the EWMH specification was not fully clear about whether we can expect to find
+ * all of these atoms on the _NET_SUPPORTED root window property, so we have to test with all
+ * interesting window managers. If this fails for a user when you think it should succeed
+ * they should try executing:
+ * xprop -root | egrep -w '_NET_WM_FULLSCREEN_MONITORS|_NET_WM_STATE|_NET_WM_STATE_FULLSCREEN'
+ * in an X11 terminal window.
+ * All three strings should be found under a property called "_NET_SUPPORTED(ATOM)". */
+
+ /* Using a global to get at the display does not feel right, but that is how it is done elsewhere in the code. */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ Atom atomSupported = XInternAtom(pDisplay, "_NET_SUPPORTED",
+ True /* only_if_exists */);
+ Atom atomWMFullScreenMonitors = XInternAtom(pDisplay,
+ "_NET_WM_FULLSCREEN_MONITORS",
+ True /* only_if_exists */);
+ Atom atomWMState = XInternAtom(pDisplay,
+ "_NET_WM_STATE",
+ True /* only_if_exists */);
+ Atom atomWMStateFullScreen = XInternAtom(pDisplay,
+ "_NET_WM_STATE_FULLSCREEN",
+ True /* only_if_exists */);
+ bool fFoundFullScreenMonitors = false;
+ bool fFoundState = false;
+ bool fFoundStateFullScreen = false;
+ Atom atomType;
+ int cFormat;
+ unsigned long cItems;
+ unsigned long cbLeft;
+ Atom *pAtomHints;
+ int rc;
+ unsigned i;
+
+ if ( atomSupported == None || atomWMFullScreenMonitors == None
+ || atomWMState == None || atomWMStateFullScreen == None)
+ return false;
+ /* Get atom value: */
+ rc = XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay),
+ atomSupported, 0, 0x7fffffff /*LONG_MAX*/,
+ False /* delete */, XA_ATOM, &atomType,
+ &cFormat, &cItems, &cbLeft,
+ (unsigned char **)&pAtomHints);
+ if (rc != Success)
+ return false;
+ if (pAtomHints == NULL)
+ return false;
+ if (atomType == XA_ATOM && cFormat == 32 && cbLeft == 0)
+ for (i = 0; i < cItems; ++i)
+ {
+ if (pAtomHints[i] == atomWMFullScreenMonitors)
+ fFoundFullScreenMonitors = true;
+ if (pAtomHints[i] == atomWMState)
+ fFoundState = true;
+ if (pAtomHints[i] == atomWMStateFullScreen)
+ fFoundStateFullScreen = true;
+ }
+ XFree(pAtomHints);
+ return fFoundFullScreenMonitors && fFoundState && fFoundStateFullScreen;
+}
+
+bool NativeWindowSubsystem::X11SetFullScreenMonitor(QWidget *pWidget, ulong uScreenId)
+{
+ return XXSendClientMessage(NativeWindowSubsystem::X11GetDisplay(),
+ pWidget->window()->winId(),
+ "_NET_WM_FULLSCREEN_MONITORS",
+ uScreenId, uScreenId, uScreenId, uScreenId,
+ 1 /* Source indication (1 = normal application) */);
+}
+
+QVector<Atom> flagsNetWmState(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ QVector<Atom> resultNetWmState;
+ Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
+
+ /* Get the size of the property data: */
+ Atom actual_type;
+ int iActualFormat;
+ ulong uPropertyLength;
+ ulong uBytesLeft;
+ uchar *pPropertyData = 0;
+ if (XGetWindowProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, 0, 0, False, XA_ATOM, &actual_type, &iActualFormat,
+ &uPropertyLength, &uBytesLeft, &pPropertyData) == Success &&
+ actual_type == XA_ATOM && iActualFormat == 32)
+ {
+ resultNetWmState.resize(uBytesLeft / 4);
+ XFree((char*)pPropertyData);
+ pPropertyData = 0;
+
+ /* Fetch all data: */
+ if (XGetWindowProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, 0, resultNetWmState.size(), False, XA_ATOM, &actual_type, &iActualFormat,
+ &uPropertyLength, &uBytesLeft, &pPropertyData) != Success)
+ resultNetWmState.clear();
+ else if (uPropertyLength != (ulong)resultNetWmState.size())
+ resultNetWmState.resize(uPropertyLength);
+
+ /* Put it into resultNetWmState: */
+ if (!resultNetWmState.isEmpty())
+ memcpy(resultNetWmState.data(), pPropertyData, resultNetWmState.size() * sizeof(Atom));
+ if (pPropertyData)
+ XFree((char*)pPropertyData);
+ }
+
+ /* Return result: */
+ return resultNetWmState;
+}
+
+#if 0 // unused for now?
+bool NativeWindowSubsystem::isFullScreenFlagSet(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ Atom net_wm_state_fullscreen = XInternAtom(pDisplay, "_NET_WM_STATE_FULLSCREEN", True /* only if exists */);
+
+ /* Check if flagsNetWmState(pWidget) contains full-screen flag: */
+ return flagsNetWmState(pWidget).contains(net_wm_state_fullscreen);
+}
+
+void NativeWindowSubsystem::setFullScreenFlag(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
+ Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
+ Atom net_wm_state_fullscreen = XInternAtom(pDisplay, "_NET_WM_STATE_FULLSCREEN", True /* only if exists */);
+
+ /* Append resultNetWmState with fullscreen flag if necessary: */
+ if (!resultNetWmState.contains(net_wm_state_fullscreen))
+ {
+ resultNetWmState.append(net_wm_state_fullscreen);
+ /* Apply property to widget again: */
+ XChangeProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
+ }
+}
+#endif // unused for now?
+
+void NativeWindowSubsystem::X11SetSkipTaskBarFlag(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
+ Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
+ Atom net_wm_state_skip_taskbar = XInternAtom(pDisplay, "_NET_WM_STATE_SKIP_TASKBAR", True /* only if exists */);
+
+ /* Append resultNetWmState with skip-taskbar flag if necessary: */
+ if (!resultNetWmState.contains(net_wm_state_skip_taskbar))
+ {
+ resultNetWmState.append(net_wm_state_skip_taskbar);
+ /* Apply property to widget again: */
+ XChangeProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
+ }
+}
+
+void NativeWindowSubsystem::X11SetSkipPagerFlag(QWidget *pWidget)
+{
+ /* Get display: */
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+
+ /* Prepare atoms: */
+ QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
+ Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
+ Atom net_wm_state_skip_pager = XInternAtom(pDisplay, "_NET_WM_STATE_SKIP_PAGER", True /* only if exists */);
+
+ /* Append resultNetWmState with skip-pager flag if necessary: */
+ if (!resultNetWmState.contains(net_wm_state_skip_pager))
+ {
+ resultNetWmState.append(net_wm_state_skip_pager);
+ /* Apply property to widget again: */
+ XChangeProperty(pDisplay, pWidget->window()->winId(),
+ net_wm_state, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
+ }
+}
+
+void NativeWindowSubsystem::X11SetWMClass(QWidget *pWidget, const QString &strNameString, const QString &strClassString)
+{
+ /* Make sure all arguments set: */
+ AssertReturnVoid(pWidget && !strNameString.isNull() && !strClassString.isNull());
+
+ /* Define QByteArray objects to make sure data is alive within the scope: */
+ QByteArray nameByteArray;
+ /* Check the existence of RESOURCE_NAME env. variable and override name string if necessary: */
+ const char resourceName[] = "RESOURCE_NAME";
+ if (qEnvironmentVariableIsSet(resourceName))
+ nameByteArray = qgetenv(resourceName);
+ else
+ nameByteArray = strNameString.toLatin1();
+ QByteArray classByteArray = strClassString.toLatin1();
+
+ AssertReturnVoid(nameByteArray.data() && classByteArray.data());
+
+ XClassHint windowClass;
+ windowClass.res_name = nameByteArray.data();
+ windowClass.res_class = classByteArray.data();
+ /* Set WM_CLASS of the window to passed name and class strings: */
+ XSetClassHint(NativeWindowSubsystem::X11GetDisplay(), pWidget->window()->winId(), &windowClass);
+}
+
+void NativeWindowSubsystem::X11SetXwaylandMayGrabKeyboardFlag(QWidget *pWidget)
+{
+ XXSendClientMessage(NativeWindowSubsystem::X11GetDisplay(), pWidget->window()->winId(),
+ "_XWAYLAND_MAY_GRAB_KEYBOARD", 1);
+}
+
+Display *NativeWindowSubsystem::X11GetDisplay()
+{
+#ifdef VBOX_IS_QT6_OR_LATER /** QX11Info is replaced with QNativeInterface::QX11Application since qt6 */
+ Display *pDisplay = 0;
+ if (qApp)
+ {
+ QNativeInterface::QX11Application *pX11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
+ if (pX11App)
+ pDisplay = pX11App->display();
+ }
+#else
+ Display *pDisplay = QX11Info::display();
+#endif
+ Assert(pDisplay);
+ return pDisplay;
+}
+
+xcb_connection_t *NativeWindowSubsystem::X11GetConnection()
+{
+#ifdef VBOX_IS_QT6_OR_LATER /** QX11Info is replaced with QNativeInterface::QX11Application since qt6 */
+ xcb_connection_t *pConnection = 0;
+ if (qApp)
+ {
+ QNativeInterface::QX11Application *pX11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
+ if (pX11App)
+ pConnection = pX11App->connection();
+ }
+#else
+ xcb_connection_t *pConnection = QX11Info::connection();
+#endif
+ Assert(pConnection);
+ return pConnection;
+}
+
+uint32_t NativeWindowSubsystem::X11GetAppRootWindow()
+{
+#ifdef VBOX_IS_QT6_OR_LATER /** QX11Info is replaced with QNativeInterface::QX11Application since qt6 */
+ Window idWindow = 0;
+ Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
+ if (pDisplay)
+ idWindow = DefaultRootWindow(pDisplay);
+ return idWindow;
+#else
+ return QX11Info::appRootWindow();
+#endif
+}
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.h b/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.h
new file mode 100644
index 00000000..12b4a172
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/VBoxUtils-x11.h
@@ -0,0 +1,140 @@
+/* $Id: VBoxUtils-x11.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of utility classes and functions for handling X11 specific tasks.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_x11_VBoxUtils_x11_h
+#define FEQT_INCLUDED_SRC_platform_x11_VBoxUtils_x11_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QString>
+#include <QVector>
+#include <QWindow>
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/** X11: Known Window Manager types. */
+enum X11WMType
+{
+ X11WMType_Unknown,
+ X11WMType_Compiz,
+ X11WMType_GNOMEShell,
+ X11WMType_KWin,
+ X11WMType_Metacity,
+ X11WMType_Mutter,
+ X11WMType_Xfwm4,
+};
+
+/** X11: Screen-saver inhibit methods. */
+struct SHARED_LIBRARY_STUFF X11ScreenSaverInhibitMethod
+{
+ QString m_strServiceName;
+ QString m_strInterface;
+ QString m_strPath;
+ uint m_iCookie;
+};
+
+/** X11: XCB size-hints. */
+typedef struct
+{
+ /** User specified flags */
+ uint32_t flags;
+ /** User-specified position */
+ int32_t x, y;
+ /** User-specified size */
+ int32_t width, height;
+ /** Program-specified minimum size */
+ int32_t min_width, min_height;
+ /** Program-specified maximum size */
+ int32_t max_width, max_height;
+ /** Program-specified resize increments */
+ int32_t width_inc, height_inc;
+ /** Program-specified minimum aspect ratios */
+ int32_t min_aspect_num, min_aspect_den;
+ /** Program-specified maximum aspect ratios */
+ int32_t max_aspect_num, max_aspect_den;
+ /** Program-specified base size */
+ int32_t base_width, base_height;
+ /** Program-specified window gravity */
+ uint32_t win_gravity;
+} xcb_size_hints_t;
+
+/* X11 structs to avoid dragging in unnecessary X headers: */
+struct xcb_connection_t;
+struct _XDisplay;
+
+/* Namespace for native window sub-system functions: */
+namespace NativeWindowSubsystem
+{
+ /** X11: Determines and returns whether the compositing manager is running. */
+ bool X11IsCompositingManagerRunning();
+ /** X11: Determines and returns current Window Manager type. */
+ X11WMType X11WindowManagerType();
+
+ /** X11: Returns true if XLib extension with name @p extensionName is avaible, false otherwise. */
+ bool X11CheckExtension(const char *extensionName);
+
+ /** X11: Returns whether there are any DBus services whose name contains the substring 'screensaver'. */
+ bool X11CheckDBusScreenSaverServices();
+ /** X11: Returns the list of Inhibit methods found by introspecting DBus services. */
+ SHARED_LIBRARY_STUFF QVector<X11ScreenSaverInhibitMethod*> X11FindDBusScrenSaverInhibitMethods();
+ /** X11: Disables/enables Screen Saver through QDBus. */
+ SHARED_LIBRARY_STUFF void X11InhibitUninhibitScrenSaver(bool fInhibit, QVector<X11ScreenSaverInhibitMethod*> &inOutInhibitMethods);
+
+ /** Activates window with certain @a wId, @a fSwitchDesktop if requested. */
+ bool X11ActivateWindow(WId wId, bool fSwitchDesktop);
+
+ /** X11: Test whether the current window manager supports full screen mode. */
+ SHARED_LIBRARY_STUFF bool X11SupportsFullScreenMonitorsProtocol();
+ /** X11: Performs mapping of the passed @a pWidget to host-screen with passed @a uScreenId. */
+ SHARED_LIBRARY_STUFF bool X11SetFullScreenMonitor(QWidget *pWidget, ulong uScreenId);
+
+ /** X11: Sets _NET_WM_STATE_SKIP_TASKBAR flag for passed @a pWidget. */
+ SHARED_LIBRARY_STUFF void X11SetSkipTaskBarFlag(QWidget *pWidget);
+ /** X11: Sets _NET_WM_STATE_SKIP_PAGER flag for passed @a pWidget. */
+ SHARED_LIBRARY_STUFF void X11SetSkipPagerFlag(QWidget *pWidget);
+
+ /** X11: Assigns WM_CLASS property for passed @a pWidget. */
+ SHARED_LIBRARY_STUFF void X11SetWMClass(QWidget *pWidget, const QString &strNameString, const QString &strClassString);
+
+ /** X11: Tell the WM we are well behaved wrt Xwayland keyboard-grabs. This will
+ * make the WM turn our grab into a Wayland shortcut inhibition request,
+ * so that e.g. alt+tab will get send to the VM instead of moving the
+ * focus away from the VM. */
+ SHARED_LIBRARY_STUFF void X11SetXwaylandMayGrabKeyboardFlag(QWidget *pWidget);
+
+ /** X11: Gets the X11 display pointer. */
+ SHARED_LIBRARY_STUFF struct _XDisplay *X11GetDisplay();
+ /** X11: Gets the X11 connection. */
+ SHARED_LIBRARY_STUFF struct xcb_connection_t *X11GetConnection();
+ /** X11: Gets the X11 root (desktop) window. */
+ SHARED_LIBRARY_STUFF uint32_t X11GetAppRootWindow();
+}
+
+#endif /* !FEQT_INCLUDED_SRC_platform_x11_VBoxUtils_x11_h */
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard-new.cpp b/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard-new.cpp
new file mode 100644
index 00000000..59e183bf
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard-new.cpp
@@ -0,0 +1,289 @@
+/* $Id: XKeyboard-new.cpp $ */
+/** @file
+ * VBox Qt GUI - Implementation of Linux-specific keyboard functions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* Define GUI log group.
+ * This define should go *before* VBox/log.h include: */
+#define LOG_GROUP LOG_GROUP_GUI
+
+/* Qt includes: */
+#include <QString>
+#include <QStringList>
+
+/* Other VBox includes: */
+#include <VBox/log.h>
+
+/* GUI includes: */
+#include "XKeyboard.h"
+
+/* Other VBox includes: */
+#include "VBox/VBoxKeyboard.h"
+
+/* External includes: */
+#include <X11/XKBlib.h>
+#include <X11/keysym.h>
+
+
+/* VBoxKeyboard uses the deprecated XKeycodeToKeysym(3) API, but uses it safely. */
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+static unsigned gfByLayoutOK = 1;
+static unsigned gfByTypeOK = 1;
+static unsigned gfByXkbOK = 1;
+
+/** Prints a key to the release log in the format needed for the Wine layout tables. */
+static void printKey(Display *pDisplay, int cKeys)
+{
+ bool fWasEscape = false;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ int iKeySym = XKeycodeToKeysym(pDisplay, cKeys, i);
+
+ int iValue = iKeySym & 0xff;
+ if ('\\' == iValue)
+ {
+ LogRel(("\\\\"));
+ }
+ else if ('"' == iValue)
+ {
+ LogRel(("\\\""));
+ }
+ else if ((iValue > 32) && (iValue < 127))
+ {
+ if ( fWasEscape
+ && ( ((iValue >= '0') && (iValue <= '9'))
+ || ((iValue >= 'A') && (iValue <= 'F'))
+ || ((iValue >= 'a') && (iValue <= 'f'))))
+ {
+ LogRel(("\"\""));
+ }
+ LogRel(("%c", (char)iValue));
+ }
+ else
+ {
+ LogRel(("\\x%x", iValue));
+ fWasEscape = true;
+ }
+ }
+}
+
+/** Dumps the keyboard layout to the release log. */
+static void dumpLayout(Display *pDisplay)
+{
+ LogRel(("Your keyboard layout does not appear to be fully supported by\n"
+ "VirtualBox. If you are experiencing keyboard problems this.\n"
+ "information may help us to resolve them.\n"
+ "(Note: please tell us if you are using a custom layout.)\n\n"
+ "The correct table for your layout is:\n"));
+ /* First, build up a table of scan-to-key code mappings */
+ unsigned scanToKeycode[512] = { 0 };
+ int iMinKey, iMaxKey;
+ XDisplayKeycodes(pDisplay, &iMinKey, &iMaxKey);
+ for (int i = iMinKey; i < iMaxKey; ++i)
+ scanToKeycode[X11DRV_KeyEvent(pDisplay, i)] = i;
+ LogRel(("\""));
+ printKey(pDisplay, scanToKeycode[0x29]); /* `~ */
+ for (int i = 2; i <= 0xd; ++i) /* 1! - =+ */
+ {
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[i]);
+ }
+ LogRel(("\",\n"));
+ LogRel(("\""));
+ printKey(pDisplay, scanToKeycode[0x10]); /* qQ */
+ for (int i = 0x11; i <= 0x1b; ++i) /* wW - ]} */
+ {
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[i]);
+ }
+ LogRel(("\",\n"));
+ LogRel(("\""));
+ printKey(pDisplay, scanToKeycode[0x1e]); /* aA */
+ for (int i = 0x1f; i <= 0x28; ++i) /* sS - '" */
+ {
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[i]);
+ }
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[0x2b]); /* \| */
+ LogRel(("\",\n"));
+ LogRel(("\""));
+ printKey(pDisplay, scanToKeycode[0x2c]); /* zZ */
+ for (int i = 0x2d; i <= 0x35; ++i) /* xX - /? */
+ {
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[i]);
+ }
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[0x56]); /* The 102nd key */
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[0x73]); /* The Brazilian key */
+ LogRel(("\",\""));
+ printKey(pDisplay, scanToKeycode[0x7d]); /* The Yen key */
+ LogRel(("\"\n\n"));
+}
+
+/** Dumps the keyboard type tables to the release log. */
+static void dumpType(Display *pDisplay)
+{
+ LogRel(("Your keyboard type does not appear to be known to VirtualBox. If\n"
+ "you are experiencing keyboard problems this information may help us\n"
+ "to resolve them. Please also provide information about what type\n"
+ "of keyboard you have and whether you are using a remote X server or\n"
+ "something similar.\n\n"
+ "The tables for your keyboard are:\n"));
+ for (unsigned i = 0; i < 256; ++i)
+ {
+ LogRel(("0x%x", X11DRV_KeyEvent(pDisplay, i)));
+ if (i < 255)
+ LogRel((", "));
+ if (15 == (i % 16))
+ LogRel(("\n"));
+ }
+ LogRel(("and\n"));
+ LogRel(("NULL, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x,\n0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ XKeysymToKeycode(pDisplay, XK_Control_L),
+ XKeysymToKeycode(pDisplay, XK_Shift_L),
+ XKeysymToKeycode(pDisplay, XK_Caps_Lock),
+ XKeysymToKeycode(pDisplay, XK_Tab),
+ XKeysymToKeycode(pDisplay, XK_Escape),
+ XKeysymToKeycode(pDisplay, XK_Return),
+ XKeysymToKeycode(pDisplay, XK_Up),
+ XKeysymToKeycode(pDisplay, XK_Down),
+ XKeysymToKeycode(pDisplay, XK_Left),
+ XKeysymToKeycode(pDisplay, XK_Right),
+ XKeysymToKeycode(pDisplay, XK_F1),
+ XKeysymToKeycode(pDisplay, XK_F2),
+ XKeysymToKeycode(pDisplay, XK_F3),
+ XKeysymToKeycode(pDisplay, XK_F4),
+ XKeysymToKeycode(pDisplay, XK_F5),
+ XKeysymToKeycode(pDisplay, XK_F6),
+ XKeysymToKeycode(pDisplay, XK_F7),
+ XKeysymToKeycode(pDisplay, XK_F8)));
+}
+
+/** Builds a table mapping the X server's scan codes to PC keyboard scan codes.
+ * The logic of the function is that while the X server may be using a different
+ * set of scan codes (if for example it is running on a non-PC machine), the
+ * keyboard layout should be similar to a PC layout. So we look at the symbols
+ * attached to each key on the X server, find the PC layout which is closest to
+ * it and remember the mappings. */
+bool initXKeyboard(Display *pDisplay, int (*remapScancodes)[2])
+{
+ X11DRV_InitKeyboard(pDisplay, &gfByLayoutOK, &gfByTypeOK, &gfByXkbOK, remapScancodes);
+
+ /* It will almost always work to some extent.. */
+ return true;
+}
+
+void initMappedX11Keyboard(Display *pDisplay, const QString &remapScancodes)
+{
+ /* Initialize X11 keyboard including the remapping specified in the
+ * global property GUI/RemapScancodes. This property is a string of
+ * comma-separated x=y pairs, where x is the X11 keycode and y is the
+ * keyboard scancode that is emitted when the key attached to the X11
+ * keycode is pressed. */
+
+ int (*scancodes)[2] = NULL;
+ int (*scancodesTail)[2] = NULL;
+
+ if (remapScancodes != QString())
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList tuples = remapScancodes.split(",", Qt::SkipEmptyParts);
+#else
+ QStringList tuples = remapScancodes.split(",", QString::SkipEmptyParts);
+#endif
+ scancodes = scancodesTail = new int [tuples.size()+1][2];
+ for (int i = 0; i < tuples.size(); ++i)
+ {
+ QStringList keyc2scan = tuples.at(i).split("=");
+ (*scancodesTail)[0] = keyc2scan.at(0).toUInt();
+ (*scancodesTail)[1] = keyc2scan.at(1).toUInt();
+ /* Do not advance on (ignore) identity mappings as this
+ * is the stop signal to initXKeyboard and friends: */
+ if ((*scancodesTail)[0] != (*scancodesTail)[1])
+ ++scancodesTail;
+ }
+ (*scancodesTail)[0] = (*scancodesTail)[1] = 0;
+ }
+
+ /* Initialize the X keyboard subsystem: */
+ initXKeyboard(pDisplay, scancodes);
+
+ if (scancodes)
+ delete scancodes;
+}
+
+unsigned handleXKeyEvent(Display *pDisplay, unsigned int iDetail)
+{
+ /* Call the WINE event handler: */
+ unsigned iKey = X11DRV_KeyEvent(pDisplay, iDetail);
+ LogRel3(("VBoxKeyboard: converting keycode %d to scancode %s0x%x\n",
+ iDetail, iKey > 0x100 ? "0xe0 " : "", iKey & 0xff));
+ return iKey;
+}
+
+void doXKeyboardLogging(Display *pDisplay)
+{
+ if (((1 == gfByTypeOK) || (1 == gfByXkbOK)) && (gfByLayoutOK != 1))
+ dumpLayout(pDisplay);
+ if (((1 == gfByLayoutOK) || (1 == gfByXkbOK)) && (gfByTypeOK != 1))
+ dumpType(pDisplay);
+ if ((gfByLayoutOK != 1) && (gfByTypeOK != 1) && (gfByXkbOK != 1))
+ {
+ LogRel(("Failed to recognize the keyboard mapping or to guess it based on\n"
+ "the keyboard layout. It is very likely that some keys will not\n"
+ "work correctly in the guest. If this is the case, please submit\n"
+ "a bug report, giving us information about your keyboard type,\n"
+ "its layout and other relevant information such as whether you\n"
+ "are using a remote X server or something similar. \n"));
+ unsigned *keyc2scan = X11DRV_getKeyc2scan();
+
+ LogRel(("The keycode-to-scancode table is: %d=%d",0,keyc2scan[0]));
+ for (int i = 1; i < 256; i++)
+ LogRel((",%d=%d",i,keyc2scan[i]));
+ LogRel(("\n"));
+ }
+ LogRel(("X Server details: vendor: %s, release: %d, protocol version: %d.%d, display string: %s\n",
+ ServerVendor(pDisplay), VendorRelease(pDisplay), ProtocolVersion(pDisplay),
+ ProtocolRevision(pDisplay), DisplayString(pDisplay)));
+ LogRel(("Using %s for keycode to scan code conversion\n",
+ gfByXkbOK ? "XKB"
+ : gfByTypeOK ? "known keycode mapping"
+ : "host keyboard layout detection"));
+}
+
+unsigned long wrapXkbKeycodeToKeysym(Display *pDisplay, unsigned char cCode,
+ unsigned int cGroup, unsigned int cIndex)
+{
+ KeySym cSym = XkbKeycodeToKeysym(pDisplay, cCode, cGroup, cIndex);
+ if (cSym != NoSymbol)
+ return cSym;
+ return XKeycodeToKeysym(pDisplay, cCode, cGroup * 2 + cIndex % 2);
+}
+
diff --git a/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard.h b/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard.h
new file mode 100644
index 00000000..9e394f17
--- /dev/null
+++ b/src/VBox/Frontends/VirtualBox/src/platform/x11/XKeyboard.h
@@ -0,0 +1,55 @@
+/* $Id: XKeyboard.h $ */
+/** @file
+ * VBox Qt GUI - Declarations of Linux-specific keyboard functions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef FEQT_INCLUDED_SRC_platform_x11_XKeyboard_h
+#define FEQT_INCLUDED_SRC_platform_x11_XKeyboard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QString;
+typedef struct _XDisplay Display;
+
+/** Initializes the X keyboard subsystem. */
+SHARED_LIBRARY_STUFF void initMappedX11Keyboard(Display *pDisplay, const QString &remapScancodes);
+
+/** Handles native XKey events. */
+SHARED_LIBRARY_STUFF unsigned handleXKeyEvent(Display *pDisplay, unsigned int iDetail);
+
+/** Handles log requests from initXKeyboard after release logging is started. */
+SHARED_LIBRARY_STUFF void doXKeyboardLogging(Display *pDisplay);
+
+/** Wraps for the XkbKeycodeToKeysym(3) API which falls back to the deprecated XKeycodeToKeysym(3) if it is unavailable. */
+SHARED_LIBRARY_STUFF unsigned long wrapXkbKeycodeToKeysym(Display *pDisplay, unsigned char cCode,
+ unsigned int cGroup, unsigned int cIndex);
+
+#endif /* !FEQT_INCLUDED_SRC_platform_x11_XKeyboard_h */
+