From d428f3b96fc5a162cf20d69f06811e2aa605b8d6 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Sun, 6 Mar 2016 19:33:37 -0800
Subject: [PATCH] Working on crash bugs

---
 interface/src/Application.cpp                 |   8 +-
 interface/src/Menu.cpp                        |   9 +-
 interface/src/Menu.h                          |   1 -
 interface/src/PluginContainerProxy.cpp        | 134 ++---------------
 interface/src/PluginContainerProxy.h          |  20 +--
 libraries/fbx/src/OBJReader.cpp               |   6 +
 .../plugins/src/plugins/PluginContainer.cpp   | 136 ++++++++++++++++++
 .../plugins/src/plugins/PluginContainer.h     |  41 ++++--
 libraries/shared/src/SharedUtil.cpp           |   3 +-
 libraries/shared/src/SharedUtil.h             |  34 ++++-
 .../ui}/src/MainWindow.cpp                    |  10 +-
 {interface => libraries/ui}/src/MainWindow.h  |   1 +
 libraries/ui/src/ui/Menu.cpp                  |  24 ++--
 libraries/ui/src/ui/Menu.h                    |  12 +-
 plugins/hifiNeuron/CMakeLists.txt             |   2 +-
 plugins/hifiSdl2/CMakeLists.txt               |   2 +-
 plugins/hifiSixense/CMakeLists.txt            |   2 +-
 plugins/oculus/CMakeLists.txt                 |   2 +-
 plugins/openvr/CMakeLists.txt                 |   2 +-
 tests/controllers/src/main.cpp                |  15 +-
 20 files changed, 246 insertions(+), 218 deletions(-)
 rename {interface => libraries/ui}/src/MainWindow.cpp (97%)
 rename {interface => libraries/ui}/src/MainWindow.h (98%)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index bf83fdcde9..518672916b 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1122,8 +1122,6 @@ Application::~Application() {
     _octreeProcessor.terminate();
     _entityEditSender.terminate();
 
-    Menu::getInstance()->deleteLater();
-
     _physicsEngine->setCharacterController(NULL);
 
     ModelEntityItem::cleanupLoadedAnimations();
@@ -1168,6 +1166,10 @@ Application::~Application() {
 #if 0
     ConnexionClient::getInstance().destroy();
 #endif
+    // The window takes ownership of the menu, so this has the side effect of destroying it.
+    _window->setMenuBar(nullptr);
+    
+    _window->deleteLater();
 
     qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
 }
@@ -1334,7 +1336,7 @@ void Application::initializeUi() {
             _keyboardMouseDevice = std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin);
         }
     }
-    Menu::setInstance();
+    _window->setMenuBar(new Menu());
     updateInputModes();
 
     auto compositorHelper = DependencyManager::get<CompositorHelper>();
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 164f94a094..15fae25646 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -46,15 +46,8 @@
 
 #include "Menu.h"
 
-// Fixme make static member of Menu
-static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu";
-
-void Menu::setInstance() {
-    globalInstance<Menu>(MENU_PROPERTY_NAME);
-}
-
 Menu* Menu::getInstance() {
-    return static_cast<Menu*>(ui::Menu::getInstance());
+    return static_cast<Menu*>(qApp->getWindow()->menuBar());
 }
 
 Menu::Menu() {
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 6d5fd45b66..7130dd59e8 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -20,7 +20,6 @@ class Menu : public ui::Menu {
     Q_OBJECT
 
 public:
-    static void setInstance();
     static Menu* getInstance();
     Menu();
     Q_INVOKABLE void addMenuItem(const MenuItemProperties& properties);
diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp
index 43a35e5ab4..574acbe586 100644
--- a/interface/src/PluginContainerProxy.cpp
+++ b/interface/src/PluginContainerProxy.cpp
@@ -23,124 +23,16 @@ PluginContainerProxy::PluginContainerProxy() {
 PluginContainerProxy::~PluginContainerProxy() {
 }
 
+ui::Menu* PluginContainerProxy::getPrimaryMenu() {
+    auto appMenu = qApp->_window->menuBar();
+    auto uiMenu = dynamic_cast<ui::Menu*>(appMenu);
+    return uiMenu;
+}
+
 bool PluginContainerProxy::isForeground() {
     return qApp->isForeground() && !qApp->getWindow()->isMinimized();
 }
 
-void PluginContainerProxy::addMenu(const QString& menuName) {
-    Menu::getInstance()->addMenu(menuName);
-}
-
-void PluginContainerProxy::removeMenu(const QString& menuName) {
-    Menu::getInstance()->removeMenu(menuName);
-}
-
-QAction* PluginContainerProxy::addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
-    auto menu = Menu::getInstance();
-    MenuWrapper* parentItem = menu->getMenu(path);
-    QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name);
-    if (!groupName.isEmpty()) {
-        QActionGroup* group{ nullptr };
-        if (!_exclusiveGroups.count(groupName)) {
-            group = _exclusiveGroups[groupName] = new QActionGroup(menu);
-            group->setExclusive(true);
-        } else {
-            group = _exclusiveGroups[groupName];
-        }
-        group->addAction(action);
-    }
-    connect(action, &QAction::triggered, [=] {
-        onClicked(action->isChecked());
-    });
-    action->setCheckable(checkable);
-    action->setChecked(checked);
-    if (type == PluginType::DISPLAY_PLUGIN) {
-        _currentDisplayPluginActions.push_back({ path, name });
-    } else {
-        _currentInputPluginActions.push_back({ path, name });
-    }
-    return action;
-}
-
-void PluginContainerProxy::removeMenuItem(const QString& menuName, const QString& menuItem) {
-    Menu::getInstance()->removeMenuItem(menuName, menuItem);
-}
-
-bool PluginContainerProxy::isOptionChecked(const QString& name) {
-    return Menu::getInstance()->isOptionChecked(name);
-}
-
-void PluginContainerProxy::setIsOptionChecked(const QString& path, bool checked) {
-    Menu::getInstance()->setIsOptionChecked(path, checked);
-}
-
-// FIXME there is a bug in the fullscreen setting, where leaving
-// fullscreen does not restore the window frame, making it difficult
-// or impossible to move or size the window.
-// Additionally, setting fullscreen isn't hiding the menu on windows
-// make it useless for stereoscopic modes.
-void PluginContainerProxy::setFullscreen(const QScreen* target, bool hideMenu) {
-    auto _window = qApp->getWindow();
-    if (!_window->isFullScreen()) {
-        _savedGeometry = _window->geometry();
-    }
-    if (nullptr == target) {
-        // FIXME target the screen where the window currently is
-        target = qApp->primaryScreen();
-    }
-
-    _window->setGeometry(target->availableGeometry());
-    _window->windowHandle()->setScreen((QScreen*)target);
-    _window->showFullScreen();
-
-#ifndef Q_OS_MAC
-    // also hide the QMainWindow's menuBar
-    QMenuBar* menuBar = _window->menuBar();
-    if (menuBar && hideMenu) {
-        menuBar->setVisible(false);
-    }
-#endif
-}
-
-void PluginContainerProxy::unsetFullscreen(const QScreen* avoid) {
-    auto _window = qApp->getWindow();
-    _window->showNormal();
-
-    QRect targetGeometry = _savedGeometry;
-    if (avoid != nullptr) {
-        QRect avoidGeometry = avoid->geometry();
-        if (avoidGeometry.contains(targetGeometry.topLeft())) {
-            QScreen* newTarget = qApp->primaryScreen();
-            if (newTarget == avoid) {
-                foreach(auto screen, qApp->screens()) {
-                    if (screen != avoid) {
-                        newTarget = screen;
-                        break;
-                    }
-                }
-            }
-            targetGeometry = newTarget->availableGeometry();
-        }
-    }
-#ifdef Q_OS_MAC
-    QTimer* timer = new QTimer();
-    timer->singleShot(2000, [=] {
-        _window->setGeometry(targetGeometry);
-        timer->deleteLater();
-    });
-#else
-    _window->setGeometry(targetGeometry);
-#endif
-
-#ifndef Q_OS_MAC
-    // also show the QMainWindow's menuBar
-    QMenuBar* menuBar = _window->menuBar();
-    if (menuBar) {
-        menuBar->setVisible(true);
-    }
-#endif
-}
-
 void PluginContainerProxy::requestReset() {
     // We could signal qApp to sequence this, but it turns out that requestReset is only used from within the main thread anyway.
     qApp->resetSensors(true);
@@ -154,8 +46,8 @@ GLWidget* PluginContainerProxy::getPrimaryWidget() {
     return qApp->_glWidget;
 }
 
-QWindow* PluginContainerProxy::getPrimaryWindow() {
-    return qApp->_glWidget->windowHandle();
+MainWindow* PluginContainerProxy::getPrimaryWindow() {
+    return qApp->getWindow();
 }
 
 QOpenGLContext* PluginContainerProxy::getPrimaryContext() {
@@ -184,13 +76,3 @@ void PluginContainerProxy::releaseOverlayTexture(const gpu::TexturePointer& text
     qApp->_applicationOverlay.releaseOverlay(texture);
 }
 
-/// settings interface
-bool PluginContainerProxy::getBoolSetting(const QString& settingName, bool defaultValue) {
-    Setting::Handle<bool> settingValue(settingName, defaultValue);
-    return settingValue.get();
-}
-
-void PluginContainerProxy::setBoolSetting(const QString& settingName, bool value) {
-    Setting::Handle<bool> settingValue(settingName, value);
-    return settingValue.set(value);
-}
diff --git a/interface/src/PluginContainerProxy.h b/interface/src/PluginContainerProxy.h
index eff1dcf938..d6ee354192 100644
--- a/interface/src/PluginContainerProxy.h
+++ b/interface/src/PluginContainerProxy.h
@@ -14,34 +14,20 @@ class PluginContainerProxy : public QObject, PluginContainer {
     Q_OBJECT
     PluginContainerProxy();
     virtual ~PluginContainerProxy();
-    virtual void addMenu(const QString& menuName) override;
-    virtual void removeMenu(const QString& menuName) override;
-    virtual QAction* addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override;
-    virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override;
-    virtual bool isOptionChecked(const QString& name) override;
-    virtual void setIsOptionChecked(const QString& path, bool checked) override;
-    virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override;
-    virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override;
     virtual void showDisplayPluginsTools() override;
     virtual void requestReset() override;
     virtual bool makeRenderingContextCurrent() override;
     virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;
     virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override;
     virtual GLWidget* getPrimaryWidget() override;
-    virtual QWindow* getPrimaryWindow() override;
+    virtual MainWindow* getPrimaryWindow() override;
+    virtual ui::Menu* getPrimaryMenu() override;
     virtual QOpenGLContext* getPrimaryContext() override;
     virtual bool isForeground() override;
     virtual const DisplayPlugin* getActiveDisplayPlugin() const override;
 
-    /// settings interface
-    virtual bool getBoolSetting(const QString& settingName, bool defaultValue) override;
-    virtual void setBoolSetting(const QString& settingName, bool value) override;
-
-    QRect _savedGeometry{ 10, 120, 800, 600 };
-    std::map<QString, QActionGroup*> _exclusiveGroups;
-
     friend class Application;
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp
index e735ba06d9..ee8a2a2fae 100644
--- a/libraries/fbx/src/OBJReader.cpp
+++ b/libraries/fbx/src/OBJReader.cpp
@@ -184,6 +184,9 @@ bool OBJReader::isValidTexture(const QByteArray &filename) {
     }
     QUrl candidateUrl = _url.resolved(QUrl(filename));
     QNetworkReply *netReply = request(candidateUrl, true);
+    if (!netReply) {
+        return false;
+    }
     bool isValid = netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200);
     netReply->deleteLater();
     return isValid;
@@ -257,6 +260,9 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
 }
 
 QNetworkReply* OBJReader::request(QUrl& url, bool isTest) {
+    if (!qApp) {
+        return nullptr;
+    }
     QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
     QNetworkRequest netRequest(url);
     QNetworkReply* netReply = isTest ? networkAccessManager.head(netRequest) : networkAccessManager.get(netRequest);
diff --git a/libraries/plugins/src/plugins/PluginContainer.cpp b/libraries/plugins/src/plugins/PluginContainer.cpp
index 8afac745f3..2581389424 100644
--- a/libraries/plugins/src/plugins/PluginContainer.cpp
+++ b/libraries/plugins/src/plugins/PluginContainer.cpp
@@ -7,6 +7,14 @@
 //
 #include "PluginContainer.h"
 
+#include <QtCore/QTimer>
+#include <QtGui/QScreen>
+#include <QtGui/QWindow>
+#include <QtWidgets/QApplication>
+
+#include <ui/Menu.h>
+#include <MainWindow.h>
+
 static PluginContainer* INSTANCE{ nullptr };
 
 PluginContainer& PluginContainer::getInstance() {
@@ -23,3 +31,131 @@ PluginContainer::~PluginContainer() {
     Q_ASSERT(INSTANCE == this);
     INSTANCE = nullptr;
 };
+
+
+void PluginContainer::addMenu(const QString& menuName) {
+    getPrimaryMenu()->addMenu(menuName);
+}
+
+void PluginContainer::removeMenu(const QString& menuName) {
+    getPrimaryMenu()->removeMenu(menuName);
+}
+
+QAction* PluginContainer::addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
+    auto menu = getPrimaryMenu();
+    MenuWrapper* parentItem = menu->getMenu(path);
+    QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name);
+    if (!groupName.isEmpty()) {
+        QActionGroup* group { nullptr };
+        if (!_exclusiveGroups.count(groupName)) {
+            group = _exclusiveGroups[groupName] = new QActionGroup(menu);
+            group->setExclusive(true);
+        } else {
+            group = _exclusiveGroups[groupName];
+        }
+        group->addAction(action);
+    }
+    QObject::connect(action, &QAction::triggered, [=] {
+        onClicked(action->isChecked());
+    });
+    action->setCheckable(checkable);
+    action->setChecked(checked);
+    if (type == PluginType::DISPLAY_PLUGIN) {
+        _currentDisplayPluginActions.push_back({ path, name });
+    } else {
+        _currentInputPluginActions.push_back({ path, name });
+    }
+    return action;
+}
+
+void PluginContainer::removeMenuItem(const QString& menuName, const QString& menuItem) {
+    getPrimaryMenu()->removeMenuItem(menuName, menuItem);
+}
+
+bool PluginContainer::isOptionChecked(const QString& name) {
+    return getPrimaryMenu()->isOptionChecked(name);
+}
+
+void PluginContainer::setIsOptionChecked(const QString& path, bool checked) {
+    getPrimaryMenu()->setIsOptionChecked(path, checked);
+}
+
+
+
+// FIXME there is a bug in the fullscreen setting, where leaving
+// fullscreen does not restore the window frame, making it difficult
+// or impossible to move or size the window.
+// Additionally, setting fullscreen isn't hiding the menu on windows
+// make it useless for stereoscopic modes.
+void PluginContainer::setFullscreen(const QScreen* target, bool hideMenu) {
+    auto _window = getPrimaryWindow();
+    if (!_window->isFullScreen()) {
+        _savedGeometry = _window->geometry();
+    }
+    if (nullptr == target) {
+        // FIXME target the screen where the window currently is
+        target = qApp->primaryScreen();
+    }
+
+    _window->setGeometry(target->availableGeometry());
+    _window->windowHandle()->setScreen((QScreen*)target);
+    _window->showFullScreen();
+
+#ifndef Q_OS_MAC
+    // also hide the QMainWindow's menuBar
+    QMenuBar* menuBar = _window->menuBar();
+    if (menuBar && hideMenu) {
+        menuBar->setVisible(false);
+    }
+#endif
+}
+
+void PluginContainer::unsetFullscreen(const QScreen* avoid) {
+    auto _window = getPrimaryWindow();
+    _window->showNormal();
+
+    QRect targetGeometry = _savedGeometry;
+    if (avoid != nullptr) {
+        QRect avoidGeometry = avoid->geometry();
+        if (avoidGeometry.contains(targetGeometry.topLeft())) {
+            QScreen* newTarget = qApp->primaryScreen();
+            if (newTarget == avoid) {
+                foreach(auto screen, qApp->screens()) {
+                    if (screen != avoid) {
+                        newTarget = screen;
+                        break;
+                    }
+                }
+            }
+            targetGeometry = newTarget->availableGeometry();
+        }
+    }
+#ifdef Q_OS_MAC
+    QTimer* timer = new QTimer();
+    timer->singleShot(2000, [=] {
+        _window->setGeometry(targetGeometry);
+        timer->deleteLater();
+    });
+#else
+    _window->setGeometry(targetGeometry);
+#endif
+
+#ifndef Q_OS_MAC
+    // also show the QMainWindow's menuBar
+    QMenuBar* menuBar = _window->menuBar();
+    if (menuBar) {
+        menuBar->setVisible(true);
+    }
+#endif
+}
+
+/// settings interface
+bool PluginContainer::getBoolSetting(const QString& settingName, bool defaultValue) {
+    Setting::Handle<bool> settingValue(settingName, defaultValue);
+    return settingValue.get();
+}
+
+void PluginContainer::setBoolSetting(const QString& settingName, bool value) {
+    Setting::Handle<bool> settingValue(settingName, value);
+    return settingValue.set(value);
+}
diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/plugins/src/plugins/PluginContainer.h
index b5ef4b0d55..991b8260c2 100644
--- a/libraries/plugins/src/plugins/PluginContainer.h
+++ b/libraries/plugins/src/plugins/PluginContainer.h
@@ -8,10 +8,13 @@
 #pragma once
 
 #include <functional>
+#include <map>
 #include <stdint.h>
-#include <QString>
+
+#include <QtCore/QString>
 #include <QtCore/QVector>
 #include <QtCore/QPair>
+#include <QtCore/QRect>
 
 #include "Forward.h"
 
@@ -28,33 +31,44 @@ namespace gpu {
     using TexturePointer = std::shared_ptr<Texture>;
 }
 
+namespace ui {
+    class Menu;
+}
+
+class QActionGroup;
+class MainWindow;
+
 class PluginContainer {
 public:
     static PluginContainer& getInstance();
     PluginContainer();
     virtual ~PluginContainer();
-    virtual void addMenu(const QString& menuName) = 0;
-    virtual void removeMenu(const QString& menuName) = 0;
-    virtual QAction* addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0;
-    virtual void removeMenuItem(const QString& menuName, const QString& menuItem) = 0;
-    virtual bool isOptionChecked(const QString& name) = 0;
-    virtual void setIsOptionChecked(const QString& path, bool checked) = 0;
-    virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = false) = 0;
-    virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0;
+
+    void addMenu(const QString& menuName);
+    void removeMenu(const QString& menuName);
+    QAction* addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "");
+    void removeMenuItem(const QString& menuName, const QString& menuItem);
+    bool isOptionChecked(const QString& name);
+    void setIsOptionChecked(const QString& path, bool checked);
+
+    void setFullscreen(const QScreen* targetScreen, bool hideMenu = false);
+    void unsetFullscreen(const QScreen* avoidScreen = nullptr);
+
+    virtual ui::Menu* getPrimaryMenu() = 0;
     virtual void showDisplayPluginsTools() = 0;
     virtual void requestReset() = 0;
     virtual bool makeRenderingContextCurrent() = 0;
     virtual void releaseSceneTexture(const gpu::TexturePointer& texture) = 0;
     virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) = 0;
     virtual GLWidget* getPrimaryWidget() = 0;
-    virtual QWindow* getPrimaryWindow() = 0;
+    virtual MainWindow* getPrimaryWindow() = 0;
     virtual QOpenGLContext* getPrimaryContext() = 0;
     virtual bool isForeground() = 0;
     virtual const DisplayPlugin* getActiveDisplayPlugin() const = 0;
 
     /// settings interface
-    virtual bool getBoolSetting(const QString& settingName, bool defaultValue) = 0;
-    virtual void setBoolSetting(const QString& settingName, bool value) = 0;
+    bool getBoolSetting(const QString& settingName, bool defaultValue);
+    void setBoolSetting(const QString& settingName, bool value);
 
     QVector<QPair<QString, QString>>& currentDisplayActions() {
         return _currentDisplayPluginActions;
@@ -67,5 +81,6 @@ public:
 protected:
     QVector<QPair<QString, QString>> _currentDisplayPluginActions;
     QVector<QPair<QString, QString>> _currentInputPluginActions;
-
+    std::map<QString, QActionGroup*> _exclusiveGroups;
+    QRect _savedGeometry { 10, 120, 800, 600 };
 };
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index 8db66f3fca..c979d8f4b6 100644
--- a/libraries/shared/src/SharedUtil.cpp
+++ b/libraries/shared/src/SharedUtil.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "SharedUtil.h"
+
 #include <cassert>
 #include <cstdlib>
 #include <cstdio>
@@ -33,7 +35,6 @@
 #include "NumericalConstants.h"
 #include "OctalCode.h"
 #include "SharedLogging.h"
-#include "SharedUtil.h"
 
 static int usecTimestampNowAdjust = 0; // in usec
 void usecTimestampNowForceClockSkew(int clockSkew) {
diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h
index 1fae3bcff6..01c5a74bb4 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -24,12 +24,33 @@
 #include <QtCore/QDebug>
 #include <QtCore/QCoreApplication>
 
+// Access to the global instance pointer to enable setting / unsetting
+template <typename T>
+std::unique_ptr<T>& globalInstancePointer() {
+    static std::unique_ptr<T> instancePtr;
+    return instancePtr;
+}
+
+template <typename T>
+void setGlobalInstance(const char* propertyName, T* instance) {
+    globalInstancePointer<T>().reset(instance);
+}
+
+template <typename T>
+bool destroyGlobalInstance() {
+    std::unique_ptr<T>& instancePtr = globalInstancePointer<T>();
+    if (instancePtr.get()) {
+        instancePtr.reset();
+        return true;
+    }
+    return false;
+}
+
 // Provides efficient access to a named global type.  By storing the value
 // in the QApplication by name we can implement the singleton pattern and 
 // have the single instance function across DLL boundaries.  
 template <typename T, typename... Args>
 T* globalInstance(const char* propertyName, Args&&... args) {
-    static std::unique_ptr<T> instancePtr;
     static T* resultInstance { nullptr };
     static std::mutex mutex;
     if (!resultInstance) {
@@ -37,10 +58,12 @@ T* globalInstance(const char* propertyName, Args&&... args) {
         if (!resultInstance) {
             auto variant = qApp->property(propertyName);
             if (variant.isNull()) {
-                // Since we're building the object, store it in a shared_ptr so it's 
-                // destroyed by the destructor of the static instancePtr
-                instancePtr = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-
+                std::unique_ptr<T>& instancePtr = globalInstancePointer<T>();
+                if (!instancePtr.get()) {
+                    // Since we're building the object, store it in a shared_ptr so it's 
+                    // destroyed by the destructor of the static instancePtr
+                    instancePtr = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+                }
                 void* voidInstance = &(*instancePtr);
                 variant = QVariant::fromValue(voidInstance);
                 qApp->setProperty(propertyName, variant);
@@ -52,6 +75,7 @@ T* globalInstance(const char* propertyName, Args&&... args) {
     return resultInstance;
 }
 
+
 const int BYTES_PER_COLOR = 3;
 const int BYTES_PER_FLAGS = 1;
 typedef unsigned char colorPart;
diff --git a/interface/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp
similarity index 97%
rename from interface/src/MainWindow.cpp
rename to libraries/ui/src/MainWindow.cpp
index 16aedc4bb7..ce23bd02a2 100644
--- a/interface/src/MainWindow.cpp
+++ b/libraries/ui/src/MainWindow.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "MainWindow.h"
+
 #include <QApplication>
 #include <QDesktopWidget>
 #include <QEvent>
@@ -20,10 +22,8 @@
 #include <QDragEnterEvent>
 #include <QDropEvent>
 #include <QMimeData>
+#include <QDebug>
 
-#include "MainWindow.h"
-#include "Menu.h"
-#include "Util.h"
 
 MainWindow::MainWindow(QWidget* parent) :
     QMainWindow(parent),
@@ -33,6 +33,10 @@ MainWindow::MainWindow(QWidget* parent) :
     setAcceptDrops(true);
 }
 
+MainWindow::~MainWindow() {
+    qDebug() << "Destroying main window";
+}
+
 void MainWindow::restoreGeometry() {
     // Did not use setGeometry() on purpose,
     // see http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application
diff --git a/interface/src/MainWindow.h b/libraries/ui/src/MainWindow.h
similarity index 98%
rename from interface/src/MainWindow.h
rename to libraries/ui/src/MainWindow.h
index eb262e0f97..3fee62692d 100644
--- a/interface/src/MainWindow.h
+++ b/libraries/ui/src/MainWindow.h
@@ -20,6 +20,7 @@ class MainWindow : public QMainWindow {
     Q_OBJECT
 public:
     explicit MainWindow(QWidget* parent = NULL);
+    ~MainWindow();
     
 public slots:
     void restoreGeometry();
diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp
index 47f7aa4eac..1b70213876 100644
--- a/libraries/ui/src/ui/Menu.cpp
+++ b/libraries/ui/src/ui/Menu.cpp
@@ -18,15 +18,8 @@
 #include "Logging.h"
 
 using namespace ui;
-static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu";
 
-Menu* Menu::getInstance() {
-    static Menu* instance = globalInstance<Menu>(MENU_PROPERTY_NAME);
-    return instance;
-}
-
-Menu::Menu() {
-}
+Menu::Menu() {}
 
 void Menu::toggleAdvancedMenus() {
     setGroupingIsVisible("Advanced", !getGroupingIsVisible("Advanced"));
@@ -223,7 +216,7 @@ void Menu::removeAction(MenuWrapper* menu, const QString& actionName) {
 
 void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
     if (thread() != QThread::currentThread()) {
-        QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection,
+        QMetaObject::invokeMethod(this, "setIsOptionChecked", Qt::BlockingQueuedConnection,
                     Q_ARG(const QString&, menuOption),
                     Q_ARG(bool, isChecked));
         return;
@@ -275,7 +268,7 @@ QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) {
 MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) {
     QAction* action = getActionFromName(menuName, menu);
     if (action) {
-        return MenuWrapper::fromMenu(action->menu());
+        return _backMap[action->menu()];
     }
     return NULL;
 }
@@ -320,7 +313,7 @@ QAction* Menu::getMenuAction(const QString& menuName) {
         if (!action) {
             break;
         }
-        parent = MenuWrapper::fromMenu(action->menu());
+        parent = _backMap[action->menu()];
     }
     return action;
 }
@@ -357,7 +350,7 @@ MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
         menu = getSubMenuFromName(menuTreePart.trimmed(), addTo);
         if (!menu) {
             if (!addTo) {
-                menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed()));
+                menu = new MenuWrapper(*this, QMenuBar::addMenu(menuTreePart.trimmed()));
             } else {
                 menu = addTo->addMenu(menuTreePart.trimmed());
             }
@@ -498,11 +491,11 @@ void Menu::removeActionGroup(const QString& groupName) {
     removeMenu(groupName);
 }
 
-MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) {
+MenuWrapper::MenuWrapper(ui::Menu& rootMenu, QMenu* menu) : _rootMenu(rootMenu), _realMenu(menu) {
     VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
         vrMenu->addMenu(menu);
     });
-    _backMap[menu] = this;
+    _rootMenu._backMap[menu] = this;
 }
 
 QList<QAction*> MenuWrapper::actions() {
@@ -510,7 +503,7 @@ QList<QAction*> MenuWrapper::actions() {
 }
 
 MenuWrapper* MenuWrapper::addMenu(const QString& menuName) {
-    return new MenuWrapper(_realMenu->addMenu(menuName));
+    return new MenuWrapper(_rootMenu, _realMenu->addMenu(menuName));
 }
 
 void MenuWrapper::setEnabled(bool enabled) {
@@ -558,4 +551,3 @@ void MenuWrapper::insertAction(QAction* before, QAction* action) {
     });
 }
 
-QHash<QMenu*, MenuWrapper*> MenuWrapper::_backMap;
diff --git a/libraries/ui/src/ui/Menu.h b/libraries/ui/src/ui/Menu.h
index 44aa661a21..895e40fe68 100644
--- a/libraries/ui/src/ui/Menu.h
+++ b/libraries/ui/src/ui/Menu.h
@@ -41,14 +41,9 @@ public:
     }
 
 private:
-    MenuWrapper(QMenu* menu);
-
-    static MenuWrapper* fromMenu(QMenu* menu) {
-        return _backMap[menu];
-    }
-
+    MenuWrapper(ui::Menu& rootMenu, QMenu* menu);
+    ui::Menu& _rootMenu;
     QMenu* const _realMenu;
-    static QHash<QMenu*, MenuWrapper*> _backMap;
     friend class ui::Menu;
 };
 
@@ -60,7 +55,6 @@ public:
     static const int UNSPECIFIED_POSITION = -1;
 
     Menu();
-    static Menu* getInstance();
 
     void loadSettings();
     void saveSettings();
@@ -146,8 +140,10 @@ protected:
     bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; }
     QHash<QString, bool> _groupingVisible;
     QHash<QString, QSet<QAction*>> _groupingActions;
+    QHash<QMenu*, MenuWrapper*> _backMap;
 
     static bool _isSomeSubmenuShown;
+    friend class ::MenuWrapper;
 };
 
 } // namespace ui
diff --git a/plugins/hifiNeuron/CMakeLists.txt b/plugins/hifiNeuron/CMakeLists.txt
index 9c512fc877..1cab2359a9 100644
--- a/plugins/hifiNeuron/CMakeLists.txt
+++ b/plugins/hifiNeuron/CMakeLists.txt
@@ -8,6 +8,6 @@
 
 set(TARGET_NAME hifiNeuron)
 setup_hifi_plugin(Script Qml Widgets)
-link_hifi_libraries(shared controllers plugins input-plugins)
+link_hifi_libraries(shared controllers ui plugins input-plugins)
 target_neuron()
 
diff --git a/plugins/hifiSdl2/CMakeLists.txt b/plugins/hifiSdl2/CMakeLists.txt
index 2f106aedce..7e499e314a 100644
--- a/plugins/hifiSdl2/CMakeLists.txt
+++ b/plugins/hifiSdl2/CMakeLists.txt
@@ -8,5 +8,5 @@
 
 set(TARGET_NAME hifiSdl2)
 setup_hifi_plugin(Script Qml Widgets)
-link_hifi_libraries(shared controllers plugins input-plugins script-engine)
+link_hifi_libraries(shared controllers ui plugins input-plugins script-engine)
 target_sdl2()
diff --git a/plugins/hifiSixense/CMakeLists.txt b/plugins/hifiSixense/CMakeLists.txt
index c790f6f14d..589b5b8964 100644
--- a/plugins/hifiSixense/CMakeLists.txt
+++ b/plugins/hifiSixense/CMakeLists.txt
@@ -8,5 +8,5 @@
 
 set(TARGET_NAME hifiSixense)
 setup_hifi_plugin(Script Qml Widgets)
-link_hifi_libraries(shared controllers plugins input-plugins)
+link_hifi_libraries(shared controllers ui plugins input-plugins)
 target_sixense()
diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt
index 4b2927e0fa..e5cbffda21 100644
--- a/plugins/oculus/CMakeLists.txt
+++ b/plugins/oculus/CMakeLists.txt
@@ -13,7 +13,7 @@ if (WIN32)
 
     set(TARGET_NAME oculus)
     setup_hifi_plugin()
-    link_hifi_libraries(shared gl gpu controllers plugins display-plugins input-plugins)
+    link_hifi_libraries(shared gl gpu controllers ui plugins display-plugins input-plugins)
     
     include_hifi_library_headers(octree)
     
diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt
index 878df3bfa5..1ba8d05b92 100644
--- a/plugins/openvr/CMakeLists.txt
+++ b/plugins/openvr/CMakeLists.txt
@@ -11,7 +11,7 @@ if (WIN32)
     add_definitions(-DGLEW_STATIC)
     set(TARGET_NAME openvr)
     setup_hifi_plugin(OpenGL Script Qml Widgets)
-    link_hifi_libraries(shared gl networking controllers
+    link_hifi_libraries(shared gl networking controllers ui 
         plugins display-plugins input-plugins script-engine
         render-utils model gpu render model-networking fbx)
 
diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp
index 8d5b6cc7f7..9bf74fc501 100644
--- a/tests/controllers/src/main.cpp
+++ b/tests/controllers/src/main.cpp
@@ -81,26 +81,17 @@ class PluginContainerProxy : public QObject, PluginContainer {
     Q_OBJECT
 public:
     virtual ~PluginContainerProxy() {}
-    virtual void addMenu(const QString& menuName) override {}
-    virtual void removeMenu(const QString& menuName) override {}
-    virtual QAction* addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr;  }
-    virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {}
-    virtual bool isOptionChecked(const QString& name) override { return false;  }
-    virtual void setIsOptionChecked(const QString& path, bool checked) override {}
-    virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override {}
-    virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {}
     virtual void showDisplayPluginsTools() override {}
     virtual void requestReset() override {}
     virtual bool makeRenderingContextCurrent() override { return true; }
     virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override {}
     virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override {}
     virtual GLWidget* getPrimaryWidget() override { return nullptr; }
-    virtual QWindow* getPrimaryWindow() override { return nullptr; }
+    virtual MainWindow* getPrimaryWindow() override { return nullptr; }
     virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }
-    virtual bool isForeground() override { return true;  }
+    virtual ui::Menu* getPrimaryMenu() { return nullptr; }
+    virtual bool isForeground() override { return true; }
     virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr;  }
-    virtual bool getBoolSetting(const QString& settingName, bool defaultValue) override { return defaultValue; }
-    virtual void setBoolSetting(const QString& settingName, bool value) override { }
 };
 
 class MyControllerScriptingInterface : public controller::ScriptingInterface {