diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
index fa9cb62b7c..14518ac37a 100644
--- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp
+++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
@@ -751,30 +751,9 @@ void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) {
         return;
     }
 
-    QVariant result;
-#if 1
-    auto invokeResult = QMetaObject::invokeMethod(object, "inputMethodQuery", Q_RETURN_ARG(QVariant, result),
-        Q_ARG(Qt::InputMethodQuery, Qt::ImEnabled),
-        Q_ARG(QVariant, QVariant()));
-#else
-
-    //static const char* INPUT_METHOD_QUERY_METHOD_NAME = "inputMethodQuery(Qt::InputMethodQuery, QVariant)";
-    static const char* INPUT_METHOD_QUERY_METHOD_NAME = "inputMethodQuery";
-    auto meta = object->metaObject();
-    qDebug() << "new focus " << object;
-    auto index = meta->indexOfMethod(INPUT_METHOD_QUERY_METHOD_NAME);
-    if (index < 0 || index >= meta->methodCount()) {
-        setFocusText(false);
-        return;
-    }
-
-    auto method = meta->method(index);
-    auto invokeResult = method.invoke(object,
-        Q_RETURN_ARG(QVariant, result),
-        Q_ARG(Qt::InputMethodQuery, Qt::ImEnabled),
-        Q_ARG(QVariant, QVariant()));
-#endif
-    setFocusText(invokeResult && result.toBool());
+    QInputMethodQueryEvent query(Qt::ImEnabled);
+    qApp->sendEvent(object, &query);
+    setFocusText(query.value(Qt::ImEnabled).toBool());
 }
 
 void OffscreenQmlSurface::setFocusText(bool newFocusText) {
diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp
index 155bc9f079..b13a60d388 100644
--- a/plugins/openvr/src/OpenVrHelpers.cpp
+++ b/plugins/openvr/src/OpenVrHelpers.cpp
@@ -14,9 +14,13 @@
 #include <QtCore/QTimer>
 #include <QtCore/QLoggingCategory>
 #include <QtCore/QProcessEnvironment>
+#include <QtGui/QInputMethodEvent>
+#include <QtQuick/QQuickWindow>
 
 #include <Windows.h>
 
+#include <OffscreenUi.h>
+
 Q_DECLARE_LOGGING_CATEGORY(displayplugins)
 Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display")
 
@@ -90,6 +94,82 @@ void releaseOpenVrSystem() {
     }
 }
 
+static char textArray[8192];
+
+static QMetaObject::Connection _focusConnection, _focusTextConnection;
+extern bool _openVrDisplayActive;
+static vr::IVROverlay* _overlay { nullptr };
+static QObject* _focusObject { nullptr };
+static QString _existingText;
+static Qt::InputMethodHints _currentHints;
+
+void showOpenVrKeyboard(bool show = true) {
+    if (_overlay) {
+        if (show) {
+            auto offscreenUi = DependencyManager::get<OffscreenUi>();
+            _focusObject = offscreenUi->getWindow()->focusObject();
+
+            QInputMethodQueryEvent query(Qt::ImQueryInput | Qt::ImHints);
+            qApp->sendEvent(_focusObject, &query);
+            _currentHints = Qt::InputMethodHints(query.value(Qt::ImHints).toUInt());
+            vr::EGamepadTextInputMode inputMode = vr::k_EGamepadTextInputModeNormal;
+            if (_currentHints & Qt::ImhHiddenText) {
+                inputMode = vr::k_EGamepadTextInputModePassword;
+            }
+            vr::EGamepadTextInputLineMode lineMode = vr::k_EGamepadTextInputLineModeSingleLine;
+            if (_currentHints & Qt::ImhMultiLine) {
+                lineMode = vr::k_EGamepadTextInputLineModeMultipleLines;
+            }
+            _existingText = query.value(Qt::ImSurroundingText).toString();
+            _overlay->ShowKeyboard(inputMode, lineMode, "Keyboard", 1024, _existingText.toLocal8Bit().toStdString().c_str(), false, (uint64_t)(void*)_focusObject);
+        } else {
+            _focusObject = nullptr;
+            _overlay->HideKeyboard();
+        }
+    }
+}
+
+void finishOpenVrKeyboardInput() {
+    auto offscreenUi = DependencyManager::get<OffscreenUi>();
+    auto chars = _overlay->GetKeyboardText(textArray, 8192);
+    auto newText = QString(QByteArray(textArray, chars));
+    // TODO modify the new text to match the possible input hints:
+    // ImhDigitsOnly  ImhFormattedNumbersOnly  ImhUppercaseOnly  ImhLowercaseOnly 
+    // ImhDialableCharactersOnly ImhEmailCharactersOnly  ImhUrlCharactersOnly  ImhLatinOnly
+    QInputMethodEvent event(_existingText, QList<QInputMethodEvent::Attribute>());
+    event.setCommitString(newText, 0, _existingText.size());
+    qApp->sendEvent(_focusObject, &event);
+    // Simulate an enter press on the top level window to trigger the action
+    if (0 == (_currentHints & Qt::ImhMultiLine)) {
+        qApp->sendEvent(offscreenUi->getWindow(), &QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::KeyboardModifiers(), QString("\n")));
+        qApp->sendEvent(offscreenUi->getWindow(), &QKeyEvent(QEvent::KeyRelease, Qt::Key_Return, Qt::KeyboardModifiers()));
+    }
+}
+
+void enableOpenVrKeyboard() {
+    auto offscreenUi = DependencyManager::get<OffscreenUi>();
+    _overlay = vr::VROverlay();
+
+    _focusConnection = QObject::connect(offscreenUi->getWindow(), &QQuickWindow::focusObjectChanged, [](QObject* object) {
+        if (object != _focusObject && _overlay) {
+            showOpenVrKeyboard(false);
+        }
+    });
+
+    _focusTextConnection = QObject::connect(offscreenUi.data(), &OffscreenUi::focusTextChanged, [](bool focusText) {
+        if (_openVrDisplayActive) {
+            showOpenVrKeyboard(focusText);
+        }
+    });
+}
+
+
+void disableOpenVrKeyboard() {
+    QObject::disconnect(_focusTextConnection);
+    QObject::disconnect(_focusConnection);
+}
+
+
 void handleOpenVrEvents() {
     if (!activeHmd) {
         return;
@@ -107,6 +187,10 @@ void handleOpenVrEvents() {
                 activeHmd->AcknowledgeQuit_Exiting();
                 break;
 
+            case vr::VREvent_KeyboardDone: 
+                finishOpenVrKeyboardInput();
+                break;
+
             default:
                 break;
         }
@@ -114,3 +198,4 @@ void handleOpenVrEvents() {
     }
 
 }
+
diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h
index 1e5914844c..41e7dcb27d 100644
--- a/plugins/openvr/src/OpenVrHelpers.h
+++ b/plugins/openvr/src/OpenVrHelpers.h
@@ -18,6 +18,9 @@ vr::IVRSystem* acquireOpenVrSystem();
 void releaseOpenVrSystem();
 void handleOpenVrEvents();
 bool openVrQuitRequested();
+void enableOpenVrKeyboard();
+void disableOpenVrKeyboard();
+
 
 template<typename F>
 void openvr_for_each_eye(F f) {
diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp
index 8d6c661ae6..6d3ce46e82 100644
--- a/plugins/openvr/src/ViveControllerManager.cpp
+++ b/plugins/openvr/src/ViveControllerManager.cpp
@@ -11,9 +11,6 @@
 
 #include "ViveControllerManager.h"
 
-#include <QtCore/QProcessEnvironment>
-#include <QtQuick/QQuickWindow>
-
 #include <PerfStat.h>
 #include <PathUtils.h>
 #include <GeometryCache.h>
@@ -57,9 +54,6 @@ bool ViveControllerManager::isSupported() const {
     return openVrSupported();
 }
 
-QMetaObject::Connection _focusConnection;
-extern bool _openVrDisplayActive;
-
 bool ViveControllerManager::activate() {
     InputPlugin::activate();
 
@@ -72,20 +66,9 @@ bool ViveControllerManager::activate() {
         _system = acquireOpenVrSystem();
     }
     Q_ASSERT(_system);
-    auto offscreenUi = DependencyManager::get<OffscreenUi>();
-    _focusConnection = connect(offscreenUi.data(), &OffscreenUi::focusTextChanged, [this](bool focusText) {
-        if (_openVrDisplayActive) {
-            auto overlay = vr::VROverlay();
-            if (overlay) {
-                if (focusText) {
-                    //virtual EVROverlayError ShowKeyboard( eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32_t unCharMax, const char *pchExistingText, bool bUseMinimalMode, uint64_t uUserValue) = 0;
-                    overlay->ShowKeyboard(vr::EGamepadTextInputMode::k_EGamepadTextInputModeNormal, vr::k_EGamepadTextInputLineModeSingleLine, "Test", 1024, "", false, 0);
-                } else {
-                    overlay->HideKeyboard();
-                }
-            }
-        }
-    });
+
+    enableOpenVrKeyboard();
+
     // OpenVR provides 3d mesh representations of the controllers
     // Disabled controller rendering code
     /*
@@ -150,7 +133,7 @@ bool ViveControllerManager::activate() {
 void ViveControllerManager::deactivate() {
     InputPlugin::deactivate();
 
-    disconnect(_focusConnection);
+    disableOpenVrKeyboard();
 
     _container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS);
     _container->removeMenu(MENU_PATH);
@@ -240,18 +223,6 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu
         return;
     }
 
-    vr::VREvent_t vrEvent;
-    static char textArray[8192];
-    while (vr::VRSystem()->PollNextEvent(&vrEvent, sizeof(vrEvent))) {
-        if (vrEvent.eventType == vr::VREvent_KeyboardDone) {
-            auto chars = vr::VROverlay()->GetKeyboardText(textArray, 8192);
-            QInputMethodEvent* event = new QInputMethodEvent();
-            event->setCommitString(QString(QByteArray(textArray, chars)), 0, 0);
-            auto focusObject = DependencyManager::get<OffscreenUi>()->getWindow()->focusObject();
-            qApp->postEvent(focusObject, event);
-        }
-    }
-
     // because update mutates the internal state we need to lock
     userInputMapper->withLock([&, this]() {
         _inputDevice->update(deltaTime, inputCalibrationData);