From 5f236bd0252994a2a902bd4dc9c03930de575275 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 14 Jun 2017 16:45:17 -0400 Subject: [PATCH 01/11] simplify device settings --- interface/src/scripting/AudioDevices.cpp | 70 +++++++++++++----------- interface/src/scripting/AudioDevices.h | 3 +- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index 055d59efe3..4abaf70127 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "AudioDevices.h" #include "Application.h" @@ -17,10 +19,24 @@ using namespace scripting; -Setting::Handle inputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }}; -Setting::Handle outputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }}; -Setting::Handle inputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }}; -Setting::Handle outputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; +static Setting::Handle desktopInputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }}; +static Setting::Handle desktopOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }}; +static Setting::Handle hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }}; +static Setting::Handle hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; + +const bool hmdSetting = true; +const bool desktopSetting = false; + +std::map> deviceSettings { + { desktopSetting, + { QAudio::AudioInput, desktopInputDeviceSetting }, + { QAudio::AudioOutput, desktopOutputDeviceSetting } + }, + { hmdSetting, + { QAudio::AudioInput, hmdInputDeviceSetting }, + { QAudio::AudioOutput, hmdOutputDeviceSetting } + } +}; QHash AudioDeviceList::_roles { { Qt::DisplayRole, "display" }, @@ -167,48 +183,38 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { _inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput)); _outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput)); - connect(&_inputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onInputDeviceSelected); - connect(&_outputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onOutputDeviceSelected); + connect(&_inputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device) { + onDeviceSelected(QAudio::AudioInput, device); + }); + connect(&_outputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device) { + onDeviceSelected(QAudio::AudioOutput, device); + }); } void AudioDevices::onContextChanged(const QString& context) { - QString input; - QString output; - if (_contextIsHMD) { - input = inputDeviceHMD.get(); - output = outputDeviceHMD.get(); - } else { - input = inputDeviceDesktop.get(); - output = outputDeviceDesktop.get(); - } + auto input = deviceSettings[_contextIsHMD][QAudio::AudioInput].get(); + auto output = deviceSettings[_contextIsHMD][QAudio::AudioOutput].get(); _inputs.resetDevice(_contextIsHMD, input); _outputs.resetDevice(_contextIsHMD, output); } -void AudioDevices::onInputDeviceSelected(const QAudioDeviceInfo& device) { +void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device) { QString deviceName; if (!device.isNull()) { deviceName = device.deviceName(); } - if (_contextIsHMD) { - inputDeviceHMD.set(deviceName); - } else { - inputDeviceDesktop.set(deviceName); - } -} + auto& setting = deviceSettings[_contextIsHMD][mode]; -void AudioDevices::onOutputDeviceSelected(const QAudioDeviceInfo& device) { - QString deviceName; + // retrieve the prior device + auto lastDeviceName = setting.get(); + + // store the selected device + setting.set(deviceName); + + // TODO: log the selected device if (!device.isNull()) { - deviceName = device.deviceName(); - } - - if (_contextIsHMD) { - outputDeviceHMD.set(deviceName); - } else { - outputDeviceDesktop.set(deviceName); } } @@ -239,4 +245,4 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices); From 837f37220af464cddd3f5ab316fa8eafe134a37b Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 14 Jun 2017 17:17:15 -0400 Subject: [PATCH 02/11] log selected_audio_device --- interface/src/scripting/AudioDevices.cpp | 52 +++++++++++++++++------- interface/src/scripting/AudioDevices.h | 5 ++- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index 4abaf70127..03416f5102 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -17,12 +17,17 @@ #include "AudioClient.h" #include "Audio.h" +#include "UserActivityLogger.h" + using namespace scripting; -static Setting::Handle desktopInputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }}; -static Setting::Handle desktopOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }}; -static Setting::Handle hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }}; -static Setting::Handle hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; +static const INPUT = "INPUT"; +static const OUTPUT= "OUTPUT"; + +static Setting::Handle desktopInputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, INPUT }}; +static Setting::Handle desktopOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, OUTPUT }}; +static Setting::Handle hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, INPUT }}; +static Setting::Handle hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, OUTPUT }}; const bool hmdSetting = true; const bool desktopSetting = false; @@ -82,7 +87,7 @@ bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, i if (success) { device.selected = true; - emit deviceSelected(device.info); + emit deviceSelected(device.info, _selectedDevice); emit deviceChanged(device.info); } } @@ -109,7 +114,7 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { // the selection failed - reset it if (!success) { - emit deviceSelected(QAudioDeviceInfo()); + emit deviceSelected(); } } @@ -183,11 +188,11 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { _inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput)); _outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput)); - connect(&_inputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device) { - onDeviceSelected(QAudio::AudioInput, device); + connect(&_inputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { + onDeviceSelected(QAudio::AudioInput, device, previousDevice); }); - connect(&_outputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device) { - onDeviceSelected(QAudio::AudioOutput, device); + connect(&_outputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { + onDeviceSelected(QAudio::AudioOutput, device, previousDevice); }); } @@ -199,7 +204,7 @@ void AudioDevices::onContextChanged(const QString& context) { _outputs.resetDevice(_contextIsHMD, output); } -void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device) { +void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { QString deviceName; if (!device.isNull()) { deviceName = device.deviceName(); @@ -207,14 +212,33 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d auto& setting = deviceSettings[_contextIsHMD][mode]; - // retrieve the prior device - auto lastDeviceName = setting.get(); + // check for a previous device + auto wasDefault = setting.get().isNull(); // store the selected device setting.set(deviceName); - // TODO: log the selected device + // log the selected device if (!device.isNull()) { + QJsonObject data; + + const QString MODE = "audio_mode"; + data[MODE] = mode == QAudio::AudioInput ? INPUT : OUTPUT; + + const QString CONTEXT = "display_mode"; + data[CONTEXT] = _contextIsHMD ? Audio::HMD : Audio::DESKTOP; + + const QString DISPLAY = "display_device"; + data[DISPLAY] = qApp->getActiveDisplayPlugin()->getName(); + + const QString DEVICE = "device"; + const QString PREVIOUS_DEVICE = "previous_device"; + const QString WAS_DEFAULT = "was_default"; + data[DEVICE] = deviceName; + data[PREVIOUS_DEVICE] = previousDevice.deviceName(); + data[WAS_DEFAULT] = wasDefault; + + UserActivityLogger::getInstance()->logAction("selected_audio_device", data); } } diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 54637d2269..c0f8c18b49 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -43,7 +43,8 @@ public: void resetDevice(bool contextIsHMD, const QString& device); signals: - void deviceSelected(const QAudioDeviceInfo& device); + void deviceSelected(const QAudioDeviceInfo& device = QAudioDeviceInfo(), + const QAudioDeviceInfo& previousDevice = QAudioDeviceInfo()); void deviceChanged(const QAudioDeviceInfo& device); private slots: @@ -76,7 +77,7 @@ signals: private slots: void onContextChanged(const QString& context); - void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device); + void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice); void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); void onDevicesChanged(QAudio::Mode mode, const QList& devices); From 3d807743176684e54cf30fed457904a64cc5fe79 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 19 Jun 2017 15:57:24 -0700 Subject: [PATCH 03/11] clean up select_audio_device --- interface/src/scripting/AudioDevices.cpp | 97 ++++++++++++------------ interface/src/scripting/AudioDevices.h | 2 + 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index 03416f5102..949ce666c5 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -21,27 +21,21 @@ using namespace scripting; -static const INPUT = "INPUT"; -static const OUTPUT= "OUTPUT"; - -static Setting::Handle desktopInputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, INPUT }}; -static Setting::Handle desktopOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, OUTPUT }}; -static Setting::Handle hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, INPUT }}; -static Setting::Handle hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, OUTPUT }}; +static Setting::Handle desktopInputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }}; +static Setting::Handle desktopOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }}; +static Setting::Handle hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }}; +static Setting::Handle hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; const bool hmdSetting = true; const bool desktopSetting = false; -std::map> deviceSettings { - { desktopSetting, - { QAudio::AudioInput, desktopInputDeviceSetting }, - { QAudio::AudioOutput, desktopOutputDeviceSetting } - }, - { hmdSetting, - { QAudio::AudioInput, hmdInputDeviceSetting }, - { QAudio::AudioOutput, hmdOutputDeviceSetting } - } -}; +Setting::Handle& getSetting(bool contextIsHMD, QAudio::Mode mode) { + if (mode == QAudio::AudioInput) { + return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting; + } else { // if (mode == QAudio::AudioOutput) + return contextIsHMD ? hmdOutputDeviceSetting : desktopOutputDeviceSetting; + } +} QHash AudioDeviceList::_roles { { Qt::DisplayRole, "display" }, @@ -64,33 +58,38 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { } bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) { - if (!index.isValid() || index.row() >= _devices.size()) { - return false; - } + if (!index.isValid() || index.row() >= _devices.size() || role != Qt::CheckStateRole) { + return false; + } + // only allow switching to a new device, not deactivating an in-use device + auto selected = value.toBool(); + if (!selected) { + return false; + } + + return setDevice(index.row(), true); +} + +bool AudioDeviceList::setDevice(int row, bool fromUser) { bool success = false; + auto& device = _devices[row]; - if (role == Qt::CheckStateRole) { - auto selected = value.toBool(); - auto& device = _devices[index.row()]; + // skip if already selected + if (!device.selected) { + auto client = DependencyManager::get(); + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(QAudio::Mode, _mode), + Q_ARG(const QAudioDeviceInfo&, device.info)); - // only allow switching to a new device, not deactivating an in-use device - if (selected - // skip if already selected - && selected != device.selected) { - - auto client = DependencyManager::get(); - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, success), - Q_ARG(QAudio::Mode, _mode), - Q_ARG(const QAudioDeviceInfo&, device.info)); - - if (success) { - device.selected = true; - emit deviceSelected(device.info, _selectedDevice); - emit deviceChanged(device.info); - } - } + if (success) { + device.selected = true; + if (fromUser) { + emit deviceSelected(device.info, _selectedDevice); + } + emit deviceChanged(device.info); + } } emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); @@ -109,7 +108,7 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { } } if (i < rowCount()) { - success = setData(createIndex(i, 0), true, Qt::CheckStateRole); + success = setDevice(i, false); } // the selection failed - reset it @@ -197,20 +196,17 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { } void AudioDevices::onContextChanged(const QString& context) { - auto input = deviceSettings[_contextIsHMD][QAudio::AudioInput].get(); - auto output = deviceSettings[_contextIsHMD][QAudio::AudioOutput].get(); + auto input = getSetting(_contextIsHMD, QAudio::AudioInput).get(); + auto output = getSetting(_contextIsHMD, QAudio::AudioOutput).get(); _inputs.resetDevice(_contextIsHMD, input); _outputs.resetDevice(_contextIsHMD, output); } void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { - QString deviceName; - if (!device.isNull()) { - deviceName = device.deviceName(); - } + QString deviceName = device.isNull() ? QString() : device.deviceName(); - auto& setting = deviceSettings[_contextIsHMD][mode]; + auto& setting = getSetting(_contextIsHMD, mode); // check for a previous device auto wasDefault = setting.get().isNull(); @@ -223,7 +219,8 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d QJsonObject data; const QString MODE = "audio_mode"; - data[MODE] = mode == QAudio::AudioInput ? INPUT : OUTPUT; + const QString INPUT = "INPUT"; + const QString OUTPUT = "OUTPUT"; data[MODE] = mode == QAudio::AudioInput ? INPUT : OUTPUT; const QString CONTEXT = "display_mode"; data[CONTEXT] = _contextIsHMD ? Audio::HMD : Audio::DESKTOP; @@ -238,7 +235,7 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d data[PREVIOUS_DEVICE] = previousDevice.deviceName(); data[WAS_DEFAULT] = wasDefault; - UserActivityLogger::getInstance()->logAction("selected_audio_device", data); + UserActivityLogger::getInstance().logAction("selected_audio_device", data); } } diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index c0f8c18b49..36d887d3d4 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -54,6 +54,8 @@ private slots: private: friend class AudioDevices; + bool setDevice(int index, bool fromUser); + static QHash _roles; static Qt::ItemFlags _flags; From 4bff925506b2f164b49828e470a86f3c440b5335 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 19 Jun 2017 17:01:39 -0700 Subject: [PATCH 04/11] rm unused bools --- interface/src/scripting/AudioDevices.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index 949ce666c5..4e88232ce1 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -26,9 +26,6 @@ static Setting::Handle desktopOutputDeviceSetting { QStringList { Audio static Setting::Handle hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }}; static Setting::Handle hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; -const bool hmdSetting = true; -const bool desktopSetting = false; - Setting::Handle& getSetting(bool contextIsHMD, QAudio::Mode mode) { if (mode == QAudio::AudioInput) { return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting; From ff5b37c785dbd2e495a9f1f08985be6f69a34528 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 22 Jun 2017 14:09:05 -0700 Subject: [PATCH 05/11] new --scripts switch overrides default scripts location --- interface/src/Application.cpp | 17 +++++++- interface/src/Menu.cpp | 4 +- libraries/script-engine/src/ScriptEngine.cpp | 40 +++++++++---------- libraries/script-engine/src/ScriptEngines.cpp | 21 ++++++---- libraries/script-engine/src/ScriptEngines.h | 3 ++ libraries/script-engine/src/ScriptsModel.cpp | 10 ++--- libraries/shared/src/PathUtils.cpp | 34 +++++++++++----- libraries/shared/src/PathUtils.h | 5 +-- 8 files changed, 85 insertions(+), 49 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e12c73d0bc..a7bed9fc9c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -903,6 +903,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _saveAvatarOverrideUrl = true; } + QString defaultScriptsLocation = getCmdOption(argc, constArgv, "--scripts"); + if (!defaultScriptsLocation.isEmpty()) { + PathUtils::defaultScriptsLocation(defaultScriptsLocation); + } + _glWidget = new GLCanvas(); getApplicationCompositor().setRenderingWidget(_glWidget); _window->setCentralWidget(_glWidget); @@ -1167,7 +1172,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // force the model the look at the correct directory (weird order of operations issue) scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation()); // do this as late as possible so that all required subsystems are initialized - scriptEngines->loadScripts(); + // If we've overridden the default scripts location, just load default scripts + // otherwise, load 'em all + if (!defaultScriptsLocation.isEmpty()) { + scriptEngines->loadDefaultScripts(); + scriptEngines->defaultScriptsLocationOverridden(true); + } else { + scriptEngines->loadScripts(); + } + // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -5855,7 +5868,7 @@ void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const void Application::showScriptLogs() { auto scriptEngines = DependencyManager::get(); - QUrl defaultScriptsLoc = defaultScriptsLocation(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js"); scriptEngines->loadScript(defaultScriptsLoc.toString()); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8c9baa7c43..3fab5d3d37 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -300,7 +300,7 @@ Menu::Menu() { // Settings > Avatar... action = addActionToQMenuAndActionHash(settingsMenu, "Avatar..."); connect(action, &QAction::triggered, [] { - qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"), + qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"), QString("../../hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog"); }); @@ -626,7 +626,7 @@ Menu::Menu() { action = addActionToQMenuAndActionHash(audioDebugMenu, "Stats..."); connect(action, &QAction::triggered, [] { auto scriptEngines = DependencyManager::get(); - QUrl defaultScriptsLoc = defaultScriptsLocation(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/audio/stats.js"); scriptEngines->loadScript(defaultScriptsLoc.toString()); }); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 67b16df1ce..096b3aecfa 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -172,7 +172,7 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const emit unhandledException(exception); } }, Qt::DirectConnection); - + setProcessEventsInterval(MSECS_PER_SECOND); if (isEntityServerScript()) { qCDebug(scriptengine) << "isEntityServerScript() -- limiting maxRetries to 1"; @@ -281,7 +281,7 @@ void ScriptEngine::runDebuggable() { scriptDebugMenu = nullptr; } } - disconnect(timer); + disconnect(timer); }); connect(timer, &QTimer::timeout, [this, timer] { @@ -340,7 +340,7 @@ void ScriptEngine::runInThread() { QThread* workerThread = new QThread(); workerThread->setObjectName(QString("js:") + getFilename().replace("about:","")); moveToThread(workerThread); - + // NOTE: If you connect any essential signals for proper shutdown or cleanup of // the script engine, make sure to add code to "reconnect" them to the // disconnectNonEssentialSignals() method @@ -1047,26 +1047,26 @@ void ScriptEngine::run() { auto beforeSleep = clock::now(); // Throttle to SCRIPT_FPS - // We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will + // We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will // calculate a sleepUntil to be the time from our start time until the original target - // sleepUntil for this frame. This approach will allow us to "catch up" in the event - // that some of our script udpates/frames take a little bit longer than the target average + // sleepUntil for this frame. This approach will allow us to "catch up" in the event + // that some of our script udpates/frames take a little bit longer than the target average // to execute. // NOTE: if we go to variable SCRIPT_FPS, then we will need to reconsider this approach const std::chrono::microseconds TARGET_SCRIPT_FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1); clock::time_point targetSleepUntil(startTime + (thisFrame++ * TARGET_SCRIPT_FRAME_DURATION)); - // However, if our sleepUntil is not at least our average update and timer execution time - // into the future it means our script is taking too long in its updates, and we want to - // punish the script a little bit. So we will force the sleepUntil to be at least our + // However, if our sleepUntil is not at least our average update and timer execution time + // into the future it means our script is taking too long in its updates, and we want to + // punish the script a little bit. So we will force the sleepUntil to be at least our // averageUpdate + averageTimerPerFrame time into the future. auto averageUpdate = totalUpdates / thisFrame; auto averageTimerPerFrame = _totalTimerExecution / thisFrame; auto averageTimerAndUpdate = averageUpdate + averageTimerPerFrame; auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageTimerAndUpdate); - // We don't want to actually sleep for too long, because it causes our scripts to hang - // on shutdown and stop... so we want to loop and sleep until we've spent our time in + // We don't want to actually sleep for too long, because it causes our scripts to hang + // on shutdown and stop... so we want to loop and sleep until we've spent our time in // purgatory, constantly checking to see if our script was asked to end bool processedEvents = false; while (!_isFinished && clock::now() < sleepUntil) { @@ -1399,7 +1399,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return QString(); } - QUrl defaultScriptsLoc = defaultScriptsLocation(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); QUrl url(moduleId); auto displayId = moduleId; @@ -1465,7 +1465,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re canonical.setPath(file.canonicalFilePath()); } - bool disallowOutsideFiles = !defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile(); + bool disallowOutsideFiles = !PathUtils::defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile(); if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) { return throwResolveError(makeError(message.arg( QString("path '%1' outside of origin script '%2' '%3'") @@ -1750,7 +1750,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac return; } if (DependencyManager::get()->isStopped()) { - scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:" + scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:" + includeFiles.join(",") + "parent script:" + getFilename()); return; // bail early } @@ -1762,7 +1762,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac bool isStandardLibrary = false; if (file.startsWith("/~/")) { thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file))); - QUrl defaultScriptsLoc = defaultScriptsLocation(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); if (!defaultScriptsLoc.isParentOf(thisURL)) { scriptWarningMessage("Script.include() -- skipping" + file + "-- outside of standard libraries"); continue; @@ -1774,7 +1774,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac bool disallowOutsideFiles = thisURL.isLocalFile() && !isStandardLibrary && !currentSandboxURL.isLocalFile(); if (disallowOutsideFiles && !PathUtils::isDescendantOf(thisURL, currentSandboxURL)) { - scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString() + scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString() + "outside of original entity script" + currentSandboxURL.toString()); } else { // We could also check here for CORS, but we don't yet. @@ -1844,7 +1844,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { if (DependencyManager::get()->isStopped()) { - scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:" + scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:" + includeFile + "parent script:" + getFilename()); return; // bail early } @@ -1862,12 +1862,12 @@ void ScriptEngine::load(const QString& loadFile) { return; } if (DependencyManager::get()->isStopped()) { - scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:" + scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:" + loadFile + "parent script:" + getFilename()); return; // bail early } if (!currentEntityIdentifier.isInvalidID()) { - scriptWarningMessage("Script.load() from entity script is ignored... loadFile:" + scriptWarningMessage("Script.load() from entity script is ignored... loadFile:" + loadFile + "parent script:" + getFilename() + "entity: " + currentEntityIdentifier.toString()); return; // bail early } @@ -2548,7 +2548,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; #endif - + if (HIFI_AUTOREFRESH_FILE_SCRIPTS) { refreshFileScript(entityID); } diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 69de067d10..9200550ff2 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -74,7 +74,7 @@ ScriptEngines::ScriptEngines(ScriptEngine::Context context) QUrl normalizeScriptURL(const QUrl& rawScriptURL) { if (rawScriptURL.scheme() == "file") { QUrl fullNormal = rawScriptURL; - QUrl defaultScriptLoc = defaultScriptsLocation(); + QUrl defaultScriptLoc = PathUtils::defaultScriptsLocation(); // if this url is something "beneath" the default script url, replace the local path with ~ if (fullNormal.scheme() == defaultScriptLoc.scheme() && @@ -93,7 +93,7 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) { QString expandScriptPath(const QString& rawPath) { QStringList splitPath = rawPath.split("/"); - QUrl defaultScriptsLoc = defaultScriptsLocation(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); return defaultScriptsLoc.path() + "/" + splitPath.mid(2).join("/"); // 2 to skip the slashes in /~/ } @@ -112,7 +112,7 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { QFileInfo fileInfo(url.toLocalFile()); url = QUrl::fromLocalFile(fileInfo.canonicalFilePath()); - QUrl defaultScriptsLoc = defaultScriptsLocation(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); if (!defaultScriptsLoc.isParentOf(url)) { qCWarning(scriptengine) << "Script.include() ignoring file path" << rawScriptURL << "-- outside of standard libraries: " @@ -327,6 +327,13 @@ void ScriptEngines::saveScripts() { return; } + // don't save scripts if we started with --scripts, as we would overwrite + // the scripts that the user expects to be there when launched without the + // --scripts override. + if (_defaultScriptsLocationOverridden) { + return; + } + // Saves all currently running user-loaded scripts QVariantList list; @@ -533,11 +540,11 @@ void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) { initializer(scriptEngine); } - // FIXME disabling 'shift key' debugging for now. If you start up the application with - // the shift key held down, it triggers a deadlock because of script interfaces running + // FIXME disabling 'shift key' debugging for now. If you start up the application with + // the shift key held down, it triggers a deadlock because of script interfaces running // on the main thread auto const wantDebug = scriptEngine->isDebuggable(); // || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier); - + if (HIFI_SCRIPT_DEBUGGABLES && wantDebug) { scriptEngine->runDebuggable(); } else { @@ -573,5 +580,5 @@ void ScriptEngines::onScriptEngineError(const QString& scriptFilename) { } QString ScriptEngines::getDefaultScriptsLocation() const { - return defaultScriptsLocation().toString(); + return PathUtils::defaultScriptsLocation().toString(); } diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 91dc54a0ec..fbd5f370cf 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -66,6 +66,8 @@ public: Q_PROPERTY(QString defaultScriptsPath READ getDefaultScriptsLocation) + void defaultScriptsLocationOverridden(bool overridden) { _defaultScriptsLocationOverridden = overridden; }; + // Called at shutdown time void shutdownScripting(); bool isStopped() const { return _isStopped; } @@ -112,6 +114,7 @@ protected: ScriptsModel _scriptsModel; ScriptsModelFilter _scriptsModelFilter; std::atomic _isStopped { false }; + bool _defaultScriptsLocationOverridden { false }; }; QUrl normalizeScriptURL(const QUrl& rawScriptURL); diff --git a/libraries/script-engine/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp index 31f3a96fbd..9b7a367773 100644 --- a/libraries/script-engine/src/ScriptsModel.cpp +++ b/libraries/script-engine/src/ScriptsModel.cpp @@ -125,15 +125,15 @@ int ScriptsModel::columnCount(const QModelIndex& parent) const { void ScriptsModel::updateScriptsLocation(const QString& newPath) { _fsWatcher.removePath(_localDirectory.absolutePath()); - + if (!newPath.isEmpty()) { _localDirectory.setPath(newPath); - + if (!_localDirectory.absolutePath().isEmpty()) { _fsWatcher.addPath(_localDirectory.absolutePath()); } } - + reloadLocalFiles(); } @@ -154,7 +154,7 @@ void ScriptsModel::reloadDefaultFiles() { } void ScriptsModel::requestDefaultFiles(QString marker) { - QUrl url(defaultScriptsLocation()); + QUrl url(PathUtils::defaultScriptsLocation()); // targets that don't have a scripts folder in the appropriate location will have an empty URL here if (!url.isEmpty()) { @@ -244,7 +244,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) { lastKey = xml.text().toString(); if (jsRegex.exactMatch(xml.text().toString())) { QString localPath = lastKey.split("/").mid(1).join("/"); - QUrl fullPath = defaultScriptsLocation(); + QUrl fullPath = PathUtils::defaultScriptsLocation(); fullPath.setPath(fullPath.path() + lastKey); const QString fullPathStr = normalizeScriptURL(fullPath).toString(); _treeNodes.append(new TreeNodeScript(localPath, fullPathStr, SCRIPT_ORIGIN_DEFAULT)); diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 6e3acc5e99..d1a8ff757b 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -20,6 +20,8 @@ #include #include // std::once +//QUrl PathUtils::_overriddenDefaultScriptsLocation = QUrl(); + const QString& PathUtils::resourcesPath() { #ifdef Q_OS_MAC static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; @@ -71,21 +73,33 @@ QString findMostRecentFileExtension(const QString& originalFileName, QVector - #include "DependencyManager.h" /**jsdoc @@ -38,11 +37,11 @@ public: static QString stripFilename(const QUrl& url); // note: this is FS-case-sensitive version of parentURL.isParentOf(childURL) static bool isDescendantOf(const QUrl& childURL, const QUrl& parentURL); + static QUrl defaultScriptsLocation(const QString& newDefault = ""); + }; QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions); QString findMostRecentFileExtension(const QString& originalFileName, QVector possibleExtensions); -QUrl defaultScriptsLocation(); - #endif // hifi_PathUtils_h From 4439de240a0297c42256ac2d61ab5ae37a677032 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Jun 2017 14:46:24 -0700 Subject: [PATCH 06/11] Trying to fix QtQml/QtScript crashes --- CMakeGraphvizOptions.cmake | 3 +- interface/src/Application.cpp | 18 +- interface/src/Menu.cpp | 11 +- interface/src/scripting/QmlWrapper.h | 63 --- .../scripting/ToolbarScriptingInterface.cpp | 104 ----- .../src/scripting/ToolbarScriptingInterface.h | 26 -- interface/src/ui/DialogsManager.cpp | 2 +- interface/src/ui/LoginDialog.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 11 +- libraries/entities-renderer/CMakeLists.txt | 2 +- .../src/RenderableWebEntityItem.cpp | 25 +- .../src/RenderableWebEntityItem.h | 5 +- libraries/networking/CMakeLists.txt | 2 +- libraries/script-engine/CMakeLists.txt | 2 +- .../script-engine/src/MenuItemProperties.h | 6 +- libraries/script-engine/src/ScriptEngine.cpp | 6 +- libraries/script-engine/src/ScriptEngines.cpp | 3 - libraries/ui/CMakeLists.txt | 9 +- libraries/ui/src/OffscreenUi.cpp | 3 +- libraries/ui/src/OffscreenUi.h | 2 +- .../gl => ui/src/ui}/OffscreenQmlSurface.cpp | 44 ++- .../gl => ui/src/ui}/OffscreenQmlSurface.h | 0 .../src/ui}/OffscreenQmlSurfaceCache.cpp | 0 .../src/ui}/OffscreenQmlSurfaceCache.h | 0 libraries/ui/src/ui/QmlWrapper.cpp | 59 +++ libraries/ui/src/ui/QmlWrapper.h | 44 +++ .../src/ui}/TabletScriptingInterface.cpp | 363 +++++++++++------- .../src/ui}/TabletScriptingInterface.h | 44 ++- .../ui/src/ui/ToolbarScriptingInterface.cpp | 124 ++++++ .../ui/src/ui/ToolbarScriptingInterface.h | 56 +++ .../src/ui/types}/FileTypeProfile.cpp | 0 .../src => ui/src/ui/types}/FileTypeProfile.h | 0 .../ui/types}/FileTypeRequestInterceptor.cpp | 0 .../ui/types}/FileTypeRequestInterceptor.h | 0 .../ui/types}/HFTabletWebEngineProfile.cpp | 0 .../src/ui/types}/HFTabletWebEngineProfile.h | 0 .../HFTabletWebEngineRequestInterceptor.cpp | 0 .../HFTabletWebEngineRequestInterceptor.h | 0 .../src/ui/types}/HFWebEngineProfile.cpp | 0 .../src/ui/types}/HFWebEngineProfile.h | 0 .../types}/HFWebEngineRequestInterceptor.cpp | 0 .../ui/types}/HFWebEngineRequestInterceptor.h | 0 .../src/ui/types}/RequestFilters.cpp | 0 .../src => ui/src/ui/types}/RequestFilters.h | 0 .../src => ui/src/ui/types}/SoundEffect.cpp | 2 - .../src => ui/src/ui/types}/SoundEffect.h | 0 46 files changed, 618 insertions(+), 423 deletions(-) delete mode 100644 interface/src/scripting/QmlWrapper.h delete mode 100644 interface/src/scripting/ToolbarScriptingInterface.cpp delete mode 100644 interface/src/scripting/ToolbarScriptingInterface.h rename libraries/{gl/src/gl => ui/src/ui}/OffscreenQmlSurface.cpp (97%) rename libraries/{gl/src/gl => ui/src/ui}/OffscreenQmlSurface.h (100%) rename libraries/{gl/src/gl => ui/src/ui}/OffscreenQmlSurfaceCache.cpp (100%) rename libraries/{gl/src/gl => ui/src/ui}/OffscreenQmlSurfaceCache.h (100%) create mode 100644 libraries/ui/src/ui/QmlWrapper.cpp create mode 100644 libraries/ui/src/ui/QmlWrapper.h rename libraries/{script-engine/src => ui/src/ui}/TabletScriptingInterface.cpp (69%) rename libraries/{script-engine/src => ui/src/ui}/TabletScriptingInterface.h (90%) create mode 100644 libraries/ui/src/ui/ToolbarScriptingInterface.cpp create mode 100644 libraries/ui/src/ui/ToolbarScriptingInterface.h rename libraries/{networking/src => ui/src/ui/types}/FileTypeProfile.cpp (100%) rename libraries/{networking/src => ui/src/ui/types}/FileTypeProfile.h (100%) rename libraries/{networking/src => ui/src/ui/types}/FileTypeRequestInterceptor.cpp (100%) rename libraries/{networking/src => ui/src/ui/types}/FileTypeRequestInterceptor.h (100%) rename libraries/{networking/src => ui/src/ui/types}/HFTabletWebEngineProfile.cpp (100%) rename libraries/{networking/src => ui/src/ui/types}/HFTabletWebEngineProfile.h (100%) rename libraries/{networking/src => ui/src/ui/types}/HFTabletWebEngineRequestInterceptor.cpp (100%) rename libraries/{networking/src => ui/src/ui/types}/HFTabletWebEngineRequestInterceptor.h (100%) rename libraries/{networking/src => ui/src/ui/types}/HFWebEngineProfile.cpp (100%) rename libraries/{networking/src => ui/src/ui/types}/HFWebEngineProfile.h (100%) rename libraries/{networking/src => ui/src/ui/types}/HFWebEngineRequestInterceptor.cpp (100%) rename libraries/{networking/src => ui/src/ui/types}/HFWebEngineRequestInterceptor.h (100%) rename libraries/{networking/src => ui/src/ui/types}/RequestFilters.cpp (100%) rename libraries/{networking/src => ui/src/ui/types}/RequestFilters.h (100%) rename libraries/{script-engine/src => ui/src/ui/types}/SoundEffect.cpp (97%) rename libraries/{script-engine/src => ui/src/ui/types}/SoundEffect.h (100%) diff --git a/CMakeGraphvizOptions.cmake b/CMakeGraphvizOptions.cmake index f1b73f1cae..3b23172a79 100644 --- a/CMakeGraphvizOptions.cmake +++ b/CMakeGraphvizOptions.cmake @@ -1 +1,2 @@ -set(GRAPHVIZ_EXTERNAL_LIBS FALSE) \ No newline at end of file +set(GRAPHVIZ_EXTERNAL_LIBS FALSE) +set(GRAPHVIZ_IGNORE_TARGETS "shared;networking") \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e12c73d0bc..ccf81ace3f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -99,8 +99,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -125,7 +125,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -166,7 +167,6 @@ #include "scripting/SettingsScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" -#include "scripting/ToolbarScriptingInterface.h" #include "scripting/RatesScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" @@ -1792,6 +1792,8 @@ void Application::cleanupBeforeQuit() { #endif // stop QML + DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); @@ -5535,8 +5537,16 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("OffscreenFlags", DependencyManager::get()->getFlags()); scriptEngine->registerGlobalObject("Desktop", DependencyManager::get().data()); + + qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue, wrapperFromScriptValue); + qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue, wrapperFromScriptValue); scriptEngine->registerGlobalObject("Toolbars", DependencyManager::get().data()); + qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue, wrapperFromScriptValue); + qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue, wrapperFromScriptValue); + scriptEngine->registerGlobalObject("Tablet", DependencyManager::get().data()); + + DependencyManager::get().data()->setToolbarScriptingInterface(DependencyManager::get().data()); scriptEngine->registerGlobalObject("Window", DependencyManager::get().data()); @@ -5610,7 +5620,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); - qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); // connect this script engines printedMessage signal to the global ScriptEngines these various messages @@ -5619,6 +5628,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get().data(), &ScriptEngines::onWarningMessage); connect(scriptEngine, &ScriptEngine::infoMessage, DependencyManager::get().data(), &ScriptEngines::onInfoMessage); connect(scriptEngine, &ScriptEngine::clearDebugWindow, DependencyManager::get().data(), &ScriptEngines::onClearDebugWindow); + } bool Application::canAcceptURL(const QString& urlString) const { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8c9baa7c43..5fbae75a57 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -121,9 +121,14 @@ Menu::Menu() { QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); // Edit > Reload All Scripts... [advanced] - addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R, - scriptEngines.data(), SLOT(reloadAllScripts()), + action = addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R, + nullptr, nullptr, QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); + connect(action, &QAction::triggered, [] { + DependencyManager::get()->reloadAllScripts(); + DependencyManager::get()->clearCache(); + }); + // Edit > Console... [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J, diff --git a/interface/src/scripting/QmlWrapper.h b/interface/src/scripting/QmlWrapper.h deleted file mode 100644 index 7dd319e445..0000000000 --- a/interface/src/scripting/QmlWrapper.h +++ /dev/null @@ -1,63 +0,0 @@ -// -// Created by Anthony J. Thibault on 2016-12-12 -// Copyright 2013-2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_QmlWrapper_h -#define hifi_QmlWrapper_h - -#include -#include -#include - -class QmlWrapper : public QObject { - Q_OBJECT -public: - QmlWrapper(QObject* qmlObject, QObject* parent = nullptr) - : QObject(parent), _qmlObject(qmlObject) { - } - - Q_INVOKABLE void writeProperty(QString propertyName, QVariant propertyValue) { - auto offscreenUi = DependencyManager::get(); - offscreenUi->executeOnUiThread([=] { - _qmlObject->setProperty(propertyName.toStdString().c_str(), propertyValue); - }); - } - - Q_INVOKABLE void writeProperties(QVariant propertyMap) { - auto offscreenUi = DependencyManager::get(); - offscreenUi->executeOnUiThread([=] { - QVariantMap map = propertyMap.toMap(); - for (const QString& key : map.keys()) { - _qmlObject->setProperty(key.toStdString().c_str(), map[key]); - } - }); - } - - Q_INVOKABLE QVariant readProperty(const QString& propertyName) { - auto offscreenUi = DependencyManager::get(); - return offscreenUi->returnFromUiThread([&]()->QVariant { - return _qmlObject->property(propertyName.toStdString().c_str()); - }); - } - - Q_INVOKABLE QVariant readProperties(const QVariant& propertyList) { - auto offscreenUi = DependencyManager::get(); - return offscreenUi->returnFromUiThread([&]()->QVariant { - QVariantMap result; - for (const QVariant& property : propertyList.toList()) { - QString propertyString = property.toString(); - result.insert(propertyString, _qmlObject->property(propertyString.toStdString().c_str())); - } - return result; - }); - } - -protected: - QObject* _qmlObject{ nullptr }; -}; - -#endif \ No newline at end of file diff --git a/interface/src/scripting/ToolbarScriptingInterface.cpp b/interface/src/scripting/ToolbarScriptingInterface.cpp deleted file mode 100644 index 2b4f64f35e..0000000000 --- a/interface/src/scripting/ToolbarScriptingInterface.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016-06-16 -// Copyright 2013-2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ToolbarScriptingInterface.h" - - -#include - -#include -#include "QmlWrapper.h" - -class ToolbarButtonProxy : public QmlWrapper { - Q_OBJECT - -public: - ToolbarButtonProxy(QObject* qmlObject, QObject* parent = nullptr) : QmlWrapper(qmlObject, parent) { - std::lock_guard guard(_mutex); - _qmlButton = qobject_cast(qmlObject); - connect(qmlObject, SIGNAL(clicked()), this, SIGNAL(clicked())); - } - - Q_INVOKABLE void editProperties(QVariantMap properties) { - std::lock_guard guard(_mutex); - QVariantMap::const_iterator iter = properties.constBegin(); - while (iter != properties.constEnd()) { - _properties[iter.key()] = iter.value(); - if (_qmlButton) { - // [01/25 14:26:20] [WARNING] [default] QMetaObject::invokeMethod: No such method ToolbarButton_QMLTYPE_195::changeProperty(QVariant,QVariant) - - QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection, - Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value())); - } - ++iter; - } - } - -signals: - void clicked(); - -protected: - mutable std::mutex _mutex; - QQuickItem* _qmlButton { nullptr }; - QVariantMap _properties; -}; - -class ToolbarProxy : public QmlWrapper { - Q_OBJECT - -public: - ToolbarProxy(QObject* qmlObject, QObject* parent = nullptr) : QmlWrapper(qmlObject, parent) { } - - Q_INVOKABLE QObject* addButton(const QVariant& properties) { - QVariant resultVar; - Qt::ConnectionType connectionType = Qt::AutoConnection; - if (QThread::currentThread() != _qmlObject->thread()) { - connectionType = Qt::BlockingQueuedConnection; - } - bool invokeResult = QMetaObject::invokeMethod(_qmlObject, "addButton", connectionType, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, properties)); - if (!invokeResult) { - return nullptr; - } - - QObject* rawButton = qvariant_cast(resultVar); - if (!rawButton) { - return nullptr; - } - - return new ToolbarButtonProxy(rawButton, this); - } - - Q_INVOKABLE void removeButton(const QVariant& name) { - QMetaObject::invokeMethod(_qmlObject, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, name)); - } -}; - - -QObject* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) { - auto offscreenUi = DependencyManager::get(); - auto desktop = offscreenUi->getDesktop(); - Qt::ConnectionType connectionType = Qt::AutoConnection; - if (QThread::currentThread() != desktop->thread()) { - connectionType = Qt::BlockingQueuedConnection; - } - QVariant resultVar; - bool invokeResult = QMetaObject::invokeMethod(desktop, "getToolbar", connectionType, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, toolbarId)); - if (!invokeResult) { - return nullptr; - } - - QObject* rawToolbar = qvariant_cast(resultVar); - if (!rawToolbar) { - return nullptr; - } - - return new ToolbarProxy(rawToolbar); -} - - -#include "ToolbarScriptingInterface.moc" diff --git a/interface/src/scripting/ToolbarScriptingInterface.h b/interface/src/scripting/ToolbarScriptingInterface.h deleted file mode 100644 index 9379284e55..0000000000 --- a/interface/src/scripting/ToolbarScriptingInterface.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016-06-16 -// Copyright 2013-2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ToolbarScriptingInterface_h -#define hifi_ToolbarScriptingInterface_h - -#include - -#include - -#include - -class ToolbarProxy; - -class ToolbarScriptingInterface : public QObject, public Dependency { - Q_OBJECT -public: - Q_INVOKABLE QObject* getToolbar(const QString& toolbarId); -}; - -#endif // hifi_ToolbarScriptingInterface_h diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 96b5da3a55..f216bb4edc 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "AddressBarDialog.h" #include "ConnectionFailureDialog.h" @@ -28,7 +29,6 @@ #include "PreferencesDialog.h" #include "UpdateDialog.h" -#include "TabletScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" static const QVariant TABLET_ADDRESS_DIALOG = "TabletAddressDialog.qml"; diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 10783afd23..c9341fd3d9 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -19,13 +19,13 @@ #include #include #include +#include #include "AccountManager.h" #include "DependencyManager.h" #include "Menu.h" #include "Application.h" -#include "TabletScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" HIFI_QML_DEF(LoginDialog) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index b67ca06aa3..80f11fa552 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -25,16 +25,15 @@ #include #include #include -#include +#include +#include +#include #include #include -#include #include #include #include #include -#include -#include #include #include "scripting/AccountScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" @@ -87,7 +86,7 @@ Web3DOverlay::~Web3DOverlay() { if (rootItem && rootItem->objectName() == "tabletRoot") { auto tabletScriptingInterface = DependencyManager::get(); - tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr, nullptr); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr); } // Fix for crash in QtWebEngineCore when rapidly switching domains @@ -206,7 +205,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); - tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data()); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); // mark the TabletProxy object as cpp ownership. QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"); diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 3df33b6a5d..40111e257b 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME entities-renderer) AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image) +link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image ui) include_hifi_library_headers(networking) include_hifi_library_headers(gl) include_hifi_library_headers(ktx) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 948a219831..9a898b4071 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" @@ -74,19 +74,6 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer qWarning() << "Too many concurrent web views to create new view"; return false; } - QString javaScriptToInject; - QFile webChannelFile(":qtwebchannel/qwebchannel.js"); - QFile createGlobalEventBridgeFile(PathUtils::resourcesPath() + "/html/createGlobalEventBridge.js"); - if (webChannelFile.open(QFile::ReadOnly | QFile::Text) && - createGlobalEventBridgeFile.open(QFile::ReadOnly | QFile::Text)) { - QString webChannelStr = QTextStream(&webChannelFile).readAll(); - QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll(); - - // concatenate these js files - _javaScriptToInject = webChannelStr + createGlobalEventBridgeStr; - } else { - qCWarning(entitiesrenderer) << "unable to find qwebchannel.js or createGlobalEventBridge.js"; - } // Save the original GL context, because creating a QML surface will create a new context QOpenGLContext* currentContext = QOpenGLContext::currentContext(); @@ -266,10 +253,7 @@ void RenderableWebEntityItem::loadSourceURL() { _webSurface->setMaxFps(DEFAULT_MAX_FPS); } - _webSurface->load("WebEntityView.qml", [&](QQmlContext* context, QObject* obj) { - context->setContextProperty("eventBridgeJavaScriptToInject", QVariant(_javaScriptToInject)); - }); - + _webSurface->load("WebEntityView.qml"); _webSurface->getRootItem()->setProperty("url", _sourceUrl); _webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant()); @@ -280,8 +264,7 @@ void RenderableWebEntityItem::loadSourceURL() { if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") { auto tabletScriptingInterface = DependencyManager::get(); - tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", - _webSurface->getRootItem(), _webSurface.data()); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); } } _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition())); @@ -386,7 +369,7 @@ void RenderableWebEntityItem::destroyWebSurface() { if (rootItem && rootItem->objectName() == "tabletRoot") { auto tabletScriptingInterface = DependencyManager::get(); - tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr, nullptr); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr); } // Fix for crash in QtWebEngineCore when rapidly switching domains diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index bbd59a03d6..0f5d307766 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -13,10 +13,9 @@ #include #include #include -#include +#include #include -#include #include "RenderableEntityItem.h" @@ -74,8 +73,6 @@ private: QMetaObject::Connection _mouseMoveConnection; QMetaObject::Connection _hoverLeaveConnection; - QString _javaScriptToInject; - enum contentType { htmlContent, qmlContent diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index ff9a05b959..288e98d5a5 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME networking) -setup_hifi_library(Network WebEngine) +setup_hifi_library(Network) link_hifi_libraries(shared) target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes") diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 39338fd767..0c0004d132 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -16,6 +16,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree gpu ui procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image) +link_hifi_libraries(shared networking octree gpu procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/libraries/script-engine/src/MenuItemProperties.h b/libraries/script-engine/src/MenuItemProperties.h index 9b95fc66e1..9de4337838 100644 --- a/libraries/script-engine/src/MenuItemProperties.h +++ b/libraries/script-engine/src/MenuItemProperties.h @@ -14,7 +14,6 @@ #include -#include #include "KeyEvent.h" @@ -35,7 +34,7 @@ public: QKeySequence shortcutKeySequence; // this is what we actually use, it's set from one of the above // location related items: in order of priority - int position { ui::Menu::UNSPECIFIED_POSITION }; + int position { UNSPECIFIED_POSITION }; QString beforeItem; QString afterItem; @@ -45,6 +44,9 @@ public: bool isSeparator { false }; QString grouping; /// Either: "", "Advanced", or "Developer" + +private: + static const int UNSPECIFIED_POSITION = -1; }; Q_DECLARE_METATYPE(MenuItemProperties) QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuItemProperties& props); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 67b16df1ce..e41d41ee09 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include @@ -48,7 +50,6 @@ #include #include #include -#include #include #include @@ -70,7 +71,6 @@ #include "WebSocketClass.h" #include "RecordingScriptingInterface.h" #include "ScriptEngines.h" -#include "TabletScriptingInterface.h" #include "ModelScriptingInterface.h" @@ -697,8 +697,6 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); - registerGlobalObject("Tablet", DependencyManager::get().data()); - qScriptRegisterMetaType(this, tabletToScriptValue, tabletFromScriptValue); registerGlobalObject("Assets", &_assetScriptingInterface); registerGlobalObject("Resources", DependencyManager::get().data()); diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 69de067d10..4f134d8a22 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -16,8 +16,6 @@ #include #include -#include - #include "ScriptEngine.h" #include "ScriptEngineLogging.h" @@ -431,7 +429,6 @@ void ScriptEngines::setScriptsLocation(const QString& scriptsLocation) { void ScriptEngines::reloadAllScripts() { qCDebug(scriptengine) << "reloadAllScripts -- clearing caches"; DependencyManager::get()->clearCache(); - DependencyManager::get()->clearCache(); qCDebug(scriptengine) << "reloadAllScripts -- stopping all scripts"; stopAllScripts(true); } diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index f2b48446fe..68a6fd25b9 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,3 +1,8 @@ set(TARGET_NAME ui) -setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebSockets XmlPatterns) -link_hifi_libraries(shared networking gl script-engine) +setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebEngine WebSockets XmlPatterns) +link_hifi_libraries(shared networking gl audio) + +if (NOT ANDROID) + # Required for some low level GL interaction in the OffscreenQMLSurface + target_glew() +endif () diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index a80105293b..76a8a780b9 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -20,7 +20,8 @@ #include #include #include -#include + +#include "ui/TabletScriptingInterface.h" #include "FileDialogHelper.h" #include "VrMenu.h" diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 55fb8b2c3d..f228f28d5e 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -21,9 +21,9 @@ #include #include -#include #include +#include "ui/OffscreenQmlSurface.h" #include "OffscreenQmlElement.h" class VrMenu; diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp similarity index 97% rename from libraries/gl/src/gl/OffscreenQmlSurface.cpp rename to libraries/ui/src/ui/OffscreenQmlSurface.cpp index 9dcc1d7991..f0006cb399 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -6,7 +6,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OffscreenQmlSurface.h" -#include "Config.h" + +// Has to come before Qt GL includes +#include #include #include @@ -33,14 +35,17 @@ #include #include #include -#include -#include -#include -#include "OffscreenGLCanvas.h" -#include "GLHelpers.h" -#include "GLLogging.h" -#include "Context.h" +#include +#include +#include + +#include "types/FileTypeProfile.h" +#include "types/HFWebEngineProfile.h" +#include "types/HFTabletWebEngineProfile.h" +#include "types/SoundEffect.h" + +#include "Logging.h" Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml") Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl") @@ -272,7 +277,7 @@ QString getEventBridgeJavascript() { QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll(); javaScriptToInject = webChannelStr + createGlobalEventBridgeStr; } else { - qCWarning(glLogging) << "Unable to find qwebchannel.js or createGlobalEventBridge.js"; + qCWarning(uiLogging) << "Unable to find qwebchannel.js or createGlobalEventBridge.js"; } return javaScriptToInject; } @@ -297,6 +302,14 @@ private: QQmlEngine* acquireEngine(QQuickWindow* window) { Q_ASSERT(QThread::currentThread() == qApp->thread()); + if (QThread::currentThread() != qApp->thread()) { + qCWarning(uiLogging) << "Cannot acquire QML engine on any thread but the main thread"; + } + static std::once_flag once; + std::call_once(once, [] { + qmlRegisterType("Hifi", 1, 0, "SoundEffect"); + }); + if (!globalEngine) { Q_ASSERT(0 == globalEngineRefCount); globalEngine = new QQmlEngine(); @@ -325,8 +338,6 @@ QQmlEngine* acquireEngine(QQuickWindow* window) { rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext)); rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext)); rootContext->setContextProperty("HFTabletWebEngineProfile", new HFTabletWebEngineProfile(rootContext)); - - } ++globalEngineRefCount; @@ -457,7 +468,7 @@ void OffscreenQmlSurface::onAboutToQuit() { } void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { - qCDebug(glLogging) << "Building QML surface"; + qCDebug(uiLogging) << "Building QML surface"; _renderControl = new QMyQuickRenderControl(); connect(_renderControl, &QQuickRenderControl::renderRequested, this, [this] { _render = true; }); @@ -548,7 +559,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { return; } - qCDebug(glLogging) << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height(); + qCDebug(uiLogging) << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height(); gl::withSavedContext([&] { _canvas->makeCurrent(); @@ -595,6 +606,9 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) { } QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function f) { + if (QThread::currentThread() != thread()) { + qCWarning(uiLogging) << "Called load on a non-surface thread"; + } // Synchronous loading may take a while; restart the deadlock timer QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection); @@ -636,7 +650,7 @@ QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlCon disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0); if (qmlComponent->isError()) { for (const auto& error : qmlComponent->errors()) { - qCWarning(glLogging) << error.url() << error.line() << error; + qCWarning(uiLogging) << error.url() << error.line() << error; } qmlComponent->deleteLater(); return nullptr; @@ -646,7 +660,7 @@ QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlCon QObject* newObject = qmlComponent->beginCreate(qmlContext); if (qmlComponent->isError()) { for (const auto& error : qmlComponent->errors()) { - qCWarning(glLogging) << error.url() << error.line() << error; + qCWarning(uiLogging) << error.url() << error.line() << error; } if (!_rootItem) { qFatal("Unable to finish loading QML root"); diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h similarity index 100% rename from libraries/gl/src/gl/OffscreenQmlSurface.h rename to libraries/ui/src/ui/OffscreenQmlSurface.h diff --git a/libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp b/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp similarity index 100% rename from libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp rename to libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp diff --git a/libraries/gl/src/gl/OffscreenQmlSurfaceCache.h b/libraries/ui/src/ui/OffscreenQmlSurfaceCache.h similarity index 100% rename from libraries/gl/src/gl/OffscreenQmlSurfaceCache.h rename to libraries/ui/src/ui/OffscreenQmlSurfaceCache.h diff --git a/libraries/ui/src/ui/QmlWrapper.cpp b/libraries/ui/src/ui/QmlWrapper.cpp new file mode 100644 index 0000000000..518a05613a --- /dev/null +++ b/libraries/ui/src/ui/QmlWrapper.cpp @@ -0,0 +1,59 @@ +// +// Created by Bradley Austin Davis on 2017/06/22 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "QmlWrapper.h" + +#include +#include + +QmlWrapper::QmlWrapper(QObject* qmlObject, QObject* parent) + : QObject(parent), _qmlObject(qmlObject) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); +} + +void QmlWrapper::writeProperty(QString propertyName, QVariant propertyValue) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "writeProperty", Q_ARG(QString, propertyName), Q_ARG(QVariant, propertyValue)); + } + _qmlObject->setProperty(propertyName.toStdString().c_str(), propertyValue); +} + +void QmlWrapper::writeProperties(QVariant propertyMap) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "writeProperties", Q_ARG(QVariant, propertyMap)); + } + QVariantMap map = propertyMap.toMap(); + for (const QString& key : map.keys()) { + _qmlObject->setProperty(key.toStdString().c_str(), map[key]); + } +} + +QVariant QmlWrapper::readProperty(const QString& propertyName) { + if (QThread::currentThread() != thread()) { + QVariant result; + QMetaObject::invokeMethod(this, "readProperty", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QString, propertyName)); + return result; + } + + return _qmlObject->property(propertyName.toStdString().c_str()); +} + +QVariant QmlWrapper::readProperties(const QVariant& propertyList) { + if (QThread::currentThread() != thread()) { + QVariant result; + QMetaObject::invokeMethod(this, "readProperties", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, propertyList)); + return result; + } + + QVariantMap result; + for (const QVariant& property : propertyList.toList()) { + QString propertyString = property.toString(); + result.insert(propertyString, _qmlObject->property(propertyString.toStdString().c_str())); + } + return result; +} diff --git a/libraries/ui/src/ui/QmlWrapper.h b/libraries/ui/src/ui/QmlWrapper.h new file mode 100644 index 0000000000..d77e45c9dc --- /dev/null +++ b/libraries/ui/src/ui/QmlWrapper.h @@ -0,0 +1,44 @@ +// +// Created by Anthony J. Thibault on 2016-12-12 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_QmlWrapper_h +#define hifi_QmlWrapper_h + +#include +#include +#include +#include + +class QmlWrapper : public QObject { + Q_OBJECT +public: + QmlWrapper(QObject* qmlObject, QObject* parent = nullptr); + + Q_INVOKABLE void writeProperty(QString propertyName, QVariant propertyValue); + Q_INVOKABLE void writeProperties(QVariant propertyMap); + Q_INVOKABLE QVariant readProperty(const QString& propertyName); + Q_INVOKABLE QVariant readProperties(const QVariant& propertyList); + +protected: + QObject* _qmlObject{ nullptr }; +}; + +template +QScriptValue wrapperToScriptValue(QScriptEngine* engine, T* const &in) { + if (!in) { + return engine->undefinedValue(); + } + return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); +} + +template +void wrapperFromScriptValue(const QScriptValue& value, T* &out) { + out = qobject_cast(value.toQObject()); +} + +#endif \ No newline at end of file diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp similarity index 69% rename from libraries/script-engine/src/TabletScriptingInterface.cpp rename to libraries/ui/src/ui/TabletScriptingInterface.cpp index ef2887fdfb..f85106918e 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -9,66 +9,51 @@ #include "TabletScriptingInterface.h" #include +#include -#include -#include "DependencyManager.h" #include -#include -#include +#include +#include #include -#include "ScriptEngineLogging.h" -#include -#include -#include "SoundEffect.h" +#include "../QmlWindowClass.h" +#include "../OffscreenUi.h" +#include "../InfoView.h" +#include "ToolbarScriptingInterface.h" +#include "Logging.h" + +// FIXME move to global app properties const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; -QScriptValue tabletToScriptValue(QScriptEngine* engine, TabletProxy* const &in) { - return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); -} - -void tabletFromScriptValue(const QScriptValue& value, TabletProxy* &out) { - out = qobject_cast(value.toQObject()); -} - TabletScriptingInterface::TabletScriptingInterface() { - qmlRegisterType("Hifi", 1, 0, "SoundEffect"); + qCDebug(uiLogging) << "Building tablet scripting interface"; } -QObject* TabletScriptingInterface::getSystemToolbarProxy() { - Qt::ConnectionType connectionType = Qt::AutoConnection; - if (QThread::currentThread() != _toolbarScriptingInterface->thread()) { - connectionType = Qt::BlockingQueuedConnection; - } +TabletScriptingInterface::~TabletScriptingInterface() { + qCDebug(uiLogging) << "Destroying tablet scripting interface"; +} - QObject* toolbarProxy = nullptr; - bool hasResult = QMetaObject::invokeMethod(_toolbarScriptingInterface, "getToolbar", connectionType, Q_RETURN_ARG(QObject*, toolbarProxy), Q_ARG(QString, SYSTEM_TOOLBAR)); - if (hasResult) { - return toolbarProxy; - } else { - qCWarning(scriptengine) << "ToolbarScriptingInterface getToolbar has no result"; - return nullptr; - } +ToolbarProxy* TabletScriptingInterface::getSystemToolbarProxy() { + Q_ASSERT(QThread::currentThread() == qApp->thread()); + return _toolbarScriptingInterface->getToolbar(SYSTEM_TOOLBAR); } TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { TabletProxy* tabletProxy = nullptr; - { - // the only thing guarded should be map mutation - // this avoids a deadlock with the Main thread - // from Qt::BlockingQueuedEvent invocations later in the call-tree - std::lock_guard guard(_mapMutex); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "getTablet", Qt::BlockingQueuedConnection, Q_RETURN_ARG(TabletProxy*, tabletProxy), Q_ARG(QString, tabletId)); + return tabletProxy; + } - auto iter = _tabletProxies.find(tabletId); - if (iter != _tabletProxies.end()) { - // tablet already exists - return iter->second; - } else { - // tablet must be created - tabletProxy = new TabletProxy(this, tabletId); - _tabletProxies[tabletId] = tabletProxy; - } + auto iter = _tabletProxies.find(tabletId); + if (iter != _tabletProxies.end()) { + // tablet already exists + return iter->second; + } else { + // tablet must be created + tabletProxy = new TabletProxy(this, tabletId); + _tabletProxies[tabletId] = tabletProxy; } assert(tabletProxy); @@ -78,42 +63,40 @@ TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { } void TabletScriptingInterface::setToolbarMode(bool toolbarMode) { - { - // the only thing guarded should be _toolbarMode - // this avoids a deadlock with the Main thread - // from Qt::BlockingQueuedEvent invocations later in the call-tree - std::lock_guard guard(_mapMutex); - _toolbarMode = toolbarMode; - } - + Q_ASSERT(QThread::currentThread() == qApp->thread()); + _toolbarMode = toolbarMode; for (auto& iter : _tabletProxies) { iter.second->setToolbarMode(toolbarMode); } } -void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) { +void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, OffscreenQmlSurface* qmlOffscreenSurface) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); TabletProxy* tablet = qobject_cast(getTablet(tabletId)); if (tablet) { - tablet->setQmlTabletRoot(qmlTabletRoot, qmlOffscreenSurface); + tablet->setQmlTabletRoot(qmlOffscreenSurface); } else { - qCWarning(scriptengine) << "TabletScriptingInterface::setupTablet() bad tablet object"; + qCWarning(uiLogging) << "TabletScriptingInterface::setupTablet() bad tablet object"; } } QQuickWindow* TabletScriptingInterface::getTabletWindow() { + Q_ASSERT(QThread::currentThread() == qApp->thread()); TabletProxy* tablet = qobject_cast(getTablet(SYSTEM_TABLET)); - QObject* qmlSurface = tablet->getTabletSurface(); - - OffscreenQmlSurface* surface = dynamic_cast(qmlSurface); - - if (!surface) { + if (!tablet) { return nullptr; } - QQuickWindow* window = surface->getWindow(); - return window; + + auto* qmlSurface = tablet->getTabletSurface(); + if (!qmlSurface) { + return nullptr; + } + + return qmlSurface->getWindow(); } void TabletScriptingInterface::processMenuEvents(QObject* object, const QKeyEvent* event) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); switch (event->key()) { case Qt::Key_Down: QMetaObject::invokeMethod(object, "nextItem"); @@ -141,6 +124,7 @@ void TabletScriptingInterface::processMenuEvents(QObject* object, const QKeyEven } void TabletScriptingInterface::processTabletEvents(QObject* object, const QKeyEvent* event) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); switch (event->key()) { case Qt::Key_Down: QMetaObject::invokeMethod(object, "downItem"); @@ -167,8 +151,8 @@ void TabletScriptingInterface::processTabletEvents(QObject* object, const QKeyEv } } - void TabletScriptingInterface::processEvent(const QKeyEvent* event) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); TabletProxy* tablet = qobject_cast(getTablet(SYSTEM_TABLET)); QObject* qmlTablet = tablet->getQmlTablet(); QObject* qmlMenu = tablet->getQmlMenu(); @@ -180,8 +164,8 @@ void TabletScriptingInterface::processEvent(const QKeyEvent* event) { } } -QObject* TabletScriptingInterface::getFlags() -{ +QObject* TabletScriptingInterface::getFlags() { + Q_ASSERT(QThread::currentThread() == qApp->thread()); auto offscreenUi = DependencyManager::get(); return offscreenUi->getFlags(); } @@ -199,10 +183,23 @@ class TabletRootWindow : public QmlWindowClass { }; TabletProxy::TabletProxy(QObject* parent, QString name) : QObject(parent), _name(name) { + if (QThread::currentThread() != qApp->thread()) { + qCWarning(uiLogging) << "Creating tablet proxy on wrong thread " << _name; + } +} + +TabletProxy::~TabletProxy() { + qCDebug(uiLogging) << "Destroying tablet proxy " << _name; + if (QThread::currentThread() != thread()) { + qCWarning(uiLogging) << "Destroying tablet proxy on wrong thread" << _name; + } } void TabletProxy::setToolbarMode(bool toolbarMode) { - std::lock_guard guard(_tabletMutex); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setToolbarMode", Q_ARG(bool, toolbarMode)); + return; + } if (toolbarMode == _toolbarMode) { return; @@ -245,25 +242,23 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { } static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) { - QVariant resultVar; - Qt::ConnectionType connectionType = Qt::AutoConnection; - if (QThread::currentThread() != qmlTablet->thread()) { - connectionType = Qt::BlockingQueuedConnection; + Q_ASSERT(QThread::currentThread() == qApp->thread()); + if (buttonProxy == NULL){ + qCCritical(uiLogging) << "TabletScriptingInterface addButtonProxyToQmlTablet buttonProxy is NULL"; + return; } - if (buttonProxy == NULL){ - qCCritical(scriptengine) << "TabletScriptingInterface addButtonProxyToQmlTablet buttonProxy is NULL"; - return; - } - bool hasResult = QMetaObject::invokeMethod(qmlTablet, "addButtonProxy", connectionType, + + QVariant resultVar; + bool hasResult = QMetaObject::invokeMethod(qmlTablet, "addButtonProxy", Qt::DirectConnection, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, buttonProxy->getProperties())); if (!hasResult) { - qCWarning(scriptengine) << "TabletScriptingInterface addButtonProxyToQmlTablet has no result"; + qCWarning(uiLogging) << "TabletScriptingInterface addButtonProxyToQmlTablet has no result"; return; } QObject* qmlButton = qvariant_cast(resultVar); if (!qmlButton) { - qCWarning(scriptengine) << "TabletScriptingInterface addButtonProxyToQmlTablet result not a QObject"; + qCWarning(uiLogging) << "TabletScriptingInterface addButtonProxyToQmlTablet result not a QObject"; return; } QObject::connect(qmlButton, SIGNAL(clicked()), buttonProxy, SLOT(clickedSlot())); @@ -281,6 +276,11 @@ static QString getUsername() { } void TabletProxy::initialScreen(const QVariant& url) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "initialScreen", Q_ARG(QVariant, url)); + return; + } + if (_qmlTabletRoot) { pushOntoStack(url); } else { @@ -290,34 +290,49 @@ void TabletProxy::initialScreen(const QVariant& url) { } bool TabletProxy::isMessageDialogOpen() { - if (_qmlTabletRoot) { - QVariant result; - QMetaObject::invokeMethod(_qmlTabletRoot, "isDialogOpen",Qt::DirectConnection, - Q_RETURN_ARG(QVariant, result)); - - return result.toBool(); + if (QThread::currentThread() != thread()) { + bool result = false; + QMetaObject::invokeMethod(this, "isMessageDialogOpen", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result)); + return result; } - return false; + if (!_qmlTabletRoot) { + return false; + } + + QVariant result; + QMetaObject::invokeMethod(_qmlTabletRoot, "isDialogOpen",Qt::DirectConnection, + Q_RETURN_ARG(QVariant, result)); + return result.toBool(); } void TabletProxy::emitWebEvent(QVariant msg) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "emitWebEvent", Q_ARG(QVariant, msg)); + return; + } emit webEventReceived(msg); } bool TabletProxy::isPathLoaded(QVariant path) { + if (QThread::currentThread() != thread()) { + bool result = false; + QMetaObject::invokeMethod(this, "isPathLoaded", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path)); + return result; + } + return path.toString() == _currentPathLoaded.toString(); } -void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) { - std::lock_guard guard(_tabletMutex); - _qmlOffscreenSurface = qmlOffscreenSurface; - _qmlTabletRoot = qmlTabletRoot; - if (_qmlTabletRoot && _qmlOffscreenSurface) { - QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection); +void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); + _qmlOffscreenSurface = qmlOffscreenSurface; + _qmlTabletRoot = qmlOffscreenSurface ? qmlOffscreenSurface->getRootItem() : nullptr; + if (_qmlTabletRoot && _qmlOffscreenSurface) { + QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant))); // forward qml surface events to interface js - connect(dynamic_cast(_qmlOffscreenSurface), &OffscreenQmlSurface::fromQml, [this](QVariant message) { + connect(_qmlOffscreenSurface, &OffscreenQmlSurface::fromQml, [this](QVariant message) { if (message.canConvert()) { emit fromQml(qvariant_cast(message).toVariant()); } else if (message.canConvert()) { @@ -330,7 +345,7 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr if (_toolbarMode) { // if someone creates the tablet in toolbar mode, make sure to display the home screen on the tablet. auto loader = _qmlTabletRoot->findChild("loader"); - QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()), Qt::DirectConnection); + QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen())); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL))); } @@ -360,9 +375,18 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr } void TabletProxy::gotoHomeScreen() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "gotoHomeScreen"); + return; + } loadHomeScreen(false); } + void TabletProxy::gotoMenuScreen(const QString& submenu) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "gotoMenuScreen", Q_ARG(QString, submenu)); + return; + } QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { @@ -385,6 +409,11 @@ void TabletProxy::gotoMenuScreen(const QString& submenu) { } void TabletProxy::loadQMLOnTop(const QVariant& path) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadQMLOnTop", Q_ARG(QVariant, path)); + return; + } + QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -396,11 +425,16 @@ void TabletProxy::loadQMLOnTop(const QVariant& path) { QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, path)); QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } else { - qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null"; + qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; } } void TabletProxy::returnToPreviousApp() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "returnToPreviousApp"); + return; + } + QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -412,11 +446,15 @@ void TabletProxy::returnToPreviousApp() { QMetaObject::invokeMethod(root, "returnToPreviousApp"); QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } else { - qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null"; + qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; } } void TabletProxy::loadQMLSource(const QVariant& path) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadQMLSource", Q_ARG(QVariant, path)); + return; + } QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { @@ -435,11 +473,17 @@ void TabletProxy::loadQMLSource(const QVariant& path) { QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } } else { - qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null"; + qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; } } bool TabletProxy::pushOntoStack(const QVariant& path) { + if (QThread::currentThread() != thread()) { + bool result = false; + QMetaObject::invokeMethod(this, "pushOntoStack", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path)); + return result; + } + QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -455,13 +499,18 @@ bool TabletProxy::pushOntoStack(const QVariant& path) { loadQMLSource(path); } } else { - qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; + qCDebug(uiLogging) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; } return root; } void TabletProxy::popFromStack() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "popFromStack"); + return; + } + QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -473,15 +522,20 @@ void TabletProxy::popFromStack() { auto stack = root->findChild("stack"); QMetaObject::invokeMethod(stack, "popSource"); } else { - qCDebug(scriptengine) << "tablet cannot pop QML because _qmlTabletRoot or _desktopWindow is null"; + qCDebug(uiLogging) << "tablet cannot pop QML because _qmlTabletRoot or _desktopWindow is null"; } } void TabletProxy::loadHomeScreen(bool forceOntoHomeScreen) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadHomeScreen", Q_ARG(bool, forceOntoHomeScreen)); + return; + } + if ((_state != State::Home && _state != State::Uninitialized) || forceOntoHomeScreen) { if (!_toolbarMode && _qmlTabletRoot) { auto loader = _qmlTabletRoot->findChild("loader"); - QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()), Qt::DirectConnection); + QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen())); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL))); QMetaObject::invokeMethod(_qmlTabletRoot, "playButtonClickSound"); } else if (_toolbarMode && _desktopWindow) { @@ -505,6 +559,11 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url) { } void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJavaScriptUrl) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadWebScreenOnTop", Q_ARG(QVariant, url), Q_ARG(QString, injectJavaScriptUrl)); + return; + } + QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -521,6 +580,10 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJ } void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "gotoWebScreen", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl)); + return; + } QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { @@ -540,57 +603,59 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS _currentPathLoaded = QVariant(url); } -QObject* TabletProxy::addButton(const QVariant& properties) { +TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) { + if (QThread::currentThread() != thread()) { + TabletButtonProxy* result = nullptr; + QMetaObject::invokeMethod(this, "addButton", Qt::BlockingQueuedConnection, Q_RETURN_ARG(TabletButtonProxy*, result), Q_ARG(QVariant, properties)); + return result; + } + auto tabletButtonProxy = QSharedPointer(new TabletButtonProxy(properties.toMap())); - std::unique_lock guard(_tabletMutex); _tabletButtonProxies.push_back(tabletButtonProxy); if (!_toolbarMode && _qmlTabletRoot) { auto tablet = getQmlTablet(); if (tablet) { addButtonProxyToQmlTablet(tablet, tabletButtonProxy.data()); } else { - qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; + qCCritical(uiLogging) << "Could not find tablet in TabletRoot.qml"; } } else if (_toolbarMode) { auto tabletScriptingInterface = DependencyManager::get(); - QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); - - Qt::ConnectionType connectionType = Qt::AutoConnection; - if (QThread::currentThread() != toolbarProxy->thread()) { - connectionType = Qt::BlockingQueuedConnection; - } - - guard.unlock(); - - // copy properties from tablet button proxy to toolbar button proxy. - QObject* toolbarButtonProxy = nullptr; - bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties())); - if (hasResult) { - tabletButtonProxy->setToolbarButtonProxy(toolbarButtonProxy); - } else { - qCWarning(scriptengine) << "ToolbarProxy addButton has no result"; + auto toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); + if (toolbarProxy) { + // copy properties from tablet button proxy to toolbar button proxy. + toolbarProxy->addButton(tabletButtonProxy->getProperties()); } } return tabletButtonProxy.data(); } bool TabletProxy::onHomeScreen() { + if (QThread::currentThread() != thread()) { + bool result = false; + QMetaObject::invokeMethod(this, "onHomeScreen", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result)); + return result; + } + return _state == State::Home; } -void TabletProxy::removeButton(QObject* tabletButtonProxy) { - std::unique_lock guard(_tabletMutex); +void TabletProxy::removeButton(TabletButtonProxy* tabletButtonProxy) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "removeButton", Q_ARG(TabletButtonProxy*, tabletButtonProxy)); + return; + } auto tablet = getQmlTablet(); if (!tablet) { - qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; + qCCritical(uiLogging) << "Could not find tablet in TabletRoot.qml"; } QSharedPointer buttonProxy; { auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy); if (iter == _tabletButtonProxies.end()) { - qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy; + qCWarning(uiLogging) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy; return; } buttonProxy = *iter; @@ -600,21 +665,23 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) { if (!_toolbarMode && _qmlTabletRoot) { buttonProxy->setQmlButton(nullptr); if (tablet) { - guard.unlock(); QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getProperties())); } } else if (_toolbarMode) { auto tabletScriptingInterface = DependencyManager::get(); QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); - // remove button from toolbarProxy - guard.unlock(); QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString())); buttonProxy->setToolbarButtonProxy(nullptr); } } void TabletProxy::emitScriptEvent(QVariant msg) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "emitScriptEvent", Q_ARG(QVariant, msg)); + return; + } + if (!_toolbarMode && _qmlOffscreenSurface) { QMetaObject::invokeMethod(_qmlOffscreenSurface, "emitScriptEvent", Qt::AutoConnection, Q_ARG(QVariant, msg)); } else if (_toolbarMode && _desktopWindow) { @@ -623,6 +690,11 @@ void TabletProxy::emitScriptEvent(QVariant msg) { } void TabletProxy::sendToQml(QVariant msg) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "sendToQml", Q_ARG(QVariant, msg)); + return; + } + if (!_toolbarMode && _qmlOffscreenSurface) { QMetaObject::invokeMethod(_qmlOffscreenSurface, "sendToQml", Qt::AutoConnection, Q_ARG(QVariant, msg)); } else if (_toolbarMode && _desktopWindow) { @@ -644,7 +716,7 @@ void TabletProxy::addButtonsToHomeScreen() { QObject::disconnect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen())); } -QObject* TabletProxy::getTabletSurface() { +OffscreenQmlSurface* TabletProxy::getTabletSurface() { return _qmlOffscreenSurface; } @@ -678,7 +750,7 @@ void TabletProxy::addButtonsToToolbar() { if (hasResult) { buttonProxy->setToolbarButtonProxy(toolbarButtonProxy); } else { - qCWarning(scriptengine) << "ToolbarProxy addButton has no result"; + qCWarning(uiLogging) << "ToolbarProxy addButton has no result"; } } @@ -753,34 +825,55 @@ TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) : _properties[UUID_KEY] = _uuid; _properties[OBJECT_NAME_KEY] = _uuid.toString(); _properties[STABLE_ORDER_KEY] = _stableOrder; + if (QThread::currentThread() != qApp->thread()) { + qCWarning(uiLogging) << "Creating tablet button proxy on wrong thread"; + } +} + +TabletButtonProxy::~TabletButtonProxy() { + qCDebug(uiLogging) << "Destroying tablet button proxy " ; + if (QThread::currentThread() != thread()) { + qCWarning(uiLogging) << "Destroying tablet button proxy on wrong thread"; + } } void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { - std::lock_guard guard(_buttonMutex); + Q_ASSERT(QThread::currentThread() == qApp->thread()); _qmlButton = qmlButton; } void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) { - std::lock_guard guard(_buttonMutex); _toolbarButtonProxy = toolbarButtonProxy; if (_toolbarButtonProxy) { QObject::connect(_toolbarButtonProxy, SIGNAL(clicked()), this, SLOT(clickedSlot())); } } -QVariantMap TabletButtonProxy::getProperties() const { - std::lock_guard guard(_buttonMutex); +QVariantMap TabletButtonProxy::getProperties() { + if (QThread::currentThread() != thread()) { + QVariantMap result; + QMetaObject::invokeMethod(this, "getProperties", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariantMap, result)); + return result; + } + return _properties; } void TabletButtonProxy::editProperties(QVariantMap properties) { - std::lock_guard guard(_buttonMutex); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "editProperties", Qt::BlockingQueuedConnection, Q_ARG(QVariantMap, properties)); + return; + } QVariantMap::const_iterator iter = properties.constBegin(); while (iter != properties.constEnd()) { - _properties[iter.key()] = iter.value(); - if (_qmlButton) { - QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection, Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value())); + const auto& key = iter.key(); + const auto& value = iter.value(); + if (!_properties.contains(key) || _properties[key] != value) { + _properties[iter.key()] = iter.value(); + if (_qmlButton) { + QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection, Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value())); + } } ++iter; } @@ -789,5 +882,3 @@ void TabletButtonProxy::editProperties(QVariantMap properties) { QMetaObject::invokeMethod(_toolbarButtonProxy, "editProperties", Qt::AutoConnection, Q_ARG(QVariantMap, properties)); } } - -#include "TabletScriptingInterface.moc" diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h similarity index 90% rename from libraries/script-engine/src/TabletScriptingInterface.h rename to libraries/ui/src/ui/TabletScriptingInterface.h index 23d7ecaea4..0303a0a1c4 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -26,9 +26,13 @@ #include +class ToolbarProxy; +class ToolbarScriptingInterface; + class TabletProxy; class TabletButtonProxy; class QmlWindowClass; +class OffscreenQmlSurface; /**jsdoc * @namespace Tablet @@ -37,9 +41,9 @@ class TabletScriptingInterface : public QObject, public Dependency { Q_OBJECT public: TabletScriptingInterface(); + ~TabletScriptingInterface(); - void setToolbarScriptingInterface(QObject* toolbarScriptingInterface) { _toolbarScriptingInterface = toolbarScriptingInterface; } - QObject* getSystemToolbarProxy(); + void setToolbarScriptingInterface(ToolbarScriptingInterface* toolbarScriptingInterface) { _toolbarScriptingInterface = toolbarScriptingInterface; } /**jsdoc * Creates or retruns a new TabletProxy and returns it. @@ -51,7 +55,7 @@ public: void setToolbarMode(bool toolbarMode); - void setQmlTabletRoot(QString tabletId, QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface); + void setQmlTabletRoot(QString tabletId, OffscreenQmlSurface* offscreenQmlSurface); void processEvent(const QKeyEvent* event); @@ -67,13 +71,14 @@ signals: void tabletNotification(); private: + friend class TabletProxy; void processMenuEvents(QObject* object, const QKeyEvent* event); void processTabletEvents(QObject* object, const QKeyEvent* event); + ToolbarProxy* getSystemToolbarProxy(); protected: - std::mutex _mapMutex; std::map _tabletProxies; - QObject* _toolbarScriptingInterface { nullptr }; + ToolbarScriptingInterface* _toolbarScriptingInterface { nullptr }; bool _toolbarMode { false }; }; @@ -91,18 +96,19 @@ class TabletProxy : public QObject { Q_PROPERTY(bool tabletShown MEMBER _tabletShown NOTIFY tabletShownChanged) public: TabletProxy(QObject* parent, QString name); + ~TabletProxy(); - void setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface); - - Q_INVOKABLE void gotoMenuScreen(const QString& submenu = ""); - - QString getName() const { return _name; } + void setQmlTabletRoot(OffscreenQmlSurface* offscreenQmlSurface); + const QString getName() const { return _name; } bool getToolbarMode() const { return _toolbarMode; } void setToolbarMode(bool toolbarMode); + + Q_INVOKABLE void gotoMenuScreen(const QString& submenu = ""); Q_INVOKABLE void initialScreen(const QVariant& url); + /**jsdoc * transition to the home screen * @function TabletProxy#gotoHomeScreen @@ -143,14 +149,14 @@ public: * @param properties {Object} button properties UI_TABLET_HACK: enumerate these when we figure out what they should be! * @returns {TabletButtonProxy} */ - Q_INVOKABLE QObject* addButton(const QVariant& properties); + Q_INVOKABLE TabletButtonProxy* addButton(const QVariant& properties); /**jsdoc * removes button from the tablet * @function TabletProxy.removeButton * @param tabletButtonProxy {TabletButtonProxy} button to be removed */ - Q_INVOKABLE void removeButton(QObject* tabletButtonProxy); + Q_INVOKABLE void removeButton(TabletButtonProxy* tabletButtonProxy); /**jsdoc * Used to send an event to the html/js embedded in the tablet @@ -184,7 +190,7 @@ public: QQuickItem* getTabletRoot() const { return _qmlTabletRoot; } - QObject* getTabletSurface(); + OffscreenQmlSurface* getTabletSurface(); QQuickItem* getQmlTablet() const; @@ -236,10 +242,9 @@ protected: QVariant _initialPath { "" }; QVariant _currentPathLoaded { "" }; QString _name; - std::mutex _tabletMutex; std::vector> _tabletButtonProxies; QQuickItem* _qmlTabletRoot { nullptr }; - QObject* _qmlOffscreenSurface { nullptr }; + OffscreenQmlSurface* _qmlOffscreenSurface { nullptr }; QmlWindowClass* _desktopWindow { nullptr }; bool _toolbarMode { false }; bool _tabletShown { false }; @@ -251,9 +256,6 @@ protected: Q_DECLARE_METATYPE(TabletProxy*); -QScriptValue tabletToScriptValue(QScriptEngine* engine, TabletProxy* const &in); -void tabletFromScriptValue(const QScriptValue& value, TabletProxy* &out); - /**jsdoc * @class TabletButtonProxy * @property uuid {QUuid} READ_ONLY: uniquely identifies this button @@ -263,6 +265,7 @@ class TabletButtonProxy : public QObject { Q_PROPERTY(QUuid uuid READ getUuid) public: TabletButtonProxy(const QVariantMap& properties); + ~TabletButtonProxy(); void setQmlButton(QQuickItem* qmlButton); void setToolbarButtonProxy(QObject* toolbarButtonProxy); @@ -274,7 +277,7 @@ public: * @function TabletButtonProxy#getProperties * @returns {ButtonProperties} */ - Q_INVOKABLE QVariantMap getProperties() const; + Q_INVOKABLE QVariantMap getProperties(); /**jsdoc * Replace the values of some of this button's properties @@ -297,12 +300,13 @@ signals: protected: QUuid _uuid; int _stableOrder; - mutable std::mutex _buttonMutex; QQuickItem* _qmlButton { nullptr }; QObject* _toolbarButtonProxy { nullptr }; QVariantMap _properties; }; +Q_DECLARE_METATYPE(TabletButtonProxy*); + /**jsdoc * @typedef TabletButtonProxy.ButtonProperties * @property {string} icon - url to button icon. (50 x 50) diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.cpp b/libraries/ui/src/ui/ToolbarScriptingInterface.cpp new file mode 100644 index 0000000000..330c652cdc --- /dev/null +++ b/libraries/ui/src/ui/ToolbarScriptingInterface.cpp @@ -0,0 +1,124 @@ +// +// Created by Bradley Austin Davis on 2016-06-16 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ToolbarScriptingInterface.h" + +#include +#include +#include +#include +#include "../OffscreenUi.h" + +QScriptValue toolbarToScriptValue(QScriptEngine* engine, ToolbarProxy* const &in) { + if (!in) { + return engine->undefinedValue(); + } + return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); +} + +void toolbarFromScriptValue(const QScriptValue& value, ToolbarProxy* &out) { + out = qobject_cast(value.toQObject()); +} + +QScriptValue toolbarButtonToScriptValue(QScriptEngine* engine, ToolbarButtonProxy* const &in) { + if (!in) { + return engine->undefinedValue(); + } + return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); +} + +void toolbarButtonFromScriptValue(const QScriptValue& value, ToolbarButtonProxy* &out) { + out = qobject_cast(value.toQObject()); +} + + +ToolbarButtonProxy::ToolbarButtonProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); + _qmlButton = qobject_cast(qmlObject); + connect(qmlObject, SIGNAL(clicked()), this, SIGNAL(clicked())); +} + +void ToolbarButtonProxy::editProperties(const QVariantMap& properties) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "editProperties", Q_ARG(QVariantMap, properties)); + return; + } + + QVariantMap::const_iterator iter = properties.constBegin(); + while (iter != properties.constEnd()) { + _properties[iter.key()] = iter.value(); + if (_qmlButton) { + // [01/25 14:26:20] [WARNING] [default] QMetaObject::invokeMethod: No such method ToolbarButton_QMLTYPE_195::changeProperty(QVariant,QVariant) + QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection, + Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value())); + } + ++iter; + } +} + +ToolbarProxy::ToolbarProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); +} + +ToolbarButtonProxy* ToolbarProxy::addButton(const QVariant& properties) { + if (QThread::currentThread() != thread()) { + ToolbarButtonProxy* result = nullptr; + QMetaObject::invokeMethod(this, "addButton", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ToolbarButtonProxy*, result), Q_ARG(QVariant, properties)); + return result; + } + + QVariant resultVar; + bool invokeResult = QMetaObject::invokeMethod(_qmlObject, "addButton", Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, properties)); + if (!invokeResult) { + return nullptr; + } + + QObject* rawButton = qvariant_cast(resultVar); + if (!rawButton) { + return nullptr; + } + + return new ToolbarButtonProxy(rawButton, this); +} + +void ToolbarProxy::removeButton(const QVariant& name) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "removeButton", Q_ARG(QVariant, name)); + return; + } + + QMetaObject::invokeMethod(_qmlObject, "removeButton", Q_ARG(QVariant, name)); +} + + +ToolbarProxy* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) { + if (QThread::currentThread() != thread()) { + ToolbarProxy* result = nullptr; + QMetaObject::invokeMethod(this, "getToolbar", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ToolbarProxy*, result), Q_ARG(QString, toolbarId)); + return result; + } + + auto offscreenUi = DependencyManager::get(); + auto desktop = offscreenUi->getDesktop(); + Qt::ConnectionType connectionType = Qt::AutoConnection; + if (QThread::currentThread() != desktop->thread()) { + connectionType = Qt::BlockingQueuedConnection; + } + QVariant resultVar; + bool invokeResult = QMetaObject::invokeMethod(desktop, "getToolbar", connectionType, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, toolbarId)); + if (!invokeResult) { + return nullptr; + } + + QObject* rawToolbar = qvariant_cast(resultVar); + if (!rawToolbar) { + return nullptr; + } + + return new ToolbarProxy(rawToolbar); +} diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.h b/libraries/ui/src/ui/ToolbarScriptingInterface.h new file mode 100644 index 0000000000..108cf6bdd5 --- /dev/null +++ b/libraries/ui/src/ui/ToolbarScriptingInterface.h @@ -0,0 +1,56 @@ +// +// Created by Bradley Austin Davis on 2016-06-16 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ToolbarScriptingInterface_h +#define hifi_ToolbarScriptingInterface_h + +#include + +#include +#include + +#include +#include "QmlWrapper.h" + +class QQuickItem; + +class ToolbarButtonProxy : public QmlWrapper { + Q_OBJECT + +public: + ToolbarButtonProxy(QObject* qmlObject, QObject* parent = nullptr); + Q_INVOKABLE void editProperties(const QVariantMap& properties); + +signals: + void clicked(); + +protected: + QQuickItem* _qmlButton { nullptr }; + QVariantMap _properties; +}; + +Q_DECLARE_METATYPE(ToolbarButtonProxy*); + +class ToolbarProxy : public QmlWrapper { + Q_OBJECT +public: + ToolbarProxy(QObject* qmlObject, QObject* parent = nullptr); + Q_INVOKABLE ToolbarButtonProxy* addButton(const QVariant& properties); + Q_INVOKABLE void removeButton(const QVariant& name); +}; + +Q_DECLARE_METATYPE(ToolbarProxy*); + +class ToolbarScriptingInterface : public QObject, public Dependency { + Q_OBJECT +public: + Q_INVOKABLE ToolbarProxy* getToolbar(const QString& toolbarId); +}; + + +#endif // hifi_ToolbarScriptingInterface_h diff --git a/libraries/networking/src/FileTypeProfile.cpp b/libraries/ui/src/ui/types/FileTypeProfile.cpp similarity index 100% rename from libraries/networking/src/FileTypeProfile.cpp rename to libraries/ui/src/ui/types/FileTypeProfile.cpp diff --git a/libraries/networking/src/FileTypeProfile.h b/libraries/ui/src/ui/types/FileTypeProfile.h similarity index 100% rename from libraries/networking/src/FileTypeProfile.h rename to libraries/ui/src/ui/types/FileTypeProfile.h diff --git a/libraries/networking/src/FileTypeRequestInterceptor.cpp b/libraries/ui/src/ui/types/FileTypeRequestInterceptor.cpp similarity index 100% rename from libraries/networking/src/FileTypeRequestInterceptor.cpp rename to libraries/ui/src/ui/types/FileTypeRequestInterceptor.cpp diff --git a/libraries/networking/src/FileTypeRequestInterceptor.h b/libraries/ui/src/ui/types/FileTypeRequestInterceptor.h similarity index 100% rename from libraries/networking/src/FileTypeRequestInterceptor.h rename to libraries/ui/src/ui/types/FileTypeRequestInterceptor.h diff --git a/libraries/networking/src/HFTabletWebEngineProfile.cpp b/libraries/ui/src/ui/types/HFTabletWebEngineProfile.cpp similarity index 100% rename from libraries/networking/src/HFTabletWebEngineProfile.cpp rename to libraries/ui/src/ui/types/HFTabletWebEngineProfile.cpp diff --git a/libraries/networking/src/HFTabletWebEngineProfile.h b/libraries/ui/src/ui/types/HFTabletWebEngineProfile.h similarity index 100% rename from libraries/networking/src/HFTabletWebEngineProfile.h rename to libraries/ui/src/ui/types/HFTabletWebEngineProfile.h diff --git a/libraries/networking/src/HFTabletWebEngineRequestInterceptor.cpp b/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.cpp similarity index 100% rename from libraries/networking/src/HFTabletWebEngineRequestInterceptor.cpp rename to libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.cpp diff --git a/libraries/networking/src/HFTabletWebEngineRequestInterceptor.h b/libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h similarity index 100% rename from libraries/networking/src/HFTabletWebEngineRequestInterceptor.h rename to libraries/ui/src/ui/types/HFTabletWebEngineRequestInterceptor.h diff --git a/libraries/networking/src/HFWebEngineProfile.cpp b/libraries/ui/src/ui/types/HFWebEngineProfile.cpp similarity index 100% rename from libraries/networking/src/HFWebEngineProfile.cpp rename to libraries/ui/src/ui/types/HFWebEngineProfile.cpp diff --git a/libraries/networking/src/HFWebEngineProfile.h b/libraries/ui/src/ui/types/HFWebEngineProfile.h similarity index 100% rename from libraries/networking/src/HFWebEngineProfile.h rename to libraries/ui/src/ui/types/HFWebEngineProfile.h diff --git a/libraries/networking/src/HFWebEngineRequestInterceptor.cpp b/libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.cpp similarity index 100% rename from libraries/networking/src/HFWebEngineRequestInterceptor.cpp rename to libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.cpp diff --git a/libraries/networking/src/HFWebEngineRequestInterceptor.h b/libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.h similarity index 100% rename from libraries/networking/src/HFWebEngineRequestInterceptor.h rename to libraries/ui/src/ui/types/HFWebEngineRequestInterceptor.h diff --git a/libraries/networking/src/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp similarity index 100% rename from libraries/networking/src/RequestFilters.cpp rename to libraries/ui/src/ui/types/RequestFilters.cpp diff --git a/libraries/networking/src/RequestFilters.h b/libraries/ui/src/ui/types/RequestFilters.h similarity index 100% rename from libraries/networking/src/RequestFilters.h rename to libraries/ui/src/ui/types/RequestFilters.h diff --git a/libraries/script-engine/src/SoundEffect.cpp b/libraries/ui/src/ui/types/SoundEffect.cpp similarity index 97% rename from libraries/script-engine/src/SoundEffect.cpp rename to libraries/ui/src/ui/types/SoundEffect.cpp index bfc0ad2100..e35d009ef6 100644 --- a/libraries/script-engine/src/SoundEffect.cpp +++ b/libraries/ui/src/ui/types/SoundEffect.cpp @@ -41,5 +41,3 @@ void SoundEffect::play(QVariant position) { _injector = AudioInjector::playSound(samples, options); } } - -#include "SoundEffect.moc" diff --git a/libraries/script-engine/src/SoundEffect.h b/libraries/ui/src/ui/types/SoundEffect.h similarity index 100% rename from libraries/script-engine/src/SoundEffect.h rename to libraries/ui/src/ui/types/SoundEffect.h From d50c44815167216ba99ea9743c4bafe48893e4af Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Jun 2017 19:15:58 -0700 Subject: [PATCH 07/11] Fix heap corruption coming out of running scripts dialog --- .../qml/hifi/dialogs/RunningScripts.qml | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index b8ba66b73f..da9ffdb07e 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -39,10 +39,26 @@ ScrollingWindow { property alias x: root.x property alias y: root.y } + + Timer { + id: refreshTimer + interval: 100 + repeat: false + running: false + onTriggered: updateRunningScripts(); + } + + Component { + id: listModelBuilder + ListModel { } + } Connections { target: ScriptDiscoveryService - onScriptCountChanged: updateRunningScripts(); + onScriptCountChanged: { + runningScriptsModel = listModelBuilder.createObject(root); + refreshTimer.restart(); + } } Component.onCompleted: { @@ -65,10 +81,16 @@ ScrollingWindow { b = simplify(b.path); return a < b ? -1 : a > b ? 1 : 0; }); - runningScriptsModel.clear() + // Calling `runningScriptsModel.clear()` here instead of creating a new object + // triggers some kind of weird heap corruption deep inside Qt. So instead of + // modifying the model in place, possibly triggering behaviors in the table + // instead we create a new `ListModel`, populate it and update the + // existing model atomically. + var newRunningScriptsModel = listModelBuilder.createObject(root); for (var i = 0; i < runningScripts.length; ++i) { - runningScriptsModel.append(runningScripts[i]); + newRunningScriptsModel.append(runningScripts[i]); } + runningScriptsModel = newRunningScriptsModel; } function loadScript(script) { From a82cc0ec09612035abd906c8de263cdbec94288c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 23 Jun 2017 09:35:48 -0400 Subject: [PATCH 08/11] retab --- interface/src/scripting/AudioDevices.cpp | 70 ++++++++++++------------ interface/src/scripting/AudioDevices.h | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index 4e88232ce1..b0ea8226e8 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -27,11 +27,11 @@ static Setting::Handle hmdInputDeviceSetting { QStringList { Audio::AUD static Setting::Handle hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; Setting::Handle& getSetting(bool contextIsHMD, QAudio::Mode mode) { - if (mode == QAudio::AudioInput) { - return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting; - } else { // if (mode == QAudio::AudioOutput) - return contextIsHMD ? hmdOutputDeviceSetting : desktopOutputDeviceSetting; - } + if (mode == QAudio::AudioInput) { + return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting; + } else { // if (mode == QAudio::AudioOutput) + return contextIsHMD ? hmdOutputDeviceSetting : desktopOutputDeviceSetting; + } } QHash AudioDeviceList::_roles { @@ -55,38 +55,38 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { } bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) { - if (!index.isValid() || index.row() >= _devices.size() || role != Qt::CheckStateRole) { - return false; - } + if (!index.isValid() || index.row() >= _devices.size() || role != Qt::CheckStateRole) { + return false; + } - // only allow switching to a new device, not deactivating an in-use device - auto selected = value.toBool(); - if (!selected) { - return false; - } + // only allow switching to a new device, not deactivating an in-use device + auto selected = value.toBool(); + if (!selected) { + return false; + } - return setDevice(index.row(), true); + return setDevice(index.row(), true); } bool AudioDeviceList::setDevice(int row, bool fromUser) { bool success = false; - auto& device = _devices[row]; + auto& device = _devices[row]; - // skip if already selected - if (!device.selected) { - auto client = DependencyManager::get(); - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, success), - Q_ARG(QAudio::Mode, _mode), - Q_ARG(const QAudioDeviceInfo&, device.info)); + // skip if already selected + if (!device.selected) { + auto client = DependencyManager::get(); + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(QAudio::Mode, _mode), + Q_ARG(const QAudioDeviceInfo&, device.info)); - if (success) { - device.selected = true; - if (fromUser) { - emit deviceSelected(device.info, _selectedDevice); - } - emit deviceChanged(device.info); - } + if (success) { + device.selected = true; + if (fromUser) { + emit deviceSelected(device.info, _selectedDevice); + } + emit deviceChanged(device.info); + } } emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); @@ -105,7 +105,7 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { } } if (i < rowCount()) { - success = setDevice(i, false); + success = setDevice(i, false); } // the selection failed - reset it @@ -193,8 +193,8 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { } void AudioDevices::onContextChanged(const QString& context) { - auto input = getSetting(_contextIsHMD, QAudio::AudioInput).get(); - auto output = getSetting(_contextIsHMD, QAudio::AudioOutput).get(); + auto input = getSetting(_contextIsHMD, QAudio::AudioInput).get(); + auto output = getSetting(_contextIsHMD, QAudio::AudioOutput).get(); _inputs.resetDevice(_contextIsHMD, input); _outputs.resetDevice(_contextIsHMD, output); @@ -203,7 +203,7 @@ void AudioDevices::onContextChanged(const QString& context) { void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { QString deviceName = device.isNull() ? QString() : device.deviceName(); - auto& setting = getSetting(_contextIsHMD, mode); + auto& setting = getSetting(_contextIsHMD, mode); // check for a previous device auto wasDefault = setting.get().isNull(); @@ -216,8 +216,8 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d QJsonObject data; const QString MODE = "audio_mode"; - const QString INPUT = "INPUT"; - const QString OUTPUT = "OUTPUT"; data[MODE] = mode == QAudio::AudioInput ? INPUT : OUTPUT; + const QString INPUT = "INPUT"; + const QString OUTPUT = "OUTPUT"; data[MODE] = mode == QAudio::AudioInput ? INPUT : OUTPUT; const QString CONTEXT = "display_mode"; data[CONTEXT] = _contextIsHMD ? Audio::HMD : Audio::DESKTOP; diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 36d887d3d4..cd47ab4191 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -54,7 +54,7 @@ private slots: private: friend class AudioDevices; - bool setDevice(int index, bool fromUser); + bool setDevice(int index, bool fromUser); static QHash _roles; static Qt::ItemFlags _flags; From 7ef50042bafc79268f5fdb729032bb14f8636e5e Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 23 Jun 2017 08:00:18 -0700 Subject: [PATCH 09/11] remove turd --- libraries/shared/src/PathUtils.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index d1a8ff757b..9bf9d7bdcf 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -20,8 +20,6 @@ #include #include // std::once -//QUrl PathUtils::_overriddenDefaultScriptsLocation = QUrl(); - const QString& PathUtils::resourcesPath() { #ifdef Q_OS_MAC static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; From 341646c780edcc41a5926f91f34b80daad22811b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Jun 2017 09:58:27 -0700 Subject: [PATCH 10/11] Fix bad invocation, cleanup signatures --- .../ui/src/ui/TabletScriptingInterface.cpp | 55 +++++++------------ .../ui/src/ui/TabletScriptingInterface.h | 12 ++-- 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index f85106918e..8b3dc342e2 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -182,7 +182,7 @@ class TabletRootWindow : public QmlWindowClass { virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; } }; -TabletProxy::TabletProxy(QObject* parent, QString name) : QObject(parent), _name(name) { +TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent), _name(name) { if (QThread::currentThread() != qApp->thread()) { qCWarning(uiLogging) << "Creating tablet proxy on wrong thread " << _name; } @@ -306,7 +306,7 @@ bool TabletProxy::isMessageDialogOpen() { return result.toBool(); } -void TabletProxy::emitWebEvent(QVariant msg) { +void TabletProxy::emitWebEvent(const QVariant& msg) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "emitWebEvent", Q_ARG(QVariant, msg)); return; @@ -314,7 +314,7 @@ void TabletProxy::emitWebEvent(QVariant msg) { emit webEventReceived(msg); } -bool TabletProxy::isPathLoaded(QVariant path) { +bool TabletProxy::isPathLoaded(const QVariant& path) { if (QThread::currentThread() != thread()) { bool result = false; QMetaObject::invokeMethod(this, "isPathLoaded", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path)); @@ -620,8 +620,7 @@ TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) { qCCritical(uiLogging) << "Could not find tablet in TabletRoot.qml"; } } else if (_toolbarMode) { - auto tabletScriptingInterface = DependencyManager::get(); - auto toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); + auto toolbarProxy = DependencyManager::get()->getSystemToolbarProxy(); if (toolbarProxy) { // copy properties from tablet button proxy to toolbar button proxy. toolbarProxy->addButton(tabletButtonProxy->getProperties()); @@ -668,15 +667,16 @@ void TabletProxy::removeButton(TabletButtonProxy* tabletButtonProxy) { QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getProperties())); } } else if (_toolbarMode) { - auto tabletScriptingInterface = DependencyManager::get(); - QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); + auto toolbarProxy = DependencyManager::get()->getSystemToolbarProxy(); // remove button from toolbarProxy - QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString())); - buttonProxy->setToolbarButtonProxy(nullptr); + if (toolbarProxy) { + toolbarProxy->removeButton(buttonProxy->getUuid().toString()); + buttonProxy->setToolbarButtonProxy(nullptr); + } } } -void TabletProxy::emitScriptEvent(QVariant msg) { +void TabletProxy::emitScriptEvent(const QVariant& msg) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "emitScriptEvent", Q_ARG(QVariant, msg)); return; @@ -689,7 +689,7 @@ void TabletProxy::emitScriptEvent(QVariant msg) { } } -void TabletProxy::sendToQml(QVariant msg) { +void TabletProxy::sendToQml(const QVariant& msg) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "sendToQml", Q_ARG(QVariant, msg)); return; @@ -707,8 +707,6 @@ void TabletProxy::addButtonsToHomeScreen() { if (!tablet || _toolbarMode) { return; } - - auto tabletScriptingInterface = DependencyManager::get(); for (auto& buttonProxy : _tabletButtonProxies) { addButtonProxyToQmlTablet(tablet, buttonProxy.data()); } @@ -735,35 +733,23 @@ void TabletProxy::desktopWindowClosed() { } void TabletProxy::addButtonsToToolbar() { - auto tabletScriptingInterface = DependencyManager::get(); - QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); - - Qt::ConnectionType connectionType = Qt::AutoConnection; - if (QThread::currentThread() != toolbarProxy->thread()) { - connectionType = Qt::BlockingQueuedConnection; - } - + Q_ASSERT(QThread::currentThread() == thread()); + ToolbarProxy* toolbarProxy = DependencyManager::get()->getSystemToolbarProxy(); for (auto& buttonProxy : _tabletButtonProxies) { // copy properties from tablet button proxy to toolbar button proxy. - QObject* toolbarButtonProxy = nullptr; - bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, buttonProxy->getProperties())); - if (hasResult) { - buttonProxy->setToolbarButtonProxy(toolbarButtonProxy); - } else { - qCWarning(uiLogging) << "ToolbarProxy addButton has no result"; - } + buttonProxy->setToolbarButtonProxy(toolbarProxy->addButton(buttonProxy->getProperties())); } // make the toolbar visible - QMetaObject::invokeMethod(toolbarProxy, "writeProperty", Qt::AutoConnection, Q_ARG(QString, "visible"), Q_ARG(QVariant, QVariant(true))); + toolbarProxy->writeProperty("visible", QVariant(true)); } void TabletProxy::removeButtonsFromToolbar() { - auto tabletScriptingInterface = DependencyManager::get(); - QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy(); + Q_ASSERT(QThread::currentThread() == thread()); + ToolbarProxy* toolbarProxy = DependencyManager::get()->getSystemToolbarProxy(); for (auto& buttonProxy : _tabletButtonProxies) { // remove button from toolbarProxy - QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString())); + toolbarProxy->removeButton(buttonProxy->getUuid().toString()); buttonProxy->setToolbarButtonProxy(nullptr); } } @@ -843,6 +829,7 @@ void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { } void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) { + Q_ASSERT(QThread::currentThread() == thread()); _toolbarButtonProxy = toolbarButtonProxy; if (_toolbarButtonProxy) { QObject::connect(_toolbarButtonProxy, SIGNAL(clicked()), this, SLOT(clickedSlot())); @@ -859,9 +846,9 @@ QVariantMap TabletButtonProxy::getProperties() { return _properties; } -void TabletButtonProxy::editProperties(QVariantMap properties) { +void TabletButtonProxy::editProperties(const QVariantMap& properties) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "editProperties", Qt::BlockingQueuedConnection, Q_ARG(QVariantMap, properties)); + QMetaObject::invokeMethod(this, "editProperties", Q_ARG(QVariantMap, properties)); return; } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 0303a0a1c4..e61398585e 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -95,7 +95,7 @@ class TabletProxy : public QObject { Q_PROPERTY(bool landscape READ getLandscape WRITE setLandscape) Q_PROPERTY(bool tabletShown MEMBER _tabletShown NOTIFY tabletShownChanged) public: - TabletProxy(QObject* parent, QString name); + TabletProxy(QObject* parent, const QString& name); ~TabletProxy(); void setQmlTabletRoot(OffscreenQmlSurface* offscreenQmlSurface); @@ -163,14 +163,14 @@ public: * @function TabletProxy#emitScriptEvent * @param msg {object|string} */ - Q_INVOKABLE void emitScriptEvent(QVariant msg); + Q_INVOKABLE void emitScriptEvent(const QVariant& msg); /**jsdoc * Used to send an event to the qml embedded in the tablet * @function TabletProxy#sendToQml * @param msg {object|string} */ - Q_INVOKABLE void sendToQml(QVariant msg); + Q_INVOKABLE void sendToQml(const QVariant& msg); /**jsdoc * Check if the tablet is on the homescreen @@ -186,7 +186,7 @@ public: Q_INVOKABLE void setLandscape(bool landscape) { _landscape = landscape; } Q_INVOKABLE bool getLandscape() { return _landscape; } - Q_INVOKABLE bool isPathLoaded(QVariant path); + Q_INVOKABLE bool isPathLoaded(const QVariant& path); QQuickItem* getTabletRoot() const { return _qmlTabletRoot; } @@ -231,7 +231,7 @@ signals: protected slots: void addButtonsToHomeScreen(); void desktopWindowClosed(); - void emitWebEvent(QVariant msg); + void emitWebEvent(const QVariant& msg); protected: void removeButtonsFromHomeScreen(); void loadHomeScreen(bool forceOntoHomeScreen); @@ -284,7 +284,7 @@ public: * @function TabletButtonProxy#editProperties * @param {ButtonProperties} properties - set of properties to change */ - Q_INVOKABLE void editProperties(QVariantMap properties); + Q_INVOKABLE void editProperties(const QVariantMap& properties); public slots: void clickedSlot() { emit clicked(); } From fe7d4b1d232a291c3fe8ff403d8adab9c9c5e8cb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Jun 2017 11:54:35 -0700 Subject: [PATCH 11/11] Remove dependency between codec plugins and UI --- assignment-client/CMakeLists.txt | 2 +- assignment-client/src/audio/AudioMixer.cpp | 13 ------- interface/src/Application.cpp | 10 ++++++ libraries/plugins/src/plugins/Forward.h | 5 +++ .../plugins/src/plugins/PluginManager.cpp | 35 +++++++++++++------ libraries/plugins/src/plugins/PluginManager.h | 12 +++++++ plugins/hifiCodec/CMakeLists.txt | 2 +- plugins/hifiNeuron/CMakeLists.txt | 2 +- plugins/hifiSdl2/CMakeLists.txt | 2 +- plugins/hifiSixense/CMakeLists.txt | 2 +- plugins/pcmCodec/CMakeLists.txt | 2 +- 11 files changed, 58 insertions(+), 29 deletions(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 54afabfd21..c9af474949 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -11,7 +11,7 @@ endif () link_hifi_libraries( audio avatars octree gpu model fbx entities networking animation recording shared script-engine embedded-webserver - controllers physics plugins + physics plugins ) if (WIN32) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 5e0cd740e6..93b9b10eb7 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -166,19 +166,6 @@ void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer mes } } -DisplayPluginList getDisplayPlugins() { - DisplayPluginList result; - return result; -} - -InputPluginList getInputPlugins() { - InputPluginList result; - return result; -} - -// must be here to satisfy a reference in PluginManager::saveSettings() -void saveInputPluginSettings(const InputPluginList& plugins) {} - const std::pair AudioMixer::negotiateCodec(std::vector codecs) { QString selectedCodecName; CodecPluginPointer selectedCodec; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a7bed9fc9c..3ca8c29288 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -441,6 +441,11 @@ static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; +// Statically provided display and input plugins +extern DisplayPluginList getDisplayPlugins(); +extern InputPluginList getInputPlugins(); +extern void saveInputPluginSettings(const InputPluginList& plugins); + bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { const char** constArgv = const_cast(argv); @@ -480,6 +485,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { Setting::init(); + // Tell the plugin manager about our statically linked plugins + PluginManager::setInputPluginProvider([] { return getInputPlugins(); }); + PluginManager::setDisplayPluginProvider([] { return getDisplayPlugins(); }); + PluginManager::setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); }); + if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { steamClient->init(); } diff --git a/libraries/plugins/src/plugins/Forward.h b/libraries/plugins/src/plugins/Forward.h index b95bf4c2e6..90746d648e 100644 --- a/libraries/plugins/src/plugins/Forward.h +++ b/libraries/plugins/src/plugins/Forward.h @@ -9,6 +9,7 @@ #include #include +#include enum class PluginType { DISPLAY_PLUGIN, @@ -26,8 +27,12 @@ class PluginManager; using DisplayPluginPointer = std::shared_ptr; using DisplayPluginList = std::vector; +using DisplayPluginProvider = std::function; using InputPluginPointer = std::shared_ptr; using InputPluginList = std::vector; +using InputPluginProvider = std::function; using CodecPluginPointer = std::shared_ptr; using CodecPluginList = std::vector; +using CodecPluginProvider = std::function; using SteamClientPluginPointer = std::shared_ptr; +using InputPluginSettingsPersister = std::function; diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index 8471dbc7e8..e90d3e3a0f 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -23,6 +23,26 @@ #include "InputPlugin.h" #include "PluginLogging.h" +DisplayPluginProvider PluginManager::_displayPluginProvider = []()->DisplayPluginList { return {}; }; +InputPluginProvider PluginManager::_inputPluginProvider = []()->InputPluginList { return {}; }; +CodecPluginProvider PluginManager::_codecPluginProvider = []()->CodecPluginList { return {}; }; +InputPluginSettingsPersister PluginManager::_inputSettingsPersister = [](const InputPluginList& list) {}; + +void PluginManager::setDisplayPluginProvider(const DisplayPluginProvider& provider) { + _displayPluginProvider = provider; +} + +void PluginManager::setInputPluginProvider(const InputPluginProvider& provider) { + _inputPluginProvider = provider; +} + +void PluginManager::setCodecPluginProvider(const CodecPluginProvider& provider) { + _codecPluginProvider = provider; +} + +void PluginManager::setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister) { + _inputSettingsPersister = persister; +} PluginManager* PluginManager::getInstance() { static PluginManager _manager; @@ -117,12 +137,12 @@ const LoaderList& getLoadedPlugins() { PluginManager::PluginManager() { } -extern CodecPluginList getCodecPlugins(); - const CodecPluginList& PluginManager::getCodecPlugins() { static CodecPluginList codecPlugins; static std::once_flag once; std::call_once(once, [&] { + codecPlugins = _codecPluginProvider(); + // Now grab the dynamic plugins for (auto loader : getLoadedPlugins()) { CodecProvider* codecProvider = qobject_cast(loader->instance()); @@ -163,11 +183,6 @@ const SteamClientPluginPointer PluginManager::getSteamClientPlugin() { #ifndef Q_OS_ANDROID -// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class -extern DisplayPluginList getDisplayPlugins(); -extern InputPluginList getInputPlugins(); - -extern void saveInputPluginSettings(const InputPluginList& plugins); static DisplayPluginList displayPlugins; const DisplayPluginList& PluginManager::getDisplayPlugins() { @@ -183,7 +198,7 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() { std::call_once(once, [&] { // Grab the built in plugins - displayPlugins = ::getDisplayPlugins(); + displayPlugins = _displayPluginProvider(); // Now grab the dynamic plugins @@ -229,7 +244,7 @@ const InputPluginList& PluginManager::getInputPlugins() { }; std::call_once(once, [&] { - inputPlugins = ::getInputPlugins(); + inputPlugins = _inputPluginProvider(); // Now grab the dynamic plugins for (auto loader : getLoadedPlugins()) { @@ -288,7 +303,7 @@ void PluginManager::disableInputs(const QStringList& inputs) { } void PluginManager::saveSettings() { - saveInputPluginSettings(getInputPlugins()); + _inputSettingsPersister(getInputPlugins()); } void PluginManager::shutdown() { diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index 687e5c9e9b..cb011392a4 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -31,6 +31,18 @@ public: void setContainer(PluginContainer* container) { _container = container; } void shutdown(); + + // Application that have statically linked plugins can expose them to the plugin manager with these function + static void setDisplayPluginProvider(const DisplayPluginProvider& provider); + static void setInputPluginProvider(const InputPluginProvider& provider); + static void setCodecPluginProvider(const CodecPluginProvider& provider); + static void setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister); + private: + static DisplayPluginProvider _displayPluginProvider; + static InputPluginProvider _inputPluginProvider; + static CodecPluginProvider _codecPluginProvider; + static InputPluginSettingsPersister _inputSettingsPersister; + PluginContainer* _container { nullptr }; }; diff --git a/plugins/hifiCodec/CMakeLists.txt b/plugins/hifiCodec/CMakeLists.txt index 15572e8266..28c1dc3807 100644 --- a/plugins/hifiCodec/CMakeLists.txt +++ b/plugins/hifiCodec/CMakeLists.txt @@ -8,7 +8,7 @@ set(TARGET_NAME hifiCodec) setup_hifi_client_server_plugin() -link_hifi_libraries(audio plugins input-plugins display-plugins) +link_hifi_libraries(audio plugins) add_dependency_external_projects(hifiAudioCodec) target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES}) diff --git a/plugins/hifiNeuron/CMakeLists.txt b/plugins/hifiNeuron/CMakeLists.txt index e3a725ca2f..a9ed8cca6e 100644 --- a/plugins/hifiNeuron/CMakeLists.txt +++ b/plugins/hifiNeuron/CMakeLists.txt @@ -10,7 +10,7 @@ if (APPLE OR WIN32) set(TARGET_NAME hifiNeuron) setup_hifi_plugin(Script Qml Widgets) - link_hifi_libraries(shared controllers ui plugins input-plugins display-plugins) + link_hifi_libraries(shared controllers ui plugins input-plugins) target_neuron() endif() diff --git a/plugins/hifiSdl2/CMakeLists.txt b/plugins/hifiSdl2/CMakeLists.txt index 86bda5a15d..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 ui plugins input-plugins script-engine display-plugins) +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 54884bddff..14676217db 100644 --- a/plugins/hifiSixense/CMakeLists.txt +++ b/plugins/hifiSixense/CMakeLists.txt @@ -9,6 +9,6 @@ if (NOT ANDROID) set(TARGET_NAME hifiSixense) setup_hifi_plugin(Script Qml Widgets) - link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins display-plugins) + link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) target_sixense() endif () diff --git a/plugins/pcmCodec/CMakeLists.txt b/plugins/pcmCodec/CMakeLists.txt index 5e52705033..900a642a88 100644 --- a/plugins/pcmCodec/CMakeLists.txt +++ b/plugins/pcmCodec/CMakeLists.txt @@ -8,6 +8,6 @@ set(TARGET_NAME pcmCodec) setup_hifi_client_server_plugin() -link_hifi_libraries(shared plugins input-plugins display-plugins) +link_hifi_libraries(shared plugins) install_beside_console()