From b6a824d0a3b4fabd9c22fe4c954d50e8755fb509 Mon Sep 17 00:00:00 2001
From: Dmitry Shachnev <mitya57@gmail.com>
Date: Fri, 15 Jan 2016 17:04:01 +0300
Subject: [PATCH] Add native support for D-Bus global menu

The protocol was originally developed by Canonical, currently supported
by Unity and Plasma.

Adjust some tests to use the non-native menu bar when they require it.

[ChangeLog][XCB / X11] QMenuBar uses the unified D-Bus AppMenu menubar
when the desktop environment supports it.

Change-Id: Iea74b40522573bcc4f70168fe7fa2a49b4f3fc21
Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com>
---
 .../com.canonical.AppMenu.Registrar.xml            |  56 +++++++
 src/platformsupport/dbusmenu/dbusmenu.pri          |   4 +
 src/platformsupport/dbusmenu/qdbusmenubar.cpp      | 172 +++++++++++++++++++++
 src/platformsupport/dbusmenu/qdbusmenubar_p.h      |  91 +++++++++++
 .../dbusmenu/qdbusmenuregistrarproxy.cpp           |  64 ++++++++
 .../dbusmenu/qdbusmenuregistrarproxy_p.h           | 127 +++++++++++++++
 .../themes/genericunix/qgenericunixthemes.cpp      |  68 +++++++-
 .../themes/genericunix/qgenericunixthemes_p.h      |  12 ++
 .../other/qaccessibility/tst_qaccessibility.cpp    |   1 +
 .../auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp |   5 +-
 .../widgets/qmdisubwindow/tst_qmdisubwindow.cpp    |   5 +-
 tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp     |   1 +
 .../auto/widgets/widgets/qmenubar/tst_qmenubar.cpp |   5 +
 13 files changed, 607 insertions(+), 4 deletions(-)
 create mode 100644 src/3rdparty/dbus-ifaces/com.canonical.AppMenu.Registrar.xml
 create mode 100644 src/platformsupport/dbusmenu/qdbusmenubar.cpp
 create mode 100644 src/platformsupport/dbusmenu/qdbusmenubar_p.h
 create mode 100644 src/platformsupport/dbusmenu/qdbusmenuregistrarproxy.cpp
 create mode 100644 src/platformsupport/dbusmenu/qdbusmenuregistrarproxy_p.h

--- /dev/null
+++ b/src/3rdparty/dbus-ifaces/com.canonical.AppMenu.Registrar.xml
@@ -0,0 +1,56 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
+	<dox:d><![CDATA[
+	  @mainpage
+	 
+	  An interface to register menus that are associated with a window in an application.  The
+	  main interface is documented here: @ref com::canonical::AppMenu::Registrar.
+	    
+	  The actual menus are transported using the dbusmenu protocol which is available
+	  here: @ref com::canonical::dbusmenu.
+	]]></dox:d>
+	<interface name="com.canonical.AppMenu.Registrar" xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
+		<dox:d>
+		  An interface to register a menu from an application's window to be displayed in another
+		  window.  This manages that association between XWindow Window IDs and the dbus
+		  address and object that provides the menu using the dbusmenu dbus interface.
+		</dox:d>
+		<method name="RegisterWindow">
+			<dox:d><![CDATA[
+			  Associates a dbusmenu with a window
+	     
+			  /note this method assumes that the connection from the caller is the DBus connection
+			    to use for the object.  Applications that use multiple DBus connections will need to
+			    ensure this method is called with the same connection that implmenets the object.
+			]]></dox:d>
+			<arg name="windowId" type="u" direction="in">
+				<dox:d>The XWindow ID of the window</dox:d>
+			</arg>
+			<arg name="menuObjectPath" type="o" direction="in">
+				<dox:d>The object on the dbus interface implementing the dbusmenu interface</dox:d>
+			</arg>
+		</method>
+		<method name="UnregisterWindow">
+			<dox:d>
+			  A method to allow removing a window from the database.  Windows will also be removed
+			  when the client drops off DBus so this is not required.  It is polite though.  And
+			  important for testing.
+			</dox:d>
+			<arg name="windowId" type="u" direction="in">
+				<dox:d>The XWindow ID of the window</dox:d>
+			</arg>
+		</method>
+		<method name="GetMenuForWindow">
+			<dox:d>Gets the registered menu for a given window ID.</dox:d>
+			<arg name="windowId" type="u" direction="in">
+				<dox:d>The XWindow ID of the window to get</dox:d>
+			</arg>
+			<arg name="service" type="s" direction="out">
+				<dox:d>The address of the connection on DBus (e.g. :1.23 or org.example.service)</dox:d>
+			</arg>
+			<arg name="menuObjectPath" type="o" direction="out">
+				<dox:d>The path to the object which implements the com.canonical.dbusmenu interface.</dox:d>
+			</arg>
+		</method>
+	</interface>
+</node>
--- a/src/platformsupport/dbusmenu/dbusmenu.pri
+++ b/src/platformsupport/dbusmenu/dbusmenu.pri
@@ -6,10 +6,14 @@ HEADERS += \
     $$PWD/qdbusmenuadaptor_p.h \
     $$PWD/qdbusmenutypes_p.h \
     $$PWD/qdbusmenuconnection_p.h \
+    $$PWD/qdbusmenubar_p.h \
+    $$PWD/qdbusmenuregistrarproxy_p.h \
     $$PWD/qdbusplatformmenu_p.h \
 
 SOURCES += \
     $$PWD/qdbusmenuadaptor.cpp \
     $$PWD/qdbusmenutypes.cpp \
     $$PWD/qdbusmenuconnection.cpp \
+    $$PWD/qdbusmenubar.cpp \
+    $$PWD/qdbusmenuregistrarproxy.cpp \
     $$PWD/qdbusplatformmenu.cpp \
--- /dev/null
+++ b/src/platformsupport/dbusmenu/qdbusmenubar.cpp
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdbusmenubar_p.h"
+#include "qdbusmenuregistrarproxy_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/* note: do not change these to QStringLiteral;
+   we are unloaded before QtDBus is done using the strings.
+ */
+#define REGISTRAR_SERVICE QLatin1String("com.canonical.AppMenu.Registrar")
+#define REGISTRAR_PATH QLatin1String("/com/canonical/AppMenu/Registrar")
+
+QDBusMenuBar::QDBusMenuBar()
+    : QPlatformMenuBar()
+    , m_menu(new QDBusPlatformMenu())
+    , m_menuAdaptor(new QDBusMenuAdaptor(m_menu))
+    , m_windowId(0)
+{
+    QDBusMenuItem::registerDBusTypes();
+    connect(m_menu, &QDBusPlatformMenu::propertiesUpdated,
+            m_menuAdaptor, &QDBusMenuAdaptor::ItemsPropertiesUpdated);
+    connect(m_menu, &QDBusPlatformMenu::updated,
+            m_menuAdaptor, &QDBusMenuAdaptor::LayoutUpdated);
+}
+
+QDBusMenuBar::~QDBusMenuBar()
+{
+    unregisterMenuBar();
+    delete m_menuAdaptor;
+    delete m_menu;
+    qDeleteAll(m_menuItems);
+}
+
+QDBusPlatformMenuItem *QDBusMenuBar::menuItemForMenu(QPlatformMenu *menu)
+{
+    if (!menu)
+        return nullptr;
+    quintptr tag = menu->tag();
+    const auto it = m_menuItems.constFind(tag);
+    if (it != m_menuItems.cend()) {
+        return *it;
+    } else {
+        QDBusPlatformMenuItem *item = new QDBusPlatformMenuItem;
+        updateMenuItem(item, menu);
+        m_menuItems.insert(tag, item);
+        return item;
+    }
+}
+
+void QDBusMenuBar::updateMenuItem(QDBusPlatformMenuItem *item, QPlatformMenu *menu)
+{
+    const QDBusPlatformMenu *ourMenu = qobject_cast<const QDBusPlatformMenu *>(menu);
+    item->setText(ourMenu->text());
+    item->setIcon(ourMenu->icon());
+    item->setEnabled(ourMenu->isEnabled());
+    item->setVisible(ourMenu->isVisible());
+    item->setMenu(menu);
+}
+
+void QDBusMenuBar::insertMenu(QPlatformMenu *menu, QPlatformMenu *before)
+{
+    QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
+    QDBusPlatformMenuItem *beforeItem = menuItemForMenu(before);
+    m_menu->insertMenuItem(menuItem, beforeItem);
+    m_menu->emitUpdated();
+}
+
+void QDBusMenuBar::removeMenu(QPlatformMenu *menu)
+{
+    QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
+    m_menu->removeMenuItem(menuItem);
+    m_menu->emitUpdated();
+}
+
+void QDBusMenuBar::syncMenu(QPlatformMenu *menu)
+{
+    QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
+    updateMenuItem(menuItem, menu);
+}
+
+void QDBusMenuBar::handleReparent(QWindow *newParentWindow)
+{
+    if (newParentWindow && newParentWindow->winId() != m_windowId) {
+        unregisterMenuBar();
+        m_windowId = newParentWindow->winId();
+        registerMenuBar();
+    }
+}
+
+QPlatformMenu *QDBusMenuBar::menuForTag(quintptr tag) const
+{
+    QDBusPlatformMenuItem *menuItem = m_menuItems.value(tag);
+    if (menuItem)
+        return const_cast<QPlatformMenu *>(menuItem->menu());
+    return nullptr;
+}
+
+void QDBusMenuBar::registerMenuBar()
+{
+    static uint menuBarId = 0;
+
+    QDBusConnection connection = QDBusConnection::sessionBus();
+    m_objectPath = QStringLiteral("/MenuBar/%1").arg(++menuBarId);
+    if (!connection.registerObject(m_objectPath, m_menu))
+        return;
+
+    QDBusMenuRegistrarInterface registrar(REGISTRAR_SERVICE, REGISTRAR_PATH, connection, this);
+    QDBusPendingReply<> r = registrar.RegisterWindow(m_windowId, QDBusObjectPath(m_objectPath));
+    r.waitForFinished();
+    if (r.isError()) {
+        qWarning("Failed to register window menu, reason: %s (\"%s\")",
+                 qUtf8Printable(r.error().name()), qUtf8Printable(r.error().message()));
+        connection.unregisterObject(m_objectPath);
+    }
+}
+
+void QDBusMenuBar::unregisterMenuBar()
+{
+    QDBusConnection connection = QDBusConnection::sessionBus();
+
+    if (m_windowId) {
+        QDBusMenuRegistrarInterface registrar(REGISTRAR_SERVICE, REGISTRAR_PATH, connection, this);
+        QDBusPendingReply<> r = registrar.UnregisterWindow(m_windowId);
+        r.waitForFinished();
+        if (r.isError())
+            qWarning("Failed to unregister window menu, reason: %s (\"%s\")",
+                     qUtf8Printable(r.error().name()), qUtf8Printable(r.error().message()));
+    }
+
+    if (!m_objectPath.isEmpty())
+        connection.unregisterObject(m_objectPath);
+}
+
+QT_END_NAMESPACE
--- /dev/null
+++ b/src/platformsupport/dbusmenu/qdbusmenubar_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDBUSMENUBAR_P_H
+#define QDBUSMENUBAR_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qdbusplatformmenu_p.h>
+#include <private/qdbusmenuadaptor_p.h>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+#include <QtGui/QWindow>
+
+QT_BEGIN_NAMESPACE
+
+class QDBusMenuBar : public QPlatformMenuBar
+{
+    Q_OBJECT
+
+public:
+    QDBusMenuBar();
+    virtual ~QDBusMenuBar();
+
+    void insertMenu(QPlatformMenu *menu, QPlatformMenu *before) Q_DECL_OVERRIDE;
+    void removeMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE;
+    void syncMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE;
+    void handleReparent(QWindow *newParentWindow) Q_DECL_OVERRIDE;
+    QPlatformMenu *menuForTag(quintptr tag) const Q_DECL_OVERRIDE;
+
+private:
+    QDBusPlatformMenu *m_menu;
+    QDBusMenuAdaptor *m_menuAdaptor;
+    QHash<quintptr, QDBusPlatformMenuItem *> m_menuItems;
+    uint m_windowId;
+    QString m_objectPath;
+
+    QDBusPlatformMenuItem *menuItemForMenu(QPlatformMenu *menu);
+    static void updateMenuItem(QDBusPlatformMenuItem *item, QPlatformMenu *menu);
+    void registerMenuBar();
+    void unregisterMenuBar();
+};
+
+QT_END_NAMESPACE
+
+#endif // QDBUSMENUBAR_P_H
--- /dev/null
+++ b/src/platformsupport/dbusmenu/qdbusmenuregistrarproxy.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ * This file was originally created by qdbusxml2cpp version 0.8
+ * Command line was: qdbusxml2cpp -p qdbusmenuregistrarproxy ../../3rdparty/dbus-ifaces/com.canonical.AppMenu.Registrar.xml
+ *
+ * However it is maintained manually.
+ */
+
+#include "qdbusmenuregistrarproxy_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * Implementation of interface class QDBusMenuRegistrarInterface
+ */
+
+QDBusMenuRegistrarInterface::QDBusMenuRegistrarInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
+    : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
+{
+}
+
+QDBusMenuRegistrarInterface::~QDBusMenuRegistrarInterface()
+{
+}
+
+QT_END_NAMESPACE
--- /dev/null
+++ b/src/platformsupport/dbusmenu/qdbusmenuregistrarproxy_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ * This file was originally created by qdbusxml2cpp version 0.8
+ * Command line was: qdbusxml2cpp -p qdbusmenuregistrarproxy ../../3rdparty/dbus-ifaces/com.canonical.AppMenu.Registrar.xml
+ *
+ * However it is maintained manually.
+ */
+
+#ifndef QDBUSMENUREGISTRARPROXY_P_H
+#define QDBUSMENUREGISTRARPROXY_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QObject>
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+#include <QtCore/QString>
+#include <QtCore/QVariant>
+#include <QtDBus/QDBusAbstractInterface>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusReply>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * Proxy class for interface com.canonical.AppMenu.Registrar
+ */
+class QDBusMenuRegistrarInterface : public QDBusAbstractInterface
+{
+    Q_OBJECT
+public:
+    static inline const char *staticInterfaceName()
+    {
+        return "com.canonical.AppMenu.Registrar";
+    }
+
+public:
+    explicit QDBusMenuRegistrarInterface(const QString &service,
+                                         const QString &path,
+                                         const QDBusConnection &connection,
+                                         QObject *parent = nullptr);
+
+    ~QDBusMenuRegistrarInterface();
+
+public Q_SLOTS: // METHODS
+    QDBusPendingReply<QString, QDBusObjectPath> GetMenuForWindow(uint windowId)
+    {
+        QList<QVariant> argumentList;
+        argumentList << QVariant::fromValue(windowId);
+        return asyncCallWithArgumentList(QStringLiteral("GetMenuForWindow"), argumentList);
+    }
+    QDBusReply<QString> GetMenuForWindow(uint windowId, QDBusObjectPath &menuObjectPath)
+    {
+        QList<QVariant> argumentList;
+        argumentList << QVariant::fromValue(windowId);
+        QDBusMessage reply = callWithArgumentList(QDBus::Block, QStringLiteral("GetMenuForWindow"), argumentList);
+        QList<QVariant> arguments = reply.arguments();
+        if (reply.type() == QDBusMessage::ReplyMessage && arguments.count() == 2)
+            menuObjectPath = qdbus_cast<QDBusObjectPath>(arguments.at(1));
+        return reply;
+    }
+
+    QDBusPendingReply<> RegisterWindow(uint windowId, const QDBusObjectPath &menuObjectPath)
+    {
+        QList<QVariant> argumentList;
+        argumentList << QVariant::fromValue(windowId) << QVariant::fromValue(menuObjectPath);
+        return asyncCallWithArgumentList(QStringLiteral("RegisterWindow"), argumentList);
+    }
+
+    QDBusPendingReply<> UnregisterWindow(uint windowId)
+    {
+        QList<QVariant> argumentList;
+        argumentList << QVariant::fromValue(windowId);
+        return asyncCallWithArgumentList(QStringLiteral("UnregisterWindow"), argumentList);
+    }
+};
+
+QT_END_NAMESPACE
+
+#endif // QDBUSMENUREGISTRARPROXY_P_H
--- a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp
+++ b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp
@@ -53,9 +53,12 @@
 #include <qpa/qplatformintegration.h>
 #include <qpa/qplatformservices.h>
 #include <qpa/qplatformdialoghelper.h>
+#ifndef QT_NO_DBUS
+#include "QtPlatformSupport/private/qdbusplatformmenu_p.h"
+#include "QtPlatformSupport/private/qdbusmenubar_p.h"
+#endif
 #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
 #include "QtPlatformSupport/private/qdbustrayicon_p.h"
-#include "QtPlatformSupport/private/qdbusplatformmenu_p.h"
 #endif
 
 #include <algorithm>
@@ -108,6 +111,21 @@ static bool isDBusTrayAvailable() {
 }
 #endif
 
+#ifndef QT_NO_DBUS
+static bool checkDBusGlobalMenuAvailable()
+{
+    QDBusConnection connection = QDBusConnection::sessionBus();
+    QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar");
+    return connection.interface()->isServiceRegistered(registrarService);
+}
+
+static bool isDBusGlobalMenuAvailable()
+{
+    static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
+    return dbusGlobalMenuAvailable;
+}
+#endif
+
 class QGenericUnixThemePrivate : public QPlatformThemePrivate
 {
 public:
@@ -166,6 +184,22 @@ QStringList QGenericUnixTheme::xdgIconTh
     return paths;
 }
 
+#ifndef QT_NO_DBUS
+QPlatformMenu *QGenericUnixTheme::createPlatformMenu() const
+{
+    if (isDBusGlobalMenuAvailable())
+        return new QDBusPlatformMenu();
+    return nullptr;
+}
+
+QPlatformMenuBar *QGenericUnixTheme::createPlatformMenuBar() const
+{
+    if (isDBusGlobalMenuAvailable())
+        return new QDBusMenuBar();
+    return nullptr;
+}
+#endif
+
 #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
 QPlatformSystemTrayIcon *QGenericUnixTheme::createPlatformSystemTrayIcon() const
 {
@@ -553,6 +587,22 @@ QPlatformTheme *QKdeTheme::createKdeThem
     return new QKdeTheme(kdeDirs, kdeVersion);
 }
 
+#ifndef QT_NO_DBUS
+QPlatformMenu *QKdeTheme::createPlatformMenu() const
+{
+    if (isDBusGlobalMenuAvailable())
+        return new QDBusPlatformMenu();
+    return nullptr;
+}
+
+QPlatformMenuBar *QKdeTheme::createPlatformMenuBar() const
+{
+    if (isDBusGlobalMenuAvailable())
+        return new QDBusMenuBar();
+    return nullptr;
+}
+#endif
+
 #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
 QPlatformSystemTrayIcon *QKdeTheme::createPlatformSystemTrayIcon() const
 {
@@ -649,6 +699,22 @@ QString QGnomeTheme::gtkFontName() const
     return QStringLiteral("%1 %2").arg(QLatin1String(defaultSystemFontNameC)).arg(defaultSystemFontSize);
 }
 
+#ifndef QT_NO_DBUS
+QPlatformMenu *QGnomeTheme::createPlatformMenu() const
+{
+    if (isDBusGlobalMenuAvailable())
+        return new QDBusPlatformMenu();
+    return nullptr;
+}
+
+QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const
+{
+    if (isDBusGlobalMenuAvailable())
+        return new QDBusMenuBar();
+    return nullptr;
+}
+#endif
+
 #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
 QPlatformSystemTrayIcon *QGnomeTheme::createPlatformSystemTrayIcon() const
 {
--- a/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h
+++ b/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h
@@ -79,6 +79,10 @@ public:
     QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE;
 
     static QStringList xdgIconThemePaths();
+#ifndef QT_NO_DBUS
+    QPlatformMenu *createPlatformMenu() const Q_DECL_OVERRIDE;
+    QPlatformMenuBar *createPlatformMenuBar() const Q_DECL_OVERRIDE;
+#endif
 #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
     QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const Q_DECL_OVERRIDE;
 #endif
@@ -101,6 +105,10 @@ public:
     const QPalette *palette(Palette type = SystemPalette) const Q_DECL_OVERRIDE;
 
     const QFont *font(Font type) const Q_DECL_OVERRIDE;
+#ifndef QT_NO_DBUS
+    QPlatformMenu *createPlatformMenu() const Q_DECL_OVERRIDE;
+    QPlatformMenuBar *createPlatformMenuBar() const Q_DECL_OVERRIDE;
+#endif
 #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
     QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const Q_DECL_OVERRIDE;
 #endif
@@ -121,6 +129,10 @@ public:
     QString standardButtonText(int button) const Q_DECL_OVERRIDE;
 
     virtual QString gtkFontName() const;
+#ifndef QT_NO_DBUS
+    QPlatformMenu *createPlatformMenu() const Q_DECL_OVERRIDE;
+    QPlatformMenuBar *createPlatformMenuBar() const Q_DECL_OVERRIDE;
+#endif
 #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
     QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const Q_DECL_OVERRIDE;
 #endif
--- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
+++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
@@ -1421,6 +1421,7 @@ void tst_QAccessibility::menuTest()
     {
     QMainWindow mw;
     mw.resize(300, 200);
+    mw.menuBar()->setNativeMenuBar(false);
     QMenu *file = mw.menuBar()->addMenu("&File");
     QMenu *fileNew = file->addMenu("&New...");
     fileNew->menuAction()->setShortcut(tr("Ctrl+N"));
--- a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp
+++ b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp
@@ -634,7 +634,7 @@ void tst_QMdiArea::changeWindowTitle()
     mw->setWindowTitle( mwc );
     QMdiArea *ws = new QMdiArea( mw );
     mw->setCentralWidget( ws );
-    mw->menuBar();
+    mw->menuBar()->setNativeMenuBar(false);
     mw->show();
     QVERIFY(QTest::qWaitForWindowExposed(mw));
 
@@ -743,7 +743,7 @@ void tst_QMdiArea::changeModified()
     mw->setWindowTitle( mwc );
     QMdiArea *ws = new QMdiArea( mw );
     mw->setCentralWidget( ws );
-    mw->menuBar();
+    mw->menuBar()->setNativeMenuBar(false);
     mw->show();
 
     QWidget *widget = new QWidget( ws );
@@ -2008,6 +2008,7 @@ void tst_QMdiArea::iconGeometryInMenuBar
 #if !defined (Q_OS_MAC) && !defined(Q_OS_WINCE)
     QMainWindow mainWindow;
     QMenuBar *menuBar = mainWindow.menuBar();
+    menuBar->setNativeMenuBar(false);
     QMdiArea *mdiArea = new QMdiArea;
     QMdiSubWindow *subWindow = mdiArea->addSubWindow(new QWidget);
     mainWindow.setCentralWidget(mdiArea);
--- a/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp
+++ b/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp
@@ -970,7 +970,7 @@ void tst_QMdiSubWindow::setSystemMenu()
     QMdiArea *mdiArea = new QMdiArea;
     mdiArea->addSubWindow(subWindow);
     mainWindow.setCentralWidget(mdiArea);
-    mainWindow.menuBar();
+    mainWindow.menuBar()->setNativeMenuBar(false);
     mainWindow.show();
     QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
     QTest::qWait(60);
@@ -1470,6 +1470,7 @@ void tst_QMdiSubWindow::hideAndShow()
     QMainWindow mainWindow;
     mainWindow.setGeometry(0, 0, 640, 480);
     QMenuBar *menuBar = mainWindow.menuBar();
+    menuBar->setNativeMenuBar(false);
     mainWindow.setCentralWidget(tabWidget);
     mainWindow.show();
     QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
@@ -1738,6 +1739,7 @@ void tst_QMdiSubWindow::replaceMenuBarWh
 
     mainWindow.setCentralWidget(mdiArea);
     QMenuBar *menuBar = mainWindow.menuBar();
+    menuBar->setNativeMenuBar(false);
     mainWindow.show();
     QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
 
@@ -1754,6 +1756,7 @@ void tst_QMdiSubWindow::replaceMenuBarWh
     // Replace.
     mainWindow.setMenuBar(new QMenuBar);
     menuBar = mainWindow.menuBar();
+    menuBar->setNativeMenuBar(false);
     qApp->processEvents();
 
     QVERIFY(subWindow->maximizedButtonsWidget());
--- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
+++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
@@ -455,6 +455,7 @@ void tst_QMenu::overrideMenuAction()
     //test the override menu action by first creating an action to which we set its menu
     QMainWindow w;
     w.resize(300, 200);
+    w.menuBar()->setNativeMenuBar(false);
     centerOnScreen(&w);
 
     QAction *aFileMenu = new QAction("&File", &w);
--- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp
+++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp
@@ -211,6 +211,7 @@ void tst_QMenuBar::cleanup()
 TestMenu tst_QMenuBar::initSimpleMenuBar(QMenuBar *mb)
 {
     TestMenu result;
+    mb->setNativeMenuBar(false);
     connect(mb, SIGNAL(triggered(QAction*)), this, SLOT(onSimpleActivated(QAction*)));
     QMenu *menu = mb->addMenu(QStringLiteral("&accel"));
     QAction *action = menu->addAction(QStringLiteral("menu1") );
@@ -278,6 +279,7 @@ void tst_QMenuBar::onComplexActionTrigge
 TestMenu tst_QMenuBar::initComplexMenuBar(QMenuBar *mb)
 {
     TestMenu result;
+    mb->setNativeMenuBar(false);
     QMenu *menu = addNumberedMenu(mb, 1);
     result.menus << menu;
     for (char c = 'a'; c < 'c'; ++c)
@@ -991,6 +993,7 @@ void tst_QMenuBar::check_altClosePress()
 
     QMainWindow w;
     w.setWindowTitle(QTest::currentTestFunction());
+    w.menuBar()->setNativeMenuBar(false);
     QMenu *menuFile = w.menuBar()->addMenu(tr("&File"));
     menuFile->addAction("Quit");
     QMenu *menuEdit = w.menuBar()->addMenu(tr("&Edit"));
@@ -1066,6 +1069,7 @@ void tst_QMenuBar::check_menuPosition()
         menu.addAction("item");
     }
 
+    w.menuBar()->setNativeMenuBar(false);
     QAction *menu_action = w.menuBar()->addMenu(&menu);
     centerOnScreen(&w);
     w.show();
@@ -1399,6 +1403,7 @@ void tst_QMenuBar::cornerWidgets()
     widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + dataTag);
     QVBoxLayout *layout = new QVBoxLayout(&widget);
     QMenuBar *menuBar = new QMenuBar(&widget);
+    menuBar->setNativeMenuBar(false);
     layout->addWidget(menuBar);
     QMenu *fileMenu = menuBar->addMenu("File");
     fileMenu->addAction("Quit");
